From b07472f884708a6bfdf63d999004bbf0bb5f00a8 Mon Sep 17 00:00:00 2001
From: huangxiaoqiang <huangxiaoqiang@hnkhzn.com>
Date: 星期一, 17 十一月 2025 17:12:57 +0800
Subject: [PATCH] 新增分单功能、二维码打印及物料供应商管理页面

---
 项目代码/WMS/WIDESEA_WMSClient/src/views/Home.vue | 1471 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 1,287 insertions(+), 184 deletions(-)

diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WIDESEA_WMSClient/src/views/Home.vue" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WIDESEA_WMSClient/src/views/Home.vue"
index 832503f..00b689d 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WIDESEA_WMSClient/src/views/Home.vue"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WIDESEA_WMSClient/src/views/Home.vue"
@@ -1,49 +1,242 @@
 <template>
   <div class="dashboard-container">
+    <!-- 鏁版嵁鎬昏閮ㄥ垎 -->
     <div class="overview-section">
-      <div class="data-overview">
-        <h3>鏁版嵁鎬昏</h3>
-        <div class="metrics">
-          <div class="metric-item" v-for="(item, index) in dataMetrics" :key="index">
-            <div class="metric-name">{{ item.name }}</div>
-            <div class="metric-value">{{ item.value }}</div>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="charts-section">
       <el-row :gutter="20">
-        <el-col :lg="12">
-          <div class="chart-container">
-            <h3>鍑哄簱閲�</h3>
-            <div v-if="loading" class="loading">鍔犺浇涓�...</div>
-            <div v-else-if="error" class="error">鏁版嵁鍔犺浇澶辫触</div>
-            <div v-else-if="!chartData.outbound.values.length" class="no-data">鏆傛棤鍑哄簱鏁版嵁</div>
-            <div v-else ref="outboundChart" class="chart"></div>
-          </div>
-        </el-col>
-        <el-col :lg="12">
-          <div class="chart-container">
-            <h3>鍏ュ簱閲�</h3>
-            <div v-if="loading" class="loading">鍔犺浇涓�...</div>
-            <div v-else-if="error" class="error">鏁版嵁鍔犺浇澶辫触</div>
-            <div v-else-if="!chartData.inbound.values.length" class="no-data">鏆傛棤鍏ュ簱鏁版嵁</div>
-            <div v-else ref="inboundChart" class="chart"></div>
+        <el-col :lg="24">
+          <div class="data-overview">
+            <div class="overview-header">
+              <div class="header-left">
+                <h3 class="title">
+                  <i class="el-icon-data-analysis header-icon"></i>
+                  鏁版嵁鎬昏
+                </h3>
+              </div>
+              <div class="header-right">
+                <div class="time-range">鏁版嵁鏇存柊浜�: {{ currentTime }}</div>
+                <div>
+                  <el-button type="primary" size="small" :icon="Refresh" @click="handleRefresh" :loading="refreshing"
+                    class="refresh-btn">
+                    鍒锋柊鏁版嵁
+                  </el-button>
+                </div>
+              </div>
+            </div>
+            <div class="metrics-grid">
+              <div class="metric-card" v-for="(item, index) in dataMetrics" :key="`metric-${index}-${refreshKey}`"
+                :class="getMetricCardClass(item.name)">
+                <div class="metric-content">
+                  <div class="metric-icon-wrapper">
+                    <i :class="getMetricIcon(item.name)"></i>
+                  </div>
+                  <div class="metric-info">
+                    <div class="metric-name">{{ item.name }}</div>
+                    <div class="metric-value">{{ formatNumber(item.value) }}</div>
+                    <div class="metric-compare" v-if="item.compare !== undefined">
+                      <span v-if="item.name == '搴撳瓨鎬婚噺'">
+
+                      </span>
+                      <span :class="getCompareClass(item.compare)" v-else>
+                        <i :class="getCompareIcon(item.compare)"></i>
+                        {{ formatCompareValue(item.compare) }}
+                      </span>
+                      <!-- <span class="compare-label">杈冩槰鏃�</span> -->
+                      <span class="compare-label">{{ text(item.name) }}</span>
+
+                    </div>
+                  </div>
+                </div>
+                <div class="metric-trend" v-if="item.compare !== undefined">
+                  <div class="trend-chart">
+                    <div class="trend-bar" :style="{ height: getTrendHeight(item) }"></div>
+                  </div>
+                </div>
+                <div class="metric-decoration">
+                  <div class="decoration-circle"></div>
+                </div>
+              </div>
+            </div>
           </div>
         </el-col>
       </el-row>
     </div>
