lex-xin 2 years ago
parent
commit
754d4f1f17
38 changed files with 879 additions and 362 deletions
  1. 2 1
      src/business-components/course-video-item/index.module.less
  2. 3 1
      src/business-components/course-video-item/index.tsx
  3. 15 2
      src/components/col-header/index.tsx
  4. 55 43
      src/helpers/native-message.ts
  5. 4 4
      src/helpers/utils.ts
  6. 8 0
      src/router/routes-student.ts
  7. 35 24
      src/student/layout/auth.tsx
  8. 2 3
      src/student/live-class/index.tsx
  9. 1 1
      src/student/live-class/live-item.module.less
  10. 2 2
      src/student/main.ts
  11. 0 59
      src/student/practice-class/index.module.less
  12. 102 79
      src/student/practice-class/index.tsx
  13. 66 0
      src/student/practice-class/model/all-search.module.less
  14. 114 0
      src/student/practice-class/model/all-search.tsx
  15. 87 40
      src/student/practice-class/practice-item.tsx
  16. 9 9
      src/student/practice-class/practice.ts
  17. 3 0
      src/student/teacher-dependent/components/live.module.less
  18. 69 8
      src/student/teacher-dependent/components/live.tsx
  19. 45 2
      src/student/teacher-dependent/components/practice.tsx
  20. 32 20
      src/student/teacher-dependent/components/single.tsx
  21. 38 2
      src/student/teacher-dependent/components/video.tsx
  22. 0 0
      src/student/teacher-dependent/teacher-elegant.module.less
  23. 8 0
      src/student/teacher-dependent/teacher-elegant.tsx
  24. 3 0
      src/student/teacher-dependent/teacher-home.module.less
  25. 58 12
      src/student/teacher-dependent/teacher-home.tsx
  26. 5 0
      src/styles/index.less
  27. 8 3
      src/teacher/layout/auth.tsx
  28. 2 1
      src/teacher/live-class/create-components/arrange.tsx
  29. 2 1
      src/teacher/live-class/create-components/course-plan.tsx
  30. 21 3
      src/teacher/live-class/create-components/course-start.tsx
  31. 1 0
      src/teacher/live-class/create-components/course.tsx
  32. 1 0
      src/teacher/live-class/create-components/createState.ts
  33. 2 2
      src/teacher/video-class/class-info.tsx
  34. 8 1
      src/views/order-detail/index.tsx
  35. 31 33
      src/views/order-detail/payment/index.tsx
  36. 35 6
      src/views/order-detail/userAuth/index.tsx
  37. 1 0
      src/views/trade/trade-detail.module.less
  38. 1 0
      src/views/trade/trade-detail.tsx

+ 2 - 1
src/business-components/course-video-item/index.module.less

@@ -35,9 +35,10 @@
     .videoTitleText {
       font-size: 15px;
       color: #000;
+      max-width: 210px;
     }
     .videoTitleContent {
-      color: #7A7A7A;
+      color: #7a7a7a;
       line-height: 18px;
     }
   }

+ 3 - 1
src/business-components/course-video-item/index.tsx

@@ -50,7 +50,9 @@ export default defineComponent({
           ),
           title: () => (
             <div class={styles.videoTitle}>
-              <p class={styles.videoTitleText}>{this.detail.title}</p>
+              <p class={[styles.videoTitleText, 'van-ellipsis']}>
+                {this.detail.title}
+              </p>
               <p class={[styles.videoTitleContent, 'van-multi-ellipsis--l2']}>
                 {this.detail.content}
               </p>

+ 15 - 2
src/components/col-header/index.tsx

@@ -13,6 +13,11 @@ export default defineComponent({
       type: Boolean,
       default: false
     },
+    backIconColor: {
+      // 返回按钮颜色
+      type: String as PropType<backIconColor>,
+      default: 'black'
+    },
     isFixed: {
       type: Boolean,
       default: true
@@ -52,17 +57,25 @@ export default defineComponent({
     this.navBarInit()
   },
   unmounted() {
+    console.log(true, 'unmounted')
     // 设置是否显示导航栏 0 显示 1 不显示
     postMessage({ api: 'setBarStatus', content: { status: 1 } })
     // 设置返回按钮颜色
-    // postMessage({ api: 'backIconChange', content: { iconStyle: 'black' as backIconColor } })
+    postMessage({
+      api: 'backIconChange',
+      content: { iconStyle: 'black' as backIconColor }
+    })
   },
   methods: {
     navBarInit() {
       // 设置是否显示导航栏 0 显示 1 不显示
       postMessage({ api: 'setBarStatus', content: { status: 0 } })
       // 设置返回按钮颜色
-      // postMessage({ api: 'backIconChange', content: { iconStyle: 'white' as backIconColor } })
+      console.log(this.backIconColor, 'this.backIconColor')
+      postMessage({
+        api: 'backIconChange',
+        content: { iconStyle: this.backIconColor || 'black' }
+      })
 
       let sNavHeight = sessionStorage.getItem('navHeight')
       let sTitleHeight = sessionStorage.getItem('titleHeight')

+ 55 - 43
src/helpers/native-message.ts

@@ -1,20 +1,20 @@
-import { browser, getRandomKey } from '@/helpers/utils';
+import { browser, getRandomKey } from '@/helpers/utils'
 
 export interface IPostMessage {
-  api: string;
-  content?: any;
+  api: string
+  content?: any
 }
 
 /**
  * 劫持postMessage
  */
 
-const originalPostMessage = window.postMessage;
+const originalPostMessage = window.postMessage
 
 window.postMessage = (message: IPostMessage) => {
   // console.log('通过劫持', message)
-  originalPostMessage(message, '*');
-};
+  originalPostMessage(message, '*')
+}
 
 /**
  *
@@ -24,90 +24,102 @@ window.postMessage = (message: IPostMessage) => {
  *
  */
 
-type CallBack = (evt?: IPostMessage) => void;
+type CallBack = (evt?: IPostMessage) => void
 
 // eslint-disable-next-line @typescript-eslint/no-empty-function
-const loop = () => {};
+const loop = () => {}
 
-const calls: { [key: string]: CallBack | CallBack[] } = {};
+const calls: { [key: string]: CallBack | CallBack[] } = {}
 
-const browserInfo = browser();
+const browserInfo = browser()
 
 if (browserInfo.isApp) {
   window.addEventListener('message', evt => {
     try {
+      console.log(evt, 'message', evt.data)
       const data = evt.data
         ? typeof evt.data === 'object'
           ? evt.data
           : JSON.parse(evt.data)
-        : {};
-      const uuid = data.content?.uuid || data.uuid;
+        : {}
+      const uuid = data.content?.uuid || data.uuid
+      console.log(uuid, data.content, 'uuid')
       try {
         if (data.content) {
-          data.content = JSON.parse(data.content);
+          data.content = JSON.parse(data.content)
         }
       } catch (error) {
         //
       }
       if (data?.content?.uuid) {
-        console.log('data', data);
+        console.log('data', data)
       }
       if (!uuid) {
         const keys = Object.keys(calls).filter(
           key => key.indexOf(data.api) === 0
-        );
+        )
+        console.log(keys, 'keys')
+        console.log(data, 'data')
         for (const key of keys) {
-          const callback = calls[key] || loop;
-          typeof callback === 'function' && callback(data);
+          const callback = calls[key] || loop
+          typeof callback === 'function' && callback(data)
         }
-        return;
+        return
       }
-      const callId = data.content?.uuid || data.uuid || data.api + data.uuid;
-      const callback = calls[callId] || loop;
-      typeof callback === 'function' && callback(data);
+      const callId = data.content?.uuid || data.uuid || data.api + data.uuid
+      const callback = calls[callId] || loop
+      console.log(data, 'data')
+      typeof callback === 'function' && callback(data)
     } catch (error) {
-      console.error('通信消息解析错误', error);
+      console.error('通信消息解析错误', error)
     }
-  });
+  })
 }
 
 const instance: any =
-  (window as any).DAYA || (window as any).webkit?.messageHandlers?.DAYA;
+  (window as any).COLEXIU || (window as any).webkit?.messageHandlers?.COLEXIU
 
 export const postMessage = (data: IPostMessage, callback?: CallBack) => {
   if (browserInfo.isApp) {
-    const uuid = getRandomKey();
-    calls[uuid] = callback || loop;
-    data.content = data.content ? { ...data.content, uuid } : { uuid };
-    instance.postMessage(JSON.stringify(data));
-    console.log('send:', JSON.stringify(data));
+    const uuid = getRandomKey()
+    calls[uuid] = callback || loop
+    data.content = data.content ? { ...data.content, uuid } : { uuid }
+    instance.postMessage(JSON.stringify(data))
+    console.log('send:', JSON.stringify(data))
   }
-};
+}
 
+// export const listenerMessage = (api: string, callback: CallBack) => {
+//   console.log(browserInfo, 'browserInfo', api, 'api', callback)
+//   if (browserInfo.isApp) {
+//     const uuid = api
+//     if (!calls[uuid]) {
+//       calls[uuid] = []
+//     }
+//     ;(calls[uuid] as CallBack[]).push(callback || loop)
+//   }
+// }
 export const listenerMessage = (api: string, callback: CallBack) => {
   if (browserInfo.isApp) {
-    const uuid = api;
-    if (!calls[uuid]) {
-      calls[uuid] = [];
-    }
-    (calls[uuid] as CallBack[]).push(callback || loop);
+    const uuid = api + getRandomKey()
+    calls[uuid] = callback || loop
   }
-};
+}
 
 export const removeListenerMessage = (api: string, callback: CallBack) => {
   if (browserInfo.isApp) {
-    const uuid = api;
+    const uuid = api
     if (Array.isArray(calls[uuid])) {
-      const indexOf = (calls[uuid] as CallBack[]).indexOf(callback);
-      (calls[uuid] as CallBack[]).splice(indexOf, 1);
+      const indexOf = (calls[uuid] as CallBack[]).indexOf(callback)
+      ;(calls[uuid] as CallBack[]).splice(indexOf, 1)
     }
   }
-};
+}
 
 export const promisefiyPostMessage = (
   data: IPostMessage
 ): Promise<IPostMessage | undefined> => {
   return new Promise(resolve => {
-    postMessage(data, res => resolve(res));
-  });
-};
+    postMessage(data, res => resolve(res))
+  })
+}

