suv融合
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
18f23d2bba
commit
8d6ae3a49f
|
@ -21,7 +21,7 @@ service.interceptors.request.use(
|
|||
async config => {
|
||||
path = router && router.app && router.app._route && router.app._route.path
|
||||
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
|
||||
config.baseURL = process.env.NODE_ENV === 'prod' ? "https://api.irc.extimaging.com" : '/api'
|
||||
// config.baseURL = process.env.NODE_ENV === 'prod' ? "https://api.irc.extimaging.com" : '/api'
|
||||
config.headers['Self-Referer'] = window.location.href
|
||||
var language = zzSessionStorage.getItem('lang')
|
||||
config.headers['Accept-Language'] = language === 'en' ? 'en-US,en;q=0.5' : 'zh-CN,zh;q=0.9'
|
||||
|
|
|
@ -1,24 +1,14 @@
|
|||
<template>
|
||||
<div
|
||||
v-loading="loading"
|
||||
:element-loading-text="loadingText"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(0, 0, 0, 0.8)"
|
||||
class="dicom-viewer-container"
|
||||
@contextmenu="rightClick"
|
||||
>
|
||||
<div v-loading="loading" :element-loading-text="loadingText" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(0, 0, 0, 0.8)" class="dicom-viewer-container" @contextmenu="rightClick">
|
||||
<!-- 工具条 -->
|
||||
<div class="dicom-tools">
|
||||
<!-- 窗宽窗位 -->
|
||||
<el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:wwwc')}`" placement="bottom">
|
||||
<div class="tool-wrapper" @click.stop="showPanel($event)" @mouseleave="handleMouseout">
|
||||
<div class="dropdown">
|
||||
<div
|
||||
class="icon"
|
||||
:class="[activeTool==='WindowLevel'?'tool_active':'']"
|
||||
data-tool="WindowLevel"
|
||||
@click.prevent="setBasicToolActive('WindowLevel')"
|
||||
>
|
||||
<div class="icon" :class="[activeTool === 'WindowLevel' ? 'tool_active' : '']" data-tool="WindowLevel"
|
||||
@click.prevent="setBasicToolActive('WindowLevel')">
|
||||
<svg-icon icon-class="reverse" class="svg-icon" />
|
||||
<i class="el-icon-arrow-down" style="color:#fff;" />
|
||||
</div>
|
||||
|
@ -56,13 +46,10 @@
|
|||
</div>
|
||||
</el-tooltip> -->
|
||||
<!-- 反色 -->
|
||||
<el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:reverseColor')}`" placement="bottom">
|
||||
<el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:reverseColor')}`"
|
||||
placement="bottom">
|
||||
<div class="tool-wrapper">
|
||||
<div
|
||||
class="icon"
|
||||
data-tool="reverse"
|
||||
@click.prevent="toggleInvert"
|
||||
>
|
||||
<div class="icon" data-tool="reverse" @click.prevent="toggleInvert">
|
||||
<svg-icon icon-class="reversecolor" class="svg-icon" />
|
||||
</div>
|
||||
<!-- 反色 -->
|
||||
|
@ -72,12 +59,8 @@
|
|||
<!-- 缩放 -->
|
||||
<el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:zoom')}`" placement="bottom">
|
||||
<div class="tool-wrapper">
|
||||
<div
|
||||
class="icon"
|
||||
:class="[activeTool==='Zoom'?'tool_active':'']"
|
||||
data-tool="Zoom"
|
||||
@click.prevent="setBasicToolActive('Zoom')"
|
||||
>
|
||||
<div class="icon" :class="[activeTool === 'Zoom' ? 'tool_active' : '']" data-tool="Zoom"
|
||||
@click.prevent="setBasicToolActive('Zoom')">
|
||||
<svg-icon icon-class="magnifier" class="svg-icon" />
|
||||
</div>
|
||||
<!-- 缩放 -->
|
||||
|
@ -87,12 +70,8 @@
|
|||
<!-- 移动 -->
|
||||
<el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:move')}`" placement="bottom">
|
||||
<div class="tool-wrapper">
|
||||
<div
|
||||
class="icon"
|
||||
:class="[activeTool==='Pan'?'tool_active':'']"
|
||||
data-tool="Pan"
|
||||
@click.prevent="setBasicToolActive('Pan')"
|
||||
>
|
||||
<div class="icon" :class="[activeTool === 'Pan' ? 'tool_active' : '']" data-tool="Pan"
|
||||
@click.prevent="setBasicToolActive('Pan')">
|
||||
<svg-icon icon-class="move" class="svg-icon" />
|
||||
</div>
|
||||
<!-- 移动 -->
|
||||
|
@ -103,12 +82,8 @@
|
|||
<el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:rotate')}`" placement="bottom">
|
||||
<div class="tool-wrapper" @click.stop="showPanel($event)" @mouseleave="handleMouseout">
|
||||
<div class="dropdown">
|
||||
<div
|
||||
class="icon"
|
||||
:class="[activeTool==='Rotate'?'tool_active':'']"
|
||||
data-tool="Pan"
|
||||
@click.prevent="setBasicToolActive('TrackballRotate')"
|
||||
>
|
||||
<div class="icon" :class="[activeTool === 'Rotate' ? 'tool_active' : '']" data-tool="Pan"
|
||||
@click.prevent="setBasicToolActive('TrackballRotate')">
|
||||
<svg-icon icon-class="rotate" class="svg-icon" />
|
||||
<i class="el-icon-arrow-down" style="color:#fff;" />
|
||||
</div>
|
||||
|
@ -126,7 +101,8 @@
|
|||
</el-tooltip>
|
||||
<!-- 椭圆oval -->
|
||||
<template v-for="tool in measuredTools">
|
||||
<el-tooltip v-if="isCurrentTask && readingTaskState !== 2" :key="tool.toolName" class="item" effect="dark" placement="bottom">
|
||||
<el-tooltip v-if="isCurrentTask && readingTaskState !== 2" :key="tool.toolName" class="item" effect="dark"
|
||||
placement="bottom">
|
||||
<div slot="content">
|
||||
<div v-if="tool.disabledReason">
|
||||
<h4 style="margin:0;padding-bottom:2px;">{{ tool.text }}</h4>
|
||||
|
@ -136,12 +112,8 @@
|
|||
|
||||
</div>
|
||||
<div class="tool-wrapper">
|
||||
<div
|
||||
class="icon"
|
||||
:class="[activeTool===tool.toolName?'tool_active':'']"
|
||||
@mouseenter="enter($event,tool.toolName)"
|
||||
@click.prevent="setMeasureToolActive(tool.toolName)"
|
||||
>
|
||||
<div class="icon" :class="[activeTool === tool.toolName ? 'tool_active' : '']"
|
||||
@mouseenter="enter($event, tool.toolName)" @click.prevent="setMeasureToolActive(tool.toolName)">
|
||||
<svg-icon :icon-class="tool.icon" class="svg-icon" />
|
||||
</div>
|
||||
<div class="text">{{ tool.text }}</div>
|
||||
|
@ -152,21 +124,16 @@
|
|||
<!-- 重置 -->
|
||||
<el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:reset')}`" placement="bottom">
|
||||
<div class="tool-wrapper">
|
||||
<div
|
||||
class="icon"
|
||||
@click.prevent="resetViewport"
|
||||
>
|
||||
<div class="icon" @click.prevent="resetViewport">
|
||||
<svg-icon icon-class="refresh" class="svg-icon" />
|
||||
</div>
|
||||
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="!isBaseLineTask" class="item" effect="dark" :content="$t('trials:lugano:button:record')" placement="bottom">
|
||||
<el-tooltip v-if="!isBaseLineTask" class="item" effect="dark" :content="$t('trials:lugano:button:record')"
|
||||
placement="bottom">
|
||||
<div class="tool-wrapper">
|
||||
<div
|
||||
class="icon"
|
||||
@click.prevent="historyRecord"
|
||||
>
|
||||
<div class="icon" @click.prevent="historyRecord">
|
||||
<svg-icon icon-class="record" class="svg-icon" />
|
||||
</div>
|
||||
<div class="text">{{ $t('trials:lugano:button:record') }}</div>
|
||||
|
@ -174,21 +141,14 @@
|
|||
</el-tooltip>
|
||||
<!-- 伪彩 -->
|
||||
<el-tooltip class="item" effect="dark" :content="$t('trials:lugano:button:colormap')" placement="bottom">
|
||||
<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 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>
|
||||
<!-- <canvas id="colorBarCanvas" /> -->
|
||||
<div class="dropdown">
|
||||
<div
|
||||
id="colorBar"
|
||||
class="icon"
|
||||
style="display: flex;align-items: center;width:266px"
|
||||
>
|
||||
<div id="colorBar" class="icon" style="display: flex;align-items: center;width:266px">
|
||||
<canvas id="colorBarCanvas" />
|
||||
|
||||
</div>
|
||||
|
@ -196,7 +156,9 @@
|
|||
<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)">
|
||||
<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>
|
||||
|
||||
|
@ -207,14 +169,8 @@
|
|||
</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"
|
||||
>
|
||||
<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>
|
||||
|
@ -256,55 +212,25 @@
|
|||
/>
|
||||
</div> -->
|
||||
<!-- @dblclick.native="reloadViewport('CT_AXIAL')" -->
|
||||
<div ref="dicomContainer" class="dicom-container box box_2_2" style="position: relative;" @dblclick="reloadViewport">
|
||||
<Viewport
|
||||
ref="CT_AXIAL"
|
||||
:index="1"
|
||||
:active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo"
|
||||
:series-info="ctSeries"
|
||||
:rendering-engine-id="renderingEngineId"
|
||||
viewport-id="CT_AXIAL"
|
||||
:volume="ctVolume"
|
||||
:measure-datas="measureDatas"
|
||||
:style="1===activeIndex ? viewportStyle : {}"
|
||||
/>
|
||||
<Viewport
|
||||
ref="PT_AXIAL"
|
||||
:index="2"
|
||||
:active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo"
|
||||
:series-info="petSeries"
|
||||
:rendering-engine-id="renderingEngineId"
|
||||
viewport-id="PT_AXIAL"
|
||||
:volume="ptVolume"
|
||||
:measure-datas="measureDatas"
|
||||
:style="2===activeIndex ? viewportStyle : {}"
|
||||
/>
|
||||
<Viewport
|
||||
ref="FUSION_AXIAL"
|
||||
:index="3"
|
||||
:active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo"
|
||||
:series-info="petSeries"
|
||||
:rendering-engine-id="renderingEngineId"
|
||||
viewport-id="FUSION_AXIAL"
|
||||
:volume="ptVolume"
|
||||
:measure-datas="measureDatas"
|
||||
:rgb-preset-name="rgbPresetName"
|
||||
:style="3===activeIndex ? viewportStyle : {}"
|
||||
/>
|
||||
<Viewport
|
||||
ref="PET_MIP_CORONAL"
|
||||
:index="4"
|
||||
:active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo"
|
||||
:series-info="petSeries"
|
||||
:rendering-engine-id="renderingEngineId"
|
||||
viewport-id="PET_MIP_CORONAL"
|
||||
:measure-datas="measureDatas"
|
||||
:style="4===activeIndex ? viewportStyle : {}"
|
||||
/>
|
||||
<div ref="dicomContainer" class="dicom-container box box_2_2" style="position: relative;"
|
||||
@dblclick="reloadViewport">
|
||||
<Viewport ref="CT_AXIAL" :index="1" :active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="ctSeries"
|
||||
:rendering-engine-id="renderingEngineId" viewport-id="CT_AXIAL" :volume="ctVolume"
|
||||
:measure-datas="measureDatas" :style="1 === activeIndex ? viewportStyle : {}" />
|
||||
<Viewport ref="PT_AXIAL" :index="2" :active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
|
||||
:rendering-engine-id="renderingEngineId" viewport-id="PT_AXIAL" :volume="ptVolume"
|
||||
:measure-datas="measureDatas" :style="2 === activeIndex ? viewportStyle : {}" />
|
||||
<Viewport ref="FUSION_AXIAL" :index="3" :active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
|
||||
:rendering-engine-id="renderingEngineId" viewport-id="FUSION_AXIAL" :volume="ptVolume"
|
||||
:measure-datas="measureDatas" :rgb-preset-name="rgbPresetName"
|
||||
:style="3 === activeIndex ? viewportStyle : {}" />
|
||||
<Viewport ref="PET_MIP_CORONAL" :index="4" :active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
|
||||
:rendering-engine-id="renderingEngineId" viewport-id="PET_MIP_CORONAL" :measure-datas="measureDatas"
|
||||
:style="4 === activeIndex ? viewportStyle : {}" />
|
||||
</div>
|
||||
<!-- 表单 -->
|
||||
<div class="form-container" style="overflow-y: auto;">
|
||||
|
@ -319,22 +245,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-if="customWwc.visible"
|
||||
:visible.sync="customWwc.visible"
|
||||
:close-on-click-modal="false"
|
||||
:title="customWwc.title"
|
||||
width="400px"
|
||||
custom-class="base-dialog-wrapper"
|
||||
>
|
||||
<el-dialog v-if="customWwc.visible" :visible.sync="customWwc.visible" :close-on-click-modal="false"
|
||||
:title="customWwc.title" width="400px" custom-class="base-dialog-wrapper">
|
||||
<CustomWwwcForm :ww="activeCanvasWW" :wc="activeCanvasWC" @close="customWwc.visible = false" @setWwwc="setWwwc" />
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
:visible.sync="fusion.visible"
|
||||
:close-on-click-modal="false"
|
||||
:title="$t('trials:lugano:button:record')"
|
||||
width="850px"
|
||||
>
|
||||
<el-dialog :visible.sync="fusion.visible" :close-on-click-modal="false" :title="$t('trials:lugano:button:record')"
|
||||
width="850px">
|
||||
<FusionForm @close="fusion.visible = false" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
@ -607,7 +523,7 @@ export default {
|
|||
FusionEvent.$on('imageLocation', (obj) => {
|
||||
this.imageLocation(obj)
|
||||
})
|
||||
FusionEvent.$on('getScreenshots', async(measuredData, callback) => {
|
||||
FusionEvent.$on('getScreenshots', async (measuredData, callback) => {
|
||||
// var base64Str = await this.$refs['FUSION_AXIAL'].getScreenshots()
|
||||
if (!measuredData) {
|
||||
callback()
|
||||
|
@ -1017,7 +933,7 @@ export default {
|
|||
},
|
||||
debounce(callback, delay) {
|
||||
let timerId
|
||||
return function() {
|
||||
return function () {
|
||||
clearTimeout(timerId)
|
||||
timerId = setTimeout(() => {
|
||||
callback.apply(this, arguments)
|
||||
|
@ -1767,7 +1683,7 @@ export default {
|
|||
renderingEngine.getViewport(viewportIds.FUSION.AXIAL)
|
||||
)
|
||||
|
||||
viewport.setProperties({ colormap: { name: rgbPresetName }}, ptVolumeId)
|
||||
viewport.setProperties({ colormap: { name: rgbPresetName } }, ptVolumeId)
|
||||
// viewport.setProperties({ colormap: { name: rgbPresetName }}, ctVolumeId)
|
||||
viewport.render()
|
||||
// document.onselectstart = function() { return false }// 解决拖动会选中文字的问题
|
||||
|
@ -2263,42 +2179,49 @@ export default {
|
|||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.dicom-viewer-container{
|
||||
display:flex;
|
||||
.dicom-viewer-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
padding: 5px 2px;
|
||||
|
||||
::v-deep.el-dialog {
|
||||
background: #1e1e1e;
|
||||
border: 1px solid #ddd;
|
||||
color: #ddd;
|
||||
.el-dialog__title{
|
||||
color:#fff;
|
||||
|
||||
.el-dialog__title {
|
||||
color: #fff;
|
||||
}
|
||||
.el-input .el-input__inner{
|
||||
|
||||
.el-input .el-input__inner {
|
||||
background-color: transparent;
|
||||
color: #ddd;
|
||||
border: 1px solid #5e5e5e;
|
||||
}
|
||||
.el-input.is-disabled .el-input__inner{
|
||||
|
||||
.el-input.is-disabled .el-input__inner {
|
||||
background-color: #646464a1;
|
||||
}
|
||||
.el-form-item__label{
|
||||
|
||||
.el-form-item__label {
|
||||
color: #dfdfdf
|
||||
}
|
||||
}
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
background: #d0d0d0;
|
||||
}
|
||||
.dicom-tools{
|
||||
}
|
||||
|
||||
.dicom-tools {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 61px;
|
||||
|
@ -2308,24 +2231,27 @@ export default {
|
|||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
.tool-wrapper{
|
||||
|
||||
.tool-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 30px;
|
||||
.icon{
|
||||
|
||||
.icon {
|
||||
padding: 5px;
|
||||
border: 1px solid #404040;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
.svg-icon{
|
||||
font-size:25px;
|
||||
color:#ddd;
|
||||
|
||||
.svg-icon {
|
||||
font-size: 25px;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.text{
|
||||
.text {
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
|
@ -2333,51 +2259,63 @@ export default {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.tool_active{
|
||||
|
||||
.tool_active {
|
||||
background-color: #607d8b;
|
||||
}
|
||||
.tool_disabled{
|
||||
cursor:not-allowed
|
||||
|
||||
.tool_disabled {
|
||||
cursor: not-allowed
|
||||
}
|
||||
.icon:hover{
|
||||
|
||||
.icon:hover {
|
||||
background-color: #607d8b;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
.icon-content{
|
||||
|
||||
.icon-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #404040;
|
||||
}
|
||||
.text{
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
}
|
||||
.tool-icon{
|
||||
|
||||
.tool-icon {
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
.svg-icon{
|
||||
font-size:20px;
|
||||
color:#ddd;
|
||||
|
||||
.svg-icon {
|
||||
font-size: 20px;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-icon{
|
||||
.arrow-icon {
|
||||
cursor: pointer;
|
||||
padding: 7px 2px 7px 0px;
|
||||
}
|
||||
.arrow-icon:hover{
|
||||
|
||||
.arrow-icon:hover {
|
||||
background-color: #607d8b;
|
||||
}
|
||||
.icon-content-d:hover{
|
||||
|
||||
.icon-content-d:hover {
|
||||
background-color: #607d8b;
|
||||
}
|
||||
.tool-icon-d{
|
||||
|
||||
.tool-icon-d {
|
||||
padding: 5px;
|
||||
.svg-icon{
|
||||
font-size:20px;
|
||||
color:#ddd;
|
||||
|
||||
.svg-icon {
|
||||
font-size: 20px;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2388,31 +2326,36 @@ export default {
|
|||
position: absolute;
|
||||
background-color: #383838;
|
||||
color: #fff;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 9999;
|
||||
font-size: 12px;
|
||||
ul{
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
li{
|
||||
a{
|
||||
|
||||
li {
|
||||
a {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul li:hover{
|
||||
|
||||
ul li:hover {
|
||||
background-color: #727272;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.layout-content ul li{
|
||||
border-top:1px solid #ddd;
|
||||
border-left:1px solid #ddd;
|
||||
|
||||
.layout-content ul li {
|
||||
border-top: 1px solid #ddd;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
.layout-content ul .flex_row{
|
||||
|
||||
.layout-content ul .flex_row {
|
||||
// border: 1px solid #ddd;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -2421,44 +2364,51 @@ export default {
|
|||
// padding: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.layout-content ul .flex_column{
|
||||
|
||||
.layout-content ul .flex_column {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.layout_box_1_1{
|
||||
flex:1;
|
||||
|
||||
.layout_box_1_1 {
|
||||
flex: 1;
|
||||
// border: 1px solid #ddd;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
border-bottom:1px solid #ddd;
|
||||
border-right:1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
// padding: 0 5px;
|
||||
}
|
||||
.layout_box_1_2{
|
||||
flex:1;
|
||||
|
||||
.layout_box_1_2 {
|
||||
flex: 1;
|
||||
line-height: 15px;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
border-bottom:1px solid #ddd;
|
||||
border-right:1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
.layout-content li .layout_box_1_1 :last-child{
|
||||
|
||||
.layout-content li .layout_box_1_1 :last-child {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.layout-content li:hover {
|
||||
cursor: pointer;
|
||||
background-color: #727272;
|
||||
}
|
||||
.activeLi{
|
||||
|
||||
.activeLi {
|
||||
background-color: #919295;
|
||||
}
|
||||
|
||||
}
|
||||
.dicom-datas{
|
||||
}
|
||||
|
||||
.dicom-datas {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
margin-top: 5px;
|
||||
|
@ -2467,56 +2417,63 @@ export default {
|
|||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
.form-container{
|
||||
|
||||
.form-container {
|
||||
width: 350px;
|
||||
height: 100%;
|
||||
border: 1px solid #727272;
|
||||
}
|
||||
.dicom-container{
|
||||
|
||||
.dicom-container {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
border: 1px solid #727272;
|
||||
}
|
||||
|
||||
.measurement-container{
|
||||
.measurement-container {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.box{
|
||||
|
||||
.box {
|
||||
display: grid;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
.item{
|
||||
|
||||
.item {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
border: 1px solid rgba(255, 255, 255, 0.21);
|
||||
position: relative;
|
||||
&_active{
|
||||
|
||||
&_active {
|
||||
// border: 2px solid #ffeb3b;fff
|
||||
border: 1px dashed rgb(250, 250, 0);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.box_2_2{
|
||||
|
||||
.box_2_2 {
|
||||
grid-template-columns: repeat(2, 50%); //1列,占50%
|
||||
grid-template-rows: repeat(2, 50%); //1行,占50%
|
||||
}
|
||||
.box_1_1{
|
||||
|
||||
.box_1_1 {
|
||||
grid-template-columns: repeat(1, 100%);
|
||||
grid-template-rows: repeat(1, 100%);
|
||||
}
|
||||
|
||||
}
|
||||
.colorBar{
|
||||
::v-deep .el-input--mini .el-input__inner{
|
||||
}
|
||||
|
||||
.colorBar {
|
||||
::v-deep .el-input--mini .el-input__inner {
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color:#ddd;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="container" style="width:100%;height:100%" class="dicom-container">
|
||||
<!-- 访视阅片 -->
|
||||
<div v-if="readingCategory=== 1 && (CriterionType === 7 || ((CriterionType === 1 || CriterionType === 0) && readingVersionEnum === 1)) " class="reading-wrapper">
|
||||
<VisitReview />
|
||||
<VisitReview :readingTool="readingTool" />
|
||||
</div>
|
||||
<div v-else-if="(isReadingTaskViewInOrder === 1 || ((isReadingTaskViewInOrder !== 1) && isShow)) && readingCategory=== 1 && CriterionType !== 0" class="reading-wrapper">
|
||||
<el-tabs v-model="activeName" v-loading="loading" :before-leave="beforeLeave">
|
||||
|
@ -222,7 +222,7 @@ export default {
|
|||
isReadingTaskViewInOrder: null,
|
||||
firstTaskReadingCategory: null,
|
||||
criterionType: null,
|
||||
readingTool: null,
|
||||
readingTool: 0,
|
||||
dialogVisible: false,
|
||||
TrialReadingCriterionId: null,
|
||||
isNewSubject: null,
|
||||
|
@ -272,7 +272,7 @@ export default {
|
|||
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
|
||||
// this.isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
|
||||
// this.criterionType = this.$router.currentRoute.query.criterionType
|
||||
// this.readingTool = this.$router.currentRoute.query.readingTool
|
||||
this.readingTool = this.$router.currentRoute.query.readingTool ? Number(this.$router.currentRoute.query.readingTool) : 0
|
||||
this.TrialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
// this.isNewSubject = this.$router.currentRoute.query.isNewSubject
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,780 @@
|
|||
<template>
|
||||
<div ref="viewport" class="viewport-wrapper" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
|
||||
@mouseleave="sliderMouseleave">
|
||||
<div v-if="series && taskInfo" class="left-top-text">
|
||||
<div v-if="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(series.TaskInfo.VisitTaskId)" />
|
||||
</div>
|
||||
<h2 v-if="taskInfo.IsReadingShowSubjectInfo && series.TaskInfo" class="subject-info">
|
||||
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
|
||||
</h2>
|
||||
<div>Series: #{{ series.SeriesNumber }}</div>
|
||||
<div v-if="series.Stack">Image: #{{ `${series.SliceIndex + 1}/${series.Stack.length}` }}</div>
|
||||
<div>{{ series.Modality }}</div>
|
||||
</div>
|
||||
<div v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1 && series.TaskInfo"
|
||||
class="top-center-tool">
|
||||
<div class="toggle-visit-container">
|
||||
<div class="arrw_icon"
|
||||
:style="{ cursor: series.TaskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: series.TaskInfo.VisitTaskNum !== 0 ? '#fff' : '#6b6b6b' }"
|
||||
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, -1)"
|
||||
@dblclick.stop="preventDefault($event)">
|
||||
<i class="el-icon-caret-left" />
|
||||
</div>
|
||||
<div class="arrow_text">
|
||||
{{ series.TaskInfo.TaskBlindName }}
|
||||
</div>
|
||||
<div class="arrw_icon"
|
||||
:style="{ cursor: series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff' : '#6b6b6b' }"
|
||||
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, 1)"
|
||||
@dblclick.stop="preventDefault($event)">
|
||||
<i class="el-icon-caret-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="series" class="right-top-text">
|
||||
<div>{{ series.Description }}</div>
|
||||
</div>
|
||||
<div v-if="series" class="left-bottom-text">
|
||||
<div v-show="mousePosition.index.length > 0">
|
||||
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
|
||||
</div>
|
||||
<div
|
||||
v-if="(series.Modality === 'CT' || series.Modality === 'DR' || series.Modality === 'CR') && mousePosition.value">
|
||||
HU: {{ mousePosition.value }}
|
||||
</div>
|
||||
<div v-else-if="(series.Modality === 'PT' && mousePosition.value)">
|
||||
SUVbw(g/ml): {{ digitPlaces === -1 ? mousePosition.value.toFixed(3) : mousePosition.value.toFixed(digitPlaces)
|
||||
}}
|
||||
</div>
|
||||
<div v-else-if="mousePosition.value">
|
||||
Density: {{ mousePosition.value }}
|
||||
</div>
|
||||
<div v-show="imageInfo.size">
|
||||
W*H: {{ imageInfo.size }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="series" class="right-bottom-text">
|
||||
<div v-show="imageInfo.location">Location: {{ `${Number(imageInfo.location).toFixed(digitPlaces)} mm` }}</div>
|
||||
<div v-show="series.SliceThickness">Slice Thickness: {{ `${Number(series.SliceThickness).toFixed(digitPlaces)} mm`
|
||||
}}</div>
|
||||
<div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
|
||||
</div>
|
||||
<div class="orientation-top">
|
||||
{{ markers.top }}
|
||||
</div>
|
||||
<div class="orientation-right">
|
||||
{{ markers.right }}
|
||||
</div>
|
||||
|
||||
<div class="orientation-bottom">
|
||||
{{ markers.bottom }}
|
||||
</div>
|
||||
<div class="orientation-left">
|
||||
{{ markers.left }}
|
||||
</div>
|
||||
<div v-if="isFusion" class="color_bar">
|
||||
<canvas id="colorBar_Canvas" />
|
||||
<!-- <div id="colorBar_Canvas" /> -->
|
||||
</div>
|
||||
<div ref="sliderBox" class="right-slider-box" @click.stop="clickSlider($event)">
|
||||
<div :style="{ top: sliderInfo.height + '%' }" class="slider" @click.stop.prevent="() => { return }"
|
||||
@mousedown.stop="sliderMousedown($event)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import * as cornerstonejs from '@cornerstonejs/core'
|
||||
import {
|
||||
// RenderingEngine,
|
||||
Enums,
|
||||
// imageLoader,
|
||||
metaData,
|
||||
getRenderingEngine,
|
||||
volumeLoader,
|
||||
// eventTarget,
|
||||
utilities as csUtils
|
||||
} from '@cornerstonejs/core'
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||||
import { utilities as cstUtils } from '@cornerstonejs/tools';
|
||||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
|
||||
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
|
||||
const {
|
||||
ToolGroupManager,
|
||||
MIPJumpToClickTool,
|
||||
VolumeRotateTool,
|
||||
Enums: csToolsEnums,
|
||||
} = cornerstoneTools
|
||||
const { BlendModes, OrientationAxis } = Enums;
|
||||
const { MouseBindings } = csToolsEnums
|
||||
const { getColormap, getColormapNames } = csUtils.colormap;
|
||||
const { ViewportColorbar } = cstUtils.voi.colorbar;
|
||||
const { ColorbarRangeTextPosition } = cstUtils.voi.colorbar.Enums;
|
||||
import { vec3 } from 'gl-matrix'
|
||||
export default {
|
||||
name: 'ImageViewport',
|
||||
props: {
|
||||
renderingEngineId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
viewportId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
viewportIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
element: '',
|
||||
series: {},
|
||||
ctSeries: {},
|
||||
petSeries: {},
|
||||
isFusion: false,
|
||||
isMip: false,
|
||||
taskInfo: null,
|
||||
sliderInfo: {
|
||||
oldB: null,
|
||||
oldM: null,
|
||||
isMove: false,
|
||||
height: 0
|
||||
},
|
||||
mousePosition: {
|
||||
index: [],
|
||||
value: null,
|
||||
modalityUnit: '',
|
||||
world: []
|
||||
},
|
||||
imageInfo: {
|
||||
zoom: null,
|
||||
size: null,
|
||||
location: null,
|
||||
sliceThickness: null,
|
||||
wwwc: null
|
||||
},
|
||||
digitPlaces: 2,
|
||||
orientationMarkers: [],
|
||||
originalMarkers: [],
|
||||
markers: { top: '', right: '', bottom: '', left: '' },
|
||||
playClipState: false,
|
||||
wwwcIdx: 2,
|
||||
presetName: '',
|
||||
volumeId: null,
|
||||
defaultWindowLevel: {},
|
||||
Colorbar: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
|
||||
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
|
||||
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
|
||||
this.$nextTick(() => {
|
||||
this.initViewport()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
initViewport() {
|
||||
this.element = this.$refs['viewport']
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
if (renderingEngine) {
|
||||
renderingEngine.resize(true, false)
|
||||
}
|
||||
})
|
||||
this.element.oncontextmenu = (e) => e.preventDefault()
|
||||
resizeObserver.observe(this.element)
|
||||
this.element.addEventListener("CORNERSTONE_VOLUME_NEW_IMAGE", this.stackNewImage)
|
||||
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
|
||||
|
||||
this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
|
||||
this.element.addEventListener('mouseleave', () => {
|
||||
this.mousePosition.index = []
|
||||
})
|
||||
// console.log(cornerstoneTools)
|
||||
// element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
|
||||
},
|
||||
stackNewImage(e) {
|
||||
const { detail } = e
|
||||
this.series.SliceIndex = detail.imageIndex
|
||||
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const zoom = viewport.getZoom()
|
||||
this.imageInfo.zoom = zoom.toFixed(4)
|
||||
let imageId = viewport.getCurrentImageId()
|
||||
if (imageId) {
|
||||
const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
|
||||
this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
|
||||
this.imageInfo.location = imagePlaneModule.sliceLocation
|
||||
this.getOrientationMarker()
|
||||
this.$emit('renderAnnotations', this.series)
|
||||
}
|
||||
|
||||
},
|
||||
voiModified(e) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const properties = viewport.getProperties()
|
||||
if (properties && properties.voiRange) {
|
||||
var { lower, upper } = properties.voiRange
|
||||
const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
|
||||
lower,
|
||||
upper
|
||||
)
|
||||
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
|
||||
}
|
||||
},
|
||||
getOrientationMarker() {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const { viewUp, viewPlaneNormal } = viewport.getCamera()
|
||||
|
||||
const viewRight = vec3.create()
|
||||
vec3.cross(viewRight, viewUp, viewPlaneNormal)
|
||||
|
||||
const columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]]
|
||||
const rowCosines = viewRight
|
||||
const rowString = cornerstoneTools.utilities.orientation.getOrientationStringLPS(rowCosines)
|
||||
const columnString = cornerstoneTools.utilities.orientation.getOrientationStringLPS(columnCosines)
|
||||
const oppositeRowString = cornerstoneTools.utilities.orientation.invertOrientationStringLPS(rowString)
|
||||
const oppositeColumnString = cornerstoneTools.utilities.orientation.invertOrientationStringLPS(columnString)
|
||||
this.markers.top = oppositeColumnString
|
||||
this.markers.right = rowString
|
||||
this.markers.bottom = columnString
|
||||
this.markers.left = oppositeRowString
|
||||
this.orientationMarkers = [oppositeColumnString, rowString, columnString, oppositeRowString]
|
||||
if (this.originalMarkers.length === 0) {
|
||||
this.originalMarkers = [...this.orientationMarkers]
|
||||
}
|
||||
},
|
||||
setMarkers() {
|
||||
const markers = [...this.orientationMarkers]
|
||||
for (const key in this.markers) {
|
||||
const v = markers.shift(0)
|
||||
this.markers[key] = v
|
||||
}
|
||||
},
|
||||
resetOrientationMarkers() {
|
||||
if (this.originalMarkers.length > 0) {
|
||||
console.log(this.originalMarkers)
|
||||
this.orientationMarkers = [...this.originalMarkers]
|
||||
this.setMarkers()
|
||||
}
|
||||
},
|
||||
rotateOrientationMarkers(type) {
|
||||
if (this.orientationMarkers.length > 0) {
|
||||
if (type === 1) {
|
||||
this.resetOrientationMarkers()
|
||||
return
|
||||
}
|
||||
const markers = [...this.orientationMarkers]
|
||||
if (type === 2) {
|
||||
// 垂直翻转
|
||||
this.orientationMarkers[0] = markers[2]
|
||||
this.orientationMarkers[2] = markers[0]
|
||||
} else if (type === 3) {
|
||||
// 水平翻转
|
||||
this.orientationMarkers[1] = markers[3]
|
||||
this.orientationMarkers[3] = markers[1]
|
||||
} else if (type === 4) {
|
||||
// 左转90度
|
||||
this.orientationMarkers = markers.slice(1, 4).concat(markers[0])
|
||||
} else if (type === 5) {
|
||||
// 右转90度
|
||||
this.orientationMarkers = [markers[3]].concat(markers.slice(0, 3))
|
||||
}
|
||||
this.setMarkers()
|
||||
}
|
||||
},
|
||||
toggleClipPlay(isPlay, framesPerSecond) {
|
||||
this.playClipState = isPlay
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
||||
if (isPlay) {
|
||||
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: false })
|
||||
} else {
|
||||
cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
}
|
||||
},
|
||||
scrollPage(type) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
|
||||
const numImages = viewport.getImageIds().length
|
||||
let newImageIdIndex = null
|
||||
if (type === 0) {
|
||||
newImageIdIndex = 0
|
||||
} else if (type === -1) {
|
||||
newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1
|
||||
} else if (type === 1) {
|
||||
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
|
||||
} else if (type === 99999) {
|
||||
newImageIdIndex = numImages - 1
|
||||
}
|
||||
// viewport.setImageIdIndex(newImageIdIndex)
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: newImageIdIndex })
|
||||
cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
},
|
||||
setZoom(ratio) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
||||
const zoom = viewport.getZoom()
|
||||
if (ratio > 0) {
|
||||
viewport.setZoom(zoom * 1.05)
|
||||
} else {
|
||||
viewport.setZoom(zoom / 1.05)
|
||||
}
|
||||
viewport.render()
|
||||
},
|
||||
resize(forceFitToWindow) {
|
||||
console.log('resize: ', forceFitToWindow)
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
renderingEngine.resize(true, forceFitToWindow)
|
||||
},
|
||||
voiChange(v) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const voiRange = { lower: 0, upper: v }
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
if (!viewport) return
|
||||
const viewportsContainingVolumeUID = csUtils.getViewportsWithVolumeId(
|
||||
this.volumeId,
|
||||
viewport.renderingEngineId
|
||||
)
|
||||
|
||||
viewport.setProperties({ voiRange }, this.volumeId)
|
||||
viewportsContainingVolumeUID.forEach((vp) => {
|
||||
vp.render()
|
||||
// this.$refs[vp.id].voiModified()
|
||||
this.voiModified()
|
||||
})
|
||||
},
|
||||
renderColorBar(presetName) {
|
||||
var colorMap = null
|
||||
this.Colorbar = null
|
||||
if (presetName === 'hsv') {
|
||||
colorMap = vtkColorMaps.getPresetByName(presetName)
|
||||
} else {
|
||||
colorMap = getColormap(presetName)
|
||||
}
|
||||
// let colormaps = getColormapNames().map(name => getColormap(name))
|
||||
// console.log(colormaps)
|
||||
// const canvas = document.getElementById('colorBar_Canvas')
|
||||
// this.Colorbar = new ViewportColorbar({
|
||||
// id: this.volumeId,
|
||||
// element: this.element,
|
||||
// container: canvas,
|
||||
// colormaps,
|
||||
// activeColormapName: presetName,
|
||||
// ticks: {
|
||||
// position: ColorbarRangeTextPosition.Left,
|
||||
// style: {
|
||||
// font: '12px Arial',
|
||||
// color: '#fff',
|
||||
// maxNumTicks: 8,
|
||||
// tickSize: 5,
|
||||
// tickWidth: 1,
|
||||
// labelMargin: 3,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
const rgbPoints = colorMap.RGBPoints
|
||||
const canvas = document.getElementById('colorBar_Canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
const canvasWidth = 160
|
||||
const canvasHeight = 5
|
||||
const rectWidth = 160
|
||||
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)
|
||||
},
|
||||
setPreset(presetName) {
|
||||
this.presetName = presetName
|
||||
},
|
||||
setColorMap(presetName) {
|
||||
if (this.Colorbar) {
|
||||
this.Colorbar.activeColormapName = presetName;
|
||||
}
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
viewport.setProperties({ colormap: { name: presetName } }, this.volumeId)
|
||||
viewport.render()
|
||||
},
|
||||
async prefetchMetadataInformation(imageIdsToPrefetch) {
|
||||
let taskPromises = []
|
||||
for (let i = 0; i < imageIdsToPrefetch.length; i++) {
|
||||
taskPromises.push(cornerstoneDICOMImageLoader.wadouri.loadImage(imageIdsToPrefetch[i]).promise)
|
||||
if (taskPromises.length >= 6 || i === imageIdsToPrefetch.length - 1) {
|
||||
await Promise.all(taskPromises)
|
||||
taskPromises = []
|
||||
}
|
||||
}
|
||||
},
|
||||
async setSeriesInfo(obj, isLocate = false, option = {}) {
|
||||
try {
|
||||
if (this.series && obj.Id === this.series.Id && obj.Description === this.series.Description && !isLocate) {
|
||||
obj.SliceIndex = this.series.SliceIndex
|
||||
}
|
||||
if (this.series) {
|
||||
obj.cellType = this.series.cellType
|
||||
}
|
||||
this.series = {}
|
||||
let { isFusion, isMip } = 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 = { ...ct }
|
||||
this.ctSeries = { ...ct }
|
||||
this.petSeries = { ...pet }
|
||||
await this.prefetchMetadataInformation(ct.ImageIds)
|
||||
await this.prefetchMetadataInformation(pet.ImageIds)
|
||||
let ctVolumeId =
|
||||
'fusion_ct' + ':' + csUtils.uuidv4()
|
||||
let ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, { imageIds: ct.ImageIds })
|
||||
this.volumeId =
|
||||
'fusion_ct' + ':' + csUtils.uuidv4()
|
||||
let petVolume = await volumeLoader.createAndCacheVolume(this.volumeId, { imageIds: pet.ImageIds })
|
||||
ctVolume.load();
|
||||
petVolume.load();
|
||||
viewport
|
||||
.setVolumes([{ volumeId: ctVolumeId, callback: () => { console.log("融合ct渲染成功") } }, {
|
||||
volumeId: this.volumeId, callback: () => {
|
||||
console.log("融合pet渲染成功");
|
||||
}
|
||||
}]).then(() => {
|
||||
this.setColorMap(this.presetName)
|
||||
})
|
||||
|
||||
} else {
|
||||
this.series = { ...obj }
|
||||
await this.prefetchMetadataInformation(obj.ImageIds)
|
||||
this.volumeId =
|
||||
'volume' + ':' + csUtils.uuidv4()
|
||||
let volume = await volumeLoader.createAndCacheVolume(this.volumeId, { imageIds: obj.ImageIds })
|
||||
|
||||
volume.load();
|
||||
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
|
||||
},
|
||||
],
|
||||
})
|
||||
const ptVolumeDimensions = volume.dimensions;
|
||||
const slabThickness = Math.sqrt(
|
||||
ptVolumeDimensions[0] * ptVolumeDimensions[0] +
|
||||
ptVolumeDimensions[1] * ptVolumeDimensions[1] +
|
||||
ptVolumeDimensions[2] * ptVolumeDimensions[2]
|
||||
);
|
||||
viewport
|
||||
.setVolumes([{
|
||||
volumeId: this.volumeId,
|
||||
callback: () => { console.log("mip渲染成功") },
|
||||
slabThickness,
|
||||
blendMode: BlendModes.MAXIMUM_INTENSITY_BLEND,
|
||||
defaultOptions: {
|
||||
orientation: OrientationAxis.CORONAL
|
||||
}
|
||||
}])
|
||||
} else {
|
||||
viewport
|
||||
.setVolumes([{ volumeId: this.volumeId, callback: () => { console.log("渲染成功") } }])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
},
|
||||
cornerstoneToolsMouseMove(e) {
|
||||
const { currentPoints } = e.detail
|
||||
const worldPoint = currentPoints.world
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const imageData = viewport.getImageData()
|
||||
if (!imageData) return
|
||||
const index = imageData.imageData.worldToIndex(worldPoint)
|
||||
index[0] = Math.floor(index[0])
|
||||
index[1] = Math.floor(index[1])
|
||||
index[2] = Math.floor(index[2])
|
||||
this.mousePosition.index = index
|
||||
},
|
||||
toggleTask(evt, visitTaskNum, i) {
|
||||
this.$emit('activeViewport', this.viewportIndex)
|
||||
const num = visitTaskNum + i
|
||||
if (num >= 0 && num <= this.taskInfo.VisitNum) {
|
||||
this.$emit('toggleTaskByViewport', { series: this.series, visitTaskNum: num })
|
||||
}
|
||||
|
||||
evt.stopImmediatePropagation()
|
||||
evt.stopPropagation()
|
||||
evt.preventDefault()
|
||||
},
|
||||
viewCD(taskId) {
|
||||
this.$emit('previewCD', taskId)
|
||||
},
|
||||
setWwwcIdx(idx) {
|
||||
this.wwwcIdx = idx
|
||||
},
|
||||
clickSlider(e) {
|
||||
const height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
|
||||
this.sliderInfo.height = height
|
||||
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
|
||||
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(
|
||||
this.viewportId
|
||||
)
|
||||
// viewport.setImageIdIndex(sliceIdx)
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
|
||||
viewport.render()
|
||||
},
|
||||
sliderMouseup(e) {
|
||||
this.sliderInfo.isMove = false
|
||||
},
|
||||
sliderMousedown(e) {
|
||||
const boxHeight = this.$refs['sliderBox'].clientHeight
|
||||
this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
|
||||
this.sliderInfo.oldM = e.clientY
|
||||
this.sliderInfo.isMove = true
|
||||
e.stopImmediatePropagation()
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
},
|
||||
sliderMousemove(e) {
|
||||
if (!this.sliderInfo.isMove) return
|
||||
const delta = this.sliderInfo.oldB - (this.sliderInfo.oldM - e.clientY)
|
||||
const boxHeight = this.$refs['sliderBox'].clientHeight
|
||||
if (delta < 0) return
|
||||
if (delta > boxHeight) return
|
||||
const height = delta * 100 / boxHeight
|
||||
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
|
||||
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
|
||||
this.sliderInfo.height = height
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(
|
||||
this.viewportId
|
||||
)
|
||||
// viewport.setImageIdIndex(sliceIdx)
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
|
||||
viewport.render()
|
||||
},
|
||||
sliderMouseleave(e) {
|
||||
if (!this.sliderInfo.isMove) return
|
||||
this.sliderInfo.isMove = false
|
||||
},
|
||||
preventDefault(e) {
|
||||
e.stopImmediatePropagation()
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.viewport-wrapper {
|
||||
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-top-text {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
color: #ddd;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.left-bottom-text {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
bottom: 5px;
|
||||
color: #ddd;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.right-bottom-text {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
color: #ddd;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.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
|
||||
}
|
||||
|
||||
.orientation-top {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 30px;
|
||||
color: #f44336;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orientation-bottom {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 15px;
|
||||
color: #f44336;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orientation-left {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 15px;
|
||||
color: #f44336;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orientation-right {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
color: #f44336;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.color_bar {
|
||||
|
||||
position: absolute;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: right;
|
||||
left: -150px;
|
||||
top: 30%;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
|
@ -1,36 +1,25 @@
|
|||
<template>
|
||||
<div class="visit-review-container">
|
||||
<el-tabs
|
||||
v-model="activeName"
|
||||
>
|
||||
<el-tabs v-model="activeName">
|
||||
<!-- 阅片 -->
|
||||
<el-tab-pane
|
||||
v-if="taskInfo"
|
||||
:label="$t('trials:reading:tabTitle:review')"
|
||||
name="read"
|
||||
>
|
||||
<read-page ref="readPage" />
|
||||
<el-tab-pane v-if="taskInfo" :label="$t('trials:reading:tabTitle:review')" name="read">
|
||||
<read-page ref="readPage" v-if="!readingTool" />
|
||||
<PetCtReadPage v-else-if="readingTool === 2" />
|
||||
</el-tab-pane>
|
||||
<!-- 报告 -->
|
||||
<el-tab-pane
|
||||
v-if="taskInfo && !taskInfo.IseCRFShowInDicomReading"
|
||||
:label="$t('trials:reading:tabTitle:report')"
|
||||
name="report"
|
||||
>
|
||||
<report-page
|
||||
v-if="activeName === 'report' && taskInfo.CriterionType !== 0"
|
||||
@setReadingTaskState="setReadingTaskState"
|
||||
/>
|
||||
<customize-report-page
|
||||
v-if="activeName === 'report' && taskInfo.CriterionType === 0"
|
||||
@setReadingTaskState="setReadingTaskState"
|
||||
/>
|
||||
<el-tab-pane v-if="taskInfo && !taskInfo.IseCRFShowInDicomReading" :label="$t('trials:reading:tabTitle:report')"
|
||||
name="report">
|
||||
<report-page v-if="activeName === 'report' && taskInfo.CriterionType !== 0"
|
||||
@setReadingTaskState="setReadingTaskState" />
|
||||
<customize-report-page v-if="activeName === 'report' && taskInfo.CriterionType === 0"
|
||||
@setReadingTaskState="setReadingTaskState" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import ReadPage from './ReadPage'
|
||||
import PetCtReadPage from './PetCtReadPage'
|
||||
import ReportPage from './ReportPage'
|
||||
import CustomizeReportPage from './customize/ReportPage'
|
||||
export default {
|
||||
|
@ -38,8 +27,15 @@ export default {
|
|||
components: {
|
||||
ReadPage,
|
||||
ReportPage,
|
||||
PetCtReadPage,
|
||||
CustomizeReportPage
|
||||
},
|
||||
props: {
|
||||
readingTool: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeName: 'read',
|
||||
|
@ -57,37 +53,44 @@ export default {
|
|||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.visit-review-container {
|
||||
.visit-review-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #000;
|
||||
padding: 5px;
|
||||
|
||||
::v-deep .el-tabs {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-tabs__item {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
.el-tabs__item:hover {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
.el-tabs__header {
|
||||
height: 50px;
|
||||
margin:0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
flex: 1;
|
||||
margin:0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -261,7 +261,15 @@ const config = {
|
|||
'i18nKey': 'trials:reading:button:arrowAnnotate',
|
||||
'isDisabled': false,
|
||||
'disabledReason': ''
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': '椭圆工具',
|
||||
'icon': 'oval',
|
||||
'toolName': 'CircleROI',
|
||||
'i18nKey': 'trials:reading:button:Circle',
|
||||
'isDisabled': false,
|
||||
'disabledReason': ''
|
||||
},
|
||||
]
|
||||
}
|
||||
const getTools = (criterionType) => {
|
||||
|
@ -269,6 +277,7 @@ const getTools = (criterionType) => {
|
|||
return standard?.tools || []
|
||||
}
|
||||
const getCustomizeStandardsTools = (toolNames) => {
|
||||
if(toolNames.length<=0) return config.customizeStandards
|
||||
const filteredTools = config.customizeStandards.filter(item => toolNames.includes(item.toolName))
|
||||
return filteredTools || []
|
||||
}
|
||||
|
|
|
@ -436,7 +436,7 @@ export default {
|
|||
)
|
||||
var token = getToken()
|
||||
var path = ''
|
||||
if (this.readingTool === 0) {
|
||||
if (this.readingTool === 0 || this.readingTool === 2) {
|
||||
if (this.criterionType === 0 && this.trialId === '08dd28b3-6843-fc05-0242-ac1301000000') {
|
||||
path = `/fusion?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&studyId=62b3dfc4-1e04-4180-910d-fe595f398361&ctseriesId=1bd24f53-d419-32e5-92d4-2b04640aaa65&ptseriesId=2b7b128d-8c3f-8357-ad14-e38f3acbbdff&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&TokenKey=${token}&lang=${this.$i18n.locale}`
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue