<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="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" :marked-series-ids="markedSeriesIds" :readingTool="readingTool"
            @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', isFusion ? 'tool-disabled' : '']" :title="$t('trials:reading:button:layout')"
              @click.stop="showPanel($event, 'layout')" @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 class="wwwc-list">
                    <li v-for="item in wwwcArr" :key="item.label">
                      <span @click.stop="changeVoiRange(item)">
                        <div v-if="item.wc !== null" class="wwwc-item-content">
                          <div class="wwwc-item-label">{{ item.label }}</div>
                          <div class="wwwc-item-value">{{ `${item.ww} / ${item.wc}` }}</div>
                        </div>
                        <div v-else style="text-align:left;">
                          {{ item.label }}
                        </div>
                      </span>
                      <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 class="rotate-options-list">
                    <li v-for="rotate in rotateOptions" :key="rotate.label" style="text-align:left;">
                      <span @click.prevent="setViewportRotate(rotate.val)">
                        <svg-icon v-if="rotate.icon" :icon-class="rotate.icon" class="svg-icon" />
                        {{ rotate.label }}
                      </span>
                    </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-if="!isFusion && !isMPR && cells.length > 1"
              :class="['tool-item', isScrollSync ? 'tool-item-active' : '']"
              :title="$t('trials:reading:button:imageIndexSync')" @click.prevent="setImageIndexSync">
              <i v-if="isScrollSync" class="el-icon-lock svg-icon" />
              <i v-else class="el-icon-unlock svg-icon" />
            </div>
            <!-- 截图 -->
            <div class="tool-item" :title="$t('trials:reading:button:screenShot')" @click.prevent="saveImage">
              <svg-icon icon-class="image" class="svg-icon" />
            </div>
            <!-- MPR -->
            <div class="tool-item" :title="`${$t('trials:reading:button:mpr')}`" @click.prevent="openMPRViewport()"
              v-if="((criterionType === 0 && readingTool === 0) || this.readingTool === 3) && !isMPR">
              <svg-icon icon-class="mpr" class="svg-icon" style="transform: rotate(180deg);" />
            </div>
            <!-- 退出MPR -->
            <div class="tool-item" :title="`${$t('trials:reading:button:exit_mpr')}`" @click.prevent="openMPRViewport()"
              v-if="((criterionType === 0 && readingTool === 0) || this.readingTool === 3) && isMPR">
              <svg-icon icon-class="exit_mpr" class="svg-icon" style="transform: rotate(180deg);" />
            </div>
            <!-- 关键序列 -->
            <el-popover v-model="keySeriesPopoverVisible" placement="bottom" trigger="click"
              popper-class="key-series-popper" v-if="!isFusion && !isMPR" @show="showKeySeriesPanel">
              <ul class="key-series-list">
                <li v-if="keyTaskInfo && keyTaskInfo.TaskBlindName" class="key-series-header">
                  <div class="key-series-header-top">
                    <div class="key-series-header-title">
                      <span class="key-series-header-dot"></span>
                      <div class="key-series-task-name" :title="keyTaskInfo.TaskBlindName">
                        {{ keyTaskInfo.TaskBlindName }}
                      </div>
                    </div>
                  </div>
                </li>
                <li v-if="keySeriesLoading" class="key-series-empty">
                  <i class="el-icon-loading"></i>
                </li>
                <li v-else-if="keySeries.length === 0" class="key-series-empty">
                  <!-- 暂无关键序列 -->
                  {{ $t('trials:reading:keySeries:noData') }}
                </li>
                <li v-for="(kf, idx) in keySeries" :key="idx" class="key-series-item"
                  @click.stop.prevent="jumpToKeySeries(kf)">
                  <div class="key-series-img">
                    <img v-if="kf.PicturePath" :src="`${OSSclientConfig.basePath}${kf.PicturePath}`" />
                    <svg-icon v-else icon-class="image" class="svg-icon default-icon" />
                  </div>
                  <div class="key-series-info">
                    <div class="key-series-meta" v-if="kf.MarkName">
                      <!-- 标注名称 -->
                      <div class="key-series-label">{{ $t('trials:reading:keySeries:annotationName') }}</div>
                      <div class="key-series-uid" :title="kf.MarkName">
                        {{ kf.MarkName }}
                      </div>
                    </div>
                    <div class="key-series-meta">
                      <!-- 图像号 -->
                      <div class="key-series-label">{{ $t('trials:reading:keySeries:sopUid') }}</div>
                      <div class="key-series-uid" :title="kf.SopInstanceUid">
                        {{ kf.SopInstanceUid }}
                      </div>
                    </div>
                  </div>
                  <div class="key-series-star">
                    <svg-icon icon-class="star" class="svg-icon" />
                  </div>
                </li>
              </ul>
              <div slot="reference" class="tool-item">
                <div class="icon" :title="$t('trials:reading:button:keySeries')">
                  <!-- <svg-icon icon-class="star" class="svg-icon" /> -->
                  <i class="el-icon-star-off svg-icon"></i>
                </div>
              </div>
            </el-popover>
            <!-- 直方图 -->
            <div class="tool-item" :title="`${$t('trials:reading:button:histogram')}`" @click.prevent="openHistogram"
              v-if="this.readingTool === 3">
              <svg-icon icon-class="histogram" class="svg-icon" />
            </div>
            <!-- 十字准星 -->
            <div :class="['tool-item', activeTool === 'Crosshairs' ? 'tool-item-active' : '']" v-if="isMPR"
              :title="$t('trials:reading:button:crosshairs')" @click.prevent="setToolActive('Crosshairs')">
              <svg-icon icon-class="crosshairs" class="svg-icon" />
            </div>
            <!--融合-->
            <div v-if="readingTool === 2" class="tool-item" :title="$t('trials:lugano:button:fusion')"
              @click.prevent="openFusion">
              <svg-icon icon-class="fusion" class="svg-icon" />
            </div>
            <div v-if="isFusion"
              :class="['tool-item', activeTool === 'FusionJumpToPointTool' ? 'tool-item-active' : '']"
              :title="$t('trials:reading:button:crosshairsPosition')"
              @click.prevent="setToolActive('FusionJumpToPointTool')">
              <svg-icon icon-class="position" 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 v-if="criterionType === 0" :class="['tool-item', activeTool === 'Eraser' ? 'tool-item-active' : '']"
              :title="$t('trials:dicom-show:Eraser')" @click.prevent="setToolActive('Eraser')">
              <svg-icon icon-class="clear" 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"
                @change="handleClipFpsChange">
                <!-- 默认值 -->
                <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>
            <!-- 更多 :class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '']" -->
            <div v-if="criterionType === 0 && customizeStandards.length > 0" :class="['tool-item']"
              @click.stop="showPanel($event)" @mouseleave="toolMouseout">
              <div class="dropdown">
                <div class="icon" data-tool="more" :title="$t('trials:reading:button:more')">
                  <svg-icon icon-class="more" class="svg-icon" />
                  <i class="el-icon-arrow-down" style="color:#fff;" />
                </div>
                <div class="dropdown-content">
                  <!--v-if="readingTaskState < 2"-->
                  <ul class="customize-standards-list">
                    <li v-for="i in customizeStandards" :key="i.toolName" style="text-align:left;">
                      <span @click.prevent="setMoreToolActive(i.toolName)">
                        <svg-icon :icon-class="i.icon" class="svg-icon" style="margin-right: 5px; font-size: 15px;" />
                        {{ $t(i.i18nKey) }}
                      </span>
                    </li>
                  </ul>
                </div>
              </div>
            </div>
            <div class="tool-item" :title="$t('trials:reading:button:upload')"
              v-if="trialCriterion.ImageUploadEnum > 0 && readingTaskState < 2" v-hasPermi="['role:ir']">
              <div class="tool-wrapper">
                <div class="icon" @click.prevent="openUploadImage('upload')">
                  <i class="el-icon-upload2 svg-icon" />
                </div>
              </div>
            </div>
            <div v-if="trialCriterion.ImageDownloadEnum > 0" v-hasPermi="[
              'role:ir',
              'role:mim',
              'role:mc',
              'role:pm',
              'role:apm',
              'role:ea',
              'role:qa',
            ]" class="tool-item" :title="$t('trials:reading:button:download')">
              <div class="tool-wrapper">
                <div class="icon" @click.prevent="openUploadImage('download')">
                  <i class="el-icon-download svg-icon" />
                </div>
              </div>
            </div>
            <!-- 伪彩 -->
            <template v-if="readingTool === 2">
              <colorMap v-show="isFusion" ref="colorMap" :modality="fusionOverlayModality" @setColorMap="setColorMap"
                @voiChange="voiChange" />
            </template>
          </div>

          <div style="display: flex;">
            <!-- 手册 -->
            <div class="tool-item" :title="$t('trials:reading:button:handbooks')"
              v-if="taskInfo && (taskInfo.ExistsManual || taskInfo.IsHaveKeyFile) && readingTaskState < 2"
              @click.prevent="previewManuals()">
              <svg-icon icon-class="constitution" class="svg-icon" />
            </div>
            <!-- 临床数据 -->
            <div class="tool-item" :title="$t('trials:reading:button:clinicalData')"
              v-if="taskInfo && taskInfo.IsExistsClinicalData" @click.prevent="previewCD(taskInfo.VisitTaskId)">
              <svg-icon icon-class="documentation" class="svg-icon" />
            </div>
            <!-- 个性化配置 -->
            <div class="tool-item" :title="$t('trials:reading:button:customCfg')" @click.prevent="previewConfig">
              <svg-icon icon-class="individuation" class="svg-icon" />
            </div>
          </div>
        </div>
        <div class="content-wrapper">
          <!-- viewports -->
          <div class="viewports-wrapper">
            <div ref="container" class="grid-container">
              <div
                :class="['viewports-box', isFusion || isMPR ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']"
                :style="gridStyle">
                <div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-${index}`"
                  :class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
                  @dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
                  <VolumeViewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
                    :rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
                    :histogramVisible="histogramVisible" :actionConfiguration="actionConfiguration"
                    :SegmentConfig="SegmentConfig" :segmentationId.sync="segId" :segmentIndex.sync="segIndex"
                    :curSegSeries.sync="curSegSeries" :is-scroll-sync="isScrollSync"
                    @activeViewport="activeViewport"
                    @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
                    @renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
                    @resetViewport="resetViewport" @resetHistogram="resetHistogram"
                    @scrollSync="handleViewportScrollSync" v-if="readingTool === 3"
                    v-resize="(e) => handleSizeChange(e, `viewport-${index}`)" />
                  <Viewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
                    :rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
                    :is-scroll-sync="isScrollSync" @scrollSync="handleViewportScrollSync"
                    @activeViewport="activeViewport" @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
                    @renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" v-else />
                </div>
              </div>
              <div v-if="criterionType === 0 && readingTool === 0 || readingTool === 3"
                :class="['viewports-box', !isMPR ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']"
                :style="gridStyleMPR">
                <div v-for="(v, index) in 3" :key="`viewport-MPR-${index}`" v-show="index < cells.length"
                  :style="cellStyle"
                  :class="['grid-cell', index === 0 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
                  @dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
                  <MPRViewport :ref="`viewport-MPR-${index}`" :data-viewport-uid="`viewport-MPR-${index}`"
                    :rendering-engine-id="renderingEngineId" :viewport-id="`viewport-MPR-${index}`"
                    :viewport-index="index" :histogramVisible="histogramVisible"
                    :actionConfiguration="actionConfiguration" :SegmentConfig="SegmentConfig"
                    :segmentationId.sync="segId" :segmentIndex.sync="segIndex" :curSegSeries.sync="curSegSeries"
                    :MPRInfo="MPRInfo" @activeViewport="activeViewport" @setMPRInfo="setMPRInfo"
                    @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
                    @renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
                    @resetHistogram="resetHistogram" v-resize="(e) => handleSizeChange(e, `viewport-MPR-${index}`)" />
                </div>
              </div>
              <div v-if="readingTool === 2"
                :class="['viewports-box', !isFusion ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']"
                :style="gridStyle">
                <div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-fusion-${index}`"
                  :class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
                  @dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)"
                  @mouseenter="hoverFusionViewport(index)" @mouseleave="hoverFusionViewport(-1)">
                  <PetCtViewport :ref="`viewport-fusion-${index}`" :data-viewport-uid="`viewport-fusion-${index}`"
                    :rendering-engine-id="renderingEngineId" :viewport-id="`viewport-fusion-${index}`"
                    :viewport-index="index" :active-tool="activeTool" @activeViewport="activeViewport"
                    @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
                    @renderAnnotations="renderAnnotations" @upperRangeChange="upperRangeChange"
                    @contentMouseup="contentMouseup" @fusionSlicePointChange="handleFusionSlicePointChange" />
                </div>
              </div>
              <div v-if="readingTool === 2" class="fusion-hidden-viewports">
                <div ref="viewport-fusion-hidden-sag" class="fusion-hidden-viewport" />
              </div>
            </div>
          </div>
          <!-- 表单 -->
          <div class="form-wrapper">
            <el-tabs v-model="formWrapperActiveName" v-if="readingTool === 3" @tab-click="handleClick">
              <el-tab-pane :label="$t('trials:reading:dicom3D:tabs:segment')" name="segment">
                <Segmentations ref="Segmentations" :visitInfo="taskInfo" :isMPR="isMPR"
                  :volumeToolGroupId="volumeToolGroupId" :viewportKey="viewportKey" :global-loading.sync="loading"
                  :trialCriterion="trialCriterion" :loadingText.sync="loadingText"
                  :rendering-engine-id="renderingEngineId" :SegmentConfig="SegmentConfig" :segId.sync="segId"
                  :segIndex.sync="segIndex" :curSegSeries.sync="curSegSeries" :activeViewportIndex="activeViewportIndex"
                  :activeTool.sync="activeTool" :actionConfiguration="actionConfiguration"
                  :histogramVisible="histogramVisible" @setToolsPassive="setToolsPassive"
                  @resetQuestion="resetQuestion" />
              </el-tab-pane>
              <el-tab-pane :label="$t('trials:reading:dicom3D:tabs:ecrf')" name="ecrf">
                <div v-for="s in visitTaskList" v-show="lastViewportTaskId === s.VisitTaskId" :key="s.VisitTaskId"
                  style="height: 100%;">
                  <customize-question-list
                    v-if="lastViewportTaskId && criterionType === 0 && lastViewportTaskIds.includes(s.VisitTaskId)"
                    :ref="`ecrf_${s.VisitTaskId}`"
                    :reading-task-state="taskInfo && currentVisitInfo.VisitTaskId === taskInfo.VisitTaskId ? readingTaskState : 2"
                    :last-viewport-task-id="lastViewportTaskId" :visit-info="s" @resetAnnotations="resetAnnotations"
                    @setReadingTaskState="setReadingTaskState" @viewCustomAnnotationSeries="viewCustomAnnotationSeries"
                    @getCustomScreenshots="getCustomScreenshots" @setReadingToolActive="setReadingToolActive"
                    @setReadingToolPassive="setReadingToolPassive" @handleReadingChart="handleReadingChart"
                    @openSegmentForm="openSegmentForm" />
                </div>
              </el-tab-pane>
            </el-tabs>
            <template v-else>
              <div v-for="s in visitTaskList" v-show="lastViewportTaskId === s.VisitTaskId" :key="s.VisitTaskId"
                style="height: 100%;">
                <mRecisit
                  v-if="lastViewportTaskId && criterionType === 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" @handleReadingChart="handleReadingChart"
                  @setReadingToolPassive="setReadingToolPassive" />
                <recisit
                  v-else-if="lastViewportTaskId && criterionType === 1 && 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" @handleReadingChart="handleReadingChart"
                  @setReadingToolPassive="setReadingToolPassive" />
                <customize-question-list
                  v-else-if="lastViewportTaskId && criterionType === 0 && lastViewportTaskIds.includes(s.VisitTaskId)"
                  :ref="`ecrf_${s.VisitTaskId}`"
                  :reading-task-state="taskInfo && currentVisitInfo.VisitTaskId === taskInfo.VisitTaskId ? readingTaskState : 2"
                  :last-viewport-task-id="lastViewportTaskId" :visit-info="s" @resetAnnotations="resetAnnotations"
                  @setReadingTaskState="setReadingTaskState" @viewCustomAnnotationSeries="viewCustomAnnotationSeries"
                  @getCustomScreenshots="getCustomScreenshots" @setReadingToolActive="setReadingToolActive"
                  @setReadingToolPassive="setReadingToolPassive" @handleReadingChart="handleReadingChart" />
              </div>
            </template>
          </div>
        </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">
      <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="manualsDialog.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" :justKeyDoc="manualsDialog.justKeyDoc" />
        <div slot="footer" style="text-align:right;" v-if="openManuals && !ManualsClose">
          <!-- 确认 -->
          <el-button type="primary" size="mini" @click="handleSubmitKeyDoc">
            {{ $t('common:button:confirm') }}</el-button>
        </div>
      </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'" :imageToolType="1" />
        </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>
    <el-dialog :visible.sync="fusionVisible" :close-on-click-modal="false" :title="$t('trials:lugano:button:record')"
      width="850px">
      <FusionForm v-if="fusionVisible" :active-task-index="activeTaskIndex" :task-list="visitTaskList"
        @close="closeFusion" @fusion="handleFusion" />
    </el-dialog>

    <el-dialog :visible.sync="segmentVisible" :close-on-click-modal="false" :title="$t('trials:segment:title:bind')"
      width="550px">
      <SegmentForm ref="SegmentForm" v-if="segmentVisible" :visible.sync="segmentVisible" :visitInfo="segmentVisitInfo"
        @handleSegmentSave="handleSegmentSave" />
    </el-dialog>
    <!--直方图-->
    <histogram ref="histogram" v-if="readingTool === 3" :visible.sync="histogramVisible" :activeTool.sync="activeTool"
      :viewportKey="viewportKey" :rendering-engine-id="renderingEngineId" :activeViewportIndex="activeViewportIndex" />
    <!--分割可视化窗口-->
    <!-- <SurfaceViewport ref="surfaceViewport" viewportId="surfaceViewport" v-if="readingTool === 3"
      :visible.sync="surfaceVisible" :renderingEngineId="renderingEngineId" :visitInfo="taskInfo" />
    <ContourViewport ref="contourViewport" viewportId="contourViewport" v-if="readingTool === 3"
      :renderingEngineId="renderingEngineId" :visitInfo="taskInfo" /> -->
    <upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
      :subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
      :visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true" />
    <download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId"
      :subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId"
      :visible.sync="downloadImageVisible" :is-reading-task-view-in-order="isReadingTaskViewInOrder"
      :isReading="true" />
    <readingChart ref="readingChart" />
  </div>
