zhangjie преди 5 години
родител
ревизия
1e8747d625

+ 44 - 15
src/api.js

@@ -213,11 +213,29 @@ export const finishTryGradingTask = datas => {
 export const checkMissionStatus = ({ workId, subject }) => {
   return $get("/api/trial/checkMissionStatus", { workId, subject });
 };
-export const createGradingTask = ({ subjectId, taskCount, questionId }) => {
-  return $patch(`/api/marksubjects/${subjectId}`, { taskCount, questionId });
+// 分档任务和打分任务发布时都使用这个接口
+export const createGradingTask = ({
+  subjectId,
+  taskCount,
+  questionId,
+  taskList
+}) => {
+  return $patch(
+    `/api/marksubjects/${subjectId}`,
+    {
+      taskCount,
+      questionId,
+      taskList
+    },
+    "json"
+  );
 };
 
-// grading-detail
+// grading-detail ------------------------->
+// grading-analysis
+export const gradingStatData = datas => {
+  return $get("/api/marktasks/levelStatDetail", datas);
+};
 // level relate
 export const workLevelList = workId => {
   return $get(`/api/admin/works/${workId}/levels`);
@@ -242,8 +260,9 @@ export const markerPaperList = datas => {
   return $get("/api/marktasks", datas);
 };
 // grading or scoring
-export const paperSelectLevel = (paperId, level, stage) => {
-  return $patch(`/api/marktasks/${paperId}`, { stage, result: level }, "json");
+export const paperSelectLevelOrScore = (paperId, result, stage) => {
+  // stage => LEVEL or SCORE
+  return $patch(`/api/marktasks/${paperId}`, { stage, result }, "json");
 };
 export const paperPassLevel = paperId => {
   return $post(`/api/marktasks/${paperId}/skip`, {});
@@ -277,22 +296,32 @@ export const taskSnSearch = (type, code, questionId) => {
     return $get(`/api/papers/one`, { [paramName]: code, questionId });
   }
 };
+// mark step change level
+export const markStepChangeLevel = ({ subjectId, paperId, level }) => {
+  // paperId,level
+  return $post(`/api/changelevel/${subjectId}/changeLevel`, { paperId, level });
+};
+export const changeLevelPaperList = datas => {
+  // 非纪检人员和超级管理员查询改档列表
+  // status => 0:申请的,1:审核通过的,2:审核未通过
+  return $get("/api/changelevel/list", datas);
+};
+export const changeLevelPaperAllList = datas => {
+  // 纪检人员和超级管理员查询改档列表
+  // status => 0:未审核,1:已审核
+  return $get("/api/changelevel/list", datas);
+};
 
 // mark -------------------------->
 // mark-progress
-export const markProgressDetail = subjectId => {
-  // TODO:
-  return $get(`/api/marksubjects/${subjectId}/markergroups`, {});
-};
+// to see grading progress
 
 // mark-task-manage
-export const markTaskInfo = subjectId => {
-  // TODO:
-  return $get(`/api/marksubjects/${subjectId}/markergroups`, {});
+export const checkMarkTaskCanSubmit = (subjectId, questionId) => {
+  return $get(`/api/marksubjects/${subjectId}/canScore`, { questionId });
 };
-export const createMarkTask = (subjectId, datas) => {
-  // TODO:
-  return $post(`/api/marksubjects/${subjectId}/markergroups`, datas);
+export const markTaskInfo = (subjectId, questionId) => {
+  return $get(`/api/marksubjects/${subjectId}/scoreProgress`, { questionId });
 };
 
 // inspection -------------------------->

+ 20 - 7
src/components/EchartRender.vue

@@ -39,20 +39,30 @@ export default {
   methods: {
     getOptions() {
       const name = this.chartType[0].toUpperCase() + this.chartType.slice(1);
-      this.chartOption = this[`get${name}Opiton`](this.chartData);
+      this.chartOption = this[`get${name}Option`](this.chartData);
     },
     getLineOption(datas) {
-      if (!datas.chartLabels.length) return;
+      let labels = [],
+        vals = [];
+      datas.map(item => {
+        labels.push(item.name);
+        vals.push(item.value);
+      });
 
       return {
         grid: {
           top: "15%",
-          bottom: "18%"
+          bottom: "15%"
+        },
+        tooltip: {
+          show: true,
+          formatter: function(params) {
+            return params.value + "%";
+          }
         },
-        tooltip: { show: true },
         xAxis: {
           type: "category",
-          data: datas.chartLabels,
+          data: labels,
           axisLabel: {
             fontSize: 14,
             fontWeight: "bold"
@@ -75,7 +85,10 @@ export default {
             }
           },
           axisLabel: {
-            fontSize: 14
+            fontSize: 14,
+            formatter: function(value, index) {
+              return value + "%";
+            }
           },
           axisTick: {
             show: false
@@ -86,7 +99,7 @@ export default {
             name: "数量",
             type: "line",
             smooth: true,
-            data: datas.chartData[0],
+            data: vals,
             areaStyle: {
               color: "#b23f3a",
               opacity: 0.3

+ 6 - 0
src/constants/enumerate.js

@@ -109,3 +109,9 @@ export const SCORE_HANDLE_TYPE = {
   0: "四舍五入",
   1: "非零进一"
 };
+
+// apply-change-level-status
+export const CHANGE_LEVEL_STATUS = {
+  0: "未处理",
+  1: "已处理"
+};

+ 32 - 14
src/modules/grading/GradingDetail.vue

@@ -10,7 +10,11 @@
       <div class="detail-filter">
         <Form ref="FilterForm" label-position="left" inline>
           <FormItem>
-            <Select v-model="filter.questionId" placeholder="请选择考区">
+            <Select
+              v-model="filter.questionId"
+              @on-change="areaChange"
+              placeholder="请选择考区"
+            >
               <Option
                 v-for="area in areas"
                 :key="area.id"
@@ -87,7 +91,12 @@
       </div>
     </div>
     <div class="detail-body" v-else>
-      <grade-analysis ref="GradeAnalysis"></grade-analysis>
+      <grade-analysis
+        :question-id="filter.questionId"
+        :subject-id="subjectId"
+        ref="GradeAnalysis"
+        v-if="filter.questionId && subjectId"
+      ></grade-analysis>
     </div>
 
     <!-- image-preview -->
@@ -129,7 +138,6 @@ import {
   levelStatData,
   areaList,
   workLevelList,
-  paperSelectLevel,
   taskSnSearch
 } from "@/api";
 import ImagePreview from "@/components/common/ImagePreview";
@@ -185,7 +193,6 @@ export default {
       total: 0,
       totalPage: 1,
       curStep: { type: "analysis", name: "analysis" },
-      curStandardGradeId: "",
       steps: [],
       levels: [],
       areas: [],
@@ -221,10 +228,13 @@ export default {
   },
   methods: {
     async initData() {
+      // 获取档位列表
+      this.getWorkLevels();
+      // 获取考区列表,一个考区对应一个questionId
       await this.getAreaList();
       this.filter.questionId = this.areas[0].id;
+      // 获取顶部档位导航
       this.getStepLevels();
-      this.getWorkLevels();
     },
     async getList() {
       const datas = {
@@ -310,6 +320,14 @@ export default {
         this.curPaper = {};
       }
     },
+    areaChange() {
+      if (this.curStep.type === "analysis") {
+        this.$refs.GradeAnalysis.initData();
+      } else {
+        this.getStepLevels();
+        this.toPage(1);
+      }
+    },
     selectPaper(index) {
       this.curPaperIndex = index;
       this.curPaper = { ...this.papers[index] };
@@ -333,21 +351,17 @@ export default {
         this.$Message.warning("当前已经是最后一条数据了");
         return;
       }
-      this.current++;
+      // 下一页时,继续获取当前页数据。
       await this.getList();
       this.selectPaper(0);
       this.$refs.ImagePreview.initData();
     },
-    async gradeCurPaper(level) {
-      if (!this.IS_MARK_LEADER) return;
-
-      await paperSelectLevel(this.curPaper.id, level.name, "LEVEL");
-      this.getStepLevels();
-      this.toNext();
-    },
     toNext() {
       this.$refs.ImagePreview.showNext();
     },
+    updateHistory() {
+      this.$refs.GradeHistoryPaper.updatePapers();
+    },
     async serachPaperByCode(params) {
       const data = await taskSnSearch(
         params.codeType,
@@ -367,8 +381,12 @@ export default {
       this.markers = markers;
       this.$refs.ModifyLeaderGrading.open();
     },
-    leaderGradingSuccess() {
+    leaderGradingSuccess(datas) {
+      if (datas.action === "sampling") {
+        // todo:设定标准卷之后
+      }
       this.getStepLevels();
+      this.updateHistory();
       this.toNext();
     },
     // paper carousel

+ 28 - 12
src/modules/grading/GradingOperation.vue

@@ -19,7 +19,11 @@
           <div class="detail-filter">
             <Form ref="FilterForm" label-position="left" inline>
               <FormItem>
-                <Select v-model="filter.questionId" placeholder="请选择考区">
+                <Select
+                  v-model="filter.questionId"
+                  @on-change="areaChange"
+                  placeholder="请选择考区"
+                >
                   <Option
                     v-for="area in areas"
                     :key="area.id"
@@ -134,7 +138,7 @@ import {
   areaList,
   workLevelList,
   subjectDetail,
-  paperSelectLevel,
+  paperSelectLevelOrScore,
   paperPassLevel
 } from "@/api";
 import ImagePreview from "@/components/common/ImagePreview";
@@ -186,7 +190,7 @@ export default {
       size: 6,
       total: 0,
       totalPage: 1,
-      curStep: { type: "undo", name: "待评" },
+      curStep: null,
       curStandardGradeId: "",
       steps: [],
       levels: [],
@@ -214,10 +218,12 @@ export default {
   methods: {
     async initData() {
       this.getSubjectDetail();
+      this.getWorkLevels();
+
       await this.getAreaList();
       this.filter.questionId = this.areas[0].id;
-      this.getStepLevels();
-      this.getWorkLevels();
+      await this.getStepLevels();
+      this.getList();
     },
     async getSubjectDetail() {
       this.curSubject = await subjectDetail(this.subjectId);
@@ -276,13 +282,15 @@ export default {
         };
       });
       this.steps = [...levelMenu, ...otherStep];
-      let curStep = {};
-      if (undoIndex === -1) {
-        curStep = this.steps[0];
-      } else {
-        curStep = this.steps[this.steps.length - 2];
+      if (!this.curStep) {
+        let curStep = {};
+        if (undoIndex === -1) {
+          curStep = this.steps[0];
+        } else {
+          curStep = this.steps[this.steps.length - 2];
+        }
+        this.curStep = curStep;
       }
-      this.curStep = curStep;
     },
     async getWorkLevels() {
       const data = await workLevelList(this.workId);
@@ -323,6 +331,10 @@ export default {
         this.curPaper = {};
       }
     },
+    areaChange() {
+      this.getStepLevels();
+      this.toPage(1);
+    },
     selectPaper(index) {
       this.curPaperIndex = index;
       this.curPaper = { ...this.papers[index] };
@@ -352,8 +364,9 @@ export default {
       this.$refs.ImagePreview.initData();
     },
     async gradeCurPaper(level) {
-      await paperSelectLevel(this.curPaper.id, level.name, "LEVEL");
+      await paperSelectLevelOrScore(this.curPaper.id, level.name, "LEVEL");
       this.getStepLevels();
+      this.updateHistory();
       this.toNext();
     },
     async passCurPaper(level) {
@@ -363,6 +376,9 @@ export default {
     toNext() {
       this.$refs.ImagePreview.showNext();
     },
+    updateHistory() {
+      this.$refs.GradeHistoryPaper.updatePapers();
+    },
     // paper carousel
     toViewCarouselPaper(paperIndex, papers) {
       this.curCarouselPaperIndex = paperIndex;

+ 31 - 2
src/modules/grading/GradingProgress.vue

@@ -2,7 +2,7 @@
   <div class="grading-progress">
     <div class="progress-title">
       <span>当前阶段:{{ stepName }}</span
-      ><span>进度:{{ stepProgress }}</span>
+      ><span v-if="!IS_SCORE">进度:{{ stepProgress }}</span>
     </div>
     <div class="part-box-head" v-if="IS_ADMIN">
       <div class="part-box-head-left">
@@ -42,7 +42,13 @@
         </table>
       </div>
       <div class="progress-table">
-        <table class="table table-noborder">
+        <table class="table table-noborder" v-if="IS_LEVEL">
+          <tr>
+            <td style="width: 12%">{{ kzzInfo.name }}</td>
+            <td colspan="3" style="text-align:left">
+              {{ kzzInfo.arbitrated }}
+            </td>
+          </tr>
           <tr v-for="(item, aindex) in markerProgress" :key="aindex">
             <td style="width: 12%">{{ item.markerName }}</td>
             <td style="width: 60%;">
@@ -55,6 +61,21 @@
             <td style="width: 14%;">打回:{{ item.rejectedCount }}</td>
           </tr>
         </table>
+
+        <table class="table table-noborder" v-if="IS_SCORE">
+          <tr v-for="(item, aindex) in markerProgress" :key="aindex">
+            <td>{{ item.markerName }}</td>
+            <td style="width: 60%;">
+              <progress-line
+                :sum="item.totalCount"
+                :current="item.successCount"
+              ></progress-line>
+            </td>
+            <td>进度:{{ item.progress }}%</td>
+            <td>改档:{{ item.shiftCount }}</td>
+            <td>改档打分:{{ item.shiftScoreCount }}</td>
+          </tr>
+        </table>
       </div>
     </div>
 
@@ -95,6 +116,7 @@ export default {
       stepProgress: "",
       curSubject: {},
       SUBJECT_STAGE,
+      kzzInfo: {},
       totalProgress: {},
       areaProgress: [],
       markerProgress: [],
@@ -132,6 +154,12 @@ export default {
     },
     IS_MARK_LEADER() {
       return this.curUserRoleType === "MARK_LEADER";
+    },
+    IS_LEVEL() {
+      return this.curSubject.stage === "LEVEL";
+    },
+    IS_SCORE() {
+      return this.curSubject.stage === "SCORE";
     }
   },
   mounted() {
@@ -148,6 +176,7 @@ export default {
         workId: this.curSubject.workId,
         subject: this.curSubject.subject
       });
+      this.kzzInfo = data.kzz || {};
       this.totalProgress = data.totalProgress;
       this.stepProgress = data.totalProgress.progress.toFixed(2) + "%";
       const totalInfo = {

+ 25 - 3
src/modules/grading/components/GradeAction.vue

@@ -82,7 +82,7 @@ import { CODE_TYPE } from "@/constants/enumerate";
 // 科组长:查询,头部信息,选择档位,评卷记录
 // 评卷员:头部信息,选择档位
 /*
-[curpaper template]
+[paper template]
 {
   "id": 165,
   "sn": "029947536",
@@ -111,6 +111,28 @@ import { CODE_TYPE } from "@/constants/enumerate";
   "missing": false,
   "manual": false
 }
+[marktask template]
+{
+  "id": 511,
+  "sn": "4929446110",
+  "redoLevel": null,
+  "level": "A",
+  "score": null,
+  "result": "100",
+  "originLevel": null,
+  "markerId": 49,
+  "marker": "pj061001",
+  "updatedOn": 1594775592000,
+  "imgSrc": "",
+  "thumbSrc": "",
+  "markByLeader": true,
+  "oldRejected": false,
+  "paperId": 168,
+  "randomSeqNew": 9446110,
+  "randomSeq": null,
+  "rejected": false,
+  "sample": true
+}
 */
 
 const initRights = {
@@ -236,7 +258,7 @@ export default {
         return {
           id: item.markerId,
           name: item.marker,
-          value: item.result
+          value: item.result || "未评"
         };
       });
     },
@@ -247,7 +269,7 @@ export default {
           {
             curPaperId: this.curPaper.id,
             curLevel: this.curPaper.level,
-            selectLevel: level.name
+            selectedLevel: level.name
           },
           this.gradingHistory
         );

+ 51 - 50
src/modules/grading/components/GradeAnalysis.vue

@@ -17,64 +17,91 @@
       ></Table>
     </div>
     <div class="part-box">
-      <v-chart :options="barOption" v-if="barOption" autoresize></v-chart>
+      <echart-render
+        :chart-data="lineChartData"
+        chart-type="line"
+        chart-title="档位分布图"
+        v-if="lineChartData"
+      ></echart-render>
     </div>
   </div>
 </template>
 
 <script>
-import { levels } from "@/constants/apiTempData";
-import chartOption from "@/constants/chartOptions";
+import { gradingStatData } from "@/api";
+import EchartRender from "@/components/EchartRender";
 
 export default {
   name: "grade-analysis",
+  components: { EchartRender },
+  props: {
+    questionId: {
+      type: Number,
+      required: true
+    },
+    subjectId: {
+      type: String,
+      required: true
+    }
+  },
   data() {
     return {
-      barOption: {},
       levelData: [],
+      lineChartData: null,
       columns: [
         {
           title: "科目",
-          key: "subject"
+          key: "subjectName"
         },
         {
           title: "档位",
-          key: "name"
+          key: "code"
         },
         {
           title: "范围",
-          key: "range",
           render: (h, param) => {
-            return h("div", `${param.row.range[0]}~${param.row.range[1]}`);
+            return h("div", `${param.row.minScore}~${param.row.maxScore}`);
           }
         },
         {
           title: "数量",
-          key: "count"
+          key: "levelCount"
         },
         {
           title: "占比",
-          key: "percent"
+          key: "levelProp",
+          render: (h, param) => {
+            return h("div", `${param.row.levelProp}%`);
+          }
         },
         {
           title: "预设占比",
-          key: "pt"
+          key: "examLevelProp",
+          render: (h, param) => {
+            return h("div", `${param.row.examLevelProp}%`);
+          }
         },
         {
           title: "差值",
-          key: "ptDis"
+          key: "diffProp",
+          render: (h, param) => {
+            return h("div", `${param.row.diffProp}%`);
+          }
         },
         {
           title: "累计数量",
-          key: "gcount"
+          key: "cumulateCount"
         },
         {
           title: "累计占比",
-          key: "percent"
+          key: "cumulateProp",
+          render: (h, param) => {
+            return h("div", `${param.row.cumulateProp}%`);
+          }
         },
         {
           title: "调整",
-          key: "justify"
+          key: "adjustmentCount"
         }
       ]
     };
@@ -83,44 +110,18 @@ export default {
     this.initData();
   },
   methods: {
-    parseGroupBarData(datas) {
-      if (!datas.length) {
-        return { names: [], dataList: [] };
-      }
-      var names = datas[0].data.map(function(item) {
-        return item.markerName;
-      });
-      var dataList = datas.map(function(item) {
-        return {
-          name: item.name,
-          data: item.data.map(function(elem) {
-            return elem.prop;
-          })
-        };
-      });
-      return {
-        names: names,
-        dataList: dataList
-      };
-    },
-    initData() {
-      let chartLabels = [],
-        chartData = [];
-      levels.map(item => {
-        chartLabels.push(item.name);
-        chartData.push(item.gpercent);
+    async initData() {
+      const subs = this.subjectId.split("-");
+      this.levelData = await gradingStatData({
+        questionId: this.questionId,
+        workId: subs[0],
+        subject: subs[1]
       });
-      this.barOption = chartOption.getLineOption(
-        { chartData, chartLabels },
-        "档位分布图"
-      );
 
-      this.levelData = levels.map(item => {
+      this.lineChartData = this.levelData.map(item => {
         return {
-          ...item,
-          subject: "速写",
-          ptDis: 2,
-          justify: 200
+          name: item.code,
+          value: item.levelProp
         };
       });
     },

+ 4 - 1
src/modules/grading/components/GradeHistoryPaper.vue

@@ -22,12 +22,15 @@ export default {
   props: {
     questionId: {
       type: Number
+    },
+    stage: {
+      type: String,
+      default: "LEVEL"
     }
   },
   data() {
     return {
       userId: "",
-      stage: "LEVEL",
       curPaper: {},
       papers: []
     };

+ 5 - 5
src/modules/grading/components/ModifyLeaderGrading.vue

@@ -8,7 +8,7 @@
   >
     <div class="leader-grading">
       <div class="leader-level">
-        <h3>{{ LevelInfo.selectLevel }}</h3>
+        <h3>{{ LevelInfo.selectedLevel }}</h3>
       </div>
       <RadioGroup v-model="actionType">
         <div class="leader-aciton" v-if="standardVolume">
@@ -56,7 +56,7 @@ export default {
         return {
           curPaperId: "",
           curLevel: "",
-          selectLevel: ""
+          selectedLevel: ""
         };
       }
     },
@@ -82,7 +82,7 @@ export default {
   methods: {
     initData(val) {
       this.noneedLevelOrReject =
-        this.LevelInfo.curLevel === this.LevelInfo.selectLevel;
+        this.LevelInfo.curLevel === this.LevelInfo.selectedLevel;
       const user = this.$ls.get("user");
       this.standardVolume = user.standardVolume;
       this.levelCallback = user.levelCallback;
@@ -110,7 +110,7 @@ export default {
       }
       const datas = {
         action: this.actionType,
-        level: this.LevelInfo.selectLevel
+        level: this.LevelInfo.selectedLevel
       };
       if (this.actionType === "reject")
         datas.range = this.selectedMarkers.join();
@@ -126,7 +126,7 @@ export default {
       if (!result) return;
 
       this.$Message.success("操作成功!");
-      this.$emit("modified");
+      this.$emit("modified", datas);
       this.cancel();
     }
   }

+ 238 - 95
src/modules/mark/MarkDetail.vue

@@ -1,12 +1,27 @@
 <template>
   <div class="mark-detail grading-detail">
-    <grade-step :show-analysis="false" @on-change="stepChange"></grade-step>
-    <div :class="bodyClasses">
+    <grade-step
+      :init-step="curStep"
+      :show-analysis="false"
+      @on-change="stepChange"
+      ref="GradeStep"
+      v-if="steps.length"
+    ></grade-step>
+    <div class="detail-body">
       <div class="detail-filter">
         <Form ref="FilterForm" label-position="left" inline>
           <FormItem>
-            <Select v-model="filter.areaCode" placeholder="请选择考区">
-              <Option value=""></Option>
+            <Select
+              v-model="filter.questionId"
+              @on-change="areaChange"
+              placeholder="请选择考区"
+            >
+              <Option
+                v-for="area in areas"
+                :key="area.id"
+                :value="area.id"
+                :label="area.areaName"
+              ></Option>
             </Select>
           </FormItem>
           <FormItem>
@@ -18,18 +33,22 @@
       </div>
       <!-- detail-papers -->
       <div :class="detailPapersClasses">
-        <div
-          class="detail-papers-carousel"
-          v-if="this.curUserRoleType !== 'ADMIN'"
-        >
+        <div class="detail-papers-carousel" v-if="!IS_ADMIN">
           <grade-history-paper
             :question-id="filter.questionId"
+            stage="SCORE"
+            @on-paper-click="toViewCarouselPaper"
+            v-if="filter.questionId"
+            ref="GradeHistoryPaper"
           ></grade-history-paper>
         </div>
         <div class="detail-papers-list">
           <div :class="imageViewClasses">
             <div
-              class="image-view"
+              :class="[
+                'image-view',
+                { 'image-view-act': curPaperIndex === index }
+              ]"
               v-for="(image, index) in papers"
               :key="index"
             >
@@ -43,16 +62,13 @@
               </div>
             </div>
           </div>
-          <div class="part-page">
+          <div class="part-page" v-if="total > size">
             <Page
               :current="current"
               :total="total"
               :page-size="size"
               show-total
               show-elevator
-              :show-sizer="curUserRoleType === 'MARKER'"
-              :page-size-opts="[4, 6, 8]"
-              @on-page-size-change="pageSizeChange"
               @on-change="toPage"
             ></Page>
           </div>
@@ -61,12 +77,13 @@
       <!-- detail-aciton -->
       <div class="detail-action">
         <mark-action
-          :paper="curPaper"
+          :cur-paper="curPaper"
           :levels="levels"
-          :step="curStep"
           :user-role="curUserRoleType"
-          @on-confirm="gradeCurPaper"
-          ref="GradeAction"
+          @on-leader-level="leaderSelectLevel"
+          @on-code-search="serachPaperByCode"
+          @on-grade-change-search="searchGradeChangeList"
+          v-if="curPaper.id"
         ></mark-action>
       </div>
     </div>
@@ -76,19 +93,38 @@
       class="grading-detail-image-preview"
       :image-list="papers"
       :init-index="curPaperIndex"
-      @on-prev="paperPrev"
-      @on-next="paperNext"
+      @on-paper-change="selectPaper"
       @on-page-prev="prevPage"
       @on-page-next="nextPage"
       header-hide
       ref="ImagePreview"
       v-if="papers.length"
     ></image-preview>
+    <!-- carousel paper review -->
+    <image-preview
+      class="grading-detail-image-preview"
+      :image-list="carouselPapers"
+      :init-index="curCarouselPaperIndex"
+      @on-paper-change="selectCarouselPaper"
+      loop
+      header-hide
+      ref="CarouselPapersPreview"
+      v-if="carouselPapers.length"
+    ></image-preview>
   </div>
 </template>
 
 <script>
-import { paperPageList } from "@/api";
+import {
+  paperList,
+  changeLevelPaperList,
+  changeLevelPaperAllList,
+  levelStatData,
+  areaList,
+  workLevelList,
+  taskSnSearch,
+  markStepChangeLevel
+} from "@/api";
 import ImagePreview from "@/components/common/ImagePreview";
 import GradeStep from "../grading/components/GradeStep";
 import GradeHistoryPaper from "../grading/components/GradeHistoryPaper";
@@ -97,6 +133,8 @@ import MarkAction from "./components/MarkAction";
 // 管理员(ADMIN),科组长(MARK_LEADER),评卷员(MARKER)
 // 管理员:试卷列表,操作盘
 // 科组长:操作记录,试卷列表,操作盘(改档)
+
+// TIP:不考虑评卷员的情况
 // 评卷员:操作记录,试卷列表,操作盘(打分)
 
 export default {
@@ -110,21 +148,30 @@ export default {
   data() {
     return {
       filter: {
-        workId: this.$route.params.workId,
-        subjectId: this.$route.params.subjectId,
-        areaCode: ""
+        questionId: "",
+        sort: "score,desc",
+        level: "",
+        isSample: false
       },
-      curUserRoleType: "ADMIN",
+      workId: this.$route.params.workId,
+      subjectId: this.$route.params.subjectId,
+      subject: "",
+      curUserRoleType: "",
+      applyChangeLevelStatus: 1, // 改档申请处理状态
       current: 1,
-      size: 8,
+      size: 6,
       total: 0,
       totalPage: 1,
-      curStep: {},
-      curStandardGradeId: "",
+      curStep: null,
+      steps: [],
       levels: [],
+      areas: [],
       papers: [],
       curPaper: {},
-      curPaperIndex: 0
+      curPaperIndex: 0,
+      // carousel paper review,
+      carouselPapers: [],
+      curCarouselPaperIndex: 0
     };
   },
   computed: {
@@ -144,108 +191,204 @@ export default {
       ];
     },
     imageViewClasses() {
-      return [
-        "detail-paper-list",
-        "image-view-list",
-        `image-view-list-${this.size / 2}`
-      ];
+      return ["image-view-list", `image-view-list-${this.size / 2}`];
+    },
+    IS_ADMIN() {
+      return this.curUserRoleType === "ADMIN";
+    },
+    IS_MARK_LEADER() {
+      return this.curUserRoleType === "MARK_LEADER";
     }
   },
   mounted() {
-    // this.curUserRoleType = this.$ls.get("user", { role: "" }).role;
-    // if (this.curUserRoleType === "ADMIN") this.size = 8;
+    this.subject = this.subjectId.split("-")[1];
+    this.curUserRoleType = this.$ls.get("user", { role: "" }).role;
+    if (this.curUserRoleType === "ADMIN") this.size = 8;
     this.initData();
   },
   methods: {
-    initData() {
-      this.papers = "#"
-        .repeat(this.size)
-        .split("")
-        .map((item, index) => {
-          return {
-            id: index,
-            subjectName: "素描",
-            title: "2020105133",
-            score: "95",
-            grade: "A",
-            url:
-              "http://127.0.0.1:9000/api/file/image/download/33/1/833/1?random=fa8244bb-8ec4-46c1-a16e-1bd6f3b8848e",
-            thumbSrc:
-              "http://127.0.0.1:9000/api/file/image/download/33/1/833/2?random=497cc903-c01a-458a-9b4e-82b391cef176"
-          };
-        });
+    async initData() {
+      this.getWorkLevels();
+
+      await this.getAreaList();
+      this.filter.questionId = this.areas[0].id;
+      await this.getStepLevels();
+      this.getList();
     },
     async getList() {
-      const datas = {
-        ...this.filter,
-        page: this.current - 1,
-        size: this.size
-      };
-      const data = await paperPageList(datas);
-      this.papers = data.data.map(paper => {
-        return {
-          id: paper.id,
-          title: paper.examNumber,
-          url: paper.imgSrc,
-          thumbSrc: paper.thumbSrc,
-          missing: paper.missing,
-          stage: paper.stage,
-          style: {},
-          deg: 0
+      let data = [];
+      if (this.step.type === "done") {
+        const datas = {
+          ...this.filter,
+          level: this.curStep.name,
+          page: this.current - 1,
+          size: this.size
         };
+
+        data = await paperList(datas);
+      } else {
+        const datas = {
+          markerId: this.$ls.get("user", { id: "" }).id,
+          questionId: this.filter.questionId,
+          status: this.applyChangeLevelStatus,
+          page: this.current - 1,
+          size: this.size
+        };
+        const requestAction = this.IS_ADMIN
+          ? changeLevelPaperAllList
+          : changeLevelPaperList;
+        data = await requestAction(datas);
+      }
+      this.papers = data.data.map(paper => {
+        paper.title = paper.examNumber;
+        return paper;
       });
       this.total = data.totalCount;
     },
     toPage(page) {
       this.current = page;
-      // this.getList();
-      this.initData();
+      this.getList();
     },
-    pageSizeChange(size) {
-      this.size = size;
-      this.toPage(1);
+    async getStepLevels() {
+      const data = await levelStatData(this.subjectId, this.filter.questionId);
+      const undoIndex = data.findIndex(item => item.id === null);
+      let otherStep = [];
+      if (undoIndex !== -1) {
+        const undo = { ...data[undoIndex] };
+        data.splice(undoIndex, 1);
+        otherStep.push({
+          name: "改档",
+          count: undo.shift,
+          type: "shift"
+        });
+      }
+      let levelMenu = data.map(item => {
+        return {
+          ...item,
+          name: item.id,
+          type: "done"
+        };
+      });
+      this.steps = [...levelMenu, ...otherStep];
+      if (!this.curStep) this.curStep = this.steps[0];
+    },
+    async getWorkLevels() {
+      const data = await workLevelList(this.workId);
+      this.levels = data.map(item => {
+        return {
+          id: item.id,
+          name: item.code,
+          minScore: item.minScore,
+          maxScore: item.maxScore
+        };
+      });
     },
-    stepChange(step) {
+    async getAreaList() {
+      const data = await areaList({
+        workId: this.workId,
+        subject: this.subject
+      });
+      this.areas = data.map(item => {
+        return {
+          id: item.id,
+          areaName: item.areaName,
+          areaCode: item.areaCode
+        };
+      });
+    },
+    async stepChange(step) {
       this.curStep = step;
+      this.current = 1;
+      await this.getList();
+      if (this.papers.length) {
+        this.selectPaper(0);
+      } else {
+        this.curPaper = {};
+      }
     },
-    toReview(index) {
-      this.curPaper = { ...this.papers[index] };
-      this.curPaperIndex = index;
-      this.$refs.ImagePreview.open();
+    areaChange() {
+      this.getStepLevels();
+      this.toPage(1);
     },
-    paperPrev(index) {
-      this.curPaper = { ...this.papers[index] };
+    selectPaper(index) {
       this.curPaperIndex = index;
-    },
-    paperNext(index) {
       this.curPaper = { ...this.papers[index] };
-      this.curPaperIndex = index;
     },
-    prevPage() {
+    toReview(index) {
+      this.selectPaper(index);
+      this.$refs.ImagePreview.open();
+    },
+    async prevPage() {
       if (this.current === 1) {
         this.$Message.warning("当前已经是第一条数据了");
         return;
       }
       this.current--;
-      this.getList();
+      await this.getList();
+      this.selectPaper(this.papers.length - 1);
+      this.$refs.ImagePreview.initData();
     },
-    nextPage() {
+    async nextPage() {
       if (this.current === this.totalPage) {
         this.$Message.warning("当前已经是最后一条数据了");
         return;
       }
-      this.current++;
-      this.getList();
+      // 下一页时,继续获取当前页数据。
+      await this.getList();
+      this.selectPaper(0);
+      this.$refs.ImagePreview.initData();
     },
-    gradeCurPaper(grade) {
-      // TODO:to grade
-
-      if (this.curPaperIndex === this.size - 1) {
-        this.nextPage();
+    toNext() {
+      this.$refs.ImagePreview.showNext();
+    },
+    updateHistory() {
+      this.$refs.GradeHistoryPaper.updatePapers();
+    },
+    async serachPaperByCode(params) {
+      const data = await taskSnSearch(
+        params.codeType,
+        params.code,
+        this.filter.questionId
+      );
+      if (!data) {
+        this.$Message.error("没有查找到结果!");
         return;
-      } else {
-        this.paperNext(this.curPaperIndex + 1);
       }
+      data.title = data.examNumber;
+      this.papers = [data];
+      this.total = 1;
+    },
+    searchGradeChangeList(applyChangeLevelStatus) {
+      this.applyChangeLevelStatus = applyChangeLevelStatus;
+      this.toPage(1);
+    },
+    leaderSelectLevel(levelInfo) {
+      const content = `确定申请由${levelInfo.curLevel}档改为${levelInfo.selectedLevel}并打回给所有老师吗?`;
+      this.$Modal.confirm({
+        title: "改档",
+        content,
+        onOk: async () => {
+          await markStepChangeLevel({
+            subjectId: this.subjectId,
+            paperId: levelInfo.paperId,
+            level: levelInfo.selectedLevel
+          });
+          this.$Message.success("改档成功!");
+          this.getStepLevels();
+          this.updateHistory();
+          this.toNext();
+        }
+      });
+    },
+    // paper carousel
+    toViewCarouselPaper(paperIndex, papers) {
+      this.curCarouselPaperIndex = paperIndex;
+      this.carouselPapers = papers;
+      this.$refs.CarouselPapersPreview.open();
+    },
+    selectCarouselPaper(index) {
+      this.curCarouselPaperIndex = index;
+      this.curPaper = { ...this.carouselPapers[index] };
     }
   }
 };

+ 370 - 4
src/modules/mark/MarkOperation.vue

@@ -1,15 +1,381 @@
 <template>
-  <div class="mark-operation">
-    mark-operation
+  <div class="mark-operation home">
+    <div class="home-header">
+      <view-header>
+        <h1 slot="logo">{{ curSubject.name }}</h1>
+      </view-header>
+    </div>
+    <div class="home-body">
+      <div class="home-main home-main-nofooter grading-detail">
+        <grade-step
+          :steps="steps"
+          :init-step="curStep"
+          :show-analysis="false"
+          @on-change="stepChange"
+          ref="GradeStep"
+          v-if="steps.length"
+        ></grade-step>
+        <div class="detail-body">
+          <div class="detail-filter">
+            <Form ref="FilterForm" label-position="left" inline>
+              <FormItem>
+                <Select
+                  v-model="filter.questionId"
+                  @on-change="areaChange"
+                  placeholder="请选择考区"
+                >
+                  <Option
+                    v-for="area in areas"
+                    :key="area.id"
+                    :value="area.id"
+                    :label="area.areaName"
+                  ></Option>
+                </Select>
+              </FormItem>
+              <FormItem>
+                <Button type="primary" icon="ios-search" @click="toPage(1)"
+                  >查询</Button
+                >
+              </FormItem>
+            </Form>
+          </div>
+          <!-- detail-papers -->
+          <div :class="detailPapersClasses">
+            <div class="detail-papers-carousel">
+              <grade-history-paper
+                :question-id="filter.questionId"
+                stage="SCORE"
+                @on-paper-click="toViewCarouselPaper"
+                v-if="filter.questionId"
+                ref="GradeHistoryPaper"
+              ></grade-history-paper>
+            </div>
+            <div class="detail-papers-list">
+              <div :class="imageViewClasses">
+                <div
+                  :class="[
+                    'image-view',
+                    { 'image-view-act': curPaperIndex === index }
+                  ]"
+                  v-for="(image, index) in papers"
+                  :key="index"
+                >
+                  <h5 class="image-view-title">{{ image.title }}</h5>
+                  <div class="image-view-contain">
+                    <img
+                      :src="image.thumbSrc"
+                      :alt="image.title"
+                      @click="toReview(index)"
+                    />
+                  </div>
+                </div>
+              </div>
+              <div class="part-page" v-if="total > size">
+                <Page
+                  :current="current"
+                  :total="total"
+                  :page-size="size"
+                  show-total
+                  show-elevator
+                  :page-size-opts="[4, 6, 8]"
+                  @on-page-size-change="pageSizeChange"
+                  @on-change="toPage"
+                ></Page>
+              </div>
+            </div>
+          </div>
+          <!-- detail-aciton -->
+          <div class="detail-action">
+            <mark-action
+              :cur-paper="curPaper"
+              :levels="levels"
+              :user-role="curUserRoleType"
+              @on-select-score="scoreCurPaper"
+              @on-pass="passCurPaper"
+              v-if="curPaper.id"
+            ></mark-action>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- image-preview -->
+    <image-preview
+      class="grading-detail-image-preview"
+      :image-list="papers"
+      :init-index="curPaperIndex"
+      @on-paper-change="selectPaper"
+      @on-page-prev="prevPage"
+      @on-page-next="nextPage"
+      header-hide
+      ref="ImagePreview"
+      v-if="papers.length"
+    ></image-preview>
+
+    <!-- carousel paper review -->
+    <image-preview
+      class="grading-detail-image-preview"
+      :image-list="carouselPapers"
+      :init-index="curCarouselPaperIndex"
+      @on-paper-change="selectCarouselPaper"
+      loop
+      header-hide
+      ref="CarouselPapersPreview"
+      v-if="carouselPapers.length"
+    ></image-preview>
   </div>
 </template>
 
 <script>
+import {
+  markerPaperList,
+  markerLevelStatData,
+  areaList,
+  workLevelList,
+  subjectDetail,
+  paperSelectLevelOrScore,
+  paperPassLevel
+} from "@/api";
+import ImagePreview from "@/components/common/ImagePreview";
+import GradeStep from "../grading/components/GradeStep";
+import GradeHistoryPaper from "../grading/components/GradeHistoryPaper";
+import MarkAction from "./components/MarkAction";
+// 三种情况:
+// 管理员(ADMIN),科组长(MARK_LEADER),评卷员(MARKER)
+// 评卷员:操作记录,试卷列表,操作盘(打分)
+
+// TIP:不考虑管理员、科组长的情况
+// 管理员:试卷列表,操作盘
+// 科组长:操作记录,试卷列表,操作盘(改档)
+
 export default {
   name: "mark-operation",
+  components: {
+    ImagePreview,
+    GradeStep,
+    GradeHistoryPaper,
+    MarkAction
+  },
   data() {
-    return {};
+    return {
+      filter: {
+        markerId: this.$ls.get("user").id,
+        questionId: "",
+        sort: "paper.level",
+        stage: "SCORE"
+      },
+      workId: this.$route.params.workId,
+      subjectId: this.$route.params.subjectId,
+      subject: "",
+      curSubject: {},
+      curUserRoleType: "MARKER",
+      current: 1,
+      size: 6,
+      total: 0,
+      totalPage: 1,
+      curStep: null,
+      curStandardGradeId: "",
+      steps: [],
+      levels: [],
+      areas: [],
+      papers: [],
+      curPaper: {},
+      curPaperIndex: 0,
+      // carousel paper review,
+      carouselPapers: [],
+      curCarouselPaperIndex: 0
+    };
   },
-  methods: {}
+  computed: {
+    detailPapersClasses() {
+      return ["detail-papers", `detail-papers-col-${1 + this.size / 2}`];
+    },
+    imageViewClasses() {
+      return ["image-view-list", `image-view-list-${this.size / 2}`];
+    }
+  },
+  mounted() {
+    this.subject = this.subjectId.split("-")[1];
+    this.initData();
+  },
+  methods: {
+    async initData() {
+      this.getSubjectDetail();
+      this.getWorkLevels();
+
+      await this.getAreaList();
+      this.filter.questionId = this.areas[0].id;
+      await this.getStepLevels();
+      this.getList();
+    },
+    async getSubjectDetail() {
+      this.curSubject = await subjectDetail(this.subjectId);
+    },
+    async getList() {
+      const datas = {
+        ...this.filter,
+        page: this.current - 1,
+        size: this.size
+      };
+      if (this.curStep.type === "done") {
+        datas.level = this.curStep.name;
+        datas.sort = "updatedOn,desc";
+      }
+
+      const data = await markerPaperList(datas);
+      this.papers = data.data.map(paper => {
+        paper.title = paper.examNumber;
+        return paper;
+      });
+      this.total = data.totalCount;
+      this.totalPage = data.totalPage;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    async getStepLevels() {
+      const data = await markerLevelStatData(
+        this.filter.markerId,
+        this.filter.questionId
+      );
+      const undoIndex = data.findIndex(item => item.id === null);
+      let otherStep = [];
+      if (undoIndex !== -1) {
+        const undo = { ...data[undoIndex] };
+        data.splice(undoIndex, 1);
+
+        otherStep.push({
+          name: "待评",
+          count: undo.count,
+          type: "undo"
+        });
+        otherStep.push({
+          name: "改档",
+          count: undo.shift,
+          type: "shift"
+        });
+        otherStep.push({
+          name: "改档打分",
+          count: undo.shiftScore,
+          type: "shiftScore"
+        });
+      }
+      let levelMenu = data.map(item => {
+        return {
+          ...item,
+          name: item.id,
+          type: "done"
+        };
+      });
+      this.steps = [...levelMenu, ...otherStep];
+      if (!this.curStep) {
+        let curStep = {};
+        if (undoIndex === -1) {
+          curStep = this.steps[0];
+        } else {
+          curStep = this.steps[this.steps.length - 3];
+        }
+        this.curStep = curStep;
+      }
+    },
+    async getWorkLevels() {
+      const data = await workLevelList(this.workId);
+      this.levels = data.map(item => {
+        return {
+          id: item.id,
+          name: item.code,
+          minScore: item.minScore,
+          maxScore: item.maxScore
+        };
+      });
+    },
+    async getAreaList() {
+      const data = await areaList({
+        workId: this.workId,
+        subject: this.subject
+      });
+      this.areas = data.map(item => {
+        return {
+          id: item.id,
+          areaName: item.areaName,
+          areaCode: item.areaCode
+        };
+      });
+    },
+    pageSizeChange(size) {
+      this.size = size;
+      this.toPage(1);
+      this.getStepLevels();
+    },
+    async stepChange(step) {
+      this.curStep = step;
+      this.current = 1;
+      await this.getList();
+      if (this.papers.length) {
+        this.selectPaper(0);
+      } else {
+        this.curPaper = {};
+      }
+    },
+    areaChange() {
+      this.getStepLevels();
+      this.toPage(1);
+    },
+    selectPaper(index) {
+      this.curPaperIndex = index;
+      this.curPaper = { ...this.papers[index] };
+    },
+    toReview(index) {
+      this.selectPaper(index);
+      this.$refs.ImagePreview.open();
+    },
+    async prevPage() {
+      if (this.current === 1) {
+        this.$Message.warning("当前已经是第一条数据了");
+        return;
+      }
+      this.current--;
+      await this.getList();
+      this.selectPaper(this.papers.length - 1);
+      this.$refs.ImagePreview.initData();
+    },
+    async nextPage() {
+      if (this.current === this.totalPage) {
+        this.$Message.warning("当前已经是最后一条数据了");
+        return;
+      }
+      // 下一页时,继续获取当前页数据。
+      await this.getList();
+      this.selectPaper(0);
+      this.$refs.ImagePreview.initData();
+    },
+    async scoreCurPaper(score) {
+      await paperSelectLevelOrScore(this.curPaper.id, score, "SCORE");
+      this.getStepLevels();
+      this.updateHistory();
+      this.toNext();
+    },
+    async passCurPaper(level) {
+      await paperPassLevel(this.curPaper.id);
+      this.toNext();
+    },
+    toNext() {
+      this.$refs.ImagePreview.showNext();
+    },
+    updateHistory() {
+      this.$refs.GradeHistoryPaper.updatePapers();
+    },
+    // paper carousel
+    toViewCarouselPaper(paperIndex, papers) {
+      this.curCarouselPaperIndex = paperIndex;
+      this.carouselPapers = papers;
+      this.$refs.CarouselPapersPreview.open();
+    },
+    selectCarouselPaper(index) {
+      this.curCarouselPaperIndex = index;
+      this.curPaper = { ...this.carouselPapers[index] };
+    }
+  }
 };
 </script>

+ 6 - 111
src/modules/mark/MarkProgress.vue

@@ -1,124 +1,19 @@
 <template>
   <div class="mark-progress">
-    <div class="progress-title">
-      <span>当前阶段:{{ stepName }}</span>
-    </div>
-
-    <div class="part-box">
-      <div class="progress-table">
-        <table class="table table-noborder">
-          <tr v-for="(area, aindex) in areaInfos" :key="aindex">
-            <td style="width: 11%">{{ area.name }}</td>
-            <td style="width: 56%;">
-              <progress-line
-                :sum="area.sum"
-                :current="area.current"
-              ></progress-line>
-            </td>
-            <td style="width: 11%;">进度:{{ area.progress }}%</td>
-            <td style="width: 11%;"></td>
-            <td style="width: 11%;"></td>
-          </tr>
-        </table>
-      </div>
-      <div class="progress-table">
-        <table class="table table-noborder">
-          <tr v-for="(area, aindex) in userInfos" :key="aindex">
-            <td style="width: 11%">{{ area.name }}</td>
-            <td style="width: 56%;">
-              <progress-line
-                :sum="area.sum"
-                :current="area.current"
-              ></progress-line>
-            </td>
-            <td style="width: 11%;">进度:{{ area.progress }}%</td>
-            <td style="width: 11%;">改档:{{ area.arbitration }}</td>
-            <td style="width: 11%;">改档打分:{{ area.arbitrationScore }}</td>
-          </tr>
-        </table>
-      </div>
-    </div>
+    <!-- TODO:可能会将分档进度和打分进度分开展示 -->
+    <grading-progress></grading-progress>
   </div>
 </template>
 
 <script>
-import ProgressLine from "../grading/components/ProgressLine";
-import { markProgressDetail } from "@/api";
+import GradingProgress from "@/modules/grading/GradingProgress";
 
 export default {
   name: "mark-progress",
-  components: { ProgressLine },
+  components: { GradingProgress },
   data() {
-    return {
-      subjectId: this.$route.params.subjectId,
-      stepName: "打分阶段",
-      areaInfos: [
-        {
-          name: "总体进度",
-          sum: 5500,
-          current: 0,
-          progress: 0
-        },
-        {
-          name: "考区1进度",
-          sum: 1000,
-          current: 990,
-          progress: 100
-        },
-        {
-          name: "考区2进度",
-          sum: 5500,
-          current: 2000,
-          progress: 45.45
-        },
-        {
-          name: "考区3进度",
-          sum: 5500,
-          current: 2000,
-          progress: 45.45
-        }
-      ],
-      userInfos: [
-        {
-          name: "科组长",
-          sum: 5500,
-          current: 2000,
-          arbitration: 10,
-          arbitrationScore: 8,
-          progress: 45.45
-        },
-        {
-          name: "评卷员1",
-          sum: 5500,
-          current: 2000,
-          arbitration: 10,
-          arbitrationScore: 8,
-          progress: 45.45
-        },
-        {
-          name: "评卷员2",
-          sum: 5500,
-          current: 2000,
-          arbitration: 10,
-          arbitrationScore: 8,
-          progress: 45.45
-        },
-        {
-          name: "评卷员3",
-          sum: 5500,
-          current: 2000,
-          arbitration: 10,
-          arbitrationScore: 8,
-          progress: 45.45
-        }
-      ]
-    };
+    return {};
   },
-  methods: {
-    async getData() {
-      const data = await markProgressDetail(this.subjectId);
-      console.log(data);
-    }
-  }
+  methods: {}
 };
 </script>

+ 73 - 28
src/modules/mark/MarkTaskManage.vue

@@ -2,11 +2,17 @@
   <div class="mark-task-manage">
     <div class="part-box-head mark-task-head">
       <Select
-        v-model="areaCode"
+        v-model="questionId"
         placeholder="请选择考区"
+        @on-change="areaChange"
         style="width: 140px;margin-right: 10px;"
       >
-        <Option value="">考区一</Option>
+        <Option
+          v-for="area in areas"
+          :key="area.id"
+          :value="area.id"
+          :label="area.areaName"
+        ></Option>
       </Select>
       <Button type="primary" icon="md-search" @click="getData">查询</Button>
     </div>
@@ -22,10 +28,10 @@
           <th>本次任务数</th>
         </tr>
         <tr v-for="(task, tindex) in taskList" :key="tindex">
-          <td>{{ task.grade }}</td>
-          <td>{{ task.sum }}</td>
-          <td>{{ task.finished }}</td>
-          <td>{{ task.unfinished }}</td>
+          <td>{{ task.level }}</td>
+          <td>{{ task.totalCount }}</td>
+          <td>{{ task.successCount }}</td>
+          <td>{{ task.waitCount }}</td>
           <td>
             <Select v-model="task.sortRule" @on-change="sortRuleChange(task)">
               <Option
@@ -37,7 +43,7 @@
             </Select>
           </td>
           <td>
-            <Select v-model="task.showSerialNo">
+            <Select v-model="task.displayNumber">
               <Option
                 v-for="(val, key) in BOOLEAN_TYPE"
                 :key="key"
@@ -50,9 +56,9 @@
             <InputNumber
               v-model="task.taskCount"
               :min="0"
-              :max="task.unfinished"
+              :max="task.waitCount"
               :precision="0"
-              :disabled="!task.unfinished"
+              :disabled="!task.waitCount"
             ></InputNumber>
           </td>
         </tr>
@@ -69,7 +75,12 @@
 </template>
 
 <script>
-import { markTaskInfo, createMarkTask } from "@/api";
+import {
+  areaList,
+  markTaskInfo,
+  checkMarkTaskCanSubmit,
+  createGradingTask
+} from "@/api";
 import { calcSum } from "@/plugins/utils";
 import { SORT_ORDER_TYPE, BOOLEAN_TYPE } from "@/constants/enumerate";
 
@@ -77,12 +88,16 @@ export default {
   name: "mark-task-manage",
   data() {
     return {
+      workId: this.$route.params.workId,
       subjectId: this.$route.params.subjectId,
+      subject: "",
+      questionId: "",
       SORT_ORDER_TYPE,
       BOOLEAN_TYPE,
-      areaCode: "",
+      canSubmitTask: true,
       isSubmit: false,
-      taskList: []
+      taskList: [],
+      areas: []
     };
   },
   computed: {
@@ -92,39 +107,69 @@ export default {
     }
   },
   mounted() {
+    this.subject = this.subjectId.split("-")[1];
     this.initData();
   },
   methods: {
-    initData() {
-      this.taskList = "ABCDEFGHIJK".split("").map(item => {
-        const sum = Math.floor(Math.random() * 20);
-        const finished = Math.floor(sum * Math.random());
+    async initData() {
+      await this.getAreaList();
+      this.questionId = this.areas[0].id;
+      this.areaChange();
+    },
+    async getAreaList() {
+      const data = await areaList({
+        workId: this.workId,
+        subject: this.subject
+      });
+      this.areas = data.map(item => {
         return {
-          grade: item,
-          sum,
-          finished,
-          unfinished: sum - finished,
-          sortRule: 0,
-          showSerialNo: 0,
-          taskCount: 0
+          id: item.id,
+          areaName: item.areaName,
+          areaCode: item.areaCode
         };
       });
     },
+    async checkCanSubmitTask() {
+      this.canSubmitTask = await checkMarkTaskCanSubmit(
+        this.subjectId,
+        this.questionId
+      );
+    },
+    areaChange() {
+      this.checkCanSubmitTask();
+      this.getData();
+    },
     async getData() {
-      const data = await markTaskInfo(this.subjectId);
-      console.log(data);
+      const data = await markTaskInfo(this.subjectId, this.questionId);
+      this.taskList = data.map(item => {
+        return {
+          ...item,
+          displayNumber: 0,
+          sortRule: 0,
+          taskCount: 0
+        };
+      });
     },
     sortRuleChange(task) {
-      if (!task.sortRule) task.showSerialNo = 0;
+      if (!task.sortRule) task.displayNumber = 0;
     },
     async submit() {
+      if (!this.curTaskCount) {
+        this.$Message.error("请设置任务数量!");
+        return;
+      }
       if (this.isSubmit) return;
       this.isSubmit = true;
-      const data = await createMarkTask(this.taskList).catch(() => {});
+      const data = await createGradingTask({
+        subjectId: this.subjectId,
+        taskCount: this.curTaskCount,
+        questionId: this.questionId,
+        taskList: this.taskList
+      }).catch(() => {});
       this.isSubmit = false;
       if (!data) return;
       this.$Message.success("任务创建成功!");
-      this.getData();
+      this.areaChange();
     }
   }
 };

+ 4 - 1
src/modules/mark/MarkUserManage.vue

@@ -1,12 +1,15 @@
 <template>
   <div class="mark-user-manage">
-    mark-user-manage
+    <grading-user-manage></grading-user-manage>
   </div>
 </template>
 
 <script>
+import GradingUserManage from "@/modules/grading/GradingUserManage";
+
 export default {
   name: "mark-user-manage",
+  components: { GradingUserManage },
   data() {
     return {};
   },

+ 167 - 116
src/modules/mark/components/MarkAction.vue

@@ -16,40 +16,42 @@
             :label="val"
           ></Option>
         </Select>
-        <Button icon="ios-search" slot="append" @click="search"></Button>
+        <Button icon="ios-search" slot="append" @click="searchCode"></Button>
       </Input>
     </div>
     <!-- 改档处理状态查询 -->
     <div class="action-grade-change-search" v-if="rights.gradeChangeSearch">
-      <Select v-model="filter.codeType" placeholder="类型" style="width: 100px">
+      <Select
+        v-model="applyChangeLevelStatus"
+        placeholder="类型"
+        style="width: 100px"
+      >
         <Option
-          v-for="(val, key) in CODE_TYPE"
+          v-for="(val, key) in CHANGE_LEVEL_STATUS"
           :key="key"
-          :value="key"
+          :value="key * 1"
           :label="val"
         ></Option>
       </Select>
-      <Button
-        icon="ios-search"
-        slot="append"
-        @click="gradeChangeSearch"
-      ></Button>
+      <Button icon="ios-search" slot="append" @click="searchGradeChange"
+        >查询</Button
+      >
     </div>
     <!-- 头部信息 ------ -->
     <!-- 试卷状态 -->
     <!-- 状态:已评,待评,改档,改档打分 -->
-    <div class="action-paper-state" v-if="rights.paperState">
+    <div class="action-paper-state">
       <p class="paper-state-cont">{{ step.typeName }}</p>
     </div>
     <!-- 试卷信息 -->
-    <div class="action-paper-info" v-if="rights.paperInfo">
-      <p>{{ curPaper.examNumber }}</p>
+    <div class="action-paper-info">
+      <p v-if="IS_ADMIN">{{ curPaper.examNumber }}</p>
       <p>No.{{ curPaper.sn }}</p>
     </div>
     <!-- 改档信息 -->
     <div class="action-grade-change" v-if="rights.gradeChange">
-      <p>原档位:</p>
-      <p>申请档位:</p>
+      <p>原档位:{{ curPaper.originLevel }}</p>
+      <p>申请档位:{{ curPaper.redoLevel }}</p>
     </div>
     <div class="action-grade-change-status" v-if="rights.gradeChange">
       <p>已同意</p>
@@ -59,9 +61,9 @@
     <div class="action-grade-info" v-if="rights.gradeInfo">
       <h3 class="grade-info-name">{{ curLevel.name }}</h3>
       <p class="grade-info-range">
-        <span>{{ curLevel.range[0] }}</span>
+        <span>{{ curLevel.minScore }}</span>
         <span>~</span>
-        <span>{{ curLevel.range[1] }}</span>
+        <span>{{ curLevel.maxScore }}</span>
       </p>
     </div>
     <!-- 打分信息 -->
@@ -69,19 +71,32 @@
       <p>{{ curPaper.score }}</p>
     </div>
     <!-- 选择分数 / 档位 -->
-    <div class="action-grade-list action-mark-list" v-if="rights.gradeList">
+    <div class="action-grade-list" v-if="rights.gradeList && IS_MARK_LEADER">
       <div
         class="action-grade-list-item"
-        v-for="(level, index) in levelList"
+        v-for="(level, index) in levels"
         :key="index"
       >
-        <p>{{ level.name }}</p>
+        <p @click="selectLevel(level)">{{ level.name }}</p>
         <p>{{ level.range[0] }}~{{ level.range[1] }}</p>
       </div>
     </div>
-    <div class="action-grade-pass" v-if="rights.gradeList">
-      <Button type="primary" @click="toPass">跳过</Button>
+    <div
+      class="action-grade-list action-mark-list"
+      v-if="rights.gradeList && !IS_MARK_LEADER"
+    >
+      <div
+        class="action-grade-list-item"
+        v-for="(score, index) in scores"
+        :key="index"
+      >
+        <p @click="selectScore(score)">{{ score }}</p>
+      </div>
     </div>
+    <!-- <div class="action-grade-pass" v-if="rights.gradeList && IS_MARKER">
+      <Button type="primary" @click="toPass">跳过</Button>
+    </div> -->
+
     <!-- 评卷记录 -->
     <div class="action-grade-history" v-if="rights.markHis">
       <h3>评卷记录</h3>
@@ -100,17 +115,70 @@
 </template>
 
 <script>
-import { CODE_TYPE } from "@/constants/enumerate";
+import { markHistoryList } from "@/api";
+import { CODE_TYPE, CHANGE_LEVEL_STATUS } from "@/constants/enumerate";
 // 三种情况:
 // 管理员(ADMIN),科组长(MARK_LEADER),评卷员(MARKER)
 // 管理员:查询,头部信息,评卷记录
 // 科组长:查询,头部信息,选择档位,评卷记录
 // 评卷员:头部信息,选择分数
+/*
+[paper template]
+{
+  "id": 165,
+  "sn": "029947536",
+  "examNumber": "1901040084",
+  "level": "A",
+  "score": null,
+  "redoLevel": null,
+  "updatedOn": 1591767742000,
+  "imgSrc": "",
+  "thumbSrc": "",
+  "markByLeader": false,
+  "markedLogic": true,
+  "areaCode": "2",
+  "inspectScore": null,
+  "inspectLevel": null,
+  "inspector": null,
+  "sheetSrc": null,
+  "stage": "LEVEL",
+  "test": 0,
+  "paperTest": 0,
+  "markResults": [],
+  "rejected": false,
+  "arbitrated": false,
+  "sample": false,
+  "tagged": false,
+  "missing": false,
+  "manual": false
+}
+[marktask template]
+{
+  "id": 511,
+  "sn": "4929446110",
+  "redoLevel": null,
+  "level": "A",
+  "score": null,
+  "result": "100",
+  "originLevel": null,
+  "markerId": 49,
+  "marker": "pj061001",
+  "updatedOn": 1594775592000,
+  "imgSrc": "",
+  "thumbSrc": "",
+  "markByLeader": true,
+  "oldRejected": false,
+  "paperId": 168,
+  "randomSeqNew": 9446110,
+  "randomSeq": null,
+  "rejected": false,
+  "sample": true
+}
+*/
+
 const initRights = {
   search: false,
   gradeChangeSearch: false,
-  paperState: false,
-  paperInfo: false,
   gradeChange: false,
   gradeInfo: false,
   markInfo: false,
@@ -121,7 +189,7 @@ const initRights = {
 export default {
   name: "mark-action",
   props: {
-    paper: {
+    curPaper: {
       type: Object,
       default() {
         return {};
@@ -136,12 +204,6 @@ export default {
     userRole: {
       type: String,
       default: "MARKER"
-    },
-    step: {
-      type: Object,
-      default() {
-        return {};
-      }
     }
   },
   data() {
@@ -151,51 +213,18 @@ export default {
       },
       roleRight: {
         ADMIN: {
-          done: [
-            "search",
-            "gradeHis",
-            "paperState",
-            "paperInfo",
-            "gradeInfo",
-            "markInfo"
-          ],
-          editGrade: [
-            "search",
-            "gradeChangeSearch",
-            "paperState",
-            "paperInfo",
-            "gradeChange"
-          ]
+          done: ["search", "gradeHis", "gradeInfo", "markInfo"],
+          shift: ["search", "gradeChangeSearch", "gradeChange"]
         },
         MARK_LEADER: {
-          done: [
-            "search",
-            "gradeList",
-            "gradeHis",
-            "paperState",
-            "paperInfo",
-            "gradeInfo",
-            "markInfo"
-          ],
-          editGrade: [
-            "search",
-            "gradeList",
-            "paperState",
-            "paperInfo",
-            "gradeChange"
-          ]
+          done: ["search", "gradeList", "gradeHis", "gradeInfo", "markInfo"],
+          shift: ["search", "gradeList", "gradeChange"]
         },
         MARKER: {
-          done: [
-            "gradeList",
-            "paperState",
-            "paperInfo",
-            "gradeInfo",
-            "markInfo"
-          ],
-          undo: ["gradeList", "paperState", "paperInfo", "gradeInfo"],
-          editGrade: ["gradeList", "paperState", "paperInfo", "gradeChange"],
-          editGradeMark: ["gradeList", "paperState", "paperInfo", "gradeInfo"]
+          done: ["gradeList", "gradeInfo", "markInfo"],
+          undo: ["gradeList", "gradeInfo"],
+          shift: ["gradeList", "gradeChange"],
+          shiftScore: ["gradeList", "gradeInfo"]
         }
       },
       filter: {
@@ -203,17 +232,19 @@ export default {
         code: ""
       },
       CODE_TYPE,
-      curPaper: {
-        examNumber: "2020105133",
-        sn: "2020105133"
+      CHANGE_LEVEL_STATUS,
+      applyChangeLevelStatus: null,
+      stepDict: {
+        undo: "待评",
+        done: "已评",
+        level: "改档",
+        levelScore: "改档打分"
       },
-      levelList: [],
+      stepType: "",
+      stepLabel: "",
+      scores: [],
       gradingHistory: [],
-      curLevel: {
-        name: "A",
-        range: [5, 20]
-      },
-      curMark: 95
+      curLevel: {}
     };
   },
   computed: {
@@ -228,60 +259,80 @@ export default {
     }
   },
   watch: {
-    paper(val) {
+    curPaper(val) {
       this.rebuildRight();
     }
   },
   mounted() {
-    this.initData();
+    this.rebuildRight();
   },
   methods: {
-    initData() {
-      this.gradingHistory = "ABCDEFGHIJK".split("").map((item, index) => {
-        return {
-          id: index,
-          name: "张珊",
-          value: item
-        };
-      });
+    getStepType() {
+      const paper = this.curPaper;
+      if (paper.level) return "done";
+      // TODO:
+      // if (paper.arbitrated) return "arbitrate";
+      // if (paper.rejected) return "reject";
+      // if (!paper.rejected && !paper.arbitrated && !paper.level) return "undo";
+      return;
     },
     rebuildRight() {
+      this.stepType = this.getStepType();
+      this.stepLabel = this.stepDict[this.stepType];
       this.rights = { ...initRights };
-      const rights = this.roleRight[this.userRole][this.step.type] || [];
-      rights.map(key => {
+      const roleRights = this.roleRight[this.userRole][this.step.type] || [];
+      roleRights.map(key => {
         this.rights[key] = true;
       });
 
-      this.updateCurLevel();
-      this.updateLevelList();
-    },
-    updateCurLevel() {
-      if (!this.paper.level) return;
-      const paperLevel = this.levels.find(
-        level => level.name === this.paper.level
-      );
-      this.curLevel = {
-        name: paperLevel.name,
-        range: paperLevel.range
-      };
+      if (this.curPaper.level) {
+        this.curLevel = this.levels.find(
+          level => level.name === this.curPaper.level
+        );
+        this.updateScoreList();
+      }
+      if (this.rights.gradeHis) {
+        this.getMarkHistory();
+      }
     },
-    updateLevelList() {
-      if (!this.curLevel.range) return;
-      let levelList = [];
-      let [start, end] = this.curLevel.range;
-      for (let i = start; i <= end; i++) {
-        levelList.push({ name: i });
+    updateScoreList() {
+      let scores = [];
+      const { minScore, maxScore } = this.curLevel;
+      for (let i = minScore; i <= maxScore; i++) {
+        scores.push(i);
       }
-      this.levelList = levelList;
+      this.scores = scores;
     },
-    search() {
-      this.$emit("code-search", this.filter);
+    async getMarkHistory() {
+      const data = await markHistoryList(this.curPaper.id, "SCORE");
+      this.gradingHistory = data.map(item => {
+        return {
+          id: item.markerId,
+          name: item.marker,
+          value: item.result || "未评"
+        };
+      });
+    },
+    selectLevel(level) {
+      // 科组长改档
+      this.$emit("on-leader-level", {
+        curPaperId: this.curPaper.id,
+        curLevel: this.curPaper.level,
+        selectedLevel: level.name
+      });
+    },
+    selectScore(score) {
+      // 评卷员打分
+      this.$emit("on-select-score", score * 1);
     },
     toPass() {
       this.$emit("on-pass");
     },
-    gradeChangeSearch() {
-      this.$emit("grade-change-search", this.filter);
+    searchCode() {
+      this.$emit("on-code-search", this.filter);
+    },
+    searchGradeChange() {
+      this.$emit("on-grade-change-search", this.applyChangeLevelStatus);
     }
   }
 };