index.tsx 41 KB

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