浏览代码

接口调试

zhangjie 2 年之前
父节点
当前提交
ead1aa658d

+ 1 - 1
src/api.js

@@ -232,7 +232,7 @@ export const deleteGradingGroup = (subjectId, groupId) => {
   return $del(`/api/marksubjects/${subjectId}/markergroups/${groupId}`);
 };
 export const deleteGradeGroupStudent = (subject, groupId) => {
-  return $post(`/api/marksubjects/marker_groups_student/${subject}/${groupId}`);
+  return $del(`/api/marksubjects/marker_groups_student/${subject}/${groupId}`);
 };
 export const markUserList = ({ workId, subjectId }) => {
   return $get("/api/markers", { workId, subject: subjectId });

+ 439 - 436
src/assets/styles/base.less

@@ -1,436 +1,439 @@
-/* reset */
-body,
-div,
-ul,
-ol,
-li,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-input,
-p,
-tr,
-th,
-td,
-span,
-a,
-header,
-footer {
-  margin: 0;
-  padding: 0;
-  box-sizing: border-box;
-  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
-}
-li {
-  list-style: none;
-}
-em,
-i,
-u {
-  font-style: normal;
-}
-input {
-  outline: none;
-  border: none;
-  font-family: "Microsoft YaHei", Tahoma, Helvetica, Arial, sans-serif;
-  -webkit-appearance: none;
-}
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
-  font-size: 100%;
-  font-weight: normal;
-}
-fieldset,
-img {
-  border: 0;
-}
-abbr {
-  border: 0;
-  font-variant: normal;
-}
-a {
-  text-decoration: none;
-  color: inherit;
-  *color: #999;
-}
-img {
-  vertical-align: middle;
-}
-button {
-  outline: 0;
-}
-
-/* common-style */
-input,
-select {
-  -webkit-appearance: none;
-}
-
-/* browse style */
-::-webkit-scrollbar {
-  width: 8px;
-  height: 8px;
-  background: transparent;
-}
-::-webkit-scrollbar-button {
-  display: none;
-}
-::-webkit-scrollbar-track {
-  background: transparent;
-}
-::-webkit-scrollbar-thumb {
-  border-radius: 8px;
-  background: @dark-color-lighter;
-}
-::-webkit-scrollbar-corner {
-  background: transparent;
-}
-::-webkit-scrollbar-resizer {
-  background: transparent;
-}
-
-body {
-  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
-    "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  font-size: @font-size-base;
-  color: @font-color-main;
-  background-color: @background-color;
-}
-.page-container-flex {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  justify-content: space-between;
-}
-// box-justify
-.box-justify {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
-/* part */
-.part-box {
-  padding: 20px;
-  margin-bottom: 20px;
-  background-color: @white;
-  border-radius: @box-border-radius;
-
-  &-filter {
-    padding-bottom: 10px;
-  }
-
-  .ivu-form-item-label {
-    text-align: right;
-  }
-
-  &-head {
-    margin-bottom: 20px;
-    display: flex;
-    align-items: stretch;
-    justify-content: space-between;
-
-    &-right {
-      padding-bottom: 10px;
-      white-space: nowrap;
-      display: flex;
-      align-items: flex-end;
-    }
-  }
-}
-.part-title {
-  font-size: 18px;
-  font-weight: 600;
-  margin-bottom: 15px;
-  height: 32px;
-  line-height: 32px;
-}
-.part-title h2 {
-  float: left;
-  font-weight: 600;
-}
-.part-title-infos {
-  float: right;
-  > .ivu-btn:not(:first-child) {
-    margin-left: 5px;
-  }
-}
-.part-page {
-  margin-top: 15px;
-  text-align: center;
-}
-.part-filter {
-  border-bottom: 1px dashed #e0e0e0;
-  margin-bottom: 20px;
-}
-.part-none {
-  padding: 100px;
-  font-size: 20px;
-  color: @dark-color-lighter;
-  text-align: center;
-}
-.part-box-top {
-  margin-bottom: 10px;
-  text-align: right;
-}
-// page
-.page {
-  &-navs {
-    margin-bottom: 10px;
-  }
-  &-reload {
-    position: fixed;
-    width: 35px;
-    height: 35px;
-    top: 140px;
-    right: 10px;
-    z-index: 999;
-    border-radius: 50%;
-    background-color: @white;
-    box-shadow: 0 0 5px #999;
-    color: @dark-color-lighter;
-    font-size: 22px;
-    line-height: 32px;
-    text-align: center;
-    cursor: pointer;
-    > i {
-      vertical-align: middle;
-    }
-
-    &:hover {
-      background-color: @primary-color;
-      color: @white;
-    }
-  }
-}
-/* table */
-.table {
-  width: 100%;
-  border-spacing: 0 4px;
-  border-collapse: separate;
-  margin-bottom: 20px;
-  text-align: left;
-
-  tr {
-    background-color: @white;
-    color: @dark-color-light;
-    td,
-    th {
-      border: none;
-      padding: 8px;
-      line-height: 20px;
-      &:first-child {
-        border-radius: @box-border-radius-small 0 0 @box-border-radius-small;
-      }
-      &:last-child {
-        border-radius: 0 @box-border-radius-small @box-border-radius-small 0;
-      }
-    }
-    th {
-      padding: 13px 8px;
-      color: @dark-color;
-      font-weight: 600;
-    }
-  }
-  &.table-width {
-    td,
-    th {
-      padding-left: 18px;
-      padding-right: 18px;
-    }
-  }
-
-  &.table-dark {
-    tr {
-      background-color: @color-background-light;
-      td,
-      th {
-        color: @color-text;
-        padding: 8px 15px;
-      }
-      .td-nopad {
-        padding-left: 0;
-        padding-right: 0;
-      }
-
-      .td-bl {
-        border-left: 2px solid @color-background;
-      }
-    }
-  }
-}
-.table .td-th {
-  font-weight: 600;
-  color: @dark-color;
-}
-tr.row-active td {
-  color: @main-color;
-}
-tr.row-disabled td {
-  color: @disabled-color;
-}
-tr.tr-tips-error td {
-  padding: 2px 10px;
-  font-size: 13px;
-  color: @error-color;
-}
-
-/* list */
-.list-lr-right {
-  float: right;
-  width: 300px;
-}
-.list-lr-left {
-  margin-right: 320px;
-}
-
-/* user reset */
-h3.account-title {
-  text-align: center;
-  font-weight: 600;
-}
-.account-form {
-  width: 60%;
-  min-width: 600px;
-  margin: 50px auto;
-}
-.vlcode {
-  height: 36px;
-}
-.vlcode-left {
-  margin-right: 135px;
-}
-.vlcode-right {
-  float: right;
-  width: 120px;
-}
-
-// vue-echarts
-.echarts {
-  height: 100% !important;
-  width: 100% !important;
-}
-.chart-box {
-  height: 300px;
-  // box-shadow: 0 0 1px #e0e0e0;
-  background: #fff;
-  position: relative;
-}
-.chart-box .chart-none {
-  padding-top: 150px;
-  font-size: 20px;
-  text-align: center;
-  color: @dark-color-lighter;
-}
-.chart-box .chart-title {
-  margin: 0;
-  font-size: 18px;
-  font-weight: 600;
-  color: #333;
-  position: absolute;
-  top: 0;
-  left: 16px;
-  line-height: 30px;
-  z-index: 99;
-}
-
-// other
-.tips-info {
-  font-size: 14px;
-  height: 25px;
-  line-height: 25px;
-  color: @dark-color-lighter;
-}
-.tips-error {
-  color: @error-color;
-}
-
-.text-center {
-  text-align: center;
-}
-.btn-form-search {
-  width: 60px;
-}
-.btn-disabled {
-  cursor: not-allowed !important;
-}
-.color-primary {
-  color: @primary-color;
-}
-.color-dark {
-  color: @dark-color;
-}
-.color-error {
-  color: @error-color;
-}
-.color-default-hover {
-  &:hover {
-    color: fade(@primary-color, 80%);
-  }
-}
-.color-text-hover {
-  color: @dark-color-light;
-  &:hover {
-    color: @dark-color;
-  }
-}
-.color-error-hover {
-  color: @error-color;
-
-  &:hover {
-    color: fade(@error-color, 80%);
-  }
-}
-
-.clear-float {
-  &::after {
-    content: "";
-    display: block;
-    clear: both;
-    visibility: hidden;
-  }
-}
-.ml-1 {
-  margin-left: 5px;
-}
-.ml-2 {
-  margin-left: 10px;
-}
-.mr-1 {
-  margin-right: 5px;
-}
-.mr-2 {
-  margin-right: 10px;
-}
-.mr-4 {
-  margin-right: 20px;
-}
-.mb-0 {
-  margin-bottom: 0;
-}
-.mb-2 {
-  margin-bottom: 10px;
-}
-.mb-4 {
-  margin-bottom: 20px;
-}
-
-// border-randius
-.border-radius {
-  &-small {
-    border-radius: @box-border-radius-small;
-  }
-  &-large {
-    border-radius: @box-border-radius-large;
-  }
-}
+/* reset */
+body,
+div,
+ul,
+ol,
+li,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+input,
+p,
+tr,
+th,
+td,
+span,
+a,
+header,
+footer {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+}
+li {
+  list-style: none;
+}
+em,
+i,
+u {
+  font-style: normal;
+}
+input {
+  outline: none;
+  border: none;
+  font-family: "Microsoft YaHei", Tahoma, Helvetica, Arial, sans-serif;
+  -webkit-appearance: none;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-size: 100%;
+  font-weight: normal;
+}
+fieldset,
+img {
+  border: 0;
+}
+abbr {
+  border: 0;
+  font-variant: normal;
+}
+a {
+  text-decoration: none;
+  color: inherit;
+  *color: #999;
+}
+img {
+  vertical-align: middle;
+}
+button {
+  outline: 0;
+}
+
+/* common-style */
+input,
+select {
+  -webkit-appearance: none;
+}
+
+/* browse style */
+::-webkit-scrollbar {
+  width: 8px;
+  height: 8px;
+  background: transparent;
+}
+::-webkit-scrollbar-button {
+  display: none;
+}
+::-webkit-scrollbar-track {
+  background: transparent;
+}
+::-webkit-scrollbar-thumb {
+  border-radius: 8px;
+  background: @dark-color-lighter;
+}
+::-webkit-scrollbar-corner {
+  background: transparent;
+}
+::-webkit-scrollbar-resizer {
+  background: transparent;
+}
+
+body {
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
+    "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  font-size: @font-size-base;
+  color: @font-color-main;
+  background-color: @background-color;
+}
+.page-container-flex {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+// box-justify
+.box-justify {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+/* part */
+.part-box {
+  padding: 20px;
+  margin-bottom: 20px;
+  background-color: @white;
+  border-radius: @box-border-radius;
+
+  &-filter {
+    padding-bottom: 10px;
+  }
+
+  .ivu-form-item-label {
+    text-align: right;
+  }
+
+  &-head {
+    margin-bottom: 20px;
+    display: flex;
+    align-items: stretch;
+    justify-content: space-between;
+
+    &-right {
+      padding-bottom: 10px;
+      white-space: nowrap;
+      display: flex;
+      align-items: flex-end;
+    }
+  }
+}
+.part-title {
+  font-size: 18px;
+  font-weight: 600;
+  margin-bottom: 15px;
+  height: 32px;
+  line-height: 32px;
+}
+.part-title h2 {
+  float: left;
+  font-weight: 600;
+}
+.part-title-infos {
+  float: right;
+  > .ivu-btn:not(:first-child) {
+    margin-left: 5px;
+  }
+}
+.part-page {
+  margin-top: 15px;
+  text-align: center;
+}
+.part-filter {
+  border-bottom: 1px dashed #e0e0e0;
+  margin-bottom: 20px;
+}
+.part-none {
+  padding: 100px;
+  font-size: 20px;
+  color: @dark-color-lighter;
+  text-align: center;
+}
+.part-box-top {
+  margin-bottom: 10px;
+  text-align: right;
+}
+// page
+.page {
+  &-navs {
+    margin-bottom: 10px;
+  }
+  &-reload {
+    position: fixed;
+    width: 35px;
+    height: 35px;
+    top: 140px;
+    right: 10px;
+    z-index: 999;
+    border-radius: 50%;
+    background-color: @white;
+    box-shadow: 0 0 5px #999;
+    color: @dark-color-lighter;
+    font-size: 22px;
+    line-height: 32px;
+    text-align: center;
+    cursor: pointer;
+    > i {
+      vertical-align: middle;
+    }
+
+    &:hover {
+      background-color: @primary-color;
+      color: @white;
+    }
+  }
+}
+/* table */
+.table {
+  width: 100%;
+  border-spacing: 0 4px;
+  border-collapse: separate;
+  margin-bottom: 20px;
+  text-align: left;
+
+  tr {
+    background-color: @white;
+    color: @dark-color-light;
+    td,
+    th {
+      border: none;
+      padding: 8px;
+      line-height: 20px;
+      &:first-child {
+        border-radius: @box-border-radius-small 0 0 @box-border-radius-small;
+      }
+      &:last-child {
+        border-radius: 0 @box-border-radius-small @box-border-radius-small 0;
+      }
+    }
+    th {
+      padding: 13px 8px;
+      color: @dark-color;
+      font-weight: 600;
+    }
+  }
+  &.table-width {
+    td,
+    th {
+      padding-left: 18px;
+      padding-right: 18px;
+    }
+  }
+
+  &.table-dark {
+    tr {
+      background-color: @color-background-light;
+      td,
+      th {
+        color: @color-text;
+        padding: 8px 15px;
+      }
+      .td-nopad {
+        padding-left: 0;
+        padding-right: 0;
+      }
+
+      .td-bl {
+        border-left: 2px solid @color-background;
+      }
+    }
+  }
+}
+.table .td-th {
+  font-weight: 600;
+  color: @dark-color;
+}
+tr.row-active td {
+  color: @main-color;
+}
+tr.row-disabled td {
+  color: @disabled-color;
+}
+tr.tr-tips-error td {
+  padding: 2px 10px;
+  font-size: 13px;
+  color: @error-color;
+}
+
+/* list */
+.list-lr-right {
+  float: right;
+  width: 300px;
+}
+.list-lr-left {
+  margin-right: 320px;
+}
+
+/* user reset */
+h3.account-title {
+  text-align: center;
+  font-weight: 600;
+}
+.account-form {
+  width: 60%;
+  min-width: 600px;
+  margin: 50px auto;
+}
+.vlcode {
+  height: 36px;
+}
+.vlcode-left {
+  margin-right: 135px;
+}
+.vlcode-right {
+  float: right;
+  width: 120px;
+}
+
+// vue-echarts
+.echarts {
+  height: 100% !important;
+  width: 100% !important;
+}
+.chart-box {
+  height: 300px;
+  // box-shadow: 0 0 1px #e0e0e0;
+  background: #fff;
+  position: relative;
+}
+.chart-box .chart-none {
+  padding-top: 150px;
+  font-size: 20px;
+  text-align: center;
+  color: @dark-color-lighter;
+}
+.chart-box .chart-title {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 600;
+  color: #333;
+  position: absolute;
+  top: 0;
+  left: 16px;
+  line-height: 30px;
+  z-index: 99;
+}
+
+// other
+.tips-info {
+  font-size: 14px;
+  height: 25px;
+  line-height: 25px;
+  color: @dark-color-lighter;
+}
+.tips-error {
+  color: @error-color;
+}
+
+.text-center {
+  text-align: center;
+}
+.btn-form-search {
+  width: 60px;
+}
+.btn-disabled {
+  cursor: not-allowed !important;
+}
+.color-primary {
+  color: @primary-color;
+}
+.color-success {
+  color: @success-color;
+}
+.color-dark {
+  color: @dark-color;
+}
+.color-error {
+  color: @error-color;
+}
+.color-default-hover {
+  &:hover {
+    color: fade(@primary-color, 80%);
+  }
+}
+.color-text-hover {
+  color: @dark-color-light;
+  &:hover {
+    color: @dark-color;
+  }
+}
+.color-error-hover {
+  color: @error-color;
+
+  &:hover {
+    color: fade(@error-color, 80%);
+  }
+}
+
+.clear-float {
+  &::after {
+    content: "";
+    display: block;
+    clear: both;
+    visibility: hidden;
+  }
+}
+.ml-1 {
+  margin-left: 5px;
+}
+.ml-2 {
+  margin-left: 10px;
+}
+.mr-1 {
+  margin-right: 5px;
+}
+.mr-2 {
+  margin-right: 10px;
+}
+.mr-4 {
+  margin-right: 20px;
+}
+.mb-0 {
+  margin-bottom: 0;
+}
+.mb-2 {
+  margin-bottom: 10px;
+}
+.mb-4 {
+  margin-bottom: 20px;
+}
+
+// border-randius
+.border-radius {
+  &-small {
+    border-radius: @box-border-radius-small;
+  }
+  &-large {
+    border-radius: @box-border-radius-large;
+  }
+}

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

@@ -187,6 +187,16 @@
     flex-grow: 2;
   }
 }
+.point-tag {
+  width: 16px;
+  height: 16px;
+  border-radius: 50%;
+  background-color: @color-text;
+
+  &.is-active {
+    background-color: @success-color;
+  }
+}
 // client-monitor
 .client-monitor {
   .image-view-list {

+ 7 - 4
src/modules/grading-set/GradingLevelSet.vue

@@ -223,13 +223,13 @@
         :disabled="isSubmit"
         >确定</Button
       >
-      <Checkbox v-model="roughLevel">roughLevel</Checkbox>
+      <!-- <Checkbox v-model="roughLevel">roughLevel</Checkbox> -->
     </div>
   </div>
 </template>
 
 <script>
-import { workDetail, updateWork } from "@/api";
+import { workDetail, updateWork, getParamsSet } from "@/api";
 import { LEVEL_TYPE, ROUGH_LEVEL_TYPE } from "@/constants/enumerate";
 import schema from "async-validator";
 schema.warning = function() {};
@@ -276,6 +276,7 @@ export default {
       });
 
       this.getData();
+      this.getParamsSetInfo();
     },
     async getData() {
       const data = await workDetail(this.workId);
@@ -283,11 +284,13 @@ export default {
       this.levels = data.levels.map(item => {
         let nitem = { ...item };
         nitem.canEdit = false;
-        nitem.roughCode = null;
-        nitem.roughWeight = null;
         return nitem;
       });
     },
+    async getParamsSetInfo() {
+      const data = await getParamsSet(this.workId);
+      this.roughLevel = data.levelConfig.roughLevel;
+    },
     checkLevelCodeIsContinuous() {
       let levelIsContinuous = true;
       for (var i = 0, num = this.levels.length; i < num; i++) {

+ 5 - 4
src/modules/grading-set/GradingRuleSet.vue

@@ -362,7 +362,7 @@
           type="primary"
           shape="circle"
           style="width: 80px;"
-          :disabled="isSubmit"
+          :disabled="isRoughSubmit"
           @click="submitRough"
           >保存</Button
         >
@@ -396,6 +396,7 @@ export default {
   data() {
     return {
       isSubmit: false,
+      isRoughSubmit: false,
       workId: this.$route.params.workId,
       BOOLEAN_TYPE,
       PROP_DENOMINATOR_TYPE,
@@ -448,13 +449,13 @@ export default {
       const valid = await this.$refs.modalFormRoughComp.validate();
       if (!valid) return;
 
-      if (this.isSubmit) return;
-      this.isSubmit = true;
+      if (this.isRoughSubmit) return;
+      this.isRoughSubmit = true;
       let result = true;
       await updateRoughLevelParams(this.modalFormRough).catch(() => {
         result = false;
       });
-      this.isSubmit = false;
+      this.isRoughSubmit = false;
 
       if (!result) return;
       this.modalFormRoughCanEdit = false;

+ 230 - 175
src/modules/grading/components/ModifyFormalGradingTask.vue

@@ -1,175 +1,230 @@
-<template>
-  <Modal
-    class="modify-formal-grading-task"
-    v-model="modalIsShow"
-    title="创建正评任务"
-    :mask-closable="false"
-    @on-visible-change="visibleChange"
-  >
-    <p class="tips-info tips-error" v-if="!canPublish">
-      警告:当前批次还有未完成的任务,无法发布新任务!
-    </p>
-    <Form
-      ref="modalFormComp"
-      class="modal-form"
-      :model="modalForm"
-      :rules="rules"
-      :key="modalForm.id"
-      :label-width="130"
-    >
-      <FormItem prop="questionId" label="考区">
-        <Select
-          size="large"
-          v-model="modalForm.questionId"
-          @on-change="areaChange"
-          placeholder="请选择考区"
-        >
-          <Option
-            v-for="(item, index) in areas"
-            :key="index"
-            :value="item.questionId"
-            :label="item.areaName"
-          ></Option>
-        </Select>
-      </FormItem>
-      <FormItem label="已评数量">
-        <Input
-          size="large"
-          v-model.trim="modalForm.successCount"
-          readonly
-        ></Input>
-      </FormItem>
-      <FormItem size="large" label="待评数量">
-        <Input v-model.trim="modalForm.waitCount" readonly></Input>
-      </FormItem>
-      <FormItem size="large" label="整体进度">
-        <Input v-model.trim="modalForm.progress" readonly></Input>
-      </FormItem>
-      <FormItem prop="taskCount" label="分配任务数量">
-        <InputNumber
-          size="large"
-          :min="modalForm.waitCount ? 1 : 0"
-          :max="modalForm.waitCount"
-          :precision="0"
-          v-model.trim="modalForm.taskCount"
-          :disabled="!modalForm.waitCount || !canPublish"
-          style="width: 120px"
-          clearable
-        ></InputNumber>
-      </FormItem>
-    </Form>
-    <div slot="footer">
-      <Button
-        shape="circle"
-        type="primary"
-        :disabled="isSubmit || !canPublish || !modalForm.waitCount"
-        @click="submit"
-        >发布</Button
-      >
-      <Button shape="circle" @click="cancel">取消</Button>
-    </div>
-  </Modal>
-</template>
-
-<script>
-import {
-  checkMissionStatus,
-  checkCanPublishTask,
-  createGradingTask,
-  areaStatProgress
-} from "@/api";
-import { numberValidator } from "@/plugins/formRules";
-
-const initModalForm = {
-  questionId: null,
-  totalCount: 0,
-  successCount: 0,
-  waitCount: 0,
-  progress: 0,
-  taskCount: null
-};
-
-export default {
-  name: "modify-formal-grading-task",
-  props: {
-    subjectId: {
-      type: String,
-      required: true
-    }
-  },
-  data() {
-    return {
-      modalIsShow: false,
-      isSubmit: false,
-      canPublish: true,
-      modalForm: { ...initModalForm },
-      areas: [],
-      rules: {
-        questionId: numberValidator("请选择考区"),
-        taskCount: numberValidator("请输入分配任务数量")
-      }
-    };
-  },
-  methods: {
-    visibleChange(visible) {
-      if (visible) {
-        this.$refs.modalFormComp.resetFields();
-        this.getArea();
-        this.checkStatus();
-      }
-    },
-    async checkStatus() {
-      this.canPublish = await checkCanPublishTask(this.subjectId);
-    },
-    async getArea() {
-      const data = await areaStatProgress(this.subjectId);
-      if (data.length > 1) {
-        data[data.length - 1].questionId = -1;
-      }
-      this.areas = data;
-      this.modalForm.questionId = data[data.length - 1].questionId;
-      this.areaChange();
-    },
-    areaChange() {
-      const curArea = this.areas.find(
-        item => item.questionId === this.modalForm.questionId
-      );
-      this.modalForm = Object.assign({}, initModalForm, curArea);
-      this.modalForm.progress += "%";
-    },
-    cancel() {
-      this.modalIsShow = false;
-    },
-    open() {
-      this.modalIsShow = true;
-    },
-    async submit() {
-      const valid = await this.$refs.modalFormComp.validate();
-      if (!valid) return;
-
-      if (this.isSubmit) return;
-
-      const ids = this.subjectId.split("-");
-      await checkMissionStatus({ workId: ids[0], subject: ids[1] });
-
-      this.isSubmit = true;
-      let result = true;
-      await createGradingTask({
-        subjectId: this.subjectId,
-        taskCount: this.modalForm.taskCount,
-        questionId:
-          this.modalForm.questionId === -1 ? null : this.modalForm.questionId
-      }).catch(() => {
-        result = false;
-      });
-
-      this.isSubmit = false;
-      if (!result) return;
-
-      this.$Modal.success({ content: "评卷任务创建成功" });
-      this.$emit("modified");
-      this.cancel();
-    }
-  }
-};
-</script>
+<template>
+  <Modal
+    class="modify-formal-grading-task"
+    v-model="modalIsShow"
+    title="创建正评任务"
+    :mask-closable="false"
+    @on-visible-change="visibleChange"
+  >
+    <p class="tips-info tips-error" v-if="!canPublish">
+      警告:当前批次还有未完成的任务,无法发布新任务!
+    </p>
+    <Form
+      ref="modalFormComp"
+      class="modal-form"
+      :model="modalForm"
+      :rules="rules"
+      :key="modalForm.id"
+      :label-width="130"
+    >
+      <FormItem prop="questionId" label="考区">
+        <Select
+          size="large"
+          v-model="modalForm.questionId"
+          placeholder="请选择考区"
+          multiple
+          @on-change="areaChange"
+        >
+          <Option
+            v-for="(item, index) in areas"
+            :key="index"
+            :value="item.questionId"
+            :label="item.areaName"
+            :disabled="!!item.initCount"
+          ></Option>
+        </Select>
+      </FormItem>
+      <FormItem label="已评数量">
+        <Input
+          size="large"
+          v-model.trim="modalForm.successCount"
+          readonly
+        ></Input>
+      </FormItem>
+      <FormItem size="large" label="待评数量">
+        <Input v-model.trim="modalForm.waitCount" readonly></Input>
+      </FormItem>
+      <FormItem size="large" label="整体进度">
+        <Input v-model.trim="modalForm.progress" readonly></Input>
+      </FormItem>
+      <FormItem prop="taskCount" label="分配任务数量">
+        <InputNumber
+          size="large"
+          :min="minTaskCount"
+          :max="maxTaskCount"
+          :precision="0"
+          v-model.trim="modalForm.taskCount"
+          :disabled="!modalForm.waitCount || !canPublish"
+          style="width: 120px"
+          clearable
+        ></InputNumber>
+      </FormItem>
+    </Form>
+    <div slot="footer">
+      <Button
+        shape="circle"
+        type="primary"
+        :disabled="isSubmit || !canPublish || !modalForm.waitCount"
+        @click="submit"
+        >发布</Button
+      >
+      <Button shape="circle" @click="cancel">取消</Button>
+    </div>
+  </Modal>
+</template>
+
+<script>
+import {
+  checkMissionStatus,
+  checkCanPublishTask,
+  createGradingTask,
+  areaStatProgress
+} from "@/api";
+import { calcSum } from "@/plugins/utils";
+
+const initModalForm = {
+  questionId: [],
+  totalCount: 0,
+  successCount: 0,
+  waitCount: 0,
+  progress: 0,
+  taskCount: null
+};
+
+export default {
+  name: "modify-formal-grading-task",
+  props: {
+    subjectId: {
+      type: String,
+      required: true
+    }
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      canPublish: true,
+      modalForm: { ...initModalForm },
+      areas: [],
+      defaultQuestionIds: [],
+      minTaskCount: 0,
+      maxTaskCount: 0,
+      rules: {
+        questionId: [
+          {
+            required: true,
+            validator: (rule, value, callback) => {
+              if (value && value.length) {
+                callback();
+              } else {
+                callback(new Error("请选择考区"));
+              }
+            },
+            trigger: "change"
+          }
+        ],
+        taskCount: [
+          {
+            required: true,
+            validator: (rule, value, callback) => {
+              if (value) {
+                callback();
+              } else {
+                callback(new Error("请输入分配任务数量"));
+              }
+            },
+            trigger: "change"
+          }
+        ]
+      }
+    };
+  },
+  methods: {
+    visibleChange(visible) {
+      if (visible) {
+        this.$refs.modalFormComp.resetFields();
+        this.getArea();
+        this.checkStatus();
+      }
+    },
+    async checkStatus() {
+      this.canPublish = await checkCanPublishTask(this.subjectId);
+    },
+    async getArea() {
+      const data = await areaStatProgress(this.subjectId);
+      this.areas = data;
+      this.defaultQuestionIds = data
+        .filter(item => item.initCount)
+        .map(item => item.questionId);
+      this.modalForm.questionId = this.defaultQuestionIds.length
+        ? [...this.defaultQuestionIds]
+        : [data[data.length - 1].questionId];
+
+      this.areaChange();
+    },
+    areaChange() {
+      this.modalForm.questionId = this.modalForm.questionId.filter(
+        item => !this.defaultQuestionIds.includes(item)
+      );
+      this.modalForm.questionId = [
+        ...this.modalForm.questionId,
+        ...this.defaultQuestionIds
+      ];
+      const curAreas = this.areas.filter(item =>
+        this.modalForm.questionId.includes(item.questionId)
+      );
+      this.modalForm.successCount = calcSum(
+        curAreas.map(item => item.successCount)
+      );
+      this.modalForm.waitCount = calcSum(curAreas.map(item => item.waitCount));
+
+      this.modalForm.progress = !(
+        this.modalForm.successCount + this.modalForm.waitCount
+      )
+        ? "0%"
+        : (
+            (100 * this.modalForm.successCount) /
+            (this.modalForm.successCount + this.modalForm.waitCount)
+          ).toFixed(2) + "%";
+
+      this.modalForm.taskCount = calcSum(curAreas.map(item => item.initCount));
+      this.minTaskCount = this.modalForm.taskCount || 1;
+      this.maxTaskCount = this.modalForm.waitCount;
+
+      console.log(curAreas);
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate();
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+
+      const ids = this.subjectId.split("-");
+      await checkMissionStatus({ workId: ids[0], subject: ids[1] });
+
+      this.isSubmit = true;
+      let result = true;
+      await createGradingTask({
+        subjectId: this.subjectId,
+        taskCount: this.modalForm.taskCount,
+        questionId: this.modalForm.questionId
+      }).catch(() => {
+        result = false;
+      });
+
+      this.isSubmit = false;
+      if (!result) return;
+
+      this.$Modal.success({ content: "评卷任务创建成功" });
+      this.$emit("modified");
+      this.cancel();
+    }
+  }
+};
+</script>

+ 1 - 1
src/modules/grading/components/NoticeDialog.vue

@@ -92,7 +92,7 @@ export default {
     }
   },
   mounted() {
-    this.initData();
+    // this.initData();
   },
   beforeDestroy() {
     this.clearSetTs();

+ 13 - 1
src/modules/main/StudentManage.vue

@@ -386,7 +386,19 @@ export default {
         const column = {
           title: item.name,
           key: item.subject,
-          minWidth: 80
+          minWidth: 80,
+          render: (h, param) => {
+            return h("Icon", {
+              class: [{ "color-success": param.row[item.subject] == "1" }],
+              props: {
+                size: 18,
+                type:
+                  param.row[item.subject] == "1"
+                    ? "ios-checkmark-circle"
+                    : "ios-radio-button-off"
+              }
+            });
+          }
         };
         this.columns.splice(this.columns.length - 1, 0, column);
       });

+ 276 - 275
src/plugins/utils.js

@@ -1,275 +1,276 @@
-const deepmerge = require("deepmerge");
-
-/**
- * 判断对象类型
- * @param {*} obj 对象
- */
-function objTypeOf(obj) {
-  const toString = Object.prototype.toString;
-  const map = {
-    "[object Boolean]": "boolean",
-    "[object Number]": "number",
-    "[object String]": "string",
-    "[object Function]": "function",
-    "[object Array]": "array",
-    "[object Date]": "date",
-    "[object RegExp]": "regExp",
-    "[object Undefined]": "undefined",
-    "[object Null]": "null",
-    "[object Object]": "object"
-  };
-  return map[toString.call(obj)];
-}
-
-/**
- * 深拷贝
- * @param {Object/Array} data 需要拷贝的数据
- */
-function deepCopy(data, options) {
-  const defObj = objTypeOf(data) === "array" ? [] : {};
-  return deepmerge(defObj, data, options || {});
-}
-
-/**
- * 将目标对象中有的属性值与源对象中的属性值合并
- * @param {Object} target 目标对象
- * @param {Object} sources 源对象
- */
-function objAssign(target, sources) {
-  let targ = { ...target };
-  for (let k in targ) {
-    targ[k] = Object.prototype.hasOwnProperty.call(sources, k)
-      ? sources[k]
-      : targ[k];
-  }
-  return targ;
-}
-
-/**
- * 文件流下载
- * @param {Object} option 文件下载设置
- */
-function download(option) {
-  let defOpt = {
-    type: "get",
-    url: "",
-    data: "",
-    fileName: "",
-    header: ""
-  };
-  let opt = objAssign(defOpt, option);
-
-  return new Promise((resolve, reject) => {
-    let xhr = new XMLHttpRequest();
-    const requestType = opt.type.toUpperCase();
-    const IS_POST = requestType === "POST";
-    xhr.open(requestType, opt.url, true);
-    if (IS_POST)
-      xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8");
-    xhr.responseType = "blob";
-
-    // header set
-    if (opt.header && objTypeOf(opt.header) === "object") {
-      for (let key in opt.header) {
-        xhr.setRequestHeader(key, opt.header[key]);
-      }
-    }
-
-    xhr.onload = function() {
-      if (this.readyState === 4 && this.status === 200) {
-        var blob = this.response;
-        let pdfUrl = "";
-        let uRl = window.URL || window.webkitURL;
-        if (uRl && uRl.createObjectURL) {
-          pdfUrl = uRl.createObjectURL(blob);
-        } else {
-          reject("浏览器不兼容!");
-        }
-        let a = document.createElement("a");
-        a.download = opt.fileName;
-        a.href = pdfUrl;
-        document.body.appendChild(a);
-        a.click();
-        a.parentNode.removeChild(a);
-        resolve();
-      } else {
-        reject(this);
-      }
-    };
-
-    if (IS_POST) {
-      // let formData = new FormData();
-      // for (let key in opt.data) {
-      //   formData.append(key, opt.data[key]);
-      // }
-      // xhr.send(formData);
-      xhr.send(JSON.stringify(opt.data));
-    } else {
-      xhr.send();
-    }
-  });
-}
-
-/**
- * 构建图表btn
- * @param {Function} h createElement
- * @param {Array} actions 操作分类数组
- */
-function tableAction(h, actions) {
-  return actions.map(item => {
-    let attr = {
-      props: {
-        type: item.type || "primary",
-        size: "small",
-        disabled: !!item.disabled,
-        icon: item.icon
-      },
-      attrs: item.attrs,
-      on: {
-        click: () => {
-          item.action();
-        }
-      }
-    };
-    return h("Button", attr, item.name);
-  });
-}
-
-/**
- * 构建图表icon btn
- * @param {Function} h createElement
- * @param {Array} actions 操作分类数组
- */
-function tableIconAction(h, actions) {
-  return actions.map(item => {
-    let attr = {
-      class: item.classes,
-      props: {
-        size: item.size || 16,
-        type: item.icon,
-        color: item.color
-      },
-      attrs: item.attrs,
-      on: {
-        click: () => {
-          item.action();
-        }
-      }
-    };
-    return h("Icon", attr, item.name);
-  });
-}
-
-/**
- * 获取随机code,默认获取16位
- * @param {Number} len 推荐8的倍数
- *
- */
-function randomCode(len = 16) {
-  if (len <= 0) return;
-  let steps = Math.ceil(len / 8);
-  let stepNums = [];
-  for (let i = 0; i < steps; i++) {
-    let ranNum = Math.random()
-      .toString(32)
-      .slice(-8);
-    stepNums.push(ranNum);
-  }
-
-  return stepNums.join("");
-}
-
-/**
- * 序列化参数
- * @param {Object} params 参数对象
- */
-function qsParams(params) {
-  return Object.entries(params)
-    .filter(([key, val]) => val !== null)
-    .map(([key, val]) => `${key}=${val}`)
-    .join("&");
-}
-
-/**
- *
- * @param {String} format 时间格式
- * @param {Date} date 需要格式化的时间对象
- */
-function formatDate(format = "YYYY-MM-DD HH:mm:ss", date = new Date()) {
-  if (objTypeOf(date) !== "date") return;
-  const options = {
-    "Y+": date.getFullYear(),
-    "M+": date.getMonth() + 1,
-    "D+": date.getDate(),
-    "H+": date.getHours(),
-    "m+": date.getMinutes(),
-    "s+": date.getSeconds()
-  };
-  Object.entries(options).map(([key, val]) => {
-    if (new RegExp("(" + key + ")").test(format)) {
-      const zeros = key === "Y+" ? "0000" : "00";
-      const value = (zeros + val).substr(("" + val).length);
-      format = format.replace(RegExp.$1, value);
-    }
-  });
-  return format;
-}
-
-/**
- * 清除html标签
- * @param {String} str html字符串
- */
-function removeHtmlTag(str) {
-  return str.replace(/<[^>]+>/g, "");
-}
-
-/**
- * 驼峰命名
- * @param {Array} params
- */
-function humpFormat(params) {
-  return params
-    .map(item => {
-      const lowStr = item.toLowerCase();
-      return lowStr.slice(0, 1).toUpperCase() + lowStr.slice(1);
-    })
-    .join("");
-}
-
-/**
- * 计算总数
- * @param {Array} dataList 需要统计的数组
- */
-function calcSum(dataList) {
-  return dataList.reduce(function(total, item) {
-    return total + item;
-  }, 0);
-}
-
-/**
- *
- * @param {Object} obj 对象
- */
-function filterObjNull(obj) {
-  let newObj = {};
-  Object.keys(obj).map(key => {
-    if (obj[key] !== null) newObj[key] = obj[key];
-  });
-  return newObj;
-}
-
-export {
-  objTypeOf,
-  deepCopy,
-  objAssign,
-  download,
-  tableAction,
-  tableIconAction,
-  randomCode,
-  qsParams,
-  formatDate,
-  removeHtmlTag,
-  humpFormat,
-  calcSum,
-  filterObjNull
-};
+const deepmerge = require("deepmerge");
+
+/**
+ * 判断对象类型
+ * @param {*} obj 对象
+ */
+function objTypeOf(obj) {
+  const toString = Object.prototype.toString;
+  const map = {
+    "[object Boolean]": "boolean",
+    "[object Number]": "number",
+    "[object String]": "string",
+    "[object Function]": "function",
+    "[object Array]": "array",
+    "[object Date]": "date",
+    "[object RegExp]": "regExp",
+    "[object Undefined]": "undefined",
+    "[object Null]": "null",
+    "[object Object]": "object"
+  };
+  return map[toString.call(obj)];
+}
+
+/**
+ * 深拷贝
+ * @param {Object/Array} data 需要拷贝的数据
+ */
+function deepCopy(data, options) {
+  const defObj = objTypeOf(data) === "array" ? [] : {};
+  return deepmerge(defObj, data, options || {});
+}
+
+/**
+ * 将目标对象中有的属性值与源对象中的属性值合并
+ * @param {Object} target 目标对象
+ * @param {Object} sources 源对象
+ */
+function objAssign(target, sources) {
+  let targ = { ...target };
+  for (let k in targ) {
+    targ[k] = Object.prototype.hasOwnProperty.call(sources, k)
+      ? sources[k]
+      : targ[k];
+  }
+  return targ;
+}
+
+/**
+ * 文件流下载
+ * @param {Object} option 文件下载设置
+ */
+function download(option) {
+  let defOpt = {
+    type: "get",
+    url: "",
+    data: "",
+    fileName: "",
+    header: ""
+  };
+  let opt = objAssign(defOpt, option);
+
+  return new Promise((resolve, reject) => {
+    let xhr = new XMLHttpRequest();
+    const requestType = opt.type.toUpperCase();
+    const IS_POST = requestType === "POST";
+    xhr.open(requestType, opt.url, true);
+    if (IS_POST)
+      xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8");
+    xhr.responseType = "blob";
+
+    // header set
+    if (opt.header && objTypeOf(opt.header) === "object") {
+      for (let key in opt.header) {
+        xhr.setRequestHeader(key, opt.header[key]);
+      }
+    }
+
+    xhr.onload = function() {
+      if (this.readyState === 4 && this.status === 200) {
+        var blob = this.response;
+        let pdfUrl = "";
+        let uRl = window.URL || window.webkitURL;
+        if (uRl && uRl.createObjectURL) {
+          pdfUrl = uRl.createObjectURL(blob);
+        } else {
+          reject("浏览器不兼容!");
+        }
+        let a = document.createElement("a");
+        a.download = opt.fileName;
+        a.href = pdfUrl;
+        document.body.appendChild(a);
+        a.click();
+        a.parentNode.removeChild(a);
+        resolve();
+      } else {
+        reject(this);
+      }
+    };
+
+    if (IS_POST) {
+      // let formData = new FormData();
+      // for (let key in opt.data) {
+      //   formData.append(key, opt.data[key]);
+      // }
+      // xhr.send(formData);
+      xhr.send(JSON.stringify(opt.data));
+    } else {
+      xhr.send();
+    }
+  });
+}
+
+/**
+ * 构建图表btn
+ * @param {Function} h createElement
+ * @param {Array} actions 操作分类数组
+ */
+function tableAction(h, actions) {
+  return actions.map(item => {
+    let attr = {
+      props: {
+        type: item.type || "primary",
+        size: "small",
+        disabled: !!item.disabled,
+        icon: item.icon
+      },
+      attrs: item.attrs,
+      on: {
+        click: () => {
+          item.action();
+        }
+      }
+    };
+    return h("Button", attr, item.name);
+  });
+}
+
+/**
+ * 构建图表icon btn
+ * @param {Function} h createElement
+ * @param {Array} actions 操作分类数组
+ */
+function tableIconAction(h, actions) {
+  return actions.map(item => {
+    let attr = {
+      class: item.classes,
+      props: {
+        size: item.size || 16,
+        type: item.icon,
+        color: item.color
+      },
+      attrs: item.attrs,
+      on: {
+        click: () => {
+          item.action();
+        }
+      }
+    };
+    return h("Icon", attr, item.name);
+  });
+}
+
+/**
+ * 获取随机code,默认获取16位
+ * @param {Number} len 推荐8的倍数
+ *
+ */
+function randomCode(len = 16) {
+  if (len <= 0) return;
+  let steps = Math.ceil(len / 8);
+  let stepNums = [];
+  for (let i = 0; i < steps; i++) {
+    let ranNum = Math.random()
+      .toString(32)
+      .slice(-8);
+    stepNums.push(ranNum);
+  }
+
+  return stepNums.join("");
+}
+
+/**
+ * 序列化参数
+ * @param {Object} params 参数对象
+ */
+function qsParams(params) {
+  return Object.entries(params)
+    .filter(([key, val]) => val !== null)
+    .map(([key, val]) => `${key}=${val}`)
+    .join("&");
+}
+
+/**
+ *
+ * @param {String} format 时间格式
+ * @param {Date} date 需要格式化的时间对象
+ */
+function formatDate(format = "YYYY-MM-DD HH:mm:ss", date = new Date()) {
+  if (objTypeOf(date) !== "date") return;
+  const options = {
+    "Y+": date.getFullYear(),
+    "M+": date.getMonth() + 1,
+    "D+": date.getDate(),
+    "H+": date.getHours(),
+    "m+": date.getMinutes(),
+    "s+": date.getSeconds()
+  };
+  Object.entries(options).map(([key, val]) => {
+    if (new RegExp("(" + key + ")").test(format)) {
+      const zeros = key === "Y+" ? "0000" : "00";
+      const value = (zeros + val).substr(("" + val).length);
+      format = format.replace(RegExp.$1, value);
+    }
+  });
+  return format;
+}
+
+/**
+ * 清除html标签
+ * @param {String} str html字符串
+ */
+function removeHtmlTag(str) {
+  return str.replace(/<[^>]+>/g, "");
+}
+
+/**
+ * 驼峰命名
+ * @param {Array} params
+ */
+function humpFormat(params) {
+  return params
+    .map(item => {
+      const lowStr = item.toLowerCase();
+      return lowStr.slice(0, 1).toUpperCase() + lowStr.slice(1);
+    })
+    .join("");
+}
+
+/**
+ * 计算总数
+ * @param {Array} dataList 需要统计的数组
+ */
+function calcSum(dataList) {
+  if (!dataList.length) return 0;
+  return dataList.reduce(function(total, item) {
+    return total + item;
+  }, 0);
+}
+
+/**
+ *
+ * @param {Object} obj 对象
+ */
+function filterObjNull(obj) {
+  let newObj = {};
+  Object.keys(obj).map(key => {
+    if (obj[key] !== null) newObj[key] = obj[key];
+  });
+  return newObj;
+}
+
+export {
+  objTypeOf,
+  deepCopy,
+  objAssign,
+  download,
+  tableAction,
+  tableIconAction,
+  randomCode,
+  qsParams,
+  formatDate,
+  removeHtmlTag,
+  humpFormat,
+  calcSum,
+  filterObjNull
+};