liu
5 小时以前 600fe725775e28af1f86f7f7f1e2521f494252b1
feat(mes): 新增管理驾驶舱页面

五维指标KPI与图表展示,支持Excel下载、PDF导出(html-to-image+jspdf)、打印存档。

Co-Authored-By: Claude <noreply@anthropic.com>
已添加9个文件
已修改2个文件
1509 ■■■■ 文件已修改
package.json 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pnpm-lock.yaml 497 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/management-cockpit/index.ts 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/chart-options.ts 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/index.vue 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/modules/inventory-chart.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/modules/kpi-cards.vue 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/modules/outbound-chart.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/modules/production-trend.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/modules/quality-chart.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/management-cockpit/modules/trace-chart.vue 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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:",
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:
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');
}
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' }],
  };
}
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(v å¼€å¤´ï¼‰åœ¨ <script setup> ä¸­ä¼šè¢« Vue è‡ªåŠ¨æ³¨å†Œä¸ºå±€éƒ¨æŒ‡ä»¤ v-print
// @ts-expect-error - vue3-print-nb æœªæä¾›ç±»åž‹å£°æ˜Ž
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('正在生成 PDF,请稍候…', 0);
  try {
    // åŠ¨æ€åŠ è½½ï¼Œé¿å…æ‹–æ…¢é¦–å±ï¼ˆhtml-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>
    <!-- æŠ¥è¡¨å†…容(Excel/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>
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>
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>
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>
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>
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>
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>