50 Commits 04ad9dbdb4 ... 58d53a572e

Tác giả SHA1 Thông báo Ngày
  TIANYONG 58d53a572e build 4 tháng trước cách đây
  TIANYONG d53b6ac96f Merge branch 'feature-patch' into gym-online 4 tháng trước cách đây
  TIANYONG 1ac7cb4ef4 feat: 滑音点击区域修改 4 tháng trước cách đây
  TIANYONG 8eb1974095 feat: 滑音点击区域修改 4 tháng trước cách đây
  TIANYONG 854268e6ef build 4 tháng trước cách đây
  TIANYONG 91f9f996ae Merge branch 'feature-patch' into gym-online 4 tháng trước cách đây
  TIANYONG cb40a890c7 feat: 滑音点击区域待修改 4 tháng trước cách đây
  TIANYONG 28b6160f57 Merge branch 'feature-patch' into gym-online 4 tháng trước cách đây
  TIANYONG 3458a574ec feat: 缩小滑音音符的点击区域 4 tháng trước cách đây
  TIANYONG 0735d409ef feat: 计算时值过滤掉和弦音符 4 tháng trước cách đây
  TIANYONG 74a021e7d7 build 4 tháng trước cách đây
  TIANYONG 9eab3b52d4 Merge branch 'feature-patch' into gym-online 4 tháng trước cách đây
  TIANYONG 3ebd3ac9ea feat: 多轨修改 4 tháng trước cách đây
  TIANYONG 99c4162bf3 feat: 多轨修改 4 tháng trước cách đây
  TIANYONG 1abd94a37d build 4 tháng trước cách đây
  TIANYONG 65e356a23f Merge branch 'feature-patch' into gym-online 4 tháng trước cách đây
  TIANYONG 3cb9327ea7 feat: 手动点击音符不执行谱面滚动 4 tháng trước cách đây
  TIANYONG 2dda67a5f7 feat: scrollTo滚动top值修改 4 tháng trước cách đây
  TIANYONG 0f3303431b build 4 tháng trước cách đây
  TIANYONG f240b35114 Merge branch 'feature-patch' into gym-online 4 tháng trước cách đây
  TIANYONG 1784f558bc fest: 判断timeLog是否可用 4 tháng trước cách đây
  TIANYONG 150c0a49df feat: osmd对象Instruments过滤掉common 4 tháng trước cách đây
  TIANYONG a26aee8752 build 4 tháng trước cách đây
  TIANYONG 39e9fc2a00 feat: 管理端进行延迟检测 4 tháng trước cách đây
  TIANYONG 94672b33fe feat: 文案修改 4 tháng trước cách đây
  TIANYONG 8adbaabe53 feat: 评测记录保存成功后再显示评测结果弹窗 4 tháng trước cách đây
  TIANYONG 7e285c6eea feat: 评测记录保存成功后再显示评测结果弹窗 4 tháng trước cách đây
  TIANYONG 0f88cb9f9a Merge branch 'gym-online' into feature-patch 4 tháng trước cách đây
  TIANYONG 38bd53ad36 build 5 tháng trước cách đây
  TIANYONG 587dd71205 Merge branch 'feature-tianyong' into gym-online 5 tháng trước cách đây
  TIANYONG a061a9023e feat: noteId赋值修改 5 tháng trước cách đây
  TIANYONG 6792e2a7ed feat: 评测报告兼容没有音频的情况 5 tháng trước cách đây
  TIANYONG ef87abb12e fix: #12279问题5修复 5 tháng trước cách đây
  TIANYONG 741ebd6bd4 feat: 单行谱页面不需要调用乐器code接口 5 tháng trước cách đây
  TIANYONG 09569f065d feat: 先不调用api_closeCamera 5 tháng trước cách đây
  TIANYONG bcc8a9f6af feat: 单行谱页面不需要调用乐器code接口 5 tháng trước cách đây
  TIANYONG 866226f8b8 feat: api:recordAudioUpload,增加recordId参数 5 tháng trước cách đây
  TIANYONG de498d9b96 feat: useNativeEvaluation赋值 5 tháng trước cách đây
  TIANYONG e9cbba9b87 feat: 增加是否使用原生评测服务字段 5 tháng trước cách đây
  TIANYONG 29c5273b12 feat: 增加recordAudioUpload 5 tháng trước cách đây
  TIANYONG 0245188d02 style: 评测得分样式优化 5 tháng trước cách đây
  TIANYONG b6262d8223 fix: #12279问题5修改 5 tháng trước cách đây
  TIANYONG fab1443b96 build 5 tháng trước cách đây
  TIANYONG 131f567759 Merge remote-tracking branch 'origin/hqyDev' into feature-tianyong 5 tháng trước cách đây
  TIANYONG 4228389c4a feat: 选段非第一小节不播放系统节拍器 5 tháng trước cách đây
  TIANYONG b079398276 fix: 转简谱基准时值逻辑修改 5 tháng trước cách đây
  TIANYONG 27ae2051ac feat: 音频比选段的xml时值短兼容 5 tháng trước cách đây
  TIANYONG e0b840aee1 fix: #12279第一、第二个问题修复 5 tháng trước cách đây
  TIANYONG d0beb036ed fix: 12991、12992、12993bug修复 5 tháng trước cách đây
  TIANYONG 037e435b1a feat: 选段模式兼容音频时长比xml时值短的情况 5 tháng trước cách đây
35 tập tin đã thay đổi với 381 bổ sung79 xóa
  1. 0 0
      dist/css/instrument-13bbe6a6.css
  2. 4 4
      dist/instrument.html
  3. 0 0
      dist/js/index-06f55027.js
  4. 0 0
      dist/js/index-23fa57ac.js
  5. 1 1
      dist/js/index-90b167b5.js
  6. 0 0
      dist/js/index-95b4b606.js
  7. 0 0
      dist/js/index-legacy-1d002e56.js
  8. 1 1
      dist/js/index-legacy-40f6f324.js
  9. 0 0
      dist/js/index-legacy-cd14361c.js
  10. 0 0
      dist/js/index-legacy-e247fb81.js
  11. 0 0
      dist/js/instrument-d0e01293.js
  12. 0 0
      dist/js/instrument-legacy-b744f6aa.js
  13. 0 0
      dist/js/modeView-ecdb2634.js
  14. 0 0
      dist/js/modeView-legacy-2e07befc.js
  15. 0 0
      dist/js/src-1e27eb4c.js
  16. 0 0
      dist/js/src-f076abb8.js
  17. 0 0
      dist/js/src-legacy-53d6b23f.js
  18. 0 0
      dist/js/src-legacy-f790ad4c.js
  19. 1 1
      osmd-extended
  20. 9 1
      src/helpers/communication.ts
  21. 202 25
      src/helpers/formateMusic.ts
  22. 1 0
      src/page-instrument/component/the-music-list/list.tsx
  23. 7 2
      src/page-instrument/custom-plugins/guide-driver/index.tsx
  24. 24 6
      src/page-instrument/evaluat-model/evaluat-result/index.tsx
  25. 27 3
      src/page-instrument/evaluat-model/index.tsx
  26. 5 2
      src/page-instrument/view-detail/index.tsx
  27. 6 1
      src/page-instrument/view-evaluat-report/index.tsx
  28. 32 7
      src/state.ts
  29. 12 1
      src/view/audio-list/index.tsx
  30. 30 15
      src/view/evaluating/index.tsx
  31. 3 2
      src/view/music-score/index.tsx
  32. 1 1
      src/view/plugins/toggleMusicSheet/index.tsx
  33. 3 3
      src/view/selection/index.module.less
  34. 12 3
      src/view/selection/index.tsx
  35. 0 0
      stats.html

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/css/instrument-13bbe6a6.css


