<template>
  <div class="test">
    <SquareGame ref="squareGameRef"></SquareGame>
    <Header @confirmExit="confirmExit" @setMusic="setMusic"></Header>
    <div class="main">
      <div class="main-left">
        <div class="main-left-top">
          <div class="top-left" @click="getChooseStudent">
            <div class="top-left-center">
              <div class="pic" :class="{ 'pic2': faceCheckStu.student_id }" v-if="faceCheckStu.student_id">
                <img :src="faceCheckStu.face_pic || faceCheckStu.logo_url" />
              </div>
              <div class="pic" v-else>
                <img src="@/assets/images/test/profilePicture.png" />
              </div>
              <div class="name" :class="{ 'name2': faceCheckStu.student_id }">
                {{ faceCheckStu.student_id ? faceCheckStu.name : "虚位以待" }}
              </div>
            </div>
          </div>
          <div class="top-right">
            <Transition :enter-active-class="proxy?.animate.dialog.enter"
              :leave-active-class="proxy?.animate.dialog.leave">
              <div class="time"
                v-show="needStart && [42].includes(examState) && !['basketballv1', 'footballv1'].includes(parameter.project)">
                {{
                  time.countdownNum
                }}
              </div>
            </Transition>
            <div class="tips" v-if="examState == 41">
              <img v-if="parameter.gesture" src="@/assets/images/test/ready1.png" />
              <img v-if="!parameter.gesture" src="@/assets/images/test/ready2.png" />
            </div>
            <div class="complete" :class="{ 'complete2': needStart && [42].includes(examState) }"
              v-if="faceCheckStu.student_id && time.ready <= 0 && examState != 43 && examState != 41">
              <div class="scoreBox">
                <div class="score" v-if="currentResultObj?.count && currentResultObj.count >= 0">{{
                  currentResultObj.count
                  }}</div>
                <div class="prompt" v-if="currentResultObj?.count && currentResultObj.count == 0 && examState == 42">
                  请开始测试
                </div>
                <div class="unit"
                  v-if="!['basketballv1', 'footballv1'].includes(parameter.project) && currentResultObj.count && !needStart">
                  {{ unit }}
                </div>
              </div>
              <div class="fractionViolation">
                <div class="fraction">
                  <div class="lable">得分：</div>
                  <div class="value">{{ currentResultObj.score || "" }}</div>
                </div>
                <div class="violation">
                  <div class="lable">
                    {{ ['jumprope', 'jumpingjack', 'highknees'].includes(parameter.project) ? '中断' : '犯规'
                    }}
                  </div>
                  <div class="value">{{ currentResultObj.back_num || 0 }}</div>
                </div>
              </div>
            </div>
            <div class="foulBox" v-if="examState == 42 && backReason.length">
              <Transition :enter-active-class="proxy?.animate.mask.enter"
                :leave-active-class="proxy?.animate.mask.leave">
                <div class="foul" v-show="backReasonStr ? true : false">
                  <div class="lable">!</div>
                  <div class="value">{{ backReasonStr }}</div>
                </div>
              </Transition>
            </div>

            <div v-show="examState == 43 && time.ready">
              <div class="readyBox">
                <div class="value" :class="{ 'transparent': time.ready > 5 }">{{ time.ready }}</div>
                <div class="lable">倒计时</div>
              </div>
            </div>

            <div v-show="examState == 43 && faceCheckStu.student_id && !time.ready && readyState">
              <div class="readyBoxBefore">
                <div class="item" v-if="parameter.handcontroller">
                  <div><img src="@/assets/images/test/jushou.png" /></div>
                  <div class="lable">
                    <div>请举左手开始</div>
                  </div>
                </div>
                <div class="item" v-else>
                  <div><img src="@/assets/images/test/bujushou.png" /></div>
                  <div class="lable">
                    <div>请点击开始</div>
                  </div>
                </div>
              </div>
            </div>
            <div v-show="examState == 43 && !faceCheckStu.student_id">
              <div class="btn btn2" @click="getChooseStudent">点击重新识别</div>
            </div>
            <div class="btn" @click="getReady"
              v-if="needStart && examState == 43 && faceCheckStu.student_id && !time.ready && readyState">开 始</div>
            <!-- <div v-if="needStart"> -->
            <!-- <div class="btn" @click="getOpenOneTestAndStartFace" v-if="examState < 41">开始识别</div> -->
            <!-- <div class="btn" @click="getStopFace" v-if="examState == 41 && !parameter.gesture">停止人脸识别</div> -->
            <!-- <div class="btn" @click="getStartOneTest" v-if="examState == 43">开始测试</div> -->
            <!-- <div @click="getRetestFace" v-if="examState == 43 || examState == 42">重新识别</div> -->
            <!-- </div> -->
            <!-- <div>当前状态：({{ examState == 3 ? "初始化完成" : examState == 40 ? "创建测试" : examState == 41 ? "正在人脸识别":examState == 43 ? "停止人脸识别" : examState == 42 ? "正在测试" : "请初始化" }})</div> -->
            <!-- <div @click="getAgain" v-if="examState == 42 || showTestAgain">再测一次</div> -->
          </div>
          <i></i>
        </div>
        <div class="main-left-bottom">
          <div class="bottom-left">
            <div class="tips"><img src="/src/assets/images/test/tips.png" /></div>
            <div class="pic"><img :src="'static/images/tips/' + parameter.project + '.png'" /></div>
          </div>
          <div class="bottom-right" v-html="dic.projectNote[parameter.project]"></div>
        </div>
      </div>
      <div class="main-right">
        <ReportList ref="reportListRef" :parameter="parameter" :showQRCode="true" />
      </div>
    </div>
    <FaceWindow ref="faceWindowRef" :faceCheckStu="faceCheckStu" :gesture="parameter.gesture" />
    <ChooseStudent ref="chooseStudentRef" @returnData="returnStudent" />
    <JumpRopeGame ref="gameContainer"
      v-if="['test'].includes(parameter.project) && (!readyState || [42].includes(examState))" />
    <div class="close" @click="confirmExit"></div>
  </div>
