Browse Source

Merge branch 'feature-tianyong-newVersion' into feature-wxl-newVersion

lex 11 months ago
parent
commit
b260cf0039
43 changed files with 305 additions and 67 deletions
  1. 6 0
      src/helpers/communication.ts
  2. 4 2
      src/helpers/formateMusic.ts
  3. BIN
      src/page-instrument/component/the-music-list/imgs/empty.png
  4. 11 5
      src/page-instrument/component/the-music-list/index.module.less
  5. 6 1
      src/page-instrument/component/the-music-list/list.tsx
  6. 3 0
      src/page-instrument/custom-plugins/work-ealuating/index.tsx
  7. 2 1
      src/page-instrument/custom-plugins/work-home/index.tsx
  8. 14 1
      src/page-instrument/custom-plugins/work-index/index.tsx
  9. BIN
      src/page-instrument/evaluat-model/countdown/imgs/step1.png
  10. BIN
      src/page-instrument/evaluat-model/countdown/imgs/step2.png
  11. BIN
      src/page-instrument/evaluat-model/countdown/imgs/step3.png
  12. 1 0
      src/page-instrument/evaluat-model/evaluat-result/index.tsx
  13. 9 3
      src/page-instrument/evaluat-model/index.tsx
  14. BIN
      src/page-instrument/header-top/image/background1Act.png
  15. BIN
      src/page-instrument/header-top/image/backgroundAct.png
  16. 0 0
      src/page-instrument/header-top/image/glMode.json
  17. BIN
      src/page-instrument/header-top/image/icon_menuAct.png
  18. BIN
      src/page-instrument/header-top/image/icon_pause.png
  19. 0 0
      src/page-instrument/header-top/image/lxMode.json
  20. BIN
      src/page-instrument/header-top/image/mingsongAct.png
  21. BIN
      src/page-instrument/header-top/image/music1Act.png
  22. BIN
      src/page-instrument/header-top/image/musicAct.png
  23. 0 0
      src/page-instrument/header-top/image/pcMode.json
  24. BIN
      src/page-instrument/header-top/image/performAct.png
  25. BIN
      src/page-instrument/header-top/image/shengguiAct.png
  26. BIN
      src/page-instrument/header-top/image/singAct.png
  27. BIN
      src/page-instrument/header-top/image/tickoffAct.png
  28. BIN
      src/page-instrument/header-top/image/tickonAct.png
  29. 103 16
      src/page-instrument/header-top/index.module.less
  30. 42 13
      src/page-instrument/header-top/index.tsx
  31. 10 0
      src/page-instrument/header-top/speed/index.module.less
  32. 33 1
      src/page-instrument/header-top/speed/index.tsx
  33. 4 2
      src/page-instrument/view-detail/index.tsx
  34. 2 2
      src/page-instrument/view-detail/smoothAnimation/bird/index.module.less
  35. 2 2
      src/page-instrument/view-detail/smoothAnimation/index.less
  36. 3 3
      src/page-instrument/view-detail/smoothAnimation/index.ts
  37. 5 2
      src/page-instrument/view-evaluat-report/index.tsx
  38. 16 7
      src/state.ts
  39. 3 0
      src/view/audio-list/index.tsx
  40. 1 0
      src/view/evaluating/index.tsx
  41. 10 1
      src/view/fingering/fingering-config.ts
  42. 12 4
      src/view/follow-practice/index.tsx
  43. 3 1
      src/view/selection/index.tsx

+ 6 - 0
src/helpers/communication.ts

@@ -266,6 +266,12 @@ export const api_createMusicPlayer = (content: any) => {
 	return promisefiyPostMessage({ api: "createMusicPlayer", content });
 };
 
