Explorar el Código

[新增] 老师评审功能

Kunduin hace 7 años
padre
commit
66b4f7b8ab

+ 6 - 0
mock/serializers/review/CheckListResultItemSerializer.js

@@ -6,6 +6,9 @@ const makeId = require("../../util/makeId");
  * @property {number|string} id checkListItem id
  * @property {string} content 评价内容
  * @property {string} explain 老师说明
+ * @property {boolean} teacherLevel 老师作出的评价
+ * @property {string} teacherComment 老师做出的备注
+ * @property {boolean} hasTeacherReview 老师是否评审过
  * @property {ResultByGroupType[]} results 各个小组的评价
  */
 
@@ -37,6 +40,9 @@ module.exports = ({ id = makeId() }) => {
     id,
     content: "该项评价的内容, String",
     explain: "老师的说明,String",
+    teacherLevel: true,
+    teacherComment: "老师的评论论论",
+    hasTeacherReview: true,
     results: [groupResult(), groupResult(), groupResult()]
   };
 };

+ 1 - 1
mock/serializers/user/UserSerializer.js

@@ -14,7 +14,7 @@ const makeId = require("../../util/makeId");
  * @param role
  * @returns {UserSerializerType}
  */
-module.exports = (role = "STUDENT") => {
+module.exports = (role = "TEACHER") => {
   return {
     id: makeId(),
     role: role,

+ 11 - 16
src/api/codehw.js

@@ -1,18 +1,17 @@
 import request from "@/util/request";
 import { CODE_HOMEWORK_MODULE } from "./_prefix";
 
-/**
- * 获得学生的 代码作业 列表
- */
-export const getCodeHomeworkList = courseId => {
-  console.log(courseId);
-  return {
-    data: [
-      { homeworkId: "1", description: "codeHomework1" },
-      { homeworkId: "2", description: "codeHomework2" }
-    ]
-  };
-};
+// /**
+//  * 获得学生的 代码作业 列表
+//  */
+// export const getCodeHomeworkList = courseId => {
+//   return {
+//     data: [
+//       { homeworkId: "1", description: "codeHomework1" },
+//       { homeworkId: "2", description: "codeHomework2" }
+//     ]
+//   };
+// };
 
 /**
  * 获得某个 Homework 的 Project 列表(应该只有一个)
@@ -20,8 +19,6 @@ export const getCodeHomeworkList = courseId => {
  * @return {Promise<{data:{projectId:string,name:string}[]}>}
  */
 export const getProjectList = homeworkId => {
-  //TODO:delete this before deploy
-  console.log(homeworkId);
   return request(`${CODE_HOMEWORK_MODULE}/projects`, {
     method: "POST",
     body: {
@@ -36,8 +33,6 @@ export const getProjectList = homeworkId => {
  * @return {Promise<{id:string,data:ProjectSerializerType}>}
  */
 export const getProjectDetail = projectId => {
-  //TODO:delete this before deploy
-  console.log(projectId);
   return request(`${CODE_HOMEWORK_MODULE}/project/${projectId}`);
 };
 

+ 3 - 3
src/api/review.js

@@ -98,13 +98,13 @@ export const getTeacherStartArgue = (reviewId, groupId) => {
  * 老师提交对某次互评作业一个小组的评价结果
  * @param {string|number} reviewId
  * @param {string|number} groupId
- * @param {CheckListItemSerializerType[]} checkList
+ * @param {CheckListItemSerializerType[]} checkListItem
  * @return {Promise<{checkList:CheckListItemSerializerType[]}>}
  */
-export const postTeacherArgueResult = (reviewId, groupId, checkList) => {
+export const postTeacherArgueResult = (reviewId, groupId, checkListItem) => {
   return request(`${REVIEW_MODULE}/sum/${reviewId}/argue/${groupId}`, {
     method: "POST",
-    body: { checkList }
+    body: { checkList: checkListItem }
   });
 };
 

BIN
src/assets/icon/edit-dark.png


+ 105 - 7
src/components/CheckList/ResultCheckListItem.vue

@@ -1,17 +1,30 @@
 <template>
   <card-judge-item :success="pass">
     <div class="result-section">
-      <strong>{{ checkResultItem.content }}</strong>
+      <div class="line-container">
+        <strong style="flex-grow: 1">
+          <b-tooltip :label="checkResultItem.explain" position="is-top">
+            {{ checkResultItem.content }}
+          </b-tooltip>
+        </strong>
+        <div @click="selectReviewingTarget" class="image-container">
+          <img
+            style="width: 20px;height: 20px"
+            alt=""
+            src="../../assets/icon/edit-dark.png"
+          />
+        </div>
+      </div>
       <div
-        :class="{ 'group-line--hover': !result.isArgued }"
+        :class="{ 'group-line--hover': !result.isArgued && !canBeReviewed }"
         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
-        >
+        <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>
@@ -20,9 +33,47 @@
           type="is-warning"
           class="state"
           style="margin-left: 10px;"
-          >已申请
+        >
+          已申请
         </b-tag>
       </div>
+      <div
+        v-if="checkResultItem.hasTeacherReview && !isReviewing"
+        :class="{ 'group-line--hover': canBeReviewed }"
+        @click="selectReviewingTarget"
+        class="group-line"
+      >
+        <strong style="padding-right:10px;color:grey">「老师评价」</strong>
+        <span class="state state-success" v-if="checkResultItem.teacherLevel">
+          PASS
+        </span>
+        <span class="state state-error" v-else>ERROR</span>
+        <span class="comments">{{ checkResultItem.teacherComment }}</span>
+      </div>
+      <div v-else>
+        <div class="line-container">
+          <b-checkbox
+            class="checkbox-section"
+            v-model="reviewLevel"
+            type="is-primary"
+          >
+            是否满足条件
+          </b-checkbox>
+          <a
+            class="button is-small is-primary"
+            :class="{ 'is-loading': submitLoading }"
+            @click="submitReviewResult"
+          >
+            提交
+          </a>
+        </div>
+
+        <b-input
+          placeholder="请输入评价"
+          v-model="reviewComment"
+          type="textarea"
+        ></b-input>
+      </div>
     </div>
 
     <b-modal :active.sync="showModal" :width="400">
@@ -60,6 +111,14 @@ export default {
     arguable: {
       type: Boolean,
       default: false
+    },
+    canBeReviewed: {
+      type: Boolean,
+      default: false
+    },
+    isReviewing: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -67,16 +126,26 @@ export default {
       selectedResult: {},
       showModal: false,
       remark: "",
-      submitLoading: false
+      submitLoading: false,
+      reviewLevel: this.checkResultItem.teacherLevel,
+      reviewComment: this.checkResultItem.teacherComment || ""
     };
   },
   computed: {
     pass() {
       const { results = [] } = this.checkResultItem;
+      if (this.checkResultItem.hasTeacherReview) {
+        return this.checkResultItem.teacherLevel;
+      }
       return results.filter(result => result.level === false).length === 0;
     }
   },
   methods: {
+    selectReviewingTarget() {
+      if (this.canBeReviewed) {
+        this.$emit("review-item", this.checkResultItem.id);
+      }
+    },
     argue(result) {
       if (this.arguable && !result.isArgued) {
         this.selectedResult = result;
@@ -93,6 +162,15 @@ export default {
       this.submitLoading = false;
       this.showModal = false;
       this.remark = "";
+    },
+    async submitReviewResult() {
+      this.submitLoading = true;
+      await this.$emit("review-teacher", {
+        id: this.checkResultItem.id,
+        level: this.reviewLevel,
+        comment: this.reviewComment
+      });
+      this.submitLoading = false;
     }
   }
 };
@@ -100,6 +178,10 @@ export default {
 
 <style lang="scss" scoped>
 @import "../../assets/scss/app";
+.checkbox-section {
+  padding: $default-margin 0;
+}
+
 .result-section {
   padding: $default-margin;
   border-bottom: 1px solid rgba(0, 0, 0, 0.12);
@@ -110,6 +192,12 @@ export default {
   border-radius: $default-radius;
 }
 
+.line-container {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
 .state {
   padding: 2px 5px;
   font-weight: bold;
@@ -131,6 +219,16 @@ export default {
   font-size: 14px;
 }
 
+.image-container {
+  border-radius: 2px;
+  height: 20px;
+  cursor: pointer;
+}
+
+.image-container:hover {
+  background-color: #f6f5fa;
+}
+
 .group-line {
   padding: 2px 0;
   border-radius: $default-radius;

+ 2 - 1
src/components/CheckList/index.vue

@@ -67,8 +67,9 @@
           @click="submitSolution(item)"
           :class="{ 'is-loading': submitSolutionLoading }"
           class="button is-primary"
-          >提交处理好的申诉</span
         >
+          提交处理好的申诉
+        </span>
       </div>
     </card-judge-item>
   </div>

+ 0 - 1
src/views/student/Code/ProjectDetail/TestDetail.vue

@@ -132,7 +132,6 @@ export default {
       }
     },
     sortTestList(list) {
-      console.log(list);
       if (!list) return [];
       const copyList = list.slice(0);
       copyList.sort((a, b) => b.time - a.time);

+ 0 - 1
src/views/student/Code/ProjectDetail/index.vue

@@ -84,7 +84,6 @@ export default {
     const { projectId } = this.$route.params;
     this.detailLoading = true;
     const detail = await getProjectDetail(projectId);
-    console.log(detail.data);
     this.projectDetail = detail.data;
     this.detailLoading = false;
   }

+ 40 - 13
src/views/teacher/Review/ReviewDetail/GroupDocDetail/index.vue

@@ -16,11 +16,6 @@
       <template slot="title">
         {{ groupDetail.name }}
       </template>
-      <template slot="action">
-        <router-link :to="`${$route.path}/argue`" class="button is-primary">
-          开始重评
-        </router-link>
-      </template>
     </page-header>
     <page-body>
       <div class="columns">
@@ -35,12 +30,20 @@
         <div class="column is-4">
           <div class="right-part">
             <div v-if="detailLoading"><page-section-loading show /></div>
-            <result-check-list-item
-              v-else
-              v-for="item in detail.checkListResult"
-              :key="item.id"
-              :check-result-item="item"
-            />
+            <div v-else>
+              <div v-if="!!detail.level" class="notification is-success">
+                <strong>小组得分 : </strong>{{ detail.level }}
+              </div>
+              <result-check-list-item
+                v-for="item in detail.checkListResult"
+                :key="item.id"
+                :check-result-item="item"
+                :can-be-reviewed="true"
+                :is-reviewing="item.id === reviewingId"
+                @review-item="onReviewItem"
+                @review-teacher="onReviewSubmit"
+              />
+            </div>
           </div>
         </div>
       </div>
@@ -48,7 +51,10 @@
   </div>
 </template>
 <script>
-import { getTeacherReviewGroupDetail } from "@/api/review";
+import {
+  getTeacherReviewGroupDetail,
+  postTeacherArgueResult
+} from "@/api/review";
 import timeMixins from "@/mixins/time";
 import ResultCheckListItem from "@/components/CheckList/ResultCheckListItem";
 import PageHeader from "@/components/PageHeader";
@@ -73,10 +79,31 @@ export default {
       detailLoading: false,
       groupDetail: {},
       groupDetailLoading: false,
-      submitLoading: false
+      submitLoading: false,
+      reviewingId: -1
     };
   },
   mixins: [timeMixins],
+  methods: {
+    onReviewItem(id) {
+      this.reviewingId = id;
+    },
+    onReviewSubmit(checkListItem) {
+      const { reviewId, groupId } = this.$route.params;
+      postTeacherArgueResult(reviewId, groupId, checkListItem)
+        .then(() =>
+          getTeacherReviewGroupDetail(reviewId, groupId).then(
+            resp => (this.detail = resp.data)
+          )
+        )
+        .catch(err =>
+          this.$toast.open({
+            message: err.message || "服务器未知错误",
+            type: "is-danger"
+          })
+        );
+    }
+  },
   mounted() {
     const { reviewId, groupId } = this.$route.params;
     this.detailLoading = true;

+ 2 - 2
vue.config.js

@@ -2,9 +2,9 @@ module.exports = {
   devServer: {
     proxy: {
       "^/api": {
-        target: "http://localhost:4000/",
+        // target: "http://localhost:4000/",
         // target: "http://10.1.1.198:8080/",
-        //target: "http://192.168.0.101:8080",
+        target: "http://192.168.0.100:8080",
         // target: "http://10.1.2.3:8080/",
 
         ws: true,