-    <div style="margin-top: 20px;"></div>
+
+    <!-- 鐢熶骇浠诲姟鐪嬫澘 -->
+    <div class="overview-section">
+      <el-row :gutter="20">
+        <el-col :lg="24">
+          <div class="task-board-container">
+            <div class="board-header">
+              <div class="header-left">
+                <i class="el-icon-s-management board-icon"></i>
+                <span class="board-title">褰撳墠鐢熶骇浠诲姟</span>
+              </div>
+              <div class="header-right">
+                <span class="task-count">鍏� {{ tableData.length }} 涓换鍔�</span>
+              </div>
+            </div>
+            <div class="board-body">
+              <div class="scroll-table-container" @mouseenter="pauseScroll" @mouseleave="resumeScroll">
+                <!-- 琛ㄥご -->
+                <div class="table-header" ref="tableHeader">
+                  <table class="header-table">
+                    <thead>
+                      <tr>
+                        <th>搴忓彿</th>
+                        <th>鎵樼洏鐮�</th>
+                        <th>璧峰鍦板潃</th>
+                        <th>鐩爣鍦板潃</th>
+                        <th>浠诲姟绫诲瀷</th>
+                        <th>浠诲姟鐘舵��</th>
+                        <th>鍒涘缓鏃堕棿</th>
+                      </tr>
+                    </thead>
+                  </table>
+                </div>
+
+                <!-- 琛ㄦ牸鍐呭鍖哄煙 -->
+                <div class="table-body-container" ref="tableBody">
+                  <div class="table-body-wrapper" :style="{ transform: `translateY(-${scrollPosition}px)` }">
+                    <!-- 鍘熷鏁版嵁 -->
+                    <table class="body-table">
+                      <tbody>
+                        <tr v-for="(row, index) in tableData" :key="`task-${index}-${refreshKey}`"
+                          :class="index % 2 === 0 ? 'even-row' : 'odd-row'">
+                          <td>{{ index + 1 }}</td>
+                          <td>{{ row.palletCode || '鏃�' }}</td>
+                          <td>{{ row.sourceAddress || '鏃�' }}</td>
+                          <td>{{ row.targetAddress || '鏃�' }}</td>
+                          <td>{{ row.taskType || '鏃�' }}</td>
+                          <td>
+                            <el-tag :type="getStatusType(row.taskState)" size="small" class="status-tag">
+                              {{ row.taskState || '鏃�' }}
+                            </el-tag>
+                          </td>
+                          <td>{{ formatDateTime(row.createDate) || '鏃�' }}</td>
+                        </tr>
+                      </tbody>
+                    </table>
+
+                    <!-- 澶嶅埗涓�浠芥暟鎹敤浜庢棤缂濇粴鍔� -->
+                    <table class="body-table" v-if="tableData.length > rowNum">
+                      <tbody>
+                        <tr v-for="(row, index) in tableData" :key="`task-copy-${index}-${refreshKey}`"
+                          :class="index % 2 === 0 ? 'even-row' : 'odd-row'">
+                          <td>{{ index + tableData.length + 1 }}</td>
+                          <td>{{ row.palletCode || '鏃�' }}</td>
+                          <td>{{ row.sourceAddress || '鏃�' }}</td>
+                          <td>{{ row.targetAddress || '鏃�' }}</td>
+                          <td>{{ row.taskType || '鏃�' }}</td>
+                          <td>
+                            <el-tag :type="getStatusType(row.taskState)" size="small" class="status-tag">
+                              {{ row.taskState || '鏃�' }}
+                            </el-tag>
+                          </td>
+                          <td>{{ formatDateTime(row.createDate) || '鏃�' }}</td>
+                        </tr>
+                      </tbody>
+                    </table>
+                  </div>
+                </div>
+              </div>
+              <div v-if="tableData.length === 0" class="no-data-board">
+                <el-empty description="鏆傛棤鐢熶骇浠诲姟鏁版嵁" :image-size="80" />
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 鍥捐〃閮ㄥ垎 -->
+    <div class="charts-section">
+      <el-row :gutter="20">
+        <el-col :lg="12">
+          <div class="chart-card">
+            <div class="chart-header">
+              <h3 class="chart-title">
+                <i class="el-icon-trend-charts chart-icon"></i>
+                鍑哄簱閲忚秼鍔�
+              </h3>
+              <div class="chart-subtitle">杩�7鏃ュ嚭搴撶粺璁�</div>
+            </div>
+            <div class="chart-content">
+              <div v-if="loading" class="chart-loading">
+                <i class="el-icon-loading"></i>
+                <span>鏁版嵁鍔犺浇涓�...</span>
+              </div>
+              <div v-else-if="error" class="chart-error">
+                <i class="el-icon-warning"></i>
+                <span>鏁版嵁鍔犺浇澶辫触</span>
+                <el-button type="text" @click="fetchData" class="retry-btn">閲嶈瘯</el-button>
+              </div>
+              <div v-else-if="!chartData.outbound.values.length" class="chart-no-data">
+                <i class="el-icon-data-line"></i>
+                <span>鏆傛棤鍑哄簱鏁版嵁</span>
+              </div>
+              <div v-else ref="outboundChart" class="chart" :key="`outbound-${refreshKey}`"></div>
+            </div>
+          </div>
+        </el-col>
+        <el-col :lg="12">
+          <div class="chart-card">
+            <div class="chart-header">
+              <h3 class="chart-title">
+                <i class="el-icon-trend-charts chart-icon"></i>
+                鍏ュ簱閲忚秼鍔�
+              </h3>
+              <div class="chart-subtitle">杩�7鏃ュ叆搴撶粺璁�</div>
+            </div>
+            <div class="chart-content">
+              <div v-if="loading" class="chart-loading">
+                <i class="el-icon-loading"></i>
+                <span>鏁版嵁鍔犺浇涓�...</span>
+              </div>
+              <div v-else-if="error" class="chart-error">
+                <i class="el-icon-warning"></i>
+                <span>鏁版嵁鍔犺浇澶辫触</span>
+                <el-button type="text" @click="fetchData" class="retry-btn">閲嶈瘯</el-button>
+              </div>
+              <div v-else-if="!chartData.inbound.values.length" class="chart-no-data">
+                <i class="el-icon-data-line"></i>
+                <span>鏆傛棤鍏ュ簱鏁版嵁</span>
+              </div>
+              <div v-else ref="inboundChart" class="chart" :key="`inbound-${refreshKey}`"></div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 鏈堝害鏁版嵁鍥捐〃 -->
     <div class="charts-section">
       <el-row :gutter="20">
         <el-col :lg="24">
-          <div class="chart-container">
-            <h3>鏈堝嚭鍏ュ簱閲�</h3>
-            <div v-if="loading" class="loading">鍔犺浇涓�...</div>
-            <div v-else-if="error" class="error">鏁版嵁鍔犺浇澶辫触</div>
-            <div v-else-if="!chartData.monthData.inValue.length || !chartData.monthData.outValue.length" class="no-data">
-              鏆傛棤鍑哄簱鏁版嵁</div>
-            <div v-else ref="monthDataChart" class="chart1"></div>
+          <div class="chart-card">
+            <div class="chart-header">
+              <h3 class="chart-title">
+                <i class="el-icon-data-line chart-icon"></i>
+                鏈堝害鍑哄叆搴撳姣�
+              </h3>
+              <div class="chart-subtitle">鏈湀姣忔棩鍑哄叆搴撻噺缁熻</div>
+            </div>
+            <div class="chart-content">
+              <div v-if="loading" class="chart-loading">
+                <i class="el-icon-loading"></i>
+                <span>鏁版嵁鍔犺浇涓�...</span>
+              </div>
+              <div v-else-if="error" class="chart-error">
+                <i class="el-icon-warning"></i>
+                <span>鏁版嵁鍔犺浇澶辫触</span>
+                <el-button type="text" @click="fetchData" class="retry-btn">閲嶈瘯</el-button>
+              </div>
+              <div v-else-if="!chartData.monthData.inValue.length || !chartData.monthData.outValue.length"
+                class="chart-no-data">
+                <i class="el-icon-data-line"></i>
+                <span>鏆傛棤鍑哄叆搴撴暟鎹�</span>
+              </div>
+              <div v-else ref="monthDataChart" class="chart-large" :key="`month-${refreshKey}`"></div>
+            </div>
           </div>
         </el-col>
       </el-row>
