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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 758 | \ No newline at end of file |