suv融合
continuous-integration/drone/push Build is passing Details

uat
wangxiaoshuang 2025-05-08 10:36:57 +08:00
parent f53412acbf
commit 02fcc4a2c1
6 changed files with 998 additions and 145 deletions

View File

@ -0,0 +1,276 @@
<template>
<el-form ref="fusionForm" :model="fusionForm" label-width="120px" label-position="left">
<!-- 任务名称 -->
<el-form-item :label="$t('trials:lugano:fusionDialog:label:taskBlindName')" prop="taskBlindName">
<el-select v-model="fusionForm.visitTaskId" clearable @change="handleTaskIdChange">
<el-option v-for="item in taskList" :key="item.VisitTaskId" :label="item.TaskBlindName"
:value="item.VisitTaskId" />
</el-select>
</el-form-item>
<!-- 检查 -->
<el-form-item :label="$t('trials:lugano:fusionDialog:label:study')" prop="studyId" :rules="[
{ 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-select>
</el-form-item>
<!-- 融合图像 -->
<label style="color: #dfdfdf;line-height: 36px;vertical-align: middle;font-size: 14px;">{{
$t('trials:lugano:fusionDialog:label:fusionImages') }}</label>
<div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom:10px;"
class="series-table">
<div style="border: 1px solid #5e5e5e;border-radius: 5px;padding: 0 10px;">
<!-- CT/MRI序列 -->
<h3 style="color:#ddd">{{ $t('trials:lugano:fusionDialog:label:ctSeries') }}</h3>
<el-table ref="ctSeries" :data="ctSeries" :show-header="false" style="width: 380px" size="mini" height="300"
:default-sort="{ prop: 'instanceCount', order: 'descending' }" @selection-change="handleCTSelectionChange"
@select="handleCTSelect">
<el-table-column type="selection" width="30" />
<el-table-column label="" width="100">
<template slot-scope="scope">
<el-image style="width: 60px; height: 60px"
:src="`${OSSclientConfig.basePath}${scope.row.ImageResizePath}`" fit="fit" crossorigin="anonymous" />
</template>
</el-table-column>
<el-table-column label="" min-width="50" show-overflow-tooltip>
<template slot-scope="scope">
<div>#{{ scope.row.SeriesNumber }} </div>
<div>{{ scope.row.Modality }}: {{ scope.row.InstanceCount }} images T: {{
parseFloat(scope.row.SliceThickness).toFixed(digitPlaces) }}</div>
<div>{{ scope.row.Description }}</div>
</template>
</el-table-column>
</el-table>
</div>
<div style="border: 1px solid #5e5e5e;border-radius: 5px;padding: 0 10px;">
<!-- PET序列 -->
<h3 style="color:#ddd">{{ $t('trials:lugano:fusionDialog:label:ptSeries') }}</h3>
<el-table ref="petSeries" :data="petSeries" :show-header="false" style="width: 380px" size="mini" height="300"
@selection-change="handlePTSelectionChange" @select="handlePTSelect">
<el-table-column type="selection" width="30" />
<el-table-column label="" width="100">
<template slot-scope="scope">
<el-image style="width: 60px; height: 60px"
:src="`${OSSclientConfig.basePath}${scope.row.ImageResizePath}`" fit="fit" crossorigin="anonymous" />
</template>
</el-table-column>
<el-table-column label="" min-width="50" show-overflow-tooltip>
<template slot-scope="scope">
<div>#{{ scope.row.SeriesNumber }} </div>
<div>{{ scope.row.Modality }}: {{ scope.row.InstanceCount }} images T: {{
parseFloat(scope.row.SliceThickness).toFixed(digitPlaces) }}</div>
<div>{{ scope.row.Description }}</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-form-item style="text-align:right;">
<!-- 取消 -->
<el-button size="mini" @click="handleCancel">{{ $t('common:button:cancel') }}</el-button>
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSubmitFusionSeries">
{{ $t('common:button:confirm') }}</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'FusionForm',
props: {
taskList: {
type: Array,
default: () => {
return []
}
},
activeTaskIndex: {
type: Number,
default: 0
}
},
data() {
return {
fusionForm: {
visitTaskId: '',
studyId: '',
taskBlindName: '',
isBaseLineTask: null,
ctSeriesInfo: {},
petSeriesInfo: {},
defaultStudyId: '',
defaultCTSeriesId: '',
defaultPTSeriesId: ''
},
studyList: [],
ctSeries: [],
petSeries: [],
petctWindow: null,
digitPlaces: 2
}
},
mounted() {
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.initForm()
},
methods: {
initForm() {
if (this.taskList.length === 0) return
this.getStudyList(this.taskList[this.activeTaskIndex])
this.handleStudyIdChange(this.studyList[0].StudyId)
if (this.studyList.length > 0) {
this.fusionForm.studyId = this.studyList[0].StudyId
this.handleStudyIdChange(this.studyList[0].StudyId)
} else {
this.fusionForm.studyId = ''
this.ctSeries = []
this.petSeries = []
}
this.fusionForm.visitTaskId = this.taskList[this.activeTaskIndex].VisitTaskId
this.fusionForm.readingTaskState = this.taskList[this.activeTaskIndex].ReadingTaskState
this.fusionForm.taskBlindName = this.taskList[this.activeTaskIndex].TaskBlindName
this.fusionForm.subjectCode = this.taskList[this.activeTaskIndex].SubjectCode
this.fusionForm.isBaseLineTask = this.taskList[this.activeTaskIndex].IsBaseLineTask
this.fusionForm.isCurrentTask = this.taskList[this.activeTaskIndex].IsCurrentTask
},
getStudyList(obj) {
if (obj) {
var studyList = obj.StudyList || []
studyList = studyList.filter(i => !i.IsCriticalSequence && i.Modalities.indexOf('CT') !== -1 && i.Modalities.indexOf('PT') !== -1)
if (studyList.length === 0) return
this.studyList = studyList
}
},
handleSubmitFusionSeries() {
this.$refs['fusionForm'].validate((valid) => {
if (!valid) return
if (Object.keys(this.fusionForm.ctSeriesInfo).length === 0 || Object.keys(this.fusionForm.petSeriesInfo).length === 0) {
// ''
this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
type: 'warning'
}).then(() => { }).catch(() => { })
return
}
var count = Math.abs(this.fusionForm.ctSeriesInfo.instanceCount - this.fusionForm.petSeriesInfo.instanceCount)
if (count > 10) {
// ', ?'
this.$confirm(this.$t('trials:lugano:fusionDialog:message:validSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
type: 'warning'
}).then(() => {
this.fusion()
}).catch(() => { })
} else {
this.fusion()
}
})
},
fusion() {
this.$emit('fusion', { ct: this.fusionForm.ctSeriesInfo, pt: this.fusionForm.petSeriesInfo })
},
handleCancel() {
this.$emit('close')
},
handleTaskIdChange(v) {
var idx = this.taskList.findIndex(i => i.VisitTaskId === v)
this.studyList = idx > -1 ? this.getStudyList(this.taskList[idx]) : []
this.fusionForm.taskBlindName = idx > -1 ? this.taskList[idx].TaskBlindName : ''
this.fusionForm.isBaseLineTask = idx > -1 ? this.taskList[idx].IsBaseLineTask : null
this.studyList = idx > -1 ? this.taskList[idx].StudyList : null
this.fusionForm.studyId = ''
this.fusionForm.defaultStudyId = idx > -1 ? this.taskList[idx].StudyId : ''
this.fusionForm.defaultCTSeriesId = idx > -1 ? this.taskList[idx].CTSeriesId : ''
this.fusionForm.defaultPTSeriesId = idx > -1 ? this.taskList[idx].PTSeriesId : ''
this.ctSeries = []
this.petSeries = []
},
handleStudyIdChange(v) {
var i = this.studyList.findIndex(i => i.StudyId === v)
if (i === -1) {
this.ctSeries = []
this.petSeries = []
} else {
var seriesList = this.studyList[i].SeriesList
var series = seriesList.filter(series => series.Modality === 'CT')
this.ctSeries = series.sort((a, b) => b.instanceCount - a.instanceCount)
series = seriesList.filter(series => series.Modality === 'PT')
this.petSeries = series.sort((a, b) => b.instanceCount - a.instanceCount)
}
},
handleCTSelectionChange(rows) {
if (rows.length > 0) {
this.fusionForm.ctSeriesInfo = Object.assign({}, rows[0])
} else {
this.fusionForm.ctSeriesInfo = {}
}
},
handleCTSelect(selection) {
if (selection.length > 1) {
const del_row = selection.shift()
this.$refs.ctSeries.toggleRowSelection(del_row, false)
}
},
handlePTSelectionChange(rows) {
if (rows.length > 0) {
this.fusionForm.petSeriesInfo = Object.assign({}, rows[0])
} else {
this.fusionForm.petSeriesInfo = {}
}
},
handlePTSelect(selection) {
if (selection.length > 1) {
const del_row = selection.shift()
this.$refs.petSeries.toggleRowSelection(del_row, false)
}
}
}
}
</script>
<style lang="scss" scoped>
.series-table {
::v-deep.el-table {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
::v-deep.el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #dfdfdf;
}
.el-table--border::after,
.el-table--group::after,
.el-table::before {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
}
::v-deep.el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
tr:hover>td {
background-color: #1e1e1e !important;
}
}
::v-deep.el-table__empty-block {
background-color: #1e1e1e !important;
}
}
</style>

