<template>
  <custom-modal
    v-model="visible"
    :width="1280"
    class="interactive-analysis-tools-dialog"
    :enableFullScreen="true"
    title="Interactive Analyse Tools"
    :footer="null"
    destroy-on-close
    @fullscreen="handleFullScreenChange"
  >
    <div class="interactive-analysis-tools">
      <div class="interactive-analysis-tools-left">
        <div class="chart">
          <CustomChart
            ref="chartRef"
            :option="option"
            :opts="opts"
            @zr:mousedown="handleMouseDown"
            @zr:mouseup="handleMouseUp"
            @brushEnd="handleBrushEnd"
            @zr:click="handleChartClick"
          />
          <rect-list
            ref="rectListRef"
            :chartRef="$refs.chartRef"
            :baseControls="baseCtrls_Copy"
            :draggable="isModifying"
            @move="handleMove"
            @moveEnd="handleMoveEnd"
          />
        </div>
        <!-- 缩略图 -->
        <div class="thumbnail">
          <CustomChart ref="thumbnailRef" :option="thumbnailOption" />
        </div>
        <!-- 缩略图结束 -->

        <!-- 表格 -->
        <div class="table">
          <p class="title">
            <span @click="handleChangeMarkLine('prev')">&lt; </span>
            6 Peaks with Anthro.Nuclides
            <span @click="handleChangeMarkLine('next')">&gt;</span>
          </p>
          <custom-table
            ref="tableRef"
            size="small"
            :class="list.length ? 'has-data' : ''"
            :list="list"
            :columns="columns"
            :scroll="{ y: 288 }"
            :selectedRowKeys.sync="selectedKeys"
            rowKey="index"
            :canDeselect="false"
            @rowClick="handleTableRowClick"
          >
          </custom-table>
          <div class="operators">
            <a-button type="primary" @click="nuclideReviewModalVisible = true">Nuclide Review Window</a-button>
            <a-button type="primary" @click="handleAddPeakComment()">Add Peak Comment</a-button>
            <a-button type="primary" @click="handleAddGeneralComment()">Add General Comment</a-button>
          </div>
        </div>
        <!-- 表格结束 -->

        <resize-observer @notify="handleResize" />
      </div>

      <!-- 右侧 -->
      <div class="interactive-analysis-tools-right">
        <title-over-border class="peak-box-container" :title="btnGroupType == 1 ? 'Peak' : 'Baseline Control Points'">
          <div class="peak-box">
            <!-- 按钮组1 -->
            <template v-if="btnGroupType == 1">
              <div class="peak-box-item">
                <a-button type="primary" @click="handleInsert">Insert</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" @click="handleDel">Delete</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" :class="{ 'is-fitting': isFitting }" @click="handleFit">Fit</a-button>
              </div>

              <div class="peak-box-item symbol" :key="4">
                <a-button type="primary" @click="handleChangeMarkLine('prev')">&lt;</a-button>
                <a-button type="primary" @click="handleChangeMarkLine('next')">&gt;</a-button>
              </div>

              <div class="peak-box-item base-line">
                <a-button type="primary" @click="handleSwitchOperation">BaseLine</a-button>
              </div>
            </template>
            <!-- 按钮组2 -->
            <template v-if="btnGroupType == 2">
              <div class="peak-box-item">
                <a-button type="primary" @click="handleAddCP">(A)dd CP</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" @click="handleRemoveCP">(R)emove CP</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" key="modify-btn" :class="{ 'is-modify': isModifying }" @click="handleModifyCP"
                  >(M)odify CP</a-button
                >
              </div>
              <div class="peak-box-item">
                <a-button type="primary" @click="handleEditSlope">Edit (S)lope</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" :disabled="isOperationStackEmpty" @click="handleUndo">Undo</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" :loading="isReploting" @click="handleReplot">Replot</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" :loading="isAccepting" @click="handleAccept">Accept</a-button>
              </div>
              <div class="peak-box-item">
                <a-button type="primary" @click="handleSwitchOperation">Cancel</a-button>
              </div>
            </template>
          </div>
        </title-over-border>
        <div class="reset-btn-box">
          <a-button type="primary" @click="handleResetChart">Reset Chart</a-button>
        </div>
        <div class="identify-box">
          <title-over-border title="Nuclide Identify">
            <a-form-model class="tolerance">
              <a-form-model-item label="Tolerance">
                <a-input-number
                  v-model="model.tolerance"
                  :step="0.1"
                  :min="0"
                  @change="handleToleranceChange"
                ></a-input-number>
              </a-form-model-item>
            </a-form-model>
            <div class="identify-item">
              <div class="title">Possible Nuclide</div>
              <a-spin :spinning="!!(selectedTableItem && selectedTableItem._loading)">
                <div class="content">
                  <template v-if="selectedTableItem && selectedTableItem._possible">
                    <div
                      class="item"
                      :class="{ active: possible == model.possibleNuclide }"
                      v-for="(possible, index) in selectedTableItem._possible"
                      :key="index"
                      @click="model.possibleNuclide = possible"
                    >
                      {{ possible }}
                    </div>
                  </template>
                </div>
              </a-spin>
            </div>
            <div class="identify-item">
              <div class="title">Nuclide Identified</div>
              <div class="content">
                <template v-if="selectedTableItem">
                  <div
                    class="item"
                    :class="{ active: identified == model.identifiedNuclide }"
                    v-for="(identified, index) in selectedTableItem.nuclides"
                    :key="index"
                    @click="model.identifiedNuclide = identified"
                  >
                    {{ identified }}
                  </div>
                </template>
              </div>
            </div>
            <div class="identify-operators">
              <span class="text">{{ model.possibleNuclide }}</span>
              <a-button type="primary" :disabled="!model.possibleNuclide" @click="handleAddNuclide">Add</a-button>
              <a-button type="primary" @click="handleDelNuclide">Del</a-button>
            </div>
          </title-over-border>
        </div>
      </div>
      <!-- 右侧结束 -->
    </div>
    <!-- Peak Comment弹窗 开始 -->
    <peak-comment-modal v-model="peakCommentModalVisible" :curRow="curRow" />
    <!-- Peak Comment弹窗 结束 -->

    <!-- General Comment弹窗 开始 -->
    <general-comment-modal v-model="generalCommentModalVisible" />
    <!-- General Comment弹窗 结束 -->

    <!-- Fit Peaks and Baseline弹窗 开始 -->
    <fit-peaks-and-base-line-modal
      v-model="fitPeaksAndBaselineModalVisible"
      :channel_1="channel_1"
      :channel_2="channel_2"
      :isInsertPeak="isInsertPeak"
      @result="handleInsertSuccess"
      @cancel="handleCancelSuccess"
    />
    <!-- Fit Peaks and Baseline弹窗 结束 -->
    <!-- Nuclide Review 弹窗开始 -->
    <nuclide-review-modal v-model="nuclideReviewModalVisible" :sampleId="sampleId" :channel="currChannel" />
    <!-- Nuclide Review 弹窗结束 -->

    <!-- Edit Slope 弹窗 -->
    <edit-slope-modal ref="editSlopeModal" @change="handleSlopeChange" />
    <!-- Edit Slope 结束 -->
  </custom-modal>
