<template>
    <div class="warehouse-screen">
        <div class="scale-wrapper" :style="scaleWrapperStyle">
        <!-- 动态背景 -->
        <div class="dynamic-bg">
            <!-- 网格线 -->
            <div class="grid-container">
                <div class="grid-lines horizontal"></div>
                <div class="grid-lines vertical"></div>
            </div>
            <!-- 粒子 -->
            <div class="particles-container">
                <div v-for="i in 30" :key="i" class="particle" :style="getParticleStyle(i)"></div>
            </div>
            <!-- 扫描线 -->
            <div class="scan-line"></div>
            <!-- 光晕效果 -->
            <div class="glow-globe globe-1"></div>
            <div class="glow-globe globe-2"></div>
            <div class="glow-globe globe-3"></div>
        </div>

        <!-- 头部 -->
        <header class="screen-header">
            <dv-border-box-1 class="title-border">
                <div class="title-box">
                    <span class="title-icon"><span class="diamond"></span></span>
                    <h1 class="screen-title">库区总览大屏</h1>
                    <span class="subtitle">Warehouse Overview Dashboard</span>
                    <span class="clock-text">{{ currentDate }}</span>
                </div>
            </dv-border-box-1>

        </header>

        <!-- 主内容 -->
        <main class="screen-body">
            <!-- 左侧边栏 -->
            <aside class="sidebar">

                <!-- 仓库结构下拉 -->

                <div class="tree-panel">
                    <dv-border-box-8 :reverse="true">
                    <div class="panel-header header-compact">
                        <span class="header-icon">
                            <svg class="header-icon-svg" viewBox="0 0 24 24" width="18" height="18" color="#00d4ff">
                                <circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="1" opacity="0.35"/>
                                <circle cx="12" cy="12" r="5" fill="none" stroke="currentColor" stroke-width="0.5" opacity="0.2"/>
                                <line x1="2" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="0.5" opacity="0.2"/>
                                <line x1="12" y1="2" x2="12" y2="22" stroke="currentColor" stroke-width="0.5" opacity="0.2"/>
                                <g class="radar-beam">
                                    <path d="M12,12 L12,3 A9,9 0 0,1 21,12 Z" fill="currentColor" opacity="0.25"/>
                                </g>
                                <circle cx="12" cy="12" r="1.5" fill="#00d4ff" opacity="0.9"/>
                            </svg>
                        </span>
                        <span class="panel-title">仓库结构</span>
                    </div>
                    <div class="select-body">
                        <el-select
                            v-model="selectedNodeId"
                            placeholder="请选择仓库分区"
                            filterable
                            clearable
                            :teleported="false"
                            @change="handleSelectChange"
                            class="warehouse-select"
                            popper-class="warehouse-select-popper"
                        >
                            <el-option-group
                                v-for="group in warehouseOptions"
                                :key="group.label"
                                :label="group.label"
                            >
                                <el-option
                                    v-for="item in group.options"
                                    :key="item.id"
                                    :label="item.name"
                                    :value="item.id"
                                />
                            </el-option-group>
                        </el-select>
                    </div>
                    </dv-border-box-8>
                </div>


                <!-- 统计卡片 -->
                <div class="stats-wrapper">
                    <dv-border-box-8 :reverse="true">
                <div class="stats-sidebar">
                    <div class="total-row">
                        <span class="total-num">{{ animatedTotal }}</span>
                        <span class="total-lbl">总库位数</span>
                    </div>
                    <div class="ring-chart-wrapper">
                        <div ref="chartRef" class="ring-chart"></div>
                    </div>
                    <div class="util-text">
                        <span class="util-number">{{ statistics.utilization }}%</span>
                        <span class="util-label">利用率</span>
                    </div>
                    <div class="chart-legend">
                        <div class="legend-item">
                            <span class="chart-dot occupied"></span>
                            已占用 <span class="legend-val">{{ statistics.occupied }}</span>
                        </div>
                        <div class="legend-item">
                            <span class="chart-dot error"></span>
                            异常 <span class="legend-val">{{ statistics.error }}</span>
                        </div>
                        <div class="legend-item">
                            <span class="chart-dot available"></span>
                            空闲 <span class="legend-val">{{ statistics.available }}</span>
                        </div>
                    </div>
                    <div class="status-bar">
                        <span v-for="item in statusBarData" :key="item.label"
                              class="status-bar-fill"
                              :style="{ width: item.pct + '%', background: item.color }"></span>
                    </div>
                    <div class="status-bar-labels">
                        <div class="status-bar-label" v-for="item in statusBarData" :key="item.label">
                            <span class="sbl-dot" :style="{ background: item.color }"></span>
                            <span class="sbl-text">{{ item.label }} {{ item.pct }}%</span>
                        </div>
                    </div>
                    <div class="breath-indicators">
                        <div v-for="item in statusBarData" :key="'b'+item.label"
                             class="breath-dot"
                             :style="{
                                 background: item.color,
                                 animationDuration: Math.max(1.5, 4 - item.pct * 0.03) + 's',
                                 boxShadow: '0 0 ' + Math.round(3 + item.pct * 0.12) + 'px ' + item.color
                             }"
                             :title="item.label + ' ' + item.pct + '%'">
                        </div>
                    </div>
                    <div class="micro-eq">
                        <span v-for="i in 18" :key="i" class="micro-eq-bar"
                              :style="{
                                  animationDelay: (i * 0.08) + 's',
                                  animationDuration: (1.2 + (i % 5) * 0.15) + 's'
                              }"></span>
                    </div>
                    <div class="scan-line-red"><div class="scan-beam"></div></div>
                    <div class="mini-chart">
                        <div class="mini-chart-bars">
                            <div v-for="i in 10" :key="i" class="mini-bar"
                                 :style="{
                                     height: (18 + (i * 7) % 52) + '%',
                                     animationDelay: i * 0.12 + 's',
                                     animationDuration: (1.8 + (i % 3) * 0.4) + 's'
                                 }"></div>
                        </div>
                        <div class="mini-chart-axis"></div>
                    </div>
                </div>
                    </dv-border-box-8>
                </div>
                <div class="split-wrapper">
                    <dv-border-box-8 :reverse="true">
                <div class="split-stats">

                    <div class="split-header">
                        <span class="split-title-icon">
                            <svg class="flow-icon-svg" viewBox="0 0 24 24" width="16" height="16" color="#00d4ff">
                                <line x1="5" y1="7" x2="15" y2="7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.25"/>
                                <line x1="2" y1="12" x2="20" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.4"/>
                                <line x1="5" y1="17" x2="15" y2="17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.25"/>
                                <circle r="2" fill="#00d4ff" opacity="0.9">
                                    <animate attributeName="cx" values="2;20;2" dur="2s" repeatCount="indefinite"/>
                                    <animate attributeName="cy" values="12;12;12" dur="2s" repeatCount="indefinite"/>
                                </circle>
                                <circle r="4" fill="none" stroke="#00d4ff" stroke-width="0.8" opacity="0.2">
                                    <animate attributeName="cx" values="2;20;2" dur="2s" repeatCount="indefinite"/>
                                    <animate attributeName="cy" values="12;12;12" dur="2s" repeatCount="indefinite"/>
                                </circle>
                            </svg>
                        </span>
                        <span class="split-title-text">库位拆分状态</span>
                    </div>
                    <div class="split-body">
                        <div class="split-item">
                            <span class="split-num unsplit">{{ splitStats.unsplit }}</span>
                            <span class="split-lbl">大库位</span>
                            <span class="split-pct">({{ splitStats.unsplitPct }}%)</span>
                        </div>
                        <div class="split-vr"></div>
                        <div class="split-item">
                            <span class="split-num split">{{ splitStats.split }}</span>
                            <span class="split-lbl">小库位</span>
                            <span class="split-pct">({{ splitStats.splitPct }}%)</span>
                        </div>

                    </div>

                    <div class="split-bar">
                        <div class="split-bar-fill unsplit" :style="{ width: splitStats.unsplitPct + '%' }"></div>
                        <div class="split-bar-fill split" :style="{ width: splitStats.splitPct + '%' }"></div>
                    </div>
                    <div class="split-bar-labels">
                        <span class="sbl-item">
                            <span class="sbl-dot unsplit-dot"></span>大库位 {{ splitStats.unsplitPct }}%
                        </span>
                        <span class="sbl-item">
                            <span class="sbl-dot split-dot"></span>小库位 {{ splitStats.splitPct }}%
                        </span>
                    </div>

                </div>
                </dv-border-box-8>
                </div>

                <!-- 层列信息 -->
                <div class="layer-wrapper">
                <div class="layer-stats">
                    <dv-border-box-8 :reverse="true">
                    <div class="layer-header">
                        <span class="layer-title-icon">
                            <svg class="flow-icon-svg" viewBox="0 0 24 24" width="16" height="16" color="#00d4ff">
                                <line x1="5" y1="7" x2="15" y2="7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.25"/>
                                <line x1="2" y1="12" x2="20" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.4"/>
                                <line x1="5" y1="17" x2="15" y2="17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.25"/>
                                <circle r="2" fill="#00d4ff" opacity="0.9">
                                    <animate attributeName="cx" values="2;20;2" dur="2.5s" repeatCount="indefinite"/>
                                    <animate attributeName="cy" values="12;12;12" dur="2.5s" repeatCount="indefinite"/>
                                </circle>
                                <circle r="4" fill="none" stroke="#00d4ff" stroke-width="0.8" opacity="0.2">
                                    <animate attributeName="cx" values="2;20;2" dur="2.5s" repeatCount="indefinite"/>
                                    <animate attributeName="cy" values="12;12;12" dur="2.5s" repeatCount="indefinite"/>
                                </circle>
                            </svg>
                        </span>
                        <span class="layer-title-text">层列信息</span>
                    </div>
                    <div class="layer-body">
                        <div class="layer-row">
                            <span class="layer-lbl">总列数</span>
                            <span class="layer-val">{{ columnStats.totalColumns }}</span>
                        </div>
                        <div class="layer-row">
                            <span class="layer-lbl">1层库位</span>
                            <span class="layer-val">{{ columnStats.totalLayer1 }} 个</span>
                        </div>
                        <div class="layer-row">
                            <span class="layer-lbl">2层库位</span>
                            <span class="layer-val">{{ columnStats.totalLayer2 }} 个</span>
                        </div>
                        <div class="layer-row">
                            <span class="layer-lbl">每列平均</span>
                            <span class="layer-val">{{ columnStats.avgPerColumn }} 个</span>
                        </div>
                    </div>
                    </dv-border-box-8>
                </div>
                </div>

            </aside>

            <!-- 中间大框：库位列表 -->
            <section class="main-content">
                <dv-border-box1 ref="borderRef">
                    <div class="panel">
                        <div class="panel-header">
                            <span class="header-icon">
                                <svg class="header-icon-svg" viewBox="0 0 24 24" width="18" height="18" color="#00d4ff">
                                    <line x1="4" y1="8" x2="18" y2="8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.25"/>
                                    <line x1="4" y1="12" x2="18" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.4"/>
                                    <line x1="4" y1="16" x2="18" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.25"/>
                                    <circle r="2" fill="#00d4ff" opacity="0.9">
                                        <animate attributeName="cx" values="4;18;4" dur="2s" repeatCount="indefinite"/>
                                        <animate attributeName="cy" values="12;12;12" dur="2s" repeatCount="indefinite"/>
                                    </circle>
                                    <circle r="4" fill="none" stroke="#00d4ff" stroke-width="0.8" opacity="0.2">
                                        <animate attributeName="cx" values="4;18;4" dur="2s" repeatCount="indefinite"/>
                                        <animate attributeName="cy" values="12;12;12" dur="2s" repeatCount="indefinite"/>
                                    </circle>
                                </svg>
                            </span>
                            <span class="panel-title">{{ selectedNodeName || '库位列表' }}</span>
                            <span class="panel-count">共 {{ warehouseList.length }} 条</span>
                        </div>
                        <div class="panel-body table-container">
                            <div v-if="warehouseList.length === 0" class="empty-tip">
                                <span class="empty-icon">📭</span>
                                <div class="empty-text">请从左侧选择仓库分区</div>
                            </div>
                            <template v-else>
                                <!-- 图例 -->
                                <div class="grid-legend">
                                    <div class="legend-item">
                                        <span class="legend-dot available"></span>
                                        <span>空闲</span>
                                    </div>
                                    <div class="legend-item">
                                        <span class="legend-dot occupied"></span>
                                        <span>已占用</span>
                                    </div>
                                    <div class="legend-item">
                                        <span class="legend-dot error"></span>
                                        <span>异常</span>
                                    </div>
                                    <div class="legend-item">
                                        <span class="legend-dot has-coil-icon"></span>
                                        <span>有钢卷</span>
                                    </div>
                                </div>
                                <!-- 库位网格：外层容器负责裁剪，内层负责 flex 布局 + transform 移动 -->
                                <div ref="gridContainerRef" class="warehouse-grid" @mouseenter="onGridMouseEnter" @mouseleave="onGridMouseLeave">
                                    <div class="grid-inner" :class="{ 'grid-centered': isGridCentered }" ref="gridInnerRef" @mousedown="onGridDragStart">
                                    <div
                                        v-for="colNum in sortedColumnKeys"
                                        :key="colNum"
                                        class="grid-column"
                                        :style="{ minWidth: columnWidth + 'px' }"
                                    >
                                        <div class="column-header">{{ colNum }}列</div>
                                        <div class="column-pair">
                                            <div class="layer-cells layer1-cells">
                                                <div
                                                    v-for="wh in gridData[colNum].layer1"
                                                    :key="wh.actualWarehouseId"
                                                    class="grid-cell"
                                                    :class="[getStatusClass(wh), 'cell-layer-1', { 'has-coil': !!wh.currentCoilNo }]"
                                                    :style="cellStyle"
                                                    @click="showDetail(wh, $event)"
                                                >
                                                    <span class="cell-code">{{ wh.actualWarehouseCode }}</span>
                                                    <span v-if="wh.currentCoilNo" class="cell-coil">{{ wh.currentCoilNo }}</span>
                                                </div>
                                            </div>
                                            <div class="layer-cells layer2-cells" :style="{ marginTop: cellMarginOffset + 'px' }">
                                                <div
                                                    v-for="wh in gridData[colNum].layer2"
                                                    :key="wh.actualWarehouseId"
                                                    class="grid-cell"
                                                    :class="[getStatusClass(wh), 'cell-layer-2', { 'has-coil': !!wh.currentCoilNo }]"
                                                    :style="cellStyle"
                                                    @click="showDetail(wh, $event)"
                                                >
                                                    <span class="cell-code">{{ wh.actualWarehouseCode }}</span>
                                                    <span v-if="wh.currentCoilNo" class="cell-coil">{{ wh.currentCoilNo }}</span>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    </div>
                                </div>
                            </template>
                        </div>
                    </div>
                </dv-border-box1>
            </section>
        </main>

        <!-- 底部跃动方块 -->
        <div class="eq-bar-container">
            <div
                v-for="i in 40"
                :key="i"
                class="eq-bar"
                :style="getEqBarStyle(i)"
            ></div>
        </div>

    </div><!-- /scale-wrapper -->

        <!-- 库位详情浮层 -->
        <div
            v-show="!!detailWarehouse"
            ref="tooltipRef"
            class="warehouse-tooltip"
            :style="tooltipStyle"
            @mousedown.stop
        >
            <template v-if="detailWarehouse">
                <div class="tooltip-header">
                    <span class="tooltip-code">{{ detailWarehouse.actualWarehouseCode }}</span>
                    <span class="tooltip-status" :class="getStatusClass(detailWarehouse)">{{ getStatusText(detailWarehouse) }}</span>
                </div>
                <div class="tooltip-divider"></div>
                <div class="tooltip-body">
                    <div class="tooltip-row">
                        <span class="tooltip-label">库位名称</span>
                        <span class="tooltip-value">{{ detailWarehouse.actualWarehouseName }}</span>
                    </div>
                    <div class="tooltip-row">
                        <span class="tooltip-label">钢卷编号</span>
                        <span class="tooltip-value highlight" v-if="detailWarehouse.currentCoilNo">{{ detailWarehouse.currentCoilNo }}</span>
                        <span class="tooltip-value dim" v-else>无</span>
                    </div>
                    <div class="tooltip-row">
                        <span class="tooltip-label">数量</span>
                        <span class="tooltip-value">{{ detailWarehouse.coilQuantity || 0 }}</span>
                    </div>
                    <div class="tooltip-row" v-if="detailWarehouse.parsedInfo">
                        <span class="tooltip-label">位置</span>
                        <span class="tooltip-value">{{ detailWarehouse.parsedInfo.column }}列 {{ detailWarehouse.parsedInfo.row }}行 {{ detailWarehouse.parsedInfo.layer }}层</span>
                    </div>
                </div>
            </template>
        </div>

    </div><!-- /warehouse-screen -->
