<template>
  <div>
  <div class="actual-container">
    <!-- 顶部实绩列表 (PLTCM_PDO_EXCOIL) -->
    <div class="top-section">
      <el-table
        ref="excoilTable"
        :data="excoilRows"
        size="mini"
        highlight-current-row
        border
        :height="topTableHeight"
        style="width:100%"
        @row-click="handleRowClick"
      >
        <el-table-column label="子卷号" min-width="110" show-overflow-tooltip>
          <template slot-scope="{ row }">{{ row.EXCOILID || row.excoilid }}</template>
        </el-table-column>
        <el-table-column label="热卷号" min-width="100" show-overflow-tooltip>
          <template slot-scope="{ row }">{{ row.HOT_COILID || row.hot_coilid || '—' }}</template>
        </el-table-column>
        <el-table-column label="班" width="40" align="center">
          <template slot-scope="{ row }">{{ row.SHIFT || row.shift || '—' }}</template>
        </el-table-column>
        <el-table-column label="组" width="40" align="center">
          <template slot-scope="{ row }">{{ row.CREW || row.crew || '—' }}</template>
        </el-table-column>
        <el-table-column label="钢种" min-width="80" show-overflow-tooltip>
          <template slot-scope="{ row }">{{ row.ORDER_QUALITY || row.order_quality || row.GRADE || row.grade || '—' }}</template>
        </el-table-column>
        <el-table-column label="来料厚度" width="68" align="right">
          <template slot-scope="{ row }">{{ row.ENTRY_THICK || row.entry_thick || '—' }}</template>
        </el-table-column>
        <el-table-column label="出口厚度" width="68" align="right">
          <template slot-scope="{ row }">{{ row.EXIT_THICK || row.exit_thick || '—' }}</template>
        </el-table-column>
        <el-table-column label="偏差上限" width="60" align="right">
          <template slot-scope="{ row }">{{ row.EXIT_POS_DEV || row.exit_pos_dev || '0' }}</template>
        </el-table-column>
        <el-table-column label="偏差下限" width="60" align="right">
          <template slot-scope="{ row }">{{ row.EXIT_NEG_DEV || row.exit_neg_dev || '0' }}</template>
        </el-table-column>
        <el-table-column label="来料宽度" width="60" align="right">
          <template slot-scope="{ row }">{{ row.ENTRY_WIDTH || row.entry_width || '—' }}</template>
        </el-table-column>
        <el-table-column label="出口宽度" width="60" align="right">
          <template slot-scope="{ row }">{{ row.EXIT_WIDTH || row.exit_width || '—' }}</template>
        </el-table-column>
        <el-table-column label="来料重量" width="60" align="right">
          <template slot-scope="{ row }">{{ row.USED_ENTRY_WEIGHT || row.used_entry_weight || row.ENTRY_WEIGHT || row.entry_weight || '—' }}</template>
        </el-table-column>
        <el-table-column label="称重重量" width="60" align="right">
          <template slot-scope="{ row }">{{ row.MEAS_EXIT_WEIGHT || row.meas_exit_weight || '—' }}</template>
        </el-table-column>
        <el-table-column label="包装要求" width="60">
          <template slot-scope="{ row }">{{ row.PARK_TYPE || row.park_type || '—' }}</template>
        </el-table-column>
        <el-table-column label="切边要求" width="60">
          <template slot-scope="{ row }">{{ row.SIDE_TRIM || row.side_trim || '—' }}</template>
        </el-table-column>
        <el-table-column label="成品质量" width="58" align="right">
          <template slot-scope="{ row }">{{ row.QUALITY || row.quality || '—' }}</template>
        </el-table-column>
        <el-table-column label="成品长度" width="60" align="right">
          <template slot-scope="{ row }">{{ row.EXIT_LENGTH || row.exit_length || '—' }}</template>
        </el-table-column>
        <el-table-column label="吨钢长度" width="60" align="right">
          <template slot-scope="{ row }">{{ calcLengthPerTon(row) }}</template>
        </el-table-column>
        <el-table-column label="下线时间" width="140">
          <template slot-scope="{ row }">{{ formatDate(row.END_DATE || row.end_date) }}</template>
        </el-table-column>
        <el-table-column label="状态" width="68" align="center">
          <template slot-scope="{ row }">
            <el-tag type="primary" size="mini" effect="plain">{{ row.STATUS || row.status || '产出' }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="" width="64" align="center" fixed="right">
          <template slot-scope="{ row }">
            <el-button type="text" size="mini" style="color:#2c5282;font-weight:600" @click.stop="openQualityReport(row)">质保书</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div class="table-pagination">
        <el-pagination
          small layout="total, prev, pager, next"
          :total="pagination.total"
          :page-size="pagination.pageSize"
          :current-page="pagination.page"
          @current-change="handlePageChange"
        />
      </div>
    </div>

    <!-- 底部图表区域 -->
    <div class="bottom-section">
      <div class="chart-section">
        <el-tabs v-model="activeTab" size="small" class="chart-tabs" @tab-click="handleTabSwitch">

          <!-- ① 趋势参数：左树形目录 + 右单图 -->
          <el-tab-pane label="趋势参数" name="trend">
            <div v-if="!selectedRow" class="no-data-hint">请在上方选择钢卷</div>
            <div v-else-if="segLoading" class="no-data-hint">加载中…</div>
            <div v-else-if="!segData" class="no-data-hint">暂无 SEG 数据</div>
            <div v-else class="trend-layout">
              <!-- 左侧目录树 -->
              <div class="trend-tree">
                <div v-for="group in trendGroups" :key="group.label" class="tree-group">
                  <div class="tree-group-label" @click="toggleGroup(group.label)">
                    <i :class="expandedGroups[group.label] ? 'el-icon-caret-bottom' : 'el-icon-caret-right'" />
                    {{ group.label }}
                  </div>
                  <div v-show="expandedGroups[group.label]" class="tree-children">
                    <div
                      v-for="item in group.children"
                      :key="item.col"
                      class="tree-item"
                      :class="{ active: selectedTrendParam && selectedTrendParam.col === item.col }"
                      @click="selectTrendParam(item)"
                    >{{ item.label }}</div>
                  </div>
                </div>
              </div>
              <!-- 右侧图表 -->
              <div class="trend-chart-area">
                <div v-if="!selectedTrendParam" class="no-data-hint">← 点击左侧参数查看曲线</div>
                <!-- 保持 DOM 存在，仅用 v-show 控制显示，避免 ref 失效 -->
                <div ref="trendSingleChart" :style="{ display: selectedTrendParam ? 'block' : 'none', height: '100%', width: '100%' }" />
              </div>
            </div>
          </el-tab-pane>

          <!-- ② 厚度曲线 -->
          <el-tab-pane label="厚度曲线" name="thickness">
            <div v-if="!selectedRow" class="no-data-hint">请在上方选择钢卷</div>
            <div v-else-if="realtimeLoading" class="no-data-hint">加载中…</div>
            <div v-else-if="!gaugeRows || !gaugeRows.length" class="no-data-hint">暂无厚度数据</div>
            <div v-else class="charts-scroll charts-grid">
              <div ref="chartGauge1" class="chart-box" />
              <div ref="chartGauge2" class="chart-box" />
              <div ref="chartGauge3" class="chart-box" />
              <div ref="chartGauge4" class="chart-box" />
            </div>
          </el-tab-pane>

          <!-- ③ 带钢板形 -->
          <el-tab-pane label="带钢板形" name="flatness3d">
            <div v-if="!selectedRow" class="no-data-hint">请在上方选择钢卷</div>
            <div v-else-if="realtimeLoading" class="no-data-hint">加载中…</div>
            <div v-else-if="!shapeRows || !shapeRows.length" class="no-data-hint">暂无板形数据</div>
            <div v-else class="charts-scroll">
              <div ref="chartFlatness3d" class="chart-box chart-box-tall" />
            </div>
          </el-tab-pane>

          <!-- ④ 板形曲线：一行两图 -->
          <el-tab-pane label="板形曲线" name="flatness">
            <div v-if="!selectedRow" class="no-data-hint">请在上方选择钢卷</div>
            <div v-else-if="realtimeLoading" class="no-data-hint">加载中…</div>
            <div v-else-if="!shapeRows || !shapeRows.length" class="no-data-hint">暂无板形数据</div>
            <div v-else class="charts-scroll charts-grid">
              <div ref="chartFlatDev"  class="chart-box" />
              <div ref="chartTilt"     class="chart-box" />
              <div ref="chartWrBend"   class="chart-box" />
              <div ref="chartIrBend"   class="chart-box" />
            </div>
          </el-tab-pane>
        </el-tabs>
      </div>

      <!-- 右侧查找面板 + 质保书入口 -->
      <div class="search-panel">
        <div class="panel-title">查找</div>
        <div class="search-type-group">
          <el-radio v-model="searchType" label="coil">按钢卷号</el-radio>
          <div v-if="searchType === 'coil'" class="search-field">
            <span class="search-label">钢卷号：</span>
            <el-input v-model="searchCoilId" size="mini" style="width:140px" placeholder="EXCOILID" />
          </div>
        </div>
        <div class="search-type-group">
          <el-radio v-model="searchType" label="time">按时间</el-radio>
          <template v-if="searchType === 'time'">
            <div class="search-field">
              <span class="search-label">开始时间：</span>
              <el-date-picker v-model="searchStartDate" type="datetime" size="mini" style="width:160px"
                value-format="yyyy-MM-dd HH:mm:ss" placeholder="开始时间" />
            </div>
            <div class="search-field">
              <span class="search-label">结束时间：</span>
              <el-date-picker v-model="searchEndDate" type="datetime" size="mini" style="width:160px"
                value-format="yyyy-MM-dd HH:mm:ss" placeholder="结束时间" />
            </div>
          </template>
        </div>
        <div class="search-actions">
          <el-button type="primary" size="mini" :loading="excoilLoading" @click="handleFindSearch">查找</el-button>
          <el-button size="mini" @click="handleFindReset">重置</el-button>
        </div>

      </div>
    </div>
  </div>

  <quality-report-dialog ref="qualityReport" />


  </div>
</template>

<script>
import * as echarts from 'echarts'
import 'echarts-gl'
import QualityReportDialog from './QualityReportDialog.vue'
import {
  getExcoilList,
  getExcoilCount,
  getTimingSegByEncoilId,
  getTimingRealtimeData,
  getPresetSetupByCoilId
} from '@/api/l2/timing'

// 趋势参数树结构，对应 PLTCM_PRO_SEG 列名
const TREND_GROUPS = [
  {
    label: '张力',
    children: [
      { label: '开卷张力',     col: 'PORTENS' },
      { label: '入口活套张力', col: 'ENLTENS' },
      { label: '拉矫张力',     col: 'TLTENS' },
      { label: '酸洗张力',     col: 'PLTENS' },
      { label: '出口活套张力', col: 'CXLTENS' },
      { label: '圆盘剪张力',   col: 'TRIMTENS' }
    ]
  },
  {
    label: '速度',
    children: [
      { label: '开卷速度',     col: 'PORSPEED' },
      { label: '酸洗速度',     col: 'PLSPEED' },
      { label: '圆盘剪速度',   col: 'TRIMSPEED' },
      { label: '轧机入口速度', col: 'MILLENTRYSPEED' },
      { label: '轧机出口速度', col: 'MILLEXITSPEED' }
    ]
  },
  {
    label: '拉矫机',
    children: [
      { label: '1#插入量', col: 'TLMESH1' },
      { label: '2#插入量', col: 'TLMESH2' },
      { label: '3#插入量', col: 'TLMESH3' },
      { label: '延伸率',   col: 'TLELONG' }
    ]
  },
  {
    label: '酸洗段',
    children: [
      { label: '1#温度',   col: 'TK1TEMP' },
      { label: '2#温度',   col: 'TK2TEMP' },
      { label: '3#温度',   col: 'TK3TEMP' },
      { label: '漂洗温度', col: 'RINSETEMP' }
    ]
  }
]

// V_VBDA_GAUGE 厚度曲线：4 个图，列名来自 DDL
// 厚度图纵轴显示实测/设定值百分比，速度图保持原单位
const GAUGE_COLS = [
  { col: 'THICK0',     title: '入口测厚仪 [%]' },
  { col: 'THICK1',     title: '1架出口厚度 [%]' },
  { col: 'THICK4',     title: '末架出口厚度 [%]' },
  { col: 'EXIT_SPEED', title: '轧制速度 [m/min]' }
]

// V_VBDA_SHAPE 板形曲线：4 个图，列名来自 DDL
const SHAPE_SCALAR_COLS = [
  { col: 'ABSDEVIATION', title: '总板形偏差 [IU]' },
  { col: 'TILT',         title: '末架倾斜量 [mm]' },
  { col: 'WRBEND',       title: '工作辊弯辊力 [kN]' },
  { col: 'IRBEND',       title: '中间辊弯辊力 [kN]' }
]

// 参数单位映射
const TREND_UNIT_MAP = {
  PORTENS: 'N/mm²', ENLTENS: 'N/mm²', TLTENS: 'N/mm²', PLTENS: 'N/mm²',
  CXLTENS: 'N/mm²', TRIMTENS: 'N/mm²', TRTENS: 'N/mm²', TELTENS: 'N/mm²',
  PORSPEED: 'm/min', PLSPEED: 'm/min', TRIMSPEED: 'm/min',
  MILLENTRYSPEED: 'm/min', MILLEXITSPEED: 'm/min',
  TLMESH1: 'mm', TLMESH2: 'mm', TLMESH3: 'mm',
  TLELONG: '%',
  TK1TEMP: '℃', TK2TEMP: '℃', TK3TEMP: '℃', RINSETEMP: '℃'
}

// PLTCM_PRO_SEG 列 → PLTCM_PRESET_SETUP 设定值列
const TREND_PRESET_MAP = {
  PORTENS: 'POR_TEN',   ENLTENS: 'CEL_TEN',   TLTENS: 'TLV_TEN',
  PLTENS:  'CPL_TEN',   CXLTENS: 'CXL_TEN',   TRIMTENS: 'TRIM_TEN',
  TRTENS:  'TR_TEN',    TELTENS: 'TEL_TEN',
  TLMESH1: 'TLV_MESH_1', TLMESH2: 'TLV_MESH_2', TLMESH3: 'TLV_MESH_3',
  TLELONG: 'TLV_ELONG',  PLSPEED: 'CPL_MAX_SPEED'
}

function calcYRange(vals) {
  const nums = vals.filter(v => v != null && isFinite(Number(v))).map(Number)
  if (!nums.length) return {}
  const min = Math.min(...nums)
  const max = Math.max(...nums)
  if (min === max) {
    const base = Math.abs(min) || 1
    return { min: parseFloat((min - base * 0.2).toFixed(4)), max: parseFloat((max + base * 0.2).toFixed(4)) }
  }
  const pad = (max - min) * 0.15
  return { min: parseFloat((min - pad).toFixed(4)), max: parseFloat((max + pad).toFixed(4)) }
}

/**
 * 生成折线图 option。
 * extras: [{ name, data, color, dash }]  — 上下限或参考线
 * yAxisSuffix: 纵轴标签后缀，如 '%'
 */
function makeLine(title, xData, yData, extras = [], yAxisSuffix = '') {
  const allVals = [yData, ...extras.map(e => e.data)].flat()
  const range = calcYRange(allVals)
  const hasExtras = extras.length > 0
  const mainSeries = {
    name: title, type: 'line', smooth: false, symbol: 'none',
    lineStyle: { width: 1.5, color: '#409EFF' }, data: yData, z: 3
  }
  const extraSeries = extras.map(e => ({
    name: e.name,
    type: 'line', smooth: false, symbol: 'none',
    lineStyle: { width: 1, color: e.color || '#E6A23C', type: e.dash !== false ? 'dashed' : 'solid' },
    data: e.data, z: 2
  }))
  return {
    title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 4, left: 8 },
    legend: hasExtras
      ? { data: [title, ...extras.map(e => e.name)], top: 4, right: 4,
          textStyle: { fontSize: 9 }, itemWidth: 14, itemHeight: 8 }
      : { show: false },
    tooltip: {
      trigger: 'axis',
      formatter: yAxisSuffix
        ? params => params.map(p => `${p.marker}${p.seriesName}: ${p.value != null ? p.value + yAxisSuffix : '—'}`).join('<br/>')
        : undefined
    },
    grid: { top: hasExtras ? 44 : 36, bottom: 28, left: 8, right: 16, containLabel: true },
    xAxis: {
      type: 'category', data: xData,
      name: 'pos(m)', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 }
    },
    yAxis: {
      type: 'value', min: range.min, max: range.max,
      nameTextStyle: { fontSize: 10 },
      axisLabel: {
        fontSize: 10,
        formatter: yAxisSuffix ? val => val + yAxisSuffix : undefined
      }
    },
    dataZoom: [
      { type: 'inside', xAxisIndex: 0, zoomOnMouseWheel: true, moveOnMouseMove: true },
      { type: 'inside', yAxisIndex: 0, zoomOnMouseWheel: false, moveOnMouseWheel: true }
    ],
    series: [mainSeries, ...extraSeries]
  }
}

