<template>
  <div class="aps-quick-sheet excel-theme">
    <div class="sheet-toolbar">
      <div class="sheet-actions">
        <el-button size="small" icon="el-icon-refresh" @click="loadRows">刷新</el-button>
        <el-button size="small" type="primary" icon="el-icon-finished" :loading="saving" @click="saveAll">保存</el-button>
        <el-button size="small" icon="el-icon-plus" @click="addRow">新增行</el-button>
        <el-button size="small" icon="el-icon-download" @click="exportCsv">导出</el-button>
        <el-date-picker
          v-model="filter.range"
          type="daterange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          value-format="yyyy-MM-dd"
          size="small"
          style="width: 240px"
          @change="onFilterChange"
        />
      </div>
      <div class="preset-bar">
        <span class="preset-title">预设方案：</span>
        <el-button
          v-for="line in lineOptions"
          :key="line.lineId"
          size="mini"
          plain
          @click="applyPreset(line)"
        >
          {{ line.lineName || line.lineCode || ('产线' + line.lineId) }}
        </el-button>
      </div>
    </div>

    <div class="sheet-body">
      <el-table
        v-loading="loading"
        :data="displayRows"
        border
        size="mini"
        class="excel-table"
      >
        <el-table-column label="序号" width="60" align="center">
          <template slot-scope="scope">
            {{ ((pager.pageNum - 1) * pager.pageSize) + scope.$index + 1 }}
          </template>
        </el-table-column>
        <el-table-column
          v-for="col in flatColumns"
          :key="col.prop || col.label"
          :label="col.label"
          :prop="col.prop"
          :width="col.width"
          :min-width="col.minWidth"
          :align="col.align || 'center'"
          :show-overflow-tooltip="col.showOverflowTooltip !== false"
        >
          <template slot-scope="scope">
            <template v-if="col.prop === 'rawMaterialId'">
              <el-button
                size="mini"
                type="text"
                class="coil-picker-btn"
                @click="openCoilPicker(scope.row)"
              >
                {{ scope.row.rawCoilNos || '选择原料钢卷' }}
              </el-button>
            </template>
            <template v-else-if="isTimeField(col.prop)">
              <el-date-picker
                v-model="scope.row[col.prop]"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                size="mini"
                style="width: 100%"
                @change="onCellChange(scope.row)"
                @input="onCellChange(scope.row)"
              />
            </template>
            <template v-else>
              <el-autocomplete
                v-if="col.prop === 'lineName'"
                v-model="scope.row[col.prop]"
                size="mini"
                clearable
                :fetch-suggestions="queryLineHistory"
                @select="onLineHistorySelect(scope.row)"
                @input="onLineHistoryInput(scope.row)"
              />
              <el-input
                v-else
                v-model="scope.row[col.prop]"
                size="mini"
                clearable
                @input="onCellChange(scope.row)"
              />
            </template>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="70" fixed="right" align="center">
          <template slot-scope="scope">
            <el-button type="text" size="mini" @click="clearRow(scope.row)">清空</el-button>
          </template>
        </el-table-column>
      </el-table>
     
      <div class="pager-wrap">
        <el-pagination
          small
          background
          layout="prev, pager, next, total"
          :current-page.sync="pager.pageNum"
          :page-size="pager.pageSize"
          :total="totalRows"
        />
      </div>
    </div>
    <div v-if="missingRows.length" class="missing-tip">
        <div v-for="msg in missingRows" :key="msg" class="missing-item">{{ msg }}</div>
      </div>
    <el-dialog title="选择原料钢卷" :visible.sync="coilPicker.visible" width="1280px" append-to-body>
      <el-form :inline="true" size="small" class="coil-picker-form">
        <el-form-item label="库区">
          <el-select v-model="coilPicker.selectedWarehouseId" clearable filterable placeholder="全部库区" style="width: 220px" @change="onCoilFilterChange">
            <el-option
              v-for="w in coilPicker.warehouseOptions"
              :key="w.actualWarehouseId"
              :label="w.actualWarehouseName"
              :value="w.actualWarehouseId"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="入场钢卷号">
          <el-input v-model="coilPicker.enterCoilNo" clearable placeholder="支持模糊" style="width: 170px" @keyup.enter.native="searchCoils" />
        </el-form-item>
        <el-form-item label="当前钢卷号">
          <el-input v-model="coilPicker.currentCoilNo" clearable placeholder="支持模糊" style="width: 170px" @keyup.enter.native="searchCoils" />
        </el-form-item>
        <el-form-item label="厂家">
          <el-input v-model="coilPicker.manufacturer" clearable placeholder="支持模糊" style="width: 170px" @keyup.enter.native="searchCoils" />
        </el-form-item>
        <el-form-item>
          <el-button size="mini" type="primary" @click="searchCoils">查询</el-button>
        </el-form-item>
      </el-form>

      <div class="coil-stack-wrap" v-loading="coilPicker.loading">
        <el-empty v-if="!coilColumnKeys.length" description="暂无可选原料钢卷" :image-size="60" />
        <div v-else class="coil-bird-wrap">
          <div class="coil-legend">
            <span class="legend-item"><i class="dot layer1" />一层</span>
            <span class="legend-item"><i class="dot layer2" />二层</span>
            <span class="legend-item"><i class="dot occupied" />已占用</span>
          </div>
          <div class="coil-grid-scroll">
            <div class="coil-col-ruler">
              <div class="ruler-empty" />
              <div v-for="col in coilColumnKeys" :key="`ruler-${col}`" class="ruler-col">{{ col }}</div>
            </div>
            <div class="coil-grid-main">
              <div class="coil-row-ruler">
                <div v-for="row in coilMaxRow" :key="`row-${row}`" class="ruler-row">{{ row }}</div>
              </div>
              <div class="coil-grid-columns">
                <div v-for="col in coilColumnKeys" :key="`col-${col}`" class="coil-col-pair">
                  <div class="coil-layer">
                    <div
                      v-for="row in coilMaxRow"
                      :key="`l1-${col}-${row}`"
                      class="coil-cell layer1"
                      :class="{ occupied: !!(getCoilCell(col, row, 1) && getCoilCell(col, row, 1).rawMaterialId) }"
                      @click="selectCoilCell(col, row, 1)"
                    >
                      <template v-if="getCoilCell(col, row, 1)">
                        <div class="line1" :title="getCoilCell(col, row, 1).rawLocation">{{ getCoilCell(col, row, 1).rawLocation }}</div>
                        <div class="line2" :title="getCoilCell(col, row, 1).rawMaterialCode">{{ getCoilCell(col, row, 1).rawMaterialCode || '-' }}</div>
                      </template>
                      <template v-else>-</template>
                    </div>
                  </div>
                  <div class="coil-layer layer2-shift">
                    <div
                      v-for="row in (coilMaxRow - 1)"
                      :key="`l2-${col}-${row}`"
                      class="coil-cell layer2"
                      :class="{ occupied: !!(getCoilCell(col, row, 2) && getCoilCell(col, row, 2).rawMaterialId) }"
                      @click="selectCoilCell(col, row, 2)"
                    >
                      <template v-if="getCoilCell(col, row, 2)">
                        <div class="line1" :title="getCoilCell(col, row, 2).rawLocation">{{ getCoilCell(col, row, 2).rawLocation }}</div>
                        <div class="line2" :title="getCoilCell(col, row, 2).rawMaterialCode">{{ getCoilCell(col, row, 2).rawMaterialCode || '-' }}</div>
                      </template>
                      <template v-else>-</template>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="coil-picker-foot">共 {{ coilPicker.total || 0 }} 条钢卷（按库位分布展示）</div>
    </el-dialog>
  </div>
</template>

<script>
import { fetchQuickSheetList, fetchQuickSheetPreset, saveQuickSheet, exportQuickSheet, deleteQuickSheetRow } from '@/api/aps/quickSheet'
import { listProductionLine } from '@/api/wms/productionLine'
import { getMaterialCoilLocationGrid } from '@/api/wms/coil'
import { treeActualWarehouseTwoLevel } from '@/api/wms/actualWarehouse'
import { getTemplateByKey } from './sheets/templates'
import { saveAs } from 'file-saver'

export default {
  name: 'ApsQuickSheet',
  data() {
    return {
      loading: false,
      saving: false,
      rows: [],
      lineOptions: [],
      lineHistory: [],
      dirtyIds: new Set(),
      autoSaveTimer: null,
      templateKey: 'unified',
      filter: {
        range: []
      },
      pager: {
        pageNum: 1,
        pageSize: 25
      },
      coilPicker: {
        visible: false,
        loading: false,
        currentRow: null,
        selectedWarehouseId: undefined,
        enterCoilNo: '',
        currentCoilNo: '',
        manufacturer: '',
        warehouseOptions: [],
        coilGrid: {},
        total: 0
      }
    }
  },
  computed: {
    currentTemplate() {
      return getTemplateByKey(this.templateKey)
    },
    flatColumns() {
      const res = []
      const loop = (cols) => {
        ;(cols || []).forEach(c => {
          if (c.children && c.children.length) loop(c.children)
          else res.push(c)
        })
      }
      loop(this.currentTemplate.columns || [])
      return res
    },
    displayRows() {
      const [startAfter, endBefore] = this.filter.range || []
      const filtered = this.rows.filter(r => {
        if (!r.startTime) return true
        const d = String(r.startTime).slice(0, 10)
        if (startAfter && d < startAfter) return false
        if (endBefore && d > endBefore) return false
        return true
      })
      const start = (this.pager.pageNum - 1) * this.pager.pageSize
      const page = filtered.slice(start, start + this.pager.pageSize)
      if (page.length < this.pager.pageSize) {
        return page.concat(this.buildEmptyRows(this.pager.pageSize - page.length))
      }
      return page
    },
    totalRows() {
      const [startAfter, endBefore] = this.filter.range || []
      return this.rows.filter(r => {
        if (!r.startTime) return true
        const d = String(r.startTime).slice(0, 10)
        if (startAfter && d < startAfter) return false
        if (endBefore && d > endBefore) return false
        return true
      }).length
    },
    missingRows() {
      return this.collectMissing(this.displayRows)
    },
    coilColumnKeys() {
      const keys = Object.keys(this.coilPicker.coilGrid || {})
      return keys.map(v => Number(v)).filter(v => !Number.isNaN(v)).sort((a, b) => a - b)
    },
    coilMaxRow() {
      let max = 0
      Object.values(this.coilPicker.coilGrid || {}).forEach(col => {
        const l1 = col.layer1 || []
        const l2 = col.layer2 || []
        ;[...l1, ...l2].forEach(item => {
          const row = Number(item._row || 0)
          if (row > max) max = row
        })
      })
      return max || 1
    }
  },
  created() {
    const today = new Date()
    const start = `${today.getFullYear()}-${`${today.getMonth() + 1}`.padStart(2, '0')}-${`${today.getDate()}`.padStart(2, '0')}`
    this.filter.range = [start, '']
    this.loadRows()
    this.loadLines()
  },
  beforeDestroy() {
    if (this.autoSaveTimer) clearTimeout(this.autoSaveTimer)
  },
  methods: {
    async loadLines() {
      const res = await listProductionLine({ pageNum: 1, pageSize: 1000 })
      this.lineOptions = res.rows || []
    },
    async loadRows() {
      this.loading = true
      try {
        const res = await fetchQuickSheetList()
        this.rows = (res.data || []).map(r => this.normalizeRow(r))
        this.lineHistory = this.rows
          .map(r => r.lineName)
          .filter(v => v && v.trim())
          .filter((v, i, arr) => arr.indexOf(v) === i)
      } finally {
        this.loading = false
      }
    },
    buildEmptyRows(count) {
      return Array.from({ length: count }).map(() => this.buildEmptyRow())
    },
    async applyPreset(line) {
      if (!line || !line.lineId) return
      const res = await fetchQuickSheetPreset({ lineId: line.lineId })
      const preset = res.data || []
      if (!preset.length) return
      const targetRow = this.rows.find(r => !r.lineId && !r.lineName) || this.buildEmptyRow()
      const base = this.normalizeRow(preset[0])
      Object.keys(base).forEach(k => {
        if (k === 'quickSheetId') return
        targetRow[k] = base[k]
      })
      targetRow.lineId = line.lineId
      targetRow.lineName = line.lineName || line.lineCode || ''
      if (!this.rows.includes(targetRow)) {
        this.rows.push(targetRow)
      }
      if (this.rows.length < 25) {
        this.rows = this.rows.concat(this.buildEmptyRows(25 - this.rows.length))
      }
      this.markDirty(targetRow)
      this.scheduleAutoSave()
    },
    async loadWarehouseOptions() {
      const res = await treeActualWarehouseTwoLevel()
      const tree = res.data || []
      const leaf = []
      tree.forEach(p => {
        ;(p.children || []).forEach(c => {
          leaf.push({
            actualWarehouseId: c.actualWarehouseId,
            actualWarehouseName: `${p.actualWarehouseName || ''}/${c.actualWarehouseName || ''}`
          })
        })
      })
      this.coilPicker.warehouseOptions = leaf
    },
    async loadCoilsByWarehouse() {
      if (!this.coilPicker.selectedWarehouseId) {
        this.coilPicker.coilGrid = {}
        this.coilPicker.total = 0
        return
      }
      this.coilPicker.loading = true
      try {
        const gridRes = await getMaterialCoilLocationGrid({
          itemType: 'raw_material',
          actualWarehouseId: this.coilPicker.selectedWarehouseId,
          enterCoilNo: this.coilPicker.enterCoilNo || undefined,
          currentCoilNo: this.coilPicker.currentCoilNo || undefined,
          manufacturer: this.coilPicker.manufacturer || undefined
        })
        const data = gridRes.data || {}
        const warehouses = data.warehouses || []
        const coils = (data.coils || []).map(item => ({
          rawMaterialId: item.coilId,
          rawMaterialCode: item.currentCoilNo || item.enterCoilNo || item.coilNo || '',
          rawMaterialName: item.itemName || item.materialName || '',
          specification: item.specification || '',
          coilWeight: item.netWeight,
          packageType: item.packagingRequirement || item.packageType,
          edgeType: item.trimmingRequirement || item.edgeType,
          zincLayer: item.coatingType || item.zincLayer,
          surfaceTreatmentDesc: item.surfaceTreatmentDesc,
          actualWarehouseId: item.actualWarehouseId,
          actualWarehouseName: item.actualWarehouseName || '未分配库位',
          rawLocation: item.actualWarehouseName || '未分配库位'
        }))
        this.coilPicker.total = coils.length
        const coilByWarehouseId = {}
        coils.forEach(c => {
          if (c.actualWarehouseId && !coilByWarehouseId[c.actualWarehouseId]) {
            coilByWarehouseId[c.actualWarehouseId] = c
          }
        })
        const grid = {}
        warehouses.forEach(w => {
          const code = String(w.actualWarehouseCode || '')
          const m = code.match(/^([A-Za-z0-9]{3})([^-]+)-(\d{2})-(\d+)$/)
          if (!m) return
          const col = Number(m[2])
          const row = Number(m[3])
          const layer = Number(m[4])
          if (!grid[col]) grid[col] = { layer1: [], layer2: [] }
          const coil = coilByWarehouseId[w.actualWarehouseId] || null
          const cell = {
            _col: col,
            _row: row,
            _layer: layer,
            actualWarehouseId: w.actualWarehouseId,
            actualWarehouseCode: w.actualWarehouseCode,
            rawLocation: w.actualWarehouseName || w.actualWarehouseCode,
            ...(coil || {})
          }
          if (layer === 1) grid[col].layer1.push(cell)
          else if (layer === 2) grid[col].layer2.push(cell)
        })
        Object.keys(grid).forEach(k => {
          grid[k].layer1.sort((a, b) => Number(a._row) - Number(b._row))
          grid[k].layer2.sort((a, b) => Number(a._row) - Number(b._row))
        })
        this.coilPicker.coilGrid = grid
      } finally {
        this.coilPicker.loading = false
      }
    },
    searchCoils() {
      this.loadCoilsByWarehouse()
    },
    onCoilFilterChange() {
      this.loadCoilsByWarehouse()
    },
    openCoilPicker(row) {
      this.coilPicker.currentRow = row
      this.coilPicker.visible = true
      this.loadWarehouseOptions().then(() => {
        if (!this.coilPicker.selectedWarehouseId && this.coilPicker.warehouseOptions.length) {
          this.coilPicker.selectedWarehouseId = this.coilPicker.warehouseOptions[0].actualWarehouseId
        }
        this.loadCoilsByWarehouse()
      })
    },
    getCoilCell(col, row, layer) {
      const c = this.coilPicker.coilGrid[col]
      if (!c) return null
      const list = layer === 1 ? (c.layer1 || []) : (c.layer2 || [])
      return list.find(i => Number(i._row) === Number(row)) || null
    },
    selectCoilCell(col, row, layer) {
      const item = this.getCoilCell(col, row, layer)
      if (!item) return
      if (!item.rawMaterialId) {
        this.$message.warning('该库位当前无钢卷')
        return
      }
      this.pickRawMaterial(item)
    },
    pickRawMaterial(item) {
      const row = this.coilPicker.currentRow
      if (!row) return
      this.$set(row, 'rawMaterialId', item.rawMaterialId)
      this.$set(row, 'rawCoilNos', item.rawMaterialCode || '')
      this.$set(row, 'rawNetWeight', item.coilWeight != null ? item.coilWeight : '')
      this.$set(row, 'rawPackaging', item.packageType || '')
      this.$set(row, 'rawEdgeReq', item.edgeType || '')
      this.$set(row, 'rawCoatingType', item.zincLayer || item.surfaceTreatmentDesc || '')
      this.$set(row, 'rawLocation', item.rawLocation || '')
      this.coilPicker.visible = false
      this.onCellChange(row)
    },
    onCellChange(row) {
      if (row && !this.rows.includes(row)) {
        this.rows.push(row)
      }
      this.markDirty(row)
      this.scheduleAutoSave()
    },
    queryLineHistory(query, cb) {
      const list = this.lineHistory || []
      const q = (query || '').trim().toLowerCase()
      const res = list
        .filter(item => !q || String(item).toLowerCase().includes(q))
        .map(item => ({ value: item }))
      cb(res)
    },
    onLineHistoryInput(row) {
      if (!row) return
      if (row.lineId && row.lineName && row.lineName.trim() !== String(row.lineName).trim()) {
        row.lineId = null
      }
      this.onCellChange(row)
    },
    onLineHistorySelect(row) {
      return () => {
        this.onCellChange(row)
      }
    },
    onFilterChange() {
      this.pager.pageNum = 1
    },
    normalizeRow(row) {
      const base = this.buildEmptyRow()
      Object.keys(base).forEach(k => {
        if (row && row[k] !== undefined && row[k] !== null) base[k] = row[k]
      })
      const idVal = row ? (row.quickSheetId !== undefined && row.quickSheetId !== null ? row.quickSheetId : row.id) : null
      base.quickSheetId = idVal != null ? idVal : null
      return base
    },
    buildEmptyRow() {
      const row = { quickSheetId: null }
      this.flatColumns.forEach(col => {
        if (!col.prop) return
        row[col.prop] = ''
      })
      return row
    },
    isRowEmpty(row) {
      if (!row) return true
      return !this.flatColumns.some(col => col.prop && row[col.prop])
    },
    isTimeField(prop) {
      return prop === 'startTime' || prop === 'endTime'
    },
    markDirty(row) {
      if (!row) return
      if (!row._tmpId) row._tmpId = Math.random().toString(36).slice(2)
      this.dirtyIds.add(row._tmpId)
    },
    async clearRow(row) {
      if (!row) return
      if (!row.quickSheetId) {
        this.flatColumns.forEach(col => {
          if (!col.prop) return
          row[col.prop] = ''
        })
        row.quickSheetId = null
        return
      }
      await deleteQuickSheetRow(row.quickSheetId)
      this.flatColumns.forEach(col => {
        if (!col.prop) return
        row[col.prop] = ''
      })
      row.quickSheetId = null
    },
    scheduleAutoSave() {
      if (this.autoSaveTimer) clearTimeout(this.autoSaveTimer)
      this.autoSaveTimer = setTimeout(() => {
        this.saveAll(true)
      }, 800)
    },
    async saveAll(isAuto = false, payload) {
      const rows = payload && payload.rows
        ? payload.rows
        : this.rows.filter(r => r && r._tmpId && this.dirtyIds.has(r._tmpId))
          .map(r => ({
            ...r,
            quickSheetId: r.quickSheetId || null
          }))
      if (!rows.length) {
        if (!isAuto) this.$message.warning('暂无可保存数据')
        return
      }
      const missing = this.collectMissing(rows)
      if (missing.length && !isAuto) {
        this.$message.warning(`必填项未填写：${missing.join('；')}`)
        return
      }
      this.saving = true
      try {
        await saveQuickSheet({ rows })
        if (!isAuto) this.$message.success('保存成功')
        rows.forEach(r => {
          if (r._tmpId) this.dirtyIds.delete(r._tmpId)
        })
      } finally {
        this.saving = false
      }
    },
    addRow() {
      this.rows.push(this.buildEmptyRow())
      if (this.rows.length < this.pager.pageSize) {
        this.rows = this.rows.concat(this.buildEmptyRows(this.pager.pageSize - this.rows.length))
      }
    },
    onFilterChange() {
      this.pager.pageNum = 1
    },
    exportCsv() {
      this.loading = true
      exportQuickSheet()
        .then(blob => {
          const filename = `quick_sheet_${new Date().getTime()}.xlsx`
          saveAs(new Blob([blob], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), filename)
        })
        .finally(() => {
          this.loading = false
        })
    },
    addRow() {
      this.rows.unshift(this.buildEmptyRow())
      this.pager.pageNum = 1
    },
    collectMissing(rows) {
      const msgs = []
      rows.forEach((row, idx) => {
        if (this.isRowEmpty(row)) return
        const line = idx + 1
        const missing = []
        if (!row.lineName && !row.lineId) missing.push('产线')
        if (!row.planCode) missing.push('计划号')
        if (!row.startTime) missing.push('开始时间')
        if (!row.endTime) missing.push('结束时间')
        if (missing.length) {
          msgs.push(`第${line}行缺少${missing.join('、')}`)
        }
      })
      return msgs
    }
  }
}
</script>

<style scoped lang="scss">
.aps-quick-sheet {
  padding: 8px;
  background: #f7f9fc;
}

.sheet-toolbar,
.sheet-body {
  border: 1px solid #eef2f7;
  border-radius: 8px;
  background: #fff;
}

.sheet-toolbar {
  margin-bottom: 8px;
  padding: 8px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.sheet-actions { display: flex; gap: 8px; }

.preset-bar { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #667085; }
.preset-title { font-weight: 600; color: #475467; }

::v-deep .excel-table {
  border: 1px solid #edf1f7;
}
::v-deep .excel-table th.el-table__cell {
  background: #fafbfe;
  color: #5d6b82;
  font-weight: 600;
  border-right: 1px solid #edf1f7;
  border-bottom: 1px solid #edf1f7;
  padding: 4px 0;
}
::v-deep .excel-table td.el-table__cell {
  border-right: 1px solid #f0f2f6;
  border-bottom: 1px solid #f0f2f6;
  padding: 1px 2px;
}
::v-deep .excel-table .el-table__row:hover > td {
  background-color: #f7faff !important;
}

::v-deep .el-input__inner {
  border-radius: 0;
  height: 26px;
  line-height: 26px;
  border: none;
  box-shadow: none;
  background: transparent;
}

::v-deep .el-input__inner:focus {
  border: none;
  box-shadow: inset 0 0 0 1px #4a90e2;
  background: #fff;
}

.missing-tip {
  margin-top: 8px;
  padding: 8px 12px;
  background: #fff7e6;
  border: 1px solid #ffd591;
  border-radius: 6px;
  font-size: 12px;
  color: #d46b08;
}

.missing-item {
  line-height: 1.6;
}

.coil-picker-btn {
  padding: 0;
}

.coil-bird-wrap { border: 1px solid #ebeef5; border-radius: 6px; padding: 8px; }
.coil-legend { display: flex; gap: 14px; font-size: 12px; color: #606266; margin-bottom: 8px; }
.legend-item { display: inline-flex; align-items: center; gap: 4px; }
.dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }
.dot.layer1 { background: #fff3e0; border: 1px solid #f5d7a1; }
.dot.layer2 { background: #e8f5e9; border: 1px solid #b7ddb9; }
.dot.occupied { background: #f6f8fb; border: 1px solid #d7dde5; }
.coil-grid-scroll { overflow: auto; max-height: 460px; }
.coil-col-ruler { display: flex; min-width: max-content; }
.ruler-empty { width: 28px; flex: none; }
.ruler-col { width: 138px; text-align: center; font-size: 12px; color: #606266; }
.coil-grid-main { display: flex; min-width: max-content; }
.coil-row-ruler { width: 28px; display: flex; flex-direction: column; }
.ruler-row { height: 56px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #909399; }
.coil-grid-columns { display: flex; gap: 8px; }
.coil-col-pair { display: grid; grid-template-columns: 1fr 1fr; width: 180px; border: 1px solid #eef2f7; border-radius: 4px; overflow: visible; }
.coil-layer { display: flex; flex-direction: column; }
.coil-layer.layer2-shift { margin-top: 28px; }
.coil-cell { height: 56px; border-bottom: 1px solid #eef2f7; border-right: 1px solid #eef2f7; padding: 4px; font-size: 11px; color: #909399; display: flex; align-items: center; justify-content: center; text-align: center; cursor: pointer; }
.coil-cell.layer1 { background: #fff3e0; color: #e67e22; }
.coil-cell.layer2 { background: #e8f5e9; color: #2e8b57; border-right: none; }
.coil-cell.occupied { font-weight: 600; }
.coil-cell .line1 { width: 100%; font-size: 11px; line-height: 1.1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.coil-cell .line2 { width: 100%; font-size: 11px; line-height: 1.1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.coil-picker-foot { margin-top: 8px; text-align: right; color: #909399; font-size: 12px; }
</style>