View File

@ -921,11 +921,13 @@ export default {
this.rows = 2;
this.cols = 2;
this.$refs[`viewport-${0}`][0].setSeriesInfo(this.ctSeriesInfo)
this.$refs[`viewport-${1}`][0].setSeriesInfo(this.petSeriesInfo)
this.$refs[`viewport-${2}`][0].setSeriesInfo({ ct: this.ctSeriesInfo, pet: this.petSeriesInfo }, false, { isFusion: true })
this.$refs[`viewport-${3}`][0].setSeriesInfo(this.petSeriesInfo, false, { isMip: true })
this.$refs[`viewport-${1}`][0].setSeriesInfo(this.petSeriesInfo, false, { colorMap: true })
this.$refs[`viewport-${2}`][0].setSeriesInfo({ ct: this.ctSeriesInfo, pet: this.petSeriesInfo }, false, { isFusion: true, colorMap: true })
this.$refs[`viewport-${3}`][0].setSeriesInfo(this.petSeriesInfo, false, { isMip: true, colorMap: true })
this.cellType = ['CT', 'PT', 'FUSION', 'MIP']
this.$nextTick(() => {
this.setColorMap(this.rgbPresetName)
})
},
handleStudyIdChange(v) {
var i = this.studyList.findIndex(i => i.StudyId === v)

View File

@ -1,5 +1,5 @@
<template>
<div ref="viewport" class="viewport-wrapper" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
<div ref="viewport-fusion" class="viewport-wrapper" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
@mouseleave="sliderMouseleave">
<div v-if="series && taskInfo" class="left-top-text">
<div v-if="taskInfo.IsExistsClinicalData && !isMip && !isFusion" class="cd-info"
@ -38,7 +38,7 @@
<div>{{ series.Description }}</div>
</div>
<div v-if="series" class="left-bottom-text">
<div v-show="mousePosition.index.length > 0">
<div v-show="mousePosition.index.length > 0 && !isMip">
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
</div>
<div
@ -184,6 +184,7 @@ export default {
initLeft: null,
isMove: false
},
ptVolumeId: null,
}
},
mounted() {
@ -196,7 +197,7 @@ export default {
},
methods: {
initViewport() {
this.element = this.$refs['viewport']
this.element = this.$refs['viewport-fusion']
const resizeObserver = new ResizeObserver(() => {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (renderingEngine) {
@ -243,7 +244,17 @@ export default {
this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
this.imageInfo.location = imagePlaneModule.sliceLocation
this.getOrientationMarker()
this.$emit('renderAnnotations', this.series)
// this.$emit('renderAnnotations', this.series)
var properties = viewport.getProperties()
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const windowWidth = upper - lower
const windowCenter = (upper + lower) / 2
this.defaultWindowLevel.windowWidth = windowWidth
this.defaultWindowLevel.windowCenter = windowCenter
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
// const toolGroupId = `viewport-${this.viewportIndex}`
// const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
// toolGroup.setToolEnabled('ScaleOverlay')
@ -295,7 +306,6 @@ export default {
},
resetOrientationMarkers() {
if (this.originalMarkers.length > 0) {
console.log(this.originalMarkers)
this.orientationMarkers = [...this.originalMarkers]
this.setMarkers()
}
@ -388,6 +398,18 @@ export default {
// this.$refs[vp.id].voiModified()
this.voiModified()
})
if (this.ptVolumeId) {
const viewportsContainingVolumeUIDPT = csUtils.getViewportsWithVolumeId(
this.ptVolumeId,
viewport.renderingEngineId
)
viewport.setProperties({ voiRange }, this.ptVolumeId)
viewportsContainingVolumeUIDPT.forEach((vp) => {
vp.render()
this.voiModified()
})
}
},
renderColorBar(presetName) {
var colorMap = null
@ -470,78 +492,64 @@ export default {
imageIds: obj.ImageIds
})
},
async setSeriesInfo(obj, isLocate = false, option = {}) {
async setSeriesInfo(obj, option = {}) {
try {
if (this.series && obj.Id === this.series.Id && obj.Description === this.series.Description && !isLocate) {
obj.SliceIndex = this.series.SliceIndex
let { data, volumeId } = obj
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description) {
data.SliceIndex = this.series.SliceIndex
}
this.ptVolumeId = null
this.series = {}
let { isFusion, isMip } = option
let { isFusion, isMip, colorMap } = option
this.isFusion = isFusion;
this.isMip = isMip;
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
viewport.setOrientation(OrientationAxis.AXIAL)
const toolGroup = ToolGroupManager.getToolGroup(this.viewportId)
toolGroup.setToolEnabled(MIPJumpToClickTool.toolName)
toolGroup.setToolEnabled(VolumeRotateTool.toolName)
if (this.isFusion) {
this.$nextTick(() => {
this.renderColorBar(this.presetName)
})
let { ct, pet } = obj
this.series = { ...pet }
let { ct, data, ctVolumeId, ptVolumeId, fusionVolumeId } = obj
this.series = { ...data }
this.ctSeries = { ...ct }
this.petSeries = { ...pet }
const maxCount = Math.max(this.ctSeries.ImageIds.length, this.petSeries.ImageIds.length)
this.$set(this.ctSeries, 'imageMaxLength', maxCount)
this.$set(this.petSeries, 'imageMaxLength', maxCount)
await this.createImageIdsAndCacheMetaData(this.ctSeries)
await this.createImageIdsAndCacheMetaData(this.petSeries)
let ctVolumeId =
'fusion_ct' + ':' + csUtils.uuidv4()
let ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, { imageIds: this.ctSeries.ImageIds })
this.volumeId =
'fusion_ct' + ':' + csUtils.uuidv4()
let petVolume = await volumeLoader.createAndCacheVolume(this.volumeId, { imageIds: this.petSeries.ImageIds })
ctVolume.load();
petVolume.load();
this.petSeries = { ...data }
// const maxCount = Math.max(this.ctSeries.ImageIds.length, this.petSeries.ImageIds.length)
// this.$set(this.ctSeries, 'imageMaxLength', maxCount)
// this.$set(this.petSeries, 'imageMaxLength', maxCount)
// await this.createImageIdsAndCacheMetaData(this.ctSeries)
// await this.createImageIdsAndCacheMetaData(this.petSeries)
this.volumeId = ptVolumeId
this.ptVolumeId = fusionVolumeId
await viewport
.setVolumes([{
.setVolumes([
{
volumeId: this.ptVolumeId, callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r)
console.log("融合pet渲染成功");
}
},
{
volumeId: ctVolumeId, callback: (r) => {
setCtTransferFunctionForVolumeActor(r)
console.log("融合ct渲染成功")
}
}, {
},
{
volumeId: this.volumeId, callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r)
console.log("融合pet渲染成功");
}
}])
},
]).then(res => {
if (colorMap) {
this.setColorMap(this.presetName)
}
})
} else {
this.series = { ...obj }
await this.createImageIdsAndCacheMetaData(obj)
this.volumeId =
'volume' + ':' + csUtils.uuidv4()
let volume = await volumeLoader.createAndCacheVolume(this.volumeId, { imageIds: obj.ImageIds })
volume.load();
this.series = { ...data }
this.volumeId = volumeId
if (this.isMip) {
viewport.setOrientation(OrientationAxis.CORONAL);
toolGroup.setToolActive(VolumeRotateTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel, // mouse wheel
},
],
})
toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary, // Left Click
},
],
})
let { volume } = obj
const ptVolumeDimensions = volume.dimensions;
const slabThickness = Math.sqrt(
ptVolumeDimensions[0] * ptVolumeDimensions[0] +
@ -552,6 +560,7 @@ export default {
.setVolumes([{
volumeId: this.volumeId,
callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r)
console.log("mip渲染成功")
},
slabThickness,
@ -559,23 +568,31 @@ export default {
defaultOptions: {
orientation: OrientationAxis.CORONAL
}
}])
}]).then(res => {
if (colorMap) {
this.setColorMap(this.presetName)
}
})
} else {
viewport
.setVolumes([{
volumeId: this.volumeId, callback: (r) => {
if (this.series.Modality === 'PT') {
setPetColorMapTransferFunctionForVolumeActor(r)
} else {
setCtTransferFunctionForVolumeActor(r)
}
console.log("渲染成功")
}
}])
}]).then(res => {
if (colorMap) {
this.setColorMap(this.presetName)
}
})
}
}
viewport.render()
if (!this.series.cellType) {
if (this.isFusion) return this.series.cellType = 'FUSION'
if (this.isMip) return this.series.cellType = 'MIP'
this.series.cellType = this.series.Modality
}
} catch (e) {
console.log(e)
}
@ -660,7 +677,7 @@ export default {
},
handletoolsMouseWheel(e) {
const { viewportId, wheel } = e.detail
if (this.series.cellType === 'MIP') {
if (this.isMip) {
const container = document.getElementById('rotateBar')
const slider = document.getElementById('rotateSlider')
const containerWidth = container.offsetWidth
@ -922,7 +939,7 @@ export default {
bottom: 5px;
left: 50%;
transform: translateX(-50%);
background: #fff;
background: #333;
z-index: 10;
cursor: pointer;

View File

@ -1,5 +1,6 @@
<template>
<div v-loading="loading" class="read-page-container">
<div v-loading="loading" :element-loading-text="loadingText" element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)" class="read-page-container">
<!-- 检查列表 -->
<div class="left-panel">
<div class="task-container">
@ -42,9 +43,9 @@
<div class="tools-left">
<!-- 布局 -->
<div
class="tool-item"
:class="['tool-item', isFusion ? 'tool-disabled' : '']"
:title="$t('trials:reading:button:layout')"
@click.stop="showPanel($event)"
@click.stop="showPanel($event, 'layout')"
@mouseleave="toolMouseout"
>
<div class="dropdown">
@ -198,6 +199,10 @@
<svg-icon v-if="forceFitToWindow" icon-class="fitToWindow" class="svg-icon" />
<svg-icon v-else icon-class="fitToImage" class="svg-icon" />
</div>
<!--融合-->
<div class="tool-item" :title="$t('trials:lugano:button:fusion')" @click.prevent="openFusion" v-if="readingTool === 2">
<svg-icon icon-class="fusion" class="svg-icon" />
</div>
<div
v-for="tool in tools"
:key="tool.toolName"
@ -297,6 +302,10 @@
</div>
</div>
</div>
<!-- 伪彩 -->
<template v-if="readingTool === 2">
<colorMap v-show="isFusion" ref="colorMap" @setColorMap="setColorMap" @voiChange="voiChange" />
</template>
</div>
<div>
@ -331,8 +340,8 @@
<div ref="container" class="grid-container" :style="gridStyle">
<div
v-for="(v, index) in cellsMax"
v-show="index < cells.length"
:key="index"
v-show="index < cells.length && !isFusion"
:key="`viewport-${index}`"
:style="cellStyle"
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
@dblclick="toggleFullScreen($event, index)"
@ -350,6 +359,29 @@
@renderAnnotations="renderAnnotations"
/>
</div>
<template v-if="readingTool === 2">
<div
v-for="(v, index) in cellsMax"
v-show="index < cells.length && isFusion"
:key="`viewport-fusion-${index}`"
:style="cellStyle"
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
@dblclick="toggleFullScreen($event, index)"
@click="activeViewport(index)"
>
<PetCtViewport
:ref="`viewport-fusion-${index}`"
:data-viewport-uid="`viewport-fusion-${index}`"
:rendering-engine-id="renderingEngineId"
:viewport-id="`viewport-fusion-${index}`"
:viewport-index="index"
@activeViewport="activeViewport"
@toggleTaskByViewport="toggleTaskByViewport"
@previewCD="previewCD"
@renderAnnotations="renderAnnotations"
/>
</div>
</template>
</div>
</div>
<!-- 表单 -->
@ -486,6 +518,10 @@
</div>
</el-dialog>
<el-dialog :visible.sync="fusionVisible" :close-on-click-modal="false" :title="$t('trials:lugano:button:record')"
width="850px" >
<FusionForm v-if="fusionVisible" :activeTaskIndex="activeTaskIndex" :taskList="visitTaskList" @close="closeFusion" @fusion="handleFusion" />
</el-dialog>
</div>
</template>
<script>
@ -497,6 +533,7 @@ import {
Enums,
// imageLoader,
// metaData,
volumeLoader,
getRenderingEngine,
eventTarget,
utilities as csUtils,
@ -511,6 +548,7 @@ import html2canvas from 'html2canvas'
import { getTools, getCustomizeStandardsTools, config } from './toolConfig'
import StudyList from './StudyList'
import Viewport from './Viewport'
import PetCtViewport from './PetCtViewport'
import mRecisit from './mRecist/QuestionList'
import recisit from './Recist/QuestionList'
import customizeQuestionList from './customize/QuestionList'
@ -520,6 +558,8 @@ import Hotkeys from '@/views/trials/trials-panel/reading/dicoms/components/Hotke
import WL from '@/views/trials/trials-panel/reading/dicoms/components/WL'
import Others from '@/views/trials/trials-panel/reading/dicoms/components/Others'
import ClinicalData from '@/views/trials/trials-panel/reading/clinical-data'
import FusionForm from './FusionForm.vue'
import colorMap from './colorMap.vue'
const { visibility } = annotation
const { ViewportType, Events } = Enums
const renderingEngineId = 'myRenderingEngine'
@ -539,7 +579,9 @@ const {
RectangleROITool,
PlanarFreehandROITool,
CircleROITool,
EraserTool
EraserTool,
MIPJumpToClickTool,
VolumeRotateTool,
// cursors
} = cornerstoneTools
@ -570,9 +612,16 @@ annotation.config.style.setDefaultToolStyles(newStyles)
const { MouseBindings, Events: toolsEvents } = csToolsEnums
export default {
name: 'ReadPage',
props: {
readingTool: {
type: Number,
default: 2
}
},
components: {
StudyList,
Viewport,
PetCtViewport,
mRecisit,
recisit,
customizeQuestionList,
@ -581,7 +630,9 @@ export default {
Hotkeys,
WL,
Others,
ClinicalData
ClinicalData,
FusionForm,
colorMap
},
data() {
return {
@ -645,7 +696,13 @@ export default {
instanceInfo: {},
lastViewportTaskIds: [],
markedSeriesIds: [],
customizeStandards: []
customizeStandards: [],
fusionVisible: false,
isFusion: false,
studyList: [],
volumeData: {},
loadingText: null
}
},
computed: {
@ -668,6 +725,9 @@ export default {
},
cells() {
return Array(this.rows * this.cols).fill(0)
},
viewportKey() {
return this.isFusion ? 'viewport-fusion' : 'viewport'
}
},
watch: {
@ -682,9 +742,9 @@ export default {
immediate: true,
handler(index) {
let series = null
if (this.$refs[`viewport-${this.activeViewportIndex}`] && this.$refs[`viewport-${this.activeViewportIndex}`][0]) {
this.clipPlaying = this.$refs[`viewport-${this.activeViewportIndex}`][0].playClipState
series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
if (this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`] && this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0]) {
this.clipPlaying = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].playClipState
series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
} else {
this.clipPlaying = false
this.fps = 15
@ -950,7 +1010,7 @@ export default {
if (activeStudyIndex > -1 && activeSeriesIndex > -1) {
seriesArr.map((i, index) => {
this.$refs[`viewport-${index}`][0].setSeriesInfo(i)
this.$refs[`${this.viewportKey}-${index}`][0].setSeriesInfo(i)
})
const visitTaskId = this.visitTaskList[this.activeTaskIndex].VisitTaskId
this.lastViewportTaskId = visitTaskId
@ -971,7 +1031,7 @@ export default {
const element3 = this.$refs['viewport-2'][0].$el
const element4 = this.$refs['viewport-3'][0].$el
const viewportInputArray = [
let viewportInputArray = [
{
viewportId: 'viewport-0',
type: ViewportType.STACK,
@ -993,7 +1053,52 @@ export default {
element: element4
}
]
const viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
let fusionViewportIds = ["viewport-fusion-0", "viewport-fusion-1", "viewport-fusion-2", "viewport-fusion-3"]
if(this.readingTool === 2){
const fusionElement1 = this.$refs['viewport-fusion-0'][0].$el
const fusionElement2 = this.$refs['viewport-fusion-1'][0].$el
const fusionElement3 = this.$refs['viewport-fusion-2'][0].$el
const fusionElement4 = this.$refs['viewport-fusion-3'][0].$el
let arr = [
{
viewportId: 'viewport-fusion-0',
type: ViewportType.ORTHOGRAPHIC,
element: fusionElement1,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL
}
},
{
viewportId: 'viewport-fusion-1',
type: ViewportType.ORTHOGRAPHIC,
element: fusionElement2,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL,
// background: [1, 1, 1]
}
},
{
viewportId: 'viewport-fusion-2',
type: ViewportType.ORTHOGRAPHIC,
element: fusionElement3,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL
}
},
{
viewportId: 'viewport-fusion-3',
type: ViewportType.ORTHOGRAPHIC,
element: fusionElement4,
defaultOptions: {
orientation: Enums.OrientationAxis.CORONAL,
// background: [1, 1, 1]
}
}
]
viewportInputArray = [ ...viewportInputArray, ...arr ]
viewportIds = viewportIds.concat(fusionViewportIds)
}
renderingEngine.setViewports(viewportInputArray)
this.addAnnotationListeners()
cornerstoneTools.addTool(StackScrollTool)
@ -1010,9 +1115,12 @@ export default {
cornerstoneTools.addTool(BidirectionalTool)
cornerstoneTools.addTool(ScaleOverlayTool)
cornerstoneTools.addTool(CircleROITool)
cornerstoneTools.addTool(MIPJumpToClickTool)
cornerstoneTools.addTool(VolumeRotateTool)
viewportIds.forEach((viewportId, i) => {
const toolGroupId = `viewport-${i}`
// const toolGroupId = `viewport-${i}`
const toolGroupId = viewportId
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId)
toolGroup.addViewport(viewportId, renderingEngineId)
toolGroup.addTool(StackScrollTool.toolName)
@ -1029,7 +1137,7 @@ export default {
return doneChangingTextCallback(data.text)
},
getTextCallback: async(doneChangingTextCallback) => {
return doneChangingTextCallback('_')
return doneChangingTextCallback('Annotation')
}
})
toolGroup.addTool(RectangleROITool.toolName, {
@ -1054,7 +1162,29 @@ export default {
toolGroup.addTool(CircleROITool.toolName, {
getTextLines: this.getCircleROIToolTextLines
})
if(toolGroupId === 'viewport-fusion-3'){
toolGroup.addTool(VolumeRotateTool.toolName);
toolGroup.setToolActive(VolumeRotateTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel, // mouse wheel
},
],
});
toolGroup.addTool(MIPJumpToClickTool.toolName, {
targetViewportIds: fusionViewportIds,
});
// Set the initial state of the tools, here we set one tool active on left click.
// This means left click will draw that tool.
toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary, // Left Click
},
],
});
}
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Wheel }]
})
@ -1210,7 +1340,7 @@ export default {
const { annotation } = e.detail
if (!annotation) return
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const referencedImageId = annotation.metadata.referencedImageId
const params = this.getInstanceInfo(referencedImageId)
@ -1236,7 +1366,7 @@ export default {
if (!annotation.highlighted) return
if (!annotation) return
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
// this.$refs['ecrf'].modifyAnnotation({ annotation, toolName: annotation.metadata.toolName })
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].modifyAnnotation({ annotation, toolName: annotation.metadata.toolName })
@ -1264,7 +1394,7 @@ export default {
return
}
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const referencedImageId = annotation.metadata.referencedImageId
const params = this.getInstanceInfo(referencedImageId)
@ -1323,7 +1453,7 @@ export default {
}
if (!annotation.data.label) return
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
this.saveCustomAnnotation(annotation)
}
@ -1351,7 +1481,7 @@ export default {
}
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `viewport-${i}`
const viewportId = `${this.viewportKey}-${i}`
const viewport = renderingEngine.getViewport(viewportId)
viewport.render()
}
@ -1359,7 +1489,7 @@ export default {
cornerstoneTools.annotation.state.addAnnotation(annotation)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `viewport-${i}`
const viewportId = `${this.viewportKey}-${i}`
const viewport = renderingEngine.getViewport(viewportId)
viewport.render()
}
@ -1371,7 +1501,7 @@ export default {
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `viewport-${i}`
const viewportId = `${this.viewportKey}-${i}`
const viewport = renderingEngine.getViewport(viewportId)
viewport.render()
}
@ -1440,7 +1570,7 @@ export default {
})
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `viewport-${i}`
const viewportId = `${this.viewportKey}-${i}`
const viewport = renderingEngine.getViewport(viewportId)
viewport.render()
}
@ -1457,7 +1587,7 @@ export default {
}
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportId = `viewport-${this.activeViewportIndex}`
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
viewport.render()
},
@ -1613,7 +1743,7 @@ export default {
},
//
setToolActive(toolName) {
const toolGroupId = `viewport-${this.activeViewportIndex}`
const toolGroupId = `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (this.activeTool === toolName) {
toolGroup.setToolPassive(this.activeTool)
@ -1633,9 +1763,9 @@ export default {
if (this.readingTaskState === 2) return
const toolObj = this.tools.find(i => i.toolName === toolName)
if (!toolObj || toolObj.isDisabled) return
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const toolGroupId = `viewport-${this.activeViewportIndex}`
const toolGroupId = `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (this.activeTool === toolName) {
toolGroup.setToolPassive(this.activeTool)
@ -1654,9 +1784,9 @@ export default {
setMoreToolActive(toolName) {
if (this.readingTaskState === 2) return
this.setToolsPassive()
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const toolGroupId = `viewport-${this.activeViewportIndex}`
const toolGroupId = `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
@ -1666,7 +1796,7 @@ export default {
},
setToolsPassive() {
if (!this.activeTool) return
const toolGroupIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
const toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
toolGroupIds.forEach(toolGroupId => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolPassive(this.activeTool)
@ -1675,7 +1805,7 @@ export default {
},
setToolEnabled() {
if (!this.activeTool) return
const toolGroupIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
const toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
toolGroupIds.forEach(toolGroupId => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolEnabled(this.activeTool)
@ -1686,7 +1816,7 @@ export default {
enter(e, toolName) {
const i = this.tools.findIndex(i => i.toolName === toolName)
if (i === -1) return
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
const isCurrentTask = series.TaskInfo.IsCurrentTask
const readingTaskState = this.readingTaskState
if (!isCurrentTask || readingTaskState >= 2) {
@ -1715,7 +1845,7 @@ export default {
setViewportRotate(value) {
this.setToolsPassive()
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportId = `viewport-${this.activeViewportIndex}`
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
const type = parseInt(value)
// 123490590
@ -1740,15 +1870,15 @@ export default {
viewport.setViewPresentation({ rotation: rotation + 90 })
viewport.render()
}
this.$refs[`viewport-${this.activeViewportIndex}`][0].rotateOrientationMarkers(type)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].rotateOrientationMarkers(type)
},
//
resetViewport() {
this.setToolsPassive()
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportId = `viewport-${this.activeViewportIndex}`
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
this.$refs[`viewport-${this.activeViewportIndex}`][0].resetOrientationMarkers()
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resetOrientationMarkers()
viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
viewport.resetProperties()
viewport.render()
@ -1759,7 +1889,7 @@ export default {
this.setToolsPassive()
this.fullScreenIndex = null
this.layout = v
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
const seriesArr = []
if (v === 1) {
this.rows = 1
@ -1830,7 +1960,7 @@ export default {
this.activeViewportIndex = 3
}
seriesArr.map((i, index) => {
this.$refs[`viewport-${index}`][0].setSeriesInfo(i)
this.$refs[`${this.viewportKey}-${index}`][0].setSeriesInfo(i)
})
this.$nextTick(() => {
const renderingEngine = getRenderingEngine(renderingEngineId)
@ -1841,7 +1971,7 @@ export default {
changeVoiRange(v) {
this.setToolsPassive()
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportId = `viewport-${this.activeViewportIndex}`
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
if (v.val === -1) {
//
@ -1849,7 +1979,7 @@ export default {
viewport.render()
} else if (v.val === 0) {
//
const wwwc = this.$refs[`viewport-${this.activeViewportIndex}`][0].imageInfo.wwwc
const wwwc = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].imageInfo.wwwc
this.activeViewportWW = wwwc ? parseInt(wwwc.split('/')[0]) : null
this.activeViewportWC = wwwc ? parseInt(wwwc.split('/')[1]) : null
this.customWwc.visible = true
@ -1870,7 +2000,7 @@ export default {
//
setWwwc(v) {
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportId = `viewport-${this.activeViewportIndex}`
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
const lower = v.wc - v.ww / 2
const upper = v.wc + v.ww / 2 - 1
@ -1881,7 +2011,7 @@ export default {
//
toggleInvert() {
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`viewport-${this.activeViewportIndex}`)
const viewport = renderingEngine.getViewport(`${this.viewportKey}-${this.activeViewportIndex}`)
const { invert } = viewport.getProperties()
viewport.setProperties({ invert: !invert })
viewport.render()
@ -1889,12 +2019,12 @@ export default {
//
scrollPage(type) {
this.clipPlaying = false
this.$refs[`viewport-${this.activeViewportIndex}`][0].scrollPage(type)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(type)
},
//
toggleClipPlay(isPlay) {
this.clipPlaying = !this.clipPlaying
this.$refs[`viewport-${this.activeViewportIndex}`][0].toggleClipPlay(isPlay, this.fps)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].toggleClipPlay(isPlay, this.fps)
},
//
async getWwcTpl() {
@ -1939,18 +2069,18 @@ export default {
this.activeViewport(viewportIndex)
} else if (shortcutKeyEnum === 3) {
//
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
this.$refs[this.activeTaskId][0].getPreviousOrNextSeries(-1, series)
} else if (shortcutKeyEnum === 4) {
//
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
this.$refs[this.activeTaskId][0].getPreviousOrNextSeries(1, series)
} else if (shortcutKeyEnum === 5) {
//
this.$refs[`viewport-${this.activeViewportIndex}`][0].scrollPage(-1)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(-1)
} else if (shortcutKeyEnum === 6) {
//
this.$refs[`viewport-${this.activeViewportIndex}`][0].scrollPage(1)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(1)
} else if (shortcutKeyEnum === 7) {
//
this.setViewportRotate(4)
@ -1965,16 +2095,16 @@ export default {
this.setViewportRotate(2)
} else if (shortcutKeyEnum === 11) {
//
this.$refs[`viewport-${this.activeViewportIndex}`][0].setZoom(1)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setZoom(1)
} else if (shortcutKeyEnum === 12) {
//
this.$refs[`viewport-${this.activeViewportIndex}`][0].setZoom(-1)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setZoom(-1)
} else if (shortcutKeyEnum === 13) {
//
this.$refs[`viewport-${this.activeViewportIndex}`][0].resize(false)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(false)
} else if (shortcutKeyEnum === 14) {
//
this.$refs[`viewport-${this.activeViewportIndex}`][0].resize(true)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(true)
} else if (shortcutKeyEnum === 15) {
//
} else if (shortcutKeyEnum === 16) {
@ -1982,11 +2112,11 @@ export default {
this.toggleInvert()
} else if (shortcutKeyEnum === 17) {
// /
const wwwcIdx = this.$refs[`viewport-${this.activeViewportIndex}`][0].wwwcIdx
const wwwcIdx = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].wwwcIdx
const newWwwcIdx = wwwcIdx === this.wwwcArr.length - 1 ? 3 : wwwcIdx + 1
const wwwcTpl = this.wwwcArr[newWwwcIdx]
this.changeVoiRange(wwwcTpl)
this.$refs[`viewport-${this.activeViewportIndex}`][0].setWwwcIdx(newWwwcIdx)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setWwwcIdx(newWwwcIdx)
} else if (shortcutKeyEnum === 18) {
//
this.resetViewport()
@ -2002,7 +2132,7 @@ export default {
})
}
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
const viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
renderingEngine.renderViewports(viewportIds)
}
event.stopImmediatePropagation()
@ -2019,7 +2149,7 @@ export default {
},
fitToType(forceFitToWindow) {
this.forceFitToWindow = !forceFitToWindow
this.$refs[`viewport-${this.activeViewportIndex}`][0].resize(forceFitToWindow)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(forceFitToWindow)
},
//
toggleFullScreen(e, index) {
@ -2027,6 +2157,12 @@ export default {
this.activeViewportIndex = index
},
async toggleTask(taskInfo, taskIndex) {
if(this.isFusion){
let confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
if(!confirm) return false
this.isFusion = false
this.setToolsPassive()
}
if (taskIndex === this.activeTaskIndex) return
if (!this.selectArr.includes(taskInfo.VisitTaskId)) {
this.selectArr.push(taskInfo.VisitTaskId)
@ -2082,7 +2218,7 @@ export default {
}
const series = this.getRelatedSeries(this.visitTaskList[i], obj.series)
this.$nextTick(() => {
this.$refs[`viewport-${this.activeViewportIndex}`][0].setSeriesInfo(series)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series)
this.$refs[this.activeTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
if (this.activeViewportIndex === this.cells.length - 1) {
this.lastViewportTaskId = series.TaskInfo.VisitTaskId
@ -2092,7 +2228,17 @@ export default {
this.setToolsPassive()
},
async activeSeries(obj) {
this.$refs[`viewport-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
if(this.isFusion){
let confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
if(!confirm) return false
this.isFusion = false
this.setToolsPassive()
this.rows = 1
this.cols = 1
this.activeViewportIndex = 0
return this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
}
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
this.clipPlaying = false
this.fps = 15
if (this.activeViewportIndex === this.cells.length - 1) {
@ -2104,7 +2250,7 @@ export default {
}
},
async showMultiFrame(obj) {
this.$refs[`viewport-${this.activeViewportIndex}`][0].setSeriesInfo(obj, true)
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj, true)
this.clipPlaying = false
this.fps = 15
if (this.activeViewportIndex === this.cells.length - 1) {
@ -2118,7 +2264,7 @@ export default {
if (this.activeViewportIndex === index) return
this.activeViewportIndex = index
//
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series) {
const i = this.visitTaskList.findIndex(v => v.VisitTaskId === series.TaskInfo.VisitTaskId)
if (i > -1) {
@ -2243,7 +2389,7 @@ export default {
if (measureData) {
await this.imageLocation(measureData)
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="viewport-${this.activeViewportIndex}"]`
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
)
const canvas = await html2canvas(divForDownloadViewport)
const base64Str = canvas.toDataURL('image/png', 1)
@ -2327,24 +2473,24 @@ export default {
// }
//
currentAddSeries = this.$refs[`viewport-${this.cells.length - 1}`][0].series
currentAddSeries = this.$refs[`${this.viewportKey}-${this.cells.length - 1}`][0].series
}
} else {
//
currentAddSeries = this.getMarkedSeries(this.visitTaskList[index].StudyList, obj)
if (Object.keys(currentAddSeries).length === 0) {
// cells.length - 1
currentAddSeries = this.$refs[`viewport-${this.cells.length - 1}`][0].series
currentAddSeries = this.$refs[`${this.viewportKey}-${this.cells.length - 1}`][0].series
}
firstAddSeries = currentAddSeries
}
for (let i = 0; i < this.cells.length; i++) {
if (i === this.cells.length - 1) {
this.$refs[`viewport-${i}`][0].setSeriesInfo(currentAddSeries, true)
this.$refs[`${this.viewportKey}-${i}`][0].setSeriesInfo(currentAddSeries, true)
this.activeViewportIndex = i
this.$refs[currentAddSeries.TaskInfo.VisitTaskId][0].setSeriesActive(currentAddSeries.StudyIndex, currentAddSeries.SeriesIndex)
} else {
this.$refs[`viewport-${i}`][0].setSeriesInfo(firstAddSeries, true)
this.$refs[`${this.viewportKey}-${i}`][0].setSeriesInfo(firstAddSeries, true)
}
}
this.setToolsPassive()
@ -2362,7 +2508,7 @@ export default {
if (this.activeTool) {
this.setToolsPassive()
}
const toolGroupId = `viewport-${this.activeViewportIndex}`
const toolGroupId = `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
@ -2463,7 +2609,8 @@ export default {
return null
}
},
showPanel(e) {
showPanel(e, toolName) {
if(toolName === 'layout' && this.isFusion) return false
e.currentTarget.firstChild.lastChild.style.display = 'block'
},
toolMouseout(e) {
@ -2477,7 +2624,94 @@ export default {
callback.apply(this, arguments)
}, delay)
}
},
setColorMap(rgbPresetName) {
let fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
fusionViewportIds.forEach(id => {
this.$refs[id][0].setPreset(rgbPresetName)
this.$refs[id][0].renderColorBar(rgbPresetName)
this.$refs[id][0].setColorMap(rgbPresetName)
})
},
voiChange(v) {
let fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
fusionViewportIds.forEach(id => {
this.$refs[id][0].voiChange(v)
})
},
async handleFusion(data) {
try{
this.fusionVisible = false
this.isFusion = true
this.rows = 2;
this.cols = 2
let { ct, pt } = data
this.loading = true
this.loadingText = this.$t('trials:lugano:message:loadVolumes')
await this.getVolume(ct)
await this.getVolume(pt)
await this.getVolume(pt, true)
this.loading = false
this.loadingText = null
let ctData = {
data: ct,
volumeId: this.volumeData[ct.Id].volumeId,
}
let ptData = {
data: pt,
volumeId: this.volumeData[pt.Id].volumeId,
volume: this.volumeData[pt.Id].volume,
}
let fusionData = {
ct,
data: pt,
volumeId: this.volumeData[pt.Id].volumeId,
ctVolumeId: this.volumeData[ct.Id].volumeId,
ptVolumeId: this.volumeData[pt.Id].volumeId,
fusionVolumeId: this.volumeData[`fusion_${pt.Id}`].volumeId,
}
this.$refs[`viewport-0`][0].setSeriesInfo(ct)
this.$refs[`viewport-1`][0].setSeriesInfo(pt)
this.$refs[`viewport-2`][0].setSeriesInfo(pt)
this.$refs[`viewport-3`][0].setSeriesInfo(pt)
this.$refs[`viewport-fusion-0`][0].setSeriesInfo(ctData)
this.$refs[`viewport-fusion-1`][0].setSeriesInfo(ptData, { colorMap: true})
this.$refs[`viewport-fusion-2`][0].setSeriesInfo(fusionData, { isFusion: true, colorMap: true })
this.$refs[`viewport-fusion-3`][0].setSeriesInfo(ptData, { isMip: true, colorMap: true })
this.$nextTick(() => {
this.$refs[`colorMap`].init()
})
}catch(err){
console.log(err)
this.loading = false
this.loadingText = null
}
},
async getVolume(serie, isFusion = false ) {
let volumeId = null, volume = null
let key = isFusion ? `fusion_${serie.Id}` : serie.Id
if(!this.volumeData[key] || !cache.getVolume(this.volumeData[key].volumeId)) {
await this.$refs[`viewport-fusion-0`][0].createImageIdsAndCacheMetaData(serie)
volumeId = `${isFusion ? 'fusion' : serie.Modality}Volume` + ':' + csUtils.uuidv4()
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
volume.load()
this.volumeData[key] = {}
this.volumeData[key].volumeId = volumeId
this.volumeData[key].volume = volume
} else {
volumeId = this.volumeData[key].volumeId
volume = this.volumeData[key].volume
}
return { volumeId, volume }
},
openFusion() {
this.fusionVisible = true
},
closeFusion() {
this.fusionVisible = false
},
}
}
</script>

View File

@ -3,8 +3,7 @@
<el-tabs v-model="activeName">
<!-- 阅片 -->
<el-tab-pane v-if="taskInfo" :label="$t('trials:reading:tabTitle:review')" name="read">
<read-page v-if="!readingTool" ref="readPage" />
<PetCtReadPage v-else-if="readingTool === 2" ref="readPage" />
<read-page ref="readPage" :reading-tool="readingTool" />
</el-tab-pane>
<!-- 报告 -->
<el-tab-pane

View File

@ -0,0 +1,325 @@
<template>
<div class="colorMap">
<div class="item" effect="dark" :title="$t('trials:lugano:button:colormap')">
<div class="colorBar"
style="display:flex;justify-content: flex-start;align-items: center;position: relative;"
@mouseleave="isSlideMoving = false">
<div class="tool-wrapper" style="margin-right:0px" @click.stop="showColorBarPanel($event)"
@mouseleave="handleColorBarMouseout">
<div>
<div class="dropdown">
<div id="colorBar" class="icon" style="display: flex;align-items: center;width:266px">
<canvas id="colorBarCanvas" />
</div>
<!-- 伪彩 -->
<div class="text">{{ $t('trials:lugano:button:colormap') }}</div>
<div class="dropdown-content" style="width:266px">
<ul>
<li v-for="(colorMap, index) in colorMaps" :key="colorMap"
style="display: flex;align-items: center;margin-bottom:5px;padding:0 5px;justify-content: space-between;"
:class="{ activeLi: rgbPresetName === colorMap }"
@click="setColorMap(colorMap)">
<canvas :id="`colorBarCanvas${index}`" />
<span style="margin-left:5px;font-size: 10px;">{{ colorMap
}}</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<div style="margin-left:-1px;border: 1px solid #424242;">
<el-input v-model="range" size="mini" style="width:120px" maxlength="3"
oninput="if(value){value=value.replace(/[^\d]/g,'')} if(value<=0){value=''}"
@change="upperRangeChange">
<template slot="append">g/ml</template>
</el-input>
</div>
<div id="slider" style="position: absolute;left: 6px;top:5px;cursor: pointer;">
<div id="sliderBox" class="slider" style="height: 17px;width: 10px;background-color: #909399;" />
<div id="slider-position" style="color:#ddd;font-size: 12px;">0</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {
utilities as csUtils,
} from '@cornerstonejs/core'
import colormaps from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/colormaps'
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
const { registerColormap, getColormapNames, getColormap } = csUtils.colormap
export default {
name: "colorMap",
data() {
return {
colorMaps: [],
rgbPresetName: 'siemens',
range: 40,
upper: 6,
}
},
mounted() {
colormaps.forEach(colormap => {
registerColormap(colormap)
})
this.colorMaps = getColormapNames()
this.colorMaps.unshift('hsv')
this.$nextTick(() => {
this.renderColorMaps()
this.upperRangeChange(this.range)
this.initSlider()
})
},
methods: {
renderColorMaps() {
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
this.colorMaps.forEach((e, index) => {
this.createColorBar(e, `colorBarCanvas${index}`, 180, 15)
})
},
voiChange(v) {
this.$emit('voiChange', v)
},
initSlider() {
var slider = document.getElementById('slider')
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
slider.addEventListener('mousedown', () => {
this.isSlideMoving = true
})
document.addEventListener('mousemove', (e) => {
if (this.isSlideMoving) {
var containerWidth = container.clientWidth
var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth
var left = e.clientX - container.getBoundingClientRect().left
var position = null
position = left
if (left < 0) {
left = 6
position = 0
} else if (left > maxLeft) {
left = maxLeft + 6
position = maxLeft
}
slider.style.left = left + 'px'
var positionValue = document.getElementById('slider-position')
var upper = this.range
position = parseInt((position / maxLeft) * upper)
positionValue.textContent = position
this.upper = position
this.voiChange(position)
}
})
document.addEventListener('mouseup', () => {
this.isSlideMoving = false
})
},
upperRangeChange(v) {
if (v === 0 || v < this.upper) {
return
}
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
var containerWidth = container.clientWidth
var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth
var left = (this.upper / this.range) * maxLeft
if (left < 0) {
left = 6
} else if (left >= maxLeft) {
left = maxLeft + 6
}
var slider = document.getElementById('slider')
slider.style.left = left + 'px'
var positionValue = document.getElementById('slider-position')
positionValue.textContent = this.upper
},
createColorBar(rgbPresetName, elId, width, height) {
var colorMap = null
if (rgbPresetName === 'hsv') {
colorMap = vtkColorMaps.getPresetByName(rgbPresetName)
} else {
colorMap = getColormap(rgbPresetName)
}
const rgbPoints = colorMap.RGBPoints
const canvas = document.getElementById(elId)
const ctx = canvas.getContext('2d')
const canvasWidth = width
const canvasHeight = height
const rectWidth = width
const rectHeight = canvasHeight
canvas.width = canvasWidth
canvas.height = canvasHeight
const gradient = ctx.createLinearGradient(0, 0, rectWidth, 0)
for (let i = 0; i < rgbPoints.length; i += 4) {
let position = 0
if (rgbPoints[0] === -1) {
position = (rgbPoints[i] + 1) / 2
} else {
position = rgbPoints[i]
}
const color = `rgb(${parseInt(rgbPoints[i + 1] * 255)}, ${parseInt(rgbPoints[i + 2] * 255)}, ${parseInt(rgbPoints[i + 3] * 255)})`
gradient.addColorStop(position, color)
}
ctx.fillStyle = gradient
ctx.fillRect(0, 0, rectWidth, rectHeight)
},
async setColorMap(rgbPresetName) {
this.rgbPresetName = rgbPresetName
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
this.$emit("setColorMap", rgbPresetName)
},
showColorBarPanel(e) {
e.currentTarget.firstChild.firstChild.lastChild.style.display = 'block'
},
handleColorBarMouseout(e) {
e.currentTarget.firstChild.firstChild.lastChild.style.display = 'none'
},
init() {
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
this.$emit("setColorMap", this.rgbPresetName)
}
}
}
</script>
<style lang="scss" scoped>
.activeLi {
background-color: #919295;
}
.tool-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-right: 30px;
.icon {
padding: 5px;
border: 1px solid #404040;
cursor: pointer;
text-align: center;
.svg-icon {
font-size: 25px;
color: #ddd;
}
}
.text {
position: relative;
font-size: 12px;
margin-top: 5px;
color: #d0d0d0;
display: none;
}
}
.dropdown {
position: relative;
display: inline-block;
.icon-content {
display: flex;
align-items: center;
border: 1px solid #404040;
}
.text {
text-align: center;
}
.tool-icon {
padding: 5px;
cursor: pointer;
text-align: center;
.svg-icon {
font-size: 20px;
color: #ddd;
}
}
.arrow-icon {
cursor: pointer;
padding: 7px 2px 7px 0px;
}
.arrow-icon:hover {
background-color: #607d8b;
}
.icon-content-d:hover {
background-color: #607d8b;
}
.tool-icon-d {
padding: 5px;
.svg-icon {
font-size: 20px;
color: #ddd;
}
}
}
.dropdown-content {
display: none;
position: absolute;
background-color: #383838;
color: #fff;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 9999;
font-size: 12px;
ul {
list-style: none;
margin: 0;
padding: 0;
text-align: center;
li {
a {
display: block;
padding: 5px;
}
}
}
ul li:hover {
background-color: #727272;
cursor: pointer;
}
}
.item {
box-sizing: border-box;
position: relative;
border: 1px solid rgba(255, 255, 255, 0.21);
position: relative;
&_active {
// border: 2px solid #ffeb3b;fff
border: 1px dashed rgb(250, 250, 0);
}
}
.colorBar {
::v-deep .el-input--mini .el-input__inner {
height: 25px;
line-height: 25px;
border: none;
background-color: transparent;
color: #ddd;
}
}
</style>