+ 4 - 4
src/helpers/utils.ts

@@ -12,11 +12,11 @@ export const browser = () => {
     mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
     ios: !!u.match(/Mac OS X/), //ios终端
     // ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
-    android: u.indexOf('DAYAAPPA') > -1 || u.indexOf('Adr') > -1, //android终端
-    iPhone: u.indexOf('DAYAAPPI') > -1, //是否为iPhone或者QQHD浏览器
+    android: u.indexOf('COLEXIUAPPA') > -1 || u.indexOf('Adr') > -1, //android终端
+    iPhone: u.indexOf('COLEXIUAPPI') > -1, //是否为iPhone或者QQHD浏览器
     isApp:
-      u.indexOf('DAYAAPPI') > -1 ||
-      u.indexOf('DAYAAPPA') > -1 ||
+      u.indexOf('COLEXIUAPPI') > -1 ||
+      u.indexOf('COLEXIUAPPA') > -1 ||
       u.indexOf('Adr') > -1,
     iPad: u.indexOf('iPad') > -1, //是否iPad
     webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部

+ 8 - 0
src/router/routes-student.ts

@@ -103,6 +103,14 @@ export default [
         meta: {
           title: '老师主页'
         }
+      },
+      {
+        path: '/teacherElegant',
+        name: 'teacherElegant',
+        component: () => import('@/student/teacher-dependent/teacher-elegant'),
+        meta: {
+          title: '老师风采'
+        }
       }
     ]
   },

+ 35 - 24
src/student/layout/auth.tsx

@@ -1,26 +1,26 @@
-import { defineComponent } from "vue";
-import styles from './auth.module.less';
-import { state, setLogin } from '@/state';
-import { browser, setAuth } from "@/helpers/utils";
-import { postMessage } from "@/helpers/native-message";
-import { RouterView } from "vue-router";
-import { Button, Icon } from "vant";
-import request from "@/helpers/request";
+import { defineComponent } from 'vue'
+import styles from './auth.module.less'
+import { state, setLogin, setLogout, setLoginError } from '@/state'
+import { browser, setAuth } from '@/helpers/utils'
+import { postMessage } from '@/helpers/native-message'
+import { RouterView } from 'vue-router'
+import { Button, Icon } from 'vant'
+import request from '@/helpers/request'
 
 export default defineComponent({
-  name: "Auth",
+  name: 'Auth',
   data() {
     return {
-      loading: false as boolean,
+      loading: false as boolean
     }
   },
   computed: {
     isNeedView() {
-      return state.user.status === 'login' || this.$route.path === '/login';
+      return state.user.status === 'login' || this.$route.path === '/login'
     }
   },
   mounted() {
-    this.setAuth();
+    this.setAuth()
   },
   methods: {
     async setAuth() {
@@ -32,7 +32,7 @@ export default defineComponent({
       if (this.loading) {
         return
       }
-      if ((state.user.status === 'init' || state.user.status === 'error')) {
+      if (state.user.status === 'init' || state.user.status === 'error') {
         this.loading = true
         try {
           let res = await request.get('/api-auth/api/queryUserInfo', {
@@ -41,7 +41,12 @@ export default defineComponent({
           // console.log(res)
           setLogin(res.data)
         } catch (e: any) {
-          // console.log(e)
+          const message = e.message
+          if (message.indexOf('430') !== -1) {
+            setLogout()
+          } else {
+            setLoginError()
+          }
         }
         this.loading = false
       }
@@ -53,8 +58,8 @@ export default defineComponent({
             let route = this.$route
             let query = {
               returnUrl: this.$route.path,
-              ...this.$route.query,
-            } as any;
+              ...this.$route.query
+            } as any
             if (route.meta.isRegister) {
               query.isRegister = route.meta.isRegister
             }
@@ -62,7 +67,7 @@ export default defineComponent({
               path: '/login',
               query: query
             })
-          } catch (error) { }
+          } catch (error) {}
         }
       }
     }
@@ -70,14 +75,20 @@ export default defineComponent({
   render() {
     return (
       <>
-        {state.user.status === 'error' ? <div class={styles.error}>
-          <div class={styles.info}>
-            <Icon name="clear" size="36" color="#ee0a24" />
-            <span>加载失败,请重新尝试</span>
+        {state.user.status === 'error' ? (
+          <div class={styles.error}>
+            <div class={styles.info}>
+              <Icon name="clear" size="36" color="#ee0a24" />
+              <span>加载失败,请重新尝试</span>
+            </div>
+            <Button type="primary" round onClick={this.setAuth}>
+              重新加载
+            </Button>
           </div>
-          <Button type="primary" round onClick={this.setAuth}>重新加载</Button>
-        </div> : this.isNeedView ? <RouterView></RouterView> : null}
+        ) : this.isNeedView ? (
+          <RouterView></RouterView>
+        ) : null}
       </>
     )
   }
-})
+})

+ 2 - 3
src/student/live-class/index.tsx

@@ -58,7 +58,6 @@ export default defineComponent({
       } catch {}
     },
     onDetail(item: any) {
-      console.log(item)
       this.$router.push({
         path: '/liveDetail',
         query: {
@@ -69,7 +68,7 @@ export default defineComponent({
   },
   render() {
     return (
-      <div>
+      <div class={styles.liveClass}>
         <Sticky offsetTop={0}>
           <ColHeader
             class={styles.classHeader}
@@ -92,7 +91,7 @@ export default defineComponent({
             v-model:loading={this.loading}
             finished={this.finished}
             finishedText=" "
-            class={styles.liveList}
+            class={[styles.liveList, 'mb12']}
             onLoad={this.getList}
           >
             {this.list.map((item: any) => (

+ 1 - 1
src/student/live-class/live-item.module.less

@@ -1,6 +1,6 @@
 .liveItem {
   margin: 10px 14px 0;
-  width: auto;
+  width: auto !important;
   border-radius: 8px;
 
   .liCover {

+ 2 - 2
src/student/main.ts

@@ -11,8 +11,8 @@ import '../styles/index.less'
 
 const app = createApp(App)
 
-// import Vconsole from 'vconsole'
-// const vconsole = new Vconsole()
+import Vconsole from 'vconsole'
+const vconsole = new Vconsole()
 
 dayjs.locale('zh-ch')
 app.config.globalProperties.$dayjs = dayjs

+ 0 - 59
src/student/practice-class/index.module.less

@@ -30,65 +30,6 @@
   }
 }
 
-.filterTitle {
-  font-size: 18px;
-  font-weight: 500;
-  color: #000000;
-  line-height: 25px;
-  text-align: center;
-  padding: 20px 0;
-}
-
-.searchResult {
-  padding: 0 16px;
-  overflow: hidden;
-  margin-bottom: 20px;
-  .searchTitle {
-    font-size: 16px;
-    color: #333333;
-    line-height: 22px;
-  }
-}
-
-.radio-group {
-  display: flex;
-  margin-top: 10px;
-  margin-bottom: 20px;
-  .radio:first-child {
-    :global {
-      .van-radio__label {
-        margin-left: 0;
-      }
-    }
-  }
-}
-
-.radio {
-  :global {
-    .van-radio__icon {
-      display: none;
-    }
-    .van-tag--large {
-      width: 80px;
-      height: 32px;
-      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);
-    }
-  }
-}
-
 .btn {
   padding: 5px 14px;
 

+ 102 - 79
src/student/practice-class/index.tsx

@@ -1,22 +1,86 @@
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
 import ColHeader from '@/components/col-header'
-import { Button, Icon, Popup, RadioGroup, Sticky, Radio, Tag } from 'vant'
+import { Button, Icon, Popup, RadioGroup, Sticky, Radio, Tag, List } from 'vant'
 import ColSearch from '@/components/col-search'
 import PracticeItem from './practice-item'
-import { practiceNumType, classPriceType, scoreType } from './practice'
+import request from '@/helpers/request'
+import ColResult from '@/components/col-result'
+import AllSearch from './model/all-search'
 
 export default defineComponent({
   name: 'practiceClass',
   data() {
     return {
       searchStatus: false,
-      chargeType: 0
+
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      tempSort: {
+        starGrade: 'ALL',
+        expTime: 'ALL',
+        subjectPrice: 'ALL'
+      },
+      params: {
+        search: '',
+        sort: '',
+        subjectId: null as any,
+        page: 1,
+        rows: 20
+      }
     }
   },
+  async mounted() {
+    try {
+      const res = await request.get('/api-student/subject/subjectSelect')
+      console.log(res)
+    } catch {}
+    this.getList()
+  },
   methods: {
-    onSearch(_file: any) {
-      console.log(_file)
+    onSearch(_search?: any) {
+      this.params.search = _search
+      this.onSort()
+    },
+    onSort() {
+      const popupParams = this.tempSort
+      let str: any = []
+      for (let i in popupParams) {
+        if (popupParams[i] !== 'ALL') {
+          str.push(`${i} ${popupParams[i]}`)
+        }
+      }
+      this.params.sort = str.join(',')
+      this.params.page = 1
+      this.list = []
+      this.dataShow = true // 判断是否有数据
+      this.loading = false
+      this.finished = false
+      this.searchStatus = false
+      this.getList()
+    },
+    async getList() {
+      try {
+        const res = await request.post(
+          '/api-student/courseSchedule/teacherList',
+          {
+            data: {
+              ...this.params
+            }
+          }
+        )
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {}
     }
   },
   render() {
@@ -27,6 +91,7 @@ export default defineComponent({
             title="陪练课"
             isFixed={false}
             border={false}
+            backIconColor="white"
             background="var(--van-primary)"
             color="#fff"
           />
@@ -78,88 +143,46 @@ export default defineComponent({
           </div>
         </Sticky>
 
-        <div class={styles.practiceList}>
-          {[1, 2, 3].map(item => (
-            <PracticeItem />
-          ))}
-        </div>
+        {this.dataShow ? (
+          <List
+            v-model:loading={this.loading}
+            finished={this.finished}
+            finishedText=" "
+            immediateCheck={false}
+            class={[styles.practiceList, 'mb12']}
+            onLoad={this.getList}
+          >
+            {this.list.map((item: any) => (
+              <PracticeItem
+                item={item}
+                onClick={() => {
+                  this.$router.push({
+                    path: '/teacherHome',
+                    query: {
+                      teacherId: item.teacherId
+                    }
+                  })
+                }}
+              />
+            ))}
+          </List>
+        ) : (
+          <ColResult
+            btnStatus={false}
+            classImgSize="SMALL"
+            tips="暂无陪练老师"
+          />
+        )}
 
         <Popup
           show={this.searchStatus}
           position="bottom"
           round
           closeable
+          safe-area-inset-bottom
           onClose={() => (this.searchStatus = false)}
         >
-          <div class={styles.filterTitle}>全部筛选</div>
-          <div class={styles.searchResult}>
-            <div class={styles.searchTitle}>陪练课数</div>
-            <RadioGroup
-              class={styles['radio-group']}
-              modelValue={this.chargeType}
-              onUpdate:modelValue={val => (this.chargeType = val)}
-            >
-              {Object.keys(practiceNumType).map((item: string) => {
-                const isActive = Number(item) === Number(this.chargeType)
-                const type = isActive ? 'primary' : 'default'
-                return (
-                  <Radio class={styles.radio} name={item}>
-                    <Tag size="large" plain={isActive} type={type} round>
-                      {practiceNumType[item]}
-                    </Tag>
-                  </Radio>
-                )
-              })}
-            </RadioGroup>
-
-            <div class={styles.searchTitle}>课时单价</div>
-            <RadioGroup
-              class={styles['radio-group']}
-              modelValue={this.chargeType}
-              onUpdate:modelValue={val => (this.chargeType = val)}
-            >
-              {Object.keys(practiceNumType).map((item: string) => {
-                const isActive = Number(item) === this.chargeType
-                const type = isActive ? 'primary' : 'default'
-                return (
-                  <Radio class={styles.radio} name={item}>
-                    <Tag size="large" plain={isActive} type={type} round>
-                      {practiceNumType[item]}
-                    </Tag>
-                  </Radio>
-                )
-              })}
-            </RadioGroup>
-
-            <div class={styles.searchTitle}>评分</div>
-            <RadioGroup
-              class={styles['radio-group']}
-              modelValue={this.chargeType}
-              onUpdate:modelValue={val => (this.chargeType = val)}
-            >
-              {Object.keys(practiceNumType).map((item: string) => {
-                const isActive = Number(item) === this.chargeType
-                const type = isActive ? 'primary' : 'default'
-                return (
-                  <Radio class={styles.radio} name={item}>
-                    <Tag size="large" plain={isActive} type={type} round>
-                      {practiceNumType[item]}
-                    </Tag>
-                  </Radio>
-                )
-              })}
-            </RadioGroup>
-          </div>
-          <Sticky offsetBottom={0} position="bottom">
-            <div class="btnGroup btnMore">
-              <Button type="primary" plain round>
-                重置
-              </Button>
-              <Button type="primary" round>
-                确认
-              </Button>
-            </div>
-          </Sticky>
+          <AllSearch v-model={this.tempSort} onSort={this.onSort} />
         </Popup>
       </>
     )

+ 66 - 0
src/student/practice-class/model/all-search.module.less

@@ -0,0 +1,66 @@
+.filterTitle {
+  font-size: 18px;
+  font-weight: 500;
+  color: #000000;
+  line-height: 25px;
+  text-align: center;
+  padding: 20px 0;
+}
+
+.searchResult {
+  padding: 0 16px;
+  overflow: hidden;
+  margin-bottom: 20px;
+  .searchTitle {
+    font-size: 16px;
+    color: #333333;
+    line-height: 22px;
+  }
+}
+
+.radio-group {
+  display: flex;
+  margin-top: 10px;
+  margin-bottom: 20px;
+  .radio:first-child {
+    :global {
+      .van-radio__label {
+        margin-left: 0;
+      }
+    }
+  }
+}
+
+.radio {
+  :global {
+    .van-radio__icon {
+      display: none;
+    }
+    .van-tag--large {
+      width: 80px;
+      height: 32px;
+      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);
+    }
+  }
+}
+
+.btn {
+  padding: 5px 14px;
+
+  & + .btn {
+    margin-left: 10px;
+  }
+}

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

@@ -0,0 +1,114 @@
+import { defineComponent } from 'vue'
+import { practiceNumType, classPriceType, scoreType } from '../practice'
+import styles from './all-search.module.less'
+import { RadioGroup, Radio, Tag, Button } from 'vant'
+
+export default defineComponent({
+  name: 'allSearch',
+  props: {
+    onSort: {
+      type: Function,
+      default: (item: any) => {}
+    }
+  },
+  data() {
+    return {
+      popupParams: {
+        starGrade: 'ALL',
+        expTime: 'ALL',
+        subjectPrice: 'ALL'
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        <div class={styles.filterTitle}>全部筛选</div>
+        <div class={styles.searchResult}>
+          <div class={styles.searchTitle}>陪练课数</div>
+          <RadioGroup
+            class={styles['radio-group']}
+            modelValue={this.popupParams.expTime}
+            onUpdate:modelValue={val => (this.popupParams.expTime = val)}
+          >
+            {Object.keys(practiceNumType).map((item: string) => {
+              const isActive = item === this.popupParams.expTime
+              const type = isActive ? 'primary' : 'default'
+              return (
+                <Radio class={styles.radio} name={item}>
+                  <Tag size="large" plain={isActive} type={type} round>
+                    {practiceNumType[item]}
+                  </Tag>
+                </Radio>
+              )
+            })}
+          </RadioGroup>
+
+          <div class={styles.searchTitle}>课时单价</div>
+          <RadioGroup
+            class={styles['radio-group']}
+            modelValue={this.popupParams.subjectPrice}
+            onUpdate:modelValue={val => (this.popupParams.subjectPrice = val)}
+          >
+            {Object.keys(classPriceType).map((item: string) => {
+              const isActive = item === this.popupParams.subjectPrice
+              const type = isActive ? 'primary' : 'default'
+              return (
+                <Radio class={styles.radio} name={item}>
+                  <Tag size="large" plain={isActive} type={type} round>
+                    {classPriceType[item]}
+                  </Tag>
+                </Radio>
+              )
+            })}
+          </RadioGroup>
+
+          <div class={styles.searchTitle}>评分</div>
+          <RadioGroup
+            class={styles['radio-group']}
+            modelValue={this.popupParams.starGrade}
+            onUpdate:modelValue={val => (this.popupParams.starGrade = val)}
+          >
+            {Object.keys(scoreType).map((item: string) => {
+              const isActive = item === this.popupParams.starGrade
+              const type = isActive ? 'primary' : 'default'
+              return (
+                <Radio class={styles.radio} name={item}>
+                  <Tag size="large" plain={isActive} type={type} round>
+                    {scoreType[item]}
+                  </Tag>
+                </Radio>
+              )
+            })}
+          </RadioGroup>
+        </div>
+        <div class="btnGroup btnMore">
+          <Button
+            type="primary"
+            plain
+            round
+            onClick={() => {
+              this.popupParams = {
+                starGrade: 'ALL',
+                expTime: 'ALL',
+                subjectPrice: 'ALL'
+              }
+              this.onSort(this.popupParams)
+            }}
+          >
+            重置
+          </Button>
+          <Button
+            type="primary"
+            round
+            onClick={() => {
+              this.onSort(this.popupParams)
+            }}
+          >
+            确认
+          </Button>
+        </div>
+      </>
+    )
+  }
+})

+ 87 - 40
src/student/practice-class/practice-item.tsx

@@ -1,52 +1,99 @@
-import { Cell, Icon, Image, Rate } from "vant";
-import { defineComponent } from "vue";
-import styles from './practice-item.module.less';
+import { Cell, Icon, Image, Rate } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './practice-item.module.less'
 
-import iconEdu from './images/icon_edu.png';
-import iconMusicMan from './images/icon_music_man.png';
-import iconNew from './images/icon_new.png';
+import iconEdu from './images/icon_edu.png'
+import iconMusicMan from './images/icon_music_man.png'
+import iconNew from './images/icon_new.png'
+
+import iconTeacher from '@/common/images/icon_teacher.png'
 
 export default defineComponent({
   name: 'practiceItem',
-
+  props: {
+    item: {
+      type: Object,
+      default: {}
+    },
+    onClick: {
+      type: Function,
+      default: (item: any) => {}
+    }
+  },
+  computed: {
+    subjectNameList() {
+      const { configSubject } = this.item
+      return configSubject ? configSubject.split(',') : []
+    }
+  },
   render() {
     return (
-      <Cell center border={false} class={styles.practiceItem}
+      <Cell
+        center
+        border={false}
+        class={styles.practiceItem}
+        onClick={() => {
+          this.onClick(this.item)
+        }}
         v-slots={{
-          icon: () => (<div class={styles.piCoverContainer}>
-            <Image class={styles.piCover} fit="cover" src="https://cdn.jsdelivr.net/npm/@vant/assets/cat.jpeg" />
-            <Image src={iconMusicMan} class={styles.tag} />
-            {/* <Image src={iconNew} class={styles.tag} /> */}
-          </div>),
-          title: () => <div>
-            <div class={[styles.piTitle, 'van-ellipsis']}>
-              <p class={styles.piNameSubject}>
-                <span class={styles.piName}>陪练课</span>
-                <span class={styles.subject}>圆号</span>
-                <span class={styles.subject}>大号</span>
-              </p>
-              <Rate readonly modelValue={3} iconPrefix="iconfont" color="#FFC459" void-icon="star_default" icon="star_active" size={11} />
+          icon: () => (
+            <div class={styles.piCoverContainer}>
+              <Image
+                class={styles.piCover}
+                fit="cover"
+                src={this.item.avatar || iconTeacher}
+              />
+              {/* <Image src={iconMusicMan} class={styles.tag} /> */}
+              {/* <Image src={iconNew} class={styles.tag} /> */}
             </div>
+          ),
+          title: () => (
+            <div>
+              <div class={[styles.piTitle, 'van-ellipsis']}>
+                <p class={styles.piNameSubject}>
+                  <span class={styles.piName}>{this.item.userName}</span>
+                  {/* {this.subjectNameList.map(
+                    (item: string, index: number) =>
+                      index <= 1 && <span class={styles.subject}>{item}</span>
+                  )} */}
+                </p>
+                <Rate
+                  readonly
+                  modelValue={Number(this.item.starGrade)}
+                  iconPrefix="iconfont"
+                  color="#FFC459"
+                  void-icon="star_default"
+                  icon="star_active"
+                  size={11}
+                />
+              </div>
 
-            <div class={styles.piContent}>
-              <p class={styles.edu}>
-                <Icon size={14} name={iconEdu} style={{ paddingRight: '5px' }} />
-                <span>中国音乐学院</span>
-              </p>
-              <p class={styles.courseInfo}>
-                <span class={styles.classNum}>
-                  已上课程
-                  <i>99+</i>
-                  节
-                </span>
-                <span class={styles.priceTime}>
-                  <i>¥40</i>/25分钟
-                </span>
-              </p>
-            </div>
+              <div class={styles.piContent}>
+                {this.item.schoolSubject && (
+                  <p class={styles.edu}>
+                    <Icon
+                      size={14}
+                      name={iconEdu}
+                      style={{ paddingRight: '5px' }}
+                    />
+                    <span>{this.item.schoolSubject}</span>
+                  </p>
+                )}
 
-          </div>
-        }} />
+                <p class={styles.courseInfo}>
+                  <span class={styles.classNum}>
+                    已上课程
+                    <i>{this.item.expTime}</i>节
+                  </span>
+                  <span class={styles.priceTime}>
+                    <i>¥40</i>/{this.item.courseMinutes}分钟
+                  </span>
+                </p>
+              </div>
+            </div>
+          )
+        }}
+      />
     )
   }
-})
+})

+ 9 - 9
src/student/practice-class/practice.ts

@@ -1,17 +1,17 @@
 export const practiceNumType = {
-  0: '不限',
-  1: '最高',
-  2: '最低'
+  ALL: '不限',
+  ASC: '最高',
+  DESC: '最低'
 }
 
 export const classPriceType = {
-  0: '不限',
-  1: '最高',
-  2: '最低'
+  ALL: '不限',
+  ASC: '最高',
+  DESC: '最低'
 }
 
 export const scoreType = {
-  0: '不限',
-  1: '最高',
-  2: '最低'
+  ALL: '不限',
+  ASC: '最高',
+  DESC: '最低'
 }

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

@@ -6,6 +6,7 @@
   width: 105px;
   height: 71px;
   border-radius: 4px;
+  overflow: hidden;
 }
 
 .liContent {
@@ -17,6 +18,7 @@
     line-height: 20px;
     padding-bottom: 8px;
     padding-top: 8px;
+    max-width: 180px;
   }
   .avatar {
     width: 18px;
@@ -33,6 +35,7 @@
     font-size: 13px;
     color: #999999;
     line-height: 18px;
+    flex-wrap: wrap;
   }
 
   .userInfo {

+ 69 - 8
src/student/teacher-dependent/components/live.tsx

@@ -4,6 +4,8 @@ import { defineComponent } from 'vue'
 import styles from './live.module.less'
 
 import iconTimer from '@common/images/icon_timer2.png'
+import request from '@/helpers/request'
+import dayjs from 'dayjs'
 
 export default defineComponent({
   name: 'live',
@@ -14,11 +16,54 @@ export default defineComponent({
       loading: false,
       finished: false,
       params: {
+        groupStatus: 'APPLY',
         page: 1,
         rows: 20
       }
     }
   },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    formatTime(time: string) {
+      let timeStr = dayjs(time || new Date())
+      const weekStr = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
+      console.log(timeStr.day())
+
+      return timeStr.format('YYYY-MM-DD') + `(${weekStr[timeStr.day()]})`
+    },
+    async getList() {
+      try {
+        let params = this.params
+        const res = await request.post(
+          '/api-student/courseGroup/queryPageCourseGroup',
+          {
+            data: {
+              ...params
+            }
+          }
+        )
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {}
+    },
+    onDetail(item: any) {
+      this.$router.push({
+        path: '/liveDetail',
+        query: {
+          groupId: item.courseGroupId
+        }
+      })
+    }
+  },
   render() {
     return (
       <>
@@ -26,25 +71,37 @@ export default defineComponent({
           <List
             class={styles.liveList}
             v-model:loading={this.loading}
+            immediateCheck={false}
             finished={this.finished}
             finishedText="没有更多了"
           >
-            {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(item => (
-              <CellGroup class={styles.liveGroup}>
+            {this.list.map((item: any) => (
+              <CellGroup
+                class={styles.liveGroup}
+                onClick={() => this.onDetail(item)}
+              >
                 <Cell
                   v-slots={{
-                    icon: () => <Image class={styles.liCover} fit="cover" />,
+                    icon: () => (
+                      <Image
+                        class={styles.liCover}
+                        src={item.backgroundPic}
+                        fit="cover"
+                      />
+                    ),
                     title: () => (
                       <div class={styles.liContent}>
                         <div class={[styles.liTitle, 'van-ellipsis']}>
-                          竖笛基础入门直播课
+                          {item.courseGroupName}
                         </div>
                         <div class={styles.liUserInfo}>
                           <div class={[styles.userInfo, 'van-hairline--right']}>
                             <Image class={styles.avatar} fit="cover" />
-                            <p>老师:李老师</p>
+                            <p>老师:{item.teacherName}</p>
                           </div>
-                          <span class={styles.num}>0人已购买</span>
+                          <span class={styles.num}>
+                            {item.studentCount}人购买
+                          </span>
                         </div>
                       </div>
                     )
@@ -66,12 +123,16 @@ export default defineComponent({
                           size="16"
                           style={{ marginRight: '5px' }}
                         />
-                        2022/3月20日(周日)
+                        {this.formatTime(item.salesStartDate)}
                       </span>
                     ),
                     default: () => (
                       <div class={styles.price}>
-                        <span>¥40</span>/40课时
+                        <span>
+                          ¥
+                          {(this as any).$filters.moneyFormat(item.coursePrice)}
+                        </span>
+                        /{item.courseNum}课时
                       </div>
                     )
                   }}

+ 45 - 2
src/student/teacher-dependent/components/practice.tsx

@@ -1,4 +1,7 @@
 import Calendar from '@/business-components/calendar'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import dayjs from 'dayjs'
 import { Cell, CellGroup, Dialog, Stepper, Tag } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './practice.module.less'
@@ -6,11 +9,44 @@ import styles from './practice.module.less'
 export default defineComponent({
   name: 'practice',
   data() {
+    const query = this.$route.query
     return {
-      showSelectList: []
+      teacherId: query.teacherId,
+      showSelectList: [],
+      calendarList: {},
+      selectCourseList: []
     }
   },
+  mounted() {
+    this.getList()
+  },
   methods: {
+    async getList(date?: Date) {
+      try {
+        console.log(state.user.data)
+        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?.id,
+              teacherId: this.teacherId
+            }
+          }
+        )
+        const result = res.data || []
+        let tempObj = {}
+        result.forEach((item: any) => {
+          tempObj[item.date] = item
+        })
+        this.calendarList = tempObj
+      } catch {}
+    },
     onCloseTag(item: any) {
       Dialog.confirm({
         title: '提示',
@@ -48,7 +84,14 @@ export default defineComponent({
         </CellGroup>
 
         <div class={styles.group}>
-          <Calendar />
+          <Calendar
+            selectList={this.selectCourseList}
+            list={this.calendarList}
+            maxDays={5}
+            nextMonth={(date: Date) => this.getList(date)}
+            prevMonth={(date: Date) => this.getList(date)}
+            selectDay={this.onSelectDay}
+          />
         </div>
 
         <Cell

+ 32 - 20
src/student/teacher-dependent/components/single.tsx

@@ -1,5 +1,6 @@
 import SectionDetail from '@/business-components/section-detail'
 import ColVideo from '@/components/col-video'
+import request from '@/helpers/request'
 import { Button, Cell, Icon, Image } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './single.module.less'
@@ -12,7 +13,14 @@ export const getAssetsHomeFile = (fileName: string) => {
 
 export default defineComponent({
   name: 'single',
+  props: {
+    userInfo: {
+      type: Object,
+      default: {}
+    }
+  },
   render() {
+    const userInfo = this.userInfo
     return (
       <div class={styles.single}>
         <SectionDetail
@@ -21,29 +29,33 @@ export default defineComponent({
           size={24}
           border={false}
         >
-          <p class={styles.introduction}>
-            毕业于中央音乐学员长笛专业,师从央音长笛系
-            曾获2016年锦绣杯长笛大赛冠军
-            自2018年起研究长笛启蒙、考级到专业考试教育
-            总结出一套适合各个阶段需要的教学方式所教学员考级通过率100%,专业院校复试率92%
-          </p>
+          <p class={styles.introduction}>{userInfo.introduction}</p>
         </SectionDetail>
 
-        <SectionDetail icon="elegant" title="老师风采" size={24} border={false}>
-          <div class={styles.videoList}>
-            <div class={styles.videoItem}>
-              <Image
-                src="https://daya.ks3-cn-beijing.ksyun.com/202204/T3JggS0.png"
-                fit="cover"
-              />
-              <Icon
-                class={styles['icon-upload']}
-                name={getAssetsHomeFile('icon_video.png')}
-                size={26}
-              />
+        {userInfo.styleVideo && userInfo.styleVideo.length > 0 && (
+          <SectionDetail
+            icon="elegant"
+            title="老师风采"
+            size={24}
+            border={false}
+          >
+            <div class={styles.videoList}>
+              {userInfo.styleVideo.map((item: any) => (
+                <div class={styles.videoItem}>
+                  <Image
+                    src="https://daya.ks3-cn-beijing.ksyun.com/202204/T3JggS0.png"
+                    fit="cover"
+                  />
+                  <Icon
+                    class={styles['icon-upload']}
+                    name={getAssetsHomeFile('icon_video.png')}
+                    size={26}
+                  />
+                </div>
+              ))}
             </div>
-          </div>
-        </SectionDetail>
+          </SectionDetail>
+        )}
 
         <SectionDetail icon="fans" title="粉丝群" size={24} border={false}>
           {[1, 2, 3].map(item => (

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

@@ -1,4 +1,5 @@
 import ColResult from '@/components/col-result'
+import request from '@/helpers/request'
 import VideoItem from '@/student/video-class/video-item'
 import { List } from 'vant'
 import { defineComponent } from 'vue'
@@ -18,6 +19,39 @@ export default defineComponent({
       }
     }
   },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      try {
+        let params = this.params
+        const res = await request.post('/api-student/videoLesson/selectGroup', {
+          data: {
+            ...params
+          }
+        })
+        const result = res.data || {}
+        console.log(result)
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {}
+    },
+    onDetail(item: any) {
+      this.$router.push({
+        path: '/videoDetail',
+        query: {
+          groupId: item.id
+        }
+      })
+    }
+  },
   render() {
     return (
       <>
@@ -26,10 +60,12 @@ export default defineComponent({
             class={styles.videoList}
             v-model:loading={this.loading}
             finished={this.finished}
+            immediateCheck={false}
             finishedText="没有更多了"
+            onLoad={this.getList}
           >
-            {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(item => (
-              <VideoItem />
+            {this.list.map(item => (
+              <VideoItem item={item} onClick={this.onDetail} />
             ))}
           </List>
         ) : (

+ 0 - 0
src/student/teacher-dependent/teacher-elegant.module.less


+ 8 - 0
src/student/teacher-dependent/teacher-elegant.tsx

@@ -0,0 +1,8 @@
+import { defineComponent } from 'vue'
+import styles from './teacher-elegant.module.less'
+
+export default defineComponent({
+  render() {
+    return <>老师风采</>
+  }
+})

+ 3 - 0
src/student/teacher-dependent/teacher-home.module.less

@@ -113,6 +113,9 @@
     font-size: 16px !important;
     color: #333333;
   }
+  .van-button--plain.van-button--primary {
+    background-color: transparent;
+  }
 }
 
 .piNameSubject {

+ 58 - 12
src/student/teacher-dependent/teacher-home.tsx

@@ -1,7 +1,7 @@
 import ColHeader from '@/components/col-header'
 import { defineComponent } from 'vue'
 import styles from './teacher-home.module.less'
-import { Button, Cell, Icon, Image, Rate, Sticky, Tab, Tabs } from 'vant'
+import { Button, Cell, Icon, Image, Rate, Sticky, Tab, Tabs, Toast } from 'vant'
 
 import iconTeacher from '@common/images/icon_teacher.png'
 import Single from './components/single'
@@ -9,6 +9,7 @@ import Practice from './components/practice'
 import Live from './components/live'
 import VideoList from './components/video'
 import Music from './components/music'
+import request from '@/helpers/request'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -19,12 +20,46 @@ export const getAssetsHomeFile = (fileName: string) => {
 export default defineComponent({
   name: 'teacherHome',
   data() {
+    // 先取session中的数据,没有再取参数,默认为老师风采
+    const tabs = sessionStorage.getItem('teacherHomeTabs') || ''
     const query = this.$route.query
     return {
-      tabs: query.tabs || 'single'
+      teacherId: query.teacherId,
+      tabs: tabs || query.tabs || 'single',
+      userInfo: {} as any
+    }
+  },
+  async mounted() {
+    try {
+      const res = await request.get('/api-student/teacher/queryTeacherHome', {
+        params: {
+          userId: this.teacherId
+        }
+      })
+      // const result = res.data
+      this.userInfo = res.data
+      // console.log(result)
+    } catch {}
+  },
+  methods: {
+    async onStart() {
+      // 关注与取消关注
+      try {
+        const star = this.userInfo.isStar ? 0 : 1
+        await request.get('/api-student/teacher/starOrUnStar', {
+          params: {
+            userId: this.teacherId,
+            starStatus: star
+          }
+        })
+        const str = star ? '关注成功' : '已取消关注'
+        this.userInfo.isStar = star
+        let count = star ? this.userInfo.fansNum + 1 : this.userInfo.fansNum - 1
+        this.userInfo.fansNum = count <= 0 ? 0 : count
+        Toast(str)
+      } catch {}
     }
   },
-  mounted() {},
   render() {
     return (
       <div class={styles['teacher-record']}>
@@ -34,6 +69,7 @@ export default defineComponent({
             background="transparent"
             border={false}
             color="#fff"
+            backIconColor="white"
             isFixed={false}
             v-slots={{
               default: () => (
@@ -46,27 +82,33 @@ export default defineComponent({
                         icon: () => (
                           <Image
                             class={styles.userLogo}
-                            src={iconTeacher}
+                            src={this.userInfo.heardUrl || iconTeacher}
                             fit="cover"
                           />
                         )
                       }}
                     >
                       <div class={styles['teacher-info']}>
-                        <div class={styles['teacher-name']}>李老师</div>
+                        <div class={styles['teacher-name']}>
+                          {this.userInfo.username}
+                        </div>
                         <p class={styles.piNameSubject}>
-                          <span class={styles.subject}>圆号</span>
-                          <span class={styles.subject}>大号</span>
+                          <span class={styles.subject}>
+                            {this.userInfo.subjectName}
+                          </span>
                         </p>
                       </div>
                       <div class={styles['teacher-desc']}>
                         <Button
                           type="primary"
                           size="mini"
+                          plain={!!this.userInfo.isStar}
                           style={{ borderRadius: '5px', padding: '5px 10px' }}
+                          onClick={this.onStart}
                         >
-                          <Icon name="plus" />
-                          关注
+                          {!this.userInfo.isStar && <Icon name="plus" />}
+
+                          {this.userInfo.isStar ? '已关注' : '关注'}
                         </Button>
                       </div>
                     </Cell>
@@ -76,16 +118,17 @@ export default defineComponent({
                           iconPrefix="iconfont"
                           color="#FFC459"
                           void-icon="star_default"
+                          modelValue={this.userInfo.starGrade}
                           icon="star_active"
                           size={15}
                         />
                       </div>
                       <div class={styles['teacher-data']}>
                         <div class={styles['teacher-data_item']}>
-                          粉丝 <span>134</span>
+                          粉丝 <span>{this.userInfo.fansNum || 0}</span>
                         </div>
                         <div class={styles['teacher-data_item']}>
-                          已上课时 <span>134</span>
+                          已上课时 <span>{this.userInfo.expTime || 0}</span>
                         </div>
                       </div>
                     </div>
@@ -99,6 +142,9 @@ export default defineComponent({
             background="#f8f9fc"
             lineWidth={20}
             v-model:active={this.tabs}
+            onChange={() => {
+              sessionStorage.setItem('teacherHomeTabs', this.tabs as string)
+            }}
           >
             <Tab title="个人风采" name="single"></Tab>
             <Tab title="陪练课" name="practice"></Tab>
@@ -109,7 +155,7 @@ export default defineComponent({
         </Sticky>
 
         <div class={styles.container}>
-          {this.tabs === 'single' && <Single />}
+          {this.tabs === 'single' && <Single userInfo={this.userInfo} />}
           {this.tabs === 'practice' && <Practice />}
           {this.tabs === 'live' && <Live />}
           {this.tabs === 'video' && <VideoList />}

+ 5 - 0
src/styles/index.less

@@ -129,3 +129,8 @@ body {
   }
   // }
 }
+
+.van-sticky {
+  transform: translateZ(0);
+  -webkit-transform: translateZ(0);
+}

+ 8 - 3
src/teacher/layout/auth.tsx

@@ -1,6 +1,6 @@
 import { defineComponent } from 'vue'
 import styles from './auth.module.less'
-import { state, setLogin, setLoginError } from '@/state'
+import { state, setLogin, setLoginError, setLogout } from '@/state'
 import { browser, setAuth } from '@/helpers/utils'
 import { postMessage } from '@/helpers/native-message'
 import { RouterView } from 'vue-router'
@@ -43,8 +43,13 @@ export default defineComponent({
           // console.log(res)
           setLogin(res.data)
         } catch (e: any) {
-          // console.log(e)
-          // setLoginError()
+          const message = e.message
+          console.log(message)
+          if (message.indexOf('430') !== -1) {
+            setLogout()
+          } else {
+            setLoginError()
+          }
         }
         this.loading = false
       }

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

@@ -59,7 +59,8 @@ export default defineComponent({
           {
             data: {
               ...params,
-              singleCourseMinutes: createState.live.singleCourseMinutes,
+              singleCourseMinutes: createState.live.singleMins,
+              freeCourseMinutes: createState.live.freeMinutes,
               teacherId: state.user.data?.userId
             }
           }

+ 2 - 1
src/teacher/live-class/create-components/course-plan.tsx

@@ -1,6 +1,6 @@
 import { Button, Field, Form, Sticky } from 'vant'
 import { defineComponent } from 'vue'
-import { createState } from './createState'
+import { basePlan, createState } from './createState'
 import styles from './course-plan.module.less'
 import ColField from '@/components/col-field'
 import ColFieldGroup from '@/components/col-field-group'
@@ -73,6 +73,7 @@ export default defineComponent({
               plain
               onClick={() => {
                 createState.active = 1
+                createState.live.coursePlanList = [{ ...basePlan }]
               }}
             >
               上一步

+ 21 - 3
src/teacher/live-class/create-components/course-start.tsx

@@ -43,8 +43,11 @@ export default defineComponent({
       .subtract(1, 'day')
       .toDate()
 
-    createState.live.salesStartDate = dayjs(this.minDate).format('YYYY-MM-DD')
-    createState.live.salesEndDate = dayjs(this.maxDate).format('YYYY-MM-DD')
+    createState.live.salesStartDate =
+      createState.live.salesStartDate ||
+      dayjs(this.minDate).format('YYYY-MM-DD')
+    createState.live.salesEndDate =
+      createState.live.salesEndDate || dayjs(this.maxDate).format('YYYY-MM-DD')
   },
   methods: {
     tabChange(name: number) {
@@ -64,6 +67,14 @@ export default defineComponent({
     onConfirm(val: any) {
       if (this.typeDateTime === 'start') {
         createState.live.salesStartDate = dayjs(val).format('YYYY-MM-DD')
+        if (
+          createState.live.salesEndDate &&
+          dayjs(createState.live.salesStartDate).isAfter(
+            dayjs(createState.live.salesEndDate)
+          )
+        ) {
+          createState.live.salesEndDate = ''
+        }
       } else if (this.typeDateTime === 'end') {
         createState.live.salesEndDate = dayjs(val).format('YYYY-MM-DD')
       }
@@ -86,6 +97,7 @@ export default defineComponent({
               isLink
               placeholder="请选择停售日期"
               onClick={() => {
+                this.minDate = dayjs().toDate()
                 this.currentDate = dayjs(
                   createState.live.salesStartDate
                 ).toDate()
@@ -102,6 +114,7 @@ export default defineComponent({
               readonly
               isLink
               onClick={() => {
+                this.minDate = dayjs(createState.live.salesStartDate).toDate()
                 this.currentDate = dayjs(createState.live.salesEndDate).toDate()
                 this.typeDateTime = 'end'
                 this.dateStatus = true
@@ -213,7 +226,7 @@ export default defineComponent({
               rules={[
                 {
                   required: createState.tabIndex == 2,
-                  message: '请选择图片模板'
+                  message: '请上传自定义模板'
                 }
               ]}
               v-slots={{
@@ -253,6 +266,11 @@ export default defineComponent({
               plain
               onClick={() => {
                 createState.active = 3
+                createState.live.salesStartDate = ''
+                createState.live.salesEndDate = ''
+                createState.live.backgroundPic = ''
+                createState.live.backgroundPicTemplate = ''
+                createState.live.mixStudentNum = null
               }}
             >
               上一步

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

@@ -74,6 +74,7 @@ export default defineComponent({
       createState.live.singleCourseMinutes =
         Number(action.name || 0) + Number(action.freeMinutes || 0)
       createState.live.singleMins = Number(action.name || 0)
+      createState.live.freeMinutes = Number(action.freeMinutes || 0)
     }
   },
   render() {

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

@@ -28,6 +28,7 @@ export const createState = reactive({
     courseNum: null as any,
     singleCourseMinutes: 0,
     singleMins: null as any,
+    freeMinutes: 0,
     coursePrice: null as any,
     salesStartDate: '',
     salesEndDate: '',

+ 2 - 2
src/teacher/video-class/class-info.tsx

@@ -197,7 +197,7 @@ export default defineComponent({
                   required:
                     createState.tabIndex === 1 &&
                     !createState.lessonGroup.lessonCoverUrl,
-                  message: '请选择课程声部'
+                  message: '请选择图片模板'
                 }
               ]}
               v-slots={{
@@ -244,7 +244,7 @@ export default defineComponent({
               rules={[
                 {
                   required: createState.tabIndex == 2,
-                  message: '请选择课程声部'
+                  message: '请上传自定义模板'
                 }
               ]}
               v-slots={{

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

@@ -17,6 +17,7 @@ import UserAuth from './userAuth'
 import iconTips from '@common/images/icon_tips.png'
 import Payment from './payment'
 import ColHeader from '@/components/col-header'
+import { state } from '@/state'
 
 export default defineComponent({
   name: 'order-detail',
@@ -39,13 +40,19 @@ export default defineComponent({
     onAuthSuccess() {
       console.log('auth success')
       this.popupShow = false
+      this.onSubmit() // 实名成功后自动支付
     },
     async onSubmit() {
-      // this.popupShow = true
       if (!this.agreeStatus) {
         Toast('请先阅读并同意《酷乐秀平台服务协议》')
         return
       }
+      const users = state.user.data
+      // 判断是否需要实名认证
+      // if (!users?.realName || !users?.idCard) {
+      //   this.popupShow = true
+      //   return
+      // }
       // return
       let result: any
       if (this.$refs.orderVideo && this.orderType == 'VIDEO') {

+ 31 - 33
src/views/order-detail/payment/index.tsx

@@ -60,18 +60,21 @@ export default defineComponent({
       })
         .then(() => {})
         .catch(async () => {
-          try {
-            await request.post('/api-student/userOrder/orderCancel', {
-              data: {
-                orderNo: this.orderInfo.orderNo
-              }
-            })
-            this.$emit('update:modelValue', false)
-            this.$router.go(-1)
-            this.onBackOut && this.onBackOut()
-          } catch {}
+          this.onCancel()
         })
     },
+    async onCancel() {
+      try {
+        await request.post('/api-student/userOrder/orderCancel', {
+          data: {
+            orderNo: this.orderInfo.orderNo
+          }
+        })
+        this.$emit('update:modelValue', false)
+        this.$router.go(-1)
+        this.onBackOut && this.onBackOut()
+      } catch {}
+    },
     async onSubmit() {
       // 支付...
       // const pt = this.payType
@@ -117,32 +120,27 @@ export default defineComponent({
 
         // 唤起支付时状态
         listenerMessage('paymentOperation', (res: any) => {
+          console.log(res)
           if (res.status === 'success') {
-            Toast.success('支付成功')
-          } else if (res.status === 'cancel') {
-            Toast.fail('支付取消')
-          } else if (res.status === 'fail') {
-            Toast.fail('支付失败')
+            Toast.loading({
+              message: '支付中...',
+              forbidClick: true,
+              duration: 1000,
+              loadingType: 'spinner',
+              onClose: () => {
+                this.$emit('update:modelValue', false)
+                this.$router.replace({
+                  path: '/tradeDetail',
+                  query: {
+                    orderNo: this.orderInfo.orderNo
+                  }
+                })
+              }
+            })
+          } else if (res.status === 'cancel' || res.status === 'fail') {
+            this.onCancel()
           }
         })
-
-        // pay_info
-        // Toast.loading({
-        //   message: '支付中...',
-        //   forbidClick: true,
-        //   duration: 3000,
-        //   loadingType: 'spinner',
-        //   onClose: () => {
-        //     this.$emit('update:modelValue', false)
-        //     this.$router.replace({
-        //       path: '/tradeDetail',
-        //       query: {
-        //         orderNo: this.orderInfo.orderNo
-        //       }
-        //     })
-        //   }
-        // })
-        // this.$emit('update:modelValue', false)
       } catch {}
     }
   },

+ 35 - 6
src/views/order-detail/userAuth/index.tsx

@@ -1,5 +1,8 @@
 import ColField from '@/components/col-field'
 import ColFieldGroup from '@/components/col-field-group'
+import ColHeader from '@/components/col-header'
+import request from '@/helpers/request'
+import { verifyIdCard } from '@/helpers/toolsValidate'
 import { Button, CellGroup, Field, Form, Toast } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
@@ -13,28 +16,54 @@ export default defineComponent({
       default: () => {}
     }
   },
+  data() {
+    return {
+      form: {
+        realName: '',
+        idCardNo: ''
+      }
+    }
+  },
   methods: {
-    onSubmit() {
-      Toast('实名成功')
-      this.onSuccess()
+    async onSubmit() {
+      try {
+        await request.post('/api-auth/user/realNameAuth', {
+          data: {
+            ...this.form
+          }
+        })
+        Toast('实名成功')
+        // this.onSuccess()
+      } catch {}
+      // verifyIdCard
     }
   },
   render() {
     return (
       <Form class={styles.userAuth} onSubmit={this.onSubmit}>
+        <ColHeader title="实名认证" />
         <ColFieldGroup style={{ marginTop: '15px' }}>
           <ColField title="姓名" required>
             <Field
               name="lessonName"
-              maxlength={50}
+              maxlength={20}
+              v-model={this.form.realName}
               placeholder="请输入真实姓名"
-              rules={[{ required: false, message: '请输入真实姓名' }]}
+              rules={[{ required: true, message: '请输入真实姓名' }]}
             />
           </ColField>
           <ColField title="证件号码" required>
             <Field
               name="lessonSubjectName"
-              rules={[{ required: false, message: '请输入身份证号' }]}
+              v-model={this.form.idCardNo}
+              rules={[
+                { required: true, message: '请输入身份证号' },
+                {
+                  pattern:
+                    /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
+                  message: '请输入正确的身份证号'
+                }
+              ]}
               placeholder="请输入身份证号"
             />
           </ColField>

+ 1 - 0
src/views/trade/trade-detail.module.less

@@ -106,6 +106,7 @@
     :global {
       .van-col {
         display: flex;
+        line-height: 1.5;
       }
       .van-col--8 {
         color: #999999;

+ 1 - 0
src/views/trade/trade-detail.tsx

@@ -108,6 +108,7 @@ export default defineComponent({
               background={this.order[this.type].background}
               color="#fff"
               title="交易详情"
+              backIconColor="white"
               border={false}
             />