Browse Source

feat: 驳回命题任务可以修改考试对象

zhangjie 10 tháng trước cách đây
mục cha
commit
bb46a8962e

+ 7 - 4
src/modules/exam/components/createExamAndPrintTask/InfoPrintTask.vue

@@ -220,7 +220,7 @@
     <preview-task-student
       ref="PreviewTaskStudent"
       :student-list="examStudentList"
-      :show-student="showStudent"
+      :editable="!showStudent"
       @close="previewStudentClosed"
     ></preview-task-student>
   </div>
@@ -400,15 +400,18 @@ export default {
     updateData() {
       const tableData = this.tableData.map((row) => {
         const nrow = { ...row };
-        nrow.examTaskStudentObjectParamList = row.examTaskStudentObjectParamList
-          .filter((item) => item.enable)
-          .map((item) => item.id);
+        // TODO:注意这里字段改了
+        nrow.examTaskStudentObjectParamListIds =
+          row.examTaskStudentObjectParamList
+            .filter((item) => item.enable)
+            .map((item) => item.id);
         return nrow;
       });
 
       this.updateTaskInfo({
         infoPrintTask: {
           ...this.modalForm,
+          model2Students: this.model2Students,
           list: tableData,
         },
       });

+ 3 - 3
src/modules/exam/components/createExamAndPrintTask/PreviewTaskStudent.vue

@@ -64,7 +64,7 @@
       <el-table-column prop="teachClassName" label="教学班" min-width="120">
       </el-table-column>
       <el-table-column
-        v-if="!showStudent"
+        v-if="editable"
         class-name="action-column"
         label="操作"
         width="80px"
@@ -105,9 +105,9 @@ export default {
         return [];
       },
     },
-    showStudent: {
+    editable: {
       type: Boolean,
-      default: false,
+      default: true,
     },
   },
   data() {

+ 19 - 2
src/modules/exam/components/taskApply/ModifyTaskApply.vue

@@ -14,7 +14,8 @@
     <div v-if="dataReady" class="apply-content task-detail">
       <task-info></task-info>
       <task-paper ref="TaskPaper"></task-paper>
-      <task-flow ref="TaskFlow"></task-flow>
+      <task-print ref="TaskPrint"></task-print>
+      <task-flow v-if="showTaskPrint" ref="TaskFlow"></task-flow>
     </div>
 
     <div slot="footer">
@@ -49,6 +50,7 @@ import { COMMON_CARD_RULE_ID } from "@/constants/enumerate";
 
 import TaskInfo from "./TaskInfo.vue";
 import TaskPaper from "./TaskPaper.vue";
+import TaskPrint from "./TaskPrint.vue";
 import TaskFlow from "./TaskFlow.vue";
 
 const initExamTask = {
@@ -99,6 +101,8 @@ const initTaskApply = {
   drawCount: 1,
   exposedPaperType: "",
   auditContent: [],
+  // 入库申请中新建的命题任务提交信息
+  examTaskContent: "",
 
   // 流程
   flowId: "",
@@ -119,7 +123,7 @@ const initTaskApply = {
 
 export default {
   name: "modify-task-apply",
-  components: { TaskInfo, TaskPaper, TaskFlow },
+  components: { TaskInfo, TaskPaper, TaskFlow, TaskPrint },
   props: {
     rowData: {
       type: Object,
@@ -155,6 +159,12 @@ export default {
         return "审核入库申请";
       }
     },
+    showTaskPrint() {
+      return (
+        this.examTask.examModel !== "IS_MODEL3" &&
+        this.curTaskApply.examTaskContent
+      );
+    },
   },
   data() {
     return {
@@ -259,6 +269,7 @@ export default {
     },
     async submit() {
       if (!this.$refs.TaskPaper.checkData()) return;
+      if (this.showTaskPrint && !this.$refs.TaskPrint.checkData()) return;
       if (!this.$refs.TaskFlow.checkData()) return;
 
       const result = await this.$confirm(
@@ -278,6 +289,12 @@ export default {
         ...flowData,
         operateType: "SUBMIT",
       };
+
+      if (this.showTaskPrint) {
+        const examTaskContent = this.$refs.TaskPrint.getData(datas);
+        datas.examTaskContent = JSON.stringify(examTaskContent);
+      }
+
       const data = await updateTaskApply(datas).catch(() => {});
       if (!data) return;
       this.$message.success("提交成功!");

+ 649 - 0
src/modules/exam/components/taskApply/TaskPrint.vue

@@ -0,0 +1,649 @@
+<template>
+  <div class="task-print">
+    <template v-if="!IS_REBUILD">
+      <p v-if="IS_MODEL2" class="tips-info mb-2">
+        考试需要命题老师提交具体印刷份数
+      </p>
+      <p v-else class="tips-info mb-2">
+        考试需要命题老师提交完整的考务数据,每个卷袋表示一个考场,人数字段为应考人数,备份数量为该考场试卷题卡备用数量
+      </p>
+    </template>
+    <div v-if="IS_MODEL2 || IS_REBUILD" class="part-box">
+      <div class="box-justify mb-1">
+        <div></div>
+        <el-button v-if="editable" type="primary" @click="toSelectClass"
+          >选择班级</el-button
+        >
+      </div>
+      <table class="table">
+        <colgroup>
+          <col width="90" />
+          <col width="120" />
+          <col width="120" />
+          <col width="200" />
+          <col width="300" />
+          <col width="100" />
+        </colgroup>
+        <tr>
+          <th>卷袋序号</th>
+          <th>印刷份数</th>
+          <th>备份数量</th>
+          <th>印刷室</th>
+          <th>班级</th>
+          <th>操作</th>
+        </tr>
+        <tr>
+          <td>1</td>
+          <td>
+            <el-input-number
+              v-model="modalForm.totalSubjects"
+              class="width-full"
+              :min="1"
+              :max="999999"
+              :step="1"
+              step-strictly
+              :controls="false"
+              :disabled="IS_REBUILD || !editable"
+            ></el-input-number>
+          </td>
+          <td>
+            <el-input-number
+              v-model="modalForm.backupCount"
+              class="width-full"
+              :min="infoExamPrintPlan.backupCount"
+              :max="999999"
+              :step="1"
+              step-strictly
+              :controls="false"
+              :disabled="!editable"
+            ></el-input-number>
+          </td>
+          <td>
+            <el-select
+              v-model="modalForm.printHouseId"
+              placeholder="请选择"
+              class="width-full"
+              filterable
+              :disabled="!editable"
+            >
+              <el-option
+                v-for="room in printHouses"
+                :key="room.printHouseId"
+                :value="room.printHouseId"
+                :label="room.printHouseName"
+              ></el-option>
+            </el-select>
+          </td>
+          <td>
+            {{ modalForm.className }}
+          </td>
+          <td>
+            <el-button
+              class="btn-primary"
+              type="text"
+              @click="toViewModel2Student"
+              >查看考生</el-button
+            >
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div v-else class="part-box">
+      <div class="box-justify mb-2">
+        <div>
+          <p>
+            每个卷袋包含一个考场全部文件(试卷、题卡、卷袋贴、签到表等文件)
+          </p>
+          <p>
+            共{{ packageInfos.packageCount }}个卷袋,{{
+              packageInfos.studentCount
+            }}个考生,共印试卷{{ packageInfos.paperCount }}份(正式{{
+              packageInfos.paperReleaseCount
+            }}份,备用{{ packageInfos.paperBackupCount }}份)
+          </p>
+        </div>
+        <div>
+          <el-button
+            v-if="checkPrivilege('button', 'ExamTaskStudentObject') && editable"
+            type="primary"
+            @click="toAdd"
+            >新增考试对象</el-button
+          >
+        </div>
+      </div>
+      <el-table ref="TableList" :data="tableData" border>
+        <el-table-column type="index" width="80" label="卷袋序号">
+        </el-table-column>
+        <el-table-column prop="className" label="考试对象" width="160">
+        </el-table-column>
+        <el-table-column prop="studentCount" label="人数" width="60">
+        </el-table-column>
+        <el-table-column prop="backupCount" label="备份数量" width="90">
+          <template slot-scope="scope">
+            <el-input-number
+              v-model="scope.row.backupCount"
+              style="width: 60px"
+              :min="scope.row.minBackupCount"
+              :max="99999"
+              :step="1"
+              step-strictly
+              :controls="false"
+              :disabled="!editable"
+              @change="backupCountChange"
+            ></el-input-number>
+          </template>
+        </el-table-column>
+        <el-table-column label="考试时间" width="300">
+          <template slot-scope="scope">
+            <template v-if="!scope.row.canEditTime">
+              {{ scope.row.examDate }} {{ scope.row.examTime }}
+            </template>
+            <template v-else>
+              <one-date-time-range
+                :start-date-time.sync="scope.row.examStartTime"
+                :end-date-time.sync="scope.row.examEndTime"
+                :disabled="!editable"
+                @change="timeChange(scope.row)"
+              >
+              </one-date-time-range>
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column prop="examPlace" label="校区(考点)" width="140">
+          <template slot-scope="scope">
+            <el-input
+              v-model.trim="scope.row.examPlace"
+              :maxlength="20"
+              clearable
+              :disabled="!editable"
+            ></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column prop="examRoom" label="考试教室(考场)" width="140">
+          <template slot-scope="scope">
+            <el-input
+              v-model.trim="scope.row.examRoom"
+              :maxlength="20"
+              clearable
+              :disabled="!editable"
+            ></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column prop="printHouseName" label="印刷室" width="140">
+          <template slot-scope="scope">
+            <el-select
+              v-model="scope.row.printHouseId"
+              placeholder="请选择"
+              filterable
+              :disabled="!editable"
+              @change="() => printHouseChange(scope.row)"
+            >
+              <el-option
+                v-for="room in printHouses"
+                :key="room.printHouseId"
+                :value="room.printHouseId"
+                :label="room.printHouseName"
+              ></el-option>
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="操作"
+          width="120"
+          class-name="action-column"
+          align="center"
+          fixed="right"
+        >
+          <template slot-scope="scope">
+            <el-button
+              v-if="editable"
+              class="btn-danger"
+              type="text"
+              @click="toDelete(scope.$index)"
+              >取消</el-button
+            >
+            <el-button
+              class="btn-primary"
+              type="text"
+              @click="toViewStudent(scope.row)"
+              >查看考生</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- ModifyExamTaskStudent -->
+    <modify-exam-task-student
+      v-if="editable"
+      ref="ModifyExamTaskStudent"
+      :disabled-ids="disabledStudentIds"
+      :filter-params="{
+        courseId: infoExamTask.courseId,
+        examId: infoExamTask.examId,
+        teachingRoomId: infoExamTask.teachingRoomId,
+      }"
+      :show-student="showStudent"
+      :selected-ids="IS_MODEL2 || IS_REBUILD ? model2ClassList : null"
+      @modified="examStudentModified"
+    ></modify-exam-task-student>
+    <!-- PreviewTaskStudent -->
+    <preview-task-student
+      ref="PreviewTaskStudent"
+      :student-list="examStudentList"
+      :editable="!showStudent && editable"
+      @close="previewStudentClosed"
+    ></preview-task-student>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import { calcSum, getExamDateTime, getTimeDatestamp } from "@/plugins/utils";
+import { listTaskPrintHouse } from "../../api";
+import { examConfigByExamIdOrgId } from "../../../base/api";
+import ModifyExamTaskStudent from "../createExamAndPrintTask/ModifyExamTaskStudent.vue";
+import PreviewTaskStudent from "../createExamAndPrintTask/PreviewTaskStudent.vue";
+import OneDateTimeRange from "@/components/OneDateTimeRange.vue";
+
+const initModalForm = {
+  paperNumber: "",
+  courseName: "",
+  courseCode: "",
+  courseId: "",
+  totalSubjects: 1,
+  printHouseId: "",
+  backupCount: null,
+  className: "",
+  basicStudentIds: [],
+};
+
+export default {
+  name: "task-print",
+  components: { ModifyExamTaskStudent, PreviewTaskStudent, OneDateTimeRange },
+  data() {
+    return {
+      modalForm: {
+        ...initModalForm,
+      },
+      tableData: [],
+      model2ClassList: [],
+      model2Students: [],
+      curRow: {},
+      printHouses: [],
+      packageInfos: {
+        packageCount: 0,
+        studentCount: 0,
+        paperCount: 0,
+        paperReleaseCount: 0,
+        paperBackupCount: 0,
+      },
+      selectedStudentIds: [],
+      disabledStudentIds: [],
+      examStudentList: [],
+      showStudent: false,
+      curCreateTime: [],
+      defaultTime: "",
+      // exam task content
+      infoExamTask: {},
+      infoExamTaskDetail: {},
+      infoExamPrintPlan: {},
+      infoPrintTask: {},
+    };
+  },
+  computed: {
+    ...mapState("exam", ["editType", "examTask", "curTaskApply"]),
+    IS_MODEL2() {
+      return this.examTask.examModel === "MODEL2";
+    },
+    IS_REBUILD() {
+      return this.examTask.category === "REBUILD";
+    },
+    editable() {
+      return this.taskStatus.IS_APPLY;
+    },
+  },
+  mounted() {
+    const curDate = getTimeDatestamp(Date.now());
+    const hour = 60 * 60 * 1000;
+    this.curCreateTime = [curDate + 8 * hour, curDate + 10 * hour];
+    this.defaultTime = curDate;
+
+    this.initData();
+  },
+  methods: {
+    async getPrintHouses() {
+      this.printHouses = await listTaskPrintHouse();
+    },
+    async getInfoPrintTask() {
+      const orgInfo = this.$ls.get("user", { orgInfo: {} }).orgInfo;
+      const examPrintPlan = await examConfigByExamIdOrgId({
+        examId: this.examTask.examId,
+        orgId: orgInfo.id,
+      });
+      this.infoExamPrintPlan = examPrintPlan || {};
+    },
+    async initData() {
+      await this.getPrintHouses();
+      await this.getInfoPrintTask();
+
+      if (!this.curTaskApply.examTaskContent) return;
+
+      const { examTask, examTaskDetail, examDetail } = JSON.parse(
+        this.curTaskApply.examTaskContent
+      );
+      this.infoExamTask = examTask;
+      this.infoExamTaskDetail = examTaskDetail;
+      this.infoPrintTask = examDetail;
+
+      this.modalForm = this.$objAssign(initModalForm, this.infoPrintTask);
+      if (this.IS_MODEL2 || this.IS_REBUILD) {
+        this.model2ClassList = this.infoPrintTask.className.split(",");
+        this.model2Students = this.infoPrintTask.model2Students || [];
+        return;
+      }
+
+      this.tableData = this.infoPrintTask.list;
+      this.updatePackageInfos();
+    },
+    updatePackageInfos() {
+      this.packageInfos.packageCount = this.tableData.length;
+      this.packageInfos.studentCount = calcSum(
+        this.tableData.map((item) => item.studentCount)
+      );
+      this.packageInfos.paperReleaseCount = this.packageInfos.studentCount;
+      this.packageInfos.paperBackupCount = calcSum(
+        this.tableData.map((item) => item.backupCount || 0)
+      );
+      this.packageInfos.paperCount =
+        this.packageInfos.paperReleaseCount +
+        this.packageInfos.paperBackupCount;
+    },
+    getBackupCount(studentCount) {
+      const { backupCount } = this.infoExamPrintPlan;
+      if (backupCount < 1) {
+        const count = Math.ceil(backupCount * studentCount);
+        return { backupCount: count, minBackupCount: count };
+      }
+      return { backupCount, minBackupCount: backupCount };
+    },
+    timeChange(row) {
+      if (!row.examStartTime || !row.examEndTime) {
+        row.examDate = "";
+        row.examTime = "";
+        return;
+      }
+
+      Object.assign(row, getExamDateTime(row.examStartTime, row.examEndTime));
+    },
+    backupCountChange() {
+      this.updatePackageInfos();
+    },
+    printHouseChange(row) {
+      const curHouse = this.printHouses.find(
+        (item) => item.printHouseId === row.printHouseId
+      );
+      if (curHouse) {
+        row.printHouseName = curHouse.printHouseName;
+      }
+    },
+    toAdd() {
+      if (!this.infoExamTask.courseId) {
+        this.$message.error("请先选择课程");
+        return;
+      }
+
+      this.disabledStudentIds = this.getTabelStudentIds();
+      this.showStudent =
+        this.checkPrivilege("button", "SelectStudent") &&
+        this.infoExamTask.examModel !== "MODEL1";
+      this.$refs.ModifyExamTaskStudent.open();
+    },
+    getTabelStudentIds() {
+      return this.tableData
+        .map((item) => {
+          return item.examTaskStudentObjectParamList.map((elem) => elem.id);
+        })
+        .flat();
+    },
+    getInitTableRow() {
+      let modalFormData = { ...this.modalForm };
+      delete modalFormData.printHouseId;
+      let data = {
+        id: this.$randomCode(),
+        groupName: "",
+        examPlace: "",
+        examRoom: "",
+        examStartTime: "",
+        examEndTime: "",
+        examDate: "",
+        examTime: "",
+        examSetDate: [],
+        className: "",
+        studentCount: "",
+        printHouseId: "",
+        printHouseName: "",
+        backupCount: 0,
+        minBackupCount: 0,
+        isSelectStudent: true,
+        examTaskStudentObjectParamList: [],
+        classNames: [],
+        canEditTime: false,
+        ...modalFormData,
+      };
+      return data;
+    },
+    examStudentModified({ selectedStudents }) {
+      if (this.IS_MODEL2 || this.IS_REBUILD) {
+        this.model2ClassList = selectedStudents.map((item) => item.label);
+        this.modalForm.className = this.model2ClassList.join();
+        this.model2Students = selectedStudents
+          .map((item) => item.children)
+          .flat()
+          .map((item) => {
+            return { ...item, enable: true };
+          });
+        this.modalForm.basicStudentIds = this.model2Students.map(
+          (item) => item.id
+        );
+        this.modalForm.totalSubjects = this.model2Students.length;
+        return;
+      }
+
+      this.updateTableData(selectedStudents);
+
+      this.updatePackageInfos();
+    },
+    updateTableData(selectedStudents) {
+      const groupMap = {};
+      selectedStudents.forEach((item) => {
+        item.children.forEach((student) => {
+          const { examRoom, examPlace, examStartTime, examEndTime } = student;
+          const groupName = `${examRoom}_${examPlace}_${examStartTime}_${examEndTime}`;
+          if (!groupMap[groupName]) {
+            groupMap[groupName] = {
+              examTaskStudentObjectParamList: [],
+              classNames: [],
+              examRoom,
+              examPlace,
+              examStartTime,
+              examEndTime,
+              groupName,
+            };
+          }
+          // TODO:可能需要简化一下学生信息
+          groupMap[groupName].examTaskStudentObjectParamList.push({
+            ...student,
+            enable: true,
+          });
+          if (!groupMap[groupName].classNames.includes(item.label)) {
+            groupMap[groupName].classNames.push(item.label);
+          }
+        });
+      });
+
+      const tableGroupNames = this.tableData.map((item) => {
+        const { examRoom, examPlace, examStartTime, examEndTime } = item;
+        return `${examRoom}_${examPlace}_${examStartTime}_${examEndTime}`;
+      });
+
+      Object.keys(groupMap).forEach((groupName) => {
+        const data = groupMap[groupName];
+        const ind = tableGroupNames.indexOf(groupName);
+        if (ind !== -1) {
+          this.tableData[ind].examTaskStudentObjectParamList.push(
+            ...data.examTaskStudentObjectParamList
+          );
+          const classNameSet = new Set([
+            ...this.tableData[ind].classNames,
+            ...data.classNames,
+          ]);
+          const studentCount =
+            this.tableData[ind].examTaskStudentObjectParamList.length;
+          this.tableData[ind].studentCount = studentCount;
+          this.tableData[ind].classNames = Array.from(classNameSet);
+          this.tableData[ind].className = this.tableData[ind].classNames.join();
+
+          Object.assign(this.tableData[ind], this.getBackupCount(studentCount));
+          return;
+        }
+
+        const times =
+          data.examStartTime && data.examEndTime
+            ? getExamDateTime(data.examStartTime, data.examEndTime)
+            : { examSetDate: [...this.curCreateTime] };
+
+        const tableRow = this.$objAssign(this.getInitTableRow(), {
+          ...data,
+          ...times,
+          ...this.getBackupCount(data.examTaskStudentObjectParamList.length),
+          className: data.classNames.join(),
+          studentCount: data.examTaskStudentObjectParamList.length,
+          canEditTime: !data.examStartTime || !data.examEndTime,
+        });
+        this.tableData.push(tableRow);
+      });
+    },
+    toDelete(index) {
+      this.tableData.splice(index, 1);
+      this.updatePackageInfos();
+    },
+    toViewStudent(row) {
+      // console.log(row);
+      this.curRow = row;
+      this.examStudentList = row.examTaskStudentObjectParamList;
+      this.$refs.PreviewTaskStudent.open();
+    },
+    toViewModel2Student() {
+      this.examStudentList = this.model2Students;
+      this.$refs.PreviewTaskStudent.open();
+    },
+    previewStudentClosed() {
+      const students = this.examStudentList.filter((item) => item.enable);
+      const studentCount = students.length;
+
+      if (this.IS_MODEL2 || this.IS_REBUILD) {
+        this.modalForm.totalSubjects = studentCount;
+        this.modalForm.basicStudentIds = students.map((item) => item.id);
+        return;
+      }
+
+      Object.assign(this.curRow, {
+        ...this.getBackupCount(studentCount),
+        studentCount,
+      });
+    },
+    // model2 select class
+    toSelectClass() {
+      if (!this.infoExamTask.courseId) {
+        this.$message.error("请先选择课程");
+        return;
+      }
+
+      this.disabledStudentIds = [];
+      this.showStudent = false;
+      this.$refs.ModifyExamTaskStudent.open();
+    },
+    // action
+    getData(examTaskData) {
+      const tableData = this.tableData.map((row) => {
+        const nrow = { ...row };
+        nrow.examTaskStudentObjectParamListIds =
+          row.examTaskStudentObjectParamList
+            .filter((item) => item.enable)
+            .map((item) => item.id);
+        return nrow;
+      });
+
+      this.infoPrintTask = {
+        ...this.modalForm,
+        model2Students: this.model2Students,
+        list: tableData,
+      };
+
+      this.$objAssign(this.infoExamTaskDetail, examTaskData);
+      const examTaskContent = {
+        examTask: this.infoExamTask,
+        examTaskDetail: this.infoExamTaskDetail,
+        examDetail: this.infoPrintTask,
+      };
+      examTaskContent.examTask.examStartTime = this.infoPrintTask.examStartTime;
+      examTaskContent.examTask.examEndTime = this.infoPrintTask.examEndTime;
+      return examTaskContent;
+    },
+    checkData() {
+      if (this.IS_MODEL2 || this.IS_REBUILD) {
+        if (!this.modalForm.totalSubjects) {
+          this.$message.error("请输入印刷份数!");
+          return;
+        }
+        if (!this.modalForm.printHouseId) {
+          this.$message.error("请选择印刷室!");
+          return;
+        }
+        if (!this.modalForm.className) {
+          this.$message.error("请选择班级!");
+          return;
+        }
+        return true;
+      }
+
+      if (!this.tableData.length) {
+        this.$message.error("请添加考试对象!");
+        return;
+      }
+
+      const errorMsg = [];
+      this.tableData.forEach((row) => {
+        const errorFields = [];
+
+        if (!row.examStartTime || !row.examEndTime) {
+          errorFields.push("考试时间");
+        }
+        if (!row.backupCount) {
+          errorFields.push("备份数量");
+        }
+        if (!row.examPlace) {
+          errorFields.push("校区(考点)");
+        }
+        if (!row.examRoom) {
+          errorFields.push("考试教室(考场)");
+        }
+        if (!row.printHouseId) {
+          errorFields.push("印刷室");
+        }
+        if (errorFields.length) {
+          errorMsg.push(
+            `考试对象${row.className}中,${errorFields.join("、")}必须填写`
+          );
+        }
+      });
+      if (errorMsg.length) {
+        this.$message.error(errorMsg.join("。"));
+        return;
+      }
+      return true;
+    },
+  },
+};
+</script>