lex-xin 4 månader sedan
förälder
incheckning
fb8756bd6d
27 ändrade filer med 1343 tillägg och 249 borttagningar
  1. 1 0
      src/student/discount-card/index.module.less
  2. 33 63
      src/student/discount-card/index.tsx
  3. 4 0
      src/student/teacher-dependent/components/live.module.less
  4. 3 1
      src/student/teacher-dependent/components/live.tsx
  5. 13 2
      src/student/teacher-dependent/components/practice.module.less
  6. 19 12
      src/student/teacher-dependent/components/practice.tsx
  7. 45 0
      src/student/teacher-dependent/components/tips/index.module.less
  8. 40 0
      src/student/teacher-dependent/components/tips/index.tsx
  9. 62 0
      src/student/teacher-dependent/components/video-item/index.module.less
  10. 92 0
      src/student/teacher-dependent/components/video-item/index.tsx
  11. 4 0
      src/student/teacher-dependent/components/video.module.less
  12. 4 2
      src/student/teacher-dependent/components/video.tsx
  13. 168 0
      src/student/teacher-dependent/components/vip.module.less
  14. 581 0
      src/student/teacher-dependent/components/vip.tsx
  15. BIN
      src/student/teacher-dependent/images/icon-add.png
  16. BIN
      src/student/teacher-dependent/images/icon-cert.png
  17. BIN
      src/student/teacher-dependent/images/icon-message.png
  18. BIN
      src/student/teacher-dependent/images/icon1.png
  19. BIN
      src/student/teacher-dependent/images/icon2.png
  20. BIN
      src/student/teacher-dependent/images/icon3.png
  21. BIN
      src/student/teacher-dependent/images/icon_subject1.png
  22. 66 33
      src/student/teacher-dependent/model/teacher-header.module.less
  23. 75 118
      src/student/teacher-dependent/model/teacher-header.tsx
  24. 68 5
      src/student/teacher-dependent/teacher-home.module.less
  25. 30 13
      src/student/teacher-dependent/teacher-home.tsx
  26. 31 0
      src/student/trade/tradeOrder.ts
  27. 4 0
      src/views/order-detail/orderStatus.ts

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

@@ -115,6 +115,7 @@
       line-height: 17px;
 
       span {
+        padding-left: 2px;
         color: #1d88ff;
         font-weight: 500;
       }

+ 33 - 63
src/student/discount-card/index.tsx

@@ -4,7 +4,7 @@ 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 } from '@/state'
+import { state as baseState, setLogin } from '@/state'
 
 import iconMemo from './images/memo.png'
 import iconTip from './images/tip.png'
@@ -23,7 +23,7 @@ export default defineComponent({
     const state = reactive({
       discountDetail: {} as any,
       titleOpacity: 0,
-      orderVisiable: false,
+      orderVisible: false,
       orderDetail: {} as any
     })
     const userInfo = computed(() => {
@@ -54,7 +54,7 @@ export default defineComponent({
         await request.post(`${baseState.platformApi}/userOrder/orderCancel`, {
           data: { orderNo: state.orderDetail.orderNo }
         })
-        state.orderVisiable = false
+        state.orderVisible = false
       } catch {
         //
       }
@@ -75,62 +75,20 @@ export default defineComponent({
 
     const onSubmit = async () => {
       try {
-        // 永久会员
-        // const { data } = await request.post(
-        //   `${baseState.platformApi}/memberPriceSettings/list`,
-        //   {
-        //     data: {
-        //       status: 1
-        //     }
-        //   }
-        // )
-        // const result = data.list || []
-        // const selectItem = result.find(
-        //   (item: any) => item.id === state.selectMember?.id
-        // )
-        // // 状态、售价变更时
-        // if (
-        //   !selectItem ||
-        //   (selectItem && selectItem.salePrice !== state.selectMember.salePrice)
-        // ) {
-        //   state.dialogVisiable = true
-        //   return
-        // }
 
         // 判断是否有待支付订单
-        // const resPadding = await request.post(
-        //   `${baseState.platformApi}/userOrder/getPendingOrder`,
-        //   {
-        //     data: { goodType: 'DISCOUNT' }
-        //   }
-        // )
-        // console.log(resPadding, 'resPadding')
-        // if (resPadding?.data?.id) {
-        //   state.orderVisiable = true
-        //   state.orderDetail = resPadding.data || {}
-        //   return
-        // }
-
-        // const member: any = state.selectMember
-        // // 判断是否有会员
-        // let startTime = new Date()
-        // // vip类型 VIP:会员 SVIP:SVIP,PERMANENT_SVIP:永久SVIP,NOT_VIP:不是vip
-        // // vip过期类型 VIP:会员 SVIP:SVIP,ALL_VIP:全vip
-        // if (state.tabActive === 'SVIP') {
-        //   startTime = dayjs(
-        //     userInfo.value.userVip.svipEndDate || new Date()
-        //   ).toDate()
-        // } else if (state.tabActive === 'VIP') {
-        //   // 购买Vip时,先有vip的有效时间,如果没有则取SVIP有效时间,都没有默认当前
-        //   startTime = dayjs(
-        //     userInfo.value.userVip.vipEndDate ||
-        //       userInfo.value.userVip.svipEndDate ||
-        //       new Date()
-        //   ).toDate()
-        // } else if (isPermanent.value) {
-        //   Toast('您已是永久SVIP会员')
-        //   return
-        // }
+        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) {
@@ -180,10 +138,22 @@ export default defineComponent({
     }
 
     onMounted(async () => {
-      const { data } = await request.get(
-        `${baseState.platformApi}/memberPriceSettings/getDiscount`
-      )
-      state.discountDetail = data
+      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}>
@@ -223,7 +193,7 @@ export default defineComponent({
                 )}
               </div>
               <div class={styles.member_time}>
-                {userInfo.value.discountCardFlag ? <>有效期至<span>{userInfo.value.discountEndTime}</span></> : '您当前尚未开通畅学卡'}
+                {userInfo.value.discountCardFlag ? <>有效期至<span>{dayjs(userInfo.value.discountEndTime).format('YYYY-MM-DD')}</span></> : '您当前尚未开通畅学卡'}
               </div>
             </div>
           </div>
@@ -265,7 +235,7 @@ export default defineComponent({
 
 
         <Popup
-          v-model:show={state.orderVisiable}
+          v-model:show={state.orderVisible}
           style={{ background: 'transparent' }}
           closeOnClickOverlay={false}
         >

+ 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;

+ 19 - 12
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
       },
@@ -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
             }
@@ -379,7 +384,15 @@ 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="趣纠课收费"
@@ -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

+ 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;
+  }
+}

+ 30 - 13
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,10 +132,17 @@ 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">
             <div style={{ minHeight: this.homeContaiterHeight }}>
@@ -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>
     )
   }

+ 31 - 0
src/student/trade/tradeOrder.ts

@@ -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 {

+ 4 - 0
src/views/order-detail/orderStatus.ts

@@ -262,6 +262,10 @@ export const orderTenantInfos = () => {
       params.bizContent = {
         activityId: item.activityId
       }
+    } else if(item.orderType == 'DISCOUNT') {
+      params.bizContent = item.id
+      params.goodsNum = item.num
+      params.goodNum = item.num
     }
     return params
   })