wangxiaoshuang 2026-08-24 09:45:13 +08:00
parent 1ca4fb7dd4
commit 9ecaf5e267
5 changed files with 172 additions and 145 deletions

View File

@ -422,7 +422,7 @@
</div>
</div>
</div>
<el-tooltip v-if="trialCriterion.ImageUploadEnum > 0 && currentReadingTaskState < 2" v-hasPermi="['role:ir']"
<el-tooltip v-if="trialCriterion.ImageUploadEnum > 0"
class="item" effect="dark" :content="$t('trials:reading:button:upload')" placement="bottom">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
@ -431,15 +431,7 @@
<div class="text">{{ $t('trials:reading:button:upload') }}</div>
</div>
</el-tooltip>
<el-tooltip v-if="trialCriterion.ImageDownloadEnum > 0" v-hasPermi="[
'role:ir',
'role:mim',
'role:mc',
'role:pm',
'role:apm',
'role:ea',
'role:qa',
]" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<el-tooltip v-if="trialCriterion.ImageDownloadEnum > 0" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('download')">
<i class="el-icon-download svg-icon" />

View File

@ -96,6 +96,7 @@ async function deleteCache(path) {
*
*/
export async function changeFile(key, file, study, image) {
// console.log(image, 'image')
// return console.log(study, 'study')
let dataset = dcmjs.data.DicomMessage.readFile(image.data.byteArray.buffer)
// console.log(dataset, 'dataset')

View File

@ -477,6 +477,10 @@ export default {
DicomEvent.$off('updateImage')
},
methods: {
getInfo() {
var image = cornerstone.getImage(this.canvas)
return image
},
goViewer(e) {
console.log(this.$refs['sliderBox'].clientHeight)
var height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight

View File

@ -309,11 +309,8 @@
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageUploadEnum > 0"
class="item" effect="dark"
:content="$t('trials:reading:button:upload')"
placement="bottom">
<el-tooltip v-if="trialCriterion.ImageUploadEnum > 0" class="item" effect="dark"
:content="$t('trials:reading:button:upload')" placement="bottom">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
<i class="el-icon-upload2 svg-icon" />
@ -321,15 +318,8 @@
<div class="text">{{ $t('trials:reading:button:upload') }}</div>
</div>
</el-tooltip>
<el-tooltip v-if="trialCriterion.ImageDownloadEnum > 0" v-hasPermi="[
'role:ir',
'role:mim',
'role:mc',
'role:pm',
'role:apm',
'role:ea',
'role:qa',
]" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<el-tooltip v-if="trialCriterion.ImageDownloadEnum > 0" class="item" effect="dark"
:content="$t('trials:reading:button:download')" placement="bottom">
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('download')">
<i class="el-icon-download svg-icon" />
@ -337,7 +327,10 @@
<div class="text">{{ $t('trials:reading:button:download') }}</div>
</div>
</el-tooltip>
<label for="Replacement" style="color: #fff;cursor: pointer;">
替换
</label>
<input type="file" id="Replacement" @change="beginScanFiles($event, 'replace')" style="display: none;">
<div style="margin-left: auto">
<div style="display: flex;">
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:handbooks')" placement="bottom"
@ -449,10 +442,10 @@
/> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :SubjectId="uploadSubjectId" :SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible" :VisitTaskId="taskId"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :StudyInstanceUID="StudyInstanceUID" :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>
@ -479,6 +472,8 @@ import { getDoctorShortcutKey, getUserWLTemplateList } from '@/api/user'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import { getCriterionReadingInfo, setReadKeyFile } from '@/api/trials'
import { changeFile } from "@/views/trials/trials-panel/reading/dicoms/components/upload.js"
import dcmjs from '@/utils/dcmUpload/dcmjs'
export default {
name: 'DicomViewer',
components: {
@ -684,7 +679,10 @@ export default {
fullScreenWidth: window.innerWidth - 570 + 'px',
fullScreenHeight: window.innerHeight - 128 + 'px',
ManualsClose: false
ManualsClose: false,
fileKey: null,
file: null,
StudyInstanceUID: null
}
},
@ -857,6 +855,12 @@ export default {
this.loadLinkedImageStack(seriesInfo)
console.log('loadLinkedImageStack')
})
DicomEvent.$on('sendStudyFile', (study) => {
let image = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].getInfo()
study.visitTaskList = this.visitTaskList
this.$emit("update:Loading", true)
changeFile('IR', this.file, study, image)
})
},
beforeDestroy() {
DicomEvent.$off('updateImage')
@ -867,6 +871,11 @@ export default {
DicomEvent.$off('loadLinkedImageStack')
},
methods: {
beginScanFiles(e, key) {
this.fileKey = key
this.file = e.target.files[0]
DicomEvent.$emit('getStudyFile')
},
handleReadingChart(e) {
this.$emit('handleReadingChart', e)
},
@ -899,6 +908,11 @@ export default {
if (idx > -1) {
this.taskId = this.visitTaskList[idx].VisitTaskId
}
let image = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].getInfo()
let dataset = dcmjs.data.DicomMessage.readFile(image.data.byteArray.buffer)
this.StudyInstanceUID = dataset.dict['0020000D'].Value[0]
dataset = null
console.log(this.StudyInstanceUID, 'StudyInstanceUID')
this.uploadSubjectCode = localStorage.getItem("subjectCode")
this.uploadSubjectId = localStorage.getItem("subjectId")
this.uploadTrialCriterion = this.trialCriterion

View File

@ -1,9 +1,11 @@
<template>
<div v-loading="loading" class="study-wrapper">
<h4 v-if="isReadingShowSubjectInfo" style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;">
<h4 v-if="isReadingShowSubjectInfo"
style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;">
{{ subjectCode }}
</h4>
<h4 v-if="isReadingShowSubjectInfo" style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;margin-bottom: 5px;">
<h4 v-if="isReadingShowSubjectInfo"
style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;margin-bottom: 5px;">
{{ taskBlindName }}
</h4>
<div class="ps">
@ -14,11 +16,8 @@
<!-- 关键序列 -->
{{ $t('trials:reading:title:keySeries') }}
</div>
<div
v-else
class="dicom-desc"
style="width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
<div v-else class="dicom-desc"
style="width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
<div>
<div style="text-overflow: ellipsis;overflow: hidden;" v-if="!study.StudyName">
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
@ -31,59 +30,47 @@
</span>
<div>{{ study.Modalities }} ({{ study.SeriesCount }})</div>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;" :title="study.Description">{{ study.Description }}</div>
<div style="text-overflow: ellipsis;overflow: hidden;" :title="study.Description">{{ study.Description
}}</div>
</div>
</div>
</template>
<div class="series">
<div
v-for="(series, i) in study.SeriesList"
:key="i"
style="position:relative;margin-top:5px;"
series-type="current"
@click="showSeriesImage(index,i,series)"
>
<div v-for="(series, i) in study.SeriesList" :key="i" style="position:relative;margin-top:5px;"
series-type="current" @click="showSeriesImage(index, i, series)">
<div
:class="{'series-active': i==seriesIndex && index === studyIndex}"
class="series-wrapper"
>
<el-image
class="image-preview"
:src="series.previewImageUrl"
fit="fill"
crossorigin="anonymous"
/>
<div :class="{ 'series-active': i == seriesIndex && index === studyIndex }" class="series-wrapper">
<el-image class="image-preview" :src="series.previewImageUrl" fit="fill" crossorigin="anonymous" />
<div class="image-desc">
<div class="flex-div">
<div style="width: 40px;display: flex;flex-direction: row;justify-content: space-between;">
<div v-if="!study.IsCriticalSequence">#{{ series.seriesNumber }}</div>
<div v-if="series.isDicom && series.prefetchInstanceCount<series.instanceCount * 100 && series.modality!== 'SR'">
<div
v-if="series.isDicom && series.prefetchInstanceCount < series.instanceCount * 100 && series.modality !== 'SR'">
<!-- 下载 -->
<el-tooltip v-if="!series.isLoading" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play" style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;" @click.stop="loadSeries(series,index,i)" />
<el-tooltip v-if="!series.isLoading" class="item" effect="dark"
:content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play"
style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;"
@click.stop="loadSeries(series, index, i)" />
</el-tooltip>
</div>
<el-tooltip v-else-if="series.isDicom && series.prefetchInstanceCount === 0 &&series.modality!== 'SR'" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play" style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;" @click.stop="loadSeries(series,index,i)" />
<el-tooltip
v-else-if="series.isDicom && series.prefetchInstanceCount === 0 && series.modality !== 'SR'"
class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play"
style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;"
@click.stop="loadSeries(series, index, i)" />
</el-tooltip>
</div>
<div v-if="series.isExistMutiFrames && series.instanceCount > 1">
<el-popover
placement="right"
trigger="hover"
popper-class="instance_frame_wrapper"
>
<el-popover placement="right" trigger="hover" popper-class="instance_frame_wrapper">
<div class="frame_list">
<div
v-for="(instance, idx) in series.instanceInfoList"
:key="instance.Id"
class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
<div v-for="(instance, idx) in series.instanceInfoList" :key="instance.Id"
class="frame_content" :class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < series.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click.stop="showMultiFrames(index,series, i, instance)"
>
@click.stop="showMultiFrames(index, series, i, instance)">
<!-- <div>
<img
class="image-preview"
@ -96,12 +83,14 @@
</div> -->
<div>
<div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ? instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ?
instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
</div>
</div>
</div>
<i slot="reference" class="el-icon-connection" style="font-size: 15px;cursor: pointer;color: #ffeb3b;" />
<i slot="reference" class="el-icon-connection"
style="font-size: 15px;cursor: pointer;color: #ffeb3b;" />
</el-popover>
</div>
</div>
@ -121,9 +110,11 @@
{{ series.modality }}: {{ series.instanceCount }} image
</p>
<div class="flex-div">
<div v-if="measureData && measureData.findIndex(v=>v.SeriesId === series.seriesId && v.MeasureData) > -1">
<div
v-if="measureData && measureData.findIndex(v => v.SeriesId === series.seriesId && v.MeasureData) > -1">
<!-- 有标注 -->
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:marked')" placement="right">
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:marked')"
placement="right">
<i class="el-icon-star-on" style="font-size: 16px;color: #ff5722;" />
</el-tooltip>
@ -131,10 +122,10 @@
</div>
</div>
</div>
<div v-if="series.isDicom && series.prefetchInstanceCount>0 && series.prefetchInstanceCount<series.instanceCount * 100" style="width: 100%;">
<el-progress
:percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))"
/>
<div
v-if="series.isDicom && series.prefetchInstanceCount > 0 && series.prefetchInstanceCount < series.instanceCount * 100"
style="width: 100%;">
<el-progress :percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))" />
</div>
</div>
@ -144,24 +135,19 @@
</div>
<div class="sr-wrapper">
<el-dialog
:visible.sync="srDialogVisible"
:custom-class="isSrFullscreen?'sr-full-dialog-container':'sr-dialog-container'"
:show-close="false"
:close-on-click-modal="false"
:fullscreen="isSrFullscreen"
>
<el-dialog :visible.sync="srDialogVisible"
:custom-class="isSrFullscreen ? 'sr-full-dialog-container' : 'sr-dialog-container'" :show-close="false"
:close-on-click-modal="false" :fullscreen="isSrFullscreen">
<span slot="title" class="dialog-footer">
<div style="position: absolute;right: 20px;top: 10px;">
<svg-icon :icon-class="isSrFullscreen?'exit-fullscreen':'fullscreen'" style="cursor: pointer;font-size: 20px;color:#000;" @click="isSrFullscreen=!isSrFullscreen" />
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;" @click="srDialogVisible = false" />
<svg-icon :icon-class="isSrFullscreen ? 'exit-fullscreen' : 'fullscreen'"
style="cursor: pointer;font-size: 20px;color:#000;" @click="isSrFullscreen = !isSrFullscreen" />
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;"
@click="srDialogVisible = false" />
</div>
</span>
<div style="height: 100%;margin:0;">
<SrList
v-if="srDialogVisible"
:sr-info="srInfo"
/>
<SrList v-if="srDialogVisible" :sr-info="srInfo" />
</div>
</el-dialog>
</div>
@ -266,6 +252,9 @@ export default {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.measureData = this.visitTaskList[idx].MeasureData
})
DicomEvent.$on("getStudyFile", () => {
DicomEvent.$emit('sendStudyFile', { studyList: this.studyList, studyIndex: this.studyIndex, seriesIndex: this.seriesIndex, visitTaskId: this.visitTaskId })
})
// DicomEvent.$on('setReadingState', readingTaskState => {
// var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
// if (idx > -1) {
@ -871,10 +860,12 @@ export default {
::v-deep .el-progress-bar__inner {
transition: width 0s ease;
}
width:100%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.dicom-desc {
font-weight: bold;
font-size: 13px;
@ -887,19 +878,23 @@ export default {
overflow-anchor: none;
touch-action: auto;
}
.series-active {
background-color: #607d8b !important;
border: 1px solid #607d8b !important;
}
::v-deep .el-progress__text {
color: #ccc;
font-size: 12px;
}
.series {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
.series-wrapper {
display: flex;
flex-direction: row;
@ -912,9 +907,11 @@ export default {
border-radius: 2px;
border: 1px solid #404040;
background-color: #3a3a3a;
.el-progress__text {
display: none;
}
.el-progress-bar {
padding-right: 0px;
}
@ -925,8 +922,10 @@ export default {
border: 2px solid #252525;
cursor: pointer;
}
.image-desc {
vertical-align: top;
p {
width: 95px;
white-space: nowrap;
@ -936,6 +935,7 @@ export default {
color: #ddd;
margin: 0px;
line-height: 1.5;
div {
width: 95px;
white-space: nowrap;
@ -944,6 +944,7 @@ export default {
}
}
}
.flex-div {
display: flex;
flex-direction: row;
@ -960,17 +961,21 @@ export default {
}
}
}
::v-deep .el-collapse {
border: none;
.el-collapse-item {
background-color: #000 !important;
color: #ddd;
}
.el-collapse-item__content {
padding-bottom: 5px;
background-color: #000 !important;
}
.el-collapse-item__header {
background-color: #000 !important;
color: #ddd;
@ -980,27 +985,33 @@ export default {
line-height: 20px;
}
}
.sr-wrapper {
::v-deep .el-dialog {
background: #fff !important;
border: 1px solid #ddd;
// color: #ddd;
.el-dialog__title {
color: #fff;
}
}
::v-deep .sr-dialog-container {
margin-top: 50px !important;
width: 75%;
height: 80%;
}
::v-deep .el-dialog__body {
padding: 10px;
height: calc(100% - 50px);
}
.el-dialog__header {
position: relative;
}
.sr-full-dialog-container {
::v-deep .is-fullscreen .el-dialog__body {
height: calc(100% - 50px);
@ -1008,7 +1019,6 @@ export default {
}
}
}
</style>
<style>
.instance_frame_wrapper {
@ -1017,18 +1027,22 @@ export default {
border: 1px solid #2c2c2c;
padding: 5px;
}
.frame_list {
max-height: 500px;
overflow-y: auto;
}
.instance_frame_wrapper ::-webkit-scrollbar {
width: 7px;
height: 7px;
}
.instance_frame_wrapper ::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.frame_content {
height: 50px;
padding: 5px;
@ -1038,6 +1052,7 @@ export default {
font-size: 12px;
border: 1px solid #404040;
}
.frame_content:hover {
/* font-weight: bold; */
/* box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); */
@ -1046,6 +1061,7 @@ export default {
border-color: #213a54 !important;
background-color: #213a54;
}
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;