+/** 初始化曲谱音频 和效音音频 */
+export const api_updateMusicPlayer = (content: any) => {
+	if (!storeData.isApp) return Promise.resolve({} as any);
+	return promisefiyPostMessage({ api: "createMusicPlayer", content });
+};
+
 /** 进入页面设置常量 */
 export const api_keepScreenLongLight = () => {
 	postMessage({

+ 4 - 2
src/helpers/formateMusic.ts

@@ -1163,7 +1163,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				if (["2670"].includes(state.cbsExamSongId)) {
 					// fixtime -= _firstMeasureRealValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
 				} else {
-					if (difftime > 0) {
+					if (difftime > 0 && !state.isEvxml) {
 						fixtime += difftime;
 						state.fixtime = fixtime;
 					}
@@ -1218,6 +1218,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			if (svgElement?.modifiers?.length) {
 				hasGraceNote = svgElement?.modifiers.some((item: any) => item?.attrs?.type === "GraceNoteGroup")
 			}
+			const filterRepeatIdx = allNotes.filter((item: any) => item.noteId === note.NoteToGraphicalNoteObjectId).length
 			const nodeDetail = {
 				isStaccato: note.voiceEntry.isStaccato(),
 				isRestFlag: note.isRestFlag,
@@ -1265,7 +1266,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				totalMultipleRestMeasures, // 当前小节总的合并小节数
 				measureSpeed,  // 小节速度
 				maxNoteNum: note.maxNoteNum, // 当前小节音符最多的分轨的音符数量
-				repeatIdx: iterator.repeatIdx || 0, // 标记是第几遍循环,从0开始
+				// repeatIdx: iterator.repeatIdx || 0, // 标记是第几遍循环,从0开始
+				repeatIdx: filterRepeatIdx,
 				xmlNoteTime: retain(xmlNoteTime), // xml上音符开始时间 唱名用
 				xmlNoteEndTime: retain(xmlNoteTime + noteLength), //xml上音符结束时间 唱名用
 				xmlMp3BeatFixTime,  //xml上节拍器的时间

BIN
src/page-instrument/component/the-music-list/imgs/empty.png


+ 11 - 5
src/page-instrument/component/the-music-list/index.module.less

@@ -216,11 +216,17 @@
         }
     }
 }
-.noData {
+.empty{
+    margin-top: 10px;
     display: flex;
-    justify-content: center;
+    flex-direction: column;
     align-items: center;
-    height: 100%;
-    font-size: 14px;
-    color: #999999;
+    >img{
+        width: 182px;
+    }
+    >span{
+        font-size: 14px;
+        color: rgba(0,0,0,0.46);
+        margin-top: 10px;
+    }
 }

+ 6 - 1
src/page-instrument/component/the-music-list/list.tsx

@@ -7,6 +7,7 @@ import { postMessage } from "/src/utils/native-message";
 import qs from "query-string";
 import searImg from "./imgs/searImg.png"
 import huoimg from "./imgs/huo.png"
+import emptyImg from "./imgs/empty.png"
 
 export default defineComponent({
   name: "TheMusicList-list",
@@ -122,7 +123,11 @@ export default defineComponent({
               </div>
             );
           })}
-          {!data.loading && data.list.length === 0 && <div class={styles.noData}>暂无数据</div>}
+          {!data.loading && data.list.length === 0 
+            &&  <div class={styles.empty}>
+                  <img src={emptyImg}/>
+                  <span>暂无内容</span>
+                </div>}
         </List>
       </div>
     );

+ 3 - 0
src/page-instrument/custom-plugins/work-ealuating/index.tsx

@@ -80,6 +80,9 @@ export default defineComponent({
 			getWorkData();
 			// verifyMembershipServices();
 		});
+		expose({
+			getWorkData
+		})		
 		return () => <div></div>;
 	},
 });

+ 2 - 1
src/page-instrument/custom-plugins/work-home/index.tsx

@@ -102,7 +102,8 @@ export default defineComponent({
 			// verifyMembershipServices();
 		});
 		expose({
-			handleAdd
+			handleAdd,
+			getWorkData
 		})
 		return () => (
 			<div class={styles.homework}>

+ 14 - 1
src/page-instrument/custom-plugins/work-index/index.tsx

@@ -5,8 +5,10 @@ import { getQuery } from "/src/utils/queryString";
 import { api_lessonTrainingTrainingStudentDetail } from "../../api";
 import { headTopData } from "../../header-top";
 import { evaluatingData } from "/src/view/evaluating";
+import state from "/src/state";
 
 const workHomeRef = ref();
+const workEvaluatRef = ref();
 
 export const data = reactive({
 	/** 作业类型:练习PRACTICE, 评测EVALUATION */
@@ -20,6 +22,17 @@ export const HANDLE_WORK_ADD = () => {
 	}
 };
 
+// 刷新谱面后,设置作业选段
+export const resetSection = () => {
+	if (data.trainingType === "PRACTICE"){
+		workHomeRef.value?.getWorkData();
+	}
+	if (data.trainingType === "EVALUATION") {
+		workHomeRef.value?.getWorkData();
+	}
+	state.workSectionNeedReset = false;
+};
+
 export default defineComponent({
 	name: "workIndex",
 	setup(props) {
@@ -56,7 +69,7 @@ export default defineComponent({
 				{data.trainingType === "PRACTICE" && <WorkHome ref={workHomeRef} workeData={data.worke} />}
 				{/* 评测作业 */}
 				{data.trainingType === "EVALUATION" && (
-					<WorkEaluating workeData={data.worke} />
+					<WorkEaluating ref={workEvaluatRef} workeData={data.worke} />
 				)}
 			</>
 		);

BIN
src/page-instrument/evaluat-model/countdown/imgs/step1.png


BIN
src/page-instrument/evaluat-model/countdown/imgs/step2.png


BIN
src/page-instrument/evaluat-model/countdown/imgs/step3.png


+ 1 - 0
src/page-instrument/evaluat-model/evaluat-result/index.tsx

@@ -75,6 +75,7 @@ export default defineComponent({
       if (res?.code === 200) {
         evaluatingData.resultData.recordId = res.data;
       }
+      evaluatingData.needReplayEvaluat = evaluatingData.oneselfCancleEvaluating ? true : false;
       data.saveLoading = false;
     };
 

+ 9 - 3
src/page-instrument/evaluat-model/index.tsx

@@ -332,6 +332,8 @@ export default defineComponent({
         evaluatingData.oneselfCancleEvaluating = true;
         // handleCancelEvaluat();
         handleEndEvaluat(true, 'selfCancel');
+        evaluatingData.isBeginMask = true;
+        state.playState = "paused";
       }
       resetPlaybackToStart();
       evaluatingData.resulstMode = false;
@@ -370,6 +372,7 @@ export default defineComponent({
     };
 
     const startBtnHandle = async () => {
+      evaluatingData.needReplayEvaluat = false;
       // 选段未完成时,清除选段状态
       if (state.sectionStatus && state.section.length < 2) {
         clearSelection();
@@ -448,7 +451,7 @@ export default defineComponent({
 
     // 手动取消评测,需要自动再次评测
     watch(
-      () => evaluatingData.hideResultModal,
+      () => evaluatingData.needReplayEvaluat,
       (val) => {
         if (val && evaluatingData.oneselfCancleEvaluating) {
           setTimeout(() => {
@@ -485,7 +488,7 @@ export default defineComponent({
     return () => (
       <div>
         <div class={styles.operatingBtn}>
-          {evaluatingData.websocketState && !evaluatingData.startBegin && evaluatingData.checkEnd && (
+          {evaluatingData.websocketState && !evaluatingData.startBegin && (
             <img
               class={[styles.iconBtn, "evaluting-1"]}
               src={headImg("icon_play.png")}
@@ -540,7 +543,10 @@ export default defineComponent({
             onClose={() => {
               clearTimeout(checkErjiTimer);
               checkErjiTimer = null;
-              evaluatingData.earphoneMode = false;
+              // #11035,可能刚好关闭耳机弹窗的时候,第二次又出现了弹窗
+              setTimeout(() => {
+                evaluatingData.earphoneMode = false;
+              }, 0);
               // handlePerformDetection();
               checkEarphoneStatus("start");
             }}

BIN
src/page-instrument/header-top/image/background1Act.png


BIN
src/page-instrument/header-top/image/backgroundAct.png


File diff suppressed because it is too large
+ 0 - 0
src/page-instrument/header-top/image/glMode.json


BIN
src/page-instrument/header-top/image/icon_menuAct.png


BIN
src/page-instrument/header-top/image/icon_pause.png


File diff suppressed because it is too large
+ 0 - 0
src/page-instrument/header-top/image/lxMode.json


BIN
src/page-instrument/header-top/image/mingsongAct.png


BIN
src/page-instrument/header-top/image/music1Act.png


BIN
src/page-instrument/header-top/image/musicAct.png


File diff suppressed because it is too large
+ 0 - 0
src/page-instrument/header-top/image/pcMode.json


BIN
src/page-instrument/header-top/image/performAct.png


BIN
src/page-instrument/header-top/image/shengguiAct.png


BIN
src/page-instrument/header-top/image/singAct.png


BIN
src/page-instrument/header-top/image/tickoffAct.png


BIN
src/page-instrument/header-top/image/tickonAct.png


+ 103 - 16
src/page-instrument/header-top/index.module.less

@@ -56,16 +56,20 @@
         margin-left: 10px;
         &.isMusicList{
             :global{
-                .van-notice-bar .van-notice-bar__content::after{
-                    position: absolute;
-                    top: 50%;
-                    right: 0;
-                    transform: translateY(-50%);
-                    content: "";
-                    width: 11px;
-                    height: 6px;
-                    background: url("./image/sj.png") no-repeat;
-                    background-size: 100% 100%;                         
+                .van-notice-bar{
+                    &::after{
+                        max-width: calc(216px + 16px);
+                        position: absolute;
+                        top: 0;
+                        left: 0;
+                        content: "";
+                        width: calc(var(--noticeBarWidth,100%) + 16px);
+                        height: 100%;
+                        background: url("./image/sj.png") no-repeat;
+                        background-size: 9px 6px;
+                        background-position: center right;                   
+                    }
+                    
                 }
             }
         }
@@ -77,10 +81,6 @@
                 font-weight: 600;
                 font-size: 18px;
                 color: #FFFFFF;
-                .van-notice-bar__content{
-                    position: relative;
-                    padding-right: 16px;
-                }
             }
         }
     }
@@ -135,6 +135,93 @@
             color: #FFFFFF;
             line-height: 17px;
         }
+        &:active{
+            >span{
+                color: #34D6FF
+            };
+        }
+        &.playType:active{
+            >img:nth-child(1){
+                content: url("./image/performAct.png");
+            }            
+            >img:nth-child(2){
+                content: url("./image/singAct.png");
+            }
+        }
+        &.playSource:active{
+            >img:nth-child(1){
+                content: url("./image/musicAct.png");
+            }            
+            >img:nth-child(2){
+                content: url("./image/backgroundAct.png");
+            }            
+        }      
+        &.songSource:active{
+            >img:nth-child(1){
+                content: url("./image/music1Act.png");
+            }            
+            >img:nth-child(2){
+                content: url("./image/background1Act.png");
+            }            
+            >img:nth-child(3){
+                content: url("./image/mingsongAct.png");
+            }
+        }
+        &.section:active{
+            >img{
+                content: url("./image/section2.png");
+            }
+        }
+        &.isSection{
+            >span{
+                color: #34D6FF
+            };
+        }
+        &.speed:active{
+            >img:nth-child(1){
+                content: url("./image/tickonAct.png");
+            }            
+            >img:nth-child(2){
+                content: url("./image/tickoffAct.png");
+            } 
+        }
+        &.isSpeed{
+            >img:nth-child(1){
+                content: url("./image/tickonAct.png");
+            }            
+            >img:nth-child(2){
+                content: url("./image/tickoffAct.png");
+            } 
+            >span{
+                color: #34D6FF
+            }; 
+        }      
+        &.settingMode:active{
+            >img{
+                content: url("./image/icon_menuAct.png");
+            }            
+        }  
+        &.isSettingMode{
+            >img{
+                content: url("./image/icon_menuAct.png");
+            }            
+            >span{
+                color: #34D6FF
+            }; 
+        }  
+        &.musicSheet:active{
+            >img{
+                content: url("./image/shengguiAct.png");
+            }            
+        }       
+        &.isMusicSheet{
+            >img{
+                content: url("./image/shengguiAct.png");
+            }            
+            >span{
+                color: #34D6FF
+            }; 
+        }
     }
     .metronomeBtn{
         position: relative;
@@ -202,8 +289,8 @@
         left: 50%;
         top: 50%;
         transform: translate(-50%, -50%);
-        width: 85%;
-        height: 85%;
+        width: 43px;
+        height: 43px;
     }
 }
 

+ 42 - 13
src/page-instrument/header-top/index.tsx

@@ -1,4 +1,4 @@
-import { Transition, computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch, toRef, ComputedRef } from "vue";
+import { Transition, computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch, toRef, ComputedRef, nextTick } from "vue";
 import styles from "./index.module.less";
 
 import iconBack from "./image/icon-back.png";
@@ -494,7 +494,10 @@ export default defineComponent({
       /** 作业模式 end */
       if (state.defaultModeType == 1) {
         headTopData.handleChangeModeType("practise");
-        if (state.platform === IPlatform.PC || state.isPreView) {
+        // if (state.platform === IPlatform.PC || state.isPreView) {
+        //   headTopData.showBack = false;
+        // }
+        if (state.isPreView) {
           headTopData.showBack = false;
         }
       } else {
@@ -555,7 +558,16 @@ export default defineComponent({
     onUnmounted(() => {
       window.removeEventListener("message", changePlay);
     });
-
+    const noticeBarWidth = ref<number>()
+    watch(()=>smoothAnimationState.isShow.value, ()=>{
+      // NoticeBar能不能滚动
+      if(smoothAnimationState.isShow.value && isMusicList.value){
+        nextTick(()=>{
+          const widthCon = (document.querySelector("#noticeBarRollDom .van-notice-bar__content") as any)?.offsetWidth || undefined
+          noticeBarWidth.value = widthCon
+        })
+      }
+    },{ immediate: true })
     // 设置改变触发
     watch(state.setting, () => {
       console.log(state.setting, "state.setting");
@@ -607,10 +619,17 @@ export default defineComponent({
         >
           {/* 返回和标题 */}
           {!(state.playState == "play" || followData.start || evaluatingData.startBegin) && (
-            <div class={styles.headTopLeftBox}>
+            <div id="noticeBarRollDom" class={styles.headTopLeftBox}>
               <img src={iconBack} class={["headTopBackBtn", styles.img, !headTopData.showBack && styles.hidenBack]} onClick={handleBack} />
               {smoothAnimationState.isShow.value ? (
                 <div
+                  style={
+                    noticeBarWidth.value
+                      ? {
+                          "--noticeBarWidth": noticeBarWidth.value + "px",
+                        }
+                      : {}
+                  }
                   class={[styles.title, isMusicList.value && styles.isMusicList, "driver-8"]}
                   onClick={() => {
                     isMusicList.value && (musicListShow.value = true);
@@ -631,8 +650,8 @@ export default defineComponent({
               )}
             </div>
           )}
-          {/* 模式切换 */}
 
+          {/* 模式切换 */}
           <div
             id={state.platform === IPlatform.PC ? "teacherTop-0" : "studnetT-0"}
             style={{ display: toggleBtn.value.display ? "" : "none" }}
@@ -689,7 +708,7 @@ export default defineComponent({
             ) : null} */}
             <div
               style={{ display: playTypeBtn.value.display ? "" : "none" }}
-              class={["driver-2", styles.btn, playTypeBtn.value.disabled && styles.disabled]}
+              class={["driver-2", styles.btn, playTypeBtn.value.disabled && styles.disabled, styles.playType]}
               onClick={() => {
                 const oldPlayType = state.playType;
                 const oldPlaySource = state.playSource;
@@ -735,7 +754,7 @@ export default defineComponent({
             <div
               id={state.platform === IPlatform.PC ? "teacherTop-1" : "studnetT-1"}
               style={{ display: originBtn.value.display ? "" : "none" }}
-              class={["driver-3", styles.btn, originBtn.value.disabled && styles.disabled]}
+              class={["driver-3", styles.btn, originBtn.value.disabled && styles.disabled, state.playType === "play" ? styles.playSource : styles.songSource]}
               onClick={() => {
                 const oldPlayType = state.playType;
                 const oldPlaySource = state.playSource;
@@ -763,7 +782,12 @@ export default defineComponent({
               <img style={{ display: state.playSource === "mingSong" ? "" : "none" }} class={styles.iconBtn} src={headImg(`mingsong.png`)} />
               <span>{state.playSource === "music" ? (state.playType === "play" ? "原声" : "范唱") : state.playSource === "background" ? (state.playType === "play" ? "伴奏" : "伴唱") : "唱名"}</span>
             </div>
-            <div id={state.platform === IPlatform.PC ? "teacherTop-2" : "studnetT-2"} style={{ display: selectBtn.value.display ? "" : "none" }} class={["driver-4", styles.btn, selectBtn.value.disabled && styles.disabled]} onClick={() => handleChangeSection()}>
+            <div
+              id={state.platform === IPlatform.PC ? "teacherTop-2" : "studnetT-2"}
+              style={{ display: selectBtn.value.display ? "" : "none" }}
+              class={["driver-4", styles.btn, selectBtn.value.disabled && styles.disabled, styles.section, state.sectionStatus && styles.isSection]}
+              onClick={() => handleChangeSection()}
+            >
               <img style={{ display: state.section.length === 0 ? "" : "none" }} class={styles.iconBtn} src={headImg(`section0.png`)} />
               <img style={{ display: state.section.length === 1 ? "" : "none" }} class={styles.iconBtn} src={headImg(`section1.png`)} />
               <img style={{ display: state.section.length === 2 ? "" : "none" }} class={styles.iconBtn} src={headImg(`section2.png`)} />
@@ -773,7 +797,7 @@ export default defineComponent({
               <>
                 <div
                   style={{ display: metronomeBtn.value.display ? "" : "none" }}
-                  class={["driver-5", styles.btn, styles.metronomeBtn, metronomeBtn.value.disabled && styles.disabled]}
+                  class={["driver-5", styles.btn, styles.metronomeBtn, metronomeBtn.value.disabled && styles.disabled, headData.speedShow && styles.isSpeed, styles.speed]}
                   onClick={async () => {
                     headData.speedShow = !headData.speedShow;
                   }}
@@ -817,7 +841,7 @@ export default defineComponent({
             ) : null} */}
             {state.musicRendered && !query.lessonTrainingId && !query.questionId && state.isConcert && (
               <div
-                class={[styles.btn, state.playState === "play" && fingeringBtn.value.disabled && styles.disabled, "driver-10"]}
+                class={[styles.btn, state.playState === "play" && fingeringBtn.value.disabled && styles.disabled, toggleMusicSheet.show && styles.isMusicSheet, styles.musicSheet,  "driver-10"]}
                 onClick={() => {
                   toggleMusicSheet.toggle(true);
                 }}
@@ -826,7 +850,12 @@ export default defineComponent({
                 <span>声部</span>
               </div>
             )}
-            <div id={state.platform === IPlatform.PC ? "teacherTop-6" : "studnetT-6"} style={{ display: settingBtn.value.display ? "" : "none" }} class={["driver-6", styles.btn, settingBtn.value.disabled && styles.disabled]} onClick={() => (headTopData.settingMode = true)}>
+            <div
+              id={state.platform === IPlatform.PC ? "teacherTop-6" : "studnetT-6"}
+              style={{ display: settingBtn.value.display ? "" : "none" }}
+              class={["driver-6", styles.btn, settingBtn.value.disabled && styles.disabled, headTopData.settingMode && styles.isSettingMode, styles.settingMode]}
+              onClick={() => (headTopData.settingMode = true)}
+            >
               <img class={styles.iconBtn} src={headImg("icon_menu.png")} />
               <span>设置</span>
             </div>
@@ -883,9 +912,9 @@ export default defineComponent({
               subjectStatus: state.musicRendered && !query.lessonTrainingId && !query.questionId && state.isConcert,
               modelTypeStatus: toggleBtn.value.display,
               playType: playTypeBtn.value.display,
-              originPlayType: state.playType === 'play' ? true : false,
+              originPlayType: state.playType === "play" ? true : false,
               backTitle: !(state.playState == "play" || followData.start || evaluatingData.startBegin),
-              titleType: smoothAnimationState.isShow.value ? "TEXT" : isMusicList.value ? 'IMG' : 'NONE'
+              titleType: smoothAnimationState.isShow.value ? "TEXT" : isMusicList.value ? "IMG" : "NONE",
             }}
           />
         )}

+ 10 - 0
src/page-instrument/header-top/speed/index.module.less

@@ -176,6 +176,16 @@
                         }
                     }
                 }
+                .switchLoading {
+                    :global {
+                        .van-switch__node{
+                            display: block;
+                            width: 16px;
+                            height: 16px;
+                            // top: 3px;
+                        }
+                    }
+                }
             }
         }
     }

+ 33 - 1
src/page-instrument/header-top/speed/index.tsx

@@ -6,11 +6,14 @@ import { headImg } from "../image";
 import state, { handleSetSpeed, resetBaseRate } from "../../../state";
 import { metronomeData } from "../../../helpers/metronome"; 
 import { getQuery } from "/src/utils/queryString";
+import { api_createMusicPlayer, api_updateMusicPlayer } from "/src/helpers/communication";
+import { storeData } from "/src/store";
 
 export default defineComponent({
 	name: "speed",
 	setup() {
 		const speed = ref(state.speed);
+		const switchLoading = ref(false);
 		const query: any = getQuery();
 		const minusSpeed = () => {
 			let canSpeed = Math.max(speed.value - 1, 45);
@@ -50,6 +53,30 @@ export default defineComponent({
 				metronomeData.disable = !val
 			}
 		})
+		const toggleSwitch = async (res: any) => {
+			switchLoading.value = true;
+			try {
+			  // 模拟异步操作,例如 API 调用
+			  console.log(123567,res)
+			  if (storeData.isApp) {
+				const targetSrc = res ? state.beatSong.accompany || state.beatSong.music : state.accompany || state.music;
+				api_updateMusicPlayer({
+					musicSrc: targetSrc || state.accompany || state.music, // 曲谱音频url,有可能含节拍器的音频不存在
+					tuneSrc: "https://oss.dayaedu.com/MECMP/1722593665681.mp3", //效音音频url
+					checkFrequence: 496,
+				})
+				metronomeDisable.value = res;
+				switchLoading.value = false;
+			  } else {
+				metronomeDisable.value = res;
+				switchLoading.value = false;
+			  }
+			} catch (error) {
+			  console.log(error)
+			} finally {
+				//switchLoading.value = false;
+			}
+		  };
 		return () => (
 			<div class={[styles.speedContainer, styles[state.modeType]]}>
 				<div class={styles.head}>
@@ -84,7 +111,12 @@ export default defineComponent({
 						</div>
 						<div class={styles.metronome}>
 							<div class={styles.tit}>节拍器</div>
-							<Switch v-model={metronomeDisable.value}></Switch>
+							<Switch 
+								class={switchLoading.value ? styles.switchLoading : ''}
+								v-model:modelValue={metronomeDisable.value} 
+								loading={switchLoading.value}
+								onChange={toggleSwitch}			
+							></Switch>
 						</div>
 					</div>
 				</div>

+ 4 - 2
src/page-instrument/view-detail/index.tsx

@@ -22,7 +22,7 @@ import Tick, { handleInitTick } from "/src/view/tick";
 import FollowPractice, { followData } from "/src/view/follow-practice";
 import FollowModel from "../follow-model";
 import RecordingTime from "../custom-plugins/recording-time";
-import WorkIndex from "../custom-plugins/work-index";
+import WorkIndex, { resetSection } from "../custom-plugins/work-index";
 import TheMusicList, { isMusicList } from "../component/the-music-list";
 import { storeData } from "/src/store";
 import ViewFigner from "../view-figner";
@@ -277,7 +277,9 @@ export default defineComponent({
 
       evaluatCreateMusicPlayer();
       resetPlaybackToStart();
-
+      if (state.workSectionNeedReset) {
+        resetSection();
+      }
       // pushAppMusic();
       // console.timeEnd("渲染加载耗时");
     };

+ 2 - 2
src/page-instrument/view-detail/smoothAnimation/bird/index.module.less

@@ -3,7 +3,7 @@
 }
 .note{
     position: absolute;
-    width: 40Px;
+    width: 54Px;
     top: -16Px;
-    left: -28Px;
+    left: -38Px;
 }

+ 2 - 2
src/page-instrument/view-detail/smoothAnimation/index.less

@@ -12,8 +12,8 @@
         position: relative;
         .smoothBot{
             position: absolute;
-            width: 40Px;
-            height: 50Px;
+            width: 36Px;
+            height: 46Px;
             left: 0;
             top: 0;
         }

+ 3 - 3
src/page-instrument/view-detail/smoothAnimation/index.ts

@@ -162,8 +162,8 @@ export function moveSmoothAnimation(progress: number, activeIndex: number, isMov
    // 移动
    smoothAnimationMove(
       {
-         x: nowPointsPos.x - 20,
-         y: nowPointsPos.y - 25
+         x: nowPointsPos.x - 18,
+         y: nowPointsPos.y - 23
       },
       smoothAnimationState.pointsPos,
       smoothAnimationState.pointsPos.slice(0, nowIndex)
@@ -468,7 +468,7 @@ function createSmoothCurvePoints(pointsPos: pointsPosType, tension?: number, clo
  * 根据坐标划线
  */
 function drawSmoothCurve(context: CanvasRenderingContext2D, pointsPos: pointsPosType, progresspointsPos?: pointsPosType) {
-   context.lineWidth = 4
+   context.lineWidth = 2
    context.lineJoin = 'round';// 优化锯齿
    context.lineCap = 'round'; // 优化锯齿
    context.strokeStyle = "rgba(255,255,255,0.6)"

+ 5 - 2
src/page-instrument/view-evaluat-report/index.tsx

@@ -283,8 +283,11 @@ export default defineComponent({
            */
           // const isNeedCopyElement = scoreData.itemType === "integrity" ? false : ["HIGH", "LOW", "EARLY", "LATE"].includes(errType);
           const isNeedCopyElement = false;
+          // if (scoreData.itemType === "integrity") {
+          //   errType = errType = note.pitchAssessment.result === "HIGH" || note.pitchAssessment.result === "LOW" || note.pitchAssessment.result === "WRONG" ? "RIGHT" : errType;
+          // }
           if (scoreData.itemType === "integrity") {
-            errType = errType = note.pitchAssessment.result === "HIGH" || note.pitchAssessment.result === "LOW" || note.pitchAssessment.result === "WRONG" ? "RIGHT" : errType;
+            errType = errType = note.integrityAssessment.result === "NORMAL" ? "RIGHT" : note.integrityAssessment.result === "SHORT" ? "SHORT" : errType;
           }
           stemEl?.classList.add(colorsClass[errType]);
           svgEl?.classList.add(colorsClass[errType]);
@@ -402,7 +405,7 @@ export default defineComponent({
       allNote.value = formateTimes(osmd);
       console.log("🚀 ~ state.times:", allNote.value);
       // @ts-ignore
-      const startMeasureNum = detailData.musicalNotesPlayStats?.[0].measureRenderIndex, endMeasureNum = detailData.musicalNotesPlayStats?.last()?.measureRenderIndex;
+      const startMeasureNum = detailData.musicalNotesPlayStats?.[0]?.measureRenderIndex, endMeasureNum = detailData.musicalNotesPlayStats?.last()?.measureRenderIndex;
       allNote.value = allNote.value.filter((item: any) => (item.MeasureNumberXML >= startMeasureNum+1 && item.MeasureNumberXML <= endMeasureNum+1))
       // @ts-ignore
       const beams = Array.from(new Set(document.getElementsByClassName("vf-beam")));

+ 16 - 7
src/state.ts

@@ -547,6 +547,8 @@ const state = reactive({
   xmlHasLyric: false,
   /** 生成图片的模式 */
   isCreateImg: false,
+  /** 切换谱面后,作业选段是否需要刷新 */
+  workSectionNeedReset: false,
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -721,7 +723,8 @@ const handlePlaying = () => {
   // if (state.modeType !== "evaluating") {
   //   metronomeData.metro?.sound(currentTime);
   // }
-  metronomeData.metro?.sound(currentTime);
+  // 不需要播放节拍器的声音,因为音频带有节拍器的声音
+  // metronomeData.metro?.sound(currentTime);
   // 一行谱,需要滚动小节
   if (state.isSingleLine) {
     moveSmoothAnimationByPlayTime()
@@ -743,7 +746,8 @@ export const skipNotePlay = async (itemIndex: number, isStart = false) => {
     setAudioCurrentTime(itemTime, itemIndex);
     // 一行谱,点击音符,或者播放完成,需要跳转音符位置
     gotoNext(item, true);
-    metronomeData.metro?.sound(itemTime);
+    // 不需要播放节拍器的声音,因为音频带有节拍器的声音
+    // metronomeData.metro?.sound(itemTime);
     if (state.isAppPlay) {
       await api_cloudSetCurrentTime({
         currentTime: itemTime * 1000,
@@ -1322,7 +1326,7 @@ function xmlToTracks(xmlString: string) {
   const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
   return partNames.reduce((arr: string[], item) => {
     const textContent = item?.textContent?.trim()
-    if (textContent !== "COMMON" && textContent) {
+    if (textContent != "COMMON" && textContent != "common" && textContent) {
       arr.push(textContent)
     }
     return arr
@@ -1418,7 +1422,8 @@ const setState = (data: any, index: number) => {
   /**
    * 单曲,指法根据用户当前的乐器来显示,如果没有则取musicSheetSoundList第一个track
    */
-  let musicalCode = !storeData.user?.instrumentId ? data.musicSheetSoundList?.find((item:any)=>{ return item.audioPlayType === "PLAY" })?.track || '' : data.musicSheetSoundList?.find((item: any) => item?.musicalInstrumentId == storeData.user?.instrumentId && item.audioPlayType === "PLAY")?.track || '';
+  const currentInstrumentId = query.instrumentId || storeData.user?.instrumentId;
+  let musicalCode = !currentInstrumentId ? data.musicSheetSoundList?.find((item:any)=>{ return item.audioPlayType === "PLAY" })?.track || '' : data.musicSheetSoundList?.find((item: any) => item?.musicalInstrumentId == currentInstrumentId && item.audioPlayType === "PLAY")?.track || '';
   const pitchSubject = musicalInstrumentCodeInfo.find((n) => n.code.toLocaleLowerCase() === subjectCode.toLocaleLowerCase())
   const pitchMusical = musicalInstrumentCodeInfo.find((n) => n.code.toLocaleLowerCase() === musicalCode.toLocaleLowerCase())
   state.subjectCodeId = pitchSubject ? pitchSubject.id : 0
@@ -1530,9 +1535,9 @@ const setState = (data: any, index: number) => {
   let pitchTrack = null
   if (state.isConcert) {
     musicalCode = musicalInstrumentCodeInfo.find((item: any) => item.id === state.musicalCodeId)?.code
-    pitchTrack = data.musicalInstruments?.find((item: any) => item.code === musicalCode)
+    pitchTrack = data.musicalInstruments?.find((item: any) => item.code?.split(',')[0] === musicalCode)
   } else {
-    pitchTrack = data.musicalInstruments?.find((item: any) => item.code === musicalCode)
+    pitchTrack = data.musicalInstruments?.find((item: any) => item.code?.split(',')[0] === musicalCode)
   }
   let musicalRenderType = ''
   // if (pitchTrack?.defaultScore) {
@@ -1700,7 +1705,8 @@ export const fillWordColor = () => {
   })
   const currentLyrics: SVGAElement[] = Array.from(document.querySelectorAll(`.lyric${currentNote?.noteId}`));
   currentLyrics.forEach((lyric, index) => {
-    if (index === currentNote.repeatIdx) {
+    const lyricIndex = lyric.getAttribute('lyricIndex');
+    if ((index === currentNote.repeatIdx && currentNote.repeatIdx + 1 == lyricIndex) || (currentNote.repeatIdx > 0 && currentNote.formatLyricsEntries?.length === 1)) {
       lyric?.classList.add('lyricActive')
     }
   })
@@ -1864,6 +1870,9 @@ export const refreshMusicSvg = () => {
   clearSelection();
   resetBaseRate();
   state.activeMeasureIndex = -1;
+  if (query.workRecord) {
+    state.workSectionNeedReset = true;
+  }
   // 销毁旋律线
   destroySmoothAnimation()
   musicScoreRef.value?.refreshMusicScore()

+ 3 - 0
src/view/audio-list/index.tsx

@@ -94,6 +94,8 @@ export const getAudioDuration = () => {
 		const songEndTime = state.times[state.times.length - 1 || 0]?.endtime || 0
 		return audioData.duration || songEndTime;
 	}
+	// 唱名文件时长比较短,和普通文件不一样 所以这里单独处理
+	if(state.playSource === "mingSong") return audioData.mingSongEle?.duration || audioData.duration;
 	return audioData.songEle?.duration || audioData.backgroundEle?.duration || audioData.mingSongEle?.duration || audioData.duration;
 };
 
@@ -224,6 +226,7 @@ export default defineComponent({
 		// 监听评测曲谱音频播放进度,返回
 		const progress = (res: any) => {
 			const currentTime = res?.currentTime || res?.content?.currentTime;
+			console.log('app进度时间',currentTime)
 			const total = res?.totalDuration || res?.content?.totalDuration;
 			const time = currentTime / 1000;
 			audioData.progress = time;

+ 1 - 0
src/view/evaluating/index.tsx

@@ -111,6 +111,7 @@ export const evaluatingData = reactive({
   recordingTime: 0, // 调用startRecording的时间 
   endEvaluatingTime: 0, // 调用endEvaluating的时间 
   evaluatSpeed: 0, // 评测记录的速度
+  needReplayEvaluat: false, // 手动取消评测,需要自动开始评测
 });
 
 const sendOffsetTime = async (offsetTime: number) => {

+ 10 - 1
src/view/fingering/fingering-config.ts

@@ -273,6 +273,8 @@ export const matchVoicePart = (id: number | string, type: "SINGLE" | "CONCERT"):
       "Bass Clarinet in Bb": 4,
       Bassoon: 1,
       "Alto Saxophone": 5,
+      "Alto Saxophone1": 5,
+      "Alto Saxophone2": 5,
       "Tenor Saxophone": 5,
       "Baritone Saxophone": 5,
       "Trumpet in Bb 1": 12,
@@ -338,7 +340,14 @@ export const matchVoicePart = (id: number | string, type: "SINGLE" | "CONCERT"):
       code = code.toLocaleLowerCase().replace(/ /g, "");
       for (let sKey in subject) {
         let pitchKey = sKey;
-        if (typeof sKey === "string") pitchKey = pitchKey.toLocaleLowerCase().replace(/ /g, "");
+        if (typeof sKey === "string") {
+          pitchKey = pitchKey.toLocaleLowerCase().replace(/ /g, "");
+          pitchKey = pitchKey.replace(/[_0-9]+$/, '');
+        }
+        if (typeof sKey === "string") {
+          // 去掉声轨后面的数字
+          code = code.replace(/[_0-9]+$/, '');
+        }
         if (pitchKey === code) {
           _track = subject[sKey];
           break;

+ 12 - 4
src/view/follow-practice/index.tsx

@@ -21,7 +21,8 @@ export const followData = reactive({
 	rendered: false,
 	/** 麦克风权限 */
 	earphone: false,
-	isBeginMask: false // 倒计时和系统节拍器时候的遮罩,防止用户点击
+	isBeginMask: false, // 倒计时和系统节拍器时候的遮罩,防止用户点击
+	dontAccredit: true, // 没有开启麦克风权限,不需要调用结束收音的api
 });
 
 // 记录跟练时长
@@ -49,7 +50,10 @@ export const toggleFollow = (notCancel = true) => {
 	// 取消跟练
 	if (!notCancel) {
 		followData.start = false;
-		openToggleRecord(false);
+		// 开启了麦克风授权,才需要调用结束收音
+		if (storeData.isApp && !followData.dontAccredit) {
+			openToggleRecord(false);
+		}
 	}
 };
 
@@ -106,6 +110,7 @@ export const handleFollowStart = async () => {
 		followData.isBeginMask = false
 		followData.start = false;
 	} else {
+		followData.dontAccredit = false;
 		// 跟练模式开始前,增加播放系统节拍器
 		const tickend = await handleStartTick();
 		// console.log("🚀 ~ tickend:", tickend)
@@ -312,10 +317,13 @@ export default defineComponent({
 			console.log("进入跟练模式");
 		});
 		onUnmounted(() => {
-			api_cloudFollowTime(onFollowTime, false);
+			// api_cloudFollowTime(onFollowTime, false);
 			resetPlaybackToStart();
 			onClear();
-			openToggleRecord(false);
+			// 开启了麦克风授权,才需要调用结束收音
+			if (storeData.isApp && !followData.dontAccredit) {
+				openToggleRecord(false);
+			}
 			console.log("退出跟练模式");
 		});
 		return () => <div></div>;

+ 3 - 1
src/view/selection/index.tsx

@@ -1,5 +1,5 @@
 import { computed, defineComponent, onMounted, reactive, Transition, nextTick, watch } from "vue";
-import state, { EnumMusicRenderType, handleSelection, skipNotePlay, IPlatform } from "/src/state";
+import state, { EnumMusicRenderType, handleSelection, skipNotePlay, IPlatform, resetBaseRate } from "/src/state";
 import styles from "./index.module.less";
 import { metronomeData } from "/src/helpers/metronome";
 import { evaluatingData } from "../evaluating";
@@ -340,6 +340,8 @@ export default defineComponent({
 									// 如果选择了2个 删除左边的时候
 									if(state.section.length===2&&index === 0){
 										state.section = []
+										// 重置速度和播放倍率
+										resetBaseRate(state.activeNoteIndex);
 										showToast({
 											message: "请选择开始小节",
 											duration: 0,

Some files were not shown because too many files changed in this diff