From c4a2d727fb1b72a1b7b7ae8c0d778f0e81952cce Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 03 八月 2026 11:28:41 +0800
Subject: [PATCH] feat(dashboard): 重构分析仪表盘页面并移除旧图表组件

---
 src/views/dashboard/analytics/data.ts                      |   17 
 /dev/null                                                  |   55 --
 src/views/dashboard/analytics/modules/quick-actions.vue    |   73 +++
 src/views/dashboard/analytics/modules/alert-panel.vue      |  139 +++++++
 src/views/dashboard/analytics/modules/kpi-cards.vue        |  210 +++++++++++
 src/views/dashboard/analytics/modules/production-trend.vue |   78 ++++
 src/views/dashboard/analytics/index.vue                    |  197 ++++++----
 src/views/dashboard/analytics/modules/biz-cards.vue        |  218 +++++++++++
 docs/dashboard-layout.canvas                               |  135 +++++++
 9 files changed, 987 insertions(+), 135 deletions(-)

diff --git a/docs/dashboard-layout.canvas b/docs/dashboard-layout.canvas
new file mode 100644
index 0000000..3adba79
--- /dev/null
+++ b/docs/dashboard-layout.canvas
@@ -0,0 +1,135 @@
+{
+  "nodes": [
+    {
+      "id": "a1b2c3d4e5f60001",
+      "type": "text",
+      "x": 0,
+      "y": 0,
+      "width": 1200,
+      "height": 70,
+      "text": "# MOM 鍒堕�犺繍钀ュ钩鍙� 鈥� 棣栭〉浠〃鐩榎n**鎶�鏈爤**: Vue 3 + Ant Design Vue 4 + Tailwind CSS + ECharts  |  **椋庢牸**: Industrial Bento Grid + 鐜荤拑鎬佸崱鐗�  |  **閰嶈壊**: 宸ヤ笟钃� #2563eb"
+    },
+    {
+      "id": "a1b2c3d4e5f60002",
+      "type": "group",
+      "x": 0,
+      "y": 90,
+      "width": 1200,
+      "height": 100,
+      "label": "Row 0: 娆㈣繋鏍� 鈥� 鏃ユ湡 + 闂�欒 + 绯荤粺鐘舵��",
+      "color": "5"
+    },
+    {
+      "id": "a1b2c3d4e5f60003",
+      "type": "text",
+      "x": 20,
+      "y": 120,
+      "width": 1160,
+      "height": 50,
+      "text": "`<WelcomeBar />` 鈥� 宸︿晶: 鏃ユ湡 + \"鏃╀笂濂�/涓嬪崍濂�/鏅氫笂濂絓" 鏍规嵁鏃堕棿鍔ㄦ�佸垏鎹� | 鍙充晶: 绯荤粺杩愯鐘舵�佹寚绀哄櫒 + 鍒锋柊鎸夐挳"
+    },
+    {
+      "id": "a1b2c3d4e5f60004",
+      "type": "group",
+      "x": 0,
+      "y": 210,
+      "width": 1200,
+      "height": 150,
+      "label": "Row 1: 4 涓� KPI 鏍稿績鎸囨爣鍗$墖 (Bento Grid)",
+      "color": "4"
+    },
+    {
+      "id": "a1b2c3d4e5f60005",
+      "type": "text",
+      "x": 20,
+      "y": 240,
+      "width": 270,
+      "height": 100,
+      "text": "## 鐢熶骇璁㈠崟\n**杩涜涓�**: CountTo 鍔ㄧ敾\n寰呮帓浜� / 宸插畬鎴怽n\n馃帹 钃濊壊娓愬彉 `#2563eb 鈫� #60a5fa`\n馃敆 鐐瑰嚮璺宠浆 鈫� 鐢熶骇宸ュ崟鍒楄〃"
+    },
+    {
+      "id": "a1b2c3d4e5f60006",
+      "type": "text",
+      "x": 310,
+      "y": 240,
+      "width": 270,
+      "height": 100,
+      "text": "## 浠婃棩浜ч噺\n**浜ч噺**: CountTo 鍔ㄧ敾\n鏄ㄦ棩浜ч噺瀵规瘮\n\n馃帹 缁胯壊娓愬彉 `#16a34a 鈫� #4ade80`\n馃敆 鐐瑰嚮璺宠浆 鈫� 鐢熶骇鎶ュ伐"
+    },
+    {
+      "id": "a1b2c3d4e5f60007",
+      "type": "text",
+      "x": 600,
+      "y": 240,
+      "width": 270,
+      "height": 100,
+      "text": "## 璐ㄩ噺鍚堟牸鐜嘰n**鍚堟牸鐜�%**: CountTo 鍔ㄧ敾\n鍚堟牸鍝� / 涓嶈壇鍝乗n\n馃帹 姗欒壊娓愬彉 `#ea580c 鈫� #fb923c`\n馃敆 鐐瑰嚮璺宠浆 鈫� 璐ㄦ璁板綍"
+    },
+    {
+      "id": "a1b2c3d4e5f60008",
+      "type": "text",
+      "x": 890,
+      "y": 240,
+      "width": 270,
+      "height": 100,
+      "text": "## 璁惧杩愯鐜嘰n**杩愯涓� / 鎬绘暟**: CountTo 鍔ㄧ敾\n鍋滄満 / 缁存姢涓璡n\n馃帹 绱壊娓愬彉 `#7c3aed 鈫� #a78bfa`\n馃敆 鐐瑰嚮璺宠浆 鈫� 璁惧绠$悊"
+    },
+    {
+      "id": "a1b2c3d4e5f60009",
+      "type": "group",
+      "x": 0,
+      "y": 380,
+      "width": 1200,
+      "height": 300,
+      "label": "Row 2: 鍥捐〃鍖� 鈥� 鐢熶骇瓒嬪娍 + 棰勮闈㈡澘",
+      "color": "1"
+    },
+    {
+      "id": "a1b2c3d4e5f60010",
+      "type": "text",
+      "x": 20,
+      "y": 410,
+      "width": 700,
+      "height": 250,
+      "text": "## 鐢熶骇瓒嬪娍鍥� (`ProductionTrend`)\n**绫诲瀷**: ECharts 鎶樼嚎 + 鏌辩姸娣峰悎鍥綷n**鏁版嵁**: 杩� 7 澶╀骇閲� / 鍚堟牸鍝� / 涓嶈壇鍝乗n\n- X 杞�: 鏃ユ湡 (鏈�-鏃�)\n- Y 杞�: 鏁伴噺\n- 钃濊壊鏌�: 浜ч噺\n- 缁胯壊绾�: 鍚堟牸鍝乗n- 绾㈣壊绾�: 涓嶈壇鍝乗n\n`getProductionTrend(days=7)`\n\n馃搻 鍗犲: 14/24 鏍呮牸 (58%)"
+    },
+    {
+      "id": "a1b2c3d4e5f60011",
+      "type": "text",
+      "x": 740,
+      "y": 410,
+      "width": 440,
+      "height": 250,
+      "text": "## 棰勮涓庡緟鍔� (`AlertPanel`)\n\n### 瀹夌伅鍛煎彨\n鏈缃懠鍙暟 + 璺宠浆閾炬帴\n\n### 缁翠慨宸ュ崟\n寰呭鐞嗙淮淇崟鏁� + 璺宠浆閾炬帴\n\n### 宸ュ崟鐘舵�佸垎甯僜n鐜舰楗煎浘 `getWorkOrderStatusDistribution()`\n\n馃搻 鍗犲: 10/24 鏍呮牸 (42%)"
+    },
+    {
+      "id": "a1b2c3d4e5f60012",
+      "type": "group",
+      "x": 0,
+      "y": 700,
+      "width": 1200,
+      "height": 150,
+      "label": "Row 3: 蹇嵎鍏ュ彛 + 鍔ㄦ�佷俊鎭�",
+      "color": "6"
+    },
+    {
+      "id": "a1b2c3d4e5f60013",
+      "type": "text",
+      "x": 20,
+      "y": 730,
+      "width": 540,
+      "height": 100,
+      "text": "## 蹇嵎鎿嶄綔 (`QuickActions`)\n9 瀹牸蹇嵎鍏ュ彛:\n鐢熶骇宸ュ崟 | 鐢熶骇鎶ュ伐 | 璐ㄦ绠$悊\n璁惧鐐规 | 鐢熶骇鎺掔▼ | 瀹夌伅鍛煎彨\n鐗╂枡搴撳瓨 | 宸ヨ壓璺嚎 | 鏁版嵁鎶ヨ〃\n\n馃帹 鍥炬爣 + 鏍囩 鍗$墖鏍峰紡"
+    },
+    {
+      "id": "a1b2c3d4e5f60014",
+      "type": "text",
+      "x": 580,
+      "y": 730,
+      "width": 560,
+      "height": 100,
+      "text": "## 鏈�杩戝姩鎬� (`RecentActivity`)\n鏈�杩戞洿鏂扮殑宸ュ崟 / 鎶ュ伐璁板綍\n鏃堕棿绾� Timeline 缁勪欢\n\n绀轰緥:\n- 10:30 宸ュ崟 WO-2024-089 瀹屾垚鐢熶骇\n- 09:45 璐ㄦ閫氳繃 鎵规 B-0128\n- 08:00 璁惧 DEV-003 寮�濮嬬淮鎶�"
+    }
+  ],
+  "edges": []
+}
diff --git a/src/views/dashboard/analytics/analytics-trends.vue b/src/views/dashboard/analytics/analytics-trends.vue
deleted file mode 100644
index f1f0b23..0000000
--- a/src/views/dashboard/analytics/analytics-trends.vue
+++ /dev/null
@@ -1,98 +0,0 @@
-<script lang="ts" setup>
-import type { EchartsUIType } from '@vben/plugins/echarts';
-
-import { onMounted, ref } from 'vue';
-
-import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
-
-const chartRef = ref<EchartsUIType>();
-const { renderEcharts } = useEcharts(chartRef);
-
-onMounted(() => {
-  renderEcharts({
-    grid: {
-      bottom: 0,
-      containLabel: true,
-      left: '1%',
-      right: '1%',
-      top: '2 %',
-    },
-    series: [
-      {
-        areaStyle: {},
-        data: [
-          111, 2000, 6000, 16_000, 33_333, 55_555, 64_000, 33_333, 18_000,
-          36_000, 70_000, 42_444, 23_222, 13_000, 8000, 4000, 1200, 333, 222,
-          111,
-        ],
-        itemStyle: {
-          color: '#5ab1ef',
-        },
-        smooth: true,
-        type: 'line',
-      },
-      {
-        areaStyle: {},
-        data: [
-          33, 66, 88, 333, 3333, 6200, 20_000, 3000, 1200, 13_000, 22_000,
-          11_000, 2221, 1201, 390, 198, 60, 30, 22, 11,
-        ],
-        itemStyle: {
-          color: '#019680',
-        },
-        smooth: true,
-        type: 'line',
-      },
-    ],
-    tooltip: {
-      axisPointer: {
-        lineStyle: {
-          color: '#019680',
-          width: 1,
-        },
-      },
-      trigger: 'axis',
-    },
-    // xAxis: {
-    //   axisTick: {
-    //     show: false,
-    //   },
-    //   boundaryGap: false,
-    //   data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
-    //   type: 'category',
-    // },
-    xAxis: {
-      axisTick: {
-        show: false,
-      },
-      boundaryGap: false,
-      data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
-      splitLine: {
-        lineStyle: {
-          type: 'solid',
-          width: 1,
-        },
-        show: true,
-      },
-      type: 'category',
-    },
-    yAxis: [
-      {
-        axisTick: {
-          show: false,
-        },
-        max: 80_000,
-        splitArea: {
-          show: true,
-        },
-        splitNumber: 4,
-        type: 'value',
-      },
-    ],
-  });
-});
-</script>
-
-<template>
-  <EchartsUI ref="chartRef" />
-</template>
diff --git a/src/views/dashboard/analytics/analytics-visits-data.vue b/src/views/dashboard/analytics/analytics-visits-data.vue
deleted file mode 100644
index 190fb41..0000000
--- a/src/views/dashboard/analytics/analytics-visits-data.vue
+++ /dev/null
@@ -1,82 +0,0 @@
-<script lang="ts" setup>
-import type { EchartsUIType } from '@vben/plugins/echarts';
-
-import { onMounted, ref } from 'vue';
-
-import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
-
-const chartRef = ref<EchartsUIType>();
-const { renderEcharts } = useEcharts(chartRef);
-
-onMounted(() => {
-  renderEcharts({
-    legend: {
-      bottom: 0,
-      data: ['璁块棶', '瓒嬪娍'],
-    },
-    radar: {
-      indicator: [
-        {
-          name: '缃戦〉',
-        },
-        {
-          name: '绉诲姩绔�',
-        },
-        {
-          name: 'Ipad',
-        },
-        {
-          name: '瀹㈡埛绔�',
-        },
-        {
-          name: '绗笁鏂�',
-        },
-        {
-          name: '鍏跺畠',
-        },
-      ],
-      radius: '60%',
-      splitNumber: 8,
-    },
-    series: [
-      {
-        areaStyle: {
-          opacity: 1,
-          shadowBlur: 0,
-          shadowColor: 'rgba(0,0,0,.2)',
-          shadowOffsetX: 0,
-          shadowOffsetY: 10,
-        },
-        data: [
-          {
-            itemStyle: {
-              color: '#b6a2de',
-            },
-            name: '璁块棶',
-            value: [90, 50, 86, 40, 50, 20],
-          },
-          {
-            itemStyle: {
-              color: '#5ab1ef',
-            },
-            name: '瓒嬪娍',
-            value: [70, 75, 70, 76, 20, 85],
-          },
-        ],
-        itemStyle: {
-          // borderColor: '#fff',
-          borderRadius: 10,
-          borderWidth: 2,
-        },
-        symbolSize: 0,
-        type: 'radar',
-      },
-    ],
-    tooltip: {},
-  });
-});
-</script>
-
-<template>
-  <EchartsUI ref="chartRef" />
-</template>
diff --git a/src/views/dashboard/analytics/analytics-visits-sales.vue b/src/views/dashboard/analytics/analytics-visits-sales.vue
deleted file mode 100644
index 6ff5208..0000000
--- a/src/views/dashboard/analytics/analytics-visits-sales.vue
+++ /dev/null
@@ -1,46 +0,0 @@
-<script lang="ts" setup>
-import type { EchartsUIType } from '@vben/plugins/echarts';
-
-import { onMounted, ref } from 'vue';
-
-import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
-
-const chartRef = ref<EchartsUIType>();
-const { renderEcharts } = useEcharts(chartRef);
-
-onMounted(() => {
-  renderEcharts({
-    series: [
-      {
-        animationDelay() {
-          return Math.random() * 400;
-        },
-        animationEasing: 'exponentialInOut',
-        animationType: 'scale',
-        center: ['50%', '50%'],
-        color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
-        data: [
-          { name: '澶栧寘', value: 500 },
-          { name: '瀹氬埗', value: 310 },
-          { name: '鎶�鏈敮鎸�', value: 274 },
-          { name: '杩滅▼', value: 400 },
-        ].toSorted((a, b) => {
-          return a.value - b.value;
-        }),
-        name: '鍟嗕笟鍗犳瘮',
-        radius: '80%',
-        roseType: 'radius',
-        type: 'pie',
-      },
-    ],
-
-    tooltip: {
-      trigger: 'item',
-    },
-  });
-});
-</script>
-
-<template>
-  <EchartsUI ref="chartRef" />
-</template>
diff --git a/src/views/dashboard/analytics/analytics-visits-source.vue b/src/views/dashboard/analytics/analytics-visits-source.vue
deleted file mode 100644
index 0915c7a..0000000
--- a/src/views/dashboard/analytics/analytics-visits-source.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script lang="ts" setup>
-import type { EchartsUIType } from '@vben/plugins/echarts';
-
-import { onMounted, ref } from 'vue';
-
-import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
-
-const chartRef = ref<EchartsUIType>();
-const { renderEcharts } = useEcharts(chartRef);
-
-onMounted(() => {
-  renderEcharts({
-    legend: {
-      bottom: '2%',
-      left: 'center',
-    },
-    series: [
-      {
-        animationDelay() {
-          return Math.random() * 100;
-        },
-        animationEasing: 'exponentialInOut',
-        animationType: 'scale',
-        avoidLabelOverlap: false,
-        color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
-        data: [
-          { name: '鎼滅储寮曟搸', value: 1048 },
-          { name: '鐩存帴璁块棶', value: 735 },
-          { name: '閭欢钀ラ攢', value: 580 },
-          { name: '鑱旂洘骞垮憡', value: 484 },
-        ],
-        emphasis: {
-          label: {
-            fontSize: '12',
-            fontWeight: 'bold',
-            show: true,
-          },
-        },
-        itemStyle: {
-          // borderColor: '#fff',
-          borderRadius: 10,
-          borderWidth: 2,
-        },
-        label: {
-          position: 'center',
-          show: false,
-        },
-        labelLine: {
-          show: false,
-        },
-        name: '璁块棶鏉ユ簮',
-        radius: ['40%', '65%'],
-        type: 'pie',
-      },
-    ],
-    tooltip: {
-      trigger: 'item',
-    },
-  });
-});
-</script>
-
-<template>
-  <EchartsUI ref="chartRef" />
-</template>
diff --git a/src/views/dashboard/analytics/analytics-visits.vue b/src/views/dashboard/analytics/analytics-visits.vue
deleted file mode 100644
index 7e0f101..0000000
--- a/src/views/dashboard/analytics/analytics-visits.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<script lang="ts" setup>
-import type { EchartsUIType } from '@vben/plugins/echarts';
-
-import { onMounted, ref } from 'vue';
-
-import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
-
-const chartRef = ref<EchartsUIType>();
-const { renderEcharts } = useEcharts(chartRef);
-
-onMounted(() => {
-  renderEcharts({
-    grid: {
-      bottom: 0,
-      containLabel: true,
-      left: '1%',
-      right: '1%',
-      top: '2 %',
-    },
-    series: [
-      {
-        barMaxWidth: 80,
-        // color: '#4f69fd',
-        data: [
-          3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000,
-          3200, 4800,
-        ],
-        type: 'bar',
-      },
-    ],
-    tooltip: {
-      axisPointer: {
-        lineStyle: {
-          // color: '#4f69fd',
-          width: 1,
-        },
-      },
-      trigger: 'axis',
-    },
-    xAxis: {
-      data: Array.from({ length: 12 }).map((_item, index) => `${index + 1}鏈坄),
-      type: 'category',
-    },
-    yAxis: {
-      max: 8000,
-      splitNumber: 4,
-      type: 'value',
-    },
-  });
-});
-</script>
-
-<template>
-  <EchartsUI ref="chartRef" />
-</template>
diff --git a/src/views/dashboard/analytics/data.ts b/src/views/dashboard/analytics/data.ts
new file mode 100644
index 0000000..3de09c7
--- /dev/null
+++ b/src/views/dashboard/analytics/data.ts
@@ -0,0 +1,17 @@
+import type { MesHomeApi } from '#/api/mes/home';
+
+export const defaultSummary: MesHomeApi.Summary = {
+  workOrderActiveCount: 0,
+  workOrderPrepareCount: 0,
+  workOrderFinishedCount: 0,
+  todayOutput: 0,
+  yesterdayOutput: 0,
+  todayQualifiedQuantity: 0,
+  todayUnqualifiedQuantity: 0,
+  machineryTotal: 0,
+  machineryProducing: 0,
+  machineryStop: 0,
+  machineryMaintenance: 0,
+  andonActiveCount: 0,
+  repairActiveCount: 0,
+};
diff --git a/src/views/dashboard/analytics/index.vue b/src/views/dashboard/analytics/index.vue
index e794c99..6085aef 100644
--- a/src/views/dashboard/analytics/index.vue
+++ b/src/views/dashboard/analytics/index.vue
@@ -1,90 +1,127 @@
 <script lang="ts" setup>
-import type { AnalysisOverviewItem } from '@vben/common-ui';
-import type { TabOption } from '@vben/types';
+import { computed, onMounted, ref } from 'vue';
+import { useRouter } from 'vue-router';
 
-import {
-  AnalysisChartCard,
-  AnalysisChartsTabs,
-  AnalysisOverview,
-} from '@vben/common-ui';
-import {
-  SvgBellIcon,
-  SvgCakeIcon,
-  SvgCardIcon,
-  SvgDownloadIcon,
-} from '@vben/icons';
+import { Page } from '@vben/common-ui';
 
-import AnalyticsTrends from './analytics-trends.vue';
-import AnalyticsVisitsData from './analytics-visits-data.vue';
-import AnalyticsVisitsSales from './analytics-visits-sales.vue';
-import AnalyticsVisitsSource from './analytics-visits-source.vue';
-import AnalyticsVisits from './analytics-visits.vue';
+import { Card, Col, Row } from 'ant-design-vue';
 
-const overviewItems: AnalysisOverviewItem[] = [
-  {
-    icon: SvgCardIcon,
-    title: '鐢ㄦ埛閲�',
-    totalTitle: '鎬荤敤鎴烽噺',
-    totalValue: 120_000,
-    value: 2000,
-  },
-  {
-    icon: SvgCakeIcon,
-    title: '璁块棶閲�',
-    totalTitle: '鎬昏闂噺',
-    totalValue: 500_000,
-    value: 20_000,
-  },
-  {
-    icon: SvgDownloadIcon,
-    title: '涓嬭浇閲�',
-    totalTitle: '鎬讳笅杞介噺',
-    totalValue: 120_000,
-    value: 8000,
-  },
-  {
-    icon: SvgBellIcon,
-    title: '浣跨敤閲�',
-    totalTitle: '鎬讳娇鐢ㄩ噺',
-    totalValue: 50_000,
-    value: 5000,
-  },
-];
+import { getHomeSummary } from '#/api/mes/home';
 
-const chartTabs: TabOption[] = [
-  {
-    label: '娴侀噺瓒嬪娍',
-    value: 'trends',
-  },
-  {
-    label: '鏈堣闂噺',
-    value: 'visits',
-  },
-];
+import { defaultSummary } from './data';
+import AlertPanel from './modules/alert-panel.vue';
+import BizCards from './modules/biz-cards.vue';
+import KpiCards from './modules/kpi-cards.vue';
+import ProductionTrend from './modules/production-trend.vue';
+import QuickActions from './modules/quick-actions.vue';
+
+defineOptions({ name: 'DashboardAnalytics' });
+
+const router = useRouter();
+const summary = ref(defaultSummary);
+
+const greeting = computed(() => {
+  const hour = new Date().getHours();
+  if (hour < 6) return '澶滄繁浜�';
+  if (hour < 9) return '鏃╀笂濂�';
+  if (hour < 12) return '涓婂崍濂�';
+  if (hour < 14) return '涓崍濂�';
+  if (hour < 18) return '涓嬪崍濂�';
+  return '鏅氫笂濂�';
+});
+
+const today = computed(() => {
+  const d = new Date();
+  const weekNames = ['鏃�', '涓�', '浜�', '涓�', '鍥�', '浜�', '鍏�'];
+  return `${d.getFullYear()}骞�${d.getMonth() + 1}鏈�${d.getDate()}鏃� 鏄熸湡${weekNames[d.getDay()]}`;
+});
+
+function handleNavigate(name: string) {
+  router.push({ name });
+}
+
+async function loadSummary() {
+  summary.value = await getHomeSummary();
+}
+
+onMounted(() => {
+  loadSummary();
+});
 </script>
 
 <template>
-  <div class="p-5">
-    <AnalysisOverview :items="overviewItems" />
-    <AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
-      <template #trends>
-        <AnalyticsTrends />
-      </template>
-      <template #visits>
-        <AnalyticsVisits />
-      </template>
-    </AnalysisChartsTabs>
-
-    <div class="mt-5 w-full md:flex">
-      <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="璁块棶鏁伴噺">
-        <AnalyticsVisitsData />
-      </AnalysisChartCard>
-      <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="璁块棶鏉ユ簮">
-        <AnalyticsVisitsSource />
-      </AnalysisChartCard>
-      <AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="璁块棶鏉ユ簮">
-        <AnalyticsVisitsSales />
-      </AnalysisChartCard>
+  <Page auto-content-height>
+    <!-- 娆㈣繋鏍� -->
+    <div class="glass-card mb-6 flex items-center justify-between rounded-2xl p-6">
+      <div>
+        <h1 class="bg-gradient-to-r from-blue-600 via-purple-600 to-pink-500 bg-clip-text text-2xl font-bold text-transparent dark:from-blue-400 dark:via-purple-400 dark:to-pink-400">
+          {{ greeting }}锛屾杩庡洖鏉�
+        </h1>
+        <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">{{ today }}</p>
+      </div>
+      <div class="hidden items-center gap-2 sm:flex">
+        <div class="flex items-center gap-1.5 rounded-full bg-emerald-50 px-3 py-1.5 text-xs font-medium text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-400">
+          <span class="relative flex size-2">
+            <span class="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-75" />
+            <span class="relative inline-flex size-2 rounded-full bg-emerald-500" />
+          </span>
+          绯荤粺杩愯涓�
+        </div>
+      </div>
     </div>
-  </div>
+
+    <!-- MES 鐢熶骇 KPI -->
+    <KpiCards :summary="summary" @navigate="handleNavigate" />
+
+    <!-- ERP/WMS/BPM 缁忚惀 KPI -->
+    <BizCards @navigate="handleNavigate" />
+
+    <!-- 鐢熶骇瓒嬪娍 + 棰勮闈㈡澘 -->
+    <Row :gutter="16" class="mb-4">
+      <Col :lg="14" :md="24" :sm="24" :xl="14" :xs="24" class="mb-4">
+        <div class="glass-card rounded-2xl p-5">
+          <h3 class="mb-4 text-base font-semibold text-gray-800 dark:text-gray-100">
+            鐢熶骇瓒嬪娍锛堣繎7澶╋級
+          </h3>
+          <ProductionTrend />
+        </div>
+      </Col>
+      <Col :lg="10" :md="24" :sm="24" :xl="10" :xs="24" class="mb-4">
+        <AlertPanel
+          :andon-active-count="summary.andonActiveCount"
+          :repair-active-count="summary.repairActiveCount"
+          @navigate="handleNavigate"
+        />
+      </Col>
+    </Row>
+
+    <!-- 蹇嵎鎿嶄綔 -->
+    <Row :gutter="16" class="mb-4">
+      <Col :span="24">
+        <QuickActions @navigate="handleNavigate" />
+      </Col>
+    </Row>
+  </Page>
 </template>
+
+<style scoped>
+.glass-card {
+  background: linear-gradient(135deg, rgba(255,255,255,0.8), rgba(255,255,255,0.5));
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
+  border: 1px solid rgba(255,255,255,0.6);
+  box-shadow:
+    0 8px 32px rgba(0,0,0,0.04),
+    0 2px 8px rgba(0,0,0,0.02),
+    inset 0 1px 0 rgba(255,255,255,0.8);
+}
+
+.dark .glass-card {
+  background: linear-gradient(135deg, rgba(30,30,50,0.7), rgba(20,20,40,0.5));
+  border: 1px solid rgba(255,255,255,0.08);
+  box-shadow:
+    0 8px 32px rgba(0,0,0,0.3),
+    0 2px 8px rgba(0,0,0,0.2),
+    inset 0 1px 0 rgba(255,255,255,0.05);
+}
+</style>
diff --git a/src/views/dashboard/analytics/modules/alert-panel.vue b/src/views/dashboard/analytics/modules/alert-panel.vue
new file mode 100644
index 0000000..4be1544
--- /dev/null
+++ b/src/views/dashboard/analytics/modules/alert-panel.vue
@@ -0,0 +1,139 @@
+<script lang="ts" setup>
+import type { MesHomeApi } from '#/api/mes/home';
+import type { EchartsUIType } from '@vben/plugins/echarts';
+
+import { onMounted, ref } from 'vue';
+
+import { CountTo } from '@vben/common-ui';
+import { IconifyIcon } from '@vben/icons';
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { Spin } from 'ant-design-vue';
+
+import { getWorkOrderStatusDistribution } from '#/api/mes/home';
+
+defineOptions({ name: 'DashboardAlertPanel' });
+
+const props = defineProps<{
+  andonActiveCount: number;
+  repairActiveCount: number;
+}>();
+
+const emit = defineEmits<{
+  navigate: [name: string];
+}>();
+
+const chartRef = ref<EchartsUIType>();
+const { renderEcharts } = useEcharts(chartRef);
+const loading = ref(false);
+
+async function loadStatus() {
+  loading.value = true;
+  try {
+    const data = await getWorkOrderStatusDistribution();
+    if (data.length > 0) {
+      renderEcharts({
+        legend: { bottom: 0, textStyle: { fontSize: 11 } },
+        series: [
+          {
+            animationDuration: 1200,
+            animationEasing: 'cubicOut',
+            color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#ec4899'],
+            data: data.map((d: MesHomeApi.WorkOrderStatus) => ({ name: d.statusName, value: d.count })),
+            emphasis: {
+              itemStyle: { shadowBlur: 20, shadowColor: 'rgba(0,0,0,0.2)', shadowOffsetX: 0 },
+              scaleSize: 8,
+            },
+            itemStyle: { borderRadius: 6, borderColor: 'transparent', borderWidth: 3 },
+            label: { show: false },
+            name: '宸ュ崟鐘舵��',
+            radius: ['50%', '75%'],
+            type: 'pie',
+          },
+        ],
+        tooltip: { trigger: 'item' },
+      });
+    }
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  loadStatus();
+});
+</script>
+
+<template>
+  <div class="space-y-4">
+    <!-- 棰勮鍗$墖琛� -->
+    <div class="grid grid-cols-2 gap-3">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0 p-4"
+        @click="emit('navigate', 'MesProAndon')"
+      >
+        <div class="relative z-10 flex items-center gap-3">
+          <div class="flex size-10 items-center justify-center rounded-lg bg-red-50 text-red-600 shadow-sm dark:bg-red-500/15 dark:text-red-400">
+            <IconifyIcon class="size-5" icon="lucide:alert-triangle" />
+          </div>
+          <div>
+            <div class="text-xs text-gray-500 dark:text-gray-400">瀹夌伅鍛煎彨</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-xl font-bold text-red-600 dark:text-red-400" :end-val="andonActiveCount" :duration="1000" />
+              <span class="text-xs text-gray-400">鏈缃�</span>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0 p-4"
+        @click="emit('navigate', 'MesProRepair')"
+      >
+        <div class="relative z-10 flex items-center gap-3">
+          <div class="flex size-10 items-center justify-center rounded-lg bg-amber-50 text-amber-600 shadow-sm dark:bg-amber-500/15 dark:text-amber-400">
+            <IconifyIcon class="size-5" icon="lucide:wrench" />
+          </div>
+          <div>
+            <div class="text-xs text-gray-500 dark:text-gray-400">缁翠慨宸ュ崟</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-xl font-bold text-amber-600 dark:text-amber-400" :end-val="repairActiveCount" :duration="1000" />
+              <span class="text-xs text-gray-400">寰呭鐞�</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 宸ュ崟鐘舵�佸浘 -->
+    <div class="glass-tilt rounded-2xl p-5">
+      <h4 class="mb-3 text-sm font-semibold text-gray-800 dark:text-gray-100">
+        宸ュ崟鐘舵�佸垎甯�
+      </h4>
+      <Spin :spinning="loading">
+        <EchartsUI ref="chartRef" class="!h-[200px]" />
+      </Spin>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.glass-tilt {
+  background: linear-gradient(135deg, rgba(255,255,255,0.85), rgba(255,255,255,0.55));
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
+  border: 1px solid rgba(255,255,255,0.6);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.04),
+    0 1px 4px rgba(0,0,0,0.02),
+    inset 0 1px 0 rgba(255,255,255,0.8);
+}
+
+.dark .glass-tilt {
+  background: linear-gradient(135deg, rgba(30,30,55,0.75), rgba(20,20,40,0.5));
+  border: 1px solid rgba(255,255,255,0.08);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.3),
+    0 1px 4px rgba(0,0,0,0.2),
+    inset 0 1px 0 rgba(255,255,255,0.04);
+}
+</style>
diff --git a/src/views/dashboard/analytics/modules/biz-cards.vue b/src/views/dashboard/analytics/modules/biz-cards.vue
new file mode 100644
index 0000000..95a9e63
--- /dev/null
+++ b/src/views/dashboard/analytics/modules/biz-cards.vue
@@ -0,0 +1,218 @@
+<script lang="ts" setup>
+import { onMounted, ref } from 'vue';
+
+import { CountTo } from '@vben/common-ui';
+import { IconifyIcon } from '@vben/icons';
+
+import { Col, Row } from 'ant-design-vue';
+
+import { getPurchaseOrderPage } from '#/api/erp/purchase/order';
+import { getSaleOrderPage } from '#/api/erp/sale/order';
+import { getMaterialStockPage } from '#/api/mes/wm/materialstock';
+import { getTaskTodoPage } from '#/api/bpm/task';
+
+defineOptions({ name: 'DashboardBizCards' });
+
+const emit = defineEmits<{
+  navigate: [name: string];
+}>();
+
+const purchaseCount = ref(0);
+const saleCount = ref(0);
+const stockCount = ref(0);
+const bpmTodoCount = ref(0);
+
+async function loadPurchase() {
+  try {
+    const result = await getPurchaseOrderPage({ pageNo: 1, pageSize: 1 });
+    purchaseCount.value = result.total ?? 0;
+  } catch { /* 鏃犳潈闄愭垨鎺ュ彛寮傚父 */ }
+}
+
+async function loadSale() {
+  try {
+    const result = await getSaleOrderPage({ pageNo: 1, pageSize: 1 });
+    saleCount.value = result.total ?? 0;
+  } catch { /* 鏃犳潈闄愭垨鎺ュ彛寮傚父 */ }
+}
+
+async function loadStock() {
+  try {
+    const result = await getMaterialStockPage({ pageNo: 1, pageSize: 1 });
+    stockCount.value = result.total ?? 0;
+  } catch { /* 鏃犳潈闄愭垨鎺ュ彛寮傚父 */ }
+}
+
+async function loadBpmTodo() {
+  try {
+    const result = await getTaskTodoPage({ pageNo: 1, pageSize: 1 });
+    bpmTodoCount.value = result.total ?? 0;
+  } catch { /* 鏃犳潈闄愭垨鎺ュ彛寮傚父 */ }
+}
+
+/** 3D 鍊炬枩 */
+interface TiltState { x: number; y: number }
+const tilts = ref<Record<string, TiltState>>({});
+
+function onMouseMove(e: MouseEvent, id: string) {
+  const el = e.currentTarget as HTMLElement;
+  const rect = el.getBoundingClientRect();
+  const x = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
+  const y = ((e.clientY - rect.top) / rect.height - 0.5) * 2;
+  tilts.value[id] = { x: x * -5, y: y * 5 };
+}
+
+function onMouseLeave(id: string) {
+  tilts.value[id] = { x: 0, y: 0 };
+}
+
+function tiltStyle(id: string) {
+  const t = tilts.value[id] ?? { x: 0, y: 0 };
+  return {
+    transform: `perspective(500px) rotateX(${t.y}deg) rotateY(${t.x}deg)`,
+    transition: t.x === 0 && t.y === 0 ? 'transform 0.5s ease' : 'transform 0.1s ease',
+  };
+}
+
+onMounted(() => {
+  loadPurchase();
+  loadSale();
+  loadStock();
+  loadBpmTodo();
+});
+</script>
+
+<template>
+  <Row :gutter="16">
+    <!-- 閲囪喘璁㈠崟 -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('purchase')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'purchase')"
+        @mouseleave="onMouseLeave('purchase')"
+        @click="emit('navigate', 'ErpPurchaseOrder')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-cyan-500 to-teal-400 shadow-lg shadow-cyan-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:shopping-cart" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">閲囪喘璁㈠崟</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-2xl font-bold leading-tight text-cyan-600 dark:text-cyan-400" :end-val="purchaseCount" :duration="1200" />
+              <span class="text-xs text-gray-400">绗�</span>
+            </div>
+            <div class="mt-1 text-xs text-gray-400">ERP 閲囪喘绠$悊</div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-cyan-500/5 to-teal-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-cyan-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+
+    <!-- 閿�鍞鍗� -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('sale')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'sale')"
+        @mouseleave="onMouseLeave('sale')"
+        @click="emit('navigate', 'ErpSaleOrder')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-pink-500 to-rose-400 shadow-lg shadow-pink-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:trending-up" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">閿�鍞鍗�</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-2xl font-bold leading-tight text-pink-600 dark:text-pink-400" :end-val="saleCount" :duration="1200" />
+              <span class="text-xs text-gray-400">绗�</span>
+            </div>
+            <div class="mt-1 text-xs text-gray-400">ERP 閿�鍞鐞�</div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-pink-500/5 to-rose-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-pink-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+
+    <!-- 鐗╂枡搴撳瓨 -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('stock')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'stock')"
+        @mouseleave="onMouseLeave('stock')"
+        @click="emit('navigate', 'MesWmMaterialStock')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-amber-500 to-yellow-400 shadow-lg shadow-amber-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:package-search" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">鐗╂枡搴撳瓨</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-2xl font-bold leading-tight text-amber-600 dark:text-amber-400" :end-val="stockCount" :duration="1200" />
+              <span class="text-xs text-gray-400">鏉�</span>
+            </div>
+            <div class="mt-1 text-xs text-gray-400">MES 搴撳瓨鍙拌处</div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-amber-500/5 to-yellow-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-amber-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+
+    <!-- BPM 寰呭姙 -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('bpm')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'bpm')"
+        @mouseleave="onMouseLeave('bpm')"
+        @click="emit('navigate', 'BpmTaskTodo')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-red-500 to-rose-400 shadow-lg shadow-red-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:clipboard-check" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">鎴戠殑寰呭姙</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-2xl font-bold leading-tight text-red-600 dark:text-red-400" :end-val="bpmTodoCount" :duration="1200" />
+              <span class="text-xs text-gray-400">椤�</span>
+            </div>
+            <div class="mt-1 text-xs text-gray-400">BPM 寰呭鎵�</div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-red-500/5 to-rose-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-red-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+  </Row>
+</template>
+
+<style scoped>
+.glass-tilt {
+  background: linear-gradient(135deg, rgba(255,255,255,0.85), rgba(255,255,255,0.55));
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
+  border: 1px solid rgba(255,255,255,0.6);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.04),
+    0 1px 4px rgba(0,0,0,0.02),
+    inset 0 1px 0 rgba(255,255,255,0.8);
+  transform-style: preserve-3d;
+}
+
+.dark .glass-tilt {
+  background: linear-gradient(135deg, rgba(30,30,55,0.75), rgba(20,20,40,0.5));
+  border: 1px solid rgba(255,255,255,0.08);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.3),
+    0 1px 4px rgba(0,0,0,0.2),
+    inset 0 1px 0 rgba(255,255,255,0.04);
+}
+</style>
diff --git a/src/views/dashboard/analytics/modules/kpi-cards.vue b/src/views/dashboard/analytics/modules/kpi-cards.vue
new file mode 100644
index 0000000..761db56
--- /dev/null
+++ b/src/views/dashboard/analytics/modules/kpi-cards.vue
@@ -0,0 +1,210 @@
+<script lang="ts" setup>
+import type { MesHomeApi } from '#/api/mes/home';
+
+import { computed, ref } from 'vue';
+
+import { CountTo } from '@vben/common-ui';
+import { IconifyIcon } from '@vben/icons';
+
+import { Col, Row } from 'ant-design-vue';
+
+defineOptions({ name: 'DashboardKpiCards' });
+
+const props = defineProps<{
+  summary: MesHomeApi.Summary;
+}>();
+
+const emit = defineEmits<{
+  navigate: [name: string];
+}>();
+
+const qualityRate = computed(() => {
+  const total =
+    props.summary.todayQualifiedQuantity +
+    props.summary.todayUnqualifiedQuantity;
+  return total === 0 ? 0 : (props.summary.todayQualifiedQuantity / total) * 100;
+});
+
+const equipmentRate = computed(() => {
+  if (props.summary.machineryTotal === 0) return 0;
+  return (props.summary.machineryProducing / props.summary.machineryTotal) * 100;
+});
+
+/** 3D 鍊炬枩鏁堟灉 */
+interface TiltState { x: number; y: number }
+
+const tilts = ref<Record<string, TiltState>>({});
+
+function onMouseMove(e: MouseEvent, id: string) {
+  const el = e.currentTarget as HTMLElement;
+  const rect = el.getBoundingClientRect();
+  const x = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
+  const y = ((e.clientY - rect.top) / rect.height - 0.5) * 2;
+  tilts.value[id] = { x: x * -6, y: y * 6 };
+}
+
+function onMouseLeave(id: string) {
+  tilts.value[id] = { x: 0, y: 0 };
+}
+
+function tiltStyle(id: string) {
+  const t = tilts.value[id] ?? { x: 0, y: 0 };
+  return {
+    transform: `perspective(600px) rotateX(${t.y}deg) rotateY(${t.x}deg)`,
+    transition: t.x === 0 && t.y === 0 ? 'transform 0.5s ease' : 'transform 0.1s ease',
+  };
+}
+</script>
+
+<template>
+  <Row :gutter="16">
+    <!-- 鐢熶骇璁㈠崟 -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('order')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'order')"
+        @mouseleave="onMouseLeave('order')"
+        @click="emit('navigate', 'MesProWorkOrder')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-cyan-400 shadow-lg shadow-blue-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:clipboard-list" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">鐢熶骇璁㈠崟</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-2xl font-bold leading-tight text-blue-600 dark:text-blue-400" :end-val="summary.workOrderActiveCount" :duration="1200" />
+              <span class="text-xs text-gray-400">杩涜涓�</span>
+            </div>
+            <div class="mt-1 flex items-center gap-1 text-xs text-gray-400">
+              <span class="inline-block size-1.5 rounded-full bg-amber-400" /> 寰呮帓浜� {{ summary.workOrderPrepareCount }}
+              <span class="mx-1">|</span>
+              <span class="inline-block size-1.5 rounded-full bg-emerald-400" /> 宸插畬鎴� {{ summary.workOrderFinishedCount }}
+            </div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-blue-500/5 to-cyan-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-blue-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+
+    <!-- 浠婃棩浜ч噺 -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('output')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'output')"
+        @mouseleave="onMouseLeave('output')"
+        @click="emit('navigate', 'MesProFeedback')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-emerald-500 to-green-400 shadow-lg shadow-emerald-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:trending-up" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">浠婃棩浜ч噺</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-2xl font-bold leading-tight text-emerald-600 dark:text-emerald-400" :end-val="summary.todayOutput" :duration="1200" />
+              <span class="text-xs text-gray-400">浠�</span>
+            </div>
+            <div class="mt-1 text-xs text-gray-400">
+              鏄ㄦ棩 {{ summary.yesterdayOutput }} 浠�
+              <span v-if="summary.yesterdayOutput > 0 && summary.todayOutput >= summary.yesterdayOutput" class="text-emerald-500">鈫�</span>
+              <span v-else-if="summary.yesterdayOutput > 0" class="text-red-500">鈫�</span>
+            </div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-emerald-500/5 to-green-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-emerald-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+
+    <!-- 璐ㄩ噺鍚堟牸鐜� -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('quality')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'quality')"
+        @mouseleave="onMouseLeave('quality')"
+        @click="emit('navigate', 'MesQcRecord')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-orange-500 to-amber-400 shadow-lg shadow-orange-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:shield-check" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">璐ㄩ噺鍚堟牸鐜�</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo
+                class="text-2xl font-bold leading-tight"
+                :class="qualityRate >= 95 ? 'text-emerald-600 dark:text-emerald-400' : qualityRate >= 80 ? 'text-orange-600 dark:text-orange-400' : 'text-red-600 dark:text-red-400'"
+                :decimals="1" :end-val="qualityRate" :duration="1200"
+              />
+              <span class="text-xs text-gray-400">%</span>
+            </div>
+            <div class="mt-1 text-xs text-gray-400">
+              鍚堟牸 {{ summary.todayQualifiedQuantity }} | 涓嶈壇 {{ summary.todayUnqualifiedQuantity }}
+            </div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-orange-500/5 to-amber-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-orange-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+
+    <!-- 璁惧杩愯鐜� -->
+    <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
+      <div
+        class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0"
+        :style="tiltStyle('equip')"
+        @mousemove="(e: MouseEvent) => onMouseMove(e, 'equip')"
+        @mouseleave="onMouseLeave('equip')"
+        @click="emit('navigate', 'MesDvMachinery')"
+      >
+        <div class="relative z-10 flex items-center gap-4 p-5">
+          <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-violet-500 to-purple-400 shadow-lg shadow-violet-500/25 transition-transform duration-300 group-hover:scale-110">
+            <IconifyIcon class="size-6 text-white" icon="lucide:cpu" />
+          </div>
+          <div class="min-w-0 flex-1">
+            <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">璁惧杩愯鐜�</div>
+            <div class="flex items-baseline gap-1">
+              <CountTo class="text-2xl font-bold leading-tight text-violet-600 dark:text-violet-400" :decimals="1" :end-val="equipmentRate" :duration="1200" />
+              <span class="text-xs text-gray-400">%</span>
+            </div>
+            <div class="mt-1 flex items-center gap-1 text-xs text-gray-400">
+              <span class="text-red-400">鍋滄満 {{ summary.machineryStop }}</span>
+              <span class="mx-1">|</span>
+              <span class="text-amber-400">缁存姢 {{ summary.machineryMaintenance }}</span>
+            </div>
+          </div>
+        </div>
+        <div class="absolute inset-0 -z-0 bg-gradient-to-br from-violet-500/5 to-purple-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
+        <div class="absolute -right-6 -top-6 size-24 rounded-full bg-violet-400/10 blur-xl transition-all duration-300 group-hover:scale-150" />
+      </div>
+    </Col>
+  </Row>
+</template>
+
+<style scoped>
+.glass-tilt {
+  background: linear-gradient(135deg, rgba(255,255,255,0.85), rgba(255,255,255,0.55));
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
+  border: 1px solid rgba(255,255,255,0.6);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.04),
+    0 1px 4px rgba(0,0,0,0.02),
+    inset 0 1px 0 rgba(255,255,255,0.8);
+  transform-style: preserve-3d;
+}
+
+.dark .glass-tilt {
+  background: linear-gradient(135deg, rgba(30,30,55,0.75), rgba(20,20,40,0.5));
+  border: 1px solid rgba(255,255,255,0.08);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.3),
+    0 1px 4px rgba(0,0,0,0.2),
+    inset 0 1px 0 rgba(255,255,255,0.04);
+}
+</style>
diff --git a/src/views/dashboard/analytics/modules/production-trend.vue b/src/views/dashboard/analytics/modules/production-trend.vue
new file mode 100644
index 0000000..e6ad064
--- /dev/null
+++ b/src/views/dashboard/analytics/modules/production-trend.vue
@@ -0,0 +1,78 @@
+<script lang="ts" setup>
+import type { MesHomeApi } from '#/api/mes/home';
+import type { EchartsUIType } from '@vben/plugins/echarts';
+
+import { onMounted, ref, watch } from 'vue';
+
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { getProductionTrend } from '#/api/mes/home';
+
+defineOptions({ name: 'DashboardProductionTrend' });
+
+const chartRef = ref<EchartsUIType>();
+const { renderEcharts } = useEcharts(chartRef);
+
+async function loadTrend() {
+  const data = await getProductionTrend(7);
+  const dates = data.map((d: MesHomeApi.ProductionTrend) => d.date.slice(5));
+  const quantities = data.map((d: MesHomeApi.ProductionTrend) => d.quantity);
+  const qualified = data.map((d: MesHomeApi.ProductionTrend) => d.qualifiedQuantity);
+  const unqualified = data.map((d: MesHomeApi.ProductionTrend) => d.unqualifiedQuantity);
+
+  renderEcharts({
+    grid: { bottom: 30, containLabel: true, left: 10, right: 30, top: 30 },
+    legend: { bottom: 0, data: ['浜ч噺', '鍚堟牸鍝�', '涓嶈壇鍝�'], textStyle: { fontSize: 12 } },
+    tooltip: { trigger: 'axis' },
+    xAxis: {
+      axisTick: { show: false },
+      boundaryGap: false,
+      data: dates,
+      type: 'category',
+    },
+    yAxis: {
+      axisTick: { show: false },
+      splitNumber: 4,
+      type: 'value',
+    },
+    series: [
+      {
+        barMaxWidth: 32,
+        data: quantities,
+        itemStyle: {
+          borderRadius: 4,
+          color: '#2563eb',
+        },
+        name: '浜ч噺',
+        type: 'bar',
+      },
+      {
+        data: qualified,
+        itemStyle: { color: '#16a34a' },
+        name: '鍚堟牸鍝�',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 6,
+        type: 'line',
+      },
+      {
+        data: unqualified,
+        itemStyle: { color: '#dc2626' },
+        name: '涓嶈壇鍝�',
+        smooth: true,
+        symbol: 'diamond',
+        symbolSize: 6,
+        type: 'line',
+      },
+    ],
+  });
+}
+
+onMounted(() => {
+  loadTrend();
+});
+</script>
+
+<template>
+  <EchartsUI ref="chartRef" class="!h-[320px]" />
+</template>
diff --git a/src/views/dashboard/analytics/modules/quick-actions.vue b/src/views/dashboard/analytics/modules/quick-actions.vue
new file mode 100644
index 0000000..f84affb
--- /dev/null
+++ b/src/views/dashboard/analytics/modules/quick-actions.vue
@@ -0,0 +1,73 @@
+<script lang="ts" setup>
+import { IconifyIcon } from '@vben/icons';
+
+defineOptions({ name: 'DashboardQuickActions' });
+
+const emit = defineEmits<{
+  navigate: [name: string];
+}>();
+
+interface QuickAction {
+  title: string;
+  icon: string;
+  route: string;
+  gradient: string;
+  shadow: string;
+}
+
+const actions: QuickAction[] = [
+  { title: '鐢熶骇宸ュ崟', icon: 'lucide:file-text', route: 'MesProWorkOrder', gradient: 'from-blue-500 to-cyan-500', shadow: 'shadow-blue-500/15' },
+  { title: '鐢熶骇鎶ュ伐', icon: 'lucide:check-square', route: 'MesProFeedback', gradient: 'from-emerald-500 to-green-500', shadow: 'shadow-emerald-500/15' },
+  { title: '璐ㄦ绠$悊', icon: 'lucide:clipboard-check', route: 'MesQcRecord', gradient: 'from-orange-500 to-amber-500', shadow: 'shadow-orange-500/15' },
+  { title: '璁惧鐐规', icon: 'lucide:settings', route: 'MesDvMachinery', gradient: 'from-violet-500 to-purple-500', shadow: 'shadow-violet-500/15' },
+  { title: '鐢熶骇鎺掔▼', icon: 'lucide:calendar-range', route: 'MesProScheduling', gradient: 'from-cyan-500 to-teal-500', shadow: 'shadow-cyan-500/15' },
+  { title: '瀹夌伅鍛煎彨', icon: 'lucide:bell-ring', route: 'MesProAndon', gradient: 'from-red-500 to-rose-500', shadow: 'shadow-red-500/15' },
+  { title: '鐗╂枡搴撳瓨', icon: 'lucide:package-search', route: 'WlsMaterialStock', gradient: 'from-indigo-500 to-blue-500', shadow: 'shadow-indigo-500/15' },
+  { title: '宸ヨ壓璺嚎', icon: 'lucide:route', route: 'MesPdRouting', gradient: 'from-teal-500 to-emerald-500', shadow: 'shadow-teal-500/15' },
+  { title: '鏁版嵁鎶ヨ〃', icon: 'lucide:bar-chart-big', route: 'MesHomeIndex', gradient: 'from-pink-500 to-rose-500', shadow: 'shadow-pink-500/15' },
+];
+</script>
+
+<template>
+  <div class="glass-card rounded-2xl p-5">
+    <h3 class="mb-4 text-sm font-semibold text-gray-800 dark:text-gray-100">蹇嵎鎿嶄綔</h3>
+    <div class="grid grid-cols-3 gap-3 sm:grid-cols-9">
+      <div
+        v-for="action in actions"
+        :key="action.route"
+        class="group flex cursor-pointer flex-col items-center gap-2 rounded-xl p-3 transition-all duration-300 hover:-translate-y-1"
+        @click="emit('navigate', action.route)"
+      >
+        <div
+          class="flex size-10 items-center justify-center rounded-xl bg-gradient-to-br transition-shadow duration-300 group-hover:shadow-lg"
+          :class="[action.gradient, action.shadow]"
+        >
+          <IconifyIcon :icon="action.icon" class="size-5 text-white" />
+        </div>
+        <span class="text-xs font-medium text-gray-600 dark:text-gray-400">{{ action.title }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.glass-card {
+  background: linear-gradient(135deg, rgba(255,255,255,0.85), rgba(255,255,255,0.55));
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
+  border: 1px solid rgba(255,255,255,0.6);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.04),
+    0 1px 4px rgba(0,0,0,0.02),
+    inset 0 1px 0 rgba(255,255,255,0.8);
+}
+
+.dark .glass-card {
+  background: linear-gradient(135deg, rgba(30,30,55,0.75), rgba(20,20,40,0.5));
+  border: 1px solid rgba(255,255,255,0.08);
+  box-shadow:
+    0 4px 24px rgba(0,0,0,0.3),
+    0 1px 4px rgba(0,0,0,0.2),
+    inset 0 1px 0 rgba(255,255,255,0.04);
+}
+</style>

--
Gitblit v1.9.3