Ver Fonte

日常开发

林旭祥 há 1 mês atrás
pai
commit
5b5ead9b60

+ 0 - 550
src/components/AddItemWindow/index.vue

@@ -1,550 +0,0 @@
-<template>
-  <div>
-    <Transition :enter-active-class="proxy?.animate.mask.enter" :leave-active-class="proxy?.animate.mask.leave">
-      <div class="mask" v-if="optionWindow.show"></div>
-    </Transition>
-    <Transition :enter-active-class="proxy?.animate.dialog.enter" :leave-active-class="proxy?.animate.dialog.leave">
-      <div class="optionWindow" v-if="optionWindow.show">
-        <div class="box">
-          <div class="top">
-            <div class="close" @click="close"></div>
-          </div>
-          <div class="content">
-            <div class="areaBox">
-              <div class="content-title content-title2">
-                {{ project.key !== 'skiprope' ? '测试区域' : '设备组' }}
-              </div>
-              <div class="testAreaChooseRoll">
-                <div class="li" v-for="(item, index) in areaList"
-                  :class="{ 'select': chooseArea.includes(item.key), 'ing': item.value != '0' }" :key="index"
-                  @click="getChooseArea(item)">
-                  <div>{{ item.name }}</div>
-                </div>
-              </div>
-              <div @click="getAllArea" class="allBtn" :class="{ 'active': chooseAllState }">{{ chooseAllState ? '重 置' :
-                '全 选' }}</div>
-            </div>
-            <!-- <div class="standardBox">
-              <div class="content-title content-title2">评分标准</div>
-              <div class="standardBoxBtn">
-                <div v-for="(item, index) in standardList" :class="{ 'select': optionForm.standard == item.value }"
-                  :key="index" class="li" @click="getChooseStandard(item)">
-                  <div>{{ item.label }}</div>
-                </div>
-              </div>
-            </div> -->
-            <div class="switchList">
-              <div class="li">
-                <span>举右手识别</span>
-                <el-switch v-model="optionForm.gesture" :active-value="true" :inactive-value="false"
-                  style="--el-switch-on-color: #08FFA9; --el-switch-off-color: #ACACAC" />
-              </div>
-              <div class="li" v-if="project.key.slice(0, 3) != 'run'">
-                <span>体验模式</span>
-                <el-switch v-model="optionForm.demo" :active-value="1" :inactive-value="0"
-                  style="--el-switch-on-color: #08FFA9; --el-switch-off-color: #ACACAC" />
-              </div>
-              <div class="li" v-if="['skiprope'].includes(project.key)">
-                <span>接收心率</span>
-                <el-switch v-model="optionForm.hasHB" :active-value="true" :inactive-value="false"
-                  style="--el-switch-on-color: #08FFA9; --el-switch-off-color: #ACACAC" />
-              </div>
-            </div>
-            <div v-if="['heartbeat'].includes(project.key)">
-              <div>设置运动量目标</div>
-              <div>
-                平均心率:
-                <div style="display: flex;">
-                  <el-input v-model="optionForm.standHBL" />
-                  ~
-                  <el-input v-model="optionForm.standHBH" />
-                </div>
-              </div>
-              <div>
-                预警心率:
-                <div>
-                  <el-input v-model="optionForm.highHB" size="small" />
-                </div>
-              </div>
-            </div>
-            <div class="switchList">
-              <div class="li" v-if="['jumprope', 'jumpingjack', 'highknees', 'situp'].includes(project.key)">
-                <span>时长</span>
-                <el-select v-model="optionForm.time" :popper-append-to-body="false" placeholder="请选择">
-                  <el-option v-for="item in timeList" :key="item.value" :label="item.label" :value="item.value" />
-                </el-select>
-              </div>
-              <div class="li" v-if="['jumprope', 'jumpingjack', 'highknees', 'situp'].includes(project.key)">
-                <span>音乐</span>
-                <el-select v-model="optionForm.music" :popper-append-to-body="false" placeholder="请选择" clearable>
-                  <el-option v-for="item in musicList" :key="item.id" :label="item.name" :value="item.url" />
-                </el-select>
-              </div>
-            </div>
-          </div>
-          <div class="bottom">
-            <div class="btn" @click="confirm">
-              <el-icon class="is-loading" v-if="loading">
-                <Loading />
-              </el-icon>
-              <div>确 定</div>
-            </div>
-          </div>
-          <div class="boxBg" :style="{ backgroundImage: 'url(static/images/train/' + project.key + '.png)' }">
-          </div>
-        </div>
-      </div>
-    </Transition>
-  </div>
-</template>
-<script setup lang="ts">
-import useAppStore from '@/store/modules/app';
-const { proxy } = getCurrentInstance() as any;
-const router = useRouter();
-
-//父值
-const props = defineProps({
-  projectList: {
-    type: Array as any,
-    default: () => []
-  },
-});
-
-//评分标准,数据字典无需双向绑定
-const standardList = [{ label: '考试', value: 0 }, { label: '体测', value: 1 }]
-
-//时长选择,数据字典无需双向绑定
-const timeList =
-  [{
-    label: '10秒',
-    value: 10
-  }, {
-    label: '20秒',
-    value: 20
-  }, {
-    label: '30秒',
-    value: 30
-  }, {
-    label: '1分钟',
-    value: 60
-  }, {
-    label: '2分钟',
-    value: 120
-  }, {
-    label: '3分钟',
-    value: 180
-  }, {
-    label: '5分钟',
-    value: 300
-  }];
-
-const data = reactive<any>({
-  optionForm: {
-    gesture: true,
-    standard: 0
-  },
-  optionWindow: {
-    show: false,
-    time: "",
-  },
-  project: {},
-  musicList: [],
-  classList: [],
-  chooseArea: [],
-  chooseAllState: false,
-  loading: false,
-});
-
-const { optionForm, optionWindow, project, musicList, classList, chooseArea, chooseAllState, loading } = toRefs(data);
-
-//打开
-const open = (data: any) => {
-  console.log("data", data);
-  getClass();
-  getMusic();
-  project.value = data;
-  chooseArea.value = [];
-  optionWindow.value.show = true;
-};
-
-//关闭
-const close = () => {
-  optionWindow.value.show = false;
-};
-
-//筛选测试区
-const areaList = computed(() => {
-  let area = [];
-  let list = props.projectList.filter((item: any) => {
-    return item.key == project.value.key;
-  })
-  if (list.length > 0) {
-    area = list[0].area;
-  }
-  //console.log("area", area)
-  return area;
-});
-
-//获取音乐
-const getMusic = () => {
-  const list: any = useAppStore().getMusic();
-  if (list.length) {
-    musicList.value = list;
-  } else {
-    proxy?.$http.train.musicList().then((res: any) => {
-      if (res.data.length > 0) {
-        let myList: any = res.data;
-        musicList.value = myList;
-        useAppStore().setMusic(myList);
-      }
-    });
-  }
-
-};
-
-//获取班级列表
-const getClass = () => {
-  const list: any = useAppStore().getClass();
-  if (list.length) {
-    classList.value = list;
-  } else {
-    let params = {
-      page: 1,
-      per_page: 9999,
-    };
-    proxy?.$http.common.classList(params).then((res: any) => {
-      if (res.data.length > 0) {
-        let myList: any = res.data;
-        classList.value = myList;
-        useAppStore().setClass(myList);
-      }
-    });
-  }
-};
-
-//选择测试区
-const getChooseArea = (data: any) => {
-  if (chooseArea.value.length > 10) {
-    optionForm.value.gesture = false;
-  } else {
-    optionForm.value.gesture = true;
-  }
-  let id = data.key;
-  let inData = chooseArea.value.includes(id);
-  if (inData) {
-    //已存在就移除
-    chooseArea.value = chooseArea.value.filter((item: any) => {
-      return item != id
-    })
-  } else {
-    //不存在就点选
-    chooseArea.value.push(id);
-  }
-};
-
-//选择测试标准
-const getChooseStandard = (data: any) => {
-  optionForm.value.standard = data.value;
-};
-
-//全选或全取消测试区
-const getAllArea = (data: any) => {
-  chooseAllState.value = !chooseAllState.value;
-  if (chooseAllState.value) {
-    //全选
-    chooseArea.value = areaList.value.filter((item: any) => {
-      return item.value == 0;
-    }).map((item: any) => {
-      return item.key;
-    })
-  } else {
-    //全取消
-    chooseArea.value = [];
-  }
-};
-
-//确定
-const confirm = (data: any) => {
-  optionForm.value.project = project.value.key;
-  optionForm.value.classes = classList.value.map((item: any) => { return item.id; }).join();
-  optionForm.value.area = chooseArea.value.join();
-  console.log("optionForm", optionForm.value);
-  if (!optionForm.value.classes) {
-    getClass();
-    let message = "没有班级,将重新加载班级请重新操作";
-    ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
-    return false;
-  }
-  if (!optionForm.value.area) {
-    let message = "请选择测试区";
-    ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
-    return false;
-  }
-  if (optionForm.value.standard == null) {
-    let message = "请选择评分标准";
-    ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
-    return false;
-  }
-  loading.value = true;
-  if (chooseArea.value.length > 1) {
-    //多区域
-    router.push({ path: '/train/multiple', query: optionForm.value });
-  }
-  else if (["run50", "run70", "run100", "run200", "run400", "run800", "run1000", "run15x4", "run10x4", "run50x8"].includes(project.value.key)) {
-    //跑步项目
-    router.push({ path: '/train/run', query: optionForm.value });
-  } else {
-    //单项
-    router.push({ path: '/train/test', query: optionForm.value });
-  }
-};
-
-onMounted(() => {
-})
-
-onBeforeUnmount(() => {
-  loading.value = false;
-})
-
-//暴露给父组件用
-defineExpose({
-  open,
-  close,
-  optionWindow
-})
-</script>
-<style lang="scss" scoped>
-.mask {
-  position: fixed;
-  height: 100vh;
-  width: 100vw;
-  top: 0;
-  left: 0;
-  background: rgba(0, 0, 0, 0.78);
-  z-index: 998;
-}
-
-.optionWindow {
-  position: fixed;
-  height: 100vh;
-  width: 100vw;
-  top: 0;
-  left: 0;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  color: #FFFFFF;
-  z-index: 999;
-
-  .box {
-    width: 50%;
-    border-radius: 1.6rem;
-    background: linear-gradient(46deg, #092941 -83%, #2A484B 95%);
-    box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.2);
-    position: relative;
-
-    .top {
-      color: #ffffff;
-      display: flex;
-      justify-content: flex-end;
-      position: relative;
-      z-index: 2;
-    }
-
-    .content {
-      max-height: 65vh;
-      margin-bottom: 2vh;
-      position: relative;
-      z-index: 2;
-
-      .content-title {
-        font-size: 1.65rem;
-        display: flex;
-        justify-content: center;
-      }
-
-      .content-title2 {
-        padding: 0 0 2.5vh 0;
-      }
-
-      .areaBox {
-        padding-bottom: 2vh;
-        text-align: center;
-
-        .testAreaChooseRoll {
-          width: 100%;
-          max-height: 30vh;
-          overflow-y: scroll;
-          display: flex;
-          flex-wrap: wrap;
-          justify-content: center;
-          padding: 0 20px 0 30px;
-          box-sizing: border-box;
-          margin-bottom: 5px;
-
-          .li {
-            border: 3px solid #979797;
-            color: #ffffff;
-            font-size: 1.6rem;
-            margin: 0 8px 16px 8px;
-            padding: 5px 8px;
-            min-width: 2.6rem;
-            border-radius: 4px;
-            font-weight: bold;
-            text-align: center;
-            cursor: pointer;
-          }
-
-          .select {
-            border: 3px solid #ffffff;
-            background: #ffffff;
-            color: #1A293A;
-          }
-
-          .ing {
-            border: 3px solid #ff0000;
-            color: #ff0000;
-          }
-
-          &::-webkit-scrollbar {
-            width: 10px;
-          }
-
-          &::-webkit-scrollbar-thumb {
-            border-width: 2px;
-            border-radius: 4px;
-            border-style: dashed;
-            border-color: transparent;
-            background-color: rgba(26, 62, 78, 0.9);
-            background-clip: padding-box;
-          }
-
-          &::-webkit-scrollbar-button:hover {
-            border-radius: 6px;
-            background: rgba(26, 62, 78, 1);
-          }
-
-        }
-
-        .allBtn {
-          display: inline;
-          margin: 0 auto;
-          cursor: pointer;
-        }
-      }
-
-      .standardBox {
-        padding: 2.5vh 0;
-
-        .standardBoxBtn {
-          display: flex;
-          width: 42%;
-          height: 3.6rem;
-          line-height: 3.6rem;
-          font-size: 1.65rem;
-          margin: 0 auto;
-          border-radius: 1.8rem;
-          font-size: 2rem;
-          text-align: center;
-          color: #1A293A;
-          background: #ACACAC;
-          overflow: hidden;
-
-          .li {
-            width: 50%;
-            text-align: center;
-            cursor: pointer;
-          }
-
-          .select {
-            border-radius: 1.8rem;
-            box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.3);
-            background: radial-gradient(50% 181% at 163% 0%, #35FFC6 0%, #00FFE8 100%);
-          }
-        }
-      }
-
-
-      .switchList {
-        padding: 2.5vh 0;
-        display: flex;
-        justify-content: center;
-
-        .li {
-          display: flex;
-          align-items: center;
-          margin: 0 2rem;
-
-          span {
-            font-size: 1.65rem;
-            margin-right: 8px;
-            flex-shrink: 0;
-          }
-
-          .el-select {
-            width: 100px;
-          }
-        }
-      }
-
-
-    }
-
-    .bottom {
-      height: 3.59rem;
-      padding: 2.5rem 0;
-      align-items: center;
-      color: #ffffff;
-      display: flex;
-      justify-content: space-around;
-      position: relative;
-      z-index: 2;
-
-      .btn {
-        width: 33%;
-        height: 3.59rem;
-        line-height: 3.59rem;
-        font-size: 2rem;
-        margin: 0 auto;
-        border-radius: 0.83rem;
-        font-size: 2rem;
-        text-align: center;
-        color: #1A293A;
-        background: radial-gradient(141% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
-        box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.3);
-        cursor: pointer;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-
-        .is-loading {
-          margin-right: 5px;
-        }
-      }
-    }
-
-    .boxBg {
-      width: 100%;
-      height: 100%;
-      display: block;
-      background-position: center center;
-      background-repeat: no-repeat;
-      background-size: contain;
-      position: absolute;
-      left: 0;
-      top: 0;
-      z-index: 1;
-      opacity: 0.1;
-    }
-  }
-
-  .close {
-    margin: 1.5rem 1.5rem 0 0;
-
-    &:hover {
-      transform: rotate(180deg);
-      background-color: rgba(216, 216, 216, 1);
-    }
-  }
-}
-</style>

