Browse Source

标记与试题过滤

Michael Wang 6 years ago
parent
commit
4962eb78f6

+ 31 - 11
src/features/OnlineExam/Examing/ExamingHome.vue

@@ -4,7 +4,7 @@
       <RemainTime></RemainTime>
       <OverallProgress :exam-question-list="validQuestions"></OverallProgress>
       <QuestionFilters :exam-question-list="validQuestions"></QuestionFilters>
-      <Button class="qm-primary-button">交卷</Button>
+      <Button class="qm-primary-button" @click="submitPaper">交卷</Button>
     </div>
     <div class="main">
       <QuestionView :exam-question="examQuestion"></QuestionView>
@@ -43,6 +43,31 @@ export default {
     };
   },
   async mounted() {
+    if (!this.$route.query.examQuestionId) {
+      const exam = await this.$http.get(
+        "/api/ecs_exam_work/exam/" + this.$route.params.examId
+      );
+      this.exam = exam.data;
+
+      const paperStruct = await this.$http.get(
+        "/api/exam_question/paper_struct/?exam_record_id=" +
+          this.$route.query.examRecordId
+      );
+      this.paperStruct = paperStruct.data;
+
+      // FIXME: global API processing. mock or not
+      const examQuestionList = await this.$http.get(
+        "/api/exam_question/?exam_record_id=" + this.$route.query.examRecordId
+      );
+      this.examQuestionList = examQuestionList.data;
+      this.validQuestions = this.examQuestionList.filter(
+        q => q.nestedQuestion === false
+      );
+      this.$router.push(
+        this.$route.fullPath + "&examQuestionId=" + this.examQuestionList[0].id
+      );
+      return;
+    }
     this.init();
   },
   beforeRouteUpdate(to, from, next) {
@@ -63,8 +88,7 @@ export default {
 
       // FIXME: global API processing. mock or not
       const examQuestionList = await this.$http.get(
-        "/api/mock/exam_question/?exam_record_id=" +
-          this.$route.query.examRecordId
+        "/api/exam_question/?exam_record_id=" + this.$route.query.examRecordId
       );
       this.examQuestionList = examQuestionList.data;
       this.validQuestions = this.examQuestionList.filter(
@@ -81,20 +105,16 @@ export default {
       //   }
       // }
 
-      if (!this.$route.query.examQuestionId) {
-        this.$router.push(
-          this.$route.fullPath +
-            "&examQuestionId=" +
-            this.examQuestionList[0].id
-        );
-        return;
-      }
       next && next();
       this.examQuestion = this.examQuestionList.find(
         eq => eq.id == this.$route.query.examQuestionId // number == string
       );
       this.preExamQuestion = this.validQuestions[this.examQuestion.orders - 2];
       this.nextExamQuestion = this.validQuestions[this.examQuestion.orders];
+    },
+    async submitPaper() {
+      const res = await this.$http.get("/api/exam_control/submit");
+      this.$router.push("/");
     }
   },
   components: {

+ 18 - 2
src/features/OnlineExam/Examing/OverallProgress.vue

@@ -1,5 +1,9 @@
 <template>
-  <div class="remain-time">进度 {{progress}}</div>
+  <div class="progress-container">
+    <i-progress :percent="progressNum" :stroke-width="20" status="active" hide-info>
+    </i-progress>
+    <span>{{progress}}</span>
+  </div>
 </template>
 
 <script>
@@ -13,6 +17,13 @@ export default {
   },
   async mounted() {},
   computed: {
+    progressNum() {
+      return (
+        100 *
+        this.examQuestionList.filter(q => q.stuAnswer).length /
+        this.examQuestionList.length
+      );
+    },
     progress: function() {
       return `${this.examQuestionList.filter(q => q.stuAnswer).length} / ${
         this.examQuestionList.length
@@ -23,7 +34,12 @@ export default {
 </script>
 
 <style scoped>
-.remain-time {
+.progress-container {
+  display: grid;
   justify-self: flex-start;
+
+  place-items: center;
+  grid-template-columns: 200px 50px;
+  width: 250px;
 }
 </style>

+ 20 - 4
src/features/OnlineExam/Examing/QuestionFilters.vue

@@ -1,13 +1,16 @@
 <template>
   <div class="q-filters">
-    <div class="remain-time">全部 {{all}}</div>
-    <div class="remain-time">已答 {{answered}}</div>
-    <div class="remain-time">标记 {{signed}}</div>
-    <div class="remain-time">未答 {{unanswered}}</div>
+    <div :class="questionFilterType == 'ALL' && 'selected-type'" @click="updateQuestionFilter('ALL')">全部 {{all}}</div>
+    <div :class="questionFilterType == 'ANSWERED' && 'selected-type'" @click="updateQuestionFilter('ANSWERED')">已答 {{answered}}</div>
+    <div :class="questionFilterType == 'SIGNED' && 'selected-type'" @click="updateQuestionFilter('SIGNED')">标记 {{signed}}</div>
+    <div :class="questionFilterType == 'UNANSWERED' && 'selected-type'" @click="updateQuestionFilter('UNANSWERED')">未答 {{unanswered}}</div>
   </div>
 </template>
 
 <script>
+import { createNamespacedHelpers } from "vuex";
+const { mapState, mapMutations } = createNamespacedHelpers("examHomeModule");
+
 export default {
   name: "QuestionFilters",
   data() {
@@ -17,7 +20,11 @@ export default {
     examQuestionList: Array
   },
   async mounted() {},
+  methods: {
+    ...mapMutations(["updateQuestionFilter"])
+  },
   computed: {
+    ...mapState(["questionFilterType"]),
     all: function() {
       return this.examQuestionList.length;
     },
@@ -38,5 +45,14 @@ export default {
 .q-filters {
   display: grid;
   grid-template-columns: 1fr 1fr 1fr 1fr;
+  cursor: pointer;
+}
+
+.q-filters > div {
+  border-radius: 6px;
+  padding: 5px 10px;
+}
+.selected-type {
+  background-color: white;
 }
 </style>

+ 48 - 8
src/features/OnlineExam/Examing/QuestionNavView.vue

@@ -5,9 +5,13 @@
         <div v-for="(struct, index1) in paperStruct" :key="index1" class="section">
           <div class="title">{{struct.title}} ({{struct.totalScore}}分)</div>
           <div class="list">
-            <div v-for="(_, index2) in new Array(struct.questionCount)" :key="index2" :class="itemClass(index1, index2)">
-              <router-link :to="{ path: '/online-exam/exam/' + $route.params.examId, query: { 'examRecordId': $route.query.examRecordId, 'examQuestionId': getQuestionNum(index1, index2) }}">{{index2+1}}</router-link>
-            </div>
+            <template v-for="(_, index2) in new Array(struct.questionCount)">
+              <template v-if="isSelectedQuestion(index1, index2)">
+                <div :key="index2" :class="itemClass(index1, index2)">
+                  <router-link :to="{ path: '/online-exam/exam/' + $route.params.examId, query: { 'examRecordId': $route.query.examRecordId, 'examQuestionId': getQuestionNum(index1, index2).id }}">{{index2+1}}</router-link>
+                </div>
+              </template>
+            </template>
           </div>
         </div>
       </template>
@@ -17,6 +21,9 @@
 </template>
 
 <script>
+import { createNamespacedHelpers } from "vuex";
+const { mapState, mapMutations } = createNamespacedHelpers("examHomeModule");
+
 export default {
   name: "QuestionNavView",
   data() {
@@ -40,18 +47,36 @@ export default {
         total += this.paperStruct[i].questionCount;
       }
       // console.log(section, index, total + index);
-      return this.validQuestions[total + index].id;
+      return this.validQuestions[total + index];
+    },
+    isSelectedQuestion(index1, index2) {
+      let q = this.getQuestionNum(index1, index2);
+
+      if (this.questionFilterType === "ALL") return true;
+      else if (this.questionFilterType === "ANSWERED" && q.stuAnswer) {
+        return true;
+      } else if (this.questionFilterType === "SIGNED" && q.isSign) {
+        return true;
+      } else if (this.questionFilterType === "UNANSWERED" && !q.stuAnswer) {
+        return true;
+      }
+      return false;
     },
     itemClass(index1, index2) {
+      const isCurrentQuestion =
+        this.$route.query.examQuestionId ==
+        this.getQuestionNum(index1, index2).id; // 故意用的 ==
       return {
         item: true,
-        "current-question":
-          this.$route.query.examQuestionId ==
-          this.getQuestionNum(index1, index2) // 故意用的 ==
+        "current-question": isCurrentQuestion,
+        "star-question": this.getQuestionNum(index1, index2).isSign,
+        "is-answered": this.getQuestionNum(index1, index2).stuAnswer != null
       };
     }
   },
-  computed: {}
+  computed: {
+    ...mapState(["questionFilterType"])
+  }
 };
 </script>
 
@@ -82,7 +107,22 @@ export default {
   margin-right: 5px;
   margin-bottom: 5px;
 }
+.is-answered {
+  background-color: #13bb8a;
+}
+.is-answered > a {
+  color: white;
+}
 .current-question {
   background-color: white;
 }
+.current-question > a {
+  color: black;
+}
+.star-question {
+  background-color: #ffcc00;
+}
+.star-question > a {
+  color: white;
+}
 </style>

+ 16 - 0
src/features/OnlineExam/Examing/QuestionView.vue

@@ -1,5 +1,9 @@
 <template>
   <div v-if="question && examQuestion" class="question-view">
+    <div class="sign-button">
+      <!-- <i-button :icon="examQuestion.isSign ? 'ios-star':'ios-star-outline'" style="color: yellow" size="large" @click="toggleSign"></i-button> -->
+      <Icon :type="examQuestion.isSign ? 'ios-star':'ios-star-outline'" :style="{color: examQuestion.isSign ? '#ffcc00' : 'black'}" class="star" @click="toggleSign" />
+    </div>
     <template v-if="question.questionType === 'SINGLE_ANSWER_QUESTION'">
       <single-question-view :question="question" :examQuestion="examQuestion" />
     </template>
@@ -118,6 +122,12 @@ export default {
       }
 
       this.question = question;
+    },
+    async toggleSign() {
+      await this.$http.put("/api/exam_question/" + this.examQuestion.id, {
+        isSign: !this.examQuestion.isSign
+      });
+      this.examQuestion.isSign = !this.examQuestion.isSign;
     }
   },
   watch: {
@@ -142,4 +152,10 @@ export default {
   font-size: 16px;
   text-align: left;
 }
+.star {
+  font-size: 36px;
+}
+.star:hover {
+  cursor: pointer;
+}
 </style>

+ 11 - 2
src/features/OnlineExam/OnlineExamFaceCheckModal.vue

@@ -94,11 +94,20 @@ export default {
           title: "郑重承诺",
           content:
             "我承诺由本人参加考试,并且同意接受考试监控系统信息审核,一经发现作弊,立即取消本门课程考试成绩。",
-          onOk: () => this.$router.push("/online-practice")
+          onOk: () =>
+            this.$router.push(
+              `/online-exam/exam/${
+                this.course.onlineBatchId
+              }/overview?stuExamInfoId=${this.course.stuExamInfoId}`
+            )
         });
         return;
       }
-      this.$router.push("/online-practice");
+      this.$router.push(
+        `/online-exam/exam/${
+          this.course.onlineBatchId
+        }/overview?stuExamInfoId=${this.course.stuExamInfoId}`
+      );
     }
   },
   components: {

+ 4 - 1
src/store.js

@@ -5,7 +5,7 @@ Vue.use(Vuex);
 
 const examHomeModule = {
   namespaced: true,
-  state: { faceCheckModalOpen: false },
+  state: { faceCheckModalOpen: false, questionFilterType: "ALL" },
   mutations: {
     toggleFaceCheckModal(state, open) {
       if (open === undefined) {
@@ -13,6 +13,9 @@ const examHomeModule = {
       } else {
         state.faceCheckModalOpen = open;
       }
+    },
+    updateQuestionFilter(state, type) {
+      state.questionFilterType = type;
     }
   },
   actions: {},