Browse Source

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

lex 11 months ago
parent
commit
0521762f99
41 changed files with 493 additions and 272 deletions
  1. 10 9
      dist/colexiu.html
  2. 9 8
      dist/index.html
  3. 9 9
      dist/instrument.html
  4. 11 10
      dist/orchestra.html
  5. 7 6
      dist/report-share.html
  6. 5 0
      instrument.html
  7. 5 0
      src/helpers/communication.ts
  8. 26 2
      src/helpers/customMusicScore.ts
  9. 8 5
      src/helpers/formateMusic.ts
  10. 2 2
      src/page-instrument/component/authorName/index.module.less
  11. 4 1
      src/page-instrument/evaluat-model/evaluat-result/index.tsx
  12. 61 16
      src/page-instrument/evaluat-model/index.tsx
  13. 1 0
      src/page-instrument/header-top/index.module.less
  14. 4 4
      src/page-instrument/header-top/index.tsx
  15. 69 78
      src/page-instrument/header-top/modeView.tsx
  16. 2 2
      src/page-instrument/header-top/settting/index.tsx
  17. 4 0
      src/page-instrument/header-top/speed/index.module.less
  18. 4 3
      src/page-instrument/header-top/speed/index.tsx
  19. 20 6
      src/page-instrument/simple-detail/index.tsx
  20. 1 0
      src/page-instrument/view-detail/images/index.json
  21. 22 12
      src/page-instrument/view-detail/index.module.less
  22. 9 2
      src/page-instrument/view-detail/index.tsx
  23. 24 14
      src/page-instrument/view-detail/smoothAnimation/index.ts
  24. 0 0
      src/page-instrument/view-evaluat-report/component/share-top/image/audioBga.json
  25. BIN
      src/page-instrument/view-evaluat-report/component/share-top/image/videobg.png
  26. 13 4
      src/page-instrument/view-evaluat-report/component/share-top/index.module.less
  27. 6 6
      src/page-instrument/view-evaluat-report/component/share-top/index.tsx
  28. 27 16
      src/page-instrument/view-evaluat-report/index.tsx
  29. 12 10
      src/state.ts
  30. 34 12
      src/view/evaluating/index.tsx
  31. 2 2
      src/view/fingering/fingering-config.ts
  32. 9 0
      src/view/fingering/fingering-img/hulusi-flute0/index.json
  33. 3 1
      src/view/fingering/index.tsx
  34. 18 2
      src/view/follow-practice/index.tsx
  35. 6 5
      src/view/music-score/index.tsx
  36. 15 2
      src/view/plugins/toggleMusicSheet/choosePartName/index.tsx
  37. 1 2
      src/view/plugins/toggleMusicSheet/index.tsx
  38. 3 2
      src/view/selection/index.module.less
  39. 26 19
      src/view/tick/index.tsx
  40. 1 0
      src/view/transfer-to-img/index.tsx
  41. 0 0
      stats.html

+ 10 - 9
dist/colexiu.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-7c3d5f60.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
 
   <meta charset="UTF-8" />
   <link rel="icon" type="image/svg+xml" href="./vite.svg" />
@@ -40,12 +40,13 @@
       },
     })
   </script>
-  <script type="module" crossorigin src="./js/colexiu-817dd032.js"></script>
-  <link rel="modulepreload" crossorigin href="./js/index-763e8b6a.js">
-  <link rel="modulepreload" crossorigin href="./js/index-624d1810.js">
-  <link rel="modulepreload" crossorigin href="./js/index-d6925e45.js">
-  <link rel="stylesheet" href="./css/index-ccf6117c.css">
-  <link rel="stylesheet" href="./css/colexiu-e8b419b5.css">
+  <script type="module" crossorigin src="./js/colexiu-234f0b98.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-f30bced8.js">
+  <link rel="modulepreload" crossorigin href="./js/index-563e1186.js">
+  <link rel="modulepreload" crossorigin href="./js/index-faeb03f1.js">
+  <link rel="modulepreload" crossorigin href="./js/index-236e13a9.js">
+  <link rel="stylesheet" href="./css/index-cfe86983.css">
+  <link rel="stylesheet" href="./css/colexiu-62f31c4f.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>
@@ -55,8 +56,8 @@
   <img id="loading" class="show" src="./loading.svg" alt="loading" />
   
   <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-95755a14.js"></script>
-  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/colexiu-legacy-587fa4ae.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-241ca397.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/colexiu-legacy-40f75a59.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 9 - 8
dist/index.html

@@ -2,7 +2,7 @@
 <html lang="ZH-cn">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-7c3d5f60.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
 
   <meta charset="UTF-8">
   <link rel="icon" href="./favicon.ico" />
@@ -75,12 +75,13 @@
       }
     })
   </script>
-  <script type="module" crossorigin src="./js/gym-a1294940.js"></script>
-  <link rel="modulepreload" crossorigin href="./js/index-763e8b6a.js">
-  <link rel="modulepreload" crossorigin href="./js/index-624d1810.js">
-  <link rel="modulepreload" crossorigin href="./js/index-1dc5c539.js">
+  <script type="module" crossorigin src="./js/gym-94329614.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-f30bced8.js">
+  <link rel="modulepreload" crossorigin href="./js/index-563e1186.js">
+  <link rel="modulepreload" crossorigin href="./js/index-22f26d47.js">
+  <link rel="modulepreload" crossorigin href="./js/index-236e13a9.js">
   <link rel="modulepreload" crossorigin href="./js/plyr.min-c8c2777b.js">
-  <link rel="stylesheet" href="./css/index-ccf6117c.css">
+  <link rel="stylesheet" href="./css/index-cfe86983.css">
   <link rel="stylesheet" href="./css/index-85f95688.css">
   <link rel="stylesheet" href="./css/plyr-ad8ef5ae.css">
   <link rel="stylesheet" href="./css/index-171cd132.css">
@@ -97,8 +98,8 @@
   <img id="loading" class="show" src="./loading.svg" alt="loading" />
   
   <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-95755a14.js"></script>
-  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/gym-legacy-9c45ea39.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-241ca397.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/gym-legacy-097b0596.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 9 - 9
dist/instrument.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-7c3d5f60.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
 
   <meta charset="UTF-8" />
   <meta name="viewport"
@@ -41,10 +41,10 @@
       })
     }
   </script>
-  <script type="module" crossorigin src="./js/instrument-e3875fa3.js"></script>
-  <link rel="modulepreload" crossorigin href="./js/index-763e8b6a.js">
-  <link rel="stylesheet" href="./css/index-ccf6117c.css">
-  <link rel="stylesheet" href="./css/instrument-f3d1af9e.css">
+  <script type="module" crossorigin src="./js/instrument-dd2ce7b4.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-f30bced8.js">
+  <link rel="stylesheet" href="./css/index-cfe86983.css">
+  <link rel="stylesheet" href="./css/instrument-9ce1010f.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>
@@ -64,11 +64,11 @@
   <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
   <script>
     // VConsole will be exported to `window.VConsole` by default.
-    // var vConsole = new window.VConsole();
-  </script>
+    var vConsole = new window.VConsole();
+  </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-95755a14.js"></script>
-  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/instrument-legacy-4b1cdc45.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-241ca397.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/instrument-legacy-e02d6237.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 11 - 10
dist/orchestra.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-7c3d5f60.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
 
   <meta charset="UTF-8" />
   <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
@@ -41,14 +41,15 @@
       transition: opacity .3s;
     }
   </style>
-  <script type="module" crossorigin src="./js/orchestra-fb7429c2.js"></script>
-  <link rel="modulepreload" crossorigin href="./js/index-763e8b6a.js">
-  <link rel="modulepreload" crossorigin href="./js/index-624d1810.js">
-  <link rel="modulepreload" crossorigin href="./js/index-d6925e45.js">
-  <link rel="modulepreload" crossorigin href="./js/index-1dc5c539.js">
-  <link rel="stylesheet" href="./css/index-ccf6117c.css">
+  <script type="module" crossorigin src="./js/orchestra-30fef581.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-f30bced8.js">
+  <link rel="modulepreload" crossorigin href="./js/index-563e1186.js">
+  <link rel="modulepreload" crossorigin href="./js/index-faeb03f1.js">
+  <link rel="modulepreload" crossorigin href="./js/index-22f26d47.js">
+  <link rel="modulepreload" crossorigin href="./js/index-236e13a9.js">
+  <link rel="stylesheet" href="./css/index-cfe86983.css">
   <link rel="stylesheet" href="./css/index-85f95688.css">
-  <link rel="stylesheet" href="./css/orchestra-8e05e751.css">
+  <link rel="stylesheet" href="./css/orchestra-8bc1a9c0.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>
@@ -69,8 +70,8 @@
   </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-95755a14.js"></script>
-  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/orchestra-legacy-56a35f49.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-241ca397.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/orchestra-legacy-702b578f.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 7 - 6
dist/report-share.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-7c3d5f60.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
 
   <meta charset="UTF-8" />
   <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
@@ -25,10 +25,11 @@
       transition: opacity .3s;
     }
   </style>
-  <script type="module" crossorigin src="./js/report-share-ad36c120.js"></script>
-  <link rel="modulepreload" crossorigin href="./js/index-763e8b6a.js">
+  <script type="module" crossorigin src="./js/report-share-2b948aea.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-f30bced8.js">
   <link rel="modulepreload" crossorigin href="./js/plyr.min-c8c2777b.js">
-  <link rel="stylesheet" href="./css/index-ccf6117c.css">
+  <link rel="modulepreload" crossorigin href="./js/index-236e13a9.js">
+  <link rel="stylesheet" href="./css/index-cfe86983.css">
   <link rel="stylesheet" href="./css/plyr-ad8ef5ae.css">
   <link rel="stylesheet" href="./css/report-share-0f4c3151.css">
   <script type="module">import.meta.url;import("_").catch(()=>1);async function* g(){};window.__vite_is_modern_browser=true;</script>
@@ -51,8 +52,8 @@
   </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-95755a14.js"></script>
-  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/report-share-legacy-7001a7f7.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-241ca397.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/report-share-legacy-d1f9ef4f.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 5 - 0
instrument.html

@@ -53,6 +53,11 @@
   </script>
 
   <script type="module" src="/src/page-instrument/main.ts"></script>
+  <!-- <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
+  <script>
+    // VConsole will be exported to `window.VConsole` by default.
+    var vConsole = new window.VConsole();
+  </script>   -->
 </body>
 
 </html>

+ 5 - 0
src/helpers/communication.ts

@@ -511,6 +511,11 @@ export const api_remove_finishDelayCheck = (callback: any) => {
 	removeListenerMessage("finishDelayCheck", callback);
 };
 
+/** 取消监听APP取消延迟检测 */
+export const api_remove_cancelDelayCheck = (callback: any) => {
+	removeListenerMessage("cancelDelayCheck", callback);
+};
+
 /** 监听APP播放进度 */
 export const simple_playProgress = (callback: any) => {
 	listenerMessage("api_playProgress", callback);

+ 26 - 2
src/helpers/customMusicScore.ts

@@ -314,6 +314,10 @@ export const resetFormate = () => {
 		for (let i = 0; i < vftexts.length; i++) {
 			const _text = vftexts[i];
 			for (let j = 0; j < vftexts.length; j++) {
+				if (_text.textContent === 'second time only') {
+					// @ts-ignore
+					_text.style.transform = `translateY(15px)`;
+				}
 				if (_text.parentNode === vftexts[j].parentNode) continue;
 				const result = collisionDetection(_text as SVGAElement, vftexts[j] as SVGAElement);
 				if (result.isCollision) {
@@ -554,7 +558,7 @@ 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="12" fill="#2B70A5" />`
+				const rectBottom = `<rect class="vf-custom-bot" x="${bbox.x}" y="${bbox.y+bbox.height}" width="${bbox.width}" height="7.5" fill="#2B70A5" />`
 				const customG = `<g>${rect}${rectBottom}</g>`
 				try {
 					if (list.length) {
@@ -570,9 +574,29 @@ export const resetFormate = () => {
 		}
 
 	}
-	
+	if (!state.isCombineRender && state.isSingleLine) {
+		transSinglePage();
+	}
 	// setTimeout(() => this.resetGlobalText());
 };
+
+// 一行谱时,五线谱/简谱的谱面staffLine,居中显示
+const transSinglePage = () => {
+	if (state.isSingleLine) {
+		const svgPage = document?.getElementById('osmdSvgPage1')?.getBoundingClientRect();
+		const staffLine = document?.querySelector('.staffline')?.getBoundingClientRect();
+		if (svgPage && staffLine && svgPage.height > 130) {
+			// 需要上移的距离
+			console.log('need',svgPage.height,staffLine.height)
+			const rate = svgPage.height > 400 ? 1.2 : 2;
+			const needTransTop = (svgPage.height - staffLine.height) / rate;
+			// @ts-ignore
+			document.getElementById('osmdSvgPage1').style.transform = `translateY(-${needTransTop}px)`;
+			// document.querySelector('.staffline').style.transform = `translateY(-${needTransTop}px)`;
+		}
+	}
+}
+
 // 技巧文本
 const resetGlobalText = () => {
 	const svg = container.value.querySelector("svg");

+ 8 - 5
src/helpers/formateMusic.ts

@@ -971,17 +971,17 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			// }
 			
 			activeVerticalMeasureList = [note.sourceMeasure?.verticalMeasureList?.[0]] || [];
-
+			const currenrtVfVoices = activeVerticalMeasureList[0]?.vfVoices['1'] ? activeVerticalMeasureList[0]?.vfVoices['1'] : activeVerticalMeasureList[0]?.vfVoices['2'] ? activeVerticalMeasureList[0]?.vfVoices['2'] : null;
 			/**
 			 * TODO:多分轨合并的小节,音符可能没有id,此时就去其它分轨找
 			 */
 			const vmLength = note.sourceMeasure?.verticalMeasureList?.length
 			let currentVmIndex = 0;
-			let hasSvgElement = activeVerticalMeasureList[0]?.vfVoices['1']?.tickables[staveNoteIndex];
+			let hasSvgElement = currenrtVfVoices?.tickables[staveNoteIndex];
 			while (!hasSvgElement && vmLength > 1 && currentVmIndex <= vmLength - 1) {
 				currentVmIndex += 1;
 				activeVerticalMeasureList = [note.sourceMeasure?.verticalMeasureList?.[currentVmIndex]] || [];
-				hasSvgElement = activeVerticalMeasureList[0]?.vfVoices['1']?.tickables[staveNoteIndex];
+				hasSvgElement = currenrtVfVoices?.tickables[staveNoteIndex];
 			}
 
 			const { realValue } = iterator.currentTimeStamp;
@@ -1130,7 +1130,10 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			// console.log('relaEndtime',noteLength, relativeTime)
 			const fixedKey = note.fixedKey || 0;
 			// const svgElement = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables[si];
-			const svgElement = activeVerticalMeasureList[0]?.vfVoices['1']?.tickables[staveNoteIndex];
+			//const svgElement = currenrtVfVoices?.tickables[staveNoteIndex];
+			// 过滤掉ghostnote
+			const filterTickables = currenrtVfVoices?.tickables?.filter((tickable: any) => tickable.attrs?.type !== "GhostNote")
+			const svgElement = filterTickables?.[staveNoteIndex];
 
 			// console.log('si',si,i)
 			// console.log(note.sourceMeasure.MeasureNumberXML,note,svgElement, NoteRealValue, measureLength)
@@ -1287,7 +1290,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			}		
 			nodeDetail.realKey = formatRealKey(note.halfTone - fixedKey * 12, nodeDetail);
 			nodeDetail.duration = nodeDetail.endtime - nodeDetail.time;
-			let tickables = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables || [];
+			let tickables = currenrtVfVoices?.tickables || [];
 			if ([121].includes(state.subjectId)) {
 				tickables = note.sourceMeasure.verticalSourceStaffEntryContainers;
 			}

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

@@ -1,5 +1,5 @@
 .authorName{
-    height: 2.4rem;
+    height: 1.8rem;
 }
 .title{
     width: 216px;
@@ -10,7 +10,7 @@
             line-height: 30px;
             padding: 0;
             font-weight: 600;
-            font-size: 18px;
+            font-size: 22px;
             color: #FFFFFF;
             .van-notice-bar__wrap{
                 justify-content: center;

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

@@ -48,6 +48,8 @@ export default defineComponent({
       console.log("结束", evaluatingData.resultData);
       /** 生成评测记录的时候,记录当前评测的谱面类型,用于评测报告默认展示的谱面类型 */
       evaluatingData.resultData.scoreData.musicType = state.musicRenderType;
+      // 评测的速度,如果是选段,则选选段开头小节的速度
+      const evaluatSpeed = state.sectionStatus && state.section.length === 2 && state.section[0].measureSpeed ? state.section[0].measureSpeed * state.basePlayRate : state.speed;      
       const body = {
         deviceType: browser().android ? "ANDROID" : "IOS", // 设备类型
         intonation: evaluatingData.resultData.intonation, // 音准
@@ -57,7 +59,7 @@ export default defineComponent({
         behaviorId: getBehaviorId(), // 行为id
         sourceTime: getAudioDuration(), // 音频时长
         partIndex: state.partIndex, // 音轨
-        speed: state.speed, // 速度
+        speed: evaluatSpeed, // 速度
         practiceSource: query.workRecord ? "LESSON_TRAINING" : "EVALUATION", // 练习来源
         score: evaluatingData.resultData.score, // 分数
         clientType: storeData.user.clientType, // 客户端类型
@@ -66,6 +68,7 @@ export default defineComponent({
         playTime: evaluatingData.resultData.playTime / 1000, // 播放时长
         heardLevel: state.setting.evaluationDifficulty, // 听力等级
         recordFilePath: evaluatingData.resultData.url, // 录音文件路径
+        delFlag: evaluatingData.oneselfCancleEvaluating
       };
       data.saveLoading = true;
       const res = await api_musicPracticeRecordSave(body);

+ 61 - 16
src/page-instrument/evaluat-model/index.tsx

@@ -1,16 +1,16 @@
 import { Transition, defineComponent, onMounted, reactive, watch, defineAsyncComponent, computed, onUnmounted } from "vue";
-import { connectWebsocket, evaluatingData, handleEndBegin, handleStartBegin, handleStartEvaluat, handleViewReport, startCheckDelay, checkUseEarphone, handleCancelEvaluat } from "/src/view/evaluating";
+import { connectWebsocket, evaluatingData, handleEndBegin, handleStartBegin, handleStartEvaluat, handleViewReport, startCheckDelay, checkUseEarphone, handleCancelEvaluat, checkMinInterval, handleEndEvaluat } from "/src/view/evaluating";
 import Earphone from "./earphone";
 import styles from "./index.module.less";
 import SoundEffect from "./sound-effect";
-import state, { handleRessetState, resetPlaybackToStart, musicalInstrumentCodeInfo, clearSelection } from "/src/state";
+import state, { handleRessetState, resetPlaybackToStart, musicalInstrumentCodeInfo, clearSelection, initSetPlayRate } from "/src/state";
 import { storeData } from "/src/store";
 import { browser } from "/src/utils";
 import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic";
 import { Icon, Popup, showToast, closeToast, showLoadingToast } from "vant";
 import EvaluatResult from "./evaluat-result";
 import EvaluatAudio from "./evaluat-audio";
-import { api_getDeviceDelay, api_openAdjustRecording, api_proxyServiceMessage, api_videoUpdate, getEarphone, api_back, api_startDelayCheck, api_cancelDelayCheck, api_closeDelayCheck, api_finishDelayCheck, api_retryEvaluating, api_remove_finishDelayCheck } from "/src/helpers/communication";
+import { api_getDeviceDelay, api_openAdjustRecording, api_proxyServiceMessage, api_videoUpdate, getEarphone, api_back, api_startDelayCheck, api_cancelDelayCheck, api_remove_cancelDelayCheck, api_closeDelayCheck, api_finishDelayCheck, api_retryEvaluating, api_remove_finishDelayCheck } from "/src/helpers/communication";
 import EvaluatShare from "./evaluat-share";
 import { Vue3Lottie } from "vue3-lottie";
 import startData from "./data/start.json";
@@ -196,7 +196,7 @@ export default defineComponent({
         const note = getNoteByMeasuresSlursStart(item);
         // #8701 bug: 评测模式,是以曲谱本身的速度进行评测,所以rate取1,不需要转换
         // const rate = state.speed / state.originSpeed;
-        const rate = 1;
+        const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率
         const difftime = item.difftime;
         const start = difftime + (item.sourceRelativeTime || item.relativeTime) - starTime;
         const end = difftime + (item.sourceRelaEndtime || item.relaEndtime) - starTime;
@@ -261,10 +261,13 @@ export default defineComponent({
     /** 连接websocket */
     const handleConnect = async () => {
       const behaviorId = localStorage.getItem("behaviorId") || localStorage.getItem("BEHAVIORID") || undefined;
-      let rate = state.speed / state.originSpeed;
-      rate = parseFloat(rate.toFixed(2));
+      // let rate = state.speed / state.originSpeed;
+      const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率
+      // rate = parseFloat(rate.toFixed(2));
       console.log("速度比例", rate, "速度", state.speed);
       calculateInfo = formatTimes();
+      // 评测的速度,如果是选段,则选选段开头小节的速度
+      const evaluatSpeed = state.sectionStatus && state.section.length === 2 && state.section[0].measureSpeed ? state.section[0].measureSpeed * state.basePlayRate : state.speed;
       const content = {
         musicXmlInfos: calculateInfo.datas,
         subjectId: state.musicalCode,
@@ -277,12 +280,12 @@ export default defineComponent({
         clientId: storeData.platformType === "STUDENT" ? "student" : storeData.platformType === "TEACHER" ? "teacher" : "education",
         hertz: state.setting.frequency,
         reactionTimeMs: state.setting.reactionTimeMs ? Number(state.setting.reactionTimeMs) : 0,
-        speed: state.speed,
+        speed: evaluatSpeed,
         heardLevel: state.setting.evaluationDifficulty,
         // beatLength: Math.round((state.fixtime * 1000) / rate),
         beatLength: actualBeatLength,
         evaluationCriteria: state.evaluationStandard,
-        speedRate: rate, // 播放倍率
+        speedRate: parseFloat(rate.toFixed(2)), // 播放倍率
       };
       await connectWebsocket(content);
       // state.playSource = "music";
@@ -296,15 +299,14 @@ export default defineComponent({
           resetPlaybackToStart();
           return;
         } else if (evaluatingData.resultData?.recordIdStr || evaluatingData.resultData?.recordId) {
-          let rate = state.speed / state.originSpeed;
-          rate = parseFloat(rate.toFixed(2));
+          const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率
           // 上传云端
           // evaluatModel.evaluatUpdateAudio = true;
           api_openAdjustRecording({
             recordId: evaluatingData.resultData?.recordIdStr || evaluatingData.resultData?.recordId,
             title: state.examSongName || "曲谱演奏",
             coverImg: state.coverImg,
-            speedRate: rate, // 播放倍率
+            speedRate: parseFloat(rate.toFixed(2)), // 播放倍率
             musicRenderType: state.musicRenderType,
             musicSheetId: state.examSongId,
             'part-index': state.partIndex
@@ -327,8 +329,8 @@ export default defineComponent({
       } else if (type === "selfCancel") {
         // 再来一次,需要手动取消评测,不生成评测记录,不显示评测结果弹窗
         evaluatingData.oneselfCancleEvaluating = true;
-        handleCancelEvaluat();
-        startBtnHandle();
+        // handleCancelEvaluat();
+        handleEndEvaluat(true, 'selfCancel');
       }
       resetPlaybackToStart();
       evaluatingData.resulstMode = false;
@@ -390,6 +392,7 @@ export default defineComponent({
         });
       }
       // console.log('异常流程3')
+      initSetPlayRate();
       // 检测APP端socket状态
       const res: any = await startCheckDelay();
       if (res?.checked) {
@@ -418,6 +421,7 @@ export default defineComponent({
       evaluatingData.socketErrorPop = false;
       if (res?.content) {
         evaluatingData.checkEnd = true;
+        state.setting.soundEffect = false;
         checkEarphoneStatus();
       }
     };
@@ -431,9 +435,33 @@ export default defineComponent({
 			return evaluatingData.earphoneMode && !state.isLoading && !state.hasDriverPop;
 		});
 
+    watch(
+      () => state.setting.soundEffect,
+      (val) => {
+        if (val) {
+          headTopData.settingMode = false
+          api_startDelayCheck({});
+        }
+      }
+    );
+
+    // 手动取消评测,需要自动再次评测
+    watch(
+      () => evaluatingData.hideResultModal,
+      (val) => {
+        if (val && evaluatingData.oneselfCancleEvaluating) {
+          setTimeout(() => {
+            startBtnHandle();
+          }, 500);
+        }
+      }
+    );
+
     onMounted(async () => {
       // 如果打开了延迟检测开关,需要先发送开始检测的消息
-      if (state.setting.soundEffect) {
+      const delayData = await api_getDeviceDelay();
+      console.log('设备的延迟值',delayData.content?.value)
+      if (state.setting.soundEffect || (delayData && delayData.content?.value < 0)) {
         await api_startDelayCheck({});
       } else {
         evaluatingData.checkEnd = true;
@@ -448,6 +476,7 @@ export default defineComponent({
     
     onUnmounted(() => {
       api_remove_finishDelayCheck(handleFinishDelayCheck);
+      api_remove_cancelDelayCheck(handleCancelDelayCheck);
 			clearTimeout(checkErjiTimer);
       checkErjiTimer = null;
 		});
@@ -466,8 +495,24 @@ export default defineComponent({
           )}
           {evaluatingData.websocketState && evaluatingData.startBegin && (
             <>
-              <img class={styles.iconBtn} src={headImg("icon_reset.png")} onClick={() => handleEvaluatResult("selfCancel")} />
-              <img class={styles.iconBtn} src={headImg("submit.png")} onClick={() => handleEndBegin()} />
+              <img class={styles.iconBtn} src={headImg("icon_reset.png")} onClick={() => {
+                // 校验评测最小间隔时间
+                const currentTime = +new Date();
+                // 开始评测和结束评测的间隔时间小于800毫秒,则不处理
+                if (currentTime - evaluatingData.recordingTime < 800) {
+                  return;
+                }
+                handleEvaluatResult("selfCancel")
+              }} />
+              <img class={styles.iconBtn} src={headImg("submit.png")} onClick={() => {
+                // 校验评测最小间隔时间
+                const currentTime = +new Date();
+                // 开始评测和结束评测的间隔时间小于800毫秒,则不处理
+                if (currentTime - evaluatingData.recordingTime < 800) {
+                  return;
+                }
+                handleEndBegin()
+              }} />
             </>
           )}
         </div>

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

@@ -34,6 +34,7 @@
         height: 18px;
     }
     &.modeWarnRight {
+        left: inherit;
         right: 30px;
     }
 }

+ 4 - 4
src/page-instrument/header-top/index.tsx

@@ -877,7 +877,7 @@ export default defineComponent({
         {isAllBtnsStudent.value && !query.isCbs && showGuideIndex.value && <StudentTop></StudentTop>} */}
 
         {/* 练习模式功能引导 加载音频完成 不是会员 */}
-        {state.modeType === "practise" && headTopData.modeType !== "init" && !query.isCbs && state.audioDone && !state.isVip && (
+        {state.modeType === "practise" && headTopData.modeType !== "init" && !query.isCbs && state.audioDone && !state.isLoading && !state.isVip && (
           <PractiseDriver
             statusAll={{
               subjectStatus: state.musicRendered && !query.lessonTrainingId && !query.questionId && state.isConcert,
@@ -887,7 +887,7 @@ export default defineComponent({
           />
         )}
         {/* 跟练模式功能引导 加载音频完成 不是会员 */}
-        {state.modeType === "follow" && headTopData.modeType !== "init" && !query.isCbs && state.audioDone && !state.isVip && (
+        {state.modeType === "follow" && headTopData.modeType !== "init" && !query.isCbs && state.audioDone && !state.isLoading && !state.isVip && (
           <FollowDriver
             statusAll={{
               subjectStatus: state.musicRendered && !query.lessonTrainingId && !query.questionId && state.isConcert,
@@ -895,7 +895,7 @@ export default defineComponent({
           />
         )}
         {/* 评测模式功能引导 加载音频完成 不是会员 */}
-        {state.modeType === "evaluating" && headTopData.modeType !== "init" && !evaluatingData.earphoneMode && !query.isCbs && state.audioDone && !state.isVip && evaluatingData.websocketState && !evaluatingData.startBegin && evaluatingData.checkEnd && (
+        {state.modeType === "evaluating" && headTopData.modeType !== "init" && !evaluatingData.earphoneMode && !query.isCbs && state.audioDone && !state.isLoading && !state.isVip && evaluatingData.websocketState && !evaluatingData.startBegin && evaluatingData.checkEnd && (
           <EvaluatingDriver
             statusAll={{
               subjectStatus: state.musicRendered && !query.lessonTrainingId && !query.questionId && state.isConcert,
@@ -905,4 +905,4 @@ export default defineComponent({
       </>
     );
   },
-});
+});

+ 69 - 78
src/page-instrument/header-top/modeView.tsx

@@ -57,81 +57,72 @@ export default defineComponent({
         state.isVip = false;
         openGuid();
       }
-   }
-      const pageVisible = usePageVisibility()
-      watch(
-         () => pageVisible.value,
-         val => {
-            if (val === "visible") {
-               if (storeData.user.vipMember) return
-               console.log("页面显示")
-               getUserInfo()
-            }
-         }
-      )
-      watch(() => headTopData.modeType, (value,oldValue) => {
-         // headTopData.modeType 值 刚开始是 ""  所以 第一次切换时候不触发播放动画
-         if(!oldValue) return
-         nextTick(()=>{
-            if(value === "show"){
-               modeImgDom1.value?.pause()
-               modeImgDom2.value?.pause()
-               modeImgDom3.value?.pause()
-            }else if(value === "init"){
-               modeImgDom1.value?.play()
-               modeImgDom2.value?.play()
-               modeImgDom3.value?.play()
-            }
-         })
-      })
-      onMounted(() => {
-         openGuid()
-      })
-      watch(
-         () => evaluatingData.socketErrorStatus,
-         () => {
-           if (evaluatingData.socketErrorStatus === 2) {
-             setTimeout(() => {
-               evaluatingData.socketErrorPop = false;
-             }, 1000);
-           }
-         }
-       );      
-      return () => (
-         <div class={[styles.modeView, headTopData.modeType !== "init" && styles.hidden]}>
-            <img
-               src={backImg}
-               class={styles.back}
-               onClick={() => {
-                  headTopData.modeType = "show"
-               }}
-            />
-            <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>
-               {
-                  !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"]} transition="van-scale" v-model:show={evaluatingData.socketErrorPop}>
-                  <AbnormalPop onConfirm={hanldeConfirmPop} onClose={hanldeClosePop} />
-               </Popup>
-            </div>            
-         </div>
-      )
-   }
-})
+    };
+    const pageVisible = usePageVisibility();
+    watch(
+      () => pageVisible.value,
+      (val) => {
+        if (val === "visible") {
+          if (storeData.user.vipMember) return;
+          console.log("页面显示");
+          getUserInfo();
+        }
+      }
+    );
+    watch(
+      () => headTopData.modeType,
+      (value, oldValue) => {
+        // headTopData.modeType 值 刚开始是 ""  所以 第一次切换时候不触发播放动画
+        if (!oldValue) return;
+        nextTick(() => {
+          if (value === "show") {
+            modeImgDom1.value?.pause();
+            modeImgDom2.value?.pause();
+            modeImgDom3.value?.pause();
+          } else if (value === "init") {
+            modeImgDom1.value?.play();
+            modeImgDom2.value?.play();
+            modeImgDom3.value?.play();
+          }
+        });
+      }
+    );
+    onMounted(() => {
+      openGuid();
+    });
+    watch(
+      () => evaluatingData.socketErrorStatus,
+      () => {
+        if (evaluatingData.socketErrorStatus === 2) {
+          setTimeout(() => {
+            evaluatingData.socketErrorPop = false;
+          }, 1000);
+        }
+      }
+    );      
+    return () => (
+      <div class={[styles.modeView, headTopData.modeType !== "init" && styles.hidden]}>
+        <img
+          src={backImg}
+          class={styles.back}
+          onClick={() => {
+            headTopData.modeType = "show";
+          }}
+        />
+        <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>
+          {!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"]} transition="van-scale" v-model:show={evaluatingData.socketErrorPop}>
+              <AbnormalPop onConfirm={hanldeConfirmPop} onClose={hanldeClosePop} />
+            </Popup>
+        </div>         
+      </div>
+    );
+  },
+});

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

@@ -62,7 +62,7 @@ export default defineComponent({
                                 </div>
                         }
                         {
-                            state.isSingleLine && state.modeType === "practise" &&
+                            state.isSingleLine && state.modeType === "practise" && !state.isCombineRender && !state.isPercussion && 
                                 <div class={styles.cellBox}>
                                 <div class={styles.tit}>旋律线</div>
                                     <Switch v-model={smoothAnimationState.isShow.value}></Switch>
@@ -87,7 +87,7 @@ export default defineComponent({
                                     </div>   
                                 }                     
                                 <div class={styles.cellBox}>
-                                    <div class={styles.tit}>延迟检测</div>
+                                    <div class={styles.tit}>重新延迟检测</div>
                                     <Switch v-model={state.setting.soundEffect}></Switch>
                                 </div> 
                                 <div class={[styles.cellBox, state.setting.camera && styles.isCamera]}>

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

@@ -150,6 +150,10 @@
                     }
                 }
             }
+            .disableSpend {
+                opacity: 0.4;
+                pointer-events: none;
+            }
             .metronome{
                 margin-top: 18px;
                 display: flex;

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

@@ -5,12 +5,13 @@ import { headData } from "../index"
 import { headImg } from "../image";
 import state, { handleSetSpeed, resetBaseRate } from "../../../state";
 import { metronomeData } from "../../../helpers/metronome"; 
+import { getQuery } from "/src/utils/queryString";
 
 export default defineComponent({
 	name: "speed",
 	setup() {
 		const speed = ref(state.speed);
-
+		const query: any = getQuery();
 		const minusSpeed = () => {
 			let canSpeed = Math.max(speed.value - 1, 45);
 			canSpeed = Math.min(canSpeed, 270);
@@ -58,7 +59,7 @@ export default defineComponent({
 				<div class={styles.content}>
 					<div class={styles.conBox}>
 						<div class={styles.tit}>速度</div>
-						<div class={styles.spendCon}>
+						<div class={[styles.spendCon, query.workRecord && styles.disableSpend]}>
 							<img src={headImg("cutImg.png")} class={[styles.btn]} onClick={minusSpeed} />
 							<div class={styles.sliderCon}>
 								<Slider class={styles.slider} max={270} min={speed.value < 45 ? speed.value : 45} v-model={speed.value}>
@@ -73,7 +74,7 @@ export default defineComponent({
 							</div>
 							<img src={headImg("addImg.png")} class={[styles.btn]} onClick={plusSpeed} />	
 						</div>
-						<div class={styles.speedSel}>
+						<div class={[styles.speedSel, query.workRecord && styles.disableSpend]}>
 							<div onClick={resetCurrentSpeed}>原速</div>
 							<div onClick={()=>{ speed.value = 70 }}>70</div>
 							<div onClick={()=>{ speed.value = 80 }}>80</div>

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

@@ -1,5 +1,5 @@
 import { defineComponent, onMounted, onUnmounted, reactive, nextTick } from "vue";
-import state, { getMusicDetail, handleSetSpeed, addNoteBBox, getNote, gotoNext, fillWordColor } from "/src/state";
+import state, { getMusicDetail, handleSetSpeed, addNoteBBox, getNote, gotoNext, fillWordColor, moveSvgDom } from "/src/state";
 import MusicScore from "../../view/music-score";
 import styles from "./index.module.less";
 import { getQuery } from "/src/utils/queryString";
@@ -44,18 +44,32 @@ export default defineComponent({
 			if (resInfo?.api === "api_updateProgress") {
 				console.log('拖动的进度')
 				if (state.playState === 'paused') {
-					detailData.currentTime = resInfo?.content?.currentTime ? resInfo?.content?.currentTime : detailData.currentTime;
+					detailData.currentTime = resInfo?.content?.currentTime ?? detailData.currentTime;
+					// 坐标和小节都改为初始值
+					state.activeNoteIndex = 0
+					state.activeMeasureIndex = state.times[0].MeasureNumberXML;
 					handlePlaying(true);
 				}
 			}
 			// 播放进度
 			if (resInfo?.api === "api_playProgress") {
 				// console.log('播放进度',resInfo)
-				if (resInfo?.content?.currentTime) {
-					if (resInfo?.content?.currentTime < detailData.currentTime) {
+				const currentTime = resInfo?.content?.currentTime
+				if (currentTime) {
+					if (currentTime < detailData.currentTime) {
+						// 坐标和小节都改为初始值
 						state.activeNoteIndex = 0
+						let item = getNote(currentTime) || state.times[0];
+						if(item.i === state.activeNoteIndex){
+							state.activeMeasureIndex = item.MeasureNumberXML;
+						}
+						// 当拖动到MP3节拍器时候 需要手动移动到当前为止
+						const fixtime = state.times[0].fixtime
+						if(currentTime <= fixtime){
+							moveSvgDom(true)
+						}
 					}
-					detailData.currentTime = resInfo?.content?.currentTime
+					detailData.currentTime = currentTime
 				}
 			}
 		};
@@ -134,7 +148,7 @@ export default defineComponent({
 			//detailData.currentTime += 0.03
 			const currentTime = detailData.currentTime;
 			// console.log('👀~播放进度',currentTime)
-			let item = getNote(currentTime);
+			let item = getNote(currentTime) || state.times[0];
 			if (item) {
 				gotoNext(item, skipNote);
 			}

File diff suppressed because it is too large
+ 1 - 0
src/page-instrument/view-detail/images/index.json


+ 22 - 12
src/page-instrument/view-detail/index.module.less

@@ -17,19 +17,26 @@
     overflow: hidden;
     --header-height: 80px;
     --pc-header-height: 72px;
-    &.practise{
-        background: url("./images/bg1.png") no-repeat;
-        background-size: 100% 100%;
-    }    
-    &.follow{
-        background: url("./images/bg3.png") no-repeat;
-        background-size: 100% 100%;
-    }    
-    &.evaluating{
-        background: url("./images/bg2.png") no-repeat;
-        background-size: 100% 100%;
+    // &.practise{
+    //     background: url("./images/bg1.png") no-repeat;
+    //     background-size: 100% 100%;
+    // }    
+    // &.follow{
+    //     background: url("./images/bg3.png") no-repeat;
+    //     background-size: 100% 100%;
+    // }    
+    // &.evaluating{
+    //     background: rgba(255, 255, 255, 0.2) url("./images/bg2.png") no-repeat;
+    //     background-size: 100% 100%;
+    // }
+    .pageBg {
+        position: absolute;
+        left: 0;
+        top: 0;
+        width: 100%;
+        height: 100%;
+        object-fit: cover; /* 保持宽高比 */
     }
-
     .headHeight {
         position: absolute;
         bottom: 0;
@@ -43,6 +50,9 @@
             :global{
                 .practiseModeWarn{
                     opacity: 1;
+                    img {
+                        opacity: 0.7;
+                    }
                 }
             }
         }

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

@@ -38,6 +38,7 @@ import { initSmoothAnimation } from "./smoothAnimation"
 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";
 
 // const DelayCheck = defineAsyncComponent(() =>
 //   import('/src/page-instrument/evaluat-model/delay-check')
@@ -86,6 +87,7 @@ export const musicScoreRef = ref();
 export default defineComponent({
   name: "music-list",
   setup() {
+    const browsInfo = browser();
     const query: any = getQuery();
 
     const detailData = reactive({
@@ -104,7 +106,8 @@ export default defineComponent({
         // console.log("🚀 ~ screenData:", screenData.content);
         const { isSpecialShapedScreen, notchHeight } = screenData.content;
         if (isSpecialShapedScreen) {
-          detailData.paddingLeft = 25 + "px";
+          // 安卓取刘海值
+          detailData.paddingLeft = 27 + "px";
         }
       }
     };
@@ -410,7 +413,6 @@ export default defineComponent({
         }
       }
     );
-    const browsInfo = browser();
     const handleOpenFignerView = () => {
       if (!query.modelType) {
         detailData.orientation = state.fingeringInfo.orientation || 0;
@@ -452,6 +454,11 @@ export default defineComponent({
           background: state.setting.camera ? `rgba(${state.setting.eyeProtection ? "253,244,229" : "255,255,255"} ,${state.setting.cameraOpacity / 100}) !important` : "",
         }}
       >
+        <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] : ''} 
+        />
         {/* 骨架屏 */}
         {/* <Transition name="van-fade">
           {detailData.skeletonLoading && (

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

@@ -19,11 +19,11 @@ type smoothAnimationType = {
    osmdCanvasPageDom: null | HTMLElement
    osdmScrollDom: null | HTMLElement
    osdmScrollDomWith: number
+   osdmScrollDomOffsetWidth: number
    selectionBoxDom: null | HTMLElement
    pointsPos: pointsPosType
    translateXNum: number
    aveSpeed: number
-   clientWidth: number
 }
 
 const _numberOfSegments = 58 // 中间切割线的个数
@@ -39,11 +39,11 @@ export const smoothAnimationState = {
    osmdCanvasPageDom: null,
    osdmScrollDom: null,
    osdmScrollDomWith: 0,
+   osdmScrollDomOffsetWidth: 0,
    selectionBoxDom: null,
    pointsPos: [], // 计算之后的点坐标数组
    translateXNum: 0, // 当前谱面的translateX的距离   谱面的位置信息 由translateX和scrollLeft的偏移一起决定
-   aveSpeed: 0, // 谱面的一帧的平均速度
-   clientWidth: 0 // 屏幕宽度
+   aveSpeed: 0 // 谱面的一帧的平均速度
 } as smoothAnimationType
 
 // 监听显示与隐藏
@@ -81,6 +81,10 @@ export function initSmoothAnimation() {
    window.addEventListener("resize", calcClientWidth)
    // 初始化 只有练习模式 才显示
    state.modeType === "practise" && (smoothAnimationState.isShow.value = true)
+   // 多分轨合并显示、打击乐、节奏练习的曲子不显示旋律线
+   if (state.isCombineRender || state.isPercussion) {
+      smoothAnimationState.isShow.value = false
+   }
    console.log(smoothAnimationState, "一行谱小鸟数据")
 }
 
@@ -100,10 +104,12 @@ export function destroySmoothAnimation() {
       smoothBotDom: null,
       osmdCanvasPageDom: null,
       osdmScrollDom: null,
+      osdmScrollDomWith: 0,
+      osdmScrollDomOffsetWidth: 0,
+      selectionBoxDom: null,
       pointsPos: [],
       translateXNum: 0,
-      aveSpeed: 0,
-      clientWidth: 0
+      aveSpeed: 0
    })
 }
 
@@ -177,9 +183,12 @@ export function moveSmoothAnimation(progress: number, activeIndex: number, isMov
  */
 function move_osmd(nowPointsPos: pointsPosType[0]) {
    const speed = smoothAnimationState.aveSpeed * (state.speed / 60)
-   const clientWidth = smoothAnimationState.clientWidth
-   const clientMidWidth = smoothAnimationState.clientWidth / 2
-   const { left, right, width } = smoothAnimationState.smoothBotDom!.getBoundingClientRect()
+   // 视口宽度
+   const clientWidth = smoothAnimationState.osdmScrollDomWith
+   const clientMidWidth = clientWidth / 2
+   let { left, right, width } = smoothAnimationState.smoothBotDom!.getBoundingClientRect()
+   left -= smoothAnimationState.osdmScrollDomOffsetWidth
+   right -= smoothAnimationState.osdmScrollDomOffsetWidth
    const midBotNum = left + width / 2
    // 分阶段移动
    if (right > clientWidth) {
@@ -249,10 +258,11 @@ function smoothAnimationMove(pos: { x: number; y: number }, pointsPos: pointsPos
    smoothAnimationState.canvasCtx && drawSmoothCurve(smoothAnimationState.canvasCtx, pointsPos, progresspointsPos)
 }
 /**
- * 计算屏幕宽度
+ * 计算视口宽度
  */
-function calcClientWidth() {
-   smoothAnimationState.clientWidth = document.body.clientWidth
+export function calcClientWidth() {
+   smoothAnimationState.osdmScrollDomWith = smoothAnimationState.osdmScrollDom?.offsetWidth || 0
+   smoothAnimationState.osdmScrollDomOffsetWidth = smoothAnimationState.osdmScrollDom?.offsetLeft || 0
 }
 /**
  * 创建dom
@@ -261,7 +271,6 @@ function createSmoothAnimation() {
    // osdmScrollDom
    const osdmScrollDom = document.querySelector("#musicAndSelection") as HTMLElement
    smoothAnimationState.osdmScrollDom = osdmScrollDom
-   smoothAnimationState.osdmScrollDomWith = osdmScrollDom?.offsetWidth | 0
    // osmdCanvasPage
    const osmdCanvasPageDom = document.querySelector("#osmdCanvasPage1") as HTMLElement
    smoothAnimationState.osmdCanvasPageDom = osmdCanvasPageDom
@@ -281,7 +290,7 @@ function createSmoothAnimation() {
    const smoothCanvasDom = document.createElement("canvas")
    smoothCanvasDom.className = "smoothCanvas"
    smoothAnimationState.canvasDom = smoothCanvasDom
-   smoothAnimationState.canvasDomWith = osmdCanvasPageDom?.offsetWidth | 0
+   smoothAnimationState.canvasDomWith = osmdCanvasPageDom?.offsetWidth || 0
    smoothCanvasDom.width = smoothAnimationState.canvasDomWith
    smoothCanvasDom.height = smoothAnimationState.canvasDomHeight
    smoothAnimationState.canvasCtx = smoothCanvasDom.getContext("2d")
@@ -460,7 +469,8 @@ function createSmoothCurvePoints(pointsPos: pointsPosType, tension?: number, clo
  */
 function drawSmoothCurve(context: CanvasRenderingContext2D, pointsPos: pointsPosType, progresspointsPos?: pointsPosType) {
    context.lineWidth = 4
-   context.lineJoin = "round" // 优化锯齿
+   context.lineJoin = 'round';// 优化锯齿
+   context.lineCap = 'round'; // 优化锯齿
    context.strokeStyle = "rgba(255,255,255,0.6)"
    drawLines(context, pointsPos)
    if (progresspointsPos?.length) {

File diff suppressed because it is too large
+ 0 - 0
src/page-instrument/view-evaluat-report/component/share-top/image/audioBga.json


BIN
src/page-instrument/view-evaluat-report/component/share-top/image/videobg.png


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

@@ -6,7 +6,9 @@
     height: 100%;
     flex-shrink: 0;
     padding: 10px 22px;
-    background-color: rgba(6, 34, 87, 0.45);
+    background-color: rgba(6, 34, 87, 0.8);
+    backdrop-filter: blur(3px);
+    -webkit-backdrop-filter: blur(3px);
     position: relative;
 }
 
@@ -120,7 +122,7 @@
     position: absolute;
     left: 50%;
     top: 50%;
-    transform: translate(-50%, -50%);
+    transform: translate(-35%, -50%);
 
     .cItem {
         width: 64px;
@@ -231,6 +233,7 @@
             border-radius: 16px;
             width: 100%;
             height: 100%;
+            box-shadow: 4px -3px 6px 0px #B2E8FF;
 
             .plyr__control.plyr__control--overlaid {
                 width: 48px;
@@ -247,6 +250,9 @@
             .plyr__controls {
                 background: initial;
                 padding: 0 20px 13px;
+                opacity: 1 !important;
+                transform: translateY(0) !important;
+                pointer-events: initial !important;
 
                 .plyr__controls__item.plyr__control {
                     padding: 0;
@@ -296,6 +302,7 @@
                         width: 12px;
                         height: 12px;
                         margin-top: -4px;
+                        box-shadow: initial;
                     }
 
                     .plyr__progress__buffer {
@@ -332,6 +339,7 @@
         background-size: 100% 100%;
         position: relative;
         border-radius: 16px;
+        box-shadow: 4px -3px 6px 0px #B2E8FF;
 
         .audioBga {
             width: 100%;
@@ -358,6 +366,7 @@
                 height: initial;
                 left: 0;
                 bottom: 0;
+                box-shadow: initial;
             }
         }
 
@@ -420,7 +429,7 @@
 }
 
 .shiyiPopup {
-    background: #7AAEE0;
+    background: #B0D8FF;
     border-radius: 20px;
     width: 80vw;
     max-width: 460px;
@@ -442,7 +451,7 @@
         position: absolute;
         width: 154px;
         left: 50%;
-        top: -6px;
+        top: -5px;
         transform: translateX(-50%);
     }
 }

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

@@ -61,7 +61,7 @@ export default defineComponent({
       slow: "#FF7B00",
     };
     // console.log("🚀 ~ scoreData:", scoreData.value)
-    const itemType = ref<IItemType>("intonation");
+    const itemType = ref<IItemType>(state.isPercussion ? "cadence" : "intonation");
     /** 返回 */
     const handleBack = () => {
       api_back();
@@ -227,7 +227,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.score}分
+                {level[scoreData.value.heardLevel]}|速度:{scoreData.value.speed}|综合分数:{scoreData.value.score}分
               </div>
             </div>
           </div>
@@ -307,7 +307,7 @@ export default defineComponent({
           {/* 五线谱,简谱类型提示  */}
           {scoreData.value.musicType === "staff" ? (
             <>
-              {state.isPercussion ? null : (
+              {(
                 <div class={styles.demos}>
                   {itemType.value === "intonation" && (
                     <>
@@ -368,7 +368,7 @@ export default defineComponent({
             </>
           ) : (
             <>
-              {state.isPercussion ? null : (
+              {(
                 <div class={styles.demos}>
                   {itemType.value === "intonation" && (
                     <>
@@ -466,7 +466,7 @@ export default defineComponent({
                   </div>
                   <div class={styles.item}>
                     <Note fill="#4BED98" />
-                    <span>绿色音符:演奏/时值正确</span>
+                    <span>绿色音符:演奏正确</span>
                   </div>
                   <div class={styles.item}>
                     <Note fill="#FFB900" />
@@ -504,7 +504,7 @@ export default defineComponent({
                   </div>
                   <div class={styles.itemTone}>
                     <i style={{ background: bgColors.right }}></i>
-                    <span>绿色音符:演奏/时值正确</span>
+                    <span>绿色音符:演奏正确</span>
                   </div>
                   <div class={styles.itemTone}>
                     <i style={{ background: bgColors.low }}></i>

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

@@ -64,12 +64,14 @@ export default defineComponent({
   setup() {
     const query: any = getQuery();
     const useedid = ref<string[]>([]);
+    const allNote = ref<any[]>([]);
     const scoreData = reactive({
       videoFilePath: "", // 回放视频路径
       cadence: 0,
       integrity: 0,
       intonation: 0,
       score: 0,
+      speed: 0,
       heardLevel: "",
       itemType: "intonation",
       musicType: "staff",
@@ -162,6 +164,7 @@ export default defineComponent({
     };
 
     onMounted(async () => {
+      state.isEvaluatReport = true;
       const res = await api_musicPracticeRecordDetail(query.id);
       state.partIndex = Number(res?.data?.partIndex);
       let resultData = {} as any;
@@ -173,6 +176,7 @@ export default defineComponent({
       // console.log("🚀 ~ resultData:", resultData);
       // @ts-ignore
       // resultData.musicalNotesPlayStats?.notesData.forEach((item) => item.rhythmicAssessment.result = 'EARLY')
+      console.log('结果11',resultData)
       detailData.musicalNotesPlayStats = resultData.musicalNotesPlayStats?.notesData || [];
       detailData.userMeasureScore = resultData.userMeasureScore || {};
 
@@ -181,15 +185,18 @@ export default defineComponent({
       scoreData.integrity = res.data?.integrity;
       scoreData.intonation = res.data?.intonation;
       scoreData.score = res.data?.score;
+      scoreData.speed = res.data?.speed;
       scoreData.videoFilePath = res.data?.videoFilePath || res.data?.recordFilePath;
-      state.isEvaluatReport = true;
       await getMusicDetail(resultData.musicalNotesPlayStats?.examSongId);
       // 从练习记录进入评测报告,默认显示五线谱
       // if (!query.musicRenderType) {
       // 	state.musicRenderType = EnumMusicRenderType.staff
       // }
       // 评测报告展示什么类型的谱面
+      state.isSingleLine = false;
       scoreData.musicType = query.musicRenderType ? query.musicRenderType : resultData.musicType ? resultData.musicType : state.musicRenderType;
+      // @ts-ignore
+      state.musicRenderType = scoreData.musicType;
       detailData.isLoading = false;
       // Promise.all([
       // 	getMusicSheetDetail(resultData.musicalNotesPlayStats?.examSongId),
@@ -259,7 +266,7 @@ export default defineComponent({
       const notes = filterNotes();
       // console.log(1111,notes)
       for (const note of notes) {
-        const active = state.times[note.index];
+        const active = allNote.value[note.musicalNotesIndex];
         setTimeout(() => {
           if (useedid.value.includes(active.id)) {
             return;
@@ -269,7 +276,11 @@ export default defineComponent({
           const stemEl = document.getElementById("vf-" + active.id + "-stem");
           let errType = scoreData.itemType === "cadence" ? note.rhythmicAssessment.result : note.pitchAssessment.result;
           // console.log(1111222,errType)
-          const isNeedCopyElement = scoreData.itemType === "integrity" ? false : ["HIGH", "LOW", "EARLY", "LATE"].includes(errType);
+          /**
+           * 新版小酷AI不需要在当前的音符复制出来一个音符,所以注释掉isNeedCopyElement和copySvg
+           */
+          // const isNeedCopyElement = scoreData.itemType === "integrity" ? false : ["HIGH", "LOW", "EARLY", "LATE"].includes(errType);
+          const isNeedCopyElement = false;
           if (scoreData.itemType === "integrity") {
             errType = errType = note.pitchAssessment.result === "HIGH" || note.pitchAssessment.result === "LOW" || note.pitchAssessment.result === "WRONG" ? "RIGHT" : errType;
           }
@@ -292,14 +303,14 @@ export default defineComponent({
             if (scoreData.musicType === "staff") {
               stemEl?.classList.add(colorsClass.RIGHT);
               svgEl?.classList.add(colorsClass.RIGHT);
-              copySvg = svgEl.querySelector(".vf-notehead")!.cloneNode(true) as SVGSVGElement;
+              // copySvg = svgEl.querySelector(".vf-notehead")!.cloneNode(true) as SVGSVGElement;
             } else {
               //copySvg = svgEl.querySelector('.vf-numbered-note-head')!.cloneNode(true) as SVGSVGElement
 
               if (isNeedCopyElement) {
                 svgEl?.classList.add(styles.inaccuracy);
                 const targetId = errType === "HIGH" ? "topSvg" : errType === "LOW" ? "bottomSvg" : errType === "EARLY" ? "leftSvg" : errType === "LATE" ? "rightSvg" : "";
-                copySvg = document.getElementById(targetId)!.cloneNode(true) as SVGSVGElement;
+                // copySvg = document.getElementById(targetId)!.cloneNode(true) as SVGSVGElement;
                 const { width, height } = svgEl.getBoundingClientRect() || {};
                 // @ts-ignore
                 let { x, y } = svgEl?.getBBox() || {};
@@ -319,18 +330,15 @@ export default defineComponent({
               // svgEl.prepend(rect);
             }
             if (scoreData.musicType === "staff") {
-              copySvg.style.transform = getOffsetPosition(errType);
-              //svgEl.style.opacity = '.7'
+              // copySvg.style.transform = getOffsetPosition(errType);
               if (stemEl) {
-                //stemEl.style.opacity = '.7'
+                //
               }
             }
-            copySvg.id = "vf-" + active.id + "-copy";
-            copySvg?.classList.add(colorsClass[errType]);
-            // stemEl?.classList.add(colorsClass.RIGHT)
+            // copySvg.id = "vf-" + active.id + "-copy";
+            // copySvg?.classList.add(colorsClass[errType]);
             // @ts-ignore
-            state.osmd?.container.querySelector("svg")!.insertAdjacentElement("afterbegin", copySvg);
-            // svgEl?.parentElement?.appendChild(copySvg)
+            // state.osmd?.container.querySelector("svg")!.insertAdjacentElement("afterbegin", copySvg);
           }
         }, 300);
       }
@@ -362,7 +370,7 @@ export default defineComponent({
     const setPathColor = () => {
       console.log(11111, detailData.musicalNotesPlayStats, scoreData.itemType);
       for (const note of detailData.musicalNotesPlayStats) {
-        const active = state.times[note.index];
+        const active = allNote.value[note.index];
         const svgEl = active?.id ? document.getElementById("vf-" + active?.id) : null;
         switch (scoreData.itemType) {
           case "intonation":
@@ -389,8 +397,11 @@ export default defineComponent({
     const handleRendered = (osmd: any) => {
       state.musicRendered = true;
       state.osmd = osmd;
-      state.times = formateTimes(osmd);
-      console.log("🚀 ~ state.times:", state.times);
+      allNote.value = formateTimes(osmd);
+      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))
       // @ts-ignore
       const beams = Array.from(new Set(document.getElementsByClassName("vf-beam")));
       beams.forEach((item: any) => {

+ 12 - 10
src/state.ts

@@ -15,7 +15,7 @@ import { getMusicSheetDetail } from "./utils/baseApi"
 import { getQuery } from "/src/utils/queryString";
 import { followData, skipNotePractice } from "/src/view/follow-practice/index"
 import { changeSongSourceByBate } from "/src/view/audio-list"
-import { moveSmoothAnimation, smoothAnimationState, moveSmoothAnimationByPlayTime, moveTranslateXNum, destroySmoothAnimation } from "/src/page-instrument/view-detail/smoothAnimation"
+import { moveSmoothAnimation, smoothAnimationState, moveSmoothAnimationByPlayTime, moveTranslateXNum, destroySmoothAnimation, calcClientWidth } from "/src/page-instrument/view-detail/smoothAnimation"
 import { storeData } from "/src/store";
 import { downloadXmlStr } from "./view/music-score"
 import { musicScoreRef } from "/src/page-instrument/view-detail/index"
@@ -382,7 +382,7 @@ const state = reactive({
   /** 激活的note index */
   activeNoteIndex: 0,
   /** 激活的小节 */
-  activeMeasureIndex: 0,
+  activeMeasureIndex: -1,
   /** 选段状态 */
   sectionStatus: false,
   /** 选段数据 */
@@ -1284,8 +1284,8 @@ export const evaluatCreateMusicPlayer = () => {
   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/1722336027096.mp3", //效音音频url
-    checkFrequence: 787,
+    tuneSrc: "https://oss.dayaedu.com/MECMP/1722593665681.mp3", //效音音频url
+    checkFrequence: 496,
   });
 };
 
@@ -1418,7 +1418,7 @@ const setState = (data: any, index: number) => {
   /**
    * 单曲,指法根据用户当前的乐器来显示,如果没有则取musicSheetSoundList第一个track
    */
-  let musicalCode = !storeData.user?.instrumentId ? data.musicSheetSoundList.find((item:any)=>{ return item.audioPlayType === "PLAY" })?.track || '' : data.musicSheetSoundList?.find((item: any) => item?.musicalInstrumentId == storeData.user?.instrumentId && item.audioPlayType === "PLAY")?.track || '';
+  let musicalCode = !storeData.user?.instrumentId ? data.musicSheetSoundList?.find((item:any)=>{ return item.audioPlayType === "PLAY" })?.track || '' : data.musicSheetSoundList?.find((item: any) => item?.musicalInstrumentId == storeData.user?.instrumentId && item.audioPlayType === "PLAY")?.track || '';
   const pitchSubject = musicalInstrumentCodeInfo.find((n) => n.code.toLocaleLowerCase() === subjectCode.toLocaleLowerCase())
   const pitchMusical = musicalInstrumentCodeInfo.find((n) => n.code.toLocaleLowerCase() === musicalCode.toLocaleLowerCase())
   state.subjectCodeId = pitchSubject ? pitchSubject.id : 0
@@ -1545,6 +1545,9 @@ const setState = (data: any, index: number) => {
    * STAVE("五线谱"),JIAN("固定调"),FIRST("首调"),
    */
   musicalRenderType = data.scoreType === 'STAVE' ? 'staff' : data.scoreType === 'JIAN' ? 'fixedTone' : data.scoreType === 'FIRST' ? '' : 'firstTone';
+  if (!state.isEvaluatReport) {
+    state.musicRenderType = query.musicRenderType || musicalRenderType || EnumMusicRenderType.firstTone;  
+  }
   state.musicRenderType = query.musicRenderType || musicalRenderType || EnumMusicRenderType.firstTone;  
   /**
    * TODO:摇篮曲特殊处理
@@ -1591,7 +1594,7 @@ export const followBeatPaly = () => {
     metroTimer = null
     return;
   }
-  const time = state.measureTime * 1000 / metronomeData.totalNumerator
+  const time = state.measureTime * 1000 / metronomeData.totalNumerator / state.basePlayRate;
   requestAnimationFrame(() => {
     const endTime = Date.now();
     if (endTime - state.beatStartTime < time) {
@@ -1858,8 +1861,9 @@ watch(
 
 /** 刷新谱面 */
 export const refreshMusicSvg = () => {
+  clearSelection();
   resetBaseRate();
-  state.activeMeasureIndex = 0;
+  state.activeMeasureIndex = -1;
   // 销毁旋律线
   destroySmoothAnimation()
   musicScoreRef.value?.refreshMusicScore()
@@ -1872,9 +1876,7 @@ watch(
     // 有字符 并且是竖向指法 并且是一行谱
     if(state.fingeringInfo?.name && state.fingeringInfo.direction === "vertical" && state.isSingleLine){
       nextTick(() => {
-        if (smoothAnimationState.osdmScrollDom) {
-          smoothAnimationState.osdmScrollDomWith = smoothAnimationState.osdmScrollDom.offsetWidth | 0
-        }
+        calcClientWidth()
       })
     }
     // 如果有指法,并且是竖向指法时,切换指法时,谱面宽度变化,需要重新渲染谱面

+ 34 - 12
src/view/evaluating/index.tsx

@@ -107,7 +107,9 @@ export const evaluatingData = reactive({
 	jsonLoadDone: true, // 延迟检测的动画dom加载完成状态
 	hideResultModal: false, // 评测作业,如果不是完整评测,需要隐藏评测结果弹窗
 	oneselfCancleEvaluating: false, // 是否是自主取消评测,自主取消评测,不生产评测记录
-	isBeginMask: false // 倒计时和系统节拍器时候的遮罩,防止用户点击
+	isBeginMask: false, // 倒计时和系统节拍器时候的遮罩,防止用户点击,
+  recordingTime: 0, // 调用startRecording的时间 
+  endEvaluatingTime: 0, // 调用endEvaluating的时间 
 });
 
 const sendOffsetTime = async (offsetTime: number) => {
@@ -305,9 +307,9 @@ export const addMeasureScore = (measureScore: any, show = true) => {
 const handleScoreResult = (res?: IPostMessage) => {
   console.log("返回", res, evaluatingData.oneselfCancleEvaluating);
   // 如果是手动取消评测,不生成评测记录
-  if (evaluatingData.oneselfCancleEvaluating) {
-    return;
-  }
+  // if (evaluatingData.oneselfCancleEvaluating) {
+  //   return;
+  // }
   if (res?.content) {
     const { header, body } = res.content;
     // 效音返回
@@ -331,6 +333,10 @@ const handleScoreResult = (res?: IPostMessage) => {
         } else {
           evaluatingData.hideResultModal = false;
         }
+        // 手动取消评测,不展示评测弹窗
+        if (evaluatingData.oneselfCancleEvaluating) {
+          evaluatingData.hideResultModal = true;
+        }
         evaluatingData.resulstMode = evaluatingData.isErrorState ? false : true;
       }, 200);
       evaluatingData.resultData = {
@@ -367,7 +373,6 @@ export const handleStartBegin = async (preTimes?: number) => {
 		evaluatingData.startBegin = false;
 		return;
 	}
-  initSetPlayRate();
 	resetPlaybackToStart();
 	evaluatingData.startBegin = true;
 	if (evaluatingData.isDisabledPlayMusic) {
@@ -415,6 +420,11 @@ export const handleStartBegin = async (preTimes?: number) => {
 			}, 300);
 		}
 	})
+  /**
+   * 安卓端,快速点击开始评测和结束评测,间隔太小了时,会出现异常情况(音频没有停止播放)
+   * 记录一下startRecord的开始时间
+   */
+  evaluatingData.recordingTime = +new Date();
 	// 如果开启了摄像头, 开启录制视频
 	if (state.setting.camera) {
 		console.log("开始录制视频");
@@ -476,9 +486,10 @@ const recordStartTimePoint = async (res?: IPostMessage) => {
 /**
  * 结束评测
  * @param isComplete 是否完整评测
+ * @param endType 结束类型,selfCancel:是否是自己取消本次评测
  * @returns
  */
-export const handleEndEvaluat = (isComplete = false) => {
+export const handleEndEvaluat = (isComplete = false, endType?: string) => {
   // 没有开始评测 , 不是评测模式 , 不评分
   if (!evaluatingData.startBegin || state.modeType !== "evaluating") return;
   // 结束录音
@@ -494,12 +505,14 @@ export const handleEndEvaluat = (isComplete = false) => {
       message: "完整演奏结束才算作业分数!",
     });
   } else {
-    showLoadingToast({
-      message: "评分中",
-      duration: 0,
-      overlay: true,
-      overlayClass: styles.scoreMode,
-    });
+    if (!endType) {
+      showLoadingToast({
+        message: "评分中",
+        duration: 0,
+        overlay: true,
+        overlayClass: styles.scoreMode,
+      });
+    }
   }
   setTimeout(() => {
     evaluatingData.startBegin = false;
@@ -512,6 +525,15 @@ export const handleEndEvaluat = (isComplete = false) => {
   }
 };
 
+// 校验评测结束最小的时间间隔
+export const checkMinInterval = () => {
+  const currentTime = +new Date();
+  // 开始评测和结束评测的间隔时间小于800毫秒,则不处理
+  if (currentTime - evaluatingData.recordingTime < 800) {
+    return;
+  }
+}
+
 /**
  * 结束评测(手动结束评测)
  */

+ 2 - 2
src/view/fingering/fingering-config.ts

@@ -525,7 +525,7 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
   }
 };
 
-export const getFingeringConfig = async (type: IVocals | undefined): Promise<ITypeFingering> => {
+export const getFingeringConfig = async (type: IVocals | undefined, source?: string): Promise<ITypeFingering> => {
   switch (type) {
     case "flute":
       const flute = await import(`./fingering-img/flute/index.json`);
@@ -614,7 +614,7 @@ export const getFingeringConfig = async (type: IVocals | undefined): Promise<ITy
         width: "180px",
       };
     case "hulusi-flute":
-      const hulusi = await import(`./fingering-img/hulusi-flute/index.json`);
+      const hulusi = source === 'musicDetail' ? await import(`./fingering-img/hulusi-flute0/index.json`) : await import(`./fingering-img/hulusi-flute/index.json`);
       return {
         json: hulusi.default,
         relationship: relationships.hulusi,

File diff suppressed because it is too large
+ 9 - 0
src/view/fingering/fingering-img/hulusi-flute0/index.json


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

@@ -13,7 +13,9 @@ export default defineComponent({
       delay: 0,
     });
     const getFingeringData = async () => {
-      fingerData.subject = await getFingeringConfig(state.fingeringInfo.name);
+      // 葫芦丝的指法,云教练页面和听音练习页面,展示的不一样,需要区分
+      const source = state.fingeringInfo.name === 'hulusi-flute' ? 'musicDetail' : '';
+      fingerData.subject = await getFingeringConfig(state.fingeringInfo.name, source);
       console.log("🚀 ~ fingerData.subject:", fingerData.subject);
     };
     onBeforeMount(() => {

+ 18 - 2
src/view/follow-practice/index.tsx

@@ -1,5 +1,5 @@
 import { defineComponent, onMounted, onUnmounted, reactive, ref, watch } from "vue";
-import state, { gotoNext, resetPlaybackToStart, followBeatPaly, skipNotePlay } from "/src/state";
+import state, { gotoNext, resetPlaybackToStart, followBeatPaly, skipNotePlay, initSetPlayRate } from "/src/state";
 import { IPostMessage } from "/src/utils/native-message";
 import { api_cloudFollowTime, api_cloudToggleFollow } from "/src/helpers/communication";
 import { storeData } from "/src/store";
@@ -103,7 +103,8 @@ export const handleFollowStart = async () => {
 	const res = await api_cloudToggleFollow("start");
 	// 用户没有授权,需要重置状态
 	if (res?.content?.reson) {
-		// 
+		followData.isBeginMask = false
+		followData.start = false;
 	} else {
 		// 跟练模式开始前,增加播放系统节拍器
 		const tickend = await handleStartTick();
@@ -119,6 +120,7 @@ export const handleFollowStart = async () => {
 		followData.start = true;
 		followData.index = 0;
 		followData.list = [];
+		initSetPlayRate();
 		resetPlaybackToStart();
 		openToggleRecord(true);
 		getNoteIndex();
@@ -138,6 +140,18 @@ export const handleFollowEnd = () => {
 	console.log("结束");
 };
 
+// 清除当前音符右侧的音符的颜色状态
+const clearRightNoteColor = () => {
+	const noteId = state.times[state.activeNoteIndex]?.id;
+	const leftVal = document.getElementById(`vf-${noteId}`)?.getBoundingClientRect()?.left || 0;
+	state.times.forEach((item: any) => {
+		const note: HTMLElement = document.getElementById(`vf-${item.id}`)!;
+		if (note?.getBoundingClientRect()?.left >= leftVal) {
+			note.classList.remove("follow-up", "follow-down", "follow-error", "follow-success");
+		}
+	});
+}
+
 /**
  * 2024.6.17 新增自动结束跟练模式功能
  * 如果跟练模式,唱完了最后一个有频率的音符,需要自动结束掉跟练模式
@@ -156,6 +170,7 @@ const autoEndFollow = () => {
 		handleFollowEnd()
 		return
 	}
+	clearRightNoteColor();
 }
 
 // 下一个
@@ -265,6 +280,7 @@ export const skipNotePractice = () => {
 	noteFollows.forEach((noteFollow) => {
 		noteFollow?.classList.remove("follow-up", "follow-down", "follow-error");
 	})
+	clearRightNoteColor();
 }
 
 // 移动到对应音符的位置

+ 6 - 5
src/view/music-score/index.tsx

@@ -10,6 +10,7 @@ import { getGradualLengthByXml } from "/src/helpers/calcSpeed";
 import { resetFormate, resetGivenFormate, setGlobalMusicSheet } from "/src/helpers/customMusicScore"
 import { setGlobalData } from "/src/utils";
 import Loading from "/src/view/audio-list/loading"
+import { storeData } from "/src/store";
 
 export const musicRenderTypeKey = "musicRenderType";
 let osmd: any = null;
@@ -96,7 +97,7 @@ export default defineComponent({
 				renderSingleHorizontalStaffline: state.isSingleLine ? true : false,
 				// autoGenerateMultipleRestMeasuresFromRestMeasures: state.isSingleLine ? false : true, // 连续休止小节是否合并显示
 				autoGenerateMultipleRestMeasuresFromRestMeasures: true,
-				drawLyrics: (state.playType === 'sing' && !state.isSimplePage) ? true : false, // 演唱模式才渲染歌词,simple页面不显示歌词
+				drawLyrics: ( ((!state.accompany && !state.music ) || state.playType === 'sing') && !state.isSimplePage) ? true : false, // 演唱模式才渲染歌词,simple页面不显示歌词
 				// darkMode: true, // 暗黑模式
 				// pageFormat: 'A4_P',
 				// autoBeam: true,
@@ -115,14 +116,14 @@ export default defineComponent({
 			// }
 			//osmd.EngravingRules.PageBottomMargin = state.platform === IPlatform.PC ? 1 : 2;
 			if (state.isSimplePage) {
-				osmd.EngravingRules.PageTopMargin = 0;
+				osmd.EngravingRules.PageTopMargin = state.musicRenderType === 'staff' ? 2 : 4;
 				osmd.EngravingRules.PageTopMarginNarrow = 0;
-				osmd.EngravingRules.PageLeftMargin = 0;
+				osmd.EngravingRules.PageLeftMargin = 1;
 				osmd.EngravingRules.PageRightMargin = 0;
 				osmd.EngravingRules.BreathMarkDistance = 0.1; 
 				osmd.EngravingRules.PageBottomMargin = 0;
 			} else {
-				osmd.EngravingRules.PageTopMargin = 2; // 老师端顶部间距
+				osmd.EngravingRules.PageTopMargin = state.isEvaluatReport && storeData.isApp ? 24 : state.isEvaluatReport ? 7 : 3; // 顶部间距
 				osmd.EngravingRules.PageTopMarginNarrow = 3;
 				osmd.EngravingRules.PageLeftMargin = 2;
 				osmd.EngravingRules.PageRightMargin = 2;
@@ -219,7 +220,7 @@ export default defineComponent({
 					isInTheGradualRange.value && styles.inGradualRange,
 					state.musicRenderType == EnumMusicRenderType.staff ? "staff" : "jianpuTone",
 					state.isSingleLine && "singleLineMusicBox",
-					!state.isCreateImg ? "blueMusicXml" : ""
+					(!state.isCreateImg && state.musicRenderType === EnumMusicRenderType.staff) ? "blueMusicXml" : ""
 				]}
 			>
 				{slots.default?.()}

+ 15 - 2
src/view/plugins/toggleMusicSheet/choosePartName/index.tsx

@@ -1,9 +1,10 @@
-import { PropType, computed, defineComponent, ref, toRefs, onMounted } from 'vue'
+import { PropType, computed, defineComponent, ref, toRefs, onMounted, watch } from 'vue'
 import { Picker, Button, Icon } from 'vant'
 import styles from './index.module.less'
 import state, { IPlatform } from "/src/state";
 import changeName from "./imgs/changeName.png"
 import { headImg } from "/src/page-instrument/header-top/image";
+import { toggleMusicSheet } from "../index"
 
 export default defineComponent({
   name: 'choosePartName',
@@ -38,6 +39,18 @@ export default defineComponent({
     onMounted(() => {
 			// console.log(myPicker.value,99999,selValues.value,props.partIndex)
 		});
+
+    watch(
+      () => toggleMusicSheet.show,
+      () => {
+        if (toggleMusicSheet.show) {
+          selectIndex.value = partIndex.value
+          selValues.value = [partIndex.value]
+        }
+        //console.log('声轨',selValues.value,partIndex.value,selectIndex.value)
+      }
+    );
+
     return () => (
       <div class={[styles.container, state.platform === IPlatform.PC && styles.pcContainer, styles[state.modeType]]}>
         <div class={styles.head}>
@@ -56,7 +69,7 @@ export default defineComponent({
               columns={columns.value}
               visibleItemCount={Math.ceil(document.body.clientHeight / 40 / 3)}
               onChange={(row) => {
-                // console.log(1111,'选择的索引', row)
+                console.log(1111,'选择的索引', row)
                 if (!partIndexChanged.value) partIndexChanged.value = true
                 selectIndex.value = row.selectedValues[0]
               }}

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

@@ -1,4 +1,4 @@
-import { computed, defineComponent, reactive, toRef } from 'vue'
+import { computed, defineComponent, reactive, toRef, watch } from 'vue'
 import styles from './index.module.less'
 import { Icon, Popup } from 'vant'
 import ChoosePartName from './choosePartName'
@@ -22,7 +22,6 @@ export default defineComponent({
   name: 'ToggleMusicSheet',
   setup() {
     const query = getQuery();
-
     const partListNames = computed(() => {
       let partList = state.partListNames || []
       partList = partList.filter((item: any) => !item?.toLocaleUpperCase()?.includes('COMMON'))

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

@@ -141,6 +141,7 @@
     background-color: #FFC121;
     color: #673207;
     font-size: 14px;
+    font-weight: 600;
 }
 
 .noteFollow {
@@ -257,7 +258,7 @@
     }
 
     .follow-success {
-        display: block;
+        // display: block;
         transform-box: fill-box;
         transform-origin: center;
         animation: noteAnimate 1s linear;
@@ -300,7 +301,7 @@
         content: "";
         position: absolute;
         left: 0;
-        bottom: -10Px;
+        bottom: -3Px;
         width: 100%;
         height: 8Px;
         background: linear-gradient(rgba(7, 24, 56, 0.5) 0%, #010D31 100%);

+ 26 - 19
src/view/tick/index.tsx

@@ -36,27 +36,34 @@ export function closeTick(){
 		tickData.show = false
 	}
 }
+const tickPlayCb = (i: any, resolve: any, source: any) => {
+	if (tickData.tickEnd) {
+		resolve(i)
+		return
+	};
+	tickData.index++;
+	// 当系统节拍器才播放声音,跟练模式需要播放系统节拍器的声音,评测模式,如果没有伴奏,也需要播放系统节拍器的声音
+	if (source && (isUseSystemBeat.value || state.modeType === 'follow' || (state.modeType === 'evaluating' && !state.accompany)) ) {
+		const beatVolume = state.setting.beatVolume / 100
+		source.volume = beatVolume;
+		if (source.volume <= 0) {
+			source.muted = true
+		} else {
+			source.muted = false
+		}
+		source.play();
+	}
+	resolve(i);
+}
 const handlePlay = (i: number, source: any | null) => {
 	return new Promise((resolve) => {
-		_time=setTimeout(() => {
-			if (tickData.tickEnd) {
-				resolve(i)
-				return
-			};
-			tickData.index++;
-			// 当系统节拍器才播放声音
-			if (source && isUseSystemBeat.value) {
-				const beatVolume = state.setting.beatVolume / 100
-				source.volume = beatVolume;
-				if (source.volume <= 0) {
-					source.muted = true
-				} else {
-					source.muted = false
-				}
-				source.play();
-			}
-			resolve(i);
-		}, tickData.beatLengthInMilliseconds);
+		if (i === 0 ) {
+			tickPlayCb(i, resolve, source);
+		} else {
+			_time=setTimeout(() => {
+				tickPlayCb(i, resolve, source);
+			}, tickData.beatLengthInMilliseconds);
+		}
 	});
 };
 

+ 1 - 0
src/view/transfer-to-img/index.tsx

@@ -41,6 +41,7 @@ export default defineComponent({
 
 		onMounted(() => {
 			(window as any).appName = "colexiu";
+			state.isSingleLine = false;
 			state.isCreateImg = true;
 			state.isEvxml = true;
 			state.xmlUrl = decodeURIComponent(query.xmlUrl);

File diff suppressed because it is too large
+ 0 - 0
stats.html


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