<template>
  <div v-loading="loading" class="home-dashboard">
    <!-- Header -->
    <section class="db-header">
      <div class="db-header-left">
        <div class="db-badge-row">
          <span class="db-badge">
            <span class="pulse-dot"></span>
            实时系统运行概览
          </span>
        </div>
        <h1>能源设备运行驾驶舱</h1>
        <p class="subtitle">设备台账统计 · 实时状态监控 · 告警规则联动</p>
      </div>
      <div class="db-header-right">
        <div class="db-clock-pill">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="clock-icon"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
          <span class="clock-time">{{ currentTime }}</span>
        </div>
        <el-button type="primary" class="refresh-btn" @click="loadAll(true)">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="refresh-icon"><path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/></svg>
          刷新面板
        </el-button>
      </div>
    </section>

    <!-- KPI Cards -->
    <div class="kpi-row">
      <div
        v-for="card in kpiCards"
        :key="card.label"
        class="kpi-item"
        :style="{ '--accent': card.color, '--accent-grad': card.grad }"
      >
        <div class="kpi-icon" :style="{ background: card.grad }">
          <span v-html="card.icon"></span>
        </div>
        <div class="kpi-body">
          <span class="kpi-num">
            {{ card.value }}<small v-if="card.unit">{{ card.unit }}</small>
          </span>
          <span class="kpi-title">{{ card.label }}</span>
        </div>
      </div>
    </div>

    <!-- Charts Row -->
    <div class="chart-row">
      <div class="chart-panel">
        <div class="panel-header-simple">
          <div class="panel-title-with-bar">设备类型分布</div>
          <div class="chart-sub">按监测类型统计设备台数</div>
        </div>
        <div class="chart-container">
          <Chart class="chart-box" :chart-option="typeBarOption" />
        </div>
      </div>
      <div class="chart-panel">
        <div class="panel-header-simple">
          <div class="panel-title-with-bar2">告警处理状态</div>
          <div class="chart-sub">已处理 / 待处理占比及处理率</div>
        </div>
        <div class="chart-container">
          <Chart class="chart-box" :chart-option="alarmPieOption" />
        </div>
      </div>
    </div>

    <!-- Interactive Dashboard Bottom Row
    <div class="bottom-row">
      <div class="bottom-card alarm-card">
        <div class="panel-header">
          <div class="panel-title-group">
            <span class="panel-icon bg-grad-alarm-icon">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01"/></svg>
            </span>
            <div class="panel-titles">
              <h3>最近异常告警日志</h3>
              <p>实时推送最新的设备越界告警记录</p>
            </div>
          </div>
          <el-button link type="primary" class="more-link" @click="goToPage('/ems/record/recordAlarmData')">
            查看全部
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              class="arrow-icon"
            >
              <polyline points="9 18 15 12 9 6" />
            </svg>
          </el-button>
        </div>
        <div class="alarm-table-wrapper">
          <el-table
            :data="alarmList"
            stripe
            style="width: 100%"
            size="small"
            :header-cell-style="{ background: '#f8fafc', color: '#475569', fontWeight: 600 }"
          >
            <el-table-column label="处理状态" width="90" align="center">
              <template #default="{ row }">
                <el-tag :type="row.alarmStatus === 1 ? 'success' : 'danger'" effect="light" size="small" class="status-tag">
                  {{ row.alarmStatus === 1 ? '已处理' : '待处理' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="级别" width="80" align="center">
              <template #default="{ row }">
                <el-tag :type="getAlarmLevelType(row.alarmLevel)" effect="dark" size="small" class="level-tag">
                  {{ getAlarmLevelLabel(row.alarmLevel) }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="告警详情" min-width="180" show-overflow-tooltip>
              <template #default="{ row }">
                <div class="alarm-title-text">{{ row.alarmTitle || row.alarmContent }}</div>
              </template>
            </el-table-column>
            <el-table-column prop="monitorName" label="监测点" width="120" show-overflow-tooltip />
            <el-table-column label="实际值 / 阈值" width="130" align="center">
              <template #default="{ row }">
                <span class="actual-val">{{ row.actualValue }}</span>
                <span class="val-sep">/</span>
                <span class="threshold-val">{{ row.thresholdValue }}</span>
              </template>
            </el-table-column>
            <el-table-column label="发生时间" width="140" align="center">
              <template #default="{ row }">
                <span class="time-text">{{ formatTime(row.collectTime) }}</span>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </div>

      <div class="bottom-card shortcut-card-panel">
        <div class="panel-header">
          <div class="panel-title-group">
            <span class="panel-icon bg-grad-shortcut-icon">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></svg>
            </span>
            <div class="panel-titles">
              <h3>快捷功能通道</h3>
              <p>高频业务功能一键直达</p>
            </div>
          </div>
        </div>
        <div class="shortcut-grid">
          <div v-for="sc in shortcuts" :key="sc.title" class="shortcut-item" @click="goToPage(sc.path)">
            <div class="sc-icon-circle" :style="{ background: sc.color }">
              <span class="sc-svg" v-html="sc.icon"></span>
            </div>
            <div class="sc-content">
              <h4>{{ sc.title }}</h4>
              <p>{{ sc.desc }}</p>
            </div>
            <div class="sc-arrow">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
            </div>
          </div>
        </div>
      </div>
    </div>
    -->
  </div>
</template>

<script setup name="Index" lang="ts">
import Chart from '@/components/Charts/Chart.vue';
import type { EChartsOption } from 'echarts';
import { getMonitorInfoTree } from '@/api/ems/base/baseMonitorInfo';
import { getRecordAlarmDataSummary, listRecordAlarmData } from '@/api/ems/record/recordAlarmData';

// ---- type config ----
const TYPE_MAP: Record<number, string> = { 5: '温度', 6: '温湿度', 7: '噪声', 8: '照度', 9: '气体浓度', 10: '振动' };
const TYPE_COLOR: Record<number, string> = { 5: '#3b82f6', 6: '#10b981', 7: '#f59e0b', 8: '#8b5cf6', 9: '#ef4444', 10: '#6366f1' };

// ---- module cache ----
let cachedTypeCounts: Record<number, number> | null = null;
let cachedAlarm: { total: number; handled: number; unhandled: number } | null = null;

// ---- data helpers ----
const toNum = (v: unknown, fallback = 0) => { const n = Number(v); return Number.isFinite(n) ? n : fallback; };

const countLeavesByType = (nodes: any[]): Record<number, number> => {
  const map: Record<number, number> = {};
  const walk = (list: any[]) => {
    for (const n of list || []) {
      if (n.children?.length) { walk(n.children); continue; }
      const t = Number(n.type ?? n.monitorType ?? 0);
      if (t) map[t] = (map[t] || 0) + 1;
    }
  };
  walk(nodes);
  return map;
};

// ---- state ----
const loading = ref(false);
const typeCounts = ref<Record<number, number>>({});
const alarmHandled = ref(0);
const alarmUnhandled = ref(0);
const alarmList = ref<any[]>([]);
const currentTime = ref('');

// Router
const router = useRouter();

// ---- shortcuts ----
const shortcuts = [
  {
    title: '组态画面编辑器',
    desc: '基于 Vue Flow 组态设计画面',
    path: '/visualEditor',
    color: 'rgba(59, 130, 246, 0.1)',
    icon: `<svg viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>`
  },
  {
    title: '设备监测点台账',
    desc: '层级化设备台账及指标阈值',
    path: '/ems/base/baseMonitorInfo',
    color: 'rgba(99, 102, 241, 0.1)',
    icon: `<svg viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>`
  },
  {
    title: '异常告警中心',
    desc: '越界异常处理与确认详情',
    path: '/ems/record/recordAlarmData',
    color: 'rgba(245, 158, 11, 0.1)',
    icon: `<svg viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0"/></svg>`
  },
  {
    title: '告警规则配置',
    desc: '定制采集点高低越界规则',
    path: '/ems/record/recordAlarmRule',
    color: 'rgba(16, 185, 129, 0.1)',
    icon: `<svg viewBox="0 0 24 24" fill="none" stroke="#10b981" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`
  }
];

// ---- KPI cards ----
const kpiCards = computed(() => {
  const temp = typeCounts.value[5] || 0;
  const vib = typeCounts.value[10] || 0;
  const env = Object.entries(typeCounts.value).filter(([k]) => k !== '10').reduce((s, [, v]) => s + v, 0);
  const totalAlarms = alarmHandled.value + alarmUnhandled.value;
  const unhandled = alarmUnhandled.value;

  return [
    {
      label: '温度测点',
      value: temp,
      unit: '个',
      color: '#3b82f6',
      grad: 'linear-gradient(135deg, #3b82f6, #60a5fa)',
      icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 14.76V3.5a2.5 2.5 0 0 0-5 0v11.26a4.5 4.5 0 1 0 5 0z"/></svg>`
    },
    {
      label: '振动设备',
      value: vib,
      unit: '台',
      color: '#6366f1',
      grad: 'linear-gradient(135deg, #6366f1, #818cf8)',
      icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20M17 5v14M22 9v6M7 5v14M2 9v6"/></svg>`
    },
    {
      label: '温湿度',
      value: env,
      unit: '个',
      color: '#10b981',
      grad: 'linear-gradient(135deg, #10b981, #34d399)',
      icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 22c1.25-6.84 5.37-12 11-14.7L22 2l-5.3 9C14 16.63 8.84 20.75 2 22z"/><path d="M12 22V12"/></svg>`
    },
    {
      label: '告警总数',
      value: totalAlarms,
      unit: '条',
      color: '#f59e0b',
      grad: 'linear-gradient(135deg, #f59e0b, #fbbf24)',
      icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0"/></svg>`
    },
    {
      label: '待处理',
      value: unhandled,
      unit: '条',
      color: '#ef4444',
      grad: 'linear-gradient(135deg, #ef4444, #f87171)',
      icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01"/></svg>`
    }
  ];
});

// ---- charts ----
const typeBarOption = computed<EChartsOption | null>(() => {
  const entries = Object.entries(typeCounts.value).filter(([, v]) => v > 0).sort((a, b) => b[1] - a[1]);
  if (!entries.length) return null;
  return {
    tooltip: {
      trigger: 'axis',
      axisPointer: { type: 'shadow' },
      backgroundColor: 'rgba(255, 255, 255, 0.95)',
      borderColor: '#e2e8f0',
      borderWidth: 1,
      textStyle: { color: '#1e293b', fontSize: 12 },
      shadowColor: 'rgba(0, 0, 0, 0.05)',
      shadowBlur: 10
    },
    grid: { left: 80, right: 30, top: 15, bottom: 20 },
    xAxis: {
      type: 'value',
      minInterval: 1,
      axisLabel: { fontSize: 11, color: '#64748b' },
      splitLine: { lineStyle: { type: 'dashed', color: '#f1f5f9' } }
    },
    yAxis: {
      type: 'category',
      data: entries.map(([k]) => TYPE_MAP[Number(k)] || k),
      axisLabel: { fontSize: 12, color: '#334155', fontWeight: 500 },
      axisLine: { show: false },
      axisTick: { show: false }
    },
    series: [{
      type: 'bar',
      barWidth: 14,
      data: entries.map(([k, v]) => {
        const baseColor = TYPE_COLOR[Number(k)] || '#64748b';
        return {
          value: v,
          itemStyle: {
            color: {
              type: 'linear',
              x: 0,
              y: 0,
              x2: 1,
              y2: 0,
              colorStops: [
                { offset: 0, color: baseColor + '40' },
                { offset: 1, color: baseColor }
              ]
            },
            borderRadius: [0, 8, 8, 0]
          }
        };
      })
    }]
  };
});

const alarmPieOption = computed<EChartsOption | null>(() => {
  const h = alarmHandled.value;
  const u = alarmUnhandled.value;
  if (!h && !u) return null;
  const total = h + u;
  const handledRate = total > 0 ? Math.round((h / total) * 100) : 0;

  return {
    color: ['#10b981', '#ef4444'],
    tooltip: {
      trigger: 'item',
      backgroundColor: 'rgba(255, 255, 255, 0.95)',
      borderColor: '#e2e8f0',
      borderWidth: 1,
      textStyle: { color: '#1e293b', fontSize: 12 }
    },
    legend: {
      bottom: '2%',
      left: 'center',
      icon: 'circle',
      itemWidth: 8,
      itemHeight: 8,
      textStyle: { color: '#64748b', fontSize: 11 }
    },
    title: {
      text: `${handledRate}%`,
      subtext: '处理率',
      left: 'center',
      top: '36%',
      textStyle: {
        fontSize: 22,
        fontWeight: 'bold',
        color: '#0f172a'
      },
      subtextStyle: {
        fontSize: 12,
        color: '#64748b'
      }
    },
    series: [{
      type: 'pie',
      radius: ['58%', '78%'],
      center: ['50%', '44%'],
      avoidLabelOverlap: false,
      label: { show: false },
      emphasis: {
        scale: true,
        scaleSize: 5,
        label: { show: false }
      },
      data: [
        {
          name: '已处理',
          value: h,
          itemStyle: {
            color: {
              type: 'linear',
              x: 0,
              y: 0,
              x2: 0,
              y2: 1,
              colorStops: [
                { offset: 0, color: '#10b981' },
                { offset: 1, color: '#059669' }
              ]
            }
          }
        },
        {
          name: '待处理',
          value: u,
          itemStyle: {
            color: {
              type: 'linear',
              x: 0,
              y: 0,
              x2: 0,
              y2: 1,
              colorStops: [
                { offset: 0, color: '#ef4444' },
                { offset: 1, color: '#dc2626' }
              ]
            }
          }
        }
      ]
    }]
  };
});

// ---- helpers for alarm records ----
const getAlarmLevelType = (level: string | number) => {
  const lvl = String(level).toUpperCase();
  if (lvl === '1' || lvl === 'CRITICAL' || lvl === 'H' || lvl === '3') return 'danger';
  if (lvl === '2' || lvl === 'WARNING' || lvl === 'M' || lvl === '2') return 'warning';
  return 'info';
};

const getAlarmLevelLabel = (level: string | number) => {
  const lvl = String(level).toUpperCase();
  if (lvl === '1' || lvl === 'CRITICAL' || lvl === 'H' || lvl === '3') return '紧急';
  if (lvl === '2' || lvl === 'WARNING' || lvl === 'M' || lvl === '2') return '警告';
  return '提醒';
};

const formatTime = (time: string | Date | undefined) => {
  if (!time) return '--';
  const date = new Date(time);
  if (isNaN(date.getTime())) return String(time);
  const pad = (n: number) => String(n).padStart(2, '0');
  return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
};

const goToPage = (path: string) => {
  router.push(path);
};

// ---- clock timer ----
let timerId: number | null = null;
const updateClock = () => {
  const now = new Date();
  const pad = (n: number) => String(n).padStart(2, '0');
  currentTime.value = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
};

// ---- load ----
const loadAll = async (force = false) => {
  loading.value = true;
  try {
    if (force || !cachedTypeCounts) {
      try {
        const res = await getMonitorInfoTree({});
        cachedTypeCounts = countLeavesByType(res.data || []);
      } catch { cachedTypeCounts = {}; }
    }
    typeCounts.value = { ...cachedTypeCounts };

    if (force || !cachedAlarm) {
      try {
        const r = await getRecordAlarmDataSummary();
        const t = toNum(r.data?.totalCount);
        const u = toNum(r.data?.unhandledCount);
        cachedAlarm = { total: t, handled: t - u, unhandled: u };
      } catch { cachedAlarm = { total: 0, handled: 0, unhandled: 0 }; }
    }
    alarmHandled.value = cachedAlarm.handled;
    alarmUnhandled.value = cachedAlarm.unhandled;

    // Load recent alarms
    try {
      const res = await listRecordAlarmData({ pageNum: 1, pageSize: 6 });
      alarmList.value = res.rows || [];
    } catch {
      alarmList.value = [];
    }
  } finally { loading.value = false; }
};

onMounted(() => {
  void loadAll();
  updateClock();
  timerId = window.setInterval(updateClock, 1000);
});

onBeforeUnmount(() => {
  if (timerId) {
    clearInterval(timerId);
  }
});
</script>

<style lang="scss" scoped>
.home-dashboard {
  min-height: calc(100vh - 84px);
  padding: 24px;
  background: #f1f5f9;
}

// Header
.db-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
  padding: 24px 30px;
  background: #ffffff;
  border-radius: 16px;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
  border: 1px solid rgba(226, 232, 240, 0.8);
}
.db-header-left {
  display: flex;
  flex-direction: column;
  gap: 6px;

  h1 {
    margin: 4px 0 2px;
    font-size: 24px;
    color: #0f172a;
    font-weight: 800;
    letter-spacing: -0.5px;
  }
  .subtitle {
    margin: 0;
    font-size: 14px;
    color: #64748b;
  }
}
.db-badge-row {
  display: flex;
  align-items: center;
}
.db-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  font-size: 11px;
  font-weight: 600;
  color: #3b82f6;
  background: rgba(59, 130, 246, 0.08);
  border-radius: 99px;
  letter-spacing: 0.5px;

  .pulse-dot {
    width: 6px;
    height: 6px;
    background-color: #10b981;
    border-radius: 50%;
    animation: pulse 2s infinite;
  }
}
.db-header-right {
  display: flex;
  align-items: center;
  gap: 16px;
}
.db-clock-pill {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: 99px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  font-size: 13px;
  font-weight: 500;
  color: #475569;

  .clock-icon {
    width: 15px;
    height: 15px;
    color: #64748b;
  }
}
.refresh-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border-radius: 99px;
  padding: 8px 20px;
  font-weight: 500;
  height: auto;
  border: none;
  background: linear-gradient(135deg, #3b82f6, #2563eb);
  color: #ffffff;
  box-shadow: 0 4px 10px rgba(59, 130, 246, 0.3);
  transition: all 0.3s ease;
  cursor: pointer;

  &:hover {
    background: linear-gradient(135deg, #2563eb, #1d4ed8);
    box-shadow: 0 6px 14px rgba(59, 130, 246, 0.4);
    transform: translateY(-1px);
    color: #ffffff;
  }

  .refresh-icon {
    width: 14px;
    height: 14px;
  }
}

// KPI
.kpi-row {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 16px;
  margin-bottom: 24px;
}
.kpi-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 24px 20px;
  background: #ffffff;
  border-radius: 16px;
  border: 1px solid rgba(226, 232, 240, 0.8);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 4px;
    height: 100%;
    background: var(--accent-grad);
    opacity: 0.8;
  }

  &:hover {
    transform: translateY(-4px);
    box-shadow: 0 12px 20px -8px rgba(0, 0, 0, 0.1), 0 4px 12px -2px rgba(0, 0, 0, 0.05);
    border-color: var(--accent);
  }
}
.kpi-icon {
  width: 46px;
  height: 46px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  flex-shrink: 0;

  span {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
  }
}
.kpi-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.kpi-num {
  font-size: 26px;
  font-weight: 800;
  color: #0f172a;
  line-height: 1;
  letter-spacing: -0.5px;

  small {
    margin-left: 4px;
    font-size: 13px;
    font-weight: 600;
    color: #94a3b8;
  }
}
.kpi-title {
  font-size: 13px;
  font-weight: 500;
  color: #64748b;
}

