Selaa lähdekoodia

印刷管理完成

zhangjie 4 vuotta sitten
vanhempi
commit
2395a0831d
43 muutettua tiedostoa jossa 2618 lisäystä ja 305 poistoa
  1. 3 0
      src/assets/styles/base.scss
  2. 19 1
      src/assets/styles/pages.scss
  3. 10 4
      src/components/UploadButton.vue
  4. 0 4
      src/components/base/CardRuleSelect.vue
  5. 0 4
      src/components/base/CourseSelect.vue
  6. 1 5
      src/components/base/PaperNumberSelect.vue
  7. 66 0
      src/components/base/PlaceSelect.vue
  8. 66 0
      src/components/base/PrintPlanSelect.vue
  9. 0 2
      src/components/base/QuestionTeacherSelect.vue
  10. 66 0
      src/components/base/RoomSelect.vue
  11. 58 33
      src/constants/enumerate.js
  12. 5 5
      src/constants/navs.js
  13. 23 9
      src/modules/base/components/ModifyCardRule.vue
  14. 32 3
      src/modules/base/components/ModifyRole.vue
  15. 42 42
      src/modules/base/components/ModifyTemplate.vue
  16. 9 4
      src/modules/base/components/ModifyUser.vue
  17. 5 1
      src/modules/base/components/SelectOrgs.vue
  18. 4 4
      src/modules/base/router.js
  19. 7 7
      src/modules/base/views/CardRuleManage.vue
  20. 7 5
      src/modules/base/views/CourseManage.vue
  21. 14 9
      src/modules/base/views/MenuManage.vue
  22. 10 8
      src/modules/base/views/OrganizationManage.vue
  23. 12 5
      src/modules/base/views/RoleManage.vue
  24. 5 5
      src/modules/base/views/TemplateManage.vue
  25. 21 18
      src/modules/exam/components/ApplyContent.vue
  26. 1 2
      src/modules/exam/components/BatchAddExamTask.vue
  27. 10 13
      src/modules/exam/components/CardOptionDialog.vue
  28. 6 8
      src/modules/exam/components/ModifyExamTask.vue
  29. 9 7
      src/modules/exam/components/ModifyTaskApply.vue
  30. 14 18
      src/modules/exam/views/ExamTaskManage.vue
  31. 18 22
      src/modules/exam/views/TaskApplyManage.vue
  32. 12 16
      src/modules/exam/views/TaskPaperManage.vue
  33. 9 16
      src/modules/exam/views/TaskReviewManage.vue
  34. 69 0
      src/modules/print/api.js
  35. 256 0
      src/modules/print/components/ModifyPlanPaper.vue
  36. 520 0
      src/modules/print/components/ModifyPrintPlan.vue
  37. 134 5
      src/modules/print/views/BusinessDataDetail.vue
  38. 226 5
      src/modules/print/views/BusinessDataExport.vue
  39. 170 5
      src/modules/print/views/PlanLinkPaper.vue
  40. 245 3
      src/modules/print/views/PrintPlanManage.vue
  41. 406 5
      src/modules/print/views/PrintTaskManage.vue
  42. 13 1
      src/plugins/filters.js
  43. 15 1
      src/plugins/globalVuePlugins.js

+ 3 - 0
src/assets/styles/base.scss

@@ -329,6 +329,9 @@ body {
 .mr-2 {
   margin-right: 10px;
 }
+.mr-4 {
+  margin-right: 20px;
+}
 .mb-2 {
   margin-bottom: 10px;
 }

+ 19 - 1
src/assets/styles/pages.scss

@@ -179,5 +179,23 @@
 }
 
 // rule-business
-.rule-business {
+
+// select-orgs
+.select-orgs {
+  position: relative;
+
+  &-disabled {
+    &::before {
+      content: "";
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      z-index: 99;
+      background-color: #f5f7fa;
+      opacity: 0.3;
+      cursor: not-allowed;
+    }
+  }
 }

+ 10 - 4
src/components/UploadButton.vue

@@ -9,13 +9,18 @@
     :on-error="handleError"
     :on-success="handleSuccess"
     :http-request="upload"
+    :disabled="disabled"
     :show-file-list="false"
     style="display:inline-block;margin: 0 18px;"
     ref="UploadComp"
   >
-    <el-button :type="btnType" :icon="btnIcon" :loading="loading">{{
-      btnContent
-    }}</el-button>
+    <el-button
+      :type="btnType"
+      :icon="btnIcon"
+      :loading="loading"
+      :disabled="disabled"
+      >{{ btnContent }}</el-button
+    >
   </el-upload>
 </template>
 
@@ -59,7 +64,8 @@ export default {
     addFilenameParam: {
       type: String,
       default: "filename"
-    }
+    },
+    disabled: { type: Boolean, default: false }
   },
   data() {
     return {

+ 0 - 4
src/components/base/CardRuleSelect.vue

@@ -5,8 +5,6 @@
     placeholder="请选择"
     :style="styles"
     filterable
-    remote
-    :remote-method="search"
     :clearable="clearable"
     :disabled="disabled"
     @change="select"
@@ -69,5 +67,3 @@ export default {
   }
 };
 </script>
-
-<style></style>

+ 0 - 4
src/components/base/CourseSelect.vue

@@ -5,8 +5,6 @@
     placeholder="请选择"
     :style="styles"
     filterable
-    remote
-    :remote-method="search"
     :clearable="clearable"
     :disabled="disabled"
     @change="select"
@@ -65,5 +63,3 @@ export default {
   }
 };
 </script>
-
-<style></style>

+ 1 - 5
src/components/base/PaperNumberSelect.vue

@@ -5,8 +5,6 @@
     placeholder="请选择"
     :style="styles"
     filterable
-    remote
-    :remote-method="search"
     :clearable="clearable"
     :disabled="disabled"
     @change="select"
@@ -58,11 +56,9 @@ export default {
       this.$emit("input", this.selected);
       this.$emit(
         "change",
-        this.optionList.find(item => item.id === this.selected)
+        this.optionList.find(item => item.pageNumber === this.selected)
       );
     }
   }
 };
 </script>
-
-<style></style>

+ 66 - 0
src/components/base/PlaceSelect.vue

@@ -0,0 +1,66 @@
+<template>
+  <el-select
+    v-model="selected"
+    class="place-select"
+    placeholder="请选择"
+    :style="styles"
+    filterable
+    :clearable="clearable"
+    :disabled="disabled"
+    @change="select"
+  >
+    <el-option
+      v-for="(item, index) in optionList"
+      :key="index"
+      :value="item.examPlace"
+      :label="item.examPlace"
+    >
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+import { placeQuery } from "../../modules/print/api";
+
+export default {
+  name: "place-select",
+  props: {
+    disabled: { type: Boolean, default: false },
+    value: { type: [Number, String], default: "" },
+    styles: { type: String, default: "" },
+    clearable: { type: Boolean, default: true }
+  },
+  data() {
+    return {
+      optionList: [],
+      selected: ""
+    };
+  },
+  watch: {
+    value: {
+      immediate: true,
+      handler(val) {
+        this.selected = val;
+      }
+    }
+  },
+  async created() {
+    this.search();
+  },
+  methods: {
+    async search(query) {
+      const res = await placeQuery(query);
+      this.optionList = res;
+    },
+    select() {
+      this.$emit("input", this.selected);
+      this.$emit(
+        "change",
+        this.optionList.find(item => item.examPlace === this.selected)
+      );
+    }
+  }
+};
+</script>
+
+<style></style>

+ 66 - 0
src/components/base/PrintPlanSelect.vue

@@ -0,0 +1,66 @@
+<template>
+  <el-select
+    v-model="selected"
+    class="print-plan-select"
+    placeholder="请选择"
+    :style="styles"
+    filterable
+    :clearable="clearable"
+    :disabled="disabled"
+    @change="select"
+  >
+    <el-option
+      v-for="item in optionList"
+      :key="item.id"
+      :value="item.id"
+      :label="item.name"
+    >
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+import { printPlanQuery } from "../../modules/print/api";
+
+export default {
+  name: "print-plan-select",
+  props: {
+    disabled: { type: Boolean, default: false },
+    value: { type: [Number, String], default: "" },
+    styles: { type: String, default: "" },
+    clearable: { type: Boolean, default: true }
+  },
+  data() {
+    return {
+      optionList: [],
+      selected: ""
+    };
+  },
+  watch: {
+    value: {
+      immediate: true,
+      handler(val) {
+        this.selected = val;
+      }
+    }
+  },
+  async created() {
+    this.search();
+  },
+  methods: {
+    async search(query) {
+      const res = await printPlanQuery(query);
+      this.optionList = res;
+    },
+    select() {
+      this.$emit("input", this.selected);
+      this.$emit(
+        "change",
+        this.optionList.find(item => item.id === this.selected)
+      );
+    }
+  }
+};
+</script>
+
+<style></style>

+ 0 - 2
src/components/base/QuestionTeacherSelect.vue

@@ -5,8 +5,6 @@
     placeholder="请选择"
     :style="styles"
     filterable
-    remote
-    :remote-method="search"
     :clearable="clearable"
     :disabled="disabled"
     @change="select"

+ 66 - 0
src/components/base/RoomSelect.vue

@@ -0,0 +1,66 @@
+<template>
+  <el-select
+    v-model="selected"
+    class="room-select"
+    placeholder="请选择"
+    :style="styles"
+    filterable
+    :clearable="clearable"
+    :disabled="disabled"
+    @change="select"
+  >
+    <el-option
+      v-for="(item, index) in optionList"
+      :key="index"
+      :value="item.examRoom"
+      :label="item.examRoom"
+    >
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+import { roomQuery } from "../../modules/print/api";
+
+export default {
+  name: "room-select",
+  props: {
+    disabled: { type: Boolean, default: false },
+    value: { type: [Number, String], default: "" },
+    styles: { type: String, default: "" },
+    clearable: { type: Boolean, default: true }
+  },
+  data() {
+    return {
+      optionList: [],
+      selected: ""
+    };
+  },
+  watch: {
+    value: {
+      immediate: true,
+      handler(val) {
+        this.selected = val;
+      }
+    }
+  },
+  async created() {
+    this.search();
+  },
+  methods: {
+    async search(query) {
+      const res = await roomQuery(query);
+      this.optionList = res;
+    },
+    select() {
+      this.$emit("input", this.selected);
+      this.$emit(
+        "change",
+        this.optionList.find(item => item.examRoom === this.selected)
+      );
+    }
+  }
+};
+</script>
+
+<style></style>

+ 58 - 33
src/constants/enumerate.js

@@ -16,38 +16,34 @@ export const BOOLEAN_TYPE = {
 };
 
 // 打印状态
-// export const PRINT_STATUS = {
-//   "3": "未开始",
-//   "4": "命题中",
-//   "0": "待打印",
-//   "1": "打印中",
-//   "2": "已打印",
-//   "9": "无"
-// };
-export const PRINT_STATUS = [
-  {
-    key: "3",
-    name: "未开始"
-  },
+export const PRINT_STATUS = {
+  NEW: "新建",
+  READY: "就绪",
+  WAIT: "待印刷",
+  PROCESS: "印刷中",
+  END: "已完成"
+};
+
+export const PRINT_STATUS_LIST = [
   {
-    key: "4",
-    name: "命题中"
+    key: "NEW",
+    name: "新建"
   },
   {
-    key: "0",
-    name: "待打印"
+    key: "READY",
+    name: "就绪"
   },
   {
-    key: "1",
-    name: "打印中"
+    key: "WAIT",
+    name: "待印刷"
   },
   {
-    key: "2",
-    name: "已打印"
+    key: "PROCESS",
+    name: "印刷中"
   },
   {
-    key: "9",
-    name: ""
+    key: "END",
+    name: "已完成"
   }
 ];
 // 撤回状态
@@ -121,17 +117,16 @@ export const TEMPLATE_TYPE = {
   ORDINARY: "普通印品模板"
 };
 
-export const TEMPLATE_CLASSIFY = {
-  SIGN: "签到表",
-  PACKAGE: "卷袋贴",
-  CHECK_IN: "登记表"
-};
-
 export const PRINT_CONTENT_TYPE = {
-  examPaper: "试卷",
-  answerSheet: "答题卡",
-  signIn: "签到表",
-  paperSticker: "卷袋贴"
+  PAPER: "试卷",
+  CARD: "题卡"
+};
+export const PRINT_PLAN_STATUS = {
+  NEW: "新建",
+  READY: "准备就绪",
+  QUESTION: "命题中",
+  PRINT: "印刷中",
+  END: "已完成"
 };
 
 export const PAPER_TYPE_FIELDS = ["A", "B"];
@@ -146,6 +141,12 @@ export const PRIVILEGE_TYPE = {
   F: "操作"
 };
 
+export const TEMPLATE_CLASSIFY = {
+  SIGN: "签到表",
+  PACKAGE: "卷袋贴",
+  CHECK_IN: "考试情况登记表"
+};
+
 export const EXAM_TASK_STATUS = {
   NEW: "新建任务",
   STAGE: "暂存",
@@ -153,6 +154,30 @@ export const EXAM_TASK_STATUS = {
   CANCEL: "撤回"
 };
 
+export const DRAW_RULE_TYPE = {
+  ONE: "只抽取一次",
+  CIRCLE: "可反复抽取"
+};
+
+export const PRINT_BACKUP_TYPE = {
+  ROOM: "每考场",
+  STUDENT: "每考生"
+};
+
+export const PAPER_BACKUP_TYPE = {
+  ROOM: "每考场",
+  PLACE: "每考点"
+};
+
+export const ROLE_TYPE = {
+  ADMIN: "管理员",
+  EXAM_TEACHER: "考务老师",
+  QUESTION_TEACHER: "命题老师",
+  CUSTOMER: "客服人员",
+  PRINT: "印刷人员",
+  CUSTOM: "自定义"
+};
+
 export const keepAliveRoutesPairs = [
   ["wait-task", ["wait-task-detail"]],
   ["done-task", ["done-task-detail"]],

+ 5 - 5
src/constants/navs.js

@@ -65,11 +65,11 @@ const navs = [
           {
             title: "印刷任务管理",
             router: "PrintTaskManage"
-          },
-          {
-            title: "印刷监控查询",
-            router: "PrintProgressManage"
           }
+          // {
+          //   title: "印刷监控查询",
+          //   router: "PrintProgressManage"
+          // }
         ]
       }
     ]
@@ -112,7 +112,7 @@ const navs = [
           },
           {
             title: "题卡规则管理",
-            router: "CardRule"
+            router: "CardRuleManage"
           },
           {
             title: "通卡模板",

+ 23 - 9
src/modules/base/components/ModifyCardRule.vue

@@ -137,13 +137,21 @@
           ></el-input>
         </el-form-item>
         <el-form-item prop="orgs" label="适用学院范围:">
-          <select-orgs v-model="modalForm.orgs" ref="SelectOrgs"></select-orgs>
+          <select-orgs
+            v-model="modalForm.orgs"
+            ref="SelectOrgs"
+            :disabled="!editable"
+          ></select-orgs>
         </el-form-item>
       </el-form>
     </div>
 
     <div slot="footer">
-      <el-button type="primary" :disabled="isSubmit" @click="submit"
+      <el-button
+        v-if="editable"
+        type="primary"
+        :disabled="isSubmit"
+        @click="submit"
         >确认</el-button
       >
       <el-button type="danger" @click="cancel" plain>取消</el-button>
@@ -183,17 +191,23 @@ export default {
         return {};
       }
     },
-    editable: {
-      type: Boolean,
-      default: true
+    editType: {
+      type: String,
+      default: "ADD",
+      validator: val => ["ADD", "PREVIEW", "EDIT"].includes(val)
     }
   },
   computed: {
-    isEdit() {
-      return !!this.instance.id;
-    },
     title() {
-      return (this.isEdit ? "编辑" : "新增") + "题卡规则";
+      const names = {
+        ADD: "新增题卡规则",
+        PREVIEW: "题卡规则详情",
+        EDIT: "编辑题卡规则"
+      };
+      return names[this.editType];
+    },
+    editable() {
+      return this.editType !== "PREVIEW";
     }
   },
   data() {

+ 32 - 3
src/modules/base/components/ModifyRole.vue

@@ -25,7 +25,25 @@
             clearable
           ></el-input>
         </el-form-item>
-        <el-form-item prop="privilegeIds" label="角色权限:">
+        <el-form-item prop="type" label="角色类型:">
+          <el-select
+            v-model="modalForm.type"
+            style="width: 100%;"
+            placeholder="请选择"
+          >
+            <el-option
+              v-for="(val, key) in ROLE_TYPE"
+              :key="key"
+              :value="key"
+              :label="val"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-show="modalForm.type === 'CUSTOM'"
+          prop="privilegeIds"
+          label="角色权限:"
+        >
           <div class="part-box part-box-pad part-box-border">
             <el-tree
               :data="menus"
@@ -53,9 +71,12 @@
 
 <script>
 import { updateRole, roleBoundPrivileges } from "../api";
+import { ROLE_TYPE } from "../../../constants/enumerate";
+
 const initModalForm = {
   id: null,
   name: "",
+  type: "",
   privilegeIds: []
 };
 
@@ -90,6 +111,7 @@ export default {
       defaultProps: {
         label: "name"
       },
+      ROLE_TYPE,
       rules: {
         name: [
           {
@@ -99,11 +121,18 @@ export default {
             trigger: "change"
           }
         ],
+        type: [
+          {
+            required: true,
+            message: "请选择角色类型",
+            trigger: "change"
+          }
+        ],
         privilegeIds: [
           {
             required: true,
             validator: (rule, value, callback) => {
-              if (value.length) {
+              if (this.modalForm.type !== "CUSTOM" || value.length) {
                 callback();
               } else {
                 callback(new Error("请选择扩展字段"));
@@ -164,7 +193,7 @@ export default {
         ...this.$refs.MenuTree.getCheckedKeys(),
         ...this.$refs.MenuTree.getHalfCheckedKeys()
       ];
-      data.privilegeIds = privilegeIds.join();
+      datas.privilegeIds = privilegeIds.join();
       const data = await updateRole(datas).catch(() => {});
       this.isSubmit = false;
       if (!data) return;

+ 42 - 42
src/modules/base/components/ModifyTemplate.vue

@@ -4,7 +4,7 @@
     :visible.sync="modalIsShow"
     :title="title"
     top="10px"
-    width="950px"
+    width="750px"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body
@@ -51,10 +51,10 @@
             clearable
           >
             <el-option
-              v-for="item in categories"
-              :key="item.code"
-              :value="item.code"
-              :label="item.name"
+              v-for="(val, key) in categories"
+              :key="key"
+              :value="key"
+              :label="val"
             ></el-option>
           </el-select>
         </el-form-item>
@@ -68,13 +68,21 @@
           ></upload-file-view>
         </el-form-item>
         <el-form-item prop="orgs" label="适用学院范围:">
-          <select-orgs v-model="modalForm.orgs" ref="SelectOrgs"></select-orgs>
+          <select-orgs
+            v-model="modalForm.orgs"
+            ref="SelectOrgs"
+            :disabled="!editable"
+          ></select-orgs>
         </el-form-item>
       </el-form>
     </div>
 
     <div slot="footer">
-      <el-button type="primary" :disabled="isSubmit" @click="submit"
+      <el-button
+        v-if="editable"
+        type="primary"
+        :disabled="isSubmit"
+        @click="submit"
         >确认</el-button
       >
       <el-button type="danger" @click="cancel" plain>取消</el-button>
@@ -83,7 +91,7 @@
 </template>
 
 <script>
-import { organizationList, updateTemplate, templateCategoryList } from "../api";
+import { updateTemplate } from "../api";
 import { attachmentPreview } from "../../login/api";
 import UploadFileView from "@/components/UploadFileView";
 import SelectOrgs from "./SelectOrgs";
@@ -98,6 +106,11 @@ const initModalForm = {
   orgs: []
 };
 
+const TEMPLATE_CLASSIFY = {
+  VARIABLE: { SIGN: "签到表", PACKAGE: "卷袋贴" },
+  ORDINARY: { CHECK_IN: "考试情况登记表" }
+};
+
 export default {
   name: "modify-template",
   components: { UploadFileView, SelectOrgs },
@@ -108,17 +121,26 @@ export default {
         return {};
       }
     },
-    editable: {
-      type: Boolean,
-      default: true
+    editType: {
+      type: String,
+      default: "ADD",
+      validator: val => ["ADD", "PREVIEW", "EDIT"].includes(val)
     }
   },
   computed: {
-    isEdit() {
-      return !!this.instance.id;
-    },
     title() {
-      return (this.isEdit ? "编辑" : "新增") + "模板";
+      const names = {
+        ADD: "新增模板",
+        PREVIEW: "模板详情",
+        EDIT: "编辑模板"
+      };
+      return names[this.editType];
+    },
+    editable() {
+      return this.editType !== "PREVIEW";
+    },
+    categories() {
+      return TEMPLATE_CLASSIFY[this.modalForm.templateType] || {};
     }
   },
   data() {
@@ -127,21 +149,6 @@ export default {
       isSubmit: false,
       modalForm: {},
       attachment: {},
-      organizations: [
-        {
-          id: 1,
-          name: "外语学院"
-        },
-        {
-          id: 2,
-          name: "数学学院"
-        },
-        {
-          id: 3,
-          name: "计算机学院"
-        }
-      ],
-      categories: [],
       rules: {
         name: [
           {
@@ -183,24 +190,17 @@ export default {
       uploadUrl: "/api/exam/file/upload"
     };
   },
-  mounted() {
-    // this.getColleges();
-    // if (this.modalForm.templateType !== 'GENERIC') this.getCategories();
-  },
+  mounted() {},
   methods: {
-    async getColleges() {
-      this.organizations = await organizationList();
-    },
-    async getCategories() {
-      this.categories = await templateCategoryList(
-        this.modalForm.modalForm.templateType
-      );
-    },
     initData(val) {
       this.modalForm = this.$objAssign(initModalForm, val);
       if (val.id) {
         this.modalForm.orgs = val.orgs.map(item => item.id);
         this.getAttachment();
+      } else {
+        this.$nextTick(() => {
+          this.$refs.UploadFileView.setAttachmentName("");
+        });
       }
     },
     async getAttachment() {

+ 9 - 4
src/modules/base/components/ModifyUser.vue

@@ -99,7 +99,7 @@ const initModalForm = {
   loginName: "",
   realName: "",
   mobileNumber: "",
-  roleIds: "",
+  roleIds: [],
   courseCodes: []
 };
 
@@ -128,6 +128,13 @@ export default {
     }
   },
   data() {
+    const roleIdsValidator = (rule, value, callback) => {
+      if (!value || !value.length) {
+        callback(new Error("请选择角色"));
+      } else {
+        callback();
+      }
+    };
     const courseCodesValidator = (rule, value, callback) => {
       if (!value || !value.length) {
         callback(new Error("请选择课程"));
@@ -159,7 +166,7 @@ export default {
         roleIds: [
           {
             required: true,
-            message: "请选择角色",
+            validator: roleIdsValidator,
             trigger: "change"
           }
         ],
@@ -209,8 +216,6 @@ export default {
       this.isSubmit = true;
 
       const datas = { ...this.modalForm };
-      data.roleIds = data.roleIds.join();
-      data.courseCodes = data.courseCodes.join();
       // TODO:
       if (datas.roleIds !== "QUESTION_TEACHER") delete datas.courseCodes;
       const data = await updateUser(datas).catch(() => {});

+ 5 - 1
src/modules/base/components/SelectOrgs.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="select-orgs">
+  <div :class="['select-orgs', { 'select-orgs-disabled': disabled }]">
     <div class="part-box part-box-pad part-box-border" v-if="isTree">
       <el-tree
         :data="orgs"
@@ -34,6 +34,10 @@ export default {
       default() {
         return [];
       }
+    },
+    disabled: {
+      type: Boolean,
+      default: false
     }
   },
   data() {

+ 4 - 4
src/modules/base/router.js

@@ -5,7 +5,7 @@ import UserManage from "./views/UserManage.vue";
 import MenuManage from "./views/MenuManage.vue";
 // rule
 import CommonRule from "./views/CommonRule.vue";
-import CardRule from "./views/CardRule.vue";
+import CardRuleManage from "./views/CardRuleManage.vue";
 import CommonCardTemplate from "./views/CommonCardTemplate.vue";
 import ParamPrintTemplate from "./views/ParamPrintTemplate.vue";
 import CommonPrintTemplate from "./views/CommonPrintTemplate.vue";
@@ -38,9 +38,9 @@ export default [
     component: CommonRule
   },
   {
-    path: "/base/card-rule",
-    name: "CardRule",
-    component: CardRule
+    path: "/base/card-rule-manage",
+    name: "CardRuleManage",
+    component: CardRuleManage
   },
   {
     path: "/base/common-card-template",

+ 7 - 7
src/modules/base/views/CardRule.vue → src/modules/base/views/CardRuleManage.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="card-rule">
+  <div class="card-rule-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="规则名称:">
@@ -123,7 +123,7 @@
     <modify-card-rule
       ref="ModifyCardRule"
       :instance="curRule"
-      :editable="ruleCanEdit"
+      :edit-type="editType"
       @modified="getList"
     ></modify-card-rule>
   </div>
@@ -135,7 +135,7 @@ import { commonRuleDetail, cardRuleListPage, ableCardRule } from "../api";
 import ModifyCardRule from "../components/ModifyCardRule";
 
 export default {
-  name: "card-rule",
+  name: "card-rule-manage",
   components: {
     ModifyCardRule
   },
@@ -151,7 +151,7 @@ export default {
       total: 0,
       rules: [],
       curRule: {},
-      ruleCanEdit: true,
+      editType: "ADD",
       commonRule: {},
       ABLE_TYPE
     };
@@ -191,17 +191,17 @@ export default {
             return item;
           })
       };
-      this.ruleCanEdit = true;
+      this.editType = "ADD";
       this.$refs.ModifyCardRule.open();
     },
     toEdit(row) {
       this.curRule = row;
-      this.ruleCanEdit = true;
+      this.editType = "EDIT";
       this.$refs.ModifyCardRule.open();
     },
     toDetail(row) {
       this.curRule = row;
-      this.ruleCanEdit = false;
+      this.editType = "PREVIEW";
       this.$refs.ModifyCardRule.open();
     },
     async toEnable(row) {

+ 7 - 5
src/modules/base/views/CourseManage.vue

@@ -125,11 +125,13 @@ export default {
         cancelButtonClass: "el-button--danger is-plain",
         confirmButtonClass: "el-button--primary",
         type: "warning"
-      }).then(async () => {
-        await deleteCourse(row.id);
-        this.$message.success("删除成功!");
-        this.deletePageLastItem();
-      });
+      })
+        .then(async () => {
+          await deleteCourse(row.id);
+          this.$message.success("删除成功!");
+          this.deletePageLastItem();
+        })
+        .catch(() => {});
     }
   }
 };

+ 14 - 9
src/modules/base/views/MenuManage.vue

@@ -90,20 +90,25 @@ export default {
       this.$refs.ModifyMenu.open();
     },
     remove(node, data) {
-      this.$confirm(`确定要删除【${data.name}】吗?`, "提示", {
+      const name = node.parent.label
+        ? `${node.parent.label}->${data.name}`
+        : data.name;
+      this.$confirm(`确定要删除【${name}】吗?`, "提示", {
         cancelButtonClass: "el-button--danger is-plain",
         confirmButtonClass: "el-button--primary",
         type: "warning"
-      }).then(async () => {
-        await deleteMenu(data.id);
+      })
+        .then(async () => {
+          await deleteMenu(data.id);
 
-        const parent = node.parent;
-        const children = parent.data.children || parent.data;
-        const index = children.findIndex(d => d.id === data.id);
-        children.splice(index, 1);
+          const parent = node.parent;
+          const children = parent.data.children || parent.data;
+          const index = children.findIndex(d => d.id === data.id);
+          children.splice(index, 1);
 
-        this.$message.success("删除成功!");
-      });
+          this.$message.success("删除成功!");
+        })
+        .catch(() => {});
     },
     append(node, data) {
       this.curMenu = {

+ 10 - 8
src/modules/base/views/OrganizationManage.vue

@@ -90,16 +90,18 @@ export default {
         cancelButtonClass: "el-button--danger is-plain",
         confirmButtonClass: "el-button--primary",
         type: "warning"
-      }).then(async () => {
-        await deleteOrganization(data.id);
+      })
+        .then(async () => {
+          await deleteOrganization(data.id);
 
-        const parent = node.parent;
-        const children = parent.data.children || parent.data;
-        const index = children.findIndex(d => d.id === data.id);
-        children.splice(index, 1);
+          const parent = node.parent;
+          const children = parent.data.children || parent.data;
+          const index = children.findIndex(d => d.id === data.id);
+          children.splice(index, 1);
 
-        this.$message.success("删除成功!");
-      });
+          this.$message.success("删除成功!");
+        })
+        .catch(() => {});
     },
     append(data) {
       this.curOrg = { parentId: data.id, parentName: data.name };

+ 12 - 5
src/modules/base/views/RoleManage.vue

@@ -47,6 +47,11 @@
           :index="indexMethod"
         ></el-table-column>
         <el-table-column prop="name" label="角色名称"></el-table-column>
+        <el-table-column prop="type" label="角色类型">
+          <template slot-scope="scope">
+            {{ scope.row.type | roleTypeFilter }}
+          </template>
+        </el-table-column>
         <el-table-column prop="enable" label="启用/禁用">
           <template slot-scope="scope">
             {{ scope.row.enable | enableFilter }}
@@ -185,11 +190,13 @@ export default {
         cancelButtonClass: "el-button--danger is-plain",
         confirmButtonClass: "el-button--primary",
         type: "warning"
-      }).then(async () => {
-        await deleteRole(row.id);
-        this.$message.success("删除成功!");
-        this.deletePageLastItem();
-      });
+      })
+        .then(async () => {
+          await deleteRole(row.id);
+          this.$message.success("删除成功!");
+          this.deletePageLastItem();
+        })
+        .catch(() => {});
     },
     async toEnable(row) {
       const enable = !row.enable;

+ 5 - 5
src/modules/base/views/TemplateManage.vue

@@ -132,7 +132,7 @@
     <modify-template
       ref="ModifyTemplate"
       :instance="curTemplate"
-      :editable="templateCanEdit"
+      :edit-type="editType"
       :template-type="templateType"
       @modified="getList"
     ></modify-template>
@@ -170,7 +170,7 @@ export default {
       total: 0,
       templates: [],
       curTemplate: {},
-      templateCanEdit: true,
+      editType: "ADD",
       ABLE_TYPE
     };
   },
@@ -196,17 +196,17 @@ export default {
     },
     toAdd() {
       this.curTemplate = { templateType: this.templateType };
-      this.templateCanEdit = true;
+      this.editType = "ADD";
       this.$refs.ModifyTemplate.open();
     },
     toEdit(row) {
       this.curTemplate = { ...row, templateType: this.templateType };
-      this.templateCanEdit = true;
+      this.editType = "EDIT";
       this.$refs.ModifyTemplate.open();
     },
     toDetail(row) {
       this.curTemplate = { ...row, templateType: this.templateType };
-      this.templateCanEdit = false;
+      this.editType = "PREVIEW";
       this.$refs.ModifyTemplate.open();
     },
     async toEnable(row) {

+ 21 - 18
src/modules/exam/components/ApplyContent.vue

@@ -197,10 +197,10 @@ export default {
         return {};
       }
     },
-    viewType: {
+    editType: {
       type: String,
       default: "APPLY",
-      validator: val => ["APPLY", "VIEW", "AUDIT"].includes(val)
+      validator: val => ["APPLY", "PREVIEW", "AUDIT"].includes(val)
     }
   },
   data() {
@@ -220,13 +220,13 @@ export default {
   },
   computed: {
     IS_APPLY() {
-      return this.viewType === "APPLY";
+      return this.editType === "APPLY";
     },
-    IS_VIEW() {
-      return this.viewType === "VIEW";
+    IS_PREVIEW() {
+      return this.editType === "PREVIEW";
     },
     IS_AUDIT() {
-      return this.viewType === "AUDIT";
+      return this.editType === "AUDIT";
     },
     cardTodoName() {
       let name = "创建答题卡";
@@ -412,15 +412,16 @@ export default {
           confirmButtonClass: "el-button--primary",
           type: "warning"
         }
-      ).then(async () => {
-        const datas = this.getTaskData();
-        datas.operateType = "SUBMIT";
-        const data = await updateTaskApply(datas).catch(() => {});
-        if (!data) return;
-        this.$message.success("提交成功!");
-        this.goback();
-        this.$emit("modified");
-      });
+      )
+        .then(async () => {
+          const datas = this.getTaskData();
+          datas.operateType = "SUBMIT";
+          const data = await updateTaskApply(datas).catch(() => {});
+          if (!data) return;
+          this.$message.success("提交成功!");
+          this.$emit("modified");
+        })
+        .catch(() => {});
     },
     toAuditApply(type) {
       if (type === "PASS") {
@@ -435,9 +436,11 @@ export default {
           cancelButtonClass: "el-button--danger is-plain",
           confirmButtonClass: "el-button--primary",
           type: "warning"
-        }).then(() => {
-          this.auditApply(type);
-        });
+        })
+          .then(() => {
+            this.auditApply(type);
+          })
+          .catch(() => {});
       }
     },
     async auditApply(type) {

+ 1 - 2
src/modules/exam/components/BatchAddExamTask.vue

@@ -117,7 +117,6 @@
 <script>
 import { batchAddExamTask } from "../api";
 import UploadFileView from "@/components/UploadFileView";
-import CardRuleSelect from "../../../components/base/CardRuleSelect";
 
 const initModalForm = {
   cardRuleId: "",
@@ -129,7 +128,7 @@ const initModalForm = {
 
 export default {
   name: "batch-add-exam-task",
-  components: { UploadFileView, CardRuleSelect },
+  components: { UploadFileView },
   data() {
     return {
       modalIsShow: false,

+ 10 - 13
src/modules/exam/components/CardOptionDialog.vue

@@ -228,19 +228,17 @@ export default {
       this.applyModalForm.paperAttachmentId = res.data.id;
       this.$refs.ApplyModalForm.validateField("paperAttachmentId");
     },
-    confirm() {
-      if (this.modalForm.cardSource !== "0" && !this.modalForm.courseCode) {
-        this.$message.error("请选择科目!");
-        return;
-      }
-
-      if (this.modalForm.cardSource === "2") {
+    async confirm() {
+      // TODO:具体后续逻辑待定
+      if (this.modalForm.cardSource === 2) {
         this.$emit("draft-task");
-        this.$refs.UploadSamplePaperDialog.open();
-        return;
+        const valid = await this.$refs.ApplyModalForm.validate().catch(
+          () => {}
+        );
+        if (!valid) return;
       }
 
-      if (this.modalForm.cardSource === "1") {
+      if (this.modalForm.cardSource === 1) {
         // 暂存任务,确保以上传的试卷信息正常保存
         this.$emit("draft-task");
         // 打开题卡编辑页,创建题卡,并预设需要绑定的任务
@@ -251,14 +249,13 @@ export default {
         return;
       }
 
-      if (this.modalForm.cardSource === "0" && !this.modalForm.refCardId) {
+      if (this.modalForm.cardSource === 0 && !this.modalForm.refCardId) {
         this.$message.error("请选择已有的题卡!");
         return;
       }
 
       const data = { ...this.modalForm };
-      if (data.cardSource !== "0") data.refCardId = "";
-      data.cardSource = data.cardSource * 1;
+      if (data.cardSource !== 0) data.refCardId = "";
       this.$emit("confirm", data);
       this.cancel();
     },

+ 6 - 8
src/modules/exam/components/ModifyExamTask.vue

@@ -107,9 +107,6 @@
 
 <script>
 import { updateExamTask } from "../api";
-import CardRuleSelect from "../../../components/base/CardRuleSelect";
-import CourseSelect from "../../../components/base/CourseSelect";
-import QuestionTeacherSelect from "../../../components/base/QuestionTeacherSelect";
 
 const initModalForm = {
   id: null,
@@ -127,7 +124,6 @@ const initModalForm = {
 
 export default {
   name: "modify-exam-task",
-  components: { CardRuleSelect, CourseSelect, QuestionTeacherSelect },
   props: {
     instance: {
       type: Object,
@@ -142,11 +138,13 @@ export default {
     }
   },
   computed: {
-    isEdit() {
-      return !!this.instance.id;
-    },
     title() {
-      return (this.isEdit ? "编辑" : "新增") + "命题任务";
+      const names = {
+        ADD: "新增命题任务",
+        PREVIEW: "命题任务详情",
+        EDIT: "编辑命题任务"
+      };
+      return names[this.editType];
     },
     editable() {
       return this.editType === "ADD";

+ 9 - 7
src/modules/exam/components/ModifyTaskApply.vue

@@ -82,7 +82,7 @@
       <apply-content
         ref="ApplyContent"
         :exam-task="modalForm"
-        :view-type="viewType"
+        :edit-type="editType"
         v-show="curMenu.id === '1'"
         @cancel="cancel"
         @modified="modified"
@@ -128,18 +128,20 @@ export default {
         return {};
       }
     },
-    viewType: {
+    editType: {
       type: String,
       default: "APPLY",
-      validator: val => ["APPLY", "VIEW", "AUDIT"].includes(val)
+      validator: val => ["APPLY", "PREVIEW", "AUDIT"].includes(val)
     }
   },
   computed: {
-    isEdit() {
-      return !!this.instance.id;
-    },
     title() {
-      return (this.isEdit ? "编辑" : "新增") + "命题任务";
+      const names = {
+        APPLY: "提交入库申请",
+        PREVIEW: "入库申请详情",
+        AUDIT: "审核入库申请"
+      };
+      return names[this.editType];
     }
   },
   data() {

+ 14 - 18
src/modules/exam/views/ExamTaskManage.vue

@@ -21,7 +21,7 @@
           <card-rule-select
             ref="CardRuleSelect"
             v-model.trim="filter.cardRuleId"
-            placeholder="请输入内容"
+            placeholder="请选择"
             clearable
           ></card-rule-select>
         </el-form-item>
@@ -29,7 +29,7 @@
           <course-select
             ref="CourseSelect"
             v-model.trim="filter.courseCode"
-            placeholder="请输入内容"
+            placeholder="请选择"
             clearable
           ></course-select>
         </el-form-item>
@@ -196,9 +196,6 @@
 <script>
 import ModifyExamTask from "../components/ModifyExamTask";
 import BatchAddExamTask from "../components/BatchAddExamTask";
-import CardRuleSelect from "../../../components/base/CardRuleSelect";
-import CourseSelect from "../../../components/base/CourseSelect";
-import PaperNumberSelect from "../../../components/base/PaperNumberSelect";
 import { EXAM_TASK_STATUS } from "@/constants/enumerate";
 import { examTaskListPage, ableExamTask } from "../api";
 
@@ -206,10 +203,7 @@ export default {
   name: "exam-task-manage",
   components: {
     ModifyExamTask,
-    BatchAddExamTask,
-    CardRuleSelect,
-    CourseSelect,
-    PaperNumberSelect
+    BatchAddExamTask
   },
   data() {
     return {
@@ -317,15 +311,17 @@ export default {
         confirmButtonClass: "el-button--primary",
         dangerouslyUseHTMLString: true,
         type: "warning"
-      }).then(async () => {
-        const enable = Number(!row.enable);
-        await ableExamTask({
-          id: row.id,
-          enable
-        });
-        row.enable = enable;
-        this.$message.success("操作成功!");
-      });
+      })
+        .then(async () => {
+          const enable = Number(!row.enable);
+          await ableExamTask({
+            id: row.id,
+            enable
+          });
+          row.enable = enable;
+          this.$message.success("操作成功!");
+        })
+        .catch(() => {});
     },
     toBatchAdd() {
       this.$refs.BatchAddExamTask.open();

+ 18 - 22
src/modules/exam/views/TaskApplyManage.vue

@@ -36,7 +36,7 @@
           <card-rule-select
             ref="CardRuleSelect"
             v-model.trim="filter.cardRuleId"
-            placeholder="请输入内容"
+            placeholder="请选择"
             clearable
           ></card-rule-select>
         </el-form-item>
@@ -44,7 +44,7 @@
           <course-select
             ref="CourseSelect"
             v-model.trim="filter.courseCode"
-            placeholder="请输入内容"
+            placeholder="请选择"
             clearable
           ></course-select>
         </el-form-item>
@@ -185,7 +185,7 @@
     <!-- ModifyTaskApply -->
     <modify-task-apply
       ref="ModifyTaskApply"
-      :view-type="viewType"
+      :edit-type="editType"
       :instance="curExamTask"
       @modified="getList"
     ></modify-task-apply>
@@ -194,19 +194,13 @@
 
 <script>
 import ModifyTaskApply from "../components/ModifyTaskApply";
-import CardRuleSelect from "../../../components/base/CardRuleSelect";
-import CourseSelect from "../../../components/base/CourseSelect";
-import PaperNumberSelect from "../../../components/base/PaperNumberSelect";
 import { AUDITING_STATUS, AUDITING_RESULT } from "@/constants/enumerate";
 import { taskApplyListPage, cancelOrRestartTaskApply } from "../api";
 
 export default {
   name: "task-apply-manage",
   components: {
-    ModifyTaskApply,
-    CardRuleSelect,
-    CourseSelect,
-    PaperNumberSelect
+    ModifyTaskApply
   },
   data() {
     return {
@@ -223,7 +217,7 @@ export default {
       current: 1,
       size: this.GLOBAL.pageSize,
       total: 0,
-      viewType: "APPLY",
+      editType: "APPLY",
       ABLE_TYPE: {
         0: "取消",
         1: "启用"
@@ -293,24 +287,26 @@ export default {
         cancelButtonClass: "el-button--danger is-plain",
         confirmButtonClass: "el-button--primary",
         type: "warning"
-      }).then(async () => {
-        const data = await cancelOrRestartTaskApply({
-          id: row.id,
-          status: "CANCEL"
-        }).catch(() => {});
-        if (!data) return;
-        this.$message.success("操作成功!");
-        this.getList();
-      });
+      })
+        .then(async () => {
+          const data = await cancelOrRestartTaskApply({
+            id: row.id,
+            status: "CANCEL"
+          }).catch(() => {});
+          if (!data) return;
+          this.$message.success("操作成功!");
+          this.getList();
+        })
+        .catch(() => {});
     },
     toEdit(row) {
       this.curExamTask = row;
-      this.viewType = "APPLY";
+      this.editType = "APPLY";
       this.$refs.ModifyTaskApply.open();
     },
     toPreview(row) {
       this.curExamTask = row;
-      this.viewType = "VIEW";
+      this.editType = "PREVIEW";
       this.$refs.ModifyTaskApply.open();
     }
   }

+ 12 - 16
src/modules/exam/views/TaskPaperManage.vue

@@ -6,7 +6,7 @@
           <course-select
             ref="CourseSelect"
             v-model.trim="filter.courseCode"
-            placeholder="请输入内容"
+            placeholder="请选择"
             clearable
           ></course-select>
         </el-form-item>
@@ -135,16 +135,10 @@
 </template>
 
 <script>
-import CourseSelect from "../../../components/base/CourseSelect";
-import PaperNumberSelect from "../../../components/base/PaperNumberSelect";
 import { taskPaperListPage, ableTaskPaper } from "../api";
 
 export default {
   name: "task-paper-manage",
-  components: {
-    CourseSelect,
-    PaperNumberSelect
-  },
   data() {
     return {
       filter: {
@@ -241,15 +235,17 @@ export default {
         confirmButtonClass: "el-button--primary",
         dangerouslyUseHTMLString: true,
         type: "warning"
-      }).then(async () => {
-        const enable = !row.enable;
-        await ableTaskPaper({
-          id: row.id,
-          enable
-        });
-        row.enable = enable;
-        this.$message.success("操作成功!");
-      });
+      })
+        .then(async () => {
+          const enable = !row.enable;
+          await ableTaskPaper({
+            id: row.id,
+            enable
+          });
+          row.enable = enable;
+          this.$message.success("操作成功!");
+        })
+        .catch(() => {});
     },
     toEdit(row) {
       this.curPaper = row;

+ 9 - 16
src/modules/exam/views/TaskReviewManage.vue

@@ -15,7 +15,7 @@
           <card-rule-select
             ref="CardRuleSelect"
             v-model.trim="filter.cardRuleId"
-            placeholder="请输入内容"
+            placeholder="请选择"
             clearable
           ></card-rule-select>
         </el-form-item>
@@ -23,7 +23,7 @@
           <course-select
             ref="CourseSelect"
             v-model.trim="filter.courseCode"
-            placeholder="请输入内容"
+            placeholder="请选择"
             clearable
           ></course-select>
         </el-form-item>
@@ -194,7 +194,7 @@
     <!-- ModifyTaskApply -->
     <modify-task-apply
       ref="ModifyTaskApply"
-      :view-type="viewType"
+      :edit-type="editType"
       :instance="curExamTask"
       @modified="getList"
     ></modify-task-apply>
@@ -235,10 +235,6 @@
 
 <script>
 import ModifyTaskApply from "../components/ModifyTaskApply";
-import CardRuleSelect from "../../../components/base/CardRuleSelect";
-import CourseSelect from "../../../components/base/CourseSelect";
-import PaperNumberSelect from "../../../components/base/PaperNumberSelect";
-import QuestionTeacherSelect from "../../../components/base/QuestionTeacherSelect";
 import { AUDITING_RESULT, AUDITING_STATUS } from "@/constants/enumerate";
 import { taskReviewListPage, batchUpdateTaskReview } from "../api";
 import { download, randomCode } from "@/plugins/utils";
@@ -246,11 +242,7 @@ import { download, randomCode } from "@/plugins/utils";
 export default {
   name: "task-review-manage",
   components: {
-    ModifyTaskApply,
-    CardRuleSelect,
-    CourseSelect,
-    PaperNumberSelect,
-    QuestionTeacherSelect
+    ModifyTaskApply
   },
   data() {
     return {
@@ -267,7 +259,7 @@ export default {
       current: 1,
       size: this.GLOBAL.pageSize,
       total: 0,
-      viewType: "APPLY",
+      editType: "APPLY",
       AUDITING_RESULT,
       AUDITING_STATUS,
       caches: {},
@@ -323,7 +315,7 @@ export default {
     }
   },
   created() {
-    this.getList();
+    this.toPage(1);
   },
   methods: {
     async getList() {
@@ -344,6 +336,7 @@ export default {
     toPage(page) {
       this.current = page;
       this.getList();
+      this.multipleSelection = [];
     },
     handleSelectionChange(val) {
       this.multipleSelection = val.map(item => item.id);
@@ -422,12 +415,12 @@ export default {
     },
     toEdit(row) {
       this.curExamTask = row;
-      this.viewType = "AUDIT";
+      this.editType = "AUDIT";
       this.$refs.ModifyTaskApply.open();
     },
     toPreview(row) {
       this.curExamTask = row;
-      this.viewType = "PREVIEW";
+      this.editType = "PREVIEW";
       this.$refs.ModifyTaskApply.open();
     }
   }

+ 69 - 0
src/modules/print/api.js

@@ -0,0 +1,69 @@
+import { $get, $post } from "@/plugins/axios";
+
+// other
+// 印刷计划模糊查询
+export const printPlanQuery = param => {
+  return $get("/api/exam/print/query", { param });
+};
+// 考点模糊查询
+export const placeQuery = param => {
+  return $get("/api/exam/print_data/place_query", { param });
+};
+// 考场模糊查询
+export const roomQuery = param => {
+  return $get("/api/exam/print_data/room_query", { param });
+};
+
+// print-plan
+export const printPlanListPage = datas => {
+  return $get("/api/exam/print/list", datas);
+};
+export const updatePrintPlan = datas => {
+  return $post("/api/exam/print/save", datas);
+};
+export const removePrintPlan = id => {
+  return $post("/api/exam/print/remove", { id });
+};
+export const printPlanTemplateList = datas => {
+  return $get("/api/exam/print/get_template", datas);
+};
+// business-data-export
+export const businessDataListPage = datas => {
+  return $get("/api/exam/print_data/list", datas);
+};
+export const businessTotalData = datas => {
+  return $get("/api/exam/print_data/total", datas);
+};
+
+// business-data-detail
+export const businessDataDetailListPage = datas => {
+  return $get("/api/exam/print_data/detail", datas);
+};
+
+// plan-link-paper
+export const planLinkPaperListPage = datas => {
+  return $get("/api/exam/print_relate/list", datas);
+};
+export const updatePlanLinkPaper = datas => {
+  return $post("/api/exam/print_relate/update", datas);
+};
+export const linkPaperNumberList = datas => {
+  return $get("/api/exam/print_relate/get_paper_numbers", datas);
+};
+
+// print-task-manage
+export const printTaskListPage = datas => {
+  return $get("/api/exam/print_task/list", datas);
+};
+export const submitPrintTask = id => {
+  return $post("/api/exam/print_task/submit", { id });
+};
+export const cancelPrintTask = id => {
+  return $post("/api/exam/print_task/cancel", { id });
+};
+export const printTaskTotalInfo = datas => {
+  return $get("/api/exam/print_task/total", datas);
+};
+export const getPrintTaskPdf = id => {
+  return $post("/api/exam/print_task/view_pdf", { id });
+};

+ 256 - 0
src/modules/print/components/ModifyPlanPaper.vue

@@ -0,0 +1,256 @@
+<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
+        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 }}</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>
+          <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>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="relatePaperType" label="关联类型:">
+              <el-select
+                v-model="modalForm.relatePaperType"
+                placeholder="请选择"
+              >
+                <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 }}</span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="未曝光类型:">
+              <span>{{ instance.unexposedPaperType }}</span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </div>
+
+    <div class="part-box part-box-pad part-box-border">
+      <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 class="td-link">
+            <span @click="downloadPaper(attachment)" title="点击下载试卷">
+              <i class="icon icon-download-act mr-1"></i
+              >{{ attachment.filename }}
+            </span>
+          </td>
+          <td
+            class="td-link"
+            :rowspan="paperAttachments.length"
+            v-if="index === 0"
+          >
+            <span @click="toViewCard"
+              ><i class="icon icon-circle-right mr-1"></i>查看答题卡</span
+            >
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <div slot="footer">
+      <el-button type="primary" :disabled="isSubmit" @click="submit"
+        >确认</el-button
+      >
+      <el-button type="danger" @click="cancel" plain>取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { updatePlanLinkPaper, linkPaperNumberList } from "../api";
+import { taskApplyDetail } from "../../exam/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: [],
+      cardId: "",
+      rules: {
+        paperNumber: [
+          {
+            required: true,
+            message: "请选择试卷编号",
+            trigger: "change"
+          }
+        ],
+        relatePaperType: [
+          {
+            required: true,
+            message: "请选择关联类型",
+            trigger: "change"
+          }
+        ]
+      }
+    };
+  },
+  methods: {
+    async getAttachments() {
+      const data = await taskApplyDetail(this.instance.id);
+      this.paperAttachments = data.paperAttachmentIds
+        ? JSON.parse(data.paperAttachmentIds)
+        : [];
+      this.cardId = data.cardId;
+    },
+    async getPapers() {
+      this.papers = await linkPaperNumberList({
+        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.getPapers();
+      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;
+    },
+    downloadPaper(attachment) {
+      window.open(attachment.path);
+    },
+    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;
+      let datas = {
+        ...this.modalForm
+      };
+      const data = await updatePlanLinkPaper(datas).catch(() => {});
+      this.isSubmit = false;
+      if (!data) return;
+
+      this.$message.success("保存成功!");
+      this.$emit("modified");
+      this.cancel();
+    }
+  }
+};
+</script>

+ 520 - 0
src/modules/print/components/ModifyPrintPlan.vue

@@ -0,0 +1,520 @@
+<template>
+  <el-dialog
+    class="modify-print-plan"
+    :visible.sync="modalIsShow"
+    :title="title"
+    top="10px"
+    width="700px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      label-width="110px"
+      :rules="rules"
+      :model="modalForm"
+    >
+      <div class="part-box part-box-pad part-box-border">
+        <h4 class="part-box-tips">基本信息:</h4>
+        <el-form-item prop="name" label="印刷计划名称:">
+          <el-input
+            v-model.trim="modalForm.name"
+            clearable
+            placeholder="请输入"
+            :disabled="!editable"
+          ></el-input>
+        </el-form-item>
+        <el-form-item prop="examStartTime" label="考试时间:">
+          <el-date-picker
+            v-model="createTime"
+            type="datetimerange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="timestamp"
+            align="right"
+            unlink-panels
+            :disabled="!editable"
+            @change="dateChange"
+          >
+          </el-date-picker>
+        </el-form-item>
+      </div>
+
+      <div class="part-box part-box-pad part-box-border">
+        <h4 class="part-box-tips">试卷&题卡印品:</h4>
+        <el-form-item prop="printContent" label="试卷、题卡:">
+          <el-checkbox-group
+            v-model="modalForm.printContent"
+            :disabled="!editable"
+          >
+            <el-checkbox
+              v-for="(val, key) in PRINT_CONTENT_TYPE"
+              :key="key"
+              :label="key"
+              >{{ val }}</el-checkbox
+            >
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item
+          prop="backupMethod"
+          label="试卷备份方式:"
+          :required="!!modalForm.printContent.length"
+        >
+          <el-select
+            v-model="modalForm.backupMethod"
+            class="mr-2"
+            size="small"
+            placeholder="请选择"
+            :disabled="!editable"
+          >
+            <el-option
+              v-for="(val, key) in PAPER_BACKUP_TYPE"
+              :key="key"
+              :value="key"
+              :label="val"
+            ></el-option>
+          </el-select>
+          <el-input-number
+            class="mr-1"
+            v-model="modalForm.backupCount"
+            size="small"
+            :min="1"
+            :max="200"
+            :step="1"
+            step-strictly
+            :controls="false"
+            :disabled="!editable"
+          ></el-input-number>
+          <span>份</span>
+        </el-form-item>
+        <el-form-item
+          prop="drawRule"
+          label="抽卷规则:"
+          :required="!!modalForm.printContent.length"
+        >
+          <el-radio-group v-model="modalForm.drawRule" :disabled="!editable">
+            <el-radio
+              v-for="(val, key) in DRAW_RULE_TYPE"
+              :label="key"
+              :key="key"
+              >{{ val }}</el-radio
+            >
+          </el-radio-group>
+          <p class="tips-info">
+            1.只抽取一次:不同印刷计划下,同一试卷编号下的卷型只能被抽取一次;
+          </p>
+          <p class="tips-info">
+            2.可反复抽取:不同印刷计划下,同一试卷编号下的卷型可重复抽取,系统默认优先抽取未曝光卷型。
+          </p>
+        </el-form-item>
+      </div>
+
+      <div class="part-box part-box-pad part-box-border">
+        <h4 class="part-box-tips">变量印品:</h4>
+        <el-form-item
+          v-for="(item, index) in modalForm.variableContent"
+          :key="item.type"
+          :label="`${TEMPLATE_CLASSIFY[item.type]}:`"
+          :prop="`variableContent.${index}.templateId`"
+          :rules="{
+            required: false,
+            validator: templateValidator,
+            trigger: 'change'
+          }"
+          :required="!!item.templateId.length"
+        >
+          <el-checkbox-group
+            v-model="item.templateId"
+            :disabled="!editable"
+            @change="vals => tempChange(vals, `variableContent.${index}`)"
+          >
+            <el-checkbox
+              v-for="temp in templateSources[item.type]"
+              :label="temp.id"
+              :key="temp.id"
+              >{{ temp.name }}</el-checkbox
+            >
+          </el-checkbox-group>
+          <el-select
+            v-model="item.backupMethod"
+            class="mr-2"
+            size="small"
+            placeholder="请选择"
+            :disabled="!editable"
+          >
+            <el-option
+              v-for="(val, key) in PRINT_BACKUP_TYPE"
+              :key="key"
+              :value="key"
+              :label="val"
+            ></el-option>
+          </el-select>
+          <el-input-number
+            v-model="item.backupCount"
+            class="mr-1"
+            size="small"
+            :min="1"
+            :max="200"
+            :step="1"
+            step-strictly
+            :controls="false"
+            :disabled="!editable"
+          ></el-input-number>
+          <span>份</span>
+        </el-form-item>
+      </div>
+
+      <div class="part-box part-box-pad part-box-border">
+        <h4 class="part-box-tips">普通印品:</h4>
+        <el-form-item
+          v-for="(item, index) in modalForm.ordinaryContent"
+          :key="item.type"
+          :label="`${TEMPLATE_CLASSIFY[item.type]}:`"
+          label-width="130px"
+          :prop="`ordinaryContent.${index}.templateId`"
+          :rules="{
+            required: false,
+            validator: templateValidator,
+            trigger: 'change'
+          }"
+          :required="!!item.templateId.length"
+        >
+          <el-checkbox-group
+            v-model="item.templateId"
+            :disabled="!editable"
+            @change="vals => tempChange(vals, `ordinaryContent.${index}`)"
+          >
+            <el-checkbox
+              v-for="temp in templateSources[item.type]"
+              :label="temp.id"
+              :key="temp.id"
+              >{{ temp.name }}</el-checkbox
+            >
+          </el-checkbox-group>
+          <el-select
+            v-model="item.backupMethod"
+            class="mr-2"
+            size="small"
+            placeholder="请选择"
+            :disabled="!editable"
+          >
+            <el-option
+              v-for="(val, key) in PRINT_BACKUP_TYPE"
+              :key="key"
+              :value="key"
+              :label="val"
+            ></el-option>
+          </el-select>
+          <el-input-number
+            v-model="item.backupCount"
+            class="mr-1"
+            size="small"
+            :min="1"
+            :max="200"
+            :step="1"
+            step-strictly
+            :controls="false"
+            :disabled="!editable"
+          ></el-input-number>
+          <span>份</span>
+        </el-form-item>
+      </div>
+
+      <el-form-item prop="selectedPrint"></el-form-item>
+    </el-form>
+
+    <div slot="footer">
+      <el-button
+        v-if="editable"
+        type="primary"
+        :disabled="isSubmit"
+        @click="submit"
+        >确认</el-button
+      >
+      <el-button type="danger" @click="cancel" plain>取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import {
+  PRINT_CONTENT_TYPE,
+  DRAW_RULE_TYPE,
+  PRINT_BACKUP_TYPE,
+  PAPER_BACKUP_TYPE,
+  TEMPLATE_CLASSIFY
+} from "@/constants/enumerate";
+// import { deepCopy } from "@/plugins/utils";
+import { updatePrintPlan, printPlanTemplateList } from "../api";
+
+const initModalForm = {
+  id: null,
+  name: "",
+  examStartTime: "",
+  examEndTime: "",
+  printContent: [],
+  backupMethod: "PLACE",
+  backupCount: 1,
+  drawRule: "ONE",
+  variableContent: [
+    {
+      type: "SIGN",
+      templateId: [],
+      oldTemplateId: [],
+      backupMethod: "ROOM",
+      backupCount: 1
+    },
+    {
+      type: "PACKAGE",
+      templateId: [],
+      oldTemplateId: [],
+      backupMethod: "STUDENT",
+      backupCount: 1
+    }
+  ],
+  ordinaryContent: [
+    {
+      type: "CHECK_IN",
+      templateId: [],
+      oldTemplateId: [],
+      backupMethod: "ROOM",
+      backupCount: 1
+    }
+  ]
+};
+
+export default {
+  name: "modify-print-plan",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      }
+    },
+    editType: {
+      type: String,
+      default: "ADD",
+      validator: val => ["ADD", "PREVIEW", "EDIT"].includes(val)
+    }
+  },
+  computed: {
+    isEdit() {
+      return !!this.instance.id;
+    },
+    title() {
+      const names = {
+        ADD: "新增印刷计划",
+        PREVIEW: "印刷计划详情",
+        EDIT: "编辑印刷计划"
+      };
+      return names[this.editType];
+    },
+    editable() {
+      return this.editType !== "PREVIEW";
+    }
+  },
+  data() {
+    const printContentValidator = (rule, value, callback) => {
+      if (value.includes("PAPER") && !value.includes("CARD")) {
+        callback(new Error("选择了试卷,同时必须选择题卡"));
+      } else {
+        callback();
+      }
+    };
+    const backupMethodValidator = (rule, value, callback) => {
+      if (!this.modalForm.printContent.length) {
+        return callback();
+      }
+      if (!value) {
+        return callback(new Error("请选择备份方式"));
+      }
+      if (!this.modalForm.backupCount) {
+        return callback(new Error("请输入备份数量"));
+      }
+
+      callback();
+    };
+    const selectedPrintValidator = (rule, value, callback) => {
+      const printInfo = [
+        ...this.modalForm.variableContent,
+        ...this.modalForm.ordinaryContent
+      ];
+      const hasPrintInfo = printInfo.some(item => item.templateId.length);
+
+      if (hasPrintInfo || this.modalForm.printContent.length) {
+        callback();
+      } else {
+        callback(new Error("必须选择一项印品"));
+      }
+    };
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      createTime: [],
+      PRINT_CONTENT_TYPE,
+      DRAW_RULE_TYPE,
+      PRINT_BACKUP_TYPE,
+      PAPER_BACKUP_TYPE,
+      TEMPLATE_CLASSIFY,
+      variableContent: [],
+      ordinaryContent: [],
+      templateSources: {},
+      rules: {
+        name: [
+          {
+            required: true,
+            message: "请输入印刷计划名称,长度不要超过100个字符",
+            max: 100,
+            trigger: "change"
+          }
+        ],
+        examStartTime: [
+          {
+            required: true,
+            message: "请设置考试时间",
+            trigger: "change"
+          }
+        ],
+        printContent: [
+          {
+            required: false,
+            validator: printContentValidator,
+            trigger: "change"
+          }
+        ],
+        backupMethod: [
+          {
+            required: false,
+            validator: backupMethodValidator,
+            trigger: "change"
+          }
+        ],
+        drawRule: [
+          {
+            required: false,
+            message: "请选择抽卷规则",
+            trigger: "change"
+          }
+        ],
+        selectedPrint: [
+          {
+            required: false,
+            validator: selectedPrintValidator,
+            trigger: "change"
+          }
+        ]
+      }
+    };
+  },
+  mounted() {
+    this.getTemplates();
+  },
+  methods: {
+    async getTemplates() {
+      const data = await printPlanTemplateList();
+      const templateSources = {};
+      const templates = [...data.variable, ...data.ordinary];
+      templates.forEach(item => {
+        templateSources[item.type] = item.template;
+      });
+      this.templateSources = templateSources;
+    },
+    templateValidator(rule, value, callback) {
+      const [field, index] = rule.field.split(".");
+      const val = this.modalForm[field][index];
+      if (val.templateId.length) {
+        if (!val.backupMethod) {
+          return callback(new Error("请选择备份方式"));
+        }
+        if (!val.backupCount) {
+          return callback(new Error("请输入备份数量"));
+        }
+        callback();
+      } else {
+        callback();
+      }
+    },
+    tempChange(vals, name) {
+      const [field, index] = name.split(".");
+      const info = this.modalForm[field][index];
+      const newVals = vals.filter(item => !info.oldTemplateId.includes(item));
+      info.templateId = newVals;
+      info.oldTemplateId = newVals;
+
+      this.$refs.modalFormComp.validateField("selectedPrint");
+    },
+    initData(val) {
+      if (val.id) {
+        this.createTime = [val.examStartTime, val.examEndTime];
+        this.modalForm = this.$objAssign(initModalForm, val);
+        const transformInfo = item => {
+          return {
+            type: item.type,
+            templateId: [item.templateId],
+            oldTemplateId: [item.templateId],
+            backupMethod: item.backupMethod,
+            backupCount: item.backupCount
+          };
+        };
+        this.modalForm.variableContent = val.variableContent.map(transformInfo);
+        this.modalForm.ordinaryContent = val.ordinaryContent.map(transformInfo);
+      } else {
+        this.modalForm = { ...initModalForm };
+        this.createTime = [];
+      }
+    },
+    visibleChange() {
+      this.initData(this.instance);
+
+      this.$nextTick(() => {
+        this.$refs.modalFormComp.clearValidate();
+      });
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    dateChange() {
+      if (this.createTime) {
+        this.modalForm.examStartTime = this.createTime[0];
+        this.modalForm.examEndTime = this.createTime[1];
+      } else {
+        this.modalForm.examStartTime = "";
+        this.modalForm.examEndTime = "";
+      }
+    },
+    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 transformInfo = item => {
+        return {
+          type: item.type,
+          templateId: item.templateId.join(),
+          backupMethod: item.backupMethod,
+          backupCount: item.backupCount
+        };
+      };
+      datas.variableContent = this.modalForm.variableContent.map(transformInfo);
+      datas.ordinaryContent = this.modalForm.ordinaryContent.map(transformInfo);
+      const data = await updatePrintPlan(datas).catch(() => {});
+      this.isSubmit = false;
+      if (!data) return;
+
+      this.$message.success("保存成功!");
+      this.$emit("modified");
+      this.cancel();
+    }
+  }
+};
+</script>

+ 134 - 5
src/modules/print/views/BusinessDataDetail.vue

@@ -1,15 +1,144 @@
 <template>
-  <div class="BusinessDataDetail">
-    BusinessDataDetail
+  <div class="business-data-detail">
+    <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="印刷计划:">
+          <print-plan-select
+            v-model.trim="filter.printPlanId"
+            placeholder="请选择"
+            clearable
+          ></print-plan-select>
+        </el-form-item>
+        <el-form-item label="课程(代码):" label-width="110px">
+          <course-select
+            v-model.trim="filter.courseCode"
+            placeholder="请选择"
+            clearable
+          ></course-select>
+        </el-form-item>
+        <el-form-item label="试卷编号:">
+          <paper-number-select
+            ref="PaperNumberSelect"
+            v-model="filter.paperNumber"
+            placeholder="请选择"
+            clearable
+          ></paper-number-select>
+        </el-form-item>
+        <el-form-item label="考点:" label-width="55px">
+          <place-select
+            v-model.trim="filter.examPlace"
+            placeholder="请选择"
+            clearable
+          ></place-select>
+        </el-form-item>
+        <el-form-item label="考场:" label-width="55px">
+          <room-select
+            v-model.trim="filter.examRoom"
+            placeholder="请选择"
+            clearable
+          ></room-select>
+        </el-form-item>
+        <el-form-item label="考生:" label-width="55px">
+          <el-input
+            v-model="filter.studentParams"
+            placeholder="考生/学号/姓名"
+            clearable
+          ></el-input>
+        </el-form-item>
+        <el-form-item label-width="0px">
+          <el-button type="primary" icon="icon icon-search" @click="search"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <div class="part-box">
+      <el-table ref="TableList" :data="dataList" border stripe>
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          align="center"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column
+          prop="printPlanName"
+          label="印刷计划"
+        ></el-table-column>
+        <el-table-column prop="courseNameCode" label="课程(代码)">
+        </el-table-column>
+        <el-table-column prop="specialty" label="适用专业(方向)">
+        </el-table-column>
+        <el-table-column prop="paperNumber" label="试卷编码"></el-table-column>
+        <el-table-column prop="examPlace" label="考点"> </el-table-column>
+        <el-table-column prop="examRoom" label="考场"> </el-table-column>
+        <el-table-column prop="ticketNumber" label="考号"> </el-table-column>
+        <el-table-column prop="studentCode" label="学号"> </el-table-column>
+        <el-table-column prop="studentName" label="姓名"> </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 { businessDataDetailListPage } from "../api";
+
 export default {
-  name: "BusinessDataDetail",
+  name: "business-data-detail",
   data() {
-    return {};
+    return {
+      filter: {
+        printPlanId: "",
+        courseCode: "",
+        paperNumber: "",
+        examPlace: "",
+        examRoom: "",
+        studentParams: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: []
+    };
+  },
+  created() {
+    const curBusiness = this.$ls.get("curBusiness");
+    if (curBusiness) this.filter = this.$objAssign(this.filter, curBusiness);
+    this.search();
+  },
+  methods: {
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      const data = await businessDataDetailListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    search() {
+      this.toPage(1);
+    }
   },
-  methods: {}
+  beforeDestroy() {
+    this.$ls.remove("curBusiness");
+  }
 };
 </script>

+ 226 - 5
src/modules/print/views/BusinessDataExport.vue

@@ -1,15 +1,236 @@
 <template>
-  <div class="BusinessDataExport">
-    BusinessDataExport
+  <div class="business-data-export">
+    <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="印刷计划:">
+          <print-plan-select
+            v-model.trim="filter.printPlanId"
+            placeholder="请选择"
+            clearable
+            @change="planChange"
+          ></print-plan-select>
+        </el-form-item>
+        <el-form-item label="课程(代码):" label-width="110px">
+          <course-select
+            v-model.trim="filter.courseCode"
+            placeholder="请选择"
+            clearable
+          ></course-select>
+        </el-form-item>
+        <el-form-item label="试卷编号:">
+          <paper-number-select
+            ref="PaperNumberSelect"
+            v-model="filter.paperNumber"
+            placeholder="请选择"
+            clearable
+          ></paper-number-select>
+        </el-form-item>
+        <el-form-item label="考点:" label-width="55px">
+          <place-select
+            v-model.trim="filter.examPlace"
+            placeholder="请选择"
+            clearable
+          ></place-select>
+        </el-form-item>
+        <el-form-item label="考场:" label-width="55px">
+          <room-select
+            v-model.trim="filter.examRoom"
+            placeholder="请选择"
+            clearable
+          ></room-select>
+        </el-form-item>
+        <el-form-item label="考生:" label-width="55px">
+          <el-input
+            v-model="filter.studentParams"
+            placeholder="考生/学号/姓名"
+            clearable
+          ></el-input>
+        </el-form-item>
+        <el-form-item label-width="0px">
+          <el-button type="primary" icon="icon icon-search" @click="search"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+      <div class="part-box-action">
+        <el-button icon="icon icon-download-act">
+          <a :href="downloadUrl" download="考务数据模板.xlsx"
+            >考务数据模板下载</a
+          >
+        </el-button>
+        <el-button icon="icon icon-download" type="primary" @click="toExport">
+          导出查询结果
+        </el-button>
+        <upload-button
+          btn-icon="icon icon-share"
+          btn-type="warning"
+          btn-content="导入"
+          :upload-url="uploadUrl"
+          :format="['xls', 'xlsx']"
+          :upload-data="uploadData"
+          @upload-error="uplaodError"
+          @upload-success="uploadSuccess"
+          :disabled="!canImport"
+        >
+        </upload-button>
+      </div>
+    </div>
+
+    <div class="part-box">
+      <el-table ref="TableList" :data="dataList" border stripe>
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          align="center"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column
+          prop="printPlanName"
+          label="印刷计划"
+        ></el-table-column>
+        <el-table-column prop="examStartDate" label="考试日期">
+          <span slot-scope="scope">{{
+            scope.row.examStartDate | 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="courseNameCode" label="课程(代码)">
+        </el-table-column>
+        <el-table-column prop="paperNumber" label="试卷编码"></el-table-column>
+        <el-table-column prop="examPlace" label="考点"> </el-table-column>
+        <el-table-column prop="packageCount" label="卷袋数" width="80">
+        </el-table-column>
+        <el-table-column prop="totalSubjects" label="科次" width="80">
+        </el-table-column>
+        <el-table-column
+          class-name="action-column"
+          label="操作"
+          align="center"
+          width="120px"
+        >
+          <template slot-scope="scope">
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-circle-right"
+              @click="toPreview(scope.row)"
+              title="查看详情"
+            ></el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="part-box-flex">
+      <div>
+        <span class="mr-2"
+          >科次总计:<i class="color-primary">{{ totalData.totalSubjects }}</i>
+          科次</span
+        >
+        <span
+          >卷袋数量总计:<i class="color-primary">{{
+            totalData.packageCount
+          }}</i>
+          个</span
+        >
+      </div>
+      <el-pagination
+        background
+        layout="total,prev, pager, next"
+        :current-page="current"
+        :total="total"
+        :page-size="size"
+        @current-change="toPage"
+      >
+      </el-pagination>
+    </div>
   </div>
 </template>
 
 <script>
+import { businessDataListPage, businessTotalData } from "../api";
+import UploadButton from "@/components/UploadButton";
+
 export default {
-  name: "BusinessDataExport",
+  name: "business-data-export",
+  components: { UploadButton },
   data() {
-    return {};
+    return {
+      filter: {
+        printPlanId: "",
+        courseCode: "",
+        paperNumber: "",
+        examPlace: "",
+        examRoom: "",
+        studentParams: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: [],
+      curRow: {},
+      totalData: {},
+      curPrintPlan: {},
+      // import
+      downloadUrl: "/temps/考务数据模板.xlsx",
+      uploadUrl: "/api/sys/user/import",
+      uploadData: {
+        schoolId: this.$ls.get("schoolId"),
+        userId: this.$ls.get("user", { id: "" }).id
+      }
+    };
+  },
+  computed: {
+    canImport() {
+      return (
+        this.curPrintPlan && ["NEW", "READY"].includes(this.curPrintPlan.status)
+      );
+    }
+  },
+  created() {
+    this.search();
   },
-  methods: {}
+  methods: {
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      const data = await businessDataListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    async getTotalData() {
+      this.totalData = await businessTotalData(this.filter);
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    search() {
+      this.toPage(1);
+      this.getTotalData();
+    },
+    planChange(plan) {
+      this.curPrintPlan = plan;
+    },
+    uplaodError(errorData) {
+      this.$notify.error({ title: "错误提示", message: errorData.message });
+    },
+    uploadSuccess(res) {
+      this.$message.success("上传成功!");
+      this.getList();
+    },
+    toExport() {},
+    toPreview(row) {
+      this.$ls.set("curBusiness", this.$objAssign(this.filter, row));
+      this.$router.push({ name: "BusinessDataDetail" });
+    }
+  }
 };
 </script>

+ 170 - 5
src/modules/print/views/PlanLinkPaper.vue

@@ -1,15 +1,180 @@
 <template>
-  <div class="PlanLinkPaper">
-    PlanLinkPaper
+  <div class="plan-link-paper">
+    <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="filter.relateType"
+            style="width: 100px;"
+            placeholder="请选择"
+          >
+            <el-option
+              v-for="(val, key) in RELATE_TYPE"
+              :key="key"
+              :value="key"
+              :label="val"
+            ></el-option>
+          </el-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="课程(代码):" label-width="110px">
+          <course-select
+            v-model.trim="filter.courseCode"
+            placeholder="请选择"
+            clearable
+          ></course-select>
+        </el-form-item>
+        <el-form-item label="试卷编号:">
+          <paper-number-select
+            ref="PaperNumberSelect"
+            v-model="filter.paperNumber"
+            placeholder="请选择"
+            clearable
+          ></paper-number-select>
+        </el-form-item>
+        <el-form-item label-width="0px">
+          <el-button type="primary" icon="icon icon-search" @click="search"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <div class="part-box">
+      <el-table ref="TableList" :data="dataList" border stripe>
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          align="center"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column
+          prop="printPlanName"
+          label="印刷计划"
+        ></el-table-column>
+        <el-table-column prop="paperNumber" label="试卷编号"></el-table-column>
+        <el-table-column prop="courseName" label="课程(代码)">
+          <span slot-scope="scope"
+            >{{ scope.row.courseName }}({{ scope.row.courseCode }})</span
+          >
+        </el-table-column>
+        <el-table-column prop="specialty" label="适用专业(方向)">
+        </el-table-column>
+        <el-table-column prop="cardRuleName" label="题卡规则">
+        </el-table-column>
+        <el-table-column prop="userName" label="命题老师"> </el-table-column>
+        <el-table-column prop="exposedPaperType" label="已曝光类型">
+        </el-table-column>
+        <el-table-column prop="unexposedPaperType" label="未曝光类型">
+        </el-table-column>
+        <el-table-column prop="relatePaperType" label="关联卷型">
+        </el-table-column>
+        <el-table-column label="关联状态" width="80">
+          <span slot-scope="scope">
+            {{ scope.row.relatePaperType ? "已关联" : "未关联" }}
+          </span>
+        </el-table-column>
+        <el-table-column prop="totalSubjects" label="总科次" width="80">
+        </el-table-column>
+        <el-table-column
+          class-name="action-column"
+          label="操作"
+          align="center"
+          width="80px"
+        >
+          <template slot-scope="scope">
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-edit"
+              @click="toEdit(scope.row)"
+              title="关联试卷"
+            ></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>
+
+    <!-- ModifyPlanPaper -->
+    <modify-plan-paper
+      :instance="curRow"
+      @modified="getList"
+      ref="ModifyPlanPaper"
+    ></modify-plan-paper>
   </div>
 </template>
 
 <script>
+import { planLinkPaperListPage } from "../api";
+import ModifyPlanPaper from "../components/ModifyPlanPaper";
+
+export const RELATE_TYPE = {
+  RELATE: "已关联",
+  NOT_RELATE: "未关联"
+};
+
 export default {
-  name: "PlanLinkPaper",
+  name: "plan-link-paper",
+  components: { ModifyPlanPaper },
   data() {
-    return {};
+    return {
+      filter: {
+        printPlanId: "",
+        courseCode: "",
+        paperNumber: "",
+        relateType: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: [],
+      curRow: {},
+      RELATE_TYPE
+    };
+  },
+  created() {
+    this.search();
   },
-  methods: {}
+  methods: {
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      const data = await planLinkPaperListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    search() {
+      this.toPage(1);
+    },
+    toEdit(row) {
+      this.curRow = row;
+      this.$refs.ModifyPlanPaper.open();
+    }
+  }
 };
 </script>

+ 245 - 3
src/modules/print/views/PrintPlanManage.vue

@@ -1,15 +1,257 @@
 <template>
   <div class="print-plan-manage">
-    print-plan-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="印刷计划:">
+          <print-plan-select
+            v-model.trim="filter.printPlanId"
+            placeholder="请选择"
+            clearable
+          ></print-plan-select>
+        </el-form-item>
+        <el-form-item label="当前状态:">
+          <el-select
+            v-model="filter.status"
+            style="width: 142px;"
+            placeholder="请选择"
+            clearable
+          >
+            <el-option
+              v-for="(val, key) in PRINT_PLAN_STATUS"
+              :key="key"
+              :value="key"
+              :label="val"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="创建时间:">
+          <el-date-picker
+            v-model="createTime"
+            type="datetimerange"
+            :picker-options="pickerOptions"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="timestamp"
+            align="right"
+            unlink-panels
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label-width="0px">
+          <el-button type="primary" icon="icon icon-search" @click="search"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+      <div class="part-box-action">
+        <el-button icon="icon icon-plus" type="primary" @click="toAdd">
+          新建印刷计划
+        </el-button>
+      </div>
+    </div>
+
+    <div class="part-box">
+      <el-table ref="TableList" :data="dataList" border stripe>
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          align="center"
+          :index="indexMethod"
+        ></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
+          class-name="action-column"
+          label="操作"
+          align="center"
+          width="120px"
+        >
+          <template slot-scope="scope">
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-edit"
+              @click="toEdit(scope.row)"
+              title="修改"
+            ></el-button>
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-circle-right"
+              @click="toPreview(scope.row)"
+              title="查看"
+            ></el-button>
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-delete"
+              @click="toDelete(scope.row)"
+              title="删除"
+            ></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>
+
+    <!-- ModifyPrintPlan -->
+    <modify-print-plan
+      ref="ModifyPrintPlan"
+      :instance="curPrintPlan"
+      :edit-type="editType"
+      @modified="getList"
+    ></modify-print-plan>
   </div>
 </template>
 
 <script>
+import { printPlanListPage, removePrintPlan } from "../api";
+import { PRINT_PLAN_STATUS } from "@/constants/enumerate";
+import ModifyPrintPlan from "../components/ModifyPrintPlan";
+
 export default {
   name: "print-plan-manage",
+  components: { ModifyPrintPlan },
   data() {
-    return {};
+    return {
+      filter: {
+        printPlanId: "",
+        status: "",
+        startTime: "",
+        endTime: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: [],
+      curPrintPlan: {},
+      createTime: [],
+      editType: "ADD",
+      PRINT_PLAN_STATUS,
+      // date-picker
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "最近三个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+              picker.$emit("pick", [start, end]);
+            }
+          }
+        ]
+      }
+    };
+  },
+  created() {
+    this.search();
   },
-  methods: {}
+  methods: {
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      if (this.createTime) {
+        datas.startTime = this.createTime[0];
+        datas.endTime = this.createTime[1];
+      }
+      const data = await printPlanListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    search() {
+      this.toPage(1);
+    },
+    toAdd() {
+      this.curPrintPlan = {};
+      this.editType = "ADD";
+      this.$refs.ModifyPrintPlan.open();
+    },
+    toEdit(row) {
+      this.curPrintPlan = row;
+      this.editType = "EDIT";
+      this.$refs.ModifyPrintPlan.open();
+    },
+    toPreview(row) {
+      this.curPrintPlan = row;
+      this.editType = "PREVIEW";
+      this.$refs.ModifyPrintPlan.open();
+    },
+    toDelete(row) {
+      this.$confirm("确定要删除当前印刷计划吗?", "提示", {
+        cancelButtonClass: "el-button--danger is-plain",
+        confirmButtonClass: "el-button--primary",
+        type: "warning"
+      })
+        .then(async () => {
+          await removePrintPlan(row.id);
+          this.$message.success("删除成功!");
+          this.deletePageLastItem();
+        })
+        .catch(() => {});
+    }
+  }
 };
 </script>

+ 406 - 5
src/modules/print/views/PrintTaskManage.vue

@@ -1,15 +1,416 @@
 <template>
-  <div class="PrintTaskManage">
-    PrintTaskManage
+  <div class="print-task-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="印刷计划:">
+          <print-plan-select
+            v-model.trim="filter.printPlanId"
+            placeholder="请选择"
+            clearable
+          ></print-plan-select>
+        </el-form-item>
+        <el-form-item label="印刷状态:">
+          <el-select
+            v-model="filter.status"
+            style="width: 100px;"
+            placeholder="请选择"
+            clearable
+          >
+            <el-option
+              v-for="item in PRINT_STATUS_LIST"
+              :key="item.key"
+              :value="item.key"
+              :label="item.name"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="课程(代码):" label-width="110px">
+          <course-select
+            v-model.trim="filter.courseCode"
+            placeholder="请选择"
+            clearable
+          ></course-select>
+        </el-form-item>
+        <el-form-item label="试卷编号:">
+          <paper-number-select
+            ref="PaperNumberSelect"
+            v-model="filter.paperNumber"
+            placeholder="请选择"
+            clearable
+          ></paper-number-select>
+        </el-form-item>
+        <el-form-item label="考点:" label-width="55px">
+          <place-select
+            v-model.trim="filter.examPlace"
+            placeholder="请选择"
+            clearable
+          ></place-select>
+        </el-form-item>
+        <el-form-item label="考场:" label-width="55px">
+          <room-select
+            v-model.trim="filter.examRoom"
+            placeholder="请选择"
+            clearable
+          ></room-select>
+        </el-form-item>
+        <el-form-item label="考试日期:">
+          <el-date-picker
+            v-model="createTime"
+            type="daterange"
+            :picker-options="pickerOptions"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="timestamp"
+            align="right"
+            unlink-panels
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label-width="0px">
+          <el-button type="primary" icon="icon icon-search" @click="search"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+      <div class="part-box-action">
+        <el-button
+          icon="el-icon-download"
+          type="primary"
+          :disabled="loading"
+          @click="toExport"
+        >
+          批量下载PDF
+        </el-button>
+      </div>
+    </div>
+    <div class="part-box part-box-flex">
+      <p>
+        <span class="mr-4"
+          >科次总计:<i class="color-primary">{{ totalInfo.totalSubjects }}</i>
+          科次</span
+        >
+        <span class="mr-4"
+          >卷袋数量总计:<i class="color-primary">{{
+            totalInfo.packageCount
+          }}</i>
+          个</span
+        >
+        <span class="mr-4"
+          >A3印量累计:<i class="color-primary">{{ totalInfo.pageA3 }}</i>
+          张</span
+        >
+        <span class="mr-4"
+          >A4印量累计:<i class="color-primary">{{ totalInfo.pageA4 }}</i>
+          张</span
+        >
+      </p>
+      <p>
+        <span class="mr-4"
+          >A3剩余印量:<i class="color-primary">{{ totalInfo.pageA3Left }}</i>
+          张</span
+        >
+        <span
+          >A4剩余印量:<i class="color-primary">{{ totalInfo.pageA4Left }}</i>
+          张</span
+        >
+      </p>
+    </div>
+    <div class="part-box">
+      <el-table
+        ref="TableList"
+        :data="dataList"
+        border
+        stripe
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          align="center"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column prop="packageCode" label="卷袋编号"></el-table-column>
+        <el-table-column
+          prop="printPlanName"
+          label="印刷计划"
+          width="160"
+        ></el-table-column>
+        <el-table-column
+          prop="examDate"
+          label="考试日期"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="examTime"
+          label="考试时间"
+          width="100"
+        ></el-table-column>
+        <el-table-column prop="courseNameCode" label="课程(代码)" width="160">
+        </el-table-column>
+        <el-table-column
+          prop="paperNumber"
+          label="试卷编号"
+          width="120"
+        ></el-table-column>
+        <el-table-column prop="examPlace" label="考点"></el-table-column>
+        <el-table-column prop="examRoom" label="考场"></el-table-column>
+        <el-table-column prop="pageA3" label="单科次A3(页)"></el-table-column>
+        <el-table-column prop="pageA4" label="单科次A4(页)"></el-table-column>
+        <el-table-column prop="totalSubjects" label="科次"></el-table-column>
+        <el-table-column prop="totalPages" label="印刷小计"></el-table-column>
+        <el-table-column prop="status" label="印刷状态">
+          <span slot-scope="scope">{{
+            scope.row.status | printStatusFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column prop="validate" label="是否校验">
+          <span slot-scope="scope">{{ scope.row.validate ? "是" : "否" }}</span>
+        </el-table-column>
+        <el-table-column prop="printTime" label="打印开始时间" width="160">
+          <span slot-scope="scope">{{
+            scope.row.printTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column
+          prop="expectPrintTime"
+          label="打印完成时间"
+          width="160"
+        >
+          <span slot-scope="scope">{{
+            scope.row.expectPrintTime | timestampFilter
+          }}</span>
+        </el-table-column>
+        <el-table-column
+          class-name="action-column"
+          label="操作"
+          align="center"
+          width="120px"
+          fixed="right"
+        >
+          <template slot-scope="scope">
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-circle-share"
+              @click="toSubmit(scope.row)"
+              title="提交印刷"
+            ></el-button>
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-close-act"
+              @click="toCancel(scope.row)"
+              title="撤回提交"
+            ></el-button>
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-circle-right"
+              @click="toPreview(scope.row)"
+              title="查看"
+            ></el-button>
+            <el-button
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-download-act"
+              @click="toViewPdf(scope.row)"
+              title="查看pdf"
+            ></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 {
+  printTaskListPage,
+  submitPrintTask,
+  cancelPrintTask,
+  printTaskTotalInfo,
+  getPrintTaskPdf
+} from "../api";
+import { PRINT_STATUS_LIST } from "@/constants/enumerate";
+import { download, randomCode } from "@/plugins/utils";
+
 export default {
-  name: "PrintTaskManage",
+  name: "print-task-manage",
   data() {
-    return {};
+    return {
+      filter: {
+        printPlanId: "",
+        status: "",
+        courseCode: "",
+        paperNumber: "",
+        examPlace: "",
+        examRoom: "",
+        examStartTime: "",
+        examEndTime: ""
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      totalInfo: {},
+      dataList: [],
+      curRow: {},
+      createTime: [],
+      multipleSelection: [],
+      PRINT_STATUS_LIST,
+      loading: false,
+      // date-picker
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "最近三个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+              picker.$emit("pick", [start, end]);
+            }
+          }
+        ]
+      }
+    };
+  },
+  created() {
+    this.search();
   },
-  methods: {}
+  methods: {
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      if (this.createTime) {
+        datas.examStartTime = this.createTime[0];
+        datas.examEndTime = this.createTime[1];
+      }
+      const data = await printTaskListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+      this.multipleSelection = [];
+    },
+    search() {
+      this.toPage(1);
+      this.getTotalInfo();
+    },
+    async getTotalInfo() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size
+      };
+      if (this.createTime) {
+        datas.examStartTime = this.createTime[0];
+        datas.examEndTime = this.createTime[1];
+      }
+      this.totalInfo = await printTaskTotalInfo(datas);
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val.map(item => item.id);
+    },
+    toPreview(row) {},
+    toSubmit(row) {
+      this.$confirm("确定提交该印刷计划吗?", "提示", {
+        cancelButtonClass: "el-button--danger is-plain",
+        confirmButtonClass: "el-button--primary",
+        type: "warning"
+      })
+        .then(async () => {
+          const data = await submitPrintTask(row.id);
+          if (!data) return;
+
+          this.$message.success("提交成功!");
+          this.getList();
+        })
+        .catch(() => {});
+    },
+    toCancel(row) {
+      this.$confirm("确定撤回该印刷计划的提交吗?", "提示", {
+        cancelButtonClass: "el-button--danger is-plain",
+        confirmButtonClass: "el-button--primary",
+        type: "warning"
+      })
+        .then(async () => {
+          const data = await cancelPrintTask(row.id);
+          if (!data) return;
+
+          this.$message.success("撤回成功!");
+          this.getList();
+        })
+        .catch(() => {});
+    },
+    async toViewPdf(row) {
+      const data = await getPrintTaskPdf(row.id);
+      if (!data) return;
+      window.open(data.path);
+    },
+    async toExport() {
+      if (this.loading) return;
+
+      if (!this.multipleSelection.length) {
+        this.$message.error("请选择要下载的记录!");
+        return;
+      }
+
+      this.loading = true;
+      const res = await download({
+        type: "post",
+        url: this.GLOBAL.domain + "/api/exam/print_task/download_pdf",
+        data: {
+          examTaskIds: this.multipleSelection
+        },
+        fileName: `${Date.now()}${randomCode()}.zip`,
+        header: {
+          schoolId: this.$ls.get("schoolId"),
+          userId: this.$ls.get("user", { id: "" }).id
+        }
+      }).catch(error => {
+        this.$message.error(error + "文件下载失败,请重新尝试!");
+      });
+      this.loading = false;
+      if (!res) return;
+      this.$message.success("文件下载成功!");
+    }
+  }
 };
 </script>

+ 13 - 1
src/plugins/filters.js

@@ -3,7 +3,10 @@ import {
   TEMPLATE_CLASSIFY,
   EXAM_TASK_STATUS,
   AUDITING_STATUS,
-  AUDITING_RESULT
+  AUDITING_RESULT,
+  PRINT_PLAN_STATUS,
+  ROLE_TYPE,
+  PRINT_STATUS
 } from "../constants/enumerate";
 import { formatDate } from "../plugins/utils";
 
@@ -33,6 +36,15 @@ Vue.filter("auditStatusFilter", function(val) {
 Vue.filter("reviewStatusFilter", function(val) {
   return AUDITING_RESULT[val] || DEFAULT_FIELD;
 });
+Vue.filter("printPlanStatusFilter", function(val) {
+  return PRINT_PLAN_STATUS[val] || DEFAULT_FIELD;
+});
+Vue.filter("roleTypeFilter", function(val) {
+  return ROLE_TYPE[val] || DEFAULT_FIELD;
+});
+Vue.filter("printStatusFilter", function(val) {
+  return PRINT_STATUS[val] || DEFAULT_FIELD;
+});
 Vue.filter("timestampFilter", function(val) {
   return val ? formatDate("YYYY-MM-DD HH:mm:ss", new Date(val)) : DEFAULT_FIELD;
 });

+ 15 - 1
src/plugins/globalVuePlugins.js

@@ -3,9 +3,23 @@ import { objAssign, randomCode, tableAction } from "@/plugins/utils";
 import globalMixins from "./mixins";
 // components
 import ViewFooter from "@/components/ViewFooter.vue";
+import RoomSelect from "../components/base/RoomSelect.vue";
+import PlaceSelect from "../components/base/PlaceSelect.vue";
+import PrintPlanSelect from "../components/base/PrintPlanSelect.vue";
+import CardRuleSelect from "../components/base/CardRuleSelect.vue";
+import CourseSelect from "../components/base/CourseSelect.vue";
+import PaperNumberSelect from "../components/base/PaperNumberSelect.vue";
+import QuestionTeacherSelect from "../components/base/QuestionTeacherSelect.vue";
 
 const components = {
-  ViewFooter
+  ViewFooter,
+  RoomSelect,
+  PlaceSelect,
+  PrintPlanSelect,
+  CardRuleSelect,
+  CourseSelect,
+  PaperNumberSelect,
+  QuestionTeacherSelect
 };
 
 export default {