index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <template>
  2. <div class="gesture">
  3. <Header :showTool="true" @confirmExit="getLogout" type="gesture"></Header>
  4. <div class="menu" v-if="projectList.length" :key="projectList.length">
  5. <swiper :slides-per-view="5" :space-between="0" :loop="true" :observe-parents="true" :observer="true"
  6. :centered-slides="true" :modules="[Navigation]" @swiper="onSwiper">
  7. <swiper-slide v-for="(item, index) in projectList" :key="index" @click="getOption(item)">
  8. <div class="li">
  9. <div>
  10. <div class="pic"><img :src="'static/images/train/' + item.key + '.png'" /></div>
  11. <div class="name">
  12. {{ item.name }}
  13. </div>
  14. </div>
  15. </div>
  16. </swiper-slide>
  17. </swiper>
  18. </div>
  19. <!-- <div class="erweima">
  20. <img :src="erweima" />
  21. <span>扫码遥控</span>
  22. </div> -->
  23. <OptionWindow ref="optionWindowRef" :projectList="projectList" />
  24. </div>
  25. </template>
  26. <script setup name="Gesture" lang="ts">
  27. import { useWebSocket } from '@/utils/handWs';
  28. import { Swiper, SwiperSlide } from 'swiper/vue';
  29. import { Navigation } from 'swiper/modules';
  30. import QRCode from 'qrcode';
  31. import 'swiper/css';
  32. import 'swiper/scss/navigation';
  33. const { handWs, startDevice, startHand, stateHand } = useWebSocket();
  34. const router = useRouter();
  35. const route = useRoute();
  36. const { proxy } = getCurrentInstance() as any;
  37. const optionWindowRef = ref();
  38. const data = reactive<any>({
  39. mySwiper: {},
  40. projectList: [],
  41. timerManager: {},
  42. deviceInfo: {},
  43. erweima: '',
  44. sid: '',
  45. });
  46. const { mySwiper, projectList, timerManager, deviceInfo, erweima, sid } = toRefs(data);
  47. /**
  48. * 清空定时任务
  49. */
  50. const getClearTimer = (data?: any) => {
  51. if (data) {
  52. //清除指定
  53. clearInterval(timerManager.value[data]);
  54. timerManager.value[data] = null;
  55. } else {
  56. for (let key in timerManager.value) {
  57. if (timerManager.value.hasOwnProperty(key)) {
  58. clearInterval(timerManager.value[key]);
  59. timerManager.value[key] = null;
  60. }
  61. }
  62. }
  63. };
  64. /**
  65. * 初始化项目
  66. */
  67. const getInitExam = () => {
  68. getExam();
  69. //定时刷新
  70. timerManager.value.exam = setInterval(() => {
  71. getExam();
  72. }, 5000);
  73. };
  74. /**
  75. * 获取项目
  76. */
  77. const getExam = async () => {
  78. let myList = deviceInfo.value?.project_list || [];
  79. let examList = myList.map((item: any) => {
  80. return item.exam_name;
  81. });
  82. await proxy?.$http.train.projectList().then((res: any) => {
  83. projectList.value = proxy?.$utils.getProject(res.exams).filter((item: any) => {
  84. //只显示能开的
  85. return item.area.length > 0 && examList.includes(item.key);
  86. //return item.area.length > 0;
  87. });
  88. // console.log("projectList.value", projectList.value)
  89. });
  90. };
  91. /**
  92. * 获取实例
  93. */
  94. const onSwiper = ($ev: any) => {
  95. // console.log('1111111', $ev);
  96. mySwiper.value = $ev;
  97. slidePrev();
  98. };
  99. /**
  100. * 切换上一页
  101. */
  102. const slidePrev = () => {
  103. mySwiper.value.slidePrev();
  104. };
  105. /**
  106. * 切换下一页
  107. */
  108. const slideNext = () => {
  109. mySwiper.value.slideNext();
  110. };
  111. /**
  112. * 启动切换
  113. */
  114. const slideChange = () => {
  115. // if (projectList.value.length <= 3) {
  116. // slidePrev()
  117. // } else {
  118. // slideNext();
  119. // }
  120. slidePrev();
  121. };
  122. /**
  123. * 进入项目
  124. */
  125. const getOption = (data: any) => {
  126. confirm(data);
  127. };
  128. /**
  129. * 确定并进入
  130. */
  131. const confirm = (myProject?: any) => {
  132. let project = myProject || projectList.value[mySwiper.value.realIndex];
  133. console.log('projectList.value', projectList.value);
  134. console.log('mySwiper.value', mySwiper.value);
  135. console.log('mySwiper.value.realIndex', mySwiper.value.realIndex);
  136. console.log('project', project);
  137. if (project == undefined) {
  138. proxy?.$modal.msgError('获取不到项目信息');
  139. return false;
  140. }
  141. let obj = deviceInfo.value.project_list.find((item: any) => {
  142. return item.exam_name == project.key;
  143. });
  144. if (obj == undefined) {
  145. proxy?.$modal.msgError('该项目没有配置参数');
  146. return false;
  147. }
  148. let dataObj = {
  149. gesture: obj.gesture ? true : false,
  150. demo: obj?.demo || 0,
  151. area: obj?.area_test_id || '',
  152. ctrl: obj?.area_ctrl_id || '',
  153. time: obj?.test_time || '',
  154. music: obj?.music_info?.id || '',
  155. handcontroller: deviceInfo.value.handcontroller_id
  156. };
  157. optionWindowRef.value.getGesture(project, dataObj);
  158. };
  159. /**
  160. * 退出
  161. */
  162. const getLogout = async () => {
  163. if (import.meta.env.DEV) {
  164. proxy?.$modal.msgSuccess('测试环境免密退出');
  165. await proxy?.$http.common.logout({}).then((res: any) => { });
  166. proxy?.$modal?.closeLoading();
  167. //清空缓存
  168. //localStorage.clear();
  169. localStorage.removeItem('token');
  170. localStorage.removeItem('userInfo');
  171. //跳转
  172. router.push({ path: '/login/qrcode' });
  173. } else {
  174. let myInfo: any = localStorage.getItem('userInfo');
  175. let userInfo = JSON.parse(myInfo);
  176. proxy?.$modal
  177. .prompt(`【${userInfo.login_name}】,请输入密码`, 'password')
  178. .then((e: any) => {
  179. // console.log("e", e)
  180. if (e.action == 'confirm' && e.value) {
  181. let params = {
  182. password: e.value
  183. };
  184. proxy?.$http.common.checkPassword(params).then(async (res: any) => {
  185. if (res.status === 200 || res.status === 1) {
  186. await proxy?.$http.common.logout({}).then((res: any) => { });
  187. proxy?.$modal?.closeLoading();
  188. //清空缓存
  189. // localStorage.clear();
  190. localStorage.removeItem('token');
  191. localStorage.removeItem('userInfo');
  192. //跳转
  193. router.push({ path: '/login/qrcode' });
  194. } else {
  195. proxy?.$modal.msgError(res.message);
  196. }
  197. });
  198. }
  199. })
  200. .finally(() => { });
  201. }
  202. };
  203. /**
  204. * 提示语
  205. */
  206. const getTips = () => {
  207. let myTime = 7000;
  208. let num = 0;
  209. timerManager.value.tips = setInterval(() => {
  210. if (projectList.value.length) {
  211. if (num % 2 == 0) {
  212. proxy?.$modal.msgWarning({
  213. message: `手向左滑切换项目`,
  214. duration: 5000
  215. });
  216. } else {
  217. proxy?.$modal.msgWarning({
  218. message: `举左手进入测试`,
  219. duration: 5000
  220. });
  221. }
  222. num++;
  223. }
  224. }, myTime);
  225. };
  226. /**
  227. * 获取设备项目
  228. */
  229. const getDevice = async () => {
  230. let deviceid = localStorage.getItem('deviceid') || '';
  231. if (deviceid) {
  232. startDevice({ deviceid: deviceid });
  233. } else {
  234. proxy?.$modal.msgError(`缺少设备信息请重新登录!`);
  235. await proxy?.$http.common.logout({}).then((res: any) => { });
  236. proxy?.$modal?.closeLoading();
  237. //清空缓存
  238. // localStorage.clear();
  239. localStorage.removeItem('token');
  240. localStorage.removeItem('userInfo');
  241. //跳转
  242. router.push({ path: '/login/qrcode' });
  243. }
  244. };
  245. /**
  246. * 获取二维码
  247. */
  248. const getErweima = () => {
  249. let deviceid = localStorage.getItem('deviceid') || '';
  250. QRCode.toDataURL(`${location.origin}/#/login/mobile?sid=${sid}&deviceid=${deviceid}`).then((res: any) => {
  251. erweima.value = res;
  252. });
  253. };
  254. onBeforeMount(() => {
  255. localStorage.setItem('loginType', "2");
  256. //加载设备WS
  257. handWs((e: any) => {
  258. if (router.currentRoute.value.path != '/gesture') {
  259. return false;
  260. }
  261. // console.log("eeeee", e)
  262. if (e?.wksid) {
  263. //获取设备项目
  264. getDevice();
  265. }
  266. //接收设备信息
  267. if (e?.device_info) {
  268. deviceInfo.value = e.device_info;
  269. let handcontroller_id = deviceInfo.value.handcontroller_id;
  270. stateHand(handcontroller_id);
  271. getInitExam();
  272. }
  273. //获取手势状态
  274. if (e?.cmd == 'get_handcontroller_state' && e?.state == 0) {
  275. let handcontroller_id = deviceInfo.value.handcontroller_id;
  276. startHand(handcontroller_id);
  277. }
  278. //左滑动
  279. if (e?.data?.result == 'next_item') {
  280. // proxy?.$modal.msgSuccess('手势指令:左滑动');
  281. // if (projectList.value.length == 0) {
  282. // return false;
  283. // }
  284. // slideChange();
  285. }
  286. //举左手
  287. if (e?.data?.result == 'left_hand') {
  288. // proxy?.$modal.msgSuccess('手势指令:举左手');
  289. // confirm();
  290. }
  291. //退出
  292. if (e?.data?.result == 'exit') {
  293. // proxy?.$modal.msgSuccess('手势指令:交叉手');
  294. }
  295. //刷新
  296. if (e?.data?.result == 'refresh') {
  297. window.location.reload();
  298. }
  299. });
  300. });
  301. onMounted(() => {
  302. //登录码
  303. getErweima();
  304. //提示语
  305. // getTips();
  306. });
  307. onBeforeUnmount(() => {
  308. getClearTimer();
  309. ElMessage.closeAll();
  310. });
  311. </script>
  312. <style lang="scss" scoped>
  313. $topPadding: 5.19rem;
  314. $waiPadding: 6.51rem;
  315. .menu {
  316. width: calc(100% - ($waiPadding * 2));
  317. height: 100vh;
  318. margin: 0 auto;
  319. display: flex;
  320. align-items: center;
  321. .li {
  322. // width: calc((100% / 6) - 1rem + (1rem/6));
  323. // margin-right: 1rem;
  324. // margin-bottom: 1rem;
  325. width: 100%;
  326. height: 100%;
  327. padding: 3vh 0;
  328. border-radius: 1.6rem;
  329. box-sizing: border-box;
  330. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
  331. display: flex;
  332. justify-content: center;
  333. align-items: center;
  334. background: radial-gradient(96% 96% at 2% 32%, #ffffff 0%, #fcfdfd 54%, #e1e4e7 100%);
  335. cursor: pointer;
  336. .name {
  337. width: 100%;
  338. font-size: 2.48rem;
  339. color: #1a293a;
  340. padding: 0.5rem 0;
  341. text-align: center;
  342. }
  343. .pic {
  344. width: 11.36vw;
  345. height: 11.36vw;
  346. border-radius: 50%;
  347. background: radial-gradient(78% 78% at 53% 50%, #07121a 0%, #2a4256 49%, #5180a9 100%);
  348. box-shadow: 0px 0px 2px 2px #ffffff;
  349. margin-bottom: 2vh;
  350. overflow: hidden;
  351. display: flex;
  352. align-items: center;
  353. justify-content: center;
  354. flex-shrink: 0;
  355. img {
  356. max-width: 88%;
  357. max-height: 88%;
  358. transition: all 1s;
  359. }
  360. }
  361. &:hover {
  362. img {
  363. transform: translateY(-0.5vw);
  364. }
  365. }
  366. }
  367. .swiper {
  368. width: 100%;
  369. }
  370. .swiper-slide {
  371. transform: scale(0.8);
  372. transition: all 0.3s ease-in-out;
  373. border-radius: 1.6rem;
  374. opacity: 0.6;
  375. overflow: hidden;
  376. }
  377. .swiper-slide-active {
  378. opacity: 1;
  379. transform: scale(1);
  380. .li {
  381. background: radial-gradient(167% 126% at 97% 6%, #35ffc6 0%, #00ffe8 100%);
  382. }
  383. }
  384. }
  385. .erweima {
  386. position: absolute;
  387. right: 3rem;
  388. bottom: 3rem;
  389. text-align: center;
  390. img {
  391. width: 6.625rem;
  392. }
  393. span {
  394. display: block;
  395. color: #ffffff;
  396. font-size: 1rem;
  397. padding-top: 3px;
  398. }
  399. }
  400. </style>