From 0f710285d9f02e3d4cea19557e17945e9ef9532b Mon Sep 17 00:00:00 2001
From: xiazhengtongxue <133085197+xiazhengtongxue@users.noreply.github.com>
Date: 星期五, 01 五月 2026 09:48:49 +0800
Subject: [PATCH] Merge branch 'dev' of http://115.159.85.185:8098/r/SuZhouGuanHong/ShanMeiXinNengYuan into dev

---
 Code/WMS/WIDESEA_WMSClient/src/views/Home.vue |  642 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 536 insertions(+), 106 deletions(-)

diff --git a/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue b/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
index ea951db..32359d5 100644
--- a/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
+++ b/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
@@ -1,14 +1,83 @@
 <template>
   <div class="dashboard-container">
-    <!-- 椤堕儴锛氭湰鏈堝嚭鍏ュ簱瓒嬪娍 (鍏ㄥ) -->
-    <div class="chart-row full-width">
-      <div class="chart-card">
-        <div class="card-title">姣忔湀鍑哄叆搴撹秼鍔�</div>
-        <div id="chart-monthly-trend" class="chart-content"></div>
+    <!-- 椤堕儴KPI鍗$墖锛氭樉绀轰粨搴撴�绘暟鍜屾�诲簱瀛橀噺 -->
+    <div class="kpi-cards">
+      <div class="kpi-card">
+        <div class="kpi-icon">馃彋锔�</div>
+        <div class="kpi-info">
+          <div class="kpi-label">浠撳簱鎬绘暟</div>
+          <div class="kpi-value">{{ totalWarehouses }}</div>
+        </div>
+      </div>
+      <div class="kpi-card">
+        <div class="kpi-icon">馃摝</div>
+        <div class="kpi-info">
+          <div class="kpi-label">鎬诲簱瀛橀噺</div>
+          <div class="kpi-value">{{ totalStock.toLocaleString() }}</div>
+        </div>
+      </div>
+      <div class="kpi-card">
+        <div class="kpi-icon">馃搳</div>
+        <div class="kpi-info">
+          <div class="kpi-label">鏈湀鎬诲叆搴�</div>
+          <div class="kpi-value">{{ monthlyInboundTotal.toLocaleString() }}</div>
+        </div>
+      </div>
+      <div class="kpi-card">
+        <div class="kpi-icon">馃摛</div>
+        <div class="kpi-info">
+          <div class="kpi-label">鏈湀鎬诲嚭搴�</div>
+          <div class="kpi-value">{{ monthlyOutboundTotal.toLocaleString() }}</div>
+        </div>
       </div>
     </div>
 
