lex-xin 4 месяцев назад
Родитель
Сommit
2f8b62fd11
100 измененных файлов с 3208 добавлено и 393 удалено
  1. 1 0
      dist/assets/index-legacy.61b668c0.js
  2. 0 0
      dist/assets/index-legacy.7e5345b3.js
  3. 1 0
      dist/assets/index-legacy.a71f6993.js
  4. 0 0
      dist/assets/index.601b709a.js
  5. 0 0
      dist/assets/index.69da6d0f.css
  6. 0 0
      dist/assets/index.71dc593f.js
  7. 1 0
      dist/assets/index.8e956944.js
  8. 1 0
      dist/assets/polyfills-legacy.0eefb68f.js
  9. 1 0
      dist/assets/polyfills-legacy.e0a67103.js
  10. 1 0
      dist/assets/routes-common.d03e4a3e.js
  11. 1 0
      dist/assets/teacher-legacy.46883fc6.js
  12. 1 0
      dist/assets/teacher-legacy.bf0433c1.js
  13. 1 0
      dist/assets/teacher.7a8e20c0.js
  14. 1 0
      dist/assets/teacher.c6f32188.js
  15. 1 0
      dist/assets/tenant-legacy.1e440264.js
  16. 1 0
      dist/assets/tenant-legacy.8b8ba6da.js
  17. 0 0
      dist/assets/tenant.71ed39b9.js
  18. 4 0
      dist/index.html
  19. 9 0
      dist/teacher.html
  20. 9 0
      dist/tenant.html
  21. BIN
      src/common/images/icon_discount.png
  22. BIN
      src/common/images/icon_studycard.png
  23. 6 2
      src/constant/index.ts
  24. 17 1
      src/router/routes-student.ts
  25. 9 1
      src/router/routes-teacher.ts
  26. BIN
      src/student/activePage/double12Active/imgs/boxImg.png
  27. BIN
      src/student/activePage/double12Active/imgs/class.png
  28. BIN
      src/student/activePage/double12Active/imgs/head.png
  29. BIN
      src/student/activePage/double12Active/imgs/shadow.png
  30. BIN
      src/student/activePage/double12Active/imgs/subBtn.png
  31. BIN
      src/student/activePage/double12Active/imgs/svipCon1.png
  32. BIN
      src/student/activePage/double12Active/imgs/svipCon2.png
  33. BIN
      src/student/activePage/double12Active/imgs/tip1Img.png
  34. BIN
      src/student/activePage/double12Active/imgs/tip2Img.png
  35. 336 0
      src/student/activePage/double12Active/index.module.less
  36. 373 0
      src/student/activePage/double12Active/index.tsx
  37. BIN
      src/student/discount-card/images/bg.png
  38. BIN
      src/student/discount-card/images/btn-bg.png
  39. BIN
      src/student/discount-card/images/card-bg.png
  40. BIN
      src/student/discount-card/images/icon-star.png
  41. BIN
      src/student/discount-card/images/icon-svip-disabled.png
  42. BIN
      src/student/discount-card/images/icon-svip.png
  43. BIN
      src/student/discount-card/images/icon-vip-disabled.png
  44. BIN
      src/student/discount-card/images/icon-vip.png
  45. BIN
      src/student/discount-card/images/memo.png
  46. BIN
      src/student/discount-card/images/ring.png
  47. BIN
      src/student/discount-card/images/tip.png
  48. BIN
      src/student/discount-card/images/title1.png
  49. 294 0
      src/student/discount-card/index.module.less
  50. 267 0
      src/student/discount-card/index.tsx
  51. 1 1
      src/student/practice-class/index.tsx
  52. 1 1
      src/student/practice-class/model/all-search.tsx
  53. 4 0
      src/student/teacher-dependent/components/live.module.less
  54. 3 1
      src/student/teacher-dependent/components/live.tsx
  55. 13 2
      src/student/teacher-dependent/components/practice.module.less
  56. 27 20
      src/student/teacher-dependent/components/practice.tsx
  57. 45 0
      src/student/teacher-dependent/components/tips/index.module.less
  58. 40 0
      src/student/teacher-dependent/components/tips/index.tsx
  59. 62 0
      src/student/teacher-dependent/components/video-item/index.module.less
  60. 92 0
      src/student/teacher-dependent/components/video-item/index.tsx
  61. 4 0
      src/student/teacher-dependent/components/video.module.less
  62. 4 2
      src/student/teacher-dependent/components/video.tsx
  63. 168 0
      src/student/teacher-dependent/components/vip.module.less
  64. 581 0
      src/student/teacher-dependent/components/vip.tsx
  65. BIN
      src/student/teacher-dependent/images/icon-add.png
  66. BIN
      src/student/teacher-dependent/images/icon-cert.png
  67. BIN
      src/student/teacher-dependent/images/icon-message.png
  68. BIN
      src/student/teacher-dependent/images/icon1.png
  69. BIN
      src/student/teacher-dependent/images/icon2.png
  70. BIN
      src/student/teacher-dependent/images/icon3.png
  71. BIN
      src/student/teacher-dependent/images/icon_subject1.png
  72. 66 33
      src/student/teacher-dependent/model/teacher-header.module.less
  73. 75 118
      src/student/teacher-dependent/model/teacher-header.tsx
  74. 68 5
      src/student/teacher-dependent/teacher-home.module.less
  75. 31 14
      src/student/teacher-dependent/teacher-home.tsx
  76. 32 1
      src/student/trade/tradeOrder.ts
  77. BIN
      src/styles/font/DIN_Alternate_Bold.ttf
  78. 4 0
      src/styles/font/index.less
  79. 1 0
      src/styles/index.less
  80. 1 1
      src/teacher/extend-plan/index.tsx
  81. 2 2
      src/teacher/income-consus/echarts.ts
  82. 2 2
      src/teacher/income-consus/index.tsx
  83. 1 1
      src/teacher/piano-room/tradeOrder.ts
  84. BIN
      src/teacher/practice-class/icon-question.png
  85. 1 1
      src/teacher/practice-class/model/timer.tsx
  86. 8 0
      src/teacher/practice-class/practice-setting.module.less
  87. 50 168
      src/teacher/practice-class/practice-setting.tsx
  88. 140 0
      src/teacher/practice-class/timer/timer.module.less
  89. 309 0
      src/teacher/practice-class/timer/timer.tsx
  90. 2 2
      src/tenant/exercise-record/echats.ts
  91. 1 0
      src/tenant/music/coursewarePlay/component/courseware-tips/index.module.less
  92. 6 1
      src/tenant/music/coursewarePlay/component/courseware-type/index.module.less
  93. 1 1
      src/tenant/music/coursewarePlay/component/courseware-type/index.tsx
  94. 8 1
      src/tenant/music/coursewarePlay/component/o-guide/index.module.less
  95. 1 1
      src/tenant/music/coursewarePlay/component/play-loading/index.module.less
  96. 6 1
      src/tenant/music/coursewarePlay/component/point.module.less
  97. 1 1
      src/tenant/music/coursewarePlay/component/points.tsx
  98. 6 4
      src/tenant/music/coursewarePlay/component/tips/index.module.less
  99. 2 2
      src/tenant/music/coursewarePlay/component/tools/pen.module.less
  100. 2 2
      src/tenant/music/coursewarePlay/component/tools/pen.tsx

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/index-legacy.61b668c0.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index-legacy.7e5345b3.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/index-legacy.a71f6993.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index.601b709a.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index.69da6d0f.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index.71dc593f.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/index.8e956944.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/polyfills-legacy.0eefb68f.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/polyfills-legacy.e0a67103.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/routes-common.d03e4a3e.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/teacher-legacy.46883fc6.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/teacher-legacy.bf0433c1.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/teacher.7a8e20c0.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/teacher.c6f32188.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/tenant-legacy.1e440264.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
dist/assets/tenant-legacy.8b8ba6da.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/tenant.71ed39b9.js


+ 4 - 0
dist/index.html

@@ -67,7 +67,11 @@
   
   <!-- <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.e0a67103.js"></script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.0eefb68f.js"></script>
+>>>>>>> iteration-20241126
   <script nomodule id="vite-legacy-entry" data-src="./assets/index-legacy.8b1e4cd2.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
 </body>
 

+ 9 - 0
dist/teacher.html

@@ -52,7 +52,11 @@
             _T_));
     })(Object);
   </script>
+<<<<<<< HEAD
   <script type="module" crossorigin src="./assets/teacher.c6f32188.js"></script>
+=======
+  <script type="module" crossorigin src="./assets/teacher.7a8e20c0.js"></script>
+>>>>>>> iteration-20241126
   <link rel="modulepreload" href="./assets/ResizeObserver.es.de70eb81.js">
   <link rel="modulepreload" href="./assets/routes-common.d03e4a3e.js">
   <link rel="stylesheet" href="./assets/ResizeObserver.es.ea6352ab.css">
@@ -64,8 +68,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.e0a67103.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.46883fc6.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.0eefb68f.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.bf0433c1.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.e1921f49.js"></script>
+=======
+  <script type="module" crossorigin src="./assets/tenant.71ed39b9.js"></script>
+>>>>>>> iteration-20241126
   <link rel="modulepreload" href="./assets/ResizeObserver.es.de70eb81.js">
   <link rel="modulepreload" href="./assets/auth.54ddf2cf.js">
   <link rel="stylesheet" href="./assets/ResizeObserver.es.ea6352ab.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.e0a67103.js"></script>
   <script nomodule id="vite-legacy-entry" data-src="./assets/tenant-legacy.1e440264.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.0eefb68f.js"></script>
+  <script nomodule id="vite-legacy-entry" data-src="./assets/tenant-legacy.8b8ba6da.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration-20241126
 </body>
 
 </html>

BIN
src/common/images/icon_discount.png


BIN
src/common/images/icon_studycard.png


+ 6 - 2
src/constant/index.ts

