lex 1 ano atrás
pai
commit
a9d6469cca
72 arquivos alterados com 1360 adições e 626 exclusões
  1. 1 0
      dist/assets/auth-login-legacy.957c6f5b.js
  2. 1 0
      dist/assets/auth-login-legacy.f97b193f.js
  3. 1 0
      dist/assets/auth-login.43dbd94a.js
  4. 1 0
      dist/assets/auth-login.ea00d81f.js
  5. 1 0
      dist/assets/formatSvgToImg-legacy.79270df8.js
  6. 1 0
      dist/assets/formatSvgToImg-legacy.b0aca565.js
  7. 1 0
      dist/assets/formatSvgToImg.21a14c72.js
  8. 1 0
      dist/assets/formatSvgToImg.b8cefcee.js
  9. 1 0
      dist/assets/home-layout-center-legacy.41df48bf.js
  10. 4 0
      dist/assets/home-layout-center.822b1651.js
  11. 5 0
      dist/assets/home-layout-center.ee340582.js
  12. 1 0
      dist/assets/index-legacy.027ad059.js
  13. 1 0
      dist/assets/index-legacy.2b3cc9eb.js
  14. 0 0
      dist/assets/index-legacy.3c8595b3.js
  15. 1 0
      dist/assets/index-legacy.466d5aca.js
  16. 1 0
      dist/assets/index-legacy.49578f3a.js
  17. 1 0
      dist/assets/index-legacy.62a1eaaf.js
  18. 1 0
      dist/assets/index-legacy.648a47ed.js
  19. 1 0
      dist/assets/index-legacy.7e84f665.js
  20. 0 0
      dist/assets/index-legacy.7fe858ee.js
  21. 1 0
      dist/assets/index-legacy.865c17f9.js
  22. 1 0
      dist/assets/index.06892600.js
  23. 1 0
      dist/assets/index.307b09b4.js
  24. 0 0
      dist/assets/index.4b05faa4.js
  25. 1 0
      dist/assets/index.99a0d2d4.js
  26. 0 0
      dist/assets/index.a57e0aaa.js
  27. 0 0
      dist/assets/index.bce2836a.js
  28. 0 0
      dist/assets/index.e901838d.js
  29. 1 0
      dist/assets/index.e9e17070.js
  30. 1 0
      dist/assets/login-legacy.3189548a.js
  31. 1 0
      dist/assets/login-legacy.6830763f.js
  32. 1 0
      dist/assets/login.54707c74.js
  33. 1 0
      dist/assets/login.e36c5ead.js
  34. 0 0
      dist/assets/new-index-legacy.ca8b19b7.js
  35. 0 0
      dist/assets/new-index.bc94fa5f.js
  36. 0 0
      dist/assets/polyfills-legacy.f80d84d0.js
  37. 1 0
      dist/assets/routes-common-legacy.14738471.js
  38. 1 0
      dist/assets/routes-common-legacy.a9a11b1c.js
  39. 0 0
      dist/assets/routes-common.e99ae211.js
  40. 0 0
      dist/assets/teacher-legacy.d460f021.js
  41. 0 0
      dist/assets/teacher.caf8a15d.js
  42. 0 0
      dist/assets/tenant-legacy.9f48ffcc.js
  43. 0 0
      dist/assets/tenant.0557f03c.js
  44. 16 0
      dist/index.html
  45. 3 3
      dist/osmd/index.html
  46. 0 0
      dist/osmd/opensheetmusicdisplay.min.js
  47. 14 0
      dist/teacher.html
  48. 11 0
      dist/tenant.html
  49. 3 3
      public/osmd/index.html
  50. 0 0
      public/osmd/opensheetmusicdisplay.min.js
  51. 17 7
      src/business-components/user-detail/index.tsx
  52. 1 1
      src/components/col-field/index.module.less
  53. 10 1
      src/components/col-video/index.tsx
  54. 90 132
      src/constant/instruments.ts
  55. 1 1
      src/helpers/native-message.ts
  56. 53 3
      src/student/video-class/video-class-detail.tsx
  57. 57 4
      src/student/video-class/video-detail.tsx
  58. 25 15
      src/student/video-class/video-item.tsx
  59. 118 85
      src/teacher/video-class/class-info.module.less
  60. 66 27
      src/teacher/video-class/class-info.tsx
  61. 3 0
      src/teacher/video-class/create-submit.tsx
  62. 1 0
      src/teacher/video-class/createState.tsx
  63. 7 3
      src/teacher/video-class/video-class-detail.tsx
  64. 1 0
      src/teacher/video-class/video-detail.tsx
  65. 6 0
      src/tenant/music/music-detail/StringUtil.ts
  66. 2 2
      src/tenant/music/music-detail/formatSvgToImg.ts
  67. 352 0
      src/tenant/music/music-detail/instrument.ts
  68. 114 80
      src/tenant/music/music-detail/new-index.tsx
  69. 88 0
      src/tenant/music/music-detail/speed-tag.ts
  70. 6 0
      src/tenant/music/train-tool/index.tsx
  71. 258 258
      src/views/video-class/index.tsx
  72. 1 1
      vite.config.ts

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/auth-login-legacy.957c6f5b.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/auth-login-legacy.f97b193f.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/auth-login.43dbd94a.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/auth-login.ea00d81f.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/formatSvgToImg-legacy.79270df8.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/formatSvgToImg-legacy.b0aca565.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/formatSvgToImg.21a14c72.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/formatSvgToImg.b8cefcee.js


+ 1 - 0
dist/assets/home-layout-center-legacy.41df48bf.js

@@ -0,0 +1 @@
+!function(){function t(t,e,n,r,o,s,a){try{var u=t[s](a),c=u.value}catch(i){return void n(i)}u.done?e(c):Promise.resolve(c).then(r,o)}System.register(["./index-legacy.2562bdba.js","./index-legacy.62a1eaaf.js","./auth-legacy.291d9f1d.js","./routes-common-legacy.a9a11b1c.js"],(function(e){"use strict";var n,r,o,s,a,u,c,i,f,l;return{setters:[function(t){n=t.d,r=t.g,o=t.t,s=t.p,a=t.i,u=t.j,c=t.C,i=t.F},function(t){f=t.a,l=t.b},function(){},function(){}],execute:function(){e("default",n({name:"home-layout-center",data:function(){return{status:!1}},mounted:function(){this.getUserInfo()},methods:{getUserInfo:function(){var e,n=this;return(e=regeneratorRuntime.mark((function t(){var e,u,c;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,t.next=3,f.post("/api-auth/smsLogin",{requestType:"form",data:{clientId:"student",clientSecret:"student",phone:r.orchestraInfo.phone,token:r.orchestraInfo.token,isSurportRegister:!0}});case 3:return e=t.sent,u=e.data.authentication,o(u.token_type+" "+u.access_token),s({api:"setCache",content:{key:"h5-colexiu-token",value:u.token_type+" "+u.access_token}}),t.next=9,f.get("/api-student/student/queryUserInfo",{initRequest:!0});case 9:c=t.sent,a(c.data),n.$router.push("/home"),t.next=17;break;case 14:t.prev=14,t.t0=t.catch(0),n.status=!0;case 17:case"end":return t.stop()}}),t,null,[[0,14]])})),function(){var n=this,r=arguments;return new Promise((function(o,s){var a=e.apply(n,r);function u(e){t(a,o,s,u,c,"next",e)}function c(e){t(a,o,s,u,c,"throw",e)}u(void 0)}))})()}},render:function(){return u(i,null,[this.status?u("div",{class:l.error},[u(c,{type:"notFond",classImgSize:"CERT",tips:"加载失败,请稍后重试",buttonText:"重新加载",plain:!0,onClick:this.getUserInfo},null)]):u("div",null,null)])}}))}}}))}();

+ 4 - 0
dist/assets/home-layout-center.822b1651.js

@@ -1 +1,5 @@
+<<<<<<<< HEAD:dist/assets/home-layout-center.822b1651.js
 import{d as r,g as s,t as u,p as i,i as c,j as e,C as l,F as p}from"./index.13d115ab.js";import{a,b as h}from"./index.17edd043.js";import"./auth.a1b2dd90.js";import"./routes-common.ad39f4c8.js";var g=r({name:"home-layout-center",data(){return{status:!1}},mounted(){this.getUserInfo()},methods:{async getUserInfo(){try{const o=await a.post("/api-auth/smsLogin",{requestType:"form",data:{clientId:"student",clientSecret:"student",phone:s.orchestraInfo.phone,token:s.orchestraInfo.token,isSurportRegister:!0}}),{authentication:t}=o.data;u(t.token_type+" "+t.access_token),i({api:"setCache",content:{key:"h5-colexiu-token",value:t.token_type+" "+t.access_token}});const n=await a.get("/api-student/student/queryUserInfo",{initRequest:!0});c(n.data),this.$router.push("/home")}catch{this.status=!0}}},render(){return e(p,null,[this.status?e("div",{class:h.error},[e(l,{type:"notFond",classImgSize:"CERT",tips:"\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",buttonText:"\u91CD\u65B0\u52A0\u8F7D",plain:!0,onClick:this.getUserInfo},null)]):e("div",null,null)])}});export{g as default};
+========
+import{d as r,g as s,t as u,p as i,i as c,j as e,C as l,F as p}from"./index.3bfa6a0e.js";import{a,b as h}from"./index.e901838d.js";import"./auth.37c81a49.js";import"./routes-common.e99ae211.js";var g=r({name:"home-layout-center",data(){return{status:!1}},mounted(){this.getUserInfo()},methods:{async getUserInfo(){try{const o=await a.post("/api-auth/smsLogin",{requestType:"form",data:{clientId:"student",clientSecret:"student",phone:s.orchestraInfo.phone,token:s.orchestraInfo.token,isSurportRegister:!0}}),{authentication:t}=o.data;u(t.token_type+" "+t.access_token),i({api:"setCache",content:{key:"h5-colexiu-token",value:t.token_type+" "+t.access_token}});const n=await a.get("/api-student/student/queryUserInfo",{initRequest:!0});c(n.data),this.$router.push("/home")}catch{this.status=!0}}},render(){return e(p,null,[this.status?e("div",{class:h.error},[e(l,{type:"notFond",classImgSize:"CERT",tips:"\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",buttonText:"\u91CD\u65B0\u52A0\u8F7D",plain:!0,onClick:this.getUserInfo},null)]):e("div",null,null)])}});export{g as default};
+>>>>>>>> iteration-20240624:dist/assets/home-layout-center.ee340582.js

+ 5 - 0
dist/assets/home-layout-center.ee340582.js

@@ -0,0 +1,5 @@
+<<<<<<<< HEAD:dist/assets/home-layout-center.822b1651.js
+import{d as r,g as s,t as u,p as i,i as c,j as e,C as l,F as p}from"./index.13d115ab.js";import{a,b as h}from"./index.17edd043.js";import"./auth.a1b2dd90.js";import"./routes-common.ad39f4c8.js";var g=r({name:"home-layout-center",data(){return{status:!1}},mounted(){this.getUserInfo()},methods:{async getUserInfo(){try{const o=await a.post("/api-auth/smsLogin",{requestType:"form",data:{clientId:"student",clientSecret:"student",phone:s.orchestraInfo.phone,token:s.orchestraInfo.token,isSurportRegister:!0}}),{authentication:t}=o.data;u(t.token_type+" "+t.access_token),i({api:"setCache",content:{key:"h5-colexiu-token",value:t.token_type+" "+t.access_token}});const n=await a.get("/api-student/student/queryUserInfo",{initRequest:!0});c(n.data),this.$router.push("/home")}catch{this.status=!0}}},render(){return e(p,null,[this.status?e("div",{class:h.error},[e(l,{type:"notFond",classImgSize:"CERT",tips:"\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",buttonText:"\u91CD\u65B0\u52A0\u8F7D",plain:!0,onClick:this.getUserInfo},null)]):e("div",null,null)])}});export{g as default};
+========
+import{d as r,g as s,t as u,p as i,i as c,j as e,C as l,F as p}from"./index.3bfa6a0e.js";import{a,b as h}from"./index.e901838d.js";import"./auth.37c81a49.js";import"./routes-common.e99ae211.js";var g=r({name:"home-layout-center",data(){return{status:!1}},mounted(){this.getUserInfo()},methods:{async getUserInfo(){try{const o=await a.post("/api-auth/smsLogin",{requestType:"form",data:{clientId:"student",clientSecret:"student",phone:s.orchestraInfo.phone,token:s.orchestraInfo.token,isSurportRegister:!0}}),{authentication:t}=o.data;u(t.token_type+" "+t.access_token),i({api:"setCache",content:{key:"h5-colexiu-token",value:t.token_type+" "+t.access_token}});const n=await a.get("/api-student/student/queryUserInfo",{initRequest:!0});c(n.data),this.$router.push("/home")}catch{this.status=!0}}},render(){return e(p,null,[this.status?e("div",{class:h.error},[e(l,{type:"notFond",classImgSize:"CERT",tips:"\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",buttonText:"\u91CD\u65B0\u52A0\u8F7D",plain:!0,onClick:this.getUserInfo},null)]):e("div",null,null)])}});export{g as default};
+>>>>>>>> iteration-20240624:dist/assets/home-layout-center.ee340582.js

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.027ad059.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.2b3cc9eb.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/index-legacy.3c8595b3.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.466d5aca.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.49578f3a.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.62a1eaaf.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.648a47ed.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.7e84f665.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/index-legacy.7fe858ee.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index-legacy.865c17f9.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index.06892600.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index.307b09b4.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/index.4b05faa4.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index.99a0d2d4.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/index.a57e0aaa.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/index.bce2836a.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/index.e901838d.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/index.e9e17070.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/login-legacy.3189548a.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/login-legacy.6830763f.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/login.54707c74.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/login.e36c5ead.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/new-index-legacy.ca8b19b7.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/new-index.bc94fa5f.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/polyfills-legacy.f80d84d0.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/routes-common-legacy.14738471.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
dist/assets/routes-common-legacy.a9a11b1c.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/routes-common.e99ae211.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/teacher-legacy.d460f021.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/teacher.caf8a15d.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/tenant-legacy.9f48ffcc.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/assets/tenant.0557f03c.js


