From c2e8526f8f857be5e3a8c6f2b39c5efd424de33e Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 15 十月 2026 11:50:24 +0800
Subject: [PATCH] refactor(auth): 优化认证逻辑和界面组件结构

---
 src/views/bi/dashboard/index.vue |  136 ++++++++++-----------------------------------
 1 files changed, 31 insertions(+), 105 deletions(-)

diff --git a/src/views/bi/dashboard/index.vue b/src/views/bi/dashboard/index.vue
index 98072c2..87299c4 100644
--- a/src/views/bi/dashboard/index.vue
+++ b/src/views/bi/dashboard/index.vue
@@ -12,6 +12,7 @@
 
 import ChartCard from './modules/ChartCard.vue';
 import ClockWidget from './modules/ClockWidget.vue';
+import RealtimeTable from './modules/RealtimeTable.vue';
 import { DASHBOARD_META, getDashboardCode, getDefaultCharts } from './data';
 
 defineOptions({ name: 'BiDashboard' });
@@ -24,26 +25,38 @@
 const loading = ref(true);
 const lastUpdateTime = ref('');
 let dataRefreshTimer: ReturnType<typeof setInterval> | undefined;
+let requestSequence = 0;
+let fetching = false;
+let disposed = false;
 
 async function loadData(background = false) {
-  if (!meta.value) return;
+  if (!meta.value || disposed || (background && fetching)) return;
+  const sequence = ++requestSequence;
+  const code = meta.value.code;
+  fetching = true;
   if (!background && dataRefreshTimer) clearInterval(dataRefreshTimer);
   try {
     if (!background) loading.value = true;
-    const data = await getDashboardData(meta.value.code);
+    const data = await getDashboardData(code);
+    if (disposed || sequence !== requestSequence) return;
     charts.value = data.length > 0 ? data : getDefaultCharts();
     lastUpdateTime.value = new Date().toLocaleTimeString('zh-CN', { hour12: false });
   } catch {
-    if (!background) charts.value = getDefaultCharts();
+    if (!background && !disposed && sequence === requestSequence) charts.value = getDefaultCharts();
   } finally {
-    loading.value = false;
-    await nextTick();
-    resizeAllCharts();
-    if (!background) {
-      const intervals = charts.value.map((chart) => chart.refreshInterval).filter((interval) => interval > 0);
-      if (intervals.length) dataRefreshTimer = setInterval(() => {
-        if (!loading.value && document.visibilityState === 'visible') loadData(true);
-      }, Math.max(15, Math.min(...intervals)) * 1000);
+    if (!disposed && sequence === requestSequence) {
+      fetching = false;
+      loading.value = false;
+      await nextTick();
+      if (!disposed && sequence === requestSequence) {
+        resizeAllCharts();
+        if (!background) {
+          const intervals = charts.value.map((chart) => chart.refreshInterval).filter((interval) => interval > 0);
+          if (intervals.length) dataRefreshTimer = setInterval(() => {
+            if (!loading.value && document.visibilityState === 'visible') loadData(true);
+          }, Math.max(15, Math.min(...intervals)) * 1000);
+        }
+      }
     }
   }
 }
@@ -161,11 +174,6 @@
   return val;
 }
 
