Ver Fonte

enable vuex; 显示人脸检测的modal

Michael Wang há 7 anos atrás
pai
commit
c3a50cf009

+ 9 - 7
src/components/MainLayout/MainLayout.vue

@@ -2,17 +2,17 @@
   <div class="main-layout">
     <header class="header qm-primary-text">
       <Poptip trigger="hover" width="300">
-        <span class="name-arrow">{{user.name}} &nbsp;
+        <span class="name-arrow">{{user.displayName}} &nbsp;
           <i class="ivu-icon ivu-icon-chevron-down" style="vertical-align: middle;"></i>
         </span>
         <div slot="content">
           <div class="info qm-primary-text">
             <div>学号</div>
-            <div>{{user.stuNum || 'mock'}}</div>
+            <div>{{user.studentCode}}</div>
             <div>身份证号</div>
-            <div>{{user.idNum || 'mock'}}</div>
+            <div>{{user.identityNumber}}</div>
             <div>学习中心</div>
-            <div>{{user.center || 'mock'}}</div>
+            <div>{{user.orgName}}</div>
             <div style="grid-column: span 2; place-self: center; width: 100%">
               <i-button class="qm-primary-button" long @click="goChangePwd">修改密码</i-button>
             </div>
@@ -47,12 +47,11 @@
 </template>
 
 <script>