@@ -1,6 +1,6 @@
 export const goodsType = {
   LIVE: '直播课',
-  PRACTICE: '陪练课',
+  PRACTICE: '趣纠课',
   VIDEO: '视频课',
   VIP: '开通VIP会员',
   SVIP: '开通SVIP会员',
@@ -47,6 +47,10 @@ export const memberSimpleType = {
   PERPETUAL: '永久'
 }
 
+export const studyCardType = {
+  YEAR: '一年期'
+}
+
 export const courseType = {
   NOT_START: '未开始',
   ING: '进行中',
@@ -55,7 +59,7 @@ export const courseType = {
 }
 
 export const bizStatus = {
-  PRACTICE: '陪练课',
+  PRACTICE: '趣纠课',
   LIVE: '直播课',
   VIDEO: '视频课',
   MUSIC: '乐谱',

+ 17 - 1
src/router/routes-student.ts

@@ -45,7 +45,7 @@ export default [
         name: 'practiceClass',
         component: () => import('@/student/practice-class/index'),
         meta: {
-          title: '陪练课'
+          title: '趣纠课'
         }
       },
       {
@@ -151,6 +151,22 @@ export default [
         meta: {
           title: '评测曲目'
         }
+      },
+      {
+        path: '/discount-card',
+        component: () =>
+          import('@/student/discount-card/index'),
+        meta: {
+          title: '畅学卡'
+        }
+      },
+      {
+        path: '/double12Active',
+        component: () =>
+          import('@/student/activePage/double12Active'),
+        meta: {
+          title: '双十二限时特惠'
+        }
       }
     ]
   },

+ 9 - 1
src/router/routes-teacher.ts

@@ -189,7 +189,15 @@ export default [
         name: 'practiceSetting',
         component: () => import('@/teacher/practice-class/practice-setting'),
         meta: {
-          title: '陪练课设置'
+          title: '趣纠课设置'
+        }
+      },
+      {
+        path: '/practiceSettingTimer',
+        name: 'practiceSettingTimer',
+        component: () => import('@/teacher/practice-class/timer/timer'),
+        meta: {
+          title: '设置约课时间段'
         }
       },
       {

BIN
src/student/activePage/double12Active/imgs/boxImg.png


BIN
src/student/activePage/double12Active/imgs/class.png


BIN
src/student/activePage/double12Active/imgs/head.png


BIN
src/student/activePage/double12Active/imgs/shadow.png


BIN
src/student/activePage/double12Active/imgs/subBtn.png


BIN
src/student/activePage/double12Active/imgs/svipCon1.png


BIN
src/student/activePage/double12Active/imgs/svipCon2.png


BIN
src/student/activePage/double12Active/imgs/tip1Img.png


BIN
src/student/activePage/double12Active/imgs/tip2Img.png


+ 336 - 0
src/student/activePage/double12Active/index.module.less

@@ -0,0 +1,336 @@
+.double12Active {
+  width: 100%;
+  background: linear-gradient(180deg, #fdccbf 0%, #fde0cf 100%);
+  padding: 0 16px 108px;
+  box-sizing: border-box;
+  :global {
+    .van-nav-bar .van-nav-bar__left {
+      color: rgb(44, 62, 80);
+    }
+  }
+  .headImg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 369px;
+    z-index: 1;
+    background: url('./imgs//head.png') no-repeat;
+    background-size: 100% 100%;
+  }
+  .activeArea1 {
+    margin-top: calc(
+      369px - var(--van-nav-bar-height) - var(--navBarHeight) - 18px
+    );
+    background-color: #fff;
+    width: 100%;
+    border-radius: 20px;
+    border: 2px solid #ffffff;
+    filter: blur(0px);
+    position: relative;
+    padding: 12px 0px 24px 20px;
+    box-sizing: border-box;
+    .tip1 {
+      position: absolute;
+      left: 2px;
+      top: -24px;
+      width: 187px;
+      height: 43px;
+      z-index: 1;
+    }
+    .shadowImg {
+      position: absolute;
+      left: 0;
+      top: 0;
+      z-index: 0;
+      width: 100%;
+      height: 60px;
+      border-radius: 20px 20px 0 0;
+    }
+    .activeCon {
+      margin-top: 24px;
+      position: relative;
+      z-index: 2;
+      .activeBox {
+        width: 80px;
+        height: 27px;
+        background: #ffdd71;
+        border-radius: 14px;
+        font-weight: 600;
+        font-size: 15px;
+        color: #000000;
+        line-height: 27px;
+        text-align: center;
+      }
+      .activeTimes {
+        margin-top: 12px;
+        font-weight: 500;
+        font-size: 15px;
+        color: #333333;
+        line-height: 21px;
+      }
+      .activeListCon {
+        box-sizing: border-box;
+        padding-left: 13px;
+        margin-top: 12px;
+        .activeList {
+          position: relative;
+          font-weight: 500;
+          font-size: 15px;
+          color: #333333;
+          line-height: 25px;
+          margin-top: 3px;
+          &:first-child {
+            margin-top: 0;
+          }
+          &::after {
+            content: '';
+            position: absolute;
+            width: 5px;
+            height: 5px;
+            background: linear-gradient(
+              162deg,
+              #ff1e3d 0%,
+              #ff0704 51%,
+              #ff7a4f 100%
+            );
+            opacity: 0.6;
+            left: -13px;
+            top: 50%;
+            transform: translateY(-50%);
+            border-radius: 50%;
+          }
+          & > span {
+            color: #ff1a00;
+          }
+          .number {
+            font-family: 'DIN';
+            font-size: 22px;
+            line-height: 1;
+          }
+        }
+      }
+    }
+    .activeContent {
+      margin-top: 16px;
+      width: calc(100% - 20px);
+      height: 187px;
+      background: url('./imgs/boxImg.png') no-repeat;
+      background-size: 100% 100%;
+      position: relative;
+      .vipNumber {
+        position: absolute;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        & > :nth-child(1) {
+          font-weight: 600;
+          font-size: 11px;
+          color: #5e2b17;
+          line-height: 16px;
+        }
+        & > :nth-child(2) {
+          font-weight: 500;
+          font-size: 11px;
+          color: rgba(94, 43, 23, 50%);
+          line-height: 16px;
+          text-decoration-line: line-through;
+        }
+        &.vipBuy1 {
+          left: 0;
+          bottom: 60px;
+          width: 33.33%;
+        }
+        &.vipBuy2 {
+          left: 33.33%;
+          bottom: 60px;
+          width: 33.33%;
+          box-sizing: border-box;
+          padding-left: 10px;
+        }
+        &.vipBuy3 {
+          left: 66.66%;
+          bottom: 60px;
+          width: 33.33%;
+          box-sizing: border-box;
+          padding-right: 10px;
+        }
+      }
+      .svipDetails {
+        position: absolute;
+        bottom: 6px;
+        left: 12px;
+        display: flex;
+        align-items: flex-end;
+        line-height: 28px;
+        & > span:nth-child(1) {
+          font-weight: 600;
+          font-size: 18px;
+          color: #ffffff;
+        }
+        & > span:nth-child(2) {
+          margin: 0 4px;
+          font-family: 'DIN';
+          font-size: 30px;
+          color: #ffffff;
+          line-height: 38px;
+        }
+        & > span:nth-child(3) {
+          font-weight: 500;
+          font-size: 12px;
+          color: #ffffff;
+        }
+      }
+    }
+  }
+  .activeArea2 {
+    margin-top: 20px;
+    width: 100%;
+    height: 445px;
+  }
+  .activeArea3 {
+    margin-top: 43px;
+    background-color: #fff;
+    width: 100%;
+    border-radius: 20px;
+    border: 2px solid #ffffff;
+    filter: blur(0px);
+    position: relative;
+    padding: 32px 14px 24px 20px;
+    box-sizing: border-box;
+    .tip1 {
+      position: absolute;
+      left: 2px;
+      top: -24px;
+      width: 107px;
+      height: 43px;
+      z-index: 1;
+    }
+    .shadowImg {
+      position: absolute;
+      left: 0;
+      top: 0;
+      z-index: 0;
+      width: 100%;
+      height: 60px;
+      border-radius: 20px 20px 0 0;
+    }
+    .titCon {
+      position: relative;
+      z-index: 2;
+      font-weight: 500;
+      font-size: 15px;
+      color: #333333;
+      line-height: 26px;
+      > span {
+        color: #ff1a00;
+      }
+    }
+    .classCon {
+      margin-top: 16px;
+      width: calc(100% - 6px);
+      height: 128px;
+      background: url('./imgs/class.png') no-repeat;
+      background-size: 100% 100%;
+      position: relative;
+      .classBox {
+        position: absolute;
+        left: 50%;
+        transform: translateX(-50%);
+        height: 32px;
+        line-height: 32px;
+        display: flex;
+        align-items: flex-end;
+        font-size: 15px;
+        color: #ffffff;
+        & > span:nth-child(1) {
+          font-size: 22px;
+          font-weight: 500;
+          line-height: 34px;
+        }
+        & > span {
+          color: #fff176;
+        }
+      }
+    }
+  }
+  .activeArea4 {
+    margin-top: 20px;
+    width: 100%;
+    height: 186px;
+  }
+  .subBtnCon {
+    position: fixed;
+    z-index: 999;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    height: 78px;
+    padding-top: 10px;
+    box-sizing: border-box;
+    display: flex;
+    justify-content: center;
+    background-color: #fff;
+    .subBtn {
+      background: url('./imgs/subBtn.png') no-repeat;
+      background-size: 100% 100%;
+      width: 315px;
+      height: 46px;
+      line-height: 46px;
+      font-size: 18px;
+      color: #ffffff;
+      letter-spacing: 1px;
+      text-align: center;
+      &.disable {
+        pointer-events: none;
+        opacity: 0.4;
+      }
+    }
+  }
+}
+// 弹窗样式
+.dialogContainer {
+  width: 287px;
+  box-sizing: border-box;
+  background: #ffffff;
+  border-radius: 12px;
+  padding: 16px 24px 22px;
+  text-align: center;
+
+  .dialogTitle {
+    font-weight: 500;
+    font-size: 18px;
+    color: #333333;
+    line-height: 25px;
+  }
+
+  .dialogContent {
+    padding: 16px 0 21px;
+    font-size: 15px;
+    color: #777777;
+    line-height: 26px;
+  }
+
+  .dialogBtnGroup {
+    padding: 0 16px;
+
+    &.orderGroup {
+      display: flex;
+      align-items: center;
+      padding: 0;
+    }
+
+    :global {
+      .van-button {
+        font-weight: 500;
+        font-size: 16px;
+      }
+    }
+  }
+
+  .dialogBtn {
+    margin-left: 12px;
+    color: #ffffff;
+    line-height: 22px;
+  }
+}

+ 373 - 0
src/student/activePage/double12Active/index.tsx

@@ -0,0 +1,373 @@
+import { defineComponent, onMounted, reactive, computed } from 'vue'
+import styles from './index.module.less'
+import ColHeader from '@/components/col-header'
+import { postMessage } from '@/helpers/native-message'
+import request from '@/helpers/request'
+import { useEventListener } from '@vant/use'
+import tip1Img from './imgs/tip1Img.png'
+import tip2Img from './imgs/tip2Img.png'
+import shadowImg from './imgs/shadow.png'
+import svipCon1 from './imgs/svipCon1.png'
+import svipCon2 from './imgs/svipCon2.png'
+import dayjs from 'dayjs'
+import { state as baseState } from '@/state'
+import { Toast, Popup, Button } from 'vant'
+import { useRouter } from 'vue-router'
+import { tradeOrder } from '@/student/trade/tradeOrder'
+import { orderStatus } from '@/views/order-detail/orderStatus'
+import { memberType, studyCardType } from '@/constant'
+
+export default defineComponent({
+  name: 'double12Active',
+  setup(props, { emit }) {
+    const router = useRouter()
+    const state = reactive({
+      titleOpacity: 0,
+      navBarHeight: 0,
+      orderVisiable: false,
+      orderDetail: {} as any,
+      activityId: '19'
+    })
+    const activitData = reactive({
+      activityStart: '2024-12-12 00:00:00',
+      activityEnd: '2024-12-12 23:59:59',
+      registrationPrice: 0,
+      buyCount: 0, //  buyCount 小于1的时候 代表能无限购买
+      buyNum: 0,
+      vipCardId: 0,
+      activityList: []
+    })
+    const subBtnState = computed(() => {
+      if (Date.now() < new Date(activitData.activityStart).getTime()) {
+        return {
+          disable: true,
+          text: '活动未开始'
+        }
+      } else if (Date.now() > new Date(activitData.activityEnd).getTime()) {
+        return {
+          disable: true,
+          text: '活动已结束'
+        }
+      } else if (activitData.buyCount <= activitData.buyNum) {
+        return {
+          disable: true,
+          text: '您已参与活动'
+        }
+      }
+      return {
+        disable: false,
+        text: '立即购买'
+      }
+    })
+    const userInfo = computed(() => {
+      return baseState.user.data
+    })
+    // 是否为永久会员
+    const isPermanent = computed(() => {
+      return userInfo.value.userVip?.vipType === 'PERMANENT_SVIP' ? true : false
+    })
+    initActivit()
+    function initActivit() {
+      request
+        .get(
+          `/api-student/memberPriceSettings/getDoubleTwelve/${state.activityId}`
+        )
+        .then(res => {
+          if (res.code === 200 && res.data) {
+            const {
+              activityStart,
+              activityEnd,
+              registrationPrice,
+              buyCount,
+              buyNum,
+              vipCardId,
+              activityRewardList
+            } = res.data || {}
+            activitData.activityStart = activityStart
+            activitData.activityEnd = activityEnd
+            activitData.registrationPrice = registrationPrice
+            activitData.buyCount = buyCount < 1 ? 99999 : buyCount //buyCount 小于1的时候 代表能无限购买
+            activitData.buyNum = buyNum
+            activitData.vipCardId = vipCardId
+            activitData.activityList = (activityRewardList || []).map(item => {
+              const { rewardType, vipCardId, unit } = item.activityReward
+              if (rewardType === 'DISCOUNT') {
+                return {
+                  goodType: rewardType,
+                  goodName: `畅学卡 ${studyCardType[unit]}`,
+                  goodNum: 1,
+                  bizContent: vipCardId,
+                  giftFlag: true,
+                  vipEndDays: null,
+                  goodsNum: 1,
+                  unit
+                }
+              }
+              return {
+                goodType: rewardType,
+                goodName: `小酷Ai SVIP ${memberType[unit]}`,
+                goodNum: 1,
+                bizContent: vipCardId,
+                giftFlag: true,
+                vipEndDays: null,
+                goodsNum: 1,
+                unit
+              }
+            })
+          }
+        })
+        .catch(err => {
+          console.log(err)
+        })
+    }
+    async function handleSubmit() {
+      try {
+        // 永久会员
+        if (isPermanent.value) {
+          Toast('您已是永久SVIP会员')
+          return
+        }
+        const vipType = 'SVIP'
+        // 判断是否有待支付订单
+        const resPadding = await request.post(
+          `/api-student/userOrder/getPendingOrder`,
+          {
+            data: { goodType: vipType }
+          }
+        )
+        console.log(resPadding, 'resPadding')
+        if (resPadding?.data?.id) {
+          state.orderVisiable = true
+          state.orderDetail = resPadding.data || {}
+          return
+        }
+        // 组合订单参数
+        const { data } = await request.post(
+          `/api-student/memberPriceSettings/list`,
+          {
+            data: {
+              status: 1
+            }
+          }
+        )
+
+        const member: any = (data?.list || []).find(item => {
+          return item.id === activitData.vipCardId
+        })
+        if (!member) {
+          Toast('未匹配上活动')
+        }
+        member.title = memberType[member.period]
+        const startTime = dayjs(
+          userInfo.value.userVip.svipEndDate || new Date()
+        ).toDate()
+        const endTime = dayjs(startTime).add(1, 'year').toDate()
+
+        orderStatus.orderObject.orderType = vipType
+        orderStatus.orderObject.orderName = `小酷Ai ${vipType} ${member.title}`
+        orderStatus.orderObject.orderDesc = `小酷Ai ${vipType} ${member.title}`
+        orderStatus.orderObject.actualPrice = activitData.registrationPrice
+        orderStatus.orderObject.recomUserId = ''
+        orderStatus.orderObject.activityId = state.activityId
+        orderStatus.orderObject.orderNo = ''
+        const orderData = {
+          orderType: vipType,
+          goodsName: `小酷Ai ${vipType} ${member.title}`,
+          id: member.id,
+          title: member.title,
+          num: 1, // 购买个数
+          salePrice: member.salePrice,
+          period: member.period,
+          vipEndDays: userInfo.value.userVip?.vipEndDays || 0, // 会员剩余天数
+          svipEndDays: userInfo.value.userVip?.svipEndDays || 0,
+          discount: member.discount, // 是否有折扣
+          discountPrice: member.discountPrice, // 折扣金额
+          price: activitData.registrationPrice,
+          startTime: dayjs(startTime).format('YYYY-MM-DD'),
+          endTime: dayjs(endTime).format('YYYY-MM-DD'),
+          recomUserId: '',
+          activityBuyCount: 0, // 活动购买限制次数
+          activityList: [], // 活动赠送的东西
+          discountEndTime: userInfo.value.discountEndTime, // 畅学卡结束时间
+          discountStartTime: userInfo.value.discountStartTime // 畅学卡开始时间
+        }
+        const canBuyNum = activitData.buyCount - activitData.buyNum
+        if (canBuyNum > 0) {
+          orderData.activityBuyCount = canBuyNum
+          orderData.activityList = activitData.activityList
+        }
+        orderStatus.orderObject.orderList = [orderData]
+        router.push({
+          path: '/orderDetail',
+          query: {
+            orderType: vipType
+          }
+        })
+      } catch {
+        //
+      }
+    }
+    // 取消支付
+    const onCancelOrder = async () => {
+      try {
+        await request.post(`/api-student/userOrder/orderCancel`, {
+          data: { orderNo: state.orderDetail.orderNo }
+        })
+        state.orderVisiable = false
+      } catch {
+        //
+      }
+    }
+    // 继续支付
+    const onContinueOrder = async () => {
+      const orderDetail = state.orderDetail || {}
+      tradeOrder(orderDetail, () => {
+        router.push({
+          path: '/orderDetail',
+          query: {
+            orderType: orderDetail.orderType
+          }
+        })
+      })
+    }
+    onMounted(() => {
+      postMessage({ api: 'getNavHeight' }, res => {
+        const { content } = res as any
+        const dpi = content.dpi || 2
+        if (content.navHeight) {
+          const navHeight = content.navHeight / dpi
+          state.navBarHeight = navHeight
+        }
+      })
+    })
+    useEventListener('scroll', () => {
+      const height =
+        window.scrollY ||
+        window.pageYOffset ||
+        document.documentElement.scrollTop
+      state.titleOpacity = height > 30 ? 1 : 0
+    })
+    return () => (
+      <div
+        class={styles.double12Active}
+        style={{
+          '--navBarHeight': `${state.navBarHeight}px`
+        }}
+      >
+        <ColHeader
+          background={`rgba(255,255,255, ${state.titleOpacity})`}
+          color={`rgba(0,0,0, ${state.titleOpacity})`}
+          backIconColor="black"
+          hideHeader={false}
+          border={false}
+        />
+        <div class={styles.headImg}></div>
+        <div class={styles.activeArea1}>
+          <img class={styles.tip1} src={tip1Img} />
+          <img class={styles.shadowImg} src={shadowImg} />
+          <div class={styles.activeCon}>
+            <div class={styles.activeBox}>活动时间</div>
+            <div class={styles.activeTimes}>
+              {`${dayjs(activitData.activityStart).format(
+                'YYYY年MM月DD日HH:MM'
+              )}-${dayjs(activitData.activityEnd).format(
+                'YYYY年MM月DD日HH:MM'
+              )}`}
+            </div>
+          </div>
+          <div class={styles.activeCon}>
+            <div class={styles.activeBox}>活动内容</div>
+            <div class={styles.activeListCon}>
+              <div class={styles.activeList}>
+                SVIP年度会员限时特惠至
+                <span class={styles.number}>
+                  {' '}
+                  {activitData.registrationPrice}{' '}
+                </span>
+                <span>元</span>
+              </div>
+              <div class={styles.activeList}>
+                活动期间<span>买一年送一年</span>
+              </div>
+              <div class={styles.activeList}>
+                同时额外<span>获赠一年畅学卡</span>,享受约课75折!
+              </div>
+            </div>
+          </div>
+          <div class={styles.activeContent}>
+            <div class={[styles.vipNumber, styles.vipBuy1]}>
+              <div>SVIP年度会员</div>
+              <div>价值1280元</div>
+            </div>{' '}
+            <div class={[styles.vipNumber, styles.vipBuy2]}>
+              <div>SVIP年度会员</div>
+              <div>价值1280元</div>
+            </div>{' '}
+            <div class={[styles.vipNumber, styles.vipBuy3]}>
+              <div>畅学卡一年期</div>
+              <div>价值129元</div>
+            </div>
+            <div class={styles.svipDetails}>
+              <span>¥</span>
+              <span>{activitData.registrationPrice}</span>
+              <span>(SVIP会员买1年赠1年,还送1年畅学)</span>
+            </div>
+          </div>
+        </div>
+        <img class={styles.activeArea2} src={svipCon1} />
+        <div class={styles.activeArea3}>
+          <img class={styles.tip1} src={tip2Img} />
+          <img class={styles.shadowImg} src={shadowImg} />
+          <div class={styles.titCon}>
+            专为音乐学习者设计的全方位学习通行证,畅学卡生效期间,购买任何VIP定制课、趣纠课、直播课、视频课
+            <span>享受75折</span>优惠,助您在音乐道路上更进一步。
+          </div>
+          <div class={styles.classCon}>
+            <div class={styles.classBox}>
+              课程全部<span>75</span>
+              <span>折</span>
+            </div>
+          </div>
+        </div>
+        <img class={styles.activeArea4} src={svipCon2} />
+        <div class={styles.subBtnCon}>
+          <div
+            class={[styles.subBtn, subBtnState.value.disable && styles.disable]}
+            onClick={() => {
+              !subBtnState.value.disable && handleSubmit()
+            }}
+          >
+            {subBtnState.value.text}
+          </div>
+        </div>
+        <Popup
+          v-model:show={state.orderVisiable}
+          style={{ background: 'transparent' }}
+          closeOnClickOverlay={false}
+        >
+          <div class={styles.dialogContainer}>
+            <div class={styles.dialogTitle}>提示</div>
+            <div class={styles.dialogContent}>
+              您有待支付的订单,是否继续支付
+            </div>
+
+            <div class={[styles.dialogBtnGroup, styles.orderGroup]}>
+              <Button round type="default" plain block onClick={onCancelOrder}>
+                取消订单
+              </Button>
+              <Button
+                round
+                type="primary"
+                block
+                class={styles.dialogBtn}
+                onClick={onContinueOrder}
+              >
+                继续支付
+              </Button>
+            </div>
+          </div>
+        </Popup>
+      </div>
+    )
+  }
+})

BIN
src/student/discount-card/images/bg.png


BIN
src/student/discount-card/images/btn-bg.png


BIN
src/student/discount-card/images/card-bg.png


BIN
src/student/discount-card/images/icon-star.png


BIN
src/student/discount-card/images/icon-svip-disabled.png


BIN
src/student/discount-card/images/icon-svip.png


BIN
src/student/discount-card/images/icon-vip-disabled.png


BIN
src/student/discount-card/images/icon-vip.png


BIN
src/student/discount-card/images/memo.png


BIN
src/student/discount-card/images/ring.png


BIN
src/student/discount-card/images/tip.png


BIN
src/student/discount-card/images/title1.png


+ 294 - 0
src/student/discount-card/index.module.less

@@ -0,0 +1,294 @@
+.discountCardContainer {
+  min-height: 100vh;
+  background: url('./images/bg.png') no-repeat top center #cbf3ff;
+  background-size: contain;
+}
+
+.cardContainer {
+  position: relative;
+  padding: 20px 14px 0;
+
+  .imgSection {
+    font-size: 0;
+    padding-bottom: 20px;
+    img {
+      width: 100%;
+      margin-top: 16px;
+    }
+  }
+}
+
+.userSection {
+  display: flex;
+  align-items: flex-start;
+
+  .userImgSection {
+    position: relative;
+    width: 48px;
+    height: 48px;
+    margin-right: 13px;
+    flex-shrink: 0;
+
+    .userImg {
+      width: 48px;
+      height: 48px;
+      border-radius: 50%;
+      overflow: hidden;
+      border: 2px solid #f2f6f7;
+    }
+
+    .showMemeber {
+      position: absolute;
+      bottom: 1px;
+      right: -4px;
+      width: 18px;
+      height: 18px;
+    }
+
+    &.userVip {
+      .showMemeber {
+        background: url('./images/icon-vip-disabled.png') no-repeat center;
+        background-size: contain;
+      }
+
+      &.isVip {
+        .userImg {
+          border: 2px solid #f0af88;
+        }
+
+        .showMemeber {
+          background: url('./images/icon-vip.png') no-repeat center;
+          background-size: contain;
+        }
+      }
+    }
+
+    &.userSVip {
+      .showMemeber {
+        background: url('./images/icon-svip-disabled.png') no-repeat center;
+        background-size: contain;
+      }
+
+      &.isVip {
+        .userImg {
+          border: 2px solid #f0af88;
+        }
+
+        .showMemeber {
+          background: url('./images/icon-svip.png') no-repeat center;
+          background-size: contain;
+        }
+      }
+    }
+  }
+
+  .userInfo {
+    padding-top: 5px;
+    max-width: 185px;
+
+    .userName {
+      display: flex;
+      align-items: center;
+      padding-bottom: 4px;
+    }
+
+    .name {
+      font-weight: 500;
+      font-size: 18px;
+      color: #000000;
+      line-height: 25px;
+      letter-spacing: 1px;
+      max-width: 90px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+
+    .phone {
+      font-size: 13px;
+      color: rgba(0, 0, 0, 0.6);
+    }
+
+    .member_time {
+      font-size: 12px;
+      color: #777777;
+      line-height: 17px;
+
+      span {
+        padding-left: 2px;
+        color: #1d88ff;
+        font-weight: 500;
+      }
+    }
+  }
+}
+
+.cardSection {
+  position: relative;
+  background: url('./images/card-bg.png') no-repeat center;
+  background-size: contain;
+  height: 203px;
+  max-width: 347px;
+  margin-top: 27px;
+
+  .top {
+    display: flex;
+    align-items: center;
+    padding-top: 19px;
+    padding-left: 26px;
+    .iconTitle1 {
+      // width: 63px;
+      height: 25px;
+      margin-right: 6px;
+    }
+
+    .priceSection {
+      flex-shrink: 0;
+      display: flex;
+      align-items: center;
+      .currentPrice {
+        display: flex;
+        align-items: flex-end;
+        background: linear-gradient(270deg, #ff7b57 0%, #ff3460 100%);
+        border-radius: 100px 100px 100px 2px;
+        padding: 0 8px;
+        font-weight: 600;
+        color: #ffffff;
+        height: 23px;
+        .l {
+          font-size: 12px;
+          line-height: 17px;
+          padding-right: 2px;
+        }
+        .c {
+          font-size: 20px;
+          line-height: 1;
+        }
+        .r {
+          font-size: 12px;
+          line-height: 17px;
+        }
+      }
+      .originPrice {
+        padding-left: 6px;
+        font-size: 12px;
+        color: rgba(19, 20, 21, 0.4)
+      }
+    }
+  }
+
+  .chapter {
+    position: absolute;
+    right: 19px;
+    top: -14px;
+    background: url('./images/ring.png') no-repeat center;
+    background-size: contain;
+    width: 60px;
+    height: 60px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+    transform: rotate(-15deg);
+
+    .chapterTop {
+      padding-top: 6px;
+      font-weight: 500;
+      font-size: 11px;
+      color: #ffffff;
+      line-height: 15px;
+    }
+    .chapterBottom {
+      font-family: DINAlternate, DINAlternate;
+      font-weight: bold;
+      font-size: 22px;
+      color: #ffffff;
+      line-height: 1;
+      // display: flex;
+      // align-items: center;
+      i {
+        vertical-align: middle;
+        padding-left: 1px;
+        font-style: normal;
+        font-weight: 500;
+        font-size: 12px;
+        color: #ffffff;
+        line-height: 1;
+      }
+    }
+  }
+}
+
+.btnGroup {
+  background-color: #fff;
+  padding: 10px 30px 30px;
+
+  .submitBtn {
+    background: url('./images/btn-bg.png') no-repeat center;
+    background-size: contain;
+    width: 315px;
+    height: 46px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: 600;
+    font-size: 18px;
+    color: #ffffff;
+    line-height: 26px;
+    i {
+      padding-top: 2px;
+      padding-right: 2px;
+      font-size: 14px;
+      font-style: normal;
+      line-height: 20px;
+    }
+  }
+}
+
+
+// 弹窗样式
+.dialogContainer {
+  width: 287px;
+  box-sizing: border-box;
+  background: #FFFFFF;
+  border-radius: 12px;
+  padding: 16px 24px 22px;
+  text-align: center;
+
+  .dialogTitle {
+    font-weight: 500;
+    font-size: 18px;
+    color: #333333;
+    line-height: 25px;
+  }
+
+  .dialogContent {
+    padding: 16px 0 21px;
+    font-size: 15px;
+    color: #777777;
+    line-height: 26px;
+  }
+
+  .dialogBtnGroup {
+    padding: 0 16px;
+
+    &.orderGroup {
+      display: flex;
+      align-items: center;
+      padding: 0;
+    }
+
+    :global {
+      .van-button {
+        font-weight: 500;
+        font-size: 16px;
+      }
+    }
+  }
+
+  .dialogBtn {
+    margin-left: 12px;
+    color: #FFFFFF;
+    line-height: 22px;
+  }
+}

+ 267 - 0
src/student/discount-card/index.tsx

@@ -0,0 +1,267 @@
+import { computed, defineComponent, onMounted, reactive } from 'vue'
+import styles from './index.module.less'
+import ColHeader from '@/components/col-header'
+import { useEventListener } from '@vant/use'
+import iconStudent from '@common/images/icon_student.png'
+import { Button, Image, Popup } from 'vant'
+import { state as baseState, setLogin } from '@/state'
+
+import iconMemo from './images/memo.png'
+import iconTip from './images/tip.png'
+import iconTitle1 from './images/title1.png'
+import TheSticky from '@/components/the-sticky'
+import request from '@/helpers/request'
+import { tradeOrder } from '../trade/tradeOrder'
+import { useRouter } from 'vue-router'
+import dayjs from 'dayjs'
+import { orderStatus } from '@/views/order-detail/orderStatus'
+
+export default defineComponent({
+  name: 'discount-card',
+  setup() {
+    const router = useRouter()
+    const state = reactive({
+      discountDetail: {} as any,
+      titleOpacity: 0,
+      orderVisible: false,
+      orderDetail: {} as any
+    })
+    const userInfo = computed(() => {
+      const users = baseState.user.data
+      console.log(users, 'users')
+      return {
+        username: users?.username,
+        phone: users?.phone,
+        avatar: users?.heardUrl,
+        id: users?.userId,
+        discountCardFlag: users?.discountCardFlag,
+        discountEndTime: users?.discountEndTime,
+        userVip: users?.userVip
+      }
+    })
+
+    useEventListener('scroll', () => {
+      const height =
+        window.scrollY ||
+        window.pageYOffset ||
+        document.documentElement.scrollTop
+      state.titleOpacity = height > 30 ? 1 : 0
+    })
+
+     // 取消支付
+     const onCancelOrder = async () => {
+      try {
+        await request.post(`${baseState.platformApi}/userOrder/orderCancel`, {
+          data: { orderNo: state.orderDetail.orderNo }
+        })
+        state.orderVisible = false
+      } catch {
+        //
+      }
+    }
+
+    // 继续支付
+    const onContinueOrder = async () => {
+      const orderDetail = state.orderDetail || {}
+      tradeOrder(orderDetail, () => {
+        router.push({
+          path: '/orderDetail',
+          query: {
+            orderType: orderDetail.orderType
+          }
+        })
+      })
+    }
+
+    const onSubmit = async () => {
+      try {
+
+        // 判断是否有待支付订单
+        const resPadding = await request.post(
+          `${baseState.platformApi}/userOrder/getPendingOrder`,
+          {
+            data: { goodType: 'DISCOUNT' }
+          }
+        )
+        console.log(resPadding, 'resPadding')
+        if (resPadding?.data?.id) {
+          state.orderVisible = true
+          state.orderDetail = resPadding.data || {}
+          return
+        }
+
+        let startTime = new Date()
+        if(userInfo.value.discountCardFlag) {
+          startTime = dayjs(userInfo.value.discountEndTime || new Date()).toDate()
+        }
+        let endTime = new Date()
+        if (state.discountDetail.period === 'MONTH') {
+          endTime = dayjs(startTime).add(1, 'month').toDate()
+        } else if (state.discountDetail.period === 'QUARTERLY') {
+          endTime = dayjs(startTime).add(3, 'month').toDate()
+        } else if (state.discountDetail.period === 'YEAR_HALF') {
+          endTime = dayjs(startTime).add(6, 'month').toDate()
+        } else if (state.discountDetail.period === 'YEAR') {
+          endTime = dayjs(startTime).add(1, 'year').toDate()
+        }
+        orderStatus.orderObject.orderType = 'DISCOUNT'
+        orderStatus.orderObject.orderName = `畅学卡`
+        orderStatus.orderObject.orderDesc = `畅学卡`
+        orderStatus.orderObject.actualPrice = state.discountDetail.salePrice || 0
+        orderStatus.orderObject.recomUserId = null
+        orderStatus.orderObject.activityId = null
+        orderStatus.orderObject.orderNo = ''
+
+        orderStatus.orderObject.orderList = [
+          {
+            orderType: 'DISCOUNT',
+            goodsName: `畅学卡`,
+            id: state.discountDetail.id,
+            title: '畅学卡',
+            num: 1, // 购买个数
+            salePrice: state.discountDetail.salePrice,
+            period: state.discountDetail.period,
+            price: state.discountDetail.salePrice,
+            startTime: dayjs(startTime).format('YYYY-MM-DD'),
+            endTime: dayjs(endTime).format('YYYY-MM-DD')
+          }
+        ]
+        router.push({
+          path: '/orderDetail',
+          query: {
+            orderType: 'DISCOUNT'
+          }
+        })
+      } catch {
+        //
+      }
+    }
+
+    onMounted(async () => {
+      try {
+        const userInfo = await request.get(
+          baseState.platformType === 'TEACHER'
+            ? '/api-teacher/teacher/queryUserInfo'
+            : '/api-student/student/queryUserInfo'
+        )
+        setLogin(userInfo.data)
+
+        const { data } = await request.get(
+          `${baseState.platformApi}/memberPriceSettings/getDiscount`
+        )
+        state.discountDetail = data
+      } catch {
+        //
+      }
+      
+    })
+    return () => (
+      <div class={styles.discountCardContainer}>
+        <ColHeader
+          background={`rgba(255,255,255, ${state.titleOpacity})`}
+          backIconColor="black"
+          hideHeader={false}
+          border={false}
+        />
+        <div class={styles.cardContainer}>
+          <div class={styles.userSection}>
+            <div
+              class={[
+                styles.userImgSection,
+                (userInfo.value.userVip.vipType === 'SVIP' ||
+                  userInfo.value.userVip.vipType === 'PERMANENT_SVIP') &&
+                  styles.userSVip,
+                userInfo.value.userVip.vipType === 'VIP' && styles.userVip,
+                userInfo.value.userVip.svipEndDays > 0 ||
+                userInfo.value.userVip.vipEndDays > 0
+                  ? styles.isVip
+                  : ''
+              ]}
+            >
+              <Image
+                class={styles.userImg}
+                src={userInfo.value.avatar || iconStudent}
+                fit="cover"
+              />
+              <i class={styles.showMemeber}></i>
+            </div>
+            <div class={styles.userInfo}>
+              <div class={styles.userName}>
+                <span class={styles.name}>{userInfo.value.username}</span>
+                {userInfo.value.phone && (
+                  <span class={styles.phone}>({userInfo.value.phone})</span>
+                )}
+              </div>
+              <div class={styles.member_time}>
+                {userInfo.value.discountCardFlag ? <>有效期至<span>{dayjs(userInfo.value.discountEndTime).format('YYYY-MM-DD')}</span></> : '您当前尚未开通畅学卡'}
+              </div>
+            </div>
+          </div>
+
+          <div class={styles.cardSection}>
+            <div class={styles.top}>
+              <img src={iconTitle1} class={styles.iconTitle1} />
+              <div class={styles.priceSection}>
+                <div class={styles.currentPrice}>
+                  <div class={styles.l}>¥</div>
+                  <div class={styles.c}>{state.discountDetail.salePrice || 0}</div>
+                  <div class={styles.r}>/年</div>
+                </div>
+                <del class={styles.originPrice}>原价¥{state.discountDetail.originalPrice}/年</del>
+              </div>
+            </div>
+
+            <div class={styles.chapter}>
+              <div class={styles.chapterTop}>课程全部</div>
+              <div class={styles.chapterBottom}>
+                {(state.discountDetail.discountRate || 0) * 100}<i>折</i>
+              </div>
+            </div>
+          </div>
+
+          <div class={styles.imgSection}>
+            <img src={iconMemo} class={styles.iconMemo} />
+            <img src={iconTip} class={styles.iconTip} />
+          </div>
+        </div>
+
+        <TheSticky position="bottom">
+          <div class={styles.btnGroup}>
+            <div class={styles.submitBtn} onClick={onSubmit}>
+              {userInfo.value.discountCardFlag ? '立即续费' : <><i>¥</i>{state.discountDetail.salePrice||0}元立即开通</>}
+            </div>
+          </div>
+        </TheSticky>
+
+
+        <Popup
+          v-model:show={state.orderVisible}
+          style={{ background: 'transparent' }}
+          closeOnClickOverlay={false}
+        >
+          <div class={styles.dialogContainer}>
+            <div class={styles.dialogTitle}>提示</div>
+            <div class={styles.dialogContent}>
+              您有待支付的订单,是否继续支付
+            </div>
+
+            <div class={[styles.dialogBtnGroup, styles.orderGroup]}>
+              <Button round type="default" plain block onClick={onCancelOrder}>
+                取消订单
+              </Button>
+              <Button
+                round
+                type="primary"
+                block
+                class={styles.dialogBtn}
+                onClick={onContinueOrder}
+              >
+                继续支付
+              </Button>
+            </div>
+          </div>
+        </Popup>
+      </div>
+    )
+  }
+})

+ 1 - 1
src/student/practice-class/index.tsx

@@ -164,7 +164,7 @@ export default defineComponent({
         >
           <div ref="headers">
             <ColHeader
-              title="陪练课"
+              title="趣纠课"
               isFixed={false}
               border={false}
               backIconColor="white"

+ 1 - 1
src/student/practice-class/model/all-search.tsx

@@ -25,7 +25,7 @@ export default defineComponent({
       <>
         <div class={styles.filterTitle}>全部筛选</div>
         <div class={styles.searchResult}>
-          <div class={styles.searchTitle}>陪练课数</div>
+          <div class={styles.searchTitle}>趣纠课数</div>
           <RadioGroup
             class={styles['radio-group']}
             modelValue={this.popupParams.expTime}

+ 4 - 0
src/student/teacher-dependent/components/live.module.less

@@ -108,3 +108,7 @@
   align-items: center;
   color: #666;
 }
+
+.tips {
+  margin: 0 14px;
+}

+ 3 - 1
src/student/teacher-dependent/components/live.tsx

@@ -2,13 +2,14 @@ import ColResult from '@/components/col-result'
 import { Cell, CellGroup, List, Image, Icon } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './live.module.less'
-
+import icon3 from '../images/icon3.png'
 import iconTimer from '@common/images/icon_timer2.png'
 import iconTeacher from '@common/images/icon_teacher.png'
 import iconSuccess from '@common/images/icon_success.png'
 import request from '@/helpers/request'
 import dayjs from 'dayjs'
 import { state } from '@/state'
+import Tips from './tips'
 
 export default defineComponent({
   name: 'live',
@@ -82,6 +83,7 @@ export default defineComponent({
   render() {
     return (
       <>
+        <Tips class={styles.tips} title='什么是直播课?' content='直播课是现代教育领域中一种广受欢迎的课程形式,它集实时互动、多媒体展示和高度便利性于一体,为学习者带来了独特且丰富的学习体验。特别是在管乐直播课中,教师可以通过播放经典音乐作品,加深学生对音乐之美的感知与理解。对于那些需要具体操作演示的教学内容,直播课能够提供清晰直观的视角,让教师的每一个动作细节都展现在学生面前,确保学习效果。直播课程的内容围绕特定主题精心设计,旨在满足不同学习者的需求,促进知识与技能的有效传递。' />
         {this.dataShow ? (
           <List
             class={styles.liveList}

+ 13 - 2
src/student/teacher-dependent/components/practice.module.less

@@ -1,10 +1,21 @@
 .practice {
-  padding: 14px 14px 0;
+  padding: 4px 14px 80px;
   overflow: hidden;
+  
   .group {
     margin-bottom: 12px;
     border-radius: 10px;
     overflow: hidden;
+
+    :global {
+      .van-cell {
+        padding: 16px 15px;
+        font-size: 16px;
+        .van-cell__title {
+          color: #1A1A1A;
+        }
+      }
+    }
   }
   .price {
     font-size: 14px;
@@ -97,7 +108,7 @@
 }
 
 .arrangeCell {
-  margin: 10px 0 80px !important;
+  margin: 10px 0 0 !important;
   width: auto;
   border-radius: 10px;
   overflow: hidden;

+ 27 - 20
src/student/teacher-dependent/components/practice.tsx

@@ -12,7 +12,8 @@ import {
   Sticky,
   Tag,
   Popup,
-  Toast
+  Toast,
+  Icon
 } from 'vant'
 import { defineComponent } from 'vue'
 import { getWeekCh } from '@/helpers/utils'
@@ -20,6 +21,7 @@ import styles from './practice.module.less'
 import { orderStatus } from '@/views/order-detail/orderStatus'
 import ColResult from '@/components/col-result'
 import { tradeOrder } from '@/student/trade/tradeOrder'
+import Tips from './tips'
 
 export default defineComponent({
   name: 'practice',
@@ -39,6 +41,7 @@ export default defineComponent({
       subjectInfo: {
         subjectPrice: 0,
         courseMinutes: 0,
+        id: null,
         subjectName: '',
         subjectId: 0
       },
@@ -50,7 +53,7 @@ export default defineComponent({
       selectStatus: false,
       coursePlanList: [] as any,
       calendarDate: dayjs().add(1, 'day').toDate() as Date, // 日历当前时间
-      settingStatus: true, // 是否设置陪练
+      settingStatus: true, // 是否设置趣纠
       loadDataStatus: true // 是否加载数据
     }
   },
@@ -74,9 +77,10 @@ export default defineComponent({
         })
         // 判断是否有跟学生相同的科目,如果没有则默认取第一个
         const tempRes = findItem || result[0]
-        const { subjectName, subjectPrice, courseMinutes, subjectId } = tempRes
+        const { subjectName, subjectPrice, courseMinutes, subjectId, id } = tempRes
         this.subjectInfo = {
           subjectPrice,
+          id,
           courseMinutes,
           subjectName,
           subjectId
@@ -160,6 +164,7 @@ export default defineComponent({
           {
             data: {
               ...params,
+              teacherSubjectPriceId: this.subjectInfo.id,
               studentId: state.user.data?.userId,
               teacherId: this.teacherId
             }
@@ -306,8 +311,8 @@ export default defineComponent({
           ).format('HH:mm')}~${dayjs(item.endTime).format('HH:mm')}`
         })
         orderStatus.orderObject.orderType = 'PRACTICE'
-        orderStatus.orderObject.orderName = subjectInfo.subjectName + '陪练课'
-        orderStatus.orderObject.orderDesc = subjectInfo.subjectName + '陪练课'
+        orderStatus.orderObject.orderName = subjectInfo.subjectName + '趣纠课'
+        orderStatus.orderObject.orderDesc = subjectInfo.subjectName + '趣纠课'
         orderStatus.orderObject.actualPrice = Number(
           (this.courseNum * subjectInfo.subjectPrice).toFixed(2)
         )
@@ -315,9 +320,9 @@ export default defineComponent({
         orderStatus.orderObject.orderList = [
           {
             orderType: 'PRACTICE',
-            goodsName: subjectInfo.subjectName + '陪练课',
-            courseGroupName: subjectInfo.subjectName + '陪练课',
-            courseIntroduce: subjectInfo.subjectName + '陪练课',
+            goodsName: subjectInfo.subjectName + '趣纠课',
+            courseGroupName: subjectInfo.subjectName + '趣纠课',
+            courseIntroduce: subjectInfo.subjectName + '趣纠课',
             subjectId: subjectInfo.subjectId,
             singleCourseMinutes: subjectInfo.courseMinutes,
             courseNum: this.courseNum,
@@ -379,10 +384,18 @@ export default defineComponent({
           (this.settingStatus ? (
             <>
               <div class={styles.practice}>
+                <Tips title="什么是趣纠课?" content="趣纠课以一对一专属、高度针对性的形式进行,每次课程时长为25分钟。本课程专为解决学生日常练习中的疑问与误区设计,尤其适合那些在自我练习后感到困惑或不确定自己方法是否正确的学生。不同于传统的教学模式,趣纠课不侧重于新知识或新技能的传授,而是全心全意致力于检查学生现有的练习成果,并及时纠正其中出现的问题。这种方式不仅有助于学生巩固已掌握的知识和技能,还能有效防止错误习惯的形成和发展,为他们今后的学习打下更加坚实的基础。" />
+
                 <CellGroup class={styles.group} border={false}>
+                  <Cell
+                    title="选择专业"
+                    isLink
+                    value={this.subjectInfo.subjectName}
+                    onClick={() => (this.subjectStatus = true)}
+                  />
                   {this.subjectInfo.subjectPrice > 0 && (
                     <Cell
-                      title="陪练课收费"
+                      title="趣纠课收费"
                       v-slots={{
                         default: () => (
                           <div class={styles.price}>
@@ -398,13 +411,6 @@ export default defineComponent({
                       }}
                     />
                   )}
-
-                  <Cell
-                    title="选择专业"
-                    isLink
-                    value={this.subjectInfo.subjectName}
-                    onClick={() => (this.subjectStatus = true)}
-                  />
                   <Cell
                     title="课时数"
                     v-slots={{
@@ -437,8 +443,7 @@ export default defineComponent({
                     />
                   </div>
                 )}
-
-                <Cell
+                {this.showSelectList.length > 0 && <Cell
                   class={[styles.arrangeCell]}
                   v-slots={{
                     title: () => (
@@ -467,7 +472,7 @@ export default defineComponent({
                       </div>
                     )
                   }}
-                ></Cell>
+                ></Cell>}
 
                 <Popup show={this.selectStatus} class={styles.selectPopup}>
                   <div class={styles.selectContainer}>
@@ -540,10 +545,12 @@ export default defineComponent({
                       subjectName,
                       subjectPrice,
                       courseMinutes,
+                      id,
                       subjectId
                     } = item
                     this.subjectInfo = {
                       subjectPrice,
+                      id,
                       courseMinutes,
                       subjectName,
                       subjectId
@@ -565,7 +572,7 @@ export default defineComponent({
             <ColResult
               btnStatus={false}
               classImgSize="SMALL"
-              tips="老师暂未开放陪练课"
+              tips="老师暂未开放趣纠课"
             />
           ))}
       </>

+ 45 - 0
src/student/teacher-dependent/components/tips/index.module.less

@@ -0,0 +1,45 @@
+
+.tipSection {
+  position: relative;
+  background: #FFFFFF;
+  border-radius: 6px;
+  margin-bottom: 12px;
+  .iconCross {
+    position: absolute;
+    top: 15px;
+    right: 13px;
+    font-size: 16px;
+    color: #CCCCCC;
+    cursor: pointer;
+  }
+  .tipTitle {
+    padding: 12px 12px 10px;
+    font-weight: 500;
+    font-size: 15px;
+    color: #333333;
+    line-height: 18px;
+
+    img {
+      width: 18px;
+      height: 18px;
+      margin-right: 6px;
+      vertical-align: text-bottom;
+    }
+  }
+  .tipContent {
+    padding: 0 12px 10px;
+    font-size: 13px;
+    color: #777777;
+    line-height: 22px;
+    text-align: justify;
+  }
+  .tipFooter {
+    margin: 0 12px;
+    padding: 10px 0;
+    text-align: center;
+    border-top: 1px solid #F2F2F2;
+    font-size: 13px;
+    color: #2DC7AA;
+    line-height: 18px;
+  }
+}

+ 40 - 0
src/student/teacher-dependent/components/tips/index.tsx

@@ -0,0 +1,40 @@
+import { Icon } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import icon3 from '../../images/icon3.png'
+
+export default defineComponent({
+  name: 'tips',
+  props: {
+    /** 标题 */
+    title: {
+      type: String,
+      default: '',
+    },
+    /** 内容 */
+    content: {
+      type: String,
+      default: ''
+    },
+    btnTxt: {
+      type: String,
+      default: '不再提醒'
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    return () => (
+      <div class={styles.tipSection}>
+        <Icon class={styles.iconCross} onClick={() => emit("close")} name="cross" />
+        <div class={styles.tipTitle}>
+          <img src={icon3} />
+          {props.title}
+        </div>
+        <div class={styles.tipContent}>
+          {props.content}
+        </div>
+        <div class={styles.tipFooter} onClick={() => emit("confirm")}>{props.btnTxt}</div>
+      </div>
+    )
+  }
+})

+ 62 - 0
src/student/teacher-dependent/components/video-item/index.module.less

@@ -0,0 +1,62 @@
+.videoItem {
+  border-radius: 8px;
+  background-color: #fff;
+  overflow: hidden;
+  width: 168px;
+  margin-bottom: 12px;
+
+  .viCover {
+    height: 94px;
+    width: 100%;
+    vertical-align: middle;
+  }
+
+  .viSection {
+    padding: 8px 12px 13px;
+  }
+
+  .viTitle {
+    font-size: 14px;
+    color: #1a1a1a;
+    line-height: 20px;
+  }
+
+  .viUserNum {
+    padding-top: 4px;
+    color: #ff802c;
+    font-size: 12px;
+  }
+
+  .viPrice {
+    padding-top: 10px;
+    font-size: 14px;
+    color: #999;
+
+    .priceNum {
+      color: #ff0000;
+      font-size: 16px;
+      font-weight: bold;
+
+      i {
+        font-size: 15px;
+        font-style: normal;
+      }
+    }
+
+    .label {
+      padding-left: 8px;
+    }
+  }
+
+  .subjectName {
+    position: absolute;
+    bottom: 6px;
+    right: 6px;
+    font-size: 10px;
+    padding: 3px 5px;
+    color: #ffffff;
+    line-height: 1;
+    border-radius: 2px;
+    background: rgba(0, 0, 0, 0.29);
+  }
+}

+ 92 - 0
src/student/teacher-dependent/components/video-item/index.tsx

@@ -0,0 +1,92 @@
+import { defineComponent, PropType } from 'vue'
+import { Image } from 'vant'
+import styles from './index.module.less'
+
+import iconTeacher from '@common/images/icon_teacher.png'
+
+interface VideoItemProps {
+  id?: number
+  teacherId?: number
+  lessonName: string
+  userName: string
+  avatar: string
+  lessonCoverUrl: string
+  lessonCount: number
+  lessonPrice: number
+  countStudent: number
+  lessonSubjectName: string
+  auditVersion: number
+}
+
+export default defineComponent({
+  name: 'VideoItem',
+  props: {
+    item: Object as PropType<VideoItemProps>,
+    onClick: {
+      type: Function as PropType<(item: any) => void>,
+      default: (item: any) => {}
+    }
+  },
+  render() {
+    const item: any = this.item
+    return (
+      <div
+        class={styles.videoItem}
+        onClick={() => {
+          this.onClick(item)
+        }}
+      >
+        <div style={{ position: 'relative' }}>
+          <Image
+            class={styles.viCover}
+            fit="cover"
+            src={item?.lessonCoverUrl}
+          />
+          <span class={styles.subjectName}>{item?.lessonSubjectName}</span>
+        </div>
+
+        <div class={styles.viSection}>
+          <div class={[styles.viTitle, 'van-ellipsis']}>{item?.lessonName}</div>
+          {/* <div class={styles.viUserInfo}>
+            <Image
+              src={item?.avatar || iconTeacher}
+              class={styles.viUserLogo}
+            />
+            <span class={[styles.viUserName, 'van-hairline--right']}>
+              {item?.userName ||
+                item?.username ||
+                `游客${item?.teacherId || ''}`}
+            </span>
+          </div> */}
+          <div class={styles.viPrice}>
+            <span class={styles.priceNum}>
+              {item.payType === 'VIP' ? (
+                <span style={{ color: '#C76E21' }}>会员</span>
+              ) : (
+                <>
+                  {item?.lessonPrice > 0 && (
+                    <>
+                      <i>¥</i>
+                      {item?.lessonPrice}
+                    </>
+                  )}
+                  {item?.lessonPrice <= 0 && item.auditVersion !== 0 && (
+                    <>
+                      <i>¥</i>0
+                    </>
+                  )}
+                  {item?.lessonPrice <= 0 && item.auditVersion === 0 && (
+                    <span style={{ color: '#20BEA0' }}>免费</span>
+                  )}
+                </>
+              )}
+            </span>
+            <span class={styles.label}>/{item?.lessonCount}课时</span>
+          </div>
+
+          <div class={styles.viUserNum}>{item?.countStudent}人学习</div>
+        </div>
+      </div>
+    )
+  }
+})

+ 4 - 0
src/student/teacher-dependent/components/video.module.less

@@ -12,3 +12,7 @@
     width: 100%;
   }
 }
+
+.tips {
+  margin: 0 14px;
+}

+ 4 - 2
src/student/teacher-dependent/components/video.tsx

@@ -1,10 +1,11 @@
 import ColResult from '@/components/col-result'
 import request from '@/helpers/request'
 import { state } from '@/state'
-import VideoItem from '@/student/video-class/video-item'
+import VideoItem from './video-item'
 import { List } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './video.module.less'
+import Tips from './tips'
 
 export default defineComponent({
   name: 'VideoList',
@@ -66,7 +67,8 @@ export default defineComponent({
   },
   render() {
     return (
-      <>
+      <> 
+        <Tips class={styles.tips} title='什么是视频课?' content='视频课是由教师事先精心准备并录制的课程内容。教师依据教学大纲和目标,系统规划每一节视频的主题与内容,确保教学的连贯性和完整性。在录制过程中,采用专业设备如高清摄像机和录屏软件,保障视频画质清晰、音频质量优良。视频课为学生提供了极大的学习灵活性,他们可以依据个人的时间安排自由选择学习时间,不受地点限制。特别适用于系统化学习体系的内容,视频课能够帮助学生按部就班地掌握知识,实现自主高效学习。' />
         {this.dataShow ? (
           <List
             class={styles.videoList}

+ 168 - 0
src/student/teacher-dependent/components/vip.module.less

@@ -0,0 +1,168 @@
+.practice {
+  padding: 4px 14px 80px;
+  overflow: hidden;
+  
+  .group {
+    margin-bottom: 12px;
+    border-radius: 10px;
+    overflow: hidden;
+
+    :global {
+      .van-cell {
+        padding: 16px 15px;
+        font-size: 16px;
+        .van-cell__title {
+          color: #1A1A1A;
+        }
+      }
+    }
+  }
+  .price {
+    font-size: 14px;
+    color: #999999;
+    span {
+      font-weight: 600;
+      color: #fa6400;
+      font-size: 16px;
+    }
+  }
+
+  :global {
+    .van-stepper--round .van-stepper__minus {
+      color: #333 !important;
+      border: #f3f3f3;
+      background: #f3f3f3;
+    }
+
+    .van-stepper--round .van-stepper__plus {
+      background: var(--van-primary);
+    }
+  }
+}
+
+.rTag {
+  padding: 10px 0;
+  .tag {
+    background: #e9fff8;
+    margin-bottom: 8px;
+  }
+}
+
+.selectPopup {
+  width: 312px;
+  background: #ffffff;
+  border-radius: 8px;
+  .selectContainer {
+    padding: 18px 14px;
+  }
+  .rTitle {
+    font-size: 18px;
+  }
+  .selectPopupContent {
+    padding: 20px 0;
+  }
+  .desc,
+  .times {
+    font-size: 14px;
+    color: #666666;
+    line-height: 20px;
+  }
+
+  .times {
+    padding-top: 15px;
+    span {
+      display: block;
+    }
+  }
+
+  .selectBtn {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    .btn {
+      width: 48%;
+    }
+  }
+}
+
+.rTitle {
+  display: flex;
+  align-items: center;
+  font-size: 16px;
+  color: #333;
+  font-weight: 500;
+  &::before {
+    margin-right: 8px;
+    content: ' ';
+    display: inline-block;
+    width: 4px;
+    height: 17px;
+    background: linear-gradient(180deg, #59e5d5 0%, #2dc7aa 100%);
+    border-radius: 3px;
+  }
+}
+
+.protocol {
+  padding: 0 14px;
+  background-color: #f6f8f9;
+}
+
+.arrangeCell {
+  margin: 10px 0 0 !important;
+  width: auto;
+  border-radius: 10px;
+  overflow: hidden;
+}
+
+.fixedBtn {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  z-index: 9;
+}
+
+
+.tipSection {
+  position: relative;
+  background: #FFFFFF;
+  border-radius: 6px;
+  margin-bottom: 12px;
+  .iconCross {
+    position: absolute;
+    top: 15px;
+    right: 13px;
+    font-size: 16px;
+    color: #CCCCCC;
+    cursor: pointer;
+  }
+  .tipTitle {
+    padding: 12px 12px 10px;
+    font-weight: 500;
+    font-size: 15px;
+    color: #333333;
+    line-height: 18px;
+
+    img {
+      width: 18px;
+      height: 18px;
+      margin-right: 6px;
+      vertical-align: text-bottom;
+    }
+  }
+  .tipContent {
+    padding: 0 12px 10px;
+    font-size: 13px;
+    color: #777777;
+    line-height: 22px;
+  }
+  .tipFooter {
+    margin: 0 12px;
+    padding: 10px 0;
+    text-align: center;
+    border-top: 1px solid #F2F2F2;
+    font-size: 13px;
+    color: #2DC7AA;
+    line-height: 18px;
+  }
+}

+ 581 - 0
src/student/teacher-dependent/components/vip.tsx

@@ -0,0 +1,581 @@
+import Calendar from '@/business-components/calendar'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import dayjs from 'dayjs'
+import {
+  ActionSheet,
+  Button,
+  Cell,
+  CellGroup,
+  Dialog,
+  Stepper,
+  Sticky,
+  Tag,
+  Popup,
+  Toast,
+  Icon
+} from 'vant'
+import { defineComponent } from 'vue'
+import { getWeekCh } from '@/helpers/utils'
+import styles from './practice.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 Tips from './tips'
+
+export default defineComponent({
+  name: 'vip',
+  props: {
+    userInfo: {
+      type: Object,
+      default: {}
+    }
+  },
+  data() {
+    const query = this.$route.query
+    return {
+      teacherId: query.teacherId,
+      subjectId: query.subjectId,
+      teacherSubjectList: [],
+      subjectStatus: false,
+      subjectInfo: {
+        subjectPrice: 0,
+        courseMinutes: 0,
+        id: null,
+        subjectName: '',
+        subjectId: 0
+      },
+      courseNum: 4,
+      calendarStatus: false,
+      calendarList: [] as any,
+      selectCourseList: [] as any,
+      coursePlanStatus: false,
+      selectStatus: false,
+      coursePlanList: [] as any,
+      calendarDate: dayjs().add(1, 'day').toDate() as Date, // 日历当前时间
+      settingStatus: true, // 是否设置VIP定制课
+      loadDataStatus: true // 是否加载数据
+    }
+  },
+  async mounted() {
+    try {
+      this.loadDataStatus = true
+      const res = await request.get(
+        '/api-student/courseSchedule/getTeacherSubjectPrice',
+        {
+          params: {
+            teacherId: this.teacherId
+          }
+        }
+      )
+      
+      const result = res.data || []
+      if (result.length > 0) {
+        const userSubjectId = this.subjectId || state.user.data?.subjectId
+        const findItem = result.find((item: any) => {
+          return item.subjectId === Number(userSubjectId)
+        })
+        // 判断是否有跟学生相同的科目,如果没有则默认取第一个
+        const tempRes = findItem || result[0]
+        const { subjectName, subjectPrice, courseMinutes, subjectId, id } = tempRes
+        this.subjectInfo = {
+          subjectPrice,
+          id,
+          courseMinutes,
+          subjectName,
+          subjectId
+        }
+
+        result.forEach((item: any) => {
+          item.name = item.subjectName
+        })
+        this.teacherSubjectList = result
+
+        this.getList()
+
+        this.onBuy(true)
+
+        this.settingStatus = true
+      } else {
+        this.settingStatus = false
+      }
+
+      // 判断如果是审核的则不显示
+      const resVersion = await request.post('/api-teacher/open/appVersion', {
+        data: {
+          platform:
+            state.platformType === 'STUDENT' ? 'ios-student' : 'ios-teacher',
+          version: state.version
+        }
+      })
+      this.settingStatus =  resVersion.data.check ? false : true
+      this.loadDataStatus = false
+    } catch {
+      this.loadDataStatus = false
+    }
+  },
+  computed: {
+    showSelectList() {
+      const arr: any = this.selectCourseList
+      let list = [...arr]
+      list.forEach((item: any) => {
+        item.title =
+          dayjs(item.startTime).format('YYYY-MM-DD') +
+          ' ' +
+          getWeekCh(dayjs(item.startTime).day()) +
+          ' ' +
+          item.start +
+          '~' +
+          item.end
+      })
+      return list
+    },
+    selectType() {
+      // 循环次数是否足够
+      return this.selectCourseList.length < this.courseNum
+        ? 'noEnough'
+        : 'enough'
+    }
+  },
+  methods: {
+    async onSubmit() {
+      if (this.selectCourseList.length <= 0) {
+        Toast('请选择课程时间')
+        return
+      }
+
+      if (this.selectCourseList.length < this.courseNum) {
+        this.selectStatus = true
+        return
+      }
+
+      await this._lookCourse()
+    },
+    async getList(date?: Date) {
+      try {
+        const tempDate = date || dayjs().add(1, 'day').toDate()
+        let params = {
+          day: dayjs(tempDate).format('DD'),
+          month: dayjs(tempDate).format('MM'),
+          year: dayjs(tempDate).format('YYYY')
+        }
+        let res = await request.post(
+          '/api-student/courseSchedule/createPracticeCourseCalendar',
+          {
+            data: {
+              ...params,
+              teacherSubjectPriceId: this.subjectInfo.id,
+              studentId: state.user.data?.userId,
+              teacherId: this.teacherId
+            }
+          }
+        )
+        const result = res.data || []
+        let tempObj = {}
+        result.forEach((item: any) => {
+          tempObj[item.date] = item
+        })
+        this.calendarList = tempObj
+        this.calendarStatus = result.length > 0
+      } catch {}
+    },
+    onSelectDay(obj: any) {
+      const result = obj || []
+      let list = [...this.selectCourseList] as any
+
+      result.forEach((item: any) => {
+        const isExist = list.some(
+          (course: any) => course.startTime === item.startTime
+        )
+        !isExist && list.push({ ...item })
+      })
+      // 去掉不在
+      let tempList: any[] = []
+      list.forEach((item: any) => {
+        const isExist = result.some(
+          (course: any) => course.startTime === item.startTime
+        )
+        isExist && tempList.push(item)
+      })
+      // 对数组进行排序
+      tempList.sort((first: any, second: any) => {
+        if (first.startTime > second.startTime) return 1
+        if (first.startTime < second.startTime) return -1
+        return 0
+      })
+      console.log(tempList, 'list')
+      this.selectCourseList = [...tempList] as any
+    },
+    onCloseTag(item: any) {
+      Dialog.confirm({
+        title: '提示',
+        message: '您是否要删除该选择的课程?',
+        confirmButtonColor: 'var(--van-primary)'
+      }).then(() => {
+        const index = this.selectCourseList.findIndex(
+          (course: any) => course.startTime === item.startTime
+        )
+        this.selectCourseList.splice(index, 1)
+      })
+    },
+    async _lookCourse(callBack?: Function) {
+      try {
+        let times = [] as any
+        this.selectCourseList.forEach((item: any) => {
+          times.push({
+            startTime: item.startTime,
+            endTime: item.endTime
+          })
+        })
+        const res = await request.post(
+          '/api-student/courseGroup/lockCourseToCache',
+          {
+            data: {
+              courseNum: this.courseNum,
+              courseType: 'PRACTICE',
+              loop: this.selectType === 'noEnough' ? 1 : 0,
+              teacherId: this.teacherId,
+              timeList: [...times]
+            }
+          }
+        )
+        const result = res.data || []
+        result.forEach((item: any, index: number) => {
+          this.coursePlanList[index] = {
+            ...this.coursePlanList[index],
+            startTime: item.startTime,
+            endTime: item.endTime,
+            classNum: index + 1
+          }
+        })
+        this.coursePlanStatus = true
+        this.selectStatus = true
+        callBack && callBack()
+      } catch (e: any) {
+        // 报错时需要重置日历表的数据
+        const message = e.message
+        Dialog.alert({
+          title: '提示',
+          confirmButtonColor: 'var(--van-primary)',
+          message
+        }).then(() => {
+          this.getList(this.calendarDate || new Date())
+          this.selectCourseList = []
+          this.selectStatus = false
+        })
+      }
+    },
+    async onReset() {
+      // 是否有锁课状态 或 是锁课类型的
+      if (this.coursePlanStatus || this.selectType === 'enough') {
+        this.selectStatus = false
+        setTimeout(() => {
+          this.coursePlanList = []
+        }, 500)
+      } else if (this.selectType === 'noEnough') {
+        this.selectStatus = false
+      }
+      setTimeout(() => {
+        this.coursePlanStatus = false
+      }, 500)
+    },
+    async onSure() {
+      const status = this.coursePlanStatus
+      await this._lookCourse(() => {
+        if (status) {
+          this.selectStatus = false
+          this.onBuy()
+        }
+      })
+    },
+    async onBuy(goTo?: boolean) {
+      try {
+        const res = await request.post(
+          '/api-student/userOrder/getPendingOrder',
+          {
+            data: {
+              goodType: 'VIP',
+              bizId: this.teacherId
+            }
+          }
+        )
+        const subjectInfo = this.subjectInfo
+        const tempCourseList = [...this.coursePlanList]
+        // console.log(this.coursePlanList)
+        tempCourseList.forEach((item: any) => {
+          item.classDate = dayjs(item.startTime).format('YYYY-MM-DD')
+          item.title = `${dayjs(item.startTime).format(
+            'YYYY-MM-DD'
+          )} ${getWeekCh(dayjs(item.startTime).day())} ${dayjs(
+            item.startTime
+          ).format('HH:mm')}~${dayjs(item.endTime).format('HH:mm')}`
+        })
+        orderStatus.orderObject.orderType = 'VIP'
+        orderStatus.orderObject.orderName = subjectInfo.subjectName + 'VIP定制课'
+        orderStatus.orderObject.orderDesc = subjectInfo.subjectName + 'VIP定制课'
+        orderStatus.orderObject.actualPrice = Number(
+          (this.courseNum * subjectInfo.subjectPrice).toFixed(2)
+        )
+        orderStatus.orderObject.orderNo = ''
+        orderStatus.orderObject.orderList = [
+          {
+            orderType: 'VIP',
+            goodsName: subjectInfo.subjectName + 'VIP定制课',
+            courseGroupName: subjectInfo.subjectName + 'VIP定制课',
+            courseIntroduce: subjectInfo.subjectName + 'VIP定制课',
+            subjectId: subjectInfo.subjectId,
+            singleCourseMinutes: subjectInfo.courseMinutes,
+            courseNum: this.courseNum,
+            coursePrice: (this.courseNum * subjectInfo.subjectPrice).toFixed(2),
+            teacherName:
+              this.userInfo.username || `游客${this.userInfo.userId || ''}`,
+            teacherId: this.userInfo.userId,
+            starGrade: this.userInfo.starGrade,
+            avatar: this.userInfo.heardUrl,
+            classTime: tempCourseList
+          }
+        ]
+        const result = res.data
+        if (result) {
+          Dialog.confirm({
+            title: '提示',
+            message: '您有一个未支付的订单,是否继续支付?',
+            confirmButtonColor: '#269a93',
+            cancelButtonText: '取消订单',
+            confirmButtonText: '继续支付'
+          })
+            .then(async () => {
+              tradeOrder(result, this.routerTo)
+              // this.routerTo()
+            })
+            .catch(() => {
+              Dialog.close()
+              // 只用取消订单,不用做其它处理
+              this.cancelPayment(result.orderNo)
+            })
+        } else {
+          !goTo && this.routerTo()
+        }
+      } catch {}
+    },
+    routerTo() {
+      this.$router.push({
+        path: '/orderDetail',
+        query: {
+          orderType: 'VIP'
+        }
+      })
+    },
+    async cancelPayment(orderNo: string) {
+      try {
+        await request.post('/api-student/userOrder/orderCancel', {
+          data: {
+            orderNo
+          }
+        })
+        // this.routerTo()
+      } catch {}
+    }
+  },
+  render() {
+    return (
+      <>
+        {!this.loadDataStatus &&
+          (this.settingStatus ? (
+            <>
+              <div class={styles.practice}>
+                <Tips title='什么是VIP定制课?' content='VIP定制课程采用一对一专属授课模式,每节课时长为45分钟。课程内容根据学生的具体需求量身打造,旨在全面提升学生的个人技能与表现。不论是希望在乐器演奏技巧上取得突破,如提高指法精准度、气息控制能力或节奏掌握等;还是为即将到来的重要活动、比赛或考级做充分准备,我们都能提供高度匹配的教学方案。此外,教学进度将根据每位学员的学习吸收情况灵活调整,确保每个人都能在最适合自己的节奏中稳步前进,扎实提升个人能力。' />
+                <CellGroup class={styles.group} border={false}>
+                  <Cell
+                    title="选择专业"
+                    isLink
+                    value={this.subjectInfo.subjectName}
+                    onClick={() => (this.subjectStatus = true)}
+                  />
+                  {this.subjectInfo.subjectPrice > 0 && (
+                    <Cell
+                      title="VIP定制课收费"
+                      v-slots={{
+                        default: () => (
+                          <div class={styles.price}>
+                            <span>
+                              ¥
+                              {(this as any).$filters.moneyFormat(
+                                this.subjectInfo.subjectPrice
+                              )}
+                            </span>
+                            /{this.subjectInfo.courseMinutes}分钟
+                          </div>
+                        )
+                      }}
+                    />
+                  )}
+                  <Cell
+                    title="课时数"
+                    v-slots={{
+                      default: () => (
+                        <Stepper
+                          v-model={this.courseNum}
+                          theme="round"
+                          max={12}
+                          min={1}
+                          buttonSize={22}
+                          onChange={() => {
+                            this.selectCourseList = []
+                          }}
+                        />
+                      )
+                    }}
+                  />
+                </CellGroup>
+
+                {this.calendarStatus && (
+                  <div class={styles.group}>
+                    <Calendar
+                      selectList={this.selectCourseList}
+                      list={this.calendarList}
+                      maxDays={this.courseNum}
+                      nextMonth={(date: Date) => this.getList(date)}
+                      prevMonth={(date: Date) => this.getList(date)}
+                      selectDay={this.onSelectDay}
+                      v-model:calendarDate={this.calendarDate}
+                    />
+                  </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>}
+
+                <Popup show={this.selectStatus} class={styles.selectPopup}>
+                  <div class={styles.selectContainer}>
+                    <div class={styles.rTitle}>
+                      <span>提示</span>
+                    </div>
+                    <div class={styles.selectPopupContent}>
+                      <p class={styles.desc}>
+                        {this.selectType === 'noEnough' &&
+                        !this.coursePlanStatus
+                          ? '您所选择的上课时间未达到您输入的课时数,系统根据已选时间将自动按周顺延排课。'
+                          : '您已选择以下上课时间段,时间段会暂时锁定,锁定期间学员不可购买该时间段课程。'}
+                      </p>
+                      {this.coursePlanList &&
+                        this.coursePlanList.length > 0 &&
+                        this.coursePlanStatus && (
+                          <p class={styles.times}>
+                            {this.coursePlanList.map((item: any) => (
+                              <span>
+                                {dayjs(item.startTime || new Date()).format(
+                                  'YYYY-MM-DD'
+                                )}{' '}
+                                {dayjs(item.startTime || new Date()).format(
+                                  'HH:mm'
+                                )}
+                                ~
+                                {dayjs(item.endTime || new Date()).format(
+                                  'HH:mm'
+                                )}
+                              </span>
+                            ))}
+                          </p>
+                        )}
+                    </div>
+
+                    <div class={styles.selectBtn}>
+                      <Button
+                        class={styles.btn}
+                        type="primary"
+                        round
+                        block
+                        plain
+                        onClick={this.onReset}
+                      >
+                        {this.selectType === 'noEnough'
+                          ? '继续选择'
+                          : '重新选择'}
+                      </Button>
+                      <Button
+                        class={styles.btn}
+                        type="primary"
+                        round
+                        block
+                        onClick={this.onSure}
+                      >
+                        确认
+                      </Button>
+                    </div>
+                  </div>
+                </Popup>
+
+                <ActionSheet
+                  show={this.subjectStatus}
+                  actions={this.teacherSubjectList}
+                  cancelText="取消"
+                  closeOnClickAction
+                  onCancel={() => (this.subjectStatus = false)}
+                  onSelect={(item: any) => {
+                    const {
+                      subjectName,
+                      subjectPrice,
+                      courseMinutes,
+                      id,
+                      subjectId
+                    } = item
+                    this.subjectInfo = {
+                      subjectPrice,
+                      id,
+                      courseMinutes,
+                      subjectName,
+                      subjectId
+                    }
+                    this.subjectStatus = false
+                  }}
+                />
+              </div>
+              <div
+                class={['btnGroup', styles.fixedBtn]}
+                style={{ background: '#fff', paddingTop: '10px' }}
+              >
+                <Button block round type="primary" onClick={this.onSubmit}>
+                  确认约课
+                </Button>
+              </div>
+            </>
+          ) : (
+            <ColResult
+              btnStatus={false}
+              classImgSize="SMALL"
+              tips="老师暂未开放VIP定制课"
+            />
+          ))}
+      </>
+    )
+  }
+})

BIN
src/student/teacher-dependent/images/icon-add.png


BIN
src/student/teacher-dependent/images/icon-cert.png


BIN
src/student/teacher-dependent/images/icon-message.png


BIN
src/student/teacher-dependent/images/icon1.png


BIN
src/student/teacher-dependent/images/icon2.png


BIN
src/student/teacher-dependent/images/icon3.png


BIN
src/student/teacher-dependent/images/icon_subject1.png


+ 66 - 33
src/student/teacher-dependent/model/teacher-header.module.less

@@ -1,6 +1,6 @@
 .headerContent {
-  padding-top: 40px;
-  padding-bottom: 20px;
+  padding-top: 12px;
+  // padding-bottom: 20px;
   min-height: 100px;
   position: relative;
 }
@@ -11,9 +11,14 @@
   flex-shrink: 0;
 }
 
+.teacherUs {
+  padding-left: 20px;
+  flex: 1 auto;
+}
+
 .teacherIcon {
   position: relative;
-  margin-top: -38px;
+  // margin-top: -38px;
   line-height: 0;
 
   .avatar {
@@ -42,13 +47,12 @@
 .teacherInfo {
   display: flex;
   align-items: center;
-  padding: 14px 0;
+  padding: 14px 0 0;
 
   .teacherInfoName {
-    font-size: 20px;
-    font-weight: 500;
-    color: #1a1a1a;
-    // line-height: 22px;
+    font-weight: 600;
+    font-size: 22px;
+    color: #FFFFFF;
     max-width: 150px;
     overflow: hidden;
     white-space: nowrap;
@@ -69,8 +73,8 @@
   line-height: 16px;
   color: #666;
   font-weight: 500;
-  padding-bottom: 12px;
-  padding-top: 10px;
+  padding-top: 4px;
+  padding-left: 8px;
 
   .score {
     margin-left: 25px;
@@ -78,44 +82,69 @@
 }
 
 .headerCount {
-  width: calc(100% - 28px);
-  padding: 12px;
+  width: 100%;
+  padding: 14px 14px 20px;
   margin: 0 auto;
-  background-color: #fff;
   border-radius: 10px;
   box-sizing: border-box;
 }
 
 .teacherOperation {
+  padding-top: 18px;
+  display: flex;
   :global {
     .van-button {
-      height: 28px;
+      height: 36px;
+      border-radius: 6px;
+      background: rgba(255, 255, 255, .2);
+      border: none;
+      color: #FFFFFF;
+      font-size: 14px;
+      font-weight: 600;
     }
   }
 
   .btn {
     padding: 3px 12px 1px;
-    min-width: 62px;
+    flex: 1;
+    img {
+      width: 18px;
+      // height: 18px;
+      vertical-align: sub;
+      margin-right: 6px;
+
+    }
   }
 
+
   .btnStar {
-    color: #f18400;
-    border-color: #f18400;
+    
+    
+    color: #F8F9FC;
+    background: #2DC7AA;
+    margin-right: 13px;
+    // &::before {
+    //   background: #2DC7AA;
+    // }
   }
 }
 
 .subjectSection {
   margin-right: 10px;
   // height: 18px;
-  max-width: 68px;
+  max-width: 44px;
   box-sizing: content-box;
 }
 
 .teacher-bottom {
-  padding: 30px 0 0 0;
+  padding: 17px 0 0 0;
   display: flex;
   align-items: center;
-  justify-content: space-between;
+
+  .iconCert {
+    height: 18px;
+    margin-right: 8px;
+  }
 }
 
 .followFans {
@@ -128,14 +157,17 @@
   justify-content: space-between;
 
   .teacher-data_item {
-    font-size: 14px;
-    color: #333333;
+    font-size: 13px;
+    color: rgba(255,255,255,0.6);
     position: relative;
+    line-height: 1.2;
 
     span {
       font-weight: 500;
-      color: #000000;
-      font-size: 20px;
+      font-family: DINAlternate, DINAlternate;
+      font-weight: bold;
+      font-size: 15px;
+      color: #fff;
       margin-left: 5px;
     }
 
@@ -207,7 +239,7 @@
 
 .subjectList {
   overflow: auto;
-  width: 255px;
+  width: 285px;
   // height: 18px;
   display: flex;
   flex-wrap: nowrap;
@@ -216,17 +248,18 @@
 .piNameSubject {
   display: flex;
   align-items: center;
+  padding-top: 20px;
 
   .subject {
     display: flex;
     align-items: center;
-    margin-left: 4px;
-    background: #effbf9;
-    border-radius: 8px;
+    margin-left: 8px;
+    background: rgba(255, 255, 255, .2);
+    border-radius: 10px;
     font-size: 12px;
-    line-height: 16px;
-    color: #2dc7aa;
-    padding: 0 5px;
+    line-height: 1.3;
+    color: #FFFFFF;
+    padding: 4px 8px 3px;
     white-space: nowrap;
 
     &:first-child {
@@ -362,8 +395,8 @@
     }
 
     .avatar {
-      width: 60px;
-      height: 60px;
+      width: 72px;
+      height: 72px;
     }
 
     .piNameSubject {

+ 75 - 118
src/student/teacher-dependent/model/teacher-header.tsx

@@ -179,132 +179,89 @@ export default defineComponent({
                   />
                 )}
               </div>
-              <div class={styles.teacherOperation}>
-                <Button
-                  type="primary"
-                  size="small"
-                  plain
-                  round
-                  class={[
-                    styles.btn,
-                    this.userInfo.isStar ? styles.btnStar : ''
-                  ]}
-                  onClick={this.onStart}
-                >
-                  {/* {!this.userInfo.isStar && <Icon name="plus" />} */}
-
-                  {this.userInfo.isStar ? '已关注' : '关注'}
-                </Button>
-                <Button
-                  type="primary"
-                  size="small"
-                  round
-                  style={{ marginLeft: '5px' }}
-                  class={styles.btn}
-                  icon={IconChat}
-                  onClick={() => {
-                    postMessage({
-                      api: 'joinChatGroup',
-                      content: {
-                        type: 'single', // single 单人 multi 多人
-                        id: this.userInfo.imUserId
-                        // id: this.teacherId
-                      }
-                    })
-                  }}
-                >
-                  {/* <Icon name={} size="16" style={{ marginRight: '3px' }} /> */}
-                  聊天
-                </Button>
-              </div>
-            </div>
-            <div class={styles.teacherInfo}>
-              <div class={styles.teacherInfoName}>
-                {this.userInfo.username || `游客${this.userInfo.userId || ''}`}
+              <div class={styles.teacherUs}>
+                <div class={styles.teacherInfo}>
+                  <div class={styles.teacherInfoName}>
+                    {this.userInfo.username ||
+                      `游客${this.userInfo.userId || ''}`}
+                  </div>
+                  <div class={styles.teacherHonor}>
+                    {/* <div class={styles.score}>评分:</div> */}
+                    <div class={styles.level}>
+                      {this.starGrade ? (
+                        <Rate
+                          readonly
+                          modelValue={this.starGrade}
+                          iconPrefix="iconfont"
+                          color="#FFC459"
+                          void-icon="star_default"
+                          icon="star_active"
+                          size={15}
+                        />
+                      ) : (
+                        ''
+                      )}
+                    </div>
+                  </div>
+                </div>
+                <div class={styles['teacher-bottom']}>
+                  <img src={getAssetsHomeFile('icon-cert.png')} class={styles.iconCert} />
+                  <div class={styles['teacher-data']}>
+                    <div class={styles['teacher-data_item']}>
+                      粉丝 <span>{this.userInfo.fansNum || 0}</span>
+                    </div>
+                    <div class={styles['teacher-data_item']}>
+                      已上课时 <span>{this.userInfo.expTime || 0}</span>
+                    </div>
+                  </div>
+                </div>
               </div>
-              {this.userInfo.degreeFlag ? <img src={IconXueli} /> : null}
-              {this.userInfo.teacherFlag ? <img src={IconJiaozi} /> : null}
             </div>
-            <div class={styles.teacherHonor}>
-              <div>勋章:</div>
-              <div class={styles.teacherIcons} onClick={this.openTeacherIcon}>
-                <Image
-                  class={styles.iconOther}
-                  src={
-                    this.checkBadge('STYLE')
-                      ? getAssetsHomeFile('cert_active.png')
-                      : getAssetsHomeFile('cert_default.png')
-                  }
-                />
-                <Image
-                  class={styles.iconOther}
-                  src={
-                    this.checkBadge('VIDEO')
-                      ? getAssetsHomeFile('video_active.png')
-                      : getAssetsHomeFile('video_default.png')
-                  }
-                />
-                <Image
-                  class={styles.iconOther}
-                  src={
-                    this.checkBadge('LIVE')
-                      ? getAssetsHomeFile('live_active.png')
-                      : getAssetsHomeFile('live_default.png')
-                  }
-                />
+            <div class={styles.piNameSubject}>
                 <Image
-                  class={styles.iconOther}
-                  src={
-                    this.checkBadge('MUSIC')
-                      ? getAssetsHomeFile('music_active.png')
-                      : getAssetsHomeFile('music_default.png')
-                  }
+                  class={styles.subjectSection}
+                  src={getAssetsHomeFile('icon_subject1.png')}
+                  fit="contain"
                 />
-              </div>
-              <div class={styles.score}>评分:</div>
-              <div class={styles.level}>
-                {this.starGrade ? (
-                  <Rate
-                    readonly
-                    modelValue={this.starGrade}
-                    iconPrefix="iconfont"
-                    color="#FFC459"
-                    void-icon="star_default"
-                    icon="star_active"
-                    size={15}
-                  />
-                ) : (
-                  <span style={{ fontSize: '12px', color: '#999999' }}>
-                    暂无评分
-                  </span>
-                )}
-              </div>
-            </div>
-            <div class={styles.piNameSubject}>
-              <Image
-                class={styles.subjectSection}
-                src={getAssetsHomeFile('icon_subject.png')}
-                fit="contain"
-              />
-              <div class={styles.subjectList}>
-                {this.subjectNameList.map((item: any) => (
-                  <span class={styles.subject}>{item}</span>
-                ))}
-              </div>
-            </div>
-            <div class={styles['teacher-bottom']}>
-              <div class={styles['teacher-data']}>
-                <div class={styles['teacher-data_item']}>
-                  粉丝 <span>{this.userInfo.fansNum || 0}</span>
-                </div>
-                <div class={styles['teacher-data_item']}>
-                  已上课时 <span>{this.userInfo.expTime || 0}</span>
+                <div class={styles.subjectList}>
+                  {this.subjectNameList.map((item: any) => (
+                    <span class={styles.subject}>{item}</span>
+                  ))}
                 </div>
               </div>
+            <div class={styles.teacherOperation}>
+              <Button
+                type="primary"
+                size="small"
+                class={[styles.btn, styles.btnStar]}
+                onClick={this.onStart}
+              >
+                {/* {this.userInfo.isStar  <img src={getAssetsHomeFile('icon-add.png')} />} */}
+                <img src={getAssetsHomeFile('icon-add.png')} />
+                {this.userInfo.isStar ? '已关注' : '关注'}
+              </Button>
+              <Button
+                type="primary"
+                size="small"
+                class={styles.btn}
+                onClick={() => {
+                  postMessage({
+                    api: 'joinChatGroup',
+                    content: {
+                      type: 'single', // single 单人 multi 多人
+                      id: this.userInfo.imUserId
+                      // id: this.teacherId
+                    }
+                  })
+                }}
+              >
+                <img src={getAssetsHomeFile('icon-message.png')} />
+                聊天
+              </Button>
             </div>
           </div>
         </div>
-        <Popup class={styles['teaherPopup']} v-model:show={this.iconShow}>
+        {/* <Popup class={styles['teaherPopup']} v-model:show={this.iconShow}>
           <Image src={getAssetsHomeFile('teacher-icon.png')} />
           <div class={styles.teacherIconWrap}>
             {iconList.map(n => {
@@ -324,7 +281,7 @@ export default defineComponent({
             class={styles.closeIcon}
             src={getAssetsHomeFile('icon-close.png')}
           />
-        </Popup>
+        </Popup> */}
       </>
     )
   }

+ 68 - 5
src/student/teacher-dependent/teacher-home.module.less

@@ -8,18 +8,35 @@
     .van-nav-bar{
       transition: all .3s;
     }
+    .van-tabs {
+      // --van-tabs-line-height: 22px;
+    }
+    .van-tabs__nav--line {
+      padding-bottom: 0;
+    }
     .van-tab {
-      margin-top: 12px;
-      margin-bottom: 5px;
+      // margin-top: 12px;
+      // margin-bottom: 5px;
       padding: 0 14px;
+      font-size: 16px;
+      color: #000000;
+      position: relative;
+      z-index: 9;
+      // --van-tab-line-height: 22px;
     }
     .van-tab--active {
-      font-size: 16px !important;
-      color: #333333;
+      font-weight: 600;
     }
     .van-button--plain.van-button--primary {
       background-color: transparent;
     }
+    .van-tabs__line {
+      width: 44px !important;
+      height: 6px;  
+      background: linear-gradient( 90deg, #2DC7AA 0%, rgba(45,199,170,0) 100%) !important;
+      border-radius: 3px;
+      bottom: 15px;
+    }
   }
 }
 .bgImg{
@@ -35,7 +52,7 @@
   top: 0;
   left: 0;
   width: 100%;
-  height: 188px;
+  height: 238px;
   background-color: rgba(0, 0, 0, .6);
   backdrop-filter: blur(10px);
   -webkit-backdrop-filter: blur(10px);
@@ -44,3 +61,49 @@
   position: relative;
   z-index: 10;
 }
+
+
+.singleSection {  
+  background: #F8F9FC;
+  border-radius: 10px 10px 0px 0px;
+  padding: 15px 14px 6px;
+  .btnType {
+    display: flex;
+    .btn2, .btn1 {
+      flex: 1;
+      font-weight: 500;
+      font-size: 14px;
+      color: #333333;
+      line-height: 36px;
+      border-radius: 6px;
+      text-align: center;
+      img {
+        vertical-align: middle;
+        width: 16px;
+        height: 16px;
+        margin-right: 6px;
+      }
+    }
+    .btn1 {
+      
+      background: linear-gradient( 310deg, rgba(92,214,214,0.07) 0%, rgba(255,255,255,0.03) 50%, rgba(255,255,255,0) 100%, rgba(255,255,255,0) 100%);
+      border: 1px solid #E3EFED;
+      margin-right: 10px;
+    }
+    .btn2 {
+      background: linear-gradient( 310deg, rgba(214,96,92,0.07) 0%, rgba(255,255,255,0.04) 50%, rgba(255,255,255,0) 100%, rgba(255,255,255,0) 100%);
+      border: 1px solid #FFEBE5;
+    }
+  }
+
+  .singleContent {
+    background: #FFFFFF;
+    border-radius: 6px;
+    margin-top: 12px;
+    font-size: 13px;
+    color: #777777;
+    line-height: 22px;
+    padding: 12px;
+    text-align: justify;
+  }
+}

+ 31 - 14
src/student/teacher-dependent/teacher-home.tsx

@@ -2,7 +2,6 @@ import ColHeader from '@/components/col-header'
 import { defineComponent } from 'vue'
 import styles from './teacher-home.module.less'
 import { Tab, Tabs } from 'vant'
-import Single from './components/single'
 import Practice from './components/practice'
 import Live from './components/live'
 import VideoList from './components/video'
@@ -13,6 +12,7 @@ import { useEventListener, useWindowScroll } from '@vueuse/core'
 import TeacherHeader from './model/teacher-header'
 import { useRect } from '@vant/use'
 import { useEventTracking } from '@/helpers/hooks'
+import Vip from './components/vip'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -28,7 +28,7 @@ export default defineComponent({
     const query = this.$route.query
     return {
       teacherId: query.teacherId as string,
-      tabs: tabs || query.tabs || 'single',
+      tabs: tabs || query.tabs || 'vip',
       userInfo: {} as any,
       background: 'rgba(55, 205, 177, 0)',
       headColor: '#fff',
@@ -77,6 +77,7 @@ export default defineComponent({
       <div class={styles['teacher-record']}>
         <div ref="headers">
           <ColHeader
+            // hideHeader={false}
             background={this.background}
             border={false}
             color={this.headColor}
@@ -91,7 +92,7 @@ export default defineComponent({
           />
         </div>
         <img class={styles.bgImg} src={this.userInfo.heardUrl} />
-        <div class={styles.bg}></div>
+        <div class={styles.bg} style={{ paddingTop: this.height + 'px' }}></div>
         <div class={styles.teacherHeader}>
           <TeacherHeader
             userInfo={this.userInfo}
@@ -101,12 +102,29 @@ export default defineComponent({
               this.userInfo.fansNum = val.fansNum
             }}
           />
+
+          <div class={styles.singleSection}>
+            <div class={styles.btnType}>
+              <div class={styles.btn1}>
+                <img src={getAssetsHomeFile('icon1.png')} />
+                个人风采
+              </div>
+              <div class={styles.btn2}>
+                <img src={getAssetsHomeFile('icon2.png')} />
+                粉丝群
+              </div>
+            </div>
+
+            <div class={styles.singleContent}>
+              毕业于中央音乐学员长笛专业,师从央音长笛系。曾获得2016年锦绣杯长笛大赛冠军。自2018年起研究长笛启蒙、考级到专业考试教育。总结出一套适合各个阶段需要的教学方式。所教学员考级通过率100%,专业院校复试率92%。
+            </div>
+          </div>
         </div>
         <Tabs
           color="var(--van-primary)"
           background="#f8f9fc"
           shrink
-          lineWidth={20}
+          lineWidth={44}
           sticky
           offsetTop={this.height}
           v-model:active={this.tabs}
@@ -114,12 +132,19 @@ export default defineComponent({
             sessionStorage.setItem('teacherHomeTabs', this.tabs as string)
           }}
         >
-          <Tab title="个人风采" name="single">
+          {/* <Tab title="个人风采" name="single">
             <div style={{ minHeight: this.homeContaiterHeight }}>
               {this.tabs === 'single' && <Single userInfo={this.userInfo} />}
             </div>
+          </Tab> */}
+          <Tab title="VIP定制课" name="vip">
+            <div style={{ minHeight: this.homeContaiterHeight }}>
+              {this.tabs === 'vip' && (
+                <Vip userInfo={this.userInfo} />
+              )}
+            </div>
           </Tab>
-          <Tab title="陪练课" name="practice">
+          <Tab title="趣纠课" name="practice">
             <div style={{ minHeight: this.homeContaiterHeight }}>
               {this.tabs === 'practice' && (
                 <Practice userInfo={this.userInfo} />
@@ -142,14 +167,6 @@ export default defineComponent({
             </div>
           </Tab>
         </Tabs>
-
-        {/* <div class={styles.container}>
-          {this.tabs === 'single' && <Single userInfo={this.userInfo} />}
-          {this.tabs === 'practice' && <Practice userInfo={this.userInfo} />}
-          {this.tabs === 'live' && <Live />}
-          {this.tabs === 'video' && <VideoList />}
-          {this.tabs === 'music' && <Music />}
-        </div> */}
       </div>
     )
   }

+ 32 - 1
src/student/trade/tradeOrder.ts

@@ -7,7 +7,7 @@ import dayjs from 'dayjs'
 const apiSuffix =
   state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
 // LIVE: '直播课',
-// PRACTICE: '陪练课',
+// PRACTICE: '趣纠课',
 // VIDEO: '视频课',
 // VIP: '开通会员',
 // MUSIC: '单曲点播'
@@ -113,6 +113,27 @@ export const formatOrderDetail = async (item: any, amount?: IAmount) => {
         }
       }
       break
+    case 'DISCOUNT':
+      {
+        try {
+          const res = await getDiscountDetail()
+          console.log(res, 'res', item, 'item')
+          tempList = {
+            orderType: item.goodType,
+            goodName: item.goodName,
+            num: item.goodNum,
+            id: item.id,
+            title: item.goodName || '',
+            // 判断是否有优惠金额
+            price: res.salePrice,
+            startTime: dayjs(res.startTime).format('YYYY-MM-DD'),
+            endTime: dayjs(res.endTime).format('YYYY-MM-DD')
+          }
+        } catch (e: any) {
+          throw new Error(e.message)
+        }
+      }
+      break
     case 'MUSIC':
       {
         try {
@@ -239,6 +260,16 @@ export const getAlbumDetail = async (id: any) => {
   }
 }
 
+// 获取畅学卡详情
+export const getDiscountDetail = async () => {
+  try {
+    const {data} = await request.get(`${apiSuffix}/memberPriceSettings/getDiscount`)
+    return data
+  } catch {
+    throw new Error('获取畅学卡详情失败')
+  }
+}
+
 // 为了处理继续支付逻辑
 export const tradeOrder = (result: any, callBack?: any) => {
   const {

BIN
src/styles/font/DIN_Alternate_Bold.ttf


+ 4 - 0
src/styles/font/index.less

@@ -0,0 +1,4 @@
+@font-face {
+  font-family: 'DIN';
+  src: url('./DIN_Alternate_Bold.ttf');
+}

+ 1 - 0
src/styles/index.less

@@ -1,4 +1,5 @@
 @import url('./iconfont/iconfont.css');
+@import url('./font/index.less');
 
 :root {
   // Color Palette

+ 1 - 1
src/teacher/extend-plan/index.tsx

@@ -56,7 +56,7 @@ export default defineComponent({
         page: 1,
         rows: 20
       },
-      // 业务类型:PRACTICE, 陪练课 LIVE, 直播课 VIDEO, 视频课 MUSIC, 乐谱 WITHDRAWAL, 提现 LIVE_SHARE, 直播课分润 VIDEO_SHARE, 视频课分润 MUSIC_SHARE, 乐谱分润 VIP_SHARE, 会员分润 MALL_SHARE, 商品分润 ,可用值:PRACTICE,LIVE,VIDEO,MUSIC,VIP,MALL,WITHDRAWAL,LIVE_SHARE,VIDEO_SHARE,MUSIC_SHARE,VIP_SHARE,MALL_SHARE
+      // 业务类型:PRACTICE, 趣纠课 LIVE, 直播课 VIDEO, 视频课 MUSIC, 乐谱 WITHDRAWAL, 提现 LIVE_SHARE, 直播课分润 VIDEO_SHARE, 视频课分润 MUSIC_SHARE, 乐谱分润 VIP_SHARE, 会员分润 MALL_SHARE, 商品分润 ,可用值:PRACTICE,LIVE,VIDEO,MUSIC,VIP,MALL,WITHDRAWAL,LIVE_SHARE,VIDEO_SHARE,MUSIC_SHARE,VIP_SHARE,MALL_SHARE
       actions: [
         { name: '全部推广', value: '' },
         { name: '直播课', value: 'LIVE_SHARE' },

+ 2 - 2
src/teacher/income-consus/echarts.ts

@@ -53,7 +53,7 @@ export const lineChartOption = {
         '0.00'
       ],
       symbol: 'circle',
-      name: '陪练课',
+      name: '趣纠课',
       type: 'line',
       emphasis: { lineStyle: { width: 1 } }
     },
@@ -345,7 +345,7 @@ export const pieChartOption = {
       avoidLabelOverlap: false,
       label: { show: false },
       data: [
-        { name: '陪练课', value: '0.00' },
+        { name: '趣纠课', value: '0.00' },
         { name: '直播课', value: '0.00' },
         { name: '视频课', value: '0.00' },
         { name: '乐谱', value: '0.00' },

+ 2 - 2
src/teacher/income-consus/index.tsx

@@ -297,7 +297,7 @@ export default defineComponent({
               <Col span={6}>
                 <i></i>
                 <div class={styles.type}>
-                  <span>陪练课</span>
+                  <span>趣纠课</span>
                   <span class={styles.price}>
                     {moneyFormat(this.moneyInfo.practiceAmount)}
                   </span>
@@ -426,7 +426,7 @@ export default defineComponent({
           <div class={styles.pieData}>
             <div>
               <i class={styles.piePractice}></i>
-              <span class={styles.pieTitle}>陪练课</span>
+              <span class={styles.pieTitle}>趣纠课</span>
               <span>{this.moneyInfo.practiceRate}%</span>
             </div>
             <div>

+ 1 - 1
src/teacher/piano-room/tradeOrder.ts

@@ -3,7 +3,7 @@ import request from '@/helpers/request'
 import { orderStatus } from '@/views/order-detail/orderStatus'
 import dayjs from 'dayjs'
 // LIVE: '直播课',
-// PRACTICE: '陪练课',
+// PRACTICE: '趣纠课',
 // VIDEO: '视频课',
 // VIP: '开通会员',
 // MUSIC: '单曲点播'

BIN
src/teacher/practice-class/icon-question.png


+ 1 - 1
src/teacher/practice-class/model/timer.tsx

@@ -172,7 +172,7 @@ export default defineComponent({
         <div class={styles.tips}>
           <div class={styles.tipsTitle}>请选择陪练开始时间</div>
           <div class={styles.tipsTime}>
-            陪练课单课时时长为 <span>{this.courseMinutes}</span> 分钟
+            趣纠课单课时时长为 <span>{this.courseMinutes}</span> 分钟
           </div>
         </div>
 

+ 8 - 0
src/teacher/practice-class/practice-setting.module.less

@@ -77,3 +77,11 @@
     }
   }
 }
+
+.PracticeSettingBtns {
+  :global {
+    .van-button {
+      font-size: 18px;
+    }
+  }
+}

+ 50 - 168
src/teacher/practice-class/practice-setting.tsx

@@ -6,8 +6,6 @@ import request from '@/helpers/request'
 import { postMessage } from '@/helpers/native-message'
 import {
   Form,
-  Radio,
-  RadioGroup,
   Tag,
   Sticky,
   Button,
@@ -21,14 +19,17 @@ import {
 import { defineComponent } from 'vue'
 import styles from './practice-setting.module.less'
 import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
-import Timer from './model/timer'
 import ColHeader from '@/components/col-header'
 import { state } from '@/state'
+import TheSticky from '@/components/the-sticky'
 
 export default defineComponent({
   name: 'PracticeSetting',
   data() {
+    const query = this.$route.query
     return {
+      courseType: query.tabs == 'vip' ? 'VIP' : 'PRACTICE',
+      text: query.tabs == 'vip' ? 'VIP定制课' : '趣纠课',
       subjectList: [],
       chargeTypeArr: {
         0: '否',
@@ -37,23 +38,12 @@ export default defineComponent({
       checkStatus:false, // 是否审核版本
       classTimeStatus: false,
       subjectStatus: false,
-      timerStatus: false,
-      timeSetting: {
-        courseMinutes: 25,
-        freeMinutes: 5,
-        startSetting: '08:00',
-        endSetting: '18:00'
-      },
-      timerObject: {} as any,
       form: {
-        enableFlag: 1,
         courseMinutes: null as any,
         freeMinutes: 0,
         subjectIdTemp: '',
         subjectId: [] as any[],
         subjectPrice: [] as any[],
-        skipHolidayFlag: 1,
-        setting: ''
       },
       minutes: [] as any,
       rate: 0
@@ -67,6 +57,7 @@ export default defineComponent({
     }
   },
   async mounted() {
+    document.title = `${this.text}设置`
     try {
       // 获取手续费和分钟数
       const config = await request.get(
@@ -74,13 +65,13 @@ export default defineComponent({
         {
           params: {
             paramNames:
-              'practice_times_setting,practice_service_fee,course_start_setting,course_end_setting'
+              'practice_times_setting,practice_service_fee,vip_course_times_setting'
           }
         }
       )
       const configData = config.data || []
       configData.forEach((item: any) => {
-        if (item.paramName === 'practice_times_setting') {
+        if (item.paramName === (this.courseType === 'VIP' ? 'vip_course_times_setting' : 'practice_times_setting')) {
           const mins = item.paramValue ? JSON.parse(item.paramValue) : []
           const tempArr = [] as any
           mins.forEach((item: any) => {
@@ -90,16 +81,14 @@ export default defineComponent({
             })
           })
           this.minutes = [...tempArr]
+          if(this.minutes.length > 0) {
+            this.form.courseMinutes = this.minutes[0].courseMinutes
+            this.form.freeMinutes = this.minutes[0].freeMinutes
+          }
         }
         if (item.paramName === 'practice_service_fee') {
           this.rate = item.paramValue
         }
-        if (item.paramName === 'course_start_setting') {
-          this.timeSetting.startSetting = item.paramValue
-        }
-        if (item.paramName === 'course_end_setting') {
-          this.timeSetting.endSetting = item.paramValue
-        }
       })
       //
       const teacher = await request.post('/api-teacher/teacher/querySubject')
@@ -107,60 +96,30 @@ export default defineComponent({
 
       // 获取课程设置
       const setting = await request.post(
-        '/api-teacher/teacherFreeTime/getDetail',
+        '/api-teacher/teacherSubjectPrice/list',
         {
           data: {
-            defaultFlag: 1
+            courseType: this.courseType
           }
         }
       )
       const sr = setting.data
-      if (sr) {
-        this.timeSetting.courseMinutes = sr.courseMinutes
-        this.timeSetting.freeMinutes = sr.freeMinutes
-
-        this.timerObject = {
-          monday: sr.monday ? JSON.parse(sr.monday) : [],
-          tuesday: sr.tuesday ? JSON.parse(sr.tuesday) : [],
-          wednesday: sr.wednesday ? JSON.parse(sr.wednesday) : [],
-          thursday: sr.thursday ? JSON.parse(sr.thursday) : [],
-          friday: sr.friday ? JSON.parse(sr.friday) : [],
-          saturday: sr.saturday ? JSON.parse(sr.saturday) : [],
-          sunday: sr.sunday ? JSON.parse(sr.sunday) : []
-        }
-
-        const tempIds: any = []
-        const tempPrices: any = []
-        const subjectPrice = sr.subjectPrice || []
-        subjectPrice.forEach((item: any) => {
-          tempIds.push(item.subjectId)
-          tempPrices.push({
+      if(Array.isArray(sr)) {
+        const tempSubjects: any = []
+        sr.forEach((item: any) => {
+          this.form.courseMinutes = item.courseMinutes
+          this.form.freeMinutes = item.freeMinutes
+          this.form.subjectId.push(item.subjectId)
+          tempSubjects.push({
             subjectId: item.subjectId,
-            subjectPrice: item.subjectPrice,
-            subjectName: item.subjectName
+            subjectName: item.subjectName,
+            subjectPrice: item.subjectPrice
           })
         })
-        const to = this.timerObject
-        this.form = {
-          enableFlag: sr.enableFlag,
-          courseMinutes: sr.courseMinutes,
-          freeMinutes: sr.freeMinutes,
-          subjectIdTemp: tempIds.join(','),
-          subjectId: tempIds,
-          subjectPrice: tempPrices,
-          skipHolidayFlag: sr.skipHolidayFlag,
-          setting:
-            to.monday.length > 0 ||
-            to.tuesday.length > 0 ||
-            to.wednesday.length > 0 ||
-            to.thursday.length > 0 ||
-            to.friday.length > 0 ||
-            to.saturday.length > 0 ||
-            to.sunday.length > 0
-              ? '已设置'
-              : ''
-        }
+        this.form.subjectIdTemp = this.form.subjectId.join(',')
+        this.form.subjectPrice = tempSubjects
       }
+      
 
       // 判断如果是审核的则不显示
       const resVersion = await request.post('/api-teacher/open/appVersion', {
@@ -176,31 +135,9 @@ export default defineComponent({
   },
   methods: {
     onSelect(item: any) {
-      // 如果分钟数不同,则清空
-      if (this.form.courseMinutes !== item.courseMinutes) {
-        this.timerObject = {}
-        this.form.setting = ''
-      }
       this.form.courseMinutes = item.courseMinutes
       this.form.freeMinutes = item.freeMinutes
     },
-    async onTimer() {
-      try {
-        const form = this.form
-        if (!form.courseMinutes) {
-          Toast('请选择单课时时长')
-          return
-        }
-        this.timeSetting.courseMinutes = Number(form.courseMinutes)
-        this.timeSetting.freeMinutes = Number(form.freeMinutes)
-        this.timerStatus = true
-      } catch {}
-    },
-    onChoiceTimer(item: any, status: boolean) {
-      this.form.setting = status ? '已设置' : ''
-      this.timerObject = item
-      this.timerStatus = false
-    },
     onChoice(item: any) {
       console.log(item)
       const tempItem = item || []
@@ -240,16 +177,26 @@ export default defineComponent({
     async onSubmit() {
       try {
         const form = this.form
+        const params: any = []
         form.subjectPrice.forEach((item: any) => {
-          item.subjectName = this.getSubjectName(item.subjectId)
+          const subjectName = this.getSubjectName(item.subjectId)
+          console.log(subjectName, 'subjectName')
+          params.push({
+            ...item,
+            courseMinutes: this.form.courseMinutes,
+            courseType: this.courseType,
+            freeMinutes: this.form.freeMinutes,
+            subjectName
+            
+          })
         })
-        await request.post('/api-teacher/teacherFreeTime/upSet', {
-          data: {
-            ...form,
-            ...this.timerObject
-          }
+        console.log(params,'foparamsrm')
+        await request.post('/api-teacher/teacherSubjectPrice/saveOrUpdate', {
+          data: params
         })
-        Toast('设置成功')
+        setTimeout(() => {
+          Toast('设置成功')
+        }, 100);
         setTimeout(() => {
           postMessage({ api: 'back', content: {} })
         }, 500)
@@ -259,27 +206,8 @@ export default defineComponent({
   render() {
     return (
       <Form style={{ paddingTop: '15px' }} onSubmit={this.onSubmit}>
-        <ColHeader />
+        <ColHeader title={`${this.text}设置`} />
         <ColFieldGroup>
-          <ColField title="是否开启陪练课" required border={false}>
-            <RadioGroup
-              class={styles['radio-group']}
-              modelValue={this.form.enableFlag}
-              onUpdate:modelValue={val => (this.form.enableFlag = val)}
-            >
-              {['1', '0'].map((item: string) => {
-                const isActive = Number(item) === Number(this.form.enableFlag)
-                const type = isActive ? 'primary' : 'default'
-                return (
-                  <Radio class={styles.radio} name={item}>
-                    <Tag size="large" plain={isActive} type={type}>
-                      {this.chargeTypeArr[item]}
-                    </Tag>
-                  </Radio>
-                )
-              })}
-            </RadioGroup>
-          </ColField>
           <ColField title="可教授乐器" required>
             {this.form.subjectPrice && this.form.subjectPrice.length > 0 && (
               <CheckboxGroup
@@ -371,7 +299,7 @@ export default defineComponent({
           <ColFieldGroup>
             {this.form.subjectPrice.map((item: any) => (
               <ColField
-                title={`${this.getSubjectName(item.subjectId)}声部陪练价格`}
+                title={`${this.getSubjectName(item.subjectId)}声部${this.text}价格`}
                 required
               >
                 <Field
@@ -383,12 +311,12 @@ export default defineComponent({
                   rules={[
                     {
                       required: true,
-                      message: `请选择声部陪练价格`
+                      message: `请选择声部${this.text}价格`
                     }
                   ]}
                   formatter={this.onFormatter}
                   maxlength={8}
-                  placeholder={`请选择声部陪练价格`}
+                  placeholder={`请选择声部${this.text}价格`}
                   v-slots={{
                     button: () => <span>元</span>
                   }}
@@ -398,49 +326,13 @@ export default defineComponent({
           </ColFieldGroup>
         )}
 
-        <ColFieldGroup>
-          <ColField title="可陪练时间段">
-            <Field
-              modelValue={this.form.setting}
-              name="singleMins"
-              readonly
-              isLink
-              onClick={this.onTimer}
-              placeholder="未设置"
-            />
-          </ColField>
-        </ColFieldGroup>
-
-        <ColFieldGroup>
-          <ColField required title="是否跳过节假日" border={false}>
-            <RadioGroup
-              class={styles['radio-group']}
-              modelValue={this.form.skipHolidayFlag}
-              onUpdate:modelValue={val => (this.form.skipHolidayFlag = val)}
-            >
-              {['1', '0'].map((item: string) => {
-                const isActive =
-                  Number(item) === Number(this.form.skipHolidayFlag)
-                const type = isActive ? 'primary' : 'default'
-                return (
-                  <Radio class={styles.radio} name={item}>
-                    <Tag size="large" plain={isActive} type={type}>
-                      {this.chargeTypeArr[item]}
-                    </Tag>
-                  </Radio>
-                )
-              })}
-            </RadioGroup>
-          </ColField>
-        </ColFieldGroup>
-
-        <Sticky offsetBottom={0} position="bottom">
-          <div class={'btnGroup'}>
+        <TheSticky position="bottom">
+          <div class={['btnGroup', styles.PracticeSettingBtns]}>
             <Button block round type="primary" native-type="submit">
               提交
             </Button>
           </div>
-        </Sticky>
+        </TheSticky>
 
         <ColPopup v-model={this.subjectStatus} destroy>
           <ColHeader />
@@ -453,17 +345,7 @@ export default defineComponent({
           />
         </ColPopup>
 
-        <ColPopup v-model={this.timerStatus} destroy>
-          <ColHeader title="设置陪练时间段" />
-          <Timer
-            onChoice={this.onChoiceTimer}
-            timerObject={this.timerObject}
-            courseMinutes={Number(this.timeSetting.courseMinutes)}
-            freeMinutes={Number(this.timeSetting.freeMinutes)}
-            startSetting={this.timeSetting.startSetting}
-            endSetting={this.timeSetting.endSetting}
-          />
-        </ColPopup>
+
 
         <ActionSheet
           v-model:show={this.classTimeStatus}

+ 140 - 0
src/teacher/practice-class/timer/timer.module.less

@@ -0,0 +1,140 @@
+.timer {
+  background: #f6f8f9;
+  min-height: 100vh;
+  overflow: hidden;
+  padding: 0 14px;
+  :global {
+    .van-button {
+      font-size: 18px;
+      color: #FFFFFF;
+    }
+  }
+}
+.tips {
+  margin: 12px 0;
+  padding: 15px 12px;
+  background: #ffffff;
+  border-radius: 10px;
+
+  .tipsTitle {
+    font-size: 18px;
+    font-weight: 500;
+    color: #000000;
+    line-height: 25px;
+  }
+
+  .tipsTime {
+    padding-top: 4px;
+    font-size: 14px;
+    color: #ff9e5a;
+    line-height: 22px;
+    span {
+      font-weight: 600;
+    }
+  }
+}
+
+.radio-group,
+.checkbox-group {
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 14px;
+  .radio:first-child {
+    :global {
+      .van-radio__label {
+        margin-left: 0;
+      }
+    }
+  }
+  .checkbox:first-child {
+    :global {
+      .van-checked__label {
+        margin-left: 0;
+      }
+    }
+  }
+}
+
+.radio {
+  :global {
+    .van-radio__icon {
+      display: none;
+    }
+  }
+}
+.colField {
+  margin: 12px 0;
+  padding: 14px 12px;
+  background-color: #fff;
+  border-radius: 10px;
+  .radio-group {
+    :global {
+      .van-tag--large {
+        width: 94px;
+        height: 30px;
+        font-size: 16px;
+        text-align: center;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+      .van-tag {
+        box-sizing: border-box;
+      }
+      .van-tag--default {
+        color: var(--van-tag-text-default-color);
+      }
+      .van-tag--primary {
+        background-color: var(--tag-bg-color);
+      }
+    }
+  }
+
+  .title {
+    display: flex;
+    align-items: center;
+    img {
+      width: 18px;
+      height: 18px;
+      margin-right: 4px;
+    }
+  }
+}
+.timerContainer {
+  background: #ffffff;
+  border-radius: 10px;
+  padding: 14px 5px 9px;
+  :global {
+    .van-col {
+      margin-bottom: 5px;
+    }
+  }
+}
+
+.tag {
+  height: 28px;
+  background: #eff6f5;
+  border-radius: 4px;
+  font-size: 14px;
+  font-weight: 500;
+  color: #2dc7aa;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.active {
+  background: #2dc7aa;
+  color: #ffffff;
+}
+
+.select {
+  color: #ffffff !important;
+  background: #ffb752;
+}
+
+
+.btnGroupTimer {
+  padding-top: 20px;
+  background: #F6F8F9;
+}

+ 309 - 0
src/teacher/practice-class/timer/timer.tsx

@@ -0,0 +1,309 @@
+import { Button, Col, Radio, RadioGroup, Row, Tag, Toast } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './timer.module.less'
+import dayjs from 'dayjs'
+import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
+import customParseFormat from 'dayjs/plugin/customParseFormat'
+import iconQuestion from '../icon-question.png'
+import ColField from '@/components/col-field'
+import request from '@/helpers/request'
+import { postMessage } from '@/helpers/native-message'
+import TheSticky from '@/components/the-sticky'
+dayjs.extend(customParseFormat)
+dayjs.extend(isSameOrBefore)
+
+export default defineComponent({
+  name: 'timer',
+  data() {
+    return {
+      startSetting: '08:00',  // 开始设置时间
+      endSetting: '18:00', // 结束设置时间
+      freeMinutes: 5, // 空余时长
+      courseMinutes: 25, // // 课程时长
+      timerObject: {},
+      chargeTypeArr: {
+        0: '否',
+        1: '是'
+      },
+      timerList: [],
+      skipHolidayFlag: 1,
+      list: [] as any,
+      weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+      weekType: [
+        'monday',
+        'tuesday',
+        'wednesday',
+        'thursday',
+        'friday',
+        'saturday',
+        'sunday'
+      ]
+    }
+  },
+  async mounted() {
+   await this._initFetch()
+    this.list = this.timerInit(
+      this.startSetting,
+      this.endSetting,
+      this.courseMinutes + this.freeMinutes || 30
+    )
+  },
+  methods: {
+    async _initFetch() {
+      try {
+        // 获取手续费和分钟数
+        const config = await request.get(
+          '/api-teacher/sysConfig/queryByParamNameList',
+          {
+            params: {
+              paramNames:
+                'practice_service_fee,course_start_setting,course_end_setting'
+            }
+          }
+        )
+        const configData = config.data || []
+        configData.forEach((item: any) => {
+          if (item.paramName === 'course_start_setting') {
+            this.startSetting = item.paramValue
+          }
+          if (item.paramName === 'course_end_setting') {
+            this.endSetting = item.paramValue
+          }
+        })
+  
+        // 获取课程设置
+        const setting = await request.get('/api-teacher/teacherFreeTime/get')
+        const sr = setting.data
+        if (sr) {
+          this.timerObject = {
+            monday: sr.monday ? JSON.parse(sr.monday) : [],
+            tuesday: sr.tuesday ? JSON.parse(sr.tuesday) : [],
+            wednesday: sr.wednesday ? JSON.parse(sr.wednesday) : [],
+            thursday: sr.thursday ? JSON.parse(sr.thursday) : [],
+            friday: sr.friday ? JSON.parse(sr.friday) : [],
+            saturday: sr.saturday ? JSON.parse(sr.saturday) : [],
+            sunday: sr.sunday ? JSON.parse(sr.sunday) : []
+          }
+          this.skipHolidayFlag = sr.skipHolidayFlag ? 1 : 0
+        }
+  
+        
+      } catch {}
+    },
+    timerInit(startTime: string, endTime: string, space: number) {
+      let start = dayjs(startTime, 'HH:mm')
+      const end = dayjs(endTime, 'HH:mm')
+
+      const timerList: any = []
+      // 生成一天的时间段
+      while (start.add(space, 'minute').isSameOrBefore(dayjs(end))) {
+        const item = {
+          startTime: start.format('HH:mm'),
+          endTime: start.add(space, 'minute').format('HH:mm'),
+          status: false
+        }
+        // 一周
+        timerList.push(item)
+        start = start.add(space, 'minute')
+      }
+      const list: any = []
+      // 生成一周的时间段
+      timerList.forEach((item: any) => {
+        const weekList: any = []
+        for (let i = 0; i < 7; i++) {
+          weekList.push({
+            ...item
+          })
+        }
+        list.push(weekList)
+      })
+
+      const tempList = this._initData(list)
+      return tempList
+    },
+    _initData(list: Array<any>) {
+      // 回显数据
+      const weekType = this.weekType
+      const timerObject = this.timerObject
+      list.forEach((item: any) => {
+        item.forEach((slot: any, slotIndex: number) => {
+          const dayList = timerObject[weekType[slotIndex]]
+          const startTime = dayjs(slot.startTime, 'HH:mm').format('HH:mm:ss')
+          const isExist = dayList?.some(
+            (course: any) => course.startTime === startTime
+          )
+          isExist && (slot.status = true)
+        })
+      })
+      return list
+    },
+    btnStatus(index: number, type: 'row' | 'col') {
+      if (type === 'row') {
+        return this.list.every((item: any) => {
+          return item[index].status
+        })
+      }
+
+      if (type == 'col') {
+        return this.list[index].every((item: any) => item.status)
+      }
+    },
+    choice(index: number, type: 'row' | 'col', status?: boolean) {
+      if (type === 'row') {
+        this.list.forEach((item: any, i: number) => {
+          const type = !status ? true : false
+          item[index].status = type
+        })
+      }
+
+      if (type == 'col') {
+        this.list[index].forEach((item: any, i: number) => {
+          const type = !status ? true : false
+          item.status = type
+        })
+      }
+    },
+    async onSubmit() {
+      const list = this.list
+      const weekList = {
+        monday: [],
+        tuesday: [],
+        wednesday: [],
+        thursday: [],
+        friday: [],
+        saturday: [],
+        sunday: []
+      }
+      const weekType = this.weekType
+      let status = false
+      list.forEach((item: any, i: number) => {
+        item.forEach((times: any, j: number) => {
+          if (times.status) {
+            status = true
+            weekList[weekType[j]].push({
+              startTime: dayjs(times.startTime, 'HH:mm').format('HH:mm:ss'),
+              endTime: dayjs(times.endTime, 'HH:mm').subtract(this.freeMinutes, 'minute').format('HH:mm:ss')
+            })
+          }
+        })
+      })
+      await request.post('/api-teacher/teacherFreeTime/upSet', {
+        data: {...weekList,
+          skipHolidayFlag: this.skipHolidayFlag}
+      })
+      setTimeout(() => {
+        Toast('设置成功')
+      }, 100);
+      setTimeout(() => {
+        postMessage({ api: 'back', content: {} })
+      }, 500)
+    }
+  },
+  render() {
+    return (
+      <div class={styles.timer}>
+            <ColField  border={false} class={styles.colField}>
+              {{
+                title: () => <div class={styles.title}>
+                  <img src={iconQuestion}></img>
+                  是否跳过节假日
+                </div>,
+                default: () => <RadioGroup
+                class={styles['radio-group']}
+                modelValue={this.skipHolidayFlag}
+                onUpdate:modelValue={val => (this.skipHolidayFlag = val)}
+              >
+                {['1', '0'].map((item: string) => {
+                  const isActive =
+                    Number(item) === Number(this.skipHolidayFlag)
+                  const type = isActive ? 'primary' : 'default'
+                  return (
+                    <Radio class={styles.radio} name={item}>
+                      <Tag size="large" plain={isActive} type={type}>
+                        {this.chargeTypeArr[item]}
+                      </Tag>
+                    </Radio>
+                  )
+                })}
+              </RadioGroup>
+              }}
+            </ColField>
+        {/* <div class={styles.tips}>
+          <div class={styles.tipsTitle}>请选择陪练开始时间</div>
+          <div class={styles.tipsTime}>
+            趣纠课单课时时长为 <span>{this.courseMinutes}</span> 分钟
+          </div>
+        </div> */}
+
+        <div class={[styles.timerContainer, 'mb12']}>
+          <Row gutter={5}>
+            <Col span={3}></Col>
+            {this.weekList.map((item: any) => (
+              <Col span={3}>
+                <span class={styles.tag}>{item}</span>
+              </Col>
+            ))}
+          </Row>
+
+          <Row gutter={5}>
+            <Col span={3}></Col>
+            {this.weekList.map((item: any, index: number) => (
+              <Col span={3}>
+                <span
+                  class={[
+                    styles.tag,
+                    this.btnStatus(index, 'row') && styles.active
+                  ]}
+                  onClick={() =>
+                    this.choice(index, 'row', this.btnStatus(index, 'row'))
+                  }
+                  title={item}
+                >
+                  全选
+                </span>
+              </Col>
+            ))}
+          </Row>
+
+          {this.list.map((item: any, index: number) => (
+            <Row gutter={5}>
+              <Col span={3}>
+                <span
+                  class={[
+                    styles.tag,
+                    this.btnStatus(index, 'col') && styles.active
+                  ]}
+                  onClick={() =>
+                    this.choice(index, 'col', this.btnStatus(index, 'col'))
+                  }
+                >
+                  全选
+                </span>
+              </Col>
+              {item.map((week: any) => (
+                <Col span={3}>
+                  <span
+                    class={[styles.tag, week.status && styles.select]}
+                    title={week}
+                    style={{ color: '#333333' }}
+                    onClick={() => (week.status = !week.status)}
+                  >
+                    {week.startTime}
+                  </span>
+                </Col>
+              ))}
+            </Row>
+          ))}
+        </div>
+
+        <TheSticky position='bottom' >
+          <div class={['btnGroup', styles.btnGroupTimer]}>
+            <Button block round type="primary" onClick={this.onSubmit}>
+              确定
+            </Button>
+          </div>
+        </TheSticky>
+      </div>
+    )
+  }
+})

+ 2 - 2
src/tenant/exercise-record/echats.ts

@@ -227,7 +227,7 @@ export const lineChartOption = {
 //         '0.00'
 //       ],
 //       symbol: 'circle',
-//       name: '陪练课',
+//       name: '趣纠课',
 //       type: 'line',
 //       emphasis: { lineStyle: { width: 1 } }
 //     },
@@ -519,7 +519,7 @@ export const pieChartOption = {
       avoidLabelOverlap: false,
       label: { show: false },
       data: [
-        { name: '陪练课', value: '0.00' },
+        { name: '趣纠课', value: '0.00' },
         { name: '直播课', value: '0.00' },
         { name: '视频课', value: '0.00' },
         { name: '乐谱', value: '0.00' },

+ 1 - 0
src/tenant/music/coursewarePlay/component/courseware-tips/index.module.less

@@ -57,6 +57,7 @@
     margin-bottom: 16px;
     font-size: 14px;
     line-height: 1.6;
+    color: #333;
 
     &::-webkit-scrollbar {
       display: none;

+ 6 - 1
src/tenant/music/coursewarePlay/component/courseware-type/index.module.less

@@ -16,12 +16,17 @@
   flex-shrink: 0;
   font-size: 16px;
   font-weight: 500;
+  box-sizing: border-box;
 
   img {
     width: 20px;
     height: 20px;
     margin-right: 6px;
   }
+  span {
+    line-height: 1;
+    padding-top: 2px;
+  }
 }
 
 .content {
@@ -123,7 +128,7 @@
     &::before {
       content: '';
       position: absolute;
-      left: 5px;
+      left: 0px;
       width: 5px;
       height: 100%;
       background: linear-gradient(270deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0.03) 100%);

+ 1 - 1
src/tenant/music/coursewarePlay/component/courseware-type/index.tsx

@@ -17,7 +17,7 @@ export default defineComponent({
       <div class={styles.container}>
         <div class={styles.pointHead}>
           <img src={iconMenucw} />
-          课程类型
+          <span>课程类型</span>
         </div>
         <div class={styles.content}>
           {props.list.map((item: any) => <div class={styles.item} onClick={() => emit("confirm", item)}>

+ 8 - 1
src/tenant/music/coursewarePlay/component/o-guide/index.module.less

@@ -4,10 +4,14 @@
   :global {
     .van-tabs__nav {
       background-color: transparent;
+      --van-tabs-bottom-bar-width: 16px;
       .van-tab {
-        color: #fff;
+        color: rgba(255,255,255,.5);
         font-size: 16px;
       }
+      .van-tab--active {
+        color: #fff;
+      }
     }
     .van-tab__panel {
       height: calc(100vh - var(--van-tabs-line-height));
@@ -20,6 +24,9 @@
     }
     .van-tabs__line{
       bottom: .5rem;
+      background: linear-gradient( 135deg, #FF8BB3 0%, #FF3761 100%);
+      width: 16px;
+      height: 4px;
     }
   }
   .content {

+ 1 - 1
src/tenant/music/coursewarePlay/component/play-loading/index.module.less

@@ -13,7 +13,7 @@
   div {
     width: 2px;
     height: 10px;
-    background: linear-gradient(135deg, #FF6E8E; 0%, #ff6e8e 100%);
+    background: linear-gradient(135deg, #FF6486; 0%, #FF6486 100%);
     transform-origin: bottom;
     border-radius: 5px;
     margin: 0 1px 0;

+ 6 - 1
src/tenant/music/coursewarePlay/component/point.module.less

@@ -22,6 +22,10 @@
     height: 20px;
     margin-right: 6px;
   }
+  span {
+    line-height: 1;
+    padding-top: 2px;
+  }
 }
 
 
@@ -147,7 +151,8 @@
 }
 
 .itemActive {
-  background: rgba(255, 255, 225, 0.08);
+  background: rgba(255, 110, 142, 0.15);
+  
   color: #ff6e8e;
 
   :global {

+ 1 - 1
src/tenant/music/coursewarePlay/component/points.tsx

@@ -65,7 +65,7 @@ export default defineComponent({
       <div class={styles.container}>
         <div class={styles.pointHead}>
           <img src={iconMulv} />
-          课程目录
+          <span>知识点目录</span>
         </div>
         <div class={styles.content}>
           <Collapse

+ 6 - 4
src/tenant/music/coursewarePlay/component/tips/index.module.less

@@ -1,14 +1,12 @@
 
 .courseDialog {
   padding: 20px !important;
-  max-width: 310px !important;
+  max-width: 295px !important;
   min-width: 295px !important;
-  // background: url('./top-bg.png') no-repeat top center #fff !important;
-  // background-size: contain !important;
-  overflow: hidden;
   border-radius: 20px !important;
   overflow: hidden;
   background-color: #fff;
+  box-sizing: border-box;
 
   &::before {
     content: '';
@@ -68,6 +66,10 @@
 
     :global {
       --van-button-default-height: 40px;
+      .van-button--primary {
+        background: #FE2451 !important;
+        border-color: #FE2451 !important;
+      }
     }
   }
 }

+ 2 - 2
src/tenant/music/coursewarePlay/component/tools/pen.module.less

@@ -4,7 +4,7 @@
     right: 0;
     bottom: 0;
     top: 0;
-    z-index: 11;
+    z-index: 9999;
 }
 .open{
     display: block;
@@ -23,7 +23,7 @@
 }
 .rightItem{
     position: absolute;
-    right: 15Px;
+    right: 8Px;
     bottom: 0;
     bottom: constant(safe-area-inset-bottom);
     bottom: env(safe-area-inset-bottom);

+ 2 - 2
src/tenant/music/coursewarePlay/component/tools/pen.tsx

@@ -37,8 +37,8 @@ export default defineComponent({
     const { show } = toRefs(props);
     const firstRender = ref(true);
     const src = /(localhost|192)/.test(location.host)
-      ? 'https://test.lexiaoya.cn/whiteboard-noCollab'
-      : `https://online.lexiaoya.cn/whiteboard-noCollab`;
+      ? 'https://test.lexiaoya.cn/whiteboard-noCollab?platform=colexiu'
+      : `https://online.lexiaoya.cn/whiteboard-noCollab?platform=colexiu`;
 
     const exportImg = (event: MessageEvent) => {
       const data = event.data;

Некоторые файлы не были показаны из-за большого количества измененных файлов