index.tsx 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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('up', child.index);
  152. child.index = obj.index;
  153. child.url = obj.url;
  154. }}></div>
  155. <div
  156. class={styles.down}
  157. onClick={() => {
  158. if (setting.playState === 'play') return;
  159. if (setting.tempo.length <= 1) {
  160. showToast('无法切换,请选择至少2种节奏型');
  161. return;
  162. }
  163. // const obj = randomScoreElement(child.index);
  164. const obj = elementDirection('down', child.index);
  165. child.index = obj.index;
  166. child.url = obj.url;
  167. }}></div>
  168. </div>
  169. <div class={styles.imgSection}>
  170. <img src={getImage(child.url)} />
  171. </div>
  172. </div>
  173. ))}
  174. </div>
  175. ))}
  176. </div>
  177. </div>
  178. <div class={styles.footer}>
  179. {/* 播放 */}
  180. <div class={styles.play} onClick={handlePlay}>
  181. {setting.playState === 'pause' ? (
  182. <img src={iconPause} />
  183. ) : (
  184. <img src={iconPlay} />
  185. )}
  186. </div>
  187. {/* 播放类型 */}
  188. <div class={styles.playType} onClick={handlePlayType}>
  189. {setting.playType === 'beat' ? (
  190. <img src={beat} />
  191. ) : (
  192. <img src={tempo} />
  193. )}
  194. </div>
  195. {/* 随机生成 */}
  196. <div
  197. class={styles.randomTempo}
  198. onClick={() => {
  199. renderScore();
  200. handleStop();
  201. }}>
  202. <img src={randDom} />
  203. </div>
  204. {/* 速度 */}
  205. <div class={styles.speedChange}>
  206. <img
  207. src={iconPlus}
  208. class={styles.speedPlus}
  209. onClick={() => {
  210. if (setting.speed <= 40) return;
  211. setting.speed -= 1;
  212. handleStop();
  213. }}
  214. />
  215. <Popover
  216. placement="top"
  217. class={styles.popupContainer}
  218. actions={state.speedList}
  219. onSelect={(val: any) => {
  220. if (val.value === setting.speed) return;
  221. state.speedList.forEach((item: any) => {
  222. if (item.value === val.value) {
  223. item.color = '#1CACF1';
  224. setting.speed = val.value;
  225. } else {
  226. item.color = '#060606';
  227. }
  228. });
  229. handleStop();
  230. }}>
  231. {{
  232. reference: () => (
  233. <div class={styles.speedNum}>{setting.speed}</div>
  234. )
  235. }}
  236. </Popover>
  237. <img
  238. src={iconAdd}
  239. class={styles.speedAdd}
  240. onClick={() => {
  241. if (setting.speed >= 200) return;
  242. setting.speed += 1;
  243. handleStop();
  244. }}
  245. />
  246. </div>
  247. </div>
  248. <Popup v-model:show={state.settingStatus} class={styles.settingPopup}>
  249. <SettingModal onClose={() => (state.settingStatus = false)} />
  250. </Popup>
  251. </div>
  252. );
  253. }
  254. });