-// 澶у睆琛ㄦ牸銆屽疄鏃舵粴鍔ㄣ�嶏細鏁版嵁澶嶅埗涓�浠斤紝閰嶅悎 CSS marquee 鍋氭棤缂濈旱鍚戞粴鍔�
-function duplicateRows(rows?: Record<string, unknown>[]): Record<string, unknown>[] {
-  return rows ? [...rows, ...rows] : [];
-}
-
 // ======== 鐪嬫澘涓婚鑹� ========
 const dashboardAccentColors: Record<string, string> = {
   'purchase-sales': '#6366f1',
@@ -205,6 +213,8 @@
   loadData();
 });
 onBeforeUnmount(() => {
+  disposed = true;
+  requestSequence++;
   if (dataRefreshTimer) clearInterval(dataRefreshTimer);
   document.removeEventListener('fullscreenchange', onFullscreenChange);
   resizeObserver?.disconnect();
@@ -311,30 +321,11 @@
             <span class="table-header-title">{{ chart.name }}</span>
             <span class="table-badge">瀹炴椂婊氬姩</span>
           </div>
-          <div v-if="chart.data && chart.data.length > 0" class="table-wrap">
-            <table class="data-table">
-              <thead>
-                <tr>
-                  <th
-                    v-for="key in Object.keys(chart.data[0] || {})"
-                    :key="key"
-                  >{{ key }}</th>
-                </tr>
-              </thead>
-              <tbody class="marquee-tbody">
-                <tr
-                  v-for="(row, ri) in duplicateRows(chart.data)"
-                  :key="ri"
-                  :style="{ animationDelay: `${ri * 50}ms` }"
-                >
-                  <td
-                    v-for="key in Object.keys(chart.data[0] || {})"
-                    :key="key"
-                  >{{ getCellValue(row, key) }}</td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
+          <RealtimeTable
+            v-if="chart.data && chart.data.length > 0"
+            :rows="chart.data"
+            :format-cell="getCellValue"
+          />
           <Empty v-else description="鏆傛棤鏁版嵁" />
         </div>
       </template>
@@ -612,71 +603,6 @@
   padding: 2px 8px; border-radius: 10px;
   background: rgba(0,255,136,0.08); color: var(--green);
   border: 1px solid rgba(0,255,136,0.15);
-}
-
-.table-wrap {
-  overflow-x: auto;
-  overflow-y: hidden;
-  max-height: 320px;
-  border-radius: 8px;
-  background: rgba(4, 14, 36, 0.55);
-  backdrop-filter: blur(6px);
-  -webkit-backdrop-filter: blur(6px);
-  border: 1px solid rgba(0, 229, 255, 0.08);
-}
-
-.data-table {
-  width: 100%; border-collapse: collapse; font-size: 12px;
-}
-
-.data-table thead {
-  position: sticky;
-  top: 0;
-  z-index: 2;
-}
-
-.data-table thead th {
-  background: rgba(6, 18, 42, 0.8);
-  color: var(--text-secondary);
-  font-weight: 600; font-size: 10px;
-  text-transform: uppercase; letter-spacing: 0.8px;
-  padding: 10px 14px; text-align: left;
-  border-bottom: 1px solid rgba(0, 229, 255, 0.12);
-  white-space: nowrap;
-}
-
-.data-table tbody td {
-  padding: 9px 14px; color: var(--text-primary);
-  border-bottom: 1px solid rgba(0, 229, 255, 0.04);
-  white-space: nowrap;
-}
-
-.data-table tbody tr {
-  transition: all 0.25s;
-  opacity: 0;
-  animation: row-fade-in 0.45s ease forwards;
-}
-
-.data-table tbody tr:hover {
-  background: rgba(0, 229, 255, 0.06);
-  box-shadow: inset 0 0 20px rgba(0, 229, 255, 0.03);
-}
-
-@keyframes row-fade-in {
-  from { opacity: 0; transform: translateY(6px); }
-  to { opacity: 1; transform: translateY(0); }
-}
-
-/* 澶у睆琛ㄦ牸銆屽疄鏃舵粴鍔ㄣ�嶏細tbody 澶嶅埗涓�浠藉悗 translateY(-50%) 鏃犵紳寰幆 */
-.marquee-tbody {
-  animation: table-marquee 15s linear infinite;
-}
-.marquee-tbody:hover {
-  animation-play-state: paused;
-}
-@keyframes table-marquee {
-  from { transform: translateY(0); }
-  to { transform: translateY(-50%); }
 }
 
 /* ======== 绌虹姸鎬� ======== */

--
Gitblit v1.9.3