From 600fe725775e28af1f86f7f7f1e2521f494252b1 Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期二, 01 九月 2026 15:27:26 +0800
Subject: [PATCH] feat(mes): 新增管理驾驶舱页面

---
 src/views/management-cockpit/modules/kpi-cards.vue        |  125 ++++
 src/views/management-cockpit/modules/quality-chart.vue    |   67 ++
 src/views/management-cockpit/modules/trace-chart.vue      |  125 ++++
 src/views/management-cockpit/modules/outbound-chart.vue   |   77 ++
 package.json                                              |    2 
 src/views/management-cockpit/index.vue                    |  207 +++++++
 src/api/mes/management-cockpit/index.ts                   |  158 ++++++
 src/views/management-cockpit/modules/inventory-chart.vue  |   82 +++
 pnpm-lock.yaml                                            |  497 ++++++++++++------
 src/views/management-cockpit/chart-options.ts             |  113 ++++
 src/views/management-cockpit/modules/production-trend.vue |   56 ++
 11 files changed, 1,330 insertions(+), 179 deletions(-)

diff --git a/package.json b/package.json
index 08c8f5e..a26e953 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,8 @@
     "fast-xml-parser": "catalog:",
     "gsap": "^3.15.0",
     "highlight.js": "catalog:",
+    "html-to-image": "^1.11.13",
+    "jspdf": "^4.2.1",
     "livekit-client": "^2.20.0",
     "pinia": "catalog:",
     "sortablejs": "catalog:",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index af265b9..caab85b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -119,7 +119,7 @@
       version: 7.0.0
     '@types/codemirror':
       specifier: ^5.60.17
-      version: 5.60.17
+      version: 5.60.18
     '@types/crypto-js':
       specifier: ^4.2.2
       version: 4.2.2
@@ -134,7 +134,7 @@
       version: 4.5.9
     '@types/markdown-it':
       specifier: ^14.1.2
-      version: 14.1.2
+      version: 14.2.0
     '@types/node':
       specifier: ^25.9.4
       version: 25.9.4
@@ -653,6 +653,12 @@
       highlight.js:
         specifier: 'catalog:'
         version: 11.11.1
+      html-to-image:
+        specifier: ^1.11.13
+        version: 1.11.13
+      jspdf:
+        specifier: ^4.2.1
+        version: 4.2.1
       livekit-client:
         specifier: ^2.20.0
         version: 2.20.0(@types/dom-mediacapture-record@1.0.22)
@@ -1625,10 +1631,10 @@
     devDependencies:
       '@types/codemirror':
         specifier: 'catalog:'
-        version: 5.60.17
+        version: 5.60.18
       '@types/markdown-it':
         specifier: 'catalog:'
-        version: 14.1.2
+        version: 14.2.0
 
   src/packages/effects/request:
     dependencies:
@@ -3144,17 +3150,14 @@
   '@jridgewell/source-map@0.3.11':
     resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
 
-  '@jridgewell/sourcemap-codec@1.4.10':
-    resolution: {integrity: sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg==}
-
-  '@jridgewell/sourcemap-codec@1.4.14':
-    resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
-
   '@jridgewell/sourcemap-codec@1.5.0':
     resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
 
   '@jridgewell/sourcemap-codec@1.5.5':
     resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+  '@jridgewell/sourcemap-codec@1.6.0':
+    resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==}
 
   '@jridgewell/trace-mapping@0.3.31':
     resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
@@ -4419,8 +4422,8 @@
   '@types/archiver@7.0.0':
     resolution: {integrity: sha512-/3vwGwx9n+mCQdYZ2IKGGHEFL30I96UgBlk8EtRDDFQ9uxM1l4O5Ci6r00EMAkiDaTqD9DQ6nVrWRICnBPtzzg==}
 
-  '@types/codemirror@5.60.17':
-    resolution: {integrity: sha512-AZq2FIsUHVMlp7VSe2hTfl5w4pcUkoFkM3zVsRKsn1ca8CXRDYvnin04+HP2REkwsxemuHqvDofdlhUWNpbwfw==}
+  '@types/codemirror@5.60.18':
+    resolution: {integrity: sha512-aSBOPXH2PXRYixxUpVP1sJ5+S0vEfKDvR+lABZ7Kju/9Qb0SSbZk72NihWaKoQbANyibs92q4DBfYasywxnNkA==}
 
   '@types/conventional-commits-parser@5.0.2':
     resolution: {integrity: sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==}
@@ -4476,8 +4479,8 @@
   '@types/lodash@4.17.24':
     resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==}
 
-  '@types/markdown-it@14.1.2':
-    resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+  '@types/markdown-it@14.2.0':
+    resolution: {integrity: sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==}
 
   '@types/mdurl@2.0.0':
     resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
@@ -4497,11 +4500,17 @@
   '@types/nprogress@0.2.3':
     resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
 
+  '@types/pako@2.0.0':
+    resolution: {integrity: sha512-10+iaz93qR5WYxTo+PMifD5TSxiOtdRaxBf7INGGXMQgTCu8Z/7GYWYFUOS3q/G0nE5boj1r4FEB+WSy7s5gbA==}
+
   '@types/qrcode@1.5.6':
     resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
 
   '@types/qs@6.15.1':
     resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==}
+
+  '@types/raf@3.4.3':
+    resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
 
   '@types/readdir-glob@1.1.5':
     resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==}
@@ -4743,8 +4752,8 @@
   '@vue/devtools-api@6.5.0':
     resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
 
-  '@vue/devtools-api@7.5.2':
-    resolution: {integrity: sha512-VxPbAQxJrYSIkoGVvQ2oOoKW8u4CMpvRLySTxhoJA38z8bQEGy9GO33eoRY/DulJbSFRfjZFNvH+dh8B4qpesQ==}
+  '@vue/devtools-api@7.7.10':
+    resolution: {integrity: sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==}
 
   '@vue/devtools-api@7.7.7':
     resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
@@ -4757,8 +4766,8 @@
     peerDependencies:
       vue: ^3.0.0
 
-  '@vue/devtools-kit@7.5.2':
-    resolution: {integrity: sha512-0leUOE2HBfl8sHf9ePKzxqnCFskkU22tWWqd9OfeSlslAKE30/TViYvWcF4vgQmPlJnAAdHU0WfW5dYlCeOiuw==}
+  '@vue/devtools-kit@7.7.10':
+    resolution: {integrity: sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==}
 
   '@vue/devtools-kit@7.7.7':
     resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
@@ -4766,11 +4775,8 @@
   '@vue/devtools-kit@8.1.3':
     resolution: {integrity: sha512-cRn7GXiCQkMYU2Z3h3pM4YO/ndbx9FY1yLDAqIqPLcmIq4H6zAOJHein6tvZU3AfPwgrodqLiPBEF+YQaS8AxA==}
 
-  '@vue/devtools-shared@7.5.2':
-    resolution: {integrity: sha512-+zmcixnD6TAo+zwm30YuwZckhL9iIi4u+gFwbq9C8zpm3SMndTlEYZtNhAHUhOXB+bCkzyunxw80KQ/T0trF4w==}
-
-  '@vue/devtools-shared@7.7.7':
-    resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
+  '@vue/devtools-shared@7.7.10':
+    resolution: {integrity: sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==}
 
   '@vue/devtools-shared@8.1.3':
     resolution: {integrity: sha512-CM3uIPL+v+lrJUk33+pxspYo0MhuMWlCvf7zC9fybifvCPyM2jUbYRPwoYEJgYbwRqPikm5HozbUhp60MF2QuA==}
@@ -4946,8 +4952,8 @@
     resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
     engines: {node: '>=8'}
 
-  ansi-regex@6.2.2:
-    resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+  ansi-regex@6.3.0:
+    resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==}
     engines: {node: '>=12'}
 
   ansi-styles@4.3.0:
@@ -5147,6 +5153,10 @@
   bare-url@2.4.5:
     resolution: {integrity: sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==}
 
+  base64-arraybuffer@1.0.2:
+    resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
+    engines: {node: '>= 0.6.0'}
+
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
@@ -5164,9 +5174,6 @@
 
   bindings@1.5.0:
     resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
-
-  birpc@0.2.19:
-    resolution: {integrity: sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==}
 
   birpc@2.3.0:
     resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==}
@@ -5306,6 +5313,10 @@
   caniuse-lite@1.0.30001799:
     resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==}
 
+  canvg@3.0.11:
+    resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
+    engines: {node: '>=10.0.0'}
+
   chalk@4.1.2:
     resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
     engines: {node: '>=10'}
@@ -5400,8 +5411,8 @@
   color-name@1.1.4:
     resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
 
-  colord@2.9.3:
-    resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
+  colord@2.10.0:
+    resolution: {integrity: sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==}
 
   colorjs.io@0.5.0:
     resolution: {integrity: sha512-qekjTiBLM3F/sXKks/ih5aWaHIGu+Ftel0yKEvmpbKvmxpNOhojKgha5uiWEUOqEpRjC1Tq3nJRT7WgdBOxIGg==}
@@ -5577,6 +5588,7 @@
 
   crypto-js@4.2.0:
     resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
+    deprecated: Active development of CryptoJS has been discontinued. This library is no longer maintained.
 
   crypto-random-string@2.0.0:
     resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
@@ -5585,6 +5597,9 @@
   css-functions-list@3.3.3:
     resolution: {integrity: sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==}
     engines: {node: '>=12'}
+
+  css-line-break@2.1.0:
+    resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
 
   css-select@5.1.0:
     resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
@@ -5823,8 +5838,8 @@
     resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==}
     engines: {node: '>=18'}
 
-  default-browser@5.5.0:
-    resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==}
+  default-browser@5.5.1:
+    resolution: {integrity: sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==}
     engines: {node: '>=18'}
 
   default-passive-events@2.0.0:
@@ -5942,8 +5957,8 @@
   dot-case@3.0.4:
     resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
 
-  dot-prop@10.1.0:
-    resolution: {integrity: sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==}
+  dot-prop@10.2.0:
+    resolution: {integrity: sha512-BTJ9aZYL3vCfZlZOBLy9v8TUqWGQ0pzFnygKwFZt5udj6viBoFIBviKPUoZLDCPn1FoXffv6McQFDenrm5Krfw==}
     engines: {node: '>=20'}
 
   dot-prop@5.3.0:
@@ -6049,8 +6064,8 @@
   error-stack-parser-es@1.0.5:
     resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
 
-  errx@0.1.0:
-    resolution: {integrity: sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==}
+  errx@0.1.2:
+    resolution: {integrity: sha512-chfpPHmCerdo/rXr/nNvPZRkV4WwDRwzwnsJ0Uzz3tVi8Z41tDctRjduYy1138ii77AFlts1qvWtX3g/Acg91Q==}
 
   es-abstract-get@1.0.0:
     resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==}
@@ -6265,11 +6280,6 @@
     resolution: {integrity: sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
-  esprima@2.7.3:
-    resolution: {integrity: sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==}
-    engines: {node: '>=0.10.0'}
-    hasBin: true
-
   esprima@4.0.1:
     resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
     engines: {node: '>=4'}
@@ -6349,6 +6359,9 @@
 
   fast-levenshtein@2.0.6:
     resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+  fast-png@6.2.0:
+    resolution: {integrity: sha512-fO4DewoEd9WwuP8DQcfj8Tlc88Jno6lJAjlDYzvJSqMIZwxUpRT4zuzPXgqygjJqngBdCbeQRaL/FVz3InExhA==}
 
   fast-uri@3.1.2:
     resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==}
@@ -6676,6 +6689,10 @@
     resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
     engines: {node: '>=12.0.0'}
 
+  highlight.js@11.12.0:
+    resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==}
+    engines: {node: '>=12.0.0'}
+
   hookable@5.5.3:
     resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
 