</template>

<script setup name="TrainTest" lang="ts">
import useAppStore from '@/store/modules/app';
import { useWs } from '@/utils/trainWs';
import { initSpeech, speckText, playMusic, controlMusic, speckCancel, chineseNumber } from '@/utils/speech';
import { useWebSocket } from '@/utils/handWs';
import dayjs from 'dayjs';
import dataDictionary from '@/utils/dataDictionary';
const { handWs, startDevice, startHand, stateHand } = useWebSocket();
const {
  initWs,
  examEnds,
  openOneTest,
  startFace,
  stopFace,
  faceConfirmOnly,
  startOneTest,
  finishOneTest,
  closeOneTest,
  suspendFaceRecognitionChannels,
  resumeFaceRecognitionChannels
} = useWs();
const { proxy } = getCurrentInstance() as any;
const router = useRouter();
const route = useRoute();
const faceWindowRef = ref();
const chooseStudentRef = ref();
const reportListRef = ref();
const gameContainer = ref();
const myInfo: any = localStorage.getItem('userInfo');
const dic: any = dataDictionary;
const squareGameRef = ref();
const data = reactive<any>({
  timerManager: {}, //计时器管理
  parameter: {}, //参数
  time: {
    testTime: 60, //时长
    countdownNum: 0, //计时
    ready: 0, //预备
    exit: 0 //退出倒计时
  },
  userInfo: {}, //用户信息
  examState: 0, //当前状态
  resultId: null, //测试ID
  currentResultObj: {}, //成绩
  faceCheckStu: {}, //人脸信息
  unit: '', //单位
  backReason: [], //犯规项
  backReasonStr: '', //犯规提示
  needStart: false, //是否需要按钮
  showTestAgain: false, //再测一次按钮
  readyState: true, //倒计时按钮状态
  exitStatus: 0, //退出响应次数
  sid: null, //WS的id
  deviceInfo: {}, //设备信息
  musicList: [], //音乐列表
});
const {
  timerManager,
  parameter,
  time,
  userInfo,
  examState,
  resultId,
  faceCheckStu,
  currentResultObj,
  unit,
  backReason,
  backReasonStr,
  needStart,
  showTestAgain,
  readyState,
  exitStatus,
  sid,
  deviceInfo,
  musicList
} = toRefs(data);

/**
 * 接收消息
 */
const getMessage = (e: any) => {
  //console.log("WS响应：", e)
  //获取sid
  if (e.cmd === 'mySid') {
    console.log('e.data.sid', e.data.sid);
    sid.value = e.data.sid;
  }
  //实时状态
  if (e.cmd === 'exam_status') {
    examState.value = e.data;
  }
  //工作站状态
  if (e.cmd === 'init_result') {
  }
  //测试违规
  if (e.cmd === 'warning_result') {
    console.log('eeeeeeeeeee', e);
    if ((e.status + '')[0] === '2') {
      proxy?.$modal.msgError(e.data.message);
      speckText(e.data.message);
    }
  }
  //后端播报语音
  if (e.cmd === 'return_audio_msg') {
    if (e.data.message) {
      proxy?.$modal.msgError(e.data.message);
      speckText(e.data.message);
    }
  }
  //错误提示
  if (e.cmd === 'info_result') {
    proxy?.$modal.msgError(e.data.message);
  }
  //错误提示
  if (e.cmd === 'error_result') {
    proxy?.$modal.msgError(e.data.message);
  }
  //测试中违规提示
  if (e.cmd === 'warning_notify') {
    let message = e.data?.message;
    if (message) {
      proxy?.$modal.msgError(message);
      speckText(message);
    }
    if (message == '工作站已断开！') {
      getExit();
    }
  }
  //断线状态
  if (e.cmd === 'disconnect_request') {
    let message = e.data.message;
    if (message) {
      proxy?.$modal.msgError(`${message}`);
      speckText(message);
    }
    getExit();
  }
  //状态变更
  if (e.cmd === 'set_exam_state') {
    examState.value = e.data;
    if (e.data === 3) {
      initProject();
    }
    if (e.data === 40) {
      cleanData();
    }
    if (e.data == 41) {
      getFaceWindow(true);
    }
    if (e.data == 43) {
    }
    if (e.data == 42) {
      getClearTimer('readyTimer');
      time.value.ready = 0;
    }
  }
  //新建测试后返回信息，获取result_id
  if (e.cmd === 'open_one_test_ack') {
    resultId.value = e.data.result_id;
  }
  //人脸识别状态
  if (e.cmd === 'face_check_result') {
    let myData = e.data[0] || e.data;
    returnStudent(myData);
  }
  //测试结束结果
  if (e.cmd === 'oneresult') {
    if (e.data.length) {
      let data = e.data[0];
      getAchievement(data);
    }
  }
  //结果生成完成（视频图片）
  if (e.cmd === 'static_urls_finished') {
  }
  //选择学生或测试结束后返回的数据
  if (e.cmd === 'result_info') {
  }
};

/**
 * 开始识别
 */
const getOpenOneTestAndStartFace = async () => {
  if (examState.value > 3) {
    await closeOneTest();
  }
  await openOneTest();
  await startFace();
};

/**
 * 停止人脸识别
 */
const getStopFace = async () => {
  // 旧版识别成功直接43了这里先屏蔽
  // if (examState.value != 41) {
  //   return false;
  // }
  getClearTimer('face');
  if (needStart.value) {
    let txt = parameter.value.handcontroller ? '，请举左手开始测试' : '，请准备';
    speckText(faceCheckStu.value.name + txt);
  }
  if (examState.value == 41) {
    await stopFace();
  }
  if (faceCheckStu.value.student_id) {
    getFaceConfirmOnly();
  }
};

/**
 * 确定人脸信息
 */
const getFaceConfirmOnly = (data?: any) => {
  if (data) {
    faceCheckStu.value = data;
  }
  faceConfirmOnly(
    {
      result_id: resultId.value,
      student_id: faceCheckStu.value.student_id,
      gender: faceCheckStu.value.gender
    },
    () => {
      faceWindowRef.value?.close();
      getReady();
    }
  );
};

/**
 * 重新识别
 */
const getRetestFace = () => {
  proxy?.$modal
    .confirm('确定重新识别吗？')
    .then(() => {
      cleanData();
      if (needStart.value == false) {
        //自动流程项目重新识别直接返回3
        closeOneTest();
      } else {
        //手动流程项目重新识别43返回41,42返回3
        if (examState.value == 43) {
          startFace();
        } else if (examState.value == 42) {
          finishOneTest();
        } else {
          closeOneTest();
        }
      }
    })
    .finally(() => { });
};

/**
 * 开始测试
 */
const getStartOneTest = () => {
  if (examState.value != 43 || !faceCheckStu.value.student_id) {
    return false;
  }
  if (!faceCheckStu.value.student_id) {
    proxy?.$modal.msgWarning('请选择人员！');
    return false;
  }
  startOneTest(null, () => {
    //显示再测一次按钮
    showTestAgain.value = true;
    //停止播报;
    speckCancel();
    //计时项目才开
    if (needStart.value == true) {
      speckText('哨声');
      if (parameter.value.music && musicList.value.length) {
        let obj = musicList.value.find((item: any) => {
          return item.id == parameter.value.music;
        });
        if (obj != undefined) {
          playMusic(obj.url);
        }
      }
      //时间为0的为正计时，大于0的为倒计时
      if (time.value.testTime == 0) {
        getCounting('+');
      } else {
        getCounting('-');
      }
    } else {
      speckText(faceCheckStu.value.name + '，请开始测试');
    }
  });
};

/**
 * 再测一次
 */
const getAgain = async () => {
  let txt = '是否再测一次？';
  await proxy?.$modal.confirm(txt);
  getClearTimer();
  //预存测试人员
  let student = JSON.parse(JSON.stringify(faceCheckStu.value));
  //测试中
  if (examState.value == 42) {
    await finishOneTest();
  }
  //其他状态
  if (examState.value > 3) {
    await closeOneTest();
  }
  //重新走一次流程
  await openOneTest();
  await startFace();
  await stopFace();
  getFaceConfirmOnly(student);
};

/**
 * 确认退出
 */
const confirmExit = () => {
  let handcontroller_id = parameter.value.handcontroller;
  proxy?.$modal
    .confirm(handcontroller_id ? `请保持两秒确认退出` : `确定退出吗？`)
    .then(() => {
      getExit();
    })
    .finally(() => { });
};

/**
 * 退出
 */
const getExit = () => {
  getClearTimer(); //清除计时器
  examEnds(); //通知工作站关闭
  speckCancel(); //停止播报
  window.onbeforeunload = null; //移除事件处理器
  router.push({ path: '/game' });
};

/**
 * 清空定时任务
 */
const getClearTimer = (data?: any) => {
  if (data) {
    //清除指定
    clearInterval(timerManager.value[data]);
    timerManager.value[data] = null;
  } else {
    //清除全部
    for (let key in timerManager.value) {
      if (timerManager.value.hasOwnProperty(key)) {
        clearInterval(timerManager.value[key]);
        timerManager.value[key] = null;
      }
    }
  }
};

/**
 * 选择学生
 */
const getChooseStudent = () => {
  if (examState.value < 41) {
    proxy?.$modal.msgWarning('请等待');
  }
  if (examState.value == 41) {
    stopFace();
    chooseStudentRef.value.open();
    //然后定时自动关闭
    setTimeout(() => {
      faceWindowRef.value.close();
    }, 3000);
  }
  if (examState.value == 43) {
    getRetestFace();
  }
  if (examState.value == 42) {
    getRetestFace();
    // proxy?.$modal.msgWarning(`正在测试请结束后再操作，当前状态：${examState.value}`);
    // return false;
  }
};

/**
 * 返回被选学生
 */
const returnStudent = (data: any) => {
  speckCancel();
  chooseStudentRef.value.close();
  faceCheckStu.value = data;
  faceWindowRef.value.open();
  //然后定时自动关闭
  setTimeout(() => {
    faceWindowRef.value.close();
  }, 1000);
  getStopFace();
};

/**
 * 清除历史记录
 */
const cleanData = () => {
  time.value.countdownNum = time.value.testTime;
  showTestAgain.value = false;
  faceCheckStu.value = {};
  currentResultObj.value = {};
  backReason.value = [];
};

/**
 * 自动初始化项目
 */
const initProject = () => {
  //停止计时
  getClearTimer('countdownTimer');
  //恢复倒计时按钮状态
  readyState.value = true;
  //自动项目定时进入下一步
  let time = 0;
  //控制新建测试的时间，第一次快，之后就慢
  if (!faceCheckStu.value.student_id) {
    time = 1000;
  } else {
    time = 6000;
  }
  setTimeout(() => {
    //再加一个判断以免和再测一次冲突
    if (examState.value == 3) {
      getOpenOneTestAndStartFace();
    }
  }, time);
};

/**
 * 倒计时
 */
const getCounting = (type: string) => {
  timerManager.value.countdownTimer = setInterval(() => {
    //正计时
    if (type == '+') {
      time.value.countdownNum++;
    }
    //倒计时
    if (type == '-') {
      if (time.value.countdownNum <= 0) {
        getClearTimer('countdownTimer');
      } else {
        time.value.countdownNum--;
      }
    }
  }, 1000);
};

/**
 * 人脸窗口
 */
const getFaceWindow = (data: boolean, num: number = 0) => {
  let total = num + 1; //叠加三次后不再播放
  let txt = parameter.value.gesture === true ? '请举右手看摄像头进行人脸识别' : '请看摄像头进行人脸识别';
  speckText(txt);
  //data=true为弹出框，data=false为不要弹出框
  if (data) {
    faceWindowRef.value.open();
    //然后定时自动关闭
    setTimeout(() => {
      if (examState.value == 41 && faceWindowRef.value?.faceState == true) {
        faceWindowRef.value.close();
      }
    }, 3000);
  }
  //定时检查如果一直停留在人脸识别就提示
  let timeout = 16000;
  timerManager.value.face = setInterval(() => {
    getClearTimer('face');
    if (examState.value == 41 && total < 3) {
      getFaceWindow(false, total);
    }
  }, timeout);
};

/**
 * 成绩
 */
const getAchievement = (data: any) => {
  //console.log("成绩", data);
  let type = parameter.value.project;
  let result = data?.[dic.typeResultKey[type]] || 0;
  let count = null;
  if (['trijump', 'solidball', 'shotput', 'longjump'].includes(type)) {
    count = (Math.round(result) / 100).toFixed(2);
  } else if (['basketballv1', 'footballv1'].includes(type)) {
    count = proxy?.$utils.runTime(result, true, 1);
  } else {
    count = result;
  }
  data.count = count || 0;
  data.score = data.score || '0';
  currentResultObj.value = data;
  //违规处理
  let arr = backReason.value;
  if (['situp', 'pullup', 'sidepullup', 'jumprope', 'jumpingjack', 'highknees', 'jump', 'longjump', 'verticaljump'].indexOf(type) > -1) {
    if (['pullup', 'situp', 'jumprope', 'jumpingjack', 'highknees'].indexOf(type) > -1) {
      currentResultObj.value.back_num = data?.all_failed_num;
    }
    if (type === 'sidepullup') {
      currentResultObj.value.back_num = data?.['0']?.hip_failed_num;
    }
    if (['jump', 'longjump', 'verticaljump'].includes(type)) {
      if (data?.startline_check == 0) {
        let txt = '踩线违规';
        speckText(txt);
        arr.push(txt);
      }
      if (data?.singleleg_jump_check == 0) {
        let txt = '单脚跳违规';
        speckText(txt);
        arr.push(txt);
      }
      if (data?.outside_check == 0) {
        let txt = '跳出测试区违规';
        speckText(txt);
        arr.push(txt);
      }
    }
    if (data?.elbow_check == false) {
      let txt = '肘部违规';
      speckText(txt);
      arr.push(txt);
    }
    if (['situp', 'pullup'].indexOf(type) > -1 && data?.knee_check === false) {
      let txt = '腿部违规';
      speckText(txt);
      if (!arr.includes(txt)) {
      }
      arr.push(txt);
    }
    if (['situp'].indexOf(type) > -1 && data?.hand_check === false) {
      let txt = '手部违规';
      speckText(txt);
      if (!arr.includes(txt)) {
      }
      arr.push(txt);
    }
    if (['pullup'].indexOf(type) > -1 && data?.['0']?.elbow_check === false) {
      let txt = '手部违规';
      speckText(txt);
      if (!arr.includes(txt)) {
      }
      arr.push(txt);
    }
    if (['situp'].indexOf(type) > -1 && data?.['0']?.back_check === false) {
      let txt = '背部违规';
      speckText(txt);
      if (!arr.includes(txt)) {
      }
      arr.push(txt);
    }
    if (['sidepullup', 'situp'].indexOf(type) > -1 && data?.['0']?.hip_check === false) {
      let txt = '臀部违规';
      speckText(txt);
      if (!arr.includes(txt)) {
      }
      arr.push(txt);
    }
  }
  backReason.value = arr;
  if (data.isfinish) {
    if (['jump'].includes(type) && backReason.value.length) {
      speckText('请重新测试');
      return false;
    }
    if (['basketballv1', 'footballv1'].includes(type)) {
      speckText(
        faceCheckStu?.value.name +
        '成绩为' +
        (chineseNumber(proxy?.$utils.runTime(data?.[dic.typeResultKey[type]], false, 0, 1)) || 0) +
        '，请下一位准备！' || ''
      );
    } else {
      speckText(faceCheckStu?.value.name + '成绩为' + (chineseNumber(count) || 0) + unit.value + '，请下一位准备！' || '');
    }
    reportListRef.value.getIniReportList();
    faceWindowRef.value.open();
    //然后定时自动关闭
    setTimeout(() => {
      faceWindowRef.value.close('right');
    }, 1000);
  }
};

