林旭祥 8 kuukautta sitten
vanhempi
commit
08195382df

+ 9 - 0
src/api/module/common.ts

@@ -101,5 +101,14 @@ export default {
       method: 'post',
       data: data
     });
+  },
+
+  //测试手势
+  cmdtest: (data: any) => {
+    return req({
+      url: '/device/handcontroller/cmdtest',
+      method: 'post',
+      data: data
+    });
   }
 };

+ 28 - 3
src/components/ChooseStudent/index.vue

@@ -23,8 +23,8 @@
             <el-button class="button" type="primary" @click="getSearchStudent">搜索</el-button>
           </div>
           <div class="tableBox">
-            <el-table :data="tableData" class="table" @cell-click="handleCellClickChange"
-              @selection-change="handleSelectionChange" @row-dblclick="handleDblclick" highlight-current-row>
+            <el-table ref="myTable" :data="tableData" class="table" :class="{ 'table2': props.selectType == 'single' }"
+              @cell-click="handleCellClickChange" @row-dblclick="handleDblclick" highlight-current-row>
               <el-table-column type="selection" width="55" v-if="props.selectType == 'multiple'" />
               <el-table-column label="头像" width="120">
                 <template #default="scope">
@@ -64,7 +64,7 @@ import useAppStore from '@/store/modules/app';
 import dataDictionary from "@/utils/dataDictionary"
 const { proxy } = getCurrentInstance() as any;
 const emit = defineEmits(['returnData']);
-
+const myTable = ref();
 //父值
 const props = defineProps({
   selectType: {
@@ -193,6 +193,21 @@ const handleCellClickChange = (data: any) => {
   if (props.selectType == 'single') {
     selectValue.value = [data];
   }
+  if (props.selectType == 'multiple') {
+    let ids = selectValue.value.map((item: any) => {
+      return item.student_id;
+    })
+    if (!ids.includes(data.student_id)) {
+      //不在内就添加
+      myTable.value.toggleRowSelection(data, true)
+      selectValue.value.push(data);
+    } else {
+      //在内就移除
+      myTable.value.toggleRowSelection(data, false)
+      delChooseStudent(data);
+    }
+
+  }
 };
 
 //多选行
@@ -464,4 +479,14 @@ defineExpose({
     }
   }
 }
+
+::v-deep(.table .el-table__body tr:hover>td.el-table__cell) {
+  background: #dddddd;
+  cursor: pointer;
+}
+
+::v-deep(.table2 .el-table__body tr.current-row > td) {
+  background: #13ED84;
+  color: #ffffff !important;
+}
 </style>

+ 32 - 3
src/components/Header/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
-    <div class="close" @click="confirmExit"></div>
-    <div class="toolList">
+    <div class="close" :class="{ close2: closeClass == 'close2' }" @click="confirmExit" v-if="showClose"></div>
+    <div class="toolList" v-if="showTool">
       <div class="li">{{ date }}</div>
       <div class="li btn speck" :class="{ 'on': voice, 'off': !voice }" @click="getVoice"></div>
       <div class="li btn screen" :class="{ 'on': screen, 'off': !screen }" @click="getFullScreen"></div>
@@ -9,7 +9,7 @@
     <div class="ranking" v-if="props.type == 'ranking'">
       <img src="@/assets/images/ranking/ranking.png" />
     </div>
-    <div class="logo" v-else>
+    <div class="logo" :class="{ logo2: logoClass == 'logo2' }" v-else>
       <img src="@/assets/images/logo.png" />
       <div class="title" v-if="parameter.project"><i></i><span>{{ dic.project[parameter.project] || "" }}</span></div>
     </div>
@@ -30,6 +30,22 @@ const props = defineProps({
     type: String,
     default: ""
   },
+  showClose: {
+    type: Boolean,
+    default: true
+  },
+  showTool: {
+    type: Boolean,
+    default: true
+  },
+  closeClass: {
+    type: String,
+    default: ""
+  },
+  logoClass: {
+    type: String,
+    default: ""
+  },
 });
 
 const data = reactive<any>({
@@ -160,6 +176,13 @@ $waiPadding: 6.51rem;
   }
 }
 
+.close2 {
+  $topPadding: 5.19rem;
+  $waiPadding: 6.51rem;
+  right: calc($waiPadding - 3.2rem);
+  top: $topPadding;
+}
+
 .toolList {
 
   position: absolute;
@@ -210,4 +233,10 @@ $waiPadding: 6.51rem;
     background-image: url("@/assets/images/common/screen2.png");
   }
 }
+
+@media screen and (max-width: 1450px) {
+  .logo2 {
+    left: 13rem;
+  }
+}
 </style>

+ 4 - 0
src/components/MultipleItem/index.vue

