林旭祥 9 miesięcy temu
rodzic
commit
01e40629dd

+ 3 - 1
src/components/MultipleItem/index.vue

@@ -17,7 +17,7 @@
     <div>当前状态:({{ examState == 3 ? "初始化完成" : examState == 40 ? "创建测试" : examState == 41 ? "正在人脸识别"
       :
       examState ==
-        43 ? "停止人脸识别" : examState == 42 ? "正在测试" : "请初始化" }})</div>
+        43 ? "停止人脸识别" : examState == 42 ? "正在测试" : "离线状态" }})</div>
     <div @click="getChooseStudent" v-if="examState == 41 || (examState == 43 && !faceCheckStu.student_id)">选择学生</div>
     <div @click="getRetestFace" v-if="examState == 43">重新识别</div>
     <ChooseStudent ref="chooseStudentRef" @returnData="returnStudent" />
@@ -99,6 +99,8 @@ const getMessage = (e: any) => {
   }
   //断线状态
   if (e.cmd === 'disconnect_request') {
+    examState.value = 0;
+    emit('returnData', { examState: examState.value, area: area });
     if (e.data.message) {
       speckText(e.data.message);
     }

+ 34 - 0
src/layout/index.vue

@@ -0,0 +1,34 @@
+<template>
+  <div class="app-main">
+    <router-view v-slot="{ Component, route }">
+      <transition :enter-active-class="proxy?.animate.page.enter">
+        <component :is="Component" :key="route.path" />
+      </transition>
+    </router-view>
+  </div>
+</template>
+
+<script setup name="AppMain" lang="ts">
+const { proxy } = getCurrentInstance() as any;
+</script>
+<style lang="scss" scoped>
+.app-main {
+  height: 100vh;
+  width: 100%;
+}
+</style>
+<style lang="scss">
+::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track {
+  background-color: #f1f1f1;
+}
+
+::-webkit-scrollbar-thumb {
+  background-color: #c0c0c0;
+  border-radius: 3px;
+}
+</style>

+ 15 - 7
src/router/index.ts

@@ -1,15 +1,23 @@
 import { createRouter, createWebHashHistory } from 'vue-router';
 
+import Layout from '@/layout/index.vue';
+
 const router = createRouter({
   history: createWebHashHistory(),
   routes: [
-    { path: '/', component: () => import('@/views/home/index.vue') },
-    { path: '/home', component: () => import('@/views/home/index.vue') },
-    { path: '/login', component: () => import('@/views/login/index.vue') },
-    { path: '/train', component: () => import('@/views/train/index.vue') },
-    { path: '/train/test', component: () => import('@/views/train/test.vue') },
-    { path: '/train/run', component: () => import('@/views/train/run.vue') },
-    { path: '/train/multiple', component: () => import('@/views/train/multiple.vue') }
+    {
+      path: '',
+      component: Layout,
+      redirect: '/home',
+      children: [
+        { path: '/home', component: () => import('@/views/home/index.vue') },
+        { path: '/login', component: () => import('@/views/login/index.vue') },
+        { path: '/train', component: () => import('@/views/train/index.vue') },
+        { path: '/train/test', component: () => import('@/views/train/test.vue') },
+        { path: '/train/run', component: () => import('@/views/train/run.vue') },
+        { path: '/train/multiple', component: () => import('@/views/train/multiple.vue') }
+      ]
+    }
   ]
 });
 

+ 7 - 1
src/utils/animate.ts

@@ -18,8 +18,14 @@ const face = {
   leave2: animatePrefix + 'animate__fadeOut'
 };
 
+const page = {
+  enter: animatePrefix + 'animate__fadeInLeft',
+  leave: animatePrefix + 'animate__fadeOutLeft'
+};
+
 export default {
   mask,
   dialog,
-  face
+  face,
+  page
 };

+ 85 - 29
src/utils/ws.ts

@@ -8,10 +8,9 @@ let parameter: any = {}; //参数
 let testTime: number = 0; //默认时长
 let userInfo: any = JSON.parse(myInfo); //用户信息
 let beatTime: number = 10000; //心跳频率
-let beatNumber: number = 0; //心跳次数
 let loading: any = null; //遮罩层
 let version: string = ''; //ws接口版本v2表示单ws多项目
-let examState: number = 0; //当前状态码
+let examStateList: any = []; //当前状态码
 let testList: any = []; //区列表
 let wkList: any = []; //工作站列表
 
@@ -21,6 +20,15 @@ export const initWs = (data: any, callback: any) => {
   testTime = data.testTime;
   version = data.version || '';
   testList = data.parameter.area.split(',');
+  examStateList = testList.map((item: any) => {
+    let examId = `${parameter.project}_${item}`;
+    let obj = {
+      examState: 0,
+      examId: examId,
+      beatNumber: 0
+    };
+    return obj;
+  });
   socket = io(address + '/midexam', {
     transports: ['websocket', 'polling'],
     query: {
@@ -30,10 +38,13 @@ export const initWs = (data: any, callback: any) => {
   });
   let loadingTime = setTimeout(() => {
     //30秒还在0状态就算超时
-    if (examState == 0) {
+    let list = examStateList.filter((item: any) => {
+      return item.examState == 0;
+    });
+    //考虑到多开只有一个在线也有效的
+    if (list.length == testList.length) {
       clearTimeout(loadingTime);
       callback({ cmd: 'disconnect_request', data: { message: 'WS连接超时' } });
-      getExit();
     }
   }, 30000);
   socket.on('connect', (e: any) => {
@@ -44,16 +55,17 @@ export const initWs = (data: any, callback: any) => {
         getExamStarts(examId);
         getNetWork(examId, (e: any) => {
           if (!e.status) {
-            callback({ cmd: 'disconnect_request', data: { message: '工作站未响应' } });
+            callback({ cmd: 'disconnect_request', exam_id: examId, data: { message: '工作站未响应' } });
           }
         });
       });
     } else {
       //单WS单区
+      let examId = parameter.examId;
       getExamStarts();
       getNetWork(data == null, (e: any) => {
         if (!e.status) {
-          callback({ cmd: 'disconnect_request', data: { message: '工作站未响应' } });
+          callback({ cmd: 'disconnect_request', exam_id: examId, data: { message: '工作站未响应' } });
         }
       });
     }
@@ -63,8 +75,11 @@ export const initWs = (data: any, callback: any) => {
     callback(e);
     //实时状态
     if (e.cmd === 'exam_status') {
-      examState = e.data;
-      beatNumber++;
+      let index = examStateList.findIndex((item: any) => {
+        return item.examId == e.exam_id;
+      });
+      examStateList[index].examState = e.data;
+      examStateList[index].beatNumber = examStateList[index].beatNumber + 1;
     }
     //工作站状态
     if (e.cmd === 'init_result') {
@@ -97,11 +112,18 @@ export const initWs = (data: any, callback: any) => {
       if (message) {
         ElMessage({ message: message, type: 'error', duration: 5 * 1000 });
       }
-      getExit();
+      let examId = e?.exam_id || '';
+      if (testList.length == 1) {
+        getExit();
+        //getExit(examId);
+      }
     }
     //状态变更
     if (e.cmd === 'set_exam_state') {
-      examState = e.data;
+      let index = examStateList.findIndex((item: any) => {
+        return item.examId == e.exam_id;
+      });
+      examStateList[index].examState = e.data;
       if (e.data == 3) {
         //关闭遮罩层
         loading?.close();
@@ -191,6 +213,10 @@ const getExamStarts = (data?: any) => {
 export const openOneTest = (data?: any) => {
   return new Promise((resolve, reject) => {
     let examId = data ? data : parameter.examId;
+    let index = examStateList.findIndex((item: any) => {
+      return item.examId == examId;
+    });
+    let examState = examStateList[index].examState;
     sendMessage('msgfrom_frontend', {
       data: {
         cmd: 'open_one_test',
@@ -208,7 +234,7 @@ export const openOneTest = (data?: any) => {
       if (examState == 3) {
         clearInterval(timer1);
         clearTimeout(timer2);
-        reject({ cmd: 'disconnect_request', data: { message: '超时:open_one_test' } });
+        reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:open_one_test' } });
       }
     }, 30000);
   });
@@ -220,6 +246,10 @@ export const openOneTest = (data?: any) => {
 export const startFace = (data?: any) => {
   return new Promise((resolve, reject) => {
     let examId = data ? data : parameter.examId;
+    let index = examStateList.findIndex((item: any) => {
+      return item.examId == examId;
+    });
+    let examState = examStateList[index].examState;
     sendMessage('msgfrom_frontend', {
       data: {
         cmd: 'start_face_recognition',
@@ -237,7 +267,7 @@ export const startFace = (data?: any) => {
       if (examState == 40) {
         clearInterval(timer1);
         clearTimeout(timer2);
-        reject({ cmd: 'disconnect_request', data: { message: '超时:start_face_recognition' } });
+        reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:start_face_recognition' } });
       }
     }, 30000);
   });
@@ -249,6 +279,10 @@ export const startFace = (data?: any) => {
 export const stopFace = (data?: any) => {
   return new Promise((resolve, reject) => {
     let examId = data ? data : parameter.examId;
+    let index = examStateList.findIndex((item: any) => {
+      return item.examId == examId;
+    });
+    let examState = examStateList[index].examState;
     sendMessage('msgfrom_frontend', {
       data: {
         cmd: 'stop_face_recognition',
@@ -266,7 +300,7 @@ export const stopFace = (data?: any) => {
       if (examState == 41) {
         clearInterval(timer1);
         clearTimeout(timer2);
-        reject({ cmd: 'disconnect_request', data: { message: '超时:stop_face_recognition' } });
+        reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:stop_face_recognition' } });
       }
     }, 30000);
   });
@@ -330,6 +364,10 @@ export const startOneTest = (data?: any, callback?: any) => {
 export const finishOneTest = (data?: any) => {
   return new Promise((resolve, reject) => {
     let examId = data ? data : parameter.examId;
+    let index = examStateList.findIndex((item: any) => {
+      return item.examId == examId;
+    });
+    let examState = examStateList[index].examState;
     sendMessage('msgfrom_frontend', {
       data: {
         cmd: 'finish_one_test',
@@ -347,7 +385,7 @@ export const finishOneTest = (data?: any) => {
       if (examState == 42) {
         clearInterval(timer1);
         clearTimeout(timer2);
-        reject({ cmd: 'disconnect_request', data: { message: '超时:finish_one_test' } });
+        reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:finish_one_test' } });
       }
     }, 60000);
   });
@@ -359,6 +397,10 @@ export const finishOneTest = (data?: any) => {
 export const closeOneTest = (data?: any) => {
   return new Promise((resolve, reject) => {
     let examId = data ? data : parameter.examId;
+    let index = examStateList.findIndex((item: any) => {
+      return item.examId == examId;
+    });
+    let examState = examStateList[index].examState;
     sendMessage('msgfrom_frontend', {
       data: {
         cmd: 'close_one_test',
@@ -376,7 +418,7 @@ export const closeOneTest = (data?: any) => {
       if (examState != 3) {
         clearInterval(timer1);
         clearTimeout(timer2);
-        reject({ cmd: 'disconnect_request', data: { message: '超时:close_one_test' } });
+        reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:close_one_test' } });
       }
     }, 30000);
   });
@@ -416,8 +458,8 @@ export const getNetWork = (data: any, callback?: any) => {
     let obj = wkList.find((item: any) => {
       return item.examId == data;
     });
-    let examId = data ? data : parameter.examId;
     let wk_id = obj.wk_id;
+    let examId = data ? data : parameter.examId;
     sendMessage(
       'get_exam_status',
       {
@@ -427,11 +469,17 @@ export const getNetWork = (data: any, callback?: any) => {
       },
       () => {
         //如果心跳停止了就退出去
-        let beforBeatNumber = JSON.parse(JSON.stringify(beatNumber));
+        let index = examStateList.findIndex((item: any) => {
+          return item.examId == examId;
+        });
+        let beforBeatNumber = JSON.parse(JSON.stringify(examStateList[index].beatNumber));
         setTimeout(() => {
-          if (beforBeatNumber == beatNumber) {
+          //5秒后验证是否有变
+          if (beforBeatNumber == examStateList[index].beatNumber) {
+            //异常
             callback({ status: false });
           } else {
+            //正常
             callback({ status: true });
           }
         }, 5000);
@@ -450,32 +498,40 @@ export const examEnds = () => {
 /**
  * 退出
  */
-const getExit = () => {
+const getExit = (data?: any) => {
   //关闭遮罩层
   loading?.close();
-  //清除计时器
-  getClearTimer();
   //通知工作站关闭
-  if (testList.length > 1) {
+  if (testList.length > 1 && !data) {
     //单WS多区
-    testList.forEach((item: any) => {
-      let examId = `${parameter.project}_${item}`;
+    examStateList.forEach((item: any) => {
+      let examId = item.examId;
       sendMessage('exam_ends', {
         data: 'end_' + examId,
         class_id: parameter.classes
       });
     });
+    //清除计时器
+    getClearTimer();
+    //如果正在连接就关闭
+    if (socket?.connected) {
+      socket?.close();
+    }
   } else {
     //单WS单区
-    let examId = parameter.examId;
+    let examId = data ? data : parameter.examId;
     sendMessage('exam_ends', {
       data: 'end_' + examId,
       class_id: parameter.classes
     });
-  }
-  //如果正在连接就关闭
-  if (socket?.connected) {
-    socket?.close();
+    if (!data) {
+      //清除计时器
+      getClearTimer();
+      //如果正在连接就关闭
+      if (socket?.connected) {
+        socket?.close();
+      }
+    }
   }
 };
 

+ 6 - 6
src/views/home/index.vue

@@ -1,10 +1,10 @@
 <template>
-
-  <div @click="getLogout">退出</div>
-
-  <div style="display: flex; justify-content: space-around;">
-    <div v-for="(item, index) in list1" :key="index" @click="getJump(item.url, item.name)">{{ item.name }}</div>
-    <div v-for="(item, index) in list2" :key="index" @click="getJump(item.url, item.name)">{{ item.name }}</div>
+  <div>
+    <div @click="getLogout">退出</div>
+    <div style="display: flex; justify-content: space-around;">
+      <div v-for="(item, index) in list1" :key="index" @click="getJump(item.url, item.name)">{{ item.name }}</div>
+      <div v-for="(item, index) in list2" :key="index" @click="getJump(item.url, item.name)">{{ item.name }}</div>
+    </div>
   </div>
 </template>
 

+ 21 - 4
src/views/train/index.vue

@@ -11,11 +11,13 @@
         </div>
       </transition-group>
     </div>
+    <div @click="getExit">返回</div>
     <OptionWindow ref="optionWindowRef" :projectList="projectList" />
   </div>
 </template>
 
 <script setup name="TrainIndex" lang="ts">
+const router = useRouter();
 const { proxy } = getCurrentInstance() as any;
 const optionWindowRef = ref();
 
@@ -26,7 +28,9 @@ const data = reactive<any>({
 
 const { projectList, timerManager } = toRefs(data);
 
-//获取项目
+/**
+ * 获取项目
+*/
 const getExam = () => {
   proxy?.$http.train.projectList().then((res: any) => {
     projectList.value = proxy?.$utils.getProject(res.exams).filter((item: any) => {
@@ -36,12 +40,16 @@ const getExam = () => {
   });
 };
 
-//弹出选项窗口
+/**
+ * 弹出选项窗口
+*/
 const getOption = (data: any) => {
   optionWindowRef.value.open(data);
 };
 
-//清空定时任务
+/**
+ * 清空定时任务
+*/
 const getClearTimer = () => {
   for (let key in timerManager.value) {
     if (timerManager.value.hasOwnProperty(key)) {
@@ -51,7 +59,9 @@ const getClearTimer = () => {
   }
 };
 
-//初始化项目
+/**
+ * 初始化项目
+*/
 const getInitExam = () => {
   getExam();
   //定时刷新
@@ -60,6 +70,13 @@ const getInitExam = () => {
   }, 5000)
 };
 
+/**
+ * 退出
+*/
+const getExit = () => {
+  router.go(-1);
+};
+
 onMounted(() => {
   getInitExam();
 })

+ 17 - 0
src/views/train/multiple.vue

@@ -271,6 +271,23 @@ const returnData = (data: any) => {
       examState.value = 41;
     }
   }
+  //如果全部状态为0就退出
+  if (examState.value >= 0) {
+    let flag = false;
+    //只监听人脸识别的区
+    let newList = testList.value;
+    for (let i = 0; i < newList.length; i++) {
+      if (newList[i] && newList[i].examState == 0) {
+        flag = true;
+      } else {
+        return false;
+      }
+    }
+    if (flag) {
+      examState.value = 0;
+      getExit();
+    }
+  }
 };
 
 onMounted(() => {