suv融合
continuous-integration/drone/push Build is passing Details

uat
wangxiaoshuang 2025-04-27 17:04:54 +08:00
parent 18f23d2bba
commit 8d6ae3a49f
8 changed files with 4219 additions and 441 deletions

View File

@ -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'

View File

@ -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>
@ -2263,41 +2179,48 @@ export default {
}
</script>
<style lang="scss" scoped>
.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-input .el-input__inner {
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
.el-input.is-disabled .el-input__inner {
background-color: #646464a1;
}
.el-form-item__label {
color: #dfdfdf
}
}
}
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.dicom-tools {
box-sizing: border-box;
width: 100%;
@ -2308,17 +2231,20 @@ export default {
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: 25px;
color: #ddd;
@ -2333,30 +2259,38 @@ export default {
display: none;
}
}
.tool_active {
background-color: #607d8b;
}
.tool_disabled {
cursor: not-allowed
}
.icon:hover {
background-color: #607d8b;
}
.dropdown {
position: relative;
display: inline-block;
.icon-content {
display: flex;
align-items: center;
border: 1px solid #404040;
}
.text {
text-align: center;
}
.tool-icon {
padding: 5px;
cursor: pointer;
text-align: center;
.svg-icon {
font-size: 20px;
color: #ddd;
@ -2367,14 +2301,18 @@ export default {
cursor: pointer;
padding: 7px 2px 7px 0px;
}
.arrow-icon:hover {
background-color: #607d8b;
}
.icon-content-d:hover {
background-color: #607d8b;
}
.tool-icon-d {
padding: 5px;
.svg-icon {
font-size: 20px;
color: #ddd;
@ -2391,11 +2329,13 @@ export default {
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 9999;
font-size: 12px;
ul {
list-style: none;
margin: 0;
padding: 0;
text-align: center;
li {
a {
display: block;
@ -2403,15 +2343,18 @@ export default {
}
}
}
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 .flex_row {
// border: 1px solid #ddd;
display: flex;
@ -2421,6 +2364,7 @@ export default {
// padding: 2px;
margin-bottom: 2px;
}
.layout-content ul .flex_column {
display: flex;
justify-content: space-between;
@ -2428,6 +2372,7 @@ export default {
align-items: center;
margin-bottom: 2px;
}
.layout_box_1_1 {
flex: 1;
// border: 1px solid #ddd;
@ -2438,6 +2383,7 @@ export default {
border-right: 1px solid #ddd;
// padding: 0 5px;
}
.layout_box_1_2 {
flex: 1;
line-height: 15px;
@ -2446,18 +2392,22 @@ export default {
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd;
}
.layout-content li .layout_box_1_1 :last-child {
color: red;
}
.layout-content li:hover {
cursor: pointer;
background-color: #727272;
}
.activeLi {
background-color: #919295;
}
}
.dicom-datas {
box-sizing: border-box;
flex: 1;
@ -2467,11 +2417,13 @@ export default {
flex-direction: row;
justify-content: flex-start;
overflow: hidden;
.form-container {
width: 350px;
height: 100%;
border: 1px solid #727272;
}
.dicom-container {
box-sizing: border-box;
flex: 1;
@ -2482,16 +2434,19 @@ export default {
.measurement-container {
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: 2px solid #ffeb3b;fff
border: 1px dashed rgb(250, 250, 0);
@ -2499,16 +2454,19 @@ export default {
}
}
}
.box_2_2 {
grid-template-columns: repeat(2, 50%); //150%
grid-template-rows: repeat(2, 50%); //150%
}
.box_1_1 {
grid-template-columns: repeat(1, 100%);
grid-template-rows: repeat(1, 100%);
}
}
.colorBar {
::v-deep .el-input--mini .el-input__inner {
height: 25px;
@ -2518,5 +2476,4 @@ export default {
color: #ddd;
}
}
</style>

View File

@ -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

View File

@ -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>

View File

@ -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',
@ -63,27 +59,34 @@ export default {
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;
}
.el-tabs__content {
flex: 1;
margin: 0px;
}
.el-tab-pane {
height: 100%;
}

View File

@ -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 || []
}

View File

@ -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 {