+ 16 - 0
dist/index.html

@@ -97,6 +97,17 @@
             _T_));
     })(Object);
   </script>
+<<<<<<< HEAD
+=======
+  <script type="module" crossorigin src="./assets/index.e901838d.js"></script>
+  <link rel="modulepreload" href="./assets/index.3bfa6a0e.js">
+  <link rel="modulepreload" href="./assets/auth.37c81a49.js">
+  <link rel="modulepreload" href="./assets/routes-common.e99ae211.js">
+  <link rel="stylesheet" href="./assets/index.d06f6378.css">
+  <link rel="stylesheet" href="./assets/auth.f09b6d78.css">
+  <link rel="stylesheet" href="./assets/index.820e7d12.css">
+  <script type="module">!function(){try{new Function("m","return import(m)")}catch(o){console.warn("vite: loading legacy build because dynamic import is unsupported, syntax error above 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>
+>>>>>>> iteration-20240624
 </head>
 
 <body>
@@ -104,8 +115,13 @@
   
   <!-- <script type="module" src="/src/teacher/main.ts"></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>
+<<<<<<< HEAD
   <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.e2059479.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/index-legacy.648a47ed.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.f80d84d0.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/index-legacy.62a1eaaf.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration-20240624
 </body>
 
 </html>

+ 3 - 3
dist/osmd/index.html

@@ -19,9 +19,9 @@
       overflow: hidden;
     }
 
-    .vf-text {
+    /* .vf-text {
       display: none;
-    }
+    } */
 
     #cursorImg-0 {
       display: none;
@@ -156,4 +156,4 @@
   </script>
 </body>
 
-</html>
+</html>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/osmd/opensheetmusicdisplay.min.js


+ 14 - 0
dist/teacher.html

@@ -94,14 +94,28 @@
             _T_));
     })(Object);
   </script>
+<<<<<<< HEAD
+=======
+  <script type="module" crossorigin src="./assets/teacher.caf8a15d.js"></script>
+  <link rel="modulepreload" href="./assets/index.3bfa6a0e.js">
+  <link rel="modulepreload" href="./assets/routes-common.e99ae211.js">
+  <link rel="stylesheet" href="./assets/index.d06f6378.css">
+  <link rel="stylesheet" href="./assets/teacher.ded4b243.css">
+  <script type="module">!function(){try{new Function("m","return import(m)")}catch(o){console.warn("vite: loading legacy build because dynamic import is unsupported, syntax error above 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>
+>>>>>>> iteration-20240624
 </head>
 
 <body>
   <div id="app"></div>
   
   <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>
+<<<<<<< HEAD
   <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.e2059479.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.48b0500d.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.f80d84d0.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.d460f021.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration-20240624
 </body>
 
 </html>

+ 11 - 0
dist/tenant.html

@@ -52,9 +52,15 @@
             _T_));
     })(Object);
   </script>
+<<<<<<< HEAD
   <script type="module" crossorigin src="./assets/tenant.834afce0.js"></script>
   <link rel="modulepreload" href="./assets/index.13d115ab.js">
   <link rel="modulepreload" href="./assets/auth.a1b2dd90.js">
+=======
+  <script type="module" crossorigin src="./assets/tenant.0557f03c.js"></script>
+  <link rel="modulepreload" href="./assets/index.3bfa6a0e.js">
+  <link rel="modulepreload" href="./assets/auth.37c81a49.js">
+>>>>>>> iteration-20240624
   <link rel="stylesheet" href="./assets/index.d06f6378.css">
   <link rel="stylesheet" href="./assets/auth.f09b6d78.css">
   <link rel="stylesheet" href="./assets/tenant.d44cc3f0.css">
@@ -66,8 +72,13 @@
   
   <!-- <script type="module" src="/src/teacher/main.ts"></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>
+<<<<<<< HEAD
   <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.e2059479.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/tenant-legacy.fc1ba89b.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.f80d84d0.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/tenant-legacy.9f48ffcc.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration-20240624
 </body>
 
 </html>

+ 3 - 3
public/osmd/index.html

@@ -19,9 +19,9 @@
       overflow: hidden;
     }
 