/**
 * 准备开始
 */
const getReady = () => {
  if (examState.value == 43 && !time.value.ready && readyState.value) {
    if (time.value.ready) {
      return false;
    }
    speckCancel();
    readyState.value = false;
    time.value.ready = 6;
    timerManager.value.readyTimer = setInterval(() => {
      time.value.ready--;
      if (time.value.ready <= 0) {
        getClearTimer('readyTimer');
        getStartOneTest();
        squareGameRef.value.loginGame();
      } else {
        speckText(time.value.ready);
      }
    }, 1000);
  }
};

/**
 * 获取音乐
 */
const getMusic = async () => {
  const list: any = useAppStore().getMusic();
  if (list.length) {
    musicList.value = list;
  } else {
    await proxy?.$http.train.musicList().then((res: any) => {
      if (res.data.length > 0) {
        let myList: any = res.data;
        musicList.value = myList;
        useAppStore().setMusic(myList);
      }
    });
  }
};

/**
 * 设置音乐
 */
const setMusic = async (data: any) => {
  //console.log("data",data)
  parameter.value.music = data;
};

/**
 * 获取设备项目
 */
const getDevice = async () => {
  let deviceid = localStorage.getItem('deviceid') || '';
  if (deviceid) {
    startDevice({ deviceid: deviceid });
  } else {
    proxy?.$modal.msgError(`缺少设备信息请重新登录！`);
    await proxy?.$http.common.logout({}).then((res: any) => { });
    proxy?.$modal?.closeLoading();
    //清空缓存
    // localStorage.clear();
    localStorage.removeItem('token');
    localStorage.removeItem('userInfo');
    //跳转
    router.push({ path: '/login/qrcode' });
  }
};

/**
 * 加载手势WS
 */