</template>
<script>
import { getRelatedVisitTask, getReadingVisitStudyList, getTableAnswerRowInfoList, deleteCustomTag, getCriterionReadingInfo, setReadKeyFile } from '@/api/trials'
import { getDoctorShortcutKey, getUserWLTemplateList } from '@/api/user'
import { getCustomTag, submitCustomTag, getMarkList } from '@/api/reading'
import { getToken } from '@/utils/auth'
import {
  RenderingEngine,
  Enums,
  // imageLoader,
  // CONSTANTS,
  metaData,
  volumeLoader,
  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, getCustomizeStandardsTools, config } from './toolConfig'
import StudyList from './StudyList'
import Viewport from './Viewport'
import PetCtViewport from './PetCtViewport'
import MPRViewport from './MPRViewport'
import VolumeViewport from './VolumeViewport'
import Segmentations from './Segmentations'
import histogram from "./histogram"
// import SurfaceViewport from "./SurfaceViewport"
// import ContourViewport from "./ContourViewport"
import mRecisit from './mRecist/QuestionList'
import recisit from './Recist/QuestionList'
import customizeQuestionList from './customize/QuestionList'
import CustomWwwcForm from '@/views/trials/trials-panel/reading/dicoms/components/CustomWwwcForm'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
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'
import FusionForm from './FusionForm.vue'
import SegmentForm from './SegmentForm.vue'
import colorMap from './colorMap.vue'
import RectangleROITool from './tools/RectangleROITool'
import CircleROITool from './tools/CircleROITool'
import ScaleOverlayTool from './tools/ScaleOverlayTool'
import SegmentBidirectionalTool from './tools/SegmentBidirectionalTool'
import FusionJumpToPointTool from './tools/FusionJumpToPointTool'
import MIPJumpToClickTool from './tools/MIPJumpToClickTool'
import { setPTClinicalDataForInstance, clearPTClinicalDataCache } from '@/utils/ptClinicalDataCache'
import FixedRadiusCircleROITool from './tools/FixedRadiusCircleROITool'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import { getNetWorkSpeed, setNetWorkSpeedSizeAll, workSpeedclose } from "@/utils"
import readingChart from '@/components/readingChart'
import SystemInfo from "@/utils/systemInfo";
import md5 from 'js-md5'
const { visibility } = annotation
const { ViewportType, Events } = Enums
const renderingEngineId = 'myRenderingEngine'
const DEFAULT_FUSION_COLOR_MAP_RANGE = 40
const DEFAULT_FUSION_COLOR_MAP_UPPER = 6
const {
  ToolGroupManager,
  Enums: csToolsEnums,
  StackScrollTool,
  TrackballRotateTool,
  PlanarFreehandContourSegmentationTool,
  SplineContourSegmentationTool,
  // ScaleOverlayTool,
  PanTool,
  ZoomTool,
  WindowLevelTool,
  WindowLevelRegionTool,
  PlanarRotateTool,
  LengthTool,
  BidirectionalTool,
  ArrowAnnotateTool,
  // RectangleROITool,
  PlanarFreehandROITool,
  AngleTool,
  CobbAngleTool,
  EraserTool,
  VolumeRotateTool,
  CrosshairsTool,
  EllipticalROITool,
  synchronizers,
  LabelMapEditWithContourTool,
  BrushTool,
  // SegmentBidirectionalTool,
  utilities: CStUtils,
  // cursors
} = cornerstoneTools
const { createCameraPositionSynchronizer, createVOISynchronizer, createSlabThicknessSynchronizer } = synchronizers
const newStyles = {
  global: {
    color: 'rgb(255, 0, 0)',
    colorHighlighted: 'rgb(0, 255, 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(0, 255, 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, ChangeTypes } = csToolsEnums
export default {
  name: 'ReadPage',
  components: {
    StudyList,
    Viewport,
    PetCtViewport,
    MPRViewport,
    VolumeViewport,
    Segmentations,
    histogram,
    // SurfaceViewport,
    // ContourViewport,
    mRecisit,
    recisit,
    customizeQuestionList,
    CustomWwwcForm,
    Manuals,
    Hotkeys,
    WL,
    Others,
    ClinicalData,
    FusionForm,
    SegmentForm,
    colorMap,
    downloadDicomAndNonedicom,
    uploadDicomAndNonedicom,
    readingChart
  },
  props: {
    readingTool: {
      type: Number,
      default: 2
    }
  },
  data() {
    return {
      loading: false,
      trialId: '',
      visitTaskList: [],
      selectArr: [],
      taskInfo: {},
      activeTaskId: null,
      activeTaskIndex: -1,
      activeStudyIndex: -1,
      activeSeriesIndex: -1,
      currentVisitInfo: {},
      layout: 1,
      cellsMax: 4,
      rows: 1,
      cols: 1,
      fullScreenIndex: null,
      activeViewportIndex: 0,
      keySeries: [],
      keyTaskInfo: null,
      keySeriesPopoverVisible: false,
      keySeriesLoading: false,
      activeTool: '',
      readingTaskState: 2,
      renderingEngineId: renderingEngineId,
      sLoading: false,
      renderedTaskIds: [],
      criterionType: null,
      tools: [],
      rotateOptions: [
        { label: this.$t('trials:reading:button:rotateDefault'), val: 1, icon: '' },
        { label: this.$t('trials:reading:button:rotateVertical'), val: 2, icon: 'rotateVertical' },
        { label: this.$t('trials:reading:button:rotateHorizontal'), val: 3, icon: 'rotateHorizontal' },
        { label: this.$t('trials:reading:button:rotateTurnLeft'), val: 4, icon: 'rotateTurnLeft' },
        { label: this.$t('trials:reading:button:rotateTurnRight'), val: 5, icon: 'rotateTurnRight' }
      ],
      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 }
      ],
      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,
      isScrollSync: false,
      manualsDialog: { visible: false, isFullscreen: false, justKeyDoc: false },
      hotKeyList: [],
      forceFitToWindow: false,
      isShowAnnotations: true,
      clinicalDataVisible: false,
      isClinicalDataFullscreen: false,
      cdVisitTaskId: '',
      lastViewportTaskId: '',
      digitPlaces: 2,
      instanceInfo: {},
      lastViewportTaskIds: [],
      markedSeriesIds: [],
      customizeStandards: [],

      fusionVisible: false,
      isFusion: false,
      studyList: [],
      fusionSerieId: {},
      loadingText: null,
      toolNames: ['Length', 'Bidirectional', 'RectangleROI', 'ArrowAnnotate', 'CircleROI', 'Eraser'],
      // resetAnnotation: false , // 是否初始化标记 （融合时使用）
      saveCustomAnnotationTimer: null,

      // 上传
      downloadImageVisible: false,
      uploadImageVisible: false,
      uploadSubjectId: null,
      uploadSubjectCode: null,
      uploadTrialCriterion: {},
      uploadStatus: 'upload',
      taskId: '',
      isReadingTaskViewInOrder: null,
      trialCriterion: {},

      curOperation: {
        type: '',
        annotation: null
      },

      ManualsClose: false,

      isMPR: false,
      volumeToolGroupId: "share-viewport-volume",
      fusionToolGroupId: "share-viewport-fusion",
      MPRInfo: {
        AXIAL: {
          imageNum: 0
        },
        CORONAL: {
          imageNum: 0
        },
        SAGITTAL: {
          imageNum: 0
        },
      },

      segmentVisible: false,
      segmentVisitInfo: {},
      formWrapperActiveName: 'segment',
      actionConfiguration: {
        contourBidirectional: {
          method: CStUtils.segmentation.segmentContourAction,
          bindings: [
            {
              key: 'c',
            },
          ],
          data: {
            segmentData: new Map(),
            segmentationId: null
          },
        },
      },
      SegmentConfig: {
        renderOutline: true,
        renderFill: true,
        fillAlpha: 0.5,
        outlineWidth: 1,
        InactiveSegmentations: {
          show: true,
          fillAlpha: 0.3,
        }
      },
      segId: null,
      segIndex: null,
      curSegSeries: {},
      fusionOverlayModality: null,
      fusionOverlayDefaultUpper: null,
      fusionOverlayDefaultRange: null,
      fusionCrosshairStyle: {
        lineWidth: 2,
        lineLength: 20,
        centerHoleSize: 20,
      },
      lastUpper: null,
      hasFusionUpperInitialized: false,
      timer: {},
      FullTimerOut: null,
      isDelay: false,

      histogramVisible: false,
      // surfaceVisible: false
    }
  },
  computed: {
    gridStyle() {
      return {
        display: 'grid',
        gridTemplateRows: `repeat(${this.rows}, 1fr)`,
        gridTemplateColumns: `repeat(${this.cols}, 1fr)`,
        height: '100%',
        width: '100%'
      }
    },
    gridStyleMPR() {
      return {
        display: 'grid',
        gridTemplateRows: `repeat(2, 1fr)`,
        gridTemplateColumns: `repeat(2, 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)
    },
    viewportKey() {
      return this.isMPR ? 'viewport-MPR' : this.isFusion ? 'viewport-fusion' : 'viewport'
    },
    openManuals() {
      return !this.taskInfo.IsReadKeyFile && this.taskInfo.IsHaveKeyFile
    },
  },
  watch: {
    openManuals: {
      handler() {
        if (this.openManuals) this.previewManuals(true)
      },
      immediate: true
    },
    // currentReadingTaskState() { console.log(this.currentReadingTaskState, 'currentReadingTaskState') },
    activeTaskId: {
      immediate: true,
      handler(id) {
        if (!id) return
        // this.addHistoryAnnotations(id)
      }
    },
    activeViewportIndex: {
      immediate: true,
      handler(index) {
        let series = null
        if (this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`] && this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0]) {
          this.clipPlaying = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].playClipState
          series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
        } else {
          this.clipPlaying = false
          this.fps = 15
        }
        if (index === this.cells.length - 1 && series && series.TaskInfo) {
          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)
        }
      }
    },
    readingTaskState: {
      immediate: true,
      handler(state) {
        if (state === 2) {
          // 设置标记锁定
          const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
          annotations.map(annotation => {
            cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
          })
          this.setToolsPassive()
        }
      }
    },
    // histogramVisible: {
    //   handler() {
    //     if (this.readingTool !== 3) return false
    //     this.setToolsPassive()
    //     let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3', this.volumeToolGroupId]
    //     // if (this.isMPR) {
    //     //   viewportIds = [this.volumeToolGroupId]
    //     // }
    //     viewportIds.forEach(id => {
    //       const toolGroup = ToolGroupManager.getToolGroup(id)
    //       if (this.histogramVisible) {
    //         toolGroup.setToolEnabled(StackScrollTool.toolName)
    //       } else {
    //         toolGroup.setToolActive(StackScrollTool.toolName, {
    //           bindings: [{ mouseButton: MouseBindings.Wheel }]
    //         })
    //         let annotations = annotation.state.getAllAnnotations().filter(item => item.metadata.toolName.includes('histogram_'));
    //         annotations.forEach(item => {
    //           annotation.state.removeAnnotation(item.annotationUID)
    //         })
    //         for (let i = 0; i < this.cells.length; i++) {
    //           const viewportId = `${this.viewportKey}-${i}`
    //           let renderingEngine = getRenderingEngine(renderingEngineId)
    //           const viewport = renderingEngine.getViewport(viewportId)
    //           viewport.render()
    //         }
    //       }

    //     })
    //   }
    // },
  },
  mounted() {
    this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
    this.isReadingTaskViewInOrder = this.taskInfo.IsReadingTaskViewInOrder
    this.criterionType = this.taskInfo.CriterionType
    const digitPlaces = Number(localStorage.getItem('digitPlaces'))
    this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
    if (this.criterionType === 0) {
      this.tools = getCustomizeStandardsTools(this.taskInfo.ReadingToolList)
      const toolNames = this.tools.map(i => i.toolName)
      this.customizeStandards = config.customizeStandards.filter(item => !toolNames.includes(item.toolName))
    } else {
      this.tools = getTools(this.criterionType)
    }

    this.trialId = this.$route.query.trialId
    this.readingTaskState = this.taskInfo.ReadingTaskState
    if ((!this.taskInfo.IsBaseLine && this.taskInfo.IsReadingTaskViewInOrder === 1) || this.taskInfo.IsReadingTaskViewInOrder === 2) {
      this.rows = 1
      this.cols = 2
      this.activeViewportIndex = 1
    }
    if (localStorage.getItem('SegmentConfig') && this.readingTool === 3) {
      let obj = JSON.parse(localStorage.getItem('SegmentConfig'))
      this.SegmentConfig.renderOutline = obj.renderOutline
      this.SegmentConfig.renderFill = obj.renderFill
      this.SegmentConfig.fillAlpha = obj.fillAlpha
      this.SegmentConfig.outlineWidth = obj.outlineWidth
    }

    this.$nextTick(() => {
      this.loadRelatedTasks()
      this.initLoader()
      this.getWwcTpl()
      this.getHotKeys()
    })
    this.getTrialCriterion()
    DicomEvent.$on('isCanActiveNoneDicomTool', data => {
      this.open.postMessage({ type: 'isCanActiveNoneDicomTool', data: data }, window.location)
    })
    DicomEvent.$on('removeNoneDicomMeasureData', data => {
      this.open.postMessage({ type: 'removeNoneDicomMeasureData', data: data }, window.location)
    })
    DicomEvent.$on('addNoneDicomMeasureData', data => {
      this.open.postMessage({ type: 'addNoneDicomMeasureData', data: data }, window.location)
    })
    DicomEvent.$on('activeSeries', (series) => {
      this.renderedTaskIds = []
    })
    document.addEventListener("click", this.foo);
    this.getSystemInfoReading();
  },
  methods: {
    changeScreenSave() {
      return new Promise(async (reslove, reject) => {
        if (this.$refs.Segmentations.readingTaskState < 2) {
          let isSaved = this.$refs.Segmentations.segmentList.some(item => !item.isSaved)
          if (isSaved) {
            let confirm = await this.$confirm(this.$t("trials:reading:Segmentations:confirm:changeScreenSave"))
            if (!confirm) reslove(true)
            this.loading = true
            this.loadingText = this.$t("segment:loadingText:saveSegmentation")
            this.$refs.Segmentations.checkSaveLoading(() => {
              reslove(true)
            })
          } else {
            reslove(true)
          }
        } else {
          reslove(true)
        }
      })
    },
    resetHistogram() {
      if (!this.histogramVisible) return false
      if (this.timer['histogram']) {
        clearTimeout(this.timer['histogram'])
        this.timer['histogram'] = null
      }
      this.timer['histogram'] = setTimeout(() => {
        if (this.$refs.histogram && this.histogramVisible) {
          this.$refs.histogram.init()
        }
        clearTimeout(this.timer['histogram'])
        this.timer['histogram'] = null
      }, 500)

    },
    showSurface(obj) {
      // this.surfaceVisible = true
      // this.$refs.contourViewport.setSeriesInfo(obj)
      // this.$refs.surfaceViewport.setSeriesInfo(obj)
    },
    async openHistogram() {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      let viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
      const viewport = renderingEngine.getViewport(viewportId)
      let imageIds = viewport.getImageIds(this.$refs[viewportId][0].volumeId)
      let imageId = imageIds[0]
      const imagePixelModule = metaData.get('imagePixelModule', imageId);
      const photometricInterpretation = imagePixelModule?.photometricInterpretation;
      if (photometricInterpretation && photometricInterpretation !== 'MONOCHROME1' && photometricInterpretation !== 'MONOCHROME2') return this.$confirm(this.$t('trials:histogram:confirm:photometricInterpretationNotSupported'))
      this.histogramVisible = true
      this.setToolsPassive()
      this.$refs.histogram.init()

    },
    handleSizeChange(e, viewportId) {
      let index = this.$refs[viewportId][0].series.SliceIndex
      this.resetRenderingEngine(viewportId, index)
    },
    resetQuestion() {
      this.$refs[`ecrf_${this.lastViewportTaskId}`][0].getQuestions(false)
      this.$refs[`ecrf_${this.lastViewportTaskId}`][0].initSegmentBinding()
    },
    handleSegmentSave(obj) {
      this.$refs[`ecrf_${this.lastViewportTaskId}`][0].handleSegmentSave(obj)
    },
    openSegmentForm(obj) {
      let { visitInfo } = obj
      this.segmentVisitInfo = Object.assign(visitInfo, { ...obj })
      this.segmentVisible = true
      this.$nextTick(() => {
        this.$refs.SegmentForm.setSeries(this.$refs[`viewport-${this.activeViewportIndex}`][0].series)
        this.$refs.SegmentForm.init()
      })
    },
    handleClick(tab, event) {
      this.formWrapperActiveName = tab.name
      this.SegmentConfig.InactiveSegmentations.show = true
    },
    setMPRInfo(obj) {
      let { type, key, value } = obj
      this.$set(this.MPRInfo[type], key, value)
    },
    handleReadingChart(row) {
      let { e, data } = row
      let obj = Object.assign({}, data)
      obj.TrialId = this.$route.query.trialId
      obj.VisitTaskId = this.taskInfo.VisitTaskId
      let zIndex = 9
      if (obj.RowIndex) {
        zIndex = 9999
      }
      this.$refs.readingChart.init(e, obj, zIndex)
    },
    foo() {
      if (!this.$refs.readingChart) return false
      this.$refs.readingChart.foo()
    },
    async handleSubmitKeyDoc() {
      try {
        let data = {
          TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
        }
        let res = await setReadKeyFile(data)
        if (res.IsSuccess) {
          this.manualsDialog.visible = false
          this.ManualsClose = true
        }
      } catch (err) {
        console.log(err)
      }
    },
    // 加载当前任务关联的任务信息
    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.syncMarkedSeriesIds(annotations)
            }
          }

          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) {
      return new Promise(async (resolve, reject) => {
        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
          arr.forEach((study, studyIndex) => {
            // 仅对 PT/PET study 缓存临床参数，供 3D SUV 计算链路覆盖原始 DICOM 元数据
            const ptClinicalData = {
              PatientSex: study.PatientSex,
              PatientWeight: study.PatientWeight,
              RadionuclideTotalDose: study.RadionuclideTotalDose,
              RadionuclideHalfLife: study.RadionuclideHalfLife,
              RadiopharmaceuticalStartTime: study.RadiopharmaceuticalStartTime,
              AcquisitionTime: study.AcquisitionTime
            }
            const isPtStudy = ['PT、CT', 'CT、PT', 'PET-CT'].includes(study.Modalities)
            const hasPtClinicalData =
              isPtStudy &&
              (
                ptClinicalData.PatientWeight !== null ||
                ptClinicalData.RadionuclideTotalDose !== null ||
                ptClinicalData.RadionuclideHalfLife !== null ||
                ptClinicalData.RadiopharmaceuticalStartTime !== null ||
                ptClinicalData.AcquisitionTime !== null
              )
            study.SeriesList.forEach((series, seriesIndex) => {
              const imageIds = []
              const stack = []
              series.InstanceInfoList.forEach((instance, instanceIndex) => {
                if (hasPtClinicalData && ['PT', 'PET'].includes(String(series.Modality).toUpperCase())) {
                  setPTClinicalDataForInstance(instance.Id, ptClinicalData)
                }
                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}`)
                  } else {
                    // 多帧
                    for (let i = 0; i < nFrames; i++) {
                      const newImageId = `wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&frame=${i + 1}`
                      stack.push(newImageId)
                    }
                  }
                  imageIds.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}`)
                  this.instanceInfo[instance.Id] = { taskIndex, studyIndex, seriesIndex }
                }
              })
              series.Stack = stack
              series.ImageIds = imageIds
              series.SliceIndex = 0
              series.LoadedImageCount = 0
              series.LoadedImageProgress = 0
              series.ImageLoadState = {}
              series.TaskInfo = Object.assign({}, taskInfo)
              series.StudyIndex = studyIndex
              series.SeriesIndex = seriesIndex
            })
          })
          if (keyStudyIndex > -1 && keySeriesIndex > -1 && keyImages.length > 0) {
            const keyImageIds = []
            const keyStack = []
            keyImages.forEach(instance => {
              if (instance.KeyFramesList.length > 0) {
                instance.KeyFramesList.map(i => {
                  keyStack.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&frame=${i}`)
                })
              } else {
                keyStack.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}`)
              }
              keyImageIds.push(`wadouri:${this.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}`)
            })
            res1.Result[keyStudyIndex].SeriesList[keySeriesIndex].ImageIds = keyImageIds
            res1.Result[keyStudyIndex].SeriesList[keySeriesIndex].Stack = keyStack
          }
          // 获取标注信息
          let res2 = null
          if (this.criterionType === 0) {
            res2 = await getCustomTag({ visitTaskId: taskId })
          } else {
            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)
              if (this.criterionType === 0) {
                i.MeasureData.id = i.Id
                i.MeasureData.markId = i.MarkId
              }
              annotationUIDs.push(i.MeasureData.annotationUID)
            }
            return i
          })
          // return { studyList: arr, annotations: annotations, annotationUIDs, keyImages }
          resolve({ studyList: arr, annotations: annotations, annotationUIDs, keyImages })
        } catch (e) {
          console.log(e)
          reject(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 (seriesList.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) => {
          const allowInitPrefetch = i.TaskInfo.IsCurrentTask === true && index === this.activeViewportIndex
          this.$refs[`${this.viewportKey}-${index}`][0].setSeriesInfo(i, false, {
            limitInitPrefetch: allowInitPrefetch,
            skipInitPrefetch: !allowInitPrefetch
          })
        })
        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(4 * 1024 * 1024 * 1024)
      let renderingEngine = getRenderingEngine(renderingEngineId)
      if (!renderingEngine) {
        renderingEngine = new RenderingEngine(renderingEngineId)
      }
      let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
      const fusionViewportIds = ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3']
      const volumeViewportIds = ['viewport-MPR-0', 'viewport-MPR-1', 'viewport-MPR-2']
      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
      let 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
        }
      ]
      if (this.readingTool === 3) {
        viewportInputArray = [
          {
            viewportId: 'viewport-0',
            type: ViewportType.ORTHOGRAPHIC,
            element: element1,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL
            }
          },
          {
            viewportId: 'viewport-1',
            type: ViewportType.ORTHOGRAPHIC,
            element: element2,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL
            }
          },
          {
            viewportId: 'viewport-2',
            type: ViewportType.ORTHOGRAPHIC,
            element: element3,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL
            }
          },
          {
            viewportId: 'viewport-3',
            type: ViewportType.ORTHOGRAPHIC,
            element: element4,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL
            }
          }
        ]
        // let element5 = this.$refs.surfaceViewport.$el
        // let element6 = this.$refs.contourViewport.$el
        // viewportInputArray.push({
        //   viewportId: 'surfaceViewport',
        //   type: ViewportType.VOLUME_3D,
        //   element: element5,
        //   defaultOptions: {
        //     orientation: Enums.OrientationAxis.CORONAL,
        //     background: [1, 1, 1]
        //   }
        // })
        // viewportInputArray.push({
        //   viewportId: 'contourViewport',
        //   type: ViewportType.ORTHOGRAPHIC,
        //   element: element6,
        //   defaultOptions: {
        //     orientation: Enums.OrientationAxis.AXIAL
        //   }
        // })
        // viewportIds.push('surfaceViewport')
        // viewportIds.push('contourViewport')
      }
      if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) {
        const volumeElement1 = this.$refs['viewport-MPR-0'][0].$el
        const volumeElement2 = this.$refs['viewport-MPR-1'][0].$el
        const volumeElement3 = this.$refs['viewport-MPR-2'][0].$el
        const arr = [
          {
            viewportId: 'viewport-MPR-0',
            type: ViewportType.ORTHOGRAPHIC,
            element: volumeElement1,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL
            }
          },
          {
            viewportId: 'viewport-MPR-1',
            type: ViewportType.ORTHOGRAPHIC,
            element: volumeElement2,
            defaultOptions: {
              orientation: Enums.OrientationAxis.SAGITTAL
            }
          },
          {
            viewportId: 'viewport-MPR-2',
            type: ViewportType.ORTHOGRAPHIC,
            element: volumeElement3,
            defaultOptions: {
              orientation: Enums.OrientationAxis.CORONAL
            }
          },
        ]
        viewportInputArray = [...viewportInputArray, ...arr]
        viewportIds = viewportIds.concat(volumeViewportIds)
      }
      if (this.readingTool === 2) {
        const fusionElement1 = this.$refs['viewport-fusion-0'][0].$el
        const fusionElement2 = this.$refs['viewport-fusion-1'][0].$el
        const fusionElement3 = this.$refs['viewport-fusion-2'][0].$el
        const fusionElement4 = this.$refs['viewport-fusion-3'][0].$el
        const fusionHiddenSag = this.$refs['viewport-fusion-hidden-sag']
        const arr = [
          {
            viewportId: 'viewport-fusion-0',
            type: ViewportType.ORTHOGRAPHIC,
            element: fusionElement1,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL
            }
          },
          {
            viewportId: 'viewport-fusion-1',
            type: ViewportType.ORTHOGRAPHIC,
            element: fusionElement2,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL,
              background: [1, 1, 1]
            }
          },
          {
            viewportId: 'viewport-fusion-2',
            type: ViewportType.ORTHOGRAPHIC,
            element: fusionElement3,
            defaultOptions: {
              orientation: Enums.OrientationAxis.AXIAL
            }
          },
          {
            viewportId: 'viewport-fusion-3',
            type: ViewportType.ORTHOGRAPHIC,
            element: fusionElement4,
            defaultOptions: {
              orientation: Enums.OrientationAxis.CORONAL,
              background: [1, 1, 1]
            }
          },
          {
            viewportId: 'viewport-fusion-hidden-sag',
            type: ViewportType.ORTHOGRAPHIC,
            element: fusionHiddenSag,
            defaultOptions: {
              orientation: Enums.OrientationAxis.SAGITTAL,
              background: [1, 1, 1]
            }
          }
        ]
        viewportInputArray = [...viewportInputArray, ...arr]
        viewportIds = viewportIds.concat(fusionViewportIds, ['viewport-fusion-hidden-sag'])
      }
      renderingEngine.setViewports(viewportInputArray)
      this.addAnnotationListeners()
      // cornerstoneTools.addTool(TrackballRotateTool)
      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)
      cornerstoneTools.addTool(CircleROITool)
      cornerstoneTools.addTool(FixedRadiusCircleROITool)
      cornerstoneTools.addTool(EllipticalROITool)
      cornerstoneTools.addTool(AngleTool)
      cornerstoneTools.addTool(CobbAngleTool)
      cornerstoneTools.addTool(FusionJumpToPointTool)
      cornerstoneTools.addTool(MIPJumpToClickTool)
      cornerstoneTools.addTool(VolumeRotateTool)
      cornerstoneTools.addTool(CrosshairsTool)
      cornerstoneTools.addTool(LabelMapEditWithContourTool)
      cornerstoneTools.addTool(BrushTool)
      cornerstoneTools.addTool(SegmentBidirectionalTool)
      // cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool);
      // cornerstoneTools.addTool(SplineContourSegmentationTool);
      viewportIds.forEach((viewportId, i) => {
        // const toolGroupId = `viewport-${i}`
        let toolGroupId = viewportId
        if (volumeViewportIds.includes(viewportId)) {
          toolGroupId = this.volumeToolGroupId
        } else if (viewportId.startsWith('viewport-fusion-')) {
          toolGroupId = this.fusionToolGroupId
        }

        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) ? ToolGroupManager.getToolGroup(toolGroupId) : ToolGroupManager.createToolGroup(toolGroupId)
        toolGroup.addViewport(viewportId, renderingEngineId)
        if (toolGroupId.includes('surface')) {
          // toolGroup.addTool(TrackballRotateTool.toolName, {
          //   rotateSampleDistanceFactor: 0,
          // });
          // toolGroup.setToolActive(TrackballRotateTool.toolName, {
          //   bindings: [
          //     {
          //       mouseButton: MouseBindings.Primary,
          //     },
          //   ],
          // });
        } else if (toolGroupId.includes('contour')) {
          // toolGroup.addTool(PlanarFreehandContourSegmentationTool.toolName);
          // toolGroup.addTool(SplineContourSegmentationTool.toolName);
          // toolGroup.setToolActive(PlanarFreehandContourSegmentationTool.toolName, {
          //   bindings: [
          //     {
          //       mouseButton: MouseBindings.Primary, // Middle Click
          //     },
          //   ],
          // });
        } else {
          toolGroup.addTool(StackScrollTool.toolName, {
            loop: true,        // 启用循环滚动
          })

          toolGroup.addTool(ScaleOverlayTool.toolName)

          toolGroup.addTool(PanTool.toolName)
          toolGroup.addTool(ZoomTool.toolName)
          toolGroup.addTool(BrushTool.toolName)
          if (this.readingTool === 3 || toolGroupId === this.volumeToolGroupId) {
            toolGroup.addToolInstance(
              'histogram_RectangleROI',
              RectangleROITool.toolName,
            );
            toolGroup.addToolInstance(
              'histogram_CircleROI',
              CircleROITool.toolName,
            );
            toolGroup.addToolInstance(
              'histogram_PlanarFreehandROI',
              PlanarFreehandROITool.toolName,
            );
            toolGroup.addToolInstance(
              'CircularBrush',
              BrushTool.toolName,
              {
                activeStrategy: 'FILL_INSIDE_CIRCLE',
                preview: {
                  previewColors: {}
                }
              }
            );
            toolGroup.addToolInstance(
              'CircularEraser',
              BrushTool.toolName,
              {
                activeStrategy: 'ERASE_INSIDE_CIRCLE',
                preview: {
                  previewColors: {}
                }

              }
            )
            toolGroup.addToolInstance(
              'ThresholdCircle',
              BrushTool.toolName,
              {
                activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
              }
            )
            toolGroup.addToolInstance(
              'ThresholdSphere',
              BrushTool.toolName,
              {
                activeStrategy: 'THRESHOLD_INSIDE_SPHERE',
              }
            )
            toolGroup.addTool(LabelMapEditWithContourTool.toolName);
            toolGroup.addTool(SegmentBidirectionalTool.toolName, {
              getTextLines: this.getBidirectionalToolTextLines
            });
          }
          if (volumeViewportIds.includes(viewportId)) {
            toolGroup.addTool(WindowLevelTool.toolName, {
              targetViewportIds: volumeViewportIds
            })
            toolGroup.addTool(CrosshairsTool.toolName, {
              getReferenceLineColor: this.setCrosshairsToolLineColor
            });
          } else if (toolGroupId === this.fusionToolGroupId) {
            // toolGroup.addTool(CrosshairsTool.toolName, {
            //   getReferenceLineColor: this.setFusionCrosshairsToolLineColor,
            //   getReferenceLineSlabThicknessControlsOn: () => false,
            //   minimal: { enabled: true, lineLengthInPx: 10000 }
            // });
          } else {
            toolGroup.addTool(WindowLevelTool.toolName)
          }
          toolGroup.addTool(WindowLevelTool.toolName)
          toolGroup.addTool(WindowLevelRegionTool.toolName)
          toolGroup.addTool(PlanarRotateTool.toolName)
          toolGroup.addTool(ArrowAnnotateTool.toolName, {
            arrowHeadStyle: 'standard',
            changeTextCallback: async (data, eventData, doneChangingTextCallback) => {
              return doneChangingTextCallback(data.text)
            },
            getTextCallback: async (doneChangingTextCallback) => {
              return doneChangingTextCallback('Annotation')
            }
          })
          toolGroup.addTool(RectangleROITool.toolName, {
            cachedStats: false,
            getTextLines: this.criterionType === 0 ? this.getCustomRectangleROIToolTextLines : 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,
            actions: this.actionConfiguration,
            getTextLines: this.getBidirectionalToolTextLines
          })
          toolGroup.addTool(CircleROITool.toolName, {
            getTextLines: this.getCircleROIToolTextLines
          })
          toolGroup.addTool(EllipticalROITool.toolName, {
            getTextLines: this.getEllipticalROIToolTextLines
          })
          toolGroup.addTool(FixedRadiusCircleROITool.toolName, {
            radius: Number.isFinite(this.taskInfo.CircleRadius) ? this.taskInfo.CircleRadius : 6,
            getTextLines: this.getCircleROIToolTextLines
          })
          toolGroup.addTool(AngleTool.toolName, {
            getTextLines: this.getAngleToolTextLines
          })
          toolGroup.addTool(CobbAngleTool.toolName, {
            getTextLines: this.getCobbAngleToolTextLines
          })
          if (viewportId === 'viewport-fusion-3') {
            toolGroup.addTool(VolumeRotateTool.toolName)
            toolGroup.addTool(MIPJumpToClickTool.toolName, {
              targetViewportIds: ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3', 'viewport-fusion-hidden-sag'],
              sourceViewportIds: ['viewport-fusion-3'],
            })
            toolGroup.addTool(FusionJumpToPointTool.toolName, {
              targetViewportIds: ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3', 'viewport-fusion-hidden-sag'],
              useBrightestPoint: true,
              jumpToTargetViewports: true,
              dispatchEventName: 'fusion-mip-point-selected',
              getReferenceLineColor: this.setFusionCrosshairsToolLineColor,
              style: this.fusionCrosshairStyle,
              referenceLinesCenterGapRadius: this.fusionCrosshairStyle.centerHoleSize,
              minimal: {
                enabled: true,
                lineLengthInPx: this.fusionCrosshairStyle.lineLength,
              },
              mipViewportIds: ['viewport-fusion-3'],
            })

            toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
              bindings: [{ mouseButton: MouseBindings.Primary }]
            })

            toolGroup.setToolActive(VolumeRotateTool.toolName, {
              bindings: [
                {
                  mouseButton: MouseBindings.Wheel,
                },
              ]
            })
          }

          toolGroup.setToolConfiguration(
            ScaleOverlayTool.toolName,
            {
              scaleLocation: 'bottom'
            },
            true // overwrite
          )
          toolGroup.setToolActive(StackScrollTool.toolName, {
            bindings: [{ mouseButton: MouseBindings.Wheel }]
          })
          toolGroup.setToolActive(ZoomTool.toolName, {
            bindings: [{ mouseButton: MouseBindings.Secondary }]
          })
          toolGroup.setToolActive(PanTool.toolName, {
            bindings: [{ mouseButton: MouseBindings.Auxiliary }]
          })
          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)
            toolGroup.setToolPassive(CircleROITool.toolName)
            toolGroup.setToolPassive(EllipticalROITool.toolName)
            toolGroup.setToolPassive(FixedRadiusCircleROITool.toolName)
            toolGroup.setToolPassive(AngleTool.toolName)
            toolGroup.setToolPassive(CobbAngleTool.toolName)
            if (this.readingTool === 3) {
              toolGroup.setToolPassive(LabelMapEditWithContourTool.toolName)
              toolGroup.setToolPassive(SegmentBidirectionalTool.toolName, {});
            }
          } else {
            toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
            toolGroup.setToolEnabled(RectangleROITool.toolName)
            toolGroup.setToolEnabled(PlanarFreehandROITool.toolName)
            toolGroup.setToolEnabled(LengthTool.toolName)
            toolGroup.setToolEnabled(BidirectionalTool.toolName)
            toolGroup.setToolEnabled(CircleROITool.toolName)
            toolGroup.setToolEnabled(EllipticalROITool.toolName)
            toolGroup.setToolEnabled(FixedRadiusCircleROITool.toolName)
            toolGroup.setToolEnabled(AngleTool.toolName)
            toolGroup.setToolEnabled(CobbAngleTool.toolName)
            if (this.readingTool === 3) {
              toolGroup.setToolEnabled(LabelMapEditWithContourTool.toolName)
              toolGroup.setToolEnabled(SegmentBidirectionalTool.toolName);
            }
          }
          toolGroup.setToolPassive(EraserTool.toolName)
        }

      })
      eventTarget.addEventListener('cornerstoneimageloadprogress', this.imageLoadProgress)
      // console.log(Events, toolsEvents)
      if (this.readingTool === 2) {
        this.setUpSynchronizers()
      }
      if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) {
        this.setUpSynchronizersMPR()
      }
      renderingEngine.render();
    },
    // 影像下载进度回调
    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]
      if (series && series.length > 0) {
        let file = series.find(item => item.ImageId === imageId)
        if (file) {
          getNetWorkSpeed()
          setNetWorkSpeedSizeAll(percentComplete, detail.total, imageId)
        }
      }
      if (this.readingTool === 3) {
        getNetWorkSpeed()
        setNetWorkSpeedSizeAll(percentComplete, detail.total, imageId)
      }
      if (percentComplete === 100) {
        workSpeedclose()
      }
      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)
    // },
    // 设置图像下载进度信息
    setImageLoadedProgress(series, percentComplete, instanceId) {
      if (!series || !instanceId) return
      if (!series.ImageLoadState || typeof series.ImageLoadState !== 'object') {
        this.$set(series, 'ImageLoadState', {})
      }
      const imageLoadState = series.ImageLoadState
      const prevPercentComplete = Number(imageLoadState[instanceId]?.percentComplete || 0)
      const normalizedPercentComplete = Math.max(prevPercentComplete, Number(percentComplete || 0))
      const wasLoaded = Boolean(imageLoadState[instanceId]?.loaded)
      const isLoaded = wasLoaded || normalizedPercentComplete === 100

      this.$set(imageLoadState, instanceId, {
        percentComplete: normalizedPercentComplete,
        loaded: isLoaded
      })

      const newLoadedImageProgress = series.LoadedImageProgress - prevPercentComplete + normalizedPercentComplete
      this.$set(series, 'LoadedImageProgress', newLoadedImageProgress)
      if (!wasLoaded && isLoaded) {
        this.$set(series, 'LoadedImageCount', series.LoadedImageCount + 1)
      }
    },
    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 ? series.TaskInfo.VisitTaskId : null
      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 && !Object.hasOwn(i.MeasureData, 'isDicomReading')) {
          const annotation = i.MeasureData
          if ((this.readingTool === 3 && annotation.seriesId === series.Id) || this.readingTool < 3) {
            annotation.highlighted = false
            const textBox = annotation?.data?.handles?.textBox
            const worldPosition = textBox?.worldPosition
            const hasValidTextBoxWorldPosition = Array.isArray(worldPosition) &&
              worldPosition.length === 3 &&
              worldPosition.every(v => Number.isFinite(v)) &&
              worldPosition.some(v => Math.abs(v) > 0)

            // 如果保存的标注里已带 textbox 世界坐标，则优先按该坐标回显，避免重新计算位置。
            if (textBox && hasValidTextBoxWorldPosition) {
              textBox.hasMoved = true
            }
            cornerstoneTools.annotation.state.addAnnotation(annotation)
            if (this.visitTaskList[taskIdx].ReadingTaskState === 2) {
              cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
            }
          }
        }
      })
    },
    addAnnotationListeners() {
      const debouncedCallback = this.debounce((evt) => {
        this.criterionType === 0 ? this.customAnnotationModifiedListener(evt) : this.annotationModifiedListener(evt)
      }, 100)
      eventTarget.addEventListener(
        toolsEvents.ANNOTATION_MODIFIED,
        (evt) => {
          debouncedCallback(evt)
        }
      )
      eventTarget.addEventListener(
        toolsEvents.ANNOTATION_COMPLETED,
        this.criterionType === 0 ? this.customAnnotationCompletedListener : this.annotationCompletedListener
      )
      eventTarget.addEventListener(
        toolsEvents.ANNOTATION_REMOVED,
        this.criterionType === 0 ? this.customAnnotationRemovedListener : this.annotationRemovedListener
      )
      eventTarget.addEventListener(
        toolsEvents.ANNOTATION_SELECTION_CHANGE,
        this.criterionType === 0 ? this.customAnnotationSelectionChangeListener : this.annotationSelectionChangeListener
      )

      // eventTarget.addEventListener(
      //   toolsEvents.ANNOTATION_ADDED,
      //   this.annotationAddedListener
      // )
      // eventTarget.addEventListener(
      //   toolsEvents.TOOL_MODE_CHANGED,
      //   this.toolModeChanged
      // )
    },
    annotationAddedListener(e) {

    },

    annotationCompletedListener(e) {
      console.log('Completed')
      const { annotation } = e.detail
      if (!annotation) return
      if (annotation.metadata.toolName === FusionJumpToPointTool.toolName) return
      if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
      if (this.readingTaskState === 2) return
      if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
      let viewportIndex = this.isFusion ? 2 : this.activeViewportIndex
      const series = this.$refs[`${this.viewportKey}-${viewportIndex}`][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.syncMarkedSeriesIdsFromState(series.TaskInfo.VisitTaskId)
      }

      this.setToolsPassive()
    },
    annotationModifiedListener(e) {
      console.log('Modified')
      const { annotation, changeType } = e.detail
      const isStatsUpdated = changeType === ChangeTypes.StatsUpdated
      if (!annotation) return
      if (annotation.metadata.toolName === FusionJumpToPointTool.toolName) return
      if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
      if (this.readingTaskState === 2) return
      if (!annotation.highlighted && !isStatsUpdated) return
      if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
      const series = this.$refs[`${this.viewportKey}-${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) {
      const { annotation } = e.detail
      try {
        if (!annotation) return
        if (annotation.metadata.toolName === FusionJumpToPointTool.toolName) return
        if (annotation.metadata.toolName === CrosshairsTool.toolName) return
        if (this.readingTaskState === 2 && !annotation.data.label) return false
        if (this.readingTaskState === 2) {
          const errorMsg = { message: 'annotation Not allowed to operate' }
          throw errorMsg
        }
        if (annotation.visitTaskId === this.taskInfo.VisitTaskId && annotation.seriesId) {
          this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
        } else {
          const errorMsg = { message: 'annotation Not allowed to operate' }
          throw errorMsg
        }
      } catch (e) {
        cornerstoneTools.annotation.state.addAnnotation(annotation)
        this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
        const renderingEngine = getRenderingEngine(renderingEngineId)
        for (let i = 0; i < this.cells.length; i++) {
          const viewportId = `${this.viewportKey}-${i}`
          const viewport = renderingEngine.getViewport(viewportId)
          viewport.render()
        }
        console.log(e)
      }
    },
    async customAnnotationCompletedListener(e) {
      console.log('completed')
      const { annotation } = e.detail
      if (!annotation) return
      if (annotation.metadata.toolName === FusionJumpToPointTool.toolName) return
      if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
      if (this.readingTaskState === 2) return
      const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
      if (i === -1) {
        if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
        return
      }
      if (annotation.metadata.segmentationId) return
      if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
      let viewportIndex = this.isFusion ? 2 : this.activeViewportIndex
      const series = this.$refs[`${this.viewportKey}-${viewportIndex}`][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.from = this.isMPR ? 'MPR' : this.isFusion ? "Fusion" : ''
        annotation.instanceId = params.instanceId
        annotation.sliceThickness = series.SliceThickness
        annotation.numberOfFrames = isNaN(parseInt(params.frame)) ? null : parseInt(params.frame)
        annotation.markTool = annotation.metadata.toolName
        this.syncMarkedSeriesIdsFromState(series.TaskInfo.VisitTaskId)
        const operateStateEnum = this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].operateStateEnum
        const markName = await this.customPrompt(!this.isNumber(operateStateEnum))

        if (markName) {
          annotation.data.label = markName
          if (annotation.metadata.toolName === 'ArrowAnnotate') {
            annotation.data.text = markName
          }
          if (this.isNumber(operateStateEnum)) {
            this.$refs[`ecrf_${series.TaskInfo.VisitTaskId}`][0].bindAnnotationToQuestion(annotation)
          } else {
            if (this.saveCustomAnnotationTimer) {
              clearTimeout(this.saveCustomAnnotationTimer)
              this.saveCustomAnnotationTimer = null
            }
            this.saveCustomAnnotationTimer = setTimeout(() => { this.saveCustomAnnotation(annotation) }, 500)
          }
        } else {
          // if (this.isNumber(operateStateEnum)) {
          //   this.removeAnnotation(annotation)
          // }  
        }
      }

      this.setToolsPassive()
    },
    validMarkName(markName) {
      const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
      const i = annotations.findIndex(i => i.visitTaskId === this.taskInfo.VisitTaskId && i.data.label === markName)
      return i > -1
    },
    async saveCustomAnnotation(annotation) {
      try {
        const measureData = Object.assign({}, annotation)
        const params = {}
        await this.getScreenshots({
          visitTaskId: this.taskInfo.VisitTaskId,
          annotation
        }, async (base64Str) => {
          const pictureObj = await this.uploadScreenshots(`${Date.now()}`, base64Str)
          let picturePath = pictureObj.isSuccess ? this.$getObjectName(pictureObj.result.url) : ''
          params.PicturePath = picturePath
          params.MarkTool = measureData.markTool
          params.OrderMarkName = measureData.data.label
          params.VisitTaskId = this.taskInfo.VisitTaskId
          params.StudyId = annotation.studyId
          params.SeriesId = annotation.seriesId
          params.InstanceId = annotation.instanceId
          params.Id = annotation.id ? annotation.id : ''
          params.MeasureData = JSON.stringify(measureData)
          params.NumberOfFrames = annotation.numberOfFrames
          params.MarkId = annotation.annotationUID
          const res = await submitCustomTag(params)
          annotation.id = res.Result
          annotation.markId = params.MarkId
        })

      } catch (e) {
        this.loading = false
        console.log(e)
      }
    },
    customAnnotationModifiedListener(e) {
      console.log('modified')
      const { annotation, changeType } = e.detail
      const isStatsUpdated = changeType === ChangeTypes.StatsUpdated
      if (!annotation) return
      if (annotation.metadata.toolName === FusionJumpToPointTool.toolName) return
      if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
      if (this.readingTaskState === 2) return
      if (!annotation.highlighted) return
      const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
      if (i === -1) {
        if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
        return
      }
      if (annotation.metadata.segmentationId) return
      if (!annotation.data.label) return
      if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
      const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
      if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
        const operateStateEnum = this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].operateStateEnum
        const isBound = this.$refs[`ecrf_${annotation.visitTaskId}`][0].verifyAnnotationIsBound(annotation)
        if (isBound || this.isNumber(operateStateEnum)) {
          this.$refs[`ecrf_${series.TaskInfo.VisitTaskId}`][0].updateAnnotationToQuestion(annotation)
        } else {
          this.curOperation.type = 'Modified'
          this.curOperation.annotation = annotation
        }
      }
      this.setToolsPassive()
    },
    async customAnnotationRemovedListener(e) {
      const { annotation } = e.detail
      try {
        // if ( this.resetAnnotation && this.isFusion ) return false
        if (!annotation) return false
        if (annotation.metadata.toolName === FusionJumpToPointTool.toolName) return false
        if (annotation.metadata.toolName === CrosshairsTool.toolName) return false
        if (this.readingTaskState === 2 && !annotation.data.label) return false
        if (this.readingTaskState === 2) {
          const errorMsg = { message: 'annotation Not allowed to operate' }
          throw errorMsg
        }
        if (this.activeTool !== 'Eraser') return false
        const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
        if (i === -1) {
          if (annotation.metadata.toolName === SegmentBidirectionalTool.toolName) {
            this.setToolsPassive()
            const errorMsg = { message: 'SegmentBidirectionalTool Not remove' }
            throw errorMsg
          }
          // 临时标记
          return
        }
        if (annotation.metadata.segmentationId) return
        if (annotation.visitTaskId === this.taskInfo.VisitTaskId) {
          const isBound = this.$refs[`ecrf_${annotation.visitTaskId}`][0].verifyAnnotationIsBound(annotation)
          if (isBound && this.activeTool === 'Eraser') {
            // '该标记已与问题进行绑定，不允许删除！'
            this.$alert(this.$t('dicom3D:ReadPage:alert:MarkToQuestionNoDel'))
            const errorMsg = { message: 'annotation Not allowed to operate' }
            throw errorMsg
          }
        }

        if (annotation.visitTaskId === this.taskInfo.VisitTaskId && annotation.seriesId) {
          if (this.activeTool === 'Eraser') {
            await this.$confirm(
              this.$t('trials:trials-list:table:isDeleted') +
              annotation.data.label +
              '?'
            )
            if (annotation.markId) {
              let res = await deleteCustomTag(annotation.markId)
              if (!res.IsSuccess) throw ''
            } else if (annotation.id) {
              let res = await deleteCustomTag(annotation.id)
              if (!res.IsSuccess) throw ''
            }
          }
          this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
          const renderingEngine = getRenderingEngine(renderingEngineId)
          for (let i = 0; i < this.cells.length; i++) {
            const viewportId = `${this.viewportKey}-${i}`
            const viewport = renderingEngine.getViewport(viewportId)
            viewport.render()
          }
        } else {
          const errorMsg = { message: 'annotation Not allowed to operate' }
          throw errorMsg
        }
      } catch (e) {
        cornerstoneTools.annotation.state.addAnnotation(annotation)
        this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
        const renderingEngine = getRenderingEngine(renderingEngineId)
        for (let i = 0; i < this.cells.length; i++) {
          const viewportId = `${this.viewportKey}-${i}`
          const viewport = renderingEngine.getViewport(viewportId)
          viewport.render()
        }
        console.log(e)
      }
    },
    contentMouseup(e) {
      if (this.$refs.Segmentations) {
        this.$refs.Segmentations.contentMouseup()
      }
      if (this.curOperation.type === 'Modified') {
        let annotation = this.curOperation.annotation
        this.saveCustomAnnotation(annotation)
      }
      this.curOperation = {
        type: '',
        annotation: null
      }
    },
    removeAnnotation(annotation) {
      cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
      const renderingEngine = getRenderingEngine(renderingEngineId)
      for (let i = 0; i < this.cells.length; i++) {
        const viewportId = `${this.viewportKey}-${i}`
        const viewport = renderingEngine.getViewport(viewportId)
        viewport.render()
      }
    },
    customAnnotationSelectionChangeListener(e) {
      if (this.readingTaskState === 2) return
      const { detail } = e
      const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
      const i = annotations.findIndex(i => i.annotationUID === detail.selection[0])
      if (i > -1) {
        if (annotations[i].visitTaskId !== this.taskInfo.VisitTaskId) {
          return
        } else {
          this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].bindAnnotationToQuestion(annotations[i])
        }
      }
    },
    annotationSelectionChangeListener(e) {
      const { annotation } = e.detail || {}
      if (!annotation) return
      if (annotation.metadata.toolName === FusionJumpToPointTool.toolName) return
    },
    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
      // 获取标注信息
      let res = null
      if (this.criterionType === 0) {
        res = await getCustomTag({ visitTaskId: visitTaskId })
      } else {
        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)
          if (this.criterionType === 0) {
            i.MeasureData.id = i.Id
            i.MeasureData.markId = i.MarkId
          }
          annotationUIDs.push(i.MeasureData.annotationUID)
        }
        return i
      })
      this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
      this.$set(this.visitTaskList[taskIdx], 'AnnotationUIDs', annotationUIDs)
    },
    collectMarkedSeriesIds(list = []) {
      return [...new Set(
        list
          .map(item => item?.MeasureData?.seriesId || item?.seriesId)
          .filter(Boolean)
      )]
    },
    syncMarkedSeriesIds(list = []) {
      this.markedSeriesIds = this.collectMarkedSeriesIds(list)
    },
    syncMarkedSeriesIdsFromState(visitTaskId = this.taskInfo?.VisitTaskId) {
      const annotations = cornerstoneTools.annotation.state.getAllAnnotations().filter(item => {
        return item.visitTaskId === visitTaskId && item.seriesId
      })
      this.syncMarkedSeriesIds(annotations)
    },
    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
      // 获取标注信息
      let res = null
      if (this.criterionType === 0) {
        res = await getCustomTag({ visitTaskId: visitTaskId })
      } else {
        res = await getTableAnswerRowInfoList(visitTaskId)
      }
      const annotations = res.Result.map(i => {
        if (typeof i.MeasureData === 'string' && i.MeasureData) {
          i.MeasureData = JSON.parse(i.MeasureData)
          if (this.criterionType === 0) {
            i.MeasureData.id = i.Id
            i.MeasureData.markId = i.MarkId
          }
        }
        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)
        }
      })
      annotations.map(i => {
        if (i.MeasureData) {
          const annotation = i.MeasureData
          cornerstoneTools.annotation.state.addAnnotation(annotation)
        }
      })
      this.syncMarkedSeriesIds(annotations)
      const renderingEngine = getRenderingEngine(renderingEngineId)
      for (let i = 0; i < this.cells.length; i++) {
        const viewportId = `${this.viewportKey}-${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
        annotations[idx].data.label = obj.name
      } else {
        annotations[idx].data.label = obj.name
      }
      annotations[idx].data.status = obj.status
      const renderingEngine = getRenderingEngine(renderingEngineId)
      const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
      const viewport = renderingEngine.getViewport(viewportId)
      viewport.render()
    },
    setCrosshairsToolLineColor(viewportId) {
      let colors = [
        '#ff0000',
        '#00ff00',
        '#0000ff',
      ]
      let index = viewportId.split("-").pop()
      return colors[colors.length - 1 - Number(index)] || colors[0]
    },
    setFusionCrosshairsToolLineColor(viewportId) {
      let colors = [
        '#0000ff',
        '#0000ff',
        '#0000ff',
        '#ff0000',
        '#00ff00',
      ]

      if (viewportId === 'viewport-fusion-hidden-sag') {
        return colors[colors.length - 1]
      } else {
        let index = viewportId.split("-").pop()
        return colors[Number(index)] || colors[0]
      }
    },
    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.status ? `${data.label}(${data.status})` : 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 (data.label) {
        textLines.push(data.label)
      }
      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.status ? `${data.label}(${data.status})` : data.label)
      }

      return textLines
    },
    getCustomRectangleROIToolTextLines(data, targetId) {
      const cachedVolumeStats = data.cachedStats[targetId]
      const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats

      if (mean === undefined) {
        return
      }

      const textLines = []
      if (data.label) {
        textLines.push(data.label)
      }
      textLines.push(`Area: ${this.reRound(area, this.digitPlaces)} ${areaUnit}`)

      textLines.push(`Mean: ${this.reRound(mean, this.digitPlaces)} ${modalityUnit}`)
      textLines.push(`Max: ${this.reRound(max, this.digitPlaces)} ${modalityUnit}`)
      textLines.push(`Std Dev: ${this.reRound(stdDev, this.digitPlaces)} ${modalityUnit}`)

      return textLines
    },
    getBidirectionalToolTextLines(data, targetId) {
      const { cachedStats, label } = data
      const { length, width, unit } = cachedStats[targetId]

      const textLines = []

      if (label) {
        textLines.push(data.status ? `${label}(${data.status})` : label)
      }
      if (length === undefined) {
        return textLines
      }

      textLines.push(
        `L: ${parseFloat(length).toFixed(this.digitPlaces)} ${unit || unit}`,
        `S: ${parseFloat(width).toFixed(this.digitPlaces)} ${unit}`
      )

      return textLines
    },
    getCircleROIToolTextLines(data, targetId) {
      const cachedVolumeStats = data.cachedStats[targetId]
      const {
        Modality,
        radius,
        radiusUnit,
        area,
        mean,
        stdDev,
        max,
        total,
        isEmptyArea,
        areaUnit,
        modalityUnit
      } = cachedVolumeStats
      const textLines = []
      if (data.label) {
        // textLines.push(data.label)
        textLines.push(data.label)
      }

      if (radius) {
        const radiusLine = isEmptyArea
          ? `Radius: Oblique not supported`
          : `Radius: ${this.reRound(radius, this.digitPlaces)} ${radiusUnit}`
        textLines.push(radiusLine)
      }

      if (area) {
        const areaLine = isEmptyArea
          ? `Area: Oblique not supported`
          : `Area: ${parseFloat(area).toFixed(2)} ${areaUnit}`
        textLines.push(areaLine)
      }

      if (total !== undefined && total !== null && Modality === 'NM') {
        textLines.push(`Total: ${this.formatStatSum(total)}`)
      }

      if (mean) {
        textLines.push(`Mean: ${this.reRound(mean, this.digitPlaces)} ${modalityUnit}`)
      }

      if (max) {
        textLines.push(`Max: ${this.reRound(max, Modality === 'NM' ? 0 : this.digitPlaces)} ${modalityUnit}`)
      }

      if (stdDev) {
        textLines.push(`Std Dev: ${this.reRound(stdDev, this.digitPlaces)} ${modalityUnit}`)
      }

      return textLines
    },
    getEllipticalROIToolTextLines(data, targetId) {
      const cachedVolumeStats = data.cachedStats[targetId]
      const {
        // radius,
        // radiusUnit,
        area,
        mean,
        stdDev,
        max,
        isEmptyArea,
        areaUnit,
        modalityUnit
      } = cachedVolumeStats
      const textLines = []
      if (data.label) {
        // textLines.push(data.label)
        textLines.push(data.label)
      }

      // if (radius) {
      //   const radiusLine = isEmptyArea
      //     ? `Radius: Oblique not supported`
      //     : `Radius: ${this.reRound(radius, this.digitPlaces)} ${radiusUnit}`
      //   textLines.push(radiusLine)
      // }

      if (area) {
        const areaLine = isEmptyArea
          ? `Area: Oblique not supported`
          : `Area: ${parseFloat(area).toFixed(2)} ${areaUnit}`
        textLines.push(areaLine)
      }

      if (mean) {
        textLines.push(`Mean: ${this.reRound(mean, this.digitPlaces)} ${modalityUnit}`)
      }

      if (max) {
        textLines.push(`Max: ${this.reRound(max, this.digitPlaces)} ${modalityUnit}`)
      }

      if (stdDev) {
        textLines.push(`Std Dev: ${this.reRound(stdDev, this.digitPlaces)} ${modalityUnit}`)
      }

      return textLines
    },
    getAngleToolTextLines(data, targetId) {
      const cachedVolumeStats = data.cachedStats[targetId]
      const { label } = data
      const { angle } = cachedVolumeStats
      if (angle === undefined) {
        return
      }

      if (isNaN(angle)) {
        return [`${angle}`]
      }
      const textLines = []
      if (label) {
        textLines.push(label)
      }
      textLines.push(`${angle.toFixed(this.digitPlaces)} ${String.fromCharCode(176)}`)
      return textLines
    },
    getCobbAngleToolTextLines(data, targetId) {
      const cachedVolumeStats = data.cachedStats[targetId]
      const { angle } = cachedVolumeStats
      const { label } = data

      if (angle === undefined) {
        return
      }
      const textLines = []
      if (label) {
        textLines.push(label)
      }
      textLines.push(`${angle.toFixed(this.digitPlaces)} ${String.fromCharCode(176)}`)

      return textLines
    },
    reRound(result, finalPrecision) {
      if (typeof result === 'string' && result.includes(', ')) {
        const numStrs = result.split(', ')
        const processed = numStrs.map(str => this.processSingle(str, finalPrecision))
        return processed.join(', ')
      }
      return this.processSingle(result, finalPrecision)
    },
    formatStatSum(value) {
      const num = Number(value)
      if (!Number.isFinite(num)) return value

      if (Math.abs(num - Math.round(num)) < 1e-6) {
        return String(Math.round(num))
      }

      return this.reRound(num, this.digitPlaces)
    },
    processSingle(str, precision) {
      const num = parseFloat(str)
      if (isNaN(num)) return 'NaN'

      // 保留原极小值处理逻辑
      if (Math.abs(num) < 0.0001) return str
      const factor = 10 ** precision
      return (Math.round(num * factor + 0.0000001) / factor).toFixed(precision)
    },
    getActiveToolGroupId() {
      if (this.isMPR) return this.volumeToolGroupId
      if (this.isFusion) return this.fusionToolGroupId
      return `${this.viewportKey}-${this.activeViewportIndex}`
    },
    getCurrentToolGroupIds() {
      if (this.isMPR) return [this.volumeToolGroupId]
      if (this.isFusion) return [this.fusionToolGroupId]
      return [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
    },
    handleFusionSlicePointChange({ viewportId, worldPoint }) {
      if (!this.isFusion) return
      if (!viewportId || !Array.isArray(worldPoint) || worldPoint.length < 3) return

      const activeFusionViewportId = `viewport-fusion-${this.activeViewportIndex}`
      if (viewportId !== activeFusionViewportId) return
      if (viewportId === 'viewport-fusion-3') return

      const renderingEngine = getRenderingEngine(renderingEngineId)
      if (!renderingEngine) return
      const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
      if (!toolGroup) return
      const instance = toolGroup.getToolInstance(FusionJumpToPointTool.toolName)
      if (!instance || !instance.setPoint) return

      instance.setPoint(worldPoint, viewportId, renderingEngine.id, {
        jumpToTargetViewports: false,
        dispatchEvent: false,
      })
    },
    setFusionMipClickEnabled(enabled) {
      const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
      if (!toolGroup || !toolGroup.hasTool(MIPJumpToClickTool.toolName)) return
      if (enabled && this.isFusion) {
        toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
          bindings: [{ mouseButton: MouseBindings.Primary }]
        })
      } else {
        toolGroup.setToolDisabled(MIPJumpToClickTool.toolName)
      }
    },
    setFusionMipJumpEnabled(enabled) {
      const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
      if (!toolGroup) return
      if (enabled) {
        this.setFusionMipClickEnabled(false)
        if (!toolGroup.hasTool(FusionJumpToPointTool.toolName)) return
        if (!this.isFusion) return
        toolGroup.setToolActive(FusionJumpToPointTool.toolName, {
          bindings: [{ mouseButton: MouseBindings.Primary }]
        })
        this.dispatchFusionCenterPoint()
      } else {
        this.setFusionMipClickEnabled(this.isFusion)
        if (!toolGroup.hasTool(FusionJumpToPointTool.toolName)) return
        toolGroup.setToolDisabled(FusionJumpToPointTool.toolName)
        this.clearFusionJumpToPointAnnotations()
      }
    },
    clearFusionJumpToPointAnnotations() {
      const annotations = cornerstoneTools.annotation.state.getAllAnnotations() || []
      const removeList = []
      for (let i = 0; i < annotations.length; i++) {
        const item = annotations[i]
        if (!item || !item.metadata) continue
        if (item.metadata.toolName !== FusionJumpToPointTool.toolName) continue
        removeList.push(item)
      }
      if (!removeList.length) return
      removeList.forEach(i => {
        cornerstoneTools.annotation.state.removeAnnotation(i.annotationUID)
      })
      const renderingEngine = getRenderingEngine(renderingEngineId)
      if (!renderingEngine) return
      const viewportIds = ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3', 'viewport-fusion-hidden-sag']
      viewportIds.forEach(viewportId => {
        const viewport = renderingEngine.getViewport(viewportId)
        if (viewport && viewport.render) {
          viewport.render()
        }
      })
    },
    dispatchFusionCenterPoint(retryCount = 0) {
      const renderingEngine = getRenderingEngine(renderingEngineId)
      if (!renderingEngine) return
      const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
      if (!toolGroup || !toolGroup.getToolInstance) return
      const instance = toolGroup.getToolInstance(FusionJumpToPointTool.toolName)
      if (!instance || !instance.setPoint) return
      const candidates = ['viewport-fusion-2', 'viewport-fusion-1', 'viewport-fusion-0']
      for (const viewportId of candidates) {
        const viewport = renderingEngine.getViewport(viewportId)
        if (!viewport || !viewport.canvasToWorld || !viewport.element) continue
        const width = viewport.element.clientWidth
        const height = viewport.element.clientHeight
        let worldPoint = null
        if (width && height) {
          worldPoint = viewport.canvasToWorld([width / 2, height / 2])
        }
        if ((!worldPoint || worldPoint.length < 3) && viewport.getCamera) {
          const camera = viewport.getCamera()
          if (camera && camera.focalPoint) {
            worldPoint = camera.focalPoint
          }
        }
        if (!worldPoint || worldPoint.length < 3) continue
        instance.setPoint(worldPoint, viewportId, renderingEngine.id, {
          jumpToTargetViewports: true,
          dispatchEvent: false,
        })
        return
      }
      if (retryCount < 10) {
        setTimeout(() => {
          this.dispatchFusionCenterPoint(retryCount + 1)
        }, 120)
      }
    },
    setFusionMipRotateEnabled(enabled) {
      if (!this.isFusion) return
      const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
      if (!toolGroup || !toolGroup.hasTool(VolumeRotateTool.toolName)) return
      if (enabled) {
        toolGroup.setToolActive(VolumeRotateTool.toolName, {
          bindings: [{ mouseButton: MouseBindings.Wheel }]
        })
      } else {
        toolGroup.setToolDisabled(VolumeRotateTool.toolName)
      }
    },
    // 激活工具
    setToolActive(toolName) {
      if (this.histogramVisible) return false
      if (this.isFusion && toolName === CrosshairsTool.toolName) return false
      const toolGroupId = this.getActiveToolGroupId()
      const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
      if (!toolGroup) return
      if (this.activeTool === toolName) {
        if (toolName === CrosshairsTool.toolName) {
          if (toolGroup.hasTool(this.activeTool)) {
            toolGroup.setToolDisabled(this.activeTool)
          }
          this.setFusionMipJumpEnabled(true)
          // this.setFusionMipRotateEnabled(true)
        } else if (toolName === FusionJumpToPointTool.toolName) {
          this.setFusionMipJumpEnabled(false)
        } else {
          toolGroup.setToolPassive(this.activeTool)
        }
        this.activeTool = ''
      } else {
        if (this.activeTool) {
          if (this.activeTool === CrosshairsTool.toolName) {
            if (toolGroup.hasTool(this.activeTool)) {
              toolGroup.setToolDisabled(this.activeTool)
            }
            this.setFusionMipJumpEnabled(true)
            // this.setFusionMipRotateEnabled(true)
          } else if (this.activeTool === FusionJumpToPointTool.toolName) {
            this.setFusionMipJumpEnabled(false)
          } else {
            toolGroup.setToolPassive(this.activeTool)
          }
        }
        if (toolName === FusionJumpToPointTool.toolName) {
          this.setFusionMipJumpEnabled(true)
        } else {
          toolGroup.setToolActive(toolName, {
            bindings: [{ mouseButton: MouseBindings.Primary }]
          })
          if (toolName === CrosshairsTool.toolName) {
            if (this.isFusion) {
              // const instance = toolGroup.getToolInstance?.(CrosshairsTool.toolName)
              // if (instance && !instance.__fusionSameForPatched) {
              //   instance.__fusionSameForPatched = true
              //   const original = instance._checkIfViewportsRenderingSameScene?.bind(instance)
              //   instance._checkIfViewportsRenderingSameScene = (viewport, otherViewport) => {
              //     try {
              //       const a = viewport?.getFrameOfReferenceUID?.()
              //       const b = otherViewport?.getFrameOfReferenceUID?.()
              //       if (a && b && a === b) return true
              //     } catch (e) { }
              //     return original ? original(viewport, otherViewport) : true
              //   }
              // }
            }
            this.setFusionMipJumpEnabled(false)
            // this.setFusionMipRotateEnabled(false)
          }
        }
        this.activeTool = toolName
      }
    },
    hoverFusionViewport(index) {
      if (!this.isFusion) return
      const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
      if (!toolGroup) return

      const isMip = index === 3
      if (isMip) {
        if (toolGroup.hasTool(StackScrollTool.toolName)) {
          toolGroup.setToolDisabled(StackScrollTool.toolName)
        }
        if (toolGroup.hasTool(VolumeRotateTool.toolName)) {
          toolGroup.setToolActive(VolumeRotateTool.toolName, {
            bindings: [{ mouseButton: MouseBindings.Wheel }]
          })
        }
      } else {
        if (toolGroup.hasTool(VolumeRotateTool.toolName)) {
          toolGroup.setToolDisabled(VolumeRotateTool.toolName)
        }
        if (toolGroup.hasTool(StackScrollTool.toolName)) {
          toolGroup.setToolActive(StackScrollTool.toolName, {
            bindings: [{ mouseButton: MouseBindings.Wheel }]
          })
        }
      }
    },
    // 激活标注工具
    setAnnotateToolActive(toolName) {
      // if (this.readingTaskState === 2) return
      if (this.histogramVisible) return false
      const toolObj = this.tools.find(i => i.toolName === toolName)
      if (!toolObj || toolObj.isDisabled) return
      const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
      if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
        const toolGroupId = this.getActiveToolGroupId()
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
        if (!toolGroup) return
        if (this.activeTool === toolName) {
          if (toolName === CrosshairsTool.toolName) {
            if (toolGroup.hasTool(this.activeTool)) {
              toolGroup.setToolDisabled(this.activeTool)
            }
          } else if (toolName === FusionJumpToPointTool.toolName) {
            this.setFusionMipJumpEnabled(false)
          } else {
            toolGroup.setToolPassive(this.activeTool)
          }
          this.activeTool = ''
        } else {
          if (this.activeTool) {
            if (this.activeTool === CrosshairsTool.toolName) {
              if (toolGroup.hasTool(this.activeTool)) {
                toolGroup.setToolDisabled(this.activeTool)
              }
            } else if (this.activeTool === FusionJumpToPointTool.toolName) {
              this.setFusionMipJumpEnabled(false)
            } else {
              toolGroup.setToolPassive(this.activeTool)
            }
          }
          toolGroup.setToolActive(toolName, {
            bindings: [{ mouseButton: MouseBindings.Primary }]
          })
          this.activeTool = toolName
        }
      }
      if (this.criterionType === 0 && this.readingTaskState < 2) {
        this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].resetOperateState()
      }
    },
    setReadingToolActive(toolName) {
      if (this.readingTaskState === 2) return
      if (this.activeTool === toolName) return
      const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
      if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
        const toolGroupId = this.getActiveToolGroupId()
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
        if (!toolGroup) return
        if (this.activeTool) {
          if (this.activeTool === CrosshairsTool.toolName) {
            if (toolGroup.hasTool(this.activeTool)) {
              toolGroup.setToolDisabled(this.activeTool)
            }
          } else if (this.activeTool === FusionJumpToPointTool.toolName) {
            this.setFusionMipJumpEnabled(false)
          } else {
            toolGroup.setToolPassive(this.activeTool)
          }
        }
        toolGroup.setToolActive(toolName, {
          bindings: [{ mouseButton: MouseBindings.Primary }]
        })
        this.activeTool = toolName
      }
    },
    setReadingToolPassive() {
      if (this.readingTaskState === 2) return
      if (this.activeTool && this.toolNames.includes(this.activeTool)) {
        const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
        if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
          const toolGroupId = this.getActiveToolGroupId()
          const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
          if (!toolGroup) return
          if (this.activeTool === CrosshairsTool.toolName) {
            if (toolGroup.hasTool(this.activeTool)) {
              toolGroup.setToolDisabled(this.activeTool)
            }
          } else if (this.activeTool === FusionJumpToPointTool.toolName) {
            this.setFusionMipJumpEnabled(false)
          } else {
            toolGroup.setToolPassive(this.activeTool)
          }
          this.activeTool = ''
        }
      }
    },
    setMoreToolActive(toolName) {
      // if (this.readingTaskState === 2) return
      if (this.histogramVisible) return false
      this.setToolsPassive()
      const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
      if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
        const toolGroupId = this.getActiveToolGroupId()
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
        if (!toolGroup) return
        toolGroup.setToolActive(toolName, {
          bindings: [{ mouseButton: MouseBindings.Primary }]
        })
        this.activeTool = toolName
      }
    },
    setToolsPassive() {
      if (!this.activeTool) return
      const toolGroupIds = this.getCurrentToolGroupIds()
      toolGroupIds.forEach(toolGroupId => {
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
        if (!toolGroup) return
        if (this.activeTool === CrosshairsTool.toolName) {
          if (toolGroup.hasTool(this.activeTool)) {
            toolGroup.setToolDisabled(this.activeTool)
          }
        } else if (this.activeTool === FusionJumpToPointTool.toolName) {
          this.setFusionMipJumpEnabled(false)
        } else if (this.activeTool === 'SphericalBrush') {
          toolGroup.setToolDisabled('ThresholdSphere')
        } else {
          toolGroup.setToolPassive(this.activeTool)
        }
      })
      this.activeTool = ''
    },
    setToolEnabled() {
      if (!this.activeTool) return
      const toolGroupIds = this.getCurrentToolGroupIds()
      toolGroupIds.forEach(toolGroupId => {
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
        if (!toolGroup) return
        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[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
      const isCurrentTask = series.TaskInfo.IsCurrentTask
      const readingTaskState = this.readingTaskState
      if (isCurrentTask && readingTaskState >= 2) {
        this.tools[i].isDisabled = false
        e.target.style.cursor = 'pointer'
      } else if (!isCurrentTask || readingTaskState >= 2) {
        this.tools[i].isDisabled = true
        e.target.style.cursor = 'not-allowed'
        if (this.activeTool) {
          this.setToolEnabled()
        }
      } else {
        if (this.criterionType === 0) {
          this.tools[i].isDisabled = false
          e.target.style.cursor = 'pointer'
          return
        }
        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 = `${this.viewportKey}-${this.activeViewportIndex}`
      const viewport = renderingEngine.getViewport(viewportId)
      const type = parseInt(value)
      if (this.readingTool === 3 || this.isMPR) this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFilp(true)
      // 1：默认值；2：垂直翻转；3：水平翻转；4：左转90度；5：右转90度；
      if (type === 1) {
        // viewport.resetCamera()
        // viewport.resetProperties()
        // viewport.render()
        viewport.setViewPresentation({ rotation: 0 })
        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[`${this.viewportKey}-${this.activeViewportIndex}`][0].rotateOrientationMarkers(type)
    },
    resetCrosshairsAnnotationsForViewports(viewportIds = []) {
      if (!viewportIds || viewportIds.length === 0) return
      const viewportIdSet = new Set(viewportIds)
      const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
      annotations.forEach((a) => {
        if (!a) return
        if (a.metadata.toolName !== CrosshairsTool.toolName) return
        const vpId = a.data.viewportId
        if (!vpId || !viewportIdSet.has(vpId)) return
        cornerstoneTools.annotation.state.removeAnnotation(a.annotationUID)
      })
    },
    // 重置视口
    async resetViewport() {
      const renderingEngine = getRenderingEngine(renderingEngineId)
      if (this.isFusion) {
        this.setToolsPassive()
        const fusionViewportIds = ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3']
        const fusionAllViewportIds = [...fusionViewportIds, 'viewport-fusion-hidden-sag']
        for (const id of fusionAllViewportIds) {
          const viewport = renderingEngine.getViewport(id)
          if (!viewport) continue
          const ref = this.$refs[id]?.[0]
          const index = ref?.series?.SliceIndex
          if (ref.resetOrientationMarkers) ref.resetOrientationMarkers()
          viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true, resetRotation: true })
          if (viewport.resetSlabThickness) viewport.resetSlabThickness()
          viewport.resetProperties()
          if (id === 'viewport-fusion-3') {
            if (ref.rotateBarLeft || ref.rotateBarLeft === 0) ref.rotateBarLeft = 0
            if (ref.rotateAngle || ref.rotateAngle === 0) ref.rotateAngle = 0
            const ptSeries = ref?.series
            if (ptSeries && ptSeries.SeriesInstanceUid) {
              await ref.setSeriesInfo({ data: ptSeries }, false, { isMip: true, colorMap: false })
            }
          } else {
            viewport.render()
          }
          if ((index || index === 0) && ref.setFullScreen) {
            ref.setFullScreen(index)
          }
        }
        this.resetCrosshairsAnnotationsForViewports(fusionAllViewportIds)
        renderingEngine.render()
        this.dispatchFusionCenterPoint()
        if (Number.isFinite(this.fusionOverlayDefaultUpper) && Number.isFinite(this.fusionOverlayDefaultRange)) {
          this.lastUpper = null
          this.hasFusionUpperInitialized = false
          if (this.$refs.colorMap) {
            this.$refs.colorMap.range = this.fusionOverlayDefaultRange
            this.$refs.colorMap.upper = this.fusionOverlayDefaultUpper
            this.$refs.colorMap.upperRangeChange(this.fusionOverlayDefaultRange)
            this.$refs.colorMap.changeVoi(this.fusionOverlayDefaultUpper)
          }
          this.voiChange(this.fusionOverlayDefaultUpper)
        }
        return
      }

      const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
      const viewport = renderingEngine.getViewport(viewportId)
      this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resetOrientationMarkers()
      let index = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
      if (this.readingTool !== 3) {
        viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true, resetRotation: true })
      }
      if (this.isMPR) {
        let volume = cache.getVolume(this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].volumeId)
        const voi = metaData.get('voiLutModule', volume._imageIds[Math.ceil((volume._imageIds.length - 1) / 2)])
        const lower = voi.windowCenter[0] - voi.windowWidth[0] / 2
        const upper = voi.windowCenter[0] + voi.windowWidth[0] / 2 - 1
        let viewportIds = ['viewport-MPR-0', 'viewport-MPR-1', 'viewport-MPR-2']
        viewportIds.forEach(viewportId => {
          let viewport = renderingEngine.getViewport(viewportId)
          viewport.resetProperties()
          viewport.setProperties({ voiRange: { upper: upper, lower: lower } })
          viewport.render()
        })

        return this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
      }
      // viewport.resetProperties()
      this.setToolsPassive()
      if (this.readingTool === 3 && this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.Modality === 'PT') {
        viewport.setProperties({ voiRange: { upper: 5, lower: 0 }, invert: true }, this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].volumeId)
      } else {
        viewport.resetProperties()
      }
      viewport.render()
      // renderingEngine.render()
      if (this.readingTool === 3) {
        DicomEvent.$emit('isloaded', { isChange: false, viewportId })
      }
      if (this.readingTool === 3 || this.isMPR) {
        this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
      }
    },
    // 更改视图布局
    async changeLayout(v) {
      if (this.readingTool === 3) {
        let res = await this.changeScreenSave()
        if (!res) return false
      }
      this.setToolsPassive()
      this.fullScreenIndex = null
      this.layout = v
      const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
      let index = series.SliceIndex
      const seriesArr = []
      let activeViewportIndex = 0
      if (v === 1) {
        this.rows = 1
        this.cols = 1
        activeViewportIndex = 0
        series.curIndex = index
        if (typeof series === 'object') {
          seriesArr.push(series)
        }
      } else if (v === 2) {
        this.rows = 1
        this.cols = 2
        activeViewportIndex = 1
        series.curIndex = index
        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.criterionType === 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)
          }
        }
        activeViewportIndex = 1
      } else if (v === 4) {
        this.rows = 2
        this.cols = 2
        series.curIndex = index
        if (typeof series === 'object') {
          seriesArr.push(series)
          seriesArr.push(series)
          seriesArr.push(series)
          seriesArr.push(series)
        }
        activeViewportIndex = 3
      }
      seriesArr.map(async (i, index) => {
        await this.$refs[`${this.viewportKey}-${index}`][0].setSeriesInfo(i)
        if (index === seriesArr.length - 1) this.activeViewportIndex = activeViewportIndex

      })
      this.$nextTick(() => {
        const renderingEngine = getRenderingEngine(renderingEngineId)
        renderingEngine.resize(true, false)
      })
    },
    // 更改窗宽窗位
    changeVoiRange(v) {
      this.setToolsPassive()
      const renderingEngine = getRenderingEngine(renderingEngineId)
      const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
      const viewport = renderingEngine.getViewport(viewportId)
      let index = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
      if (this.readingTool === 3 || this.isMPR) this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFilp(true)
      if (v.val === -1) {
        // 默认值
        viewport.resetProperties()
        viewport.render()
      } else if (v.val === 0) {
        // 自定义
        const wwwc = this.$refs[`${this.viewportKey}-${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()
      }
      if (this.readingTool === 3 || this.isMPR) this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
    },
    setWindowLevelActive(e) {
      this.setToolActive('WindowLevel')
      this.showPanel(e)
    },
    // 设置窗宽窗位
    setWwwc(v) {
      const renderingEngine = getRenderingEngine(renderingEngineId)
      const viewportId = v.id ? v.id : `${this.viewportKey}-${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(`${this.viewportKey}-${this.activeViewportIndex}`)
      const { invert } = viewport.getProperties()
      if (this.isFusion || this.readingTool === 3) {
        viewport.setProperties({ invert: !invert }, this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].volumeId)
      } else {
        viewport.setProperties({ invert: !invert })
      }
      viewport.render()
    },
    // 翻页
    scrollPage(type) {
      // if (this.histogramVisible) return false
      this.clipPlaying = false
      if (this.isScrollSync && !this.isFusion && !this.isMPR && this.cells.length > 1) {
        for (let i = 0; i < this.cells.length; i++) {
          const viewportRef = this.$refs[`${this.viewportKey}-${i}`]
          if (viewportRef && viewportRef[0] && typeof viewportRef[0].scrollPage === 'function') {
            viewportRef[0].scrollPage(type)
          }
        }
        return
      }
      this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(type)
    },
    setImageIndexSync() {
      this.isScrollSync = !this.isScrollSync
    },
    handleViewportScrollSync({ viewportIndex, type, direction }) {
      if (!this.isScrollSync || this.isFusion || this.isMPR || this.cells.length <= 1) return
      const syncType = type ?? direction
      if (viewportIndex !== this.activeViewportIndex || ![-1, 1, 0, 99999].includes(syncType)) return

      this.clipPlaying = false
      for (let i = 0; i < this.cells.length; i++) {
        if (i === viewportIndex) continue
        const viewportRef = this.$refs[`${this.viewportKey}-${i}`]
        if (viewportRef && viewportRef[0] && typeof viewportRef[0].scrollPage === 'function') {
          viewportRef[0].scrollPage(syncType)
        }
      }
    },
    // 播放
    toggleClipPlay(isPlay) {
      // if (this.histogramVisible) return false
      this.clipPlaying = !this.clipPlaying
      this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].toggleClipPlay(isPlay, this.fps)
    },
    handleClipFpsChange() {
      if (!this.clipPlaying) return

      const activeViewportRef = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`]
      const activeViewport = activeViewportRef && activeViewportRef[0]
      if (!activeViewport || typeof activeViewport.toggleClipPlay !== 'function') return

      activeViewport.toggleClipPlay(true, 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: 1 })
        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 => {
        const 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
        const 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[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
          this.$refs[this.activeTaskId][0].getPreviousOrNextSeries(-1, series)
        } else if (shortcutKeyEnum === 4) {
          // 下一个序列
          const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
          this.$refs[this.activeTaskId][0].getPreviousOrNextSeries(1, series)
        } else if (shortcutKeyEnum === 5) {
          // 上一张图像
          this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(-1)
        } else if (shortcutKeyEnum === 6) {
          // 下一张图像
          this.$refs[`${this.viewportKey}-${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[`${this.viewportKey}-${this.activeViewportIndex}`][0].setZoom(1)
        } else if (shortcutKeyEnum === 12) {
          // 缩小
          this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setZoom(-1)
        } else if (shortcutKeyEnum === 13) {
          // 适应图像
          this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(false)
        } else if (shortcutKeyEnum === 14) {
          // 适应窗口
          this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(true)
        } else if (shortcutKeyEnum === 15) {
          // 截图
          this.saveImage()
        } else if (shortcutKeyEnum === 16) {
          // 反色
          this.toggleInvert()
        } else if (shortcutKeyEnum === 17) {
          // 窗宽/窗位
          const wwwcIdx = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].wwwcIdx
          const newWwwcIdx = wwwcIdx === this.wwwcArr.length - 1 ? 3 : wwwcIdx + 1
          const wwwcTpl = this.wwwcArr[newWwwcIdx]
          this.changeVoiRange(wwwcTpl)
          this.$refs[`${this.viewportKey}-${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)
          let viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
          if (this.isMPR) {
            viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
          }
          renderingEngine.renderViewports(viewportIds)
        }
        event.stopImmediatePropagation()
        event.stopPropagation()
        event.preventDefault()
      })
    },
    async saveImage() {
      const divForDownloadViewport = document.querySelector(
        `div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
      )
      let series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
      const canvas = await html2canvas(divForDownloadViewport)
      const base64Str = canvas.toDataURL('image/png', 1)
      let file = this.convertBase64ToBlob(base64Str)
      const a = document.createElement('a')
      a.download = `${series.SeriesInstanceUid}.png`
      const blobUrl = URL.createObjectURL(file)
      a.href = blobUrl
      document.body.appendChild(a)
      a.click()
      a.remove()
      URL.revokeObjectURL(blobUrl)
    },
    // 重置热键信息
    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.forceFitToWindow = !forceFitToWindow
      this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(forceFitToWindow)
    },
    // 重置视口
    resetRenderingEngine(viewportId = null, i) {
      if (viewportId.includes('MPR') && !this.isMPR) return false
      if (this.timer[viewportId]) {
        clearInterval(this.timer[viewportId])
        this.timer[viewportId] = null
      }
      const renderingEngine = getRenderingEngine(renderingEngineId)
      const viewport = renderingEngine.getViewport(viewportId)
      if (!viewport) return false
      if (viewport.volumeIds.size <= 0) return false
      let index = null
      this.timer[viewportId] = setTimeout(() => {
        index = i || i === 0 ? i : this.$refs[viewportId ? viewportId : `${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
        renderingEngine.resize(true, false)
        renderingEngine.render()
        this.$refs[viewportId ? viewportId : `${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
        clearTimeout(this.timer[viewportId])
        this.timer[viewportId] = null
        if (this.readingTool === 3) {
          DicomEvent.$emit('isloaded', { isChange: false, viewportId })
        }
      }, 100)
    },
    setDelay(time) {
      if (this.FullTimerOut) return false
      this.FullTimerOut = setTimeout(() => {
        this.isDelay = false
        clearTimeout(this.FullTimerOut)
        this.FullTimerOut = null
      }, time)
    },
    // 切换全屏
    async toggleFullScreen(e, index) {
      if (!this.isMPR && !this.isFusion && this.cells.length <= 1) return false
      if (this.isDelay && (this.readingTool === 3 || this.isMPR)) return false
      if (this.readingTool === 3 && this.isMPR) return false
      if (this.readingTool === 3) {
        let res = await this.changeScreenSave()
        if (!res) return false
      }
      this.fullScreenIndex = this.fullScreenIndex === index ? null : index
      this.activeViewportIndex = index
      if (this.readingTool === 3 || this.isMPR) {
        // this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series)
        this.$nextTick(() => {
          // this.resetRenderingEngine(`${this.viewportKey}-${index}`)
          this.isDelay = true
          this.setDelay(2000)
          // if (this.readingTool === 3) {
          //   DicomEvent.$emit('isloaded', { isChange: false })
          // }
        })

      }
      if (this.isFusion) {
        let viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
        viewportIds.forEach(id => {
          const index = this.$refs[id][0].series.SliceIndex
          this.$refs[id][0].setFullScreen(index)
        })
      }
    },
    async toggleTask(taskInfo, taskIndex) {
      if (this.isFusion || this.isMPR) {
        const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
        if (!confirm) return false
        this.isFusion = false
        this.isMPR = false
        this.fullScreenIndex = null
        this.rows = 1
        this.cols = 1
        this.activeViewportIndex = 0
        this.setToolsPassive()

      }
      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.$refs[this.activeTaskId][0].setSeriesActive(0, 0)
      // this.setToolsPassive()
    },
    async toggleTaskByViewport(obj) {
      const i = this.visitTaskList.findIndex(v => v.VisitTaskNum === obj.visitTaskNum)
      if (i === -1) return
      if (this.readingTool === 3) {
        let res = await this.changeScreenSave()
        if (!res) return false
      }
      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[`${this.viewportKey}-${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, isSave = false) {
      if (this.readingTool === 3) {
        let res = await this.changeScreenSave()
        if (!res) return false
        DicomEvent.$emit('activeSeries', obj)
      }
      if (this.isFusion || this.isMPR) {
        if (this.isFusion && this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].verifyAnnotationIsSave()) {
          const confirm = await this.$confirm(this.$t('trials:reading:confirm:clearnAnnotation'))
          if (!confirm) return false
          this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].removeAllNoSaveAnnotation()
        } else {
          if (isSave) return false
          const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
          if (!confirm) return false
        }
        if (this.histogramVisible) this.$refs.histogram.close()
        this.isFusion = false
        this.setToolsPassive()
        this.rows = 1
        this.cols = 1
        this.fullScreenIndex = null
        this.isMPR = false
        obj.isChange = false
        return this.$nextTick(() => {
          DicomEvent.$emit('activeSeries', obj)
          DicomEvent.$emit('changeMPR')
          this.$refs[`${this.viewportKey}-0`][0].setSeriesInfo(obj)
          this.activeViewportIndex = 0
          const renderingEngine = getRenderingEngine(renderingEngineId)
          renderingEngine.resize(true, false)
          renderingEngine.render()
          // if (this.readingTool === 3) {
          //   DicomEvent.$emit('isloaded', { isChange: false })
          // }
        })

      }
      if (this.histogramVisible) this.$refs.histogram.close()
      if (!obj.IsDicom) {
        return this.previewNoneDicoms(obj)
      }
      // this.activeViewportIndex = 0
      this.$refs[`${this.viewportKey}-${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
      }
      if (obj.TaskInfo.VisitTaskId !== this.taskInfo.VisitTaskId) {
        this.setToolsPassive()
      }
    },
    async showMultiFrame(obj) {
      this.$refs[`${this.viewportKey}-${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()
    },
    // 激活视口
    async activeViewport(index) {
      if (this.activeViewportIndex === index) return
      if (this.readingTool === 3) {
        let res = await this.changeScreenSave()
        if (!res) return false
      }
      this.activeViewportIndex = index
      // 切换检查列表
      const series = this.$refs[`${this.viewportKey}-${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)
        }
      }
      if (this.readingTool === 3) {
        this.$nextTick(() => {
          DicomEvent.$emit('SegmentationLoading', `${this.viewportKey}-${this.activeViewportIndex}`)
        })
      }
      if (this.activeTool && this.activeTool !== CrosshairsTool.toolName && this.activeTool !== FusionJumpToPointTool.toolName) {
        const toolGroupId = this.getActiveToolGroupId()
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
        if (toolGroup && toolGroup.hasTool(this.activeTool)) {
          toolGroup.setToolActive(this.activeTool, {
            bindings: [{ mouseButton: MouseBindings.Primary }]
          })
        }
      }
    },
    getRelatedSeries(visitTaskInfo, baselineSeries) {
      let obj = {}
      const studyList = visitTaskInfo.StudyList
      const annotations = visitTaskInfo.Annotations
      if (baselineSeries) {
        const seriesList = studyList.filter(i => i.IsDicom).map(s => 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].MeasureData)
          }
        }
        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].MeasureData)
        } 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, checkFrom = false) {
      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 = `instanceId=${annotation.instanceId}&frame=${annotation.numberOfFrames}`
            } 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
          }
          if ((annotation.from === 'MPR' || annotation.metadata.volumeId) && checkFrom) {
            obj = Object.assign({}, seriesList[seriesIdx])
            obj.SliceIndex = annotation?.metadata?.sliceIndex
            obj.IsMarked = true
            obj.MeasureData = annotation
          }

        }
      }
      return obj
    },
    async viewCustomAnnotationSeries(obj) {
      const i = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
      if (i === -1) return
      const studyList = this.visitTaskList[i].StudyList
      let series = null
      if (this.isFusion) {
        this.activeViewportIndex = 2
      }
      let curSeriesId = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.Id
      if (obj.segment) {
        let study = studyList.find(item => item.StudyId === obj.segmentGroup.StudyId)
        let Series = study.SeriesList.find(item => item.Id === obj.segmentGroup.SeriesId)
        Series.SliceIndex = 0
        series = Object.assign(Series, { segment: obj.segment })
      } else {
        series = this.getMarkedSeries(studyList, obj.annotation, true)
        delete series.segment
      }

      if (series) {
        if (this.isFusion && series.SeriesInstanceUid !== this.fusionSerieId.ct && series.SeriesInstanceUid !== this.fusionSerieId.pt) {
          return await this.$confirm(this.$t('trials:reading:confirm:fusionPTOrCTInconsistent'))
        }
        this.activeViewportIndex = this.cells.length - 1
        if (obj.annotation && obj.annotation.from === 'MPR') {
          let viewPlaneNormal = obj.annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')
          if (viewPlaneNormal === '0,0,-1') {
            this.activeViewportIndex = 0
            // series = Object.assign({ orientation: 'AXIAL' }, series)
          } else if (viewPlaneNormal === '0,-1,0') {
            this.activeViewportIndex = 2
            // series = Object.assign({ orientation: 'CORONAL' }, series)
          } else if (viewPlaneNormal === '1,0,0') {
            this.activeViewportIndex = 1
            // series = Object.assign({ orientation: 'SAGITTAL' }, series)
          }
          let res = await this.openMPRViewport(series)
          if (!res) return false
        } else {
          if (this.isMPR) {
            this.isMPR = false
            this.rows = 1
            this.cols = 1
            this.activeViewportIndex = 0
            // return this.activeSeries(series)
          }
          if (this.isFusion) {
            this.activeViewportIndex = 0
            if (series.Modality === 'PT' || series.Modality === 'NM') {
              this.activeViewportIndex = 2
            }
            series = {
              Id: series.Id,
              Modality: series.Modality,
              data: series,
              StudyIndex: series.StudyIndex,
              SeriesIndex: series.SeriesIndex,
              TaskInfo: series.TaskInfo
            }
          }
        }
        this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, series.segment || curSeriesId !== series.Id ? false : true, { isFusion: this.isFusion && (series.Modality === 'PT' || series.Modality === 'NM') })
        this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
      }
    },
    async getCustomScreenshots(obj, callback) {
      const i = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
      if (i === -1) return
      const studyList = this.visitTaskList[i].StudyList
      let series = this.getMarkedSeries(studyList, obj.annotation, true)
      if (series) {
        this.activeViewportIndex = this.cells.length - 1
        if (obj.annotation.from === 'MPR') {
          let viewPlaneNormal = obj.annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')
          if (viewPlaneNormal === '0,0,-1') {
            this.activeViewportIndex = 0
            // series = Object.assign({ orientation: 'AXIAL' }, series)
          } else if (viewPlaneNormal === '0,-1,0') {
            this.activeViewportIndex = 2
            // series = Object.assign({ orientation: 'CORONAL' }, series)
          } else if (viewPlaneNormal === '1,0,0') {
            this.activeViewportIndex = 1
            // series = Object.assign({ orientation: 'SAGITTAL' }, series)
          }
          let res = await this.openMPRViewport(series)
          if (!res) {
            this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex, true)
            setTimeout(async () => {
              const divForDownloadViewport = document.querySelector(
                `div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
              )
              const canvas = await html2canvas(divForDownloadViewport)
              const base64Str = canvas.toDataURL('image/png', 1)
              callback(base64Str)
            }, 200)
            return false
          }
        } else {
          if (this.isMPR) {
            this.isMPR = false
            this.rows = 1
            this.cols = 1
            this.activeViewportIndex = 0
            // return this.activeSeries(series)
          }
          if (this.isFusion) {
            this.activeViewportIndex = 0
            if (series.Modality === 'PT' || series.Modality === 'NM') {
              this.activeViewportIndex = 2
            }
            series = {
              data: series,
              StudyIndex: series.StudyIndex,
              SeriesIndex: series.SeriesIndex,
              TaskInfo: series.TaskInfo
            }
          }
        }
        if (!this.isActiveViewportAtMeasureLocation(obj.annotation)) {
          this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, true)
          this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
        }
        setTimeout(async () => {
          const divForDownloadViewport = document.querySelector(
            `div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
          )
          const canvas = await html2canvas(divForDownloadViewport)
          const base64Str = canvas.toDataURL('image/png', 1)
          callback(base64Str)
        }, 200)
      }
    },
    getMeasureImageLocation(measureData = {}) {
      const referencedImageId = measureData?.metadata?.referencedImageId
      const referencedImageParams = referencedImageId ? this.getInstanceInfo(referencedImageId) : {}
      return {
        visitTaskId: measureData.visitTaskId,
        studyId: measureData.studyId || measureData.StudyId,
        seriesId: measureData.seriesId || measureData.SeriesId,
        instanceId: measureData.instanceId || measureData.InstanceId || referencedImageParams.instanceId || null,
        frame: measureData.numberOfFrames ?? measureData.NumberOfFrames ?? referencedImageParams.frame
      }
    },
    getActiveViewportImageLocation() {
      const viewportRef = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`]?.[0]
      const currentSeries = viewportRef?.series
      if (!currentSeries) return null
      const renderingEngine = getRenderingEngine(renderingEngineId)
      const viewport = renderingEngine?.getViewport(`${this.viewportKey}-${this.activeViewportIndex}`)
      const currentImageId = viewport?.getCurrentImageId?.()
      const currentImageParams = currentImageId ? this.getInstanceInfo(currentImageId) : {}
      return {
        visitTaskId: currentSeries.TaskInfo?.VisitTaskId,
        studyId: currentSeries.StudyId,
        seriesId: currentSeries.Id,
        instanceId: currentImageParams.instanceId || null,
        frame: currentImageParams.frame
      }
    },
    isActiveViewportAtMeasureLocation(measureData) {
      const targetLocation = this.getMeasureImageLocation(measureData)
      const currentLocation = this.getActiveViewportImageLocation()
      if (!currentLocation || !targetLocation.seriesId) return false
      if (targetLocation.visitTaskId && currentLocation.visitTaskId !== targetLocation.visitTaskId) return false
      if (targetLocation.studyId && currentLocation.studyId !== targetLocation.studyId) return false
      if (currentLocation.seriesId !== targetLocation.seriesId) return false
      if (targetLocation.instanceId && currentLocation.instanceId !== targetLocation.instanceId) return false
      return currentLocation.frame === targetLocation.frame
    },
    async getScreenshots(measureData, callback) {
      if (measureData) {
        if (!this.isActiveViewportAtMeasureLocation(measureData.annotation)) {
          await this.imageLocation(measureData)
        }
        const divForDownloadViewport = document.querySelector(
          `div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
        )
        const canvas = await html2canvas(divForDownloadViewport)
        const base64Str = canvas.toDataURL('image/png', 1)
        callback(base64Str)
      } 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].MeasureData)
            }
          }
          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 && obj.SplitOrMergeType !== '1' && obj.SplitOrMergeType !== '3') {
              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[`${this.viewportKey}-${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[`${this.viewportKey}-${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[`${this.viewportKey}-${i}`][0].setSeriesInfo(currentAddSeries, true)
            this.activeViewportIndex = i
            this.$refs[currentAddSeries.TaskInfo.VisitTaskId][0].setSeriesActive(currentAddSeries.StudyIndex, currentAddSeries.SeriesIndex)
          } else {
            this.$refs[`${this.viewportKey}-${i}`][0].setSeriesInfo(firstAddSeries, true)
          }
        }
        this.setToolsPassive()
        if (obj.isActiveTarget && obj.SplitOrMergeType !== '1' && obj.SplitOrMergeType !== '3') {
          this.setToolToTarget(obj)
        }
        resolve()
      })
    },
    setToolToTarget(obj) {
      if (obj.visitTaskId === this.taskInfo.VisitTaskId && this.readingTaskState !== 2 && obj.markTool && !obj.isMarked) {
        const toolName = obj.markTool
        if (this.activeTool) {
          this.setToolsPassive()

        }
        const toolGroupId = this.getActiveToolGroupId()
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
        if (!toolGroup) return
        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(justKeyDoc = false) {
      this.manualsDialog.isFullscreen = false
      this.manualsDialog.visible = true
      this.manualsDialog.justKeyDoc = justKeyDoc
    },
    // 临床数据
    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 = {}
      if (imageId) {
        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(isShowCancelButton = true) {
      try {
        const that = this
        // 请输入标记名称
        let message = this.$t('trials:noneDicom:message:msg1')
        // if (isShowCancelButton) {
        //   message = `<div><p>${this.$t('trials:noneDicom:message:msg1')}</p><p style='font-size:12px' class='customPromptTip'>${this.$t('trials:noneDicom:message:saveTip')}</p></div>`
        // }
        const { value } = await this.$prompt(message, '', {
          showClose: false,
          cancelButtonText: isShowCancelButton ? this.$t('trials:reading:button:temporarySave') : this.$t('common:button:cancel'),
          confirmButtonText: isShowCancelButton ? this.$t('trials:reading:button:enduringSave') : this.$t('trials:reading:button:relevancy'),
          // showCancelButton: isShowCancelButton,
          // dangerouslyUseHTMLString: isShowCancelButton,
          inputValidator: (res) => {
            if (!res) {
              return isShowCancelButton ? this.$t('trials:noneDicom:message:saveTip') : this.$t('trials:noneDicom:message:saveTipByRelevancy')
            }
          },
          inputErrorMessage: this.$t('trials:noneDicom:message:saveTip'),
          showCancelButton: true,
          closeOnClickModal: false,
          closeOnPressEscape: false,
          beforeClose: (action, instance, done) => {
            if (action === 'confirm') {
              const value = instance.inputValue
              if (!value) {
                that.$message.error(this.$t('trials:customReading:error:validMarkName1'))
              } else if (this.validMarkName(value)) {
                that.$message.error(this.$t('trials:customReading:error:validMarkName'))
              } else {
                done()
              }
            } else {
              done()
            }
          }
        })
        return value
      } catch (err) {
        console.log(err)
        return null
      }
    },
    isNumber(value) {
      return typeof value === 'number' && value !== null && !isNaN(value)
    },
    async showKeySeriesPanel() {
      this.keySeries = []
      this.keySeriesLoading = true
      try {
        const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
        this.keyTaskInfo = series ? series.TaskInfo : null
        if (this.keyTaskInfo) {
          let res = await getMarkList({ visitTaskId: this.keyTaskInfo.VisitTaskId })
          if (res && res.Result) {
            this.keySeries = res.Result
          }
        }
      } catch (err) {
        console.error(err)
      } finally {
        this.keySeriesLoading = false
      }
    },
    jumpToKeySeries(kf) {

      let tIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.keyTaskInfo.VisitTaskId)
      if (tIdx < 0) return
      const task = this.visitTaskList[tIdx]
      const studyList = task.StudyList
      const annotation = kf.MeasureData ? JSON.parse(kf.MeasureData) : {}
      let targetSeries = this.getMarkedSeries(studyList, annotation, true)
      if (Object.keys(targetSeries).length === 0) return
      // this.activeTaskId = this.taskInfo.VisitTaskId
      this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(targetSeries, true)
      this.$refs[this.keyTaskInfo.VisitTaskId][0].setSeriesActive(targetSeries.StudyIndex, targetSeries.SeriesIndex)
    },
    showPanel(e, toolName) {
      if (toolName === 'layout' && (this.isFusion || this.isMPR)) return false
      const dropdownContent = e.currentTarget.querySelector('.dropdown-content')
      if (dropdownContent) {
        dropdownContent.style.display = 'block'
      } else {
        e.currentTarget.firstChild.lastChild.style.display = 'block'
      }
    },
    toolMouseout(e) {
      const dropdownContent = e.currentTarget.querySelector('.dropdown-content')
      if (dropdownContent) {
        dropdownContent.style.display = 'none'
      } else {
        e.currentTarget.firstChild.lastChild.style.display = 'none'
      }
    },
    debounce(callback, delay) {
      let timerId
      return function () {
        clearTimeout(timerId)
        timerId = setTimeout(() => {
          callback.apply(this, arguments)
        }, delay)
      }
    },
    previewNoneDicoms(obj) {
      var index = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.TaskInfo.VisitTaskId)
      var taskBlindName = this.visitTaskList[index].TaskBlindName
      var token = getToken()
      let subjectId = localStorage.getItem("subjectId")
      const routeData = this.$router.resolve({
        path: `/nonedicoms?subjectId=${subjectId}&trialId=${this.trialId}&visitTaskId=${obj.TaskInfo.VisitTaskId}&taskBlindName=${taskBlindName}&readingTaskState=${this.visitTaskList[index].ReadingTaskState}&TokenKey=${token}`
      })
      this.open = window.open(routeData.href, '_blank')
    },
    // 融合视口相机同步
    setUpSynchronizers() {
      const axialCameraPositionSynchronizer = createCameraPositionSynchronizer(
        'AXIAL_CAMERA_SYNCHRONIZER_ID'
      )
      const ctVoiSynchronizer = createVOISynchronizer('CT_VOI_SYNCHRONIZER_ID', {
        syncInvertState: false,
        syncColormap: false
      })
      const ptVoiSynchronizer = createVOISynchronizer('PT_VOI_SYNCHRONIZER_ID', {
        syncInvertState: false,
        syncColormap: false
      })
      const fusionVoiSynchronizer = createVOISynchronizer('FUSION_VOI_SYNCHRONIZER_ID', {
        syncInvertState: false,
        syncColormap: false
      });
      [
        `viewport-fusion-0`,
        `viewport-fusion-1`,
        `viewport-fusion-2`
      ].forEach((viewportId) => {
        axialCameraPositionSynchronizer.add({
          renderingEngineId: this.renderingEngineId,
          viewportId
        })
      });
      [
        `viewport-fusion-0`
      ].forEach((viewportId) => {
        ctVoiSynchronizer.add({
          renderingEngineId: this.renderingEngineId,
          viewportId
        })
      });
      [
        `viewport-fusion-3`,
        `viewport-fusion-1`
      ].forEach((viewportId) => {
        ptVoiSynchronizer.add({
          renderingEngineId: this.renderingEngineId,
          viewportId
        })
      });
      [
        `viewport-fusion-2`
      ].forEach((viewportId) => {
        fusionVoiSynchronizer.add({
          renderingEngineId,
          viewportId
        })
        ctVoiSynchronizer.addTarget({
          renderingEngineId,
          viewportId
        })
        ptVoiSynchronizer.addTarget({
          renderingEngineId,
          viewportId
        })
      })
    },
    // mpr调窗同步设置
    setUpSynchronizersMPR() {
      const MPRVoiSynchronizer = createVOISynchronizer('MPR_VOI_SYNCHRONIZER_ID', {
        syncInvertState: false,
        syncColormap: false
      })
      let viewportIds = [
        `viewport-MPR-0`,
        `viewport-MPR-1`,
        `viewport-MPR-2`
      ]
      viewportIds.forEach((viewportId) => {
        MPRVoiSynchronizer.add({
          renderingEngineId: this.renderingEngineId,
          viewportId
        })
      });
      const synchronizer = createSlabThicknessSynchronizer('SLAB_THICKNESS_SYNCHRONIZER_ID');

      // Add viewports to VOI synchronizers
      [
        `viewport-MPR-0`,
        `viewport-MPR-1`,
        `viewport-MPR-2`
      ].forEach((viewportId) => {
        synchronizer.add({
          renderingEngineId: this.renderingEngineId,
          viewportId,
        });
      });
      synchronizer.setEnabled(false);
    },
    setColorMap(rgbPresetName) {
      const fusionViewportIds = [`viewport-fusion-2`]
      // const fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
      fusionViewportIds.forEach(id => {
        this.$refs[id][0].setPreset(rgbPresetName)
        this.$refs[id][0].renderColorBar(rgbPresetName)
        this.$refs[id][0].setColorMap(rgbPresetName)
      })
    },
    voiChange(v) {
      const fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
      fusionViewportIds.forEach(id => {
        this.$refs[id][0].voiChange(v)
      })
    },
    async openMPRViewport(data = null) {
      return new Promise(async (resolve, reject) => {
        this.setToolsPassive()
        if (this.isMPR) {
          this.activeSeries(this.$refs[`viewport-MPR-0`][0].series, true)
          resolve(false)
          if (!data) return resolve(false)
          let viewportSeries = this.$refs[`viewport-MPR-0`][0].series
          if (data && viewportSeries.SeriesInstanceUid === data.SeriesInstanceUid) return resolve(true)
        } else {
          if (this.readingTool === 3) {
            let res = await this.changeScreenSave()
            if (!res) return false
          }
        }
        this.fullScreenIndex = null
        if (!data) {
          let { imageOrientationPatient, imagePositionPatient } = this.$refs[`viewport-${this.activeViewportIndex}`][0].imageInfo
          if (!imageOrientationPatient || !imagePositionPatient || imagePositionPatient.length <= 0 || imageOrientationPatient.length <= 0) return this.$confirm(this.$t('trials:reading:confirm:imageNotMPR'), this.$t('system:menu:confirm:title:warning'), {
            type: 'warning'
          })
        }
        const series = data ? data : this.$refs[`viewport-${this.activeViewportIndex}`][0].series
        if (series.ImageIds.length <= 5) return this.$confirm(this.$t('trials:reading:confirm:smallNumberOfimage'), this.$t('system:menu:confirm:title:warning'), {
          type: 'warning'
        })
        series.curIndex = series.SliceIndex
        DicomEvent.$emit('changeMPR')
        if (series.ImageIds.length > 500) {
          let res = await this.getSystemInfo()
          if (!res) return false
        }
        this.isMPR = true
        // this.rows = 3
        // this.cols = 1
        this.loading = true
        this.loadingText = this.$t('trials:lugano:message:loadVolumes')
        if (!data) this.activeViewportIndex = 0
        const visitTaskId = this.visitTaskList[this.activeTaskIndex].VisitTaskId
        this.$refs[visitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
        await this.getVolume(series)
        this.loading = false
        this.loadingText = null
        delete series.orientation
        delete series.isLocation
        this.$refs[`viewport-MPR-0`][0].setSeriesInfo(Object.assign({ orientation: 'AXIAL', isLocation: data && this.activeViewportIndex === 0 }, series))
        this.$refs[`viewport-MPR-1`][0].setSeriesInfo(Object.assign({ orientation: 'SAGITTAL', isLocation: data && this.activeViewportIndex === 1 }, series))
        this.$refs[`viewport-MPR-2`][0].setSeriesInfo(Object.assign({ orientation: 'CORONAL', isLocation: data && this.activeViewportIndex === 2 }, series))
        this.setToolActive('Crosshairs')
        resolve(false)
      })

    },
    async handleFusion(data) {
      try {
        if (!this.isFusion && this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].verifyAnnotationIsSave()) {
          const confirm = await this.$confirm(this.$t('trials:reading:confirm:clearnAnnotation'), this.$t('system:menu:confirm:title:warning'), {
            type: 'warning'
          })
          if (!confirm) return false
          this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].removeAllNoSaveAnnotation()
        }
        const { ct, pt } = data
        this.fusionOverlayModality = pt?.Modality || null
        if (ct.ImageIds.length > 400) {
          let res = await this.getSystemInfo()
          if (!res) return false
        }
        this.fusionVisible = false
        this.isFusion = true
        this.rows = 2
        this.cols = 2
        this.loading = true
        this.loadingText = this.$t('trials:lugano:message:loadVolumes')
        this.renderedTaskIds = []
        if (this.verifyFusionData(ct, pt)) {
          this.loading = false
          this.loadingText = null
          this.$refs[`viewport-0`][0].setSeriesInfo(ct)
          this.$refs[`viewport-1`][0].setSeriesInfo(pt)
          this.$refs[`viewport-2`][0].setSeriesInfo(pt)
          this.$refs[`viewport-3`][0].setSeriesInfo(pt)
          // this.resetAnnotation = false
          return true
        }
        if (!this.fusionSerieId.ct || this.fusionSerieId.ct !== ct.SeriesInstanceUid) {
          this.fusionSerieId.ct = ct.SeriesInstanceUid
        }
        if (!this.fusionSerieId.pt || this.fusionSerieId.pt !== pt.SeriesInstanceUid) {
          this.fusionSerieId.pt = pt.SeriesInstanceUid
        }
        await this.getVolume(pt)
        await this.getVolume(ct)
        await this.getVolume(pt, true)
        this.loading = false
        this.loadingText = null
        const ctData = {
          data: ct
        }
        const ptData = {
          data: pt
        }
        const fusionData = {
          ct,
          data: pt,
        }
        this.$refs[`viewport-0`][0].setSeriesInfo(ct)
        this.$refs[`viewport-1`][0].setSeriesInfo(pt)
        this.$refs[`viewport-2`][0].setSeriesInfo(pt)
        this.$refs[`viewport-3`][0].setSeriesInfo(pt)

        this.$refs[`viewport-fusion-0`][0].setSeriesInfo(ctData)
        this.$refs[`viewport-fusion-1`][0].setSeriesInfo(ptData, false, { colorMap: false })
        this.$refs[`viewport-fusion-2`][0].setSeriesInfo(fusionData, false, { isFusion: true, colorMap: true })
        this.$refs[`viewport-fusion-3`][0].setSeriesInfo(ptData, false, { isMip: true, colorMap: false })
        await this.initFusionHiddenSagViewport(pt)
        // this.resetAnnotation = false
        this.$nextTick(() => {
          this.setFusionMipJumpEnabled(this.activeTool === FusionJumpToPointTool.toolName)
          const defaultRange = this.getFusionDefaultColorMapRange(pt)
          const defaultUpper = this.getFusionDefaultColorMapUpper(defaultRange)
          this.$refs[`colorMap`].init()
          this.fusionOverlayDefaultRange = defaultRange
          this.fusionOverlayDefaultUpper = defaultUpper
          this.lastUpper = null
          this.hasFusionUpperInitialized = false
          this.$refs.colorMap.range = defaultRange
          this.$refs.colorMap.upper = defaultUpper
          this.$refs.colorMap.upperRangeChange(defaultRange)
          this.voiChange(defaultUpper)
        })
      } catch (err) {
        console.log(err)
        this.loading = false
        this.loadingText = null
      }
    },
    verifyFusionData(ct, pt) {
      if (this.fusionSerieId.ct === ct.SeriesInstanceUid && this.fusionSerieId.pt === pt.SeriesInstanceUid && cache.getVolume(ct.SeriesInstanceUid) && cache.getVolume(pt.SeriesInstanceUid) && cache.getVolume(`fusion_${pt.SeriesInstanceUid}`)) {
        return true
      }
      return false
    },
    async initFusionHiddenSagViewport(pt) {
      const ptVolumeId = pt?.SeriesInstanceUid
      if (!ptVolumeId || !cache.getVolume(ptVolumeId)) return
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const sagViewport = renderingEngine?.getViewport?.('viewport-fusion-hidden-sag')
      if (!sagViewport) return

      await sagViewport.setVolumes([{ volumeId: ptVolumeId }])
      const midIndex = Math.max(0, Math.floor((pt.ImageIds?.length || 1) / 2))
      await csUtils.jumpToSlice(sagViewport.element, { imageIndex: midIndex })
      sagViewport.render()
    },
    async getVolume(serie, isFusion = false) {
      return new Promise(async res => {
        let volumeId = `${isFusion ? 'fusion_' : ''}` + serie.SeriesInstanceUid;
        let volume = null;
        if (cache.getVolume(volumeId)) {
          volume = cache.getVolume(volumeId)
        } else {
          let imageIds = await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
          // imageIds = this.sortImageIdsByImagePositionPatient(imageIds)
          volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: imageIds })
          volume.load()
        }
        res({ volumeId, volume })
      })
    },
    sortImageIdsByImagePositionPatient(imageIds) {
      if (!Array.isArray(imageIds) || imageIds.length < 2) {
        return imageIds;
      }

      const firstPlane = metaData.get('imagePlaneModule', imageIds[0]);
      if (!firstPlane?.imagePositionPatient || !firstPlane?.imageOrientationPatient) {
        return imageIds;
      }

      const reference = firstPlane.imagePositionPatient.map((v) => Number(v));
      const iop = firstPlane.imageOrientationPatient.map((v) => Number(v));
      const row = [iop[0], iop[1], iop[2]];
      const col = [iop[3], iop[4], iop[5]];
      const normal = [
        row[1] * col[2] - row[2] * col[1],
        row[2] * col[0] - row[0] * col[2],
        row[0] * col[1] - row[1] * col[0],
      ];

      const pairs = [];
      for (const imageId of imageIds) {
        const plane = metaData.get('imagePlaneModule', imageId);
        const ipp = plane?.imagePositionPatient;
        if (!ipp) {
          return imageIds;
        }
        const pos = ipp.map((v) => Number(v));
        const positionVector = [
          reference[0] - pos[0],
          reference[1] - pos[1],
          reference[2] - pos[2],
        ];
        const distance =
          positionVector[0] * normal[0] +
          positionVector[1] * normal[1] +
          positionVector[2] * normal[2];
        pairs.push({ imageId, distance });
      }

      pairs.sort((a, b) => b.distance - a.distance);
      return pairs.map((p) => p.imageId);
    },
    getFusionDefaultColorMapRange(series) {
      if (this.fusionOverlayModality !== 'NM') {
        return DEFAULT_FUSION_COLOR_MAP_RANGE
      }

      const imageIds = this.sortImageIdsByImagePositionPatient(series?.ImageIds || [])
      const imageId = imageIds?.[0]
      if (!imageId) {
        return DEFAULT_FUSION_COLOR_MAP_RANGE
      }

      const voiLutModule = metaData.get('voiLutModule', imageId)
      const rawWidth = Array.isArray(voiLutModule?.windowWidth)
        ? voiLutModule.windowWidth[0]
        : voiLutModule?.windowWidth
      const windowWidth = Math.round(Number(rawWidth))

      if (!Number.isFinite(windowWidth) || windowWidth <= 0) {
        return DEFAULT_FUSION_COLOR_MAP_RANGE
      }

      return windowWidth
    },
    getFusionDefaultColorMapUpper(range) {
      if (this.fusionOverlayModality !== 'NM') {
        return DEFAULT_FUSION_COLOR_MAP_UPPER
      }

      const safeRange = Number(range)
      if (!Number.isFinite(safeRange) || safeRange <= 0) {
        return DEFAULT_FUSION_COLOR_MAP_UPPER
      }

      return Math.round(safeRange * 0.5)
    },
    upperRangeChange(upper) {
      if (!this.hasFusionUpperInitialized) {
        if (!upper) return
        this.hasFusionUpperInitialized = true
      }
      if (this.lastUpper === upper) return;
      this.lastUpper = upper;
      if (this.$refs.colorMap) {
        this.$refs.colorMap.changeVoi(upper)
      }
      this.voiChange(upper)
    },
    openFusion() {
      this.fusionVisible = true
    },
    closeFusion() {
      this.fusionVisible = false
      this.fusionOverlayModality = null
    },
    getTrialCriterion() {
      getCriterionReadingInfo({
        TrialId: this.$route.query.trialId,
        TrialReadingCriterionId: this.$route.query.TrialReadingCriterionId
      })
        .then((res) => {
          this.trialCriterion = res.Result
        })
        .catch(() => { })
    },
    async uploadScreenshots(fileName, file) {
      try {
        file = this.convertBase64ToBlob(file)
        const trialId = this.$route.query.trialId
        const taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
        const subjectId = taskInfo.SubjectId
        const result = await this.OSSclient.put(`/${trialId}/Read/${subjectId}/${this.taskInfo.VisitTaskId}/${fileName}.png`, file)
        return { isSuccess: true, result: result }
      } catch (e) {
        console.log(e)
        return { isSuccess: false, result: e }
      }
    },
    convertBase64ToBlob(imageEditorBase64) {
      const base64Arr = imageEditorBase64.split(',')
      let imgtype = ''
      let base64String = ''
      if (base64Arr.length > 1) {
        base64String = base64Arr[1]
        imgtype = base64Arr[0].substring(
          base64Arr[0].indexOf(':') + 1,
          base64Arr[0].indexOf(';')
        )
      }
      const bytes = atob(base64String)
      const bytesCode = new ArrayBuffer(bytes.length)
      const byteArray = new Uint8Array(bytesCode)
      for (let i = 0; i < bytes.length; i++) {
        byteArray[i] = bytes.charCodeAt(i)
      }
      return new Blob([bytesCode], { type: imgtype })
    },
    openUploadImage(status) {
      const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
      if (idx > -1) {
        this.taskId = this.visitTaskList[idx].VisitTaskId
      }
      this.uploadSubjectCode = localStorage.getItem("subjectCode")
      this.uploadSubjectId = localStorage.getItem("subjectId")
      this.uploadTrialCriterion = this.trialCriterion
      this.uploadStatus = status
      this[`${status}ImageVisible`] = true
    },
    getSystemInfo() {
      return new Promise(async resolve => {
        const systemInfo = new SystemInfo();
        const allInfo = systemInfo.getAllInfo();
        let deviceMemory = allInfo.hardware.deviceMemory; // 设备内存
        let { width, height } = allInfo.screen; // 分辨率
        let discrete = allInfo.webgl.gpuType.discrete; // 是否独立显卡
        let estimatedMemory = allInfo.webgl.memoryInfo.estimatedMemory; // 显卡内存
        // parseFloat(deviceMemory) < 16 ||
        if (width < 1920 || height < 1080 || !discrete || parseFloat(estimatedMemory) < 2) {
          let res = await this.$confirm(this.$t('browser:tip:ReadingConfiguration'), this.$t('system:menu:confirm:title:warning'), {
            type: 'warning'
          })
          resolve(res)
        } else {
          resolve(true)
        }

      })
    },
    async getSystemInfoReading() {
      return new Promise(async resolve => {
        let whitelisting = localStorage.getItem('whitelisting') ? JSON.parse(localStorage.getItem('whitelisting')) : []
        let user = md5(sessionStorage.getItem('identityUserId'))
        let r = whitelisting.some(item => item === user)
        if (r) return resolve(true)
        const systemInfo = new SystemInfo();
        const allInfo = systemInfo.getAllInfo();
        let deviceMemory = allInfo.hardware.deviceMemory; // 设备内存
        let { width, height } = allInfo.screen; // 分辨率
        // let discrete = allInfo.webgl.gpuType.discrete; // 是否独立显卡
        // let estimatedMemory = allInfo.webgl.memoryInfo.estimatedMemory; // 显卡内存
        // parseFloat(deviceMemory) < 16 || 
        if (width < 1920 || height < 1080) {
          let res = await this.$confirm(this.$t('browser:tip:Configuration'))
          whitelisting.push(user)
          localStorage.setItem('whitelisting', JSON.stringify(whitelisting))
          resolve(res)
        } else {
          resolve(true)
        }
      })

    },
  },
  beforeDestroy() {
    clearPTClinicalDataCache()
    DicomEvent.$off('isCanActiveNoneDicomTool')
    DicomEvent.$off('removeNoneDicomMeasureData')
    DicomEvent.$off('addNoneDicomMeasureData')
    if (this.saveCustomAnnotationTimer) {
      clearTimeout(this.saveCustomAnnotationTimer)
      this.saveCustomAnnotationTimer = null
    }
    if (this.timer) {
      Object.keys(this.timer).forEach(key => {
        if (this.timer[key]) {
          clearInterval(this.timer[key])
          clearTimeout(this.timer[key])
          this.timer[key] = null
        }
      })
      this.timer = {}
    }
    if (this.FullTimerOut) {
      clearTimeout(this.FullTimerOut)
      this.FullTimerOut = null
      this.isDelay = false
    }
    workSpeedclose(true)
  }
}
</script>
<style lang="scss" scoped>
.form-wrapper {
  ::v-deep .el-tabs__nav-scroll {
    width: 100%;
    display: flex;
    justify-content: center;
  }
}