</template>

<script>
import { ref, reactive, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
import * as echarts from 'echarts'
import { treeActualWarehouseTwoLevel, listActualWarehouse } from '@/api/wms/actualWarehouse'

export default {
    name: 'WarehouseOverview',
    setup() {
        // 时间显示
        const currentDate = ref('')
        let timeInterval = null

        // 树形数据
        const warehouseTree = ref([])

        // 下拉选项（树形结构展平为分组）
        const warehouseOptions = computed(() => {
            return (warehouseTree.value || []).map(parent => ({
                label: parent.actualWarehouseName,
                options: (parent.children || []).map(child => ({
                    id: child.actualWarehouseId,
                    name: child.actualWarehouseName
                }))
            }))
        })

        // 选中状态
        const selectedNodeId = ref('')
        const selectedNodeName = ref('')

        // 库位列表（Mock数据）
        const warehouseList = ref([])

        // 统计数据
        const statistics = reactive({
            total: 0,
            error: 0,
            occupied: 0,
            available: 0,
            utilization: 0
        })

        // 拆分状态统计
        const splitStats = computed(() => {
            const total = warehouseList.value.length
            const split = warehouseList.value.filter(w => w.splitStatus === 1).length
            const unsplit = total - split
            return {
                total,
                split,
                unsplit,
                splitPct: total > 0 ? Math.round((split / total) * 100) : 0,
                unsplitPct: total > 0 ? Math.round((unsplit / total) * 100) : 0
            }
        })

        // 层列信息统计
        const columnStats = computed(() => {
            const keys = Object.keys(gridData.value).sort((a, b) => Number(a) - Number(b))
            const totalLayer1 = keys.reduce((sum, k) => sum + (gridData.value[k]?.layer1?.length || 0), 0)
            const totalLayer2 = keys.reduce((sum, k) => sum + (gridData.value[k]?.layer2?.length || 0), 0)
            return {
                totalColumns: keys.length,
                totalLayer1,
                totalLayer2,
                avgPerColumn: keys.length > 0 ? Math.round((totalLayer1 + totalLayer2) / keys.length) : 0
            }
        })

        // 状态占比条数据
        const statusBarData = computed(() => {
            const total = statistics.total || 1
            return [
                { label: '已占用', pct: Math.round(statistics.occupied / total * 100), color: '#7c63ff' },
                { label: '异常', pct: Math.round(statistics.error / total * 100), color: '#ff6b6b' },
                { label: '空闲', pct: Math.round(statistics.available / total * 100), color: '#00ff88' }
            ]
        })

        // 动画数字
        const animatedTotal = ref(0)
        const animatedOccupied = ref(0)
        const animatedError = ref(0)
        const animatedAvailable = ref(0)
        const animatedUtilization = ref(0)

        // 加载状态
        const loading = ref(false)

        // 缩放比例（transform scale 适配超大屏）
        const scaleRatio = ref(1)
        const scaleWrapperStyle = computed(() => ({
            transform: `scale(${scaleRatio.value})`
        }))
        const updateScale = () => {
            scaleRatio.value = Math.min(window.innerWidth / 1920, window.innerHeight / 1080)
        }

        // 网格数据
        const gridData = ref({})

        // 排序的列键
        const sortedColumnKeys = computed(() => {
            return Object.keys(gridData.value).sort((a, b) => Number(a) - Number(b))
        })

        // 详情浮层
        const detailWarehouse = ref(null)
        const tooltipRef = ref(null)
        const tooltipPos = reactive({ x: 0, y: 0 })
        const tooltipStyle = computed(() => ({
            left: tooltipPos.x + 'px',
            top: tooltipPos.y + 'px'
        }))

        // 容器尺寸观测（自适应网格）
        const gridContainerRef = ref(null)
        const containerRect = reactive({ width: 800, height: 600 })
        let resizeObserver = null
        let chartResizeObserver = null

        // 自适应格子尺寸（错层布局：高宽独立）
        const maxLayerRows = computed(() => {
            const keys = sortedColumnKeys.value
            let m = 0
            keys.forEach(k => {
                const col = gridData.value[k]
                if (col) {
                    m = Math.max(m, col.layer1.length, col.layer2.length)
                }
            })
            return Math.max(m, 1)
        })

        const cellHeight = computed(() => {
            const keys = sortedColumnKeys.value
            const colCount = keys.length
            if (!colCount) return 60
            const mRows = maxLayerRows.value
            const ch = containerRect.height
            const h = (ch - 120 - (mRows - 1) * 4) / mRows
            // 大屏下按容器高度比例放大，不再固定封顶 120
            const maxH = Math.floor(ch * 0.15)
            return Math.max(48, Math.min(Math.max(maxH, 120), Math.floor(h)))
        })

        const cellWidth = computed(() => {
            const keys = sortedColumnKeys.value
            const colCount = keys.length
            if (!colCount) return 60
            const cw = containerRect.width
            const byWidth = (cw - 30 - (colCount - 1) * 8) / colCount
            let w = (byWidth - 6) / 2
            if (colCount <= 15) {
                w = Math.max(w, cellHeight.value)
            }
            // 大屏下按列宽比例放大，不再固定封顶 140
            const maxW = Math.floor(byWidth * 0.45)
            return Math.max(30, Math.min(Math.max(maxW, 140), Math.floor(w)))
        })

        // 错层偏移量（二层下移半格）
        const cellMarginOffset = computed(() => Math.round(cellHeight.value / 2))

        // 网格居中（列数少于15时居中更美观）
        const isGridCentered = computed(() => {
            return sortedColumnKeys.value.length <= 15
        })

        const cellStyle = computed(() => {
            const h = cellHeight.value
            const w = cellWidth.value
            const fs = Math.max(9, Math.min(11, Math.floor(Math.min(w, h) * 0.14)))
            return { width: w + 'px', height: h + 'px', fontSize: fs + 'px' }
        })

        const columnWidth = computed(() => cellWidth.value * 2 + 14)

        // 测量网格容器尺寸（始终使用 pre-scale 尺寸，避免缩放反馈循环）
        const measureGridContainer = () => {
            nextTick(() => {
                const el = gridContainerRef.value
                if (!el) return
                containerRect.width = el.clientWidth
                containerRect.height = el.clientHeight
                // 持久化 ResizeObserver，监听容器尺寸变化
                if (!resizeObserver) {
                    resizeObserver = new ResizeObserver(entries => {
                        for (const entry of entries) {
                            containerRect.width = entry.contentRect.width
                            containerRect.height = entry.contentRect.height
                        }
                        nextTick(() => checkOverflow())
                    })
                    resizeObserver.observe(el)
                }
                // 检查是否需要自动滚动（立即 + 延迟，确保布局完成）
                checkOverflow()
                setTimeout(() => checkOverflow(), 400)
            })
        }

        // --- 自动轮播滚动（transform 驱动，不依赖 overflow） ---
        const gridInnerRef = ref(null)
        let scrollTimer = null
        let pauseTimer = null
        let stayTimer = null
        let isScrollPaused = false

        // 滚动共享状态（自动轮播 + 拖拽共用）
        const scrollPos = { x: 0, y: 0 }
        const overflowDist = { x: 0, y: 0 }
        const dragState = { active: false, maybeClick: false, startX: 0, startY: 0, startScrollX: 0, startScrollY: 0 }

        const applyTransform = () => {
            const inner = gridInnerRef.value
            if (!inner) return
            inner.style.transform = `translate(${-scrollPos.x}px, ${-scrollPos.y}px)`
        }

        const startAutoScroll = () => {
            stopAutoScroll()
            const container = gridContainerRef.value
            const inner = gridInnerRef.value
            if (!container || !inner) return

            // 使用预测量的溢出距离
            if (overflowDist.x <= 0 && overflowDist.y <= 0) return

            const step = () => {
                if (isScrollPaused) return

                let changed = false
                const inner = gridInnerRef.value

                if (overflowDist.x > 0) {
                    // 渐进减速：距终点 100px 内线性降低速度
                    const endX = overflowDist.x + 40
                    const remainingX = endX - scrollPos.x
                    const decelDist = 100
                    const speedX = remainingX < decelDist
                        ? Math.max(0.2, remainingX / decelDist)
                        : 1
                    scrollPos.x += speedX
                    if (scrollPos.x >= endX) {
                        scrollPos.x = 0
                        changed = true
                        // CSS 过渡实现平滑回弹
                        if (inner) {
                            inner.style.transition = 'transform 0.35s ease'
                            setTimeout(() => {
                                if (inner) inner.style.transition = ''
                            }, 400)
                        }
                    }
                }

                if (overflowDist.y > 0) {
                    const endY = overflowDist.y + 30
                    const remainingY = endY - scrollPos.y
                    const speedY = remainingY < 100
                        ? Math.max(0.1, remainingY / 100)
                        : 0.5
                    scrollPos.y += speedY
                    if (scrollPos.y >= endY) {
                        scrollPos.y = 0
                        changed = true
                        if (inner) {
                            inner.style.transition = 'transform 0.35s ease'
                            setTimeout(() => {
                                if (inner) inner.style.transition = ''
                            }, 400)
                        }
                    }
                }

                if (changed) {
                    isScrollPaused = true
                    clearTimeout(pauseTimer)
                    pauseTimer = setTimeout(() => { isScrollPaused = false }, 2000)
                }

                applyTransform()
            }

            scrollTimer = setInterval(step, 30)
        }

        const stopAutoScroll = () => {
            if (scrollTimer) {
                clearInterval(scrollTimer)
                scrollTimer = null
            }
            if (pauseTimer) {
                clearTimeout(pauseTimer)
                pauseTimer = null
            }
            if (stayTimer) {
                clearTimeout(stayTimer)
                stayTimer = null
            }
        }

        const resetScroll = () => {
            scrollPos.x = 0
            scrollPos.y = 0
            applyTransform()
        }

        const checkOverflow = () => {
            const el = gridContainerRef.value
            const inner = gridInnerRef.value
            if (!el || !inner) return
            stopAutoScroll()
            resetScroll()
            // 等待一帧让布局稳定再测量溢出
            requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    overflowDist.x = Math.max(inner.scrollWidth - el.clientWidth, 0)
                    overflowDist.y = Math.max(inner.scrollHeight - el.clientHeight, 0)
                    startAutoScroll()
                })
            })
        }

        const onGridMouseEnter = () => { isScrollPaused = true }
        const onGridMouseLeave = () => { isScrollPaused = false }

        // --- 鼠标拖拽滑动网格（带阈值 + 停留窗口） ---
        const onGridDragStart = (e) => {
            if (e.button !== 0) return
            e.preventDefault()
            dragState.maybeClick = true
            dragState.startX = e.clientX
            dragState.startY = e.clientY
            dragState.startScrollX = scrollPos.x
            dragState.startScrollY = scrollPos.y
            isScrollPaused = true
            clearTimeout(stayTimer)
            document.addEventListener('mousemove', onGridDragMove)
            document.addEventListener('mouseup', onGridDragEnd)
        }

        const onGridDragMove = (e) => {
            const dx = e.clientX - dragState.startX
            const dy = e.clientY - dragState.startY

            // 阈值判断：移动超过 8px 才真正进入拖拽模式
            if (dragState.maybeClick) {
                if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return
                dragState.maybeClick = false
                dragState.active = true
                const inner = gridInnerRef.value
                if (inner) inner.style.cursor = 'grabbing'
            }

            if (!dragState.active) return

            const s = scaleRatio.value
            scrollPos.x = Math.max(0, Math.min(overflowDist.x + 40, dragState.startScrollX - dx / s))
            scrollPos.y = Math.max(0, Math.min(overflowDist.y + 30, dragState.startScrollY - dy * 0.5 / s))
            applyTransform()
        }

        const onGridDragEnd = () => {
            document.removeEventListener('mousemove', onGridDragMove)
            document.removeEventListener('mouseup', onGridDragEnd)

            // 未超过阈值，视为点击，不干涉
            if (dragState.maybeClick) return

            dragState.active = false
            const inner = gridInnerRef.value
            if (inner) inner.style.cursor = ''

            // 停留窗口：4 秒后恢复自动轮播，让用户有足够时间点击
            clearTimeout(stayTimer)
            stayTimer = setTimeout(() => {
                isScrollPaused = false
            }, 4000)
        }

        // 监听仓库列表变化，数据就绪后启动滚动
        const stopWatchList = watch(warehouseList, () => {
            nextTick(() => checkOverflow())
        })

        // 更新时间
        const updateTime = () => {
            const now = new Date()
            currentDate.value = now.toLocaleString('zh-CN', {
                year: 'numeric',
                month: 'long',
                day: 'numeric',
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit'
            })
        }

        // 获取粒子样式
        const getParticleStyle = (index) => ({
            left: `${Math.random() * 100}%`,
            animationDelay: `${index * 0.3}s`,
            animationDuration: `${6 + Math.random() * 4}s`
        })

        const getEqBarStyle = (index) => ({
            animationDelay: `${index * 0.06}s`,
            animationDuration: `${1.8 + (index % 7) * 0.25}s`
        })

        // 数字动画
        const animateValue = (start, end, callback, duration = 1000) => {
            const startTime = performance.now()
            const animate = (currentTime) => {
                const elapsed = currentTime - startTime
                const progress = Math.min(elapsed / duration, 1)
                const easeOut = 1 - Math.pow(1 - progress, 4)
                const currentValue = Math.round(start + (end - start) * easeOut)
                callback(currentValue)
                if (progress < 1) {
                    requestAnimationFrame(animate)
                }
            }
            requestAnimationFrame(animate)
        }

        // ===== 环图 =====
        const chartRef = ref(null)
        let chartInstance = null
        let chartInitTimer = null

        const initChart = () => {
            // dv-border-box8 渲染较慢，需要延迟以确保容器有尺寸
            if (chartInitTimer) clearTimeout(chartInitTimer)
            chartInitTimer = setTimeout(() => {
                const el = chartRef.value
                if (!el || chartInstance) return
                chartInstance = echarts.init(el, null, { renderer: 'canvas' })
                updateChart()
                // 监听 chart 容器尺寸变化，大屏缩放时实时重绘
                const parent = el.parentElement
                if (parent && !chartResizeObserver) {
                    chartResizeObserver = new ResizeObserver(() => {
                        if (chartInstance) chartInstance.resize()
                    })
                    chartResizeObserver.observe(parent)
                }
            }, 300)
        }

        const updateChart = () => {
            if (!chartInstance) return
            chartInstance.setOption({
                backgroundColor: 'transparent',
                tooltip: { trigger: 'item', formatter: '{b}: {c} 个 ({d}%)' },
                series: [{
                    type: 'pie',
                    radius: ['50%', '72%'],
                    center: ['50%', '50%'],
                    avoidLabelOverlap: false,
                    label: { show: false },
                    emphasis: {
                        label: { show: true, fontSize: 14, fontWeight: 'bold', color: '#fff' }
                    },
                    animationDuration: 1200,
                    data: [
                        { value: statistics.occupied || 0, name: '已占用', itemStyle: { color: '#7c63ff' } },
                        { value: statistics.error || 0, name: '异常', itemStyle: { color: '#ff6b6b' } },
                        { value: statistics.available || 0, name: '空闲', itemStyle: { color: '#00ff88' } }
                    ]
                }],
            })
        }

        const handleChartResize = () => {
            if (chartInstance) chartInstance.resize()
        }

        // 获取仓库树形结构（两级）
        const fetchWarehouseTree = async () => {
            try {
                loading.value = true
                const data = await treeActualWarehouseTwoLevel()
                warehouseTree.value = data || []
            } catch (error) {
                console.error('获取仓库树形结构失败:', error)
            } finally {
                loading.value = false
            }
        }

        // 下拉选择变更
        const handleSelectChange = (value) => {
            if (!value) {
                selectedNodeId.value = ''
                selectedNodeName.value = ''
                warehouseList.value = []
                gridData.value = {}
                return
            }
            // 找到选中节点名称
            for (const parent of warehouseTree.value) {
                if (parent.children) {
                    const child = parent.children.find(c => c.actualWarehouseId === value)
                    if (child) {
                        selectedNodeName.value = child.actualWarehouseName
                        getWarehouseList(value)
                        return
                    }
                }
            }
        }

        // 获取库位列表
        const getWarehouseList = async (parentId) => {
            try {
                loading.value = true
                const data = await listActualWarehouse({ parentId })
                warehouseList.value = data || []
                buildGridData()
                measureGridContainer()

                // 确保环图已初始化（数据加载时 DOM 必定就绪）
                nextTick(() => {
                    if (!chartInstance) initChart()
                    else updateChart()
                })

                // 统计数据 — 与 getStatusClass 逻辑一一对应
                const total = warehouseList.value.length
                const occupied = warehouseList.value.filter(w =>
                    w.isEnabled === 0 && w.currentCoilNo
                ).length
                const available = warehouseList.value.filter(w =>
                    w.isEnabled === 1 && !w.currentCoilNo
                ).length
                const error = total - occupied - available
                const usable = occupied + available
                const utilization = usable > 0 ? Math.round((occupied / usable) * 100) : 0

                // 触发动画
                animateValue(animatedTotal.value, total, (val) => { animatedTotal.value = val })
                animateValue(animatedOccupied.value, occupied, (val) => { animatedOccupied.value = val })
                animateValue(animatedError.value, error, (val) => { animatedError.value = val })
                animateValue(animatedAvailable.value, available, (val) => { animatedAvailable.value = val })
                animateValue(animatedUtilization.value, utilization, (val) => { animatedUtilization.value = val })

                statistics.total = total
                statistics.occupied = occupied
                statistics.error = error
                statistics.available = available
                statistics.utilization = utilization
                updateChart()
            } catch (error) {
                console.error('获取库位列表失败:', error)
            } finally {
                loading.value = false
            }
        }

        // 获取状态文本
        const getStatusText = (row) => {
            if (row.isEnabled === 0 && row.currentCoilNo) {
                return '已占用'
            } else if (row.isEnabled === 1 && !row.currentCoilNo) {
                return '空闲'
            } else {
                return '异常'
            }
        }

        // 获取状态样式类
        const getStatusClass = (row) => {
            if (row.isEnabled === 0 && row.currentCoilNo) {
                return 'occupied'
            } else if (row.isEnabled === 1 && !row.currentCoilNo) {
                return 'available'
            } else {
                return 'error'
            }
        }

        // 解析库位编码：支持三级编码 F2B101-01-1 和四级编码 F2B3-X38-2
        const parseWarehouseCode = (code) => {
            if (!code) return null
            // 先尝试四级编码 F2B3-X38-2
            const reg4 = /^([A-Za-z0-9]{3})([^-]+)-X(\d{2})-(\d+)$/
            const match4 = code.match(reg4)
            if (match4) {
                return {
                    level: 4,
                    column: Number(match4[2]),
                    row: Number(match4[3]),
                    layer: match4[4]
                }
            }
            // 再尝试三级编码 F2B101-01-1
            const reg3 = /^([A-Za-z0-9]{3})([^-]+)-(\d{2})-(\d+)$/
            const match3 = code.match(reg3)
            if (match3) {
                return {
                    level: 3,
                    column: Number(match3[2]),
                    row: Number(match3[3]),
                    layer: match3[4]
                }
            }
            return null
        }

        // 构建网格数据结构：按列分组 → 按层分行
        const buildGridData = () => {
            const columns = {}
            warehouseList.value.forEach(wh => {
                const parsed = parseWarehouseCode(wh.actualWarehouseCode)
                if (!parsed) return
                wh.parsedInfo = parsed
                if (!columns[parsed.column]) {
                    columns[parsed.column] = { layer1: [], layer2: [] }
                }
                if (String(parsed.layer) === '1') {
                    columns[parsed.column].layer1.push(wh)
                } else {
                    columns[parsed.column].layer2.push(wh)
                }
            })
            Object.values(columns).forEach(col => {
                col.layer1.sort((a, b) => a.parsedInfo.row - b.parsedInfo.row)
                col.layer2.sort((a, b) => a.parsedInfo.row - b.parsedInfo.row)
            })
            gridData.value = columns
        }

        // 显示库位详情浮层（基于视口坐标，tooltip 使用 position: fixed）
        const showDetail = (wh, event) => {
            detailWarehouse.value = wh
            const cellRect = event.currentTarget.getBoundingClientRect()
            const vw = window.innerWidth
            const vh = window.innerHeight
            let left = cellRect.right + 8
            let top = cellRect.top - 10
            const tw = 260
            const th = 180
            const pad = 10
            if (left + tw > vw - pad) {
                left = cellRect.left - tw - 8
            }
            if (top + th > vh - pad) {
                top = vh - th - pad
            }
            tooltipPos.x = Math.round(Math.max(pad, left))
            tooltipPos.y = Math.round(Math.max(pad, top))
        }
        const closeTooltip = () => {
            detailWarehouse.value = null
        }
        const handleDocumentMousedown = (e) => {
            if (!detailWarehouse.value) return
            const el = tooltipRef.value
            if (el && !el.contains(e.target)) {
                closeTooltip()
            }
        }

        // 全屏切换 → 刷新 popper 位置
        const handleFullscreenChange = () => {
            setTimeout(() => window.dispatchEvent(new Event('resize')), 100)
        }

        // 初始化
        onMounted(() => {
            updateScale()
            window.addEventListener('resize', updateScale)
            updateTime()
            timeInterval = setInterval(updateTime, 1000)
            // 加载仓库树形结构
            fetchWarehouseTree()
            // 初始化环图
            initChart()
            document.addEventListener('mousedown', handleDocumentMousedown)
            window.addEventListener('resize', handleChartResize)
            document.addEventListener('fullscreenchange', handleFullscreenChange)
            document.addEventListener('webkitfullscreenchange', handleFullscreenChange)
        })

        onBeforeUnmount(() => {
            stopWatchList()
            if (timeInterval) clearInterval(timeInterval)
            if (resizeObserver) resizeObserver.disconnect()
            if (chartResizeObserver) chartResizeObserver.disconnect()
            stopAutoScroll()
            resetScroll()
            window.removeEventListener('resize', updateScale)
            document.removeEventListener('mousemove', onGridDragMove)
            document.removeEventListener('mouseup', onGridDragEnd)
            if (chartInitTimer) clearTimeout(chartInitTimer)
            document.removeEventListener('mousedown', handleDocumentMousedown)
            window.removeEventListener('resize', handleChartResize)
            document.removeEventListener('fullscreenchange', handleFullscreenChange)
            document.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
            if (chartInstance) {
                chartInstance.dispose()
                chartInstance = null
            }
        })

        return {
            scaleWrapperStyle,
            currentDate,
            warehouseTree,
            warehouseOptions,
            warehouseList,
            selectedNodeName,
            statistics,
            splitStats,
            columnStats,
            animatedTotal,
            animatedOccupied,
            animatedError,
            animatedAvailable,
            animatedUtilization,
            statusBarData,
            getParticleStyle,
            getEqBarStyle,
            handleSelectChange,
            getStatusText,
            getStatusClass,
            // 网格
            gridData,
            sortedColumnKeys,
            detailWarehouse,
            showDetail,
            tooltipRef,
            tooltipStyle,
            cellStyle,
            cellMarginOffset,
            columnWidth,
            isGridCentered,
            onGridMouseEnter,
            onGridMouseLeave,
            onGridDragStart,
            chartRef,
            gridContainerRef,
            gridInnerRef
        };
    }
};
</script>

<style scoped>
.warehouse-screen {
    width: 100vw;
    height: 100vh;
    background: linear-gradient(135deg, #050a15 0%, #0a1428 50%, #0d1b34 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
}

.scale-wrapper {
    width: 1920px;
    height: 1080px;
    flex-shrink: 0;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transform-origin: center center;
}

/* 动态背景 */
.dynamic-bg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    overflow: hidden;
    z-index: 0;
}

/* 网格背景 */
.grid-container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

.grid-lines {
    position: absolute;
    width: 100%;
    height: 100%;
    opacity: 0.15;
}

.grid-lines.horizontal {
    background: repeating-linear-gradient(
        0deg,
        transparent,
        transparent 50px,
        rgba(0, 212, 255, 0.1) 50px,
        rgba(0, 212, 255, 0.1) 51px
    );
    animation: gridMoveHorizontal 20s linear infinite;
}

.grid-lines.vertical {
    background: repeating-linear-gradient(
        90deg,
        transparent,
        transparent 50px,
        rgba(0, 212, 255, 0.1) 50px,
        rgba(0, 212, 255, 0.1) 51px
    );
    animation: gridMoveVertical 20s linear infinite;
}

@keyframes gridMoveHorizontal {
    0% { transform: translateY(0); }
    100% { transform: translateY(50px); }
}

