Browse Source

全部下拉菜单增加模糊匹配

zhangjie 2 years ago
parent
commit
6debfcea75

+ 213 - 212
src/modules/admin/components/ModifyUser.vue

@@ -1,212 +1,213 @@
-<template>
-  <el-dialog
-    class="modify-user"
-    :visible.sync="modalIsShow"
-    :title="title"
-    top="10vh"
-    width="448px"
-    :close-on-click-modal="false"
-    :close-on-press-escape="false"
-    append-to-body
-    @open="visibleChange"
-  >
-    <el-form
-      ref="modalFormComp"
-      :model="modalForm"
-      :rules="rules"
-      label-position="top"
-    >
-      <el-form-item prop="loginName" label="用户名:">
-        <el-input
-          v-model.trim="modalForm.loginName"
-          placeholder="请输入用户名"
-          :disabled="isEdit"
-        ></el-input>
-      </el-form-item>
-      <el-form-item prop="realName" label="姓名:">
-        <el-input
-          v-model.trim="modalForm.realName"
-          placeholder="请输入姓名"
-          clearable
-        ></el-input>
-      </el-form-item>
-      <el-form-item prop="mobileNumber" label="手机号:">
-        <el-input
-          v-model.trim="modalForm.mobileNumber"
-          placeholder="请输入手机号"
-          clearable
-        ></el-input>
-      </el-form-item>
-      <el-form-item prop="code" label="工号:">
-        <el-input
-          v-model.trim="modalForm.code"
-          placeholder="请输入工号"
-          clearable
-        ></el-input>
-      </el-form-item>
-      <el-form-item prop="roleIds" label="角色:">
-        <el-select
-          style="width:100%;"
-          v-model="modalForm.roleIds"
-          placeholder="请选择角色"
-          multiple
-        >
-          <el-option
-            v-for="item in roles"
-            :key="item.id"
-            :value="item.id"
-            :label="item.name"
-          >
-          </el-option>
-        </el-select>
-      </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 { updateUser } from "../api";
-import { phone } from "@/plugins/formRules";
-
-const initModalForm = {
-  id: "",
-  loginName: "",
-  realName: "",
-  code: "",
-  mobileNumber: "",
-  roleIds: []
-};
-
-export default {
-  name: "modify-user",
-  props: {
-    instance: {
-      type: Object,
-      default() {
-        return {};
-      }
-    },
-    roles: {
-      type: Array,
-      default() {
-        return [];
-      }
-    }
-  },
-  computed: {
-    isEdit() {
-      return !!this.instance.id;
-    },
-    title() {
-      return (this.isEdit ? "编辑" : "新增") + "用户";
-    }
-  },
-  data() {
-    const roleIdsValidator = (rule, value, callback) => {
-      if (!value || !value.length) {
-        callback(new Error("请选择角色"));
-      } else {
-        callback();
-      }
-    };
-    return {
-      modalIsShow: false,
-      isSubmit: false,
-      modalForm: {},
-      rules: {
-        mobileNumber: phone,
-        loginName: [
-          {
-            required: true,
-            message: "请输入用户名",
-            trigger: "change"
-          },
-          {
-            max: 50,
-            message: "用户名不能超过50",
-            trigger: "change"
-          }
-        ],
-        realName: [
-          {
-            required: true,
-            message: "请输入姓名",
-            trigger: "change"
-          },
-          {
-            max: 50,
-            message: "姓名不能超过50",
-            trigger: "change"
-          }
-        ],
-        code: [
-          {
-            required: true,
-            message: "请输入工号",
-            trigger: "change"
-          },
-          {
-            max: 50,
-            message: "工号不能超过50",
-            trigger: "change"
-          }
-        ],
-        roleIds: [
-          {
-            required: true,
-            validator: roleIdsValidator,
-            trigger: "change"
-          }
-        ]
-      },
-      user: {},
-      courses: [],
-      roleTypes: []
-    };
-  },
-  methods: {
-    initData(val) {
-      if (val.id) {
-        this.modalForm = this.$objAssign(initModalForm, val);
-        this.modalForm.roleIds = val.roles.map(item => item.id);
-      } else {
-        this.modalForm = { ...initModalForm };
-        this.$nextTick(() => {
-          this.$refs.modalFormComp.clearValidate();
-        });
-      }
-    },
-    visibleChange() {
-      this.initData(this.instance);
-    },
-    cancel() {
-      this.modalIsShow = false;
-    },
-    open() {
-      this.modalIsShow = true;
-    },
-    async submit() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
-      if (!valid) return;
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-
-      const datas = { ...this.modalForm };
-      const data = await updateUser(datas).catch(() => {});
-      this.isSubmit = false;
-      if (!data) return;
-
-      this.$message.success("修改成功!");
-      this.$emit("modified");
-      this.cancel();
-    }
-  }
-};
-</script>
+<template>
+  <el-dialog
+    class="modify-user"
+    :visible.sync="modalIsShow"
+    :title="title"
+    top="10vh"
+    width="448px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-position="top"
+    >
+      <el-form-item prop="loginName" label="用户名:">
+        <el-input
+          v-model.trim="modalForm.loginName"
+          placeholder="请输入用户名"
+          :disabled="isEdit"
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="realName" label="姓名:">
+        <el-input
+          v-model.trim="modalForm.realName"
+          placeholder="请输入姓名"
+          clearable
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="mobileNumber" label="手机号:">
+        <el-input
+          v-model.trim="modalForm.mobileNumber"
+          placeholder="请输入手机号"
+          clearable
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="code" label="工号:">
+        <el-input
+          v-model.trim="modalForm.code"
+          placeholder="请输入工号"
+          clearable
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="roleIds" label="角色:">
+        <el-select
+          style="width:100%;"
+          v-model="modalForm.roleIds"
+          placeholder="请选择角色"
+          multiple
+          filterable
+        >
+          <el-option
+            v-for="item in roles"
+            :key="item.id"
+            :value="item.id"
+            :label="item.name"
+          >
+          </el-option>
+        </el-select>
+      </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 { updateUser } from "../api";
+import { phone } from "@/plugins/formRules";
+
+const initModalForm = {
+  id: "",
+  loginName: "",
+  realName: "",
+  code: "",
+  mobileNumber: "",
+  roleIds: []
+};
+
+export default {
+  name: "modify-user",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      }
+    },
+    roles: {
+      type: Array,
+      default() {
+        return [];
+      }
+    }
+  },
+  computed: {
+    isEdit() {
+      return !!this.instance.id;
+    },
+    title() {
+      return (this.isEdit ? "编辑" : "新增") + "用户";
+    }
+  },
+  data() {
+    const roleIdsValidator = (rule, value, callback) => {
+      if (!value || !value.length) {
+        callback(new Error("请选择角色"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: {},
+      rules: {
+        mobileNumber: phone,
+        loginName: [
+          {
+            required: true,
+            message: "请输入用户名",
+            trigger: "change"
+          },
+          {
+            max: 50,
+            message: "用户名不能超过50",
+            trigger: "change"
+          }
+        ],
+        realName: [
+          {
+            required: true,
+            message: "请输入姓名",
+            trigger: "change"
+          },
+          {
+            max: 50,
+            message: "姓名不能超过50",
+            trigger: "change"
+          }
+        ],
+        code: [
+          {
+            required: true,
+            message: "请输入工号",
+            trigger: "change"
+          },
+          {
+            max: 50,
+            message: "工号不能超过50",
+            trigger: "change"
+          }
+        ],
+        roleIds: [
+          {
+            required: true,
+            validator: roleIdsValidator,
+            trigger: "change"
+          }
+        ]
+      },
+      user: {},
+      courses: [],
+      roleTypes: []
+    };
+  },
+  methods: {
+    initData(val) {
+      if (val.id) {
+        this.modalForm = this.$objAssign(initModalForm, val);
+        this.modalForm.roleIds = val.roles.map(item => item.id);
+      } else {
+        this.modalForm = { ...initModalForm };
+        this.$nextTick(() => {
+          this.$refs.modalFormComp.clearValidate();
+        });
+      }
+    },
+    visibleChange() {
+      this.initData(this.instance);
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+
+      const datas = { ...this.modalForm };
+      const data = await updateUser(datas).catch(() => {});
+      this.isSubmit = false;
+      if (!data) return;
+
+      this.$message.success("修改成功!");
+      this.$emit("modified");
+      this.cancel();
+    }
+  }
+};
+</script>

+ 1 - 1
src/modules/base/components/ModifyCardRule.vue

@@ -46,9 +46,9 @@
         <el-select
           v-model="modalForm.examNumberStyle"
           style="width: 142px;"
-          @change="numStyleChange"
           placeholder="请选择"
           :disabled="!editable"
+          @change="numStyleChange"
         >
           <el-option
             v-for="(val, key) in EXAM_NUMBER_STYLE"

+ 150 - 149
src/modules/base/components/ModifyClazzSimple.vue

@@ -1,149 +1,150 @@
-<template>
-  <el-dialog
-    class="modify-clazz"
-    :visible.sync="modalIsShow"
-    :title="title"
-    top="10vh"
-    width="448px"
-    :close-on-click-modal="false"
-    :close-on-press-escape="false"
-    append-to-body
-    @open="visibleChange"
-  >
-    <el-form
-      ref="modalFormComp"
-      :model="modalForm"
-      :rules="rules"
-      :key="modalForm.id"
-      label-position="top"
-    >
-      <el-form-item prop="teachClazzName" label="班级名称:">
-        <el-input
-          v-model.trim="modalForm.teachClazzName"
-          placeholder="请输入班级名称"
-          clearable
-        ></el-input>
-      </el-form-item>
-      <el-form-item prop="teachCourseId" label="课程:">
-        <el-select
-          v-model="modalForm.teachCourseId"
-          placeholder="课程"
-          clearable
-          :disabled="isEdit"
-          class="width-full"
-        >
-          <el-option
-            v-for="item in courses"
-            :key="item.id"
-            :value="item.id"
-            :label="item.name"
-          >
-            <span>{{ `${item.name}(${item.code})` }}</span>
-          </el-option>
-        </el-select>
-      </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 { updateClazzSimple } from "../api";
-
-const initModalForm = {
-  id: null,
-  teachClazzName: "",
-  teachCourseId: ""
-};
-
-export default {
-  name: "modify-clazz-simple",
-  props: {
-    instance: {
-      type: Object,
-      default() {
-        return {};
-      }
-    },
-    courses: {
-      type: Array,
-      default() {
-        return [];
-      }
-    }
-  },
-  computed: {
-    isEdit() {
-      return !!this.instance.id;
-    },
-    title() {
-      return (this.isEdit ? "编辑" : "新增") + "班级";
-    }
-  },
-  data() {
-    return {
-      modalIsShow: false,
-      isSubmit: false,
-      modalForm: { ...initModalForm },
-      rules: {
-        teachClazzName: [
-          {
-            required: true,
-            message: "请输入班级名称",
-            trigger: "change"
-          },
-          {
-            max: 30,
-            message: "班级名称不能超过30个字",
-            trigger: "change"
-          }
-        ],
-        teachCourseId: [
-          {
-            required: true,
-            message: "请选择课程",
-            trigger: "change"
-          }
-        ]
-      }
-    };
-  },
-  methods: {
-    initData(val) {
-      this.modalForm = this.$objAssign(initModalForm, val);
-    },
-    visibleChange() {
-      this.initData(this.instance);
-    },
-    cancel() {
-      this.modalIsShow = false;
-    },
-    open() {
-      this.modalIsShow = true;
-    },
-    async submit() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
-      if (!valid) return;
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      let datas = { ...this.modalForm };
-      const data = await updateClazzSimple(datas).catch(() => {
-        this.isSubmit = false;
-      });
-
-      if (!data) return;
-
-      this.isSubmit = false;
-      this.$message.success(this.title + "成功!");
-      this.$emit("modified");
-      this.cancel();
-    }
-  }
-};
-</script>
+<template>
+  <el-dialog
+    class="modify-clazz"
+    :visible.sync="modalIsShow"
+    :title="title"
+    top="10vh"
+    width="448px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      :key="modalForm.id"
+      label-position="top"
+    >
+      <el-form-item prop="teachClazzName" label="班级名称:">
+        <el-input
+          v-model.trim="modalForm.teachClazzName"
+          placeholder="请输入班级名称"
+          clearable
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="teachCourseId" label="课程:">
+        <el-select
+          v-model="modalForm.teachCourseId"
+          placeholder="课程"
+          clearable
+          filterable
+          :disabled="isEdit"
+          class="width-full"
+        >
+          <el-option
+            v-for="item in courses"
+            :key="item.id"
+            :value="item.id"
+            :label="item.name"
+          >
+            <span>{{ `${item.name}(${item.code})` }}</span>
+          </el-option>
+        </el-select>
+      </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 { updateClazzSimple } from "../api";
+
+const initModalForm = {
+  id: null,
+  teachClazzName: "",
+  teachCourseId: ""
+};
+
+export default {
+  name: "modify-clazz-simple",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      }
+    },
+    courses: {
+      type: Array,
+      default() {
+        return [];
+      }
+    }
+  },
+  computed: {
+    isEdit() {
+      return !!this.instance.id;
+    },
+    title() {
+      return (this.isEdit ? "编辑" : "新增") + "班级";
+    }
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      rules: {
+        teachClazzName: [
+          {
+            required: true,
+            message: "请输入班级名称",
+            trigger: "change"
+          },
+          {
+            max: 30,
+            message: "班级名称不能超过30个字",
+            trigger: "change"
+          }
+        ],
+        teachCourseId: [
+          {
+            required: true,
+            message: "请选择课程",
+            trigger: "change"
+          }
+        ]
+      }
+    };
+  },
+  methods: {
+    initData(val) {
+      this.modalForm = this.$objAssign(initModalForm, val);
+    },
+    visibleChange() {
+      this.initData(this.instance);
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      let datas = { ...this.modalForm };
+      const data = await updateClazzSimple(datas).catch(() => {
+        this.isSubmit = false;
+      });
+
+      if (!data) return;
+
+      this.isSubmit = false;
+      this.$message.success(this.title + "成功!");
+      this.$emit("modified");
+      this.cancel();
+    }
+  }
+};
+</script>

+ 1 - 0
src/modules/base/components/ModifyStudent.vue

@@ -69,6 +69,7 @@
           v-model="modalForm.clazzId"
           placeholder="请选择班级"
           style="width: 100%;"
+          filterable
         >
           <el-option
             v-for="item in clazzList"

+ 1 - 0
src/modules/base/components/ModifyUser.vue

@@ -50,6 +50,7 @@
           v-model="modalForm.roleIds"
           placeholder="请选择角色"
           multiple
+          filterable
         >
           <el-option
             v-for="item in roles"

+ 277 - 276
src/modules/base/views/PrintPlanPushManage.vue

@@ -1,276 +1,277 @@
-<template>
-  <div class="print-plan-push-manage">
-    <div class="part-box part-box-filter part-box-flex">
-      <el-form ref="FilterForm" label-position="left" label-width="85px" inline>
-        <el-form-item label="使用学期:">
-          <el-select
-            v-model.trim="filter.semesterId"
-            placeholder="请选择使用学期"
-            clearable
-          >
-            <el-option
-              v-for="item in semesters"
-              :key="item.id"
-              :value="item.id"
-              :label="item.name"
-            ></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="考试:">
-          <exam-select
-            v-model="filter.examId"
-            :semester-id="filter.semesterId"
-          ></exam-select>
-        </el-form-item>
-        <el-form-item label="印刷计划:">
-          <print-plan-select
-            v-model.trim="filter.printPlanId"
-            placeholder="印刷计划"
-            clearable
-          ></print-plan-select>
-        </el-form-item>
-        <el-form-item label-width="0px">
-          <el-button type="primary" @click="search">查询</el-button>
-        </el-form-item>
-      </el-form>
-      <div class="part-box-action">
-        <el-button
-          icon="el-icon-circle-plus-outline"
-          type="primary"
-          :disabled="loading"
-          @click="toBatchPush"
-        >
-          批量推送
-        </el-button>
-        <el-button icon="el-icon-circle-plus-outline" @click="toMergePush">
-          合并推送印刷计划
-        </el-button>
-      </div>
-    </div>
-
-    <div class="part-box part-box-pad">
-      <el-table
-        ref="TableList"
-        :data="dataList"
-        @selection-change="handleSelectionChange"
-      >
-        <el-table-column
-          type="selection"
-          width="55"
-          align="center"
-          :selectable="row => row.canPush"
-        ></el-table-column>
-        <el-table-column prop="name" label="印刷计划"></el-table-column>
-        <el-table-column prop="examStartTime" label="考试开始时间">
-          <span slot-scope="scope">{{
-            scope.row.examStartTime | timestampFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column prop="examEndTime" label="考试结束时间">
-          <span slot-scope="scope">{{
-            scope.row.examEndTime | timestampFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column prop="status" label="计划状态" width="100">
-          <span slot-scope="scope">
-            {{ scope.row.status | printPlanStatusFilter }}
-          </span>
-        </el-table-column>
-        <el-table-column prop="totalGates" label="总门次" width="80">
-        </el-table-column>
-        <el-table-column prop="totalSubjects" label="总科次" width="80">
-        </el-table-column>
-        <el-table-column prop="totalPackages" label="总卷袋数" width="100">
-        </el-table-column>
-        <el-table-column prop="createTime" label="创建时间">
-          <span slot-scope="scope">{{
-            scope.row.createTime | timestampFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column prop="createName" label="创建人"></el-table-column>
-        <el-table-column prop="thirdRelateName" label="考试名称">
-          <span slot-scope="scope">{{
-            scope.row.thirdRelateName | defaultFieldFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column prop="thirdRelateId" label="考试ID">
-          <span slot-scope="scope">{{
-            scope.row.thirdRelateId | defaultFieldFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column prop="syncStatus" label="同步状态">
-          <span slot-scope="scope">{{
-            scope.row.syncStatus | syncPrintStatusFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column class-name="action-column" label="操作" width="120px">
-          <template slot-scope="scope">
-            <el-button
-              class="btn-primary"
-              type="text"
-              @click="toPreview(scope.row)"
-              >查看</el-button
-            >
-            <el-button
-              v-if="scope.row.canPush"
-              class="btn-primary"
-              type="text"
-              :disabled="loading"
-              @click="toPush(scope.row)"
-              >推送</el-button
-            >
-          </template>
-        </el-table-column>
-      </el-table>
-      <div class="part-page">
-        <el-pagination
-          background
-          layout="total,prev, pager, next"
-          :current-page="current"
-          :total="total"
-          :page-size="size"
-          @current-change="toPage"
-        >
-        </el-pagination>
-      </div>
-    </div>
-
-    <!-- MergePushDialog -->
-    <merge-push-dialog
-      ref="MergePushDialog"
-      :ids="multipleSelection"
-      @modified="mergePushed"
-    ></merge-push-dialog>
-    <!-- ModifyPrintPlan -->
-    <modify-print-plan
-      ref="ModifyPrintPlan"
-      :instance="curPrintPlan"
-      edit-type="PREVIEW"
-    ></modify-print-plan>
-  </div>
-</template>
-
-<script>
-import {
-  printPlanPushListQuery,
-  printPlanBatchPush,
-  printPlanPush
-} from "../api";
-import { examSemesterList } from "../../base/api";
-import MergePushDialog from "../components/MergePushDialog";
-import ModifyPrintPlan from "../../print/components/ModifyPrintPlan";
-
-export default {
-  name: "print-plan-push-manage",
-  components: { MergePushDialog, ModifyPrintPlan },
-  data() {
-    return {
-      filter: {
-        semesterId: "",
-        printPlanId: "",
-        examId: ""
-      },
-      current: 1,
-      size: this.GLOBAL.pageSize,
-      total: 0,
-      loading: false,
-      semesters: [],
-      dataList: [],
-      curPrintPlan: {},
-      multipleSelection: []
-    };
-  },
-  mounted() {
-    this.initData();
-  },
-  methods: {
-    async initData() {
-      await this.getSemesters();
-      await this.getList();
-    },
-    async getList() {
-      const datas = {
-        ...this.filter,
-        pageNumber: this.current,
-        pageSize: this.size
-      };
-      const data = await printPlanPushListQuery(datas);
-      this.dataList = data.records;
-      this.total = data.total;
-    },
-    toPage(page) {
-      this.multipleSelection = [];
-      this.current = page;
-      this.getList();
-    },
-    search() {
-      this.toPage(1);
-    },
-    async getSemesters() {
-      const res = await examSemesterList({});
-      this.semesters = res || [];
-      if (!this.semesters.length) return;
-
-      let curSemester = this.semesters.find(item => item.inUsed);
-      curSemester = curSemester || this.semesters[0];
-      this.filter.semesterId = curSemester.id;
-    },
-    handleSelectionChange(val) {
-      this.multipleSelection = val.map(item => item.id);
-    },
-    async toBatchPush() {
-      if (this.loading) return;
-      if (!this.multipleSelection.length) {
-        this.$message.error("请选择要批量推送的数据");
-        return;
-      }
-      const result = await this.$confirm("确定要推送选中的计划吗?", "提示", {
-        type: "warning"
-      }).catch(() => {});
-      if (result !== "confirm") return;
-
-      this.loading = true;
-      const data = await printPlanBatchPush(
-        this.multipleSelection
-      ).catch(() => {});
-      this.loading = false;
-      if (!data) return;
-
-      this.$message.success("提交成功!");
-      this.toPage(this.current);
-    },
-    toMergePush() {
-      if (!this.multipleSelection.length) {
-        this.$message.error("请选择要合并推送的数据");
-        return;
-      }
-      this.$refs.MergePushDialog.open();
-    },
-    mergePushed() {
-      this.toPage(this.current);
-    },
-    toPreview(row) {
-      this.curPrintPlan = row;
-      this.$refs.ModifyPrintPlan.open();
-    },
-    async toPush(row) {
-      if (this.loading) return;
-      const result = await this.$confirm("确定要推送当前计划吗?", "提示", {
-        type: "warning"
-      }).catch(() => {});
-      if (result !== "confirm") return;
-
-      this.loading = true;
-      const data = await printPlanPush({
-        printPlanId: row.id,
-        thirdRelateId: row.thirdRelateId
-      }).catch(() => {});
-      this.loading = false;
-      if (!data) return;
-
-      this.$message.success("提交成功!");
-      this.toPage(this.current);
-    }
-  }
-};
-</script>
+<template>
+  <div class="print-plan-push-manage">
+    <div class="part-box part-box-filter part-box-flex">
+      <el-form ref="FilterForm" label-position="left" label-width="85px" inline>
+        <el-form-item label="使用学期:">
+          <el-select
+            v-model.trim="filter.semesterId"
+            placeholder="请选择使用学期"
+            clearable
+            filterable
+          >
+            <el-option
+              v-for="item in semesters"
+              :key="item.id"
+              :value="item.id"
+              :label="item.name"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="考试:">
+          <exam-select
+            v-model="filter.examId"
+            :semester-id="filter.semesterId"
+          ></exam-select>
+        </el-form-item>
+        <el-form-item label="印刷计划:">
+          <print-plan-select
+            v-model.trim="filter.printPlanId"
+            placeholder="印刷计划"
+            clearable
+          ></print-plan-select>
+        </el-form-item>
+        <el-form-item label-width="0px">
+          <el-button type="primary" @click="search">查询</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="part-box-action">
+        <el-button
+          icon="el-icon-circle-plus-outline"
+          type="primary"
+          :disabled="loading"
+          @click="toBatchPush"
+        >
+          批量推送
+        </el-button>
+        <el-button icon="el-icon-circle-plus-outline" @click="toMergePush">
+          合并推送印刷计划
+        </el-button>
+      </div>
+    </div>
+
+    <div class="part-box part-box-pad">
+      <el-table
+        ref="TableList"
+        :data="dataList"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column
+          type="selection"
+          width="55"
+          align="center"
+          :selectable="row => row.canPush"
+        ></el-table-column>
+        <el-table-column prop="name" label="印刷计划"></el-table-column>
+        <el-table-column prop="examStartTime" label="考试开始时间">
+          <span slot-scope="scope">{{
+            scope.row.examStartTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column prop="examEndTime" label="考试结束时间">
+          <span slot-scope="scope">{{
+            scope.row.examEndTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column prop="status" label="计划状态" width="100">
+          <span slot-scope="scope">
+            {{ scope.row.status | printPlanStatusFilter }}
+          </span>
+        </el-table-column>
+        <el-table-column prop="totalGates" label="总门次" width="80">
+        </el-table-column>
+        <el-table-column prop="totalSubjects" label="总科次" width="80">
+        </el-table-column>
+        <el-table-column prop="totalPackages" label="总卷袋数" width="100">
+        </el-table-column>
+        <el-table-column prop="createTime" label="创建时间">
+          <span slot-scope="scope">{{
+            scope.row.createTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column prop="createName" label="创建人"></el-table-column>
+        <el-table-column prop="thirdRelateName" label="考试名称">
+          <span slot-scope="scope">{{
+            scope.row.thirdRelateName | defaultFieldFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column prop="thirdRelateId" label="考试ID">
+          <span slot-scope="scope">{{
+            scope.row.thirdRelateId | defaultFieldFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column prop="syncStatus" label="同步状态">
+          <span slot-scope="scope">{{
+            scope.row.syncStatus | syncPrintStatusFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column class-name="action-column" label="操作" width="120px">
+          <template slot-scope="scope">
+            <el-button
+              class="btn-primary"
+              type="text"
+              @click="toPreview(scope.row)"
+              >查看</el-button
+            >
+            <el-button
+              v-if="scope.row.canPush"
+              class="btn-primary"
+              type="text"
+              :disabled="loading"
+              @click="toPush(scope.row)"
+              >推送</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="part-page">
+        <el-pagination
+          background
+          layout="total,prev, pager, next"
+          :current-page="current"
+          :total="total"
+          :page-size="size"
+          @current-change="toPage"
+        >
+        </el-pagination>
+      </div>
+    </div>
+
+    <!-- MergePushDialog -->
+    <merge-push-dialog
+      ref="MergePushDialog"
+      :ids="multipleSelection"
+      @modified="mergePushed"
+    ></merge-push-dialog>
+    <!-- ModifyPrintPlan -->
+    <modify-print-plan
+      ref="ModifyPrintPlan"
+      :instance="curPrintPlan"
+      edit-type="PREVIEW"
+    ></modify-print-plan>
+  </div>
+</template>
+
+<script>
+import {
+  printPlanPushListQuery,
+  printPlanBatchPush,
+  printPlanPush
+} from "../api";
+import { examSemesterList } from "../../base/api";
+import MergePushDialog from "../components/MergePushDialog";
+import ModifyPrintPlan from "../../print/components/ModifyPrintPlan";
+
+export default {
+  name: "print-plan-push-manage",
+  components: { MergePushDialog, ModifyPrintPlan },
+  data() {
+    return {
+      filter: {
+        semesterId: "",
+        printPlanId: "",
+        examId: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      loading: false,
+      semesters: [],
+      dataList: [],
+      curPrintPlan: {},
+      multipleSelection: []
+    };
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    async initData() {
+      await this.getSemesters();
+      await this.getList();
+    },
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      const data = await printPlanPushListQuery(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.multipleSelection = [];
+      this.current = page;
+      this.getList();
+    },
+    search() {
+      this.toPage(1);
+    },
+    async getSemesters() {
+      const res = await examSemesterList({});
+      this.semesters = res || [];
+      if (!this.semesters.length) return;
+
+      let curSemester = this.semesters.find(item => item.inUsed);
+      curSemester = curSemester || this.semesters[0];
+      this.filter.semesterId = curSemester.id;
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val.map(item => item.id);
+    },
+    async toBatchPush() {
+      if (this.loading) return;
+      if (!this.multipleSelection.length) {
+        this.$message.error("请选择要批量推送的数据");
+        return;
+      }
+      const result = await this.$confirm("确定要推送选中的计划吗?", "提示", {
+        type: "warning"
+      }).catch(() => {});
+      if (result !== "confirm") return;
+
+      this.loading = true;
+      const data = await printPlanBatchPush(
+        this.multipleSelection
+      ).catch(() => {});
+      this.loading = false;
+      if (!data) return;
+
+      this.$message.success("提交成功!");
+      this.toPage(this.current);
+    },
+    toMergePush() {
+      if (!this.multipleSelection.length) {
+        this.$message.error("请选择要合并推送的数据");
+        return;
+      }
+      this.$refs.MergePushDialog.open();
+    },
+    mergePushed() {
+      this.toPage(this.current);
+    },
+    toPreview(row) {
+      this.curPrintPlan = row;
+      this.$refs.ModifyPrintPlan.open();
+    },
+    async toPush(row) {
+      if (this.loading) return;
+      const result = await this.$confirm("确定要推送当前计划吗?", "提示", {
+        type: "warning"
+      }).catch(() => {});
+      if (result !== "confirm") return;
+
+      this.loading = true;
+      const data = await printPlanPush({
+        printPlanId: row.id,
+        thirdRelateId: row.thirdRelateId
+      }).catch(() => {});
+      this.loading = false;
+      if (!data) return;
+
+      this.$message.success("提交成功!");
+      this.toPage(this.current);
+    }
+  }
+};
+</script>

+ 256 - 255
src/modules/exam/components/BatchAddExamTask.vue

@@ -1,255 +1,256 @@
-<template>
-  <el-dialog
-    class="batch-add-exam-task"
-    :visible.sync="modalIsShow"
-    title="批量新建命题任务"
-    top="10vh"
-    width="700px"
-    :close-on-click-modal="false"
-    :close-on-press-escape="false"
-    append-to-body
-    @open="visibleChange"
-  >
-    <el-form
-      ref="modalFormComp"
-      :model="modalForm"
-      :rules="rules"
-      label-position="top"
-    >
-      <el-form-item prop="semesterId" label="学期:">
-        <semester-select
-          v-model="modalForm.semesterId"
-          class="width-400"
-        ></semester-select>
-      </el-form-item>
-      <el-form-item prop="examId" label="考试:">
-        <exam-select
-          v-model="modalForm.examId"
-          class="width-400"
-          :semester-id="modalForm.semesterId"
-        ></exam-select>
-      </el-form-item>
-      <el-form-item prop="batchNo" label="命题任务表:">
-        <upload-file-view
-          :upload-url="uploadExamTaskUrl"
-          @upload-success="uploadExamTaskSuccess"
-          ref="ExamTaskUploadFileView"
-          v-if="modalIsShow"
-        ></upload-file-view>
-        <el-button
-          type="success"
-          icon="el-icon-download"
-          style="margin-left:10px;"
-        >
-          <a :href="downloadUrl" download="命题任务导入模板.xlsx">模板下载</a>
-        </el-button>
-      </el-form-item>
-      <el-form-item prop="startTime" label="命题时间:">
-        <el-date-picker
-          v-model="createTime"
-          type="datetimerange"
-          range-separator="至"
-          start-placeholder="开始时间"
-          end-placeholder="结束时间"
-          value-format="timestamp"
-          align="right"
-          unlink-panels
-          @change="dateChange"
-        >
-        </el-date-picker>
-      </el-form-item>
-      <el-form-item label="指派命题老师:">
-        <el-table class="el-table--noback" :data="tasks" border>
-          <el-table-column prop="courseName" label="课程(代码)">
-            <span slot-scope="scope">
-              {{ scope.row.courseName }}({{ scope.row.courseCode }})
-            </span>
-          </el-table-column>
-          <el-table-column prop="paperNumber" label="试卷编号">
-            <template slot-scope="scope">
-              {{ scope.row.paperNumber | defaultFieldFilter }}
-            </template>
-          </el-table-column>
-          <el-table-column label="命题老师" width="180px">
-            <template slot-scope="scope">
-              <el-select
-                v-model="scope.row.userId"
-                size="small"
-                style="width: 150px;"
-                placeholder="请选择"
-                clearable
-              >
-                <el-option
-                  v-for="user in scope.row.users"
-                  :key="user.id"
-                  :value="user.id"
-                  :label="user.name"
-                ></el-option>
-              </el-select>
-            </template>
-          </el-table-column>
-        </el-table>
-      </el-form-item>
-    </el-form>
-
-    <div slot="footer">
-      <el-button type="primary" :disabled="isSubmit" @click="toSave"
-        >确认</el-button
-      >
-      <el-button @click="cancel">取消</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import { batchAddExamTask } from "../api";
-import UploadFileView from "@/components/UploadFileView";
-
-const initModalForm = {
-  semesterId: "",
-  examId: "",
-  cardRuleId: "",
-  startTime: "",
-  endTime: "",
-  batchNo: "",
-  userIds: []
-};
-
-export default {
-  name: "batch-add-exam-task",
-  components: { UploadFileView },
-  data() {
-    return {
-      modalIsShow: false,
-      isSubmit: false,
-      modalForm: { ...initModalForm },
-      createTime: [],
-      tasks: [],
-      batchConfirmMessage: "",
-      rules: {
-        semesterId: [
-          {
-            required: true,
-            message: "请选择学期",
-            trigger: "change"
-          }
-        ],
-        examId: [
-          {
-            required: true,
-            message: "请选择考试",
-            trigger: "change"
-          }
-        ],
-        batchNo: [
-          {
-            required: true,
-            message: "请上传命题任务表",
-            trigger: "change"
-          }
-        ],
-        startTime: [
-          {
-            required: true,
-            message: "请设置命题时间",
-            trigger: "change"
-          }
-        ]
-      },
-      downloadUrl: "/temps/examTaskTemplate.xlsx",
-      // import
-      uploadExamTaskUrl: "/api/admin/exam/task/import"
-    };
-  },
-  methods: {
-    visibleChange() {
-      this.modalForm = { ...initModalForm };
-      this.tasks = [];
-      this.createTime = [];
-      this.batchConfirmMessage = "";
-
-      this.$nextTick(() => {
-        this.$refs.modalFormComp.clearValidate();
-      });
-    },
-    cancel() {
-      this.modalIsShow = false;
-    },
-    open() {
-      this.modalIsShow = true;
-    },
-    dateChange() {
-      if (this.createTime) {
-        this.modalForm.startTime = this.createTime[0];
-        this.modalForm.endTime = this.createTime[1];
-      } else {
-        this.modalForm.startTime = "";
-        this.modalForm.endTime = "";
-      }
-    },
-    toSave() {
-      if (!this.batchConfirmMessage) {
-        this.save();
-        return;
-      }
-
-      this.$confirm(`${this.batchConfirmMessage}, 是否继续提交?`, "提示", {
-        type: "warning"
-      })
-        .then(() => {
-          this.save();
-        })
-        .catch(() => {});
-    },
-    async save() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
-      if (!valid) return;
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      let datas = {
-        ...this.modalForm
-      };
-      datas.users = this.tasks.map(task => {
-        return {
-          courseCode: task.courseCode,
-          courseName: task.courseName,
-          paperNumber: task.paperNumber,
-          specialty: task.specialty,
-          userId: task.userId
-        };
-      });
-
-      const data = await batchAddExamTask(datas).catch(() => {});
-      this.isSubmit = false;
-      if (!data) return;
-
-      this.$message.success("保存成功!");
-      this.$emit("modified");
-      this.cancel();
-    },
-    // upload-handler
-    // uplaodError(errorData) {
-    // let message = errorData.message;
-    // try {
-    //   message = JSON.parse(errorData.error);
-    //   message = message.map(item => item.excelErrorType).join(",");
-    // } catch (msg) {
-    //   console.error(msg);
-    // }
-    // if (message) this.$notify.error({ title: "错误提示", message });
-    // },
-    uploadExamTaskSuccess(data) {
-      this.$message.success("上传成功!");
-      this.modalForm.batchNo = data.batchNo;
-      this.batchConfirmMessage = data.errorMsg;
-      this.tasks = data.tasks.map(item => {
-        if (!item.userId)
-          item.userId = item.users.length === 1 ? item.users[0].id : "";
-        return item;
-      });
-      this.$refs.modalFormComp.validateField("batchNo");
-    }
-  }
-};
-</script>
+<template>
+  <el-dialog
+    class="batch-add-exam-task"
+    :visible.sync="modalIsShow"
+    title="批量新建命题任务"
+    top="10vh"
+    width="700px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-position="top"
+    >
+      <el-form-item prop="semesterId" label="学期:">
+        <semester-select
+          v-model="modalForm.semesterId"
+          class="width-400"
+        ></semester-select>
+      </el-form-item>
+      <el-form-item prop="examId" label="考试:">
+        <exam-select
+          v-model="modalForm.examId"
+          class="width-400"
+          :semester-id="modalForm.semesterId"
+        ></exam-select>
+      </el-form-item>
+      <el-form-item prop="batchNo" label="命题任务表:">
+        <upload-file-view
+          :upload-url="uploadExamTaskUrl"
+          @upload-success="uploadExamTaskSuccess"
+          ref="ExamTaskUploadFileView"
+          v-if="modalIsShow"
+        ></upload-file-view>
+        <el-button
+          type="success"
+          icon="el-icon-download"
+          style="margin-left:10px;"
+        >
+          <a :href="downloadUrl" download="命题任务导入模板.xlsx">模板下载</a>
+        </el-button>
+      </el-form-item>
+      <el-form-item prop="startTime" label="命题时间:">
+        <el-date-picker
+          v-model="createTime"
+          type="datetimerange"
+          range-separator="至"
+          start-placeholder="开始时间"
+          end-placeholder="结束时间"
+          value-format="timestamp"
+          align="right"
+          unlink-panels
+          @change="dateChange"
+        >
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="指派命题老师:">
+        <el-table class="el-table--noback" :data="tasks" border>
+          <el-table-column prop="courseName" label="课程(代码)">
+            <span slot-scope="scope">
+              {{ scope.row.courseName }}({{ scope.row.courseCode }})
+            </span>
+          </el-table-column>
+          <el-table-column prop="paperNumber" label="试卷编号">
+            <template slot-scope="scope">
+              {{ scope.row.paperNumber | defaultFieldFilter }}
+            </template>
+          </el-table-column>
+          <el-table-column label="命题老师" width="180px">
+            <template slot-scope="scope">
+              <el-select
+                v-model="scope.row.userId"
+                size="small"
+                style="width: 150px;"
+                placeholder="请选择"
+                clearable
+                filterable
+              >
+                <el-option
+                  v-for="user in scope.row.users"
+                  :key="user.id"
+                  :value="user.id"
+                  :label="user.name"
+                ></el-option>
+              </el-select>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form-item>
+    </el-form>
+
+    <div slot="footer">
+      <el-button type="primary" :disabled="isSubmit" @click="toSave"
+        >确认</el-button
+      >
+      <el-button @click="cancel">取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { batchAddExamTask } from "../api";
+import UploadFileView from "@/components/UploadFileView";
+
+const initModalForm = {
+  semesterId: "",
+  examId: "",
+  cardRuleId: "",
+  startTime: "",
+  endTime: "",
+  batchNo: "",
+  userIds: []
+};
+
+export default {
+  name: "batch-add-exam-task",
+  components: { UploadFileView },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      createTime: [],
+      tasks: [],
+      batchConfirmMessage: "",
+      rules: {
+        semesterId: [
+          {
+            required: true,
+            message: "请选择学期",
+            trigger: "change"
+          }
+        ],
+        examId: [
+          {
+            required: true,
+            message: "请选择考试",
+            trigger: "change"
+          }
+        ],
+        batchNo: [
+          {
+            required: true,
+            message: "请上传命题任务表",
+            trigger: "change"
+          }
+        ],
+        startTime: [
+          {
+            required: true,
+            message: "请设置命题时间",
+            trigger: "change"
+          }
+        ]
+      },
+      downloadUrl: "/temps/examTaskTemplate.xlsx",
+      // import
+      uploadExamTaskUrl: "/api/admin/exam/task/import"
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.modalForm = { ...initModalForm };
+      this.tasks = [];
+      this.createTime = [];
+      this.batchConfirmMessage = "";
+
+      this.$nextTick(() => {
+        this.$refs.modalFormComp.clearValidate();
+      });
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    dateChange() {
+      if (this.createTime) {
+        this.modalForm.startTime = this.createTime[0];
+        this.modalForm.endTime = this.createTime[1];
+      } else {
+        this.modalForm.startTime = "";
+        this.modalForm.endTime = "";
+      }
+    },
+    toSave() {
+      if (!this.batchConfirmMessage) {
+        this.save();
+        return;
+      }
+
+      this.$confirm(`${this.batchConfirmMessage}, 是否继续提交?`, "提示", {
+        type: "warning"
+      })
+        .then(() => {
+          this.save();
+        })
+        .catch(() => {});
+    },
+    async save() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      let datas = {
+        ...this.modalForm
+      };
+      datas.users = this.tasks.map(task => {
+        return {
+          courseCode: task.courseCode,
+          courseName: task.courseName,
+          paperNumber: task.paperNumber,
+          specialty: task.specialty,
+          userId: task.userId
+        };
+      });
+
+      const data = await batchAddExamTask(datas).catch(() => {});
+      this.isSubmit = false;
+      if (!data) return;
+
+      this.$message.success("保存成功!");
+      this.$emit("modified");
+      this.cancel();
+    },
+    // upload-handler
+    // uplaodError(errorData) {
+    // let message = errorData.message;
+    // try {
+    //   message = JSON.parse(errorData.error);
+    //   message = message.map(item => item.excelErrorType).join(",");
+    // } catch (msg) {
+    //   console.error(msg);
+    // }
+    // if (message) this.$notify.error({ title: "错误提示", message });
+    // },
+    uploadExamTaskSuccess(data) {
+      this.$message.success("上传成功!");
+      this.modalForm.batchNo = data.batchNo;
+      this.batchConfirmMessage = data.errorMsg;
+      this.tasks = data.tasks.map(item => {
+        if (!item.userId)
+          item.userId = item.users.length === 1 ? item.users[0].id : "";
+        return item;
+      });
+      this.$refs.modalFormComp.validateField("batchNo");
+    }
+  }
+};
+</script>

+ 341 - 340
src/modules/exam/components/CardOptionDialog.vue

@@ -1,340 +1,341 @@
-<template>
-  <div class="card-option-dialog">
-    <el-dialog
-      class="card-option-dialog"
-      :visible.sync="modalIsShow"
-      title="选择制卡方式"
-      top="10vh"
-      width="600px"
-      :close-on-click-modal="false"
-      :close-on-press-escape="false"
-      append-to-body
-      @open="visibleChange"
-    >
-      <div class="card-option-body">
-        <p>请您选择创建题卡方式:</p>
-        <p>1.自助创建:使用题卡工具自助设计题卡,操作简单,即刻生成;</p>
-        <p>
-          2.申请客服制卡:由客服后台统一处理,处理完毕后您可查看或微调。
-        </p>
-        <div class="card-type">
-          <el-radio-group v-model="modalForm.makeMethod">
-            <el-radio
-              v-for="item in cardSourceTypes"
-              :key="item.type"
-              :label="item.type"
-              :disabled="item.disabled"
-              >{{ item.name }}</el-radio
-            >
-          </el-radio-group>
-        </div>
-        <!-- card-select -->
-        <div class="card-select" v-if="modalForm.makeMethod === 'SELECT'">
-          <el-form
-            ref="ModalForm"
-            :model="modalForm"
-            :rules="rules"
-            label-width="90px"
-          >
-            <el-form-item prop="cardId" label="已选题卡:">
-              <el-select
-                v-model="modalForm.cardId"
-                style="width: 300px;"
-                placeholder="请选择"
-                clearable
-              >
-                <el-option
-                  v-for="item in cards"
-                  :key="item.id"
-                  :value="item.id"
-                  :label="item.title"
-                >
-                </el-option>
-              </el-select>
-              <span class="card-view" @click="toPreview">预览</span>
-            </el-form-item>
-          </el-form>
-        </div>
-        <!-- apply-customer -->
-        <div class="card-apply" v-if="modalForm.makeMethod === 'CUST'">
-          <el-form
-            ref="ApplyModalForm"
-            :model="applyModalForm"
-            :rules="applyRules"
-            label-width="90px"
-          >
-            <el-form-item prop="title" label="题卡标题:">
-              <el-input
-                style="width:300px"
-                v-model.trim="applyModalForm.title"
-                placeholder="请输入题卡标题"
-              ></el-input>
-            </el-form-item>
-            <el-form-item prop="custAttachmentId" label="上传文件:">
-              <upload-file-view
-                input-width="300px"
-                :upload-url="uploadUrl"
-                :upload-data="uploadData"
-                :format="format"
-                @valid-error="validError"
-                @upload-success="uploadSuccess"
-                ref="UploadFileView"
-              ></upload-file-view>
-              <p class="tips-info">
-                上传的文件只支持{{ format.join(",") }},大小不超过20M
-              </p>
-            </el-form-item>
-          </el-form>
-        </div>
-      </div>
-      <div slot="footer">
-        <el-button type="primary" :disabled="isSubmit" @click="confirm"
-          >确定</el-button
-        >
-        <el-button @click="cancel">返回</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import { cardForSelectList } from "../api";
-import { applyCustomerCard } from "../../customer/api";
-import { CARD_SOURCE_TYPE, COMMON_CARD_RULE_ID } from "@/constants/enumerate";
-import UploadFileView from "@/components/UploadFileView";
-
-const initModalForm = {
-  examId: "",
-  examTaskId: "",
-  paperType: "",
-  courseCode: "",
-  courseName: "",
-  cardRuleId: "",
-  cardId: "",
-  makeMethod: "",
-  // 考务规则
-  customCard: false
-};
-
-export default {
-  name: "card-option-dialog-view",
-  props: {
-    data: {
-      type: Object,
-      default() {
-        return {};
-      }
-    }
-  },
-  components: { UploadFileView },
-  data() {
-    return {
-      modalIsShow: false,
-      isSubmit: false,
-      modalForm: { ...initModalForm },
-      rules: {
-        cardId: [
-          {
-            required: true,
-            message: "请选择已选题卡",
-            trigger: "change"
-          }
-        ]
-      },
-      onlySelect: false,
-      cards: [],
-      cardSourceTypes: [],
-      CARD_SOURCE_TYPE,
-      // apply-customer
-      applyModalForm: {
-        title: "",
-        custAttachmentId: ""
-      },
-      applyRules: {
-        title: [
-          {
-            required: true,
-            message: "请输入文件标题",
-            trigger: "change"
-          },
-          {
-            message: "文件标题不能超过100个字",
-            max: 100,
-            trigger: "change"
-          }
-        ],
-        custAttachmentId: [
-          {
-            required: true,
-            message: "请上传样卷或试卷结构文件",
-            trigger: "change"
-          }
-        ]
-      },
-      // upload
-      format: ["pdf"],
-      maxSize: 20 * 1024 * 1024,
-      uploadUrl: "/api/admin/common/file/upload",
-      uploadData: {
-        type: "PAPER"
-      }
-    };
-  },
-  methods: {
-    visibleChange() {
-      this.getCardSourceTypes();
-      this.modalForm = this.$objAssign(initModalForm, this.data);
-      this.modalForm.makeMethod =
-        this.data.makeMethod || this.cardSourceTypes[0].type;
-
-      this.getCardList();
-    },
-    getCardSourceTypes() {
-      let cardSourceTypes = [];
-      Object.keys(CARD_SOURCE_TYPE).forEach(key => {
-        // 自助创建 =>
-        // 若题卡规则为专卡,命题人可即时创建专卡;
-        // 若题卡规则为全部通卡,则不显示该制卡方式;
-        // 申请客服制卡 =>
-        // 若题卡规则为专卡,命题人可即时创建专卡;
-        // 若题卡规则为全部通卡,则不显示该制卡方式;
-        if (this.data.cardRuleId === COMMON_CARD_RULE_ID && key !== "SELECT")
-          return;
-
-        // 考务规则中未开启客服制卡时,不显示客服制卡选项
-        if (!this.data.customCard && key === "CUST") return;
-
-        cardSourceTypes.push({
-          type: key,
-          name: CARD_SOURCE_TYPE[key]
-        });
-      });
-      this.cardSourceTypes = cardSourceTypes;
-    },
-    async getCardList() {
-      // 选择已有题卡 =>
-      // 若题卡规则为专卡,选择已有题卡时,下拉列表将显示适用的专卡和通卡;
-      // 若题卡规则为“全部通卡”,则下拉列表中只显示适用的通卡模板;
-      const data = await cardForSelectList({
-        courseCode: this.modalForm.courseCode,
-        paperType: this.modalForm.paperType,
-        cardRuleId: this.modalForm.cardRuleId,
-        examId: this.modalForm.examId
-      });
-      this.cards = data || [];
-    },
-    cancel() {
-      this.modalIsShow = false;
-    },
-    open() {
-      this.modalIsShow = true;
-    },
-    validError(errorData) {
-      this.$message.error(errorData.message);
-    },
-    uploadSuccess(data) {
-      this.$message.success("上传成功!");
-      this.applyModalForm.custAttachmentId = data.id;
-      this.$refs.ApplyModalForm.validateField("custAttachmentId");
-    },
-    async confirm() {
-      // 客服制卡
-      if (this.modalForm.makeMethod === "CUST") {
-        const valid = await this.$refs.ApplyModalForm.validate().catch(
-          () => {}
-        );
-        if (!valid) return;
-
-        if (this.isSubmit) return;
-        this.isSubmit = true;
-        // 创建题卡
-        const result = await applyCustomerCard({
-          status: "STAGE",
-          examTaskId: this.modalForm.examTaskId,
-          courseCode: this.modalForm.courseCode,
-          courseName: this.modalForm.courseName,
-          makeMethod: "CUST",
-          type: "CUSTOM",
-          ...this.applyModalForm
-        }).catch(() => {});
-        this.isSubmit = false;
-        if (!result) return;
-        this.$message.success("申请成功!");
-        this.$emit("confirm", {
-          cardId: result,
-          makeMethod: this.modalForm.makeMethod
-        });
-        this.cancel();
-        return;
-      }
-
-      // 自主创建
-      if (this.modalForm.makeMethod === "SELF") {
-        this.$emit("confirm", {
-          examTaskId: this.modalForm.examTaskId,
-          courseCode: this.modalForm.courseCode,
-          courseName: this.modalForm.courseName,
-          schoolName: this.$ls.get("schoolName"),
-          makeMethod: this.modalForm.makeMethod,
-          cardRuleId: this.modalForm.cardRuleId,
-          paperType: this.modalForm.paperType,
-          type: "CUSTOM",
-          createMethod: "STANDARD"
-        });
-        this.cancel();
-        return;
-      }
-
-      // 选择已有题卡
-      if (this.modalForm.makeMethod === "SELECT") {
-        if (!this.modalForm.cardId) {
-          this.$message.error("请选择已有的题卡!");
-          return;
-        }
-
-        this.$emit("confirm", {
-          cardId: this.modalForm.cardId,
-          makeMethod: this.modalForm.makeMethod
-        });
-        this.cancel();
-      }
-    },
-    toPreview() {
-      if (!this.modalForm.cardId) {
-        this.$message.error("请先选择题卡!");
-        return;
-      }
-      window.open(
-        this.getRouterPath({
-          name: "CardPreview",
-          params: {
-            cardId: this.modalForm.cardId,
-            viewType: "view"
-          }
-        })
-      );
-    }
-  }
-};
-</script>
-
-<style scoped>
-.card-option-body {
-  color: #878787;
-}
-.card-type {
-  margin: 32px 0;
-}
-.card-select {
-  margin-bottom: 10px;
-}
-.card-view {
-  display: inline-block;
-  margin-left: 10px;
-  color: rgba(35, 196, 185, 1);
-  cursor: pointer;
-}
-.card-view:hover {
-  color: rgba(28, 208, 161, 1);
-}
-</style>
+<template>
+  <div class="card-option-dialog">
+    <el-dialog
+      class="card-option-dialog"
+      :visible.sync="modalIsShow"
+      title="选择制卡方式"
+      top="10vh"
+      width="600px"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      append-to-body
+      @open="visibleChange"
+    >
+      <div class="card-option-body">
+        <p>请您选择创建题卡方式:</p>
+        <p>1.自助创建:使用题卡工具自助设计题卡,操作简单,即刻生成;</p>
+        <p>
+          2.申请客服制卡:由客服后台统一处理,处理完毕后您可查看或微调。
+        </p>
+        <div class="card-type">
+          <el-radio-group v-model="modalForm.makeMethod">
+            <el-radio
+              v-for="item in cardSourceTypes"
+              :key="item.type"
+              :label="item.type"
+              :disabled="item.disabled"
+              >{{ item.name }}</el-radio
+            >
+          </el-radio-group>
+        </div>
+        <!-- card-select -->
+        <div class="card-select" v-if="modalForm.makeMethod === 'SELECT'">
+          <el-form
+            ref="ModalForm"
+            :model="modalForm"
+            :rules="rules"
+            label-width="90px"
+          >
+            <el-form-item prop="cardId" label="已选题卡:">
+              <el-select
+                v-model="modalForm.cardId"
+                style="width: 300px;"
+                placeholder="请选择"
+                clearable
+                filterable
+              >
+                <el-option
+                  v-for="item in cards"
+                  :key="item.id"
+                  :value="item.id"
+                  :label="item.title"
+                >
+                </el-option>
+              </el-select>
+              <span class="card-view" @click="toPreview">预览</span>
+            </el-form-item>
+          </el-form>
+        </div>
+        <!-- apply-customer -->
+        <div class="card-apply" v-if="modalForm.makeMethod === 'CUST'">
+          <el-form
+            ref="ApplyModalForm"
+            :model="applyModalForm"
+            :rules="applyRules"
+            label-width="90px"
+          >
+            <el-form-item prop="title" label="题卡标题:">
+              <el-input
+                style="width:300px"
+                v-model.trim="applyModalForm.title"
+                placeholder="请输入题卡标题"
+              ></el-input>
+            </el-form-item>
+            <el-form-item prop="custAttachmentId" label="上传文件:">
+              <upload-file-view
+                input-width="300px"
+                :upload-url="uploadUrl"
+                :upload-data="uploadData"
+                :format="format"
+                @valid-error="validError"
+                @upload-success="uploadSuccess"
+                ref="UploadFileView"
+              ></upload-file-view>
+              <p class="tips-info">
+                上传的文件只支持{{ format.join(",") }},大小不超过20M
+              </p>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+      <div slot="footer">
+        <el-button type="primary" :disabled="isSubmit" @click="confirm"
+          >确定</el-button
+        >
+        <el-button @click="cancel">返回</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { cardForSelectList } from "../api";
+import { applyCustomerCard } from "../../customer/api";
+import { CARD_SOURCE_TYPE, COMMON_CARD_RULE_ID } from "@/constants/enumerate";
+import UploadFileView from "@/components/UploadFileView";
+
+const initModalForm = {
+  examId: "",
+  examTaskId: "",
+  paperType: "",
+  courseCode: "",
+  courseName: "",
+  cardRuleId: "",
+  cardId: "",
+  makeMethod: "",
+  // 考务规则
+  customCard: false
+};
+
+export default {
+  name: "card-option-dialog-view",
+  props: {
+    data: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  components: { UploadFileView },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      rules: {
+        cardId: [
+          {
+            required: true,
+            message: "请选择已选题卡",
+            trigger: "change"
+          }
+        ]
+      },
+      onlySelect: false,
+      cards: [],
+      cardSourceTypes: [],
+      CARD_SOURCE_TYPE,
+      // apply-customer
+      applyModalForm: {
+        title: "",
+        custAttachmentId: ""
+      },
+      applyRules: {
+        title: [
+          {
+            required: true,
+            message: "请输入文件标题",
+            trigger: "change"
+          },
+          {
+            message: "文件标题不能超过100个字",
+            max: 100,
+            trigger: "change"
+          }
+        ],
+        custAttachmentId: [
+          {
+            required: true,
+            message: "请上传样卷或试卷结构文件",
+            trigger: "change"
+          }
+        ]
+      },
+      // upload
+      format: ["pdf"],
+      maxSize: 20 * 1024 * 1024,
+      uploadUrl: "/api/admin/common/file/upload",
+      uploadData: {
+        type: "PAPER"
+      }
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.getCardSourceTypes();
+      this.modalForm = this.$objAssign(initModalForm, this.data);
+      this.modalForm.makeMethod =
+        this.data.makeMethod || this.cardSourceTypes[0].type;
+
+      this.getCardList();
+    },
+    getCardSourceTypes() {
+      let cardSourceTypes = [];
+      Object.keys(CARD_SOURCE_TYPE).forEach(key => {
+        // 自助创建 =>
+        // 若题卡规则为专卡,命题人可即时创建专卡;
+        // 若题卡规则为全部通卡,则不显示该制卡方式;
+        // 申请客服制卡 =>
+        // 若题卡规则为专卡,命题人可即时创建专卡;
+        // 若题卡规则为全部通卡,则不显示该制卡方式;
+        if (this.data.cardRuleId === COMMON_CARD_RULE_ID && key !== "SELECT")
+          return;
+
+        // 考务规则中未开启客服制卡时,不显示客服制卡选项
+        if (!this.data.customCard && key === "CUST") return;
+
+        cardSourceTypes.push({
+          type: key,
+          name: CARD_SOURCE_TYPE[key]
+        });
+      });
+      this.cardSourceTypes = cardSourceTypes;
+    },
+    async getCardList() {
+      // 选择已有题卡 =>
+      // 若题卡规则为专卡,选择已有题卡时,下拉列表将显示适用的专卡和通卡;
+      // 若题卡规则为“全部通卡”,则下拉列表中只显示适用的通卡模板;
+      const data = await cardForSelectList({
+        courseCode: this.modalForm.courseCode,
+        paperType: this.modalForm.paperType,
+        cardRuleId: this.modalForm.cardRuleId,
+        examId: this.modalForm.examId
+      });
+      this.cards = data || [];
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    validError(errorData) {
+      this.$message.error(errorData.message);
+    },
+    uploadSuccess(data) {
+      this.$message.success("上传成功!");
+      this.applyModalForm.custAttachmentId = data.id;
+      this.$refs.ApplyModalForm.validateField("custAttachmentId");
+    },
+    async confirm() {
+      // 客服制卡
+      if (this.modalForm.makeMethod === "CUST") {
+        const valid = await this.$refs.ApplyModalForm.validate().catch(
+          () => {}
+        );
+        if (!valid) return;
+
+        if (this.isSubmit) return;
+        this.isSubmit = true;
+        // 创建题卡
+        const result = await applyCustomerCard({
+          status: "STAGE",
+          examTaskId: this.modalForm.examTaskId,
+          courseCode: this.modalForm.courseCode,
+          courseName: this.modalForm.courseName,
+          makeMethod: "CUST",
+          type: "CUSTOM",
+          ...this.applyModalForm
+        }).catch(() => {});
+        this.isSubmit = false;
+        if (!result) return;
+        this.$message.success("申请成功!");
+        this.$emit("confirm", {
+          cardId: result,
+          makeMethod: this.modalForm.makeMethod
+        });
+        this.cancel();
+        return;
+      }
+
+      // 自主创建
+      if (this.modalForm.makeMethod === "SELF") {
+        this.$emit("confirm", {
+          examTaskId: this.modalForm.examTaskId,
+          courseCode: this.modalForm.courseCode,
+          courseName: this.modalForm.courseName,
+          schoolName: this.$ls.get("schoolName"),
+          makeMethod: this.modalForm.makeMethod,
+          cardRuleId: this.modalForm.cardRuleId,
+          paperType: this.modalForm.paperType,
+          type: "CUSTOM",
+          createMethod: "STANDARD"
+        });
+        this.cancel();
+        return;
+      }
+
+      // 选择已有题卡
+      if (this.modalForm.makeMethod === "SELECT") {
+        if (!this.modalForm.cardId) {
+          this.$message.error("请选择已有的题卡!");
+          return;
+        }
+
+        this.$emit("confirm", {
+          cardId: this.modalForm.cardId,
+          makeMethod: this.modalForm.makeMethod
+        });
+        this.cancel();
+      }
+    },
+    toPreview() {
+      if (!this.modalForm.cardId) {
+        this.$message.error("请先选择题卡!");
+        return;
+      }
+      window.open(
+        this.getRouterPath({
+          name: "CardPreview",
+          params: {
+            cardId: this.modalForm.cardId,
+            viewType: "view"
+          }
+        })
+      );
+    }
+  }
+};
+</script>
+
+<style scoped>
+.card-option-body {
+  color: #878787;
+}
+.card-type {
+  margin: 32px 0;
+}
+.card-select {
+  margin-bottom: 10px;
+}
+.card-view {
+  display: inline-block;
+  margin-left: 10px;
+  color: rgba(35, 196, 185, 1);
+  cursor: pointer;
+}
+.card-view:hover {
+  color: rgba(28, 208, 161, 1);
+}
+</style>

+ 330 - 329
src/modules/exam/components/CreatePrintTask.vue

@@ -1,329 +1,330 @@
-<template>
-  <el-dialog
-    class="create-print-task"
-    :visible.sync="modalIsShow"
-    :title="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="印刷计划:">
-        <span>{{ instance.printPlanName }}</span>
-      </el-form-item>
-      <el-form-item label="考试时间:"
-        >{{ instance.examStartTime | timestampFilter }} 至
-        {{ instance.examEndTime | timestampFilter }}
-      </el-form-item>
-      <el-form-item prop="examRoom" label="考场:">
-        <el-input
-          v-model="modalForm.examRoom"
-          placeholder="请填写考场名称"
-          clearable
-        ></el-input>
-      </el-form-item>
-      <el-form-item prop="examPlace" label="考点:">
-        <el-input
-          v-model="modalForm.examPlace"
-          placeholder="请填写考点名称"
-          clearable
-        ></el-input>
-      </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="campusId" label="校区:">
-        <campus-select
-          v-model="modalForm.campusId"
-          @change="campusChange"
-        ></campus-select>
-      </el-form-item>
-      <el-form-item prop="printHouseId" label="印刷室:">
-        <el-select
-          v-model="modalForm.printHouseId"
-          placeholder="请选择"
-          @change="printHouseChange"
-        >
-          <el-option
-            v-for="room in printRooms"
-            :key="room.printHouseId"
-            :value="room.printHouseId"
-            :label="room.printHouseName"
-          ></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";
-import {
-  createTaskPrint,
-  listTaskPrintHouse,
-  listTaskPrintClass
-} from "../api";
-
-export default {
-  name: "create-print-task",
-  props: {
-    instance: {
-      type: Object,
-      default() {
-        return {};
-      }
-    },
-    extendFields: {
-      type: Array,
-      default() {
-        return [];
-      }
-    }
-  },
-  computed: {
-    isEdit() {
-      return !!this.instance.id;
-    },
-    title() {
-      return (this.isEdit ? "编辑" : "新增") + "印刷任务";
-    }
-  },
-  data() {
-    return {
-      modalIsShow: false,
-      isSubmit: false,
-      modalForm: {},
-      tableData: [],
-      printRooms: [],
-      teachers: [],
-      classList: [],
-      classIds: [],
-      rules: {
-        examRoom: [
-          {
-            required: true,
-            message: "请输入考场名称",
-            trigger: "change"
-          },
-          {
-            max: 100,
-            message: "考场名称不能超过100个字符",
-            trigger: "change"
-          }
-        ],
-        examPlace: [
-          {
-            required: true,
-            message: "请输入考点名称",
-            trigger: "change"
-          },
-          {
-            max: 100,
-            message: "考点名称不能超过100个字符",
-            trigger: "change"
-          }
-        ],
-        invigilatorTeacher: [
-          {
-            required: true,
-            message: "请选择监考老师",
-            trigger: "change"
-          }
-        ],
-        classId: [
-          {
-            required: true,
-            message: "请选择考试对象",
-            trigger: "change"
-          }
-        ],
-        studentCount: [
-          {
-            required: true,
-            message: "请输入人数",
-            trigger: "change"
-          }
-        ],
-        campusId: [
-          {
-            required: true,
-            message: "请选择校区",
-            trigger: "change"
-          }
-        ],
-        printHouseId: [
-          {
-            required: true,
-            message: "请选择印刷室",
-            trigger: "change"
-          }
-        ]
-      },
-      // extend field
-      extendFieldModal: {},
-      extendFieldRules: {}
-    };
-  },
-  created() {
-    this.getPrintHouses();
-    this.extendFields.forEach(field => {
-      this.extendFieldRules[field.code] = [
-        {
-          required: true,
-          message: `请输入${field.name}`,
-          trigger: "change"
-        },
-        {
-          max: 100,
-          message: `${field.name}不能超过100个字符`,
-          trigger: "change"
-        }
-      ];
-    });
-  },
-  methods: {
-    async getPrintHouses() {
-      this.printRooms = await listTaskPrintHouse();
-    },
-    async getClazzs() {
-      this.classList = [];
-      const data = await listTaskPrintClass({
-        printPlanId: this.instance.printPlanId,
-        courseCode: this.instance.courseCode,
-        paperNumber: this.instance.paperNumber,
-        examTaskPrintId: this.instance.id
-      });
-      if (!data) return;
-
-      this.classList = data.map(item => {
-        return {
-          id: item.classId,
-          name: item.className,
-          studentCount: item.studentCount
-        };
-      });
-    },
-    visibleChange() {
-      this.getClazzs();
-      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.$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", () => {});
-    },
-    campusChange(val) {
-      this.modalForm.printHouseId = val.printHouseId;
-      this.modalForm.printHouseName = val.printHouseName;
-    },
-    printHouseChange(val) {
-      const curHouse = this.printRooms.find(item => item.printHouseId === val);
-      if (curHouse) {
-        this.modalForm.printHouseName = curHouse.printHouseName;
-      }
-    },
-    async submit() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
-      if (!valid) return;
-
-      const extendValid = await this.$refs.extendFieldFormComp
-        .validate()
-        .catch(() => {});
-      if (!extendValid) return;
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-
-      let datas = { ...this.modalForm };
-      const extendFields = this.extendFields.map(field => {
-        let info = { ...field };
-        info.value = this.extendFieldModal[field.code];
-        return info;
-      });
-      datas.extendFields = JSON.stringify(extendFields);
-      datas.extends = { ...this.extendFieldModal };
-
-      const data = await createTaskPrint(datas).catch(() => {});
-      this.isSubmit = false;
-      if (!data) return;
-
-      this.$emit("modified", this.modalForm);
-      this.cancel();
-    }
-  }
-};
-</script>
+<template>
+  <el-dialog
+    class="create-print-task"
+    :visible.sync="modalIsShow"
+    :title="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="印刷计划:">
+        <span>{{ instance.printPlanName }}</span>
+      </el-form-item>
+      <el-form-item label="考试时间:"
+        >{{ instance.examStartTime | timestampFilter }} 至
+        {{ instance.examEndTime | timestampFilter }}
+      </el-form-item>
+      <el-form-item prop="examRoom" label="考场:">
+        <el-input
+          v-model="modalForm.examRoom"
+          placeholder="请填写考场名称"
+          clearable
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="examPlace" label="考点:">
+        <el-input
+          v-model="modalForm.examPlace"
+          placeholder="请填写考点名称"
+          clearable
+        ></el-input>
+      </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="campusId" label="校区:">
+        <campus-select
+          v-model="modalForm.campusId"
+          @change="campusChange"
+        ></campus-select>
+      </el-form-item>
+      <el-form-item prop="printHouseId" label="印刷室:">
+        <el-select
+          v-model="modalForm.printHouseId"
+          placeholder="请选择"
+          filterable
+          @change="printHouseChange"
+        >
+          <el-option
+            v-for="room in printRooms"
+            :key="room.printHouseId"
+            :value="room.printHouseId"
+            :label="room.printHouseName"
+          ></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";
+import {
+  createTaskPrint,
+  listTaskPrintHouse,
+  listTaskPrintClass
+} from "../api";
+
+export default {
+  name: "create-print-task",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      }
+    },
+    extendFields: {
+      type: Array,
+      default() {
+        return [];
+      }
+    }
+  },
+  computed: {
+    isEdit() {
+      return !!this.instance.id;
+    },
+    title() {
+      return (this.isEdit ? "编辑" : "新增") + "印刷任务";
+    }
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: {},
+      tableData: [],
+      printRooms: [],
+      teachers: [],
+      classList: [],
+      classIds: [],
+      rules: {
+        examRoom: [
+          {
+            required: true,
+            message: "请输入考场名称",
+            trigger: "change"
+          },
+          {
+            max: 100,
+            message: "考场名称不能超过100个字符",
+            trigger: "change"
+          }
+        ],
+        examPlace: [
+          {
+            required: true,
+            message: "请输入考点名称",
+            trigger: "change"
+          },
+          {
+            max: 100,
+            message: "考点名称不能超过100个字符",
+            trigger: "change"
+          }
+        ],
+        invigilatorTeacher: [
+          {
+            required: true,
+            message: "请选择监考老师",
+            trigger: "change"
+          }
+        ],
+        classId: [
+          {
+            required: true,
+            message: "请选择考试对象",
+            trigger: "change"
+          }
+        ],
+        studentCount: [
+          {
+            required: true,
+            message: "请输入人数",
+            trigger: "change"
+          }
+        ],
+        campusId: [
+          {
+            required: true,
+            message: "请选择校区",
+            trigger: "change"
+          }
+        ],
+        printHouseId: [
+          {
+            required: true,
+            message: "请选择印刷室",
+            trigger: "change"
+          }
+        ]
+      },
+      // extend field
+      extendFieldModal: {},
+      extendFieldRules: {}
+    };
+  },
+  created() {
+    this.getPrintHouses();
+    this.extendFields.forEach(field => {
+      this.extendFieldRules[field.code] = [
+        {
+          required: true,
+          message: `请输入${field.name}`,
+          trigger: "change"
+        },
+        {
+          max: 100,
+          message: `${field.name}不能超过100个字符`,
+          trigger: "change"
+        }
+      ];
+    });
+  },
+  methods: {
+    async getPrintHouses() {
+      this.printRooms = await listTaskPrintHouse();
+    },
+    async getClazzs() {
+      this.classList = [];
+      const data = await listTaskPrintClass({
+        printPlanId: this.instance.printPlanId,
+        courseCode: this.instance.courseCode,
+        paperNumber: this.instance.paperNumber,
+        examTaskPrintId: this.instance.id
+      });
+      if (!data) return;
+
+      this.classList = data.map(item => {
+        return {
+          id: item.classId,
+          name: item.className,
+          studentCount: item.studentCount
+        };
+      });
+    },
+    visibleChange() {
+      this.getClazzs();
+      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.$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", () => {});
+    },
+    campusChange(val) {
+      this.modalForm.printHouseId = val.printHouseId;
+      this.modalForm.printHouseName = val.printHouseName;
+    },
+    printHouseChange(val) {
+      const curHouse = this.printRooms.find(item => item.printHouseId === val);
+      if (curHouse) {
+        this.modalForm.printHouseName = curHouse.printHouseName;
+      }
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      const extendValid = await this.$refs.extendFieldFormComp
+        .validate()
+        .catch(() => {});
+      if (!extendValid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+
+      let datas = { ...this.modalForm };
+      const extendFields = this.extendFields.map(field => {
+        let info = { ...field };
+        info.value = this.extendFieldModal[field.code];
+        return info;
+      });
+      datas.extendFields = JSON.stringify(extendFields);
+      datas.extends = { ...this.extendFieldModal };
+
+      const data = await createTaskPrint(datas).catch(() => {});
+      this.isSubmit = false;
+      if (!data) return;
+
+      this.$emit("modified", this.modalForm);
+      this.cancel();
+    }
+  }
+};
+</script>

+ 1 - 0
src/modules/exam/components/createExamAndPrintTask/InfoExamTask.vue

@@ -136,6 +136,7 @@
                 v-model="examTaskDetail.cardId"
                 placeholder="请选择"
                 style="width: 200px"
+                filterable
                 @change="cardChange"
               >
                 <el-option

+ 512 - 507
src/modules/exam/components/createExamAndPrintTask/InfoPrintTask.vue

@@ -1,507 +1,512 @@
-<template>
-  <div class="info-print-task">
-    <p v-if="IS_MODEL2" class="tips-info mb-2">
-      考试需要命题老师提交具体印刷份数
-    </p>
-    <p v-else class="tips-info mb-2">
-      考试需要命题老师提交完整的考务数据,每个卷袋表示一个考场,人数字段为应考人数,备份数量为该考场试卷题卡备用数量
-    </p>
-    <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"
-          :editable="false"
-          @change="timeChange"
-        >
-        </el-date-picker>
-        <el-time-picker
-          is-range
-          v-model="createTime"
-          range-separator="至"
-          start-placeholder="考试开始时间"
-          end-placeholder="考试结束时间"
-          placeholder="选择时间范围"
-          value-format="timestamp"
-          :editable="false"
-          @change="timeChange"
-        >
-        </el-time-picker>
-      </el-form-item>
-      <el-form-item label="考试对象:" required></el-form-item>
-    </el-form>
-    <div v-if="IS_MODEL2" class="part-box">
-      <table class="table">
-        <colgroup>
-          <col width="50" />
-          <col width="50" />
-          <col width="50" />
-          <col width="100" />
-        </colgroup>
-        <tr>
-          <th>卷袋序号</th>
-          <th>印刷份数</th>
-          <th>备份数量</th>
-          <th>印刷室</th>
-        </tr>
-        <tr>
-          <td>1</td>
-          <td>
-            <el-input-number
-              v-model="modalForm.printCount"
-              style="width:80px"
-              :min="1"
-              :max="999999"
-              :step="1"
-              step-strictly
-              :controls="false"
-            ></el-input-number>
-          </td>
-          <td>{{ infoExamPrintPlan.backupCount }}</td>
-          <td>
-            <el-select v-model="modalForm.printHouseId" placeholder="请选择">
-              <el-option
-                v-for="room in printHouses"
-                :key="room.printHouseId"
-                :value="room.printHouseId"
-                :label="room.printHouseName"
-              ></el-option>
-            </el-select>
-          </td>
-        </tr>
-      </table>
-    </div>
-    <div v-else 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="backupCount" label="备份数量" width="90">
-          <!-- <template slot-scope="scope">
-            <el-input-number
-              v-model="scope.row.backupCount"
-              style="width:60px"
-              :min="1"
-              :max="99999"
-              :step="1"
-              step-strictly
-              :controls="false"
-              @change="backupCountChange"
-            ></el-input-number>
-          </template> -->
-        </el-table-column>
-        <el-table-column prop="examPlace" label="考点名称">
-          <template slot-scope="scope">
-            <el-input
-              v-model.trim="scope.row.examPlace"
-              :maxlength="100"
-              clearable
-            ></el-input>
-          </template>
-        </el-table-column>
-        <el-table-column prop="examRoom" label="考场名称">
-          <template slot-scope="scope">
-            <el-input
-              v-model.trim="scope.row.examRoom"
-              :maxlength="50"
-              clearable
-            ></el-input>
-          </template>
-        </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.printHouseId"
-                :value="room.printHouseId"
-                :label="room.printHouseName"
-              ></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";
-import { mapState, mapMutations } from "vuex";
-
-export default {
-  name: "info-print-task",
-  components: { ModifyPrintTask },
-  data() {
-    return {
-      modalForm: {
-        examStartTime: "",
-        examEndTime: "",
-        paperNumber: "",
-        courseName: "",
-        courseCode: "",
-        printCount: 1,
-        printHouseId: ""
-      },
-      tableData: [],
-      curRow: {},
-      classList: [],
-      validClassList: [],
-      unusedClassList: [],
-      printHouses: [],
-      extendFields: [],
-      packageInfos: {
-        packageCount: 0,
-        studentCount: 0,
-        paperCount: 0,
-        paperReleaseCount: 0,
-        paperBackupCount: 0
-      },
-      // date-picker
-      curCreateTime: [],
-      createDate: "",
-      createTime: []
-    };
-  },
-  computed: {
-    ...mapState("exam", [
-      "infoExamTask",
-      "infoExamTaskDetail",
-      "infoPrintTask",
-      "infoExamPrintPlan"
-    ]),
-    cannotAdd() {
-      return !this.unusedClassList.length;
-    },
-    IS_MODEL2() {
-      return this.infoExamTask.examModel === "MODEL2";
-    }
-  },
-  watch: {
-    "infoExamTask.courseCode": function(val, oldval) {
-      if (val !== oldval) this.initData();
-    },
-    "infoExamPrintPlan.backupCount": function(val, oldval) {
-      if (val !== oldval) this.planBackupCountChange();
-    }
-  },
-  mounted() {
-    this.getExtendFields();
-    this.getPrintHouses();
-
-    const curDate = getTimeDatestamp(Date.now());
-    const hour = 60 * 60 * 1000;
-    this.curCreateTime = [curDate + 8 * hour, curDate + 10 * hour];
-    this.createTime = [...this.curCreateTime];
-  },
-  methods: {
-    ...mapMutations("exam", ["updateTaskInfo"]),
-    async initData() {
-      if (this.IS_MODEL2) return;
-
-      this.modalForm = Object.assign(this.modalForm, {
-        paperNumber: this.infoExamTask.paperNumber,
-        courseName: this.infoExamTask.courseName,
-        courseCode: this.infoExamTask.courseCode
-      });
-
-      await this.getClassList();
-
-      const { examStartTime, examEndTime } = this.infoPrintTask;
-      if (examStartTime && examEndTime) {
-        this.createTime = [examStartTime, examEndTime];
-        this.createDate = getTimeDatestamp(examStartTime);
-        this.modalForm.examStartTime = this.createTime[0];
-        this.modalForm.examEndTime = this.createTime[1];
-      }
-
-      this.tableData = [];
-      this.unusedClassList = [];
-      this.buildTableData();
-      this.updatePackageInfos();
-      this.updeteData();
-    },
-    planBackupCountChange() {
-      this.tableData.forEach(item => {
-        item.backupCount = this.infoExamPrintPlan.backupCount || 0;
-      });
-      this.updatePackageInfos();
-    },
-    checkTime() {
-      if (!this.modalForm.examStartTime || !this.modalForm.examEndTime) {
-        this.$message.error("请选择考试时间!");
-        return false;
-      }
-
-      if (this.modalForm.examStartTime >= this.modalForm.examEndTime) {
-        this.$message.error("考试开始时间必须小于考试结束时间!");
-        return false;
-      }
-      return true;
-    },
-    checkData() {
-      if (!this.checkTime()) return Promise.reject();
-
-      if (this.IS_MODEL2) {
-        if (!this.modalForm.printCount) {
-          this.$message.error("请输入印刷分数!");
-          return Promise.reject();
-        }
-        if (!this.modalForm.printHouseId) {
-          this.$message.error("请选择印刷室!");
-          return Promise.reject();
-        }
-        return Promise.resolve(true);
-      }
-
-      if (!this.tableData.length) {
-        this.$message.error("请添加考试对象!");
-        return Promise.reject();
-      }
-
-      let errorMsg = [];
-      this.tableData.forEach(row => {
-        let errorFields = [];
-        this.extendFields.forEach(field => {
-          if (!row.extends[field.code]) {
-            errorFields.push(field.name);
-          }
-        });
-
-        if (!row.printHouseId) {
-          errorFields.push("印刷室");
-        }
-        if (errorFields.length) {
-          errorMsg.push(
-            `考试对象${row.className}中,${errorFields.join("、")}必须填写`
-          );
-        }
-      });
-      if (errorMsg.length) {
-        this.$message.error(errorMsg.join("。"));
-        return Promise.reject();
-      }
-      return Promise.resolve(true);
-    },
-    updeteData() {
-      const tableList = this.tableData.map(row => {
-        let nrow = { ...row };
-        let extendFields = this.extendFields.map(field => {
-          let info = { ...field };
-          info.value = row.extends[field.code];
-          return info;
-        });
-
-        nrow.extendFields = JSON.stringify(extendFields);
-        nrow.examStartTime = this.modalForm.examStartTime;
-        nrow.examEndTime = this.modalForm.examEndTime;
-        return nrow;
-      });
-
-      this.updateTaskInfo({
-        infoPrintTask: {
-          ...this.modalForm,
-          list: tableList
-        }
-      });
-    },
-    updateData() {
-      this.$emit("data-change", this.getData());
-    },
-    buildTableData() {
-      this.tableData = this.classList.map(clazz => {
-        let modalFormData = { ...this.modalForm };
-        delete modalFormData.printHouseId;
-        let data = {
-          examPlace: "",
-          examRoom: "",
-          classId: clazz.id,
-          className: clazz.name,
-          studentCount: clazz.studentCount,
-          printHouseId: clazz.printHouseId,
-          printHouseName: clazz.printHouseName,
-          extendFields: "",
-          backupCount: this.infoExamPrintPlan.backupCount,
-          ...modalFormData
-        };
-        let extendFieldModal = {};
-        this.extendFields.forEach(field => {
-          extendFieldModal[field.code] = "";
-        });
-        data.extends = extendFieldModal;
-        return data;
-      });
-    },
-    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 = calcSum(
-        this.tableData.map(item => item.backupCount || 0)
-      );
-      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 = [];
-      if (!this.infoExamTask.courseCode) return;
-
-      const data = await listTaskApplyClass({
-        courseCode: this.infoExamTask.courseCode
-      });
-      if (!data) return;
-
-      this.classList = data.map(item => {
-        return {
-          id: item.classId,
-          name: item.className,
-          studentCount: item.studentCount,
-          printHouseId: item.printHouseId,
-          printHouseName: item.printHouseName
-        };
-      });
-    },
-    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;
-    },
-    backupCountChange() {
-      this.updatePackageInfos();
-    },
-    printHouseChange(row) {
-      const curHouse = this.printHouses.find(
-        item => item.printHouseId === row.printHouseId
-      );
-      if (curHouse) {
-        row.printHouseName = curHouse.printHouseName;
-      }
-    },
-    toAdd() {
-      if (!this.checkTime()) return;
-
-      this.curRow = {
-        examPlace: "",
-        examRoom: "",
-        classId: "",
-        className: "",
-        studentCount: null,
-        printHouseId: null,
-        printHouseName: null,
-        extendFields: "",
-        backupCount: this.infoExamPrintPlan.backupCount,
-        ...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>
+<template>
+  <div class="info-print-task">
+    <p v-if="IS_MODEL2" class="tips-info mb-2">
+      考试需要命题老师提交具体印刷份数
+    </p>
+    <p v-else class="tips-info mb-2">
+      考试需要命题老师提交完整的考务数据,每个卷袋表示一个考场,人数字段为应考人数,备份数量为该考场试卷题卡备用数量
+    </p>
+    <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"
+          :editable="false"
+          @change="timeChange"
+        >
+        </el-date-picker>
+        <el-time-picker
+          is-range
+          v-model="createTime"
+          range-separator="至"
+          start-placeholder="考试开始时间"
+          end-placeholder="考试结束时间"
+          placeholder="选择时间范围"
+          value-format="timestamp"
+          :editable="false"
+          @change="timeChange"
+        >
+        </el-time-picker>
+      </el-form-item>
+      <el-form-item label="考试对象:" required></el-form-item>
+    </el-form>
+    <div v-if="IS_MODEL2" class="part-box">
+      <table class="table">
+        <colgroup>
+          <col width="50" />
+          <col width="50" />
+          <col width="50" />
+          <col width="100" />
+        </colgroup>
+        <tr>
+          <th>卷袋序号</th>
+          <th>印刷份数</th>
+          <th>备份数量</th>
+          <th>印刷室</th>
+        </tr>
+        <tr>
+          <td>1</td>
+          <td>
+            <el-input-number
+              v-model="modalForm.printCount"
+              style="width:80px"
+              :min="1"
+              :max="999999"
+              :step="1"
+              step-strictly
+              :controls="false"
+            ></el-input-number>
+          </td>
+          <td>{{ infoExamPrintPlan.backupCount }}</td>
+          <td>
+            <el-select
+              v-model="modalForm.printHouseId"
+              placeholder="请选择"
+              filterable
+            >
+              <el-option
+                v-for="room in printHouses"
+                :key="room.printHouseId"
+                :value="room.printHouseId"
+                :label="room.printHouseName"
+              ></el-option>
+            </el-select>
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div v-else 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="backupCount" label="备份数量" width="90">
+          <!-- <template slot-scope="scope">
+            <el-input-number
+              v-model="scope.row.backupCount"
+              style="width:60px"
+              :min="1"
+              :max="99999"
+              :step="1"
+              step-strictly
+              :controls="false"
+              @change="backupCountChange"
+            ></el-input-number>
+          </template> -->
+        </el-table-column>
+        <el-table-column prop="examPlace" label="考点名称">
+          <template slot-scope="scope">
+            <el-input
+              v-model.trim="scope.row.examPlace"
+              :maxlength="100"
+              clearable
+            ></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column prop="examRoom" label="考场名称">
+          <template slot-scope="scope">
+            <el-input
+              v-model.trim="scope.row.examRoom"
+              :maxlength="50"
+              clearable
+            ></el-input>
+          </template>
+        </el-table-column>
+        <el-table-column prop="printHouseName" label="印刷室">
+          <template slot-scope="scope">
+            <el-select
+              v-model="scope.row.printHouseId"
+              placeholder="请选择"
+              filterable
+              @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
+          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";
+import { mapState, mapMutations } from "vuex";
+
+export default {
+  name: "info-print-task",
+  components: { ModifyPrintTask },
+  data() {
+    return {
+      modalForm: {
+        examStartTime: "",
+        examEndTime: "",
+        paperNumber: "",
+        courseName: "",
+        courseCode: "",
+        printCount: 1,
+        printHouseId: ""
+      },
+      tableData: [],
+      curRow: {},
+      classList: [],
+      validClassList: [],
+      unusedClassList: [],
+      printHouses: [],
+      extendFields: [],
+      packageInfos: {
+        packageCount: 0,
+        studentCount: 0,
+        paperCount: 0,
+        paperReleaseCount: 0,
+        paperBackupCount: 0
+      },
+      // date-picker
+      curCreateTime: [],
+      createDate: "",
+      createTime: []
+    };
+  },
+  computed: {
+    ...mapState("exam", [
+      "infoExamTask",
+      "infoExamTaskDetail",
+      "infoPrintTask",
+      "infoExamPrintPlan"
+    ]),
+    cannotAdd() {
+      return !this.unusedClassList.length;
+    },
+    IS_MODEL2() {
+      return this.infoExamTask.examModel === "MODEL2";
+    }
+  },
+  watch: {
+    "infoExamTask.courseCode": function(val, oldval) {
+      if (val !== oldval) this.initData();
+    },
+    "infoExamPrintPlan.backupCount": function(val, oldval) {
+      if (val !== oldval) this.planBackupCountChange();
+    }
+  },
+  mounted() {
+    this.getExtendFields();
+    this.getPrintHouses();
+
+    const curDate = getTimeDatestamp(Date.now());
+    const hour = 60 * 60 * 1000;
+    this.curCreateTime = [curDate + 8 * hour, curDate + 10 * hour];
+    this.createTime = [...this.curCreateTime];
+  },
+  methods: {
+    ...mapMutations("exam", ["updateTaskInfo"]),
+    async initData() {
+      if (this.IS_MODEL2) return;
+
+      this.modalForm = Object.assign(this.modalForm, {
+        paperNumber: this.infoExamTask.paperNumber,
+        courseName: this.infoExamTask.courseName,
+        courseCode: this.infoExamTask.courseCode
+      });
+
+      await this.getClassList();
+
+      const { examStartTime, examEndTime } = this.infoPrintTask;
+      if (examStartTime && examEndTime) {
+        this.createTime = [examStartTime, examEndTime];
+        this.createDate = getTimeDatestamp(examStartTime);
+        this.modalForm.examStartTime = this.createTime[0];
+        this.modalForm.examEndTime = this.createTime[1];
+      }
+
+      this.tableData = [];
+      this.unusedClassList = [];
+      this.buildTableData();
+      this.updatePackageInfos();
+      this.updeteData();
+    },
+    planBackupCountChange() {
+      this.tableData.forEach(item => {
+        item.backupCount = this.infoExamPrintPlan.backupCount || 0;
+      });
+      this.updatePackageInfos();
+    },
+    checkTime() {
+      if (!this.modalForm.examStartTime || !this.modalForm.examEndTime) {
+        this.$message.error("请选择考试时间!");
+        return false;
+      }
+
+      if (this.modalForm.examStartTime >= this.modalForm.examEndTime) {
+        this.$message.error("考试开始时间必须小于考试结束时间!");
+        return false;
+      }
+      return true;
+    },
+    checkData() {
+      if (!this.checkTime()) return Promise.reject();
+
+      if (this.IS_MODEL2) {
+        if (!this.modalForm.printCount) {
+          this.$message.error("请输入印刷分数!");
+          return Promise.reject();
+        }
+        if (!this.modalForm.printHouseId) {
+          this.$message.error("请选择印刷室!");
+          return Promise.reject();
+        }
+        return Promise.resolve(true);
+      }
+
+      if (!this.tableData.length) {
+        this.$message.error("请添加考试对象!");
+        return Promise.reject();
+      }
+
+      let errorMsg = [];
+      this.tableData.forEach(row => {
+        let errorFields = [];
+        this.extendFields.forEach(field => {
+          if (!row.extends[field.code]) {
+            errorFields.push(field.name);
+          }
+        });
+
+        if (!row.printHouseId) {
+          errorFields.push("印刷室");
+        }
+        if (errorFields.length) {
+          errorMsg.push(
+            `考试对象${row.className}中,${errorFields.join("、")}必须填写`
+          );
+        }
+      });
+      if (errorMsg.length) {
+        this.$message.error(errorMsg.join("。"));
+        return Promise.reject();
+      }
+      return Promise.resolve(true);
+    },
+    updeteData() {
+      const tableList = this.tableData.map(row => {
+        let nrow = { ...row };
+        let extendFields = this.extendFields.map(field => {
+          let info = { ...field };
+          info.value = row.extends[field.code];
+          return info;
+        });
+
+        nrow.extendFields = JSON.stringify(extendFields);
+        nrow.examStartTime = this.modalForm.examStartTime;
+        nrow.examEndTime = this.modalForm.examEndTime;
+        return nrow;
+      });
+
+      this.updateTaskInfo({
+        infoPrintTask: {
+          ...this.modalForm,
+          list: tableList
+        }
+      });
+    },
+    updateData() {
+      this.$emit("data-change", this.getData());
+    },
+    buildTableData() {
+      this.tableData = this.classList.map(clazz => {
+        let modalFormData = { ...this.modalForm };
+        delete modalFormData.printHouseId;
+        let data = {
+          examPlace: "",
+          examRoom: "",
+          classId: clazz.id,
+          className: clazz.name,
+          studentCount: clazz.studentCount,
+          printHouseId: clazz.printHouseId,
+          printHouseName: clazz.printHouseName,
+          extendFields: "",
+          backupCount: this.infoExamPrintPlan.backupCount,
+          ...modalFormData
+        };
+        let extendFieldModal = {};
+        this.extendFields.forEach(field => {
+          extendFieldModal[field.code] = "";
+        });
+        data.extends = extendFieldModal;
+        return data;
+      });
+    },
+    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 = calcSum(
+        this.tableData.map(item => item.backupCount || 0)
+      );
+      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 = [];
+      if (!this.infoExamTask.courseCode) return;
+
+      const data = await listTaskApplyClass({
+        courseCode: this.infoExamTask.courseCode
+      });
+      if (!data) return;
+
+      this.classList = data.map(item => {
+        return {
+          id: item.classId,
+          name: item.className,
+          studentCount: item.studentCount,
+          printHouseId: item.printHouseId,
+          printHouseName: item.printHouseName
+        };
+      });
+    },
+    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;
+    },
+    backupCountChange() {
+      this.updatePackageInfos();
+    },
+    printHouseChange(row) {
+      const curHouse = this.printHouses.find(
+        item => item.printHouseId === row.printHouseId
+      );
+      if (curHouse) {
+        row.printHouseName = curHouse.printHouseName;
+      }
+    },
+    toAdd() {
+      if (!this.checkTime()) return;
+
+      this.curRow = {
+        examPlace: "",
+        examRoom: "",
+        classId: "",
+        className: "",
+        studentCount: null,
+        printHouseId: null,
+        printHouseName: null,
+        extendFields: "",
+        backupCount: this.infoExamPrintPlan.backupCount,
+        ...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>

+ 226 - 225
src/modules/exam/components/createExamAndPrintTask/ModifyPrintTask.vue

@@ -1,225 +1,226 @@
-<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.printHouseId"
-            :value="room.printHouseId"
-            :label="room.printHouseName"
-          ></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.printHouseId === val);
-      if (curHouse) {
-        this.modalForm.printHouseName = curHouse.printHouseName;
-      }
-    },
-    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>
+<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="请选择"
+          filterable
+          @change="printHouseChange"
+        >
+          <el-option
+            v-for="room in printHouses"
+            :key="room.printHouseId"
+            :value="room.printHouseId"
+            :label="room.printHouseName"
+          ></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.printHouseId === val);
+      if (curHouse) {
+        this.modalForm.printHouseName = curHouse.printHouseName;
+      }
+    },
+    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>

+ 289 - 284
src/modules/exam/views/DataTaskManage.vue

@@ -1,284 +1,289 @@
-<template>
-  <div class="data-task-manage">
-    <div class="part-box part-box-filter part-box-flex">
-      <el-form ref="FilterForm" label-position="left" label-width="55px" inline>
-        <template v-if="checkPrivilege('condition', 'condition')">
-          <el-form-item label="状态:">
-            <el-select
-              v-model="filter.status"
-              placeholder="状态"
-              clearable
-              style="width: 120px;"
-            >
-              <el-option
-                v-for="(val, key) in DATA_TASK_STATUS"
-                :key="key"
-                :value="key"
-                :label="val"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="类别:">
-            <el-select v-model="filter.type" placeholder="类别" clearable>
-              <el-option
-                v-for="(val, key) in DATA_TASK_TYPE"
-                :key="key"
-                :value="key"
-                :label="val"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="数据结果:" label-width="85px">
-            <el-select
-              v-model="filter.result"
-              placeholder="数据结果"
-              clearable
-              style="width: 120px;"
-            >
-              <el-option
-                v-for="(val, key) in DATA_TASK_RESULT"
-                :key="key"
-                :value="key"
-                :label="val"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </template>
-        <el-form-item>
-          <el-button
-            v-if="checkPrivilege('button', 'select')"
-            type="primary"
-            @click="toPage(1)"
-            >查询</el-button
-          >
-        </el-form-item>
-      </el-form>
-      <div class="part-box-action">
-        <!-- <el-button
-          type="danger"
-          icon="el-icon-delete"
-          @click="toRemove('DELETE')"
-          >批量删除</el-button
-        >
-        <el-button
-          type="danger"
-          icon="el-icon-delete-solid"
-          @click="toRemove('CLEAR')"
-          >清空</el-button
-        > -->
-      </div>
-    </div>
-    <div class="part-box part-box-pad">
-      <el-table
-        ref="TableList"
-        :data="tasks"
-        @selection-change="handleSelectionChange"
-      >
-        <el-table-column
-          type="index"
-          label="序号"
-          width="50"
-          :index="indexMethod"
-        ></el-table-column>
-        <el-table-column prop="printPlanName" label="项目"></el-table-column>
-        <el-table-column prop="type" label="类别"> </el-table-column>
-        <el-table-column prop="status" label="状态"> </el-table-column>
-        <el-table-column prop="result" label="结果"> </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="180">
-          <span slot-scope="scope">{{
-            scope.row.createTime | timestampFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column prop="createName" label="创建人"></el-table-column>
-        <el-table-column
-          class-name="action-column"
-          label="操作"
-          width="160px"
-          align="center"
-        >
-          <template slot-scope="scope">
-            <el-button
-              v-if="scope.row.hasReportFile && checkPrivilege('link', 'export')"
-              class="btn-primary"
-              type="text"
-              :disabled="loading"
-              @click="toDonwloadLog(scope.row)"
-              >导出日志</el-button
-            >
-            <el-button
-              v-if="
-                scope.row.resetCreatePdf && checkPrivilege('link', 'createPdf')
-              "
-              class="btn-primary"
-              type="text"
-              :disabled="loading"
-              @click="toResetCreatePdf(scope.row)"
-              >重新生成PDF</el-button
-            >
-            <el-button
-              v-if="
-                !scope.row.resetCreatePdf && checkPrivilege('link', 'download')
-              "
-              class="btn-primary"
-              type="text"
-              :disabled="loading"
-              @click="toDonwloadFile(scope.row)"
-              >下载文件</el-button
-            >
-          </template>
-        </el-table-column>
-      </el-table>
-      <div class="part-page">
-        <el-pagination
-          background
-          layout="total,prev, pager, next"
-          :current-page="current"
-          :total="total"
-          :page-size="size"
-          @current-change="toPage"
-        >
-        </el-pagination>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import {
-  DATA_TASK_STATUS,
-  DATA_TASK_TYPE,
-  DATA_TASK_RESULT
-} from "@/constants/enumerate";
-import { dataTaskList, removeDataTask, taskResetPdf } from "../api";
-import { attachmentDownload } from "../../login/api";
-import { downloadByUrl } from "@/plugins/download";
-
-export default {
-  name: "data-task-manage",
-  data() {
-    return {
-      filter: {
-        type: "",
-        status: "",
-        result: ""
-      },
-      current: 1,
-      size: this.GLOBAL.pageSize,
-      total: 0,
-      DATA_TASK_STATUS,
-      DATA_TASK_TYPE,
-      DATA_TASK_RESULT,
-      tasks: [],
-      curTask: {},
-      multipleSelection: [],
-      loading: false
-    };
-  },
-  mounted() {
-    this.toPage(1);
-  },
-  methods: {
-    async getList() {
-      if (!this.checkPrivilege("list", "list")) return;
-
-      const datas = {
-        ...this.filter,
-        pageNumber: this.current,
-        pageSize: this.size
-      };
-      const data = await dataTaskList(datas);
-      this.tasks = data.records;
-      this.total = data.total;
-    },
-    toPage(page) {
-      this.current = page;
-      this.getList();
-      this.multipleSelection = [];
-    },
-    handleSelectionChange(val) {
-      this.multipleSelection = val.map(item => item.id);
-      console.log(this.multipleSelection);
-    },
-    toRemove(type) {
-      let tips = "";
-      if (type === "CLEAR") {
-        tips = "确定要清空所有任务吗?";
-      } else {
-        tips = "确定要删除选中的任务吗?";
-        if (!this.multipleSelection.length) {
-          this.$message.error("请选择要删除的记录!");
-          return;
-        }
-      }
-
-      this.$confirm(tips, "提示", {
-        type: "warning"
-      })
-        .then(async () => {
-          const data = await removeDataTask({
-            ids: type === "CLEAR" ? null : this.multipleSelection,
-            type: type === "CLEAR" ? "ALL" : null
-          }).catch(() => {});
-          if (!data) return;
-          this.$message.success("操作成功!");
-          this.deletePageLastItem();
-        })
-        .catch(() => {});
-    },
-    async toDonwloadLog(row) {
-      if (this.loading) return;
-
-      this.loading = true;
-      const res = await attachmentDownload({
-        id: row.id,
-        type: "TASK_REPORT"
-      }).catch(() => {});
-      this.loading = false;
-
-      if (!res) {
-        this.$message.error("文件下载失败,请重新尝试!");
-        return;
-      }
-
-      const url = res.url;
-      if (url.endsWith(".txt")) {
-        window.open(url);
-        return;
-      }
-
-      downloadByUrl(url);
-      this.$message.success("文件下载成功!");
-    },
-    async toDonwloadFile(row) {
-      if (this.loading) return;
-
-      this.loading = true;
-      const type = row.hasResultFile ? "RESULT" : "IMPORT_FILE";
-      const res = await attachmentDownload({
-        id: row.id,
-        type
-      }).catch(() => {});
-      this.loading = false;
-
-      if (!res) {
-        this.$message.error("文件下载失败,请重新尝试!");
-        return;
-      }
-
-      downloadByUrl(res.url);
-      this.$message.success("文件下载成功!");
-    },
-    async toResetCreatePdf(row) {
-      if (this.loading) return;
-
-      this.loading = true;
-      const res = await taskResetPdf(row.id).catch(() => {});
-      this.loading = false;
-      if (!res) return;
-
-      this.$message.success("操作成功!");
-      this.getList();
-    }
-  }
-};
-</script>
+<template>
+  <div class="data-task-manage">
+    <div class="part-box part-box-filter part-box-flex">
+      <el-form ref="FilterForm" label-position="left" label-width="55px" inline>
+        <template v-if="checkPrivilege('condition', 'condition')">
+          <el-form-item label="状态:">
+            <el-select
+              v-model="filter.status"
+              placeholder="状态"
+              clearable
+              style="width: 120px;"
+            >
+              <el-option
+                v-for="(val, key) in DATA_TASK_STATUS"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="类别:">
+            <el-select
+              v-model="filter.type"
+              placeholder="类别"
+              clearable
+              filterable
+            >
+              <el-option
+                v-for="(val, key) in DATA_TASK_TYPE"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="数据结果:" label-width="85px">
+            <el-select
+              v-model="filter.result"
+              placeholder="数据结果"
+              clearable
+              style="width: 120px;"
+            >
+              <el-option
+                v-for="(val, key) in DATA_TASK_RESULT"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+        <el-form-item>
+          <el-button
+            v-if="checkPrivilege('button', 'select')"
+            type="primary"
+            @click="toPage(1)"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+      <div class="part-box-action">
+        <!-- <el-button
+          type="danger"
+          icon="el-icon-delete"
+          @click="toRemove('DELETE')"
+          >批量删除</el-button
+        >
+        <el-button
+          type="danger"
+          icon="el-icon-delete-solid"
+          @click="toRemove('CLEAR')"
+          >清空</el-button
+        > -->
+      </div>
+    </div>
+    <div class="part-box part-box-pad">
+      <el-table
+        ref="TableList"
+        :data="tasks"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column
+          type="index"
+          label="序号"
+          width="50"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column prop="printPlanName" label="项目"></el-table-column>
+        <el-table-column prop="type" label="类别"> </el-table-column>
+        <el-table-column prop="status" label="状态"> </el-table-column>
+        <el-table-column prop="result" label="结果"> </el-table-column>
+        <el-table-column prop="createTime" label="创建时间" width="180">
+          <span slot-scope="scope">{{
+            scope.row.createTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column prop="createName" label="创建人"></el-table-column>
+        <el-table-column
+          class-name="action-column"
+          label="操作"
+          width="160px"
+          align="center"
+        >
+          <template slot-scope="scope">
+            <el-button
+              v-if="scope.row.hasReportFile && checkPrivilege('link', 'export')"
+              class="btn-primary"
+              type="text"
+              :disabled="loading"
+              @click="toDonwloadLog(scope.row)"
+              >导出日志</el-button
+            >
+            <el-button
+              v-if="
+                scope.row.resetCreatePdf && checkPrivilege('link', 'createPdf')
+              "
+              class="btn-primary"
+              type="text"
+              :disabled="loading"
+              @click="toResetCreatePdf(scope.row)"
+              >重新生成PDF</el-button
+            >
+            <el-button
+              v-if="
+                !scope.row.resetCreatePdf && checkPrivilege('link', 'download')
+              "
+              class="btn-primary"
+              type="text"
+              :disabled="loading"
+              @click="toDonwloadFile(scope.row)"
+              >下载文件</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="part-page">
+        <el-pagination
+          background
+          layout="total,prev, pager, next"
+          :current-page="current"
+          :total="total"
+          :page-size="size"
+          @current-change="toPage"
+        >
+        </el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {
+  DATA_TASK_STATUS,
+  DATA_TASK_TYPE,
+  DATA_TASK_RESULT
+} from "@/constants/enumerate";
+import { dataTaskList, removeDataTask, taskResetPdf } from "../api";
+import { attachmentDownload } from "../../login/api";
+import { downloadByUrl } from "@/plugins/download";
+
+export default {
+  name: "data-task-manage",
+  data() {
+    return {
+      filter: {
+        type: "",
+        status: "",
+        result: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      DATA_TASK_STATUS,
+      DATA_TASK_TYPE,
+      DATA_TASK_RESULT,
+      tasks: [],
+      curTask: {},
+      multipleSelection: [],
+      loading: false
+    };
+  },
+  mounted() {
+    this.toPage(1);
+  },
+  methods: {
+    async getList() {
+      if (!this.checkPrivilege("list", "list")) return;
+
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      const data = await dataTaskList(datas);
+      this.tasks = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+      this.multipleSelection = [];
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val.map(item => item.id);
+      console.log(this.multipleSelection);
+    },
+    toRemove(type) {
+      let tips = "";
+      if (type === "CLEAR") {
+        tips = "确定要清空所有任务吗?";
+      } else {
+        tips = "确定要删除选中的任务吗?";
+        if (!this.multipleSelection.length) {
+          this.$message.error("请选择要删除的记录!");
+          return;
+        }
+      }
+
+      this.$confirm(tips, "提示", {
+        type: "warning"
+      })
+        .then(async () => {
+          const data = await removeDataTask({
+            ids: type === "CLEAR" ? null : this.multipleSelection,
+            type: type === "CLEAR" ? "ALL" : null
+          }).catch(() => {});
+          if (!data) return;
+          this.$message.success("操作成功!");
+          this.deletePageLastItem();
+        })
+        .catch(() => {});
+    },
+    async toDonwloadLog(row) {
+      if (this.loading) return;
+
+      this.loading = true;
+      const res = await attachmentDownload({
+        id: row.id,
+        type: "TASK_REPORT"
+      }).catch(() => {});
+      this.loading = false;
+
+      if (!res) {
+        this.$message.error("文件下载失败,请重新尝试!");
+        return;
+      }
+
+      const url = res.url;
+      if (url.endsWith(".txt")) {
+        window.open(url);
+        return;
+      }
+
+      downloadByUrl(url);
+      this.$message.success("文件下载成功!");
+    },
+    async toDonwloadFile(row) {
+      if (this.loading) return;
+
+      this.loading = true;
+      const type = row.hasResultFile ? "RESULT" : "IMPORT_FILE";
+      const res = await attachmentDownload({
+        id: row.id,
+        type
+      }).catch(() => {});
+      this.loading = false;
+
+      if (!res) {
+        this.$message.error("文件下载失败,请重新尝试!");
+        return;
+      }
+
+      downloadByUrl(res.url);
+      this.$message.success("文件下载成功!");
+    },
+    async toResetCreatePdf(row) {
+      if (this.loading) return;
+
+      this.loading = true;
+      const res = await taskResetPdf(row.id).catch(() => {});
+      this.loading = false;
+      if (!res) return;
+
+      this.$message.success("操作成功!");
+      this.getList();
+    }
+  }
+};
+</script>

+ 298 - 297
src/modules/print/components/ModifyPlanPaper.vue

@@ -1,297 +1,298 @@
-<template>
-  <el-dialog
-    class="modify-plan-paper"
-    :visible.sync="modalIsShow"
-    title="关联试卷"
-    top="10px"
-    width="900px"
-    :close-on-click-modal="false"
-    :close-on-press-escape="false"
-    append-to-body
-    @open="visibleChange"
-  >
-    <div class="part-box part-box-pad part-box-border">
-      <el-form
-        class="form-info"
-        ref="modalFormComp"
-        label-width="130px"
-        :rules="rules"
-        :model="modalForm"
-      >
-        <el-row>
-          <el-col :span="12">
-            <el-form-item label="课程(代码):">
-              <span
-                >{{ instance.courseName }}({{ instance.courseCode }})</span
-              >
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <!-- <el-form-item label="适用专业(方向):">
-              <span>{{ instance.specialty | defaultFieldFilter }}</span>
-            </el-form-item> -->
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="12">
-            <el-form-item label="题卡规则:">
-              <span>{{ instance.cardRuleName }}</span>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="命题老师:">
-              <span>{{ instance.userName }}</span>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row class="mb-2">
-          <el-col :span="12">
-            <el-form-item prop="paperNumber" label="试卷编号:">
-              <el-select
-                v-model="modalForm.paperNumber"
-                placeholder="请选择"
-                @change="paperChange"
-              >
-                <el-option
-                  v-for="item in papers"
-                  :key="item.id"
-                  :value="item.paperNumber"
-                  :label="item.paperNumber"
-                ></el-option>
-              </el-select>
-              <!-- <span>{{ instance.paperNumber }}</span> -->
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item prop="relatePaperType" label="关联类型:">
-              <el-select
-                v-model="modalForm.relatePaperType"
-                placeholder="请选择"
-                multiple
-                :multiple-limit="drawCount"
-              >
-                <el-option
-                  v-for="item in paperTypes"
-                  :key="item"
-                  :value="item"
-                  :label="item"
-                ></el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="12">
-            <el-form-item label="已曝光类型:">
-              <span>{{ instance.exposedPaperType | defaultFieldFilter }}</span>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="未曝光类型:">
-              <span>{{
-                instance.unexposedPaperType | defaultFieldFilter
-              }}</span>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
-    </div>
-
-    <h4 class="part-box-tips">试卷内容:</h4>
-    <p class="tips-info tips-dark mb-2">
-      提示:多卷型试卷由于会绑定一个答题卡模板,因此试卷结构必须相同。多卷型试卷之间客观题要求试题内容相同,可允许大题内的小题题序不同。
-    </p>
-    <table class="table">
-      <tr>
-        <th>卷型</th>
-        <th>试卷</th>
-        <th>答题卡</th>
-      </tr>
-      <tr v-for="(attachment, index) in paperAttachments" :key="index">
-        <td>{{ attachment.name }}卷</td>
-        <td>
-          <el-button
-            type="text"
-            class="btn-primary"
-            @click="downloadPaper(attachment)"
-            title="点击查看试卷"
-          >
-            <i
-              class="icon icon-download mr-1"
-              v-if="attachment.attachmentId"
-            ></i
-            >{{ attachment.filename }}
-          </el-button>
-        </td>
-        <td :rowspan="paperAttachments.length" v-if="index === 0">
-          <el-button type="text" class="btn-primary" @click="toViewCard"
-            >查看答题卡</el-button
-          >
-        </td>
-      </tr>
-    </table>
-
-    <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 { updatePlanLinkPaper, linkPaperNumberList } from "../api";
-import { taskApplyDetail } from "../../exam/api";
-import { attachmentPreview } from "../../login/api";
-
-const initModalForm = {
-  id: null,
-  paperNumber: "",
-  relatePaperType: []
-};
-
-export default {
-  name: "modify-plan-paper",
-  props: {
-    instance: {
-      type: Object,
-      default() {
-        return {};
-      }
-    }
-  },
-  data() {
-    return {
-      modalIsShow: false,
-      isSubmit: false,
-      modalForm: { ...initModalForm },
-      papers: [],
-      paperTypes: [],
-      paperAttachments: [],
-      drawCount: 0,
-      cardId: "",
-      rules: {
-        paperNumber: [
-          {
-            required: true,
-            message: "请选择试卷编号",
-            trigger: "change"
-          }
-        ],
-        relatePaperType: [
-          {
-            required: true,
-            validator: (rule, value, callback) => {
-              if (
-                value.length < this.drawCount ||
-                value.length > this.drawCount
-              ) {
-                return callback(
-                  new Error(`需要选择${this.drawCount}套关联试卷`)
-                );
-              }
-              callback();
-            },
-            trigger: "change"
-          }
-        ]
-      }
-    };
-  },
-  methods: {
-    async getAttachments() {
-      const data = await taskApplyDetail(this.instance.id);
-      this.paperAttachments = data.paperAttachmentIds
-        ? JSON.parse(data.paperAttachmentIds)
-        : [];
-      this.cardId = data.cardId;
-      this.drawCount = data.drawCount;
-    },
-    // async getPaperTypes() {
-    //   const papers = await linkPaperNumberList({
-    //     examTaskId: this.instance.id,
-    //     printPlanId: this.instance.printPlanId
-    //   });
-
-    //   if (papers && papers[0]) this.paperTypes = papers[0].paperTypes;
-    // },
-    async getPapers() {
-      this.papers = await linkPaperNumberList({
-        examTaskId: this.instance.id,
-        printPlanId: this.instance.printPlanId,
-        courseCode: this.instance.courseCode
-      });
-
-      const paper = this.papers.find(
-        item => item.paperNumber === this.modalForm.paperNumber
-      );
-      if (paper) this.paperTypes = paper.paperTypes;
-    },
-    visibleChange() {
-      this.modalForm = this.$objAssign(initModalForm, this.instance);
-      this.modalForm.relatePaperType = this.instance.relatePaperType
-        ? this.instance.relatePaperType.split(",")
-        : [];
-
-      this.getPapers();
-      // this.getPaperTypes();
-      this.getAttachments();
-
-      this.$nextTick(() => {
-        this.$refs.modalFormComp.clearValidate();
-      });
-    },
-    cancel() {
-      this.modalIsShow = false;
-    },
-    open() {
-      this.modalIsShow = true;
-    },
-    paperChange() {
-      this.modalForm.relatePaperType = [];
-      this.paperTypes = [];
-      const paper = this.papers.find(
-        item => item.paperNumber === this.modalForm.paperNumber
-      );
-      if (paper) this.paperTypes = paper.paperTypes;
-    },
-    async downloadPaper(attachment) {
-      const data = await attachmentPreview(attachment.attachmentId);
-      window.open(data.url);
-    },
-    toViewCard() {
-      window.open(
-        this.getRouterPath({
-          name: "CardPreview",
-          params: {
-            cardId: this.cardId,
-            viewType: "view"
-          }
-        })
-      );
-    },
-    async submit() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
-      if (!valid) return;
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      const datas = {
-        examTaskId: this.modalForm.id,
-        relatePaperType: this.modalForm.relatePaperType.join(),
-        paperNumber: this.modalForm.paperNumber,
-        printPlanId: this.instance.printPlanId,
-        examDetailCourseIds: this.instance.examDetailCourseIds
-      };
-      const data = await updatePlanLinkPaper(datas).catch(() => {});
-      this.isSubmit = false;
-      if (!data) return;
-
-      this.$message.success("保存成功!");
-      this.$emit("modified");
-      this.cancel();
-    }
-  }
-};
-</script>
+<template>
+  <el-dialog
+    class="modify-plan-paper"
+    :visible.sync="modalIsShow"
+    title="关联试卷"
+    top="10px"
+    width="900px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <div class="part-box part-box-pad part-box-border">
+      <el-form
+        class="form-info"
+        ref="modalFormComp"
+        label-width="130px"
+        :rules="rules"
+        :model="modalForm"
+      >
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="课程(代码):">
+              <span
+                >{{ instance.courseName }}({{ instance.courseCode }})</span
+              >
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <!-- <el-form-item label="适用专业(方向):">
+              <span>{{ instance.specialty | defaultFieldFilter }}</span>
+            </el-form-item> -->
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="题卡规则:">
+              <span>{{ instance.cardRuleName }}</span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="命题老师:">
+              <span>{{ instance.userName }}</span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row class="mb-2">
+          <el-col :span="12">
+            <el-form-item prop="paperNumber" label="试卷编号:">
+              <el-select
+                v-model="modalForm.paperNumber"
+                placeholder="请选择"
+                filterable
+                @change="paperChange"
+              >
+                <el-option
+                  v-for="item in papers"
+                  :key="item.id"
+                  :value="item.paperNumber"
+                  :label="item.paperNumber"
+                ></el-option>
+              </el-select>
+              <!-- <span>{{ instance.paperNumber }}</span> -->
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="relatePaperType" label="关联类型:">
+              <el-select
+                v-model="modalForm.relatePaperType"
+                placeholder="请选择"
+                multiple
+                :multiple-limit="drawCount"
+              >
+                <el-option
+                  v-for="item in paperTypes"
+                  :key="item"
+                  :value="item"
+                  :label="item"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="已曝光类型:">
+              <span>{{ instance.exposedPaperType | defaultFieldFilter }}</span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="未曝光类型:">
+              <span>{{
+                instance.unexposedPaperType | defaultFieldFilter
+              }}</span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </div>
+
+    <h4 class="part-box-tips">试卷内容:</h4>
+    <p class="tips-info tips-dark mb-2">
+      提示:多卷型试卷由于会绑定一个答题卡模板,因此试卷结构必须相同。多卷型试卷之间客观题要求试题内容相同,可允许大题内的小题题序不同。
+    </p>
+    <table class="table">
+      <tr>
+        <th>卷型</th>
+        <th>试卷</th>
+        <th>答题卡</th>
+      </tr>
+      <tr v-for="(attachment, index) in paperAttachments" :key="index">
+        <td>{{ attachment.name }}卷</td>
+        <td>
+          <el-button
+            type="text"
+            class="btn-primary"
+            @click="downloadPaper(attachment)"
+            title="点击查看试卷"
+          >
+            <i
+              class="icon icon-download mr-1"
+              v-if="attachment.attachmentId"
+            ></i
+            >{{ attachment.filename }}
+          </el-button>
+        </td>
+        <td :rowspan="paperAttachments.length" v-if="index === 0">
+          <el-button type="text" class="btn-primary" @click="toViewCard"
+            >查看答题卡</el-button
+          >
+        </td>
+      </tr>
+    </table>
+
+    <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 { updatePlanLinkPaper, linkPaperNumberList } from "../api";
+import { taskApplyDetail } from "../../exam/api";
+import { attachmentPreview } from "../../login/api";
+
+const initModalForm = {
+  id: null,
+  paperNumber: "",
+  relatePaperType: []
+};
+
+export default {
+  name: "modify-plan-paper",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      papers: [],
+      paperTypes: [],
+      paperAttachments: [],
+      drawCount: 0,
+      cardId: "",
+      rules: {
+        paperNumber: [
+          {
+            required: true,
+            message: "请选择试卷编号",
+            trigger: "change"
+          }
+        ],
+        relatePaperType: [
+          {
+            required: true,
+            validator: (rule, value, callback) => {
+              if (
+                value.length < this.drawCount ||
+                value.length > this.drawCount
+              ) {
+                return callback(
+                  new Error(`需要选择${this.drawCount}套关联试卷`)
+                );
+              }
+              callback();
+            },
+            trigger: "change"
+          }
+        ]
+      }
+    };
+  },
+  methods: {
+    async getAttachments() {
+      const data = await taskApplyDetail(this.instance.id);
+      this.paperAttachments = data.paperAttachmentIds
+        ? JSON.parse(data.paperAttachmentIds)
+        : [];
+      this.cardId = data.cardId;
+      this.drawCount = data.drawCount;
+    },
+    // async getPaperTypes() {
+    //   const papers = await linkPaperNumberList({
+    //     examTaskId: this.instance.id,
+    //     printPlanId: this.instance.printPlanId
+    //   });
+
+    //   if (papers && papers[0]) this.paperTypes = papers[0].paperTypes;
+    // },
+    async getPapers() {
+      this.papers = await linkPaperNumberList({
+        examTaskId: this.instance.id,
+        printPlanId: this.instance.printPlanId,
+        courseCode: this.instance.courseCode
+      });
+
+      const paper = this.papers.find(
+        item => item.paperNumber === this.modalForm.paperNumber
+      );
+      if (paper) this.paperTypes = paper.paperTypes;
+    },
+    visibleChange() {
+      this.modalForm = this.$objAssign(initModalForm, this.instance);
+      this.modalForm.relatePaperType = this.instance.relatePaperType
+        ? this.instance.relatePaperType.split(",")
+        : [];
+
+      this.getPapers();
+      // this.getPaperTypes();
+      this.getAttachments();
+
+      this.$nextTick(() => {
+        this.$refs.modalFormComp.clearValidate();
+      });
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    paperChange() {
+      this.modalForm.relatePaperType = [];
+      this.paperTypes = [];
+      const paper = this.papers.find(
+        item => item.paperNumber === this.modalForm.paperNumber
+      );
+      if (paper) this.paperTypes = paper.paperTypes;
+    },
+    async downloadPaper(attachment) {
+      const data = await attachmentPreview(attachment.attachmentId);
+      window.open(data.url);
+    },
+    toViewCard() {
+      window.open(
+        this.getRouterPath({
+          name: "CardPreview",
+          params: {
+            cardId: this.cardId,
+            viewType: "view"
+          }
+        })
+      );
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const datas = {
+        examTaskId: this.modalForm.id,
+        relatePaperType: this.modalForm.relatePaperType.join(),
+        paperNumber: this.modalForm.paperNumber,
+        printPlanId: this.instance.printPlanId,
+        examDetailCourseIds: this.instance.examDetailCourseIds
+      };
+      const data = await updatePlanLinkPaper(datas).catch(() => {});
+      this.isSubmit = false;
+      if (!data) return;
+
+      this.$message.success("保存成功!");
+      this.$emit("modified");
+      this.cancel();
+    }
+  }
+};
+</script>

+ 234 - 234
src/modules/stmms/views/SyncManage.vue

@@ -1,234 +1,234 @@
-<template>
-  <div class="sync-manage">
-    <div class="part-box part-box-filter part-box-flex">
-      <el-form ref="FilterForm" label-position="left" inline>
-        <template v-if="checkPrivilege('condition', 'condition')">
-          <el-form-item label="同步类型:">
-            <el-select v-model="filter.type" placeholder="同步类型" clearable>
-              <el-option
-                v-for="(val, key) in STMMS_SYNC_TYPE"
-                :key="key"
-                :value="key"
-                :label="val"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="同步状态:">
-            <el-select v-model="filter.status" placeholder="同步状态" clearable>
-              <el-option
-                v-for="(val, key) in STMMS_SYNC_STATUS"
-                :key="key"
-                :value="key"
-                :label="val"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="同步结果:">
-            <el-select
-              v-model="filter.result"
-              placeholder="同步结果"
-              clearable
-              style="width: 120px;"
-            >
-              <el-option
-                v-for="(val, key) in DATA_TASK_RESULT"
-                :key="key"
-                :value="key"
-                :label="val"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </template>
-        <el-form-item label-width="0px">
-          <el-button
-            v-if="checkPrivilege('button', 'select')"
-            type="primary"
-            @click="toPage(1)"
-            >查询</el-button
-          >
-        </el-form-item>
-      </el-form>
-    </div>
-
-    <div class="part-box part-box-pad">
-      <el-table ref="TableList" :data="dataList">
-        <el-table-column
-          type="index"
-          label="序号"
-          width="70"
-          :index="indexMethod"
-        ></el-table-column>
-        <el-table-column
-          prop="thirdRelateId"
-          label="云阅卷考试ID"
-          width="110"
-        ></el-table-column>
-        <el-table-column prop="objectId" label="同步对象ID"></el-table-column>
-        <el-table-column prop="type" label="类别"></el-table-column>
-        <el-table-column prop="status" label="状态" width="100">
-        </el-table-column>
-        <el-table-column prop="resultStr" label="结果" width="80">
-          <template slot-scope="scope">
-            <el-popover
-              v-if="scope.row.result === 'ERROR'"
-              placement="left-start"
-              popper-class="answer-popover"
-              width="400"
-              trigger="hover"
-            >
-              <p class="text-prewrap" v-html="scope.row.errorMessage"></p>
-              <span class="color-danger" slot="reference">{{
-                scope.row.resultStr
-              }}</span>
-            </el-popover>
-            <p v-else class="color-success">{{ scope.row.resultStr }}</p>
-          </template>
-        </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="180">
-          <span slot-scope="scope">{{
-            scope.row.createTime | timestampFilter
-          }}</span>
-        </el-table-column>
-        <el-table-column
-          prop="createName"
-          label="创建人"
-          width="140"
-        ></el-table-column>
-        <el-table-column class-name="action-column" label="操作" width="100">
-          <template slot-scope="scope">
-            <el-button
-              v-if="scope.row.hasReportFile && checkPrivilege('link', 'export')"
-              type="text"
-              class="btn-primary"
-              :disabled="loading"
-              @click="toDonwloadLog(scope.row)"
-              >导出日志</el-button
-            >
-            <el-button
-              v-if="scope.row.result === 'ERROR'"
-              type="text"
-              class="btn-primary"
-              :disabled="loading"
-              @click="toResync(scope.row)"
-              >重新同步</el-button
-            >
-          </template>
-        </el-table-column>
-      </el-table>
-      <div class="part-page">
-        <el-pagination
-          background
-          layout="total,prev, pager, next"
-          :current-page="current"
-          :total="total"
-          :page-size="size"
-          @current-change="toPage"
-        >
-        </el-pagination>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import { DATA_TASK_RESULT } from "@/constants/enumerate";
-import { syncResultListPage, syncResync } from "../api";
-import { attachmentDownload } from "../../login/api";
-import { enumsByType } from "../../base/api";
-import { downloadByUrl } from "@/plugins/download";
-
-export default {
-  name: "sync-manage",
-  data() {
-    return {
-      STMMS_SYNC_TYPE: {},
-      STMMS_SYNC_STATUS: {},
-      DATA_TASK_RESULT,
-      filter: {
-        result: "",
-        type: "",
-        status: ""
-      },
-      current: 1,
-      size: this.GLOBAL.pageSize,
-      total: 0,
-      dataList: [],
-      loading: false
-    };
-  },
-  created() {
-    this.getSyncTypes();
-    this.getSyncStatus();
-    this.getList();
-  },
-  methods: {
-    async getSyncTypes() {
-      const res = await enumsByType("PUSH_TYPE_ENUM");
-      const data = res || [];
-      this.STMMS_SYNC_TYPE = {};
-      data.forEach(item => {
-        this.STMMS_SYNC_TYPE[item.name] = item.desc;
-      });
-    },
-    async getSyncStatus() {
-      const res = await enumsByType("TB_TASK_STATUS");
-      const data = res || [];
-      this.STMMS_SYNC_STATUS = {};
-      data.forEach(item => {
-        this.STMMS_SYNC_STATUS[item.name] = item.desc;
-      });
-    },
-    async getList() {
-      if (!this.checkPrivilege("list", "list")) return;
-
-      const datas = {
-        ...this.filter,
-        pageNumber: this.current,
-        pageSize: this.size
-      };
-      const data = await syncResultListPage(datas);
-      this.dataList = data.records;
-      this.total = data.total;
-    },
-    toPage(page) {
-      this.current = page;
-      this.getList();
-    },
-    async toDonwloadLog(row) {
-      if (this.loading) return;
-
-      this.loading = true;
-      const res = await attachmentDownload({
-        id: row.id,
-        type: "SYNC_REPORT"
-      }).catch(() => {});
-      this.loading = false;
-
-      if (!res) {
-        this.$message.error("文件下载失败,请重新尝试!");
-        return;
-      }
-
-      const url = res.url;
-      if (url.endsWith(".txt")) {
-        this.loading = false;
-        window.open(url);
-        return;
-      }
-
-      downloadByUrl(url);
-      this.$message.success("文件下载成功!");
-    },
-    async toResync(row) {
-      if (this.loading) return;
-
-      this.loading = true;
-      const res = await syncResync(row.id).catch(() => false);
-      this.loading = false;
-
-      if (!res) return;
-      this.$message.success("操作成功!");
-    }
-  }
-};
-</script>
+<template>
+  <div class="sync-manage">
+    <div class="part-box part-box-filter part-box-flex">
+      <el-form ref="FilterForm" label-position="left" inline>
+        <template v-if="checkPrivilege('condition', 'condition')">
+          <el-form-item label="同步类型:">
+            <el-select v-model="filter.type" placeholder="同步类型" clearable>
+              <el-option
+                v-for="(val, key) in STMMS_SYNC_TYPE"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="同步状态:">
+            <el-select v-model="filter.status" placeholder="同步状态" clearable>
+              <el-option
+                v-for="(val, key) in STMMS_SYNC_STATUS"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="同步结果:">
+            <el-select
+              v-model="filter.result"
+              placeholder="同步结果"
+              clearable
+              style="width: 120px;"
+            >
+              <el-option
+                v-for="(val, key) in DATA_TASK_RESULT"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+        <el-form-item label-width="0px">
+          <el-button
+            v-if="checkPrivilege('button', 'select')"
+            type="primary"
+            @click="toPage(1)"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <div class="part-box part-box-pad">
+      <el-table ref="TableList" :data="dataList">
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column
+          prop="thirdRelateId"
+          label="云阅卷考试ID"
+          width="110"
+        ></el-table-column>
+        <el-table-column prop="objectId" label="同步对象ID"></el-table-column>
+        <el-table-column prop="type" label="类别"></el-table-column>
+        <el-table-column prop="status" label="状态" width="100">
+        </el-table-column>
+        <el-table-column prop="resultStr" label="结果" width="80">
+          <template slot-scope="scope">
+            <el-popover
+              v-if="scope.row.result === 'ERROR'"
+              placement="left-start"
+              popper-class="answer-popover"
+              width="400"
+              trigger="hover"
+            >
+              <p class="text-prewrap" v-html="scope.row.errorMessage"></p>
+              <span class="color-danger" slot="reference">{{
+                scope.row.resultStr
+              }}</span>
+            </el-popover>
+            <p v-else class="color-success">{{ scope.row.resultStr }}</p>
+          </template>
+        </el-table-column>
+        <el-table-column prop="createTime" label="创建时间" width="180">
+          <span slot-scope="scope">{{
+            scope.row.createTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column
+          prop="createName"
+          label="创建人"
+          width="140"
+        ></el-table-column>
+        <el-table-column class-name="action-column" label="操作" width="100">
+          <template slot-scope="scope">
+            <el-button
+              v-if="scope.row.hasReportFile && checkPrivilege('link', 'export')"
+              type="text"
+              class="btn-primary"
+              :disabled="loading"
+              @click="toDonwloadLog(scope.row)"
+              >导出日志</el-button
+            >
+            <el-button
+              v-if="scope.row.result === 'ERROR'"
+              type="text"
+              class="btn-primary"
+              :disabled="loading"
+              @click="toResync(scope.row)"
+              >重新同步</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="part-page">
+        <el-pagination
+          background
+          layout="total,prev, pager, next"
+          :current-page="current"
+          :total="total"
+          :page-size="size"
+          @current-change="toPage"
+        >
+        </el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { DATA_TASK_RESULT } from "@/constants/enumerate";
+import { syncResultListPage, syncResync } from "../api";
+import { attachmentDownload } from "../../login/api";
+import { enumsByType } from "../../base/api";
+import { downloadByUrl } from "@/plugins/download";
+
+export default {
+  name: "sync-manage",
+  data() {
+    return {
+      STMMS_SYNC_TYPE: {},
+      STMMS_SYNC_STATUS: {},
+      DATA_TASK_RESULT,
+      filter: {
+        result: "",
+        type: "",
+        status: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: [],
+      loading: false
+    };
+  },
+  created() {
+    this.getSyncTypes();
+    this.getSyncStatus();
+    this.getList();
+  },
+  methods: {
+    async getSyncTypes() {
+      const res = await enumsByType("PUSH_TYPE_ENUM");
+      const data = res || [];
+      this.STMMS_SYNC_TYPE = {};
+      data.forEach(item => {
+        this.STMMS_SYNC_TYPE[item.name] = item.desc;
+      });
+    },
+    async getSyncStatus() {
+      const res = await enumsByType("TB_TASK_STATUS");
+      const data = res || [];
+      this.STMMS_SYNC_STATUS = {};
+      data.forEach(item => {
+        this.STMMS_SYNC_STATUS[item.name] = item.desc;
+      });
+    },
+    async getList() {
+      if (!this.checkPrivilege("list", "list")) return;
+
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      const data = await syncResultListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    async toDonwloadLog(row) {
+      if (this.loading) return;
+
+      this.loading = true;
+      const res = await attachmentDownload({
+        id: row.id,
+        type: "SYNC_REPORT"
+      }).catch(() => {});
+      this.loading = false;
+
+      if (!res) {
+        this.$message.error("文件下载失败,请重新尝试!");
+        return;
+      }
+
+      const url = res.url;
+      if (url.endsWith(".txt")) {
+        this.loading = false;
+        window.open(url);
+        return;
+      }
+
+      downloadByUrl(url);
+      this.$message.success("文件下载成功!");
+    },
+    async toResync(row) {
+      if (this.loading) return;
+
+      this.loading = true;
+      const res = await syncResync(row.id).catch(() => false);
+      this.loading = false;
+
+      if (!res) return;
+      this.$message.success("操作成功!");
+    }
+  }
+};
+</script>