.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: 13px;

          ul {
            list-style: none;
            margin: 0;
            padding: 0;
            text-align: center;

            li {
              span {
                display: block;
                padding: 8px;
              }
            }
          }

          ul li span:hover {
            background-color: #727272;
          }
        }

        .layout-content-ul li:hover {
          background-color: #727272;
        }

        .customize-standards-list {
          width: max-content;
          white-space: nowrap;

          li span {
            display: flex;
            align-items: center;
            padding: 8px !important;
            box-sizing: border-box;
          }
        }

        .wwwc-list {
          width: max-content;
          white-space: nowrap;

          li span {
            padding: 8px !important;
          }
        }

        .wwwc-item-content {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 16px;
          min-width: 210px;
        }

        .wwwc-item-label {
          text-align: left;
        }

        .wwwc-item-value {
          text-align: right;
        }

        .rotate-options-list {
          width: max-content;
          white-space: nowrap;

          li span {
            display: flex;
            align-items: center;
            padding: 8px !important;
            box-sizing: border-box;
          }
        }

        .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;
        }
      }

      .content-wrapper {
        flex: 1;
        height: calc(100% - 50px);
        display: flex;
        flex-direction: row;
      }

      .viewports-wrapper {
        flex: 1;

        .grid-container {
          display: grid;
          height: 100%;
          width: 100%;
          position: relative;
        }

        .fusion-hidden-viewports {
          position: absolute;
          left: -100000px;
          top: -100000px;
          width: 512px;
          height: 512px;
          overflow: hidden;
          opacity: 0;
          pointer-events: none;
        }

        .fusion-hidden-viewport {
          width: 512px;
          height: 512px;
        }

        .viewports-box {
          display: grid;
          position: absolute;
          height: 100%;
          width: 100%;
          top: 0;
          left: 0;
          bottom: 0;
          right: 0;
          z-index: 9;
          overflow: hidden;
        }

        .viewports-box-down {
          z-index: 1;
          opacity: 0;
          pointer-events: none;
        }

        .viewports-box-down {
          >.grid-cell {
            border-color: transparent;
          }
        }

        .viewports-box-full-screen {
          >.grid-cell {
            display: none;
          }

          >.grid-cell.cell-full-screen {
            display: flex;
          }
        }

        .grid-cell {
          border: 1px dashed #ccc;
          display: flex;
          align-items: center;
          justify-content: center;
          overflow: hidden;
          min-width: 0;
          min-height: 0;
          z-index: 1;
        }

        .grid-cell-3 {
          grid-column: 0 / 3;
          grid-row: 1 / 3;
        }

        .cell_active {
          border-color: #fafa00 !important;
        }

        .cell-full-screen {
          grid-column: 1 / -1;
          grid-row: 1 / -1;
          z-index: 9;
        }
      }

      .form-wrapper {
        width: 400px;
        height: 100%;
        border: 1px solid #727272;
        user-select: none;
      }
    }
  }

  .personal_config {
    ::v-deep .el-tabs__content {
      height: 450px;
      overflow-y: auto;
    }
  }

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