// Charts
.chart-row {
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 24px;
  margin-bottom: 24px;
}
.chart-panel {
  padding: 24px;
  background: #ffffff;
  border-radius: 16px;
  border: 1px solid rgba(226, 232, 240, 0.8);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
  display: flex;
  flex-direction: column;
  min-width: 0; // 防止子元素撑开容器导致布局挤压变形
}
.panel-header-simple {
  margin-bottom: 16px;
}
.panel-title-with-bar, .panel-title-with-bar2 {
  font-size: 16px;
  font-weight: 700;
  color: #1e293b;
  position: relative;
  padding-left: 12px;

  &::before {
    content: '';
    position: absolute;
    left: 0;
    top: 3px;
    width: 4px;
    height: 14px;
    border-radius: 2px;
  }
}
.panel-title-with-bar::before {
  background: #3b82f6;
}
.panel-title-with-bar2::before {
  background: #10b981;
}
.chart-sub {
  font-size: 12px;
  color: #94a3b8;
  margin-top: 4px;
  font-weight: 400;
}
.chart-container {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 260px;
}
.chart-box {
  width: 100%;
  height: 260px;
}

// Bottom layout
.bottom-row {
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 24px;
}
.bottom-card {
  background: #ffffff;
  border-radius: 16px;
  border: 1px solid rgba(226, 232, 240, 0.8);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
  padding: 24px;
  display: flex;
  flex-direction: column;
  min-width: 0; // 防止内部 el-table 撑开容器导致挤压右侧快捷通道
}
.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
.panel-title-group {
  display: flex;
  align-items: center;
  gap: 12px;
}
.panel-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;

  svg {
    width: 18px;
    height: 18px;
  }
}
.bg-grad-alarm-icon {
  background: linear-gradient(135deg, #ef4444, #f43f5e);
}
.bg-grad-shortcut-icon {
  background: linear-gradient(135deg, #6366f1, #4f46e5);
}
.panel-titles {
  h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    color: #1e293b;
  }
  p {
    margin: 2px 0 0;
    font-size: 12px;
    color: #94a3b8;
  }
}
.more-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;

  .arrow-icon {
    width: 12px;
    height: 12px;
    transition: transform 0.2s ease;
  }

  &:hover .arrow-icon {
    transform: translateX(2px);
  }
}

