Compare commits

..

20 Commits
temp ... main

Author SHA1 Message Date
caiyiling 0b9f33b36f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-08-07 16:03:19 +08:00
caiyiling 8bdac9bd6e 阅片工具更改 2026-08-07 16:02:57 +08:00
wangxiaoshuang 97a406d32a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2026-08-07 14:08:00 +08:00
wangxiaoshuang f47db19702 下载优化 2026-08-07 14:07:50 +08:00
caiyiling f3610b685a Merge commit '5bbbd9334e9ad13c4dd2cf02b7c348278e777feb' into main
continuous-integration/drone/push Build is passing Details
2026-08-07 09:16:44 +08:00
wangxiaoshuang 3cf1cf5f91 下载优化
continuous-integration/drone/push Build is passing Details
2026-08-06 15:56:17 +08:00
wangxiaoshuang 8315bcf59f 【dicom分割】MPR横截面的普通标记绑定保存时会提示是否切换至二维视图
continuous-integration/drone/push Build is passing Details
2026-08-06 15:16:31 +08:00
wangxiaoshuang 653faaf4ba 【ivus&oct】靶段标注上传页面,非dicom鼠标悬浮的位置和高亮显示的行不一致
continuous-integration/drone/push Build is passing Details
2026-08-06 14:38:37 +08:00
wangxiaoshuang aa92776b31 【受试者】受试者创建时,受试者名称禁用特殊符号
continuous-integration/drone/push Build is passing Details
2026-08-06 09:52:03 +08:00
caiyiling 5bbbd9334e ivus & oct 导表后自动保存逻辑更改
continuous-integration/drone/push Build is passing Details
2026-08-05 16:48:41 +08:00
wangxiaoshuang fcd6efaa39 Merge branch 'uat'
continuous-integration/drone/push Build is pending Details
2026-08-05 14:11:15 +08:00
caiyiling b7de254b5f Merge commit 'c53890add7987e6753ded211b8675a0a1853f56a' into main
continuous-integration/drone/push Build is passing Details
2026-08-05 10:16:12 +08:00
caiyiling 9256970662 IVUS & OCT标准更改
continuous-integration/drone/push Build is passing Details
2026-08-05 09:08:46 +08:00
wangxiaoshuang c53890add7 质控dicom编辑检查类型报错修改
continuous-integration/drone/push Build is passing Details
2026-08-04 13:14:58 +08:00
caiyiling 23fb7be52b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-08-03 14:44:37 +08:00
caiyiling 4e5c9aaf38 质控页面注册自定义metaDataProvider逻辑更改 2026-08-03 14:43:44 +08:00
wangxiaoshuang 52bb39d6c2 下载优化
continuous-integration/drone/push Build is passing Details
2026-08-03 11:49:09 +08:00
wangxiaoshuang cbd860a35e 先下载再上传的逻辑指定IVUS和OCT标准
continuous-integration/drone/push Build is passing Details
2026-07-31 15:01:02 +08:00
wangxiaoshuang 7e9eb1f3a5 待阅列表屏蔽上传下载按钮
continuous-integration/drone/push Build is passing Details
2026-07-31 11:00:34 +08:00
wangxiaoshuang 254e43ac3d 分割球体工具bug修复
continuous-integration/drone/push Build is passing Details
2026-07-30 10:47:35 +08:00
29 changed files with 574 additions and 672 deletions

View File