-    .vf-text {
+    /* .vf-text {
       display: none;
-    }
+    } */
 
     #cursorImg-0 {
       display: none;
@@ -156,4 +156,4 @@
   </script>
 </body>
 
-</html>
+</html>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
public/osmd/opensheetmusicdisplay.min.js


+ 17 - 7
src/business-components/user-detail/index.tsx

@@ -13,6 +13,7 @@ import IconJiaozi from '@common/images/icon-jiaozi.png'
  * @param {type} username 姓名
  * @param {type} startTime 开始时间
  * @param {type} buyNum 购买用户数
+ * @param {type} payType 收费方式
  * @param {type} lessonPrice 价格
  * @param {type} lessonCoverUrl 视频封面
  * @param {type} lessonDesc 课程描述
@@ -26,6 +27,7 @@ interface UserType {
   startTime?: string
   id?: number
   buyNum?: number
+  payType?: string
   lessonPrice: number
   lessonNum?: number
   lessonDesc?: string
@@ -129,19 +131,27 @@ export default defineComponent({
               value: () => (
                 <div class={styles.info}>
                   {/* 0元不显示,为了处理ios审核问题 */}
-                  {this.userInfo.lessonPrice > 0 && (
-                    <>¥{this.userInfo.lessonPrice}</>
+                  {this.userInfo.payType === 'VIP' ? (
+                    <span style={{ color: '#C76E21' }}>会员</span>
+                  ) : (
+                    <>
+                      {this.userInfo.lessonPrice > 0 && (
+                        <>¥{this.userInfo.lessonPrice}</>
+                      )}
+                      {this.userInfo.lessonPrice <= 0 &&
+                        this.userInfo.auditVersion !== 0 && <>¥{0}</>}
+                      {this.userInfo.lessonPrice <= 0 &&
+                        this.userInfo.auditVersion === 0 && (
+                          <span style={{ color: '#20BEA0' }}>免费</span>
+                        )}
+                    </>
                   )}
-                  {this.userInfo.lessonPrice <= 0 &&
-                    this.userInfo.auditVersion !== 0 && <>¥{0}</>}
-                  {this.userInfo.lessonPrice <= 0 &&
-                    this.userInfo.auditVersion === 0 && <>免费</>}
                   <span
                     style={{ color: '#999', fontSize: '14px', fontWeight: 400 }}
                   >
                     /{this.userInfo.lessonNum}课时
                   </span>
-                  {this.showBuy && (
+                  {this.showBuy && this.userInfo.payType === 'PAY' && (
                     <div class={styles.buyNum}>
                       {this.userInfo.buyNum}人已
                       {this.userInfo.lessonPrice <= 0 &&

+ 1 - 1
src/components/col-field/index.module.less

@@ -1,7 +1,7 @@
 .formTitle {
   font-size: 16px;
   color: #000;
-  display: flex;
+  display: flex !important;
   align-items: center;
   justify-content: space-between;
 

+ 10 - 1
src/components/col-video/index.tsx

@@ -20,6 +20,11 @@ export default defineComponent({
       type: Boolean,
       default: false
     },
+    // 收费方式
+    payType: {
+      type: String,
+      default: ''
+    },
     freeTitleStatus: {
       type: Boolean,
       default: true
@@ -382,7 +387,7 @@ export default defineComponent({
         <video
           ref="video"
           class={styles['video']}
-          src={this.src}
+          // src={this.src}
           id={this.videoID}
           playsinline={this.playsinline}
           poster={this.poster}
@@ -435,6 +440,8 @@ export default defineComponent({
                   <p class={styles.tips}>
                     {this.freeTitleStatus
                       ? '免费试看结束,购买完整课程后继续学习'
+                      : this.payType === 'VIP'
+                      ? '免费试看结束,开通会员后继续学习'
                       : '试看结束,领取课程后继续学习'}
                   </p>
                 ) : (
@@ -453,6 +460,8 @@ export default defineComponent({
                     {state.platformType === 'STUDENT'
                       ? this.freeTitleStatus
                         ? '立即购买'
+                        : this.payType === 'VIP'
+                        ? '开通会员'
                         : '免费领取'
                       : '返回免费'}
                   </Button>

+ 90 - 132
src/constant/instruments.ts

@@ -4,23 +4,22 @@ const instruments = {
   'Electric Grand Piano': '电钢琴',
   'Rhodes Piano': '柔和的电钢琴',
   'Chorused Piano': '加合唱效果的电钢琴',
-  Harpsichord: '羽管键琴',
-  Clavichord: '科拉维科特琴',
-  Celesta: '钢片琴',
-  Glockenspiel: '钢片琴',
+  'Harpsichord': '羽管键琴',
+  'Clavichord': '科拉维科特琴',
+  'Celesta': '钢片琴',
   'Music box': '八音盒',
-  Vibraphone: '颤音琴',
-  Marimba: '马林巴',
-  Xylophone: '木琴',
+  'Vibraphone': '颤音琴',
+  'Marimba': '马林巴',
+  'Xylophone': '木琴',
   'Tubular Bells': '管钟',
-  Dulcimer: '大扬琴',
+  'Dulcimer': '大扬琴',
   'Hammond Organ': '击杆风琴',
   'Percussive Organ': '打击式风琴',
   'Rock Organ': '摇滚风琴',
   'Church Organ': '教堂风琴',
   'Reed Organ': '簧管风琴',
-  Accordian: '手风琴',
-  Harmonica: '口琴',
+  'Accordian': '手风琴',
+  'Harmonica': '口琴',
   'Tango Accordian': '探戈手风琴',
   'Acoustic Guitar': '钢弦吉他',
   'Electric Guitar': '闷音电吉他',
@@ -32,182 +31,141 @@ const instruments = {
   'Fretless Bass': '无品贝司',
   'Slap Bass': '掌击',
   'Synth Bass': '电子合成',
-  Violin: '小提琴',
-  Viola: '中提琴',
-  Cello: '大提琴',
-  Contrabass: '低音大提琴',
+  'Violin': '小提琴',
+  'Viola': '中提琴',
+  'Cello': '大提琴',
+  'Contrabass': '低音大提琴',
   'Tremolo Strings': '弦乐群颤音音色',
   'Pizzicato Strings': '弦乐群拨弦音色',
   'Orchestral Harp': '竖琴',
-  Timpani: '定音鼓',
+  'Timpani': '定音鼓',
   'String Ensemble': '弦乐合奏音色',
   'Synth Strings': '合成弦乐合奏音色',
   'Choir Aahs': '人声合唱',
   'Voice Oohs': '人声',
   'Synth Voice': '合成人声',
   'Orchestra Hit': '管弦乐敲击齐奏',
-  Trumpet: '小号',
-  Trombone: '长号',
-  Tuba: '大号',
+  'Trumpet': '小号',
+  'Trombone': '长号',
+  'Tuba': '大号',
   'Muted Trumpet': '加弱音器小号',
   'French Horn': '法国号',
   'Brass Section': '铜管组',
   'Synth Brass': '合成铜管音色',
-  'Soprano Sax': '高音萨克斯',
-  'Alto Sax': '中音萨克斯',
-  'Tenor Sax': '次中音萨克斯',
-  'Baritone Sax': '低音萨克斯',
-  Oboe: '双簧管',
+  'Soprano Sax': '高音萨克斯',
+  'Alto Sax': '中音萨克斯',
+  'Tenor Sax': '次中音萨克斯',
+  'Baritone Sax': '低音萨克斯',
+  'Oboe': '双簧管',
   'English Horn': '英国管',
-  Bassoon: '巴松',
-  Clarinet: '单簧管',
-  'Soprano Saxophone': '高音萨克斯管',
-  'Alto Saxophone': '中音萨克斯管',
-  'Tenor Saxophone': '次中音萨克斯管',
-  'Baritone Saxophone': '低音萨克斯管',
-  Piccolo: '短笛',
-  Flute: '长笛',
-  Recorder: '竖笛',
-  'Soprano Recorder': '高音竖笛',
+  'Bassoon': '巴松',
+  'Clarinet': '单簧管',
+  'Piccolo': '短笛',
+  'Flute': '长笛',
+  'Recorder': '竖笛',
   'Pan Flute': '排箫',
   'Bottle Blow': '瓶木管',
-  Whistle: '口哨声',
-  Ocarina: '陶笛',
-  Lead: '合成主音',
+  'Whistle': '口哨声',
+  'Ocarina': '奥卡雷那',
+  'Lead': '合成主音',
   'Lead lead': '合成主音',
   'Pad age': '合成音色',
-  Pad: '合成音色',
-  FX: '合成效果  科幻',
-  Sitar: '西塔尔',
-  Banjo: '班卓琴',
-  Shamisen: '三昧线',
-  Koto: '十三弦筝',
-  Kalimba: '卡林巴',
-  Bagpipe: '风笛',
-  Fiddle: '民族提琴',
-  Shanai: '山奈',
+  'Pad': '合成音色',
+  'FX': '合成效果  科幻',
+  'Sitar': '西塔尔',
+  'Banjo': '班卓琴',
+  'Shamisen': '三昧线',
+  'Koto': '十三弦筝',
+  'Kalimba': '卡林巴',
+  'Bagpipe': '风笛',
+  'Fiddle': '民族提琴',
+  'Shanai': '山奈',
   'Tinkle Bell': '叮当铃',
-  Agogos: '阿戈戈铃',
+  'Agogo': '阿哥哥铃',
   'Steel Drums': '钢鼓',
   'Taiko Drum': '太鼓',
-  'Melodic Toms': '嗵嗵鼓',
-  'Synth Drums': '合成鼓',
-  'Reverse Cymbals': '反向镲',
-  'Agogo Bells': '阿戈戈铃',
-  'Taiko Drums': '太鼓',
-  Bongos: '邦戈鼓',
-  'Bongo Bell': '邦戈铃',
-  Congas: '康加鼓',
-  Guiro: '刮壶',
+  'Melodic Tom': '通通鼓',
+  'Synth Drum': '合成鼓',
+  'Reverse Cymbal': '铜钹',
   'Guitar Fret Noise': '吉他换把杂音',
   'Breath Noise': '呼吸声',
-  Seashore: '海浪声',
+  'Seashore': '海浪声',
   'Bird Tweet': '鸟鸣',
   'Telephone Ring': '电话铃',
-  Helicopter: '直升机',
-  Applause: '鼓掌声',
-  Gunshot: '枪声',
+  'Helicopter': '直升机',
+  'Applause': '鼓掌声',
+  'Gunshot': '枪声',
   'Acoustic Bass Drum': '大鼓',
   'Bass Drum': '大鼓',
-  'Side Drum': '小鼓鼓边',
+  'Side Stick': '小鼓鼓边',
   'Acoustic Snare': '小鼓',
-  'Hand Claps': '拍手',
+  'Hand Clap': '拍手',
   'Electric Snare': '小鼓',
-  'Low Floor Tom': '低音鼓',
-  'Closed Hi-Hat': '闭合踩镲',
-  'High Floor Tom': '高音落地嗵鼓',
-  'Pedal Hi-Hat': '脚踏踩镲',
-  'Low Tom': '低音嗵鼓',
-  'Open Hi-Hat': '开音踩镲',
-  'Low-Mid Tom': '中鼓',
+  'Low Floor Tom': '低音鼓',
+  'Closed Hi-Hat': '闭合',
+  'High Floor Tom': '低音鼓',
+  'Pedal Hi-Hat': '脚踏',
+  'Low Tom': '中音鼓',
+  'Open Hi-Hat': '强音钹',
+  'Low-Mid Tom': '中音鼓',
   'Hi Mid Tom': '高音鼓',
-  'Crash Cymbals': '对镲',
-  'High Tom': '高音嗵鼓',
-  'Ride Cymbals': '叮叮镲',
-  'Chinese Cymbals': '中国镲',
-  'Ride Bell': '圆铃',
-  Tambourine: '铃鼓',
-  'Splash Cymbal': '溅音镲',
-  Cowbell: '牛铃',
   'Crash Cymbal': '强音钹',
-  'Vibra-Slap': '颤音器',
+  'High Tom': '高音鼓',
   'Ride Cymbal': '打点钹',
-  'Hi Bongo': '高音邦戈鼓',
-  'Low Bongo': '低音邦戈鼓',
+  'Chinese Cymbal': '钹',
+  'Ride Bell': '圆铃',
+  'Tambourine': '铃鼓',
+  'Splash Cymbal': '小钹铜钹',
+  'Cowbell': '牛铃',
+  'Vibra-Slap': '颤音器',
+  'Hi Bongo': '高音邦加鼓',
+  'Low Bongo': '低音邦加鼓',
   'Mute Hi Conga': '弱音高音康加鼓',
   'Open Hi Conga': '强音高音康加鼓',
   'Low Conga': '低音康加鼓',
   'High Timbale': '高音天巴鼓',
   'Low Timbale': '低音天巴鼓',
-  'High Agogo': '高音阿戈戈铃',
-  'Low Agogo': '低音阿戈戈铃',
-  Cabasa: '卡巴萨',
-  Maracas: '沙锤',
+  'High Agogo': '高音阿哥哥',
+  'Low Agogo': '低音阿哥哥',
+  'Cabasa': '串珠',
+  'Maracas': '沙铃',
   'Short Whistle': '短口哨',
   'Long Whistle': '长口哨',
   'Short Guiro': '短刮壶',
   'Long Guiro': '长刮壶',
-  Claves: '响棒',
-  'Hi Wood Block': '高音木',
-  'Low Wood Block': '低音木',
+  'Claves': '梆子',
+  'Hi Wood Block': '高音木',
+  'Low Wood Block': '低音木',
   'Mute Triangle': '弱音三角铁',
   'Open Triangle': '强音三角铁',
-  'Drum Set': '架子鼓',
   'Hulusi flute': '葫芦丝',
-  Melodica: '口风琴',
+  'Melodica': '口风琴',
   'Snare Drum': '小军鼓',
-  Cymbal: '镲',
-  Cymbals: '镲',
+  'Cymbal': '镲',
+  'Cymbals': '镲',
   'Horn in F': '圆号',
-  Triangle: '三角铁',
-  Vibrato: '颤音琴',
-  'Suspend Cymbals': '吊镲',
-  'Suspended Cymbals': '吊镲',
+  'Triangle': '三角铁',
+  'Vibrato': '颤音琴',
+  'Suspend Cymbal': '吊镲',
   'Tom-Toms': '嗵嗵鼓',
-  Bell: '铃铛',
-  Bells: '铃铛',
+  'Bell': '铃铛',
+  'Bells': '铃铛',
   'Alto Clarinet': '中音单簧管',
-  'Bass Clarinet': '低音单簧管',
-  Cornet: '短号',
-  Euphonium: '上低音号',
+  'Soprano Saxophone': '高音萨克斯风',
+  'Alto Saxophone': '中音萨克斯风',
+  'Tenor Saxophone': '次中音萨克斯风',
+  'Baritone Saxophone': '低音萨克斯风',
+  'Cornet': '短号',
+  'Euphonium': '上低音号',
   'crash cymbals': '对镲',
-  Castanets: '响板',
-  Shaker: '沙锤',
-  'Mark tree': '音树',
-  Chimes: '管钟',
-  'Mark Tree': '音树',
-  'Tom-toms': '嗵嗵鼓',
-  'Hi-Hat': '踩镲',
-  'Sleigh Bells': '雪橇铃',
-  Flexatone: '弹音器',
-  'Brake drum': '闸鼓',
-  Gong: '锣',
-  'concert tom': '音乐会嗵嗵鼓',
-  'brake drum': '车轮鼓',
-  'finger cymbal': '指钹',
-  'ride cymbal': '叮叮镲',
-  'Concert Toms': '音乐会嗵嗵鼓',
-  Vibraslap: '弹音器',
-  'Wood Blocks': '木鱼',
-  'Temple Blocks': '木鱼',
-  'Wood Block': '木鱼',
-  'Field Drum': '军鼓',
-  'Quad-Toms': '筒鼓',
-  Quads: '筒鼓',
-  'Drums set': '架子鼓',
-  'High Bongo': '邦戈',
-  Timbales: '天巴鼓',
-  'rain stick': '雨棒',
-  'String Bass': '弦乐低音',
-  'Floor Tom': '侧嗵鼓',
-  'Brake Drum': '闸鼓',
-  'Tam-tam': '大锣'
+  'Suspend cymbals': '吊镲',
+  'Castanets': '响板'
 }
 
 /**
  * 获取乐器名称
  * @param instrumentName 乐器code
- * @returns 
+ * @returns
  */
 export const getInstrumentName = (instrumentName: string) => {
   const _instrumentName = instrumentName.replace(/ /g, ' ').toLocaleLowerCase()

+ 1 - 1
src/helpers/native-message.ts

@@ -77,7 +77,7 @@ if (browserInfo.isApp) {
 }
 
 // 判断是否是管乐团学生端的app
-console.log(browserInfo.isOrchestraStudent)
+// console.log(browserInfo.isOrchestraStudent)
 let instance: any
 if (browserInfo.isOrchestraStudent) {
   instance = (window as any).ORCHESTRA || (window as any).webkit?.messageHandlers?.ORCHESTRA

+ 53 - 3
src/student/video-class/video-class-detail.tsx

@@ -17,25 +17,30 @@ import {
   Toast,
   Dialog
 } from 'vant'
+import { postMessage } from '@/helpers/native-message'
 import { defineComponent } from 'vue'
 import styles from './video-class-detail.module.less'
-import { state } from '@/state'
+import { setLogin, state } from '@/state'
 
 import iconTeacher from '@common/images/icon_teacher.png'
 import ColResult from '@/components/col-result'
 import dayjs from 'dayjs'
 import { onSubmitZero, orderStatus } from '@/views/order-detail/orderStatus'
+import { browser } from '@/helpers/utils'
+import { usePageVisibility } from '@vant/use'
 
 export default defineComponent({
   name: 'VideoClassDetail',
   data() {
     const query = this.$route.query
     return {
+      pageVisibility: 'visible' as any,
       groupId: query.groupId,
       classId: query.classId,
       tabIndex: 1,
       title: '',
       lessonPrice: 0,
+      payType: '',
       useRelationType: '',
       alreadyBuy: false,
       videoDetail: {} as any,
@@ -71,6 +76,7 @@ export default defineComponent({
     }
   },
   async mounted() {
+    this.pageVisibility = usePageVisibility()
     // 处理视频显示
     const width = document.body.clientWidth || document.body.offsetWidth
     this.videoHeight = (width / 16) * 9 + 'px'
@@ -105,13 +111,22 @@ export default defineComponent({
           }
         )
         const result = res.data || {}
+        const userInfo = state.user.data as any
+        const alreadyBuy =
+          result.lessonGroup.payType === 'VIP'
+            ? userInfo.isVip
+              ? true
+              : false
+            : result.alreadyBuy
         this.videoDetail = result.lessonGroup
         this.title = result.lessonGroup.lessonName
+        this.payType = result.lessonGroup.payType
         this.lessonPrice = result.lessonGroup.lessonPrice
         this.useRelationType = result.lessonGroup.relationType
-        this.alreadyBuy = result.alreadyBuy
+        this.alreadyBuy = alreadyBuy
         this.detailList = result.detailList || []
-        this.trySee = !result.alreadyBuy
+        this.trySee = !alreadyBuy
+
         this.detailList.forEach((item: any, index: number) => {
           if (item.id === Number(this.classId)) {
             this.posterUrl = item.coverUrl
@@ -193,6 +208,22 @@ export default defineComponent({
       } catch {}
     }
   },
+  watch: {
+    pageVisibility() {
+      // 如果不是会员则不用刷新
+      if (this.payType !== 'VIP') {
+        return
+      }
+      request
+        .get('/api-student/student/queryUserInfo', {
+          initRequest: true // 初始化接口
+        })
+        .then((res: any) => {
+          setLogin(res.data)
+          this.__init()
+        })
+    }
+  },
   render() {
     return (
       <div class={styles['video-class-detail']}>
@@ -206,10 +237,29 @@ export default defineComponent({
                   freeTitleStatus={this.lessonPrice > 0 ? true : false}
                   trySee={this.trySee}
                   src={this.srcUrl}
+                  payType={this.payType}
                   poster={this.posterUrl}
                   height={this.videoHeight}
                   isBuy
                   onBuyEmit={async () => {
+                    if (this.payType === 'VIP') {
+                      if (browser().isApp) {
+                        postMessage({
+                          api: 'openWebView',
+                          content: {
+                            url: `${location.origin}${location.pathname}#/memberCenter`,
+                            orientation: 1,
+                            isHideTitle: false
+                          }
+                        })
+                      } else {
+                        this.$router.push({
+                          path: '/memberCenter'
+                        })
+                      }
+                      return
+                    }
+
                     if (this.lessonPrice > 0) {
                       this.$router.back()
                       return

+ 57 - 4
src/student/video-class/video-detail.tsx

@@ -2,6 +2,7 @@ import CourseVideoItem from '@/business-components/course-video-item'
 import SectionDetail from '@/business-components/section-detail'
 import UserDetail from '@/business-components/user-detail'
 import { Sticky, Button, Dialog, Popup, Toast } from 'vant'
+import { postMessage } from '@/helpers/native-message'
 import { defineComponent } from 'vue'
 import styles from './video-detail.module.less'
 import request from '@/helpers/request'
@@ -12,13 +13,15 @@ import ColSticky from '@/components/col-sticky'
 import iconShare from '@/views/shop-mall/images/icon-share.svg'
 import ColShare from '@/components/col-share'
 import LiveItem from '@/views/live-class/live-item'
-import { state } from '@/state'
+import { setLogin, state } from '@/state'
 import { browser } from '@/helpers/utils'
+import { usePageVisibility } from '@vant/use'
 export default defineComponent({
   name: 'VideoDetail',
   data() {
     const query = this.$route.query
     return {
+      pageVisibility: 'visible' as any,
       userInfo: {} as any,
       detailList: [],
       recomUserId: query.recomUserId, // 推荐人id
@@ -40,6 +43,7 @@ export default defineComponent({
     }
   },
   async mounted() {
+    this.pageVisibility = usePageVisibility()
     await this._init()
     if (/(localhost|192)/g.test(location.origin)) {
       this.shareUrl = `https://dev.colexiu.com/teacher#/shareVideo?recomUserId=${state.user.data?.userId}&groupId=${this.params.groupId}&userType=${state.platformType}&p=tenant`
@@ -61,8 +65,16 @@ export default defineComponent({
         const result = res.data || {}
         const lessonGroup = result.lessonGroup || {}
         const teachertTag = result.teachertTag || ''
+        const userInfo = state.user.data as any
+        const alreadyBuy =
+          lessonGroup.payType === 'VIP'
+            ? userInfo.isVip
+              ? true
+              : false
+            : result.alreadyBuy
+
         this.userInfo = {
-          alreadyBuy: result.alreadyBuy,
+          alreadyBuy: alreadyBuy,
           username:
             lessonGroup.username || `游客${lessonGroup.teacherId || ''}`,
           headUrl: lessonGroup.avatar,
@@ -70,6 +82,7 @@ export default defineComponent({
           id: lessonGroup.id,
           lessonNum: lessonGroup.lessonCount,
           lessonName: lessonGroup.lessonName,
+          payType: lessonGroup.payType,
           lessonDesc: lessonGroup.lessonDesc,
           lessonPrice: lessonGroup.lessonPrice,
           relationType: lessonGroup.relationType,
@@ -80,6 +93,9 @@ export default defineComponent({
           isTeacher: result.teacherFlag ? true : false
         }
         this.detailList = result.detailList || []
+
+        console.log(lessonGroup.payType, userInfo.isVip)
+        console.log(this.userInfo, 'this.userInfo')
       } catch {
         //
       }
@@ -95,6 +111,24 @@ export default defineComponent({
     },
     async onBuy() {
       try {
+        if (this.userInfo.payType === 'VIP') {
+          if (browser().isApp) {
+            postMessage({
+              api: 'openWebView',
+              content: {
+                url: `${location.origin}${location.pathname}#/memberCenter`,
+                orientation: 1,
+                isHideTitle: false
+              }
+            })
+          } else {
+            this.$router.push({
+              path: '/memberCenter'
+            })
+          }
+          return
+        }
+
         const userInfo = this.userInfo
         orderStatus.orderObject.orderType = 'VIDEO'
         orderStatus.orderObject.orderName = '视频课购买'
@@ -182,6 +216,22 @@ export default defineComponent({
       } catch {}
     }
   },
+  watch: {
+    pageVisibility() {
+      // 如果不是会员则不用刷新
+      if (this.userInfo.payType !== 'VIP') {
+        return
+      }
+      request
+        .get('/api-student/student/queryUserInfo', {
+          initRequest: true // 初始化接口
+        })
+        .then((res: any) => {
+          setLogin(res.data)
+          this.userInfo.alreadyBuy = res.data?.isVip ? true : false
+        })
+    }
+  },
   render() {
     return (
       <div class={[styles['video-detail']]}>
@@ -195,7 +245,6 @@ export default defineComponent({
         <UserDetail
           userInfo={this.userInfo}
           onUserDetail={(item: any) => {
-            console.log(item)
             if (browser().isApp && state.platformType === 'STUDENT') {
               this.$router.push({
                 path: '/teacherHome',
@@ -279,7 +328,11 @@ export default defineComponent({
                 onClick={this.onBuy}
                 disabled={this.platformStatus}
               >
-                {this.userInfo.lessonPrice <= 0 ? '免费领取' : `立即购买`}
+                {this.userInfo.payType === 'VIP'
+                  ? '开通会员'
+                  : this.userInfo.lessonPrice <= 0
+                  ? '免费领取'
+                  : `立即购买`}
               </Button>
             </div>
           </ColSticky>

+ 25 - 15
src/student/video-class/video-item.tsx

@@ -60,28 +60,38 @@ export default defineComponent({
           </div> */}
           <div class={styles.viPrice}>
             <span class={styles.priceNum}>
-              {item?.lessonPrice > 0 && (
+              {item.payType === 'VIP' ? (
+                <span style={{ color: '#C76E21' }}>会员</span>
+              ) : (
                 <>
-                  <i>¥</i>
-                  {item?.lessonPrice}
+                  {item?.lessonPrice > 0 && (
+                    <>
+                      <i>¥</i>
+                      {item?.lessonPrice}
+                    </>
+                  )}
+                  {item?.lessonPrice <= 0 && item.auditVersion !== 0 && (
+                    <>
+                      <i>¥</i>0
+                    </>
+                  )}
+                  {item?.lessonPrice <= 0 && item.auditVersion === 0 && (
+                    <span style={{ color: '#20BEA0' }}>免费</span>
+                  )}
                 </>
               )}
-              {item?.lessonPrice <= 0 && item.auditVersion !== 0 && (
-                <>
-                  <i>¥</i>0
-                </>
-              )}
-              {item?.lessonPrice <= 0 && item.auditVersion === 0 && <>免费</>}
             </span>
             <span class={styles.label}>/{item?.lessonCount}课时</span>
           </div>
 
-          <div class={styles.viUserNum}>
-            {item?.countStudent}人已
-            {item?.lessonPrice <= 0 && item.auditVersion === 0
-              ? '领取'
-              : '购买'}
-          </div>
+          {item?.payType !== 'VIP' && (
+            <div class={styles.viUserNum}>
+              {item?.countStudent}人已
+              {item?.lessonPrice <= 0 && item.auditVersion === 0
+                ? '领取'
+                : '购买'}
+            </div>
+          )}
         </div>
       </div>
     )

+ 118 - 85
src/teacher/video-class/class-info.module.less

@@ -1,85 +1,118 @@
-.classInfo {
-  .infoField {
-    width: 50vw;
-    font-size: 16px;
-
-    :global {
-      .van-tab {
-        font-size: 16px;
-      }
-      .van-tabs__nav--line {
-        padding-left: 0;
-      }
-      .van-tab--active {
-        color: #000;
-      }
-      // .van-tab--shrink {
-      //   padding-left: 0;
-      // }
-    }
-  }
-
-  .boxStyle {
-    background: transparent !important;
-    width: 18px;
-    height: 18px;
-    border: transparent !important;
-  }
-  :global {
-    .van-radio {
-      display: inline-block;
-      align-items: inherit;
-      overflow: inherit;
-    }
-    .van-radio__icon {
-      height: 18px;
-      line-height: 18px;
-      display: inline-block;
-      vertical-align: sub;
-    }
-    .van-radio__label {
-      line-height: 18px;
-    }
-  }
-
-  .imgContainer {
-    width: 150px;
-    height: 85px;
-    border-radius: 10px;
-    overflow: hidden;
-    margin: 0 0 12px;
-    position: relative;
-
-    :global {
-      .van-radio {
-        position: absolute;
-        bottom: 10px;
-        right: 20px;
-        z-index: 9;
-      }
-    }
-  }
-
-  .btnGroup {
-    padding: 0 14px;
-    padding-bottom: 15px;
-  }
-}
-
-.class-info-tip {
-  font-size: 14px;
-  color: #999999;
-  line-height: 27px;
-  padding: 0 12px 12px;
-
-  span {
-    color: #ff4e19;
-  }
-}
-
-.photoTip {
-  font-size: 14px;
-  color: #999999;
-  line-height: 27px;
-  padding: 5px 0;
-}
+.classInfo {
+  .infoField {
+    width: 50vw;
+    font-size: 16px;
+
+    :global {
+      .van-tab {
+        font-size: 16px;
+      }
+
+      .van-tabs__nav--line {
+        padding-left: 0;
+      }
+
+      .van-tab--active {
+        color: #000;
+      }
+
+      // .van-tab--shrink {
+      //   padding-left: 0;
+      // }
+    }
+  }
+
+  .boxStyle {
+    background: transparent !important;
+    width: 18px;
+    height: 18px;
+    border: transparent !important;
+  }
+
+  :global {
+    .van-radio {
+      display: inline-block;
+      align-items: inherit;
+      overflow: inherit;
+    }
+
+    .van-radio__icon {
+      height: 18px;
+      line-height: 18px;
+      display: inline-block;
+      vertical-align: sub;
+    }
+
+    .van-radio__label {
+      line-height: 18px;
+    }
+  }
+
+  .radioGroup {
+    padding-top: 14px;
+    display: flex;
+  }
+
+  .radio {
+    width: 80px;
+    height: 28px;
+    line-height: 28px;
+    background: #F8F8F8;
+    border: 1px solid #F8F8F8;
+    border-radius: 5px;
+    font-size: 16px;
+    color: #C0C0C0;
+    text-align: center;
+
+    &+.radio {
+      margin-left: 12px;
+    }
+
+    &.active {
+      background: #E9FFF8;
+      border: 1px solid var(--van-primary);
+      color: var(--van-primary);
+    }
+  }
+
+  .imgContainer {
+    width: 150px;
+    height: 85px;
+    border-radius: 10px;
+    overflow: hidden;
+    margin: 0 0 12px;
+    position: relative;
+
+    :global {
+      .van-radio {
+        position: absolute;
+        bottom: 10px;
+        right: 20px;
+        z-index: 9;
+      }
+    }
+  }
+
+  .btnGroup {
+    padding: 0 14px;
+    padding-bottom: 15px;
+  }
+}
+
+.class-info-tip {
+  font-size: 14px;
+  color: #999999;
+  line-height: 27px;
+  padding: 0 12px 12px;
+
+  span {
+    color: #ff4e19;
+  }
+}
+
+.photoTip {
+  font-size: 14px;
+  color: #999999;
+  line-height: 27px;
+  padding: 5px 0;
+}

+ 66 - 27
src/teacher/video-class/class-info.tsx

@@ -166,35 +166,74 @@ export default defineComponent({
           </ColField>
         </ColFieldGroup>
 
-        {!this.checked && (
-          <ColFieldGroup>
-            <ColField title="课程组售价" required>
-              <Field
-                v-model={createState.lessonGroup.lessonPrice}
-                name="lessonPrice"
-                placeholder="请输入您的课程组售价"
-                formatter={this.onFormatter}
-                type="number"
-                maxlength={8}
-                rules={[{ required: true, message: '请输入您的课程组售价' }]}
-                v-slots={{
-                  button: () => <span>元</span>
+        <ColFieldGroup>
+          <ColField title="收费方式" required border={false}>
+            <div class={styles.radioGroup}>
+              <div
+                onClick={() => {
+                  createState.lessonGroup.payType = 'PAY'
                 }}
-              />
-            </ColField>
-          </ColFieldGroup>
-        )}
+                class={[
+                  styles.radio,
+                  createState.lessonGroup.payType === 'PAY'
+                    ? styles.active
+                    : null
+                ]}
+              >
+                购买
+              </div>
+              <div
+                onClick={() => {
+                  createState.lessonGroup.payType = 'VIP'
+                }}
+                class={[
+                  styles.radio,
+                  createState.lessonGroup.payType === 'VIP'
+                    ? styles.active
+                    : null
+                ]}
+              >
+                会员
+              </div>
+            </div>
+          </ColField>
+        </ColFieldGroup>
+
+        {createState.lessonGroup.payType === 'PAY' && (
+          <>
+            {!this.checked && (
+              <ColFieldGroup>
+                <ColField title="课程组售价" required>
+                  <Field
+                    v-model={createState.lessonGroup.lessonPrice}
+                    name="lessonPrice"
+                    placeholder="请输入您的课程组售价"
+                    formatter={this.onFormatter}
+                    type="number"
+                    maxlength={8}
+                    rules={[
+                      { required: true, message: '请输入您的课程组售价' }
+                    ]}
+                    v-slots={{
+                      button: () => <span>元</span>
+                    }}
+                  />
+                </ColField>
+              </ColFieldGroup>
+            )}
 
-        <div class={styles['class-info-tip']}>
-          <p>扣除手续费后您的课程预计收入为:</p>
-          <p>
-            课程组总收入<span>{this.calcRatePrice}</span>元/人
-          </p>
-          <p>
-            您的课程收入将在学员购买{createState.video_account_period}
-            天后结算到您的账户中
-          </p>
-        </div>
+            <div class={styles['class-info-tip']}>
+              <p>扣除手续费后您的课程预计收入为:</p>
+              <p>
+                课程组总收入<span>{this.calcRatePrice}</span>元/人
+              </p>
+              <p>
+                您的课程收入将在学员购买{createState.video_account_period}
+                天后结算到您的账户中
+              </p>
+            </div>
+          </>
+        )}
 
         <ColFieldGroup>
           <ColField

+ 3 - 0
src/teacher/video-class/create-submit.tsx

@@ -21,6 +21,7 @@ export default defineComponent({
         headUrl: users.headUrl,
         lessonName: videoDetail.lessonName,
         buyNum: 0,
+        payType: videoDetail.payType,
         lessonDesc: videoDetail.lessonDesc,
         lessonPrice: videoDetail.lessonPrice,
         lessonCoverUrl:
@@ -57,6 +58,8 @@ export default defineComponent({
           lessonList: this.lessonList,
           lessonGroup: {
             ...videoDetail,
+            lessonPrice:
+              videoDetail.payType === 'VIP' ? 0 : videoDetail.lessonPrice,
             lessonCoverUrl:
               videoDetail.lessonCoverTemplateUrl || videoDetail.lessonCoverUrl
           }

+ 1 - 0
src/teacher/video-class/createState.tsx

@@ -30,6 +30,7 @@ export const createState = reactive({
     lessonName: '',
     lessonSubject: null as any,
     lessonDesc: '',
+    payType: 'PAY',
     lessonPrice: null as any,
     lessonCoverUrl: '',
     relationType: 'RECOMMEND',

+ 7 - 3
src/teacher/video-class/video-class-detail.tsx

@@ -34,6 +34,7 @@ export default defineComponent({
       tabIndex: 1,
       title: '',
       lessonPrice: 0,
+      payType: '',
       useRelationType: '',
       alreadyBuy: false,
       currentClassIndex: 1,
@@ -82,6 +83,7 @@ export default defineComponent({
       this.title = result.lessonGroup.lessonName
       this.useRelationType = result.lessonGroup.relationType
       this.lessonPrice = result.lessonGroup.lessonPrice
+      this.payType = result.lessonGroup.payType
       this.alreadyBuy = result.alreadyBuy
       this.detailList = result.detailList || []
 
@@ -100,7 +102,7 @@ export default defineComponent({
       })
 
       const config = await request.get(
-        '/api-student/sysConfig/queryByParamNameList',
+        '/api-teacher/sysConfig/queryByParamNameList',
         {
           params: {
             paramNames: 'video_lesson_free_rate'
@@ -132,7 +134,6 @@ export default defineComponent({
         })
         this.loading = false
         const result = res.data || {}
-        console.log(result)
         // 处理重复请求数据
         if (this.list.length > 0 && result.pageNo === 1) {
           return
@@ -201,8 +202,11 @@ export default defineComponent({
         <ColVideo
           src={this.srcUrl}
           poster={this.posterUrl}
-          freeTitleStatus={this.lessonPrice > 0 ? true : false}
+          freeTitleStatus={
+            this.lessonPrice > 0 || this.payType === 'VIP' ? true : false
+          }
           freeRate={Number(this.freeRate)}
+          payType={this.payType}
           trySee={this.trySee}
           height={this.videoHeight}
         />

+ 1 - 0
src/teacher/video-class/video-detail.tsx

@@ -58,6 +58,7 @@ export default defineComponent({
         buyNum: result.lessonGroup.countStudent,
         lessonId: result.lessonGroup.id,
         lessonNum: result.lessonGroup.lessonCount,
+        payType: result.lessonGroup.payType,
         lessonName: result.lessonGroup.lessonName,
         lessonDesc: result.lessonGroup.lessonDesc,
         lessonPrice: result.lessonGroup.lessonPrice,

+ 6 - 0
src/tenant/music/music-detail/StringUtil.ts

@@ -0,0 +1,6 @@
+export class StringUtil {
+  public static StringContainsSeparatedWord(str: string, wordRegExString: string, ignoreCase = false): boolean {
+    const regExp = new RegExp("( |^)" + wordRegExString + "([ .]|$)", ignoreCase ? "i" : undefined);
+    return regExp.test(str);
+  }
+}

+ 2 - 2
src/tenant/music/music-detail/formatSvgToImg.ts

@@ -36,7 +36,7 @@ const blobToBase64 = (blob: any) => {
 }
 let canvas = null as any
 export const svgtopng = async (svg: any, width: any, height: any) => {
-  console.log(canvas, +new Date() + '-----')
+  // console.log(canvas, +new Date() + '-----')
 
   if (!canvas) {
     canvas = new OffscreenCanvas(width, height)
@@ -62,7 +62,7 @@ export const svgtopng = async (svg: any, width: any, height: any) => {
   // releaseCanvas(canvas)
   ctx.clearRect(0, 0, canvas.width, canvas.height)
   // canvas = null
-  console.log(canvas, 'draw')
+  // console.log(canvas, 'draw')
   await v.stop()
   v = null
   return base64

+ 352 - 0
src/tenant/music/music-detail/instrument.ts

@@ -0,0 +1,352 @@
+import { StringUtil } from "./StringUtil";
+import { isSpecialMark, isSpeedKeyword, isGradientWords, GRADIENT_SPEED_RESET_TAG } from "./speed-tag";
+
+export const formatXML = (xml: string): string => {
+  if (!xml) return "";
+  const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+  const measures: any = xmlParse.getElementsByTagName("measure");
+  // const repeats: any = Array.from(xmlParse.querySelectorAll('repeat'))
+  // 处理重复小节信息
+  // let speed = -1
+  let beats = -1;
+  let beatType = -1;
+  // 小节中如果没有节点默认为休止符
+  for (const measure of measures) {
+    if (beats === -1 && measure.getElementsByTagName("beats").length) {
+      beats = parseInt(measure.getElementsByTagName("beats")[0].textContent || "4");
+    }
+    if (beatType === -1 && measure.getElementsByTagName("beat-type").length) {
+      beatType = parseInt(measure.getElementsByTagName("beat-type")[0].textContent || "4");
+    }
+    // if (speed === -1 && measure.getElementsByTagName('per-minute').length) {
+    //   speed = parseInt(measure.getElementsByTagName('per-minute')[0].textContent || this.firstLib?.speed)
+    // }
+    const divisions = parseInt(measure.getElementsByTagName("divisions")[0]?.textContent || "256");
+    if (measure.getElementsByTagName("note").length === 0) {
+      const forwardTimeElement = measure.getElementsByTagName("forward")[0]?.getElementsByTagName("duration")[0];
+      if (forwardTimeElement) {
+        forwardTimeElement.textContent = "0";
+      }
+      measure.innerHTML =
+        measure.innerHTML +
+        `
+        <note>
+          <rest measure="yes"/>
+          <duration>${divisions * beats}</duration>
+          <voice>1</voice>
+          <type>whole</type>
+        </note>`;
+    }
+  }
+  return new XMLSerializer().serializeToString(xmlParse);
+};
+
+
+
+export const onlyVisible = (xml: string, partIndex: number): string => {
+  if (!xml) return "";
+  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 || "");
+  const parts: any = xmlParse.getElementsByTagName("part");
+  // const firstTimeInfo = parts[0]?.getElementsByTagName('metronome')[0]?.parentElement?.parentElement?.cloneNode(true)
+  const part: any = parts[0]
+  const firstMeasures = [...part.getElementsByTagName("measure")];
+  const metronomes = [...part.getElementsByTagName("metronome")];
+  const words = [...part.getElementsByTagName("words")];
+  const codas = [...part.getElementsByTagName("coda")];
+  const rehearsals = [...part.getElementsByTagName("rehearsal")];
+
+  /** 第一分谱如果是约定的配置分谱则跳过 */
+  if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
+    partIndex++;
+    partListNames.shift();
+  }
+  const visiblePartInfo = partList[partIndex];
+  // console.log(visiblePartInfo, partIndex)
+  if (visiblePartInfo) {
+    const id = visiblePartInfo.getAttribute("id");
+    Array.from(parts).forEach((part: any) => {
+      if (part && part.getAttribute("id") !== id) {
+        part.parentNode?.removeChild(part);
+        // 不等于第一行才添加避免重复添加
+      } else if (part && part.getAttribute("id") !== "P1") {
+        // 速度标记仅保留最后一个
+        const metronomeData: {
+          [key in string]: Element;
+        } = {};
+        for (let i = 0; i < metronomes.length; i++) {
+          const metronome = metronomes[i];
+          const metronomeContainer = metronome.parentElement?.parentElement?.parentElement;
+          if (metronomeContainer) {
+            const index = firstMeasures.indexOf(metronomeContainer);
+            metronomeData[index] = metronome;
+          }
+        }
+        Object.values(metronomeData).forEach((metronome) => {
+          const metronomeContainer: any = metronome.parentElement?.parentElement;
+          const parentMeasure: any = metronomeContainer?.parentElement;
+          const measureMetronomes = [...(parentMeasure?.childNodes || [])];
+          const metronomesIndex = metronomeContainer ? measureMetronomes.indexOf(metronomeContainer) : -1;
+          // console.log(parentMeasure)
+          if (parentMeasure && metronomesIndex > -1) {
+            const index = firstMeasures.indexOf(parentMeasure);
+            const activeMeasure = part.getElementsByTagName("measure")[index];
+            setElementNoteBefore(metronomeContainer, parentMeasure, activeMeasure);
+            //   console.log(measureMetronomes, metronomesIndex, activeMeasure?.childNodes, activeMeasure?.childNodes[metronomesIndex])
+            //   activeMeasure?.insertBefore(metronomeContainer.cloneNode(true), activeMeasure?.childNodes[metronomesIndex])
+            //   // part.getElementsByTagName('measure')[index]?.appendChild(metronomeContainer.cloneNode(true))
+            //   // console.log(index, parentMeasure, firstMeasures.indexOf(parentMeasure))
+          }
+        });
+        /** word比较特殊需要精确到note位置 */
+        // words.forEach((word) => {
+        //   let text = word.textContent || "";
+        //   text = ["cresc."].includes(text) ? "" : text;
+        //   if ((isSpecialMark(text) || isSpeedKeyword(text) || isGradientWords(text) || isRepeatWord(text) || GRADIENT_SPEED_RESET_TAG) && text) {
+        //     const wordContainer = word.parentElement?.parentElement;
+        //     const parentMeasure = wordContainer?.parentElement;
+        //     const measureWords = [...(parentMeasure?.childNodes || [])];
+        //     const wordIndex = wordContainer ? measureWords.indexOf(wordContainer) : -1;
+        //     if (wordContainer && parentMeasure && wordIndex > -1) {
+        //       const index = firstMeasures.indexOf(parentMeasure);
+        //       const activeMeasure = part.getElementsByTagName("measure")[index];
+        //       // 找当前小节是否包含word标签
+        //       const _words: any = Array.from(activeMeasure?.getElementsByTagName("words") || []);
+        //       // 遍历word标签,检查是否和第一小节重复,如果有重复则不平移word
+        //       const total = _words.reduce((total: any, _word) => {
+        //         if (_word.textContent?.includes(text)) {
+        //           total++;
+        //         }
+        //         return total;
+        //       }, 0);
+        //       if (total === 0) {
+        //         setElementNoteBefore(wordContainer, parentMeasure, activeMeasure);
+
+        //       }
+        //     }
+        //   }
+        // });
+        /** word比较特殊需要精确到note位置 */
+        // codas.forEach((coda) => {
+        //   const wordContainer = coda.parentElement?.parentElement;
+        //   const parentMeasure = wordContainer?.parentElement;
+        //   const measureWords = [...(parentMeasure?.childNodes || [])];
+        //   const wordIndex = wordContainer ? measureWords.indexOf(wordContainer) : -1;
+        //   if (wordContainer && parentMeasure && wordIndex > -1) {
+        //     const index = firstMeasures.indexOf(parentMeasure);
+        //     const activeMeasure = part.getElementsByTagName("measure")[index];
+
+        //     setElementNoteBefore(wordContainer, parentMeasure, activeMeasure);
+
+        //   }
+        // });
+        // rehearsals.forEach((rehearsal) => {
+        //   const container = rehearsal.parentElement?.parentElement;
+        //   const parentMeasure = container?.parentElement;
+        //   // console.log(rehearsal)
+        //   if (parentMeasure) {
+        //     const index = firstMeasures.indexOf(parentMeasure);
+        //     part.getElementsByTagName("measure")[index]?.appendChild(container.cloneNode(true));
+        //     // console.log(index, parentMeasure, firstMeasures.indexOf(parentMeasure))
+        //   }
+        // });
+      } else {
+        // words.forEach((word) => {
+        //   const text = word.textContent || "";
+        //   if (isSpeedKeyword(text) && text) {
+        //     const wordContainer = word.parentElement?.parentElement?.parentElement;
+        //     if (wordContainer && wordContainer.firstElementChild && wordContainer.firstElementChild !== word) {
+        //       const wordParent = word.parentElement?.parentElement;
+        //       const fisrt = wordContainer.firstElementChild;
+        //       wordContainer.insertBefore(wordParent, fisrt);
+        //     }
+        //   }
+        // });
+      }
+
+      // 最后一个小节的结束线元素不在最后 调整
+      if (part && part.getAttribute("id") === id) {
+        const barlines = part.getElementsByTagName("barline");
+        const lastParent = barlines[barlines.length - 1]?.parentElement;
+        if (lastParent?.lastElementChild?.tagName !== "barline") {
+          const children: any = lastParent?.children || [];
+          for (const el of children) {
+            if (el.tagName === "barline") {
+              // 将结束线元素放到最后
+              lastParent?.appendChild(el);
+              break;
+            }
+          }
+        }
+      }
+    });
+    Array.from(partList).forEach((part) => {
+      if (part && part.getAttribute("id") !== id) {
+        part.parentNode?.removeChild(part);
+      }
+    });
+    // 处理装饰音问题
+    const notes = xmlParse.getElementsByTagName("note");
+    const getNextvNoteDuration = (i: number) => {
+      let nextNote = notes[i + 1];
+      // 可能存在多个装饰音问题,取下一个非装饰音时值
+      for (let index = i; index < notes.length; index++) {
+        const note = notes[index];
+        if (!note.getElementsByTagName("grace")?.length) {
+          nextNote = note;
+          break;
+        }
+      }
+      const nextNoteDuration = nextNote?.getElementsByTagName("duration")[0];
+      return nextNoteDuration;
+    };
+    Array.from(notes).forEach((note, i) => {
+      const graces = note.getElementsByTagName("grace");
+      if (graces && graces.length) {
+        // if (i !== 0) {
+        // note.appendChild(getNextvNoteDuration(i)?.cloneNode(true))
+        // }
+      }
+    });
+  }
+  // console.log(xmlParse)
+  return new XMLSerializer().serializeToString(appoggianceFormate(xmlParse));
+};
+
+
+// 倚音后连音线
+export const appoggianceFormate = (xmlParse: Document): Document => {
+  if (!xmlParse) return xmlParse;
+  const graces: any = xmlParse.querySelectorAll("grace");
+  if (!graces.length) return xmlParse;
+  const getNextElement = (el: HTMLElement): HTMLElement => {
+    if (el.querySelector("grace")) {
+      return getNextElement(el?.nextElementSibling as HTMLElement);
+    }
+    return el;
+  };
+  for (const grace of graces) {
+    const notations = grace.parentElement?.querySelector("notations");
+    if (notations && notations.querySelectorAll("slur").length > 1) {
+      const nextEle: Element = getNextElement(grace.parentElement?.nextElementSibling as HTMLElement);
+      if (nextEle && nextEle.querySelectorAll("slur").length > 0) {
+        const slurNumber = Array.from(nextEle.querySelector("notations")?.children || []).map((el: Element) => {
+          return el.getAttribute("number");
+        });
+        const slurs = notations.querySelectorAll("slur");
+        for (const nota of slurs) {
+          if (!slurNumber.includes(nota.getAttribute("number"))) {
+            nextEle.querySelector("notations")?.appendChild(nota);
+          }
+        }
+      }
+    }
+  }
+  return xmlParse;
+};
+
+
+/**
+ * 添加第一分谱信息至当前分谱
+ * @param ele 需要插入的元素
+ * @param fitstParent 合奏谱第一个分谱
+ * @param parent 需要添加的分谱
+ */
+const setElementNoteBefore = (ele: Element, fitstParent: Element, parent?: Element | null) => {
+  let noteIndex = 0;
+  if (!fitstParent) {
+    return;
+  }
+  for (let index = 0; index < fitstParent.childNodes.length; index++) {
+    const element = fitstParent.childNodes[index];
+    if (element.nodeName === "note") {
+      noteIndex++;
+    }
+    if (element === ele) {
+      break;
+    }
+  }
+  if (noteIndex === 0 && parent) {
+    parent.insertBefore(ele, parent.childNodes[0]);
+    return;
+  }
+  if (parent && parent.childNodes.length > 0) {
+    let noteIndex2 = 0;
+    const notes = Array.from(parent.childNodes).filter((child) => child.nodeName === "note");
+    const lastNote = notes[notes.length - 1];
+    if (noteIndex >= notes.length && lastNote) {
+      parent.insertBefore(ele, parent.childNodes[Array.from(parent.childNodes).indexOf(lastNote)]);
+      return;
+    }
+    for (let index = 0; index < notes.length; index++) {
+      const element = notes[index];
+      if (element.nodeName === "note") {
+        noteIndex2 = noteIndex2 + 1;
+        if (noteIndex2 === noteIndex) {
+          parent.insertBefore(ele, element);
+          break;
+        }
+      }
+    }
+  }
+  // console.log(noteIndex, parent)
+};
+
+
+
+
+/**
+ * 检查传入文字是否为重复关键词
+ * @param text 总谱xml
+ * @returns 是否是重复关键词
+ */
+export const isRepeatWord = (text: string): boolean => {
+  if (text) {
+    const innerText = text.toLocaleLowerCase();
+    const dsRegEx = "d\\s?\\.s\\.";
+    const dcRegEx = "d\\.\\s?c\\.";
+
+    return (
+      innerText === "@" ||
+      StringUtil.StringContainsSeparatedWord(innerText, dsRegEx + " al fine", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, dsRegEx + " al coda", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, dcRegEx + " al fine", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, dcRegEx + " al coda", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, dcRegEx) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "da\\s?capo", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, dsRegEx, true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "dal\\s?segno", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "al\\s?coda", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "to\\s?coda", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "a (la )?coda", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "fine", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "coda", true) ||
+      StringUtil.StringContainsSeparatedWord(innerText, "segno", true)
+    );
+  }
+  return false;
+};
+
+
+
+/** 从xml中获取自定义信息,并删除多余的字符串 */
+export const getCustomInfo = (xml: string): any => {
+  const data = {
+    showSpeed: true,
+    parsedXML: xml,
+  };
+  const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+  const words: any = xmlParse.getElementsByTagName("words");
+  for (const word of words) {
+    if (word && word.textContent?.trim() === "隐藏速度") {
+      data.showSpeed = false;
+      word.textContent = "";
+    }
+    if (word && word.textContent?.trim() === "@") {
+      word.textContent = "segno";
+    }
+  }
+  data.parsedXML = new XMLSerializer().serializeToString(xmlParse);
+  return data;
+};

+ 114 - 80
src/tenant/music/music-detail/new-index.tsx

@@ -63,6 +63,7 @@ import { getUploadSign, onOnlyFileUpload } from '@/helpers/oss-file-upload'
 import { svgtopng } from './formatSvgToImg'
 import { shareCall } from '@/teacher/share-page/share'
 import deepClone from '@/helpers/deep-clone'
+import { formatXML, getCustomInfo, onlyVisible } from './instrument'
 
 export default defineComponent({
   name: 'new-index',
@@ -248,16 +249,6 @@ export default defineComponent({
           ...dataObj,
           file: files
         })
-        // for (const key in dataObj) {
-        //   formData.append(key, dataObj[key])
-        // }
-
-        // formData.append('file', files, fileName)
-        // const ossUploadUrl = getOssUploadUrl('cloud-coach')
-        // await umiRequest(ossUploadUrl, {
-        //   method: 'POST',
-        //   data: formData
-        // })
         Toast.clear()
         // const imgurl = getOssUploadUrl('cloud-coach') + keyTime
 
@@ -359,40 +350,43 @@ export default defineComponent({
 
         uploadImgs.value = true
 
-        for (let i = 0; i < osmdImg.length; i++) {
-          const img = await svgtopng(
-            osmdImg[i].img,
-            osmdImg[i].width,
-            osmdImg[i].height
-          )
-          const fileName =
-            route.query.id + state.user.data.userId + +new Date() + '.png'
-          const obj = {
-            filename: fileName,
-            bucketName: 'cloud-coach',
-            postData: {
+        if (osmdImg) {
+          for (let i = 0; i < osmdImg.length; i++) {
+            const img = await svgtopng(
+              osmdImg[i].img,
+              osmdImg[i].width,
+              osmdImg[i].height
+            )
+            const fileName =
+              route.query.id + state.user.data.userId + +new Date() + '.png'
+            const obj = {
               filename: fileName,
+              bucketName: 'cloud-coach',
+              postData: {
+                filename: fileName,
+                acl: 'public-read',
+                key: fileName
+              }
+            }
+            const { data } = await getUploadSign(obj, true)
+            const dataObj = {
+              policy: data.policy,
+              signature: data.signature,
+              key: fileName,
+              KSSAccessKeyId: data.kssAccessKeyId,
               acl: 'public-read',
-              key: fileName
+              name: fileName
             }
+            const files = base64ToBlob(img)
+            const ossUploadUrl = getOssUploadUrl('cloud-coach')
+            const imgurl = await onOnlyFileUpload(ossUploadUrl, {
+              ...dataObj,
+              file: files
+            })
+            imgs.push(imgurl)
           }
-          const { data } = await getUploadSign(obj, true)
-          const dataObj = {
-            policy: data.policy,
-            signature: data.signature,
-            key: fileName,
-            KSSAccessKeyId: data.kssAccessKeyId,
-            acl: 'public-read',
-            name: fileName
-          }
-          const files = base64ToBlob(img)
-          const ossUploadUrl = getOssUploadUrl('cloud-coach')
-          const imgurl = await onOnlyFileUpload(ossUploadUrl, {
-            ...dataObj,
-            file: files
-          })
-          imgs.push(imgurl)
         }
+
         uploadImgs.value = false
         // 判断是否为独奏
         if (
@@ -466,7 +460,7 @@ export default defineComponent({
             userId: state.user.data?.userId
           }
         })
-        console.log(res)
+        // console.log(res)
         setTimeout(() => {
           musicDetail.value.coursewareId = res.data.id || ''
           Toast('添加成功')
@@ -647,10 +641,10 @@ export default defineComponent({
       单簧管: 2,
       中音单簧管: 3,
       低音单簧管: 4,
-      高音萨克斯: 5,
-      中音萨克斯: 6,
-      次中音萨克斯: 7,
-      低音萨克斯: 8,
+      高音萨克斯: 5,
+      中音萨克斯: 6,
+      次中音萨克斯: 7,
+      低音萨克斯: 8,
       小号: 9,
       长号: 10,
       圆号: 11,
@@ -673,21 +667,23 @@ export default defineComponent({
           const res = await umiRequest.get(musicDetail.value?.xmlFileUrl, {
             mode: 'cors'
           })
-          const xmlParse = new DOMParser().parseFromString(res, 'text/xml')
-          const parts = xmlParse.getElementsByTagName('score-part')
-
-          const partList: any = []
-          for (let i = 0; i < parts.length; i++) {
-            const childDom = parts[i].children
-            for (let j = 0; j < childDom.length; j++) {
-              if (childDom[j].nodeName === 'part-name') {
-                partList.push({
-                  name: childDom[j].textContent,
-                  value: i
-                })
-              }
+          let partNames: string[] = []
+          const xml: any = new DOMParser().parseFromString(res, 'text/xml')
+          for (const item of xml.getElementsByTagName('part-name')) {
+            if (item.textContent) {
+              partNames.push(item.textContent)
             }
           }
+          partNames = partNames.filter(
+            (item: any) => !item?.toLocaleUpperCase()?.includes('COMMON')
+          )
+          const partList: any = []
+          for (let j = 0; j < partNames.length; j++) {
+            partList.push({
+              name: partNames[j],
+              value: j
+            })
+          }
           staffData.xmlPartList = partList
         }
 
@@ -698,57 +694,94 @@ export default defineComponent({
           location.pathname
         }osmd/index.html?t=${new Date().getTime()}`
         staffData.musicXml = musicDetail.value?.xmlFileUrl || ''
-        staffData.partList = musicDetail.value?.background || []
-        staffData.partList.forEach((part: any) => {
-          const item = staffData.xmlPartList.find(
-            item => item.name === part.track
-          )
-          if (item) {
-            part.index = item.value
-          }
+        const tempList = musicDetail.value?.background || []
+        const tempPartList = [] as any
+        staffData.xmlPartList.forEach((part: any) => {
+          const item = tempList.find((item: any) => item.track === part.name)
+          tempPartList.push({
+            ...item,
+            index: part.value
+          })
         })
-        console.log(staffData.partList, '-staffData.partList')
+
+        staffData.partList = tempPartList
+
         staffData.tempPartList = JSON.parse(JSON.stringify(staffData.partList))
         staffData.partList = instrumentSort(staffData.partList)
+        // console.log(staffData.partList, ' staffData.partList')
         staffData.partXmlIndex = staffData.partList[0].index || 0
 
-        staffData.instrumentName = getInstrumentName(
-          staffData.partList[staffData.partIndex]?.track
-        )
+        staffData.instrumentName =
+          musicDetail.value?.musicSheetType === 'CONCERT'
+            ? getInstrumentName(staffData.partList[staffData.partIndex]?.track)
+            : ''
+
+        // console.log(staffData.partList, 'staffData', staffData.xmlPartList)
       } catch (error) {
         //
       }
     }
 
-    const musicIframeLoad = () => {
+    const musicIframeLoad = async () => {
       const iframeRef: any = document.getElementById('staffIframeRef')
       if (iframeRef && iframeRef.contentWindow.renderXml) {
-        iframeRef.contentWindow.renderXml(
-          staffData.musicXml,
-          staffData.partXmlIndex
-        )
+        const res = await umiRequest.get(staffData.musicXml, {
+          mode: 'cors'
+        })
+        const parseXmlInfo = getCustomInfo(res)
+        const xml = formatXML(parseXmlInfo.parsedXML)
+
+        const currentXml = onlyVisible(xml, staffData.partXmlIndex)
+        iframeRef.contentWindow.renderXml(currentXml, 0)
+
+        // iframeRef.contentWindow.renderXml(
+        //   staffData.musicXml,
+        //   staffData.partXmlIndex
+        // )
       }
     }
-    const resetRender = () => {
+    const resetRender = async () => {
       const iframeRef: any = document.getElementById('staffIframeRef')
       if (iframeRef && iframeRef.contentWindow.renderXml) {
-        iframeRef.contentWindow.resetRender(staffData.partXmlIndex)
+        loading.value = true
+        // iframeRef.contentWindow.resetRender(staffData.partXmlIndex)
+        const res = await umiRequest.get(staffData.musicXml, {
+          mode: 'cors'
+        })
+        const parseXmlInfo = getCustomInfo(res)
+        const xml = formatXML(parseXmlInfo.parsedXML)
+
+        const currentXml = onlyVisible(xml, staffData.partXmlIndex)
+        iframeRef.contentWindow.renderXml(currentXml, 0)
+
         staffData.instrumentName = getInstrumentName(
           staffData.partList[staffData.partIndex]?.track
         )
       }
     }
 
-    const resetRenderPage = (type: string, xmlUrl: string) => {
+    const resetRenderPage = async (type: string, xmlUrl: string) => {
       const iframeRef: any = document.getElementById('staffIframeRef')
       if (iframeRef && iframeRef.contentWindow.renderXml) {
-        console.log('resetRenderPage')
-        iframeRef.contentWindow.resetRenderPage(type, xmlUrl)
+        // console.log('resetRenderPage')
+        // iframeRef.contentWindow.resetRenderPage(type, xmlUrl)
+
+        const res = await umiRequest.get(staffData.musicXml, {
+          mode: 'cors'
+        })
+        const parseXmlInfo = getCustomInfo(res)
+        const xml = formatXML(parseXmlInfo.parsedXML)
+
+        const currentXml = onlyVisible(xml, staffData.partXmlIndex)
+        iframeRef.contentWindow.resetRenderPage(type, currentXml)
       }
     }
     const partColumns = computed(() => {
       return staffData.partList.map((item: any, index: number) => {
-        const instrumentName = getInstrumentName(item.track)
+        const instrumentName =
+          musicDetail.value?.musicSheetType === 'CONCERT'
+            ? getInstrumentName(item.track)
+            : ''
         return {
           text: item.track + (instrumentName ? `(${instrumentName})` : ''),
           value: index,
@@ -1114,6 +1147,7 @@ export default defineComponent({
                         const item: any = partColumns.value.find(
                           (c: any) => c.value === staffData.partIndex
                         )
+
                         // const index = staffData.tempPartList.findIndex(
                         //   (i: any) => i.track === item?.track
                         // )

+ 88 - 0
src/tenant/music/music-detail/speed-tag.ts

@@ -0,0 +1,88 @@
+/**
+  Grava壮板=40
+  Largo广板=46
+  Lento慢板=52
+  Adagio柔板=56
+  Larghetto小广板=60
+  Andante行板=66
+  Anderato/Andantino小行板=69
+  Moderato中板=88
+  Allegretto小快板=108
+  Allegro Moderato=108 // 考级需要
+  Allegro快板=132
+  Vivace快速有生气=152
+  Vivo快速有生气=160
+  Vivacissimo极其活泼的快板=168
+  Presto急板=184
+  Prestissimo最急板=208
+ */
+
+// import {TextAlignmentEnum} from "../../Common/Enums/TextAlignment";
+
+export const SpeedTag: { [key in string]: number } = {
+  "Grava": 40,
+  "Largo": 46,
+  "Lento": 52,
+  "Adagio": 56,
+  "Larghetto": 60,
+  "Andante": 66,
+  "Anderato": 69,
+  "Andantino": 69,
+  "Moderato": 88,
+  "Allegretto": 108,
+  "Allegro Moderato": 108,
+  "Allegro": 132,
+  "Vivace": 152,
+  "Vivo": 160,
+  "Vivacissimo": 168,
+  "Presto": 184,
+  "Prestissimo": 208
+};
+
+export const SpecialMarks: string[] = ["纯律", "纯律结束"];
+
+export const HideWords: string[] = ["跳过下一个", "b", "#", "§", "º", "X"];
+
+export const GradientWords: string[] = ["poco rit.", "rall.", "rit.", "accel.", "molto rit.", "molto rall", "lentando", "poco accel.", "calando"];
+
+export const GRADIENT_SPEED_CLOSE_TAG = "结束范围速度";
+
+export const GRADIENT_SPEED_RESET_TAG = "a tempo";
+
+export const SpecialWords: string[] = [GRADIENT_SPEED_CLOSE_TAG];
+
+export const SpeedKeyword = "速度 ";
+export const SpeedHiddenKeyword = "仅文本速度 ";
+
+/** 是否为速度关键词 */
+export function isSpeedKeyword(str: string): boolean {
+  return str.indexOf(SpeedKeyword) === 0;
+};
+
+/** 是否为速度关键词 */
+export function isSpeedHiddenKeyword(str: string): boolean {
+  return str.indexOf(SpeedHiddenKeyword) === 0;
+};
+
+/** 格式化速度关键词移除前缀 */
+export function formatSpeedKeyword(str: string): string {
+  return str.replace(SpeedHiddenKeyword, "").replace(SpeedKeyword, "");
+};
+
+/** 是否是渐变速度关键词 */
+export function isGradientWords(str: string): boolean {
+  return GradientWords.includes(str);
+};
+
+/**
+ * 是否为特殊标记
+ * 包含中文与英文,中文正则存在部分问题
+ */
+export function isSpecialMark(str: string): boolean {
+  return [...Object.keys(SpeedTag), ...SpecialMarks, ...SpecialWords, ...HideWords]
+    .map((s: string) => s.trim().toLocaleUpperCase()).includes(str.toLocaleUpperCase().trim());
+}
+
+// export function isTopFont(textAlignment): boolean {
+//   return [TextAlignmentEnum.CenterTop, TextAlignmentEnum.RightTop].includes(textAlignment);
+// };

+ 6 - 0
src/tenant/music/train-tool/index.tsx

@@ -202,6 +202,12 @@ export default defineComponent({
         albumId: state.details.id || null,
         subjectType: state.activeTab,
         ...params
+      } as any
+
+      // 老师端默认查询声部
+      if (baseState.platformType === 'TEACHER') {
+        const users = baseState.user.data
+        tempParams.subjectId = users.defaultSubject || null
       }
 
       try {

+ 258 - 258
src/views/video-class/index.tsx

@@ -1,258 +1,258 @@
-import ColHeader from '@/components/col-header'
-import ColSearch from '@/components/col-search'
-import { Sticky, Image, List, Popup, Icon } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-import VideoItem from '@/student/video-class/video-item'
-// import VideoItem from './video-item'
-
-import banner from './images/banner.png'
-import request from '@/helpers/request'
-import ColResult from '@/components/col-result'
-import { state } from '@/state'
-import OrganSearch from '@/student/practice-class/model/organ-search'
-import { SubjectEnum, useEventTracking, useSubjectId } from '@/helpers/hooks'
-
-export default defineComponent({
-  name: 'VideoClass',
-  data() {
-    return {
-      apiSuffix:
-        state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher',
-      search: '',
-      list: [],
-      dataShow: true, // 判断是否有数据
-      loading: false,
-      finished: false,
-      listLoading: true,
-      params: {
-        search: '',
-        lessonSubject: null as any,
-        subjectName: '全部声部',
-        page: 1,
-        rows: 20
-      },
-      searchStatus: false,
-      openStatus: false,
-      subjectList: []
-    }
-  },
-  async mounted() {
-    try {
-      const res = await request.get(
-        `${this.apiSuffix}/subject/subjectSelect?type=VIDEO`
-      )
-      this.subjectList = res.data || []
-    } catch {
-      //
-    }
-
-    if (state.platformType === 'TEACHER') {
-      const users = state.user.data
-      if (users.defaultSubject) {
-        this.params.lessonSubject = users.defaultSubject
-        this.params.subjectName = users.defaultSubjectName
-      }
-    } else {
-      // 判断是否在缓存
-      const subjects: any = useSubjectId(SubjectEnum.VIDEO)
-      if (subjects.id) {
-        this.params.lessonSubject = Number(subjects.id)
-        this.params.subjectName = subjects.name
-      } else {
-        const list = this.subjectList
-        const subjectIds = state.user.data?.subjectId || ''
-        const subjectId = subjectIds ? Number(subjectIds.split(',')[0]) : null
-        list.forEach((subject: any) => {
-          const child = subject.subjects || []
-          child.forEach((sub: any) => {
-            if (sub.id === Number(subjectId)) {
-              this.params.lessonSubject = sub.id
-              this.params.subjectName = sub.name
-            }
-          })
-        })
-      }
-    }
-    this.listLoading = false
-    this.getList()
-
-    useEventTracking('视频课')
-  },
-  methods: {
-    async getList() {
-      try {
-        if (this.listLoading) return
-        this.listLoading = true
-
-        const params: any = {
-          ...this.params
-        }
-        // 只有学生端会有version
-        if (state.version) {
-          params.version = state.version || '' // 处理ios审核版本
-          params.platform =
-            state.platformType === 'STUDENT' ? 'ios-student' : 'ios-teacher'
-        }
-        const url =
-          state.platformType === 'STUDENT'
-            ? '/api-student/videoLesson/selectGroup'
-            : '/api-teacher/videoLessonGroup/page'
-        // 处理搜索,老师端分享用
-        // if (state.platformType === 'TEACHER') {
-        params.myself = false
-        // }
-        const res = await request.post(url, {
-          data: {
-            ...params
-          }
-        })
-        this.loading = false
-        const result = res.data || {}
-        // 处理重复请求数据
-        if (this.list.length > 0 && result.pageNo === 1) {
-          return
-        }
-        this.list = this.list.concat(result.rows || [])
-        this.finished = result.pageNo >= result.totalPage
-        this.params.page = result.pageNo + 1
-        this.dataShow = this.list.length > 0
-      } catch {
-        this.dataShow = false
-        this.finished = true
-      } finally {
-        this.listLoading = false
-      }
-    },
-    // 设置默认声部
-    async setDefaultSubject(subjectId: any) {
-      try {
-        await request.post('/api-teacher/teacher/defaultSubject', {
-          params: {
-            subjectId
-          }
-        })
-      } catch {
-        //
-      }
-    },
-    onSort() {
-      this.params.page = 1
-      this.list = []
-      this.dataShow = true // 判断是否有数据
-      this.loading = false
-      this.finished = false
-      this.searchStatus = false
-      if (state.platformType === 'TEACHER') {
-        this.setDefaultSubject(this.params.lessonSubject)
-      } else {
-        this.params.lessonSubject &&
-          useSubjectId(
-            SubjectEnum.VIDEO,
-            JSON.stringify({
-              id: this.params.lessonSubject,
-              name: this.params.subjectName
-            }),
-            'set'
-          )
-      }
-      this.getList()
-    },
-    onSearch(val: string) {
-      this.params.search = val
-      this.onSort()
-    },
-    onDetail(item: any) {
-      const params: any = {
-        groupId: item.id
-      }
-      // 判断是否是老师端,如果是,则添加分享按钮
-      if (state.platformType === 'TEACHER') {
-        params.share = 1
-      }
-      this.$router.push({
-        path: '/videoDetail',
-        query: params
-      })
-    }
-  },
-  render() {
-    return (
-      <div class={styles.videoClass}>
-        <Sticky offsetTop={0} position="top">
-          <ColHeader
-            class={styles.classHeader}
-            border={false}
-            isFixed={false}
-            background="transparent"
-          />
-          <ColSearch
-            placeholder="请输入老师名称/课程名称"
-            onSearch={this.onSearch}
-            v-slots={{
-              left: () => (
-                <div
-                  class={styles.label}
-                  onClick={() => {
-                    this.searchStatus = !this.searchStatus
-                    this.openStatus = !this.openStatus
-                  }}
-                >
-                  {this.params.subjectName}
-                  <Icon
-                    classPrefix="iconfont"
-                    name="down"
-                    size={12}
-                    color="#333"
-                  />
-                </div>
-              )
-            }}
-          />
-        </Sticky>
-
-        {/* <div class={styles.banner}>
-          <Image src={banner} />
-        </div> */}
-
-        <div>
-          {this.dataShow ? (
-            <List
-              class={styles.videoList}
-              v-model:loading={this.loading}
-              finished={this.finished}
-              finishedText="没有更多了"
-              onLoad={this.getList}
-              immediateCheck={false}
-            >
-              {this.list.map(item => (
-                <VideoItem item={item} onClick={this.onDetail} />
-              ))}
-            </List>
-          ) : (
-            <ColResult btnStatus={false} tips="暂无视频课" />
-          )}
-        </div>
-
-        <Popup
-          show={this.searchStatus}
-          position="bottom"
-          round
-          closeable
-          safe-area-inset-bottom
-          onClose={() => (this.searchStatus = false)}
-          onClosed={() => (this.openStatus = false)}
-        >
-          {this.openStatus && (
-            <OrganSearch
-              subjectList={this.subjectList}
-              onSort={this.onSort}
-              v-model={this.params.lessonSubject}
-              v-model:subjectName={this.params.subjectName}
-            />
-          )}
-        </Popup>
-      </div>
-    )
-  }
-})
+import ColHeader from '@/components/col-header'
+import ColSearch from '@/components/col-search'
+import { Sticky, Image, List, Popup, Icon } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import VideoItem from '@/student/video-class/video-item'
+// import VideoItem from './video-item'
+
+import banner from './images/banner.png'
+import request from '@/helpers/request'
+import ColResult from '@/components/col-result'
+import { state } from '@/state'
+import OrganSearch from '@/student/practice-class/model/organ-search'
+import { SubjectEnum, useEventTracking, useSubjectId } from '@/helpers/hooks'
+
+export default defineComponent({
+  name: 'VideoClass',
+  data() {
+    return {
+      apiSuffix:
+        state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher',
+      search: '',
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      listLoading: true,
+      params: {
+        search: '',
+        lessonSubject: null as any,
+        subjectName: '全部声部',
+        page: 1,
+        rows: 20
+      },
+      searchStatus: false,
+      openStatus: false,
+      subjectList: []
+    }
+  },
+  async mounted() {
+    try {
+      const res = await request.get(
+        `${this.apiSuffix}/subject/subjectSelect?type=VIDEO`
+      )
+      this.subjectList = res.data || []
+    } catch {
+      //
+    }
+
+    if (state.platformType === 'TEACHER') {
+      const users = state.user.data
+      if (users.defaultSubject) {
+        this.params.lessonSubject = users.defaultSubject
+        this.params.subjectName = users.defaultSubjectName
+      }
+    } else {
+      // 判断是否在缓存
+      const subjects: any = useSubjectId(SubjectEnum.VIDEO)
+      if (subjects.id) {
+        this.params.lessonSubject = Number(subjects.id)
+        this.params.subjectName = subjects.name
+      } else {
+        const list = this.subjectList
+        const subjectIds = state.user.data?.subjectId || ''
+        const subjectId = subjectIds ? Number(subjectIds.split(',')[0]) : null
+        list.forEach((subject: any) => {
+          const child = subject.subjects || []
+          child.forEach((sub: any) => {
+            if (sub.id === Number(subjectId)) {
+              this.params.lessonSubject = sub.id
+              this.params.subjectName = sub.name
+            }
+          })
+        })
+      }
+    }
+    this.listLoading = false
+    this.getList()
+
+    useEventTracking('视频课')
+  },
+  methods: {
+    async getList() {
+      try {
+        if (this.listLoading) return
+        this.listLoading = true
+
+        const params: any = {
+          ...this.params
+        }
+        // 只有学生端会有version
+        if (state.version) {
+          params.version = state.version || '' // 处理ios审核版本
+          params.platform =
+            state.platformType === 'STUDENT' ? 'ios-student' : 'ios-teacher'
+        }
+        const url =
+          state.platformType === 'STUDENT'
+            ? '/api-student/videoLesson/selectGroup'
+            : '/api-teacher/videoLessonGroup/page'
+        // 处理搜索,老师端分享用
+        // if (state.platformType === 'TEACHER') {
+        params.myself = false
+        // }
+        const res = await request.post(url, {
+          data: {
+            ...params
+          }
+        })
+        this.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      } finally {
+        this.listLoading = false
+      }
+    },
+    // 设置默认声部
+    async setDefaultSubject(subjectId: any) {
+      try {
+        await request.post('/api-teacher/teacher/defaultSubject', {
+          params: {
+            subjectId
+          }
+        })
+      } catch {
+        //
+      }
+    },
+    onSort() {
+      this.params.page = 1
+      this.list = []
+      this.dataShow = true // 判断是否有数据
+      this.loading = false
+      this.finished = false
+      this.searchStatus = false
+      if (state.platformType === 'TEACHER') {
+        this.setDefaultSubject(this.params.lessonSubject)
+      } else {
+        this.params.lessonSubject &&
+          useSubjectId(
+            SubjectEnum.VIDEO,
+            JSON.stringify({
+              id: this.params.lessonSubject,
+              name: this.params.subjectName
+            }),
+            'set'
+          )
+      }
+      this.getList()
+    },
+    onSearch(val: string) {
+      this.params.search = val
+      this.onSort()
+    },
+    onDetail(item: any) {
+      const params: any = {
+        groupId: item.id
+      }
+      // 判断是否是老师端,如果是,则添加分享按钮
+      if (state.platformType === 'TEACHER') {
+        params.share = 1
+      }
+      this.$router.push({
+        path: '/videoDetail',
+        query: params
+      })
+    }
+  },
+  render() {
+    return (
+      <div class={styles.videoClass}>
+        <Sticky offsetTop={0} position="top">
+          <ColHeader
+            class={styles.classHeader}
+            border={false}
+            isFixed={false}
+            background="transparent"
+          />
+          <ColSearch
+            placeholder="请输入老师名称/课程名称"
+            onSearch={this.onSearch}
+            v-slots={{
+              left: () => (
+                <div
+                  class={styles.label}
+                  onClick={() => {
+                    this.searchStatus = !this.searchStatus
+                    this.openStatus = !this.openStatus
+                  }}
+                >
+                  {this.params.subjectName}
+                  <Icon
+                    classPrefix="iconfont"
+                    name="down"
+                    size={12}
+                    color="#333"
+                  />
+                </div>
+              )
+            }}
+          />
+        </Sticky>
+
+        {/* <div class={styles.banner}>
+          <Image src={banner} />
+        </div> */}
+
+        <div>
+          {this.dataShow ? (
+            <List
+              class={styles.videoList}
+              v-model:loading={this.loading}
+              finished={this.finished}
+              finishedText="没有更多了"
+              onLoad={this.getList}
+              immediateCheck={false}
+            >
+              {this.list.map(item => (
+                <VideoItem item={item} onClick={this.onDetail} />
+              ))}
+            </List>
+          ) : (
+            <ColResult btnStatus={false} tips="暂无视频课" />
+          )}
+        </div>
+
+        <Popup
+          show={this.searchStatus}
+          position="bottom"
+          round
+          closeable
+          safe-area-inset-bottom
+          onClose={() => (this.searchStatus = false)}
+          onClosed={() => (this.openStatus = false)}
+        >
+          {this.openStatus && (
+            <OrganSearch
+              subjectList={this.subjectList}
+              onSort={this.onSort}
+              v-model={this.params.lessonSubject}
+              v-model:subjectName={this.params.subjectName}
+            />
+          )}
+        </Popup>
+      </div>
+    )
+  }
+})

+ 1 - 1
vite.config.ts

@@ -13,7 +13,7 @@ function resolve(dir: string) {
 // https://vitejs.dev/config/
 // https://github.com/vitejs/vite/issues/1930 .env
 // const proxyUrl = 'https://online.colexiu.com/'
-const proxyUrl = 'https://test.colexiu.com/'
+const proxyUrl = 'https://dev.colexiu.com/'
 // const proxyUrl = 'http://192.168.3.14:8000/'
 export default defineConfig({
   base: './',

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff