Pārlūkot izejas kodu

grading and mark

zhangjie 5 gadi atpakaļ
vecāks
revīzija
0888f08b72

+ 1 - 1
.gitignore

@@ -1,5 +1,5 @@
 .DS_Store
-node_modules
+node_modules*
 /dist
 dev-proxy.js
 

+ 17 - 0
src/api.js

@@ -213,3 +213,20 @@ export const createGradingTask = datas => {
   // TODO:
   return $post(`/api/marksubjects/${datas.subjectId}/markergroups`, datas);
 };
+
+// mark -------------------------->
+// mark-progress
+export const markProgressDetail = subjectId => {
+  // TODO:
+  return $get(`/api/marksubjects/${subjectId}/markergroups`, {});
+};
+
+// mark-task-manage
+export const markTaskInfo = subjectId => {
+  // TODO:
+  return $get(`/api/marksubjects/${subjectId}/markergroups`, {});
+};
+export const createMarkTask = (subjectId, datas) => {
+  // TODO:
+  return $post(`/api/marksubjects/${subjectId}/markergroups`, datas);
+};

+ 4 - 2
src/assets/styles/home.less

@@ -4,7 +4,6 @@
   width: 100%;
   height: 100%;
   z-index: auto;
-  min-width: 1200px;
 }
 .home-body {
   position: absolute;
@@ -20,7 +19,10 @@
   position: relative;
   padding: 20px 20px 60px;
   min-height: 100%;
-  min-width: 1200px;
+
+  &-nofooter {
+    padding-bottom: 0;
+  }
 }
 
 /* view-footer */

+ 6 - 2
src/assets/styles/main.less

@@ -115,8 +115,7 @@
     margin-top: 10px;
   }
 }
-.image-action-list {
-  padding: 15px 0;
+.image-view-list {
   font-size: 0;
   .image-view {
     width: 20%;
@@ -142,6 +141,11 @@
       width: 33.33%;
     }
   }
+  &-2 {
+    .image-view {
+      width: 50%;
+    }
+  }
 }
 
 // client-param-set

+ 228 - 0
src/assets/styles/mark.less

@@ -93,3 +93,231 @@
     border-bottom-right-radius: 5px;
   }
 }
+
+// grade-step
+.grade-step {
+  position: fixed;
+  top: 50px;
+  left: 0;
+  width: 100%;
+  z-index: 99;
+  height: 70px;
+  overflow: auto;
+  .table {
+    background-color: #fff;
+    margin: 0;
+    td {
+      padding: 8px 2px;
+      cursor: pointer;
+      &:hover {
+        color: @mainColor;
+      }
+      &.step-analysis {
+        width: 60px;
+        font-size: 30px;
+      }
+      &.step-act {
+        border-color: @mainColor;
+        background-color: @mainColor;
+        color: #fff;
+      }
+    }
+  }
+  .step-item {
+    font-size: 12px;
+    line-height: 1;
+    .step-name {
+      font-size: 16px;
+      font-weight: 600;
+      margin: 6px 0;
+    }
+  }
+}
+
+// grading-detail
+.grading-detail {
+  .detail-body {
+    margin: -20px -20px 0;
+    padding-top: 70px;
+    min-height: 500px;
+
+    &-2 {
+      .detail-standard {
+        display: none;
+      }
+      .detail-paper {
+        margin-left: 0;
+      }
+    }
+  }
+  .detail-carousel {
+    position: fixed;
+    top: 120px;
+    left: 0;
+    bottom: 0;
+    width: 300px;
+    padding: 15px 0;
+    // background-color: #fff;
+    z-index: 98;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+  .detail-action {
+    position: fixed;
+    top: 120px;
+    right: 0;
+    bottom: 0;
+    z-index: 98;
+    width: 300px;
+    padding: 15px;
+    background-color: #fff;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+  .detail-paper {
+    margin: 0 300px;
+    padding: 15px 0;
+    .ivu-page {
+      text-align: center;
+    }
+  }
+  .detail-filter {
+    text-align: center;
+  }
+  &-image-preview {
+    .ivu-modal-mask,
+    .ivu-modal-wrap {
+      left: 0;
+      bottom: 0;
+      top: 120px;
+      right: 300px;
+    }
+  }
+}
+
+// .paper-carousel
+.paper-carousel {
+  width: 300px;
+
+  .carousel-title {
+    text-align: center;
+    margin-bottom: 10px;
+    > h3 {
+      font-size: 16px;
+    }
+  }
+}
+// grade-standard-paper
+.grade-standard-paper {
+  .carousel-title {
+    > h3 {
+      display: inline-block;
+      vertical-align: middle;
+      margin-right: 15px;
+    }
+    .ivu-select {
+      width: 80px;
+    }
+  }
+}
+
+// .grade-action
+.grade-action {
+  text-align: center;
+
+  .action-search {
+    margin-bottom: 15px;
+    text-align: left;
+  }
+
+  .action-paper-info {
+    font-size: 16px;
+    line-height: 30px;
+    margin-bottom: 15px;
+  }
+
+  .action-grade-info {
+    margin-bottom: 15px;
+    .grade-info-name {
+      width: 60px;
+      height: 60px;
+      margin: 0 auto;
+      font-size: 44px;
+      line-height: 60px;
+      text-align: center;
+      background-color: @mainColor;
+      border-radius: 50%;
+      color: #fff;
+    }
+    .grade-info-range {
+      font-size: 16px;
+
+      > span {
+        margin: 0 2px;
+      }
+    }
+  }
+  .action-grade-list {
+    font-size: 0;
+    &-item {
+      display: inline-block;
+      vertical-align: top;
+      width: 33.33%;
+      padding: 10px;
+      font-size: 14px;
+
+      > p {
+        font-size: 16px;
+        &:first-child {
+          font-size: 22px;
+        }
+      }
+    }
+  }
+  .action-grade-history {
+    > h3 {
+      font-size: 16px;
+    }
+  }
+}
+.mark-action {
+  .mark-info {
+    font-size: 30px;
+  }
+  .action-mark-list {
+    .action-grade-list-item {
+      padding: 5px;
+      width: 20%;
+      > p {
+        margin: 0 auto;
+        width: 40px;
+        height: 40px;
+        line-height: 40px;
+        border-radius: 50%;
+        background-color: #e0e0e0;
+        cursor: pointer;
+        &:hover {
+          background-color: @mainColor;
+          color: #fff;
+        }
+      }
+    }
+  }
+}
+
+// .grade-analysis
+.grade-analysis {
+  width: 900px;
+  margin: 15px auto;
+}
+
+// mark-task
+.mark-task {
+  &-action {
+    text-align: center;
+
+    > span {
+      margin-right: 15px;
+    }
+  }
+}

+ 8 - 3
src/constants/enumerate.js

@@ -52,9 +52,9 @@ export const LEVEL_TYPE = {
 
 // student score
 export const CODE_TYPE = {
-  0: "考号",
-  1: "试卷密号",
-  2: "任务密号"
+  examNumber: "考号",
+  paper: "试卷密号",
+  task: "任务密号"
 };
 
 // role type
@@ -68,3 +68,8 @@ export const MARKER_RIGHT_TYPE = {
   ALLOW_SCORING: "只允许打分",
   ALLOW_ALL: "允许分档打分"
 };
+
+export const SORT_TYPE = {
+  0: "乱序",
+  1: "定序"
+};

+ 2 - 4
src/modules/grading/Grading.vue

@@ -19,10 +19,8 @@
     </div>
 
     <div class="home-body">
-      <div class="home-main">
+      <div class="home-main home-main-nofooter">
         <router-view />
-
-        <view-footer></view-footer>
       </div>
     </div>
   </div>
@@ -41,7 +39,7 @@ export default {
       subjectId: this.$route.params.subjectId,
       navs: [],
       curNav: {},
-      curSubject: { name: "灭是" }
+      curSubject: { name: "色彩" }
     };
   },
   mounted() {

+ 202 - 3
src/modules/grading/GradingDetail.vue

@@ -1,15 +1,214 @@
 <template>
   <div class="grading-detail">
-    grading-detail
+    <grade-step @on-change="stepChange"></grade-step>
+    <div class="detail-body" v-if="curStep.name !== 'analysis'">
+      <div class="detail-part detail-carousel">
+        <grade-standard-paper ref="GradeStandardPaper"></grade-standard-paper>
+        <paper-carousel
+          :papers="papers"
+          v-if="curUserRoleType !== 'ADMIN'"
+        ></paper-carousel>
+      </div>
+      <div class="detail-part detail-paper">
+        <div class="detail-filter">
+          <Form ref="FilterForm" label-position="left" inline>
+            <FormItem>
+              <Select v-model="filter.areaCode" placeholder="请选择考区">
+                <Option value=""></Option>
+              </Select>
+            </FormItem>
+            <FormItem>
+              <Button type="primary" icon="ios-search" @click="toPage(1)"
+                >查询</Button
+              >
+            </FormItem>
+          </Form>
+        </div>
+        <div class="detail-paper-list image-view-list image-view-list-3">
+          <div class="image-view" v-for="(image, index) in papers" :key="index">
+            <h5 class="image-view-title">{{ image.title }}</h5>
+            <div class="image-view-contain" @click="toReview(index)">
+              <img :src="image.thumbUrl" :alt="image.title" />
+            </div>
+          </div>
+        </div>
+        <div class="part-page">
+          <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>
+      </div>
+      <div class="detail-part detail-action">
+        <grade-action
+          :paper="curPage"
+          @on-confirm="gradeCurPaper"
+          ref="GradeAction"
+        ></grade-action>
+      </div>
+    </div>
+    <div class="detail-body" v-else>
+      <grade-analysis ref="GradeAnalysis"></grade-analysis>
+    </div>
+
+    <!-- image-preview -->
+    <image-preview
+      class="grading-detail-image-preview"
+      :image-list="papers"
+      :init-index="curPaperIndex"
+      @on-prev="paperPrev"
+      @on-next="paperNext"
+      @page-prev="prevPage"
+      @page-next="nextPage"
+      header-hide
+      ref="ImagePreview"
+      v-if="papers.length"
+    ></image-preview>
   </div>
 </template>
 
 <script>
+import { paperPageList } from "@/api";
+import ImagePreview from "@/components/common/ImagePreview";
+import GradeStep from "./components/GradeStep";
+import GradeStandardPaper from "./components/GradeStandardPaper";
+import PaperCarousel from "./components/PaperCarousel";
+import GradeAction from "./components/GradeAction";
+import GradeAnalysis from "./components/GradeAnalysis";
+// 三种情况:
+// 管理员(ADMIN),科组长(MARK_LEADER),评卷员(MARKER)
+
 export default {
   name: "grading-detail",
+  components: {
+    ImagePreview,
+    PaperCarousel,
+    GradeStep,
+    GradeStandardPaper,
+    GradeAction,
+    GradeAnalysis
+  },
   data() {
-    return {};
+    return {
+      filter: {
+        workId: this.$route.params.workId,
+        subjectId: this.$route.params.subjectId,
+        areaCode: ""
+      },
+      curUserRoleType: "",
+      current: 1,
+      size: 6,
+      total: 0,
+      totalPage: 1,
+      curStep: {},
+      curStandardGradeId: "",
+      grades: [],
+      papers: [],
+      curPage: {},
+      curPaperIndex: 0
+    };
   },
-  methods: {}
+  mounted() {
+    this.curUserRoleType = this.$ls.get("user", { role: "" }).role;
+    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",
+            thumbUrl:
+              "http://127.0.0.1:9000/api/file/image/download/33/1/833/2?random=497cc903-c01a-458a-9b4e-82b391cef176"
+          };
+        });
+    },
+    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,
+          thumbUrl: paper.thumbSrc,
+          missing: paper.missing,
+          stage: paper.stage,
+          style: {},
+          deg: 0
+        };
+      });
+      this.total = data.totalCount;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    pageSizeChange(size) {
+      this.size = size;
+      this.toPage(1);
+    },
+    stepChange(step) {
+      this.curStep = step;
+    },
+    toReview(index) {
+      this.curPage = { ...this.papers[index] };
+      this.curPaperIndex = index;
+      this.$refs.ImagePreview.open();
+    },
+    paperPrev(index) {
+      this.curPage = { ...this.papers[index] };
+      this.curPaperIndex = index;
+    },
+    paperNext(index) {
+      this.curPage = { ...this.papers[index] };
+      this.curPaperIndex = index;
+    },
+    prevPage() {
+      if (this.current === 1) {
+        this.$Message.warning("当前已经是第一条数据了");
+        return;
+      }
+      this.current--;
+      this.getList();
+    },
+    nextPage() {
+      if (this.current === this.totalPage) {
+        this.$Message.warning("当前已经是最后一条数据了");
+        return;
+      }
+      this.current++;
+      this.getList();
+    },
+    gradeCurPaper(grade) {
+      // TODO:to grade
+
+      if (this.curPaperIndex === this.size - 1) {
+        this.nextPage();
+        return;
+      } else {
+        this.paperNext(this.curPaperIndex + 1);
+      }
+    }
+  }
 };
 </script>

+ 118 - 0
src/modules/grading/components/GradeAction.vue

@@ -0,0 +1,118 @@
+<template>
+  <div class="grade-action">
+    <div class="action-search">
+      <Select
+        v-model="filter.codeType"
+        placeholder="请选择密号类型"
+        style="margin-bottom: 10px;"
+      >
+        <Option
+          v-for="(val, key) in CODE_TYPE"
+          :key="key"
+          :value="key"
+          :label="val"
+        ></Option>
+      </Select>
+      <Input v-model.trim="filter.code" placeholder="请输入号码">
+        <Button type="primary" @click="search" slot="append">查询</Button>
+      </Input>
+    </div>
+
+    <div class="action-paper-info">
+      <p>{{ curPaper.examNumber }}</p>
+      <p>No.{{ curPaper.sn }}</p>
+    </div>
+
+    <div class="action-grade-info">
+      <h3 class="grade-info-name">{{ curGrade.name }}</h3>
+      <p class="grade-info-range">
+        <span>{{ curGrade.range[0] }}</span>
+        <span>~</span>
+        <span>{{ curGrade.range[1] }}</span>
+      </p>
+    </div>
+    <div class="action-grade-list">
+      <div
+        class="action-grade-list-item"
+        v-for="(grade, index) in grades"
+        :key="index"
+      >
+        <p>{{ grade.name }}</p>
+        <p>{{ grade.range[0] }}~{{ grade.range[1] }}</p>
+      </div>
+    </div>
+
+    <div class="action-grade-history">
+      <h3>评卷记录</h3>
+      <div class="action-grade-list grade-history-list">
+        <div
+          class="action-grade-list-item"
+          v-for="(his, hindex) in gradingHistory"
+          :key="hindex"
+        >
+          <p>{{ his.name }}</p>
+          <p>{{ his.value }}</p>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { CODE_TYPE } from "@/constants/enumerate";
+
+export default {
+  name: "grade-action",
+  props: {
+    paper: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    return {
+      filter: {
+        codeType: "",
+        code: ""
+      },
+      CODE_TYPE,
+      curPaper: {
+        examNumber: "2020105133",
+        sn: "2020105133"
+      },
+      gradingHistory: [],
+      grades: [],
+      curGrade: {
+        name: "A",
+        range: [95, 100]
+      }
+    };
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    initData() {
+      this.grades = "ABCDEFGHIJK".split("").map((item, index) => {
+        return {
+          id: index,
+          name: item,
+          range: [10, 50]
+        };
+      });
+      this.gradingHistory = "ABCDEFGHIJK".split("").map((item, index) => {
+        return {
+          id: index,
+          name: "张珊",
+          value: item
+        };
+      });
+    },
+    search() {
+      this.$emit("code-search", this.filter);
+    }
+  }
+};
+</script>

+ 58 - 0
src/modules/grading/components/GradeAnalysis.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="grade-analysis part-box">
+    <div class="analysis-part">
+      <v-chart :options="barOption" v-if="barOption" autoresize></v-chart>
+    </div>
+  </div>
+</template>
+
+<script>
+import chartOption from "@/constants/chartOptions";
+
+export default {
+  name: "grade-analysis",
+  data() {
+    return {
+      barOption: {}
+    };
+  },
+  mounted() {
+    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() {
+      const data = [
+        {
+          data: [{ prop: 37.5, markerId: 98, markerName: "pj001" }],
+          name: "A"
+        },
+        { data: [{ prop: 62.5, markerId: 98, markerName: "pj001" }], name: "B" }
+      ];
+      this.barOption = chartOption.getBarGroupOption(
+        this.parseGroupBarData(data),
+        "档位分布图"
+      );
+    }
+  }
+};
+</script>

+ 23 - 0
src/modules/grading/components/GradeImageList.vue

@@ -0,0 +1,23 @@
+<template>
+  <div class="grade-image-list">
+    grade-image-list
+  </div>
+</template>
+
+<script>
+export default {
+  name: "grade-image-list",
+  data() {
+    return {
+      filter: {
+        workId: this.$route.params.workId,
+        subjectId: this.$route.params.subjectId
+      },
+      current: 1,
+      size: 6,
+      total: 0
+    };
+  },
+  methods: {}
+};
+</script>

+ 65 - 0
src/modules/grading/components/GradeStandardPaper.vue

@@ -0,0 +1,65 @@
+<template>
+  <div class="grade-standard-paper paper-carousel">
+    <div class="carousel-title">
+      <h3>标准卷</h3>
+      <Select v-model="curStandardGradeId">
+        <Option
+          v-for="(grade, gindex) in grades"
+          :key="gindex"
+          :value="grade.id"
+          :label="grade.name"
+        ></Option>
+      </Select>
+    </div>
+    <div class="carousel-body">
+      <Carousel v-model="curPaperIndex" arrow="always" dots="none">
+        <CarouselItem v-for="(paper, pindex) in papers" :key="pindex">
+          <div class="image-view-contain">
+            <img :src="paper.thumbUrl" :alt="paper.title" />
+          </div>
+        </CarouselItem>
+      </Carousel>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "grade-standard-paper",
+  data() {
+    return {
+      curStandardGradeId: "",
+      grades: [],
+      papers: [],
+      curPaperIndex: 0
+    };
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    initData() {
+      this.papers = "####".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",
+          thumbUrl:
+            "http://127.0.0.1:9000/api/file/image/download/33/1/833/2?random=497cc903-c01a-458a-9b4e-82b391cef176"
+        };
+      });
+
+      this.grades = "ABCDEFGHIJK".split("").map((item, index) => {
+        return {
+          id: index,
+          name: item
+        };
+      });
+    }
+  }
+};
+</script>

+ 89 - 0
src/modules/grading/components/GradeStep.vue

@@ -0,0 +1,89 @@
+<template>
+  <div class="grade-step">
+    <table class="table">
+      <tr>
+        <td
+          v-if="showAnalysis"
+          :class="[
+            'step-analysis',
+            { 'step-act': curStep.name === 'analysis' }
+          ]"
+          @click="selectAnalysis"
+        >
+          <p><Icon type="md-stats" /></p>
+        </td>
+        <td
+          v-for="(step, sindex) in steps"
+          :key="sindex"
+          :class="['step-item', { 'step-act': curStep.name === step.name }]"
+          @click="selectStep(step)"
+        >
+          <p>
+            <span>{{ step.gcount }}/</span><span>{{ step.gpercent }}%</span
+            ><span>({{ step.pt }}%)</span>
+          </p>
+          <p class="step-name">{{ step.name }}</p>
+          <p>
+            <span>{{ step.count }}/</span><span>{{ step.percent }}%</span
+            ><span>({{ step.kdpt }}%)</span>
+          </p>
+        </td>
+      </tr>
+    </table>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "grade-step",
+  props: {
+    showAnalysis: {
+      type: Boolean,
+      default: true
+    },
+    curStepKey: {
+      type: String
+    },
+    steps2: {
+      type: Array,
+      default() {
+        return [];
+      }
+    }
+  },
+  data() {
+    return {
+      steps: [],
+      curStep: { name: "" }
+    };
+  },
+  watch: {
+    curStepKey(val) {
+      const curStep = this.steps.filter(step => step.name === val);
+      this.curStep = curStep || { name: "" };
+    }
+  },
+  mounted() {
+    this.steps = "ABCDEFGHIJK".split("").map(item => {
+      return {
+        name: item,
+        gcount: 100,
+        gpercent: 10.1,
+        pt: 12,
+        count: 100,
+        percent: 8.1,
+        kdpt: 10
+      };
+    });
+  },
+  methods: {
+    selectStep(step) {
+      this.curStep = { ...step };
+      this.$emit("on-change", this.curStep);
+    },
+    selectAnalysis() {
+      this.selectStep({ name: "analysis" });
+    }
+  }
+};
+</script>

+ 38 - 0
src/modules/grading/components/PaperCarousel.vue

@@ -0,0 +1,38 @@
+<template>
+  <div class="paper-carousel">
+    <div class="carousel-title">
+      <slot name="title">
+        <h3>No.123456789</h3>
+      </slot>
+    </div>
+    <div class="carousel-body">
+      <Carousel v-model="curPaperIndex" arrow="always" dots="none">
+        <CarouselItem v-for="(paper, pindex) in papers" :key="pindex">
+          <div class="image-view-contain">
+            <img :src="paper.thumbUrl" :alt="paper.title" />
+          </div>
+        </CarouselItem>
+      </Carousel>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "paper-carousel",
+  props: {
+    papers: {
+      type: Array,
+      default() {
+        return [];
+      }
+    }
+  },
+  data() {
+    return {
+      curPaperIndex: 0
+    };
+  },
+  methods: {}
+};
+</script>

+ 5 - 1
src/modules/main/components/ImageActionList.vue

@@ -78,7 +78,11 @@ export default {
   },
   computed: {
     classes() {
-      return ["image-action-list", `image-action-list-${this.columnNumber}`];
+      return [
+        "image-action-list",
+        "image-view-list",
+        `image-view-list-${this.columnNumber}`
+      ];
     },
     canRotate() {
       return this.actions.includes("rotate");

+ 209 - 4
src/modules/mark/MarkDetail.vue

@@ -1,15 +1,220 @@
 <template>
-  <div class="mark-detail">
-    mark-detail
+  <div class="mark-detail grading-detail">
+    <grade-step :show-analysis="false" @on-change="stepChange"></grade-step>
+    <div :class="bodyClasses">
+      <div class="detail-part detail-carousel">
+        <paper-carousel :papers="papers" ref="PaperCarousel"></paper-carousel>
+      </div>
+      <div class="detail-part detail-paper">
+        <div class="detail-filter">
+          <Form ref="FilterForm" label-position="left" inline>
+            <FormItem>
+              <Select v-model="filter.areaCode" placeholder="请选择考区">
+                <Option value=""></Option>
+              </Select>
+            </FormItem>
+            <FormItem>
+              <Button type="primary" icon="ios-search" @click="toPage(1)"
+                >查询</Button
+              >
+            </FormItem>
+          </Form>
+        </div>
+        <div :class="imageViewClasses">
+          <div class="image-view" v-for="(image, index) in papers" :key="index">
+            <h5 class="image-view-title">{{ image.title }}</h5>
+            <div class="image-view-contain" @click="toReview(index)">
+              <img :src="image.thumbUrl" :alt="image.title" />
+            </div>
+          </div>
+        </div>
+        <div class="part-page">
+          <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>
+      </div>
+      <div class="detail-part detail-action">
+        <mark-action
+          :paper="curPage"
+          @on-confirm="gradeCurPaper"
+          ref="GradeAction"
+        ></mark-action>
+      </div>
+    </div>
+
+    <!-- image-preview -->
+    <image-preview
+      class="grading-detail-image-preview"
+      :image-list="papers"
+      :init-index="curPaperIndex"
+      @on-prev="paperPrev"
+      @on-next="paperNext"
+      @page-prev="prevPage"
+      @page-next="nextPage"
+      header-hide
+      ref="ImagePreview"
+      v-if="papers.length"
+    ></image-preview>
   </div>
 </template>
 
 <script>
+import { paperPageList } from "@/api";
+import ImagePreview from "@/components/common/ImagePreview";
+import GradeStep from "../grading/components/GradeStep";
+import PaperCarousel from "../grading/components/PaperCarousel";
+import MarkAction from "./components/MarkAction";
+// 三种情况:
+// 管理员(ADMIN),科组长(MARK_LEADER),评卷员(MARKER)
+
 export default {
   name: "mark-detail",
+  components: {
+    ImagePreview,
+    GradeStep,
+    PaperCarousel,
+    MarkAction
+  },
   data() {
-    return {};
+    return {
+      filter: {
+        workId: this.$route.params.workId,
+        subjectId: this.$route.params.subjectId,
+        areaCode: ""
+      },
+      curUserRoleType: "MARKER",
+      current: 1,
+      size: 6,
+      total: 0,
+      totalPage: 1,
+      curStep: {},
+      curStandardGradeId: "",
+      grades: [],
+      papers: [],
+      curPage: {},
+      curPaperIndex: 0
+    };
   },
-  methods: {}
+  computed: {
+    bodyClasses() {
+      return [
+        "detail-body",
+        { "detail-body-2": this.curUserRoleType === "ADMIN" }
+      ];
+    },
+    imageViewClasses() {
+      return [
+        "detail-paper-list",
+        "image-view-list",
+        `image-view-list-${this.size / 2}`
+      ];
+    }
+  },
+  mounted() {
+    // 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",
+            thumbUrl:
+              "http://127.0.0.1:9000/api/file/image/download/33/1/833/2?random=497cc903-c01a-458a-9b4e-82b391cef176"
+          };
+        });
+    },
+    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,
+          thumbUrl: paper.thumbSrc,
+          missing: paper.missing,
+          stage: paper.stage,
+          style: {},
+          deg: 0
+        };
+      });
+      this.total = data.totalCount;
+    },
+    toPage(page) {
+      this.current = page;
+      // this.getList();
+      this.initData();
+    },
+    pageSizeChange(size) {
+      this.size = size;
+      this.toPage(1);
+    },
+    stepChange(step) {
+      this.curStep = step;
+    },
+    toReview(index) {
+      this.curPage = { ...this.papers[index] };
+      this.curPaperIndex = index;
+      this.$refs.ImagePreview.open();
+    },
+    paperPrev(index) {
+      this.curPage = { ...this.papers[index] };
+      this.curPaperIndex = index;
+    },
+    paperNext(index) {
+      this.curPage = { ...this.papers[index] };
+      this.curPaperIndex = index;
+    },
+    prevPage() {
+      if (this.current === 1) {
+        this.$Message.warning("当前已经是第一条数据了");
+        return;
+      }
+      this.current--;
+      this.getList();
+    },
+    nextPage() {
+      if (this.current === this.totalPage) {
+        this.$Message.warning("当前已经是最后一条数据了");
+        return;
+      }
+      this.current++;
+      this.getList();
+    },
+    gradeCurPaper(grade) {
+      // TODO:to grade
+
+      if (this.curPaperIndex === this.size - 1) {
+        this.nextPage();
+        return;
+      } else {
+        this.paperNext(this.curPaperIndex + 1);
+      }
+    }
+  }
 };
 </script>

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

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

+ 27 - 5
src/modules/mark/MarkHome.vue

@@ -2,6 +2,7 @@
   <div class="mark-home home">
     <div class="home-header">
       <view-header>
+        <h1 slot="logo">{{ curSubject.name }}</h1>
         <div class="home-navs" slot="info">
           <ul>
             <li
@@ -30,20 +31,41 @@
 <script>
 import { navs } from "@/routers/mark";
 import menuMixins from "@/components/homeMenuMixins";
+import { subjectDetail } from "@/api";
 
 export default {
   name: "mark-home",
   mixins: [menuMixins],
   data() {
     return {
-      navs,
-      curNav: {}
+      subjectId: this.$route.params.subjectId,
+      navs: [],
+      curNav: {},
+      curSubject: { name: "素描" }
     };
   },
   mounted() {
-    const router = this.$route.matched.filter(item => item.name !== "Mark")[0];
-    this.actCurNav(router);
+    this.buildNavs();
+    this.actSubNav();
+    this.getSubjectDetail();
   },
-  methods: {}
+  methods: {
+    async getSubjectDetail() {
+      this.curSubject = await subjectDetail(this.subjectId);
+    },
+    buildNavs() {
+      const user = this.$ls.get("user", {});
+      this.navs = navs;
+      if (user.role === "ADMIN") {
+        this.navs = [{ title: "总览", name: "WorkOverview" }, ...navs];
+      }
+    },
+    actSubNav() {
+      const router = this.$route.matched.filter(
+        item => item.name !== "Mark"
+      )[0];
+      this.actCurNav(router);
+    }
+  }
 };
 </script>

+ 112 - 3
src/modules/mark/MarkProgress.vue

@@ -1,15 +1,124 @@
 <template>
   <div class="mark-progress">
-    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>
   </div>
 </template>
 
 <script>
+import ProgressLine from "../grading/components/ProgressLine";
+import { markProgressDetail } from "@/api";
+
 export default {
   name: "mark-progress",
+  components: { ProgressLine },
   data() {
-    return {};
+    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
+        }
+      ]
+    };
   },
-  methods: {}
+  methods: {
+    async getData() {
+      const data = await markProgressDetail(this.subjectId);
+      console.log(data);
+    }
+  }
 };
 </script>

+ 115 - 3
src/modules/mark/MarkTaskManage.vue

@@ -1,15 +1,127 @@
 <template>
   <div class="mark-task-manage">
-    mark-task-manage
+    <div class="part-box-head mark-task-head">
+      <Select
+        v-model="areaCode"
+        placeholder="请选择考区"
+        style="width: 140px;margin-right: 10px;"
+      >
+        <Option value="">考区一</Option>
+      </Select>
+      <Button type="primary" icon="md-search" @click="getData">查询</Button>
+    </div>
+    <div class="part-box mark-task-body">
+      <table class="table">
+        <tr>
+          <th>档位</th>
+          <th>总数量</th>
+          <th>已评</th>
+          <th>未评</th>
+          <th>排序规则</th>
+          <th>是否显示档内序号</th>
+          <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>
+            <Select v-model="task.sortRule">
+              <Option
+                v-for="(val, key) in SORT_TYPE"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></Option>
+            </Select>
+          </td>
+          <td>
+            <Select v-model="task.showSerialNo">
+              <Option
+                v-for="(val, key) in BOOLEAN_TYPE"
+                :key="key"
+                :value="key"
+                :label="val"
+              ></Option>
+            </Select>
+          </td>
+          <td>
+            <InputNumber
+              v-model="task.taskCount"
+              :min="0"
+              :max="task.unfinished"
+              :precision="0"
+            ></InputNumber>
+          </td>
+        </tr>
+      </table>
+
+      <div class="mark-task-action">
+        <span>已选任务数量:{{ curTaskCount }}</span>
+        <Button type="primary" @click="submit" :disabled="isSubmit"
+          >发布评卷任务</Button
+        >
+      </div>
+    </div>
   </div>
 </template>
 
 <script>
+import { markTaskInfo, createMarkTask } from "@/api";
+import { calcSum } from "@/plugins/utils";
+import { SORT_TYPE, BOOLEAN_TYPE } from "@/constants/enumerate";
+
 export default {
   name: "mark-task-manage",
   data() {
-    return {};
+    return {
+      subjectId: this.$route.params.subjectId,
+      SORT_TYPE,
+      BOOLEAN_TYPE,
+      areaCode: "",
+      isSubmit: false,
+      taskList: []
+    };
+  },
+  computed: {
+    curTaskCount() {
+      let nums = this.taskList.map(item => item.taskCount);
+      return calcSum(nums);
+    }
+  },
+  mounted() {
+    this.initData();
   },
-  methods: {}
+  methods: {
+    initData() {
+      this.taskList = "ABCDEFGHIJK".split("").map(item => {
+        const sum = Math.floor(Math.random() * 1000);
+        const finished = Math.floor(sum * Math.random());
+        return {
+          grade: item,
+          sum,
+          finished,
+          unfinished: sum - finished,
+          sortRule: "0",
+          showSerialNo: "0",
+          taskCount: 0
+        };
+      });
+    },
+    async getData() {
+      const data = await markTaskInfo(this.subjectId);
+      console.log(data);
+    },
+    async submit() {
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const data = await createMarkTask(this.taskList).catch(() => {});
+      this.isSubmit = false;
+      if (!data) return;
+      this.$Message.success("任务创建成功!");
+      this.getData();
+    }
+  }
 };
 </script>

+ 119 - 0
src/modules/mark/components/MarkAction.vue

@@ -0,0 +1,119 @@
+<template>
+  <div class="mark-action grade-action">
+    <div class="action-search">
+      <Select
+        v-model="filter.codeType"
+        placeholder="请选择密号类型"
+        style="margin-bottom: 10px;"
+      >
+        <Option
+          v-for="(val, key) in CODE_TYPE"
+          :key="key"
+          :value="key"
+          :label="val"
+        ></Option>
+      </Select>
+      <Input v-model.trim="filter.code" placeholder="请输入号码">
+        <Button type="primary" @click="search" slot="append">查询</Button>
+      </Input>
+    </div>
+
+    <div class="action-paper-info">
+      <p>{{ curPaper.examNumber }}</p>
+      <p>No.{{ curPaper.sn }}</p>
+    </div>
+
+    <p class="mark-info">{{ curMark }}</p>
+
+    <div class="action-grade-info">
+      <h3 class="grade-info-name">{{ curGrade.name }}</h3>
+      <p class="grade-info-range">
+        <span>{{ curGrade.range[0] }}</span>
+        <span>~</span>
+        <span>{{ curGrade.range[1] }}</span>
+      </p>
+    </div>
+    <div class="action-grade-list action-mark-list">
+      <div
+        class="action-grade-list-item"
+        v-for="(grade, index) in grades"
+        :key="index"
+      >
+        <p>{{ grade }}</p>
+      </div>
+    </div>
+
+    <div class="action-grade-history">
+      <h3>评卷记录</h3>
+      <div class="action-grade-list grade-history-list">
+        <div
+          class="action-grade-list-item"
+          v-for="(his, hindex) in gradingHistory"
+          :key="hindex"
+        >
+          <p>{{ his.name }}</p>
+          <p>{{ his.value }}</p>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { CODE_TYPE } from "@/constants/enumerate";
+
+export default {
+  name: "mark-action",
+  props: {
+    paper: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    return {
+      filter: {
+        codeType: "",
+        code: ""
+      },
+      CODE_TYPE,
+      curPaper: {
+        examNumber: "2020105133",
+        sn: "2020105133"
+      },
+      gradingHistory: [],
+      grades: [],
+      curGrade: {
+        name: "A",
+        range: [5, 20]
+      },
+      curMark: 95
+    };
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    initData() {
+      let grades = [];
+      let [start, end] = this.curGrade.range;
+      for (let i = start; i <= end; i++) {
+        grades.push(i);
+      }
+      this.grades = grades;
+      this.gradingHistory = "ABCDEFGHIJK".split("").map((item, index) => {
+        return {
+          id: index,
+          name: "张珊",
+          value: item
+        };
+      });
+    },
+    search() {
+      this.$emit("code-search", this.filter);
+    }
+  }
+};
+</script>

+ 12 - 1
src/plugins/utils.js

@@ -209,6 +209,16 @@ function getNavs(routes) {
   });
 }
 
+/**
+ * 计算总数
+ * @param {Array} dataList 需要统计的数组
+ */
+function calcSum(dataList) {
+  return dataList.reduce(function(total, item) {
+    return total + item;
+  }, 0);
+}
+
 export {
   objTypeOf,
   deepCopy,
@@ -220,5 +230,6 @@ export {
   formatDate,
   removeHtmlTag,
   humpFormat,
-  getNavs
+  getNavs,
+  calcSum
 };

+ 11 - 11
src/routers/mark.js

@@ -19,19 +19,19 @@ const markRoutes = [
     }
   },
   {
-    path: "mark-detail",
-    name: "MarkDetail",
-    component: MarkDetail,
+    path: "mark-task-manage",
+    name: "MarkTaskManage",
+    component: MarkTaskManage,
     meta: {
-      title: "打分进度"
+      title: "任务发布"
     }
   },
   {
-    path: "mark-task-manage",
-    name: "MarkTaskManage",
-    component: MarkTaskManage,
+    path: "mark-detail",
+    name: "MarkDetail",
+    component: MarkDetail,
     meta: {
-      title: "打分进度"
+      title: "打分详情"
     }
   },
   {
@@ -39,7 +39,7 @@ const markRoutes = [
     name: "MarkGroupManage",
     component: MarkGroupManage,
     meta: {
-      title: "打分进度"
+      title: "打分分组"
     }
   }
 ];
@@ -48,14 +48,14 @@ export const navs = getNavs(markRoutes);
 
 export default [
   {
-    path: "/mark/:workId(\\d+)",
+    path: "/mark/:workId(\\d+)/:subjectId",
     name: "Mark",
     component: MarkHome,
     redirect: { name: "MarkProgress" },
     children: markRoutes
   },
   {
-    path: "/mark-operation/:workId(\\d+)",
+    path: "/mark-operation/:workId(\\d+)/:subjectId",
     name: "MarkOperation",
     component: MarkOperation,
     meta: {