From 64840e4e6d97d177b8a765ba8c53888abcf86d16 Mon Sep 17 00:00:00 2001
From: heshaofeng <heshaofeng@hnkhzn.com>
Date: 星期二, 13 一月 2026 18:48:20 +0800
Subject: [PATCH] Merge branch 'htq20251215' of http://115.159.85.185:8098/r/ZhongRui/ALDbanyunxiangmu into htq20251215

---
 项目代码/WIDESEA_WMSClient/src/views/Home.vue | 1234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1,223 insertions(+), 11 deletions(-)

diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue"
index 820437a..c86c83d 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue"
@@ -1,24 +1,1236 @@
 <template>
-  <div class="title"></div>
+  <div class="wms-dashboard">
+    <!-- 缁熻鍗$墖鍖哄煙 - 缁戝畾鍝嶅簲寮忔暟鎹� -->
+    <el-row :gutter="20" class="stats-card-row">
+      <el-col :span="4">
+        <div class="stats-card">
+          <div class="metric-icon">
+            <el-icon :size="32">
+              <Box />
+            </el-icon>
+          </div>
+          <div class="card-title">鎬诲簱瀛�</div>
+          <div class="card-value">{{ formatNumber(bigscreendata.totalStockQuantity) }}</div>
+        </div>
+      </el-col>
+      <el-col :span="4">
+        <div class="stats-card">
+          <div class="metric-icon">
+            <el-icon :size="32">
+              <Document />
+            </el-icon>
+          </div>
+          <div class="card-title">寰呭嚭搴撹鍗�</div>
+          <div class="card-value">{{ bigscreendata.unOutBoundOrderCount }}</div>
+        </div>
+      </el-col>
+      <el-col :span="4">
+        <div class="stats-card">
+          <div class="metric-icon">
+            <el-icon :size="32">
+              <Download />
+            </el-icon>
+          </div>
+          <div class="card-title">浠婃棩鍏ュ簱瀹屾垚鏁�</div>
+          <div class="card-value">{{ bigscreendata.inboundCount }}</div>
+        </div>
+      </el-col>
+      <el-col :span="4">
+        <div class="stats-card">
+          <div class="metric-icon">
+            <el-icon :size="32">
+              <Upload />
+            </el-icon>
+          </div>
+          <div class="card-title">浠婃棩鍑哄簱瀹屾垚鏁�</div>
+          <div class="card-value">{{ bigscreendata.outboundCount }}</div>
+        </div>
+      </el-col>
+      <el-col :span="4">
+        <div class="stats-card">
+          <div class="metric-icon">
+            <el-icon :size="32">
+              <Box />
+            </el-icon>
+          </div>
+          <div class="card-title">鏈夎揣鏂欑</div>
+          <div class="card-value">{{ formatNumber(bigscreendata.inStockPallet) }}</div>
+        </div>
+      </el-col>
+      <el-col :span="4">
+        <div class="stats-card">
+          <div class="metric-icon">
+            <el-icon :size="32">
+              <Box />
+            </el-icon>
+          </div>
+          <div class="card-title">绌虹鏁伴噺</div>
+          <div class="card-value">{{ formatNumber(bigscreendata.freeStockPallet) }}</div>
+        </div>
+      </el-col>
+    </el-row>
+
+    <!-- 鍥捐〃鍖哄煙锛堢涓�琛岋級 -->
+    <el-row :gutter="20" class="chart-row">
+      <el-col :span="8">
+        <div class="chart-card">
+          <div class="chart-title">搴撲綅鍒╃敤鐜�</div>
+          <div ref="locationRateRef" class="chart-container"></div>
+        </div>
+      </el-col>
+      <!-- 涓存湡淇℃伅鏀逛负琛ㄦ牸 -->
+      <el-col :span="8">
+        <div class="chart-card">
+          <div class="chart-title">鐗╂枡涓存湡淇℃伅</div>
+          <!-- 涓存湡鐗╂枡琛ㄦ牸 -->
+          <div class="expiration-table-container">
+            <el-table 
+              :data="expirationTableData" 
+              border 
+              stripe 
+              style="width: 100%;"
+              :empty-text="emptyText"
+            >
+              <el-table-column prop="materielCode" label="鐗╂枡缂栫爜" align="center" />
+              <el-table-column prop="materielName" label="鐗╂枡鍚嶇О" align="center" show-overflow-tooltip />
+              <el-table-column prop="batchNo" label="鎵规鍙�" align="center" />
+              <el-table-column prop="validDate" label="鏈夋晥鏈�" align="center" />
+              <el-table-column label="涓存湡绛夌骇" align="center">
+                <template #default="{ row }">
+                  <span :class="getExpireLevelClass(row.expireLevel)">{{ row.expireLevel }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column prop="daysToExpiration" label="涓存湡澶╂暟" align="center">
+                <template #default="{ row }">
+                  <span :class="row.daysToExpiration < 0 ? 'text-red' : ''">
+                    {{ row.daysToExpiration < 0 ? '宸茶繃鏈�' : `${row.daysToExpiration}澶ー }}
+                  </span>
+                </template>
+              </el-table-column>
+              <!-- <el-table-column prop="barcode" label="鏉$爜" align="center" /> -->
+              <el-table-column prop="stockQuantity" label="搴撳瓨鏁伴噺" align="center" />
+              <el-table-column prop="locationCode" label="搴撲綅" align="center" />
+              <el-table-column prop="palletCode" label="鎵樼洏缂栧彿" align="center" />
+            </el-table>
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="8">
+        <div class="chart-card">
+          <div class="chart-title">杩�7鏃ュ嚭鍏ュ簱瓒嬪娍锛堝浘鍍忓寲璧板娍锛�</div>
+          <div ref="stockTrendRef" class="chart-container"></div>
+        </div>
+      </el-col>
+    </el-row>
+
+    <!-- 鍥捐〃鍖哄煙锛堢浜岃锛� -->
+    <el-row :gutter="20" class="chart-row">
+    </el-row>
+
+    <!-- 琛ㄦ牸鍖哄煙 - 缁戝畾鍚庣浣滀笟鏁版嵁 -->
+    <el-row :gutter="20" class="table-row" width="100%">
+      <el-col :span="24">
+        <div class="table-card">
+          <div class="table-title">瀹炴椂浣滀笟鐩戞帶</div>
+          <el-table :data="showTaskList" border style="width: 100%;">
+            <el-table-column prop="taskNum" label="浠诲姟鍙�" />
+            <el-table-column prop="taskStatus" label="浠诲姟鐘舵��" >
+              <template #default="{ row }">
+                <span class="task-status" :class="getStatusClass(row.taskStatus)">{{ getTaskStatusText(row.taskStatus) }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="taskType" label="浠诲姟绫诲瀷" >
+              <template #default="{ row }">
+                <span class="task-type" :class="getTypeClass(row.taskType)">{{ getTaskTypeText(row.taskType) }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="palletCode" label="鎵樼洏缂栧彿" />
+            <el-table-column prop="sourceAddress" label="璧风偣浣嶇疆"/>
+            <el-table-column prop="targetAddress" label="缁堢偣浣嶇疆"/>
+            <el-table-column prop="createDate" label="鍒涘缓鏃堕棿"/>
+          </el-table>
+          <div class="table-pagination">
+            <el-pagination layout="prev, pager, next, jumper" :current-page="1" :total="50" />
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
 </template>
 
-<script>
-import { ref, reactive } from 'vue'
+<script setup>
+import { ref, onMounted, onUnmounted, nextTick, computed } from 'vue';
+import * as echarts from 'echarts';
+import http from "@/api/http.js";
+// 琛ュ厖缂哄け鐨勫浘鏍囧鍏�
+import { Box, Document, Download, Upload } from '@element-plus/icons-vue';
+// 琛ュ厖ElMessage瀵煎叆
+import { ElMessage } from 'element-plus';
 
-export default {
-  setup() {
-    return {
+// 鍝嶅簲寮忔暟鎹�
+const month = ref('month');
+const orderType = ref('return');
 
+// 鍚庣杩斿洖鏁版嵁锛堝搷搴斿紡锛�
+const bigscreendata = ref({
+  totalStockQuantity: 0,
+  unOutBoundOrderCount: 0,
+  dailyCompletionRate: 0,
+  unhandledExceptionCount: 0,
+  locationUtilizationRate: 0,
+  inStockPallet: 0,
+  freeStockPallet: 0,
+  dailyInOutBoundList: [],
+  taskList: [],
+  inboundCount: 0,
+  outboundCount: 0,
+  inventoryLocationDist: [], 
+  exceptionTypeTrend: [],    
+  nearExpirationList: []      // 鏀逛负鏁扮粍绫诲瀷锛屽尮閰嶆柊鏁版嵁缁撴瀯
+});
+
+// 涓存湡琛ㄦ牸鏁版嵁锛堣绠楀睘鎬э細閫傞厤鏂扮殑鏁扮粍鏁版嵁缁撴瀯锛�
+const expirationTableData = computed(() => {
+  // 鏂版暟鎹粨鏋勬槸鏁扮粍锛岀洿鎺ュ彇鏁扮粍骞跺鐞嗙┖鍊�
+  const expirationList = bigscreendata.value.nearExpirationList || [];
+
+  // 绗竴姝ワ細鍘婚噸 - 鎸� 鐗╂枡缂栫爜+鎵规鍙�+鎵樼洏缂栧彿+鏈夋晥鏈� 鍞竴鏍囪瘑鍘婚噸
+  const uniqueMap = new Map();
+  expirationList.forEach(item => {
+    // 鎷兼帴鍞竴鏍囪瘑锛堝鐞嗙┖鍊硷紝閬垮厤鍥犵┖鍊煎鑷撮噸澶嶅垽鏂敊璇級
+    const uniqueKey = [
+      item.materielCode || '鏈煡缂栫爜',
+      item.batchNo || '鏈煡鎵规',
+      item.palletCode || '鏈煡鎵樼洏',
+    ].join('|'); // 鐢ㄧ壒娈婂瓧绗﹀垎闅旓紝閬垮厤瀛楁鍊兼嫾鎺ュ悗娣锋穯
+
+    // 鍙繚鐣欑涓�娆″嚭鐜扮殑璁板綍
+    if (!uniqueMap.has(uniqueKey)) {
+      uniqueMap.set(uniqueKey, item);
     }
+  });
+
+  // 鎻愬彇鍘婚噸鍚庣殑鏁扮粍
+  const uniqueExpirationList = Array.from(uniqueMap.values());
+
+  // 绗簩姝ワ細閬嶅巻鍘婚噸鍚庣殑鏁版嵁锛岃ˉ鍏呬复鏈熺瓑绾у拰榛樿鍊�
+  return uniqueExpirationList.map(item => {
+    // 鎻愬彇褰撳墠鐗╂枡鐨勪复鏈熷ぉ鏁帮紙鏍稿績瀛楁锛�
+    const daysToExpiration = item.daysToExpiration || 0;
+    // 璁$畻涓存湡绛夌骇
+    let expireLevel = '';
+    if (daysToExpiration < 0) {
+      expireLevel = '宸茶繃鏈�';
+    } else if (daysToExpiration <= 7) {
+      expireLevel = '7澶╁唴涓存湡';
+    } else if (daysToExpiration <= 15) {
+      expireLevel = '15澶╁唴涓存湡';
+    } else if (daysToExpiration <= 30) {
+      expireLevel = '30澶╁唴涓存湡';
+    } else {
+      expireLevel = '30澶╀互涓�';
+    }
+
+    return {
+      materielCode: item.materielCode || '鏈煡缂栫爜',
+      materielName: item.materielName || '鏈煡鍚嶇О',
+      batchNo: item.batchNo || '鏈煡鎵规',
+      validDate: item.validDate || '鏈煡鏈夋晥鏈�',
+      daysToExpiration: daysToExpiration,
+      expireLevel: expireLevel,
+      stockQuantity: item.stockQuantity || 0,
+      locationCode: item.locationCode || '鏈煡搴撲綅',
+      palletCode: item.palletCode || '鏈煡鎵樼洏',
+      unit: item.unit || 'PCS'
+    };
+  });
+});
+
+// 绌烘暟鎹彁绀烘枃鏈紙淇鍒ゆ柇閫昏緫锛�
+const emptyText = computed(() => {
+  const expirationList = bigscreendata.value.nearExpirationList || [];
+  // 鏁扮粍闀垮害涓�0鏃舵樉绀虹┖鎻愮ず锛屽惁鍒欎笉鏄剧ず锛堝師閫昏緫鍐欏弽浜嗭級
+  return expirationList.length === 0 ? '鏆傛棤涓存湡鐗╂枡鏁版嵁' : '';
+});
+
+// 鑾峰彇涓存湡绛夌骇鏍峰紡绫�
+const getExpireLevelClass = (level) => {
+  switch(level) {
+    case '宸茶繃鏈�': return 'expire-level expired';
+    case '7澶╁唴涓存湡': return 'expire-level urgent';
+    case '15澶╁唴涓存湡': return 'expire-level warning';
+    case '30澶╁唴涓存湡': return 'expire-level normal';
+    case '30澶╀互涓�': return 'expire-level low';
+    default: return 'expire-level default';
   }
-}
+};
+
+const taskStatusMap = {
+  100: "鏂板缓",
+  105: "宸插彂閫�",
+  200: "鍫嗗灈鏈哄緟鎵ц",
+  210: "鍫嗗灈鏈烘墽琛屼腑",
+  220: "鍫嗗灈鏈哄畬鎴�",
+  400: "杈撻�佺嚎寰呮墽琛�",
+  410: "杈撻�佺嚎鎵ц涓�",
+  420: "杈撻�佺嚎瀹屾垚",
+  300: "AGV寰呮墽琛�",
+  310: "AGV鎵ц涓�",
+  315: "AGV鍙栬揣涓�",
+  320: "AGV寰呯户缁墽琛�",
+  325: "AGV鏀捐揣涓�",
+  330: "AGV瀹屾垚",
+  900: "浠诲姟瀹屾垚",
+  970: "浠诲姟鎸傝捣",
+  980: "浠诲姟鍙栨秷",
+  990: "浠诲姟寮傚父",
+  110: "鎻愬崌鏈烘墽琛屼腑"
+};
+
+// 鑾峰彇浠诲姟鐘舵�佹枃鏈�
+const getTaskStatusText = (statusNum) => {
+  if (statusNum === undefined || statusNum === null || isNaN(statusNum)) {
+    return "鏈煡鐘舵��";
+  }
+  return taskStatusMap[statusNum] || "鏈煡鐘舵��";
+};
+
+// 琛ㄦ牸鏄剧ず鐨勪换鍔″垪琛紙杞挱鐢級
+const showTaskList = ref([]);
+const currentTaskIndex = ref(5);
+let taskCarouselTimer = null;
+
+// 鏁板瓧鏍煎紡鍖栵紙鍗冨垎浣嶅垎闅旓級
+const formatNumber = (num) => {
+  if (!num) return '0';
+  return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+};
+
+// 浠诲姟鍒楄〃杞挱閫昏緫
+const startTaskCarousel = () => {
+  if (taskCarouselTimer) clearInterval(taskCarouselTimer);
+
+  const totalTask = bigscreendata.value.taskList.length;
+  if (totalTask <= 5) {
+    showTaskList.value = [...bigscreendata.value.taskList];
+    return;
+  }
+
+  taskCarouselTimer = setInterval(() => {
+    showTaskList.value.push(bigscreendata.value.taskList[currentTaskIndex.value]);
+    showTaskList.value.shift();
+    currentTaskIndex.value++;
+    if (currentTaskIndex.value >= totalTask) {
+      currentTaskIndex.value = 0;
+    }
+  }, 5000);
+};
+
+// 鑾峰彇浠诲姟绫诲瀷鏂囨湰
+const getTaskTypeText = (taskTypeNum) => {
+  if (!taskTypeNum || isNaN(taskTypeNum)) return "鏈煡绫诲瀷";
+  
+  if (taskTypeNum >= 500 && taskTypeNum < 900) return "鍏ュ簱";
+  if (taskTypeNum >= 100 && taskTypeNum < 500) return "鍑哄簱";
+  if (taskTypeNum >= 900 && taskTypeNum < 1000) return "绉诲簱";
+  return "鍏朵粬浣滀笟";
+};
+
+// 鑾峰彇浠诲姟鐘舵�佹牱寮忕被
+const getStatusClass = (statusNum) => {
+  if (statusNum === undefined || statusNum === null || isNaN(statusNum)) {
+    return "status-unknown";
+  }
+  
+  if (statusNum >= 900) return "status-completed";
+  if (statusNum >= 400) return "status-processing";
+  if (statusNum >= 300) return "status-processing";
+  if (statusNum >= 200) return "status-processing";
+  if (statusNum >= 100) return "status-pending";
+  if (statusNum === 970) return "status-suspended";
+  if (statusNum === 980) return "status-canceled";
+  if (statusNum === 990) return "status-error";
+  
+  return "status-unknown";
+};
+
+// 鑾峰彇浠诲姟绫诲瀷鏍峰紡绫�
+const getTypeClass = (taskTypeNum) => {
+  if (!taskTypeNum || isNaN(taskTypeNum)) return "type-unknown";
+  
+  if (taskTypeNum >= 500 && taskTypeNum < 900) return "type-inbound";
+  if (taskTypeNum >= 100 && taskTypeNum < 500) return "type-outbound";
+  if (taskTypeNum >= 900 && taskTypeNum < 1000) return "type-transfer";
+  
+  return "type-other";
+};
+
+// 鑾峰彇鍚庣澶у睆鏁版嵁
+const fetchBigGreenData = async () => {
+  try {
+    const res = await http.get('/api/BigScreen/GetBigGreenData');
+    console.log('澶у睆鏁版嵁', res);
+    
+    bigscreendata.value = res.data || res;
+    // 鏁版嵁鏇存柊鍚庡埛鏂板浘琛ㄥ拰琛ㄦ牸
+    nextTick(() => {
+      const total = bigscreendata.value.taskList.length;
+      showTaskList.value = total >=5 
+        ? [...bigscreendata.value.taskList.slice(0,5)] 
+        : [...bigscreendata.value.taskList];
+      startTaskCarousel();
+      refreshCharts();
+    });
+  } catch (error) {
+    ElMessage.error('鏁版嵁鑾峰彇澶辫触锛岃妫�鏌ュ悗绔帴鍙f槸鍚︽甯�');
+    console.error('鏁版嵁鑾峰彇澶辫触锛�', error);
+  }
+};
+
+// 澶囩敤妯℃嫙鏁版嵁
+const operationList = ref([
+  { opNo: 'JW251224001', opType: '鍏ュ簱', operator: '寮犱笁', startTime: '15:30:22', status: '澶勭悊涓�' },
+  { opNo: 'CK251224002', opType: '鍑哄簱', operator: '鏉庡洓', startTime: '15:25:10', status: '宸插畬鎴�' },
+  { opNo: 'PD251224003', opType: '鐩樼偣', operator: '鐜嬩簲', startTime: '15:20:05', status: '寰呯‘璁�' },
+  { opNo: 'SC251224005', opType: '涓婃灦', operator: '瀛欎竷', startTime: '15:10:18', status: '寮傚父' }
+]);
+
+// 鍥捐〃瀹瑰櫒Ref
+const inventoryPieRef = ref(null);
+const stockTrendRef = ref(null);
+const locationRateRef = ref(null);
+const exceptionTrendRef = ref(null);
+
+// 鍥捐〃瀹炰緥锛堝叏灞�绠$悊锛�
+let inventoryPieChart = null;
+let stockTrendChart = null;
+let locationRateChart = null;
+let exceptionTrendChart = null;
+
+// 鍒濆鍖栧簱瀛樺簱浣嶅垎甯冮ゼ鍥�
+const initInventoryPie= () => {
+  if (!inventoryPieRef.value) return;
+
+  if (inventoryPieChart) {
+    inventoryPieChart.dispose();
+  }
+
+  inventoryPieChart = echarts.init(inventoryPieRef.value);
+  const locationData = bigscreendata.value.inventoryLocationDist.length 
+    ? bigscreendata.value.inventoryLocationDist
+    : [
+        { value: 48.7, name: '甯告俯鍖篈鍖�', itemStyle: { color: '#5470c6' } },
+        { value: 29.2, name: '鍐疯棌鍖築鍖�', itemStyle: { color: '#91cc75' } },
+        { value: 21.9, name: '淇濈◣鍖篊鍖�', itemStyle: { color: '#fac858' } },
+        { value: 2.2, name: '娈嬫鍝佸尯D鍖�', itemStyle: { color: '#ee6666' } }
+      ];
+
+  const option = {
+    tooltip: {
+      trigger: 'item',
+      formatter: '{a} <br/>{b}: {c}%'
+    },
+    legend: {
+      bottom: 0,
+      left: 'center',
+      data: locationData.map(item => item.name)
+    },
+    series: [{
+      name: '搴撳瓨搴撲綅鍒嗗竷',
+      type: 'pie',
+      radius: ['40%', '70%'],
+      center: ['50%', '40%'],
+      avoidLabelOverlap: false,
+      itemStyle: {
+        borderRadius: 10,
+        borderColor: '#fff',
+        borderWidth: 2
+      },
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: 20,
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: locationData
+    }]
+  };
+
+  inventoryPieChart.setOption(option);
+  return inventoryPieChart;
+};
+
+// 鍒濆鍖栬繎7鏃ュ嚭鍏ュ簱瓒嬪娍鍥�
+const initStockTrend = () => {
+  if (!stockTrendRef.value) return;
+
+  if (stockTrendChart) {
+    stockTrendChart.dispose();
+  }
+
+  stockTrendChart = echarts.init(stockTrendRef.value);
+  const trendData = bigscreendata.value.dailyInOutBoundList;
+
+  const maxInbound = trendData.length ? Math.max(...trendData.map(item => item.dailyInboundQuantity || 0)) : 0;
+  const maxOutbound = trendData.length ? Math.max(...trendData.map(item => item.dailyOutboundQuantity || 0)) : 0;
+  const maxValue = Math.max(maxInbound, maxOutbound);
+
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'cross'
+      }
+    },
+    legend: {
+      data: ['鍏ュ簱閲�', '鍑哄簱閲�'],
+      top: 10
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      top: '15%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      boundaryGap: true,
+      data: trendData.map(item => item.date)
+    },
+    yAxis: {
+      type: 'value',
+      name: '鏁伴噺',
+      min: 0,
+      max: maxValue > 0 ? Math.ceil(maxValue * 1.2) : 10
+    },
+    series: [
+      {
+        name: '鍏ュ簱閲�',
+        type: 'bar',
+        barWidth: '30%',
+        data: trendData.map(item => item.dailyInboundQuantity),
+        itemStyle: { 
+          color: '#52c41a',
+          borderRadius: [4, 4, 0, 0]
+        }
+      },
+      {
+        name: '鍑哄簱閲�',
+        type: 'bar',
+        barWidth: '30%',
+        data: trendData.map(item => item.dailyOutboundQuantity),
+        itemStyle: { 
+          color: '#1890ff',
+          borderRadius: [4, 4, 0, 0]
+        }
+      }
+    ]
+  };
+
+  stockTrendChart.setOption(option);
+  return stockTrendChart;
+};
+
+// 鍒濆鍖栧簱浣嶅埄鐢ㄧ巼鐜舰鍥�
+const initLocationRate = () => {
+  if (!locationRateRef.value) return;
+
+  if (locationRateChart) {
+    locationRateChart.dispose();
+  }
+
+  locationRateChart = echarts.init(locationRateRef.value);
+  const utilizationRate = bigscreendata.value.locationUtilizationRate || 0;
+  const freeRate = 100 - utilizationRate;
+
+  const option = {
+    tooltip: {
+      trigger: 'item',
+      formatter: '{b}: {c}%'
+    },
+    legend: {
+      bottom: 0,
+      left: 'center',
+      data: ['宸插崰鐢ㄥ簱浣�', '绌洪棽搴撲綅'],
+      textStyle: { fontSize: 12, color: '#666' }
+    },
+    graphic: [
+      {
+        type: 'text',
+        left: 'right',
+        top: '10%',
+        style: {
+          text: `${utilizationRate}%`,
+          fontSize: 24,
+          fontWeight: 'bold',
+          fill: '#333'
+        }
+      },
+      {
+        type: 'text',
+        left: 'right',
+        top: '25%',
+        style: {
+          text: '搴撲綅鍒╃敤鐜�',
+          fontSize: 14,
+          fill: '#666'
+        }
+      }
+    ],
+    series: [
+      {
+        type: 'pie',
+        radius: ['50%', '70%'],
+        center: ['40%', '50%'],
+        avoidLabelOverlap: false,
+        label: { show: false },
+        labelLine: { show: false },
+        data: [
+          { value: utilizationRate, name: '宸插崰鐢ㄥ簱浣�', itemStyle: { color: '#1890ff' } },
+          { value: freeRate, name: '绌洪棽搴撲綅', itemStyle: { color: '#e5e9f2' } }
+        ]
+      }
+    ]
+  };
+
+  locationRateChart.setOption(option);
+  return locationRateChart;
+};
+
+// 鍒濆鍖栧紓甯哥被鍨嬬粺璁¤秼鍔垮浘
+const initExceptionTrend = () => {
+  if (!exceptionTrendRef.value) return;
+
+  if (exceptionTrendChart) {
+    exceptionTrendChart.dispose();
+  }
+
+  exceptionTrendChart = echarts.init(exceptionTrendRef.value);
+  const exceptionData = bigscreendata.value.exceptionTypeTrend.length
+    ? bigscreendata.value.exceptionTypeTrend
+    : {
+        dates: ['12-18', '12-19', '12-20', '12-21', '12-22', '12-23', '12-24'],
+        stockShort: [10, 11, 9, 12, 10, 13, 12],
+        orderTimeout: [8, 9, 7, 8, 7, 9, 8],
+        locationException: [4, 5, 2, 4, 3, 5, 4],
+        checkDiff: [2, 3, 1, 2, 1, 3, 2]
+      };
+
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'cross'
+      }
+    },
+    legend: {
+      data: ['搴撳瓨涓嶈冻', '璁㈠崟瓒呮椂', '搴撲綅寮傚父', '鐩樼偣宸紓'],
+      top: 10
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      top: '15%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: exceptionData.dates
+    },
+    yAxis: {
+      type: 'value',
+      name: '寮傚父鏁伴噺'
+    },
+    series: [
+      {
+        name: '搴撳瓨涓嶈冻',
+        type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        data: exceptionData.stockShort,
+        itemStyle: { color: '#ff4d4f' },
+        lineStyle: { width: 3 }
+      },
+      {
+        name: '璁㈠崟瓒呮椂',
+        type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        data: exceptionData.orderTimeout,
+        itemStyle: { color: '#faad14' },
+        lineStyle: { width: 3 }
+      },
+      {
+        name: '搴撲綅寮傚父',
+        type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        data: exceptionData.locationException,
+        itemStyle: { color: '#722ed1' },
+        lineStyle: { width: 3 }
+      },
+      {
+        name: '鐩樼偣宸紓',
+        type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        data: exceptionData.checkDiff,
+        itemStyle: { color: '#13c2c2' },
+        lineStyle: { width: 3 }
+      }
+    ]
+  };
+
+  exceptionTrendChart.setOption(option);
+  return exceptionTrendChart;
+};
+
+// 鍒锋柊鎵�鏈夊浘琛�
+const refreshCharts = () => {
+  const charts = [
+    initStockTrend,
+    initLocationRate
+  ];
+
+  charts.forEach(initFunc => {
+    const chart = initFunc();
+    if (chart) {
+      chart.resize();
+    }
+  });
+};
+
+// 绐楀彛澶у皬鍙樺寲鐩戝惉
+const handleResize = () => {
+  const charts = [
+    inventoryPieChart,
+    stockTrendChart,
+    locationRateChart,
+    exceptionTrendChart
+  ];
+
+  charts.forEach(chart => {
+    if (chart) {
+      chart.resize();
+    }
+  });
+};
+
+// 缁勪欢鎸傝浇鏃�
+onMounted(() => {
+  // 鍏堣幏鍙栧悗绔暟鎹�
+  fetchBigGreenData();
+  // 鍒濆鍖栧浘琛紙纭繚DOM宸叉覆鏌擄級
+  nextTick(() => {
+    initInventoryPie();
+    initStockTrend();
+    initLocationRate();
+    initExceptionTrend();
+    window.addEventListener('resize', handleResize);
+  });
+});
+
+// 缁勪欢鍗歌浇鏃�
+onUnmounted(() => {
+  const charts = [
+    inventoryPieChart,
+    stockTrendChart,
+    locationRateChart,
+    exceptionTrendChart
+  ];
+
+  charts.forEach(chart => {
+    if (chart) {
+      chart.dispose();
+    }
+  });
+  clearInterval(taskCarouselTimer);
+  window.removeEventListener('resize', handleResize);
+});
 </script>
 
 <style scoped>
-.title {
-  line-height: 70vh;
+.wms-dashboard {
+  padding: 24px;
+  background: linear-gradient(135deg, #f0f2f5 0%, #e6e9f0 100%);
+  min-height: 100vh;
+  box-sizing: border-box;
+  font-family: "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+  background: white;
+  padding: 15px 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.stats-card-row,
+.chart-row,
+.table-row {
+  margin-bottom: 20px;
+}
+
+/* 鏍稿績鏍峰紡锛氬崱鐗囧熀纭�鏍峰紡 */
+.stats-card, .chart-card, .table-card {
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
+  border: 1px solid #ebeef5;
+}
+
+.stats-card {
+  height: 140px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  padding: 20px 15px;
+  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  position: relative;
+  overflow: hidden;
+  background: linear-gradient(145deg, #ffffff 0%, #f9fafc 100%);
+}
+
+.stats-card:hover {
+  transform: translateY(-6px) scale(1.02);
+  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
+  border-color: #409eff;
+  background: linear-gradient(145deg, #ffffff 0%, #f0f2f5 100%);
+}
+
+.metric-icon {
+  width: 56px;
+  height: 56px;
+  border-radius: 16px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: white;
+  margin-bottom: 12px;
+  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+  transition: all 0.3s ease;
+}
+
+.card-title {
+  font-size: 15px;
+  color: #606266;
+  margin-bottom: 10px;
+  font-weight: 500;
+  letter-spacing: 0.5px;
+}
+
+.card-value {
+  font-size: 32px;
+  font-weight: 700;
+  margin: 8px 0 4px;
+  color: #2c3e50;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+  line-height: 1.2;
+  background: linear-gradient(to right, #409eff, #36cfc9);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+}
+
+.card-change {
+  margin-top: 3px;
+}
+
+.chart-card {
+  height: 400px;
+  padding: 24px;
+  display: flex;
+  flex-direction: column;
+  background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);
+  border: none;
+  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
+  border-radius: 12px;
+  transition: all 0.3s ease;
+  overflow: hidden;
+  position: relative;
+}
+
+.chart-card:hover {
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
+  transform: translateY(-2px);
+}
+
+.chart-container {
+  width: 100%;
+  height: 100%;
+  min-height: 300px;
+  flex: 1;
+  position: relative;
+  border-radius: 8px;
+  background: rgba(255, 255, 255, 0.7);
+  backdrop-filter: blur(5px);
+  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.03);
+}
+
+/* 涓存湡鐗╂枡琛ㄦ牸瀹瑰櫒鏍峰紡 */
+.expiration-table-container {
+  width: 100%;
+  height: 100%;
+  min-height: 300px;
+  flex: 1;
+  overflow-y: auto;
+}
+
+/* 涓存湡绛夌骇鏍囩鏍峰紡 */
+.expire-level {
+  padding: 2px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 500;
+}
+.expire-level.expired {
+  background-color: #fff2f0;
+  color: #ff4d4f;
+  border: 1px solid #ffccc7;
+}
+.expire-level.urgent {
+  background-color: #fff7e6;
+  color: #fa8c16;
+  border: 1px solid #ffd591;
+}
+.expire-level.warning {
+  background-color: #f6ffed;
+  color: #52c41a;
+  border: 1px solid #b7eb8f;
+}
+.expire-level.normal {
+  background-color: #e6f7ff;
+  color: #1890ff;
+  border: 1px solid #91d5ff;
+}
+.expire-level.low {
+  background-color: #f0f2f5;
+  color: #666666;
+  border: 1px solid #d9d9d9;
+}
+.expire-level.default {
+  background-color: #fafafa;
+  color: #8c8c8c;
+  border: 1px solid #e8e8e8;
+}
+
+/* 宸茶繃鏈熸枃瀛楃孩鑹� */
+.text-red {
+  color: #ff4d4f;
+  font-weight: 500;
+}
+
+.chart-title,
+.table-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 24px;
+  font-size: 18px;
+  font-weight: 600;
+  color: #2c3e50;
+  padding-left: 12px;
+  border-left: 4px solid #409eff;
+  position: relative;
+  letter-spacing: 0.5px;
+}
+
+.view-btn {
+  font-size: 12px;
+}
+
+.table-card {
+  padding: 24px;
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
+  border: 1px solid rgba(0, 0, 0, 0.04);
+  overflow: hidden;
+  transition: all 0.3s ease;
+}
+
+.table-card:hover {
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
+  transform: translateY(-2px);
+}
+
+.table-pagination {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  margin-top: 20px;
+  padding-top: 15px;
+  border-top: 1px solid #ebeef5;
+}
+
+/* 琛ㄦ牸鏍峰紡浼樺寲 */
+:deep(.el-table) {
+  border-radius: 6px;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+}
+
+:deep(.el-table th) {
+  background-color: #f5f7fa;
+  color: #606266;
+  font-weight: 600;
+  padding: 12px 0;
+}
+
+:deep(.el-table td) {
+  padding: 12px 0;
+}
+
+:deep(.el-table--border) {
+  border-radius: 6px;
+}
+
+:deep(.el-table--border::after) {
+  display: none;
+}
+
+:deep(.el-table--group::after) {
+  display: none;
+}
+
+:deep(.el-table::before) {
+  display: none;
+}
+
+:deep(.el-table__fixed-right::before) {
+  display: none;
+}
+
+:deep(.el-table__fixed::before) {
+  display: none;
+}
+
+/* 鍒嗛〉鍣ㄦ牱寮忎紭鍖� */
+:deep(.el-pagination) {
+  margin-top: 10px;
+}
+
+:deep(.el-pagination .btn-prev),
+:deep(.el-pagination .btn-next),
+:deep(.el-pagination .el-pager li) {
+  border-radius: 4px;
+  margin: 0 2px;
+  transition: all 0.3s;
+}
+
+:deep(.el-pagination .btn-prev:hover),
+:deep(.el-pagination .btn-next:hover),
+:deep(.el-pagination .el-pager li:hover) {
+  transform: translateY(-2px);
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+:deep(.el-pagination .el-pager li.active) {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  color: #fff;
+}
+
+/* 浠诲姟鐘舵�佹牱寮� */
+.task-status {
+  display: inline-block;
+  padding: 6px 12px;
+  border-radius: 20px;
+  font-size: 13px;
+  font-weight: 500;
   text-align: center;
-  font-size: 28px;
-  color: orange;
+  min-width: 80px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+  transition: all 0.2s ease;
+  letter-spacing: 0.5px;
+}
+
+.status-pending {
+  background-color: rgba(64, 158, 255, 0.1);
+  color: #409eff;
+  border: 1px solid rgba(64, 158, 255, 0.2);
+}
+
+.status-processing {
+  background-color: rgba(103, 194, 58, 0.1);
+  color: #67c23a;
+  border: 1px solid rgba(103, 194, 58, 0.2);
+}
+
+.status-completed {
+  background-color: rgba(103, 194, 58, 0.1);
+  color: #67c23a;
+  border: 1px solid rgba(103, 194, 58, 0.2);
+}
+
+.status-suspended {
+  background-color: rgba(230, 162, 60, 0.1);
+  color: #e6a23c;
+  border: 1px solid rgba(230, 162, 60, 0.2);
+}
+
+.status-canceled {
+  background-color: rgba(144, 147, 153, 0.1);
+  color: #909399;
+  border: 1px solid rgba(144, 147, 153, 0.2);
+}
+
+.status-error {
+  background-color: rgba(245, 108, 108, 0.1);
+  color: #f56c6c;
+  border: 1px solid rgba(245, 108, 108, 0.2);
+}
+
+.status-unknown {
+  background-color: rgba(144, 147, 153, 0.1);
+  color: #909399;
+  border: 1px solid rgba(144, 147, 153, 0.2);
+}
+
+/* 浠诲姟绫诲瀷鏍峰紡 */
+.task-type {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  padding: 5px 12px;
+  border-radius: 16px;
+  font-size: 13px;
+  font-weight: 600;
+  text-align: center;
+  min-width: 70px;
+  position: relative;
+  overflow: hidden;
+  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  letter-spacing: 0.5px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+@keyframes pulse {
+  0% {
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  }
+  50% {
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12);
+  }
+  100% {
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  }
+}
+
+.task-type::before {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
+  transform: translateX(-100%);
+  transition: transform 0.8s cubic-bezier(0.19, 1, 0.22, 1);
+}
+
+.task-type:hover::before {
+  transform: translateX(0);
+}
+
+.task-type:hover {
+  transform: translateY(-3px);
+  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
+}
+
+.task-type::after {
+  content: "";
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 3px;
+  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
+  transform: scaleX(0);
+  transform-origin: center;
+  transition: transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
+}
+
+.task-type:hover::after {
+  transform: scaleX(1);
+}
+
+.type-inbound:hover {
+  box-shadow: 0 5px 15px rgba(19, 194, 194, 0.4);
+}
+
+.type-outbound:hover {
+  box-shadow: 0 5px 15px rgba(47, 84, 235, 0.4);
+}
+
+.type-transfer:hover {
+  box-shadow: 0 5px 15px rgba(250, 140, 22, 0.4);
+}
+
+.type-other:hover {
+  box-shadow: 0 5px 15px rgba(114, 46, 209, 0.4);
+}
+
+.type-unknown:hover {
+  box-shadow: 0 5px 15px rgba(89, 89, 89, 0.4);
+}
+
+.type-inbound {
+  background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
+  color: white;
+  box-shadow: 0 3px 5px rgba(19, 194, 194, 0.3);
+  animation: pulse 2s infinite;
+}
+
+.type-outbound {
+  background: linear-gradient(135deg, #2f54eb 0%, #597ef7 100%);
+  color: white;
+  box-shadow: 0 3px 5px rgba(47, 84, 235, 0.3);
+  animation: pulse 2.5s infinite;
+}
+
+.type-transfer {
+  background: linear-gradient(135deg, #fa8c16 0%, #ffc53d 100%);
+  color: white;
+  box-shadow: 0 3px 5px rgba(250, 140, 22, 0.3);
+  animation: pulse 3s infinite;
+}
+
+.type-other {
+  background: linear-gradient(135deg, #722ed1 0%, #b37feb 100%);
+  color: white;
+  box-shadow: 0 3px 5px rgba(114, 46, 209, 0.3);
+  animation: pulse 2.2s infinite;
+}
+
+.type-unknown {
+  background: linear-gradient(135deg, #595959 0%, #8c8c8c 100%);
+  color: white;
+  box-shadow: 0 3px 5px rgba(89, 89, 89, 0.3);
+  animation: pulse 2.8s infinite;
+}
+
+.btn-group {
+  margin-left: 10px;
 }
 </style>
\ No newline at end of file

--
Gitblit v1.9.3