@keyframes gridMoveVertical {
    0% { transform: translateX(0); }
    100% { transform: translateX(50px); }
}

/* 粒子效果 */
.particles-container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

.particle {
    position: absolute;
    width: 6px;
    height: 6px;
    background: radial-gradient(circle, #00d4ff 0%, rgba(0, 212, 255, 0.5) 50%, transparent 100%);
    border-radius: 50%;
    animation: particleFloat 10s ease-in-out infinite;
    box-shadow:
        0 0 10px #00d4ff,
        0 0 20px rgba(0, 212, 255, 0.6),
        0 0 40px rgba(0, 212, 255, 0.3);
}

@keyframes particleFloat {
    0%, 100% {
        top: 100%;
        transform: translateX(0) scale(0.5);
        opacity: 0;
    }
    10% {
        top: 80%;
        transform: translateX(0) scale(1);
        opacity: 1;
    }
    50% {
        top: 50%;
        transform: translateX(30px) scale(1.2);
    }
    90% {
        opacity: 0.8;
        top: 10%;
        transform: translateX(-20px) scale(0.8);
    }
}

/* 扫描线 */
.scan-line {
    position: absolute;
    top: -10%;
    left: 0;
    width: 100%;
    height: 100px;
    background: linear-gradient(
        to bottom,
        transparent 0%,
        rgba(0, 212, 255, 0.1) 50%,
        transparent 100%
    );
    animation: scanDown 8s linear infinite;
}

@keyframes scanDown {
    0% { top: -10%; }
    100% { top: 110%; }
}

/* 光晕效果 */
.glow-globe {
    position: absolute;
    border-radius: 50%;
    filter: blur(100px);
    opacity: 0.3;
    animation: globePulse 8s ease-in-out infinite;
}

.glow-globe.globe-1 {
    width: 400px;
    height: 400px;
    background: radial-gradient(circle, rgba(0, 212, 255, 0.4) 0%, transparent 70%);
    top: 10%;
    right: 10%;
    animation-delay: 0s;
}

.glow-globe.globe-2 {
    width: 300px;
    height: 300px;
    background: radial-gradient(circle, rgba(124, 99, 255, 0.3) 0%, transparent 70%);
    bottom: 20%;
    left: 5%;
    animation-delay: 2s;
}

.glow-globe.globe-3 {
    width: 350px;
    height: 350px;
    background: radial-gradient(circle, rgba(0, 255, 136, 0.2) 0%, transparent 70%);
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    animation-delay: 4s;
}

@keyframes globePulse {
    0%, 100% {
        transform: scale(1);
        opacity: 0.3;
    }
    50% {
        transform: scale(1.3);
        opacity: 0.5;
    }
}

.bg-border {
    position: absolute;
    top: 10px;
    left: 10px;
    right: 10px;
    bottom: 10px;
    pointer-events: none;
    z-index: 100;
}

/* 头部 */
.screen-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    position: relative;
    z-index: 10;
}