+ 4 - 4
dist/instrument.html

@@ -41,10 +41,10 @@
       })
     }
   </script>
-  <script type="module" crossorigin src="./js/instrument-f5925bb3.js"></script>
+  <script type="module" crossorigin src="./js/instrument-d0e01293.js"></script>
   <link rel="modulepreload" crossorigin href="./js/node_modules-081fca9f.js">
-  <link rel="modulepreload" crossorigin href="./js/src-f076abb8.js">
-  <link rel="stylesheet" href="./css/instrument-9723cd86.css">
+  <link rel="modulepreload" crossorigin href="./js/src-1e27eb4c.js">
+  <link rel="stylesheet" href="./css/instrument-13bbe6a6.css">
   <script type="module">import.meta.url;import("_").catch(()=>1);async function* g(){};window.__vite_is_modern_browser=true;</script>
   <script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy chunks, syntax error above and the same error below should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
 </head>
@@ -128,7 +128,7 @@
   </script>   -->
   <script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
   <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-00a2b340.js"></script>
-  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/instrument-legacy-6649239b.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/instrument-legacy-b744f6aa.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/index-06f55027.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/index-23fa57ac.js


+ 1 - 1
dist/js/index-10637826.js → dist/js/index-90b167b5.js

@@ -1 +1 @@
-import{d as s,g as a,r as e,E as t,o,s as n,c as i,M as r}from"./instrument-f5925bb3.js";import"./node_modules-081fca9f.js";import"./src-f076abb8.js";const d="_detail_vtlsh_12",l="_container_vtlsh_20",c=s({name:"music-list",setup(){const s=a(),c=e({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:t.staff,base64:""},{state:!1,name:"首调",type:t.firstTone,base64:""},{state:!1,name:"固定调",type:t.fixedTone,base64:""}]});o((()=>{window.appName="colexiu",n.xmlUrl=s.xmlUrl,c.isLoading=!1}));const m=async()=>{console.log("渲染完成")};return()=>i("div",{class:d},[i("div",{id:"scrollContainer",class:[l,"hideCursor"]},[!c.isLoading&&i(r,{onRendered:m},null)])])}});export{c as default};
+import{d as s,g as a,r as e,E as t,o,s as n,c as i,M as r}from"./instrument-d0e01293.js";import"./node_modules-081fca9f.js";import"./src-1e27eb4c.js";const d="_detail_vtlsh_12",l="_container_vtlsh_20",c=s({name:"music-list",setup(){const s=a(),c=e({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:t.staff,base64:""},{state:!1,name:"首调",type:t.firstTone,base64:""},{state:!1,name:"固定调",type:t.fixedTone,base64:""}]});o((()=>{window.appName="colexiu",n.xmlUrl=s.xmlUrl,c.isLoading=!1}));const m=async()=>{console.log("渲染完成")};return()=>i("div",{class:d},[i("div",{id:"scrollContainer",class:[l,"hideCursor"]},[!c.isLoading&&i(r,{onRendered:m},null)])])}});export{c as default};

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/index-95b4b606.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/index-legacy-1d002e56.js


+ 1 - 1
dist/js/index-legacy-5fa744d7.js → dist/js/index-legacy-40f6f324.js

@@ -1 +1 @@
-System.register(["./instrument-legacy-6649239b.js","./node_modules-legacy-cc3cd557.js","./src-legacy-53d6b23f.js"],(function(e,t){"use strict";var n,i,a,o,s,r,l,d,c=document.createElement("style");return c.textContent="._skeleton_vtlsh_1{position:fixed;left:0;top:0;width:100vw;height:100vh;padding:.53333rem .8rem;background-color:#fff;z-index:1000;--van-skeleton-paragraph-height: .8rem}._detail_vtlsh_12{width:100vw;height:100vh;overflow:hidden;overflow-y:auto;--header-height: 1.65333rem;background:var(--container-background)}._detail_vtlsh_12 ._container_vtlsh_20{margin:0 .26667rem;border-radius:.26667rem}._detail_vtlsh_12 #musicAndSelection{overflow:initial!important;height:initial!important;max-height:initial!important}\n",document.head.appendChild(c),{setters:[e=>{n=e.d,i=e.g,a=e.r,o=e.E,s=e.o,r=e.s,l=e.c,d=e.M},null,null],execute:function(){const t="_detail_vtlsh_12",c="_container_vtlsh_20";e("default",n({name:"music-list",setup(){const e=i(),n=a({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:o.staff,base64:""},{state:!1,name:"首调",type:o.firstTone,base64:""},{state:!1,name:"固定调",type:o.fixedTone,base64:""}]});s((()=>{window.appName="colexiu",r.xmlUrl=e.xmlUrl,n.isLoading=!1}));const h=async()=>{console.log("渲染完成")};return()=>l("div",{class:t},[l("div",{id:"scrollContainer",class:[c,"hideCursor"]},[!n.isLoading&&l(d,{onRendered:h},null)])])}}))}}}));
+System.register(["./instrument-legacy-b744f6aa.js","./node_modules-legacy-cc3cd557.js","./src-legacy-f790ad4c.js"],(function(e,t){"use strict";var n,i,a,o,s,r,l,d,c=document.createElement("style");return c.textContent="._skeleton_vtlsh_1{position:fixed;left:0;top:0;width:100vw;height:100vh;padding:.53333rem .8rem;background-color:#fff;z-index:1000;--van-skeleton-paragraph-height: .8rem}._detail_vtlsh_12{width:100vw;height:100vh;overflow:hidden;overflow-y:auto;--header-height: 1.65333rem;background:var(--container-background)}._detail_vtlsh_12 ._container_vtlsh_20{margin:0 .26667rem;border-radius:.26667rem}._detail_vtlsh_12 #musicAndSelection{overflow:initial!important;height:initial!important;max-height:initial!important}\n",document.head.appendChild(c),{setters:[e=>{n=e.d,i=e.g,a=e.r,o=e.E,s=e.o,r=e.s,l=e.c,d=e.M},null,null],execute:function(){const t="_detail_vtlsh_12",c="_container_vtlsh_20";e("default",n({name:"music-list",setup(){const e=i(),n=a({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:o.staff,base64:""},{state:!1,name:"首调",type:o.firstTone,base64:""},{state:!1,name:"固定调",type:o.fixedTone,base64:""}]});s((()=>{window.appName="colexiu",r.xmlUrl=e.xmlUrl,n.isLoading=!1}));const h=async()=>{console.log("渲染完成")};return()=>l("div",{class:t},[l("div",{id:"scrollContainer",class:[c,"hideCursor"]},[!n.isLoading&&l(d,{onRendered:h},null)])])}}))}}}));

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/index-legacy-cd14361c.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/index-legacy-e247fb81.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/instrument-d0e01293.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/instrument-legacy-b744f6aa.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/modeView-ecdb2634.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/modeView-legacy-2e07befc.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/src-1e27eb4c.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/src-f076abb8.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/src-legacy-53d6b23f.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/js/src-legacy-f790ad4c.js