@@ -445,6 +445,10 @@ watch(() => props.examState, (newVal, oldVal) => {
   if (newVal == 3 && examState.value == 43) {
     initProject();
   }
+  //父状态已经进入41了就不要等子组件40清空数据了
+  if (newVal == 41) {
+    cleanData();
+  }
 }, { deep: true });
 
 onMounted(() => {

+ 1 - 0
src/layout/index.vue

@@ -15,6 +15,7 @@ const { proxy } = getCurrentInstance() as any;
 .app-main {
   height: 100vh;
   width: 100%;
+  background: radial-gradient(87% 87% at 9% -34%, #315f7e 0%, #0f1926 100%);
 }
 </style>
 <style lang="scss">

+ 2 - 1
src/router/index.ts

@@ -19,7 +19,8 @@ const router = createRouter({
         { path: '/set', component: () => import('@/views/set/index.vue') },
         { path: '/set/config', component: () => import('@/views/set/config.vue') },
         { path: '/ranking', component: () => import('@/views/ranking/index.vue') },
-        { path: '/course', component: () => import('@/views/course/index.vue') }
+        { path: '/course', component: () => import('@/views/course/index.vue') },
+        { path: '/gesture', component: () => import('@/views/gesture/index.vue') }
       ]
     }
   ]

+ 50 - 0
src/utils/handController.ts

@@ -0,0 +1,50 @@
+import io from 'socket.io-client';
+const address: any = import.meta.env.VITE_APP_BASE_API;
+const token: any = localStorage.getItem('token');
+let socket: any = {}; //ws实例对象
+
+export const initWs = (callback: any) => {
+  socket = io(address + '/', {
+    transports: ['websocket', 'polling'],
+    query: {
+      Authorization: token
+    }
+  });
+  socket.on('connect', (e: any) => {
+    sendMessage(
+      'handcontroller',
+      {
+        hctrl_name: 'handcontroller_2',
+        cmd: 'open_handcontroller'
+      },
+      () => {}
+    );
+  });
+
+  socket.on('handcontroller_ack', (e: any) => {
+    callback(e);
+    if (e.code == 94210) {
+      sendMessage(
+        'handcontroller',
+        {
+          hctrl_name: 'handcontroller_2',
+          cmd: 'terminate_handcontroller'
+        },
+        () => {}
+      );
+    }
+  });
+
+  socket.on('handcontroller_result', (e: any) => {
+    callback(e);
+  });
+
+  socket.on('disconnect', (e: any) => {});
+};
+
+export const sendMessage = (type: string, data: any, callback?: () => void) => {
+  if (socket.connected) {
+    callback = callback || function () {};
+    socket.emit(type, data, callback);
+  }
+};

+ 1 - 3
src/views/course/index.vue

@@ -1,8 +1,6 @@
 <template>
   <div class="train">
-    <div class="logo"> <img src="@/assets/images/logo.png">
-      </img></div>
-    <div class="close" @click="getExit"></div>
+    <Header @confirmExit="getExit" :showTool="false" closeClass="close2"></Header>
     <div class="menu">
       <div class="box">
         <div class="left">

+ 179 - 0
src/views/gesture/index.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="gesture">
+    <Header :showClose="false" :showTool="false"></Header>
+    <div class="menu">
+      <swiper v-if="projectList.length" ref="mySwiper" :slides-per-view="5" :space-between="0" :initialSlide="2"
+        :loop="true" :centeredSlides="true">
+        <swiper-slide v-for="(item, index) in projectList " :key="index">
+          <div class="li">
+            <div class="pic"><img :src="'static/images/train/' + item.key + '.png'"></div>
+            <div class="name">
+              {{ item.name }}
+            </div>
+          </div>
+        </swiper-slide>
+      </swiper>
+    </div>
+  </div>
+</template>
+
+<script setup name="Gesture" lang="ts">
+import { initWs } from '@/utils/handController'
+import { Swiper, SwiperSlide } from 'swiper/vue';
+import { Navigation, Pagination } from 'swiper/modules';
+import 'swiper/css';
+import 'swiper/scss/navigation';
+import 'swiper/scss/pagination';
+
+const mySwiper = ref(null);
+const router = useRouter();
+const { proxy } = getCurrentInstance() as any;
+
+const data = reactive<any>({
+  projectList: [],
+  timerManager: {},
+});
+const { projectList, timerManager } = toRefs(data);
+
+/**
+ * 清空定时任务
+*/
+const getClearTimer = () => {
+  for (let key in timerManager.value) {
+    if (timerManager.value.hasOwnProperty(key)) {
+      clearInterval(timerManager.value[key])
+      timerManager.value[key] = null;
+    }
+  }
+};
+
+/**
+ * 初始化项目
+*/
+const getInitExam = () => {
+  getExam();
+  //定时刷新
+  timerManager.value.exam = setInterval(() => {
+    getExam();
+  }, 5000)
+};
+
+/**
+ * 获取项目
+*/
+const getExam = async () => {
+  await proxy?.$http.train.projectList().then((res: any) => {
+    projectList.value = proxy?.$utils.getProject(res.exams).filter((item: any) => {
+      //只显示能开的
+      return item.area.length > 0;
+    });
+  });
+};
+
+onBeforeMount(() => {
+  //加载WS
+  initWs((e: any) => {
+    //左滑动
+    if (e.data.result == "next_item") {
+
+    }
+    //举左手
+    if (e.data.result == "left_hand") {
+
+    }
+    //退出
+    if (e.data.result == "exit") {
+
+    }
+  });
+  getInitExam();
+})
+onMounted(() => {
+})
+onUnmounted(() => {
+  getClearTimer();
+})
+</script>
+
+<style lang="scss" scoped>
+$topPadding: 5.19rem;
+$waiPadding: 6.51rem;
+
+.menu {
+  width: calc(100% - ($waiPadding * 2));
+  height: 100vh;
+  margin: 0 auto;
+  display: flex;
+  align-items: center;
+
+  .li {
+    // width: calc((100% / 6) - 1rem + (1rem/6));
+    // margin-right: 1rem;
+    // margin-bottom: 1rem;
+    width: 100%;
+    height: 100%;
+    padding: 3vh 0;
+    border-radius: 1.6rem;
+    box-sizing: border-box;
+    box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.9046), inset 0px 3px 6px 0px rgba(0, 0, 0, 0.0851);
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: center;
+    text-align: center;
+    background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
+    flex-shrink: 0;
+    cursor: pointer;
+
+    .name {
+      width: 100%;
+      font-size: 2.48rem;
+      color: #1A293A;
+      padding: 0.5rem 0;
+    }
+
+
+    .pic {
+      width: 11.36vw;
+      height: 11.36vw;
+      border-radius: 50%;
+      background: radial-gradient(78% 78% at 53% 50%, #07121A 0%, #2A4256 49%, #5180A9 100%);
+      box-shadow: 0px 0px 2px 2px #FFFFFF;
+      margin-bottom: 2vh;
+      overflow: hidden;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      flex-shrink: 0;
+
+      img {
+        max-width: 88%;
+        max-height: 88%;
+        transition: all 1s;
+      }
+    }
+
+    &:hover {
+      img {
+        transform: translateY(-0.5vw);
+      }
+    }
+  }
+
+  .swiper-slide {
+    transform: scale(0.8);
+    transition: all 0.3s ease-in-out;
+    border-radius: 1.6rem;
+    opacity: 0.6;
+    overflow: hidden;
+  }
+
+  .swiper-slide-active {
+    opacity: 1;
+    transform: scale(1);
+
+    .li {
+      background: radial-gradient(167% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
+    }
+  }
+}
+</style>

+ 1 - 1
src/views/home/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="home">
-    <div class="logo"><img src="@/assets/images/logo.png" /></div>
+    <Header :showClose="false" :showTool="false" logoClass="logo2"></Header>
     <div class="menu">
       <div class="left">
         <div class="li" @click="getJump('/train', '自助锻炼')"><img src="@/assets/images/home/train.png" />

+ 1 - 2
src/views/login/index.vue

@@ -1,7 +1,6 @@
 <template>
   <div class="login">
-    <div class="logo"> <img src="@/assets/images/logo.png">
-      </img></div>
+    <Header :showClose="false" :showTool="false" logoClass="logo2"></Header>
     <transition :enter-active-class="proxy?.animate.error.enter">
       <div class="login-content" :key="key">
         <div class="login-content-center">

+ 16 - 0
src/views/set/index.vue

@@ -20,6 +20,11 @@
         </div>
       </div>
     </div>
+    <div style=" color: #ffffff; font-size:2rem">
+      <div @click="getCmdtest(1)">左滑动</div>
+      <div @click="getCmdtest(2)">举左手</div>
+      <div @click="getCmdtest(3)">双手胸前交叉</div>
+    </div>
   </div>
 </template>
 
@@ -49,6 +54,17 @@ const getLogout = () => {
   });
 };
 
+
+// 手势
+const getCmdtest = (data: any) => {
+  let params = {
+    hctrl_name: 'handcontroller_2',
+    cmd: data
+  };
+  proxy?.$http.common.cmdtest(params).then((res: any) => {
+  });
+};
+
 /**
  * 返回
 */

+ 1 - 7
src/views/train/index.vue

@@ -1,8 +1,6 @@
 <template>
   <div class="train">
-    <div class="logo"> <img src="@/assets/images/logo.png">
-      </img></div>
-    <div class="close" @click="getExit"></div>
+    <Header @confirmExit="getExit" :showTool="false" closeClass="close2"></Header>
     <div class="menu" :class="projectList.length <= 6 ? 'menu1' : 'menu2'">
       <swiper :slides-per-view="6" :modules="[Grid]" :grid="{
         fill: projectList.length <= 6 ? 'row' : 'column',
@@ -28,18 +26,14 @@ import { Grid } from 'swiper/modules';
 import 'swiper/css';
 import 'swiper/css/grid';
 
-
-
 const router = useRouter();
 const { proxy } = getCurrentInstance() as any;
 const optionWindowRef = ref();
 
-
 const data = reactive<any>({
   projectList: [],
   timerManager: {},
 });
-
 const { projectList, timerManager } = toRefs(data);
 
 /**