@ -4522,24 +4522,4 @@ export function addOrUpdateCommonUploadRecord(data) {
method: 'post',
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 from '@/utils/metaDataProvider'
import metaDataProvider, { registerImageSharedCacheKey, unregisterImageSharedCacheKeys } from '@/utils/metaDataProvider'
const scroll = cornerstoneTools.import('util/scrollToIndex')
import Hammer from 'hammerjs'
@ -211,6 +211,9 @@ export default {
this.stackScrollCallback
)
},
beforeDestroy() {
unregisterImageSharedCacheKeys(this.stack?.imageIds)
},
methods: {
loadImageStack(dicomSeries, text = '') {
@ -400,6 +403,10 @@ 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,7 +477,10 @@ export default {
let res = await getSubjectImageUploadList(params)
this.loading = false
if (res.IsSuccess) {
this.isDownloaded = res.OtherInfo.IsIRImageDownloaded
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.TrialModality = res.OtherInfo.TrialModality.split('|')
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder || res.OtherInfo.IsReadingTaskViewInOrder === 0 ? res.OtherInfo.IsReadingTaskViewInOrder : 2
this.StudyInstanceUidList = []
@ -955,7 +958,7 @@ export default {
var instanceItem = instanceList.find(function (item) {
return item.instanceUid === instanceUid
})
if (!false) {
if (!instanceItem) {
var date = data.string('x00080023')
var time = data.string('x00080033')
var instanceTime = ''
@ -1251,60 +1254,58 @@ 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(
@ -1445,7 +1446,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')" fixed="right" width="180">
<el-table-column :label="$t('common:action:action')" width="180">
<template slot-scope="scope">
<!--预览--->
<el-button circle :disabled="scope.row.UploadedFileCount <= 0" icon="el-icon-view"
@ -353,7 +353,10 @@ export default {
if (res.IsSuccess) {
this.list = res.Result
this.relationInfo = res.OtherInfo
this.isDownloaded = res.OtherInfo.IsIRImageDownloaded
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.faccept = []
this.relationInfo.ImageFormatList.forEach((item) => {
this.faccept.push(`.${item}`)

View File

@ -119,6 +119,25 @@ 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);
@ -257,10 +276,20 @@ function getSpacingBetweenSlices(dataSet) {
}
function metaDataProvider(type, imageId) {
const parsedImageId = parseImageId(imageId);
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
let 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;
}
@ -421,7 +450,6 @@ function metaDataProvider(type, imageId) {
let columnPixelSpacing = null;
let rowPixelSpacing = null;
if (pixelSpacing) {
rowPixelSpacing = pixelSpacing[0];
columnPixelSpacing = pixelSpacing[1];

View File

@ -11,10 +11,11 @@ function zipFiles(zipName, files) {
store.dispatch('trials/setUnLock', true)
files = formatFiles(files)
// 创建压缩文件输出流
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
let zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
const readableZipStream = new ZIP({
let Url = [];
let readableZipStream = new ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
@ -23,6 +24,7 @@ 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 => {
@ -40,13 +42,31 @@ 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) {
@ -61,15 +81,17 @@ async function updateFile(file, name) {
return new Promise(async resolve => {
try {
store.dispatch('trials/setUnLock', true)
const fileOutputStream = streamSaver.createWriteStream(name);
let 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)
@ -110,13 +132,17 @@ function downloadFileToUrl(url) {
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
const arraybuffer = xhr.response;
let dataset = dcmjs.data.DicomMessage.readFile(arraybuffer)
let newDicomFile = dataset.write({ fragmentMultiframe: false, startOffset: false }) // fragmentMultiframe 原始数据是否进行分割
const bufferArray = new Uint8Array(newDicomFile)
const blob = new Blob([bufferArray], { type: 'application/octet-stream' })
const href = URL.createObjectURL(blob)
resolve(href)
try {
const arraybuffer = xhr.response;
let dataset = dcmjs.data.DicomMessage.readFile(arraybuffer)
let newDicomFile = dataset.write({ fragmentMultiframe: false, startOffset: false }) // fragmentMultiframe 原始数据是否进行分割
const bufferArray = new Uint8Array(newDicomFile)
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 from '@/utils/metaDataProvider'
import metaDataProvider, { registerImageSharedCacheKey, unregisterImageSharedCacheKeys } from '@/utils/metaDataProvider'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
@ -480,6 +480,7 @@ export default {
},
beforeDestroy() {
// Store.$off('getMeasureData')
unregisterImageSharedCacheKeys(this.stack?.imageIds)
DicomEvent.$off('setReadingState')
},
methods: {
@ -1279,6 +1280,7 @@ 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,11 +189,22 @@
</template>
<!-- 计算值 -->
<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="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="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
:group-classify="5" />
<!-- 新增编辑弹窗 -->
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
@ -622,7 +633,7 @@ export default {
return v
}
},
async handleSave(index) {
async handleSave(index, options = {}) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
this.$set(this.rowSaveLoadingMap, index, true)
@ -640,9 +651,13 @@ export default {
answers: answers
}
await saveTaskQuestion(12, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.formChanged = false
} catch (e) {
console.log(e)
@ -858,9 +873,29 @@ export default {
}
},
async uploadDlgClose() {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
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.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')

View File

@ -189,11 +189,22 @@
</template>
<!-- 计算值 -->
<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="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="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
:group-classify="5" />
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
:title="addOrEdit.title" width="500px">
@ -617,7 +628,7 @@ export default {
return v
}
},
async handleSave(index) {
async handleSave(index, options = {}) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
this.$set(this.rowSaveLoadingMap, index, true)
@ -635,9 +646,13 @@ export default {
answers: answers
}
await saveTaskQuestion(12, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.formChanged = false
} catch (e) {
console.log(e)
@ -884,9 +899,29 @@ export default {
}
},
async uploadDlgClose() {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
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.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')

View File

@ -279,84 +279,86 @@ export default {
})
},
handleSave() {
if (this.saveLoading) return
async handleSave(options = {}) {
if (this.saveLoading) return false
this.saveLoading = true
this.$refs['questions'].validate(async (valid) => {
if (!valid) {
this.saveLoading = false
return
}
// lugano
if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 3) {
//
var existUnSave = this.checkAnnotationStatus(this.questions)
if (existUnSave) {
this.$alert(this.$t('trials:lugano:message:saveWarning'), this.$t('trials:lugano:fusionDialog:warning'))
this.saveLoading = false
return
}
var currentSpleenStatus = this.questionForm[this.spleenStatusId]
var currentSpleenLength = this.questionForm[this.spleenLengthId]
currentSpleenStatus = isNaN(parseInt(currentSpleenStatus)) ? null : parseInt(currentSpleenStatus)
var stIdx = this.measurements.findIndex(i => i.QuestionType === 60)
var slIdx = this.measurements.findIndex(i => i.QuestionType === 61)
if (currentSpleenLength && currentSpleenStatus === 5) {
// '!'
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
} else if (currentSpleenStatus === 5 && ((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData))) {
//
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
} else if (((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData)) && !currentSpleenLength) {
this.$alert(this.$t('trials:lugano:message:validSpleen2'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
} else {
this.saveQuestionsList()
}
} 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
if (this.criterionType === 18) {
res = await getCanChooseNotMergeNoPet({ visitTaskId: this.visitTaskId })
} else {
res = await getCanChooseNotMerge({ visitTaskId: this.visitTaskId })
}
if (res.IsSuccess && !res.Result.IsCanChooseNotMerge) {
// 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
} else {
// PET-CTFDG-PETNE
this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.saveQuestionsList()
})
}
} else {
this.saveQuestionsList()
}
// this.saveQuestionsList()
const valid = await new Promise(resolve => {
this.$refs['questions'].validate(v => resolve(v))
})
if (!valid) {
this.saveLoading = false
return false
}
// 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.saveLoading = false
return false
}
var currentSpleenStatus = this.questionForm[this.spleenStatusId]
var currentSpleenLength = this.questionForm[this.spleenLengthId]
currentSpleenStatus = isNaN(parseInt(currentSpleenStatus)) ? null : parseInt(currentSpleenStatus)
var stIdx = this.measurements.findIndex(i => i.QuestionType === 60)
var slIdx = this.measurements.findIndex(i => i.QuestionType === 61)
if (currentSpleenLength && currentSpleenStatus === 5) {
// '!'
await 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'), {
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'), {
callback: _ => { }
})
this.saveLoading = false
return false
}
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
if (this.criterionType === 18) {
res = await getCanChooseNotMergeNoPet({ visitTaskId: this.visitTaskId })
} else {
res = await getCanChooseNotMerge({ visitTaskId: this.visitTaskId })
}
if (res.IsSuccess && !res.Result.IsCanChooseNotMerge) {
await this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
callback: action => { }
})
this.saveLoading = false
return false
}
// PET-CTFDG-PETNE
const confirm = await this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => true).catch(() => false)
if (!confirm) {
this.saveLoading = false
return false
}
return await this.saveQuestionsList(options)
} else {
return await this.saveQuestionsList(options)
}
},
async saveQuestionsList() {
async saveQuestionsList(options = {}) {
this.loading = true
var answers = []
var imageQuality = null
@ -382,18 +384,22 @@ export default {
// })
// params.questionMarkInfoList = questionMarkInfoList
// }
saveTaskQuestion(this.questionType, params).then(async res => {
try {
const res = await saveTaskQuestion(this.questionType, params)
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')
}
}
this.$message.success(this.$t('common:message:savedSuccessfully'))
var trialId = this.$route.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: trialId, visitTaskId: this.visitTaskId })
this.getQuestions(this.visitTaskId, true)
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)
@ -417,10 +423,12 @@ export default {
}
this.formChanged = false
this.saveLoading = false
}).catch(() => {
return res
} catch (e) {
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 from '@/utils/metaDataProvider'
import metaDataProvider, { registerImageSharedCacheKey, unregisterImageSharedCacheKeys } from '@/utils/metaDataProvider'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
@ -473,6 +473,7 @@ export default {
},
beforeDestroy() {
// Store.$off('getMeasureData')
unregisterImageSharedCacheKeys(this.stack?.imageIds)
DicomEvent.$off('setReadingState')
DicomEvent.$off('updateImage')
},
@ -1186,6 +1187,7 @@ 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,10 +309,9 @@
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageUploadEnum > 0"
class="item" effect="dark"
:content="$t('trials:reading:button:upload')"
<el-tooltip v-if="
trialCriterion.ImageUploadEnum > 0 && currentReadingTaskState < 2
" v-hasPermi="['role:ir']" class="item" effect="dark" :content="$t('trials:reading:button:upload')"
placement="bottom">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
@ -412,7 +411,7 @@
<WL v-if="activeName === '2'" @getWwcTpl="getWwcTpl" />
</el-tab-pane>
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="activeName === '3'" :imageToolType="1"/>
<Others v-if="activeName === '3'" :imageToolType="1" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@ -449,10 +448,10 @@
/> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :SubjectId="uploadSubjectId" :SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible" :VisitTaskId="taskId"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion" :TaskId="taskId"
:visible.sync="downloadImageVisible" />
:visible.sync="downloadImageVisible" :isReading="true"/>
</div>
</template>
<script>

View File

@ -128,7 +128,7 @@ export default {
immediate: true,
handler(val) {
if (val) {
this.readingTaskState = 1
this.readingTaskState = val
}
}
}
@ -169,7 +169,7 @@ export default {
if (i > -1) {
this.visitTaskId = this.visitTaskList[i].VisitTaskId
this.taskBlindName = this.visitTaskList[i].TaskBlindName
this.readingTaskState = 1
this.readingTaskState = this.visitTaskList[i].ReadingTaskState
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 = 1
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 1 })
DicomEvent.$emit('setReadingState', 1)
this.readingTaskState = 2
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
DicomEvent.$emit('setReadingState', 2)
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">
<el-form v-if="isRender" ref="questions" size="small" :model="questionForm" :disabled="readingTaskState >= 2">
<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 = 1
DicomEvent.$emit('setReadingState', 1)
this.readingTaskState = res.OtherInfo.ReadingTaskState
DicomEvent.$emit('setReadingState', res.OtherInfo.ReadingTaskState)
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;">
<div style="text-align:right;margin-top: 10px;" v-if="!IsViewStudyPart">
<!-- 确认 -->
<el-button type="primary" @click="handleConfirmBP">{{ $t('trials:reading:button:confirm')
}}</el-button>
@ -223,7 +223,8 @@ export default {
readingVersionEnum: null,
dialogBodyPartVisible: false,
isReadKeyFile: true,
isHaveKeyFile: false
isHaveKeyFile: false,
IsViewStudyPart: true
}
},
computed: {

View File

@ -160,6 +160,17 @@
<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">
@ -320,7 +331,7 @@
</div>
</div>
<div class="tool-item" :title="$t('trials:reading:button:upload')"
v-if="trialCriterion.ImageUploadEnum > 0" v-hasPermi="['role:ir']">
v-if="trialCriterion.ImageUploadEnum > 0 && readingTaskState < 2" v-hasPermi="['role:ir']">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
<i class="el-icon-upload2 svg-icon" />
@ -381,13 +392,16 @@
: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" @activeViewport="activeViewport"
:curSegSeries.sync="curSegSeries" :is-scroll-sync="isScrollSync"
@activeViewport="activeViewport"
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
@resetViewport="resetViewport" @resetHistogram="resetHistogram" v-if="readingTool === 3"
@resetViewport="resetViewport" @resetHistogram="resetHistogram"
@scrollSync="handleViewportScrollSync" 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>
@ -799,6 +813,7 @@ export default {
activeViewportWC: null,
clipPlaying: false,
fps: 15,
isScrollSync: false,
manualsDialog: { visible: false, isFullscreen: false, justKeyDoc: false },
hotKeyList: [],
forceFitToWindow: false,
@ -3415,8 +3430,34 @@ 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
@ -3723,7 +3764,7 @@ export default {
})
this.setToolsPassive()
},
async activeSeries(obj) {
async activeSeries(obj, isSave = false) {
if (this.readingTool === 3) {
let res = await this.changeScreenSave()
if (!res) return false
@ -3735,6 +3776,7 @@ 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
}
@ -4022,7 +4064,7 @@ export default {
}
let res = await this.openMPRViewport(series)
if (!res) {
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex, true)
setTimeout(async () => {
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
@ -4534,7 +4576,7 @@ export default {
return new Promise(async (resolve, reject) => {
this.setToolsPassive()
if (this.isMPR) {
this.activeSeries(this.$refs[`viewport-MPR-0`][0].series)
this.activeSeries(this.$refs[`viewport-MPR-0`][0].series, true)
resolve(false)
if (!data) return resolve(false)
let viewportSeries = this.$refs[`viewport-MPR-0`][0].series

View File

@ -804,12 +804,16 @@ 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

@ -14,7 +14,7 @@
<div class="ps">
<el-collapse v-model="activeNames">
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`" :name="`${study.StudyId}`">
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`" :name="`${study.StudyId}`">
<template slot="title">
<div v-if="!study.IsCriticalSequence" class="dicom-desc">
<div style="text-overflow: ellipsis;overflow: hidden;">
@ -24,7 +24,8 @@
</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">
@ -81,8 +82,7 @@
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)">
@ -130,7 +130,7 @@
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
@ -205,18 +205,19 @@ export default {
this.activeStudyIndex = studyIndex
this.activeSeriesIndex = seriesIndex
this.activeInstanceId = null
},
},
activeStudy(id) {
console.log('activeStudy')
if (this.activeNames.indexOf(id) > -1) return
this.activeNames.push(id)
},
setSeriesActive(studyIndex, seriesIndex) {
setSeriesActive(studyIndex, seriesIndex, isSave = false) {
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) {
@ -482,25 +483,29 @@ export default {
<style lang="scss">
.study-wrapper {
.instance_frame_wrapper{
.instance_frame_wrapper {
min-width: 120px;
background-color: #2c2c2c;
border: 1px solid #2c2c2c;
padding: 5px;
}
.frame_list{
.frame_list {
max-height: 500px;
overflow-y: auto;
}
.instance_frame_wrapper ::-webkit-scrollbar {
width: 7px;
height: 7px;
}
.instance_frame_wrapper ::-webkit-scrollbar-thumb {
.instance_frame_wrapper ::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.frame_content{
.frame_content {
height: 50px;
padding: 5px;
display: flex;
@ -509,6 +514,7 @@ 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); */
@ -517,10 +523,10 @@ export default {
border-color: #213a54 !important;
background-color: #213a54;
}
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
}
</style>

View File

@ -117,6 +117,10 @@ export default {
viewportIndex: {
type: Number,
required: true
},
isScrollSync: {
type: Boolean,
default: false
}
},
data() {
@ -191,6 +195,7 @@ 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)
@ -374,6 +379,32 @@ 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)
@ -383,9 +414,9 @@ export default {
if (type === 0) {
newImageIdIndex = 0
} else if (type === -1) {
newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1
newImageIdIndex = currentImageIdIndex === 0 ? numImages - 1 : currentImageIdIndex - 1
} else if (type === 1) {
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
newImageIdIndex = currentImageIdIndex === numImages - 1 ? 0 : currentImageIdIndex + 1
} else if (type === 99999) {
newImageIdIndex = numImages - 1
}

View File

@ -130,6 +130,10 @@ export default {
type: Number,
required: true
},
isScrollSync: {
type: Boolean,
default: false
},
histogramVisible: {
type: Boolean,
default: false
@ -304,23 +308,7 @@ 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', (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('wheel', this.handleWheelSync)
this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
// this.element.addEventListener(cornerstoneTools.Enums.Events.MOUSE_WHEEL, this.handletoolsMouseWheel)
this.element.addEventListener('mouseleave', () => {
@ -390,6 +378,30 @@ 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
@ -573,9 +585,9 @@ export default {
if (type === 0) {
newImageIdIndex = 0
} else if (type === -1) {
newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1
newImageIdIndex = currentImageIdIndex === 0 ? numImages - 1 : currentImageIdIndex - 1
} else if (type === 1) {
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
newImageIdIndex = currentImageIdIndex === numImages - 1 ? 0 : 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="1" :criterion-id="criterionId" :calculation-list="calculationList"
:reading-task-state="readingTaskState" :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>
<el-form-item v-if="readingTaskState < 2">
<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,58 +1,35 @@
<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" />
<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" @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-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 -->
@ -73,12 +50,7 @@
<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

@ -1,142 +0,0 @@
<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

@ -1,136 +0,0 @@
<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,9 +194,6 @@
<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>
@ -625,20 +622,6 @@
<!-- :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>
@ -647,12 +630,11 @@
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'
@ -688,7 +670,7 @@ const searchDataDefault = () => {
}
}
export default {
components: { BaseContainer, Pagination, JsonEditor, BaseModel },
components: { BaseContainer, Pagination, BaseModel },
dicts: ['OptType', 'ModuleType', 'ChildrenType'],
data() {
@ -726,10 +708,7 @@ export default {
imagesList: [],
moduleTypeList: [],
list2_total: 0,
list2_searchData: {},
editDialogVisible: false,
jsonData: null,
editRow: null
list2_searchData: {}
}
},
mounted() {
@ -1367,12 +1346,7 @@ 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 (!false) {
if (!instanceItem) {
var date = data.string('x00080023')
var time = data.string('x00080033')
var instanceTime = ''
@ -1572,60 +1572,58 @@ 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(
@ -1763,7 +1761,7 @@ export default {
Record.Failed.push(name)
Record.FileCount++
}
// }
}
resolve1()
} catch (e) {
console.log(e)

View File

@ -300,7 +300,19 @@
<!-- 审核完成时间 -->
<el-table-column prop="AuditTime" :label="$t('trials:crcUpload:table:auditTime')" show-overflow-tooltip
width="170" />
<el-table-column :label="$t('common:action:action')" width="250" fixed="right">
<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">
<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 ||
@ -308,8 +320,10 @@
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"
circle :title="$t('trials:crcUpload:action:upload')" @click="CRChandleUpload2(scope.row)" />
<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 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="Modalities" :rules="[
<el-form-item v-else :label="$t('trials:audit:table:modality')" prop="Modality" :rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),