Compare commits

..

2 Commits
main ... temp

Author SHA1 Message Date
caiyiling 4c642cd814 上传更改 2026-07-31 17:20:20 +08:00
caiyiling 9dcbad7616 数据处理更改 2026-07-31 16:40:37 +08:00
29 changed files with 667 additions and 569 deletions

View File

@ -4523,3 +4523,23 @@ export function addOrUpdateCommonUploadRecord(data) {
data,
})
}
export function getInspectionById(data) {
return request({
url: `/Inspection/getInspectionById`,
method: 'post',
data,
})
}
export function setJsonDetail(data) {
return request({
url: `/Inspection/setJsonDetail`,
method: 'post',
data,
})
}

View File

@ -80,7 +80,7 @@
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
import metaDataProvider, { registerImageSharedCacheKey, unregisterImageSharedCacheKeys } from '@/utils/metaDataProvider'
import metaDataProvider from '@/utils/metaDataProvider'
const scroll = cornerstoneTools.import('util/scrollToIndex')
import Hammer from 'hammerjs'
@ -211,9 +211,6 @@ export default {
this.stackScrollCallback
)
},
beforeDestroy() {
unregisterImageSharedCacheKeys(this.stack?.imageIds)
},
methods: {
loadImageStack(dicomSeries, text = '') {
@ -403,10 +400,6 @@ export default {
},
onNewImage(e) {
e.detail.enabledElement.options = {}
registerImageSharedCacheKey(
e.detail.image?.imageId,
e.detail.image?.sharedCacheKey
)
var data = e.detail.image.data
this.dicomInfo.hospital = data.string('x00080080')
// let instanceInfo = this.series.instanceInfoList.find(item => item.ImageId === e.detail.image.imageId)

View File

@ -477,10 +477,7 @@ export default {
let res = await getSubjectImageUploadList(params)
this.loading = false
if (res.IsSuccess) {
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.isDownloaded = res.OtherInfo.IsIRImageDownloaded
this.TrialModality = res.OtherInfo.TrialModality.split('|')
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder || res.OtherInfo.IsReadingTaskViewInOrder === 0 ? res.OtherInfo.IsReadingTaskViewInOrder : 2
this.StudyInstanceUidList = []
@ -958,7 +955,7 @@ export default {
var instanceItem = instanceList.find(function (item) {
return item.instanceUid === instanceUid
})
if (!instanceItem) {
if (!false) {
var date = data.string('x00080023')
var time = data.string('x00080033')
var instanceTime = ''
@ -1254,58 +1251,60 @@ export default {
try {
let o = v.instanceList[ii]
let name = `${v.instanceList[ii].file.webkitRelativePath}_${v.instanceList[ii].instanceUid}`
if (o.isReUpload) {
dicomInfo.failedFileCount++
dicomInfo.uploadFileSize += o.file.size
Record.Existed.push(name)
Record.FileCount++
} else if (o.myPath) {
instanceList.push({
studyInstanceUid: dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
SOPClassUID: o.SOPClassUID,
TransferSytaxUID: o.TransferSytaxUID,
MediaStorageSOPInstanceUID:
o.MediaStorageSOPInstanceUID,
MediaStorageSOPClassUID:
o.MediaStorageSOPClassUID,
sopInstanceUid: o.instanceUid,
instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime,
imageRows: o.imageRows,
imageColumns: o.imageColumns,
sliceLocation: o.sliceLocation,
sliceThickness: o.sliceThickness,
numberOfFrames: o.numberOfFrames,
pixelSpacing: o.pixelSpacing,
imagerPixelSpacing: o.imagerPixelSpacing,
frameOfReferenceUID: o.frameOfReferenceUID,
windowCenter: o.windowCenter,
windowWidth: o.windowWidth,
path: o.myPath,
FileSize: o.FileSize,
// if (o.isReUpload) {
// dicomInfo.failedFileCount++
// dicomInfo.uploadFileSize += o.file.size
// Record.Existed.push(name)
// Record.FileCount++
// }
// else if (o.myPath) {
// instanceList.push({
// studyInstanceUid: dicomInfo.studyUid,
// seriesInstanceUid: v.seriesUid,
// SOPClassUID: o.SOPClassUID,
// TransferSytaxUID: o.TransferSytaxUID,
// MediaStorageSOPInstanceUID:
// o.MediaStorageSOPInstanceUID,
// MediaStorageSOPClassUID:
// o.MediaStorageSOPClassUID,
// sopInstanceUid: o.instanceUid,
// instanceNumber: o.instanceNumber,
// instanceTime: o.instanceTime,
// imageRows: o.imageRows,
// imageColumns: o.imageColumns,
// sliceLocation: o.sliceLocation,
// sliceThickness: o.sliceThickness,
// numberOfFrames: o.numberOfFrames,
// pixelSpacing: o.pixelSpacing,
// imagerPixelSpacing: o.imagerPixelSpacing,
// frameOfReferenceUID: o.frameOfReferenceUID,
// windowCenter: o.windowCenter,
// windowWidth: o.windowWidth,
// path: o.myPath,
// FileSize: o.FileSize,
PhotometricInterpretation:
o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient:
o.ImageOrientationPatient,
SequenceOfUltrasoundRegions:
o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage,
Units: o.Units,
DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument,
})
Record.Uploaded.push(name)
dicomInfo.failedFileCount++
Record.FileCount++
} else {
// PhotometricInterpretation:
// o.PhotometricInterpretation,
// BitsAllocated: o.BitsAllocated,
// PixelRepresentation: o.PixelRepresentation,
// RescaleIntercept: o.RescaleIntercept,
// RescaleSlope: o.RescaleSlope,
// ImagePositionPatient: o.ImagePositionPatient,
// ImageOrientationPatient:
// o.ImageOrientationPatient,
// SequenceOfUltrasoundRegions:
// o.SequenceOfUltrasoundRegions,
// FrameTime: o.FrameTime,
// CorrectedImage: o.CorrectedImage,
// Units: o.Units,
// DecayCorrection: o.DecayCorrection,
// EncapsulatedDocument: o.EncapsulatedDocument,
// })
// Record.Uploaded.push(name)
// dicomInfo.failedFileCount++
// Record.FileCount++
// }
// else {
let path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/${dicomInfo.visitTaskId
}/${scope.getGuid(
@ -1446,7 +1445,7 @@ export default {
Record.Failed.push(name)
Record.FileCount++
}
}
// }
resolve1()
} catch (e) {
console.log(e)

View File

@ -104,7 +104,7 @@
<span v-else>{{ scope.row.UploadedFileCount }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="180">
<el-table-column :label="$t('common:action:action')" fixed="right" width="180">
<template slot-scope="scope">
<!--预览--->
<el-button circle :disabled="scope.row.UploadedFileCount <= 0" icon="el-icon-view"
@ -353,10 +353,7 @@ export default {
if (res.IsSuccess) {
this.list = res.Result
this.relationInfo = res.OtherInfo
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.isDownloaded = res.OtherInfo.IsIRImageDownloaded
this.faccept = []
this.relationInfo.ImageFormatList.forEach((item) => {
this.faccept.push(`.${item}`)

View File

@ -119,25 +119,6 @@ function parseImageId(imageId) {
return { scheme, url, frame };
}
const imageIdToSharedCacheKey = new Map();
export function registerImageSharedCacheKey(imageId, sharedCacheKey) {
if (!imageId || !sharedCacheKey) {
return;
}
imageIdToSharedCacheKey.set(imageId, sharedCacheKey);
}
export function unregisterImageSharedCacheKeys(imageIds) {
if (!Array.isArray(imageIds) || imageIds.length === 0) {
return;
}
imageIds.forEach((imageId) => {
if (imageId) {
imageIdToSharedCacheKey.delete(imageId);
}
});
}
function getNumberValues(dataSet, tag, minimumLength) {
const values = [];
const valueAsString = dataSet.string(tag);
@ -276,20 +257,10 @@ function getSpacingBetweenSlices(dataSet) {
}
function metaDataProvider(type, imageId) {
const parsedImageId = parseImageId(imageId);
let dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
parsedImageId.url
);
if (!dataSet) {
const sharedCacheKey = imageIdToSharedCacheKey.get(imageId);
if (sharedCacheKey) {
dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
sharedCacheKey
);
}
}
if (!dataSet) {
return;
}
@ -450,6 +421,7 @@ function metaDataProvider(type, imageId) {
let columnPixelSpacing = null;
let rowPixelSpacing = null;
if (pixelSpacing) {
rowPixelSpacing = pixelSpacing[0];
columnPixelSpacing = pixelSpacing[1];

View File

@ -11,11 +11,10 @@ function zipFiles(zipName, files) {
store.dispatch('trials/setUnLock', true)
files = formatFiles(files)
// 创建压缩文件输出流
let zipFileOutputStream = streamSaver.createWriteStream(zipName);
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
let Url = [];
let readableZipStream = new ZIP({
const readableZipStream = new ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
@ -24,7 +23,6 @@ function zipFiles(zipName, files) {
let { name, url, IsEncapsulated = false } = fileInfo.value;
if (IsEncapsulated) {
url = await downloadFileToUrl(url)
Url.push(url)
}
// url = decodeUtf8(url); // 待定,可能做过特殊处理
return fetch(url).then(res => {
@ -42,31 +40,13 @@ function zipFiles(zipName, files) {
.pipeTo(zipFileOutputStream)
.then(() => {
console.log("同步下载打包结束时间:" + new Date());
store.dispatch('trials/setUnLock', false)
resolve(true)
}).catch(err => {
console.log(err);
resolve(false)
}).finally(() => {
store.dispatch('trials/setUnLock', false)
readableZipStream = null;
zipFileOutputStream = null;
if (Url && Url.length > 0) {
Url.forEach(url => {
URL.revokeObjectURL(url)
})
Url = null
}
});
} else {
store.dispatch('trials/setUnLock', false)
readableZipStream = null;
zipFileOutputStream = null;
if (Url && Url.length > 0) {
Url.forEach(url => {
URL.revokeObjectURL(url)
})
Url = null
}
resolve(false)
}
} catch (err) {
@ -81,17 +61,15 @@ async function updateFile(file, name) {
return new Promise(async resolve => {
try {
store.dispatch('trials/setUnLock', true)
let fileOutputStream = streamSaver.createWriteStream(name);
const fileOutputStream = streamSaver.createWriteStream(name);
// file = decodeUtf8(file);
let res = await fetch(file);
res.body.pipeTo(fileOutputStream).then(() => {
store.dispatch('trials/setUnLock', true)
resolve(true)
}).catch(err => {
console.log(err)
resolve(false)
}).finally(() => {
store.dispatch('trials/setUnLock', false)
fileOutputStream = null;
});
} catch (err) {
console.log(err)
@ -132,7 +110,6 @@ function downloadFileToUrl(url) {
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
try {
const arraybuffer = xhr.response;
let dataset = dcmjs.data.DicomMessage.readFile(arraybuffer)
let newDicomFile = dataset.write({ fragmentMultiframe: false, startOffset: false }) // fragmentMultiframe 原始数据是否进行分割
@ -140,9 +117,6 @@ function downloadFileToUrl(url) {
const blob = new Blob([bufferArray], { type: 'application/octet-stream' })
const href = URL.createObjectURL(blob)
resolve(href)
} catch (err) {
reject(err);
}
};
xhr.send();
})

View File

@ -133,7 +133,7 @@ import { mapGetters } from 'vuex'
import Contextmenu from 'vue-contextmenujs'
Vue.use(Contextmenu)
import * as cornerstone from 'cornerstone-core'
import metaDataProvider, { registerImageSharedCacheKey, unregisterImageSharedCacheKeys } from '@/utils/metaDataProvider'
import metaDataProvider from '@/utils/metaDataProvider'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
@ -480,7 +480,6 @@ export default {
},
beforeDestroy() {
// Store.$off('getMeasureData')
unregisterImageSharedCacheKeys(this.stack?.imageIds)
DicomEvent.$off('setReadingState')
},
methods: {
@ -1280,7 +1279,6 @@ export default {
this.resetHideMeasureArr()
}
const { image } = e.detail
registerImageSharedCacheKey(image?.imageId, image?.sharedCacheKey)
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
const imageId = image.imageId
ToolStateManager.clearImageIdToolState(imageId)

View File

@ -189,22 +189,11 @@
</template>
<!-- 计算值 -->
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
:question-type="12"
style="margin-top:20px" />
<Questions ref="ecrf2" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="4" style="margin-top:20px" />
<!-- 评估结果 -->
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
<Questions ref="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="5" />
<!-- 新增编辑弹窗 -->
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
@ -633,7 +622,7 @@ export default {
return v
}
},
async handleSave(index, options = {}) {
async handleSave(index) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
this.$set(this.rowSaveLoadingMap, index, true)
@ -651,13 +640,9 @@ export default {
answers: answers
}
await saveTaskQuestion(12, params)
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.refreshQuestions()
this.formChanged = false
} catch (e) {
console.log(e)
@ -873,29 +858,9 @@ export default {
}
},
async uploadDlgClose() {
try {
await this.refreshQuestions()
const saveTasks = []
for (let index = 0; index < this.questions.length; index++) {
const qs = this.questions[index]
if (qs.GroupClassify === 6) {
saveTasks.push(() => this.handleSave(index, { silentSuccess: true, skipRefresh: true }))
}
}
saveTasks.push(() => this.$refs['ecrf2'] && this.$refs['ecrf2'].handleSave({ silentSuccess: true, skipRefresh: true }))
saveTasks.push(() => this.$refs['ecrf3'] && this.$refs['ecrf3'].handleSave({ silentSuccess: true, skipRefresh: true }))
for (const saveTask of saveTasks) {
try {
await saveTask()
} catch (e) {
console.log(e)
}
}
} catch (e) {
console.log(e)
} finally {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')

View File

@ -189,22 +189,11 @@
</template>
<!-- 计算值 -->
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
:question-type="12"
style="margin-top:20px" />
<Questions ref="ecrf2" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="4" style="margin-top:20px" />
<!-- 评估结果 -->
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
<Questions ref="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="5" />
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
:title="addOrEdit.title" width="500px">
@ -628,7 +617,7 @@ export default {
return v
}
},
async handleSave(index, options = {}) {
async handleSave(index) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
this.$set(this.rowSaveLoadingMap, index, true)
@ -646,13 +635,9 @@ export default {
answers: answers
}
await saveTaskQuestion(12, params)
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.refreshQuestions()
this.formChanged = false
} catch (e) {
console.log(e)
@ -899,29 +884,9 @@ export default {
}
},
async uploadDlgClose() {
try {
await this.refreshQuestions()
const saveTasks = []
for (let index = 0; index < this.questions.length; index++) {
const qs = this.questions[index]
if (qs.GroupClassify === 6) {
saveTasks.push(() => this.handleSave(index, { silentSuccess: true, skipRefresh: true }))
}
}
saveTasks.push(() => this.$refs['ecrf2'] && this.$refs['ecrf2'].handleSave({ silentSuccess: true, skipRefresh: true }))
saveTasks.push(() => this.$refs['ecrf3'] && this.$refs['ecrf3'].handleSave({ silentSuccess: true, skipRefresh: true }))
for (const saveTask of saveTasks) {
try {
await saveTask()
} catch (e) {
console.log(e)
}
}
} catch (e) {
console.log(e)
} finally {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')

View File

@ -279,24 +279,22 @@ export default {
})
},
async handleSave(options = {}) {
if (this.saveLoading) return false
handleSave() {
if (this.saveLoading) return
this.saveLoading = true
const valid = await new Promise(resolve => {
this.$refs['questions'].validate(v => resolve(v))
})
this.$refs['questions'].validate(async (valid) => {
if (!valid) {
this.saveLoading = false
return false
return
}
// lugano
if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 3) {
//
var existUnSave = this.checkAnnotationStatus(this.questions)
if (existUnSave) {
await this.$alert(this.$t('trials:lugano:message:saveWarning'), this.$t('trials:lugano:fusionDialog:warning'))
this.$alert(this.$t('trials:lugano:message:saveWarning'), this.$t('trials:lugano:fusionDialog:warning'))
this.saveLoading = false
return false
return
}
var currentSpleenStatus = this.questionForm[this.spleenStatusId]
@ -307,26 +305,24 @@ export default {
if (currentSpleenLength && currentSpleenStatus === 5) {
// '!'
await this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
} else if (currentSpleenStatus === 5 && ((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData))) {
//
await this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
} else if (((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData)) && !currentSpleenLength) {
await this.$alert(this.$t('trials:lugano:message:validSpleen2'), this.$t('trials:lugano:fusionDialog:warning'), {
this.$alert(this.$t('trials:lugano:message:validSpleen2'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
} else {
this.saveQuestionsList()
}
return await this.saveQuestionsList(options)
} else if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 1 && this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
// PET-CT
let res = null
@ -337,28 +333,30 @@ export default {
}
if (res.IsSuccess && !res.Result.IsCanChooseNotMerge) {
await this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {
// this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {showCancelButton: false})
this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
callback: action => { }
})
this.saveLoading = false
return false
}
return
} else {
// PET-CTFDG-PETNE
const confirm = await this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => true).catch(() => false)
if (!confirm) {
this.saveLoading = false
return false
})
.then(() => {
this.saveQuestionsList()
})
}
return await this.saveQuestionsList(options)
} else {
return await this.saveQuestionsList(options)
this.saveQuestionsList()
}
// this.saveQuestionsList()
})
},
async saveQuestionsList(options = {}) {
async saveQuestionsList() {
this.loading = true
var answers = []
var imageQuality = null
@ -384,22 +382,18 @@ export default {
// })
// params.questionMarkInfoList = questionMarkInfoList
// }
try {
const res = await saveTaskQuestion(this.questionType, params)
saveTaskQuestion(this.questionType, params).then(async res => {
if (this.imageQualityIssuesId) {
store.dispatch('reading/setImageQualityIssues', this.questionForm[this.imageQualityIssuesId])
if (this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
DicomEvent.$emit('closePetct')
}
}
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
if (!options.skipRefresh) {
var trialId = this.$route.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: trialId, visitTaskId: this.visitTaskId })
this.getQuestions(this.visitTaskId, true)
}
this.loading = false
if (this.isQulityIssues) {
DicomEvent.$emit('questionFormChange', false)
@ -423,12 +417,10 @@ export default {
}
this.formChanged = false
this.saveLoading = false
return res
} catch (e) {
}).catch(() => {
this.loading = false
this.saveLoading = false
throw e
}
})
},
checkAnnotationStatus(obj) {
for (let i = 0; i < obj.length; i++) {

View File

@ -126,7 +126,7 @@ import { mapGetters } from 'vuex'
import Contextmenu from 'vue-contextmenujs'
Vue.use(Contextmenu)
import * as cornerstone from 'cornerstone-core'
import metaDataProvider, { registerImageSharedCacheKey, unregisterImageSharedCacheKeys } from '@/utils/metaDataProvider'
import metaDataProvider from '@/utils/metaDataProvider'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
@ -473,7 +473,6 @@ export default {
},
beforeDestroy() {
// Store.$off('getMeasureData')
unregisterImageSharedCacheKeys(this.stack?.imageIds)
DicomEvent.$off('setReadingState')
DicomEvent.$off('updateImage')
},
@ -1187,7 +1186,6 @@ export default {
this.resetHideMeasureArr()
}
const { image } = e.detail
registerImageSharedCacheKey(image?.imageId, image?.sharedCacheKey)
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
const imageId = image.imageId
ToolStateManager.clearImageIdToolState(imageId)

View File

@ -309,9 +309,10 @@
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
</div>
</el-tooltip>
<el-tooltip v-if="
trialCriterion.ImageUploadEnum > 0 && currentReadingTaskState < 2
" v-hasPermi="['role:ir']" class="item" effect="dark" :content="$t('trials:reading:button:upload')"
<el-tooltip
v-if="trialCriterion.ImageUploadEnum > 0"
class="item" effect="dark"
:content="$t('trials:reading:button:upload')"
placement="bottom">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
@ -448,10 +449,10 @@
/> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :SubjectId="uploadSubjectId" :SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible" :VisitTaskId="taskId"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :isReading="true" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion" :TaskId="taskId"
:visible.sync="downloadImageVisible" :isReading="true"/>
:visible.sync="downloadImageVisible" />
</div>
</template>
<script>

View File

@ -128,7 +128,7 @@ export default {
immediate: true,
handler(val) {
if (val) {
this.readingTaskState = val
this.readingTaskState = 1
}
}
}
@ -169,7 +169,7 @@ export default {
if (i > -1) {
this.visitTaskId = this.visitTaskList[i].VisitTaskId
this.taskBlindName = this.visitTaskList[i].TaskBlindName
this.readingTaskState = this.visitTaskList[i].ReadingTaskState
this.readingTaskState = 1
console.log(this.visitTaskList[i].IsInit)
if (!this.visitTaskList[i].IsInit) {
var loading = this.$loading({ fullscreen: true })
@ -306,9 +306,9 @@ export default {
}
this.signVisible = false
//
this.readingTaskState = 2
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
DicomEvent.$emit('setReadingState', 2)
this.readingTaskState = 1
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 1 })
DicomEvent.$emit('setReadingState', 1)
window.opener.postMessage('refreshTaskList', window.location)
this.$confirm(this.$t('trials:oncologyReview:title:msg2'), {
type: 'warning',

View File

@ -1,6 +1,6 @@
<template>
<div v-loading="loading" style="min-height: 500px;">
<el-form v-if="isRender" ref="questions" size="small" :model="questionForm" :disabled="readingTaskState >= 2">
<el-form v-if="isRender" ref="questions" size="small" :model="questionForm">
<template>
<QuestionFormItem v-for="question of questions" :key="question.Id" :visitTaskId="visitTaskId"
:question="question" :question-form="questionForm" :reading-task-state="readingTaskState"
@ -112,8 +112,8 @@ export default {
VisitTaskId: visitTaskId ? visitTaskId : this.visitTaskId
}).then(res => {
this.IsBaseline = res.OtherInfo.IsBaseline
this.readingTaskState = res.OtherInfo.ReadingTaskState
DicomEvent.$emit('setReadingState', res.OtherInfo.ReadingTaskState)
this.readingTaskState = 1
DicomEvent.$emit('setReadingState', 1)
res.Result.SinglePage.map((v) => {
if (v.Type === 'group' && v.Childrens.length === 0) return
if (!v.IsPage && v.Type !== 'group' && v.Type !== 'summary' && v.Type !== 'table' && v.Type !== 'basicTable' && v.Type !== 'number') {

View File

@ -147,7 +147,7 @@
</template>
</el-table-column>
</el-table>
<div style="text-align:right;margin-top: 10px;" v-if="!IsViewStudyPart">
<div style="text-align:right;margin-top: 10px;">
<!-- 确认 -->
<el-button type="primary" @click="handleConfirmBP">{{ $t('trials:reading:button:confirm')
}}</el-button>
@ -223,8 +223,7 @@ export default {
readingVersionEnum: null,
dialogBodyPartVisible: false,
isReadKeyFile: true,
isHaveKeyFile: false,
IsViewStudyPart: true
isHaveKeyFile: false
}
},
computed: {

View File

@ -160,17 +160,6 @@
<svg-icon v-if="forceFitToWindow" icon-class="fitToWindow" class="svg-icon" />
<svg-icon v-else icon-class="fitToImage" class="svg-icon" />
</div>
<!-- 同步切图 -->
<div v-if="!isFusion && !isMPR && cells.length > 1"
:class="['tool-item', isScrollSync ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:imageIndexSync')" @click.prevent="setImageIndexSync">
<i v-if="isScrollSync" class="el-icon-lock svg-icon" />
<i v-else class="el-icon-unlock svg-icon" />
</div>
<!-- 截图 -->
<div class="tool-item" :title="$t('trials:reading:button:screenShot')" @click.prevent="saveImage">
<svg-icon icon-class="image" class="svg-icon" />
</div>
<!-- MPR -->
<div class="tool-item" :title="`${$t('trials:reading:button:mpr')}`" @click.prevent="openMPRViewport()"
v-if="((criterionType === 0 && readingTool === 0) || this.readingTool === 3) && !isMPR">
@ -331,7 +320,7 @@
</div>
</div>
<div class="tool-item" :title="$t('trials:reading:button:upload')"
v-if="trialCriterion.ImageUploadEnum > 0 && readingTaskState < 2" v-hasPermi="['role:ir']">
v-if="trialCriterion.ImageUploadEnum > 0" v-hasPermi="['role:ir']">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
<i class="el-icon-upload2 svg-icon" />
@ -392,16 +381,13 @@
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
:histogramVisible="histogramVisible" :actionConfiguration="actionConfiguration"
:SegmentConfig="SegmentConfig" :segmentationId.sync="segId" :segmentIndex.sync="segIndex"
:curSegSeries.sync="curSegSeries" :is-scroll-sync="isScrollSync"
@activeViewport="activeViewport"
:curSegSeries.sync="curSegSeries" @activeViewport="activeViewport"
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
@resetViewport="resetViewport" @resetHistogram="resetHistogram"
@scrollSync="handleViewportScrollSync" v-if="readingTool === 3"
@resetViewport="resetViewport" @resetHistogram="resetHistogram" v-if="readingTool === 3"
v-resize="(e) => handleSizeChange(e, `viewport-${index}`)" />
<Viewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
:is-scroll-sync="isScrollSync" @scrollSync="handleViewportScrollSync"
@activeViewport="activeViewport" @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" v-else />
</div>
@ -813,7 +799,6 @@ export default {
activeViewportWC: null,
clipPlaying: false,
fps: 15,
isScrollSync: false,
manualsDialog: { visible: false, isFullscreen: false, justKeyDoc: false },
hotKeyList: [],
forceFitToWindow: false,
@ -3430,34 +3415,8 @@ export default {
scrollPage(type) {
// if (this.histogramVisible) return false
this.clipPlaying = false
if (this.isScrollSync && !this.isFusion && !this.isMPR && this.cells.length > 1) {
for (let i = 0; i < this.cells.length; i++) {
const viewportRef = this.$refs[`${this.viewportKey}-${i}`]
if (viewportRef && viewportRef[0] && typeof viewportRef[0].scrollPage === 'function') {
viewportRef[0].scrollPage(type)
}
}
return
}
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(type)
},
setImageIndexSync() {
this.isScrollSync = !this.isScrollSync
},
handleViewportScrollSync({ viewportIndex, type, direction }) {
if (!this.isScrollSync || this.isFusion || this.isMPR || this.cells.length <= 1) return
const syncType = type ?? direction
if (viewportIndex !== this.activeViewportIndex || ![-1, 1, 0, 99999].includes(syncType)) return
this.clipPlaying = false
for (let i = 0; i < this.cells.length; i++) {
if (i === viewportIndex) continue
const viewportRef = this.$refs[`${this.viewportKey}-${i}`]
if (viewportRef && viewportRef[0] && typeof viewportRef[0].scrollPage === 'function') {
viewportRef[0].scrollPage(syncType)
}
}
},
//
toggleClipPlay(isPlay) {
// if (this.histogramVisible) return false
@ -3764,7 +3723,7 @@ export default {
})
this.setToolsPassive()
},
async activeSeries(obj, isSave = false) {
async activeSeries(obj) {
if (this.readingTool === 3) {
let res = await this.changeScreenSave()
if (!res) return false
@ -3776,7 +3735,6 @@ export default {
if (!confirm) return false
this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].removeAllNoSaveAnnotation()
} else {
if (isSave) return false
const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
if (!confirm) return false
}
@ -4064,7 +4022,7 @@ export default {
}
let res = await this.openMPRViewport(series)
if (!res) {
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex, true)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
setTimeout(async () => {
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
@ -4576,7 +4534,7 @@ export default {
return new Promise(async (resolve, reject) => {
this.setToolsPassive()
if (this.isMPR) {
this.activeSeries(this.$refs[`viewport-MPR-0`][0].series, true)
this.activeSeries(this.$refs[`viewport-MPR-0`][0].series)
resolve(false)
if (!data) return resolve(false)
let viewportSeries = this.$refs[`viewport-MPR-0`][0].series

View File

@ -804,16 +804,12 @@ export default {
toolGroup.setToolPassive(this.activeTool)
}
this.$emit('update:activeTool', '')
} else if (this.activeTool === name) {
toolGroup.setToolPassive(this.ThresholdTools[1])
this.$emit('update:activeTool', '')
} else {
if (this.activeTool) {
if (toolName === CrosshairsTool.toolName) {
toolGroup.setToolDisabled(this.activeTool)
} else {
toolGroup.setToolPassive(this.activeTool)
toolGroup.setToolPassive(this.ThresholdTools[1])
}
}
toolGroup.setToolActive(toolName, {

View File

@ -24,8 +24,7 @@
</div>
<div>
<span class="study-meta-line" :title="study.Modalities">
<span v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder !== 0" class="study-code"
:title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder !== 0" class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span class="study-modality">{{ `${study.Modalities}(${study.SeriesCount})` }}</span>
<span class="patient-info" v-if="['PT、CT', 'CT、PT', 'PET-CT'].includes(study.Modalities)">
<el-popover placement="right-start" trigger="hover" popper-class="patient-info-popper">
@ -82,7 +81,8 @@
style="position: absolute;right: 0;top: 0;">
<el-popover placement="right" trigger="hover" popper-class="instance_frame_wrapper">
<div class="frame_list">
<div v-for="(instance, idx) in series.InstanceInfoList" :key="instance.Id" class="frame_content"
<div v-for="(instance, idx) in series.InstanceInfoList" :key="instance.Id"
class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < series.InstanceInfoList.length - 1 ? '5px' : '0px' }"
@click.stop="showMultiFrames(index, series, i, instance)">
@ -211,13 +211,12 @@ export default {
if (this.activeNames.indexOf(id) > -1) return
this.activeNames.push(id)
},
setSeriesActive(studyIndex, seriesIndex, isSave = false) {
setSeriesActive(studyIndex, seriesIndex) {
this.activeStudyIndex = studyIndex
this.activeSeriesIndex = seriesIndex
const studyId = this.studyList[studyIndex].StudyId
if (!studyId) return
this.activeStudy(studyId)
if (isSave) return false
DicomEvent.$emit('activeSeries', this.studyList[studyIndex].SeriesList[this.activeSeriesIndex])
},
showMultiFrames(studyIndex, series, seriesIndex, instance) {
@ -489,22 +488,18 @@ export default {
border: 1px solid #2c2c2c;
padding: 5px;
}
.frame_list{
max-height: 500px;
overflow-y: auto;
}
.instance_frame_wrapper ::-webkit-scrollbar {
width: 7px;
height: 7px;
}
.instance_frame_wrapper ::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.frame_content{
height: 50px;
padding: 5px;
@ -514,7 +509,6 @@ export default {
font-size: 12px;
border: 1px solid #404040;
}
.frame_content:hover {
/* font-weight: bold; */
/* box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); */
@ -523,10 +517,10 @@ export default {
border-color: #213a54 !important;
background-color: #213a54;
}
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
}
</style>

View File

@ -117,10 +117,6 @@ export default {
viewportIndex: {
type: Number,
required: true
},
isScrollSync: {
type: Boolean,
default: false
}
},
data() {
@ -195,7 +191,6 @@ export default {
this.element.oncontextmenu = (e) => e.preventDefault()
this.resizeObserver.observe(this.element)
this.element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
this.element.addEventListener('wheel', this.handleWheelSync)
// this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
// this.element.addEventListener('CORNERSTONE_CAMERA_MODIFIED', this.cameraModified)
@ -379,32 +374,6 @@ export default {
toolGroup.setToolEnabled('ScaleOverlay')
}
},
handleWheelSync(e) {
if (!this.isScrollSync) return
const deltaY = typeof e.deltaY === 'number' ? e.deltaY : -e.wheelDeltaY
if (!deltaY) return
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine && renderingEngine.getViewport(this.viewportId)
if (!viewport) return
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
const numImages = viewport.getImageIds().length
if (numImages <= 1) return
const direction = deltaY > 0 ? 1 : -1
let type = direction
if (direction === 1 && currentImageIdIndex >= numImages - 1) {
type = 0
csUtils.jumpToSlice(viewport.element, { imageIndex: 0 })
} else if (direction === -1 && currentImageIdIndex <= 0) {
type = 99999
csUtils.jumpToSlice(viewport.element, { imageIndex: numImages - 1 })
}
this.$emit('scrollSync', { viewportIndex: this.viewportIndex, type })
},
scrollPage(type) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
@ -414,9 +383,9 @@ export default {
if (type === 0) {
newImageIdIndex = 0
} else if (type === -1) {
newImageIdIndex = currentImageIdIndex === 0 ? numImages - 1 : currentImageIdIndex - 1
newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1
} else if (type === 1) {
newImageIdIndex = currentImageIdIndex === numImages - 1 ? 0 : currentImageIdIndex + 1
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
} else if (type === 99999) {
newImageIdIndex = numImages - 1
}

View File

@ -130,10 +130,6 @@ export default {
type: Number,
required: true
},
isScrollSync: {
type: Boolean,
default: false
},
histogramVisible: {
type: Boolean,
default: false
@ -308,7 +304,23 @@ export default {
this.element.addEventListener("CORNERSTONE_VOLUME_NEW_IMAGE", this.stackNewImage)
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
this.element.addEventListener('CORNERSTONE_IMAGE_RENDERED', this.imageRendered)
this.element.addEventListener('wheel', this.handleWheelSync)
this.element.addEventListener('wheel', (e) => {
// if (this.histogramVisible) return false
console.log('CORNERSTONE_STACK_VIEWPORT_SCROLL')
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
const totalImages = this.imageInfo.total;
//
if (currentImageIdIndex >= totalImages - 1 && e.wheelDeltaY < 0) {
//
csUtils.jumpToSlice(viewport.element, { imageIndex: 0 });
} else if (currentImageIdIndex <= 0 && e.wheelDeltaY > 0) {
//
csUtils.jumpToSlice(viewport.element, { imageIndex: totalImages - 1 });
}
});
this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
// this.element.addEventListener(cornerstoneTools.Enums.Events.MOUSE_WHEEL, this.handletoolsMouseWheel)
this.element.addEventListener('mouseleave', () => {
@ -378,30 +390,6 @@ export default {
setFilp(f = false) {
this.isFlip = f
},
handleWheelSync(e) {
const deltaY = typeof e.deltaY === 'number' ? e.deltaY : -e.wheelDeltaY
if (!deltaY) return
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
const totalImages = this.imageInfo.total
if (totalImages <= 1) return
const direction = deltaY > 0 ? 1 : -1
let type = direction
if (currentImageIdIndex >= totalImages - 1 && direction === 1) {
type = 0
csUtils.jumpToSlice(viewport.element, { imageIndex: 0 })
} else if (currentImageIdIndex <= 0 && direction === -1) {
type = 99999
csUtils.jumpToSlice(viewport.element, { imageIndex: totalImages - 1 })
}
if (this.isScrollSync) {
this.$emit('scrollSync', { viewportIndex: this.viewportIndex, type })
}
},
stackNewImage(e) {
if (this.isFlip) return this.isFlip = false
const { detail } = e
@ -585,9 +573,9 @@ export default {
if (type === 0) {
newImageIdIndex = 0
} else if (type === -1) {
newImageIdIndex = currentImageIdIndex === 0 ? numImages - 1 : currentImageIdIndex - 1
newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1
} else if (type === 1) {
newImageIdIndex = currentImageIdIndex === numImages - 1 ? 0 : currentImageIdIndex + 1
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
} else if (type === 99999) {
newImageIdIndex = numImages - 1
}

View File

@ -24,7 +24,7 @@
<template v-if="rerender">
<QuestionFormItem v-for="question of questions" :key="question.Id" :visit-task-id="visitTaskId"
:isNoneDicom="isNoneDicom" :question="question" :question-form="questionForm"
:reading-task-state="readingTaskState" :criterion-id="criterionId" :calculation-list="calculationList"
:reading-task-state="1" :criterion-id="criterionId" :calculation-list="calculationList"
:question-mark-info-list="questionMarkInfoList" :questions-mark-status="questionsMarkStatus"
:questionsSegmentMarkStatus="questionsSegmentMarkStatus" :is-baseline="isBaseLineTask"
@resetFormItemData="resetFormItemData" @setFormItemData="setFormItemData" @getQuestions="getQuestions"
@ -32,7 +32,7 @@
@handleReadingChart="handleReadingChart" @saveSegmentBindingAndAnswer="saveSegmentBindingAndAnswer" />
</template>
<el-form-item v-if="readingTaskState < 2">
<el-form-item>
<div class="action-bar">
<!-- <i class="el-icon-warning feedback-icon" @click="openFeedBackTable"
:style="{ color: taskInfo && taskInfo.IsExistUnprocessedFeedback ? '#ffeb3b' : '#fff' }" /> -->

View File

@ -88,21 +88,21 @@
: $t('trials:pendingReadingTasks:button:review')
" icon="el-icon-edit-outline" @click="handleReadImage(scope.row)" />
<!-- 上传 -->
<!-- <el-button v-if="
<el-button v-if="
item.ImageUploadEnum > 0 &&
item.IsReadingTaskViewInOrder > 1 &&
!scope.row.IsSubjectJudge
" v-hasPermi="['role:ir']" circle icon="el-icon-upload2"
:title="$t('trials:pendingReadingTasks:button:upload')"
@click="openUploadImage(scope.row, item, 'upload')" /> -->
@click="openUploadImage(scope.row, item, 'upload')" />
<!-- 下载 -->
<!-- <el-button v-if="
<el-button v-if="
item.ImageDownloadEnum > 0 &&
item.IsReadingTaskViewInOrder > 1 &&
!scope.row.IsSubjectJudge
" v-hasPermi="['role:ir']" circle icon="el-icon-download"
:title="$t('trials:pendingReadingTasks:button:download')"
@click="openUploadImage(scope.row, item, 'download')" /> -->
@click="openUploadImage(scope.row, item, 'download')" />
</template>
</el-table-column>
</el-table>

View File

@ -1,35 +1,58 @@
<template>
<el-form ref="subjectForm" v-loading="loading" :model="form" :rules="rules" size="small" label-width="200px">
<el-form
ref="subjectForm"
v-loading="loading"
:model="form"
:rules="rules"
size="small"
label-width="200px"
>
<div class="base-dialog-body">
<!-- Site -->
<el-form-item :label="$t('trials:subject:table:site')" prop="TrialSiteId">
<el-select v-model="form.TrialSiteId" clearable filterable style="width:100%"
:disabled="form.StudyCount > 0 || isEdit">
<el-option v-for="item of siteOptions" :key="item.Id" :label="item.TrialSiteCode" :value="item.TrialSiteId" />
<el-select
v-model="form.TrialSiteId"
clearable
filterable
style="width:100%"
:disabled="form.StudyCount>0 || isEdit"
>
<el-option
v-for="item of siteOptions"
:key="item.Id"
:label="item.TrialSiteCode"
:value="item.TrialSiteId"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:subject:table:subjectId')" prop="Code">
<el-input v-model="form.Code" :disabled="form.StudyCount > 0 || isEdit" @input="
form.Code =
form.Code.replace(/[^0-9a-zA-Z\_\-]/g, '')
" />
<span v-show="otherInfo.IsNoticeSubjectCodeRule" style="color:#F56C6C;font-size:12px;">{{
otherInfo.SubjectCodeRule }}</span>
<el-input v-model="form.Code" :disabled="form.StudyCount > 0 || isEdit" />
<span v-show="otherInfo.IsNoticeSubjectCodeRule" style="color:#F56C6C;font-size:12px;">{{ otherInfo.SubjectCodeRule }}</span>
</el-form-item>
<!-- 受试者第二编号 -->
<el-form-item v-if="otherInfo.IsSubjectSecondCodeView" :label="$t('trials:subject:table:medicalNo')"
prop="MedicalNo">
<el-form-item v-if="otherInfo.IsSubjectSecondCodeView" :label="$t('trials:subject:table:medicalNo')" prop="MedicalNo">
<el-input v-model="form.MedicalNo" :disabled="form.StudyCount > 0 || isEdit" />
</el-form-item>
<!-- 访视基准日期 -->
<el-form-item v-show="otherInfo.IsHaveFirstGiveMedicineDate"
:label="$t('trials:subject:table:firstGiveMedicineTime')">
<el-date-picker v-model="form.FirstGiveMedicineTime" type="date" :picker-options="pickerOption"
value-format="yyyy-MM-dd" format="yyyy-MM-dd" />
<el-form-item
v-show="otherInfo.IsHaveFirstGiveMedicineDate"
:label="$t('trials:subject:table:firstGiveMedicineTime')"
>
<el-date-picker
v-model="form.FirstGiveMedicineTime"
type="date"
:picker-options="pickerOption"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
/>
</el-form-item>
<!-- Age -->
<el-form-item v-if="otherInfo.IsHaveSubjectAge" :label="$t('trials:subject:table:age')" prop="Age">
<el-form-item
v-if="otherInfo.IsHaveSubjectAge"
:label="$t('trials:subject:table:age')"
prop="Age"
>
<el-input-number v-model="form.Age" controls-position="right" :min="0" />
</el-form-item>
<!-- Gender -->
@ -50,7 +73,12 @@
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancel">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleCancel"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->

View File

@ -0,0 +1,142 @@
<template>
<div class="json-editor" v-loading="loading">
<div class="header">
<!-- <span class="title">JSON 编辑</span> -->
<el-button type="primary" size="mini" icon="el-icon-check" @click="save">
{{ $t('common:button:save') }}
</el-button>
</div>
<div class="json">
<json-node
:node-key="'root'"
:node-value="jsonData"
:is-root="true"
@update="handleUpdate"
/>
</div>
</div>
</template>
<script>
import { getInspectionById, setJsonDetail } from '@/api/trials'
import JsonNode from './JsonNode.vue'
export default {
name: 'JsonEditor',
components: { JsonNode },
props: {
id: {
type: String,
required: true
}
},
data() {
return {
loading: false,
jsonData: {}
}
},
mounted() {
this.getJsonData()
},
methods: {
handleUpdate({ path, value }) {
const target = this.getTarget(path.slice(0, -1))
this.$set(target, path[path.length - 1], value)
},
getTarget(path) {
let t = this.jsonData
path.forEach(k => { t = t[k] })
return t
},
parseValue(val) {
if (typeof val === 'string') {
try {
const parsed = JSON.parse(val)
if (parsed && typeof parsed === 'object') {
return JSON.parse(JSON.stringify(parsed))
}
} catch (e) {
console.warn(e.message)
}
return {}
}
return val ? JSON.parse(JSON.stringify(val)) : {}
},
async getJsonData() {
try {
this.loading = true
const res = await getInspectionById({ id: this.id })
if (res.IsSuccess) {
this.jsonData = this.parseValue(res.Result.JsonDetail)
}
} catch (err) {
console.log(err)
} finally {
this.loading = false
}
},
isValidJsonObject(obj) {
if (obj === null || typeof obj !== 'object') return false
try {
JSON.stringify(obj)
return true
} catch (e) {
console.warn(e.message)
return false
}
},
safeClone(obj) {
if (this.isValidJsonObject(obj)) {
return JSON.parse(JSON.stringify(obj))
}
return {}
},
async save() {
try {
this.loading = true
const result = this.safeClone(this.jsonData)
const res = await setJsonDetail({ id: this.id, jsonDetail: JSON.stringify(result) })
if (res.IsSuccess) {
this.$emit('getList')
this.$emit('close')
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
} catch (err) {
console.log(err)
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
.json-editor {
font-family: Consolas, Monaco, monospace;
font-size: 13px;
line-height: 2;
padding: 12px;
background: #fafafa;
border-radius: 4px;
}
.header {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #ebeef5;
}
.title {
font-weight: 600;
font-size: 14px;
color: #303133;
}
.json {
max-height: 50vh;
overflow: auto;
}
</style>

View File

@ -0,0 +1,136 @@
<template>
<div class="node" :style="{ paddingLeft: depth * 20 + 'px' }">
<span v-if="!isRoot" class="key">
{{ isArray ? `[${nodeKey}]` : `"${nodeKey}"` }}:
</span>
<!-- 对象 -->
<template v-if="isObject">
<span class="bracket">{</span>
<div class="children">
<json-node
v-for="(v, k) in nodeValue"
:key="k"
:node-key="k"
:node-value="v"
:depth="depth + 1"
:parent-path="currentPath"
@update="$emit('update', $event)"
/>
</div>
<span class="bracket">}</span>
</template>
<!-- 数组 -->
<template v-else-if="isArray">
<span class="bracket">[</span>
<div class="children">
<json-node
v-for="(item, i) in nodeValue"
:key="i"
:node-key="i"
:node-value="item"
:depth="depth + 1"
:parent-path="currentPath"
@update="$emit('update', $event)"
/>
</div>
<span class="bracket">]</span>
</template>
<!-- 文本 -->
<el-input
v-else-if="valueType === 'string'"
v-model="editVal"
size="mini"
style="width: 300px"
@blur="emitUpdate"
@keyup.enter.native="emitUpdate"
/>
<!-- 数字 -->
<el-input-number
v-else-if="valueType === 'number'"
v-model="editVal"
size="mini"
style="width: 140px"
@blur="emitUpdate"
/>
<!-- 布尔值 -->
<el-switch
v-else-if="valueType === 'boolean'"
v-model="editVal"
size="small"
@change="emitUpdate"
/>
<!-- null -->
<el-tag v-else size="mini" type="info">null</el-tag>
</div>
</template>
<script>
export default {
name: 'JsonNode',
props: {
nodeKey: [String, Number],
nodeValue: [Object, Array, String, Number, Boolean],
depth: {
type: Number,
default: 0
},
isRoot: {
type: Boolean,
default: false
},
parentPath: {
type: Array,
default: () => []
}
},
data() {
return { editVal: this.nodeValue }
},
computed: {
isObject() {
return this.nodeValue && typeof this.nodeValue === 'object' && !Array.isArray(this.nodeValue)
},
isArray() {
return Array.isArray(this.nodeValue)
},
valueType() {
if (this.nodeValue === null) return 'null'
return typeof this.nodeValue
},
currentPath() {
return this.isRoot ? [] : [...this.parentPath, this.nodeKey]
}
},
watch: {
nodeValue(v) { this.editVal = v }
},
methods: {
emitUpdate() {
this.$emit('update', { path: this.currentPath, value: this.editVal })
}
}
}
</script>
<style scoped>
.node {
padding-bottom: 5px;
}
.key {
color: #c7254e;
margin-right: 4px;
}
.bracket {
color: #e6a23c;
font-weight: bold;
}
.children {
padding-left: 4px;
}
</style>

View File

@ -194,6 +194,9 @@
<el-button type="text" @click="lookDetails(scope.row)">
{{ $t('trials:auditRecord:action:detail') }}
</el-button>
<el-button type="text" @click="edit(scope.row)">
{{ $t('common:button:edit') }}
</el-button>
</template>
</el-table-column>
</el-table>
@ -622,6 +625,20 @@
<!-- :src="openImageUrl"-->
<!-- style="width: 50px;height: 50px; cursor:pointer">-->
<!-- </div>-->
<!-- <json-editor v-model="data" /> -->
<el-dialog
v-if="editDialogVisible"
:visible.sync="editDialogVisible"
:close-on-click-modal="false"
:title="$t('common:button:edit') + `${$i18n.locale === 'zh' ? '' + editRow.DescriptionCN + '' : '( ' + editRow.Description + ' )'}`"
width="720px"
custom-class="base-dialog-wrapper">
<json-editor
:id="editRow.Id"
@close="editDialogVisible = false"
@getList="getList"
/>
</el-dialog>
</template>
</BaseContainer>
</template>
@ -630,11 +647,12 @@
import {
getTrialSiteSelect,
getTrialVisitStageSelect,
getTrialCriterionList
getTrialCriterionList,
} from '@/api/trials'
import { getInspectionList, getInspectionJsonDataList } from '@/api/trials/inspection'
import { getFrontAuditConfigList, getAuditConfigChildList, getModuleTypeDescriptionList, setInspectionEnumValue, getModuleTypeList } from '@/api/dictionary/checkConfig'
import Pagination from '@/components/Pagination'
import JsonEditor from './components/JsonEditor'
import BaseContainer from '@/components/BaseContainer'
import BaseModel from '@/components/BaseModel'
import { getToken } from '@/utils/auth'
@ -670,7 +688,7 @@ const searchDataDefault = () => {
}
}
export default {
components: { BaseContainer, Pagination, BaseModel },
components: { BaseContainer, Pagination, JsonEditor, BaseModel },
dicts: ['OptType', 'ModuleType', 'ChildrenType'],
data() {
@ -708,7 +726,10 @@ export default {
imagesList: [],
moduleTypeList: [],
list2_total: 0,
list2_searchData: {}
list2_searchData: {},
editDialogVisible: false,
jsonData: null,
editRow: null
}
},
mounted() {
@ -1346,7 +1367,12 @@ export default {
}).catch(() => {
this.loading = false
})
}
},
edit(row) {
this.editRow = Object.assign({}, row)
this.editDialogVisible = true
},
}
}
</script>

View File

@ -1144,7 +1144,7 @@ export default {
var instanceItem = instanceList.find(function (item) {
return item.instanceUid === instanceUid
})
if (!instanceItem) {
if (!false) {
var date = data.string('x00080023')
var time = data.string('x00080033')
var instanceTime = ''
@ -1572,58 +1572,60 @@ export default {
try {
let o = v.instanceList[ii]
let name = `${v.instanceList[ii].file.webkitRelativePath}_${v.instanceList[ii].instanceUid}`
if (o.isReUpload) {
dicomInfo.failedFileCount++
dicomInfo.uploadFileSize += o.file.size
Record.Existed.push(name)
Record.FileCount++
} else if (o.myPath) {
instanceList.push({
studyInstanceUid: dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
SOPClassUID: o.SOPClassUID,
TransferSytaxUID: o.TransferSytaxUID,
MediaStorageSOPInstanceUID:
o.MediaStorageSOPInstanceUID,
MediaStorageSOPClassUID:
o.MediaStorageSOPClassUID,
sopInstanceUid: o.instanceUid,
instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime,
imageRows: o.imageRows,
imageColumns: o.imageColumns,
sliceLocation: o.sliceLocation,
sliceThickness: o.sliceThickness,
numberOfFrames: o.numberOfFrames,
pixelSpacing: o.pixelSpacing,
imagerPixelSpacing: o.imagerPixelSpacing,
frameOfReferenceUID: o.frameOfReferenceUID,
windowCenter: o.windowCenter,
windowWidth: o.windowWidth,
path: o.myPath,
FileSize: o.FileSize,
// if (o.isReUpload) {
// dicomInfo.failedFileCount++
// dicomInfo.uploadFileSize += o.file.size
// Record.Existed.push(name)
// Record.FileCount++
// }
// else if (o.myPath) {
// instanceList.push({
// studyInstanceUid: dicomInfo.studyUid,
// seriesInstanceUid: v.seriesUid,
// SOPClassUID: o.SOPClassUID,
// TransferSytaxUID: o.TransferSytaxUID,
// MediaStorageSOPInstanceUID:
// o.MediaStorageSOPInstanceUID,
// MediaStorageSOPClassUID:
// o.MediaStorageSOPClassUID,
// sopInstanceUid: o.instanceUid,
// instanceNumber: o.instanceNumber,
// instanceTime: o.instanceTime,
// imageRows: o.imageRows,
// imageColumns: o.imageColumns,
// sliceLocation: o.sliceLocation,
// sliceThickness: o.sliceThickness,
// numberOfFrames: o.numberOfFrames,
// pixelSpacing: o.pixelSpacing,
// imagerPixelSpacing: o.imagerPixelSpacing,
// frameOfReferenceUID: o.frameOfReferenceUID,
// windowCenter: o.windowCenter,
// windowWidth: o.windowWidth,
// path: o.myPath,
// FileSize: o.FileSize,
PhotometricInterpretation:
o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient:
o.ImageOrientationPatient,
SequenceOfUltrasoundRegions:
o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage,
Units: o.Units,
DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument,
})
Record.Uploaded.push(name)
dicomInfo.failedFileCount++
Record.FileCount++
} else {
// PhotometricInterpretation:
// o.PhotometricInterpretation,
// BitsAllocated: o.BitsAllocated,
// PixelRepresentation: o.PixelRepresentation,
// RescaleIntercept: o.RescaleIntercept,
// RescaleSlope: o.RescaleSlope,
// ImagePositionPatient: o.ImagePositionPatient,
// ImageOrientationPatient:
// o.ImageOrientationPatient,
// SequenceOfUltrasoundRegions:
// o.SequenceOfUltrasoundRegions,
// FrameTime: o.FrameTime,
// CorrectedImage: o.CorrectedImage,
// Units: o.Units,
// DecayCorrection: o.DecayCorrection,
// EncapsulatedDocument: o.EncapsulatedDocument,
// })
// Record.Uploaded.push(name)
// dicomInfo.failedFileCount++
// Record.FileCount++
// }
// else {
let path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/${dicomInfo.studyUid
}/${scope.getGuid(
@ -1761,7 +1763,7 @@ export default {
Record.Failed.push(name)
Record.FileCount++
}
}
// }
resolve1()
} catch (e) {
console.log(e)

View File

@ -300,19 +300,7 @@
<!-- 审核完成时间 -->
<el-table-column prop="AuditTime" :label="$t('trials:crcUpload:table:auditTime')" show-overflow-tooltip
width="170" />
<el-table-column v-if="
hasPermi(
[
'trials:trials-panel:visit:crc-upload:upload',
'trials:trials-panel:visit:crc-upload:submit',
'trials:trials-panel:visit:crc-upload:edit',
'trials:trials-panel:visit:crc-upload:delete',
'trials:trials-panel:visit:crc-upload:back',
'trials:trials-panel:visit:crc-upload:authBack'
],
'||'
)
" :label="$t('common:action:action')" width="250" fixed="right">
<el-table-column :label="$t('common:action:action')" width="250" fixed="right">
<template slot-scope="scope">
<!-- 上传 -->
<el-button v-hasPermi="['trials:trials-panel:visit:crc-upload:upload']" icon="el-icon-upload2" :disabled="!isOCTorIVUS && (scope.row.SubmitState * 1 === 2 ||
@ -320,10 +308,8 @@
scope.row.IsLostVisit || (scope.row.IsSubjectQuit && scope.row.SubmitState * 1 !== 2))
" circle :title="$t('trials:crcUpload:action:upload')" @click="CRChandleUpload(scope.row)" />
<!-- 上传2 -->
<el-button icon="el-icon-upload2" v-hasPermi="['trials:trials-panel:visit:crc-upload:upload2']" :disabled="!isOCTorIVUS && (scope.row.SubmitState * 1 === 2 ||
scope.row.VisitExecuted === 2 ||
scope.row.IsLostVisit || (scope.row.IsSubjectQuit && scope.row.SubmitState * 1 !== 2))
" circle :title="$t('trials:crcUpload:action:upload')" @click="CRChandleUpload2(scope.row)" />
<el-button icon="el-icon-upload2"
circle :title="$t('trials:crcUpload:action:upload')" @click="CRChandleUpload2(scope.row)" />
<!-- 提交 -->
<el-button v-hasPermi="['trials:trials-panel:visit:crc-upload:submit']" icon="el-icon-check" :disabled="scope.row.AuditState * 1 > 0 ||
scope.row.SubmitState * 1 !== 1 ||

View File

@ -644,7 +644,7 @@
<el-input v-model="studyForm.Modalities" disabled />
</el-form-item>
<!-- 检查类型 -->
<el-form-item v-else :label="$t('trials:audit:table:modality')" prop="Modality" :rules="[
<el-form-item v-else :label="$t('trials:audit:table:modality')" prop="Modalities" :rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),