const initHand = () => {
  handWs((e: any) => {
    if (router.currentRoute.value.path != '/train/test' || parameter.value.handcontroller == undefined || examState.value == 0) {
      return false;
    }
    console.log('eeeee', e);
    if (e?.wksid) {
      //获取设备项目
      getDevice();
    }
    //接收设备信息
    if (e?.device_info) {
      deviceInfo.value = e.device_info;
      let handcontroller_id = deviceInfo.value.handcontroller_id;
      stateHand(handcontroller_id);
    }
    //获取手势状态
    if (e?.cmd == 'get_handcontroller_state' && e?.state == 0) {
      let handcontroller_id = deviceInfo.value.handcontroller_id;
      startHand(handcontroller_id);
    }
    //刷新
    if (e?.data?.result == 'refresh') {
      getExit();
      //刷新
      window.location.reload();
    }
    //没初始化完成不监听手势动作
    if (examState.value == 0) {
      return false;
    }
    //左滑动
    // if (e?.data?.result == "next_item") {
    //   proxy?.$modal.msgSuccess('手势指令：左滑动');
    //   if(examState.value == 43 && time.value.ready){
    //     return false;
    //   }
    //   if (examState.value == 43 || examState.value == 42) {
    //     speckCancel();//停止播报
    //     if (needStart.value == false) {
    //       //自动流程项目重新识别直接返回3
    //       closeOneTest();
    //     } else {
    //       //手动流程项目重新识别43返回41,42返回3
    //       if (examState.value == 43) {
    //         cleanData();
    //         startFace();
    //       } else {
    //         closeOneTest();
    //       }
    //     }
    //   }
    // }
    //举左手
    if (e?.data?.result == 'left_hand') {
      //proxy?.$modal.msgSuccess('手势指令：举左手');
      //举左手确认退出
      // if (exitStatus.value) {
      //   exitStatus.value = 0;
      //   //确认退出
      //   let keyEvent: any = null;
      //   keyEvent = new KeyboardEvent('keydown', {
      //     key: 'Enter', // 键值
      //     code: 'Enter', // 键盘代码
      //     keyCode: 13, // 旧的键盘代码
      //     which: 13, // 新的键盘代码
      //     shiftKey: false, // 是否按下Shift键
      //     ctrlKey: false, // 是否按下Ctrl键
      //     metaKey: false, // 是否按下Meta键（Win键或Command键）
      //     bubbles: true, // 事件是否冒泡
      //     cancelable: true // 是否可以取消事件的默认行为
      //   });
      //   document.activeElement?.dispatchEvent(keyEvent);
      //   return false;
      // }
      //开始识别
      if (needStart.value && examState.value < 41) {
        getOpenOneTestAndStartFace();
        return false;
      }
      //停止人脸识别
      // if (needStart.value && examState.value == 41) {
      //   getStopFace();
      // }
      //开始测试
      if (examState.value == 43) {
        if (needStart.value) {
          getReady();
        } else {
          getStartOneTest();
        }
        return false;
      }
    }
    //退出
    // if (e?.data?.result == "exit") {
    //   proxy?.$modal.msgSuccess('手势指令：交叉手');
    //   // console.log("exitStatus.value", exitStatus.value)
    //   if (exitStatus.value == 0) {
    //     speckText("请保持两秒确认退出");
    //     //第一次才弹出
    //     confirmExit();
    //     setTimeout(() => {
    //       let keyEvent: any = null;
    //       let myKey = null;
    //       //如果交叉手两秒后返回超过4次就确认退出
    //       if (exitStatus.value >= 4) {
    //         myKey = 'Enter';
    //       } else {
    //         myKey = 'Esc';
    //       }
    //       if (myKey == 'Esc') {
    //         keyEvent = new KeyboardEvent('keydown', {
    //           key: 'Escape', // 键值
    //           code: 'Escape', // 键盘代码
    //           keyCode: 27, // 旧的键盘代码
    //           which: 27, // 新的键盘代码
    //           shiftKey: false, // 是否按下Shift键
    //           ctrlKey: false, // 是否按下Ctrl键
    //           metaKey: false, // 是否按下Meta键（Win键或Command键）
    //           bubbles: true, // 事件是否冒泡
    //           cancelable: true // 是否可以取消事件的默认行为
    //         });
    //         exitStatus.value = 0;
    //       }
    //       if (myKey == 'Enter') {
    //         keyEvent = new KeyboardEvent('keydown', {
    //           key: 'Enter', // 键值
    //           code: 'Enter', // 键盘代码
    //           keyCode: 13, // 旧的键盘代码
    //           which: 13, // 新的键盘代码
    //           shiftKey: false, // 是否按下Shift键
    //           ctrlKey: false, // 是否按下Ctrl键
    //           metaKey: false, // 是否按下Meta键（Win键或Command键）
    //           bubbles: true, // 事件是否冒泡
    //           cancelable: true // 是否可以取消事件的默认行为
    //         });
    //       }
    //       document.activeElement?.dispatchEvent(keyEvent);
    //     }, 2500)
    //   }
    //   exitStatus.value = exitStatus.value + 1
    // }
    // if (e?.data?.result == "exit") {
    //   console.log("exitStatus.value", exitStatus.value)
    //   if (exitStatus.value == 0) {
    //     exitStatus.value = 1
    //     speckText("请5秒内举左手确认退出");
    //     //第一次才弹出
    //     confirmExit();
    //     time.value.exit = 6;
    //     timerManager.value.exitTimer = setInterval(() => {
    //       time.value.exit--;
    //       console.log("取消倒计时", time.value.exit)
    //       proxy?.$modal.msgWarning(`取消倒计时：${time.value.exit}`)
    //       if (time.value.exit == 0) {
    //         exitStatus.value = 0;
    //         getClearTimer("exitTimer");
    //         let keyEvent: any = null;
    //         keyEvent = new KeyboardEvent('keydown', {
    //           key: 'Escape', // 键值
    //           code: 'Escape', // 键盘代码
    //           keyCode: 27, // 旧的键盘代码
    //           which: 27, // 新的键盘代码
    //           shiftKey: false, // 是否按下Shift键
    //           ctrlKey: false, // 是否按下Ctrl键
    //           metaKey: false, // 是否按下Meta键（Win键或Command键）
    //           bubbles: true, // 事件是否冒泡
    //           cancelable: true // 是否可以取消事件的默认行为
    //         });
    //         document.activeElement?.dispatchEvent(keyEvent);
    //       }
    //     }, 1000);
    //   }
    // }
  });
};

/**
 * 输出犯规
 */
watch(
  () => backReason.value.length,
  (v) => {
    backReasonStr.value = backReason.value[backReason.value.length - 1];
    setTimeout(() => {
      backReasonStr.value = '';
    }, 1500);
  },
  { immediate: true }
);

/**
 * 播报时间
 */
watch(
  () => time.value.countdownNum,
  (newData) => {
    if (examState.value != 42) {
      return false;
    }
    if (newData >= 30 && newData < time.value.testTime) {
      if (newData % 30 == 0) {
        speckText(`还有${newData}秒`);
      }
    }
    if (newData == 10) {
      speckText('还有10秒');
    }
    if (newData <= 5) {
      speckText(newData);
    }
    if (newData == 0) {
      speckText('哨声');
    }
  },
  { immediate: true }
);

/**
 * 成绩整数播报
 */
watch(
  () => currentResultObj.value,
  (newData: any, oldData: any) => {
    if (examState.value != 42 || newData.count <= 0) {
      return false;
    }
    let project = parameter.value.project;
    //引体向上比较慢所以都播报
    if (['pullup'].includes(project) && newData.count > 0 && oldData.back_num == oldData.back_num) {
      speckText(newData.count);
    }
    if (
      ['situp', 'sidepullup', 'jumprope', 'jumpingjack', 'highknees'].includes(project) &&
      newData.count > 0 &&
      newData.count % 10 == 0 &&
      oldData.back_num == oldData.back_num
    ) {
      speckText(newData.count);
    }
  },
  { immediate: true }
);

/**
 * 监听跳跃
 */
watch(
  () => currentResultObj.value.count,
  (newData) => {
    if (newData > 0 && ['jumprope'].includes(parameter.value.project)) {
      //let frameRate = 12;
      //gameContainer.value.sports(frameRate);
      squareGameRef.value.jump();
    }
  },
  { immediate: true }
);

/**
 * 时间转换
 */
// const countdownNumFormat = computed(() => {
//   return time.value.countdownNum;
//   //return proxy?.$utils.timeFormat(time.value.countdownNum);
// });

