549 lines
16 KiB
Plaintext
549 lines
16 KiB
Plaintext
<template>
|
||
<div class="dicom-viewer-wrapper">
|
||
<div class="dicom-viewer-container">
|
||
<div class="dicom-tools">
|
||
|
||
<div class="tool-wrapper">
|
||
<div class="dropdown">
|
||
<div class="icon">
|
||
<svg-icon icon-class="layout" class="svg-icon" />
|
||
</div>
|
||
<div class="text">布局<i class="el-icon-caret-bottom" /></div>
|
||
<div class="dropdown-content">
|
||
<ul style="width:40px">
|
||
<li v-for="layout in layouts" :key="layout.index">
|
||
<a href="#" @click.prevent="changeLayout(layout)">{{ layout.row }}*{{ layout.col }}</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
:class="[activeTool==='Zoom'?'tool_active':'']"
|
||
data-tool="Zoom"
|
||
@click.prevent="setToolActive('Zoom',false)"
|
||
>
|
||
<svg-icon icon-class="magnifier" class="svg-icon" />
|
||
</div>
|
||
<div class="text">缩放</div>
|
||
</div>
|
||
|
||
<div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
:class="[activeTool==='Pan'?'tool_active':'']"
|
||
data-tool="Pan"
|
||
@click.prevent="setToolActive('Pan',false)"
|
||
>
|
||
<svg-icon icon-class="move" class="svg-icon" />
|
||
</div>
|
||
<div class="text">移动</div>
|
||
</div>
|
||
|
||
<div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
@click.prevent="fitToType('fitToWindow')"
|
||
>
|
||
<svg-icon icon-class="fitToWindow" class="svg-icon" />
|
||
</div>
|
||
<div class="text">适应窗口</div>
|
||
</div>
|
||
|
||
<div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
@click.prevent="fitToType('fitToImage')"
|
||
>
|
||
<svg-icon icon-class="fitToImage" class="svg-icon" />
|
||
</div>
|
||
<div class="text">适应图像</div>
|
||
</div>
|
||
|
||
<div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
:class="[activeTool==='Length'?'tool_active':'']"
|
||
data-tool="Length"
|
||
@click.prevent="setToolActive('Length',true)"
|
||
>
|
||
<svg-icon icon-class="length" class="svg-icon" />
|
||
</div>
|
||
<div class="text">长度测量</div>
|
||
</div>
|
||
|
||
<div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
:class="[activeTool==='Bidirectional'?'tool_active':'']"
|
||
data-tool="Bidirectional"
|
||
@click.prevent="setToolActive('Bidirectional',true)"
|
||
>
|
||
<svg-icon icon-class="bidirection" class="svg-icon" />
|
||
</div>
|
||
<div class="text">十字线</div>
|
||
</div>
|
||
|
||
<div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
:class="[activeTool==='ArrowAnnotate'?'tool_active':'']"
|
||
data-tool="ArrowAnnotate"
|
||
@click.prevent="setToolActive('ArrowAnnotate',true)"
|
||
>
|
||
<svg-icon icon-class="label" class="svg-icon" />
|
||
</div>
|
||
<div class="text">文字标注</div>
|
||
</div>
|
||
|
||
<!-- <div class="tool-wrapper">
|
||
<div
|
||
class="icon"
|
||
:class="[activeTool==='Eraser'?'tool_active':'']"
|
||
data-tool="Eraser"
|
||
@click.prevent="setToolActive('Eraser')"
|
||
>
|
||
<svg-icon icon-class="clear" class="svg-icon" />
|
||
</div>
|
||
<div class="text">清除标记</div>
|
||
</div> -->
|
||
|
||
<div class="tool-wrapper">
|
||
<div class="dropdown">
|
||
<div class="icon">
|
||
<svg-icon icon-class="more" class="svg-icon" />
|
||
</div>
|
||
<div class="text">更多<i class="el-icon-caret-bottom" /></div>
|
||
<div class="dropdown-content">
|
||
<ul style="width:90px;text-align: left;">
|
||
<li>
|
||
<a href="#">
|
||
<svg-icon icon-class="angle" />
|
||
角度测量
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a href="#">
|
||
<svg-icon icon-class="cobb" />
|
||
Cobb测量
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a href="#">
|
||
<svg-icon icon-class="oval" />
|
||
椭圆测量
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a href="#">
|
||
<svg-icon icon-class="rectangle" />
|
||
矩形测量
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a href="#">
|
||
<svg-icon icon-class="image" />
|
||
导出Png图像
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="dicom-viewers">
|
||
<div ref="container" class="viewer-container" :class="['box', `box_${layoutRow}_${layoutCol}`]">
|
||
<div
|
||
v-for="i in maxCanvas"
|
||
:key="i"
|
||
:class="['item', i-1===currentDicomCanvasIndex?'item_active':'']"
|
||
:data-index="i-1"
|
||
@click="activateDicomCanvas(i-1)"
|
||
>
|
||
<dicom-canvas
|
||
v-if="canvasW"
|
||
:ref="`dicomCanvas${i-1}`"
|
||
:style="{width:canvasW,height: canvasH}"
|
||
:is-current-task="isCurrentTask"
|
||
:is-active="i-1===currentDicomCanvasIndex"
|
||
@setMeasureData="setMeasureData"
|
||
@removeMeasureData="removeMeasureData"
|
||
@modifyMeasureData="modifyMeasureData"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div ref="form-container" class="form-container">
|
||
|
||
<MeasurementList
|
||
v-if="visitTaskId"
|
||
ref="measurementList"
|
||
:is-current-task="isCurrentTask"
|
||
/>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import * as cornerstone from 'cornerstone-core'
|
||
import * as cornerstoneMath from 'cornerstone-math'
|
||
import * as cornerstoneTools from 'cornerstone-tools'
|
||
import Hammer from 'hammerjs'
|
||
cornerstoneTools.external.cornerstone = cornerstone
|
||
cornerstoneTools.external.Hammer = Hammer
|
||
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
|
||
import DicomCanvas from './DicomCanvas'
|
||
import MeasurementList from './MeasurementList'
|
||
import Store from './Store'
|
||
export default {
|
||
name: 'DicomViewer',
|
||
components: { DicomCanvas, MeasurementList },
|
||
props: {
|
||
visitTaskId: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
isCurrentTask: {
|
||
type: Boolean,
|
||
required: true
|
||
},
|
||
readingTaskState: {
|
||
type: Number,
|
||
required: true
|
||
}
|
||
|
||
},
|
||
data() {
|
||
return {
|
||
layouts: [
|
||
{ index: 0, row: 1, col: 1 },
|
||
{ index: 1, row: 1, col: 2 },
|
||
{ index: 2, row: 2, col: 1 },
|
||
{ index: 3, row: 2, col: 2 }
|
||
],
|
||
maxCanvas: 1,
|
||
layoutRow: 1,
|
||
layoutCol: 1,
|
||
currentDicomCanvasIndex: 0,
|
||
currentDicomCanvas: {
|
||
toolState: {
|
||
clipPlaying: false
|
||
}
|
||
},
|
||
colormapsList: [],
|
||
rotateList: [],
|
||
colorList: [],
|
||
wwwcList: [],
|
||
canvasW: null,
|
||
canvasH: null,
|
||
activeTool: '',
|
||
measuredTools: ['Length', 'Bidirectional']
|
||
}
|
||
},
|
||
mounted() {
|
||
this.rotateList[0] = '1'
|
||
this.colorList[0] = ''
|
||
this.wwwcList[0] = '1'
|
||
this.colormapsList = cornerstone.colors.getColormapsList()
|
||
this.currentDicomCanvas = this.$refs['dicomCanvas0'] ? this.$refs['dicomCanvas0'][0] : ''
|
||
this.setCanvasStyle()
|
||
window.addEventListener('resize', this.setCanvasStyle)
|
||
Store.$on('updateImage', (measureData) => {
|
||
for (let i = 0; i < this.maxCanvas; i++) {
|
||
var stack = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].stack
|
||
if (stack.studyId) {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].updateImage(measureData)
|
||
}
|
||
}
|
||
console.log('updateImage')
|
||
})
|
||
},
|
||
beforeDestroy() {
|
||
Store.$off('updateImage')
|
||
},
|
||
methods: {
|
||
loadImageStack(dicomSeries) {
|
||
if (this.activeTool) {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolPassive(this.activeTool)
|
||
this.activeTool = ''
|
||
}
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].loadImageStack(dicomSeries)
|
||
},
|
||
|
||
// 设置画布大小
|
||
setCanvasStyle() {
|
||
this.canvasW = (window.innerWidth - 520) / this.layoutCol + 'px'
|
||
this.canvasH = (window.innerHeight - 130) / this.layoutRow + 'px'
|
||
},
|
||
// 切换布局
|
||
changeLayout(layout) {
|
||
this.layoutRow = layout.row
|
||
this.layoutCol = layout.col
|
||
this.setCanvasStyle()
|
||
this.maxCanvas = layout.row * layout.col
|
||
this.$nextTick(() => {
|
||
const elements = document.querySelectorAll('.cornerstone-element')
|
||
Array.from(elements).forEach((element) => {
|
||
cornerstone.enable(element)
|
||
cornerstone.resize(element)
|
||
})
|
||
})
|
||
},
|
||
// 激活画布
|
||
activateDicomCanvas(index) {
|
||
if (index !== this.currentDicomCanvasIndex) {
|
||
if (this.activeTool) {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolPassive(this.activeTool)
|
||
this.activeTool = ''
|
||
}
|
||
|
||
this.currentDicomCanvasIndex = index
|
||
this.currentDicomCanvas = this.$refs[`dicomCanvas${index}`]
|
||
this.currentDicomCanvas.tabIndex = 0
|
||
if (!this.rotateList[this.currentDicomCanvasIndex]) {
|
||
this.rotateList[this.currentDicomCanvasIndex] = '1'
|
||
}
|
||
if (!this.colorList[this.currentDicomCanvasIndex]) {
|
||
this.colorList[this.currentDicomCanvasIndex] = ''
|
||
}
|
||
if (!this.wwwcList[this.currentDicomCanvasIndex]) {
|
||
this.wwwcList[this.currentDicomCanvasIndex] = '1'
|
||
}
|
||
var stack = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].stack
|
||
if (stack.studyId) {
|
||
Store.$emit('toggleVisitList', stack)
|
||
}
|
||
}
|
||
},
|
||
setToolActive(toolName, isMeasuredTool) {
|
||
console.log((!this.isCurrentTask && isMeasuredTool) || this.readingTaskState >= 2)
|
||
if ((!this.isCurrentTask && isMeasuredTool) || this.readingTaskState >= 2) {
|
||
this.activeTool = ''
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(toolName)
|
||
return
|
||
}
|
||
if (this.activeTool === toolName) {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(toolName)
|
||
this.activeTool = ''
|
||
} else {
|
||
if (this.measuredTools.includes(toolName)) {
|
||
var isCanActiveTool = this.$refs['measurementList'].isCanActiveTool()
|
||
if (isCanActiveTool) {
|
||
this.activeTool = toolName
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolActive(toolName)
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
setToolEnabled() {
|
||
if (!this.isCurrentTask) {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(this.activeTool)
|
||
this.activeTool = ''
|
||
return
|
||
}
|
||
if (this.activeTool) {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(this.activeTool)
|
||
this.activeTool = ''
|
||
}
|
||
},
|
||
fitToType(toolName) {
|
||
if (this.activeTool !== 'fitToWindow' && this.activeTool !== 'fitToImage') {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolPassive(this.activeTool)
|
||
}
|
||
if (toolName === 'fitToWindow') {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].fitToWindow()
|
||
} else if (toolName === 'fitToImage') {
|
||
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].fitToImage()
|
||
}
|
||
this.activeTool = toolName
|
||
},
|
||
setMeasureData(data) {
|
||
this.$refs['measurementList'].setMeasuredData(data)
|
||
this.activeTool = ''
|
||
},
|
||
removeMeasureData(uuid) {
|
||
console.log(uuid)
|
||
},
|
||
modifyMeasureData(data) {
|
||
this.$refs['measurementList'].modifyMeasuredData(data)
|
||
this.activeTool = ''
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
.dicom-viewer-wrapper{
|
||
height: 100%;
|
||
padding: 5px 0px 5px 5px;
|
||
box-sizing: border-box;
|
||
|
||
.dicom-viewer-container{
|
||
display:flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
}
|
||
::-webkit-scrollbar {
|
||
width: 5px;
|
||
height: 5px;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
border-radius: 10px;
|
||
background: #d0d0d0;
|
||
}
|
||
.dicom-tools{
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
height: 80px;
|
||
padding: 0 5px;
|
||
border: 1px solid #727272;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
.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:20px;
|
||
color:#ddd;
|
||
}
|
||
}
|
||
.text{
|
||
position: relative;
|
||
font-size: 12px;
|
||
margin-top: 5px;
|
||
color: #d0d0d0;
|
||
}
|
||
}
|
||
.tool_active{
|
||
background-color: #607d8b;
|
||
}
|
||
.icon:hover{
|
||
background-color: #607d8b;
|
||
}
|
||
.dropdown {
|
||
position: relative;
|
||
display: inline-block;
|
||
.text{
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
.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: 1;
|
||
font-size: 12px;
|
||
ul{
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
text-align: center;
|
||
li{
|
||
a{
|
||
display: block;
|
||
padding: 5px 0px;
|
||
}
|
||
}
|
||
}
|
||
ul li:hover a{
|
||
background-color: #727272;
|
||
}
|
||
}
|
||
|
||
.dropdown:hover .dropdown-content {
|
||
display: block;
|
||
}
|
||
}
|
||
.dicom-viewers{
|
||
box-sizing: border-box;
|
||
flex: 1;
|
||
// width: 100%;
|
||
margin-top: 5px;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: flex-start;
|
||
.viewer-container{
|
||
box-sizing: border-box;
|
||
flex: 1;
|
||
height: 100%;
|
||
border: 1px solid #727272;
|
||
}
|
||
.form-container{
|
||
// box-sizing: border-box;
|
||
width: 300px;
|
||
height: 100%;
|
||
border: 1px solid #727272;
|
||
// overflow-y: auto;
|
||
}
|
||
.measurement-container{
|
||
// height: 100%;
|
||
overflow-y: auto;
|
||
}
|
||
.box{
|
||
display: grid;
|
||
box-sizing: border-box;
|
||
height: 100%;
|
||
padding: 0;
|
||
.item{
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
border: 1px solid rgba(255, 255, 255, 0.21);
|
||
position: relative;
|
||
&_active{
|
||
border: 1px solid #5297b9;
|
||
}
|
||
}
|
||
}
|
||
.box_1_1{
|
||
grid-template-columns: repeat(1, 100%); //1列,占100%
|
||
grid-template-rows: repeat(1, 100%); //1行,占100%
|
||
}
|
||
.box_1_2{
|
||
grid-template-columns: repeat(2, 50%); //1列,占50%
|
||
grid-template-rows: repeat(1, 100%); //1行,占100%
|
||
}
|
||
.box_2_1{
|
||
grid-template-columns: repeat(1, 100%); //1列,占100%
|
||
grid-template-rows: repeat(2, 50%); //1行,占50%
|
||
}
|
||
.box_2_2{
|
||
grid-template-columns: repeat(2, 50%); //1列,占50%
|
||
grid-template-rows: repeat(2, 50%); //1行,占50%
|
||
}
|
||
|
||
// .box_3_1{
|
||
// grid-template-columns: repeat(3, 100%); //1列,占100%
|
||
// grid-template-rows: repeat(3, 33.33%); //1行,占100%
|
||
// }
|
||
// .box_3_2{
|
||
// grid-template-columns: repeat(3, 50%); //1列,占100%
|
||
// grid-template-rows: repeat(3, 50%); //1行,占100%
|
||
// }
|
||
// .box_3_3{
|
||
// grid-template-columns: repeat(3, 33.33%); //1列,占100%
|
||
// grid-template-rows: repeat(3, 33.33%); //1行,占100%
|
||
// }
|
||
|
||
}
|
||
}
|
||
</style>
|