@@ -55,9 +248,11 @@
 import http from '../api/http.js';
 import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue';
 import * as echarts from 'echarts';
+import { Refresh } from '@element-plus/icons-vue';
 
 // 鍝嶅簲寮忔暟鎹�
 const dataMetrics = ref([]);
+const tableData = ref([]);
 const chartData = reactive({
   outbound: { dates: [], values: [] },
   inbound: { dates: [], values: [] },
@@ -65,6 +260,20 @@
 });
 const loading = ref(true);
 const error = ref(false);
+const refreshing = ref(false);
+const refreshKey = ref(0); // 鐢ㄤ簬寮哄埗閲嶆柊娓叉煋
+
+// 婊氬姩鐩稿叧
+const tableHeader = ref(null);
+const tableBody = ref(null);
+const scrollPosition = ref(0);
+const isScrolling = ref(true);
+let scrollInterval = null;
+const rowNum = 7; // 鏄剧ず鐨勮鏁�
+const rowHeight = 48; // 姣忚楂樺害
+
+// 褰撳墠鏃堕棿
+const currentTime = ref('');
 
 // 鍥捐〃寮曠敤鍜屽疄渚�
 const outboundChart = ref(null);
@@ -75,8 +284,166 @@
 const monthDataInstance = ref(null);
 const charts = ref([]);
 
+// 鏍煎紡鍖栨暟瀛�
+const formatNumber = (num) => {
+  if (num === undefined || num === null) return '0';
+  return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+};
+
+const text = (str) => {
+  if (str == "浠婃棩杩涘簱閲�" || str == "浠婃棩鍑哄簱閲�") {
+    return "杈冩槰鏃�"
+  } else if (str == "鏈湀杩涘簱閲�" || str == "鏈湀鍑哄簱閲�") {
+    return "杈冧笂鏈�"
+  } else {
+    return ""
+  }
+}
+
+// 鏍煎紡鍖栨瘮杈冨��
+const formatCompareValue = (value) => {
+  if (value === 0) return '0';
+  return value > 0 ? `+${formatNumber(value)}` : formatNumber(value);
+};
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+const formatDateTime = (dateString) => {
+  if (!dateString) return '';
+  try {
+    const date = new Date(dateString);
+    return date.toLocaleString('zh-CN', {
+      month: '2-digit',
+      day: '2-digit',
+      hour: '2-digit',
+      minute: '2-digit'
+    });
+  } catch {
+    return dateString;
+  }
+};
+
+// 鑾峰彇姣旇緝鍊兼牱寮忕被
+const getCompareClass = (compare) => {
+  if (compare > 0) return 'compare-positive';
+  if (compare < 0) return 'compare-negative';
+  return 'compare-zero';
+};
+
+// 鑾峰彇姣旇緝鍊煎浘鏍�
+const getCompareIcon = (compare) => {
+  if (compare > 0) return 'el-icon-top';
+  if (compare < 0) return 'el-icon-bottom';
+  return 'el-icon-minus';
+};
+
+// 鑾峰彇瓒嬪娍楂樺害
+const getTrendHeight = (item) => {
+  if (item.compare === undefined || item.value === 0) return '0%';
+
+  const maxValue = Math.max(Math.abs(item.value), Math.abs(item.compare));
+  if (maxValue === 0) return '0%';
+
+  const percentage = (Math.abs(item.compare) / maxValue) * 100;
+  return `${Math.min(percentage, 100)}%`;
+};
+
+// 鑾峰彇鎸囨爣鍗$墖鏍峰紡绫�
+const getMetricCardClass = (name) => {
+  const classMap = {
+    '浠婃棩杩涘簱閲�': 'metric-inbound-today',
+    '浠婃棩鍑哄簱閲�': 'metric-outbound-today',
+    '鏈湀杩涘簱閲�': 'metric-inbound-month',
+    '鏈湀鍑哄簱閲�': 'metric-outbound-month',
+    '搴撳瓨鎬婚噺': 'metric-total'
+  };
+  return classMap[name] || '';
+};
+
+const getMetricIcon = (type) => {
+  const iconMap = {
+    '浠婃棩杩涘簱閲�': 'el-icon-download',
+    '浠婃棩鍑哄簱閲�': 'el-icon-upload2',
+    '鏈湀杩涘簱閲�': 'el-icon-download',
+    '鏈湀鍑哄簱閲�': 'el-icon-upload2',
+    '搴撳瓨鎬婚噺': 'el-icon-box',
+  };
+  return iconMap[type] || 'el-icon-data-board';
+};
+
+// 鐘舵�佹爣绛剧被鍨�
+const getStatusType = (status) => {
+  const statusMap = {
+    '杩涜涓�': 'primary',
+    '宸插畬鎴�': 'success',
+    '宸插彇娑�': 'info',
+    '寮傚父': 'danger',
+    '寰呮墽琛�': 'warning'
+  };
+  return statusMap[status] || 'primary';
+};
+
+// 鏇存柊褰撳墠鏃堕棿
+const updateCurrentTime = () => {
+  const now = new Date();
+  currentTime.value = now.toLocaleString('zh-CN', {
+    year: 'numeric',
+    month: '2-digit',
+    day: '2-digit',
+    hour: '2-digit',
+    minute: '2-digit',
+    second: '2-digit'
+  });
+};
+
+// 鍚姩婊氬姩
+const startScrolling = () => {
+  if (tableData.value.length <= rowNum) {
+    isScrolling.value = false;
+    return; // 鏁版嵁灏戞椂涓嶆粴鍔�
+  }
+
+  isScrolling.value = true;
+  const totalHeight = tableData.value.length * rowHeight;
+
+  scrollInterval = setInterval(() => {
+    scrollPosition.value += 1;
+
+    // 褰撴粴鍔ㄥ埌绗竴浠芥暟鎹殑鏈熬鏃讹紝閲嶇疆浣嶇疆瀹炵幇鏃犵紳婊氬姩
+    if (scrollPosition.value >= totalHeight) {
+      scrollPosition.value = 0;
+    }
+  }, 30); // 璋冩暣婊氬姩閫熷害
+};
+
+// 鏆傚仠婊氬姩
+const pauseScroll = () => {
+  if (scrollInterval) {
+    clearInterval(scrollInterval);
+    scrollInterval = null;
+  }
+  isScrolling.value = false;
+};
+
+// 鎭㈠婊氬姩
+const resumeScroll = () => {
+  if (tableData.value.length > rowNum) {
+    startScrolling();
+  }
+};
+
 // 鍒濆鍖栧浘琛�
 const initCharts = () => {
+  // 娓呯悊鏃х殑鍥捐〃瀹炰緥
+  if (outboundInstance.value) {
+    outboundInstance.value.dispose();
+  }
+  if (inboundInstance.value) {
+    inboundInstance.value.dispose();
+  }
+  if (monthDataInstance.value) {
+    monthDataInstance.value.dispose();
+  }
+
   if (!outboundChart.value || !inboundChart.value || !monthDataChart.value) {
     console.log('鍥捐〃瀹瑰櫒鏈壘鍒帮紝寤惰繜鍒濆鍖�');
     return;
@@ -93,49 +460,81 @@
     tooltip: {
       trigger: 'axis',
       axisPointer: {
-        type: 'shadow' // 闃村奖鎸囩ず鍣�
+        type: 'shadow'
       },
       formatter: function (params) {
         const data = params[0];
         return `
-          <div style="font-weight: bold; margin-bottom: 5px;">${data.name}</div>
-          <div style="display: flex; align-items: center;">
-            <span style="display: inline-block; width: 10px; height: 10px; background: ${data.color}; border-radius: 50%; margin-right: 5px;"></span>
-            <span>${data.seriesName}: </span>
-            <span style="font-weight: bold; margin-left: 5px;">${data.value}</span>
+          <div style="font-weight: 600; margin-bottom: 8px; color: #303133;">${data.name}</div>
+          <div style="display: flex; align-items: center; font-size: 14px;">
+            <span style="display: inline-block; width: 8px; height: 8px; background: ${data.color}; border-radius: 50%; margin-right: 8px;"></span>
+            <span style="color: #606266;">${data.seriesName}: </span>
+            <span style="font-weight: 600; margin-left: 8px; color: #303133;">${data.value}</span>
           </div>
         `;
       },
-      backgroundColor: 'rgba(255, 255, 255, 0.9)',
-      borderColor: '#ddd',
+      backgroundColor: 'rgba(255, 255, 255, 0.95)',
+      borderColor: '#e4e7ed',
       borderWidth: 1,
       textStyle: {
-        color: '#333'
-      }
+        color: '#303133'
+      },
+      padding: [8, 12],
+      borderRadius: 6,
+      shadowColor: 'rgba(0, 0, 0, 0.1)',
+      shadowBlur: 8
     },
     grid: {
       left: '3%',
-      right: '4%',
+      right: '3%',
       bottom: '3%',
+      top: '15%',
       containLabel: true
     },
     xAxis: {
       type: 'category',
       data: chartData.outbound.dates,
+      axisLine: {
+        lineStyle: {
+          color: '#e4e7ed'
+        }
+      },
       axisLabel: {
+        color: '#606266',
         rotate: 45
       }
     },
     yAxis: {
       type: 'value',
-      name: '鏁伴噺'
+      name: '鏁伴噺',
+      nameTextStyle: {
+        color: '#909399'
+      },
+      axisLine: {
+        lineStyle: {
+          color: '#e4e7ed'
+        }
+      },
+      axisLabel: {
+        color: '#606266'
+      },
+      splitLine: {
+        lineStyle: {
+          color: '#f0f2f5',
+          type: 'dashed'
+        }
+      }
     },
     series: [{
       name: '鍑哄簱閲�',
       data: chartData.outbound.values,
       type: 'bar',
       itemStyle: {
-        color: '#e06e6e'
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          { offset: 0, color: '#ff9f7f' },
+          { offset: 1, color: '#ff6b6b' }
+        ]),
+        borderRadius: [4, 4, 0, 0]
       },
       barWidth: '60%',
       animation: true,
@@ -143,7 +542,8 @@
         show: true,
         position: 'top',
         formatter: '{c}',
-        color: '#e06e6e'
+        color: '#ff6b6b',
+        fontWeight: 'bold'
       }
     }]
   };
