Compare commits
17 Commits
fe64feac80
...
cbd860a35e
| Author | SHA1 | Date |
|---|---|---|
|
|
cbd860a35e | |
|
|
7e9eb1f3a5 | |
|
|
254e43ac3d | |
|
|
197b3213e7 | |
|
|
3bfda749d8 | |
|
|
6f911d0d74 | |
|
|
78f4a78994 | |
|
|
4e1911223d | |
|
|
7dd02e84aa | |
|
|
7f260799d8 | |
|
|
06c7a8126b | |
|
|
78624d9cce | |
|
|
6ecc0e58e9 | |
|
|
84a81e1851 | |
|
|
b09209af31 | |
|
|
9892070ccc | |
|
|
63cbc8746f |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -480,3 +480,11 @@ export function getNoneDicomMarkList(data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getFilterTableQuestion(data) {
|
||||
return request({
|
||||
url: `/ReadingImageTask/getFilterTableQuestion`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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}`)
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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: '',
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}"]`
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue