index.tsx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import { defineComponent, onMounted, reactive, ref } from 'vue';
  2. import styles from './index.module.less';
  3. import { postMessage } from '@/helpers/native-message';
  4. import icon_title from './images/icon-title.png';
  5. import icon_back from './images/icon-back.png';
  6. import icon_setting from './images/icon-setting.png';
  7. import iconPlay from './images/icon-play.png';
  8. import iconPause from './images/icon-pause.png';
  9. import beat from './images/btn-2.png';
  10. import tempo from './images/btn-3.png';
  11. import randDom from './images/btn-1.png';
  12. import iconPlus from './images/icon-plus.png';
  13. import iconAdd from './images/icon-add.png';
  14. import { getImage } from './images/music';
  15. import '@vant/touch-emulator';
  16. // import j2 from './images/music/j-2.png';
  17. import { Popover, Popup, showToast } from 'vant';
  18. import SettingModal from './setting-modal';
  19. import {
  20. randomScoreElement,
  21. renderScore,
  22. setting,
  23. elementDirection
  24. } from './setting';
  25. import { handleStartTick, hendleEndTick } from './tick';
  26. import { handleStartBeat, hendleEndBeat } from './beat-tick';
  27. import { browser } from '@/helpers/utils';
  28. import { useRoute } from 'vue-router';
  29. export default defineComponent({
  30. name: 'tempo-practice',
  31. setup() {
  32. const route = useRoute();
  33. const state = reactive({
  34. platform: route.query.platform, // microapp 老师端应用里面打开单独处理返回逻辑
  35. win: route.query.win,
  36. settingStatus: false,
  37. speedList: [
  38. { text: '40', value: 40, color: '#060606' },
  39. { text: '50', value: 50, color: '#060606' },
  40. { text: '60', value: 60, color: '#060606' },
  41. { text: '70', value: 70, color: '#060606' },
  42. { text: '80', value: 80, color: '#060606' },
  43. { text: '90', value: 90, color: '#060606' },
  44. { text: '100', value: 100, color: '#060606' },
  45. { text: '110', value: 110, color: '#060606' },
  46. { text: '120', value: 120, color: '#060606' },
  47. { text: '130', value: 130, color: '#060606' },
  48. { text: '140', value: 140, color: '#060606' },
  49. { text: '150', value: 150, color: '#060606' },
  50. { text: '160', value: 160, color: '#060606' },
  51. { text: '170', value: 170, color: '#060606' },
  52. { text: '180', value: 180, color: '#060606' },
  53. { text: '190', value: 190, color: '#060606' },
  54. { text: '200', value: 200, color: '#060606' }
  55. ]
  56. });
  57. // 返回
  58. const goback = () => {
  59. if (state.platform === 'microapp') {
  60. window.parent.postMessage(
  61. {
  62. api: 'iframe_exit'
  63. },
  64. '*'
  65. );
  66. return;
  67. }
  68. if (!browser().isApp) {
  69. window.close();
  70. return;
  71. }
  72. postMessage({ api: 'goBack' });
  73. };
  74. /** 播放切换 */
  75. const handlePlay = async () => {
  76. if (setting.playState === 'pause') {
  77. setting.playState = 'play';
  78. if (setting.playType === 'beat') {
  79. await handleStartTick();
  80. } else {
  81. await handleStartBeat();
  82. }
  83. } else {
  84. handleStop();
  85. }
  86. };
  87. /** 播放类型 */
  88. const handlePlayType = () => {
  89. handleStop();
  90. if (setting.playType === 'beat') {
  91. setting.playType = 'tempo';
  92. } else {
  93. setting.playType = 'beat';
  94. }
  95. };
  96. const handleStop = () => {
  97. setting.playState = 'pause';
  98. if (setting.playType === 'beat') {
  99. hendleEndTick();
  100. } else {
  101. hendleEndBeat();
  102. }
  103. };
  104. onMounted(() => {
  105. state.speedList.forEach((item: any) => {
  106. if (item.value === setting.speed) item.color = '#1CACF1';
  107. });
  108. renderScore();
  109. });
  110. return () => (
  111. <div class={[styles.tempoPractice, state.win === 'pc' ? styles.pc : '']}>
  112. <div class={styles.head}>
  113. <div class={styles.back} onClick={goback}>
  114. <img src={icon_back} />
  115. </div>
  116. <div class={styles.title}>
  117. <img src={icon_title} />
  118. </div>
  119. <div
  120. class={styles.back}
  121. onClick={() => {
  122. handleStop();
  123. state.settingStatus = true;
  124. }}>
  125. <img src={icon_setting} />
  126. </div>
  127. </div>
  128. <div class={styles.conCon}>
  129. <div class={styles.container}>
  130. {setting.scorePart.map((item: any, i: number) => (
  131. <div
  132. class={[
  133. styles.beatSection,
  134. setting.scorePart.length >= 2 &&
  135. item.length !== 1 &&
  136. styles.small
  137. ]}>
  138. {item.map((child: any, jIndex: number) => (
  139. <div
  140. class={[styles.beat, child.selected ? styles.active : '']}>
  141. <div class={styles.direction}>
  142. <div
  143. class={styles.up}
  144. onClick={() => {
  145. if (setting.playState === 'play') return;
  146. if (setting.tempo.length <= 1) {
  147. showToast('无法切换,请选择至少2种节奏型');
  148. return;
  149. }
  150. // const obj = randomScoreElement(child.index);
  151. const obj = elementDirection(
  152. 'up',
  153. Number(child.index)
  154. );
  155. child.index = obj.index;
  156. child.url = obj.url;
  157. }}></div>
  158. <div
  159. class={styles.down}
  160. onClick={() => {
  161. if (setting.playState === 'play') return;
  162. if (setting.tempo.length <= 1) {
  163. showToast('无法切换,请选择至少2种节奏型');
  164. return;
  165. }
  166. // const obj = randomScoreElement(child.index);
  167. const obj = elementDirection(
  168. 'down',
  169. Number(child.index)
  170. );
  171. child.index = obj.index;
  172. child.url = obj.url;
  173. }}></div>
  174. </div>
  175. <div class={styles.imgSection}>
  176. <img src={getImage(child.url)} />
  177. </div>
  178. </div>
  179. ))}
  180. </div>
  181. ))}
  182. </div>
  183. </div>
  184. <div class={styles.footer}>
  185. {/* 播放 */}
  186. <div class={styles.play} onClick={handlePlay}>
  187. {setting.playState === 'pause' ? (
  188. <img src={iconPause} />
  189. ) : (
  190. <img src={iconPlay} />
  191. )}
  192. </div>
  193. {/* 播放类型 */}
  194. <div class={styles.playType} onClick={handlePlayType}>
  195. {setting.playType === 'beat' ? (
  196. <img src={beat} />
  197. ) : (
  198. <img src={tempo} />
  199. )}
  200. </div>
  201. {/* 随机生成 */}
  202. <div
  203. class={styles.randomTempo}
  204. onClick={() => {
  205. renderScore();
  206. handleStop();
  207. }}>
  208. <img src={randDom} />
  209. </div>
  210. {/* 速度 */}
  211. <div class={styles.speedChange}>
  212. <img
  213. src={iconPlus}
  214. class={styles.speedPlus}
  215. onClick={() => {
  216. if (setting.speed <= 40) return;
  217. setting.speed -= 1;
  218. handleStop();
  219. }}
  220. />
  221. <Popover
  222. placement="top"
  223. class={styles.popupContainer}
  224. actions={state.speedList}
  225. onSelect={(val: any) => {
  226. if (val.value === setting.speed) return;
  227. state.speedList.forEach((item: any) => {
  228. if (item.value === val.value) {
  229. item.color = '#1CACF1';
  230. setting.speed = val.value;
  231. } else {
  232. item.color = '#060606';
  233. }
  234. });
  235. handleStop();
  236. }}>
  237. {{
  238. reference: () => (
  239. <div class={styles.speedNum}>{setting.speed}</div>
  240. )
  241. }}
  242. </Popover>
  243. <img
  244. src={iconAdd}
  245. class={styles.speedAdd}
  246. onClick={() => {
  247. if (setting.speed >= 200) return;
  248. setting.speed += 1;
  249. handleStop();
  250. }}
  251. />
  252. </div>
  253. </div>
  254. <Popup v-model:show={state.settingStatus} class={styles.settingPopup}>
  255. <SettingModal onClose={() => (state.settingStatus = false)} />
  256. </Popup>
  257. </div>
  258. );
  259. }
  260. });