From 523365960513f297024a419f94b2b42eccd9456f Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期四, 09 四月 2026 11:21:41 +0800
Subject: [PATCH] #

---
 rsf-design/src/views/dashboard/console/index.vue |  279 +++++++++++++++++++++----------------------------------
 1 files changed, 108 insertions(+), 171 deletions(-)

diff --git a/rsf-design/src/views/dashboard/console/index.vue b/rsf-design/src/views/dashboard/console/index.vue
index 1a2f229..9bfbcc2 100644
--- a/rsf-design/src/views/dashboard/console/index.vue
+++ b/rsf-design/src/views/dashboard/console/index.vue
@@ -1,143 +1,114 @@
 <template>
-  <div class="art-full-height flex flex-col gap-6">
-    <section class="grid gap-6 md:grid-cols-2 xl:grid-cols-4" v-loading="sectionLoading.summary">
+  <div class="art-full-height flex flex-col gap-6 lg:min-h-0 lg:overflow-hidden lg:gap-2">
+    <section class="grid gap-3 md:grid-cols-2 lg:grid-cols-4 lg:shrink-0" v-loading="sectionLoading.summary">
       <div
         v-for="item in summaryCardItems"
         :key="item.title"
-        class="art-card flex items-start justify-between rounded-3xl px-7 py-6"
+        class="art-card flex items-start justify-between rounded-3xl px-4 py-4 lg:px-4 lg:py-3 xl:px-5"
       >
-        <div class="min-w-0 pr-6">
-          <p class="text-sm font-medium text-g-700">{{ item.title }}</p>
-          <ArtCountTo class="mt-3 block text-[2.3rem] font-semibold leading-none text-g-900" :target="item.count" :duration="1400" />
-          <div class="mt-4 flex items-center gap-2 text-sm">
-            <span :class="item.metaTone">{{ item.metaLabel }}</span>
-            <span class="text-g-500">{{ item.metaValue }}</span>
+        <div class="min-w-0 flex-1 pr-3">
+          <p class="truncate text-sm font-medium text-g-700 lg:text-[13px]">{{ item.title }}</p>
+          <ArtCountTo
+            class="mt-1 block truncate text-[1.95rem] font-semibold leading-none text-g-900 lg:text-[1.72rem] xl:text-[1.92rem]"
+            :target="item.count"
+            :duration="1400"
+          />
+          <div class="mt-1.5 flex items-center gap-1 text-xs lg:text-[12px] xl:text-sm">
+            <span :class="item.metaTone" class="shrink-0">{{ item.metaLabel }}</span>
+            <span class="truncate text-g-500">{{ item.metaValue }}</span>
           </div>
         </div>
-        <div class="flex size-13 shrink-0 items-center justify-center rounded-2xl" :class="item.iconBoxClass">
-          <ArtSvgIcon :icon="item.icon" class="text-2xl" :class="item.iconClass" />
+        <div class="flex size-10 shrink-0 items-center justify-center rounded-2xl lg:size-9 xl:size-11" :class="item.iconBoxClass">
+          <ArtSvgIcon :icon="item.icon" class="text-xl xl:text-2xl" :class="item.iconClass" />
         </div>
       </div>
     </section>
 
-    <section class="grid gap-6 xl:grid-cols-[1.35fr_1fr]">
-      <div class="art-card h-115 overflow-hidden p-6 box-border">
-        <div class="art-card-header">
-          <div class="title">
-            <h4>杩� 30 澶╁嚭鍏ュ簱瓒嬪娍</h4>
-            <p>鐪熷疄閾捐矾 <span class="text-success">宸叉帴閫�</span></p>
+    <section class="flex min-h-0 flex-1 flex-col gap-2">
+      <div class="grid min-h-0 flex-1 gap-2 lg:grid-cols-[1.35fr_1fr]">
+        <div class="art-card box-border flex h-full min-h-0 flex-col overflow-hidden p-4 lg:p-5">
+          <div class="art-card-header">
+            <div class="title">
+              <h4>杩� 30 澶╁嚭鍏ュ簱瓒嬪娍</h4>
+              <p>鐪熷疄閾捐矾 <span class="text-success">宸叉帴閫�</span></p>
+            </div>
+          </div>
+          <div class="mt-3 min-h-0 flex-1">
+            <ArtBarChart
+              height="100%"
+              :loading="sectionLoading.trend"
+              :data="trendChartSeries"
+              :x-axis-data="trendChartXAxisData"
+              :show-axis-line="false"
+              :show-legend="true"
+              :show-split-line="true"
+              legend-position="top"
+              bar-width="38%"
+            />
           </div>
         </div>
-        <div class="h-[calc(100%-4.5rem)]">
-          <ArtBarChart
-            height="22rem"
-            :loading="sectionLoading.trend"
-            :data="trendChartSeries"
-            :x-axis-data="trendChartXAxisData"
-            :show-axis-line="false"
-            :show-legend="true"
-            :show-split-line="true"
-            legend-position="top"
-            bar-width="38%"
-          />
-        </div>
-      </div>
 
-      <div class="art-card h-115 overflow-hidden p-6 box-border" v-loading="sectionLoading.locUsage">
-        <div class="art-card-header">
-          <div class="title">
-            <h4>搴撲綅浣跨敤鍒嗗竷</h4>
-            <p>{{ usageLegendCount }} 涓淮搴�</p>
+        <div class="art-card box-border flex h-full min-h-0 flex-col overflow-hidden p-4 lg:p-5" v-loading="sectionLoading.locUsage">
+          <div class="art-card-header">
+            <div class="title">
+              <h4>搴撲綅浣跨敤鍒嗗竷</h4>
+              <p>{{ usageLegendCount }} 涓淮搴�</p>
+            </div>
           </div>
-        </div>
-        <div class="grid h-[calc(100%-4.5rem)] gap-6 lg:grid-cols-[1fr_0.95fr] lg:items-center">
-          <ArtRingChart
-            height="21rem"
-            :data="locUsageList"
-            center-text="搴撲綅鍗犳瘮"
-            :show-legend="false"
-            :show-label="false"
-          />
-
-          <div class="space-y-1">
-            <div
-              v-for="item in usageLegend"
-              :key="item.name"
-              class="flex items-center justify-between border-b border-[var(--art-border-color)] py-4 last:border-b-0"
-            >
-              <div class="flex items-center gap-3">
-                <span class="size-2.5 rounded-full" :style="{ backgroundColor: item.color }"></span>
-                <span class="text-sm text-[var(--art-gray-900)]">{{ item.name }}</span>
-              </div>
-              <span class="text-sm font-medium text-[var(--art-gray-700)]">{{ item.value }}%</span>
+          <div class="mt-3 grid min-h-0 flex-1 gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(220px,0.92fr)] lg:items-center">
+            <div class="min-h-0 h-full">
+              <ArtRingChart
+                height="100%"
+                :data="locUsageList"
+                center-text="搴撲綅鍗犳瘮"
+                :show-legend="false"
+                :show-label="false"
+              />
             </div>
 
-            <ElEmpty v-if="!locUsageList.length" description="鏆傛棤搴撲綅浣跨敤鏁版嵁" :image-size="88" />
-          </div>
-        </div>
-      </div>
-    </section>
-
-    <section class="grid gap-6 xl:grid-cols-[0.95fr_1.05fr]">
-      <div class="art-card h-98 p-5 box-border" v-loading="sectionLoading.tasks">
-        <div class="art-card-header">
-          <div class="title">
-            <h4>鎵ц涓换鍔�</h4>
-            <p>{{ taskSubtitle }}</p>
-          </div>
-        </div>
-
-        <div class="mt-3 h-[calc(100%-3.75rem)] overflow-hidden">
-          <ElScrollbar>
-            <div
-              v-for="item in taskCardItems"
-              :key="`${item.time}-${item.title}`"
-              class="flex gap-4 border-b border-g-300 py-4 last:border-b-0"
-            >
-              <div class="flex flex-col items-center pt-1">
-                <span class="size-3 rounded-full bg-[var(--el-color-primary)]"></span>
-                <span class="mt-2 min-h-10 w-px bg-[var(--art-border-color)]"></span>
-              </div>
-              <div class="min-w-0 flex-1">
-                <p class="text-xs text-g-500">{{ item.time }}</p>
-                <div class="mt-2 flex items-center gap-2">
-                  <p class="truncate text-base font-medium text-[var(--art-gray-900)]">
-                    {{ item.title }}
-                  </p>
-                  <ElTag size="small" effect="light" :type="item.tagType">{{ item.tagText }}</ElTag>
+            <div class="min-h-0 overflow-hidden">
+              <div
+                v-for="item in usageLegend"
+                :key="item.name"
+                class="flex items-center justify-between gap-3 border-b border-[var(--art-border-color)] py-3 last:border-b-0"
+              >
+                <div class="flex items-center gap-3">
+                  <span class="size-2.5 rounded-full" :style="{ backgroundColor: item.color }"></span>
+                  <span class="truncate text-sm text-[var(--art-gray-900)]">{{ item.name }}</span>
                 </div>