-    <!-- 绗簩琛岋細姣忔棩鍑哄叆搴撹秼鍔� (鍏ㄥ) -->
+    <!-- 椤堕儴锛氭湰鏈堝嚭鍏ュ簱瓒嬪娍 - 涓�3涓�2甯冨眬锛屾瘡涓崱鐗囩洿鎺ユ樉绀轰粨搴撴暟瀛� -->
+    <div class="chart-row top-three">
+      <div v-for="warehouse in topWarehouses" :key="warehouse.code" class="chart-card">
+        <div class="card-title">{{ warehouse.name }}</div>
+        <!-- 浠撳簱鏁板瓧鏄剧ず鍖哄煙 -->
+        <div class="warehouse-numbers">
+          <div class="number-item inbound">
+            <span class="number-label">鍏ュ簱</span>
+            <span class="number-value">{{ getMonthlyInbound(warehouse.code) }}</span>
+          </div>
+          <div class="number-item outbound">
+            <span class="number-label">鍑哄簱</span>
+            <span class="number-value">{{ getMonthlyOutbound(warehouse.code) }}</span>
+          </div>
+          <div class="number-item stock">
+            <span class="number-label">搴撳瓨</span>
+            <span class="number-value">{{ getWarehouseStock(warehouse.code) }}</span>
+          </div>
+        </div>
+        <div :id="`chart-${warehouse.code}`" class="chart-content"></div>
+      </div>
+    </div>
+
+    <div class="chart-row bottom-two">
+      <div v-for="warehouse in bottomWarehouses" :key="warehouse.code" class="chart-card">
+        <div class="card-title">{{ warehouse.name }}</div>
+        <!-- 浠撳簱鏁板瓧鏄剧ず鍖哄煙 -->
+        <div class="warehouse-numbers">
+          <div class="number-item inbound">
+            <span class="number-label">鍏ュ簱</span>
+            <span class="number-value">{{ getMonthlyInbound(warehouse.code) }}</span>
+          </div>
+          <div class="number-item outbound">
+            <span class="number-label">鍑哄簱</span>
+            <span class="number-value">{{ getMonthlyOutbound(warehouse.code) }}</span>
+          </div>
+          <div class="number-item stock">
+            <span class="number-label">搴撳瓨</span>
+            <span class="number-value">{{ getWarehouseStock(warehouse.code) }}</span>
+          </div>
+        </div>
+        <div :id="`chart-${warehouse.code}`" class="chart-content"></div>
+      </div>
+    </div>
+
+    <!-- 姣忔棩鍑哄叆搴撹秼鍔� (鍏ㄥ) -->
     <div class="chart-row full-width">
       <div class="chart-card">
         <div class="card-title">姣忔棩鍑哄叆搴撹秼鍔�</div>
@@ -16,7 +85,7 @@
       </div>
     </div>
 
-    <!-- 绗洓琛岋細浠撳簱鍒嗗竷 -->
+    <!-- 浠撳簱鍒嗗竷 -->
     <div class="chart-row">
       <div class="chart-card">
         <div class="card-title">鍚勪粨搴撳簱瀛樺垎甯�</div>