+ 0 - 18
src/types/components.d.ts

@@ -7,32 +7,17 @@ export {}
 /* prettier-ignore */
 declare module 'vue' {
   export interface GlobalComponents {
-    AddItemWindow: typeof import('./../components/AddItemWindow/index.vue')['default']
     ChooseStudent: typeof import('./../components/ChooseStudent/index.vue')['default']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElButton: typeof import('element-plus/es')['ElButton']
-    ElCollapse: typeof import('element-plus/es')['ElCollapse']
-    ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
-    ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition']
-    ElContainer: typeof import('element-plus/es')['ElContainer']
-    ElDialog: typeof import('element-plus/es')['ElDialog']
-    ElForm: typeof import('element-plus/es')['ElForm']
-    ElFormItem: typeof import('element-plus/es')['ElFormItem']
-    ElHeader: typeof import('element-plus/es')['ElHeader']
     ElIcon: typeof import('element-plus/es')['ElIcon']
-    ElImage: typeof import('element-plus/es')['ElImage']
     ElInput: typeof import('element-plus/es')['ElInput']
-    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
-    ElLink: typeof import('element-plus/es')['ElLink']
-    ElMain: typeof import('element-plus/es')['ElMain']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
-    ElPopover: typeof import('element-plus/es')['ElPopover']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
-    ElUpload: typeof import('element-plus/es')['ElUpload']
     FaceWindow: typeof import('./../components/FaceWindow/index.vue')['default']
     Header: typeof import('./../components/Header/index.vue')['default']
     JumpRopeGame: typeof import('./../components/JumpRopeGame/index.vue')['default']
@@ -44,7 +29,4 @@ declare module 'vue' {
     RouterView: typeof import('vue-router')['RouterView']
     WorkstationWindow: typeof import('./../components/WorkstationWindow/index.vue')['default']
   }
-  export interface ComponentCustomProperties {
-    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
-  }
 }

+ 1 - 1
src/utils/index.ts

@@ -39,7 +39,7 @@ let utils = {
       newArr.push(data[key]);
       list.push(newArr);
     }
-    console.log("list",list)
+    // console.log("list",list)
     //重组数组
     let newList = [];
     let project: any = dataDictionary.project;

+ 10 - 10
src/views/gesture/index.vue

@@ -48,12 +48,12 @@ const data = reactive<any>({
   mySwiper: {},
   projectList: [],
   timerManager: {},
-  device_info: {},
+  deviceInfo: {},
   erweima: '',
   sid: '',
   key: 0
 });
-const { mySwiper, projectList, timerManager, device_info, erweima, sid, key } = toRefs(data);
+const { mySwiper, projectList, timerManager, deviceInfo, erweima, sid, key } = toRefs(data);
 
 /**
  * 清空定时任务
@@ -88,7 +88,7 @@ const getInitExam = () => {
  * 获取项目
  */
 const getExam = async () => {
-  let myList = device_info.value?.project_list || [];
+  let myList = deviceInfo.value?.project_list || [];
   let examList = myList.map((item: any) => {
     return item.exam_name;
   });
@@ -157,23 +157,23 @@ const confirm = (myProject?: any) => {
     proxy?.$modal.msgError('获取不到项目信息');
     return false;
   }
-  let obj = device_info.value.project_list.find((item: any) => {
+  let obj = deviceInfo.value.project_list.find((item: any) => {
     return item.exam_name == project.key;
   });
   if (obj == undefined) {
     proxy?.$modal.msgError('该项目没有配置参数');
     return false;
   }
-  let data = {
+  let dataObj = {
     gesture: obj.gesture ? true : false,
     demo: obj?.demo || 0,
     area: obj?.area_test_id || '',
     ctrl: obj?.area_ctrl_id || '',
     time: obj?.test_time || '',
     music: obj?.music_info?.url || '',
-    handcontroller: device_info.value.handcontroller_id
+    handcontroller: deviceInfo.value.handcontroller_id
   };
-  optionWindowRef.value.getGesture(project, data);
+  optionWindowRef.value.getGesture(project, dataObj);
 };
 
 /**
@@ -304,14 +304,14 @@ onBeforeMount(() => {
     }
     //接收设备信息
     if (e?.device_info) {
-      device_info.value = e.device_info;
-      let handcontroller_id = device_info.value.handcontroller_id;
+      deviceInfo.value = e.device_info;
+      let handcontroller_id = deviceInfo.value.handcontroller_id;
       stateHand(handcontroller_id);
       getInitExam();
     }
     //获取手势状态
     if (e?.cmd == 'get_handcontroller_state' && e?.state == 0) {
-      let handcontroller_id = device_info.value.handcontroller_id;
+      let handcontroller_id = deviceInfo.value.handcontroller_id;
       startHand(handcontroller_id);
     }
     //左滑动

+ 32 - 3
src/views/login/index.vue

@@ -13,6 +13,10 @@
               <input class="login-input login-input-password" type="password" autocomplete="off" placeholder="请输入密码"
                 v-model.trim="loginForm.password" />
             </div>
+            <div class="login-item">
+              <input class="login-input login-input-username" type="text" placeholder="请输入设备ID"
+                v-model.trim="loginForm.deviceid" />
+            </div>
             <div @click="getLogin" class="login-btn">
               <el-icon class="is-loading" v-if="loading">
                 <Loading />
@@ -27,7 +31,9 @@
 </template>
 
 <script setup name="Login" lang="ts">
+import CryptoJS from 'crypto-js';
 import useAppStore from '@/store/modules/app';
+
 const { proxy } = getCurrentInstance() as any;
 const router = useRouter();
 
@@ -46,7 +52,9 @@ const { loginForm, loading, key } = toRefs(data);
 const getLogin = () => {
   let username = loginForm.value.username;
   let password = loginForm.value.password;
+  let deviceid = loginForm.value.deviceid;
   if (!username || !password) {
+    key.value++;
     return false;
   }
   loading.value = true;
@@ -59,7 +67,15 @@ const getLogin = () => {
     if (res.access_token) {
       //保存token
       let token = res.access_token;
+      let passwordStr = CryptoJS.AES.encrypt(password, 'trops').toString();
       localStorage.setItem("token", token);
+      if(deviceid){
+        localStorage.setItem("deviceid", deviceid);
+      }else{
+        localStorage.removeItem('deviceid');
+      }
+      localStorage.setItem('username', username);
+      localStorage.setItem('password', passwordStr);
       router.push({ path: '/' });
       getUserInfo();
     }
@@ -89,9 +105,22 @@ const getUserInfo = () => {
 
 onMounted(() => {
   //测试环境默认密码
-  if (import.meta.env.DEV) {
-    loginForm.value.username = 'manage1';
-    loginForm.value.password = 'trops@2022';
+  // if (import.meta.env.DEV) {
+  //   loginForm.value.username = 'manage1';
+  //   loginForm.value.password = 'trops@2022';
+  // }
+  let deviceid = localStorage.getItem('deviceid');
+  let username = localStorage.getItem('username');
+  let password = localStorage.getItem('password');
+  if (deviceid) {
+    loginForm.value.deviceid = deviceid;
+  }
+  if (username) {
+    loginForm.value.username = username;
+  }
+  if (password) {
+    let myPassword = CryptoJS.AES.decrypt(password, 'trops').toString(CryptoJS.enc.Utf8);
+    loginForm.value.password = myPassword;
   }
 })
 </script>

+ 8 - 1
src/views/login/qrcode.vue

@@ -129,8 +129,15 @@ const getLogin = () => {
     .qrlogin(params)
     .then((res: any) => {
       if (res.access_token) {
+        //保存token
+        let token = res.access_token;
         let passwordStr = CryptoJS.AES.encrypt(password, 'trops').toString();
-        localStorage.setItem('deviceid', deviceid);
+        localStorage.setItem('token', token);
+        if (deviceid) {
+          localStorage.setItem('deviceid', deviceid);
+        } else {
+          localStorage.removeItem('deviceid');
+        }
         localStorage.setItem('username', username);
         localStorage.setItem('password', passwordStr);
       }

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

@@ -53,7 +53,9 @@ const getLogout = async () => {
   }).finally(() => {
   });
   //清空缓存
-  localStorage.clear();
+  //localStorage.clear();
+  localStorage.removeItem('token');
+  localStorage.removeItem('userInfo');
 };
 
 /**

+ 84 - 25
src/views/train/index.vue

@@ -2,14 +2,20 @@
   <div class="train">
     <Header @confirmExit="getExit" :showTool="false" closeClass="close2"></Header>
     <div class="menu" v-if="projectList.length" :class="projectList.length <= 12 ? 'menu1' : 'menu2'" :key="projectList.length">
-      <swiper :slides-per-view="6" :modules="[Grid]" :grid="{
+      <swiper
+        :slides-per-view="6"
+        :modules="[Grid]"
+        :grid="{
         fill: projectList.length <= 12 ? 'row' : 'column',
         rows: 2,
-      }" :space-between="20" :slides-per-group="12">
+      }"
+        :space-between="20"
+        :slides-per-group="12"
+      >
         <swiper-slide v-for="(item, index) in projectList " :key="index" @click="getOption(item)">
           <div class="li">
             <div>
-              <div class="pic"><img :src="'static/images/train/' + item.key + '.png'"></div>
+              <div class="pic"><img :src="'static/images/train/' + item.key + '.png'" /></div>
               <div class="name">
                 {{ item.name }}
               </div>
@@ -35,35 +41,72 @@ const optionWindowRef = ref();
 const data = reactive<any>({
   projectList: [],
   timerManager: {},
+  deviceInfo: {}
 });
-const { projectList, timerManager } = toRefs(data);
+const { projectList, timerManager, deviceInfo } = toRefs(data);
 
 /**
  * 获取项目
-*/
+ */
 const getExam = () => {
+  let deviceid = localStorage.getItem('deviceid');
+  let myList = deviceInfo.value?.project_list || [];
+  let examList = myList.map((item: any) => {
+    return item.exam_name;
+  });
   proxy?.$http.train.projectList().then((res: any) => {
     projectList.value = proxy?.$utils.getProject(res.exams).filter((item: any) => {
       //只显示能开的
-      return item.area.length > 0;
+      if (deviceid) {
+        return item.area.length > 0 && examList.includes(item.key);
+      } else {
+        return item.area.length > 0;
+      }
     });
   });
 };
 
 /**
  * 弹出选项窗口
-*/
+ */
 const getOption = (data: any) => {
-  optionWindowRef.value.open(data);
+  let deviceid = localStorage.getItem('deviceid');
+  if (deviceid) {
+    //设备快捷入口
+    let project = data;
+    if (project == undefined) {
+      proxy?.$modal.msgError('获取不到项目信息');
+      return false;
+    }
+    let obj = deviceInfo.value.project_list.find((item: any) => {
+      return item.exam_name == project.key;
+    });
+    if (obj == undefined) {
+      proxy?.$modal.msgError('该项目没有配置参数');
+      return false;
+    }
+    let dataObj = {
+      gesture: obj.gesture ? true : false,
+      demo: obj?.demo || 0,
+      area: obj?.area_test_id || '',
+      ctrl: obj?.area_ctrl_id || '',
+      time: obj?.test_time || '',
+      music: obj?.music_info?.url || '',
+    };
+    optionWindowRef.value.getGesture(project, dataObj);
+  } else {
+    //弹窗选项
+    optionWindowRef.value.open(data);
+  }
 };
 
 /**
  * 清空定时任务
-*/
+ */
 const getClearTimer = () => {
   for (let key in timerManager.value) {
     if (timerManager.value.hasOwnProperty(key)) {
-      clearInterval(timerManager.value[key])
+      clearInterval(timerManager.value[key]);
       timerManager.value[key] = null;
     }
   }
@@ -71,29 +114,47 @@ const getClearTimer = () => {
 
 /**
  * 初始化项目
-*/
+ */
 const getInitExam = () => {
   getExam();
   //定时刷新
   timerManager.value.exam = setInterval(() => {
     getExam();
-  }, 5000)
+  }, 5000);
 };
 
 /**
  * 退出
-*/
+ */
 const getExit = () => {
   router.go(-1);
 };
 
-onBeforeMount(() => {
+onBeforeMount(async () => {
+  let deviceid = localStorage.getItem('deviceid') || '';
+  if (deviceid) {
+    //如果有设备就先查设备配置信息
+    let params = {
+      search_info: deviceid,
+      page: 1,
+      per_page: 1
+    };
+    await proxy?.$http.common
+      .deviceInfo(params)
+      .then((res: any) => {
+        if (res.data.length) {
+          deviceInfo.value = res.data[0];
+        }
+      })
+      .catch(() => {})
+      .finally(() => {});
+  }
   getInitExam();
-})
+});
 
 onBeforeUnmount(() => {
   getClearTimer();
-})
+});
 </script>
 
 <style lang="scss" scoped>
@@ -125,8 +186,8 @@ $waiPadding: 6.51rem;
       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;
+      background: radial-gradient(78% 78% at 53% 50%, #07121a 0%, #2a4256 49%, #5180a9 100%);
+      box-shadow: 0px 0px 2px 2px #ffffff;
       margin-bottom: 2.5vh;
       overflow: hidden;
       display: flex;
@@ -144,7 +205,7 @@ $waiPadding: 6.51rem;
     .name {
       width: 100%;
       font-size: 2.48rem;
-      color: #1A293A;
+      color: #1a293a;
       text-align: center;
     }
 
@@ -166,7 +227,7 @@ $waiPadding: 6.51rem;
     margin-bottom: 20px;
 
     .li {
-      background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
+      background: radial-gradient(96% 96% at 2% 32%, #ffffff 0%, #fcfdfd 54%, #e1e4e7 100%);
     }
 
     &:nth-child(2),
@@ -176,7 +237,7 @@ $waiPadding: 6.51rem;
     &:nth-child(9),
     &:nth-child(11) {
       .li {
-        background: radial-gradient(167% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
+        background: radial-gradient(167% 126% at 97% 6%, #35ffc6 0%, #00ffe8 100%);
       }
     }
   }
@@ -186,7 +247,6 @@ $waiPadding: 6.51rem;
   display: flex;
 
   .swiper-slide {
-
     &:nth-child(1),
     &:nth-child(4),
     &:nth-child(5),
@@ -210,7 +270,7 @@ $waiPadding: 6.51rem;
     &:nth-child(41),
     &:nth-child(44) {
       .li {
-        background: radial-gradient(96% 96% at 2% 32%, #FFFFFF 0%, #FCFDFD 54%, #E1E4E7 100%);
+        background: radial-gradient(96% 96% at 2% 32%, #ffffff 0%, #fcfdfd 54%, #e1e4e7 100%);
       }
     }
 
@@ -237,7 +297,7 @@ $waiPadding: 6.51rem;
     &:nth-child(42),
     &:nth-child(43) {
       .li {
-        background: radial-gradient(167% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
+        background: radial-gradient(167% 126% at 97% 6%, #35ffc6 0%, #00ffe8 100%);
       }
     }
   }
@@ -251,7 +311,6 @@ $waiPadding: 6.51rem;
 
 @media screen and (max-width: 1450px) {
   .menu {
-
     .li {
       .name {
         font-size: 1.8rem;