Compare commits

...

17 Commits

Author SHA1 Message Date
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
caiyiling 197b3213e7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main 2026-07-29 16:06:05 +08:00
caiyiling 3bfda749d8 质控预览非DICOM文件处理zip类型的数据 2026-07-29 16:05:06 +08:00
wangxiaoshuang 6f911d0d74 锁定蒙版添加白名单
continuous-integration/drone/push Build is passing Details
2026-07-29 15:53:17 +08:00
caiyiling 78f4a78994 测量数据预览按钮控制更改
continuous-integration/drone/push Build is passing Details
2026-07-29 09:53:43 +08:00
caiyiling 4e1911223d 1
continuous-integration/drone/push Build is passing Details
2026-07-28 09:17:40 +08:00
caiyiling 7dd02e84aa 样式调整
continuous-integration/drone/push Build is passing Details
2026-07-27 15:38:03 +08:00
caiyiling 7f260799d8 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-07-27 15:22:13 +08:00
caiyiling 06c7a8126b IVUS & OCT测量表格添加新开页签预览功能 2026-07-27 15:21:30 +08:00
wangxiaoshuang 78624d9cce 阅片中上传影像需要在下载影像后才能上传
continuous-integration/drone/push Build is passing Details
2026-07-27 14:28:55 +08:00
caiyiling 6ecc0e58e9 重置标记更改;标记定位更改
continuous-integration/drone/push Build is passing Details
2026-07-16 15:40:28 +08:00
wangxiaoshuang 84a81e1851 临床数据输入框禁用样式修改
continuous-integration/drone/push Build is passing Details
2026-07-16 14:12:04 +08:00
wangxiaoshuang b09209af31 完全随机分割问题绑定弹框不显示检查编号显示检查模态
continuous-integration/drone/push Build is passing Details
2026-07-16 13:30:48 +08:00
caiyiling 9892070ccc 播放功能修改
continuous-integration/drone/push Build is passing Details
2026-07-16 11:43:10 +08:00
wangxiaoshuang 63cbc8746f 完全随机融合弹框不显示检查号显示检查模态
continuous-integration/drone/push Build is passing Details
2026-07-16 10:54:55 +08:00
26 changed files with 509 additions and 92 deletions

View File

