Selaa lähdekoodia

修改答题、考试逻辑,新增vuex用户信息

yellowtaotao 4 vuotta sitten
vanhempi
commit
b0f2546192

+ 3 - 1
src/store/index.js

@@ -4,6 +4,7 @@ import createPersistedState from "vuex-persistedstate"; // 持久化存储插件
 
 import exam from "./modules/exam";
 import answer from "./modules/answer";
+import user from "./modules/user";
 
 Vue.use(Vuex);
 
@@ -48,6 +49,7 @@ export default new Vuex.Store({
   },
   modules: {
     exam,
-    answer
+    answer,
+    user
   }
 });

+ 1 - 1
src/store/modules/answer.js

@@ -4,7 +4,7 @@ const answer = {
     examItemQuestionsList: [] // 单个考试的试题信息
   },
   mutations: {
-    updateAnswerStore(state, { field, value }) {
+    updateAnswerItemStore(state, { field, value }) {
       state[field] = value;
     }
   },

+ 1 - 1
src/store/modules/exam.js

@@ -4,7 +4,7 @@ const exam = {
     examItemQuestionsList: [] // 单个考试的试题信息
   },
   mutations: {
-    updateExamStore(state, { field, value }) {
+    updateExamItemStore(state, { field, value }) {
       state[field] = value;
     }
   },

+ 13 - 0
src/store/modules/user.js

@@ -0,0 +1,13 @@
+const user = {
+  state: {
+    userInfo: null // 用户信息
+  },
+  mutations: {
+    updateUserItemStore(state, { field, value }) {
+      state[field] = value;
+    }
+  },
+  actions: {}
+};
+
+export default user;

+ 1 - 1
src/views/home/answer/page-answer-recruit-result.vue

@@ -60,7 +60,7 @@ export default {
     },
     // 操作:再来一组
     handleGoOnFun() {
-      this.$store.commit("updateAnswerStore", {
+      this.$store.commit("updateAnswerItemStore", {
         field: "answerRecruitId",
         value: "daily-questions" // item.id
       });

+ 50 - 26
src/views/home/answer/page-answer-recruit.vue

@@ -60,12 +60,15 @@
           :class="{ 'exam-question-gapFilling-false': answerStatus === 2 }"
         >
           <textarea
-            ref="questionInputRef"
-            v-model="inputValue"
+            v-for="(item, index) in examQuestionList[answerIndex]
+              .questionInputNum"
+            :key="index"
+            :ref="'questionInputRef' + index"
+            v-model="inputValue[index]"
             :disabled="[1, 2].includes(answerStatus)"
             maxlength="200"
-            rows="5"
-            @change="handleExamQuestionOptionsItemFun(inputValue)"
+            rows="1"
+            @change="handleExamQuestionOptionsItemFun(inputValue, index)"
           />
         </div>
         <div class="exam-question-button-box">
@@ -107,22 +110,13 @@
 </template>
 
 <script>
-import { Toast } from "vant";
+import { Dialog } from "vant";
 import { mapState } from "vuex";
 export default {
   name: "page-exam-item-doing",
   components: {},
   data() {
     return {
-      userInfo: {
-        id: "testuser-ztdxxDGdNj",
-        username: "testuser-ztdxxDGdNj",
-        firstName: "string",
-        lastName: "string",
-        email: "string",
-        encodedPassword: "string",
-        userStatus: "suspended"
-      },
       questionType: {
         singleChoice: "DanXuan", // 单选题
         multipleChoice: "DuoXuan", // 多选题
@@ -131,7 +125,7 @@ export default {
       examQuestionList: [], // 试题列表
       answerIndex: null, // 当前试题的下标索引
       answerValue: [], // 当前试题的所答
-      inputValue: "", // 填空题时输入框的值
+      inputValue: [], // 填空题时输入框的值
       isInited: false, // 是否已初始化完毕
       answerTime: {
         startTime: 0,
@@ -146,7 +140,8 @@ export default {
   watch: {},
   computed: {
     ...mapState({
-      answerRecruitId: state => state.answer.answerRecruitId
+      answerRecruitId: state => state.answer.answerRecruitId,
+      userInfo: state => state.user.userInfo
     })
   },
   methods: {
@@ -157,7 +152,12 @@ export default {
     // 方法:开始考试
     examStartFun() {
       if (!this.answerRecruitId) {
-        Toast("数据出错,请重新从上一级页面进入");
+        Dialog.alert({
+          message: "试题信息有误,请重新进入当前页面",
+          theme: "round-button"
+        }).then(() => {
+          this.$router.back();
+        });
         return;
       }
       this.$store.commit("toggleLoading", true);
@@ -219,13 +219,15 @@ export default {
       );
       httpResultDataItme.typeTxt = results.typeTxt;
       httpResultDataItme.questionContent = results.questionContent;
+      httpResultDataItme.questionInputNum = results.questionInputNum;
       httpResultDataItme.userAnswer = [];
       return httpResultDataItme;
     },
-    // 方法:过滤试题的类型
+    // 方法:过滤试题的类型、填空题的题目、填空题的回答框个数
     formatQuestionType(type, content) {
       let typeTxt = "";
-      let questionContent = null;
+      let questionContent = "";
+      let questionInputNum = 0;
       switch (type) {
         case this.questionType.singleChoice:
           typeTxt = "单选题";
@@ -235,16 +237,30 @@ export default {
           break;
         case this.questionType.gapFilling:
           typeTxt = "填空题";
+          console.log(content);
+          questionInputNum = this.getPlaceholderCount(content);
           questionContent = content.replace("$PH$", "()");
+          console.log(questionContent, questionInputNum);
           break;
         default:
           break;
       }
       return {
         typeTxt: typeTxt,
-        questionContent: questionContent
+        questionContent: questionContent,
+        questionInputNum: questionInputNum
       };
     },
+    // 方法:统计字符串中包含的特定字符个数
+    getPlaceholderCount(strSource) {
+      // 统计字符串中包含{}或{xxXX}的个数
+      let countNum = 0;
+      strSource.replace("$PH$", function(m, i) {
+        // m为找到的{xx}元素、i为索引
+        countNum++;
+      });
+      return countNum;
+    },
     // 方法:过滤正确答案的选项的编号
     formatQuestionFinalAnswerIndex(finalAnswer) {
       let txt = "";
@@ -285,14 +301,14 @@ export default {
           break;
         case this.questionType.gapFilling:
           this.answerValue = [...new Set(item.userAnswer)];
-          this.inputValue = this.answerValue[0] || "";
+          this.inputValue = this.answerValue || [];
           break;
         default:
           break;
       }
     },
     // 操作:作答了某个选项
-    handleExamQuestionOptionsItemFun(value) {
+    handleExamQuestionOptionsItemFun(value, index) {
       // 答错了的不允许修改
       if (this.answerStatus === 2) {
         return;
@@ -323,8 +339,8 @@ export default {
           break;
         // 填空题
         case this.questionType.gapFilling:
-          this.$refs.questionInputRef.blur(); // 清除input的焦点
-          this.answerValue = [value];
+          this.$refs["questionInputRef" + index][0].blur(); // 清除input的焦点
+          this.answerValue = value;
           break;
         default:
           break;
@@ -332,7 +348,11 @@ export default {
     },
     // 操作:确定
     handleSureFun() {
+      if (!this.examQuestionList.length) {
+        return;
+      }
       this.examQuestionList[this.answerIndex].userAnswer = this.answerValue;
+      console.log(this.examQuestionList[this.answerIndex].userAnswer);
       this.answerStatus = this.getAnswerItemResultFun(
         this.examQuestionList[this.answerIndex]
       )
@@ -376,7 +396,7 @@ export default {
       this.answerTime.endTime = curTime; // 赋值开始时间
       this.$store.commit("toggleLoading", true);
       let grades = this.getUserExamAllPointsFun(); // 方法:计算成绩
-      let answers = this.getUserExamAllAnswersFun(); // 方法:获取题目的答案
+      let answers = this.getUserExamAllAnswersFun(); // 方法:获取当前用户所有题目作答的答案
       let params = {
         user: this.userInfo,
         exam: {
@@ -412,6 +432,9 @@ export default {
         })
         .catch(() => {
           this.$store.commit("toggleLoading", false);
+          Dialog.close();
+          Dialog({ message: "结束考试异常,请联系系统管理员" });
+          this.$router.back();
         });
     },
     // 方法:计算成绩
@@ -436,7 +459,8 @@ export default {
     getUserExamAllAnswersFun() {
       let answers = [];
       this.examQuestionList.forEach((item, index) => {
-        answers[index] = item.userAnswer.join(",");
+        // answers[index] = item.userAnswer;
+        answers[index] = item.userAnswer.join("||");
       });
       return answers;
     },

+ 1 - 1
src/views/home/answer/page-answer.vue

@@ -106,7 +106,7 @@ export default {
       //   default:
       //     break;
       // }
-      this.$store.commit("updateAnswerStore", {
+      this.$store.commit("updateAnswerItemStore", {
         field: "answerRecruitId",
         value: "daily-questions" // item.id
       });

+ 0 - 23
src/views/home/exam/page-exam-item-detail.vue

@@ -1,15 +1,6 @@
 <template>
   <div class="page-exam-item-detial-box">
     <van-nav-bar title="考试详情" />
-    <input
-      ref="uploadUserInput"
-      type="file"
-      name="file"
-      accept="image/*"
-      capture="user"
-      @change="takePhoto"
-      style="display: none;"
-    />
     <div class="exam-item-detial-div">
       <div class="exam-item-detial">
         <div class="exam-item-detial-title">{{ examItemData.name }}</div>
@@ -44,7 +35,6 @@
 </template>
 
 <script>
-import { Toast } from "vant";
 import { mapState } from "vuex";
 export default {
   name: "page-exam-item-detial",
@@ -72,18 +62,6 @@ export default {
     })
   },
   methods: {
-    takePhoto(event) {
-      let file = event.target.files[0]; // 获取文件对象
-      if (file) {
-        // let fd = new FormData(); // 构造formdata对象
-        // fd.append("file", file); // 向formdata里面存放键值对存放图片文件
-        // fd.append("userId", this.userInfo.id); // 向formdata里面存放用户的ID
-        // console.log(fd);
-        this.$router.replace({ name: "Exam" });
-      } else {
-        Toast("请完成拍照后,再进入考试");
-      }
-    },
     // 查询:当前考试的信息
     getExamItemDataFun() {
       this.examItemData = this.examItem;
@@ -104,7 +82,6 @@ export default {
     },
     // 操作:开始考试
     handleStartExamFun() {
-      // this.$refs.uploadUserInput.click(); // 调用拍照工具
       this.$router.replace({ name: "Exam" });
     },
     // 操作:返回

+ 113 - 33
src/views/home/exam/page-exam-item-doing.vue

@@ -29,7 +29,7 @@
     <div class="exam-question-div">
       <div class="exam-question-card">
         <!-- 标题、分数、题页 -->
-        <div class="exam-question-head">
+        <div v-if="examQuestionList.length" class="exam-question-head">
           <div class="exam-question-head-left">
             <div class="exam-question-head-left-icon"></div>
             <div class="exam-question-head-left-txt">
@@ -48,7 +48,7 @@
           </div>
         </div>
         <!-- 描述 -->
-        <div class="exam-question-describe">
+        <div v-if="examQuestionList.length" class="exam-question-describe">
           {{
             examQuestionList[answerIndex].questionContent ||
               examQuestionList[answerIndex].content
@@ -58,8 +58,11 @@
         <!-- 单选题、多选题的选项区域 -->
         <div
           v-if="
-            examQuestionList[answerIndex].type === questionType.singleChoice ||
-              examQuestionList[answerIndex].type === questionType.multipleChoice
+            examQuestionList.length &&
+              (examQuestionList[answerIndex].type ===
+                questionType.singleChoice ||
+                examQuestionList[answerIndex].type ===
+                  questionType.multipleChoice)
           "
           class="exam-question-options"
         >
@@ -77,15 +80,21 @@
         </div>
         <!-- 填空题的答题区域 -->
         <div
-          v-if="examQuestionList[answerIndex].type === questionType.gapFilling"
+          v-if="
+            examQuestionList.length &&
+              examQuestionList[answerIndex].type === questionType.gapFilling
+          "
           class="exam-question-gapFilling"
         >
           <textarea
-            ref="questionInputRef"
-            v-model="inputValue"
+            v-for="(item, index) in examQuestionList[answerIndex]
+              .questionInputNum"
+            :key="index"
+            :ref="'questionInputRef' + index"
+            v-model="inputValue[index]"
             maxlength="200"
             rows="1"
-            @change="handleExamQuestionOptionsItemFun(inputValue)"
+            @change="handleExamQuestionOptionsItemFun(inputValue, index)"
           />
         </div>
         <div class="exam-question-button-box">
@@ -93,7 +102,6 @@
             class="exam-question-button"
             type="primary"
             color="#FE6347"
-            :disabled="this.timeDiff === '00:00:00'"
             @click="handleSureFun"
             >确定</van-button
           >
@@ -109,6 +117,16 @@
         </div>
       </div>
     </div>
+    <!-- 拍照用 -->
+    <input
+      ref="uploadUserInput"
+      type="file"
+      name="file"
+      accept="image/*"
+      capture="user"
+      @change="startTakePhotoFUn"
+      style="display: none;"
+    />
   </div>
 </template>
 
@@ -120,15 +138,6 @@ export default {
   components: {},
   data() {
     return {
-      userInfo: {
-        id: "testuser-ztdxxDGdNj",
-        username: "testuser-ztdxxDGdNj",
-        firstName: "string",
-        lastName: "string",
-        email: "string",
-        encodedPassword: "string",
-        userStatus: "suspended"
-      },
       questionType: {
         singleChoice: "DanXuan", // 单选题
         multipleChoice: "DuoXuan", // 多选题
@@ -140,7 +149,7 @@ export default {
       examQuestionList: [], // 试题列表
       answerIndex: null, // 当前试题的下标索引
       answerValue: [], // 当前试题的所答
-      inputValue: "", // 填空题时输入框的值
+      inputValue: [], // 填空题时输入框的值
       isInited: false, // 是否已初始化完毕
       answerTime: {
         startTime: 0,
@@ -159,14 +168,20 @@ export default {
   watch: {},
   computed: {
     ...mapState({
-      examItem: state => state.exam.examItem
+      examItem: state => state.exam.examItem,
+      userInfo: state => state.user.userInfo
     })
   },
   methods: {
     // 初始化数据信息
     initDataFun() {
       if (!this.examItem) {
-        Dialog({ message: "试题信息有误,请重新进入当前页面" });
+        Dialog.alert({
+          message: "试题信息有误,请重新进入当前页面",
+          theme: "round-button"
+        }).then(() => {
+          this.$router.back();
+        });
         return;
       }
       this.examStartFun(); // 方法:开始考试
@@ -229,6 +244,7 @@ export default {
       );
       httpResultDataItme.typeTxt = results.typeTxt;
       httpResultDataItme.questionContent = results.questionContent;
+      httpResultDataItme.questionInputNum = results.questionInputNum;
       httpResultDataItme.userAnswer = [];
       return httpResultDataItme;
     },
@@ -291,7 +307,7 @@ export default {
         if (diffTime <= 0) {
           clearInterval(_self.interval);
           _self.timeDiff = "00:00:00";
-          this.handleSubmitFun(); // 自动交卷
+          this.examsEndFun(true); // 自动交卷
           return;
         }
         let hours = Math.floor(diffTime / (1000 * 60 * 60));
@@ -309,10 +325,11 @@ export default {
         }
       }, 1000);
     },
-    // 方法:过滤试题的类型、填空题的题目
+    // 方法:过滤试题的类型、填空题的题目、填空题的回答框个数
     formatQuestionType(type, content) {
       let typeTxt = "";
       let questionContent = "";
+      let questionInputNum = 0;
       switch (type) {
         case this.questionType.singleChoice:
           typeTxt = "单选题";
@@ -322,16 +339,30 @@ export default {
           break;
         case this.questionType.gapFilling:
           typeTxt = "填空题";
-          this.questionContent = content.replace("$PH$", "()");
+          console.log(content);
+          questionInputNum = this.getPlaceholderCount(content);
+          questionContent = content.replace("$PH$", "()");
+          console.log(questionContent, questionInputNum);
           break;
         default:
           break;
       }
       return {
         typeTxt: typeTxt,
-        questionContent: questionContent
+        questionContent: questionContent,
+        questionInputNum: questionInputNum
       };
     },
+    // 方法:统计字符串中包含的特定字符个数
+    getPlaceholderCount(strSource) {
+      // 统计字符串中包含{}或{xxXX}的个数
+      let thisCount = 0;
+      strSource.replace("$PH$", function(m, i) {
+        // m为找到的{xx}元素、i为索引
+        thisCount++;
+      });
+      return thisCount;
+    },
     // 操作:点击了某个题序
     handleExamQuestionItemFun(item, index) {
       if (this.answerIndex === index || !this.examQuestionList.length) {
@@ -345,14 +376,14 @@ export default {
           break;
         case this.questionType.gapFilling:
           this.answerValue = [...new Set(item.userAnswer)];
-          this.inputValue = this.answerValue[0] || "";
+          this.inputValue = this.answerValue || [];
           break;
         default:
           break;
       }
     },
     // 操作:作答了某个选项
-    handleExamQuestionOptionsItemFun(value) {
+    handleExamQuestionOptionsItemFun(value, index) {
       let answerQuestionType = this.examQuestionList[this.answerIndex].type;
       switch (answerQuestionType) {
         // 单选题
@@ -379,8 +410,8 @@ export default {
           break;
         // 填空题
         case this.questionType.gapFilling:
-          this.$refs.questionInputRef.blur(); // 清除input的焦点
-          this.answerValue = [value];
+          this.$refs["questionInputRef" + index][0].blur(); // 清除input的焦点
+          this.answerValue = value;
           break;
         default:
           break;
@@ -396,17 +427,49 @@ export default {
         this.examQuestionList[nextIndex],
         nextIndex
       );
+      if (nextIndex + 1 > 3) {
+        let isPrimeVal = this.isPrime(nextIndex + 1);
+        if (isPrimeVal) {
+          this.$refs.uploadUserInput.click(); // 调用拍照工具
+        }
+      }
     },
     // 操作:确定
     handleSureFun() {
+      if (!this.examQuestionList.length) {
+        return;
+      }
       this.examQuestionList[this.answerIndex].userAnswer = this.answerValue;
       this.handleNextFun();
     },
+    // 方法:判断一个数是否为质数
+    isPrime(num) {
+      // 不是数字或者数字小于2
+      if (typeof num !== "number" || !Number.isInteger(num)) {
+        // Number.isInterget 判断是否为整数
+        return false;
+      }
+      // 2是质数
+      if (num === 2) {
+        return true;
+      } else if (num % 2 === 0) {
+        // 排除偶数
+        return false;
+      }
+      // 依次判断是否能被奇数整除,最大循环为数值的开方
+      var squareRoot = Math.sqrt(num);
+      // 因为2已经验证过,所以从3开始;且已经排除偶数,所以每次加2
+      for (var i = 3; i <= squareRoot; i += 2) {
+        if (num % i === 0) {
+          return false;
+        }
+      }
+      return true;
+    },
     // 操作:交卷 isAuto:true(时间到了的自动交卷)  false(手动交卷)
     handleSubmitFun() {
       let isAuto = this.timeDiff === "00:00:00";
       if (isAuto) {
-        Dialog({ message: "考试时间已到,该考试已结束" });
         this.examsEndFun(isAuto);
       } else {
         Dialog.confirm({
@@ -424,11 +487,11 @@ export default {
     // 方法:结束考试
     examsEndFun(isAuto) {
       if (isAuto) {
-        Toast("考试时间已到");
+        Dialog({ message: "考试时间已到,该考试已结束,并自动交卷" });
       }
       this.$store.commit("toggleLoading", true);
       let grades = this.getUserExamAllPointsFun(); // 方法:计算成绩
-      let answers = this.getUserExamAllAnswersFun(); // 方法:获取题目的答案
+      let answers = this.getUserExamAllAnswersFun(); // 方法:获取当前用户所有题目作答的答案
       let params = {
         user: this.userInfo,
         exam: {
@@ -461,6 +524,9 @@ export default {
         })
         .catch(() => {
           this.$store.commit("toggleLoading", false);
+          Dialog.close();
+          Dialog({ message: "结束考试异常,请联系系统管理员" });
+          this.$router.back();
         });
     },
     // 方法:计算成绩
@@ -485,9 +551,23 @@ export default {
     getUserExamAllAnswersFun() {
       let answers = [];
       this.examQuestionList.forEach((item, index) => {
-        answers[index] = item.userAnswer.join(",");
+        // answers[index] = item.userAnswer;
+        answers[index] = item.userAnswer.join("||");
       });
       return answers;
+    },
+    // 方法:开始考试
+    startTakePhotoFUn(event) {
+      let file = event.target.files[0]; // 获取文件对象
+      if (file) {
+        // let fd = new FormData(); // 构造formdata对象
+        // fd.append("file", file); // 向formdata里面存放键值对存放图片文件
+        // fd.append("userId", this.userInfo.id); // 向formdata里面存放用户的ID
+        // console.log(fd);
+        this.$router.replace({ name: "Exam" });
+      } else {
+        Toast("请完成拍照后,再进行考试");
+      }
     }
   }
 };

+ 1 - 1
src/views/home/exam/page-exam.vue

@@ -77,7 +77,7 @@ export default {
     },
     // 操作:点击了某个考试
     handleClickExamItemFun(item) {
-      this.$store.commit("updateExamStore", {
+      this.$store.commit("updateExamItemStore", {
         field: "examItem",
         value: item
       });

+ 12 - 0
src/views/home/index.vue

@@ -67,6 +67,18 @@ export default {
     };
   },
   created() {
+    this.$store.commit("updateUserItemStore", {
+      field: "userInfo",
+      value: {
+        id: "testuser-ztdxxDGdNj",
+        username: "testuser-ztdxxDGdNj",
+        firstName: "string",
+        lastName: "string",
+        email: "string",
+        encodedPassword: "string",
+        userStatus: "suspended"
+      }
+    });
     this.activeTabName = 0;
     this.currentComponents = this.tabbars[this.activeTabName].pageName;
   },