新增用户和题目的数据展示

This commit is contained in:
2025-04-13 23:35:47 +08:00
parent 9a4cbe97a8
commit 316b39e1e9
7 changed files with 313 additions and 271 deletions

353
package-lock.json generated
View File

@@ -10,7 +10,7 @@
"dependencies": { "dependencies": {
"@codemirror/lang-cpp": "^6.0.2", "@codemirror/lang-cpp": "^6.0.2",
"@codemirror/lang-python": "^6.1.7", "@codemirror/lang-python": "^6.1.7",
"@vueuse/core": "^13.0.0", "@vueuse/core": "^13.1.0",
"@wangeditor-next/editor": "^5.6.34", "@wangeditor-next/editor": "^5.6.34",
"@wangeditor-next/editor-for-vue": "^5.1.14", "@wangeditor-next/editor-for-vue": "^5.1.14",
"axios": "^1.8.4", "axios": "^1.8.4",
@@ -22,7 +22,7 @@
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"naive-ui": "^2.41.0", "naive-ui": "^2.41.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"pinia": "^3.0.1", "pinia": "^3.0.2",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-chartjs": "^5.3.2", "vue-chartjs": "^5.3.2",
"vue-codemirror": "^6.1.1", "vue-codemirror": "^6.1.1",
@@ -30,17 +30,17 @@
}, },
"devDependencies": { "devDependencies": {
"@iconify/vue": "^4.3.0", "@iconify/vue": "^4.3.0",
"@shikijs/markdown-it": "^3.2.1", "@shikijs/markdown-it": "^3.2.2",
"@types/canvas-confetti": "^1.9.0", "@types/canvas-confetti": "^1.9.0",
"@types/node": "^22.13.11", "@types/node": "^22.14.1",
"@vitejs/plugin-vue": "^5.2.3", "@vitejs/plugin-vue": "^5.2.3",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0", "prettier-plugin-organize-imports": "^4.1.0",
"typescript": "^5.8.2", "typescript": "^5.8.3",
"unplugin-auto-import": "^19.1.1", "unplugin-auto-import": "^19.1.2",
"unplugin-vue-components": "^28.4.1", "unplugin-vue-components": "^28.4.1",
"unplugin-vue-markdown": "^28.3.1", "unplugin-vue-markdown": "^28.3.1",
"vite": "^6.2.2", "vite": "^6.2.6",
"vue-tsc": "^2.2.8" "vue-tsc": "^2.2.8"
} }
}, },
@@ -60,15 +60,16 @@
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.24.7", "version": "7.26.2",
"resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"@babel/highlight": "^7.24.7", "@babel/helper-validator-identifier": "^7.25.9",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0" "picocolors": "^1.0.0"
}, },
"engines": { "engines": {
@@ -251,18 +252,18 @@
} }
}, },
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.24.8", "version": "7.25.9",
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
"integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-identifier": { "node_modules/@babel/helper-validator-identifier": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
"integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -281,46 +282,28 @@
} }
}, },
"node_modules/@babel/helpers": { "node_modules/@babel/helpers": {
"version": "7.25.6", "version": "7.27.0",
"resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.25.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
"integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"@babel/template": "^7.25.0", "@babel/template": "^7.27.0",
"@babel/types": "^7.25.6" "@babel/types": "^7.27.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
"version": "7.24.7",
"resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.24.7.tgz",
"integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.24.7",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.25.6", "version": "7.27.0",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.25.6.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
"integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.25.6" "@babel/types": "^7.27.0"
}, },
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
@@ -330,9 +313,9 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.26.9", "version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
"integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
@@ -353,17 +336,17 @@
} }
}, },
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.25.0", "version": "7.27.0",
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.25.0.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
"integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.24.7", "@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.25.0", "@babel/parser": "^7.27.0",
"@babel/types": "^7.25.0" "@babel/types": "^7.27.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -391,14 +374,13 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.25.6", "version": "7.27.0",
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.25.6.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.24.8", "@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.24.7", "@babel/helper-validator-identifier": "^7.25.9"
"to-fast-properties": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1548,60 +1530,60 @@
] ]
}, },
"node_modules/@shikijs/core": { "node_modules/@shikijs/core": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.2.2.tgz",
"integrity": "sha512-FhsdxMWYu/C11sFisEp7FMGBtX/OSSbnXZDMBhGuUDBNTdsoZlMSgQv5f90rwvzWAdWIW6VobD+G3IrazxA6dQ==", "integrity": "sha512-yvlSKVMLjddAGBa2Yu+vUZxuu3sClOWW1AG+UtJkvejYuGM5BVL35s6Ijiwb75O9QdEx6IkMxinHZSi8ZyrBaA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "3.2.1", "@shikijs/types": "3.2.2",
"@shikijs/vscode-textmate": "^10.0.2", "@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4", "@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.5" "hast-util-to-html": "^9.0.5"
} }
}, },
"node_modules/@shikijs/engine-javascript": { "node_modules/@shikijs/engine-javascript": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.2.2.tgz",
"integrity": "sha512-eMdcUzN3FMQYxOmRf2rmU8frikzoSHbQDFH2hIuXsrMO+IBOCI9BeeRkCiBkcLDHeRKbOCtYMJK3D6U32ooU9Q==", "integrity": "sha512-tlDKfhWpF4jKLUyVAnmL+ggIC+0VyteNsUpBzh1iwWLZu4i+PelIRr0TNur6pRRo5UZIv3ss/PLMuwahg9S2hg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "3.2.1", "@shikijs/types": "3.2.2",
"@shikijs/vscode-textmate": "^10.0.2", "@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.1.0" "oniguruma-to-es": "^4.1.0"
} }
}, },
"node_modules/@shikijs/engine-oniguruma": { "node_modules/@shikijs/engine-oniguruma": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.2.2.tgz",
"integrity": "sha512-wZZAkayEn6qu2+YjenEoFqj0OyQI64EWsNR6/71d1EkG4sxEOFooowKivsWPpaWNBu3sxAG+zPz5kzBL/SsreQ==", "integrity": "sha512-vyXRnWVCSvokwbaUD/8uPn6Gqsf5Hv7XwcW4AgiU4Z2qwy19sdr6VGzMdheKKN58tJOOe5MIKiNb901bgcUXYQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "3.2.1", "@shikijs/types": "3.2.2",
"@shikijs/vscode-textmate": "^10.0.2" "@shikijs/vscode-textmate": "^10.0.2"
} }
}, },
"node_modules/@shikijs/langs": { "node_modules/@shikijs/langs": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.2.2.tgz",
"integrity": "sha512-If0iDHYRSGbihiA8+7uRsgb1er1Yj11pwpX1c6HLYnizDsKAw5iaT3JXj5ZpaimXSWky/IhxTm7C6nkiYVym+A==", "integrity": "sha512-NY0Urg2dV9ETt3JIOWoMPuoDNwte3geLZ4M1nrPHbkDS8dWMpKcEwlqiEIGqtwZNmt5gKyWpR26ln2Bg2ecPgw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "3.2.1" "@shikijs/types": "3.2.2"
} }
}, },
"node_modules/@shikijs/markdown-it": { "node_modules/@shikijs/markdown-it": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/markdown-it/-/markdown-it-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@shikijs/markdown-it/-/markdown-it-3.2.2.tgz",
"integrity": "sha512-G4lz018Lth1y8xbYMgOUgl2E3xvITlEiXgMTNUhei9e5idWDLwYHz0bVU5u53nyWakUdCIwMM60ibseNNqo0IQ==", "integrity": "sha512-abxppHBxksFKhAHn/nM/VAktZVMOtigPgWFuokENJ0jPAoqMs4Xn7zMCjizftgld0B+JbM7IGGJsC2qaP4j0OQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"shiki": "3.2.1" "shiki": "3.2.2"
}, },
"peerDependencies": { "peerDependencies": {
"markdown-it-async": "^2.2.0" "markdown-it-async": "^2.2.0"
@@ -1613,19 +1595,19 @@
} }
}, },
"node_modules/@shikijs/themes": { "node_modules/@shikijs/themes": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.2.2.tgz",
"integrity": "sha512-k5DKJUT8IldBvAm8WcrDT5+7GA7se6lLksR+2E3SvyqGTyFMzU2F9Gb7rmD+t+Pga1MKrYFxDIeyWjMZWM6uBQ==", "integrity": "sha512-Zuq4lgAxVKkb0FFdhHSdDkALuRpsj1so1JdihjKNQfgM78EHxV2JhO10qPsMrm01FkE3mDRTdF68wfmsqjt6HA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "3.2.1" "@shikijs/types": "3.2.2"
} }
}, },
"node_modules/@shikijs/types": { "node_modules/@shikijs/types": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.2.2.tgz",
"integrity": "sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==", "integrity": "sha512-a5TiHk7EH5Lso8sHcLHbVNNhWKP0Wi3yVnXnu73g86n3WoDgEra7n3KszyeCGuyoagspQ2fzvy4cpSc8pKhb0A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1744,13 +1726,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.13.11", "version": "22.14.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
"integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.20.0" "undici-types": "~6.21.0"
} }
}, },
"node_modules/@types/unist": { "node_modules/@types/unist": {
@@ -2044,14 +2026,14 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vueuse/core": { "node_modules/@vueuse/core": {
"version": "13.0.0", "version": "13.1.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.0.0.tgz", "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.1.0.tgz",
"integrity": "sha512-rkgb4a8/0b234lMGCT29WkCjPfsX0oxrIRR7FDndRoW3FsaC9NBzefXg/9TLhAgwM11f49XnutshM4LzJBrQ5g==", "integrity": "sha512-PAauvdRXZvTWXtGLg8cPUFjiZEddTqmogdwYpnn60t08AA5a8Q4hZokBnpTOnVNqySlFlTcRYIC8OqreV4hv3Q==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/web-bluetooth": "^0.0.21", "@types/web-bluetooth": "^0.0.21",
"@vueuse/metadata": "13.0.0", "@vueuse/metadata": "13.1.0",
"@vueuse/shared": "13.0.0" "@vueuse/shared": "13.1.0"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
@@ -2061,18 +2043,18 @@
} }
}, },
"node_modules/@vueuse/metadata": { "node_modules/@vueuse/metadata": {
"version": "13.0.0", "version": "13.1.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.0.0.tgz", "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.1.0.tgz",
"integrity": "sha512-TRNksqmvtvqsuHf7bbgH9OSXEV2b6+M3BSN4LR5oxWKykOFT9gV78+C2/0++Pq9KCp9KQ1OQDPvGlWNQpOb2Mw==", "integrity": "sha512-+TDd7/a78jale5YbHX9KHW3cEDav1lz1JptwDvep2zSG8XjCsVE+9mHIzjTOaPbHUAk5XiE4jXLz51/tS+aKQw==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
} }
}, },
"node_modules/@vueuse/shared": { "node_modules/@vueuse/shared": {
"version": "13.0.0", "version": "13.1.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.0.0.tgz", "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.1.0.tgz",
"integrity": "sha512-9MiHhAPw+sqCF/RLo8V6HsjRqEdNEWVpDLm2WBRW2G/kSQjb8X901sozXpSCaeLG0f7TEfMrT4XNaA5m1ez7Dg==", "integrity": "sha512-IVS/qRRjhPTZ6C2/AM3jieqXACGwFZwWTdw5sNTSKk2m/ZpkuuN+ri+WCVUP8TqaKwJYt/KuMwmXspMAw8E6ew==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
@@ -2260,9 +2242,9 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.14.0", "version": "8.14.1",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@@ -2279,21 +2261,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/anymatch": { "node_modules/anymatch": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
@@ -2493,35 +2460,6 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/chalk/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/character-entities-html4": { "node_modules/character-entities-html4": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
@@ -2616,27 +2554,6 @@
"@codemirror/view": "^6.0.0" "@codemirror/view": "^6.0.0"
} }
}, },
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -3345,18 +3262,6 @@
"node": ">=6.0" "node": ">=6.0"
} }
}, },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=4"
}
},
"node_modules/hash-sum": { "node_modules/hash-sum": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz",
@@ -3612,7 +3517,7 @@
}, },
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
@@ -4424,9 +4329,9 @@
} }
}, },
"node_modules/pinia": { "node_modules/pinia": {
"version": "3.0.1", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.1.tgz", "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.2.tgz",
"integrity": "sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg==", "integrity": "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/devtools-api": "^7.7.2" "@vue/devtools-api": "^7.7.2"
@@ -4797,18 +4702,18 @@
} }
}, },
"node_modules/shiki": { "node_modules/shiki": {
"version": "3.2.1", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.2.1.tgz", "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.2.2.tgz",
"integrity": "sha512-VML/2o1/KGYkEf/stJJ+s9Ypn7jUKQPomGLGYso4JJFMFxVDyPNsjsI3MB3KLjlMOeH44gyaPdXC6rik2WXvUQ==", "integrity": "sha512-0qWBkM2t/0NXPRcVgtLhtHv6Ak3Q5yI4K/ggMqcgLRKm4+pCs3namgZlhlat/7u2CuqNtlShNs9lENOG6n7UaQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/core": "3.2.1", "@shikijs/core": "3.2.2",
"@shikijs/engine-javascript": "3.2.1", "@shikijs/engine-javascript": "3.2.2",
"@shikijs/engine-oniguruma": "3.2.1", "@shikijs/engine-oniguruma": "3.2.2",
"@shikijs/langs": "3.2.1", "@shikijs/langs": "3.2.2",
"@shikijs/themes": "3.2.1", "@shikijs/themes": "3.2.2",
"@shikijs/types": "3.2.1", "@shikijs/types": "3.2.2",
"@shikijs/vscode-textmate": "^10.0.2", "@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4" "@types/hast": "^3.0.4"
} }
@@ -5026,21 +4931,6 @@
"node": ">=16" "node": ">=16"
} }
}, },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/tar": { "node_modules/tar": {
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
@@ -5132,14 +5022,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"engines": {
"node": ">=4"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -5175,9 +5057,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.8.2", "version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@@ -5227,9 +5109,9 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.20.0", "version": "6.21.0",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -5388,17 +5270,17 @@
} }
}, },
"node_modules/unplugin-auto-import": { "node_modules/unplugin-auto-import": {
"version": "19.1.1", "version": "19.1.2",
"resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-19.1.1.tgz", "resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-19.1.2.tgz",
"integrity": "sha512-sCGZZrSR1Bc8RfN8Q0RUDxXtC20rdAt7UB4lDyq8MNtKVHiXXh+5af6Nz4JRp9Q+7HjnbgQfQox0TkEymjdUAQ==", "integrity": "sha512-EkxNIJm4ZPYtV7rRaPBKnsscgTaifIZNrJF5DkMffTxkUOJOlJuKVypA6YBSBOjzPJDTFPjfVmCQPoBuOO+YYQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"local-pkg": "^1.0.0", "local-pkg": "^1.1.1",
"magic-string": "^0.30.17", "magic-string": "^0.30.17",
"picomatch": "^4.0.2", "picomatch": "^4.0.2",
"unimport": "^4.1.2", "unimport": "^4.1.2",
"unplugin": "^2.2.0", "unplugin": "^2.2.2",
"unplugin-utils": "^0.2.4" "unplugin-utils": "^0.2.4"
}, },
"engines": { "engines": {
@@ -5553,13 +5435,14 @@
} }
}, },
"node_modules/unplugin-auto-import/node_modules/unplugin": { "node_modules/unplugin-auto-import/node_modules/unplugin": {
"version": "2.2.0", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.2.0.tgz", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.2.tgz",
"integrity": "sha512-m1ekpSwuOT5hxkJeZGRxO7gXbXT3gF26NjQ7GdVHoLoF8/nopLcd/QfPigpCy7i51oFHiRJg/CyHhj4vs2+KGw==", "integrity": "sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"acorn": "^8.14.0", "acorn": "^8.14.1",
"picomatch": "^4.0.2",
"webpack-virtual-modules": "^0.6.2" "webpack-virtual-modules": "^0.6.2"
}, },
"engines": { "engines": {
@@ -5830,9 +5713,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.2.2", "version": "6.2.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
"integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"@codemirror/lang-cpp": "^6.0.2", "@codemirror/lang-cpp": "^6.0.2",
"@codemirror/lang-python": "^6.1.7", "@codemirror/lang-python": "^6.1.7",
"@vueuse/core": "^13.0.0", "@vueuse/core": "^13.1.0",
"@wangeditor-next/editor": "^5.6.34", "@wangeditor-next/editor": "^5.6.34",
"@wangeditor-next/editor-for-vue": "^5.1.14", "@wangeditor-next/editor-for-vue": "^5.1.14",
"axios": "^1.8.4", "axios": "^1.8.4",
@@ -23,7 +23,7 @@
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"naive-ui": "^2.41.0", "naive-ui": "^2.41.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"pinia": "^3.0.1", "pinia": "^3.0.2",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-chartjs": "^5.3.2", "vue-chartjs": "^5.3.2",
"vue-codemirror": "^6.1.1", "vue-codemirror": "^6.1.1",
@@ -31,17 +31,17 @@
}, },
"devDependencies": { "devDependencies": {
"@iconify/vue": "^4.3.0", "@iconify/vue": "^4.3.0",
"@shikijs/markdown-it": "^3.2.1", "@shikijs/markdown-it": "^3.2.2",
"@types/canvas-confetti": "^1.9.0", "@types/canvas-confetti": "^1.9.0",
"@types/node": "^22.13.11", "@types/node": "^22.14.1",
"@vitejs/plugin-vue": "^5.2.3", "@vitejs/plugin-vue": "^5.2.3",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0", "prettier-plugin-organize-imports": "^4.1.0",
"typescript": "^5.8.2", "typescript": "^5.8.3",
"unplugin-auto-import": "^19.1.1", "unplugin-auto-import": "^19.1.2",
"unplugin-vue-components": "^28.4.1", "unplugin-vue-components": "^28.4.1",
"unplugin-vue-markdown": "^28.3.1", "unplugin-vue-markdown": "^28.3.1",
"vite": "^6.2.2", "vite": "^6.2.6",
"vue-tsc": "^2.2.8" "vue-tsc": "^2.2.8"
} }
} }

2
src/components.d.ts vendored
View File

@@ -34,6 +34,8 @@ declare module 'vue' {
NGrid: typeof import('naive-ui')['NGrid'] NGrid: typeof import('naive-ui')['NGrid']
NH1: typeof import('naive-ui')['NH1'] NH1: typeof import('naive-ui')['NH1']
NH2: typeof import('naive-ui')['NH2'] NH2: typeof import('naive-ui')['NH2']
NH3: typeof import('naive-ui')['NH3']
NH4: typeof import('naive-ui')['NH4']
NIcon: typeof import('naive-ui')['NIcon'] NIcon: typeof import('naive-ui')['NIcon']
NInput: typeof import('naive-ui')['NInput'] NInput: typeof import('naive-ui')['NInput']
NLayout: typeof import('naive-ui')['NLayout'] NLayout: typeof import('naive-ui')['NLayout']

View File

@@ -70,6 +70,10 @@ export function getProblem(problemID: string, contestID: string) {
}) })
} }
export function getProblemBeatRate(problemID: number) {
return http.get("problem/beat_count", { params: { problem_id: problemID } })
}
export function getSubmission(id: string) { export function getSubmission(id: string) {
return http.get<Submission>("submission", { return http.get<Submission>("submission", {
params: { id }, params: { id },
@@ -220,3 +224,7 @@ export function getCommentStatistics(problemID: number) {
export function refreshUserProblemDisplayIds() { export function refreshUserProblemDisplayIds() {
return http.get("profile/fresh_display_id") return http.get("profile/fresh_display_id")
} }
export function getMetrics(userid: number) {
return http.get("metrics", { params: { userid } })
}

View File

@@ -1,10 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { Icon } from "@iconify/vue"
import { problem } from "oj/composables/problem" import { problem } from "oj/composables/problem"
import { DIFFICULTY, JUDGE_STATUS } from "utils/constants" import { DIFFICULTY, JUDGE_STATUS } from "utils/constants"
import { getACRate, getTagColor, parseTime } from "utils/functions" import { getACRate, getTagColor, parseTime } from "utils/functions"
import { Pie } from "vue-chartjs" import { Pie } from "vue-chartjs"
import { getProblemBeatRate } from "~/oj/api"
import { isDesktop } from "~/shared/composables/breakpoints" import { isDesktop } from "~/shared/composables/breakpoints"
const beatRate = ref("0%")
const data = computed(() => { const data = computed(() => {
const status = problem.value!.statistic_info const status = problem.value!.statistic_info
const labels = [] const labels = []
@@ -22,11 +26,46 @@ const data = computed(() => {
} }
}) })
const numbers = computed(() => {
return [
{
icon: "streamline-emojis:scroll",
title: problem.value?.submission_number ?? 0,
content: "总提交",
},
{
icon: "streamline-emojis:woman-raising-hand-2",
title: problem.value?.accepted_number ?? 0,
content: "通过数",
},
{
icon: "emojione:chart-increasing",
title: getACRate(
problem.value?.accepted_number ?? 0,
problem.value?.submission_number ?? 0,
),
content: "通过率",
},
{
icon: "streamline-emojis:sparkles",
title: beatRate.value,
content: "击败用户",
},
]
})
const options = { const options = {
plugins: { plugins: {
title: { text: "提交结果的比例", display: true, font: { size: 20 } }, title: { text: "提交结果的比例", display: true, font: { size: 20 } },
}, },
} }
async function getBeatRate() {
const res = await getProblemBeatRate(problem.value!.id)
beatRate.value = res.data
}
onMounted(getBeatRate)
</script> </script>
<template> <template>
@@ -50,34 +89,45 @@ const options = {
{{ DIFFICULTY[problem.difficulty] }} {{ DIFFICULTY[problem.difficulty] }}
</n-tag> </n-tag>
</n-descriptions-item> </n-descriptions-item>
<n-descriptions-item label="时间限制"> <n-descriptions-item :span="2" label="标签">
{{ problem.time_limit }}毫秒
</n-descriptions-item>
<n-descriptions-item label="内存限制">
{{ problem.memory_limit }}MB
</n-descriptions-item>
<n-descriptions-item label="提交正确">
{{ problem.accepted_number }}
</n-descriptions-item>
<n-descriptions-item label="提交错误">
{{ problem.submission_number - problem.accepted_number }}
</n-descriptions-item>
<n-descriptions-item label="正确率">
{{ getACRate(problem.accepted_number, problem.submission_number) }}
</n-descriptions-item>
<n-descriptions-item :span="3" label="标签">
<n-flex> <n-flex>
<n-tag size="small" type="info" v-for="tag in problem.tags" :key="tag"> <n-tag type="info" v-for="tag in problem.tags" :key="tag">
{{ tag }} {{ tag }}
</n-tag> </n-tag>
</n-flex> </n-flex>
</n-descriptions-item> </n-descriptions-item>
</n-descriptions> </n-descriptions>
<n-grid :cols="isDesktop ? 4 : 2" :x-gap="10" :y-gap="10" class="cards">
<n-gi v-for="item in numbers" :key="item.title">
<n-card hoverable>
<n-flex align="center">
<Icon :icon="item.icon" width="40" />
<div>
<n-h2 class="number">{{ item.title }}</n-h2>
<n-h4 class="number-label">{{ item.content }}</n-h4>
</div>
</n-flex>
</n-card>
</n-gi>
</n-grid>
<div class="pie" v-if="problem && problem.submission_number > 0"> <div class="pie" v-if="problem && problem.submission_number > 0">
<Pie :data="data" :options="options" /> <Pie :data="data" :options="options" />
</div> </div>
</template> </template>
<style scoped> <style scoped>
.cards {
margin-top: 24px;
}
.number {
margin-bottom: 0;
font-weight: bold;
}
.number-label {
margin: 0;
}
.pie { .pie {
width: 100%; width: 100%;
max-width: 500px; max-width: 500px;

View File

@@ -1,11 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { Icon } from "@iconify/vue"
import { NH2, NH3 } from "naive-ui"
import { getProfile } from "~/shared/api" import { getProfile } from "~/shared/api"
import { isDesktop } from "~/shared/composables/breakpoints"
import { durationToDays, parseTime } from "~/utils/functions"
import { Profile } from "~/utils/types" import { Profile } from "~/utils/types"
import { getMetrics } from "../api"
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const profile = ref<Profile | null>(null) const profile = ref<Profile | null>(null)
const problems = ref<string[]>([]) const problems = ref<string[]>([])
const firstSubmissionAt = ref("")
const latestSubmissionAt = ref("")
const toLatestAt = ref("")
const learnDuration = ref("")
const [loading, toggle] = useToggle() const [loading, toggle] = useToggle()
async function init() { async function init() {
@@ -25,11 +34,60 @@ async function init() {
} }
ac.sort() ac.sort()
problems.value = ac problems.value = ac
if (profile.value.submission_number > 0) {
const metricsRes = await getMetrics(profile.value.user.id)
firstSubmissionAt.value = parseTime(metricsRes.data.first)
latestSubmissionAt.value = parseTime(metricsRes.data.latest)
toLatestAt.value = durationToDays(
metricsRes.data.latest,
metricsRes.data.now,
)
learnDuration.value = durationToDays(
metricsRes.data.first,
metricsRes.data.latest,
)
}
} finally { } finally {
toggle(false) toggle(false)
} }
} }
const metrics = computed(() => {
if (loading.value) return []
return [
{
icon: "fluent-emoji:face-with-peeking-eye",
title: learnDuration.value ?? "1天",
content: "总共学习天数",
},
{
icon: "fluent-emoji:cheese-wedge",
title: toLatestAt.value,
content: "距离上次提交",
},
{
icon: "fluent-emoji:dog-face",
title: latestSubmissionAt.value,
content: "最新一次提交时间",
},
{
icon: "fluent-emoji:cat-with-wry-smile",
title: firstSubmissionAt.value,
content: "第一次提交时间",
},
{
icon: "fluent-emoji:candy",
title: profile.value?.accepted_number ?? 0,
content: "已解决的题目数量",
},
{
icon: "fluent-emoji:thinking-face",
title: profile.value?.submission_number ?? 0,
content: "总提交数量",
},
]
})
onMounted(init) onMounted(init)
</script> </script>
<template> <template>
@@ -44,20 +102,31 @@ onMounted(init)
<h2>{{ profile.user.username }}</h2> <h2>{{ profile.user.username }}</h2>
<p class="desc">{{ profile.mood }}</p> <p class="desc">{{ profile.mood }}</p>
</n-flex> </n-flex>
<n-descriptions
v-if="!loading && profile" <n-grid
v-if="profile && profile.submission_number > 0"
class="wrapper" class="wrapper"
bordered :cols="2"
:column="2" :x-gap="10"
label-style="width: 50%" :y-gap="10"
> >
<n-descriptions-item label="已解决的题目数量"> <n-gi v-for="item in metrics" :key="item.title">
{{ profile.accepted_number }} <n-card hoverable>
</n-descriptions-item> <n-flex align="center">
<n-descriptions-item label="总提交数"> <Icon v-if="isDesktop" :icon="item.icon" width="50" />
{{ profile.submission_number }} <div>
</n-descriptions-item> <Component :is="isDesktop ? NH2 : NH3" class="number">
<n-descriptions-item v-if="problems.length" label="已解决的题目" :span="2"> {{ item.title }}
</Component>
<n-h4 class="number-label">{{ item.content }}</n-h4>
</div>
</n-flex>
</n-card>
</n-gi>
</n-grid>
<n-descriptions v-if="!loading && profile" class="wrapper" bordered>
<n-descriptions-item v-if="problems.length" label="已解决的题目">
<n-flex> <n-flex>
<n-button <n-button
v-for="id in problems" v-for="id in problems"
@@ -81,6 +150,15 @@ onMounted(init)
margin: 16px auto 0; margin: 16px auto 0;
} }
.number {
margin-bottom: 0;
font-weight: bold;
}
.number-label {
margin: 0;
}
h2 { h2 {
margin: 0; margin: 0;
font-weight: normal; font-weight: normal;

View File

@@ -71,6 +71,27 @@ export function duration(
return result return result
} }
export function durationToDays(
start: Date | string,
end: Date | string,
): string {
const duration = intervalToDuration({
start: getTime(parseISO(start.toString())),
end: getTime(parseISO(end.toString())),
})
let result = ""
if (duration.years) {
result += duration.years + "年"
}
if (duration.months) {
result += duration.months + "月"
}
if (duration.days) {
result += duration.days + "天"
}
return result
}
export function secondsToDuration(seconds: number): string { export function secondsToDuration(seconds: number): string {
const duration = intervalToDuration({ const duration = intervalToDuration({
start: 0, start: 0,