lex-xin 8 月之前
父節點
當前提交
cd4d4e8346
共有 100 個文件被更改,包括 2453 次插入400 次删除
  1. 1 0
      dist/assets/auth-login-legacy.849fcf80.js
  2. 1 0
      dist/assets/auth-login-legacy.c653f047.js
  3. 1 0
      dist/assets/auth-login.71094868.js
  4. 1 0
      dist/assets/auth-login.86170115.js
  5. 5 0
      dist/assets/home-layout-center-legacy.bd9e3956.js
  6. 4 0
      dist/assets/home-layout-center-legacy.edb328af.js
  7. 4 0
      dist/assets/home-layout-center.7d29b07d.js
  8. 5 0
      dist/assets/home-layout-center.f37c4270.js
  9. 1 0
      dist/assets/index-legacy.053ade53.js
  10. 0 0
      dist/assets/index-legacy.30917c0e.js
  11. 0 0
      dist/assets/index-legacy.36079324.js
  12. 0 0
      dist/assets/index-legacy.82341cfe.js
  13. 1 0
      dist/assets/index-legacy.c691bb3c.js
  14. 1 0
      dist/assets/index.046dadb0.js
  15. 1 0
      dist/assets/index.7b973e74.js
  16. 0 0
      dist/assets/index.9fc513ab.js
  17. 0 0
      dist/assets/index.ab9be12e.js
  18. 1 0
      dist/assets/index.ad94d5f4.js
  19. 1 0
      dist/assets/index.f0513a9d.js
  20. 1 0
      dist/assets/login-legacy.31456d4a.js
  21. 1 0
      dist/assets/login-legacy.cbaf3e35.js
  22. 1 0
      dist/assets/login.4394a365.js
  23. 1 0
      dist/assets/login.b2a78be4.js
  24. 1 0
      dist/assets/new-index-legacy.50e7cfcd.js
  25. 1 0
      dist/assets/new-index-legacy.8375839b.js
  26. 1 0
      dist/assets/new-index.03aca409.js
  27. 1 0
      dist/assets/new-index.c0e57cbd.js
  28. 1 0
      dist/assets/polyfills-legacy.721957f7.js
  29. 1 0
      dist/assets/polyfills-legacy.810f0c80.js
  30. 1 0
      dist/assets/routes-common-legacy.303f6dfb.js
  31. 1 0
      dist/assets/routes-common-legacy.4c516b81.js
  32. 0 0
      dist/assets/routes-common.34a62762.js
  33. 0 0
      dist/assets/teacher-home-legacy.fbcf658b.js
  34. 0 0
      dist/assets/teacher-home.dca8a7f7.js
  35. 0 0
      dist/assets/teacher-legacy.8df3a5f6.js
  36. 0 0
      dist/assets/teacher.e934e7a8.js
  37. 1 0
      dist/assets/tenant-legacy.10748f36.js
  38. 1 0
      dist/assets/tenant-legacy.6e35d615.js
  39. 0 0
      dist/assets/tenant.da116a06.js
  40. 12 0
      dist/index.html
  41. 11 0
      dist/teacher.html
  42. 9 0
      dist/tenant.html
  43. 21 2
      src/business-components/calendar/index.tsx
  44. 二進制
      src/business-components/section-detail/images/icon_course_introduction.png
  45. 二進制
      src/business-components/section-detail/images/icon_course_list.png
  46. 二進制
      src/business-components/section-detail/images/icon_course_list_1.png
  47. 3 3
      src/business-components/section-detail/index.module.less
  48. 5 1
      src/business-components/section-detail/index.tsx
  49. 二進制
      src/business-components/user-detail/images/icon-course.png
  50. 116 9
      src/business-components/user-detail/index.module.less
  51. 58 49
      src/business-components/user-detail/index.tsx
  52. 二進制
      src/common/images/icon_timer4.png
  53. 5 3
      src/components/col-share/index.module.less
  54. 2 0
      src/components/col-share/index.tsx
  55. 9 6
      src/components/col-upload-video/index.module.less
  56. 6 7
      src/components/col-upload-video/index.tsx
  57. 5 3
      src/components/col-upload/index.module.less
  58. 2 4
      src/components/col-upload/index.tsx
  59. 16 1
      src/helpers/hooks.ts
  60. 3 3
      src/helpers/utils.ts
  61. 32 0
      src/router/routes-teacher.ts
  62. 4 0
      src/student/group-class/group-detail.module.less
  63. 31 24
      src/student/group-class/group-detail.tsx
  64. 5 1
      src/student/live-class/live-detail.module.less
  65. 33 37
      src/student/live-class/live-detail.tsx
  66. 4 3
      src/student/teacher-dependent/components/group.tsx
  67. 4 37
      src/student/teacher-dependent/components/live.tsx
  68. 8 10
      src/student/teacher-dependent/components/music.tsx
  69. 66 44
      src/student/teacher-dependent/components/practice.tsx
  70. 12 6
      src/student/teacher-dependent/components/video-item/index.tsx
  71. 1 14
      src/student/teacher-dependent/components/video.tsx
  72. 21 15
      src/student/teacher-dependent/components/vip.tsx
  73. 1 4
      src/student/teacher-dependent/teacher-home.tsx
  74. 5 1
      src/student/video-class/video-detail.module.less
  75. 25 17
      src/student/video-class/video-detail.tsx
  76. 10 0
      src/styles/README.md
  77. 54 0
      src/styles/component-ui.less
  78. 129 0
      src/styles/global.less
  79. 5 4
      src/styles/index.less
  80. 15 0
      src/teacher/group-class/create-components/detail.module.less
  81. 6 4
      src/teacher/group-class/create-components/detail.tsx
  82. 18 60
      src/teacher/group-class/group-detail.tsx
  83. 3 2
      src/teacher/live-class/create-components/arrange.tsx
  84. 8 7
      src/teacher/live-class/create-components/course-plan.tsx
  85. 7 0
      src/teacher/live-class/create-components/course.tsx
  86. 1 0
      src/teacher/live-class/create-components/createState.ts
  87. 15 0
      src/teacher/live-class/create-components/detail.module.less
  88. 5 5
      src/teacher/live-class/create-components/detail.tsx
  89. 14 12
      src/teacher/live-class/live-detail.tsx
  90. 0 1
      src/teacher/main.ts
  91. 13 1
      src/teacher/my-sheetMusic/index.tsx
  92. 177 0
      src/teacher/statistics/exercise-detail/exercise-detail.module.less
  93. 319 0
      src/teacher/statistics/exercise-detail/exercise-detail.tsx
  94. 96 0
      src/teacher/statistics/home-statistics-detail/buy-item/index.module.less
  95. 59 0
      src/teacher/statistics/home-statistics-detail/buy-item/index.tsx
  96. 243 0
      src/teacher/statistics/home-statistics-detail/echats/index.module.less
  97. 479 0
      src/teacher/statistics/home-statistics-detail/echats/index.tsx
  98. 38 0
      src/teacher/statistics/home-statistics-detail/index.module.less
  99. 78 0
      src/teacher/statistics/home-statistics-detail/index.tsx
  100. 84 0
      src/teacher/statistics/home-statistics-detail/list/index.module.less

File diff suppressed because it is too large
+ 1 - 0
dist/assets/auth-login-legacy.849fcf80.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/auth-login-legacy.c653f047.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/auth-login.71094868.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/auth-login.86170115.js


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

@@ -0,0 +1,5 @@
+<<<<<<<< HEAD:dist/assets/home-layout-center-legacy.edb328af.js
+!function(){function e(e,t,n,r,o,s,u){try{var a=e[s](u),c=a.value}catch(i){return void n(i)}a.done?t(c):Promise.resolve(c).then(r,o)}System.register(["./ResizeObserver.es-legacy.44b91b4c.js","./index-legacy.5fa4fcaf.js","./auth-legacy.767687ba.js","./routes-common-legacy.4c516b81.js"],(function(t){"use strict";var n,r,o,s,u,a,c,i,f,l;return{setters:[function(e){n=e.d,r=e.g,o=e.u,s=e.p,u=e.i,a=e.j,c=e.C,i=e.F},function(e){f=e.a,l=e.b},function(){},function(){}],execute:function(){t("default",n({name:"home-layout-center",data:function(){return{status:!1}},mounted:function(){this.getUserInfo()},methods:{getUserInfo:function(){var t,n=this;return(t=regeneratorRuntime.mark((function e(){var t,a,c;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.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 t=e.sent,a=t.data.authentication,o(a.token_type+" "+a.access_token),s({api:"setCache",content:{key:"h5-colexiu-token",value:a.token_type+" "+a.access_token}}),e.next=9,f.get("/api-student/student/queryUserInfo",{initRequest:!0});case 9:c=e.sent,u(c.data),n.$router.push("/home"),e.next=17;break;case 14:e.prev=14,e.t0=e.catch(0),n.status=!0;case 17:case"end":return e.stop()}}),e,null,[[0,14]])})),function(){var n=this,r=arguments;return new Promise((function(o,s){var u=t.apply(n,r);function a(t){e(u,o,s,a,c,"next",t)}function c(t){e(u,o,s,a,c,"throw",t)}a(void 0)}))})()}},render:function(){return a(i,null,[this.status?a("div",{class:l.error},[a(c,{type:"notFond",classImgSize:"CERT",tips:"加载失败,请稍后重试",buttonText:"重新加载",plain:!0,onClick:this.getUserInfo},null)]):a("div",null,null)])}}))}}}))}();
+========
+!function(){function e(e,t,n,r,o,s,u){try{var a=e[s](u),c=a.value}catch(i){return void n(i)}a.done?t(c):Promise.resolve(c).then(r,o)}System.register(["./ResizeObserver.es-legacy.44b91b4c.js","./index-legacy.36079324.js","./auth-legacy.767687ba.js","./routes-common-legacy.303f6dfb.js"],(function(t){"use strict";var n,r,o,s,u,a,c,i,f,l;return{setters:[function(e){n=e.d,r=e.g,o=e.u,s=e.p,u=e.i,a=e.j,c=e.C,i=e.F},function(e){f=e.a,l=e.b},function(){},function(){}],execute:function(){t("default",n({name:"home-layout-center",data:function(){return{status:!1}},mounted:function(){this.getUserInfo()},methods:{getUserInfo:function(){var t,n=this;return(t=regeneratorRuntime.mark((function e(){var t,a,c;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.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 t=e.sent,a=t.data.authentication,o(a.token_type+" "+a.access_token),s({api:"setCache",content:{key:"h5-colexiu-token",value:a.token_type+" "+a.access_token}}),e.next=9,f.get("/api-student/student/queryUserInfo",{initRequest:!0});case 9:c=e.sent,u(c.data),n.$router.push("/home"),e.next=17;break;case 14:e.prev=14,e.t0=e.catch(0),n.status=!0;case 17:case"end":return e.stop()}}),e,null,[[0,14]])})),function(){var n=this,r=arguments;return new Promise((function(o,s){var u=t.apply(n,r);function a(t){e(u,o,s,a,c,"next",t)}function c(t){e(u,o,s,a,c,"throw",t)}a(void 0)}))})()}},render:function(){return a(i,null,[this.status?a("div",{class:l.error},[a(c,{type:"notFond",classImgSize:"CERT",tips:"加载失败,请稍后重试",buttonText:"重新加载",plain:!0,onClick:this.getUserInfo},null)]):a("div",null,null)])}}))}}}))}();
+>>>>>>>> iteration-20241126:dist/assets/home-layout-center-legacy.bd9e3956.js

+ 4 - 0
dist/assets/home-layout-center-legacy.edb328af.js

@@ -1 +1,5 @@
+<<<<<<<< HEAD:dist/assets/home-layout-center-legacy.edb328af.js
 !function(){function e(e,t,n,r,o,s,u){try{var a=e[s](u),c=a.value}catch(i){return void n(i)}a.done?t(c):Promise.resolve(c).then(r,o)}System.register(["./ResizeObserver.es-legacy.44b91b4c.js","./index-legacy.5fa4fcaf.js","./auth-legacy.767687ba.js","./routes-common-legacy.4c516b81.js"],(function(t){"use strict";var n,r,o,s,u,a,c,i,f,l;return{setters:[function(e){n=e.d,r=e.g,o=e.u,s=e.p,u=e.i,a=e.j,c=e.C,i=e.F},function(e){f=e.a,l=e.b},function(){},function(){}],execute:function(){t("default",n({name:"home-layout-center",data:function(){return{status:!1}},mounted:function(){this.getUserInfo()},methods:{getUserInfo:function(){var t,n=this;return(t=regeneratorRuntime.mark((function e(){var t,a,c;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.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 t=e.sent,a=t.data.authentication,o(a.token_type+" "+a.access_token),s({api:"setCache",content:{key:"h5-colexiu-token",value:a.token_type+" "+a.access_token}}),e.next=9,f.get("/api-student/student/queryUserInfo",{initRequest:!0});case 9:c=e.sent,u(c.data),n.$router.push("/home"),e.next=17;break;case 14:e.prev=14,e.t0=e.catch(0),n.status=!0;case 17:case"end":return e.stop()}}),e,null,[[0,14]])})),function(){var n=this,r=arguments;return new Promise((function(o,s){var u=t.apply(n,r);function a(t){e(u,o,s,a,c,"next",t)}function c(t){e(u,o,s,a,c,"throw",t)}a(void 0)}))})()}},render:function(){return a(i,null,[this.status?a("div",{class:l.error},[a(c,{type:"notFond",classImgSize:"CERT",tips:"加载失败,请稍后重试",buttonText:"重新加载",plain:!0,onClick:this.getUserInfo},null)]):a("div",null,null)])}}))}}}))}();
+========
+!function(){function e(e,t,n,r,o,s,u){try{var a=e[s](u),c=a.value}catch(i){return void n(i)}a.done?t(c):Promise.resolve(c).then(r,o)}System.register(["./ResizeObserver.es-legacy.44b91b4c.js","./index-legacy.36079324.js","./auth-legacy.767687ba.js","./routes-common-legacy.303f6dfb.js"],(function(t){"use strict";var n,r,o,s,u,a,c,i,f,l;return{setters:[function(e){n=e.d,r=e.g,o=e.u,s=e.p,u=e.i,a=e.j,c=e.C,i=e.F},function(e){f=e.a,l=e.b},function(){},function(){}],execute:function(){t("default",n({name:"home-layout-center",data:function(){return{status:!1}},mounted:function(){this.getUserInfo()},methods:{getUserInfo:function(){var t,n=this;return(t=regeneratorRuntime.mark((function e(){var t,a,c;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.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 t=e.sent,a=t.data.authentication,o(a.token_type+" "+a.access_token),s({api:"setCache",content:{key:"h5-colexiu-token",value:a.token_type+" "+a.access_token}}),e.next=9,f.get("/api-student/student/queryUserInfo",{initRequest:!0});case 9:c=e.sent,u(c.data),n.$router.push("/home"),e.next=17;break;case 14:e.prev=14,e.t0=e.catch(0),n.status=!0;case 17:case"end":return e.stop()}}),e,null,[[0,14]])})),function(){var n=this,r=arguments;return new Promise((function(o,s){var u=t.apply(n,r);function a(t){e(u,o,s,a,c,"next",t)}function c(t){e(u,o,s,a,c,"throw",t)}a(void 0)}))})()}},render:function(){return a(i,null,[this.status?a("div",{class:l.error},[a(c,{type:"notFond",classImgSize:"CERT",tips:"加载失败,请稍后重试",buttonText:"重新加载",plain:!0,onClick:this.getUserInfo},null)]):a("div",null,null)])}}))}}}))}();
+>>>>>>>> iteration-20241126:dist/assets/home-layout-center-legacy.bd9e3956.js

+ 4 - 0
dist/assets/home-layout-center.7d29b07d.js

@@ -1 +1,5 @@
+<<<<<<<< HEAD:dist/assets/home-layout-center.7d29b07d.js
 import{d as r,g as s,u,p as i,i as c,j as e,C as l,F as p}from"./ResizeObserver.es.b7226882.js";import{a,b as h}from"./index.40db6008.js";import"./auth.9a0890d5.js";import"./routes-common.549e133f.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,u,p as i,i as c,j as e,C as l,F as p}from"./ResizeObserver.es.b7226882.js";import{a,b as h}from"./index.ab9be12e.js";import"./auth.9a0890d5.js";import"./routes-common.34a62762.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-20241126:dist/assets/home-layout-center.f37c4270.js

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

@@ -0,0 +1,5 @@
+<<<<<<<< HEAD:dist/assets/home-layout-center.7d29b07d.js
+import{d as r,g as s,u,p as i,i as c,j as e,C as l,F as p}from"./ResizeObserver.es.b7226882.js";import{a,b as h}from"./index.40db6008.js";import"./auth.9a0890d5.js";import"./routes-common.549e133f.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,u,p as i,i as c,j as e,C as l,F as p}from"./ResizeObserver.es.b7226882.js";import{a,b as h}from"./index.ab9be12e.js";import"./auth.9a0890d5.js";import"./routes-common.34a62762.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-20241126:dist/assets/home-layout-center.f37c4270.js