@@ -153,60 +553,103 @@
     tooltip: {
       trigger: 'axis',
       axisPointer: {
-        type: 'line' // 绾垮瀷鎸囩ず鍣�
+        type: 'line'
       },
       formatter: function (params) {
         const data = params[0];
         return `
-          <div style="font-weight: bold; margin-bottom: 5px;">${data.name}</div>
-          <div style="display: flex; align-items: center;">
-            <span style="display: inline-block; width: 10px; height: 10px; background: ${data.color}; border-radius: 50%; margin-right: 5px;"></span>
-            <span>${data.seriesName}: </span>
-            <span style="font-weight: bold; margin-left: 5px;">${data.value}</span>
+          <div style="font-weight: 600; margin-bottom: 8px; color: #303133;">${data.name}</div>
+          <div style="display: flex; align-items: center; font-size: 14px;">
+            <span style="display: inline-block; width: 8px; height: 8px; background: ${data.color}; border-radius: 50%; margin-right: 8px;"></span>
+            <span style="color: #606266;">${data.seriesName}: </span>
+            <span style="font-weight: 600; margin-left: 8px; color: #303133;">${data.value}</span>
           </div>
         `;
       },
-      backgroundColor: 'rgba(255, 255, 255, 0.9)',
-      borderColor: '#ddd',
+      backgroundColor: 'rgba(255, 255, 255, 0.95)',
+      borderColor: '#e4e7ed',
       borderWidth: 1,
       textStyle: {
-        color: '#333'
-      }
+        color: '#303133'
+      },
+      padding: [8, 12],
+      borderRadius: 6,
+      shadowColor: 'rgba(0, 0, 0, 0.1)',
+      shadowBlur: 8
     },
     grid: {
       left: '3%',
-      right: '4%',
+      right: '3%',
       bottom: '3%',
+      top: '15%',
       containLabel: true
     },
     xAxis: {
       type: 'category',
       data: chartData.inbound.dates,
+      axisLine: {
+        lineStyle: {
+          color: '#e4e7ed'
+        }
+      },
       axisLabel: {
+        color: '#606266',
         rotate: 45
       }
     },
     yAxis: {
       type: 'value',
-      name: '鏁伴噺'
+      name: '鏁伴噺',
+      nameTextStyle: {
+        color: '#909399'
+      },
+      axisLine: {
+        lineStyle: {
+          color: '#e4e7ed'
+        }
+      },
+      axisLabel: {
+        color: '#606266'
+      },
+      splitLine: {
+        lineStyle: {
+          color: '#f0f2f5',
+          type: 'dashed'
+        }
+      }
     },
     series: [{
       name: '鍏ュ簱閲�',
       data: chartData.inbound.values,
       type: 'line',
+      smooth: true,
+      symbol: 'circle',
+      symbolSize: 8,
       itemStyle: {
-        color: '#4a7bff'
+        color: '#409eff',
+        borderColor: '#fff',
+        borderWidth: 2
       },
       lineStyle: {
-        width: 3
+        width: 3,
+        color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+          { offset: 0, color: '#409eff' },
+          { offset: 1, color: '#67c23a' }
+        ])
       },