+import { mapState } from "vuex";
 export default {
   name: "MainLayout",
   data() {
-    return {
-      user: window.wk_user
-    };
+    return {};
   },
   methods: {
     goChangePwd() {
@@ -61,6 +60,9 @@ export default {
     logout() {
       this.$router.push("/login");
     }
+  },
+  computed: {
+    ...mapState(["user"])
   }
 };
 </script>

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

@@ -0,0 +1,74 @@
+<template>
+  <Modal v-model="faceCheckModalOpen" width="900" :mask-closable="false" :closable="false">
+    <div slot="header" style="display: flex; justify-content: space-between; align-items: center;">
+      <div class="qm-title-text">人脸识别</div>
+      <Icon type="ios-close" class="qm-icon-button" size="24" @click="close" />
+    </div>
+    <div style="display: grid; grid-template-columns: 200px 400px 1fr; grid-gap: 5px;">
+      <div class="avatar">
+        <img :src="userPhoto" width="200px" alt="底照" />
+        <div class="avatar-info" style="text-align: center; margin-top: -50px; color: white;">
+          <!-- FIXME: 没有底照的逻辑 -->
+          <span style="background-color: rgba(0, 0, 0, 0.5); display: inline-block;padding: 6px 16px; border-radius: 6px;">我的底照</span>
+        </div>
+      </div>
+      <div class="camera">
+        <video id="video" width="400px" height="100%" autoplay>
+        </video>
+        <div class="avatar-info" style="text-align: center; margin-top: -50px; color: #232323;">
+          <span class="verify-button" style="font-size: 16px; background-color: #ffcc00; display: inline-block;padding: 6px 16px; border-radius: 6px;">开始识别</span>
+        </div>
+      </div>
+      <div class="verify-desc">
+        <h4 class="font-thin m-t-none m-b text-info">操作提示:</h4>
+        <p class="text-sm m-b-xs">1.请先确保摄像头设备已连接并能正常工作;</p>
+        <p class="text-sm m-b-xs">2.请保持光源充足,不要逆光操作;</p>
+        <p class="text-sm m-b-xs">3.请保证脸部正面面向摄像头,并适当调整姿势保证整个脸部能够进入左侧识别画面;</p>
+        <p class="text-sm m-b-xs">4.系统识别通过后,将自动跳转进入考试界面;</p>
+      </div>
+    </div>
+    <div slot="footer">
+    </div>
+  </Modal>
+</template>
+
+<script>
+import { createNamespacedHelpers } from "vuex";
+const { mapState, mapMutations } = createNamespacedHelpers("examHomeModule");
+
+export default {
+  data() {
+    return { userPhoto: null };
+  },
+  props: {
+    open: Boolean
+  },
+  async created() {
+    const res = await this.$http.get(
+      "/api/ecs_core/studentFaceInfo/identityNumber",
+      {
+        params: {
+          identityNumber: this.$store.state.user.identityNumber,
+          orgId: this.$store.state.user.rootOrgId
+        }
+      }
+    );
+    this.userPhoto = res.data.student.photoPath;
+    // FIXME: 以后api返回的是绝对路径
+    if (this.userPhoto.startsWith("http") === false) {
+      this.userPhoto =
+        "https://ecs-test-static.qmth.com.cn/student_base_photo/" +
+        this.userPhoto;
+    }
+  },
+  computed: {
+    ...mapState(["faceCheckModalOpen"])
+  },
+  methods: {
+    ...mapMutations(["toggleFaceCheckModal"]),
+    close() {
+      this.toggleFaceCheckModal(false);
+    }
+  }
+};
+</script>

+ 1 - 7
src/features/OnlineExam/OnlineExamHome.vue

@@ -29,13 +29,7 @@ export default {
 
     const res = await this.$http.get("/api/online_exam_course");
 
-    this.courses = res.data.map(c => ({
-      courseName: c.courseName,
-      specialtyName: c.specialtyName,
-      startTime: c.startTime,
-      endTime: c.endTime,
-      allowExamCount: c.allowExamCount
-    }));
+    this.courses = res.data;
   },
   components: {
     "ecs-online-list": EcsOnlineList

+ 17 - 2
src/features/OnlineExam/OnlineExamList.vue

@@ -16,7 +16,7 @@
           <td>{{ course.startTime }} <br> ~ <br> {{ course.endTime }}</td>
           <td>{{ course.allowExamCount }}</td>
           <td style="min-width: 180px">
-            <template v-if="!course.isvalid">
+            <template v-if="course.isvalid">
               <div style="display: grid; grid-template-columns: 1fr 1fr; grid-gap: 10px">
                 <i-button class="qm-primary-button" :disabled="!courseInBetween(course)" @click="enterExam(course)">进入考试</i-button>
 
@@ -30,13 +30,19 @@
         </tr>
       </tbody>
     </table>
+
+    <OnlineExamFaceCheckModal :open="faceCheckModalOpen"></OnlineExamFaceCheckModal>
   </div>
 </template>
 
 <script>
+import { createNamespacedHelpers } from "vuex";
 import OnlineExamResultList from "./OnlineExamResultList.vue";
+import OnlineExamFaceCheckModal from "./OnlineExamFaceCheckModal.vue";
 import moment from "moment";
 
+const { mapState, mapMutations } = createNamespacedHelpers("examHomeModule");
+
 export default {
   name: "EcsOnlineList",
   data() {
@@ -53,6 +59,7 @@ export default {
     clearInterval(this.intervalID);
   },
   methods: {
+    ...mapMutations(["toggleFaceCheckModal"]),
     getNow() {
       this.now = new Date();
     },
@@ -69,10 +76,14 @@ export default {
         // if 人脸识别失败 && 考试开启强制人脸识别 return
         // if 人脸识别失败 && 考试未开启强制人脸识别
         //    让学生手动确认进入考试,若取消,则返回
+        this.toggleFaceCheckModal(true);
       }
 
       // this.$router.push("/online-exam/exam/:id/overview")
     },
+    async faceCheckResultCallback(course, faceMatched) {
+      // if faceMatched
+    },
     previewPaper(course) {
       var user = {
         loginName: course.examStudentId,
@@ -87,8 +98,12 @@ export default {
         "?isback=true";
     }
   },
+  computed: {
+    ...mapState(["faceCheckModalOpen"])
+  },
   components: {
-    "ecs-online-exam-result-list": OnlineExamResultList
+    "ecs-online-exam-result-list": OnlineExamResultList,
+    OnlineExamFaceCheckModal
   }
 };
 </script>

+ 1 - 6
src/features/login/Login.vue

@@ -121,12 +121,7 @@ export default {
         window.localStorage.setItem("token", data.token);
         window.localStorage.setItem("key", data.key);
 
-        //FIXME: vuex
-        window.wk_user = {
-          name: data.displayName,
-          code: data.studentCode,
-          orgName: data.orgName
-        };
+        this.$store.state.user = data;
         this.$router.push("/online-exam");
       } else {
         this.errorInfo = data.desc;

+ 5 - 14
src/main.js

@@ -20,18 +20,14 @@ Vue.config.productionTip = process.env.NODE_ENV !== "production";
 Vue.component("main-layout", MainLayout);
 
 if (process.env.NODE_ENV === "development") {
-  console.log("非生产环境自动登录");
+  console.log("非生产环境:准备自动登录");
   (async () => {
     if (window.localStorage.getItem("token")) {
-      //FIXME: vuex
-      window.wk_user = {
-        name: "mock name",
-        code: "mock code",
-        orgName: "mock org name"
-      };
-      console.log("非生产环境: mock user name");
+      console.log("非生产环境: 已有token,自动登录");
       return;
     }
+    console.log("非生产环境: 没有token,自动登录");
+
     const response = await fetch("/api/ecs_core/auth/login", {
       method: "POST",
       headers: {
@@ -49,12 +45,7 @@ if (process.env.NODE_ENV === "development") {
       window.localStorage.setItem("token", data.token);
       window.localStorage.setItem("key", data.key);
 
-      //FIXME: vuex
-      window.wk_user = {
-        name: data.displayName,
-        code: data.studentCode,
-        orgName: data.orgName
-      };
+      window.localStorage.setItem("user", JSON.stringify(data));
     } else {
       console.log(data.desc);
     }

+ 29 - 2
src/store.js

@@ -3,8 +3,35 @@ import Vuex from "vuex";
 
 Vue.use(Vuex);
 
+const examHomeModule = {
+  namespaced: true,
+  state: { faceCheckModalOpen: true },
+  mutations: {
+    toggleFaceCheckModal(state, open) {
+      if (open === undefined) {
+        state.faceCheckModalOpen = !state.faceCheckModalOpen;
+      } else {
+        state.faceCheckModalOpen = open;
+      }
+    }
+  },
+  actions: {},
+  getters: {}
+};
+
+let initUser = {};
+if (process.env.NODE_ENV !== "production") {
+  const userStr = window.localStorage.getItem("user");
+  initUser = JSON.parse(userStr);
+}
+
 export default new Vuex.Store({
-  state: {},
+  state: {
+    user: initUser
+  },
   mutations: {},
-  actions: {}
+  actions: {},
+  modules: {
+    examHomeModule: examHomeModule
+  }
 });

+ 14 - 0
src/styles/global.css

@@ -79,3 +79,17 @@
   color: #999999;
   background-color: #ffffff;
 }
+
+.qm-icon-button {
+  height: 36px;
+  font-size: 36px;
+  color: #999999;
+  background-color: #ffffff;
+  line-height: 36px;
+  overflow: hidden;
+}
+
+.qm-icon-button:hover {
+  color: #444444;
+  background-color: #ffffff;
+}