</template>

<script>
import CustomChart from '@/components/CustomChart/index.vue'
import TitleOverBorder from '../../TitleOverBorder.vue'
import PeakCommentModal from './components/PeakCommentModal.vue'
import FitPeaksAndBaseLineModal from './components/FitPeaksAndBaselineModal.vue'
import NuclideReviewModal from './components/NuclideReviewModal.vue'
import ModalMixin from '@/mixins/ModalMixin'
import { getAction, postAction } from '@/api/manage'
import { cloneDeep } from 'lodash'
import { buildLineSeries, findSeriesByName, getXAxisAndYAxisByPosition, rangeNumber } from '@/utils/chartHelper'
import SampleDataMixin from '@/views/spectrumAnalysis/SampleDataMixin'
import GeneralCommentModal from './components/GeneralCommentModal.vue'
import EditSlopeModal from './components/EditSlopeModal.vue'
// import Response from './Response.json'
import { updateBaseLine } from '@/utils/WasmHelper'
import RectList from './components/RectList.vue'
import { isNullOrUndefined } from '@/utils/util'
import { findNearPeak, getLineData, transformPointListData } from '@/utils/sampleHelper'
import { getSampleData } from '@/utils/SampleStore'

// 初始配置
const initialOption = {
  grid: {
    top: 40,
    left: 80,
    right: 30,
    bottom: 30,
  },
  title: {
    text: '',
    left: 'center',
    bottom: 10,
    textStyle: {
      color: '#8FD4F8',
      rich: {
        a: {
          padding: [0, 20, 0, 0],
          fontSize: 16,
        },
      },
    },
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      lineStyle: {
        color: '#3CAEBB',
        width: 1,
      },
    },
    formatter: undefined,
    className: 'figure-chart-option-tooltip',
  },
  xAxis: {
    axisLine: {
      lineStyle: {
        color: '#ade6ee',
      },
    },
    splitLine: {
      show: false,
    },
    axisLabel: {
      textStyle: {
        color: '#ade6ee',
      },
    },
    min: 1,
    max: 'dataMax',
    animation: false,
  },
  yAxis: {
    type: 'log',
    name: 'Counts',
    nameLocation: 'center',
    nameGap: 40,
    nameTextStyle: {
      color: '#8FD4F8',
      fontSize: 16,
    },
    axisLine: {
      show: true,
      lineStyle: {
        color: '#ade6ee',
      },
    },
    splitLine: {
      show: true,
      lineStyle: {
        color: 'rgba(173, 230, 238, .2)',
      },
    },
    axisLabel: {
      textStyle: {
        color: '#ade6ee',
      },
    },
    min: 0.1,
    max: 'dataMax',
    animation: false,
  },
  series: [],
  brush: {},
}

const columns = [
  {
    title: 'ID',
    customRender: (_, __, index) => {
      return index + 1
    },
    width: 60,
  },
  {
    title: 'Energy (keV)',
    dataIndex: 'energy',
    width: 120,
    customRender: (text) => {
      return text.toFixed(3)
    },
  },
  {
    title: 'Centroid (C)',
    dataIndex: 'peakCentroid',
    width: 120,
    customRender: (text) => {
      return text.toFixed(3)
    },
  },
  {
    title: 'FWHM (keV)',
    dataIndex: 'fwhm',
    width: 120,
    customRender: (text) => {
      return text.toFixed(3)
    },
  },
  {
    title: 'Area',
    dataIndex: 'area',
    width: 120,
    customRender: (text) => {
      return text.toFixed(3)
    },
  },
  {
    title: 'Detectability',
    dataIndex: 'significance',
    width: 120,
    customRender: (text) => {
      return text == 'Infinity' ? 'inf' : text.toFixed(3)
    },
  },
  {
    title: '#Cmnt',
    dataIndex: 'comments',
    width: 120,
  },
  {
    title: 'Nuclides',
    dataIndex: 'nuclides',
    width: 120,
    ellipsis: true,
    customRender: (text) => {
      return text && text.join(';')
    },
  },
]

