2539 lines
94 KiB
Vue
2539 lines
94 KiB
Vue
<template>
|
||
<div v-loading="loading" class="read-page-container">
|
||
<!-- 检查列表 -->
|
||
<div class="left-panel">
|
||
<div class="task-container">
|
||
<div class="task-info">
|
||
<div
|
||
v-for="(s, index) in visitTaskList"
|
||
:key="s.VisitTaskId"
|
||
class="task-item"
|
||
:class="{'task-item-active': activeTaskId==s.VisitTaskId}"
|
||
|
||
@click.prevent="toggleTask(s, index)"
|
||
>{{ s.TaskBlindName }}</div>
|
||
</div>
|
||
</div>
|
||
<div v-loading="sLoading" class="study-info">
|
||
<div
|
||
v-for="s in visitTaskList"
|
||
v-show="activeTaskId === s.VisitTaskId"
|
||
:key="s.VisitTaskId"
|
||
style="height:100%;"
|
||
>
|
||
<study-list
|
||
v-if="selectArr.includes(s.VisitTaskId) && s.StudyList.length > 0"
|
||
:ref="s.VisitTaskId"
|
||
:visit-task-info="s"
|
||
:markedSeriesIds="markedSeriesIds"
|
||
@activeSeries="activeSeries"
|
||
@showMultiFrame="showMultiFrame"
|
||
/>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 图像 -->
|
||
<div class="middle-panel">
|
||
<div class="dicom-viewer">
|
||
<!-- tools -->
|
||
<div class="tools-wrapper">
|
||
<div class="tools-left">
|
||
<!-- 布局 -->
|
||
<div
|
||
class="tool-item"
|
||
:title="$t('trials:reading:button:layout')"
|
||
@click.stop="showPanel($event)"
|
||
@mouseleave="toolMouseout"
|
||
>
|
||
<div class="dropdown">
|
||
<div class="icon">
|
||
<svg-icon icon-class="layout" class="svg-icon" />
|
||
<i class="el-icon-arrow-down" style="color:#fff;" />
|
||
</div>
|
||
<div class="dropdown-content layout-content">
|
||
<ul style="width:50px" class="layout-content-ul">
|
||
<li class="layout_flex_row" @click.stop="changeLayout(1)">
|
||
<div class="layout_box_1_1">
|
||
A
|
||
</div>
|
||
</li>
|
||
<li class="layout_flex_row" @click.stop="changeLayout(2)">
|
||
<div class="layout_box_1_1">
|
||
A
|
||
</div>
|
||
<div class="layout_box_1_1">
|
||
A
|
||
</div>
|
||
</li>
|
||
<li v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder === 1" class="layout_flex_row" @click.stop="changeLayout(3)">
|
||
<div class="layout_box_1_1">
|
||
A
|
||
</div>
|
||
<div class="layout_box_1_1">
|
||
B
|
||
</div>
|
||
</li>
|
||
|
||
<li class="layout_flex_column" @click.stop="changeLayout(4)">
|
||
|
||
<div style="flex:1;display: flex;width:100%;">
|
||
<div class="layout_box_1_2">
|
||
A
|
||
</div>
|
||
<div class="layout_box_1_2">
|
||
A
|
||
</div>
|
||
</div>
|
||
<div style="flex:1;display: flex;width:100%;">
|
||
<div class="layout_box_1_2">
|
||
A
|
||
</div>
|
||
<div class="layout_box_1_2">
|
||
A
|
||
</div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 调窗 -->
|
||
<div
|
||
:class="['tool-item', activeTool === 'WindowLevel' ? 'tool-item-active' : '']"
|
||
:title="$t('trials:reading:button:wwwc')"
|
||
@click.stop="setWindowLevelActive($event)"
|
||
@mouseleave="toolMouseout"
|
||
>
|
||
<div class="dropdown">
|
||
<div class="icon">
|
||
<svg-icon icon-class="reverse" class="svg-icon" />
|
||
<i class="el-icon-arrow-down" style="color:#fff;" />
|
||
</div>
|
||
<div class="dropdown-content">
|
||
<ul style="width:165px;">
|
||
<li v-for="item in wwwcArr" :key="item.label">
|
||
<a href="#" @click.stop="changeVoiRange(item)">
|
||
<div v-if="item.wc !== null" style="display:flex;flex-direction: row;justify-content: space-between;">
|
||
<div>{{ item.label }}</div>
|
||
<div>{{ `${item.ww} / ${item.wc}` }}</div>
|
||
</div>
|
||
<div v-else style="text-align:left;">
|
||
{{ item.label }}
|
||
</div>
|
||
</a>
|
||
<el-divider v-if="item.val === 1" class="divider" content-position="center">
|
||
{{ ` ${$t('trials:reading:title:preset') }` }}
|
||
</el-divider>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 反色 -->
|
||
<div
|
||
class="tool-item"
|
||
:title="$t('trials:reading:button:reverseColor')"
|
||
@click.prevent="toggleInvert"
|
||
>
|
||
<svg-icon icon-class="reversecolor" class="svg-icon" />
|
||
</div>
|
||
<!-- 缩放 -->
|
||
<div
|
||
:class="['tool-item', activeTool === 'Zoom' ? 'tool-item-active' : '']"
|
||
:title="$t('trials:reading:button:zoom')"
|
||
@click.prevent="setToolActive('Zoom')"
|
||
>
|
||
<svg-icon icon-class="magnifier" class="svg-icon" />
|
||
</div>
|
||
<!-- 移动 -->
|
||
<div
|
||
:class="['tool-item', activeTool === 'Pan' ? 'tool-item-active' : '']"
|
||
:title="$t('trials:reading:button:move')"
|
||
@click.prevent="setToolActive('Pan')"
|
||
>
|
||
<svg-icon icon-class="move" class="svg-icon" />
|
||
</div>
|
||
<!-- 旋转 -->
|
||
<!-- <div
|
||
:class="['tool-item', activeTool === 'PlanarRotate' ? 'tool-item-active' : '']"
|
||
:title="$t('trials:reading:button:rotate')"
|
||
@click.prevent="setToolActive('PlanarRotate')"
|
||
>
|
||
<svg-icon icon-class="rotate" class="svg-icon" />
|
||
</div> -->
|
||
<div
|
||
class="tool-item"
|
||
:title="$t('trials:reading:button:rotate')"
|
||
@click.stop="showPanel($event)"
|
||
@mouseleave="toolMouseout"
|
||
>
|
||
<div class="dropdown">
|
||
<div
|
||
class="icon"
|
||
data-tool="Rotate"
|
||
:class="[activeTool==='Rotate'?'tool_active':'']"
|
||
>
|
||
<svg-icon icon-class="rotate" class="svg-icon" />
|
||
<i class="el-icon-arrow-down" style="color:#fff;" />
|
||
</div>
|
||
<div class="dropdown-content">
|
||
<ul style="width:100px;">
|
||
<li v-for="rotate in rotateOptions" :key="rotate.label" style="text-align:left;">
|
||
<a href="#" @click.prevent="setViewportRotate(rotate.val)">
|
||
{{ rotate.label }}
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 适应图像或窗口 -->
|
||
<!-- <div
|
||
class="tool-item"
|
||
:title="forceFitToWindow ? `${$t('trials:reading:button:fitWindow')}` : `${$t('trials:reading:button:fitImage')}`"
|
||
@click.prevent="fitToType(forceFitToWindow)"
|
||
>
|
||
<svg-icon v-if="forceFitToWindow" icon-class="fitToWindow" class="svg-icon" />
|
||
<svg-icon v-else icon-class="fitToImage" class="svg-icon" />
|
||
</div> -->
|
||
<div
|
||
v-for="tool in tools"
|
||
:key="tool.toolName"
|
||
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === tool.toolName ? 'tool-item-active' : '']"
|
||
:style="{cursor: tool.isDisabled ? 'not-allowed' : 'pointer'}"
|
||
:title="tool.disabledReason ? tool.disabledReason : $t(`${tool.i18nKey}`)"
|
||
@click.prevent="setAnnotateToolActive(tool.toolName)"
|
||
@mouseenter="enter($event,tool.toolName)"
|
||
>
|
||
<svg-icon :icon-class="tool.icon" class="svg-icon" />
|
||
</div>
|
||
|
||
<div class="tool-frame">
|
||
<!-- 第一帧 -->
|
||
<div :title="$t('trials:dicom-show:firstframe')" class="icon" @click.prevent="scrollPage(0)">
|
||
<svg-icon icon-class="firstframe" class="svg-icon" />
|
||
</div>
|
||
<!-- 上一帧 -->
|
||
<div :title="$t('trials:dicom-show:previousframe')" class="icon" @click.prevent="scrollPage(-1)">
|
||
<svg-icon icon-class="previousframe" class="svg-icon" />
|
||
</div>
|
||
<!-- 播放/暂停 -->
|
||
<div
|
||
v-if="clipPlaying"
|
||
:title="$t('trials:dicom-show:stop')"
|
||
class="icon"
|
||
@click.prevent="toggleClipPlay(false)"
|
||
>
|
||
<svg-icon icon-class="stop" class="svg-icon" />
|
||
</div>
|
||
<div
|
||
v-else
|
||
:title="$t('trials:dicom-show:play')"
|
||
class="icon"
|
||
@click.prevent="toggleClipPlay(true)"
|
||
>
|
||
<svg-icon icon-class="play" class="svg-icon" />
|
||
</div>
|
||
<!-- 下一帧 -->
|
||
<div :title="$t('trials:dicom-show:nextframe')" class="icon" @click.prevent="scrollPage(1)">
|
||
<svg-icon icon-class="nextframe" class="svg-icon" />
|
||
</div>
|
||
<!-- 最后一帧 -->
|
||
<div :title="$t('trials:dicom-show:lastframe')" class="icon" @click.prevent="scrollPage(99999)">
|
||
<svg-icon icon-class="lastframe" class="svg-icon" />
|
||
</div>
|
||
<select v-model="fps" :title="$t('trials:dicom-show:speed')" class="select-wrapper" :disabled="clipPlaying">
|
||
<!-- 默认值 -->
|
||
<option :value="5">5</option>
|
||
<option :value="10">10</option>
|
||
<option :value="15">15</option>
|
||
<option :value="20">20</option>
|
||
<option :value="25">25</option>
|
||
<option :value="30">30</option>
|
||
</select>
|
||
</div>
|
||
<!-- 重置 -->
|
||
<div
|
||
class="tool-item"
|
||
:title="$t('trials:reading:button:reset')"
|
||
@click.prevent="resetViewport"
|
||
>
|
||
<svg-icon icon-class="refresh" class="svg-icon" />
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<!-- 手册 -->
|
||
<el-button
|
||
v-if="taskInfo && taskInfo.ExistsManual"
|
||
type="text"
|
||
@click="previewManuals"
|
||
>
|
||
{{ $t('trials:reading:button:handbooks') }}
|
||
</el-button>
|
||
<!-- 临床数据 -->
|
||
<el-button
|
||
v-if="taskInfo && taskInfo.IsExistsClinicalData"
|
||
type="text"
|
||
@click="previewCD(taskInfo.VisitTaskId)"
|
||
>
|
||
{{ $t('trials:reading:button:clinicalData') }}
|
||
</el-button>
|
||
<!-- 个性化配置 -->
|
||
<el-button
|
||
type="text"
|
||
@click="previewConfig"
|
||
>
|
||
{{ $t('trials:reading:button:customCfg') }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<!-- viewports -->
|
||
<div class="viewports-wrapper">
|
||
<div ref="container" class="grid-container" :style="gridStyle">
|
||
<div
|
||
v-for="(v, index) in cellsMax"
|
||
v-show="index < cells.length"
|
||
:key="index"
|
||
:style="cellStyle"
|
||
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||
@dblclick="toggleFullScreen($event, index)"
|
||
@click="activeViewport(index)"
|
||
>
|
||
<Viewport
|
||
:ref="`viewport-${index}`"
|
||
:data-viewport-uid="`viewport-${index}`"
|
||
:rendering-engine-id="renderingEngineId"
|
||
:viewport-id="`viewport-${index}`"
|
||
:viewport-index="index"
|
||
@activeViewport="activeViewport"
|
||
@toggleTaskByViewport="toggleTaskByViewport"
|
||
@previewCD="previewCD"
|
||
@renderAnnotations="renderAnnotations"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 表单 -->
|
||
<div class="right-panel">
|
||
<div
|
||
v-for="s in visitTaskList"
|
||
v-show="lastViewportTaskId === s.VisitTaskId"
|
||
:key="s.VisitTaskId"
|
||
style="height:100%;"
|
||
>
|
||
<mRecisit
|
||
v-if="lastViewportTaskId && citerionType === 7 && lastViewportTaskIds.includes(s.VisitTaskId)"
|
||
:ref="`ecrf_${s.VisitTaskId}`"
|
||
:reading-task-state="currentVisitInfo.VisitTaskId === taskInfo.VisitTaskId ? readingTaskState : 2"
|
||
:last-viewport-task-id="lastViewportTaskId"
|
||
:visit-info="s"
|
||
@removeAnnotation="removeAnnotation"
|
||
@getScreenshots="getScreenshots"
|
||
@setMarkName="setMarkName"
|
||
@imageLocation="imageLocation"
|
||
@resetAnnotations="resetAnnotations"
|
||
@getAnnotations="getAnnotations"
|
||
@setToolToTarget="setToolToTarget"
|
||
/>
|
||
</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"
|
||
>
|
||
<custom-wwwc-form :ww="activeViewportWW" :wc="activeViewportWC" @close="customWwc.visible = false" @setWwwc="setWwwc" />
|
||
</el-dialog>
|
||
<!-- 手册 -->
|
||
<el-dialog
|
||
v-if="manualsDialog.visible"
|
||
:visible.sync="manualsDialog.visible"
|
||
:custom-class="manualsDialog.isFullscreen?'manuals-full-dialog-container':'manuals-dialog-container'"
|
||
:show-close="false"
|
||
:close-on-click-modal="false"
|
||
:fullscreen="manualsDialog.isFullscreen"
|
||
>
|
||
<span slot="title" class="dialog-footer">
|
||
<!-- 手册 -->
|
||
<span>{{ $t('trials:reading:button:handbooks') }}</span>
|
||
<span style="position: absolute;right: 20px;font-size: 20px;">
|
||
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" style="margin-right:10px;cursor: pointer;" @click="manualsDialog.isFullscreen=!manualsDialog.isFullscreen" />
|
||
<svg-icon icon-class="close" style="cursor: pointer;" @click="manualsDialog.visible = false" />
|
||
</span>
|
||
</span>
|
||
<div style="height: 100%;margin:0;">
|
||
<Manuals :trial-id="trialId" />
|
||
</div>
|
||
</el-dialog>
|
||
<!-- 个性化配置 -->
|
||
<el-dialog
|
||
v-if="personalConfigDialog.visible"
|
||
:visible.sync="personalConfigDialog.visible"
|
||
:close-on-click-modal="false"
|
||
:title="personalConfigDialog.title"
|
||
width="600px"
|
||
>
|
||
<el-tabs v-model="personalConfigDialog.activeName" class="personal_config">
|
||
<!-- 热键 -->
|
||
<el-tab-pane :label="$t('trials:reading:tab:hotkeys')" name="1">
|
||
<Hotkeys v-if="personalConfigDialog.activeName === '1'" :reading-tool="0" @reset="resetHotkeyList" />
|
||
</el-tab-pane>
|
||
<!-- W/L模板 -->
|
||
<el-tab-pane :label="$t('trials:reading:tab:wlTemplate')" name="2">
|
||
<WL v-if="personalConfigDialog.activeName === '2'" @getWwcTpl="getWwcTpl" />
|
||
</el-tab-pane>
|
||
<!-- 其他 -->
|
||
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
|
||
<Others v-if="personalConfigDialog.activeName === '3'" />
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
</el-dialog>
|
||
<!-- 临床数据 -->
|
||
<el-dialog
|
||
:visible.sync="clinicalDataVisible"
|
||
:custom-class="isClinicalDataFullscreen?'cd-full-dialog-container':'cd-dialog-container'"
|
||
:show-close="false"
|
||
:close-on-click-modal="false"
|
||
:fullscreen="isClinicalDataFullscreen"
|
||
>
|
||
<span slot="title" class="dialog-footer">
|
||
<div style="position: absolute;right: 20px;top: 10px;color: #000;">
|
||
<svg-icon :icon-class="isClinicalDataFullscreen?'exit-fullscreen':'fullscreen'" style="cursor: pointer;font-size: 20px;" @click="isClinicalDataFullscreen=!isClinicalDataFullscreen" />
|
||
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;" @click="clinicalDataVisible = false" />
|
||
</div>
|
||
</span>
|
||
<div style="height: 100%;margin:0;display: flex;flex-direction: column;">
|
||
<clinical-data
|
||
v-if="clinicalDataVisible"
|
||
style="flex: 1"
|
||
:trial-id="trialId"
|
||
:subject-id="taskInfo.SubjectId"
|
||
:visit-task-id="cdVisitTaskId"
|
||
:is-reading-show-subject-info="taskInfo.IsReadingShowSubjectInfo"
|
||
/>
|
||
</div>
|
||
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import { getRelatedVisitTask, getReadingVisitStudyList, getTableAnswerRowInfoList } from '@/api/trials'
|
||
import { getDoctorShortcutKey, getUserWLTemplateList } from '@/api/user'
|
||
import {
|
||
RenderingEngine,
|
||
Enums,
|
||
// imageLoader,
|
||
// metaData,
|
||
getRenderingEngine,
|
||
eventTarget,
|
||
utilities as csUtils,
|
||
cache
|
||
} from '@cornerstonejs/core'
|
||
import {
|
||
annotation
|
||
} from '@cornerstonejs/tools'
|
||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||
import initLibraries from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/initLibraries'
|
||
import html2canvas from 'html2canvas'
|
||
import { getTools } from './toolConfig'
|
||
import StudyList from './StudyList'
|
||
import Viewport from './Viewport'
|
||
import mRecisit from './mRecist/QuestionList'
|
||
import CustomWwwcForm from '@/views/trials/trials-panel/reading/dicoms/components/CustomWwwcForm'
|
||
import Manuals from '@/views/trials/trials-panel/reading/dicoms/components/Manuals'
|
||
import Hotkeys from '@/views/trials/trials-panel/reading/dicoms/components/Hotkeys'
|
||
import WL from '@/views/trials/trials-panel/reading/dicoms/components/WL'
|
||
import Others from '@/views/trials/trials-panel/reading/dicoms/components/Others'
|
||
import ClinicalData from '@/views/trials/trials-panel/reading/clinical-data'
|
||
const { visibility } = annotation
|
||
const { ViewportType, Events } = Enums
|
||
const renderingEngineId = 'myRenderingEngine'
|
||
const {
|
||
ToolGroupManager,
|
||
Enums: csToolsEnums,
|
||
StackScrollTool,
|
||
ScaleOverlayTool,
|
||
PanTool,
|
||
ZoomTool,
|
||
WindowLevelTool,
|
||
WindowLevelRegionTool,
|
||
PlanarRotateTool,
|
||
LengthTool,
|
||
BidirectionalTool,
|
||
ArrowAnnotateTool,
|
||
RectangleROITool,
|
||
PlanarFreehandROITool,
|
||
EraserTool
|
||
// cursors
|
||
} = cornerstoneTools
|
||
|
||
const newStyles = {
|
||
global: {
|
||
color: 'rgb(255, 0, 0)',
|
||
colorHighlighted: 'rgb(255, 0, 0)',
|
||
colorSelected: 'rgb(255, 0, 0)',
|
||
colorLocked: 'rgb(255, 0, 0)',
|
||
lineWidth: '1',
|
||
lineDash: '',
|
||
shadow: true,
|
||
textBoxVisibility: true,
|
||
textBoxFontFamily: 'Helvetica Neue, Helvetica, Arial, sans-serif',
|
||
textBoxFontSize: '14px',
|
||
textBoxColor: 'rgb(255, 0, 0)',
|
||
textBoxColorHighlighted: 'rgb(255, 0, 0)',
|
||
textBoxColorSelected: 'rgb(255, 0, 0)',
|
||
textBoxColorLocked: 'rgb(255, 0, 0)',
|
||
textBoxBackground: '',
|
||
textBoxLinkLineWidth: '1',
|
||
textBoxLinkLineDash: '2,3',
|
||
textBoxShadow: true,
|
||
markerSize: '10'
|
||
}
|
||
}
|
||
annotation.config.style.setDefaultToolStyles(newStyles)
|
||
const { MouseBindings, Events: toolsEvents } = csToolsEnums
|
||
export default {
|
||
name: 'ReadPage',
|
||
components: {
|
||
StudyList,
|
||
Viewport,
|
||
mRecisit,
|
||
CustomWwwcForm,
|
||
Manuals,
|
||
Hotkeys,
|
||
WL,
|
||
Others,
|
||
ClinicalData
|
||
},
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
trialId: '',
|
||
visitTaskList: [],
|
||
selectArr: [],
|
||
taskInfo: null,
|
||
activeTaskId: null,
|
||
activeTaskIndex: -1,
|
||
activeStudyIndex: -1,
|
||
activeSeriesIndex: -1,
|
||
currentVisitInfo: null,
|
||
layout: 1,
|
||
cellsMax: 4,
|
||
rows: 1,
|
||
cols: 1,
|
||
fullScreenIndex: null,
|
||
activeViewportIndex: 0,
|
||
activeTool: '',
|
||
readingTaskState: 2,
|
||
renderingEngineId: renderingEngineId,
|
||
sLoading: false,
|
||
renderedTaskIds: [],
|
||
citerionType: null,
|
||
tools: [],
|
||
rotateOptions: [
|
||
{ label: this.$t('trials:reading:button:rotateDefault'), val: 1 },
|
||
{ label: this.$t('trials:reading:button:rotateVertical'), val: 2 },
|
||
{ label: this.$t('trials:reading:button:rotateHorizontal'), val: 3 },
|
||
{ label: this.$t('trials:reading:button:rotateTurnLeft'), val: 4 },
|
||
{ label: this.$t('trials:reading:button:rotateTurnRight'), val: 5 }
|
||
],
|
||
defaultWwwc: [
|
||
{ label: this.$t('trials:reading:button:wwwcDefault'), val: -1, ww: null, wc: null }, // 默认值
|
||
{ label: this.$t('trials:reading:button:wwwcCustom'), val: 0, ww: null, wc: null }, // 自定义
|
||
{ label: this.$t('trials:reading:button:wwwcRegion'), val: 1, ww: null, wc: null }, // 区域窗宽
|
||
{ label: 'CT Brain', wc: 40, ww: 80 },
|
||
{ label: 'CT Lungs', wc: -400, ww: 1500 },
|
||
{ label: 'CT Abdomen', wc: 60, ww: 400 },
|
||
{ label: 'CT Liver', wc: 40, ww: 400 },
|
||
{ label: 'CT Bone', wc: 300, ww: 1500 },
|
||
{ label: 'CT Bone1', wc: 0, ww: 0 }
|
||
],
|
||
wwwcArr: [],
|
||
customWwc: { visible: false, title: this.$t('trials:reading:dagTitle:wwwcCustom') }, // 自定义调窗
|
||
personalConfigDialog: { visible: false, title: this.$t('trials:reading:button:customCfg'), activeName: '1' }, // 个性化配置
|
||
activeViewportWW: null,
|
||
activeViewportWC: null,
|
||
clipPlaying: false,
|
||
fps: 15,
|
||
manualsDialog: { visible: false, isFullscreen: false },
|
||
hotKeyList: [],
|
||
forceFitToWindow: true,
|
||
isShowAnnotations: true,
|
||
clinicalDataVisible: false,
|
||
isClinicalDataFullscreen: false,
|
||
cdVisitTaskId: '',
|
||
lastViewportTaskId: '',
|
||
digitPlaces: 2,
|
||
instanceInfo: {},
|
||
lastViewportTaskIds: [],
|
||
markedSeriesIds: []
|
||
}
|
||
},
|
||
computed: {
|
||
gridStyle() {
|
||
return {
|
||
display: 'grid',
|
||
gridTemplateRows: `repeat(${this.rows}, 1fr)`,
|
||
gridTemplateColumns: `repeat(${this.cols}, 1fr)`,
|
||
height: '100%',
|
||
width: '100%'
|
||
}
|
||
},
|
||
cellStyle() {
|
||
return {
|
||
border: '1px dashed #ccc',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
},
|
||
cells() {
|
||
return Array(this.rows * this.cols).fill(0)
|
||
}
|
||
},
|
||
watch: {
|
||
activeTaskId: {
|
||
immediate: true,
|
||
handler(id) {
|
||
if (!id) return
|
||
// this.addHistoryAnnotations(id)
|
||
}
|
||
},
|
||
activeViewportIndex: {
|
||
immediate: true,
|
||
handler(index) {
|
||
let series = null
|
||
if (this.$refs[`viewport-${this.activeViewportIndex}`] && this.$refs[`viewport-${this.activeViewportIndex}`][0]) {
|
||
this.clipPlaying = this.$refs[`viewport-${this.activeViewportIndex}`][0].playClipState
|
||
series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
} else {
|
||
this.clipPlaying = false
|
||
this.fps = 15
|
||
}
|
||
if (index === this.cells.length - 1 && series) {
|
||
this.lastViewportTaskId = series.TaskInfo.VisitTaskId
|
||
this.currentVisitInfo = series.TaskInfo
|
||
}
|
||
}
|
||
},
|
||
lastViewportTaskId: {
|
||
immediate: true,
|
||
handler(id) {
|
||
if (!id) return
|
||
if (!this.lastViewportTaskIds.includes(id)) {
|
||
this.lastViewportTaskIds.push(id)
|
||
}
|
||
// if (this.readingTaskState === 2) return
|
||
// if (id !== this.taskInfo.VisitTaskId) {
|
||
// const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.taskInfo.VisitTaskId)
|
||
// // const annotationUIDs = this.visitTaskList[taskIdx].AnnotationUIDs
|
||
// const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
|
||
// // const annotations = this.visitTaskList[taskIdx].Annotations
|
||
// annotations.forEach(i => {
|
||
// if (i.visitTaskId === this.taskInfo.VisitTaskId) {
|
||
// cornerstoneTools.annotation.state.removeAnnotation(i.annotationUID)
|
||
// let idx = this.visitTaskList[taskIdx].Annotations.findIndex(v=>v.MeasureData && v.MeasureData.annotationUID === i.annotationUID)
|
||
// console.log('123',idx)
|
||
// if (idx > -1) {
|
||
// cornerstoneTools.annotation.state.addAnnotation(this.visitTaskList[taskIdx].Annotations[idx].MeasureData)
|
||
// }
|
||
|
||
// }
|
||
// })
|
||
// // const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
|
||
// // annotations.forEach(i => {
|
||
// // if (!(annotationUIDs.includes(i.annotationUID)) && i.visitTaskId === this.taskInfo.VisitTaskId) {
|
||
// // cornerstoneTools.annotation.state.removeAnnotation(i.annotationUID)
|
||
// // }
|
||
// // })
|
||
// }
|
||
}
|
||
}
|
||
},
|
||
mounted() {
|
||
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
|
||
this.citerionType = this.taskInfo.CriterionType
|
||
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
|
||
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
|
||
this.tools = getTools(this.citerionType)
|
||
this.trialId = this.$route.query.trialId
|
||
this.readingTaskState = this.taskInfo.ReadingTaskState
|
||
if (!this.taskInfo.IsBaseLine && this.taskInfo.IsReadingTaskViewInOrder !== 0) {
|
||
this.rows = 1
|
||
this.cols = 2
|
||
this.activeViewportIndex = 1
|
||
}
|
||
|
||
this.$nextTick(() => {
|
||
this.loadRelatedTasks()
|
||
this.initLoader()
|
||
this.getWwcTpl()
|
||
this.getHotKeys()
|
||
})
|
||
},
|
||
methods: {
|
||
// 加载当前任务关联的任务信息
|
||
async loadRelatedTasks() {
|
||
this.loading = true
|
||
try {
|
||
const params = {
|
||
visitTaskId: this.taskInfo.VisitTaskId
|
||
}
|
||
const res = await getRelatedVisitTask(params)
|
||
let currentTaskIndex = 0
|
||
let currentTaskId = ''
|
||
this.visitTaskList = []
|
||
for (let i = 0; i < res.Result.length; i++) {
|
||
const item = res.Result[i]
|
||
let studyList = []
|
||
let annotations = []
|
||
let annotationUIDs = []
|
||
let keyImages = []
|
||
let isInit = false
|
||
if (item.IsCurrentTask) {
|
||
currentTaskIndex = i
|
||
currentTaskId = item.VisitTaskId
|
||
}
|
||
if (item.IsCurrentTask || (this.taskInfo.IsReadingTaskViewInOrder === 1 && !this.taskInfo.IsBaseLineTask && item.IsBaseLineTask)) {
|
||
const res = await this.loadTaskDetails(item, i)
|
||
isInit = true
|
||
studyList = res ? res.studyList : []
|
||
annotations = res ? res.annotations : []
|
||
annotationUIDs = res ? res.annotationUIDs : []
|
||
keyImages = res ? res.keyImages : []
|
||
if (!this.selectArr.includes(item.VisitTaskId)) {
|
||
this.selectArr.push(item.VisitTaskId)
|
||
}
|
||
if (item.IsCurrentTask) {
|
||
this.markedSeriesIds = []
|
||
annotations.map(i=> {
|
||
if (i.MeasureData && i.MeasureData.seriesId) {
|
||
this.markedSeriesIds.push(i.MeasureData.seriesId)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
this.visitTaskList.push({
|
||
...item,
|
||
StudyList: studyList,
|
||
Annotations: annotations,
|
||
KeyImages: keyImages,
|
||
AnnotationUIDs: annotationUIDs,
|
||
IsInit: isInit
|
||
})
|
||
}
|
||
this.activeTaskId = currentTaskId
|
||
this.activeTaskIndex = currentTaskIndex
|
||
this.$nextTick(() => {
|
||
this.setInitSeries()
|
||
})
|
||
|
||
this.loading = false
|
||
} catch (e) {
|
||
console.log(e)
|
||
this.loading = false
|
||
}
|
||
},
|
||
// 加载检查及标注信息
|
||
async loadTaskDetails(taskInfo, taskIndex) {
|
||
try {
|
||
const sujectVisitId = taskInfo.VisitId
|
||
const taskId = taskInfo.VisitTaskId
|
||
// 获取检查信息
|
||
const keyImages = []
|
||
const res1 = await getReadingVisitStudyList(this.trialId, sujectVisitId, taskId)
|
||
let keyStudyIndex = -1
|
||
let keySeriesIndex = -1
|
||
// const arr = [res1.Result[0]]
|
||
const arr = res1.Result
|
||
arr.forEach((study, studyIndex) => {
|
||
study.SeriesList.forEach((series, seriesIndex) => {
|
||
const imageIds = []
|
||
const stack = []
|
||
series.InstanceInfoList.forEach((instance, instanceIndex) => {
|
||
if (study.IsCriticalSequence) {
|
||
keyStudyIndex = studyIndex
|
||
keySeriesIndex = seriesIndex
|
||
keyImages.push({ Id: instance.Id, Path: instance.Path, KeyFramesList: instance.KeyFramesList, KeyStudyIndex: studyIndex, KeySeriesIndex: seriesIndex })
|
||
} else {
|
||
const i = keyImages.findIndex(k => k.Id === instance.Id)
|
||
if (i > -1) {
|
||
keyImages[i].StudyIndex = studyIndex
|
||
keyImages[i].SeriesIndex = seriesIndex
|
||
}
|
||
const nFrames = instance.NumberOfFrames || 0
|
||
if (nFrames === 0) {
|
||
// 单帧
|
||
stack.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&visitTaskId=${taskId}`)
|
||
} else {
|
||
// 多帧
|
||
for (let i = 0; i < nFrames; i++) {
|
||
const newImageId = `wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&visitTaskId=${taskId}&frame=${i + 1}`
|
||
stack.push(newImageId)
|
||
}
|
||
}
|
||
imageIds.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&visitTaskId=${taskId}`)
|
||
this.instanceInfo[instance.Id] = { taskIndex, studyIndex, seriesIndex }
|
||
}
|
||
})
|
||
series.Stack = stack
|
||
series.ImageIds = imageIds
|
||
series.SliceIndex = 0
|
||
series.LoadedImageCount = 0
|
||
series.LoadedImageProgress = 0
|
||
series.TaskInfo = Object.assign({}, taskInfo)
|
||
series.StudyIndex = studyIndex
|
||
series.SeriesIndex = seriesIndex
|
||
})
|
||
})
|
||
if (keyStudyIndex > -1 && keySeriesIndex > -1 && keyImages.length > 0) {
|
||
const keyImageIds = []
|
||
keyImages.forEach(instance => {
|
||
if (instance.KeyFramesList.length > 0) {
|
||
instance.KeyFramesList.map(i => {
|
||
keyImageIds.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&visitTaskId=${taskId}&frame=${i}`)
|
||
})
|
||
} else {
|
||
keyImageIds.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&visitTaskId=${taskId}`)
|
||
}
|
||
})
|
||
res1.Result[keyStudyIndex].SeriesList[keySeriesIndex].ImageIds = keyImageIds
|
||
}
|
||
// 获取标注信息
|
||
const res2 = await getTableAnswerRowInfoList(taskId)
|
||
const annotationUIDs = []
|
||
const annotations = res2.Result.map(i => {
|
||
if (typeof i.MeasureData === 'string' && i.MeasureData) {
|
||
i.MeasureData = JSON.parse(i.MeasureData)
|
||
annotationUIDs.push(i.MeasureData.annotationUID)
|
||
}
|
||
return i
|
||
})
|
||
return { studyList: arr, annotations: annotations, annotationUIDs, keyImages }
|
||
} catch (e) {
|
||
console.log(e)
|
||
}
|
||
},
|
||
// 设置初始化序列信息
|
||
setInitSeries() {
|
||
const seriesArr = []
|
||
let activeStudyIndex = -1
|
||
let activeSeriesIndex = -1
|
||
const studyList = this.visitTaskList[this.activeTaskIndex].StudyList.filter(i => i.IsDicom)
|
||
const seriesList = studyList.map(s => s.SeriesList).flat()
|
||
if (this.taskInfo.IsReadingTaskViewInOrder === 0) {
|
||
// 完全随机(默认一个视口,展示第一个序列)
|
||
const series = this.getRelatedSeries(this.visitTaskList[this.activeTaskIndex])
|
||
seriesArr.push(series)
|
||
activeStudyIndex = series.StudyIndex
|
||
activeSeriesIndex = series.SeriesIndex
|
||
} else if (this.taskInfo.IsReadingTaskViewInOrder === 1) {
|
||
// 按时间顺序(默认基线时显示一个视口,展示第一个序列;随访时显示基线和当前随访的序列)
|
||
if (this.taskInfo.IsBaseLine) {
|
||
// 基线默认显示第一个序列
|
||
const series = this.getRelatedSeries(this.visitTaskList[this.activeTaskIndex])
|
||
seriesArr.push(series)
|
||
activeStudyIndex = series.StudyIndex
|
||
activeSeriesIndex = series.SeriesIndex
|
||
} else {
|
||
// 随访
|
||
const i = this.visitTaskList.findIndex(i => i.IsBaseLineTask)
|
||
const baseSeries = this.getRelatedSeries(this.visitTaskList[i])
|
||
seriesArr.push(baseSeries)
|
||
const followUpSeries = this.getRelatedSeries(this.visitTaskList[this.activeTaskIndex], baseSeries)
|
||
seriesArr.push(followUpSeries)
|
||
activeStudyIndex = followUpSeries.StudyIndex
|
||
activeSeriesIndex = followUpSeries.SeriesIndex
|
||
}
|
||
} else if (this.taskInfo.IsReadingTaskViewInOrder === 2) {
|
||
// 受试者内随机(默认两个视口,显示第一个序列和第二个序列;如果只有一个序列则两个视口均显示这个序列)
|
||
if (seriesList.length === 1) {
|
||
seriesArr.push(seriesList[0], seriesList[0])
|
||
activeStudyIndex = seriesList[0].StudyIndex
|
||
activeSeriesIndex = seriesList[0].SeriesIndex
|
||
} else if (seriesArr.length > 1) {
|
||
seriesArr.push(seriesList[0], seriesList[1])
|
||
activeStudyIndex = seriesList[1].StudyIndex
|
||
activeSeriesIndex = seriesList[1].SeriesIndex
|
||
}
|
||
}
|
||
|
||
if (activeStudyIndex > -1 && activeSeriesIndex > -1) {
|
||
seriesArr.map((i, index) => {
|
||
this.$refs[`viewport-${index}`][0].setSeriesInfo(i)
|
||
})
|
||
const visitTaskId = this.visitTaskList[this.activeTaskIndex].VisitTaskId
|
||
this.lastViewportTaskId = visitTaskId
|
||
this.currentVisitInfo = this.visitTaskList[this.activeTaskIndex]
|
||
this.$refs[visitTaskId][0].setSeriesActive(activeStudyIndex, activeSeriesIndex)
|
||
}
|
||
},
|
||
// 初始化加载器
|
||
async initLoader() {
|
||
await initLibraries()
|
||
cache.setMaxCacheSize(6 * 1024 * 1024 * 1024);
|
||
let renderingEngine = getRenderingEngine(renderingEngineId)
|
||
if (!renderingEngine) {
|
||
renderingEngine = new RenderingEngine(renderingEngineId)
|
||
}
|
||
const element1 = this.$refs['viewport-0'][0].$el
|
||
const element2 = this.$refs['viewport-1'][0].$el
|
||
const element3 = this.$refs['viewport-2'][0].$el
|
||
const element4 = this.$refs['viewport-3'][0].$el
|
||
|
||
const viewportInputArray = [
|
||
{
|
||
viewportId: 'viewport-0',
|
||
type: ViewportType.STACK,
|
||
element: element1
|
||
},
|
||
{
|
||
viewportId: 'viewport-1',
|
||
type: ViewportType.STACK,
|
||
element: element2
|
||
},
|
||
{
|
||
viewportId: 'viewport-2',
|
||
type: ViewportType.STACK,
|
||
element: element3
|
||
},
|
||
{
|
||
viewportId: 'viewport-3',
|
||
type: ViewportType.STACK,
|
||
element: element4
|
||
}
|
||
]
|
||
const viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
|
||
renderingEngine.setViewports(viewportInputArray)
|
||
this.addAnnotationListeners()
|
||
cornerstoneTools.addTool(StackScrollTool)
|
||
cornerstoneTools.addTool(PanTool)
|
||
cornerstoneTools.addTool(ZoomTool)
|
||
cornerstoneTools.addTool(WindowLevelTool)
|
||
cornerstoneTools.addTool(WindowLevelRegionTool)
|
||
cornerstoneTools.addTool(PlanarRotateTool)
|
||
cornerstoneTools.addTool(ArrowAnnotateTool)
|
||
cornerstoneTools.addTool(RectangleROITool)
|
||
cornerstoneTools.addTool(PlanarFreehandROITool)
|
||
cornerstoneTools.addTool(EraserTool)
|
||
cornerstoneTools.addTool(LengthTool)
|
||
cornerstoneTools.addTool(BidirectionalTool)
|
||
cornerstoneTools.addTool(ScaleOverlayTool)
|
||
|
||
viewportIds.forEach((viewportId, i) => {
|
||
const toolGroupId = `viewport-${i}`
|
||
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId)
|
||
toolGroup.addViewport(viewportId, renderingEngineId)
|
||
toolGroup.addTool(StackScrollTool.toolName)
|
||
toolGroup.addTool(ScaleOverlayTool.toolName)
|
||
|
||
toolGroup.addTool(PanTool.toolName)
|
||
toolGroup.addTool(ZoomTool.toolName)
|
||
toolGroup.addTool(WindowLevelTool.toolName)
|
||
toolGroup.addTool(WindowLevelRegionTool.toolName)
|
||
toolGroup.addTool(PlanarRotateTool.toolName)
|
||
if (this.citerionType === 0) {
|
||
toolGroup.addTool(ArrowAnnotateTool.toolName, {
|
||
arrowHeadStyle: 'standard',
|
||
changeTextCallback: async(data, eventData, doneChangingTextCallback) => {
|
||
return doneChangingTextCallback(await this.customPrompt())
|
||
},
|
||
getTextCallback: async(doneChangingTextCallback) => {
|
||
return doneChangingTextCallback(await this.customPrompt())
|
||
}
|
||
})
|
||
} else {
|
||
toolGroup.addTool(ArrowAnnotateTool.toolName, {
|
||
arrowHeadStyle: 'standard',
|
||
changeTextCallback: async(data, eventData, doneChangingTextCallback) => {
|
||
return doneChangingTextCallback(data.text)
|
||
},
|
||
getTextCallback: async(doneChangingTextCallback) => {
|
||
return doneChangingTextCallback('_')
|
||
}
|
||
})
|
||
}
|
||
toolGroup.addTool(RectangleROITool.toolName, {
|
||
cachedStats: false,
|
||
getTextLines: this.getRectangleROIToolTextLines
|
||
})
|
||
toolGroup.addTool(PlanarFreehandROITool.toolName, {
|
||
allowOpenContours: false,
|
||
cachedStats: false,
|
||
getTextLines: this.getPlanarFreehandROIToolTextLines
|
||
})
|
||
|
||
toolGroup.addTool(EraserTool.toolName)
|
||
toolGroup.addTool(LengthTool.toolName, {
|
||
getTextLines: this.getLengthToolTextLines
|
||
// cachedStats: false
|
||
})
|
||
toolGroup.addTool(BidirectionalTool.toolName, {
|
||
// cachedStats: true
|
||
getTextLines: this.getBidirectionalToolTextLines
|
||
})
|
||
|
||
toolGroup.setToolActive(StackScrollTool.toolName, {
|
||
bindings: [{ mouseButton: MouseBindings.Wheel }]
|
||
})
|
||
// toolGroup.setToolEnabled(ScaleOverlayTool.toolName);
|
||
toolGroup.setToolPassive(PanTool.toolName)
|
||
toolGroup.setToolPassive(ZoomTool.toolName)
|
||
toolGroup.setToolPassive(WindowLevelTool.toolName)
|
||
toolGroup.setToolPassive(WindowLevelRegionTool.toolName)
|
||
toolGroup.setToolPassive(PlanarRotateTool.toolName)
|
||
|
||
if (this.readingTaskState < 2) {
|
||
toolGroup.setToolPassive(ArrowAnnotateTool.toolName)
|
||
toolGroup.setToolPassive(RectangleROITool.toolName)
|
||
toolGroup.setToolPassive(PlanarFreehandROITool.toolName)
|
||
toolGroup.setToolPassive(LengthTool.toolName)
|
||
toolGroup.setToolPassive(BidirectionalTool.toolName)
|
||
} else {
|
||
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
|
||
toolGroup.setToolEnabled(RectangleROITool.toolName)
|
||
toolGroup.setToolEnabled(PlanarFreehandROITool.toolName)
|
||
toolGroup.setToolEnabled(LengthTool.toolName)
|
||
toolGroup.setToolEnabled(BidirectionalTool.toolName)
|
||
}
|
||
toolGroup.setToolPassive(EraserTool.toolName)
|
||
})
|
||
|
||
eventTarget.addEventListener('cornerstoneimageloadprogress', this.imageLoadProgress)
|
||
console.log(Events)
|
||
},
|
||
// 影像下载进度回调
|
||
imageLoadProgress(e) {
|
||
const { detail } = e
|
||
const percentComplete = detail.percentComplete
|
||
const imageId = detail.imageId
|
||
const params = this.getInstanceInfo(imageId)
|
||
const instanceId = params.instanceId
|
||
const idxObj = this.instanceInfo[instanceId]
|
||
if (!(idxObj && typeof idxObj === 'object')) return
|
||
if (!(this.instanceInfo[instanceId].hasOwnProperty('percentComplete'))) {
|
||
this.instanceInfo[instanceId].percentComplete = 0
|
||
}
|
||
// if (!params.hasOwnProperty('idx')) return
|
||
// const indexArr = params.idx.split('|')
|
||
// if (indexArr.length < 3) return
|
||
const taskIndex = idxObj.taskIndex
|
||
const studyIndex = idxObj.studyIndex
|
||
const seriesIndex = idxObj.seriesIndex
|
||
const isCriticalSequence = this.visitTaskList[taskIndex].StudyList[studyIndex].IsCriticalSequence
|
||
const keyImages = this.visitTaskList[taskIndex].KeyImages
|
||
const series = this.visitTaskList[taskIndex].StudyList[studyIndex].SeriesList[seriesIndex]
|
||
this.setImageLoadedProgress(series, percentComplete, instanceId)
|
||
if (!isCriticalSequence && series.IsBeMark && keyImages.length > 0) {
|
||
const i = keyImages.findIndex(i => i.Id === params.instanceId)
|
||
if (i === -1) return
|
||
const keyStudyIndex = parseInt(keyImages[i].KeyStudyIndex)
|
||
const keySeriesIndex = parseInt(keyImages[i].KeySeriesIndex)
|
||
const keySeries = this.visitTaskList[taskIndex].StudyList[keyStudyIndex].SeriesList[keySeriesIndex]
|
||
this.setImageLoadedProgress(keySeries, percentComplete, instanceId)
|
||
}
|
||
},
|
||
// 设置图像下载进度信息
|
||
setImageLoadedProgress(series, percentComplete, instanceId) {
|
||
let loadedImageCount = series.LoadedImageCount
|
||
if (percentComplete === 100) {
|
||
++loadedImageCount
|
||
this.$set(series, 'LoadedImageCount', loadedImageCount)
|
||
}
|
||
const newLoadedImageProgress = series.LoadedImageProgress - this.instanceInfo[instanceId].percentComplete + percentComplete
|
||
this.instanceInfo[instanceId].percentComplete = percentComplete
|
||
this.$set(series, 'LoadedImageProgress', newLoadedImageProgress)
|
||
},
|
||
addHistoryAnnotations(taskId) {
|
||
// if (this.renderedTaskIds.includes(taskId)) return
|
||
// this.renderedTaskIds.push(taskId)
|
||
// let taskIdx = this.visitTaskList.findIndex(i=>i.VisitTaskId === taskId)
|
||
// if (taskIdx=== -1) return
|
||
// const annotations = this.visitTaskList[taskIdx].Annotations
|
||
// annotations.map(i => {
|
||
// if (i.MeasureData) {
|
||
// const annotation = i.MeasureData
|
||
// cornerstoneTools.annotation.state.addAnnotation(annotation)
|
||
// if (this.visitTaskList[taskIdx].ReadingTaskState === 2) {
|
||
// cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
|
||
// }
|
||
// }
|
||
// })
|
||
},
|
||
renderAnnotations(series) {
|
||
const taskId = series.TaskInfo.VisitTaskId
|
||
if (!taskId || this.renderedTaskIds.includes(taskId)) return
|
||
this.renderedTaskIds.push(taskId)
|
||
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === taskId)
|
||
if (taskIdx === -1) return
|
||
const annotations = this.visitTaskList[taskIdx].Annotations
|
||
annotations.map(i => {
|
||
if (i.MeasureData) {
|
||
const annotation = i.MeasureData
|
||
cornerstoneTools.annotation.state.addAnnotation(annotation)
|
||
if (this.visitTaskList[taskIdx].ReadingTaskState === 2) {
|
||
cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
|
||
}
|
||
}
|
||
})
|
||
},
|
||
addAnnotationListeners() {
|
||
const debouncedCallback = this.debounce((evt) => {
|
||
this.annotationModifiedListener(evt)
|
||
}, 100)
|
||
eventTarget.addEventListener(
|
||
toolsEvents.ANNOTATION_MODIFIED,
|
||
(evt) => {
|
||
debouncedCallback(evt)
|
||
}
|
||
)
|
||
eventTarget.addEventListener(
|
||
toolsEvents.ANNOTATION_COMPLETED,
|
||
this.annotationCompletedListener
|
||
)
|
||
eventTarget.addEventListener(
|
||
toolsEvents.ANNOTATION_REMOVED,
|
||
this.annotationRemovedListener
|
||
)
|
||
// eventTarget.addEventListener(
|
||
// toolsEvents.ANNOTATION_ADDED,
|
||
// this.annotationAddedListener
|
||
// )
|
||
// eventTarget.addEventListener(
|
||
// toolsEvents.TOOL_MODE_CHANGED,
|
||
// this.toolModeChanged
|
||
// )
|
||
},
|
||
toolModeChanged(e) {
|
||
console.log(e)
|
||
const arr = cornerstoneTools.annotation.state.getAllAnnotations()
|
||
// if (arr)
|
||
console.log(arr)
|
||
},
|
||
annotationAddedListener(e) {
|
||
|
||
},
|
||
annotationCompletedListener(e) {
|
||
console.log('Completed')
|
||
if (this.readingTaskState === 2) return
|
||
const { annotation } = e.detail
|
||
if (!annotation) return
|
||
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||
const referencedImageId = annotation.metadata.referencedImageId
|
||
const params = this.getInstanceInfo(referencedImageId)
|
||
annotation.visitTaskId = series.TaskInfo.VisitTaskId
|
||
annotation.studyId = series.StudyId
|
||
annotation.seriesId = series.Id
|
||
annotation.instanceId = params.instanceId
|
||
annotation.sliceThickness = series.SliceThickness
|
||
annotation.numberOfFrames = isNaN(parseInt(params.frame)) ? null : parseInt(params.frame)
|
||
annotation.markTool = annotation.metadata.toolName
|
||
|
||
// this.$refs['ecrf'].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
|
||
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
|
||
this.markedSeriesIds.push(series.Id)
|
||
}
|
||
|
||
this.setToolsPassive()
|
||
},
|
||
annotationModifiedListener(e) {
|
||
console.log('Modified')
|
||
if (this.readingTaskState === 2) return
|
||
const { annotation } = e.detail
|
||
if (!annotation) return
|
||
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||
// this.$refs['ecrf'].modifyAnnotation({ annotation, toolName: annotation.metadata.toolName })
|
||
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].modifyAnnotation({ annotation, toolName: annotation.metadata.toolName })
|
||
}
|
||
this.setToolsPassive()
|
||
},
|
||
annotationRemovedListener(e) {
|
||
if (this.readingTaskState === 2) return
|
||
const { annotation } = e.detail
|
||
if (!annotation) return
|
||
if (annotation.visitTaskId === this.taskInfo.VisitTaskId && annotation.seriesId) {
|
||
const index = this.markedSeriesIds.indexOf(annotation.seriesId)
|
||
if (index !== -1) {
|
||
this.markedSeriesIds.splice(index, 1)
|
||
}
|
||
}
|
||
console.log(this.markedSeriesIds)
|
||
},
|
||
removeAnnotation(annotation) {
|
||
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
for (let i = 0; i < this.cells.length; i++) {
|
||
const viewportId = `viewport-${i}`
|
||
const viewport = renderingEngine.getViewport(viewportId)
|
||
viewport.render()
|
||
}
|
||
},
|
||
async getAnnotations(visitTaskId) {
|
||
if (this.readingTaskState === 2) return
|
||
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
|
||
if (taskIdx === -1) return
|
||
if (!this.visitTaskList[taskIdx].IsCurrentTask) return
|
||
// 获取标注信息
|
||
const res = await getTableAnswerRowInfoList(visitTaskId)
|
||
const annotationUIDs = []
|
||
const annotations = res.Result.map(i => {
|
||
if (typeof i.MeasureData === 'string' && i.MeasureData) {
|
||
i.MeasureData = JSON.parse(i.MeasureData)
|
||
annotationUIDs.push(i.MeasureData.annotationUID)
|
||
}
|
||
return i
|
||
})
|
||
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
|
||
this.$set(this.visitTaskList[taskIdx], 'AnnotationUIDs', annotationUIDs)
|
||
},
|
||
async resetAnnotations(visitTaskId) {
|
||
if (this.readingTaskState === 2) return
|
||
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
|
||
if (taskIdx === -1) return
|
||
if (!this.visitTaskList[taskIdx].IsCurrentTask) return
|
||
// 获取标注信息
|
||
const res = await getTableAnswerRowInfoList(visitTaskId)
|
||
const annotations = res.Result.map(i => {
|
||
if (typeof i.MeasureData === 'string' && i.MeasureData) {
|
||
i.MeasureData = JSON.parse(i.MeasureData)
|
||
}
|
||
return i
|
||
})
|
||
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
|
||
// 移除病灶
|
||
const arr = cornerstoneTools.annotation.state.getAllAnnotations()
|
||
arr.map(i => {
|
||
if (i.visitTaskId === visitTaskId) {
|
||
cornerstoneTools.annotation.state.removeAnnotation(i.annotationUID)
|
||
}
|
||
})
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
for (let i = 0; i < this.cells.length; i++) {
|
||
const viewportId = `viewport-${i}`
|
||
const viewport = renderingEngine.getViewport(viewportId)
|
||
viewport.render()
|
||
}
|
||
},
|
||
setMarkName(obj) {
|
||
const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
|
||
const idx = annotations.findIndex(i => i.annotationUID === obj.annotationUID)
|
||
if (idx === -1) return
|
||
if (annotations[idx].metadata.toolName === 'ArrowAnnotate') {
|
||
annotations[idx].data.text = obj.name
|
||
} else {
|
||
annotations[idx].data.label = obj.name
|
||
}
|
||
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
const viewportId = `viewport-${this.activeViewportIndex}`
|
||
const viewport = renderingEngine.getViewport(viewportId)
|
||
viewport.render()
|
||
},
|
||
getLengthToolTextLines(data, targetId) {
|
||
const cachedVolumeStats = data.cachedStats[targetId]
|
||
const { length, unit } = cachedVolumeStats
|
||
if (length === undefined || length === null || isNaN(length)) {
|
||
return
|
||
}
|
||
const textLines = []
|
||
if (data.label) {
|
||
textLines.push(data.label)
|
||
}
|
||
textLines.push(`${parseFloat(length).toFixed(this.digitPlaces)} ${unit}`)
|
||
return textLines
|
||
},
|
||
getPlanarFreehandROIToolTextLines(data, targetId) {
|
||
const cachedVolumeStats = data.cachedStats[targetId]
|
||
const {
|
||
area,
|
||
mean,
|
||
stdDev,
|
||
// length,
|
||
perimeter,
|
||
max,
|
||
isEmptyArea,
|
||
unit,
|
||
areaUnit,
|
||
modalityUnit
|
||
} = cachedVolumeStats || {}
|
||
|
||
const textLines = []
|
||
|
||
if (area) {
|
||
const areaLine = isEmptyArea
|
||
? `Area: Oblique not supported`
|
||
: `Area: ${parseFloat(area).toFixed(this.digitPlaces)} ${areaUnit}`
|
||
textLines.push(areaLine)
|
||
}
|
||
if (mean) {
|
||
textLines.push(`Mean: ${parseFloat(mean).toFixed(this.digitPlaces)} ${modalityUnit}`)
|
||
}
|
||
|
||
if (Number.isFinite(max)) {
|
||
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`)
|
||
}
|
||
|
||
if (stdDev) {
|
||
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`)
|
||
}
|
||
|
||
if (perimeter) {
|
||
textLines.push(`Perimeter: ${parseFloat(perimeter).toFixed(this.digitPlaces)} ${unit}`)
|
||
}
|
||
|
||
// if (length) {
|
||
// // No need to show length prefix as there is just the single value
|
||
// textLines.push(`${csUtils.roundNumber(length)} ${unit}`);
|
||
// }
|
||
|
||
return textLines
|
||
},
|
||
getRectangleROIToolTextLines(data, targetId) {
|
||
const cachedVolumeStats = data.cachedStats[targetId]
|
||
// const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats
|
||
const { mean } = cachedVolumeStats
|
||
|
||
if (mean === undefined) {
|
||
return
|
||
}
|
||
|
||
const textLines = []
|
||
if (data.label) {
|
||
textLines.push(data.label)
|
||
}
|
||
// textLines.push(`Area: ${parseFloat(area).toFixed(this.digitPlaces)} ${areaUnit}`)
|
||
|
||
// textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`)
|
||
// textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`)
|
||
// textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`)
|
||
|
||
return textLines
|
||
},
|
||
getBidirectionalToolTextLines(data, targetId) {
|
||
const { cachedStats, label } = data
|
||
const { length, width, unit } = cachedStats[targetId]
|
||
|
||
const textLines = []
|
||
// if (label) {
|
||
// textLines.push(label);
|
||
// }
|
||
if (label) {
|
||
textLines.push(label)
|
||
}
|
||
if (length === undefined) {
|
||
return textLines
|
||
}
|
||
|
||
// spaceBetweenSlices & pixelSpacing &
|
||
// magnitude in each direction? Otherwise, this is "px"?
|
||
textLines.push(
|
||
`L: ${csUtils.roundNumber(length)} ${unit || unit}`,
|
||
`S: ${csUtils.roundNumber(width)} ${unit}`
|
||
)
|
||
|
||
return textLines
|
||
},
|
||
// 激活工具
|
||
setToolActive(toolName) {
|
||
const toolGroupId = `viewport-${this.activeViewportIndex}`
|
||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||
if (this.activeTool === toolName) {
|
||
toolGroup.setToolPassive(this.activeTool)
|
||
this.activeTool = ''
|
||
} else {
|
||
if (this.activeTool) {
|
||
toolGroup.setToolPassive(this.activeTool)
|
||
}
|
||
toolGroup.setToolActive(toolName, {
|
||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||
})
|
||
this.activeTool = toolName
|
||
}
|
||
},
|
||
// 激活标注工具
|
||
setAnnotateToolActive(toolName) {
|
||
if (this.readingTaskState === 2) return
|
||
const toolObj = this.tools.find(i => i.toolName === toolName)
|
||
if (!toolObj || toolObj.isDisabled) return
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||
const toolGroupId = `viewport-${this.activeViewportIndex}`
|
||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||
if (this.activeTool === toolName) {
|
||
toolGroup.setToolPassive(this.activeTool)
|
||
this.activeTool = ''
|
||
} else {
|
||
if (this.activeTool) {
|
||
toolGroup.setToolPassive(this.activeTool)
|
||
}
|
||
toolGroup.setToolActive(toolName, {
|
||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||
})
|
||
this.activeTool = toolName
|
||
}
|
||
}
|
||
},
|
||
|
||
setToolsPassive() {
|
||
if (!this.activeTool) return
|
||
const toolGroupIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
|
||
toolGroupIds.forEach(toolGroupId => {
|
||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||
toolGroup.setToolPassive(this.activeTool)
|
||
})
|
||
this.activeTool = ''
|
||
},
|
||
setToolEnabled() {
|
||
if (!this.activeTool) return
|
||
const toolGroupIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
|
||
toolGroupIds.forEach(toolGroupId => {
|
||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||
toolGroup.setToolEnabled(this.activeTool)
|
||
})
|
||
this.activeTool = ''
|
||
},
|
||
// 鼠标移入测量工具时,判断工具是否可激活
|
||
enter(e, toolName) {
|
||
const i = this.tools.findIndex(i => i.toolName === toolName)
|
||
if (i === -1) return
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
const isCurrentTask = series.TaskInfo.IsCurrentTask
|
||
const readingTaskState = this.readingTaskState
|
||
if (!isCurrentTask || readingTaskState >= 2) {
|
||
this.tools[i].isDisabled = true
|
||
e.target.style.cursor = 'not-allowed'
|
||
if (this.activeTool) {
|
||
this.setToolEnabled()
|
||
}
|
||
} else {
|
||
// const obj = this.$refs['ecrf'].validTool(toolName, true)
|
||
const obj = this.$refs[`ecrf_${this.lastViewportTaskId}`][0].validTool(toolName, true)
|
||
this.tools[i].disabledReason = obj.reason
|
||
if (!obj.isCanActiveTool) {
|
||
if (this.activeTool === toolName) {
|
||
this.setToolsPassive()
|
||
}
|
||
this.tools[i].isDisabled = true
|
||
e.target.style.cursor = 'not-allowed'
|
||
} else {
|
||
this.tools[i].isDisabled = false
|
||
e.target.style.cursor = 'pointer'
|
||
}
|
||
}
|
||
},
|
||
// 旋转视口
|
||
setViewportRotate(value) {
|
||
this.setToolsPassive()
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
const viewportId = `viewport-${this.activeViewportIndex}`
|
||
const viewport = renderingEngine.getViewport(viewportId)
|
||
const type = parseInt(value)
|
||
// 1:默认值;2:垂直翻转;3:水平翻转;4:左转90度;5:右转90度;
|
||
if (type === 1) {
|
||
viewport.resetCamera()
|
||
viewport.resetProperties()
|
||
viewport.render()
|
||
} else if (type === 2) {
|
||
const { flipVertical } = viewport.getCamera()
|
||
viewport.setCamera({ flipVertical: !flipVertical })
|
||
viewport.render()
|
||
} else if (type === 3) {
|
||
const { flipHorizontal } = viewport.getCamera()
|
||
viewport.setCamera({ flipHorizontal: !flipHorizontal })
|
||
viewport.render()
|
||
} else if (type === 4) {
|
||
const { rotation } = viewport.getViewPresentation()
|
||
viewport.setViewPresentation({ rotation: rotation === 0 ? 270 : rotation - 90 })
|
||
viewport.render()
|
||
} else if (type === 5) {
|
||
const { rotation } = viewport.getViewPresentation()
|
||
viewport.setViewPresentation({ rotation: rotation + 90 })
|
||
viewport.render()
|
||
}
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].rotateOrientationMarkers(type)
|
||
},
|
||
// 重置视口
|
||
resetViewport() {
|
||
this.setToolsPassive()
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
const viewportId = `viewport-${this.activeViewportIndex}`
|
||
const viewport = renderingEngine.getViewport(viewportId)
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].resetOrientationMarkers()
|
||
viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
|
||
viewport.resetProperties()
|
||
viewport.render()
|
||
renderingEngine.render()
|
||
},
|
||
// 更改视图布局
|
||
async changeLayout(v) {
|
||
this.setToolsPassive()
|
||
this.fullScreenIndex = null
|
||
this.layout = v
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
const seriesArr = []
|
||
if (v === 1) {
|
||
this.rows = 1
|
||
this.cols = 1
|
||
this.activeViewportIndex = 0
|
||
if (typeof series === 'object') {
|
||
seriesArr.push(series)
|
||
}
|
||
} else if (v === 2) {
|
||
this.rows = 1
|
||
this.cols = 2
|
||
this.activeViewportIndex = 1
|
||
if (typeof series === 'object') {
|
||
seriesArr.push(series)
|
||
seriesArr.push(series)
|
||
}
|
||
} else if (v === 3) {
|
||
this.rows = 1
|
||
this.cols = 2
|
||
if (series) {
|
||
const idx = this.visitTaskList.findIndex(i => i.VisitTaskId === series.TaskInfo.VisitTaskId)
|
||
const visitTaskNum = idx > -1 ? this.visitTaskList[idx].VisitTaskNum : -1
|
||
if (visitTaskNum > 0) {
|
||
let visitTaskIdx = -1
|
||
if (this.citerionType === 10) {
|
||
visitTaskIdx = this.visitTaskList.findIndex(i => i.VisitTaskNum === visitTaskNum - 1)
|
||
} else {
|
||
visitTaskIdx = this.visitTaskList.findIndex(i => i.IsBaseLineTask)
|
||
}
|
||
if (visitTaskIdx > -1) {
|
||
if (!this.visitTaskList[visitTaskIdx].IsInit) {
|
||
this.sLoading = true
|
||
try {
|
||
const res = await this.loadTaskDetails(this.visitTaskList[visitTaskIdx], visitTaskIdx)
|
||
const studyList = res ? res.studyList : []
|
||
const annotations = res ? res.annotations : []
|
||
const keyImages = res ? res.keyImages : []
|
||
const annotationUIDs = res ? res.annotationUIDs : []
|
||
this.$set(this.visitTaskList[visitTaskIdx], 'StudyList', studyList)
|
||
this.$set(this.visitTaskList[visitTaskIdx], 'Annotations', annotations)
|
||
this.$set(this.visitTaskList[visitTaskIdx], 'KeyImages', keyImages)
|
||
this.$set(this.visitTaskList[visitTaskIdx], 'AnnotationUIDs', annotationUIDs)
|
||
this.$set(this.visitTaskList[visitTaskIdx], 'IsInit', true)
|
||
this.sLoading = false
|
||
} catch (e) {
|
||
this.sLoading = false
|
||
}
|
||
}
|
||
const relatedSeries = this.getRelatedSeries(this.visitTaskList[visitTaskIdx], series)
|
||
seriesArr.push(relatedSeries)
|
||
seriesArr.push(series)
|
||
}
|
||
} else if (visitTaskNum === 0) {
|
||
seriesArr.push(series)
|
||
seriesArr.push(series)
|
||
}
|
||
}
|
||
this.activeViewportIndex = 1
|
||
} else if (v === 4) {
|
||
this.rows = 2
|
||
this.cols = 2
|
||
if (typeof series === 'object') {
|
||
seriesArr.push(series)
|
||
seriesArr.push(series)
|
||
seriesArr.push(series)
|
||
seriesArr.push(series)
|
||
}
|
||
this.activeViewportIndex = 3
|
||
}
|
||
seriesArr.map((i, index) => {
|
||
this.$refs[`viewport-${index}`][0].setSeriesInfo(i)
|
||
})
|
||
this.$nextTick(() => {
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
renderingEngine.resize(true, false)
|
||
})
|
||
},
|
||
// 更改窗宽窗位
|
||
changeVoiRange(v) {
|
||
this.setToolsPassive()
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
const viewportId = `viewport-${this.activeViewportIndex}`
|
||
const viewport = renderingEngine.getViewport(viewportId)
|
||
if (v.val === -1) {
|
||
// 默认值
|
||
viewport.resetProperties()
|
||
viewport.render()
|
||
} else if (v.val === 0) {
|
||
// 自定义
|
||
const wwwc = this.$refs[`viewport-${this.activeViewportIndex}`][0].imageInfo.wwwc
|
||
this.activeViewportWW = wwwc ? parseInt(wwwc.split('/')[0]) : null
|
||
this.activeViewportWC = wwwc ? parseInt(wwwc.split('/')[1]) : null
|
||
this.customWwc.visible = true
|
||
} else if (v.val === 1) {
|
||
// 区域窗宽窗位
|
||
this.setToolActive('WindowLevelRegion')
|
||
} else {
|
||
const lower = v.wc - v.ww / 2
|
||
const upper = v.wc + v.ww / 2 - 1
|
||
viewport.setProperties({ voiRange: { upper: upper, lower: lower }})
|
||
viewport.render()
|
||
}
|
||
},
|
||
setWindowLevelActive(e) {
|
||
this.setToolActive('WindowLevel')
|
||
this.showPanel(e)
|
||
},
|
||
// 设置窗宽窗位
|
||
setWwwc(v) {
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
const viewportId = `viewport-${this.activeViewportIndex}`
|
||
const viewport = renderingEngine.getViewport(viewportId)
|
||
const lower = v.wc - v.ww / 2
|
||
const upper = v.wc + v.ww / 2 - 1
|
||
viewport.setProperties({ voiRange: { upper: upper, lower: lower }})
|
||
viewport.render()
|
||
this.customWwc.visible = false
|
||
},
|
||
// 反色
|
||
toggleInvert() {
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
const viewport = renderingEngine.getViewport(`viewport-${this.activeViewportIndex}`)
|
||
const { invert } = viewport.getProperties()
|
||
viewport.setProperties({ invert: !invert })
|
||
viewport.render()
|
||
},
|
||
// 翻页
|
||
scrollPage(type) {
|
||
this.clipPlaying = false
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].scrollPage(type)
|
||
},
|
||
// 播放
|
||
toggleClipPlay(isPlay) {
|
||
this.clipPlaying = !this.clipPlaying
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].toggleClipPlay(isPlay, this.fps)
|
||
},
|
||
// 获取窗宽窗位模板
|
||
async getWwcTpl() {
|
||
try {
|
||
const res = await getUserWLTemplateList()
|
||
const customWwcTpl = []
|
||
res.Result.map(i => {
|
||
customWwcTpl.push({ label: i.TemplateName, wc: i.WL, ww: i.WW })
|
||
})
|
||
this.wwwcArr = [...this.defaultWwwc, ...customWwcTpl]
|
||
} catch (e) {
|
||
console.log(e)
|
||
}
|
||
},
|
||
// 获取热键配置信息
|
||
async getHotKeys() {
|
||
try {
|
||
const res = await getDoctorShortcutKey({ imageToolType: 0 })
|
||
res.Result.map(item => {
|
||
this.hotKeyList.push({ id: item.Id, altKey: item.AltKey, ctrlKey: item.CtrlKey, shiftKey: item.ShiftKey, metaKey: item.MetaKey, key: item.Keyboardkey, code: item.Code, text: item.Text, shortcutKeyEnum: item.ShortcutKeyEnum })
|
||
})
|
||
this.bindHotKey()
|
||
} catch (e) {
|
||
console.log(e)
|
||
}
|
||
},
|
||
// 绑定热键事件
|
||
bindHotKey() {
|
||
const container = this.$refs['container']
|
||
container.addEventListener('keydown', event => {
|
||
let idx = this.hotKeyList.findIndex(i => i.code === event.code && i.ctrlKey === event.ctrlKey && i.shiftKey === event.shiftKey && i.altKey === event.altKey)
|
||
if (idx === -1) return
|
||
let shortcutKeyEnum = this.hotKeyList[idx].shortcutKeyEnum
|
||
|
||
if (shortcutKeyEnum === 1) {
|
||
// 前一图像视口
|
||
const viewportIndex = this.activeViewportIndex === 0 ? this.activeViewportIndex : this.activeViewportIndex - 1
|
||
this.activeViewport(viewportIndex)
|
||
} else if (shortcutKeyEnum === 2) {
|
||
// 后一图像视口
|
||
const viewportIndex = this.activeViewportIndex === this.cells.length - 1 ? this.activeViewportIndex : this.activeViewportIndex + 1
|
||
this.activeViewport(viewportIndex)
|
||
} else if (shortcutKeyEnum === 3) {
|
||
// 上一个序列
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
this.$refs[this.activeTaskId][0].getPreviousOrNextSeries(-1, series)
|
||
} else if (shortcutKeyEnum === 4) {
|
||
// 下一个序列
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
this.$refs[this.activeTaskId][0].getPreviousOrNextSeries(1, series)
|
||
} else if (shortcutKeyEnum === 5) {
|
||
// 上一张图像
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].scrollPage(-1)
|
||
} else if (shortcutKeyEnum === 6) {
|
||
// 下一张图像
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].scrollPage(1)
|
||
} else if (shortcutKeyEnum === 7) {
|
||
// 向左旋转
|
||
this.setViewportRotate(4)
|
||
} else if (shortcutKeyEnum === 8) {
|
||
// 向右旋转
|
||
this.setViewportRotate(5)
|
||
} else if (shortcutKeyEnum === 9) {
|
||
// 水平翻转
|
||
this.setViewportRotate(3)
|
||
} else if (shortcutKeyEnum === 10) {
|
||
// 垂直翻转
|
||
this.setViewportRotate(2)
|
||
} else if (shortcutKeyEnum === 11) {
|
||
// 放大
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].setZoom(1)
|
||
} else if (shortcutKeyEnum === 12) {
|
||
// 缩小
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].setZoom(-1)
|
||
} else if (shortcutKeyEnum === 13) {
|
||
// 适应图像
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].resize(false)
|
||
} else if (shortcutKeyEnum === 14) {
|
||
// 适应窗口
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].resize(true)
|
||
} else if (shortcutKeyEnum === 15) {
|
||
// 截图
|
||
} else if (shortcutKeyEnum === 16) {
|
||
// 反色
|
||
this.toggleInvert()
|
||
} else if (shortcutKeyEnum === 17) {
|
||
// 窗宽/窗位
|
||
const wwwcIdx = this.$refs[`viewport-${this.activeViewportIndex}`][0].wwwcIdx
|
||
const newWwwcIdx = wwwcIdx === this.wwwcArr.length - 1 ? 3 : wwwcIdx + 1
|
||
const wwwcTpl = this.wwwcArr[newWwwcIdx]
|
||
this.changeVoiRange(wwwcTpl)
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].setWwwcIdx(newWwwcIdx)
|
||
} else if (shortcutKeyEnum === 18) {
|
||
// 重置
|
||
this.resetViewport()
|
||
} else if (shortcutKeyEnum === 19) {
|
||
// 显示或隐藏标记
|
||
this.isShowAnnotations = !this.isShowAnnotations
|
||
if (this.isShowAnnotations) {
|
||
visibility.showAllAnnotations()
|
||
} else {
|
||
const annotationUIDs = annotation.state.getAllAnnotations().map((a) => a.annotationUID)
|
||
annotationUIDs.forEach((annotationUID) => {
|
||
visibility.setAnnotationVisibility(annotationUID, false)
|
||
})
|
||
}
|
||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||
const viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
|
||
renderingEngine.renderViewports(viewportIds)
|
||
}
|
||
event.stopImmediatePropagation()
|
||
event.stopPropagation()
|
||
event.preventDefault()
|
||
})
|
||
},
|
||
// 重置热键信息
|
||
resetHotkeyList(arr) {
|
||
this.hotKeyList = []
|
||
arr.map(item => {
|
||
this.hotKeyList.push({ id: item.id, altKey: item.keys.controlKey.altKey, ctrlKey: item.keys.controlKey.ctrlKey, shiftKey: item.keys.controlKey.shiftKey, metaKey: item.keys.controlKey.metaKey, key: item.keys.controlKey.key, code: item.keys.controlKey.code, text: item.keys.text, shortcutKeyEnum: item.label })
|
||
})
|
||
},
|
||
fitToType(forceFitToWindow) {
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].resize(forceFitToWindow)
|
||
this.forceFitToWindow = !forceFitToWindow
|
||
},
|
||
// 切换全屏
|
||
toggleFullScreen(e, index) {
|
||
this.fullScreenIndex = this.fullScreenIndex === index ? null : index
|
||
this.activeViewportIndex = index
|
||
},
|
||
async toggleTask(taskInfo, taskIndex) {
|
||
if (taskIndex === this.activeTaskIndex) return
|
||
if (!this.selectArr.includes(taskInfo.VisitTaskId)) {
|
||
this.selectArr.push(taskInfo.VisitTaskId)
|
||
}
|
||
this.activeTaskId = taskInfo.VisitTaskId
|
||
this.activeTaskIndex = taskIndex
|
||
if (!taskInfo.IsInit) {
|
||
this.sLoading = true
|
||
try {
|
||
const res = await this.loadTaskDetails(taskInfo, taskIndex)
|
||
const studyList = res ? res.studyList : []
|
||
const annotations = res ? res.annotations : []
|
||
const annotationUIDs = res ? res.annotationUIDs : []
|
||
const keyImages = res ? res.keyImages : []
|
||
this.$set(taskInfo, 'StudyList', studyList)
|
||
this.$set(taskInfo, 'Annotations', annotations)
|
||
this.$set(taskInfo, 'KeyImages', keyImages)
|
||
this.$set(taskInfo, 'AnnotationUIDs', annotationUIDs)
|
||
this.$set(taskInfo, 'IsInit', true)
|
||
|
||
this.sLoading = false
|
||
} catch (e) {
|
||
this.sLoading = false
|
||
}
|
||
}
|
||
this.setToolsPassive()
|
||
},
|
||
async toggleTaskByViewport(obj) {
|
||
const i = this.visitTaskList.findIndex(v => v.VisitTaskNum === obj.visitTaskNum)
|
||
if (i === -1) return
|
||
this.activeTaskId = this.visitTaskList[i].VisitTaskId
|
||
this.activeTaskIndex = i
|
||
if (!this.selectArr.includes(this.activeTaskId)) {
|
||
this.selectArr.push(this.activeTaskId)
|
||
}
|
||
if (!this.visitTaskList[i].IsInit) {
|
||
this.sLoading = true
|
||
try {
|
||
const res = await this.loadTaskDetails(this.visitTaskList[i], i)
|
||
const studyList = res ? res.studyList : []
|
||
const annotations = res ? res.annotations : []
|
||
const annotationUIDs = res ? res.annotationUIDs : []
|
||
const keyImages = res ? res.keyImages : []
|
||
this.$set(this.visitTaskList[i], 'StudyList', studyList)
|
||
this.$set(this.visitTaskList[i], 'Annotations', annotations)
|
||
this.$set(this.visitTaskList[i], 'KeyImages', keyImages)
|
||
this.$set(this.visitTaskList[i], 'AnnotationUIDs', annotationUIDs)
|
||
this.$set(this.visitTaskList[i], 'IsInit', true)
|
||
this.sLoading = false
|
||
} catch (e) {
|
||
this.sLoading = false
|
||
}
|
||
}
|
||
const series = this.getRelatedSeries(this.visitTaskList[i], obj.series)
|
||
this.$nextTick(() => {
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].setSeriesInfo(series)
|
||
this.$refs[this.activeTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
|
||
if (this.activeViewportIndex === this.cells.length - 1) {
|
||
this.lastViewportTaskId = series.TaskInfo.VisitTaskId
|
||
this.currentVisitInfo = series.TaskInfo
|
||
}
|
||
})
|
||
this.setToolsPassive()
|
||
},
|
||
async activeSeries(obj) {
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
|
||
this.clipPlaying = false
|
||
this.fps = 15
|
||
if (this.activeViewportIndex === this.cells.length - 1) {
|
||
this.lastViewportTaskId = obj.TaskInfo.VisitTaskId
|
||
this.currentVisitInfo = obj.TaskInfo
|
||
}
|
||
this.setToolsPassive()
|
||
},
|
||
async showMultiFrame(obj) {
|
||
this.$refs[`viewport-${this.activeViewportIndex}`][0].setSeriesInfo(obj, true)
|
||
this.clipPlaying = false
|
||
this.fps = 15
|
||
if (this.activeViewportIndex === this.cells.length - 1) {
|
||
this.lastViewportTaskId = obj.TaskInfo.VisitTaskId
|
||
this.currentVisitInfo = obj.TaskInfo
|
||
}
|
||
this.setToolsPassive()
|
||
},
|
||
// 激活视口
|
||
activeViewport(index) {
|
||
if (this.activeViewportIndex === index) return
|
||
this.activeViewportIndex = index
|
||
// 切换检查列表
|
||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||
if (series) {
|
||
const i = this.visitTaskList.findIndex(v => v.VisitTaskId === series.TaskInfo.VisitTaskId)
|
||
if (i > -1) {
|
||
this.activeTaskId = series.TaskInfo.VisitTaskId
|
||
this.activeTaskIndex = i
|
||
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
|
||
}
|
||
}
|
||
this.setToolsPassive()
|
||
},
|
||
getRelatedSeries(visitTaskInfo, baselineSeries) {
|
||
let obj = {}
|
||
const studyList = visitTaskInfo.StudyList
|
||
const annotations = visitTaskInfo.Annotations
|
||
if (baselineSeries) {
|
||
const seriesList = studyList.map(s => s.IsDicom && s.SeriesList).flat()
|
||
if (baselineSeries.IsMarked) {
|
||
const i = annotations.findIndex(i => Object.keys(i.MeasureData).length > 0 && i.OrderMarkName === baselineSeries.MeasureData.OrderMarkName)
|
||
if (i > -1) {
|
||
obj = this.getMarkedSeries(studyList, annotations[i])
|
||
}
|
||
}
|
||
if (Object.keys(obj).length === 0) {
|
||
// 筛选描述相似
|
||
let similarArr = seriesList.map((i, index) => {
|
||
return { similar: this.strSimilarity2Percent(i.Description, baselineSeries.Description), index }
|
||
})
|
||
similarArr = similarArr.sort((a, b) => {
|
||
return b.similar - a.similar
|
||
})
|
||
const idx = similarArr[0] && similarArr[0].similar > 0.85 ? similarArr[0].index : -1
|
||
if (idx > -1) {
|
||
obj = Object.assign({}, seriesList[idx])
|
||
const stack = seriesList[idx].Stack
|
||
const sliceIdx = Math.floor(stack.length * ((baselineSeries.SliceIndex + 1) / baselineSeries.Stack.length))
|
||
// let sliceIdx = Math.floor(imageIds.length / 2)
|
||
obj.SliceIndex = sliceIdx > 0 ? sliceIdx - 1 : 0
|
||
}
|
||
}
|
||
if (Object.keys(obj).length === 0) {
|
||
// 筛选层厚一致
|
||
const idx = seriesList.findIndex(series => series.InstanceCount === baselineSeries.InstanceCount)
|
||
if (idx > -1) {
|
||
obj = Object.assign({}, seriesList[idx])
|
||
const stack = seriesList[idx].Stack
|
||
const sliceIdx = Math.floor(stack.length / 2)
|
||
obj.SliceIndex = sliceIdx > 0 ? sliceIdx - 1 : 0
|
||
}
|
||
}
|
||
if (Object.keys(obj).length === 0) {
|
||
// 筛选层厚为5的序列
|
||
const idx = seriesList.findIndex(series => series.IsDicom && parseInt(series.SliceThickness) === 5)
|
||
if (idx > -1) {
|
||
obj = Object.assign({}, seriesList[idx])
|
||
const stack = seriesList[idx].Stack
|
||
const sliceIdx = Math.floor(stack.length / 2)
|
||
obj.SliceIndex = sliceIdx > 0 ? sliceIdx - 1 : 0
|
||
}
|
||
}
|
||
} else {
|
||
const i = annotations.findIndex(a => Object.keys(a.MeasureData).length > 0)
|
||
if (i > -1) {
|
||
// 有标记时,显示带标记影像所在序列
|
||
obj = this.getMarkedSeries(studyList, annotations[i])
|
||
} else {
|
||
for (let k = 0; k < studyList.length; k++) {
|
||
const seriesIdx = studyList[k].SeriesList.findIndex(s => s.SliceThickness && parseInt(s.SliceThickness) === 5)
|
||
if (seriesIdx > -1) {
|
||
obj = Object.assign({}, studyList[k].SeriesList[seriesIdx])
|
||
const stack = studyList[k].SeriesList[seriesIdx].Stack
|
||
const sliceIdx = Math.floor(stack.length / 2)
|
||
obj.SliceIndex = sliceIdx > 0 ? sliceIdx - 1 : 0
|
||
obj.IsMarked = false
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (Object.keys(obj).length === 0) {
|
||
const sIdx = studyList.findIndex(s => s.IsDicom)
|
||
if (sIdx > -1) {
|
||
obj = Object.assign({}, studyList[sIdx].SeriesList[0])
|
||
const stack = studyList[sIdx].SeriesList[0].Stack
|
||
const sliceIdx = Math.floor(stack.length / 2)
|
||
obj.SliceIndex = sliceIdx > 0 ? sliceIdx - 1 : 0
|
||
obj.IsMarked = false
|
||
}
|
||
}
|
||
return obj
|
||
},
|
||
getMarkedSeries(studyList, annotation) {
|
||
let obj = {}
|
||
const sIdx = studyList.findIndex(s => s.StudyId === annotation.StudyId)
|
||
if (sIdx > -1) {
|
||
const seriesList = studyList[sIdx].SeriesList
|
||
const seriesIdx = seriesList.findIndex(s => s.Id === annotation.SeriesId)
|
||
if (seriesIdx > -1) {
|
||
const instanceIdx = seriesList[seriesIdx].InstanceInfoList.findIndex(i => i.Id === annotation.InstanceId)
|
||
let filterStr = ''
|
||
if (instanceIdx > -1 && seriesList[seriesIdx].IsExistMutiFrames) {
|
||
if (seriesList[seriesIdx].InstanceInfoList[instanceIdx].NumberOfFrames > 0) {
|
||
filterStr = `frame=${annotation.MeasureData.frame}&instanceId=${annotation.InstanceId}`
|
||
} else {
|
||
filterStr = `instanceId=${annotation.InstanceId}`
|
||
}
|
||
} else {
|
||
filterStr = `instanceId=${annotation.InstanceId}`
|
||
}
|
||
const stack = seriesList[seriesIdx].Stack
|
||
const imageIdIdx = stack.findIndex(is => is.includes(filterStr))
|
||
if (imageIdIdx > -1) {
|
||
obj = Object.assign({}, seriesList[seriesIdx])
|
||
obj.SliceIndex = imageIdIdx
|
||
obj.IsMarked = true
|
||
obj.MeasureData = annotation
|
||
}
|
||
}
|
||
}
|
||
return obj
|
||
},
|
||
async getScreenshots(measureData, callback) {
|
||
if (measureData) {
|
||
await this.imageLocation(measureData)
|
||
setTimeout(async() => {
|
||
const divForDownloadViewport = document.querySelector(
|
||
`div[data-viewport-uid="viewport-${this.activeViewportIndex}"]`
|
||
)
|
||
const canvas = await html2canvas(divForDownloadViewport)
|
||
const base64Str = canvas.toDataURL('image/png', 1)
|
||
callback(base64Str)
|
||
}, 50)
|
||
} else {
|
||
callback()
|
||
}
|
||
},
|
||
imageLocation(obj) {
|
||
return new Promise(async resolve => {
|
||
let loading = null
|
||
const index = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
|
||
if (index === -1) {
|
||
resolve()
|
||
return
|
||
}
|
||
if (obj.annotationUID) {
|
||
obj.StudyId = obj.studyId
|
||
obj.SeriesId = obj.seriesId
|
||
obj.InstanceId = obj.instanceId
|
||
obj.isMarked = true
|
||
} else {
|
||
obj.isMarked = false
|
||
}
|
||
|
||
let firstAddSeries = {}
|
||
let currentAddSeries = {}
|
||
if (this.taskInfo.IsReadingTaskViewInOrder === 1) {
|
||
// 有序
|
||
// 获取病灶第一次出现的访视序列
|
||
let firstAddVisitTaskId = null
|
||
const idx = this.visitTaskList[index].Annotations.findIndex(item => {
|
||
return item.OrderMarkName === obj.lesionName
|
||
})
|
||
if (idx > -1) {
|
||
firstAddVisitTaskId = this.visitTaskList[index].Annotations[idx].FristAddTaskId
|
||
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === firstAddVisitTaskId)
|
||
if (taskIdx > -1 && !this.visitTaskList[taskIdx].IsInit) {
|
||
loading = this.$loading({ fullscreen: true })
|
||
try {
|
||
const res = await this.loadTaskDetails(this.visitTaskList[taskIdx], taskIdx)
|
||
const studyList = res ? res.studyList : []
|
||
const annotations = res ? res.annotations : []
|
||
const annotationUIDs = res ? res.annotationUIDs : []
|
||
const keyImages = res ? res.keyImages : []
|
||
this.$set(this.visitTaskList[taskIdx], 'StudyList', studyList)
|
||
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
|
||
this.$set(this.visitTaskList[taskIdx], 'KeyImages', keyImages)
|
||
this.$set(this.visitTaskList[taskIdx], 'AnnotationUIDs', annotationUIDs)
|
||
this.$set(this.visitTaskList[taskIdx], 'IsInit', true)
|
||
loading.close()
|
||
} catch (e) {
|
||
loading.close()
|
||
}
|
||
}
|
||
const annotationIdx = this.visitTaskList[taskIdx].Annotations.findIndex(i => i.OrderMarkName === obj.lesionName)
|
||
if (annotationIdx > -1) {
|
||
firstAddSeries = this.getMarkedSeries(this.visitTaskList[taskIdx].StudyList, this.visitTaskList[taskIdx].Annotations[annotationIdx])
|
||
}
|
||
}
|
||
if (obj.isMarked) {
|
||
currentAddSeries = this.getMarkedSeries(this.visitTaskList[index].StudyList, obj)
|
||
}
|
||
|
||
if (Object.keys(firstAddSeries).length === 0 && Object.keys(currentAddSeries).length !== 0) {
|
||
firstAddSeries = currentAddSeries
|
||
} else if (Object.keys(firstAddSeries).length === 0 && Object.keys(currentAddSeries).length === 0) {
|
||
this.setToolsPassive()
|
||
if (obj.isActiveTarget) {
|
||
this.setToolToTarget(obj)
|
||
}
|
||
resolve()
|
||
return
|
||
} else if (Object.keys(firstAddSeries).length !== 0 && Object.keys(currentAddSeries).length === 0) {
|
||
// 当前访视序列与首次出现病灶的序列对齐
|
||
// currentAddSeries = this.getRelatedSeries(this.visitTaskList[index], firstAddSeries)
|
||
|
||
// if (Object.keys(currentAddSeries).length === 0) {
|
||
// // 未找到对齐的,则就显示当前最后一个窗口现实的序列信息
|
||
// currentAddSeries = this.$refs[`viewport-${this.cells.length - 1}`][0].series
|
||
// }
|
||
|
||
// 显示当前序列
|
||
currentAddSeries = this.$refs[`viewport-${this.cells.length - 1}`][0].series
|
||
}
|
||
} else {
|
||
// 无序
|
||
currentAddSeries = this.getMarkedSeries(this.visitTaskList[index].StudyList, obj)
|
||
if (Object.keys(currentAddSeries).length === 0) {
|
||
// 未找到标注序列的,则就显示当前最后一个窗口现实的序列信息cells.length - 1
|
||
currentAddSeries = this.$refs[`viewport-${this.cells.length - 1}`][0].series
|
||
}
|
||
firstAddSeries = currentAddSeries
|
||
}
|
||
for (let i = 0; i < this.cells.length; i++) {
|
||
if (i === this.cells.length - 1) {
|
||
this.$refs[`viewport-${i}`][0].setSeriesInfo(currentAddSeries, true)
|
||
this.activeViewportIndex = i
|
||
this.$refs[currentAddSeries.TaskInfo.VisitTaskId][0].setSeriesActive(currentAddSeries.StudyIndex, currentAddSeries.SeriesIndex)
|
||
} else {
|
||
this.$refs[`viewport-${i}`][0].setSeriesInfo(firstAddSeries, true)
|
||
}
|
||
}
|
||
this.setToolsPassive()
|
||
if (obj.isActiveTarget) {
|
||
this.setToolToTarget(obj)
|
||
}
|
||
|
||
resolve()
|
||
})
|
||
},
|
||
setToolToTarget(obj) {
|
||
console.log('setToolToTarget')
|
||
if (obj.visitTaskId === this.taskInfo.VisitTaskId && this.readingTaskState !== 2 && obj.markTool && !obj.isMarked) {
|
||
const toolName = obj.markTool
|
||
if (this.activeTool) {
|
||
this.setToolsPassive()
|
||
}
|
||
const toolGroupId = `viewport-${this.activeViewportIndex}`
|
||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||
toolGroup.setToolActive(toolName, {
|
||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||
})
|
||
this.activeTool = toolName
|
||
}
|
||
},
|
||
setReadingTaskState(state) {
|
||
this.readingTaskState = state
|
||
},
|
||
// 两个字符串的相似程度,并返回相似度百分比
|
||
strSimilarity2Percent(s, t) {
|
||
var l = s.length > t.length ? s.length : t.length
|
||
var d = this.strSimilarity2Number(s, t)
|
||
return Number((1 - d / l).toFixed(4))
|
||
},
|
||
strSimilarity2Number(s, t) {
|
||
var n = s.length; var m = t.length; var d = []
|
||
var i, j, s_i, t_j, cost
|
||
if (n === 0) return m
|
||
if (m === 0) return n
|
||
for (i = 0; i <= n; i++) {
|
||
d[i] = []
|
||
d[i][0] = i
|
||
}
|
||
for (j = 0; j <= m; j++) {
|
||
d[0][j] = j
|
||
}
|
||
for (i = 1; i <= n; i++) {
|
||
s_i = s.charAt(i - 1)
|
||
for (j = 1; j <= m; j++) {
|
||
t_j = t.charAt(j - 1)
|
||
if (s_i === t_j) {
|
||
cost = 0
|
||
} else {
|
||
cost = 1
|
||
}
|
||
d[i][j] = this.Minimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost)
|
||
}
|
||
}
|
||
return d[n][m]
|
||
},
|
||
Minimum(a, b, c) {
|
||
return a < b ? (a < c ? a : c) : (b < c ? b : c)
|
||
},
|
||
// 手册
|
||
previewManuals() {
|
||
this.manualsDialog.isFullscreen = false
|
||
this.manualsDialog.visible = true
|
||
},
|
||
// 临床数据
|
||
previewCD(taskId) {
|
||
this.isClinicalDataFullscreen = false
|
||
this.dialogVisible = true
|
||
this.cdVisitTaskId = taskId
|
||
this.clinicalDataVisible = true
|
||
},
|
||
// 个性化配置
|
||
previewConfig() {
|
||
this.personalConfigDialog.activeName = '1'
|
||
this.personalConfigDialog.visible = true
|
||
},
|
||
getInstanceInfo(imageId) {
|
||
const params = {}
|
||
const searchParams = new URLSearchParams(imageId.split('?')[1])
|
||
for (const [key, value] of searchParams.entries()) {
|
||
params[key] = value
|
||
}
|
||
if (isNaN(params.frame)) {
|
||
params.frame = null
|
||
}
|
||
return params
|
||
},
|
||
// 箭头工具输入标记名称自定义弹窗
|
||
async customPrompt() {
|
||
try {
|
||
// 请输入标记名称
|
||
const { value } = await this.$prompt(this.$t('trials:noneDicom:message:msg1'))
|
||
return value
|
||
} catch {
|
||
return null
|
||
}
|
||
},
|
||
showPanel(e) {
|
||
e.currentTarget.firstChild.lastChild.style.display = 'block'
|
||
},
|
||
toolMouseout(e) {
|
||
e.currentTarget.firstChild.lastChild.style.display = 'none'
|
||
},
|
||
debounce(callback, delay) {
|
||
let timerId
|
||
return function() {
|
||
clearTimeout(timerId)
|
||
timerId = setTimeout(() => {
|
||
callback.apply(this, arguments)
|
||
}, delay)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
.read-page-container {
|
||
box-sizing: border-box;
|
||
height: 100%;
|
||
display: flex;
|
||
::-webkit-scrollbar {
|
||
width: 5px;
|
||
height: 5px;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
border-radius: 10px;
|
||
background: #d0d0d0;
|
||
}
|
||
.left-panel {
|
||
display: flex;
|
||
width: 200px;
|
||
border: 1px solid #727272;
|
||
color: #fff;
|
||
user-select: none;
|
||
::-webkit-scrollbar {
|
||
width: 3px;
|
||
height: 3px;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
border-radius: 10px;
|
||
background: #d0d0d0;
|
||
}
|
||
.task-container {
|
||
position: relative;
|
||
width: 25px;
|
||
overflow-y: auto;
|
||
}
|
||
.task-info {
|
||
position: absolute;
|
||
top: 5px;
|
||
right: 20px;
|
||
transform-origin: right top;
|
||
transform: rotate(-90deg);
|
||
display: flex;
|
||
.task-item {
|
||
margin-left: 10px;
|
||
white-space: nowrap;
|
||
padding: 0px 4px;
|
||
border: 1px solid #999999;
|
||
border-bottom:none ;
|
||
text-align: center;
|
||
background-color: #4e4e4e;
|
||
color: #d5d5d5;
|
||
cursor: pointer;
|
||
}
|
||
.task-item-active {
|
||
background-color: #607d8b;
|
||
border: 1px solid #607d8b;
|
||
}
|
||
}
|
||
.study-info {
|
||
width: 170px;
|
||
border-left: 1px solid #727272;
|
||
}
|
||
}
|
||
.middle-panel {
|
||
flex: 1;
|
||
border: 1px solid #727272;
|
||
margin: 0 5px;
|
||
user-select: none;
|
||
.dicom-viewer {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width:100%;
|
||
height: 100%;
|
||
user-select: none;
|
||
.tools-wrapper {
|
||
height: 50px;
|
||
display: flex;
|
||
align-items: center;
|
||
border-bottom: 1px solid #727272;
|
||
color: #ddd;
|
||
padding: 0 5px;
|
||
.tools-left {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
}
|
||
.tool-item {
|
||
padding: 5px;
|
||
margin: 0 5px;
|
||
border: 1px solid #333;
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
.el-dropdown-link {
|
||
.svg-icon {
|
||
color: #ddd;
|
||
font-size: 20px;
|
||
}
|
||
}
|
||
}
|
||
.tool-item-active {
|
||
background-color: #607d8b;
|
||
}
|
||
.tool-disabled {
|
||
cursor: not-allowed;
|
||
}
|
||
.tool-frame{
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
// margin-right: 20px;
|
||
padding: 1px;
|
||
margin: 0 5px;
|
||
border: 1px solid #404040;
|
||
.icon{
|
||
padding: 5px;
|
||
border-right: 1px solid #404040;
|
||
cursor: pointer;
|
||
text-align: center;
|
||
.svg-icon{
|
||
font-size:20px;
|
||
color:#ddd;
|
||
}
|
||
}
|
||
.select-wrapper{
|
||
width: 60px;
|
||
background-color: black;
|
||
color: #ddd;
|
||
border: none;
|
||
font-size: 13px;
|
||
outline: none;
|
||
}
|
||
.text{
|
||
position: relative;
|
||
font-size: 12px;
|
||
margin-top: 5px;
|
||
color: #d0d0d0;
|
||
display: none;
|
||
}
|
||
}
|
||
// .icon:hover{
|
||
// background-color: #607d8b;
|
||
// }
|
||
.dropdown {
|
||
position: relative;
|
||
display: inline-block;
|
||
.text{
|
||
text-align: center;
|
||
}
|
||
}
|
||
.dropdown-content {
|
||
display: none;
|
||
position: absolute;
|
||
background-color: #383838;
|
||
color: #fff;
|
||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||
z-index: 9999;
|
||
font-size: 12px;
|
||
ul{
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
text-align: center;
|
||
li{
|
||
a{
|
||
display: block;
|
||
padding: 5px;
|
||
}
|
||
}
|
||
}
|
||
ul li a:hover{
|
||
background-color: #727272;
|
||
}
|
||
}
|
||
.layout-content-ul li:hover{
|
||
background-color: #727272;
|
||
}
|
||
.layout-content ul li{
|
||
border-top:1px solid #ddd;
|
||
border-left:1px solid #ddd;
|
||
}
|
||
.layout_flex_row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
text-align: center;
|
||
margin-bottom: 2px;
|
||
}
|
||
.layout_flex_column{
|
||
display: flex;
|
||
justify-content: space-between;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-bottom: 2px;
|
||
}
|
||
.layout_box_1_1{
|
||
flex:1;
|
||
line-height: 30px;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
border-bottom:1px solid #ddd;
|
||
border-right:1px solid #ddd;
|
||
}
|
||
.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;
|
||
}
|
||
.divider{
|
||
display: block;
|
||
height: 1px;
|
||
width: 100%;
|
||
margin: 10px 0;
|
||
}
|
||
.el-divider__text{
|
||
padding: 0 10px;
|
||
font-size: 12px;
|
||
width: 80px;
|
||
background-color: #383838;
|
||
color: #fff;
|
||
}
|
||
}
|
||
.viewports-wrapper {
|
||
flex: 1;
|
||
.grid-container {
|
||
display: grid;
|
||
height: 100%;
|
||
width: 100%;
|
||
position: relative;
|
||
}
|
||
|
||
.grid-cell {
|
||
border: 1px dashed #ccc;;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.cell_active {
|
||
border-color: #fafa00!important;
|
||
}
|
||
.cell-full-screen {
|
||
grid-column: 1 / -1;
|
||
grid-row: 1 / -1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.right-panel {
|
||
width: 400px;
|
||
border: 1px solid #727272;
|
||
overflow-y: auto;
|
||
user-select: none;
|
||
}
|
||
.personal_config {
|
||
::v-deep .el-tabs__content{
|
||
height: 450px;
|
||
overflow-y: auto;
|
||
}
|
||
}
|
||
::v-deep .manuals-dialog-container{
|
||
margin-top: 50px !important;
|
||
width:75%;
|
||
height:80%;
|
||
.el-dialog__body{
|
||
padding: 10px;
|
||
height: calc(100% - 50px) !important;
|
||
}
|
||
.el-dialog__header{
|
||
position: relative;
|
||
}
|
||
}
|
||
::v-deep .manuals-full-dialog-container{
|
||
.el-dialog__body{
|
||
padding: 10px;
|
||
height: calc(100% - 50px) !important;
|
||
}
|
||
.el-dialog__header{
|
||
position: relative;
|
||
}
|
||
}
|
||
::v-deep .cd-dialog-container{
|
||
background: #fff !important;
|
||
margin-top: 50px !important;
|
||
width: 75%;
|
||
height: 80%;
|
||
.el-dialog__body{
|
||
padding: 20px 20px 0 20px;
|
||
height: calc(100% - 70px);
|
||
}
|
||
.el-dialog__header{
|
||
position: relative;
|
||
}
|
||
}
|
||
|
||
::v-deep .cd-full-dialog-container{
|
||
background: #fff !important;
|
||
.el-dialog__body{
|
||
padding: 10px;
|
||
height: calc(100% - 50px) !important;
|
||
}
|
||
.el-dialog__header{
|
||
position: relative;
|
||
}
|
||
}
|
||
::v-deep .el-dropdown-menu{
|
||
.el-dropdown-menu__item {
|
||
padding: 0 5px;
|
||
}
|
||
.layout_flex_row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
width: 40px;
|
||
align-items: center;
|
||
text-align: center;
|
||
}
|
||
.layout_flex_column{
|
||
display: flex;
|
||
justify-content: space-between;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.layout_box_1_1{
|
||
flex:1;
|
||
line-height: 30px;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
// border-bottom:1px solid #ddd;
|
||
// border-right:1px solid #ddd;
|
||
}
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
|
||
</style>
|