add wc
This commit is contained in:
93
package-lock.json
generated
93
package-lock.json
generated
@@ -24,6 +24,7 @@
|
|||||||
"axios": "^1.16.1",
|
"axios": "^1.16.1",
|
||||||
"canvas-confetti": "^1.9.4",
|
"canvas-confetti": "^1.9.4",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
|
"chartjs-chart-wordcloud": "^4.4.5",
|
||||||
"client-zip": "^2.5.0",
|
"client-zip": "^2.5.0",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"copy-text-to-clipboard": "^3.2.2",
|
"copy-text-to-clipboard": "^3.2.2",
|
||||||
@@ -523,6 +524,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz",
|
||||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@marijn/find-cluster-break": "^1.0.0"
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -532,6 +534,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.4.tgz",
|
||||||
"integrity": "sha512-hduz0suCcUSC/kM8Fq3A9iLwInJDl8fD1xLpTIk+5xkNm8z/FT7UsIa9sOXrkpChh+XXc18RzswE8QqELsVl+g==",
|
"integrity": "sha512-hduz0suCcUSC/kM8Fq3A9iLwInJDl8fD1xLpTIk+5xkNm8z/FT7UsIa9sOXrkpChh+XXc18RzswE8QqELsVl+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"crelt": "^1.0.6",
|
"crelt": "^1.0.6",
|
||||||
@@ -1184,6 +1187,7 @@
|
|||||||
"integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==",
|
"integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.8.0"
|
"tslib": "^2.8.0"
|
||||||
}
|
}
|
||||||
@@ -1280,6 +1284,21 @@
|
|||||||
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
|
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/d3-cloud": {
|
||||||
|
"version": "1.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.9.tgz",
|
||||||
|
"integrity": "sha512-5EWJvnlCrqTThGp8lYHx+DL00sOjx2HTlXH1WRe93k5pfOIhPQaL63NttaKYIbT7bTXp/USiunjNS/N4ipttIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3": "^3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-cloud/node_modules/@types/d3": {
|
||||||
|
"version": "3.5.53",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.53.tgz",
|
||||||
|
"integrity": "sha512-8yKQA9cAS6+wGsJpBysmnhlaaxlN42Qizqkw+h2nILSlS+MAG2z4JdO6p+PJrJ+ACvimkmLJL281h157e52psQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/d3-color": {
|
"node_modules/@types/d3-color": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||||
@@ -1559,6 +1578,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@uppy/core/-/core-2.3.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@uppy/core/-/core-2.3.4.tgz",
|
||||||
"integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==",
|
"integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@transloadit/prettier-bytes": "0.0.7",
|
"@transloadit/prettier-bytes": "0.0.7",
|
||||||
"@uppy/store-default": "^2.1.1",
|
"@uppy/store-default": "^2.1.1",
|
||||||
@@ -1608,6 +1628,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz",
|
||||||
"integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==",
|
"integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uppy/companion-client": "^2.2.2",
|
"@uppy/companion-client": "^2.2.2",
|
||||||
"@uppy/utils": "^4.1.2",
|
"@uppy/utils": "^4.1.2",
|
||||||
@@ -1682,6 +1703,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@vue-flow/core/-/core-1.48.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vue-flow/core/-/core-1.48.2.tgz",
|
||||||
"integrity": "sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==",
|
"integrity": "sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vueuse/core": "^10.5.0",
|
"@vueuse/core": "^10.5.0",
|
||||||
"d3-drag": "^3.0.0",
|
"d3-drag": "^3.0.0",
|
||||||
@@ -2033,6 +2055,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz",
|
||||||
"integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==",
|
"integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/web-bluetooth": "^0.0.21",
|
"@types/web-bluetooth": "^0.0.21",
|
||||||
"@vueuse/metadata": "14.3.0",
|
"@vueuse/metadata": "14.3.0",
|
||||||
@@ -2087,6 +2110,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@wangeditor-next/basic-modules/-/basic-modules-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@wangeditor-next/basic-modules/-/basic-modules-2.0.0.tgz",
|
||||||
"integrity": "sha512-oH7Cv6mHorvBkj5t3isP9wncgWABYLlQpoQZYOIFtWVwgsQatwoGVFHF6PoJzz+mTkt5UaJcyfDxFSo9Thvhdw==",
|
"integrity": "sha512-oH7Cv6mHorvBkj5t3isP9wncgWABYLlQpoQZYOIFtWVwgsQatwoGVFHF6PoJzz+mTkt5UaJcyfDxFSo9Thvhdw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-url": "^1.2.4"
|
"is-url": "^1.2.4"
|
||||||
},
|
},
|
||||||
@@ -2119,6 +2143,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@wangeditor-next/core/-/core-1.8.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@wangeditor-next/core/-/core-1.8.0.tgz",
|
||||||
"integrity": "sha512-U2TlQ0Lpo6aLb0KD8oJgzG/rFAYO61cy+qbZu+t5lDfS3CECNjOhGIC3C7/dXIhiMQ8V/LY4cvrPt9R5G4vLjA==",
|
"integrity": "sha512-U2TlQ0Lpo6aLb0KD8oJgzG/rFAYO61cy+qbZu+t5lDfS3CECNjOhGIC3C7/dXIhiMQ8V/LY4cvrPt9R5G4vLjA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/event-emitter": "^0.3.3",
|
"@types/event-emitter": "^0.3.3",
|
||||||
"event-emitter": "^0.3.5",
|
"event-emitter": "^0.3.5",
|
||||||
@@ -2148,6 +2173,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@wangeditor-next/editor/-/editor-5.7.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@wangeditor-next/editor/-/editor-5.7.0.tgz",
|
||||||
"integrity": "sha512-bxkw/TeWBJz7AU4qXZnx5tx/s1yzx8XdLmSRN9ev4btArKnfXR/6hr3dqxUSsyea8q//IpEv1qr9tc2ureEAsA==",
|
"integrity": "sha512-bxkw/TeWBJz7AU4qXZnx5tx/s1yzx8XdLmSRN9ev4btArKnfXR/6hr3dqxUSsyea8q//IpEv1qr9tc2ureEAsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uppy/core": "^2.1.1",
|
"@uppy/core": "^2.1.1",
|
||||||
"@uppy/xhr-upload": "^2.0.3",
|
"@uppy/xhr-upload": "^2.0.3",
|
||||||
@@ -2439,6 +2465,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||||
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kurkle/color": "^0.3.0"
|
"@kurkle/color": "^0.3.0"
|
||||||
},
|
},
|
||||||
@@ -2446,6 +2473,19 @@
|
|||||||
"pnpm": ">=8"
|
"pnpm": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chartjs-chart-wordcloud": {
|
||||||
|
"version": "4.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-chart-wordcloud/-/chartjs-chart-wordcloud-4.4.5.tgz",
|
||||||
|
"integrity": "sha512-x7gdE5BZyj31+bjHbZ/0tX4hB6un7TQhvwdO5qHhqpmJCS6bONHjMuDzSjL/Qw2uI6CT2/U04LGqBREHwBiK3g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-cloud": "^1.2.9",
|
||||||
|
"d3-cloud": "^1.2.7"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
|
||||||
@@ -2472,6 +2512,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz",
|
||||||
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/commands": "^6.0.0",
|
"@codemirror/commands": "^6.0.0",
|
||||||
@@ -2591,6 +2632,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.14.tgz",
|
"resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.14.tgz",
|
||||||
"integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==",
|
"integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/hash": "~0.8.0",
|
"@emotion/hash": "~0.8.0",
|
||||||
"csstype": "~3.0.5"
|
"csstype": "~3.0.5"
|
||||||
@@ -2619,6 +2661,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.3.tgz",
|
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.3.tgz",
|
||||||
"integrity": "sha512-Gej7U+OKR+LZ8kvX7rb2HhCYJ0IhvEFsnkud4SB1PR+BUY/TsSO0dmOW59WEVLu51b1Rm+gQRKoz4bLYxGSZ2g==",
|
"integrity": "sha512-Gej7U+OKR+LZ8kvX7rb2HhCYJ0IhvEFsnkud4SB1PR+BUY/TsSO0dmOW59WEVLu51b1Rm+gQRKoz4bLYxGSZ2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@@ -2765,6 +2808,21 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-cloud": {
|
||||||
|
"version": "1.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.9.tgz",
|
||||||
|
"integrity": "sha512-leL1GLneC9ZQtnV+6TGWrNlGfI1WX7S2arcTv2vae12DaXo5wjm6GBCkskXbrDlyOymd/A75Pyj1H37MW4BZ/Q==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dispatch": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-cloud/node_modules/d3-dispatch": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/d3-collection": {
|
"node_modules/d3-collection": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
|
||||||
@@ -3047,6 +3105,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -3481,6 +3540,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/kossnocorp"
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
@@ -3541,6 +3601,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-4.0.6.tgz",
|
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-4.0.6.tgz",
|
||||||
"integrity": "sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA==",
|
"integrity": "sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ssr-window": "^4.0.0"
|
"ssr-window": "^4.0.0"
|
||||||
}
|
}
|
||||||
@@ -4059,7 +4120,8 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz",
|
||||||
"integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==",
|
"integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/is-url": {
|
"node_modules/is-url": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
@@ -4235,37 +4297,43 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.clonedeep": {
|
"node_modules/lodash.clonedeep": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.debounce": {
|
"node_modules/lodash.debounce": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.foreach": {
|
"node_modules/lodash.foreach": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
|
||||||
"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==",
|
"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.throttle": {
|
"node_modules/lodash.throttle": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||||
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
|
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.toarray": {
|
"node_modules/lodash.toarray": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||||
"integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==",
|
"integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lucide-vue-next": {
|
"node_modules/lucide-vue-next": {
|
||||||
"version": "0.543.0",
|
"version": "0.543.0",
|
||||||
@@ -4305,6 +4373,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
|
||||||
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
|
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1",
|
"argparse": "^2.0.1",
|
||||||
"entities": "^4.4.0",
|
"entities": "^4.4.0",
|
||||||
@@ -4596,6 +4665,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.js"
|
"nanoid": "bin/nanoid.js"
|
||||||
},
|
},
|
||||||
@@ -4673,6 +4743,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
|
||||||
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-api": "^7.7.7"
|
"@vue/devtools-api": "^7.7.7"
|
||||||
},
|
},
|
||||||
@@ -5034,7 +5105,8 @@
|
|||||||
"version": "0.123.0",
|
"version": "0.123.0",
|
||||||
"resolved": "https://registry.npmmirror.com/slate/-/slate-0.123.0.tgz",
|
"resolved": "https://registry.npmmirror.com/slate/-/slate-0.123.0.tgz",
|
||||||
"integrity": "sha512-Oon3HR/QzJQBjuOUJT1jGGlp8Ff7t3Bkr/rJ2lDqxNT4H+cBnXpEVQ/si6hn1ZCHhD2xY/2N91PQoH/rD7kxTg==",
|
"integrity": "sha512-Oon3HR/QzJQBjuOUJT1jGGlp8Ff7t3Bkr/rJ2lDqxNT4H+cBnXpEVQ/si6hn1ZCHhD2xY/2N91PQoH/rD7kxTg==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/slate-history": {
|
"node_modules/slate-history": {
|
||||||
"version": "0.115.0",
|
"version": "0.115.0",
|
||||||
@@ -5050,6 +5122,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.6.3.tgz",
|
"resolved": "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.6.3.tgz",
|
||||||
"integrity": "sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==",
|
"integrity": "sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.17.0"
|
"node": ">=12.17.0"
|
||||||
}
|
}
|
||||||
@@ -5196,6 +5269,7 @@
|
|||||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -5410,6 +5484,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz",
|
||||||
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
|
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.34",
|
"@vue/compiler-dom": "3.5.34",
|
||||||
"@vue/compiler-sfc": "3.5.34",
|
"@vue/compiler-sfc": "3.5.34",
|
||||||
@@ -5457,6 +5532,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.6.tgz",
|
||||||
"integrity": "sha512-9+kmUTGbKMyW9Asoy98IXXYIzrTMT7JDAdpDDeEkorHvybpUvBI2wsrSM5jFOXrFydpzRFJ9vAh+80DN2PGu9w==",
|
"integrity": "sha512-9+kmUTGbKMyW9Asoy98IXXYIzrTMT7JDAdpDDeEkorHvybpUvBI2wsrSM5jFOXrFydpzRFJ9vAh+80DN2PGu9w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/generator": "^7.28.6",
|
"@babel/generator": "^7.28.6",
|
||||||
"@vue-macros/common": "^3.1.1",
|
"@vue-macros/common": "^3.1.1",
|
||||||
@@ -5709,6 +5785,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/yjs/-/yjs-13.6.30.tgz",
|
"resolved": "https://registry.npmmirror.com/yjs/-/yjs-13.6.30.tgz",
|
||||||
"integrity": "sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ==",
|
"integrity": "sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.99"
|
"lib0": "^0.2.99"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"axios": "^1.16.1",
|
"axios": "^1.16.1",
|
||||||
"canvas-confetti": "^1.9.4",
|
"canvas-confetti": "^1.9.4",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
|
"chartjs-chart-wordcloud": "^4.4.5",
|
||||||
"client-zip": "^2.5.0",
|
"client-zip": "^2.5.0",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"copy-text-to-clipboard": "^3.2.2",
|
"copy-text-to-clipboard": "^3.2.2",
|
||||||
|
|||||||
@@ -1,329 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Python流程图作业 - 学情分析看板</title>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/echarts-wordcloud@2.1.0/dist/echarts-wordcloud.min.js"></script>
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color: #f0f2f5;
|
|
||||||
--card-bg: #ffffff;
|
|
||||||
--primary: #1890ff;
|
|
||||||
--text-main: #333;
|
|
||||||
--text-secondary: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.header h1 { color: var(--text-main); margin: 0; }
|
|
||||||
.header p { color: var(--text-secondary); margin-top: 5px; }
|
|
||||||
|
|
||||||
/* 顶部概览卡片 */
|
|
||||||
.overview-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
||||||
flex: 1;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
.card:hover { transform: translateY(-5px); }
|
|
||||||
|
|
||||||
.stat-title { font-size: 14px; color: var(--text-secondary); }
|
|
||||||
.stat-value { font-size: 28px; font-weight: bold; color: var(--text-main); margin-top: 10px; }
|
|
||||||
.stat-sub { font-size: 12px; color: #52c41a; margin-top: 5px; }
|
|
||||||
|
|
||||||
/* 图表布局 */
|
|
||||||
.charts-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-box {
|
|
||||||
background: var(--card-bg);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
||||||
height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
border-left: 4px solid var(--primary);
|
|
||||||
padding-left: 10px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 学生列表 */
|
|
||||||
.student-list {
|
|
||||||
background: var(--card-bg);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
table { width: 100%; border-collapse: collapse; }
|
|
||||||
th, td { text-align: left; padding: 12px; border-bottom: 1px solid #eee; }
|
|
||||||
th { background-color: #fafafa; color: var(--text-secondary); }
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.tag-S { background: #fff7e6; color: #fa8c16; }
|
|
||||||
.tag-A { background: #e6f7ff; color: #1890ff; }
|
|
||||||
.tag-B { background: #f6ffed; color: #52c41a; }
|
|
||||||
.tag-C { background: #fff1f0; color: #f5222d; }
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="overview-container">
|
|
||||||
<div class="card">
|
|
||||||
<div class="stat-title">班级平均分</div>
|
|
||||||
<div class="stat-value" id="avgScore">0</div>
|
|
||||||
<div class="stat-sub">↑ 比上周For循环 +2.5分</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="stat-title">S+A 级别(卓越+优秀)人数</div>
|
|
||||||
<div class="stat-value" id="countA">0</div>
|
|
||||||
<div class="stat-sub">占比 35.7%</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="stat-title">未掌握核心难点</div>
|
|
||||||
<div class="stat-value" style="color: #f5222d; font-size: 24px;">循环条件</div>
|
|
||||||
<div class="stat-sub">需重点讲解 a<100 边界</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="charts-grid">
|
|
||||||
<div class="chart-box">
|
|
||||||
<div class="chart-title">作业评级分布</div>
|
|
||||||
<div id="pieChart" style="width: 100%; height: 340px;"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chart-box">
|
|
||||||
<div class="chart-title">薄弱知识点词云 (AI分析)</div>
|
|
||||||
<div id="wordCloud" style="width: 100%; height: 340px;"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="charts-grid">
|
|
||||||
<div class="chart-box" style="grid-column: span 2;">
|
|
||||||
<div class="chart-title">全班分数段统计</div>
|
|
||||||
<div id="barChart" style="width: 100%; height: 340px;"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// --- 1. 模拟数据生成 (40位同学) ---
|
|
||||||
const totalStudents = 40;
|
|
||||||
const students = [];
|
|
||||||
const familyNames = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨";
|
|
||||||
|
|
||||||
// 精确分配等级人数:S 10% (4人), A 15% (6人), B 50% (20人), C 15% (6人), D 10% (4人)
|
|
||||||
const gradeDistribution = {
|
|
||||||
S: 4, // 10% = 4人
|
|
||||||
A: 6, // 15% = 6人
|
|
||||||
B: 20, // 50% = 20人
|
|
||||||
C: 6, // 15% = 6人
|
|
||||||
D: 4 // 10% = 4人
|
|
||||||
};
|
|
||||||
|
|
||||||
let gradeCounts = { S: 0, A: 0, B: 0, C: 0, D: 0 };
|
|
||||||
let totalScore = 0;
|
|
||||||
|
|
||||||
// 创建等级数组,确保精确分配
|
|
||||||
let levelQueue = [];
|
|
||||||
for (let level in gradeDistribution) {
|
|
||||||
for (let i = 0; i < gradeDistribution[level]; i++) {
|
|
||||||
levelQueue.push(level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 打乱顺序
|
|
||||||
for (let i = levelQueue.length - 1; i > 0; i--) {
|
|
||||||
const j = Math.floor(Math.random() * (i + 1));
|
|
||||||
[levelQueue[i], levelQueue[j]] = [levelQueue[j], levelQueue[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 1; i <= totalStudents; i++) {
|
|
||||||
let score;
|
|
||||||
let level = levelQueue[i - 1];
|
|
||||||
|
|
||||||
// 根据等级生成对应分数范围
|
|
||||||
if (level === 'S') {
|
|
||||||
// S级:95-100
|
|
||||||
score = Math.floor(Math.random() * (100 - 95 + 1) + 95);
|
|
||||||
} else if (level === 'A') {
|
|
||||||
// A级:85-94
|
|
||||||
score = Math.floor(Math.random() * (95 - 85) + 85);
|
|
||||||
} else if (level === 'B') {
|
|
||||||
// B级:70-84
|
|
||||||
score = Math.floor(Math.random() * (85 - 70) + 70);
|
|
||||||
} else if (level === 'C') {
|
|
||||||
// C级:60-69
|
|
||||||
score = Math.floor(Math.random() * (70 - 60) + 60);
|
|
||||||
} else {
|
|
||||||
// D级:50-59
|
|
||||||
score = Math.floor(Math.random() * (60 - 50) + 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
gradeCounts[level]++;
|
|
||||||
totalScore += score;
|
|
||||||
|
|
||||||
let comment = "";
|
|
||||||
if (level === 'S') comment = "完美!逻辑清晰,变量初始化正确,闭环完美,代码规范。";
|
|
||||||
else if (level === 'A') comment = "逻辑清晰,变量初始化正确,闭环完美。";
|
|
||||||
else if (level === 'B') comment = "整体逻辑正确,但部分连线方向有误。";
|
|
||||||
else if (level === 'C') comment = "循环条件判断错误,导致死循环或无法进入。";
|
|
||||||
else comment = "基础概念理解不足,需要重新学习。";
|
|
||||||
|
|
||||||
students.push({
|
|
||||||
id: 2025000 + i,
|
|
||||||
name: familyNames[i % familyNames.length] + "同学",
|
|
||||||
score: score,
|
|
||||||
level: level,
|
|
||||||
comment: comment
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 2. 填充顶部数据 ---
|
|
||||||
document.getElementById('avgScore').innerText = (totalScore / totalStudents).toFixed(1);
|
|
||||||
const excellentCount = gradeCounts.S + gradeCounts.A; // S级和A级合计
|
|
||||||
document.getElementById('countA').innerText = excellentCount;
|
|
||||||
// 更新占比显示
|
|
||||||
const excellentPercent = ((excellentCount / totalStudents) * 100).toFixed(1);
|
|
||||||
const countACard = document.getElementById('countA').parentElement;
|
|
||||||
countACard.querySelector('.stat-sub').innerText = `占比 ${excellentPercent}%`;
|
|
||||||
|
|
||||||
// --- 3. 初始化图表 ---
|
|
||||||
|
|
||||||
// A. 饼图 - 等级分布
|
|
||||||
const pieChart = echarts.init(document.getElementById('pieChart'));
|
|
||||||
pieChart.setOption({
|
|
||||||
tooltip: { trigger: 'item' },
|
|
||||||
legend: { top: '5%', left: 'center' },
|
|
||||||
color: ['#fa8c16', '#1890ff', '#52c41a', '#faad14', '#f5222d'],
|
|
||||||
series: [{
|
|
||||||
name: '评级占比',
|
|
||||||
type: 'pie',
|
|
||||||
radius: ['40%', '70%'],
|
|
||||||
avoidLabelOverlap: false,
|
|
||||||
itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 },
|
|
||||||
label: { show: false, position: 'center' },
|
|
||||||
emphasis: { label: { show: true, fontSize: 20, fontWeight: 'bold' } },
|
|
||||||
data: [
|
|
||||||
{ value: gradeCounts.S, name: 'S级 (卓越)' },
|
|
||||||
{ value: gradeCounts.A, name: 'A级 (优秀)' },
|
|
||||||
{ value: gradeCounts.B, name: 'B级 (良好)' },
|
|
||||||
{ value: gradeCounts.C, name: 'C级 (待改进)' }
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// B. 词云图 - 知识点掌握情况
|
|
||||||
// 这里重点突出“循环条件”
|
|
||||||
const wordChart = echarts.init(document.getElementById('wordCloud'));
|
|
||||||
wordChart.setOption({
|
|
||||||
tooltip: {},
|
|
||||||
series: [{
|
|
||||||
type: 'wordCloud',
|
|
||||||
gridSize: 2,
|
|
||||||
sizeRange: [12, 60], // 字体大小范围
|
|
||||||
rotationRange: [-45, 45],
|
|
||||||
shape: 'circle',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
textStyle: {
|
|
||||||
fontFamily: 'sans-serif',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: function () {
|
|
||||||
return 'rgb(' + [
|
|
||||||
Math.round(Math.random() * 160),
|
|
||||||
Math.round(Math.random() * 160),
|
|
||||||
Math.round(Math.random() * 160)
|
|
||||||
].join(',') + ')';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: [
|
|
||||||
{ name: '循环条件', value: 150, textStyle: { color: 'red' } }, // 核心痛点
|
|
||||||
{ name: '变量初始化', value: 80 },
|
|
||||||
{ name: 'i=i+1', value: 70 },
|
|
||||||
{ name: 'a<100', value: 65 },
|
|
||||||
{ name: '死循环', value: 60 },
|
|
||||||
{ name: '连线方向', value: 50 },
|
|
||||||
{ name: '退出逻辑', value: 45 },
|
|
||||||
{ name: 'While语法', value: 40 },
|
|
||||||
{ name: 'Print缩进', value: 35 },
|
|
||||||
{ name: 'Yes/No分支', value: 30 },
|
|
||||||
{ name: '流程结束符', value: 25 },
|
|
||||||
{ name: '变量定义', value: 20 }
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// C. 柱状图 - 分数段
|
|
||||||
const barChart = echarts.init(document.getElementById('barChart'));
|
|
||||||
// 简单的分段统计
|
|
||||||
let ranges = { '90-100': 0, '80-89': 0, '70-79': 0, '60-69': 0, '<60': 0 };
|
|
||||||
students.forEach(s => {
|
|
||||||
if (s.score >= 90) ranges['90-100']++;
|
|
||||||
else if (s.score >= 80) ranges['80-89']++;
|
|
||||||
else if (s.score >= 70) ranges['70-79']++;
|
|
||||||
else if (s.score >= 60) ranges['60-69']++;
|
|
||||||
else ranges['<60']++;
|
|
||||||
});
|
|
||||||
|
|
||||||
barChart.setOption({
|
|
||||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
||||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
|
||||||
xAxis: { type: 'category', data: Object.keys(ranges) },
|
|
||||||
yAxis: { type: 'value' },
|
|
||||||
series: [{
|
|
||||||
name: '人数',
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: '50%',
|
|
||||||
data: Object.values(ranges),
|
|
||||||
itemStyle: {
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{ offset: 0, color: '#83bff6' },
|
|
||||||
{ offset: 0.5, color: '#188df0' },
|
|
||||||
{ offset: 1, color: '#188df0' }
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 窗口缩放适配
|
|
||||||
window.addEventListener('resize', function() {
|
|
||||||
pieChart.resize();
|
|
||||||
wordChart.resize();
|
|
||||||
barChart.resize();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -353,6 +353,20 @@ export function getFlowchartSubmissions(params: {
|
|||||||
return http.get("flowchart/submissions", { params })
|
return http.get("flowchart/submissions", { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFlowchartStatistics(
|
||||||
|
duration: { start?: string; end: string },
|
||||||
|
problemID?: string,
|
||||||
|
username?: string,
|
||||||
|
) {
|
||||||
|
return http.get("admin/flowchart/statistics", {
|
||||||
|
params: {
|
||||||
|
...duration,
|
||||||
|
problem_id: problemID,
|
||||||
|
username,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function retryFlowchartSubmission(submissionId: string) {
|
export function retryFlowchartSubmission(submissionId: string) {
|
||||||
return http.post("flowchart/submission/retry", {
|
return http.post("flowchart/submission/retry", {
|
||||||
submission_id: submissionId,
|
submission_id: submissionId,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { LANGUAGE_SHOW_VALUE } from "utils/constants"
|
|||||||
import { renderTableTitle } from "utils/renders"
|
import { renderTableTitle } from "utils/renders"
|
||||||
import ButtonWithSearch from "./components/ButtonWithSearch.vue"
|
import ButtonWithSearch from "./components/ButtonWithSearch.vue"
|
||||||
import StatisticsPanel from "shared/components/StatisticsPanel.vue"
|
import StatisticsPanel from "shared/components/StatisticsPanel.vue"
|
||||||
|
import FlowchartStatisticsPanel from "shared/components/FlowchartStatisticsPanel.vue"
|
||||||
import SubmissionLink from "./components/SubmissionLink.vue"
|
import SubmissionLink from "./components/SubmissionLink.vue"
|
||||||
import SubmissionDetail from "./detail.vue"
|
import SubmissionDetail from "./detail.vue"
|
||||||
import Grade from "./components/Grade.vue"
|
import Grade from "./components/Grade.vue"
|
||||||
@@ -448,9 +449,14 @@ const flowchartColumns: DataTableColumn<FlowchartSubmissionListItem>[] = [
|
|||||||
preset="card"
|
preset="card"
|
||||||
:style="{ maxWidth: isDesktop && '800px', maxHeight: '80vh' }"
|
:style="{ maxWidth: isDesktop && '800px', maxHeight: '80vh' }"
|
||||||
:content-style="{ overflow: 'auto' }"
|
:content-style="{ overflow: 'auto' }"
|
||||||
title="提交记录的统计"
|
:title="query.language === 'Flowchart' ? '流程图提交的统计' : '提交记录的统计'"
|
||||||
>
|
>
|
||||||
<StatisticsPanel :problem="query.problem" :username="query.username" />
|
<FlowchartStatisticsPanel
|
||||||
|
v-if="query.language === 'Flowchart'"
|
||||||
|
:problem="query.problem"
|
||||||
|
:username="query.username"
|
||||||
|
/>
|
||||||
|
<StatisticsPanel v-else :problem="query.problem" :username="query.username" />
|
||||||
</n-modal>
|
</n-modal>
|
||||||
<n-modal
|
<n-modal
|
||||||
v-model:show="codePanel"
|
v-model:show="codePanel"
|
||||||
|
|||||||
431
src/shared/components/FlowchartStatisticsPanel.vue
Normal file
431
src/shared/components/FlowchartStatisticsPanel.vue
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
<template>
|
||||||
|
<n-flex align="center">
|
||||||
|
<n-input
|
||||||
|
placeholder="用户(可选)"
|
||||||
|
v-model:value="query.username"
|
||||||
|
style="width: 150px"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<n-input
|
||||||
|
placeholder="题号(可选)"
|
||||||
|
v-model:value="query.problem"
|
||||||
|
style="width: 120px"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<n-select
|
||||||
|
style="width: 120px"
|
||||||
|
v-model:value="query.duration"
|
||||||
|
:options="durationOptions"
|
||||||
|
/>
|
||||||
|
<n-button type="primary" @click="handleStatistics">统计</n-button>
|
||||||
|
</n-flex>
|
||||||
|
|
||||||
|
<n-empty
|
||||||
|
v-if="data.total_count === 0"
|
||||||
|
description="暂无数据"
|
||||||
|
style="margin: 40px 0"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="data.total_count > 0">
|
||||||
|
<n-divider style="margin: 16px 0" />
|
||||||
|
<n-flex justify="space-around">
|
||||||
|
<div class="stat-item">
|
||||||
|
<n-text>总提交</n-text>
|
||||||
|
<n-gradient-text type="info" font-size="28">
|
||||||
|
{{ data.total_count }}
|
||||||
|
</n-gradient-text>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<n-text>平均分</n-text>
|
||||||
|
<n-gradient-text type="primary" font-size="28">
|
||||||
|
{{ data.avg_score }}
|
||||||
|
</n-gradient-text>
|
||||||
|
</div>
|
||||||
|
<template v-if="data.person_count > 0">
|
||||||
|
<div class="stat-item">
|
||||||
|
<n-text>完成人数</n-text>
|
||||||
|
<n-gradient-text type="error" font-size="28">
|
||||||
|
{{ data.completed_count }}
|
||||||
|
</n-gradient-text>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<n-text>班级人数</n-text>
|
||||||
|
<n-gradient-text type="warning" font-size="28">
|
||||||
|
{{ data.person_count }}
|
||||||
|
</n-gradient-text>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<n-text>完成度</n-text>
|
||||||
|
<n-gradient-text type="success" font-size="28">
|
||||||
|
{{ completionRate }}
|
||||||
|
</n-gradient-text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-flex>
|
||||||
|
<n-divider style="margin: 16px 0" />
|
||||||
|
|
||||||
|
<n-tabs animated type="line">
|
||||||
|
<n-tab-pane name="charts" tab="数据图表">
|
||||||
|
<n-grid :cols="2" :x-gap="20" :y-gap="20" style="margin-top: 12px">
|
||||||
|
<!-- 1. Grade pie chart -->
|
||||||
|
<n-gi>
|
||||||
|
<n-card title="等级分布">
|
||||||
|
<Doughnut :data="gradeChartData" :options="doughnutOptions" />
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
<!-- 3. Completion doughnut -->
|
||||||
|
<n-gi v-if="data.person_count > 0">
|
||||||
|
<n-card title="班级完成度">
|
||||||
|
<Doughnut
|
||||||
|
:data="completionChartData"
|
||||||
|
:options="doughnutOptions"
|
||||||
|
/>
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
<!-- 2. Radar chart -->
|
||||||
|
<n-gi v-if="hasRadarData">
|
||||||
|
<n-card title="四维评分雷达图">
|
||||||
|
<Radar :data="radarChartData" :options="radarOptions" />
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
<!-- 4. Word cloud -->
|
||||||
|
<n-gi v-if="data.word_frequencies.length > 0">
|
||||||
|
<n-card title="高频改进建议">
|
||||||
|
<div class="wordcloud-container">
|
||||||
|
<canvas ref="wordcloudCanvas"></canvas>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
</n-grid>
|
||||||
|
</n-tab-pane>
|
||||||
|
|
||||||
|
<n-tab-pane
|
||||||
|
v-if="data.data_unaccepted.length > 0"
|
||||||
|
name="unaccepted"
|
||||||
|
:tab="`未完成(${data.data_unaccepted.length})`"
|
||||||
|
>
|
||||||
|
<n-flex size="large" align="center" style="margin-top: 12px">
|
||||||
|
<span
|
||||||
|
v-for="item in data.data_unaccepted"
|
||||||
|
:key="item.username"
|
||||||
|
style="font-size: 24px"
|
||||||
|
>
|
||||||
|
{{ item.real_name }}
|
||||||
|
</span>
|
||||||
|
</n-flex>
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { formatISO, sub, type Duration } from "date-fns"
|
||||||
|
import { getFlowchartStatistics } from "oj/api"
|
||||||
|
import { DURATION_OPTIONS } from "utils/constants"
|
||||||
|
import { Doughnut, Radar } from "vue-chartjs"
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
ArcElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
RadialLinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Filler,
|
||||||
|
LinearScale,
|
||||||
|
} from "chart.js"
|
||||||
|
import {
|
||||||
|
WordCloudController,
|
||||||
|
WordElement,
|
||||||
|
} from "chartjs-chart-wordcloud"
|
||||||
|
|
||||||
|
ChartJS.register(
|
||||||
|
ArcElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
RadialLinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Filler,
|
||||||
|
LinearScale,
|
||||||
|
WordCloudController,
|
||||||
|
WordElement,
|
||||||
|
)
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
problem: string
|
||||||
|
username: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const durationOptions: SelectOption[] = [
|
||||||
|
{ label: "10分钟内", value: "minutes:10" },
|
||||||
|
{ label: "20分钟内", value: "minutes:20" },
|
||||||
|
{ label: "30分钟内", value: "minutes:30" },
|
||||||
|
...DURATION_OPTIONS,
|
||||||
|
{ label: "全部时段", value: "all" },
|
||||||
|
]
|
||||||
|
|
||||||
|
const query = reactive({
|
||||||
|
username: props.username,
|
||||||
|
problem: props.problem,
|
||||||
|
duration: durationOptions[0].value,
|
||||||
|
})
|
||||||
|
|
||||||
|
interface StatisticsData {
|
||||||
|
total_count: number
|
||||||
|
avg_score: number
|
||||||
|
grade_distribution: Record<string, number>
|
||||||
|
criteria_averages: Record<string, { avg: number; max: number }>
|
||||||
|
person_count: number
|
||||||
|
completed_count: number
|
||||||
|
word_frequencies: { word: string; count: number }[]
|
||||||
|
data_unaccepted: { username: string; real_name: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = reactive<StatisticsData>({
|
||||||
|
total_count: 0,
|
||||||
|
avg_score: 0,
|
||||||
|
grade_distribution: {},
|
||||||
|
criteria_averages: {},
|
||||||
|
person_count: 0,
|
||||||
|
completed_count: 0,
|
||||||
|
word_frequencies: [],
|
||||||
|
data_unaccepted: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const wordcloudCanvas = useTemplateRef<HTMLCanvasElement>("wordcloudCanvas")
|
||||||
|
let wordcloudChart: ChartJS | null = null
|
||||||
|
|
||||||
|
const completionRate = computed(() => {
|
||||||
|
if (data.person_count <= 0) return "0%"
|
||||||
|
const rate = Math.min(
|
||||||
|
100,
|
||||||
|
(data.completed_count / data.person_count) * 100,
|
||||||
|
)
|
||||||
|
return `${Math.round(rate * 100) / 100}%`
|
||||||
|
})
|
||||||
|
|
||||||
|
const GRADE_COLORS: Record<string, { bg: string; border: string }> = {
|
||||||
|
S: { bg: "rgba(24, 160, 88, 0.6)", border: "rgba(24, 160, 88, 1)" },
|
||||||
|
A: { bg: "rgba(32, 128, 240, 0.6)", border: "rgba(32, 128, 240, 1)" },
|
||||||
|
B: { bg: "rgba(240, 160, 32, 0.6)", border: "rgba(240, 160, 32, 1)" },
|
||||||
|
C: { bg: "rgba(208, 48, 80, 0.6)", border: "rgba(208, 48, 80, 1)" },
|
||||||
|
}
|
||||||
|
|
||||||
|
const gradeChartData = computed(() => {
|
||||||
|
const grades = ["S", "A", "B", "C"]
|
||||||
|
const counts = grades.map((g) => data.grade_distribution[g] || 0)
|
||||||
|
const labels = grades.map(
|
||||||
|
(g) => `${g}级 (${data.grade_distribution[g] || 0})`,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: counts,
|
||||||
|
backgroundColor: grades.map((g) => GRADE_COLORS[g].bg),
|
||||||
|
borderColor: grades.map((g) => GRADE_COLORS[g].border),
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const completionChartData = computed(() => {
|
||||||
|
const uncompleted = Math.max(0, data.person_count - data.completed_count)
|
||||||
|
return {
|
||||||
|
labels: ["已完成", "未完成"],
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: [data.completed_count, uncompleted],
|
||||||
|
backgroundColor: [
|
||||||
|
"rgba(106, 176, 76, 0.6)",
|
||||||
|
"rgba(255, 159, 64, 0.6)",
|
||||||
|
],
|
||||||
|
borderColor: ["rgba(106, 176, 76, 1)", "rgba(255, 159, 64, 1)"],
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const doughnutOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: { position: "bottom" as const },
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label(context: any) {
|
||||||
|
const label = context.label || ""
|
||||||
|
const value = context.parsed || 0
|
||||||
|
const total = context.dataset.data.reduce(
|
||||||
|
(a: number, b: number) => a + b,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
const pct = ((value / total) * 100).toFixed(1)
|
||||||
|
return `${label}: ${value} (${pct}%)`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const CRITERIA_ORDER = ["逻辑正确性", "完整性", "规范性", "清晰度"]
|
||||||
|
|
||||||
|
const hasRadarData = computed(() =>
|
||||||
|
CRITERIA_ORDER.some((k) => k in data.criteria_averages),
|
||||||
|
)
|
||||||
|
|
||||||
|
const radarChartData = computed(() => {
|
||||||
|
const labels = CRITERIA_ORDER
|
||||||
|
const values = CRITERIA_ORDER.map((k) => {
|
||||||
|
const item = data.criteria_averages[k]
|
||||||
|
if (!item) return 0
|
||||||
|
return Math.round((item.avg / item.max) * 100)
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "平均得分率 (%)",
|
||||||
|
data: values,
|
||||||
|
backgroundColor: "rgba(32, 128, 240, 0.2)",
|
||||||
|
borderColor: "rgba(32, 128, 240, 1)",
|
||||||
|
borderWidth: 2,
|
||||||
|
pointBackgroundColor: "rgba(32, 128, 240, 1)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const radarOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
r: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
ticks: { stepSize: 20 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label(context: any) {
|
||||||
|
const key = CRITERIA_ORDER[context.dataIndex]
|
||||||
|
const item = data.criteria_averages[key]
|
||||||
|
if (!item) return ""
|
||||||
|
return `${key}: ${item.avg}/${item.max} (${context.parsed.r}%)`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const WORD_COLORS = [
|
||||||
|
"#2080f0",
|
||||||
|
"#18a058",
|
||||||
|
"#f0a020",
|
||||||
|
"#d03050",
|
||||||
|
"#722ed1",
|
||||||
|
"#13c2c2",
|
||||||
|
"#1890ff",
|
||||||
|
"#52c41a",
|
||||||
|
"#faad14",
|
||||||
|
"#f5222d",
|
||||||
|
]
|
||||||
|
|
||||||
|
function renderWordCloud() {
|
||||||
|
if (!wordcloudCanvas.value || data.word_frequencies.length === 0) return
|
||||||
|
|
||||||
|
if (wordcloudChart) {
|
||||||
|
wordcloudChart.destroy()
|
||||||
|
wordcloudChart = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const words = data.word_frequencies
|
||||||
|
const maxCount = Math.max(...words.map((w) => w.count))
|
||||||
|
|
||||||
|
wordcloudChart = new ChartJS(wordcloudCanvas.value, {
|
||||||
|
type: "wordCloud" as any,
|
||||||
|
data: {
|
||||||
|
labels: words.map((w) => w.word),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
data: words.map((w) => 10 + (w.count / maxCount) * 50),
|
||||||
|
color: words.map(
|
||||||
|
(_, i) => WORD_COLORS[i % WORD_COLORS.length],
|
||||||
|
),
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label(context: any) {
|
||||||
|
const word = words[context.dataIndex]
|
||||||
|
return word ? `${word.word}: ${word.count}次` : ""
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const subOptions = computed<Duration>(() => {
|
||||||
|
const dur =
|
||||||
|
durationOptions.find((it) => it.value === query.duration) ??
|
||||||
|
durationOptions[0]
|
||||||
|
const x = dur.value!.toString().split(":")
|
||||||
|
return { [x[0]]: parseInt(x[1]) }
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleStatistics() {
|
||||||
|
const current = Date.now()
|
||||||
|
const end = formatISO(current)
|
||||||
|
const duration =
|
||||||
|
query.duration === "all"
|
||||||
|
? { end }
|
||||||
|
: { start: formatISO(sub(current, subOptions.value)), end }
|
||||||
|
const res = await getFlowchartStatistics(
|
||||||
|
duration,
|
||||||
|
query.problem,
|
||||||
|
query.username,
|
||||||
|
)
|
||||||
|
Object.assign(data, res.data)
|
||||||
|
await nextTick()
|
||||||
|
renderWordCloud()
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (wordcloudChart) {
|
||||||
|
wordcloudChart.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wordcloud-container {
|
||||||
|
height: 300px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user