// 缩略图配置
const thumbnailOption = {
  grid: {
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
  xAxis: {
    type: 'category',
    axisLine: {
      lineStyle: {
        color: '#fff',
      },
    },
    splitLine: {
      show: false,
    },
    axisLabel: {
      show: false,
    },
    axisTick: {
      show: false,
    },
    min: 1,
    max: 'dataMax',
  },
  yAxis: {
    type: 'value',
    axisLine: {
      show: false,
    },
    splitLine: {
      show: false,
    },
    axisLabel: {
      show: false,
    },
    max: 0,
    min: 0,
  },
  series: null,
}

const nuclideIdentifyModal = {
  possibleNuclide: '',
  tolerance: 0.5,
  identifiedNuclide: '',
}

// 操作类型
const Operators = {
  ADD: 1, // 新增
  REMOVE: 2, // 移除
  MODIFY: 3, // 改变
  SLOPE_CHANGE: 4, // 改变slope
}

export default {
  mixins: [ModalMixin, SampleDataMixin],
  components: {
    CustomChart,
    TitleOverBorder,
    PeakCommentModal,
    FitPeaksAndBaseLineModal,
    NuclideReviewModal,
    GeneralCommentModal,
    EditSlopeModal,
    RectList,
  },
  props: {
    colorConfig: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      columns,
      searchParam: {
        energy: '',
        tolerance: '',
      },
      option: cloneDeep(initialOption),
      opts: { notMerge: false },
      thumbnailOption: cloneDeep(thumbnailOption),
      channelBaseCPChart: [],
      channelBaseLineChart: [],
      channelCountChart: [],
      channelPeakChart: [],
      barChart: [],
      energy: [],
      list: [],
      BaseCtrls: {},
      baseCtrls_Copy: {},
      sampleId: -1,

      peakCommentModalVisible: false, // Comment 弹窗是否显示
      generalCommentModalVisible: false, // Comment 弹窗是否显示

      btnGroupType: 1, // 右侧 Peak 中的按钮组切换

      selectedKeys: [], // 选中的列表

      fitPeaksAndBaselineModalVisible: false, // Fit Peaks And Base Line 弹窗
      nuclideReviewModalVisible: false, // Nuclide Review 弹窗

      model: cloneDeep(nuclideIdentifyModal),

      currChannel: undefined, // 当currChannel前选中的channel

      channel_1: undefined, // 用于Fit Peaks And Baseline Modal的道值
      channel_2: undefined,
      isInsertPeak: false, // 是否是插入Peak

      selectedTableItem: undefined, // 当前选中的表格项

      isModifying: false, // 正在修改控制点
      isFitting: false, // 正在进行Fit操作
      firstFittingChannel: null, // Fit操作时点击的第一个channel
      isAccepting: false,
      isReploting: false,

      operationStack: [], // 操作记录
      replotNeeded: false,
    }
  },
  created() {
    this.option.tooltip.formatter = (params) => {
      const channel = parseInt(params[0].value[0])
      const energy = this.energy.pointlist ? this.energy.pointlist[channel - 1].x : 0
      return `<div class="channel">Channel: ${channel}</div>
              <div class="energy">${isNullOrUndefined(energy) ? '' : `Energy: ${energy.toFixed(2)}`}</div>`
    }
  },
  methods: {
    getInfo() {
      this.option.series = []
      this.thumbnailOption.series = []
      this.list = []
      this.model = cloneDeep(nuclideIdentifyModal)

      const { inputFileName } = this.sampleData

      const currSampleDetailInfo = getSampleData(inputFileName)
      const {
        data: { allData, shadowChannelChart, shapeChannelData, peak, BaseCtrls, barChart },
      } = currSampleDetailInfo

      const channelBaseLine = getLineData(allData, 'BaseLine', 'channel')
      const channelPeakGroup = getLineData(allData, 'Peak', 'channel', true)

      const allEnergy = getLineData(allData, 'Energy', 'energy')

      this.channelBaseCPChart = shapeChannelData
      this.channelBaseLineChart = channelBaseLine
      this.channelCountChart = shadowChannelChart
      this.channelPeakChart = channelPeakGroup
      this.energy = allEnergy
      this.BaseCtrls = BaseCtrls
      this.barChart = barChart

      this.setChartOption(channelBaseLine, shadowChannelChart, channelPeakGroup, shapeChannelData, barChart)
      this.list = peak
    },

    setChartOption(baseLine, count, peaks, baseCP, bar) {
      const series = []

      // 推入BaseLine
      series.push(this.buildBaseLine(baseLine))

      // 推入Count
      series.push(this.buildCountLine(count))

      // 推入Peak
      series.push(...this.buildPeaks(peaks))

      // 推入基线控制点
      series.push(this.buildCtrlPoint(baseCP))
      this.option.series = series

      this.thumbnailOption.series = this.buildBarChart(bar)
      this.setThumbnailRange(1, bar.length)
    },

    reset() {
      this.currChannel = undefined
      this.btnGroupType = 1
      this.opts.notMerge = false
      this.isFitting = false
      this.replotNeeded = false
      this.selectedTableItem = null
      this.$nextTick(() => {
        this.option.brush = { toolbox: [] }
        const firstLine = this.list[0]
        if (firstLine) {
          this.handleTableRowClick(firstLine)
          this.selectedKeys = [firstLine.index]
        }
      })
      this.clearRect()
      this.handleResetChart()
    },

    beforeModalOpen() {
      this.getInfo()
      this.reset()
    },

    // 点击图表，设置红线，改变表格的选中项
    handleChartClick(param) {
      const { offsetX, offsetY } = param
      const point = getXAxisAndYAxisByPosition(this.$refs.chartRef.getChartInstance(), offsetX, offsetY)
      if (point) {
        const xAxis = Math.round(point[0])
        this.setMarkLineXAxis(xAxis)

        this.currChannel = xAxis
        const { index } = findNearPeak(xAxis, this.list)

        if (this.list.length) {
          const selectedRow = this.list[index]
          this.selectTableRow(selectedRow.index, index)
          this.getSelPosNuclide(selectedRow)
          this.selectedTableItem = selectedRow
        }

        // 如果点击了Fit按钮
        if (this.isFitting) {
          this.handleFittingChannel(xAxis)
        }
      }
    },

    // 处理Fit中的两个channel
    handleFittingChannel(channel) {
      // 第一个channel存在，又点了一次，则是第二次点击
      if (this.firstFittingChannel !== null) {
        // 查找两个channel之间的Peak峰

        let left = this.firstFittingChannel
        let right = channel
        if (left > right) {
          right = left
          left = channel
        }

        const peaksBetweenChannel = this.list.filter((peak) => {
          const centroidId = peak.peakCentroid
          return centroidId >= left && centroidId <= right
        })

        if (!peaksBetweenChannel.length) {
          this.$message.warn(`There are 0 peak between channel ${left} and ${right}`)
          this.isFitting = false
          return
        }

        this.channel_1 = left
        this.channel_2 = right
        this.isInsertPeak = false
        this.fitPeaksAndBaselineModalVisible = true

        this.isFitting = false
        this.firstFittingChannel = null
      } else {
        this.firstFittingChannel = channel
      }
    },

    // 切换图表上的红色竖线及表格选中
    handleChangeMarkLine(direction) {
      const prevAxis = this.getMarkLineXAxis()
      let i,
        size = this.list.length
      if (direction == 'next') {
        for (i = 0; i < size; i++) {
          const centroid = Math.round(this.list[i].peakCentroid)
          if (centroid > prevAxis) {
            this.setMarkLineXAxis(centroid)
            const selectedRow = this.list[i]
            this.selectedTableItem = selectedRow
            this.selectTableRow(selectedRow.index, i)
            this.getSelPosNuclide(selectedRow)
            return
          }
        }
      } else if (direction == 'prev') {
        for (i = size - 1; i >= 0; i--) {
          const centroid = Math.round(this.list[i].peakCentroid)
          if (centroid < prevAxis) {
            this.setMarkLineXAxis(centroid)
            const selectedRow = this.list[i]
            this.selectedTableItem = selectedRow
            this.selectTableRow(selectedRow.index, i)
            this.getSelPosNuclide(selectedRow)
            return
          }
        }
      }
    },

    /**
     * 根据当前Peak调整缩放范围
     */
    adjustArea() {
      const {
        xAxis: { max, min },
      } = this.option

      // 找到最高点在这个范围内的峰
      const peaks = []
      for (let i = 0; i < this.channelPeakChart.length; i++) {
        const peak = this.channelPeakChart[i]
        const pointlist = peak.pointlist

        // 如果 第一个/最后一个 都 大于/小于 范围，跳过
        if (pointlist[0].x > max || pointlist[pointlist.length - 1].x < min) {
          continue
        }

        // 找到峰最大值
        const peakMaxY = Math.max(...pointlist.map(({ y }) => y))
        const find = pointlist.find(({ y }) => y == peakMaxY)

        // 如果最大值在范围内
        if (find.x >= min && find.x <= max) {
          peaks.push({
            max: peakMaxY,
            min: Math.min(pointlist[0].y, pointlist[pointlist.length - 1].y),
          })
        }
      }

      const peaksMax = Math.max(...peaks.map(({ max }) => max))
      const peaksMin = Math.min(...peaks.map(({ min }) => min))

      const {
        yAxis: { max: yAxisMax, min: yAxisMin },
      } = this.option

      if (peaksMax > yAxisMax) {
        this.option.yAxis.max = Math.ceil(peaksMax)
      }

      if (peaksMin < yAxisMin) {
        this.option.yAxis.min = Math.floor(peaksMin)
      }
    },

    selectTableRow(key, index) {
      this.selectedKeys = [key]
      this.$refs.tableRef.scrollIntoView(index)
    },

    // 设置红色标记线的位置
    setMarkLineXAxis(xAxis) {
      const markLineOption = this.option.series[0].markLine.data[0]
      markLineOption.xAxis = xAxis

      const { xAxis: chartXAxisOption } = this.option
      const { max, min } = chartXAxisOption

      // 如果不在范围内
      if (xAxis >= max || xAxis <= min) {
        const halfDiff = (max - min) / 2
        const lastChannel = this.channelCountChart.pointlist[this.channelCountChart.pointlist.length - 1].x
        let nextMax = xAxis + halfDiff
        let nextMin = xAxis - halfDiff
        chartXAxisOption.max = nextMax > lastChannel ? lastChannel : nextMax
        chartXAxisOption.min = nextMin < 1 ? 1 : nextMin

        this.setThumbnailRange(chartXAxisOption.min, chartXAxisOption.max)
        this.adjustArea()
      }
    },

    getMarkLineXAxis() {
      const markLineOption = this.option.series[0].markLine.data[0]
      return markLineOption.xAxis
    },

    // 获取右下角possible nuclide 和 identified nuclide
    async getSelPosNuclide(row) {
      this.model.possibleNuclide = ''
      this.model.identifiedNuclide = ''
      this.model.tolerance = 0.5

      if (!row._possible) {
        this.$set(row, '_loading', true)
        try {
          const { sampleId, inputFileName: fileName } = this.sampleData
          const { success, result, message } = await getAction('/gamma/getSelPosNuclide', {
            sampleId,
            channel: Math.round(row.peakCentroid),
            fileName,
          })
          if (success) {
            const { possible } = result
            this.$set(row, '_possible', possible)
          } else {
            this.$message.error(message)
          }
        } catch (error) {
          console.error(error)
        } finally {
          row._loading = false
        }
      }
    },
    async handleToleranceChange(val) {
      // this.selectedTableItem._loading = true
      this.searchParam.energy = this.selectedTableItem.energy
      this.searchParam.tolerance = val
      try {
        const { sampleId, inputFileName: fileName } = this.sampleData
        const { success, result, message } = await getAction('/gamma/searchNuclide', {
          sampleId,
          fileName,
          ...this.searchParam,
        })
        if (success) {
          const { list } = result
          this.selectedTableItem._possible = list
        } else {
          this.$message.error(message)
        }
      } catch (error) {
        console.error(error)
      } finally {
        // this.selectedTableItem._loading = false
      }
    },

    // 显示peak comment弹窗
    handleAddPeakComment() {
      if (!this.selectedKeys.length) {
        this.$message.warn('Please Select a Peak that You Want to Add Comment!')
        return
      }
      this.peakCommentModalVisible = true
    },

    // 显示general comment弹窗
    handleAddGeneralComment() {
      this.generalCommentModalVisible = true
    },

    // Insert按钮
    handleInsert() {
      this.isFitting = false
      const { rg_high, rg_low } = this.BaseCtrls
      if (!this.currChannel || this.currChannel <= rg_low + 1 || this.currChannel >= rg_high - 1) {
        this.$message.warn("Couldn't insert peak, maybe out of range")
        return
      }

      this.channel_1 = this.currChannel

      this.fitPeaksAndBaselineModalVisible = true
      this.isInsertPeak = true
    },

    // 点击 Fit Peak XXX 弹窗中的 Peaks 按钮
    handleInsertSuccess(result) {
      const {
        allData,
        barChart,
        channelBaseLineChart,
        channelPeakChart,
        shadowChannelChart,
        shadowEnergyChart,
        shapeChannelData,
        shapeEnergyData,
        table,
      } = result

      this.$bus.$emit('gammaRefresh', {
        allData,
        channelPeakChart,
        shadowChannelChart,
        shadowEnergyChart,
        shapeChannelData,
        shapeEnergyData,
        peak: table,
        barChart: this.barChart,
      })

      this.channelPeakChart = channelPeakChart
      this.channelBaseLineChart = channelBaseLineChart
      this.barChart = barChart

      this.setChartOption(
        channelBaseLineChart,
        this.channelCountChart,
        channelPeakChart,
        this.channelBaseCPChart,
        barChart
      )
      this.list = table
      this.handleTableRowClick(this.list[this.curRow])
    },

    // 点击 Fit Peak XXX 弹窗中的 Cancel 按钮
    handleCancelSuccess(result) {
      const { channelPeakChart, table } = result
      this.channelPeakChart = channelPeakChart

      this.setChartOption(
        this.channelBaseLineChart,
        this.channelCountChart,
        channelPeakChart,
        this.channelBaseCPChart,
        this.barChart
      )

      this.list = table
    },

    // 删除
    handleDel() {
      this.isFitting = false

      if (!this.selectedKeys.length) {
        this.$message.warn('No peak to delete.')
        return
      }

      this.$confirm({
        title: 'Warning',
        content: 'Are you sure to delete this peak?',
        cancelButtonProps: {
          props: {
            type: 'warn',
          },
        },
        onOk: async () => {
          // this.list.splice(findIndex, 1)
          // this.selectedKeys = []

          // const seriesIndex = this.option.series.findIndex(item => {
          //   return item.name == 'Peak_' + willDelKey
          // })

          // this.opts.notMerge = true
          // this.option.series.splice(seriesIndex, 1)
          // this.channelPeakChart.splice(findIndex, 1)

          // this.$nextTick(() => {
          //   this.resetChartOpts()
          // })
          try {
            const { inputFileName: fileName } = this.sampleData
            const { success, result, message } = await getAction('/gamma/deletePeak', {
              fileName,
              curRow: this.curRow,
            })
            if (success) {
              const {
                allData,
                channelPeakChart,
                shadowChannelChart,
                shadowEnergyChart,
                shapeChannelData,
                shapeEnergyData,
                table,
              } = result

              this.$bus.$emit('gammaRefresh', {
                allData,
                channelPeakChart,
                shadowChannelChart,
                shadowEnergyChart,
                shapeChannelData,
                shapeEnergyData,
                peak: table,
                barChart: this.barChart,
              })

              this.opts.notMerge = true
              this.channelPeakChart = channelPeakChart
              const series = []
              // 推入旧的BaseLine
              series.push(this.buildBaseLine(this.channelBaseLineChart))

              // 推入旧的Count
              series.push(this.buildCountLine(this.channelCountChart))

              // 推入Peak
              series.push(...this.buildPeaks(channelPeakChart))

              // 推入旧的基线控制点
              series.push(this.buildCtrlPoint(this.channelBaseCPChart))

              this.list = table

              this.option.series = series
              this.$nextTick(() => {
                this.resetChartOpts()
              })

              this.selectedKeys = []

              this.selectedTableItem = null
            } else {
              this.$message.error(message)
            }
          } catch (error) {
            console.error(error)
          }
        },
      })
    },

    // 重置图表配置
    resetChartOpts() {
      this.opts.notMerge = false
      this.option.brush = { toolbox: [] }
    },

    // 匹配
    handleFit() {
      if (!this.channelPeakChart || !this.channelPeakChart.length) {
        this.$message.warn('No peak to fit.')
        return
      }

      this.isFitting = true
      this.firstFittingChannel = null
    },

    // 表格的行点击
    handleTableRowClick(row, index) {
      if (this.selectedTableItem == row) {
        return
      }

      const channel = Math.round(row.peakCentroid)
      this.currChannel = channel

      this.option.series[0].markLine.data[0].xAxis = channel

      const { xAxis: chartXAxisOption } = this.option
      const { max, min } = chartXAxisOption

      // 如果不在范围内
      if (channel >= max || channel <= min) {
        const halfDiff = (max - min) / 2
        const lastChannel = this.channelCountChart.pointlist[this.channelCountChart.pointlist.length - 1].x
        let nextMax = channel + halfDiff
        let nextMin = channel - halfDiff
        chartXAxisOption.max = nextMax > lastChannel ? lastChannel : nextMax
        chartXAxisOption.min = nextMin < 1 ? 1 : nextMin

        this.setThumbnailRange(chartXAxisOption.min, chartXAxisOption.max)
      }

      this.getSelPosNuclide(row)

      this.selectedTableItem = row

      this.adjustArea()
    },

    // 鼠标按下时开启可刷选状态
    handleMouseDown() {
      if (this.isModifying) {
        return
      }

      const chart = this.$refs.chartRef.getChartInstance()
      chart.dispatchAction({
        type: 'takeGlobalCursor',
        // 如果想变为“可刷选状态”，必须设置。不设置则会关闭“可刷选状态”。
        key: 'brush',
        brushOption: {
          // 参见 brush 组件的 brushType。如果设置为 false 则关闭“可刷选状态”。
          brushType: 'rect',
        },
      })
    },

    handleMouseUp() {
      setTimeout(() => {
        const chart = this.$refs.chartRef.getChartInstance()
        this.clearBrush(chart)
      }, 0)
    },

    clearBrush(chart) {
      // 清理刷选的范围
      chart.dispatchAction({
        type: 'brush',
        areas: [],
      })

      // 改为不可刷选状态
      chart.dispatchAction({
        type: 'takeGlobalCursor',
      })
    },

    // 刷选完毕时
    handleBrushEnd(param) {
      const chart = this.$refs.chartRef.getChartInstance()
      const areas = param.areas[0]
      if (areas) {
        const range = areas.range
        const [[minX, maxX], [minY, maxY]] = range
        const point1 = chart.convertFromPixel({ seriesIndex: 0 }, [minX, minY]).map((num) => parseInt(num.toFixed()))
        const point2 = chart.convertFromPixel({ seriesIndex: 0 }, [maxX, maxY]).map((num) => parseInt(num.toFixed()))
        const xAxisMax = chart.getModel().getComponent('xAxis').axis.scale._extent[1]
        const yAxisMax = this.option.yAxis.max
        let [x1, y2, x2, y1] = [...point1, ...point2] // 根据解析出的数据确定真实的范围
        const xAxisLimit = rangeNumber(1, xAxisMax)
        const yAxisLimit = rangeNumber(0.1, yAxisMax)
        x1 = xAxisLimit(x1)
        x2 = xAxisLimit(x2)
        y1 = yAxisLimit(y1)
        y2 = yAxisLimit(y2)
        this.option.xAxis.min = x1
        this.option.xAxis.max = x2
        this.option.yAxis.min = y1
        this.option.yAxis.max = y2

        this.adjustArea()

        this.setThumbnailRange(x1, x2)
        if (this.btnGroupType == 2) {
          this.buildRect()
        }
      }
      this.clearBrush(chart)
    },

    // 查找barChart范围内的最大值
    setThumbnailRange(x1, x2) {
      const slicedArr = this.barChart.slice(x1 - 1, x2)
      const yData = slicedArr.map(({ y }) => y)
      const max = Math.max(...yData)
      const min = Math.min(...yData)
      const thumbnailYMax = Math.max(Math.abs(max), Math.abs(min))

      this.thumbnailOption.xAxis.min = x1
      this.thumbnailOption.xAxis.max = x2
      this.thumbnailOption.yAxis.max = thumbnailYMax
      this.thumbnailOption.yAxis.min = -thumbnailYMax
    },

    handleResetChart() {
      this.option.xAxis.min = 1
      this.option.xAxis.max = 'dataMax'
      this.option.yAxis.min = 0.1
      this.option.yAxis.max = 'dataMax'

      this.setThumbnailRange(1, this.barChart.length)

      if (this.btnGroupType == 2) {
        this.buildRect()
      }
    },

    // 切换操作
    handleSwitchOperation() {
      // 切换到Base Line 和 Control Point 操作
      if (this.btnGroupType == 1) {
        this.btnGroupType = 2
        this.baseCtrls_Copy = cloneDeep(this.BaseCtrls)
        this.replotNeeded = false

        // 供编辑的白色基线
        const baseLineEditSeries = buildLineSeries(
          'BaseLine_Edit',
          this.baseCtrls_Copy.baseline.map((val, index) => [index + 1, val]),
          this.colorConfig.Color_Fitbase || '#fff',
          {
            zlevel: 21,
          }
        )
        this.option.series.push(baseLineEditSeries)

        this.$nextTick(() => {
          this.buildRect()
        })
      }
      // 切换回 Peak 操作
      else {
        this.btnGroupType = 1
        this.opts.notMerge = true
        const baseLineEditSeries = findSeriesByName(this.option.series, 'BaseLine_Edit')
        const index = this.option.series.findIndex((item) => item == baseLineEditSeries)
        this.option.series.splice(index, 1)

        this.clearRect()

        const baseLineSeries = findSeriesByName(this.option.series, 'BaseLine')
        baseLineSeries.data = transformPointListData(this.channelBaseLineChart.pointlist) // 恢复基线

        const baseLineCP = findSeriesByName(this.option.series, 'BaseLine_Ctrl_Point')
        baseLineCP.data = this.buildCPPointData(this.channelBaseCPChart)

        this.redrawPeaks(this.channelPeakChart)

        this.$nextTick(() => {
          this.resetChartOpts()
        })
      }

      this.isModifying = false
      this.isFitting = false
      this.clearOperationStack()
    },

    // 根据数据绘制小方块
    buildRect() {
      this.$refs.rectListRef.init()
    },

    clearRect() {
      const rectListRef = this.$refs.rectListRef
      if (rectListRef) {
        rectListRef.clear()
      }
    },

    // 小方块移动
    handleMove(yctrl) {
      this.baseCtrls_Copy.yctrl = yctrl
      this.redrawBaseLine()
    },

    // 小方块移动完毕
    handleMoveEnd(prevYAxis, index) {
      this.isModifying = false
      this.pushOperationStack(Operators.MODIFY, {
        index,
        prevYAxis,
      })
    },

    // 重新生成基线
    redrawBaseLine() {
      this.replotNeeded = true
      try {
        console.time('updateBaseLine')
        const res = updateBaseLine(JSON.stringify(this.baseCtrls_Copy))
        console.timeEnd('updateBaseLine')
        const parsed = JSON.parse(res)
        const { baseline } = parsed
        const baseLineEditSeries = findSeriesByName(this.option.series, 'BaseLine_Edit')
        baseLineEditSeries.data = baseline.map((val, index) => [index + 1, val])

        this.baseCtrls_Copy.baseline = baseline
      } catch (error) {
        console.error(error)
      }
    },

    // 重绘Peaks
    redrawPeaks(peakList) {
      this.option.series = this.option.series.filter((item) => {
        return !item.name.includes('Peak_')
      })
      this.option.series.push(...this.buildPeaks(peakList))
    },

    /**
     * 设置小方块可拖拽
     */
    setGraphicDraggable(draggable) {
      this.isModifying = draggable
    },

    // 在当前选中的红线位置新增控制点
    handleAddCP() {
      this.setGraphicDraggable(false)
      const { rg_high, rg_low } = this.BaseCtrls
      if (!this.currChannel || this.currChannel < rg_low || this.currChannel > rg_high) {
        this.$message.warn("Can't insert Control Point out of range")
        return
      }

      const { xctrl, yctrl, baseline, yslope } = this.baseCtrls_Copy

      let i = 0 // 记录新控制点在列表中的位置
      for (; i < xctrl.length; ++i) {
        const currCP = xctrl[i]
        if (currCP >= this.currChannel) {
          if (currCP == this.currChannel) {
            this.$message.warn(`The new control point in channel ${this.currChannel} exists, can't introduce twice`)
            return
          }
          break
        }
      }

      // 新增的控制点跟基线的值平齐
      const yAxis = baseline[this.currChannel - 1]
      xctrl.splice(i, 0, this.currChannel)
      yctrl.splice(i, 0, yAxis)
      yslope.splice(i, 0, 0)
      this.buildRect()
      this.pushOperationStack(Operators.ADD, { index: i })
    },

    // 移除控制点
    handleRemoveCP() {
      this.setGraphicDraggable(false)

      const { xctrl, yctrl, yslope } = this.baseCtrls_Copy

      // find nearest control-point
      let i = 1
      for (; i < xctrl.length; ++i) {
        const currXAxis = xctrl[i]
        if (currXAxis >= this.currChannel) {
          const prevX = xctrl[i - 1]
          if (currXAxis - this.currChannel > this.currChannel - prevX) --i
          break
        }
      }

      if (i == 0 || i >= xctrl.length - 1) {
        this.$message.warn("Can't remove first/last control point")
        return
      }

      const [removeXAxis] = xctrl.splice(i, 1)
      const [removeYAxis] = yctrl.splice(i, 1)
      const [removeYSlope] = yslope.splice(i, 1)
      this.buildRect()
      this.redrawBaseLine()
      this.pushOperationStack(Operators.REMOVE, {
        index: i,
        removeXAxis,
        removeYAxis,
        removeYSlope,
      })
    },

    // 修改控制点
    handleModifyCP() {
      this.setGraphicDraggable(!this.isModifying)
    },

    // 编辑斜率
    handleEditSlope() {
      this.setGraphicDraggable(false)

      const { xctrl, yslope } = this.baseCtrls_Copy
      if (!xctrl.length) {
        this.$message.warn('No control points to be edited')
        return
      }
      // find nearest control-point
      let i = 1,
        n = xctrl.length
      for (; i < n; ++i) {
        const currXCtrl = xctrl[i]
        const prevXCtrl = xctrl[i - 1]
        if (currXCtrl >= this.currChannel) {
          if (currXCtrl - this.currChannel > this.currChannel - prevXCtrl) {
            --i
          }
          break
        }
      }
      if (i == n) i = n - 1

      this.$refs.editSlopeModal.open({
        index: i,
        value: yslope[i],
        allowNaN: !(i == 0 || i == n - 1),
      })
    },

    // 确认编辑斜率
    handleSlopeChange(slope, index, prevSlope) {
      if (slope === prevSlope) {
        return
      }

      const { yslope } = this.baseCtrls_Copy
      yslope[index] = slope
      this.pushOperationStack(Operators.SLOPE_CHANGE, {
        index,
        slope: prevSlope,
      })
      this.redrawBaseLine()
      this.buildRect()
    },

    // 撤销
    handleUndo() {
      this.setGraphicDraggable(false)
      this.popOperationStack()
    },

    // 将原先的基线和控制点移动到新位置
    async handleReplot() {
      if (!this.replotNeeded) {
        return
      }
      try {
        const { inputFileName: fileName } = this.sampleData
        this.isReploting = true
        const { success, result, message } = await postAction('/gamma/replotBaseLine', {
          ...this.baseCtrls_Copy,
          fileName,
          replotNeeded: this.replotNeeded,
        })
        if (success) {
          const { chartData, peakSet, shapeData } = result

          const { xctrl, yctrl, yslope, baseline } = this.baseCtrls_Copy
          const baseLineSeries = findSeriesByName(this.option.series, 'BaseLine')
          baseLineSeries.data = baseline.map((val, index) => [index + 1, val])

          const baseLineCP = findSeriesByName(this.option.series, 'BaseLine_Ctrl_Point')
          // 第一个控制点（因为第一个和最后一个不会被删除）
          const firstCP = this.channelBaseCPChart[0]
          const { color, size } = firstCP
          const baseCPPoints = xctrl.map((xAxis, index) => {
            return {
              size,
              color,
              point: {
                x: xAxis,
                y: yctrl[index],
              },
            }
          })
          baseLineCP.data = this.buildCPPointData(baseCPPoints)

          this.opts.notMerge = true
          this.redrawPeaks(peakSet)
          this.$nextTick(() => {
            this.resetChartOpts()
          })

          this.replotNeeded = false
        } else {
          this.$message.error(message)
        }
      } catch (error) {
        console.error(error)
      } finally {
        this.isReploting = false
      }
    },

    /**
     * 构建基线控制点数据
     * @param {Array} controlPointList
     **/
    buildCPPointData(controlPointList) {
      return controlPointList.map(({ size, color, point: { x, y } }) => {
        return {
          value: [x, y],
          itemStyle: {
            color: 'transparent',
            borderColor: color,
            borderWidth: size / 2,
          },
        }
      })
    },

    // 确定对Baseline Control Points 的操作
    async handleAccept() {
      // this.BaseCtrls = cloneDeep(this.baseCtrls_Copy)
      // const { baseline, xctrl, yctrl } = this.BaseCtrls
      // this.channelBaseLineChart.pointlist = baseline.map((val, index) => {
      //   return {
      //     x: index + 1,
      //     y: val
      //   }
      // })

      // this.channelBaseCPChart = xctrl.map((val, index) => {
      //   return {
      //     color: this.channelBaseCPChart[0].color,
      //     name: index.toString(),
      //     point: {
      //       x: val,
      //       y: yctrl[index]
      //     },
      //     size: 4
      //   }
      // })

      const { inputFileName: fileName } = this.sampleData

      try {
        this.isAccepting = true
        const { success, result, message } = await postAction('/gamma/acceptBaseLine', {
          ...this.baseCtrls_Copy,
          fileName,
        })
        if (success) {
          this.BaseCtrls = cloneDeep(this.baseCtrls_Copy)

          const {
            allData,
            barChart,
            channelBaseLineChart,
            peakSet,
            shadowChannelChart,
            shadowEnergyChart,
            shapeChannelData,
            shapeData,
            shapeEnergyData,
          } = result

          this.channelBaseLineChart = channelBaseLineChart
          this.channelPeakChart = peakSet
          this.shadowChannelChart = shadowChannelChart
          this.channelBaseCPChart = shapeChannelData
          this.barChart = barChart

          this.btnGroupType = 1
          this.opts.notMerge = true
          this.clearRect()

          this.setChartOption(channelBaseLineChart, this.channelCountChart, peakSet, this.channelBaseCPChart, barChart)
          this.$nextTick(() => {
            this.resetChartOpts()
          })

          this.$bus.$emit('accept', { ...result, BaseCtrls: cloneDeep(this.baseCtrls_Copy) })
        } else {
          this.$message.error(message)
        }
      } catch (error) {
        console.error(error)
      } finally {
        this.isAccepting = false
      }
    },

    // 右下角添加当前选中的nuclide
    async handleAddNuclide() {
      const nuclides = this.selectedTableItem.nuclides
      const possibleNuclide = this.model.possibleNuclide
      if (!nuclides.includes(possibleNuclide)) {
        if (this.selectedTableItem._adding) {
          return
        }
        try {
          this.$set(this.selectedTableItem, '_adding', true)
          const { inputFileName: fileName } = this.sampleData
          const { success, message } = await postAction('/gamma/addNuclide', {
            curRow: this.curRow,
            nuclideName: possibleNuclide,
            fileName,
            list_identify: nuclides,
            energyTolerance: this.model.tolerance
          })
          if (success) {
            nuclides.push(possibleNuclide)
          } else {
            this.$message.error(message)
          }
        } catch (error) {
          console.error(error)
        } finally {
          this.selectedTableItem._adding = false
        }
      }
    },
    // 右下角删除当前选中的nuclide
    async handleDelNuclide() {
      if (!this.model.identifiedNuclide) {
        return
      }
      const nuclides = this.selectedTableItem.nuclides
      if (this.selectedTableItem._deleting) {
        return
      }
      const findIndex = nuclides.findIndex((nuclide) => nuclide == this.model.identifiedNuclide)
      if (-1 !== findIndex) {
        try {
          this.$set(this.selectedTableItem, '_deleting', true)
          const { inputFileName: fileName } = this.sampleData
          const { success, result, message } = await postAction('/gamma/deleteNuclide', {
            curRow: this.curRow,
            nuclideName: this.model.identifiedNuclide,
            fileName,
            list_identify: nuclides,
          })
          if (success) {
            const { identify, table } = result
            this.selectedTableItem.nuclides = identify
            this.list = table
          } else {
            this.$message.error(message)
          }
        } catch (error) {
          console.error(error)
        } finally {
          this.selectedTableItem._deleting = false
        }
      }
    },

    // 构建baseline
    buildBaseLine(channelBaseLineChart) {
      return buildLineSeries(
        'BaseLine',
        transformPointListData(channelBaseLineChart.pointlist),
        channelBaseLineChart.color,
        {
          markLine: {
            silent: true,
            symbol: 'none',
            label: {
              show: false,
            },
            lineStyle: {
              color: 'red',
              width: 1,
            },
            data: [{ xAxis: -1 }],
          },
          zlevel: 10,
        }
      )
    },

    // 构建count
    buildCountLine(channelCountChart) {
      return buildLineSeries('CountChart', transformPointListData(channelCountChart.pointlist), channelCountChart.color)
    },

    // 构建Peaks
    buildPeaks(channelPeakChart) {
      return channelPeakChart.map((item, index) => {
        return buildLineSeries('Peak_' + (index + 1), transformPointListData(item.pointlist), item.color)
      })
    },

    // 构建基线控制点
    buildCtrlPoint(channelBaseCPChart) {
      return {
        name: 'BaseLine_Ctrl_Point',
        type: 'scatter',
        data: this.buildCPPointData(channelBaseCPChart),
        silent: true,
        animation: false,
        zlevel: 20,
      }
    },

    // 构建缩略图
    buildBarChart(barChart) {
      return {
        name: 'BarChart',
        type: 'bar',
        data: barChart.map(({ x, y }) => [x, y]),
        itemStyle: {
          color: '#fff',
        },
        lineStyle: {
          width: 1,
        },
        symbol: 'none',
        symbolSize: 1,
        emphasis: {
          disabled: true,
        },
        animation: false,
        silent: true,
      }
    },

    /**
     * 推入操作
     * @param {*} operator 操作符
     * @param {*} operand 操作数
     */
    pushOperationStack(operator, operand) {
      this.operationStack.push({
        operator,
        operand,
      })
    },

    /**
     * 弹出操作
     */
    popOperationStack() {
      const { operator, operand } = this.operationStack.pop()
      const { index } = operand

      const { xctrl, yctrl, yslope } = this.baseCtrls_Copy
      switch (operator) {
        case Operators.ADD:
          xctrl.splice(index, 1)
          yctrl.splice(index, 1)
          yslope.splice(index, 1)
          break
        case Operators.MODIFY:
          const { prevYAxis } = operand

          // 恢复点的y轴位置
          yctrl[index] = prevYAxis
          break
        case Operators.REMOVE:
          const { removeXAxis, removeYAxis, removeYSlope } = operand
          xctrl.splice(index, 0, removeXAxis)
          yctrl.splice(index, 0, removeYAxis)
          yslope.splice(index, 0, removeYSlope)
          break
        case Operators.SLOPE_CHANGE:
          const { slope } = operand
          yslope[index] = slope
          break
      }
      this.buildRect()
      // 恢复基线位置
      this.redrawBaseLine()
    },

    /**
     * 清理操作栈
     */
    clearOperationStack() {
      this.operationStack = []
    },

    handleResize() {
      this.$refs.chartRef.resize()
      this.$refs.thumbnailRef.resize()
    },

    handleFullScreenChange(isFullScreen) {
      this.columns[7].width = isFullScreen ? 180 : 120
    },
  },
  computed: {
    curRow() {
      const [selectedKey] = this.selectedKeys
      const findIndex = this.list.findIndex((item) => item.index == selectedKey)
      return findIndex
    },

    isOperationStackEmpty() {
      return this.operationStack.length == 0
    },
  },
}
</script>

<style lang="less" scoped>
.interactive-analysis-tools {
  display: flex;
  padding-top: 5px;
  gap: 20px;

  &-left {
    flex: 1;
    overflow: hidden;

    .chart {
      height: 331px;
      position: relative;
    }

    .thumbnail {
      height: 50px;
      margin: 10px 30px 35px 80px;
      background-color: #255369;
    }

    .table {
      .title {
        color: #0cebc9;
        font-size: 20px;
        text-align: center;
        margin-bottom: 10px;
        user-select: none;

        span {
          cursor: pointer;
          margin: 0 5px;
        }
      }

      .custom-table {
        &.has-data {
          ::v-deep {
            .ant-table-body {
              height: 288px;
              background-color: #06282a;
            }
          }
        }

        ::v-deep {
          .ant-table-placeholder {
            height: 289px;
            display: flex;
            justify-content: center;
            align-items: center;
          }
        }
      }

      .operators {
        display: flex;
        margin-top: 10px;
        gap: 10px;

        .ant-btn {
          flex: 1;
        }
      }
    }
  }

  &-right {
    width: 290px;
    flex-shrink: 0;

    .peak-box {
      height: 326px;

      &-item:not(:last-child) {
        margin-bottom: 10px;
      }

      .symbol {
        display: flex;

        .ant-btn {
          flex: 1;

          &:first-child {
            margin-right: 10px;
          }
        }
      }
    }

    .base-line {
      margin-top: 136px;
    }

    .reset-btn-box {
      margin-top: 20px;
      margin-bottom: 30px;
    }

    .identify-box {
      .tolerance {
        ::v-deep {
          .ant-form-item {
            margin-bottom: 10px;

            &-control-wrapper {
              flex: 1;
            }

            &-control {
              width: 100%;
            }
          }
        }

        .ant-input-number {
          width: 100%;
        }
      }

      .identify-item {
        margin-bottom: 10px;

        .title {
          background-color: #497e9d;
          height: 30px;
          line-height: 30px;
          text-align: center;
          font-size: 16px;
        }

        .content {
          height: 80px;
          background-color: #275466;
          margin-top: 10px;
          padding: 5px;
          overflow: auto;

          .item {
            cursor: pointer;
            line-height: 26px;
            padding: 0 5px;

            &.active {
              background: #296d81;
            }
          }
        }
      }

      .identify-operators {
        display: flex;
        gap: 10px;
        .text {
          flex: 1;
          line-height: 32px;
          background-color: #285366;
          padding: 0 10px;
        }

        .ant-btn {
          width: 50px;
          padding-left: 5px;
          padding-right: 5px;
        }
      }
    }
  }

  .ant-btn {
    width: 100%;
  }
}
::v-deep {
  .ant-modal {
    top: 5px;
    padding-bottom: 7px;
  }
}
.is-modify,
.is-fitting {
  color: #f00;
}
</style>
<style lang="less">
.interactive-analysis-tools-dialog.fullscreen {
  .interactive-analysis-tools {
    height: calc(100vh - 90px);

    &-left {
      display: flex;
      flex-direction: column;

      .chart {
        flex: 1;
      }

      .thumbnail {
        height: 60px;
      }

      .table {
        height: 397px;

        .custom-table {
          height: calc(100% - 82px);

          .ant-spin-nested-loading,
          .ant-spin-container,
          .ant-table,
          .ant-table-content,
          .ant-table-scroll {
            height: 100%;
          }

          .ant-table-body {
            height: calc(100% - 25px) !important;
            max-height: calc(100% - 25px) !important;
          }
        }
      }
    }

    &-right {
      display: flex;
      flex-direction: column;

      .identify-box {
        flex: 1;
        padding-top: 5px;
        overflow: hidden;

        .title-over-border {
          height: 100%;

          &-content {
            display: flex;
            flex-direction: column;

            .identify-item {
              flex: 1;
              overflow: auto;

              .ant-spin-nested-loading,
              .content {
                height: calc(100% - 40px);
                .ant-spin-container,
                .content {
                  height: 100%;
                }
              }
            }
          }
        }
      }
    }
  }
}
</style>
