commit 37d15fdda9de8d04c843fe2dfd235cdde957a25e Author: YangQiang Date: Tue Apr 14 23:05:55 2026 +0800 chore: 添加项目配置文件和依赖 diff --git a/.codebuddy/integration/eop.json b/.codebuddy/integration/eop.json new file mode 100644 index 0000000..5576e49 --- /dev/null +++ b/.codebuddy/integration/eop.json @@ -0,0 +1,10 @@ +{ + "projectName": "aixuediebian-kanban", + "projectId": "pages-eip23arpzoar", + "deployUrl": "https://aixuediebian-kanban-3fzkam9s.edgeone.cool", + "previewUrl": "https://aixuediebian-kanban-3fzkam9s.edgeone.cool?eo_token=e87d722adbc6936c1429a1f77bba0d71&eo_time=1776139462", + "consoleUrl": "https://console.cloud.tencent.com/edgeone/pages/project/pages-eip23arpzoar/index", + "deploymentUrl": "https://console.cloud.tencent.com/edgeone/pages/project/pages-eip23arpzoar/deployment/gnpthgy3s1", + "deployId": "gnpthgy3s1", + "lastDeployTime": 1776139462 +} diff --git a/.codebuddy/plans/inline-color-threshold-control_e6827aeb.md b/.codebuddy/plans/inline-color-threshold-control_e6827aeb.md new file mode 100644 index 0000000..3ff66a2 --- /dev/null +++ b/.codebuddy/plans/inline-color-threshold-control_e6827aeb.md @@ -0,0 +1,100 @@ +--- +name: inline-color-threshold-control +overview: 在表格卡片头部添加内联数值调整控件,用于实时配置红色高亮阈值(默认10%),并将 getDiffClass/getNoParticipationDiffClass 中的硬编码 -10 替换为该响应式变量,同步更新图例文字。 +todos: + - id: add-color-threshold-var + content: 新增 colorThreshold 响应式变量、localStorage 加载逻辑与 watch 持久化 + status: completed + - id: update-diff-functions + content: 替换 getDiffClass 和 getNoParticipationDiffClass 中的硬编码 10 为 colorThreshold.value + status: completed + dependencies: + - add-color-threshold-var + - id: update-template-and-style + content: 在 card-header 图例旁添加内联 a-input-number 控件,动态化图例文字,更新提示条文字,添加控件样式 + status: completed + dependencies: + - add-color-threshold-var +--- + +## 用户需求 + +在作业打卡行为报告表格的 **card header 区域**,新增一个内联的数值调整控件,用于实时配置红色高亮的触发阈值。 + +## 产品概述 + +当前颜色判定逻辑中,红/橙/绿三色分界点硬编码为 `10`,无法动态调整。需将其改为响应式变量,并在表格头部提供直接可操作的输入控件,修改后立即生效,无需弹窗、无需刷新页面。 + +## 核心功能 + +- **内联阈值控件**:在 card header 的颜色图例旁,添加一个带标签的 `a-input-number` 数值输入框,默认值 `10`,范围 `0.1 ~ 50`,步长 `0.5`,单位 `%` +- **实时颜色更新**:输入框的值直接绑定到 `colorThreshold` 响应式变量,`getDiffClass` 和 `getNoParticipationDiffClass` 两个函数引用该变量,Vue 响应式系统自动触发表格重渲染 +- **动态图例**:颜色图例文字中的 `10%` 替换为 `{{ colorThreshold }}%`,随输入实时变化 +- **持久化**:通过 `localStorage` 保存用户设置的阈值,页面刷新后自动恢复 +- **提示文字更新**:更新 `.user-tip` 提示条内容,说明新控件的用途 + +## 技术栈 + +- Vue 3 Composition API(`ref`、`watch`) +- Ant Design Vue(`a-input-number`) +- localStorage 持久化 + +## 实现方案 + +### 核心思路 + +在 ` + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5b1f616 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1356 @@ +{ + "name": "aikue-report-prototype", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aikue-report-prototype", + "version": "1.0.0", + "dependencies": { + "ant-design-vue": "^4.2.6", + "echarts": "^6.0.0", + "file-saver": "^2.0.5", + "marked": "^17.0.5", + "sortablejs": "^1.15.7", + "tdesign-vue-next": "^1.18.6", + "tencentcloud-sdk-nodejs": "^4.1.208", + "vue": "^3.4.21", + "vue-router": "^4.6.4", + "vuedraggable": "^4.1.0", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "vite": "^5.2.8" + } + }, + "node_modules/@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "dependencies": { + "@ctrl/tinycolor": "^3.4.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==" + }, + "node_modules/@ant-design/icons-vue": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz", + "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1" + }, + "peerDependencies": { + "vue": ">=3.0.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@simonwep/pickr": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz", + "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==", + "dependencies": { + "core-js": "^3.15.1", + "nanopop": "^2.1.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/sortablejs": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.9.tgz", + "integrity": "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==" + }, + "node_modules/@types/tinycolor2": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz", + "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==" + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz", + "integrity": "sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ==", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/shared": "3.5.31", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz", + "integrity": "sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw==", + "dependencies": { + "@vue/compiler-core": "3.5.31", + "@vue/shared": "3.5.31" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz", + "integrity": "sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q==", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/compiler-core": "3.5.31", + "@vue/compiler-dom": "3.5.31", + "@vue/compiler-ssr": "3.5.31", + "@vue/shared": "3.5.31", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.8", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz", + "integrity": "sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog==", + "dependencies": { + "@vue/compiler-dom": "3.5.31", + "@vue/shared": "3.5.31" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.31.tgz", + "integrity": "sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g==", + "dependencies": { + "@vue/shared": "3.5.31" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.31.tgz", + "integrity": "sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q==", + "dependencies": { + "@vue/reactivity": "3.5.31", + "@vue/shared": "3.5.31" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz", + "integrity": "sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g==", + "dependencies": { + "@vue/reactivity": "3.5.31", + "@vue/runtime-core": "3.5.31", + "@vue/shared": "3.5.31", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.31.tgz", + "integrity": "sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA==", + "dependencies": { + "@vue/compiler-ssr": "3.5.31", + "@vue/shared": "3.5.31" + }, + "peerDependencies": { + "vue": "3.5.31" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.31.tgz", + "integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==" + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ant-design-vue": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-4.2.6.tgz", + "integrity": "sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-vue": "^7.0.0", + "@babel/runtime": "^7.10.5", + "@ctrl/tinycolor": "^3.5.0", + "@emotion/hash": "^0.9.0", + "@emotion/unitless": "^0.8.0", + "@simonwep/pickr": "~1.8.0", + "array-tree-filter": "^2.1.0", + "async-validator": "^4.0.0", + "csstype": "^3.1.1", + "dayjs": "^1.10.5", + "dom-align": "^1.12.1", + "dom-scroll-into-view": "^2.0.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.15", + "resize-observer-polyfill": "^1.5.1", + "scroll-into-view-if-needed": "^2.2.25", + "shallow-equal": "^1.0.0", + "stylis": "^4.1.3", + "throttle-debounce": "^5.0.0", + "vue-types": "^3.0.0", + "warning": "^4.0.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design-vue" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, + "node_modules/array-tree-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "engines": { + "node": "*" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" + }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-align": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", + "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==" + }, + "node_modules/dom-scroll-into-view": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz", + "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/echarts": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", + "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "6.0.0" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, + "node_modules/form-data": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.5.tgz", + "integrity": "sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanopop": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.4.2.tgz", + "integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + }, + "node_modules/sortablejs": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz", + "integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==" + }, + "node_modules/tdesign-icons-vue-next": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/tdesign-icons-vue-next/-/tdesign-icons-vue-next-0.4.2.tgz", + "integrity": "sha512-mTPk1ApcCA9oxDiSs9ttMdd09H8ICBooZIr2bwDEELnYr60sYSUbvWojQ2tp84MUAMuw21HgyVyGkT49db0GFg==", + "dependencies": { + "@babel/runtime": "^7.16.5" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/tdesign-vue-next": { + "version": "1.18.6", + "resolved": "https://registry.npmjs.org/tdesign-vue-next/-/tdesign-vue-next-1.18.6.tgz", + "integrity": "sha512-oc7wOE5awfWd0/mqCVOESv3rg1Nh6HJGr9vAvgjUkRTp6/KS9gexiKU2qR1hBKqh6cmBQkbFMWmX8MLnJJ6zLQ==", + "dependencies": { + "@babel/runtime": "^7.22.6", + "@popperjs/core": "^2.11.8", + "@types/lodash-es": "^4.17.12", + "@types/sortablejs": "^1.15.1", + "@types/tinycolor2": "^1.4.3", + "@types/validator": "^13.7.17", + "dayjs": "^1.11.10", + "lodash-es": "^4.17.21", + "mitt": "^3.0.1", + "sortablejs": "^1.15.0", + "tdesign-icons-vue-next": "~0.4.2", + "tinycolor2": "^1.6.0", + "validator": "^13.15.23" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "vue": ">=3.1.0" + } + }, + "node_modules/tencentcloud-sdk-nodejs": { + "version": "4.1.208", + "resolved": "https://registry.npmjs.org/tencentcloud-sdk-nodejs/-/tencentcloud-sdk-nodejs-4.1.208.tgz", + "integrity": "sha512-Av4UagKMrSiUUMtCqhVOCrJtd5YeKcB3ebc38a7MnqV6P/9+IIwmSvP68s6N9YtsKwI5bUeh1H/Gmb6ILNgnUg==", + "dependencies": { + "form-data": "^3.0.4", + "get-stream": "^6.0.0", + "https-proxy-agent": "^5.0.0", + "ini": "^5.0.0", + "is-stream": "^2.0.0", + "json-bigint": "^1.0.0", + "node-fetch": "^2.2.0", + "tslib": "1.13.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tencentcloud-sdk-nodejs/node_modules/tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", + "integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==", + "dependencies": { + "@vue/compiler-dom": "3.5.31", + "@vue/compiler-sfc": "3.5.31", + "@vue/runtime-dom": "3.5.31", + "@vue/server-renderer": "3.5.31", + "@vue/shared": "3.5.31" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz", + "integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==", + "dependencies": { + "is-plain-object": "3.0.1" + }, + "engines": { + "node": ">=10.15.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vuedraggable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", + "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==", + "dependencies": { + "sortablejs": "1.14.0" + }, + "peerDependencies": { + "vue": "^3.0.1" + } + }, + "node_modules/vuedraggable/node_modules/sortablejs": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", + "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==" + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/zrender": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", + "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ec80465 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "aikue-report-prototype", + "version": "1.0.0", + "description": "爱学蝶变报告系统原型设计", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "ant-design-vue": "^4.2.6", + "echarts": "^6.0.0", + "file-saver": "^2.0.5", + "marked": "^17.0.5", + "sortablejs": "^1.15.7", + "tdesign-vue-next": "^1.18.6", + "tencentcloud-sdk-nodejs": "^4.1.208", + "vue": "^3.4.21", + "vue-router": "^4.6.4", + "vuedraggable": "^4.1.0", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "vite": "^5.2.8" + } +} diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..95d7904 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/src/Assets/content-block-1-eZzov1mX.png b/src/Assets/content-block-1-eZzov1mX.png new file mode 100644 index 0000000..9891d2a Binary files /dev/null and b/src/Assets/content-block-1-eZzov1mX.png differ diff --git a/src/Assets/logo.png b/src/Assets/logo.png new file mode 100644 index 0000000..37ef110 Binary files /dev/null and b/src/Assets/logo.png differ diff --git a/src/Assets/工作台-爱学蝶变-学生管理.png b/src/Assets/工作台-爱学蝶变-学生管理.png new file mode 100644 index 0000000..f4b6fd7 Binary files /dev/null and b/src/Assets/工作台-爱学蝶变-学生管理.png differ diff --git a/src/Assets/工作台-爱学蝶变.png b/src/Assets/工作台-爱学蝶变.png new file mode 100644 index 0000000..4d3cfd4 Binary files /dev/null and b/src/Assets/工作台-爱学蝶变.png differ diff --git a/src/components/AdminDashboardPage.vue b/src/components/AdminDashboardPage.vue new file mode 100644 index 0000000..3ccda1b --- /dev/null +++ b/src/components/AdminDashboardPage.vue @@ -0,0 +1,389 @@ + + + + + diff --git a/src/components/ClassAllocationPage.vue b/src/components/ClassAllocationPage.vue new file mode 100644 index 0000000..4fe4cde --- /dev/null +++ b/src/components/ClassAllocationPage.vue @@ -0,0 +1,1624 @@ + + + + + diff --git a/src/components/CloudSchoolReport.vue b/src/components/CloudSchoolReport.vue new file mode 100644 index 0000000..61a640b --- /dev/null +++ b/src/components/CloudSchoolReport.vue @@ -0,0 +1,4135 @@ + + + + + diff --git a/src/components/DocIconButton.vue b/src/components/DocIconButton.vue new file mode 100644 index 0000000..7f1223f --- /dev/null +++ b/src/components/DocIconButton.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/src/components/DocModal.vue b/src/components/DocModal.vue new file mode 100644 index 0000000..6d538a1 --- /dev/null +++ b/src/components/DocModal.vue @@ -0,0 +1,450 @@ + + + + + diff --git a/src/components/EnglishWordReport.vue b/src/components/EnglishWordReport.vue new file mode 100644 index 0000000..f9c9604 --- /dev/null +++ b/src/components/EnglishWordReport.vue @@ -0,0 +1,2664 @@ + + + + + diff --git a/src/components/HomeNav.vue b/src/components/HomeNav.vue new file mode 100644 index 0000000..14eb769 --- /dev/null +++ b/src/components/HomeNav.vue @@ -0,0 +1,355 @@ + + + + + diff --git a/src/components/HomePage.vue b/src/components/HomePage.vue new file mode 100644 index 0000000..8a861a6 --- /dev/null +++ b/src/components/HomePage.vue @@ -0,0 +1,683 @@ + + + + + diff --git a/src/components/LeaderboardPage.vue b/src/components/LeaderboardPage.vue new file mode 100644 index 0000000..5f8c223 --- /dev/null +++ b/src/components/LeaderboardPage.vue @@ -0,0 +1,1685 @@ + + + + + diff --git a/src/components/LearningBehaviorReport.vue b/src/components/LearningBehaviorReport.vue new file mode 100644 index 0000000..21b8516 --- /dev/null +++ b/src/components/LearningBehaviorReport.vue @@ -0,0 +1,2292 @@ + + + + + diff --git a/src/components/LoginPage.vue b/src/components/LoginPage.vue new file mode 100644 index 0000000..d1087f1 --- /dev/null +++ b/src/components/LoginPage.vue @@ -0,0 +1,673 @@ + + + + + diff --git a/src/components/MobilePrototypePage.vue b/src/components/MobilePrototypePage.vue new file mode 100644 index 0000000..20da0ff --- /dev/null +++ b/src/components/MobilePrototypePage.vue @@ -0,0 +1,689 @@ + + + + + diff --git a/src/components/NoteEvaluationPage.vue b/src/components/NoteEvaluationPage.vue new file mode 100644 index 0000000..3216528 --- /dev/null +++ b/src/components/NoteEvaluationPage.vue @@ -0,0 +1,5557 @@ + + + + + diff --git a/src/components/PortalHomePage.vue b/src/components/PortalHomePage.vue new file mode 100644 index 0000000..fa03cfb --- /dev/null +++ b/src/components/PortalHomePage.vue @@ -0,0 +1,862 @@ + + + + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..8ce5d2c --- /dev/null +++ b/src/main.js @@ -0,0 +1,13 @@ +import { createApp } from 'vue' +import TDesign from 'tdesign-vue-next' +import 'tdesign-vue-next/es/style/index.css' +import Antd from 'ant-design-vue' +import 'ant-design-vue/dist/reset.css' +import App from './App.vue' +import router from './router' + +const app = createApp(App) +app.use(TDesign) +app.use(Antd) +app.use(router) +app.mount('#app') diff --git a/src/md/CheckinStatsReport.md b/src/md/CheckinStatsReport.md new file mode 100644 index 0000000..6694e28 --- /dev/null +++ b/src/md/CheckinStatsReport.md @@ -0,0 +1,148 @@ +# 打卡互动实时统计报告 + +## 页面概述 + +打卡互动实时统计报告页面展示学员打卡和互动的实时统计数据,包括已打卡/未打卡名单、总参与情况和互动情况。页面提供学校、年级、班级、日期范围等多维筛选条件,并以标签页形式切换不同数据视图。 + +## 页面结构 + +### 1. 页面标题区域 +- 返回按钮:点击触发 `go-back` 事件,返回报告中心 +- 页面标题:打卡互动实时统计报告 +- 导出报告按钮(`t-button`,primary outline) + +### 2. 筛选区域 + +| 筛选项 | 组件 | 占列数 | 选项 | +|--------|------|--------|------| +| 学校 | `t-select` | 3 | 全部学校、第一中学、第二中学 | +| 年级 | `t-select` | 3 | 全部年级、初一、初二、初三 | +| 班级 | `t-select` | 3 | 全部班级、1班、2班、3班 | +| 日期范围 | `t-date-range-picker` | 3 | 选择日期范围 | + +### 3. 核心指标概览 + +| 指标 | 示例值 | 趋势 | 颜色 | +|------|--------|------|------| +| 应打卡总人数 | 1,000 | +2.5% | `#333` | +| 实际打卡人数 | 850 | +5.2% | `#0052d9` | +| 总互动次数 | 3,240 | +12.4% | `#ed7b2f` | +| 平均互动/人 | 3.8 | -1.2% | `#2ba471` | + +> 趋势上升为红色(`#e34d59`),下降为绿色(`#2ba471`) + +### 4. 主标签页 + +使用 `t-tabs` 组件切换三个数据视图: + +#### 4.1 打卡情况 + +**工具栏**: +- 左侧:名单类型切换(`t-radio-group`,`default-filled` 变体) + - 已打卡名单(`checked`) + - 未打卡名单(`unchecked`) +- 右侧:选择日期(`t-date-picker`,默认今天) + +**已打卡名单表格列**: + +| 列Key | 列名 | 宽度 | 固定 | 特殊渲染 | +|-------|------|------|------|----------| +| `date` | 日期 | 120 | left | - | +| `studentName` | 学员名 | 100 | left | - | +| `cloudSchool` | 云校 | 120 | - | - | +| `school` | 学校 | 120 | - | - | +| `grade` | 年级 | 80 | - | - | +| `className` | 班级 | 100 | - | - | +| `subject` | 科目 | 80 | - | - | +| `level` | 分层 | 80 | - | - | +| `courseName` | 作业课程名称 | 200 | - | 超长省略 | +| `objTotal` | 客观题总题量 | 120 | - | - | +| `objCorrect` | 客观题正确数 | 120 | - | - | +| `objRate` | 客观题正确率 | 120 | - | - | +| `subjTotal` | 主观题总题量 | 120 | - | - | +| `subjCorrect` | 主观题正确数 | 120 | - | - | +| `subjRate` | 主观题正确率 | 120 | - | - | +| `totalQuestions` | 作业总题量 | 100 | - | - | +| `totalCorrect` | 作业正确数 | 100 | - | - | +| `totalRate` | 作业正确率 | 100 | - | - | +| `progressRate` | 作业进步率 | 100 | - | - | +| `answerDuration` | 答题时长 | 100 | - | - | +| `studyDuration` | 学习时长 | 100 | - | - | +| `isFeatured` | 是否精选 | 100 | - | 精选显示 `t-tag`(warning),否则显示 `-` | +| `firstCheckinTime` | 首次打卡时间 | 180 | - | - | +| `updateCheckinTime` | 打卡更新时间 | 180 | - | - | + +**状态列渲染**:`status` 列使用 `t-tag`(success,light 变体)显示"已打卡" + +**未打卡名单表格列**: + +| 列Key | 列名 | 宽度 | 固定 | 特殊渲染 | +|-------|------|------|------|----------| +| `date` | 日期 | 120 | left | - | +| `studentName` | 学员名 | 100 | left | - | +| `cloudSchool` | 云校 | 120 | - | - | +| `school` | 学校 | 120 | - | - | +| `grade` | 年级 | 80 | - | - | +| `className` | 班级 | 100 | - | - | +| `subject` | 科目 | 80 | - | - | +| `level` | 分层 | 80 | - | - | +| `courseName` | 作业课程名称 | 200 | - | 超长省略 | +| `lastCheckinTime` | 最后打卡时间 | 180 | - | - | +| `lastCheckinCourse` | 最后打卡课程 | 200 | - | 超长省略 | + +#### 4.2 总参与情况 + +**表格列**: + +| 列Key | 列名 | 宽度 | 固定 | +|-------|------|------|------| +| `studentName` | 学员名 | 100 | left | +| `cloudSchool` | 云校 | 120 | - | +| `school` | 学校 | 120 | - | +| `grade` | 年级 | 80 | - | +| `className` | 班级 | 100 | - | +| `subject` | 科目 | 80 | - | +| `level` | 分层 | 80 | - | +| `firstCheckinTime` | 首次打卡时间 | 180 | - | +| `lastCheckinTime` | 最后一次打卡时间 | 180 | - | +| `totalCheckins` | 累计打卡次数 | 120 | - | +| `totalStudyDuration` | 累计学习时长 | 120 | - | +| `totalAnswerDuration` | 累计答题时长 | 120 | - | +| `totalFeatured` | 累计精选数量 | 120 | - | + +#### 4.3 互动情况 + +**工具栏**: +- 右侧:选择日期范围(`t-date-range-picker`,默认近7天) +- 日期禁用逻辑:最大可选范围30天(选中起始日期后,结束日期限制在前后30天内) + +**表格列**: + +| 列Key | 列名 | 宽度 | 固定 | +|-------|------|------|------| +| `time` | 时间 | 180 | left | +| `studentName` | 学员 | 100 | left | +| `cloudSchool` | 云校 | 120 | - | +| `school` | 学校 | 120 | - | +| `grade` | 年级 | 80 | - | +| `className` | 班级 | 100 | - | +| `likeCount` | 点赞次数 | 100 | - | +| `commentCount` | 评论次数 | 100 | - | +| `receivedLikeCount` | 获赞次数 | 100 | - | +| `receivedCommentCount` | 获评论次数 | 100 | - | + +### 5. 分页配置 +- 默认当前页:1 +- 默认每页条数:10 +- 总条数:50 + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `go-back` | 点击返回按钮 | 无 | + +## 组件依赖 + +- TDesign Vue Next(`t-icon`、`t-button`、`t-select`、`t-card`、`t-row`、`t-col`、`t-date-range-picker`、`t-date-picker`、`t-tabs`、`t-tab-panel`、`t-radio-group`、`t-radio-button`、`t-table`、`t-tag`) +- dayjs(日期处理) diff --git a/src/md/CloudSchoolReport.md b/src/md/CloudSchoolReport.md new file mode 100644 index 0000000..e9a564a --- /dev/null +++ b/src/md/CloudSchoolReport.md @@ -0,0 +1,259 @@ +# 作业打卡行为报告 + +## 页面概述 + +作业打卡行为报告页面展示学员作业打卡相关的各项行为指标,包括互动率、打卡率、上线率、正确率等多维度统计。核心特色是每列均支持独立的阈值配置和颜色规则,通过差值列直观反映各指标与阈值的偏差情况。 + +## 页面结构 + +### 1. 页面标题区域 + +- 返回首页按钮:触发 `goBack` 事件 +- 页面标题:作业打卡行为报告 +- 副标题:作业打卡行为数据分析与趋势报告 + +### 2. 筛选控制区 + +#### 组织架构筛选 + +| 筛选项 | 组件 | 默认值 | 级联关系 | 选项 | +| --- | ---------- | ----------- | ---- | -------------------------------- | +| 云校 | `t-select` | `all`(所有云校) | 顶层 | 所有云校、重庆八中云校、河南云校、安徽云校 | +| 学校 | `t-select` | 空 | 依赖云校 | 重庆八中、河南郑州一中、安徽合肥一中、四川成都七中、湖北武汉二中 | +| 年级 | `t-select` | 空 | 依赖学校 | 高一~高三(未选学校时禁用,动态计算) | +| 班级 | `t-select` | 空 | 依赖年级 | X年级(1~6)班(未选年级时禁用,动态计算) | + +> 级联提示:未选学校时显示"请先选择学校以激活年级筛选",未选年级时显示"请选择年级以激活班级筛选" + +#### 范围筛选 + +| 筛选项 | 组件 | 默认值 | 选项 | +| --- | ---------------- | --------- | ----------------------------- | +| 科目 | `t-select` | `all`(全部) | 全部、语文、数学、英语、物理、化学、政治、生物、历史、地理 | +| 层次 | `t-select` | `all`(全部) | 全部、本科、特控、拔尖 | +| 时间 | `a-range-picker` | 最近30天 | 最多查询30天,禁止选择未来日期 | + +#### 级联选择逻辑 + +- 切换学校 → 清空年级、班级 +- 切换年级 → 清空班级 + +#### 日期范围验证 + +- 最大查询跨度:30天 +- 选中第一个日期后,自动禁用距离超过30天的日期 +- 禁止选择未来日期 +- 校验失败时显示红色错误提示,并清空日期选择 +- 提交查询时进行二次校验 + +### 3. 数据表格区域 + +#### 用户提示信息 + +- **阈值设置**:点击列标题旁的齿轮图标,可分别设置该列的计算阈值与红色阈值 +- **颜色规则**:每列独立配置红色阈值——差值低于该列红色阈值显示红色,差值在红色阈值~0之间显示橙色,差值大于0显示绿色 + +#### 操作按钮 + +- **导出数据**:打开导出确认模态框 +- **编辑列**:打开列编辑模态框 + +#### 表格列定义 + +**基础指标列(15列)**: + +| Key | 列名 | 宽度 | 对齐 | 公式/说明 | +| ------------------------ | -------- | --- | ------ | ------------------------------------ | +| `id` | 序号 | 60 | center | - | +| `date` | 日期 | 100 | center | - | +| `interactionRate` | 互动率 | 90 | right | 当天点赞+评论的人数/当天攻克一个知识点的人数(按人头去重) | +| `dailyCheckInRate` | 打卡率 | 120 | right | 存在任何一课程打卡的人数/学员人数 | +| `onlineRate` | 上线率 | 90 | right | 上线人数/学员人数(上线=当日任意课程学习+答题时长>30秒) | +| `qualityCheckInRate` | 打卡优质率 | 100 | right | 当日任一门课程笔记被评为"精选"的人数/当日知识点攻克人数(按人头去重) | +| `objectiveAccuracy` | 客观题正确率 | 110 | right | 当日客观题正确数/当日客观题总数量 | +| `subjectiveAccuracy` | 主观题正确率 | 110 | right | 当日主观题正确数/当日主观题总数量 | +| `homeworkAccuracy` | 作业正确率 | 100 | right | (客观题正确数+主观题正确数)/(客观题总数量+主观题总数量) | +| `homeworkExcellenceRate` | 作业优秀率 | 100 | right | 作业正确率>80%的人数/当天攻克一个知识点的人数 | +| `improvementRate` | 作业进步率 | 100 | right | (今日作业正确率-昨日作业正确率)/昨日作业正确率 | +| `courseCompletionRate` | 课程完成率 | 120 | right | 当日打卡的课程数/当日总课程数×学员人数 | +| `homeworkMasteryRate` | 作业攻克率 | 120 | right | 当日已攻克知识点数/当日推送总知识点数 | +| `noParticipationRate` | 未参与打卡率 | 150 | right | 当日未打卡人数/学员人数 | +| `dropoutRate` | 累计退学人数占比 | 130 | right | 退学人数/学员人数 | + +**差值列(13列)**: + +| Key | 列名 | 宽度 | 差值计算公式 | 默认阈值 | 默认红色阈值 | +| ---------------------------- | ---------- | --- | ------------------- | ---- | ------ | +| `interactionRateDiff` | 互动率与阈值差 | 160 | 互动率 - 阈值 | 40% | -10% | +| `dailyCheckInRateDiff` | 打卡率与阈值差 | 110 | 打卡率 - 阈值 | 80% | -10% | +| `onlineRateDiff` | 上线率与阈值差 | 110 | 上线率 - 阈值 | 80% | -10% | +| `qualityCheckInRateDiff` | 打卡优质率与阈值差 | 110 | 打卡优质率 - 阈值 | 40% | -10% | +| `objectiveAccuracyDiff` | 客观题正确率与阈值差 | 110 | 客观题正确率 - 阈值 | 30% | -10% | +| `subjectiveAccuracyDiff` | 主观题正确率与阈值差 | 110 | 主观题正确率 - 阈值 | 30% | -10% | +| `homeworkAccuracyDiff` | 作业正确率与阈值差 | 130 | 作业正确率 - 阈值 | 30% | -10% | +| `homeworkExcellenceRateDiff` | 作业优秀率与阈值差 | 130 | 作业优秀率 - 阈值 | 20% | -10% | +| `improvementRateDiff` | 作业进步率与阈值差 | 110 | 作业进步率 - 阈值 | 15% | -10% | +| `courseCompletionRateDiff` | 课程完成率与阈值差 | 130 | 课程完成率 - 阈值 | 70% | -10% | +| `homeworkMasteryRateDiff` | 作业攻克率与阈值差 | 130 | 作业攻克率 - 阈值 | 30% | -10% | +| `noParticipationRateDiff` | 未参与打卡率与阈值差 | 130 | **阈值 - 未参与打卡率**(反向) | 30% | -10% | +| `dropoutRateDiff` | 退学率与阈值之差 | 130 | **阈值 - 退学率**(反向) | 5% | -10% | + +> 注:未参与打卡率和退学率的差值计算为反向(阈值 - 实际值),因为这两项指标越低越好 +> +> 判断课程是否打卡规则:课程下有任一知识点的学习时长+答题时长>灵活设置的时长 +> +> 判断上线规则:当日总学习时长+总答题时长>30秒 + +#### 颜色规则 + +**正向指标(互动率、打卡率等)**: + +- 差值 < 红色阈值 → 红色(`cell-bg-red`) +- 红色阈值 ≤ 差值 ≤ 0 → 橙色(`cell-bg-orange`) +- 差值 > 0 → 绿色(`cell-bg-green`) + +**反向指标(未参与打卡率、退学率)**: + +- 差值 < 红色阈值 → 红色(严重问题) +- 红色阈值 ≤ 差值 ≤ 0 → 橙色(警示) +- 差值 > 0 → 绿色(优秀) + +#### 差值格式化 + +- 正值前缀 `+`,负值前缀 `-` +- 保留1位小数,后缀 `%` + +#### 分页配置 + +- 默认每页:10条 +- 可选每页条数:10、20、50、100 +- 显示总条数:`共 X 条记录` +- 支持快速跳页 + +### 4. 阈值配置系统 + +#### 配置方式 + +- 每个差值列的列标题旁有齿轮图标(`SettingOutlined`) +- 点击齿轮图标弹出 `a-popover` 气泡卡片 +- 同一时间只允许打开一个气泡卡片 + +#### 气泡卡片内容 + +- 公式说明(如:互动率与阈值差 = 互动率 - 阈值) +- **阈值输入**:`a-input-number`,范围 0~100,步长1,带 `%` 格式化 +- **红色阈值输入**:`a-input-number`,最大值 -0.1,步长0.5,精度1位,带 `%` 格式化 +- 提示文字:差值低于红色阈值时显示红色 +- 操作按钮:取消、确定 + +#### 快照与取消机制 + +- 打开气泡时保存所有阈值的快照 +- 点击取消:从快照恢复该列的两个阈值 +- 点击确定:值已实时写入 ref,直接关闭 +- 打开新气泡时自动关闭其他气泡 + +### 5. 列编辑器 + +#### 功能说明 + +- 支持拖拽排序列顺序(使用 `vuedraggable` 组件) +- 支持勾选控制列的显示/隐藏 +- 序号列和日期列固定在最前面,不可编辑 +- 至少需要选择一列才能保存 + +#### 持久化 + +- 存储键名:`homework_report_column_order` +- 存储位置:`localStorage` +- 加载时验证配置有效性 +- 序号和日期列始终固定在最前面且可见 + +### 6. 统计图表区域 + +#### 图表控制 + +**展示指标选择**: + +- 多选下拉框,最多可选10个指标 +- 动态从表格列配置中过滤(排除序号、日期、差值列) +- 显示已选/总数计数 +- 快捷操作:全选、清空 +- 默认选中所有13个基础指标 + +**可选指标及颜色映射**: + +| 指标 | Key | 颜色 | +| -------- | ------------------------ | --------- | +| 互动率 | `interactionRate` | `#5470c6` | +| 打卡率 | `dailyCheckInRate` | `#91cc75` | +| 上线率 | `onlineRate` | `#fac858` | +| 打卡优质率 | `qualityCheckInRate` | `#73c0de` | +| 客观题正确率 | `objectiveAccuracy` | `#3ba272` | +| 主观题正确率 | `subjectiveAccuracy` | `#fc8452` | +| 作业正确率 | `homeworkAccuracy` | `#9a60b4` | +| 作业优秀率 | `homeworkExcellenceRate` | `#acbf60` | +| 作业进步率 | `improvementRate` | `#ea7ccc` | +| 课程完成率 | `courseCompletionRate` | `#8d5e40` | +| 作业攻克率 | `homeworkMasteryRate` | `#6b9e7e` | +| 未参与打卡率 | `noParticipationRate` | `#48b8d0` | +| 累计退学人数占比 | `dropoutRate` | `#dd6b66` | + +**图表类型切换**: + +- 折线图(默认):带平滑曲线和渐变面积 +- 柱状图 + +#### 图表特性 + +- ECharts 渲染,支持窗口自适应 +- Tooltip:十字准线指示器 +- 图例:可滚动 +- 工具栏:保存图片、数据视图、图表类型切换、还原 +- 折线图面积渐变:从指标颜色40%透明度渐变到5%透明度 +- 动画:800ms,cubicOut 缓动 + +### 7. 导出确认模态框 + +#### 功能说明 + +- 展示当前筛选条件,允许在导出前调整 +- 导出模态框中的时间范围无30天限制(仅禁用未来日期) +- 解决模态框中日期选择器层级遮挡问题(`getPopupContainer` 设为 `document.body`) + +#### 导出逻辑 + +``` +handleExportData(): + 1. 检查是否有可导出数据 + 2. 根据当前可见列配置生成数据行 + 3. 百分比列格式化为 "X.X%" + 4. 差值列格式化为 "+X.X%" 或 "-X.X%" + 5. 使用 XLSX 库创建工作簿 + 6. 导出文件名:云校作业打卡行为报告_YYYYMMDDHHmmss.xlsx + 7. 导出成功/失败提示 +``` + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +| -------- | -------- | --- | +| `goBack` | 点击返回首页按钮 | 无 | + +## 数据联动 + +- 筛选条件变化(云校、科目、层次、时间范围)→ 自动更新表格和图表 +- 图表类型切换 → 自动更新图表 +- 指标选择变化 → 自动更新图表 +- 阈值变化 → 差值列重新计算并更新颜色 +- 窗口大小变化 → 图表自适应缩放 + +## 组件依赖 + +- TDesign Vue Next(`t-button`、`t-icon`、`t-select`、`t-card`、`t-dialog`、`t-checkbox`、`t-radio-group`、`t-radio-button`) +- Ant Design Vue(`a-table`、`a-range-picker`、`a-tooltip`、`a-popover`、`a-input-number`、`a-button`) +- ECharts(图表渲染) +- dayjs(日期处理) +- xlsx(Excel 导出) +- vuedraggable(列拖拽排序) +- @ant-design/icons-vue(`SettingOutlined`、`InfoCircleOutlined`、`ReloadOutlined`、`ExclamationCircleOutlined`) diff --git a/src/md/EnglishWordReport.md b/src/md/EnglishWordReport.md new file mode 100644 index 0000000..2335eb1 --- /dev/null +++ b/src/md/EnglishWordReport.md @@ -0,0 +1,304 @@ +# 英语单词报告 + +## 页面概述 + +英语单词报告页面全面展示英语单词学习数据与学情分析,包含总体学情统计、答题正确率排名、答题进度分布、正确率分布、学生在线统计五个维度的数据分析。支持云校/学校/年级/班级的级联筛选,各标签页均支持独立导出功能。 + +## 页面结构 + +### 1. 页面标题区域 +- 返回按钮:点击触发 `go-back` 事件 +- 标题图标 + 标题"英语单词报告" +- 副标题:全面展示英语单词学习数据与学情分析 +- 右侧筛选器(`size="large"`): + +| 筛选项 | 组件 | 宽度 | 级联关系 | 禁用条件 | +|--------|------|------|----------|----------| +| 云校 | `t-select` | 140px | 顶层 | - | +| 学校 | `t-select` | 140px | 依赖云校 | - | +| 年级 | `t-select` | 120px | 依赖学校 | 未选学校时禁用 | +| 班级 | `t-select` | 120px | 依赖年级 | 未选年级时禁用 | + +#### 级联选择逻辑 +- 切换学校 → 清空年级、班级 +- 切换年级 → 清空班级 + +#### 筛选选项 + +**云校列表**:全部云校、爱学云校A、爱学云校B + +**学校列表**:全部学校、第一中学、第二中学、实验中学 + +**年级列表**:全部年级、2027届、2028届、2029届 + +**班级列表**:全部班级、高一1班、高一2班、高二1班、高二2班 + +### 2. 标签页 + +使用 `t-tabs` 组件切换五个数据视图: + +#### 2.1 总体学情统计(`overview`) + +**标题**:总体学情统计分析表 +**描述**:统计各年级英语单词学习整体情况,包含在线率、完成率、正确率等核心指标 + +**日期范围选择**: +- `t-date-range-picker`,默认最近30天 +- 禁止选择未来日期 +- 跨度超过30天时自动截断并显示警告提示(5秒后消失) + +**表格特性**: +- 使用 `t-enhanced-table`,支持树形展开 +- 树形结构:云校 → 学校 → 年级 → 班级(`defaultExpandAll: true`) +- 虚拟滚动(`scroll: { type: 'virtual' }`) +- 班级行可点击,点击后打开班级详情模态框 + +**表格列**: + +| 列Key | 列名 | 宽度 | 对齐 | 固定 | 特殊渲染 | +|-------|------|------|------|------|----------| +| `subject` | 组织架构 | 300 | left | left | 树形节点 | +| `totalStudents` | 总人数 | 90 | center | - | - | +| `onlineCount` | 在线人数 | 100 | center | - | - | +| `onlineRate` | 在线率 | 100 | center | - | 蓝色标签样式(`#0052d9`,浅蓝背景) | +| `totalAnswerRate` | 总答题率 | 110 | center | - | - | +| `answerProgress` | 答题进度 | 100 | center | - | - | +| `answerUserCount` | 答题用户数 | 110 | center | - | - | +| `avgAnswerCount` | 人均答题数 | 110 | center | - | - | +| `correctAnswerCount` | 正确答题数 | 110 | center | - | - | +| `accuracy` | 正确率 | 100 | center | - | 绿色标签样式(`#00a870`,浅绿背景) | + +**数据层级示例**: +``` +八中云校(476人) + ├── 第一中学(156人) + │ ├── 2027届(102人) + │ │ ├── 高一1班(50人) + │ │ └── 高一2班(52人) + │ └── 2026届(54人) + │ └── 高二1班(54人) + ├── 第二中学(148人) + └── 实验中学(172人) +河南云校(332人) +湖北云校(175人) +``` + +**导出按钮**:打开导出确认对话框 + +#### 2.2 答题正确率排名(`accuracy_rank`) + +**标题**:学生答题正确率排名表 +**描述**:按答题正确率从高到低排列,展示学生学习成效排名 + +**表格列**: + +| 列Key | 列名 | 宽度 | 对齐 | 特殊渲染 | +|-------|------|------|------|----------| +| `rank` | 排名 | 70 | center | 前三名:渐变圆形奖牌(金 `#f5a623`、银 `#94a3b8`、铜 `#cd7f32`),其余:灰色数字 | +| `name` | 学生姓名 | 110 | - | - | +| `cloud` | 云校 | 130 | - | - | +| `school` | 学校 | 130 | - | - | +| `grade` | 年级 | 90 | - | - | +| `class` | 班级 | 110 | - | - | +| `totalCount` | 总答题数 | 100 | center | - | +| `correctCount` | 正确答题数 | 100 | center | - | +| `accuracy` | 正确率 | 110 | center | 蓝色标签样式 | + +**分页**:每页10条,支持跳页 + +#### 2.3 答题进度分布(`progress_dist`) + +**标题**:学生答题进度分布表 +**描述**:按答题完成进度区间统计学生分布情况,了解整体学习推进状态 + +**图表**: +- ECharts 柱状图,高度 400px +- X轴:进度区间(0%~9% ~ 90%~100%),标签旋转45度 +- Y轴:学生人数 +- 柱体颜色按进度区间分级: + - ≥90%:绿色 `#00a870` + - ≥80%:蓝色 `#0052d9` + - ≥60%:橙色 `#ed7b2f` + - ≥40%:黄色 `#f5a623` + - <40%:红色 `#e34d59` +- 柱顶显示数值(0不显示) + +**表格列**: + +| 列Key | 列名 | 宽度 | 特殊渲染 | +|-------|------|------|----------| +| `progressRange` | 进度区间 | 160 | 文字颜色按区间分级 | +| `studentCount` | 学生人数 | 100 | 居中 | +| `percentage` | 占比 | 160 | 进度条 + 百分比文字 | + +**进度区间数据**: + +| 进度区间 | 学生人数 | 占比 | +|----------|----------|------| +| 90%~100% | 270 | 45.7% | +| 80%~89% | 145 | 24.6% | +| 70%~79% | 80 | 13.5% | +| 60%~69% | 45 | 7.6% | +| 50%~59% | 25 | 4.2% | +| 40%~49% | 13 | 2.2% | +| 30%~39% | 7 | 1.2% | +| 20%~29% | 3 | 0.5% | +| 10%~19% | 2 | 0.3% | +| 0%~9% | 0 | 0.0% | + +#### 2.4 正确率分布(`accuracy_dist`) + +**标题**:学生答题正确率分布表 +**描述**:按正确率区间统计学生分布,直观呈现学生掌握程度层次 + +**图表**: +- ECharts 柱状图,高度 400px +- X轴:正确率区间(0%~9% ~ 90%~100%),标签旋转45度 +- Y轴:学生人数 +- 柱体颜色按正确率区间分级(同进度分布颜色规则) +- 柱顶显示数值(0不显示) + +**表格列**: + +| 列Key | 列名 | 宽度 | 特殊渲染 | +|-------|------|------|----------| +| `accuracyRange` | 正确率区间 | 160 | 文字颜色按区间分级 | +| `studentCount` | 学生人数 | 100 | 居中 | +| `percentage` | 占比 | 160 | 进度条 + 百分比文字 | + +**正确率区间数据**: + +| 正确率区间 | 学生人数 | 占比 | +|------------|----------|------| +| 90%~100% | 198 | 33.6% | +| 80%~89% | 215 | 36.4% | +| 70%~79% | 112 | 19.0% | +| 60%~69% | 45 | 7.6% | +| 50%~59% | 12 | 2.0% | +| 40%~49% | 5 | 0.8% | +| 30%~39% | 2 | 0.3% | +| 20%~29% | 1 | 0.2% | +| 10%~19% | 0 | 0.0% | +| 0%~9% | 0 | 0.0% | + +#### 2.5 学生在线统计(`online_stats`) + +**标题**:学生在线与答题趋势 +**描述**:展示不同时间维度下的在线用户数量与答题数量变化趋势 + +**时间维度切换**(`t-radio-group`,`default-filled` 变体): + +| 维度 | Key | 日期选择器 | 说明 | +|------|-----|-----------|------| +| 分钟 | `minute` | `t-date-picker`(单日选择) | 每10分钟一个数据点 | +| 小时 | `hour` | `t-date-picker`(单日选择) | 每小时一个数据点 | +| 天 | `day` | `t-date-range-picker`(范围选择) | 每天一个数据点,最大30天 | + +**日期验证**: +- 禁止选择未来日期 +- 天维度日期范围超过30天时自动截断并显示警告 + +**趋势数据生成逻辑**: +- 分钟维度:从当天0:00开始,每10分钟一个点,截止至当前时间前1小时(向下取整到10分钟) +- 小时维度:从当天0:00开始,每小时一个点,截止至当前时间前1小时 +- 天维度:根据日期范围生成,每天一个点 +- 处理跨天边界:当前时间不足1小时时截断在当天00:00 + +**图表**: +- ECharts 双Y轴折线图,高度 400px +- 左Y轴:在线用户数(蓝色 `#0052d9`,带面积渐变) +- 右Y轴:答题数量(绿色 `#00a870`,带面积渐变) +- 平滑曲线 +- Tooltip:十字准线指示器 +- X轴标签:分钟维度每隔5个点显示(即1小时间隔),天维度旋转45度 + +### 3. 导出确认对话框 + +#### 功能说明 +- `t-dialog` 弹窗,标题"确认导出" +- 展示当前筛选条件,允许在导出前调整 +- 级联重置:切换学校清空年级和班级,切换年级清空班级 + +#### 导出筛选条件 + +| 筛选项 | 组件 | 显示条件 | +|--------|------|----------| +| 云校 | `t-select` | 始终显示 | +| 学校 | `t-select` | 始终显示 | +| 年级 | `t-select` | 始终显示(未选学校时禁用) | +| 班级 | `t-select` | 始终显示(未选年级时禁用) | +| 日期范围 | `t-date-range-picker` | 总体学情 或 在线统计(天维度) | +| 选择日期 | `t-date-picker` | 在线统计(分钟/小时维度) | +| 时间维度 | 文字显示 | 在线统计时显示 | + +#### 导出逻辑 +``` +confirmExport(): + 1. 根据 exportType 确定数据源和列配置 + 2. 提取表头和数据行 + 3. 使用 XLSX 库创建工作簿 + 4. 文件名格式:{报告名}_{云校}_{学校}_{年级}_{班级}.xlsx + 5. 导出成功提示 +``` + +**各标签页导出文件名**: +- 总体学情:`总体学情统计分析表` +- 正确率排名:`学生答题正确率排名表` +- 进度分布:`学生答题进度分布表` +- 正确率分布:`学生答题正确率分布表` +- 在线统计:`学生在线与答题趋势` + +### 4. 班级详情模态框 + +#### 功能说明 +- `t-dialog` 弹窗,宽度 900px,无底部按钮 +- 标题:`{科目} - 学生详情` +- 触发方式:点击总体学情表格中的班级行 + +#### 班级概览信息 + +| 信息项 | 说明 | +|--------|------| +| 班级名称 | 科目名称 | +| 总人数 | 高亮显示 | +| 平均在线率 | 蓝色样式,计算公式:在线人数/总人数×100% | +| 平均完成率 | 普通样式 | +| 平均正确率 | 绿色样式 | + +#### 学生数据表格 + +| 列Key | 列名 | 宽度 | 对齐 | 特殊渲染 | +|-------|------|------|------|----------| +| `name` | 姓名 | 100 | center | 固定左侧 | +| `onlineDays` | 在线天数 | 90 | center | - | +| `onlineRate` | 在线率 | 90 | center | 蓝色标签样式 | +| `totalAnswerCount` | 总答题数 | 95 | center | - | +| `completionRate` | 完成率 | 90 | center | ≥80%绿色,≥60%蓝色,<60%橙色 | +| `answerProgress` | 答题进度 | 90 | center | - | +| `correctAnswerCount` | 正确答题数 | 100 | center | - | +| `accuracy` | 正确率 | 90 | center | ≥85%绿色背景,≥60%蓝色背景,<60%红色背景 | + +**学生数据导出**: +- 导出格式:CSV(UTF-8 BOM) +- 文件名:`{班级名}_学生数据_{YYYY-MM-DD_HH-mm-ss}.csv` +- 导出过程有 loading 状态 + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `go-back` | 点击返回按钮 | 无 | + +## 数据联动 + +- 标签页切换 → 重置页码到第1页,对应标签页图表重新渲染 +- 时间维度切换 → 重新生成趋势数据并渲染图表 +- 日期选择变化 → 重新生成趋势数据并渲染图表 +- 窗口大小变化 → 所有图表自适应缩放 + +## 组件依赖 + +- TDesign Vue Next(`t-button`、`t-icon`、`t-select`、`t-option`、`t-tabs`、`t-tab-panel`、`t-table`、`t-enhanced-table`、`t-date-range-picker`、`t-date-picker`、`t-radio-group`、`t-radio-button`、`t-dialog`、`MessagePlugin`) +- ECharts(图表渲染) +- dayjs(日期处理) +- xlsx(Excel 导出) diff --git a/src/md/HomePage.md b/src/md/HomePage.md new file mode 100644 index 0000000..af1e543 --- /dev/null +++ b/src/md/HomePage.md @@ -0,0 +1,44 @@ +# 首页(报告中心) + +## 页面概述 + +首页是报告中心的入口页面,以卡片网格形式展示各类数据报告的入口。用户可通过点击卡片跳转到对应的报告详情页,也可通过返回按钮回到门户首页。 + +## 页面结构 + +### 1. 返回按钮区域 +- 位于页面顶部 +- 点击后触发 `go-back` 事件,返回门户首页 +- 悬停时边框变为主题蓝色,背景变为浅蓝 + +### 2. 报告入口网格 +采用 `t-row` / `t-col` 栅格布局,每行4列(`span=6`),展示以下5个报告入口卡片: + +| 序号 | 报告ID | 报告名称 | 图标 | 图标背景色 | 描述 | +|------|--------|----------|------|------------|------| +| 1 | `cloud_school` | 作业打卡行为报告 | cloud | 蓝色渐变 `#4A90E2 → #357ABD` | 查看作业打卡行为数据分析与趋势报告 | +| 2 | `school` | 学习行为报告 | home | 橙色渐变 `#F5A623 → #E59411` | 查看各校学生行为详细报告与对比分析 | +| 3 | `leaderboard` | 排行榜 | chart-bar | 绿色渐变 `#7ED321 → #68B019` | 查看积分、知识点、互动、笔记精选等多维度排行榜 | +| 4 | `english_word` | 英语单词报告 | layers | 紫色渐变 `#667eea → #764ba2` | 查看英语单词学情统计、正确率排名、进度分布等报告 | +| 5 | `checkin_stats` | 打卡互动实时统计报告 | chart-pie | 蓝色渐变 `#00C6FF → #0072FF` | 查看已打卡名单、未打卡名单、总参与概况、互动统计等实时数据 | + +## 交互行为 + +- **点击卡片**:触发 `navigate` 事件,传递对应报告的 `id`,由父组件路由到对应报告页面 +- **卡片悬停**:卡片上浮4px,阴影加深,过渡动画使用 `cubic-bezier(0.4, 0, 0.2, 1)` + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `navigate` | 点击报告卡片 | `report.id`(如 `'cloud_school'`、`'leaderboard'` 等) | +| `go-back` | 点击返回按钮 | 无 | + +## 响应式布局 + +- `max-width: 1200px`:卡片下边距缩小为16px +- `max-width: 768px`:卡片内边距缩小,卡片下边距缩小为16px + +## 组件依赖 + +- TDesign Vue Next(`t-row`、`t-col`、`t-card`、`t-icon`) diff --git a/src/md/LeaderboardPage.md b/src/md/LeaderboardPage.md new file mode 100644 index 0000000..e7b5ea2 --- /dev/null +++ b/src/md/LeaderboardPage.md @@ -0,0 +1,120 @@ +# 排行榜 + +## 页面概述 + +排行榜页面展示学员在多个维度的排名数据,支持灵活的筛选条件和时间范围选择。页面包含4个排行榜标签页,每个标签页均有独立的时间筛选器和数据表格。 + +## 页面结构 + +### 1. 页面标题区域 +- 返回首页按钮:触发 `go-back` 事件 +- 页面标题:排行榜 + +### 2. 筛选区域 + +#### 筛选规则提示 +- 提示文字:先选**云校/学校**,再选**年级**,最后选**班级**(级联选择) +- 重置按钮:恢复所有筛选条件为默认值 + +#### 筛选条件 + +| 筛选项 | 组件 | 默认值 | 级联关系 | 选项 | +|--------|------|--------|----------|------| +| 云校 | `a-select` | `all`(全部云校) | 顶层 | 全部云校、华东云校、华北云校、华南云校、西部云校 | +| 学校 | `a-select` | 无 | 依赖云校 | 全部学校 + 根据云校动态过滤的学校列表 | +| 年级 | `a-select` | 无 | 依赖学校 | 高一、高二、高三(未选学校时禁用) | +| 班级 | `a-select` | 无 | 依赖年级 | 根据年级动态过滤的班级列表(未选年级时禁用) | +| 科目 | `a-select` | `all`(全部科目) | 独立 | 全部科目、语文、数学、英语、物理、化学、生物、历史、地理、政治 | +| 学生层次 | `a-select` | `all`(全部) | 独立 | 全部、本科、特控、拔尖 | + +#### 级联选择逻辑 +- 切换云校 → 清空学校、年级、班级 +- 切换学校 → 清空年级、班级 +- 切换年级 → 清空班级 +- 年级列和班级列根据筛选条件动态显示/隐藏 + +#### 学校与云校的对应关系 +| 云校 | 所属学校 | +|------|----------| +| 华东云校 | 清华大学、北京大学、复旦大学、上海交通大学 | +| 华北云校 | 浙江大学、南京大学 | +| 华南云校 | 武汉大学、华中科技大学、中山大学 | +| 西部云校 | 四川大学 | + +#### 年级与班级的对应关系 +| 年级 | 班级 | +|------|------| +| 高一 | 1班、2班、3班 | +| 高二 | 4班、5班、6班 | +| 高三 | 7班、8班、9班 | + +### 3. 排行榜标签页 + +#### 标签页列表 + +| Key | 标签名 | 数据列 | +|-----|--------|--------| +| `points` | 积分排行榜 | 排名、学员姓名、学校、[年级]、[班级]、总积分 | +| `courses` | 完成知识点排行榜 | 排名、学员姓名、学校、[年级]、[班级]、完成知识点数 | +| `likes` | 互动排行榜 | 排名、学员姓名、学校、[年级]、[班级]、点赞+评论数 | +| `answers` | 笔记精选排行榜 | 排名、学员姓名、学校、[年级]、[班级]、精选次数 | + +> 注:年级列和班级列仅在对应筛选条件被选中时才显示(动态列配置) + +### 4. 时间筛选器 + +每个标签页均有独立的时间筛选器: + +| 选项 | Key | 日期范围计算逻辑 | +|------|-----|------------------| +| 总榜 | `all` | 默认最近30天,可自定义(最多60天) | +| 本周榜 | `thisWeek` | 本周一至本周日 | +| 上周榜 | `lastWeek` | 上周一至上周日 | +| 今日榜 | `today` | 当天 | +| 昨日榜 | `yesterday` | 昨天 | + +#### 自定义时间范围(仅总榜可用) +- 非总榜时,时间范围选择器禁用,自动计算对应日期范围 +- 总榜时,可手动选择起止日期 +- 最大跨度限制:60天 +- 起始日期最大值 = 结束日期 +- 结束日期最小值 = 起始日期 +- 结束日期最大值 = 起始日期 + 60天 + +### 5. 排行榜表格 + +#### 排名展示 +- 前三名:显示奖牌图标(金 `rank-gold`、银 `rank-silver`、铜 `rank-bronze`) +- 第四名及以后:显示数字 + +#### 学员信息 +- 头像:根据姓名首字生成,颜色由姓名字符编码决定 +- 姓名:直接显示 + +#### 数据展示 +- 积分:带千分位格式化,后缀"积分" +- 完成知识点数:后缀"个" +- 互动数:带千分位格式化,后缀"次" +- 精选次数:后缀"次" + +#### 分页 +- 每页10条 + +### 6. 导出功能 +- 每个标签页均有"导出表格"按钮 +- 导出格式:CSV + +## 数据生成逻辑 + +模拟数据采用确定性算法生成,覆盖所有学校、年级、班级、科目、学生层次的组合,每个组合生成2名学生。数据按数值降序排序后分配排名。筛选后重新计算排名。 + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `go-back` | 点击返回首页按钮 | 无 | + +## 组件依赖 + +- TDesign Vue Next(`t-button`、`t-icon`) +- Ant Design Vue(`a-select`、`a-table`、`a-tabs`、`a-avatar`、`a-button`) diff --git a/src/md/LearningBehaviorReport.md b/src/md/LearningBehaviorReport.md new file mode 100644 index 0000000..3be5a34 --- /dev/null +++ b/src/md/LearningBehaviorReport.md @@ -0,0 +1,190 @@ +# 学习行为报告 + +## 页面概述 + +学习行为报告页面展示学员学习行为的各项数据指标,包括学员人数、上线人数、打卡人数、互动数据、学习时长等多维度统计。页面提供灵活的筛选条件、可配置的数据表格和交互式趋势图表。 + +## 页面结构 + +### 1. 页面标题区域 +- 返回首页按钮:触发 `goBack` 事件 +- 页面标题:学习行为报告 +- 副标题:学员学习行为数据统计与分析 + +### 2. 筛选控制区 + +#### 筛选规则 +- 标题:筛选条件(带过滤图标) +- 操作按钮:重置、查询 + +#### 组织架构筛选 + +| 筛选项 | 组件 | 默认值 | 级联关系 | 选项 | +|--------|------|--------|----------|------| +| 云校 | `t-select` | `all`(所有云校) | 顶层 | 所有云校、重庆八中云校、河南云校、安徽云校 | +| 学校 | `t-select` | 空 | 依赖云校 | 重庆八中、河南郑州一中、安徽合肥一中、四川成都七中、湖北武汉二中 | +| 年级 | `t-select` | 空 | 依赖学校 | 高一、高二、高三(未选学校时禁用) | +| 班级 | `t-select` | 空 | 依赖年级 | 1班~6班(未选年级时禁用) | + +#### 范围筛选 + +| 筛选项 | 组件 | 默认值 | 选项 | +|--------|------|--------|------| +| 科目 | `t-select` | `all`(全部) | 全部、语文、数学、英语、物理、化学、政治、生物、历史、地理 | +| 层次 | `t-select` | `all`(全部) | 全部、本科、特控、拔尖 | +| 时间 | `a-range-picker` | 最近30天 | 最多查询30天,禁止选择未来日期 | + +#### 级联选择逻辑 +- 切换云校 → 触发 `handleCloudSchoolChange` +- 切换学校 → 清空年级、班级 +- 切换年级 → 清空班级 + +#### 日期范围验证 +- 最大查询跨度:30天 +- 选中第一个日期后,自动禁用距离超过30天的日期 +- 禁止选择未来日期 +- 校验失败时显示红色错误提示,并清空日期选择 +- 提交查询时进行二次校验 + +### 3. 数据表格区域 + +#### 操作按钮 +- **导出数据**:打开导出确认模态框 +- **编辑列**:打开列编辑模态框 + +#### 表格列定义(共17列) + +| Key | 列名 | 宽度 | 对齐 | 固定 | Tooltip 说明 | +|-----|------|------|------|------|-------------| +| `id` | 序号 | 70 | center | left | - | +| `date` | 日期 | 110 | center | left | - | +| `studentCount` | 学员人数 | 100 | right | - | - | +| `onlineCount` | 上线人数 | 100 | right | - | 上线标准=当日学习+答题时长>30秒 | +| `checkInCount` | 打卡人数 | 100 | right | - | 打卡标准=当日任意课程完成打卡,课程打卡指标需灵活设置 | +| `dailyDropoutCount` | 当日退学人数 | 120 | right | - | - | +| `totalDropoutCount` | 累计退学人数 | 120 | right | - | - | +| `likedCount` | 点赞人数 | 110 | right | - | 当日点赞人数(按人头去重) | +| `commentedCount` | 评论人数 | 110 | right | - | 当日评论人数(按人头去重) | +| `homeworkCompletedCount` | 完成所有课程人数 | 140 | right | - | 当日所有课程完成打卡人数(按人头去重) | +| `noCheckInCount` | 未打卡人数 | 110 | right | - | 当日所有课程未打卡 | +| `excellentHomeworkCount` | 笔记评精选人数 | 140 | right | - | 当日笔记被评选为精选的人数(按人头去重) | +| `avgStudyDuration` | 平均学习时长 | 120 | right | - | - | +| `onlyStudyNoPayCount` | 仅学无交人数 | 120 | right | - | 当日仅参与学习,但没有提交动作的人数 | +| `masteredKnowledgePoints` | 攻克知识点数 | 120 | right | - | 当日所有人攻克知识点的数量 | +| `incompleteCount` | 未完成所有课程人数 | 140 | right | - | 当日未完成所有课程的人数 | +| `failedMasterCount` | 攻克失败人数 | 120 | right | - | 当日最大重试后仍未掌握知识点的人数 | + +#### 平均学习时长格式化 +- 格式化规则:分钟数转换为"X小时Y分钟"格式 +- 不足1小时时仅显示"Y分钟" + +#### 分页配置 +- 默认每页:10条 +- 可选每页条数:10、20、50、100 +- 显示总条数:`共 X 条数据` +- 支持快速跳页 + +### 4. 列编辑器 + +#### 功能说明 +- 支持拖拽排序列顺序(使用 `vuedraggable` 组件) +- 支持勾选控制列的显示/隐藏 +- 序号列和日期列固定在最前面,不可编辑 +- 至少需要选择一列才能保存 + +#### 操作按钮 +- 全选:选中所有可编辑列 +- 清空:取消所有可编辑列 +- 保存:保存列配置到 `localStorage` +- 取消:放弃本次修改 + +#### 持久化 +- 存储键名:`learning_behavior_report_column_order` +- 存储位置:`localStorage` +- 加载时验证配置有效性,确保所有 key 存在于默认配置中 +- 序号和日期列始终固定在最前面且可见 + +### 5. 统计图表区域 + +#### 图表控制 + +**展示指标选择**: +- 多选下拉框,最多可选10个指标 +- 显示已选/总数计数 +- 快捷操作:全选、清空 +- 默认选中:学员人数、上线人数、打卡人数、完成所有课程人数、仅学无交人数 + +**可选指标及颜色映射**: + +| 指标 | Key | 颜色 | +|------|-----|------| +| 学员人数 | `studentCount` | `#5470c6` | +| 上线人数 | `onlineCount` | `#91cc75` | +| 打卡人数 | `checkInCount` | `#2ba471` | +| 当日退学人数 | `dailyDropoutCount` | `#ee6666` | +| 累计退学人数 | `totalDropoutCount` | `#fc8452` | +| 点赞人数 | `likedCount` | `#fac858` | +| 评论人数 | `commentedCount` | `#73c0de` | +| 完成所有课程人数 | `homeworkCompletedCount` | `#3ba272` | +| 未打卡人数 | `noCheckInCount` | `#9a60b4` | +| 笔记评精选人数 | `excellentHomeworkCount` | `#ea7ccc` | +| 平均学习时长(分钟) | `avgStudyDuration` | `#48b8d0` | +| 仅学无交人数 | `onlyStudyNoPayCount` | `#dd6b66` | +| 攻克知识点数 | `masteredKnowledgePoints` | `#c23531` | +| 未完成所有课程人数 | `incompleteCount` | `#61a0a8` | +| 攻克失败人数 | `failedMasterCount` | `#ee6666` | + +**图表类型切换**: +- 折线图(默认):带平滑曲线和渐变面积 +- 柱状图 + +#### 图表特性 +- ECharts 渲染,支持窗口自适应 +- Tooltip:十字准线指示器,白色背景带阴影 +- 图例:可滚动,支持翻页 +- 工具栏:保存图片(2倍像素比)、数据视图(只读)、图表类型切换、还原 +- X轴:日期,标签旋转45度 +- Y轴:数值轴,虚线网格线 +- 折线图面积渐变:从指标颜色40%透明度渐变到5%透明度 +- 动画:800ms,cubicOut 缓动 + +### 6. 导出确认模态框 + +#### 功能说明 +- 展示当前筛选条件,允许在导出前调整 +- 导出模态框中的时间范围无30天限制(仅禁用未来日期) +- 解决模态框中日期选择器层级遮挡问题(`getPopupContainer` 设为 `document.body`) + +#### 导出逻辑 +``` +handleExportData(): + 1. 检查是否有可导出数据 + 2. 根据当前可见列配置生成数据行 + 3. 平均学习时长列使用格式化后的文本 + 4. 使用 XLSX 库创建工作簿 + 5. 导出文件名:学习行为报告_YYYYMMDDHHmmss.xlsx + 6. 导出成功/失败提示 +``` + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `goBack` | 点击返回首页按钮 | 无 | + +## 数据联动 + +- 筛选条件变化(云校、科目、层次、时间范围)→ 自动更新图表 +- 图表类型切换 → 自动更新图表 +- 指标选择变化 → 自动更新图表 +- 窗口大小变化 → 图表自适应缩放 + +## 组件依赖 + +- TDesign Vue Next(`t-button`、`t-icon`、`t-select`、`t-card`、`t-dialog`、`t-checkbox`、`t-radio-group`、`t-radio-button`) +- Ant Design Vue(`a-table`、`a-range-picker`、`a-tooltip`) +- ECharts(图表渲染) +- dayjs(日期处理) +- xlsx(Excel 导出) +- vuedraggable(列拖拽排序) +- @ant-design/icons-vue(`ExclamationCircleOutlined`、`InfoCircleOutlined`) diff --git a/src/md/LoginPage.md b/src/md/LoginPage.md new file mode 100644 index 0000000..44cf45e --- /dev/null +++ b/src/md/LoginPage.md @@ -0,0 +1,101 @@ +# 登录页面 + +## 页面概述 + +登录页面是系统的入口,提供扫码登录和账号密码登录两种方式。页面采用左右分栏布局,左侧为装饰插画区,右侧为登录操作区。当前为产品原型演示阶段,扫码登录暂不可用。 + +## 页面结构 + +### 1. 顶部 Logo 区域 +- 展示劝学品牌 Logo 图标和"劝学"文字 +- Logo 图标来源于 `../assets/logo.png` + +### 2. 主体内容区(左右分栏) + +#### 左侧装饰区 +- 展示装饰插画,图片来源 `../assets/content-block-1-eZzov1mX.png` +- 最大宽度 480px,自适应缩放 + +#### 右侧登录区域 + +##### 原型提示框 +- 默认显示(`showNotice = true`),可手动关闭 +- 提示内容: + - 标题:产品原型演示阶段 + - 说明:扫码登录暂不可用,请使用账号密码登录 + - 测试账号:账号 `1`,密码 `1`(以橙色标签高亮展示) +- 关闭按钮位于右上角 +- 带有淡入淡出动画(`notice-fade` 过渡) + +##### 登录卡片(宽度 340px) + +**Tab 切换**: +- 扫码登录(默认激活) +- 账号登录 +- 激活状态:文字变为主题蓝色,底部显示蓝色下划线指示器 + +**扫码登录面板**: +- 模拟二维码 SVG 展示(150×150px) +- 提示文字:请使用劝学APP扫码登录 +- 操作路径说明:打开劝学·我的-右上角扫一扫 +- App 下载横幅: + - 链接地址:`https://app.23544.com/#/download`(新窗口打开) + - 内容:下载图标 + "没有劝学APP?" + "点击立即下载,体验更多功能" + - 悬停效果:上浮2px,阴影加深,边框变蓝 + +**账号登录面板**: +- 账号输入框: + - 标签:账号 + - 占位符:请输入手机号/账号 + - 默认值:`1`(测试账号) +- 密码输入框: + - 标签:密码 + - 占位符:请输入密码 + - 默认值:`1`(测试密码) + - 支持密码显示/隐藏切换(眼睛图标按钮) + - `showPassword` 状态控制输入框类型(`text` / `password`) +- 表单选项行: + - 记住我:复选框,绑定 `form.remember` + - 忘记密码:链接(功能待实现) +- 登录按钮:点击触发 `handleLogin` 方法 + +## 交互逻辑 + +### 登录验证 +``` +handleLogin(): + 1. 校验:用户名和密码不能为空 + 2. 校验失败:弹出 alert 提示"请输入账号和密码" + 3. 校验成功:触发 login-success 事件 +``` + +### 表单数据模型 +```javascript +form = { + username: '1', // 默认测试账号 + password: '1', // 默认测试密码 + remember: false // 记住我状态 +} +``` + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `login-success` | 账号密码校验通过 | 无 | + +## 页面样式 + +- 整体背景:蓝色渐变 `linear-gradient(145deg, #dce8fb, #e8f1fd, #c8dcf8)` +- 底部波浪装饰:半透明蓝色渐变 +- 登录卡片:白色圆角卡片,阴影 `0 8px 40px rgba(0, 82, 217, 0.1)` + +## 注意事项 + +- 当前为原型演示阶段,扫码登录仅为 UI 展示,实际不可用 +- 测试账号密码均为 `1`,已预填在表单中 +- 忘记密码链接功能尚未实现 + +## 组件依赖 + +- 无外部组件库依赖,纯 Vue 3 组件 diff --git a/src/md/NoteEvaluationPage.md b/src/md/NoteEvaluationPage.md new file mode 100644 index 0000000..50173e2 --- /dev/null +++ b/src/md/NoteEvaluationPage.md @@ -0,0 +1,210 @@ +# 笔记评优 + +## 页面概述 + +笔记评优页面用于查看和管理班级课程笔记的评价数据。页面采用左右分栏布局,左侧为日期选择和课程列表,右侧为朋友圈风格的笔记提交记录展示。教师可对学员笔记进行评优操作,包括选择图片、添加点评等。 + +## 页面结构 + +### 1. 页面标题区域 +- 返回按钮:触发 `go-back` 事件 +- 标题图标:笔记评优 +- 副标题:查看和管理班级课程笔记评价数据 + +### 2. 标签页 + +| Key | 标签名 | 状态 | +|-----|--------|------| +| `class1` | 爱学蝶变高一1班 | 已开发 | +| `class2` | 爱学蝶变高一2班 | 开发中 | +| `class3` | 爱学蝶变高一3班 | 开发中 | + +> 仅高一1班有完整功能,其余班级显示"功能开发中"占位 + +### 3. 左侧面板(30%宽度) + +#### 日期选择器 +- 默认选中当天 +- 左右箭头切换日期(左箭头=下一天,右箭头=上一天) +- 点击日期显示区域展开自定义日历选择器 +- 活动时间范围:过去30天至今天,禁止选择未来日期 +- 日历组件功能:月份切换、星期标题、日期网格、清除/今天/确定按钮 +- 点击外部区域自动关闭日历 +- 日期验证:不在活动范围内时显示错误提示 + +#### 课程安排列表 +- 显示课程数量徽标 +- 课程卡片内容:节次名称、上课时间、科目名称、课程类型标签(预习/复习)、状态图标 +- 课程状态根据选中日期动态计算: + - 过去日期:全部"已上课"(绿色对勾) + - 未来日期:全部"未上课"(灰色时钟) + - 当天:第1~3节"已上课",第4节"正在上课"(黄色播放图标+脉冲动画),第5~8节"未上课" +- 点击课程卡片选中/取消选中 +- 选中状态:卡片高亮显示 + +**课程数据**: + +| ID | 节次 | 时间 | 科目 | 类型 | +|----|------|------|------|------| +| 1 | 第1节 | 8:00-8:45 | 语文 | 预习 | +| 2 | 第2节 | 8:55-9:40 | 数学 | 复习 | +| 3 | 第3节 | 10:00-10:45 | 英语 | 预习 | +| 4 | 第4节 | 10:55-11:40 | 物理 | 复习 | +| 5 | 第5节 | 14:00-14:45 | 化学 | 预习 | +| 6 | 第6节 | 14:55-15:40 | 生物 | 复习 | +| 7 | 第7节 | 16:00-16:45 | 历史 | 预习 | +| 8 | 第8节 | 16:55-17:40 | 地理 | 复习 | + +### 4. 右侧面板(70%宽度) + +#### 空状态 +- 未选择课程时显示 +- 提示:选择左侧课程卡片查看详情 + +#### 笔记提交记录(选择课程后显示) + +**标题栏**: +- 课程名称 - 笔记提交记录 +- 已交人数 / 筛选结果人数 + +**筛选区域**: + +| 筛选项 | 组件 | 选项 | +|--------|------|------| +| 评优状态 | `a-radio-group` | 全部、已评优、未评优 | +| 学生姓名 | `a-input` | 关键词搜索,支持清除 | + +- 有筛选条件时显示"清除筛选"按钮 +- 筛选条件变化时自动重置到第1页 + +**笔记卡片(朋友圈风格)**: + +每条笔记包含以下区域: +1. **评优标签**:已评优笔记顶部显示星形图标 + "优秀标记" +2. **头像**:学员头像 +3. **姓名**:学员姓名 +4. **内容**:笔记文字描述 +5. **图片墙**: + - 布局规则:1张=`grid-1`,2或4张=`grid-2`,其他=`grid-3` + - 超过9张图片时只显示前9张,第9格显示"+N 查看全部"遮罩 + - 未评优状态下:左上角显示勾选圆圈,点击切换图片选中状态 + - 已评优状态下:图片锁定,不可取消选中 + - 选中逻辑:默认全选(`selectedImages` 为空时所有图片视为选中) +6. **评优操作栏**: + - 左侧:评优标签文字 + 已选图片数量提示 + - 右侧:`a-switch` 开关(选中显示"优",未选中显示"评") +7. **教师点评**:已评优笔记显示点评内容和时间 +8. **底部**:提交时间 + 点赞数/评论数(点击打开详情弹窗) + +**图片选择逻辑**: +``` +toggleImageSelect(student, index): + - 已评优 → 不允许操作 + - selectedImages 为空(全选状态)→ 取消该张,其余保留 + - selectedImages 非空: + - 已选中 → 从列表移除 + - 未选中 → 添加到列表,若全部选中则重置为空(全选) +``` + +### 5. 分页控件 +- 总记录数 / 当前页 / 总页数 +- 每页显示数量选择:10、20、50 条 +- 页码导航:首页、上一页、页码按钮、下一页、末页 +- 页码显示规则:总页数≤7时全部显示,否则使用省略号 + +### 6. 灯箱(Lightbox) + +#### 功能说明 +- 全屏遮罩层,使用 `teleport` 渲染到 `body` +- 图片居中显示,带切换动画 +- 图片计数器:`当前索引 + 1 / 总数` +- 左右箭头切换图片(循环) +- 底部缩略图条:当前图片高亮,已选中图片带勾选标记 +- 未评优状态下:灯箱内显示"选择此图/已选中"按钮,缩略图支持勾选 + +#### 键盘快捷键 + +| 快捷键 | 功能 | +|--------|------| +| `ArrowLeft` | 上一张图片 | +| `ArrowRight` | 下一张图片 | +| `Escape` | 关闭灯箱 | + +### 7. 详情弹窗 + +#### 功能说明 +- `a-modal` 弹窗,宽度 520px,居中显示 +- 头部:学员头像、姓名、提交时间、优秀笔记标签 +- 笔记内容文字 +- 点赞区:点赞数量 + 点赞者列表(头像+姓名) +- 评论区:评论数量 + 评论列表(头像+姓名+内容+时间) +- 无数据时显示"暂无点赞"/"暂无评论" + +### 8. 评优模态框 + +#### 功能说明 +- 开启评优时弹出(`a-switch` 切换为 `true`) +- 使用 `teleport` 渲染到 `body` +- 标题:评为优秀笔记 +- 副标题:为 XXX 的笔记添加点评 + +#### 内容区域 +1. **已选图片预览**:最多显示4张缩略图,超过4张显示"+N" +2. **点评内容输入**: + - `a-textarea`,4行,最大200字,带字数统计 + - 必填项,校验失败时显示红色错误提示 + - 占位符:请输入对该笔记的点评内容 + +#### 操作按钮 +- 取消:关闭模态框,不保存 +- 确认评优:校验点评内容,通过后设置评优状态 + +#### 评优确认逻辑 +``` +confirmExcellent(): + 1. 校验点评内容不为空 + 2. 设置 student.isExcellent = true + 3. 设置 student.excellentComment = 输入的点评 + 4. 设置 student.excellentTime = 当前时间(HH:mm) + 5. 关闭模态框 +``` + +### 9. 取消评优确认对话框 + +#### 功能说明 +- 取消评优时弹出(`a-switch` 切换为 `false`) +- 使用 `teleport` 渲染到 `body` +- 警告图标 + 标题"确认取消评优?" +- 说明文字:取消后优秀标记及点评内容将被清除,此操作不可撤销 +- 操作按钮: + - 再想想:关闭对话框 + - 确认取消:执行取消评优操作 + +#### 取消评优逻辑 +``` +doRemoveExcellent(): + 1. 设置 student.isExcellent = false + 2. 清空 student.excellentComment + 3. 清空 student.excellentTime + 4. 关闭对话框 +``` + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `go-back` | 点击返回按钮 | 无 | + +## 数据说明 + +- 模拟数据:100名学生,每名有1~8张随机图片 +- 每7名学生中有1名预设为已评优状态 +- 点赞和评论数据随机生成 +- 头像使用 `api.dicebear.com` 生成 +- 图片使用 `picsum.photos` 生成 + +## 组件依赖 + +- Ant Design Vue(`a-tabs`、`a-tab-pane`、`a-row`、`a-col`、`a-radio-group`、`a-input`、`a-switch`、`a-textarea`、`a-select`、`a-modal`) +- @ant-design/icons-vue(`CaretLeftOutlined`、`CaretRightOutlined`、`StarFilled`) +- dayjs(日期处理) diff --git a/src/md/PortalHomePage.md b/src/md/PortalHomePage.md new file mode 100644 index 0000000..595aedf --- /dev/null +++ b/src/md/PortalHomePage.md @@ -0,0 +1,96 @@ +# 门户首页 + +## 页面概述 + +门户首页是用户登录后的主入口页面,展示系统的主要功能模块。页面分为两大模块区域:双师课堂和爱学蝶变,每个模块以卡片形式展示功能入口。 + +## 页面结构 + +### 1. 顶部导航栏 +- 高度 60px,毛玻璃效果(`backdrop-filter: blur(20px)`) +- 粘性定位(`position: sticky`),始终置顶 +- 左侧:Logo 图标(来源于 `../Assets/logo.png`) +- 右侧:用户信息区域 + - 用户头像:蓝色渐变圆角矩形,显示姓氏首字"杨" + - 用户名:显示"杨某某" + - 下拉箭头:悬停时旋转180度 + - 悬停效果:背景变为浅蓝色 + +#### 用户下拉菜单 +- 鼠标移入用户信息区域时显示 +- 菜单项:退出登录(带图标) +- 悬停效果:背景渐变,文字变蓝 +- 过渡动画:`dropdown` 动画(淡入 + 上移 + 缩放) +- 点击"退出登录":触发 `logout` 事件 + +### 2. 主内容区 + +#### 双师课堂模块 +- 模块标识:蓝色圆点 + "双师课堂"标题 +- 功能卡片: + +| 卡片ID | 名称 | 图标颜色 | 点击行为 | +|--------|------|----------|----------| +| `schedule` | 课表管理 | 蓝色 `#1677ff` | 弹出提示框"跳转之前的课表管理界面" | + +#### 爱学蝶变模块 +- 模块标识:紫色圆点 + "爱学蝶变"标题 +- 功能卡片: + +| 卡片ID | 名称 | 图标颜色 | 点击行为 | +|--------|------|----------|----------| +| `schedule2` | 课表管理 | 蓝色 `#1677ff` | 弹出提示框,说明与双师课堂课表管理的差异 | +| `notes` | 笔记评优 | 橙色 `#fa8c16` | 触发 `card-click` 事件,参数 `'note_evaluation'` | +| `report` | 报告中心 | 绿色 `#52c41a` | 触发 `card-click` 事件,参数 `'report'` | + +#### 爱学蝶变课表管理与双师课堂课表管理的差异 +点击爱学蝶变的"课表管理"时,弹窗提示以下差异: +1. 没有单双周功能 +2. 没有临时调课功能 +3. 新建课表没有创建科目信息这一步,默认九大科目:语文、数学、英语、物理、化学、生物、政治、历史、地理 +4. 增加课表的课程需要增加:预习、复习 + +### 3. 底部版权 +- 内容:`Copyright © 2025 QuanXue. All Rights Reserved. 渝ICP备05088888号` + +### 4. 提示弹窗 +- 用于展示功能说明信息(如课表管理差异说明) +- 点击遮罩层可关闭 +- 包含信息图标、消息文本和"确定"按钮 + +## 交互逻辑 + +### 卡片点击处理 +``` +handleCardClick(cardId): + - 'schedule' → 显示提示弹窗"跳转之前的课表管理界面" + - 'schedule2' → 显示提示弹窗,列出爱学蝶变课表管理的4项差异 + - 'notes' → 触发 card-click 事件,参数 'note_evaluation' + - 'report' → 触发 card-click 事件,参数 'report' +``` + +### 退出登录 +``` +handleLogout(): + 1. 关闭下拉菜单 + 2. 触发 logout 事件 +``` + +## 事件说明 + +| 事件名 | 触发时机 | 参数 | +|--------|----------|------| +| `navigate` | 保留事件 | - | +| `card-click` | 点击笔记评优或报告中心卡片 | `'note_evaluation'` 或 `'report'` | +| `logout` | 点击退出登录 | 无 | + +## 页面样式 + +- 整体背景:灰蓝渐变 `linear-gradient(165deg, #f5f7fc, #eef2fa, #e8ecf8)` +- 模块区块:白色毛玻璃卡片,圆角20px,多层阴影 +- 功能卡片:白色渐变背景,圆角18px,悬停时边框变蓝、阴影加深 +- 最大内容宽度:1120px,居中显示 + +## 组件依赖 + +- 无外部组件库依赖,纯 Vue 3 组件 diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..c533964 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,71 @@ +import { createRouter, createWebHistory } from 'vue-router' +import HomeNav from '../components/HomeNav.vue' +import LoginPage from '../components/LoginPage.vue' +import MobilePrototypePage from '../components/MobilePrototypePage.vue' +import NoteEvaluationPage from '../components/NoteEvaluationPage.vue' +import HomePage from '../components/HomePage.vue' + +const routes = [ + { + path: '/', + name: 'HomeNav', + component: HomeNav + }, + { + path: '/login', + name: 'LoginPage', + component: LoginPage + }, + { + path: '/mb', + name: 'MobilePrototype', + component: MobilePrototypePage + }, + { + path: '/admin', + name: 'Admin', + component: () => import('../components/PortalHomePage.vue') + }, + { + path: '/admin-dashboard', + name: 'AdminDashboard', + component: () => import('../components/AdminDashboardPage.vue') + }, + { + path: '/note-evaluation', + name: 'NoteEvaluation', + component: () => import('../components/NoteEvaluationPage.vue') + }, + { + path: '/report-home', + name: 'ReportHome', + component: () => import('../components/HomePage.vue') + }, + { + path: '/cloud-school-report', + name: 'CloudSchoolReport', + component: () => import('../components/CloudSchoolReport.vue') + }, + { + path: '/learning-behavior-report', + name: 'LearningBehaviorReport', + component: () => import('../components/LearningBehaviorReport.vue') + }, + { + path: '/leaderboard', + name: 'Leaderboard', + component: () => import('../components/LeaderboardPage.vue') + }, + { + path: '/english-word-report', + name: 'EnglishWordReport', + component: () => import('../components/EnglishWordReport.vue') + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router diff --git a/styles_patch.css b/styles_patch.css new file mode 100644 index 0000000..95ea24b --- /dev/null +++ b/styles_patch.css @@ -0,0 +1,31 @@ +/* + * 将以下样式添加到 src/components/CloudSchoolReport.vue 的