Commit 86201e49a31aabcf2ceea94015f500b285e76a2a
1 parent
53300a7b
即时测模块
Showing
5 changed files
with
1954 additions
and
0 deletions
src/views/basic/test/components/contrast.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div ref="main" class="page-container"> | |
| 3 | + <div class="page-content"> | |
| 4 | + <div class="content-header"> | |
| 5 | + <div class="tab-box"> | |
| 6 | + <span v-for="(item, index) in tabList" :key="item" class="tab-item" :class="type == index ? 'active' : ''" | |
| 7 | + @click="setType(index)">{{ item }}</span> | |
| 8 | + </div> | |
| 9 | + <el-button class="setMinScore" @click="openDia" round size="small">对比成绩等级设置</el-button> | |
| 10 | + </div> | |
| 11 | + <div id="print-content" class="table-box" v-loading="loading"> | |
| 12 | + <el-table :max-height="tableMaxHeight" v-show="type == 0" :data="tableData" border style="width: 100%"> | |
| 13 | + <el-table-column type="index" label="序号" fixed align="center" width="60"></el-table-column> | |
| 14 | + <el-table-column prop="className" label="班级" align="center" fixed></el-table-column> | |
| 15 | + <el-table-column label="测验人数/班级人数" align="center" width="84"> | |
| 16 | + <template slot-scope="scope"> | |
| 17 | + <p v-for="(item, index) in scope.row.count.split('/')"> | |
| 18 | + {{ item }}{{ index == 0 ? "/" : "" }} | |
| 19 | + </p> | |
| 20 | + </template> | |
| 21 | + </el-table-column> | |
| 22 | + <el-table-column prop="percent" label="参与度" align="center"></el-table-column> | |
| 23 | + <el-table-column prop="avg" label="班平均分" align="center"></el-table-column> | |
| 24 | + <el-table-column prop="max" label="班最高分" sortable align="center"></el-table-column> | |
| 25 | + <el-table-column prop="min" label="班最低分" sortable align="center"></el-table-column> | |
| 26 | + | |
| 27 | + <el-table-column v-for="(item, index) in defaultLevels.levels" :label="item[0] + '数(率)'" | |
| 28 | + align="center"><template slot-scope="scoped"> | |
| 29 | + <p class="p1">{{ scoped.row.levels[index].people }}</p> | |
| 30 | + <p class="p1">({{ scoped.row.levels[index].percent }})</p> | |
| 31 | + </template></el-table-column> | |
| 32 | + </el-table> | |
| 33 | + <el-table v-show="type == 1" :max-height="tableMaxHeight" :data="tableData2" border style="width: 100%"> | |
| 34 | + <el-table-column prop="rank" label="排名" sortable align="center"></el-table-column> | |
| 35 | + <el-table-column prop="name" label="姓名" align="center"></el-table-column> | |
| 36 | + <el-table-column prop="className" label="班级" align="center"></el-table-column> | |
| 37 | + <el-table-column prop="score" label="总分" sortable align="center"></el-table-column> | |
| 38 | + <el-table-column prop="levelName" label="成绩等级" align="center"></el-table-column> | |
| 39 | + </el-table> | |
| 40 | + </div> | |
| 41 | + <div class="down"> | |
| 42 | + <div> | |
| 43 | + <el-button v-loading="exportLoading" @click="exportData" type="primary" plain round | |
| 44 | + icon="fa fa-cloud-download">导出报表</el-button> | |
| 45 | + <el-button v-if="!this.$store.getters.code" @click="print" type="primary" plain round | |
| 46 | + icon="el-icon-printer">打印</el-button> | |
| 47 | + </div> | |
| 48 | + </div> | |
| 49 | + | |
| 50 | + <el-dialog :close-on-click-modal="false" title="等级设置" :visible.sync="diaLogBox" width="800px" @closed="closeDia"> | |
| 51 | + <el-form class="use-form"> | |
| 52 | + <el-form-item class="use-form-item-box"> | |
| 53 | + <el-form-item label="等级名称:" class="use-form-item"> | |
| 54 | + <el-select size="small" v-model="fromData.type" @change="changeType"> | |
| 55 | + <el-option label="优良合格不合格" :value="0"></el-option> | |
| 56 | + <el-option label="ABCD" :value="1"></el-option> | |
| 57 | + <el-option label="自定义" :value="2"></el-option> | |
| 58 | + </el-select> | |
| 59 | + </el-form-item> | |
| 60 | + <el-form-item label="等级设置模式:" class="use-form-item"> | |
| 61 | + <el-select size="small" v-model="fromData.levelType"> | |
| 62 | + <el-option label="按分数比例" :value="0"></el-option> | |
| 63 | + <el-option label="按已考人数比例" :value="1"></el-option> | |
| 64 | + </el-select> | |
| 65 | + </el-form-item> | |
| 66 | + </el-form-item> | |
| 67 | + <el-form-item> | |
| 68 | + <div class="dia-tab-box"> | |
| 69 | + <p class="dia-tab-tit"> | |
| 70 | + <span class="item1">编号</span> | |
| 71 | + <span class="item2"><i>*</i>等级名称</span> | |
| 72 | + <span class="item3"><i>*</i>等级最高</span> | |
| 73 | + <span class="item3"><i>*</i>等级最低</span> | |
| 74 | + <span class="item"></span> | |
| 75 | + </p> | |
| 76 | + <div class="dia-tab-item" v-for="(item, index) in fromData.levels"> | |
| 77 | + <span class="item1">{{ index + 1 }}</span> | |
| 78 | + <p class="item2"> | |
| 79 | + <el-input class="score-ipt" v-model="item[0]" :maxlength="12" | |
| 80 | + @keydown.native="keydownRange($event)"></el-input> | |
| 81 | + </p> | |
| 82 | + <p class="item3"> | |
| 83 | + <el-input class="score-ipt" type="number" v-model="item[1]" :min="item[2]" | |
| 84 | + :max="index == 0 ? 100 : fromData.levels[index - 1][2]" | |
| 85 | + @keydown.native="keydownRange($event)"></el-input> | |
| 86 | + % | |
| 87 | + <template v-if="fromData.levelType == 0"> | |
| 88 | + ({{ index != 0 ? "不含" : "" | |
| 89 | + }}{{ | |
| 90 | + Number(((item[1] / 100) * examPaperScore).toFixed(1)) | |
| 91 | +}}分) | |
| 92 | + </template> | |
| 93 | + <template v-else>{{ index != 0 ? "不含" : "" }}</template> | |
| 94 | + </p> | |
| 95 | + <p>~</p> | |
| 96 | + <p class="item3"> | |
| 97 | + <el-input class="score-ipt" type="number" v-model="item[2]" :min="0" :max="item[1]" | |
| 98 | + @keydown.native="keydownRange($event)"></el-input> | |
| 99 | + % | |
| 100 | + <template v-if="fromData.levelType == 0"> | |
| 101 | + ({{ | |
| 102 | + Number(((item[2] / 100) * examPaperScore).toFixed(1)) | |
| 103 | + }}分) | |
| 104 | + </template> | |
| 105 | + </p> | |
| 106 | + <p class="item"> | |
| 107 | + <el-link type="danger" :underline="false" @click="fromData.levels.splice(index, 1)">删除</el-link> | |
| 108 | + </p> | |
| 109 | + </div> | |
| 110 | + <div class="add"> | |
| 111 | + <p @click="fromData.levels.push(['', '', ''])"> | |
| 112 | + <el-button size="mini" icon="el-icon-plus" circle type="primary"></el-button>添加一行 | |
| 113 | + </p> | |
| 114 | + </div> | |
| 115 | + </div> | |
| 116 | + </el-form-item> | |
| 117 | + </el-form> | |
| 118 | + | |
| 119 | + <div class="dialog-footer" slot="footer" align="center"> | |
| 120 | + <el-button type="danger" @click="savefrom">保存</el-button> | |
| 121 | + <el-button @click="diaLogBox = false">取 消</el-button> | |
| 122 | + </div> | |
| 123 | + </el-dialog> | |
| 124 | + </div> | |
| 125 | + </div> | |
| 126 | +</template> | |
| 127 | + | |
| 128 | +<script> | |
| 129 | +import { downloadFile, tablePrint } from "@/utils"; | |
| 130 | +export default { | |
| 131 | + props: { | |
| 132 | + role: "", | |
| 133 | + ids: Array, | |
| 134 | + classId: String, | |
| 135 | + subjectName: String, | |
| 136 | + title: String, | |
| 137 | + }, | |
| 138 | + data() { | |
| 139 | + return { | |
| 140 | + tabList: ["班级对比情况", "学生成绩排名"], | |
| 141 | + type: 0, | |
| 142 | + loading: false, | |
| 143 | + exportLoading: false, | |
| 144 | + diaLogBox: false, | |
| 145 | + fromData: { | |
| 146 | + type: 0, | |
| 147 | + levelType: 0, | |
| 148 | + levels: [ | |
| 149 | + ["优秀", 100, 90], | |
| 150 | + ["良好", 89.9, 70], | |
| 151 | + ["合格", 69.9, 60], | |
| 152 | + ["不合格", 59.9, 0], | |
| 153 | + ], | |
| 154 | + }, | |
| 155 | + defaultLevels: { | |
| 156 | + levelType: 0, | |
| 157 | + levels: [], | |
| 158 | + }, | |
| 159 | + tableMaxHeight: 600, | |
| 160 | + tableData: [], | |
| 161 | + tableData2: [], | |
| 162 | + examPaperScore: 100, //卷面最高分 | |
| 163 | + }; | |
| 164 | + }, | |
| 165 | + async created() { | |
| 166 | + await this._QueryData(); | |
| 167 | + await this._QueryDefaultLevels(); | |
| 168 | + }, | |
| 169 | + destroyed() { | |
| 170 | + sessionStorage.setItem("levelFromData", ""); | |
| 171 | + }, | |
| 172 | + methods: { | |
| 173 | + // 禁止输入负数 | |
| 174 | + keydownRange(event) { | |
| 175 | + if (event.key == "-" || event.key == "e") { | |
| 176 | + event.returnValue = ""; | |
| 177 | + } | |
| 178 | + }, | |
| 179 | + print() { | |
| 180 | + tablePrint( | |
| 181 | + "print-content", | |
| 182 | + `多班_${this.subjectName}_${this.title}_测练成绩对比分析` | |
| 183 | + ); | |
| 184 | + }, | |
| 185 | + setType(type) { | |
| 186 | + console.log(this.$refs.main.offsetHeight - 50); | |
| 187 | + this.tableMaxHeight = this.$refs.main.offsetHeight; | |
| 188 | + this.type = type; | |
| 189 | + }, | |
| 190 | + openDia() { | |
| 191 | + this.diaLogBox = true; | |
| 192 | + }, | |
| 193 | + closeDia() { | |
| 194 | + let levelFromData = sessionStorage.getItem("levelFromData"); | |
| 195 | + if (levelFromData) { | |
| 196 | + levelFromData = JSON.parse(levelFromData); | |
| 197 | + this.fromData.type = levelFromData.type; | |
| 198 | + this.fromData.levelType = levelFromData.levelType; | |
| 199 | + this.fromData.levels = [...levelFromData.levels]; | |
| 200 | + } else { | |
| 201 | + this.fromData.type = 0; | |
| 202 | + this.fromData.levelType = this.defaultLevels.levelType; | |
| 203 | + this.fromData.levels = [...this.defaultLevels.levels]; | |
| 204 | + } | |
| 205 | + }, | |
| 206 | + changeType(val) { | |
| 207 | + if (val == this.defaultLevels.type) { | |
| 208 | + this.fromData.levels = [...this.defaultLevels.levels]; | |
| 209 | + } else { | |
| 210 | + this.fromData.levels = this.fromData.levels.splice(0, 4); | |
| 211 | + if (val == 0) { | |
| 212 | + this.fromData.levels = this.fromData.levels.map((item, index) => { | |
| 213 | + let arrTxt = ["优秀", "良好", "合格", "不合格"]; | |
| 214 | + return [arrTxt[index], item[1], item[2]]; | |
| 215 | + }); | |
| 216 | + } else if (val == 1) { | |
| 217 | + this.fromData.levels = this.fromData.levels.map((item, index) => { | |
| 218 | + let arrTxt = ["A", "B", "C", "D"]; | |
| 219 | + return [arrTxt[index], item[1], item[2]]; | |
| 220 | + }); | |
| 221 | + } else { | |
| 222 | + this.fromData.levels = this.fromData.levels.map((item, index) => { | |
| 223 | + return ["", item[1], item[2]]; | |
| 224 | + }); | |
| 225 | + } | |
| 226 | + } | |
| 227 | + }, | |
| 228 | + savefrom() { | |
| 229 | + for (let i = 0; i < this.fromData.levels.length; i++) { | |
| 230 | + if (this.fromData.levels[i].includes("")) { | |
| 231 | + this.$message.warning("请补全编号" + (i + 1) + "设置信息!"); | |
| 232 | + return; | |
| 233 | + } | |
| 234 | + } | |
| 235 | + if (this.fromData.levels.length == 0) { | |
| 236 | + this.$message.warning("请添加等级设置!"); | |
| 237 | + return; | |
| 238 | + } | |
| 239 | + let nums = []; | |
| 240 | + let ERR_OK = false; | |
| 241 | + this.fromData.levels.map((item) => { | |
| 242 | + nums.push(Number(item[1])); | |
| 243 | + nums.push(Number(item[2])); | |
| 244 | + }); | |
| 245 | + for (let i = 0; i < nums.length; i++) { | |
| 246 | + if (nums[i + 1] && nums[i + 1] > nums[i]) { | |
| 247 | + ERR_OK = true; | |
| 248 | + this.$message.warning("高等级比例不能低于低等级比例!请检查"); | |
| 249 | + break; | |
| 250 | + } | |
| 251 | + } | |
| 252 | + if (ERR_OK) return; | |
| 253 | + this.tableData = []; | |
| 254 | + this.tableData2 = []; | |
| 255 | + this.defaultLevels.type = this.fromData.type; | |
| 256 | + this.defaultLevels.levelType = this.fromData.levelType; | |
| 257 | + this.defaultLevels.levels = [...this.fromData.levels]; | |
| 258 | + sessionStorage.setItem("levelFromData", JSON.stringify(this.fromData)); | |
| 259 | + this.diaLogBox = false; | |
| 260 | + this._QueryData({ | |
| 261 | + levelType: this.fromData.levelType, | |
| 262 | + levels: this.fromData.levels, | |
| 263 | + }); | |
| 264 | + }, | |
| 265 | + | |
| 266 | + async _QueryDefaultLevels() { | |
| 267 | + const { data, info, status } = await this.$request.defaultLevels(); | |
| 268 | + if (status === 0) { | |
| 269 | + this.defaultLevels = { ...data } || { | |
| 270 | + levelType: 0, | |
| 271 | + levels: [ | |
| 272 | + ["优秀", 100, 90], | |
| 273 | + ["良好", 89.9, 70], | |
| 274 | + ["合格", 69.9, 60], | |
| 275 | + ["不合格", 59.9, 0], | |
| 276 | + ], | |
| 277 | + }; | |
| 278 | + this.defaultLevels.type = 0 | |
| 279 | + this.fromData.levelType = this.defaultLevels.levelType; | |
| 280 | + this.fromData.levels = [...this.defaultLevels.levels]; | |
| 281 | + sessionStorage.setItem( | |
| 282 | + "levelFromData", | |
| 283 | + JSON.stringify(this.defaultLevels) | |
| 284 | + ); | |
| 285 | + } else { | |
| 286 | + this.$message.error(info); | |
| 287 | + } | |
| 288 | + }, | |
| 289 | + async _QueryData(params) { | |
| 290 | + let query = {}; | |
| 291 | + if (params) { | |
| 292 | + let paramObj = JSON.parse(JSON.stringify(params)) | |
| 293 | + if (paramObj.levelType == 0) { | |
| 294 | + paramObj.levels = paramObj.levels.map((item) => { | |
| 295 | + item[1] = ((item[1] / 100) * this.examPaperScore).toFixed(1); | |
| 296 | + item[2] = ((item[2] / 100) * this.examPaperScore).toFixed(1); | |
| 297 | + return item; | |
| 298 | + }); | |
| 299 | + } | |
| 300 | + query = { ...paramObj }; | |
| 301 | + } | |
| 302 | + const { data, info, status } = await this.$request.examMultiClassReport({ | |
| 303 | + examIds: this.ids, | |
| 304 | + ...query, | |
| 305 | + }); | |
| 306 | + if (status === 0) { | |
| 307 | + this.examPaperScore = data.examPaperScore || 100; | |
| 308 | + this.tableData = data.classes || []; | |
| 309 | + this.tableData2 = | |
| 310 | + data.students.map((item) => { | |
| 311 | + item.score = Number(item.score); | |
| 312 | + return item; | |
| 313 | + }) || []; | |
| 314 | + } else { | |
| 315 | + this.$message.error(info); | |
| 316 | + } | |
| 317 | + }, | |
| 318 | + | |
| 319 | + //导出 | |
| 320 | + async exportData() { | |
| 321 | + if (this.exportLoading == true) return; | |
| 322 | + this.exportLoading = true; | |
| 323 | + let params = { ...this.fromData }; | |
| 324 | + if (params.levelType == 0) { | |
| 325 | + params.levels = params.levels.map((item) => { | |
| 326 | + console.log(item); | |
| 327 | + item[1] = ((item[1] / 100) * this.examPaperScore).toFixed(1); | |
| 328 | + item[2] = ((item[2] / 100) * this.examPaperScore).toFixed(1); | |
| 329 | + return item; | |
| 330 | + }); | |
| 331 | + } | |
| 332 | + const data = await this.$request.exportExamMultiReport({ | |
| 333 | + examIds: this.ids, | |
| 334 | + levels: params.levels, | |
| 335 | + levelType: params.levelType, | |
| 336 | + }); | |
| 337 | + this.exportLoading = false; | |
| 338 | + if (data) { | |
| 339 | + let blob = new Blob([data], { | |
| 340 | + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| 341 | + }); | |
| 342 | + downloadFile( | |
| 343 | + `多班_${this.subjectNames}_${this.title}_测练成绩对比分析`, | |
| 344 | + blob | |
| 345 | + ); | |
| 346 | + } else { | |
| 347 | + this.$message.error("下载失败"); | |
| 348 | + } | |
| 349 | + }, | |
| 350 | + }, | |
| 351 | +}; | |
| 352 | +</script> | |
| 353 | + | |
| 354 | +<style lang="scss" scoped> | |
| 355 | +.page-container { | |
| 356 | + position: relative; | |
| 357 | + height: 100%; | |
| 358 | + | |
| 359 | + .table-box { | |
| 360 | + min-height: 100%; | |
| 361 | + } | |
| 362 | + | |
| 363 | + &.active { | |
| 364 | + overflow: hidden; | |
| 365 | + } | |
| 366 | + | |
| 367 | + .content-header { | |
| 368 | + width: 100%; | |
| 369 | + position: relative; | |
| 370 | + | |
| 371 | + .setMinScore { | |
| 372 | + position: absolute; | |
| 373 | + bottom: 0; | |
| 374 | + right: 0; | |
| 375 | + } | |
| 376 | + } | |
| 377 | + | |
| 378 | + .page-content { | |
| 379 | + padding: 20px 20px 0; | |
| 380 | + } | |
| 381 | +} | |
| 382 | + | |
| 383 | +.tab-box { | |
| 384 | + width: 400px; | |
| 385 | + margin: 0 auto 12px; | |
| 386 | + background: #f8f8f8; | |
| 387 | + border-radius: 20px; | |
| 388 | + display: flex; | |
| 389 | + user-select: none; | |
| 390 | + | |
| 391 | + .tab-item { | |
| 392 | + flex: 1; | |
| 393 | + height: 40px; | |
| 394 | + line-height: 40px; | |
| 395 | + text-align: center; | |
| 396 | + font-size: 16px; | |
| 397 | + color: #666; | |
| 398 | + font-weight: 500; | |
| 399 | + background: transparent; | |
| 400 | + border-radius: 20px; | |
| 401 | + cursor: pointer; | |
| 402 | + | |
| 403 | + &.active { | |
| 404 | + background: #667ffd; | |
| 405 | + color: #fff; | |
| 406 | + } | |
| 407 | + } | |
| 408 | +} | |
| 409 | + | |
| 410 | +.down { | |
| 411 | + padding-top: 20px; | |
| 412 | + width: 100%; | |
| 413 | + display: flex; | |
| 414 | + justify-content: space-between; | |
| 415 | +} | |
| 416 | + | |
| 417 | +.use-form { | |
| 418 | + padding: 0 12px; | |
| 419 | + | |
| 420 | + .use-form-item-box { | |
| 421 | + :deep(.el-form-item__content) { | |
| 422 | + display: flex; | |
| 423 | + } | |
| 424 | + | |
| 425 | + .use-form-item { | |
| 426 | + width: 40%; | |
| 427 | + margin-right: 20px; | |
| 428 | + } | |
| 429 | + } | |
| 430 | +} | |
| 431 | + | |
| 432 | +.dia-tab-box { | |
| 433 | + | |
| 434 | + .dia-tab-tit, | |
| 435 | + .dia-tab-item { | |
| 436 | + margin-bottom: 10px; | |
| 437 | + | |
| 438 | + i { | |
| 439 | + color: #f30; | |
| 440 | + padding-right: 5px; | |
| 441 | + } | |
| 442 | + | |
| 443 | + display: flex; | |
| 444 | + | |
| 445 | + .item { | |
| 446 | + width: 40px; | |
| 447 | + } | |
| 448 | + | |
| 449 | + .item1 { | |
| 450 | + padding-left: 10px; | |
| 451 | + width: 10%; | |
| 452 | + } | |
| 453 | + | |
| 454 | + .item2 { | |
| 455 | + width: 18%; | |
| 456 | + } | |
| 457 | + | |
| 458 | + .item3 { | |
| 459 | + padding-left: 12px; | |
| 460 | + flex: 1; | |
| 461 | + } | |
| 462 | + | |
| 463 | + .score-ipt { | |
| 464 | + width: 100px; | |
| 465 | + } | |
| 466 | + } | |
| 467 | + | |
| 468 | + .dia-tab-tit { | |
| 469 | + background: rgba(243, 243, 243, 1); | |
| 470 | + } | |
| 471 | + | |
| 472 | + .add { | |
| 473 | + display: flex; | |
| 474 | + justify-content: center; | |
| 475 | + margin: 0 auto; | |
| 476 | + | |
| 477 | + p { | |
| 478 | + cursor: pointer; | |
| 479 | + } | |
| 480 | + | |
| 481 | + .el-button { | |
| 482 | + margin-right: 6px; | |
| 483 | + } | |
| 484 | + } | |
| 485 | +} | |
| 486 | + | |
| 487 | +.p1 { | |
| 488 | + line-height: 18px; | |
| 489 | +} | |
| 490 | +</style> | |
| 0 | 491 | \ No newline at end of file | ... | ... |
src/views/basic/test/components/multipleSubTest.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="table-box" ref="main" v-loading="loading"> | |
| 3 | + <div id="print-content"> | |
| 4 | + <!-- 多科多卷 --> | |
| 5 | + <el-table :data="tableData" :max-height="tableMaxHeight" border style="width: 100%"> | |
| 6 | + <el-table-column prop="studentCode" label="学号" align="center" fixed></el-table-column> | |
| 7 | + | |
| 8 | + <el-table-column prop="studentName" label="姓名" fixed align="center"> | |
| 9 | + <template slot-scope="scoped"><span class="click-b" @click="toPortrait(scoped.row)"> | |
| 10 | + {{ scoped.row.studentName }} | |
| 11 | + </span></template> | |
| 12 | + </el-table-column> | |
| 13 | + <el-table-column align="center" v-for="(item, index) in answerList" :key="index" :label="item"> | |
| 14 | + <el-table-column :prop="'examCount' + item" label="测练数" align="center" | |
| 15 | + :class-name="index % 2 == 0 ? 'bg' : ''"></el-table-column> | |
| 16 | + <el-table-column :prop="'participationCount' + item" label="参与数" align="center" | |
| 17 | + :class-name="index % 2 == 0 ? 'bg' : ''"></el-table-column> | |
| 18 | + <el-table-column :prop="'score' + item" label="总分" align="center" | |
| 19 | + :class-name="index % 2 == 0 ? 'bg' : ''"></el-table-column> | |
| 20 | + <el-table-column :prop="'classRank' + item" label="班名" align="center" | |
| 21 | + :class-name="index % 2 == 0 ? 'bg' : ''"></el-table-column> | |
| 22 | + </el-table-column> | |
| 23 | + <el-table-column label="查看折线图" align="center"> | |
| 24 | + <template slot-scope="scoped"> | |
| 25 | + <el-button @click="openChart(scoped.row)" type="primary" size="mini" circle | |
| 26 | + icon="el-icon-arrow-right"></el-button> | |
| 27 | + </template> | |
| 28 | + </el-table-column> | |
| 29 | + </el-table> | |
| 30 | + </div> | |
| 31 | + <div class="down"> | |
| 32 | + <el-button @click="exportData" type="primary" plain round icon="fa fa-cloud-download">导出报表</el-button> | |
| 33 | + <el-button v-if="!this.$store.getters.code" @click="print" type="primary" plain round | |
| 34 | + icon="el-icon-printer">打印</el-button> | |
| 35 | + </div> | |
| 36 | + <el-dialog class="chart-dia" :visible.sync="chartDia" :title="chartTitle" width="800"> | |
| 37 | + <div class="chart-box"> | |
| 38 | + <RadarChart id="radarChart" :params="chartData" /> | |
| 39 | + </div> | |
| 40 | + </el-dialog> | |
| 41 | + </div> | |
| 42 | +</template> | |
| 43 | +<script> | |
| 44 | +import RadarChart from "@/components/charts/radarChart" | |
| 45 | + | |
| 46 | +import { downloadFile, tablePrint } from "@/utils"; | |
| 47 | +export default { | |
| 48 | + components: { | |
| 49 | + RadarChart | |
| 50 | + }, | |
| 51 | + props: { | |
| 52 | + role: "", | |
| 53 | + ids: Array, | |
| 54 | + classId: String, | |
| 55 | + subjectName: String, | |
| 56 | + }, | |
| 57 | + data() { | |
| 58 | + return { | |
| 59 | + tableMaxHeight: null, | |
| 60 | + answerList: [], //设置多卷内容供tableStage表格数据用 | |
| 61 | + tableData: [], | |
| 62 | + loading: false, | |
| 63 | + exportLoading: false, | |
| 64 | + chartData: { | |
| 65 | + indicator: [ | |
| 66 | + { | |
| 67 | + name: '', max: 100, | |
| 68 | + axisLabel: { | |
| 69 | + show: true, | |
| 70 | + showMaxLabel: true, | |
| 71 | + formatter: '{value}%' | |
| 72 | + }, | |
| 73 | + }, | |
| 74 | + ], | |
| 75 | + seriesData: [] | |
| 76 | + }, | |
| 77 | + chartDia: false, | |
| 78 | + chartTitle: "", | |
| 79 | + } | |
| 80 | + }, | |
| 81 | + computed: { | |
| 82 | + subjectList: function () { | |
| 83 | + return this.subjectName?.split(",") | |
| 84 | + } | |
| 85 | + }, | |
| 86 | + created() { | |
| 87 | + this.phaseExamReport() | |
| 88 | + }, | |
| 89 | + mounted() { | |
| 90 | + this.tableMaxHeight = this.$refs.main.offsetHeight; | |
| 91 | + }, | |
| 92 | + methods: { | |
| 93 | + print() { | |
| 94 | + tablePrint("print-content", this.title + this.tabList[this.type]); | |
| 95 | + }, | |
| 96 | + toPortrait(obj) { | |
| 97 | + //暂时不上线 | |
| 98 | + return; | |
| 99 | + if (this.$store.getters.code) { | |
| 100 | + return; | |
| 101 | + } | |
| 102 | + let subjectNames = []; | |
| 103 | + subjectNames = | |
| 104 | + this.role == "ROLE_BANZHUREN" | |
| 105 | + ? [...this.query["subjectNames"]] | |
| 106 | + : [this.query["subjectNames"]]; | |
| 107 | + if ( | |
| 108 | + this.query["subjectNames"] && | |
| 109 | + this.query["subjectNames"]?.length == 1 && | |
| 110 | + this.query["subjectNames"][0] == "全部" | |
| 111 | + ) { | |
| 112 | + subjectNames = this.subjectList.map((item) => { | |
| 113 | + return item.value; | |
| 114 | + }); | |
| 115 | + subjectNames?.shift(); | |
| 116 | + } | |
| 117 | + //去学生画像 | |
| 118 | + this.$router.push({ | |
| 119 | + path: "/portraitDetail", | |
| 120 | + query: { | |
| 121 | + id: obj.studentId, | |
| 122 | + classId: this.query.classId, | |
| 123 | + subjectNames: subjectNames.join(","), | |
| 124 | + studentName: obj.studentName, | |
| 125 | + studentCode: obj.studentCode, | |
| 126 | + startDay: this.query.startDay, | |
| 127 | + endDay: this.query.endDay, | |
| 128 | + date: this.date, | |
| 129 | + }, | |
| 130 | + }); | |
| 131 | + }, | |
| 132 | + //查看折线图 | |
| 133 | + openChart(obj) { | |
| 134 | + this.chartTitle = obj.studentName + '-多科-多课时作答表现图' | |
| 135 | + let subjectList = obj.dataList.map(item => item.subjectName) | |
| 136 | + subjectList.map((item, index) => { | |
| 137 | + if (index < 1) { | |
| 138 | + this.chartData.indicator[index].name = item | |
| 139 | + } else { | |
| 140 | + this.chartData.indicator.push({ name: item, max: 100 }) | |
| 141 | + } | |
| 142 | + }) | |
| 143 | + if (this.chartData.indicator.length < 3) { | |
| 144 | + let num = this.chartData.indicator.length | |
| 145 | + for (let i = 0; i < 6; i++) { | |
| 146 | + if (i >= num) { | |
| 147 | + this.chartData.indicator.push({ name: "", max: 100 }) | |
| 148 | + } | |
| 149 | + } | |
| 150 | + } | |
| 151 | + this.chartData.seriesData = [ | |
| 152 | + { | |
| 153 | + value: obj.dataList.map(item => item.participationRate), | |
| 154 | + name: '参与度' | |
| 155 | + }, | |
| 156 | + { | |
| 157 | + value: obj.dataList.map(item => item.correctRate), | |
| 158 | + name: '正确率' | |
| 159 | + }, | |
| 160 | + ] | |
| 161 | + this.chartDia = true | |
| 162 | + | |
| 163 | + | |
| 164 | + }, | |
| 165 | + async phaseExamReport() { | |
| 166 | + this.loading = true; | |
| 167 | + const { data, status, info } = await this.$request.cTPhaseExamReport({ | |
| 168 | + classId: this.classId, | |
| 169 | + examIds: this.ids, | |
| 170 | + subjectNames: this.subjectList, | |
| 171 | + }); | |
| 172 | + this.loading = false; | |
| 173 | + if (status === 0) { | |
| 174 | + this.total = data.count; | |
| 175 | + let subjectName = []; | |
| 176 | + this.tableData = data?.list.map((item) => { | |
| 177 | + let params = {}; | |
| 178 | + item.dataList.map((items, index) => { | |
| 179 | + if (!subjectName.includes(items.subjectName)) { | |
| 180 | + subjectName.push(items.subjectName); | |
| 181 | + } | |
| 182 | + params["examCount" + items.subjectName] = items.examCount; | |
| 183 | + params["participationCount" + items.subjectName] = | |
| 184 | + items.participationCount; | |
| 185 | + params["score" + items.subjectName] = items.score; | |
| 186 | + params["classRank" + items.subjectName] = items.classRank; | |
| 187 | + }); | |
| 188 | + return { | |
| 189 | + ...item, | |
| 190 | + ...params, | |
| 191 | + }; | |
| 192 | + }); | |
| 193 | + this.answerList = [...subjectName]; | |
| 194 | + } else { | |
| 195 | + this.$message.error(info); | |
| 196 | + } | |
| 197 | + }, | |
| 198 | + //导出 | |
| 199 | + async exportData() { | |
| 200 | + if (this.exportLoading == true) return; | |
| 201 | + this.exportLoading = true; | |
| 202 | + const data = await this.$request.exportExamReport({ | |
| 203 | + examId: this.ids, | |
| 204 | + }); | |
| 205 | + this.exportLoading = false; | |
| 206 | + if (data) { | |
| 207 | + let blob = new Blob([data], { | |
| 208 | + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| 209 | + }); | |
| 210 | + downloadFile( | |
| 211 | + this.status | |
| 212 | + ? "即时测-已归档单卷测练报表.xlsx" | |
| 213 | + : "即时测-单卷测练报表.xlsx", | |
| 214 | + blob | |
| 215 | + ); | |
| 216 | + } else { | |
| 217 | + this.$message.error("下载失败"); | |
| 218 | + } | |
| 219 | + }, | |
| 220 | + } | |
| 221 | +}; | |
| 222 | +</script> | |
| 223 | +<style lang="scss" scoped> | |
| 224 | +.table-box { | |
| 225 | + padding: 20px; | |
| 226 | +} | |
| 227 | + | |
| 228 | +.down { | |
| 229 | + padding-top: 20px; | |
| 230 | + width: 100%; | |
| 231 | + display: flex; | |
| 232 | + justify-content: space-between; | |
| 233 | +} | |
| 234 | + | |
| 235 | +.chart-dia { | |
| 236 | + .chart-box { | |
| 237 | + width: 100%; | |
| 238 | + height: 400px; | |
| 239 | + } | |
| 240 | + | |
| 241 | + :deep(.el-dialog__body) { | |
| 242 | + padding: 0 0 20px 0; | |
| 243 | + } | |
| 244 | +} | |
| 245 | +</style> | ... | ... |
src/views/basic/test/components/multipleTest.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="table-box" ref="main" v-loading="loading"> | |
| 3 | + <div id="print-content"> | |
| 4 | + <el-table :max-height="tableMaxHeight" :data="tableData" border style="width: 100%"> | |
| 5 | + <el-table-column prop="studentCode" label="学号" align="center" fixed></el-table-column> | |
| 6 | + <el-table-column prop="studentName" label="姓名" fixed align="center"> | |
| 7 | + <template slot-scope="scoped"><span class="click-b" @click="toPortrait(scoped.row)"> | |
| 8 | + {{ scoped.row.studentName }} | |
| 9 | + </span></template></el-table-column> | |
| 10 | + <el-table-column align="center" v-for="(item, index) in answerList" :key="index" :label="item.title"> | |
| 11 | + <el-table-column :prop="'score' + index" :label="index == 0 ? '总分' : '成绩'" align="center" | |
| 12 | + :class-name="index % 2 == 0 ? 'bg' : ''"></el-table-column> | |
| 13 | + <el-table-column :prop="'classRank' + index" label="班名" align="center" sortable | |
| 14 | + :class-name="index % 2 == 0 ? 'bg' : ''"></el-table-column> | |
| 15 | + </el-table-column> | |
| 16 | + <el-table-column label="查看折线图" align="center"> | |
| 17 | + <template slot-scope="scoped"> | |
| 18 | + <el-button @click="openChart(scoped.row)" type="primary" size="mini" circle | |
| 19 | + icon="el-icon-arrow-right"></el-button> | |
| 20 | + </template> | |
| 21 | + </el-table-column> | |
| 22 | + </el-table> | |
| 23 | + </div> | |
| 24 | + <div class="down"> | |
| 25 | + <el-button @click="exportData" type="primary" plain round icon="fa fa-cloud-download">导出报表</el-button> | |
| 26 | + <el-button v-if="!this.$store.getters.code" @click="print" type="primary" plain round | |
| 27 | + icon="el-icon-printer">打印</el-button> | |
| 28 | + </div> | |
| 29 | + <el-dialog class="chart-dia" :visible.sync="chartDia" :title="chartTitle" width="800"> | |
| 30 | + <div class="chart-box"> | |
| 31 | + <LineChart id="lineChart" :params="chartData" :xAxis="xAxis" /> | |
| 32 | + </div> | |
| 33 | + </el-dialog> | |
| 34 | + </div> | |
| 35 | + <!-- 单科多卷 --> | |
| 36 | +</template> | |
| 37 | +<script> | |
| 38 | +import LineChart from "@/components/charts/lineChart" | |
| 39 | +import { downloadFile, tablePrint } from "@/utils"; | |
| 40 | +export default { | |
| 41 | + components: { LineChart }, | |
| 42 | + props: { | |
| 43 | + role: "", | |
| 44 | + ids: Array, | |
| 45 | + classId: String, | |
| 46 | + subjectName: String, | |
| 47 | + }, | |
| 48 | + data() { | |
| 49 | + return { | |
| 50 | + tableMaxHeight: null, | |
| 51 | + answerList: [], //设置多卷内容供tableStage表格数据用 | |
| 52 | + tableData: [], | |
| 53 | + loading: false, | |
| 54 | + exportLoading: false, | |
| 55 | + chartDia: false, | |
| 56 | + studentName: "", | |
| 57 | + xAxis: [], | |
| 58 | + chartData: [], | |
| 59 | + } | |
| 60 | + }, | |
| 61 | + computed: { | |
| 62 | + chartTitle: function () { | |
| 63 | + return `${this.studentName}-${this.subjectName}-多课时作答表现图` | |
| 64 | + } | |
| 65 | + }, | |
| 66 | + created() { | |
| 67 | + this.phaseExamReport() | |
| 68 | + }, | |
| 69 | + mounted() { | |
| 70 | + this.tableMaxHeight = this.$refs.main.offsetHeight; | |
| 71 | + }, | |
| 72 | + methods: { | |
| 73 | + print() { | |
| 74 | + tablePrint("print-content", this.subjectName + '汇总报表'); | |
| 75 | + }, | |
| 76 | + toPortrait(obj) { | |
| 77 | + //暂时不上线 | |
| 78 | + return; | |
| 79 | + if (this.$store.getters.code) { | |
| 80 | + return; | |
| 81 | + } | |
| 82 | + let subjectNames = []; | |
| 83 | + subjectNames = | |
| 84 | + this.role == "ROLE_BANZHUREN" | |
| 85 | + ? [...this.query["subjectNames"]] | |
| 86 | + : [this.query["subjectNames"]]; | |
| 87 | + if ( | |
| 88 | + this.query["subjectNames"] && | |
| 89 | + this.query["subjectNames"]?.length == 1 && | |
| 90 | + this.query["subjectNames"][0] == "全部" | |
| 91 | + ) { | |
| 92 | + subjectNames = this.subjectList.map((item) => { | |
| 93 | + return item.value; | |
| 94 | + }); | |
| 95 | + subjectNames?.shift(); | |
| 96 | + } | |
| 97 | + //去学生画像 | |
| 98 | + this.$router.push({ | |
| 99 | + path: "/portraitDetail", | |
| 100 | + query: { | |
| 101 | + id: obj.studentId, | |
| 102 | + classId: this.query.classId, | |
| 103 | + subjectNames: subjectNames.join(","), | |
| 104 | + studentName: obj.studentName, | |
| 105 | + studentCode: obj.studentCode, | |
| 106 | + startDay: this.query.startDay, | |
| 107 | + endDay: this.query.endDay, | |
| 108 | + date: this.date, | |
| 109 | + }, | |
| 110 | + }); | |
| 111 | + }, | |
| 112 | + //查看折线图 | |
| 113 | + openChart(obj) { | |
| 114 | + this.studentName = obj.studentName | |
| 115 | + this.chartDia = true | |
| 116 | + let participationRate = [] | |
| 117 | + let correctRate = [] | |
| 118 | + let answerCorrectRate = [] | |
| 119 | + this.xAxis = obj.examList.map(item => { | |
| 120 | + participationRate.push(item.participationRate) | |
| 121 | + correctRate.push(item.correctRate) | |
| 122 | + answerCorrectRate.push(item.answerCorrectRate) | |
| 123 | + return item.title | |
| 124 | + }) | |
| 125 | + this.chartData = [ | |
| 126 | + { | |
| 127 | + name: "参与度", | |
| 128 | + value: participationRate | |
| 129 | + }, | |
| 130 | + { | |
| 131 | + name: "正确率", | |
| 132 | + value: correctRate | |
| 133 | + }, | |
| 134 | + { | |
| 135 | + name: "已答正确率", | |
| 136 | + value: answerCorrectRate | |
| 137 | + }, | |
| 138 | + ] | |
| 139 | + | |
| 140 | + }, | |
| 141 | + async phaseExamReport() { | |
| 142 | + this.loading = true; | |
| 143 | + const phaseExamReport = | |
| 144 | + this.role == "ROLE_PERSONAL" | |
| 145 | + ? this.$request.pPhaseExamReport | |
| 146 | + : this.$request.phaseExamReport; | |
| 147 | + const { data, status, info } = await phaseExamReport({ | |
| 148 | + classId: this.classId, | |
| 149 | + examIds: this.ids, | |
| 150 | + subjectName: this.subjectName | |
| 151 | + }); | |
| 152 | + this.loading = false; | |
| 153 | + if (status === 0) { | |
| 154 | + this.total = data.count; | |
| 155 | + let dataIdsList = [], | |
| 156 | + dataList = []; | |
| 157 | + data?.list.map((item) => { | |
| 158 | + item.examList.map((items) => { | |
| 159 | + if (!dataIdsList.includes(items.title)) { | |
| 160 | + dataIdsList.push(items.title); | |
| 161 | + dataList.push(items); | |
| 162 | + } | |
| 163 | + }); | |
| 164 | + }); | |
| 165 | + this.tableData = data?.list.map((item) => { | |
| 166 | + let params = {}; | |
| 167 | + dataIdsList.map((ids, index) => { | |
| 168 | + params["score" + index] = "--"; | |
| 169 | + params["classRank" + index] = "--"; | |
| 170 | + item.examList.map((items) => { | |
| 171 | + if (items.title == ids) { | |
| 172 | + params["score" + index] = items.score; | |
| 173 | + params["classRank" + index] = items.classRank; | |
| 174 | + } | |
| 175 | + }); | |
| 176 | + }); | |
| 177 | + return { | |
| 178 | + ...item, | |
| 179 | + ...params, | |
| 180 | + }; | |
| 181 | + }); | |
| 182 | + this.answerList = dataList; | |
| 183 | + } else { | |
| 184 | + this.$message.error(info); | |
| 185 | + } | |
| 186 | + }, | |
| 187 | + //导出 | |
| 188 | + async exportData() { | |
| 189 | + if (this.exportLoading == true) return; | |
| 190 | + this.exportLoading = true; | |
| 191 | + const exportPhaseExamReport = this.role == "ROLE_PERSONAL" | |
| 192 | + ? this.$request.pExportPhaseExamReport | |
| 193 | + : this.$request.exportPhaseExamReport; | |
| 194 | + const data = await exportPhaseExamReport({ | |
| 195 | + examId: this.ids, | |
| 196 | + }); | |
| 197 | + this.exportLoading = false; | |
| 198 | + if (data) { | |
| 199 | + let blob = new Blob([data], { | |
| 200 | + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| 201 | + }); | |
| 202 | + downloadFile("即时测-单科多卷报表.xlsx", blob); | |
| 203 | + } else { | |
| 204 | + this.$message.error("下载失败"); | |
| 205 | + } | |
| 206 | + }, | |
| 207 | + } | |
| 208 | +}; | |
| 209 | +</script> | |
| 210 | +<style lang="scss" scoped> | |
| 211 | +.table-box { | |
| 212 | + padding: 20px; | |
| 213 | +} | |
| 214 | + | |
| 215 | +.down { | |
| 216 | + padding-top: 20px; | |
| 217 | + width: 100%; | |
| 218 | + display: flex; | |
| 219 | + justify-content: space-between; | |
| 220 | +} | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | +.chart-dia { | |
| 225 | + .chart-box { | |
| 226 | + width: 100%; | |
| 227 | + height: 300px; | |
| 228 | + } | |
| 229 | + | |
| 230 | + :deep(.el-dialog__body) { | |
| 231 | + padding: 0 0 20px 0; | |
| 232 | + } | |
| 233 | +} | |
| 234 | +</style> | ... | ... |
src/views/basic/test/components/scoreSet.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="set-container"> | |
| 3 | + <div class="back"> | |
| 4 | + <div class="back-l" @click="closeScoreSet"> | |
| 5 | + <i class="fa fa-mail-reply-all"></i> | |
| 6 | + <span>答卷录分</span> | |
| 7 | + </div> | |
| 8 | + </div> | |
| 9 | + <div class="set-content"> | |
| 10 | + <div class="test-title"> | |
| 11 | + <el-button class="import-btn" type="primary" round @click="diaUp = true">从excel文件导入</el-button> | |
| 12 | + <p class="p1">{{ title }}</p> | |
| 13 | + <p class="p2">卷面总分:{{ examScore }}分</p> | |
| 14 | + </div> | |
| 15 | + <el-table :data="tableData" border style="width: 100%"> | |
| 16 | + <el-table-column prop="studentCode" label="学号" align="center" fixed></el-table-column> | |
| 17 | + <el-table-column prop="studentName" label="学号" align="center" fixed></el-table-column> | |
| 18 | + <el-table-column label="总得分" align="center" fixed></el-table-column> | |
| 19 | + <el-table-column label="客观题分" align="center"></el-table-column> | |
| 20 | + <el-table-column label="主观题分" align="center"></el-table-column> | |
| 21 | + <el-table-column v-for="(item, index) in questionList" :key="index" :label="'第' + cNum[index] + '大题'" | |
| 22 | + align="center"> | |
| 23 | + <el-table-column prop="score" v-for="(question, indexs) in item.subQuestions" :label="'Q' + question.id" | |
| 24 | + align="center"> | |
| 25 | + <template slot-scope="scoped"> | |
| 26 | + <el-input type="number" :min="0" | |
| 27 | + v-model="scoped.row.questionList[index].subQuestions[indexs].score"></el-input> | |
| 28 | + </template> | |
| 29 | + </el-table-column> | |
| 30 | + </el-table-column> | |
| 31 | + </el-table> | |
| 32 | + <p class="btn-box"> | |
| 33 | + <el-button type="primary" :loading="loadingSave" @click="_SubmitScore">保存</el-button> | |
| 34 | + </p> | |
| 35 | + </div> | |
| 36 | + | |
| 37 | + <el-dialog :close-on-click-modal="false" :append-to-body="true" title="答卷录分" :visible.sync="diaUp" width="600px" | |
| 38 | + top="120px"> | |
| 39 | + <upload :url="url" :examId="id" @upSuccess="upSuccess" fileName="主观题分数" v-loading="loadingDown"> | |
| 40 | + <template slot="down"> | |
| 41 | + <p class="down-txt"> | |
| 42 | + 第一步:<el-link type="danger" @click="downExcel">下载模板</el-link>,并编辑完成学生分数。 | |
| 43 | + </p> | |
| 44 | + <p class="down-txt">第二步:上传完成编辑的模板文件并导入。</p> | |
| 45 | + </template> | |
| 46 | + </upload> | |
| 47 | + <div class="dialog-footer" slot="footer"> | |
| 48 | + <el-button @click="diaUp = false">取 消</el-button> | |
| 49 | + </div> | |
| 50 | + </el-dialog> | |
| 51 | + </div> | |
| 52 | +</template> | |
| 53 | +<script> | |
| 54 | +import { downloadFile, cNum } from "utils"; | |
| 55 | +export default { | |
| 56 | + props: { | |
| 57 | + title: String, | |
| 58 | + id: String, | |
| 59 | + role: String, | |
| 60 | + examScore: { | |
| 61 | + type: Number, | |
| 62 | + default: 0 | |
| 63 | + }, | |
| 64 | + }, | |
| 65 | + data() { | |
| 66 | + return { | |
| 67 | + diaUp: false, | |
| 68 | + loading: false, | |
| 69 | + loadingDown: false, | |
| 70 | + loadingSave: false, | |
| 71 | + url: "/api_html/teaching/importScore", | |
| 72 | + tableData: [], | |
| 73 | + questionList: [], | |
| 74 | + cNum: cNum | |
| 75 | + } | |
| 76 | + }, | |
| 77 | + watch: { | |
| 78 | + id: { | |
| 79 | + handler: function (nVal) { | |
| 80 | + if (nVal) { | |
| 81 | + this._QueryData() | |
| 82 | + } | |
| 83 | + }, | |
| 84 | + immediate: true | |
| 85 | + } | |
| 86 | + }, | |
| 87 | + methods: { | |
| 88 | + closeScoreSet() { | |
| 89 | + this.$emit('closeScoreSet') | |
| 90 | + }, | |
| 91 | + async _QueryData() { | |
| 92 | + this.loading = true; | |
| 93 | + const { data, status, info } = await this.$request.listStudentsAndQuestions({ | |
| 94 | + examId: this.id | |
| 95 | + }); | |
| 96 | + this.loading = false; | |
| 97 | + if (status === 0) { | |
| 98 | + this.tableData = data.students || [] | |
| 99 | + this.questionList = data?.students ? data?.students[0].questionList : [] | |
| 100 | + console.log(this.questionList) | |
| 101 | + } else { | |
| 102 | + this.$message.error(info); | |
| 103 | + } | |
| 104 | + }, | |
| 105 | + async _SubmitScore() { | |
| 106 | + this.loadingSave = true; | |
| 107 | + let list = this.tableData.map(item => { | |
| 108 | + let scores = {} | |
| 109 | + item.questionList.map(list => { | |
| 110 | + list.subQuestions.map(question => { | |
| 111 | + scores[question.id] = question.score | |
| 112 | + }) | |
| 113 | + }) | |
| 114 | + return { | |
| 115 | + studentCode: item.studentCode, | |
| 116 | + scores | |
| 117 | + } | |
| 118 | + }) | |
| 119 | + console.log(list) | |
| 120 | + const { data, status, info } = await this.$request.submitScore({ | |
| 121 | + examId: this.id, | |
| 122 | + list | |
| 123 | + }); | |
| 124 | + this.loadingSave = false; | |
| 125 | + if (status === 0) { | |
| 126 | + this.tableData = data.students | |
| 127 | + } else { | |
| 128 | + this.$message.error(info); | |
| 129 | + } | |
| 130 | + }, | |
| 131 | + | |
| 132 | + //导入成功 | |
| 133 | + upSuccess(res) { | |
| 134 | + this.$message.success("导入成功"); | |
| 135 | + this.diaUp = false; | |
| 136 | + this._QueryData() | |
| 137 | + }, | |
| 138 | + async downExcel() { | |
| 139 | + //模板下载 | |
| 140 | + this.loadingDown = true; | |
| 141 | + let data = await this.$request.scoreTemplate({ | |
| 142 | + examId: this.id, | |
| 143 | + }); | |
| 144 | + this.loadingDown = false; | |
| 145 | + if (data && !data.code) { | |
| 146 | + let blob = new Blob([data], { | |
| 147 | + type: "application/vnd.ms-excel;charset=utf-8", | |
| 148 | + }); | |
| 149 | + downloadFile(`答卷录分模版.xlsx`, blob); | |
| 150 | + } else { | |
| 151 | + this.$message.error(data.info); | |
| 152 | + } | |
| 153 | + }, | |
| 154 | + } | |
| 155 | +} | |
| 156 | +</script> | |
| 157 | + | |
| 158 | +<style lang="scss" scoped> | |
| 159 | +.set-container { | |
| 160 | + position: absolute; | |
| 161 | + left: 0; | |
| 162 | + top: 0; | |
| 163 | + width: 100%; | |
| 164 | + height: 100%; | |
| 165 | + background: #fff; | |
| 166 | + z-index: 2000; | |
| 167 | + overflow-y: auto; | |
| 168 | +} | |
| 169 | + | |
| 170 | +.back { | |
| 171 | + width: 100%; | |
| 172 | + height: 56px; | |
| 173 | + border-bottom: 1px solid #e2e2e2; | |
| 174 | + display: flex; | |
| 175 | + justify-content: space-between; | |
| 176 | + align-items: center; | |
| 177 | + padding: 0 20px; | |
| 178 | + box-sizing: border-box; | |
| 179 | + | |
| 180 | + .back-l { | |
| 181 | + display: flex; | |
| 182 | + align-items: center; | |
| 183 | + cursor: pointer; | |
| 184 | + flex-shrink: 0; | |
| 185 | + font-size: 18px; | |
| 186 | + font-weight: 500; | |
| 187 | + } | |
| 188 | + | |
| 189 | + .back-r { | |
| 190 | + flex: 1; | |
| 191 | + display: flex; | |
| 192 | + justify-content: flex-end; | |
| 193 | + } | |
| 194 | + | |
| 195 | + .fa-mail-reply-all { | |
| 196 | + font-size: 28px; | |
| 197 | + color: #b3b3b3; | |
| 198 | + margin-right: 12px; | |
| 199 | + } | |
| 200 | +} | |
| 201 | + | |
| 202 | +.set-content { | |
| 203 | + padding: 12px 20px; | |
| 204 | + | |
| 205 | + .test-title { | |
| 206 | + width: 100%; | |
| 207 | + text-align: center; | |
| 208 | + position: relative; | |
| 209 | + margin-bottom: 20px; | |
| 210 | + | |
| 211 | + .import-btn { | |
| 212 | + position: absolute; | |
| 213 | + right: 0; | |
| 214 | + top: 5px; | |
| 215 | + } | |
| 216 | + | |
| 217 | + .p1 { | |
| 218 | + font-size: 18px; | |
| 219 | + font-weight: 700; | |
| 220 | + } | |
| 221 | + } | |
| 222 | +} | |
| 223 | + | |
| 224 | +.btn-box { | |
| 225 | + padding-top: 12px; | |
| 226 | + text-align: right; | |
| 227 | +} | |
| 228 | +</style> | |
| 0 | 229 | \ No newline at end of file | ... | ... |
src/views/basic/test/components/test.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div ref="main"> | |
| 3 | + <div class="tips" v-if="paperModifyLog.modifiedTime && !status"> | |
| 4 | + <p class="tips-p"> | |
| 5 | + <i class="fa fa-bell-o"></i> | |
| 6 | + {{ | |
| 7 | + `${paperModifyLog.modifiedTime} ${paperModifyLog.realName}` | |
| 8 | + }}修改了答案,是否重新记分? | |
| 9 | + </p> | |
| 10 | + <div class="btn-box"> | |
| 11 | + <el-button type="danger" round @click="_ReScore" size="mini">重新计分</el-button> | |
| 12 | + <el-button type="danger" round plain size="mini" @click="paperModifyLog.modifiedTime = ''">暂时不计</el-button> | |
| 13 | + </div> | |
| 14 | + </div> | |
| 15 | + <div class="page-content"> | |
| 16 | + <div class="content-header"> | |
| 17 | + <div class="tab-box"> | |
| 18 | + <span v-for="(item, index) in tabList" :key="item" class="tab-item" :class="type == index ? 'active' : ''" | |
| 19 | + @click="setType(index)">{{ item }}</span> | |
| 20 | + </div> | |
| 21 | + <el-button v-if="!status" class="setMinScore" @click="diaMinScore = true" round size="small">设置低分值</el-button> | |
| 22 | + </div> | |
| 23 | + <div id="print-content" v-loading="loading"> | |
| 24 | + <div class="table-box"> | |
| 25 | + <el-table :max-height="tableMaxHeight" v-show="type == 0" :data="tableData" border style="width: 100%"> | |
| 26 | + <el-table-column prop="questionIndex" label="题号" align="center" fixed width="60"></el-table-column> | |
| 27 | + <el-table-column prop="questionType" label="题型" align="center" fixed width="100"><template | |
| 28 | + slot-scope="scope">{{ | |
| 29 | + setSubPro(scope.row.questionType) | |
| 30 | + }}</template></el-table-column> | |
| 31 | + <el-table-column prop="score" width="100" label="满分值" sortable align="center"></el-table-column> | |
| 32 | + <el-table-column width="110" prop="highestScore" label="班最高分" sortable align="center"></el-table-column> | |
| 33 | + <el-table-column width="110" prop="lowestScore" label="班最低分" sortable align="center"></el-table-column> | |
| 34 | + <el-table-column width="110" prop="avgScore" label="班平均分" sortable align="center"></el-table-column> | |
| 35 | + <el-table-column prop="classScoringRate" width="120" sortable label="班级得分率" align="center"><template | |
| 36 | + slot-scope="scoped">{{ scoped.row.classScoringRate }}%</template></el-table-column> | |
| 37 | + <el-table-column prop="correctAnswer" label="答案" align="center"><template slot-scope="scoped">{{ | |
| 38 | + scoped.row.correctAnswer == 1 | |
| 39 | + ? "✓" | |
| 40 | + : scoped.row.correctAnswer == 2 | |
| 41 | + ? "✗" | |
| 42 | + : scoped.row.correctAnswer | |
| 43 | + }}</template> | |
| 44 | + </el-table-column> | |
| 45 | + <el-table-column v-for="(item, index) in optionsList" :key="index" :label="item.title" :prop="'count' + index" | |
| 46 | + align="center" width="120"><template slot-scope="scope"> | |
| 47 | + <p class="persent"> | |
| 48 | + {{ | |
| 49 | + scope.row.questionType == "5" | |
| 50 | + ? "" | |
| 51 | + : scope.row["option" + index] | |
| 52 | + ? `${scope.row["option" + index]}(${scope.row["persent" + index] | |
| 53 | + })` | |
| 54 | + : "" | |
| 55 | + }} | |
| 56 | + </p> | |
| 57 | + </template> | |
| 58 | + </el-table-column> | |
| 59 | + </el-table> | |
| 60 | + <div id="print-table"> | |
| 61 | + <table class="hide"> | |
| 62 | + <thead> | |
| 63 | + <tr> | |
| 64 | + <th>题号</th> | |
| 65 | + <th>题型</th> | |
| 66 | + <th>满分值</th> | |
| 67 | + <th>班最高分</th> | |
| 68 | + <th>班最低分</th> | |
| 69 | + <th>班平均分</th> | |
| 70 | + <th>班级得分率</th> | |
| 71 | + <th>答案</th> | |
| 72 | + <th>选项1</th> | |
| 73 | + <th>选项2</th> | |
| 74 | + <th>选项3</th> | |
| 75 | + <th>选项4</th> | |
| 76 | + <th>未答</th> | |
| 77 | + </tr> | |
| 78 | + </thead> | |
| 79 | + <tbody> | |
| 80 | + <tr v-for="(tr, index) in tableData"> | |
| 81 | + <td width="60">{{ index + 1 }}</td> | |
| 82 | + <td width="100">{{ setSubPro(tr.questionType) }}</td> | |
| 83 | + <td width="100">{{ tr.sortable }}</td> | |
| 84 | + <td width="110">{{ tr.highestScore }}</td> | |
| 85 | + <td width="110">{{ tr.lowestScore }}</td> | |
| 86 | + <td width="110">{{ tr.avgScore }}</td> | |
| 87 | + <td width="120">{{ tr.classScoringRate }}%</td> | |
| 88 | + <td> | |
| 89 | + {{ | |
| 90 | + tr.correctAnswer == 1 | |
| 91 | + ? "✓" | |
| 92 | + : tr.correctAnswer == 2 | |
| 93 | + ? "✗" | |
| 94 | + : tr.correctAnswer | |
| 95 | + }} | |
| 96 | + </td> | |
| 97 | + <td v-for="(item, index) in optionsList" :key="index" width="120"> | |
| 98 | + <p class="persent"> | |
| 99 | + {{ | |
| 100 | + tr.questionType == "5" | |
| 101 | + ? "" | |
| 102 | + : tr["option" + index] | |
| 103 | + ? `${tr["option" + index]}(${tr["persent" + index]})` | |
| 104 | + : "" | |
| 105 | + }} | |
| 106 | + </p> | |
| 107 | + </td> | |
| 108 | + </tr> | |
| 109 | + </tbody> | |
| 110 | + </table> | |
| 111 | + <div class="hui-box" v-show="type == 0"> | |
| 112 | + <span class="s-txt">汇总</span> | |
| 113 | + <ul class="hui-ul"> | |
| 114 | + <li class="hui-li"> | |
| 115 | + <span class="hui-s s1">主观题</span> | |
| 116 | + <span class="hui-s s1">{{ examReport.subjectiveScore }}</span> | |
| 117 | + <span class="hui-s s2">{{ | |
| 118 | + examReport.subjectiveHighestScore | |
| 119 | + }}</span> | |
| 120 | + <span class="hui-s s2">{{ | |
| 121 | + examReport.subjectiveLowestScore | |
| 122 | + }}</span> | |
| 123 | + <span class="hui-s s2">{{ | |
| 124 | + examReport.subjectiveAvgScore | |
| 125 | + }}</span> | |
| 126 | + <span class="hui-s s3">{{ examReport.subjectiveClassScoringRate }}%</span> | |
| 127 | + </li> | |
| 128 | + <li class="hui-li"> | |
| 129 | + <span class="hui-s s1">客观题</span> | |
| 130 | + <span class="hui-s s1">{{ examReport.objectiveScore }}</span> | |
| 131 | + <span class="hui-s s2">{{ | |
| 132 | + examReport.objectiveHighestScore | |
| 133 | + }}</span> | |
| 134 | + <span class="hui-s s2">{{ | |
| 135 | + examReport.objectiveLowestScore | |
| 136 | + }}</span> | |
| 137 | + <span class="hui-s s2">{{ examReport.objectiveAvgScore }}</span> | |
| 138 | + <span class="hui-s s3">{{ examReport.objectiveClassScoringRate }}%</span> | |
| 139 | + </li> | |
| 140 | + <li class="hui-li"> | |
| 141 | + <span class="hui-s s1">整卷</span> | |
| 142 | + <span class="hui-s s1">{{ examReport.examPaperScore }}</span> | |
| 143 | + <span class="hui-s s2">{{ examReport.highestScore }}</span> | |
| 144 | + <span class="hui-s s2">{{ examReport.lowestScore }}</span> | |
| 145 | + <span class="hui-s s2">{{ examReport.avgScore }}</span> | |
| 146 | + <span class="hui-s s3">{{ examReport.classScoringRate }}%</span> | |
| 147 | + </li> | |
| 148 | + </ul> | |
| 149 | + </div> | |
| 150 | + </div> | |
| 151 | + <el-table v-show="type == 1" :max-height="tableMaxHeight" :data="tableData2" border style="width: 100%" | |
| 152 | + :default-sort="{ prop: 'dadui', order: 'descending' }"> | |
| 153 | + <el-table-column prop="studentCode" label="学号" align="center" fixed></el-table-column> | |
| 154 | + <el-table-column prop="studentName" label="姓名" fixed align="center"></el-table-column> | |
| 155 | + <el-table-column prop="examScore" label="总分" sortable align="center"></el-table-column> | |
| 156 | + <el-table-column prop="scoringRate" label="得分率" sortable align="center"><template slot-scope="scope">{{ | |
| 157 | + scope.row.scoringRate }}%</template></el-table-column> | |
| 158 | + <el-table-column prop="classRank" label="班名" sortable align="center"></el-table-column> | |
| 159 | + <el-table-column label="客观题" align="center"> | |
| 160 | + <el-table-column prop="objectiveExamScore" label="得分" align="center"></el-table-column> | |
| 161 | + <el-table-column prop="objectiveScoringRate" label="得分率" align="center"><template slot-scope="scope">{{ | |
| 162 | + scope.row.objectiveScoringRate }}%</template></el-table-column> | |
| 163 | + </el-table-column> | |
| 164 | + <el-table-column label="主观题" align="center"> | |
| 165 | + <el-table-column prop="subjectiveExamScore" label="得分" align="center"></el-table-column> | |
| 166 | + <el-table-column prop="subjectiveScoringRate" label="得分率" align="center"><template slot-scope="scope">{{ | |
| 167 | + scope.row.subjectiveScoringRate }}%</template></el-table-column> | |
| 168 | + </el-table-column> | |
| 169 | + </el-table> | |
| 170 | + <el-table v-show="type == 2" :max-height="tableMaxHeight" :data="tableData2" border style="width: 100%" | |
| 171 | + :default-sort="{ prop: '', order: 'descending' }"> | |
| 172 | + <el-table-column prop="studentCode" label="学号" fixed align="center" width="120"></el-table-column> | |
| 173 | + <el-table-column prop="studentName" label="姓名" fixed align="center"></el-table-column> | |
| 174 | + <el-table-column prop="examScore" label="总分" sortable align="center"></el-table-column> | |
| 175 | + <el-table-column label="分数组成" align="center"> | |
| 176 | + <el-table-column prop="objectiveExamScore" label="客观题分" align="center"></el-table-column> | |
| 177 | + <el-table-column prop="subjectiveExamScore" label="主观题分" align="center"></el-table-column> | |
| 178 | + </el-table-column> | |
| 179 | + <el-table-column align="center" v-for="(item, index) in questionList" :key="index" :label="'Q' + item.id" | |
| 180 | + :prop="'score' + item.id"> | |
| 181 | + </el-table-column> | |
| 182 | + </el-table> | |
| 183 | + <el-table :max-height="tableMaxHeight" v-show="type == 3" :data="tableData2" border style="width: 100%" | |
| 184 | + :default-sort="{ prop: '', order: 'descending' }"> | |
| 185 | + <el-table-column prop="studentCode" label="学号" fixed align="center"></el-table-column> | |
| 186 | + <el-table-column prop="studentName" label="姓名" fixed align="center"></el-table-column> | |
| 187 | + <el-table-column prop="className" label="班级" align="center"></el-table-column> | |
| 188 | + <el-table-column prop="examScore" label="总分" sortable align="center"></el-table-column> | |
| 189 | + <el-table-column align="center" v-for="(item, index) in questionList" :key="index" :label="'Q' + item.id"> | |
| 190 | + <template slot-scope="scope"> | |
| 191 | + <span v-if="tableData[index]?.questionType == 5">*</span> | |
| 192 | + <span v-else-if="scope.row['answer' + item.id]" :class="scope.row['isRight' + item.id] ? '' : 'error'"> | |
| 193 | + {{ scope.row["answer" + item.id] }} | |
| 194 | + </span> | |
| 195 | + <span v-else :class="scope.row['questionType' + item.id] == 5 ? '' : 'error'">-</span> | |
| 196 | + </template> | |
| 197 | + </el-table-column> | |
| 198 | + </el-table> | |
| 199 | + </div> | |
| 200 | + </div> | |
| 201 | + <div class="down"> | |
| 202 | + <div> | |
| 203 | + <el-button @click="exportData" type="primary" plain round icon="fa fa-cloud-download">导出报表</el-button> | |
| 204 | + <el-button v-if="!this.$store.getters.code" @click="print" type="primary" plain round | |
| 205 | + icon="el-icon-printer">打印</el-button> | |
| 206 | + </div> | |
| 207 | + <div v-if="!status"> | |
| 208 | + <el-button type="primary" round @click="openScoreSet">答卷录分</el-button> | |
| 209 | + | |
| 210 | + <template v-if="role != 'ROLE_BANZHUREN'"> | |
| 211 | + <el-button @click="edit" type="primary" v-if="examReport.subjectiveScore != examReport.examPaperScore" | |
| 212 | + round>查看题目</el-button></template> | |
| 213 | + </div> | |
| 214 | + </div> | |
| 215 | + <ScoreSet v-show="diaScoreSet" :role="role" :id="id" :title="title" :examScore="score" | |
| 216 | + @closeScoreSet="closeScoreSet" /> | |
| 217 | + <el-dialog :close-on-click-modal="false" title="低分区间设置" :visible.sync="diaMinScore" width="480px" | |
| 218 | + @closed="closeDiaMinScore"> | |
| 219 | + <el-form> | |
| 220 | + <el-form-item label="低分设置模式:"> | |
| 221 | + <el-select v-model="lowRange.type" @change="changeScore"> | |
| 222 | + <el-option label="按分数设置" :value="0"></el-option> | |
| 223 | + <el-option label="按已考人数比例" :value="1"></el-option> | |
| 224 | + <el-option label="按分数比例设置(按题目)" :value="2"></el-option> | |
| 225 | + </el-select> | |
| 226 | + </el-form-item> | |
| 227 | + <el-form-item label="低分区间:"> | |
| 228 | + <el-input class="score-ipt" type="number" v-model="lowRange.range[0]" :min="0" :max="100" | |
| 229 | + @input="lowRange.range[1] > 100 ? (lowRange.range[1] = 100) : ''" | |
| 230 | + @keydown.native="keydownRange($event)"></el-input>{{ lowRange.type != 0 ? "%" : "分" }}(含) | |
| 231 | + <el-input class="score-ipt" type="number" v-model="lowRange.range[1]" :min="0" :max="100" | |
| 232 | + @input="lowRange.range[1] > 100 ? (lowRange.range[1] = 100) : ''" | |
| 233 | + @keydown.native="keydownRange($event)"></el-input>{{ lowRange.type != 0 ? "%" : "分" }}(含) | |
| 234 | + </el-form-item> | |
| 235 | + </el-form> | |
| 236 | + | |
| 237 | + <div class="dialog-footer" slot="footer" align="center" v-loading="loadingTange"> | |
| 238 | + <el-button type="danger" @click="_SavelowRange">保存</el-button> | |
| 239 | + <el-button @click="diaMinScore = false">取 消</el-button> | |
| 240 | + </div> | |
| 241 | + </el-dialog> | |
| 242 | + </div> | |
| 243 | + </div> | |
| 244 | +</template> | |
| 245 | +<script> | |
| 246 | +import ScoreSet from "./scoreSet.vue" | |
| 247 | +import { downloadFile, tablePrint } from "@/utils"; | |
| 248 | +export default { | |
| 249 | + components: { | |
| 250 | + ScoreSet | |
| 251 | + }, | |
| 252 | + props: { | |
| 253 | + role: "", | |
| 254 | + id: "", | |
| 255 | + title: String, | |
| 256 | + classId: String, | |
| 257 | + subjectName: String, | |
| 258 | + score: { | |
| 259 | + type: Number, | |
| 260 | + default: 0 | |
| 261 | + }, | |
| 262 | + }, | |
| 263 | + data() { | |
| 264 | + return { | |
| 265 | + status: 0,// 1:已归档试卷 | |
| 266 | + tableMaxHeight: 600, | |
| 267 | + loading: false, | |
| 268 | + exportLoading: false, | |
| 269 | + tabList: ["试题分析", "成绩排名", "小题分报表", "作答明细表"], | |
| 270 | + type: 0, | |
| 271 | + paperModifyLog: { //修改信息 | |
| 272 | + realName: "", | |
| 273 | + modifiedTime: "", | |
| 274 | + }, | |
| 275 | + examReport: { | |
| 276 | + subjectiveScore: 0, | |
| 277 | + subjectiveHighestScore: "", | |
| 278 | + subjectiveLowestScore: "", | |
| 279 | + subjectiveAvgScore: "", | |
| 280 | + subjectiveClassScoringRate: "", | |
| 281 | + objectiveScore: "", | |
| 282 | + objectiveHighestScore: "", | |
| 283 | + objectiveLowestScore: "", | |
| 284 | + objectiveAvgScore: "", | |
| 285 | + objectiveClassScoringRate: "", | |
| 286 | + examPaperScore: "", | |
| 287 | + highestScore: "", | |
| 288 | + lowestScore: "", | |
| 289 | + avgScore: "", | |
| 290 | + classScoringRate: "", | |
| 291 | + }, | |
| 292 | + tableData: [], | |
| 293 | + optionsList: [], | |
| 294 | + tableData2: [], | |
| 295 | + questionList: [], | |
| 296 | + page: 1, | |
| 297 | + size: 20, | |
| 298 | + total: 0, | |
| 299 | + // 设置低分值 | |
| 300 | + loadingTange: false, | |
| 301 | + diaMinScore: false, | |
| 302 | + lowRange: { | |
| 303 | + type: 0, | |
| 304 | + range: [60, 0], | |
| 305 | + }, | |
| 306 | + defaultLowRange: { | |
| 307 | + type: 0, | |
| 308 | + range: [], | |
| 309 | + }, | |
| 310 | + //答题录分 | |
| 311 | + diaScoreSet: false | |
| 312 | + }; | |
| 313 | + }, | |
| 314 | + created() { | |
| 315 | + this.status = this.$route.query.status ? this.$route.query.status : 0; | |
| 316 | + this._QueryData(); | |
| 317 | + }, | |
| 318 | + methods: { | |
| 319 | + print() { | |
| 320 | + if (this.type == 0) { | |
| 321 | + tablePrint("print-table", this.title + this.tabList[this.type], true); | |
| 322 | + } else { | |
| 323 | + tablePrint("print-content", this.title + this.tabList[this.type]); | |
| 324 | + } | |
| 325 | + }, | |
| 326 | + //打开答卷录分 | |
| 327 | + openScoreSet() { | |
| 328 | + this.diaScoreSet = true; | |
| 329 | + }, | |
| 330 | + //关闭设置分数 | |
| 331 | + closeScoreSet() { | |
| 332 | + this.diaScoreSet = false | |
| 333 | + }, | |
| 334 | + setType(type) { | |
| 335 | + this.tableMaxHeight = this.$refs.main.offsetHeight; | |
| 336 | + this.type = type; | |
| 337 | + }, | |
| 338 | + setSubPro(type) { | |
| 339 | + let tit; | |
| 340 | + switch (type) { | |
| 341 | + case 2: | |
| 342 | + tit = "单选题"; | |
| 343 | + break; | |
| 344 | + case 3: | |
| 345 | + tit = "多选题"; | |
| 346 | + break; | |
| 347 | + case 4: | |
| 348 | + tit = "判断题"; | |
| 349 | + break; | |
| 350 | + case 5: | |
| 351 | + tit = "主观题"; | |
| 352 | + break; | |
| 353 | + } | |
| 354 | + return tit; | |
| 355 | + }, | |
| 356 | + edit() { | |
| 357 | + this.$router.push({ | |
| 358 | + path: "/examinationPaperEdit", | |
| 359 | + query: { | |
| 360 | + paperId: this.id, | |
| 361 | + title: this.title, | |
| 362 | + type: 2, | |
| 363 | + }, | |
| 364 | + }); | |
| 365 | + }, | |
| 366 | + changePage(page) { | |
| 367 | + this.page = page; | |
| 368 | + this.examQuestionReport(); | |
| 369 | + }, | |
| 370 | + // 切换低分设置类型设置默认分值 | |
| 371 | + changeScore() { | |
| 372 | + this.lowRange.range = [...this.defaultLowRange.range]; | |
| 373 | + }, | |
| 374 | + // 禁止输入负数 | |
| 375 | + keydownRange(event) { | |
| 376 | + if (event.key == "-" || event.key == "e") { | |
| 377 | + event.returnValue = ""; | |
| 378 | + } | |
| 379 | + }, | |
| 380 | + // 关闭低分设置 | |
| 381 | + closeDiaMinScore() { | |
| 382 | + this.lowRange.type = this.defaultLowRange.type; | |
| 383 | + this.lowRange.range = [...this.defaultLowRange.range]; | |
| 384 | + }, | |
| 385 | + // 保存低分设置 | |
| 386 | + async _SavelowRange() { | |
| 387 | + if (this.lowRange.range[0].trim() == "" || this.lowRange.range[1].trim() == "") { | |
| 388 | + this.$message.warning("请补全低分设置!"); | |
| 389 | + return | |
| 390 | + } | |
| 391 | + this.loadingTange = true; | |
| 392 | + let { data, status, info } = await this.$request.setLowRange({ | |
| 393 | + classId: this.classId, | |
| 394 | + subjectName: this.subjectName, | |
| 395 | + ...this.lowRange, | |
| 396 | + }); | |
| 397 | + this.loadingTange = false; | |
| 398 | + if (status === 0) { | |
| 399 | + this.$message.success(info); | |
| 400 | + this.diaMinScore = false; | |
| 401 | + this.examDetail(); | |
| 402 | + } else { | |
| 403 | + this.$message.error(info); | |
| 404 | + } | |
| 405 | + }, | |
| 406 | + async _QueryData() { | |
| 407 | + this.examDetail(); | |
| 408 | + this.examStudentReport(); | |
| 409 | + this.examQuestionReport(); | |
| 410 | + }, | |
| 411 | + async examDetail() { | |
| 412 | + //详情 | |
| 413 | + this.loading = true; | |
| 414 | + const examDetail = this.role == "ROLE_PERSONAL" ? | |
| 415 | + this.$request.pExamDetail | |
| 416 | + : this.$request.examDetail; | |
| 417 | + | |
| 418 | + let { data, info, status } = await examDetail({ | |
| 419 | + examId: this.id, | |
| 420 | + }); | |
| 421 | + this.loading = false; | |
| 422 | + if (status === 0) { | |
| 423 | + if (data.paperModifyLog) { | |
| 424 | + this.paperModifyLog = { ...data?.paperModifyLog }; | |
| 425 | + } | |
| 426 | + this.examReport = { ...data?.examReport }; | |
| 427 | + this.defaultLowRange = data.lowRange || { | |
| 428 | + type: 1, | |
| 429 | + range: [60, 0], | |
| 430 | + }; | |
| 431 | + this.lowRange.type = this.defaultLowRange.type; | |
| 432 | + this.lowRange.range = [...this.defaultLowRange.range]; | |
| 433 | + } else { | |
| 434 | + this.$message.error(info); | |
| 435 | + } | |
| 436 | + }, | |
| 437 | + async _ReScore() { | |
| 438 | + //重新记分 | |
| 439 | + this.loading = true; | |
| 440 | + let { data, info, status } = await this.$request.reScore({ | |
| 441 | + examId: this.id, | |
| 442 | + }); | |
| 443 | + this.loading = false; | |
| 444 | + if (status === 0) { | |
| 445 | + this.$message.success(info); | |
| 446 | + this._QueryData(); | |
| 447 | + this.paperModifyLog.modifiedTime = ""; | |
| 448 | + this.paperModifyLog.realName = ""; | |
| 449 | + } else { | |
| 450 | + this.$message.error(info); | |
| 451 | + } | |
| 452 | + }, | |
| 453 | + async examStudentReport() { | |
| 454 | + //成绩排名-小题分-作答明细 | |
| 455 | + this.loading = true; | |
| 456 | + const examStudentReport = this.role == "ROLE_PERSONAL" ? | |
| 457 | + this.$request.pExamStudentReport | |
| 458 | + : this.$request.examStudentReport; | |
| 459 | + let { data, info, status } = await examStudentReport({ | |
| 460 | + examId: this.id, | |
| 461 | + }); | |
| 462 | + this.loading = false; | |
| 463 | + if (status === 0) { | |
| 464 | + let optionsList = []; | |
| 465 | + this.tableData2 = data?.list.map((item) => { | |
| 466 | + let params = {}; | |
| 467 | + | |
| 468 | + const detail = JSON.parse(item.detail); | |
| 469 | + if (detail.length > optionsList.length) { | |
| 470 | + optionsList = [...detail]; | |
| 471 | + } | |
| 472 | + detail.map((items, index) => { | |
| 473 | + params["que" + items.id] = items.id; | |
| 474 | + params["score" + items.id] = String(items.score).includes(".") | |
| 475 | + ? Number(items.score) | |
| 476 | + : items.score; | |
| 477 | + params["answer" + items.id] = | |
| 478 | + items.answer == 1 ? "✓" : items.answer == 2 ? "✗" : items.answer; | |
| 479 | + params["isRight" + items.id] = items.isRight; | |
| 480 | + params["questionType" + items.id] = items.questionType; | |
| 481 | + }); | |
| 482 | + return { | |
| 483 | + ...item, | |
| 484 | + ...params, | |
| 485 | + }; | |
| 486 | + }); | |
| 487 | + console.log(); | |
| 488 | + this.questionList = optionsList.sort((a, b) => { | |
| 489 | + return a.id - b.id; | |
| 490 | + }); | |
| 491 | + } else { | |
| 492 | + this.$message.error(info); | |
| 493 | + } | |
| 494 | + }, | |
| 495 | + async examQuestionReport() { | |
| 496 | + //试题分析 | |
| 497 | + this.loading = true; | |
| 498 | + const examQuestionReport = this.role == "ROLE_PERSONAL" ? | |
| 499 | + this.$request.pExamQuestionReport | |
| 500 | + : this.$request.examQuestionReport; | |
| 501 | + | |
| 502 | + let { data, info, status } = await examQuestionReport({ | |
| 503 | + examId: this.id, | |
| 504 | + page: this.page, | |
| 505 | + // size: this.size, | |
| 506 | + size: 9999, | |
| 507 | + }); | |
| 508 | + this.loading = false; | |
| 509 | + if (status === 0) { | |
| 510 | + let optionsList = [{}, {}, {}, {}, {}]; | |
| 511 | + let tableData = data?.list.map((item) => { | |
| 512 | + let params = {}; | |
| 513 | + const detail = JSON.parse(item.detail); | |
| 514 | + let lastOPtion = detail?.find((item) => { | |
| 515 | + return item.option == "未答"; | |
| 516 | + }); | |
| 517 | + let defaultArr = detail?.filter((item) => { | |
| 518 | + return item.option != "未答"; | |
| 519 | + }); | |
| 520 | + | |
| 521 | + optionsList.map((items, index) => { | |
| 522 | + if (index != 4) { | |
| 523 | + params["count" + index] = | |
| 524 | + defaultArr[index]?.option != "未答" | |
| 525 | + ? defaultArr[index]?.count | |
| 526 | + : ""; | |
| 527 | + params["persent" + index] = | |
| 528 | + defaultArr[index]?.option != "未答" | |
| 529 | + ? defaultArr[index]?.persent | |
| 530 | + : ""; | |
| 531 | + params["option" + index] = | |
| 532 | + defaultArr[index]?.option != "未答" | |
| 533 | + ? defaultArr[index]?.option == 1 | |
| 534 | + ? "✓" | |
| 535 | + : defaultArr[index]?.option == 2 | |
| 536 | + ? "✗" | |
| 537 | + : defaultArr[index]?.option | |
| 538 | + : ""; | |
| 539 | + items["title"] = "选项" + (index + 1); | |
| 540 | + } else { | |
| 541 | + items["title"] = "未答"; | |
| 542 | + params["count" + index] = lastOPtion.count; | |
| 543 | + params["persent" + index] = lastOPtion.persent; | |
| 544 | + params["option" + index] = "?"; | |
| 545 | + } | |
| 546 | + }); | |
| 547 | + return { | |
| 548 | + ...item, | |
| 549 | + ...params, | |
| 550 | + }; | |
| 551 | + }); | |
| 552 | + this.tableData = tableData.sort((a, b) => { | |
| 553 | + return a.questionIndex - b.questionIndex; | |
| 554 | + }); | |
| 555 | + this.optionsList = [...optionsList]; | |
| 556 | + this.total = data.count; | |
| 557 | + this.setType(0); | |
| 558 | + } else { | |
| 559 | + this.$message.error(info); | |
| 560 | + } | |
| 561 | + }, | |
| 562 | + //导出 | |
| 563 | + async exportData() { | |
| 564 | + if (this.exportLoading == true) return; | |
| 565 | + this.exportLoading = true; | |
| 566 | + const data = await this.$request.exportExamReport({ | |
| 567 | + examId: this.id, | |
| 568 | + }); | |
| 569 | + this.exportLoading = false; | |
| 570 | + if (data) { | |
| 571 | + let blob = new Blob([data], { | |
| 572 | + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| 573 | + }); | |
| 574 | + downloadFile( | |
| 575 | + this.status | |
| 576 | + ? "即时测-已归档单卷测练报表.xlsx" | |
| 577 | + : "即时测-单卷测练报表.xlsx", | |
| 578 | + blob | |
| 579 | + ); | |
| 580 | + } else { | |
| 581 | + this.$message.error("下载失败"); | |
| 582 | + } | |
| 583 | + }, | |
| 584 | + }, | |
| 585 | +}; | |
| 586 | +</script> | |
| 587 | +<style> | |
| 588 | +div::-webkit-scrollbar { | |
| 589 | + width: 3px; | |
| 590 | + height: 10px; | |
| 591 | +} | |
| 592 | + | |
| 593 | +div::-webkit-scrollbar-thumb { | |
| 594 | + border-radius: 10px; | |
| 595 | + background-color: #ccc; | |
| 596 | +} | |
| 597 | +</style> | |
| 598 | +<style lang="scss" scoped> | |
| 599 | +.hide { | |
| 600 | + display: none; | |
| 601 | +} | |
| 602 | + | |
| 603 | +.page-container { | |
| 604 | + position: relative; | |
| 605 | + height: 100%; | |
| 606 | + padding: 20px; | |
| 607 | + | |
| 608 | + .table-box { | |
| 609 | + min-height: 100%; | |
| 610 | + } | |
| 611 | + | |
| 612 | + &.active { | |
| 613 | + overflow: hidden; | |
| 614 | + } | |
| 615 | + | |
| 616 | + .edit-dia { | |
| 617 | + position: absolute; | |
| 618 | + left: 0; | |
| 619 | + top: 0; | |
| 620 | + right: 0; | |
| 621 | + bottom: 0; | |
| 622 | + width: 100%; | |
| 623 | + height: calc(100vh - 70px); | |
| 624 | + background: #fff; | |
| 625 | + overflow-y: auto; | |
| 626 | + z-index: 10; | |
| 627 | + } | |
| 628 | +} | |
| 629 | + | |
| 630 | +.persent { | |
| 631 | + white-space: nowrap; | |
| 632 | +} | |
| 633 | + | |
| 634 | +.error { | |
| 635 | + color: #f30; | |
| 636 | +} | |
| 637 | + | |
| 638 | +.page-content { | |
| 639 | + padding: 20px 20px 0; | |
| 640 | +} | |
| 641 | + | |
| 642 | +.tips { | |
| 643 | + height: 48px; | |
| 644 | + box-sizing: border-box; | |
| 645 | + line-height: 48px; | |
| 646 | + padding: 0 16px; | |
| 647 | + border: 1px solid #fac7cc; | |
| 648 | + border-radius: 5px; | |
| 649 | + background-color: #ffebec; | |
| 650 | + font-size: 14px; | |
| 651 | + color: #fd9795; | |
| 652 | + margin: 10px 20px 0 20px; | |
| 653 | + display: flex; | |
| 654 | + | |
| 655 | + &-p { | |
| 656 | + flex: 1; | |
| 657 | + } | |
| 658 | + | |
| 659 | + .fa-bell-o { | |
| 660 | + font-size: 18px; | |
| 661 | + margin-right: 5px; | |
| 662 | + } | |
| 663 | +} | |
| 664 | + | |
| 665 | +.tab-box { | |
| 666 | + width: 800px; | |
| 667 | + margin: 0 auto 12px; | |
| 668 | + background: #f8f8f8; | |
| 669 | + border-radius: 20px; | |
| 670 | + display: flex; | |
| 671 | + user-select: none; | |
| 672 | + | |
| 673 | + .tab-item { | |
| 674 | + flex: 1; | |
| 675 | + height: 40px; | |
| 676 | + line-height: 40px; | |
| 677 | + text-align: center; | |
| 678 | + font-size: 16px; | |
| 679 | + color: #666; | |
| 680 | + font-weight: 500; | |
| 681 | + background: transparent; | |
| 682 | + border-radius: 20px; | |
| 683 | + cursor: pointer; | |
| 684 | + | |
| 685 | + &.active { | |
| 686 | + background: #667ffd; | |
| 687 | + color: #fff; | |
| 688 | + } | |
| 689 | + } | |
| 690 | +} | |
| 691 | + | |
| 692 | +.down { | |
| 693 | + padding-top: 20px; | |
| 694 | + width: 100%; | |
| 695 | + display: flex; | |
| 696 | + justify-content: space-between; | |
| 697 | +} | |
| 698 | + | |
| 699 | +.hui-box { | |
| 700 | + display: flex; | |
| 701 | + text-align: center; | |
| 702 | + | |
| 703 | + .s-txt { | |
| 704 | + width: 61px; | |
| 705 | + line-height: 144px; | |
| 706 | + background: #e2e2e2; | |
| 707 | + font-size: 16px; | |
| 708 | + color: #fff; | |
| 709 | + font-weight: 700; | |
| 710 | + } | |
| 711 | + | |
| 712 | + .hui-ul { | |
| 713 | + border-top: 1px solid #e2e2e2; | |
| 714 | + } | |
| 715 | + | |
| 716 | + .hui-li { | |
| 717 | + display: flex; | |
| 718 | + | |
| 719 | + .hui-s { | |
| 720 | + height: 48px; | |
| 721 | + line-height: 48px; | |
| 722 | + border-right: 1px solid #e2e2e2; | |
| 723 | + border-bottom: 1px solid #e2e2e2; | |
| 724 | + box-sizing: border-box; | |
| 725 | + } | |
| 726 | + | |
| 727 | + .s1 { | |
| 728 | + width: 100px; | |
| 729 | + } | |
| 730 | + | |
| 731 | + .s2 { | |
| 732 | + width: 110px; | |
| 733 | + } | |
| 734 | + | |
| 735 | + .s3 { | |
| 736 | + width: 120px; | |
| 737 | + } | |
| 738 | + } | |
| 739 | +} | |
| 740 | + | |
| 741 | +// 设置低分值 | |
| 742 | +.content-header { | |
| 743 | + width: 100%; | |
| 744 | + position: relative; | |
| 745 | + | |
| 746 | + .setMinScore { | |
| 747 | + position: absolute; | |
| 748 | + bottom: 0; | |
| 749 | + right: 0px; | |
| 750 | + } | |
| 751 | +} | |
| 752 | + | |
| 753 | +.score-ipt { | |
| 754 | + width: 80px; | |
| 755 | + margin: 0 5px; | |
| 756 | +} | |
| 757 | +</style> | |
| 0 | 758 | \ No newline at end of file | ... | ... |