index.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import { Skeleton } from "vant";
  2. import { defineComponent, onBeforeMount, onBeforeUnmount, onMounted, reactive, Transition } from "vue";
  3. import { formateTimes } from "../../helpers/formateMusic";
  4. import state, { isRhythmicExercises } from "../../state";
  5. import { setGlobalData } from "../../utils";
  6. import MusicScore, { resetMusicScore } from "../../view/music-score";
  7. import styles from "./index.module.less";
  8. import { api_cloudLoading, api_setStatusBarVisibility, isSpecialShapedScreen } from "/src/helpers/communication";
  9. import { getQuery } from "/src/utils/queryString";
  10. import { mappingVoicePart, subjectFingering } from "/src/view/fingering/fingering-config";
  11. import { musicPracticeRecordGetLastEvaluationMusicalNotesPlayStats, sysMusicScoreAccompanimentQueryPage } from "./api";
  12. import ShareTop from "./share-top";
  13. import Note from "./note";
  14. import { addMeasureScore } from "/src/view/evaluating";
  15. const colorsClass: any = {
  16. RIGHT: styles.right,
  17. WRONG: styles.wrong,
  18. NOT_PLAY: styles.notPlay,
  19. CADENCE_WRONG: styles.cadence_wrong,
  20. INTONATION_WRONG: styles.intonation_wrong,
  21. INTEGRITY_WRONG: styles.integrity_wrong,
  22. };
  23. export default defineComponent({
  24. name: "music-list",
  25. setup() {
  26. const query: any = getQuery();
  27. const scoreData: any = reactive({
  28. videoFilePath: "", // 回放视频路径
  29. cadence: 0,
  30. integrity: 0,
  31. intonation: 0,
  32. score: 0,
  33. heardLevel: "",
  34. });
  35. const detailData = reactive({
  36. isLoading: true,
  37. paddingLeft: "",
  38. headerHide: false,
  39. musicalNotesPlayStats: [] as any[],
  40. userMeasureScore: {} as any,
  41. });
  42. const getAPPData = async () => {
  43. const screenData = await isSpecialShapedScreen();
  44. if (screenData?.content) {
  45. // console.log("🚀 ~ screenData:", screenData.content);
  46. const { isSpecialShapedScreen, notchHeight } = screenData.content;
  47. if (isSpecialShapedScreen) {
  48. detailData.paddingLeft = 25 + "px";
  49. }
  50. }
  51. };
  52. onBeforeMount(() => {
  53. getAPPData();
  54. api_setStatusBarVisibility();
  55. });
  56. // console.log(route.params, query)
  57. /** 获取曲谱数据 */
  58. const getMusicInfo = (res: any) => {
  59. const index = state.partIndex;
  60. const musicInfo = {
  61. ...res.data,
  62. ...res.data.background[index],
  63. };
  64. // console.log("🚀 ~ musicInfo:", musicInfo);
  65. setState(musicInfo, index);
  66. setCustom();
  67. detailData.isLoading = false;
  68. };
  69. const setState = (data: any, index: number) => {
  70. // console.log("🚀 ~ data:", data)
  71. state.scrollContainer = "scrollContainer";
  72. state.detailId = data.id;
  73. state.xmlUrl = data.xmlFileUrl;
  74. state.partIndex = index;
  75. state.subjectId = data.musicSubject;
  76. state.categoriesId = data.categoriesId;
  77. state.categoriesName = data.musicTagNames;
  78. state.enableEvaluation = data.canEvaluate ? true : false;
  79. state.examSongId = data.id + "";
  80. state.examSongName = data.musicSheetName;
  81. // 解析扩展字段
  82. if (data.extConfigJson) {
  83. try {
  84. state.extConfigJson = JSON.parse(data.extConfigJson as string);
  85. } catch (error) {
  86. console.error("解析扩展字段错误:", error);
  87. }
  88. }
  89. state.isOpenMetronome = data.mp3Type === "MP3_METRONOME" ? true : false;
  90. state.needTick = data.isOpenMetronome;
  91. state.isShowFingering = data.showFingering ? true : false;
  92. state.music = data.audioFileUrl;
  93. state.accompany = data.metronomeUrl || data.metronomeUrl;
  94. state.midiUrl = data.midiUrl;
  95. state.parentCategoriesId = data.musicTag;
  96. state.playMode = data.audioType === "MP3" ? "mp3" : "midi";
  97. state.originSpeed = state.speed = data.speed;
  98. state.track = data.track;
  99. state.enableNotation = data.notation ? true : false;
  100. // 映射声部ID
  101. state.subjectId = mappingVoicePart(state.subjectId as any, "ORCHESTRA");
  102. // console.log("🚀 ~ state.subjectId:", state.subjectId);
  103. // 是否打击乐
  104. state.isPercussion = state.subjectId == 23 || state.subjectId == 113 || state.subjectId == 121 || isRhythmicExercises();
  105. // 设置指法
  106. state.fingeringInfo = subjectFingering(state.subjectId);
  107. // console.log("🚀 ~ state.fingeringInfo:", state.fingeringInfo, state.subjectId, state.track)
  108. // state.isOpenPrepare = true
  109. };
  110. const setCustom = () => {
  111. if (state.extConfigJson.multitrack) {
  112. setGlobalData("multitrack", state.extConfigJson.multitrack);
  113. }
  114. };
  115. onMounted(async () => {
  116. (window as any).appName = "colexiu";
  117. const res = await musicPracticeRecordGetLastEvaluationMusicalNotesPlayStats(query.id);
  118. state.partIndex = Number(res?.data?.partIndex);
  119. detailData.musicalNotesPlayStats = res?.data?.musicalNotesPlayStats?.notesData || [];
  120. detailData.userMeasureScore = res?.data?.userMeasureScore || {};
  121. for(let key in scoreData){
  122. scoreData[key] = res?.data?.[key];
  123. }
  124. Promise.all([sysMusicScoreAccompanimentQueryPage(res?.data?.musicalNotesPlayStats?.examSongId)]).then((values) => {
  125. getMusicInfo(values[0]);
  126. });
  127. });
  128. const setPathColor = () => {
  129. for (const note of detailData.musicalNotesPlayStats) {
  130. const active = state.times[note.musicalNotesIndex];
  131. const svgEl = document.getElementById("vf-" + active.id);
  132. svgEl?.classList.add(colorsClass[note.musicalErrorType]);
  133. }
  134. };
  135. const setMearureColor = () => {
  136. for (let key in detailData.userMeasureScore) {
  137. addMeasureScore(detailData.userMeasureScore[key], false);
  138. }
  139. };
  140. /** 渲染完成 */
  141. const handleRendered = (osmd: any) => {
  142. state.musicRendered = true;
  143. state.osmd = osmd;
  144. state.times = formateTimes(osmd);
  145. console.log("🚀 ~ state.times:", state.times);
  146. setPathColor();
  147. setMearureColor();
  148. api_cloudLoading();
  149. };
  150. onMounted(() => {
  151. window.addEventListener("resize", resetMusicScore);
  152. });
  153. onBeforeUnmount(() => {
  154. window.removeEventListener("resize", resetMusicScore);
  155. });
  156. return () => (
  157. <div class={[styles.shareBox, styles.detail, state.setting.eyeProtection && "eyeProtection"]} style={{ paddingLeft: detailData.paddingLeft }}>
  158. {!state.musicRendered && (
  159. <div class={styles.skeleton}>
  160. <Skeleton class={styles.skeleton} row={8} />
  161. </div>
  162. )}
  163. <div class={[styles.headHeight, detailData.headerHide && styles.headHide]} onClick={(e: Event) => e.stopPropagation()}>
  164. <Transition name="van-slide-down">{state.musicRendered && <ShareTop scoreData={scoreData} />}</Transition>
  165. </div>
  166. <div id="scrollContainer" class={[styles.container, !state.setting.displayCursor && "hideCursor"]}>
  167. <div class={styles.demos}>
  168. <div>
  169. <Note fill="#01C1B5" />
  170. <span>演奏正确</span>
  171. </div>
  172. <div>
  173. <Note fill="#067DD7" />
  174. <span>节奏错误</span>
  175. </div>
  176. {!state.isPercussion ? (
  177. <>
  178. <div>
  179. <Note fill="#FFAB25" />
  180. <span>音准错误</span>
  181. </div>
  182. <div>
  183. <Note fill="#CC75FF" />
  184. <span>完成度不足</span>
  185. </div>
  186. </>
  187. ) : null}
  188. <div>
  189. <Note fill="#000" />
  190. <span>未演奏</span>
  191. </div>
  192. </div>
  193. {/* 曲谱渲染 */}
  194. {!detailData.isLoading && <MusicScore onRendered={handleRendered} />}
  195. </div>
  196. </div>
  197. );
  198. },
  199. });