index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. import {
  2. PropType,
  3. computed,
  4. defineComponent,
  5. onMounted,
  6. reactive,
  7. watch
  8. } from 'vue';
  9. import styles from './index.module.less';
  10. import { Button, Collapse, CollapseItem, showToast } from 'vant';
  11. import {
  12. barLineList,
  13. beatList,
  14. elementList,
  15. initSelectScorePartModal,
  16. renderScore,
  17. renderScoreModal,
  18. setting,
  19. setting_modal,
  20. tempo4,
  21. tempo8
  22. } from '../setting';
  23. import { getImage } from '../images/music';
  24. import { hendleEndTick } from '../tick';
  25. import { hendleEndBeat } from '../beat-tick';
  26. import { useRoute } from 'vue-router';
  27. import settingArrowActive from '../images/setting-arrow-active.png';
  28. import settingArrowDefault from '../images/setting-arrow-default.png';
  29. import Draggable from 'vuedraggable';
  30. export default defineComponent({
  31. emits: ['close'],
  32. props: {
  33. class: {
  34. type: Object as PropType<any>,
  35. default: () => {}
  36. }
  37. // dataJson: {
  38. // type: Object,
  39. // default: () => {}
  40. // }
  41. },
  42. name: 'setting-modal',
  43. setup(props, { emit, expose }) {
  44. const route = useRoute();
  45. // const { element, beat, barLine, tempo } = props.dataJson;
  46. const tempDeepClone = (val: any) => {
  47. return JSON.parse(JSON.stringify(val));
  48. };
  49. const state = reactive({
  50. win: route.query.win,
  51. platform: route.query.platform,
  52. activeNames: ['base', 'beat'] as any, // 折叠面板
  53. element:
  54. tempDeepClone(setting_modal.element) ||
  55. ('jianpu' as 'jianpu' | 'staff'), // 元素
  56. beat:
  57. tempDeepClone(setting_modal.beat) ||
  58. ('4-4' as '4-2' | '4-3' | '4-4' | '8-3' | '8-6'), // 拍号
  59. barLine: tempDeepClone(setting_modal.barLine) || ('1' as '1' | '2' | '4'), // 小节数
  60. tempo: tempDeepClone(setting_modal.tempo) || (['1', '2', '3'] as any[]) // 节奏形筛选
  61. });
  62. const tempoList = computed(() => {
  63. if (['4-2', '4-3', '4-4'].includes(state.beat)) {
  64. return tempo4;
  65. } else if (['8-3', '8-6'].includes(state.beat)) {
  66. return tempo8;
  67. }
  68. return tempo4;
  69. });
  70. // 重置选中数据
  71. watch(
  72. () => setting_modal,
  73. () => {
  74. state.element =
  75. tempDeepClone(setting_modal.element) ||
  76. ('jianpu' as 'jianpu' | 'staff'); // 元素
  77. state.beat =
  78. tempDeepClone(setting_modal.beat) ||
  79. ('4-4' as '4-2' | '4-3' | '4-4' | '8-3' | '8-6'); // 拍号
  80. state.barLine =
  81. tempDeepClone(setting_modal.barLine) || ('1' as '1' | '2' | '4'); // 小节数
  82. state.tempo =
  83. tempDeepClone(setting_modal.tempo) || (['1', '2', '3'] as any[]); // 节奏形筛选
  84. },
  85. {
  86. deep: true
  87. }
  88. );
  89. const getBeatUrl = (value: any) => {
  90. const prefix = state.element === 'jianpu' ? 'j-' : 'f-';
  91. return prefix + value + '.png';
  92. };
  93. const onChangeTempo = (item: any) => {
  94. let si = 0,
  95. ji = 0;
  96. let status = false;
  97. setting_modal.scorePart.forEach((part: Array<any>, i: number) => {
  98. part.forEach((child: any, j: number) => {
  99. if (child.selected) {
  100. child.url = getBeatUrl(item);
  101. child.index = item;
  102. child.selected = false;
  103. si = i;
  104. ji = j;
  105. status = true;
  106. }
  107. });
  108. });
  109. if (status) {
  110. const indexs = toNext(si, ji);
  111. if (indexs && indexs.length > 0) {
  112. initSelectScorePartModal(indexs[0], indexs[1]);
  113. }
  114. }
  115. };
  116. const toNext = (i: number, j: number) => {
  117. const scorePart = setting_modal.scorePart;
  118. let tempJ = j + 1;
  119. for (let si = i; si < scorePart.length; si++) {
  120. for (let ji = tempJ; ji < scorePart[si].length; ji++) {
  121. return [si, ji];
  122. }
  123. tempJ = 0;
  124. }
  125. };
  126. const handleStop = () => {
  127. setting_modal.playState = 'pause';
  128. if (setting_modal.playType === 'beat') {
  129. hendleEndTick();
  130. } else {
  131. hendleEndBeat();
  132. }
  133. };
  134. /** 数据有变化时重置 */
  135. const onChangeResetTempo = () => {
  136. // if (state.tempo.length <= 0) {
  137. // showToast('节奏型不能为空');
  138. // return;
  139. // }
  140. let status = false; // 是否有更改
  141. if (
  142. setting_modal.element !== state.element ||
  143. setting_modal.beat !== state.beat ||
  144. setting_modal.barLine !== state.barLine ||
  145. setting_modal.tempo.join(',') !== state.tempo.join(',')
  146. ) {
  147. status = true;
  148. }
  149. // 判断是否有数据变化
  150. handleStop();
  151. if (status) {
  152. setting_modal.element = tempDeepClone(state.element);
  153. setting_modal.beat = tempDeepClone(state.beat); //state.beat;
  154. setting_modal.barLine = tempDeepClone(state.barLine); // state.barLine;
  155. setting_modal.tempo = tempDeepClone(state.tempo); // state.tempo;
  156. renderScoreModal();
  157. }
  158. };
  159. const onSubmit = () => {
  160. // 初始化设置的数据
  161. for (let i in setting_modal) {
  162. setting[i] = JSON.parse(JSON.stringify(setting_modal[i]));
  163. }
  164. emit('close');
  165. };
  166. const tempoListData = computed(() => {
  167. const list = Object.keys(tempoList.value);
  168. return list.map(key => {
  169. return {
  170. index: key,
  171. key: key,
  172. url: getBeatUrl(key),
  173. sourceFrom: 'setting-modal'
  174. };
  175. });
  176. });
  177. expose({
  178. onSubmit
  179. });
  180. return () => (
  181. <div
  182. class={[
  183. props.class,
  184. styles.settingContainer,
  185. state.win === 'pc' ? styles.pcS : '',
  186. state.platform === 'modal' && state.win !== 'pc' ? styles.modalS : ''
  187. ]}>
  188. {/* <div class={styles.title}></div> */}
  189. <div class={[styles.iconTitBox, 'iconTitBoxMove']}>
  190. <i
  191. class={styles.iconClose}
  192. onClick={() => {
  193. emit('close');
  194. setTimeout(() => {
  195. state.element = tempDeepClone(setting_modal.element);
  196. state.beat = tempDeepClone(setting_modal.beat); //state.beat;
  197. state.barLine = tempDeepClone(setting_modal.barLine); // state.barLine;
  198. state.tempo = tempDeepClone(setting_modal.tempo); // state.tempo;
  199. }, 300);
  200. }}></i>
  201. </div>
  202. <div class={[styles.settingContent]}>
  203. <Collapse v-model={state.activeNames} border={false}>
  204. <CollapseItem
  205. title="基础设置"
  206. name="base"
  207. border={false}
  208. isLink={false}
  209. class={[
  210. styles.collapseContainer,
  211. state.activeNames.includes('base') ? '' : styles.paddingBottom
  212. ]}>
  213. {{
  214. icon: () => (
  215. <img
  216. src={
  217. state.activeNames.includes('base')
  218. ? settingArrowActive
  219. : settingArrowDefault
  220. }
  221. class={styles.iArrow}
  222. />
  223. ),
  224. default: () => (
  225. <>
  226. <div class={styles.parmaTitle}>元素</div>
  227. <div class={styles.paramContent}>
  228. {Object.keys(elementList).map((item: any) => (
  229. <Button
  230. round
  231. class={[
  232. styles.btn,
  233. state.element === item && styles.active
  234. ]}
  235. onClick={() => {
  236. state.element = item;
  237. onChangeResetTempo();
  238. }}>
  239. {elementList[item]}
  240. </Button>
  241. ))}
  242. </div>
  243. <div class={styles.parmaTitle}>拍号</div>
  244. <div class={[styles.paramContent, styles.beatContent]}>
  245. {Object.keys(beatList).map((item: any) => (
  246. <Button
  247. round
  248. class={[
  249. styles.btn,
  250. state.beat === item && styles.active
  251. ]}
  252. onClick={() => {
  253. state.beat = item;
  254. if (['4-2', '4-3', '4-4'].includes(state.beat)) {
  255. state.tempo = ['1', '2', '3'];
  256. } else if (['8-3', '8-6'].includes(state.beat)) {
  257. state.tempo = ['15', '16', '17'];
  258. }
  259. onChangeResetTempo();
  260. }}>
  261. {beatList[item]}
  262. </Button>
  263. ))}
  264. </div>
  265. <div class={styles.parmaTitle}>每页显示小节数量</div>
  266. <div class={styles.paramContent}>
  267. {Object.keys(barLineList).map((item: any) => (
  268. <Button
  269. round
  270. class={[
  271. styles.btn,
  272. state.barLine === item && styles.active
  273. ]}
  274. onClick={() => {
  275. state.barLine = item;
  276. onChangeResetTempo();
  277. }}>
  278. {barLineList[item]}
  279. </Button>
  280. ))}
  281. </div>
  282. </>
  283. )
  284. }}
  285. </CollapseItem>
  286. <CollapseItem
  287. title="节奏型"
  288. name="beat"
  289. border={false}
  290. isLink={false}
  291. class={styles.collapseContainer}>
  292. {{
  293. icon: () => (
  294. <img
  295. src={
  296. state.activeNames.includes('beat')
  297. ? settingArrowActive
  298. : settingArrowDefault
  299. }
  300. class={styles.iArrow}
  301. />
  302. ),
  303. default: () => (
  304. <>
  305. {/* <div class={styles.parmaTitle}>节奏型筛选</div> */}
  306. <Draggable
  307. modelValue={tempoListData.value}
  308. itemKey="id"
  309. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  310. // @ts-ignore
  311. // group="description"
  312. group={{
  313. name: 'description',
  314. pull: 'clone',
  315. put: false
  316. }}
  317. animation={200}
  318. sort={false}
  319. onMove={(evt: any) => {
  320. return evt.from !== evt.to;
  321. }}
  322. onStart={(evt: any) => {
  323. evt.from.classList.add('onstart');
  324. }}
  325. onEnd={(evt: any) => {
  326. evt.from.classList.remove('onstart');
  327. }}
  328. componentData={{
  329. draggable: 'row-nav',
  330. itemKey: 'id',
  331. tag: 'div',
  332. pull: 'clone',
  333. put: false,
  334. animation: 200,
  335. group: 'description'
  336. }}
  337. class={[styles.paramContent, styles.tempo]}>
  338. {{
  339. item: (element: any) => {
  340. const item = element.element;
  341. return (
  342. <div
  343. data-id={item.key}
  344. onClick={() => onChangeTempo(item.key)}>
  345. <img
  346. class={'draggable'}
  347. // class={state.tempo.includes(item) && styles.active}
  348. src={getImage(
  349. (state.element === 'jianpu' ? 'j-' : 'f-') +
  350. tempoList.value[item.key]
  351. )}
  352. />
  353. </div>
  354. );
  355. }
  356. }}
  357. </Draggable>
  358. </>
  359. )
  360. }}
  361. </CollapseItem>
  362. </Collapse>
  363. {/* <div class={styles.settingParams}></div> */}
  364. </div>
  365. {!state.win && !state.platform && (
  366. <div class={styles.btnGroup}>
  367. <Button class={styles.btnSubmit} onClick={onSubmit}></Button>
  368. </div>
  369. )}
  370. </div>
  371. );
  372. }
  373. });