function getRowVal(row, col) {
  const v = row[col] !== undefined ? row[col] : row[col.toLowerCase()]
  return v == null ? null : Number(v)
}

function xLocData(rows) {
  return rows.map(r => {
    const v = r.XLOCATION !== undefined ? r.XLOCATION : r.xlocation
    return v == null ? '' : Number(v).toFixed(1)
  })
}

export default {
  name: 'ActualPerformance',
  components: { QualityReportDialog },
  data() {
    return {
      excoilLoading: false,
      segLoading: false,
      realtimeLoading: false,
      excoilRows: [],
      selectedRow: null,
      segData: null,
      gaugeRows: null,
      shapeRows: null,
      activeTab: 'trend',
      // 趋势参数树状态
      trendGroups: TREND_GROUPS,
      expandedGroups: { '张力': true, '速度': true, '拉矫机': true, '酸洗段': true },
      selectedTrendParam: null,
      trendChartInst: null,
      presetData: null,
      // 查找
      searchType: 'coil',
      searchCoilId: '',
      searchStartDate: '',
      searchEndDate: '',
      pagination: { page: 1, pageSize: 50, total: 0 },
      topTableHeight: 'calc(40vh - 80px)',
      chartInstances: [],
      resizeHandler: null,
    }
  },
  created() {
    this._clickRev = 0
    this.loadExcoilCount()
    this.loadExcoilList()
  },
  beforeDestroy() {
    this.disposeAllCharts()
  },
  methods: {
    async loadExcoilCount() {
      try {
        const res = await getExcoilCount()
        this.pagination.total = res?.data?.total ?? 0
      } catch (_) {}
    },
    async loadExcoilList() {
      this.excoilLoading = true
      try {
        const res = await getExcoilList(this.pagination.page, this.pagination.pageSize)
        this.excoilRows = res?.data?.rows || []
      } finally {
        this.excoilLoading = false
      }
    },
    handlePageChange(page) {
      this.pagination.page = page
      this.loadExcoilList()
    },
    async handleRowClick(row) {
      // 快速点击防重：每次点击递增版本号，旧的 sync 任务检测到版本号变更后自动放弃
      const clickRev = ++this._clickRev

      this.selectedRow = row
      this.segData = null
      this.gaugeRows = null
      this.shapeRows = null
      this.presetData = null
      this.selectedTrendParam = null
      this.disposeAllCharts()

      const encoilId = row.ENCOILID || row.encoilid
      const excoilId = row.EXCOILID || row.excoilid

      await Promise.all([
        encoilId ? this.loadSeg(encoilId) : Promise.resolve(),
        encoilId ? this.loadPreset(encoilId) : Promise.resolve(),
        excoilId ? this.loadRealtime(excoilId) : Promise.resolve()
      ])

      // 如果期间又点击了其他行则放弃后续操作
      if (this._clickRev !== clickRev) return

      await this.$nextTick()
      // 加载完成后自动选中第一个趋势参数
      if (this.activeTab === 'trend' && this.segData) {
        this.selectTrendParam(TREND_GROUPS[0].children[0])
      } else {
        this.renderCurrentTab()
      }
    },

    async loadSeg(encoilId) {
      this.segLoading = true
      try {
        const res = await getTimingSegByEncoilId(encoilId)
        const rows = res?.data?.rows || []
        this.segData = rows.length ? (res?.data?.series || null) : null
      } catch (_) {
      } finally {
        this.segLoading = false
      }
    },

    async loadRealtime(excoilId) {
      this.realtimeLoading = true
      try {
        const res = await getTimingRealtimeData(excoilId)
        const g = res?.data?.gauge?.result
        const s = res?.data?.shape?.result
        this.gaugeRows = Array.isArray(g) ? g : null
        this.shapeRows = Array.isArray(s) ? s : null
      } catch (_) {
      } finally {
        this.realtimeLoading = false
      }
    },

    async loadPreset(coilId) {
      try {
        const res = await getPresetSetupByCoilId(coilId)
        this.presetData = res?.data?.data || null
      } catch (_) {
        this.presetData = null
      }
    },

    // ── 趋势参数树 ──────────────────────────────
    toggleGroup(label) {
      this.$set(this.expandedGroups, label, !this.expandedGroups[label])
    },
    isGroupExpanded(label) {
      return !!this.expandedGroups[label]
    },
    selectTrendParam(item) {
      this.selectedTrendParam = item
      this.$nextTick(() => this.renderTrendSingleChart())
    },
    renderTrendSingleChart() {
      if (!this.selectedTrendParam || !this.segData) return
      const el = this.$refs.trendSingleChart
      if (!el) return
      // 复用已有实例，避免重复 init
      if (!this.trendChartInst || this.trendChartInst.isDisposed()) {
        this.trendChartInst = echarts.init(el)
        // 滚轮缩放支持
        const resizeFn = () => this.trendChartInst && !this.trendChartInst.isDisposed() && this.trendChartInst.resize()
        window.addEventListener('resize', resizeFn)
        this._trendResizeFn = resizeFn
      }
      const col = this.selectedTrendParam.col
      const x = this.segX()
      const yData = this.seg(col)
      // 实测最大值 / 最小值：来自 PLTCM_PRO_SEG 的段内统计 MAX / MIN
      const maxData = this.seg(col + 'MAX')
      const minData = this.seg(col + 'MIN')
      const extras = []
      if (maxData.some(v => v != null)) {
        extras.push({ name: '最大值', data: maxData, color: '#F56C6C', dash: true })
      }
      if (minData.some(v => v != null)) {
        extras.push({ name: '最小值', data: minData, color: '#67C23A', dash: true })
      }
      // 设定值：来自 PLTCM_PRESET_SETUP（若有映射）
      const presetCol = TREND_PRESET_MAP[col]
      if (presetCol && this.presetData) {
        const setVal = this.presetData[presetCol] !== undefined
          ? this.presetData[presetCol]
          : this.presetData[presetCol.toLowerCase()]
        if (setVal != null && Number(setVal) !== 0) {
          const sv = Number(Number(setVal).toFixed(3))
          extras.push({ name: '设定值', data: new Array(x.length).fill(sv), color: '#E6A23C', dash: true })
        }
      }
      this.trendChartInst.setOption(makeLine(this.selectedTrendParam.label, x, yData, extras), true)
    },

    // ── Tab 切换 ────────────────────────────────
    handleTabSwitch() {
      this.$nextTick(() => {
        if (this.activeTab === 'trend' && this.selectedTrendParam && this.segData) {
          this.renderTrendSingleChart()
        } else {
          this.renderCurrentTab()
        }
      })
    },
    renderCurrentTab() {
      this.disposeSideCharts()
      if (this.activeTab === 'thickness'  && this.gaugeRows?.length)  this.renderGaugeCharts()
      if (this.activeTab === 'flatness3d' && this.shapeRows?.length)  this.renderFlatness3d()
      if (this.activeTab === 'flatness'   && this.shapeRows?.length)  this.renderFlatnessCharts()
    },

    // ── 销毁 ────────────────────────────────────
    disposeSideCharts() {
      if (this.resizeHandler) {
        window.removeEventListener('resize', this.resizeHandler)
        this.resizeHandler = null
      }
      this.chartInstances.forEach(c => { if (c && !c.isDisposed()) c.dispose() })
      this.chartInstances = []
    },
    disposeAllCharts() {
      this.disposeSideCharts()
      if (this._trendResizeFn) {
        window.removeEventListener('resize', this._trendResizeFn)
        this._trendResizeFn = null
      }
      if (this.trendChartInst && !this.trendChartInst.isDisposed()) {
        this.trendChartInst.dispose()
        this.trendChartInst = null
      }
    },

    // ── SEG 数据辅助 ─────────────────────────────
    seg(col) {
      const s = this.segData
      // Oracle 返回大写列名，兼容小写
      const arr = s[col] !== undefined ? s[col] : (s[col.toLowerCase()] || [])
      return arr.map(v => v == null ? null : Number(Number(v).toFixed(3)))
    },
    segX() {
      const s = this.segData
      const arr = s['STARTPOS'] !== undefined ? s['STARTPOS'] : (s['startpos'] || [])
      return arr.map(v => v == null ? '' : Number(v).toFixed(1))
    },

    // ── 图表初始化 ───────────────────────────────
    makeChart(ref, option) {
      const el = this.$refs[ref]
      if (!el) return null
      const chart = echarts.init(el)
      chart.setOption(option)
      return chart
    },
    setupResize() {
      this.resizeHandler = () => this.chartInstances.forEach(c => {
        if (c && !c.isDisposed()) c.resize()
      })
      window.addEventListener('resize', this.resizeHandler)
    },

    // ── 厚度曲线 (V_VBDA_GAUGE) ──────────────────
    // 有参考值的厚度图纵轴显示百分比：实测值 / 设定值 × 100
    renderGaugeCharts() {
      const rows = this.gaugeRows
      if (!rows || !rows.length) return
      const xData = xLocData(rows)
      // THICK0~THICK4 对应的参考列
      const refColMap = { THICK0: 'THICK0REF', THICK1: 'THICK1REF', THICK4: 'THICK4REF', THICK5: 'THICK5REF' }
      const chartRefs = ['chartGauge1', 'chartGauge2', 'chartGauge3', 'chartGauge4']
      const charts = chartRefs.map((ref, i) => {
        const { col, title } = GAUGE_COLS[i]
        const refCol = refColMap[col]

        if (refCol) {
          // ── 厚度图：转为百分比（实测 / 设定 × 100） ──
          const yData = rows.map(r => {
            const v  = getRowVal(r, col)
            const rv = getRowVal(r, refCol)
            if (v == null || rv == null || rv === 0) return null
            return parseFloat(((v / rv) * 100).toFixed(3))
          })
          const extras = []
          // 目标值恒为 100%
          const refLine = rows.map(r => {
            const rv = getRowVal(r, refCol)
            return (rv != null && rv !== 0) ? 100 : null
          })
          // 上限百分比 = (REF + TOPLIMIT) / REF × 100
          const upData = rows.map(r => {
            const rv = getRowVal(r, refCol)
            const tl = getRowVal(r, 'TOPLIMIT')
            if (rv == null || rv === 0) return null
            return parseFloat(((rv + (tl ?? 3)) / rv * 100).toFixed(3))
          })
          // 下限百分比 = (REF + BOTLIMIT) / REF × 100
          const loData = rows.map(r => {
            const rv = getRowVal(r, refCol)
            const bl = getRowVal(r, 'BOTLIMIT')
            if (rv == null || rv === 0) return null
            return parseFloat(((rv + (bl ?? -3)) / rv * 100).toFixed(3))
          })
          if (refLine.some(v => v != null)) extras.push({ name: '目标值(100%)', data: refLine, color: '#909399', dash: false })
          if (upData.some(v => v != null))  extras.push({ name: '上限', data: upData, color: '#F56C6C', dash: true })
          if (loData.some(v => v != null))  extras.push({ name: '下限', data: loData, color: '#67C23A', dash: true })
          return this.makeChart(ref, makeLine(title, xData, yData, extras, '%'))
        } else {
          // ── 速度等无参考值的图：保持原始单位 ──
          const yData = rows.map(r => {
            const v = getRowVal(r, col)
            return v == null ? null : parseFloat(v.toFixed(4))
          })
          return this.makeChart(ref, makeLine(title, xData, yData))
        }
      })
      this.chartInstances = charts.filter(Boolean)
      this.setupResize()
    },

    // ── 带钢板形 3D 线图 (V_VBDA_SHAPE) ────────────
    // 每个通道画一条独立 line3D，通道之间不连面，形成镂空效果
    renderFlatness3d() {
      const rows = this.shapeRows
      if (!rows || !rows.length) return
      const firstRow = rows[0]
      const high = parseInt(getRowVal(firstRow, 'HIGHZONEID')) || 26
      const low  = parseInt(getRowVal(firstRow, 'LOWZONEID'))  || 1
      const numZones = Math.min(Math.max(high - low + 1, 1), 26)
      const zoneCols = Array.from({ length: numZones }, (_, i) =>
        `VALUES${String(low + i).padStart(2, '0')}`
      )
      // X 方向降采样，最多 200 个点
      const step = Math.max(1, Math.floor(rows.length / 200))
      const sampled = rows.filter((_, i) => i % step === 0)
      const numX = sampled.length

      // X 轴标签（位置，单位 m）
      const xLabels = sampled.map(r => {
        const v = r.XLOCATION !== undefined ? r.XLOCATION : r.xlocation
        return v == null ? '' : Number(v).toFixed(0)
      })

      // 收集值域用于 visualMap
      let minV = Infinity, maxV = -Infinity
      sampled.forEach(row => {
        zoneCols.forEach(col => {
          const v = getRowVal(row, col)
          if (v != null) {
            if (v < minV) minV = v
            if (v > maxV) maxV = v
          }
        })
      })
      if (!isFinite(minV)) { minV = -30; maxV = 30 }
      const absMax = Math.max(Math.abs(minV), Math.abs(maxV))

      // ① 沿 X 方向网格线（每通道一条，按 Z 值着色）
      const channelLines = zoneCols.map((col, yi) => ({
        type: 'line3D',
        coordinateSystem: 'cartesian3D',
        data: sampled.map((row, xi) => {
          const v = getRowVal(row, col)
          return v == null ? null : [xi, yi, parseFloat(v.toFixed(2))]
        }).filter(Boolean),
        lineStyle: { width: 2, opacity: 1 }
      }))

      // ② 沿 Y 方向网格线（每隔若干位置连通各通道，按 Z 值着色）
      const xStride = Math.max(1, Math.floor(numX / 60))
      const crossLines = []
      for (let xi = 0; xi < numX; xi += xStride) {
        const pts = zoneCols.map((col, yi) => {
          const v = getRowVal(sampled[xi], col)
          return v == null ? null : [xi, yi, parseFloat(v.toFixed(2))]
        }).filter(Boolean)
        if (pts.length > 1) {
          crossLines.push({
            type: 'line3D',
            coordinateSystem: 'cartesian3D',
            data: pts,
            lineStyle: { width: 1.5, opacity: 1 }
          })
        }
      }

      const series = [...channelLines, ...crossLines]

      const option = {
        title: { text: '实测平直度 [IU]', textStyle: { fontSize: 13, fontWeight: 'normal' }, top: 6, left: 10 },
        tooltip: {},
        visualMap: {
          show: true,
          dimension: 2,
          min: -absMax,
          max:  absMax,
          calculable: true,
          orient: 'vertical',
          right: 10,
          top: 'center',
          textStyle: { fontSize: 10 },
          inRange: {
            // 负值红色 → 零值绿色 → 正值蓝紫
            color: ['#8B0000','#CC2200','#E84C00','#F46D43',
                    '#FDAE61','#FEE08B',
                    '#66BD63','#1A9850','#006837',
                    '#3288BD','#5E4FA2','#762A83']
          }
        },
        grid3D: {
          boxWidth: 200,
          boxHeight: 60,
          boxDepth: 80,
          viewControl: {
            projection: 'orthographic',
            autoRotate: false,
            rotateSensitivity: 1,
            zoomSensitivity: 1
          },
          light: {
            main: { intensity: 1.2, shadow: false },
            ambient: { intensity: 0.3 }
          }
        },
        xAxis3D: {
          type: 'value',
          name: '位置',
          min: 0,
          max: numX - 1,
          nameTextStyle: { fontSize: 10 },
          axisLabel: {
            fontSize: 9,
            formatter: v => xLabels[Math.round(v)] || ''
          }
        },
        yAxis3D: {
          type: 'value',
          name: '通道',
          min: 0,
          max: numZones - 1,
          nameTextStyle: { fontSize: 10 },
          axisLabel: {
            fontSize: 9,
            formatter: v => String(low + Math.round(v))
          }
        },
        zAxis3D: {
          type: 'value',
          name: 'IU',
          nameTextStyle: { fontSize: 10 },
          axisLabel: { fontSize: 9 }
        },
        series
      }

      const el = this.$refs.chartFlatness3d
      if (!el) return
      const chart = echarts.init(el)
      chart.setOption(option)
      this.chartInstances = [chart]
      this.setupResize()
    },

    // ── 板形曲线 (V_VBDA_SHAPE) ──────────────────
    renderFlatnessCharts() {
      const rows = this.shapeRows
      if (!rows || !rows.length) return
      const xData = xLocData(rows)
      const refs = ['chartFlatDev', 'chartTilt', 'chartWrBend', 'chartIrBend']
      const charts = refs.map((ref, i) => {
        const { col, title } = SHAPE_SCALAR_COLS[i]
        const yData = rows.map(r => {
          const v = getRowVal(r, col)
          return v == null ? null : parseFloat(v.toFixed(3))
        })
        return this.makeChart(ref, makeLine(title, xData, yData))
      })
      this.chartInstances = charts.filter(Boolean)
      this.setupResize()
    },

    // ── 查找 ─────────────────────────────────────
    handleFindSearch() {
      if (this.searchType === 'coil' && this.searchCoilId) {
        const found = this.excoilRows.find(r =>
          (r.EXCOILID || r.excoilid || '').includes(this.searchCoilId)
        )
        if (found) {
          this.$refs.excoilTable && this.$refs.excoilTable.setCurrentRow(found)
          this.handleRowClick(found)
        } else {
          this.$message.info('当前页未找到该卷号，请翻页查找')
        }
      } else {
        this.pagination.page = 1
        this.loadExcoilList()
      }
    },
    handleFindReset() {
      this.searchCoilId = ''
      this.searchStartDate = ''
      this.searchEndDate = ''
      this.selectedRow = null
      this.segData = null
      this.gaugeRows = null
      this.shapeRows = null
      this.selectedTrendParam = null
      this.disposeAllCharts()
      this.pagination.page = 1
      this.loadExcoilCount()
      this.loadExcoilList()
    },

    openQualityReport(row) {
      // If the clicked row is already the selected row, use in-memory data
      const isSame = this.selectedRow &&
        (row.EXCOILID || row.excoilid) === (this.selectedRow.EXCOILID || this.selectedRow.excoilid)
      const segData   = isSame ? this.segData   : null
      const gaugeRows = isSame ? this.gaugeRows : null
      const shapeRows = isSame ? this.shapeRows : null
      const presetData = isSame ? this.presetData : null
      this.$refs.qualityReport.open(row, segData, gaugeRows, shapeRows, presetData)
    },


    calcLengthPerTon(row) {
      const len = parseFloat(row.EXIT_LENGTH || row.exit_length)
      const wt  = parseFloat(row.MEAS_EXIT_WEIGHT || row.meas_exit_weight)
      if (!len || !wt || wt === 0) return '—'
      return (len / wt).toFixed(2)
    },
    formatDate(v) {
      if (!v) return '—'
      return String(v).replace('T', ' ').substring(0, 19)
    },

  }
}
</script>

<style scoped lang="scss">
.actual-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 8px;
  background: #f0f2f5;
  gap: 6px;
  box-sizing: border-box;
}

.top-section {
  flex-shrink: 0;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
  overflow: hidden;
}

.table-pagination {
  display: flex;
  justify-content: flex-end;
  padding: 4px 8px;
  border-top: 1px solid #ebeef5;
  background: #fafafa;
}

.bottom-section {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 6px;
}

.chart-section {
  flex: 1;
  min-height: 0;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
  padding: 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;

  .chart-tabs {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;

    ::v-deep .el-tabs__content {
      flex: 1;
      overflow: hidden;
      min-height: 0;
      padding: 0;
    }
    ::v-deep .el-tab-pane {
      height: 100%;
    }
  }
}

/* ── 趋势参数：左树 + 右图 ── */
.trend-layout {
  display: flex;
  height: 100%;
  gap: 0;
}

.trend-tree {
  width: 140px;
  flex-shrink: 0;
  overflow-y: auto;
  border-right: 1px solid #ebeef5;
  padding: 4px 0;

  &::-webkit-scrollbar { width: 3px; }
  &::-webkit-scrollbar-thumb { background: #dcdfe6; border-radius: 2px; }
}

.tree-group {
  user-select: none;
}

.tree-group-label {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 8px;
  font-size: 12px;
  font-weight: 600;
  color: #303133;
  cursor: pointer;

  &:hover { background: #f5f7fa; }

  i { font-size: 10px; color: #909399; }
}

.tree-children { padding-left: 4px; }

.tree-item {
  padding: 4px 8px 4px 18px;
  font-size: 12px;
  color: #606266;
  cursor: pointer;
  border-radius: 3px;
  margin: 1px 4px;

  &:hover { background: #ecf5ff; color: #409eff; }

  &.active {
    background: #ecf5ff;
    color: #409eff;
    font-weight: 500;
  }
}

.trend-chart-area {
  flex: 1;
  min-width: 0;
  height: 100%;
  padding: 4px 4px 4px 8px;
  display: flex;
  align-items: stretch;
}

/* ── 其他图表 ── */
.charts-scroll {
  height: 100%;
  overflow-y: auto;
  padding: 4px;

  &::-webkit-scrollbar { width: 4px; }
  &::-webkit-scrollbar-thumb { background: #dcdfe6; border-radius: 2px; }
}

/* 一行两图 */
.charts-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  align-content: start;

  .chart-box { margin-bottom: 0; }
}

.chart-box {
  width: 100%;
  height: 200px;
  margin-bottom: 8px;
}

.chart-box-tall { height: 480px; }

/* ── 查找面板 ── */
.search-panel {
  width: 210px;
  flex-shrink: 0;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  overflow-y: auto;
}

.panel-title {
  font-size: 13px;
  font-weight: 600;
  color: #303133;
  padding-bottom: 6px;
  border-bottom: 1px solid #ebeef5;
}

.search-type-group { display: flex; flex-direction: column; gap: 8px; }

.search-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-left: 20px;
}

.search-label { font-size: 11px; color: #909399; }

.search-actions {
  display: flex;
  gap: 8px;
  margin-top: auto;
}

.no-data-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 120px;
  color: #c0c4cc;
  font-size: 13px;
}


</style>
