irc_web/src/views/trials/trials-panel/reading/visit-review/components/FileViewer.vue

1106 lines
41 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="none-dicom-viewer">
<!-- tools -->
<div class="tools-wrapper">
<!-- 布局 -->
<div class="tool-item" :title="$t('trials:reading:button:layout')">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<svg-icon icon-class="layout" class="svg-icon" /><i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="1*1">1*1</el-dropdown-item>
<el-dropdown-item command="1*2">1*2</el-dropdown-item>
<el-dropdown-item command="2*2">2*2</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<!-- 缩放 -->
<div
:class="['tool-item', activeTool === 'Zoom' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:zoom')"
@click.prevent="setToolActive('Zoom')"
>
<svg-icon icon-class="magnifier" class="svg-icon" />
</div>
<!-- 移动 -->
<div
:class="['tool-item', activeTool === 'Pan' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:move')"
@click.prevent="setToolActive('Pan')"
>
<svg-icon icon-class="move" class="svg-icon" />
</div>
<!-- 旋转 -->
<div
:class="['tool-item', activeTool === 'PlanarRotate' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:rotate')"
@click.prevent="setToolActive('PlanarRotate')"
>
<svg-icon icon-class="rotate" class="svg-icon" />
</div>
<!-- 箭头工具 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'ArrowAnnotate' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:arrowAnnotate')"
@click.prevent="setAnnotateToolActive('ArrowAnnotate')"
>
<svg-icon icon-class="arrow" class="svg-icon" />
</div>
<!-- 矩形工具 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'RectangleROI' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:RectangleRoi')"
@click.prevent="setAnnotateToolActive('RectangleROI')"
>
<svg-icon icon-class="rectangle" class="svg-icon" />
</div>
<!-- 自由曲线 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'PlanarFreehandROI' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:planarFreehandROI')"
@click.prevent="setAnnotateToolActive('PlanarFreehandROI')"
>
<svg-icon icon-class="polygon" class="svg-icon" />
</div>
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'Eraser' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:Eraser')"
@click.prevent="setAnnotateToolActive('Eraser')"
>
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<!-- <div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'Length' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:length')"
@click.prevent="setAnnotateToolActive('Length')"
>
<svg-icon icon-class="length" class="svg-icon" />
</div> -->
<!-- 截图 -->
<!-- <div
class="tool-item"
:title="$t('trials:reading:button:screenShot')"
@click.prevent="saveImage"
>
<svg-icon icon-class="image" class="svg-icon" />
</div> -->
<!-- 重置 -->
<div
class="tool-item"
:title="$t('trials:reading:button:reset')"
@click.prevent="resetViewport"
>
<svg-icon icon-class="refresh" class="svg-icon" />
</div>
</div>
<!-- viewports -->
<div class="viewports-wrapper">
<div class="grid-container" :style="gridStyle">
<div
v-for="(v, index) in viewportInfos"
v-show="index < cells.length"
:key="index"
:style="cellStyle"
:class="['grid-cell', index === activeCanvasIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
@dblclick="toggleFullScreen($event, index)"
@click="activeCanvas(index)"
@mouseup="sliderMouseup($event, index)"
@mousemove="sliderMousemove($event, index)"
@mouseleave="sliderMouseleave($event, index)"
>
<div v-show="imageType.includes(v.fileType)" :ref="`canvas-${index}`" class="content">
<div class="left-top-text">
<div
v-if="v.taskInfo.IsExistsClinicalData"
class="cd-info"
:title="$t('trials:reading:button:clinicalData')"
>
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon" @click.stop="viewCD(v.taskInfo.VisitTaskId)" />
</div>
<h2
v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo && v.taskInfo"
class="subject-info"
>
{{ `${taskInfo.SubjectCode} ${v.taskInfo.TaskBlindName} ` }}
</h2>
<!-- <div v-if="v.currentFileName">{{ v.currentFileName }}</div> -->
</div>
<div
v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder === 1 && v.taskInfo"
class="top-center-tool"
>
<div class="toggle-visit-container">
<div
class="arrw_icon"
:style="{ cursor: v.taskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: v.taskInfo.VisitTaskNum !== 0 ? '#fff': '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, v.taskInfo.VisitTaskNum, -1, v.index)"
@dblclick.stop="preventDefault($event)"
>
<i class="el-icon-caret-left" />
</div>
<div class="arrow_text">
{{ v.taskInfo.TaskBlindName }}
</div>
<div
class="arrw_icon"
:style="{ cursor: v.taskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: v.taskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff': '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, v.taskInfo.VisitTaskNum, 1, v.index)"
@dblclick.stop="preventDefault($event)"
>
<i class="el-icon-caret-right" />
</div>
</div>
</div>
<div :ref="`sliderBox-${index}`" class="right-slider-box" @click.stop="clickSlider($event, index)">
<div :style="{top: v.height + '%'}" class="slider" @click.stop.prevent="() => {return}" @mousedown.stop="sliderMousedown($event, index)"/>
</div>
</div>
<div v-show="v.fileType === 'application/pdf' && fullScreenIndex === null " class="content flex_col">
<div class="content-top" style="height: 50px;">
<div class="left-top-text">
<div
v-if="v.taskInfo.IsExistsClinicalData"
class="cd-info"
:title="$t('trials:reading:button:clinicalData')"
>
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon" @click.stop="viewCD(v.taskInfo.VisitTaskId)" />
</div>
<h2
v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo && v.taskInfo"
class="subject-info"
>
{{ `${taskInfo.SubjectCode} ${v.taskInfo.TaskBlindName} ` }}
</h2>
<!-- <div v-if="v.currentFileName">{{ v.currentFileName }}</div> -->
</div>
<div
v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder === 1 && v.taskInfo"
class="top-center-tool"
>
<div class="toggle-visit-container">
<div
class="arrw_icon"
:style="{ cursor: v.taskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: v.taskInfo.VisitTaskNum !== 0 ? '#fff': '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, v.taskInfo.VisitTaskNum, -1, v.index)"
@dblclick.stop="preventDefault($event)"
>
<i class="el-icon-caret-left" />
</div>
<div class="arrow_text">
{{ v.taskInfo.TaskBlindName }}
</div>
<div
class="arrw_icon"
:style="{ cursor: v.taskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: v.taskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff': '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, v.taskInfo.VisitTaskNum, 1, v.index)"
@dblclick.stop="preventDefault($event)"
>
<i class="el-icon-caret-right" />
</div>
</div>
</div>
</div>
<div class="content-main" style="flex: 1;">
<iframe v-if="v.currentFilePath" :ref="`iframe-${index}`" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${v.currentFilePath}?index=${index}`" width="100%" height="100%" frameborder="0" crossorigin="anonymous" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { addNoneDicomMark, deleteTrialFileType } from '@/api/trials'
import {
RenderingEngine,
Enums,
imageLoader,
metaData,
getRenderingEngine,
eventTarget,
utilities as csUtils
// getEnabledElementByIds
} from '@cornerstonejs/core'
import * as cornerstoneTools from '@cornerstonejs/tools'
import initLibraries from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/initLibraries'
import hardcodedMetaDataProvider from './../js/hardcodedMetaDataProvider'
import registerWebImageLoader from './../js/registerWebImageLoader'
import { mapGetters } from 'vuex'
import store from '@/store'
const { ViewportType } = Enums
const renderingEngineId = 'myRenderingEngine'
const {
ToolGroupManager,
Enums: csToolsEnums,
StackScrollTool,
PanTool,
ZoomTool,
PlanarRotateTool,
ArrowAnnotateTool,
RectangleROITool,
PlanarFreehandROITool,
EraserTool,
LengthTool
// cursors
} = cornerstoneTools
const { MouseBindings, Events: toolsEvents } = csToolsEnums
export default {
name: 'ImageViewer',
props: {
relatedStudyInfo: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
rows: 1,
cols: 1,
fullScreenIndex: null,
imageIds: [],
activeCanvasIndex: 0,
activeCanvasFileType: '',
layout: '1*2',
cellsMax: 4,
viewportInfos: [],
taskInfo: null,
activeTool: '',
readingTaskState: 2,
renderHistoryAnnotationTaskIds: [],
imageType: ['image/jpeg', 'image/jpg', 'image/bmp', 'image/png']
}
},
computed: {
gridStyle() {
return {
display: 'grid',
gridTemplateRows: `repeat(${this.rows}, 1fr)`,
gridTemplateColumns: `repeat(${this.cols}, 1fr)`,
height: '100%',
width: '100%'
}
},
cellStyle() {
return {
border: '1px dashed #ccc',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}
},
cells() {
return Array(this.rows * this.cols).fill(0)
},
...mapGetters(['lastViewportTaskId', 'currentTaskState'])
},
watch: {
relatedStudyInfo: {
immediate: true,
handler(obj) {
if (!obj || Object.keys(obj).length === 0) return
this.updateViewportInfos(0, obj)
}
},
currentTaskState: {
immediate: true,
handler(state) {
if (state === 2) {
this.readingTaskState = 2
// 设置标记锁定
const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
annotations.map(annotation => {
cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
})
this.setToolsPassive()
}
}
}
},
mounted() {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
this.readingTaskState = this.taskInfo.ReadingTaskState
if (this.taskInfo.VisitNum > 0 && this.taskInfo.IsReadingTaskViewInOrder !== 0) {
this.rows = 1
this.cols = 2
this.activeCanvasIndex = 1
}
this.viewportInfos = Array.from({ length: this.cellsMax }, (_, index) => ({
index: index,
taskInfo: '',
studyId: '',
currentImageIdIndex: 0,
viewportId: `canvas-${index}`,
fileType: '',
currentFileName: '',
currentFilePath: '',
imageIds: [],
oldB: null,
oldM: null,
isMove: false,
height: 0
}))
this.initLoader()
window.addEventListener('message', this.handleIframeMessage)
},
beforeDestroy() {
window.removeEventListener('message', this.handleIframeMessage)
},
methods: {
// 初始化加载器
async initLoader() {
registerWebImageLoader(imageLoader)
await initLibraries()
let renderingEngine = getRenderingEngine(renderingEngineId)
if (!renderingEngine) {
renderingEngine = new RenderingEngine(renderingEngineId)
}
const resizeObserver = new ResizeObserver(() => {
const renderingEngine = getRenderingEngine(renderingEngineId)
if (renderingEngine) {
renderingEngine.resize(true, false)
}
})
const element1 = this.$refs['canvas-0'][0]
const element2 = this.$refs['canvas-1'][0]
const element3 = this.$refs['canvas-2'][0]
const element4 = this.$refs['canvas-3'][0]
const elements = [
element1,
element2,
element3,
element4
]
elements.forEach((element, i) => {
element.oncontextmenu = (e) => e.preventDefault()
resizeObserver.observe(element)
element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
})
const viewportInputArray = [
{
viewportId: 'canvas-0',
type: ViewportType.STACK,
element: element1
},
{
viewportId: 'canvas-1',
type: ViewportType.STACK,
element: element2
},
{
viewportId: 'canvas-2',
type: ViewportType.STACK,
element: element3
},
{
viewportId: 'canvas-3',
type: ViewportType.STACK,
element: element4
}
]
const viewportIds = ['canvas-0', 'canvas-1', 'canvas-2', 'canvas-3']
renderingEngine.setViewports(viewportInputArray)
this.addAnnotationListeners()
cornerstoneTools.addTool(StackScrollTool)
cornerstoneTools.addTool(PanTool)
cornerstoneTools.addTool(ZoomTool)
cornerstoneTools.addTool(PlanarRotateTool)
cornerstoneTools.addTool(ArrowAnnotateTool)
cornerstoneTools.addTool(RectangleROITool)
cornerstoneTools.addTool(PlanarFreehandROITool)
cornerstoneTools.addTool(EraserTool)
cornerstoneTools.addTool(LengthTool)
viewportIds.forEach((viewportId, i) => {
const toolGroupId = `canvas-${i}`
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId)
toolGroup.addViewport(viewportId, renderingEngineId)
toolGroup.addTool(StackScrollTool.toolName)
toolGroup.addTool(PanTool.toolName)
toolGroup.addTool(ZoomTool.toolName)
toolGroup.addTool(PlanarRotateTool.toolName)
toolGroup.addTool(ArrowAnnotateTool.toolName, {
arrowHeadStyle: 'standard',
changeTextCallback: async(data, eventData, doneChangingTextCallback) => {
return doneChangingTextCallback(await this.customPrompt())
},
getTextCallback: async(doneChangingTextCallback) => {
return doneChangingTextCallback(await this.customPrompt())
}
})
toolGroup.addTool(RectangleROITool.toolName)
toolGroup.addTool(PlanarFreehandROITool.toolName, {
allowOpenContours: false,
cachedStats: false
})
toolGroup.addTool(EraserTool.toolName)
toolGroup.addTool(LengthTool.toolName, {
getTextLines: this.getLengthToolTextLines
})
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Wheel }]
})
toolGroup.setToolPassive(PanTool.toolName)
toolGroup.setToolPassive(ZoomTool.toolName)
toolGroup.setToolPassive(PlanarRotateTool.toolName)
if (this.readingTaskState < 2) {
toolGroup.setToolPassive(ArrowAnnotateTool.toolName)
toolGroup.setToolPassive(RectangleROITool.toolName)
toolGroup.setToolPassive(PlanarFreehandROITool.toolName)
toolGroup.setToolPassive(LengthTool.toolName)
} else {
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
toolGroup.setToolEnabled(RectangleROITool.toolName)
toolGroup.setToolEnabled(PlanarFreehandROITool.toolName)
toolGroup.setToolEnabled(LengthTool.toolName)
}
toolGroup.setToolPassive(EraserTool.toolName)
})
},
// 加载图片回调
stackNewImage(e) {
const { detail } = e
const i = this.viewportInfos.findIndex(i => i.viewportId === detail.viewportId)
if (i === -1) return
this.viewportInfos[i].currentImageIdIndex = detail.imageIdIndex
const obj = this.viewportInfos[i].fileList[detail.imageIdIndex]
if (!obj) return
this.viewportInfos[i].currentFileName = obj.FileName
this.viewportInfos[i].height = (this.viewportInfos[i].currentImageIdIndex) * 100 / (this.viewportInfos[i].imageIds.length - 1)
const path = detail.imageId.split(`web:${this.OSSclientConfig.basePath}`)[1]
this.$emit('toggleImage', { taskId: this.viewportInfos[i].taskInfo.VisitTaskId, studyId: this.viewportInfos[i].studyId, path: path })
this.renderHistoryAnnotations(this.viewportInfos[i].taskInfo)
},
renderHistoryAnnotations(obj) {
if (this.renderHistoryAnnotationTaskIds.includes(obj.VisitTaskId)) return
this.renderHistoryAnnotationTaskIds.push(obj.VisitTaskId)
const arr = []
obj.Annotations.map(i => {
if (typeof i.MeasureData === 'string') {
const annotation = JSON.parse(i.MeasureData)
annotation.annotationId = i.Id
arr.push(annotation)
cornerstoneTools.annotation.state.addAnnotation(annotation)
if (obj.ReadingTaskState === 2) {
cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
}
}
})
},
// 渲染图片
async renderImage(imageIds, canvasIndex, sliceIndex) {
metaData.addProvider((type, imageId) => hardcodedMetaDataProvider(type, imageId, imageIds), 10000)
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${canvasIndex}`)
await viewport.setStack(imageIds)
viewport.setImageIdIndex(sliceIndex)
viewport.render()
// this.updateViewportInfos()
},
setActiveCanvasImages(obj) {
console.log('setActiveCanvasImages')
if (!obj || Object.keys(obj).length === 0) return
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
if (i === -1) return
if (obj.studyId === this.viewportInfos[i].studyId && this.imageType.includes(this.viewportInfos[i].fileType) && this.imageType.includes(obj.fileInfo.FileType)) {
const fileIndex = this.viewportInfos[i].fileList.findIndex(i => i.Path === obj.fileInfo.Path)
this.sliceIndex(fileIndex)
} else {
if (!this.imageType.includes(obj.fileInfo.FileType)) {
this.fullScreenIndex = null
}
this.updateViewportInfos(this.activeCanvasIndex, obj)
}
if (this.activeCanvasIndex === this.cells.length - 1) {
store.dispatch('noneDicomReview/setLastViewportTaskId', obj.visitTaskInfo.VisitTaskId)
}
},
// 激活视图
activeCanvas(index) {
if (this.activeCanvasIndex === index) return
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1) return
this.activeCanvasIndex = index
if (index === this.cells.length - 1) {
store.dispatch('noneDicomReview/setLastViewportTaskId', this.viewportInfos[i].taskInfo.VisitTaskId)
}
this.$emit('toggleTask', this.viewportInfos[i].taskInfo)
this.setToolsPassive()
},
// 更新视图信息
updateViewportInfos(index, obj) {
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1) return
this.viewportInfos[i].taskInfo = obj.visitTaskInfo
this.viewportInfos[i].currentFileName = obj.fileInfo.FileName
this.viewportInfos[i].studyId = obj.studyId
this.viewportInfos[i].fileType = obj.fileInfo.FileType
if (this.imageType.includes(obj.fileInfo.FileType)) {
const arr = obj.fileList.filter(i => this.imageType.includes(i.FileType))
this.viewportInfos[i].fileList = arr
const fileIndex = this.viewportInfos[i].fileList.findIndex(i => i.Path === obj.fileInfo.Path)
this.viewportInfos[i].currentImageIdIndex = fileIndex
const imageIds = []
for (let i = 0; i < arr.length; i++) {
const path = arr[i].Path
imageIds.push(`web:${this.OSSclientConfig.basePath}${path}`)
}
this.viewportInfos[i].imageIds = imageIds
if (imageIds.length > 0) {
this.renderImage(imageIds, index, fileIndex)
}
this.$nextTick(() => {
const renderingEngine = getRenderingEngine(renderingEngineId)
if (renderingEngine) {
renderingEngine.resize(true, false)
}
})
} else {
this.viewportInfos[i].currentFilePath = obj.fileInfo.Path
}
},
// 切换图片
sliceIndex(index) {
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
if (i === -1) return
if (index < 0 || index >= this.viewportInfos[i].imageIds.length) return
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(
this.viewportInfos[i].viewportId
)
viewport.setImageIdIndex(index)
viewport.render()
},
// 更改视图布局
handleCommand(command) {
this.fullScreenIndex = null
this.layout = command
this.rows = parseInt(command.split('*')[0])
this.cols = parseInt(command.split('*')[1])
if (this.rows === 1 && this.cols === 1) {
this.$nextTick(()=>{
this.activeCanvas(0)
})
}
// 有序阅片 1*1 显示当前图片 1*2 显示基线*当前图片 2*2 显示当前图片
// const obj = this.viewportInfos.find(i => i.index === this.activeCanvasIndex)
// if (obj && this.rows === 1 && this.cols === 1) {
// this.viewportInfos = this.viewportInfos.map((v, i) => {
// if (i === 0) {
// v.taskInfo = obj.taskInfo
// v.currentImageIdIndex === obj.currentImageIdIndex
// v.currentFileName === obj.currentFileName
// v.imageIds === obj.imageIds
// } else {
// v.taskInfo = ''
// v.currentImageIdIndex === 0
// v.currentFileName === ''
// v.imageIds === []
// }
// return v
// })
// this.activeCanvasIndex = 0
// } else if (obj && this.rows === 1 && this.cols === 2 && this.taskInfo.IsReadingTaskViewInOrder === 1) {
// this.viewportInfos = this.viewportInfos.map((v, i) => {
// if (i === 0) {
// v.taskInfo = this.relatedStudyInfo.visitTaskInfo
// v.currentImageIdIndex === this.relatedStudyInfo.fileIndex
// v.currentFileName === this.relatedStudyInfo.fileInfo.FileName
// const imageIds = []
// for (let i = 0; i < this.relatedStudyInfo.fileList.length; i++) {
// const path = this.relatedStudyInfo.fileList[i].Path
// imageIds.push(`web:${this.OSSclientConfig.basePath}${path}`)
// }
// v.imageIds === imageIds
// } else if (i === 1) {
// v.taskInfo = obj.taskInfo
// v.currentImageIdIndex === obj.currentImageIdIndex
// v.currentFileName === obj.currentFileName
// v.imageIds === obj.imageIds
// } else {
// v.taskInfo = ''
// v.currentImageIdIndex === 0
// v.currentFileName === ''
// v.imageIds === []
// }
// return v
// })
// this.activeCanvasIndex = 1
// } else if (obj && this.rows === 1 && this.cols === 2 && this.taskInfo.IsReadingTaskViewInOrder !== 1) {
// this.viewportInfos = this.viewportInfos.map((v, i) => {
// if (i === 0 || i === 1) {
// v.taskInfo = obj.taskInfo
// v.currentImageIdIndex === obj.currentImageIdIndex
// v.currentFileName === obj.currentFileName
// v.imageIds === obj.imageIds
// } else {
// v.taskInfo = ''
// v.currentImageIdIndex === 0
// v.currentFileName === ''
// v.imageIds === []
// }
// return v
// })
// this.activeCanvasIndex = 1
// } else if (obj && (this.rows === 2 && this.cols === 2)) {
// this.viewportInfos = this.viewportInfos.map(v => {
// v.taskInfo = obj.taskInfo
// v.currentImageIdIndex === obj.currentImageIdIndex
// v.currentFileName === obj.currentFileName
// v.imageIds === obj.imageIds
// return v
// })
// this.activeCanvasIndex = 3
// }
// this.$nextTick(() => {
// this.viewportInfos.forEach(v => {
// if (v.imageIds.length > 0) {
// this.renderImage(v.imageIds, v.index, v.currentImageIdIndex)
// }
// })
// })
},
// 切换全屏
toggleFullScreen(e, index) {
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1) return
if (!this.viewportInfos[i].currentFileName) return
if (this.imageType.includes(this.viewportInfos[i].fileType)) {
this.fullScreenIndex = this.fullScreenIndex === index ? null : index
this.activeCanvasIndex = index
}
},
// 切换任务
toggleTask(evt, visitTaskNum, i, index) {
this.activeCanvas(index)
const num = visitTaskNum + i
if (num >= 0 && num <= this.taskInfo.VisitNum) {
this.$emit('toggleTaskByViewer', num)
}
evt.stopImmediatePropagation()
evt.stopPropagation()
evt.preventDefault()
},
// 激活工具
setToolActive(toolName) {
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
if (i === -1) return
const toolGroupId = `canvas-${this.viewportInfos[i].index}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (this.activeTool === toolName) {
toolGroup.setToolPassive(this.activeTool)
this.activeTool = ''
} else {
if (this.activeTool) {
toolGroup.setToolPassive(this.activeTool)
}
toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
})
this.activeTool = toolName
}
},
// 激活标注工具
setAnnotateToolActive(toolName) {
if (this.readingTaskState === 2) return
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
if (i === -1) return
if (this.viewportInfos[i].taskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const toolGroupId = `canvas-${this.viewportInfos[i].index}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (this.activeTool === toolName) {
toolGroup.setToolPassive(this.activeTool)
this.activeTool = ''
} else {
if (this.activeTool) {
toolGroup.setToolPassive(this.activeTool)
}
toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
})
this.activeTool = toolName
}
}
},
setToolsPassive() {
if (!this.activeTool) return
const toolGroupIds = ['canvas-0', 'canvas-1', 'canvas-2', 'canvas-3']
toolGroupIds.forEach(toolGroupId => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolPassive(this.activeTool)
})
this.activeTool = ''
// const enabledElement = getEnabledElementByIds(
// `canvas-${this.activeCanvasIndex}`,
// renderingEngineId
// )
// cursors.setCursorForElement(enabledElement.viewport.element, 'default')
},
addAnnotationListeners() {
const debouncedCallback = this.debounce((evt) => {
this.annotationModifiedListener(evt)
}, 100)
eventTarget.addEventListener(
toolsEvents.ANNOTATION_MODIFIED,
(evt) => {
debouncedCallback(evt)
}
)
eventTarget.addEventListener(
toolsEvents.ANNOTATION_COMPLETED,
this.annotationCompletedListener
)
eventTarget.addEventListener(
toolsEvents.ANNOTATION_REMOVED,
this.annotationRemovedListener
)
},
async annotationRemovedListener(e) {
if (this.readingTaskState === 2) return
const { annotation } = e.detail
if (!annotation) return
if (annotation.annotationId) {
await deleteTrialFileType(annotation.annotationId)
}
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
// 删除成功
},
async annotationModifiedListener(e) {
console.log('Modified')
if (this.readingTaskState === 2) return
const { annotation } = e.detail
if (!annotation) return
if (!annotation.annotationId) return
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
if (i === -1) return
if (this.viewportInfos[i].taskInfo.VisitTaskId !== this.taskInfo.VisitTaskId) return
const imageId = annotation.metadata.referencedImageId
const path = imageId.split(`web:${this.OSSclientConfig.basePath}`)[1]
const fileList = this.viewportInfos[i].fileList
const fileIndex = fileList.findIndex(f => f.Path === path)
if (fileIndex === -1) return
const params = {
id: annotation.annotationId,
visitTaskId: this.viewportInfos[i].taskInfo.VisitTaskId,
studyId: this.viewportInfos[i].studyId,
noneDicomFileId: fileList[fileIndex].Id,
path: fileList[fileIndex].Path,
measureData: JSON.stringify(annotation)
}
await addNoneDicomMark(params)
// 保存成功
// this.$message.success(this.$t('common:message:savedSuccessfully'))
},
async annotationCompletedListener(e) {
console.log('Completed')
if (this.readingTaskState === 2) return
const { annotation } = e.detail
if (!annotation) return
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
if (i === -1) return
if (this.viewportInfos[i].taskInfo.VisitTaskId !== this.taskInfo.VisitTaskId) return
const imageId = annotation.metadata.referencedImageId
const path = imageId.split(`web:${this.OSSclientConfig.basePath}`)[1]
const fileList = this.viewportInfos[i].fileList
const fileIndex = fileList.findIndex(f => f.Path === path)
if (fileIndex === -1) return
if (annotation.metadata.toolName === 'Length') {
const { value } = await this.$prompt('请录入录入物理长度mm')
annotation.data.handles.scale = value
if (!value) {
// 移除标记
cornerstoneTools.annotation.state.addAnnotation(annotation.annotationUID)
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
}
}
const params = {
id: '',
visitTaskId: this.viewportInfos[i].taskInfo.VisitTaskId,
studyId: this.viewportInfos[i].studyId,
noneDicomFileId: fileList[fileIndex].Id,
path: fileList[fileIndex].Path,
measureData: JSON.stringify(annotation)
}
const res = await addNoneDicomMark(params)
annotation.annotationId = res.Result
// 保存成功
// this.$message.success(this.$t('common:message:savedSuccessfully'))
},
getLengthToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const { length, unit } = cachedVolumeStats
if (length === undefined || length === null || isNaN(length)) {
return
}
if (data.handles.scale === undefined || data.handles.scale === null || isNaN(data.handles.scale)) {
return
}
const textLines = []
textLines.push(`${csUtils.roundNumber(length)} ${unit}`)
textLines.push(`${csUtils.roundNumber(data.handles.scale)} mm`)
return textLines;
},
debounce(callback, delay) {
let timerId
return function() {
clearTimeout(timerId)
timerId = setTimeout(() => {
callback.apply(this, arguments)
}, delay)
}
},
// 重置视图
resetViewport() {
this.setToolsPassive()
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportId = `canvas-${this.activeCanvasIndex}`
const viewport = (renderingEngine.getViewport(viewportId))
viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
renderingEngine.render()
},
// 箭头工具输入标记名称自定义弹窗
async customPrompt() {
try {
// 请输入标记名称
const { value } = await this.$prompt(this.$t('trials:noneDicom:message:msg1'))
return value
} catch {
return null
}
},
// 滚动条
clickSlider(e, index) {
const height = e.offsetY * 100 / this.$refs[`sliderBox-${index}`][0].clientHeight
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1) return
this.viewportInfos[i].height = height
let sliceIdx = Math.trunc(this.viewportInfos[i].imageIds.length * height / 100)
sliceIdx = sliceIdx >= this.viewportInfos[i].imageIds.length ? this.viewportInfos[i].imageIds.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(
this.viewportInfos[i].viewportId
)
viewport.setImageIdIndex(sliceIdx)
viewport.render()
// this.$emit('toggleImage', { taskId: this.viewportInfos[i].taskInfo.VisitTaskId, studyId: this.viewportInfos[i].studyId, imageIndex: sliceIdx })
},
sliderMouseup(e, index) {
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1 && this.imageType.includes(this.viewportInfos[i].fileType)) return
this.viewportInfos[i].isMove = false
},
sliderMousemove(e, index) {
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1 && this.imageType.includes(this.viewportInfos[i].fileType)) return
if (!this.viewportInfos[i].isMove) return
const delta = this.viewportInfos[i].oldB - (this.viewportInfos[i].oldM - e.clientY)
const boxHeight = this.$refs[`sliderBox-${index}`][0].clientHeight
if (delta < 0) return
if (delta > boxHeight) return
const height = delta * 100 / boxHeight
let sliceIdx = Math.trunc(this.viewportInfos[i].imageIds.length * height / 100)
sliceIdx = sliceIdx >= this.viewportInfos[i].imageIds.length ? this.viewportInfos[i].imageIds.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
this.viewportInfos[i].height = height
// if (this.viewportInfos[i].currentImageIdIndex !== i) {
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(
this.viewportInfos[i].viewportId
)
viewport.setImageIdIndex(sliceIdx)
viewport.render()
// }
// this.$emit('toggleImage', { taskId: this.viewportInfos[i].taskInfo.VisitTaskId, studyId: this.viewportInfos[i].studyId, imageIndex: sliceIdx })
},
sliderMouseleave(e, index) {
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1 && this.imageType.includes(this.viewportInfos[i].fileType)) return
if (!this.viewportInfos[i].isMove) return
this.viewportInfos[i].isMove = false
},
sliderMousedown(e, index) {
const i = this.viewportInfos.findIndex(i => i.index === index)
if (i === -1) return
const boxHeight = this.$refs[`sliderBox-${index}`][0].clientHeight
this.viewportInfos[i].oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
this.viewportInfos[i].oldM = e.clientY
this.viewportInfos[i].isMove = true
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
},
handleIframeMessage(event) {
if (event.data.type === 'pdf-clicked') {
const baseUrl = event.data.data.baseUrl
const urlParams = new URL(baseUrl)
const index = parseInt(urlParams.search.split('=')[1])
this.activeCanvas(index)
}
},
preventDefault(e) {
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
},
// 查看临床数据
viewCD(id) {
this.$emit('previewCD', id)
}
}
}
</script>
<style lang="scss" scoped>
.none-dicom-viewer {
display: flex;
flex-direction: column;
width:100%;
height: 100%;
user-select: none;
.tools-wrapper {
height: 50px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
border-bottom: 1px solid #727272;
color: #ddd;
.tool-item {
padding: 5px;
margin: 0 5px;
border: 1px solid #333;
font-size: 20px;
cursor: pointer;
}
.tool-item-active {
background-color: #607d8b;
}
.tool-disabled {
cursor: not-allowed;
}
}
.viewports-wrapper {
flex: 1;
.grid-container {
display: grid;
height: 100%;
width: 100%;
position: relative;
}
.grid-cell {
border: 1px dashed #ccc;;
display: flex;
align-items: center;
justify-content: center;
}
.cell_active {
border-color: #fafa00!important;
}
.cell-full-screen {
grid-column: 1 / -1;
grid-row: 1 / -1;
}
.flex_col {
display: flex;
flex-direction: column;
}
.content {
width: 100%;
height: 100%;
position: relative;
.left-top-text {
position: absolute;
left: 5px;
top: 5px;
color: #ddd;
z-index: 1;
font-size: 12px;
.cd-info {
color: #ddd;
font-size: 18px;
}
.subject-info {
color:#f44336;
padding: 5px 0px;
margin: 0;
}
}
.top-center-tool {
position: absolute;
left:50%;
top: 5px;
transform: translateX(-50%);
z-index: 1;
.toggle-visit-container {
display: flex;
}
.arrw_icon{
width: 20px;
height: 20px;
background-color: #3f3f3f;
text-align: center;
line-height: 20px;
border-radius: 10%;
}
.arrow_text{
height: 20px;
line-height: 20px;
background-color: #00000057;
color: #fff;
padding:0 10px;
font-size: 14px;
}
}
.right-slider-box {
position: absolute;
right: 1px;
height: calc(100% - 140px);
transform: translateY(-50%);
top: calc(50% - 30px);
width: 10px;
background: #333;
z-index: 1;
cursor: pointer;
}
.right-slider-box:after{
content: '';
position: absolute;
bottom: -20px;
left: 0;
height: 20px;
width: 100%;
background: #333;
}
.slider {
height: 20px;
width: 100%;
position: absolute;
top: 0;
z-index:10;
background: #9e9e9e;
cursor: move
}
}
}
}
</style>