lex-xin 2 năm trước cách đây
mục cha
commit
887fe0cfd4

+ 8 - 0
src/constant/music.ts

@@ -17,3 +17,11 @@ export const teacherChargeType = {
   2: '是',
   0: '否'
 }
+
+export const goodsType = {
+  LIVE: '直播课',
+  PRACTICE: '陪练课',
+  VIDEO: '视频课',
+  VIP: '开通会员',
+  MUSIC: '单曲点播'
+}

+ 62 - 0
src/student/teacher-dependent/components/practice.module.less

@@ -16,6 +16,68 @@
   }
 }
 
+.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;

+ 320 - 18
src/student/teacher-dependent/components/practice.tsx

@@ -1,4 +1,5 @@
 import ColProtocol from '@/components/col-protocol'
+import Calendar from '@/business-components/calendar'
 import request from '@/helpers/request'
 import { state } from '@/state'
 import dayjs from 'dayjs'
@@ -11,10 +12,11 @@ import {
   Stepper,
   Sticky,
   Tag,
-  Popup
+  Popup,
+  Toast
 } from 'vant'
 import { defineComponent } from 'vue'
-import PracticeCalendar from '../model/practice-calendar'
+import { getWeekCh } from '@/helpers/utils'
 import styles from './practice.module.less'
 
 export default defineComponent({
@@ -29,8 +31,6 @@ export default defineComponent({
     const query = this.$route.query
     return {
       teacherId: query.teacherId,
-      calendarList: {},
-      selectCourseList: [],
       agreeStatus: false,
       teacherSubjectList: [],
       subjectStatus: false,
@@ -40,7 +40,13 @@ export default defineComponent({
         subjectName: '',
         subjectId: 0
       },
-      courseNum: 4
+      courseNum: 4,
+      calendarList: [] as any,
+      selectCourseList: [] as any,
+      coursePlanStatus: false,
+      selectStatus: false,
+      coursePlanList: [] as any,
+      calendarDate: new Date() as Date // 日历当前时间
     }
   },
   async mounted() {
@@ -71,10 +77,216 @@ export default defineComponent({
         item.name = item.subjectName
       })
       this.teacherSubjectList = result
+
+      this.getList()
     } catch {}
   },
+  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: {
-    onSubmit() {}
+    async onSubmit() {
+      if (this.selectCourseList.length <= 0) {
+        Toast('请选择课程时间')
+        return
+      }
+
+      if (!this.agreeStatus) {
+        Toast('请先阅读并同意《酷乐秀平台服务协议》')
+        return
+      }
+
+      if (this.selectCourseList.length < this.courseNum) {
+        this.selectStatus = true
+        return
+      }
+
+      await this._lookCourse()
+    },
+    async getList(date?: Date) {
+      try {
+        let params = {
+          day: dayjs(date || new Date()).format('DD'),
+          month: dayjs(date || new Date()).format('MM'),
+          year: dayjs(date || new Date()).format('YYYY')
+        }
+        let res = await request.post(
+          '/api-student/courseSchedule/createPracticeCourseCalendar',
+          {
+            data: {
+              ...params,
+              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
+      } catch {}
+    },
+    onSelectDay(obj: any) {
+      const result = obj || []
+      let list = [...this.selectCourseList] as any
+      console.log(obj, list)
+      result.forEach((item: any) => {
+        const isExist = list.some(
+          (course: any) => course.startTime === item.startTime
+        )
+        !isExist && list.push({ ...item })
+      })
+      // 去掉不在
+      list.forEach((item: any) => {
+        const isExist = result.some(
+          (course: any) => course.startTime === item.startTime
+        )
+        const index = result.findIndex(
+          (course: any) => course.startTime === item.startTime
+        )
+        !isExist && list.splice(index, 1)
+      })
+      // 对数组进行排序
+      list.sort((first: any, second: any) => {
+        if (first.startTime > second.startTime) return 1
+        if (first.startTime < second.startTime) return -1
+        return 0
+      })
+      console.log(list, 'list')
+      this.selectCourseList = [...list] 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-teacher/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 _unLookCourse() {
+      try {
+        await request.get('/api-teacher/courseGroup/unlockCourseToCache', {
+          params: {
+            teacherId: state.user.data?.userId
+          }
+        })
+        this.selectStatus = false
+        this.coursePlanList.forEach((item: any) => {
+          item.startTime = ''
+          item.endTime = ''
+        })
+      } catch {}
+    },
+    async onReset() {
+      // 是否有锁课状态 或 是锁课类型的
+      if (this.coursePlanStatus || this.selectType === 'enough') {
+        await this._unLookCourse()
+      } else if (this.selectType === 'noEnough') {
+        this.selectStatus = false
+      }
+      this.coursePlanStatus = false
+    },
+    async onSure() {
+      const status = this.coursePlanStatus
+      await this._lookCourse(() => {
+        if (status) {
+          this.selectStatus = false
+        }
+      })
+    },
+    routerTo() {
+      const live = this.live
+      this.$router.push({
+        path: '/orderDetail',
+        query: {
+          orderType: 'LIVE',
+          courseGroupId: live.courseGroupId
+        }
+      })
+    },
+    async cancelPayment(orderNo: string) {
+      try {
+        await request.post('/api-student/userOrder/orderCancel', {
+          data: {
+            orderNo
+          }
+        })
+        // this.routerTo()
+      } catch {}
+    }
   },
   render() {
     return (
@@ -122,10 +334,108 @@ export default defineComponent({
             />
           </CellGroup>
 
-          <PracticeCalendar
-            teacherId={this.teacherId as string}
-            courseNum={this.courseNum}
-          />
+          <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>
+
+          <Cell
+            class={[styles.arrangeCell, 'mb12']}
+            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>
+
+          <div class={styles.protocol}>
+            <ColProtocol
+              v-model={this.agreeStatus}
+              showHeader
+              style={{ paddingLeft: 0, paddingRight: 0 }}
+            />
+          </div>
+
+          <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}
@@ -147,14 +457,6 @@ export default defineComponent({
           />
         </div>
         <Sticky offsetBottom={0} position="bottom">
-          <div class={styles.protocol}>
-            <ColProtocol
-              v-model={this.agreeStatus}
-              showHeader
-              style={{ paddingLeft: 0, paddingRight: 0 }}
-            />
-          </div>
-
           <div
             class={'btnGroup'}
             style={{ background: '#fff', paddingTop: '10px' }}

+ 6 - 106
src/student/teacher-dependent/model/practice-calendar.tsx

@@ -17,15 +17,16 @@ export default defineComponent({
     teacherId: {
       type: [Number, String],
       default: ''
+    },
+    onSubmit: {
+      type: Function,
+      default: (item: any) => {}
     }
   },
   data() {
     return {
       calendarList: [] as any,
-      selectCourseList: [] as any,
-      coursePlanStatus: false,
-      selectStatus: false,
-      coursePlanList: []
+      selectCourseList: [] as any
     }
   },
   computed: {
@@ -43,12 +44,6 @@ export default defineComponent({
           item.end
       })
       return list
-    },
-    selectType() {
-      // 循环次数是否足够
-      return this.selectCourseList.length < this.courseNum
-        ? 'noEnough'
-        : 'enough'
     }
   },
   mounted() {
@@ -123,101 +118,6 @@ export default defineComponent({
     }
   },
   render() {
-    return (
-      <>
-        <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}
-          />
-        </div>
-
-        <Cell
-          class={[styles.arrangeCell, 'mb12']}
-          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>
-      </>
-    )
+    return <></>
   }
 })

+ 2 - 1
src/views/order-detail/index.tsx

@@ -34,6 +34,7 @@ export default defineComponent({
     }
   },
   unmounted() {
+    // 销毁时解绑监听
     orderStatus.orderInfo = {
       orderNo: '',
       actualPrice: 0,
@@ -53,7 +54,7 @@ export default defineComponent({
       }
       const users = state.user.data
       // 判断是否需要实名认证
-      if (!users?.realName || !users?.idCard) {
+      if (!users?.realName || !users?.idCardNo) {
         this.popupShow = true
         return
       }

+ 54 - 0
src/views/order-detail/order-practice/index.module.less

@@ -0,0 +1,54 @@
+.videoOrder {
+  .title {
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+    line-height: 22px;
+  }
+  .titleName {
+    font-size: 16px;
+    color: #333333;
+    line-height: 24px;
+    padding-left: 8px;
+    :global {
+      .van-cell__title {
+        padding-left: 5px;
+      }
+    }
+  }
+  .price {
+    font-size: 16px;
+    font-weight: 500;
+    color: #ff3535;
+    line-height: 20px;
+    i {
+      font-style: normal;
+      font-size: 14px;
+    }
+  }
+  .userLogo {
+    width: 24px;
+    height: 24px;
+    overflow: hidden;
+    border-radius: 50%;
+  }
+  .classItem {
+    font-size: 14px;
+    color: #333333;
+    line-height: 20px;
+    padding-bottom: 10px;
+    .time {
+      padding-bottom: 6px;
+    }
+    p {
+      color: var(--van-primary);
+    }
+  }
+  :global {
+    .van-cell-group {
+      margin-bottom: 10px;
+      border-radius: 8px;
+      overflow: hidden;
+    }
+  }
+}

+ 116 - 0
src/views/order-detail/order-practice/index.tsx

@@ -0,0 +1,116 @@
+import { Cell, CellGroup, Image } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import { orderStatus } from '../orderStatus'
+
+import iconTeacher from '@common/images/icon_teacher.png'
+import request from '@/helpers/request'
+
+export default defineComponent({
+  name: 'OrderPractice',
+  props: {
+    modelValue: {
+      type: Number,
+      default: 0
+    }
+  },
+  computed: {
+    coursePlan() {
+      const video = orderStatus.practiceInfo || {}
+      return video.courseInfo || []
+    }
+  },
+  mounted() {
+    const price = orderStatus.practiceInfo.coursePrice || 0
+    console.log(orderStatus.practiceInfo, 11212)
+    this.$emit('update:modelValue', price)
+  },
+  methods: {
+    async onSubmit() {
+      console.log('video submit')
+      try {
+        const res = await request.post('/api-student/userOrder/executeOrder', {
+          data: {
+            orderName: '陪练课购买',
+            orderDesc: '陪练课购买',
+            orderType: 'VIDEO',
+            actualPrice: orderStatus.practiceInfo.coursePrice || 0,
+            orderInfos: [
+              {
+                goodType: 'VIDEO',
+                goodName: '陪练课购买',
+                bizContent: {
+                  videoLessonGroupId: orderStatus.practiceInfo.courseGroupId,
+                  payMoney: orderStatus.practiceInfo.coursePrice || 0
+                }
+              }
+            ]
+          }
+        })
+        return res.data
+      } catch {
+        return false
+      }
+    }
+  },
+  render() {
+    return (
+      <div class={styles.videoOrder}>
+        <CellGroup border={false}>
+          <Cell titleClass={styles.title} title="课程名称" />
+          <Cell
+            title={orderStatus.practiceInfo.courseGroupName}
+            v-slots={{
+              default: () => (
+                <span class={styles.price}>
+                  <i>¥</i>
+                  {(this as any).$filters.moneyFormat(
+                    orderStatus.practiceInfo.coursePrice
+                  )}
+                </span>
+              )
+            }}
+          />
+        </CellGroup>
+
+        <CellGroup border={false}>
+          <Cell titleClass={styles.title} title="主讲老师" />
+          <Cell
+            class={styles.titleName}
+            title={orderStatus.practiceInfo.teacherName}
+            v-slots={{
+              icon: () => (
+                <Image
+                  class={styles.userLogo}
+                  src={orderStatus.practiceInfo.avatar || iconTeacher}
+                />
+              )
+            }}
+          />
+        </CellGroup>
+
+        {/* <CellGroup border={false}>
+          <Cell
+            titleClass={styles.title}
+            title="上课时间"
+            value={`(共${this.coursePlan.length}课时)`}
+          />
+          <Cell
+            v-slots={{
+              title: () => (
+                <div>
+                  {this.coursePlan.map((item: any) => (
+                    <div class={styles.classItem}>
+                      <div class={styles.time}>{item.createTime}</div>
+                    </div>
+                  ))}
+                </div>
+              )
+            }}
+          />
+        </CellGroup> */}
+      </div>
+      // 视频课
+    )
+  }
+})

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

@@ -4,6 +4,7 @@ export const orderStatus = reactive({
   orderType: '' as orderType, // 购买类型
   liveInfo: {} as any, // 直播购买信息
   videoInfo: {} as any, // 视频购买信息
+  practiceInfo: {} as any, // 视频购买信息
   orderInfo: {
     // 订单信息
     orderNo: '',

+ 1 - 1
src/views/order-detail/userAuth/index.tsx

@@ -36,7 +36,7 @@ export default defineComponent({
         })
         Toast('实名成功')
         state.user.data.realName = this.form.realName
-        state.user.data.idCard = this.form.idCardNo
+        state.user.data.idCardNo = this.form.idCardNo
         setTimeout(() => {
           this.onSuccess()
         }, 500)

+ 10 - 9
src/views/trade/trade-detail.tsx

@@ -2,6 +2,7 @@ import ColHeader from '@/components/col-header'
 import { Cell, CellGroup, Col, Image, Row } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './trade-detail.module.less'
+import { goodsType } from '@/constant/music'
 
 import iconTeacher from '@common/images/icon_teacher.png'
 import request from '@/helpers/request'
@@ -57,15 +58,12 @@ export default defineComponent({
     }
   },
   async mounted() {
-    console.log(this.type, 'this.type')
-    console.log(this.order[this.type].background)
-
     setTimeout(async () => {
       this.loading = true
       this.orderNo && (await this.getOrder())
       this.loading = false
 
-      this.interval()
+      this.type === 'ING' && this.interval()
     }, 0)
   },
   unmounted() {
@@ -91,7 +89,7 @@ export default defineComponent({
         this.result = res.data
         // WAIT_PAY 待支付 PAYING 支付中 PAID 已付款 CLOSE 已关闭 FAIL 支付失败
         this.type = this.formatType(this.result.status)
-        console.log(this.type, this.result.status)
+        this.type !== 'ING' && clearInterval(this.timerOut)
       } catch {}
     },
     formatType(type: orderType) {
@@ -169,16 +167,19 @@ export default defineComponent({
                     ),
                     title: () => (
                       <div class={styles.title}>
-                        <span>购买课程</span>
-                        <span class={styles.desc}>{item.goodName}</span>
+                        <span>{item.goodName}</span>
+                        <span class={styles.desc}>
+                          {goodsType[item.goodType]}
+                        </span>
                       </div>
                     ),
                     default: () => (
                       <div class={styles.content}>
                         <span class={styles.price}>
-                          ¥{(this as any).$filters.moneyFormat(item.goodPrice)}
+                          ¥
+                          {(this as any).$filters.moneyFormat(item.expectPrice)}
                         </span>
-                        <span class={styles.num}>x{item.goodNum}</span>
+                        <span class={styles.num}>x{1}</span>
                       </div>
                     )
                   }}