Explorar o código

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

lex hai 1 ano
pai
achega
88d0dc95e8
Modificáronse 57 ficheiros con 510 adicións e 180 borrados
  1. 4 2
      src/helpers/customMusicScore.ts
  2. 3 1
      src/page-instrument/component/authorName/index.module.less
  3. 1 1
      src/page-instrument/component/authorName/index.tsx
  4. 10 8
      src/page-instrument/component/the-music-list/index.module.less
  5. 6 1
      src/page-instrument/component/the-music-list/list.tsx
  6. 11 0
      src/page-instrument/custom-plugins/helper-model/recommendation/index.module.less
  7. 2 2
      src/page-instrument/custom-plugins/helper-model/recommendation/index.tsx
  8. BIN=BIN
      src/page-instrument/evaluat-model/evaluat-result/img/arrow_icon.png
  9. 7 6
      src/page-instrument/evaluat-model/evaluat-result/index.module.less
  10. 5 2
      src/page-instrument/evaluat-model/index.tsx
  11. 3 0
      src/page-instrument/follow-model/index.module.less
  12. 2 1
      src/page-instrument/follow-model/index.tsx
  13. BIN=BIN
      src/page-instrument/header-top/image/headImg2.png
  14. BIN=BIN
      src/page-instrument/header-top/image/speedBtn.png
  15. 2 2
      src/page-instrument/header-top/index.module.less
  16. 10 3
      src/page-instrument/header-top/index.tsx
  17. 13 7
      src/page-instrument/header-top/modeView.tsx
  18. 10 0
      src/page-instrument/header-top/settting/index.module.less
  19. 21 1
      src/page-instrument/header-top/settting/index.tsx
  20. 12 1
      src/page-instrument/header-top/speed/index.module.less
  21. 1 1
      src/page-instrument/simple-detail/index.tsx
  22. BIN=BIN
      src/page-instrument/view-detail/images/bg1.png
  23. BIN=BIN
      src/page-instrument/view-detail/images/bg2.png
  24. BIN=BIN
      src/page-instrument/view-detail/images/bg2_left_zs.png
  25. BIN=BIN
      src/page-instrument/view-detail/images/bg2_right_zs.png
  26. BIN=BIN
      src/page-instrument/view-detail/images/bg3.png
  27. 0 0
      src/page-instrument/view-detail/images/index.json
  28. 21 0
      src/page-instrument/view-detail/index.module.less
  29. 24 8
      src/page-instrument/view-detail/index.tsx
  30. 1 0
      src/page-instrument/view-detail/smoothAnimation/index.less
  31. 3 2
      src/page-instrument/view-detail/smoothAnimation/index.ts
  32. BIN=BIN
      src/page-instrument/view-evaluat-report/component/share-top/image/icon-huifang.png
  33. BIN=BIN
      src/page-instrument/view-evaluat-report/component/share-top/image/icon-shiyi.png
  34. 2 2
      src/page-instrument/view-evaluat-report/component/share-top/index.module.less
  35. 9 18
      src/page-instrument/view-evaluat-report/component/share-top/index.tsx
  36. 27 7
      src/page-instrument/view-evaluat-report/index.module.less
  37. 16 7
      src/page-instrument/view-evaluat-report/index.tsx
  38. 109 33
      src/state.ts
  39. 3 1
      src/store.ts
  40. 1 1
      src/style.css
  41. 3 1
      src/view/abnormal-pop/index.module.less
  42. 50 7
      src/view/audio-list/index.tsx
  43. 1 1
      src/view/evaluating/index.module.less
  44. 12 11
      src/view/fingering/fingering-config.ts
  45. 0 0
      src/view/fingering/fingering-img/baroque-recorder/index.json
  46. 0 0
      src/view/fingering/fingering-img/ocarina/index.json
  47. 0 0
      src/view/fingering/fingering-img/piccolo/index.json
  48. 0 0
      src/view/fingering/fingering-img/whistling/index.json
  49. 7 1
      src/view/fingering/index.tsx
  50. 3 0
      src/view/music-score/index.module.less
  51. 7 0
      src/view/music-score/index.tsx
  52. 12 2
      src/view/plugins/toggleMusicSheet/choosePartName/index.module.less
  53. 2 0
      src/view/plugins/toggleMusicSheet/index.tsx
  54. 14 14
      src/view/plugins/useDrag/index.module.less
  55. BIN=BIN
      src/view/selection/imgs/arrow_icon.png
  56. 14 12
      src/view/selection/index.module.less
  57. 46 13
      src/view/selection/index.tsx

+ 4 - 2
src/helpers/customMusicScore.ts

@@ -557,8 +557,10 @@ export const resetFormate = () => {
 					}
 				} catch (error) {}
 				const bbox = stave?.getBBox() || {};
-				const rect = `<rect class="vf-custom-bg" x="${bbox.x}" y="${bbox.y}" width="${bbox.width}" height="${bbox.height}" fill="#609FCF" />`
-				const rectBottom = `<rect class="vf-custom-bot" x="${bbox.x}" y="${bbox.y+bbox.height}" width="${bbox.width}" height="7.5" fill="#2B70A5" />`
+				const bgColor = state.isEvaluatReport ? '#132D4C' : '#609FCF';
+				const botColor = state.isEvaluatReport ? '#040D1E' : '#2B70A5';
+				const rect = `<rect class="vf-custom-bg" x="${bbox.x}" y="${bbox.y}" width="${bbox.width}" height="${bbox.height}" fill=${bgColor} />`
+				const rectBottom = `<rect class="vf-custom-bot" x="${bbox.x}" y="${bbox.y+bbox.height}" width="${bbox.width}" height="7.5" fill=${botColor} />`
 				// const filterDom = `<defs>
 				// 						<filter id="shadow">
 				// 						<feDropShadow dx="5" dy="5" stdDeviation="3" flood-color="black" />

+ 3 - 1
src/page-instrument/component/authorName/index.module.less

@@ -1,8 +1,10 @@
 .authorName{
     height: 1.8rem;
+    position: relative;
+    top: 36px;
 }
 .title{
-    width: 216px;
+    width: 280px;
     margin: 0 auto;
     :global{
         .van-notice-bar{

+ 1 - 1
src/page-instrument/component/authorName/index.tsx

@@ -15,7 +15,7 @@ export default defineComponent({
       return () => (
          <>
             {
-               !smoothAnimationState.isShow.value && 
+               !smoothAnimationState.isShow.value && !state.isCombineRender && 
                <div class={["authorName", styles.authorName]}>
                   <div class={styles.title}>
                      <NoticeBar text={state.examSongName} background="none" />

+ 10 - 8
src/page-instrument/component/the-music-list/index.module.less

@@ -113,6 +113,7 @@
                     font-weight: 400;
                     font-size: 13px;
                     color: #131415;
+                    caret-color: #1cacf1;
                     &::placeholder{
                         color: #AAAAAA;
                     }
@@ -171,7 +172,7 @@
                         .van-cell{
                             margin-top: 6px;
                             padding: 0;
-                            font-weight: 500;
+                            font-weight: 400;
                             font-size: 14px;
                             color: #323233;
                             line-height: 32px;
@@ -186,6 +187,7 @@
                                 background: #EEF8FF;
                                 border-radius: 4px;
                                 color: #1CACF1;
+                                font-weight: 500;
                             }
                             .van-cell__value{
                                 display: none;
@@ -269,11 +271,12 @@
             .usedNum{
                 display: flex;
                 align-items: center;
-                padding: 3px 5px;
-                height: 17px;
+                padding: 0 5px;
+                height: 14px;
                 background: #FFF8F7;
                 border-radius: 4px;
-                border: 1px solid #FFC5C5;
+                border: 0.6px solid #FFC5C5;
+                font-size: 10px;
                 >img{
                     width: 8px;
                     height: 11px;
@@ -281,7 +284,6 @@
                 >div{
                     margin-left: 3px;
                     font-weight: 400;
-                    font-size: 12px;
                     color: #FF6A6A
                 }
             }
@@ -294,12 +296,12 @@
             .playType,.singType{
                 margin-left: 5px;
                 width: 26px;
-                height: 17px;
+                height: 14px;
                 background: #FFFFFF;
                 border-radius: 3px;
                 text-align: center;
-                line-height: 17px;
-                border: 1px solid #15B2FD;
+                line-height: 14px;
+                border: 0.6px solid #15B2FD;
                 font-weight: 400;
                 font-size: 10px;
                 color: #15B2FD;

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

@@ -100,6 +100,11 @@ export default defineComponent({
           _t: Date.now(),
         });
     };
+    function formatNumber(num:number) {
+      return num >= 10000 
+          ? (num / 10000).toFixed(1).replace(/\.0$/, '') + "万" 
+          : num.toString();
+    }
     return () => (
       <div class={styles.wrap}>
         <div class={[styles.searchBox,data.isFocus && styles.isFocus]}>
@@ -129,7 +134,7 @@ export default defineComponent({
                 <div class={styles.content}>
                   <p class={styles.name}>{item.musicSheetName}</p>
                   <div class={styles.detail}>
-                    {item.usedNum && <div class={styles.usedNum}><img src={huoimg}/><div>{item.usedNum}</div></div>}
+                    {item.usedNum && <div class={styles.usedNum}><img src={huoimg}/><div>{formatNumber(item.usedNum)}</div></div>}
                     {
                       item.audioPlayTypes && item.audioPlayTypes.split(",").sort().map((type:"PLAY"|"SING")=>{
                         return <div class={type==="PLAY"?styles.playType:styles.singType}>{type==="PLAY"?"演奏":"演唱"}</div>

+ 11 - 0
src/page-instrument/custom-plugins/helper-model/recommendation/index.module.less

@@ -14,8 +14,18 @@
     }
     &.evaluating{
         .head{
+            width: 394px;
+            height: 62px;
+            margin-bottom: -3px;
             background: url("/src/page-instrument/header-top/image/headImg2.png") no-repeat;
             background-size: 100% 100%; 
+            .headTit{
+                bottom: 11px;
+            }
+            .closeImg{
+                top: 2px;
+                right: -26px;
+            }
         }
         .content{
             background: #B0CDFF;
@@ -133,6 +143,7 @@
                                     background: #F2FAFF;
                                     border-radius: 8px;
                                     color: #1CACF1;
+                                    font-weight: 600;
                                 }
                                 .van-cell__value{
                                     display: none;

+ 2 - 2
src/page-instrument/custom-plugins/helper-model/recommendation/index.tsx

@@ -106,7 +106,7 @@ export default defineComponent({
 			getTypeList();
 		});
 		function beforeRead(file:any[]){
-			if(file.length > 5){
+			if(file.length + fileList.value.length > 5){
 				showToast(`最多只能选择5张图片`);
 			}
 			return true
@@ -123,7 +123,7 @@ export default defineComponent({
 							<DropdownMenu class={[styles.dropdownMenu, currItem.value && styles.currItem]} overlay={false}>
 								<DropdownItem class={['recommendationDropdownItem']} title={ currItem.value?currItem.value.name:"请选择反馈类型"} v-model={recommenData.suggestId} options={suggestionTypeList.value}/>
 							</DropdownMenu>
-							<div class={styles.tit}>{`${recommenData.message.length}/200`}</div>
+							<div class={styles.tit}>{`${recommenData.message.length >= 200 ? 200 : recommenData.message.length }/200`}</div>
 						</div>
 						<Field
 							class={styles.field}

BIN=BIN
src/page-instrument/evaluat-model/evaluat-result/img/arrow_icon.png


+ 7 - 6
src/page-instrument/evaluat-model/evaluat-result/index.module.less

@@ -221,13 +221,14 @@
     .arrowIcon {
       position: absolute;
       left: 50%;
-      bottom: -8PX;
+      bottom: -9PX;
       transform: translateX(-50%);
-      width: 0;
-      height: 0;
-      border-top: 8PX solid rgba(0,0,0,0.7);
-      border-right: 8PX solid transparent;
-      border-left: 8PX solid transparent;  
+      width: 13Px;
+      height: 9Px;
+      background-image: url('./img/arrow_icon.png');
+      background-size: 100% 100%;
+      background-position: center center;
+      background-repeat: no-repeat;
       z-index: 2;      
     }
 }

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

@@ -250,6 +250,9 @@ export default defineComponent({
           const startId = item.noteElement.tie?.StartNote?.NoteToGraphicalNoteObjectId
           isTenutoSound = item.NoteToGraphicalNoteObjectId === startId ? false : true
         }
+        // 音符是否不需要评测
+        let noteNeedEvaluat = item.hasGraceNote || ListenMode || dontEvaluatingMode || !!item?.voiceEntry?.ornamentContainer || !!item.noteElement?.speedInfo?.startWord?.includes('rit.') || item.skipMode
+        noteNeedEvaluat = noteNeedEvaluat == true ? true : false;
         const data = {
           timeStamp: (start * 1000) / rate,
           duration: ((end * 1000) / rate - (start * 1000) / rate) * noteRate,
@@ -260,12 +263,12 @@ export default defineComponent({
           measureIndex: measureIndex,
           measureRenderIndex: item.measureListIndex,
           //  item.MeasureNumberXML >= 1 ? item.MeasureNumberXML - 1 : note.noteElement.sourceMeasure.measureListIndex,
-          dontEvaluating: item.hasGraceNote || ListenMode || dontEvaluatingMode || !!item?.voiceEntry?.ornamentContainer || !!item.noteElement?.speedInfo?.startWord?.includes('rit.') || item.skipMode,
+          dontEvaluating: noteNeedEvaluat,
           musicalNotesIndex: index,
           denominator: note.noteElement?.Length.denominator,
           // isOrnament: !!note?.voiceEntry?.ornamentContainer,
           isTenutoSound,
-          isStaccato: item?.voiceEntry?.isStaccato, // 是否是重音
+          isStaccato: item?.voiceEntry?.isStaccato ? true : false, // 是否是重音
         };
         datas.push(data);
       }

+ 3 - 0
src/page-instrument/follow-model/index.module.less

@@ -84,6 +84,9 @@
           margin-left: 20px;
       }
   }
+  &.operatingLeft {
+    left: 30px !important;
+  }
 }
 .beginMask{
   position: fixed;

+ 2 - 1
src/page-instrument/follow-model/index.tsx

@@ -41,12 +41,13 @@ export default defineComponent({
         {
           followData.isBeginMask && <div class={styles.beginMask}></div>
         }        
-        <div class={styles.operatingBtn}>
+        <div class={[styles.operatingBtn, state.platform === IPlatform.PC && state.musicScoreBtnDirection === "left" ? styles.operatingLeft : ""]}>
           {!followData.start && (
             <img
               class={[styles.iconBtn, "follow-1"]}
               src={headImg("icon_play.png")}
               onClick={() => {
+                followData.start = true;
                 handleFollowStart();
               }}
             />

BIN=BIN
src/page-instrument/header-top/image/headImg2.png


BIN=BIN
src/page-instrument/header-top/image/speedBtn.png


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

@@ -364,7 +364,7 @@
     }
 
     &.pauseRightButton {
-        right: 88px !important;
+        right: 108px !important;
         left: auto !important;
         bottom: 12px !important;
     }
@@ -451,5 +451,5 @@
 }
 
 .socketErrorStatus {
-    top: 10vh;
+    top: 20vh;
 }

+ 10 - 3
src/page-instrument/header-top/index.tsx

@@ -53,7 +53,6 @@ export const headTopData = reactive({
     }
     if (value === "practise") {
       // state.playIngSpeed = state.speed
-      smoothAnimationState.isShow.value = state.setting.melodyLine;
     }
     if (value === "evaluating") {
       // 如果延迟检测资源还在加载中,给出提示
@@ -359,6 +358,8 @@ export default defineComponent({
 
     /** 原声按钮 */
     const originBtn = computed(() => {
+      // 没有音源不显示
+      if (state.noMusicSource) return { display: false, disabled: false };
       // 选择模式,跟练模式 不显示
       if (headTopData.modeType !== "show" || state.modeType === "follow") return { display: false, disabled: false };
       // 评测开始 禁用
@@ -415,6 +416,8 @@ export default defineComponent({
     });
     /** 模式切换按钮 */
     const toggleBtn = computed(() => {
+      // 没有音源不显示
+      if (state.noMusicSource) return { display: false, disabled: false };
       // 不是演奏模式 影藏
       if (state.playType !== "play") return { display: false, disabled: false };
       // 选择模式, url设置模式 不显示
@@ -430,6 +433,8 @@ export default defineComponent({
 
     /** 播放按钮 */
     const playBtn = computed(() => {
+      // 没有音源不显示
+      if (state.noMusicSource) return { display: false, disabled: false };
       // 选择模式 不显示
       if (headTopData.modeType !== "show") return { display: false, disabled: false };
       // 评测模式 不显示,跟练模式 不显示
@@ -444,6 +449,8 @@ export default defineComponent({
 
     /** 重播按钮 */
     resetBtn = computed(() => {
+      // 没有音源不显示
+      if (state.noMusicSource) return { display: false, disabled: false };
       // 选择模式 不显示
       if (headTopData.modeType !== "show") return { display: false, disabled: false };
       // 评测模式 不显示,跟练模式 不显示
@@ -584,7 +591,7 @@ export default defineComponent({
       () => smoothAnimationState.isShow.value,
       () => {
         // NoticeBar能不能滚动
-        if (smoothAnimationState.isShow.value && isMusicList.value) {
+        if ((smoothAnimationState.isShow.value || state.isCombineRender) && isMusicList.value) {
           nextTick(() => {
             const widthCon = (document.querySelector("#noticeBarRollDom .van-notice-bar__content") as any)?.offsetWidth || undefined;
             noticeBarWidth.value = widthCon;
@@ -646,7 +653,7 @@ export default defineComponent({
           {!(state.playState == "play" || followData.start || evaluatingData.startBegin) && (
             <div id="noticeBarRollDom" class={styles.headTopLeftBox}>
               <img src={iconBack} class={["headTopBackBtn", styles.img, !headTopData.showBack && styles.hidenBack]} onClick={handleBack} />
-              {smoothAnimationState.isShow.value ? (
+              {smoothAnimationState.isShow.value || state.isCombineRender ? (
                 <div
                   style={
                     noticeBarWidth.value

+ 13 - 7
src/page-instrument/header-top/modeView.tsx

@@ -107,7 +107,7 @@ export default defineComponent({
           src={backImg}
           class={styles.back}
           onClick={() => {
-            smoothAnimationState.isShow.value = state.setting.melodyLine;
+            smoothAnimationState.isShow.value = state.melodyLine;
             // 返回的时候 跳转到之前记录的模式
             if(headTopData.oldModeType !== "practise"){
               headTopData.handleChangeModeType(headTopData.oldModeType)
@@ -117,17 +117,23 @@ export default defineComponent({
         />
         <img src={nameImg} class={styles.name} />
         <div class={[styles.modeBox, ((!state.isPercussion && !state.enableEvaluation) || (state.isPercussion && state.enableEvaluation) || (state.isPercussion && !state.enableEvaluation)) && styles.twoModeBox]}>
-          <Vue3Lottie ref={modeImgDom1} class={styles.modeImg} animationData={lxMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("practise")}></Vue3Lottie>
+          <Vue3Lottie ref={modeImgDom1} class={styles.modeImg} animationData={lxMode} autoPlay={false} loop={true} onClick={() => {
+            smoothAnimationState.isShow.value = state.melodyLine;
+            headTopData.handleChangeModeType("practise")
+            } }></Vue3Lottie>
           {!state.isPercussion && <Vue3Lottie ref={modeImgDom2} class={styles.modeImg} animationData={glMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("follow")}></Vue3Lottie>}
           {state.enableEvaluation && <Vue3Lottie ref={modeImgDom3} class={styles.modeImg} animationData={pcMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("evaluating")}></Vue3Lottie>}
         </div>
         {data.showVip && <TheVip />}
         {/** 延迟检测中途,socket出错,网络提示弹窗 */}
-        <div>
-            <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale", evaluatingData.socketErrorStatus === 2 && styles.socketErrorStatus]} transition="van-scale" v-model:show={evaluatingData.socketErrorPop} overlay-style={evaluatingData.socketErrorStatus === 2?{ background: "initial" }:{}}>
-              <AbnormalPop onConfirm={hanldeConfirmPop} onClose={hanldeClosePop} />
-            </Popup>
-        </div>         
+        {/* {
+          state.modeType !== 'evaluating' && 
+          <div>
+              <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale", evaluatingData.socketErrorStatus === 2 && styles.socketErrorStatus]} transition="van-scale" v-model:show={evaluatingData.socketErrorPop} overlay-style={evaluatingData.socketErrorStatus === 2?{ background: "initial" }:{}}>
+                <AbnormalPop onConfirm={hanldeConfirmPop} onClose={hanldeClosePop} />
+              </Popup>
+          </div>            
+        } */}
       </div>
     );
   },

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

@@ -17,8 +17,18 @@
     }
     &.evaluating{
         .head{
+            width: 394px;
+            height: 62px;
+            margin-bottom: -3px;
             background: url("../image/headImg2.png") no-repeat;
             background-size: 100% 100%; 
+            .headTit{
+                bottom: 11px;
+            }
+            .closeImg{
+                top: 2px;
+                right: -26px;
+            }
         }
         .content{
             background: #B0CDFF;

+ 21 - 1
src/page-instrument/header-top/settting/index.tsx

@@ -11,6 +11,7 @@ import { resetRenderMusicScore } from "/src/view/music-score";
 import ScreenModel from "../../custom-plugins/helper-model/screen-model";
 import { getQuery } from "/src/utils/queryString";
 import { reCheckDelay } from "/src/page-instrument/evaluat-model"
+import { audioData, changeMingSongType } from "/src/view/audio-list"
 
 export default defineComponent({
 	name: "settting",
@@ -61,6 +62,25 @@ export default defineComponent({
                                     <div class={styles.tit}>循环播放</div>
                                     <Switch v-model={state.setting.repeatAutoPlay}></Switch>
                                 </div>
+                        }                        
+                        {
+                            state.modeType === 'practise' && state.mingSong && state.mingSongGirl &&
+                            <div class={styles.cellBox}>
+                                <div class={styles.tit}>唱名类型</div>
+                                <div class={styles.radioBox}>
+                                    {
+                                        [{name:'男生',value:1}, {name:'女生',value:0}].map(item=>{
+                                            return <div class={ audioData.mingSongType===item.value && styles.active } onClick={ ()=>{ 
+                                                if(audioData.mingSongType === item.value){
+                                                    return
+                                                }
+                                                audioData.mingSongType = item.value as 0|1
+                                                changeMingSongType()
+                                            } }>{item.name}</div>
+                                        })
+                                    }
+                                </div>
+                            </div>                     
                         }
                         {
                             state.isSingleLine && state.modeType === "practise" && !state.isCombineRender && !state.isPercussion && 
@@ -69,7 +89,7 @@ export default defineComponent({
                                     <Switch 
                                         v-model={smoothAnimationState.isShow.value}
                                         onChange={(value) => {
-                                            state.setting.melodyLine = value
+                                            state.melodyLine = value
                                         }}                                        
                                     ></Switch>
                                 </div>   

+ 12 - 1
src/page-instrument/header-top/speed/index.module.less

@@ -17,8 +17,18 @@
     }
     &.evaluating{
         .head{
+            width: 394px;
+            height: 62px;
+            margin-bottom: -3px;
             background: url("../image/headImg2.png") no-repeat;
             background-size: 100% 100%; 
+            .headTit{
+                bottom: 11px;
+            }
+            .closeImg{
+                top: 2px;
+                right: -26px;
+            }
         }
         .content{
             background: #B0CDFF;
@@ -120,6 +130,7 @@
                                     height: 4px;
                                     background: url("../image/gg.png") no-repeat;
                                     background-size: 100% 100%;
+                                    transform: translate(-20%,-20%);
                                 }
                                 .van-slider__button-wrapper{
                                     bottom: 0;
@@ -146,7 +157,7 @@
                             padding-top: 3px;
                         }
                         .speedBtn{
-                            width: 16px;
+                            width: 20px;
                             height: 30px;
                             background: url("../image/speedBtn.png") no-repeat;
                             background-size: 100% 100%;

+ 1 - 1
src/page-instrument/simple-detail/index.tsx

@@ -46,7 +46,7 @@ export default defineComponent({
 						state.activeNoteIndex = 0
 						state.activeMeasureIndex = state.times[0].MeasureNumberXML;
 						handlePlaying(true);
-					}, 0);
+					}, 200);
 				}
 			}
 			// 暂停状态下,拖动进度

BIN=BIN
src/page-instrument/view-detail/images/bg1.png


BIN=BIN
src/page-instrument/view-detail/images/bg2.png


BIN=BIN
src/page-instrument/view-detail/images/bg2_left_zs.png


BIN=BIN
src/page-instrument/view-detail/images/bg2_right_zs.png


BIN=BIN
src/page-instrument/view-detail/images/bg3.png


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/page-instrument/view-detail/images/index.json


+ 21 - 0
src/page-instrument/view-detail/index.module.less

@@ -88,6 +88,11 @@
         overflow: hidden;
     }
 
+    .multiContainer {
+        top: 0;
+        height: calc(100vh);
+    }
+
     .pcContainer {
         // height: calc(100vh - var(--header-height) - var(--pc-header-height));
     }
@@ -267,4 +272,20 @@
         font-size: 14px;
         color: #fff;
     }
+}
+
+.bg2Left {
+    width: 52px;
+    height: 125px;
+    position: absolute;
+    left: 0;
+    top: 0;
+}
+
+.bg2Right {
+    width: 52px;
+    height: 125px;
+    position: absolute;
+    right: 0;
+    top: 0;
 }

+ 24 - 8
src/page-instrument/view-detail/index.tsx

@@ -39,6 +39,8 @@ import EmptyMusic, { isEmptyMusicShow } from "./emptyMusic";
 import { position } from "html2canvas/dist/types/css/property-descriptors/position";
 import Loading from "./loading";
 import bgJson from "./images/index.json";
+import bg2Left from "./images/bg2_left_zs.png";
+import bg2Right from "./images/bg2_right_zs.png";
 
 // const DelayCheck = defineAsyncComponent(() =>
 //   import('/src/page-instrument/evaluat-model/delay-check')
@@ -119,7 +121,7 @@ export default defineComponent({
       const settting = store.get("musicscoresetting");
       if (settting) {
         //state.setting = settting;
-        Object.assign(state.setting, settting)
+        Object.assign(state.setting, settting);
         //state.setting.beatVolume = state.setting.beatVolume || 50
         state.setting.beatVolume = 50;
         if (state.setting.camera) {
@@ -269,7 +271,7 @@ export default defineComponent({
       handleInitTick(osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Numerator || 4);
       // }
       // api_cloudLoading();
-      state.playBtnDirection = query.imagePos === "right" ? "right" : "left";
+      state.playBtnDirection = query.imagePos === "left" ? "left" : "right";
       state.isAttendClass = query.imagePos === "left" || query.imagePos === "right" ? true : false;
       // if (state.fingeringInfo.direction === "vertical" && state.setting.displayFingering) {
       //   state.musicScoreBtnDirection = state.playBtnDirection === 'right' ? 'left' : 'right';
@@ -326,10 +328,10 @@ export default defineComponent({
               fingerBox: {
                 position: "absolute",
                 width: state.fingeringInfo.width,
-                height: "80%",
+                height: state.fingeringInfo.name === "hulusi-flute" ? "86%" : "80%",
                 right: state.playBtnDirection === "right" ? "initial" : 0,
                 left: state.playBtnDirection === "right" ? 0 : "initial",
-                top: 0,
+                top: state.fingeringInfo.name === "ocarina" ? "60px" : 0,
               },
             };
           } else {
@@ -340,9 +342,9 @@ export default defineComponent({
               fingerBox: {
                 position: "absolute",
                 width: state.fingeringInfo.width,
-                height: "80%",
+                height: state.fingeringInfo.name === "hulusi-flute" ? "86%" : "80%",
                 left: 0,
-                top: 0,
+                top: state.fingeringInfo.name === "ocarina" ? "60px" : 0,
               },
             };
           }
@@ -395,6 +397,13 @@ export default defineComponent({
         sendParentMessage(state.playState);
       }
     );
+    // 监听跟练的开启状态
+    watch(
+      () => followData.start,
+      () => {
+        detailData.headerHide = followData.start;
+      }
+    );
     /** 指法预览切换 */
     watch(
       () => detailData.fingerPreView,
@@ -464,6 +473,12 @@ export default defineComponent({
         }}
       >
         <img style={{ opacity: state.setting.camera && state.modeType === "evaluating" ? state.setting.cameraOpacity / 100 : 1 }} class={styles.pageBg} src={state.modeType === "practise" ? bgJson[1] : state.modeType === "evaluating" ? bgJson[2] : state.modeType === "follow" ? bgJson[3] : ""} />
+        {state.modeType === "evaluating" ? (
+          <>
+            <img src={bg2Left} class={styles.bg2Left} />
+            <img src={bg2Right} class={styles.bg2Right} />
+          </>
+        ) : null}
         {/* 骨架屏 */}
         {/* <Transition name="van-fade">
           {detailData.skeletonLoading && (
@@ -479,13 +494,14 @@ export default defineComponent({
         <div
           id="scrollContainer"
           style={{ ...fingerConfig.value.container }}
-          class={[styles.container, !state.setting.displayCursor && "hideCursor", browsInfo.xiaomi && styles.xiaomi, state.platform === IPlatform.PC && styles.pcContainer]}
+          class={[styles.container, !state.setting.displayCursor && "hideCursor", browsInfo.xiaomi && styles.xiaomi, state.platform === IPlatform.PC && styles.pcContainer, (!state.isSingleLine || state.isCombineRender) && styles.multiContainer]}
           onClick={(e: Event) => {
             e.stopPropagation();
             // if (state.playState === "play" && state.platform != IPlatform.PC) {
             //   detailData.headerHide = !detailData.headerHide;
             // }
-            if (state.playState === "play") {
+            // 点击谱面跟练也需要切换显示按钮栏
+            if (state.playState === "play" || followData.start) {
               detailData.headerHide = !detailData.headerHide;
             }
           }}

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

@@ -26,6 +26,7 @@
     }
     .authorName{
         position: fixed;
+        // position: absolute;
         left: 0;
         top: 36px;
         width: 100vw;

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

@@ -80,7 +80,7 @@ export function initSmoothAnimation() {
    calcClientWidth()
    window.addEventListener("resize", calcClientWidth)
    // 初始化 只有练习模式 才显示
-   state.modeType === "practise" && (smoothAnimationState.isShow.value = true)
+   state.modeType === "practise" && (smoothAnimationState.isShow.value = state.melodyLine)
    // 多分轨合并显示、打击乐、节奏练习的曲子不显示旋律线
    if (state.isCombineRender || state.isPercussion) {
       smoothAnimationState.isShow.value = false
@@ -186,7 +186,8 @@ export function moveSmoothAnimation(progress: number, activeIndex: number, isMov
  * 谱面移动逻辑
  */
 function move_osmd(nowPointsPos: pointsPosType[0]) {
-   const speed = smoothAnimationState.aveSpeed * (state.speed / 60)
+   // 评测移动太快看不到前面小节的分数,评测改成0.5倍速移动谱面
+   const speed = (state.modeType === 'evaluating' ? smoothAnimationState.aveSpeed * 0.5 : smoothAnimationState.aveSpeed) * (state.speed / 60)
    // 视口宽度
    const clientWidth = smoothAnimationState.osdmScrollDomWith
    const clientMidWidth = clientWidth / 2

BIN=BIN
src/page-instrument/view-evaluat-report/component/share-top/image/icon-huifang.png


BIN=BIN
src/page-instrument/view-evaluat-report/component/share-top/image/icon-shiyi.png


+ 2 - 2
src/page-instrument/view-evaluat-report/component/share-top/index.module.less

@@ -48,7 +48,7 @@
             line-height: 20px;
             margin-bottom: 2px;
             padding: 0 !important;
-
+            width: 200px;
             :global {
                 .van-notice-bar {
                     height: 20px;
@@ -227,7 +227,7 @@
     box-shadow: inset 4px -3px 6px 0px #B2E8FF;
     border-radius: 20px;
     padding: 14px;
-
+    overflow: hidden;
     :global {
         .plyr {
             border-radius: 16px;

+ 9 - 18
src/page-instrument/view-evaluat-report/component/share-top/index.tsx

@@ -4,8 +4,8 @@ import styles from "./index.module.less";
 import { api_back } from "/src/helpers/communication";
 import state from "/src/state";
 import iconBack from "./image/back_icon.png";
-import iconShiyi from "./image/icon-shiyi.svg";
-import iconhuifang from "./image/icon-huifang.svg";
+import iconShiyi from "./image/icon-shiyi.png";
+import iconhuifang from "./image/icon-huifang.png";
 import shiyiTop from "./image/shiyi-top.png";
 import shiyiClose from "./image/closeImg.png";
 import { Grid, GridItem, Popup } from "vant";
@@ -42,9 +42,6 @@ export default defineComponent({
       isInitPlyr: false,
       _plrl: null as any,
     });
-    const lottieDom = ref<any>();
-    const lottieDom1 = ref<any>();
-    const lottieDom2 = ref<any>();
     const level: any = {
       BEGINNER: "入门级",
       ADVANCED: "进阶级",
@@ -56,7 +53,7 @@ export default defineComponent({
       low: "#FFB900",
       right: "#65FFAE",
       wrong: "#DA3736",
-      lack: "#A5CBFF",
+      lack: "#7AB2FF",
       not: "#FFFFFF",
       fast: "#B366FF",
       slow: "#FF7B00",
@@ -100,15 +97,9 @@ export default defineComponent({
 						const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement
 						const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom)
 						shareData._plrl.on('play', () => {
-							lottieDom.value?.play()
-							lottieDom1.value?.play()
-							lottieDom2.value?.play()
 							playVisualDraw()
 						});
 						shareData._plrl.on('pause', () => {
-							lottieDom.value?.pause()
-							lottieDom1.value?.pause()
-							lottieDom2.value?.pause()
 							pauseVisualDraw()
 						});
 					}, 300); // 弹窗动画是0.25秒 这里用定时器 确保canvas 能获取到宽高
@@ -230,7 +221,7 @@ export default defineComponent({
               {/* <div class={styles.lcName}>{state.examSongName}</div> */}
               <Title class={styles.lcName} text={state.examSongName} rightView={false} />
               <div class={styles.lcScore}>
-                {level[scoreData.value.heardLevel]}|速度:{scoreData.value.speed}|综合分数:{scoreData.value.score}分
+                {level[scoreData.value.heardLevel]}|速度:{Math.floor(scoreData.value.speed)}|综合分数:{scoreData.value.score}分
               </div>
             </div>
           </div>
@@ -352,7 +343,7 @@ export default defineComponent({
 
                   {itemType.value === "integrity" && (
                     <div>
-                      <Note fill="#A5CBFF" />
+                      <Note fill="#7AB2FF" />
                       <span>时值不足</span>
                     </div>
                   )}
@@ -444,9 +435,9 @@ export default defineComponent({
               {mediaType.value === "audio" ? (
                 <div class={styles.audioBox}>
                   <canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>
-                  <Vue3Lottie ref={lottieDom} class={styles.audioBga} animationData={audioBga} autoPlay={false} loop={true}></Vue3Lottie>
-                  <Vue3Lottie ref={lottieDom1} class={styles.audioBga1} animationData={audioBga1} autoPlay={false} loop={true}></Vue3Lottie>
-                  <Vue3Lottie ref={lottieDom2} class={styles.audioBga2} animationData={audioBga2} autoPlay={false} loop={true}></Vue3Lottie>
+                  <Vue3Lottie class={styles.audioBga} animationData={audioBga} autoPlay={false} loop={true}></Vue3Lottie>
+                  <Vue3Lottie class={styles.audioBga1} animationData={audioBga1} autoPlay={false} loop={true}></Vue3Lottie>
+                  <Vue3Lottie class={styles.audioBga2} animationData={audioBga2} autoPlay={false} loop={true}></Vue3Lottie>
                   <audio crossorigin="anonymous" id="audioSrc" src={scoreData.value.videoFilePath} controls="false" preload="metadata" playsinline />
                 </div>
               ) : (
@@ -484,7 +475,7 @@ export default defineComponent({
                     <span>紫色音符:节奏偏快</span>
                   </div>
                   <div class={styles.item}>
-                    <Note fill="#A5CBFF" />
+                    <Note fill="#7AB2FF" />
                     <span>浅蓝色音符:时值不足</span>
                   </div>
                   <div class={styles.item}>

+ 27 - 7
src/page-instrument/view-evaluat-report/index.module.less

@@ -28,9 +28,14 @@
   min-height: 100vh;
   --header-height: 62px;
   // background: rgba(248, 242, 232, 1);
-  background: url("../view-detail/images/bg2.png") no-repeat;
-  background-size: 100% 100%;
-
+  .pageBg {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    object-fit: cover; /* 保持宽高比 */
+  }
   :global {
 
     #cursorImg-0 {
@@ -87,8 +92,8 @@
 
 .beam {
   path {
-    fill: #ADADAD !important;
-    stroke: #ADADAD;
+    // fill: #ADADAD !important;
+    // stroke: #ADADAD;
   }
 }
 
@@ -171,8 +176,8 @@
 // 完成度
 .integrity_wrong {
   path {
-    fill: #A5CBFF;
-    stroke: #A5CBFF;
+    fill: #7AB2FF;
+    stroke: #7AB2FF;
   }
 }
 
@@ -181,4 +186,19 @@
   opacity: 0;
   width: 1;
   height: 1;
+}
+
+.bg2Left {
+  width: 52px;
+  height: 125px;
+  position: absolute;
+  left: 0;
+  top: 0;
+}
+.bg2Right {
+  width: 52px;
+  height: 125px;
+  position: absolute;
+  right: 0;
+  top: 0;
 }

+ 16 - 7
src/page-instrument/view-evaluat-report/index.tsx

@@ -16,6 +16,9 @@ import TopArrow from "./component/note/topArrow";
 import BottomArrow from "./component/note/bottomArrow";
 import LeftArrow from "./component/note/leftArrow";
 import RightArrow from "./component/note/rightArrow";
+import bgJson from "/src/page-instrument/view-detail/images/index.json";
+import bg2Left from "/src/page-instrument/view-detail/images/bg2_left_zs.png";
+import bg2Right from "/src/page-instrument/view-detail/images/bg2_right_zs.png";
 
 const colorsClass: any = {
   RIGHT: styles.right, // 正确
@@ -294,13 +297,13 @@ export default defineComponent({
           svgEl?.classList.add(colorsClass[errType]);
           // console.log(123456,'添加颜色',errType)
           // 评测过的音符,需要给小节添加背景色
-          if (errType !== "NOT_PLAYED") {
-            const staveNote = svgEl?.parentNode?.parentNode?.querySelector(".vf-stave");
-            if (staveNote) {
-              staveNote.querySelector(".vf-custom-bg")?.setAttribute("fill", "#132D4C");
-              staveNote.querySelector(".vf-custom-bot")?.setAttribute("fill", "#040D1E");
-            }
-          }
+          // if (errType !== "NOT_PLAYED") {
+          //   const staveNote = svgEl?.parentNode?.parentNode?.querySelector(".vf-stave");
+          //   if (staveNote) {
+          //     staveNote.querySelector(".vf-custom-bg")?.setAttribute("fill", "#132D4C");
+          //     staveNote.querySelector(".vf-custom-bot")?.setAttribute("fill", "#040D1E");
+          //   }
+          // }
           if (svgEl && isNeedCopyElement) {
             stemEl?.classList.remove(colorsClass[errType]);
             svgEl?.classList.remove(colorsClass[errType]);
@@ -426,6 +429,12 @@ export default defineComponent({
     );
     return () => (
       <div class={[styles.detail, state.setting.eyeProtection && "eyeProtection", styles.shareBox]} style={{ paddingLeft: detailData.paddingLeft }}>
+        <img 
+          class={styles.pageBg} 
+          src={bgJson[2]} 
+        />
+        <img src={bg2Left} class={styles.bg2Left} />
+        <img src={bg2Right} class={styles.bg2Right} />       
         <Transition name="van-fade">
           {!state.musicRendered && (
             <div class={styles.skeleton}>

+ 109 - 33
src/state.ts

@@ -333,13 +333,16 @@ const state = reactive({
   banSong: "",
   /** 唱名 */
   mingSong: "",
+  /** 唱名女 如果有男唱名的情况下*/
+  mingSongGirl:"",
   /** 节拍器音乐资源 */
   beatSong: {
     music: "",
     accompany: "",
     fanSong: "",
     banSong: "",
-    mingSong: ""
+    mingSong: "",
+    mingSongGirl: ""
   },
   /** midiURL */
   midiUrl: "",
@@ -425,8 +428,6 @@ const state = reactive({
     reactionTimeMs: 0,
     /** 节拍器音量 */
     beatVolume: 50,
-    /** 旋律线开关 */
-    melodyLine: true,
   },
   /** 后台设置的基准评测频率 */
   baseFrequency: 440,
@@ -483,7 +484,13 @@ const state = reactive({
   /** 乐器code,用于评测传参 */
   musicalCode: '' as any,
   /** 合奏曲目是否合并展示 */
-  isCombineRender: false,
+  isCombineRender: false,  
+  /** 是否支持总谱 */
+  isScoreRender: false,  
+  /** 是否默认显示总谱 */
+  defaultScoreRender: false,
+  /** 没有音源字段 */
+  noMusicSource: false,
   /** 小节的持续时长,以后台设置的播放速度计算 */
   measureTime: 0,
   /** 跟练模式,节拍器播放的时间 */
@@ -551,6 +558,8 @@ const state = reactive({
   isCreateImg: false,
   /** 切换谱面后,作业选段是否需要刷新 */
   workSectionNeedReset: false,
+  /** 旋律线开关 */
+  melodyLine: true,
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -1313,17 +1322,20 @@ export const getMusicDetail = async (id: string, type?: string) => {
 
 
 const getMusicInfo = async (res: any) => {
+  // 是否支持总谱
+  state.isScoreRender = res.data?.isScoreRender
+  // 是否默认显示总谱
+  state.defaultScoreRender = res.data?.defaultScoreRender
+  const partIndex = query["part-index"] ? parseInt(query["part-index"]) : -1 // -1为partIndex没有值的时候
+  /* 获取声轨列表 */
   const xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
   downloadXmlStr.value = xmlString //给musice-score 赋值xmlString 以免加载2次
   const tracks = xmlToTracks(xmlString) //获取声轨列表
-  let index = query["part-index"] ? parseInt(query["part-index"]) : 0
-  const musicObj = state.isSimplePage ? {} : initMusicSource(res.data, tracks[index]) //当前part-index找不到声源的时候以第一个为准
-  index = tracks.findIndex(item => {  // 筛选出当前的index
-    return item === musicObj?.track
-  })
+  // 设置音源  track 为当前的声轨 index为当前的
+  const { track, index } = state.isSimplePage ? { track:tracks[0], index:0} : initMusicSource(res.data, tracks, partIndex)
   const musicInfo = {
     ...res.data,
-    track: musicObj?.track
+    track
   };
   console.log("🚀 ~ musicInfo:", musicInfo);
   setState(musicInfo, index);
@@ -1341,15 +1353,16 @@ function xmlToTracks(xmlString: string) {
   }, []);
 }
 // 设置音源
-function initMusicSource(data: any, track?: string) {
+function initMusicSource(data: any, tracks: string[], partIndex: number) {
+  let track:string,index:number
   const { instrumentId } = storeData.user
   let { musicSheetType, isAllSubject, musicSheetSoundList, musicSheetAccompanimentList } = data
   musicSheetSoundList || (musicSheetSoundList = [])
   musicSheetAccompanimentList || (musicSheetAccompanimentList = [])
   let musicObj, accompanyObj, fanSongObj, banSongObj
-  // 独奏
+   /* 独奏 */
   if (musicSheetType === "SINGLE") {
-    // 适用声部(instrumentId)为true 时候没有乐器只有一个原音;当前用户有乐器就匹配  不然取第一个原音
+    // 适用声部(isAllSubject)为true 时候没有乐器只有一个原音;当前用户有乐器就匹配  不然取第一个原音
     musicObj = musicSheetSoundList.find((item: any) => {
       return (isAllSubject || !instrumentId) ? item.audioPlayType === "PLAY" : (item.audioPlayType === "PLAY" && item.musicalInstrumentId == instrumentId)
     })
@@ -1363,22 +1376,70 @@ function initMusicSource(data: any, track?: string) {
     banSongObj = musicSheetAccompanimentList.find((item: any) => {
       return item.audioPlayType === "SING"
     })
+    accompanyObj = musicSheetAccompanimentList.find((item: any) => {
+      return item.audioPlayType === "PLAY"
+    })
+    track = musicObj?.track   //没有原音的时候track为空 不显示指法
+    index = tracks.findIndex(item => {
+      return item === track
+    })
   } else {
-    // 合奏  合奏根据声轨来区分原音
-    musicObj = track ? musicSheetSoundList.find((item: any) => {
-      return item.track === track
-    }) : musicSheetSoundList[0]
+    /* 合奏 */
+    // 支持总谱 并且当前是总谱。partIndex是999时候,或者默认是总谱并且partIndex为-1时候  -1就是partIndex没有值
+    if(state.isScoreRender && (partIndex===999 || (state.defaultScoreRender && partIndex===-1))){
+        // 总谱渲染
+        state.isCombineRender = true
+        state.partListNames = tracks
+        banSongObj = musicSheetAccompanimentList.find((item: any) => {
+          return item.audioPlayType === "SING"
+        })
+        // 先取scoreAudioFileUrl的值
+        if(banSongObj?.scoreAudioFileUrl){
+          banSongObj.audioFileUrl = banSongObj.scoreAudioFileUrl
+          banSongObj.audioBeatMixUrl = banSongObj.scoreAudioBeatMixUrl
+        }
+        accompanyObj = musicSheetAccompanimentList.find((item: any) => {
+          return item.audioPlayType === "PLAY"
+        })
+        // 先取scoreAudioFileUrl的值
+        if(accompanyObj?.scoreAudioFileUrl){
+          accompanyObj.audioFileUrl = accompanyObj.scoreAudioFileUrl
+          accompanyObj.audioBeatMixUrl = accompanyObj.scoreAudioBeatMixUrl
+        }
+        track = "总谱"
+        index = 999
+    }else{
+      // 合奏只显示一个声轨
+      track = tracks[partIndex===-1?0:partIndex]
+      // 根据当前的声轨 取数据
+      musicObj = musicSheetSoundList.find((item: any) => {
+        return item.audioPlayType === "PLAY" && item.track === track
+      })
+      fanSongObj = musicSheetSoundList.find((item: any) => {
+        return item.audioPlayType === "SING" && item.track === track
+      })
+      banSongObj = musicSheetAccompanimentList.find((item: any) => {
+        return item.audioPlayType === "SING"
+      })
+      accompanyObj = musicSheetAccompanimentList.find((item: any) => {
+        return item.audioPlayType === "PLAY"
+      })
+      index = tracks.findIndex(item => {
+        return item === track
+      })
+    }
   }
-  accompanyObj = musicSheetAccompanimentList.find((item: any) => {
-    return item.audioPlayType === "PLAY"
-  })
   // 当没有任何曲目的时候报错
-  if (!musicObj?.audioFileUrl && !accompanyObj?.audioFileUrl && !fanSongObj?.audioFileUrl && !banSongObj?.audioFileUrl && !fanSongObj?.solmizationFileUrl) {
-    // 并且是midi没有midi文件的时候
-    if(data.playMode === "MIDI" && !data.midiFileUrl) {
-      // 是预览的时候 不报错
-      if(!query.isPreView){
-        throw new Error("该曲目无任何音源");
+  if (!musicObj?.audioFileUrl && !accompanyObj?.audioFileUrl && !fanSongObj?.audioFileUrl && !banSongObj?.audioFileUrl && !fanSongObj?.solmizationFileUrl && !fanSongObj?.femaleSolmizationFileUrl) {
+    state.noMusicSource = true // 没有音源文件
+    // 独奏的时候
+    if(musicSheetType === "SINGLE"){
+      // 并且是midi没有midi文件的时候
+      if(data.playMode === "MIDI" && !data.midiFileUrl) {
+        // 是预览的时候 不报错
+        if(!query.isPreView){
+          throw new Error("该曲目无任何音源");
+        }
       }
     }
   }
@@ -1387,16 +1448,31 @@ function initMusicSource(data: any, track?: string) {
     accompany: accompanyObj?.audioFileUrl,
     fanSong: fanSongObj?.audioFileUrl,
     banSong: banSongObj?.audioFileUrl,
-    mingSong: fanSongObj?.solmizationFileUrl
   })
+  // 如果没有男唱名
+  if(!fanSongObj?.solmizationFileUrl){
+    state.mingSong = fanSongObj?.femaleSolmizationFileUrl
+  }else{
+    state.mingSong = fanSongObj?.solmizationFileUrl
+    state.mingSongGirl = fanSongObj?.femaleSolmizationFileUrl
+  }
   Object.assign(state.beatSong, {
     music: musicObj?.audioBeatMixUrl,
     accompany: accompanyObj?.audioBeatMixUrl,
     fanSong: fanSongObj?.audioBeatMixUrl,
-    banSong: banSongObj?.audioBeatMixUrl,
-    mingSong: fanSongObj?.solmizationBeatUrl
+    banSong: banSongObj?.audioBeatMixUrl
   })
-  return musicObj
+  // 如果没有男唱名
+  if(!fanSongObj?.solmizationBeatUrl){
+    state.beatSong.mingSong = fanSongObj?.femaleSolmizationBeatUrl
+  }else{
+    state.beatSong.mingSong = fanSongObj?.solmizationBeatUrl
+    state.beatSong.mingSongGirl = fanSongObj?.femaleSolmizationBeatUrl
+  }
+  return {
+    index,
+    track
+  }
 }
 const setState = (data: any, index: number) => {
   // 根据当前文件有没有 设置当前的播放模式
@@ -1443,8 +1519,6 @@ const setState = (data: any, index: number) => {
   state.cbsExamSongId = data.id + "";
   state.examSongName = data.name;
   state.coverImg = data.musicCover ?? "";
-  // 单声部多声轨合并展示
-  state.isCombineRender = data.musicSheetType === "SINGLE" && data.musicSheetSoundList?.length > 1
   // 如果是simple页面,只显示单轨
   if (state.isSimplePage) {
     state.isCombineRender = false;
@@ -1713,7 +1787,9 @@ export const fillWordColor = () => {
   const currentLyrics: SVGAElement[] = Array.from(document.querySelectorAll(`.lyric${currentNote?.noteId}`));
   currentLyrics.forEach((lyric, index) => {
     const lyricIndex = lyric.getAttribute('lyricIndex');
-    if ((index === currentNote.repeatIdx && currentNote.repeatIdx + 1 == lyricIndex) || (currentNote.repeatIdx > 0 && currentNote.formatLyricsEntries?.length === 1)) {
+    // bug:#10942,如果需要反复唱的小节,只有一遍歌词,反复唱的时候,歌词都需要高亮
+    const onlyOneLyric = currentNote.measures?.every((item: any) => item?.formatLyricsEntries?.length <= 1);
+    if ((index === currentNote.repeatIdx && currentNote.repeatIdx + 1 == lyricIndex) || (currentNote.repeatIdx != index && !onlyOneLyric && currentNote.repeatIdx + 1 == lyricIndex) || (currentNote.repeatIdx > 0 && currentNote.formatLyricsEntries?.length === 1 && onlyOneLyric)) {
       lyric?.classList.add('lyricActive')
     }
     // if ((index === currentNote.repeatIdx && currentNote.repeatIdx + 1 == lyricIndex)) {

+ 3 - 1
src/store.ts

@@ -22,7 +22,9 @@ type IUser = {
   phone?: string;
   schoolInfos?: any[];
   // 当前用户 绑定 的乐器
-  instrumentId: string 
+  instrumentId?: string;
+  // 性别
+  gender?: 1|0;
 };
 type IStatus = "init" | "login" | "logout" | "error";
 type IPlatformType = "STUDENT" | "TEACHER" | "WEB" | "";

+ 1 - 1
src/style.css

@@ -36,7 +36,7 @@ body {
 }
 
 .selectionToast {
-  top: 10vh;
+  top: 20vh;
 }
 
 .pop-center-enter-from,

+ 3 - 1
src/view/abnormal-pop/index.module.less

@@ -30,7 +30,7 @@
             font-size: 15px;
             font-family: PingFangSC, PingFang SC;
             font-weight: 400;
-            color: rgba(255, 255, 255, 0.8);     
+            color: rgba(255, 255, 255, 0.7);     
             margin: 20px 0 24px;
             text-align: center;  
         }
@@ -79,6 +79,7 @@
     flex-direction: column;
     justify-content: center;
     align-items: center;
+    transform: translateY(-14px);
     .loadingIcon {
         width: 118px;
     }
@@ -97,6 +98,7 @@
         text-align: center;
         width: 97px;
         height: 32px;
+        box-sizing: border-box;
         &:active{
             opacity: 0.8;
         }

+ 50 - 7
src/view/audio-list/index.tsx

@@ -33,7 +33,14 @@ export const audioData = reactive({
 	},
 	midiRender: false,
 	progress: 0, // midi播放进度(单位:秒)
-	duration: 0 // 音频总时长(单位:秒)
+	duration: 0, // 音频总时长(单位:秒)
+	mingSongType: 1 as 0 | 1,
+	mingSongTypeCollection: {
+		mingSongEle: null as HTMLAudioElement | null,
+		mingSongGirlEle: null as HTMLAudioElement | null,
+		beatMingSongEle: null as HTMLAudioElement | null,
+		beatMingSongGirlEle: null as HTMLAudioElement | null
+	}
 });
 const midiRef = ref();
 /** 播放或暂停 */
@@ -183,6 +190,16 @@ export const changeSongSourceByBate = (isDisBate:boolean) => {
 		audioData.mingSongEle && (audioData.mingSongEle.muted = false);
 	}
 }
+/** 切换男生女生唱名  */
+export const changeMingSongType = () =>{
+	// 当有男声女声都有值时候才能切换 
+	const { mingSongEle, mingSongGirlEle, beatMingSongEle, beatMingSongGirlEle } = audioData.mingSongTypeCollection
+	if(mingSongEle&&mingSongGirlEle){
+		const mingSongType = audioData.mingSongType
+		audioData.songCollection.mingSongEle = mingSongType === 1 ? mingSongEle : mingSongGirlEle
+		audioData.songCollection.betaMingSongEle = mingSongType === 1 ? beatMingSongEle : beatMingSongGirlEle
+	}
+}
 export default defineComponent({
 	name: "audio-list",
 	setup() {
@@ -299,11 +316,11 @@ export default defineComponent({
 		}
 		// 加载音源
 		function loadAudio(){
-			return Promise.all([createAudio(state.music), createAudio(state.accompany), createAudio(state.fanSong), createAudio(state.banSong), createAudio(state.mingSong)])
+			return Promise.all([createAudio(state.music), createAudio(state.accompany), createAudio(state.fanSong), createAudio(state.banSong), createAudio(state.mingSong), createAudio(state.mingSongGirl)])
 		}
 		// 加载节拍器音源
 		function loadBeatAudio(){
-			return Promise.all([createAudio(state.beatSong.music), createAudio(state.beatSong.accompany), createAudio(state.beatSong.fanSong), createAudio(state.beatSong.banSong), createAudio(state.beatSong.mingSong)])
+			return Promise.all([createAudio(state.beatSong.music), createAudio(state.beatSong.accompany), createAudio(state.beatSong.fanSong), createAudio(state.beatSong.banSong), createAudio(state.beatSong.mingSong), createAudio(state.beatSong.mingSongGirl)])
 		}
 		onMounted(async () => {
 			// 预览的时候不走音频加载逻辑
@@ -314,7 +331,7 @@ export default defineComponent({
 			if (state.playMode !== "MIDI") {
 				console.time("音频加载时间")
 				// 处理音源
-				const [music, accompany, fanSong, banSong, mingSong] = await loadAudio()
+				const [music, accompany, fanSong, banSong, mingSong, mingSongGirl] = await loadAudio()
 				audioData.backgroundEle = accompany;
 				audioData.songEle = music;
 				Object.assign(audioData.songCollection, {
@@ -324,6 +341,10 @@ export default defineComponent({
 					banSongEle:banSong,
 					mingSongEle:mingSong
 				})
+				Object.assign(audioData.mingSongTypeCollection, {
+					mingSongEle: mingSong,
+					mingSongGirlEle:mingSongGirl
+				})
 				if (music) {
 					music.addEventListener("play", onPlay);
 					music.addEventListener("ended", onEnded);
@@ -346,14 +367,22 @@ export default defineComponent({
 					mingSong.addEventListener("play", onPlay);
 					mingSong.addEventListener("ended", onEnded);
 				}
+				if(mingSongGirl){
+					mingSongGirl.addEventListener("play", onPlay);
+					mingSongGirl.addEventListener("ended", onEnded);
+				}
 				// 处理带节拍器的音源
-				const [beatMusic, beatAccompany, beatFanSong, beatBanSong, beatMingSong] = await loadBeatAudio()
+				const [beatMusic, beatAccompany, beatFanSong, beatBanSong, beatMingSong, beatMingSongGirl] = await loadBeatAudio()
 				Object.assign(audioData.songCollection, {
 					beatSongEle:beatMusic,
 					betaBackgroundEle:beatAccompany,
 					betaFanSongEle:beatFanSong,
 					betaBanSongEle:beatBanSong,
-					betaMingSongEle:beatMingSong
+					beatMingSongEle:beatMingSong
+				})
+				Object.assign(audioData.mingSongTypeCollection, {
+					beatMingSongEle: beatMingSong,
+					beatMingSongGirlEle: beatMingSongGirl
 				})
 				if (beatMusic) {
 					beatMusic.addEventListener("play", onPlay);
@@ -372,10 +401,24 @@ export default defineComponent({
 					beatBanSong.addEventListener("play", onPlay);
 					beatBanSong.addEventListener("ended", onEnded);
 					beatMingSong && (beatMingSong.muted = true);
-				} else if(beatMingSong){
+				} 
+				if(beatMingSong){
 					beatMingSong.addEventListener("play", onPlay);
 					beatMingSong.addEventListener("ended", onEnded);
+				}				
+				if(beatMingSongGirl){
+					beatMingSongGirl.addEventListener("play", onPlay);
+					beatMingSongGirl.addEventListener("ended", onEnded);
 				}
+				// 给男声女声赋值
+				const userGender = storeData.user.gender
+				// 当不为null 和undefined的时候 取userGender的值
+				if(userGender!==undefined && userGender!==null){
+					audioData.mingSongType = userGender
+				}
+				// 重新根据性别赋值
+				changeMingSongType()
+
 				state.audioDone = true;
 				state.isLoading = false
 				console.timeEnd("音频加载时间")

+ 1 - 1
src/view/evaluating/index.module.less

@@ -33,5 +33,5 @@
 }
 
 .socketErrorStatus{
-    top: 10vh;
+    top: 20vh;
 }

+ 12 - 11
src/view/fingering/fingering-config.ts

@@ -264,7 +264,7 @@ export const matchVoicePart = (id: number | string, type: "SINGLE" | "CONCERT"):
   } else {
     let code = id;
     const subject: { [_key: string | number]: any } = {
-      Piccolo: "piccolo",
+      Piccolo: 1,
       Flute: 2,
       "Flute 1": 2,
       "Flute 2": 2,
@@ -453,16 +453,16 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
         hasTizhi: true,
         id: 2,
       };
-    // case "piccolo": // 德式竖笛
-    //   return {
-    //     name: "piccolo",
-    //     direction: "vertical",
-    //     width: "3rem",
-    //     orientation: 0,
-    //     code: "竖笛",
-    //     hasTizhi: true,
-    //     id: 37,
-    //   };
+    case "piccolo": // 德式竖笛
+      return {
+        name: "piccolo",
+        direction: "vertical",
+        width: "3rem",
+        orientation: 0,
+        code: "竖笛",
+        hasTizhi: true,
+        id: 37,
+      };
     case "hulusi-flute": // 葫芦丝
       return {
         name: "hulusi-flute",
@@ -510,6 +510,7 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
         hasTizhi: false,
         id: 39,
       };
+    case 137: // 口风琴
     case "melodica": // 口风琴
       return {
         name: "melodica",

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/view/fingering/fingering-img/baroque-recorder/index.json


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/view/fingering/fingering-img/ocarina/index.json


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/view/fingering/fingering-img/piccolo/index.json


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/view/fingering/fingering-img/whistling/index.json


+ 7 - 1
src/view/fingering/index.tsx

@@ -27,6 +27,12 @@ export default defineComponent({
       return state.times[state.activeNoteIndex]?.realKey || -1;
     });
 
+    // 竖笛、陶笛,云教练的乐器图片不需要正面两个字
+    const specialBgFinger = computed(() => {
+      const list: any = ['baroque-recorder', 'piccolo', 'ocarina', 'whistling']
+      return list.includes(state.fingeringInfo.name);
+    });
+
     const doubeClick = () => {
       const nowTime = Date.now();
       if (nowTime - fingerData.delay < 300) {
@@ -61,7 +67,7 @@ export default defineComponent({
           ) : (
             <div onClick={() => doubeClick()} class={[styles.fingeringContainer, styles.vertical, state.fingeringInfo.name, state.fingeringInfo.name === 'hulusi-flute' ? styles.hulusiTop : '']}>
               <div class={styles.imgs}>
-                <img class="driver-7" src={fingerData.subject?.json?.full} />
+                <img class="driver-7" src={specialBgFinger.value ? fingerData.subject?.json?.fullSpe : fingerData.subject?.json?.full} />
                 {rs.map((key: number | string, index: number) => {
                   const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
                   return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;

+ 3 - 0
src/view/music-score/index.module.less

@@ -22,6 +22,9 @@
             left: 0;
             top: 0;
         }
+        // #osmdCanvasPage2 {
+        //     display: none;
+        // }
     }
     .noteActive {
         path {

+ 7 - 0
src/view/music-score/index.tsx

@@ -150,6 +150,13 @@ export default defineComponent({
 			if(state.isSimplePage){
 				state.zoom = 0.5
 			}
+			// 需要渲染总谱的云教练页面
+			if (!state.isSimplePage && state.isCombineRender) {
+				for (let i = 0; i < osmd.Sheet.Instruments.length; i++) {
+					const trackName = osmd.Sheet.Instruments[i].Name || '';
+					osmd.Sheet.Instruments[i].Visible = state.canSelectTracks.includes(trackName)
+				  }
+			}
 			osmd.zoom = state.zoom;
 			osmd.render();
 			console.log("🚀 ~ osmd:", osmd)

+ 12 - 2
src/view/plugins/toggleMusicSheet/choosePartName/index.module.less

@@ -26,8 +26,18 @@
 }
 &.evaluating{
     .head{
-        background: url("../../../../page-instrument/header-top/image/headImg2.png") no-repeat;
-        background-size: 100% 100%; 
+      width: 394px;
+      height: 62px;
+      margin-bottom: -3px;
+      background: url("/src/page-instrument/header-top/image/headImg2.png") no-repeat;
+      background-size: 100% 100%; 
+      .headTit{
+          bottom: 11px;
+      }
+      .closeImg{
+          top: 2px;
+          right: -26px;
+      }
     }
     .pickerCon{
         background: #B0CDFF;

+ 2 - 0
src/view/plugins/toggleMusicSheet/index.tsx

@@ -38,6 +38,8 @@ export default defineComponent({
           canselect
         }
       }).filter((item: any) => item.canselect).sort((a: any, b: any) => a.sortId - b.sortId)
+      // 支持总谱渲染的时候 加上总谱字段
+      state.isScoreRender && arr.unshift({canselect:true, sortId:999, text: "总谱", value: 999})
       return arr
     })
 

+ 14 - 14
src/view/plugins/useDrag/index.module.less

@@ -7,20 +7,20 @@
   //overflow: hidden;
   position: absolute;
   bottom: 0;
-  .box {
-    position: relative;
-    bottom: 10px;
-    width: 20px;
-    height: 20px;
-    background: url('./img/left.png') no-repeat;
-    background-size: 100% 100%;
-    border-bottom-left-radius: 16Px;
-    &.right {
-      background: url('./img/right.png') no-repeat;
-      background-size: 100% 100%;
-      border-bottom-right-radius: 16Px;
-    }
-  }
+  // .box {
+  //   position: relative;
+  //   bottom: 10px;
+  //   width: 20px;
+  //   height: 20px;
+  //   background: url('./img/left.png') no-repeat;
+  //   background-size: 100% 100%;
+  //   border-bottom-left-radius: 16Px;
+  //   &.right {
+  //     background: url('./img/right.png') no-repeat;
+  //     background-size: 100% 100%;
+  //     border-bottom-right-radius: 16Px;
+  //   }
+  // }
 }
 .guide {
   position: absolute;

BIN=BIN
src/view/selection/imgs/arrow_icon.png


+ 14 - 12
src/view/selection/index.module.less

@@ -163,19 +163,20 @@
     align-items: center;
     background: rgba(0,0,0,0.6);
     position: relative;
-    padding: 3px 6px;
+    padding: 6px 10px;
     border-radius: 16px;
     width: fit-content;
     left: 50%;
-    top: -30px;
+    top: -42px;
     transform: translate(-50%);
     img {
-        width: 14px;
-        height: 14px;
-        margin-right: 4px;
+        width: 18px;
+        height: 18px;
+        margin-right: 6px;
     }
     span {
-        font-size: 12px;
+        font-size: 14px;
+        font-weight: 500;
         color: #fff;
         word-break: keep-all;
         display: flex;
@@ -185,13 +186,14 @@
         content: "";
         position: absolute;
         left: 50%;
-        bottom: -7PX;
+        bottom: -8PX;
         transform: translateX(-50%);
-        width: 0;
-        height: 0;
-        border-top: 8PX solid rgba(0,0,0,0.6);
-        border-right: 8PX solid transparent;
-        border-left: 8PX solid transparent;  
+        width: 13Px;
+        height: 9Px;
+        background-image: url('./imgs/arrow_icon.png');
+        background-size: 100% 100%;
+        background-position: center center;
+        background-repeat: no-repeat;
         z-index: 2;    
     }
 }

+ 46 - 13
src/view/selection/index.tsx

@@ -145,19 +145,51 @@ const calcNoteData = () => {
 				MeasureNumberXMLList.push(item.MeasureNumberXML);
 			} else {
 				if (item.multipleRestMeasures) {
-					const preItem = selectData.staves.find(
-						(n: any) => n.MeasureNumberXML === item.MeasureNumberXML - 1
-					);
-					if (preItem?.staveBox) {
-						noteItem.staveBox = {
-							left: preItem.staveBox.left,
-							top: preItem.staveBox.top,
-							width: preItem.staveBox.width,
-							// height: preItem.staveBox.height,
-						};
-						selectData.staves.push(noteItem);
-						MeasureNumberXMLList.push(item.MeasureNumberXML);
+					console.log(111111)
+					if (state.isCombineRender) {
+						state.vfmeasures.forEach((item: any, idx: number) => {
+							const measureNum = item.getAttribute('data-num') ? Number(item.getAttribute('data-num')) : -1;
+							
+						})
+						let currentItem = null;
+						for (let index = 0; index < state.vfmeasures.length; index++) {
+							const element = state.vfmeasures[index];
+							const measureNum = element.getAttribute('data-num') ? Number(element.getAttribute('data-num')) : -1;
+							if (measureNum === item.MeasureNumberXML) {
+								currentItem = element
+								break;
+							}
+							
+						}
+						const staveBbox = currentItem?.querySelector('.vf-stave')?.getBoundingClientRect() || { x: 0, width: 0, y: 0, height: 0 };
+						if (currentItem) {
+							noteItem.staveBox = {
+								left: staveBbox.x - parentLeft + "px",
+								// top: ((item.stave.y || 0) - 5) * state.zoom + "px",
+								top: staveBbox.y - parentTop  + "px",
+								width: staveBbox.width + "px",
+								height: staveBbox.height + "px",
+								// height: preItem.staveBox.height,
+							};
+							selectData.staves.push(noteItem);
+							MeasureNumberXMLList.push(item.MeasureNumberXML);
+						}
+					} else {
+						const preItem = selectData.staves.find(
+							(n: any) => n.MeasureNumberXML === item.MeasureNumberXML - 1
+						);
+						if (preItem?.staveBox) {
+							noteItem.staveBox = {
+								left: preItem.staveBox.left,
+								top: preItem.staveBox.top,
+								width: preItem.staveBox.width,
+								// height: preItem.staveBox.height,
+							};
+							selectData.staves.push(noteItem);
+							MeasureNumberXMLList.push(item.MeasureNumberXML);
+						}
 					}
+
 				}
 			}
 		}
@@ -236,6 +268,7 @@ export default defineComponent({
 				onClick={(e: Event) => e.stopPropagation()}
 			>
 				{selectData.staves.map((item: any) => {
+					// 评测得分
 					const scoreItem = item.id && evaluatingData.evaluatings[item.measureListIndex];
 					// for(let idx in evaluatingData.evaluatings) {
 					// 	const { show, measureIndex } = evaluatingData.evaluatings[idx]
@@ -260,7 +293,7 @@ export default defineComponent({
 								<div
 									class={[
 										styles.position,
-										scoreItem ? `scoreItemLeve${scoreItem.leve}` : "",
+										// scoreItem ? `scoreItemLeve${scoreItem.leve}` : "", // 去掉评测小节得分的背景色
 										item.multipleRestMeasures <= 1 ? styles.staveBg : "",
 										(state.platform === IPlatform.PC && state.zoom > 0.8) ? styles.linePC : '',
 									]}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio