完成下载附件,完成统计的页面和接口

This commit is contained in:
zlm133588 2025-04-08 22:53:31 +08:00
parent 30809c3896
commit 80a4e88a62
19 changed files with 811 additions and 346 deletions

View File

@ -36,6 +36,9 @@ pnpm build
4.导出功能的依赖
```shell
npm install xlsx file-saver
5.展示图标的依赖
```shell
npm install echarts
## 技术栈

View File

@ -23,7 +23,8 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
"docx": "^8.5.0",
"echarts": "^5.4.3",
"echarts": "^5.6.0",
"element-plus": "^2.9.7",
"file-saver": "^2.0.5",
"lodash-es": "^4.17.21",
"mockjs": "^1.1.0",
@ -840,6 +841,24 @@
"vue": "^3.0.11"
}
},
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@element-plus/icons-vue": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/@emotion/hash": {
"version": "0.8.0",
"resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
@ -1261,6 +1280,31 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@floating-ui/core": {
"version": "1.6.9",
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.9.tgz",
"integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.6.13",
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.13.tgz",
"integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.6.0",
"@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.9",
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.9.tgz",
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
"license": "MIT"
},
"node_modules/@gradio/client": {
"version": "0.14.0",
"resolved": "https://registry.npmmirror.com/@gradio/client/-/client-0.14.0.tgz",
@ -1523,6 +1567,17 @@
"integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==",
"dev": true
},
"node_modules/@popperjs/core": {
"name": "@sxzz/popperjs-es",
"version": "2.11.7",
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.1.0",
"resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
@ -1608,14 +1663,12 @@
"node_modules/@types/lodash": {
"version": "4.14.202",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
"dev": true
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ=="
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
@ -2796,8 +2849,7 @@
"node_modules/async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
"dev": true
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"node_modules/asynckit": {
"version": "0.4.0",
@ -4090,9 +4142,10 @@
}
},
"node_modules/dayjs": {
"version": "1.11.10",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
"version": "1.11.13",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.3.4",
@ -4416,12 +4469,13 @@
"dev": true
},
"node_modules/echarts": {
"version": "5.4.3",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz",
"integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==",
"version": "5.6.0",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "2.3.0",
"zrender": "5.4.4"
"zrender": "5.6.1"
}
},
"node_modules/editor": {
@ -4451,6 +4505,126 @@
"node": ">=0.10.0"
}
},
"node_modules/element-plus": {
"version": "2.9.7",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.7.tgz",
"integrity": "sha512-6vjZh5SXBncLhUwJGTVKS5oDljfgGMh6J4zVTeAZK3YdMUN76FgpvHkwwFXocpJpMbii6rDYU3sgie64FyPerQ==",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.3.1",
"@floating-ui/dom": "^1.0.1",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.14.182",
"@types/lodash-es": "^4.17.6",
"@vueuse/core": "^9.1.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.13",
"escape-html": "^1.0.3",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2",
"memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/element-plus/node_modules/@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==",
"license": "MIT"
},
"node_modules/element-plus/node_modules/@vueuse/core": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
"license": "MIT",
"dependencies": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.13.0",
"@vueuse/shared": "9.13.0",
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/element-plus/node_modules/@vueuse/core/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/element-plus/node_modules/@vueuse/metadata": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/element-plus/node_modules/@vueuse/shared": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
"license": "MIT",
"dependencies": {
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/element-plus/node_modules/@vueuse/shared/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -4573,8 +4747,7 @@
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dev": true
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
@ -6882,14 +7055,24 @@
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash-unified": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
"license": "MIT",
"peerDependencies": {
"@types/lodash-es": "*",
"lodash": "*",
"lodash-es": "*"
}
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@ -7260,6 +7443,12 @@
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
"license": "MIT"
},
"node_modules/meow": {
"version": "8.1.2",
"resolved": "https://registry.npmmirror.com/meow/-/meow-8.1.2.tgz",
@ -7709,6 +7898,12 @@
"node": ">=0.10.0"
}
},
"node_modules/normalize-wheel-es": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
"license": "BSD-3-Clause"
},
"node_modules/npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz",
@ -11496,9 +11691,10 @@
}
},
"node_modules/zrender": {
"version": "5.4.4",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz",
"integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==",
"version": "5.6.1",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
"license": "BSD-3-Clause",
"dependencies": {
"tslib": "2.3.0"
}

View File

@ -45,7 +45,8 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
"docx": "^8.5.0",
"echarts": "^5.4.3",
"echarts": "^5.6.0",
"element-plus": "^2.9.7",
"file-saver": "^2.0.5",
"lodash-es": "^4.17.21",
"mockjs": "^1.1.0",

File diff suppressed because it is too large Load Diff

View File

@ -91,5 +91,6 @@ export default {
updateSetup:(data)=>request.post('/problem/updateSetup',data),
findStudentSubmit:(data)=>request.post('/problem/findStudentSubmit',data),
addTime:(data)=>request.post('/problem/addTime',data),
findProblemReview:(data)=>request.post('/problem/findProblemReview',data),
findScore:(data)=>request.post('/problem/findScore',data),
}

View File

@ -254,7 +254,7 @@ const onReviewProblem = async (problemid)=>{
await router.push({name:
'AdminProblemStudent',
query:
{classId:ProblemInfoValue.value.classId,problemId:id}})
{classId:ProblemInfoValue.value.classId,problemId:id,courseId:courseId}})
}
// const search=async ()=>{
// // ProblemInfoRef.value?.validate(async (errors) => {

View File

@ -4,7 +4,7 @@
label-placement="left"style="margin-top: 15px;margin-left: 2%">
<n-form
ref="problemInfoRef"
:model="problemInfoValue"
:model="problemReviewValue"
:rules="problemAddRules"
label-placement="left"
label-align="right"
@ -12,40 +12,44 @@
:show-require-mark="false"
>
<div class="exam-name-div">
<div style='display:flex;margin-left: 30px'>
<n-h2
class="show-problem-name">
{{ problemName }}
</n-h2>
{{ problemReviewValue.title }}
</n-h2></div>
<div style='display:flex;margin-left: 30px'>
<n-form-item>{{problemDescription }}</n-form-item> </div>
<n-form-item>{{problemReviewValue.description
}}</n-form-item> </div>
<div flex>
<n-form-item
style='margin-left: 30px'>{{
studentMessage.studentNo}} </n-form-item>
<n-form-item style='margin-left: 30px'>{{studentMessage
.name}}</n-form-item>
problemReviewValue.studentNo}} </n-form-item>
<n-form-item style='margin-left:
30px'>{{problemReviewValue
.chineseName}}</n-form-item>
<n-form-item style='margin-left: 30px'>
{{studentMessage.class}}:</n-form-item>
{{problemReviewValue
.banji}}:</n-form-item>
</div>
<div flex>
<n-form-item style='margin-left: 30px'>
<a :href="docUrl" download="example.doc" target="_blank">
点击这里下载附件
</a></n-form-item>
<n-button type="primary" @click="down" style="cursor: pointer;margin-right: 40px">
下载附件
</n-button></n-form-item>
</div>
<div flex>
<n-form-item style='margin-left:
30px;width: 50%'>
<label>自动评分:{{submitReport
<label>自动评分:{{problemReviewValue
.autoscore}}</label>
</n-form-item>
<n-form-item style='width: 20%'>
<label style='width: 100px'>教师评分:
</label>
<n-input v-model="submitReport.score"
<n-input v-model="problemReviewValue.score"
/>
</n-form-item>
</div>
@ -64,13 +68,13 @@
<n-form-item style='margin-left:
30px;width: 50%'>
<pre
style='white-space: normal;word-wrap: break-word'>{{submitReport.aotocomment }}</pre>
style='white-space: normal;word-wrap: break-word'>{{problemReviewValue.autocomment }}</pre>
</n-form-item>
<n-form-item
style='width: 50%;margin-left: 30px'>
<n-input
type="textarea"
v-model="submitReport.comment"
v-model="problemReviewValue.comment"
rows="6"
:autosize="{ minRows: 6, maxRows: 10 }"
/>
@ -106,7 +110,7 @@
</template>
<script setup>
import {useRouter} from "vue-router";
import { useRoute, useRouter } from 'vue-router'
import {ref} from "vue";
import api from "@/api/admin";
import { useTagsStore } from '@/store'
@ -118,53 +122,86 @@ const tagsStore = useTagsStore()
const path=ref('')
const router = useRouter()
const rules=ref(null)
const route = useRoute()
const { classId,problemId,courseId,studentId,history } = route.query
const docUrl = ref('path/to/your/document.doc'); // 替换为你的 DOC 文件路径
const problemInfoValue = ref({
name: '',
timeFrom: [],
timeTo: [],
allow:true
});
const problemInfoRef = ref(null)
const loading = ref(false)
const descEditorHeight = ref('160px')
const problemName = ref('作业标题')
const problemDescription=ref('题目描述')
const studentMessage=ref({
studentNo:'211310509',
name:'zlm',
class:'c++班级'
})
const submitReport=ref({
const problemReviewValue=ref({
title:'',
description:'',
studentNo:'',
chineseName:'',
banji:'',
autoscore:90,
aotocomment:`角色分为管理员、用户、商家,管理员在后台可以进行用户管理、商家管理、商品类型管理、商品信息管理、商品购买管理、交流信息管理、交流回复管理、留言板管理、系统管理;商家在后台可以进行商品类型管理、商品信息管理、商品购买管理、交流信息管理和交流回复管理;用户在后台可以进行商品购买管理、交流信息管理、交流回复管理、我的收藏管理、留言板管理。`,
autocomment:``,
score:'',
comment:''
comment:'',
attachment:''
})
const cancel = ()=>{
tagsStore.removeTag(path.value)
router.push({path: 'problem-list'})
}
const onAdd = ()=>{
router.push({name: 'AdminProblemPostClass'})
}
const submit =async ()=>{
if(problemInfoValue.value.name!==''){
try {
await api.Addproblem({
...problemInfoValue.value
})
$message.success("添加课程成功")
tagsStore.removeTag(path.value)
await router.push({name: 'AdminProblemList'})
} catch (err) {
tagsStore.removeTag(path.value)
await router.push({name: 'AdminProblemList'})
const down = async() =>{
try {
const res = await api.download({
fileName: problemReviewValue.value.attachment
})
const contentDisposition = res.headers["content-disposition"]
let original_fileName = ""
if(contentDisposition) {
const uri = res.headers["content-disposition"].split("=")[1]
original_fileName = window.decodeURI(uri)
}
}else {
$message.error("作业名称不能为空")
const blob = new Blob([res.data], {type: "application/vnd.ms-excel"});
if(window.navigator && window.navigator.msSaveOrOpenBlob) {
const fileName = original_fileName;
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else{
const fileName = original_fileName;
const elink = document.createElement('a')
document.body.appendChild(elink)
elink.download= fileName;
elink.style.display ="none"
elink.href = window.URL.createObjectURL(blob)
elink.click()
window.URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
}
} catch (err) {
}
}
// const submit =async ()=>{
// if(problemInfoValue.value.name!==''){
// try {
// await api.Addproblem({
// ...problemInfoValue.value
// })
// $message.success("添加课程成功")
// tagsStore.removeTag(path.value)
// await router.push({name: 'AdminProblemList'})
// } catch (err) {
// tagsStore.removeTag(path.value)
// await router.push({name: 'AdminProblemList'})
// }
// }else {
// $message.error("作业名称不能为空")
// }
// }
const queryByPage = async () => {
loading.value = true
try {
const res = await api.findProblemReview({
problemId:problemId,
studentId:studentId
})
problemReviewValue.value=res.data
} catch (err) {
} finally {
loading.value = false
}
}
onMounted(()=>{
path.value=router.currentRoute.value.fullPath
queryByPage()
})
</script>

View File

@ -60,8 +60,8 @@
<n-form-item v-else style='margin-left: 10px;margin-right: 20px'>
<n-button type="primary"
style="margin-right: 10px" @click="exportScores">导出成绩</n-button>
<n-button type="primary"
style="margin-right: 10px" @click="exportAttachments">导出作业附件</n-button>
<!-- <n-button type="primary"-->
<!-- style="margin-right: 10px" @click="exportAttachments">导出作业附件</n-button>-->
<n-button type="primary"
style="margin-right: 10px" @click="tongji">统计</n-button>
</n-form-item> </div>
@ -92,7 +92,9 @@ import {onMounted, reactive, ref, watch} from "vue";
import { saveAs } from 'file-saver';
import * as XLSX from 'xlsx';
import {useRoute, useRouter} from 'vue-router'
import { useUserStore } from '@/store';
import api from "@/api/admin";
const userStore = useUserStore()
import ProblemStudentAction
from '@/views/admin/problem/components/problem-student-action.vue'
import {formatDateTime} from '@/utils';
@ -114,7 +116,7 @@ const submitList =
ref([])
const studentList =
reactive([])
const { classId,problemId, history } = route.query
const { classId,problemId,courseId, history } = route.query
const paginationReactive = reactive({
page: 1,
pageSize: 50,
@ -283,10 +285,9 @@ const onAddTime=async (studentId)=>{
'AdminProblemAddTime', query: {classId:
classId,problemId:problemId,studentId:id}})
}
const onReview=async ()=>{
// const {id} = classId
const id=1
await router.push({name: 'AdminProblemReview', params: {clId: id}, query: {id: clId}})
const onReview=async (studentId)=>{
const {id} = studentId
await router.push({name: 'AdminProblemReview', query:{classId: classId,problemId:problemId,courseId:courseId,studentId:id}})
}
const onDeleteClass = async (classId)=>{
try {
@ -378,7 +379,12 @@ const exportWeijiaoTable = () => {
});
const blob = new Blob([excelBuffer], { type: 'application/octet-stream' });
saveAs(blob, '导出未交名单.xlsx');
};
}
const tongji=async (studentId)=>{
const {id} = studentId
await router.push({name: 'AdminProblemScore',
query:{classId: classId,problemId:problemId,courseId:courseId,studentId:id}})
}
const rowKey = (rowData) => {
return rowData.id
}

View File

@ -0,0 +1,130 @@
<script setup>
import { ref, onMounted, reactive } from 'vue'
import * as echarts from 'echarts';
import api from '@/api/admin'
import {useRoute, useRouter} from 'vue-router'
const route = useRoute()
const loading = ref(false)
const { classId,problemId,courseId, history } = route.query
// 模拟从后端获取的分数数据
const scoreList = ref([])
const selectedStatus=ref('yijiao')
// 统计不同分数段的人数
let scoreRanges =[
{ range: '0-59', count: 0 },
{ range: '60-69', count: 0 },
{ range: '70-79', count: 0 },
{ range: '80-89', count: 0 },
{ range: '90-100', count: 0 },
]
// 获取图表 DOM 元素
const chart = ref(null);
// const studentInfoValue = ref({
// id:0,
// studentNo:'',
// chineseName:'',
// readTime:[],
// submitTime:[],
// submissionStatus:'',
// score:0,
// pageNum:paginationReactive.page,
// pageSize:paginationReactive.pageSize
// });
const queryByPage = async ()=>{
// loading.value = true;
// studentInfoValue.value.pageNum=currentPage
// studentInfoValue.value.pageSize=paginationReactive.pageSize
// const res1=await
// api.findProblemById({id:problemId});
// problem.value=res1.data
const res = await
api.findScore(
{
classId:classId,
problemId:problemId,
selectedStatus:selectedStatus.value,
}
);
const {pageNum, pageSize, total, list} = res.data
// paginationReactive.page = pageNum
// paginationReactive.pageSize = pageSize
// paginationReactive.itemCount = total
scoreList.value =res.data
const scores = scoreList.value.map(score =>
parseFloat(score));
scores.forEach(score => {
if (score < 60) {
scoreRanges[0].count++;
} else if (score < 70) {
scoreRanges[1].count++;
} else if (score < 80) {
scoreRanges[2].count++;
} else if (score < 90) {
scoreRanges[3].count++;
} else {
scoreRanges[4].count++;
}
});
console.log(scores)
console.log(scoreRanges)
const myChart = echarts.init(chart.value);
const option = {
title: {
text: '分数段统计',
left: 'center',
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
xAxis: {
type: 'category',
data: scoreRanges.map(item => item.range),
},
yAxis: {
type: 'value',
name: '人数',
},
series: [
{
name: '人数',
type: 'bar',
data: scoreRanges.map(item => item.count),
itemStyle: {
color: '#5470C6', // 柱状图颜色
},
},
],
};
myChart.setOption(option);
}
// 初始化图表
onMounted(() => {
queryByPage()
});
</script>
<template>
<n-scrollbar x-scrollable style="max-height: 100vh">
<CommonPage>
<div>
<!-- <h1>分数统计</h1>-->
<div ref="chart" style="width: 600px;
height: 400px;margin-left: 300px"></div>
</div>
</CommonPage>
</n-scrollbar>
</template>
<style scoped>
h1 {
text-align: center;
margin-bottom: 20px;
}
</style>

View File

@ -30,6 +30,7 @@ export default {
{
name: 'AdminProblem',
path: 'problem',
isHidden: true,
redirect: '/admin/problem/problem-list',
meta: {
title: '作业管理',
@ -78,6 +79,19 @@ export default {
keepAlive: true,
},
},
{
name: 'AdminProblemScore',
path: 'tongji',
isHidden: true,
component: () => import('@/views/admin/problem/tongji.vue'),
meta: {
title: '统计成绩',
icon: 'ic:baseline-add',
role: ['admin', 'teacher'],
requireAuth: true,
keepAlive: true,
},
},
{
name: 'AdminProblemEdit',
path: 'problem-edit',

View File

@ -284,7 +284,20 @@ public class ProblemController {
return RespBean.ok("获取学生提交报告列表成功!",
dataGrid);
}
@PostMapping("findScore")
public RespBean findScore(@RequestBody PMStudentSubmit pmStudentSubmit){
List<Float> scoreList=
problemServiceI.findScoreList(pmStudentSubmit);
return RespBean.ok("获取分数列表成功!",
scoreList);
}
@PostMapping("findProblemReview")
public RespBean findProblemReview(@RequestBody PMReviewMessage pmReviewMessage){
PMReviewMessage reviewMessage=
problemServiceI.findProblemReview(pmReviewMessage);
return RespBean.ok("获取批阅报告成功!",
reviewMessage);
}
//根据作业id和学生id获取学生当前报告成绩排名
@GetMapping("/getStudentRank")

View File

@ -1,5 +1,6 @@
package com.example.aes.problem.dao;
import com.example.aes.problem.model.PMReviewMessage;
import com.example.aes.problem.model.PMStudentSubmit;
import com.example.aes.problem.model.SubmitReport;
import com.example.aes.user.dao.BaseDaoI;
@ -11,5 +12,7 @@ public interface SubmitReportDaoI extends BaseDaoI<SubmitReport> {
public List<Object[]> findWeijiaoList(PMStudentSubmit pmStudentSubmit);
public List<Object[]> findYijiaoList(PMStudentSubmit pmStudentSubmit);
public int updateAddTime(LocalDateTime addTime,int problemId,int studentId);
public List<Object[]> findProblemReview(PMReviewMessage pmReviewMessage);
public List<Object[]> findScoreList(PMStudentSubmit pmStudentSubmit);
}

View File

@ -12,7 +12,6 @@ import com.example.aes.user.dao.impl.BaseDaoImpl;
import com.example.aes.user.model.Classstudents;
import org.hibernate.Query;
import org.hibernate.Session;
import com.ibm.jvm.trace.format.api.TraceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;

View File

@ -1,6 +1,7 @@
package com.example.aes.problem.dao.impl;
import com.example.aes.problem.dao.SubmitReportDaoI;
import com.example.aes.problem.model.PMReviewMessage;
import com.example.aes.problem.model.PMStudentSubmit;
import com.example.aes.problem.model.SubmitReport;
import com.example.aes.user.dao.impl.BaseDaoImpl;
@ -122,4 +123,67 @@ String chineseName=
return query.getResultList();
}
@Override
public List<Object[]> findScoreList(PMStudentSubmit pmStudentSubmit){
int classId= pmStudentSubmit.getClassId();
int problemId= pmStudentSubmit.getProblemId();
// String studentNo=
// '%'+pmStudentSubmit.getStudentNo()+'%';
// String chineseName=
// '%'+pmStudentSubmit.getChineseName()+'%';
String hql="SELECT u.chineseName," +
"u.studentNo, sr" +
".submissionTime," +
"sr.submissionStatus," +
" COALESCE(sr.score," +
" sr.autoscore) " +
"AS finalScore ,u.id " +
"FROM Classstudents cs left " +
"join Users u on cs" +
".userId=u.id "+
"left join PostProblem pp on cs" +
".classId=pp.classId "+
"left JOIN"+
" SubmitReport sr ON " +
"sr" +
".studentId=u.id"+
" WHERE cs.classId=:classId " +
"and " +
"pp.problemId " +
"=:problemId" +
" and sr.problemId=:problemId" +
" and sr" +
".submissionTime is not null";
Query<Object[]> query =
this.getCurrentSession().createQuery(hql,Object[].class);
query.setParameter("classId", classId);
query.setParameter("problemId", problemId);
return query.getResultList();
}
@Override
public List<Object[]> findProblemReview(PMReviewMessage pmReviewMessage){
int studentId= pmReviewMessage.getStudentId();
int problemId= pmReviewMessage.getProblemId();
String hql="select p.title," +
"p.description,u.chineseName," +
"u.studentNo,u.banji," +
"sr.attachment," +
"sr.autoscore,sr.autocomment," +
"sr.score," +
"sr.comment " +
"from Problem p " +
"left join SubmitReport sr " +
"on sr.problemId=p.id " +
"inner join Users u " +
"on sr.studentId=u.id " +
"where u.id=:studentId " +
"and" +
" " +
"p.id=:problemId";
Query<Object[]> query =
this.getCurrentSession().createQuery(hql,Object[].class);
query.setParameter("studentId", studentId);
query.setParameter("problemId", problemId);
return query.getResultList();
}
}

View File

@ -0,0 +1,19 @@
package com.example.aes.problem.model;
import lombok.Data;
@Data
public class PMReviewMessage {
private int studentId;
private int problemId;
private String title;
private String description;
private String chineseName;
private String studentNo;
private String banji;
private String attachment;
private float autoscore;
private String autocomment;
private float score;
private String comment;
}

View File

@ -367,6 +367,14 @@ public class ProblemServiceImpl implements ProblemServiceI {
postProblemDao.findPostProblem(problemId,classId);
return postProblems;
}
@Override
public PMReviewMessage findProblemReview(PMReviewMessage pmReviewMessage){
List<Object[]> objects=
studentSubmitDao.findProblemReview(pmReviewMessage);
List<PMReviewMessage> pmReviewMessageList=new ArrayList<>();
changeModel6(pmReviewMessageList,objects);
return pmReviewMessageList.get(0);
}
@Autowired
public void setStudentSubmitDao(SubmitReportDaoI studentSubmitDao) {
this.studentSubmitDao = studentSubmitDao;
@ -447,6 +455,15 @@ public class ProblemServiceImpl implements ProblemServiceI {
}
}
@Override
public List<Float> findScoreList(PMStudentSubmit pmStudentSubmit){
List<Float> scoreList=new ArrayList<>();
List<Object[]> theSubmitList=
studentSubmitDao.findScoreList(pmStudentSubmit);
for(Object[] object:theSubmitList){
scoreList.add((float)object[4]);
}return scoreList;
}
@Override
public int editPostProblem(int classId,
int problemId,LocalDateTime startTime,LocalDateTime endTime,boolean allow){
int n=0;
@ -549,4 +566,25 @@ public class ProblemServiceImpl implements ProblemServiceI {
}
}
}
private void changeModel6(List<PMReviewMessage> pmReviewMessages,
List<Object[]> pmObject) {
if (pmObject != null && pmObject.size() > 0) {
for (Object[] p : pmObject) {
PMReviewMessage pmReviewMessage=
new PMReviewMessage();
pmReviewMessage.setTitle((String) p[0]);
pmReviewMessage.setDescription(((String) p[1]).replaceAll("<\\/?.*?>", "").trim());
pmReviewMessage.setChineseName((String) p[2]);
pmReviewMessage.setStudentNo((String) p[3]);
pmReviewMessage.setBanji((String) p[4]);
pmReviewMessage.setAttachment((String) p[5]);
pmReviewMessage.setAutoscore((float) p[6]);
pmReviewMessage.setAutocomment((String)p[7]);
if(p[8]!=null)
pmReviewMessage.setScore((float)p[8]);
pmReviewMessage.setComment((String) p[9]);
pmReviewMessages.add(pmReviewMessage);
}
}
}
}

View File

@ -49,5 +49,7 @@ public interface ProblemServiceI {
LocalDateTime startTime,LocalDateTime endTime,boolean allow);
public DataGrid findWeijiaoList(PMStudentSubmit pmStudentSubmit);
public DataGrid findYijiaoList(PMStudentSubmit pmStudentSubmit);
public PMReviewMessage findProblemReview(PMReviewMessage pmReviewMessage);
public int editSubmitReport(LocalDateTime addTime,int problemId,int studentId);
public List<Float> findScoreList(PMStudentSubmit pmStudentSubmit);
}

View File

@ -274,7 +274,6 @@ public class importClassStudentsFileController{
}
}
@Transactional
public boolean decodeFile(String fileName, int classId) // 解析csv文件
{

View File

@ -5,7 +5,7 @@ server.port=8082
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
jdbc_url=jdbc:mysql://localhost:3306/aes?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=True
jdbc_username=root
jdbc_password=13661744518xh
jdbc_password=123456
#spring.datasource.url=jdbc:mysql://localhost:3306/aes?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=True
#spring.datasource.username=root
#spring.datasource.password=123456