Ver Fonte

自建命题任务

zhangjie há 3 anos atrás
pai
commit
489b17107f

+ 18 - 0
src/assets/styles/pages.scss

@@ -43,6 +43,11 @@
     margin-top: 50px;
     text-align: center;
   }
+  .task-audit {
+    padding-top: 15px;
+    border-top: 2px dashed $--color-border;
+    margin-top: 30px;
+  }
 
   .image-item {
     display: inline-block;
@@ -105,6 +110,9 @@
       color: $--color-text-gray-3;
     }
   }
+  .image-list-none {
+    color: $--color-text-gray-2;
+  }
 }
 // apply-audit-history
 .apply-audit-history {
@@ -146,6 +154,16 @@
     }
   }
 }
+// create-task-apply
+.create-task-apply {
+  .apply-step {
+    margin-bottom: 60px;
+  }
+  .apply-body {
+    width: 900px;
+    margin: 0 auto;
+  }
+}
 // wait-task
 .wait-task {
   .wait-module {

+ 1 - 1
src/modules/base/api.js

@@ -137,7 +137,7 @@ export const flowEnd = flowId => {
   return $post("/api/admin/flow/end", { flowId });
 };
 export const flowDelete = id => {
-  return $post("/api/admin/flow/enable", { id });
+  return $postParam("/api/admin/flow/enable", { id });
 };
 export const flowRegister = (datas, headers) => {
   return $post("/api/admin/flow/register", datas, { headers });

+ 18 - 0
src/modules/exam/api.js

@@ -78,10 +78,25 @@ export const taskAllApproverPeople = ({ taskId, courseCode }) => {
     courseCode
   });
 };
+export const taskApproverExchange = ({ taskId, userId }) => {
+  return $postParam("/api/admin/flow/task/approver/exchange", {
+    taskId,
+    userId
+  });
+};
+export const taskAllApproverExchangePeople = ({ taskId, realName }) => {
+  return $postParam("/api/admin/flow/task/approver/exchange/people", {
+    taskId,
+    realName
+  });
+};
 // 命题老师创建命题任务
 export const teacherCreateTaskApply = datas => {
   return $post("/api/admin/exam/task/save_task_apply", datas);
 };
+export const teacherSubmitTaskApply = datas => {
+  return $post("/api/admin/exam/task/submit_task_apply", datas);
+};
 export const teacherCancelTaskApply = examTaskId => {
   return $postParam("/api/admin/exam/task/remove_task_apply", { examTaskId });
 };
@@ -149,6 +164,9 @@ export const submitTaskPrint = datas => {
 export const listTaskPrintClass = datas => {
   return $postParam("/api/admin/exam/task/list_task_print_class", datas);
 };
+export const listTaskApplyClass = datas => {
+  return $postParam("/api/admin/exam/task/list_task_apply_class", datas);
+};
 // 印刷室查询
 export const listTaskPrintHouse = datas => {
   return $postParam("/api/admin/exam/task/list_task_print_house", datas);

+ 124 - 61
src/modules/exam/components/ApplyContent.vue

@@ -139,7 +139,7 @@
 
       <h4 class="mb-2" v-if="IS_APPLY">上传入库审核表(最多4张)</h4>
       <h4 class="mb-2" v-else>入库审核表</h4>
-      <div class="image-list mb-4">
+      <div class="image-list">
         <div
           class="image-item"
           v-for="(img, index) in paperConfirmAttachments"
@@ -167,62 +167,88 @@
           <i class="el-icon-plus"></i>
         </div>
       </div>
+      <div
+        v-if="!IS_APPLY && !paperConfirmAttachments.length"
+        class="image-list-none"
+      >
+        暂无数据
+      </div>
 
       <!-- audit -->
-      <el-form
-        v-if="IS_AUDIT"
-        ref="auditModalComp"
-        :model="auditModal"
-        :rules="auditRules"
-        label-width="90px"
-      >
-        <el-form-item prop="approvePass" label="审批操作:">
-          <el-radio-group
-            v-model="auditModal.approvePass"
-            @change="approvePassChange"
-          >
-            <el-radio
-              v-for="(val, key) in TASK_AUDIT_RESULT"
-              :key="key"
-              :label="key"
-              >{{ val }}</el-radio
-            >
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item
-          v-if="auditModal.approvePass === 'REJECT'"
-          prop="setup"
-          label="驳回节点:"
+      <div v-if="IS_AUDIT" class="task-audit">
+        <el-form
+          ref="auditModalComp"
+          :model="auditModal"
+          :rules="auditRules"
+          label-width="90px"
         >
-          <el-select v-model="auditModal.setup" placeholder="请选择">
-            <el-option
-              v-for="item in setups"
-              :key="item.taskKey"
-              :value="item.setup"
-              :label="item.taskName"
+          <el-form-item prop="approvePass" label="审批操作:">
+            <el-radio-group
+              v-model="auditModal.approvePass"
+              @change="approvePassChange"
             >
-            </el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item
-          :key="auditModal.approvePass"
-          prop="remark"
-          label="审批意见:"
-        >
-          <el-input
-            class="mb-2"
-            v-model="auditModal.remark"
-            type="textarea"
-            resize="none"
-            :rows="5"
-            :maxlength="1000"
-            clearable
-            show-word-limit
-            placeholder="建议不超过1000个字"
-            ref="ReasonInput"
-          ></el-input>
-        </el-form-item>
-      </el-form>
+              <el-radio
+                v-for="(val, key) in TASK_AUDIT_RESULT"
+                :key="key"
+                :label="key"
+                >{{ val }}</el-radio
+              >
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item
+            v-if="auditModal.approvePass === 'REJECT'"
+            prop="setup"
+            label="驳回节点:"
+          >
+            <el-select v-model="auditModal.setup" placeholder="请选择">
+              <el-option
+                v-for="item in setups"
+                :key="item.taskKey"
+                :value="item.setup"
+                :label="item.taskName"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            v-if="auditModal.approvePass !== 'EXCHANGE'"
+            :key="auditModal.approvePass"
+            prop="remark"
+            label="审批意见:"
+          >
+            <el-input
+              class="mb-2"
+              v-model="auditModal.remark"
+              type="textarea"
+              resize="none"
+              :rows="5"
+              :maxlength="1000"
+              clearable
+              show-word-limit
+              placeholder="建议不超过1000个字"
+              ref="ReasonInput"
+            ></el-input>
+          </el-form-item>
+          <el-form-item
+            v-if="auditModal.approvePass === 'EXCHANGE'"
+            prop="userId"
+            label="审批人:"
+          >
+            <el-select
+              v-model="auditModal.userId"
+              filterable
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="user in approveExchangePeople"
+                :key="user.id"
+                :value="user.id"
+                :label="user.realName"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
     </div>
     <div class="task-action">
       <el-button
@@ -283,7 +309,9 @@ import {
   updateTaskReview,
   switchCardCreateMethod,
   taskAllFlowSetups,
-  taskAllApproverPeople
+  taskAllApproverPeople,
+  taskAllApproverExchangePeople,
+  taskApproverExchange
 } from "../api";
 import { attachmentPreview } from "../../login/api";
 import SimpleImagePreview from "@/components/SimpleImagePreview";
@@ -344,7 +372,7 @@ export default {
       task: {},
       reason: "",
       // audit
-      TASK_AUDIT_RESULT,
+      TASK_AUDIT_RESULT: { ...TASK_AUDIT_RESULT, EXCHANGE: "转他人审批" },
       flows: [],
       setups: [],
       auditModal: {
@@ -383,8 +411,16 @@ export default {
             },
             trigger: "change"
           }
+        ],
+        userId: [
+          {
+            required: true,
+            message: "请选择审批人",
+            trigger: "change"
+          }
         ]
       },
+      approveExchangePeople: [],
       // image-preview
       curImage: {},
       curImageIndex: 0
@@ -469,6 +505,8 @@ export default {
         : [];
 
       this.buildSteps();
+
+      if (this.IS_AUDIT) this.getExchangeApproverPeople();
     },
     async buildSteps() {
       if (
@@ -527,6 +565,22 @@ export default {
       }
       this.$emit("step-change", this.flows);
     },
+    async getExchangeApproverPeople() {
+      const data = await taskAllApproverExchangePeople({
+        taskId: this.curTaskApply.flowTaskId
+      });
+
+      this.approveExchangePeople =
+        (data &&
+          data.approveUserList &&
+          data.approveUserList[0] &&
+          data.approveUserList[0].approveUser) ||
+        [];
+      const userId = this.$ls.get("user", { id: "" }).id;
+      this.approveExchangePeople = this.approveExchangePeople.filter(
+        user => user.id !== userId
+      );
+    },
     approvePassChange() {
       this.auditRules.remark[0].required =
         this.auditModal.approvePass === "REJECT";
@@ -758,8 +812,7 @@ export default {
       const valid = await this.$refs.auditModalComp.validate().catch(() => {});
       if (!valid) return;
 
-      const actionName =
-        this.auditModal.approvePass === "REJECT" ? "驳回" : "通过";
+      const actionName = this.TASK_AUDIT_RESULT[this.auditModal.approvePass];
       const result = await this.$confirm(
         `确定${actionName}该申请吗?`,
         "提示",
@@ -769,10 +822,20 @@ export default {
       ).catch(() => {});
       if (result !== "confirm") return;
 
-      let datas = { ...this.auditModal };
-      datas.taskId = this.curTaskApply.flowTaskId;
-      const data = await updateTaskReview(datas).catch(() => {});
-      if (!data) return;
+      if (this.auditModal.approvePass === "EXCHANGE") {
+        let datas = {
+          taskId: this.curTaskApply.flowTaskId,
+          userId: this.auditModal.userId
+        };
+        const data = await taskApproverExchange(datas).catch(() => {});
+        if (!data) return;
+      } else {
+        let datas = { ...this.auditModal };
+        datas.taskId = this.curTaskApply.flowTaskId;
+        const data = await updateTaskReview(datas).catch(() => {});
+        if (!data) return;
+      }
+
       this.$message.success("审批成功!");
       this.$emit("modified");
     },

+ 1 - 0
src/modules/exam/components/CreatePrintTask.vue

@@ -315,6 +315,7 @@ export default {
         return info;
       });
       datas.extendFields = JSON.stringify(extendFields);
+      datas.extends = { ...this.extendFieldModal };
 
       const data = await createTaskPrint(datas).catch(() => {});
       this.isSubmit = false;

+ 0 - 2
src/modules/exam/components/PublishPrintTask.vue

@@ -168,8 +168,6 @@ export default {
       timeDisabled: false,
       tableData: [],
       curRow: {},
-      printRooms: [],
-      teachers: [],
       clazzs: [],
       curClassId: "",
       extendFields: [],

+ 120 - 102
src/modules/exam/components/createTaskApply/CreateTaskApply.vue

@@ -7,53 +7,65 @@
     :show-close="false"
     append-to-body
     fullscreen
-    destroy-on-close
     @open="visibleChange"
   >
     <div slot="title">
       <span class="el-dialog__title">新增命题申请</span>
-      <button class="el-dialog__headerbtn" @click="close"></button>
     </div>
 
-    <el-steps class="apply-step" :active="current" align-center>
-      <el-step
-        v-for="step in steps"
-        :key="step.name"
-        :title="step.name"
-        :description="step.title"
-      >
+    <el-steps
+      class="apply-step"
+      :active="current"
+      align-center
+      finish-status="success"
+    >
+      <el-step v-for="step in steps" :key="step.name" :title="step.title">
       </el-step>
     </el-steps>
 
-    <component
-      :is="currentComponent"
-      :ref="currentComponent"
-      @next-step="toNext"
-      @on-ready="compReady"
-    ></component>
+    <div class="apply-body" v-if="dataReady">
+      <component
+        :is="currentComponent"
+        :ref="currentComponent"
+        :datas="infos"
+        @next-step="toNext"
+        @on-ready="compReady"
+      ></component>
+    </div>
 
-    <div slot="footer">
-      <el-button v-if="!isFirstStep" type="primary" @click="prevStep"
+    <div class="text-center">
+      <el-button
+        v-if="!isFirstStep"
+        type="primary"
+        :disabled="loading"
+        @click="prevStep"
         >上一步</el-button
       >
       <el-button
         v-if="isLastStep"
         type="success"
-        :disabled="isSubmit"
-        @click="submit"
+        :disabled="loading"
+        @click="nextStep"
         >确认提交</el-button
       >
-      <el-button v-else type="primary" @click="nextStep" :disabled="nextHolder"
+      <el-button v-else type="primary" @click="nextStep" :disabled="loading"
         >下一步</el-button
       >
       <el-button @click="cancel">取消</el-button>
     </div>
+    <div slot="footer"></div>
   </el-dialog>
 </template>
 
 <script>
-import { examRuleDetail } from "../../base/api";
-import { teacherCreateTaskApply, teacherCancelTaskApply } from "../api";
+import { examRuleDetail } from "../../../base/api";
+import { teacherSubmitTaskApply } from "../../api";
+import { printPlanTemplateList } from "../../../print/api";
+import InfoPrintPlan from "./InfoPrintPlan";
+import InfoExamTask from "./InfoExamTask";
+import InfoPrintTask from "./InfoPrintTask";
+import { COMMON_CARD_RULE_ID } from "@/constants/enumerate";
+import { deepCopy } from "@/plugins/utils";
 
 const STEPS_LIST = [
   {
@@ -61,11 +73,11 @@ const STEPS_LIST = [
     title: "命题信息"
   },
   {
-    name: "print",
+    name: "print-plan",
     title: "备卷印品信息"
   },
   {
-    name: "exam-business",
+    name: "print-task",
     title: "考务信息"
   }
 ];
@@ -75,7 +87,7 @@ const initExamTask = {
   courseCode: "",
   courseName: "",
   paperNumber: "",
-  cardRuleId: "",
+  cardRuleId: COMMON_CARD_RULE_ID,
   teachingRoomId: "",
   teacherName: "",
   lecturerName: ""
@@ -85,11 +97,11 @@ const initExamTaskDetail = {
   operateType: "STAGE",
   paperType: "A",
   cardId: "",
-  paperAttachmentIds: [],
-  paperConfirmAttachmentIds: [],
+  paperAttachmentIds: "[]",
+  paperConfirmAttachmentIds: "[]",
   drawCount: 2,
   remark: "",
-  makeMethod: "",
+  makeMethod: "SELECT",
   // 题卡状态
   status: "",
   // 考务规则
@@ -107,18 +119,19 @@ const initPrintPlan = {
   backupMethod: "ROOM",
   backupCount: 1,
   drawRule: "ONE",
+  templateSources: {},
   variableContent: [
     {
       type: "SIGN",
-      templateId: [],
-      oldTemplateId: [],
+      templateId: "",
+      oldTemplateId: "",
       backupMethod: "ROOM",
       backupCount: 1
     },
     {
       type: "PACKAGE",
-      templateId: [],
-      oldTemplateId: [],
+      templateId: "",
+      oldTemplateId: "",
       backupMethod: "ROOM",
       backupCount: 1
     }
@@ -126,8 +139,8 @@ const initPrintPlan = {
   ordinaryContent: [
     {
       type: "CHECK_IN",
-      templateId: [],
-      oldTemplateId: [],
+      templateId: "",
+      oldTemplateId: "",
       backupMethod: "ROOM",
       backupCount: 1
     }
@@ -135,8 +148,6 @@ const initPrintPlan = {
 };
 
 const initPrintTask = {
-  printPlanName: "",
-  printPlanId: "",
   examStartTime: "",
   examEndTime: "",
   paperNumber: "",
@@ -147,19 +158,19 @@ const initPrintTask = {
 
 export default {
   name: "create-task-apply",
+  components: { InfoPrintPlan, InfoExamTask, InfoPrintTask },
   data() {
     return {
       modalIsShow: false,
-      isSubmit: false,
       needReview: false,
       examRule: {},
       infos: {},
+      templateSources: {},
       // step
       steps: STEPS_LIST,
       current: 0,
-      nextHolder: false,
-      isPaying: false,
-      dataReady: true
+      loading: false,
+      dataReady: false
     };
   },
   computed: {
@@ -176,30 +187,54 @@ export default {
       return this.steps.length - 1;
     }
   },
-  created() {
-    this.getExamRule();
-  },
   methods: {
     async getExamRule() {
       const examRule = await examRuleDetail();
       this.examRule = examRule || {};
       this.needReview = examRule && examRule.review;
     },
-    initData() {
+    async getTemplates() {
+      const data = await printPlanTemplateList();
+      const templateSources = {};
+      const templates = [...data.variable, ...data.ordinary];
+      templates.forEach(item => {
+        templateSources[item.type] = item.template;
+      });
+      this.templateSources = templateSources;
+    },
+    async initData() {
+      await this.getExamRule();
+      await this.getTemplates();
+
       const examTaskDetail = Object.assign({}, initExamTaskDetail, {
         includePaper: this.examRule.includePaper,
         review: this.examRule.review,
         customCard: this.examRule.customCard
       });
 
+      // printPlan
+      let printPlan = deepCopy(initPrintPlan);
+      printPlan.variableContent = printPlan.variableContent.filter(
+        item => this.templateSources[item.type]
+      );
+      printPlan.ordinaryContent = printPlan.ordinaryContent.filter(
+        item => this.templateSources[item.type]
+      );
+      printPlan.templateSources = this.templateSources;
+
       this.infos = {
         examTask: { ...initExamTask },
         examTaskDetail,
-        printPlan: { ...initPrintPlan },
+        printPlan,
         printTask: { ...initPrintTask }
       };
+
+      this.dataReady = true;
     },
     visibleChange() {
+      this.dataReady = false;
+      this.current = 0;
+      this.loading = false;
       this.initData();
     },
     prevStep() {
@@ -207,25 +242,31 @@ export default {
       this.current -= 1;
     },
     nextStep() {
-      if (this.isLastStep) return;
-      this.$refs[this.currentComponent].checkForm();
+      this.loading = true;
+      this.$refs[this.currentComponent].checkData();
     },
-    toNext() {
-      if (this.isLastStep) return;
-      this.current += 1;
-      this.nextHolder = true;
+    toNext(data) {
+      console.log(data);
+      Object.entries(data).forEach(([key, val]) => {
+        this.infos[key] = Object.assign(this.infos[key], val);
+      });
+
+      if (this.isLastStep) {
+        this.submit();
+      } else {
+        this.current += 1;
+      }
     },
     compReady(type = false) {
-      this.nextHolder = type;
-    },
-    checkData() {
-      return this.$refs.examTaskComp.validate().catch(() => {});
+      this.loading = type;
     },
     async cancel() {
-      if (this.examTask.id) {
-        await teacherCancelTaskApply(this.examTask.id);
-        this.$message.success("取消命题申请成功!");
-      }
+      const result = await this.$confirm("确定取消该任务?", "提示", {
+        type: "warning"
+      }).catch(() => {});
+
+      if (result !== "confirm") return;
+
       this.close();
     },
     close() {
@@ -234,54 +275,31 @@ export default {
     open() {
       this.modalIsShow = true;
     },
-    async toSave(silent) {
-      const valid = await this.$refs.examTaskComp.validate().catch(() => {});
-      if (!valid) return;
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      this.getTaskDetailData();
-      const datas = {
-        examTaskDetail: this.examTaskDetail,
-        examTask: this.examTask
-      };
-      const data = await teacherCreateTaskApply(datas).catch(() => {});
-      this.isSubmit = false;
-      if (!data) return;
-
-      this.examTask = this.$objAssign(this.examTask, data);
-
-      if (!silent) this.$message.success("保存成功!");
-      return true;
-    },
     async submit() {
-      const valid = await this.$refs.examTaskComp.validate().catch(() => {});
-      if (!valid) return;
+      const result = await this.$confirm("确定提交该任务?", "提示", {
+        type: "warning"
+      }).catch(() => {});
 
-      if (!this.checkDataValid()) return;
+      if (result !== "confirm") {
+        this.loading = false;
+        return;
+      }
 
-      this.$confirm(
-        "任务确定提交后,则不可更改试卷及答题卡内容,确定提交该任务?",
-        "提示",
-        {
-          type: "warning"
+      const datas = {
+        examTaskContent: {
+          examTask: this.infos.examTask,
+          examTaskDetail: this.infos.examTaskDetail,
+          examPrintPlan: this.infos.printPlan,
+          examDetail: this.infos.printTask
         }
-      )
-        .then(async () => {
-          this.getTaskDetailData();
-          this.examTaskDetail.operateType = "SUBMIT";
-          const datas = {
-            examTaskDetail: this.examTaskDetail,
-            examTask: this.examTask
-          };
-          const data = await teacherCreateTaskApply(datas).catch(() => {});
+      };
 
-          if (!data) return;
-          this.$message.success("提交成功!");
-          this.close();
-          this.$emit("modified");
-        })
-        .catch(() => {});
+      const data = await teacherSubmitTaskApply(datas).catch(() => {});
+      this.loading = false;
+      if (!data) return;
+      this.$message.success("提交成功!");
+      this.close();
+      this.$emit("modified");
     }
   }
 };

+ 0 - 15
src/modules/exam/components/createTaskApply/InfoExamBusiness.vue

@@ -1,15 +0,0 @@
-<template>
-  <div class="info-exam-business">
-    info-exam-business
-  </div>
-</template>
-
-<script>
-export default {
-  name: "info-exam-business",
-  data() {
-    return {};
-  },
-  methods: {}
-};
-</script>

+ 114 - 238
src/modules/exam/components/createTaskApply/InfoExamTask.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="info-exam-task">
-    <div class="part-box part-box-pad part-box-border">
+    <div class="part-box part-box-pad part-box-border part-box-gray">
       <el-form
         ref="examTaskComp"
         :model="examTask"
@@ -18,27 +18,27 @@
           </el-col>
           <el-col :span="12">
             <el-form-item prop="courseCode" label="课程(代码):">
-              <course-select
+              <el-select
                 v-model="examTask.courseCode"
-                :teaching-room-id="examTask.teachingRoomId"
+                placeholder="请选择"
+                filterable
                 @change="courseChange"
-              ></course-select>
+              >
+                <el-option
+                  v-for="item in courses"
+                  :key="item.code"
+                  :value="item.code"
+                  :label="`${item.name}(${item.code})`"
+                >
+                </el-option>
+              </el-select>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-col :span="12">
             <el-form-item prop="cardRuleId" label="题卡规则:">
-              <card-rule-select
-                ref="CardRuleSelect"
-                v-model.trim="examTask.cardRuleId"
-                placeholder="请选择"
-                clearable
-                @change="clearCard"
-              ></card-rule-select>
-              <p class="tips-info">
-                说明:若选择全部通卡,则命题老师只能选择通卡,若选择题卡规则,则专卡和通卡均可选择
-              </p>
+              <el-input v-model.trim="cardRuleName" readonly></el-input>
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -77,32 +77,6 @@
       </el-form>
     </div>
 
-    <div class="task-apply-steps" v-if="flows.length">
-      <el-steps align-center>
-        <el-step
-          v-for="flow in flows"
-          :key="flow.taskKey"
-          :title="flow.taskName"
-          :status="flow.status"
-        >
-          <div slot="description">
-            <div class="step-info" v-if="flow.firstUser">
-              <span>{{ flow.firstUser }}</span>
-              <el-popover
-                v-if="flow.moreUser"
-                placement="top-start"
-                width="300"
-                trigger="hover"
-                :content="flow.moreUser"
-              >
-                <span class="color-primary ml-1" slot="reference">更多</span>
-              </el-popover>
-            </div>
-          </div>
-        </el-step>
-      </el-steps>
-    </div>
-
     <div class="apply-content task-detail">
       <div class="task-body">
         <div class="mb-2 text-right">
@@ -146,19 +120,15 @@
               {{ createCardTypeName }}
             </td>
             <td :rowspan="paperAttachments.length" v-if="index === 0">
-              <el-button
-                type="text"
-                class="btn-primary"
-                @click="toCreateOrViewCard"
-                >{{ cardTodoName }}</el-button
-              >
-              <el-button
-                v-if="examTaskDetail.makeMethod"
-                size="mini"
-                type="primary"
-                @click="changeCreateCardType"
-                >切换题卡创建方式</el-button
-              >
+              <el-select v-model="examTaskDetail.cardId" placeholder="请选择">
+                <el-option
+                  v-for="item in cards"
+                  :key="item.id"
+                  :value="item.id"
+                  :label="item.title"
+                >
+                </el-option>
+              </el-select>
             </td>
             <td>
               <el-button
@@ -245,13 +215,6 @@
       @confirm="uploadConfirm"
       ref="UploadPaperDialog"
     ></upload-paper-dialog>
-    <!-- card-option-dialog -->
-    <card-option-dialog
-      ref="CardOptionDialog"
-      :data="task"
-      @upload-sample-over="initData"
-      @confirm="cardConfirm"
-    ></card-option-dialog>
     <!-- image-preview -->
     <simple-image-preview
       :cur-image="curImage"
@@ -264,14 +227,14 @@
 
 <script>
 import UploadPaperDialog from "../UploadPaperDialog";
-import CardOptionDialog from "../CardOptionDialog";
 import SimpleImagePreview from "@/components/SimpleImagePreview";
 import { CARD_SOURCE_TYPE } from "@/constants/enumerate";
-import { switchCardCreateMethod, taskAllApproverPeople } from "../../api";
+import { cardForSelectList } from "../../api";
+import { courseQuery } from "../../../base/api";
 
 export default {
   name: "info-exam-task",
-  components: { UploadPaperDialog, CardOptionDialog, SimpleImagePreview },
+  components: { UploadPaperDialog, SimpleImagePreview },
   props: {
     datas: {
       type: Object,
@@ -283,32 +246,6 @@ export default {
   data() {
     return {
       task: {},
-      flows: [
-        {
-          flowId: "160684605807001600",
-          taskName: "提交试卷",
-          taskKey: "usertask1",
-          setup: 1,
-          status: "process",
-          desc: "进行中"
-        },
-        {
-          flowId: "160684605807001600",
-          taskName: "审核试卷",
-          taskKey: "usertask2",
-          setup: 2,
-          status: "wait",
-          desc: "待进行"
-        },
-        {
-          flowId: "160684605807001600",
-          taskName: "审核试卷",
-          taskKey: "usertask3",
-          setup: 3,
-          status: "wait",
-          desc: "待进行"
-        }
-      ],
       rules: {
         teachingRoomId: [
           {
@@ -332,8 +269,12 @@ export default {
           }
         ]
       },
+      examTask: {},
+      cards: [],
+      courses: [],
+      cardRuleName: "全部通卡",
       // exam-task-detail
-      examTaskDetail: {},
+      examTaskDetail: { makeMethod: "" },
       paperConfirmAttachmentId: { attachmentId: "", filename: "", url: "" },
       paperAttachments: [],
       paperConfirmAttachments: [],
@@ -347,21 +288,6 @@ export default {
     };
   },
   computed: {
-    cardTodoName() {
-      let name = "创建答题卡";
-      if (this.examTaskDetail.cardId) {
-        if (this.examTaskDetail.makeMethod === "SELECT") {
-          name = "选择题卡";
-        } else if (this.examTaskDetail.makeMethod === "SELF") {
-          name = "编辑题卡";
-        } else {
-          // 已经审核的题卡可以自行编辑,未审核的题卡只能查看
-          name =
-            this.examTaskDetail.status === "SUBMIT" ? "编辑题卡" : "查看题卡";
-        }
-      }
-      return name;
-    },
     createCardTypeName() {
       return CARD_SOURCE_TYPE[this.examTaskDetail.makeMethod] || "";
     },
@@ -375,7 +301,76 @@ export default {
     this.initData();
   },
   methods: {
-    initData() {},
+    initData() {
+      console.log(this.datas.examTask);
+      this.examTask = { ...this.datas.examTask };
+      this.examTaskDetail = { ...this.datas.examTaskDetail };
+      this.paperAttachments = this.examTaskDetail.paperAttachmentIds
+        ? JSON.parse(this.examTaskDetail.paperAttachmentIds)
+        : [];
+
+      if (!this.paperAttachments.length) {
+        this.addAtachment();
+      }
+
+      this.paperConfirmAttachments = this.examTaskDetail
+        .paperConfirmAttachmentIds
+        ? JSON.parse(this.examTaskDetail.paperConfirmAttachmentIds)
+        : [];
+
+      this.getCourses();
+      this.getCardList();
+
+      this.$emit("on-ready");
+    },
+    async getCardList() {
+      if (!this.examTask.courseCode) return;
+      const data = await cardForSelectList({
+        courseCode: this.examTask.courseCode,
+        paperType: this.paperAttachments.map(item => item.name).join(","),
+        cardRuleId: this.examTask.cardRuleId
+      });
+      this.cards = data || [];
+    },
+    async getCourses() {
+      if (!this.examTask.teachingRoomId) return;
+      const res = await courseQuery({
+        teachingRoomId: this.examTask.teachingRoomId
+      });
+      this.courses = res || [];
+    },
+    async checkData() {
+      const valid = await this.$refs.examTaskComp.validate().catch(() => {});
+      if (!valid) {
+        this.$emit("on-ready");
+        return;
+      }
+
+      const attachmentValid = !this.paperAttachments.some(
+        item => !item.attachmentId
+      );
+      // 设置了入库强制包含试卷时,校验试卷是否上传。
+      if (this.examTaskDetail.includePaper && !attachmentValid) {
+        this.$message.error("请完成试卷文件上传!");
+        this.$emit("on-ready");
+        return;
+      }
+      // if (!this.paperConfirmAttachments.length) {
+      //   this.$message.error("请上传入库审核表!");
+      //   return;
+      // }
+
+      if (!this.examTaskDetail.cardId) {
+        this.$message.error("请选择题卡!");
+        this.$emit("on-ready");
+        return;
+      }
+
+      this.$emit("next-step", {
+        examTask: this.examTask,
+        examTaskDetail: this.getTaskDetailData()
+      });
+    },
     // exam-task-detail edit
     addAtachment() {
       if (this.paperAttachments.length >= this.attachmentLimitCount) return;
@@ -443,119 +438,28 @@ export default {
         })
       );
     },
-    toEditCard() {
-      this.cachePrepareTcpCard();
-      this.$router.push({
-        name: "CardDesign",
-        params: {
-          cardId: this.examTaskDetail.cardId
-        }
-      });
-    },
-    cachePrepareTcpCard() {
-      this.$ls.set("prepareTcPCard", {
-        examTaskId: this.examTask.id,
-        courseCode: this.examTask.courseCode,
-        courseName: this.examTask.courseName,
-        makeMethod: this.examTaskDetail.makeMethod,
-        cardRuleId: this.examTask.cardRuleId
-      });
-    },
-    async toCreateOrViewCard() {
-      const result = await this.toSave(1);
-      if (!result) return;
-      this.task = {
-        ...this.examTask,
-        ...this.examTaskDetail,
-        examTaskId: this.examTask.id
-      };
-      if (!this.examTaskDetail.cardId) {
-        this.$refs.CardOptionDialog.open();
-        return;
-      }
-
-      if (this.examTaskDetail.makeMethod === "SELECT") {
-        this.$refs.CardOptionDialog.open();
-      } else if (this.examTaskDetail.makeMethod === "SELF") {
-        this.toEditCard();
-      } else {
-        // 客服制卡:制作完毕则可以编辑,未制作完毕则可以查看
-        if (this.examTaskDetail.status === "SUBMIT") {
-          this.toEditCard();
-        } else {
-          this.toViewCard();
-        }
-      }
-    },
     cardConfirm(data) {
       this.examTaskDetail = this.$objAssign(this.examTaskDetail, data);
     },
-    async changeCreateCardType() {
-      const h = this.$createElement;
-      const result = await this.$msgbox({
-        title: "切换题卡操作说明",
-        message: h("div", null, [
-          h("p", null, "1、切换题卡会将之前选择题卡数据删除。"),
-          h(
-            "p",
-            null,
-            "2、之前选择专卡进行绘制,切换题卡后再次选择专卡,需要重新开始绘制。"
-          )
-        ]),
-        showCancelButton: true,
-
-        type: "warning"
-      }).catch(() => {});
-      if (result !== "confirm") return;
-
-      await this.clearMakeMethod();
-      this.toCreateOrViewCard();
-    },
-    async clearMakeMethod() {
-      // 清除后台记录的题卡
-      if (this.examTask.id && this.examTaskDetail.cardId)
-        await switchCardCreateMethod(this.examTask.id);
-
-      this.examTaskDetail.makeMethod = "";
-      this.examTaskDetail.cardId = "";
-    },
     teachingRoomChange() {
       this.examTask.courseCode = "";
       this.examTask.courseName = "";
-      this.clearCard();
+      this.courses = [];
+      this.examTaskDetail.cardId = "";
+      this.cards = [];
+      this.getCourses();
     },
     courseChange(val) {
-      if (!val || !val.code) {
-        this.examTask.courseCode = "";
+      if (val) {
+        const course = this.courses.find(item => item.code === val);
+        this.examTask.courseName = course.name;
+      } else {
         this.examTask.courseName = "";
-        return;
       }
-      this.examTask.courseName = val.name;
-      this.clearCard();
-      this.updateApproverPeople();
-    },
-    clearCard() {
-      this.clearMakeMethod();
-    },
-    async updateApproverPeople() {
-      const data = await taskAllApproverPeople({
-        courseCode: this.examTask.courseCode
-      });
-      let approvePeople = {};
-      data.approveUserList.forEach(item => {
-        item.users = item.approveUser.map(
-          user => `${user.realName}(${user.orgName})`
-        );
-        item.firstUser = item.users[0];
-        item.moreUser = item.users.length > 1 ? item.users.join(",") : null;
-        approvePeople[item.setup] = item;
-      });
-      this.flows = this.flows.map(flow => {
-        return {
-          ...flow,
-          ...approvePeople[flow.setup]
-        };
-      });
+
+      this.examTaskDetail.cardId = "";
+      this.cards = [];
+      this.getCardList();
     },
     getTaskDetailData() {
       let data = { ...this.examTaskDetail };
@@ -566,35 +470,7 @@ export default {
       data.paperConfirmAttachmentIds = JSON.stringify(
         this.paperConfirmAttachments
       );
-      this.examTaskDetail = data;
-    },
-    checkDataValid() {
-      const attachmentValid = !this.paperAttachments.some(
-        item => !item.attachmentId
-      );
-      // 设置了入库强制包含试卷时,校验试卷是否上传。
-      if (this.examTaskDetail.includePaper && !attachmentValid) {
-        this.$message.error("请完成试卷文件上传!");
-        return;
-      }
-      // if (!this.paperConfirmAttachments.length) {
-      //   this.$message.error("请上传入库审核表!");
-      //   return;
-      // }
-
-      if (!this.examTaskDetail.cardId) {
-        this.$message.error("请选择题卡创建方式!");
-        return;
-      }
-
-      if (
-        this.examTaskDetail.makeMethod !== "SELECT" &&
-        this.examTaskDetail.status !== "SUBMIT"
-      ) {
-        this.$message.error("请先提交题卡!");
-        return;
-      }
-      return true;
+      return data;
     },
     // image-preview
     toPreview(index) {

+ 0 - 15
src/modules/exam/components/createTaskApply/InfoPrint.vue

@@ -1,15 +0,0 @@
-<template>
-  <div class="info-print">
-    info-print
-  </div>
-</template>
-
-<script>
-export default {
-  name: "info-print",
-  data() {
-    return {};
-  },
-  methods: {}
-};
-</script>

+ 443 - 0
src/modules/exam/components/createTaskApply/InfoPrintPlan.vue

@@ -0,0 +1,443 @@
+<template>
+  <div class="info-print-plan">
+    <el-form
+      ref="modalFormComp"
+      label-width="120px"
+      :rules="rules"
+      :model="modalForm"
+      label-position="left"
+    >
+      <div class="part-box">
+        <h4 class="part-box-tips">
+          试卷&题卡印品:
+          <el-checkbox
+            v-model="allSelected"
+            label="全选"
+            @change="selectAll"
+          ></el-checkbox>
+        </h4>
+        <el-form-item prop="printContent" label="试卷、题卡:">
+          <el-checkbox-group
+            v-model="modalForm.printContent"
+            @change="() => checkSelectAll()"
+          >
+            <el-checkbox
+              v-for="(val, key) in PRINT_CONTENT_TYPE"
+              :key="key"
+              :label="key"
+              >{{ val }}</el-checkbox
+            >
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item
+          v-if="modalForm.printContent.length"
+          prop="backupMethod"
+          label="试卷备份方式:"
+        >
+          <el-select
+            v-model="modalForm.backupMethod"
+            class="mr-2"
+            size="small"
+            placeholder="请选择"
+          >
+            <el-option
+              v-for="(val, key) in PAPER_BACKUP_TYPE"
+              :key="key"
+              :value="key"
+              :label="val"
+            ></el-option>
+          </el-select>
+          <el-input-number
+            class="mr-1"
+            v-model="modalForm.backupCount"
+            size="small"
+            :min="1"
+            :max="200"
+            :step="1"
+            step-strictly
+            :controls="false"
+            style="width: 60px"
+          ></el-input-number>
+          <span>份</span>
+        </el-form-item>
+        <el-form-item
+          v-if="contentIncludesPaper"
+          prop="drawRule"
+          label="抽卷规则:"
+        >
+          <el-radio-group v-model="modalForm.drawRule">
+            <el-radio
+              v-for="(val, key) in DRAW_RULE_TYPE"
+              :label="key"
+              :key="key"
+              >{{ val }}</el-radio
+            >
+          </el-radio-group>
+          <p class="tips-info">
+            1.只抽取一次:不同印刷计划下,同一试卷编号下的卷型只能被抽取一次;
+          </p>
+          <p class="tips-info">
+            2.可反复抽取:不同印刷计划下,同一试卷编号下的卷型可重复抽取,系统默认优先抽取未曝光卷型。
+          </p>
+        </el-form-item>
+      </div>
+
+      <div class="part-box">
+        <h4 class="part-box-tips">变量印品:</h4>
+        <el-form-item
+          v-for="(item, index) in modalForm.variableContent"
+          :key="item.type"
+          :label="`${TEMPLATE_CLASSIFY[item.type]}:`"
+          :prop="`variableContent.${index}.templateId`"
+          :rules="{
+            required: false,
+            validator: templateValidator,
+            trigger: 'change'
+          }"
+          :required="!!item.templateId.length"
+        >
+          <el-checkbox-group
+            v-model="item.templateId"
+            @change="vals => tempChange(vals, `variableContent.${index}`)"
+          >
+            <el-checkbox
+              v-for="temp in templateSources[item.type]"
+              :label="temp.id"
+              :key="temp.id"
+              >{{ temp.name }}</el-checkbox
+            >
+          </el-checkbox-group>
+          <div v-if="item.templateId.length">
+            <el-select
+              v-model="item.backupMethod"
+              class="mr-2"
+              size="small"
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="(val, key) in PRINT_BACKUP_TYPE"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+            <el-input-number
+              v-model="item.backupCount"
+              class="mr-1"
+              size="small"
+              :min="1"
+              :max="200"
+              :step="1"
+              step-strictly
+              :controls="false"
+              style="width: 60px"
+            ></el-input-number>
+            <span>份</span>
+          </div>
+        </el-form-item>
+      </div>
+
+      <div class="part-box">
+        <h4 class="part-box-tips">普通印品:</h4>
+        <el-form-item
+          v-for="(item, index) in modalForm.ordinaryContent"
+          :key="item.type"
+          :label="`${TEMPLATE_CLASSIFY[item.type]}:`"
+          label-width="130px"
+          :prop="`ordinaryContent.${index}.templateId`"
+          :rules="{
+            required: false,
+            validator: templateValidator,
+            trigger: 'change'
+          }"
+          :required="!!item.templateId.length"
+        >
+          <el-checkbox-group
+            v-model="item.templateId"
+            @change="vals => tempChange(vals, `ordinaryContent.${index}`)"
+          >
+            <el-checkbox
+              v-for="temp in templateSources[item.type]"
+              :label="temp.id"
+              :key="temp.id"
+              >{{ temp.name }}</el-checkbox
+            >
+          </el-checkbox-group>
+          <div v-if="item.templateId.length">
+            <el-select
+              v-model="item.backupMethod"
+              class="mr-2"
+              size="small"
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="(val, key) in PRINT_BACKUP_TYPE"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+            <el-input-number
+              v-model="item.backupCount"
+              class="mr-1"
+              size="small"
+              :min="1"
+              :max="200"
+              :step="1"
+              step-strictly
+              :controls="false"
+              style="width: 60px"
+            ></el-input-number>
+            <span>份</span>
+          </div>
+        </el-form-item>
+      </div>
+
+      <el-form-item prop="selectedPrint"></el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import {
+  PRINT_CONTENT_TYPE,
+  DRAW_RULE_TYPE,
+  PRINT_BACKUP_TYPE,
+  PAPER_BACKUP_TYPE,
+  TEMPLATE_CLASSIFY
+} from "@/constants/enumerate";
+import { deepCopy } from "@/plugins/utils";
+
+export default {
+  name: "info-print-plan",
+  props: {
+    datas: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    const backupMethodValidator = (rule, value, callback) => {
+      if (!this.modalForm.printContent.length) {
+        return callback();
+      }
+      if (!value) {
+        return callback(new Error("请选择备份方式"));
+      }
+      if (!this.modalForm.backupCount) {
+        return callback(new Error("请输入备份数量"));
+      }
+
+      callback();
+    };
+    const selectedPrintValidator = (rule, value, callback) => {
+      const printInfo = [
+        ...this.modalForm.variableContent,
+        ...this.modalForm.ordinaryContent
+      ];
+      const hasPrintInfo = printInfo.some(item => item.templateId.length);
+
+      if (hasPrintInfo || this.modalForm.printContent.length) {
+        callback();
+      } else {
+        callback(new Error("必须选择一项印品"));
+      }
+    };
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: {},
+      createTime: [],
+      PRINT_CONTENT_TYPE,
+      DRAW_RULE_TYPE,
+      PRINT_BACKUP_TYPE,
+      PAPER_BACKUP_TYPE,
+      TEMPLATE_CLASSIFY,
+      variableContent: [],
+      ordinaryContent: [],
+      templateSources: {},
+      oldPrintContent: [],
+      allSelected: false,
+      rules: {
+        printContent: [
+          {
+            required: false,
+            // validator: printContentValidator,
+            trigger: "change"
+          }
+        ],
+        backupMethod: [
+          {
+            required: true,
+            validator: backupMethodValidator,
+            trigger: "change"
+          }
+        ],
+        drawRule: [
+          {
+            required: true,
+            message: "请选择抽卷规则",
+            trigger: "change"
+          }
+        ],
+        selectedPrint: [
+          {
+            required: false,
+            validator: selectedPrintValidator,
+            trigger: "change"
+          }
+        ]
+      }
+    };
+  },
+  computed: {
+    contentIncludesPaper() {
+      return this.modalForm.printContent.includes("PAPER");
+    }
+  },
+  created() {
+    this.initData();
+  },
+  methods: {
+    async initData() {
+      const val = this.datas.printPlan;
+      this.modalForm = deepCopy(val);
+      this.templateSources = val.templateSources;
+      const transformInfo = item => {
+        const templateIds = item.templateId ? [item.templateId] : [];
+        return {
+          type: item.type,
+          templateId: templateIds,
+          oldTemplateId: templateIds,
+          backupMethod: item.backupMethod,
+          backupCount: item.backupCount
+        };
+      };
+      this.modalForm.variableContent = val.variableContent.map(transformInfo);
+      this.modalForm.ordinaryContent = val.ordinaryContent.map(transformInfo);
+
+      if (!this.checkHasSelect()) {
+        this.allSelected = true;
+        this.selectAll(this.allSelected);
+      } else {
+        this.checkSelectAll();
+      }
+      this.$emit("on-ready");
+    },
+    async checkData() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) {
+        this.$emit("on-ready");
+        return;
+      }
+
+      const printPlan = { ...this.modalForm };
+      const transformInfo = item => {
+        const templateId = item.templateId.join();
+        const template = this.templateSources[item.type].find(
+          temp => temp.id === templateId
+        );
+        return {
+          type: item.type,
+          templateId,
+          attachmentId: template && template.attachmentId,
+          backupMethod: item.backupMethod,
+          backupCount: item.backupCount
+        };
+      };
+      printPlan.variableContent = this.modalForm.variableContent.map(
+        transformInfo
+      );
+      printPlan.ordinaryContent = this.modalForm.ordinaryContent.map(
+        transformInfo
+      );
+
+      this.$emit("next-step", {
+        printPlan
+      });
+    },
+    selectAll(selected) {
+      if (selected) {
+        this.modalForm.printContent = ["PAPER", "CARD"];
+        this.modalForm.variableContent.forEach(item => {
+          if (item.templateId && item.templateId.length) return;
+
+          const source = this.templateSources[item.type][0];
+          item.templateId = source && [source.id];
+          item.oldTemplateId = source && [source.id];
+        });
+        this.modalForm.ordinaryContent.forEach(item => {
+          if (item.templateId && item.templateId.length) return;
+
+          const source = this.templateSources[item.type][0];
+          item.templateId = source && [source.id];
+          item.oldTemplateId = source && [source.id];
+        });
+      } else {
+        this.modalForm.printContent = [];
+        this.modalForm.variableContent.forEach(item => {
+          item.templateId = [];
+          item.oldTemplateId = [];
+        });
+        this.modalForm.ordinaryContent.forEach(item => {
+          item.templateId = [];
+          item.oldTemplateId = [];
+        });
+      }
+    },
+    checkSelectAll() {
+      const vNotSelected = this.modalForm.variableContent.some(
+        item => !item.templateId.length
+      );
+      const oNotSelected = this.modalForm.ordinaryContent.some(
+        item => !item.templateId.length
+      );
+      const pNotSelected = this.modalForm.printContent.length < 2;
+
+      const selecteds = [vNotSelected, oNotSelected, pNotSelected];
+
+      this.allSelected = !selecteds.some(item => item);
+    },
+    checkHasSelect() {
+      const vSelected = this.modalForm.variableContent.some(
+        item => item.templateId.length
+      );
+      const oSelected = this.modalForm.ordinaryContent.some(
+        item => item.templateId.length
+      );
+      const pSelected = !!this.modalForm.printContent.length;
+
+      const selecteds = [vSelected, oSelected, pSelected];
+
+      return selecteds.some(item => item);
+    },
+    templateValidator(rule, value, callback) {
+      const [field, index] = rule.field.split(".");
+      const val = this.modalForm[field][index];
+      if (val.templateId.length) {
+        if (!val.backupMethod) {
+          return callback(new Error("请选择备份方式"));
+        }
+        if (!val.backupCount) {
+          return callback(new Error("请输入备份数量"));
+        }
+        callback();
+      } else {
+        callback();
+      }
+    },
+    tempChange(vals, name) {
+      const [field, index] = name.split(".");
+      const info = this.modalForm[field][index];
+      const newVals = vals.filter(item => !info.oldTemplateId.includes(item));
+      info.templateId = newVals;
+      info.oldTemplateId = newVals;
+
+      this.checkSelectAll();
+      this.$refs.modalFormComp.validateField("selectedPrint");
+    }
+  }
+};
+</script>

+ 380 - 0
src/modules/exam/components/createTaskApply/InfoPrintTask.vue

@@ -0,0 +1,380 @@
+<template>
+  <div class="info-print-task">
+    <el-form ref="modalFormComp" label-position="top">
+      <el-form-item label="考试时间:" required>
+        <el-date-picker
+          v-model="createDate"
+          type="date"
+          value-format="timestamp"
+          placeholder="考试日期"
+          style="width: 150px"
+          @change="timeChange"
+        >
+        </el-date-picker>
+        <el-time-picker
+          is-range
+          v-model="createTime"
+          range-separator="至"
+          start-placeholder="考试开始时间"
+          end-placeholder="考试结束时间"
+          placeholder="选择时间范围"
+          value-format="timestamp"
+          @change="timeChange"
+        >
+        </el-time-picker>
+      </el-form-item>
+      <el-form-item label="考试对象:" required></el-form-item>
+    </el-form>
+
+    <div class="part-box">
+      <div class="box-justify mb-2">
+        <p>
+          共{{ packageInfos.packageCount }}个卷袋,{{
+            packageInfos.studentCount
+          }}个考生,共印试卷{{ packageInfos.paperCount }}份(正式{{
+            packageInfos.paperReleaseCount
+          }}份,备用{{ packageInfos.paperBackupCount }}份)
+        </p>
+        <el-button type="primary" @click="toAdd" :disabled="cannotAdd"
+          >新增考试对象</el-button
+        >
+      </div>
+      <el-table ref="TableList" :data="tableData" border>
+        <el-table-column type="index" width="50" label="卷袋序号">
+        </el-table-column>
+        <el-table-column prop="className" label="考试对象"> </el-table-column>
+        <el-table-column prop="studentCount" label="人数" width="60">
+        </el-table-column>
+        <el-table-column prop="printHouseName" label="印刷室">
+          <template slot-scope="scope">
+            <el-select
+              v-model="scope.row.printHouseId"
+              placeholder="请选择"
+              @change="() => printHouseChange(scope.row)"
+            >
+              <el-option
+                v-for="room in printHouses"
+                :key="room.houseId"
+                :value="room.houseId"
+                :label="room.houseName"
+              ></el-option>
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column
+          v-for="item in extendFields"
+          :key="item.code"
+          :label="item.name"
+        >
+          <div slot-scope="scope">
+            <el-input
+              v-model="scope.row.extends[item.code]"
+              placeholder="请输入"
+              clearable
+            ></el-input>
+          </div>
+        </el-table-column>
+        <el-table-column label="操作" width="100">
+          <template slot-scope="scope">
+            <el-button
+              class="btn-danger"
+              type="text"
+              @click="toDelete(scope.row)"
+              >删除</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- ModifyPrintTask -->
+    <modify-print-task
+      ref="ModifyPrintTask"
+      :instance="curRow"
+      :print-houses="printHouses"
+      :class-list="unusedClassList"
+      :extend-fields="extendFields"
+      @modified="modified"
+    ></modify-print-task>
+  </div>
+</template>
+
+<script>
+import { calcSum, getTimeDatestamp } from "@/plugins/utils";
+import { examRuleDetail } from "../../../base/api";
+import { listTaskPrintHouse, listTaskApplyClass } from "../../api";
+import ModifyPrintTask from "./ModifyPrintTask";
+
+export default {
+  name: "info-print-task",
+  components: { ModifyPrintTask },
+  props: {
+    datas: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    return {
+      modalForm: {
+        examStartTime: "",
+        examEndTime: "",
+        paperNumber: "",
+        courseName: "",
+        courseCode: ""
+      },
+      tableData: [],
+      curRow: {},
+      classList: [],
+      validClassList: [],
+      unusedClassList: [],
+      printHouses: [],
+      extendFields: [],
+      packageInfos: {
+        packageCount: 0,
+        studentCount: 0,
+        paperCount: 0,
+        paperReleaseCount: 0,
+        paperBackupCount: 0
+      },
+      // date-picker
+      curCreateTime: [],
+      createDate: "",
+      createTime: []
+    };
+  },
+  computed: {
+    cannotAdd() {
+      return !this.unusedClassList.length;
+    }
+  },
+  mounted() {
+    console.log("mounted");
+    this.initData();
+  },
+  methods: {
+    async initData() {
+      await this.getExtendFields();
+      await this.getPrintHouses();
+      await this.getClassList();
+
+      const curDate = getTimeDatestamp(Date.now());
+      const hour = 60 * 60 * 1000;
+      this.curCreateTime = [curDate + 8 * hour, curDate + 10 * hour];
+      this.createTime = [...this.curCreateTime];
+
+      const data = this.datas.printTask;
+      this.tableData = data.list;
+      if (data.examStartTime && data.examEndTime) {
+        this.createTime = [data.examStartTime, data.examEndTime];
+        this.createDate = getTimeDatestamp(data.examStartTime);
+        this.modalForm.examStartTime = this.createTime[0];
+        this.modalForm.examEndTime = this.createTime[1];
+      }
+
+      this.modalForm = Object.assign(this.modalForm, {
+        paperNumber: this.datas.examTask.paperNumber,
+        courseName: this.datas.examTask.courseName,
+        courseCode: this.datas.examTask.courseCode
+      });
+
+      if (this.tableData.length) {
+        this.updateUnusedClassList();
+      } else {
+        this.buildTableData();
+      }
+      this.updatePackageInfos();
+
+      this.$emit("on-ready");
+    },
+    checkData() {
+      if (!this.modalForm.examStartTime || !this.modalForm.examEndTime) {
+        this.$message.error("请选择考试时间!");
+        this.$emit("on-ready");
+        return;
+      }
+
+      if (!this.tableData.length) {
+        this.$message.error("请添加考试对象!");
+        this.$emit("on-ready");
+        return;
+      }
+
+      let errorMsg = [];
+      this.tableData.forEach(row => {
+        let errorFields = [];
+
+        let extendFields = this.extendFields.map(field => {
+          let info = { ...field };
+          info.value = row.extends[field.code];
+          if (!info.value) {
+            errorFields.push(field.name);
+          }
+          return info;
+        });
+
+        row.extendFields = JSON.stringify(extendFields);
+        row.examStartTime = this.modalForm.examStartTime;
+        row.examEndTime = this.modalForm.examEndTime;
+
+        if (errorFields.length) {
+          errorMsg.push(
+            `考试对象${row.className}中,${errorFields.join("、")}必须填写`
+          );
+        }
+      });
+
+      if (errorMsg.length) {
+        this.$message.error(errorMsg.join("。"));
+        this.$emit("on-ready");
+        return;
+      }
+
+      this.$emit("next-step", {
+        printTask: {
+          ...this.modalForm,
+          list: this.tableData
+        }
+      });
+    },
+    buildTableData() {
+      this.tableData = this.classList.map(clazz => {
+        const printRoom = this.printHouses[0] || {};
+        let data = {
+          examPlace: "",
+          examRoom: "",
+          classId: clazz.id,
+          className: clazz.name,
+          studentCount: clazz.studentCount,
+          printHouseId: printRoom["houseId"],
+          printHouseName: printRoom["houseName"],
+          extendFields: "",
+          ...this.modalForm
+        };
+        let extendFieldModal = {};
+        this.extendFields.forEach(field => {
+          extendFieldModal[field.code] = "";
+        });
+        data.extends = extendFieldModal;
+        return data;
+      });
+
+      console.log(this.tableData);
+    },
+    updateUnusedClassList() {
+      let usedClassIds = [];
+      this.tableData.forEach(row => {
+        usedClassIds = [...usedClassIds, ...row.classId.split(",")];
+      });
+
+      this.unusedClassList = this.classList.filter(
+        item => !usedClassIds.includes(item.id)
+      );
+    },
+    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 =
+        this.packageInfos.packageCount * this.datas.printPlan.backupCount;
+      this.packageInfos.paperCount =
+        this.packageInfos.paperReleaseCount +
+        this.packageInfos.paperBackupCount;
+    },
+    async getExtendFields() {
+      const examRule = await examRuleDetail();
+      this.extendFields = examRule.extendFields
+        ? JSON.parse(examRule.extendFields)
+        : [];
+    },
+    async getPrintHouses() {
+      this.printHouses = await listTaskPrintHouse();
+    },
+    async getClassList() {
+      this.classList = [];
+      const data = await listTaskApplyClass({
+        courseCode: this.modalForm.courseCode
+      });
+      if (!data) return;
+
+      this.classList = data.map(item => {
+        return {
+          id: item.classId,
+          name: item.className,
+          studentCount: item.studentCount
+        };
+      });
+    },
+    timeChange() {
+      if (!this.createDate || !this.createTime) {
+        this.modalForm.examStartTime = null;
+        this.modalForm.examEndTime = null;
+        return;
+      }
+
+      const curDate = getTimeDatestamp(this.createDate);
+      const timeDate = getTimeDatestamp(this.createTime[0]);
+
+      this.modalForm.examStartTime = curDate + this.createTime[0] - timeDate;
+      this.modalForm.examEndTime = curDate + this.createTime[1] - timeDate;
+    },
+    printHouseChange(row) {
+      const curHouse = this.printHouses.find(
+        item => item.houseId === row.printHouseId
+      );
+      if (curHouse) {
+        row.printHouseName = curHouse.houseName;
+      }
+    },
+    toAdd() {
+      if (!this.modalForm.examStartTime || !this.modalForm.examEndTime) {
+        this.$message.error("请选择考试时间!");
+        return;
+      }
+
+      if (this.modalForm.examStartTime === this.modalForm.examEndTime) {
+        this.$message.error("考试开始时间不能等于考试结束时间!");
+        return;
+      }
+
+      this.curRow = {
+        examPlace: "",
+        examRoom: "",
+        classId: "",
+        className: "",
+        studentCount: null,
+        printHouseId: null,
+        printHouseName: null,
+        extendFields: "",
+        ...this.modalForm
+      };
+      let extendFieldModal = {};
+      this.extendFields.forEach(field => {
+        extendFieldModal[field.code] = "";
+      });
+      this.curRow.extends = extendFieldModal;
+      this.$refs.ModifyPrintTask.open();
+    },
+    toEdit(row) {
+      this.curRow = { ...row };
+      this.$refs.ModifyPrintTask.open();
+    },
+    toDelete(row) {
+      this.tableData = this.tableData.filter(
+        item => item.classId !== row.classId
+      );
+      this.updateUnusedClassList();
+      this.updatePackageInfos();
+    },
+    modified(data) {
+      this.tableData.push(data);
+      this.updateUnusedClassList();
+      this.updatePackageInfos();
+    }
+  }
+};
+</script>

+ 225 - 0
src/modules/exam/components/createTaskApply/ModifyPrintTask.vue

@@ -0,0 +1,225 @@
+<template>
+  <el-dialog
+    class="modify-print-task"
+    :visible.sync="modalIsShow"
+    title="新增考试对象"
+    top="10px"
+    width="600px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @opened="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      label-width="100px"
+      :rules="rules"
+      :model="modalForm"
+    >
+      <el-form-item label="考试时间:"
+        >{{ instance.examStartTime | timestampFilter }} 至
+        {{ instance.examEndTime | timestampFilter }}
+      </el-form-item>
+      <el-form-item prop="classId" label="考试对象:">
+        <clazz-select
+          v-model="classIds"
+          placeholder="请选择"
+          multiple
+          clearable
+          :datas="classList"
+          style="width:100%;"
+          @change="classChange"
+        ></clazz-select>
+      </el-form-item>
+      <el-form-item prop="studentCount" label="人数:">
+        <el-input-number
+          v-model="modalForm.studentCount"
+          :min="0"
+          :step="1"
+          step-strictly
+          disabled
+          :controls="false"
+        ></el-input-number>
+      </el-form-item>
+      <el-form-item prop="printHouseId" label="印刷室:">
+        <el-select
+          v-model="modalForm.printHouseId"
+          placeholder="请选择"
+          @change="printHouseChange"
+        >
+          <el-option
+            v-for="room in printHouses"
+            :key="room.houseId"
+            :value="room.houseId"
+            :label="room.houseName"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <el-form
+      ref="extendFieldFormComp"
+      label-width="100px"
+      :rules="extendFieldRules"
+      :model="extendFieldModal"
+    >
+      <el-form-item
+        v-for="item in extendFields"
+        :key="item.code"
+        :label="`${item.name}:`"
+        :prop="item.code"
+      >
+        <el-input
+          v-model="extendFieldModal[item.code]"
+          :placeholder="`请填写${item.name}`"
+          clearable
+        ></el-input>
+      </el-form-item>
+    </el-form>
+
+    <div slot="footer">
+      <el-button type="primary" :disabled="isSubmit" @click="submit"
+        >确认</el-button
+      >
+      <el-button @click="cancel">取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { calcSum } from "@/plugins/utils";
+
+export default {
+  name: "modify-print-task",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      }
+    },
+    extendFields: {
+      type: Array,
+      default() {
+        return [];
+      }
+    },
+    printHouses: {
+      type: Array,
+      default() {
+        return [];
+      }
+    },
+    classList: {
+      type: Array,
+      default() {
+        return [];
+      }
+    }
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: {},
+      tableData: [],
+      teachers: [],
+      classIds: [],
+      rules: {
+        classId: [
+          {
+            required: true,
+            message: "请选择考试对象",
+            trigger: "change"
+          }
+        ],
+        studentCount: [
+          {
+            required: true,
+            message: "请输入人数",
+            trigger: "change"
+          }
+        ],
+        printHouseId: [
+          {
+            required: true,
+            message: "请选择印刷室",
+            trigger: "change"
+          }
+        ]
+      },
+      // extend field
+      extendFieldModal: {},
+      extendFieldRules: {}
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.modalForm = { ...this.instance };
+      this.classIds = this.modalForm.classId
+        ? this.modalForm.classId.split(",")
+        : [];
+      const extendVals = this.instance.extends;
+      let extendFieldModal = {};
+      this.extendFields.forEach(field => {
+        extendFieldModal[field.code] = extendVals ? extendVals[field.code] : "";
+      });
+      this.extendFieldModal = extendFieldModal;
+
+      this.extendFieldRules = {};
+      this.extendFields.forEach(field => {
+        this.extendFieldRules[field.code] = [
+          {
+            required: true,
+            message: `请输入${field.name}`,
+            trigger: "change"
+          },
+          {
+            max: 100,
+            message: `${field.name}不能超过100个字符`,
+            trigger: "change"
+          }
+        ];
+      });
+      this.$nextTick(() => {
+        this.$refs.modalFormComp.clearValidate();
+        this.$refs.extendFieldFormComp.clearValidate();
+      });
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    classChange(classes) {
+      this.modalForm.className = classes.map(item => item.name).join();
+      this.modalForm.classId = this.classIds.join();
+      this.modalForm.studentCount = calcSum(
+        classes.map(item => item.studentCount)
+      );
+      this.$refs.modalFormComp.validateField("classId", () => {});
+    },
+    printHouseChange(val) {
+      const curHouse = this.printHouses.find(item => item.houseId === val);
+      if (curHouse) {
+        this.modalForm.printHouseName = curHouse.houseName;
+      }
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      const extendValid = await this.$refs.extendFieldFormComp
+        .validate()
+        .catch(() => {});
+      if (!extendValid) return;
+
+      let datas = { ...this.modalForm };
+      datas.extends = { ...this.extendFieldModal };
+
+      this.$emit("modified", datas);
+      this.cancel();
+    }
+  }
+};
+</script>

+ 2 - 1
src/modules/exam/views/TaskApplyManage.vue

@@ -191,7 +191,8 @@
 
 <script>
 import ModifyTaskApply from "../components/ModifyTaskApply";
-import CreateTaskApply from "../components/CreateTaskApply";
+import CreateTaskApply from "../components/createTaskApply/CreateTaskApply";
+// import CreateTaskApply from "../components/CreateTaskApply";
 import { AUDITING_STATUS } from "@/constants/enumerate";
 import pickerOptions from "@/constants/datePickerOptions";
 import { taskApplyListPage, cancelOrRestartTaskApply } from "../api";