onBeforeMount(() => {
  parameter.value = route.query;
  let project = parameter.value.project;
  let area = parameter.value.area;
  parameter.value.examId = `${project}_${area}`; //项目+区
  if (parameter.value.time) {
    time.value.testTime = parameter.value.time;
  }
  time.value.countdownNum = time.value.testTime;
  userInfo.value = JSON.parse(myInfo);
  unit.value = dic.unit[project];
  if (parameter.value.gesture == 'true') {
    parameter.value.gesture = true;
  } else {
    parameter.value.gesture = false;
  }
  //需要开始按钮的项目
  let myList: any = [];
  if (myList.includes(project)) {
    needStart.value = true;
  }
  //加载WS
  initWs({ parameter: parameter.value, testTime: time.value.testTime }, (data: any) => {
    getMessage(data);
  });
  //初始化语音
  initSpeech();
  //初始化手势
  initHand();
  //加载音乐
  getMusic();
  //刷新关闭
  window.onbeforeunload = function (e) {
    var confirmationMessage = '刷新/关闭页面将会关闭页面,是否确认退出页面?';
    (e || window.event).returnValue = confirmationMessage; // 兼容 Gecko + IE
    let bUrl = import.meta.env.VITE_APP_BASE_API;
    let classId = parameter.value.classes;
    let project = parameter.value.project;
    let area = parameter.value.area;
    let examId = `${project}_${area}`;
    let mySid = sid.value;
    let token: any = localStorage.getItem('token');
    let formData = new FormData();
    formData.append('exam_id', examId);
    formData.append('class_id', classId);
    formData.append('token', token);
    formData.append('sid', mySid);
    navigator.sendBeacon(bUrl + '/exam/close_exam', formData);
    return confirmationMessage; // 兼容 Gecko + Webkit, Safari, Chrome
  };
});

onBeforeUnmount(() => {
  getExit();
});
</script>

<style lang="scss" scoped>
$topPadding: 5.19rem;
$waiPadding: 6.51rem;

.main {
  width: calc(100% - ($waiPadding * 2));
  height: 78vh;
  padding-top: 10rem;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  overflow: hidden;

  .main-left {
    width: 71.5%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;

    .main-left-top {
      display: flex;
      justify-content: space-between;
      height: 55.8%;
      position: relative;

      .top-left {
        width: 37.4%;
        height: 100%;
        border-radius: 1.6rem;
        background: radial-gradient(122% 126% at 97% 6%, #35ffc6 0%, #00ffe8 100%);
        text-align: center;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;

        .top-left-center {
          .pic {
            width: 22.3vh;
            height: 22.3vh;
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
            margin: 0 auto 2vh auto;

            img {
              width: 100%;
            }
          }

          .pic2 {
            box-sizing: border-box;
            border: 0.44rem solid rgba(26, 41, 58, 0.6315);
          }

          .name {
            width: 100%;
            color: #1a293a;
            font-size: 2.21rem;
          }

          .name2 {
            padding: 0 0.3rem;
            border-radius: 1.1rem;
            background: radial-gradient(96% 96% at 2% 32%, #ffffff 0%, #fcfdfd 54%, #e1e4e7 100%);
            box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
          }
        }
      }

      .top-right {
        width: 62%;
        height: 100%;
        border-radius: 1.6rem;
        opacity: 1;
        background: #ffffff;
        box-sizing: border-box;
        border: 0.55rem solid #13ed84;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        position: relative;

        .time {
          width: 28vh;
          height: 28vh;
          line-height: 28vh;
          border-radius: 50%;
          color: #ff9402;
          font-size: 11vh;
          text-align: center;
          background-image: url('@/assets/images/test/time.png');
          background-position: center;
          background-repeat: no-repeat;
          background-size: 100% 100%;
          position: absolute;
          right: -1.5vh;
          top: -11vh;
          font-family: 'Saira-BlackItalic';
        }

        .tips {
          display: flex;
          justify-content: center;
          align-items: center;
          height: 100%;

          img {
            max-height: 80%;
            max-height: 80%;
          }
        }

        .complete {
          width: 100%;
          display: flex;
          justify-content: center;
          flex-direction: column;

          .scoreBox {
            height: 10vh;
            color: #1a293a;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-bottom: 5vh;

            .score {
              font-size: 8.5rem;
              line-height: 8.5rem;
              font-family: 'Saira-BlackItalic';
            }

            .prompt {
              font-size: 3.5rem;
              line-height: 3.5rem;
            }

            .unit {
              font-size: 2rem;
              margin-left: 10px;
            }
          }

          .fractionViolation {
            height: 10vh;
            display: flex;
            align-items: center;
            justify-content: center;

            .fraction {
              height: 10vh;
              line-height: 10vh;
              border-radius: 5vh;
              display: flex;
              align-items: center;
              padding: 0 6%;
              background: linear-gradient(138deg, #38536c 21%, #1a293a 75%);
              box-shadow: inset 0px 1px 13px 0px rgba(255, 255, 255, 0.9452);

              .lable {
                font-size: 4vh;
                color: #13ed84;
              }

              .value {
                font-size: 7vh;
                color: #00ffe8;
                font-family: 'Saira-BlackItalic';
                min-width: 7vh;
              }
            }

            .violation {
              height: 6.1vh;
              line-height: 6.1vh;
              border-radius: 4vh;
              border: 0.25rem solid #ed7905;
              display: flex;
              align-items: center;
              margin-left: 11px;
              padding: 3px;
              box-sizing: content-box;

              .lable {
                font-size: 1.2rem;
                color: #ffffff;
                width: 6.1vh;
                height: 6.1vh;
                line-height: 6.1vh;
                background: #ed7905;
                border-radius: 50%;

                text-align: center;
              }

              .value {
                margin-left: 1.5vh;
                font-size: 3.2rem;
                color: #ed7905;
                font-family: 'Saira-BlackItalic';
                min-width: 6vh;
              }
            }
          }
        }

        .complete2 {
          padding-left: 20%;
          padding-top: 5vh;

          .scoreBox {
            margin-bottom: 3vh;
            justify-content: left;
          }

          .fractionViolation {
            justify-content: left;

            .fraction {
              padding: 0 8%;
            }
          }
        }

        .foulBox {
          height: calc(4.2vh + 0.5rem + 6px);
          overflow: hidden;
          display: flex;
          align-items: center;
          justify-content: center;
          padding-top: 3vh;

          .foul {
            height: 4.2vh;
            line-height: 4.2vh;
            border-radius: 3vh;
            border: 0.25rem solid #ed7905;
            display: flex;
            align-items: center;
            margin-left: 11px;
            padding: 3px;
            box-sizing: content-box;

            .lable {
              font-size: 2rem;
              color: #ffffff;
              width: 4.2vh;
              height: 4.2vh;
              line-height: 4.2vh;
              background: #ed7905;
              border-radius: 50%;
              text-align: center;
            }

            .value {
              margin-left: 1.5vh;
              font-size: 2rem;
              color: #ed7905;
              font-family: 'Saira-BlackItalic';
              min-width: 6vh;
              padding: 0 10px;
            }
          }
        }

        .readyBoxBefore {
          display: flex;
          justify-content: center;
          font-size: 2.5rem;
          color: #1a293a;
          padding-top: 4vh;
          line-height: 0;
          margin-bottom: 2vh;

          .item {
            display: flex;
            justify-content: center;

            .lable {
              display: flex;
              align-items: center;
              margin-left: 10px;
            }
          }

          img {
            height: 20vh;
          }
        }

        .readyBox {
          text-align: center;
          color: #1a293a;

          .value {
            font-size: 8.5rem;
            line-height: 8.5rem;
            font-family: 'Saira-BlackItalic';
          }

          .lable {
            font-size: 3.5rem;
          }

          .transparent {
            opacity: 0;
          }
        }

        .btn {
          font-size: 2.21rem;
          color: #ffffff;
          text-align: center;
          width: 50%;
          line-height: 8vh;
          line-height: 8vh;
          border-radius: 15px;
          opacity: 1;
          background: radial-gradient(159% 126% at 5% 93%, #f99f02 0%, #ed7905 100%);
          box-shadow: 3px 6px 4px 1px rgba(0, 0, 0, 0.1874), inset 0px 1px 0px 2px rgba(255, 255, 255, 0.3);
          cursor: pointer;
        }

        .btn2 {
          width: auto;
          padding: 0 10px;
        }

        .exitBtn {
          color: #ffffff;
          background: radial-gradient(159% 126% at 5% 93%, #931b1b 0%, #ec5624 0%, #ff6860 76%);
          box-shadow: 3px 6px 4px 1px rgba(0, 0, 0, 0.1874), inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);

          &:hover {
            background: #ec5624;
          }
        }
      }

      i {
        width: 4vw;
        height: 4vw;
        display: block;
        position: absolute;
        top: 50%;
        left: 37.5%;
        margin-top: calc(4vw * -0.5);
        margin-left: calc(4vw * -0.5);
        background-image: url('@/assets/images/test/yuan.png');
        background-position: center;
        background-repeat: no-repeat;
        background-size: 100% 100%;
        border-radius: 50%;
        flex-shrink: 0;
        transition: all 0.5s;
      }
    }

    .main-left-bottom {
      display: flex;
      justify-content: space-between;
      height: calc(100% - 55.8% - 3vh);
      overflow: hidden;

      .bottom-left {
        width: 58%;
        padding-right: 1rem;
        display: flex;
        flex-direction: column;

        .tips {
          height: 2.8vh;

          img {
            height: 100%;
          }
        }

        .pic {
          text-align: center;
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          overflow: hidden;

          img {
            max-width: 100%;
            max-height: 100%;
          }
        }
      }

      .bottom-right {
        width: 41%;
        height: 100%;
        overflow-y: scroll;
        color: #f9f9f9;
        font-size: 1.1rem;
        line-height: 1.6rem;

        &::-webkit-scrollbar {
          width: 10px;
        }

        &::-webkit-scrollbar-thumb {
          border-width: 2px;
          border-radius: 4px;
          border-style: dashed;
          border-color: transparent;
          background-color: rgba(26, 62, 78, 0.9);
          background-clip: padding-box;
        }

        &::-webkit-scrollbar-button:hover {
          border-radius: 6px;
          background: rgba(26, 62, 78, 1);
        }
      }
    }
  }

  .main-right {
    width: 27%;
    border-radius: 1.6rem;
    background: linear-gradient(29deg, #092941 -82%, #2a484b 94%);
    box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.4);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
}

.close {
  position: absolute;
  // right: calc($waiPadding - 3.2rem);
  left: auto;
  right: calc($topPadding / 2 - 3.2rem / 4);
  top: auto;
  bottom: calc($topPadding / 2 - 3.2rem / 4);
}
</style>