File diff suppressed because it is too large
+ 1 - 0
dist/assets/index-legacy.053ade53.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/index-legacy.30917c0e.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/index-legacy.36079324.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/index-legacy.82341cfe.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/index-legacy.c691bb3c.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/index.046dadb0.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/index.7b973e74.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/index.9fc513ab.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/index.ab9be12e.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/index.ad94d5f4.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/index.f0513a9d.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/login-legacy.31456d4a.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/login-legacy.cbaf3e35.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/login.4394a365.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/login.b2a78be4.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/new-index-legacy.50e7cfcd.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/new-index-legacy.8375839b.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/new-index.03aca409.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/new-index.c0e57cbd.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/polyfills-legacy.721957f7.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/polyfills-legacy.810f0c80.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/routes-common-legacy.303f6dfb.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/routes-common-legacy.4c516b81.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/routes-common.34a62762.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/teacher-home-legacy.fbcf658b.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/teacher-home.dca8a7f7.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/teacher-legacy.8df3a5f6.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/teacher.e934e7a8.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/tenant-legacy.10748f36.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/tenant-legacy.6e35d615.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/tenant.da116a06.js


+ 12 - 0
dist/index.html

@@ -52,10 +52,17 @@
             _T_));
     })(Object);
   </script>
+<<<<<<< HEAD
   <script type="module" crossorigin src="./assets/index.40db6008.js"></script>
   <link rel="modulepreload" href="./assets/ResizeObserver.es.b7226882.js">
   <link rel="modulepreload" href="./assets/auth.9a0890d5.js">
   <link rel="modulepreload" href="./assets/routes-common.549e133f.js">
+=======
+  <script type="module" crossorigin src="./assets/index.ab9be12e.js"></script>
+  <link rel="modulepreload" href="./assets/ResizeObserver.es.b7226882.js">
+  <link rel="modulepreload" href="./assets/auth.9a0890d5.js">
+  <link rel="modulepreload" href="./assets/routes-common.34a62762.js">
+>>>>>>> iteration-20241126
   <link rel="stylesheet" href="./assets/ResizeObserver.es.cfa7e25e.css">
   <link rel="stylesheet" href="./assets/auth.f09b6d78.css">
   <link rel="stylesheet" href="./assets/index.820e7d12.css">
@@ -67,8 +74,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.810f0c80.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/index-legacy.5fa4fcaf.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.721957f7.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/index-legacy.36079324.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration-20241126
 </body>
 
 </html>

+ 11 - 0
dist/teacher.html

@@ -52,9 +52,15 @@
             _T_));
     })(Object);
   </script>
+<<<<<<< HEAD
   <script type="module" crossorigin src="./assets/teacher.8a2c0a22.js"></script>
   <link rel="modulepreload" href="./assets/ResizeObserver.es.b7226882.js">
   <link rel="modulepreload" href="./assets/routes-common.549e133f.js">
+=======
+  <script type="module" crossorigin src="./assets/teacher.e934e7a8.js"></script>
+  <link rel="modulepreload" href="./assets/ResizeObserver.es.b7226882.js">
+  <link rel="modulepreload" href="./assets/routes-common.34a62762.js">
+>>>>>>> iteration-20241126
   <link rel="stylesheet" href="./assets/ResizeObserver.es.cfa7e25e.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>
@@ -64,8 +70,13 @@
   <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.810f0c80.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.f3a6dd97.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.721957f7.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.8df3a5f6.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration-20241126
 </body>
 
 </html>

+ 9 - 0
dist/tenant.html

@@ -52,7 +52,11 @@
             _T_));
     })(Object);
   </script>
+<<<<<<< HEAD
   <script type="module" crossorigin src="./assets/tenant.6a7ea389.js"></script>
+=======
+  <script type="module" crossorigin src="./assets/tenant.da116a06.js"></script>
+>>>>>>> iteration-20241126
   <link rel="modulepreload" href="./assets/ResizeObserver.es.b7226882.js">
   <link rel="modulepreload" href="./assets/auth.9a0890d5.js">
   <link rel="stylesheet" href="./assets/ResizeObserver.es.cfa7e25e.css">
@@ -66,8 +70,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.810f0c80.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/tenant-legacy.10748f36.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.721957f7.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/tenant-legacy.6e35d615.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration-20241126
 </body>
 
 </html>

+ 21 - 2
src/business-components/calendar/index.tsx

