Bladeren bron

Merge branch 'feature-tianyong' into gym-dev

TIANYONG 6 maanden geleden
bovenliggende
commit
a209253305

+ 11 - 13
src/helpers/formateMusic.ts

@@ -366,7 +366,7 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
 	const detailId = state.examSongId + "";
 	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
 	const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
-	const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
+	//const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
 	const parts: any = xmlParse.getElementsByTagName("part");
 	// const firstTimeInfo = parts[0]?.getElementsByTagName('metronome')[0]?.parentElement?.parentElement?.cloneNode(true)
 	const firstMeasures = [...parts[0]?.getElementsByTagName("measure")];
@@ -376,14 +376,14 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
 	const rehearsals = [...parts[0]?.getElementsByTagName("rehearsal")];
 
 	/** 第一分谱如果是约定的配置分谱则跳过 */
-	if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
-		partIndex++;
-		partListNames.shift();
-	}
+	// if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
+	// 	partIndex++;
+	// 	partListNames.shift();
+	// }
 	const visiblePartInfo = partList[partIndex];
 	// console.log(visiblePartInfo, partIndex)
 	// 根据后台已选择的分轨筛选出能切换的声轨
-	state.partListNames = partListNames;
+	//state.partListNames = partListNames;
 	// console.log('分轨名称',state.partListNames)
 	if (visiblePartInfo) {
 		const id = visiblePartInfo.getAttribute("id");
@@ -523,14 +523,11 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
 export const onlyVisible2 = (xml: string): string => {
 	if (!xml) return "";
 	// console.log('原始xml')
-	const detailId = state.examSongId + "";
+	//const detailId = state.examSongId + "";
 	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
 	const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
-	const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
-
-
-	state.partListNames = partListNames;
-
+	//const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
+	//state.partListNames = partListNames;
 	Array.from(partList).forEach((part) => {
 		let partListName = part.getElementsByTagName("part-name")?.[0]?.textContent?.trim();
 		if (!state.canSelectTracks.includes(partListName)) {
@@ -863,7 +860,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 	let differFrom = 0;
 	// let testIdx = 0;
 	let repeatIdx = 0; // 循环的次数
-	const firstTrackName = state.canSelectTracks[0] || "";
+	// 当多选声部的时候 ,取选择的第一个声部
+	const firstTrackName = state.combinePartIndexs.length>1 ? state.partListNames[state.combinePartIndexs[0]] : state.canSelectTracks[0] || "";
 	while (!iterator.EndReached) {
 		// console.log({ ...iterator });
 		/** 多声轨合并显示,当前音符的时值取所有声轨中的最小值 */

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

@@ -87,6 +87,7 @@ export default defineComponent({
         ...query
       }
       queryObj.id = item.id
+      queryObj["part-index"] = ""
       location.href =
         location.origin +
         location.pathname +

+ 8 - 0
src/page-instrument/simple-detail/index.module.less

@@ -44,5 +44,13 @@ body {
             height: 0 !important;
             overflow: hidden;
         }
+        #cursorImg-0 {
+            margin-top: -20PX !important;
+            transform: translateX(6PX) !important;
+        }
     }
 }
+
+.whiteBg {
+    background: #fff;
+}

+ 6 - 4
src/page-instrument/simple-detail/index.tsx

@@ -19,6 +19,7 @@ export default defineComponent({
 			isLoading: true,
 			currentTime: 0, // 当前播放的时间
 			totalTime: 0, // 音视频总时长
+			backgroundRendMode: "" as "video" | "audio", // 嵌入的时音频还是视频
 		});
 
 		const communicateCb = (res: any) => {
@@ -98,6 +99,7 @@ export default defineComponent({
 
 		onMounted(async () => {
 			const id = query.id || '';
+			detailData.backgroundRendMode = query.backgroundRendMode;
 			state.isSimplePage = true;
 			await getMusicDetail(id, 'open');
 			detailData.isLoading = false;
@@ -128,7 +130,7 @@ export default defineComponent({
 			console.log("🚀 ~ state.times:", state.times, state);
 			nextTick(() => {
 				state.activeMeasureIndex = state.times[0].MeasureNumberXML;
-				fillWordColor();
+				// fillWordColor();
 			})
 			// 音符添加位置信息bbox
 			addNoteBBox(state.times);
@@ -193,14 +195,14 @@ export default defineComponent({
 		};
 
 		return () => (
-			<div class={styles.detail}>
+			<div class={[styles.detail, detailData.backgroundRendMode === 'video' && styles.whiteBg]}>
 				<div class={styles.mask}></div>
-				<div id="scrollContainer" class={[styles.container, "hideCursor"]}>
+				<div id="scrollContainer" class={[styles.container]}>
 					{/* 曲谱渲染 */}
 					{!detailData.isLoading && 
 					<MusicScore 
 						onRendered={handleRendered} 
-						musicColor={'#FFFFFF'}
+						musicColor={'#000000'}
 					/>}
 				</div>
 			</div>

+ 21 - 9
src/state.ts

@@ -370,6 +370,8 @@ const state = reactive({
   track: "",
   /** 当前显示声部索引 */
   partIndex: 0,
+  /** 总谱渲染时候 只显示部分声部的值 */
+  combinePartIndexs:[],
   /** 演奏是否需要节拍器 */
   needTick: false,
   /** 演唱模式是否需要节拍器 */
@@ -1386,13 +1388,26 @@ const getMusicInfo = async (res: any) => {
   state.defaultScoreRender = res.data?.defaultScoreRender
   // 是否显示节拍器
   state.isMixBeat = res.data?.isMixBeat
-  let partIndex = query["part-index"] ? parseInt(query["part-index"]) : -1 // -1为partIndex没有值的时候
+  /* 设置partIndex */
+  let partIndexs = query["part-index"] ? query["part-index"].split(",") : ["-1"] // -1为partIndex没有值的时候
+  partIndexs = partIndexs.map((indexStr:string) => {
+    return parseInt(indexStr)
+  })
+  let partIndex = partIndexs[0]
+  // 当partIndexs 大于1个的时候,代表用户自己选择了多个声部,用总谱渲染的逻辑
+  if(partIndexs.length > 1){
+    partIndex = 999
+    state.combinePartIndexs = partIndexs
+  }
   // 如果是评测报告,会有默认的分轨index
   if (state.isEvaluatReport) {
     partIndex = state.partIndex;
   }
   // 布置作业 取作业的乐器id
   let workRecordInstrumentId:undefined | string
+  // multiTracksSelection 返回为空,默认代表全部分轨
+  state.canSelectTracks = res.data.multiTracksSelection === "null" || res.data.multiTracksSelection === "" || res.data.multiTracksSelection === null ? [] : res.data.multiTracksSelection?.split(',');
+  state.canSelectTracks = state.canSelectTracks.map((item: any)=>item.trim())
   /* 获取声轨列表 */
   let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
   xmlString = xmlAddPartName(xmlString);
@@ -1459,10 +1474,9 @@ function initMusicSource(data: any, tracks: string[], partIndex: number, workRec
   } else {
     /* 合奏 */
     // 支持总谱 并且当前是总谱。partIndex是999时候,或者默认是总谱并且partIndex为-1时候  -1就是partIndex没有值
-    if(state.isScoreRender && (partIndex===999 || (state.defaultScoreRender && partIndex===-1))){
+    if((state.isScoreRender && (partIndex===999 || (state.defaultScoreRender && partIndex===-1))) || state.combinePartIndexs.length > 1){
         // 总谱渲染
         state.isCombineRender = true
-        state.partListNames = tracks
         banSongObj = musicSheetAccompanimentList.find((item: any) => {
           return item.audioPlayType === "SING"
         })
@@ -1482,8 +1496,8 @@ function initMusicSource(data: any, tracks: string[], partIndex: number, workRec
         index = 999
         musicalInstrumentId = ''
     }else{
-      // 合奏只显示一个声轨
-      track = tracks[partIndex===-1?0:partIndex]
+      // 合奏只显示一个声轨   当为-1时候,取tracks中 后端勾选了的第一个值
+      track =  partIndex === -1 ? tracks.find(value => state.canSelectTracks.includes(value))! : tracks[partIndex]
       // 根据当前的声轨 取数据
       musicObj = musicSheetSoundList.find((item: any) => {
         return item.audioPlayType === "PLAY" && item.track === track
@@ -1502,6 +1516,7 @@ function initMusicSource(data: any, tracks: string[], partIndex: number, workRec
       })
       musicalInstrumentId = musicObj?.musicalInstrumentId
     }
+    state.partListNames = tracks
   }
   // 当没有任何曲目的时候报错
   if (!musicObj?.audioFileUrl && !accompanyObj?.audioFileUrl && !fanSongObj?.audioFileUrl && !banSongObj?.audioFileUrl && !fanSongObj?.solmizationFileUrl && !fanSongObj?.femaleSolmizationFileUrl) {
@@ -1678,9 +1693,6 @@ const setState = (data: any, index: number) => {
     state.enableEvaluation = state.accompany || state.music ? true : false
   }
   state.isConcert = data.musicSheetType === "CONCERT" ? true : false;
-  // multiTracksSelection 返回为空,默认代表全部分轨
-  state.canSelectTracks = data.multiTracksSelection === "null" || data.multiTracksSelection === "" || data.multiTracksSelection === null ? [] : data.multiTracksSelection?.split(',');
-  state.canSelectTracks = state.canSelectTracks.map((item: any)=>item.trim())
   // 开启预备小节
   state.isOpenPrepare = true;
   state.extStyleConfigJson = data.extStyleConfigJson || {}
@@ -2036,7 +2048,7 @@ watch(
         measureNum = nextMeasureNum
       }
       if (measureNum >= 0 && (measureNum === state.activeMeasureIndex || (measureNum < state.activeMeasureIndex && nextMeasureNum > state.activeMeasureIndex))) {
-        item.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(1, 193, 181, 0.2)")
+        item.querySelector('.vf-custom-bg')?.setAttribute("fill", state.isSimplePage ? "rgba(45, 199, 170, 0.3)" : "rgba(1, 193, 181, 0.2)")
         // 预备小节
         if(state.sectionFirst && measureNum === state.sectionFirst.MeasureNumberXML && state.section.length === 2){
           item?.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(255, 193, 48, 0.15)")

+ 5 - 0
src/view/fingering/index.module.less

@@ -147,6 +147,11 @@
   }
 }
 
+.transTizhi {
+  position: absolute;
+  top: -20px;
+}
+
 .canDisplay {
   opacity: 1;
   pointer-events: initial;

+ 3 - 5
src/view/fingering/index.tsx

@@ -36,10 +36,8 @@ export default defineComponent({
     });
 
     const doubeClick = () => {
-      // 如果在评测和跟练中,双击指法不跳转
-      if ((state.modeType === 'evaluating' && evaluatingData.startBegin) || (state.modeType === 'follow' && followData.start) || state.playState === "play") {
-        return;
-      }
+      // 管乐迷没有乐器指法页面,暂时不需要双击跳转
+      return;
       const nowTime = Date.now();
       if (nowTime - fingerData.delay < 300) {
         emit("open");
@@ -68,7 +66,7 @@ export default defineComponent({
               </div>
 
               {state.fingeringInfo.hasTizhi && (
-                <div class={[styles.tizhi, canTizhi && styles.canDisplay]} onClick={() => (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)}>
+                <div class={[styles.tizhi, styles.transTizhi, canTizhi && styles.canDisplay]} onClick={() => (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)}>
                   替指
                 </div>
               )}

+ 3 - 2
src/view/music-score/index.tsx

@@ -160,10 +160,11 @@ export default defineComponent({
 			}
 			// 需要渲染总谱的云教练页面
 			if (!state.isSimplePage && state.isCombineRender) {
+				const canSelectTracks = state.combinePartIndexs.length > 1 ? state.combinePartIndexs.map(partIndex => { return state.partListNames[partIndex] }) : state.canSelectTracks
 				for (let i = 0; i < osmd.Sheet.Instruments.length; i++) {
 					const trackName = state.isEvxml && state.evxmlAddPartName ? osmd.Sheet.Instruments[i].idString || '' : osmd.Sheet.Instruments[i].Name || '';
-					osmd.Sheet.Instruments[i].Visible = state.canSelectTracks.includes(trackName)
-				  }
+					osmd.Sheet.Instruments[i].Visible = canSelectTracks.includes(trackName)
+				}
 			}
 			osmd.zoom = state.zoom;
 			osmd.render();

BIN
src/view/plugins/toggleMusicSheet/choosePartName/imgs/cancelBtn.png


+ 74 - 31
src/view/plugins/toggleMusicSheet/choosePartName/index.module.less

@@ -25,44 +25,87 @@
     height: 290px;
     background: #FFFFFF;
     border-radius: 16px;
-    padding: 36px 16px 0 16px;
+    padding: 36px 20px 12px 20px;
     .pickerBox{
       width: 100%;
-      height: 100%;
+      height: calc(100% - 40px);
+      display: flex;
+      flex-direction: column;
       overflow: hidden;
-    }
-  }
-  .picker {
-    height: 188px;
-    overflow: hidden;
-    background-color: initial;
-    display: flex;
-    align-items: center;
-    :global{
-      .van-picker__columns{
-        width: 100%;
-        .van-picker__frame{
-          border-top: 1px solid #E9E9E9;
-          border-bottom: 1px solid #E9E9E9;
-          right: 0;
-          left: 0;
+      .titCon{
+        display: flex;
+        align-items: center;
+        margin-top: 10px;
+        &:first-child{
+          margin-top: 0;
+        }
+        .tit{
+          font-weight: 600;
+          font-size: 15px;
+          color: #131415;
+          line-height: 21px;
+        }
+        .tips{
+          margin-left: 6px;
+          font-weight: 400;
+          font-size: 13px;
+          color: #777777;
+          line-height: 1;
         }
       }
-      .van-picker-column__wrapper{
-        padding: 0 16px;
+      .content{
+        margin-top: 10px;
+        &.sheetCon{
+          flex-grow: 1;
+          overflow: hidden;
+          .con{
+            width: 100%;
+            height: calc(100% - 4px);
+            overflow-y: auto;
+            &::-webkit-scrollbar {
+                width: 0;
+                display: none;
+            }
+          }
+        }
+        .selBtn{
+          width: 100%;
+          height: 34px;
+          line-height: 34px;
+          background: #F6F6F6;
+          border-radius: 6px;
+          font-weight: 400;
+          font-size: 14px;
+          color: #333333;
+          text-align: center;
+          cursor: pointer;
+          border:1px solid transparent;
+          margin-top: 10px;
+          &:first-child{
+            margin-top: 0;
+          }
+          &.active{
+            background: #F2FFFC;
+            border-color: #01C1B5;
+            color: #00B2A7;
+          }
+        }
       }
-      .van-picker-column__item{
-        font-weight: 600;
-        font-size: 15px;
+    }
+    .btnCon{
+      display: flex;
+      justify-content: center;
+      .btn{
+        width: 118px;
+        height: 40px;
+        cursor: pointer;
+        &:active,&:hover{
+          opacity: 0.8;
+        }
+        & + img{
+          margin-left: 20px;
+        }
       }
     }
   }
-  .button {
-    display: block;
-    margin: 10px auto 0;
-    cursor: pointer;
-    width: 118px;
-    height: 40px;
-    z-index: 9;
-  }
 }

+ 66 - 46
src/view/plugins/toggleMusicSheet/choosePartName/index.tsx

@@ -1,11 +1,12 @@
 import { PropType, computed, defineComponent, ref, toRefs, onMounted, watch, nextTick } from 'vue'
-import { Picker, Button, Icon } from 'vant'
 import styles from './index.module.less'
 import state, { IPlatform, checkMoveNoSave } from "/src/state";
+import { showToast } from 'vant'
 import changeName from "./imgs/changeName.png"
 import { headImg } from "/src/page-instrument/header-top/image";
 import { toggleMusicSheet } from "../index"
 import okBtn from "./imgs/okBtn.png"
+import cancelBtn from "./imgs/cancelBtn.png"
 
 export default defineComponent({
   name: 'choosePartName',
@@ -14,46 +15,45 @@ export default defineComponent({
       type: Array as PropType<any[]>,
       default: () => [],
     },
-    partIndex: {
-      type: Number,
-      default: 0,
+    partIndexs: {
+      type: Array as PropType<number[]>,
+      default: () => []
     },
   },
   emits: ['close'],
   setup(props, { emit }) {
-    const selValues = ref([props.partIndex]);
-    const myPicker = ref();
+    const selValues = ref([...props.partIndexs]);
     watch(
       () => toggleMusicSheet.show,
       () => {
         if (toggleMusicSheet.show) {
-          selValues.value = [props.partIndex]
+          selValues.value = [...props.partIndexs]
         }
       }
     );
-    watch(() => toggleMusicSheet.show, ()=>{
-        // 支持滚轮事件
-        if (toggleMusicSheet.show) {
-          nextTick(() => {
-            myPicker.value.$el.addEventListener('wheel', handleWheel)
-          })
-        } else {
-            myPicker.value.$el.removeEventListener('wheel', handleWheel)
+    function hanldeSelSheet(value:number, isCombineRender = false){
+      if(isCombineRender){
+        selValues.value = [value]
+      }else{
+        // 总谱切换过来的时候
+        if(selValues.value[0] === 999){
+          selValues.value = []
+        }
+        const index = selValues.value.indexOf(value)
+        if(index > -1){
+          if(selValues.value.length > 1){
+            selValues.value.splice(index, 1)
+          }
+        }else{
+          if(selValues.value.length >=4){
+            showToast({
+              position: "top",
+              message: "最多可选4个"
+            });
+            return
+          }
+          selValues.value.push(value)
         }
-      },{immediate:true}
-    )
-    function handleWheel(e: WheelEvent){
-      e.preventDefault()
-      // 先停止 惯性滚动
-      myPicker.value.confirm()
-      const direction = e.deltaY > 0 ? 1 : -1
-      const targetObject = myPicker.value.getSelectedOptions(0)[0]
-      const index = props.partListNames.findIndex(
-          obj => obj == targetObject
-      )
-      const newIndex = index + direction
-      if (newIndex >= 0 && newIndex < props.partListNames.length) {
-        selValues.value = [props.partListNames[newIndex].value]
       }
     }
     return () => (
@@ -64,23 +64,43 @@ export default defineComponent({
         </div>
         <div class={styles.pickerCon}>
           <div class={styles.pickerBox}>
-            <Picker
-              ref={myPicker}
-              class={[styles.picker, state.platform === IPlatform.PC && styles.pcPicker]}
-              v-model={selValues.value}
-              showToolbar={false}
-              columns={props.partListNames}
-              visible-option-num={5}
-              option-height={"1.06666rem"}
-            />
-            <img src={ okBtn } class={styles.button} onClick={async () => {
-                await checkMoveNoSave();
-                myPicker.value.confirm()
-                nextTick(()=>{
-                  emit('close', selValues.value[0])
-                })
-              }
-            }></img>
+            {
+              state.isScoreRender &&
+                <>
+                  <div class={styles.titCon}>
+                    <div class={styles.tit}>选择总谱</div>
+                  </div>
+                  <div class={styles.content}>
+                    <div class={[styles.selBtn, selValues.value.includes(999) && styles.active]} onClick={()=>{ hanldeSelSheet(999, true) }}>总谱</div>
+                  </div>
+                </>
+            }
+            <div class={styles.titCon}>
+              <div class={styles.tit}>选择声部</div>
+              <div class={styles.tips}>(最多可选4个)</div>
+            </div>
+            <div class={[styles.content, styles.sheetCon]}>
+              <div class={styles.con}>
+                {
+                  props.partListNames.map((item: any)=>{
+                    return <div class={[styles.selBtn,selValues.value.includes(item.value) && styles.active]} onClick={()=>{hanldeSelSheet(item.value)}}>{item.text}</div>
+                  })
+                }
+              </div>
+            </div>
+          </div>
+          <div class={styles.btnCon}>
+              <img src={ cancelBtn } class={styles.btn} onClick={async () => {
+                  emit('close')
+                }
+              }></img>
+              <img src={ okBtn } class={styles.btn} onClick={async () => {
+                  await checkMoveNoSave();
+                  nextTick(()=>{
+                    emit('close', selValues.value)
+                  })
+                }
+              }></img>
           </div>
         </div>
       </div>

+ 8 - 13
src/view/plugins/toggleMusicSheet/index.tsx

@@ -40,24 +40,19 @@ export default defineComponent({
       }).filter((item: any) => item.canselect)
       // 不需要自定义排序,改为按照xml声轨顺序显示
       // .sort((a: any, b: any) => a.sortId - b.sortId)
-      // 支持总谱渲染的时候 加上总谱字段
-      state.isScoreRender && arr.unshift({canselect:true, sortId:999, text: "总谱", value: 999})
       return arr
     })
 
-    const trackIdx: any = computed(() => {
-      if (partListNames && partListNames.value.length) {
-        
-        const idx = partListNames.value.find((item: any) => item.value == state.partIndex)?.value || 0
-        console.log(3333,idx)
-        return idx
-      } else {
-        return 0
+    const partIndexs: any = computed(() => {
+      if(state.combinePartIndexs.length > 1){
+        return state.combinePartIndexs
+      }else{
+        return [state.partIndex]
       }
     })
 
-    const switchMusic = (index: number) => {
-      if (state.partIndex === index) return
+    const switchMusic = (partIndexs: number[]) => {
+      const index = partIndexs.join(",") as any
       // 暂停播放
       togglePlay("paused");
       // 销毁播放器
@@ -114,7 +109,7 @@ export default defineComponent({
     return () => (
       <Popup v-model:show={toggleMusicSheet.show} class="popup-custom van-scale center-closeBtn switchBoxClass_drag" transition="van-scale" teleport="body" style={positionInfo.styleDrag.value} overlay-style={{background:'rgba(0, 0, 0, 0.3)'}}>
         <ChoosePartName
-          partIndex={trackIdx.value || 0}
+          partIndexs={partIndexs.value}
           partListNames={partListNames.value}
           onClose={(value) => {
             console.log("🚀 ~ value:", value)