-      smooth: true,
+      areaStyle: {
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          { offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
+          { offset: 1, color: 'rgba(64, 158, 255, 0.1)' }
+        ])
+      },
       animation: true,
       label: {
         show: true,
         position: 'top',
         formatter: '{c}',
-        color: '#4a7bff'
+        color: '#409eff',
+        fontWeight: 'bold'
       }
     }]
   };
@@ -216,45 +659,61 @@
     tooltip: {
       trigger: 'axis',
       axisPointer: {
-        type: 'cross', // 鍗佸瓧鍑嗘槦鎸囩ず鍣�
+        type: 'cross',
         crossStyle: {
           color: '#999'
         }
       },
       formatter: function (params) {
-        let html = `<div style="font-weight: bold; margin-bottom: 5px;">${params[0].name}</div>`;
+        let html = `<div style="font-weight: 600; margin-bottom: 8px; color: #303133;">${params[0].name}</div>`;
         params.forEach(param => {
           html += `
-            <div style="display: flex; align-items: center; margin: 2px 0;">
-              <span style="display: inline-block; width: 10px; height: 10px; background: ${param.color}; border-radius: 50%; margin-right: 5px;"></span>
-              <span>${param.seriesName}: </span>
-              <span style="font-weight: bold; margin-left: 5px;">${param.value}</span>
+            <div style="display: flex; align-items: center; margin: 4px 0; font-size: 14px;">
+              <span style="display: inline-block; width: 8px; height: 8px; background: ${param.color}; border-radius: 50%; margin-right: 8px;"></span>
+              <span style="color: #606266;">${param.seriesName}: </span>
+              <span style="font-weight: 600; margin-left: 8px; color: #303133;">${param.value}</span>
             </div>
           `;
         });
         return html;
       },
-      backgroundColor: 'rgba(255, 255, 255, 0.9)',
-      borderColor: '#ddd',
+      backgroundColor: 'rgba(255, 255, 255, 0.95)',
+      borderColor: '#e4e7ed',
       borderWidth: 1,
       textStyle: {
-        color: '#333'
-      }
+        color: '#303133'
+      },
+      padding: [12, 16],
+      borderRadius: 6,
+      shadowColor: 'rgba(0, 0, 0, 0.1)',
+      shadowBlur: 8
     },
     legend: {
       data: ['鍏ュ簱閲�', '鍑哄簱閲�'],
-      bottom: 0
+      bottom: 10,
+      textStyle: {
+        color: '#606266'
+      },
+      itemWidth: 12,
+      itemHeight: 12
     },
     grid: {
       left: '3%',
-      right: '4%',
-      bottom: '10%',
+      right: '3%',
+      bottom: '12%',
+      top: '15%',
       containLabel: true
     },
     xAxis: {
       type: 'category',
       data: chartData.monthData.dates,
+      axisLine: {
+        lineStyle: {
+          color: '#e4e7ed'
+        }
+      },
       axisLabel: {
+        color: '#606266',
         rotate: 45
       },
       axisPointer: {
@@ -263,45 +722,68 @@
     },
     yAxis: {
       type: 'value',
-      name: '鏁伴噺'
+      name: '鏁伴噺',
+      nameTextStyle: {
+        color: '#909399'
+      },
+      axisLine: {
+        lineStyle: {
+          color: '#e4e7ed'
+        }
+      },
+      axisLabel: {
+        color: '#606266'
+      },
+      splitLine: {
+        lineStyle: {
+          color: '#f0f2f5',
+          type: 'dashed'
+        }
+      }
     },
     series: [
       {
         name: '鍏ュ簱閲�',
         data: chartData.monthData.inValue,
         type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 6,
         itemStyle: {
-          color: '#4a7bff'
+          color: '#409eff'
         },
         lineStyle: {
           width: 3
         },
-        smooth: true,
         animation: true,
         label: {
           show: true,
           position: 'top',
           formatter: '{c}',
-          color: '#4a7bff'
+          color: '#409eff',
+          fontWeight: 'bold'
         }
       },
       {
         name: '鍑哄簱閲�',
         data: chartData.monthData.outValue,
         type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 6,
         itemStyle: {
-          color: '#e06e6e'
+          color: '#ff6b6b'
         },
         lineStyle: {
           width: 3
         },
-        smooth: true,
         animation: true,
         label: {
           show: true,
           position: 'top',
           formatter: '{c}',
-          color: '#e06e6e'
+          color: '#ff6b6b',
+          fontWeight: 'bold'
         }
       }
     ]
@@ -315,29 +797,19 @@
 // 鏇存柊鍥捐〃鏁版嵁
 const updateCharts = () => {
   nextTick(() => {
-    if (outboundInstance.value && chartData.outbound.values.length > 0) {
-      outboundInstance.value.setOption({
-        xAxis: { data: chartData.outbound.dates },
-        series: [{ data: chartData.outbound.values }]
-      });
+    // 鍏堥攢姣佹棫鐨勫浘琛ㄥ疄渚�
+    if (outboundInstance.value) {
+      outboundInstance.value.dispose();
+    }
+    if (inboundInstance.value) {
+      inboundInstance.value.dispose();
+    }
+    if (monthDataInstance.value) {
+      monthDataInstance.value.dispose();
     }
 
-    if (inboundInstance.value && chartData.inbound.values.length > 0) {
-      inboundInstance.value.setOption({
-        xAxis: { data: chartData.inbound.dates },
-        series: [{ data: chartData.inbound.values }]
-      });
-    }
-
-    if (monthDataInstance.value && chartData.monthData.dates.length > 0) {
-      monthDataInstance.value.setOption({
-        xAxis: { data: chartData.monthData.dates },
-        series: [
-          { data: chartData.monthData.inValue },
-          { data: chartData.monthData.outValue }
-        ]
-      });
-    }
+    // 閲嶆柊鍒濆鍖栧浘琛�
+    initCharts();
   });
 };
 
@@ -346,38 +818,16 @@
   charts.value.forEach(chart => chart && chart.resize());
 };
 
-// 鏁版嵁鑾峰彇
-const fetchData = async () => {
-  try {
-    loading.value = true;
-    error.value = false;
-
-    const response = await http.post("api/StockInfo/GetStockData", {});
-    console.log('API鍝嶅簲鏁版嵁:', response.data);
-
-    if (response.data && response.data.success !== false) {
-      handleDataUpdate(response.data);
-    } else {
-      throw new Error('API杩斿洖鏁版嵁鏍煎紡閿欒');
-    }
-
-    loading.value = false;
-  } catch (err) {
-    console.error('API璇锋眰澶辫触:', err);
-    loading.value = false;
-    error.value = true;
-  }
-};
-
 // 鏁版嵁澶勭悊
 const handleDataUpdate = (data) => {
-  console.log('澶勭悊鏁版嵁:', data);
+  console.log('API鍝嶅簲鏁版嵁:', data);
 
-  // 鏇存柊鏁版嵁鎸囨爣
+  // 浣跨敤 Object.assign 纭繚鍝嶅簲寮忔洿鏂�
   if (data.metrics && Array.isArray(data.metrics)) {
     dataMetrics.value = data.metrics.map(item => ({
       name: item.name || item.Name || '鏈煡鎸囨爣',
-      value: item.value != null ? item.value : item.Value || 0
+      value: item.value != null ? item.value : item.Value || 0,
+      compare: item.compare != null ? item.compare : 0
     }));
   }
 
@@ -398,24 +848,80 @@
     chartData.monthData.outValue = data.monthData.outValue || [];
   }
 
-  console.log('鏇存柊鍚庣殑鏁版嵁鎸囨爣:', dataMetrics.value);
-  console.log('鏇存柊鍚庣殑鍥捐〃鏁版嵁:', chartData);
+  // 鏇存柊琛ㄦ牸鏁版嵁
+  if (data && data.newTask && Array.isArray(data.newTask)) {
+    tableData.value = data.newTask.map(task => ({
+      palletCode: task.palletCode || '鏃�',
+      roadway: task.roadway || '鏃�',
+      sourceAddress: task.sourceAddress || '鏃�',
+      targetAddress: task.targetAddress || '鏃�',
+      taskType: task.taskType || '鏃�',
+      taskState: task.taskState || '鏃�',
+      errorMessage: task.errorMessage || '鏃�',
+      createDate: task.createDate || '鏃�',
+    }));
+
+    // 鏁版嵁鏇存柊鍚庨噸鏂板惎鍔ㄦ粴鍔�
+    nextTick(() => {
+      pauseScroll();
+      startScrolling();
+    });
+  } else {
+    tableData.value = [];
+    pauseScroll();
+  }
+
+  // 寮哄埗鏇存柊 refreshKey 瑙﹀彂閲嶆柊娓叉煋
+  refreshKey.value++;
 
   // 寤惰繜鍒濆鍖栧浘琛紝纭繚鏁版嵁宸叉洿鏂�
   nextTick(() => {
-    if (!outboundInstance.value || !inboundInstance.value || !monthDataInstance.value) {
-      initCharts();
-    } else {
-      updateCharts();
-    }
+    updateCharts();
   });
 };
 
-// 杞鎺у埗
+// 鏁版嵁鑾峰彇
+const fetchData = async () => {
+  try {
+    loading.value = true;
+    error.value = false;
+    refreshing.value = true;
+
+    const response = await http.post("api/StockInfo/GetStockData", {});
+    console.log('API鍝嶅簲:', response);
+
+    if (response.data && response.data.success !== false) {
+      handleDataUpdate(response.data);
+      error.value = false;
+    } else {
+      throw new Error('API杩斿洖鏁版嵁鏍煎紡閿欒');
+    }
+  } catch (err) {
+    console.error('API璇锋眰澶辫触:', err);
+    error.value = true;
+  } finally {
+    loading.value = false;
+    refreshing.value = false;
+    updateCurrentTime();
+  }
+};
+
+// 鍒锋柊澶勭悊
+const handleRefresh = () => {
+  fetchData();
+};
+
 const intervalId = ref(null);
+const timeIntervalId = ref(null);
+
 const startPolling = () => {
   fetchData();
-  intervalId.value = setInterval(fetchData, 5 * 60 * 1000);
+
+  // 璁剧疆瀹氭椂鏇存柊褰撳墠鏃堕棿
+  timeIntervalId.value = setInterval(updateCurrentTime, 1000);
+
+  // 璁剧疆瀹氭椂鑾峰彇鏁版嵁
+  intervalId.value = setInterval(fetchData, 5 * 60 * 1000); // 5鍒嗛挓杞
 };
 
 const stopPolling = () => {
@@ -423,6 +929,11 @@
     clearInterval(intervalId.value);
     intervalId.value = null;
   }
+  if (timeIntervalId.value) {
+    clearInterval(timeIntervalId.value);
+    timeIntervalId.value = null;
+  }
+  pauseScroll();
 };
 
 // 鐢熷懡鍛ㄦ湡
@@ -439,92 +950,684 @@
 </script>
 
 <style scoped>
+.retry-btn {
+  margin-left: 8px;
+  color: #409eff;
+}
+
+.chart-error {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 12px;
+}
+
+.chart-error .el-button {
+  margin-top: 8px;
+}
+
 .dashboard-container {
   padding: 20px;
-  background-color: #f5f6fa;
+  background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
   min-height: 100vh;
+  font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
 }
 
 .overview-section {
-  display: flex;
-  gap: 20px;
-  margin-bottom: 20px;
+  margin-bottom: 24px;
 }
 
 .data-overview {
-  flex: 1;
-  background: white;
-  padding: 16px;
-  border-radius: 8px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+  padding: 24px;
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+  border: 1px solid rgba(255, 255, 255, 0.8);
+  backdrop-filter: blur(10px);
 }
 
