فهرست منبع

分档设置修改

zhangjie 2 سال پیش
والد
کامیت
266594247b
6فایلهای تغییر یافته به همراه1423 افزوده شده و 958 حذف شده
  1. 3 0
      src/api.js
  2. 29 0
      src/assets/styles/main.less
  3. 148 136
      src/constants/enumerate.js
  4. 633 427
      src/modules/grading-set/GradingLevelSet.vue
  5. 436 225
      src/modules/grading-set/GradingRuleSet.vue
  6. 174 170
      src/modules/main/WorkManage.vue

+ 3 - 0
src/api.js

@@ -133,6 +133,9 @@ export const updateCollectParams = datas => {
 export const updateLevelParams = datas => {
 export const updateLevelParams = datas => {
   return $post("/api/param/level", datas);
   return $post("/api/param/level", datas);
 };
 };
+export const updateCoarseLevelParams = datas => {
+  return $post("/api/param/first_level", datas);
+};
 // update-paper
 // update-paper
 // grading-rule-set
 // grading-rule-set
 
 

+ 29 - 0
src/assets/styles/main.less

@@ -784,3 +784,32 @@
     background-color: @background-color;
     background-color: @background-color;
   }
   }
 }
 }
+
+// grading-rule-set
+.grading-rule-set {
+  display: flex;
+  align-items: stretch;
+  justify-content: space-between;
+  .rule-part {
+    position: relative;
+    padding: 0 15px 36px;
+    flex-grow: 1;
+
+    &:nth-of-type(2) {
+      border-left: 1px dashed #d0d0d0;
+    }
+
+    &-title {
+      font-size: 18px;
+      font-weight: 600;
+      margin-bottom: 30px;
+      text-align: center;
+    }
+    &-footer {
+      position: absolute;
+      bottom: 0;
+      left: 245px;
+      z-index: 8;
+    }
+  }
+}

+ 148 - 136
src/constants/enumerate.js

@@ -1,136 +1,148 @@
-// 启用/禁用
-export const ABLE_TYPE = {
-  DISABLE: "禁用",
-  ENABLE: "启用"
-};
-
-// 性别
-export const GENDER_TYPE = {
-  MALE: "男",
-  FEMALE: "女"
-};
-
-export const BOOLEAN_TYPE = {
-  0: "否",
-  1: "是"
-};
-
-// subject step
-export const SUBJECT_STAGE = {
-  INIT: "采集阶段",
-  LEVEL: "分档阶段",
-  SCORE: "打分阶段"
-};
-
-// grading level
-export const LEVEL_TYPE = {
-  ADMITED: "间隔给分",
-  UNADMIT: "手动给分"
-};
-export const PROP_DENOMINATOR_TYPE = {
-  1: "全部考生数",
-  2: "去掉缺考考生数"
-};
-
-// student score
-export const CODE_TYPE = {
-  examNumber: "试卷考号",
-  paper: "试卷密号",
-  task: "任务密号"
-};
-
-// role type
-export const ROLE_TYPE = {
-  MARKER: "评卷员",
-  MARK_LEADER: "科组长"
-};
-export const MARK_LEADER_RIGHTS = {
-  oneClickLevel: {
-    name: "一键定档",
-    action: "leveling"
-  },
-  standardVolume: {
-    name: "设立标准卷",
-    action: "sampling"
-  },
-  levelCallback: {
-    name: "建议档位打回",
-    action: "reject"
-  }
-};
-
-export const MARKER_RIGHT_TYPE = {
-  ALLOW_LEVELING: "只允许分档",
-  ALLOW_SCORING: "只允许打分",
-  ALLOW_ALL: "允许分档打分"
-};
-// sort
-export const SORT_ORDER_TYPE = {
-  0: "乱序",
-  1: "定序"
-};
-export const SORT_RULE_TYPE = {
-  1: "按时间",
-  2: "按考号"
-};
-// cafa-exception
-export const CAFA_EXCEPTION_TYPE = {
-  0: "缺考",
-  1: "手工绑定"
-  // 2: "关联考号"
-};
-
-// action-type
-export const ACTION_TYPE = {
-  1: "分档",
-  2: "打分",
-  3: "回评档位",
-  4: "回评分数",
-  5: "档位打回",
-  6: "档位打回回评",
-  7: "一键定档",
-  8: "标准卷设置",
-  9: "采集",
-  10: "系统自动打回",
-  11: "重评",
-  12: "手动打回",
-  13: "档位落差"
-};
-
-// image-name-type
-export const IMAGE_NAME_TYPE = {
-  0: "按考号命名",
-  1: "随机码命名"
-};
-export const EXPORT_IMAGE_NAME_TYPE = {
-  1: "考号+姓名",
-  2: "流水号"
-};
-// image-type
-export const IMAGE_TYPE = {
-  1: "原图",
-  2: "裁切图"
-};
-
-// paper-level-known-type
-export const PAPER_LEVEL_KNOWN_TYPE = {
-  0: "未知",
-  1: "已知"
-};
-// paper-upload-type
-export const PAPER_UPLOAD_TYPE = {
-  0: "未上传",
-  1: "已上传"
-};
-
-// score-handle-type
-export const SCORE_HANDLE_TYPE = {
-  0: "非零进一",
-  1: "四舍五入"
-};
-
-// apply-change-level-status
-export const CHANGE_LEVEL_STATUS = {
-  0: "未处理",
-  1: "已处理"
-};
+// 启用/禁用
+export const ABLE_TYPE = {
+  DISABLE: "禁用",
+  ENABLE: "启用"
+};
+
+// 性别
+export const GENDER_TYPE = {
+  MALE: "男",
+  FEMALE: "女"
+};
+
+export const BOOLEAN_TYPE = {
+  0: "否",
+  1: "是"
+};
+
+// subject step
+export const SUBJECT_STAGE = {
+  INIT: "采集阶段",
+  LEVEL: "分档阶段",
+  SCORE: "打分阶段"
+};
+
+// grading level
+export const LEVEL_TYPE = {
+  ADMITED: "间隔给分",
+  UNADMIT: "手动给分"
+};
+export const PROP_DENOMINATOR_TYPE = {
+  1: "全部考生数",
+  2: "去掉缺考考生数"
+};
+
+export const COARSE_LEVEL_TYPE = {
+  1: "一",
+  2: "二",
+  3: "三",
+  4: "四",
+  5: "五",
+  6: "六",
+  7: "七",
+  8: "八",
+  9: "九"
+};
+
+// student score
+export const CODE_TYPE = {
+  examNumber: "试卷考号",
+  paper: "试卷密号",
+  task: "任务密号"
+};
+
+// role type
+export const ROLE_TYPE = {
+  MARKER: "评卷员",
+  MARK_LEADER: "科组长"
+};
+export const MARK_LEADER_RIGHTS = {
+  oneClickLevel: {
+    name: "一键定档",
+    action: "leveling"
+  },
+  standardVolume: {
+    name: "设立标准卷",
+    action: "sampling"
+  },
+  levelCallback: {
+    name: "建议档位打回",
+    action: "reject"
+  }
+};
+
+export const MARKER_RIGHT_TYPE = {
+  ALLOW_LEVELING: "只允许分档",
+  ALLOW_SCORING: "只允许打分",
+  ALLOW_ALL: "允许分档打分"
+};
+// sort
+export const SORT_ORDER_TYPE = {
+  0: "乱序",
+  1: "定序"
+};
+export const SORT_RULE_TYPE = {
+  1: "按时间",
+  2: "按考号"
+};
+// cafa-exception
+export const CAFA_EXCEPTION_TYPE = {
+  0: "缺考",
+  1: "手工绑定"
+  // 2: "关联考号"
+};
+
+// action-type
+export const ACTION_TYPE = {
+  1: "分档",
+  2: "打分",
+  3: "回评档位",
+  4: "回评分数",
+  5: "档位打回",
+  6: "档位打回回评",
+  7: "一键定档",
+  8: "标准卷设置",
+  9: "采集",
+  10: "系统自动打回",
+  11: "重评",
+  12: "手动打回",
+  13: "档位落差"
+};
+
+// image-name-type
+export const IMAGE_NAME_TYPE = {
+  0: "按考号命名",
+  1: "随机码命名"
+};
+export const EXPORT_IMAGE_NAME_TYPE = {
+  1: "考号+姓名",
+  2: "流水号"
+};
+// image-type
+export const IMAGE_TYPE = {
+  1: "原图",
+  2: "裁切图"
+};
+
+// paper-level-known-type
+export const PAPER_LEVEL_KNOWN_TYPE = {
+  0: "未知",
+  1: "已知"
+};
+// paper-upload-type
+export const PAPER_UPLOAD_TYPE = {
+  0: "未上传",
+  1: "已上传"
+};
+
+// score-handle-type
+export const SCORE_HANDLE_TYPE = {
+  0: "非零进一",
+  1: "四舍五入"
+};
+
+// apply-change-level-status
+export const CHANGE_LEVEL_STATUS = {
+  0: "未处理",
+  1: "已处理"
+};

+ 633 - 427
src/modules/grading-set/GradingLevelSet.vue

@@ -1,427 +1,633 @@
-<template>
-  <div class="grading-level-set">
-    <Button
-      class="level-add-btn"
-      type="success"
-      icon="recode-white icon"
-      shape="circle"
-      @click="toAdd"
-      >新增档位</Button
-    >
-    <table class="table table-noborder grading-table">
-      <tr>
-        <th>档位</th>
-        <th>最低分</th>
-        <th>最高分</th>
-        <th>给分间隔</th>
-        <th>典型值</th>
-        <th>类型</th>
-        <th>给分项</th>
-        <th>考区阈值%</th>
-        <th>占比阈值%</th>
-        <th>操作</th>
-      </tr>
-      <template v-for="(level, index) in levels">
-        <tr :key="index">
-          <td>
-            <Input
-              v-model="level.code"
-              style="width: 60px"
-              @on-blur="codeChange(level)"
-              v-if="level.canEdit && workDetail.modifyOtherVal"
-            ></Input>
-            <p v-else>{{ level.code }}</p>
-          </td>
-          <td>
-            <InputNumber
-              v-model="level.minScore"
-              :min="0"
-              :max="1000"
-              @on-blur="checkLevelValidate(level)"
-              v-if="level.canEdit && workDetail.modifyOtherVal"
-            ></InputNumber>
-            <p v-else>{{ level.minScore }}</p>
-          </td>
-          <td>
-            <InputNumber
-              v-model="level.maxScore"
-              :min="1"
-              :max="1000"
-              @on-blur="checkLevelValidate(level)"
-              v-if="level.canEdit && workDetail.modifyOtherVal"
-            ></InputNumber>
-            <p v-else>{{ level.maxScore }}</p>
-          </td>
-          <td style="min-width: 100px">
-            <InputNumber
-              v-model="level.intervalScore"
-              :min="1"
-              :max="100"
-              :precision="0"
-              :disabled="!level.canEdit"
-              @on-blur="checkLevelValidate(level)"
-              v-if="
-                level.levelType === 'ADMITED' &&
-                  level.canEdit &&
-                  workDetail.modifyOtherVal
-              "
-            ></InputNumber>
-            <p v-else>{{ level.intervalScore }}</p>
-          </td>
-          <td>
-            <InputNumber
-              v-model="level.weight"
-              :min="1"
-              :max="100"
-              @on-blur="checkLevelValidate(level)"
-              v-if="level.canEdit && workDetail.modifyOtherVal"
-            ></InputNumber>
-            <p v-else>{{ level.weight }}</p>
-          </td>
-          <td>
-            <Select
-              v-model="level.levelType"
-              @on-change="levelTypeChange(level)"
-              style="width: 120px"
-              v-if="level.canEdit && workDetail.modifyOtherVal"
-            >
-              <Option
-                v-for="(val, key) in LEVEL_TYPE"
-                :key="key"
-                :value="key"
-                >{{ val }}</Option
-              >
-            </Select>
-            <p v-else>{{ LEVEL_TYPE[level.levelType] }}</p>
-          </td>
-          <td style="min-width: 140px">
-            <Input
-              v-model="level.scoreList"
-              @on-blur="checkLevelValidate(level)"
-              v-if="
-                level.levelType === 'UNADMIT' &&
-                  level.canEdit &&
-                  workDetail.modifyOtherVal
-              "
-            ></Input>
-            <p v-else>{{ level.scoreList }}</p>
-          </td>
-          <td>
-            <InputNumber
-              v-model="level.kdpt"
-              :min="1"
-              :max="100"
-              @on-blur="checkLevelValidate(level)"
-              v-if="level.canEdit"
-            ></InputNumber>
-            <p v-else>{{ level.kdpt }}</p>
-          </td>
-          <td>
-            <InputNumber
-              v-model="level.pt"
-              :min="1"
-              :max="100"
-              @on-blur="checkLevelValidate(level)"
-              v-if="level.canEdit"
-            ></InputNumber>
-            <p v-else>{{ level.pt }}</p>
-          </td>
-          <td class="table-action">
-            <div style="width: 60px">
-              <Icon type="md-create" title="编辑" @click="toEdit(index)" />
-              <Icon
-                class="icon-danger"
-                type="md-trash"
-                title="删除"
-                @click="toDelete(index)"
-                v-if="workDetail.modifyOtherVal"
-              />
-            </div>
-          </td>
-        </tr>
-        <tr class="tr-tips-error" v-if="level.errors" :key="index + '1'">
-          <td>
-            {{ level.errors.code }}
-          </td>
-          <td>
-            {{ level.errors.minScore }}
-          </td>
-          <td>
-            {{ level.errors.maxScore }}
-          </td>
-          <td>
-            {{ level.errors.intervalScore }}
-          </td>
-          <td>
-            {{ level.errors.weight }}
-          </td>
-          <td></td>
-          <td>
-            {{ level.errors.scoreList }}
-          </td>
-          <td>
-            {{ level.errors.kdpt }}
-          </td>
-          <td>
-            {{ level.errors.pt }}
-          </td>
-          <td></td>
-        </tr>
-      </template>
-    </table>
-
-    <div class="text-center">
-      <Button
-        type="primary"
-        shape="circle"
-        @click="toSubmit"
-        style="width: 80px;"
-        :disabled="isSubmit"
-        >确定</Button
-      >
-    </div>
-  </div>
-</template>
-
-<script>
-import { workDetail, updateWork } from "@/api";
-import { LEVEL_TYPE } from "@/constants/enumerate";
-import schema from "async-validator";
-schema.warning = function() {};
-
-const initLevel = {
-  id: null,
-  workId: null,
-  code: null,
-  levelValue: 0,
-  maxScore: null,
-  minScore: null,
-  intervalScore: null,
-  weight: null,
-  levelType: "ADMITED",
-  scoreList: null,
-  pt: null,
-  kdpt: null
-};
-
-export default {
-  name: "grading-level-set",
-  data() {
-    return {
-      LEVEL_TYPE,
-      workId: this.$route.params.workId,
-      letterRelateNumber: {},
-      levels: [],
-      workDetail: {},
-      isSubmit: false
-    };
-  },
-  mounted() {
-    this.initData();
-  },
-  methods: {
-    initData() {
-      const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-      letters.split("").map((item, index) => {
-        this.letterRelateNumber[item] = index + 1;
-      });
-
-      this.getData();
-    },
-    async getData() {
-      const data = await workDetail(this.workId);
-      this.workDetail = data;
-      this.levels = data.levels.map(item => {
-        item.canEdit = false;
-        return item;
-      });
-    },
-    checkLevelCodeIsContinuous() {
-      let levelIsContinuous = true;
-      for (var i = 0, num = this.levels.length; i < num; i++) {
-        if (i > 0) {
-          const beforeCodeNum = this.letterRelateNumber[
-            this.levels[i - 1].code
-          ];
-          const curCodeNum = this.letterRelateNumber[this.levels[i].code];
-          levelIsContinuous =
-            levelIsContinuous && curCodeNum - beforeCodeNum === 1;
-
-          if (!levelIsContinuous) {
-            return false;
-          }
-        }
-      }
-
-      return true;
-    },
-    levelTypeChange(level) {
-      if (level.levelType === "ADMITED") {
-        level.scoreList = null;
-      } else {
-        level.intervalScore = null;
-      }
-    },
-    codeChange(level) {
-      level.code = level.code.toUpperCase();
-      this.levels.sort((a, b) => {
-        if (
-          !a.code ||
-          !b.code ||
-          !this.letterRelateNumber[a.code] ||
-          !this.letterRelateNumber[b.code]
-        )
-          return 0;
-
-        return (
-          this.letterRelateNumber[a.code] - this.letterRelateNumber[b.code]
-        );
-      });
-      this.checkLevelValidate(level);
-    },
-    getNextLevelCode() {
-      const codeNumbers = this.levels.map(
-        level => this.letterRelateNumber[level.code] || 0
-      );
-      const maxCodeNumber = Math.max.apply(null, codeNumbers);
-      const nextLevel = Object.entries(this.letterRelateNumber).find(
-        ([key, val]) => {
-          return maxCodeNumber < val;
-        }
-      );
-      return nextLevel ? nextLevel[0] : "";
-    },
-    toAdd() {
-      let level = { ...initLevel };
-      level.workId = this.workId;
-      level.code = this.getNextLevelCode();
-      this.levels.push(level);
-    },
-    toEdit(index) {
-      this.levels[index].canEdit = true;
-      this.$forceUpdate();
-    },
-    toDelete(index) {
-      this.levels.splice(index, 1);
-    },
-    checkLevelValidate(level) {
-      const descriptor = {
-        code: {
-          type: "string",
-          required: true,
-          pattern: /^[A-Z]{1}$/,
-          message: "请输入单个大写字母"
-        },
-        minScore: {
-          type: "number",
-          required: true,
-          message: "请输入最低分"
-        },
-        maxScore: {
-          type: "number",
-          required: true,
-          validator: (rule, value, callback) => {
-            if (!value || value < level.minScore) {
-              callback(new Error("最高分不得小于最低分"));
-            } else {
-              callback();
-            }
-          }
-        },
-        intervalScore: {
-          type: "number",
-          validator: (rule, value, callback) => {
-            if (level.levelType === "ADMITED" && !value) {
-              callback(new Error("请输入给分间隔"));
-            } else {
-              callback();
-            }
-          }
-        },
-        weight: {
-          type: "number",
-          required: true,
-          message: "请输入典型值"
-        },
-        scoreList: {
-          type: "string",
-          validator: (rule, value, callback) => {
-            if (level.levelType !== "UNADMIT") return callback();
-
-            if (level.levelType === "UNADMIT" && !value) {
-              return callback(new Error("请输入给分项"));
-            }
-            if (!value.match(/^[0-9,]+$/)) {
-              return callback(new Error("给分项只能包含数字和英文逗号"));
-            }
-            const unvalid = value
-              .split(",")
-              .filter(item => item)
-              .some(item => {
-                const num = item * 1;
-                return num < level.minScore || num > level.maxScore;
-              });
-            if (unvalid) {
-              return callback(
-                new Error("给分项包含的分值只能介于最低分和最高分之间")
-              );
-            }
-
-            callback();
-          }
-        },
-        pt: {
-          type: "number",
-          required: true,
-          message: "请输入占比阀值"
-        },
-        kdpt: {
-          type: "number",
-          required: true,
-          message: "请输入考区阀值"
-        }
-      };
-      return new schema(descriptor)
-        .validate(level)
-        .then(() => {
-          if (level.errors) level.errors = null;
-        })
-        .catch(({ errors, fields }) => {
-          let errorMsgs = {};
-          errors.map(error => {
-            errorMsgs[error.field] = error.message;
-          });
-          this.$set(level, "errors", errorMsgs);
-          return { errors };
-        });
-    },
-    async toSubmit() {
-      const validatorAll = this.levels.map(level =>
-        this.checkLevelValidate(level)
-      );
-      const result = await Promise.all(validatorAll);
-      const hasUnvalidate = result.some(item => !!item);
-
-      if (hasUnvalidate) return;
-
-      if (!this.checkLevelCodeIsContinuous()) {
-        this.$Message.error("请保持档位连续!");
-        return;
-      }
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      this.workDetail.levels = this.levels;
-      const data = await updateWork(this.workDetail).catch(() => {
-        this.isSubmit = false;
-      });
-
-      if (!data) return;
-
-      this.isSubmit = false;
-      this.getData();
-      this.$Message.success("保存成功!");
-    }
-  }
-};
-</script>
+<template>
+  <div class="grading-level-set">
+    <Button
+      class="level-add-btn"
+      type="success"
+      icon="recode-white icon"
+      shape="circle"
+      @click="toAdd"
+      >新增档位</Button
+    >
+    <table class="table table-noborder grading-table">
+      <tr>
+        <th>档位</th>
+        <th>最低分</th>
+        <th>最高分</th>
+        <th>给分间隔</th>
+        <th>典型值</th>
+        <th>类型</th>
+        <th>给分项</th>
+        <template v-if="coarseLevel">
+          <th>粗档位</th>
+          <th>粗档位典型值</th>
+        </template>
+        <th>考区阈值%</th>
+        <th>占比阈值%</th>
+        <th>操作</th>
+      </tr>
+      <template v-for="(level, index) in levels">
+        <tr :key="index">
+          <td>
+            <Input
+              v-model="level.code"
+              style="width: 60px"
+              @on-blur="codeChange(level)"
+              v-if="level.canEdit && workDetail.modifyOtherVal"
+            ></Input>
+            <p v-else>{{ level.code }}</p>
+          </td>
+          <td>
+            <InputNumber
+              v-model="level.minScore"
+              :min="0"
+              :max="1000"
+              @on-blur="checkLevelValidate(level)"
+              @on-change="checkCoarseCodeValidate()"
+              v-if="level.canEdit && workDetail.modifyOtherVal"
+            ></InputNumber>
+            <p v-else>{{ level.minScore }}</p>
+          </td>
+          <td>
+            <InputNumber
+              v-model="level.maxScore"
+              :min="1"
+              :max="1000"
+              @on-blur="checkLevelValidate(level)"
+              @on-change="checkCoarseCodeValidate()"
+              v-if="level.canEdit && workDetail.modifyOtherVal"
+            ></InputNumber>
+            <p v-else>{{ level.maxScore }}</p>
+          </td>
+          <td style="min-width: 100px">
+            <InputNumber
+              v-model="level.intervalScore"
+              :min="1"
+              :max="100"
+              :precision="0"
+              :disabled="!level.canEdit"
+              @on-blur="checkLevelValidate(level)"
+              v-if="
+                level.levelType === 'ADMITED' &&
+                  level.canEdit &&
+                  workDetail.modifyOtherVal
+              "
+            ></InputNumber>
+            <p v-else>{{ level.intervalScore }}</p>
+          </td>
+          <td>
+            <InputNumber
+              v-model="level.weight"
+              :min="1"
+              :max="100"
+              @on-blur="checkLevelValidate(level)"
+              v-if="level.canEdit && workDetail.modifyOtherVal"
+            ></InputNumber>
+            <p v-else>{{ level.weight }}</p>
+          </td>
+          <td>
+            <Select
+              v-model="level.levelType"
+              @on-change="levelTypeChange(level)"
+              style="width: 120px"
+              v-if="level.canEdit && workDetail.modifyOtherVal"
+            >
+              <Option
+                v-for="(val, key) in LEVEL_TYPE"
+                :key="key"
+                :value="key"
+                >{{ val }}</Option
+              >
+            </Select>
+            <p v-else>{{ LEVEL_TYPE[level.levelType] }}</p>
+          </td>
+          <td style="min-width: 140px">
+            <Input
+              v-model="level.scoreList"
+              @on-blur="checkLevelValidate(level)"
+              v-if="
+                level.levelType === 'UNADMIT' &&
+                  level.canEdit &&
+                  workDetail.modifyOtherVal
+              "
+            ></Input>
+            <p v-else>{{ level.scoreList }}</p>
+          </td>
+          <template v-if="coarseLevel">
+            <!-- coarse_code -->
+            <td>
+              <Select
+                v-model="level.coarseCode"
+                @on-change="coarseCodeChange(level)"
+                style="min-width: 80px"
+                v-if="level.canEdit && workDetail.modifyOtherVal"
+              >
+                <Option
+                  v-for="(val, key) in COARSE_LEVEL_TYPE"
+                  :key="key"
+                  :value="key * 1"
+                  >{{ val }}</Option
+                >
+              </Select>
+              <p v-else>{{ COARSE_LEVEL_TYPE[level.coarseCode] }}</p>
+            </td>
+            <!-- coarse_weight -->
+            <td>
+              <InputNumber
+                v-model="level.coarseWeight"
+                :min="1"
+                :max="100"
+                @on-change="coarseWeightChange(level)"
+                v-if="level.canEdit && workDetail.modifyOtherVal"
+              ></InputNumber>
+              <p v-else>{{ level.coarseWeight }}</p>
+            </td>
+          </template>
+          <td>
+            <InputNumber
+              v-model="level.kdpt"
+              :min="1"
+              :max="100"
+              @on-blur="checkLevelValidate(level)"
+              v-if="level.canEdit"
+            ></InputNumber>
+            <p v-else>{{ level.kdpt }}</p>
+          </td>
+          <td>
+            <InputNumber
+              v-model="level.pt"
+              :min="1"
+              :max="100"
+              @on-blur="checkLevelValidate(level)"
+              v-if="level.canEdit"
+            ></InputNumber>
+            <p v-else>{{ level.pt }}</p>
+          </td>
+          <td class="table-action">
+            <div style="width: 60px">
+              <Icon type="md-create" title="编辑" @click="toEdit(index)" />
+              <Icon
+                class="icon-danger"
+                type="md-trash"
+                title="删除"
+                @click="toDelete(index)"
+                v-if="workDetail.modifyOtherVal"
+              />
+            </div>
+          </td>
+        </tr>
+        <tr class="tr-tips-error" v-if="level.errors" :key="index + '1'">
+          <td>
+            {{ level.errors.code }}
+          </td>
+          <td>
+            {{ level.errors.minScore }}
+          </td>
+          <td>
+            {{ level.errors.maxScore }}
+          </td>
+          <td>
+            {{ level.errors.intervalScore }}
+          </td>
+          <td>
+            {{ level.errors.weight }}
+          </td>
+          <td></td>
+          <td>
+            {{ level.errors.scoreList }}
+          </td>
+          <template v-if="coarseLevel">
+            <td>
+              {{ level.errors.coarseCode }}
+            </td>
+            <td>
+              {{ level.errors.coarseWeight }}
+            </td>
+          </template>
+          <td>
+            {{ level.errors.kdpt }}
+          </td>
+          <td>
+            {{ level.errors.pt }}
+          </td>
+          <td></td>
+        </tr>
+      </template>
+    </table>
+
+    <div class="text-center">
+      <Button
+        type="primary"
+        shape="circle"
+        @click="toSubmit"
+        style="width: 80px;"
+        :disabled="isSubmit"
+        >确定</Button
+      >
+      <Checkbox v-model="coarseLevel">coarseLevel</Checkbox>
+    </div>
+  </div>
+</template>
+
+<script>
+import { workDetail, updateWork } from "@/api";
+import { LEVEL_TYPE, COARSE_LEVEL_TYPE } from "@/constants/enumerate";
+import schema from "async-validator";
+schema.warning = function() {};
+
+const initLevel = {
+  id: null,
+  workId: null,
+  code: null,
+  levelValue: 0,
+  maxScore: null,
+  minScore: null,
+  intervalScore: null,
+  weight: null,
+  coarseCode: null,
+  coarseWeight: null,
+  levelType: "ADMITED",
+  scoreList: null,
+  pt: null,
+  kdpt: null
+};
+
+export default {
+  name: "grading-level-set",
+  data() {
+    return {
+      LEVEL_TYPE,
+      COARSE_LEVEL_TYPE,
+      workId: this.$route.params.workId,
+      letterRelateNumber: {},
+      levels: [],
+      workDetail: {},
+      isSubmit: false,
+      coarseLevel: false
+    };
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    initData() {
+      const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+      letters.split("").map((item, index) => {
+        this.letterRelateNumber[item] = index + 1;
+      });
+
+      this.getData();
+    },
+    async getData() {
+      const data = await workDetail(this.workId);
+      this.workDetail = data;
+      this.levels = data.levels.map(item => {
+        let nitem = { ...item };
+        nitem.canEdit = false;
+        nitem.coarseCode = null;
+        nitem.coarseWeight = null;
+        return nitem;
+      });
+    },
+    checkLevelCodeIsContinuous() {
+      let levelIsContinuous = true;
+      for (var i = 0, num = this.levels.length; i < num; i++) {
+        if (i > 0) {
+          const beforeCodeNum = this.letterRelateNumber[
+            this.levels[i - 1].code
+          ];
+          const curCodeNum = this.letterRelateNumber[this.levels[i].code];
+          levelIsContinuous =
+            levelIsContinuous && curCodeNum - beforeCodeNum === 1;
+
+          if (!levelIsContinuous) {
+            return false;
+          }
+        }
+      }
+
+      return true;
+    },
+    levelTypeChange(level) {
+      if (level.levelType === "ADMITED") {
+        level.scoreList = null;
+      } else {
+        level.intervalScore = null;
+      }
+    },
+    codeChange(level) {
+      level.code = level.code.toUpperCase();
+      this.levels.sort((a, b) => {
+        if (
+          !a.code ||
+          !b.code ||
+          !this.letterRelateNumber[a.code] ||
+          !this.letterRelateNumber[b.code]
+        )
+          return 0;
+
+        return (
+          this.letterRelateNumber[a.code] - this.letterRelateNumber[b.code]
+        );
+      });
+      this.checkLevelValidate(level);
+    },
+    getNextLevelCode() {
+      const codeNumbers = this.levels.map(
+        level => this.letterRelateNumber[level.code] || 0
+      );
+      const maxCodeNumber = Math.max.apply(null, codeNumbers);
+      const nextLevel = Object.entries(this.letterRelateNumber).find(
+        ([key, val]) => {
+          return maxCodeNumber < val;
+        }
+      );
+      return nextLevel ? nextLevel[0] : "";
+    },
+    toAdd() {
+      let level = { ...initLevel };
+      level.workId = this.workId;
+      level.code = this.getNextLevelCode();
+      this.levels.push(level);
+    },
+    toEdit(index) {
+      this.levels[index].canEdit = true;
+      this.$forceUpdate();
+    },
+    toDelete(index) {
+      this.levels.splice(index, 1);
+    },
+    coarseCodeChange(level) {
+      let codeWeight = {};
+      this.levels.forEach(item => {
+        if (item.coarseCode && item.coarseWeight) {
+          codeWeight[item.coarseCode] = item.coarseWeight;
+        }
+      });
+      level.coarseWeight = codeWeight[level.coarseCode] || null;
+
+      this.checkCoarseCodeValidate();
+    },
+    coarseWeightChange(level) {
+      this.levels
+        .filter(
+          item =>
+            item.coarseCode &&
+            item.coarseCode === level.coarseCode &&
+            item.code !== level.code
+        )
+        .forEach(item => {
+          item.coarseWeight = level.coarseWeight;
+        });
+      this.checkCoarseCodeValidate();
+    },
+    resetError(errors, fields, fieldVals) {
+      if (!errors) return null;
+      let nerrors = { ...errors };
+      fields.forEach(field => {
+        if (fieldVals && fieldVals[field]) {
+          this.$set(nerrors, field, fieldVals[field]);
+        } else {
+          delete nerrors[field];
+        }
+      });
+      return Object.keys(nerrors).length ? nerrors : null;
+    },
+    checkLevelValidate(level) {
+      const descriptor = {
+        code: {
+          type: "string",
+          required: true,
+          pattern: /^[A-Z]{1}$/,
+          message: "请输入单个大写字母"
+        },
+        minScore: {
+          type: "number",
+          required: true,
+          message: "请输入最低分"
+        },
+        maxScore: {
+          type: "number",
+          required: true,
+          validator: (rule, value, callback) => {
+            if (!value || value < level.minScore) {
+              callback(new Error("最高分不得小于最低分"));
+            } else {
+              callback();
+            }
+          }
+        },
+        intervalScore: {
+          type: "number",
+          validator: (rule, value, callback) => {
+            if (level.levelType === "ADMITED" && !value) {
+              callback(new Error("请输入给分间隔"));
+            } else {
+              callback();
+            }
+          }
+        },
+        weight: {
+          type: "number",
+          validator: (rule, value, callback) => {
+            if (!value) return callback(new Error("请输入典型值"));
+
+            if (value < level.minScore || value > level.maxScore) {
+              return callback(
+                new Error(
+                  `典型值只能介于${level.minScore}和${level.maxScore}之间`
+                )
+              );
+            }
+
+            callback();
+          }
+        },
+        scoreList: {
+          type: "string",
+          validator: (rule, value, callback) => {
+            if (level.levelType !== "UNADMIT") return callback();
+
+            if (level.levelType === "UNADMIT" && !value) {
+              return callback(new Error("请输入给分项"));
+            }
+            if (!value.match(/^[0-9,]+$/)) {
+              return callback(new Error("给分项只能包含数字和英文逗号"));
+            }
+            const unvalid = value
+              .split(",")
+              .filter(item => item)
+              .some(item => {
+                const num = item * 1;
+                return num < level.minScore || num > level.maxScore;
+              });
+            if (unvalid) {
+              return callback(
+                new Error("给分项包含的分值只能介于最低分和最高分之间")
+              );
+            }
+
+            callback();
+          }
+        },
+        pt: {
+          type: "number",
+          required: true,
+          message: "请输入占比阀值"
+        },
+        kdpt: {
+          type: "number",
+          required: true,
+          message: "请输入考区阀值"
+        }
+      };
+      const fields = Object.keys(descriptor);
+      return new schema(descriptor)
+        .validate(level)
+        .then(() => {
+          this.$set(level, "errors", this.resetError(level.errors, fields));
+        })
+        .catch(({ errors }) => {
+          if (!errors) return;
+
+          let errorMsgs = {};
+          errors.map(error => {
+            errorMsgs[error.field] = error.message;
+          });
+          this.$set(
+            level,
+            "errors",
+            this.resetError(level.errors || {}, fields, errorMsgs)
+          );
+          return errors;
+        });
+    },
+    async checkCoarseCodeValidate(checkCodeRequired) {
+      if (!this.coarseLevel) return;
+      let weightRanges = {};
+      this.levels.forEach(level => {
+        if (!level.coarseCode) return;
+        let range = weightRanges[level.coarseCode] || {
+          sn: 100,
+          en: 0
+        };
+        range.sn = Math.min(range.sn, level.minScore);
+        range.en = Math.max(range.en, level.maxScore);
+        weightRanges[level.coarseCode] = range;
+      });
+
+      const coarseCodeValidate = level => {
+        const descriptor = {
+          coarseCode: {
+            validator: (rule, value, callback) => {
+              if (!checkCodeRequired && !value) return callback();
+
+              if (checkCodeRequired && !value)
+                return callback(new Error(`请选择粗档位`));
+
+              const pos = this.levels.findIndex(
+                item => item.code === level.code
+              );
+              if (pos === 0 && value !== 1)
+                return callback(new Error(`只能选择粗档位"一"`));
+
+              if (pos > 0) {
+                const prevLevel = this.levels[pos - 1];
+                if (prevLevel && prevLevel.coarseCode) {
+                  const codeDis = value - prevLevel.coarseCode;
+                  if (codeDis < 0 || codeDis > 1) {
+                    return callback(new Error(`请保持粗档位连续`));
+                  }
+                }
+
+                // const nextLevel = this.levels[pos + 1];
+                // if (nextLevel && nextLevel.coarseCode) {
+                //   const codeDis = nextLevel.coarseCode - value;
+                //   if (codeDis < 0 || codeDis > 1) {
+                //     return callback(new Error(`请保持粗档位连续`));
+                //   }
+                // }
+              }
+
+              callback();
+            }
+          },
+          coarseWeight: {
+            validator: (rule, value, callback) => {
+              if (!checkCodeRequired) {
+                if (level.coarseCode && !value)
+                  return callback(new Error(`请输入粗档位典型值`));
+                if (!level.coarseCode) return callback();
+              }
+              if (checkCodeRequired && !value)
+                return callback(new Error(`请输入粗档位典型值`));
+
+              const range = weightRanges[level.coarseCode];
+              if (value < range.sn || value > range.en)
+                return callback(
+                  new Error(`粗档位典型值只能介于${range.sn}和${range.en}之间`)
+                );
+
+              callback();
+            }
+          }
+        };
+        const fields = Object.keys(descriptor);
+
+        return new schema(descriptor)
+          .validate(level)
+          .then(() => {
+            this.$set(level, "errors", this.resetError(level.errors, fields));
+          })
+          .catch(({ errors }) => {
+            if (!errors) return;
+            let errorMsgs = {};
+            errors.map(error => {
+              errorMsgs[error.field] = error.message;
+            });
+            this.$set(
+              level,
+              "errors",
+              this.resetError(level.errors || {}, fields, errorMsgs)
+            );
+            return errors;
+          });
+      };
+
+      const validatorAll = this.levels.map(level => coarseCodeValidate(level));
+      const result = await Promise.all(validatorAll);
+      const hasUnvalidate = result.some(item => !!item);
+
+      return hasUnvalidate;
+    },
+    async toSubmit() {
+      const validatorAll = this.levels.map(level =>
+        this.checkLevelValidate(level)
+      );
+      const result = await Promise.all(validatorAll);
+      let hasUnvalidate = result.some(item => !!item);
+      if (hasUnvalidate) return;
+
+      hasUnvalidate = await this.checkCoarseCodeValidate(true);
+      if (hasUnvalidate) return;
+
+      if (!this.checkLevelCodeIsContinuous()) {
+        this.$Message.error("请保持档位连续!");
+        return;
+      }
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      this.workDetail.levels = this.levels;
+      const data = await updateWork(this.workDetail).catch(() => {
+        this.isSubmit = false;
+      });
+
+      if (!data) return;
+
+      this.isSubmit = false;
+      this.getData();
+      this.$Message.success("保存成功!");
+    }
+  }
+};
+</script>

+ 436 - 225
src/modules/grading-set/GradingRuleSet.vue

@@ -1,225 +1,436 @@
-<template>
-  <div class="grading-rule-set part-box">
-    <Form
-      ref="modalFormComp"
-      :model="modalForm"
-      :rules="rules"
-      :label-width="230"
-      style="width: 400px;"
-    >
-      <FormItem prop="deviation" label="仲裁档位差:">
-        <InputNumber
-          v-model="modalForm.deviation"
-          :min="1"
-          :max="100"
-          :disabled="!modalFormCanEdit"
-          style="width: 160px;"
-        ></InputNumber>
-      </FormItem>
-      <FormItem label="系统自动打回:">
-        <Select
-          v-model="modalForm.autoCallback"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in BOOLEAN_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem
-        v-if="modalForm.autoCallback"
-        prop="cumulativeError"
-        label="打回累计误差:"
-      >
-        <InputNumber
-          v-model="modalForm.cumulativeError"
-          :min="1"
-          :max="100"
-          :disabled="!modalFormCanEdit"
-          style="width: 160px;"
-        ></InputNumber>
-      </FormItem>
-      <FormItem v-if="modalForm.autoCallback" label="打回是否显示偏差方向:">
-        <Select
-          v-model="modalForm.autoCallbackShowDeviation"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in BOOLEAN_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem label="是否过半定档:">
-        <Select
-          v-model="modalForm.majority"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in BOOLEAN_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem label="是否启用取优原则:">
-        <Select
-          v-model="modalForm.takeBest"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in BOOLEAN_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem label="阅卷员是否显示所有试卷:">
-        <Select
-          v-model="modalForm.levelShowAllPaper"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in BOOLEAN_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem label="档位百分比分母:">
-        <Select
-          v-model="modalForm.propDenominator"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in PROP_DENOMINATOR_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem label="是否显示标准卷管理:">
-        <Select
-          v-model="modalForm.showStandardPaperManage"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in BOOLEAN_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem label="是否显示清除当前阅卷数据:">
-        <Select
-          v-model="modalForm.clearData"
-          :disabled="!modalFormCanEdit"
-          placeholder="请选择"
-        >
-          <Option
-            v-for="(val, key) in BOOLEAN_TYPE"
-            :key="key"
-            :value="key * 1"
-            :label="val"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem>
-        <Button
-          shape="circle"
-          style="width: 80px;"
-          @click="modalFormCanEdit = true"
-          >编辑</Button
-        >
-        <Button
-          type="primary"
-          shape="circle"
-          style="width: 80px;"
-          :disabled="isSubmit"
-          @click="submit"
-          >保存</Button
-        >
-      </FormItem>
-    </Form>
-  </div>
-</template>
-
-<script>
-import { getParamsSet, updateLevelParams } from "@/api";
-import { BOOLEAN_TYPE, PROP_DENOMINATOR_TYPE } from "@/constants/enumerate";
-import { numberValidator } from "@/plugins/formRules";
-
-export default {
-  name: "grading-rule-set",
-  data() {
-    return {
-      isSubmit: false,
-      workId: this.$route.params.workId,
-      BOOLEAN_TYPE,
-      PROP_DENOMINATOR_TYPE,
-      initModalForm: {
-        workId: "",
-        deviation: 3,
-        cumulativeError: null,
-        autoCallbackShowDeviation: 1,
-        autoCallback: 0,
-        majority: 0,
-        takeBest: 1,
-        levelShowAllPaper: 0,
-        propDenominator: 1,
-        showStandardPaperManage: 1,
-        clearData: 0
-      },
-      modalFormCanEdit: false,
-      modalForm: {},
-      rules: {
-        deviation: numberValidator("请输入仲裁档位差"),
-        cumulativeError: numberValidator("请输入打回累计误差")
-      }
-    };
-  },
-  mounted() {
-    this.modalForm = { ...this.initModalForm };
-    this.getParamsSetInfo();
-  },
-  methods: {
-    async getParamsSetInfo() {
-      const data = await getParamsSet(this.workId);
-      this.modalForm = this.$objAssign(this.modalForm, data);
-    },
-    async submit() {
-      const valid = await this.$refs.modalFormComp.validate();
-      if (!valid) return;
-
-      if (this.isSubmit) return;
-      this.isSubmit = true;
-      let result = true;
-      await updateLevelParams(this.modalForm).catch(() => {
-        result = false;
-      });
-      this.isSubmit = false;
-
-      if (!result) return;
-      this.modalFormCanEdit = false;
-      this.$Message.success("保存成功!");
-    }
-  }
-};
-</script>
+<template>
+  <div class="grading-rule-set part-box">
+    <div class="rule-part">
+      <h3 v-show="modalForm.coarseLevel" class="rule-part-title">细分档设置</h3>
+      <Form
+        ref="modalFormComp"
+        :model="modalForm"
+        :rules="rules"
+        :label-width="230"
+        style="width: 400px;"
+      >
+        <FormItem prop="deviation" label="仲裁档位差:">
+          <InputNumber
+            v-model="modalForm.deviation"
+            :min="1"
+            :max="100"
+            :disabled="!modalFormCanEdit"
+            style="width: 160px;"
+          ></InputNumber>
+        </FormItem>
+        <FormItem label="系统自动打回:">
+          <Select
+            v-model="modalForm.autoCallback"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem
+          v-if="modalForm.autoCallback"
+          prop="cumulativeError"
+          label="打回累计误差:"
+        >
+          <InputNumber
+            v-model="modalForm.cumulativeError"
+            :min="1"
+            :max="100"
+            :disabled="!modalFormCanEdit"
+            style="width: 160px;"
+          ></InputNumber>
+        </FormItem>
+        <FormItem v-if="modalForm.autoCallback" label="打回是否显示偏差方向:">
+          <Select
+            v-model="modalForm.autoCallbackShowDeviation"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否过半定档:">
+          <Select
+            v-model="modalForm.majority"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否启用取优原则:">
+          <Select
+            v-model="modalForm.takeBest"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="阅卷员是否显示所有试卷:">
+          <Select
+            v-model="modalForm.levelShowAllPaper"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="档位百分比分母:">
+          <Select
+            v-model="modalForm.propDenominator"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in PROP_DENOMINATOR_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否显示标准卷管理:">
+          <Select
+            v-model="modalForm.showStandardPaperManage"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否显示清除当前阅卷数据:">
+          <Select
+            v-model="modalForm.clearData"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否增加粗档位环节:">
+          <Select
+            v-model="modalForm.coarseLevel"
+            :disabled="!modalFormCanEdit"
+            placeholder="请选择"
+            @on-change="coarseLevelChange"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+      </Form>
+      <div class="rule-part-footer">
+        <Button
+          shape="circle"
+          style="width: 80px;"
+          @click="modalFormCanEdit = true"
+          >编辑</Button
+        >
+        <Button
+          type="primary"
+          shape="circle"
+          style="width: 80px;"
+          :disabled="isSubmit"
+          @click="submit"
+          >保存</Button
+        >
+      </div>
+    </div>
+    <div v-if="modalForm.coarseLevel" class="rule-part">
+      <h3 class="rule-part-title">粗分档设置</h3>
+      <Form
+        ref="modalFormCoarseComp"
+        :model="modalFormCoarse"
+        :rules="rules"
+        :label-width="230"
+        style="width: 400px;"
+      >
+        <FormItem prop="deviation" label="仲裁档位差:">
+          <InputNumber
+            v-model="modalFormCoarse.deviation"
+            :min="1"
+            :max="100"
+            :disabled="!modalFormCoarseCanEdit"
+            style="width: 160px;"
+          ></InputNumber>
+        </FormItem>
+        <FormItem label="系统自动打回:">
+          <Select
+            v-model="modalFormCoarse.autoCallback"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem
+          v-if="modalFormCoarse.autoCallback"
+          prop="cumulativeError"
+          label="打回累计误差:"
+        >
+          <InputNumber
+            v-model="modalFormCoarse.cumulativeError"
+            :min="1"
+            :max="100"
+            :disabled="!modalFormCoarseCanEdit"
+            style="width: 160px;"
+          ></InputNumber>
+        </FormItem>
+        <FormItem
+          v-if="modalFormCoarse.autoCallback"
+          label="打回是否显示偏差方向:"
+        >
+          <Select
+            v-model="modalFormCoarse.autoCallbackShowDeviation"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否过半定档:">
+          <Select
+            v-model="modalFormCoarse.majority"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否启用取优原则:">
+          <Select
+            v-model="modalFormCoarse.takeBest"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="阅卷员是否显示所有试卷:">
+          <Select
+            v-model="modalFormCoarse.levelShowAllPaper"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="档位百分比分母:">
+          <Select
+            v-model="modalFormCoarse.propDenominator"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in PROP_DENOMINATOR_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否显示标准卷管理:">
+          <Select
+            v-model="modalFormCoarse.showStandardPaperManage"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+        <FormItem label="是否显示清除当前阅卷数据:">
+          <Select
+            v-model="modalFormCoarse.clearData"
+            :disabled="!modalFormCoarseCanEdit"
+            placeholder="请选择"
+          >
+            <Option
+              v-for="(val, key) in BOOLEAN_TYPE"
+              :key="key"
+              :value="key * 1"
+              :label="val"
+            ></Option>
+          </Select>
+        </FormItem>
+      </Form>
+      <div class="rule-part-footer">
+        <Button
+          shape="circle"
+          style="width: 80px;"
+          @click="modalFormCoarseCanEdit = true"
+          >编辑</Button
+        >
+        <Button
+          type="primary"
+          shape="circle"
+          style="width: 80px;"
+          :disabled="isSubmit"
+          @click="submitCoarse"
+          >保存</Button
+        >
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {
+  getParamsSet,
+  updateLevelParams,
+  updateCoarseLevelParams
+} from "@/api";
+import { BOOLEAN_TYPE, PROP_DENOMINATOR_TYPE } from "@/constants/enumerate";
+import { numberValidator } from "@/plugins/formRules";
+
+const initModalForm = {
+  workId: "",
+  deviation: 3,
+  cumulativeError: null,
+  autoCallbackShowDeviation: 1,
+  autoCallback: 0,
+  majority: 0,
+  takeBest: 1,
+  levelShowAllPaper: 0,
+  propDenominator: 1,
+  showStandardPaperManage: 1,
+  clearData: 0
+};
+
+export default {
+  name: "grading-rule-set",
+  data() {
+    return {
+      isSubmit: false,
+      workId: this.$route.params.workId,
+      BOOLEAN_TYPE,
+      PROP_DENOMINATOR_TYPE,
+      modalFormCanEdit: false,
+      modalForm: {},
+      modalFormCoarseCanEdit: false,
+      modalFormCoarse: {},
+      rules: {
+        deviation: numberValidator("请输入仲裁档位差"),
+        cumulativeError: numberValidator("请输入打回累计误差")
+      }
+    };
+  },
+  mounted() {
+    this.modalForm = { ...initModalForm, coarseLevel: 0 };
+    this.getParamsSetInfo();
+  },
+  methods: {
+    async getParamsSetInfo() {
+      const data = await getParamsSet(this.workId);
+      this.modalForm = this.$objAssign(this.modalForm, data);
+    },
+    coarseLevelChange(val) {
+      if (val) {
+        this.modalFormCoarse = { ...initModalForm };
+        this.modalFormCoarseCanEdit = true;
+      }
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate();
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      let result = true;
+      await updateLevelParams(this.modalForm).catch(() => {
+        result = false;
+      });
+      this.isSubmit = false;
+
+      if (!result) return;
+      this.modalFormCanEdit = false;
+      this.$Message.success("保存成功!");
+    },
+    async submitCoarse() {
+      const valid = await this.$refs.modalFormCoarseComp.validate();
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      let result = true;
+      await updateCoarseLevelParams(this.modalFormCoarse).catch(() => {
+        result = false;
+      });
+      this.isSubmit = false;
+
+      if (!result) return;
+      this.modalFormCoarseCanEdit = false;
+      this.$Message.success("保存成功!");
+    }
+  }
+};
+</script>

+ 174 - 170
src/modules/main/WorkManage.vue

@@ -1,170 +1,174 @@
-<template>
-  <div class="home">
-    <view-header class="home-header" :show-reset-pwd="false"></view-header>
-
-    <div class="home-body">
-      <div class="home-main">
-        <div class="work-manage">
-          <div class="part-box-head">
-            <Form label-position="left" inline>
-              <FormItem>
-                <Input
-                  v-model.trim="modalForm.name"
-                  placeholder="工作名称"
-                  clearable
-                ></Input>
-              </FormItem>
-              <FormItem :label-width="0">
-                <Button
-                  size="small"
-                  type="success"
-                  icon="recode-white icon"
-                  shape="circle"
-                  @click="toAdd"
-                  >新增工作文件</Button
-                >
-              </FormItem>
-            </Form>
-          </div>
-          <Table
-            ref="TableList"
-            :columns="columns"
-            :data="works"
-            :row-class-name="rowClassName"
-            disabled-hover
-            border
-          ></Table>
-        </div>
-
-        <!-- <view-footer></view-footer> -->
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import { workList, createWork, deleteWork } from "@/api";
-
-export default {
-  name: "work-manage",
-  data() {
-    return {
-      modalForm: {
-        name: ""
-      },
-      works: [],
-      columns: [
-        {
-          title: "序号",
-          width: 80,
-          render: (h, param) => {
-            return h("div", param.index + 1 + "");
-          }
-        },
-        {
-          title: "ID",
-          key: "id",
-          minWidth: 100
-        },
-        {
-          title: "名称",
-          key: "name",
-          minWidth: 200
-        },
-        {
-          title: "创建时间",
-          key: "createdOn",
-          minWidth: 160
-        },
-        {
-          title: "操作",
-          key: "action",
-          width: 140,
-          align: "center",
-          className: "table-action",
-          render: (h, param) => {
-            const actions = [
-              {
-                icon: "md-trash",
-                classes: ["icon-danger"],
-                attrs: {
-                  title: "删除"
-                },
-                action: () => {
-                  this.toDelete(param.row);
-                }
-              },
-              {
-                icon: "md-arrow-round-forward",
-                attrs: {
-                  title: "进入"
-                },
-                action: () => {
-                  this.toDetail(param.row);
-                }
-              }
-            ];
-
-            return h("div", this.$tableIconAction(h, actions));
-          }
-        }
-      ]
-    };
-  },
-  mounted() {
-    this.getList();
-  },
-  methods: {
-    async getList() {
-      const data = await workList();
-      this.works = data.map(item => {
-        return {
-          id: item.id,
-          name: item.name,
-          active: item.active,
-          createdOn: item.createdOn
-        };
-      });
-    },
-    rowClassName(row) {
-      return row.active ? "row-active" : "";
-    },
-    async toAdd() {
-      if (!this.modalForm.name) {
-        this.$Message.error("请输入工作名称!");
-        return;
-      }
-      if (this.modalForm.name.length > 100) {
-        this.$Message.error("工作名称长度不能超过100个字符!");
-        return;
-      }
-
-      await createWork(this.modalForm);
-      this.$Message.success("创建工作成功!");
-      this.getList();
-    },
-    toDetail(row) {
-      this.$router.push({
-        name: "WorkOverview",
-        params: {
-          workId: row.id
-        }
-      });
-    },
-    toDelete(row) {
-      this.$Modal.confirm({
-        title: "删除警告",
-        content: "确定要删除当前工作吗?",
-        onOk: () => {
-          this.toDel(row.id);
-        }
-      });
-    },
-    async toDel(id) {
-      await deleteWork(id);
-      this.$Message.success("删除成功!");
-      this.getList();
-    }
-  }
-};
-</script>
+<template>
+  <div class="home">
+    <view-header class="home-header" :show-reset-pwd="false"></view-header>
+
+    <div class="home-body">
+      <div class="home-main">
+        <div class="work-manage">
+          <div class="part-box-head">
+            <Form label-position="left" inline>
+              <FormItem>
+                <Input
+                  v-model.trim="modalForm.name"
+                  placeholder="工作名称"
+                  clearable
+                ></Input>
+              </FormItem>
+              <FormItem :label-width="0">
+                <Button
+                  size="small"
+                  type="success"
+                  icon="recode-white icon"
+                  shape="circle"
+                  @click="toAdd"
+                  >新增工作文件</Button
+                >
+              </FormItem>
+            </Form>
+          </div>
+          <Table
+            ref="TableList"
+            :columns="columns"
+            :data="works"
+            :row-class-name="rowClassName"
+            disabled-hover
+            border
+          ></Table>
+        </div>
+
+        <!-- <view-footer></view-footer> -->
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { workList, createWork, deleteWork } from "@/api";
+
+export default {
+  name: "work-manage",
+  data() {
+    return {
+      modalForm: {
+        name: ""
+      },
+      works: [],
+      columns: [
+        {
+          title: "序号",
+          width: 80,
+          render: (h, param) => {
+            return h("div", param.index + 1 + "");
+          }
+        },
+        {
+          title: "ID",
+          key: "id",
+          minWidth: 100
+        },
+        {
+          title: "名称",
+          key: "name",
+          minWidth: 200
+        },
+        {
+          title: "创建时间",
+          key: "createdOn",
+          minWidth: 160
+        },
+        {
+          title: "操作",
+          key: "action",
+          width: 140,
+          align: "center",
+          className: "table-action",
+          render: (h, param) => {
+            const actions = [
+              {
+                icon: "md-trash",
+                classes: ["icon-danger"],
+                attrs: {
+                  title: "删除"
+                },
+                action: () => {
+                  this.toDelete(param.row);
+                }
+              },
+              {
+                icon: "md-arrow-round-forward",
+                attrs: {
+                  title: "进入"
+                },
+                action: () => {
+                  this.toDetail(param.row);
+                }
+              }
+            ];
+
+            return h("div", this.$tableIconAction(h, actions));
+          }
+        }
+      ]
+    };
+  },
+  mounted() {
+    this.getList();
+  },
+  methods: {
+    async getList() {
+      const data = await workList();
+      this.works = data.map(item => {
+        return {
+          id: item.id,
+          name: item.name,
+          active: item.active,
+          createdOn: item.createdOn
+        };
+      });
+    },
+    rowClassName(row) {
+      return row.active ? "row-active" : "";
+    },
+    async toAdd() {
+      if (!this.modalForm.name) {
+        this.$Message.error("请输入工作名称!");
+        return;
+      }
+      if (this.modalForm.name.length > 100) {
+        this.$Message.error("工作名称长度不能超过100个字符!");
+        return;
+      }
+
+      await createWork(this.modalForm);
+      this.$Message.success("创建工作成功!");
+      this.getList();
+    },
+    toDetail(row) {
+      const user = this.$ls.get("user", {});
+      user.workId = row.id;
+      this.$ls.set("user", user);
+
+      this.$router.push({
+        name: "WorkOverview",
+        params: {
+          workId: row.id
+        }
+      });
+    },
+    toDelete(row) {
+      this.$Modal.confirm({
+        title: "删除警告",
+        content: "确定要删除当前工作吗?",
+        onOk: () => {
+          this.toDel(row.id);
+        }
+      });
+    },
+    async toDel(id) {
+      await deleteWork(id);
+      this.$Message.success("删除成功!");
+      this.getList();
+    }
+  }
+};
+</script>