-                <p class="mt-2 text-sm text-g-600">{{ item.subtitle }}</p>
+                <span class="text-sm font-medium text-[var(--art-gray-700)]">{{ item.value }}%</span>
               </div>
+
+              <ElEmpty v-if="!locUsageList.length" description="鏆傛棤搴撲綅浣跨敤鏁版嵁" :image-size="88" />
             </div>
-          </ElScrollbar>
+          </div>
         </div>
       </div>
 
-      <div class="art-card h-98 p-5 box-border" v-loading="sectionLoading.deadStock">
+      <div class="art-card box-border flex shrink-0 flex-col p-4 lg:p-3.5">
         <div class="art-card-header">
           <div class="title">
-            <h4>搴撳瓨鏈�杩戝姩鎬�</h4>
-            <p>{{ deadStockSubtitle }}</p>
+            <h4>蹇嵎璺宠浆</h4>
+            <p>蹇�熻繘鍏ヤ换鍔¢〉鍜屽簱瀛橀〉</p>
           </div>
         </div>
 
-        <div class="mt-3 h-[calc(100%-3.75rem)] overflow-hidden">
-          <ElScrollbar>
-            <div
-              v-for="item in stockCardItems"
-              :key="`${item.title}-${item.time}`"
-              class="flex items-center gap-4 border-b border-g-300 py-4 last:border-b-0"
-            >
-              <div class="size-12 rounded-2xl bg-[var(--el-color-primary-light-9)] flex-cc">
-                <ArtSvgIcon :icon="item.icon" class="text-xl text-[var(--el-color-primary)]" />
-              </div>
-              <div class="min-w-0 flex-1">
-                <p class="truncate text-base font-medium text-[var(--art-gray-900)]">{{ item.title }}</p>
-                <p class="mt-1 text-sm text-g-500">{{ item.status }}</p>
-              </div>
-              <div class="max-w-40 text-right text-sm text-g-500">{{ item.time }}</div>
+        <div class="mt-2.5 grid gap-2 md:grid-cols-2">
+          <button
+            v-for="item in quickLinkCards"
+            :key="item.title"
+            type="button"
+            class="flex items-start justify-between rounded-3xl border border-[var(--art-border-color)] bg-[var(--art-main-bg-color)] px-4 py-3 text-left transition hover:border-[var(--el-color-primary-light-5)] hover:bg-[var(--el-color-primary-light-9)]"
+            @click="navigateTo(item.path)"
+          >
+            <div class="min-w-0 pr-4">
+              <p class="text-base font-medium text-[var(--art-gray-900)]">{{ item.title }}</p>
+              <p class="mt-0.5 text-sm text-g-500">{{ item.description }}</p>
             </div>
-          </ElScrollbar>
+            <div class="flex size-11 shrink-0 items-center justify-center rounded-2xl" :class="item.iconBoxClass">
+              <ArtSvgIcon :icon="item.icon" class="text-xl" :class="item.iconClass" />
+            </div>
+          </button>
         </div>
       </div>
     </section>
@@ -145,44 +116,55 @@
 </template>
 
 <script setup>
+  import { useRouter } from 'vue-router'
   import { storeToRefs } from 'pinia'
   import { useUserStore } from '@/store/modules/user'
   import {
     fetchDashboardHeader,
     fetchDashboardTrend,
-    fetchDashboardDeadStock,
-    fetchDashboardLocUsage,
-    fetchDashboardTasks
+    fetchDashboardLocUsage
   } from '@/api/dashboard'
   import {
     EMPTY_SUMMARY,
-    buildDashboardDeadStockQuery,
-    buildDashboardTaskQuery,
     normalizeDashboardSummary,
     normalizeDashboardTrend,
-    normalizeDashboardTaskList,
     normalizeDashboardLocUsage,
-    normalizeDashboardDeadStockList,
     withDashboardRequestGuard
   } from './consolePage.helpers'
 
   defineOptions({ name: 'Console' })
 
+  const router = useRouter()
   const summary = ref({ ...EMPTY_SUMMARY })
   const trendModel = ref(normalizeDashboardTrend())
-  const taskList = ref([])
-  const deadStockList = ref([])
   const locUsageList = ref([])
   const sectionLoading = reactive({
     summary: false,
     trend: false,
-    deadStock: false,
-    locUsage: false,
-    tasks: false
+    locUsage: false
   })
 
   const userStore = useUserStore()
   const { getUserInfo } = storeToRefs(userStore)
+
+  const quickLinkCards = [
+    {
+      title: '浠诲姟椤�',
+      description: '鏌ョ湅鍜屽鐞嗕换鍔$鐞嗘暟鎹�',
+      path: '/manager/task',
+      icon: 'ri:task-line',
+      iconBoxClass: 'bg-[var(--el-color-primary-light-9)]',
+      iconClass: 'text-[var(--el-color-primary)]'
+    },
+    {
+      title: '搴撳瓨椤�',
+      description: '鏌ョ湅褰撳墠搴撳瓨涓庡簱瀛樻槑缁�',
+      path: '/manager/stock',
+      icon: 'ri:archive-stack-line',
+      iconBoxClass: 'bg-[rgba(20,222,186,0.14)]',
+      iconClass: 'text-[#14DEBA]'
+    }
+  ]
 
   const currentUser = computed(() => getUserInfo.value || {})
   const currentUserName = computed(() => {
@@ -291,8 +273,6 @@
   })
   const trendChartXAxisData = computed(() => trendDisplayModel.value.xAxisData)
   const trendChartSeries = computed(() => trendDisplayModel.value.series)
-  const taskSubtitle = computed(() => `鏈�杩� ${taskList.value.length} 鏉′换鍔″姩鎬乣)
-  const deadStockSubtitle = computed(() => `鏈�杩� ${deadStockList.value.length} 鏉″簱瀛樿褰昤)
   const usageLegendCount = computed(() => locUsageList.value.length)
   const usageLegend = computed(() => {
     const palette = ['#5B8FF9', '#5AD8A6', '#5D7092', '#F6BD16', '#E8684A', '#6DC8EC']
@@ -301,16 +281,6 @@
       color: palette[index % palette.length]
     }))
   })
-  const taskCardItems = computed(() =>
-    taskList.value.slice(0, 6).map((item) => ({
-      title: item.title,
-      time: item.time,
-      subtitle: item.status,
-      tagText: resolveTaskTagText(item.status),
-      tagType: resolveTaskTagType(item.class)
-    }))
-  )
-  const stockCardItems = computed(() => deadStockList.value.slice(0, 6))
 
   onMounted(() => {
     loadDashboard()
@@ -319,9 +289,7 @@
   function loadDashboard() {
     void loadSummarySection()
     void loadTrendSection()
-    void loadDeadStockSection()
     void loadLocUsageSection()
-    void loadTaskSection()
   }
 
   async function loadSummarySection() {
@@ -338,16 +306,6 @@
     sectionLoading.trend = false
   }
 
-  async function loadDeadStockSection() {
-    sectionLoading.deadStock = true
-    const payload = await withDashboardRequestGuard(
-      fetchDashboardDeadStock(buildDashboardDeadStockQuery()),
-      {}
-    )
-    deadStockList.value = normalizeDashboardDeadStockList(payload || {})
-    sectionLoading.deadStock = false
-  }
-
   async function loadLocUsageSection() {
     sectionLoading.locUsage = true
     const payload = await withDashboardRequestGuard(fetchDashboardLocUsage(), null)
@@ -355,29 +313,8 @@
     sectionLoading.locUsage = false
   }
 
-  async function loadTaskSection() {
-    sectionLoading.tasks = true
-    const payload = await withDashboardRequestGuard(
-      fetchDashboardTasks(buildDashboardTaskQuery()),
-      {}
-    )
-    taskList.value = normalizeDashboardTaskList(payload || {})
-    sectionLoading.tasks = false
-  }
-
-  function resolveTaskTagType(statusClass) {
-    const text = String(statusClass || '')
-    if (text.includes('emerald')) return 'success'
-    if (text.includes('rose')) return 'danger'
-    return 'primary'
-  }
-
-  function resolveTaskTagText(statusText) {
-    const text = String(statusText || '')
-      .replace(/\b\d+\./g, '')
-      .replace(/\s+/g, ' ')
-      .trim()
-    const parts = text.split('路').map((item) => item.trim()).filter(Boolean)
-    return parts[parts.length - 1] || '澶勭悊涓�'
+  function navigateTo(path) {
+    if (!path) return
+    router.push(path)
   }
 </script>

--
Gitblit v1.9.1