index.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  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 icons from '@/common/images/index.json';
  30. import {
  31. listenerMessage,
  32. postMessage,
  33. promisefiyPostMessage
  34. } from '@/helpers/native-message';
  35. import html2canvas from 'html2canvas';
  36. import {
  37. api_musicSheetCategoriesPage,
  38. api_musicSheetPage,
  39. api_subjectList
  40. } from './api';
  41. import { state } from '@/state';
  42. import MEmpty from '@/components/m-empty';
  43. import Coaiguide from '@/custom-plugins/guide-page/coai-guide';
  44. import TheVip from '@/components/the-vip';
  45. import request from '@/helpers/request';
  46. import { useRoute } from 'vue-router';
  47. import { addWatermark, convasToImg, imgToCanvas } from './imageFunction';
  48. import ChangeVoice from './change-voice';
  49. import { storage } from '@/helpers/storage';
  50. import { ACCESS_TOKEN } from '@/store/mutation-types';
  51. import { sortMusical, getInstrumentName } from '@/helpers/utils'
  52. export default defineComponent({
  53. name: 'co-ai',
  54. setup() {
  55. const route = useRoute();
  56. const categorForms = reactive({
  57. page: 1,
  58. rows: 999,
  59. subjectId: state.user.data?.subjectId || '',
  60. musicTagIds: route.query.musicTagId ? [route.query.musicTagId] : []
  61. });
  62. const musicForms = reactive({
  63. page: 1,
  64. rows: 20,
  65. status: 1,
  66. keyword: '', // 关键词
  67. musicSheetCategoriesId: route.query.id as any,
  68. musicalInstrumentId: ''
  69. });
  70. const data = reactive({
  71. musicSheetCategoriesName: route.query.name as any,
  72. /** 教材Index */
  73. typeIndex: 0,
  74. /** 音乐Index */
  75. musicIndex: 0,
  76. /** 显示哪种曲谱 */
  77. showMusicImg: 'first' as 'staff' | 'first' | 'fixed',
  78. popoverShow: false,
  79. popoverMusicShow: false,
  80. /** 教材列表 */
  81. types: [] as any[],
  82. /** 音乐列表 */
  83. musics: [] as any[],
  84. loading: true,
  85. finshed: false,
  86. showChangeVoice: false,
  87. selectMusicInstrumentIndex: 0,
  88. iframeSrc: '',
  89. searchNoticeShow: false,
  90. searchNotice: {
  91. left: '',
  92. top: '',
  93. width: '',
  94. height: ''
  95. },
  96. showVip: false,
  97. vipMember: state.user.data?.vipMember,
  98. subjectStatus: false,
  99. subjectList: [],
  100. subjectItem: {} as any, // 当前乐器,
  101. trackList: [] as any, // 可筛选的分轨信息
  102. });
  103. const downRef = ref();
  104. const showGuide = ref(false);
  105. const _actions = computed(() => {
  106. return [
  107. {
  108. value: 'staff',
  109. text: '五线谱'
  110. },
  111. {
  112. value: 'first',
  113. text: '首调'
  114. },
  115. {
  116. value: 'fixed',
  117. text: '固定调'
  118. }
  119. ].map((item, index) => {
  120. return {
  121. ...item,
  122. color:
  123. data.showMusicImg === item.value ? 'var(--van-primary-color)' : '',
  124. className: data.showMusicImg === item.value ? 'fontBlod' : ''
  125. };
  126. });
  127. });
  128. const _types = computed(() => {
  129. return data.types.map((item: any) => {
  130. return {
  131. ...item,
  132. color:
  133. musicForms.musicSheetCategoriesId == item.value
  134. ? 'var(--van-primary-color)'
  135. : '',
  136. className:
  137. musicForms.musicSheetCategoriesId == item.value ? 'fontBlod' : ''
  138. };
  139. });
  140. });
  141. // 返回
  142. const goback = () => {
  143. postMessage({ api: 'goBack' });
  144. };
  145. /** 去云教练 */
  146. const handleGoto = () => {
  147. if (!data.vipMember) {
  148. data.showVip = true;
  149. return;
  150. }
  151. // 默认进页面显示对应的曲谱
  152. let lineType = 'staff';
  153. if (data.showMusicImg === 'first') {
  154. lineType = 'firstTone';
  155. } else if (data.showMusicImg === 'fixed') {
  156. lineType = 'fixedTone';
  157. } else if (data.showMusicImg === 'staff') {
  158. lineType = 'staff';
  159. }
  160. let src = `${location.origin}/instrument?id=${
  161. data.musics[data.musicIndex]?.id
  162. }&musicRenderType=${lineType}&showGuide=true`;
  163. postMessage({
  164. api: 'openAccompanyWebView',
  165. content: {
  166. url: src,
  167. orientation: 0,
  168. isHideTitle: true,
  169. statusBarTextColor: false,
  170. isOpenLight: true,
  171. c_orientation: 0 // 0 横屏 1 竖屏
  172. }
  173. });
  174. };
  175. /** 保存图片 */
  176. const handleSave = async () => {
  177. showLoadingToast({ message: '正在保存', duration: 0 });
  178. try {
  179. html2canvas(downRef.value, {
  180. backgroundColor: '#fff',
  181. allowTaint: true,
  182. useCORS: true
  183. })
  184. .then(async canvas => {
  185. // 添加水印
  186. const waterCanvasImg = await addWatermark(canvas);
  187. // canvas转图片
  188. const dataURL = await convasToImg(waterCanvasImg);
  189. console.log(dataURL, 'dataURL');
  190. setTimeout(() => {
  191. showToast('已保存到相册');
  192. }, 500);
  193. await promisefiyPostMessage({
  194. api: 'savePicture',
  195. content: {
  196. base64: dataURL
  197. }
  198. });
  199. })
  200. .catch(() => {
  201. setTimeout(() => {
  202. showToast('保存失败');
  203. }, 500);
  204. });
  205. } catch (error) {
  206. setTimeout(() => {
  207. showToast('保存失败');
  208. }, 500);
  209. }
  210. };
  211. /** 获取音乐教材列表 */
  212. const getMusicSheetCategories = async () => {
  213. try {
  214. const res = await api_musicSheetCategoriesPage({
  215. ...categorForms
  216. });
  217. if (res.code === 200 && Array.isArray(res?.data?.rows)) {
  218. const temp: any = [];
  219. res.data.rows.forEach((item: any) => {
  220. temp.push({
  221. value: item.id,
  222. text: item.name
  223. });
  224. });
  225. data.types = temp;
  226. }
  227. } catch (error) {
  228. console.log('🚀 ~ error:', error);
  229. }
  230. };
  231. /** 获取曲谱列表 */
  232. const getMusicList = async () => {
  233. data.loading = true;
  234. try {
  235. const res = await api_musicSheetPage({
  236. ...musicForms,
  237. musicalInstrumentId: data.subjectItem.value || null
  238. });
  239. if (res.code === 200 && Array.isArray(res?.data?.rows)) {
  240. data.musics = [...data.musics, ...res.data.rows];
  241. data.finshed = !res.data.next;
  242. }
  243. showGuide.value = true;
  244. } catch (error) {
  245. console.log('🚀 ~ error:', error);
  246. }
  247. data.loading = false;
  248. };
  249. const handleReset = () => {
  250. musicForms.page = 1;
  251. data.musics = [];
  252. getMusicList();
  253. };
  254. const spinRef = ref();
  255. const handleResh = () => {
  256. if (data.loading || data.finshed) return;
  257. musicForms.page = musicForms.page + 1;
  258. getMusicList();
  259. };
  260. const musicIframeLoad = () => {
  261. const token = storage.get(ACCESS_TOKEN);
  262. const details = data.musics[data.musicIndex];
  263. const musicRenderType = data.showMusicImg === 'first' ? 'firstTone' : data.showMusicImg === 'fixed' ? 'fixedTone' : data.showMusicImg === 'staff' ? 'staff' : 'firstTone'
  264. const origin = /(localhost|192)/.test(location.host)
  265. ? 'https://test.lexiaoya.cn'
  266. : location.origin;
  267. data.iframeSrc = `${origin}/instrument/?id=${details.id}&modelType=practise&modeType=json&Authorization=${token}&isPreView=true&part-index=${data.selectMusicInstrumentIndex}&musicRenderType=${musicRenderType}`;
  268. };
  269. const setSearchBox = () => {
  270. const el = document.querySelector('.searchNotice .van-field__control');
  271. if (el) {
  272. const rect = el.getBoundingClientRect();
  273. data.searchNotice.left = rect.x + 'px';
  274. data.searchNotice.top = rect.y + 'px';
  275. data.searchNotice.width = rect.width + 'px';
  276. data.searchNotice.height = rect.height + 'px';
  277. }
  278. };
  279. const isEnsemble = computed(() => {
  280. const musics = data.musics[data.musicIndex]?.musicalInstruments;
  281. if (musics && musics.length) {
  282. let list: any = []
  283. const arr = musics.forEach((item: any) => {
  284. list.push({'name': item.name,'code': item.code})
  285. })
  286. console.log(999,list)
  287. }
  288. if (musics && musics.length > 1) {
  289. return true;
  290. } else {
  291. return false;
  292. }
  293. });
  294. const getSubjecList = async () => {
  295. try {
  296. let subjectIds = state.user.data?.subjectId || '';
  297. subjectIds = subjectIds.split(',');
  298. const subjectId = subjectIds[0] || '';
  299. const res = await api_subjectList({
  300. enableFlag: true,
  301. delFlag: 0,
  302. page: 1,
  303. subjectId: subjectId || '',
  304. rows: 999
  305. });
  306. if (subjectId) {
  307. const result = res.data || [];
  308. let tempSubjects: any = [];
  309. result.forEach((item: any) => {
  310. const instruments = item.instruments || [];
  311. if (Number(subjectId) === item.id && instruments.length > 0) {
  312. instruments.forEach((child: any, index: number) => {
  313. tempSubjects.push({
  314. text: child.name,
  315. value: child.id,
  316. className: index === 0 ? 'selected' : ''
  317. });
  318. });
  319. }
  320. });
  321. data.subjectList = tempSubjects;
  322. if (tempSubjects.length > 0) {
  323. data.subjectItem = tempSubjects[0];
  324. }
  325. }
  326. } catch {
  327. //
  328. }
  329. };
  330. // 解析xml,获取分轨信息
  331. const analyzeXml = async () => {
  332. const details = data.musics[data.musicIndex];
  333. if (details.xmlFileUrl) {
  334. const res = await fetch(details.xmlFileUrl).then((response) => response.text());
  335. filterTracks(res)
  336. }
  337. }
  338. // 过滤出能切换的分轨
  339. const filterTracks = (xml: any) => {
  340. const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
  341. const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
  342. const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
  343. const parts: any = xmlParse.getElementsByTagName("part");
  344. /** 第一分谱如果是约定的配置分谱则跳过 */
  345. if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
  346. partListNames.shift();
  347. }
  348. // 根据后台已选择的分轨筛选出能切换的声轨
  349. const multiTracksSelection = data.musics[data.musicIndex]?.multiTracksSelection
  350. const canSelectTracks = multiTracksSelection ? multiTracksSelection?.split(',') : []
  351. const arr = partListNames.map((item: any, index: number) => {
  352. // 该声轨能否被选
  353. const canselect = canSelectTracks.length == 0 || canSelectTracks.includes(item) ? true : false
  354. // console.log(canselect,index)
  355. const instrumentName = getInstrumentName(item)
  356. const sortId = sortMusical(instrumentName, index)
  357. return {
  358. text: item + (instrumentName ? `(${instrumentName})` : ''),
  359. value: index,
  360. sortId,
  361. canselect,
  362. track: item,
  363. }
  364. }).filter((item: any) => item.canselect).sort((a: any, b: any) => a.sortId - b.sortId)
  365. data.trackList = arr;
  366. let track = arr.find((item: any) => item.value === data.selectMusicInstrumentIndex)?.track
  367. track = track.replace(/[0-9]+/g,"").replace(/\s/g, '').toLocaleLowerCase()
  368. let musicRenderType: 'staff' | 'first' | 'fixed' = 'first'
  369. data.musics[data.musicIndex]?.musicalInstruments.forEach((item: any) => {
  370. if (item.code.toLocaleLowerCase() === track) {
  371. musicRenderType = item.defaultScore === 'STAVE' ? 'staff' : item.defaultScore === 'JIAN' ? 'fixed' : item.defaultScore === 'FIRST' ? 'first' : 'first'
  372. }
  373. })
  374. data.showMusicImg = musicRenderType
  375. }
  376. watch(
  377. () => data.musicIndex,
  378. async () => {
  379. data.selectMusicInstrumentIndex = 0
  380. analyzeXml()
  381. }
  382. );
  383. onMounted(async () => {
  384. // 安卓的状态栏
  385. postMessage({
  386. api: 'setStatusBarVisibility',
  387. content: {
  388. isVisibility: 0
  389. }
  390. });
  391. await getSubjecList();
  392. await getMusicSheetCategories();
  393. await getMusicList();
  394. const obv = new IntersectionObserver(entries => {
  395. if (entries[0].intersectionRatio > 0) {
  396. handleResh();
  397. }
  398. });
  399. nextTick(() => {
  400. spinRef.value && obv.observe(spinRef.value);
  401. });
  402. const getUserInfo = async () => {
  403. const res = await request.get('/edu-app/user/getUserInfo', {
  404. initRequest: true, // 初始化接口
  405. requestType: 'form',
  406. hideLoading: true
  407. });
  408. if (res?.code === 200) {
  409. data.vipMember = res.data.vipMember;
  410. }
  411. };
  412. analyzeXml();
  413. listenerMessage('webViewOnResume', () => {
  414. console.log('页面显示');
  415. getUserInfo();
  416. data.typeIndex = 0;
  417. data.musicIndex = 0;
  418. handleReset();
  419. });
  420. setSearchBox();
  421. });
  422. return () => (
  423. <div class={styles.container}>
  424. <div class={styles.back} onClick={goback}>
  425. <img src={icon_back} />
  426. </div>
  427. <div class={styles.musicCFixed}>
  428. <Popover
  429. v-model:show={data.popoverMusicShow}
  430. class={styles.popoverMusic}
  431. actions={_types.value}
  432. placement="bottom"
  433. showArrow={false}
  434. onSelect={(item: any) => {
  435. // data.showMusicImg = item.value;
  436. if (item.value == musicForms.musicSheetCategoriesId) {
  437. return;
  438. }
  439. data.musics = [];
  440. musicForms.musicSheetCategoriesId = item.value;
  441. data.musicSheetCategoriesName = item.text;
  442. data.popoverMusicShow = false;
  443. getMusicList();
  444. }}>
  445. {{
  446. reference: () => (
  447. <span class={styles.musicName}>
  448. {data.musicSheetCategoriesName}
  449. <Icon name="arrow-down"></Icon>
  450. </span>
  451. )
  452. }}
  453. </Popover>
  454. </div>
  455. <div class={styles.content}>
  456. <div class={[styles.leftContent]}>
  457. <div class={styles.leftBg2}></div>
  458. <div class={styles.center}>
  459. <div class={styles.centerSearch}>
  460. <div id="coai-0">
  461. <MSearch
  462. class={[
  463. 'searchNotice',
  464. data.searchNoticeShow ? styles.searchNoticeShow : ''
  465. ]}
  466. shape="round"
  467. // background="transparent"
  468. clearable={false}
  469. placeholder="请输入关键字"
  470. modelValue={musicForms.keyword}
  471. onFocus={() => (data.searchNoticeShow = false)}
  472. onBlur={val => {
  473. musicForms.keyword = val?.trim() || '';
  474. requestAnimationFrame(() => {
  475. requestAnimationFrame(() => {
  476. if (musicForms.keyword) {
  477. data.searchNoticeShow = true;
  478. }
  479. });
  480. });
  481. }}
  482. onSearch={val => {
  483. musicForms.keyword = val;
  484. handleReset();
  485. }}>
  486. {{
  487. left: () =>
  488. data.subjectItem.value &&
  489. data.subjectList.length > 1 && (
  490. <div class={styles.subjects}>
  491. <Popover
  492. v-model:show={data.subjectStatus}
  493. offset={[0, 18]}
  494. actions={data.subjectList}
  495. placement="bottom-start"
  496. onSelect={(item: any) => {
  497. data.subjectList.forEach((c: any) => {
  498. c.className = '';
  499. });
  500. data.subjectItem = {
  501. ...item,
  502. className: 'selected'
  503. };
  504. handleReset();
  505. }}>
  506. {{
  507. reference: () => (
  508. <div
  509. class={[
  510. styles.subjectName,
  511. data.subjectStatus && styles.active
  512. ]}>
  513. <span>{data.subjectItem.text}</span> <i></i>
  514. </div>
  515. )
  516. }}
  517. </Popover>
  518. </div>
  519. )
  520. }}
  521. </MSearch>
  522. </div>
  523. </div>
  524. <div class={styles.musicContent}>
  525. {data.musics.map((item: any, index: number) => {
  526. return (
  527. <div
  528. class={[
  529. styles.musicItem,
  530. data.musicIndex === index
  531. ? styles.musicActive
  532. : styles.disableNotic
  533. ]}
  534. onClick={() => {
  535. data.musicIndex = index;
  536. musicIframeLoad();
  537. }}>
  538. <img
  539. class={styles.musicAvtor}
  540. src={item.titleImg}
  541. onLoad={(e: Event) => {
  542. const el = e.target as HTMLImageElement;
  543. el.setAttribute('loaded', 'true');
  544. }}
  545. />
  546. <div class={styles.musicInfo}>
  547. <div class={styles.musicName}>
  548. <NoticeBar
  549. text={item.musicSheetName}
  550. class={styles.noticeBar}
  551. background="none"
  552. />
  553. </div>
  554. <div class={styles.musicDes}>
  555. <div class={styles.musicFavitor}>{item.usedNum}</div>
  556. <div class={[styles.musicAuthor, 'van-ellipsis']}>
  557. {item.composer || '佚名'}
  558. </div>
  559. </div>
  560. </div>
  561. {/* <img class={[styles.musicIcon]} src={icon_play} /> */}
  562. </div>
  563. );
  564. })}
  565. {!data.finshed && (
  566. <div ref={spinRef} class={styles.loadingWrap}>
  567. <Loading color="#259CFE" />
  568. </div>
  569. )}
  570. {!data.loading && data.musics.length === 0 && (
  571. <div class={styles.empty}>
  572. <MEmpty description="暂无曲谱" />
  573. </div>
  574. )}
  575. </div>
  576. </div>
  577. </div>
  578. <div class={[styles.opacityBg, styles.right]}>
  579. <div class={styles.rightBox}>
  580. {isEnsemble.value && (
  581. <div
  582. class={styles.iconTransfer}
  583. onClick={() => (data.showChangeVoice = true)}>
  584. 切换声轨
  585. </div>
  586. )}
  587. <div ref={downRef}>
  588. <div class={styles['right-musicName']}>
  589. {data.musics[data.musicIndex]?.musicSheetName}
  590. </div>
  591. {/* ensembleDetail */}
  592. {isEnsemble.value ? (
  593. <div>
  594. <>
  595. {/* {loading.value && (
  596. <div>
  597. <Vue3Lottie
  598. animationData={AstronautJSON}
  599. class={styles.finch}></Vue3Lottie>
  600. <p class={styles.finchLoad}>加载中...</p>
  601. </div>
  602. )} */}
  603. <iframe
  604. id="staffIframeRef"
  605. style={{
  606. width: '100%'
  607. // opacity: loading.value ? 0 : 1
  608. }}
  609. src={data.iframeSrc}
  610. onLoad={musicIframeLoad}></iframe>
  611. {/* <OsmdPreview ref={osmdPreviewRef} /> */}
  612. </>
  613. </div>
  614. ) : (
  615. <>
  616. {data.showMusicImg === 'first' ? (
  617. <>
  618. {data.musics[data.musicIndex]?.musicSvg
  619. ?.split(',')
  620. .map((item: any, index: number) => {
  621. return (
  622. <img
  623. class={styles.staff}
  624. src={item + '?v=' + Date.now()}
  625. key={item}
  626. crossorigin="anonymous"
  627. />
  628. );
  629. })}
  630. </>
  631. ) : data.showMusicImg === 'fixed' ? (
  632. <>
  633. <TransitionGroup name="van-fade">
  634. {data.musics[data.musicIndex]?.musicJianSvg
  635. ?.split(',')
  636. .map((item: any, index: number) => {
  637. return (
  638. <img
  639. class={styles.staff}
  640. src={item + '?v=' + Date.now()}
  641. key={item}
  642. crossorigin="anonymous"
  643. />
  644. );
  645. })}
  646. </TransitionGroup>
  647. </>
  648. ) : (
  649. <>
  650. {data.musics[data.musicIndex]?.musicImg
  651. ?.split(',')
  652. .map((item: any, index: number) => {
  653. return (
  654. <img
  655. class={styles.staff}
  656. src={item + '?v=' + Date.now()}
  657. key={item}
  658. crossorigin="anonymous"
  659. />
  660. );
  661. })}
  662. </>
  663. )}
  664. </>
  665. )}
  666. </div>
  667. </div>
  668. <div class={styles.rightBtns}>
  669. <Popover
  670. v-model:show={data.popoverShow}
  671. class={styles.popover}
  672. actions={_actions.value}
  673. placement="top-start"
  674. onSelect={(item: any) => {
  675. data.showMusicImg = item.value;
  676. data.popoverShow = false;
  677. musicIframeLoad();
  678. }}
  679. // onSelect={onSelect}
  680. >
  681. {{
  682. reference: () => <img id="coai-1" src={icon_jianpuActive} />
  683. }}
  684. </Popover>
  685. {!isEnsemble.value && (
  686. <img id="coai-2" src={icon_down} onClick={handleSave} />
  687. )}
  688. <div class={styles.rightBtnsRight} id="coai-3">
  689. <img src={icons.icon_start} onClick={() => handleGoto()} />
  690. </div>
  691. </div>
  692. </div>
  693. </div>
  694. {data.searchNotice.width && data.searchNoticeShow && (
  695. <div class={styles.searchNotice} style={{ ...data.searchNotice }}>
  696. <NoticeBar
  697. text={musicForms.keyword}
  698. color="#333"
  699. background="none"
  700. />
  701. </div>
  702. )}
  703. {showGuide.value && <Coaiguide></Coaiguide>}
  704. <Popup
  705. class="popup-custom van-scale"
  706. transition="van-scale"
  707. closeOnClickOverlay={false}
  708. v-model:show={data.showVip}>
  709. <TheVip
  710. onClose={val => {
  711. if (val) {
  712. postMessage({
  713. api: 'openWebView',
  714. content: {
  715. url: `${location.origin}${location.pathname}#/member-center`,
  716. orientation: 1
  717. }
  718. });
  719. }
  720. data.showVip = false;
  721. }}
  722. />
  723. </Popup>
  724. <Popup
  725. class="popup-custom van-scale"
  726. transition="van-scale"
  727. closeOnClickOverlay={false}
  728. v-model:show={data.showVip}>
  729. <TheVip
  730. onClose={val => {
  731. if (val) {
  732. postMessage({
  733. api: 'openWebView',
  734. content: {
  735. url: `${location.origin}${location.pathname}#/member-center`,
  736. orientation: 1
  737. }
  738. });
  739. }
  740. data.showVip = false;
  741. }}
  742. />
  743. </Popup>
  744. <Popup
  745. class="popup-custom van-scale"
  746. transition="van-scale"
  747. closeOnClickOverlay={false}
  748. v-model:show={data.showChangeVoice}>
  749. <ChangeVoice
  750. musicalInstruments={
  751. data.trackList || []
  752. }
  753. musicalInstrumentIndex={data.selectMusicInstrumentIndex}
  754. onClose={() => (data.showChangeVoice = false)}
  755. onConfirm={ async (index: number) => {
  756. data.selectMusicInstrumentIndex = index;
  757. await analyzeXml();
  758. musicIframeLoad();
  759. data.showChangeVoice = false;
  760. }}
  761. />
  762. </Popup>
  763. </div>
  764. );
  765. }
  766. });