@@ -6711,6 +6728,13 @@
   html-tags@5.1.0:
     resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==}
     engines: {node: '>=20.10'}
+
+  html-to-image@1.11.13:
+    resolution: {integrity: sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==}
+
+  html2canvas@1.4.1:
+    resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
+    engines: {node: '>=8.0.0'}
 
   htmlparser2@10.1.0:
     resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
@@ -6764,10 +6788,6 @@
 
   ignore@5.3.2:
     resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
-    engines: {node: '>= 4'}
-
-  ignore@7.0.5:
-    resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
     engines: {node: '>= 4'}
 
   ignore@7.0.6:
@@ -6827,6 +6847,9 @@
   internmap@2.0.3:
     resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
     engines: {node: '>=12'}
+
+  iobuffer@5.4.0:
+    resolution: {integrity: sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==}
 
   ioredis@5.11.1:
     resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==}
@@ -6966,8 +6989,8 @@
     resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==}
     engines: {node: '>=0.10.0'}
 
-  is-plain-object@5.0.0:
-    resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
+  is-plain-object@5.1.0:
+    resolution: {integrity: sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==}
     engines: {node: '>=0.10.0'}
 
   is-reference@1.2.1:
@@ -7101,8 +7124,8 @@
   js-tokens@9.0.1:
     resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
 
-  js-yaml@3.6.1:
-    resolution: {integrity: sha512-BLv3oxhfET+w5fjPwq3PsAsxzi9i3qzU//HMpWVz0A6KplF86HdR9x2TGnv9DXhSUrO7LO8czUiTd3yb3mLSvg==}
+  js-yaml@3.15.2:
+    resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==}
     hasBin: true
 
   js-yaml@4.2.0:
@@ -7168,6 +7191,9 @@
   jsonpointer@5.0.1:
     resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
     engines: {node: '>=0.10.0'}
+
+  jspdf@4.2.1:
+    resolution: {integrity: sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==}
 
   katex@0.16.47:
     resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==}
@@ -7492,8 +7518,8 @@
   mdn-data@2.28.1:
     resolution: {integrity: sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==}
 
-  mdurl@2.0.0:
-    resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
+  mdurl@2.1.0:
+    resolution: {integrity: sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==}
 
   meow@12.0.1:
     resolution: {integrity: sha512-/QOqMALNoKQcJAOOdIXjNLtfcCdLXbMFyB1fOOPdm6RzfBTlsuodOCTBDjVbeUSmgDQb8UI2oONqYGtq1PKKKA==}
@@ -7669,8 +7695,8 @@
     resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
     hasBin: true
 
-  node-mock-http@1.0.4:
-    resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==}
+  node-mock-http@1.0.5:
+    resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==}
 
   node-releases@2.0.48:
     resolution: {integrity: sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==}
@@ -7736,8 +7762,8 @@
   ofetch@1.5.1:
     resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
 
-  ohash@2.0.11:
-    resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
+  ohash@2.0.12:
+    resolution: {integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==}
 
   on-finished@2.4.1:
     resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
@@ -7747,8 +7773,8 @@
     resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
     engines: {node: '>=18'}
 
-  open@11.0.0:
-    resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==}
+  open@11.0.2:
+    resolution: {integrity: sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==}
     engines: {node: '>=20'}
 
   optionator@0.9.4:
@@ -7823,8 +7849,8 @@
   package-json-from-dist@1.0.1:
     resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
 
-  package-manager-detector@1.6.0:
-    resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
+  package-manager-detector@1.8.0:
+    resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==}
 
   pako@2.2.0:
     resolution: {integrity: sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==}
@@ -7915,6 +7941,9 @@
 
   perfect-debounce@2.1.0:
     resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==}
+
+  performance-now@2.1.0:
+    resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
 
   picocolors@1.1.1:
     resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -8034,6 +8063,10 @@
     resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==}
     engines: {node: '>=20'}
 
+  powershell-utils@0.2.1:
+    resolution: {integrity: sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==}
+    engines: {node: '>=20'}
+
   preact@10.29.2:
     resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==}
 
@@ -8049,12 +8082,12 @@
     resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
     engines: {node: ^14.13.1 || >=16.0.0}
 
-  pretty-bytes@7.1.0:
-    resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==}
+  pretty-bytes@7.1.2:
+    resolution: {integrity: sha512-spZIOSORH9joSPGN0L/tYfvINH+xbOpnI3kdcEkq0mzGMjFvDZzyRVbqEOV0wACF0gggdKE9RWO2mosGsBh/Rg==}
     engines: {node: '>=20'}
 
-  pretty-ms@9.3.0:
-    resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
+  pretty-ms@9.3.1:
+    resolution: {integrity: sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==}
     engines: {node: '>=18'}
 
   prismjs@1.30.0:
@@ -8071,11 +8104,11 @@
   prosemirror-changeset@2.4.1:
     resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==}
 
-  prosemirror-commands@1.7.1:
-    resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
+  prosemirror-commands@1.7.2:
+    resolution: {integrity: sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw==}
 
-  prosemirror-dropcursor@1.8.2:
-    resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
+  prosemirror-dropcursor@1.8.3:
+    resolution: {integrity: sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ==}
 
   prosemirror-gapcursor@1.4.1:
     resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==}
@@ -8151,11 +8184,14 @@
   radix3@1.1.2:
     resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
 
+  raf@3.4.1:
+    resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
+
   randomcolor@0.6.2:
     resolution: {integrity: sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A==}
 
-  range-parser@1.2.1:
-    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+  range-parser@1.3.0:
+    resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==}
     engines: {node: '>= 0.6'}
 
   rc9@3.0.1:
@@ -8213,6 +8249,9 @@
 
   regenerate@1.4.2:
     resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+
+  regenerator-runtime@0.13.11:
+    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
 
   regexp.prototype.flags@1.5.4:
     resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
@@ -8282,6 +8321,10 @@
 
   rfdc@1.4.1:
     resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+  rgbcolor@1.0.1:
+    resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
+    engines: {node: '>= 0.8.15'}
 
   rimraf@5.0.5:
     resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
@@ -8704,6 +8747,10 @@
   sprintf-js@1.0.3:
     resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
 
+  stackblur-canvas@2.0.0:
+    resolution: {integrity: sha512-660gH1SpjeKyfUYnne9nuIya7CDGds6NdIrxzOToSgaSlOqbh7UYGP9VlxlQ8IX7af/nPuAHLkN03l84OesX7Q==}
+    deprecated: Fixed build bug in 2.2.0 as not converting ES6 source for older browsers
+
   standard-as-callback@2.1.0:
     resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
 
@@ -8749,8 +8796,8 @@
     resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==}
     engines: {node: '>=20'}
 
-  string.prototype.matchall@4.0.12:
-    resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
+  string.prototype.matchall@4.1.0:
+    resolution: {integrity: sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==}
     engines: {node: '>= 0.4'}
 
   string.prototype.trim@1.2.11:
@@ -8825,12 +8872,12 @@
   style-value-types@5.1.2:
     resolution: {integrity: sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q==}
 
-  stylelint-config-html@1.1.0:
-    resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==}
-    engines: {node: ^12 || >=14}
+  stylelint-config-html@2.0.0:
+    resolution: {integrity: sha512-Lk1NPEdUxzHkPv3ehktjpiOk4MPaqQ3H8fkvhxdWlQpaZCm9ze0SoMbRQLqkUxEMfPE1G9/x968uxm/+d2njoA==}
+    engines: {node: ^22.12 || >=24}
     peerDependencies:
-      postcss-html: ^1.0.0
-      stylelint: '>=14.0.0'
+      postcss-html: ^2.0.0
+      stylelint: '>=16.0.0'
 
   stylelint-config-recess-order@7.7.0:
     resolution: {integrity: sha512-TWRkg+BrwHOki4pi9y1emWgx6pFwZXOYZhNsbEObke/mzYUJVJvDEzJJQXhH7ajslQJGcrExy8ZvJqDiUbhFpA==}
@@ -8930,6 +8977,10 @@
   sver@2.0.1:
     resolution: {integrity: sha512-zqIx4tK5+gR8xE8VgLNr+IA0mqKDgMNdsfX86DTfNNnVKyuVUhjGdz6KA0rO3q445WUfGpEeJym1m3Xym7ot6w==}
 
+  svg-pathdata@6.0.3:
+    resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
+    engines: {node: '>=12.0.0'}
+
   svg-tags@1.0.0:
     resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
 
@@ -9009,6 +9060,9 @@
   text-extensions@2.4.0:
     resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==}
     engines: {node: '>=8'}
+
+  text-segmentation@1.0.3:
+    resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
 
   theme-colors@0.1.0:
     resolution: {integrity: sha512-6gTEHQqWlQNiOEGHCSSQmU//E5SnXHJ4H7oHQOD8x77CvNYNQAmt73dqR71mzw5ULV87zaHLxK5pIBnsToFuZw==}
@@ -9198,8 +9252,8 @@
   ufo@1.6.4:
     resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}
 
-  ultrahtml@1.6.0:
-    resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==}
+  ultrahtml@1.7.0:
+    resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==}
 
   unbox-primitive@1.1.0:
     resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
@@ -9316,6 +9370,10 @@
 
   unplugin-utils@0.3.1:
     resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==}
+    engines: {node: '>=20.19.0'}
+
+  unplugin-utils@0.3.2:
+    resolution: {integrity: sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==}
     engines: {node: '>=20.19.0'}
 
   unplugin-vue@7.2.0:
@@ -9444,6 +9502,9 @@
 
   util-deprecate@1.0.2:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  utrie@1.0.2:
+    resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
 
   valibot@1.4.1:
     resolution: {integrity: sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==}
@@ -9804,8 +9865,8 @@
     resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==}
     engines: {node: ^20.17.0 || >=22.9.0}
 
-  wsl-utils@0.3.1:
-    resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
+  wsl-utils@1.0.0:
+    resolution: {integrity: sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==}
     engines: {node: '>=20'}
 
   xe-utils@3.9.1:
@@ -9868,16 +9929,16 @@
     resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
     engines: {node: '>=12'}
 
-  yargs@18.0.0:
-    resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==}
+  yargs@18.1.0:
+    resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==}
     engines: {node: ^20.19.0 || ^22.12.0 || >=23}
 
   yocto-queue@1.2.2:
     resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==}
     engines: {node: '>=12.20'}
 
-  yoctocolors@2.1.2:
-    resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
+  yoctocolors@2.2.0:
+    resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==}
     engines: {node: '>=18'}
 
   youch-core@0.3.3:
@@ -9923,7 +9984,7 @@
 
   '@antfu/install-pkg@1.1.0':
     dependencies:
-      package-manager-detector: 1.6.0
+      package-manager-detector: 1.8.0
       tinyexec: 1.2.4
 
   '@apideck/better-ajv-errors@0.3.7(ajv@8.20.0)':
@@ -11185,7 +11246,7 @@
     dependencies:
       escape-string-regexp: 4.0.0
       eslint: 10.5.0(jiti@2.7.0)
-      ignore: 7.0.5
+      ignore: 7.0.6
 
   '@eslint-community/eslint-utils@4.9.1(eslint@10.5.0(jiti@2.7.0))':
     dependencies:
@@ -11456,13 +11517,13 @@
 
   '@jridgewell/gen-mapping@0.3.13':
     dependencies:
-      '@jridgewell/sourcemap-codec': 1.5.0
+      '@jridgewell/sourcemap-codec': 1.6.0
       '@jridgewell/trace-mapping': 0.3.31
 
   '@jridgewell/gen-mapping@0.3.5':
     dependencies:
       '@jridgewell/set-array': 1.2.1
