Explorar el Código

标准卷、列表、操作记录

zhangjie hace 3 años
padre
commit
96ece1f6d9

+ 1 - 1
package.json

@@ -15,7 +15,7 @@
     "echarts": "^4.7.0",
     "echarts": "^4.7.0",
     "html2canvas": "^1.0.0-rc.5",
     "html2canvas": "^1.0.0-rc.5",
     "js-md5": "^0.7.3",
     "js-md5": "^0.7.3",
-    "view-design": "^4.2.0",
+    "view-design": "4.6.0",
     "vue": "^2.6.11",
     "vue": "^2.6.11",
     "vue-echarts": "^5.0.0-beta.0",
     "vue-echarts": "^5.0.0-beta.0",
     "vue-ls": "^3.2.1",
     "vue-ls": "^3.2.1",

+ 1 - 0
src/assets/styles/index.less

@@ -7,5 +7,6 @@
 @import "./login.less";
 @import "./login.less";
 @import "./main.less";
 @import "./main.less";
 @import "./mark.less";
 @import "./mark.less";
+@import "./marker.less";
 @import "./common-component.less";
 @import "./common-component.less";
 @import "./adaptive.less";
 @import "./adaptive.less";

+ 343 - 0
src/assets/styles/marker.less

@@ -0,0 +1,343 @@
+@color-background: #2c2e3e;
+@color-background-light: #3d3f55;
+@color-act1: #155b92;
+@color-act2: #cc4635;
+@color-act3: #f9dbc4;
+@color-text: #9d9c9c;
+@color-text-act: #e1dfe0;
+
+.marker-grading {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  z-index: auto;
+  color: @color-text;
+}
+.marker-header {
+  position: fixed;
+  height: 40px;
+  padding: 5px 0;
+  top: 0;
+  left: 0;
+  width: 100%;
+  z-index: 9;
+  background-color: @color-background-light;
+  display: flex;
+  justify-content: space-between;
+  line-height: 30px;
+}
+.marker-body {
+  position: absolute;
+  left: 0;
+  top: 40px;
+  right: 280px;
+  bottom: 0;
+  overflow: auto;
+  padding: 5px;
+  background: @color-background;
+  z-index: 7;
+}
+.header-part {
+  display: inline-block;
+  vertical-align: top;
+  padding: 0 15px;
+  margin-left: 10px;
+  background: @color-background;
+  border-radius: 5px;
+  cursor: pointer;
+  &:hover {
+    background-color: shade(@color-background, 20%);
+    color: @color-text-act;
+  }
+}
+.header-history {
+  margin-right: 10px;
+}
+.header-user {
+  display: inline-block;
+  vertical-align: top;
+  width: 280px;
+  border-left: 1px solid @color-background;
+  margin: -5px 0;
+  padding: 5px 10px;
+
+  .ivu-dropdown {
+    display: block;
+  }
+
+  .user-name {
+    background: @color-background;
+    border-radius: 15px;
+    padding: 0 15px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    cursor: pointer;
+    > i {
+      color: #bec3d1;
+      &:last-child {
+        display: block;
+        float: right;
+        margin-top: 8px;
+      }
+    }
+    &:hover {
+      background-color: shade(@color-background, 20%);
+      color: @color-text-act;
+    }
+  }
+}
+
+// .marker-body
+.marker-image-list {
+  height: 100%;
+
+  .marker-image-item {
+    display: inline-block;
+    vertical-align: top;
+    width: 25%;
+    height: 50%;
+    padding: 5px;
+    font-size: 0;
+
+    &-act {
+      .marker-image-content::before {
+        content: "";
+        display: block;
+        position: absolute;
+        width: 20px;
+        height: 20px;
+        top: 0;
+        right: 0;
+        border-top-right-radius: 5px;
+        border-bottom-left-radius: 20px;
+        background-color: @color-act1;
+      }
+    }
+  }
+  .marker-image-content {
+    position: relative;
+    background-color: @color-background-light;
+    height: 100%;
+    padding: 10px;
+    border-radius: 5px;
+  }
+
+  &-3 {
+    .marker-image-item {
+      width: 33.33%;
+      height: 100%;
+    }
+  }
+  &-4 {
+    .marker-image-item {
+      width: 50%;
+      height: 50%;
+    }
+  }
+  &-6 {
+    .marker-image-item {
+      width: 33.33%;
+      height: 50%;
+    }
+  }
+  &-8 {
+    .marker-image-item {
+      width: 25%;
+      height: 50%;
+    }
+  }
+  &-10 {
+    .marker-image-item {
+      width: 20%;
+      height: 50%;
+    }
+  }
+  &-12 {
+    .marker-image-item {
+      width: 25%;
+      height: 33.33%;
+    }
+  }
+  &-15 {
+    .marker-image-item {
+      width: 20%;
+      height: 33.33%;
+    }
+  }
+  &-20 {
+    .marker-image-item {
+      width: 20%;
+      height: 25%;
+    }
+  }
+}
+.marker-image-view {
+  position: relative;
+  height: 100%;
+  font-size: 14px;
+
+  .image-view-contain {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 36px;
+  }
+  .image-view-footer {
+    position: absolute;
+    width: 100%;
+    bottom: 0;
+    left: 0;
+    height: 26px;
+    line-height: 26px;
+    padding: 0;
+    display: flex;
+    justify-content: space-between;
+  }
+  .image-info > div {
+    display: inline-block;
+    vertical-align: top;
+  }
+  .image-checkbox {
+    height: 26px;
+    width: 26px;
+
+    border-radius: 5px;
+    background-color: @color-background;
+    color: @color-text-act;
+    font-size: 20px;
+    text-align: center;
+    line-height: 26px;
+    cursor: pointer;
+
+    &:hover,
+    &.image-checked {
+      box-shadow: 0 0 3px inset @color-background-light;
+    }
+  }
+  .image-level {
+    height: 26px;
+    width: 26px;
+    margin-right: 5px;
+    border-radius: 5px;
+    background-color: @color-background;
+    font-size: 18px;
+    text-align: center;
+  }
+  .image-title {
+    height: 26px;
+    padding-left: 10px;
+    cursor: pointer;
+    &:hover {
+      color: @color-text-act;
+    }
+  }
+  .image-rotate {
+    height: 26px;
+    font-size: 26px;
+    cursor: pointer;
+    &:hover {
+      color: @color-text-act;
+    }
+  }
+}
+
+// marker-action
+.marker-action {
+  position: fixed;
+  width: 280px;
+  right: 0;
+  top: 0;
+  height: 100%;
+  z-index: 8;
+  padding: 50px 10px 20px;
+  background-color: @color-background-light;
+}
+
+// marker-history
+.marker-history {
+  .image-view-footer {
+    color: @color-text;
+  }
+  .image-action-name {
+    height: 26px;
+    padding-left: 5px;
+  }
+}
+.history-image-list {
+  height: 360px;
+
+  .marker-image-item {
+    width: 20%;
+    height: 100%;
+  }
+}
+// marker-standard
+.marker-standard {
+  .level-list {
+    text-align: center;
+    margin-bottom: 30px;
+  }
+  .level-item {
+    display: inline-block;
+    vertical-align: top;
+    width: 40px;
+    height: 26px;
+    line-height: 26px;
+    padding: 0 5px;
+    margin: 0 10px;
+    border-radius: 5px;
+    background-color: @color-background-light;
+    color: @color-text;
+    text-align: center;
+    cursor: pointer;
+    &:hover,
+    &-act {
+      color: @color-text-act;
+      background-color: @color-act1;
+    }
+  }
+  .standard-image-list {
+    height: auto;
+  }
+
+  .image-view-footer {
+    color: @color-text;
+  }
+  .marker-image-item {
+    width: 20%;
+    height: 300px;
+  }
+  .standard-image-none {
+    padding-top: 150px;
+    font-size: 16px;
+    text-align: center;
+    color: @color-text;
+  }
+}
+
+// marker-modal
+.marker-modal {
+  .ivu-modal-mask {
+    background-color: rgba(55, 55, 55, 0.8);
+  }
+  .ivu-modal {
+    width: 100% !important;
+    top: 0 !important;
+  }
+  .ivu-modal-content {
+    background-color: @color-background;
+  }
+  .ivu-modal-header {
+    border-color: @color-background-light;
+    &-inner {
+      color: @color-text;
+      text-align: left;
+    }
+  }
+
+  .ivu-modal-body {
+    padding: 15px;
+  }
+}

+ 1 - 1
src/constants/authority.js

@@ -215,7 +215,7 @@ export const checkRouterValid = (roleCode, routerName) => {
   const func = {
   const func = {
     SUPER_ADMIN: () => getAdminRouter(1),
     SUPER_ADMIN: () => getAdminRouter(1),
     ADMIN: getAdminRouter,
     ADMIN: getAdminRouter,
-    MARKER: () => ["GradingOperation", "MarkOperation"],
+    MARKER: () => ["GradingOperation", "MarkOperation", "MarkerGrading"],
     MARK_LEADER: getLeadRouter,
     MARK_LEADER: getLeadRouter,
     INSPECTION: () => inspection.map(item => item.name),
     INSPECTION: () => inspection.map(item => item.name),
     QC: () => ["Quality"]
     QC: () => ["Quality"]

+ 441 - 0
src/modules/grading/marker/MarkerGrading.vue

@@ -0,0 +1,441 @@
+<template>
+  <div class="marker-grading">
+    <marker-header
+      :data="workSubject"
+      @area-change="areaChange"
+      @to-history="toHistory"
+      @to-standard="toStandard"
+    ></marker-header>
+
+    <div class="marker-action"></div>
+
+    <div class="marker-body">
+      <div :class="markerImageListClasses" v-if="papers.length">
+        <div
+          v-for="(paper, index) in papers"
+          :key="paper.id"
+          :class="[
+            'marker-image-item',
+            {
+              'marker-image-item-act': curPaperIndex === index
+            }
+          ]"
+        >
+          <div class="marker-image-content">
+            <marker-image-view
+              :data="paper"
+              @to-review="toReview(index)"
+              @to-check="toCheck"
+            ></marker-image-view>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- MarkerHistory -->
+    <marker-history
+      :question-id="filter.questionId"
+      ref="MarkerHistory"
+    ></marker-history>
+    <!-- MarkerStandard -->
+    <marker-standard
+      :question-id="filter.questionId"
+      :levels="levels"
+      ref="MarkerStandard"
+      v-if="levels.length && filter.questionId && paramsSet.showSample"
+    ></marker-standard>
+  </div>
+</template>
+
+<script>
+import MarkerHeader from "./MarkerHeader";
+import MarkerImageView from "./MarkerImageView";
+import MarkerHistory from "./MarkerHistory";
+import MarkerStandard from "./MarkerStandard";
+import {
+  markerTaskList,
+  markerLevelTotalStatData,
+  workLevelList,
+  paperSelectLevelOrScore,
+  paperSelectLevelBatch,
+  paperTaskPass,
+  getParamsSet
+} from "@/api";
+
+export default {
+  name: "marker-grading",
+  components: { MarkerHeader, MarkerImageView, MarkerHistory, MarkerStandard },
+  data() {
+    return {
+      filter: {
+        markerId: this.$ls.get("user").id,
+        questionId: "",
+        sort: "randomSeq,asc",
+        stage: "LEVEL"
+      },
+      typeFilter: {
+        done: {
+          level: ""
+        },
+        undo: {},
+        reject: {
+          reject: true
+        }
+      },
+      workId: this.$route.params.workId,
+      subjectId: this.$route.params.subjectId,
+      subject: "",
+      workSubject: {},
+      current: 1,
+      size: 12,
+      total: 0,
+      totalPage: 1,
+      curStep: null,
+      curStandardGradeId: "",
+      steps: [],
+      levels: [],
+      curArea: {},
+      papers: [],
+      curPaper: {},
+      curPaperIndex: 0,
+      paramsSet: {}
+    };
+  },
+  computed: {
+    markerImageListClasses() {
+      return ["marker-image-list", `marker-image-list-${this.size}`];
+    }
+  },
+  created() {
+    this.subject = this.subjectId.split("-")[1];
+    this.workSubject = {
+      workId: this.workId,
+      subject: this.subject
+    };
+    this.initData();
+  },
+  methods: {
+    async initData() {
+      await this.getParamsSetInfo();
+      await this.getWorkLevels();
+    },
+    async getParamsSetInfo() {
+      this.paramsSet = await getParamsSet(this.workId);
+    },
+    async getList() {
+      this.multipleGradingList = [];
+      const datas = {
+        ...this.filter,
+        ...this.typeFilter[this.curStep.type],
+        workId: this.workId,
+        page: this.current - 1,
+        size: this.size
+      };
+      if (this.curStep.type === "done") {
+        datas.level = this.curStep.name;
+        datas.sort = "updatedOn,desc";
+      }
+
+      const data = await markerTaskList(datas);
+      this.papers = data.data.map(paper => {
+        paper.title = `NO.${paper.sn}`;
+        paper.selected = false;
+        return paper;
+      });
+      this.total = data.totalCount;
+      this.totalPage = data.pageCount;
+    },
+    async toPage(page) {
+      this.current = page;
+      await this.getList();
+      this.selectPaper(this.curPaperIndex);
+    },
+    async getStepLevels() {
+      const data = await markerLevelTotalStatData(
+        this.filter.markerId,
+        this.filter.questionId
+      );
+      const undoIndex = data.findIndex(item => item.id === null);
+      let otherStep = [];
+      let undo = {
+        count: 0,
+        rejected: 0
+      };
+      if (undoIndex !== -1) {
+        undo = { ...data[undoIndex] };
+        data.splice(undoIndex, 1);
+      }
+
+      otherStep.push({
+        name: "待评",
+        count: undo.count,
+        type: "undo"
+      });
+      otherStep.push({
+        name: "打回",
+        count: undo.rejected,
+        type: "reject"
+      });
+      let levelStep = data.map(item => {
+        // 评卷员不展示kdpt
+        item.kdpt = null;
+        return {
+          ...item,
+          name: item.id,
+          type: "done"
+        };
+      });
+      this.steps = { levelStep, otherStep };
+
+      if (!this.curStep) {
+        let curStep = {};
+        if (undoIndex === -1) {
+          curStep = levelStep[0];
+        } else {
+          curStep = otherStep[0];
+        }
+        this.curStep = curStep;
+      }
+    },
+    updateStepLevel(curStep, curLevel, count) {
+      if (curStep.type === "done") {
+        const lpos = this.steps.levelStep.findIndex(
+          item => item.name === curStep.name
+        );
+        this.steps.levelStep[lpos].count -= count;
+      } else {
+        const opos = this.steps.otherStep.findIndex(
+          item => item.type === curStep.type
+        );
+        this.steps.otherStep[opos].count -= count;
+      }
+
+      const pos = this.steps.levelStep.findIndex(
+        item => item.name === curLevel
+      );
+      this.steps.levelStep[pos].count += count;
+
+      this.steps.levelStep.forEach(item => {
+        item.percent =
+          item.finalKdTotal && item.count
+            ? ((100 * item.count) / item.finalKdTotal).toFixed(3)
+            : 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
+        };
+      });
+    },
+    pageSizeChange(size) {
+      this.size = size;
+      this.toPage(1);
+      this.getStepLevels();
+    },
+    async stepChange(step) {
+      this.curStep = step;
+      // this.getStepLevels();
+      // this.toPage(1);
+      this.current = 1;
+      this.isFullscreenMarking = false;
+      await this.getList();
+      this.getStepLevels();
+      if (this.papers.length) {
+        this.selectPaper(0);
+      } else {
+        this.curPaper = {};
+      }
+    },
+    async areaChange(curArea) {
+      this.curArea = curArea;
+      this.filter.questionId = curArea.id;
+      await this.getStepLevels();
+      this.toPage(1);
+      // this.updateHistory();
+    },
+    // selectMultiplePaper
+    selectMultiplePaper(paper) {
+      if (paper.sample) return;
+      paper.selected = !paper.selected;
+      this.multipleGradingList = this.papers.filter(paper => paper.selected);
+    },
+    async multipleSelectLevel(level) {
+      if (!this.multipleGradingList.length) return;
+      if (this.multiplebtnClicked) return;
+      this.multiplebtnClicked = true;
+      const multipleGradingListCount = this.multipleGradingList.length;
+
+      let result = true;
+      const papers = await paperSelectLevelBatch(
+        this.multipleGradingList.map(item => item.id).join(), // is taskId
+        level.name,
+        "LEVEL"
+      ).catch(() => {
+        result = false;
+      });
+
+      this.multiplebtnClicked = false;
+      if (!result) return;
+
+      this.multipleGradingList = [];
+      // this.getStepLevels();
+      this.updateStepLevel(this.curStep, level.name, multipleGradingListCount);
+      this.updateHistory(papers);
+
+      // update paper list
+      if (
+        this.current > 1 &&
+        this.current === this.pageCount &&
+        this.papers.length === multipleGradingListCount
+      ) {
+        this.current--;
+      }
+
+      await this.getList();
+      this.selectPaper(this.curPaperIndex);
+    },
+    toCheck(paper) {
+      console.log(paper);
+    },
+    // paper view action
+    toReview(index) {
+      this.isFullscreenMarking = true;
+      this.multipleGradingList = [];
+      this.selectPaper(index);
+      this.$refs.SimpleImagePreview.open();
+    },
+    selectPaper(index) {
+      let nindex = index;
+      if (!this.papers.length) {
+        nindex = 0;
+      } else if (index > this.papers.length - 1) {
+        nindex = this.papers.length - 1;
+      } else if (index < 0) {
+        nindex = 0;
+      }
+      this.curPaperIndex = nindex;
+      this.curPaper = this.papers[nindex] ? { ...this.papers[nindex] } : {};
+    },
+    async toPrevPaper() {
+      if (this.curPaperIndex === 0) {
+        if (this.current > 1) {
+          this.current--;
+          this.curPaperIndex = this.size - 1;
+          await this.getList();
+        } else {
+          this.$Message.warning("当前已经是第一条数据了");
+          return;
+        }
+      } else {
+        this.curPaperIndex--;
+      }
+
+      this.selectPaper(this.curPaperIndex);
+    },
+    async toNextPaper() {
+      if (this.curPaperIndex === this.papers.length - 1) {
+        if (this.current === this.totalPage) {
+          this.$Message.warning("当前已经是最后一条数据了");
+          return;
+        } else {
+          this.current++;
+          this.curPaperIndex = 0;
+          await this.getList();
+        }
+      } else {
+        this.curPaperIndex++;
+      }
+
+      this.selectPaper(this.curPaperIndex);
+    },
+    async toActionNextPaper() {
+      if (this.current > 1 && this.papers.length === 1) {
+        this.current--;
+        this.curPaperIndex = this.size;
+      }
+
+      await this.getList();
+      if (!this.papers.length) this.$refs.SimpleImagePreview.cancel();
+      this.selectPaper(this.curPaperIndex);
+    },
+    async gradeCurPaper(level) {
+      const paper = await paperSelectLevelOrScore(
+        this.curPaper.id, // is taskId
+        level.name,
+        "LEVEL"
+      );
+      this.updateStepLevel(this.curStep, level.name, 1);
+      this.updateCacheHistory([paper]);
+      this.toActionNextPaper();
+    },
+    async passCurPaper() {
+      await paperTaskPass(this.curPaper.id);
+      this.toActionNextPaper();
+    },
+    updateHistory() {
+      this.$refs.GradeHistoryPaper.updatePapers();
+    },
+    updateCacheHistory(papers) {
+      this.$refs.GradeHistoryPaper.updateCachePapers(papers);
+    },
+    // paper carousel
+    toViewCarouselPaper(paperIndex, papers, type) {
+      this.carouselType = type;
+      this.isFullscreenMarking = true;
+      this.carouselPapers = papers;
+      this.selectCarouselPaper(paperIndex);
+      this.$nextTick(() => {
+        this.$refs.CarouselPapersPreview.open();
+      });
+    },
+    selectCarouselPaper(index) {
+      this.curCarouselPaperIndex = index;
+      this.curPaper = { ...this.carouselPapers[index] };
+    },
+    toCarousePaper(type) {
+      if (this.carouselType === "sample") {
+        this.toSampleCarousePaper(type);
+        return;
+      }
+      if (type === "prev" && this.curCarouselPaperIndex > 0) {
+        this.curCarouselPaperIndex--;
+      } else if (
+        type === "next" &&
+        this.curCarouselPaperIndex < this.carouselPapers.length - 1
+      ) {
+        this.curCarouselPaperIndex++;
+      }
+      this.selectCarouselPaper(this.curCarouselPaperIndex);
+    },
+    toSampleCarousePaper(type) {
+      if (type === "prev") {
+        this.$refs.GradeStandardPaper.$refs.PaperCarousel.handleLeft();
+      } else if (type === "next") {
+        this.$refs.GradeStandardPaper.$refs.PaperCarousel.handleRight();
+      }
+    },
+    carouseImagePreviewClose() {
+      this.isFullscreenMarking = false;
+      this.carouselType = "";
+      this.selectPaper(this.curPaperIndex);
+    },
+    standardPaperChange(curPaper) {
+      if (!this.isFullscreenMarking) return;
+      this.curPaper = { ...curPaper };
+    },
+    // header
+    toHistory() {
+      this.$refs.MarkerHistory.open();
+    },
+    toStandard() {
+      this.$refs.MarkerStandard.open();
+    }
+  }
+};
+</script>

+ 143 - 0
src/modules/grading/marker/MarkerHeader.vue

@@ -0,0 +1,143 @@
+<template>
+  <div class="marker-header">
+    <div class="header-group">
+      <div class="header-part">
+        <Dropdown
+          placement="bottom-end"
+          transfer
+          trigger="click"
+          @on-click="areaClick"
+        >
+          <span class="el-dropdown-link">
+            {{ curArea.areaName }} <Icon type="ios-arrow-down"></Icon>
+          </span>
+          <DropdownMenu slot="list">
+            <DropdownItem
+              v-for="area in areas"
+              :key="area.id"
+              :name="area.id"
+              >{{ area.areaName }}</DropdownItem
+            >
+          </DropdownMenu>
+        </Dropdown>
+      </div>
+      <div class="header-part">
+        <p>
+          {{ curStep.name }}:{{ curStep.count }}
+          <Icon type="ios-arrow-down"></Icon>
+        </p>
+      </div>
+      <div class="header-part"></div>
+    </div>
+    <div class="header-group">
+      <div class="header-part" @click="toStandard">
+        <p>标准卷 <Icon type="ios-arrow-down"></Icon></p>
+      </div>
+      <div class="header-part header-history" @click="toHistory">
+        <p>操作记录 <Icon type="ios-arrow-down"></Icon></p>
+      </div>
+      <div class="header-user">
+        <Dropdown
+          placement="bottom-end"
+          transfer
+          trigger="click"
+          @on-click="userClick"
+        >
+          <div class="user-name">
+            <Icon type="md-person" size="16" /> {{ username }}
+            <Icon type="ios-arrow-down"></Icon>
+          </div>
+          <DropdownMenu slot="list">
+            <DropdownItem class="color-default-hover" name="toResetPwd"
+              >修改密码</DropdownItem
+            >
+            <DropdownItem class="color-error-hover" divided name="logout">
+              退出登录
+            </DropdownItem>
+          </DropdownMenu>
+        </Dropdown>
+      </div>
+    </div>
+
+    <!-- ResetPwd -->
+    <reset-pwd ref="ResetPwd"></reset-pwd>
+  </div>
+</template>
+
+<script>
+import { areaList, logout } from "@/api";
+import ResetPwd from "@/modules/login/ResetPwd";
+
+export default {
+  name: "marker-header",
+  components: { ResetPwd },
+  props: {
+    data: {
+      type: Object,
+      default() {
+        return {
+          workId: "",
+          subject: ""
+        };
+      }
+    }
+  },
+  data() {
+    return {
+      username: this.$ls.get("user", { loginName: "" }).loginName,
+      areas: [],
+      curArea: { areaName: "校区" },
+      curStep: { name: "待评", count: 0 },
+      page: {
+        total: 10,
+        current: 0
+      }
+    };
+  },
+  mounted() {
+    this.getAreaList();
+  },
+  methods: {
+    async getAreaList() {
+      const data = await areaList({
+        workId: this.data.workId,
+        subject: this.data.subject
+      });
+      this.areas = data.map(item => {
+        return {
+          id: item.id,
+          areaName: item.areaName,
+          areaCode: item.areaCode
+        };
+      });
+
+      if (!this.curArea.id) {
+        this.curArea = this.areas[0];
+        this.$emit("area-change", this.curArea);
+      }
+    },
+    areaClick(val) {
+      this.curArea = this.areas.find(item => item.id === val);
+      this.$emit("area-change", this.curArea);
+    },
+    toHistory() {
+      this.$emit("to-history");
+    },
+    toStandard() {
+      this.$emit("to-standard");
+    },
+    userClick(name) {
+      if (!name) return;
+      this[name]();
+    },
+    toResetPwd() {
+      this.$refs.ResetPwd.open();
+    },
+    async logout() {
+      await logout();
+      this.$ls.clear();
+      this.$router.push({ name: "Login" });
+    }
+  }
+};
+</script>

+ 112 - 0
src/modules/grading/marker/MarkerHistory.vue

@@ -0,0 +1,112 @@
+<template>
+  <Modal
+    v-model="modalIsShow"
+    class="marker-history marker-modal"
+    title="操作记录"
+    footer-hide
+    :transition-names="['slide-up', 'fade']"
+    @on-visible-change="visibleChange"
+  >
+    <div class="history-image-list marker-image-list">
+      <div
+        v-for="(paper, index) in papers"
+        :key="paper.id"
+        class="marker-image-item"
+      >
+        <div class="marker-image-content">
+          <marker-image-view :data="paper" @to-review="toReview(index)">
+            <div class="image-info">
+              <div class="image-level">
+                {{ IS_MARK_LEADER ? paper.result : paper.level }}
+              </div>
+              <div class="image-action-name">{{ actionName }}</div>
+            </div>
+            <div class="image-sn">NO.{{ paper.sn }}</div>
+          </marker-image-view>
+        </div>
+      </div>
+    </div>
+  </Modal>
+</template>
+
+<script>
+import { actionHistory, actionLeaderHistory } from "@/api";
+import MarkerImageView from "./MarkerImageView";
+
+export default {
+  name: "marker-history",
+  props: {
+    questionId: {
+      type: [Number, String]
+    },
+    stage: {
+      type: String,
+      default: "LEVEL"
+    }
+  },
+  components: { MarkerImageView },
+  data() {
+    return {
+      modalIsShow: false,
+      workId: "",
+      subject: "",
+      subjectId: this.$route.params.subjectId,
+      userId: "",
+      curPaper: {},
+      papers: [],
+      IS_MARK_LEADER: false
+    };
+  },
+  computed: {
+    actionName() {
+      return this.IS_MARK_LEADER ? "一键定档" : "已评";
+    }
+  },
+  mounted() {
+    this.userId = this.$ls.get("user", { id: "" }).id;
+    const curUserRoleType = this.$ls.get("user", { role: "" }).role;
+    this.IS_MARK_LEADER = curUserRoleType === "MARK_LEADER";
+    const subjectId = this.subjectId.split("-");
+    this.workId = subjectId[0];
+    this.subject = subjectId[1];
+  },
+  methods: {
+    visibleChange(visible) {
+      if (visible) {
+        this.updatePapers();
+      }
+    },
+    async updatePapers() {
+      let data = [];
+      if (this.IS_MARK_LEADER) {
+        const datas = {
+          workId: this.workId,
+          subject: this.subject,
+          questionId: this.questionId
+        };
+
+        data = await actionLeaderHistory(datas);
+      } else {
+        const datas = {
+          markerId: this.userId,
+          stage: this.stage,
+          questionId: this.questionId
+        };
+
+        data = await actionHistory(datas);
+      }
+
+      this.papers = data.reverse();
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    toReview(index) {
+      this.$emit("on-paper-click", index, this.papers);
+    }
+  }
+};
+</script>

+ 75 - 0
src/modules/grading/marker/MarkerImageView.vue

@@ -0,0 +1,75 @@
+<template>
+  <div class="marker-image-view">
+    <image-view-contain
+      ref="ImageViewContain"
+      :image="image"
+      @to-review="toReview"
+    ></image-view-contain>
+    <div class="image-view-footer">
+      <slot>
+        <div class="image-info">
+          <div v-if="image.level" class="image-level">{{ image.level }}</div>
+          <div
+            :class="['image-checkbox', { 'image-checked': image.checked }]"
+            @click="toCheck"
+          >
+            <Icon v-if="image.checked" type="md-checkmark" />
+          </div>
+          <div class="image-title" @click="toCheck">{{ image.title }}</div>
+        </div>
+        <div class="image-rotate" @click="toRotate">
+          <Icon type="md-refresh-circle" />
+        </div>
+      </slot>
+    </div>
+  </div>
+</template>
+
+<script>
+import ImageViewContain from "@/components/ImageViewContain";
+
+export default {
+  name: "marker-image-view",
+  components: { ImageViewContain },
+  props: {
+    data: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  created() {
+    this.image = this.$objAssign(this.initImage, this.data);
+  },
+  data() {
+    return {
+      initImage: {
+        id: "",
+        thumbSrc: "",
+        title: "",
+        level: "",
+        deg: 0,
+        checked: false
+      },
+      image: {}
+    };
+  },
+  methods: {
+    toReview() {
+      this.$emit("to-review", this.data);
+    },
+    toRotate() {
+      const image = this.image;
+      image.deg += 90;
+      if (image.deg === 360) image.deg = 0;
+
+      this.$refs.ImageViewContain.resizeImage(image.deg);
+    },
+    toCheck() {
+      this.image.checked = !this.image.checked;
+      this.$emit("to-check", this.image);
+    }
+  }
+};
+</script>

+ 138 - 0
src/modules/grading/marker/MarkerStandard.vue

@@ -0,0 +1,138 @@
+<template>
+  <Modal
+    v-model="modalIsShow"
+    class="marker-standard marker-modal"
+    title="标准卷"
+    footer-hide
+    fullscreen
+    @on-visible-change="visibleChange"
+  >
+    <div class="level-list">
+      <div
+        v-for="(level, index) in levels"
+        :key="index"
+        :class="['level-item', { 'level-item-act': curLevelIndex === index }]"
+        @click="setCurLevel(index)"
+      >
+        {{ level.name }}
+      </div>
+    </div>
+
+    <div class="standard-image-list marker-image-list" v-if="papers.length">
+      <div
+        v-for="(paper, index) in papers"
+        :key="paper.id"
+        class="marker-image-item"
+      >
+        <div class="marker-image-content">
+          <marker-image-view :data="paper" @to-review="toReview(index)">
+            <div class="image-info">
+              <div class="image-level">
+                {{ curLevel }}
+              </div>
+            </div>
+            <div class="image-sn">NO.{{ paper.sn }}</div>
+          </marker-image-view>
+        </div>
+      </div>
+    </div>
+    <div v-else class="standard-image-none">
+      <p>暂无数据</p>
+    </div>
+  </Modal>
+</template>
+
+<script>
+import { paperList } from "@/api";
+import MarkerImageView from "./MarkerImageView";
+
+export default {
+  name: "marker-standard",
+  props: {
+    questionId: {
+      type: [Number, String]
+    },
+    levels: {
+      type: Array,
+      default() {
+        return [];
+      }
+    }
+  },
+  components: { MarkerImageView },
+  data() {
+    return {
+      modalIsShow: false,
+      curLevel: "",
+      curLevelIndex: 0,
+      papers: [],
+      paperMap: {},
+      isLoading: false
+    };
+  },
+  computed: {
+    actionName() {
+      return this.IS_MARK_LEADER ? "一键定档" : "已评";
+    }
+  },
+  methods: {
+    visibleChange(visible) {
+      if (visible) {
+        this.curLevel = this.levels[this.curLevelIndex].name;
+        this.updatePapers();
+      }
+    },
+    async updatePapers() {
+      if (this.paperMap[this.curLevel] && this.paperMap[this.curLevel].length) {
+        this.papers = this.paperMap[this.curLevel];
+        return;
+      }
+      this.isLoading = true;
+      const datas = {
+        questionId: this.questionId,
+        level: this.curLevel,
+        sort: "secretNumber",
+        isSample: true,
+        page: 0,
+        size: 100
+      };
+      const data = await paperList(datas).catch(() => {});
+      this.isLoading = false;
+      if (!data) return;
+      this.papers = data.data;
+      this.paperMap[this.curLevel] = data.data;
+    },
+    async updateLevelPapers(level) {
+      const datas = {
+        questionId: this.questionId,
+        level: this.curLevel,
+        sort: "secretNumber",
+        isSample: true,
+        page: 0,
+        size: 100
+      };
+      const data = await paperList(datas).catch(() => {});
+      this.isLoading = false;
+      if (!data) return;
+      this.paperMap[this.curLevel] = data.data;
+      if (this.curLevel === level) {
+        this.papers = data.data;
+      }
+    },
+    setCurLevel(index) {
+      this.curLevel = this.levels[index].name;
+      this.curLevelIndex = index;
+      this.updatePapers();
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    toReview(index) {
+      this.$emit("on-paper-click", index, this.papers);
+    }
+  }
+};
+</script>

+ 1 - 1
src/modules/login/LoginHome.vue

@@ -87,7 +87,7 @@ export default {
           router: "WorkManage"
           router: "WorkManage"
         },
         },
         MARKER_LEVEL: {
         MARKER_LEVEL: {
-          router: "GradingOperation"
+          router: "MarkerGrading"
         },
         },
         MARKER_SCORE: {
         MARKER_SCORE: {
           router: "MarkOperation"
           router: "MarkOperation"

+ 9 - 0
src/routers/grading.js

@@ -10,6 +10,7 @@ import GradingStandardPaperManage from "../modules/grading/GradingStandardPaperM
 
 
 // 阅卷员分档
 // 阅卷员分档
 import GradingOperation from "../modules/grading/GradingOperation";
 import GradingOperation from "../modules/grading/GradingOperation";
+import MarkerGrading from "../modules/grading/marker/MarkerGrading";
 
 
 const gradingRoutes = [
 const gradingRoutes = [
   {
   {
@@ -79,6 +80,14 @@ export default [
     redirect: { name: "GradingProgress" },
     redirect: { name: "GradingProgress" },
     children: gradingRoutes
     children: gradingRoutes
   },
   },
+  {
+    path: "/marker-grading/:workId(\\d+)/:subjectId",
+    name: "MarkerGrading",
+    component: MarkerGrading,
+    meta: {
+      title: "阅卷员分档"
+    }
+  },
   {
   {
     path: "/grading-operation/:workId(\\d+)/:subjectId",
     path: "/grading-operation/:workId(\\d+)/:subjectId",
     name: "GradingOperation",
     name: "GradingOperation",

+ 9 - 9
yarn.lock

@@ -1586,10 +1586,10 @@ async-limiter@~1.0.0:
   resolved "https://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.1.tgz?cache=0&sync_timestamp=1574272018408&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-limiter%2Fdownload%2Fasync-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
   resolved "https://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.1.tgz?cache=0&sync_timestamp=1574272018408&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-limiter%2Fdownload%2Fasync-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
   integrity sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=
   integrity sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=
 
 
-async-validator@^1.10.0:
-  version "1.12.2"
-  resolved "https://registry.npm.taobao.org/async-validator/download/async-validator-1.12.2.tgz#beae671e7174d2938b7b4b69d2fb7e722b7fd72c"
-  integrity sha1-vq5nHnF00pOLe0tp0vt+cit/1yw=
+async-validator@^3.3.0:
+  version "3.5.2"
+  resolved "https://registry.nlark.com/async-validator/download/async-validator-3.5.2.tgz?cache=0&sync_timestamp=1630393210134&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fasync-validator%2Fdownload%2Fasync-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500"
+  integrity sha1-aOhmqWgk6LJpT/eoMcGiXETV5QA=
 
 
 async@^2.6.2:
 async@^2.6.2:
   version "2.6.3"
   version "2.6.3"
@@ -8510,12 +8510,12 @@ verror@1.10.0:
     core-util-is "1.0.2"
     core-util-is "1.0.2"
     extsprintf "^1.2.0"
     extsprintf "^1.2.0"
 
 
-view-design@^4.2.0:
-  version "4.2.0"
-  resolved "https://registry.npm.taobao.org/view-design/download/view-design-4.2.0.tgz?cache=0&sync_timestamp=1586769708173&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fview-design%2Fdownload%2Fview-design-4.2.0.tgz#2af4865c956be84b64b25187be073016eab9a748"
-  integrity sha1-KvSGXJVr6EtkslGHvgcwFuq5p0g=
+view-design@4.6.0:
+  version "4.6.0"
+  resolved "https://registry.nlark.com/view-design/download/view-design-4.6.0.tgz#7e0c2e644ea5fdc34fd3ca42b79660c7f07e7878"
+  integrity sha1-fgwuZE6l/cNP08pCt5Zgx/B+eHg=
   dependencies:
   dependencies:
-    async-validator "^1.10.0"
+    async-validator "^3.3.0"
     deepmerge "^2.2.1"
     deepmerge "^2.2.1"
     element-resize-detector "^1.2.0"
     element-resize-detector "^1.2.0"
     js-calendar "^1.2.3"
     js-calendar "^1.2.3"