.title-border {
    flex: 1;
}

.title-box {
    padding: 15px 30px;
    display: flex;
    align-items: center;
    gap: 15px;
    background: rgba(10, 20, 40, 0.8);
}

.title-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
}

.diamond {
    width: 14px;
    height: 14px;
    background: #00d4ff;
    transform: rotate(45deg);
    border-radius: 2px;
    box-shadow: 0 0 8px #00d4ff, 0 0 20px rgba(0, 212, 255, 0.4);
    animation: diamondPulse 2s ease-in-out infinite;
}

@keyframes diamondPulse {
    0%, 100% {
        box-shadow: 0 0 8px #00d4ff, 0 0 20px rgba(0, 212, 255, 0.4);
        transform: rotate(45deg) scale(1);
    }
    50% {
        box-shadow: 0 0 16px #00d4ff, 0 0 40px rgba(0, 212, 255, 0.6), 0 0 60px rgba(0, 212, 255, 0.3);
        transform: rotate(45deg) scale(1.1);
    }
}

.screen-title {
    font-size: 32px;
    color: #00d4ff;
    margin: 0;
    text-shadow: 0 0 20px rgba(0, 212, 255, 0.8), 0 0 40px rgba(0, 212, 255, 0.4);
    letter-spacing: 8px;
}

.subtitle {
    font-size: 14px;
    color: #6a8cb5;
    margin-left: 10px;
}

.date-border {
    padding: 5px;
}

.date-box {
    padding: 10px 20px;
    background: rgba(10, 20, 40, 0.8);
}

/* 时钟 */
.clock-text {
    margin-left: auto;
    font-size: 14px;
    color: #00d4ff;
    font-family: 'Courier New', monospace;
    letter-spacing: 1px;
}

/* 主内容 */
.screen-body {
    display: flex;
    gap: 20px;
    flex: 1;
    min-height: 0;
    position: relative;
    z-index: 10;
    overflow: visible;
}

.sidebar {
    width: 300px;
    display: flex;
    flex-direction: column;
    gap: 15px;
    min-height: 0;
    font-size: 14px;
}

.tree-panel {
    flex-shrink: 0;
    position: relative;
    background: rgba(10, 20, 40, 0.85);
    border-radius: 4px;
}

.tree-panel .header-compact {
    padding: 8px 10px 0;
    border-bottom: none;
    background: none;
}

.tree-panel .header-compact .header-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
}

.header-icon-svg {
    display: block;
}

.radar-beam {
    transform-origin: 12px 12px;
    animation: radarSpin 3s linear infinite;
}

@keyframes radarSpin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

.tree-panel .header-compact .panel-title {
    font-size: 15px;
}

.panel {
    background: rgba(10, 20, 40, 0.85);
    border-radius: 6px;
    overflow: hidden;
    height: 100%;
    display: flex;
    flex-direction: column;
}

.panel-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 15px 20px;
    background: linear-gradient(90deg, rgba(0, 212, 255, 0.15) 0%, transparent 100%);
    border-bottom: 1px solid rgba(0, 212, 255, 0.2);
}

.header-icon {
    color: #00d4ff;
    font-size: 18px;
}

.panel-title {
    font-size: 16px;
    color: #00d4ff;
    font-weight: 600;
    text-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
}

.panel-count {
    font-size: 12px;
    color: #6a8cb5;
}

.panel-body {
    flex: 1;
    padding: 15px 10px;
    overflow: hidden;
    min-height: 0;
}

/* 下拉选择器样式 */
.select-body {
    padding: 2px 10px 10px;
}

.warehouse-select {
    width: 100%;
}

/* 统计卡片包装 */
.stats-wrapper {
    flex: 2;
    min-height: 0;
}

/* 侧边栏统计 - 环图 */
.stats-sidebar {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 6px 8px 0;
    flex: 1;
    background: rgba(10, 20, 40, 0.85);
    border-radius: 4px;
}

.total-row {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-bottom: 2px;
}

.total-num {
    font-size: 32px;
    font-weight: bold;
    color: #00d4ff;
    text-shadow: 0 0 20px rgba(0, 212, 255, 0.6);
    line-height: 1.1;
}

.total-lbl {
    font-size: 11px;
    color: #6a8cb5;
    margin-top: 1px;
}

.ring-chart {
    width: 100%;
    flex: 1;
    min-height: 80px;
    position: relative;
    z-index: 1;
}

/* 环图外圈旋转流光 */
.ring-chart-wrapper {
    position: relative;
    width: 100%;
    flex: 1;
    min-height: 80px;
}

.ring-chart-wrapper::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44%;
    height: 44%;
    transform: translate(-50%, -50%) rotate(0deg);
    border-radius: 50%;
    background: conic-gradient(
        transparent 0deg,
        transparent 310deg,
        rgba(0, 212, 255, 0.08) 330deg,
        rgba(0, 212, 255, 0.5) 345deg,
        rgba(0, 212, 255, 0.7) 350deg,
        rgba(0, 212, 255, 0.15) 355deg,
        transparent 360deg
    );
    animation: radarSweep 2.5s linear infinite;
    z-index: 0;
    pointer-events: none;
}

@keyframes radarSweep {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to { transform: translate(-50%, -50%) rotate(360deg); }
}

.ring-chart-wrapper::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: conic-gradient(
        transparent 0deg,
        transparent 300deg,
        rgba(0, 212, 255, 0.15) 315deg,
        rgba(0, 212, 255, 0.8) 335deg,
        rgba(0, 212, 255, 0.9) 345deg,
        rgba(0, 212, 255, 0.4) 355deg,
        transparent 360deg
    );
    -webkit-mask: radial-gradient(circle, transparent 44%, #fff 47%, #fff 70%, transparent 72%);
    mask: radial-gradient(circle, transparent 44%, #fff 47%, #fff 70%, transparent 72%);
    animation: ringGlowSpin 2s linear infinite;
    pointer-events: none;
    z-index: 2;
}

@keyframes ringGlowSpin {
    100% { transform: rotate(360deg); }
}

.util-text {
    text-align: center;
    padding: 4px 0 2px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
}

.util-number {
    font-size: 22px;
    font-weight: bold;
    color: #00d4ff;
    font-family: Arial, sans-serif;
    line-height: 1.2;
    text-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
}

.util-label {
    font-size: 12px;
    color: #6a8cb5;
    line-height: 1.2;
}

.chart-legend {
    display: flex;
    flex-direction: column;
    gap: 4px;
    width: 100%;
    padding: 0 6px;
    margin-top: 2px;
}

.chart-legend .legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: #a0c4e8;
}

.chart-legend .legend-val {
    margin-left: auto;
    font-weight: 600;
    color: #fff;
}