-      '@jridgewell/sourcemap-codec': 1.4.10
+      '@jridgewell/sourcemap-codec': 1.6.0
       '@jridgewell/trace-mapping': 0.3.31
 
   '@jridgewell/remapping@2.3.5':
@@ -11479,18 +11540,16 @@
       '@jridgewell/gen-mapping': 0.3.5
       '@jridgewell/trace-mapping': 0.3.31
 
-  '@jridgewell/sourcemap-codec@1.4.10': {}
-
-  '@jridgewell/sourcemap-codec@1.4.14': {}
-
   '@jridgewell/sourcemap-codec@1.5.0': {}
 
   '@jridgewell/sourcemap-codec@1.5.5': {}
 
+  '@jridgewell/sourcemap-codec@1.6.0': {}
+
   '@jridgewell/trace-mapping@0.3.31':
     dependencies:
       '@jridgewell/resolve-uri': 3.1.2
-      '@jridgewell/sourcemap-codec': 1.4.14
+      '@jridgewell/sourcemap-codec': 1.6.0
 
   '@jspm/fetch@0.1.0': {}
 
@@ -11633,14 +11692,14 @@
       consola: 3.4.2
       defu: 6.1.7
       destr: 2.0.5
-      errx: 0.1.0
+      errx: 0.1.2
       exsolve: 1.1.0
       ignore: 7.0.6
       jiti: 2.7.0
       klona: 2.0.6
       knitwork: 1.3.0
       mlly: 1.8.2
-      ohash: 2.0.11
+      ohash: 2.0.12
       pathe: 2.0.3
       pkg-types: 2.3.1
       rc9: 3.0.1
@@ -12416,8 +12475,8 @@
   '@tiptap/pm@3.27.1':
     dependencies:
       prosemirror-changeset: 2.4.1
-      prosemirror-commands: 1.7.1
-      prosemirror-dropcursor: 1.8.2
+      prosemirror-commands: 1.7.2
+      prosemirror-dropcursor: 1.8.3
       prosemirror-gapcursor: 1.4.1
       prosemirror-history: 1.5.0
       prosemirror-inputrules: 1.5.1
@@ -12471,7 +12530,7 @@
       ejs: 3.1.10
       json5: 2.2.3
       magic-string: 0.30.21
-      string.prototype.matchall: 4.0.12
+      string.prototype.matchall: 4.1.0
 
   '@tybys/wasm-util@0.10.2':
     dependencies:
@@ -12482,7 +12541,7 @@
     dependencies:
       '@types/readdir-glob': 1.1.5
 
-  '@types/codemirror@5.60.17':
+  '@types/codemirror@5.60.18':
     dependencies:
       '@types/tern': 0.23.9
 
@@ -12535,7 +12594,7 @@
 
   '@types/lodash@4.17.24': {}
 
-  '@types/markdown-it@14.1.2':
+  '@types/markdown-it@14.2.0':
     dependencies:
       '@types/linkify-it': 5.0.0
       '@types/mdurl': 2.0.0
@@ -12554,11 +12613,16 @@
 
   '@types/nprogress@0.2.3': {}
 
+  '@types/pako@2.0.0': {}
+
   '@types/qrcode@1.5.6':
     dependencies:
       '@types/node': 25.9.4
 
   '@types/qs@6.15.1': {}
+
+  '@types/raf@3.4.3':
+    optional: true
 
   '@types/readdir-glob@1.1.5':
     dependencies:
@@ -12587,7 +12651,7 @@
       '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
       '@typescript-eslint/visitor-keys': 8.62.0
       eslint: 10.5.0(jiti@2.7.0)
-      ignore: 7.0.5
+      ignore: 7.0.6
       natural-compare: 1.4.0
       ts-api-utils: 2.5.0(typescript@6.0.3)
       typescript: 6.0.3
@@ -12891,9 +12955,9 @@
 
   '@vue/devtools-api@6.5.0': {}
 
-  '@vue/devtools-api@7.5.2':
+  '@vue/devtools-api@7.7.10':
     dependencies:
-      '@vue/devtools-kit': 7.5.2
+      '@vue/devtools-kit': 7.7.10
 
   '@vue/devtools-api@7.7.7':
     dependencies:
@@ -12909,10 +12973,10 @@
       '@vue/devtools-shared': 8.1.3
       vue: 3.5.38(typescript@6.0.3)
 
-  '@vue/devtools-kit@7.5.2':
+  '@vue/devtools-kit@7.7.10':
     dependencies:
-      '@vue/devtools-shared': 7.5.2
-      birpc: 0.2.19
+      '@vue/devtools-shared': 7.7.10
+      birpc: 2.3.0
       hookable: 5.5.3
       mitt: 3.0.1
       perfect-debounce: 1.0.0
@@ -12921,7 +12985,7 @@
 
   '@vue/devtools-kit@7.7.7':
     dependencies:
-      '@vue/devtools-shared': 7.7.7
+      '@vue/devtools-shared': 7.7.10
       birpc: 2.3.0
       hookable: 5.5.3
       mitt: 3.0.1
@@ -12936,11 +13000,7 @@
       hookable: 5.5.3
       perfect-debounce: 2.1.0
 
-  '@vue/devtools-shared@7.5.2':
-    dependencies:
-      rfdc: 1.4.1
-
-  '@vue/devtools-shared@7.7.7':
+  '@vue/devtools-shared@7.7.10':
     dependencies:
       rfdc: 1.4.1
 
@@ -13127,7 +13187,7 @@
 
   ansi-regex@5.0.1: {}
 
-  ansi-regex@6.2.2: {}
+  ansi-regex@6.3.0: {}
 
   ansi-styles@4.3.0:
     dependencies:
@@ -13350,6 +13410,9 @@
     dependencies:
       bare-path: 3.0.1
 
+  base64-arraybuffer@1.0.2:
+    optional: true
+
   base64-js@1.5.1: {}
 
   baseline-browser-mapping@2.10.38: {}
@@ -13363,8 +13426,6 @@
   bindings@1.5.0:
     dependencies:
       file-uri-to-path: 1.0.0
-
-  birpc@0.2.19: {}
 
   birpc@2.3.0: {}
 
@@ -13473,7 +13534,7 @@
       exsolve: 1.1.0
       giget: 3.3.0
       jiti: 2.7.0
-      ohash: 2.0.11
+      ohash: 2.0.12
       pathe: 2.0.3
       perfect-debounce: 2.1.0
       pkg-types: 2.3.1
@@ -13539,6 +13600,18 @@
   camunda-bpmn-moddle@7.0.1: {}
 
   caniuse-lite@1.0.30001799: {}
+
+  canvg@3.0.11:
+    dependencies:
+      '@babel/runtime': 7.29.7
+      '@types/raf': 3.4.3
+      core-js: 3.49.0
+      raf: 3.4.1
+      regenerator-runtime: 0.13.11
+      rgbcolor: 1.0.1
+      stackblur-canvas: 2.0.0
+      svg-pathdata: 6.0.3
+    optional: true
 
   chalk@4.1.2:
     dependencies:
@@ -13652,7 +13725,7 @@
 
   color-name@1.1.4: {}
 
-  colord@2.9.3: {}
+  colord@2.10.0: {}
 
   colorjs.io@0.5.0: {}
 
@@ -13800,6 +13873,11 @@
   crypto-random-string@2.0.0: {}
 
   css-functions-list@3.3.3: {}
+
+  css-line-break@2.1.0:
+    dependencies:
+      utrie: 1.0.2
+    optional: true
 
   css-select@5.1.0:
     dependencies:
@@ -14030,7 +14108,7 @@
 
   default-browser-id@5.0.1: {}
 
-  default-browser@5.5.0:
+  default-browser@5.5.1:
     dependencies:
       bundle-name: 4.1.0
       default-browser-id: 5.0.1
@@ -14146,7 +14224,7 @@
       no-case: 3.0.4
       tslib: 2.8.1
 
-  dot-prop@10.1.0:
+  dot-prop@10.2.0:
     dependencies:
       type-fest: 5.7.0
 
@@ -14231,7 +14309,7 @@
 
   error-stack-parser-es@1.0.5: {}
 
-  errx@0.1.0:
+  errx@0.1.2:
     optional: true
 
   es-abstract-get@1.0.0:
@@ -14607,8 +14685,6 @@
       acorn-jsx: 5.3.2(acorn@8.17.0)
       eslint-visitor-keys: 3.0.0
 
-  esprima@2.7.3: {}
-
   esprima@4.0.1: {}
 
   esquery@1.7.0:
@@ -14617,7 +14693,7 @@
 
   esrap@2.2.12(@typescript-eslint/types@8.62.0):
     dependencies:
-      '@jridgewell/sourcemap-codec': 1.5.0
+      '@jridgewell/sourcemap-codec': 1.6.0
     optionalDependencies:
       '@typescript-eslint/types': 8.62.0
 
@@ -14659,10 +14735,10 @@
       is-plain-obj: 4.1.0
       is-stream: 4.0.1
       npm-run-path: 6.0.0
-      pretty-ms: 9.3.0
+      pretty-ms: 9.3.1
       signal-exit: 4.1.0
       strip-final-newline: 4.0.0
-      yoctocolors: 2.1.2
+      yoctocolors: 2.2.0
 
   exsolve@1.1.0: {}
 
@@ -14683,6 +14759,12 @@
   fast-json-stable-stringify@2.1.0: {}
 
   fast-levenshtein@2.0.6: {}
+
+  fast-png@6.2.0:
+    dependencies:
+      '@types/pako': 2.0.0
+      iobuffer: 5.4.0
+      pako: 2.2.0
 
   fast-uri@3.1.2: {}
 
@@ -14983,7 +15065,7 @@
     dependencies:
       '@sindresorhus/merge-streams': 4.0.0
       fast-glob: 3.3.3
-      ignore: 7.0.5
+      ignore: 7.0.6
       is-path-inside: 4.0.0
       slash: 5.1.0
       unicorn-magic: 0.4.0
@@ -15009,7 +15091,7 @@
       defu: 6.1.7
       destr: 2.0.5
       iron-webcrypto: 1.2.1
-      node-mock-http: 1.0.4
+      node-mock-http: 1.0.5
       radix3: 1.1.2
       ufo: 1.6.4
       uncrypto: 0.1.3
@@ -15048,6 +15130,8 @@
 
   highlight.js@11.11.1: {}
 
+  highlight.js@11.12.0: {}
+
   hookable@5.5.3: {}
 
   hookable@6.1.1: {}
@@ -15079,6 +15163,14 @@
   html-tags@3.3.1: {}
 
   html-tags@5.1.0: {}
+
+  html-to-image@1.11.13: {}
+
+  html2canvas@1.4.1:
+    dependencies:
+      css-line-break: 2.1.0
+      text-segmentation: 1.0.3
+    optional: true
 
   htmlparser2@10.1.0:
     dependencies:
@@ -15145,10 +15237,7 @@
 
   ignore@5.3.2: {}
 
-  ignore@7.0.5: {}
-
-  ignore@7.0.6:
-    optional: true
+  ignore@7.0.6: {}
 
   immutable@5.1.7: {}
 
@@ -15186,6 +15275,8 @@
       side-channel: 1.1.1
 
   internmap@2.0.3: {}
+
+  iobuffer@5.4.0: {}
 
   ioredis@5.11.1:
     dependencies:
@@ -15310,7 +15401,7 @@
 
   is-plain-object@3.0.1: {}
 
-  is-plain-object@5.0.0: {}
+  is-plain-object@5.1.0: {}
 
   is-reference@1.2.1:
     dependencies:
@@ -15430,10 +15521,10 @@
 
   js-tokens@9.0.1: {}
 
-  js-yaml@3.6.1:
+  js-yaml@3.15.2:
     dependencies:
       argparse: 1.0.10
-      esprima: 2.7.3
+      esprima: 4.0.1
 
   js-yaml@4.2.0:
     dependencies:
@@ -15489,6 +15580,17 @@
   jsonparse@1.3.1: {}
 
   jsonpointer@5.0.1: {}
+
+  jspdf@4.2.1:
+    dependencies:
+      '@babel/runtime': 7.29.7
+      fast-png: 6.2.0
+      fflate: 0.8.3
+    optionalDependencies:
+      canvg: 3.0.11
+      core-js: 3.49.0
+      dompurify: 3.4.11
+      html2canvas: 1.4.1
 
   katex@0.16.47:
     dependencies:
@@ -15675,7 +15777,7 @@
   log-symbols@7.0.1:
     dependencies:
       is-unicode-supported: 2.1.0
-      yoctocolors: 2.1.2
+      yoctocolors: 2.2.0
 
   loglevel@1.9.2: {}
 
@@ -15740,7 +15842,7 @@
       argparse: 2.0.1
       entities: 4.4.0
       linkify-it: 5.0.1
-      mdurl: 2.0.0
+      mdurl: 2.1.0
       punycode.js: 2.3.1
       uc.micro: 2.1.0
 
@@ -15762,7 +15864,7 @@
     dependencies:
       '@babel/runtime': 7.29.7
       '@vscode/markdown-it-katex': 1.1.2
-      highlight.js: 11.11.1
+      highlight.js: 11.12.0
       katex: 0.16.47
       markdown-it: 14.2.0
       markdown-it-ins: 4.0.0
@@ -15797,7 +15899,7 @@
 
   mdn-data@2.28.1: {}
 
-  mdurl@2.0.0: {}
+  mdurl@2.1.0: {}
 
   meow@12.0.1:
     dependencies:
@@ -15957,7 +16059,7 @@
       db0: 0.3.4
       defu: 6.1.7
       destr: 2.0.5
-      dot-prop: 10.1.0
+      dot-prop: 10.2.0
       esbuild: 0.28.1
       escape-string-regexp: 5.0.0
       etag: 1.8.1
@@ -15977,13 +16079,13 @@
       mime: 4.1.0
       mlly: 1.8.2
       node-fetch-native: 1.6.7
-      node-mock-http: 1.0.4
+      node-mock-http: 1.0.5
       ofetch: 1.5.1
-      ohash: 2.0.11
+      ohash: 2.0.12
       pathe: 2.0.3
       perfect-debounce: 2.1.0
       pkg-types: 2.3.1
-      pretty-bytes: 7.1.0
+      pretty-bytes: 7.1.2
       radix3: 1.1.2
       rollup: 4.62.2
       rollup-plugin-visualizer: 7.0.1(rolldown@1.1.2)(rollup@4.62.2)
@@ -15994,12 +16096,12 @@
       source-map: 0.7.6
       std-env: 4.1.0
       ufo: 1.6.4
-      ultrahtml: 1.6.0
+      ultrahtml: 1.7.0
       uncrypto: 0.1.3
       unctx: 2.5.0
       unenv: 2.0.0-rc.24
       unimport: 6.3.0(rolldown@1.1.2)
-      unplugin-utils: 0.3.1
+      unplugin-utils: 0.3.2
       unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
       untyped: 2.0.0
       unwasm: 0.5.3
@@ -16055,7 +16157,7 @@
 
   node-gyp-build@4.8.4: {}
 
-  node-mock-http@1.0.4: {}
+  node-mock-http@1.0.5: {}
 
   node-releases@2.0.48: {}
 
@@ -16123,7 +16225,7 @@
       node-fetch-native: 1.6.7
       ufo: 1.6.4
 
-  ohash@2.0.11: {}
+  ohash@2.0.12: {}
 
   on-finished@2.4.1:
     dependencies:
@@ -16133,14 +16235,14 @@
     dependencies:
       mimic-function: 5.0.1
 
-  open@11.0.0:
+  open@11.0.2:
     dependencies:
-      default-browser: 5.5.0
+      default-browser: 5.5.1
       define-lazy-prop: 3.0.0
       is-in-ssh: 1.0.0
       is-inside-container: 1.0.0
-      powershell-utils: 0.1.0
-      wsl-utils: 0.3.1
+      powershell-utils: 0.2.1
+      wsl-utils: 1.0.0
 
   optionator@0.9.4:
     dependencies:
@@ -16246,7 +16348,7 @@
 
   package-json-from-dist@1.0.1: {}
 
-  package-manager-detector@1.6.0: {}
+  package-manager-detector@1.8.0: {}
 
   pako@2.2.0: {}
 
@@ -16330,6 +16432,9 @@
   perfect-debounce@1.0.0: {}
 
   perfect-debounce@2.1.0: {}
+
+  performance-now@2.1.0:
+    optional: true
 
   picocolors@1.1.1: {}
 
@@ -16433,6 +16538,8 @@
 
   powershell-utils@0.1.0: {}
 
+  powershell-utils@0.2.1: {}
+
   preact@10.29.2: {}
 
   prelude-ls@1.2.1: {}
@@ -16441,9 +16548,9 @@
 
   pretty-bytes@6.1.1: {}
 
-  pretty-bytes@7.1.0: {}
+  pretty-bytes@7.1.2: {}
 
-  pretty-ms@9.3.0:
+  pretty-ms@9.3.1:
     dependencies:
       parse-ms: 4.0.0
 
@@ -16457,13 +16564,13 @@
     dependencies:
       prosemirror-transform: 1.12.0
 
-  prosemirror-commands@1.7.1:
+  prosemirror-commands@1.7.2:
     dependencies:
       prosemirror-model: 1.25.9
       prosemirror-state: 1.4.4
       prosemirror-transform: 1.12.0
 
-  prosemirror-dropcursor@1.8.2:
+  prosemirror-dropcursor@1.8.3:
     dependencies:
       prosemirror-state: 1.4.4
       prosemirror-transform: 1.12.0
@@ -16559,9 +16666,14 @@
 
   radix3@1.1.2: {}
 
+  raf@3.4.1:
+    dependencies:
+      performance-now: 2.1.0
+    optional: true
+
   randomcolor@0.6.2: {}
 
-  range-parser@1.2.1: {}
+  range-parser@1.3.0: {}
 
   rc9@3.0.1:
     dependencies:
@@ -16584,7 +16696,7 @@
   read-yaml-file@1.1.0:
     dependencies:
       graceful-fs: 4.2.11
-      js-yaml: 3.6.1
+      js-yaml: 3.15.2
       pify: 4.0.1
       strip-bom: 3.0.0
 
@@ -16645,6 +16757,9 @@
 
   regenerate@1.4.2: {}
 
+  regenerator-runtime@0.13.11:
+    optional: true
+
   regexp.prototype.flags@1.5.4:
     dependencies:
       call-bind: 1.0.9
@@ -16680,7 +16795,7 @@
       '@vueuse/shared': 14.3.0(vue@3.5.38(typescript@6.0.3))
       aria-hidden: 1.2.6
       defu: 6.1.7
-      ohash: 2.0.11
+      ohash: 2.0.12
       vue: 3.5.38(typescript@6.0.3)
     transitivePeerDependencies:
       - '@vue/composition-api'
@@ -16718,6 +16833,9 @@
   reusify@1.1.0: {}
 
   rfdc@1.4.1: {}
+
+  rgbcolor@1.0.1:
+    optional: true
 
   rimraf@5.0.5:
     dependencies:
@@ -16791,10 +16909,10 @@
 
   rollup-plugin-visualizer@7.0.1(rolldown@1.1.2)(rollup@4.62.2):
     dependencies:
-      open: 11.0.0
+      open: 11.0.2
       picomatch: 4.0.4
       source-map: 0.7.6
-      yargs: 18.0.0
+      yargs: 18.1.0
     optionalDependencies:
       rolldown: 1.1.2
       rollup: 4.62.2
@@ -17032,7 +17150,7 @@
       mime-types: 3.0.2
       ms: 2.1.3
       on-finished: 2.4.1
-      range-parser: 1.2.1
+      range-parser: 1.3.0
       statuses: 2.0.2
     transitivePeerDependencies:
       - supports-color
@@ -17178,6 +17296,9 @@
 
   sprintf-js@1.0.3: {}
 
+  stackblur-canvas@2.0.0:
+    optional: true
+
   standard-as-callback@2.1.0: {}
 
   statuses@2.0.2: {}
@@ -17231,7 +17352,7 @@
       get-east-asian-width: 1.6.0
       strip-ansi: 7.2.0
 
-  string.prototype.matchall@4.0.12:
+  string.prototype.matchall@4.1.0:
     dependencies:
       call-bind: 1.0.9
       call-bound: 1.0.4
@@ -17295,7 +17416,7 @@
 
   strip-ansi@7.2.0:
     dependencies:
-      ansi-regex: 6.2.2
+      ansi-regex: 6.3.0
 
   strip-bom@3.0.0: {}
 
@@ -17326,7 +17447,7 @@
       hey-listen: 1.0.8
       tslib: 2.4.0
 
-  stylelint-config-html@1.1.0(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3)):
+  stylelint-config-html@2.0.0(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3)):
     dependencies:
       postcss-html: 1.8.1
       stylelint: 17.13.0(typescript@6.0.3)
@@ -17350,7 +17471,7 @@
       postcss-html: 1.8.1
       semver: 7.8.5
       stylelint: 17.13.0(typescript@6.0.3)
-      stylelint-config-html: 1.1.0(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3))
+      stylelint-config-html: 2.0.0(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3))
       stylelint-config-recommended: 18.0.0(stylelint@17.13.0(typescript@6.0.3))
 
   stylelint-config-recommended@18.0.0(stylelint@17.13.0(typescript@6.0.3)):
@@ -17375,7 +17496,7 @@
       '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1)
       '@csstools/css-tokenizer': 4.0.0
       css-tree: 3.2.1
-      is-plain-object: 5.0.0
+      is-plain-object: 5.1.0
       known-css-properties: 0.37.0
       postcss-media-query-parser: 0.2.3
       postcss-resolve-nested-selector: 0.1.6
@@ -17392,7 +17513,7 @@
       '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
       '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.4)
       '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.4)
-      colord: 2.9.3
+      colord: 2.10.0
       cosmiconfig: 9.0.2(typescript@6.0.3)
       css-functions-list: 3.3.3
       css-tree: 3.2.1
@@ -17404,7 +17525,7 @@
       globby: 16.2.0
       globjoin: 0.1.4
       html-tags: 5.1.0
-      ignore: 7.0.5
+      ignore: 7.0.6
       import-meta-resolve: 4.2.0
       mathml-tag-names: 4.0.0
       meow: 14.1.0
@@ -17466,7 +17587,7 @@
   svelte@5.56.3(@typescript-eslint/types@8.62.0):
     dependencies:
       '@jridgewell/remapping': 2.3.5
-      '@jridgewell/sourcemap-codec': 1.5.0
+      '@jridgewell/sourcemap-codec': 1.6.0
       '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0)
       '@types/estree': 1.0.9
       '@types/trusted-types': 2.0.7
@@ -17485,6 +17606,9 @@
       - '@typescript-eslint/types'
 
   sver@2.0.1: {}
+
+  svg-pathdata@6.0.3:
+    optional: true
 
   svg-tags@1.0.0: {}
 
@@ -17577,6 +17701,11 @@
       - react-native-b4a
 
   text-extensions@2.4.0: {}
+
+  text-segmentation@1.0.3:
+    dependencies:
+      utrie: 1.0.2
+    optional: true
 
   theme-colors@0.1.0: {}
 
@@ -17740,7 +17869,7 @@
 
   ufo@1.6.4: {}
 
-  ultrahtml@1.6.0: {}
+  ultrahtml@1.7.0: {}
 
   unbox-primitive@1.1.0:
     dependencies:
@@ -17805,7 +17934,7 @@
       strip-literal: 3.1.0
       tinyglobby: 0.2.17
       unplugin: 3.0.0
-      unplugin-utils: 0.3.1
+      unplugin-utils: 0.3.2
     optionalDependencies:
       rolldown: 1.1.2
 
@@ -17839,6 +17968,11 @@
       - supports-color
 
   unplugin-utils@0.3.1:
+    dependencies:
+      pathe: 2.0.3
+      picomatch: 4.0.4
+
+  unplugin-utils@0.3.2:
     dependencies:
       pathe: 2.0.3
       picomatch: 4.0.4
@@ -17952,6 +18086,11 @@
 
   util-deprecate@1.0.2: {}
 
+  utrie@1.0.2:
+    dependencies:
+      base64-arraybuffer: 1.0.2
+    optional: true
+
   valibot@1.4.1(typescript@6.0.3):
     optionalDependencies:
       typescript: 6.0.3
@@ -17965,7 +18104,7 @@
 
   vee-validate@4.15.1(vue@3.5.38(typescript@6.0.3)):
     dependencies:
-      '@vue/devtools-api': 7.5.2
+      '@vue/devtools-api': 7.7.10
       type-fest: 4.8.3
       vue: 3.5.38(typescript@6.0.3)
 
@@ -18015,11 +18154,11 @@
       ansis: 4.3.1
       error-stack-parser-es: 1.0.5
       obug: 2.1.3
-      ohash: 2.0.11
-      open: 11.0.0
+      ohash: 2.0.12
+      open: 11.0.2
       perfect-debounce: 2.1.0
       sirv: 3.0.2