@ -98,6 +98,7 @@ import timeTag from '@/components/timeTag'
import Vue from 'vue'
import i18n from './lang'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import WHITELIST from "./utils/whiteList"
export default {
name: 'App',
components: {
@ -156,7 +157,7 @@ export default {
// },
methods: {
getIsLock() {
if (zzSessionStorage.getItem('isLock') === 'true') {
if (zzSessionStorage.getItem('isLock') === 'true' && !WHITELIST.includes(this.$route.path)) {
this.isLock = true
} else {
this.isLock = false

View File

@ -480,3 +480,11 @@ export function getNoneDicomMarkList(data) {
data
})
}
export function getFilterTableQuestion(data) {
return request({
url: `/ReadingImageTask/getFilterTableQuestion`,
method: 'post',
data
})
}

View File

@ -141,6 +141,10 @@ export default {
type: Number,
default: 2,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
return {
@ -442,6 +446,9 @@ export default {
let params = {
TrialImageDownloadId: this.downloadId,
}
if (this.isReading) {
params.VisitTaskId = this.TaskId
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)

View File

@ -43,7 +43,8 @@
<template slot-scope="scope">
<div class="btnBox">
<!--上传--->
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data" v-if="!forbid">
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data"
v-if="!forbid && (!isReading || isDownloaded)">
<div class="form-group" style="margin-right: 10px">
<div :id="`directoryInputWrapper_${scope.row.Id}`" class="btn btn-link file-input">
<el-button circle icon="el-icon-upload2" :disabled="btnLoading" :loading="btnLoading"
@ -70,7 +71,7 @@
</el-table>
<div style="margin: 10px 0" class="top">
<span>{{ $t('upload:dicom:uploadTitle') }}</span>
<div class="btnBox" v-if="!forbid">
<div class="btnBox" v-if="!forbid && (!isReading || isDownloaded)">
<span style="margin-right: 10px">{{ $store.state.trials.uploadTip }}</span>
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
<div class="form-group">
@ -379,6 +380,10 @@ export default {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
}
},
components: {
'study-view': studyView,
@ -428,7 +433,8 @@ export default {
openSubjectVisitId: null,
openVisitTaskId: null,
TrialModality: [],
IsReadingTaskViewInOrder: 2
IsReadingTaskViewInOrder: 2,
isDownloaded: false
}
},
async mounted() {
@ -471,6 +477,10 @@ 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.TrialModality = res.OtherInfo.TrialModality.split('|')
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder || res.OtherInfo.IsReadingTaskViewInOrder === 0 ? res.OtherInfo.IsReadingTaskViewInOrder : 2
this.StudyInstanceUidList = []

View File

@ -6,12 +6,13 @@
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:dicom')" name="dicom">
<dicomFile v-if="activeName === 'dicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :TaskId="VisitTaskId" :isUpload.sync="isUpload"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid"
:isReading="isReading" />
</el-tab-pane>
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:nonedicom')" name="nonedicom">
<nonedicomFile v-if="activeName === 'nonedicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :VisitTaskId="VisitTaskId" :isUpload.sync="isUpload" :IsImageSegment="IsImageSegment"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :forbid="forbid" :isReading="isReading" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@ -58,6 +59,10 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {

View File

@ -111,7 +111,7 @@
:title="$t('upload:nonedicom:button:preview')" @click.stop="handlePreviewNoneDicomFiles(scope.row)" />
<!--上传--->
<el-button circle icon="el-icon-upload2" :title="$t('upload:nonedicom:button:upload')" v-if="!forbid"
@click.native.prevent="handleUpload(scope.row)" />
@click.native.prevent="handleUpload(scope.row)" :disabled="isReading && !isDownloaded" />
<!--删除--->
<el-button :disabled="scope.row.UploadedFileCount <= 0 ||
scope.row.ReadingTaskState === 2
@ -270,6 +270,10 @@ export default {
type: Number,
default: 0,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
return {
@ -291,7 +295,8 @@ export default {
BodyPart: {},
relationInfo: {
ImageFormatList: []
}
},
isDownloaded: false
}
},
watch: {
@ -348,6 +353,10 @@ 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.faccept = []
this.relationInfo.ImageFormatList.forEach((item) => {
this.faccept.push(`.${item}`)

View File

@ -149,6 +149,11 @@ export const constantRoutes = [
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/none-dicoms')
},
{
path: '/ecrfList',
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/TableList')
},
{
path: '/readingPage',
name: 'readingPage',

View File

@ -11,10 +11,10 @@ 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 readableZipStream = new ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
@ -41,9 +41,13 @@ function zipFiles(zipName, files) {
.then(() => {
console.log("同步下载打包结束时间:" + new Date());
store.dispatch('trials/setUnLock', false)
readableZipStream = null;
zipFileOutputStream = null;
resolve(true)
}).catch(err => {
console.log(err);
readableZipStream = null;
zipFileOutputStream = null;
resolve(false)
});
} else {
@ -61,14 +65,16 @@ 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)
fileOutputStream = null;
resolve(true)
}).catch(err => {
console.log(err)
fileOutputStream = null;
resolve(false)
});
} catch (err) {

View File

@ -1,2 +1,2 @@
const WHITELIST = ['/', "/curriculumVitae", 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/link_expired', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms']
const WHITELIST = ['/', "/curriculumVitae", 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/link_expired', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms', '/ecrfList']
export default WHITELIST

View File

@ -50,6 +50,9 @@
</div>
<div v-if="v.fileType.includes('zip')" class="content flex_col">
<img :title="v.FileName" crossorigin="anonymous" :src="zipImg" height="100%"/>
</div>
</div>
</div>
<div class="Anonymous" v-if="isAnonymous">
@ -158,6 +161,7 @@ import hardcodedMetaDataProvider from '@/views/trials/trials-panel/reading/visit
import registerWebImageLoader from '@/views/trials/trials-panel/reading/visit-review/js/registerWebImageLoader'
import Note_RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms3D/components/tools/Note_RectangleRoiTool'
import { noneDicomStudyMaskImage, noneDicomStudyUndoMaskImage } from "@/api/reading"
import zipImg from '@/assets/zip.png'
const { ViewportType } = Enums
const renderingEngineId = 'myRenderingEngine'
const {
@ -221,7 +225,7 @@ export default {
activeTool: '',
imageType: ['image/jpeg', 'image/jpg', 'image/bmp', 'image/png'],
loading: false,
isFitToWindowMode: false,
isFitToWindowMode: true,
trialId: null,
activeName: '1',
tools: [],
@ -231,7 +235,8 @@ export default {
tip: [
this.$t('DicomViewer:anonymous:after'),
this.$t('DicomViewer:anonymous:before')
]
],
zipImg
}
},
computed: {

View File

@ -42,6 +42,9 @@
<el-image v-else-if="
!!~k.FileType.indexOf('mp4')
" style="width: 100%; height: 100%" :src="mp4" fit="contain" crossorigin="anonymous" />
<el-image v-else-if="
!!~k.FileType.indexOf('zip')
" style="width: 100%; height: 100%" :src="zipImg" fit="contain" crossorigin="anonymous" />
</div>
<div class="file-text" :title="k.FileName">
{{ k.FileName }}
@ -73,6 +76,7 @@ import { getNoneDicomStudyList, setNodicomStudyState } from '@/api/trials'
import FileNameSorter from "@/utils/customSort"
import pdf from '@/assets/pdf.png'
import mp4 from '@/assets/mp4.png'
import zipImg from '@/assets/zip.jpg'
export default {
name: 'StudyList',
props: {
@ -98,6 +102,7 @@ export default {
studyList: [],
pdf,
mp4,
zipImg,
BodyPart: {},
subjectVisitId: '',
studyId: '',

View File

@ -218,9 +218,9 @@
<div class="text">{{ $t('trials:reading:button:screenShot') }}</div>
</div>
</el-tooltip>
<el-popover v-model="keySeriesPopoverVisible" placement="bottom" trigger="click" popper-class="key-series-popper"
@show="showKeySeriesPanel">
<el-popover v-model="keySeriesPopoverVisible" placement="bottom" trigger="click"
popper-class="key-series-popper" @show="showKeySeriesPanel">
<ul class="key-series-list">
<li v-if="keyStack && keyStack.taskBlindName" class="key-series-header">
<div class="key-series-header-top">
@ -563,7 +563,7 @@
</el-button>
</div>
</div>
</transition>
@ -596,7 +596,7 @@
</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>
@ -708,10 +708,10 @@
</el-dialog>
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId"
:visible.sync="downloadImageVisible" />
:visible.sync="downloadImageVisible" :isReading="true" />
<!-- 签名框 -->
<el-dialog v-if="signVisible" :visible.sync="signVisible" :close-on-click-modal="false" width="600px"
custom-class="base-dialog-wrapper">
@ -1129,7 +1129,7 @@ export default {
DicomEvent.$on('imageLocation', async (measuredData) => {
return new Promise(async resolve => {
if (!measuredData) return
await this.imageLocation(measuredData)
resolve()
})
@ -1197,7 +1197,7 @@ export default {
} else if (this.CriterionType === 22) {
this.setToolActive('Probe', true, null, 'tableQuestion')
}
})
window.addEventListener('beforeunload', () => {
if (this.petctWindow) {
@ -1451,7 +1451,7 @@ export default {
// } catch (err) {
// console.error(err)
// }
const series = this.getSeriesInfoByMark(visitTaskId, {lesionName: kf.MarkName})
const series = this.getSeriesInfoByMark(visitTaskId, { lesionName: kf.MarkName })
if (!series) return
this.loadImageStack(series)
},
@ -2496,7 +2496,7 @@ export default {
this.readingTaskState = 2
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask({imageToolType: 1})
const res = await getAutoCutNextTask({ imageToolType: 1 })
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@ -2609,7 +2609,7 @@ export default {
await this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
type: 'warning'
}).catch(() => {})
}).catch(() => { })
return
}
if (this.ctSeriesInfo.instanceCount > 400) {

View File

@ -33,17 +33,21 @@
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="item.LesionType === 104 && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl">
<div v-if="item.LesionType === 104">
<div
class="add-icon" @click.prevent="downloadTpl" v-if="readingTaskState < 2">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;"
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)">
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)" v-if="readingTaskState < 2">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)">
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)" v-if="readingTaskState < 2">
<i class="el-icon-plus" />
</div>
<div class="add-icon" @click.prevent="handleView(item)">
<i class="el-icon-view" />
</div>
</div>
</div>
@ -222,6 +226,7 @@
<script>
import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@/api/trials'
import { resetReadingTask, getIVUSTemplate } from '@/api/reading'
import { getToken } from '@/utils/auth'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { mapGetters } from 'vuex'
@ -284,7 +289,8 @@ export default {
sRoiEndDistanceId: '',
sRoiDistanceId: '',
rowSaveLoadingMap: {},
dialogSaveLoading: false
dialogSaveLoading: false,
childWindows: []
}
},
computed: {
@ -324,11 +330,13 @@ export default {
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
window.addEventListener('beforeunload', this.closeAllChildWindows)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
window.removeEventListener('beforeunload', this.closeAllChildWindows)
},
methods: {
handleReadingChart(e) {
@ -739,6 +747,24 @@ export default {
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
handleView(item) {
localStorage.setItem('taskBlindName', this.taskBlindName)
localStorage.setItem('isReadingShowSubjectInfo', this.isReadingShowSubjectInfo)
let token = getToken()
let routeData = this.$router.resolve({ path: `/ecrfList?trialId=${this.$route.query.trialId}&visitTaskId=${this.visitTaskId}&questionId=${item.Id}&TokenKey=${token}` })
const win = window.open(routeData.href, '_blank')
if (win) {
this.childWindows.push(win)
}
},
closeAllChildWindows() {
this.childWindows.forEach(win => {
if (win && !win.closed) {
win.close()
}
})
this.childWindows = []
},
async saveFormData() {
if (this.dialogSaveLoading) return
this.dialogSaveLoading = true

View File

@ -33,17 +33,20 @@
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="(item.LesionType === 104) && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)">
<div v-if="(item.LesionType === 104)">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)" v-if="readingTaskState < 2">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;"
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)">
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)" v-if="readingTaskState < 2">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)">
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)" v-if="readingTaskState < 2">
<i class="el-icon-plus" />
</div>
<div class="add-icon" @click.prevent="handleView(item)">
<i class="el-icon-view" />
</div>
</div>
</div>
@ -224,6 +227,7 @@ import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@
import { resetReadingTask, getOCTFCTTemplate, getOCTLipidAngleTemplate } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { getToken } from '@/utils/auth'
import { mapGetters } from 'vuex'
import Questions from './../Questions'
import QuestionTableFormItem from './QuestionTableFormItem'
@ -285,7 +289,8 @@ export default {
sRoiEndDistanceId: '',
sRoiDistanceId: '',
rowSaveLoadingMap: {},
dialogSaveLoading: false
dialogSaveLoading: false,
childWindows: []
}
},
computed: {
@ -325,11 +330,13 @@ export default {
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
window.addEventListener('beforeunload', this.closeAllChildWindows)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
window.removeEventListener('beforeunload', this.closeAllChildWindows)
},
methods: {
handleReadingChart(e) {
@ -755,6 +762,27 @@ export default {
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
handleView(item) {
if (this.openWindow) {
this.openWindow.close()
}
localStorage.setItem('taskBlindName', this.taskBlindName)
localStorage.setItem('isReadingShowSubjectInfo', this.isReadingShowSubjectInfo)
let token = getToken()
let routeData = this.$router.resolve({ path: `/ecrfList?trialId=${this.$route.query.trialId}&visitTaskId=${this.visitTaskId}&questionId=${item.Id}&TokenKey=${token}` })
const win = window.open(routeData.href, '_blank')
if (win) {
this.childWindows.push(win)
}
},
closeAllChildWindows() {
this.childWindows.forEach(win => {
if (win && !win.closed) {
win.close()
}
})
this.childWindows = []
},
async saveFormData() {
if (this.dialogSaveLoading) return
this.dialogSaveLoading = true

View File

@ -0,0 +1,115 @@
<template>
<div class="table-list-container" v-loading="loading">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<h3 v-if="tableQuestion && tableQuestion.GroupName">
{{ language === 'en' ? tableQuestion.GroupEnName : tableQuestion.GroupName }}
</h3>
<h4 v-if="tableQuestion && tableQuestion.QuestionName">
{{ tableQuestion.QuestionName }}
</h4>
<el-table
v-if="tableQuestion && tableQuestion.Type === 'basicTable' && tableQuestion.TableQuestions"
:ref="tableQuestion.Id"
:data="tableQuestion.TableQuestions.Answers"
height="100%"
>
<el-table-column
v-for="q of tableQuestion.TableQuestions.Questions"
:key="q.Id"
:prop="q.Id"
:label="q.QuestionName"
show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="q.Unit > 0 && !isNaN(parseFloat(scope.row[q.Id]))">
{{ `${scope.row[q.Id]} ${$fd('ValueUnit', parseInt(q.Unit))}` }}
</span>
<span v-else-if="q.DictionaryCode">
{{`${scope.row[q.Id] instanceof Array ? scope.row[q.Id].map(item => $fd(q.DictionaryCode,
parseInt(item))).join(',') : $fd(q.DictionaryCode, parseInt(scope.row[q.Id]))}`}}
</span>
<span v-else-if="q.OptionTypeEnum === 1">
{{ `${scope.row[q.Id] instanceof Array ? scope.row[q.Id].join(',') : scope.row[q.Id]}` }}
</span>
<span v-else>
{{ `${scope.row[q.Id]}` }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getFilterTableQuestion } from '@/api/reading'
import store from '@/store'
import { mapGetters } from 'vuex'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'DicomTableList',
data() {
return {
isReadingShowSubjectInfo: false,
subjectCode: null,
taskBlindName: null,
questionId: null,
tableQuestion: null,
loading: false
}
},
computed: {
...mapGetters(['language'])
},
created() {
if (this.$route.query.TokenKey) {
store.dispatch('user/setToken', this.$route.query.TokenKey)
changeURLStatic('TokenKey', '')
}
this.subjectCode = localStorage.getItem('subjectCode')
this.taskBlindName = localStorage.getItem('taskBlindName')
this.isReadingShowSubjectInfo = localStorage.getItem('isReadingShowSubjectInfo')
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
let params = {
trialId: this.$route.query.trialId,
visitTaskId: this.$route.query.visitTaskId,
questionId: this.$route.query.questionId
}
let res = await getFilterTableQuestion(params)
if (res.IsSuccess) {
this.tableQuestion = res.Result
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
resetList() {
}
}
}
</script>
<style lang="scss" scoped>
.table-list-container {
height: 100%;
padding: 0 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
h3, h4 {
flex-shrink: 0;
margin: 10px 0 10px;
}
</style>

View File

@ -411,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>
@ -448,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

@ -619,6 +619,14 @@ export default {
}
}
::v-deep .el-dialog.cd-dialog-container {
.el-input.is-disabled .el-input__inner {
background-color: rgb(245, 247, 250);
color: #666;
border: 1px solid #dcdfe6;
}
}
}
::v-deep .dialog-container {

View File

@ -12,7 +12,9 @@
{ required: true, message: $t('common:ruleMessage:select'), trigger: 'blur' }
]">
<el-select v-model="fusionForm.studyId" clearable @change="handleStudyIdChange">
<el-option v-for="item in studyList" :key="item.StudyId" :label="item.StudyCode" :value="item.StudyId" />
<el-option v-for="item in studyList" :key="item.StudyId"
:label="taskInfo.IsReadingTaskViewInOrder !== 0 ? item.StudyCode : `${item.Modalities}(${item.SeriesCount})`"
:value="item.StudyId" />
</el-select>
</el-form-item>
<!-- 融合图像 -->
@ -111,10 +113,12 @@ export default {
ctSeries: [],
petSeries: [],
petctWindow: null,
digitPlaces: 2
digitPlaces: 2,
taskInfo: {}
}
},
mounted() {
this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.initForm()

View File

@ -187,6 +187,7 @@ export default {
originalMarkers: [],
markers: { top: '', right: '', bottom: '', left: '' },
playClipState: false,
clipFramesPerSecond: null,
wwwcIdx: 2,
presetName: '',
volumeId: null,
@ -625,6 +626,22 @@ export default {
}
},
syncViewportStaticState() {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (!renderingEngine) return
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport) return
this.getOrientationMarker()
const toolGroup =
cornerstoneTools.ToolGroupManager.getToolGroupForViewport(
this.viewportId,
this.renderingEngineId
) || cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
if (toolGroup) {
toolGroup.setToolEnabled('ScaleOverlay')
}
},
setFullScreen(index) {
setTimeout(() => {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
@ -727,14 +744,33 @@ export default {
}
},
toggleClipPlay(isPlay, framesPerSecond) {
this.playClipState = isPlay
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (!renderingEngine) return
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport?.element) return
const parsedFramesPerSecond = Number(framesPerSecond)
const nextFramesPerSecond = Number.isFinite(parsedFramesPerSecond)
? parsedFramesPerSecond
: (this.clipFramesPerSecond || 15)
const isSameSpeedWhilePlaying = this.playClipState &&
isPlay &&
this.clipFramesPerSecond === nextFramesPerSecond
if (isPlay) {
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
if (isSameSpeedWhilePlaying) return
cornerstoneTools.utilities.cine.playClip(viewport.element, {
framesPerSecond: nextFramesPerSecond,
loop: true,
waitForRendered: 1
})
this.clipFramesPerSecond = nextFramesPerSecond
this.playClipState = true
} else {
cornerstoneTools.utilities.cine.stopClip(viewport.element)
this.clipFramesPerSecond = null
this.playClipState = false
}
},
scrollPage(type) {
@ -1125,6 +1161,7 @@ export default {
}
viewport.render()
this.syncViewportStaticState()
if (this.currentVoiUpper > 0) {
this.voiChange(this.currentVoiUpper)
}

View File

@ -284,7 +284,7 @@
<svg-icon icon-class="lastframe" class="svg-icon" />
</div>
<select v-model="fps" :title="$t('trials:dicom-show:speed')" class="select-wrapper"
:disabled="clipPlaying">
@change="handleClipFpsChange">
<!-- 默认值 -->
<option :value="5">5</option>
<option :value="10">10</option>
@ -582,10 +582,11 @@
:renderingEngineId="renderingEngineId" :visitInfo="taskInfo" /> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId"
:visible.sync="downloadImageVisible" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
:visible.sync="downloadImageVisible" :is-reading-task-view-in-order="isReadingTaskViewInOrder"
:isReading="true" />
<readingChart ref="readingChart" />
</div>
</template>
@ -1221,12 +1222,7 @@ export default {
this.selectArr.push(item.VisitTaskId)
}
if (item.IsCurrentTask) {
this.markedSeriesIds = []
annotations.map(i => {
if (i.MeasureData && i.MeasureData.seriesId) {
this.markedSeriesIds.push(i.MeasureData.seriesId)
}
})
this.syncMarkedSeriesIds(annotations)
}
}
@ -2052,7 +2048,7 @@ export default {
// this.$refs['ecrf'].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
this.markedSeriesIds.push(series.Id)
this.syncMarkedSeriesIdsFromState(series.TaskInfo.VisitTaskId)
}
this.setToolsPassive()
@ -2087,16 +2083,14 @@ export default {
throw errorMsg
}
if (annotation.visitTaskId === this.taskInfo.VisitTaskId && annotation.seriesId) {
const index = this.markedSeriesIds.indexOf(annotation.seriesId)
if (index !== -1) {
this.markedSeriesIds.splice(index, 1)
}
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
} else {
const errorMsg = { message: 'annotation Not allowed to operate' }
throw errorMsg
}
} catch (e) {
cornerstoneTools.annotation.state.addAnnotation(annotation)
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@ -2133,7 +2127,7 @@ export default {
annotation.sliceThickness = series.SliceThickness
annotation.numberOfFrames = isNaN(parseInt(params.frame)) ? null : parseInt(params.frame)
annotation.markTool = annotation.metadata.toolName
this.markedSeriesIds.push(series.Id)
this.syncMarkedSeriesIdsFromState(series.TaskInfo.VisitTaskId)
const operateStateEnum = this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].operateStateEnum
const markName = await this.customPrompt(!this.isNumber(operateStateEnum))
@ -2275,10 +2269,7 @@ export default {
if (!res.IsSuccess) throw ''
}
}
const index = this.markedSeriesIds.indexOf(annotation.seriesId)
if (index !== -1) {
this.markedSeriesIds.splice(index, 1)
}
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@ -2291,6 +2282,7 @@ export default {
}
} catch (e) {
cornerstoneTools.annotation.state.addAnnotation(annotation)
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@ -2367,6 +2359,22 @@ export default {
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
this.$set(this.visitTaskList[taskIdx], 'AnnotationUIDs', annotationUIDs)
},
collectMarkedSeriesIds(list = []) {
return [...new Set(
list
.map(item => item?.MeasureData?.seriesId || item?.seriesId)
.filter(Boolean)
)]
},
syncMarkedSeriesIds(list = []) {
this.markedSeriesIds = this.collectMarkedSeriesIds(list)
},
syncMarkedSeriesIdsFromState(visitTaskId = this.taskInfo?.VisitTaskId) {
const annotations = cornerstoneTools.annotation.state.getAllAnnotations().filter(item => {
return item.visitTaskId === visitTaskId && item.seriesId
})
this.syncMarkedSeriesIds(annotations)
},
async resetAnnotations(visitTaskId) {
if (this.readingTaskState === 2) return
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
@ -2401,9 +2409,9 @@ export default {
if (i.MeasureData) {
const annotation = i.MeasureData
cornerstoneTools.annotation.state.addAnnotation(annotation)
this.markedSeriesIds.push(annotation.seriesId)
}
})
this.syncMarkedSeriesIds(annotations)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@ -3415,6 +3423,15 @@ export default {
this.clipPlaying = !this.clipPlaying
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].toggleClipPlay(isPlay, this.fps)
},
handleClipFpsChange() {
if (!this.clipPlaying) return
const activeViewportRef = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`]
const activeViewport = activeViewportRef && activeViewportRef[0]
if (!activeViewport || typeof activeViewport.toggleClipPlay !== 'function') return
activeViewport.toggleClipPlay(true, this.fps)
},
//
async getWwcTpl() {
try {
@ -4037,8 +4054,10 @@ export default {
}
}
}
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, true)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
if (!this.isActiveViewportAtMeasureLocation(obj.annotation)) {
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, 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}"]`
@ -4049,9 +4068,48 @@ export default {
}, 200)
}
},
getMeasureImageLocation(measureData = {}) {
const referencedImageId = measureData?.metadata?.referencedImageId
const referencedImageParams = referencedImageId ? this.getInstanceInfo(referencedImageId) : {}
return {
visitTaskId: measureData.visitTaskId,
studyId: measureData.studyId || measureData.StudyId,
seriesId: measureData.seriesId || measureData.SeriesId,
instanceId: measureData.instanceId || measureData.InstanceId || referencedImageParams.instanceId || null,
frame: measureData.numberOfFrames ?? measureData.NumberOfFrames ?? referencedImageParams.frame
}
},
getActiveViewportImageLocation() {
const viewportRef = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`]?.[0]
const currentSeries = viewportRef?.series
if (!currentSeries) return null
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine?.getViewport(`${this.viewportKey}-${this.activeViewportIndex}`)
const currentImageId = viewport?.getCurrentImageId?.()
const currentImageParams = currentImageId ? this.getInstanceInfo(currentImageId) : {}
return {
visitTaskId: currentSeries.TaskInfo?.VisitTaskId,
studyId: currentSeries.StudyId,
seriesId: currentSeries.Id,
instanceId: currentImageParams.instanceId || null,
frame: currentImageParams.frame
}
},
isActiveViewportAtMeasureLocation(measureData) {
const targetLocation = this.getMeasureImageLocation(measureData)
const currentLocation = this.getActiveViewportImageLocation()
if (!currentLocation || !targetLocation.seriesId) return false
if (targetLocation.visitTaskId && currentLocation.visitTaskId !== targetLocation.visitTaskId) return false
if (targetLocation.studyId && currentLocation.studyId !== targetLocation.studyId) return false
if (currentLocation.seriesId !== targetLocation.seriesId) return false
if (targetLocation.instanceId && currentLocation.instanceId !== targetLocation.instanceId) return false
return currentLocation.frame === targetLocation.frame
},
async getScreenshots(measureData, callback) {
if (measureData) {
await this.imageLocation(measureData)
if (!this.isActiveViewportAtMeasureLocation(measureData.annotation)) {
await this.imageLocation(measureData)
}
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
)

View File

@ -4,7 +4,8 @@
<el-form-item :label="$t('segment:form:label:studyName')" prop="taskBlindName">
<el-select v-model="form.studyId" clearable @change="(e) => handleChange(e, 'study')"
@clear="(e) => handleClear(e, 'study')">
<el-option v-for="item in studyList" :key="item.StudyId" :label="item.StudyCode"
<el-option v-for="item in studyList" :key="item.StudyId"
:label="taskInfo.IsReadingTaskViewInOrder !== 0 ? item.StudyCode : `${item.Modalities}(${item.SeriesCount})`"
:value="item.StudyId" />
</el-select>
</el-form-item>
@ -87,12 +88,13 @@ export default {
}, trigger: ['blur', 'change']
}
]
}
},
taskInfo: {}
}
},
// mounted() {
// this.init()
// },
mounted() {
this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
},
methods: {
setSeries(series) {
this.series = 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

@ -150,6 +150,8 @@ export default {
originalMarkers: [],
markers: { top: '', right: '', bottom: '', left: '' },
playClipState: false,
clipFramesPerSecond: null,
toggleClipPlayTimer: null,
wwwcIdx: 2,
loading: false,
forceFitToWindow: false,
@ -169,6 +171,10 @@ export default {
this.resizeObserver.unobserve(this.element)
this.resizeObserver.disconnect()
}
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
},
methods: {
initViewport() {
@ -210,12 +216,11 @@ export default {
this.imageInfo.imageOrientationPatient = imagePlaneModule.imageOrientationPatient
this.imageInfo.imagePositionPatient = imagePlaneModule.imagePositionPatient
this.imageInfo.location = imagePlaneModule.sliceLocation
// this.imageInfo.wwwc = `${Math.round(detail.image.windowWidth)}/${Math.round(detail.image.windowCenter)}`
this.getOrientationMarker()
this.$emit('renderAnnotations', this.series)
const toolGroupId = this.viewportId
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolEnabled('ScaleOverlay')
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
if (toolGroup) {
toolGroup.setToolEnabled('ScaleOverlay')
}
},
imageRendered(e) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
@ -312,14 +317,61 @@ export default {
}
},
toggleClipPlay(isPlay, framesPerSecond) {
this.playClipState = isPlay
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (!renderingEngine) return
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport?.element) return
const parsedFramesPerSecond = Number(framesPerSecond)
const nextFramesPerSecond = Number.isFinite(parsedFramesPerSecond)
? parsedFramesPerSecond
: (this.clipFramesPerSecond || 15)
const isSameSpeedWhilePlaying = this.playClipState &&
isPlay &&
this.clipFramesPerSecond === nextFramesPerSecond
if (isPlay) {
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
if (isSameSpeedWhilePlaying) return
cornerstoneTools.utilities.cine.stopClip(viewport.element)
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
const frameInterval = Math.max(16, Math.round(1000 / nextFramesPerSecond))
this.toggleClipPlayTimer = setInterval(() => {
const imageIds = viewport.getImageIds()
if (!imageIds?.length) return
let index = viewport.getCurrentImageIdIndex() + 1
if (index > imageIds.length - 1) {
index = 0
}
csUtils.jumpToSlice(viewport.element, {
imageIndex: index,
debounceLoading: false
})
}, frameInterval)
this.clipFramesPerSecond = nextFramesPerSecond
this.playClipState = true
} else {
cornerstoneTools.utilities.cine.stopClip(viewport.element)
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
this.clipFramesPerSecond = null
this.playClipState = false
}
},
syncViewportStaticState() {
this.getOrientationMarker()
this.$emit('renderAnnotations', this.series)
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
if (toolGroup) {
toolGroup.setToolEnabled('ScaleOverlay')
}
},
scrollPage(type) {
@ -339,7 +391,13 @@ export default {
}
// viewport.setImageIdIndex(newImageIdIndex)
csUtils.jumpToSlice(viewport.element, { imageIndex: newImageIdIndex })
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
cornerstoneTools.utilities.cine.stopClip(viewport.element)
this.clipFramesPerSecond = null
this.playClipState = false
},
setZoom(ratio) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
@ -432,6 +490,7 @@ export default {
}
this.prefetchMetadataInformation(obj.ImageIds, obj.Modality)
await viewport.setStack(this.series.Stack, obj.SliceIndex)
this.syncViewportStaticState()
viewport.render()
} catch (e) {

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

@ -251,7 +251,7 @@ export default {
return i
})
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
this.$refs[visitTaskId][0].initCurrentMaredFiles()
this.$refs[visitTaskId][0].initCurrentMaredFiles(annotations)
this.$refs.fileViewer.resetAnnotations({ annotations, visitTaskId })
},
setReadingTaskState(state) {

View File

@ -97,6 +97,7 @@ export default {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
this.studyList = this.decorateStudyList(this.visitTaskInfo.StudyList || [])
this.initCurrentMaredFiles(this.visitTaskInfo.Annotations)
if (this.studyList.length === 0) return
this.$nextTick(() => {
this.activeStudy(this.studyList[0].Id)
@ -110,8 +111,26 @@ export default {
})
}
},
'visitTaskInfo.Annotations': {
handler(annotations) {
this.initCurrentMaredFiles(annotations)
},
deep: true
}
},
methods: {
buildCurrentMarkedFiles(annotations = []) {
return annotations.reduce((result, item) => {
const fileId = item?.NoneDicomFileId
if (!fileId) return result
if (!Object.hasOwn(result, fileId)) {
result[fileId] = { count: 1 }
} else {
result[fileId].count++
}
return result
}, {})
},
decorateStudyList(studyList = []) {
return studyList.map(study => ({
...study,
@ -204,6 +223,9 @@ export default {
if (type === 'remove') {
if (Object.hasOwn(this.currentMarkedFiles, fileId)) {
this.currentMarkedFiles[fileId].count--
if (this.currentMarkedFiles[fileId].count <= 0) {
this.$delete(this.currentMarkedFiles, fileId)
}
}
} else if (type === 'add') {
if (!Object.hasOwn(this.currentMarkedFiles, fileId)) {
@ -214,15 +236,8 @@ export default {
}
},
initCurrentMaredFiles() {
this.currentMarkedFiles = {}
this.visitTaskInfo.Annotations.map(i => {
if (!Object.hasOwn(this.currentMarkedFiles, i.NoneDicomFileId)) {
this.$set(this.currentMarkedFiles, i.NoneDicomFileId, { count: 1 })
} else {
this.currentMarkedFiles[i.NoneDicomFileId].count++
}
})
initCurrentMaredFiles(annotations = this.visitTaskInfo.Annotations || []) {
this.currentMarkedFiles = this.buildCurrentMarkedFiles(annotations)
},
}
}