558 lines
18 KiB
Vue
558 lines
18 KiB
Vue
<template>
|
|
<div v-loading="loading" class="read-page-container">
|
|
<!-- 检查列表 -->
|
|
<div class="left-panel">
|
|
<div class="task-container">
|
|
<div class="task-info">
|
|
<div v-for="s in visitTaskList" :key="s.VisitTaskId" class="task-item"
|
|
:class="{ 'task-item-active': activeTaskVisitId == s.VisitTaskId }" @click.prevent="toggleTask(s)">{{
|
|
s.TaskBlindName }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="study-info">
|
|
<div v-for="s in visitTaskList" v-show="activeTaskVisitId === s.VisitTaskId" :key="s.VisitTaskId"
|
|
style="height:100%;">
|
|
<study-list v-if="selectArr.includes(s.VisitTaskId) && s.StudyList.length > 0" :ref="s.VisitTaskId"
|
|
:visit-task-info="s" @selectFile="selectFile" :currentMarkedFiles="currentMarkedFiles" />
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 图像 -->
|
|
<div class="middle-panel">
|
|
<!-- <image-viewer
|
|
ref="imageViewer"
|
|
:related-study-info="relatedStudyInfo"
|
|
@toggleTaskByViewer="toggleTaskByViewer"
|
|
@toggleTask="toggleTask"
|
|
@toggleImage="toggleImage"
|
|
/> -->
|
|
<!-- <pdf-viewer
|
|
ref="imageViewer"
|
|
:related-study-info="relatedStudyInfo"
|
|
@toggleTaskByViewer="toggleTaskByViewer"
|
|
@toggleTask="toggleTask"
|
|
@toggleImage="toggleImage"
|
|
/> -->
|
|
<file-viewer ref="fileViewer" :related-study-info="relatedStudyInfo" :ps-arr="psArr" :ecrf="ecrf"
|
|
@toggleTaskByViewer="toggleTaskByViewer" @toggleTask="toggleTask"
|
|
@toggleImage="toggleImage"
|
|
@previewCD="previewCD" @setPS="setPS" @getEcrf="getEcrf" @getMarkedFileIds="getMarkedFileIds"
|
|
@selectFile="selectFile" />
|
|
</div>
|
|
<!-- 表单 -->
|
|
<div class="right-panel">
|
|
<!-- <div v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo" class="text-info">
|
|
<h3>
|
|
<span v-if="currentVisitInfo && currentVisitInfo.SubjectCode">
|
|
{{ currentVisitInfo.SubjectCode }}
|
|
</span>
|
|
<span v-if="currentVisitInfo && currentVisitInfo.TaskBlindName" style="margin-left:5px;">
|
|
{{ currentVisitInfo.TaskBlindName }}
|
|
</span>
|
|
</h3>
|
|
</div> -->
|
|
<!-- <ecrf-list
|
|
v-if="currentVisitInfo"
|
|
:visit-task-info="currentVisitInfo"
|
|
/> -->
|
|
<div v-for="s in visitTaskList" v-show="lastViewportTaskId === s.VisitTaskId" :key="s.VisitTaskId"
|
|
style="height: 100%;">
|
|
<customize-question-list v-if="lastViewportTaskId && lastViewportTaskIds.includes(s.VisitTaskId)" :ref="`ecrf_${s.VisitTaskId}`" :isNoneDicom="true"
|
|
:reading-task-state="currentVisitInfo.VisitTaskId === taskInfo.VisitTaskId ? readingTaskState : 2" :visit-info="s" @resetAnnotations="resetAnnotations"
|
|
@setReadingTaskState="setReadingTaskState" @viewCustomAnnotationSeries="viewCustomAnnotationSeries"
|
|
@getCustomScreenshots="getCustomScreenshots" @setReadingToolActive="setReadingToolActive"
|
|
@setReadingToolPassive="setReadingToolPassive" @setSaved="setSaved"
|
|
@setIsHaveBindingQuestion="setIsHaveBindingQuestion" @handleReadingChart="handleReadingChart" />
|
|
</div>
|
|
</div>
|
|
|
|
<el-dialog :visible.sync="clinicalDataVisible"
|
|
:custom-class="isClinicalDataFullscreen ? 'full-dialog-container' : 'dialog-container'" :show-close="false"
|
|
:close-on-click-modal="false" :fullscreen="isClinicalDataFullscreen">
|
|
<span slot="title" class="dialog-footer">
|
|
<div style="position: absolute;right: 20px;top: 10px;">
|
|
<svg-icon :icon-class="isClinicalDataFullscreen ? 'exit-fullscreen' : 'fullscreen'"
|
|
style="cursor: pointer;font-size: 20px;" @click="isClinicalDataFullscreen = !isClinicalDataFullscreen" />
|
|
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;"
|
|
@click="clinicalDataVisible = false" />
|
|
</div>
|
|
</span>
|
|
<div style="height: 100%;margin:0;display: flex;flex-direction: column;">
|
|
<clinical-data v-if="clinicalDataVisible" style="flex: 1" :trial-id="trialId" :subject-id="taskInfo.SubjectId"
|
|
:visit-task-id="cdVisitTaskId" :is-reading-show-subject-info="taskInfo.IsReadingShowSubjectInfo" />
|
|
</div>
|
|
|
|
</el-dialog>
|
|
<readingChart ref="readingChart" />
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { getRelatedVisitTask, getReadingImageFile, getNoneDicomMarkListOutDto } from '@/api/trials'
|
|
import StudyList from './StudyList'
|
|
// import ImageViewer from './ImageViewer'
|
|
// import PdfViewer from './PDFViewer'
|
|
import FileViewer from './FileViewer'
|
|
import EcrfList from './EcrfList'
|
|
import customizeQuestionList from '@/views/trials/trials-panel/reading/dicoms3D/components/customize/QuestionList'
|
|
import ClinicalData from '@/views/trials/trials-panel/reading/clinical-data'
|
|
import { mapGetters } from 'vuex'
|
|
import readingChart from '@/components/readingChart'
|
|
export default {
|
|
name: 'ReadPage',
|
|
components: {
|
|
StudyList,
|
|
FileViewer,
|
|
// ImageViewer,
|
|
// PdfViewer,
|
|
EcrfList,
|
|
ClinicalData,
|
|
customizeQuestionList,
|
|
readingChart
|
|
},
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
// 当前任务信息
|
|
taskInfo: null,
|
|
// 当前激活的任务Id
|
|
activeTaskVisitId: '',
|
|
// 表单数据
|
|
formData: {
|
|
name: '',
|
|
description: ''
|
|
},
|
|
// 当前任务关联的任务信息
|
|
visitTaskList: [],
|
|
selectArr: [],
|
|
fileType: 'image',
|
|
currentStudyInfo: null,
|
|
currentVisitInfo: {},
|
|
relatedStudyInfo: null,
|
|
clinicalDataVisible: false,
|
|
isClinicalDataFullscreen: false,
|
|
trialId: '',
|
|
cdVisitTaskId: '',
|
|
psArr: [],
|
|
readingTaskState: 0,
|
|
ecrf: {
|
|
IsHaveBindingQuestion: false
|
|
},
|
|
currentMarkedFiles: {},
|
|
lastViewportTaskIds: []
|
|
}
|
|
},
|
|
computed: {
|
|
...mapGetters(['lastViewportTaskId'])
|
|
},
|
|
watch: {
|
|
lastViewportTaskId: {
|
|
immediate: true,
|
|
handler(id) {
|
|
if (!id) return
|
|
const idx = this.visitTaskList.findIndex(i => i.VisitTaskId === id)
|
|
if (idx === -1) return
|
|
this.currentVisitInfo = this.visitTaskList[idx]
|
|
// this.activeTaskVisitId = id
|
|
if (!this.lastViewportTaskIds.includes(id)) {
|
|
this.lastViewportTaskIds.push(id)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
|
|
this.trialId = this.$route.query.trialId
|
|
this.readingTaskState = this.taskInfo.ReadingTaskState
|
|
this.getRelatedTask()
|
|
document.addEventListener("click", this.foo);
|
|
},
|
|
methods: {
|
|
handleReadingChart(row) {
|
|
let { e, data } = row
|
|
let obj = Object.assign({}, data)
|
|
obj.TrialId = this.$route.query.trialId
|
|
obj.VisitTaskId = this.taskInfo.VisitTaskId
|
|
let zIndex = 9
|
|
if (obj.RowIndex) {
|
|
zIndex = 9999
|
|
}
|
|
this.$refs.readingChart.init(e, obj, zIndex)
|
|
},
|
|
foo() {
|
|
if (!this.$refs.readingChart) return false
|
|
this.$refs.readingChart.foo()
|
|
},
|
|
getEcrf(data) {
|
|
let { type } = data
|
|
if (type === 'getOperateStateEnum') {
|
|
this.ecrf.operateStateEnum = this.$refs[`ecrf_${data.VisitTaskId}`][0].operateStateEnum
|
|
}
|
|
if (type === 'bindAnnotationToQuestion') {
|
|
this.$refs[`ecrf_${data.VisitTaskId}`][0].bindAnnotationToQuestion(data.annotation)
|
|
}
|
|
if (type === 'updateAnnotationToQuestion') {
|
|
this.$refs[`ecrf_${data.VisitTaskId}`][0].updateAnnotationToQuestion(data.annotation)
|
|
}
|
|
if (type === 'verifyAnnotationIsBound') {
|
|
this.ecrf.isBound = this.$refs[`ecrf_${data.VisitTaskId}`][0].verifyAnnotationIsBound(data.annotation)
|
|
}
|
|
// if (type === 'getQuestions') {
|
|
// this.ecrf.isBound = this.$refs[`ecrf_${data.VisitTaskId}`][0].getQuestions()
|
|
// }
|
|
if (type === 'changePlottingScaleChangeAnswer') {
|
|
this.ecrf.isBound = this.$refs[`ecrf_${data.VisitTaskId}`][0].changePlottingScaleChangeAnswer(data)
|
|
}
|
|
if (type === 'verifyFileIsBound') {
|
|
this.ecrf.isFileBound = this.$refs[`ecrf_${data.VisitTaskId}`][0].verifyFileIsBound(data.annotation)
|
|
}
|
|
},
|
|
setSaved(saved) {
|
|
this.$refs.fileViewer.lengthscaleToolDisabled(saved)
|
|
},
|
|
setIsHaveBindingQuestion(flag) {
|
|
this.ecrf.IsHaveBindingQuestion = flag
|
|
},
|
|
async resetAnnotations(visitTaskId) {
|
|
if (this.readingTaskState === 2) return
|
|
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
|
|
if (taskIdx === -1) return
|
|
if (!this.visitTaskList[taskIdx].IsCurrentTask) return
|
|
// 获取标注信息
|
|
let res = await getNoneDicomMarkListOutDto({ visitTaskId: visitTaskId })
|
|
|
|
const annotations = res.Result.NoneDicomMarkList.map(i => {
|
|
if (typeof i.MeasureData === 'string' && i.MeasureData) {
|
|
i.MeasureData = JSON.parse(i.MeasureData)
|
|
i.MeasureData.markId = i.MarkId
|
|
i.MeasureData.id = i.Id
|
|
}
|
|
if (i.MeasureData.metadata.toolName === 'Lengthscale' && this.psArr.findIndex(p => p.NoneDicomFileId === i.NoneDicomFileId) === -1) {
|
|
this.setPS({ NoneDicomFileId: i.NoneDicomFileId, Path: i.Path, PS: i.MeasureData.data.ps })
|
|
}
|
|
return i
|
|
})
|
|
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
|
|
this.$refs[visitTaskId][0].initCurrentMaredFiles()
|
|
this.$refs.fileViewer.resetAnnotations({ annotations, visitTaskId })
|
|
},
|
|
setReadingTaskState(state) {
|
|
this.readingTaskState = state
|
|
},
|
|
viewCustomAnnotationSeries(obj) {
|
|
this.$refs.fileViewer.viewCustomAnnotation(obj)
|
|
},
|
|
getCustomScreenshots(obj, callback) {
|
|
this.$refs.fileViewer.getScreenshots(obj, callback)
|
|
},
|
|
setReadingToolPassive() {
|
|
this.$refs.fileViewer.setToolsPassive()
|
|
},
|
|
setReadingToolActive(toolName) {
|
|
if (this.readingTaskState === 2) return
|
|
if (this.activeTool === toolName) return
|
|
this.$refs.fileViewer.setToolActive(toolName)
|
|
},
|
|
// 获取关联任务信息
|
|
async getRelatedTask() {
|
|
this.loading = true
|
|
try {
|
|
const params = {
|
|
visitTaskId: this.taskInfo.VisitTaskId
|
|
}
|
|
const res = await getRelatedVisitTask(params)
|
|
this.visitTaskList = res.Result.map((item) => ({
|
|
...item,
|
|
StudyList: [],
|
|
Annotations: []
|
|
}))
|
|
const idx = res.Result.findIndex(i => i.IsCurrentTask)
|
|
if (idx > -1) {
|
|
await this.setActiveTaskVisitId(res.Result[idx].VisitTaskId)
|
|
this.$nextTick(() => {
|
|
this.$refs[res.Result[idx].VisitTaskId][0].setInitActiveFile()
|
|
})
|
|
}
|
|
if (this.taskInfo.IsReadingTaskViewInOrder === 1 && res.Result.length > 1) {
|
|
// 按时间顺序
|
|
const i = this.visitTaskList.findIndex(i => i.IsBaseLineTask)
|
|
if (i > -1) {
|
|
await this.getReadingImageFile(res.Result[i].VisitTaskId, i)
|
|
await this.getAnnotations(res.Result[i].VisitTaskId, i)
|
|
const studyList = this.visitTaskList[i].StudyList
|
|
if (studyList.length > 0) {
|
|
const fileInfo = studyList[0].NoneDicomStudyFileList[0]
|
|
this.relatedStudyInfo = { fileInfo, visitTaskInfo: this.visitTaskList[i], fileList: studyList[0].NoneDicomStudyFileList, fileIndex: 0, studyId: studyList[0].Id }
|
|
if (!this.selectArr.includes(res.Result[i].VisitTaskId)) {
|
|
this.selectArr.push(res.Result[i].VisitTaskId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (this.taskInfo.IsReadingTaskViewInOrder === 2) {
|
|
// 受试者内随机
|
|
const studyList = this.visitTaskList[idx].StudyList
|
|
if (studyList.length > 0) {
|
|
const fileInfo = studyList[0].NoneDicomStudyFileList[0]
|
|
this.relatedStudyInfo = { fileInfo, visitTaskInfo: this.visitTaskList[idx], fileList: studyList[0].NoneDicomStudyFileList, fileIndex: 0, studyId: studyList[0].Id }
|
|
}
|
|
}
|
|
if (this.readingTaskState < 2) {
|
|
this.$refs[res.Result[idx].VisitTaskId][0].initCurrentMaredFiles()
|
|
}
|
|
this.loading = false
|
|
} catch (e) {
|
|
console.log(e)
|
|
this.loading = false
|
|
}
|
|
},
|
|
// 获取任务关联的文件信息
|
|
getReadingImageFile(visitTaskId, visitTaskIdx) {
|
|
return new Promise(async (resolve, reject) => {
|
|
this.loading = true
|
|
try {
|
|
const params = {
|
|
subjectId: this.taskInfo.SubjectId,
|
|
trialId: this.$route.query.trialId,
|
|
visistTaskId: visitTaskId
|
|
}
|
|
const res = await getReadingImageFile(params)
|
|
this.$set(this.visitTaskList[visitTaskIdx], 'StudyList', res.Result)
|
|
this.loading = false
|
|
resolve()
|
|
} catch (e) {
|
|
console.log(e)
|
|
this.loading = false
|
|
reject(e)
|
|
}
|
|
})
|
|
},
|
|
// 获取任务关联的标注信息
|
|
getAnnotations(visitTaskId, visitTaskIdx) {
|
|
return new Promise(async (resolve, reject) => {
|
|
this.loading = true
|
|
try {
|
|
const params = {
|
|
visitTaskId: visitTaskId
|
|
}
|
|
const res = await getNoneDicomMarkListOutDto(params)
|
|
const arr = res.Result.NoneDicomMarkList.map(i => {
|
|
if (typeof i.MeasureData === 'string') {
|
|
i.MeasureData = JSON.parse(i.MeasureData)
|
|
i.MeasureData.markId = i.MarkId
|
|
i.MeasureData.id = i.Id
|
|
}
|
|
if (i.MeasureData.metadata.toolName === 'Lengthscale' && this.psArr.findIndex(p => p.NoneDicomFileId === i.NoneDicomFileId) === -1) {
|
|
this.setPS({ NoneDicomFileId: i.NoneDicomFileId, Path: i.Path, PS: i.MeasureData.data.ps })
|
|
}
|
|
return i
|
|
})
|
|
this.$set(this.visitTaskList[visitTaskIdx], 'Annotations', arr)
|
|
this.loading = false
|
|
resolve()
|
|
} catch (e) {
|
|
console.log(e)
|
|
this.loading = false
|
|
reject(e)
|
|
}
|
|
})
|
|
},
|
|
setPS(obj) {
|
|
const i = this.psArr.findIndex(p => p.NoneDicomFileId === obj.NoneDicomFileId)
|
|
if (i > -1) {
|
|
this.psArr[i].PS = obj.PS
|
|
} else {
|
|
this.psArr.push(obj)
|
|
}
|
|
},
|
|
getMarkedFileIds(obj) {
|
|
const { visitTaskId } = obj
|
|
this.$refs[visitTaskId][0].getMarkedFileIds(obj)
|
|
},
|
|
// 切换任务
|
|
toggleTask(taskInfo) {
|
|
this.setActiveTaskVisitId(taskInfo.VisitTaskId)
|
|
this.$refs.fileViewer.setToolsPassive()
|
|
},
|
|
// 视图触发任务切换
|
|
async toggleTaskByViewer(visitTaskNum) {
|
|
const i = this.visitTaskList.findIndex(v => v.VisitTaskNum === visitTaskNum)
|
|
if (i === -1) return
|
|
const visitTaskId = this.visitTaskList[i].VisitTaskId
|
|
this.setActiveTaskVisitId(visitTaskId, true)
|
|
},
|
|
// 设置激活的访视
|
|
async setActiveTaskVisitId(id, isInitActiveFile = false) {
|
|
if (!id) return
|
|
if (!this.selectArr.includes(id)) {
|
|
this.selectArr.push(id)
|
|
}
|
|
const idx = this.visitTaskList.findIndex(i => i.VisitTaskId === id)
|
|
if (idx === -1) return
|
|
if (this.visitTaskList[idx].StudyList.length === 0) {
|
|
await this.getReadingImageFile(id, idx)
|
|
await this.getAnnotations(id, idx)
|
|
}
|
|
this.activeTaskVisitId = id
|
|
this.$nextTick(() => {
|
|
if (isInitActiveFile) {
|
|
this.$refs[id][0].setInitActiveFile()
|
|
}
|
|
})
|
|
},
|
|
// 切换图片
|
|
toggleImage(obj) {
|
|
this.$refs[obj.taskId][0].activeImage(obj)
|
|
},
|
|
|
|
previewCD(taskId) {
|
|
this.isClinicalDataFullscreen = false
|
|
this.dialogVisible = true
|
|
this.cdVisitTaskId = taskId
|
|
this.clinicalDataVisible = true
|
|
},
|
|
selectFile(obj) {
|
|
if (obj.fileInfo && obj.fileList && obj.visitTaskInfo && obj.studyId) {
|
|
this.$refs['fileViewer'].setActiveCanvasImages(obj)
|
|
return
|
|
}
|
|
|
|
const taskIndex = this.visitTaskList.findIndex(item => item.VisitTaskId === obj.visitTaskId)
|
|
if (taskIndex === -1) return
|
|
|
|
const visitTaskInfo = this.visitTaskList[taskIndex]
|
|
const studyList = visitTaskInfo.StudyList || []
|
|
const studyIndex = studyList.findIndex(study => study.Id === obj.studyId)
|
|
if (studyIndex === -1) return
|
|
|
|
const fileList = studyList[studyIndex].NoneDicomStudyFileList || []
|
|
const fileIndex = fileList.findIndex(file => file.Path === obj.path)
|
|
if (fileIndex === -1) return
|
|
this.setActiveTaskVisitId(obj.visitTaskId)
|
|
this.$refs['fileViewer'].setActiveCanvasImages({
|
|
fileInfo: fileList[fileIndex],
|
|
fileList,
|
|
visitTaskInfo,
|
|
studyId: obj.studyId
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.read-page-container {
|
|
box-sizing: border-box;
|
|
height: 100%;
|
|
display: flex;
|
|
|
|
::-webkit-scrollbar {
|
|
width: 5px;
|
|
height: 5px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
border-radius: 10px;
|
|
background: #d0d0d0;
|
|
}
|
|
|
|
.left-panel {
|
|
display: flex;
|
|
width: 200px;
|
|
border: 1px solid #727272;
|
|
color: #fff;
|
|
|
|
::-webkit-scrollbar {
|
|
width: 3px;
|
|
height: 3px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
border-radius: 10px;
|
|
background: #d0d0d0;
|
|
}
|
|
|
|
.task-container {
|
|
position: relative;
|
|
width: 25px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.task-info {
|
|
position: absolute;
|
|
top: 5px;
|
|
right: 20px;
|
|
transform-origin: right top;
|
|
transform: rotate(-90deg);
|
|
display: flex;
|
|
|
|
.task-item {
|
|
margin-left: 10px;
|
|
white-space: nowrap;
|
|
padding: 0px 4px;
|
|
border: 1px solid #999999;
|
|
border-bottom: none;
|
|
text-align: center;
|
|
background-color: #4e4e4e;
|
|
color: #d5d5d5;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.task-item-active {
|
|
background-color: #607d8b;
|
|
border: 1px solid #607d8b;
|
|
}
|
|
}
|
|
|
|
.study-info {
|
|
width: 170px;
|
|
border-left: 1px solid #727272;
|
|
}
|
|
}
|
|
|
|
.middle-panel {
|
|
flex: 1;
|
|
border: 1px solid #727272;
|
|
margin: 0 5px;
|
|
}
|
|
|
|
.right-panel {
|
|
width: 400px;
|
|
border: 1px solid #727272;
|
|
padding: 0 10px;
|
|
overflow-y: auto;
|
|
|
|
.text-info {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
color: #ddd;
|
|
}
|
|
}
|
|
|
|
::v-deep .dialog-container {
|
|
margin-top: 50px !important;
|
|
width: 75%;
|
|
height: 80%;
|
|
}
|
|
|
|
::v-deep .el-dialog__body {
|
|
padding: 20px 20px 0 20px;
|
|
height: calc(100% - 70px);
|
|
}
|
|
|
|
.el-dialog__header {
|
|
position: relative;
|
|
}
|
|
|
|
.full-dialog-container {
|
|
::v-deep .is-fullscreen .el-dialog__body {
|
|
height: calc(100% - 70px);
|
|
}
|
|
}
|
|
}
|
|
</style>
|