TIANYONG 3 months ago
parent
commit
09ba653922
60 changed files with 1044 additions and 169 deletions
  1. 9 9
      dist/colexiu.html
  2. 2 2
      dist/flexible.js
  3. 9 9
      dist/index.html
  4. 12 12
      dist/instrument.html
  5. 10 10
      dist/orchestra.html
  6. 7 7
      dist/report-share.html
  7. BIN
      dist/ttf/DIN_Alternate_Bold-a17c25bc.ttf
  8. 1 1
      osmd-extended
  9. BIN
      src/assets/tick.wav
  10. BIN
      src/assets/tock.wav
  11. 7 9
      src/helpers/formateMusic.ts
  12. 2 1
      src/helpers/metronome.ts
  13. 11 11
      src/page-instrument/App.tsx
  14. 9 0
      src/page-instrument/api.ts
  15. 7 0
      src/page-instrument/component/authorName/index.module.less
  16. 11 4
      src/page-instrument/component/authorName/index.tsx
  17. 288 0
      src/page-instrument/component/the-music-list/filterList.tsx
  18. BIN
      src/page-instrument/component/the-music-list/imgs/headImg.png
  19. BIN
      src/page-instrument/component/the-music-list/imgs/queding.png
  20. BIN
      src/page-instrument/component/the-music-list/imgs/quxiao.png
  21. BIN
      src/page-instrument/component/the-music-list/imgs/shouqi.png
  22. BIN
      src/page-instrument/component/the-music-list/imgs/sj.png
  23. BIN
      src/page-instrument/component/the-music-list/imgs/xiang.png
  24. BIN
      src/page-instrument/component/the-music-list/imgs/zhankai.png
  25. 344 4
      src/page-instrument/component/the-music-list/index.module.less
  26. 2 2
      src/page-instrument/component/the-music-list/index.tsx
  27. 60 19
      src/page-instrument/component/the-music-list/list.tsx
  28. BIN
      src/page-instrument/custom-plugins/the-vip/icon_bg.png
  29. 2 2
      src/page-instrument/custom-plugins/the-vip/index.module.less
  30. BIN
      src/page-instrument/evaluat-model/evaluat-result/img/icon_expression0.png
  31. BIN
      src/page-instrument/evaluat-model/evaluat-result/img/icon_expression1.png
  32. BIN
      src/page-instrument/evaluat-model/evaluat-result/img/icon_expression2.png
  33. BIN
      src/page-instrument/evaluat-model/evaluat-result/img/icon_expression3.png
  34. BIN
      src/page-instrument/evaluat-model/evaluat-result/img/icon_expression4.png
  35. 1 10
      src/page-instrument/evaluat-model/evaluat-result/index.tsx
  36. 1 0
      src/page-instrument/header-top/index.module.less
  37. 20 11
      src/page-instrument/header-top/index.tsx
  38. 1 1
      src/page-instrument/header-top/settting/index.module.less
  39. 4 1
      src/page-instrument/header-top/settting/index.tsx
  40. 6 0
      src/page-instrument/header-top/speed/index.tsx
  41. 2 1
      src/page-instrument/simple-detail/index.tsx
  42. 6 0
      src/page-instrument/view-detail/emptyMusic/index.tsx
  43. 3 2
      src/page-instrument/view-detail/smoothAnimation/index.ts
  44. 23 5
      src/page-instrument/view-figner/change-subject/index.tsx
  45. 4 0
      src/page-instrument/view-figner/index.module.less
  46. 27 0
      src/page-instrument/view-figner/index.tsx
  47. 6 6
      src/state.ts
  48. 15 18
      src/utils/crunker.ts
  49. 129 8
      src/view/audio-list/index.tsx
  50. BIN
      src/view/evaluating/icons/1.png
  51. BIN
      src/view/evaluating/icons/2.png
  52. BIN
      src/view/evaluating/icons/3.png
  53. BIN
      src/view/evaluating/icons/4.png
  54. BIN
      src/view/evaluating/icons/5.png
  55. 0 1
      src/view/evaluating/index.tsx
  56. 0 0
      src/view/fingering/fingering-img/pan-flute/index.json
  57. 1 1
      src/view/selection/index.tsx
  58. 1 1
      src/view/tick/index.tsx
  59. 0 0
      stats.html
  60. 1 1
      vite.config.ts

+ 9 - 9
dist/colexiu.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-f2575c3a.js"></script>
 
   <meta charset="UTF-8" />
   <link rel="icon" type="image/svg+xml" href="./vite.svg" />
@@ -40,12 +40,12 @@
       },
     })
   </script>
-  <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">
+  <script type="module" crossorigin src="./js/colexiu-6208e305.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-a3384f8a.js">
+  <link rel="modulepreload" crossorigin href="./js/index-819a3efd.js">
+  <link rel="modulepreload" crossorigin href="./js/index-189a7dc1.js">
+  <link rel="modulepreload" crossorigin href="./js/index-7d72126e.js">
+  <link rel="stylesheet" href="./css/index-0e9d5ebe.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>
@@ -56,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-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>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-2b34d33c.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/colexiu-legacy-5c7f1326.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 2 - 2
dist/flexible.js

@@ -16,8 +16,8 @@
       f.style.fontSize = c + "px", k.rem = a.rem = c
       window.fontSize = c
     } catch (error) {
-      f.style.fontSize = 64 + "px"
-      window.fontSize = 64
+      f.style.fontSize = 37.5 + "px"
+      window.fontSize = 37.5
     }
 
   }

+ 9 - 9
dist/index.html

@@ -2,7 +2,7 @@
 <html lang="ZH-cn">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-f2575c3a.js"></script>
 
   <meta charset="UTF-8">
   <link rel="icon" href="./favicon.ico" />
@@ -75,13 +75,13 @@
       }
     })
   </script>
-  <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">
+  <script type="module" crossorigin src="./js/gym-5714a350.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-a3384f8a.js">
+  <link rel="modulepreload" crossorigin href="./js/index-819a3efd.js">
+  <link rel="modulepreload" crossorigin href="./js/index-aec99ffc.js">
+  <link rel="modulepreload" crossorigin href="./js/index-7d72126e.js">
   <link rel="modulepreload" crossorigin href="./js/plyr.min-c8c2777b.js">
-  <link rel="stylesheet" href="./css/index-cfe86983.css">
+  <link rel="stylesheet" href="./css/index-0e9d5ebe.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">
@@ -98,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-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>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-2b34d33c.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/gym-legacy-7f20a45f.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 12 - 12
dist/instrument.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-f2575c3a.js"></script>
 
   <meta charset="UTF-8" />
   <meta name="viewport"
@@ -41,34 +41,34 @@
       })
     }
   </script>
-  <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" crossorigin src="./js/instrument-e52a4e38.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-a3384f8a.js">
+  <link rel="stylesheet" href="./css/index-0e9d5ebe.css">
+  <link rel="stylesheet" href="./css/instrument-118f68d3.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>
 
 <body>
   <div id="app"></div>
-  <img id="loading" class="show" src="./loading.svg" alt="loading" />
-  <script>
+  <!-- <img id="loading" class="show" src="/loading.svg" alt="loading" /> -->
+  <!-- <script>
     // 处理课堂乐器老师端打开听音练习时去掉加载动画
     if (location.href.indexOf('view-figner') >= 0 && location.href.indexOf('platform=pc') >= 0 && location.href.indexOf('linkSource=class') < 0 && !location.href.includes("simple-detail")) {
       var _loading = document.getElementById("loading");
       _loading && document.body.removeChild(_loading);
     }
-  </script>
+  </script> -->
 
   
-  <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></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>  
+  </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-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>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-2b34d33c.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/instrument-legacy-ae015498.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 10 - 10
dist/orchestra.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-f2575c3a.js"></script>
 
   <meta charset="UTF-8" />
   <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
@@ -41,13 +41,13 @@
       transition: opacity .3s;
     }
   </style>
-  <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">
+  <script type="module" crossorigin src="./js/orchestra-92d321fd.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-a3384f8a.js">
+  <link rel="modulepreload" crossorigin href="./js/index-819a3efd.js">
+  <link rel="modulepreload" crossorigin href="./js/index-189a7dc1.js">
+  <link rel="modulepreload" crossorigin href="./js/index-aec99ffc.js">
+  <link rel="modulepreload" crossorigin href="./js/index-7d72126e.js">
+  <link rel="stylesheet" href="./css/index-0e9d5ebe.css">
   <link rel="stylesheet" href="./css/index-85f95688.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>
@@ -70,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-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>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-2b34d33c.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/orchestra-legacy-285291d8.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

+ 7 - 7
dist/report-share.html

@@ -2,7 +2,7 @@
 <html lang="en">
 
 <head>
-  <script type="module" crossorigin src="./js/polyfills-cfb574f0.js"></script>
+  <script type="module" crossorigin src="./js/polyfills-f2575c3a.js"></script>
 
   <meta charset="UTF-8" />
   <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
@@ -25,11 +25,11 @@
       transition: opacity .3s;
     }
   </style>
-  <script type="module" crossorigin src="./js/report-share-2b948aea.js"></script>
-  <link rel="modulepreload" crossorigin href="./js/index-f30bced8.js">
+  <script type="module" crossorigin src="./js/report-share-74712f12.js"></script>
+  <link rel="modulepreload" crossorigin href="./js/index-a3384f8a.js">
   <link rel="modulepreload" crossorigin href="./js/plyr.min-c8c2777b.js">
-  <link rel="modulepreload" crossorigin href="./js/index-236e13a9.js">
-  <link rel="stylesheet" href="./css/index-cfe86983.css">
+  <link rel="modulepreload" crossorigin href="./js/index-7d72126e.js">
+  <link rel="stylesheet" href="./css/index-0e9d5ebe.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>
@@ -52,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-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>
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-2b34d33c.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/report-share-legacy-7b42df50.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 
 </html>

BIN
dist/ttf/DIN_Alternate_Bold-a17c25bc.ttf


+ 1 - 1
osmd-extended

@@ -1 +1 @@
-Subproject commit 2692703cac7823ace1b04e8923c5eb4648d2b72e
+Subproject commit 8646ea65db6520df8fe8d5d180d5c9dcf634e2b2

BIN
src/assets/tick.wav


BIN
src/assets/tock.wav


+ 7 - 9
src/helpers/formateMusic.ts

@@ -378,12 +378,12 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
 	/** 第一分谱如果是约定的配置分谱则跳过 */
 	if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
 		partIndex++;
-		partListNames.shift();
+		//partListNames.shift();
 	}
 	const visiblePartInfo = partList[partIndex];
 	// console.log(visiblePartInfo, partIndex)
 	// 根据后台已选择的分轨筛选出能切换的声轨
-	state.partListNames = partListNames;
+	//state.partListNames = partListNames;
 	// console.log('分轨名称',state.partListNames)
 	if (visiblePartInfo) {
 		const id = visiblePartInfo.getAttribute("id");
@@ -523,14 +523,11 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
 export const onlyVisible2 = (xml: string): string => {
 	if (!xml) return "";
 	// console.log('原始xml')
-	const detailId = state.examSongId + "";
+	//const detailId = state.examSongId + "";
 	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
 	const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
-	const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
-
-
-	state.partListNames = partListNames;
-
+	//const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
+	//state.partListNames = partListNames;
 	Array.from(partList).forEach((part) => {
 		let partListName = part.getElementsByTagName("part-name")?.[0]?.textContent?.trim();
 		if (!state.canSelectTracks.includes(partListName)) {
@@ -1298,7 +1295,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				// 找出这个音符前面音符的结束时间
 				let preNoteTImes = allNotes[allNotes.length - 1]?.endtime*1000
 				if(!preNoteTImes){
-					preNoteTImes = Math.max(fixtime - noteLength, 0)*1000 //如果前一个音符没有结束时间,证明这个音符是第一个音符没有打时间,所以往前奏里面找补
+					//如果前一个音符没有结束时间,证明这个音符是第一个音符没有打时间,当有timegap以fixtime当开始时间(1795013294269087745),当第一个小节有times这个往前奏里面找补(1795013306436763649)
+					preNoteTImes = (state.evXmlBeginArr.length>0 ? fixtime : Math.max(fixtime - noteLength, 0))*1000
 				}
 				// 找出这个音符后面音符的开始时间
 				let nextI = i

+ 2 - 1
src/helpers/metronome.ts

@@ -312,7 +312,8 @@ class Metronome {
 					let nextNoteStartTime = times[note.measures[note.measures.length - 1].i + 1]?.time
 					let noteEndTime = 0
 					if(!nextNoteStartTime){
-						noteEndTime = time + noteLengthTime
+						// 当不够的时候补上时值
+						noteEndTime = time + noteLengthTime > endtime ? time + noteLengthTime : endtime
 					}else{
 						if(Math.abs(nextNoteStartTime - endtime)*1000< 10){
 							// 当首位本来就是相连的

+ 11 - 11
src/page-instrument/App.tsx

@@ -113,17 +113,17 @@ export default defineComponent({
         });
       }
 
-      // 禁用右键菜单
-      document.addEventListener("contextmenu", function (event) {
-        // event.preventDefault();
-      });
-      // 禁用浏览器快捷键
-      document.addEventListener("keydown", function (event) {
-        // 屏蔽 F12 和 Ctrl+Shift+I
-        if (event.key === "F12" || (event.ctrlKey && event.shiftKey && event.key === "I") || (event.metaKey && event.altKey && event.key === "I")) {
-          event.preventDefault();
-        }
-      });
+      // // 禁用右键菜单
+      // document.addEventListener("contextmenu", function (event) {
+      //   // event.preventDefault();
+      // });
+      // // 禁用浏览器快捷键
+      // document.addEventListener("keydown", function (event) {
+      //   // 屏蔽 F12 和 Ctrl+Shift+I
+      //   if (event.key === "F12" || (event.ctrlKey && event.shiftKey && event.key === "I") || (event.metaKey && event.altKey && event.key === "I")) {
+      //     event.preventDefault();
+      //   }
+      // });
     });
 
     onUnmounted(() => {

+ 9 - 0
src/page-instrument/api.ts

@@ -64,6 +64,15 @@ export const api_musicSheetPage = (data: any) => {
   });
 };
 
+/** 获取教程和年级  */
+export const api_musicTagTree = () => {
+  return request.get("/musicTag/tree");
+};
+/** 获取标签  */
+export const api_musicSheetTag = () => {
+  return request.get("/musicSheetTag/queryList");
+};
+
 /**
  * 获取声部列表
  */

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

@@ -4,6 +4,13 @@
 .title{
     width: 280px;
     margin: 0 auto;
+    &.isScroll{
+        :global{
+            .van-notice-bar .van-notice-bar__wrap{
+                justify-content: initial;
+            }
+        }
+    }
     :global{
         .van-notice-bar{
             height: 30px;

+ 11 - 4
src/page-instrument/component/authorName/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, computed } from "vue"
+import { defineComponent, computed, onMounted, ref } from "vue"
 import styles from "./index.module.less"
 import { NoticeBar } from "vant"
 import state from "/src/state"
@@ -11,14 +11,21 @@ export default defineComponent({
 			const context = state.musicLyricist ? state.musicComposer + ' / ' + state.musicLyricist : state.musicComposer;
 			return context
 		});
-
+      const noticeBarDom = ref()
+      const isScroll = ref(false)
+      onMounted(() => {
+         const noticeBarEl = noticeBarDom.value?.$el
+         if(noticeBarEl){
+            isScroll.value = noticeBarEl.querySelector(".van-notice-bar__wrap")?.offsetWidth < noticeBarEl.querySelector(".van-notice-bar__content")?.offsetWidth
+         }
+      })
       return () => (
          <>
             {
                !smoothAnimationState.isShow.value && !state.isCombineRender && 
                <div class={["authorName", styles.authorName]}>
-                  <div class={[styles.title, state.isCbsView && styles.blackTitle]}>
-                     <NoticeBar text={state.examSongName} background="none" />
+                  <div class={[styles.title, state.isCbsView && styles.blackTitle, isScroll.value && styles.isScroll]}>
+                     <NoticeBar ref={noticeBarDom} text={state.examSongName} background="none" />
                   </div>
                   <div class={styles.authorCon}>
                      <div class={[styles.author, state.isCbsView && styles.blackTitle]}>

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

@@ -0,0 +1,288 @@
+import { defineComponent, PropType, computed, ref, nextTick, reactive } from "vue";
+import { api_musicTagTree, api_musicSheetTag, api_subjectList } from "../../api";
+import { Popover } from "vant"
+import styles from "./index.module.less";
+import headImg from "./imgs/headImg.png"
+import quedingImg from "./imgs/queding.png"
+import quxiaoImg from "./imgs/quxiao.png"
+import zhankaiImg from "./imgs/zhankai.png"
+import shouqiImg from "./imgs/shouqi.png"
+import sjImg from "./imgs/sj.png"
+import xiangImg from "./imgs/xiang.png"
+import closeImg from "../../header-top/image/closeImg.png"
+import state, { IPlatform } from "/src/state";
+import { getQuery } from "/src/utils/queryString";
+
+export default defineComponent({
+	name: "filterList",
+    emits: ["close", "handleConfirm"],
+	setup(props, { emit }) {
+        const query: any = getQuery();
+        const queryObj = reactive({
+            audioPlayTypes:"",
+            sheetTag:"",
+            course:"",
+            grade:"",
+            subject: {
+                name: "",
+                id: ""
+            }
+        })
+        function handleRefresh(){
+            queryObj.audioPlayTypes = ""
+            queryObj.sheetTag = ""
+            queryObj.course = ""
+            queryObj.grade = ""
+            queryObj.subject = {
+                name: "",
+                id: ""
+            }
+            handleSubjectOne()
+        }
+        function handleConfirm() {
+            emit("handleConfirm",{
+                audioPlayTypes:  queryObj.audioPlayTypes,
+                bookVersionId: queryObj.grade ? queryObj.grade : queryObj.course,
+                musicTagIds: queryObj.sheetTag,
+                musicalInstrumentId: queryObj.subject.id
+            })
+        }
+        // 场景
+        const audioPlayTypesOption = [
+            { text: '全部', value: "" },
+            { text: '演奏', value: "PLAY" },
+            { text: '演唱', value: "SING" },
+            { text: '演奏+演唱', value: "PLAY,SING" },
+        ]
+        function handleSelAudioPlayTypes(item:any){
+            queryObj.audioPlayTypes = item.value
+        }
+        // 获取标签
+        getMusicSheetTag()
+        const sheetTagObj = ref<any[]>([])
+        function getMusicSheetTag(){
+            api_musicSheetTag().then(res=>{
+                if(res.code === 200){
+                    sheetTagObj.value = [{ name:"全部",id:""},...res.data]
+                }
+            })
+        }
+        function handleSelSheetTag(item:any){
+            queryObj.sheetTag = item.id
+        }
+        // 获取教程和年级
+        getMusicTagTree()
+        const courseObj = ref<any[]>([])
+        const gradeObj = ref<any[]>([])
+        function getMusicTagTree(){
+            api_musicTagTree().then(res=>{
+                if(res.code === 200){
+                    courseObj.value = [{ name:"全部",id:""},...res.data]
+                }
+            })
+        }
+        const isExpand = ref(false)
+        const computedCourseObj = computed(()=>{
+            return isExpand.value ? courseObj.value : courseObj.value.slice(0,5)
+        })
+        const courseDom = ref<HTMLDivElement>()
+        const borderBoxConDom = ref<HTMLDivElement>()
+        function handleExpand(){
+            isExpand.value = true
+            nextTick(()=>{
+                const childRect = courseDom.value!.getBoundingClientRect();
+                const parentRect = borderBoxConDom.value!.getBoundingClientRect();
+                const offsetTop = borderBoxConDom.value!.scrollTop + childRect.top - parentRect.top
+                borderBoxConDom.value!.scrollTo({
+                    top: offsetTop,
+                    behavior: 'smooth'
+                });
+            })
+        }
+        function handleSelCourse(item:any){
+            queryObj.course = item.id
+            queryObj.grade = ""
+            gradeObj.value = [{ name:"全部", id:""}, ...(item.children || [])]
+        }
+        function handleSelGrade(item:any){
+            queryObj.grade = item.id
+        }
+        // 获取乐器 
+        state.platform === IPlatform.PC && getSubjectList() // 老师端才加载乐器
+        const subjectList = ref<any[]>([])
+        function getSubjectList(){
+            api_subjectList({
+                delFlag: 0,
+                page: 1,
+                rows: 999,
+            }).then(res => {
+                if(res.code === 200){
+                    subjectList.value = [...res.data.map((item:any)=>{
+                        return item.instruments.length > 1 ? Object.assign(item,{ isExpand: ref(false) }) : item
+                    })]
+                    // 赋默认值
+                    handleSubjectOne()
+                }
+            })
+        }
+        function handleSubjectOne(){
+            if(subjectList.value.length > 0){
+                const instruments = subjectList.value.reduce((arr, item) => {
+                    arr.push(...item.instruments)
+                    return arr
+                }, [])
+                const instrumentId = query.instrumentId
+                // 有id 就用id,没有就默认第一个
+                const instrumentObj = instrumentId ? instruments.find((i: any) => {
+                    return i.id === instrumentId
+                }) : instruments[0]
+                if(instrumentObj){
+                    queryObj.subject.id = instrumentObj.id
+                    queryObj.subject.name = instrumentObj.name
+                }
+            }
+        }
+        function isActiveSubjectPop(item:any) {
+            return item.instruments.some((i:any) => {
+                return i.id === queryObj.subject.id
+            })
+        }
+        function handleSelectPop(item: any) {
+            queryObj.subject.id = item.id
+            queryObj.subject.name = item.name
+        }
+		return () => (
+			<div class={[styles.filterList, styles[state.modeType], state.platform === IPlatform.PC && styles.isPc]}>
+                <div class={[styles.head, "top_draging"]}>
+                    <img class={styles.headTit} src={headImg} />
+				</div>
+                <img class={styles.closeImg} src={closeImg} onClick={()=>{ emit("close") }} />
+				<div class={styles.borderCon}>
+                    <div class={styles.borderBox}>
+                        <div ref={borderBoxConDom} class={styles.borderBoxCon}>
+                            {
+                                sheetTagObj.value.length > 1 &&
+                                    <>
+                                        <div class={styles.titCon}>
+                                            标签
+                                        </div>
+                                        <div class={[styles.filterCon, styles.sheetTag]}>
+                                            {
+                                                sheetTagObj.value.map(item => {
+                                                    return <div class={[styles.tabBox, queryObj.sheetTag === item.id && styles.tabActive]} onClick={() => { handleSelSheetTag(item) }}>{item.name}</div>
+                                                })
+                                            }
+                                        </div>  
+                                    </>
+                            }                      
+                            <div class={styles.titCon}>
+                                场景
+                            </div>
+                            <div class={styles.filterCon}>
+                                {
+                                    audioPlayTypesOption.map(item => {
+                                        return <div class={[styles.tabBox, queryObj.audioPlayTypes === item.value && styles.tabActive]} onClick={() => { handleSelAudioPlayTypes(item) }}>{item.text}</div>
+                                    })
+                                }
+                            </div>
+                            {
+                                courseObj.value.length>1 &&
+                                    <>
+                                        <div ref={courseDom} class={styles.titCon}>
+                                            教程
+                                            {
+                                                isExpand.value &&                          
+                                                    <div class={styles.shouqiImg} onClick={() => {isExpand.value = false}}>
+                                                        收起
+                                                        <img src={shouqiImg} />
+                                                    </div>
+                                            }
+                                        </div>
+                                        <div class={[styles.filterCon, styles.courseType]}>
+                                            {
+                                                computedCourseObj.value.map(item => {
+                                                    return <div class={[styles.tabBox, queryObj.course === item.id && styles.tabActive]} onClick={() => { handleSelCourse(item) }}>{item.name}</div>
+                                                })
+                                            }
+                                            {
+                                                !isExpand.value && courseObj.value.length>5 &&
+                                                    <div class={[styles.tabBox, styles.zhankaiImg]} onClick={handleExpand}>
+                                                        更多
+                                                        <img src={zhankaiImg} />
+                                                    </div>
+                                            }
+                                        </div> 
+                                    </>
+                            }
+                            {
+                                gradeObj.value.length > 1 &&
+                                    <>
+                                        <div class={styles.titCon}>
+                                            年级
+                                        </div>
+                                        <div class={styles.filterCon}>
+                                            {
+                                                gradeObj.value.map(item => {
+                                                    return <div class={[styles.tabBox, queryObj.grade === item.id && styles.tabActive]} onClick={() => { handleSelGrade(item) }}>{item.name}</div>
+                                                })
+                                            }
+                                        </div>  
+                                    </>
+                            }   
+                            {
+                                subjectList.value.length>1 && queryObj.audioPlayTypes!=='SING' &&
+                                    <>
+                                        <div class={styles.titCon}>
+                                            乐器
+                                        </div>
+                                        <div class={styles.filterCon}>
+                                            {
+                                                subjectList.value.map(item => {
+                                                    return item.instruments.length > 1 ? 
+                                                        <Popover
+                                                            class={styles.subjectPopover} 
+                                                            v-model:show={item.isExpand}
+                                                            trigger="click"
+                                                            show-arrow={false}
+                                                        >
+                                                            {{
+                                                                default: () => (
+                                                                    <div class={styles.tabPopoverBox}>
+                                                                        {
+                                                                            item.instruments.map((row: any) => {
+                                                                                return <div class={[styles.tabPopover, queryObj.subject.id === row.id && styles.active]} onClick={() => { 
+                                                                                    item.isExpand = false
+                                                                                    handleSelectPop(row) 
+                                                                                }}>{row.name}</div>
+                                                                            })
+                                                                        }
+                                                                    </div>
+                                                                ),                                                                
+                                                                reference: () => (
+                                                                    <div class={[styles.tabBox, styles.tabBoxPopCon, isActiveSubjectPop(item) && styles.tabActive]}>
+                                                                        <div class={[styles.tabBoxPop, item.isExpand && styles.actTabBoxPop]}>
+                                                                            <div>{isActiveSubjectPop(item)?queryObj.subject.name:item.name}</div>
+                                                                            <img class={styles.sjImg} src={isActiveSubjectPop(item) ? xiangImg : sjImg} />
+                                                                        </div>
+                                                                    </div>
+                                                                )
+                                                            }}
+                                                        </Popover> 
+                                                        : 
+                                                        <div class={[styles.tabBox, queryObj.subject.id === item.instruments[0].id && styles.tabActive]} onClick={() => { handleSelectPop(item.instruments[0]) }}>{item.name}</div>
+                                                })
+                                            }
+                                        </div>   
+                                    </>
+                            }
+                        </div>
+                    </div>
+				</div>
+                <div class={styles.btnCon}>
+                    <img src={quxiaoImg} onClick={handleRefresh} />
+                    <img src={quedingImg} onClick={handleConfirm} />
+                </div>
+			</div>
+		);
+	},
+});

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


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


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


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


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


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


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


+ 344 - 4
src/page-instrument/component/the-music-list/index.module.less

@@ -78,6 +78,17 @@
             }
         }
     }
+    &.isPc{
+        padding: 20px 0;
+        :global{
+            .van-tabs__wrap{
+                display: none !important;
+            }
+            .van-tabs__content{
+                height: 100% !important;
+            }
+        }
+    }
 }
 
 .wrap {
@@ -139,6 +150,31 @@
         .dropdownMenu{
             border-right: 1px solid #DADCE5;
             margin-right: 8px;
+            flex-shrink: 0;
+            display: flex;
+            align-items: center;
+            cursor: pointer;
+            &:active{
+                opacity: 0.8;
+            }
+            &>div{
+                font-weight: 400;
+                font-size: 14px;
+                color: #0CA2EA;
+                line-height: 20px;
+            }
+            &>img{
+                margin: 0 6px 0 4px;
+                width: 9px;
+                height: 5px;
+                &.onImg{
+                    transform: rotate(180deg);
+                }
+            }
+        }
+        .dropdownMenu1{
+            border-right: 1px solid #DADCE5;
+            margin-right: 8px;
             :global{
                 .van-dropdown-menu__bar{
                     height: 20px;
@@ -211,6 +247,10 @@
             margin-top: 10px;
             height: calc(100% - 44px);
             overflow-y: auto;
+            &::-webkit-scrollbar {
+                width: 0;
+                display: none;
+            }
             .van-loading__circular{
                 color: rgba(0,0,0,0.3);
             }
@@ -226,12 +266,14 @@
     align-items: center;
     background: #FFFFFF;
     border-radius: 16px;
-    padding: 6px;
+    padding: 3px;
+    border: 3px solid transparent;
     margin-bottom: 8px;
-    &.itemActive {
+    cursor: pointer;
+    &.itemActive, &:active{
         background: linear-gradient( 180deg, #FFFFFF 0%, #BFE1FF 100%);
         box-shadow: 0px 2px 4px 0px #499FE4;
-        border: 3px solid #FFFFFF;
+        border-color: #ffffff;
         .content .name{
             color: #2EAAFE;
         }
@@ -259,10 +301,17 @@
         }
     }
     .content{
+        padding-right: 15px;
+        flex-grow: 1;
+        overflow: hidden;
         .name{
             font-weight: 600;
             font-size: 16px;
             color: #333333;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            line-height: 1;
         }
         .detail{
             display: flex;
@@ -277,6 +326,7 @@
                 border-radius: 4px;
                 border: 0.6px solid #FFC5C5;
                 font-size: 10px;
+                flex-shrink: 0;
                 >img{
                     width: 8px;
                     height: 11px;
@@ -288,12 +338,18 @@
                 }
             }
             .author {
+                flex-shrink: 1;
                 margin-left: 6px;
                 font-weight: 400;
                 font-size: 13px;
                 color: rgba(0,0,0,0.5);
+                white-space: nowrap;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                line-height: 1;
             }
             .playType,.singType{
+                flex-shrink: 0;
                 margin-left: 5px;
                 width: 26px;
                 height: 14px;
@@ -314,11 +370,13 @@
     }
 }
 .empty{
-    margin-top: 10px;
     display: flex;
     flex-direction: column;
     align-items: center;
+    justify-content: center;
+    height: 100%;
     >img{
+        margin-top: -10px;
         width: 182px;
     }
     >span{
@@ -326,4 +384,286 @@
         color: rgba(0,0,0,0.46);
         margin-top: 10px;
     }
+}
+
+
+.filterList{
+    width: 408px;
+    height: 316px;
+    background: #B0D8FF;
+    box-shadow: inset 0px -2px 3px 0px #6BA5DD;
+    border-radius: 20px;
+    padding: 10px;
+    position: relative;
+    &.follow{
+        background: #BCE6F1;
+        box-shadow: inset 0px -2px 3px 0px #6397A4;
+        .borderCon{
+            background: #DAEFF5;
+            box-shadow: 0px 0px 3px 0px #98C4D0;
+            .borderBox{
+                border-color: #7CC2E0;
+            }
+        }
+        .btnCon{
+            background: linear-gradient( 180deg, rgba(213,232,255,0) 0%, rgba(218,239,245,0.73) 48%, #DAEFF5 100%);
+        }
+    }
+    &.evaluating{
+        background: #C4DAFF;
+        box-shadow: inset 0px -2px 3px 0px #6F86AD;
+        .borderCon{
+            background: #D5E2FF;
+            box-shadow: 0px 0px 3px 0px #889CBE;
+            .borderBox{
+                border-color: #91AAF9;
+            }
+        }
+        .btnCon{
+            background: linear-gradient( 180deg, rgba(213,232,255,0) 0%, rgba(213,226,255,0.73) 48%, #D5E2FF 100%);
+        }
+    }
+    &.isPc{
+        height:initial !important;
+        .borderBoxCon{
+            height: initial !important;
+            max-height: 70vh;
+        }
+    }
+    .head{
+        position: absolute;
+        top: -11px;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 100%;
+        height: 45px;
+        display: flex;
+        justify-content: center;
+        .headTit{
+            width: 164px;
+            height: 100%;
+        }
+    }      
+    .closeImg{
+        position: absolute;
+        top: -9px;
+        right: -40px;
+        width: 32px;
+        height: 32px;
+        cursor: pointer;
+    }
+    .borderCon{
+        width: 100%;
+        height: 100%;
+        background: #D5E8FF;
+        box-shadow: 0px 0px 3px 0px #639ACF;
+        border-radius: 14px;
+        padding: 10px;
+        .borderBox{
+            padding-top: 10px;
+            padding-bottom: 1px;
+            width: 100%;
+            height: 100%;
+            border-radius: 10px 14px 14px 10px;
+            border: 1px dashed rgb(155,201,246);
+            .borderBoxCon{
+                padding: 0 10px 50px 10px;
+                width: 100%;
+                height: 100%;
+                overflow-y: auto;
+                &::-webkit-scrollbar {
+                    width: 0;
+                    display: none;
+                }
+            }
+            .titCon{
+                position: relative;
+                font-weight: 500;
+                font-size: 16px;
+                color: #333333;
+                line-height: 22px;
+                padding-left: 12px;
+                margin-top: 10px;
+                &::before{
+                    content: "";
+                    position: absolute;
+                    top: 50%;
+                    transform: translateY(-50%);
+                    left: 2px;
+                    width: 4px;
+                    height: 11px;
+                    background: #1CACF1;
+                    border-radius: 3px;
+                }
+                .shouqiImg{
+                    position: absolute;
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    font-weight: 400;
+                    font-size: 12px;
+                    color: #333333;
+                    right: 0;
+                    top: 50%;
+                    transform: translateY(-50%);
+                    cursor: pointer;
+                    &:active{
+                        opacity: 0.8;
+                    }
+                    >img{
+                        margin-left: 2px;
+                        width: 11px;
+                        height: 10px;
+                    }
+                }
+            }
+            .filterCon{
+                display: flex;
+                flex-wrap: wrap;
+                margin-top: 8px;
+                margin-left: -10px;
+                width: calc(100% + 10px);
+                &.courseType{
+                    .tabBox{
+                        width: calc(50% - 10px);
+                        padding: 0 10px;
+                    }
+                }
+                &.sheetTag{
+                    .tabBox{
+                        width: calc(33.33% - 10px);
+                        padding: 0 8px;
+                    }
+                }
+                .tabBox{
+                    cursor: pointer;
+                    width: calc(25% - 10px);
+                    height: 32px;
+                    margin-bottom: 8px;
+                    flex-shrink: 0;
+                    margin-left: 10px;
+                    background: #F6F6F6;
+                    border-radius: 6px;
+                    font-weight: 400;
+                    font-size: 12px;
+                    text-align: center;
+                    line-height: 32px;
+                    color: #333333;
+                    padding: 0 5px;
+                    white-space: nowrap;
+                    overflow: hidden;
+                    text-overflow: ellipsis;
+                    border: 1px solid #F6F6F6;
+                    &:active{
+                        opacity: 0.8;
+                    }
+                    &.tabActive{
+                        background: #EBF8FF;
+                        border-color: rgba(28,172,241,0.5);
+                        color: #1CACF1;
+                    }
+                    &.zhankaiImg{
+                        display: flex;
+                        justify-content: center;
+                        align-items: center;
+                        >img{
+                            margin-left: 2px;
+                            width: 11px;
+                            height: 10px;
+                        }
+                    }
+                    &.tabBoxPopCon{
+                        width: 100%;
+                        margin-left: 0;
+                        margin-bottom: 0;
+                        .tabBoxPop{
+                            width: 100%;
+                            height: 100%;
+                            display: flex;
+                            justify-content: center;
+                            align-items: center;
+                            &.actTabBoxPop{
+                                .sjImg{
+                                    transform: rotate(180deg);
+                                }
+                            }
+                            &>div{
+                                white-space: nowrap;
+                                overflow: hidden;
+                                text-overflow: ellipsis;  
+                            }
+                            .sjImg{
+                                width: 9px;
+                                height: 5px;
+                                margin-left: 4px;
+                            }
+                        }
+                    }
+                }
+                :global{
+                    .van-popover__wrapper{
+                        width: calc(25% - 10px);
+                        height: 32px;
+                        margin-bottom: 8px;
+                        flex-shrink: 0;
+                        margin-left: 10px;
+                    }
+                }
+            }
+        }
+    }
+    .btnCon{
+        position: absolute;
+        left: 24px;
+        bottom: 21px;
+        padding: 10px 0;
+        width: calc(100% - 48px);
+        display: flex;
+        justify-content: center;
+        background: linear-gradient( 180deg, rgba(213,232,255,0) 0%, rgba(213,232,255,0.73) 48%, #D5E8FF 100%);
+        border-radius: 0px 0px 10px 10px;
+        &>img{
+            width: 139px;
+            height: 39px;
+            cursor: pointer;
+            &:active{
+                opacity: 0.8;
+            }
+            &:first-child{
+                margin-right: 10px;
+            }
+        }
+    }
+}
+.subjectPopover{
+    :global{
+        .van-popover__content{
+            margin-top: -1px;
+        }
+    }
+}
+.tabPopoverBox{
+    width: 120px;
+    background: #FFFFFF;
+    box-shadow: 0px 0px 18px 0px rgba(0,0,0,0.15);
+    border-radius: 8px;
+    padding: 8px;
+    .tabPopover{
+        cursor: pointer;
+        height: 32px;
+        border-radius: 4px;
+        font-weight: 400;
+        font-size: 14px;
+        color: #323233;
+        line-height: 32px;
+        text-align: center;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        &.active,&:hover{
+            background: #EEF8FF;
+            color: #1CACF1;
+            font-weight: 500;
+        }
+    }
 }

+ 2 - 2
src/page-instrument/component/the-music-list/index.tsx

@@ -9,7 +9,7 @@ import { getQuery } from "/src/utils/queryString";
 
 const query: any = getQuery();
 export const isMusicList = computed(()=>{
-	return !(query.workRecord || query.modelType || state.platform === IPlatform.PC || query.isCbs)
+	return !(query.workRecord || query.isCbs)
 })
 export const musicListShow = ref(false)
 export default defineComponent({
@@ -18,7 +18,7 @@ export default defineComponent({
 		return () => (
 			<>
 				<Popup class={styles.popup} position="left" v-model:show={musicListShow.value} round overlay-style={{background:'rgba(0, 0, 0, 0.3)'}}>
-					<div class={[styles.tabs, styles[state.modeType]]}>
+					<div class={[styles.tabs, styles[state.modeType], state.platform === IPlatform.PC && styles.isPc]}>
 						<Tabs>
 							<Tab title="其他曲谱">
 								<List />

+ 60 - 19
src/page-instrument/component/the-music-list/list.tsx

@@ -1,13 +1,19 @@
-import { defineComponent, onMounted, reactive, watch } from "vue";
+import { defineComponent, onMounted, reactive, watch, ref} from "vue";
 import styles from "./index.module.less";
 import { api_musicSheetPage } from "../../api";
-import state, { togglePlay } from "/src/state";
-import { List, Image, Field, DropdownMenu, DropdownItem } from "vant";
+import state, { togglePlay, IPlatform } from "/src/state";
+import { List, Image, Field, DropdownMenu, DropdownItem, Popup } from "vant";
 import { postMessage } from "/src/utils/native-message";
 import qs from "query-string";
 import searImg from "./imgs/searImg.png"
 import huoimg from "./imgs/huo.png"
 import emptyImg from "./imgs/empty.png"
+import xiangImg from "./imgs/xiang.png"
+import FilterList from "./filterList"
+import { getQuery } from "/src/utils/queryString";
+import Dragbom from "/src/view/plugins/useDrag/dragbom";
+import { storeData } from "/src/store";
+import useDrag from "/src/view/plugins/useDrag/index";
 
 export default defineComponent({
   name: "TheMusicList-list",
@@ -18,14 +24,24 @@ export default defineComponent({
     },
   },
   setup(props) {
+    const audioPlayTypesOption = [
+      { text: '全部场景', value: "" },
+      { text: '演奏', value: "PLAY" },
+      { text: '演唱', value: "SING" },
+      { text: '演奏+演唱', value: "PLAY,SING" },
+    ]
+    const query: any = getQuery();
     const forms = reactive({
       name: "",
       page: 1,
       rows: 20,
-      musicSheetCategoriesId: state.bizMusicCategoryId,
+      //musicSheetCategoriesId: state.bizMusicCategoryId,
       recentFlag: props.recentFlag ? true : null,
       excludeMusicId: props.recentFlag ? null : state.examSongId,
-      audioPlayTypes:""
+      audioPlayTypes: "",
+      bookVersionId: "",
+      musicTagIds: "",
+      musicalInstrumentId: props.recentFlag ? "" : query.instrumentId
     });
     const data = reactive({
       isFocus: false,
@@ -34,12 +50,7 @@ export default defineComponent({
       loading: false,
       hasNext: true,
     });
-    const audioPlayTypesOption = [
-      { text: '全部场景', value: "" },
-      { text: '演奏', value: "PLAY" },
-      { text: '演唱', value: "SING" },
-      { text: '演奏+演唱', value: "PLAY,SING" },
-    ]
+    const filterShow = ref(false)
     const getList = async () => {
       if (!data.hasNext) return;
       data.loading = true;
@@ -91,26 +102,45 @@ export default defineComponent({
           type: "fullscreen",
         },
       });
+      const queryObj = {
+        ...query
+      }
+      queryObj.id = item.id
+      queryObj["part-index"] = ""
+      forms.musicalInstrumentId && (queryObj.instrumentId = forms.musicalInstrumentId)
       location.href =
         location.origin +
         location.pathname +
-        "?" +
-        qs.stringify({
-          id: item.id,
-          _t: Date.now(),
-        });
+          "?" +
+          qs.stringify(queryObj);
     };
     function formatNumber(num:number) {
       return num >= 10000 
           ? (num / 10000).toFixed(1).replace(/\.0$/, '') + "万" 
           : num.toString();
     }
+    // 拖动
+    const parentClassName = "musicListClass_drag";
+		const userId = storeData.user?.id ? String(storeData.user?.id) : "";
+		const positionInfo =
+        state.platform !== IPlatform.PC
+        ? {
+            styleDrag: { value: null },
+          }
+        : useDrag([`${parentClassName} .top_draging`, `${parentClassName} .bom_drag`], parentClassName, filterShow, userId);
     return () => (
       <div class={styles.wrap}>
         <div class={[styles.searchBox,data.isFocus && styles.isFocus]}>
-          <DropdownMenu class={[styles.dropdownMenu]} overlay={false}>
-							<DropdownItem onChange={handleQuery} v-model={forms.audioPlayTypes} options={audioPlayTypesOption}/>
-					</DropdownMenu>
+          {
+            props.recentFlag ?
+            <DropdownMenu class={[styles.dropdownMenu1]} overlay={false}>
+              <DropdownItem onChange={handleQuery} v-model={forms.audioPlayTypes} options={audioPlayTypesOption}/>
+            </DropdownMenu> :
+            <div class={styles.dropdownMenu} onClick={() => { filterShow.value = true }}>
+              <div>筛选</div>
+              <img class={filterShow.value && styles.onImg} src={xiangImg} />
+            </div>
+          }
           <img src={searImg} />
           <Field placeholder="请输入曲目名称" v-model={forms.name} autocomplete="off" onFocus={()=>{ data.isFocus = true }} onBlur={()=>{ data.isFocus = false }} />
           <div class={styles.searchBtn} onClick={handleQuery}>搜索</div>
@@ -152,6 +182,17 @@ export default defineComponent({
                   <span>暂无内容</span>
                 </div>}
         </List>
+        <Popup  style={positionInfo.styleDrag.value} v-model:show={filterShow.value} class="popup-custom van-scale center-closeBtn musicListClass_drag" transition="van-scale" teleport="body" overlay-style={{ background: "rgba(0, 0, 0, 0.3)" }}>
+          <FilterList onClose={() => { filterShow.value = false }} onHandleConfirm={(queryObj) => {
+            filterShow.value = false
+            forms.audioPlayTypes = queryObj.audioPlayTypes
+            forms.bookVersionId = queryObj.bookVersionId
+            forms.musicTagIds = queryObj.musicTagIds
+            forms.musicalInstrumentId = queryObj.musicalInstrumentId
+            handleQuery()
+          }}></FilterList>
+          {state.platform === IPlatform.PC && <Dragbom />}
+        </Popup>
       </div>
     );
   },

BIN
src/page-instrument/custom-plugins/the-vip/icon_bg.png


+ 2 - 2
src/page-instrument/custom-plugins/the-vip/index.module.less

@@ -1,12 +1,12 @@
 .container {
     position: relative;
     width: 278px;
-    height: 252px;
+    height: 213px;
     background: url('./icon_bg.png') no-repeat;
     background-size: contain;
     display: flex;
     flex-direction: column;
-    padding-top: 126px;
+    padding-top: 90px;
 }
 
 .close {

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


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


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


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


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


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

@@ -13,11 +13,6 @@ import bczpImg from "./img/bczp.png";
 import bczpJzImg from "./img/bczpJz.png";
 import zlycImg from "./img/zlyc.png";
 import iconBadge from "./img/icon-badge.png";
-import icon_expression0 from "./img/icon_expression0.png";
-import icon_expression1 from "./img/icon_expression1.png";
-import icon_expression2 from "./img/icon_expression2.png";
-import icon_expression3 from "./img/icon_expression3.png";
-import icon_expression4 from "./img/icon_expression4.png";
 import yzImg from "./img/yz.png";
 import jzImg from "./img/jz.png";
 import wzxImg from "./img/wzx.png";
@@ -139,11 +134,7 @@ export default defineComponent({
                     </div>
                   </div>
                 </div>
-                <img style={{ display: evaluatingData.resultData.leve === 0 ? "" : "none" }} class={styles.rightBadge} src={icon_expression0} />
-                <img style={{ display: evaluatingData.resultData.leve === 1 ? "" : "none" }} class={styles.rightBadge} src={icon_expression1} />
-                <img style={{ display: evaluatingData.resultData.leve === 2 ? "" : "none" }} class={styles.rightBadge} src={icon_expression2} />
-                <img style={{ display: evaluatingData.resultData.leve === 3 ? "" : "none" }} class={styles.rightBadge} src={icon_expression3} />
-                <img style={{ display: evaluatingData.resultData.leve === 4 ? "" : "none" }} class={styles.rightBadge} src={icon_expression4} />
+                <img class={styles.rightBadge} src={evaluatingData.resultData.img} />
               </div>
               {!state.isPercussion && (
                 <div class={styles.detail}>

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

@@ -62,6 +62,7 @@
     .img {
         width: 32px;
         height: 32px;
+        cursor: pointer;
     }
 
     .listImg {

+ 20 - 11
src/page-instrument/header-top/index.tsx

@@ -34,6 +34,7 @@ import { isMusicList, musicListShow } from "../component/the-music-list";
 import { EvaluatingDriver, FollowDriver, PractiseDriver } from "../custom-plugins/guide-driver";
 import { fingerRef } from "/src/page-instrument/view-detail/index"
 import WorkHomePop from "./workHomePop";
+import { handleLoadBeatMusic } from "/src/view/audio-list"
 
 const ModeView = defineAsyncComponent(() =>
   import('./modeView')
@@ -125,7 +126,7 @@ let isClickMode = false;
  * @param oldPlaySource  没改变之前的播放类型
  * @param isforceReset   是否强制刷新播放状态 模式times时值改变时候也刷新
  */
-export function handlerModeChange(oldPlayType: "play" | "sing", oldPlaySource: IPlayState, isforceReset?: boolean) {
+export async function handlerModeChange(oldPlayType: "play" | "sing", oldPlaySource: IPlayState, isforceReset?: boolean) {
   const isModeChange = modeChangeHandleTimes(oldPlayType, oldPlaySource);
   // 没有切换的时候 不处理下面的
   if (isModeChange) {
@@ -140,6 +141,8 @@ export function handlerModeChange(oldPlayType: "play" | "sing", oldPlaySource: I
     // 隐藏重播按钮
     resetBtn && (resetBtn.value.display = false);
   }
+  // 节拍器音频加载
+  await handleLoadBeatMusic()
   // 当模式改变的时候  放在这里是因为需要等谱面加载完成之后再提示(点击按钮模式切换才提示)
   if (isClickMode) {
     showToast({
@@ -377,14 +380,14 @@ export default defineComponent({
       // 评测开始 禁用
       if (state.modeType === "evaluating") return { display: false, disabled: true };
       if (!state.isAppPlay) {
+        // 播放过程中不能切换
+        if (state.playState === "play") {
+          return { display: true, disabled: true };
+        }
         if (state.playType === "play") {
           // 原声, 伴奏 少一个,就不能切换
           if (state.music && state.accompany) return { display: true, disabled: false };
         } else {
-          // 播放过程中不能切换
-          if (state.playState === "play") {
-            return { display: true, disabled: true };
-          }
           // 范唱
           let index = 0;
           state.fanSong && index++;
@@ -526,6 +529,12 @@ export default defineComponent({
       HANDLE_WORK_ADD();
       // 不在APP中,
       if (!storeData.isApp) {
+        window.parent.postMessage(
+          {
+            api: "back",
+          },
+          "*"
+        );
         window.close();
         return;
       }
@@ -716,7 +725,7 @@ export default defineComponent({
           {!(state.playState == "play" || followData.start || evaluatingData.startBegin) && (
             <div id="noticeBarRollDom" class={styles.headTopLeftBox}>
               {
-                !query.isMove && <img src={iconBack} class={["headTopBackBtn", styles.img, !headTopData.showBack && styles.hidenBack]} onClick={checkBack} />
+                !query.isMove && !state.isAttendClass && <img src={iconBack} class={["headTopBackBtn", styles.img, !headTopData.showBack && styles.hidenBack]} onClick={checkBack} />
               }
               {smoothAnimationState.isShow.value || state.isCombineRender ? (
                 <div
@@ -729,14 +738,14 @@ export default defineComponent({
                   }
                   class={[styles.title, state.isCbsView && styles.blackTitle, "headeTopTitleBtn"]}
                   onClick={() => {
-                    isMusicList.value && (musicListShow.value = true);
+                    isMusicList.value && !state.isAttendClass && (musicListShow.value = true);
                   }}
                 >
-                  {isMusicList.value && <div class={[styles.symbolNote, "driver-8"]}></div>}
+                  {isMusicList.value && !state.isAttendClass && <div class={[styles.symbolNote, "driver-8"]}></div>}
                   <NoticeBar text={state.examSongName} background="none" />
                 </div>
               ) : (
-                isMusicList.value && (
+                isMusicList.value && !state.isAttendClass && (
                   <img
                     src={listImg}
                     class={[styles.img, styles.listImg, "driver-8"]}
@@ -853,7 +862,7 @@ export default defineComponent({
               id={state.platform === IPlatform.PC ? "teacherTop-1" : "studnetT-1"}
               style={{ display: originBtn.value.display ? "" : "none" }}
               class={["driver-3", styles.btn, originBtn.value.disabled && styles.disabled, state.playType === "play" ? styles.playSource : styles.songSource]}
-              onClick={() => {
+              onClick={async () => {
                 const oldPlayType = state.playType;
                 const oldPlaySource = state.playSource;
                 if (state.playType === "play") {
@@ -867,7 +876,7 @@ export default defineComponent({
                     state.playSource = state.fanSong ? "music" : "background";
                   }
                 }
-                handlerModeChange(oldPlayType, oldPlaySource);
+                await handlerModeChange(oldPlayType, oldPlaySource);
                 showToast({
                   message: state.playType === "play" ? (state.playSource === "music" ? "已切换为原声" : "已切换为伴奏") : state.playSource === "music" ? "已切换为范唱" : state.playSource === "background" ? "已切换为伴唱" : "已切换为唱名",
                   position: "top",

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

@@ -187,7 +187,7 @@
                                 padding-top: 3px;
                             }
                             .speedBtn{
-                                width: 16px;
+                                width: 20px;
                                 height: 30px;
                                 background: url("../image/speedBtn.png") no-repeat;
                                 background-size: 100% 100%;

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

@@ -16,6 +16,7 @@ import useDrag from "/src/view/plugins/useDrag/index";
 import Dragbom from "/src/view/plugins/useDrag/dragbom";
 import { storeData } from "/src/store";
 import { getGuidance, setGuidance } from "../../custom-plugins/guide-page/api";
+import { handleLoadBeatMusic } from "/src/view/audio-list"
 
 export default defineComponent({
 	name: "settting",
@@ -100,7 +101,7 @@ export default defineComponent({
                                 </div>   
                         }
                         {
-                            state.modeType === 'practise' && state.mingSong && state.mingSongGirl &&
+                            state.modeType === 'practise' && state.playSource === "mingSong" && state.mingSong && state.mingSongGirl &&
                             <div class={styles.cellBox}>
                                 <div class={styles.tit}>唱名类型</div>
                                 <div class={styles.radioBox}>
@@ -111,6 +112,8 @@ export default defineComponent({
                                                     return
                                                 }
                                                 audioData.mingSongType = item.value as 0|1
+                                                // 加载节拍器音频
+                                                handleLoadBeatMusic()
                                                 changeMingSongType()
                                             } }>{item.name}</div>
                                         })

+ 6 - 0
src/page-instrument/header-top/speed/index.tsx

@@ -9,6 +9,7 @@ import { getQuery } from "/src/utils/queryString";
 import { api_createMusicPlayer, api_updateMusicPlayer, api_checkSocketStatus } from "/src/helpers/communication";
 import { storeData } from "/src/store";
 import { data as workData } from "/src/page-instrument/custom-plugins/work-index";
+import { handleLoadBeatMusic } from "/src/view/audio-list"
 
 export default defineComponent({
 	name: "speed",
@@ -62,6 +63,11 @@ export default defineComponent({
 		// 切换节拍器
 		const toggleSwitch = async (res: any) => {
 			switchLoading.value = true;
+			metronomeDisable.value = res;
+			await handleLoadBeatMusic()
+			switchLoading.value = false;
+			return
+			switchLoading.value = true;
 			try {
 			  if (storeData.isApp && state.enableEvaluation) {
 				// 加载弹窗的开始时间

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

@@ -43,6 +43,7 @@ export default defineComponent({
 				if (currentTime === 0) {
 					// 坐标和小节都改为初始值
 					setTimeout(() => {
+						detailData.currentTime = 0
 						state.activeNoteIndex = 0
 						state.activeMeasureIndex = state.times[0].MeasureNumberXML;
 						handlePlaying(true);
@@ -165,7 +166,7 @@ export default defineComponent({
 			state.activeNoteIndex = item?.i || 0
 			// 一行谱,需要滚动小节
 			if (state.isSingleLine) {
-				moveSmoothAnimationByPlayTime(currentTime)
+				moveSmoothAnimationByPlayTime(currentTime, true)
 			}
 		
 		};

+ 6 - 0
src/page-instrument/view-detail/emptyMusic/index.tsx

@@ -18,6 +18,12 @@ export default defineComponent({
          HANDLE_WORK_ADD()
          // 不在APP中,
          if (!storeData.isApp) {
+            window.parent.postMessage(
+               {
+                  api: "back",
+               },
+               "*"
+            );
             window.close()
             return
          }

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

@@ -163,15 +163,16 @@ export function destroySmoothAnimation() {
 
 /**
  * 根据播放时间进度移动处理
+ * isIgnoreFilter  忽略这种 判断,因为有些只需要谱面移动
  */
-export function moveSmoothAnimationByPlayTime(time?: number) {
+export function moveSmoothAnimationByPlayTime(time?: number, isIgnoreFilter = false) {
    // 暂停之后不进行移动了
    if (state.playState === "paused") {
       return
    }
    const currentTime = time || getAudioCurrentTime()
    // 某些浏览器 音频暂停后返回的时间会倒退,把这种时间过滤掉
-   if(currentTime < smoothAnimationState.oldCurrentTime) {
+   if(currentTime < smoothAnimationState.oldCurrentTime && !isIgnoreFilter) {
       return
    }
    smoothAnimationState.oldCurrentTime = currentTime

+ 23 - 5
src/page-instrument/view-figner/change-subject/index.tsx

@@ -10,6 +10,10 @@ export default defineComponent({
       type: Array,
       default: () => [],
     },
+    changeSubjectShow: {
+      type: Boolean,
+      default: false,
+    },
     subject: {
       type: String,
       default: "",
@@ -71,9 +75,27 @@ export default defineComponent({
       }
     };
 
+    const onConfirm = () => {
+      if (state.selectList.length > 0 && !state.instrumentCode) {
+        showToast("请选择乐器");
+        return;
+      }
+      emit("confirm", state.instrumentCode || state.subjectValue);
+    };
+
     onMounted(() => {
       console.log(props.subjectList, "subjectList", props.subject, query);
       selectItem();
+      document.addEventListener("keydown", (e: KeyboardEvent) => {
+        if (e.code === "Tab") {
+          e.stopPropagation();
+          e.preventDefault();
+          // onStartPlayState();
+          if (props.changeSubjectShow) {
+            onConfirm();
+          }
+        }
+      });
     });
     return () => (
       <div class={[styles.changeSubject, query.platform === "pc" && styles.changeSubjectPc]}>
@@ -152,11 +174,7 @@ export default defineComponent({
             class={[styles.btn, styles.confirmBtn]}
             onClick={() => {
               console.log(state.selectList, state.instrumentCode);
-              if (state.selectList.length > 0 && !state.instrumentCode) {
-                showToast("请选择乐器");
-                return;
-              }
-              emit("confirm", state.instrumentCode || state.subjectValue);
+              onConfirm();
             }}
           ></div>
           {/* <Button

+ 4 - 0
src/page-instrument/view-figner/index.module.less

@@ -51,6 +51,10 @@
 
             &.tipHidden {
                 margin-right: -43%;
+
+                &>div {
+                    display: none;
+                }
             }
 
             .tipContentbox {

+ 27 - 0
src/page-instrument/view-figner/index.tsx

@@ -717,6 +717,17 @@ export default defineComponent({
             __init();
           }, 100);
         }
+      } else if (res.data.api === "startPlayState") {
+        onStartPlayState();
+      }
+    };
+
+    const onStartPlayState = () => {
+      if (!localStorage.getItem("fingerGuideKey")) return;
+      if (!(props.show && !data.loading && !data.loadingSoundFonts)) return;
+      if (data.changeSubjectShow) return;
+      if (data.fingeringMode === "fingeringMode" || data.fingeringMode === "listenMode") {
+        onActionPlay();
       }
     };
 
@@ -1032,6 +1043,21 @@ export default defineComponent({
       window.addEventListener("resize", onResize);
       const fingeringContainer = document.getElementById("fingeringContainer");
       fingeringContainer?.addEventListener("wheel", handleWheel);
+      document.addEventListener("keydown", (e: KeyboardEvent) => {
+        if (e.code === "Tab") {
+          e.stopPropagation();
+          e.preventDefault();
+          // onStartPlayState();
+          // 判断是否在应用中
+          window.parent.postMessage(
+            {
+              api: "documentBodyKeyup",
+              code: "Tab",
+            },
+            "*"
+          );
+        }
+      });
     });
 
     onUnmounted(() => {
@@ -2078,6 +2104,7 @@ export default defineComponent({
             }}
           >
             <ChangeSubject
+              changeSubjectShow={data.changeSubjectShow}
               subjectList={data.subjects}
               subject={data.subject}
               onClose={() => (data.changeSubjectShow = false)}

+ 6 - 6
src/state.ts

@@ -1397,6 +1397,9 @@ const getMusicInfo = async (res: any) => {
       workRecordInstrumentId = res.data?.instrumentId
     }
   }
+  // multiTracksSelection 返回为空,默认代表全部分轨
+  state.canSelectTracks = res.data.multiTracksSelection === "null" || res.data.multiTracksSelection === "" || res.data.multiTracksSelection === null ? [] : res.data.multiTracksSelection?.split(',');
+  state.canSelectTracks = state.canSelectTracks.map((item: any)=>item.trim())
   /* 获取声轨列表 */
   let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
   xmlString = xmlAddPartName(xmlString);
@@ -1466,7 +1469,6 @@ function initMusicSource(data: any, tracks: string[], partIndex: number, workRec
     if(state.isScoreRender && (partIndex===999 || (state.defaultScoreRender && partIndex===-1))){
         // 总谱渲染
         state.isCombineRender = true
-        state.partListNames = tracks
         banSongObj = musicSheetAccompanimentList.find((item: any) => {
           return item.audioPlayType === "SING"
         })
@@ -1486,8 +1488,8 @@ function initMusicSource(data: any, tracks: string[], partIndex: number, workRec
         index = 999
         musicalInstrumentId = ''
     }else{
-      // 合奏只显示一个声轨
-      track = tracks[partIndex===-1?0:partIndex]
+      // 合奏只显示一个声轨   当为-1时候,取tracks中 后端勾选了的第一个值
+      track =  partIndex === -1 ? tracks.find(value => state.canSelectTracks.includes(value))! : tracks[partIndex]
       // 根据当前的声轨 取数据
       musicObj = musicSheetSoundList.find((item: any) => {
         return item.audioPlayType === "PLAY" && item.track === track
@@ -1506,6 +1508,7 @@ function initMusicSource(data: any, tracks: string[], partIndex: number, workRec
       })
       musicalInstrumentId = musicObj?.musicalInstrumentId
     }
+    state.partListNames = tracks
   }
   // 当没有任何曲目的时候报错
   if (!musicObj?.audioFileUrl && !accompanyObj?.audioFileUrl && !fanSongObj?.audioFileUrl && !banSongObj?.audioFileUrl && !fanSongObj?.solmizationFileUrl && !fanSongObj?.femaleSolmizationFileUrl) {
@@ -1682,9 +1685,6 @@ const setState = (data: any, index: number) => {
     state.enableEvaluation = state.accompany || state.music ? true : false
   }
   state.isConcert = data.musicSheetType === "CONCERT" ? true : false;
-  // multiTracksSelection 返回为空,默认代表全部分轨
-  state.canSelectTracks = data.multiTracksSelection === "null" || data.multiTracksSelection === "" || data.multiTracksSelection === null ? [] : data.multiTracksSelection?.split(',');
-  state.canSelectTracks = state.canSelectTracks.map((item: any)=>item.trim())
   // 开启预备小节
   state.isOpenPrepare = true;
   state.extStyleConfigJson = data.extStyleConfigJson || {}

+ 15 - 18
src/utils/crunker.ts

@@ -3,7 +3,7 @@ interface CrunkerConstructorOptions {
    concurrentNetworkRequests: number
 }
 
-type CrunkerInputTypes = string | File | Blob
+type CrunkerInputTypes = string | File | Blob | undefined
 
 export default class Crunker {
    private readonly _sampleRate: number
@@ -16,15 +16,15 @@ export default class Crunker {
       this._sampleRate = sampleRate
       this._concurrentNetworkRequests = concurrentNetworkRequests
    }
-   private _createContext(sampleRate = 44_100): AudioContext {
+   private _createContext(sampleRate = 22050): AudioContext {
       window.AudioContext = window.AudioContext || (window as any).webkitAudioContext || (window as any).mozAudioContext
       return new AudioContext({ sampleRate })
    }
    /**
     *转换url等类型为buffer
     */
-   async fetchAudio(...filepaths: CrunkerInputTypes[]): Promise<AudioBuffer[]> {
-      const buffers: AudioBuffer[] = []
+   async fetchAudio(...filepaths: CrunkerInputTypes[]): Promise<(AudioBuffer | undefined)[]> {
+      const buffers: (AudioBuffer | undefined)[] = []
       const groups = Math.ceil(filepaths.length / this._concurrentNetworkRequests)
       for (let i = 0; i < groups; i++) {
          const group = filepaths.slice(i * this._concurrentNetworkRequests, (i + 1) * this._concurrentNetworkRequests)
@@ -32,9 +32,12 @@ export default class Crunker {
       }
       return buffers
    }
-   private async _fetchAudio(...filepaths: CrunkerInputTypes[]): Promise<AudioBuffer[]> {
+   private async _fetchAudio(...filepaths: CrunkerInputTypes[]): Promise<(AudioBuffer | undefined)[]> {
       return await Promise.all(
          filepaths.map(async filepath => {
+            if (!filepath) {
+               return Promise.resolve(undefined)
+            }
             let buffer: ArrayBuffer
             if (filepath instanceof File || filepath instanceof Blob) {
                buffer = await filepath.arrayBuffer()
@@ -74,24 +77,18 @@ export default class Crunker {
       }
       const output = this._context.createBuffer(this._maxNumberOfChannels(buffers), this._sampleRate * this._maxDuration(buffers), this._sampleRate)
       buffers.forEach((buffer, index) => {
-         for (let channelNumber = 0; channelNumber < buffer.numberOfChannels; channelNumber++) {
+         const offsetNum = Math.round(times[index] * this._sampleRate) //时间偏差
+         for (let channelNumber = 0; channelNumber < output.numberOfChannels; channelNumber++) {
             const outputData = output.getChannelData(channelNumber)
-            const bufferData = buffer.getChannelData(channelNumber)
-            const offsetNum = Math.round(times[index] * this._sampleRate) //时间偏差
-            for (let i = buffer.getChannelData(channelNumber).length - 1; i >= 0; i--) {
-               outputData[i + offsetNum] += bufferData[i]
+            // buffers 有可能是单声道,当单声道的时候 取第一个声道的值
+            const bufferData = buffer.getChannelData(buffer.numberOfChannels < 2 ? 0 : channelNumber)
+            for (let i = bufferData.length - 1; i >= 0; i--) {
                // 当合并大于1或者小于-1的时候可能会爆音  所以这里取最大值和最小值
-               if (outputData[i + offsetNum] > 1) {
-                  outputData[i + offsetNum] = 1
-               }
-               if (outputData[i + offsetNum] < -1) {
-                  outputData[i + offsetNum] = -1
-               }
+               const combinedValue = outputData[i + offsetNum] + bufferData[i]
+               outputData[i + offsetNum] = Math.max(-1, Math.min(1, combinedValue))
             }
-            output.getChannelData(channelNumber).set(outputData)
          }
       })
-
       return output
    }
    /**

+ 129 - 8
src/view/audio-list/index.tsx

@@ -14,6 +14,10 @@ import { evaluatingData } from "/src/view/evaluating";
 import { cloudToggleState } from "/src/helpers/midiPlay"
 import { storeData } from "/src/store";
 import { handleStartTick } from "../tick";
+import Crunker from "/src/utils/crunker"
+import tickMp3 from "/src/assets/tick.wav"
+import tockMp3 from "/src/assets/tock.wav"
+import { metronomeData } from "/src/helpers/metronome";
 
 export const audioData = reactive({
 	songEle: null as HTMLAudioElement | null, // 原生
@@ -91,12 +95,17 @@ export const getAudioCurrentTime = () => {
 		// const c = getMidiCurrentTime();
 		return audioData.progress;
 	}
-	// console.log('返回的时间',state.playSource, audioData.songEle?.currentTime,audioData.progress)
-	if (state.playSource === "music") return audioData.songEle?.currentTime || audioData.progress;
-	if (state.playSource === "background") return audioData.backgroundEle?.currentTime || audioData.progress;
-	if (state.playSource === "mingSong") return audioData.mingSongEle?.currentTime || audioData.progress;
-	
-	return audioData.songEle?.currentTime || audioData.progress;
+	if (state.modeType === 'evaluating') {
+		return audioData.progress;
+	} else {
+		// console.log('返回的时间',state.playSource, audioData.songEle?.currentTime,audioData.progress)
+		if (state.playSource === "music") return audioData.songEle?.currentTime || audioData.progress;
+		if (state.playSource === "background") return audioData.backgroundEle?.currentTime || audioData.progress;
+		if (state.playSource === "mingSong") return audioData.mingSongEle?.currentTime || audioData.progress;
+		
+		return audioData.songEle?.currentTime || audioData.progress;
+	}
+
 };
 /** 获取曲谱的总时间 */
 export const getAudioDuration = () => {
@@ -195,6 +204,118 @@ export const changeMingSongType = () =>{
 		audioData.songCollection.beatMingSongEle = mingSongType === 1 ? beatMingSongEle : beatMingSongGirlEle
 	}
 }
+
+// 处理加载节拍器音频
+let CrunkerInstance: Crunker
+export const handleLoadBeatMusic = async () => {
+	if(metronomeData.disable) {
+		return
+	}
+	const playType = state.playType
+	const playSource = state.playSource
+	const mingSongType = audioData.mingSongType
+	// 当前模式下 如果已经有合成音频了,就不走合成逻辑了
+	let isBeatMusic = false
+	let currentMusic  //当前的音频
+	const beatPlayObj = {
+		"play_music":"beatSongEle",
+		"play_background":"beatBackgroundEle",
+		"sing_music":"beatFanSongEle",
+		"sing_background":"beatBanSongEle"
+	}
+	const playObj = {
+		"play_music":"music",
+		"play_background":"accompany",
+		"sing_music":"fanSong",
+		"sing_background":"banSong",
+	}
+	if(playSource === "mingSong") {
+		// 当男声女声都有的时候 才区分
+		if(state.mingSong && state.mingSongGirl){
+			isBeatMusic = mingSongType === 1 ? !!audioData.mingSongTypeCollection.beatMingSongEle : !!audioData.mingSongTypeCollection.beatMingSongGirlEle
+			currentMusic = mingSongType === 1 ? state.mingSong : state.mingSongGirl
+		}else{
+			isBeatMusic = !!audioData.mingSongTypeCollection.beatMingSongEle
+			currentMusic = state.mingSong
+		}
+	}else{
+		// @ts-ignore
+		isBeatMusic = !!audioData.songCollection[beatPlayObj[`${playType}_${playSource}`]]
+		// @ts-ignore
+		currentMusic = state[playObj[`${playType}_${playSource}`]]
+	}
+	if(isBeatMusic || !currentMusic){
+		return
+	}
+	state.loadingText = "音频资源加载中,请稍后…"
+	state.isLoading = true
+	/* 音频合成 */
+	if(!CrunkerInstance){
+		CrunkerInstance = new Crunker()
+	}
+	console.time("音频加载时间")
+	const [audioBuffer, tickBuff, tockBuff] = await CrunkerInstance.fetchAudio(`${currentMusic}?v=${Date.now()}`, tickMp3, tockMp3)
+	console.timeEnd("音频加载时间")
+	// 计算音频空白时间
+	const silenceDuration = audioBuffer&&!state.isEvxml ? CrunkerInstance.calculateSilenceDuration(audioBuffer) : 0
+	console.log(`音频空白时间:${silenceDuration}`)
+	const beats:AudioBuffer[] = []
+    const beatsTime:number[] = []
+	metronomeData.metroMeasure.map(measures=>{
+		measures.map((item:any)=>{
+			beats.push(item.index===0?tickBuff!:tockBuff!)
+			beatsTime.push(item.time+silenceDuration) //不是妙极客的曲子才加上空白
+		})
+	})
+	console.time("音频合并时间")
+	const musicBuffMeg = audioBuffer && CrunkerInstance.mergeAudioBuffers([audioBuffer!,...beats],[0,...beatsTime])
+	console.timeEnd("音频合并时间")
+	console.time("音频audioDom生成时间")
+	const musicAudio = musicBuffMeg && CrunkerInstance.exportAudioElement(musicBuffMeg) as any
+	console.timeEnd("音频audioDom生成时间")
+	const playEleObj = {
+		"play_music":"beatSongEle",
+		"play_background":"beatBackgroundEle",
+		"sing_music":"beatFanSongEle",
+		"sing_background":"beatBanSongEle"
+	}
+	// 给音频赋值
+	if(playSource === "mingSong"){
+		// 当男声女声都有的时候 才区分
+		if(state.mingSong && state.mingSongGirl){
+			if(mingSongType === 1) {
+				audioData.mingSongTypeCollection.beatMingSongEle = musicAudio
+			}else {
+				audioData.mingSongTypeCollection.beatMingSongGirlEle = musicAudio
+			}
+		}else{
+			audioData.songCollection.beatMingSongEle = musicAudio
+			audioData.mingSongTypeCollection.beatMingSongEle = musicAudio
+		}
+		if(musicAudio){
+			musicAudio.addEventListener("play", onPlay);
+			musicAudio.addEventListener("ended", onEnded);
+		}
+		changeMingSongType()
+	}else{
+		if(playType === "play" && !audioData.songCollection.beatSongEle && !audioData.songCollection.beatBackgroundEle){
+			if(musicAudio){
+				musicAudio.addEventListener("play", onPlay);
+				musicAudio.addEventListener("ended", onEnded);
+			}
+		}		
+		if(playType === "sing" && !audioData.songCollection.beatFanSongEle && !audioData.songCollection.beatBanSongEle){
+			if(musicAudio){
+				musicAudio.addEventListener("play", onPlay);
+				musicAudio.addEventListener("ended", onEnded);
+			}
+		}
+		// @ts-ignore
+		audioData.songCollection[playEleObj[`${playType}_${playSource}`]] = musicAudio
+	}
+	state.isLoading = false
+}
+
 export default defineComponent({
 	name: "audio-list",
 	setup() {
@@ -378,7 +499,7 @@ export default defineComponent({
 					mingSongGirl.addEventListener("play", onPlay);
 					mingSongGirl.addEventListener("ended", onEnded);
 				}
-				// 处理带节拍器的音源
+				/* // 处理带节拍器的音源
 				const [beatMusic, beatAccompany, beatFanSong, beatBanSong, beatMingSong, beatMingSongGirl] = await loadBeatAudio()
 				Object.assign(audioData.songCollection, {
 					beatSongEle:beatMusic,
@@ -416,7 +537,7 @@ export default defineComponent({
 				if(beatMingSongGirl){
 					beatMingSongGirl.addEventListener("play", onPlay);
 					beatMingSongGirl.addEventListener("ended", onEnded);
-				}
+				}*/
 				// 给男声女声赋值
 				const userGender = storeData.user.gender
 				// 当不为null 和undefined的时候 取userGender的值

BIN
src/view/evaluating/icons/1.png


BIN
src/view/evaluating/icons/2.png


BIN
src/view/evaluating/icons/3.png


BIN
src/view/evaluating/icons/4.png


BIN
src/view/evaluating/icons/5.png


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

@@ -634,7 +634,6 @@ export const handleViewReport = (key: "recordId" | "recordIdStr", type: "gym" |
     statusBarTextColor: false,
     isOpenLight: true,
     c_orientation: 0,
-    showLoadingAnim: true
   });
 };
 

File diff suppressed because it is too large
+ 0 - 0
src/view/fingering/fingering-img/pan-flute/index.json


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

@@ -232,7 +232,7 @@ export default defineComponent({
 						})
 						// 获取stave里面vf-custom-bg的位置坐标,才是准确的坐标
 						// const currBgX = document.getElementById(currItem.stave.attrs.id)?.querySelector('.vf-custom-bg')?.getBoundingClientRect()?.x || 0;
-						const currBgX = currItem.stave?.attrs && currItem.stave.attrs.id ? document.getElementById(currItem.stave.attrs.id)?.querySelector('.vf-custom-bg')?.getBBox()?.x * state.zoom || 0 : 0;
+						const currBgX = currItem.stave?.attrs && currItem.stave.attrs.id ? document.getElementById(currItem.stave.attrs.id)?.querySelector('.vf-custom-bg')?.getBBox()?.x * state.zoom || 0 : 0;						
 						return currItem && {
 							left: currBgX ? currBgX + 'px' : currItem.staveBox.left,
 							top: currItem.staveBox.top,

+ 1 - 1
src/view/tick/index.tsx

@@ -133,7 +133,6 @@ export default defineComponent({
 			top: "0px",
 			left: "0px"
 		}
-		initPos()
 		function initPos() {
 			const musicAndSelectionDom = document.querySelector("#musicAndSelection")
 			const osmdSvgPage1Dom = musicAndSelectionDom?.querySelector("#osmdSvgPage1")
@@ -147,6 +146,7 @@ export default defineComponent({
 			})
 		}
 		onMounted(() => {
+			initPos()
 			Promise.all([createAudio(tickWav), createAudio(tockWav)]).then(
 				([tick, tock]) => {
 					if (tick) {

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


+ 1 - 1
vite.config.ts

@@ -77,7 +77,7 @@ export default defineConfig({
         // target: "https://test.lexiaoya.cn",
         // target: "https://kt.colexiu.com",
         target: "https://test.resource.colexiu.com", // 内容平台开发环境,内容平台开发,需在url链接上加上isCbs=true
-        // target: "https://test.kt.colexiu.com",
+        //target: "https://test.kt.colexiu.com",
         // target: "https://mec.colexiu.com",
         changeOrigin: true,
         rewrite: (path) => path.replace(/^\/instrument/, ""),

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