@@ -34,9 +103,40 @@
   data() {
     return {
       charts: {},
+      // 浜斾釜浠撳簱瀹氫箟 - 涓�3涓�
+      topWarehouses: [
+        { code: "GWSC1", name: "楂樻俯1鍙蜂粨搴�" },
+        { code: "CWSC1", name: "甯告俯1鍙蜂粨搴�" },
+        { code: "HCSC1", name: "鍒嗗1鍙蜂粨搴�" }
+      ],
+      // 涓�2涓�
+      bottomWarehouses: [
+        { code: "FJSC1", name: "璐熸瀬鍗�1鍙蜂粨搴�" },
+        { code: "ZJSC1", name: "姝f瀬鍗�1鍙蜂粨搴�" }
+      ],
       dailyData: [],
-      monthlyData: [],
-      warehouseData: []
+      // 瀛樺偍姣忎釜浠撳簱鐨勬湀搴︽暟鎹�
+      monthlyData: {
+        GWSC1: [],
+        CWSC1: [],
+        HCSC1: [],
+        FJSC1: [],
+        ZJSC1: []
+      },
+      // 瀛樺偍姣忎釜浠撳簱鐨勫綋鍓嶅簱瀛�
+      warehouseStocks: {
+        GWSC1: 0,
+        CWSC1: 0,
+        HCSC1: 0,
+        FJSC1: 0,
+        ZJSC1: 0
+      },
+      warehouseData: [],
+      // KPI 姹囨�绘暟鎹�
+      totalWarehouses: 5,
+      totalStock: 0,
+      monthlyInboundTotal: 0,
+      monthlyOutboundTotal: 0
     };
   },
   mounted() {
@@ -46,35 +146,58 @@
   },
   beforeUnmount() {
     window.removeEventListener("resize", this.handleResize);
-    Object.values(this.charts).forEach(chart => chart.dispose());
+    Object.values(this.charts).forEach(chart => chart && chart.dispose());
   },
   methods: {
     handleResize() {
-      Object.values(this.charts).forEach(chart => chart.resize());
+      Object.values(this.charts).forEach(chart => chart && chart.resize());
     },
 
     initCharts() {
-      this.charts.monthlyTrend = echarts.init(document.getElementById("chart-monthly-trend"));
+      // 鍒濆鍖栨墍鏈変粨搴撳浘琛�
+      const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+      allWarehouses.forEach(warehouse => {
+        const chartId = `chart-${warehouse.code}`;
+        const el = document.getElementById(chartId);
+        if (el) {
+          this.charts[warehouse.code] = echarts.init(el);
+        }
+      });
+      // 鍒濆鍖栨瘡鏃ュ浘琛ㄥ拰浠撳簱鍒嗗竷鍥捐〃
       this.charts.daily = echarts.init(document.getElementById("chart-daily"));
       this.charts.warehouse = echarts.init(document.getElementById("chart-warehouse"));
     },
 
     async loadData() {
-      await this.loadMonthlyStats();
+      // 骞惰鍔犺浇鎵�鏈変粨搴撶殑鏈堝害鏁版嵁锛堝垎鍒紶鍏ヤ笉鍚岀殑Roadway鍙傛暟锛�
+      const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+      const monthlyPromises = allWarehouses.map(warehouse =>
+        this.loadMonthlyStatsForWarehouse(warehouse.code)
+      );
+      await Promise.all(monthlyPromises);
+      // 鏇存柊鎵�鏈変粨搴撶殑鏈堝害鍥捐〃
+      this.updateAllMonthlyTrendCharts();
+
       await this.loadDailyStats();
       await this.loadStockByWarehouse();
+      await this.loadWarehouseStocks();
+      this.calculateKPIs();
     },
 
-    async loadMonthlyStats() {
+    async loadMonthlyStatsForWarehouse(roadway) {
+      console.log(`姝e湪鍔犺浇${roadway}鐨勬瘡鏈堢粺璁℃暟鎹�...`);
       try {
-        const res = await this.http.get("/api/Dashboard/MonthlyStats", { months: 12 });
+        // 鍏抽敭淇锛氬垎鍒紶鍏ヤ笉鍚岀殑Roadway鍙傛暟
+        const res = await this.http.get("/api/Dashboard/MonthlyStats?monthly=12&roadway=" + roadway);
         if (res.status && res.data) {
-          console.log("姣忔湀缁熻鏁版嵁:", res.data);
-          this.monthlyData = res.data;
-          this.updateMonthlyTrendChart();
+          console.log(`${roadway} 姣忔湀缁熻鏁版嵁:`, res.data);
+          this.monthlyData[roadway] = res.data;
+        } else {
+          this.monthlyData[roadway] = [];
         }
       } catch (e) {
-        console.error("鍔犺浇姣忔湀缁熻澶辫触", e);
+        console.error(`鍔犺浇${roadway}姣忔湀缁熻澶辫触`, e);
+        this.monthlyData[roadway] = [];
       }
     },
 
@@ -104,39 +227,202 @@
       }
     },
 
-    updateMonthlyTrendChart() {
+    async loadWarehouseStocks() {
+      // 妯℃嫙鍔犺浇姣忎釜浠撳簱鐨勫綋鍓嶅簱瀛橀噺
+      // 濡傛灉鍚庣鏈夋帴鍙o紝鍙互鏇挎崲涓虹湡瀹濧PI璋冪敤
+      try {
+        // 灏濊瘯鍔犺浇搴撳瓨鏁版嵁锛屽鏋滄帴鍙d笉瀛樺湪鍒欎娇鐢ㄦā鎷熸暟鎹�
+        const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+        for (const warehouse of allWarehouses) {
+          try {
+            const res = await this.http.get(`/api/Dashboard/WarehouseStock?warehouse=${warehouse.code}`);
+            if (res.status && res.data) {
+              this.warehouseStocks[warehouse.code] = res.data.stock || 0;
+            } else {
+              // 浠庢湀搴︽暟鎹腑璁$畻妯℃嫙搴撳瓨锛堟渶杩戞湀浠界疮璁″叆搴�-鍑哄簱锛�
+              const monthlyData = this.monthlyData[warehouse.code] || [];
+              let totalInbound = 0;
+              let totalOutbound = 0;
+              monthlyData.forEach(m => {
+                totalInbound += (m.inbound ?? m.Inbound) || 0;
+                totalOutbound += (m.outbound ?? m.Outbound) || 0;
+              });
+              this.warehouseStocks[warehouse.code] = Math.max(0, totalInbound - totalOutbound);
+            }
+          } catch (e) {
+            // 浣跨敤妯℃嫙鏁版嵁
+            const mockStocks = {
+              GWSC1: 12580,
+              CWSC1: 8920,
+              HCSC1: 15600,
+              FJSC1: 4300,
+              ZJSC1: 7200
+            };
+            this.warehouseStocks[warehouse.code] = mockStocks[warehouse.code] || 0;
+          }
+        }
+      } catch (e) {
+        console.error("鍔犺浇浠撳簱搴撳瓨澶辫触", e);
+      }
+    },
+
+    getMonthlyInbound(warehouseCode) {
+      const data = this.monthlyData[warehouseCode] || [];
+      if (data.length === 0) return 0;
+      // 鑾峰彇鏈�杩戜竴涓湀锛堟渶鍚庝竴鏉★級鐨勫叆搴撴暟
+      const latest = data[data.length - 1];
+      return (latest.inbound ?? latest.Inbound) || 0;
+    },
+
+    getMonthlyOutbound(warehouseCode) {
+      const data = this.monthlyData[warehouseCode] || [];
+      if (data.length === 0) return 0;
+      // 鑾峰彇鏈�杩戜竴涓湀锛堟渶鍚庝竴鏉★級鐨勫嚭搴撴暟
+      const latest = data[data.length - 1];
+      return (latest.outbound ?? latest.Outbound) || 0;
+    },
+
+    getWarehouseStock(warehouseCode) {
+      return this.warehouseStocks[warehouseCode] || 0;
+    },
+
+    calculateKPIs() {
+      // 璁$畻鎬诲簱瀛�
+      let totalStock = 0;
+      for (const code in this.warehouseStocks) {
+        totalStock += this.warehouseStocks[code];
+      }
+      this.totalStock = totalStock;
+
+      // 璁$畻鏈湀鎬诲叆搴撳拰鎬诲嚭搴擄紙鎵�鏈変粨搴撴渶杩戜竴涓湀鐨勫悎璁★級
+      let totalInbound = 0;
+      let totalOutbound = 0;
+      const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+      allWarehouses.forEach(warehouse => {
+        totalInbound += this.getMonthlyInbound(warehouse.code);
+        totalOutbound += this.getMonthlyOutbound(warehouse.code);
+      });
+      this.monthlyInboundTotal = totalInbound;
+      this.monthlyOutboundTotal = totalOutbound;
+    },
+
+    // 鏇存柊鎵�鏈変粨搴撶殑鏈堝害瓒嬪娍鍥捐〃
+    updateAllMonthlyTrendCharts() {
+      const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+      allWarehouses.forEach(warehouse => {
+        this.updateMonthlyTrendChartForWarehouse(warehouse.code);
+      });
+    },
+
+    updateMonthlyTrendChartForWarehouse(roadway) {
+      const chart = this.charts[roadway];
+      if (!chart) return;
+
+      const data = this.monthlyData[roadway] || [];
+      // 鍏煎澶у皬鍐欏瓧娈靛悕
+      const monthLabels = data.map(m => m.month || m.Month || "");
+      const inboundData = data.map(m => {
+        const val = m.inbound ?? m.Inbound;
+        return val !== undefined && val !== null ? Number(val) : 0;
+      });
+      const outboundData = data.map(m => {
+        const val = m.outbound ?? m.Outbound;
+        return val !== undefined && val !== null ? Number(val) : 0;
+      });
+
       const option = {
-        tooltip: { trigger: "axis" },
-        legend: { data: ["鍏ュ簱", "鍑哄簱"], textStyle: { color: "#fff" } },
+        tooltip: { 
+          trigger: "axis",
+          formatter: function(params) {
+            let result = params[0].axisValue + "<br/>";
+            params.forEach(p => {
+              result += `${p.marker}${p.seriesName}: ${p.value}<br/>`;
+            });
+            return result;
+          }
+        },
+        legend: {
+          data: ["鍏ュ簱", "鍑哄簱"],
+          textStyle: { color: "#fff" },
+          top: 0,
+          right: 10,
+          itemWidth: 20,
+          itemHeight: 12
+        },
+        grid: {
+          left: "8%",
+          right: "8%",
+          top: "18%",
+          bottom: "12%",
+          containLabel: true
+        },
         xAxis: {
           type: "category",
-          data: this.monthlyData.map(m => m.month),
-          axisLabel: { color: "#fff", rotate: 45 }
+          data: monthLabels,
+          axisLabel: {
+            color: "#ccc",
+            rotate: 45,
+            fontSize: 10,
+            interval: 0,
+            margin: 8
+          },
+          axisLine: { lineStyle: { color: "#4a5b6e" } }
         },
-        yAxis: [
-          {
-            type: "value",
-            name: "鏁伴噺",
-            axisLabel: { color: "#fff" }
-          }
-        ],
+        yAxis: {
+          type: "value",
+          name: "浠诲姟鏁伴噺",
+          nameTextStyle: { color: "#ccc", fontSize: 11 },
+          axisLabel: { color: "#ccc" },
+          splitLine: { lineStyle: { color: "#2a3a4a", type: "dashed" } }
+        },
         series: [
-          { name: "鍏ュ簱", type: "bar", data: this.monthlyData.map(m => m.inbound), itemStyle: { color: "#5470c6" } },
-          { name: "鍑哄簱", type: "line", data: this.monthlyData.map(m => m.outbound), itemStyle: { color: "#91cc75" } }
+          {
+            name: "鍏ュ簱",
+            type: "bar",
+            data: inboundData,
+            itemStyle: {
+              color: "#5470c6",
+              borderRadius: [4, 4, 0, 0]
+            },
+            barWidth: "35%",
+            label: {
+              show: inboundData.length <= 8,
+              position: "top",
+              color: "#5470c6",
+              fontSize: 10
+            }
+          },
+          {
+            name: "鍑哄簱",
+            type: "line",
+            data: outboundData,
+            symbol: "circle",
+            symbolSize: 6,
+            itemStyle: { color: "#91cc75" },
+            lineStyle: { width: 2, type: "solid" },
+            smooth: false,
+            label: {
+              show: outboundData.length <= 8,
+              position: "top",
+              color: "#91cc75",
+              fontSize: 10
+            }
+          }
         ]
       };
-      this.charts.monthlyTrend.setOption(option, true);
+      chart.setOption(option, true);
     },
 
     updateDailyChart() {
+      if (!this.charts.daily) return;
       const option = {
         tooltip: { trigger: "axis" },
         legend: { data: ["鍏ュ簱", "鍑哄簱"], textStyle: { color: "#fff" } },
         xAxis: {
           type: "category",
           data: this.dailyData.map(d => d.date),
-          axisLabel: { 
-            color: "#fff", 
+          axisLabel: {
+            color: "#fff",
             interval: 0,
             rotate: 45,
             fontSize: 12,
@@ -151,114 +437,120 @@
           axisLabel: { color: "#fff" }
         },
         grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '15%',
-          top: '10%',
+          left: "3%",
+          right: "4%",
+          bottom: "15%",
+          top: "10%",
           containLabel: true
         },
         series: [
-          { name: "鍏ュ簱", type: "bar", data: this.dailyData.map(d => d.inbound), itemStyle: { color: "#5470c6" } },
-          { name: "鍑哄簱", type: "bar", data: this.dailyData.map(d => d.outbound), itemStyle: { color: "#91cc75" } }
+          {
+            name: "鍏ュ簱",
+            type: "bar",
+            data: this.dailyData.map(d => d.inbound),
+            itemStyle: { color: "#5470c6" }
+          },
+          {
+            name: "鍑哄簱",
+            type: "bar",
+            data: this.dailyData.map(d => d.outbound),
+            itemStyle: { color: "#91cc75" }
+          }
         ]
       };
       this.charts.daily.setOption(option, true);
     },
 
     updateWarehouseChart() {
+      if (!this.charts.warehouse) return;
       const warehouseNames = this.warehouseData.map(w => w.warehouse);
-      const totalStocks = this.warehouseData.map(w => w.total);
       const hasStocks = this.warehouseData.map(w => w.hasStock);
       const noStocks = this.warehouseData.map(w => w.noStock);
       const hasStockPercentages = this.warehouseData.map(w => w.hasStockPercentage);
       const noStockPercentages = this.warehouseData.map(w => w.noStockPercentage);
-      
+
       const option = {
         tooltip: {
-          trigger: 'axis',
+          trigger: "axis",
           axisPointer: {
-            type: 'shadow'
+            type: "shadow"
           },
           formatter: function(params) {
-            let tip = params[0].name + '<br/>';
+            let tip = params[0].name + "<br/>";
             params.forEach(param => {
               const dataIndex = param.dataIndex;
-              const warehouse = window.homeComponent.warehouseData[dataIndex];
-              
-              if (param.seriesName === '宸茬敤瀹归噺') {
-                tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.hasStockPercentage})<br/>`;
-                tip += `鏈夊簱瀛�: ${warehouse.hasStock}<br/>`;
-                tip += `鏃犲簱瀛�: ${warehouse.noStock}<br/>`;
-                tip += `鎬诲閲�: ${warehouse.total}`;
-              } else if (param.seriesName === '鍓╀綑瀹归噺') {
-                tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.noStockPercentage})<br/>`;
-                tip += `鏈夊簱瀛�: ${warehouse.hasStock}<br/>`;
-                tip += `鏃犲簱瀛�: ${warehouse.noStock}<br/>`;
-                tip += `鎬诲閲�: ${warehouse.total}`;
+              const warehouse = window.homeComponent?.warehouseData[dataIndex];
+              if (warehouse) {
+                if (param.seriesName === "宸茬敤瀹归噺") {
+                  tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.hasStockPercentage})<br/>`;
+                  tip += `鏈夊簱瀛�: ${warehouse.hasStock}<br/>`;
+                  tip += `鏃犲簱瀛�: ${warehouse.noStock}<br/>`;
+                  tip += `鎬诲閲�: ${warehouse.total}`;
+                } else if (param.seriesName === "鍓╀綑瀹归噺") {
+                  tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.noStockPercentage})<br/>`;
+                }
+              } else {
+                tip += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
               }
             });
             return tip;
           }
         },
         legend: {
-          data: ['宸茬敤瀹归噺', '鍓╀綑瀹归噺'],
-          textStyle: { color: '#fff' }
+          data: ["宸茬敤瀹归噺", "鍓╀綑瀹归噺"],
+          textStyle: { color: "#fff" }
         },
         xAxis: {
-          type: 'category',
+          type: "category",
           data: warehouseNames,
-          axisLabel: { color: '#fff', rotate: 30 }
+          axisLabel: { color: "#fff", rotate: 30, interval: 0 }
         },
         yAxis: {
-          type: 'value',
-          axisLabel: { color: '#fff' }
+          type: "value",
+          name: "瀹归噺",
+          axisLabel: { color: "#fff" }
         },
         series: [
           {
-            name: '宸茬敤瀹归噺',
-            type: 'bar',
+            name: "宸茬敤瀹归噺",
+            type: "bar",
             data: hasStocks.map((value, index) => ({
               value: value,
               label: {
                 show: true,
-                position: 'top',
-                formatter: '{c} {a|' + hasStockPercentages[index] + '}',
-                rich: {
-                  a: {
-                    lineHeight: 20,
-                    borderColor: '#91cc75',
-                    color: '#91cc75'
-                  }
-                }
+                position: "top",
+                formatter: (params) => {
+                  const pct = hasStockPercentages[params.dataIndex];
+                  return `${params.value} (${pct})`;
+                },
+                color: "#91cc75",
+                fontSize: 11
               }
             })),
-            itemStyle: { color: '#91cc75' }
+            itemStyle: { color: "#91cc75" }
           },
           {
-            name: '鍓╀綑瀹归噺',
-            type: 'bar',
+            name: "鍓╀綑瀹归噺",
+            type: "bar",
             data: noStocks.map((value, index) => ({
               value: value,
               label: {
                 show: true,
-                position: 'top',
-                formatter: '{c} {a|' + noStockPercentages[index] + '}',
-                rich: {
-                  a: {
-                    lineHeight: 20,
-                    borderColor: '#fac858',
-                    color: '#fac858'
-                  }
-                }
+                position: "top",
+                formatter: (params) => {
+                  const pct = noStockPercentages[params.dataIndex];
+                  return `${params.value} (${pct})`;
+                },
+                color: "#fac858",
+                fontSize: 11
               }
             })),
-            itemStyle: { color: '#fac858' }
+            itemStyle: { color: "#fac858" }
           }
         ]
       };
-      
+
       window.homeComponent = this;
-      
       this.charts.warehouse.setOption(option, true);
     }
   }
@@ -274,36 +566,93 @@
   background-attachment: fixed;
 }
 
-.chart-row {
+/* KPI 鍗$墖鏍峰紡 */
+.kpi-cards {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 20px;
+  margin-bottom: 24px;
+}
+
+.kpi-card {
+  background: rgba(10, 16, 35, 0.7);
+  backdrop-filter: blur(10px);
+  border: 1px solid rgba(64, 224, 208, 0.3);
+  border-radius: 16px;
+  padding: 16px 20px;
   display: flex;
+  align-items: center;
+  gap: 16px;
+  transition: all 0.3s ease;
+  box-shadow: 0 0 15px rgba(0, 255, 255, 0.1);
+}
+
+.kpi-card:hover {
+  transform: translateY(-3px);
+  border-color: rgba(64, 224, 208, 0.6);
+  box-shadow: 0 0 25px rgba(0, 255, 255, 0.2);
+}
+
+.kpi-icon {
+  font-size: 32px;
+  opacity: 0.9;
+}
+
+.kpi-info {
+  flex: 1;
+}
+
+.kpi-label {
+  font-size: 13px;
+  color: #8ba0b5;
+  margin-bottom: 6px;
+  letter-spacing: 1px;
+}
+
+.kpi-value {
+  font-size: 28px;
+  font-weight: 700;
+  color: #00ffff;
+  text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
+  line-height: 1.2;
+}
+
+/* 涓�3涓浘琛ㄥ竷灞� */
+.chart-row.top-three {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 20px;
+  margin-bottom: 20px;
+}
+
+/* 涓�2涓浘琛ㄥ竷灞� */
+.chart-row.bottom-two {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
   gap: 20px;
   margin-bottom: 20px;
 }
 
 .chart-row.full-width {
   width: 100%;
+  margin-bottom: 20px;
 }
 
 .chart-card {
-  flex: 1;
   background: rgba(10, 16, 35, 0.6);
   backdrop-filter: blur(10px);
   border: 1px solid rgba(64, 224, 208, 0.3);
   border-radius: 12px;
   padding: 15px;
   position: relative;
-  box-shadow: 
-    0 0 15px rgba(0, 255, 255, 0.1),
-    inset 0 0 10px rgba(64, 224, 208, 0.1);
+  box-shadow: 0 0 15px rgba(0, 255, 255, 0.1), inset 0 0 10px rgba(64, 224, 208, 0.1);
   transition: all 0.3s ease;
   overflow: hidden;
 }
 
 .chart-card:hover {
   transform: translateY(-5px);
-  box-shadow: 
-    0 0 25px rgba(0, 255, 255, 0.3),
-    inset 0 0 15px rgba(64, 224, 208, 0.2);
+  box-shadow: 0 0 25px rgba(0, 255, 255, 0.3), inset 0 0 15px rgba(64, 224, 208, 0.2);
   border: 1px solid rgba(64, 224, 208, 0.6);
 }
 
@@ -337,7 +686,8 @@
 }
 
 @keyframes neon-flicker {
-  0%, 100% {
+  0%,
+  100% {
     opacity: 1;
     box-shadow: -2px -2px 10px #00ffff, 0 0 10px rgba(0, 255, 255, 0.7);
   }
@@ -349,26 +699,107 @@
 
 .card-title {
   color: #00ffff;
-  font-size: 16px;
+  font-size: 15px;
   text-align: center;
-  margin-bottom: 10px;
+  margin-bottom: 12px;
   text-shadow: 0 0 10px rgba(0, 255, 255, 0.7);
   font-weight: 500;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+/* 浠撳簱鏁板瓧鏄剧ず鍖哄煙 */
+.warehouse-numbers {
+  display: flex;
+  justify-content: space-around;
+  margin-bottom: 12px;
+  padding: 8px 0;
+  background: rgba(0, 0, 0, 0.3);
+  border-radius: 8px;
+}
+
+.number-item {
+  text-align: center;
+  flex: 1;
+}
+
+.number-label {
+  display: block;
+  font-size: 11px;
+  color: #8ba0b5;
+  margin-bottom: 4px;
+}
+
+.number-value {
+  display: block;
+  font-size: 20px;
+  font-weight: 700;
+  letter-spacing: 1px;
+}
+
+.number-item.inbound .number-value {
+  color: #5470c6;
+}
+
+.number-item.outbound .number-value {
+  color: #91cc75;
+}
+
+.number-item.stock .number-value {
+  color: #fac858;
 }
 
 .chart-content {
-  height: 280px;
+  height: 240px;
   width: 100%;
 }
 
 /* 鍏ㄥ鍥捐〃 */
 .full-width .chart-card {
-  flex: none;
   width: 100%;
 }
 
 .full-width .chart-content {
   height: 350px;
+}
+
+/* 鍝嶅簲寮忚皟鏁� */
+@media (max-width: 1024px) {
+  .kpi-cards {
+    grid-template-columns: repeat(2, 1fr);
+  }
+  .chart-row.top-three {
+    grid-template-columns: repeat(2, 1fr);
+  }
+  .chart-row.bottom-two {
+    grid-template-columns: repeat(2, 1fr);
+  }
+}
+
+@media (max-width: 768px) {
+  .kpi-cards {
+    grid-template-columns: 1fr;
+  }
+  .chart-row.top-three {
+    grid-template-columns: 1fr;
+  }
+  .chart-row.bottom-two {
+    grid-template-columns: 1fr;
+  }
+  .chart-content {
+    height: 220px;
+  }
+  .full-width .chart-content {
+    height: 280px;
+  }
+  .card-title {
+    font-size: 13px;
+    white-space: normal;
+  }
+  .number-value {
+    font-size: 16px;
+  }
 }
 
 /* 娣诲姞缃戞牸绾挎晥鏋� */
@@ -379,8 +810,7 @@
   left: 0;
   right: 0;
   bottom: 0;
-  background-image: 
-    linear-gradient(rgba(64, 224, 208, 0.05) 1px, transparent 1px),
+  background-image: linear-gradient(rgba(64, 224, 208, 0.05) 1px, transparent 1px),
     linear-gradient(90deg, rgba(64, 224, 208, 0.05) 1px, transparent 1px);
   background-size: 30px 30px;
   pointer-events: none;

--
Gitblit v1.9.3