<style lang="scss">
.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;
  }
}

.manuals-full-dialog-container {
  .el-dialog__body {
    padding: 10px;
    height: calc(100% - 50px) !important;
  }

  .el-dialog__header {
    position: relative;
  }
}

.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;
  }
}

.cd-full-dialog-container {
  background: #fff !important;

  .el-dialog__body {
    padding: 10px;
    height: calc(100% - 50px) !important;
  }

  .el-dialog__header {
    position: relative;
  }
}

.key-series-popper {
  padding: 8px !important;
  background-color: #1e1e1e !important;
  border: 1px solid #4a4a4a !important;

  .key-series-list {
    width: 340px;
    max-height: 420px;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;

    &::-webkit-scrollbar {
      width: 8px;
    }

    &::-webkit-scrollbar-thumb {
      background: #4a4a4a;
      border-radius: 4px;
    }

    &::-webkit-scrollbar-track {
      background: transparent;
    }
  }

  .key-series-header {
    padding: 5px 12px;
    margin-bottom: 10px;
    background: linear-gradient(180deg, rgba(64, 64, 64, 0.92) 0%, rgba(43, 43, 43, 0.98) 100%);
    border: 1px solid #474747;
    border-radius: 6px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 6px 18px rgba(0, 0, 0, 0.2);
  }

  .key-series-header-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .key-series-header-title {
    display: flex;
    align-items: center;
    min-width: 0;
  }

  .key-series-header-dot {
    width: 8px;
    height: 8px;
    margin-right: 8px;
    border-radius: 50%;
    background: linear-gradient(180deg, #ffd76a 0%, #f39c12 100%);
    box-shadow: 0 0 8px rgba(243, 156, 18, 0.45);
  }

  .key-series-task-name {
    color: #fff;
    font-size: 15px;
    font-weight: 600;
    line-height: 22px;
    word-break: break-all;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  }

  .key-series-empty {
    text-align: center;
    color: #999;
    padding: 10px;
  }

  .key-series-item {
    text-align: left;
    display: flex;
    align-items: center;
    padding: 10px;
    cursor: pointer;
    background: linear-gradient(180deg, rgba(46, 46, 46, 0.96) 0%, rgba(40, 40, 40, 0.98) 100%);
    border-radius: 6px;
    margin-bottom: 8px;
    border: 1px solid #353535;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
    // transition: all 0.2s ease;

    &:last-child {
      margin-bottom: 0;
    }

    &:hover {
      background: linear-gradient(180deg, rgba(54, 54, 54, 0.98) 0%, rgba(46, 46, 46, 1) 100%);
      border-color: #5a5a5a;
      // transform: translateY(-1px);
      box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
    }

    .key-series-img {
      width: 60px;
      height: 60px;
      flex-shrink: 0;
      background-color: #000;
      display: flex;
      justify-content: center;
      align-items: center;
      overflow: hidden;
      margin-right: 12px;
      border-radius: 4px;
      border: 1px solid #3a3a3a;

      img {
        width: 100%;
        height: 100%;
        object-fit: contain;
      }

      .default-icon {
        font-size: 24px;
        color: #666;
      }
    }

    .key-series-info {
      flex: 1;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      justify-content: flex-start;
      padding-right: 5px;

      .key-series-meta {
        width: 100%;
        overflow: hidden;
        display: flex;
        flex-direction: row;
        align-items: center;
        margin-bottom: 4px;

        &:last-child {
          margin-bottom: 0;
        }
      }

      .key-series-label {
        font-size: 13px;
        color: #aaa;
        margin-right: 4px;
        white-space: nowrap;
      }

      .key-series-uid {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 13px;
        color: #ddd;
        line-height: 1.2;
      }
    }

    .key-series-star {
      margin-left: 10px;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 28px;
      height: 28px;
      border-radius: 50%;
      background: rgba(243, 156, 18, 0.08);
      border: 1px solid rgba(243, 156, 18, 0.16);

      .svg-icon {
        color: #f39c12;
        font-size: 16px;
      }
    }
  }
}
</style>