@@ -104,6 +104,18 @@ export default defineComponent({
     this.maxDate = dayjs().add(1, 'day').endOf('month').toDate()
     this.minDate = dayjs().add(1, 'day').toDate()
 
+    // console.log(this.list, "this.list")
+    // for(const key in this.list) {
+    //   console.log(key, this.list[key])
+    //   let dataList = [] as any
+    //   if (this.list[key] && Array.isArray(this.list[key].courseTime)) {
+    //     dataList = [...this.list[key].courseTime].filter(n =>
+    //       dayjs().isBefore(dayjs(n.startTime))
+    //     )
+    //   }
+    //   this.list[key].courseTime = dataList
+    // }
+
     // 初始化日历
     // console.log(this.list, 323, this.maxDays)
   },
@@ -111,14 +123,21 @@ export default defineComponent({
     formatter(date: any) {
       const dateStr = dayjs(date.date).format('YYYY-MM-DD')
       const dateObj = this.list[dateStr]
+      // 格式化选择的时间
+      let courseTime = [] as any
+      if (dateObj && Array.isArray(dateObj.courseTime)) {
+        courseTime = [...dateObj.courseTime].filter(n =>
+          dayjs().isBefore(dayjs(n.startTime))
+        )
+      }
       date.type = ''
       // 判断是否有课程 并且 时间在当前时间之后
       if (dateObj && dayjs().subtract(1, 'day').isBefore(dayjs(date.date))) {
         // fullCourse当天是否排满 0: 未,1:满 , courseTime 当天没有课程
         if (
           dateObj.fullCourse ||
-          !dateObj?.courseTime ||
-          dateObj?.courseTime?.length <= 0
+          !courseTime ||
+          courseTime?.length <= 0
         ) {
           date.bottomInfo = '满'
           date.className = 'full'

二進制
src/business-components/section-detail/images/icon_course_introduction.png


二進制
src/business-components/section-detail/images/icon_course_list.png


二進制
src/business-components/section-detail/images/icon_course_list_1.png


+ 3 - 3
src/business-components/section-detail/index.module.less

@@ -6,15 +6,15 @@
   overflow: hidden;
 
   .name {
-    font-size: 16px;
-    color: #333333;
     font-weight: 500;
+    font-size: 15px;
+    color: #333333;
     padding-left: 8px;
   }
 
   :global {
     .van-cell {
-      padding: 10px 12px;
+      padding: 12px;
       font-size: 14px;
     }
   }

+ 5 - 1
src/business-components/section-detail/index.tsx

@@ -25,6 +25,10 @@ export default defineComponent({
       type: String,
       default: '课程介绍'
     },
+    groupBorder: {
+      type: Boolean,
+      default: false
+    },
     border: {
       type: Boolean,
       default: false
@@ -67,7 +71,7 @@ export default defineComponent({
   render() {
     return (
       <div class={styles['section-detail']}>
-        <CellGroup border={this.border}>
+        <CellGroup border={this.groupBorder}>
           {this.titleShow ? (
             <Cell
               center

二進制
src/business-components/user-detail/images/icon-course.png


+ 116 - 9
src/business-components/user-detail/index.module.less

@@ -1,14 +1,34 @@
 .userDetail {
+  position: relative;
   .banner {
     width: 100%;
     height: 212px;
     overflow: hidden;
     vertical-align: middle;
   }
+  .subjectName {
+    position: absolute;
+    top: 8px;
+    left: 8px;
+    background: rgba(0,0,0,0.4);
+    border-radius: 3px;
+    font-weight: 400;
+    font-size: 12px;
+    color: #FFFFFF;
+    line-height: 17px;
+    padding: 1px 6px;
+    z-index: 1;
+  }
 
   .userInfo {
     overflow: hidden;
 
+    :global {
+      .van-cell {
+        padding: 12px 14px;
+      }
+    }
+
     .avatar {
       width: 40px;
       height: 40px;
@@ -18,13 +38,35 @@
     }
 
     .name {
-      padding-left: 8px;
-      font-size: 16px;
-      font-weight: 500;
+      padding-left: 10px;
+      font-size: 15px;
+      font-weight: 400;
       color: #333333;
       line-height: 22px;
       display: flex;
       align-items: center;
+
+    }
+
+    .classInfo {
+      display: flex;
+      align-items: center;
+      font-weight: 400;
+      font-size: 12px;
+      color: #999999;
+      padding-left: 10px;
+      line-height: 1.5;
+      .iconCourse {
+        width: 12px;
+        height: 14px;
+        flex-shrink: 0;
+        margin-right: 6px;
+      }
+      span {
+        color: #333333;
+        font-weight: bold;
+        padding: 0 4px;
+      }
     }
 
     .username {
@@ -41,11 +83,47 @@
       }
     }
 
+    .timers {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding-top: 8px;
+      .timerSpan {
+        display: flex;
+        align-items: center;
+        font-size: 14px;
+        color: #666666;
+        font-weight: 400;
+      }
+    }
+
     .buyNum {
-      color: #ff802c;
+      color: #FF6827;
       font-size: 14px;
       text-align: right;
-      margin-top: 8px;
+    }
+
+    .buyNumVideo {
+      display: flex;
+      align-items: center;
+      flex-shrink: 0;
+      &::before {
+        content: '';
+        display: block;
+        width: 1px;
+        height: 11px;
+        background: #D3D3D3;
+        margin: 0 6px;
+      }
+    }
+    .buyNumOther {
+      font-weight: 400;
+      font-size: 12px;
+      line-height: 13px;
+      color: #FF6827;
+      background: rgba(255, 128, 44, 0.07);
+      border-radius: 2px;
+      padding: 3px 4px;
     }
 
     .buyNumInfo {
@@ -65,6 +143,26 @@
       line-height: 20px;
       color: #FF0000;
 
+      .prices {
+        font-family: DINAlternate, DINAlternate;
+        font-weight: bold;
+        font-size: 20px;
+        color: #F44541;
+        line-height: 24px;
+
+        .fix {
+          font-weight: bold;
+          font-size: 14px;
+          color: #F44541;
+        }
+      }
+
+      .classNum {
+        font-weight: 400;
+        font-size: 12px;
+        color: #999999;
+      }
+
       .infoPrice {
         font-size: 16px;
         font-weight: bold;
@@ -78,10 +176,18 @@
     }
 
     .userTitle {
-      font-size: 18px;
-      color: #1a1a1a;
-      font-weight: 500;
+      font-weight: 600;
+      font-size: 16px;
       display: block !important;
+      width: 100%;
+
+      >span {
+        display: block;
+        width: 100%;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
     }
 
     :global {
@@ -103,6 +209,7 @@
 
       .van-cell__label {
         margin-top: 0;
+        overflow: hidden;
       }
     }
   }
@@ -128,4 +235,4 @@
   margin-right: 6px;
   width: 39px;
   height: 16px;
-}
+}

+ 58 - 49
src/business-components/user-detail/index.tsx

@@ -4,7 +4,8 @@ import styles from './index.module.less'
 
 // import iconUserNum from '@common/images/icon_user_num.png'
 import defaultIcon from '@common/images/icon_teacher.png'
-import iconTimer from '@common/images/icon_timer2.png'
+import iconTimer from '@common/images/icon_timer4.png'
+import iconCourse from './images/icon-course.png'
 // import IconXueli from '@common/images/icon-xueli.png'
 // import IconJiaozi from '@common/images/icon-jiaozi.png'
 // import item from '@/views/coupons/item'
@@ -17,12 +18,15 @@ import iconTimer from '@common/images/icon_timer2.png'
  * @param {type} buyNum 购买用户数
  * @param {type} payType 收费方式
  * @param {type} type 课程类型
+ * @param {type} subjectName 科目名称
  * @param {type} lessonPrice 价格
  * @param {type} lessonCoverUrl 视频封面
  * @param {type} lessonDesc 课程描述
  * @param {type} lessonNum 课程数
  * @param {type} lessonName 课程名称
  * @param {type} relationType 赠送类型
+ * @param {type} completeCourseNum 已上课时
+ * @param {type} isShowCourse 是否显示课程
  */
 interface UserType {
   headUrl: string
@@ -33,6 +37,7 @@ interface UserType {
   buyNum?: number
   payType?: string
   type?: string
+  subjectName?: string
   lessonPrice: number
   lessonNum?: number
   lessonDesc?: string
@@ -41,6 +46,8 @@ interface UserType {
   lessonName: string
   auditVersion: number
   relationType?: string
+  completeCourseNum?: number
+  isShowCourse?: boolean
 }
 
 export default defineComponent({
@@ -71,6 +78,7 @@ export default defineComponent({
   render() {
     return (
       <div class={styles.userDetail}>
+        {this.userInfo.subjectName ? <span class={styles.subjectName}>{this.userInfo.subjectName}</span> : ""}
         <Image
           class={[styles.banner]}
           src={this.userInfo.lessonCoverUrl}
@@ -80,24 +88,38 @@ export default defineComponent({
           <Cell
             title={this.userInfo.lessonName}
             titleClass={styles.userTitle}
+            labelClass={styles.userLabel}
             v-slots={{
               label: () =>
                 this.userInfo.startTime && (
                   <span
-                    style={{
-                      display: 'flex',
-                      alignItems: 'center',
-                      fontSize: '13px',
-                      paddingTop: 'var(--van-cell-label-margin-top)'
-                    }}
+                    class={styles.timers}
                   >
-                    <Icon
-                      name={iconTimer}
-                      size="16"
-                      style={{ marginRight: '5px' }}
-                    />
-                    开课时间:
-                    {this.userInfo.courseStartTime}
+                    <div class={styles.timerSpan}>
+                      <Icon
+                        name={iconTimer}
+                        size="16"
+                        style={{ marginRight: '5px' }}
+                      />
+                      开课时间:
+                      {this.userInfo.courseStartTime}
+                    </div>
+
+                    {this.showBuy && ['live', 'group'].includes(this.userInfo.type || '') && (
+                      <div class={[styles.buyNum, styles.buyNumOther]}>
+                        {this.userInfo.type === 'live' ? (
+                          <span>
+                            {this.userInfo.buyNum}人已
+                            {this.userInfo.lessonPrice <= 0 &&
+                            this.userInfo.auditVersion === 0
+                              ? '领取'
+                              : '购买'}
+                          </span>
+                        ) : this.userInfo.type === "group" ?
+                          ((this.userInfo.mixStudentNum || 0 > 0) ? <>剩余{this.userInfo.mixStudentNum}个名额</> : '')
+                        : ''}
+                      </div>
+                    )}
                   </span>
                 )
             }}
@@ -115,23 +137,27 @@ export default defineComponent({
                 />
               ),
               title: () => (
-                <div
-                  class={styles.name}
-                  onClick={() => {
-                    this.onUserDetail(this.userInfo)
-                  }}
-                >
-                  <div class={styles.username}>
-                    {this.userInfo.username || `游客${this.userInfo.id || ''}`}
-                    {/* <div>
-                      {(this.userInfo as any).isDegree && (
-                        <img class={styles.iconTeacher} src={IconXueli} />
-                      )}
-                      {(this.userInfo as any).isTeacher && (
-                        <img class={styles.iconTeacher} src={IconJiaozi} />
-                      )}
-                    </div> */}
+                <div>
+                  <div
+                    class={styles.name}
+                    onClick={() => {
+                      this.onUserDetail(this.userInfo)
+                    }}
+                  >
+                    <div class={styles.username}>
+                      {this.userInfo.username || `游客${this.userInfo.id || ''}`}
+                    </div>
+                    {this.showBuy && this.userInfo.type === 'video' && (
+                      <div class={[styles.buyNum, styles.buyNumVideo]}>
+                        {this.userInfo.buyNum}人学习
+                      </div>
+                    )}
                   </div>
+                  {this.userInfo.isShowCourse ? <div class={styles.classInfo}>
+                    <img src={iconCourse} class={styles.iconCourse} />
+                    已上课时<span>{this.userInfo.completeCourseNum || 0}</span>/总课时 <span>{this.userInfo.lessonNum || 0}</span>
+                  </div> : ''}
+                  
                 </div>
               ),
               value: () => (
@@ -142,7 +168,7 @@ export default defineComponent({
                   ) : (
                     <>
                       {this.userInfo.lessonPrice > 0 && (
-                        <>¥{this.userInfo.lessonPrice}</>
+                        <span class={styles.prices}><span class={styles.fix}>¥</span>{this.userInfo.lessonPrice}</span>
                       )}
                       {this.userInfo.lessonPrice <= 0 &&
                         this.userInfo.auditVersion !== 0 && <>¥{0}</>}
@@ -153,27 +179,10 @@ export default defineComponent({
                     </>
                   )}
                   <span
-                    style={{ color: '#999', fontSize: '14px', fontWeight: 400 }}
+                    class={styles.classNum}
                   >
                     /{this.userInfo.lessonNum}课时
                   </span>
-                  {this.showBuy && (
-                    <div class={styles.buyNum}>
-                      {this.userInfo.type === 'live' ? (
-                        <>
-                          {this.userInfo.buyNum}人已
-                          {this.userInfo.lessonPrice <= 0 &&
-                          this.userInfo.auditVersion === 0
-                            ? '领取'
-                            : '购买'}
-                        </>
-                      ) : this.userInfo.type === "group" ?
-                        ((this.userInfo.mixStudentNum || 0 > 0) ? <>剩余{this.userInfo.mixStudentNum}个名额</> : '')
-                       : (
-                        <>{this.userInfo.buyNum}人学习</>
-                      )}
-                    </div>
-                  )}
                 </div>
               )
             }}

二進制
src/common/images/icon_timer4.png


+ 5 - 3
src/components/col-share/index.module.less

@@ -270,12 +270,14 @@
   justify-content: space-between;
   padding-top: 12px;
 
-  :global(.van-button) {
-    padding: 8px 46px;
-  }
+  // :global(.van-button) {
+    // font-size: 16px;
+    // padding: 8px 46px;
+  // }
 
   .savePictureBtn {
     border-color: #fff;
+    margin-right: 12px;
   }
 
   &.shareGroupTenantBtn {

+ 2 - 0
src/components/col-share/index.tsx

@@ -270,6 +270,7 @@ export default defineComponent({
                 plain
                 class={styles.savePictureBtn}
                 round
+                block
                 loading={this.saveLoading}
                 loadingText="保存中"
                 onClick={this.onSaveImg}
@@ -279,6 +280,7 @@ export default defineComponent({
               <Button
                 type="primary"
                 round
+                block
                 loading={this.shareLoading}
                 loadingText="分享中"
                 onClick={() => {

+ 9 - 6
src/components/col-upload-video/index.module.less

@@ -5,16 +5,19 @@
   border-radius: 10px;
   box-sizing: border-box;
   position: relative;
+  &.uploader-section-value {
+    border: none;
+  }
   .img-close {
     position: absolute;
-    top: 8px;
-    right: 10px;
-    z-index: 99999;
+    top: 4px;
+    right: 4px;
+    z-index: 199;
     font-size: 16px;
-    background-color: #333;
+    // background-color: #333;
     color: #fff;
-    width: 22px;
-    height: 22px;
+    width: 20px;
+    height: 20px;
     display: flex;
     justify-content: center;
     align-items: center;

+ 6 - 7
src/components/col-upload-video/index.tsx

@@ -1,14 +1,13 @@
-import request from '@/helpers/request'
-import { Icon, Toast, Uploader, Image } from 'vant'
+import { Icon, Toast, Uploader } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
 import { useCustomFieldValue } from '@vant/use'
 import { browser } from '@/helpers/utils'
-import umiRequest from 'umi-request'
 import iconUploader from '@common/images/icon_uploader_video.png'
-import iconUploadPoster from '@common/images/icon_upload_poster.png'
+import iconDelete from '@common/images/icon-delete.png'
+// import iconUploadPoster from '@common/images/icon_upload_poster.png'
 import { postMessage } from '@/helpers/native-message'
-import { getOssUploadUrl, state } from '@/state'
+import { getOssUploadUrl } from '@/state'
 import { getUploadSign, onOnlyFileUpload } from '@/helpers/oss-file-upload'
 
 export default defineComponent({
@@ -156,10 +155,10 @@ export default defineComponent({
   render() {
     useCustomFieldValue(() => this.modelValue)
     return (
-      <div class={styles['uploader-section']}>
+      <div class={[styles['uploader-section'], this.modelValue ? styles['uploader-section-value'] : '']}>
         {this.modelValue && this.deletable ? (
           <Icon
-            name="cross"
+            name={iconDelete}
             onClick={this.onClose}
             class={styles['img-close']}
           />

+ 5 - 3
src/components/col-upload/index.module.less

@@ -5,12 +5,14 @@
   border-radius: 10px;
   box-sizing: border-box;
   position: relative;
-
+  &.uploader-section-value {
+    border: none;
+  }
   .img-close {
     position: absolute;
     top: 4px;
     right: 4px;
-    z-index: 99;
+    z-index: 98;
     font-size: 16px;
     // background-color: #333;
     color: #fff;
@@ -66,7 +68,7 @@
   .uploadImg {
     width: 100%;
     height: 100%;
-    // border-radius: 10px;
+    border-radius: 10px;
     overflow: hidden;
   }
 }

+ 2 - 4
src/components/col-upload/index.tsx

@@ -4,11 +4,9 @@ import styles from './index.module.less'
 import ColCropper from '../col-cropper'
 import { useCustomFieldValue } from '@vant/use'
 import { postMessage } from '@/helpers/native-message'
-import umiRequest from 'umi-request'
 import iconUploader from '@common/images/icon_uploader.png'
 import iconDelete from '@common/images/icon-delete.png'
-import request from '@/helpers/request'
-import { getOssUploadUrl, state } from '@/state'
+import { getOssUploadUrl } from '@/state'
 import { getUploadSign, onOnlyFileUpload } from '@/helpers/oss-file-upload'
 
 export default defineComponent({
@@ -173,7 +171,7 @@ export default defineComponent({
   render() {
     useCustomFieldValue(() => this.modelValue)
     return (
-      <div class={styles['uploader-section']}>
+      <div class={[styles['uploader-section'], this.modelValue ? styles['uploader-section-value'] : '']}>
         {this.modelValue && !this.disabled && this.deletable ? (
           <Icon
             name={iconDelete}

+ 16 - 1
src/helpers/hooks.ts

@@ -1,6 +1,7 @@
 import { reactive } from 'vue'
 import { postMessage } from './native-message'
 import request from './request'
+import { browser } from './utils'
 
 // 搜索关键字
 export const SubjectEnum = {
@@ -27,7 +28,7 @@ export const useSubjectId = (
     let subject = localStorage.getItem(key)
     subject = subject ? JSON.parse(subject) : { name: '', id: '' }
     return subject
-  } else if(type === "remove") {
+  } else if (type === 'remove') {
     localStorage.removeItem(key)
   } else {
     localStorage.setItem(key, value)
@@ -135,3 +136,17 @@ export const useEventTracking = (name: eventName) => {
     }
   })
 }
+
+/**
+ * 学生端 - 埋点统计
+ * @param params { objectType: string, objectId: number}
+ */
+export const useStatisticTracking = (params: {
+  objectType: 'VIP_COURSE' | 'PRACTICE' | 'GROUP' | 'LIVE' | 'VIDEO' | 'MUSIC'
+  objectId: number | string
+}) => {
+  request.post('/api-student/exposure/record', {
+    hideLoading: false,
+    data: [{ ...params, platform: browser().ios ? 'ios' : 'android' }]
+  })
+}

+ 3 - 3
src/helpers/utils.ts

@@ -94,13 +94,13 @@ export const getHttpOrigin = () => {
  */
 export const formatterDatePicker = (type: any, option: any) => {
   if (type === 'year') {
-    option.text += '年'
+    option += '年'
   }
   if (type === 'month') {
-    option.text += '月'
+    option += '月'
   }
   if (type === 'day') {
-    option.text += '日'
+    option += '日'
   }
   return option
 }

+ 32 - 0
src/router/routes-teacher.ts

@@ -395,6 +395,38 @@ export default [
         meta: {
           title: '协议'
         }
+      },
+      {
+        path: '/home-statistics',
+        name: 'home-statistics',
+        component: () => import('@/teacher/statistics/home-statistics/index'),
+        meta: {
+          title: '浏览/购买'
+        }
+      },
+      {
+        path: '/home-statistics-detail',
+        name: 'home-statistics-detail',
+        component: () => import('@/teacher/statistics/home-statistics-detail/index'),
+        meta: {
+          title: '数据详情'
+        }
+      },
+      {
+        path: '/practice-statistics-detail',
+        name: 'practice-statistics-detail',
+        component: () => import('@/teacher/statistics/practice-statistics-detail/index'),
+        meta: {
+          title: '练习统计'
+        }
+      },
+      {
+        path: '/exercise-detail',
+        name: 'exercise-detail',
+        component: () => import('@/teacher/statistics/exercise-detail/exercise-detail'),
+        meta: {
+          title: '练习统计'
+        }
       }
     ]
   },

+ 4 - 0
src/student/group-class/group-detail.module.less

@@ -47,10 +47,14 @@
   .btnMore {
     display: flex;
     justify-content: space-around;
+    padding: 20px 28px 30px;
+    background: linear-gradient( 180deg, rgba(255,255,255,0) 0%, #FFFFFF 100%), #FFFDFD;
+    box-shadow: 0px 0px 2px 0px rgba(216,216,216,0.5);
 
     :global {
       .van-button {
         width: 100%;
+        font-size: 16px;
       }
     }
   }

+ 31 - 24
src/student/group-class/group-detail.tsx

@@ -19,6 +19,8 @@ import { browser } from '@/helpers/utils'
 import { tradeOrder } from '../trade/tradeOrder'
 import { courseType } from '@/constant'
 import GroupPlanStep from '@/business-components/group-plan-step'
+import TheSticky from '@/components/the-sticky'
+import { useStatisticTracking } from '@/helpers/hooks'
 interface IProps {
   courseTime: string
   coursePlan: string
@@ -56,15 +58,14 @@ export default defineComponent({
         headUrl: live.avatar,
         username: live.userName || `游客${live.teacherId || ''}`,
         id: live.teacherId,
-        startTime:
-          `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(startTime).format(
-            'HH:mm'
-          )}~${dayjs(endTime).format('HH:mm')}` || '',
+        startTime: `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(startTime).format('HH:mm')}` || '',
         buyNum: live.studentCount,
         type: 'group',
         lessonId: live.courseGroupId,
         lessonPrice: live.coursePrice,
+        completeCourseNum: live.completeCourseNum,
         lessonNum: live.courseNum,
+        isShowCourse: live.status !== 'APPLY',
         mixStudentNum: studentNum > 0 ? studentNum : 0,
         lessonDesc: live.courseIntroduce,
         lessonCoverUrl: live.backgroundPic || live.backgroundPicTemplate,
@@ -131,6 +132,12 @@ export default defineComponent({
     } else {
       this.shareUrl = `${location.origin}/teacher/#/shareGroup?recomUserId=${state.user.data?.userId}&groupId=${this.groupId}&userType=${state.platformType}&p=tenant`
     }
+
+    /** 埋点 */
+    useStatisticTracking({
+      objectType: "GROUP",
+      objectId: this.groupId as any
+    })
   },
   methods: {
     async _init() {
@@ -211,21 +218,21 @@ export default defineComponent({
     },
     async onBuy() {
       try {
-        const live = this.live
+        // const live = this.live
         // 判断是否是0无订单
-        if (live.coursePrice <= 0) {
-          this.initLive()
-          await onSubmitZero(() => {
-            Dialog.alert({
-              message: '领取成功',
-              confirmButtonText: '确定',
-              confirmButtonColor: '#2dc7aa'
-            }).then(() => {
-              this._init()
-            })
-          })
-          return
-        }
+        // if (live.coursePrice <= 0) {
+        //   this.initLive()
+        //   await onSubmitZero(() => {
+        //     Dialog.alert({
+        //       message: '领取成功',
+        //       confirmButtonText: '确定',
+        //       confirmButtonColor: '#2dc7aa'
+        //     }).then(() => {
+        //       this._init()
+        //     })
+        //   })
+        //   return
+        // }
         const res = await request.post(
           '/api-student/userOrder/getPendingOrder',
           {
@@ -349,8 +356,8 @@ export default defineComponent({
         </div> */}
         {this.courseInfo.length > 0 &&
           (this.live.existBuy !== 1 && this.live.existBuy !== 2) && (
-            <ColSticky position="bottom" background="white">
-              <div class={['btnGroup', styles.btnMore]}>
+            <TheSticky position="bottom">
+              <div class={["btnGroup", styles.btnMore]}>
                 <Button
                   block
                   round
@@ -361,17 +368,17 @@ export default defineComponent({
                   {this.live.coursePrice <= 0 ? '免费领取' : `立即购买`}
                 </Button>
               </div>
-            </ColSticky>
+            </TheSticky>
           )}
 
         {this.joinRoom == '1' && this.liveStatus.liveStatus !== 2 && (
-          <ColSticky position="bottom" background="white">
-            <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+          <TheSticky position="bottom">
+            <div class={["btnGroup", styles.btnMore]}>
               <Button block round type="primary" onClick={this.onJoinRoom}>
                 进入直播间
               </Button>
             </div>
-          </ColSticky>
+          </TheSticky>
         )}
 
         <Popup

+ 5 - 1
src/student/live-class/live-detail.module.less

@@ -47,10 +47,14 @@
   .btnMore {
     display: flex;
     justify-content: space-around;
+    padding: 20px 28px 30px;
+    background: linear-gradient( 180deg, rgba(255,255,255,0) 0%, #FFFFFF 100%), #FFFDFD;
+    box-shadow: 0px 0px 2px 0px rgba(216,216,216,0.5);
 
     :global {
       .van-button {
-        width: 60%;
+        width: 100%;
+        font-size: 16px;
       }
     }
   }

+ 33 - 37
src/student/live-class/live-detail.tsx

@@ -17,6 +17,8 @@ import iconShare from '@/views/shop-mall/images/icon-share.svg'
 import { state } from '@/state'
 import { browser } from '@/helpers/utils'
 import { tradeOrder } from '../trade/tradeOrder'
+import TheSticky from '@/components/the-sticky'
+import { useStatisticTracking } from '@/helpers/hooks'
 interface IProps {
   courseTime: string
   coursePlan: string
@@ -46,7 +48,7 @@ export default defineComponent({
       // console.log('live', live)
       const planList = live.planList || []
       const startTime = planList[0]?.startTime || new Date()
-      const endTime = planList[0]?.endTime || new Date()
+      // const endTime = planList[0]?.endTime || new Date()
       return {
         avatar: live.avatar,
         headUrl: live.avatar,
@@ -55,12 +57,14 @@ export default defineComponent({
         startTime:
           `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(startTime).format(
             'HH:mm'
-          )}~${dayjs(endTime).format('HH:mm')}` || '',
+          )}` || '',
         courseStartTime: dayjs(live.courseStartTime).format('YYYY-MM-DD HH:mm'),
         buyNum: live.studentCount,
         type: 'live',
         lessonId: live.courseGroupId,
         lessonPrice: live.coursePrice,
+        isShowCourse: live.status !== 'APPLY',
+        completeCourseNum: live.completeCourseNum,
         lessonNum: live.courseNum,
         lessonDesc: live.courseIntroduce,
         lessonCoverUrl: live.backgroundPic || live.backgroundPicTemplate,
@@ -123,6 +127,12 @@ export default defineComponent({
     } else {
       this.shareUrl = `${location.origin}/teacher/#/shareLive?recomUserId=${state.user.data?.userId}&groupId=${this.groupId}&userType=${state.platformType}&p=tenant`
     }
+
+    /** 埋点 */
+    useStatisticTracking({
+      objectType: "LIVE",
+      objectId: this.groupId as any
+    })
   },
   methods: {
     async _init() {
@@ -201,21 +211,21 @@ export default defineComponent({
     },
     async onBuy() {
       try {
-        const live = this.live
-        // 判断是否是0无订单
-        if (live.coursePrice <= 0) {
-          this.initLive()
-          await onSubmitZero(() => {
-            Dialog.alert({
-              message: '领取成功',
-              confirmButtonText: '确定',
-              confirmButtonColor: '#2dc7aa'
-            }).then(() => {
-              this._init()
-            })
-          })
-          return
-        }
+        // const live = this.live
+        // // 判断是否是0无订单
+        // if (live.coursePrice <= 0) {
+        //   this.initLive()
+        //   await onSubmitZero(() => {
+        //     Dialog.alert({
+        //       message: '领取成功',
+        //       confirmButtonText: '确定',
+        //       confirmButtonColor: '#2dc7aa'
+        //     }).then(() => {
+        //       this._init()
+        //     })
+        //   })
+        //   return
+        // }
         const res = await request.post(
           '/api-student/userOrder/getPendingOrder',
           {
@@ -331,15 +341,8 @@ export default defineComponent({
           </p>
         </div>
         {this.courseInfo.length > 0 && this.live.existBuy !== 1 && (
-          // <Sticky offsetBottom={0} position="bottom">
-          //   <div class={['btnGroup', styles.btnMore]}>
-          //     <Button block round type="primary" onClick={this.onBuy}>
-          //       {this.live.coursePrice <= 0 ? '免费领取' : `立即购买`}
-          //     </Button>
-          //   </div>
-          // </Sticky>
-          <ColSticky position="bottom" background="white">
-            <div class={['btnGroup', styles.btnMore]}>
+          <TheSticky position="bottom">
+            <div class={["btnGroup", styles.btnMore]}>
               <Button
                 block
                 round
@@ -350,24 +353,17 @@ export default defineComponent({
                 {this.live.coursePrice <= 0 ? '免费领取' : `立即购买`}
               </Button>
             </div>
-          </ColSticky>
+          </TheSticky>
         )}
 
         {this.joinRoom == '1' && this.liveStatus.liveStatus !== 2 && (
-          // <Sticky offsetBottom={0} position="bottom">
-          //   <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
-          //     <Button block round type="primary" onClick={this.onJoinRoom}>
-          //       进入直播间
-          //     </Button>
-          //   </div>
-          // </Sticky>
-          <ColSticky position="bottom" background="white">
-            <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+          <TheSticky position="bottom">
+            <div class={["btnGroup", styles.btnMore]}>
               <Button block round type="primary" onClick={this.onJoinRoom}>
                 进入直播间
               </Button>
             </div>
-          </ColSticky>
+          </TheSticky>
         )}
 
         <Popup

+ 4 - 3
src/student/teacher-dependent/components/group.tsx

@@ -7,6 +7,7 @@ import request from '@/helpers/request'
 import dayjs from 'dayjs'
 import { state } from '@/state'
 import Tips from './tips'
+import { moneyFormat } from '@/helpers/utils'
 
 export default defineComponent({
   name: 'group',
@@ -98,7 +99,7 @@ export default defineComponent({
             v-model:loading={this.loading}
             immediateCheck={false}
             finished={this.finished}
-            finishedText="没有更多了"
+            finishedText=" "
           >
             {this.list.map((item: any) => (
               <CellGroup
@@ -133,8 +134,8 @@ export default defineComponent({
                       {item.coursePrice > 0 && (
                         <span class={styles.priceNum}>
                           <i>¥</i>
-                          {(this as any).$filters.moneyFormat(
-                            item.coursePrice
+                          {moneyFormat(
+                            item.coursePrice, '0.00'
                           )}
                         </span>
                       )}

+ 4 - 37
src/student/teacher-dependent/components/live.tsx

@@ -11,6 +11,7 @@ import request from '@/helpers/request'
 import dayjs from 'dayjs'
 import { state } from '@/state'
 import Tips from './tips'
+import { moneyFormat } from '@/helpers/utils'
 
 export default defineComponent({
   name: 'live',
@@ -96,7 +97,7 @@ export default defineComponent({
             v-model:loading={this.loading}
             immediateCheck={false}
             finished={this.finished}
-            finishedText="没有更多了"
+            finishedText=" "
           >
             {this.list.map((item: any) => (
               <CellGroup
@@ -136,8 +137,8 @@ export default defineComponent({
                         <>
                           <span class={styles.priceNum}>
                             <i>¥</i>
-                            {(this as any).$filters.moneyFormat(
-                              item.coursePrice
+                            {moneyFormat(
+                              item.coursePrice, '0.00'
                             )}
                           </span>
                         </>
@@ -149,27 +150,6 @@ export default defineComponent({
                     </div>
                   </div>
                 </div>
-                {/* <Cell
-                  titleStyle={{ color: '#666666', fontSize: '13px' }}
-                  v-slots={{
-                    title: () => (
-                      <span class={styles.timerString}>
-                        {this.formatTime(item.salesStartDate)}
-                      </span>
-                    ),
-                    default: () => (
-                      <div>
-                        {item.existBuy === 1 ? (
-                          <span class={styles.buyNum}>学习</span>
-                        ) : (
-                          <span class={styles.num}>
-                            {item.studentCount}人学习
-                          </span>
-                        )}
-                      </div>
-                    )
-                  }}
-                /> */}
               </CellGroup>
             ))}
           </List>
@@ -178,18 +158,5 @@ export default defineComponent({
         )}
       </>
     )
-
-    {
-      /* <List
-v-model:loading={this.loading}
-finished={this.finished}
-finishedText="没有更多了"
-onLoad={this.getList}
->
-{this.buyUserList.map(item => (
-  <UserList class="mb12" users={item} />
-))}
-</List> */
-    }
   }
 })

+ 8 - 10
src/student/teacher-dependent/components/music.tsx

@@ -35,16 +35,14 @@ export default defineComponent({
   },
   render() {
     return (
-      <>
-        <MusicList
-          hideSearch
-          onlySearch
-          height={this.height}
-          myself
-          onItemClick={this.onItemClick}
-          teacherId={this.teacherId}
-        />
-      </>
+      <MusicList
+        hideSearch
+        onlySearch
+        height={this.height}
+        myself
+        onItemClick={this.onItemClick}
+        teacherId={this.teacherId}
+      />
     )
   }
 })

+ 66 - 44
src/student/teacher-dependent/components/practice.tsx

@@ -22,6 +22,7 @@ import { orderStatus } from '@/views/order-detail/orderStatus'
 import ColResult from '@/components/col-result'
 import { tradeOrder } from '@/student/trade/tradeOrder'
 import Tips from './tips'
+import { useStatisticTracking } from '@/helpers/hooks'
 
 export default defineComponent({
   name: 'practice',
@@ -70,7 +71,7 @@ export default defineComponent({
           }
         }
       )
-      
+
       const result = res.data || []
       if (result.length > 0) {
         const userSubjectId = this.subjectId || state.user.data?.subjectId
@@ -79,7 +80,14 @@ export default defineComponent({
         })
         // 判断是否有跟学生相同的科目,如果没有则默认取第一个
         const tempRes = findItem || result[0]
-        const { subjectName, subjectPrice, courseMinutes, subjectId, id, freeMinutes } = tempRes
+        const {
+          subjectName,
+          subjectPrice,
+          courseMinutes,
+          subjectId,
+          id,
+          freeMinutes
+        } = tempRes
         this.subjectInfo = {
           subjectPrice,
           id,
@@ -108,12 +116,18 @@ export default defineComponent({
             version: state.version
           }
         })
-        this.settingStatus =  resVersion.data.check ? false : true
+        this.settingStatus = resVersion.data.check ? false : true
       } else {
         this.settingStatus = false
       }
 
-      
+      if (this.settingStatus) {
+        /** 埋点 */
+        useStatisticTracking({
+          objectType: 'PRACTICE',
+          objectId: this.teacherId as any
+        })
+      }
       this.loadDataStatus = false
     } catch {
       this.loadDataStatus = false
@@ -159,12 +173,12 @@ export default defineComponent({
     async getList(date?: Date) {
       try {
         const tempDate = date || dayjs().add(1, 'day').toDate()
-        let params = {
+        const params = {
           day: dayjs(tempDate).format('DD'),
           month: dayjs(tempDate).format('MM'),
           year: dayjs(tempDate).format('YYYY')
         }
-        let res = await request.post(
+        const res = await request.post(
           '/api-student/courseSchedule/createPracticeCourseCalendar',
           {
             data: {
@@ -176,17 +190,19 @@ export default defineComponent({
           }
         )
         const result = res.data || []
-        let tempObj = {}
+        const tempObj = {}
         result.forEach((item: any) => {
           tempObj[item.date] = item
         })
         this.calendarList = tempObj
         this.calendarStatus = result.length > 0
-      } catch {}
+      } catch {
+        //
+      }
     },
     onSelectDay(obj: any) {
       const result = obj || []
-      let list = [...this.selectCourseList] as any
+      const list = [...this.selectCourseList] as any
 
       result.forEach((item: any) => {
         const isExist = list.some(
@@ -195,7 +211,7 @@ export default defineComponent({
         !isExist && list.push({ ...item })
       })
       // 去掉不在
-      let tempList: any[] = []
+      const tempList: any[] = []
       list.forEach((item: any) => {
         const isExist = result.some(
           (course: any) => course.startTime === item.startTime
@@ -225,7 +241,7 @@ export default defineComponent({
     },
     async _lookCourse(callBack?: Function) {
       try {
-        let times = [] as any
+        const times = [] as any
         this.selectCourseList.forEach((item: any) => {
           times.push({
             startTime: item.startTime,
@@ -388,13 +404,17 @@ export default defineComponent({
   render() {
     return (
       <>
-        <div class={styles.tipSection}><Tips type="PRACTICE" title="什么是趣纠课?" content="趣纠课以一对一专属、高度针对性的形式进行,每次课程时长为25分钟。本课程专为解决学生日常练习中的疑问与误区设计,尤其适合那些在自我练习后感到困惑或不确定自己方法是否正确的学生。不同于传统的教学模式,趣纠课不侧重于新知识或新技能的传授,而是全心全意致力于检查学生现有的练习成果,并及时纠正其中出现的问题。这种方式不仅有助于学生巩固已掌握的知识和技能,还能有效防止错误习惯的形成和发展,为他们今后的学习打下更加坚实的基础。" /></div>
+        <div class={styles.tipSection}>
+          <Tips
+            type="PRACTICE"
+            title="什么是趣纠课?"
+            content="趣纠课以一对一专属、高度针对性的形式进行,每次课程时长为25分钟。本课程专为解决学生日常练习中的疑问与误区设计,尤其适合那些在自我练习后感到困惑或不确定自己方法是否正确的学生。不同于传统的教学模式,趣纠课不侧重于新知识或新技能的传授,而是全心全意致力于检查学生现有的练习成果,并及时纠正其中出现的问题。这种方式不仅有助于学生巩固已掌握的知识和技能,还能有效防止错误习惯的形成和发展,为他们今后的学习打下更加坚实的基础。"
+          />
+        </div>
         {!this.loadDataStatus &&
           (this.settingStatus ? (
             <>
               <div class={styles.practice}>
-                
-
                 <CellGroup class={styles.group} border={false}>
                   <Cell
                     title="选择专业"
@@ -453,36 +473,38 @@ export default defineComponent({
                     />
                   </div>
                 )}
-                {this.showSelectList.length > 0 && <Cell
-                  class={[styles.arrangeCell]}
-                  v-slots={{
-                    title: () => (
-                      <div class={styles.rTitle}>
-                        <span>已选择课程时间</span>
-                      </div>
-                    ),
-                    label: () => (
-                      <div class={styles.rTag}>
-                        {this.showSelectList.map((item: any) => (
-                          <>
-                            <Tag
-                              plain
-                              round
-                              closeable
-                              size="large"
-                              type="primary"
-                              class={styles.tag}
-                              onClose={() => this.onCloseTag(item)}
-                            >
-                              {item.title}
-                            </Tag>
-                            <br />
-                          </>
-                        ))}
-                      </div>
-                    )
-                  }}
-                ></Cell>}
+                {this.showSelectList.length > 0 && (
+                  <Cell
+                    class={[styles.arrangeCell]}
+                    v-slots={{
+                      title: () => (
+                        <div class={styles.rTitle}>
+                          <span>已选择课程时间</span>
+                        </div>
+                      ),
+                      label: () => (
+                        <div class={styles.rTag}>
+                          {this.showSelectList.map((item: any) => (
+                            <>
+                              <Tag
+                                plain
+                                round
+                                closeable
+                                size="large"
+                                type="primary"
+                                class={styles.tag}
+                                onClose={() => this.onCloseTag(item)}
+                              >
+                                {item.title}
+                              </Tag>
+                              <br />
+                            </>
+                          ))}
+                        </div>
+                      )
+                    }}
+                  ></Cell>
+                )}
 
                 <Popup show={this.selectStatus} class={styles.selectPopup}>
                   <div class={styles.selectContainer}>

+ 12 - 6
src/student/teacher-dependent/components/video-item/index.tsx

@@ -3,6 +3,7 @@ import { Image } from 'vant'
 import styles from './index.module.less'
 
 import iconTeacher from '@common/images/icon_teacher.png'
+import { moneyFormat } from '@/helpers/utils'
 
 interface VideoItemProps {
   id?: number
@@ -48,8 +49,16 @@ export default defineComponent({
         <div class={styles.viSection}>
           <div class={[styles.viTitle, 'van-ellipsis']}>{item?.lessonName}</div>
           <div class={styles.tags}>
-            {item?.musicNum > 0 ? <span class={styles.label}>{item?.musicNum}首曲目</span> : ''}
-            {item?.lessonCount > 0 ? <span class={styles.label}>{item?.lessonCount}课时</span> : ''}
+            {item?.musicNum > 0 ? (
+              <span class={styles.label}>{item?.musicNum}首曲目</span>
+            ) : (
+              ''
+            )}
+            {item?.lessonCount > 0 ? (
+              <span class={styles.label}>{item?.lessonCount}课时</span>
+            ) : (
+              ''
+            )}
           </div>
           <div class={styles.viPrice}>
             <div class={styles.viUserNum}>{item?.countStudent}人学习</div>
@@ -61,7 +70,7 @@ export default defineComponent({
                   {item?.lessonPrice > 0 && (
                     <>
                       <i>¥</i>
-                      {item?.lessonPrice}
+                      {moneyFormat(item?.lessonPrice, '0.00')}
                     </>
                   )}
                   {item?.lessonPrice <= 0 && item.auditVersion !== 0 && (
@@ -75,10 +84,7 @@ export default defineComponent({
                 </>
               )}
             </span>
-            
           </div>
-
-          
         </div>
       </div>
     )

+ 1 - 14
src/student/teacher-dependent/components/video.tsx

@@ -75,7 +75,7 @@ export default defineComponent({
             v-model:loading={this.loading}
             finished={this.finished}
             immediateCheck={false}
-            finishedText="没有更多了"
+            finishedText=" "
             onLoad={this.getList}
           >
             {this.list.map(item => (
@@ -87,18 +87,5 @@ export default defineComponent({
         )}
       </>
     )
-
-    {
-      /* <List
-v-model:loading={this.loading}
-finished={this.finished}
-finishedText="没有更多了"
-onLoad={this.getList}
->
-{this.buyUserList.map(item => (
-  <UserList class="mb12" users={item} />
-))}
-</List> */
-    }
   }
 })

+ 21 - 15
src/student/teacher-dependent/components/vip.tsx

@@ -9,11 +9,9 @@ import {
   CellGroup,
   Dialog,
   Stepper,
-  Sticky,
   Tag,
   Popup,
-  Toast,
-  Icon
+  Toast
 } from 'vant'
 import { defineComponent } from 'vue'
 import { getWeekCh } from '@/helpers/utils'
@@ -21,15 +19,16 @@ import styles from './vip.module.less'
 import { orderStatus } from '@/views/order-detail/orderStatus'
 import ColResult from '@/components/col-result'
 import { tradeOrder } from '@/student/trade/tradeOrder'
-import icon3 from '../images/icon3.png'
+// import icon3 from '../images/icon3.png'
 import Tips from './tips'
+import { useStatisticTracking } from '@/helpers/hooks'
 
 export default defineComponent({
   name: 'VIP_COURSE',
   props: {
     userInfo: {
       type: Object,
-      default: {}
+      default: () => ({})
     }
   },
   data() {
@@ -71,7 +70,6 @@ export default defineComponent({
           }
         }
       )
-      
       const result = res.data || []
       if (result.length > 0) {
         const userSubjectId = this.subjectId || state.user.data?.subjectId
@@ -114,7 +112,13 @@ export default defineComponent({
         this.settingStatus = false
       }
 
-     
+      if(this.settingStatus) {
+        /** 埋点 */
+        useStatisticTracking({
+          objectType: "VIP_COURSE",
+          objectId: this.teacherId as any
+        })
+      }
       this.loadDataStatus = false
     } catch {
       this.loadDataStatus = false
@@ -123,7 +127,7 @@ export default defineComponent({
   computed: {
     showSelectList() {
       const arr: any = this.selectCourseList
-      let list = [...arr]
+      const list = [...arr]
       list.forEach((item: any) => {
         item.title =
           dayjs(item.startTime).format('YYYY-MM-DD') +
@@ -160,12 +164,12 @@ export default defineComponent({
     async getList(date?: Date) {
       try {
         const tempDate = date || dayjs().add(1, 'day').toDate()
-        let params = {
+        const params = {
           day: dayjs(tempDate).format('DD'),
           month: dayjs(tempDate).format('MM'),
           year: dayjs(tempDate).format('YYYY')
         }
-        let res = await request.post(
+        const res = await request.post(
           '/api-student/courseSchedule/createPracticeCourseCalendar',
           {
             data: {
@@ -177,17 +181,19 @@ export default defineComponent({
           }
         )
         const result = res.data || []
-        let tempObj = {}
+        const tempObj = {}
         result.forEach((item: any) => {
           tempObj[item.date] = item
         })
         this.calendarList = tempObj
         this.calendarStatus = result.length > 0
-      } catch {}
+      } catch {
+        //
+      }
     },
     onSelectDay(obj: any) {
       const result = obj || []
-      let list = [...this.selectCourseList] as any
+      const list = [...this.selectCourseList] as any
 
       result.forEach((item: any) => {
         const isExist = list.some(
@@ -196,7 +202,7 @@ export default defineComponent({
         !isExist && list.push({ ...item })
       })
       // 去掉不在
-      let tempList: any[] = []
+      const tempList: any[] = []
       list.forEach((item: any) => {
         const isExist = result.some(
           (course: any) => course.startTime === item.startTime
@@ -226,7 +232,7 @@ export default defineComponent({
     },
     async _lookCourse(callBack?: Function) {
       try {
-        let times = [] as any
+        const times = [] as any
         this.selectCourseList.forEach((item: any) => {
           times.push({
             startTime: item.startTime,

+ 1 - 4
src/student/teacher-dependent/teacher-home.tsx

@@ -1,11 +1,10 @@
 import ColHeader from '@/components/col-header'
-import { defineComponent, provide } from 'vue'
+import { defineComponent } from 'vue'
 import styles from './teacher-home.module.less'
 import { Popup, Tab, Tabs } from 'vant'
 import Practice from './components/practice'
 import Live from './components/live'
 import VideoList from './components/video'
-import Music from './components/music'
 import request from '@/helpers/request'
 import { listenerMessage } from '@/helpers/native-message'
 import { useEventListener, useWindowScroll } from '@vueuse/core'
@@ -13,7 +12,6 @@ import TeacherHeader from './model/teacher-header'
 import { useRect } from '@vant/use'
 import { useEventTracking } from '@/helpers/hooks'
 import Vip from './components/vip'
-import Single from './components/single'
 import JoinChat from './model/join-chat'
 import FansList from './model/fans-list'
 import MusicList from '@/views/music/list'
@@ -183,7 +181,6 @@ export default defineComponent({
               {this.userInfo.introduction && <div class={styles.singleContent}>
               {this.userInfo.introduction}
             </div>}
-            
           </div>
         </div>
         <Tabs

+ 5 - 1
src/student/video-class/video-detail.module.less

@@ -17,10 +17,14 @@
   .btnMore {
     display: flex;
     justify-content: space-around;
+    padding: 20px 28px 30px;
+    background: linear-gradient( 180deg, rgba(255,255,255,0) 0%, #FFFFFF 100%), #FFFDFD;
+    box-shadow: 0px 0px 2px 0px rgba(216,216,216,0.5);
 
     :global {
       .van-button {
-        width: 55%;
+        width: 100%;
+        font-size: 16px;
       }
     }
   }

+ 25 - 17
src/student/video-class/video-detail.tsx

@@ -16,6 +16,8 @@ import LiveItem from '@/views/live-class/live-item'
 import { setLogin, state } from '@/state'
 import { browser } from '@/helpers/utils'
 import { usePageVisibility } from '@vant/use'
+import TheSticky from '@/components/the-sticky'
+import { useStatisticTracking } from '@/helpers/hooks'
 export default defineComponent({
   name: 'VideoDetail',
   data() {
@@ -50,6 +52,12 @@ export default defineComponent({
     } else {
       this.shareUrl = `${location.origin}/teacher#/shareVideo?recomUserId=${state.user.data?.userId}&groupId=${this.params.groupId}&userType=${state.platformType}&p=tenant`
     }
+
+    /** 埋点 */
+    useStatisticTracking({
+      objectType: "VIDEO",
+      objectId: this.params.groupId as any
+    })
   },
   methods: {
     async _init() {
@@ -64,7 +72,6 @@ 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'
@@ -84,6 +91,7 @@ export default defineComponent({
           lessonName: lessonGroup.lessonName,
           payType: lessonGroup.payType,
           type: 'video',
+          subjectName: lessonGroup.lessonSubjectName,
           lessonDesc: lessonGroup.lessonDesc,
           lessonPrice: lessonGroup.lessonPrice,
           relationType: lessonGroup.relationType,
@@ -152,22 +160,22 @@ export default defineComponent({
           return
         }
 
-        const userInfo = this.userInfo
+        // const userInfo = this.userInfo
 
         // 判断是否是0无订单
-        if (userInfo.lessonPrice <= 0) {
-          this.initVideo()
-          await onSubmitZero(() => {
-            Dialog.alert({
-              message: '领取成功',
-              confirmButtonText: '确定',
-              confirmButtonColor: '#2dc7aa'
-            }).then(() => {
-              this._init()
-            })
-          })
-          return
-        }
+        // if (userInfo.lessonPrice <= 0) {
+        //   this.initVideo()
+        //   await onSubmitZero(() => {
+        //     Dialog.alert({
+        //       message: '领取成功',
+        //       confirmButtonText: '确定',
+        //       confirmButtonColor: '#2dc7aa'
+        //     }).then(() => {
+        //       this._init()
+        //     })
+        //   })
+        //   return
+        // }
 
         const res = await request.post(
           '/api-student/userOrder/getPendingOrder',
@@ -327,7 +335,7 @@ export default defineComponent({
           //     </Button>
           //   </div>
           // </Sticky>
-          <ColSticky position="bottom" background="white">
+          <TheSticky position="bottom">
             <div class={['btnGroup', styles.btnMore]}>
               <Button
                 block
@@ -343,7 +351,7 @@ export default defineComponent({
                   : `立即购买`}
               </Button>
             </div>
-          </ColSticky>
+          </TheSticky>
         )}
 
         <Popup

+ 10 - 0
src/styles/README.md

@@ -0,0 +1,10 @@
+### components-ui
+
+1、在 Vant3.x 基础上封装一套独立样式,因环境差异较大数据应统一输入尽量不要请求接口;
+2、注意 Vant 库等必要依赖库的版本差异;
+3、组件采用 less 的方式编写;
+
+### 使用
+
+1、把项目中 index.less 在项目的根目录中使用;
+2、一些基础组件,只会在原生有 UI 组件上变更样式;

+ 54 - 0
src/styles/component-ui.less

@@ -0,0 +1,54 @@
+// 公用变量
+@import './global.less';
+
+// 选择框
+// 上拉选择 - ✅
+// 选择器 - ✅
+.van-picker {
+  --van-picker-toolbar-height: 44px !important;
+  .van-picker__toolbar {
+    position: relative;
+    &::after {
+      position: absolute;
+      box-sizing: border-box;
+      content: ' ';
+      pointer-events: none;
+      right: var(--van-padding-md);
+      bottom: 0;
+      left: var(--van-padding-md);
+      border-bottom: 1px solid var(--van-cell-border-color);
+      transform: scaleY(0.5);
+    }
+  }
+  .van-picker__columns {
+    padding: 0 24px;
+  }
+  .van-picker-column {
+    position: relative;
+    z-index: 1;
+  }
+  .van-picker__frame {
+    z-index: 0;
+    &::after {
+      background: var(--k-bg-4);
+      border-radius: 8px;
+    }
+  }
+  .van-picker__cancel,
+  .van-picker__confirm {
+    font-size: 15px;
+  }
+  .van-picker__cancel {
+    color: var(--k-gray-3);
+  }
+  .van-picker__confirm {
+    color: var(--k-font-primary);
+  }
+  .van-picker-column__item {
+    color: var(--k-gray-1);
+    font-size: 16px;
+  }
+  .van-picker-column__item--selected {
+    font-weight: 600;
+  }
+}

+ 129 - 0
src/styles/global.less

@@ -0,0 +1,129 @@
+// 注意:为什么要写两个重复的 :root?
+// 由于 vant 中的主题变量也是在 :root 下声明的,所以在有些情况下会由于优先级的问题无法成功覆盖。通过
+// :root:root 可以显式地让你所写内容的优先级更高一些,从而确保主题变量的成功覆盖。
+:root:root {
+  // 01 品牌色
+  --k-primary: #2dc7aa;
+
+  // 02 背景色
+  --k-bg-1: #fff;
+  --k-bg-2: #f8f8f8;
+  --k-bg-3: #f6f6f6;
+  --k-bg-4: #f2f2f2;
+
+  // 03 辅助色
+  --k-orange: #ffebdd;
+  --k-red: #f44541;
+  --k-blue: #64a9ff;
+  --k-purple: #8f80ff;
+
+  // 04 渐变色
+  --k-gradient-1: linear-gradient(90deg, #ff9c63 0%, #ff7144 100%);
+  --k-gradient-2: linear-gradient(270deg, #ff4f44 0%, #ffafab 100%);
+  --k-gradient-3: linear-gradient(90deg, #8cccff 0%, #459aff 100%);
+  --k-gradient-4: linear-gradient(90deg, #d4a9ff 0%, #8f80ff 100%);
+  --k-gradient-5: linear-gradient(90deg, #a9f0b4 0%, #09c58c 100%);
+
+  // 05 字体颜色
+  --k-font-primary: #2dc7aa;
+  --k-font-danger: #f44541;
+  --k-gray-1: #333333;
+  --k-gray-2: #666666;
+  --k-gray-3: #777777;
+  --k-gray-4: #aaaaaa;
+  --k-gray-5: #cccccc;
+
+  // 06 分割线
+  --k-hairline-dark: #eeeeee;
+  --k-hairline-shallow: #f2f2f2;
+
+  // 07 蒙层
+  --k-overlay-background-dark: rgba(0, 0, 0, 0.7);
+  --k-overlay-background-shallow: rgba(0, 0, 0, 0.5);
+
+  // 圆角
+  --k-radius-sm: 2px;
+  --k-radius-md: 4px;
+  --k-radius-lg: 8px;
+  --k-radius-xl: 10px;
+  --k-radius-max: 999px;
+
+  // 间距
+  --k-padding-base: 4px;
+  --k-padding-xs: 6px;
+  --k-padding-sm: 8px;
+  --k-padding-md: 12px;
+  --k-padding-lg: 16px;
+  --k-padding-xl: 20px;
+  --k-padding-page: 13px; // 页面是基础边距
+  --k-padding-card: 9px; // 卡片的基础边距
+
+  // 描边 投影
+  --k-border-color: var(--k-primary);
+  --k-border-width: 1px;
+  --k-shadow: 0px 2px 12px 0px rgba(100, 101, 102, 0.12);
+
+  // 设置Vant UI组件库中的默认样式;
+  --van-primary: var(--k-primary);
+  --van-primary-color: var(--van-primary);
+  --van-primary-text: var(--k-font-primary);
+  --van-text-color: var(--k-gray-1);
+
+  // 多选框
+  --van-checkbox-border-color: #dcdcdc;
+  --van-checkbox-label-color: var(--k-gray-1);
+  --van-checkbox-disabled-icon-color: #dcdcdc;
+  --van-checkbox-disabled-label-color: var(--k-gray-5);
+  --van-checkbox-disabled-background: #f7f8fa;
+
+  // 单选框
+  --van-radio-border-color: #dcdcdc;
+  --van-radio-disabled-icon-color: #dcdcdc;
+  --van-radio-disabled-background: #f7f8fa;
+
+  // 导航
+  --van-nav-bar-arrow-size: 20px;
+  --van-nav-bar-title-font-size: 18px;
+  --van-nav-bar-title-text-color: var(--k-gray-1);
+  --van-nav-bar-icon-color: var(--k-gray-1);
+
+  // tab 选择卡
+  --van-tab-text-color: var(--k-gray-3);
+  --van-tabs-bottom-bar-width: 40px;
+  --van-tab-active-text-color: var(--k-gray-1);
+
+  // 侧边导航栏(分类选择)
+  --van-sidebar-selected-border-width: 2px;
+  --van-sidebar-selected-border-height: 18px;
+  --van-sidebar-text-color: var(--k-gray-1);
+  --van-sidebar-selected-text-color: var(--k-primary);
+
+  // 宫格
+  --van-grid-item-text-color: var(--k-gray-1);
+  --van-grid-item-text-font-size: 14px;
+
+  // 步骤条
+  --van-step-horizontal-title-font-size: 14px;
+  --van-step-finish-text-color: var(--k-gray-1);
+  --van-step-text-color: #999;
+
+  // 按钮
+  --van-button-normal-font-size: 18px;
+
+  // 通知栏
+  --van-notice-bar-background: #ffe3d2;
+  --van-notice-bar-text-color: var(--k-font-primary);
+
+  // 开关
+  --van-switch-size: 22px;
+  --van-switch-width: calc(2em + 4px);
+  --van-switch-height: calc(1em + 4px);
+  // --van-switch-background: #fff;
+
+  // 折叠面板
+  --van-collapse-item-content-text-color: #999;
+
+  // 头部高度
+  --van-nav-bar-height: 44px;
+  --van-nav-bar-arrow-size: 22px;
+}

+ 5 - 4
src/styles/index.less

@@ -1,5 +1,6 @@
 @import url('./iconfont/iconfont.css');
 @import url('./font/index.less');
+@import url('./component-ui.less');
 
 :root {
   // Color Palette
@@ -102,7 +103,7 @@
 }
 
 #app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
+  font-family: Helvetica, Arial, sans-serif, Avenir;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
   color: #2c3e50;
@@ -125,7 +126,7 @@ body {
 
 .btnGroup {
   padding: 0 28px;
-  padding-bottom: 15px;
+  padding-bottom: 30px;
 }
 
 .btnMore {
@@ -134,11 +135,11 @@ body {
 
   // :global {
   .van-button {
-    width: 48% !important;
+    width: 50% !important;
   }
 
   .van-button+.van-button {
-    margin-left: 6px;
+    margin-left: 12px;
   }
 
   // }

+ 15 - 0
src/teacher/group-class/create-components/detail.module.less

@@ -1,2 +1,17 @@
 .detail {
+    .btnMore {
+        display: flex;
+        align-items: center;
+        padding: 20px 28px 30px;
+        background: linear-gradient( 180deg, rgba(255,255,255,0) 0%, #FFFFFF 100%), #FFFDFD;
+        box-shadow: 0px 0px 2px 0px rgba(216,216,216,0.5);
+        :global {
+            .van-button {
+                font-size: 16px;
+                & + .van-button {
+                    margin-left: 10px;
+                }
+            }
+        }
+    }
 }

+ 6 - 4
src/teacher/group-class/create-components/detail.tsx

@@ -10,6 +10,7 @@ import styles from './detail.module.less'
 import dayjs from 'dayjs'
 import { postMessage } from '@/helpers/native-message'
 import GroupPlanStep from '@/business-components/group-plan-step'
+import TheSticky from '@/components/the-sticky'
 interface IProps {
   courseTime: string
   coursePlan: string
@@ -24,7 +25,7 @@ export default defineComponent({
   computed: {
     userInfo() {
       const startTime = createState.live.coursePlanList[0].startTime
-      const endTime = createState.live.coursePlanList[0].endTime
+      // const endTime = createState.live.coursePlanList[0].endTime
       return {
         headUrl: state.user.data?.heardUrl,
         username:
@@ -38,6 +39,7 @@ export default defineComponent({
         lessonPrice: createState.live.coursePrice,
         lessonNum: createState.live.courseNum,
         lessonDesc: createState.live.courseIntroduce,
+        subjectName: createState.live.subjectName,
         lessonCoverUrl:
           createState.live.backgroundPic ||
           createState.live.backgroundPicTemplate,
@@ -159,8 +161,8 @@ export default defineComponent({
           <CoursePlanStep courseInfo={this.courseInfo} />
         </SectionDetail> */}
 
-        <Sticky offsetBottom={0} position="bottom">
-          <div class={['btnGroup', 'btnMore']} style={{ paddingTop: '12px' }}>
+        <TheSticky position="bottom">
+          <div class={['btnGroup', styles.btnMore]}>
             <Button
               block
               round
@@ -183,7 +185,7 @@ export default defineComponent({
             )}
             
           </div>
-        </Sticky>
+        </TheSticky>
       </div>
     )
   }

+ 18 - 60
src/teacher/group-class/group-detail.tsx

@@ -1,20 +1,21 @@
-import CoursePlanStep from '@/business-components/course-plan-step'
+// import CoursePlanStep from '@/business-components/course-plan-step'
 import SectionDetail from '@/business-components/section-detail'
 import UserDetail from '@/business-components/user-detail'
-import UserList from '@/business-components/user-list'
-import ColResult from '@/components/col-result'
-import ColShare from '@/components/col-share'
-import ColSticky from '@/components/col-sticky'
+// import UserList from '@/business-components/user-list'
+// import ColResult from '@/components/col-result'
+// import ColShare from '@/components/col-share'
+// import ColSticky from '@/components/col-sticky'
 import { postMessage } from '@/helpers/native-message'
 import request from '@/helpers/request'
 import { state } from '@/state'
-import LiveItem from '@/views/live-class/live-item'
+// import LiveItem from '@/views/live-class/live-item'
 import dayjs from 'dayjs'
 import { Button, Popup, Tab, Tabs, Toast } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './group-detail.module.less'
 import ColHeader from '@/components/col-header'
 import GroupPlanStep from '@/business-components/group-plan-step'
+import TheSticky from '@/components/the-sticky'
 interface IProps {
   courseTime: string
   coursePlan: string
@@ -59,6 +60,8 @@ export default defineComponent({
         type: 'group',
         mixStudentNum: studentNum > 0 ? studentNum : 0,
         lessonId: live.courseGroupId,
+        completeCourseNum: live.completeCourseNum,
+        isShowCourse: live.status !== 'APPLY',
         lessonNum: live.courseNum || 0, // 课时数
         lessonDesc: live.courseIntroduce,
         lessonCoverUrl: live.backgroundPic || live.backgroundPicTemplate,
@@ -211,56 +214,11 @@ export default defineComponent({
 
         <GroupPlanStep courseInfo={this.courseInfo}
                   courseId={Number(this.courseId) || 0} />
-        {/* {this.myself ? (
-          <SectionDetail title="课程列表" icon="courseList" border={true}>
-            <CoursePlanStep
-              courseInfo={this.courseInfo}
-              courseId={Number(this.courseId) || 0}
-            />
-          </SectionDetail>
-        ) : (
-          <SectionDetail
-            title="课程列表"
-            icon="courseList"
-            titleShow={false}
-            contentStyle={{ paddingTop: '0' }}
-          >
-            <Tabs color="var(--van-primary)" lineWidth={20} sticky>
-              <Tab title="课程" titleClass="van-hairline--bottom">
-                <CoursePlanStep
-                  courseInfo={this.courseInfo}
-                  courseId={Number(this.courseId) || 0}
-                />
-              </Tab>
-              <Tab title="学员列表" titleClass="van-hairline--bottom">
-                {this.studentList.map((item: any) => (
-                  <UserList
-                    class="mb12"
-                    users={{
-                      avatar: item.avatar,
-                      studentId: item.studentId,
-                      studentName: item.userName,
-                      createTime: item.createTime
-                    }}
-                  />
-                ))}
-                {this.studentList.length === 0 && (
-                  <ColResult
-                    tips="暂无购买学员"
-                    classImgSize="SMALL"
-                    btnStatus={false}
-                  />
-                )}
-              </Tab>
-            </Tabs>
-          </SectionDetail>
-        )} */}
-
         {this.live.status !== 'OUT_SALE' && (
           <>
             {this.courseOffStatus && (
-              <ColSticky position="bottom" background="white">
-                <div class={['btnGroup']} >
+              <TheSticky position="bottom">
+                <div class={['btnGroup']} style={{ paddingTop: '10px', background: '#fff' }}>
                   <Button
                     block
                     round
@@ -270,22 +228,22 @@ export default defineComponent({
                     取消课程
                   </Button>
                 </div>
-              </ColSticky>
+              </TheSticky>
             )}
 
             {this.joinRoom == '1' && this.liveStatus.liveStatus !== 2 && (
-              <ColSticky position="bottom" background="white">
-                <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+              <TheSticky position="bottom">
+                <div class={['btnGroup']} style={{ paddingTop: '10px', background: '#fff' }}>
                   <Button block round type="primary" onClick={this.onJoinRoom}>
                     进入直播间
                   </Button>
                 </div>
-              </ColSticky>
+              </TheSticky>
             )}
 
             {/* {this.share == '1' && this.courseInfo.length > 0 && (
               <ColSticky position="bottom" background="white">
-                <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+                <div class={['btnGroup']} style={{ paddingTop: '10px' }}>
                   <Button
                     block
                     round
@@ -302,7 +260,7 @@ export default defineComponent({
           </>
         )}
 
-        <Popup
+        {/* <Popup
           v-model:show={this.shareStatus}
           style={{ background: 'transparent' }}
         >
@@ -329,7 +287,7 @@ export default defineComponent({
               }}
             />
           </ColShare>
-        </Popup>
+        </Popup> */}
       </div>
     )
   }

+ 3 - 2
src/teacher/live-class/create-components/arrange.tsx

@@ -7,6 +7,7 @@ import dayjs from 'dayjs'
 import { createState } from './createState'
 import { state } from '@/state'
 import { getWeekCh } from '@/helpers/utils'
+import TheSticky from '@/components/the-sticky'
 
 export default defineComponent({
   name: 'arrange',
@@ -303,7 +304,7 @@ export default defineComponent({
             <div class={['mb12']}></div>
           </>
         )}
-        <Sticky offsetBottom={0} position="bottom">
+        <TheSticky position="bottom">
           <div class={['btnGroup', 'btnMore']}>
             <Button
               block
@@ -322,7 +323,7 @@ export default defineComponent({
               下一步
             </Button>
           </div>
-        </Sticky>
+        </TheSticky>
 
         <Popup show={this.selectStatus} class={styles.selectPopup}>
           <div class={styles.selectContainer}>

+ 8 - 7
src/teacher/live-class/create-components/course-plan.tsx

@@ -1,9 +1,10 @@
-import { Button, Field, Form, Sticky } from 'vant'
+import { Button, Field, Form } from 'vant'
 import { defineComponent } from 'vue'
 import { basePlan, createState } from './createState'
 import styles from './course-plan.module.less'
 import ColField from '@/components/col-field'
 import ColFieldGroup from '@/components/col-field-group'
+import TheSticky from '@/components/the-sticky'
 
 export default defineComponent({
   name: 'course-plan',
@@ -11,8 +12,8 @@ export default defineComponent({
     return {}
   },
   async mounted() {
-    let list = createState.live.coursePlanList
-    let listLength = list.length || 0
+    const list = createState.live.coursePlanList
+    const listLength = list.length || 0
     console.log(list)
     if (createState.live.courseNum > listLength) {
       for (let i = 0; i < createState.live.courseNum - listLength; i++) {
@@ -26,7 +27,7 @@ export default defineComponent({
   },
   methods: {
     async addPlan() {
-      let list = createState.live.coursePlanList || []
+      const list = createState.live.coursePlanList || []
       list.push({
         plan: '',
         startTime: '',
@@ -36,7 +37,7 @@ export default defineComponent({
       createState.live.coursePlanList = list
     },
     async delPlan(index?: number) {
-      let list = createState.live.coursePlanList || []
+      const list = createState.live.coursePlanList || []
       list.splice(index || list.length - 1, 1)
       createState.live.coursePlanList = list
     }
@@ -64,7 +65,7 @@ export default defineComponent({
             </ColFieldGroup>
           ))}
 
-        <Sticky offsetBottom={0} position="bottom">
+        <TheSticky position="bottom">
           <div class={['btnGroup', 'btnMore']}>
             <Button
               block
@@ -84,7 +85,7 @@ export default defineComponent({
               下一步
             </Button>
           </div>
-        </Sticky>
+        </TheSticky>
       </Form>
     )
   }

+ 7 - 0
src/teacher/live-class/create-components/course.tsx

@@ -93,6 +93,13 @@ export default defineComponent({
   methods: {
     onChoice(id: number) {
       createState.live.subjectId = id
+      let tempStr = ''
+      this.subjectList.forEach((item: any) => {
+        if (id == item.id) {
+          tempStr = item.name
+        }
+      })
+      createState.live.subjectName = tempStr
       this.subjectStatus = false
     },
     onFormatter(val: any) {

+ 1 - 0
src/teacher/live-class/create-components/createState.ts

@@ -26,6 +26,7 @@ export const createState = reactive({
     courseGroupId: '',
     name: '',
     subjectId: null as any,
+    subjectName: '',
     courseIntroduce: '',
     courseNum: null as any,
     singleCourseMinutes: 0,

+ 15 - 0
src/teacher/live-class/create-components/detail.module.less

@@ -1,2 +1,17 @@
 .detail {
+    .btnMore {
+        display: flex;
+        align-items: center;
+        padding: 20px 28px 30px;
+        background: linear-gradient( 180deg, rgba(255,255,255,0) 0%, #FFFFFF 100%), #FFFDFD;
+        box-shadow: 0px 0px 2px 0px rgba(216,216,216,0.5);
+        :global {
+            .van-button {
+                font-size: 16px;
+                & + .van-button {
+                    margin-left: 10px;
+                }
+            }
+        }
+    }
 }

+ 5 - 5
src/teacher/live-class/create-components/detail.tsx

@@ -9,6 +9,7 @@ import { createState } from './createState'
 import styles from './detail.module.less'
 import dayjs from 'dayjs'
 import { postMessage } from '@/helpers/native-message'
+import TheSticky from '@/components/the-sticky'
 interface IProps {
   courseTime: string
   coursePlan: string
@@ -28,7 +29,7 @@ export default defineComponent({
         username:
           state.user.data?.username || `游客${state.user.data?.userId || ''}`,
         startTime:
-          `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(startTime).format(
+          `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(endTime).format(
             'HH:mm'
           )}~${dayjs(endTime).format('HH:mm')}` || '',
         courseStartTime: dayjs(startTime).format('YYYY-MM-DD HH:mm'),
@@ -153,8 +154,8 @@ export default defineComponent({
           <CoursePlanStep courseInfo={this.courseInfo} />
         </SectionDetail>
 
-        <Sticky offsetBottom={0} position="bottom">
-          <div class={['btnGroup', 'btnMore']}>
+        <TheSticky position="bottom">
+          <div class={['btnGroup', styles.btnMore]}>
             <Button
               block
               round
@@ -175,9 +176,8 @@ export default defineComponent({
                 创建成功
               </Button>
             )}
-            
           </div>
-        </Sticky>
+        </TheSticky>
       </div>
     )
   }

+ 14 - 12
src/teacher/live-class/live-detail.tsx

@@ -14,6 +14,7 @@ import { Button, Popup, Sticky, Tab, Tabs, Toast } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './live-detail.module.less'
 import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
 interface IProps {
   courseTime: string
   coursePlan: string
@@ -42,7 +43,7 @@ export default defineComponent({
       const live = this.live as any
       const planList = live.planList || []
       const startTime = planList[0]?.startTime || new Date()
-      const endTime = planList[0]?.endTime || new Date()
+      // const endTime = planList[0]?.endTime || new Date()
       return {
         headUrl: live.avatar,
         avatar: live.avatar,
@@ -51,12 +52,14 @@ export default defineComponent({
         startTime:
           `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(startTime).format(
             'HH:mm'
-          )}~${dayjs(endTime).format('HH:mm')}` || '',
+          )}` || '',
         courseStartTime: dayjs(live.courseStartTime).format('YYYY-MM-DD HH:mm'),
         lessonPrice: live.coursePrice,
         buyNum: live.studentCount || 0,
         type: 'live',
         lessonId: live.courseGroupId,
+        completeCourseNum: live.completeCourseNum, // 已上课时
+        isShowCourse: live.status !== 'APPLY', // 是否显示课程
         lessonNum: live.courseNum || 0, // 课时数
         lessonDesc: live.courseIntroduce,
         lessonCoverUrl: live.backgroundPic || live.backgroundPicTemplate,
@@ -248,12 +251,11 @@ export default defineComponent({
             </Tabs>
           </SectionDetail>
         )}
-
         {this.live.status !== 'OUT_SALE' && (
           <>
             {this.courseOffStatus && (
-              <ColSticky position="bottom" background="white">
-                <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+              <TheSticky position="bottom">
+                <div class={['btnGroup']} style={{ paddingTop: '10px', background: '#fff' }}>
                   <Button
                     block
                     round
@@ -263,22 +265,22 @@ export default defineComponent({
                     取消课程
                   </Button>
                 </div>
-              </ColSticky>
+              </TheSticky>
             )}
 
             {this.joinRoom == '1' && this.liveStatus.liveStatus !== 2 && (
-              <ColSticky position="bottom" background="white">
-                <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+              <TheSticky position="bottom">
+                <div class={['btnGroup']} style={{ paddingTop: '10px', background: '#fff' }}>
                   <Button block round type="primary" onClick={this.onJoinRoom}>
                     进入直播间
                   </Button>
                 </div>
-              </ColSticky>
+              </TheSticky>
             )}
 
             {this.share == '1' && this.courseInfo.length > 0 && (
-              <ColSticky position="bottom" background="white">
-                <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+              <TheSticky position="bottom">
+                <div class={['btnGroup']} style={{ paddingTop: '10px', background: '#fff' }}>
                   <Button
                     block
                     round
@@ -290,7 +292,7 @@ export default defineComponent({
                     分享
                   </Button>
                 </div>
-              </ColSticky>
+              </TheSticky>
             )}
           </>
         )}

+ 0 - 1
src/teacher/main.ts

@@ -43,7 +43,6 @@ postMessage(
   },
   (res: any) => {
     state.version = res.content.version
-    console.log(res, 'version')
   }
 )
 

+ 13 - 1
src/teacher/my-sheetMusic/index.tsx

@@ -17,6 +17,7 @@ export default defineComponent({
     const activeTab = ref('personal')
     const personal = ref()
     const collection = ref()
+    const collection2 = ref()
     const practice = ref()
     const height = ref<any>('auto')
     onMounted(() => {
@@ -63,7 +64,18 @@ export default defineComponent({
                 <Collection
                   ref={collection}
                   onFavorite={() => {
-                    practice.value?.reload?.()
+                    collection.value?.reload?.()
+                  }}
+                />
+              </div>
+            </Tab>
+            <Tab title="收藏机构单曲" name="collection">
+              <div class={styles.container}>
+                <Collection
+                  type="TENANT"
+                  ref={collection2}
+                  onFavorite={() => {
+                    collection2.value?.reload?.()
                   }}
                 />
               </div>

+ 177 - 0
src/teacher/statistics/exercise-detail/exercise-detail.module.less

@@ -0,0 +1,177 @@
+.member-record {
+  min-height: 100vh;
+  position: relative;
+  overflow: hidden;
+  .memberHeader {
+    background: #00d1a1;
+  }
+
+  .headerContent {
+    min-height: 158px;
+    margin-bottom: 35px;
+    position: relative;
+
+    .userInfo {
+      position: absolute;
+      bottom: 74px;
+      left: 34px;
+      display: flex;
+      align-items: center;
+      .userImg {
+        width: 24px;
+        height: 24px;
+        border-radius: 50%;
+      }
+      .userName {
+        font-weight: 600;
+        font-size: 16px;
+        color: #ffffff;
+        line-height: 20px;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        max-width: 112px;
+        padding: 0 4px
+      }
+      .subjectName {
+        font-size: 10px;
+        color: #ff8c00;
+        line-height: 14px;
+        background: #fff1de;
+        border-radius: 4px;
+        padding: 1px 4px;
+      }
+    }
+
+    .headerImg {
+      width: 100%;
+      max-height: 158px;
+      padding: 0 14px;
+    }
+
+    .headerCount {
+      position: absolute;
+      bottom: -35px;
+      display: flex;
+      align-items: center;
+      margin: 0 14px;
+      width: calc(100% - 28px);
+      background-color: #fff;
+      border-radius: 12px;
+      height: 65px;
+      .headerItem {
+        position: relative;
+        flex-basis: 33.33%;
+        display: flex;
+        align-items: center;
+        flex-direction: column;
+        font-size: 12px;
+        color: #778098;
+        line-height: 17px;
+        .num {
+          font-size: 16px;
+          font-weight: 500;
+          color: #2dc7aa;
+          line-height: 22px;
+          padding-bottom: 8px;
+        }
+
+        &::after {
+          content: ' ';
+          display: inline-block;
+          position: absolute;
+          right: 0px;
+          top: 15px;
+          width: 1px;
+          height: 16px;
+          background: #ebebeb;
+        }
+
+        &:last-child {
+          &::after {
+            display: none;
+          }
+        }
+      }
+    }
+  }
+
+  .memberCell {
+    margin: 10px 14px 0;
+    width: auto;
+    border-radius: 10px;
+    overflow: hidden;
+    .dataItem {
+      color: #1a1a1a;
+    }
+    :global {
+      .van-cell__value {
+        flex: auto 1;
+      }
+
+      .iconfont-down {
+        margin-left: 4px;
+        transform: scale(0.8);
+      }
+    }
+  }
+}
+
+.rTitle {
+  display: flex;
+  align-items: center;
+  &::before {
+    margin-right: 8px;
+    content: ' ';
+    display: inline-block;
+    width: 4px;
+    height: 14px;
+    background: #2dc7aa;
+    border-radius: 3px;
+  }
+}
+
+.data-content {
+  margin: 10px 14px 0;
+  border-radius: 10px;
+  overflow: hidden;
+  // &:first-child {
+  //   margin-top: 0;
+  // }
+  .dataTitle {
+    display: flex;
+    align-items: center;
+
+    span {
+      padding-right: 8px;
+      max-width: 150px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+  }
+
+  :global {
+    .van-row-item {
+      display: flex;
+      align-items: center;
+    }
+    .van-grid-item__content {
+      padding: 3px;
+    }
+    .van-grid-item__content {
+      background-color: transparent;
+      font-size: 14px;
+    }
+    .van-grid-item__icon-wrapper {
+      font-size: 14px;
+      font-weight: 500;
+      color: #000;
+    }
+    .van-grid-item__text {
+      padding-top: 6px;
+      font-size: 12px;
+      color: #808080;
+    }
+  }
+}

+ 319 - 0
src/teacher/statistics/exercise-detail/exercise-detail.tsx

@@ -0,0 +1,319 @@
+import ColHeader from '@/components/col-header'
+import { defineComponent } from 'vue'
+import styles from './exercise-detail.module.less'
+import {
+  Cell,
+  CellGroup,
+  DatetimePicker,
+  Grid,
+  GridItem,
+  Icon,
+  Image,
+  List,
+  Popup
+} from 'vant'
+import { levelMember } from '@/constant'
+import request from '@/helpers/request'
+import { formatterDate } from '@/helpers/utils'
+import dayjs from 'dayjs'
+import ColResult from '@/components/col-result'
+import { postMessage } from '@/helpers/native-message'
+import { useEventTracking } from '@/helpers/hooks'
+import iconVideo from '../images/icon_video.png'
+import recordBg from '../images/record_bg.png'
+import iconStudent from '@/common/images/icon_student.png'
+
+export default defineComponent({
+  name: 'memberRecord',
+  data() {
+    return {
+      userTrainOverView: {
+        trainDays: 0,
+        trainNum: 0,
+        trainTime: 0
+      },
+      userInfo: {
+        avatar: '',
+        username: '',
+        subjectName: ''
+      },
+      timeStatus: false,
+      currentDate: new Date(),
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      params: {
+        page: 1,
+        rows: 20
+      }
+    }
+  },
+  async mounted() {
+    this.getList()
+    useEventTracking('评测记录')
+  },
+  methods: {
+    onSearch() {
+      this.timeStatus = false
+      this.dataShow = true
+      this.loading = false
+      this.finished = false
+      this.list = []
+      this.params.page = 1
+      this.getList()
+    },
+    async getList() {
+      try {
+        const params = this.params
+        const res = await request.get(
+          '/api-teacher/sysMusicRecord/studentTrainData',
+          {
+            params: {
+              ...params,
+              startTime: dayjs(this.currentDate).format('YYYY-MM')
+            }
+          }
+        )
+        this.loading = false
+        const result = res.data || {}
+        // 在第一页的时候才处理数据显示
+        if (result.detail.pageNo === 1) {
+          this.userTrainOverView = result.userTrainOverView
+          this.userInfo = result.userInfo
+        }
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.detail.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.detail.rows || [])
+        this.finished = result.detail.pageNo >= result.detail.totalPage
+        this.params.page = result.detail.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+    }
+  },
+  render() {
+    return (
+      <div class={styles['member-record']}>
+        {/* <Sticky position="top" offsetTop={0}> */}
+        <ColHeader
+          class={styles.memberHeader}
+          background="#00d1a1"
+          backIconColor="white"
+          border={false}
+          color="#fff"
+          // isFixed={false}
+          title=' '
+          v-slots={{
+            default: () => (
+              <div class={styles.headerContent}>
+                <Image class={styles.headerImg} src={recordBg} />
+
+                <div class={styles.userInfo}>
+                  <img class={styles.userImg} src={this.userInfo.avatar || iconStudent} />
+                  <span class={styles.userName}>{this.userInfo.username}</span>
+                  {this.userInfo.subjectName ? <span class={styles.subjectName}>{this.userInfo.subjectName}</span> : ''}
+                </div>
+
+                <div class={styles.headerCount}>
+                  <div class={styles.headerItem}>
+                    <span class={styles.num}>
+                      {this.userTrainOverView.trainDays}天
+                    </span>
+                    <span>累计练习天数</span>
+                  </div>
+                  <div class={styles.headerItem}>
+                    <span class={styles.num}>
+                      {this.userTrainOverView.trainTime}分钟
+                    </span>
+                    <span>累计练习时长</span>
+                  </div>
+                  <div class={styles.headerItem}>
+                    <span class={styles.num}>
+                      {this.userTrainOverView.trainNum}次
+                    </span>
+                    <span>累计练习次数</span>
+                  </div>
+                </div>
+              </div>
+              //   <div class={[styles.certHeader, this.headStatus ? styles.certWhite : null]}>
+              //     老师认证
+              //   </div>
+            )
+          }}
+        />
+        {/* </Sticky> */}
+
+        <div style={{ overflow: 'hidden', paddingBottom: '12px' }}>
+          <Cell
+            class={styles.memberCell}
+            v-slots={{
+              title: () => (
+                <div class={styles.rTitle}>
+                  <span>评测记录</span>
+                </div>
+              ),
+              default: () => (
+                <div
+                  class={styles.dataItem}
+                  onClick={() => (this.timeStatus = true)}
+                >
+                  {dayjs(this.currentDate).format('YYYY年MM月')}
+                  <Icon
+                    classPrefix="iconfont"
+                    name="down"
+                    size={14}
+                    color="var(--van-primary)"
+                  />
+                </div>
+              )
+            }}
+          ></Cell>
+
+          <Popup
+            v-model:show={this.timeStatus}
+            position="bottom"
+            round
+            closeOnPopstate
+          >
+            <DatetimePicker
+              type="year-month"
+              v-model={this.currentDate}
+              formatter={formatterDate}
+              onCancel={() => {
+                this.timeStatus = false
+              }}
+              onConfirm={this.onSearch}
+            />
+          </Popup>
+          {this.dataShow ? (
+            <List
+              v-model:loading={this.loading}
+              finished={this.finished}
+              finishedText=" "
+              immediateCheck={false}
+              class={[styles.liveList, 'mb12']}
+              onLoad={this.getList}
+            >
+              {this.list.map((item: any) => (
+                <CellGroup
+                  class={styles['data-content']}
+                  border={false}
+                  onClick={() => {
+                    const behaviorId = +new Date()
+                    // 酷乐秀云教练的部署目录
+                    const musicScorePath = '/klx-music-score/'
+                    //https://dev.colexiu.com/klx-music-score/#/evaluat-report?id=6856&musicRenderType=staff&systemType=student&Authorization=bearer%2043d42470-e968-4efe-a17a-b859a350cbff
+                    postMessage({
+                      api: 'openAccompanyWebView',
+                      content: {
+                        url:
+                          location.origin +
+                          musicScorePath +
+                          '#/' +
+                          'evaluat-report?id=' +
+                          item.id +
+                          '&behaviorId=' +
+                          behaviorId,
+                        orientation: 0,
+                        isHideTitle: true,
+                        statusBarTextColor: false,
+                        isOpenLight: true
+                      }
+                    })
+                    return
+                  }}
+                >
+                  <Cell
+                    center
+                    v-slots={{
+                      title: () => (
+                        <div class={styles.dataTitle}>
+                          <span>{item.sysMusicScoreName}</span>
+                          {item.videoFilePath && (
+                            <Icon name={iconVideo} size={20} />
+                          )}
+                        </div>
+                      ),
+                      default: () => (
+                        <div class={styles.dataItem}>
+                          {dayjs(item.createTime).format('YYYY/MM/DD HH:mm')}
+                        </div>
+                      )
+                    }}
+                  ></Cell>
+                  <Cell
+                    center
+                    style={{ paddingLeft: '5px', paddingRight: '5px' }}
+                    v-slots={{
+                      title: () => (
+                        <Grid border={false} columnNum={5}>
+                          <GridItem
+                            text="评测难度"
+                            v-slots={{
+                              icon: () => (
+                                <span style={{ color: '#000000' }}>
+                                  {levelMember[item.heardLevel]}
+                                </span>
+                              )
+                            }}
+                          ></GridItem>
+                          <GridItem
+                            text="评测分数"
+                            v-slots={{
+                              icon: () => (
+                                <span style={{ color: '#000000' }}>
+                                  {item.score}分
+                                </span>
+                              )
+                            }}
+                          ></GridItem>
+                          <GridItem
+                            text="音准"
+                            v-slots={{
+                              icon: () => (
+                                <span style={{ color: '#01C1B5' }}>
+                                  {item.intonation}分
+                                </span>
+                              )
+                            }}
+                          ></GridItem>
+                          <GridItem
+                            text="节奏"
+                            v-slots={{
+                              icon: () => (
+                                <span style={{ color: '#FF802C' }}>
+                                  {item.cadence}分
+                                </span>
+                              )
+                            }}
+                          ></GridItem>
+                          <GridItem
+                            text="完成度"
+                            v-slots={{
+                              icon: () => (
+                                <span style={{ color: '#F79C00' }}>
+                                  {item.integrity}分
+                                </span>
+                              )
+                            }}
+                          ></GridItem>
+                        </Grid>
+                      )
+                    }}
+                  ></Cell>
+                </CellGroup>
+              ))}
+            </List>
+          ) : (
+            <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无记录" />
+          )}
+        </div>
+      </div>
+    )
+  }
+})

+ 96 - 0
src/teacher/statistics/home-statistics-detail/buy-item/index.module.less

@@ -0,0 +1,96 @@
+.cell {
+  padding: 13px 12px;
+
+  .top {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+  .timer {
+    display: flex;
+    align-items: center;
+    font-size: 13px;
+    color: #666666;
+    line-height: 18px;
+    img {
+      width: 16px;
+      height: 16px;
+      margin-right: 6px;
+    }
+  }
+
+  .userInfo {
+    display: flex;
+    align-items: center;
+    img {
+      width: 20px;
+      height: 20px;
+      border-radius: 20px;
+      margin-left: 4px;
+    }
+    .name {
+      font-size: 13px;
+      color: #333333;
+      line-height: 17px;
+      max-width: 70px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+  }
+
+  .content {
+    display: flex;
+    // align-items: center;
+    padding-top: 12px;
+  }
+  .cover {
+    width: 90px;
+    height: 51px;
+    border-radius: 4px;
+    overflow: hidden;
+    flex-shrink: 0;
+    margin-right: 10px;
+  }
+  .cover1 {
+    width: 51px;
+  }
+
+  .info {
+    // flex: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    padding: 3px 0;
+    .iTitle {
+      font-weight: 600;
+      font-size: 14px;
+      color: #333333;
+      line-height: 17px;
+      // width: 100%;
+      max-width: 220px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    .iPrice {
+      font-size: 13px;
+      color: #777777;
+      line-height: 17px;
+      display: flex;
+      align-items: center;
+
+      .price {
+        font-family: DINAlternate, DINAlternate;
+        font-weight: bold;
+        font-size: 18px;
+        color: #ff5a56;
+        line-height: 16px;
+        i {
+          font-style: normal;
+          font-size: 14px;
+        }
+      }
+    }
+  }
+}

+ 59 - 0
src/teacher/statistics/home-statistics-detail/buy-item/index.tsx

@@ -0,0 +1,59 @@
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import { Cell, CellGroup } from 'vant'
+import iconTimer from '@/common/images/icon_timer2.png'
+import icon_student from '@/common/images/icon_student.png'
+import { moneyFormat } from '@/helpers/utils'
+
+export default defineComponent({
+  name: 'teacher-item',
+  props: {
+    list: {
+      type: Array,
+      default: () => []
+    },
+    isSquare: {
+      type: Boolean,
+      default: false
+    }
+  },
+  setup(props) {
+    return () => (
+      <CellGroup border={false}>
+        {props.list.map((item: any) => (
+          <Cell class={styles.cell} center>
+            {{
+              title: () => (
+                <div class={styles.top}>
+                  <div class={styles.timer}>
+                    <img src={iconTimer} />
+                    <span>购买时间:{item.orderTime}</span>
+                  </div>
+                  <div class={styles.userInfo}>
+                    <span class={styles.name}>{item.userName}</span>
+                    <img src={item.userAvatar || icon_student} />
+                  </div>
+                </div>
+              ),
+              label: () => (
+                <div class={styles.content}>
+                  <img class={[styles.cover, props.isSquare && styles.cover1]} src={item.bizCover} />
+                  <div class={styles.info}>
+                    <div class={styles.iTitle}>{item.bizName}</div>
+                    <div class={styles.iPrice}>
+                      <span>预计收入</span>
+                      <span class={styles.price}>
+                        <i>¥</i>
+                        {moneyFormat(item.amount || 0)}
+                      </span>
+                    </div>
+                  </div>
+                </div>
+              )
+            }}
+          </Cell>
+        ))}
+      </CellGroup>
+    )
+  }
+})

+ 243 - 0
src/teacher/statistics/home-statistics-detail/echats/index.module.less

@@ -0,0 +1,243 @@
+.homeHead {
+  display: flex;
+  justify-content: space-between;
+  padding-bottom: 12px;
+
+  .title {
+    display: flex;
+    align-items: center;
+    font-weight: 600;
+    font-size: 15px;
+    color: #333333;
+    line-height: 20px;
+
+    img {
+      width: 18px;
+      height: 18px;
+      margin-right: 6px;
+    }
+  }
+
+  .right {
+    display: flex;
+    align-items: center;
+  }
+
+  .showItem {
+    display: flex;
+    align-items: center;
+    font-size: 13px;
+    color: #131415;
+    line-height: 18px;
+
+    img {
+      margin-left: 4px;
+      width: 9px;
+      height: 5px;
+    }
+
+    &.showItemActive {
+      color: #2dc7aa;
+    }
+  }
+}
+
+.eChartSection {
+  background-color: #fff;
+  box-shadow: 0px 2px 10px 0px rgba(229, 229, 229, 0.1);
+  border-radius: 10px;
+  padding: 12px;
+  margin: 0 14px;
+  .eChartTitle {
+    display: flex;
+    justify-content: space-between;
+    background: #f8f8f8;
+    border-radius: 4px;
+    padding: 6px 12px;
+
+    .left {
+      display: flex;
+      align-items: center;
+    }
+
+    .item {
+      display: flex;
+      align-items: center;
+      margin-right: 12px;
+      --color: #2dc7aa;
+
+      &:last-child {
+        margin-right: 0;
+      }
+
+      .line {
+        display: inline-block;
+        width: 10px;
+        height: 3px;
+        background: var(--color);
+        border-radius: 3px;
+      }
+
+      .text {
+        font-size: 12px;
+        color: #333333;
+        line-height: 16px;
+        padding: 0 4px 0 6px;
+      }
+
+      .num {
+        font-weight: 600;
+        font-size: 12px;
+        color: var(--color);
+        line-height: 16px;
+      }
+    }
+  }
+
+  .eChart {
+    height: 240px;
+    padding: 0;
+  }
+}
+
+.popupContainer {
+  // max-height: 504px;
+  // overflow-x: hidden;
+  // overflow-y: auto;
+  .popupTitle {
+    position: sticky;
+    z-index: 1;
+    top: 0;
+    text-align: center;
+    font-weight: 600;
+    font-size: 18px;
+    color: #333333;
+    line-height: 24px;
+    padding: 18px 0 12px;
+  }
+
+  // .popupSearchList {
+  // min-height: 30vh;
+  // max-height: 50vh;
+  // overflow: hidden auto;
+  // }
+
+  .popupSection {
+    padding: 0 16px 18px;
+    .title {
+      display: flex;
+      justify-content: space-between;
+      padding-bottom: 10px;
+      span {
+        display: flex;
+        align-items: center;
+        font-weight: 600;
+        font-size: 15px;
+        color: #333333;
+        line-height: 18px;
+        &::before {
+          content: '';
+          display: inline-block;
+          width: 3px;
+          height: 12px;
+          background: linear-gradient(180deg, #59e5d4 0%, #2dc7aa 100%);
+          border-radius: 2px;
+          margin-right: 4px;
+        }
+      }
+    }
+
+    .timeCount {
+      display: flex;
+      align-items: center;
+
+      p {
+        margin-left: 10px;
+        flex: 1;
+        background: #f8f8f8;
+        border: 1px solid #f8f8f8;
+        border-radius: 4px;
+        font-size: 13px;
+        color: #999999;
+        line-height: 18px;
+        text-align: center;
+        padding: 6px 0;
+        &:first-child {
+          margin-left: 0;
+        }
+
+        &.active {
+          background: #e9fff8;
+          border-radius: 4px;
+          border: 1px solid #2dc7aa;
+          color: #2dc7aa;
+        }
+      }
+    }
+
+    .timeSubject {
+      flex-wrap: wrap;
+      margin-left: -5px;
+      margin-right: -5px;
+      p {
+        width: calc(33.333% - 10px);
+        padding: 6px 3px;
+        margin: 0 5px;
+        flex: none;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        margin-bottom: 9px;
+        box-sizing: border-box;
+        &:first-child {
+          margin-left: 5px;
+        }
+      }
+    }
+
+    .timeRang {
+      margin-top: 10px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+
+      .timeInput {
+        width: 159px;
+        line-height: 32px;
+        text-align: center;
+        background: #f8f8f8;
+        border-radius: 4px;
+        font-size: 13px;
+        color: #999999;
+        cursor: pointer;
+      }
+      .hasValue {
+        color: #333;
+      }
+
+      .timeUnit {
+        width: 12px;
+        height: 1px;
+        background: #d0d0d0;
+      }
+    }
+  }
+
+  .popupBottom {
+    position: sticky;
+    z-index: 1;
+    bottom: 0;
+    border-top: 1px solid #f2f2f2;
+    padding: 20px 13px 30px;
+    display: flex;
+    align-items: center;
+    :global {
+      .van-button {
+        font-size: 16px;
+      }
+      .van-button + .van-button {
+        margin-left: 15px;
+      }
+    }
+  }
+}

+ 479 - 0
src/teacher/statistics/home-statistics-detail/echats/index.tsx

@@ -0,0 +1,479 @@
+import {
+  defineComponent,
+  nextTick,
+  onMounted,
+  PropType,
+  reactive,
+  ref,
+  shallowReactive,
+  watch
+} from 'vue'
+import styles from './index.module.less'
+import icon1 from '../../images/icon1.png'
+import iconArrow from '../../images/icon-arrow.png'
+import iconArrow1 from '../../images/icon-arrow1.png'
+import iconArrow11 from '../../images/icon-arrow1-1.png'
+import * as echarts from 'echarts/core'
+import {
+  LineChart
+  // LineSeriesOption
+} from 'echarts/charts'
+// import { PieChart } from 'echarts/charts'
+import {
+  TitleComponent,
+  // 组件类型的定义后缀都为 ComponentOption
+  // TitleComponentOption,
+  TooltipComponent,
+  // TooltipComponentOption,
+  GridComponent,
+  // 数据集组件
+  DatasetComponent,
+  // DatasetComponentOption,
+  // 内置数据转换器组件 (filter, sort)
+  // TransformComponent,
+  LegendComponent,
+  ToolboxComponent,
+  DataZoomComponent
+} from 'echarts/components'
+import { LabelLayout } from 'echarts/features'
+import { CanvasRenderer } from 'echarts/renderers'
+import { Button, DatetimePicker, Popup } from 'vant'
+import { formatterDatePicker } from '@/helpers/utils'
+import dayjs from 'dayjs'
+import { getTimeRange, TIME_TYPE } from '../../home-statistics'
+
+// 注册必须的组件
+echarts.use([
+  TitleComponent,
+  TooltipComponent,
+  GridComponent,
+  DatasetComponent,
+  // TransformComponent,
+  LabelLayout,
+  // UniversalTransition,
+  CanvasRenderer,
+  // PieChart,
+  ToolboxComponent,
+  LegendComponent,
+  DataZoomComponent,
+  LineChart
+])
+
+const lineChartOption = (xAxisData: any, seriesData: any) => {
+  return {
+    title: {
+      text: '单位:次',
+      textStyle: {
+        color: '#777777',
+        fontSize: 13,
+        fontWeight: 400
+      }
+    },
+    legend: { show: false },
+    emphasis: { lineStyle: { width: 2 } },
+    xAxis: {
+      boundaryGap: false,
+      data: xAxisData,
+      type: 'category',
+      axisLine: { lineStyle: { color: '#8C8C8C' } },
+      lineStyle: { color: '#F2F2F2' }
+    },
+    color: [
+      '#2DC7AA',
+      '#FF6079'
+      // '#2DC7AA',
+      // '#FF602C',
+      // '#91DD1C',
+      // '#FFA92C',
+      // '#BE7E2E',
+      // '#1C96DD',
+      // '#D22CFF',
+      // '#FF3C3C',
+      // '#1AEE3E',
+      // '#00c9ff'
+    ],
+    series: [
+      {
+        lineStyle: { width: 1 },
+        data: seriesData[0],
+        symbol: 'circle',
+        name: '浏览次数',
+        type: 'line',
+        emphasis: { lineStyle: { width: 1 } }
+      },
+      {
+        lineStyle: { width: 1 },
+        data: seriesData[1],
+        symbol: 'circle',
+        name: '购买次数',
+        type: 'line',
+        areaStyle: {
+          color: {
+            type: 'linear',
+            x: 0,
+            y: 0,
+            x2: 0,
+            y2: 1,
+            colorStops: [
+              {
+                offset: 0,
+                color: 'rgba(255, 96, 121, 0.23)'
+                // 0% 处的颜色
+              },
+              {
+                offset: 1,
+                // 100% 处的颜色
+                color: 'rgba(255, 96, 121, 0)'
+              }
+            ]
+          }
+        },
+        emphasis: { lineStyle: { width: 1 } }
+      }
+    ],
+    grid: {
+      bottom: '3%',
+      containLabel: true,
+      left: '3%',
+      right: '5%',
+      top: '40'
+    },
+    tooltip: {
+      trigger: 'axis',
+      confine: true,
+      formatter: function (params: any) {
+        return params[0].name
+      },
+      backgroundColor: '#FF6079',
+      borderWidth: 0,
+      borderRadius: 24,
+      padding: [1, 4],
+      textStyle: {
+        color: '#FFFFFF',
+        fontSize: 12
+      }
+    },
+    yAxis: {
+      type: 'value',
+      splitLine: {
+        axisLine: { lineStyle: { color: '#8C8C8C' } },
+        lineStyle: { color: ['#f2f2f2'], type: 'dashed' }
+      }
+    },
+    dataZoom: [{ type: 'inside', throttle: 100 }],
+    toolbox: { feature: { saveAsImage: { show: false } } }
+  }
+}
+export default defineComponent({
+  name: 'eChats-model',
+  props: {
+    obj: {
+      type: Object,
+      default: () => ({})
+    },
+    currentType: {
+      type: String as PropType<TIME_TYPE>,
+      default: 'MONTH'
+    }
+  },
+  emits: ['confirm'],
+  setup(props, { emit }) {
+    const chartId = 'eChart' + Date.now()
+    const statisticCounts = ref({
+      browseCount: 0,
+      buyCount: 0
+    })
+    const currentType = ref<TIME_TYPE>(props.currentType)
+    const timeRange = getTimeRange(currentType.value)
+    const searchStatus = ref(false)
+    const forms = reactive({
+      loading: false,
+      dataShow: true,
+      subjectId: '' as any, // 选择的声部
+      subjectList: [] as any,
+      startTimeStatus: false,
+      endTimeMinDate: new Date(),
+      endTimeMaxDate: dayjs(new Date()).add(1, 'year').toDate(),
+      endTimeStatus: false,
+      startTime: new Date(timeRange?.startTime || ''),
+      startTimeStr: timeRange?.startTime || '',
+      endTime: new Date(timeRange?.endTime || ''),
+      endTimeStr: timeRange?.endTime || ''
+    })
+    const showTimeRange = shallowReactive({
+      startTime: timeRange?.startTime || '',
+      endTime: timeRange?.endTime || ''
+    })
+    let myChart: echarts.ECharts
+
+    const _initData = () => {
+      nextTick(() => {
+        statisticCounts.value.browseCount = props.obj.browseCount || 0
+        statisticCounts.value.buyCount = props.obj.buyCount || 0
+        myChart.clear()
+        lineChartOption &&
+          myChart.setOption(
+            lineChartOption(props.obj.xAxisData, props.obj.yAxisData)
+          )
+        myChart.on('highlight', function (params: any) {
+          const batch = params.batch || []
+          const options: any = myChart.getOption()
+          batch.forEach((item: any) => {
+            const batchIndex = item.dataIndex
+
+            const browseCount = options.series[0].data[batchIndex]
+            const buyCount = options.series[1].data[batchIndex]
+            statisticCounts.value = {
+              browseCount,
+              buyCount
+            }
+          })
+        })
+      })
+    }
+
+    nextTick(() => {
+      myChart = echarts.init(document.getElementById(chartId) as HTMLDivElement)
+      _initData()
+    })
+
+    watch(
+      () => props.obj,
+      () => {
+        _initData()
+      },
+      {
+        deep: true
+      }
+    )
+
+    watch(
+      () => props.currentType,
+      () => {
+        currentType.value = props.currentType
+      }
+    )
+
+    const onChangeTime = (type: TIME_TYPE) => {
+      if (currentType.value === type) return
+      currentType.value = type
+      resetTime(type)
+      // emit('confirm', currentType.value)
+    }
+
+    // 格式化
+    const resetTime = (type: TIME_TYPE) => {
+      const timeRang = getTimeRange(type)
+
+      forms.startTime = new Date(timeRang?.startTime || '')
+      forms.startTimeStr = timeRang?.startTime || ''
+      forms.endTimeMinDate = dayjs(timeRang?.startTime || '').toDate()
+      forms.endTimeMaxDate = dayjs(timeRang?.startTime || '')
+        .add(1, 'year')
+        .toDate()
+      forms.endTime = new Date(timeRang?.endTime || '')
+      forms.endTimeStr = timeRang?.endTime || ''
+    }
+
+    return () => (
+      <div class={styles.eChartSection}>
+        <div class={styles.homeHead}>
+          <div class={styles.title}>
+            <img src={icon1} />
+            <span>浏览/购买</span>
+          </div>
+
+          <div class={styles.right}>
+            <div
+              class={[
+                styles.showItem,
+                searchStatus.value && styles.showItemActive
+              ]}
+              onClick={() => (searchStatus.value = true)}
+            >
+              <span>
+                {showTimeRange.startTime}~{showTimeRange.endTime}
+              </span>
+              <img src={searchStatus.value ? iconArrow11 : iconArrow1} />
+            </div>
+          </div>
+        </div>
+        <div class={styles.eChartTitle}>
+          <div class={styles.left}>
+            <div class={styles.item} style="--color: #2DC7AA">
+              <span class={styles.line}></span>
+              <span class={styles.text}>浏览次数</span>
+              <span class={styles.num}>
+                {statisticCounts.value.browseCount}次
+              </span>
+            </div>
+            <div class={styles.item} style="--color: #FF6079">
+              <span class={styles.line}></span>
+              <span class={styles.text}>购买次数</span>
+              <span class={styles.num}>{statisticCounts.value.buyCount}次</span>
+            </div>
+          </div>
+        </div>
+
+        <div class={styles.eChart}>
+          <div id={chartId} style="width: 100%; height: 100%;"></div>
+        </div>
+
+        <Popup
+          v-model:show={searchStatus.value}
+          closeable
+          round
+          position="bottom"
+          teleport="body"
+        >
+          <div class={styles.popupContainer}>
+            <div class={styles.popupTitle}>筛选</div>
+
+            <div class={styles.popupSearchList}>
+              <div class={styles.popupSection}>
+                <div class={styles.title}>
+                  <span>时间</span>
+                </div>
+
+                <div class={styles.timeCount}>
+                  <p
+                    onClick={() => onChangeTime('MONTH')}
+                    class={currentType.value === 'MONTH' ? styles.active : ''}
+                  >
+                    本月
+                  </p>
+                  <p
+                    onClick={() => onChangeTime('THREE_MONTH')}
+                    class={
+                      currentType.value === 'THREE_MONTH' ? styles.active : ''
+                    }
+                  >
+                    近三个月
+                  </p>
+                  <p
+                    onClick={() => onChangeTime('HALF_YEAR')}
+                    class={
+                      currentType.value === 'HALF_YEAR' ? styles.active : ''
+                    }
+                  >
+                    近半年
+                  </p>
+                  <p
+                    onClick={() => onChangeTime('YEAR')}
+                    class={currentType.value === 'YEAR' ? styles.active : ''}
+                  >
+                    近一年
+                  </p>
+                </div>
+
+                <div class={styles.timeRang}>
+                  <p
+                    class={[
+                      styles.timeInput,
+                      forms.startTimeStr && styles.hasValue
+                    ]}
+                    onClick={() => (forms.startTimeStatus = true)}
+                  >
+                    {forms.startTimeStr || '起始时间'}
+                  </p>
+                  <p class={styles.timeUnit}></p>
+                  <p
+                    class={[
+                      styles.timeInput,
+                      forms.endTimeStr && styles.hasValue
+                    ]}
+                    onClick={() => (forms.endTimeStatus = true)}
+                  >
+                    {forms.endTimeStr || '终止时间'}
+                  </p>
+                </div>
+              </div>
+            </div>
+
+            <div class={styles.popupBottom}>
+              <Button
+                round
+                block
+                type="default"
+                onClick={() => {
+                  currentType.value = props.currentType
+                  resetTime(props.currentType)
+                }}
+              >
+                重置
+              </Button>
+              <Button
+                round
+                block
+                type="primary"
+                onClick={() => {
+                  emit('confirm', {
+                    startTime: forms.startTimeStr,
+                    endTime: forms.endTimeStr
+                  })
+                  // 显示的时间
+                  showTimeRange.startTime = forms.startTimeStr
+                  showTimeRange.endTime = forms.endTimeStr
+                  searchStatus.value = false
+                }}
+              >
+                确定
+              </Button>
+            </div>
+          </div>
+        </Popup>
+
+        {/* 开始日期 */}
+        <Popup
+          v-model:show={forms.startTimeStatus}
+          position="bottom"
+          round
+          class={'popupBottomSearch'}
+          teleport={'body'}
+        >
+          <DatetimePicker
+            v-model={forms.startTime}
+            type="date"
+            formatter={formatterDatePicker}
+            onCancel={() => (forms.startTimeStatus = false)}
+            onConfirm={(val: any) => {
+              forms.startTime = val
+              forms.startTimeStr = dayjs(val).format('YYYY-MM-DD')
+              forms.startTimeStatus = false
+              forms.endTime = null as any
+              forms.endTimeStr = ''
+              forms.endTimeMinDate = dayjs(val || new Date()).toDate()
+              forms.endTimeMaxDate = dayjs(val || new Date())
+                .add(1, 'year')
+                .toDate()
+              currentType.value = '' as any
+            }}
+          />
+        </Popup>
+        {/* 结束日期 */}
+        <Popup
+          v-model:show={forms.endTimeStatus}
+          position="bottom"
+          round
+          class={'popupBottomSearch'}
+          teleport={'body'}
+        >
+          <DatetimePicker
+            v-model={forms.endTime}
+            type="date"
+            minDate={forms.endTimeMinDate}
+            maxDate={forms.endTimeMaxDate}
+            formatter={formatterDatePicker}
+            onCancel={() => (forms.endTimeStatus = false)}
+            onConfirm={(val: any) => {
+              forms.endTime = val
+              forms.endTimeStatus = false
+              forms.endTimeStr = dayjs(val).format('YYYY-MM-DD')
+              currentType.value = '' as any
+            }}
+          />
+        </Popup>
+      </div>
+    )
+  }
+})

+ 38 - 0
src/teacher/statistics/home-statistics-detail/index.module.less

@@ -0,0 +1,38 @@
+.homeStatistics {
+  min-height: 100vh;
+  // background: #F6F7F8;
+  background: linear-gradient(to bottom, #beffe6 0px, #f6f7f8 595px);
+  // background: linear-gradient(90deg, #FE4083 0%, #FEC3D4 100%);
+}
+
+.tabs {
+  :global {
+    .van-tabs__nav {
+      background-color: transparent;
+      padding-bottom: 12px;
+    }
+    .van-tab {
+      font-size: 16px;
+      color: #666;
+    }
+    .van-tab--active {
+      font-weight: 600;
+      color: #333333;
+    }
+    .van-tabs__line {
+      width: 16px;
+      height: 4px;
+      background: #2dc7aa;
+      border-radius: 2px;
+    }
+    .van-tabs__content, .van-tab__panel {
+      height: calc(100vh - var(--van-tabs-line-height) - var(--header-height, 0));
+      overflow-x: hidden;
+      overflow-y: auto;
+    }
+    .van-tab__panel {
+      height: 100%;
+    }
+  }
+}
+

+ 78 - 0
src/teacher/statistics/home-statistics-detail/index.tsx

@@ -0,0 +1,78 @@
+import { defineComponent, reactive } from 'vue'
+import styles from './index.module.less'
+import { Tab, Tabs } from 'vant'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
+import List from './list'
+import request from '@/helpers/request'
+
+export default defineComponent({
+  name: 'HomeStatistics',
+  setup() {
+    const typeList = reactive({
+      VIP_COURSE: 0,
+      PRACTICE: 0,
+      GROUP: 0,
+      LIVE: 0,
+      VIDEO: 0,
+      MUSIC: 0
+    })
+    const getSysConfig = async () => {
+      try {
+        const { data } = await request.get('/api-teacher/sysConfig/list', {
+          params: {
+            group: 'ACCOUNT_PERIOD'
+          }
+        })
+        const result = data || []
+        result.forEach((item: any) => {
+          if (item.paramName === 'vip_course_account_period') {
+            typeList.VIP_COURSE = Number(item.paramValue || 0)
+          } else if (item.paramName === 'practice_account_period') {
+            typeList.PRACTICE = Number(item.paramValue || 0)
+          } else if (item.paramName === 'group_course_account_period') {
+            typeList.GROUP = Number(item.paramValue || 0)
+          } else if (item.paramName === 'live_account_period') {
+            typeList.LIVE = Number(item.paramValue || 0)
+          } else if (item.paramName === 'video_account_period') {
+            typeList.VIDEO = Number(item.paramValue || 0)
+          } else if (item.paramName === 'music_account_period') {
+            typeList.MUSIC = Number(item.paramValue || 0)
+          }
+        })
+      } catch {
+        //
+      }
+    }
+
+    getSysConfig()
+    return () => (
+      <div class={styles.homeStatistics}>
+        <TheSticky position="top">
+          <ColHeader border={false} background="transparent" />
+        </TheSticky>
+
+        <Tabs class={styles.tabs}>
+          <Tab title="VIP定制课" name="VIP_COURSE">
+            <List accountPeriod={typeList.VIP_COURSE} type="VIP_COURSE" />
+          </Tab>
+          <Tab title="趣纠课" name="PRACTICE">
+            <List accountPeriod={typeList.PRACTICE} type="PRACTICE" />
+          </Tab>
+          <Tab title="小组课" name="GROUP">
+            <List accountPeriod={typeList.GROUP} type="GROUP" />
+          </Tab>
+          <Tab title="直播课" name="LIVE">
+            <List accountPeriod={typeList.LIVE} type="LIVE" />
+          </Tab>
+          <Tab title="视频课" name="VIDEO">
+            <List accountPeriod={typeList.VIDEO} type="VIDEO" />
+          </Tab>
+          <Tab title="乐谱" name="MUSIC">
+            <List accountPeriod={typeList.MUSIC} type="MUSIC" />
+          </Tab>
+        </Tabs>
+      </div>
+    )
+  }
+})

+ 84 - 0
src/teacher/statistics/home-statistics-detail/list/index.module.less

@@ -0,0 +1,84 @@
+.list {
+  padding: 12px 0 30px;
+}
+
+.expectedIncome {
+  background: #ffffff;
+  border-radius: 10px;
+  margin: 12px 14px 0;
+  overflow: hidden;
+  :global {
+    .van-list__loading,
+    .van-list__finished-text,
+    .van-list__error-text {
+      width: 100%;
+    }
+  }
+  .incomeTitle {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 15px 12px 12px;
+    .title {
+      display: flex;
+      align-items: center;
+      font-weight: 600;
+      font-size: 15px;
+      color: #333333;
+      line-height: 20px;
+    }
+    img {
+      width: 18px;
+      height: 18px;
+      margin-right: 6px;
+    }
+    .price {
+      font-weight: bold;
+      font-size: 16px;
+      color: #333333;
+      line-height: 16px;
+      font-family: DINAlternate, DINAlternate;
+      span {
+        font-size: 14px;
+      }
+    }
+  }
+  .incomeTip {
+    font-size: 13px;
+    color: #ef8548;
+    line-height: 18px;
+    background: #fff9f0;
+    box-shadow: 0px 2px 10px 0px rgba(229, 229, 229, 0.1);
+    border-radius: 6px;
+    border: 1px solid #ffefdf;
+    padding: 5px 10px;
+    margin: 0 12px 0;
+  }
+
+  .element {
+    position: relative;
+    height: 0;
+    border-top: 1px dashed #dedede;
+    margin: 16px 19px 0;
+
+    &::before,
+    &::after {
+      content: '';
+      position: absolute;
+      z-index: 1;
+      bottom: -6.5px;
+      display: inline-block;
+      width: 14px;
+      height: 14px;
+      background: #f6f7f8;
+      border-radius: 50%;
+    }
+
+    &::before {
+      left: -26px;
+    }
+    &::after {
+      right: -26px;
+    }
+  }
+}

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