index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. import {
  2. TransitionGroup,
  3. defineComponent,
  4. nextTick,
  5. onMounted,
  6. reactive,
  7. ref
  8. } from 'vue';
  9. import styles from './index.module.less';
  10. import MSearch from '@/components/m-search';
  11. import icon_play from '@/common/images/icon_play.svg';
  12. import {
  13. Empty,
  14. List,
  15. Loading,
  16. NoticeBar,
  17. showLoadingToast,
  18. showToast
  19. } from 'vant';
  20. import icon_back from './image/icon_back.svg';
  21. import icon_down from '@/common/images/icon_down.svg';
  22. import icon_jianpu from '@/common/images/icon_jianpu.svg';
  23. import icon_jianpuActive from '@/common/images/icon_jianpuActive.svg';
  24. import icons from '@/common/images/index.json';
  25. import { postMessage, promisefiyPostMessage } from '@/helpers/native-message';
  26. import { rows } from './data.json';
  27. import html2canvas from 'html2canvas';
  28. import { api_musicSheetCategoriesPage, api_musicSheetPage } from './api';
  29. import { state } from '@/state';
  30. import MEmpty from '@/components/m-empty';
  31. export default defineComponent({
  32. name: 'co-ai',
  33. setup() {
  34. const categorForms = reactive({
  35. page: 1,
  36. rows: 999,
  37. subjectId: state.user.data?.subjectId || ''
  38. });
  39. const musicForms = reactive({
  40. page: 1,
  41. rows: 20,
  42. status: 1,
  43. keyword: '', // 关键词
  44. musicSheetCategoriesId: ''
  45. });
  46. const titles = rows;
  47. const data = reactive({
  48. /** 教材Index */
  49. typeIndex: 0,
  50. /** 音乐Index */
  51. musicIndex: 0,
  52. /** 显示简谱 */
  53. isShowJianpu: false,
  54. /** 教材列表 */
  55. types: [] as any[],
  56. /** 音乐列表 */
  57. musics: [] as any[],
  58. loading: true,
  59. finshed: false
  60. });
  61. const downRef = ref();
  62. // 返回
  63. const goback = () => {
  64. postMessage({ api: 'goBack' });
  65. };
  66. /** 去云教练 */
  67. const handleGoto = () => {
  68. let src = `${location.origin}/instrument?id=${
  69. data.musics[data.musicIndex]?.id
  70. }`;
  71. console.log(src)
  72. postMessage({
  73. api: 'openAccompanyWebView',
  74. content: {
  75. url: src,
  76. orientation: 0,
  77. isHideTitle: true,
  78. statusBarTextColor: false,
  79. isOpenLight: true
  80. }
  81. });
  82. };
  83. /** 保存图片 */
  84. const handleSave = async () => {
  85. showLoadingToast({ message: '正在保存', duration: 0 });
  86. try {
  87. html2canvas(downRef.value, {
  88. backgroundColor: '#fff',
  89. allowTaint: true,
  90. useCORS: true
  91. })
  92. .then(async canvas => {
  93. var dataURL = canvas.toDataURL('image/png', 1); //可选取多种模式
  94. setTimeout(() => {
  95. showToast('保存成功');
  96. }, 500);
  97. const res = await promisefiyPostMessage({
  98. api: 'savePicture',
  99. content: {
  100. base64: dataURL
  101. }
  102. });
  103. })
  104. .catch(() => {
  105. setTimeout(() => {
  106. showToast('保存失败');
  107. }, 500);
  108. });
  109. } catch (error) {
  110. setTimeout(() => {
  111. showToast('保存失败');
  112. }, 500);
  113. }
  114. };
  115. /** 获取音乐教材列表 */
  116. const getMusicSheetCategories = async () => {
  117. try {
  118. const res = await api_musicSheetCategoriesPage({
  119. ...categorForms
  120. });
  121. if (res.code === 200 && Array.isArray(res?.data?.rows)) {
  122. data.types = res.data.rows;
  123. if (!musicForms.musicSheetCategoriesId && data.types.length > 0) {
  124. musicForms.musicSheetCategoriesId = data.types[0].id;
  125. }
  126. }
  127. } catch (error) {
  128. console.log('🚀 ~ error:', error);
  129. }
  130. };
  131. /** 获取曲谱列表 */
  132. const getMusicList = async () => {
  133. data.loading = true;
  134. try {
  135. const res = await api_musicSheetPage({
  136. ...musicForms
  137. });
  138. if (res.code === 200 && Array.isArray(res?.data?.rows)) {
  139. data.musics = [...data.musics, ...res.data.rows];
  140. data.finshed = !res.data.next;
  141. }
  142. } catch (error) {
  143. console.log('🚀 ~ error:', error);
  144. }
  145. data.loading = false;
  146. };
  147. const handleReset = () => {
  148. musicForms.page = 1;
  149. data.musics = [];
  150. getMusicList();
  151. };
  152. const spinRef = ref();
  153. const handleResh = () => {
  154. if (data.loading || data.finshed) return;
  155. musicForms.page = musicForms.page + 1;
  156. getMusicList();
  157. };
  158. onMounted(async () => {
  159. await getMusicSheetCategories();
  160. getMusicList();
  161. const obv = new IntersectionObserver(entries => {
  162. if (entries[0].intersectionRatio > 0) {
  163. handleResh();
  164. }
  165. });
  166. nextTick(() => {
  167. obv.observe(spinRef.value);
  168. });
  169. });
  170. return () => (
  171. <div class={styles.container}>
  172. <div class={styles.back} onClick={goback}>
  173. <img src={icon_back} />
  174. </div>
  175. <div class={styles.content}>
  176. <div class={[styles.leftContent]}>
  177. <div class={styles.leftBg2}></div>
  178. <div class={styles.leftBg}></div>
  179. <div class={styles.types}>
  180. {data.types.map((item, index) => {
  181. return (
  182. <div
  183. class={[
  184. styles.type,
  185. musicForms.musicSheetCategoriesId === item.id &&
  186. styles.typeActive
  187. ]}
  188. onClick={() => {
  189. musicForms.musicSheetCategoriesId = item.id;
  190. handleReset();
  191. }}>
  192. <div class={styles.typeImg}>
  193. <img
  194. class={styles.typeIcon}
  195. src={item.coverImg}
  196. onLoad={(e: Event) => {
  197. const el = e.target as HTMLImageElement;
  198. el.setAttribute('loaded', 'true');
  199. }}
  200. />
  201. </div>
  202. </div>
  203. );
  204. })}
  205. </div>
  206. <div class={styles.center}>
  207. <MSearch
  208. shape="round"
  209. background="transparent"
  210. placeholder="请输入曲目名称"
  211. onSearch={val => {
  212. musicForms.keyword = val;
  213. handleReset();
  214. }}
  215. />
  216. <div class={styles.musicContent}>
  217. {data.musics.map((item: any, index: number) => {
  218. return (
  219. <div
  220. class={[
  221. styles.musicItem,
  222. data.musicIndex === index
  223. ? styles.musicActive
  224. : styles.disableNotic
  225. ]}
  226. onClick={() => (data.musicIndex = index)}>
  227. <img
  228. class={styles.musicAvtor}
  229. src={item.titleImg}
  230. onLoad={(e: Event) => {
  231. const el = e.target as HTMLImageElement;
  232. el.setAttribute('loaded', 'true');
  233. }}
  234. />
  235. <div class={styles.musicInfo}>
  236. <div class={styles.musicName}>
  237. <NoticeBar
  238. text={item.musicSheetName}
  239. color="#333"
  240. class={styles.noticeBar}
  241. background="none"
  242. />
  243. </div>
  244. <div class={styles.musicDes}>
  245. <div class={styles.musicFavitor}>{item.usedNum}</div>
  246. <div class={[styles.musicAuthor, 'van-ellipsis']}>
  247. {item.composer}
  248. </div>
  249. </div>
  250. </div>
  251. {/* <img class={[styles.musicIcon]} src={icon_play} /> */}
  252. </div>
  253. );
  254. })}
  255. {!data.finshed && (
  256. <div ref={spinRef} class={styles.loadingWrap}>
  257. <Loading color="#259CFE" />
  258. </div>
  259. )}
  260. {!data.loading && data.musics.length === 0 && (
  261. <div class={styles.empty}>
  262. <MEmpty description="暂无曲谱" />
  263. </div>
  264. )}
  265. </div>
  266. </div>
  267. </div>
  268. <div class={[styles.opacityBg, styles.right]}>
  269. <div ref={downRef}>
  270. <div class={styles['right-musicName']}>
  271. {data.musics[data.musicIndex]?.musicSheetName}
  272. </div>
  273. {data.isShowJianpu ? (
  274. <>
  275. <TransitionGroup name="van-fade">
  276. {data.musics[data.musicIndex]?.firstTone
  277. ?.split(',')
  278. .map((item: any, index: number) => {
  279. return (
  280. <img
  281. class={styles.staff}
  282. src={item + '?v=' + Date.now()}
  283. key={item}
  284. />
  285. );
  286. })}
  287. </TransitionGroup>
  288. </>
  289. ) : (
  290. <>
  291. <TransitionGroup name="van-fade">
  292. {data.musics[data.musicIndex]?.musicImg
  293. ?.split(',')
  294. .map((item: any, index: number) => {
  295. return (
  296. <img
  297. class={styles.staff}
  298. src={item + '?v=' + Date.now()}
  299. key={item}
  300. crossorigin="anonymous"
  301. />
  302. );
  303. })}
  304. </TransitionGroup>
  305. </>
  306. )}
  307. </div>
  308. <div class={styles.rightBtns}>
  309. <img
  310. src={data.isShowJianpu ? icon_jianpuActive : icon_jianpu}
  311. onClick={() => (data.isShowJianpu = !data.isShowJianpu)}
  312. />
  313. <img src={icon_down} onClick={handleSave} />
  314. <img src={icons.icon_start} onClick={() => handleGoto()} />
  315. </div>
  316. </div>
  317. </div>
  318. </div>
  319. );
  320. }
  321. });