-      unplugin-utils: 0.3.1
+      unplugin-utils: 0.3.2
       vite: 8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
       vite-dev-rpc: 2.0.0(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
 
@@ -18432,7 +18571,7 @@
     dependencies:
       signal-exit: 4.1.0
 
-  wsl-utils@0.3.1:
+  wsl-utils@1.0.0:
     dependencies:
       is-wsl: 3.1.1
       powershell-utils: 0.1.0
@@ -18499,18 +18638,18 @@
       y18n: 5.0.8
       yargs-parser: 21.1.1
 
-  yargs@18.0.0:
+  yargs@18.1.0:
     dependencies:
       cliui: 9.0.1
       escalade: 3.2.0
       get-caller-file: 2.0.5
-      string-width: 7.2.0
+      string-width: 8.2.1
       y18n: 5.0.8
       yargs-parser: 22.0.0
 
   yocto-queue@1.2.2: {}
 
-  yoctocolors@2.1.2: {}
+  yoctocolors@2.2.0: {}
 
   youch-core@0.3.3:
     dependencies:
diff --git a/src/api/mes/management-cockpit/index.ts b/src/api/mes/management-cockpit/index.ts
new file mode 100644
index 0000000..c85c268
--- /dev/null
+++ b/src/api/mes/management-cockpit/index.ts
@@ -0,0 +1,158 @@
+import { requestClient } from '#/api/request';
+
+export namespace ManagementCockpitApi {
+  /** 閫氱敤鍚嶇О-鏁伴噺鍒嗗竷椤� */
+  export interface NameCountItem {
+    name: string; // 鍚嶇О锛堜粨搴�/鍝佺/鐘舵��/缁忛攢鍟�/鐪佷唤/娓犻亾绛夛級
+    count: number; // 鏁伴噺
+  }
+
+  /** 鎸夋棩鏁伴噺瓒嬪娍椤� */
+  export interface DailyCountItem {
+    date: string; // 鏃ユ湡 yyyy-MM-dd
+    count: number; // 鏁伴噺
+  }
+
+  /** 璐ㄦ鎸囨爣鍧囧�奸」 */
+  export interface IndicatorAvgItem {
+    indicatorCode: string; // 鎸囨爣缂栫爜
+    indicatorName: string; // 鎸囨爣鍚嶇О锛堢函搴�/鍑�搴�/鍙戣娊鐜�/姘村垎锛�
+    checkCount: number; // 妫�楠屾鏁�
+    averageValue: number; // 鎸囨爣骞冲潎鍊�
+    passRate: number; // 鎸囨爣鍚堟牸鐜囷紙%锛�
+  }
+
+  /** 浜旂淮搴︽眹鎬荤粺璁� */
+  export interface Summary {
+    // 鐢熶骇
+    batchTotal: number; // 鎵规鎬绘暟
+    batchDraft: number; // 鑽夌鎵规
+    batchProducing: number; // 鐢熶骇涓壒娆�
+    batchFinished: number; // 宸插畬鎴愭壒娆�
+    batchCanceled: number; // 宸蹭綔搴熸壒娆�
+    bagCodeTotal: number; // 琚嬬爜鎬绘暟
+    bagCodeGenerated: number; // 宸茬敓鎴愯鐮�
+    bagCodePrinted: number; // 宸插鍑哄嵃鍒疯鐮�
+    bagCodeCanceled: number; // 宸蹭綔搴熻鐮�
+    palletTotal: number; // 鎵樼洏鐮佹�绘暟
+    todayInboundBagCount: number; // 浠婃棩鎵爜鍏ュ簱琚嬫暟
+    // 璐ㄦ
+    qualityTotal: number; // 妫�楠岃褰曟�绘暟
+    qualityPending: number; // 澶勭悊涓�
+    qualityPass: number; // 鍚堟牸
+    qualityFail: number; // 涓嶅悎鏍�
+    qualityPassRate: number; // 妫�楠屽悎鏍肩巼锛�%锛�
+    // 搴撳瓨
+    stockTotal: number; // 褰撳墠搴撳瓨鎬婚噺
+    stockTemporary: number; // 鏆傚瓨搴撳瓨閲�
+    stockQualified: number; // 鍚堟牸搴撳瓨閲�
+    stockOutbound: number; // 宸插嚭搴撴暟閲�
+    lowStockCount: number; // 浣庡簱瀛橀璀︽暟
+    // 鍑哄簱
+    outboundOrderTotal: number; // 鍑哄簱鍗曟�绘暟
+    outboundQuantityTotal: number; // 鍑哄簱鎬婚噺
+    todayOutboundQuantity: number; // 浠婃棩鍑哄簱閲�
+    // 杩芥函
+    scanTotal: number; // 鎵爜鎬绘暟
+    todayScanCount: number; // 浠婃棩鎵爜鏁�
+    scanSuccessCount: number; // 鎵爜鎴愬姛鏁�
+    crossRegionWarningTotal: number; // 绐滆揣棰勮鎬绘暟
+    crossRegionPendingCount: number; // 寰呭鐞嗙獪璐ч璀︽暟
+  }
+
+  /** 鐢熶骇瓒嬪娍椤� */
+  export interface ProductionTrendItem {
+    date: string;
+    batchCount: number; // 鏂板鎵规
+    bagCodeCount: number; // 鏂板琚嬬爜
+    palletCount: number; // 鏂板鎵樼洏
+    inboundBagQuantity: number; // 鎵爜鍏ュ簱琚嬫暟
+  }
+
+  /** 璐ㄦ缁村害缁熻 */
+  export interface QualityStatistics {
+    resultDistribution: NameCountItem[]; // 妫�楠岀粨鏋滃垎甯�
+    indicatorList: IndicatorAvgItem[]; // 鍥涢」鎸囨爣鍧囧��
+  }
+
+  /** 搴撳瓨缁村害缁熻 */
+  export interface InventoryStatistics {
+    warehouseDistribution: NameCountItem[]; // 鎸変粨搴�
+    itemDistribution: NameCountItem[]; // 鎸夊搧绉�
+    stateDistribution: NameCountItem[]; // 鎸夌姸鎬�
+  }
+
+  /** 鍑哄簱缁村害缁熻 */
+  export interface OutboundStatistics {
+    trend: DailyCountItem[]; // 鎸夋棩鍑哄簱瓒嬪娍
+    dealerDistribution: NameCountItem[]; // 鎸夌粡閿�鍟�
+  }
+
+  /** 绐滆揣棰勮姹囨�� */
+  export interface CrossRegionSummary {
+    total: number;
+    pending: number;
+    handled: number;
+    falseAlarm: number;
+    crossProvince: number;
+    crossCity: number;
+  }
+
+  /** 杩芥函缁村害缁熻 */
+  export interface TraceStatistics {
+    scanTrend: DailyCountItem[]; // 鎵爜鎸夋棩瓒嬪娍
+    provinceDistribution: NameCountItem[]; // 鐪佷唤鍒嗗竷
+    channelDistribution: NameCountItem[]; // 娓犻亾鍒嗗竷
+    crossRegionSummary: CrossRegionSummary; // 绐滆揣棰勮姹囨��
+  }
+}
+
+/** 鑾峰緱绠$悊椹鹃┒鑸变簲缁村害姹囨�荤粺璁� */
+export function getManagementCockpitSummary() {
+  return requestClient.get<ManagementCockpitApi.Summary>(
+    '/mes/dashboard/summary',
+  );
+}
+
+/** 鑾峰緱鐢熶骇瓒嬪娍 */
+export function getManagementCockpitProductionTrend(days?: number) {
+  return requestClient.get<ManagementCockpitApi.ProductionTrendItem[]>(
+    '/mes/dashboard/production-trend',
+    { params: { days } },
+  );
+}
+
+/** 鑾峰緱璐ㄦ缁村害缁熻 */
+export function getManagementCockpitQuality() {
+  return requestClient.get<ManagementCockpitApi.QualityStatistics>(
+    '/mes/dashboard/quality-statistics',
+  );
+}
+
+/** 鑾峰緱搴撳瓨缁村害缁熻 */
+export function getManagementCockpitInventory() {
+  return requestClient.get<ManagementCockpitApi.InventoryStatistics>(
+    '/mes/dashboard/inventory-statistics',
+  );
+}
+
+/** 鑾峰緱鍑哄簱缁村害缁熻 */
+export function getManagementCockpitOutbound(days?: number) {
+  return requestClient.get<ManagementCockpitApi.OutboundStatistics>(
+    '/mes/dashboard/outbound-statistics',
+    { params: { days } },
+  );
+}
+
+/** 鑾峰緱杩芥函缁村害缁熻 */
+export function getManagementCockpitTrace(days?: number) {
+  return requestClient.get<ManagementCockpitApi.TraceStatistics>(
+    '/mes/dashboard/trace-statistics',
+    { params: { days } },
+  );
+}
+
+/** 瀵煎嚭绠$悊椹鹃┒鑸� Excel 鎶ヨ〃锛堣繑鍥� Blob锛� */
+export function exportManagementCockpitExcel() {
+  return requestClient.download('/mes/dashboard/export-excel');
+}
diff --git a/src/views/management-cockpit/chart-options.ts b/src/views/management-cockpit/chart-options.ts
new file mode 100644
index 0000000..cb61b2a
--- /dev/null
+++ b/src/views/management-cockpit/chart-options.ts
@@ -0,0 +1,113 @@
+import type { EChartsOption } from '@vben/plugins/echarts';
+
+/** 鐢熶骇瓒嬪娍鍥鹃厤缃紙澶氱郴鍒楁煴鐘�/鎶樼嚎锛� */
+export function getProductionTrendChartOptions(
+  dates: string[],
+  batchCounts: number[],
+  bagCodeCounts: number[],
+  palletCounts: number[],
+  inboundCounts: number[],
+): EChartsOption {
+  return {
+    color: ['#409EFF', '#36cfc9', '#f59e0b', '#67c23a'],
+    grid: { bottom: 40, left: 50, right: 30, top: 30 },
+    legend: { bottom: 0, type: 'scroll' },
+    series: [
+      { barMaxWidth: 24, data: batchCounts, name: '鏂板鎵规', type: 'bar' },
+      { barMaxWidth: 24, data: bagCodeCounts, name: '鏂板琚嬬爜', type: 'bar' },
+      { barMaxWidth: 24, data: palletCounts, name: '鏂板鎵樼洏', type: 'bar' },
+      { data: inboundCounts, name: '鍏ュ簱琚嬫暟', smooth: true, type: 'line' },
+    ],
+    tooltip: { trigger: 'axis' },
+    xAxis: { axisTick: { show: false }, data: dates, type: 'category' },
+    yAxis: { minInterval: 1, type: 'value' },
+  };
+}
+
+/** 閫氱敤楗煎浘/鐜舰鍥鹃厤缃� */
+export function getPieChartOptions(
+  data: Array<{ name: string; value: number }>,
+): EChartsOption {
+  return {
+    legend: { bottom: 0, type: 'scroll' },
+    series: [
+      {
+        avoidLabelOverlap: true,
+        data,
+        emphasis: { label: { fontSize: 14, fontWeight: 'bold', show: true } },
+        itemStyle: { borderColor: '#fff', borderRadius: 6, borderWidth: 2 },
+        label: { formatter: '{b}\n{c}', show: true },
+        radius: ['40%', '70%'],
+        type: 'pie',
+      },
+    ],
+    tooltip: { formatter: '{b}: {c} ({d}%)', trigger: 'item' },
+  };
+}
+
+/** 閫氱敤鏌辩姸鍥鹃厤缃� */
+export function getBarChartOptions(
+  categories: string[],
+  values: number[],
+  name: string,
+  color = '#409EFF',
+): EChartsOption {
+  return {
+    grid: { bottom: 50, left: 60, right: 20, top: 30 },
+    series: [
+      {
+        barMaxWidth: 36,
+        data: values,
+        itemStyle: { borderRadius: 4, color },
+        name,
+        type: 'bar',
+      },
+    ],
+    tooltip: { axisPointer: { type: 'shadow' }, trigger: 'axis' },
+    xAxis: {
+      axisLabel: { interval: 0, rotate: categories.length > 6 ? 30 : 0 },
+      axisTick: { show: false },
+      data: categories,
+      type: 'category',
+    },
+    yAxis: { minInterval: 1, type: 'value' },
+  };
+}
+
+/** 閫氱敤鎶樼嚎鍥鹃厤缃� */
+export function getLineChartOptions(
+  dates: string[],
+  values: number[],
+  name: string,
+  color = '#36cfc9',
+): EChartsOption {
+  return {
+    grid: { bottom: 40, left: 60, right: 20, top: 30 },
+    series: [
+      { areaStyle: { opacity: 0.12 }, data: values, itemStyle: { color }, name, smooth: true, type: 'line' },
+    ],
+    tooltip: { trigger: 'axis' },
+    xAxis: { axisTick: { show: false }, data: dates, type: 'category' },
+    yAxis: { minInterval: 1, type: 'value' },
+  };
+}
+
+/** 璐ㄦ鎸囨爣鍧囧�� + 鍚堟牸鐜囧弻绯诲垪鏌辩姸鍥鹃厤缃� */
+export function getQualityIndicatorChartOptions(
+  labels: string[],
+  avgValues: number[],
+  passRates: number[],
+): EChartsOption {
+  return {
+    color: ['#409EFF', '#67c23a'],
+    grid: { bottom: 40, left: 60, right: 60, top: 30 },
+    legend: { bottom: 0, data: ['骞冲潎鍊�', '鍚堟牸鐜�(%)'] },
+    series: [
+      { barMaxWidth: 30, data: avgValues, name: '骞冲潎鍊�', type: 'bar' },
+      { barMaxWidth: 30, data: passRates, name: '鍚堟牸鐜�(%)', type: 'bar' },
+    ],
+    tooltip: { trigger: 'axis' },
+    xAxis: { axisTick: { show: false }, data: labels, type: 'category' },
+    yAxis: [{ minInterval: 1, type: 'value' }],
+  };
+}
diff --git a/src/views/management-cockpit/index.vue b/src/views/management-cockpit/index.vue
new file mode 100644
index 0000000..498c37f
--- /dev/null
+++ b/src/views/management-cockpit/index.vue
@@ -0,0 +1,207 @@
+<script lang="ts" setup>
+import type { ManagementCockpitApi } from '#/api/mes/management-cockpit';
+
+import { ref } from 'vue';
+
+import { Page } from '@vben/common-ui';
+import { IconifyIcon } from '@vben/icons';
+import { downloadFileFromBlobPart } from '@vben/utils';
+
+import { Button, message, Space } from 'ant-design-vue';
+// 鍙橀噺鍚� vPrint锛坴 寮�澶达級鍦� <script setup> 涓細琚� Vue 鑷姩娉ㄥ唽涓哄眬閮ㄦ寚浠� v-print
+// @ts-expect-error - vue3-print-nb 鏈彁渚涚被鍨嬪0鏄�
+import vPrint from 'vue3-print-nb';
+
+import {
+  exportManagementCockpitExcel,
+  getManagementCockpitSummary,
+} from '#/api/mes/management-cockpit';
+
+import KpiCards from './modules/kpi-cards.vue';
+import InventoryChart from './modules/inventory-chart.vue';
+import OutboundChart from './modules/outbound-chart.vue';
+import ProductionTrend from './modules/production-trend.vue';
+import QualityChart from './modules/quality-chart.vue';
+import TraceChart from './modules/trace-chart.vue';
+
+defineOptions({ name: 'ManagementCockpit' });
+
+// 鎵撳嵃閰嶇疆锛堟墦鍗版姤琛ㄥ鍣級
+const printObj = ref({
+  id: 'management-cockpit-report',
+  popTitle: '&nbsp;',
+  extraCss: '/print.css',
+  zIndex: 20_003,
+});
+
+const summary = ref<ManagementCockpitApi.Summary>({
+  batchTotal: 0,
+  batchDraft: 0,
+  batchProducing: 0,
+  batchFinished: 0,
+  batchCanceled: 0,
+  bagCodeTotal: 0,
+  bagCodeGenerated: 0,
+  bagCodePrinted: 0,
+  bagCodeCanceled: 0,
+  palletTotal: 0,
+  todayInboundBagCount: 0,
+  qualityTotal: 0,
+  qualityPending: 0,
+  qualityPass: 0,
+  qualityFail: 0,
+  qualityPassRate: 0,
+  stockTotal: 0,
+  stockTemporary: 0,
+  stockQualified: 0,
+  stockOutbound: 0,
+  lowStockCount: 0,
+  outboundOrderTotal: 0,
+  outboundQuantityTotal: 0,
+  todayOutboundQuantity: 0,
+  scanTotal: 0,
+  todayScanCount: 0,
+  scanSuccessCount: 0,
+  crossRegionWarningTotal: 0,
+  crossRegionPendingCount: 0,
+});
+
+// 閫氳繃閫掑 key 瑙﹀彂瀛愮粍浠堕噸鏂版寕杞藉疄鐜版暣浣撳埛鏂�
+const reloadKey = ref(0);
+
+const loading = ref(false);
+
+/** 鍔犺浇姹囨�荤粺璁� */
+async function loadSummary() {
+  try {
+    summary.value = await getManagementCockpitSummary();
+  } catch {
+    // 鎺ュ彛寮傚父鏃朵繚鐣欓粯璁ゅ�硷紝閬垮厤椤甸潰宕╂簝
+  }
+}
+
+/** 鍒锋柊鍏ㄩ儴鏁版嵁 */
+async function handleRefresh() {
+  loading.value = true;
+  try {
+    await Promise.all([loadSummary()]);
+    reloadKey.value += 1;
+    message.success('宸插埛鏂�');
+  } finally {
+    loading.value = false;
+  }
+}
+
+/** 瀵煎嚭 Excel 鎶ヨ〃锛堝悗绔敓鎴愬 sheet 宸ヤ綔绨匡級 */
+async function handleExportExcel() {
+  try {
+    const data = await exportManagementCockpitExcel();
+    downloadFileFromBlobPart({
+      fileName: `绠$悊椹鹃┒鑸辩粺璁℃姤琛�-${new Date().toLocaleDateString()}.xls`,
+      source: data,
+    });
+    message.success('Excel 鎶ヨ〃宸插鍑�');
+  } catch {
+    // 閿欒宸茬敱 requestClient 缁熶竴鎻愮ず
+  }
+}
+
+/** 瀵煎嚭 PDF 鎶ヨ〃锛堝墠绔� html2canvas + jspdf 鐢熸垚鍙笅杞芥枃浠讹級 */
+async function handleExportPdf() {
+  const target = document.getElementById('management-cockpit-report');
+  if (!target) {
+    message.warning('鎶ヨ〃鍖哄煙鏈氨缁�');
+    return;
+  }
+  const hide = message.loading('姝e湪鐢熸垚 PDF锛岃绋嶅�欌��', 0);
+  try {
+    // 鍔ㄦ�佸姞杞斤紝閬垮厤鎷栨參棣栧睆锛坔tml-to-image 閫氳繃 SVG foreignObject 璁╂祻瑙堝櫒鍘熺敓娓叉煋锛�
+    // 瑙勯伩 html2canvas 涓嶆敮鎸� oklch 绛夌幇浠� CSS 棰滆壊鍑芥暟鐨勯棶棰橈級
+    const [{ toPng }, { jsPDF }] = await Promise.all([
+      import('html-to-image'),
+      import('jspdf'),
+    ]);
+    const dataUrl = await toPng(target, {
+      backgroundColor: '#ffffff',
+      cacheBust: true,
+      pixelRatio: 2,
+    });
+    const pdf = new jsPDF({ compress: true, format: 'a4', orientation: 'p', unit: 'mm' });
+    const pageWidth = pdf.internal.pageSize.getWidth();
+    const pageHeight = pdf.internal.pageSize.getHeight();
+    const imgWidth = pageWidth;
+    const imgHeight = (target.offsetHeight * imgWidth) / target.offsetWidth;
+
+    let heightLeft = imgHeight;
+    let position = 0;
+    pdf.addImage(dataUrl, 'PNG', 0, position, imgWidth, imgHeight);
+    heightLeft -= pageHeight;
+    while (heightLeft > 0) {
+      position -= pageHeight;
+      pdf.addPage();
+      pdf.addImage(dataUrl, 'PNG', 0, position, imgWidth, imgHeight);
+      heightLeft -= pageHeight;
+    }
+    pdf.save(`绠$悊椹鹃┒鑸辩粺璁℃姤琛�-${new Date().toLocaleDateString()}.pdf`);
+    message.success('PDF 鎶ヨ〃宸插鍑�');
+  } catch (error) {
+    console.error('[management-cockpit] PDF 瀵煎嚭澶辫触锛�', error);
+    message.error(
+      `PDF 瀵煎嚭澶辫触锛�${error instanceof Error ? error.message : '鏈煡閿欒'}锛岃鎵撳紑娴忚鍣ㄦ帶鍒跺彴(F12)鏌ョ湅璇︽儏`,
+    );
+  } finally {
+    hide();
+  }
+}
+
+/** 鍔犺浇姹囨�荤粺璁� */
+async function loadInitial() {
+  await loadSummary();
+}
+loadInitial();
+</script>
+
+<template>
+  <Page>
+    <!-- 宸ュ叿鏍� -->
+    <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
+      <div class="flex items-center gap-2">
+        <IconifyIcon class="text-xl text-blue-500" icon="lucide:gauge" />
+        <span class="text-base font-semibold">绠$悊椹鹃┒鑸变笌缁熻鍒嗘瀽</span>
+      </div>
+      <Space>
+        <Button :loading="loading" @click="handleRefresh">
+          <template #icon><IconifyIcon icon="lucide:refresh-cw" /></template>
+          鍒锋柊
+        </Button>
+        <Button v-access:code="['mes:dashboard:export']" type="primary" @click="handleExportExcel">
+          <template #icon><IconifyIcon icon="lucide:file-spreadsheet" /></template>
+          瀵煎嚭 Excel
+        </Button>
+        <Button v-access:code="['mes:dashboard:export']" type="primary" ghost @click="handleExportPdf">
+          <template #icon><IconifyIcon icon="lucide:file-down" /></template>
+          瀵煎嚭 PDF
+        </Button>
+        <Button v-print="printObj">
+          <template #icon><IconifyIcon icon="lucide:printer" /></template>
+          鎵撳嵃
+        </Button>
+      </Space>
+    </div>
+
+    <!-- 鎶ヨ〃鍐呭锛圗xcel/PDF/鎵撳嵃 鍧囧熀浜庢鍖哄煙锛� -->
+    <div id="management-cockpit-report" class="rounded-lg bg-white p-4 dark:bg-gray-800">
+      <!-- 浜旂淮搴� KPI -->
+      <KpiCards :key="`kpi-${reloadKey}`" :summary="summary" class="mb-2" />
+
+      <!-- 鐢熶骇瓒嬪娍 -->
+      <ProductionTrend :key="`pt-${reloadKey}`" class="mb-4" />
+
+      <QualityChart :key="`qc-${reloadKey}`" class="mb-4" />
+
+      <InventoryChart :key="`iv-${reloadKey}`" class="mb-4" />
+      <OutboundChart :key="`ob-${reloadKey}`" class="mb-4" />
+      <TraceChart :key="`tr-${reloadKey}`" class="mb-4" />
+    </div>
+  </Page>
+</template>
diff --git a/src/views/management-cockpit/modules/inventory-chart.vue b/src/views/management-cockpit/modules/inventory-chart.vue
new file mode 100644
index 0000000..61647c2
--- /dev/null
+++ b/src/views/management-cockpit/modules/inventory-chart.vue
@@ -0,0 +1,82 @@
+<script lang="ts" setup>
+import type { EchartsUIType } from '@vben/plugins/echarts';
+
+import { onMounted, ref } from 'vue';
+
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { Card, Col, Row, Spin } from 'ant-design-vue';
+
+import { getManagementCockpitInventory } from '#/api/mes/management-cockpit';
+
+import { getBarChartOptions, getPieChartOptions } from '../chart-options';
+
+defineOptions({ name: 'ManagementCockpitInventoryChart' });
+
+const loading = ref(false);
+const warehouseRef = ref<EchartsUIType>();
+const itemRef = ref<EchartsUIType>();
+const stateRef = ref<EchartsUIType>();
+const { renderEcharts: renderWarehouse } = useEcharts(warehouseRef);
+const { renderEcharts: renderItem } = useEcharts(itemRef);
+const { renderEcharts: renderState } = useEcharts(stateRef);
+
+/** 鍔犺浇搴撳瓨缁熻骞舵覆鏌� */
+async function loadData() {
+  loading.value = true;
+  try {
+    const data = await getManagementCockpitInventory();
+    const warehouses = data.warehouseDistribution || [];
+    const items = data.itemDistribution || [];
+    const states = data.stateDistribution || [];
+    await renderWarehouse(
+      getBarChartOptions(
+        warehouses.map((w) => w.name),
+        warehouses.map((w) => w.count),
+        '搴撳瓨鏁伴噺',
+        '#409EFF',
+      ),
+    );
+    await renderItem(
+      getBarChartOptions(
+        items.map((i) => i.name),
+        items.map((i) => i.count),
+        '搴撳瓨鏁伴噺',
+        '#36cfc9',
+      ),
+    );
+    await renderState(
+      getPieChartOptions(
+        states.map((s) => ({ name: s.name, value: s.count })),
+      ),
+    );
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  loadData();
+});
+</script>
+
+<template>
+  <Card title="搴撳瓨缁村害缁熻" class="h-full">
+    <Spin :spinning="loading">
+      <Row :gutter="16">
+        <Col :lg="9" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">鎸変粨搴撳垎甯�</div>
+          <EchartsUI ref="warehouseRef" class="h-[280px] w-full" />
+        </Col>
+        <Col :lg="9" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">鎸夊搧绉嶅垎甯�</div>
+          <EchartsUI ref="itemRef" class="h-[280px] w-full" />
+        </Col>
+        <Col :lg="6" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">鎸夊簱瀛樼姸鎬�</div>
+          <EchartsUI ref="stateRef" class="h-[280px] w-full" />
+        </Col>
+      </Row>
+    </Spin>
+  </Card>
+</template>
diff --git a/src/views/management-cockpit/modules/kpi-cards.vue b/src/views/management-cockpit/modules/kpi-cards.vue
new file mode 100644
index 0000000..d0878c2
--- /dev/null
+++ b/src/views/management-cockpit/modules/kpi-cards.vue
@@ -0,0 +1,125 @@
+<script lang="ts" setup>
+import type { ManagementCockpitApi } from '#/api/mes/management-cockpit';
+
+import { CountTo } from '@vben/common-ui';
+import { IconifyIcon } from '@vben/icons';
+
+import { Card, Col, Row } from 'ant-design-vue';
+
+defineOptions({ name: 'ManagementCockpitKpiCards' });
+
+const props = defineProps<{
+  summary: ManagementCockpitApi.Summary;
+}>();
+
+/** 淇濈暀涓や綅灏忔暟 */
+function fmt(value?: number): number {
+  return Number((value ?? 0).toFixed(2));
+}
+
+/** 浜旂淮搴� KPI 鍗¢厤缃� */
+const cards = [
+  {
+    dimension: '鐢熶骇',
+    gradient: 'linear-gradient(135deg, #0ea5e9, #38bdf8)',
+    icon: 'lucide:factory',
+    mainLabel: '鐢熶骇鎵规',
+    mainValue: () => props.summary.batchTotal,
+    items: [
+      { label: '琚嬬爜', value: () => props.summary.bagCodeTotal },
+      { label: '鎵樼洏鐮�', value: () => props.summary.palletTotal },
+      { label: '浠婃棩鍏ュ簱', value: () => props.summary.todayInboundBagCount },
+    ],
+  },
+  {
+    dimension: '璐ㄦ',
+    gradient: 'linear-gradient(135deg, #67c23a, #95d475)',
+    icon: 'lucide:clipboard-check',
+    mainLabel: '鍚堟牸鐜�(%)',
+    mainValue: () => fmt(props.summary.qualityPassRate),
+    items: [
+      { label: '鍚堟牸', value: () => props.summary.qualityPass },
+      { label: '涓嶅悎鏍�', value: () => props.summary.qualityFail },
+      { label: '澶勭悊涓�', value: () => props.summary.qualityPending },
+    ],
+  },
+  {
+    dimension: '搴撳瓨',
+    gradient: 'linear-gradient(135deg, #f59e0b, #fbbf24)',
+    icon: 'lucide:warehouse',
+    mainLabel: '搴撳瓨鎬婚噺',
+    mainValue: () => fmt(props.summary.stockTotal),
+    items: [
+      { label: '鏆傚瓨搴�', value: () => fmt(props.summary.stockTemporary) },
+      { label: '鍚堟牸搴�', value: () => fmt(props.summary.stockQualified) },
+      { label: '浣庡簱瀛橀璀�', value: () => props.summary.lowStockCount },
+    ],
+  },
+  {
+    dimension: '鍑哄簱',
+    gradient: 'linear-gradient(135deg, #ec4899, #f472b6)',
+    icon: 'lucide:package-open',
+    mainLabel: '鍑哄簱鎬婚噺',
+    mainValue: () => fmt(props.summary.outboundQuantityTotal),
+    items: [
+      { label: '鍑哄簱鍗曟暟', value: () => props.summary.outboundOrderTotal },
+      { label: '浠婃棩鍑哄簱', value: () => fmt(props.summary.todayOutboundQuantity) },
+    ],
+  },
+  {
+    dimension: '杩芥函',
+    gradient: 'linear-gradient(135deg, #14b8a6, #2dd4bf)',
+    icon: 'lucide:scan-search',
+    mainLabel: '鎵爜鎬绘暟',
+    mainValue: () => props.summary.scanTotal,
+    items: [
+      { label: '浠婃棩鎵爜', value: () => props.summary.todayScanCount },
+      { label: '绐滆揣棰勮', value: () => props.summary.crossRegionWarningTotal },
+      { label: '寰呭鐞�', value: () => props.summary.crossRegionPendingCount },
+    ],
+  },
+];
+</script>
+
+<template>
+  <Row :gutter="16">
+    <Col
+      v-for="card in cards"
+      :key="card.dimension"
+      :lg="8"
+      :md="12"
+      :sm="12"
+      :xl="8"
+      :xs="24"
+      class="mb-4"
+    >
+      <Card class="kpi-card h-full">
+        <div class="flex items-start justify-between">
+          <div class="flex items-center gap-3">
+            <div
+              class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl text-white"
+              :style="{ background: card.gradient }"
+            >
+              <IconifyIcon class="size-6" :icon="card.icon" />
+            </div>
+            <div>
+              <div class="text-muted-foreground text-sm">{{ card.dimension }}</div>
+              <CountTo
+                class="text-xl font-bold leading-tight"
+                :end-val="card.mainValue()"
+                :duration="1200"
+              />
+              <div class="text-muted-foreground text-xs">{{ card.mainLabel }}</div>
+            </div>
+          </div>
+        </div>
+        <div class="mt-3 flex flex-wrap gap-x-4 gap-y-1 border-t border-dashed pt-3">
+          <span v-for="item in card.items" :key="item.label" class="text-xs text-gray-500">
+            {{ item.label }}锛�
+            <b class="text-sm text-gray-800 dark:text-gray-200">{{ item.value() }}</b>
+          </span>
+        </div>
+      </Card>
+    </Col>
+  </Row>
+</template>
diff --git a/src/views/management-cockpit/modules/outbound-chart.vue b/src/views/management-cockpit/modules/outbound-chart.vue
new file mode 100644
index 0000000..7be7864
--- /dev/null
+++ b/src/views/management-cockpit/modules/outbound-chart.vue
@@ -0,0 +1,77 @@
+<script lang="ts" setup>
+import type { EchartsUIType } from '@vben/plugins/echarts';
+
+import { onMounted, ref } from 'vue';
+
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { Card, Col, RadioButton, RadioGroup, Row, Spin } from 'ant-design-vue';
+
+import { getManagementCockpitOutbound } from '#/api/mes/management-cockpit';
+
+import { getLineChartOptions, getPieChartOptions } from '../chart-options';
+
+defineOptions({ name: 'ManagementCockpitOutboundChart' });
+
+const trendDays = ref(30);
+const loading = ref(false);
+const trendRef = ref<EchartsUIType>();
+const dealerRef = ref<EchartsUIType>();
+const { renderEcharts: renderTrend } = useEcharts(trendRef);
+const { renderEcharts: renderDealer } = useEcharts(dealerRef);
+
+/** 鍔犺浇鍑哄簱缁熻骞舵覆鏌� */
+async function loadData() {
+  loading.value = true;
+  try {
+    const data = await getManagementCockpitOutbound(trendDays.value);
+    const trend = data.trend || [];
+    const dealers = data.dealerDistribution || [];
+    await renderTrend(
+      getLineChartOptions(
+        trend.map((d) => d.date.slice(5)),
+        trend.map((d) => Number(d.count ?? 0)),
+        '鍑哄簱鏁伴噺',
+      ),
+    );
+    await renderDealer(
+      getPieChartOptions(dealers.map((d) => ({ name: d.name, value: d.count }))),
+    );
+  } finally {
+    loading.value = false;
+  }
+}
+
+/** 鍒囨崲澶╂暟鑼冨洿 */
+function handleDaysChange() {
+  loadData();
+}
+
+onMounted(() => {
+  loadData();
+});
+</script>
+
+<template>
+  <Card title="鍑哄簱缁村害缁熻" class="h-full">
+    <template #extra>
+      <RadioGroup v-model:value="trendDays" size="small" @change="handleDaysChange">
+        <RadioButton :value="7">杩� 7 澶�</RadioButton>
+        <RadioButton :value="30">杩� 30 澶�</RadioButton>
+        <RadioButton :value="90">杩� 90 澶�</RadioButton>
+      </RadioGroup>
+    </template>
+    <Spin :spinning="loading">
+      <Row :gutter="16">
+        <Col :lg="15" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">鍑哄簱瓒嬪娍</div>
+          <EchartsUI ref="trendRef" class="h-[280px] w-full" />
+        </Col>
+        <Col :lg="9" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">缁忛攢鍟嗗嚭搴撳崰姣旓紙TOP 20锛�</div>
+          <EchartsUI ref="dealerRef" class="h-[280px] w-full" />
+        </Col>
+      </Row>
+    </Spin>
+  </Card>
+</template>
diff --git a/src/views/management-cockpit/modules/production-trend.vue b/src/views/management-cockpit/modules/production-trend.vue
new file mode 100644
index 0000000..25fa5c8
--- /dev/null
+++ b/src/views/management-cockpit/modules/production-trend.vue
@@ -0,0 +1,56 @@
+<script lang="ts" setup>
+import type { EchartsUIType } from '@vben/plugins/echarts';
+
+import { onMounted, ref } from 'vue';
+
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { Card, RadioButton, RadioGroup } from 'ant-design-vue';
+
+import { getManagementCockpitProductionTrend } from '#/api/mes/management-cockpit';
+
+import { getProductionTrendChartOptions } from '../chart-options';
+
+defineOptions({ name: 'ManagementCockpitProductionTrend' });
+
+const trendDays = ref(30); // 褰撳墠澶╂暟鑼冨洿
+const chartRef = ref<EchartsUIType>();
+const { renderEcharts } = useEcharts(chartRef);
+
+/** 鍔犺浇鐢熶骇瓒嬪娍鏁版嵁骞舵覆鏌� */
+async function loadData() {
+  const data = await getManagementCockpitProductionTrend(trendDays.value);
+  const dates = data.map((d) => d.date.slice(5));
+  await renderEcharts(
+    getProductionTrendChartOptions(
+      dates,
+      data.map((d) => d.batchCount),
+      data.map((d) => d.bagCodeCount),
+      data.map((d) => d.palletCount),
+      data.map((d) => Number(d.inboundBagQuantity ?? 0)),
+    ),
+  );
+}
+
+/** 鍒囨崲澶╂暟鑼冨洿 */
+function handleDaysChange() {
+  loadData();
+}
+
+onMounted(() => {
+  loadData();
+});
+</script>
+
+<template>
+  <Card title="鐢熶骇瓒嬪娍锛堣繎 N 澶╋級" class="h-full">
+    <template #extra>
+      <RadioGroup v-model:value="trendDays" size="small" @change="handleDaysChange">
+        <RadioButton :value="7">杩� 7 澶�</RadioButton>
+        <RadioButton :value="30">杩� 30 澶�</RadioButton>
+        <RadioButton :value="90">杩� 90 澶�</RadioButton>
+      </RadioGroup>
+    </template>
+    <EchartsUI ref="chartRef" class="h-[320px] w-full" />
+  </Card>
+</template>
diff --git a/src/views/management-cockpit/modules/quality-chart.vue b/src/views/management-cockpit/modules/quality-chart.vue
new file mode 100644
index 0000000..38cc0e5
--- /dev/null
+++ b/src/views/management-cockpit/modules/quality-chart.vue
@@ -0,0 +1,67 @@
+<script lang="ts" setup>
+import type { EchartsUIType } from '@vben/plugins/echarts';
+
+import { onMounted, ref } from 'vue';
+
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { Card, Col, Row, Spin } from 'ant-design-vue';
+
+import { getManagementCockpitQuality } from '#/api/mes/management-cockpit';
+
+import { getPieChartOptions, getQualityIndicatorChartOptions } from '../chart-options';
+
+defineOptions({ name: 'ManagementCockpitQualityChart' });
+
+const loading = ref(false);
+const resultChartRef = ref<EchartsUIType>();
+const indicatorChartRef = ref<EchartsUIType>();
+const { renderEcharts: renderResult } = useEcharts(resultChartRef);
+const { renderEcharts: renderIndicator } = useEcharts(indicatorChartRef);
+
+/** 鍔犺浇璐ㄦ缁熻骞舵覆鏌� */
+async function loadData() {
+  loading.value = true;
+  try {
+    const data = await getManagementCockpitQuality();
+    // 1. 妫�楠岀粨鏋滃垎甯冪幆褰㈠浘
+    const pieData = (data.resultDistribution || []).map((item) => ({
+      name: item.name,
+      value: item.count,
+    }));
+    await renderResult(getPieChartOptions(pieData));
+    // 2. 鍥涢」鎸囨爣鍧囧�� + 鍚堟牸鐜囨煴鐘跺浘
+    const indicators = data.indicatorList || [];
+    await renderIndicator(
+      getQualityIndicatorChartOptions(
+        indicators.map((i) => i.indicatorName || i.indicatorCode),
+        indicators.map((i) => Number(i.averageValue ?? 0)),
+        indicators.map((i) => Number(i.passRate ?? 0)),
+      ),
+    );
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  loadData();
+});
+</script>
+
+<template>
+  <Card title="璐ㄦ缁村害缁熻" class="h-full">
+    <Spin :spinning="loading">
+      <Row :gutter="16">
+        <Col :lg="10" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">妫�楠岀粨鏋滃垎甯�</div>
+          <EchartsUI ref="resultChartRef" class="h-[280px] w-full" />
+        </Col>
+        <Col :lg="14" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">鍥涢」鎸囨爣鍧囧�间笌鍚堟牸鐜�</div>
+          <EchartsUI ref="indicatorChartRef" class="h-[280px] w-full" />
+        </Col>
+      </Row>
+    </Spin>
+  </Card>
+</template>
diff --git a/src/views/management-cockpit/modules/trace-chart.vue b/src/views/management-cockpit/modules/trace-chart.vue
new file mode 100644
index 0000000..3c06043
--- /dev/null
+++ b/src/views/management-cockpit/modules/trace-chart.vue
@@ -0,0 +1,125 @@
+<script lang="ts" setup>
+import type { EchartsUIType } from '@vben/plugins/echarts';
+
+import { onMounted, ref } from 'vue';
+
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { Card, Col, Row, Spin, Statistic } from 'ant-design-vue';
+
+import { getManagementCockpitTrace } from '#/api/mes/management-cockpit';
+
+import { getBarChartOptions, getLineChartOptions, getPieChartOptions } from '../chart-options';
+
+defineOptions({ name: 'ManagementCockpitTraceChart' });
+
+const loading = ref(false);
+const scanTrendRef = ref<EchartsUIType>();
+const provinceRef = ref<EchartsUIType>();
+const channelRef = ref<EchartsUIType>();
+const { renderEcharts: renderScanTrend } = useEcharts(scanTrendRef);
+const { renderEcharts: renderProvince } = useEcharts(provinceRef);
+const { renderEcharts: renderChannel } = useEcharts(channelRef);
+
+// 绐滆揣棰勮姹囨�伙紙鐢ㄤ簬灞曠ず鍗$墖锛�
+const crossRegion = ref({
+  total: 0,
+  pending: 0,
+  handled: 0,
+  falseAlarm: 0,
+  crossProvince: 0,
+  crossCity: 0,
+});
+
+/** 鍔犺浇杩芥函缁熻骞舵覆鏌� */
+async function loadData() {
+  loading.value = true;
+  try {
+    const data = await getManagementCockpitTrace(30);
+    const scanTrend = data.scanTrend || [];
+    const provinces = data.provinceDistribution || [];
+    const channels = data.channelDistribution || [];
+    crossRegion.value = data.crossRegionSummary || crossRegion.value;
+    await renderScanTrend(
+      getLineChartOptions(
+        scanTrend.map((d) => d.date.slice(5)),
+        scanTrend.map((d) => Number(d.count ?? 0)),
+        '鎵爜娆℃暟',
+        '#409EFF',
+      ),
+    );
+    await renderProvince(
+      getBarChartOptions(
+        provinces.map((p) => p.name),
+        provinces.map((p) => p.count),
+        '鎵爜娆℃暟',
+        '#67c23a',
+      ),
+    );
+    await renderChannel(
+      getPieChartOptions(channels.map((c) => ({ name: c.name, value: c.count }))),
+    );
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  loadData();
+});
+</script>
+
+<template>
+  <Card title="杩芥函缁村害缁熻" class="h-full">
+    <Spin :spinning="loading">
+      <!-- 绐滆揣棰勮姹囨�诲崱鐗� -->
+      <Row :gutter="16" class="mb-2">
+        <Col :lg="4" :md="8" :sm="12" :xs="24" class="mb-2">
+          <Card size="small">
+            <Statistic title="棰勮鎬绘暟" :value="crossRegion.total" />
+          </Card>
+        </Col>
+        <Col :lg="4" :md="8" :sm="12" :xs="24" class="mb-2">
+          <Card size="small">
+            <Statistic title="寰呭鐞�" :value="crossRegion.pending" :value-style="{ color: '#f5222d' }" />
+          </Card>
+        </Col>
+        <Col :lg="4" :md="8" :sm="12" :xs="24" class="mb-2">
+          <Card size="small">
+            <Statistic title="宸插鐞�" :value="crossRegion.handled" />
+          </Card>
+        </Col>
+        <Col :lg="4" :md="8" :sm="12" :xs="24" class="mb-2">
+          <Card size="small">
+            <Statistic title="璇姤" :value="crossRegion.falseAlarm" />
+          </Card>
+        </Col>
+        <Col :lg="4" :md="8" :sm="12" :xs="24" class="mb-2">
+          <Card size="small">
+            <Statistic title="璺ㄧ渷" :value="crossRegion.crossProvince" />
+          </Card>
+        </Col>
+        <Col :lg="4" :md="8" :sm="12" :xs="24" class="mb-2">
+          <Card size="small">
+            <Statistic title="璺ㄥ競" :value="crossRegion.crossCity" />
+          </Card>
+        </Col>
+      </Row>
+      <!-- 鎵爜瓒嬪娍 + 鐪佷唤鍒嗗竷 + 娓犻亾鍒嗗竷 -->
+      <Row :gutter="16">
+        <Col :lg="10" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">娑堣垂鑰呮壂鐮佽秼鍔�</div>
+          <EchartsUI ref="scanTrendRef" class="h-[280px] w-full" />
+        </Col>
+        <Col :lg="9" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">鎵爜鐪佷唤鍒嗗竷</div>
+          <EchartsUI ref="provinceRef" class="h-[280px] w-full" />
+        </Col>
+        <Col :lg="5" :md="24" :sm="24" :xs="24">
+          <div class="mb-1 text-sm text-gray-500">鎵爜娓犻亾鍒嗗竷</div>
+          <EchartsUI ref="channelRef" class="h-[280px] w-full" />
+        </Col>
+      </Row>
+    </Spin>
+  </Card>
+</template>

--
Gitblit v1.9.3