index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. <template>
  2. <div class="li">
  3. <div class="userInfo" @click="getChooseStudent">
  4. <div class="userInfo-center">
  5. <Transition :enter-active-class="proxy?.animate.dialog.enter">
  6. <div class="pic" :class="{ 'pic2': faceCheckStu.student_id }" v-if="faceCheckStu.student_id"> <img
  7. :src="faceCheckStu.face_pic || faceCheckStu.logo_url" /></div>
  8. <div class="pic" v-else>
  9. <div class="area">{{ area }}</div>
  10. <img src="@/assets/images/test/profilePicture2.png" />
  11. </div>
  12. </Transition>
  13. <div class="name" :class="{ 'name2': faceCheckStu.student_id }">
  14. {{ faceCheckStu.student_id ? faceCheckStu.name : area ? "虚位以待" : "未启用" }}
  15. </div>
  16. </div>
  17. </div>
  18. <div class="score">
  19. {{ currentResultObj.count
  20. || 0 }}
  21. </div>
  22. <!-- <div>当前状态:({{ examState == 3 ? "初始化完成" : examState == 40 ? "创建测试" : examState == 41 ? "正在人脸识别" :examState == 43 ? "停止人脸识别" : examState == 42 ? "正在测试" : "离线状态" }})</div> -->
  23. <ChooseStudent ref="chooseStudentRef" @returnData="returnStudent" />
  24. </div>
  25. </template>
  26. <script setup lang="ts">
  27. import { initSpeech, speckText, speckCancel, chineseNumber } from '@/utils/speech'
  28. import { openOneTest, startFace, stopFace, faceConfirmOnly, startOneTest, finishOneTest, closeOneTest, suspendFaceRecognitionChannels, resumeFaceRecognitionChannels } from '@/utils/ws'
  29. import dataDictionary from "@/utils/dataDictionary"
  30. const route = useRoute();
  31. const { proxy } = getCurrentInstance() as any;
  32. const chooseStudentRef = ref();
  33. const emit = defineEmits(['returnData']);
  34. //父值
  35. const props = defineProps({
  36. area: {
  37. type: String,
  38. default: ""
  39. },
  40. examState: {
  41. type: Number,
  42. default: null
  43. },
  44. needStart: {
  45. type: Boolean,
  46. default: false
  47. },
  48. styleType: {
  49. type: Number,
  50. default: null
  51. },
  52. });
  53. let project: any = route.query.project;
  54. let area: any = props.area;
  55. let examId: string = `${project}_${area}`; //项目+区
  56. const data = reactive<any>({
  57. examState: 0,//当前状态
  58. resultId: null,//测试ID
  59. currentResultObj: {},//成绩
  60. faceCheckStu: {},//人脸信息
  61. unit: "",//单位
  62. backReason: [],//犯规项
  63. });
  64. const { examState, resultId, faceCheckStu, currentResultObj, unit, backReason } = toRefs(data);
  65. /**
  66. * 接收消息
  67. */
  68. const getMessage = (e: any) => {
  69. //console.log("WS响应:", e)
  70. //实时状态
  71. if (e.cmd === 'exam_status') {
  72. examState.value = e.data;
  73. }
  74. //工作站状态
  75. if (e.cmd === 'init_result') {
  76. }
  77. //测试违规
  78. if (e.cmd === 'warning_result') {
  79. }
  80. //后端播报语音
  81. if (e.cmd === 'return_audio_msg') {
  82. }
  83. //错误提示
  84. if (e.cmd === 'info_result') {
  85. proxy?.$modal.msgError(`【${area}】${e.data.message}`);
  86. }
  87. //错误提示
  88. if (e.cmd === 'error_result') {
  89. proxy?.$modal.msgError(`【${area}】${e.data.message}`);
  90. }
  91. //测试中违规提示
  92. if (e.cmd === 'warning_notify') {
  93. }
  94. //断线状态
  95. if (e.cmd === 'disconnect_request') {
  96. examState.value = 0;
  97. emit('returnData', { examState: examState.value, area: area });
  98. let message = e.data.message;
  99. if (message) {
  100. proxy?.$modal.msgError(`【${area}】${message}`);
  101. //speckText(e.data.message);
  102. }
  103. }
  104. //状态变更
  105. if (e.cmd === 'set_exam_state') {
  106. examState.value = e.data;
  107. if (e.data === 3) {
  108. initProject();
  109. }
  110. if (e.data === 40) {
  111. cleanData();
  112. }
  113. if (e.data == 41) {
  114. }
  115. if (e.data == 43) {
  116. }
  117. if (e.data == 42) {
  118. }
  119. }
  120. //新建测试后返回信息,获取result_id
  121. if (e.cmd === 'open_one_test_ack') {
  122. resultId.value = e.data.result_id;
  123. }
  124. //人脸识别状态
  125. if (e.cmd === 'face_check_result') {
  126. let myData = e.data[0] || e.data;
  127. returnStudent(myData);
  128. }
  129. //测试结束结果
  130. if (e.cmd === 'oneresult') {
  131. if (e.data.length) {
  132. let data = e.data[0];
  133. getAchievement(data)
  134. }
  135. }
  136. //结果生成完成(视频图片)
  137. if (e.cmd === 'static_urls_finished') {
  138. }
  139. //选择学生或测试结束后返回的数据
  140. if (e.cmd === 'result_info') {
  141. }
  142. };
  143. /**
  144. * 开始识别
  145. */
  146. const getOpenOneTestAndStartFace = async () => {
  147. console.log("examId", examId)
  148. if (examState.value > 3) {
  149. await closeOneTest(examId);
  150. }
  151. await openOneTest(examId);
  152. await startFace(examId);
  153. };
  154. /**
  155. * 停止人脸识别
  156. */
  157. const getStopFace = async () => {
  158. if (examState.value != 41) {
  159. return false;
  160. }
  161. await stopFace(examId);
  162. if (faceCheckStu.value.student_id) {
  163. getFaceConfirmOnly();
  164. }
  165. };
  166. /**
  167. * 确定人脸信息
  168. */
  169. const getFaceConfirmOnly = (data?: any) => {
  170. if (data) {
  171. faceCheckStu.value = data;
  172. }
  173. faceConfirmOnly({
  174. exam_id: examId,
  175. // result_id: resultId.value,
  176. student_id: faceCheckStu.value.student_id,
  177. gender: faceCheckStu.value.gender
  178. }, () => {
  179. });
  180. };
  181. /**
  182. * 重新识别
  183. */
  184. const getRetestFace = () => {
  185. if (props.examState == 42) {
  186. return false;
  187. }
  188. proxy?.$modal.confirm("确定重新识别吗?").then(() => {
  189. if (props.needStart == false) {
  190. //自动流程项目重新识别直接返回3
  191. closeOneTest(examId);
  192. } else {
  193. //手动流程项目重新识别43返回41,42返回3
  194. if (examState.value == 43) {
  195. cleanData();
  196. startFace(examId);
  197. } else {
  198. closeOneTest(examId);
  199. }
  200. }
  201. }).finally(() => {
  202. });
  203. };
  204. /**
  205. * 开始测试
  206. */
  207. const getStartOneTest = () => {
  208. if (examState.value != 43 || !faceCheckStu.value.student_id) {
  209. return false;
  210. }
  211. startOneTest(examId, () => { })
  212. };
  213. /**
  214. * 再测一次
  215. */
  216. const getAgain = async () => {
  217. //预存测试人员
  218. let student = JSON.parse(JSON.stringify(faceCheckStu.value));
  219. //测试中
  220. if (examState.value == 42) {
  221. await finishOneTest(examId);
  222. }
  223. //其他状态
  224. if (examState.value > 3) {
  225. await closeOneTest(examId);
  226. }
  227. //重新走一次流程
  228. await openOneTest(examId);
  229. await startFace(examId);
  230. await stopFace(examId);
  231. getFaceConfirmOnly(student);
  232. };
  233. /**
  234. * 选择学生
  235. */
  236. const getChooseStudent = () => {
  237. if (!area) {
  238. proxy?.$modal.msgWarning(`未启用`);
  239. return false;
  240. }
  241. console.log("examState.value", examState.value)
  242. if (examState.value == 0) {
  243. proxy?.$modal.msgWarning(`当前状态:${examState.value}`);
  244. return false;
  245. }
  246. if (examState.value == 3) {
  247. if (!props.needStart) {
  248. proxy?.$modal.msgWarning(`当前状态:${examState.value}`);
  249. }
  250. if (props.needStart) {
  251. proxy?.$modal.msgWarning(`请点击开始识别按钮`);
  252. }
  253. return false;
  254. }
  255. if (examState.value == 41) {
  256. chooseStudentRef.value.open();
  257. }
  258. if (examState.value == 43) {
  259. getRetestFace();
  260. }
  261. if (props.examState == 42) {
  262. proxy?.$modal.msgWarning(`【${area}】正在测试请结束后再操作`);
  263. return false;
  264. }
  265. };
  266. /**
  267. * 返回被选学生
  268. */
  269. const returnStudent = (data: any) => {
  270. faceCheckStu.value = data;
  271. getStopFace();
  272. };
  273. /**
  274. * 清除历史记录
  275. */
  276. const cleanData = () => {
  277. faceCheckStu.value = {};
  278. currentResultObj.value = {};
  279. backReason.value = [];
  280. };
  281. /**
  282. * 自动初始化项目
  283. */
  284. const initProject = () => {
  285. if (!area) {
  286. return false;
  287. }
  288. //自动项目定时进入下一步
  289. if (props.needStart == false) {
  290. let time = 0;
  291. //控制新建测试的时间,第一次快,之后就慢
  292. if (!faceCheckStu.value.student_id) {
  293. time = 500;
  294. } else {
  295. time = 7000;
  296. }
  297. setTimeout(() => {
  298. //再加一个判断以免和再测一次冲突
  299. if (examState.value == 3 || examState.value == 43) {
  300. console.log("执行了执行了执行了执行了执行了")
  301. getOpenOneTestAndStartFace();
  302. }
  303. }, time)
  304. }
  305. };
  306. /**
  307. * 成绩
  308. */
  309. const getAchievement = (data: any) => {
  310. //console.log("成绩", data);
  311. let dic: any = dataDictionary;
  312. let type = project;
  313. let count =
  314. data?.[dic.typeResultKey[type]]?.toFixed(0);
  315. if (["trijump", "solidball", "shotput", "longjump"].includes(type)) {
  316. count =
  317. data?.[dic.typeResultKey[type]]?.toFixed(2);
  318. count = Math.round(count) / 100;
  319. }
  320. data.count = count || "0";
  321. data.score = data.score || "0";
  322. currentResultObj.value = data;
  323. //违规处理
  324. let arr = backReason.value;
  325. if (["situp", "pullup", "sidepullup", "jumprope", "jumpingjack", "jump", "longjump", "verticaljump"]
  326. .indexOf(type) > -1) {
  327. if (["pullup", "situp", "jumprope", "jumpingjack"].indexOf(type) > -1) {
  328. currentResultObj.value.back_num = data?.all_failed_num;
  329. }
  330. if (type === "sidepullup") {
  331. currentResultObj.value.back_num = data?.["0"]?.hip_failed_num;
  332. }
  333. if (['jump', 'longjump', 'verticaljump'].includes(type)) {
  334. if (data?.startline_check == 0) {
  335. let txt = "踩线违规";
  336. arr.push(txt)
  337. }
  338. if (data?.singleleg_jump_check == 0) {
  339. let txt = "单脚跳违规";
  340. arr.push(txt)
  341. }
  342. if (data?.outside_check == 0) {
  343. let txt = "跳出测试区违规";
  344. arr.push(txt)
  345. }
  346. }
  347. if (
  348. data?.elbow_check == false
  349. ) {
  350. let txt = "肘部违规";
  351. arr.push(txt);
  352. }
  353. if (
  354. ["situp", "pullup"].indexOf(type) > -1 &&
  355. data?.knee_check === false
  356. ) {
  357. let txt = "腿部违规";
  358. if (!arr.includes(txt)) {
  359. arr.push(txt);
  360. }
  361. }
  362. if (["situp"].indexOf(type) > -1 && data?.hand_check === false) {
  363. let txt = "手部违规";
  364. if (!arr.includes(txt)) {
  365. arr.push(txt);
  366. }
  367. }
  368. if (
  369. ["pullup"].indexOf(type) > -1 &&
  370. data?.["0"]?.elbow_check === false
  371. ) {
  372. let txt = "手部违规";
  373. if (!arr.includes(txt)) {
  374. arr.push(txt);
  375. }
  376. }
  377. if (["situp"].indexOf(type) > -1 && data?.["0"]?.back_check === false) {
  378. let txt = "背部违规";
  379. if (!arr.includes(txt)) {
  380. arr.push(txt);
  381. }
  382. }
  383. if (
  384. ["sidepullup", "situp"].indexOf(type) > -1 &&
  385. data?.["0"]?.hip_check === false
  386. ) {
  387. let txt = "臀部违规";
  388. if (!arr.includes(txt)) {
  389. arr.push(txt);
  390. }
  391. }
  392. }
  393. backReason.value = arr;
  394. if (data.isfinish) {
  395. // if (['jump'].includes(type) && backReason.value.length) {
  396. // speckText("请重新测试");
  397. // return false;
  398. // }
  399. // speckText(faceCheckStu?.value.name + "成绩为" + (chineseNumber(count) || 0) + unit.value + ",请下一位准备!" || "");
  400. }
  401. };
  402. /**
  403. * 状态抛给父组件
  404. */
  405. watch(() => examState.value, (newVal, oldVal) => {
  406. console.log(area, examState.value)
  407. emit('returnData', { examState: examState.value, area: area });
  408. }, { deep: true });
  409. /**
  410. * 人脸信息抛给父组件
  411. */
  412. watch(() => faceCheckStu.value, (newVal, oldVal) => {
  413. emit('returnData', { faceCheckStu: faceCheckStu.value, area: area });
  414. }, { deep: true });
  415. /**
  416. * 如果总流程结束了个别区仍然在43就重新执行一次,用于没有开的区自动复位
  417. */
  418. watch(() => props.examState, (newVal, oldVal) => {
  419. if (newVal == 3 && examState.value == 43) {
  420. initProject();
  421. }
  422. //父状态已经进入41了就不要等子组件40清空数据了
  423. if (newVal == 41) {
  424. cleanData();
  425. }
  426. }, { deep: true });
  427. onMounted(() => {
  428. let dic: any = dataDictionary;
  429. unit.value = dic.unit[project];
  430. })
  431. //暴露给父组件用
  432. defineExpose({
  433. getMessage,
  434. getOpenOneTestAndStartFace,
  435. getStopFace,
  436. getStartOneTest,
  437. getAgain,
  438. })
  439. </script>
  440. <style scoped lang="scss"></style>