index.tsx 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  1. import {
  2. TransitionGroup,
  3. computed,
  4. defineComponent,
  5. nextTick,
  6. onMounted,
  7. reactive,
  8. ref,
  9. watch
  10. } from 'vue';
  11. import styles from './index.module.less';
  12. import MSearch from '@/components/m-search';
  13. // import icon_play from '@/common/images/icon_play.svg';
  14. import {
  15. Empty,
  16. Icon,
  17. List,
  18. Loading,
  19. NoticeBar,
  20. Popover,
  21. Popup,
  22. showLoadingToast,
  23. showToast
  24. } from 'vant';
  25. import icon_back from './image/icon_back.svg';
  26. import icon_down from '@/common/images/icon_down.svg';
  27. // import icon_jianpu from '@/common/images/icon_jianpu.svg';
  28. import icon_jianpuActive from '@/common/images/icon_jianpuActive.svg';
  29. import iconFire from './image/icon_hot.png';
  30. import icons from '@/common/images/index.json';
  31. import {
  32. listenerMessage,
  33. postMessage,
  34. promisefiyPostMessage
  35. } from '@/helpers/native-message';
  36. import html2canvas from 'html2canvas';
  37. import {
  38. api_musicSheetCategoriesPage,
  39. api_musicSheetPage,
  40. api_subjectList
  41. } from './api';
  42. import { state } from '@/state';
  43. import MEmpty from '@/components/m-empty';
  44. import Coaiguide from '@/custom-plugins/guide-page/coai-guide';
  45. import TheVip from '@/components/the-vip';
  46. import request from '@/helpers/request';
  47. import { useRoute } from 'vue-router';
  48. import { addWatermark, convasToImg, imgToCanvas } from './imageFunction';
  49. import ChangeVoice from './change-voice';
  50. import { storage } from '@/helpers/storage';
  51. import { ACCESS_TOKEN } from '@/store/mutation-types';
  52. import {
  53. sortMusical,
  54. getInstrumentName,
  55. vaildMusicScoreUrl,
  56. trackToCode
  57. } from '@/helpers/utils';
  58. import { audioPlayType } from '@/helpers/constant';
  59. export default defineComponent({
  60. name: 'co-ai',
  61. setup() {
  62. const route = useRoute();
  63. const categorForms = reactive({
  64. page: 1,
  65. rows: 999,
  66. enable: true,
  67. // subjectId: state.user.data?.subjectId || '',
  68. musicTagIds: route.query.musicTagId ? [route.query.musicTagId] : []
  69. });
  70. const musicForms = reactive({
  71. page: 1,
  72. rows: 20,
  73. status: 1,
  74. keyword: '', // 关键词
  75. audioPlayTypes: [],
  76. musicSheetCategoriesId: route.query.id as any
  77. // musicalInstrumentId: ''
  78. });
  79. const data = reactive({
  80. musicSheetCategoriesName: route.query.name as any,
  81. audioPlayTypeItem: {} as any,
  82. /** 教材Index */
  83. typeIndex: 0,
  84. /** 音乐Index */
  85. musicIndex: 0,
  86. /** 显示哪种曲谱 */
  87. showMusicImg: 'staff' as 'staff' | 'first' | 'fixed',
  88. popoverShow: false,
  89. popoverMusicShow: false,
  90. /** 教材列表 */
  91. types: [] as any[],
  92. /** 音乐列表 */
  93. musics: [] as any[],
  94. loading: true,
  95. finshed: false,
  96. showChangeVoice: false,
  97. selectMusicInstrumentIndex: 0,
  98. iframeSrc: '',
  99. searchNoticeShow: false,
  100. searchNotice: {
  101. left: '',
  102. top: '',
  103. width: '',
  104. height: ''
  105. },
  106. showVip: false,
  107. vipMember: state.user.data?.vipMember,
  108. subjectStatus: false,
  109. audioPlayTypeList: [] as any, // 场景
  110. subjectList: [],
  111. subjectItem: {} as any, // 当前乐器,
  112. trackList: [] as any, // 可筛选的分轨信息
  113. showTransBtn: true // 是否显示转谱按钮
  114. });
  115. const downRef = ref();
  116. const showGuide = ref(false);
  117. const _actions = computed(() => {
  118. const details = data.musics[data.musicIndex];
  119. let { scoreType, isConvertibleScore } = details || {};
  120. let action: any[] = [
  121. {
  122. value: 'first',
  123. text: '首调'
  124. },
  125. {
  126. value: 'fixed',
  127. text: '固定调'
  128. }
  129. ];
  130. if (
  131. !(
  132. ['JIAN', 'FIRST'].includes(scoreType) && isConvertibleScore === false
  133. ) &&
  134. !(isConvertibleScore === undefined || isConvertibleScore === null)
  135. ) {
  136. action.unshift({
  137. value: 'staff',
  138. text: '五线谱'
  139. });
  140. }
  141. return action.map((item, index) => {
  142. return {
  143. ...item,
  144. color:
  145. data.showMusicImg === item.value ? 'var(--van-primary-color)' : '',
  146. className: data.showMusicImg === item.value ? 'fontBlod' : ''
  147. };
  148. });
  149. });
  150. const _types = computed(() => {
  151. return data.types.map((item: any) => {
  152. return {
  153. ...item,
  154. color:
  155. musicForms.musicSheetCategoriesId == item.value
  156. ? 'var(--van-primary-color)'
  157. : '',
  158. className:
  159. musicForms.musicSheetCategoriesId == item.value ? 'fontBlod' : ''
  160. };
  161. });
  162. });
  163. // 返回
  164. const goback = () => {
  165. postMessage({ api: 'back' });
  166. };
  167. /** 去云练习 */
  168. const handleGoto = () => {
  169. if (
  170. !data.vipMember &&
  171. data.musics[data.musicIndex]?.paymentType === 'VIP'
  172. ) {
  173. data.showVip = true;
  174. return;
  175. }
  176. // 默认进页面显示对应的曲谱
  177. let lineType = 'staff';
  178. if (data.showMusicImg === 'first') {
  179. lineType = 'firstTone';
  180. } else if (data.showMusicImg === 'fixed') {
  181. lineType = 'fixedTone';
  182. } else if (data.showMusicImg === 'staff') {
  183. lineType = 'staff';
  184. }
  185. let src = `${vaildMusicScoreUrl()}/instrument/?id=${
  186. data.musics[data.musicIndex]?.id
  187. }&musicRenderType=${lineType}&showGuide=true&part-index=${
  188. data.selectMusicInstrumentIndex
  189. }`;
  190. // if (data.musics[data.musicIndex]?.paymentType === 'FREE') {
  191. // src += `showCourseMember=true`;
  192. // }
  193. postMessage({
  194. api: 'openAccompanyWebView',
  195. content: {
  196. url: src,
  197. orientation: 0,
  198. isHideTitle: true,
  199. statusBarTextColor: false,
  200. isOpenLight: true,
  201. c_orientation: 0 // 0 横屏 1 竖屏
  202. }
  203. });
  204. };
  205. /** 保存图片 */
  206. const handleSave = async () => {
  207. showLoadingToast({ message: '正在保存', duration: 0 });
  208. try {
  209. html2canvas(downRef.value, {
  210. backgroundColor: '#fff',
  211. allowTaint: true,
  212. useCORS: true
  213. })
  214. .then(async canvas => {
  215. // 添加水印
  216. const waterCanvasImg = await addWatermark(canvas);
  217. // canvas转图片
  218. const dataURL = await convasToImg(waterCanvasImg);
  219. console.log(dataURL, 'dataURL');
  220. setTimeout(() => {
  221. showToast('已保存到相册');
  222. }, 500);
  223. await promisefiyPostMessage({
  224. api: 'savePicture',
  225. content: {
  226. base64: dataURL
  227. }
  228. });
  229. })
  230. .catch(() => {
  231. setTimeout(() => {
  232. showToast('保存失败');
  233. }, 500);
  234. });
  235. } catch (error) {
  236. setTimeout(() => {
  237. showToast('保存失败');
  238. }, 500);
  239. }
  240. };
  241. /** 获取音乐教材列表 */
  242. const getMusicSheetCategories = async () => {
  243. try {
  244. const res = await api_musicSheetCategoriesPage({
  245. ...categorForms
  246. });
  247. if (res.code === 200 && Array.isArray(res?.data?.rows)) {
  248. const temp: any = [];
  249. res.data.rows.forEach((item: any) => {
  250. temp.push({
  251. value: item.id,
  252. text: item.name
  253. });
  254. });
  255. data.types = temp;
  256. }
  257. } catch (error) {
  258. console.log('🚀 ~ error:', error);
  259. }
  260. };
  261. /** 获取曲谱列表 */
  262. const getMusicList = async () => {
  263. data.loading = true;
  264. try {
  265. const res = await api_musicSheetPage({
  266. ...musicForms,
  267. audioPlayTypes: data.audioPlayTypeItem.value
  268. ? data.audioPlayTypeItem.value === 'PLAY_SING'
  269. ? ['PLAY', 'SING']
  270. : [data.audioPlayTypeItem.value]
  271. : []
  272. // musicalInstrumentId: data.subjectItem.value || null
  273. });
  274. if (res.code === 200) {
  275. if (Array.isArray(res?.data?.rows)) {
  276. data.musics = [...data.musics, ...res.data.rows];
  277. data.finshed = !res.data.next;
  278. // 是否显示总谱
  279. const selectMusic = data.musics[data.musicIndex];
  280. if (
  281. selectMusic &&
  282. selectMusic.isScoreRender &&
  283. data.musicIndex === 0
  284. ) {
  285. data.selectMusicInstrumentIndex = 999;
  286. }
  287. } else {
  288. data.finshed = true;
  289. }
  290. } else showGuide.value = true;
  291. } catch (error) {
  292. console.log('🚀 ~ error:', error);
  293. }
  294. data.loading = false;
  295. };
  296. const handleReset = async () => {
  297. musicForms.page = 1;
  298. data.musics = [];
  299. await getMusicList();
  300. initLoadingObv();
  301. };
  302. const spinRef = ref();
  303. const handleResh = () => {
  304. if (data.loading || data.finshed) return;
  305. musicForms.page = musicForms.page + 1;
  306. getMusicList();
  307. };
  308. const musicIframeLoad = () => {
  309. const token = storage.get(ACCESS_TOKEN);
  310. const details = data.musics[data.musicIndex];
  311. if(!details?.id){
  312. data.iframeSrc = ""
  313. return
  314. }
  315. // 如果在配置里面匹配不到,则默认显示五线谱
  316. const musicRenderType =
  317. data.showMusicImg === 'first'
  318. ? 'firstTone'
  319. : data.showMusicImg === 'fixed'
  320. ? 'fixedTone'
  321. : data.showMusicImg === 'staff'
  322. ? 'staff'
  323. : 'staff';
  324. const origin = /(localhost|192)/.test(location.host)
  325. ? 'https://test.lexiaoya.cn'
  326. : location.origin;
  327. // data.iframeSrc = `${origin}/instrument/?id=${details.id}&modelType=practise&modeType=json&Authorization=${token}&isPreView=true&part-index=${data.selectMusicInstrumentIndex}&musicRenderType=${musicRenderType}`;
  328. data.iframeSrc = `${vaildMusicScoreUrl()}/instrument/?id=${
  329. details?.id
  330. }&modelType=practise&modeType=json&Authorization=${token}&isPreView=true&part-index=${
  331. data.selectMusicInstrumentIndex
  332. }&musicRenderType=${musicRenderType}&zoom=0.6`;
  333. console.log('地址', data.iframeSrc);
  334. };
  335. const setSearchBox = () => {
  336. const el = document.querySelector('.searchNotice .van-field__control');
  337. if (el) {
  338. const rect = el.getBoundingClientRect();
  339. data.searchNotice.left = rect.x + 'px';
  340. data.searchNotice.top = rect.y + 'px';
  341. data.searchNotice.width = rect.width + 'px';
  342. data.searchNotice.height = rect.height + 'px';
  343. }
  344. };
  345. // 根据musicSheetType返回的值,判断是否显示切换声轨按钮
  346. const isEnsemble = computed(() => {
  347. if (data.musics.length) {
  348. const musicSheetType = data.musics[data.musicIndex]?.musicSheetType;
  349. if (musicSheetType === 'SINGLE') {
  350. return false;
  351. } else {
  352. return true;
  353. }
  354. } else {
  355. return false;
  356. }
  357. });
  358. // 判断 值当前有没有图片
  359. const isMusicImg = computed(() => {
  360. const musicsData = data.musics[data.musicIndex]
  361. if(data.showMusicImg === 'first' && musicsData?.musicFirstImg){
  362. return true
  363. }
  364. if(data.showMusicImg === 'fixed' && musicsData?.musicJianImg){
  365. return true
  366. }
  367. if(musicsData?.musicImg) {
  368. return true
  369. }
  370. return false
  371. })
  372. // 判断是否可转谱 - 为空也可以转谱
  373. const checkConverTible = (isConvertibleScore: any, scoreType: string) => {
  374. if (
  375. isConvertibleScore ||
  376. isConvertibleScore === '' ||
  377. isConvertibleScore === undefined ||
  378. isConvertibleScore === null ||
  379. (['JIAN', 'FIRST'].includes(scoreType) && !isConvertibleScore)
  380. ) {
  381. return true;
  382. } else {
  383. return false;
  384. }
  385. };
  386. const getSubjecList = async () => {
  387. try {
  388. let subjectIds = state.user.data?.subjectId || '';
  389. subjectIds = subjectIds.split(',');
  390. const subjectId = subjectIds[0] || '';
  391. const res = await api_subjectList({
  392. enableFlag: true,
  393. delFlag: 0,
  394. page: 1,
  395. subjectId: subjectId || '',
  396. rows: 999
  397. });
  398. if (subjectId) {
  399. const result = res.data || [];
  400. let tempSubjects: any = [];
  401. result.forEach((item: any) => {
  402. const instruments = item.instruments || [];
  403. if (Number(subjectId) === item.id && instruments.length > 0) {
  404. instruments.forEach((child: any, index: number) => {
  405. tempSubjects.push({
  406. text: child.name,
  407. value: child.id,
  408. className: index === 0 ? 'selected' : ''
  409. });
  410. });
  411. }
  412. });
  413. data.subjectList = tempSubjects;
  414. if (tempSubjects.length > 0) {
  415. data.subjectItem = tempSubjects[0];
  416. }
  417. }
  418. } catch {
  419. //
  420. }
  421. };
  422. // 解析xml,获取分轨信息
  423. const analyzeXml = async () => {
  424. const details = data.musics[data.musicIndex];
  425. console.log(details?.musicSheetType, 'details?.musicSheetType')
  426. if (details?.musicSheetType === 'CONCERT') {
  427. if (details.xmlFileUrl) {
  428. const res = await fetch(details.xmlFileUrl).then(response =>
  429. response.text()
  430. );
  431. filterTracks(res);
  432. }
  433. } else {
  434. // showMusicImg: 'first' as 'staff' | 'first' | 'fixed',
  435. const { scoreType, isConvertibleScore } = details || {};
  436. let musicImgType: 'staff' | 'first' | 'fixed' = 'first';
  437. musicImgType =
  438. scoreType === 'STAVE'
  439. ? 'staff'
  440. : scoreType === 'JIAN'
  441. ? 'fixed'
  442. : scoreType === 'FIRST'
  443. ? 'first'
  444. : 'first';
  445. data.showMusicImg = musicImgType;
  446. data.showTransBtn = checkConverTible(isConvertibleScore, scoreType);
  447. }
  448. };
  449. /** 获取分轨名称 */
  450. const getInstrumentNameCode = (instruments: any, name = '') => {
  451. name = name.toLocaleLowerCase().replace(/ /g, '').replace(/\d*/gi, '');
  452. if (!name) return '';
  453. for (let key of instruments) {
  454. const _key = key.toLocaleLowerCase().replace(/ /g, '');
  455. // if (_key.includes(name)) {
  456. // return key
  457. // }
  458. if (_key === name) {
  459. return _key;
  460. }
  461. }
  462. // for (let key of instruments) {
  463. // const _key = key.toLocaleLowerCase().replace(/ /g, '')
  464. // if (name.includes(_key)) {
  465. // return key
  466. // }
  467. // }
  468. return '';
  469. };
  470. // 通过乐器编码返回乐器编号
  471. const instrumentCodeToInstrumentId = (
  472. subjectList: Array<any>,
  473. code: string
  474. ) => {
  475. const codeIdMap = new Map<string, []>() as any;
  476. const codeMapKeys: string[] = [];
  477. subjectList.forEach((data: any) => {
  478. if (data.enableFlag) {
  479. const codes = data.code?.split(/[,,]/);
  480. codes.forEach((code: string) => {
  481. let codeTemp = code?.replace(/ /g, '').toLowerCase();
  482. codeMapKeys.push(codeTemp);
  483. if (codeIdMap.has(codeTemp)) {
  484. codeIdMap.get(codeTemp).push(data.id + '');
  485. } else {
  486. const arr = [] as any;
  487. arr.push(data.id + '');
  488. codeIdMap.set(codeTemp, arr);
  489. }
  490. });
  491. }
  492. });
  493. if (!code) {
  494. return '';
  495. }
  496. code = code && code?.replace(/ /g, '').toLowerCase();
  497. const tempCode = getInstrumentNameCode(codeMapKeys, code);
  498. if (codeIdMap.has(tempCode)) {
  499. const result = codeIdMap.get(tempCode);
  500. console.log('result:', result);
  501. return result[0] || '';
  502. }
  503. return '';
  504. };
  505. // 初始化编号
  506. const initUserDefaultInstrument = () => {
  507. const userInstrumentId = state.user.data.instrumentId;
  508. const item = data.trackList.find(
  509. (track: any) => track.instrumentId === userInstrumentId + ''
  510. );
  511. data.selectMusicInstrumentIndex = item ? item.value : 0;
  512. };
  513. // 过滤出能切换的分轨
  514. const filterTracks = (xml: any) => {
  515. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml');
  516. const partList: any =
  517. xmlParse
  518. .getElementsByTagName('part-list')?.[0]
  519. ?.getElementsByTagName('score-part') || [];
  520. const partListNames = Array.from(partList).map(
  521. (item: any) =>
  522. item.getElementsByTagName('part-name')?.[0]?.textContent?.trim() || item.getAttribute('id') || ''
  523. );
  524. const parts: any = xmlParse.getElementsByTagName('part');
  525. /** 第一分谱如果是约定的配置分谱则跳过 */
  526. if (partListNames[0]?.toLocaleUpperCase?.() === 'COMMON') {
  527. partListNames.shift();
  528. }
  529. // 根据后台已选择的分轨筛选出能切换的声轨
  530. const multiTracksSelection =
  531. data.musics[data.musicIndex]?.multiTracksSelection;
  532. const canSelectTracks = multiTracksSelection
  533. ? multiTracksSelection?.split(',')
  534. : [];
  535. const musicalInstruments =
  536. data.musics[data.musicIndex]?.musicalInstruments || [];
  537. // console.log(canSelectTracks, partListNames);
  538. const arr = partListNames
  539. .map((item: any, index: number) => {
  540. // 该声轨能否被选
  541. const canselect =
  542. canSelectTracks.length == 0 || canSelectTracks.includes(item)
  543. ? true
  544. : false;
  545. const instrumentName = getInstrumentName(item);
  546. const instrumentId = instrumentCodeToInstrumentId(
  547. musicalInstruments,
  548. item
  549. );
  550. const sortId = sortMusical(instrumentName, index);
  551. return {
  552. text: item + (instrumentName ? `(${instrumentName})` : ''),
  553. value: index,
  554. instrumentId,
  555. sortId,
  556. canselect,
  557. track: item
  558. };
  559. })
  560. .filter((item: any) => item.canselect)
  561. .sort((a: any, b: any) => a.sortId - b.sortId);
  562. data.trackList = arr;
  563. // 是否显示总谱
  564. const selectMusic = data.musics[data.musicIndex];
  565. if (selectMusic) {
  566. const musicalInstruments = selectMusic.musicalInstruments || [];
  567. if (selectMusic.isScoreRender) {
  568. data.trackList.unshift({
  569. text: '总谱',
  570. value: 999,
  571. sortId: 0,
  572. canselect: true,
  573. track: 999
  574. });
  575. if (selectMusic.defaultScoreRender) {
  576. data.selectMusicInstrumentIndex = 999;
  577. } else {
  578. initUserDefaultInstrument();
  579. }
  580. } else {
  581. initUserDefaultInstrument();
  582. }
  583. }
  584. // let track = arr.find(
  585. // (item: any) => item.value === data.selectMusicInstrumentIndex
  586. // )?.track;
  587. // track = trackToCode(track);
  588. // let musicRenderType: 'staff' | 'first' | 'fixed' = 'staff';
  589. // let canTrans = true;
  590. // data.musics[data.musicIndex]?.musicalInstruments.forEach((item: any) => {
  591. // if (item.code.toLocaleLowerCase() === track.toLocaleLowerCase()) {
  592. // musicRenderType =
  593. // item.defaultScore === 'STAVE'
  594. // ? 'staff'
  595. // : item.defaultScore === 'JIAN'
  596. // ? 'fixed'
  597. // : item.defaultScore === 'FIRST'
  598. // ? 'first'
  599. // : 'staff';
  600. // canTrans = item.transferFlag;
  601. // }
  602. // });
  603. // data.showTransBtn = canTrans;
  604. // data.showMusicImg = musicRenderType;
  605. const details = data.musics[data.musicIndex];
  606. const { scoreType, isConvertibleScore } = details || {};
  607. let musicImgType: 'staff' | 'first' | 'fixed' = 'first';
  608. musicImgType =
  609. scoreType === 'STAVE'
  610. ? 'staff'
  611. : scoreType === 'JIAN'
  612. ? 'fixed'
  613. : scoreType === 'FIRST'
  614. ? 'first'
  615. : 'first';
  616. data.showMusicImg = musicImgType;
  617. data.showTransBtn = checkConverTible(isConvertibleScore, scoreType);
  618. };
  619. watch(
  620. () => data.musicIndex,
  621. async () => {
  622. data.selectMusicInstrumentIndex = 0;
  623. analyzeXml();
  624. }
  625. );
  626. const formatUsedNum = (num: number) => {
  627. if (num < 10000) {
  628. return num;
  629. } else {
  630. const n = num / 10000;
  631. return Number(n.toFixed(1)) + '万';
  632. }
  633. };
  634. const initLoadingObv = () => {
  635. const obv = new IntersectionObserver(entries => {
  636. if (entries[0].intersectionRatio > 0) {
  637. handleResh();
  638. }
  639. });
  640. nextTick(() => {
  641. spinRef.value && obv.observe(spinRef.value);
  642. });
  643. };
  644. onMounted(async () => {
  645. // 场景
  646. const tempAudio = Object.keys(audioPlayType).map(key => {
  647. return {
  648. value: key,
  649. text: audioPlayType[key]
  650. };
  651. });
  652. data.audioPlayTypeList = [
  653. { text: '全部场景', value: '', className: 'selected' },
  654. ...tempAudio
  655. ];
  656. data.audioPlayTypeItem = data.audioPlayTypeList[0];
  657. // 安卓的状态栏
  658. postMessage({
  659. api: 'setStatusBarVisibility',
  660. content: {
  661. isVisibility: 0
  662. }
  663. });
  664. await getSubjecList();
  665. await getMusicSheetCategories();
  666. await getMusicList();
  667. initLoadingObv();
  668. console.log(state.user.data);
  669. const getUserInfo = async () => {
  670. const res = await request.get('/edu-app/user/getUserInfo', {
  671. initRequest: true, // 初始化接口
  672. requestType: 'form',
  673. hideLoading: true
  674. });
  675. if (res?.code === 200) {
  676. data.vipMember = res.data.vipMember;
  677. }
  678. };
  679. await analyzeXml();
  680. musicIframeLoad();
  681. listenerMessage('webViewOnResume', () => {
  682. console.log('页面显示', data.iframeSrc);
  683. getUserInfo();
  684. // 为了处理返回显示刷新的问题
  685. // data.typeIndex = 0;
  686. // data.musicIndex = 0;
  687. // handleReset();
  688. });
  689. setSearchBox();
  690. });
  691. return () => (
  692. <div class={styles.container}>
  693. <div class={styles.back} onClick={goback}>
  694. <img src={icon_back} />
  695. </div>
  696. <div class={styles.musicCFixed}>
  697. <Popover
  698. v-model:show={data.popoverMusicShow}
  699. class={styles.popoverMusic}
  700. actions={_types.value}
  701. placement="bottom"
  702. showArrow={false}
  703. onSelect={(item: any) => {
  704. // data.showMusicImg = item.value;
  705. if (item.value == musicForms.musicSheetCategoriesId) {
  706. return;
  707. }
  708. data.musics = [];
  709. musicForms.musicSheetCategoriesId = item.value;
  710. data.musicSheetCategoriesName = item.text;
  711. data.popoverMusicShow = false;
  712. // getMusicList();
  713. handleReset();
  714. }}>
  715. {{
  716. reference: () => (
  717. <span class={styles.musicName}>
  718. {data.musicSheetCategoriesName}
  719. <Icon name="arrow-down"></Icon>
  720. </span>
  721. )
  722. }}
  723. </Popover>
  724. </div>
  725. <div class={styles.content}>
  726. <div class={[styles.leftContent]}>
  727. <div class={styles.leftBg2}></div>
  728. <div class={styles.center}>
  729. <div class={styles.centerSearch}>
  730. <div id="coai-0">
  731. <MSearch
  732. class={[
  733. 'searchNotice'
  734. // data.searchNoticeShow ? styles.searchNoticeShow : ''
  735. ]}
  736. shape="round"
  737. // background="transparent"
  738. clearable={false}
  739. placeholder="请输入关键字"
  740. modelValue={musicForms.keyword}
  741. // onFocus={() => (data.searchNoticeShow = false)}
  742. onBlur={val => {
  743. musicForms.keyword = val?.trim() || '';
  744. }}
  745. onSearch={async val => {
  746. if (!data.loading) {
  747. musicForms.keyword = val;
  748. data.musicIndex = 0
  749. await handleReset();
  750. await analyzeXml();
  751. musicIframeLoad();
  752. }
  753. }}>
  754. {{
  755. left: () => (
  756. <div class={styles.subjects}>
  757. <Popover
  758. v-model:show={data.subjectStatus}
  759. offset={[0, 8]}
  760. actions={data.audioPlayTypeList}
  761. placement="bottom-start"
  762. showArrow={false}
  763. class={styles.subjectListContainer}
  764. onSelect={(item: any) => {
  765. data.audioPlayTypeList.forEach((c: any) => {
  766. c.className = '';
  767. if (c.value === item.value) {
  768. c.className = 'selected';
  769. }
  770. });
  771. data.audioPlayTypeItem = {
  772. ...item,
  773. className: 'selected'
  774. };
  775. handleReset();
  776. }}>
  777. {{
  778. reference: () => (
  779. <div
  780. class={[
  781. styles.subjectName,
  782. data.subjectStatus && styles.active
  783. ]}>
  784. <span>{data.audioPlayTypeItem.text}</span>
  785. <i></i>
  786. </div>
  787. )
  788. }}
  789. </Popover>
  790. </div>
  791. )
  792. }}
  793. </MSearch>
  794. </div>
  795. </div>
  796. <div class={styles.musicContent}>
  797. {data.musics.map((item: any, index: number) => {
  798. return (
  799. <div
  800. class={[
  801. styles.musicItem,
  802. data.musicIndex === index
  803. ? styles.musicActive
  804. : styles.disableNotic
  805. ]}
  806. onClick={async () => {
  807. data.musicIndex = index;
  808. await analyzeXml();
  809. // 是否显示总谱
  810. const selectMusic = data.musics[data.musicIndex];
  811. if (selectMusic && selectMusic.isScoreRender) {
  812. if (selectMusic.defaultScoreRender) {
  813. data.selectMusicInstrumentIndex = 999;
  814. } else {
  815. initUserDefaultInstrument();
  816. }
  817. } else {
  818. initUserDefaultInstrument();
  819. }
  820. musicIframeLoad();
  821. }}>
  822. <div class={styles.musicAvtor}>
  823. <i
  824. class={[
  825. styles.iconType,
  826. styles[item.paymentType]
  827. ]}></i>
  828. <img
  829. class={styles.titleImg}
  830. src={item.titleImg}
  831. onLoad={(e: Event) => {
  832. const el = e.target as HTMLImageElement;
  833. el.setAttribute('loaded', 'true');
  834. }}
  835. />
  836. </div>
  837. <div class={styles.musicInfo}>
  838. <div class={styles.musicName}>
  839. <NoticeBar
  840. text={item.musicSheetName}
  841. class={styles.noticeBar}
  842. background="none"
  843. />
  844. </div>
  845. <div class={styles.musicDes}>
  846. {/* <div class={styles.musicFavitor}>{item.usedNum}</div> */}
  847. <span class={styles.hotNum}>
  848. <img src={iconFire} class={styles.iconFire} />
  849. {formatUsedNum(item.usedNum)}
  850. </span>
  851. {item.audioPlayTypes?.includes('SING') && (
  852. <span
  853. class={[styles.iconPlayType, styles.iconSing]}>
  854. 演唱
  855. </span>
  856. )}
  857. {item.audioPlayTypes?.includes('PLAY') && (
  858. <span
  859. class={[styles.iconPlayType, styles.iconPlay]}>
  860. 演奏
  861. </span>
  862. )}
  863. <div class={[styles.musicAuthor, 'van-ellipsis']}>
  864. {item.composer || '佚名'}
  865. </div>
  866. </div>
  867. </div>
  868. {/* <img class={[styles.musicIcon]} src={icon_play} /> */}
  869. </div>
  870. );
  871. })}
  872. {!data.finshed && (
  873. <div ref={spinRef} class={styles.loadingWrap}>
  874. <Loading color="#259CFE" />
  875. </div>
  876. )}
  877. {!data.loading && data.musics.length === 0 && (
  878. <div class={styles.empty}>
  879. <MEmpty description="暂无曲谱" />
  880. </div>
  881. )}
  882. </div>
  883. </div>
  884. </div>
  885. <div class={[styles.opacityBg, styles.right]}>
  886. <div class={styles.rightBox}>
  887. {isEnsemble.value && (
  888. <div
  889. class={styles.iconTransfer}
  890. onClick={() => (data.showChangeVoice = true)}>
  891. 切换声部
  892. </div>
  893. )}
  894. <div class={styles.ensembleSection} ref={downRef}>
  895. <div class={styles['right-musicName']}>
  896. {data.musics[data.musicIndex]?.musicSheetName}
  897. </div>
  898. {/* ensembleDetail */}
  899. {data.iframeSrc && (isEnsemble.value || !isMusicImg.value) ? (
  900. <div class={styles.iframeSection}>
  901. <>
  902. {/* {loading.value && (
  903. <div>
  904. <Vue3Lottie
  905. animationData={AstronautJSON}
  906. class={styles.finch}></Vue3Lottie>
  907. <p class={styles.finchLoad}>加载中...</p>
  908. </div>
  909. )} */}
  910. {data.musics[data.musicIndex]?.id && (
  911. <iframe
  912. id="staffIframeRef"
  913. style={{
  914. width: '100%'
  915. // opacity: loading.value ? 0 : 1
  916. }}
  917. src={data.iframeSrc}
  918. // onLoad={async () => {
  919. // await analyzeXml();
  920. // musicIframeLoad();
  921. // }}
  922. ></iframe>
  923. )}
  924. {/* <OsmdPreview ref={osmdPreviewRef} /> */}
  925. </>
  926. </div>
  927. ) : (
  928. <div class={styles.imgSection}>
  929. {data.showMusicImg === 'first' ? (
  930. <>
  931. {data.musics[data.musicIndex]?.musicFirstImg
  932. ?.split(',')
  933. .map((item: any, index: number) => {
  934. return (
  935. <img
  936. class={styles.staff}
  937. src={item + '?v=' + Date.now()}
  938. key={item}
  939. crossorigin="anonymous"
  940. />
  941. );
  942. })}
  943. </>
  944. ) : data.showMusicImg === 'fixed' ? (
  945. <>
  946. <TransitionGroup name="van-fade">
  947. {data.musics[data.musicIndex]?.musicJianImg
  948. ?.split(',')
  949. .map((item: any, index: number) => {
  950. return (
  951. <img
  952. class={styles.staff}
  953. src={item + '?v=' + Date.now()}
  954. key={item}
  955. crossorigin="anonymous"
  956. />
  957. );
  958. })}
  959. </TransitionGroup>
  960. </>
  961. ) : (
  962. <>
  963. {data.musics[data.musicIndex]?.musicImg
  964. ?.split(',')
  965. .map((item: any, index: number) => {
  966. return (
  967. <img
  968. class={styles.staff}
  969. src={item + '?v=' + Date.now()}
  970. key={item}
  971. crossorigin="anonymous"
  972. />
  973. );
  974. })}
  975. </>
  976. )}
  977. </div>
  978. )}
  979. </div>
  980. </div>
  981. <div class={styles.rightBtns}>
  982. {data.showTransBtn && (
  983. <Popover
  984. v-model:show={data.popoverShow}
  985. class={styles.popover}
  986. actions={_actions.value}
  987. placement="top-start"
  988. onSelect={(item: any) => {
  989. data.showMusicImg = item.value;
  990. data.popoverShow = false;
  991. // data.iframeSrc = '';
  992. musicIframeLoad();
  993. }}
  994. // onSelect={onSelect}
  995. >
  996. {{
  997. reference: () => <img id="coai-1" src={icon_jianpuActive} />
  998. }}
  999. </Popover>
  1000. )}
  1001. {(!isEnsemble.value && isMusicImg.value) && (
  1002. <img id="coai-2" src={icon_down} onClick={handleSave} />
  1003. )}
  1004. {data.musics[data.musicIndex]?.id && (
  1005. <div class={styles.rightBtnsRight} id="coai-3">
  1006. <img src={icons.icon_start} onClick={() => handleGoto()} />
  1007. </div>
  1008. )}
  1009. </div>
  1010. </div>
  1011. </div>
  1012. {data.searchNotice.width && data.searchNoticeShow && (
  1013. <div class={styles.searchNotice} style={{ ...data.searchNotice }}>
  1014. <NoticeBar
  1015. text={musicForms.keyword}
  1016. color="#333"
  1017. background="none"
  1018. />
  1019. </div>
  1020. )}
  1021. {showGuide.value && <Coaiguide></Coaiguide>}
  1022. <Popup
  1023. class="popup-custom van-scale"
  1024. transition="van-scale"
  1025. closeOnClickOverlay={false}
  1026. v-model:show={data.showVip}>
  1027. <TheVip
  1028. onClose={val => {
  1029. if (val) {
  1030. postMessage({
  1031. api: 'openWebView',
  1032. content: {
  1033. url: `${location.origin}${location.pathname}#/member-center`,
  1034. orientation: 1
  1035. }
  1036. });
  1037. }
  1038. data.showVip = false;
  1039. }}
  1040. />
  1041. </Popup>
  1042. <Popup
  1043. class="popup-custom van-scale"
  1044. transition="van-scale"
  1045. closeOnClickOverlay={false}
  1046. v-model:show={data.showVip}>
  1047. <TheVip
  1048. onClose={val => {
  1049. if (val) {
  1050. postMessage({
  1051. api: 'openWebView',
  1052. content: {
  1053. url: `${location.origin}${location.pathname}#/member-center`,
  1054. orientation: 1
  1055. }
  1056. });
  1057. }
  1058. data.showVip = false;
  1059. }}
  1060. />
  1061. </Popup>
  1062. {data.showChangeVoice && (
  1063. <Popup
  1064. class="popup-custom van-scale"
  1065. transition="van-scale"
  1066. closeOnClickOverlay={false}
  1067. v-model:show={data.showChangeVoice}>
  1068. <ChangeVoice
  1069. musicalInstruments={data.trackList || []}
  1070. musicalInstrumentIndex={data.selectMusicInstrumentIndex}
  1071. onClose={() => (data.showChangeVoice = false)}
  1072. onConfirm={async (index: number) => {
  1073. data.selectMusicInstrumentIndex = index;
  1074. console.log(index, 'index-------------------')
  1075. // await analyzeXml();
  1076. // data.iframeSrc = '';
  1077. musicIframeLoad();
  1078. data.showChangeVoice = false;
  1079. }}
  1080. />
  1081. </Popup>
  1082. )}
  1083. </div>
  1084. );
  1085. }
  1086. });