Просмотр исходного кода

add:考试详情新增分页、筛选、统计功能

luoaoxuan 1 год назад
Родитель
Сommit
4030d217c5

+ 699 - 432
src/views/platform/examination/exam/detail.vue

@@ -1,149 +1,167 @@
 <template>
-    <el-dialog
-        :title="title"
-        :visible.sync="dialogVisible"
-        :close-on-click-modal="false"
-        :close-on-press-escape="false"
-        :show-close="false"
-        append-to-body
-        fullscreen
-        class="dialog paper-detail-dialog"
-        top="0"
-        @open="loadData"
-        @close="closeDialog"
-    >
-        <div class="container">
-            <div class="paper-info">
-                <div class="info-item">
-                    <span class="label">考试名称:</span>
-                    <span class="value">{{ paperData.examName }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">创建人:</span>
-                    <span class="value">{{ transformUser(paperData.createBy) }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">创建时间:</span>
-                    <span class="value">{{ paperData.createTime }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">发布时间:</span>
-                    <span class="value">{{ paperData.publishDate }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">限考时间:</span>
-                    <span class="value">{{ paperData.limitDate }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">考试时长:</span>
-                    <span class="value">{{ transformTime(paperData.duration) }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">限考次数:</span>
-                    <span class="value">{{ paperData.limitCount }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">参考人数:</span>
-                    <span class="value">{{ paperList.length }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">题库名称:</span>
-                    <span class="value">{{ paperData.bankName }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">题目数量:</span>
-                    <span class="value">{{ paperData.questionCount }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">总分:</span>
-                    <span class="value">{{ paperData.totalScore }}分</span>
-                </div>
-                <div class="info-item">
-                    <el-tooltip effect="dark" content="达标分值占总分的比率" placement="top">
-                        <span class="label">达标占比:</span>
-                    </el-tooltip>
-                    <span class="value">{{ paperData.qualifiedRadio }}%</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">最高分:</span>
-                    <span class="value">{{ maxScore }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">最低分:</span>
-                    <span class="value">{{ minScore }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">平均分:</span>
-                    <span class="value">{{ avgScore }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">计分方式:</span>
-                    <span class="value">{{ paperData.scoringType }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">达标率:</span>
-                    <span class="value">{{ passRate }}</span>
-                </div>
-            </div>
-            <div class="paper-table">
-                <el-table
-                    ref="elTable"
-                    :data="paperList"
-                    border
-                    stripe
-                    highlight-current-row
-                    header-row-class-name="exam-table-header"
-                    style="width: 100%"
-                    class="exam-table"
-                    @row-dblclick="handleRowDblclick"
-                >
-                    <el-table-column label="序号" type="index" width="50" />
-                    <el-table-column
-                        prop="userName"
-                        label="考生姓名"
-                        width="100"
-                    />
-                    <el-table-column
-                        prop="count"
-                        label="参考次数/完成次数"
-                        width="90"
-                    />
-                    <el-table-column
-                        prop=""
-                        label="考试成绩"
-                    >
-                        <el-table-column
-                            prop="max"
-                            label="最高得分"
-                            width="90"
-                        />
-                        <el-table-column
-                            prop="min"
-                            label="最低得分"
-                            width="90"
-                        />
-                        <el-table-column
-                            prop="avg"
-                            label="平均得分"
-                            width="90"
-                        />
-                        <el-table-column
-                            prop="latest"
-                            label="最近得分"
-                            width="90"
-                        />
-                    </el-table-column>
-                    <el-table-column
-                        prop="latest"
-                        label="最终成绩"
-                        width="90"
-                    />
-                    <el-table-column prop="isQualified" label="是否达标">
-                        <template slot-scope="scope">
-                            <el-tag :type="getTagType(scope.row)" size="medium" class="score">{{ scope.row.isQualified }}</el-tag>
-                        </template>
-                    </el-table-column>
-                    <!-- <el-table-column
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    fullscreen
+    class="dialog paper-detail-dialog"
+    top="0"
+    @open="loadData"
+    @close="closeDialog"
+  >
+    <div class="container">
+      <div class="paper-info">
+        <div class="info-item">
+          <span class="label">考试名称:</span>
+          <span class="value">{{ paperData.examName }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">创建人:</span>
+          <span class="value">{{ transformUser(paperData.createBy) }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">创建时间:</span>
+          <span class="value">{{ paperData.createTime }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">发布时间:</span>
+          <span class="value">{{ paperData.publishDate }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">限考时间:</span>
+          <span class="value">{{ paperData.limitDate }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">考试时长:</span>
+          <span class="value">{{ transformTime(paperData.duration) }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">限考次数:</span>
+          <span class="value">{{ paperData.limitCount }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">题库名称:</span>
+          <span class="value">{{ paperData.bankName }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">题目数量:</span>
+          <span class="value">{{ paperData.questionCount }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">总分:</span>
+          <span class="value">{{ paperData.totalScore }}分</span>
+        </div>
+        <div class="info-item">
+          <el-tooltip
+            effect="dark"
+            content="达标分值占总分的比率"
+            placement="top"
+          >
+            <span class="label">达标占比:</span>
+          </el-tooltip>
+          <span class="value">{{ paperData.qualifiedRadio }}%</span>
+        </div>
+        <div class="info-item">
+          <span class="label">最高分:</span>
+          <span class="value">{{ maxScore }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">最低分:</span>
+          <span class="value">{{ minScore }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">平均分:</span>
+          <span class="value">{{ avgScore }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">计分方式:</span>
+          <span class="value">{{ paperData.scoringType }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">应参考人数:</span>
+          <span class="value">{{ paperList.length }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">实参考人数:</span>
+          <span class="value">{{ joinRate.count }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">参考率:</span>
+          <span class="value">{{ joinRate.rate }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">达标率:</span>
+          <span class="value">{{ passRate }}</span>
+        </div>
+      </div>
+      <div class="paper-status">
+        <div>
+          考试状态:<el-select
+            v-model="selectValue"
+            placeholder="请选择查询状态"
+            @change="onSelectChange"
+          >
+            <el-option
+              v-for="item in paperOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </div>
+
+        <el-button type="primary" @click="showStatistics"
+          >各部门数据统计</el-button
+        >
+      </div>
+      <div class="paper-table">
+        <el-table
+          ref="elTable"
+          :data="showPaperList"
+          border
+          stripe
+          highlight-current-row
+          header-row-class-name="exam-table-header"
+          style="width: 100%"
+          class="exam-table"
+          @row-dblclick="handleRowDblclick"
+        >
+          <el-table-column label="序号" type="index" width="50" />
+          <el-table-column prop="userName" label="考生姓名" width="100" />
+          <el-table-column prop="positionId" label="部门" width="100">
+            <template slot-scope="{ row }">
+              <ibps-user-selector
+                type="position"
+                :value="row.positionId"
+                readonlyText="text"
+                :disabled="true"
+                :multiple="true"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column prop="count" label="参考次数/完成次数" width="90" />
+          <el-table-column prop="" label="考试成绩">
+            <el-table-column prop="max" label="最高得分" width="90" />
+            <el-table-column prop="min" label="最低得分" width="90" />
+            <el-table-column prop="avg" label="平均得分" width="90" />
+            <el-table-column prop="latest" label="最近得分" width="90" />
+          </el-table-column>
+          <el-table-column prop="resultScore" label="最终成绩" width="90" />
+          <el-table-column prop="isQualified" label="是否达标">
+            <template slot-scope="scope">
+              <el-tag
+                :type="getTagType(scope.row)"
+                size="medium"
+                class="score"
+                >{{ scope.row.isQualified }}</el-tag
+              >
+            </template>
+          </el-table-column>
+          <!-- <el-table-column
                         fixed="right"
                         label="操作"
                         width="60"
@@ -152,317 +170,566 @@
                             <el-button type="text" size="small" @click="handleRowDblclick(scope.row)">详情</el-button>
                         </template>
                     </el-table-column> -->
-                </el-table>
-            </div>
-        </div>
-        <div slot="footer" class="el-dialog--center">
-            <ibps-toolbar
-                :actions="toolbars"
-                @action-event="handleActionEvent"
-            />
-        </div>
-        <paper-detail
-            v-if="paperDialogVisible"
-            :id="paperId"
-            :visible.sync="paperDialogVisible"
-            :bank-id="bankId"
-            :exam-id="examId"
-            :examinee-id="examineeId"
-            @close="paperDialogVisible = false"
-        />
-    </el-dialog>
+        </el-table>
+
+        <el-pagination
+          style="margin-top: 5px; padding-bottom: 10px"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          :current-page="currentPage"
+          :page-sizes="[10, 15, 20, 25, 30, 50]"
+          :page-size="pageSize"
+          layout="prev,pager,next,jumper,sizes,->,total"
+          :total="filterPaperList.length"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <div slot="footer" class="el-dialog--center">
+      <ibps-toolbar :actions="toolbars" @action-event="handleActionEvent" />
+    </div>
+    <paper-detail
+      v-if="paperDialogVisible"
+      :id="paperId"
+      :visible.sync="paperDialogVisible"
+      :bank-id="bankId"
+      :exam-id="examId"
+      :examinee-id="examineeId"
+      @close="paperDialogVisible = false"
+    />
+    <Statistics
+      ref="statisticsRef"
+      :departmentData="departmentData"
+    ></Statistics>
+  </el-dialog>
 </template>
 
 <script>
-import { max, min, mean, sum, maxBy, minBy, meanBy, round } from 'lodash'
+import IbpsUserSelector from "@/business/platform/org/selector";
+// 数据统计组件
+import Statistics from "./statistics.vue";
+import { max, min, mean, sum, maxBy, minBy, meanBy, round } from "lodash";
 const qualifiedType = [
-    {
-        value: '达标',
-        type: 'success'
+  {
+    value: "达标",
+    type: "success",
+  },
+  {
+    value: "未达标",
+    type: "danger",
+  },
+  {
+    value: "考试未结束",
+    type: "warning",
+  },
+];
+export default {
+  components: {
+    IbpsImage: () => import("@/business/platform/file/image"),
+    PaperDetail: () => import("../questionBank/detail"),
+    IbpsUserSelector,
+    Statistics,
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false,
     },
-    {
-        value: '未达标',
-        type: 'danger'
+    bankId: {
+      type: String,
+      default: "",
     },
-    {
-        value: '考试未结束',
-        type: 'warning'
-    }
-]
-export default {
-    components: {
-        IbpsImage: () => import('@/business/platform/file/image'),
-        PaperDetail: () => import('../questionBank/detail')
+    examId: {
+      type: String,
+      default: "",
     },
-    props: {
-        visible: {
-            type: Boolean,
-            default: false
+    id: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      title: "考试详情",
+      dialogVisible: this.visible,
+      paperDialogVisible: false,
+      toolbars: [{ key: "cancel", label: "退出" }],
+      paperList: [],
+      paperData: [],
+      paperId: "",
+      examineeId: "",
+      maxScore: "",
+      minScore: "",
+      avgScore: "",
+      passRate: "",
+      paperOptions: [
+        {
+          value: "all",
+          label: "全部",
+        },
+        {
+          value: "good",
+          label: "达标",
         },
-        bankId: {
-            type: String,
-            default: ''
+        {
+          value: "bad",
+          label: "未达标",
         },
-        examId: {
-            type: String,
-            default: ''
+        {
+          value: "noFinish",
+          label: "考试未结束",
         },
-        id: {
-            type: String,
-            default: ''
+      ],
+      selectValue: "all",
+      pageSize: 10,
+      currentPage: 1,
+      departmentData: [], // 数据统计
+    };
+  },
+  watch: {
+    visible: {
+      handler(val, oldVal) {
+        this.dialogVisible = this.visible;
+      },
+    },
+  },
+  mounted() {
+    this.loadData();
+
+    // 监听键盘事件
+    window.addEventListener("keyup", this.handleKeyPress);
+  },
+  beforeDestroy() {
+    window.removeEventListener("keyup", this.handleKeyPress);
+  },
+  methods: {
+    // 获取题库数据
+    async loadData() {
+      if (!this.examId) {
+        this.$message.error("获取题目信息失败,请重试!");
+        this.closeDialog();
+        return;
+      }
+      this.paperList = await this.getQuestionData();
+      console.log(this.paperList);
+      this.paperData = this.paperList[0];
+
+      this.posData();
+    },
+    // 获取部门数据
+    posData() {
+      this.paperList.forEach((item) => {
+        let user = this.$store.getters.userList.find(
+          (u) => u.userName === item.userName
+        );
+        item.positionId = user.positionId;
+      });
+    },
+
+    handleActionEvent({ key }) {
+      switch (key) {
+        case "cancel":
+          this.closeDialog();
+          break;
+        default:
+          break;
+      }
+    },
+    handleKeyPress(event) {
+      if (event.keyCode === 27 || event.key === "Esc") {
+        this.closeDialog();
+      }
+    },
+    getTagType(row) {
+      const temp = qualifiedType.find((i) => i.value === row.isQualified);
+      return temp ? temp.type : "default";
+    },
+    transformUser(userId) {
+      const { userList = [] } = this.$store.getters;
+      const user = userList.find((u) => u.userId === userId) || {};
+      return user.userName || "-";
+    },
+    transformTime(timeStamp) {
+      if (timeStamp === "不限" || !timeStamp) return "不限";
+      const hours = Math.floor(parseInt(timeStamp) / 3600000);
+      const minutes = Math.floor((parseInt(timeStamp) % 3600000) / 60000);
+      return hours + "小时" + minutes + "分钟";
+    },
+    getPassRate(list) {
+      const passScore =
+        (parseFloat(list[0].qualifiedRadio) / 100) *
+        parseFloat(list[0].totalScore);
+      const passList = list.filter((i) => i.resultScore >= passScore);
+      return ((passList.length / list.length) * 100).toFixed(2) + "%";
+    },
+    getQuestionData() {
+      const sql = `select qb.ti_ku_ming_cheng_ as bankName, ex.id_ as examId, ex.ti_ku_id_ as bankId, e.id_ as paperId, ex.zhuang_tai_ as examState, e.zhuang_tai_ as paperState, qb.ti_shu_ as questionCount, qb.zong_fen_ as totalScore, ex.kao_shi_ming_chen as examName, ex.can_kao_ren_yuan_ as examinee, e.kao_shi_ren_ as examineeId, ex.create_by_ as createBy, ex.chuang_jian_shi_j as createTime, ex.fa_bu_shi_jian_ as publishDate, ex.xian_kao_shi_jian as limitDate, ex.kao_shi_shi_chang as duration, ex.xian_kao_ci_shu_ as limitCount, ex.da_biao_zhan_bi_ as qualifiedRadio, ex.ji_fen_fang_shi_ as scoringType, ex.yun_xu_bao_ming_ as allowRegist, ex.kao_shi_miao_shu_ as examDesc, e.de_fen_ as score, e.bao_ming_shi_jian as applyTime, e.kai_shi_shi_jian_ as startTime, e.jie_shu_shi_jian_ as endTime from t_exams ex, t_question_bank qb, t_examination e where ex.ti_ku_id_ = qb.id_ and e.exam_id_ = ex.id_ and ex.id_ = '${this.examId}' order by e.kao_shi_ren_ desc, e.jie_shu_shi_jian_ desc`;
+      return new Promise((resolve, reject) => {
+        this.$common
+          .request("sql", sql)
+          .then((res) => {
+            const { data = [] } = res.variables || {};
+            if (!data.length) {
+              this.$message.warning("未查询到已提交的考试记录,请先完成考试!");
+              this.closeDialog();
+              return;
+            }
+            const result = [];
+            const scorrType = {
+              最高分: "max",
+              平均分: "avg",
+              最近得分: "latest",
+            };
+            data.forEach((item) => {
+              const index = result.findIndex(
+                (i) => i.examineeId === item.examineeId
+              );
+              if (index === -1) {
+                result.push({
+                  ...item,
+                  totalCount: data.length,
+                  totalScore: parseFloat(item.totalScore),
+                  statusList: [item.paperState],
+                  scoreList: [parseFloat(item.score || -1)],
+                });
+              } else {
+                result[index].scoreList.push(parseFloat(item.score || -1));
+                result[index].statusList.push(item.paperState);
+              }
+            });
+            const nodatadesc = "/";
+            result.forEach((item, index) => {
+              const finishScore = item.scoreList.filter((i) => i !== -1);
+              item.userName = this.transformUser(item.examineeId);
+              item.max = finishScore.length
+                ? round(max(finishScore), 2)
+                : nodatadesc;
+              item.min = finishScore.length
+                ? round(min(finishScore), 2)
+                : nodatadesc;
+              item.avg = finishScore.length
+                ? round(mean(finishScore), 2)
+                : nodatadesc;
+              item.sum = finishScore.length
+                ? round(sum(finishScore), 2)
+                : nodatadesc;
+              item.latest = finishScore.length
+                ? round(finishScore[0], 2)
+                : nodatadesc;
+              item.examCount = item.scoreList.length;
+              item.finishCount = finishScore.length;
+              item.count = `${item.examCount}/${item.finishCount}`;
+              item.examStatus =
+                item.examCount === item.finishCount ? "已完成" : "未完成";
+              item.resultScore = item[scorrType[item.scoringType]];
+              item.isQualified =
+                item.examStatus === "已完成"
+                  ? item.resultScore >=
+                    (parseFloat(item.qualifiedRadio) / 100) *
+                      parseFloat(item.totalScore)
+                    ? "达标"
+                    : "未达标"
+                  : "考试未结束";
+            });
+            const finishList = result.filter((i) => i.examStatus === "已完成");
+            this.maxScore = finishList.length
+              ? maxBy(finishList, "max").max
+              : nodatadesc;
+            this.minScore = finishList.length
+              ? minBy(finishList, "min").min
+              : nodatadesc;
+            this.avgScore = finishList.length
+              ? meanBy(finishList, "avg").toFixed(2)
+              : nodatadesc;
+            this.passRate = finishList.length
+              ? this.getPassRate(finishList)
+              : nodatadesc;
+            resolve(result);
+          })
+          .catch((error) => {
+            reject(error);
+          });
+      });
+    },
+    handleRowDblclick(row) {
+      this.paperId = row.paperId;
+      this.examineeId = row.examineeId;
+      this.paperDialogVisible = true;
+    },
+    // 关闭当前窗口
+    closeDialog() {
+      window.removeEventListener("keyup", this.handleKeyPress);
+      this.$emit("close", false);
+    },
+    // 选择器状态改变
+    onSelectChange(select) {
+      this.pageSize = 10;
+      this.currentPage = 1;
+    },
+    // 当前页码改变
+    handleCurrentChange(val) {
+      this.currentPage = val;
+    },
+    // 页码选择器改变
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+    },
+    // position部门所有应参考人数
+    filterdepartmentData(position) {
+      let count = 0;
+      this.paperList.forEach((paper) => {
+        let pos = paper.positions.split(",");
+        for (let i = 0; i < pos.length; i++) {
+          if (position === pos[i]) {
+            count++;
+            break;
+          }
         }
+      });
+      return count;
     },
-    data () {
-        return {
-            title: '考试详情',
-            dialogVisible: this.visible,
-            paperDialogVisible: false,
-            toolbars: [
-                { key: 'cancel', label: '退出' }
-            ],
-            paperList: [],
-            paperData: [],
-            paperId: '',
-            examineeId: '',
-            maxScore: '',
-            minScore: '',
-            avgScore: '',
-            passRate: ''
+    // position部门所有已参考人数
+    filterJoindepartmentData(position) {
+      let count = 0;
+      this.paperList.forEach((paper) => {
+        let pos = paper.positions.split(",");
+        for (let i = 0; i < pos.length; i++) {
+          if (position === pos[i] && paper.count.split("/")[1] !== "0") {
+            count++;
+            break;
+          }
         }
+      });
+      return count;
     },
-    watch: {
-        visible: {
-            handler (val, oldVal) {
-                this.dialogVisible = this.visible
-            }
+    // position部门最高分
+    departmentMaxScore(pos) {
+      let res = 0;
+      this.paperList.forEach((paper) => {
+        let positionsArr = paper.positions.split(",");
+        let position = positionsArr.find((item) => item === pos);
+        if (position && paper.resultScore !== "/") {
+          res = Math.max(res, +paper.resultScore);
         }
+      });
+      return res;
     },
-    mounted () {
-        this.loadData()
-        // 监听键盘事件
-        window.addEventListener('keyup', this.handleKeyPress)
+    departmentMinScore(pos) {
+      let res = 1e9;
+      this.paperList.forEach((paper) => {
+        let positionsArr = paper.positions.split(",");
+        let position = positionsArr.find((item) => item === pos);
+        if (position && paper.resultScore !== "/") {
+          res = Math.min(res, +paper.resultScore);
+        }
+      });
+      return res === 1e9 ? 0 : res;
     },
-    beforeDestroy () {
-        window.removeEventListener('keyup', this.handleKeyPress)
+    // position部门达标人数
+    departmentPassRate(pos) {
+      let count = 0;
+      this.paperList.forEach((paper) => {
+        let positionsArr = paper.positions.split(",");
+        let position = positionsArr.find((item) => item === pos);
+        if (position && paper.isQualified === "达标") {
+          count++;
+        }
+      });
+      return count;
     },
-    methods: {
-        // 获取题库数据
-        async loadData () {
-            if (!this.examId) {
-                this.$message.error('获取题目信息失败,请重试!')
-                this.closeDialog()
-                return
-            }
-            this.paperList = await this.getQuestionData()
-            console.log(this.paperList)
-            this.paperData = this.paperList[0]
-        },
-        handleActionEvent ({ key }) {
-            switch (key) {
-                case 'cancel':
-                    this.closeDialog()
-                    break
-                default:
-                    break
-            }
-        },
-        handleKeyPress (event) {
-            if (event.keyCode === 27 || event.key === 'Esc') {
-                this.closeDialog()
-            }
-        },
-        getTagType (row) {
-            const temp = qualifiedType.find(i => i.value === row.isQualified)
-            return temp ? temp.type : 'default'
-        },
-        transformUser (userId) {
-            const { userList = [] } = this.$store.getters
-            const user = userList.find(u => u.userId === userId) || {}
-            return user.userName || '-'
-        },
-        transformTime (timeStamp) {
-            if (timeStamp === '不限' || !timeStamp) return '不限'
-            const hours = Math.floor(parseInt(timeStamp) / 3600000)
-            const minutes = Math.floor((parseInt(timeStamp) % 3600000) / 60000)
-            return hours + '小时' + minutes + '分钟'
-        },
-        getPassRate (list) {
-            const passScore = parseFloat(list[0].qualifiedRadio) / 100 * parseFloat(list[0].totalScore)
-            const passList = list.filter(i => i.resultScore >= passScore)
-            return (passList.length / list.length * 100).toFixed(2) + '%'
-        },
-        getQuestionData () {
-            const sql = `select qb.ti_ku_ming_cheng_ as bankName, ex.id_ as examId, ex.ti_ku_id_ as bankId, e.id_ as paperId, ex.zhuang_tai_ as examState, e.zhuang_tai_ as paperState, qb.ti_shu_ as questionCount, qb.zong_fen_ as totalScore, ex.kao_shi_ming_chen as examName, ex.can_kao_ren_yuan_ as examinee, e.kao_shi_ren_ as examineeId, ex.create_by_ as createBy, ex.chuang_jian_shi_j as createTime, ex.fa_bu_shi_jian_ as publishDate, ex.xian_kao_shi_jian as limitDate, ex.kao_shi_shi_chang as duration, ex.xian_kao_ci_shu_ as limitCount, ex.da_biao_zhan_bi_ as qualifiedRadio, ex.ji_fen_fang_shi_ as scoringType, ex.yun_xu_bao_ming_ as allowRegist, ex.kao_shi_miao_shu_ as examDesc, e.de_fen_ as score, e.bao_ming_shi_jian as applyTime, e.kai_shi_shi_jian_ as startTime, e.jie_shu_shi_jian_ as endTime from t_exams ex, t_question_bank qb, t_examination e where ex.ti_ku_id_ = qb.id_ and e.exam_id_ = ex.id_ and ex.id_ = '${this.examId}' order by e.kao_shi_ren_ desc, e.jie_shu_shi_jian_ desc`
-            return new Promise((resolve, reject) => {
-                this.$common.request('sql', sql).then(res => {
-                    const { data = [] } = res.variables || {}
-                    if (!data.length) {
-                        this.$message.warning('未查询到已提交的考试记录,请先完成考试!')
-                        this.closeDialog()
-                        return
-                    }
-                    const result = []
-                    const scorrType = {
-                        '最高分': 'max',
-                        '平均分': 'avg',
-                        '最近得分': 'latest'
-                    }
-                    data.forEach(item => {
-                        const index = result.findIndex(i => i.examineeId === item.examineeId)
-                        if (index === -1) {
-                            result.push({
-                                ...item,
-                                totalCount: data.length,
-                                totalScore: parseFloat(item.totalScore),
-                                statusList: [item.paperState],
-                                scoreList: [parseFloat(item.score || -1)]
-                            })
-                        } else {
-                            result[index].scoreList.push(parseFloat(item.score || -1))
-                            result[index].statusList.push(item.paperState)
-                        }
-                    })
-                    const nodatadesc = '/'
-                    result.forEach((item, index) => {
-                        const finishScore = item.scoreList.filter(i => i !== -1)
-                        item.userName = this.transformUser(item.examineeId)
-                        item.max = finishScore.length ? round(max(finishScore), 2) : nodatadesc
-                        item.min = finishScore.length ? round(min(finishScore), 2) : nodatadesc
-                        item.avg = finishScore.length ? round(mean(finishScore), 2) : nodatadesc
-                        item.sum = finishScore.length ? round(sum(finishScore), 2) : nodatadesc
-                        item.latest = finishScore.length ? round(finishScore[0], 2) : nodatadesc
-                        item.examCount = item.scoreList.length
-                        item.finishCount = finishScore.length
-                        item.count = `${item.examCount}/${item.finishCount}`
-                        item.examStatus = item.examCount === item.finishCount ? '已完成' : '未完成'
-                        item.resultScore = item[scorrType[item.scoringType]]
-                        item.isQualified = item.examStatus === '已完成' ? item.resultScore >= (parseFloat(item.qualifiedRadio) / 100 * parseFloat(item.totalScore)) ? '达标' : '未达标' : '考试未结束'
-                    })
-                    const finishList = result.filter(i => i.examStatus === '已完成')
-                    this.maxScore = finishList.length ? maxBy(finishList, 'max').max : nodatadesc
-                    this.minScore = finishList.length ? minBy(finishList, 'min').min : nodatadesc
-                    this.avgScore = finishList.length ? meanBy(finishList, 'avg').toFixed(2) : nodatadesc
-                    this.passRate = finishList.length ? this.getPassRate(finishList) : nodatadesc
-                    resolve(result)
-                }).catch(error => {
-                    reject(error)
-                })
-            })
-        },
-        handleRowDblclick (row) {
-            this.paperId = row.paperId
-            this.examineeId = row.examineeId
-            this.paperDialogVisible = true
-        },
-        // 关闭当前窗口
-        closeDialog () {
-            window.removeEventListener('keyup', this.handleKeyPress)
-            this.$emit('close', false)
+    // position部门分数和
+    departmentScoreSum(pos) {
+      let count = 0;
+      this.paperList.forEach((paper) => {
+        let positionsArr = paper.positions.split(",");
+        let position = positionsArr.find((item) => item === pos);
+        if (position && paper.resultScore !== "/") {
+          count += +paper.resultScore;
         }
-    }
-}
+      });
+      return count;
+    },
+    // 数据统计
+    showStatistics() {
+      this.paperList.forEach((paper) => {
+        let user = this.$store.getters.userList.find(
+          (user) => paper.userName === user.userName
+        );
+        if (user) {
+          paper["positions"] = user.positions;
+        }
+      });
+
+      let departmentData = [];
+      this.paperList.forEach((paper) => {
+        let pos = paper.positions.split(",");
+        for (let i = 0; i < pos.length; i++) {
+          departmentData.push(pos[i]);
+        }
+      });
+
+      departmentData = [...new Set(departmentData)];
+      this.departmentData = departmentData.map((item) => {
+        let plannedAttendees = this.filterdepartmentData(item);
+        let actualAttendees = this.filterJoindepartmentData(item);
+        let minScore = this.departmentMinScore(item);
+        let maxScore = this.departmentMaxScore(item);
+        let PassPeople = this.departmentPassRate(item);
+        let sumScore = this.departmentScoreSum(item);
+
+        return {
+          department: item,
+          plannedAttendees,
+          actualAttendees,
+          joinRate: actualAttendees / plannedAttendees,
+          minScore,
+          maxScore,
+          passRate: actualAttendees === 0 ? 0 : PassPeople / actualAttendees,
+          avgScore: actualAttendees === 0 ? 0 : sumScore / actualAttendees,
+        };
+      });
+      this.$refs.statisticsRef.open();
+    },
+  },
+  computed: {
+    filterPaperList() {
+      return this.paperList.filter((paper) => {
+        if (this.selectValue === "all") return this.paperList;
+        let { label } = this.paperOptions.find(
+          (option) => option.value === this.selectValue
+        );
+        return paper.isQualified === label;
+      });
+    },
+    showPaperList() {
+      let start = (this.currentPage - 1) * this.pageSize;
+      let end = start + this.pageSize;
+      return this.filterPaperList.slice(start, end);
+    },
+    joinRate() {
+      let count = 0;
+      this.paperList.forEach((item) => {
+        if (item.count && item.count.split("/")[1] !== "0") {
+          count++;
+        }
+      });
+      return {
+        count,
+        rate: ((count / this.paperList.length) * 100).toFixed(2) + "%",
+      };
+    },
+  },
+};
 </script>
 <style lang="scss" scoped>
-    .paper-detail-dialog {
-        ::v-deep {
-            .el-dialog__body {
-                position: relative;
-                height: calc(100vh - 115px);
-                width: 1080px;
-                margin: 0 auto;
-                box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-                .el-radio, .el-checkbox {
-                    display: block;
-                    margin-bottom: 10px;
-                    pointer-events: none;
-                    &:last-child {
-                        margin-bottom: 0;
-                    }
-                }
-                .el-radio-button {
-                    pointer-events: none;
-                }
-                .el-radio__label, .el-checkbox__label {
-                    font-size: 16px;
-                    white-space: normal;
-                    line-height: 1.5;
-                }
-                .el-input {
-                    margin-bottom: 10px;
-                    pointer-events: none;
-                    &:last-child {
-                        margin-bottom: 0;
-                    }
-                }
-                .el-tag {
-                    margin-right: 0;
-                }
-                .ibps-p-0 {
-                    margin: 0;
-                    .list-group {
-                        display: inline-block;
-                        width: 100%;
-                    }
-                }
-            }
-            .el-dialog__header {
-                text-align: center;
-            }
+.paper-detail-dialog {
+  ::v-deep {
+    .el-dialog__body {
+      position: relative;
+      height: calc(100vh - 115px);
+      width: 1080px;
+      margin: 0 auto;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+      .el-radio,
+      .el-checkbox {
+        display: block;
+        margin-bottom: 10px;
+        pointer-events: none;
+        &:last-child {
+          margin-bottom: 0;
         }
-        .container {
-            position: relative;
-            height: calc(100vh - 160px);
-            padding: 20px;
-            .paper-info {
-                display: flex;
-                flex-wrap: wrap;
-                justify-content: flex-start;
-                margin-bottom: 20px;
-                background-color: #f5f5f5;
-                border: 1px solid #ddd;
-                padding: 10px;
-                border-radius: 5px;
-                font-size: 16px;
-            }
+      }
+      .el-radio-button {
+        pointer-events: none;
+      }
+      .el-radio__label,
+      .el-checkbox__label {
+        font-size: 16px;
+        white-space: normal;
+        line-height: 1.5;
+      }
+      .el-input {
+        margin-bottom: 10px;
+        pointer-events: none;
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+      .el-tag {
+        margin-right: 0;
+      }
+      .ibps-p-0 {
+        margin: 0;
+        .list-group {
+          display: inline-block;
+          width: 100%;
+        }
+      }
+    }
+    .el-dialog__header {
+      text-align: center;
+    }
+  }
+  .container {
+    position: relative;
+    height: calc(100vh - 160px);
+    padding: 20px;
+    .paper-info {
+      display: flex;
+      flex-wrap: wrap;
+      justify-content: flex-start;
+      margin-bottom: 20px;
+      background-color: #f5f5f5;
+      border: 1px solid #ddd;
+      padding: 10px;
+      border-radius: 5px;
+      font-size: 16px;
+    }
 
-            .info-item {
-                width: 33.3%;
-                margin-bottom: 20px;
-                display: flex;
-                align-items: center;
-                .label {
-                    width: 100px;
-                    font-weight: bold;
-                    color: #333;
-                }
-                .value {
-                    flex: 1;
-                    color: #666;
-                    white-space: nowrap;
-                    overflow: hidden;
-                    text-overflow: ellipsis;
-                }
-            }
+    .info-item {
+      width: 33.3%;
+      margin-bottom: 20px;
+      display: flex;
+      align-items: center;
+      .label {
+        width: 100px;
+        font-weight: bold;
+        color: #333;
+      }
+      .value {
+        flex: 1;
+        color: #666;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
 
-            .paper-table {
-                .exam-table-header {
-                    text-align: center;
-                }
-                ::v-deep {
-                    .el-table {
-                        th {
-                            font-size: 14px !important;
-                            text-align: center;
-                        }
-                        td {
-                            font-size: 14px !important;
-                        }
-                        .el-table__row {
-                            cursor: pointer;
-                        }
-                    }
-                }
-            }
+    .paper-table {
+      .exam-table-header {
+        text-align: center;
+      }
+      ::v-deep {
+        .el-table {
+          th {
+            font-size: 14px !important;
+            text-align: center;
+          }
+          td {
+            font-size: 14px !important;
+          }
+          .el-table__row {
+            cursor: pointer;
+          }
         }
+      }
     }
+    .paper-status {
+      margin-bottom: 10px;
+      display: flex;
+      justify-content: space-between;
+    }
+  }
+}
 </style>

+ 533 - 443
src/views/platform/examination/exam/edit.vue

@@ -1,34 +1,34 @@
 <template>
-    <el-dialog
-        :title="title"
-        :visible.sync="dialogVisible"
-        :close-on-click-modal="false"
-        :close-on-press-escape="false"
-        append-to-body
-        width="60%"
-        class="dialog exam-dialog"
-        top="6vh"
-        @close="closeDialog"
-        @open="getExamData"
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    width="60%"
+    class="dialog exam-dialog"
+    top="6vh"
+    @close="closeDialog"
+    @open="getExamData"
+  >
+    <el-form
+      ref="form"
+      :label-width="formLabelWidth"
+      :model="form"
+      :rules="rules"
+      class="exam-form"
+      @submit.native.prevent
     >
-        <el-form
-            ref="form"
-            :label-width="formLabelWidth"
-            :model="form"
-            :rules="rules"
-            class="exam-form"
-            @submit.native.prevent
-        >
-            <el-form-item label="考试名称:" prop="kao_shi_ming_chen">
-                <el-input
-                    v-model="form.kao_shi_ming_chen"
-                    type="text"
-                    :maxlength="256"
-                    placeholder="请输入考试名称"
-                    :disabled="isDisabled"
-                />
-            </el-form-item>
-            <!-- <el-form-item label="考试类型:" prop="kao_shi_lei_xing_">
+      <el-form-item label="考试名称:" prop="kao_shi_ming_chen">
+        <el-input
+          v-model="form.kao_shi_ming_chen"
+          type="text"
+          :maxlength="256"
+          placeholder="请输入考试名称"
+          :disabled="isDisabled"
+        />
+      </el-form-item>
+      <!-- <el-form-item label="考试类型:" prop="kao_shi_lei_xing_">
                 <el-select
                     v-model="form.kao_shi_lei_xing_"
                     filterable
@@ -44,437 +44,527 @@
                     />
                 </el-select>
             </el-form-item> -->
-            <el-form-item label="考试题库:" prop="ti_ku_id_">
-                <ibps-custom-dialog
-                    v-model="form.ti_ku_id_"
-                    size="small"
-                    template-key="tkdhk"
-                    :multiple="false"
-                    :disabled="isDisabled"
-                    type="dialog"
-                    class="custom-dialog"
-                    placeholder="请选择考试题库"
-                />
-            </el-form-item>
-            <el-form-item prop="xian_kao_shi_jian">
-                <template slot="label">
-                    限考时间
-                    <el-tooltip effect="dark" content="设置该考试的提交截至时间。" placement="top">
-                        <i class="el-icon-question question-icon">:</i>
-                    </el-tooltip>
-                </template>
-                <el-radio-group v-model="form.isDateLimit" @change="changeLimit(form.isDateLimit, 'xian_kao_shi_jian', null)">
-                    <el-radio label="0">不限</el-radio>
-                    <el-radio label="1">限制</el-radio>
-                </el-radio-group>
-                <el-date-picker
-                    v-if="form.isDateLimit === '1'"
-                    v-model="form.xian_kao_shi_jian"
-                    type="datetime"
-                    placeholder="请选择考试限制提交时间"
-                    align="right"
-                    default-time="12:00:00"
-                    class="date-picker"
-                    :picker-options="pickerOptions"
-                />
-            </el-form-item>
-            <el-form-item label="参考人员:" prop="can_kao_ren_yuan_">
-                <ibps-custom-dialog
-                    v-model="form.can_kao_ren_yuan_"
-                    size="small"
-                    template-key="gjxcxryk"
-                    multiple
-                    :disabled="isDisabled"
-                    type="dialog"
-                    class="custom-dialog"
-                    placeholder="请选择需参加考试的人员"
-                />
-            </el-form-item>
-            <el-form-item prop="isCountLimit" class="inline-item">
-                <template slot="label">
-                    限考次数
-                    <el-tooltip effect="dark" content="限制是否可重复参加,以及可参加考试的最大次数。" placement="top">
-                        <i class="el-icon-question question-icon">:</i>
-                    </el-tooltip>
-                </template>
-                <el-radio-group
-                    v-model="form.isCountLimit"
-                    :disabled="isDisabled"
-                    @change="changeLimit(form.isCountLimit, 'xian_kao_ci_shu_', 1)"
-                >
-                    <el-radio label="0">不限</el-radio>
-                    <el-radio label="1">限制</el-radio>
-                </el-radio-group>
-                <div v-if="form.isCountLimit === '1'" class="time">
-                    <el-input-number
-                        v-model="form.xian_kao_ci_shu_"
-                        :min="1"
-                        :precision="0"
-                        :disabled="isDisabled"
-                        placeholder="请输入单个用户最大限考次数"
-                    />
-                    <div class="unit">次</div>
-                </div>
-            </el-form-item>
-            <el-form-item v-if="form.isCountLimit === '0' || form.isCountLimit === '1' && form.xian_kao_ci_shu_ > 1" prop="ji_fen_fang_shi_">
-                <template slot="label">
-                    计分方式
-                    <el-tooltip effect="dark" content="设置对于可重复参加的考试,最终得分的计算方式(仅限考次数大于1时有效)。" placement="top">
-                        <i class="el-icon-question question-icon">:</i>
-                    </el-tooltip>
-                </template>
-                <el-radio-group v-model="form.ji_fen_fang_shi_" :disabled="isDisabled">
-                    <el-radio label="平均分">平均分</el-radio>
-                    <el-radio label="最高分">最高分</el-radio>
-                    <el-radio label="最近得分">最近得分</el-radio>
-                </el-radio-group>
-            </el-form-item>
-            <el-form-item prop="isTimeLimit" class="inline-item">
-                <template slot="label">
-                    考试时长
-                    <el-tooltip effect="dark" content="设置该考试单次作答的最大时长。" placement="top">
-                        <i class="el-icon-question question-icon">:</i>
-                    </el-tooltip>
-                </template>
-                <el-radio-group v-model="form.isTimeLimit" :disabled="isDisabled">
-                    <el-radio label="0">不限</el-radio>
-                    <el-radio label="1">限制</el-radio>
-                </el-radio-group>
-                <template v-if="form.isTimeLimit === '1'">
-                    <div class="time">
-                        <el-input-number
-                            v-model="form.hours"
-                            :min="0"
-                            :max="72"
-                            :precision="0"
-                            :disabled="isDisabled"
-                        />
-                        <div class="unit">小时</div>
-                    </div>
-                    <div class="time">
-                        <el-input-number
-                            v-model="form.minutes"
-                            :min="0"
-                            :max="59"
-                            :precision="0"
-                            :disabled="isDisabled"
-                        />
-                        <div class="unit">分钟</div>
-                    </div>
-                </template>
-            </el-form-item>
-            <el-form-item prop="da_biao_zhan_bi_">
-                <template slot="label">
-                    达标分值占比
-                    <el-tooltip effect="dark" content="设置该考试的达标分数线占试题总分的百分比。" placement="top">
-                        <i class="el-icon-question question-icon">:</i>
-                    </el-tooltip>
-                </template>
-                <el-input-number
-                    v-model="form.da_biao_zhan_bi_"
-                    :min="50"
-                    :max="100"
-                    :precision="0"
-                    :disabled="isDisabled"
-                    placeholder="请输入达标分值占比"
-                />
-                <div class="unit">%</div>
-            </el-form-item>
-            <el-form-item prop="yun_xu_bao_ming_">
-                <template slot="label">
-                    允许自主报名
-                    <el-tooltip effect="dark" content="限制非本考试的参考人员是否可报名参加该考试。" placement="top">
-                        <i class="el-icon-question question-icon">:</i>
-                    </el-tooltip>
-                </template>
-                <el-radio-group v-model="form.yun_xu_bao_ming_" :disabled="isDisabled">
-                    <el-radio label="是">是</el-radio>
-                    <el-radio label="否">否</el-radio>
-                </el-radio-group>
-            </el-form-item>
-            <el-form-item v-if="!isDisabled || form.guan_lian_id_" prop="guan_lian_id_">
-                <template slot="label">
-                    关联培训记录
-                    <el-tooltip effect="dark" content="关联培训记录。" placement="top">
-                        <i class="el-icon-question question-icon">:</i>
-                    </el-tooltip>
-                </template>
-                <ibps-custom-dialog
-                    v-model="form.guan_lian_id_"
-                    size="small"
-                    template-key="pxjldhk"
-                    :multiple="false"
-                    :disabled="isDisabled"
-                    type="dialog"
-                    class="custom-dialog"
-                    placeholder="请选择培训记录"
-                />
-            </el-form-item>
-            <el-form-item v-if="!isDisabled || form.kao_shi_miao_shu_" label="考试描述:" prop="kao_shi_miao_shu_">
-                <el-input
-                    v-model="form.kao_shi_miao_shu_"
-                    type="textarea"
-                    :rows="4"
-                    :maxlength="512"
-                    placeholder="请输入描述内容"
-                    :disabled="isDisabled"
-                />
-            </el-form-item>
-        </el-form>
-        <div slot="footer" class="el-dialog--center">
-            <ibps-toolbar
-                :actions="toolbars"
-                @action-event="handleActionEvent"
+      <el-form-item label="考试题库:" prop="ti_ku_id_">
+        <div class="tiku">
+          <div>
+            <ibps-custom-dialog
+              v-model="form.ti_ku_id_"
+              size="small"
+              template-key="tkdhk"
+              :multiple="false"
+              :disabled="isDisabled"
+              type="dialog"
+              class="custom-dialog"
+              placeholder="请选择考试题库"
             />
+          </div>
+
+          <!-- <el-button
+            type="primary"
+            @click="randTiku"
+            :disabled="randButtonDisabled"
+            >随机题库</el-button
+          > -->
         </div>
-    </el-dialog>
+      </el-form-item>
+      <el-form-item prop="xian_kao_shi_jian">
+        <template slot="label">
+          限考时间
+          <el-tooltip
+            effect="dark"
+            content="设置该考试的提交截至时间。"
+            placement="top"
+          >
+            <i class="el-icon-question question-icon">:</i>
+          </el-tooltip>
+        </template>
+        <el-radio-group
+          v-model="form.isDateLimit"
+          @change="changeLimit(form.isDateLimit, 'xian_kao_shi_jian', null)"
+        >
+          <el-radio label="0">不限</el-radio>
+          <el-radio label="1">限制</el-radio>
+        </el-radio-group>
+        <el-date-picker
+          v-if="form.isDateLimit === '1'"
+          v-model="form.xian_kao_shi_jian"
+          type="datetime"
+          placeholder="请选择考试限制提交时间"
+          align="right"
+          default-time="12:00:00"
+          class="date-picker"
+          :picker-options="pickerOptions"
+        />
+      </el-form-item>
+      <el-form-item label="参考人员:" prop="can_kao_ren_yuan_">
+        <ibps-custom-dialog
+          v-model="form.can_kao_ren_yuan_"
+          size="small"
+          template-key="gjxcxryk"
+          multiple
+          :disabled="isDisabled"
+          type="dialog"
+          class="custom-dialog"
+          placeholder="请选择需参加考试的人员"
+        />
+      </el-form-item>
+      <el-form-item prop="isCountLimit" class="inline-item">
+        <template slot="label">
+          限考次数
+          <el-tooltip
+            effect="dark"
+            content="限制是否可重复参加,以及可参加考试的最大次数。"
+            placement="top"
+          >
+            <i class="el-icon-question question-icon">:</i>
+          </el-tooltip>
+        </template>
+        <el-radio-group
+          v-model="form.isCountLimit"
+          :disabled="isDisabled"
+          @change="changeLimit(form.isCountLimit, 'xian_kao_ci_shu_', 1)"
+        >
+          <el-radio label="0">不限</el-radio>
+          <el-radio label="1">限制</el-radio>
+        </el-radio-group>
+        <div v-if="form.isCountLimit === '1'" class="time">
+          <el-input-number
+            v-model="form.xian_kao_ci_shu_"
+            :min="1"
+            :precision="0"
+            :disabled="isDisabled"
+            placeholder="请输入单个用户最大限考次数"
+          />
+          <div class="unit">次</div>
+        </div>
+      </el-form-item>
+      <el-form-item
+        v-if="
+          form.isCountLimit === '0' ||
+          (form.isCountLimit === '1' && form.xian_kao_ci_shu_ > 1)
+        "
+        prop="ji_fen_fang_shi_"
+      >
+        <template slot="label">
+          计分方式
+          <el-tooltip
+            effect="dark"
+            content="设置对于可重复参加的考试,最终得分的计算方式(仅限考次数大于1时有效)。"
+            placement="top"
+          >
+            <i class="el-icon-question question-icon">:</i>
+          </el-tooltip>
+        </template>
+        <el-radio-group v-model="form.ji_fen_fang_shi_" :disabled="isDisabled">
+          <el-radio label="平均分">平均分</el-radio>
+          <el-radio label="最高分">最高分</el-radio>
+          <el-radio label="最近得分">最近得分</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item prop="isTimeLimit" class="inline-item">
+        <template slot="label">
+          考试时长
+          <el-tooltip
+            effect="dark"
+            content="设置该考试单次作答的最大时长。"
+            placement="top"
+          >
+            <i class="el-icon-question question-icon">:</i>
+          </el-tooltip>
+        </template>
+        <el-radio-group v-model="form.isTimeLimit" :disabled="isDisabled">
+          <el-radio label="0">不限</el-radio>
+          <el-radio label="1">限制</el-radio>
+        </el-radio-group>
+        <template v-if="form.isTimeLimit === '1'">
+          <div class="time">
+            <el-input-number
+              v-model="form.hours"
+              :min="0"
+              :max="72"
+              :precision="0"
+              :disabled="isDisabled"
+            />
+            <div class="unit">小时</div>
+          </div>
+          <div class="time">
+            <el-input-number
+              v-model="form.minutes"
+              :min="0"
+              :max="59"
+              :precision="0"
+              :disabled="isDisabled"
+            />
+            <div class="unit">分钟</div>
+          </div>
+        </template>
+      </el-form-item>
+      <el-form-item prop="da_biao_zhan_bi_">
+        <template slot="label">
+          达标分值占比
+          <el-tooltip
+            effect="dark"
+            content="设置该考试的达标分数线占试题总分的百分比。"
+            placement="top"
+          >
+            <i class="el-icon-question question-icon">:</i>
+          </el-tooltip>
+        </template>
+        <el-input-number
+          v-model="form.da_biao_zhan_bi_"
+          :min="50"
+          :max="100"
+          :precision="0"
+          :disabled="isDisabled"
+          placeholder="请输入达标分值占比"
+        />
+        <div class="unit">%</div>
+      </el-form-item>
+      <el-form-item prop="yun_xu_bao_ming_">
+        <template slot="label">
+          允许自主报名
+          <el-tooltip
+            effect="dark"
+            content="限制非本考试的参考人员是否可报名参加该考试。"
+            placement="top"
+          >
+            <i class="el-icon-question question-icon">:</i>
+          </el-tooltip>
+        </template>
+        <el-radio-group v-model="form.yun_xu_bao_ming_" :disabled="isDisabled">
+          <el-radio label="是">是</el-radio>
+          <el-radio label="否">否</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item
+        v-if="!isDisabled || form.guan_lian_id_"
+        prop="guan_lian_id_"
+      >
+        <template slot="label">
+          关联培训记录
+          <el-tooltip effect="dark" content="关联培训记录。" placement="top">
+            <i class="el-icon-question question-icon">:</i>
+          </el-tooltip>
+        </template>
+        <ibps-custom-dialog
+          v-model="form.guan_lian_id_"
+          size="small"
+          template-key="pxjldhk"
+          :multiple="false"
+          :disabled="isDisabled"
+          type="dialog"
+          class="custom-dialog"
+          placeholder="请选择培训记录"
+        />
+      </el-form-item>
+      <el-form-item
+        v-if="!isDisabled || form.kao_shi_miao_shu_"
+        label="考试描述:"
+        prop="kao_shi_miao_shu_"
+      >
+        <el-input
+          v-model="form.kao_shi_miao_shu_"
+          type="textarea"
+          :rows="4"
+          :maxlength="512"
+          placeholder="请输入描述内容"
+          :disabled="isDisabled"
+        />
+      </el-form-item>
+    </el-form>
+    <div slot="footer" class="el-dialog--center">
+      <ibps-toolbar :actions="toolbars" @action-event="handleActionEvent" />
+    </div>
+  </el-dialog>
 </template>
 
 <script>
-import ActionUtils from '@/utils/action'
-import { examTypeOptions, scoringType } from '../constants'
+import ActionUtils from "@/utils/action";
+import { examTypeOptions, scoringType } from "../constants";
 export default {
-    components: {
-        IbpsCustomDialog: () => import('@/business/platform/data/templaterender/custom-dialog')
+  components: {
+    IbpsCustomDialog: () =>
+      import("@/business/platform/data/templaterender/custom-dialog"),
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false,
+    },
+    id: {
+      type: String,
+      default: "",
+    },
+    isDisabled: {
+      type: Boolean,
+      default: false,
     },
-    props: {
-        visible: {
-            type: Boolean,
-            default: false
+  },
+  data() {
+    const {
+      userList = [],
+      deptList = [],
+      userId,
+      level,
+    } = this.$store.getters || {};
+    return {
+      userList,
+      examTypeOptions,
+      deptList: deptList.filter((i) => i.depth === 4),
+      title: this.id ? "编辑考试" : "新建考试",
+      formLabelWidth: "150px",
+      dialogVisible: this.visible,
+      dialogLoading: false,
+      randButtonDisabled: false,
+      form: {
+        di_dian_: level.second || level.first,
+        chuang_jian_shi_j: "",
+        kao_shi_ming_chen: "",
+        kao_shi_lei_xing_: "常规",
+        ti_ku_id_: "",
+        guan_lian_id_: "",
+        xian_kao_shi_jian: "不限",
+        xian_kao_ci_shu_: "不限",
+        can_kao_ren_yuan_: "",
+        kao_shi_shi_chang: "",
+        zhuang_tai_: "未发布",
+        da_biao_zhan_bi_: 60,
+        ji_fen_fang_shi_: scoringType.length ? scoringType[0].value : "",
+        kao_shi_miao_shu_: "",
+        yun_xu_bao_ming_: "否",
+        isCountLimit: "0",
+        isTimeLimit: "0",
+        isDateLimit: "0",
+        hours: 2,
+        minutes: 30,
+      },
+      pickerOptions: {
+        disabledDate(time) {
+          // 禁用当前日期之前的日期
+          return time.getTime() < Date.now() - 8.64e7;
         },
-        id: {
-            type: String,
-            default: ''
+      },
+      toolbars: [
+        {
+          key: "submit",
+          icon: "ibps-icon-save",
+          label: "保存",
+          hidden: () => {
+            return this.readonly;
+          },
         },
-        isDisabled: {
-            type: Boolean,
-            default: false
-        }
+        { key: "cancel", label: "关闭" },
+      ],
+      rules: {
+        kao_shi_ming_chen: [
+          { required: true, message: this.$t("validate.required") },
+        ],
+        // kao_shi_lei_xing_: [{ required: true, message: this.$t('validate.required') }],
+        ti_ku_id_: [{ required: true, message: this.$t("validate.required") }],
+        xian_kao_shi_jian: [
+          { required: true, message: this.$t("validate.required") },
+        ],
+        xian_kao_ci_shu_: [
+          { required: true, message: this.$t("validate.required") },
+        ],
+        can_kao_ren_yuan_: [
+          { required: true, message: this.$t("validate.required") },
+        ],
+        kao_shi_shi_chang: [
+          { required: true, message: this.$t("validate.required") },
+        ],
+        da_biao_zhan_bi_: [
+          { required: true, message: this.$t("validate.required") },
+        ],
+      },
+    };
+  },
+  watch: {
+    visible: {
+      handler: function (val, oldVal) {
+        this.dialogVisible = this.visible;
+      },
+      // immediate: true
     },
-    data () {
-        const { userList = [], deptList = [], userId, level } = this.$store.getters || {}
-        return {
-            userList,
-            examTypeOptions,
-            deptList: deptList.filter(i => i.depth === 4),
-            title: this.id ? '编辑考试' : '新建考试',
-            formLabelWidth: '150px',
-            dialogVisible: this.visible,
-            dialogLoading: false,
-            form: {
-                di_dian_: level.second || level.first,
-                chuang_jian_shi_j: '',
-                kao_shi_ming_chen: '',
-                kao_shi_lei_xing_: '常规',
-                ti_ku_id_: '',
-                guan_lian_id_: '',
-                xian_kao_shi_jian: '不限',
-                xian_kao_ci_shu_: '不限',
-                can_kao_ren_yuan_: '',
-                kao_shi_shi_chang: '',
-                zhuang_tai_: '未发布',
-                da_biao_zhan_bi_: 60,
-                ji_fen_fang_shi_: scoringType.length ? scoringType[0].value : '',
-                kao_shi_miao_shu_: '',
-                yun_xu_bao_ming_: '否',
-                isCountLimit: '0',
-                isTimeLimit: '0',
-                isDateLimit: '0',
-                hours: 2,
-                minutes: 30
-            },
-            pickerOptions: {
-                disabledDate (time) {
-                    // 禁用当前日期之前的日期
-                    return time.getTime() < Date.now() - 8.64e7
-                }
-            },
-            toolbars: [
-                {
-                    key: 'submit',
-                    icon: 'ibps-icon-save',
-                    label: '保存',
-                    hidden: () => {
-                        return this.readonly
-                    }
-                },
-                { key: 'cancel', label: '关闭' }
-            ],
-            rules: {
-                kao_shi_ming_chen: [{ required: true, message: this.$t('validate.required') }],
-                // kao_shi_lei_xing_: [{ required: true, message: this.$t('validate.required') }],
-                ti_ku_id_: [{ required: true, message: this.$t('validate.required') }],
-                xian_kao_shi_jian: [{ required: true, message: this.$t('validate.required') }],
-                xian_kao_ci_shu_: [{ required: true, message: this.$t('validate.required') }],
-                can_kao_ren_yuan_: [{ required: true, message: this.$t('validate.required') }],
-                kao_shi_shi_chang: [{ required: true, message: this.$t('validate.required') }],
-                da_biao_zhan_bi_: [{ required: true, message: this.$t('validate.required') }]
-            }
-
-        }
+  },
+  mounted() {
+    this.getExamData();
+  },
+  methods: {
+    changeLimit(e, type, defaultValue) {
+      this.form[type] = e === "1" ? defaultValue : "不限";
     },
-    watch: {
-        visible: {
-            handler: function (val, oldVal) {
-                this.dialogVisible = this.visible
-            }
-            // immediate: true
+    handleActionEvent({ key }) {
+      switch (key) {
+        case "submit":
+          this.handleSubmit();
+          break;
+        case "cancel":
+          this.closeDialog();
+          break;
+        default:
+          break;
+      }
+    },
+    // 获取考试数据
+    getExamData() {
+      if (this.$utils.isEmpty(this.id)) {
+        return;
+      }
+      if (this.isDisabled) {
+        this.randButtonDisabled = true;
+        this.$message.info("非未发布状态的考试仅可修改限考时间!");
+      }
+      const sql = `select id_, create_by_, ti_ku_id_, guan_lian_id_, kao_shi_ming_chen, kao_shi_lei_xing_, chuang_jian_shi_j, fa_bu_shi_jian_, fa_bu_ren_, xian_kao_shi_jian, xian_kao_ci_shu_, kao_shi_shi_chang, can_kao_ren_yuan_, zhuang_tai_, da_biao_zhan_bi_, ji_fen_fang_shi_, kao_shi_miao_shu_, yun_xu_bao_ming_ from t_exams where id_ = '${this.id}'`;
+      this.$common.request("sql", sql).then((res) => {
+        const { data = [] } = res.variables || {};
+        if (!data.length) {
+          this.$message.error("数据不存在");
+          return;
+        }
+        data[0].isCountLimit = data[0].xian_kao_ci_shu_ === "不限" ? "0" : "1";
+        data[0].isDateLimit = data[0].xian_kao_shi_jian === "不限" ? "0" : "1";
+        if (data[0].kao_shi_shi_chang === "不限") {
+          data[0].isTimeLimit = "0";
+          data[0].hours = null;
+          data[0].minutes = null;
+        } else {
+          data[0].isTimeLimit = "1";
+          data[0].hours = Math.floor(
+            data[0].kao_shi_shi_chang / (1000 * 60 * 60)
+          );
+          data[0].minutes =
+            (data[0].kao_shi_shi_chang % (1000 * 60 * 60)) / (60 * 1000);
         }
+        this.form = data[0];
+      });
     },
-    mounted () {
-        this.getExamData()
+    // 随机题库
+    randTiku() {
+      this.randButtonDisabled = true;
+      const sql = `select id_ from t_question_bank where ti_ku_zhuang_tai_='可用'`;
+      this.$common.request("sql", sql).then((res) => {
+        const { data = [] } = res.variables || {};
+        let randNumber = Math.floor(Math.random() * data.length);
+        this.form.ti_ku_id_ = data[randNumber].id_;
+        this.randButtonDisabled = false;
+      });
     },
-    methods: {
-        changeLimit (e, type, defaultValue) {
-            this.form[type] = e === '1' ? defaultValue : '不限'
-        },
-        handleActionEvent ({ key }) {
-            switch (key) {
-                case 'submit':
-                    this.handleSubmit()
-                    break
-                case 'cancel':
-                    this.closeDialog()
-                    break
-                default:
-                    break
-            }
-        },
-        // 获取考试数据
-        getExamData () {
-            if (this.$utils.isEmpty(this.id)) {
-                return
-            }
-            if (this.isDisabled) {
-                this.$message.info('非未发布状态的考试仅可修改限考时间!')
-            }
-            const sql = `select id_, create_by_, ti_ku_id_, guan_lian_id_, kao_shi_ming_chen, kao_shi_lei_xing_, chuang_jian_shi_j, fa_bu_shi_jian_, fa_bu_ren_, xian_kao_shi_jian, xian_kao_ci_shu_, kao_shi_shi_chang, can_kao_ren_yuan_, zhuang_tai_, da_biao_zhan_bi_, ji_fen_fang_shi_, kao_shi_miao_shu_, yun_xu_bao_ming_ from t_exams where id_ = '${this.id}'`
-            this.$common.request('sql', sql).then(res => {
-                const { data = [] } = res.variables || {}
-                if (!data.length) {
-                    this.$message.error('数据不存在')
-                    return
-                }
-                data[0].isCountLimit = data[0].xian_kao_ci_shu_ === '不限' ? '0' : '1'
-                data[0].isDateLimit = data[0].xian_kao_shi_jian === '不限' ? '0' : '1'
-                if (data[0].kao_shi_shi_chang === '不限') {
-                    data[0].isTimeLimit = '0'
-                    data[0].hours = null
-                    data[0].minutes = null
-                } else {
-                    data[0].isTimeLimit = '1'
-                    data[0].hours = Math.floor(data[0].kao_shi_shi_chang / (1000 * 60 * 60))
-                    data[0].minutes = (data[0].kao_shi_shi_chang % (1000 * 60 * 60)) / (60 * 1000)
-                }
-                this.form = data[0]
-            })
-        },
-        handleSubmit () {
-            this.$refs.form.validate((valid) => {
-                if (valid) {
-                    const { isTimeLimit, xian_kao_shi_jian = '' } = this.form || {}
-                    // 转换考试时长
-                    if (isTimeLimit === '0') {
-                        this.form.kao_shi_shi_chang = '不限'
-                    } else {
-                        this.form.kao_shi_shi_chang = (this.form.hours * 60 + this.form.minutes) * 60 * 1000
-                    }
-                    delete this.form.isDateLimit
-                    delete this.form.isCountLimit
-                    delete this.form.isTimeLimit
-                    delete this.form.hours
-                    delete this.form.minutes
-                    this.form.chuang_jian_shi_j = this.$common.getDateNow(19)
-                    this.form.xian_kao_shi_jian = xian_kao_shi_jian !== '不限' ? this.$common.getFormatDate('string', 16, xian_kao_shi_jian) : xian_kao_shi_jian
-                    // 表单验证通过,提交表单
-                    this.submitForm()
-                } else {
-                    ActionUtils.saveErrorMessage()
-                }
-            })
-        },
-        submitForm () {
-            const addParams = {
-                tableName: 't_exams',
-                paramWhere: [this.form]
-            }
-            const updateParams = {
-                tableName: 't_exams',
-                updList: [
-                    {
-                        where: {
-                            id_: this.id
-                        },
-                        param: this.form
-                    }
-                ]
-            }
-            const type = this.id ? 'update' : 'add'
-            const params = type === 'add' ? addParams : updateParams
-            this.$common.request(type, params).then(() => {
-                this.$message.success(this.id ? '修改考试成功' : '新建考试成功')
-                this.closeDialog()
-            })
-        },
-        // 关闭当前窗口
-        closeDialog () {
-            this.$emit('close', false)
+    handleSubmit() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          const { isTimeLimit, xian_kao_shi_jian = "" } = this.form || {};
+          // 转换考试时长
+          if (isTimeLimit === "0") {
+            this.form.kao_shi_shi_chang = "不限";
+          } else {
+            this.form.kao_shi_shi_chang =
+              (this.form.hours * 60 + this.form.minutes) * 60 * 1000;
+          }
+          delete this.form.isDateLimit;
+          delete this.form.isCountLimit;
+          delete this.form.isTimeLimit;
+          delete this.form.hours;
+          delete this.form.minutes;
+          this.form.chuang_jian_shi_j = this.$common.getDateNow(19);
+          this.form.xian_kao_shi_jian =
+            xian_kao_shi_jian !== "不限"
+              ? this.$common.getFormatDate("string", 16, xian_kao_shi_jian)
+              : xian_kao_shi_jian;
+          // 表单验证通过,提交表单
+          this.submitForm();
+        } else {
+          ActionUtils.saveErrorMessage();
         }
-    }
-}
+      });
+    },
+    submitForm() {
+      const addParams = {
+        tableName: "t_exams",
+        paramWhere: [this.form],
+      };
+      const updateParams = {
+        tableName: "t_exams",
+        updList: [
+          {
+            where: {
+              id_: this.id,
+            },
+            param: this.form,
+          },
+        ],
+      };
+      const type = this.id ? "update" : "add";
+      const params = type === "add" ? addParams : updateParams;
+      this.$common.request(type, params).then(() => {
+        this.$message.success(this.id ? "修改考试成功" : "新建考试成功");
+        this.closeDialog();
+      });
+    },
+    // 关闭当前窗口
+    closeDialog() {
+      this.$emit("close", false);
+    },
+  },
+};
 </script>
 <style lang="scss" scoped>
-    .exam-dialog {
-        ::v-deep {
-            .el-dialog {
-                max-width: 1080px;
-            }
-            .el-dialog__body {
-                height: calc(88vh - 200px);
-            }
-            .el-form-item {
-                margin-bottom: 14px !important;
-                &:last-child {
-                    margin-bottom: 0 !important;
-                }
-                .el-form-item__label {
-                    font-size: 14px !important;
-                }
-            }
-            .el-form-item--small .el-form-item__error {
-                padding-top: 6px;
-            }
-        }
-        .exam-form {
-            padding: 20px;
-            .question-icon {
-                font-size: 13px;
-                color: #606060;
-            }
-            .date-picker {
-                margin-left: 20px;
-            }
-            .custom-dialog {
-                ::v-deep {
-                    .el-input--prefix .el-input__inner {
-                        padding-left: 15px;
-                    }
-                }
-            }
-        }
-        .inline-item {
-            ::v-deep {
-                .el-radio-group {
-                    margin-right: 20px;
-                }
-            }
-            .time {
-                display: inline-block;
-            }
-        }
-        .unit {
-            display: inline-block;
-            margin: 0 20px 0 5px;
+.exam-dialog {
+  ::v-deep {
+    .el-dialog {
+      max-width: 1080px;
+    }
+    .el-dialog__body {
+      height: calc(88vh - 200px);
+    }
+    .el-form-item {
+      margin-bottom: 14px !important;
+      &:last-child {
+        margin-bottom: 0 !important;
+      }
+      .el-form-item__label {
+        font-size: 14px !important;
+      }
+    }
+    .el-form-item--small .el-form-item__error {
+      padding-top: 6px;
+    }
+  }
+  .exam-form {
+    padding: 20px;
+    .question-icon {
+      font-size: 13px;
+      color: #606060;
+    }
+    .date-picker {
+      margin-left: 20px;
+    }
+    .custom-dialog {
+      ::v-deep {
+        .el-input--prefix .el-input__inner {
+          padding-left: 15px;
         }
+      }
+    }
+  }
+  .inline-item {
+    ::v-deep {
+      .el-radio-group {
+        margin-right: 20px;
+      }
+    }
+    .time {
+      display: inline-block;
     }
+  }
+  .unit {
+    display: inline-block;
+    margin: 0 20px 0 5px;
+  }
+}
+.tiku {
+  .el-button {
+    margin-left: 20px;
+  }
+}
 </style>

+ 260 - 0
src/views/platform/examination/exam/statistics.vue

@@ -0,0 +1,260 @@
+<template>
+  <div>
+    <el-dialog
+      center
+      append-to-body
+      title="数据统计"
+      :visible.sync="dialogVisible"
+      width="70%"
+      top
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+    >
+      <div class="chart">
+        <div class="chart1">
+          <v-chart
+            :options="joinChartOptions"
+            :autoresize="true"
+            style="width: 80%"
+          ></v-chart>
+        </div>
+        <div class="chart2">
+          <v-chart
+            :options="scoreChartOptions"
+            :autoresize="true"
+            style="width: 80%"
+          ></v-chart>
+        </div>
+      </div>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button type="info" @click="dialogVisible = false">关闭</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    departmentData: {
+      type: Array,
+      default: [],
+    },
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      joinChartOptions: {},
+      scoreChartOptions: {},
+      //   departmentData:[ { "department": "检验科", "plannedAttendees": 6, "actualAttendees": 0, "joinRate": "0.00%" }, { "department": "综合/质量管理组", "plannedAttendees": 4, "actualAttendees": 0, "joinRate": "0.00%" }, { "department": "微生物组", "plannedAttendees": 4, "actualAttendees": 0, "joinRate": "0.00%" }, { "department": "生化组", "plannedAttendees": 4, "actualAttendees": 0, "joinRate": "0.00%" }, { "department": "临检组", "plannedAttendees": 8, "actualAttendees": 0, "joinRate": "0.00%" }, { "department": "免疫组", "plannedAttendees": 1, "actualAttendees": 0, "joinRate": "0.00%" } ]
+    };
+  },
+  methods: {
+    open() {
+      this.dialogVisible = true;
+    },
+    close() {
+      this.dialogVisible = false;
+    },
+    transformData(type) {
+      const xAxisData = [];
+      const plannedAttendeesData = [];
+      const actualAttendeesData = [];
+      const joinRateData = [];
+      const avgScoreData = [];
+      if (type === "join") {
+        this.departmentData.forEach((department) => {
+          xAxisData.push(department.department);
+          plannedAttendeesData.push(department.plannedAttendees);
+          actualAttendeesData.push(department.actualAttendees);
+          joinRateData.push((department.joinRate * 100).toFixed(2));
+        });
+      }
+      if (type === "score") {
+        this.departmentData.forEach((department) => {
+          xAxisData.push(department.department);
+          plannedAttendeesData.push(department.minScore.toFixed(2));
+          actualAttendeesData.push(department.maxScore.toFixed(2));
+          joinRateData.push((department.passRate * 100).toFixed(2));
+          avgScoreData.push(department.avgScore.toFixed(2));
+        });
+      }
+
+      return {
+        xAxisData,
+        plannedAttendeesData,
+        actualAttendeesData,
+        joinRateData,
+        avgScoreData,
+      };
+    },
+    generateChartOptions(type) {
+      const {
+        xAxisData,
+        plannedAttendeesData,
+        actualAttendeesData,
+        joinRateData,
+        avgScoreData,
+      } = this.transformData(type);
+
+      let text = "";
+      let legendText = [];
+      let series = [];
+      let llable = "";
+      if (type === "join") {
+        text = "各部门参考人数信息统计";
+        llable = "人数";
+        legendText = ["应参考人数", "实参考人数", "参考率"];
+        series = [
+          {
+            name: legendText[0],
+            type: "bar",
+            data: plannedAttendeesData,
+          },
+          {
+            name: legendText[1],
+            type: "bar",
+            data: actualAttendeesData,
+          },
+          {
+            name: legendText[2],
+            type: "line",
+            yAxisIndex: 1,
+            data: joinRateData,
+            label:{
+              show:true,
+              position:"top",
+            }
+          },
+        ];
+      }
+      if (type === "score") {
+        text = "各部门成绩信息统计";
+        llable = "分数";
+        legendText = ["最高分", "最低分", "平均分", "合格率"];
+        series = [
+          {
+            name: legendText[0],
+            type: "bar",
+            data: plannedAttendeesData,
+          },
+          {
+            name: legendText[1],
+            type: "bar",
+            data: actualAttendeesData,
+          },
+          {
+            name: legendText[2],
+            type: "bar",
+            data: avgScoreData,
+          },
+          {
+            name: legendText[3],
+            type: "line",
+            yAxisIndex: 1,
+            data: joinRateData,
+            label:{
+              show:true,
+              position:"top",
+            }
+          },
+        ];
+      }
+      return {
+        title: {
+          text,
+        },
+        tooltip: {
+          trigger: "axis",
+          axisPointer: {
+            type: "shadow",
+          },
+        },
+        legend: {
+          data: legendText,
+          
+        },
+        grid: {
+          // top: 0,
+          // left: 10,
+        },
+        xAxis: {
+          type: "category",
+          data: xAxisData,
+          axisLabel: {
+            rotate: 20,
+          },
+        },
+        yAxis: [
+          {
+            type: "value",
+            name: llable,
+            axisLabel: {
+              formatter: "{value}",
+            },
+          },
+          {
+            type: "value",
+            name: legendText[legendText.length - 1],
+            axisLabel: {
+              formatter: "{value}%",
+            },
+          },
+        ],
+        series,
+        dataZoom: [
+          {
+            show: true,
+            realtime: true,
+            start: 0,
+            end: 200,
+          },
+          {
+            type: "inside",
+            realtime: true,
+            start: 0,
+            end: 200,
+          },
+        ],
+      };
+    },
+  },
+  computed: {},
+  created() {
+    this.joinChartOptions = this.generateChartOptions("join");
+    this.scoreChartOptions = this.generateChartOptions("score");
+  },
+  watch: {
+    // 解决echart渲染问题
+    departmentData() {
+      this.joinChartOptions = this.generateChartOptions("join");
+      this.scoreChartOptions = this.generateChartOptions("score");
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.dialog-footer {
+  display: flex;
+  justify-content: center;
+}
+.chart {
+  display: flex;
+  justify-content: center;
+  /* align-items: center; */
+  flex-direction: column;
+  width: 100%;
+  // height: 60vh;
+  .chart1,
+  .chart2 {
+    display: flex;
+    justify-content: center;
+    margin: 10px 0 10px 0;
+  }
+  .chart1 {
+    margin-bottom: 60px;
+  }
+}
+</style>

+ 702 - 547
src/views/platform/examination/questionBank/detail.vue

@@ -1,583 +1,738 @@
 <template>
-    <el-dialog
-        :title="title"
-        :visible.sync="dialogVisible"
-        :close-on-click-modal="false"
-        :close-on-press-escape="false"
-        :show-close="false"
-        append-to-body
-        fullscreen
-        class="dialog paper-detail-dialog"
-        top="0"
-        @open="loadData"
-        @close="closeDialog"
-    >
-        <div class="container">
-            <div class="paper-info">
-                <div class="info-item">
-                    <span class="label">题库名称:</span>
-                    <span class="value">{{ paperData.paperName }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">题目数量:</span>
-                    <span class="value">{{ paperData.totalCount }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">总分:</span>
-                    <span class="value">{{ paperData.totalScore }}分</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">达标占比:</span>
-                    <span class="value">{{ paperData.qualifiedRadio }}%</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">计分方式:</span>
-                    <span class="value">{{ paperData.scoringType }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">得分:</span>
-                    <span class="value">{{ paperData.status !== '已完成' ? '未评分' : `${paperData.resultScore}分` }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">报考时间:</span>
-                    <span class="value">{{ paperData.applyTime }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">开考时间:</span>
-                    <span class="value">{{ paperData.startTime }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">结束时间:</span>
-                    <span class="value">{{ paperData.endTime }}</span>
-                </div>
-                <div class="info-item">
-                    <span class="label">考生姓名:</span>
-                    <span class="value">{{ transformUser(paperData.examinee) }}</span>
-                </div>
-                <!-- <div class="info-item">
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    fullscreen
+    class="dialog paper-detail-dialog"
+    top="0"
+    @open="loadData"
+    @close="closeDialog"
+  >
+    <div class="container">
+      <div class="paper-info">
+        <div class="info-item">
+          <span class="label">题库名称:</span>
+          <span class="value">{{ paperData.paperName }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">题目数量:</span>
+          <span class="value">{{ paperData.totalCount }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">总分:</span>
+          <span class="value">{{ paperData.totalScore }}分</span>
+        </div>
+        <div class="info-item">
+          <span class="label">达标占比:</span>
+          <span class="value">{{ paperData.qualifiedRadio }}%</span>
+        </div>
+        <div class="info-item">
+          <span class="label">计分方式:</span>
+          <span class="value">{{ paperData.scoringType }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">得分:</span>
+          <span class="value">{{
+            paperData.status !== "已完成"
+              ? "未评分"
+              : `${paperData.resultScore}分`
+          }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">报考时间:</span>
+          <span class="value">{{ paperData.applyTime }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">开考时间:</span>
+          <span class="value">{{ paperData.startTime }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">结束时间:</span>
+          <span class="value">{{ paperData.endTime }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">考生姓名:</span>
+          <span class="value">{{ transformUser(paperData.examinee) }}</span>
+        </div>
+        <!-- <div class="info-item">
                     <span class="label">部门:</span>
                     <span class="value">{{ paperData.dept }}</span>
                 </div> -->
+      </div>
+      <div class="select">
+        答题状态:<el-select v-model="paperStatus" placeholder="请选择">
+          <el-option
+            v-for="item in paperoptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          >
+          </el-option>
+        </el-select>
+      </div>
+
+      <div class="question">
+        <div
+          v-for="(item, index) in showPaperData.list"
+          :key="index"
+          class="question-item"
+        >
+          <div class="type" v-if="item.questions.length > 0">
+            {{ item.type
+            }}<el-tag
+              type="primary"
+              size="small"
+              class="score"
+              v-if="paperStatus === 'all'"
+              >{{ `共${item.totalScore}分` }}</el-tag
+            >
+          </div>
+          <div
+            v-for="(q, qIndex) in item.questions"
+            :key="`${index}${qIndex}`"
+            class="card"
+          >
+            <div class="stem">
+              <span>{{ `【${qIndex + 1}】${q.stem}` }}</span>
+              <el-tag type="primary" size="small">{{
+                `${q.questionScore}分`
+              }}</el-tag>
             </div>
-            <div class="question">
-                <div
-                    v-for="(item, index) in paperData.list"
-                    :key="index"
-                    class="question-item"
-                >
-                    <div class="type">{{ item.type }}<el-tag type="primary" size="small" class="score">{{ `共${item.totalScore}分` }}</el-tag></div>
-                    <div
-                        v-for="(q, qIndex) in item.questions"
-                        :key="`${index}${qIndex}`"
-                        class="card"
+            <div v-if="q.img && q.img.length" class="img">
+              <ibps-image
+                v-model="q.img"
+                height="100"
+                width="100"
+                accept=".jpg,.jpeg,.png,.gif,.bmp,.webp"
+                download
+                disabled
+              />
+            </div>
+            <div class="answer">
+              <div class="mine">
+                <div class="title">
+                  考生答案:<el-tag
+                    :type="q.score && q.score !== '0' ? 'success' : 'danger'"
+                    size="small"
+                    class="score"
+                    >{{
+                      !q.score && q.score !== "0" ? "未评分" : `得${q.score}分`
+                    }}</el-tag
+                  >
+                </div>
+                <div class="answer-content">
+                  <el-radio-group
+                    v-if="q.questionType === '单选题'"
+                    :value="q.answer"
+                  >
+                    <el-radio
+                      v-for="(o, i) in q.options"
+                      :key="`${index}${qIndex}${i}`"
+                      :label="o.label"
+                      >{{ `${o.label}.${o.value}` }}</el-radio
+                    >
+                  </el-radio-group>
+                  <el-checkbox-group
+                    v-else-if="q.questionType === '多选题'"
+                    :value="q.answer"
+                  >
+                    <el-checkbox
+                      v-for="(o, i) in q.options"
+                      :key="`${index}${qIndex}${i}`"
+                      :label="o.label"
+                      >{{ `${o.label}.${o.value}` }}</el-checkbox
                     >
-                        <div class="stem">
-                            <span>{{ `【${qIndex + 1}】${q.stem}` }}</span>
-                            <el-tag type="primary" size="small">{{ `${q.questionScore}分` }}</el-tag>
-                        </div>
-                        <div v-if="q.img && q.img.length" class="img">
-                            <ibps-image
-                                v-model="q.img"
-                                height="100"
-                                width="100"
-                                accept=".jpg,.jpeg,.png,.gif,.bmp,.webp"
-                                download
-                                disabled
-                            />
-                        </div>
-                        <div class="answer">
-                            <div class="mine">
-                                <div class="title">考生答案:<el-tag :type="!q.score && q.score !== '0' ? 'warning' : 'success'" size="small" class="score">{{ !q.score && q.score !== '0' ? '未评分' : `得${q.score}分` }}</el-tag></div>
-                                <div class="answer-content">
-                                    <el-radio-group v-if="q.questionType === '单选题'" :value="q.answer">
-                                        <el-radio
-                                            v-for="(o, i) in q.options"
-                                            :key="`${index}${qIndex}${i}`"
-                                            :label="o.label"
-                                        >{{ `${o.label}.${o.value}` }}</el-radio>
-                                    </el-radio-group>
-                                    <el-checkbox-group v-else-if="q.questionType === '多选题'" :value="q.answer">
-                                        <el-checkbox
-                                            v-for="(o, i) in q.options"
-                                            :key="`${index}${qIndex}${i}`"
-                                            :label="o.label"
-                                        >{{ `${o.label}.${o.value}` }}</el-checkbox>
-                                    </el-checkbox-group>
-                                    <el-radio-group v-else-if="q.questionType === '判断题'" :value="q.answer">
-                                        <el-radio-button label="√">√</el-radio-button>
-                                        <el-radio-button label="×">×</el-radio-button>
-                                    </el-radio-group>
-                                    <template v-else>
-                                        <el-input
-                                            v-for="(o, i) in q.answer"
-                                            :key="`${index}${qIndex}${i}`"
-                                            :value="o"
-                                            :type="q.questionType === '简答题' ? 'textarea' : 'text'"
-                                            :rows="q.questionType === '简答题' ? 12 : 1"
-                                            readonly
-                                        />
-                                    </template>
-                                </div>
-                            </div>
-                            <div class="right">
-                                <div class="title">参考答案:</div>
-                                <div class="answer-content">
-                                    <el-radio-group v-if="q.questionType === '单选题'" :value="q.rightKey">
-                                        <el-radio
-                                            v-for="(o, i) in q.options"
-                                            :key="`${index}${qIndex}${i}`"
-                                            :label="o.label"
-                                        >{{ `${o.label}.${o.value}` }}</el-radio>
-                                    </el-radio-group>
-                                    <el-checkbox-group v-else-if="q.questionType === '多选题'" :value="q.rightKey">
-                                        <el-checkbox
-                                            v-for="(o, i) in q.options"
-                                            :key="`${index}${qIndex}${i}`"
-                                            :label="o.label"
-                                        >{{ `${o.label}.${o.value}` }}</el-checkbox>
-                                    </el-checkbox-group>
-                                    <el-radio-group v-else-if="q.questionType === '判断题'" :value="q.rightKey">
-                                        <el-radio-button label="√">√</el-radio-button>
-                                        <el-radio-button label="×">×</el-radio-button>
-                                    </el-radio-group>
-                                    <template v-else>
-                                        <el-input
-                                            v-for="(o, i) in q.rightKey"
-                                            :key="`${index}${qIndex}${i}`"
-                                            :value="o"
-                                            :type="q.questionType === '简答题' ? 'textarea' : 'text'"
-                                            :rows="q.questionType === '简答题' ? 12 : 1"
-                                            readonly
-                                        />
-                                    </template>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
+                  </el-checkbox-group>
+                  <el-radio-group
+                    v-else-if="q.questionType === '判断题'"
+                    :value="q.answer"
+                  >
+                    <el-radio-button label="√">√</el-radio-button>
+                    <el-radio-button label="×">×</el-radio-button>
+                  </el-radio-group>
+                  <template v-else>
+                    <el-input
+                      v-for="(o, i) in q.answer"
+                      :key="`${index}${qIndex}${i}`"
+                      :value="o"
+                      :type="q.questionType === '简答题' ? 'textarea' : 'text'"
+                      :rows="q.questionType === '简答题' ? 12 : 1"
+                      readonly
+                    />
+                  </template>
                 </div>
+              </div>
+              <div class="right">
+                <div class="title">参考答案:</div>
+                <div class="answer-content">
+                  <el-radio-group
+                    v-if="q.questionType === '单选题'"
+                    :value="q.rightKey"
+                  >
+                    <el-radio
+                      v-for="(o, i) in q.options"
+                      :key="`${index}${qIndex}${i}`"
+                      :label="o.label"
+                      >{{ `${o.label}.${o.value}` }}</el-radio
+                    >
+                  </el-radio-group>
+                  <el-checkbox-group
+                    v-else-if="q.questionType === '多选题'"
+                    :value="q.rightKey"
+                  >
+                    <el-checkbox
+                      v-for="(o, i) in q.options"
+                      :key="`${index}${qIndex}${i}`"
+                      :label="o.label"
+                      >{{ `${o.label}.${o.value}` }}</el-checkbox
+                    >
+                  </el-checkbox-group>
+                  <el-radio-group
+                    v-else-if="q.questionType === '判断题'"
+                    :value="q.rightKey"
+                  >
+                    <el-radio-button label="√">√</el-radio-button>
+                    <el-radio-button label="×">×</el-radio-button>
+                  </el-radio-group>
+                  <template v-else>
+                    <el-input
+                      v-for="(o, i) in q.rightKey"
+                      :key="`${index}${qIndex}${i}`"
+                      :value="o"
+                      :type="q.questionType === '简答题' ? 'textarea' : 'text'"
+                      :rows="q.questionType === '简答题' ? 12 : 1"
+                      readonly
+                    />
+                  </template>
+                </div>
+              </div>
             </div>
+          </div>
         </div>
-        <div class="date-line">
-            <el-timeline>
-                <el-timeline-item
-                    v-for="(paper, index) in paperList"
-                    :key="index"
-                    :timestamp="paper.applyTime"
-                    :icon="paper.status === '已完成' ? 'el-icon-circle-check' : 'el-icon-view'"
-                    :type="paper.status === '已完成' ? 'success' : 'primary'"
-                    placement="top"
-                    size="large"
-                >
-                    <el-card class="timeline-card" :class="paper.dataId === showPaperId ? 'active-card' : ''" @click.native="changePaper(paper.dataId)">
-                        <div class="card-item">{{ `开始时间:${paper.startTime}` }}</div>
-                        <div class="card-item">{{ `结束时间:${paper.endTime}` }}</div>
-                        <div v-if="paper.status === '已完成'" class="card-item">
-                            <span>{{ `得分:${paper.resultScore}` }}</span>
-                            <el-tag :type="paper.isQualified ? 'success' : 'danger'" size="mini" class="score">{{ paper.isQualified ? `达标` : '未达标' }}</el-tag>
-                            <el-tag v-if="paper.resultScore === maxScore" type="success" size="mini" class="score">{{ `最高分` }}</el-tag>
-                            <el-tag v-if="paper.resultScore === minScore" type="warning" size="mini" class="score">{{ `最低分` }}</el-tag>
-                        </div>
-                    </el-card>
-                </el-timeline-item>
-            </el-timeline>
-        </div>
-        <div slot="footer" class="el-dialog--center">
-            <ibps-toolbar
-                :actions="toolbars"
-                @action-event="handleActionEvent"
-            />
-        </div>
-    </el-dialog>
+      </div>
+    </div>
+    <div class="date-line">
+      <el-timeline>
+        <el-timeline-item
+          v-for="(paper, index) in paperList"
+          :key="index"
+          :timestamp="paper.applyTime"
+          :icon="
+            paper.status === '已完成' ? 'el-icon-circle-check' : 'el-icon-view'
+          "
+          :type="paper.status === '已完成' ? 'success' : 'primary'"
+          placement="top"
+          size="large"
+        >
+          <el-card
+            class="timeline-card"
+            :class="paper.dataId === showPaperId ? 'active-card' : ''"
+            @click.native="changePaper(paper.dataId)"
+          >
+            <div class="card-item">{{ `开始时间:${paper.startTime}` }}</div>
+            <div class="card-item">{{ `结束时间:${paper.endTime}` }}</div>
+            <div v-if="paper.status === '已完成'" class="card-item">
+              <span>{{ `得分:${paper.resultScore}` }}</span>
+              <el-tag
+                :type="paper.isQualified ? 'success' : 'danger'"
+                size="mini"
+                class="score"
+                >{{ paper.isQualified ? `达标` : "未达标" }}</el-tag
+              >
+              <el-tag
+                v-if="paper.resultScore === maxScore"
+                type="success"
+                size="mini"
+                class="score"
+                >{{ `最高分` }}</el-tag
+              >
+              <el-tag
+                v-if="paper.resultScore === minScore"
+                type="warning"
+                size="mini"
+                class="score"
+                >{{ `最低分` }}</el-tag
+              >
+            </div>
+          </el-card>
+        </el-timeline-item>
+      </el-timeline>
+    </div>
+    <div slot="footer" class="el-dialog--center">
+      <ibps-toolbar :actions="toolbars" @action-event="handleActionEvent" />
+    </div>
+  </el-dialog>
 </template>
 
 <script>
+import { cloneDeep } from "lodash";
 export default {
-    components: {
-        IbpsImage: () => import('@/business/platform/file/image')
+  components: {
+    IbpsImage: () => import("@/business/platform/file/image"),
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false,
     },
-    props: {
-        visible: {
-            type: Boolean,
-            default: false
-        },
-        bankId: {
-            type: String,
-            default: ''
-        },
-        examId: {
-            type: String,
-            default: ''
-        },
-        id: {
-            type: String,
-            default: ''
-        },
-        examineeId: {
-            type: String,
-            default: ''
-        }
+    bankId: {
+      type: String,
+      default: "",
     },
-    data () {
-        return {
-            title: '考试详情',
-            dialogVisible: this.visible,
-            toolbars: [
-                { key: 'cancel', label: '退出' }
-            ],
-            paperList: [],
-            paperData: [],
-            maxScore: '',
-            minScore: '',
-            showPaperId: ''
-        }
+    examId: {
+      type: String,
+      default: "",
     },
-    computed: {
-        formData () {
-            return this.data
-        }
+    id: {
+      type: String,
+      default: "",
     },
-    watch: {
-        visible: {
-            handler (val, oldVal) {
-                this.dialogVisible = this.visible
+    examineeId: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      title: "考试详情",
+      dialogVisible: this.visible,
+      toolbars: [{ key: "cancel", label: "退出" }],
+      paperList: [],
+      paperData: [],
+      maxScore: "",
+      minScore: "",
+      showPaperId: "",
+      paperoptions: [
+        { value: "all", label: "全部" },
+        { value: "false", label: "错误" },
+        { value: "true", label: "正确" },
+        { value: "noFinished", label: "未评分" },
+      ],
+      paperStatus: "all",
+    };
+  },
+  computed: {
+    formData() {
+      return this.data;
+    },
+    showPaperData: {
+      set() {}, // 解决报错
+      get() {
+        let showPaperData = cloneDeep(this.paperData);
+        showPaperData.list?.forEach((papers) => {
+          papers.questions = papers.questions.filter((paper) => {
+            switch (this.paperStatus) {
+              case "all":
+                return true;
+              case "false":
+                return paper.score && paper.score === "0";
+              case "true":
+                return paper.score && paper.score !== "0";
+              case "noFinished":
+                return paper.score === "";
+              default:
+                break;
             }
-        }
+          });
+        });
+        return showPaperData;
+      },
     },
-    mounted () {
-        this.loadData()
-        // 监听键盘事件
-        window.addEventListener('keyup', this.handleKeyPress)
+  },
+  watch: {
+    visible: {
+      handler(val, oldVal) {
+        this.dialogVisible = this.visible;
+      },
     },
-    beforeDestroy () {
-        window.removeEventListener('keyup', this.handleKeyPress)
+  },
+  mounted() {
+    this.loadData();
+    // 监听键盘事件
+    window.addEventListener("keyup", this.handleKeyPress);
+  },
+  beforeDestroy() {
+    window.removeEventListener("keyup", this.handleKeyPress);
+  },
+  methods: {
+    // 获取题库数据
+    async loadData() {
+      if (!this.bankId) {
+        this.$message.error("获取题目信息失败,请重试!");
+        this.closeDialog();
+        return;
+      }
+      this.paperList = await this.getQuestionData();
+      this.paperData =
+        this.paperList.find((i) => i.dataId === this.id) || this.paperList[0];
+      this.showPaperId = this.paperData.dataId;
     },
-    methods: {
-        // 获取题库数据
-        async loadData () {
-            if (!this.bankId) {
-                this.$message.error('获取题目信息失败,请重试!')
-                this.closeDialog()
-                return
-            }
-            this.paperList = await this.getQuestionData()
-            this.paperData = this.paperList.find(i => i.dataId === this.id) || this.paperList[0]
-            this.showPaperId = this.paperData.dataId
-        },
-        handleActionEvent ({ key }) {
-            switch (key) {
-                case 'cancel':
-                    this.closeDialog()
-                    break
-                default:
-                    break
-            }
-        },
-        handleKeyPress (event) {
-            if (event.keyCode === 27 || event.key === 'Esc') {
-                this.closeDialog()
+    handleActionEvent({ key }) {
+      switch (key) {
+        case "cancel":
+          this.closeDialog();
+          break;
+        default:
+          break;
+      }
+    },
+    handleKeyPress(event) {
+      if (event.keyCode === 27 || event.key === "Esc") {
+        this.closeDialog();
+      }
+    },
+    transformUser(userId) {
+      const { userList = [] } = this.$store.getters;
+      const user = userList.find((u) => u.userId === userId) || {};
+      return user.userName || "-";
+    },
+    getQuestionData() {
+      const param = this.examId
+        ? `and e.exam_id_ = '${this.examId}'`
+        : "and e.exam_id_ is null";
+      const sql = `select e.id_ as dataId, e.exam_id_ as examId, e.kao_shi_ren_ as examinee, e.bu_men_ as dept, e.zhuang_tai_ as status, e.bao_ming_shi_jian as applyTime, e.kai_shi_shi_jian_ as startTime, e.jie_shu_shi_jian_ as endTime, e.ti_ku_zong_fen_ as totalScore, e.de_fen_ as resultScore, ed.ti_mu_id_ as questionId, ed.ti_gan_ as stem, ed.ti_xing_ as questionType, ed.fen_zhi_ as questionScore, ed.fu_tu_ as img, ed.xuan_xiang_lei_xi as optionsType, ed.xuan_xiang_ as options, ed.can_kao_da_an_ as rightKey, ed.ping_fen_fang_shi as rateType, ed.ping_fen_ren_ as rater, ed.hui_da_ as answer, ed.ping_yue_shi_jian as rateTime, ed.de_fen_ as score, ed.jie_xi_ as analysis, q.ti_ku_ming_cheng_ as paperName, case when e.exam_id_ is not null then ex.da_biao_zhan_bi_ else q.da_biao_zhan_bi_ end as qualifiedRadio, case when e.exam_id_ is not null then ex.ji_fen_fang_shi_ else q.ji_fen_fang_shi_ end as scoringType, case when e.exam_id_ is not null then ex.kao_shi_ming_chen else '自主考核' end as examName, case when e.exam_id_ is not null then ex.xian_kao_shi_jian else '不限' end as limitDate from t_examination e left join t_examination_detail ed on e.id_ = ed.parent_id_ left join  t_question_bank q on e.ti_ku_id_ = q.id_ left join  t_exams ex on e.exam_id_ = ex.id_ where e.ti_ku_id_ = '${this.bankId}' ${param} and e.kao_shi_ren_ = '${this.examineeId}' and (e.zhuang_tai_ = '已完成' or e.zhuang_tai_ = '已交卷') order by field(ed.ti_xing_, '单选题', '多选题', '判断题', '填空题', '简答题')`;
+      return new Promise((resolve, reject) => {
+        this.$common
+          .request("sql", sql)
+          .then((res) => {
+            const { data = [] } = res.variables || {};
+            if (!data.length) {
+              this.$message.error("未查询到已提交的考试记录,请先完成考试!");
+              this.closeDialog();
+              return;
             }
-        },
-        transformUser (userId) {
-            const { userList = [] } = this.$store.getters
-            const user = userList.find(u => u.userId === userId) || {}
-            return user.userName || '-'
-        },
-        getQuestionData () {
-            const param = this.examId ? `and e.exam_id_ = '${this.examId}'` : 'and e.exam_id_ is null'
-            const sql = `select e.id_ as dataId, e.exam_id_ as examId, e.kao_shi_ren_ as examinee, e.bu_men_ as dept, e.zhuang_tai_ as status, e.bao_ming_shi_jian as applyTime, e.kai_shi_shi_jian_ as startTime, e.jie_shu_shi_jian_ as endTime, e.ti_ku_zong_fen_ as totalScore, e.de_fen_ as resultScore, ed.ti_mu_id_ as questionId, ed.ti_gan_ as stem, ed.ti_xing_ as questionType, ed.fen_zhi_ as questionScore, ed.fu_tu_ as img, ed.xuan_xiang_lei_xi as optionsType, ed.xuan_xiang_ as options, ed.can_kao_da_an_ as rightKey, ed.ping_fen_fang_shi as rateType, ed.ping_fen_ren_ as rater, ed.hui_da_ as answer, ed.ping_yue_shi_jian as rateTime, ed.de_fen_ as score, ed.jie_xi_ as analysis, q.ti_ku_ming_cheng_ as paperName, case when e.exam_id_ is not null then ex.da_biao_zhan_bi_ else q.da_biao_zhan_bi_ end as qualifiedRadio, case when e.exam_id_ is not null then ex.ji_fen_fang_shi_ else q.ji_fen_fang_shi_ end as scoringType, case when e.exam_id_ is not null then ex.kao_shi_ming_chen else '自主考核' end as examName, case when e.exam_id_ is not null then ex.xian_kao_shi_jian else '不限' end as limitDate from t_examination e left join t_examination_detail ed on e.id_ = ed.parent_id_ left join  t_question_bank q on e.ti_ku_id_ = q.id_ left join  t_exams ex on e.exam_id_ = ex.id_ where e.ti_ku_id_ = '${this.bankId}' ${param} and e.kao_shi_ren_ = '${this.examineeId}' and (e.zhuang_tai_ = '已完成' or e.zhuang_tai_ = '已交卷') order by field(ed.ti_xing_, '单选题', '多选题', '判断题', '填空题', '简答题')`
-            return new Promise((resolve, reject) => {
-                this.$common.request('sql', sql).then(res => {
-                    const { data = [] } = res.variables || {}
-                    if (!data.length) {
-                        this.$message.error('未查询到已提交的考试记录,请先完成考试!')
-                        this.closeDialog()
-                        return
-                    }
-                    const result = []
-                    data.map(item => {
-                        // 数据转换
-                        if (['单选题', '多选题'].includes(item.questionType)) {
-                            item.options = JSON.parse(item.options)
-                            item.rightKey = item.questionType === '多选题' ? item.rightKey.split(',') : item.rightKey
-                            item.answer = item.questionType === '多选题' ? JSON.parse(item.answer) : item.answer
-                        } else if (item.questionType === '填空题') {
-                            item.rightKey = item.rightKey ? JSON.parse(item.rightKey) : []
-                            item.answer = item.answer ? JSON.parse(item.answer) : Array(item.rightKey.length).fill('')
-                        } else if (item.questionType === '简答题') {
-                            item.rightKey = [item.rightKey]
-                            item.answer = [item.answer]
-                        }
-                        item.img = item.img ? JSON.parse(item.img) : ''
+            const result = [];
+            data.map((item) => {
+              // 数据转换
+              if (["单选题", "多选题"].includes(item.questionType)) {
+                item.options = JSON.parse(item.options);
+                item.rightKey =
+                  item.questionType === "多选题"
+                    ? item.rightKey.split(",")
+                    : item.rightKey;
+                item.answer =
+                  item.questionType === "多选题"
+                    ? JSON.parse(item.answer)
+                    : item.answer;
+              } else if (item.questionType === "填空题") {
+                item.rightKey = item.rightKey ? JSON.parse(item.rightKey) : [];
+                item.answer = item.answer
+                  ? JSON.parse(item.answer)
+                  : Array(item.rightKey.length).fill("");
+              } else if (item.questionType === "简答题") {
+                item.rightKey = [item.rightKey];
+                item.answer = [item.answer];
+              }
+              item.img = item.img ? JSON.parse(item.img) : "";
 
-                        // 数据分组
-                        const index = result.findIndex(i => i.dataId === item.dataId)
-                        if (index === -1) {
-                            const { dataId, examinee, dept, status, applyTime, startTime, endTime, qualifiedRadio, paperName, totalScore, resultScore, scoringType } = item || {}
-                            result.push({
-                                dataId,
-                                examinee,
-                                dept,
-                                status,
-                                applyTime,
-                                startTime,
-                                endTime,
-                                qualifiedRadio,
-                                isQualified: status === '已完成' ? parseFloat(resultScore) >= (parseFloat(qualifiedRadio) / 100 * parseFloat(totalScore)) : '',
-                                paperName,
-                                totalScore: parseFloat(totalScore),
-                                resultScore: parseFloat(resultScore),
-                                totalCount: data.length,
-                                scoringType,
-                                list: [{
-                                    type: item.questionType,
-                                    questions: [item]
-                                }]
-                            })
-                        } else {
-                            const qIndex = result[index].list.findIndex(i => i.type === item.questionType)
-                            if (qIndex === -1) {
-                                result[index].list.push({
-                                    type: item.questionType,
-                                    questions: [item]
-                                })
-                            } else {
-                                result[index].list[qIndex].questions.push(item)
-                            }
-                        }
-                    })
-                    result.forEach(item => {
-                        item.list.forEach(i => {
-                            i.totalScore = i.questions.reduce((a, b) => a + parseFloat(b.questionScore), 0)
-                        })
-                    })
-                    // 获取最高分最低分
-                    const { maxScore, minScore } = result.filter(i => i.status === '已完成').reduce((acc, curr) => {
-                        if (curr.resultScore > acc.maxScore) {
-                            acc.maxScore = curr.resultScore
-                        }
-                        if (curr.resultScore < acc.minScore) {
-                            acc.minScore = curr.resultScore
-                        }
-                        return acc
-                    }, { maxScore: -Infinity, minScore: Infinity })
-                    this.maxScore = maxScore
-                    this.minScore = minScore
-                    resolve(result)
-                }).catch(error => {
-                    reject(error)
-                })
-            })
-        },
-        changePaper (id) {
-            this.showPaperId = id
-            this.paperData = this.paperList.find(i => i.dataId === id)
-            console.log(id)
-        },
-        // 关闭当前窗口
-        closeDialog () {
-            window.removeEventListener('keyup', this.handleKeyPress)
-            this.$emit('close', false)
-        }
-    }
-}
+              // 数据分组
+              const index = result.findIndex((i) => i.dataId === item.dataId);
+              if (index === -1) {
+                const {
+                  dataId,
+                  examinee,
+                  dept,
+                  status,
+                  applyTime,
+                  startTime,
+                  endTime,
+                  qualifiedRadio,
+                  paperName,
+                  totalScore,
+                  resultScore,
+                  scoringType,
+                } = item || {};
+                result.push({
+                  dataId,
+                  examinee,
+                  dept,
+                  status,
+                  applyTime,
+                  startTime,
+                  endTime,
+                  qualifiedRadio,
+                  isQualified:
+                    status === "已完成"
+                      ? parseFloat(resultScore) >=
+                        (parseFloat(qualifiedRadio) / 100) *
+                          parseFloat(totalScore)
+                      : "",
+                  paperName,
+                  totalScore: parseFloat(totalScore),
+                  resultScore: parseFloat(resultScore),
+                  totalCount: data.length,
+                  scoringType,
+                  list: [
+                    {
+                      type: item.questionType,
+                      questions: [item],
+                    },
+                  ],
+                });
+              } else {
+                const qIndex = result[index].list.findIndex(
+                  (i) => i.type === item.questionType
+                );
+                if (qIndex === -1) {
+                  result[index].list.push({
+                    type: item.questionType,
+                    questions: [item],
+                  });
+                } else {
+                  result[index].list[qIndex].questions.push(item);
+                }
+              }
+            });
+            result.forEach((item) => {
+              item.list.forEach((i) => {
+                i.totalScore = i.questions.reduce(
+                  (a, b) => a + parseFloat(b.questionScore),
+                  0
+                );
+              });
+            });
+            // 获取最高分最低分
+            const { maxScore, minScore } = result
+              .filter((i) => i.status === "已完成")
+              .reduce(
+                (acc, curr) => {
+                  if (curr.resultScore > acc.maxScore) {
+                    acc.maxScore = curr.resultScore;
+                  }
+                  if (curr.resultScore < acc.minScore) {
+                    acc.minScore = curr.resultScore;
+                  }
+                  return acc;
+                },
+                { maxScore: -Infinity, minScore: Infinity }
+              );
+            this.maxScore = maxScore;
+            this.minScore = minScore;
+            resolve(result);
+          })
+          .catch((error) => {
+            reject(error);
+          });
+      });
+    },
+    changePaper(id) {
+      this.showPaperId = id;
+      this.paperData = this.paperList.find((i) => i.dataId === id);
+      console.log(id);
+      this.paperStatus = "all";
+    },
+    // 关闭当前窗口
+    closeDialog() {
+      window.removeEventListener("keyup", this.handleKeyPress);
+      this.$emit("close", false);
+    },
+  },
+};
 </script>
 <style lang="scss" scoped>
-    .paper-detail-dialog {
-        ::v-deep {
-            .el-dialog__body {
-                position: relative;
-                height: calc(100vh - 115px);
-                width: 1080px;
-                margin: 0 auto;
-                box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-                .el-radio, .el-checkbox {
-                    display: block;
-                    margin-bottom: 10px;
-                    pointer-events: none;
-                    &:last-child {
-                        margin-bottom: 0;
-                    }
-                }
-                .el-radio-button {
-                    pointer-events: none;
-                }
-                .el-radio__label, .el-checkbox__label {
-                    font-size: 16px;
-                    white-space: normal;
-                    line-height: 1.5;
-                }
-                .el-input {
-                    margin-bottom: 10px;
-                    pointer-events: none;
-                    &:last-child {
-                        margin-bottom: 0;
-                    }
-                }
-                .el-tag {
-                    margin-right: 0;
-                }
-                .ibps-p-0 {
-                    margin: 0;
-                    .list-group {
-                        display: inline-block;
-                        width: 100%;
-                    }
-                }
-            }
-            .el-dialog__header {
-                text-align: center;
-            }
+.paper-detail-dialog {
+  ::v-deep {
+    .el-dialog__body {
+      position: relative;
+      height: calc(100vh - 115px);
+      width: 1080px;
+      margin: 0 auto;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+      .el-radio,
+      .el-checkbox {
+        display: block;
+        margin-bottom: 10px;
+        pointer-events: none;
+        &:last-child {
+          margin-bottom: 0;
         }
-        .container {
-            position: relative;
-            height: calc(100vh - 160px);
-            padding: 20px;
-            .paper-info {
-                display: flex;
-                flex-wrap: wrap;
-                justify-content: flex-start;
-                margin-bottom: 20px;
-                background-color: #f5f5f5;
-                border: 1px solid #ddd;
-                padding: 10px;
-                border-radius: 5px;
-                font-size: 16px;
-            }
+      }
+      .el-radio-button {
+        pointer-events: none;
+      }
+      .el-radio__label,
+      .el-checkbox__label {
+        font-size: 16px;
+        white-space: normal;
+        line-height: 1.5;
+      }
+      .el-input {
+        margin-bottom: 10px;
+        pointer-events: none;
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+      .el-tag {
+        margin-right: 0;
+      }
+      .ibps-p-0 {
+        margin: 0;
+        .list-group {
+          display: inline-block;
+          width: 100%;
+        }
+      }
+    }
+    .el-dialog__header {
+      text-align: center;
+    }
+  }
+  .container {
+    position: relative;
+    height: calc(100vh - 160px);
+    padding: 20px;
+    .paper-info {
+      display: flex;
+      flex-wrap: wrap;
+      justify-content: flex-start;
+      margin-bottom: 20px;
+      background-color: #f5f5f5;
+      border: 1px solid #ddd;
+      padding: 10px;
+      border-radius: 5px;
+      font-size: 16px;
+    }
 
-            .info-item {
-                width: 33.3%;
-                margin-bottom: 10px;
-                display: flex;
-                align-items: center;
-                .label {
-                    width: 100px;
-                    font-weight: bold;
-                    color: #333;
-                }
-                .value {
-                    flex: 1;
-                    color: #666;
-                    white-space: nowrap;
-                    overflow: hidden;
-                    text-overflow: ellipsis;
-                }
-            }
+    .info-item {
+      width: 33.3%;
+      margin-bottom: 10px;
+      display: flex;
+      align-items: center;
+      .label {
+        width: 100px;
+        font-weight: bold;
+        color: #333;
+      }
+      .value {
+        flex: 1;
+        color: #666;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
 
-            .question {
-                font-size: 16px;
-                .question-item {
-                    padding-top: 20px;
-                    &:last-child {
-                        padding-bottom: 20px;
-                    }
-                    .card {
-                        padding-bottom: 10px;
-                        &:last-child {
-                            padding-bottom: 0;
-                        }
-                    }
-                    .type {
-                        font-size: 20px;
-                        font-weight: 600;
-                        margin-bottom: 20px;
-                        .score {
-                            margin-left: 10px;
-                            vertical-align: bottom;
-                        }
-                    }
-                    .stem {
-                        display: flex;
-                        margin-bottom: 10px;
-                        align-items: flex-start;
-                        > span:first-child {
-                            line-height: 1.5;
-                            margin-right: 10px;
-                        }
-                    }
-                    .img {
-                        position: relative;
-                        width: 100%;
-                        margin-bottom: 10px;
-                    }
-                    .answer {
-                        display: flex;
-                        border: 1px solid #ccc;
-                        border-radius: 5px;
-                        .mine, .right {
-                            flex: 1;
-                            padding: 10px;
-                            .title {
-                                font-size: 16px;
-                                color: #01a39e;
-                                margin-bottom: 10px;
-                                .score {
-                                    vertical-align: middle;
-                                }
-                            }
-                        }
-                        .mine {
-                            background-color: #f5f5f5;
-                            border-right: 1px solid #ccc;
-                        }
-                    }
-                }
-            }
+    .question {
+      font-size: 16px;
+      .question-item {
+        padding-top: 20px;
+        &:last-child {
+          padding-bottom: 20px;
         }
-        .date-line {
-            position: fixed;
-            width: 280px;
-            height: calc(100vh - 120px);
-            overflow-y: auto;
-            top: 60px;
-            left: calc(50vw + 540px);
-            padding-right: 8px;
-            ::v-deep {
-                .el-timeline {
-                    padding-left: 10px;
-                }
-                .el-card__body {
-                    padding: 10px 10px;
-                }
-                .el-tag {
-                    margin-left: 5px;
-                }
-            }
-            .timeline-card {
-                cursor: pointer;
-                .card-item {
-                    margin-bottom: 10px;
-                    &:last-child {
-                        margin-bottom: 0;
-                    }
-                }
-            }
-            .active-card {
-                box-shadow: 0 2px 12px 0 rgba(64, 158, 255, 1);
-            }
+        .card {
+          padding-bottom: 10px;
+          &:last-child {
+            padding-bottom: 0;
+          }
         }
-    }
-    @media screen and (max-width: 1600px) {
-        .paper-detail-dialog {
-            ::v-deep {
-                .el-dialog__body {
-                    width: 880px;
-                }
-            }
-            .date-line {
-                left: calc(50vw + 440px);
+        .type {
+          font-size: 20px;
+          font-weight: 600;
+          margin-bottom: 20px;
+          .score {
+            margin-left: 10px;
+            vertical-align: bottom;
+          }
+        }
+        .stem {
+          display: flex;
+          margin-bottom: 10px;
+          align-items: flex-start;
+          > span:first-child {
+            line-height: 1.5;
+            margin-right: 10px;
+          }
+        }
+        .img {
+          position: relative;
+          width: 100%;
+          margin-bottom: 10px;
+        }
+        .answer {
+          display: flex;
+          border: 1px solid #ccc;
+          border-radius: 5px;
+          .mine,
+          .right {
+            flex: 1;
+            padding: 10px;
+            .title {
+              font-size: 16px;
+              color: #01a39e;
+              margin-bottom: 10px;
+              .score {
+                vertical-align: middle;
+              }
             }
+          }
+          .mine {
+            background-color: #f5f5f5;
+            border-right: 1px solid #ccc;
+          }
         }
+      }
     }
-    @media screen and (max-width: 1400px) {
-        .paper-detail-dialog {
-            ::v-deep {
-                .el-dialog__body {
-                    width: 800px;
-                }
-            }
-            .date-line {
-                left: calc(50vw + 400px);
-            }
+  }
+  .date-line {
+    position: fixed;
+    width: 280px;
+    height: calc(100vh - 120px);
+    overflow-y: auto;
+    top: 60px;
+    left: calc(50vw + 540px);
+    padding-right: 8px;
+    ::v-deep {
+      .el-timeline {
+        padding-left: 10px;
+      }
+      .el-card__body {
+        padding: 10px 10px;
+      }
+      .el-tag {
+        margin-left: 5px;
+      }
+    }
+    .timeline-card {
+      cursor: pointer;
+      .card-item {
+        margin-bottom: 10px;
+        &:last-child {
+          margin-bottom: 0;
         }
+      }
+    }
+    .active-card {
+      box-shadow: 0 2px 12px 0 rgba(64, 158, 255, 1);
+    }
+  }
+}
+@media screen and (max-width: 1600px) {
+  .paper-detail-dialog {
+    ::v-deep {
+      .el-dialog__body {
+        width: 880px;
+      }
     }
+    .date-line {
+      left: calc(50vw + 440px);
+    }
+  }
+}
+@media screen and (max-width: 1400px) {
+  .paper-detail-dialog {
+    ::v-deep {
+      .el-dialog__body {
+        width: 800px;
+      }
+    }
+    .date-line {
+      left: calc(50vw + 400px);
+    }
+  }
+}
 </style>