阅片页面增加患者信息、检查信息
continuous-integration/drone/push Build is passing Details

main
wangxiaoshuang 2025-08-25 14:00:46 +08:00
parent ae2cb2c5fb
commit 64581a3e07
2 changed files with 354 additions and 302 deletions

View File

@ -1,15 +1,7 @@
<template>
<div
id="canvas"
ref="canvas"
v-loading="loading"
element-loading-text="Loading..."
element-loading-background="rgba(0, 0, 0, 0.8)"
style="position:relative;"
class="cornerstone-element"
@mouseup="sliderMouseup"
@contextmenu.prevent="onContextmenu"
>
<div id="canvas" ref="canvas" v-loading="loading" element-loading-text="Loading..."
element-loading-background="rgba(0, 0, 0, 0.8)" style="position:relative;" class="cornerstone-element"
@mouseup="sliderMouseup" @contextmenu.prevent="onContextmenu">
<!-- 临床数据 -->
<div v-if="stack.isExistsClinicalData" class="info-cd" @click.stop="handleViewCD($event)">
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:clinicalData')" placement="bottom">
@ -18,42 +10,36 @@
</div>
<!-- 切换访视 -->
<div
v-if="stack.imageRendered && isReadingTaskViewInOrder === 1"
class="info-visit"
@dblclick.stop="preventDefault($event)"
>
<div
class="arrw_div_wrapper"
<div v-if="stack.imageRendered && isReadingTaskViewInOrder === 1" class="info-visit"
@dblclick.stop="preventDefault($event)">
<div class="arrw_div_wrapper"
:style="{ cursor: stack.visitTaskNum <= minVistNum ? 'not-allowed' : 'pointer', color: stack.visitTaskNum <= minVistNum ? '#888' : '#fff' }"
@click.stop.prevent="toggleSeries($event,-1)"
@dblclick.stop="preventDefault($event)"
>
@click.stop.prevent="toggleSeries($event, -1)" @dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-left" />
</div>
<div class="blind_name_wrapper">
{{ stack.taskBlindName }}
</div>
<div
class="arrw_div_wrapper"
<div class="arrw_div_wrapper"
:style="{ cursor: stack.visitTaskNum >= maxVistNum ? 'not-allowed' : 'pointer', color: stack.visitTaskNum >= maxVistNum ? '#888' : '#fff' }"
@click.stop.prevent="toggleSeries($event,1)"
@dblclick.stop="preventDefault($event)"
>
@click.stop.prevent="toggleSeries($event, 1)" @dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-right" />
</div>
</div>
<div class="info-series">
<h2 v-if="isReadingShowSubjectInfo" style="color:#f44336;padding: 5px 0px;margin: 0;">{{ subjectCode }} {{ stack.taskBlindName }}</h2>
<h2 v-if="isReadingShowSubjectInfo" style="color:#f44336;padding: 5px 0px;margin: 0;">{{ subjectCode }} {{
stack.taskBlindName }}</h2>
<div v-show="dicomInfo.series">Series: #{{ dicomInfo.series }}</div>
<div>Image: #{{ dicomInfo.frame }}</div>
<div>{{ dicomInfo.modality }}</div>
</div>
<div class="info-image">
<div v-show="mousePosition.mo">
Pos: {{ mousePosition.x?mousePosition.x.toFixed(0):'' }}, {{ mousePosition.y?mousePosition.y.toFixed(0):'' }}
Pos: {{ mousePosition.x ? mousePosition.x.toFixed(0) : '' }}, {{ mousePosition.y ? mousePosition.y.toFixed(0) :
'' }}
</div>
<div v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo">
<div
v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo">
HU: {{ mousePosition.mo }}
</div>
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">
@ -75,14 +61,20 @@
<div class="info-subject">
<div>{{ stack.description }}</div>
<!-- <div>{{ dicomInfo.hospital }}</div> -->
<!-- <div v-show="dicomInfo.pid">{{ dicomInfo.pid }}</div> -->
<div v-if="dicomInfo.pid">{{ dicomInfo.pid }}</div>
<div v-if="dicomInfo.patientName">{{ dicomInfo.patientName }}</div>
<!-- <div>{{ subjectCode }}</div> -->
<!-- <div>{{ dicomInfo.sex }} {{ dicomInfo.age }}</div> -->
<!-- <div v-show="dicomInfo.acc">ACC {{ dicomInfo.acc }}</div> -->
<!-- <div>{{ dicomInfo.time }}</div> -->
<div v-if="dicomInfo.modality">{{ dicomInfo.modality }}</div>
<div v-if="dicomInfo.time">{{ dicomInfo.time }}</div>
</div>
<div ref="sliderBox" class="my_slider_box" style="position: absolute;right: 1px;height: calc(100% - 140px);transform: translateY(-50%);top: calc(50% - 30px);width: 10px;background: #333;cursor: pointer" @click.stop="goViewer($event)">
<div :style="{top: height + '%'}" style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move" @click.stop.prevent="() => {return}" @mousedown.stop="sliderMousedown($event)" />
<div ref="sliderBox" class="my_slider_box"
style="position: absolute;right: 1px;height: calc(100% - 140px);transform: translateY(-50%);top: calc(50% - 30px);width: 10px;background: #333;cursor: pointer"
@click.stop="goViewer($event)">
<div :style="{ top: height + '%' }"
style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move"
@click.stop.prevent="() => { return }" @mousedown.stop="sliderMousedown($event)" />
</div>
<div style="position: absolute;left: 50%;top: 30px;color: #f44336;transform: translateX(-50%);">
{{ markers.top }}
@ -152,6 +144,7 @@ import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/too
import invertOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/invertOrientationString'
// import calculateLongestAndShortestDiameters from '@/views/trials/trials-panel/reading/dicoms/tools/Bidirectional/calculateLongestAndShortestDiameters'
import calculateSUV from '@/views/trials/trials-panel/reading/dicoms/tools/calculateSUV'
import { convertBytes } from '@/utils/dicom-character-set'
cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
@ -1218,9 +1211,24 @@ export default {
ToolStateManager.clearImageIdToolState(imageId)
e.detail.enabledElement.options = {}
var data = e.detail.image.data
const patientNameElement = data.elements.x00100010
const patientNameBytes = new Uint8Array(
data.byteArray.buffer,
patientNameElement ? patientNameElement.dataOffset : 0,
patientNameElement ? patientNameElement.length : 0
)
// dicom 2025.03.04
let SpecificCharacterSet = data.string('x00080005')
? data.string('x00080005').replace('ISO IR', 'ISO_IR')
: ''
const patientNameStr = convertBytes(
SpecificCharacterSet,
patientNameBytes
)
this.dicomInfo.hospital = data.string('x00080080')
// this.dicomInfo.pid = data.string('x00100020')
this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.pid = data.string('x00100020')
this.dicomInfo.patientName = patientNameStr
// this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.name = data.string('x00100010')
this.dicomInfo.age = data.string('x00101010')
this.dicomInfo.sex = data.string('x00100040')
@ -1231,8 +1239,7 @@ export default {
data.string('x00080030')
)
this.dicomInfo.series = data.string('x00200011')
this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${
this.stack.imageIds.length
this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${this.stack.imageIds.length
}`
this.dicomInfo.size = `${data.uint16('x00280011')}*${data.uint16(
'x00280010'
@ -1965,6 +1972,7 @@ export default {
<style lang="scss" scoped>
.context-menu-wrapper {
position: absolute;
ul {
list-style: none;
margin: 0px;
@ -1980,15 +1988,18 @@ export default {
width: 80px;
box-sizing: border-box;
}
.menu {
li {
padding: 2px 5px;
position: relative;
border-bottom: 1px solid #666;
div {
padding: 5px;
}
}
.submenu {
position: absolute;
left: 77px;
@ -1996,9 +2007,11 @@ export default {
background: #343333;
color: #fff;
display: none;
li {
padding: 2px 5px;
border-bottom: 1px solid #666;
div {
padding: 5px;
}
@ -2006,20 +2019,25 @@ export default {
}
}
.menu li:hover {
background-color: #ff5722;
.submenu {
display: block;
}
}
.menu_active {
cursor: pointer;
}
.menu_disabled {
cursor: not-allowed;
}
}
.info-visit {
position: absolute;
left: 50%;
@ -2027,6 +2045,7 @@ export default {
transform: translateX(-50%);
display: flex;
flex-direction: row;
.arrw_div_wrapper {
width: 20px;
height: 20px;
@ -2035,6 +2054,7 @@ export default {
line-height: 20px;
border-radius: 10%;
}
.blind_name_wrapper {
height: 20px;
line-height: 20px;
@ -2044,6 +2064,7 @@ export default {
font-size: 14px;
}
}
.info-cd {
position: absolute;
left: 10px;
@ -2053,6 +2074,7 @@ export default {
font-size: 18px;
cursor: pointer;
}
.info-series {
position: absolute;
left: 10px;
@ -2062,6 +2084,7 @@ export default {
font-size: 12px;
/* z-index: 1; */
}
.info-image {
position: absolute;
left: 10px;
@ -2081,6 +2104,7 @@ export default {
font-size: 12px;
/* z-index: 1; */
}
.info-instance {
position: absolute;
right: 15px;
@ -2113,6 +2137,7 @@ export default {
margin: 10px;
cursor: default;
}
.menu__item:hover {
color: #ff0000;
}
@ -2132,6 +2157,7 @@ li:hover {
background-color: #e0e0e2;
color: white;
}
.msg-div {
position: absolute;
z-index: 10;

View File

@ -1,15 +1,7 @@
<template>
<div
id="canvas"
ref="canvas"
v-loading="loading"
element-loading-text="Loading..."
element-loading-background="rgba(0, 0, 0, 0.8)"
style="position:relative;"
class="cornerstone-element"
@mouseup="sliderMouseup"
@contextmenu.prevent="onContextmenu"
>
<div id="canvas" ref="canvas" v-loading="loading" element-loading-text="Loading..."
element-loading-background="rgba(0, 0, 0, 0.8)" style="position:relative;" class="cornerstone-element"
@mouseup="sliderMouseup" @contextmenu.prevent="onContextmenu">
<!-- 临床数据 -->
<div v-if="stack.isExistsClinicalData" class="info-cd" @click.stop="handleViewCD($event)">
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:clinicalData')" placement="bottom">
@ -18,42 +10,36 @@
</div>
<!-- 切换访视 -->
<div
v-if="stack.imageRendered && isReadingTaskViewInOrder === 1"
class="info-visit"
@dblclick.stop="preventDefault($event)"
>
<div
class="arrw_div_wrapper"
<div v-if="stack.imageRendered && isReadingTaskViewInOrder === 1" class="info-visit"
@dblclick.stop="preventDefault($event)">
<div class="arrw_div_wrapper"
:style="{ cursor: stack.visitTaskNum <= minVistNum ? 'not-allowed' : 'pointer', color: stack.visitTaskNum <= minVistNum ? '#888' : '#fff' }"
@click.stop.prevent="toggleSeries($event,-1)"
@dblclick.stop="preventDefault($event)"
>
@click.stop.prevent="toggleSeries($event, -1)" @dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-left" />
</div>
<div class="blind_name_wrapper">
{{ stack.taskBlindName }}
</div>
<div
class="arrw_div_wrapper"
<div class="arrw_div_wrapper"
:style="{ cursor: stack.visitTaskNum >= maxVistNum ? 'not-allowed' : 'pointer', color: stack.visitTaskNum >= maxVistNum ? '#888' : '#fff' }"
@click.stop.prevent="toggleSeries($event,1)"
@dblclick.stop="preventDefault($event)"
>
@click.stop.prevent="toggleSeries($event, 1)" @dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-right" />
</div>
</div>
<div class="info-series">
<h2 v-if="isReadingShowSubjectInfo" style="color:#f44336;padding: 5px 0px;margin: 0;">{{ subjectCode }} {{ stack.taskBlindName }}</h2>
<h2 v-if="isReadingShowSubjectInfo" style="color:#f44336;padding: 5px 0px;margin: 0;">{{ subjectCode }} {{
stack.taskBlindName }}</h2>
<div v-show="dicomInfo.series">Series: #{{ dicomInfo.series }}</div>
<div>Image: #{{ dicomInfo.frame }}</div>
<div>{{ dicomInfo.modality }}</div>
</div>
<div class="info-image">
<div v-show="mousePosition.mo">
Pos: {{ mousePosition.x?mousePosition.x.toFixed(0):'' }}, {{ mousePosition.y?mousePosition.y.toFixed(0):'' }}
Pos: {{ mousePosition.x ? mousePosition.x.toFixed(0) : '' }}, {{ mousePosition.y ? mousePosition.y.toFixed(0) :
'' }}
</div>
<div v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo">
<div
v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo">
HU: {{ mousePosition.mo }}
</div>
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">
@ -75,14 +61,20 @@
<div class="info-subject">
<div>{{ stack.description }}</div>
<!-- <div>{{ dicomInfo.hospital }}</div> -->
<!-- <div v-show="dicomInfo.pid">{{ dicomInfo.pid }}</div> -->
<div v-if="dicomInfo.pid">{{ dicomInfo.pid }}</div>
<div v-if="dicomInfo.patientName">{{ dicomInfo.patientName }}</div>
<!-- <div>{{ subjectCode }}</div> -->
<!-- <div>{{ dicomInfo.sex }} {{ dicomInfo.age }}</div> -->
<!-- <div v-show="dicomInfo.acc">ACC {{ dicomInfo.acc }}</div> -->
<!-- <div>{{ dicomInfo.time }}</div> -->
<div v-if="dicomInfo.modality">{{ dicomInfo.modality }}</div>
<div v-if="dicomInfo.time">{{ dicomInfo.time }}</div>
</div>
<div ref="sliderBox" class="my_slider_box" style="position: absolute;right: 1px;height: calc(100% - 140px);transform: translateY(-50%);top: calc(50% - 30px);width: 10px;background: #333;cursor: pointer" @click.stop="goViewer($event)">
<div :style="{top: height + '%'}" style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move" @click.stop.prevent="() => {return}" @mousedown.stop="sliderMousedown($event)" />
<div ref="sliderBox" class="my_slider_box"
style="position: absolute;right: 1px;height: calc(100% - 140px);transform: translateY(-50%);top: calc(50% - 30px);width: 10px;background: #333;cursor: pointer"
@click.stop="goViewer($event)">
<div :style="{ top: height + '%' }"
style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move"
@click.stop.prevent="() => { return }" @mousedown.stop="sliderMousedown($event)" />
</div>
<div style="position: absolute;left: 50%;top: 30px;color: #f44336;transform: translateX(-50%);">
{{ markers.top }}
@ -153,6 +145,7 @@ import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/too
import invertOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/invertOrientationString'
// import calculateLongestAndShortestDiameters from '@/views/trials/trials-panel/reading/dicoms/tools/Bidirectional/calculateLongestAndShortestDiameters'
import calculateSUV from '@/views/trials/trials-panel/reading/dicoms/tools/calculateSUV'
import { convertBytes } from '@/utils/dicom-character-set'
cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
@ -1194,9 +1187,24 @@ export default {
ToolStateManager.clearImageIdToolState(imageId)
e.detail.enabledElement.options = {}
var data = e.detail.image.data
const patientNameElement = data.elements.x00100010
const patientNameBytes = new Uint8Array(
data.byteArray.buffer,
patientNameElement ? patientNameElement.dataOffset : 0,
patientNameElement ? patientNameElement.length : 0
)
// dicom 2025.03.04
let SpecificCharacterSet = data.string('x00080005')
? data.string('x00080005').replace('ISO IR', 'ISO_IR')
: ''
const patientNameStr = convertBytes(
SpecificCharacterSet,
patientNameBytes
)
this.dicomInfo.hospital = data.string('x00080080')
// this.dicomInfo.pid = data.string('x00100020')
this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.pid = data.string('x00100020')
this.dicomInfo.patientName = patientNameStr
// this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.name = data.string('x00100010')
this.dicomInfo.age = data.string('x00101010')
this.dicomInfo.sex = data.string('x00100040')
@ -1207,8 +1215,7 @@ export default {
data.string('x00080030')
)
this.dicomInfo.series = data.string('x00200011')
this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${
this.stack.imageIds.length
this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${this.stack.imageIds.length
}`
this.dicomInfo.size = `${data.uint16('x00280011')}*${data.uint16(
'x00280010'
@ -1942,6 +1949,7 @@ export default {
<style lang="scss" scoped>
.context-menu-wrapper {
position: absolute;
ul {
list-style: none;
margin: 0px;
@ -1957,15 +1965,18 @@ export default {
width: 80px;
box-sizing: border-box;
}
.menu {
li {
padding: 2px 5px;
position: relative;
border-bottom: 1px solid #666;
div {
padding: 5px;
}
}
.submenu {
position: absolute;
left: 77px;
@ -1973,9 +1984,11 @@ export default {
background: #343333;
color: #fff;
display: none;
li {
padding: 2px 5px;
border-bottom: 1px solid #666;
div {
padding: 5px;
}
@ -1983,20 +1996,25 @@ export default {
}
}
.menu li:hover {
background-color: #ff5722;
.submenu {
display: block;
}
}
.menu_active {
cursor: pointer;
}
.menu_disabled {
cursor: not-allowed;
}
}
.info-visit {
position: absolute;
left: 50%;
@ -2004,6 +2022,7 @@ export default {
transform: translateX(-50%);
display: flex;
flex-direction: row;
.arrw_div_wrapper {
width: 20px;
height: 20px;
@ -2012,6 +2031,7 @@ export default {
line-height: 20px;
border-radius: 10%;
}
.blind_name_wrapper {
height: 20px;
line-height: 20px;
@ -2021,6 +2041,7 @@ export default {
font-size: 14px;
}
}
.info-cd {
position: absolute;
left: 10px;
@ -2030,6 +2051,7 @@ export default {
font-size: 18px;
cursor: pointer;
}
.info-series {
position: absolute;
left: 10px;
@ -2039,6 +2061,7 @@ export default {
font-size: 12px;
/* z-index: 1; */
}
.info-image {
position: absolute;
left: 10px;
@ -2058,6 +2081,7 @@ export default {
font-size: 12px;
/* z-index: 1; */
}
.info-instance {
position: absolute;
right: 15px;
@ -2090,6 +2114,7 @@ export default {
margin: 10px;
cursor: default;
}
.menu__item:hover {
color: #ff0000;
}
@ -2109,6 +2134,7 @@ li:hover {
background-color: #e0e0e2;
color: white;
}
.msg-div {
position: absolute;
z-index: 10;