Переглянути джерело

前后端联通:活体检测

Michael Wang 6 роки тому
батько
коміт
c5778708ac

+ 5 - 2
src/components/FaceRecognition/FaceRecognition.vue

@@ -3,7 +3,7 @@
     <video id="video" ref="video" :width="width" :height="height" autoplay>
     </video>
     <div v-if="showRecognizeButton" style="position: absolute; width: 400px; text-align: center; margin-top: -50px; color: #232323;">
-      <span class="verify-button" @click="snap">{{msg}}</span>
+      <button class="verify-button" @click="snap" :disabled="disableSnap">{{msg}}</button>
     </div>
   </div>
 </template>
@@ -16,7 +16,7 @@ const { mapState, mapMutations } = createNamespacedHelpers("examingHomeModule");
 export default {
   name: "FaceRecognition",
   data() {
-    return { hide: true, msg: "开始识别" };
+    return { disableSnap: false, msg: "开始识别" };
   },
   props: {
     width: String,
@@ -82,6 +82,7 @@ export default {
       video && video.play();
     },
     async snap() {
+      this.disableSnap = true;
       this.msg = "拍照中...";
       const captureBlob = await this.getSnapShot();
       this.msg = "上传照片中...";
@@ -90,6 +91,8 @@ export default {
       await this.faceCompareSync(captureFilePath);
       const video = this.$refs.video;
       video && video.play();
+      this.msg = "开始识别";
+      this.disableSnap = false;
     },
     async getSnapShot() {
       return new Promise(resolve => {

+ 7 - 0
src/features/Login/Login.vue

@@ -97,6 +97,13 @@ export default {
     }
     window.localStorage.removeItem("token");
     window.localStorage.removeItem("key");
+    if (localStorage.getItem("user-for-reload")) {
+      this.loginForm.accountValue = JSON.parse(
+        localStorage.getItem("user-for-reload")
+      ).studentCode;
+      this.loginForm.password =
+        process.env.NODE_ENV === "production" ? "" : "180613";
+    }
   },
   methods: {
     ...mapMutations(["updateUser"]),

+ 22 - 2
src/features/OnlineExam/Examing/ExamingHome.vue

@@ -18,8 +18,8 @@
         <FaceRecognition v-if="faceEnable" width="100%" height="100%" :showRecognizeButton="false" />
       </div>
     </div>
-    <Modal v-if="showFaceId" v-model="showFaceId" :mask-closable="false" :closable="false">
-      <FaceId />
+    <Modal v-model="showFaceId" :mask-closable="false" :closable="false">
+      <FaceId v-if="showFaceId" @closeFaceId="closeFaceId" />
       <p slot="footer">
       </p>
     </Modal>
@@ -55,6 +55,23 @@ export default {
       this.toggleSnapNow();
     }, 5 * 1000); // 一分钟后抓拍
 
+    if (/^\d+$/.test(this.$route.query.faceVerifyMinute)) {
+      setTimeout(() => {
+        this.toggleSnapNow();
+        this.$Message.info("30秒后开始活体检测");
+      }, this.$route.query.faceVerifyMinute * 60 * 1000 - 30 * 1000); // 活体检测提醒
+      setTimeout(() => {
+        this.showFaceId = true;
+      }, this.$route.query.faceVerifyMinute * 60 * 1000); // 定时做活体检测
+      // }, 1 * 1000); // 定时做活体检测
+    }
+    // for test
+    // setTimeout(() => {
+    //   this.showFaceId = false;
+    //   // this.$Modal.remove();
+    //   // }, this.$route.query.faceVerifyMinute * 60 * 1000); // 定时做活体检测
+    // }, 5 * 1000); // 定时做活体检测
+
     this.$http
       .get(
         "/api/ecs_exam_work/exam/examOrgProperty/" +
@@ -172,6 +189,9 @@ export default {
       next && next();
       if (!this.exam) return;
     },
+    closeFaceId() {
+      this.showFaceId = false;
+    },
     async answerAllQuestions() {
       const answers = this.examQuestionList.filter(eq => eq.dirty).map(eq => {
         return Object.assign(

+ 7 - 3
src/features/OnlineExam/Examing/FaceId.vue

@@ -41,6 +41,7 @@ export default {
     };
   },
   mounted() {
+    console.debug("startFaceVerify");
     this.startFaceVerify();
   },
   methods: {
@@ -105,7 +106,6 @@ export default {
       };
       //定时事件,如果1分钟内未完成人脸检测,执行内部程序
       var faceIdTime = setTimeout(() => {
-        this.$Modal.remove();
         ws.close();
         var that = this;
         this.$http
@@ -159,6 +159,7 @@ export default {
           }
         } else if (receivedMsg.verifyCount >= 2) {
           if (receivedMsg.verifyResult == "VERIFY_SUCCESS") {
+            // FIXME: 什么逻辑?
             this.$Message.info("人脸检测成功,请继续完成考试", { time: 5000 });
             this.faceTestEndHandle("SUCCESS");
           } else {
@@ -177,7 +178,7 @@ export default {
         if (response.data.indexOf("verifyResult") > -1) {
           var receivedMsg = JSON.parse(response.data);
           clearTimeout(faceIdTime);
-          this.logout();
+          this.$emit("closeFaceId");
           ws.close();
           this.faceTestEnd(receivedMsg);
         }
@@ -221,7 +222,10 @@ export default {
           this.updateFaceVerify(iframeLoadMsg, null);
         } else if (iframeLoadMsg == "success") {
           clearInterval(iframeLoadTime);
-          this.iframeLoadSuccess();
+          // this.iframeLoadSuccess();
+          setTimeout(() => {
+            this.iframeLoadSuccess();
+          }, 300); // 延迟确保能删除footer
         }
       }, 500);
     }

+ 0 - 4
src/features/OnlineExam/OnlineExamFaceCheckModal.vue

@@ -52,10 +52,6 @@ export default {
       this.$Message.error("没有底照");
       return;
     }
-
-    // FIXME: 将faceEnable和faceCheck放到global的state中
-    // this.faceEnable = sysRes.data.faceEnable; // 模拟练习?
-    // this.faceCheck = sysRes.data.faceCheck; // 考试
   },
   computed: {
     ...mapState(["faceCheckModalOpen"])

+ 6 - 6
src/features/OnlineExam/OnlineExamHome.vue

@@ -22,20 +22,20 @@ export default {
     };
   },
   async mounted() {
-    // TODO: 断点续考
-    // check examRecord
-    // go to /online-exam/exam/:id/start
-    // Promise.all
-
+    // 断点续考
     const examingRes = (await this.$http.get(
       "/api/ecs_oe_student/examControl/checkExamInProgress"
     )).data;
 
     if (examingRes) {
+      console.debug(examingRes);
       this.$router.push(
         `/online-exam/exam/${examingRes.examId}/examRecordData/${
           examingRes.examRecordDataId
-        }/order/1`
+        }/order/1` +
+          (examingRes.faceVerifyMinute
+            ? `?faceVerifyMinute=${examingRes.faceVerifyMinute}`
+            : "")
       );
       return;
     }

+ 11 - 6
src/features/OnlineExam/OnlineExamOverview.vue

@@ -7,10 +7,8 @@
         <p v-html="beforeExamRemark"></p>
         <!-- <p>{{"测试".repeat(500)}}</p> -->
       </div>
-      <!-- data-ui-sref="exam.start({examRecordDataId: startInfo.id,examStudentId:stateParams.examStudentId,examMins:startInfo.paperMins,examId:examInfo.id,faceVerifyMinute:startInfo.faceVerifyMinute})" -->
-
-      <Button class="qm-primary-button" :disabled="remainTime > 110" @click="goToPaper" style="display: inline-block; width: 100%;">
-        {{ remainTime > 110 ? '强制阅读' : '开始答题'}}(倒计时:{{remainTimeFormatted}})</Button>
+      <Button class="qm-primary-button" :disabled="isForceRead" @click="goToPaper" style="display: inline-block; width: 100%;">
+        {{ isForceRead ? '强制阅读' : '开始答题'}}(倒计时:{{remainTimeFormatted}})</Button>
     </div>
 
     <div class="exam-detail">
@@ -41,13 +39,17 @@
 <script>
 import moment from "moment";
 
+const TOTAL_READ_TIME = 120;
+const FORCE_READ_TIME = process.env.NODE_ENV === "production" ? 10 : 1;
+
 export default {
   data() {
     return {
       beforeExamRemark: null,
       startInfo: null,
       paperStruct: null,
-      remainTime: 120
+      remainTime: TOTAL_READ_TIME,
+      isForceRead: this.remainTime > FORCE_READ_TIME
     };
   },
   async mounted() {
@@ -123,7 +125,10 @@ export default {
       this.$router.push(
         `/online-exam/exam/${this.$route.params.examId}/examRecordData/${
           this.examRecordDataId
-        }/order/1`
+        }/order/1` +
+          (this.startInfo.faceVerifyMinute
+            ? `?faceVerifyMinute=${this.startInfo.faceVerifyMinute}`
+            : "")
       );
       window.clearInterval(this.intervalId);
     }