Commit 86201e49a31aabcf2ceea94015f500b285e76a2a

Authored by 梁保满
1 parent 53300a7b

即时测模块

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
... ...