multiple.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119
  1. <template>
  2. <div>
  3. <Header @confirmExit="confirmExit"></Header>
  4. <div class="main">
  5. <transition :enter-active-class="proxy?.animate.dialog.enter" :leave-active-class="proxy?.animate.dialog.leave">
  6. <div class="time" v-show="[42].includes(examState)">{{
  7. time.countdownNum
  8. }}</div>
  9. </transition>
  10. <div class="testBox"
  11. :class="{ 'testBox1': styleType == 1, 'testBox2': styleType == 2, 'testBox3': styleType == 3 }">
  12. <div class="ul" :class="{ 'overlap': ![0, 3, 40, 41].includes(examState), 'ready': [41].includes(examState) }"
  13. v-for="(items, indexs) in testListArr " :key="indexs">
  14. <MultipleItem :ref="(el: any) => { multipleItemRef(el, item.itemNumber, item.area) }"
  15. v-for="(item, index) in items" :query="parameter" :area="item.area" :key="index"
  16. :itemNumber="item.itemNumber" @returnData="returnData" :examState="examState" :needStart="needStart"
  17. :styleType="styleType" />
  18. </div>
  19. </div>
  20. </div>
  21. <div class="footerBtn">
  22. <template v-if="needStart">
  23. <div class="btn" @click="getAgain" v-if="examState == 42 || showTestAgain">再测一次</div>
  24. <div class="btn" @click="getOpenOneTestAndStartFace" v-if="examState < 41">开始识别</div>
  25. <div class="btn" @click="getStopFace" v-if="examState == 41">停止人脸识别</div>
  26. <div class="btn startBtn" @click="getStartOneTest" v-if="examState == 43">开始测试</div>
  27. </template>
  28. <template v-else>
  29. <div class="btn startBtn" @click="getReady" v-if="examState == 43">开始测试</div>
  30. </template>
  31. </div>
  32. <!--倒计时开始-->
  33. <div>
  34. <transition :enter-active-class="proxy?.animate.mask.enter" :leave-active-class="proxy?.animate.mask.leave">
  35. <div class="mask" v-show="examState == 43 && time.ready"></div>
  36. </transition>
  37. <transition :enter-active-class="proxy?.animate.face.enter" :leave-active-class="proxy?.animate.face.leave2">
  38. <div class="confirmDiaBg" v-show="examState == 43 && time.ready">
  39. <div class="confirmDiaWindow">
  40. <div class="confirmDiaWindow-con">
  41. <div class="readyBox">
  42. <div class="value">{{ time.ready }}</div>
  43. <div class="lable">倒计时</div>
  44. </div>
  45. </div>
  46. </div>
  47. </div>
  48. </transition>
  49. </div>
  50. <!--倒计时结束-->
  51. </div>
  52. </template>
  53. <script setup name="Multiple" lang="ts">
  54. import { initSpeech, speckText, speckCancel, chineseNumber } from '@/utils/speech'
  55. import { initWs, examEnds, openOneTest, startFace, stopFace, faceConfirmOnly, startOneTest, finishOneTest, closeOneTest, suspendFaceRecognitionChannels, resumeFaceRecognitionChannels } from '@/utils/ws'
  56. const { proxy } = getCurrentInstance() as any;
  57. const router = useRouter();
  58. const route = useRoute();
  59. const data = reactive<any>({
  60. timerManager: {},//计时器管理
  61. parameter: {},//参数
  62. time: {
  63. testTime: 60,//时长
  64. countdownNum: 0,//计时
  65. ready: 0,//预备
  66. },
  67. userInfo: {},//用户信息
  68. examState: 0,//当前状态
  69. needStart: false,//是否需要按钮
  70. showTestAgain: false,//再测一次按钮
  71. testList: [],//获取区列表
  72. multipleItemRefList: [],//获取区列表
  73. styleType: null,//展示样式1:1-5个,2:6-10个,3:10个以上
  74. });
  75. const { timerManager, parameter, time, userInfo, examState, needStart, showTestAgain, testList, multipleItemRefList, styleType } = toRefs(data);
  76. /**
  77. * 创建组件实例
  78. */
  79. const multipleItemRef = (el: any, index: number, area: any) => {
  80. multipleItemRefList.value[index - 1] = el;
  81. }
  82. /**
  83. * 开始识别
  84. */
  85. const getOpenOneTestAndStartFace = () => {
  86. cleanData();
  87. examState.value = 41;
  88. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  89. multipleItemRefList.value[i].getOpenOneTestAndStartFace()
  90. }
  91. };
  92. /**
  93. * 停止人脸识别
  94. */
  95. const getStopFace = async () => {
  96. // let flag = false;
  97. // for (let i = 0; i < testList.value.length; i++) {
  98. // if (testList.value[i] && testList.value[i].faceCheckStu?.student_id) {
  99. // flag = true;
  100. // }
  101. // }
  102. // if (!flag) {
  103. // proxy?.$modal.msgWarning("请选择人员!");
  104. // return false;
  105. // }
  106. examState.value = 43;
  107. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  108. multipleItemRefList.value[i].getStopFace()
  109. }
  110. };
  111. /**
  112. * 开始测试
  113. */
  114. const getStartOneTest = () => {
  115. if (examState.value != 43) {
  116. return false;
  117. }
  118. let flag = false;
  119. for (let i = 0; i < testList.value.length; i++) {
  120. if (testList.value[i] && testList.value[i].faceCheckStu?.student_id) {
  121. flag = true;
  122. }
  123. }
  124. if (!flag) {
  125. proxy?.$modal.msgWarning("请选择人员!");
  126. return false;
  127. }
  128. getClearTimer("readyTimer");
  129. time.value.ready = 0;
  130. examState.value = 42;
  131. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  132. if (testList.value[i].examState == 41) {
  133. //正在识别的停止识别
  134. multipleItemRefList.value[i].getStopFace()
  135. }
  136. if (testList.value[i].examState == 43) {
  137. multipleItemRefList.value[i].getStartOneTest()
  138. }
  139. }
  140. speckText("哨声");
  141. //显示再测一次按钮
  142. showTestAgain.value = true;
  143. //时间为0的为正计时,大于0的为倒计时
  144. if (time.value.testTime == 0) {
  145. getCounting("+");
  146. } else {
  147. getCounting("-");
  148. }
  149. };
  150. /**
  151. * 再测一次
  152. */
  153. const getAgain = async () => {
  154. cleanData();
  155. examState.value = 43;
  156. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  157. multipleItemRefList.value[i].getAgain()
  158. }
  159. };
  160. /**
  161. * 确认退出
  162. */
  163. const confirmExit = () => {
  164. proxy?.$modal.confirm("确定退出吗?").then(() => {
  165. getExit();
  166. }).finally(() => {
  167. });
  168. };
  169. /**
  170. * 退出
  171. */
  172. const getExit = () => {
  173. getClearTimer();//清除计时器
  174. examEnds();//通知工作站关闭
  175. speckCancel()//停止播报;
  176. router.push({ path: '/' });//跳转
  177. };
  178. /**
  179. * 清空定时任务
  180. */
  181. const getClearTimer = (data?: any) => {
  182. if (data) {
  183. //清除指定
  184. clearInterval(timerManager.value[data])
  185. timerManager.value[data] = null;
  186. } else {
  187. //清除全部
  188. for (let key in timerManager.value) {
  189. if (timerManager.value.hasOwnProperty(key)) {
  190. clearInterval(timerManager.value[key])
  191. timerManager.value[key] = null;
  192. }
  193. }
  194. }
  195. };
  196. /**
  197. * 清除历史记录
  198. */
  199. const cleanData = () => {
  200. getClearTimer("countdownTimer");
  201. time.value.countdownNum = time.value.testTime;
  202. showTestAgain.value = false;
  203. };
  204. /**
  205. * 时间转换
  206. */
  207. // const countdownNumFormat = computed(() => {
  208. // return time.value.countdownNum;
  209. // //return proxy?.$utils.timeFormat(time.value.countdownNum);
  210. // });
  211. /**
  212. * 倒计时
  213. */
  214. const getCounting = (type: string) => {
  215. timerManager.value.countdownTimer = setInterval(() => {
  216. //正计时
  217. if (type == "+") {
  218. time.value.countdownNum++;
  219. }
  220. //倒计时
  221. if (type == "-") {
  222. if (time.value.countdownNum <= 0) {
  223. getClearTimer("countdownTimer");
  224. } else {
  225. time.value.countdownNum--;
  226. }
  227. }
  228. }, 1000);
  229. };
  230. /**
  231. * 子组件返回数据
  232. */
  233. const returnData = (data: any) => {
  234. let index = testList.value.findIndex((item: any) => {
  235. return item.area == data.area;
  236. });
  237. let obj = Object.assign(testList.value[index], data);
  238. testList.value[index] = JSON.parse(JSON.stringify(obj));
  239. console.log("testList.value", testList.value)
  240. if (examState.value == 0) {
  241. let flag = false;
  242. //只监听人脸识别的区
  243. let newList = testList.value;
  244. for (let i = 0; i < newList.length; i++) {
  245. if (newList[i] && newList[i].examState == 3) {
  246. flag = true;
  247. }
  248. }
  249. if (flag) {
  250. console.log("变更状态:", 3)
  251. examState.value = 3;
  252. }
  253. }
  254. if (examState.value == 3) {
  255. let flag = false;
  256. //只监听人脸识别的区
  257. let newList = testList.value;
  258. for (let i = 0; i < newList.length; i++) {
  259. if (newList[i] && newList[i].examState == 40) {
  260. flag = true;
  261. }
  262. }
  263. if (flag) {
  264. console.log("变更状态:", 40)
  265. examState.value = 40;
  266. }
  267. }
  268. if (examState.value == 40) {
  269. let flag = false;
  270. //只监听人脸识别的区
  271. let newList = testList.value;
  272. for (let i = 0; i < newList.length; i++) {
  273. if (newList[i] && newList[i].examState == 41) {
  274. flag = true;
  275. }
  276. }
  277. if (flag) {
  278. console.log("变更状态:", 41)
  279. examState.value = 41;
  280. let txt = parameter.value.gesture ? "请举手看摄像头人脸识别" : "请看摄像头进行人脸识别";
  281. speckText(txt);
  282. }
  283. }
  284. if (examState.value == 41) {
  285. let flag = false;
  286. //只监听人脸识别的区
  287. let newList = testList.value.filter((item: any) => {
  288. return item?.faceCheckStu?.student_id;
  289. })
  290. for (let i = 0; i < newList.length; i++) {
  291. if (newList[i] && newList[i].faceCheckStu?.student_id && newList[i].examState == 43) {
  292. flag = true;
  293. }
  294. }
  295. if (flag) {
  296. console.log("变更状态:", 43)
  297. examState.value = 43;
  298. cleanData();
  299. }
  300. }
  301. //测试完成后回退状态
  302. if (examState.value == 42) {
  303. let flag = false;
  304. //只监听人脸识别的区
  305. let newList = testList.value.filter((item: any) => {
  306. return item?.faceCheckStu?.student_id;
  307. })
  308. for (let i = 0; i < newList.length; i++) {
  309. if (newList[i] && newList[i].faceCheckStu?.student_id && newList[i].examState == 3) {
  310. flag = true;
  311. } else {
  312. return false;
  313. }
  314. }
  315. if (flag) {
  316. console.log("变更状态:", 3)
  317. examState.value = 3;
  318. }
  319. }
  320. //如果全部状态为0就退出
  321. if (examState.value >= 0) {
  322. let flag = false;
  323. //只监听人脸识别的区
  324. let newList = testList.value;
  325. for (let i = 0; i < newList.length; i++) {
  326. if (newList[i] && newList[i].examState == 0) {
  327. flag = true;
  328. } else {
  329. return false;
  330. }
  331. }
  332. if (flag) {
  333. examState.value = 0;
  334. getExit();
  335. }
  336. }
  337. };
  338. /**
  339. * 准备开始
  340. */
  341. const getReady = () => {
  342. //停止播报;
  343. speckCancel()
  344. //正在识别的停止识别
  345. for (let i = 0; i < multipleItemRefList.value.length; i++) {
  346. if (testList.value[i].examState == 41) {
  347. multipleItemRefList.value[i].getStopFace()
  348. }
  349. }
  350. time.value.ready = 5;
  351. speckText(time.value.ready);
  352. timerManager.value.readyTimer = setInterval(() => {
  353. time.value.ready--;
  354. if (time.value.ready <= 0) {
  355. getClearTimer("readyTimer");
  356. getStartOneTest();
  357. } else {
  358. speckText(time.value.ready);
  359. }
  360. }, 1000);
  361. };
  362. /**
  363. * 将测试列表转为多行
  364. */
  365. const testListArr = computed(() => {
  366. let list: any = [];
  367. let num = 0;
  368. if (styleType.value == 1) {
  369. num = 3
  370. }
  371. if (styleType.value == 2) {
  372. num = 5
  373. }
  374. if (styleType.value == 3) {
  375. num = 10
  376. }
  377. let myLength = Math.ceil(testList.value.length / num);
  378. for (let i = 0; i < myLength; i++) {
  379. list[i] = [];
  380. for (let j = 0; j < testList.value.length; j++) {
  381. if (j >= i * num && j < (i + 1) * num) {
  382. list[i].push(testList.value[j])
  383. }
  384. }
  385. }
  386. return list;
  387. });
  388. onBeforeMount(() => {
  389. parameter.value = route.query;
  390. let project = parameter.value.project;
  391. let area = parameter.value.area;
  392. parameter.value.examId = `${project}_${area}`; //项目+区
  393. if (parameter.value.time) {
  394. time.value.testTime = parameter.value.time
  395. }
  396. time.value.countdownNum = time.value.testTime;
  397. let myInfo: any = localStorage.getItem("userInfo");
  398. userInfo.value = JSON.parse(myInfo);
  399. if (parameter.value.gesture == 'true') {
  400. parameter.value.gesture = true
  401. } else {
  402. parameter.value.gesture = false
  403. }
  404. testList.value = parameter.value.area.split(',').map((item: any, index: number) => {
  405. let obj = {
  406. area: item,
  407. itemNumber: index + 1
  408. }
  409. return obj;
  410. });
  411. //需要开始按钮的项目
  412. if (testList.value.length > 10) {
  413. needStart.value = true;
  414. }
  415. //展示样式控制
  416. if (testList.value.length <= 5) {
  417. styleType.value = 1;
  418. }
  419. if (testList.value.length > 5 && testList.value.length <= 10) {
  420. styleType.value = 2;
  421. }
  422. if (testList.value.length > 10) {
  423. styleType.value = 3;
  424. }
  425. })
  426. onMounted(() => {
  427. //加载WS
  428. let project = parameter.value.project;
  429. initWs({ parameter: parameter.value, testTime: time.value.testTime, version: "v2" }, (data: any) => {
  430. let index = testList.value.findIndex((item: any) => {
  431. let examId = `${project}_${item.area}`;
  432. return examId == data.exam_id;
  433. })
  434. multipleItemRefList.value[index]?.getMessage(data);
  435. });
  436. initSpeech();
  437. setTimeout(() => {
  438. //10秒还在0状态就算超时
  439. if (examState.value == 0) {
  440. getExit();
  441. }
  442. }, 10000);
  443. })
  444. onUnmounted(() => {
  445. getExit();
  446. })
  447. </script>
  448. <style scoped lang="scss">
  449. $waiPadding: 6.51rem;
  450. .main {
  451. width: calc(100% - ($waiPadding * 2));
  452. height: 78vh;
  453. padding-top: 10rem;
  454. margin: 0 auto;
  455. display: flex;
  456. overflow: hidden;
  457. flex-direction: column;
  458. .time {
  459. width: 20vh;
  460. height: 20vh;
  461. line-height: 20vh;
  462. border-radius: 50%;
  463. color: #FF9402;
  464. font-size: 8vh;
  465. text-align: center;
  466. background-image: url("@/assets/images/test/time.png");
  467. background-position: center;
  468. background-repeat: no-repeat;
  469. background-size: 100% 100%;
  470. position: absolute;
  471. right: 50%;
  472. top: -4vh;
  473. margin-right: -10vh;
  474. font-family: 'Saira-BlackItalic';
  475. }
  476. }
  477. .mask {
  478. position: fixed;
  479. height: 100vh;
  480. width: 100vw;
  481. top: 0;
  482. left: 0;
  483. background: rgba(0, 0, 0, 0.3);
  484. z-index: 998;
  485. }
  486. .confirmDiaBg {
  487. width: 100%;
  488. height: 100vh;
  489. position: fixed;
  490. left: 0;
  491. top: 0;
  492. display: flex;
  493. align-items: center;
  494. justify-content: center;
  495. z-index: 999;
  496. .confirmDiaWindow {
  497. width: 23.5%;
  498. height: 43.4%;
  499. border-radius: 29px;
  500. opacity: 1;
  501. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  502. text-align: center;
  503. display: flex;
  504. align-items: center;
  505. justify-content: center;
  506. position: fixed;
  507. .confirmDiaWindow-con {
  508. .pic {
  509. width: 22.3vh;
  510. height: 22.3vh;
  511. border-radius: 50%;
  512. display: flex;
  513. justify-content: center;
  514. align-items: center;
  515. overflow: hidden;
  516. position: relative;
  517. margin: 0 auto 2vh auto;
  518. .shine {
  519. position: absolute;
  520. left: -5vh;
  521. top: 0;
  522. width: 5vh;
  523. height: 22.3vh;
  524. animation: shineani 3s infinite;
  525. -webkit-animation: shineani 3s infinite;
  526. z-index: 1;
  527. img {
  528. width: 100%;
  529. height: 100%;
  530. }
  531. }
  532. img {
  533. width: 100%;
  534. }
  535. }
  536. .pic2 {
  537. box-sizing: border-box;
  538. border: 0.44rem solid rgba(26, 41, 58, 0.6315);
  539. }
  540. .name {
  541. width: 100%;
  542. color: #1A293A;
  543. font-size: 2.21rem;
  544. }
  545. .name2 {
  546. padding: 0 0.3rem;
  547. border-radius: 1.1rem;
  548. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  549. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
  550. }
  551. }
  552. }
  553. }
  554. .footerBtn {
  555. width: 100%;
  556. padding: 0 calc(13.02rem /2);
  557. box-sizing: border-box;
  558. position: fixed;
  559. bottom: 3vh;
  560. display: flex;
  561. justify-content: end;
  562. .btn {
  563. width: 14.6vw;
  564. height: 6.1vh;
  565. line-height: 6.1vh;
  566. font-size: 3vh;
  567. color: #1A293A;
  568. text-align: center;
  569. border-radius: 1vh;
  570. cursor: pointer;
  571. background: radial-gradient(159% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
  572. box-shadow: 3px 6px 4px 1px rgba(0, 0, 0, 0.1874), inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  573. &:hover {
  574. background: #8EFFA9;
  575. }
  576. }
  577. .startBtn {
  578. color: #ffffff;
  579. background: radial-gradient(159% 126% at 5% 93%, #F99F02 0%, #ED7905 100%);
  580. box-shadow: 3px 6px 4px 1px rgba(0, 0, 0, 0.1874), inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  581. &:hover {
  582. background: #F99F02;
  583. }
  584. }
  585. }
  586. //列表样式写在此父组件里,子组件尽量减少个性化class和style,多样性以父组件控制类型
  587. ::v-deep(.testBox1) {
  588. $listMargin: 10vw;
  589. $listWidth: calc((100% - $listMargin) / 5);
  590. display: flex;
  591. flex-wrap: wrap;
  592. align-items: center;
  593. .ul {
  594. width: 100%;
  595. height: 35vh;
  596. display: flex;
  597. .li {
  598. width: $listWidth;
  599. position: relative;
  600. .ready {
  601. position: absolute;
  602. height: 100%;
  603. width: 100%;
  604. display: flex;
  605. align-items: center;
  606. justify-items: center;
  607. img {
  608. width: 100%;
  609. }
  610. }
  611. .userInfo {
  612. width: 100%;
  613. height: 100%;
  614. border-radius: 1.6rem;
  615. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  616. text-align: center;
  617. display: flex;
  618. align-items: center;
  619. justify-content: center;
  620. cursor: pointer;
  621. margin-bottom: 3px;
  622. .userInfo-center {
  623. .pic {
  624. width: calc($listWidth - 1vw);
  625. height: calc($listWidth - 1vw);
  626. border-radius: 50%;
  627. display: flex;
  628. justify-content: center;
  629. align-items: center;
  630. overflow: hidden;
  631. margin: 0 auto 2vh auto;
  632. position: relative;
  633. .area {
  634. position: absolute;
  635. top: 6vh;
  636. color: #203C52;
  637. font-size: 5.2vh;
  638. line-height: 1;
  639. font-family: 'Saira-ExtraBold';
  640. text-align: center;
  641. }
  642. img {
  643. width: 100%;
  644. }
  645. }
  646. .pic2 {
  647. box-sizing: border-box;
  648. border: 0.44rem solid rgba(26, 41, 58, 0.6315);
  649. }
  650. .name {
  651. width: 100%;
  652. color: #1A293A;
  653. font-size: 2.21rem;
  654. }
  655. .name2 {
  656. padding: 0 0.3rem;
  657. border-radius: 1.1rem;
  658. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  659. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
  660. }
  661. }
  662. }
  663. .score {
  664. height: 18vh;
  665. line-height: 18vh;
  666. font-size: 12vh;
  667. font-family: 'Saira-BlackItalic';
  668. background: #ffffff;
  669. box-sizing: border-box;
  670. border: 0.55rem solid #13ED84;
  671. text-align: center;
  672. border-radius: 1.6rem;
  673. box-sizing: content-box;
  674. color: #1A293A;
  675. position: relative;
  676. &::before {
  677. content: "";
  678. width: 2vw;
  679. height: 2vw;
  680. display: block;
  681. position: absolute;
  682. top: -1.5vw;
  683. left: 50%;
  684. margin-left: -1vw;
  685. background-image: url("@/assets/images/home/test_icon.png");
  686. background-position: center;
  687. background-repeat: no-repeat;
  688. background-size: 100% 100%;
  689. border-radius: 50%;
  690. flex-shrink: 0;
  691. transition: all 0.5s;
  692. }
  693. }
  694. }
  695. &:nth-child(1) {
  696. justify-content: space-between;
  697. }
  698. &:nth-child(2) {
  699. justify-content: space-evenly;
  700. .list {
  701. margin-left: calc($listMargin/4);
  702. margin-right: calc($listMargin/4);
  703. }
  704. }
  705. }
  706. .ready.ul {
  707. &:nth-child(1) {
  708. .li:nth-child(2) {
  709. &::after {
  710. content: "";
  711. position: absolute;
  712. height: 100%;
  713. width: 100%;
  714. display: block;
  715. background-image: url("@/assets/images/test/ready3.png");
  716. background-position: center center;
  717. background-repeat: no-repeat;
  718. background-size: 100%;
  719. }
  720. .score {
  721. display: none;
  722. }
  723. }
  724. }
  725. &:nth-child(2) {
  726. .list {
  727. &::after {
  728. content: "";
  729. position: absolute;
  730. height: 100%;
  731. width: 100%;
  732. display: block;
  733. background-image: url("@/assets/images/test/ready3.png");
  734. background-position: center center;
  735. background-repeat: no-repeat;
  736. background-size: 100%;
  737. top: -100%;
  738. }
  739. .score {
  740. display: none;
  741. }
  742. }
  743. }
  744. }
  745. .overlap.ul {
  746. transition: all 0.5s;
  747. &:nth-child(1) {
  748. margin-top: 5vh;
  749. .li {
  750. &::before {
  751. display: none
  752. }
  753. &::after {
  754. display: none
  755. }
  756. }
  757. }
  758. &:nth-child(2) {
  759. margin-top: -20vh;
  760. .li {
  761. &::before {
  762. display: none
  763. }
  764. &::after {
  765. display: none
  766. }
  767. }
  768. }
  769. }
  770. }
  771. ::v-deep(.testBox2) {
  772. $listMargin: 10vw;
  773. $listWidth: calc((100% - $listMargin) / 5);
  774. .ul {
  775. width: 100%;
  776. height: 34vh;
  777. display: flex;
  778. justify-content: space-between;
  779. margin-bottom: 4vh;
  780. .li {
  781. width: $listWidth;
  782. position: relative;
  783. height: 100%;
  784. border-radius: 3vh;
  785. padding: 0.6vw;
  786. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  787. display: flex;
  788. flex-direction: column;
  789. justify-content: space-between;
  790. box-sizing: border-box;
  791. .userInfo {
  792. width: 100%;
  793. height: 100%;
  794. border-radius: 1.6rem;
  795. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  796. text-align: center;
  797. display: flex;
  798. align-items: center;
  799. justify-content: center;
  800. cursor: pointer;
  801. .userInfo-center {
  802. .pic {
  803. width: 12vw;
  804. height: 12vw;
  805. border-radius: 50%;
  806. display: flex;
  807. justify-content: center;
  808. align-items: center;
  809. overflow: hidden;
  810. margin: 0 auto 2vh auto;
  811. position: relative;
  812. .area {
  813. position: absolute;
  814. top: 6vh;
  815. color: #203C52;
  816. font-size: 5.2vh;
  817. line-height: 1;
  818. font-family: 'Saira-ExtraBold';
  819. text-align: center;
  820. }
  821. img {
  822. width: 100%;
  823. }
  824. }
  825. .pic2 {
  826. box-sizing: border-box;
  827. border: 0.44rem solid rgba(26, 41, 58, 0.6315);
  828. }
  829. .name {
  830. width: 100%;
  831. color: #1A293A;
  832. font-size: 2.21rem;
  833. }
  834. }
  835. }
  836. .score {
  837. height: 14vh;
  838. line-height: 14vh;
  839. font-size: 9vh;
  840. font-family: 'Saira-BlackItalic';
  841. background: #ffffff;
  842. box-sizing: border-box;
  843. border: 0.55rem solid #13ED84;
  844. text-align: center;
  845. border-radius: 1.6rem;
  846. box-sizing: content-box;
  847. color: #1A293A;
  848. position: relative;
  849. display: none;
  850. &::before {
  851. content: "";
  852. width: 2vw;
  853. height: 2vw;
  854. display: block;
  855. position: absolute;
  856. top: -1.3vw;
  857. left: 50%;
  858. margin-left: -1vw;
  859. background-image: url("@/assets/images/home/test_icon.png");
  860. background-position: center;
  861. background-repeat: no-repeat;
  862. background-size: 100% 100%;
  863. border-radius: 50%;
  864. flex-shrink: 0;
  865. transition: all 0.5s;
  866. }
  867. }
  868. }
  869. }
  870. .overlap {
  871. .li {
  872. .userInfo {
  873. height: 6.2vw;
  874. .userInfo-center {
  875. width: 100%;
  876. text-align: center;
  877. display: flex;
  878. align-items: center;
  879. cursor: pointer;
  880. .pic {
  881. width: 6.2vw;
  882. height: 6.2vw;
  883. border-radius: 50%;
  884. display: flex;
  885. justify-content: center;
  886. align-items: center;
  887. overflow: hidden;
  888. position: relative;
  889. flex-shrink: 0;
  890. margin: 0;
  891. .area {
  892. position: absolute;
  893. top: 0;
  894. left: 0;
  895. width: 100%;
  896. height: 100%;
  897. line-height: 6.2vw;
  898. color: #ffffff;
  899. font-size: 2.5vw;
  900. font-family: 'Saira-ExtraBold';
  901. text-align: center;
  902. background: rgba(#000000, 0.4)
  903. }
  904. img {
  905. width: 100%;
  906. }
  907. }
  908. .pic2 {
  909. box-sizing: border-box;
  910. border: 2px solid rgba(26, 41, 58, 0.6315);
  911. }
  912. .name {
  913. width: 100%;
  914. color: #1A293A;
  915. font-size: 1.8rem;
  916. border-radius: 2vw;
  917. padding: 0.5rem 0;
  918. background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
  919. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
  920. }
  921. }
  922. }
  923. .score {
  924. display: block;
  925. }
  926. }
  927. }
  928. }
  929. ::v-deep(.testBox3) {
  930. $listMargin: 5vw;
  931. $listWidth: calc((100% - $listMargin) / 10);
  932. .ul {
  933. width: 100%;
  934. height: 14vh;
  935. display: flex;
  936. justify-content: space-evenly;
  937. margin-bottom: calc($listMargin / 10);
  938. .li {
  939. width: $listWidth;
  940. position: relative;
  941. height: 100%;
  942. border-radius: 5vh;
  943. background: radial-gradient(122% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
  944. .userInfo {
  945. padding: 2vh 0.6vw 0 0.6vw;
  946. margin-bottom: 0.8vh;
  947. .userInfo-center {
  948. text-align: center;
  949. display: flex;
  950. align-items: center;
  951. cursor: pointer;
  952. border-radius: 1.3vw;
  953. height: 2.6vw;
  954. background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.6571) 100%);
  955. border: 1px solid #FFFFFF;
  956. .pic {
  957. width: 2.6vw;
  958. height: 2.6vw;
  959. border-radius: 50%;
  960. display: flex;
  961. justify-content: center;
  962. align-items: center;
  963. overflow: hidden;
  964. position: relative;
  965. flex-shrink: 0;
  966. .area {
  967. position: absolute;
  968. top: 0;
  969. left: 0;
  970. width: 100%;
  971. height: 100%;
  972. line-height: 2.6vw;
  973. color: #ffffff;
  974. font-size: 1.4vw;
  975. font-family: 'Saira-ExtraBold';
  976. text-align: center;
  977. background: rgba(#000000, 0.4)
  978. }
  979. img {
  980. width: 100%;
  981. }
  982. }
  983. .pic2 {
  984. box-sizing: border-box;
  985. border: 2px solid rgba(26, 41, 58, 0.6315);
  986. }
  987. .name {
  988. color: #1A293A;
  989. font-size: 1rem;
  990. margin-left: 0.5vw;
  991. }
  992. }
  993. }
  994. .score {
  995. font-size: 3rem;
  996. font-family: 'Saira-BlackItalic';
  997. text-align: center;
  998. color: #1A293A;
  999. line-height: 1;
  1000. }
  1001. }
  1002. }
  1003. }
  1004. </style>