-.metrics {
+.overview-header {
   display: flex;
   justify-content: space-between;
-  margin-top: 16px;
+  align-items: flex-start;
+  margin-bottom: 24px;
 }
 
-.metric-item {
-  text-align: center;
+.header-left .title {
+  display: flex;
+  align-items: center;
+  margin: 0 0 8px 0;
+  font-size: 20px;
+  font-weight: 700;
+  color: #303133;
+  line-height: 1.2;
+}
+
+.header-icon {
+  margin-right: 12px;
+  font-size: 24px;
+  color: #409eff;
+  background: linear-gradient(135deg, #409eff, #79bbff);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+}
+
+.subtitle {
+  font-size: 14px;
+  color: #909399;
+  margin: 0;
+}
+
+.header-right {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+  gap: 12px;
+}
+
+.time-range {
+  font-size: 13px;
+  color: #909399;
+  background: rgba(64, 158, 255, 0.1);
+  padding: 6px 12px;
+  border-radius: 20px;
+  border: 1px solid rgba(64, 158, 255, 0.2);
+}
+
+.refresh-btn {
+  border-radius: 20px;
+  padding: 8px 16px;
+  font-weight: 500;
+  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
+  transition: all 0.3s ease;
+}
+
+.refresh-btn:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
+}
+
+.metrics-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+  gap: 20px;
+}
+
+.metric-card {
+  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+  border-radius: 12px;
+  padding: 24px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+  border: 1px solid rgba(255, 255, 255, 0.8);
+  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  position: relative;
+  overflow: hidden;
+}
+
+.metric-card:hover {
+  transform: translateY(-6px);
+  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12);
+}
+
+.metric-card::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 3px;
+  background: linear-gradient(90deg, transparent 0%, currentColor 50%, transparent 100%);
+  opacity: 0;
+  transition: opacity 0.3s ease;
+}
+
+.metric-card:hover::before {
+  opacity: 1;
+}
+
+.metric-inbound-today {
+  border-left: 4px solid #67c23a;
+  color: #67c23a;
+}
+
+.metric-outbound-today {
+  border-left: 4px solid #e6a23c;
+  color: #e6a23c;
+}
+
+.metric-inbound-month {
+  border-left: 4px solid #409eff;
+  color: #409eff;
+}
+
+.metric-outbound-month {
+  border-left: 4px solid #f56c6c;
+  color: #f56c6c;
+}
+
+.metric-total {
+  border-left: 4px solid #909399;
+  color: #909399;
+}
+
+.metric-content {
+  display: flex;
+  align-items: flex-start;
   flex: 1;
-  padding: 10px;
+  z-index: 2;
+}
+
+.metric-icon-wrapper {
+  width: 56px;
+  height: 56px;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 16px;
+  font-size: 28px;
+  transition: all 0.3s ease;
+}
+
+.metric-card:hover .metric-icon-wrapper {
+  transform: scale(1.1);
+}
+
+.metric-inbound-today .metric-icon-wrapper {
+  background: linear-gradient(135deg, rgba(103, 194, 58, 0.15), rgba(103, 194, 58, 0.05));
+  color: #67c23a;
+}
+
+.metric-outbound-today .metric-icon-wrapper {
+  background: linear-gradient(135deg, rgba(230, 162, 60, 0.15), rgba(230, 162, 60, 0.05));
+  color: #e6a23c;
+}
+
+.metric-inbound-month .metric-icon-wrapper {
+  background: linear-gradient(135deg, rgba(64, 158, 255, 0.15), rgba(64, 158, 255, 0.05));
+  color: #409eff;
+}
+
+.metric-outbound-month .metric-icon-wrapper {
+  background: linear-gradient(135deg, rgba(245, 108, 108, 0.15), rgba(245, 108, 108, 0.05));
+  color: #f56c6c;
+}
+
+.metric-total .metric-icon-wrapper {
+  background: linear-gradient(135deg, rgba(144, 147, 153, 0.15), rgba(144, 147, 153, 0.05));
+  color: #909399;
+}
+
+.metric-info {
+  flex: 1;
 }
 
 .metric-name {
   font-size: 14px;
-  color: #666;
+  color: #606266;
   margin-bottom: 8px;
+  font-weight: 500;
 }
 
 .metric-value {
+  font-size: 28px;
+  font-weight: 800;
+  color: #303133;
+  margin-bottom: 8px;
+  line-height: 1;
+  background: linear-gradient(135deg, currentColor, #303133);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+}
+
+.metric-compare {
+  display: flex;
+  align-items: center;
+  font-size: 12px;
+}
+
+.compare-positive {
+  color: #f56c6c;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  margin-right: 8px;
+}
+
+.compare-positive i {
+  font-size: 12px;
+  margin-right: 4px;
+}
+
+.compare-negative {
+  color: #67c23a;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  margin-right: 8px;
+}
+
+.compare-negative i {
+  font-size: 12px;
+  margin-right: 4px;
+}
+
+.compare-zero {
+  color: #909399;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  margin-right: 8px;
+}
+
+.compare-hidden {
+  display: hidden;
+}
+
+.compare-zero i {
+  font-size: 12px;
+  margin-right: 4px;
+}
+
+.compare-label {
+  color: #909399;
+  font-size: 11px;
+}
+
+.metric-trend {
+  width: 44px;
+  height: 44px;
+  display: flex;
+  align-items: flex-end;
+  justify-content: center;
+  z-index: 2;
+}
+
+.trend-chart {
+  width: 6px;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.06);
+  border-radius: 3px;
+  position: relative;
+  overflow: hidden;
+}
+
+.trend-bar {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  border-radius: 3px;
+  transition: height 0.8s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.metric-inbound-today .trend-bar {
+  background: linear-gradient(to top, #67c23a, #95d475);
+}
+
+.metric-outbound-today .trend-bar {
+  background: linear-gradient(to top, #e6a23c, #eebe77);
+}
+
+.metric-inbound-month .trend-bar {
+  background: linear-gradient(to top, #409eff, #79bbff);
+}
+
+.metric-outbound-month .trend-bar {
+  background: linear-gradient(to top, #f56c6c, #f89898);
+}
+
+.metric-total .trend-bar {
+  background: linear-gradient(to top, #909399, #b1b3b8);
+}
+
+.metric-decoration {
+  position: absolute;
+  top: -20px;
+  right: -20px;
+  width: 80px;
+  height: 80px;
+  opacity: 0.1;
+  z-index: 1;
+}
+
+.decoration-circle {
+  width: 100%;
+  height: 100%;
+  border-radius: 50%;
+  background: currentColor;
+}
+
+/* 浠诲姟鐪嬫澘鏍峰紡 */
+.task-board-container {
+  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+  border: 1px solid rgba(255, 255, 255, 0.8);
+  overflow: hidden;
+}
+
+.board-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20px 24px;
+  background: linear-gradient(135deg, #409eff 0%, #79bbff 100%);
+  color: white;
+}
+
+.header-left {
+  display: flex;
+  align-items: center;
+}
+
+.board-icon {
   font-size: 20px;
-  font-weight: bold;
-  margin: 8px 0;
-  color: #333;
+  margin-right: 12px;
 }
 
-.charts-section {
-  gap: 20px;
+.board-title {
+  font-size: 18px;
+  font-weight: 600;
 }
 
-.chart-container {
-  flex: 1;
-  background: white;
-  padding: 16px;
-  border-radius: 8px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+.task-count {
+  font-size: 14px;
+  opacity: 0.9;
+  background: rgba(255, 255, 255, 0.2);
+  padding: 4px 12px;
+  border-radius: 12px;
 }
 
-.chart {
-  height: 550px;
+.board-body {
+  height: 320px;
+  padding: 0;
+}
+
+.scroll-table-container {
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+}
+
+.table-header {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 20;
+  background: #f8f9fa;
+  border-bottom: 2px solid #e4e7ed;
+}
+
+.header-table {
   width: 100%;
+  border-collapse: collapse;
 }
 
-.chart1 {
-  height: 550px;
+.header-table th {
+  background: #f8f9fa;
+  color: #606266;
+  font-weight: 600;
+  padding: 16px 12px;
+  text-align: center;
+  border-right: 1px solid #e4e7ed;
+  font-size: 14px;
+  position: relative;
+}
+
+.header-table th:last-child {
+  border-right: none;
+}
+
+.header-table th::after {
+  content: '';
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 2px;
+  background: linear-gradient(90deg, #409eff, #79bbff);
+}
+
+.table-body-container {
+  position: absolute;
+  top: 56px;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+}
+
+.table-body-wrapper {
+  transition: transform 0.3s ease;
+}
+
+.body-table {
   width: 100%;
+  border-collapse: collapse;
 }
 
-.loading,
-.error,
-.no-data {
+.body-table td {
+  padding: 14px 12px;
+  border-bottom: 1px solid #f0f2f5;
+  font-size: 13px;
+  height: 48px;
+  box-sizing: border-box;
+  text-align: center;
+  color: #606266;
+}
+
+.even-row {
+  background-color: #fafbfc;
+}
+
+.odd-row {
+  background-color: #ffffff;
+}
+
+.body-table tr:hover {
+  background-color: #f0f7ff !important;
+  transform: scale(1.01);
+  transition: all 0.2s ease;
+}
+
+.status-tag {
+  border-radius: 12px;
+  padding: 4px 12px;
+  font-weight: 500;
+  border: none;
+}
+
+.no-data-board {
   display: flex;
   justify-content: center;
   align-items: center;
-  height: 500px;
+  height: 100%;
+  width: 100%;
+  background: #f8f9fa;
+}
+
+/* 鍥捐〃鍗$墖鏍峰紡 */
+.charts-section {
+  margin-bottom: 24px;
+}
+
+.chart-card {
+  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+  border: 1px solid rgba(255, 255, 255, 0.8);
+  overflow: hidden;
+  height: 400px;
+  display: flex;
+  flex-direction: column;
+}
+
+.chart-header {
+  padding: 20px 24px 0;
+  background: transparent;
+}
+
+.chart-title {
+  display: flex;
+  align-items: center;
+  margin: 0 0 8px 0;
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.chart-icon {
+  margin-right: 8px;
+  color: #409eff;
   font-size: 18px;
 }
 
-.loading {
-  color: #999;
-}
-
-.error {
-  color: #2d8cf0;
-}
-
-.no-data {
+.chart-subtitle {
+  font-size: 13px;
   color: #909399;
+  margin: 0;
+}
+
+.chart-content {
+  flex: 1;
+  padding: 0 12px 20px;
+  position: relative;
+}
+
+.chart {
+  height: 100%;
+  width: 100%;
+}
+
+.chart-large {
+  height: 100%;
+  width: 100%;
+}
+
+.chart-loading,
+.chart-error,
+.chart-no-data {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+  color: #909399;
+  font-size: 14px;
+}
+
+.chart-loading i,
+.chart-error i,
+.chart-no-data i {
+  font-size: 48px;
+  margin-bottom: 16px;
+  opacity: 0.6;
+}
+
+.chart-loading {
+  color: #409eff;
+}
+
+.chart-error {
+  color: #f56c6c;
+}
+
+/* Element Plus 鏍囩鏍峰紡璋冩暣 */
+:deep(.el-tag) {
+  border: none;
+  font-size: 12px;
+  font-weight: 500;
+}
+
+:deep(.el-tag--success) {
+  background: linear-gradient(135deg, #f0f9ff, #e1f3ff);
+  color: #67c23a;
+}
+
+:deep(.el-tag--primary) {
+  background: linear-gradient(135deg, #f0f9ff, #e1f3ff);
+  color: #409eff;
+}
+
+:deep(.el-tag--info) {
+  background: linear-gradient(135deg, #f4f4f5, #e9e9eb);
+  color: #909399;
+}
+
+:deep(.el-tag--warning) {
+  background: linear-gradient(135deg, #fdf6ec, #faecd8);
+  color: #e6a23c;
+}
+
+:deep(.el-tag--danger) {
+  background: linear-gradient(135deg, #fef0f0, #fde2e2);
+  color: #f56c6c;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 1200px) {
+  .metrics-grid {
+    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+    gap: 16px;
+  }
+
+  .metric-card {
+    padding: 20px;
+  }
+
+  .metric-value {
+    font-size: 24px;
+  }
+}
+
+@media (max-width: 768px) {
+  .dashboard-container {
+    padding: 16px;
+  }
+
+  .overview-header {
+    flex-direction: column;
+    gap: 16px;
+  }
+
+  .header-right {
+    align-items: flex-start;
+  }
+
+  .metrics-grid {
+    grid-template-columns: 1fr;
+    gap: 12px;
+  }
+
+  .chart-card {
+    height: 350px;
+  }
+}
+
+/* 婊氬姩鏉℃牱寮忎紭鍖� */
+.scroll-table-container::-webkit-scrollbar {
+  width: 6px;
+}
+
+.scroll-table-container::-webkit-scrollbar-track {
+  background: #f1f1f1;
+  border-radius: 3px;
+}
+
+.scroll-table-container::-webkit-scrollbar-thumb {
+  background: #c1c1c1;
+  border-radius: 3px;
+}
+
+.scroll-table-container::-webkit-scrollbar-thumb:hover {
+  background: #a8a8a8;
 }
 </style>
\ No newline at end of file

--
Gitblit v1.9.3