// Alarm Table Custom
.alarm-table-wrapper {
  flex: 1;

  :deep(.el-table) {
    --el-table-border-color: #f1f5f9;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid #f1f5f9;
  }

  :deep(.el-table__header-wrapper) {
    th {
      font-size: 12px;
    }
  }

  :deep(.el-table__row) {
    font-size: 12px;
    color: #334155;
    transition: background-color 0.2s ease;

    &:hover {
      background-color: #f8fafc !important;
    }
  }
}
.alarm-title-text {
  font-weight: 500;
  color: #1e293b;
}
.status-tag, .level-tag {
  font-weight: 600;
  border-radius: 6px;
}
.actual-val {
  color: #ef4444;
  font-weight: 700;
}
.val-sep {
  margin: 0 4px;
  color: #cbd5e1;
}
.threshold-val {
  color: #64748b;
  font-weight: 500;
}
.time-text {
  color: #64748b;
}

// Shortcut Card Custom
.shortcut-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  flex: 1;
}
.shortcut-item {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 18px;
  border-radius: 12px;
  background: #f8fafc;
  border: 1px solid #f1f5f9;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

  &:hover {
    background: #ffffff;
    border-color: #e2e8f0;
    box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.05);
    transform: translateY(-2px);

    .sc-arrow {
      color: #3b82f6;
      transform: translateX(2px);
    }
  }
}
.sc-icon-circle {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;

  .sc-svg {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
  }
}
.sc-content {
  flex: 1;
  min-width: 0;

  h4 {
    margin: 0;
    font-size: 14px;
    font-weight: 700;
    color: #1e293b;
  }
  p {
    margin: 4px 0 0;
    font-size: 11px;
    color: #94a3b8;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}
.sc-arrow {
  color: #cbd5e1;
  display: flex;
  align-items: center;
  transition: all 0.2s ease;

  svg {
    width: 14px;
    height: 14px;
  }
}

// Animations & Keyframes
@keyframes pulse {
  0% {
    transform: scale(0.95);
    box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);
  }
  70% {
    transform: scale(1);
    box-shadow: 0 0 0 6px rgba(16, 185, 129, 0);
  }
  100% {
    transform: scale(0.95);
    box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
  }
}

// Responsive
@media (max-width: 1400px) {
  .kpi-row {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (max-width: 1100px) {
  .chart-row, .bottom-row {
    grid-template-columns: 1fr;
    gap: 20px;
  }
}
@media (max-width: 768px) {
  .kpi-row {
    grid-template-columns: repeat(2, 1fr);
  }
  .db-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
}
@media (max-width: 480px) {
  .kpi-row {
    grid-template-columns: 1fr;
  }
}
</style>
