index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  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">
  43. <span>体验模式</span>
  44. <el-switch v-model="optionForm.experience" :active-value="true" :inactive-value="false"
  45. style="--el-switch-on-color: #08FFA9; --el-switch-off-color: #ACACAC" />
  46. </div>
  47. <div class="li" v-if="['skiprope'].includes(optionForm.project)">
  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(optionForm.project)">
  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">
  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">
  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. });
  111. //评分标准,数据字典无需双向绑定
  112. const standardList = [{ label: '考试', value: 0 }, { label: '体测', value: 1 }]
  113. //时长选择,数据字典无需双向绑定
  114. const timeList =
  115. [{
  116. label: '10秒',
  117. value: 10
  118. }, {
  119. label: '20秒',
  120. value: 20
  121. }, {
  122. label: '30秒',
  123. value: 30
  124. }, {
  125. label: '1分钟',
  126. value: 60
  127. }, {
  128. label: '2分钟',
  129. value: 120
  130. }, {
  131. label: '3分钟',
  132. value: 180
  133. }, {
  134. label: '5分钟',
  135. value: 300
  136. }];
  137. const data = reactive<any>({
  138. optionForm: {
  139. gesture: true,
  140. standard: 0
  141. },
  142. optionWindow: {
  143. show: false,
  144. time: "",
  145. },
  146. project: {},
  147. musicList: [],
  148. classList: [],
  149. chooseArea: [],
  150. chooseAllState: false,
  151. loading: false,
  152. });
  153. const { optionForm, optionWindow, project, musicList, classList, chooseArea, chooseAllState, loading } = toRefs(data);
  154. //打开
  155. const open = (data: any) => {
  156. console.log("data", data);
  157. getClass();
  158. getMusic();
  159. project.value = data;
  160. chooseArea.value = [];
  161. optionWindow.value.show = true;
  162. };
  163. //关闭
  164. const close = () => {
  165. optionWindow.value.show = false;
  166. };
  167. //筛选测试区
  168. const areaList = computed(() => {
  169. let area = [];
  170. let list = props.projectList.filter((item: any) => {
  171. return item.key == project.value.key;
  172. })
  173. if (list.length > 0) {
  174. area = list[0].area;
  175. }
  176. //console.log("area", area)
  177. return area;
  178. });
  179. //获取音乐
  180. const getMusic = () => {
  181. const list: any = useAppStore().getMusic();
  182. if (list.length) {
  183. musicList.value = list;
  184. } else {
  185. proxy?.$http.train.musicList().then((res: any) => {
  186. if (res.data.length > 0) {
  187. let myList: any = res.data;
  188. musicList.value = myList;
  189. useAppStore().setMusic(myList);
  190. }
  191. });
  192. }
  193. };
  194. //获取班级列表
  195. const getClass = () => {
  196. const list: any = useAppStore().getClass();
  197. if (list.length) {
  198. classList.value = list;
  199. } else {
  200. let params = {
  201. page: 1,
  202. per_page: 9999,
  203. };
  204. proxy?.$http.common.classList(params).then((res: any) => {
  205. if (res.data.length > 0) {
  206. let myList: any = res.data;
  207. classList.value = myList;
  208. useAppStore().setClass(myList);
  209. }
  210. });
  211. }
  212. };
  213. //选择测试区
  214. const getChooseArea = (data: any) => {
  215. if (chooseArea.value.length > 10) {
  216. optionForm.value.gesture = false;
  217. } else {
  218. optionForm.value.gesture = true;
  219. }
  220. let id = data.key;
  221. let inData = chooseArea.value.includes(id);
  222. if (inData) {
  223. //已存在就移除
  224. chooseArea.value = chooseArea.value.filter((item: any) => {
  225. return item != id
  226. })
  227. } else {
  228. //不存在就点选
  229. chooseArea.value.push(id);
  230. }
  231. };
  232. //选择测试标准
  233. const getChooseStandard = (data: any) => {
  234. optionForm.value.standard = data.value;
  235. };
  236. //全选或全取消测试区
  237. const getAllArea = (data: any) => {
  238. chooseAllState.value = !chooseAllState.value;
  239. if (chooseAllState.value) {
  240. //全选
  241. chooseArea.value = areaList.value.filter((item: any) => {
  242. return item.value == 0;
  243. }).map((item: any) => {
  244. return item.key;
  245. })
  246. } else {
  247. //全取消
  248. chooseArea.value = [];
  249. }
  250. };
  251. //确定
  252. const confirm = (data: any) => {
  253. optionForm.value.project = project.value.key;
  254. optionForm.value.classes = classList.value.map((item: any) => { return item.id; }).join();
  255. optionForm.value.area = chooseArea.value.join();
  256. console.log("optionForm", optionForm.value);
  257. if (!optionForm.value.classes) {
  258. getClass();
  259. let message = "没有班级,将重新加载班级请重新操作";
  260. ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
  261. return false;
  262. }
  263. if (!optionForm.value.area) {
  264. let message = "请选择测试区";
  265. ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
  266. return false;
  267. }
  268. if (optionForm.value.standard == null) {
  269. let message = "请选择评分标准";
  270. ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
  271. return false;
  272. }
  273. loading.value = true;
  274. if (chooseArea.value.length > 1) {
  275. //多区域
  276. router.push({ path: '/train/multiple', query: optionForm.value });
  277. }
  278. else if (["run50", "run70", "run100", "run200", "run400", "run800", "run1000", "run15x4", "run10x4", "run50x8"].includes(project.value.key)) {
  279. //跑步项目
  280. router.push({ path: '/train/run', query: optionForm.value });
  281. } else {
  282. //单项
  283. router.push({ path: '/train/test', query: optionForm.value });
  284. }
  285. };
  286. onMounted(() => {
  287. })
  288. onUnmounted(() => {
  289. loading.value = false;
  290. })
  291. //暴露给父组件用
  292. defineExpose({
  293. open,
  294. close,
  295. optionWindow
  296. })
  297. </script>
  298. <style lang="scss" scoped>
  299. .mask {
  300. position: fixed;
  301. height: 100vh;
  302. width: 100vw;
  303. top: 0;
  304. left: 0;
  305. background: rgba(0, 0, 0, 0.78);
  306. z-index: 998;
  307. }
  308. .optionWindow {
  309. position: fixed;
  310. height: 100vh;
  311. width: 100vw;
  312. top: 0;
  313. left: 0;
  314. display: flex;
  315. justify-content: center;
  316. align-items: center;
  317. color: #FFFFFF;
  318. z-index: 999;
  319. .box {
  320. width: 50%;
  321. border-radius: 1.6rem;
  322. background: linear-gradient(46deg, #092941 -83%, #2A484B 95%);
  323. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.2);
  324. position: relative;
  325. .top {
  326. color: #ffffff;
  327. display: flex;
  328. justify-content: flex-end;
  329. position: relative;
  330. z-index: 2;
  331. }
  332. .content {
  333. max-height: 65vh;
  334. margin-bottom: 2vh;
  335. position: relative;
  336. z-index: 2;
  337. .content-title {
  338. font-size: 1.65rem;
  339. display: flex;
  340. justify-content: center;
  341. }
  342. .content-title2 {
  343. padding: 0 0 2.5vh 0;
  344. }
  345. .areaBox {
  346. padding-bottom: 2vh;
  347. text-align: center;
  348. .testAreaChooseRoll {
  349. width: 100%;
  350. max-height: 17vh;
  351. overflow-y: scroll;
  352. display: flex;
  353. flex-wrap: wrap;
  354. justify-content: center;
  355. padding: 0 20px 0 30px;
  356. box-sizing: border-box;
  357. margin-bottom: 5px;
  358. .li {
  359. border: 2px solid #979797;
  360. color: #ffffff;
  361. font-size: 1.2rem;
  362. margin: 0 6px 12px 6px;
  363. padding: 5px 8px;
  364. min-width: 2.5rem;
  365. border-radius: 4px;
  366. font-weight: bold;
  367. opacity: 0.7;
  368. text-align: center;
  369. cursor: pointer;
  370. }
  371. .select {
  372. border: 2px solid #ffffff;
  373. color: #ffffff;
  374. opacity: 1;
  375. }
  376. .ing {
  377. border: 2px solid #ff0000;
  378. color: #ff0000;
  379. opacity: 1;
  380. }
  381. &::-webkit-scrollbar {
  382. width: 10px;
  383. }
  384. &::-webkit-scrollbar-thumb {
  385. border-width: 2px;
  386. border-radius: 4px;
  387. border-style: dashed;
  388. border-color: transparent;
  389. background-color: rgba(26, 62, 78, 0.9);
  390. background-clip: padding-box;
  391. }
  392. &::-webkit-scrollbar-button:hover {
  393. border-radius: 6px;
  394. background: rgba(26, 62, 78, 1);
  395. }
  396. }
  397. .allBtn {
  398. display: inline;
  399. margin: 0 auto;
  400. cursor: pointer;
  401. }
  402. }
  403. .standardBox {
  404. padding: 2.5vh 0;
  405. .standardBoxBtn {
  406. display: flex;
  407. width: 42%;
  408. height: 3.6rem;
  409. line-height: 3.6rem;
  410. font-size: 1.65rem;
  411. margin: 0 auto;
  412. border-radius: 1.8rem;
  413. font-size: 2rem;
  414. text-align: center;
  415. color: #1A293A;
  416. background: #ACACAC;
  417. overflow: hidden;
  418. .li {
  419. width: 50%;
  420. text-align: center;
  421. cursor: pointer;
  422. }
  423. .select {
  424. border-radius: 1.8rem;
  425. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  426. background: radial-gradient(50% 181% at 163% 0%, #35FFC6 0%, #00FFE8 100%);
  427. }
  428. }
  429. }
  430. .switchList {
  431. padding: 2.5vh 0;
  432. display: flex;
  433. justify-content: center;
  434. .li {
  435. display: flex;
  436. align-items: center;
  437. margin: 0 2rem;
  438. span {
  439. font-size: 1.65rem;
  440. margin-right: 8px;
  441. flex-shrink: 0;
  442. }
  443. .el-select {
  444. width: 100px;
  445. }
  446. }
  447. }
  448. }
  449. .bottom {
  450. height: 3.59rem;
  451. padding: 2.5rem 0;
  452. align-items: center;
  453. color: #ffffff;
  454. display: flex;
  455. justify-content: space-around;
  456. position: relative;
  457. z-index: 2;
  458. .btn {
  459. width: 33%;
  460. height: 3.59rem;
  461. line-height: 3.59rem;
  462. font-size: 2rem;
  463. margin: 0 auto;
  464. border-radius: 0.83rem;
  465. font-size: 2rem;
  466. text-align: center;
  467. color: #1A293A;
  468. background: radial-gradient(141% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
  469. box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
  470. cursor: pointer;
  471. display: flex;
  472. align-items: center;
  473. justify-content: center;
  474. .is-loading {
  475. margin-right: 5px;
  476. }
  477. }
  478. }
  479. .boxBg {
  480. width: 100%;
  481. height: 100%;
  482. display: block;
  483. background-position: center center;
  484. background-repeat: no-repeat;
  485. background-size: contain;
  486. position: absolute;
  487. left: 0;
  488. top: 0;
  489. z-index: 1;
  490. opacity: 0.1;
  491. }
  492. }
  493. .close {
  494. width: 3.2rem;
  495. height: 3.2rem;
  496. margin: 1.5rem 1.5rem 0 0;
  497. box-sizing: border-box;
  498. border: 1px solid #979797;
  499. background-image: url("@/assets/images/common/close.png");
  500. background-position: center;
  501. background-repeat: no-repeat;
  502. background-size: 45% 45%;
  503. background-color: rgba(216, 216, 216, 0.8);
  504. border-radius: 50%;
  505. transition: all 0.5s;
  506. cursor: pointer;
  507. &:hover {
  508. transform: rotate(180deg);
  509. background-color: rgba(216, 216, 216, 1);
  510. }
  511. }
  512. }
  513. </style>