|
@@ -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>
|