+ 1 - 1
osmd-extended

@@ -1 +1 @@
-Subproject commit 6b139a79b2825dd9e4621336e29f5db52c771092
+Subproject commit 727729315b605fcb615570b822952decbbf63a08

+ 9 - 1
src/helpers/communication.ts

@@ -557,4 +557,12 @@ export const simple_musicPage = (content: any) => {
 /** 监听重新评测消息 */
 export const api_retryEvaluating = (callback: any) => {
 	listenerMessage("retryEvaluating", callback);
-}
+}
+
+/** 通知app上传评测音频 */
+export const api_recordAudioUpload = (content: any) => {
+	postMessage({
+		api: "recordAudioUpload",
+		content,
+	});
+};

+ 202 - 25
src/helpers/formateMusic.ts

@@ -534,22 +534,188 @@ export const onlyVisible = (xml: string, partIndex: number, resourceType?: strin
 	return new XMLSerializer().serializeToString(appoggianceFormate(xmlParse));
 };
 
-export const onlyVisible2 = (xml: string): string => {
+export const onlyVisible2 = (xml: string, partIndexs: Array<any>, resourceType?: string): string => {
 	if (!xml) return "";
 	// console.log('原始xml')
-	//const detailId = state.examSongId + "";
-	console.time('解析xml 耗时5')
-	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
-	console.timeEnd('解析xml 耗时5')
+	const detailId = state.examSongId + "";
+	//console.time('解析xml 耗时4')
+	// const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	const xmlParse = xmlDocRef.value ? xmlDocRef.value : new DOMParser().parseFromString(xml, "text/xml");
+	//console.timeEnd('解析xml 耗时4')
 	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;
-	Array.from(partList).forEach((part) => {
-		let partListName = part.getElementsByTagName("part-name")?.[0]?.textContent?.trim();
-		if (!state.canSelectTracks.includes(partListName)) {
-			part.parentNode?.removeChild(part);
+	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 hasMeasureIdx = Array.from(parts).findIndex((item: any) => item.getElementsByTagName("measure").length) || 0;
+	const firstMeasures = [...parts[hasMeasureIdx]?.getElementsByTagName("measure")];
+	const metronomes = [...parts[0]?.getElementsByTagName("metronome")];
+	const words = [...parts[0]?.getElementsByTagName("words")];
+	const codas = [...parts[0]?.getElementsByTagName("coda")];
+	const rehearsals = [...parts[0]?.getElementsByTagName("rehearsal")];
+
+	/** 第一分谱如果是约定的配置分谱则跳过 */
+	if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
+		partIndexs = partIndexs.map(item => item + 1)
+		//partListNames.shift();
+	}
+	const visiblePartInfo = partList[partIndexs[0]];
+	let ids: any = [];
+	partIndexs.forEach(item => {
+		if (partList[item]) {
+			ids.push(partList[item]?.getAttribute("id"))
 		}
-	});
+	})
+	// console.log(visiblePartInfo, partIndex)
+	// 根据后台已选择的分轨筛选出能切换的声轨
+	//state.partListNames = partListNames;
+	// console.log('分轨名称',state.partListNames)
+	if (visiblePartInfo && ids.length) {
+		const id = visiblePartInfo.getAttribute("id");
+		Array.from(parts).forEach((part: any) => {
+			if (part && !ids.includes(part.getAttribute("id")) ) {
+				part.parentNode?.removeChild(part);
+				// 不等于第一行才添加避免重复添加
+			} else if (part && part.getAttribute("id") !== "P1") {
+				if (part && part.getAttribute("id") === id) {
+					// 速度标记仅保留最后一个
+					const metronomeData: {
+						[key in string]: Element;
+					} = {};
+					for (let i = 0; i < metronomes.length; i++) {
+						const metronome = metronomes[i];
+						const metronomeContainer = metronome.parentElement?.parentElement?.parentElement;
+						if (metronomeContainer) {
+							const index = firstMeasures.indexOf(metronomeContainer);
+							metronomeData[index] = metronome;
+						}
+					}
+					Object.values(metronomeData).forEach((metronome) => {
+						const metronomeContainer: any = metronome.parentElement?.parentElement;
+						const parentMeasure: any = metronomeContainer?.parentElement;
+						const measureMetronomes = [...(parentMeasure?.childNodes || [])];
+						const metronomesIndex = metronomeContainer ? measureMetronomes.indexOf(metronomeContainer) : -1;
+						// console.log(parentMeasure)
+						if (parentMeasure && metronomesIndex > -1) {
+							const index = firstMeasures.indexOf(parentMeasure);
+							const activeMeasure = part.getElementsByTagName("measure")[index];
+							setElementNoteBefore(metronomeContainer, parentMeasure, activeMeasure);
+						}
+					});
+					/** word比较特殊需要精确到note位置 */
+					words.forEach((word) => {
+						let text = word.textContent || "";
+						text = ["cresc."].includes(text) ? "" : text;
+						if ((isSpecialMark(text) || isSpeedKeyword(text) || isGradientWords(text) || isRepeatWord(text) || GRADIENT_SPEED_RESET_TAG) && text) {
+							const wordContainer = word.parentElement?.parentElement;
+							const parentMeasure = wordContainer?.parentElement;
+							const measureWords = [...(parentMeasure?.childNodes || [])];
+							const wordIndex = wordContainer ? measureWords.indexOf(wordContainer) : -1;
+							if (wordContainer && parentMeasure && wordIndex > -1) {
+								const index = firstMeasures.indexOf(parentMeasure);
+								const activeMeasure = part.getElementsByTagName("measure")[index];
+								// 找当前小节是否包含word标签
+								const _words = Array.from(activeMeasure?.getElementsByTagName("words") || []);
+								// 遍历word标签,检查是否和第一小节重复,如果有重复则不平移word
+								const total = _words.reduce((total: any, _word: any) => {
+									if (_word.textContent?.includes(text)) {
+										total++;
+									}
+									return total;
+								}, 0);
+								if (total === 0) {
+									if (["12280"].includes(detailId)) {
+										activeMeasure?.insertBefore(wordContainer.cloneNode(true), activeMeasure?.childNodes[wordIndex]);
+									} else {
+										setElementNoteBefore(wordContainer, parentMeasure, activeMeasure);
+									}
+								}
+							}
+						}
+					});
+					/** word比较特殊需要精确到note位置 */
+					codas.forEach((coda) => {
+						const wordContainer = coda.parentElement?.parentElement;
+						const parentMeasure = wordContainer?.parentElement;
+						const measureWords = [...(parentMeasure?.childNodes || [])];
+						const wordIndex = wordContainer ? measureWords.indexOf(wordContainer) : -1;
+						if (wordContainer && parentMeasure && wordIndex > -1) {
+							const index = firstMeasures.indexOf(parentMeasure);
+							const activeMeasure = part.getElementsByTagName("measure")[index];
+							if (["12280"].includes(detailId)) {
+								activeMeasure?.insertBefore(wordContainer.cloneNode(true), activeMeasure?.childNodes[wordIndex]);
+							} else {
+								setElementNoteBefore(wordContainer, parentMeasure, activeMeasure);
+							}
+						}
+					});
+					rehearsals.forEach((rehearsal) => {
+						const container = rehearsal.parentElement?.parentElement;
+						const parentMeasure = container?.parentElement;
+						// console.log(rehearsal)
+						if (parentMeasure) {
+							const index = firstMeasures.indexOf(parentMeasure);
+							part.getElementsByTagName("measure")[index]?.appendChild(container.cloneNode(true));
+							// console.log(index, parentMeasure, firstMeasures.indexOf(parentMeasure))
+						}
+					});
+				}
+
+			} else {
+				if (part && part.getAttribute("id") === id) {
+					words.forEach((word, idx) => {
+						const text = word.textContent || "";
+						// if (idx == 0 && text) {
+						// 	word.textContent = '测试一下'
+						// 	word.setAttribute('default-y',60)
+						// 	word.setAttribute('margin-left',300)
+						// 	word.setAttribute('y',300)
+						// 	word.outerHTML = '<words default-x="155" default-y="100" justify="right" valign="middle" font-family="SimHei" font-style="normal" font-size="11.9365" font-weight="normal">哈哈哈哈哈</words>'
+						// }
+						if (isSpeedKeyword(text) && text) {
+							const wordContainer = word.parentElement?.parentElement?.parentElement;
+							if (wordContainer && wordContainer.firstElementChild && wordContainer.firstElementChild !== word) {
+								const wordParent = word.parentElement?.parentElement;
+								const fisrt = wordContainer.firstElementChild;
+								wordContainer.insertBefore(wordParent, fisrt);
+							}
+						}
+					});
+				}
+
+			}
+
+			// 最后一个小节的结束线元素不在最后 调整
+			if (part && part.getAttribute("id") === id) {
+				if (!resourceType) {
+					const backups = Array.from(part.getElementsByTagName('backup')) || []
+					for (let backup of backups) {
+						// @ts-ignore
+						if (backup && backup?.getElementsByTagName('duration')?.length) {
+							state.isSingleMutliTrack = true;
+							break;
+						}
+					}
+				}
+				const barlines = part.getElementsByTagName("barline");
+				const lastParent = barlines[barlines.length - 1]?.parentElement;
+				if (lastParent?.lastElementChild?.tagName !== "barline") {
+					const children = lastParent?.children || [];
+					for (let el of children) {
+						if (el.tagName === "barline") {
+							// 将结束线元素放到最后
+							lastParent?.appendChild(el);
+							break;
+						}
+					}
+				}
+			}
+		});
+		Array.from(partList).forEach((part) => {
+			if (part && !ids.includes(part.getAttribute("id")) ) {
+				part.parentNode?.removeChild(part);
+			}
+		});
+	}
 	// console.log(xmlParse)
 	return new XMLSerializer().serializeToString(appoggianceFormate(xmlParse));
 };
@@ -751,6 +917,7 @@ export const formatXML = (xml: string, xmlUrl?: string, resourceType?: string):
 	// 前面小节的拍子
 	let preBeats: number = 4;
 	let preBeatType: number = 4;
+	let baseDivisions: number = 256;
 	// 小节中如果没有节点默认为休止符
 	for (const measure of measures) {
 		if (beats === -1 && measure.getElementsByTagName("beats").length) {
@@ -767,7 +934,8 @@ export const formatXML = (xml: string, xmlUrl?: string, resourceType?: string):
 		const currentBeatType = measure.getElementsByTagName("beat-type").length ? measure.getElementsByTagName("beat-type")[0]?.textContent : preBeatType;
 		preBeats = Number(currentBeats);
 		preBeatType = Number(currentBeatType);
-		const divisions = parseInt(measure.getElementsByTagName("divisions")[0]?.textContent || "256");
+		const divisions = parseInt(measure.getElementsByTagName("divisions")[0]?.textContent || String(baseDivisions));
+		baseDivisions = divisions
 		// 如果note节点里面有space节点,并且没有duration节点,代表这是一个空白节点,需要删除
 		if (measure.getElementsByTagName("note").length && state.isEvxml) {
 			const noteList = Array.from(measure.getElementsByTagName("note")) || [];
@@ -905,8 +1073,11 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 	 * 总谱渲染时,需要取第一个渲染的声轨名字,canSelectTracks返回的声轨名字可能不是第一个,顺序有问题,需要用state.osmd.Sheet.Instruments
 	 */
 	// const firstTrackName = state.combinePartIndexs.length>1 ? state.partListNames[state.combinePartIndexs[0]] : state.canSelectTracks[0] || "";
-	const firstTrackName = state.combinePartIndexs.length>1 ? state.partListNames[state.combinePartIndexs[0]] : (state.osmd.Sheet.Instruments[0].Name || state.osmd.Sheet.Instruments[0].NameLabel.text || "");
-	const currentTrackIndex = state.isCombineRender && state.combinePartIndexs.length > 1 ? state.combinePartIndexs[0] : 0;
+	const filterInstruments = state.osmd.Sheet.Instruments.filter(item => item.Name?.toLocaleLowerCase() !== 'common' )
+	const firstTrackName = state.combinePartIndexs.length>1 ? state.partListNames[state.combinePartIndexs[0]] : (filterInstruments[0].Name || filterInstruments[0].NameLabel.text || "");
+	// const currentTrackIndex = state.isCombineRender && state.combinePartIndexs.length > 1 ? state.combinePartIndexs[0] : 0;
+	const currentTrackIndex = 0;
+
 	while (!iterator.EndReached) {
 		// console.log({ ...iterator });
 		/** 多声轨合并显示,当前音符的时值取所有声轨中的最小值 */
@@ -965,7 +1136,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				notes.push(...n.Notes);
 				return notes;
 			}, [] as any);
-			voiceNotes = voiceNotes.filter((note: any) => !note.IsGraceNote)
+			// 过滤掉倚音和和弦音符
+			voiceNotes = voiceNotes.filter((note: any) => (!note.IsGraceNote && !note.IsChordNote) )
 			voiceNotes = voiceNotes.sort((a: any, b: any) => a?.length?.realValue - b?.length?.realValue);
 			currentTime = voiceNotes?.[0]?.length?.realValue || 0;
 
@@ -1274,14 +1446,18 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				 * 管乐迷,部分弱起的曲目,mp3制作不标准,没有按照补齐弱起后的时间进行制作,需要单独处理
 				 * 2670
 				*/
-				if (["2670"].includes(state.cbsExamSongId)) {
-					// fixtime -= _firstMeasureRealValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
-				} else {
-					if (difftime > 0 && !state.isEvxml) {
-						fixtime += difftime;
-						state.fixtime = fixtime;
-					}
-				}
+				// if (["2670"].includes(state.cbsExamSongId)) {
+				// 	// fixtime -= _firstMeasureRealValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
+				// } else {
+				// 	if (difftime > 0 && !state.isEvxml) {
+				// 		fixtime += difftime;
+				// 		state.fixtime = fixtime;
+				// 	}
+				// }
+				if (difftime > 0 && !state.isEvxml) {
+					fixtime += difftime;
+					state.fixtime = fixtime;
+				}				
 				// 管乐迷 diff获取不准确时, 弱起补齐
 				if (["2589", "2561", "2560", "2559", "2558", "2556", "2555", "2554"].includes(detailId)) {
 					// difftime = iterator.currentTimeStamp.realValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
@@ -1403,7 +1579,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				trackIndex: note.trackIndex, // 当前的音符属于第几条分轨
 				isStaccato: note.voiceEntry.isStaccato(),
 				isRestFlag: note.isRestFlag,
-				noteId: note.NoteToGraphicalNoteObjectId,
+				noteId: note.NoteToGraphicalNoteObjectId === undefined ? `restNote${note.sourceMeasure.MeasureNumberXML}` : note.NoteToGraphicalNoteObjectId,
+				// noteId: note.NoteToGraphicalNoteObjectId,
 				measureListIndex: note.sourceMeasure.measureListIndex,
 				MeasureNumberXML: note.sourceMeasure.MeasureNumberXML, // 当前的小节数,(从1开始)
 				_noteLength: _noteLength,

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

@@ -83,6 +83,7 @@ export default defineComponent({
       if (item.id === state.examSongId) return;
       // 暂停播放
       togglePlay("paused");
+      state.evaluatAudioInitDone = false
       postMessage({
         api: "cloudLoading",
         content: {

+ 7 - 2
src/page-instrument/custom-plugins/guide-driver/index.tsx

@@ -1346,13 +1346,18 @@ export const EvaluatingReportDriver = defineComponent({
             title: "",
             description: "",
             popoverClass: "popoverClass popoverClassReport3 popoverClose",
-            align: "start",
+            align: "end",
             side: "bottom",
             prevBtnText: "再看一遍",
             doneBtnText: "完成",
             showButtons: ["next", "previous"],
             onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
-              driverInitialPosition(popover, options);
+              options.config.stageRadius = 8;
+              options.config.stagePadding = 5;
+              try {
+                const rect = options.state.activeElement?.getBoundingClientRect();
+                popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 4 + "px";
+              } catch {}
             },
             onPrevClick: () => {
               driverObj.drive(0);

+ 24 - 6
src/page-instrument/evaluat-model/evaluat-result/index.tsx

@@ -1,5 +1,5 @@
 import { defineComponent, onMounted, reactive, watch, computed } from "vue";
-import { Popover } from "vant";
+import { Popover, showToast } from "vant";
 import styles from "./index.module.less";
 import state from "/src/state";
 import icon1 from "../icons/1.png";
@@ -23,6 +23,8 @@ import { api_musicPracticeRecordSave } from "../../api";
 import { getAudioDuration } from "/src/view/audio-list";
 import { debounce } from "/src/utils";
 import { EvaluatingResultDriver } from "../../custom-plugins/guide-driver";
+import { api_recordAudioUpload } from "/src/helpers/communication";
+import { uploadErrorLog } from '/src/hooks/errorLog/uploadLog'
 
 export default defineComponent({
   name: "evaluatResult",
@@ -73,10 +75,26 @@ export default defineComponent({
         body.lessonDetailId = query.evaluatingRecord
       }
       data.saveLoading = true;
-      const res = await api_musicPracticeRecordSave(body);
-      if (res?.code === 200) {
-        evaluatingData.resultData.recordId = res.data;
+      try {
+        const res = await api_musicPracticeRecordSave(body);
+        if (res?.code === 200) {
+          evaluatingData.resultData.recordId = res.data;
+          // 如果评测结果没有返回音频,需要调用api通知APP端上传音频
+          if (!evaluatingData.resultData.url && evaluatingData.resultData.recordId) {
+            api_recordAudioUpload({
+              recordId: evaluatingData.resultData.recordId
+            })
+          }
+        }
+      } catch (err:any) {
+        const contentError = `reason: ${err?.message || ''};stack: ${err?.stack || ''};bizId: ${state.examSongId || query.id || ''};partIndex: ${query["part-index"] || state.partIndex || 0};partName: ${decodeURIComponent(query["part-name"] || '') || ''};`;
+        uploadErrorLog(contentError)
+        evaluatingData.resulstMode = false
+        showToast({
+          message: "评测信息保存失败",
+        });
       }
+      state.isLoading = false
       evaluatingData.needReplayEvaluat = evaluatingData.oneselfCancleEvaluating ? true : false;
       data.saveLoading = false;
     };
@@ -122,7 +140,7 @@ export default defineComponent({
     );
     return () => (
       <>
-        {!evaluatingData.hideResultModal && (
+        {!evaluatingData.hideResultModal && evaluatingData.resultData.recordId && (
           <div class={styles.evaluatResult}>
             <div class={styles.closeBtn} onClick={() => emit("close")}>
               <img src={iconBack} />
@@ -193,7 +211,7 @@ export default defineComponent({
                     ) : null}
                   </div>
                 ) : null}
-                <img src={ckzpImg} class={[styles.ctrlsBtn, "evaluting-result-4", data.saveLoading ? styles.disablued : ""]} onClick={() => emit("close", "look")} />
+                <img src={ckzpImg} class={[styles.ctrlsBtn, "evaluting-result-4"]} onClick={() => emit("close", "look")} />
               </div>
             </div>
 

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

@@ -178,6 +178,8 @@ export default defineComponent({
       let skip = false;
       const datas = [];
       let selectTimes = state.times;
+      // 选段评测前面小节的listen、play标识
+      let preLyricsContent = ''
       let unitTestIdx = 0;
       let preTime = 0;
       let preTimes = [];
@@ -218,10 +220,27 @@ export default defineComponent({
       if (state.section.length === 2 && firstNoteTime === 0 && state.section[0]?.MeasureNumberXML === state.firstMeasureNumber + 1 && state.times[0].fixtime) {
         actualBeatLength  = actualBeatLength + Math.round((state.times[0].fixtime * 1000) / 1)
       }
-
+      // 找到选段评测,开始小节前面最近的是play或者listen的小节
+      if (preTimes.length) {
+        for (let index = preTimes.length-1; index >= 0; index--) {
+          const item = preTimes[index]
+          // const note = getNoteByMeasuresSlursStart(item)
+          const note = item
+          if (note.formatLyricsEntries.contains('Play') || note.formatLyricsEntries.contains('Play...')) {
+            preLyricsContent = 'Play'
+            break
+          }
+          if (note.formatLyricsEntries.contains('Listen')) {
+            preLyricsContent = 'Listen'
+            break
+          }
+        }
+        preLyricsContent = preLyricsContent ? preLyricsContent : 'Play'
+      } 
       for (let index = 0; index < selectTimes.length; index++) {
         const item = selectTimes[index];
-        const note = getNoteByMeasuresSlursStart(item);
+        // const note = getNoteByMeasuresSlursStart(item);
+        const note = item;
         // #8701 bug: 评测模式,是以曲谱本身的速度进行评测,所以rate取1,不需要转换
         // const rate = state.speed / state.originSpeed;
         const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率
@@ -231,6 +250,10 @@ export default defineComponent({
         const end = difftime + (item.sourceRelaEndtime || item.relaEndtime) - starTime;
         const isStaccato = note.noteElement.voiceEntry.isStaccato();
         const noteRate = isStaccato ? 0.5 : 1;
+        // 如果选段评测,开始小节没有注脚,则取前面最近的小节的注脚
+        if (index == 0 && !note.formatLyricsEntries.length) {
+          ListenMode = preLyricsContent === 'Play' ? false : preLyricsContent === 'Listen' ? true : false
+        }        
         if (note.formatLyricsEntries.contains("Play") || note.formatLyricsEntries.contains("Play...")) {
           ListenMode = false;
         }
@@ -528,7 +551,8 @@ export default defineComponent({
       // 如果打开了延迟检测开关,需要先发送开始检测的消息
       const delayData = await api_getDeviceDelay();
       console.log('设备的延迟值',delayData.content?.value)
-      if (delayData && delayData.content?.value <= 0 && state.paymentType !== "LOCK") {
+      // 管理端链接上有systemType=web的参数,管理端人员的paymentType是lock,管理端人员需要进行延迟检测,非管理端非会员不进行延迟检测
+      if (delayData && delayData.content?.value <= 0 && (state.paymentType !== "LOCK" || query.systemType === 'web') ) {
         await api_startDelayCheck({});
       } else {
         evaluatingData.checkEnd = true;

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

@@ -215,8 +215,11 @@ export default defineComponent({
       state.isLoading = false
       api_cloudLoading();
       console.timeEnd("渲染加载耗时");
-      console.timeLog('加载过程','谱面渲染完成')
-      
+      if (typeof console.timeLog === 'function') {
+        console.timeLog('加载过程','谱面渲染完成')
+      } else {
+        console.log('加载过程','谱面渲染完成')
+      }
       detailData.skeletonLoading = false;
       state.osmd = osmd;
       // 预览模式不需要往下执行

+ 6 - 1
src/page-instrument/view-evaluat-report/index.tsx

@@ -365,7 +365,12 @@ export default defineComponent({
       console.log("🚀 ~ state.times:", allNote.value);
       // @ts-ignore
       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))
+      // 从0开始的曲子,MeasureNumberXML也是从0开始,需要兼容处理
+      if (state.firstMeasureNumber === 0) {
+        allNote.value = allNote.value.filter((item: any) => (item.MeasureNumberXML >= startMeasureNum && item.MeasureNumberXML <= endMeasureNum))
+      } else {
+        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")));
       beams.forEach((item: any) => {

+ 32 - 7
src/state.ts

@@ -26,6 +26,7 @@ import { HANDLE_WORK_ADD } from "/src/page-instrument/custom-plugins/work-index"
 import { speedBeatTo, unitImgs } from "/src/helpers/beatConfig"
 import IndexedDBService from "/src/utils/indexedDB";
 import { musicalInstrumentCodeInfo, instruments, fixInstrumentNameCode } from "/src/constant/instruments";
+import evaluatModel from "./page-instrument/evaluat-model";
 
 const query: any = getQuery();
 
@@ -389,6 +390,10 @@ const state = reactive({
   isSingleMutliTrack: false,
   /** 是否是来源于缓存的xml */
   xmlFromStore: false,
+  /** 是否已经初始化评测音频,只有切了声轨后,才需要重新传音频,普通的切谱面(五线谱、简谱;单行谱、多行谱等)不需要重复传 */
+  evaluatAudioInitDone: false,
+  /** 是否使用原生评测服务 */
+  useNativeEvaluation: false,
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -528,7 +533,14 @@ const handlePlaying = () => {
       // 如果开启了预备拍
       const selectStartItem = state.sectionFirst ? state.sectionFirst : state.section[0];
       const selectEndItem = state.section[1];
-      if (currentTime - selectEndItem.endtime >= 0) {
+      // 如果选段播放结束,或者音频播放结束(判断条件:currentTime >= duration)
+      // console.log('时间',currentTime,duration)
+      /**
+       * TODO:兼容音频时长比xml时值短的曲子
+       * isAudioShort:音频比选段的xml时值短,部分手机最后一帧返回的currentTime会比duration小,在这里加上一帧的时间(0.1666~0.2)
+       */
+      const isAudioShort = duration < selectEndItem.endtime
+      if ( (currentTime - selectEndItem.endtime >= 0) || (isAudioShort && (currentTime+0.02 >= duration)) ) {
         console.log("选段播放结束", state.setting.repeatAutoPlay);
         // 如果为选段评测模式
         if (state.modeType === "evaluating" && state.isSelectMeasureMode) {
@@ -623,7 +635,7 @@ export const skipNotePlay = async (itemIndex: number, isStart = false, handType?
     }
     setAudioCurrentTime(itemTime, itemIndex);
     // 一行谱,点击音符,或者播放完成,需要跳转音符位置
-    gotoNext(item, true);
+    gotoNext(item, true, handType);
     // 不需要播放节拍器的声音,因为音频带有节拍器的声音
     // metronomeData.metro?.sound(itemTime);
     metronomeData?.metro?.findMetronomePosition(itemTime);
@@ -843,7 +855,7 @@ const setCursorPosition = (note: any, cursor: any, flag?: string) => {
  * 跳转到下一个音符
  * 一行谱,点击音符,或者播放完成,需要跳转音符位置,增加参数skipNote
  **/
-export const gotoNext = (note: any, skipNote?: boolean) => {
+export const gotoNext = (note: any, skipNote?: boolean, handType?: string) => {
   // console.log(33333333333,state.activeNoteIndex,note.i)
   const num = note.i;
 
@@ -913,7 +925,11 @@ export const gotoNext = (note: any, skipNote?: boolean) => {
   if (state.isSingleLine && state.playState === "paused") {
     moveSvgDom(skipNote);
   }
-  scrollViewNote();
+  if (handType === 'manual') {
+    // 手动点击不执行滚动屏幕方法
+  } else {
+    scrollViewNote();
+  }
 };
 /** 获取指定音符 */
 export const getNote = (currentTime: number) => {
@@ -1194,7 +1210,7 @@ export const scrollViewNote = (resetTop?: boolean) => {
   if (offsetTop > (state.headTopHeight + 30)) {
     musicScrollTop = (offsetTop - state.headTopHeight - 30) * state.musicZoom
     musicAndSelection.scrollTo({
-      top: (offsetTop - state.headTopHeight - 30) * state.musicZoom,
+      top: (offsetTop - state.headTopHeight - 100) * state.musicZoom,
       behavior: animateType,
     });
   } else {
@@ -1234,12 +1250,17 @@ export default state;
 
 /** 初始化评测音频 */
 export const evaluatCreateMusicPlayer = () => {
-  return api_createMusicPlayer({
+  if (state.evaluatAudioInitDone) {
+    return;
+  }
+  api_createMusicPlayer({
     musicSrc: state.accompany || state.music, // 曲谱音频url
     // tuneSrc: "https://oss.dayaedu.com/cloud-coach/1686725501654check_music1_(1).mp3", //效音音频url
     tuneSrc: "https://oss.dayaedu.com/MECMP/1722593665681.mp3", //效音音频url
     checkFrequence: 496,
+    useNativeEvaluation: state.useNativeEvaluation // 是否使用原生评测服务
   });
+  state.evaluatAudioInitDone = true
 };
 
 
@@ -1287,7 +1308,10 @@ const queryMusicXml = async (id: string, xmlUr: string) => {
 
 const getMusicInfo = async (res: any) => {
   try {
-    await initInstrumentCode()
+    // 单行谱页面不需要调用此接口
+    if (!state.isSimplePage) {
+      await initInstrumentCode()
+    }
   } catch (error) {
     // console.log(error)
   }
@@ -1295,6 +1319,7 @@ const getMusicInfo = async (res: any) => {
   state.isScoreRender = res.data?.isScoreRender
   // 是否默认显示总谱
   state.defaultScoreRender = res.data?.defaultScoreRender
+  state.useNativeEvaluation = res.data?.useNativeEvaluation
   // let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
   let xmlString: string = await queryMusicXml(res.data.bizId + "", res.data.xmlFileUrl);
   xmlString = xmlAddPartName(xmlString);

+ 12 - 1
src/view/audio-list/index.tsx

@@ -482,8 +482,15 @@ export default defineComponent({
 		 * #11046
 		 * 声音与圆点消失的节点不一致,可能原因是部分安卓手机没有立即播放,所以需要等待有音频进度返回时再播放节拍器
 		 * mp3节拍器的圆点动画
+		 * 
+		 * #12291
+		 * 如果是选段评测,并且开始小节不是第一个小节,不需要播放节拍器的圆点动画
 		 */
 		const tickAnimate = (time: number) => {
+			if (state.section.length && state.section[0]?.MeasureNumberXML !== state.firstMeasureNumber) {
+				evaluatingData.needPlayTick = false;
+				return;
+			}
 			if (storeData.isApp && state.modeType === 'evaluating' && evaluatingData.needPlayTick && time > 0) {
 				evaluatingData.needPlayTick = false;
 				handleStartTick()
@@ -653,7 +660,11 @@ export default defineComponent({
 				state.audioDone = true;
 				// state.isLoading = false
 				console.timeEnd("音频加载耗时")
-				console.timeLog('加载过程','音频加载完成')
+				if (typeof console.timeLog === 'function') {
+					console.timeLog('加载过程','音频加载完成')
+				} else {
+					console.log('加载过程','音频加载完成')
+				}
 				console.log("音频数据:",audioData)
 				api_playProgress(progress);
 			} else {

+ 30 - 15
src/view/evaluating/index.tsx

@@ -36,6 +36,7 @@ import {
   api_closeDelayCheck,
   api_openCamera,
   api_closeCamera,
+  api_recordAudioUpload
 } from "/src/helpers/communication";
 import state, { IPlayState, clearSelection, handleStopPlay, onPlay, resetPlaybackToStart, togglePlay, initSetPlayRate, resetBaseRate, scrollViewNote } from "/src/state";
 import { IPostMessage } from "/src/utils/native-message";
@@ -118,6 +119,7 @@ export const evaluatingData = reactive({
   tipErjiShow: false, // 评测提示弹窗
   onceErjiPopShow: false, // 是否已经提示过耳机弹窗,重新进入评测页面,重置该状态为false,手动关掉耳机弹窗,改变该状态为true,本次评测都不在提示耳机状态弹窗
   needCheckErjiStatus: true, // 点击评测模式进入评测模块的需要检测耳机状态,通过返回按钮进入评测模块的,不检测耳机状态
+  evaluatResultLoading: false, // 评测结果处理中
 });
 
 const sendOffsetTime = async (offsetTime: number) => {
@@ -312,7 +314,7 @@ export const addMeasureScore = (measureScore: any, show = true) => {
   // console.log("🚀 ~ measureScore:", evaluatingData.evaluatings)
 };
 
-const handleScoreResult = (res?: IPostMessage) => {
+const handleScoreResult = async (res?: IPostMessage) => {
   console.log("返回", res, evaluatingData.oneselfCancleEvaluating);
   // 如果是手动取消评测,不生成评测记录
   // if (evaluatingData.oneselfCancleEvaluating) {
@@ -346,6 +348,8 @@ const handleScoreResult = (res?: IPostMessage) => {
           evaluatingData.hideResultModal = true;
         }
         evaluatingData.resulstMode = evaluatingData.isErrorState ? false : true;
+        evaluatingData.startBegin = false;
+        evaluatingData.evaluatResultLoading = false;
       }, 200);
       evaluatingData.resultData = {
         ...body,
@@ -353,7 +357,8 @@ const handleScoreResult = (res?: IPostMessage) => {
       };
       // console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
       closeToast();
-      state.isLoading = false
+      // 评测记录接口调用后再取消评测打分提示loading
+      // state.isLoading = false
     }
   }
 };
@@ -397,15 +402,23 @@ export const handleStartBegin = async (preTimes?: number) => {
 		if (state.playState === "play" && (state.playType==="play"&&state.needTick)||(state.playType==="sing"&&state.needSingTick)) {
 			// 如果是系统节拍器 等系统节拍器播完了再播,如果是mp3节拍器 直接播
 			if((state.playType==="play" && !state.isOpenMetronome)||(state.playType==="sing" && !state.isSingOpenMetronome)){
-				const tickend = await handleStartTick();
-				console.log("🚀 ~ tickend:", tickend)
-				// 节拍器返回false, 取消播放
-				if (!tickend) {
-					state.playState = "paused";
-					evaluatingData.startBegin = false;
-					evaluatingData.isBeginMask = false
-					return;
-				}
+        /**
+        * #12291
+        * 如果是选段评测,并且开始小节不是第一个小节,不需要播放节拍器的圆点动画
+        */        
+        if (state.section.length && state.section[0]?.MeasureNumberXML !== state.firstMeasureNumber) {
+          // 
+        } else {
+          const tickend = await handleStartTick();
+          console.log("🚀 ~ tickend:", tickend)
+          // 节拍器返回false, 取消播放
+          if (!tickend) {
+            state.playState = "paused";
+            evaluatingData.startBegin = false;
+            evaluatingData.isBeginMask = false
+            return;
+          }
+        }
 			}else{
 				// handleStartTick()
         // 需要等待音频返回进度后再执行节拍器圆点动画
@@ -508,11 +521,12 @@ const recordStartTimePoint = async (res?: IPostMessage) => {
  * @returns
  */
 export const handleEndEvaluat = (isComplete = false, endType?: string) => {
-  // 没有开始评测 , 不是评测模式 , 不评分
-  if (!evaluatingData.startBegin || state.modeType !== "evaluating") return;
+  // 没有开始评测 , 不是评测模式 , 不评分;evaluatResultLoading:评测结果处理中,避免重复结束
+  if (!evaluatingData.startBegin || state.modeType !== "evaluating" || evaluatingData.evaluatResultLoading) return;
   // 结束录音
   // api_stopRecording();
   // 结束评测
+  evaluatingData.evaluatResultLoading = true
   console.log("评测结束1");
   endEvaluating({
     musicScoreId: state.examSongId,
@@ -535,7 +549,7 @@ export const handleEndEvaluat = (isComplete = false, endType?: string) => {
     }
   }
   setTimeout(() => {
-    evaluatingData.startBegin = false;
+    // evaluatingData.startBegin = false;
     if (endType === 'selfCancel') {
       // 重置播放倍率
       const item: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[0];
@@ -639,6 +653,7 @@ export const handleViewReport = (key: "recordId" | "recordIdStr", type: "gym" |
     statusBarTextColor: false,
     isOpenLight: true,
     c_orientation: 0,
+    showLoadingAnim: true
   });
 };
 
@@ -841,7 +856,7 @@ export default defineComponent({
       } else {
         removeSocketStatus(handleSocketStatus);
       }
-      api_closeCamera();
+      // api_closeCamera();
       api_disconnectSocket();
       console.log("卸载评测模块成功");
     });

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

@@ -1,5 +1,5 @@
 import { computed, defineComponent, onMounted, reactive, ref, onUnmounted } from "vue";
-import { formatXML, onlyVisible, getCustomInfo } from "../../helpers/formateMusic";
+import { formatXML, onlyVisible, getCustomInfo, onlyVisible2 } from "../../helpers/formateMusic";
 // // @ts-ignore
 import { OpenSheetMusicDisplay } from "/osmd-extended/src";
 import state, { EnumMusicRenderType, IPlatform, resetCursorPosition } from "/src/state";
@@ -95,7 +95,8 @@ export default defineComponent({
 			const xmlStr = downloadXmlStr.value;
 			const parseXmlInfo = getCustomInfo(xmlStr, cbType);
 			const xml = formatXML(parseXmlInfo.parsedXML, '', cbType);
-			musicData.score = state.isCombineRender ? xml : onlyVisible(xml, state.partIndex);
+			// musicData.score = state.isCombineRender ? xml : onlyVisible(xml, state.partIndex);
+			musicData.score = state.isCombineRender && state.combinePartIndexs.length === 0 ? xml : state.isCombineRender && state.combinePartIndexs.length > 1 ? onlyVisible2(xml, state.combinePartIndexs) : onlyVisible(xml, state.partIndex);
 			if (state.gradualTimes) {
 				state.gradual = getGradualLengthByXml(xml);
 			}

+ 1 - 1
src/view/plugins/toggleMusicSheet/index.tsx

@@ -91,7 +91,7 @@ export default defineComponent({
         })
       // const blob2 = new Blob([downloadXmlStr.value], { type: "text/html" });
       // console.log(_url,xmlDocRef.value,downloadXmlStr.value)
-      
+      state.evaluatAudioInitDone = false
       await storeXmlData()
       location.href = _url
     }

+ 3 - 3
src/view/selection/index.module.less

@@ -83,9 +83,9 @@
 
 .scoreItem {
     position: absolute;
-    left: 80%;
-    top: -120%;
-    transform: translateX(-50%);
+    right: 10px;
+    top: -45px;
+    // transform: translateX(-50%);    
     font-size: 16px;
     font-family: "Roboto", sans-serif;
     font-weight: bold;

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

@@ -68,7 +68,7 @@ export default defineComponent({
 					if (item.svgElement) {
 						const noteEle = document.querySelector(`#vf-${item.svgElement?.attrs?.id}`);
 						if (noteEle) {
-							const noteBbox = noteEle.getBoundingClientRect?.() || { x: 0, width: 0 };
+							let noteBbox = noteEle.getBoundingClientRect?.() || { x: 0, width: 0 };
 							if (state.musicRenderType !== EnumMusicRenderType.staff) {
 								noteItem.bbox = {
 									left: noteBbox.x - parentLeft - noteBbox.width / 4 + "px",
@@ -92,11 +92,20 @@ export default defineComponent({
 								}
 								
 							} else {
+								// 判断是否是滑音,滑音的宽度很大,会覆盖掉前面的音符区域,导致无法点击选中前一个音符,需要缩小滑音的点击区域
+								let vibratoReduceX = 0;
+								if (noteEle?.querySelector('.vf-vibrato') && noteEle?.querySelector(".vf-note")) {
+									// vibratoReduceX = 50;
+									// @ts-ignore
+									noteBbox = noteEle.querySelector(".vf-note")?.getBoundingClientRect?.() || { x: 0, width: 0 };
+								} else {
+									vibratoReduceX = 0;
+								}							
 								const needTransY = -(staveBbox.height - customBgBox.height) / 2 + "px";
 								noteItem.bbox = {
-									left: noteBbox.x - parentLeft - noteBbox.width / 4 + "px",
+									left: noteBbox.x - parentLeft - noteBbox.width / 4 + vibratoReduceX + "px",
 									top: customBgBox.y ? customBgBox.y - parentTop + "px" : staveBbox.y - parentTop + "px",
-									width: noteBbox.width * 1.5 + "px",
+									width: noteBbox.width * 1.5 - vibratoReduceX + "px",
 									height: staveBbox.height + "px",
 									x: item.bbox?.x,
 									y: item.bbox?.y,

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
stats.html


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác