zhangjie пре 4 година
родитељ
комит
e09c597222

+ 81 - 125
src/constants/enumerate.js

@@ -1,5 +1,6 @@
 export const SYS_ADMIN_NAME = "sysadmin";
 
+// 通用 -------------->
 // 启用/禁用
 export const ABLE_TYPE = {
   0: "禁用",
@@ -12,141 +13,69 @@ export const BOOLEAN_TYPE = {
   1: "是"
 };
 
-// 打印状态
-export const PRINT_STATUS = {
-  NEW: "新建",
-  READY: "就绪",
-  WAIT: "待印刷",
-  PROCESS: "印刷中",
-  END: "已完成"
-};
-
-export const PRINT_STATUS_LIST = [
-  {
-    key: "NEW",
-    name: "新建"
-  },
-  {
-    key: "READY",
-    name: "就绪"
-  },
-  {
-    key: "WAIT",
-    name: "待印刷"
-  },
-  {
-    key: "PROCESS",
-    name: "印刷中"
-  },
-  {
-    key: "END",
-    name: "已完成"
-  }
-];
-// 撤回状态
-export const REVOKE_STATUS = {
-  0: "待确认",
-  9: "无"
-};
 // 审核状态
 export const AUDITING_STATUS = {
   NOT_AUDITED: "未审核",
   AUDITED: "已审核"
 };
+
 // 审核结果
 export const AUDITING_RESULT = {
   NOT_PASS: "未通过",
   PASS: "通过"
 };
 
-// 通用题卡规则id
-export const COMMON_CARD_RULE_ID = "-1";
-
-export const CARD_BUSINESS_FIELDS = {
-  must: [
-    {
-      name: "学号",
-      code: "studentCode"
-    },
-    {
-      name: "姓名",
-      code: "name"
-    },
-    {
-      name: "课程名称",
-      code: "courseName"
-    }
-  ],
-  extend: [
-    {
-      name: "教学班号",
-      code: "classNo"
-    },
-    {
-      name: "考场",
-      code: "examRoom"
-    }
-  ]
-};
-
-export const RESERVE_TYPE = {
-  0: "考试任务备用方式",
-  1: "考场备用方式"
-};
-
-export const CARD_SOURCE_TYPE = {
-  SELECT: "选择已有答题卡",
-  SELF: "自助创建",
-  CUST: "申请客服制卡"
+// 权限类型
+export const PRIVILEGE_TYPE = {
+  MENU: "菜单",
+  URL: "操作"
 };
 
-export const EXAM_NUMBER_STYLE = {
-  PRINT: "印刷条码",
-  PASTE: "粘贴条码",
-  FILL: "考号填涂"
+// 基础 -------------->
+// 角色
+export const ROLE_TYPE = {
+  SCHOOL_ADMIN: "管理员",
+  EXAM_TEACHER: "考务老师",
+  QUESTION_TEACHER: "命题老师",
+  CUSTOMER: "客服人员",
+  PRINT: "印刷人员",
+  CUSTOM: "自定义"
 };
 
-export const PAPER_TYPE = {
-  PRINT: "印刷",
-  FILL: "填涂"
-};
+// 通用题卡规则id
+export const COMMON_CARD_RULE_ID = "-1";
 
+// 模板类型
 export const TEMPLATE_TYPE = {
   GENERIC: "通卡模板",
   VARIABLE: "变量印品模板",
   ORDINARY: "普通印品模板"
 };
 
-export const PRINT_CONTENT_TYPE = {
-  PAPER: "试卷",
-  CARD: "题卡"
-};
-export const PRINT_PLAN_STATUS = {
-  NEW: "新建",
-  READY: "准备就绪",
-  QUESTION: "命题中",
-  PRINT: "印刷中",
-  END: "已完成"
-};
-
-export const PAPER_TYPE_FIELDS = ["A", "B"];
-
+// 提交印刷方式
 export const CONFIRM_PRINT_TYPE = {
   MANUAL: "手动",
   AUTO: "自动"
 };
 
-export const PRIVILEGE_TYPE = {
-  MENU: "菜单",
-  URL: "操作"
-};
+// 命题 -------------->
+// 代办任务警告时间
+export const TASK_WARNING_TIME = 3 * 24 * 60 * 60 * 1000;
 
-export const TEMPLATE_CLASSIFY = {
-  SIGN: "签到表",
-  PACKAGE: "卷袋贴",
-  CHECK_IN: "考试情况登记表"
+// 题卡
+// 条码类型
+export const EXAM_NUMBER_STYLE = {
+  PRINT: "印刷条码",
+  PASTE: "粘贴条码",
+  FILL: "考号填涂"
+};
+// 试卷类型方式
+export const PAPER_TYPE = {
+  PRINT: "印刷",
+  FILL: "填涂"
 };
 
+// 命题任务状态
 export const EXAM_TASK_STATUS = {
   NEW: "新建",
   READY: "待命题",
@@ -156,30 +85,21 @@ export const EXAM_TASK_STATUS = {
   // CANCEL: "撤回"
 };
 
-export const DRAW_RULE_TYPE = {
-  ONE: "只抽取一次",
-  CIRCLE: "可反复抽取"
-};
-
-export const PRINT_BACKUP_TYPE = {
-  ROOM: "每考场",
-  STUDENT: "每考生"
+// 题卡创建方式
+export const CARD_SOURCE_TYPE = {
+  SELECT: "选择已有答题卡",
+  SELF: "自助创建",
+  CUST: "申请客服制卡"
 };
 
-export const PAPER_BACKUP_TYPE = {
-  ROOM: "每考场",
-  PLACE: "每考点"
+// 模板类型
+export const TEMPLATE_CLASSIFY = {
+  SIGN: "签到表",
+  PACKAGE: "卷袋贴",
+  CHECK_IN: "考试情况登记表"
 };
 
-export const ROLE_TYPE = {
-  SCHOOL_ADMIN: "管理员",
-  EXAM_TEACHER: "考务老师",
-  QUESTION_TEACHER: "命题老师",
-  CUSTOMER: "客服人员",
-  PRINT: "印刷人员",
-  CUSTOM: "自定义"
-};
-// data-manage
+// 数据管理
 export const DATA_TASK_STATUS = {
   INIT: "未开始",
   RUNNING: "进行中",
@@ -198,3 +118,39 @@ export const DATA_TASK_RESULT = {
   SUCCESS: "成功",
   ERROR: "失败"
 };
+
+// 印刷 -------------->
+// 印刷计划状态
+export const PRINT_PLAN_STATUS = {
+  NEW: "新建",
+  READY: "准备就绪",
+  QUESTION: "命题中",
+  PRINT: "印刷中",
+  END: "已完成"
+};
+// 印刷计划相关
+export const DRAW_RULE_TYPE = {
+  ONE: "只抽取一次",
+  CIRCLE: "可反复抽取"
+};
+export const PRINT_BACKUP_TYPE = {
+  ROOM: "每考场",
+  STUDENT: "每考生"
+};
+export const PAPER_BACKUP_TYPE = {
+  ROOM: "每考场",
+  PLACE: "每考点"
+};
+export const PRINT_CONTENT_TYPE = {
+  PAPER: "试卷",
+  CARD: "题卡"
+};
+
+// 印刷任务状态
+export const PRINT_TASK_STATUS = {
+  NEW: "新建",
+  READY: "就绪",
+  WAITING: "待印刷",
+  PRINTING: "印刷中",
+  FINISH: "已完成"
+};

+ 0 - 3
src/modules/base/api.js

@@ -110,9 +110,6 @@ export const courseListPage = datas => {
 export const courseQuery = param => {
   return $postParam("/api/admin/basic/course/query", { param });
 };
-export const courseList = datas => {
-  return $postParam("/api/admin/basic/course/list", datas);
-};
 export const deleteCourse = id => {
   return $post("/api/admin/basic/course/remove", { id });
 };

+ 8 - 6
src/modules/base/components/ModifyMenu.vue

@@ -4,7 +4,7 @@
     :visible.sync="modalIsShow"
     :title="title"
     top="10vh"
-    width="500px"
+    width="600px"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body
@@ -114,17 +114,17 @@ export default {
         name: [
           {
             required: true,
-            pattern: /^[\u4E00-\u9FA5/]{1,30}$/,
-            message: "菜单名称只能输入汉字和斜线,长度不能超过30",
+            length: 30,
+            message: "菜单名称不能超过30",
             trigger: "change"
           }
         ],
         url: [
           {
             required: true,
-            pattern: /^[0-9a-zA-Z/_-]{3,30}$/,
+            pattern: /^[0-9a-zA-Z/_-]{3,200}$/,
             message:
-              "URL地址只能由字母、数字、斜线、短横线和下划线组成,长度在3-30之间",
+              "URL地址只能由字母、数字、斜线、短横线和下划线组成,长度在3-200之间",
             trigger: "change"
           }
         ]
@@ -158,7 +158,9 @@ export default {
       this.isSubmit = false;
       if (!data) return;
 
-      this.$emit("confirm", this.modalForm);
+      if (!this.isEdit) datas.id = data;
+
+      this.$emit("confirm", datas);
       this.cancel();
     }
   }

+ 35 - 12
src/modules/base/components/ModifyRole.vue

@@ -3,8 +3,8 @@
     class="modify-role"
     :visible.sync="modalIsShow"
     :title="title"
-    top="10vh"
-    width="600px"
+    top="10px"
+    width="700px"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body
@@ -40,7 +40,7 @@
           </el-select>
         </el-form-item>
         <el-form-item
-          v-show="modalForm.type === 'CUSTOM'"
+          v-show="modalForm.type === 'CUSTOM' || IS_SUPER_ADMIN"
           prop="privilegeIds"
           label="角色权限:"
         >
@@ -51,6 +51,9 @@
               node-key="id"
               ref="MenuTree"
               highlight-current
+              check-on-click-node
+              :expand-on-click-node="false"
+              :default-expanded-keys="defaultExpandedKeys"
               :props="defaultProps"
               @check-change="checkChange"
             >
@@ -75,7 +78,8 @@
 </template>
 
 <script>
-import { updateRole, roleBoundPrivileges } from "../api";
+import { updateRole, menuList, roleBoundPrivileges } from "../api";
+import { SYS_ADMIN_NAME } from "@/constants/enumerate";
 
 const initModalForm = {
   id: null,
@@ -93,12 +97,6 @@ export default {
         return {};
       }
     },
-    menus: {
-      type: Array,
-      default() {
-        return [];
-      }
-    },
     roleTypes: {
       type: Object,
       default() {
@@ -118,6 +116,10 @@ export default {
     return {
       modalIsShow: false,
       isSubmit: false,
+      IS_SUPER_ADMIN:
+        this.$ls.get("user", { loginName: "" }).loginName === SYS_ADMIN_NAME,
+      menus: [],
+      defaultExpandedKeys: [],
       modalForm: {},
       defaultProps: {
         label: "name"
@@ -154,12 +156,21 @@ export default {
       }
     };
   },
+  created() {
+    this.getMenus();
+  },
   methods: {
+    async getMenus() {
+      this.menus = await menuList();
+      this.defaultExpandedKeys = [];
+      this.getDefaultExpandedKeys(this.menus);
+    },
     async visibleChange() {
       if (this.instance.id) {
         this.modalForm = this.$objAssign(initModalForm, this.instance);
-        if (this.instance.type === "CUSTOM") {
+        if (this.instance.type === "CUSTOM" || this.IS_SUPER_ADMIN) {
           let privilegeIds = await roleBoundPrivileges(this.instance.id);
+          privilegeIds = privilegeIds || [];
           let checkedIds = [];
           const getCheckedIds = list => {
             list.forEach(item => {
@@ -189,7 +200,19 @@ export default {
     open() {
       this.modalIsShow = true;
     },
-    checkChange() {
+    getDefaultExpandedKeys(menus) {
+      menus.forEach(item => {
+        if (
+          item.children &&
+          item.children.length &&
+          !item.children.some(elem => elem.type === "URL")
+        ) {
+          this.defaultExpandedKeys.push(item.id);
+          this.getDefaultExpandedKeys(item.children);
+        }
+      });
+    },
+    checkChange(data, checked) {
       this.modalForm.privilegeIds = this.$refs.MenuTree.getCheckedKeys();
       this.$refs.modalFormComp.validateField("privilegeIds");
     },

+ 37 - 8
src/modules/base/views/MenuManage.vue

@@ -24,7 +24,7 @@
               class="btn-table-icon"
               type="text"
               icon="icon icon-plus-act"
-              @click="() => append(node, data)"
+              @click="() => append(data)"
               title="新增"
             ></el-button>
             <el-button
@@ -34,13 +34,13 @@
               @click="() => edit(node, data)"
               title="修改"
             ></el-button>
-            <!-- <el-button
+            <el-button
               class="btn-table-icon"
               type="text"
               icon="icon icon-delete"
               @click="() => remove(node, data)"
               title="删除"
-            ></el-button> -->
+            ></el-button>
           </span>
         </span>
       </el-tree>
@@ -50,7 +50,7 @@
     <modify-menu
       ref="ModifyMenu"
       :instance="curMenu"
-      @confirm="getList"
+      @confirm="menuModified"
     ></modify-menu>
   </div>
 </template>
@@ -66,6 +66,8 @@ export default {
     return {
       menus: [],
       curMenu: {},
+      parentNode: null,
+      parentData: null,
       defaultProps: {
         label: "name"
       }
@@ -79,7 +81,9 @@ export default {
       this.menus = await menuList();
     },
     toAdd() {
-      this.curMenu = {};
+      this.curMenu = {
+        sequence: this.menus.length + 1
+      };
       this.$refs.ModifyMenu.open();
     },
     edit(node, data) {
@@ -87,6 +91,7 @@ export default {
         ...data,
         parentName: data.parentId && node.parent.data.name
       };
+      this.parentData = data;
       this.$refs.ModifyMenu.open();
     },
     remove(node, data) {
@@ -110,16 +115,40 @@ export default {
         })
         .catch(() => {});
     },
-    append(node, data) {
-      const sequences = node.parent.childNodes.map(item => item.sequence);
-      const maxSequence = Math.max.apply(null, sequences) || 0;
+    append(data) {
+      const sequences = data.children
+        ? data.children.map(item => item.sequence)
+        : [];
+      const maxSequence = sequences.length
+        ? Math.max.apply(null, sequences)
+        : 0;
 
       this.curMenu = {
         parentId: data.id,
         parentName: data.name,
         sequence: maxSequence + 1
       };
+      this.parentData = data;
       this.$refs.ModifyMenu.open();
+    },
+    menuModified(curNode) {
+      if (!this.curMenu.parentName) {
+        this.getList();
+        return;
+      }
+
+      if (this.curMenu.id) {
+        this.parentData.name = curNode.name;
+        this.parentData.url = curNode.url;
+        this.parentData.type = curNode.type;
+        this.parentData.sequence = curNode.sequence;
+        this.parentData.remark = curNode.remark;
+      } else {
+        if (!this.parentData.children) {
+          this.$set(this.parentData, "children", []);
+        }
+        this.parentData.children.push(curNode);
+      }
     }
   }
 };

+ 2 - 8
src/modules/base/views/RoleManage.vue

@@ -113,7 +113,6 @@
     <modify-role
       ref="ModifyRole"
       :instance="curRole"
-      :menus="menus"
       :role-types="ROLE_TYPE"
       @modified="getList"
     ></modify-role>
@@ -122,7 +121,7 @@
 
 <script>
 import { ABLE_TYPE, SYS_ADMIN_NAME } from "@/constants/enumerate";
-import { menuList, roleListPage, ableRole, deleteRole } from "../api";
+import { roleListPage, ableRole, deleteRole } from "../api";
 import { getEnums } from "../../login/api";
 import ModifyRole from "../components/ModifyRole";
 
@@ -143,8 +142,7 @@ export default {
       roles: [],
       curRole: {},
       ABLE_TYPE,
-      ROLE_TYPE: {},
-      menus: []
+      ROLE_TYPE: {}
     };
   },
   created() {
@@ -154,10 +152,6 @@ export default {
     async init() {
       await this.getRoleTypes();
       await this.getList();
-      await this.getMenus();
-    },
-    async getMenus() {
-      this.menus = await menuList();
     },
     async getRoleTypes() {
       const loginName = this.$ls.get("user", { loginName: "" }).loginName;

+ 24 - 4
src/modules/exam/api.js

@@ -1,10 +1,6 @@
 import { $postParam, $post } from "@/plugins/axios";
 
 // other
-// 待办任务数量
-export const waitTaskListCount = () => {
-  return $postParam("/api/admin/exam/task_undo_count", {});
-};
 // 试卷编号模糊查询
 export const pageNumberQuery = param => {
   return $postParam("/api/admin/exam/task/paper_number_query", { param });
@@ -14,6 +10,29 @@ export const questionTeatherQuery = datas => {
   return $postParam("/api/admin/exam/task/user_query", datas);
 };
 
+// 待办任务-------------->
+// 命题任务待办
+export const waitExamTaskListPage = datas => {
+  return $postParam("/api/admin/work/exam/task/ready", datas);
+};
+export const waitExamTaskCount = () => {
+  return $postParam("/api/admin/work/exam/task/ready_count", {});
+};
+// 命题分配待办
+export const waitExamTaskNewListPage = datas => {
+  return $postParam("/api/admin/work/exam/task/new", datas);
+};
+export const waitExamTaskNewCount = () => {
+  return $postParam("/api/admin/work/exam/task/new_count", {});
+};
+// 入库审核待办
+export const waitTaskReviewListPage = datas => {
+  return $postParam("/api/admin/work/exam/task/submit", datas);
+};
+export const waitTaskReviewCount = () => {
+  return $postParam("/api/admin/work/exam/task/submit_count", {});
+};
+
 // exam-task-manage
 export const examTaskListPage = datas => {
   return $postParam("/api/admin/exam/task/list", datas);
@@ -86,5 +105,6 @@ export const dataTaskList = datas => {
   return $postParam("/api/admin/data/task/query", datas);
 };
 export const removeDataTask = ({ ids, type }) => {
+  // TODO:
   return $post("/api/admin/data/task/query", { ids, type });
 };

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

@@ -93,9 +93,9 @@
                 >
                   <el-option
                     v-for="user in scope.row.users"
-                    :key="user.userId"
-                    :value="user.userId"
-                    :label="user.userName"
+                    :key="user.id"
+                    :value="user.id"
+                    :label="user.name"
                   ></el-option>
                 </el-select>
               </template>
@@ -140,7 +140,7 @@ export default {
         batchNo: [
           {
             required: true,
-            message: "请上传考试计划表",
+            message: "请上传命题任务表",
             trigger: "change"
           }
         ],
@@ -198,7 +198,7 @@ export default {
       let datas = {
         ...this.modalForm
       };
-      datas.userIds = this.tasks.map(task => {
+      datas.users = this.tasks.map(task => {
         return {
           courseCode: task.courseCode,
           courseName: task.courseName,
@@ -220,13 +220,11 @@ export default {
     uplaodError(errorData) {
       this.$notify.error({ title: "错误提示", message: errorData.message });
     },
-    uploadExamTaskSuccess(res) {
+    uploadExamTaskSuccess(data) {
       this.$message.success("上传成功!");
-      const data = res.data;
-
       this.modalForm.batchNo = data.batchNo;
       this.tasks = data.tasks.map(item => {
-        item.userId = "";
+        item.userId = item.users.length ? item.users[0].id : "";
         return item;
       });
       this.$refs.modalFormComp.validateField("batchNo");

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

@@ -93,7 +93,8 @@ export default {
       let infos = {
         attachmentId: data.id,
         filename: data.filename,
-        url: data.url
+        url: data.url,
+        pages: data.pages
       };
       this.attachment = Object.assign(this.attachment, infos);
     },

+ 15 - 9
src/modules/exam/components/WaitTaskAudit.vue

@@ -58,24 +58,22 @@
       ref="ModifyTaskApply"
       :edit-type="editType"
       :instance="curTask"
-      @modified="getList"
+      @modified="modified"
     ></modify-task-apply>
   </div>
 </template>
 
 <script>
 import ModifyTaskApply from "./ModifyTaskApply";
-import { taskReviewUnauditedListPage } from "../api";
+import { waitTaskReviewListPage } from "../api";
+import { residueFloorTime } from "@/plugins/utils";
+import { TASK_WARNING_TIME } from "@/constants/enumerate";
 
 export default {
   name: "wait-task-audit",
   components: { ModifyTaskApply },
   data() {
     return {
-      filter: {
-        status: "NEW",
-        enable: true
-      },
       total: 0,
       current: 1,
       size: this.GLOBAL.pageSize,
@@ -90,12 +88,17 @@ export default {
   methods: {
     async getList() {
       const datas = {
-        ...this.filter,
         pageNumber: this.current,
         pageSize: this.size
       };
-      const data = await taskReviewUnauditedListPage(datas);
-      this.tasks = data.records;
+      const data = await waitTaskReviewListPage(datas);
+      this.tasks = data.records.map(item => {
+        item.residueTips = residueFloorTime(
+          item.endTime - item.systemCurrentTime,
+          TASK_WARNING_TIME
+        );
+        return item;
+      });
       this.total = data.total;
     },
     toPage(page) {
@@ -105,6 +108,9 @@ export default {
     toDo(task) {
       this.curTask = task;
       this.$refs.ModifyTaskApply.open();
+    },
+    modified() {
+      this.$emit("update-list");
     }
   }
 };

+ 15 - 9
src/modules/exam/components/WaitTaskExam.vue

@@ -50,24 +50,22 @@
       ref="ModifyExamTask"
       :instance="curTask"
       :edit-type="editType"
-      @modified="getList"
+      @modified="modified"
     ></modify-exam-task>
   </div>
 </template>
 
 <script>
-import { examTaskListPage } from "../api";
+import { waitExamTaskNewListPage } from "../api";
 import ModifyExamTask from "./ModifyExamTask";
+import { residueFloorTime } from "@/plugins/utils";
+import { TASK_WARNING_TIME } from "@/constants/enumerate";
 
 export default {
   name: "wait-task-exam",
   components: { ModifyExamTask },
   data() {
     return {
-      filter: {
-        status: "NEW",
-        enable: true
-      },
       total: 0,
       current: 1,
       size: this.GLOBAL.pageSize,
@@ -82,12 +80,17 @@ export default {
   methods: {
     async getList() {
       const datas = {
-        ...this.filter,
         pageNumber: this.current,
         pageSize: this.size
       };
-      const data = await examTaskListPage(datas);
-      this.tasks = data.records;
+      const data = await waitExamTaskNewListPage(datas);
+      this.tasks = data.records.map(item => {
+        item.residueTips = residueFloorTime(
+          item.endTime - item.systemCurrentTime,
+          TASK_WARNING_TIME
+        );
+        return item;
+      });
       this.total = data.total;
     },
     toPage(page) {
@@ -97,6 +100,9 @@ export default {
     toDo(task) {
       this.curTask = task;
       this.$refs.ModifyExamTask.open();
+    },
+    modified() {
+      this.$emit("update-list");
     }
   }
 };

+ 17 - 11
src/modules/exam/components/WaitTaskExamTask.vue

@@ -24,8 +24,8 @@
           <span>{{ task.endTime | timestampFilter }}</span>
         </p>
         <div class="part-box-flex">
-          <el-tag type="success" effect="dark">
-            {{ task.specialty }}
+          <el-tag :type="task.residueTips.status" effect="dark">
+            {{ task.residueTips.title }}
           </el-tag>
           <span class="task-action" @click="toDo(task)"
             >立即处理 <i class="el-icon-arrow-right"></i
@@ -50,24 +50,22 @@
       ref="ModifyTaskApply"
       :edit-type="editType"
       :instance="curTask"
-      @modified="getList"
+      @modified="modified"
     ></modify-task-apply>
   </div>
 </template>
 
 <script>
-import { examTaskListPage } from "../api";
+import { waitExamTaskListPage } from "../api";
 import ModifyTaskApply from "./ModifyTaskApply";
+import { residueFloorTime } from "@/plugins/utils";
+import { TASK_WARNING_TIME } from "@/constants/enumerate";
 
 export default {
   name: "wait-task-exam",
   components: { ModifyTaskApply },
   data() {
     return {
-      filter: {
-        status: "NEW",
-        enable: true
-      },
       total: 0,
       current: 1,
       size: this.GLOBAL.pageSize,
@@ -82,12 +80,17 @@ export default {
   methods: {
     async getList() {
       const datas = {
-        ...this.filter,
         pageNumber: this.current,
         pageSize: this.size
       };
-      const data = await examTaskListPage(datas);
-      this.tasks = data.records;
+      const data = await waitExamTaskListPage(datas);
+      this.tasks = data.records.map(item => {
+        item.residueTips = residueFloorTime(
+          item.endTime - item.systemCurrentTime,
+          TASK_WARNING_TIME
+        );
+        return item;
+      });
       this.total = data.total;
     },
     toPage(page) {
@@ -97,6 +100,9 @@ export default {
     toDo(task) {
       this.curTask = task;
       this.$refs.ModifyTaskApply.open();
+    },
+    modified() {
+      this.$emit("update-list");
     }
   }
 };

+ 23 - 3
src/modules/exam/store.js

@@ -1,4 +1,9 @@
-import { waitTaskListCount } from "./api";
+import {
+  waitExamTaskCount,
+  waitExamTaskNewCount,
+  waitTaskReviewCount
+} from "./api";
+import { calcSum } from "@/plugins/utils";
 
 const state = {
   waitTaskCount: 0
@@ -11,8 +16,23 @@ const mutations = {
 };
 
 const actions = {
-  async updateWaitTaskCount({ commit }) {
-    const count = await waitTaskListCount();
+  async updateWaitTaskCount({ commit }, userRoles = []) {
+    console.log(userRoles);
+    const apis = {
+      QUESTION_TEACHER: [waitExamTaskCount],
+      EXAM_TEACHER: [waitExamTaskNewCount, waitTaskReviewCount]
+    };
+    let countAll = [];
+    Object.keys(apis).forEach(roleCode => {
+      console.log(roleCode);
+      if (userRoles.includes(roleCode))
+        countAll = [...countAll, ...apis[roleCode]];
+    });
+    const counts = await Promise.all(
+      countAll.map(item => item())
+    ).catch(() => {});
+
+    const count = calcSum(counts || [0]);
     commit("setNotDoneTaskCount", count);
   }
 };

+ 7 - 7
src/modules/exam/views/DataTaskManage.vue

@@ -69,16 +69,16 @@
           align="center"
           :index="indexMethod"
         ></el-table-column>
-        <el-table-column prop="schoolName" label="项目"></el-table-column>
+        <el-table-column prop="printPlanName" label="项目"></el-table-column>
         <el-table-column prop="type" label="类别">
-          <template slot-scope="scope">
+          <!-- <template slot-scope="scope">
             {{ scope.row.type | dataTaskTypeFilter }}
-          </template>
+          </template> -->
         </el-table-column>
         <el-table-column prop="status" label="状态">
-          <template slot-scope="scope">
+          <!-- <template slot-scope="scope">
             {{ scope.row.status | dataTaskStatusFilter }}
-          </template>
+          </template> -->
         </el-table-column>
         <el-table-column prop="result" label="结果">
           <template slot-scope="scope">
@@ -90,7 +90,7 @@
             scope.row.createTime | timestampFilter
           }}</span>
         </el-table-column>
-        <el-table-column prop="name" label="创建人"></el-table-column>
+        <el-table-column prop="createName" label="创建人"></el-table-column>
         <el-table-column
           class-name="action-column"
           label="操作"
@@ -165,7 +165,7 @@ export default {
     };
   },
   created() {
-    // this.toPage(1);
+    this.toPage(1);
   },
   methods: {
     async getList() {

+ 21 - 2
src/modules/exam/views/WaitTask.vue

@@ -5,15 +5,21 @@
         <wait-task-exam-task
           class="wait-module"
           ref="WaitTaskExamTask"
+          @update-list="updateList"
         ></wait-task-exam-task>
       </el-col>
       <el-col v-if="IS_EXAM_TEACHER" :span="colSpan">
-        <wait-task-exam class="wait-module" ref="WaitTaskExam"></wait-task-exam>
+        <wait-task-exam
+          class="wait-module"
+          ref="WaitTaskExam"
+          @update-list="updateList"
+        ></wait-task-exam>
       </el-col>
       <el-col v-if="IS_EXAM_TEACHER" :span="colSpan">
         <wait-task-audit
           class="wait-module"
           ref="WaitTaskAudit"
+          @update-list="updateList"
         ></wait-task-audit>
       </el-col>
     </el-row>
@@ -24,6 +30,7 @@
 import WaitTaskExamTask from "../components/WaitTaskExamTask";
 import WaitTaskExam from "../components/WaitTaskExam";
 import WaitTaskAudit from "../components/WaitTaskAudit";
+import { mapActions } from "vuex";
 
 export default {
   name: "wait-task",
@@ -50,6 +57,18 @@ export default {
       return Math.floor(24 / moduleCount);
     }
   },
-  methods: {}
+  methods: {
+    ...mapActions("exam", ["updateWaitTaskCount"]),
+    updateList() {
+      this.updateWaitTaskCount();
+      if (this.IS_QUESTION_TEACHER) {
+        this.$refs.WaitTaskExamTask.getList();
+      }
+      if (this.IS_EXAM_TEACHER) {
+        this.$refs.WaitTaskExam.getList();
+        this.$refs.WaitTaskAudit.getList();
+      }
+    }
+  }
 };
 </script>

+ 14 - 5
src/modules/print/api.js

@@ -67,14 +67,23 @@ export const printTaskListPage = datas => {
   return $postParam("/api/admin/exam/print/task_list", datas);
 };
 export const submitPrintTask = id => {
-  return $post("/api/admin/exam/print/task_submit", { id });
+  return $post("/api/admin/exam/print/task_submit", { id, status: "WAITING" });
+};
+export const resubmitPrintTask = ({ id, printPlanId }) => {
+  return $post("/api/admin/exam/print/task_resubmit", {
+    id,
+    printPlanId
+  });
 };
 export const cancelPrintTask = id => {
-  return $post("/api/admin/exam/print/task_cancel", { id });
+  return $post("/api/admin/exam/print/task_cancel", { id, status: "READY" });
 };
 export const printTaskTotalInfo = datas => {
-  return $postParam("/api/admin/exam/print/task_total", datas);
+  return $postParam("/api/admin/exam/print/task_total_data", datas);
+};
+export const getPrintTaskPdf = examDetailId => {
+  return $post("/api/admin/exam/print/task_view_pdf", { examDetailId });
 };
-export const getPrintTaskPdf = id => {
-  return $post("/api/admin/exam/print/task_view_pdf", { id });
+export const downloadPrintTaskPdf = ids => {
+  return $post("/api/admin/exam/print/task_download_pdf", { ids });
 };

+ 62 - 53
src/modules/print/components/ModifyPrintPlan.vue

@@ -138,32 +138,34 @@
               >{{ 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>
+          <div v-if="item.templateId.length">
+            <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>
+          </div>
         </el-form-item>
       </div>
 
@@ -194,32 +196,34 @@
               >{{ 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>
+          <div v-if="item.templateId.length">
+            <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>
+          </div>
         </el-form-item>
       </div>
 
@@ -501,9 +505,14 @@ export default {
       this.isSubmit = true;
       const datas = { ...this.modalForm };
       const transformInfo = item => {
+        const templateId = item.templateId.join();
+        const template = this.templateSources[item.type].find(
+          temp => temp.id === templateId
+        );
         return {
           type: item.type,
-          templateId: item.templateId.join(),
+          templateId,
+          attachmentId: template && template.attachmentId,
           backupMethod: item.backupMethod,
           backupCount: item.backupCount
         };

+ 12 - 12
src/modules/print/views/BusinessDataExport.vue

@@ -91,16 +91,8 @@
           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="examDate" label="考试日期"> </el-table-column>
+        <el-table-column prop="examTime" 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="packageCode" label="卷袋号" width="80">
@@ -168,7 +160,7 @@ import {
 } from "../api";
 import UploadButton from "@/components/UploadButton";
 import PreviewBusinessDetail from "../components/PreviewBusinessDetail";
-import { downloadBlob } from "@/plugins/utils";
+import { downloadBlob, parseTimeRangeDateAndTime } from "@/plugins/utils";
 
 export default {
   name: "business-data-export",
@@ -217,7 +209,15 @@ export default {
         pageSize: this.size
       };
       const data = await businessDataListPage(datas);
-      this.dataList = data.records;
+      this.dataList = data.records.map(item => {
+        const { date, time } = parseTimeRangeDateAndTime(
+          item.examStartTime,
+          item.examEndTime
+        );
+        item.examDate = date;
+        item.examTime = time;
+        return item;
+      });
       this.total = data.total;
     },
     async getTotalData() {

+ 79 - 44
src/modules/print/views/PrintTaskManage.vue

@@ -17,10 +17,10 @@
             clearable
           >
             <el-option
-              v-for="item in PRINT_STATUS_LIST"
-              :key="item.key"
-              :value="item.key"
-              :label="item.name"
+              v-for="(val, key) in PRINT_TASK_STATUS"
+              :key="key"
+              :value="key"
+              :label="val"
             ></el-option>
           </el-select>
         </el-form-item>
@@ -97,21 +97,21 @@
           个</span
         >
         <span class="mr-4"
-          >A3印量总计:<i class="color-primary">{{ totalInfo.pageA3 }}</i>
+          >A3印量总计:<i class="color-primary">{{ totalInfo.pagesA3 }}</i>
           张</span
         >
         <span class="mr-4"
-          >A4印量总计:<i class="color-primary">{{ totalInfo.pageA4 }}</i>
+          >A4印量总计:<i class="color-primary">{{ totalInfo.pagesA4 }}</i>
           张</span
         >
       </p>
       <p>
         <span class="mr-4"
-          >A3剩余印量:<i class="color-primary">{{ totalInfo.pageA3Left }}</i>
+          >A3剩余印量:<i class="color-primary">{{ totalInfo.pagesA3Left }}</i>
           张</span
         >
         <span
-          >A4剩余印量:<i class="color-primary">{{ totalInfo.pageA4Left }}</i>
+          >A4剩余印量:<i class="color-primary">{{ totalInfo.pagesA4Left }}</i>
           张</span
         >
       </p>
@@ -124,7 +124,7 @@
         stripe
         @selection-change="handleSelectionChange"
       >
-        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column type="selection" width="50"></el-table-column>
         <el-table-column
           type="index"
           label="序号"
@@ -162,7 +162,7 @@
         <el-table-column prop="examPlace" label="考点"></el-table-column>
         <el-table-column prop="examRoom" label="考场"></el-table-column>
         <el-table-column
-          prop="pageA3"
+          prop="singlePagesA3"
           label="单科次准印量A3(页)"
         ></el-table-column>
         <el-table-column
@@ -171,33 +171,29 @@
           width="80"
         ></el-table-column>
         <el-table-column
-          prop="pageA3"
+          prop="pagesA3"
           label="A3准印量小计(页)"
         ></el-table-column>
         <el-table-column
-          prop="pageA4"
+          prop="pagesA4"
           label="A4准印量小计(页)"
         ></el-table-column>
         <el-table-column prop="status" label="印刷状态" width="80">
           <span slot-scope="scope">{{
-            scope.row.status | printStatusFilter
+            scope.row.status | printTaskStatusFilter
           }}</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">
+        <el-table-column prop="printStartTime" label="打印开始时间" width="160">
           <span slot-scope="scope">{{
-            scope.row.printTime | timestampFilter
+            scope.row.printStartTime | timestampFilter
           }}</span>
         </el-table-column>
-        <el-table-column
-          prop="expectPrintTime"
-          label="打印完成时间"
-          width="160"
-        >
+        <el-table-column prop="printEndTime" label="打印完成时间" width="160">
           <span slot-scope="scope">{{
-            scope.row.expectPrintTime | timestampFilter
+            scope.row.printEndTime | timestampFilter
           }}</span>
         </el-table-column>
         <el-table-column
@@ -209,6 +205,7 @@
         >
           <template slot-scope="scope">
             <el-button
+              v-if="scope.row.status === 'READY'"
               class="btn-table-icon"
               type="text"
               icon="icon icon-circle-share"
@@ -216,6 +213,17 @@
               title="提交印刷"
             ></el-button>
             <el-button
+              v-if="
+                scope.row.status === 'PRINTING' || scope.row.status === 'FINISH'
+              "
+              class="btn-table-icon"
+              type="text"
+              icon="icon icon-circle-share"
+              @click="toResubmit(scope.row)"
+              title="重新提交"
+            ></el-button>
+            <el-button
+              v-if="scope.row.status === 'WAITING'"
               class="btn-table-icon"
               type="text"
               icon="icon icon-close-act"
@@ -258,13 +266,15 @@
 import {
   printTaskListPage,
   submitPrintTask,
+  resubmitPrintTask,
   cancelPrintTask,
   printTaskTotalInfo,
-  getPrintTaskPdf
+  getPrintTaskPdf,
+  downloadPrintTaskPdf
 } from "../api";
-import { PRINT_STATUS_LIST } from "@/constants/enumerate";
+import { PRINT_TASK_STATUS } from "@/constants/enumerate";
 import pickerOptions from "@/constants/datePickerOptions";
-import { download, randomCode } from "@/plugins/utils";
+import { parseTimeRangeDateAndTime } from "@/plugins/utils";
 
 export default {
   name: "print-task-manage",
@@ -287,7 +297,7 @@ export default {
       dataList: [],
       curRow: {},
       multipleSelection: [],
-      PRINT_STATUS_LIST,
+      PRINT_TASK_STATUS,
       loading: false,
       // date-picker
       createTime: [],
@@ -309,7 +319,15 @@ export default {
         datas.examEndTime = this.createTime[1];
       }
       const data = await printTaskListPage(datas);
-      this.dataList = data.records;
+      this.dataList = data.records.map(item => {
+        const { date, time } = parseTimeRangeDateAndTime(
+          item.examStartTime,
+          item.examEndTime
+        );
+        item.examDate = date;
+        item.examTime = time;
+        return item;
+      });
       this.total = data.total;
     },
     toPage(page) {
@@ -331,14 +349,15 @@ export default {
         datas.examStartTime = this.createTime[0];
         datas.examEndTime = this.createTime[1];
       }
-      this.totalInfo = await printTaskTotalInfo(datas);
+      const data = await printTaskTotalInfo(datas);
+      this.totalInfo = data || {};
     },
     handleSelectionChange(val) {
       this.multipleSelection = val.map(item => item.id);
     },
     toPreview(row) {},
     toSubmit(row) {
-      this.$confirm("确定提交该印刷计划吗?", "提示", {
+      this.$confirm("确定提交该印刷任务吗?", "提示", {
         cancelButtonClass: "el-button--danger is-plain",
         confirmButtonClass: "el-button--primary",
         type: "warning"
@@ -352,6 +371,24 @@ export default {
         })
         .catch(() => {});
     },
+    toResubmit(row) {
+      this.$confirm("确定重新提交该印刷任务吗?", "提示", {
+        cancelButtonClass: "el-button--danger is-plain",
+        confirmButtonClass: "el-button--primary",
+        type: "warning"
+      })
+        .then(async () => {
+          const data = await resubmitPrintTask({
+            id: row.id,
+            printPlanId: row.printPlanId
+          });
+          if (!data) return;
+
+          this.$message.success("提交成功!");
+          this.getList();
+        })
+        .catch(() => {});
+    },
     toCancel(row) {
       this.$confirm("确定撤回该印刷任务的提交吗?", "提示", {
         cancelButtonClass: "el-button--danger is-plain",
@@ -368,9 +405,16 @@ export default {
         .catch(() => {});
     },
     async toViewPdf(row) {
-      const data = await getPrintTaskPdf(row.id);
-      if (!data) return;
-      window.open(data.path);
+      let result = true;
+      const data = await getPrintTaskPdf(row.id).catch(() => {
+        result = false;
+      });
+      if (!result) return;
+      if (data) {
+        window.open(data);
+      } else {
+        this.$message.error("当前任务pdf还未生成好,请稍后再试!");
+      }
     },
     async toExport() {
       if (this.loading) return;
@@ -381,20 +425,11 @@ export default {
       }
 
       this.loading = true;
-      const res = await download({
-        type: "post",
-        url: this.GLOBAL.domain + "/api/admin/exam/print_task/download_pdf",
-        data: {
-          examTaskIds: this.multipleSelection
-        },
-        fileName: `${Date.now()}${randomCode()}.zip`,
-        header: this.getHeadIds()
-      }).catch(error => {
-        this.$message.error(error + "文件下载失败,请重新尝试!");
-      });
+      const data = await downloadPrintTaskPdf(this.multipleSelection.join());
       this.loading = false;
-      if (!res) return;
-      this.$message.success("文件下载成功!");
+      if (!data) return;
+
+      this.$message.success("文件下载任务提交成功!");
     }
   }
 };

+ 1 - 1
src/plugins/axios.js

@@ -42,7 +42,7 @@ const errorCallback = error => {
  * @param {Object} error Response中的data信息
  */
 const errorDataCallback = error => {
-  let message = error.message || "数据错误";
+  let message = error.message || error.error || "数据错误";
 
   if (error.code === 4010006 || error.code === 5000012) {
     if (unauthMsgBoxIsShow) return error;

+ 3 - 3
src/plugins/filters.js

@@ -5,7 +5,7 @@ import {
   AUDITING_STATUS,
   AUDITING_RESULT,
   PRINT_PLAN_STATUS,
-  PRINT_STATUS,
+  PRINT_TASK_STATUS,
   DATA_TASK_STATUS,
   DATA_TASK_TYPE,
   DATA_TASK_RESULT
@@ -41,8 +41,8 @@ Vue.filter("reviewStatusFilter", function(val) {
 Vue.filter("printPlanStatusFilter", function(val) {
   return PRINT_PLAN_STATUS[val] || DEFAULT_FIELD;
 });
-Vue.filter("printStatusFilter", function(val) {
-  return PRINT_STATUS[val] || DEFAULT_FIELD;
+Vue.filter("printTaskStatusFilter", function(val) {
+  return PRINT_TASK_STATUS[val] || DEFAULT_FIELD;
 });
 Vue.filter("dataTaskTypeFilter", function(val) {
   return DATA_TASK_TYPE[val] || DEFAULT_FIELD;

+ 109 - 37
src/plugins/utils.js

@@ -4,7 +4,7 @@ const deepmerge = require("deepmerge");
  * 判断对象类型
  * @param {*} obj 对象
  */
-function objTypeOf(obj) {
+export function objTypeOf(obj) {
   const toString = Object.prototype.toString;
   const map = {
     "[object Boolean]": "boolean",
@@ -25,7 +25,7 @@ function objTypeOf(obj) {
  * 深拷贝
  * @param {Object/Array} data 需要拷贝的数据
  */
-function deepCopy(data, options) {
+export function deepCopy(data, options) {
   const defObj = objTypeOf(data) === "array" ? [] : {};
   return deepmerge(defObj, data, options || {});
 }
@@ -35,7 +35,7 @@ function deepCopy(data, options) {
  * @param {Object} target 目标对象
  * @param {Object} sources 源对象
  */
-function objAssign(target, sources) {
+export function objAssign(target, sources) {
   let targ = { ...target };
   for (let k in targ) {
     targ[k] = sources.hasOwnProperty(k) ? sources[k] : targ[k];
@@ -47,7 +47,7 @@ function objAssign(target, sources) {
  * 文件流下载
  * @param {Object} option 文件下载设置
  */
-function download(option) {
+export function download(option) {
   let defOpt = {
     type: "get",
     url: "",
@@ -113,7 +113,7 @@ function download(option) {
  * @param {Function} fetchFunc 下载程序,返回promise
  * @param {String} fileName 保存的文件名
  */
-async function downloadBlob(fetchFunc, fileName) {
+export async function downloadBlob(fetchFunc, fileName) {
   const res = await fetchFunc().catch(() => {});
   if (!res) return;
 
@@ -133,7 +133,7 @@ async function downloadBlob(fetchFunc, fileName) {
  * @param {Function} h createElement
  * @param {Array} actions 操作分类数组
  */
-function tableAction(h, actions) {
+export function tableAction(h, actions) {
   return actions.map(item => {
     let attr = {
       props: {
@@ -159,7 +159,7 @@ function tableAction(h, actions) {
  * @param {Number} len 推荐8的倍数
  *
  */
-function randomCode(len = 16) {
+export function randomCode(len = 16) {
   if (len <= 0) return;
   let steps = Math.ceil(len / 8);
   let stepNums = [];
@@ -177,7 +177,7 @@ function randomCode(len = 16) {
  * 序列化参数
  * @param {Object} params 参数对象
  */
-function qsParams(params) {
+export function qsParams(params) {
   return Object.entries(params)
     .map(el => `${el[0]}=${el[1]}`)
     .join("&");
@@ -188,7 +188,7 @@ function qsParams(params) {
  * @param {String} format 时间格式
  * @param {Date} date 需要格式化的时间对象
  */
-function formatDate(format = "YYYY/MM/DD HH:mm:ss", date = new Date()) {
+export function formatDate(format = "YYYY/MM/DD HH:mm:ss", date = new Date()) {
   if (objTypeOf(date) !== "date") return;
   const options = {
     "Y+": date.getFullYear(),
@@ -208,10 +208,102 @@ function formatDate(format = "YYYY/MM/DD HH:mm:ss", date = new Date()) {
   return format;
 }
 
+/**
+ *  获取时间长度文字
+ * @param {Number} timeNumber 时间数值,单位:毫秒
+ */
+export function timeNumberToText(timeNumber) {
+  const DAY_TIME = 24 * 60 * 60 * 1000;
+  const HOUR_TIME = 60 * 60 * 1000;
+  const MINUTE_TIME = 60 * 1000;
+  const SECOND_TIME = 1000;
+  let [day, hour, minute, second] = [0, 0, 0, 0];
+  let residueTime = timeNumber;
+
+  if (residueTime >= DAY_TIME) {
+    day = Math.floor(residueTime / DAY_TIME);
+    residueTime -= day * DAY_TIME;
+    day += "天";
+  }
+  if (residueTime >= HOUR_TIME) {
+    hour = Math.floor(residueTime / HOUR_TIME);
+    residueTime -= hour * HOUR_TIME;
+    hour += "小时";
+  }
+  if (residueTime >= MINUTE_TIME) {
+    minute = Math.floor(residueTime / MINUTE_TIME);
+    residueTime -= minute * MINUTE_TIME;
+    minute += "分钟";
+  }
+  if (residueTime >= SECOND_TIME) {
+    second = Math.round(residueTime / SECOND_TIME);
+    second += "秒";
+  }
+
+  return [day, hour, minute, second].filter(item => !!item).join("");
+}
+
+/**
+ *  警告时间
+ * @param {Number} timeNumber 时间数值,单位:毫秒
+ * @param {Number} wainingTime 最大剩余警告时间数值,单位:毫秒
+ */
+export function residueFloorTime(timeNumber, wainingTime = 0) {
+  if (timeNumber < 0) {
+    return { status: "danger", title: "已过期" };
+  }
+
+  const DAY_TIME = 24 * 60 * 60 * 1000;
+  const HOUR_TIME = 60 * 60 * 1000;
+  const MINUTE_TIME = 60 * 1000;
+  const status = timeNumber < wainingTime ? "warning" : "primary";
+  let [day, hour, minute] = [0, 0, 0];
+  let residueTime = timeNumber;
+
+  if (residueTime >= DAY_TIME) {
+    day = Math.floor(residueTime / DAY_TIME);
+    residueTime -= day * DAY_TIME;
+    return {
+      status,
+      title: `剩余${day}天`
+    };
+  }
+  if (residueTime >= HOUR_TIME) {
+    hour = Math.floor(residueTime / HOUR_TIME);
+    residueTime -= hour * HOUR_TIME;
+    return {
+      status,
+      title: `剩余${hour}小时`
+    };
+  }
+  if (residueTime >= MINUTE_TIME) {
+    minute = Math.floor(residueTime / MINUTE_TIME);
+    return {
+      status,
+      title: `剩余${minute}分钟`
+    };
+  }
+
+  return {
+    status,
+    title: `不足1分钟`
+  };
+}
+
+export function parseTimeRangeDateAndTime(startTime, endTime) {
+  const st = formatDate("YYYY-MM-DD HH:mm", new Date(startTime)).split(" ");
+  const et = formatDate("YYYY-MM-DD HH:mm", new Date(endTime)).split(" ");
+
+  return {
+    date: st[0],
+    time: `${st[1]}-${et[1]}`
+  };
+}
+
 /**
  * 获取本地时间,格式:年月日时分秒
  */
-function localNowDateTime() {
+export function localNowDateTime() {
   return formatDate("YYYY年MM月DD日HH时mm分ss秒");
 }
 
@@ -219,7 +311,7 @@ function localNowDateTime() {
  * 获取指定元素个数的数组
  * @param {Number} num
  */
-function getNumList(num) {
+export function getNumList(num) {
   return "#".repeat(num).split("");
 }
 
@@ -227,7 +319,7 @@ function getNumList(num) {
  * 清除html标签
  * @param {String} str html字符串
  */
-function removeHtmlTag(str) {
+export function removeHtmlTag(str) {
   return str.replace(/<[^>]+>/g, "");
 }
 
@@ -235,13 +327,13 @@ function removeHtmlTag(str) {
  * 计算总数
  * @param {Array} dataList 需要统计的数组
  */
-function calcSum(dataList) {
+export function calcSum(dataList) {
   return dataList.reduce(function(total, item) {
     return total + item;
   }, 0);
 }
 
-function isEmptyObject(obj) {
+export function isEmptyObject(obj) {
   return !Object.keys(obj).length;
 }
 
@@ -249,7 +341,7 @@ function isEmptyObject(obj) {
  * 解决后台返回的数据中number位数超过16位的情况
  * @param {String} text json格式字符串
  */
-function jsonBigNumberToString(text) {
+export function jsonBigNumberToString(text) {
   return text
     .replace(/\\":[0-9]{16,19}/g, function(match) {
       return match.slice(0, 3) + '\\"' + match.slice(3) + '\\"';
@@ -259,14 +351,14 @@ function jsonBigNumberToString(text) {
     });
 }
 
-function humpToLowLine(a) {
+export function humpToLowLine(a) {
   return a
     .replace(/([A-Z])/g, "-$1")
     .toLowerCase()
     .slice(1);
 }
 
-function pickByNotNull(params) {
+export function pickByNotNull(params) {
   let nData = {};
   Object.entries(params).forEach(([key, val]) => {
     if (val === null || val === "null" || val === "") return;
@@ -274,23 +366,3 @@ function pickByNotNull(params) {
   });
   return nData;
 }
-
-export {
-  objTypeOf,
-  deepCopy,
-  objAssign,
-  download,
-  downloadBlob,
-  tableAction,
-  randomCode,
-  qsParams,
-  formatDate,
-  localNowDateTime,
-  getNumList,
-  removeHtmlTag,
-  calcSum,
-  isEmptyObject,
-  jsonBigNumberToString,
-  humpToLowLine,
-  pickByNotNull
-};

+ 5 - 4
src/views/Home.vue

@@ -138,6 +138,7 @@ export default {
       breadcrumbs: [],
       validRoutes: [],
       username: this.$ls.get("user", { realName: "" }).realName,
+      userRoles: this.$ls.get("user", { roleList: [] }).roleList,
       schoolLogo: this.$ls.get("schoolLogo"),
       schoolName: this.$ls.get("schoolName"),
       menuDailogIsShow: false
@@ -157,7 +158,7 @@ export default {
   },
   methods: {
     ...mapActions("exam", ["updateWaitTaskCount"]),
-    getMenus() {
+    getMenus1() {
       this.menus = localNavs;
       if (this.$route.name === "Home") {
         this.$router.replace({
@@ -165,12 +166,12 @@ export default {
         });
       } else {
         this.actCurNav();
-        // this.updateWaitTaskCount();
+        this.updateWaitTaskCount(this.userRoles);
       }
     },
-    async getMenus1() {
+    async getMenus() {
       const data = await sysMenu();
-      const { menus, firstRouter } = this.menusToTree(data.records);
+      const { menus, firstRouter } = this.menusToTree(data);
       console.log(menus, firstRouter);
       this.menus = menus;