.chart-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.chart-dot.occupied { background: #7c63ff; }
.chart-dot.error { background: #ff6b6b; }
.chart-dot.available { background: #00ff88; }

/* 状态占比条 */
.status-bar {
    display: flex;
    width: 100%;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    margin: 6px 8px 0;
    background: rgba(255, 255, 255, 0.05);
    gap: 2px;
}

.status-bar-fill {
    height: 100%;
    border-radius: 4px;
    transition: width 0.6s ease;
    min-width: 0;
}

.status-bar-labels {
    display: flex;
    width: 100%;
    justify-content: space-around;
    padding: 3px 8px 0;
}

.status-bar-label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 10px;
    color: #8aa8c8;
}

.sbl-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
}

.sbl-text {
    white-space: nowrap;
}

/* 数据呼吸指示器 */
.breath-indicators {
    display: flex;
    justify-content: center;
    gap: 20px;
    padding: 6px 0 0;
    width: 100%;
}

.breath-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    animation: breathPulse 2s ease-in-out infinite;
}

@keyframes breathPulse {
    0%, 100% { transform: scale(0.5); opacity: 0.3; }
    50% { transform: scale(1.4); opacity: 1; }
}

/* 微频谱条 */
.micro-eq {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 3px;
    padding: 6px 10px 0;
    width: 100%;
    height: 18px;
}

.micro-eq-bar {
    width: 3px;
    min-height: 2px;
    border-radius: 1px;
    background: linear-gradient(to top, transparent, rgba(0, 212, 255, 0.3), #00d4ff);
    animation: microEqBounce 1s ease-in-out infinite alternate;
}

@keyframes microEqBounce {
    0% { height: 2px; opacity: 0.1; }
    50% { height: 14px; opacity: 0.7; }
    100% { height: 3px; opacity: 0.15; }
}

/* 红色扫描线 */
.scan-line-red {
    position: relative;
    height: 20px;
    width: 100%;
    overflow: hidden;
}

.scan-line-red .scan-beam {
    position: absolute;
    top: 0;
    left: -10%;
    width: 8px;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255, 50, 50, 0.8), transparent);
    box-shadow: 0 0 12px rgba(255, 50, 50, 0.6), 0 0 30px rgba(255, 50, 50, 0.2);
    border-radius: 4px;
    animation: scanRedBeam 3s ease-in-out infinite;
}

@keyframes scanRedBeam {
    0% { left: -10%; opacity: 0; }
    10% { opacity: 1; }
    50% { left: 100%; opacity: 1; }
    60% { opacity: 0.3; }
    100% { left: 100%; opacity: 0; }
}

/* 动态柱状图 */
.mini-chart {
    width: 100%;
    padding: 4px 12px 0;
    margin-top: auto;
    display: flex;
    flex-direction: column;
}

.mini-chart-bars {
    display: flex;
    align-items: flex-end;
    justify-content: space-around;
    height: 36px;
    gap: 2px;
}

.mini-bar {
    width: 8px;
    min-height: 2px;
    border-radius: 2px 2px 0 0;
    background: linear-gradient(to top, rgba(0, 212, 255, 0.15), rgba(0, 212, 255, 0.6));
    animation: miniBarPulse 2s ease-in-out infinite alternate;
}

.mini-chart-axis {
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(0, 212, 255, 0.2), transparent);
    margin-top: 2px;
}

@keyframes miniBarPulse {
    0% { opacity: 0.3; transform: scaleY(0.4); transform-origin: bottom; }
    50% { opacity: 0.9; transform: scaleY(1); transform-origin: bottom; }
    100% { opacity: 0.4; transform: scaleY(0.6); transform-origin: bottom; }
}

/* 中间主内容区 */
.main-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    height: 100%;
    min-width: 0;
}

.main-panel {
    flex: 1;
    height: 100%;
    border: none;
    box-shadow: none;
}

.table-container {
    padding: 15px;
    height: calc(100% - 60px);
    overflow: hidden;
    background: rgba(10, 20, 40, 0.85);
    border-radius: 6px;
    display: flex;
    flex-direction: column;
}

.empty-tip {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 80px 20px;
    color: #6a8cb5;
    background: rgba(10, 20, 40, 0.6);
    border-radius: 8px;
}

.empty-icon {
    font-size: 64px;
    margin-bottom: 20px;
    opacity: 0.5;
}

.empty-text {
    font-size: 16px;
    color: #8892a6;
}

/* 图例 */
.grid-legend {
    display: flex;
    gap: 24px;
    margin-bottom: 12px;
    padding: 8px 16px;
    background: rgba(0, 212, 255, 0.05);
    border-radius: 4px;
    border: 1px solid rgba(0, 212, 255, 0.1);
}

.legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: #a0c4e8;
}

.legend-dot {
    width: 12px;
    height: 12px;
    border-radius: 2px;
    border: 1px solid rgba(255, 255, 255, 0.1);
}

