Преглед изворни кода

Merge branch 'feature/review-bay' of SEEC-II-FrontEnd-Team/SEEC-II-FrontEnd into develop

WuXinyu пре 7 година
родитељ
комит
4cd24f79eb

+ 3 - 1
mock/serializers/review/CheckListResultItemSerializer.js

@@ -15,6 +15,7 @@ const makeId = require("../../util/makeId");
  * @property {number|string} groupId 小组 id
  * @property {boolean} level 小组做出的评价
  * @property {string} comment 小组做出的备注
+ * @property {boolean} isArgued 是否已经申请过重评
  */
 
 /**
@@ -28,7 +29,8 @@ module.exports = ({ id = makeId() }) => {
   const groupResult = () => ({
     groupId: makeId(),
     level: true,
-    comment: "小组做出的备注"
+    comment: "小组做出的备注",
+    isArgued: false
   });
 
   return {

+ 1 - 1
src/api/review.js

@@ -186,7 +186,7 @@ export const postStudentArgueReview = ({
   groupId,
   remark = ""
 }) => {
-  return request(`${REVIEW_MODULE}/argue`, {
+  return request(`${REVIEW_MODULE}/student/argue`, {
     method: "POST",
     body: { reviewId, id, groupId, remark }
   });

+ 143 - 0
src/components/CheckList/ResultCheckListItem.vue

@@ -0,0 +1,143 @@
+<template>
+  <card-judge-item :success="pass">
+    <div class="result-section">
+      <strong>{{ checkResultItem.content }}</strong>
+      <div
+        :class="{ 'group-line--hover': !result.isArgued }"
+        class="group-line"
+        v-for="result in checkResultItem.results"
+        :key="result.rid"
+        @click="argue(result)"
+      >
+        <strong style="padding-right:10px;color:grey"
+          >+ id:{{ result.groupId + "\t" }}</strong
+        >
+        <span class="state state-success" v-if="result.level">PASS</span>
+        <span class="state state-error" v-else>ERROR</span>
+        <span class="comments">{{ result.comment }}</span>
+        <b-tag
+          v-if="result.isArgued"
+          type="is-warning"
+          class="state"
+          style="margin-left: 10px;"
+          >已申请
+        </b-tag>
+      </div>
+    </div>
+
+    <b-modal :active.sync="showModal" :width="400">
+      <div class="card">
+        <div class="title">申请重判</div>
+        <div class="content">
+          <b-field label="理由">
+            <b-input maxlength="200" type="textarea" v-model="remark"></b-input>
+          </b-field>
+
+          <a
+            class="button is-link"
+            :class="{ 'is-loading': submitLoading }"
+            @click="submit()"
+          >
+            提交重判申请
+          </a>
+        </div>
+      </div>
+    </b-modal>
+  </card-judge-item>
+</template>
+<script>
+import CardJudgeItem from "@/components/CardJudgeItem/index";
+export default {
+  name: "ResultCheckListItem",
+  components: { CardJudgeItem },
+  props: {
+    checkResultItem: {
+      type: Object,
+      default: () => ({
+        results: []
+      })
+    },
+    arguable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      selectedResult: {},
+      showModal: false,
+      remark: "",
+      submitLoading: false
+    };
+  },
+  computed: {
+    pass() {
+      const { results = [] } = this.checkResultItem;
+      return results.filter(result => result.level === false).length === 0;
+    }
+  },
+  methods: {
+    argue(result) {
+      if (this.arguable && !result.isArgued) {
+        this.selectedResult = result;
+        this.showModal = true;
+      }
+    },
+    async submit() {
+      this.submitLoading = true;
+      await this.$emit("argue", {
+        id: this.checkResultItem.id,
+        groupId: this.selectedResult.groupId,
+        remark: this.remark
+      });
+      this.submitLoading = false;
+      this.showModal = false;
+      this.remark = "";
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../assets/scss/app";
+.result-section {
+  padding: $default-margin;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.card {
+  padding: $default-margin-between;
+  border-radius: $default-radius;
+}
+
+.state {
+  padding: 2px 5px;
+  font-weight: bold;
+  font-size: 12px;
+  border-radius: 3px;
+  color: white;
+}
+
+.state-success {
+  background: $success;
+}
+.state-error {
+  background: $danger;
+}
+.comments {
+  padding-left: 10px;
+  display: inline-block;
+  color: $grey;
+  font-size: 14px;
+}
+
+.group-line {
+  padding: 2px 0;
+  border-radius: $default-radius;
+}
+
+.group-line--hover:hover {
+  cursor: pointer;
+  background-color: #f4f4f4;
+}
+</style>

+ 5 - 0
src/components/PageHeader/index.scss

@@ -4,6 +4,11 @@
   background: white;
   padding: $default-margin-between $default-margin-between*1.5 0 $default-margin-between*1.5;
   box-shadow: $default-shadow-lighter;
+
+  .tabs a,
+  .tabs ul{
+    border-bottom-color: white;
+  }
   .wide {
     max-width: 1200px;
     margin: auto;

+ 13 - 1
src/router/routes.js

@@ -135,7 +135,19 @@ export default [
       },
       {
         path: "review/:reviewId",
-        component: () => import("@/views/student/Review/ReviewDetail")
+        component: () => import("@/views/student/Review/ReviewDetail"),
+        children: [
+          {
+            path: "self",
+            component: () =>
+              import("@/views/student/Review/ReviewDetail/ReviewSelfDetail")
+          },
+          {
+            path: "",
+            component: () =>
+              import("@/views/student/Review/ReviewDetail/ReviewDocs")
+          }
+        ]
       },
       {
         path: "review/:reviewId/doc/:docReviewId",

+ 25 - 0
src/views/student/Review/ReviewDetail/ReviewDocs/index.vue

@@ -0,0 +1,25 @@
+<template>
+  <div class="columns is-multiline is-desktop">
+    <div :key="item.docReviewId" v-for="item in docs" class="column is-4">
+      <router-link :to="`${$route.path}/doc/${item.docReviewId}`">
+        <card hoverable> {{ item.group.name }} </card>
+      </router-link>
+    </div>
+  </div>
+</template>
+
+<script>
+import Card from "@/components/Card";
+
+export default {
+  components: {
+    Card
+  },
+  props: {
+    docs: {
+      type: Array,
+      default: () => []
+    }
+  }
+};
+</script>

+ 90 - 0
src/views/student/Review/ReviewDetail/ReviewSelfDetail/index.vue

@@ -0,0 +1,90 @@
+<template>
+  <div>
+    <div class="columns">
+      <div class="column is-8">
+        <div class="page-section">
+          <div v-if="detail.docContent" class="content">
+            <vue-markdown>{{ detail.docContent }}</vue-markdown>
+          </div>
+        </div>
+      </div>
+      <div class="column is-4">
+        <div class="right-part">
+          <result-check-list-item
+            v-for="item in detail.checkListResult"
+            :arguable="true"
+            :key="item.id"
+            :check-result-item="item"
+            @argue="argue"
+          />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import VueMarkdown from "vue-markdown";
+import {
+  getStudentReviewSelfDetail,
+  postStudentArgueReview
+} from "@/api/review";
+import timeMixins from "@/mixins/time";
+import ResultCheckListItem from "@/components/CheckList/ResultCheckListItem";
+export default {
+  components: {
+    ResultCheckListItem,
+    VueMarkdown
+  },
+  data() {
+    return {
+      detail: {
+        review: {},
+        checkListResult: []
+      },
+      submitLoading: false
+    };
+  },
+  mixins: [timeMixins],
+  mounted() {
+    const { reviewId } = this.$route.params;
+    getStudentReviewSelfDetail(reviewId).then(
+      resp => (this.detail = resp.data)
+    );
+  },
+  methods: {
+    async argue(item) {
+      const { reviewId } = this.$route.params;
+      try {
+        const { data } = await postStudentArgueReview({ reviewId, ...item });
+        this.detail.checkList = data;
+      } catch (e) {
+        this.$toast.open({
+          message: `重复的申请评审是不被允许的`,
+          position: "is-bottom",
+          type: "is-danger"
+        });
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../../assets/scss/app";
+.right-part {
+  margin-bottom: -1.5rem;
+  margin-right: -1.5rem;
+  background: white;
+  border-top: 1px solid #eff2f7;
+  /*position: sticky;*/
+  /*top: 0;*/
+  /*max-height: 100vh;*/
+  /*overflow-y: auto;*/
+  box-shadow: $default-shadow;
+}
+
+.action-button {
+  width: 100%;
+  border-radius: 0;
+}
+</style>

+ 6 - 18
src/views/student/Review/ReviewDetail/index.vue

@@ -1,9 +1,10 @@
 <template>
   <div>
     <page-header
-      :routes="[
-        { name: '互评作业', path: 'review' },
-        { name: '文档详情', path: $route.path }
+      :routes="[{ name: '互评作业', path: 'review' }, { name: '文档详情' }]"
+      :tab-list="[
+        { name: '待评文档', path: `review/${$route.params.reviewId}` },
+        { name: '我的文档', path: `review/${$route.params.reviewId}/self` }
       ]"
     >
       <template slot="title">
@@ -29,30 +30,17 @@
         </div>
       </template>
     </page-header>
-    <page-body>
-      <div class="columns is-multiline is-desktop">
-        <div
-          :key="item.docReviewId"
-          v-for="item in detail.documents"
-          class="column is-4"
-        >
-          <router-link :to="`${$route.path}/doc/${item.docReviewId}`">
-            <card hoverable> {{ item.group.name }} </card>
-          </router-link>
-        </div>
-      </div>
-    </page-body>
+    <page-body> <router-view :docs="detail.documents" /> </page-body>
   </div>
 </template>
 <script>
 import PageHeader from "@/components/PageHeader";
 import PageBody from "@/components/PageBody/index";
-import Card from "@/components/Card";
 import { getStudentReviewDetail } from "@/api/review";
 import timeMixins from "@/mixins/time";
 import ReviewStateTag from "@/components/ReviewStateTag/index";
 export default {
-  components: { ReviewStateTag, PageBody, PageHeader, Card },
+  components: { ReviewStateTag, PageBody, PageHeader },
   data() {
     return {
       detail: {}

+ 0 - 0
src/views/student/Review/ReviewSelf/index.vue