509 lines
16 KiB
Vue
509 lines
16 KiB
Vue
<template>
|
|
<div class="none-dicom-viewer">
|
|
<!-- tools -->
|
|
<div class="tools-wrapper">
|
|
<el-dropdown @command="handleCommand">
|
|
<span class="el-dropdown-link">
|
|
<i class="el-icon-menu" /><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>
|
|
<!-- 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':'']"
|
|
@click="activeCanvas(index)"
|
|
@dblclick="toggleFullScreen(index)"
|
|
>
|
|
<div :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')"
|
|
@click.stop="viewCD($event)"
|
|
>
|
|
<svg-icon icon-class="documentation" class="svg-icon" />
|
|
</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)"
|
|
@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)"
|
|
@dblclick.stop="preventDefault($event)"
|
|
>
|
|
<i class="el-icon-caret-right" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import {
|
|
RenderingEngine,
|
|
Enums,
|
|
imageLoader,
|
|
metaData,
|
|
getRenderingEngine
|
|
} 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 } = cornerstoneTools
|
|
const { MouseBindings } = csToolsEnums
|
|
export default {
|
|
name: 'ImageViewer',
|
|
props: {
|
|
relatedStudyInfo: {
|
|
type: Object,
|
|
default() {
|
|
return {}
|
|
}
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
rows: 1,
|
|
cols: 1,
|
|
fullScreenIndex: null,
|
|
imageIds: [],
|
|
activeCanvasIndex: 0,
|
|
layout: '1*2',
|
|
cellsMax: 4,
|
|
viewportInfos: [],
|
|
taskInfo: null
|
|
}
|
|
},
|
|
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'])
|
|
},
|
|
watch: {
|
|
relatedStudyInfo: {
|
|
immediate: true,
|
|
handler(obj) {
|
|
if (!obj || Object.keys(obj).length === 0) return
|
|
this.updateViewportInfos(0, obj)
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
|
|
if (this.taskInfo.VisitNum > 0) {
|
|
this.rows = 1
|
|
this.cols = 2
|
|
this.activeCanvasIndex = 1
|
|
}
|
|
this.viewportInfos = Array.from({ length: this.cellsMax }, (_, index) => ({
|
|
index: index,
|
|
taskInfo: '',
|
|
currentImageIdIndex: 0,
|
|
viewportId: `canvas-${index}`,
|
|
currentFileName: '',
|
|
imageIds: []
|
|
}))
|
|
this.initLoader()
|
|
},
|
|
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)
|
|
cornerstoneTools.addTool(StackScrollTool)
|
|
viewportIds.forEach((viewportId, i) => {
|
|
const toolGroupId = `canvas-${i}`
|
|
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId)
|
|
toolGroup.addViewport(viewportId, renderingEngineId)
|
|
toolGroup.addTool(StackScrollTool.toolName)
|
|
toolGroup.setToolActive(StackScrollTool.toolName, {
|
|
bindings: [{ mouseButton: MouseBindings.Wheel }]
|
|
})
|
|
})
|
|
},
|
|
// 加载图片回调
|
|
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
|
|
},
|
|
// 渲染图片
|
|
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) {
|
|
if (!obj || Object.keys(obj).length === 0) return
|
|
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
|
|
if (i === -1) return
|
|
if (obj.visitTaskInfo.VisitTaskId === this.viewportInfos[i].taskInfo.VisitTaskId) {
|
|
this.sliceIndex(obj.fileIndex)
|
|
} else {
|
|
this.updateViewportInfos(this.activeCanvasIndex, obj)
|
|
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
|
|
store.dispatch('noneDicomReview/setLastViewportTaskId', this.viewportInfos[i].taskInfo.VisitTaskId)
|
|
this.activeCanvasIndex = index
|
|
// this.$emit('toggleTask', this.viewportInfos[i].taskInfo)
|
|
},
|
|
// 更新视图信息
|
|
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].currentImageIdIndex = obj.fileIndex
|
|
this.viewportInfos[i].currentFileName = obj.fileInfo.FileName
|
|
this.viewportInfos[i].fileList = obj.fileList
|
|
const imageIds = []
|
|
for (let i = 0; i < obj.fileList.length; i++) {
|
|
const path = obj.fileList[i].Path
|
|
imageIds.push(`web:${this.OSSclientConfig.basePath}${path}`)
|
|
}
|
|
this.viewportInfos[i].imageIds = imageIds
|
|
if (imageIds.length > 0) {
|
|
this.renderImage(imageIds, index, obj.fileIndex)
|
|
}
|
|
},
|
|
// 切换图片
|
|
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.layout = command
|
|
this.rows = parseInt(command.split('*')[0])
|
|
this.cols = parseInt(command.split('*')[1])
|
|
// 有序阅片 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(index) {
|
|
this.fullScreenIndex = this.fullScreenIndex === index ? null : index
|
|
},
|
|
// 切换任务
|
|
toggleTask(evt, visitTaskNum, i) {
|
|
const num = visitTaskNum + i
|
|
if (num >= 0 && num <= this.taskInfo.VisitNum) {
|
|
this.$emit('toggleTaskByViewer', num)
|
|
}
|
|
|
|
evt.stopImmediatePropagation()
|
|
evt.stopPropagation()
|
|
evt.preventDefault()
|
|
},
|
|
preventDefault(e) {
|
|
e.stopImmediatePropagation()
|
|
e.stopPropagation()
|
|
e.preventDefault()
|
|
},
|
|
// 查看临床数据
|
|
viewCD(e) {
|
|
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.none-dicom-viewer {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width:100%;
|
|
height: 100%;
|
|
user-select: none;
|
|
.tools-wrapper {
|
|
height: 60px;
|
|
border-bottom: 1px solid #727272;
|
|
}
|
|
.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;
|
|
}
|
|
.full-screen {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: white;
|
|
z-index: 1000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.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;
|
|
cursor: pointer;
|
|
}
|
|
.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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|