.legend-dot.available { background: #00ff88; }
.legend-dot.occupied { background: #7c63ff; }
.legend-dot.error { background: #ff6b6b; }
.legend-dot.has-coil-icon {
    background: rgba(0, 212, 255, 0.5);
    border-color: #00d4ff;
}

/* 库位网格 */
.warehouse-grid {
    overflow: hidden;
    flex: 1;
    min-height: 0;
    position: relative;
}

/* 内层内容容器：flex 布局 + transform 驱动轮播 */
.grid-inner {
    display: flex;
    gap: 8px;
    padding-bottom: 8px;
    will-change: transform;
    cursor: grab;
}

.grid-inner.grid-centered {
    justify-content: center;
}

.grid-column {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
}

.column-header {
    font-size: 12px;
    color: #6a8cb5;
    text-align: center;
    padding: 4px 0;
    margin-bottom: 4px;
    border-bottom: 1px solid rgba(0, 212, 255, 0.2);
    width: 100%;
    white-space: nowrap;
}

.grid-cell {
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.2s ease;
    position: relative;
    border: 1px solid rgba(255, 255, 255, 0.1);
    flex-shrink: 0;
}

.grid-cell.available {
    background: rgba(0, 255, 136, 0.15);
    border-color: rgba(0, 255, 136, 0.3);
    box-shadow: 0 0 8px rgba(0, 255, 136, 0.1);
}

.grid-cell.occupied {
    background: rgba(124, 99, 255, 0.2);
    border-color: rgba(124, 99, 255, 0.3);
    box-shadow: 0 0 8px rgba(124, 99, 255, 0.15);
}

.grid-cell.error {
    background: rgba(255, 107, 107, 0.15);
    border-color: rgba(255, 107, 107, 0.3);
    box-shadow: 0 0 8px rgba(255, 107, 107, 0.1);
}

.grid-cell.has-coil {
    border-width: 2px;
}

.grid-cell.has-coil.available,
.grid-cell.has-coil.occupied {
    border-color: #00d4ff;
    box-shadow: 0 0 12px rgba(0, 212, 255, 0.3);
}

.grid-cell:hover {
    transform: scale(1.15);
    z-index: 2;
}

.grid-cell:hover.available {
    box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
}

.grid-cell:hover.occupied {
    box-shadow: 0 0 20px rgba(124, 99, 255, 0.5);
}

.grid-cell:hover.error {
    box-shadow: 0 0 20px rgba(255, 107, 107, 0.5);
}

.cell-code {
    font-weight: 600;
    color: #fff;
    text-shadow: 0 0 6px rgba(255, 255, 255, 0.3);
    line-height: 1.2;
    text-align: center;
    word-break: break-all;
}

.cell-coil {
    font-size: 0.75em;
    color: #4fc3f7;
    line-height: 1.2;
    text-align: center;
    word-break: break-all;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    max-width: 100%;
    padding: 0 2px;
}

.column-pair {
    display: flex;
    gap: 6px;
    width: 100%;
    flex: 1;
}

.layer-cells {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1;
    min-width: 0;
}

.layer1-cells .grid-cell {
    background: rgba(255, 167, 38, 0.06);
    border-color: rgba(255, 167, 38, 0.2);
}

.layer1-cells .grid-cell.occupied {
    background: rgba(124, 99, 255, 0.2);
    border-color: rgba(124, 99, 255, 0.3);
}

.layer1-cells .grid-cell.available {
    background: rgba(255, 167, 38, 0.12);
    border-color: rgba(0, 255, 136, 0.3);
}

.layer1-cells .grid-cell.error {
    background: rgba(255, 107, 107, 0.2);
    border-color: rgba(255, 107, 107, 0.3);
}

.layer2-cells .grid-cell {
    background: rgba(0, 212, 255, 0.06);
    border-color: rgba(0, 212, 255, 0.2);
}

.layer2-cells .grid-cell.occupied {
    background: rgba(124, 99, 255, 0.2);
    border-color: rgba(124, 99, 255, 0.3);
}

.layer2-cells .grid-cell.available {
    background: rgba(0, 212, 255, 0.1);
    border-color: rgba(0, 255, 136, 0.3);
}

.layer2-cells .grid-cell.error {
    background: rgba(255, 107, 107, 0.2);
    border-color: rgba(255, 107, 107, 0.3);
}

/* 拆分状态卡片 */
.split-wrapper {
    flex: 1;
    min-height: 0;
}

.split-stats {
    padding: 8px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    background: rgba(10, 20, 40, 0.85);
    border-radius: 4px;
}

.split-header {
    display: flex;
    align-items: center;
    gap: 6px;
}

.split-title-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    color: #00d4ff;
}

.flow-icon-svg {
    display: block;
}

.split-title-text {
    font-size: 13px;
    color: #00d4ff;
    font-weight: 600;
}

.split-body {
    display: flex;
    align-items: center;
    justify-content: space-around;
    gap: 8px;
}

.split-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
}

.split-num {
    font-size: 24px;
    font-weight: bold;
    line-height: 1.2;
}

.split-num.unsplit {
    color: #ffa726;
    text-shadow: 0 0 10px rgba(255, 167, 38, 0.5);
}

.split-num.split {
    color: #42a5f5;
    text-shadow: 0 0 10px rgba(66, 165, 245, 0.5);
}

.split-lbl {
    font-size: 11px;
    color: #6a8cb5;
}

.split-pct {
    font-size: 10px;
    color: #6a8cb5;
}

.split-vr {
    width: 1px;
    height: 40px;
    flex-shrink: 0;
    background: linear-gradient(to bottom, transparent, rgba(0, 212, 255, 0.3), transparent);
}

.split-bar {
    display: flex;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    margin: 8px 12px 0;
    background: rgba(255, 255, 255, 0.05);
}

.split-bar-fill {
    height: 100%;
    transition: width 0.6s ease;
    min-width: 0;
}

.split-bar-fill.unsplit {
    background: linear-gradient(90deg, #ffa726, #ffb74d);
}

.split-bar-fill.split {
    background: linear-gradient(90deg, #42a5f5, #64b5f6);
}

.split-bar-labels {
    display: flex;
    justify-content: space-around;
    padding: 3px 12px 0;
    font-size: 10px;
    color: #8aa8c8;
}

.sbl-item {
    display: flex;
    align-items: center;
    gap: 4px;
}

.sbl-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
}

.unsplit-dot { background: #ffa726; }
.split-dot { background: #42a5f5; }

/* 层列信息卡片 */
.layer-wrapper {
    flex: 1;
    min-height: 0;
}

.layer-stats {
    background: rgba(10, 20, 40, 0.85);
    border-radius: 4px;
}

.layer-header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 12px 0;
}

.layer-title-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    color: #00d4ff;
}

.layer-title-text {
    font-size: 13px;
    color: #00d4ff;
    font-weight: 600;
}

.layer-body {
    padding: 6px 12px 10px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.layer-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.layer-lbl {
    font-size: 13px;
    color: #6a8cb5;
}

.layer-val {
    font-size: 12px;
    color: #fff;
    font-weight: 500;
}

/* ===== 库位详情浮层（毛玻璃 tooltip） ===== */
.warehouse-tooltip {
    position: fixed;
    z-index: 2000;
    min-width: 220px;
    background: rgba(8, 18, 38, 0.88);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(0, 212, 255, 0.25);
    border-radius: 6px;
    padding: 12px 14px;
    box-shadow:
        0 8px 32px rgba(0, 0, 0, 0.6),
        0 0 20px rgba(0, 212, 255, 0.08);
    animation: tooltipFadeIn 0.2s ease;
    pointer-events: auto;
}

@keyframes tooltipFadeIn {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}

.warehouse-tooltip .tooltip-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

.warehouse-tooltip .tooltip-code {
    font-size: 15px;
    font-weight: 700;
    color: #00d4ff;
    text-shadow: 0 0 8px rgba(0, 212, 255, 0.4);
    letter-spacing: 1px;
}

.warehouse-tooltip .tooltip-status {
    font-size: 11px;
    padding: 1px 8px;
    border-radius: 3px;
    line-height: 1.6;
}

.warehouse-tooltip .tooltip-status.occupied {
    background: rgba(124, 99, 255, 0.2);
    color: #7c63ff;
    border: 1px solid rgba(124, 99, 255, 0.3);
}

.warehouse-tooltip .tooltip-status.available {
    background: rgba(0, 255, 136, 0.2);
    color: #00ff88;
    border: 1px solid rgba(0, 255, 136, 0.3);
}

.warehouse-tooltip .tooltip-status.error {
    background: rgba(255, 107, 107, 0.2);
    color: #ff6b6b;
    border: 1px solid rgba(255, 107, 107, 0.3);
}

.warehouse-tooltip .tooltip-divider {
    height: 1px;
    background: linear-gradient(90deg, rgba(0, 212, 255, 0.3), transparent);
    margin-bottom: 10px;
}

.warehouse-tooltip .tooltip-body {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.warehouse-tooltip .tooltip-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.warehouse-tooltip .tooltip-label {
    font-size: 12px;
    color: #6a8cb5;
}

.warehouse-tooltip .tooltip-value {
    font-size: 12px;
    color: #e0e8f0;
    font-weight: 500;
}

.warehouse-tooltip .tooltip-value.highlight {
    color: #00d4ff;
}

.warehouse-tooltip .tooltip-value.dim {
    color: #4a5568;
}

/* ===== 底部跃动方块 ===== */
.eq-bar-container {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 32px;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 3px;
    padding: 0 20px;
    box-sizing: border-box;
    z-index: 20;
    pointer-events: none;
}

.eq-bar {
    width: 3px;
    min-height: 2px;
    border-radius: 2px;
    background: linear-gradient(to top, rgba(0, 212, 255, 0.1), #00d4ff);
    box-shadow: 0 0 6px rgba(0, 212, 255, 0.5);
    animation: eqBounce 1s ease-in-out infinite alternate;
}

@keyframes eqBounce {
    0% { height: 3px; opacity: 0.2; }
    20% { height: 22px; opacity: 0.9; }
    40% { height: 8px; opacity: 0.4; }
    60% { height: 30px; opacity: 1; }
    80% { height: 14px; opacity: 0.6; }
    100% { height: 2px; opacity: 0.15; }
}

</style>

<!-- 全局样式：弹窗 + 下拉框 teleport 到 body，不受 scoped 限制 -->
<style>
/* 仓库结构下拉框深色主题 */
.warehouse-select-popper {
    background: rgba(10, 20, 40, 0.95) !important;
    border: 1px solid rgba(0, 212, 255, 0.3) !important;
    font-size: 14px;
}

.warehouse-select-popper .el-select-dropdown {
    background: transparent !important;
}

.warehouse-select-popper .el-scrollbar {
    background: transparent !important;
}

.warehouse-select-popper .el-select-dropdown__item {
    background: transparent !important;
    color: #a0c4e8 !important;
    font-size: 14px;
}

.warehouse-select-popper .el-select-dropdown__item.hover,
.warehouse-select-popper .el-select-dropdown__item:hover {
    background: rgba(0, 212, 255, 0.15) !important;
    color: #00d4ff !important;
}

.warehouse-select-popper .el-select-dropdown__item.selected {
    background: rgba(0, 212, 255, 0.1) !important;
    color: #00d4ff !important;
    font-weight: 600;
}

.warehouse-select-popper .el-select-group__title {
    background: transparent !important;
    color: #6a8cb5 !important;
    font-size: 12px;
    padding-left: 15px;
}

.warehouse-select-popper .el-select-dropdown__wrap {
    background: transparent !important;
}

.warehouse-select-popper .el-select-dropdown__list {
    background: transparent !important;
}

.warehouse-select .el-select__wrapper {
    background-color: rgba(10, 20, 40, 0.95) !important;
    box-shadow: 0 0 0 1px rgba(0, 212, 255, 0.3) inset !important;
    border-radius: 4px;
    min-height: 40px;
    padding: 0 12px;
}

.warehouse-select .el-select__wrapper:hover {
    box-shadow: 0 0 0 1px rgba(0, 212, 255, 0.5) inset !important;
}

.warehouse-select .el-select__wrapper.is-focus {
    box-shadow: 0 0 0 1px #00d4ff inset !important;
}

.warehouse-select .el-select__input {
    color: #00d4ff !important;
    font-size: 14px;
}

.warehouse-select .el-select__input::placeholder {
    color: #6a8cb5 !important;
}

.warehouse-select .el-select__caret {
    color: #00d4ff !important;
    font-size: 14px;
}

</style>
