multiple.vue 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888
  1. <template>
  2. <div>
  3. <Header @confirmExit="confirmExit"></Header>
  4. <Transition :enter-active-class="proxy?.animate.dialog.enter" :leave-active-class="proxy?.animate.dialog.leave">
  5. <div class="time"
  6. :class="{ 'time1': styleType == 1, 'time2': styleType == 2, 'time3': styleType == 3, 'time4': styleType == 4, }"
  7. v-show="(needStart && [42].includes(examState)) || (['jumprope', 'jumpingjack', 'situp'].includes(parameter.project) && [42].includes(examState))">
  8. {{
  9. time.countdownNum
  10. }}</div>
  11. </Transition>
  12. <div class="main">
  13. <div class="testBox"
  14. :class="{ 'testBox1': styleType == 1, 'testBox2': styleType == 2, 'testBox3': styleType == 3, 'testBox4': styleType == 4 }">
  15. <div class="ul"
  16. :class="{ 'overlap': (examState == 43 && time.ready) || [42].includes(examState) || (showTestAgain && ![41].includes(examState)), 'ready': [41].includes(examState), 'hands': parameter.gesture }"
  17. v-for="(items, indexs) in testListArr " :key="indexs">
  18. <MultipleItem :ref="(el: any) => { multipleItemRef(el, item.itemNumber, item.area) }"
  19. v-for="(item, index) in items" :query="parameter" :area="item.area" :key="index" @returnData="returnData"
  20. @getChooseStudent="getChooseStudent" :examState="examState" :needStart="needStart" :styleType="styleType"
  21. :parentTrainWsMethod="{ initWs, examEnds, openOneTest, startFace, stopFace, faceConfirmOnly, startOneTest, finishOneTest, closeOneTest, suspendFaceRecognitionChannels, resumeFaceRecognitionChannels }"
  22. :parentSpeechMethod="{ initSpeech, speckText, playMusic, controlMusic, speckCancel, chineseNumber }" />
  23. </div>
  24. </div>
  25. </div>
  26. <div class="footerBtn">
  27. <template v-if="needStart">
  28. <div class="btn" @click="getAgain" v-if="examState == 42 || showTestAgain">再测一次</div>
  29. <div class="btn" @click="getOpenOneTestAndStartFace" v-if="examState < 41">开始识别</div>
  30. <div class="btn" @click="getStopFace" v-if="examState == 41">停止人脸识别</div>
  31. <div class="btn startBtn" @click="getReady" v-if="examState == 43">开始测试</div>
  32. <div class="btn" @click="getAllRetestFace" v-if="examState == 43 || examState == 42">重新识别</div>
  33. </template>
  34. <template v-else>
  35. <template v-if="examState == 43">
  36. <div class="btn startBtn" @click="getReady"
  37. v-if="['jumprope', 'jumpingjack', 'situp'].includes(parameter.project)">开始测试</div>
  38. <div class="btn startBtn" @click="getStartOneTest" v-else>开始测试</div>
  39. </template>
  40. </template>
  41. <div class="btn" :class="{ testRecordBtn: showReportList }" @click="getTestRecord"
  42. v-if="![43, 42].includes(examState)">
  43. {{ showReportList ? '关闭记录' : '测试记录' }}
  44. </div>
  45. <!--测试记录开始-->
  46. <Transition :enter-active-class="proxy?.animate.mask.enter" :leave-active-class="proxy?.animate.mask.leave">
  47. <div class="mask mask2" v-show="showReportList" @click="getTestRecord"></div>
  48. </Transition>
  49. <Transition :enter-active-class="proxy?.animate.report.enter" :leave-active-class="proxy?.animate.report.leave">
  50. <div class="main-right" v-show="showReportList">
  51. <ReportList ref="reportListRef" :parameter="parameter" :showQRCode="true" />
  52. </div>
  53. </Transition>
  54. <!--测试记录结束-->
  55. </div>
  56. <!--倒计时开始-->
  57. <div>
  58. <Transition :enter-active-class="proxy?.animate.mask.enter" :leave-active-class="proxy?.animate.mask.leave">
  59. <div class="mask" v-show="examState == 43 && time.ready"></div>
  60. </Transition>
  61. <Transition :enter-active-class="proxy?.animate.face.enter" :leave-active-class="proxy?.animate.face.leave2">
  62. <div class="confirmDiaBg" v-show="examState == 43 && time.ready">
  63. <div class="confirmDiaWindow">
  64. <div class="readyBox">
  65. <div class="lable">倒计时</div>
  66. <div class="value" :class="{ 'transparent': time.ready > 5 }">{{ time.ready }}</div>
  67. </div>
  68. </div>
  69. </div>
  70. </Transition>
  71. </div>
  72. <!--倒计时结束-->
  73. <ChooseStudent ref="chooseStudentRef" @returnData="returnStudent" />
  74. </div>
  75. </template>
  76. <script setup name="Multiple" lang="ts">
  77. import { useWs } from '@/utils/trainWs';
  78. // import { initWs, examEnds, openOneTest, startFace, stopFace, faceConfirmOnly, startOneTest, finishOneTest, closeOneTest, suspendFaceRecognitionChannels, resumeFaceRecognitionChannels } from '@/utils/ws'
  79. import { initSpeech, speckText, playMusic, controlMusic, speckCancel, chineseNumber } from '@/utils/speech'
  80. import { useWebSocket } from '@/utils/handWs';
  81. const { handWs, startDevice, startHand, stateHand } = useWebSocket();
  82. const { initWs, examEnds, openOneTest, startFace, stopFace, faceConfirmOnly, startOneTest, finishOneTest, closeOneTest, suspendFaceRecognitionChannels, resumeFaceRecognitionChannels } = useWs();
  83. const { proxy } = getCurrentInstance() as any;
  84. const router = useRouter();
  85. const route = useRoute();
  86. const reportListRef = ref();
  87. const chooseStudentRef = ref();
  88. const data = reactive<any>({
  89. timerManager: {},//计时器管理
  90. parameter: {},//参数
  91. time: {
  92. testTime: 60,//时长
  93. countdownNum: 0,//计时
  94. ready: 0,//预备
  95. exit: 0,//退出倒计时
  96. },
  97. userInfo: {},//用户信息
  98. examState: 0,//当前状态
  99. needStart: false,//是否需要按钮
  100. showTestAgain: false,//再测一次按钮
  101. testList: [],//获取区列表
  102. multipleItemRefList: [],//获取区列表
  103. styleType: null,//展示样式1:1-5个,2:6-10个,3:10个以上,4:1-4个,
  104. showReportList: false,//显示隐藏测试记录
  105. exitStatus: 0,//退出响应次数
  106. sid: null,//WS的id
  107. chooseStudentArea: {},//弹出手动选择对应的区号
  108. device_info: {},//设备信息
  109. });
  110. const { timerManager, parameter, time, userInfo, examState, needStart, showTestAgain, testList, multipleItemRefList, styleType, showReportList, exitStatus, sid, chooseStudentArea, device_info } = toRefs(data);
  111. /**
  112. * 创建组件实例
  113. */
  114. const multipleItemRef = (el: any, index: number, area: any) => {
  115. multipleItemRefList.value[index - 1] = el;
  116. }
  117. /**
  118. * 开始识别
  119. */
  120. const getOpenOneTestAndStartFace = () => {
  121. cleanData();
  122. examState.value = 41;
  123. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  124. multipleItemRefList.value[i].getOpenOneTestAndStartFace()
  125. }
  126. };
  127. /**
  128. * 停止人脸识别
  129. */
  130. const getStopFace = async () => {
  131. let flag = false;
  132. for (let i = 0; i < testList.value.length; i++) {
  133. if (testList.value[i] && testList.value[i].faceCheckStu?.student_id) {
  134. flag = true;
  135. }
  136. }
  137. if (!flag) {
  138. proxy?.$modal.msgWarning("请选择人员!");
  139. return false;
  140. }
  141. examState.value = 43;
  142. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  143. multipleItemRefList.value[i].getStopFace()
  144. }
  145. };
  146. /**
  147. * 开始测试
  148. */
  149. const getStartOneTest = () => {
  150. if (examState.value != 43) {
  151. return false;
  152. }
  153. let flag = false;
  154. for (let i = 0; i < testList.value.length; i++) {
  155. if (testList.value[i] && testList.value[i].faceCheckStu?.student_id) {
  156. flag = true;
  157. }
  158. }
  159. if (!flag) {
  160. proxy?.$modal.msgWarning("请选择人员!");
  161. return false;
  162. }
  163. getClearTimer("readyTimer");
  164. time.value.ready = 0;
  165. examState.value = 42;
  166. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  167. if (testList.value[i].examState == 41) {
  168. //正在识别的停止识别
  169. multipleItemRefList.value[i].getStopFace()
  170. }
  171. if (testList.value[i].examState == 43) {
  172. multipleItemRefList.value[i].getStartOneTest()
  173. }
  174. }
  175. speckText("哨声");
  176. if (parameter.value.music) {
  177. playMusic(parameter.value.music);
  178. }
  179. //显示再测一次按钮
  180. showTestAgain.value = true;
  181. //时间为0的为正计时,大于0的为倒计时
  182. if (time.value.testTime == 0) {
  183. getCounting("+");
  184. } else {
  185. getCounting("-");
  186. }
  187. };
  188. /**
  189. * 再测一次
  190. */
  191. const getAgain = () => {
  192. let loading = ElLoading.service({ text: '请稍等...', background: 'rgba(0, 0, 0, 0.8)', customClass: `sports ${parameter.value.project}` });
  193. cleanData();
  194. examState.value = 41;
  195. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  196. multipleItemRefList.value[i].getAgain()
  197. }
  198. timerManager.value.againTimer = setInterval(() => {
  199. if (examState.value == 43) {
  200. getClearTimer("againTimer");
  201. loading?.close();
  202. }
  203. }, 300);
  204. let loadingTime = setTimeout(() => {
  205. if (examState.value <= 41) {
  206. loading?.close();
  207. clearTimeout(loadingTime);
  208. getClearTimer("againTimer");
  209. }
  210. }, 10000);
  211. };
  212. /**
  213. * 重新识别
  214. */
  215. const getAllRetestFace = async () => {
  216. let loading = ElLoading.service({ text: '请稍等...', background: 'rgba(0, 0, 0, 0.8)', customClass: `sports ${parameter.value.project}` });
  217. showTestAgain.value = false;
  218. examState.value = 3;
  219. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  220. multipleItemRefList.value[i].getAllRetestFace()
  221. }
  222. timerManager.value.allRetestFaceTimer = setInterval(() => {
  223. if (examState.value == 41) {
  224. getClearTimer("allRetestFaceTimer");
  225. loading?.close();
  226. }
  227. }, 300);
  228. let loadingTime = setTimeout(() => {
  229. if (examState.value <= 3) {
  230. loading?.close();
  231. clearTimeout(loadingTime);
  232. getClearTimer("allRetestFaceTimer");
  233. }
  234. }, 10000);
  235. };
  236. /**
  237. * 确认退出
  238. */
  239. const confirmExit = () => {
  240. let handcontroller_id = parameter.value.handcontroller;
  241. proxy?.$modal.confirm(handcontroller_id ? `请保持两秒确认退出` : `确定退出吗?`).then(() => {
  242. getExit();
  243. }).finally(() => {
  244. });
  245. };
  246. /**
  247. * 退出
  248. */
  249. const getExit = () => {
  250. getClearTimer();//清除计时器
  251. examEnds();//通知工作站关闭
  252. speckCancel();//停止播报
  253. window.onbeforeunload = null;//移除事件处理器
  254. let handcontroller_id = parameter.value.handcontroller;
  255. if (handcontroller_id) {
  256. router.push({ path: '/gesture' });//跳转
  257. } else {
  258. router.push({ path: '/' });//跳转
  259. }
  260. };
  261. /**
  262. * 清空定时任务
  263. */
  264. const getClearTimer = (data?: any) => {
  265. if (data) {
  266. //清除指定
  267. clearInterval(timerManager.value[data])
  268. timerManager.value[data] = null;
  269. } else {
  270. //清除全部
  271. for (let key in timerManager.value) {
  272. if (timerManager.value.hasOwnProperty(key)) {
  273. clearInterval(timerManager.value[key])
  274. timerManager.value[key] = null;
  275. }
  276. }
  277. }
  278. };
  279. /**
  280. * 清除历史记录
  281. */
  282. const cleanData = () => {
  283. getClearTimer("countdownTimer");
  284. time.value.countdownNum = time.value.testTime;
  285. showTestAgain.value = false;
  286. };
  287. /**
  288. * 时间转换
  289. */
  290. // const countdownNumFormat = computed(() => {
  291. // return time.value.countdownNum;
  292. // //return proxy?.$utils.timeFormat(time.value.countdownNum);
  293. // });
  294. /**
  295. * 倒计时
  296. */
  297. const getCounting = (type: string) => {
  298. timerManager.value.countdownTimer = setInterval(() => {
  299. //正计时
  300. if (type == "+") {
  301. time.value.countdownNum++;
  302. }
  303. //倒计时
  304. if (type == "-") {
  305. if (time.value.countdownNum <= 0) {
  306. getClearTimer("countdownTimer");
  307. } else {
  308. time.value.countdownNum--;
  309. }
  310. }
  311. }, 1000);
  312. };
  313. /**
  314. * 子组件选择学生
  315. */
  316. const getChooseStudent = (data: any) => {
  317. console.log("data", data)
  318. chooseStudentArea.value = data;
  319. chooseStudentRef.value.open();
  320. }
  321. /**
  322. * 返回被选学生
  323. */
  324. const returnStudent = (data: any) => {
  325. console.log("data", data)
  326. let area = chooseStudentArea.value.area;
  327. let index = testList.value.findIndex((item: any) => {
  328. return area == item.area;
  329. })
  330. console.log("index", index)
  331. multipleItemRefList.value[index]?.returnStudent(data);
  332. };
  333. /**
  334. * 子组件返回数据
  335. */
  336. const returnData = (data: any) => {
  337. let index = testList.value.findIndex((item: any) => {
  338. return item.area == data.area;
  339. });
  340. let obj = Object.assign(testList.value[index], data);
  341. testList.value[index] = JSON.parse(JSON.stringify(obj));
  342. console.log("testList.value", testList.value)
  343. if (examState.value == 0) {
  344. let flag = false;
  345. //只监听人脸识别的区
  346. let newList = testList.value;
  347. for (let i = 0; i < newList.length; i++) {
  348. if (newList[i] && newList[i].examState == 3) {
  349. flag = true;
  350. }
  351. }
  352. if (flag) {
  353. console.log("变更状态:", 3)
  354. examState.value = 3;
  355. if (needStart.value) {
  356. let handcontroller = parameter.value.handcontroller;
  357. let ctrl = parameter.value.ctrl;
  358. if (handcontroller && ctrl) {
  359. speckText(`请${ctrl}区举左手开始识别`);
  360. } else {
  361. proxy?.$modal.msgWarning(`请点击右下角的【开始识别】`);
  362. }
  363. }
  364. }
  365. }
  366. if (examState.value == 3) {
  367. let flag = false;
  368. //只监听人脸识别的区
  369. let newList = testList.value;
  370. for (let i = 0; i < newList.length; i++) {
  371. if (newList[i] && newList[i].examState == 40) {
  372. flag = true;
  373. }
  374. }
  375. if (flag) {
  376. console.log("变更状态:", 40)
  377. examState.value = 40;
  378. }
  379. }
  380. if (examState.value == 40) {
  381. let flag = false;
  382. //只监听人脸识别的区
  383. let newList = testList.value;
  384. for (let i = 0; i < newList.length; i++) {
  385. if (newList[i] && newList[i].examState == 41) {
  386. flag = true;
  387. }
  388. }
  389. if (flag) {
  390. console.log("变更状态:", 41)
  391. examState.value = 41;
  392. let txt = parameter.value.gesture ? "请举右手看摄像头人脸识别" : "请看摄像头进行人脸识别";
  393. speckText(txt);
  394. //如果过段时候后仍在识别中就提示停止识别
  395. // setTimeout(() => {
  396. // if (examState.value == 41) {
  397. // let handcontroller = parameter.value.handcontroller;
  398. // let ctrl = parameter.value.ctrl;
  399. // if (handcontroller && ctrl) {
  400. // speckText(`各就位后请${ctrl}区举左手停止识别`);
  401. // } else {
  402. // proxy?.$modal.msgWarning(`各就位后请点击右下角的【停止人脸识别】`);
  403. // }
  404. // }
  405. // }, 15000)
  406. }
  407. }
  408. if (examState.value == 41) {
  409. let flag = false;
  410. //只监听人脸识别的区
  411. let newList = testList.value.filter((item: any) => {
  412. return item?.faceCheckStu?.student_id;
  413. })
  414. for (let i = 0; i < newList.length; i++) {
  415. if (newList[i] && newList[i].faceCheckStu?.student_id && newList[i].examState == 43) {
  416. flag = true;
  417. }
  418. }
  419. if (flag) {
  420. console.log("变更状态:", 43)
  421. examState.value = 43;
  422. cleanData();
  423. //如果过段时候后仍在识别中就提示停止识别
  424. // setTimeout(() => {
  425. // if (examState.value == 43) {
  426. // let handcontroller = parameter.value.handcontroller;
  427. // let ctrl = parameter.value.ctrl;
  428. // if (handcontroller && ctrl) {
  429. // speckText(`请${ctrl}区举左手开始测试`);
  430. // } else {
  431. // proxy?.$modal.msgWarning(`请点击右下角的【开始测试】`);
  432. // }
  433. // }
  434. // }, 10000)
  435. }
  436. }
  437. //测试完成后回退状态
  438. if (examState.value == 42) {
  439. let flag = false;
  440. //只监听人脸识别的区
  441. let newList = testList.value.filter((item: any) => {
  442. return item?.faceCheckStu?.student_id;
  443. })
  444. for (let i = 0; i < newList.length; i++) {
  445. if (newList[i] && newList[i].faceCheckStu?.student_id && newList[i].examState == 3) {
  446. flag = true;
  447. } else {
  448. return false;
  449. }
  450. }
  451. if (flag) {
  452. console.log("变更状态:", 3)
  453. examState.value = 3;
  454. }
  455. }
  456. //如果全部状态为0就退出
  457. if (examState.value >= 0) {
  458. let flag = false;
  459. //只监听人脸识别的区
  460. let newList = testList.value;
  461. for (let i = 0; i < newList.length; i++) {
  462. if (newList[i] && newList[i].examState == 0) {
  463. flag = true;
  464. } else {
  465. return false;
  466. }
  467. }
  468. if (flag) {
  469. examState.value = 0;
  470. getExit();
  471. }
  472. }
  473. };
  474. /**
  475. * 准备开始
  476. */
  477. const getReady = () => {
  478. if (examState.value != 43) {
  479. return false;
  480. }
  481. if (time.value.ready) {
  482. return false;
  483. }
  484. let flag = false;
  485. for (let i = 0; i < testList.value.length; i++) {
  486. if (testList.value[i] && testList.value[i].faceCheckStu?.student_id) {
  487. flag = true;
  488. }
  489. }
  490. if (!flag) {
  491. proxy?.$modal.msgWarning("请选择人员!");
  492. return false;
  493. }
  494. //停止播报;
  495. speckCancel()
  496. //正在识别的停止识别
  497. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  498. if (testList.value[i].examState == 41) {
  499. multipleItemRefList.value[i].getStopFace()
  500. }
  501. }
  502. time.value.ready = 6;
  503. timerManager.value.readyTimer = setInterval(() => {
  504. time.value.ready--;
  505. if (time.value.ready <= 0) {
  506. getClearTimer("readyTimer");
  507. getStartOneTest();
  508. } else {
  509. speckText(time.value.ready);
  510. }
  511. }, 1000);
  512. };
  513. /**
  514. * 将测试列表转为多行
  515. */
  516. const testListArr = computed(() => {
  517. let list: any = [];
  518. let num = 0;
  519. if (styleType.value == 1) {
  520. num = 3
  521. }
  522. if (styleType.value == 2) {
  523. num = 5
  524. }
  525. if (styleType.value == 3) {
  526. num = 10
  527. }
  528. if (styleType.value == 4) {
  529. num = 4
  530. }
  531. let myLength = Math.ceil(testList.value.length / num);
  532. for (let i = 0; i < myLength; i++) {
  533. list[i] = [];
  534. for (let j = 0; j < testList.value.length; j++) {
  535. if (j >= i * num && j < (i + 1) * num) {
  536. list[i].push(testList.value[j])
  537. }
  538. }
  539. }
  540. console.log("list", list)
  541. return list;
  542. });
  543. /**
  544. * 将测试列表转为多行
  545. */
  546. const getTestRecord = () => {
  547. if (showReportList.value) {
  548. showReportList.value = false;
  549. } else {
  550. reportListRef.value.getIniReportList();
  551. showReportList.value = true;
  552. }
  553. };
  554. /**
  555. * 自动填补空缺
  556. */
  557. const getAddTestList = (num: number) => {
  558. let list = parameter.value.area.split(',');
  559. let myLength = num - list.length;
  560. for (let i = 0; i < myLength; i++) {
  561. let obj = {
  562. area: "",
  563. itemNumber: list.length + (i + 1)
  564. }
  565. testList.value.push(
  566. obj
  567. )
  568. }
  569. };
  570. /**
  571. * 获取设备项目
  572. */
  573. const getDevice = async () => {
  574. let deviceid = localStorage.getItem("deviceid") || '';
  575. if (deviceid) {
  576. startDevice({ deviceid: deviceid })
  577. } else {
  578. proxy?.$modal.msgError(`缺少设备信息请重新登录!`);
  579. await proxy?.$http.common.logout({}).then((res: any) => {
  580. });
  581. proxy?.$modal?.closeLoading()
  582. //清空缓存
  583. localStorage.clear();
  584. //跳转
  585. router.push({ path: '/login/qrcode' });
  586. }
  587. };
  588. /**
  589. * 加载手势WS
  590. */
  591. const initHand = () => {
  592. handWs((e: any) => {
  593. if (router.currentRoute.value.path != '/train/multiple' || parameter.value.handcontroller == undefined) {
  594. return false;
  595. }
  596. console.log("eeeee", e)
  597. if (e?.wksid) {
  598. //获取设备项目
  599. getDevice()
  600. }
  601. //接收设备信息
  602. if (e?.device_info) {
  603. device_info.value = e.device_info;
  604. let handcontroller_id = device_info.value.handcontroller_id;
  605. stateHand(handcontroller_id);
  606. }
  607. //获取手势状态
  608. if (e?.cmd == 'get_handcontroller_state' && e?.state == 0) {
  609. let handcontroller_id = device_info.value.handcontroller_id;
  610. startHand(handcontroller_id);
  611. }
  612. //刷新
  613. if (e?.data?.result == "refresh") {
  614. getExit();
  615. //刷新
  616. window.location.reload()
  617. }
  618. //没初始化完成不监听手势动作
  619. if (examState.value == 0) {
  620. return false;
  621. }
  622. //左滑动
  623. if (e?.data?.result == "next_item") {
  624. proxy?.$modal.msgSuccess('手势指令:左滑动');
  625. // if(examState.value == 43 && time.value.ready){
  626. // return false;
  627. // }
  628. // if (examState.value == 43 || examState.value == 42) {
  629. // speckCancel();//停止播报
  630. // getAllRetestFace();
  631. // }
  632. }
  633. //举左手
  634. if (e?.data?.result == "left_hand") {
  635. proxy?.$modal.msgSuccess('手势指令:举左手');
  636. //举左手确认退出
  637. // if (exitStatus.value) {
  638. // exitStatus.value = 0;
  639. // //确认退出
  640. // let keyEvent: any = null;
  641. // keyEvent = new KeyboardEvent('keydown', {
  642. // key: 'Enter', // 键值
  643. // code: 'Enter', // 键盘代码
  644. // keyCode: 13, // 旧的键盘代码
  645. // which: 13, // 新的键盘代码
  646. // shiftKey: false, // 是否按下Shift键
  647. // ctrlKey: false, // 是否按下Ctrl键
  648. // metaKey: false, // 是否按下Meta键(Win键或Command键)
  649. // bubbles: true, // 事件是否冒泡
  650. // cancelable: true // 是否可以取消事件的默认行为
  651. // });
  652. // document.activeElement?.dispatchEvent(keyEvent);
  653. // return false;
  654. // }
  655. //开始识别
  656. if (needStart.value && examState.value < 41) {
  657. getOpenOneTestAndStartFace();
  658. return false;
  659. }
  660. //停止人脸识别
  661. if (needStart.value && examState.value == 41) {
  662. getStopFace();
  663. return false;
  664. }
  665. //开始测试
  666. if (examState.value == 43) {
  667. getReady()
  668. return false;
  669. }
  670. }
  671. //退出
  672. if (e?.data?.result == "exit") {
  673. proxy?.$modal.msgSuccess('手势指令:交叉手');
  674. // console.log("exitStatus.value", exitStatus.value)
  675. if (exitStatus.value == 0) {
  676. speckText("请保持两秒确认退出");
  677. //第一次才弹出
  678. confirmExit();
  679. setTimeout(() => {
  680. let keyEvent: any = null;
  681. let myKey = null;
  682. //如果交叉手两秒后返回超过4次就确认退出
  683. if (exitStatus.value >= 4) {
  684. myKey = 'Enter';
  685. } else {
  686. myKey = 'Esc';
  687. }
  688. if (myKey == 'Esc') {
  689. keyEvent = new KeyboardEvent('keydown', {
  690. key: 'Escape', // 键值
  691. code: 'Escape', // 键盘代码
  692. keyCode: 27, // 旧的键盘代码
  693. which: 27, // 新的键盘代码
  694. shiftKey: false, // 是否按下Shift键
  695. ctrlKey: false, // 是否按下Ctrl键
  696. metaKey: false, // 是否按下Meta键(Win键或Command键)
  697. bubbles: true, // 事件是否冒泡
  698. cancelable: true // 是否可以取消事件的默认行为
  699. });
  700. exitStatus.value = 0;
  701. }
  702. if (myKey == 'Enter') {
  703. keyEvent = new KeyboardEvent('keydown', {
  704. key: 'Enter', // 键值
  705. code: 'Enter', // 键盘代码
  706. keyCode: 13, // 旧的键盘代码
  707. which: 13, // 新的键盘代码
  708. shiftKey: false, // 是否按下Shift键
  709. ctrlKey: false, // 是否按下Ctrl键
  710. metaKey: false, // 是否按下Meta键(Win键或Command键)
  711. bubbles: true, // 事件是否冒泡
  712. cancelable: true // 是否可以取消事件的默认行为
  713. });
  714. }
  715. document.activeElement?.dispatchEvent(keyEvent);
  716. }, 2500)
  717. }
  718. exitStatus.value = exitStatus.value + 1
  719. }
  720. // if (e?.data?.result == "exit") {
  721. // console.log("exitStatus.value", exitStatus.value)
  722. // if (exitStatus.value == 0) {
  723. // exitStatus.value = 1
  724. // speckText("请5秒内举左手确认退出");
  725. // //第一次才弹出
  726. // confirmExit();
  727. // time.value.exit = 6;
  728. // timerManager.value.exitTimer = setInterval(() => {
  729. // time.value.exit--;
  730. // console.log("取消倒计时", time.value.exit)
  731. // proxy?.$modal.msgWarning(`取消倒计时:${time.value.exit}`)
  732. // if (time.value.exit == 0) {
  733. // exitStatus.value = 0;
  734. // getClearTimer("exitTimer");
  735. // let keyEvent: any = null;
  736. // keyEvent = new KeyboardEvent('keydown', {
  737. // key: 'Escape', // 键值
  738. // code: 'Escape', // 键盘代码
  739. // keyCode: 27, // 旧的键盘代码
  740. // which: 27, // 新的键盘代码
  741. // shiftKey: false, // 是否按下Shift键
  742. // ctrlKey: false, // 是否按下Ctrl键
  743. // metaKey: false, // 是否按下Meta键(Win键或Command键)
  744. // bubbles: true, // 事件是否冒泡
  745. // cancelable: true // 是否可以取消事件的默认行为
  746. // });
  747. // document.activeElement?.dispatchEvent(keyEvent);
  748. // }
  749. // }, 1000);
  750. // }
  751. // }
  752. });
  753. };
  754. /**
  755. * 播报时间
  756. */
  757. watch(() => time.value.countdownNum, (newData) => {
  758. if (examState.value != 42) {
  759. return false;
  760. }
  761. if (newData >= 30) {
  762. if (newData % 30 == 0) {
  763. speckText(
  764. `还有${newData}秒`
  765. );
  766. }
  767. }
  768. if (newData == 10) {
  769. speckText("还有10秒");
  770. }
  771. if (newData <= 5) {
  772. speckText(newData);
  773. }
  774. if (newData == 0) {
  775. speckText("哨声");
  776. }
  777. }, { immediate: true });
  778. onBeforeMount(() => {
  779. parameter.value = route.query;
  780. let project = parameter.value.project;
  781. let area = parameter.value.area;
  782. parameter.value.examId = `${project}_${area}`; //项目+区
  783. if (parameter.value.time) {
  784. time.value.testTime = parameter.value.time
  785. }
  786. time.value.countdownNum = time.value.testTime;
  787. let myInfo: any = localStorage.getItem("userInfo");
  788. userInfo.value = JSON.parse(myInfo);
  789. if (parameter.value.gesture == 'true') {
  790. parameter.value.gesture = true
  791. } else {
  792. parameter.value.gesture = false
  793. }
  794. let list = parameter.value.area.split(',');
  795. testList.value = list.map((item: any, index: number) => {
  796. let obj = {
  797. area: item,
  798. itemNumber: index + 1
  799. }
  800. return obj;
  801. });
  802. //展示样式控制
  803. if (testList.value.length == 5) {
  804. styleType.value = 1;
  805. //填补空缺
  806. getAddTestList(5)
  807. }
  808. if (testList.value.length > 5 && testList.value.length <= 10) {
  809. styleType.value = 2;
  810. //填补空缺
  811. getAddTestList(10)
  812. }
  813. if (testList.value.length > 10) {
  814. styleType.value = 3;
  815. }
  816. if (testList.value.length < 5) {
  817. styleType.value = 4;
  818. }
  819. //需要开始按钮的项目
  820. let myList = ['jumprope', 'jumpingjack', 'situp'];
  821. if (myList.includes(project) && styleType.value == 3) {
  822. needStart.value = true;
  823. }
  824. })
  825. onMounted(() => {
  826. //加载WS
  827. let project = parameter.value.project;
  828. initWs({ parameter: parameter.value, testTime: time.value.testTime, version: "v2" }, (data: any) => {
  829. //获取sid
  830. if (data.cmd === 'mySid') {
  831. console.log("data.data.sid", data.data.sid)
  832. sid.value = data.data.sid;
  833. }
  834. let index = testList.value.findIndex((item: any) => {
  835. let examId = `${project}_${item.area}`;
  836. return examId == data.exam_id;
  837. })
  838. multipleItemRefList.value[index]?.getMessage(data);
  839. });
  840. //初始化语音
  841. initSpeech();
  842. //初始化手势
  843. initHand();
  844. setTimeout(() => {
  845. //10秒还在0状态就算超时
  846. if (examState.value == 0) {
  847. getExit();
  848. }
  849. }, 10000);
  850. //刷新关闭
  851. window.onbeforeunload = function (e) {
  852. var confirmationMessage = "刷新/关闭页面将会关闭页面,是否确认退出页面?";
  853. (e || window.event).returnValue = confirmationMessage; // 兼容 Gecko + IE
  854. testList.value.forEach((item: any) => {
  855. let examId = item.exam_id;
  856. let bUrl = import.meta.env.VITE_APP_BASE_API;
  857. let classId = parameter.value.classes;
  858. let mySid = sid.value;
  859. let token: any = localStorage.getItem("token")
  860. let formData = new FormData();
  861. formData.append("exam_id", examId);
  862. formData.append("class_id", classId);
  863. formData.append("token", token);
  864. formData.append("sid", mySid);
  865. navigator.sendBeacon(bUrl + "/exam/close_exam", formData)
  866. })
  867. return confirmationMessage; // 兼容 Gecko + Webkit, Safari, Chrome
  868. };
  869. })
  870. onBeforeUnmount(() => {
  871. getExit();
  872. })
  873. </script>
  874. <style scoped lang="scss">
  875. $waiPadding: 6.51rem;
  876. .time {
  877. width: 20vh;
  878. height: 20vh;
  879. line-height: 20vh;
  880. border-radius: 50%;
  881. color: #FF9402;
  882. font-size: 8vh;
  883. text-align: center;
  884. background-image: url("@/assets/images/test/time.png");
  885. background-position: center;
  886. background-repeat: no-repeat;
  887. background-size: 100% 100%;
  888. position: absolute;
  889. right: 50%;
  890. top: -4vh;
  891. margin-right: -10vh;
  892. font-family: 'Saira-BlackItalic';
  893. }
  894. .time1,
  895. .time4 {
  896. width: 26vh;
  897. height: 26vh;
  898. line-height: 26vh;
  899. font-size: 10.3vh;
  900. right: auto;
  901. left: 50%;
  902. top: 0vh;
  903. margin-left: 8vw;
  904. margin-right: auto;
  905. }
  906. .main {
  907. width: calc(100% - ($waiPadding * 2));
  908. height: 78vh;
  909. padding-top: 10rem;
  910. margin: 0 auto;
  911. display: flex;
  912. overflow: hidden;
  913. flex-direction: column;
  914. }
  915. .mask {
  916. position: fixed;
  917. height: 100vh;
  918. width: 100vw;
  919. top: 0;
  920. left: 0;
  921. background: rgba(0, 0, 0, 0.3);
  922. z-index: 998;
  923. }
  924. .mask2 {
  925. background: rgba(0, 0, 0, 0.68);
  926. }
  927. .confirmDiaBg {
  928. width: 100%;
  929. height: 100vh;
  930. position: fixed;
  931. left: 0;
  932. top: 0;
  933. display: flex;
  934. align-items: center;
  935. justify-content: center;
  936. z-index: 999;
  937. .confirmDiaWindow {
  938. width: 40%;
  939. height: 43.4%;
  940. border-radius: 1.6rem;
  941. opacity: 1;
  942. box-sizing: border-box;
  943. border: 0.55rem solid #13ED84;
  944. text-align: center;
  945. display: flex;
  946. align-items: center;
  947. justify-content: center;
  948. position: fixed;
  949. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  950. .readyBox {
  951. text-align: center;
  952. color: #1A293A;
  953. .lable {
  954. font-size: 4.3rem;
  955. line-height: 1;
  956. margin-bottom: 1.5rem;
  957. }
  958. .value {
  959. font-size: 10rem;
  960. line-height: 1;
  961. font-family: 'Saira-BlackItalic';
  962. }
  963. .transparent {
  964. opacity: 0;
  965. }
  966. }
  967. }
  968. }
  969. .footerBtn {
  970. width: 100%;
  971. padding: 0 calc(13.02rem /2);
  972. box-sizing: border-box;
  973. position: fixed;
  974. bottom: 3vh;
  975. display: flex;
  976. justify-content: end;
  977. .btn {
  978. width: 14.6vw;
  979. height: 6.1vh;
  980. line-height: 6.1vh;
  981. font-size: 3vh;
  982. color: #1A293A;
  983. text-align: center;
  984. border-radius: 1vh;
  985. cursor: pointer;
  986. margin-left: 10px;
  987. background: radial-gradient(159% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
  988. box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, 0.1874), inset 0px 1px 0px 1px rgba(255, 255, 255, 0.3);
  989. &:hover {
  990. background: #8EFFA9;
  991. }
  992. }
  993. .startBtn {
  994. color: #ffffff;
  995. background: radial-gradient(159% 126% at 5% 93%, #F99F02 0%, #ED7905 100%);
  996. box-shadow: 3px 6px 4px 1px rgba(0, 0, 0, 0.1874), inset 0px 1px 0px 2px rgba(255, 255, 255, 0.3);
  997. &:hover {
  998. background: #F99F02;
  999. }
  1000. }
  1001. .testRecordBtn {
  1002. position: relative;
  1003. z-index: 999;
  1004. }
  1005. }
  1006. .main-right {
  1007. width: 22.7%;
  1008. height: 80vh;
  1009. border-radius: 1.6rem;
  1010. // background: linear-gradient(29deg, #092941 -82%, #2A484B 94%);
  1011. background-color: rgba(9, 41, 65, 0.8);
  1012. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.4);
  1013. display: flex;
  1014. flex-direction: column;
  1015. overflow: hidden;
  1016. position: absolute;
  1017. z-index: 999;
  1018. bottom: 6.5vh;
  1019. right: calc(13.02rem /2);
  1020. }
  1021. //列表样式写在此父组件里,子组件尽量减少个性化class和style,多样性以父组件控制类型
  1022. ::v-deep(.testBox1) {
  1023. $listMargin: 10vw;
  1024. $listWidth: calc((100vw - ($waiPadding * 2) - $listMargin) / 5);
  1025. display: flex;
  1026. flex-wrap: wrap;
  1027. align-items: center;
  1028. .ul {
  1029. width: 100%;
  1030. height: 36vh;
  1031. display: flex;
  1032. .li {
  1033. width: $listWidth;
  1034. position: relative;
  1035. .ready {
  1036. position: absolute;
  1037. height: 100%;
  1038. width: 100%;
  1039. display: flex;
  1040. align-items: center;
  1041. justify-items: center;
  1042. img {
  1043. width: 100%;
  1044. }
  1045. }
  1046. .userInfo {
  1047. width: 100%;
  1048. height: 100%;
  1049. border-radius: 1.6rem;
  1050. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  1051. text-align: center;
  1052. display: flex;
  1053. align-items: center;
  1054. justify-content: center;
  1055. cursor: pointer;
  1056. margin-bottom: 3px;
  1057. .userInfo-center {
  1058. .pic {
  1059. width: calc(36vh * 0.62);
  1060. height: calc(36vh * 0.62);
  1061. border-radius: 50%;
  1062. display: flex;
  1063. justify-content: center;
  1064. align-items: center;
  1065. overflow: hidden;
  1066. margin: 0 auto 2vh auto;
  1067. position: relative;
  1068. .area {
  1069. position: absolute;
  1070. top: calc(36vh * 0.18);
  1071. color: #203C52;
  1072. font-size: 5vh;
  1073. line-height: 1;
  1074. font-family: 'Saira-ExtraBold';
  1075. text-align: center;
  1076. }
  1077. img {
  1078. width: 100%;
  1079. }
  1080. }
  1081. .pic2 {
  1082. box-sizing: border-box;
  1083. border: 0.44rem solid rgba(26, 41, 58, 0.6315);
  1084. }
  1085. .name {
  1086. width: 100%;
  1087. color: #1A293A;
  1088. font-size: 2.21rem;
  1089. }
  1090. .name2 {
  1091. box-sizing: border-box;
  1092. padding: 0.1rem 0.4rem;
  1093. border-radius: 1.1rem;
  1094. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  1095. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
  1096. }
  1097. .ctrlBox {
  1098. position: absolute;
  1099. left: 0;
  1100. top: 0;
  1101. border: 5px solid #ffffff;
  1102. display: flex;
  1103. flex-direction: column;
  1104. align-items: center;
  1105. background: rgba(0, 0, 0, 0.8);
  1106. z-index: 2;
  1107. width: 100%;
  1108. height: 100%;
  1109. box-sizing: border-box;
  1110. border-radius: 1.6rem;
  1111. img {
  1112. max-width: 100%;
  1113. max-height: 70%;
  1114. margin: 10% 0 5% 0;
  1115. }
  1116. .lable {
  1117. text-align: center;
  1118. font-size: 2rem;
  1119. color: #ffffff
  1120. }
  1121. }
  1122. }
  1123. }
  1124. .score {
  1125. height: 18vh;
  1126. line-height: 18vh;
  1127. font-size: 12vh;
  1128. font-family: 'Saira-BlackItalic';
  1129. border: 0.55rem solid #13ED84;
  1130. text-align: center;
  1131. border-radius: 1.6rem;
  1132. box-sizing: content-box;
  1133. color: #1A293A;
  1134. position: relative;
  1135. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  1136. display: none;
  1137. &::before {
  1138. content: "";
  1139. width: 2vw;
  1140. height: 2vw;
  1141. display: block;
  1142. position: absolute;
  1143. top: -1.5vw;
  1144. left: 50%;
  1145. margin-left: -1vw;
  1146. background-image: url("@/assets/images/test/yuan.png");
  1147. background-position: center;
  1148. background-repeat: no-repeat;
  1149. background-size: 100% 100%;
  1150. border-radius: 50%;
  1151. flex-shrink: 0;
  1152. transition: all 0.5s;
  1153. }
  1154. }
  1155. }
  1156. .disable {
  1157. .userInfo,
  1158. .score {
  1159. opacity: 0.5;
  1160. }
  1161. }
  1162. .wait {
  1163. .userInfo,
  1164. .score {
  1165. opacity: 0.5;
  1166. }
  1167. }
  1168. &:nth-child(1) {
  1169. justify-content: space-between;
  1170. }
  1171. &:nth-child(2) {
  1172. justify-content: space-evenly;
  1173. .li {
  1174. margin-left: calc($listMargin/4);
  1175. margin-right: calc($listMargin/4);
  1176. }
  1177. }
  1178. }
  1179. .ready.ul {
  1180. &:nth-child(1) {
  1181. .li:nth-child(2) {
  1182. &::after {
  1183. content: "";
  1184. position: absolute;
  1185. height: 100%;
  1186. width: 100%;
  1187. display: block;
  1188. background-image: url("@/assets/images/test/ready3.png");
  1189. background-position: center center;
  1190. background-repeat: no-repeat;
  1191. background-size: 100%;
  1192. }
  1193. }
  1194. }
  1195. &:nth-child(2) {
  1196. .li {
  1197. &::after {
  1198. content: "";
  1199. position: absolute;
  1200. height: 100%;
  1201. width: 100%;
  1202. display: block;
  1203. background-image: url("@/assets/images/test/ready3.png");
  1204. background-position: center center;
  1205. background-repeat: no-repeat;
  1206. background-size: 100%;
  1207. top: -100%;
  1208. }
  1209. }
  1210. }
  1211. }
  1212. .hands.ul {
  1213. &:nth-child(1) {
  1214. .li:nth-child(2) {
  1215. &::after {
  1216. background-image: url("@/assets/images/test/ready4.png");
  1217. }
  1218. }
  1219. }
  1220. &:nth-child(2) {
  1221. .li {
  1222. &::after {
  1223. background-image: url("@/assets/images/test/ready4.png");
  1224. }
  1225. }
  1226. }
  1227. }
  1228. .overlap.ul {
  1229. transition: all 0.5s;
  1230. .li {
  1231. .score {
  1232. display: block;
  1233. }
  1234. }
  1235. &:nth-child(1) {
  1236. margin-top: 5vh;
  1237. .li {
  1238. &::before {
  1239. display: none
  1240. }
  1241. &::after {
  1242. display: none
  1243. }
  1244. }
  1245. }
  1246. &:nth-child(2) {
  1247. margin-top: -20vh;
  1248. .li {
  1249. &::before {
  1250. display: none
  1251. }
  1252. &::after {
  1253. display: none
  1254. }
  1255. }
  1256. }
  1257. }
  1258. }
  1259. ::v-deep(.testBox2) {
  1260. $listMargin: 10vw;
  1261. $listWidth: calc((100% - $listMargin) / 5);
  1262. .ul {
  1263. width: 100%;
  1264. height: 34vh;
  1265. display: flex;
  1266. justify-content: space-between;
  1267. margin-bottom: 4vh;
  1268. .li {
  1269. width: $listWidth;
  1270. position: relative;
  1271. height: 100%;
  1272. border-radius: 3vh;
  1273. padding: 0.6vw;
  1274. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  1275. display: flex;
  1276. flex-direction: column;
  1277. justify-content: space-between;
  1278. box-sizing: border-box;
  1279. .userInfo {
  1280. width: 100%;
  1281. height: 100%;
  1282. border-radius: 1.6rem;
  1283. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  1284. text-align: center;
  1285. display: flex;
  1286. align-items: center;
  1287. justify-content: center;
  1288. cursor: pointer;
  1289. position: relative;
  1290. .userInfo-center {
  1291. .pic {
  1292. width: calc(36vh * 0.62);
  1293. height: calc(36vh * 0.62);
  1294. border-radius: 50%;
  1295. display: flex;
  1296. justify-content: center;
  1297. align-items: center;
  1298. overflow: hidden;
  1299. margin: 0 auto 2vh auto;
  1300. position: relative;
  1301. .area {
  1302. position: absolute;
  1303. top: calc(36vh * 0.18);
  1304. color: #203C52;
  1305. font-size: 5vh;
  1306. line-height: 1;
  1307. font-family: 'Saira-ExtraBold';
  1308. text-align: center;
  1309. }
  1310. img {
  1311. width: 100%;
  1312. }
  1313. }
  1314. .pic2 {
  1315. box-sizing: border-box;
  1316. border: 0.44rem solid rgba(26, 41, 58, 0.6315);
  1317. }
  1318. .name {
  1319. width: 100%;
  1320. color: #1A293A;
  1321. font-size: 2.21rem;
  1322. }
  1323. .ctrlBox {
  1324. position: absolute;
  1325. left: 0;
  1326. top: 0;
  1327. border: 5px solid #ffffff;
  1328. display: flex;
  1329. flex-direction: column;
  1330. align-items: center;
  1331. background: rgba(0, 0, 0, 0.8);
  1332. z-index: 2;
  1333. width: 100%;
  1334. height: 100%;
  1335. box-sizing: border-box;
  1336. border-radius: 1.6rem;
  1337. img {
  1338. max-width: 100%;
  1339. max-height: 70%;
  1340. margin: 10% 0 5% 0;
  1341. }
  1342. .lable {
  1343. text-align: center;
  1344. font-size: 2rem;
  1345. color: #ffffff
  1346. }
  1347. }
  1348. }
  1349. }
  1350. .score {
  1351. height: 14vh;
  1352. line-height: 14vh;
  1353. font-size: 9vh;
  1354. font-family: 'Saira-BlackItalic';
  1355. background: #ffffff;
  1356. box-sizing: border-box;
  1357. border: 0.55rem solid #13ED84;
  1358. text-align: center;
  1359. border-radius: 1.6rem;
  1360. box-sizing: content-box;
  1361. color: #1A293A;
  1362. position: relative;
  1363. display: none;
  1364. &::before {
  1365. content: "";
  1366. width: 2vw;
  1367. height: 2vw;
  1368. display: block;
  1369. position: absolute;
  1370. top: -1.3vw;
  1371. left: 50%;
  1372. margin-left: -1vw;
  1373. background-image: url("@/assets/images/test/yuan.png");
  1374. background-position: center;
  1375. background-repeat: no-repeat;
  1376. background-size: 100% 100%;
  1377. border-radius: 50%;
  1378. flex-shrink: 0;
  1379. transition: all 0.5s;
  1380. }
  1381. }
  1382. }
  1383. .disable {
  1384. opacity: 0.5;
  1385. }
  1386. .wait {
  1387. opacity: 0.5;
  1388. }
  1389. }
  1390. .overlap {
  1391. .li {
  1392. .userInfo {
  1393. height: 6.2vw;
  1394. .userInfo-center {
  1395. width: 100%;
  1396. text-align: center;
  1397. display: flex;
  1398. align-items: center;
  1399. cursor: pointer;
  1400. .pic {
  1401. width: 6.2vw;
  1402. height: 6.2vw;
  1403. border-radius: 50%;
  1404. display: flex;
  1405. justify-content: center;
  1406. align-items: center;
  1407. overflow: hidden;
  1408. position: relative;
  1409. flex-shrink: 0;
  1410. margin: 0;
  1411. .area {
  1412. position: absolute;
  1413. top: 0;
  1414. left: 0;
  1415. width: 100%;
  1416. height: 100%;
  1417. line-height: 6.2vw;
  1418. color: #ffffff;
  1419. font-size: 2.5vw;
  1420. font-family: 'Saira-ExtraBold';
  1421. text-align: center;
  1422. background: rgba(#000000, 0.4)
  1423. }
  1424. img {
  1425. width: 100%;
  1426. }
  1427. }
  1428. .pic2 {
  1429. box-sizing: border-box;
  1430. border: 2px solid rgba(26, 41, 58, 0.6315);
  1431. }
  1432. .name {
  1433. width: 100%;
  1434. color: #1A293A;
  1435. font-size: 1.8rem;
  1436. border-radius: 2vw;
  1437. padding: 0.5rem 0;
  1438. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  1439. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
  1440. }
  1441. }
  1442. }
  1443. .score {
  1444. display: block;
  1445. }
  1446. }
  1447. }
  1448. }
  1449. ::v-deep(.testBox3) {
  1450. $listMargin: 5vw;
  1451. $listWidth: calc((100% - $listMargin) / 10);
  1452. .ul {
  1453. width: 100%;
  1454. height: 14vh;
  1455. display: flex;
  1456. justify-content: space-around;
  1457. margin-bottom: calc($listMargin / 10);
  1458. .li {
  1459. width: $listWidth;
  1460. position: relative;
  1461. height: 100%;
  1462. border-radius: 5vh;
  1463. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  1464. .userInfo {
  1465. padding: 2vh 0.6vw 0 0.6vw;
  1466. margin-bottom: 0.8vh;
  1467. .userInfo-center {
  1468. text-align: center;
  1469. display: flex;
  1470. align-items: center;
  1471. cursor: pointer;
  1472. border-radius: 1.3vw;
  1473. height: 2.6vw;
  1474. background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.6571) 100%);
  1475. border: 1px solid #FFFFFF;
  1476. .pic {
  1477. width: 2.6vw;
  1478. height: 2.6vw;
  1479. border-radius: 50%;
  1480. display: flex;
  1481. justify-content: center;
  1482. align-items: center;
  1483. overflow: hidden;
  1484. position: relative;
  1485. flex-shrink: 0;
  1486. .area {
  1487. position: absolute;
  1488. top: 0;
  1489. left: 0;
  1490. width: 100%;
  1491. height: 100%;
  1492. line-height: 2.6vw;
  1493. color: #ffffff;
  1494. font-size: 1.4vw;
  1495. font-family: 'Saira-ExtraBold';
  1496. text-align: center;
  1497. background: rgba(#000000, 0.4)
  1498. }
  1499. img {
  1500. width: 100%;
  1501. }
  1502. }
  1503. .pic2 {
  1504. box-sizing: border-box;
  1505. border: 2px solid rgba(26, 41, 58, 0.6315);
  1506. }
  1507. .name {
  1508. color: #1A293A;
  1509. font-size: 1rem;
  1510. margin-left: 0.5vw;
  1511. }
  1512. .ctrlBox {
  1513. position: absolute;
  1514. left: 0;
  1515. top: 0;
  1516. border: 5px solid #ffffff;
  1517. display: flex;
  1518. flex-direction: column;
  1519. align-items: center;
  1520. background: rgba(0, 0, 0, 0.8);
  1521. z-index: 2;
  1522. width: 100%;
  1523. height: 100%;
  1524. box-sizing: border-box;
  1525. border-radius: 5vh;
  1526. img {
  1527. max-width: 100%;
  1528. max-height: 60%;
  1529. margin: 5% 0 5% 0;
  1530. }
  1531. .lable {
  1532. text-align: center;
  1533. font-size: 1rem;
  1534. color: #ffffff
  1535. }
  1536. }
  1537. }
  1538. }
  1539. .score {
  1540. font-size: 3rem;
  1541. font-family: 'Saira-BlackItalic';
  1542. text-align: center;
  1543. color: #1A293A;
  1544. line-height: 1;
  1545. }
  1546. }
  1547. .disable {
  1548. opacity: 0.5;
  1549. }
  1550. .wait {
  1551. opacity: 0.75;
  1552. }
  1553. .startedAlready {
  1554. background: #15ef96;
  1555. }
  1556. }
  1557. }
  1558. ::v-deep(.testBox4) {
  1559. $listMargin: 10vw;
  1560. $listWidth: calc((100vw - ($waiPadding * 2) - $listMargin) / 5);
  1561. display: flex;
  1562. flex-wrap: wrap;
  1563. align-items: center;
  1564. height: 100%;
  1565. .ul {
  1566. width: 100%;
  1567. display: flex;
  1568. justify-content: space-evenly;
  1569. align-items: center;
  1570. height: 36vh;
  1571. .li {
  1572. width: $listWidth;
  1573. height: 100%;
  1574. position: relative;
  1575. .ready {
  1576. position: absolute;
  1577. height: 100%;
  1578. width: 100%;
  1579. display: flex;
  1580. align-items: center;
  1581. justify-items: center;
  1582. img {
  1583. width: 100%;
  1584. }
  1585. }
  1586. .userInfo {
  1587. width: 100%;
  1588. height: 100%;
  1589. border-radius: 1.6rem;
  1590. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  1591. text-align: center;
  1592. display: flex;
  1593. align-items: center;
  1594. justify-content: center;
  1595. cursor: pointer;
  1596. margin-bottom: 3px;
  1597. .userInfo-center {
  1598. .pic {
  1599. width: calc(36vh * 0.62);
  1600. height: calc(36vh * 0.62);
  1601. border-radius: 50%;
  1602. display: flex;
  1603. justify-content: center;
  1604. align-items: center;
  1605. overflow: hidden;
  1606. margin: 0 auto 2vh auto;
  1607. position: relative;
  1608. .area {
  1609. position: absolute;
  1610. top: calc(36vh * 0.18);
  1611. color: #203C52;
  1612. font-size: 5vh;
  1613. line-height: 1;
  1614. font-family: 'Saira-ExtraBold';
  1615. text-align: center;
  1616. }
  1617. img {
  1618. width: 100%;
  1619. }
  1620. }
  1621. .pic2 {
  1622. box-sizing: border-box;
  1623. border: 0.44rem solid rgba(26, 41, 58, 0.6315);
  1624. }
  1625. .name {
  1626. width: 100%;
  1627. color: #1A293A;
  1628. font-size: 2.21rem;
  1629. }
  1630. .name2 {
  1631. box-sizing: border-box;
  1632. padding: 0.1rem 0.4rem;
  1633. border-radius: 1.1rem;
  1634. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  1635. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
  1636. }
  1637. .ctrlBox {
  1638. position: absolute;
  1639. left: 0;
  1640. top: 0;
  1641. border: 5px solid #ffffff;
  1642. display: flex;
  1643. flex-direction: column;
  1644. align-items: center;
  1645. background: rgba(0, 0, 0, 0.8);
  1646. z-index: 2;
  1647. width: 100%;
  1648. height: 100%;
  1649. box-sizing: border-box;
  1650. border-radius: 1.6rem;
  1651. img {
  1652. max-width: 100%;
  1653. max-height: 70%;
  1654. margin: 10% 0 5% 0;
  1655. }
  1656. .lable {
  1657. text-align: center;
  1658. font-size: 2rem;
  1659. color: #ffffff
  1660. }
  1661. }
  1662. }
  1663. }
  1664. .score {
  1665. height: 18vh;
  1666. line-height: 18vh;
  1667. font-size: 12vh;
  1668. font-family: 'Saira-BlackItalic';
  1669. border: 0.55rem solid #13ED84;
  1670. text-align: center;
  1671. border-radius: 1.6rem;
  1672. box-sizing: content-box;
  1673. color: #1A293A;
  1674. position: relative;
  1675. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  1676. display: none;
  1677. &::before {
  1678. content: "";
  1679. width: 2vw;
  1680. height: 2vw;
  1681. display: block;
  1682. position: absolute;
  1683. top: -1.5vw;
  1684. left: 50%;
  1685. margin-left: -1vw;
  1686. background-image: url("@/assets/images/test/yuan.png");
  1687. background-position: center;
  1688. background-repeat: no-repeat;
  1689. background-size: 100% 100%;
  1690. border-radius: 50%;
  1691. flex-shrink: 0;
  1692. transition: all 0.5s;
  1693. }
  1694. }
  1695. }
  1696. .disable {
  1697. .userInfo,
  1698. .score {
  1699. opacity: 0.5;
  1700. }
  1701. }
  1702. .wait {
  1703. .userInfo,
  1704. .score {
  1705. opacity: 0.5;
  1706. }
  1707. }
  1708. }
  1709. .overlap.ul {
  1710. transition: all 0.5s;
  1711. margin-top: -15vh;
  1712. .li {
  1713. .score {
  1714. display: block;
  1715. }
  1716. }
  1717. }
  1718. }
  1719. </style>