index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. <template>
  2. <div>
  3. <Transition :enter-active-class="proxy?.animate.mask.enter" :leave-active-class="proxy?.animate.mask.leave">
  4. <div class="mask" v-if="optionWindow.show"></div>
  5. </Transition>
  6. <Transition :enter-active-class="proxy?.animate.dialog.enter" :leave-active-class="proxy?.animate.dialog.leave">
  7. <div class="optionWindow" v-if="optionWindow.show">
  8. <div class="box">
  9. <div class="top">
  10. <div class="close" @click="close"></div>
  11. </div>
  12. <div class="content">
  13. <div class="areaBox">
  14. <div class="content-title content-title2">
  15. {{ project.key !== 'skiprope' ? '测试区域' : '设备组' }}
  16. </div>
  17. <div class="testAreaChooseRoll">
  18. <div class="li" v-for="(item, index) in areaList"
  19. :class="{ 'select': chooseArea.includes(item.key), 'ing': item.value != '0' }" :key="index"
  20. @click="getChooseArea(item)">
  21. <div>{{ item.name }}</div>
  22. </div>
  23. </div>
  24. <div @click="getAllArea" class="allBtn" :class="{ 'active': chooseAllState }">{{ chooseAllState ? '重 置' :
  25. '全 选' }}</div>
  26. </div>
  27. <!-- <div class="standardBox">
  28. <div class="content-title content-title2">评分标准</div>
  29. <div class="standardBoxBtn">
  30. <div v-for="(item, index) in standardList" :class="{ 'select': optionForm.standard == item.value }"
  31. :key="index" class="li" @click="getChooseStandard(item)">
  32. <div>{{ item.label }}</div>
  33. </div>
  34. </div>
  35. </div> -->
  36. <div class="switchList">
  37. <div class="li">
  38. <span>举右手识别</span>
  39. <el-switch v-model="optionForm.gesture" :active-value="true" :inactive-value="false"
  40. style="--el-switch-on-color: #08FFA9; --el-switch-off-color: #ACACAC" />
  41. </div>
  42. <div class="li" v-if="project.key.slice(0, 3) != 'run'">
  43. <span>体验模式</span>
  44. <el-switch v-model="optionForm.demo" :active-value="1" :inactive-value="0"
  45. style="--el-switch-on-color: #08FFA9; --el-switch-off-color: #ACACAC" />
  46. </div>
  47. <div class="li" v-if="['skiprope'].includes(project.key)">
  48. <span>接收心率</span>
  49. <el-switch v-model="optionForm.hasHB" :active-value="true" :inactive-value="false"
  50. style="--el-switch-on-color: #08FFA9; --el-switch-off-color: #ACACAC" />
  51. </div>
  52. </div>
  53. <div v-if="['heartbeat'].includes(project.key)">
  54. <div>设置运动量目标</div>
  55. <div>
  56. 平均心率:
  57. <div style="display: flex;">
  58. <el-input v-model="optionForm.standHBL" />
  59. ~
  60. <el-input v-model="optionForm.standHBH" />
  61. </div>
  62. </div>
  63. <div>
  64. 预警心率:
  65. <div>
  66. <el-input v-model="optionForm.highHB" size="small" />
  67. </div>
  68. </div>
  69. </div>
  70. <div class="switchList">
  71. <div class="li" v-if="['jumprope', 'jumpingjack', 'situp'].includes(project.key)">
  72. <span>时长</span>
  73. <el-select v-model="optionForm.time" :popper-append-to-body="false" placeholder="请选择">
  74. <el-option v-for="item in timeList" :key="item.value" :label="item.label" :value="item.value" />
  75. </el-select>
  76. </div>
  77. <div class="li" v-if="['jumprope', 'jumpingjack', 'situp'].includes(project.key)">
  78. <span>音乐</span>
  79. <el-select v-model="optionForm.music" :popper-append-to-body="false" placeholder="请选择" clearable>
  80. <el-option v-for="item in musicList" :key="item.id" :label="item.name" :value="item.url" />
  81. </el-select>
  82. </div>
  83. </div>
  84. </div>
  85. <div class="bottom">
  86. <div class="btn" @click="confirm">
  87. <el-icon class="is-loading" v-if="loading">
  88. <Loading />
  89. </el-icon>
  90. <div>确 定</div>
  91. </div>
  92. </div>
  93. <div class="boxBg" :style="{ backgroundImage: 'url(static/images/train/' + project.key + '.png)' }">
  94. </div>
  95. </div>
  96. </div>
  97. </Transition>
  98. </div>
  99. </template>
  100. <script setup lang="ts">
  101. import useAppStore from '@/store/modules/app';
  102. const { proxy } = getCurrentInstance() as any;
  103. const router = useRouter();
  104. //父值
  105. const props = defineProps({
  106. projectList: {
  107. type: Array as any,
  108. default: () => []
  109. },
  110. parentData: {
  111. type: Object,
  112. default: {}
  113. },
  114. });
  115. //评分标准,数据字典无需双向绑定
  116. const standardList = [{ label: '考试', value: 0 }, { label: '体测', value: 1 }]
  117. //时长选择,数据字典无需双向绑定
  118. const timeList =
  119. [{
  120. label: '10秒',
  121. value: 10
  122. }, {
  123. label: '20秒',
  124. value: 20
  125. }, {
  126. label: '30秒',
  127. value: 30
  128. }, {
  129. label: '1分钟',
  130. value: 60
  131. }, {
  132. label: '2分钟',
  133. value: 120
  134. }, {
  135. label: '3分钟',
  136. value: 180
  137. }, {
  138. label: '5分钟',
  139. value: 300
  140. }];
  141. const data = reactive<any>({
  142. optionForm: {
  143. gesture: true,
  144. standard: 0
  145. },
  146. optionWindow: {
  147. show: false,
  148. time: "",
  149. },
  150. project: {},
  151. musicList: [],
  152. classList: [],
  153. chooseArea: [],
  154. chooseAllState: false,
  155. loading: false,
  156. });
  157. const { optionForm, optionWindow, project, musicList, classList, chooseArea, chooseAllState, loading } = toRefs(data);
  158. //打开
  159. const open = async (data: any) => {
  160. console.log("data", data);
  161. await getClass();
  162. await getMusic();
  163. project.value = data;
  164. chooseArea.value = [];
  165. optionWindow.value.show = true;
  166. };
  167. //关闭
  168. const close = () => {
  169. optionWindow.value.show = false;
  170. };
  171. //筛选测试区
  172. const areaList = computed(() => {
  173. let area = [];
  174. let list = props.projectList.filter((item: any) => {
  175. return item.key == project.value.key;
  176. })
  177. if (list.length > 0) {
  178. area = list[0].area;
  179. }
  180. //console.log("area", area)
  181. return area;
  182. });
  183. //获取音乐
  184. const getMusic = async () => {
  185. const list: any = useAppStore().getMusic();
  186. if (list.length) {
  187. musicList.value = list;
  188. } else {
  189. await proxy?.$http.train.musicList().then((res: any) => {
  190. if (res.data.length > 0) {
  191. let myList: any = res.data;
  192. musicList.value = myList;
  193. useAppStore().setMusic(myList);
  194. }
  195. });
  196. }
  197. };
  198. //获取班级列表
  199. const getClass = async () => {
  200. const list: any = useAppStore().getClass();
  201. if (list.length) {
  202. classList.value = list;
  203. } else {
  204. let params = {
  205. page: 1,
  206. per_page: 9999,
  207. };
  208. await proxy?.$http.common.classList(params).then((res: any) => {
  209. if (res.data.length > 0) {
  210. let myList: any = res.data;
  211. classList.value = myList;
  212. useAppStore().setClass(myList);
  213. }
  214. });
  215. }
  216. };
  217. //选择测试区
  218. const getChooseArea = (data: any) => {
  219. if (chooseArea.value.length > 10) {
  220. optionForm.value.gesture = false;
  221. } else {
  222. optionForm.value.gesture = true;
  223. }
  224. let id = data.key;
  225. let inData = chooseArea.value.includes(id);
  226. if (inData) {
  227. //已存在就移除
  228. chooseArea.value = chooseArea.value.filter((item: any) => {
  229. return item != id
  230. })
  231. } else {
  232. //不存在就点选
  233. chooseArea.value.push(id);
  234. }
  235. };
  236. //选择测试标准
  237. const getChooseStandard = (data: any) => {
  238. optionForm.value.standard = data.value;
  239. };
  240. //全选或全取消测试区
  241. const getAllArea = (data: any) => {
  242. chooseAllState.value = !chooseAllState.value;
  243. if (chooseAllState.value) {
  244. //全选
  245. chooseArea.value = areaList.value.filter((item: any) => {
  246. return item.value == 0;
  247. }).map((item: any) => {
  248. return item.key;
  249. })
  250. } else {
  251. //全取消
  252. chooseArea.value = [];
  253. }
  254. };
  255. //确定
  256. const confirm = () => {
  257. console.log("project.value", project.value)
  258. optionForm.value.project = project.value.key;
  259. if (props.parentData.id) {
  260. optionForm.value.classes = props.parentData.class_ids.join();
  261. } else {
  262. optionForm.value.classes = classList.value.map((item: any) => { return item.id; }).join();
  263. }
  264. optionForm.value.area = chooseArea.value.join();
  265. console.log("optionForm", optionForm.value);
  266. if (!optionForm.value.classes) {
  267. getClass();
  268. let message = "没有班级,将重新加载班级请重新操作";
  269. ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
  270. return false;
  271. }
  272. if (!optionForm.value.area) {
  273. let message = "请选择测试区";
  274. ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
  275. return false;
  276. }
  277. // if (optionForm.value.standard == null) {
  278. // let message = "请选择评分标准";
  279. // ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
  280. // return false;
  281. // }
  282. if (props.parentData.id) {
  283. optionForm.value.taskId = props.parentData.id;
  284. optionForm.value.standard = props.parentData.exam_type; //0考试 1体测
  285. optionForm.value.taskCate = props.parentData.exam_type ? 1 : 0; //0自由 1常规
  286. }
  287. loading.value = true;
  288. if (chooseArea.value.length > 1) {
  289. //多区域
  290. router.push({ path: '/train/multiple', query: optionForm.value });
  291. }
  292. else if (["run50", "run70", "run100", "run200", "run400", "run800", "run1000", "run15x4", "run10x4", "run50x8"].includes(project.value.key)) {
  293. //跑步项目
  294. router.push({ path: '/train/run', query: optionForm.value });
  295. } else {
  296. //单项
  297. router.push({ path: '/train/test', query: optionForm.value });
  298. }
  299. };
  300. //手势赋值跳转
  301. const getGesture = async (myProject:any, data:any) => {
  302. await getClass();
  303. project.value = myProject;
  304. chooseArea.value = data.area.split(",");
  305. optionForm.value = data;
  306. confirm();
  307. }
  308. onMounted(() => {
  309. })
  310. onBeforeUnmount(() => {
  311. loading.value = false;
  312. })
  313. //暴露给父组件用
  314. defineExpose({
  315. open,
  316. close,
  317. optionWindow,
  318. getGesture
  319. })
  320. </script>
  321. <style lang="scss" scoped>
  322. .mask {
  323. position: fixed;
  324. height: 100vh;
  325. width: 100vw;
  326. top: 0;
  327. left: 0;
  328. background: rgba(0, 0, 0, 0.78);
  329. z-index: 998;
  330. }
  331. .optionWindow {
  332. position: fixed;
  333. height: 100vh;
  334. width: 100vw;
  335. top: 0;
  336. left: 0;
  337. display: flex;
  338. justify-content: center;
  339. align-items: center;
  340. color: #FFFFFF;
  341. z-index: 999;
  342. .box {
  343. width: 50%;
  344. border-radius: 1.6rem;
  345. background: linear-gradient(46deg, #092941 -83%, #2A484B 95%);
  346. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.2);
  347. position: relative;
  348. .top {
  349. color: #ffffff;
  350. display: flex;
  351. justify-content: flex-end;
  352. position: relative;
  353. z-index: 2;
  354. }
  355. .content {
  356. max-height: 65vh;
  357. margin-bottom: 2vh;
  358. position: relative;
  359. z-index: 2;
  360. .content-title {
  361. font-size: 1.65rem;
  362. display: flex;
  363. justify-content: center;
  364. }
  365. .content-title2 {
  366. padding: 0 0 2.5vh 0;
  367. }
  368. .areaBox {
  369. padding-bottom: 2vh;
  370. text-align: center;
  371. .testAreaChooseRoll {
  372. width: 100%;
  373. max-height: 30vh;
  374. overflow-y: scroll;
  375. display: flex;
  376. flex-wrap: wrap;
  377. justify-content: center;
  378. padding: 0 20px 0 30px;
  379. box-sizing: border-box;
  380. margin-bottom: 5px;
  381. .li {
  382. border: 3px solid #979797;
  383. color: #ffffff;
  384. font-size: 1.6rem;
  385. margin: 0 8px 16px 8px;
  386. padding: 5px 8px;
  387. min-width: 2.6rem;
  388. border-radius: 4px;
  389. font-weight: bold;
  390. text-align: center;
  391. cursor: pointer;
  392. }
  393. .select {
  394. border: 3px solid #ffffff;
  395. background: #ffffff;
  396. color: #1A293A;
  397. }
  398. .ing {
  399. border: 3px solid #ff0000;
  400. color: #ff0000;
  401. }
  402. &::-webkit-scrollbar {
  403. width: 10px;
  404. }
  405. &::-webkit-scrollbar-thumb {
  406. border-width: 2px;
  407. border-radius: 4px;
  408. border-style: dashed;
  409. border-color: transparent;
  410. background-color: rgba(26, 62, 78, 0.9);
  411. background-clip: padding-box;
  412. }
  413. &::-webkit-scrollbar-button:hover {
  414. border-radius: 6px;
  415. background: rgba(26, 62, 78, 1);
  416. }
  417. }
  418. .allBtn {
  419. display: inline;
  420. margin: 0 auto;
  421. cursor: pointer;
  422. }
  423. }
  424. .standardBox {
  425. padding: 2.5vh 0;
  426. .standardBoxBtn {
  427. display: flex;
  428. width: 42%;
  429. height: 3.6rem;
  430. line-height: 3.6rem;
  431. font-size: 1.65rem;
  432. margin: 0 auto;
  433. border-radius: 1.8rem;
  434. font-size: 2rem;
  435. text-align: center;
  436. color: #1A293A;
  437. background: #ACACAC;
  438. overflow: hidden;
  439. .li {
  440. width: 50%;
  441. text-align: center;
  442. cursor: pointer;
  443. }
  444. .select {
  445. border-radius: 1.8rem;
  446. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  447. background: radial-gradient(50% 181% at 163% 0%, #35FFC6 0%, #00FFE8 100%);
  448. }
  449. }
  450. }
  451. .switchList {
  452. padding: 2.5vh 0;
  453. display: flex;
  454. justify-content: center;
  455. .li {
  456. display: flex;
  457. align-items: center;
  458. margin: 0 2rem;
  459. span {
  460. font-size: 1.65rem;
  461. margin-right: 8px;
  462. flex-shrink: 0;
  463. }
  464. .el-select {
  465. width: 100px;
  466. }
  467. }
  468. }
  469. }
  470. .bottom {
  471. height: 3.59rem;
  472. padding: 2.5rem 0;
  473. align-items: center;
  474. color: #ffffff;
  475. display: flex;
  476. justify-content: space-around;
  477. position: relative;
  478. z-index: 2;
  479. .btn {
  480. width: 33%;
  481. height: 3.59rem;
  482. line-height: 3.59rem;
  483. font-size: 2rem;
  484. margin: 0 auto;
  485. border-radius: 0.83rem;
  486. font-size: 2rem;
  487. text-align: center;
  488. color: #1A293A;
  489. background: radial-gradient(141% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
  490. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  491. cursor: pointer;
  492. display: flex;
  493. align-items: center;
  494. justify-content: center;
  495. .is-loading {
  496. margin-right: 5px;
  497. }
  498. }
  499. }
  500. .boxBg {
  501. width: 100%;
  502. height: 100%;
  503. display: block;
  504. background-position: center center;
  505. background-repeat: no-repeat;
  506. background-size: contain;
  507. position: absolute;
  508. left: 0;
  509. top: 0;
  510. z-index: 1;
  511. opacity: 0.1;
  512. }
  513. }
  514. .close {
  515. margin: 1.5rem 1.5rem 0 0;
  516. }
  517. }
  518. </style>