Browse Source

Merge branch 'master' of http://git.dayaedu.com/lex/colexiu-project

mo 2 years ago
parent
commit
b2915100d4
76 changed files with 10289 additions and 170 deletions
  1. 1 0
      components.d.ts
  2. 8583 1
      package-lock.json
  3. BIN
      src/common/images/icon_teacher.png
  4. BIN
      src/components/col-upload/images/icon_upload.png
  5. 10 0
      src/components/col-upload/index.module.less
  6. 113 0
      src/components/col-upload/index.tsx
  7. 4 4
      src/components/musicLIstItem/index.tsx
  8. 11 0
      src/components/pagination/index.module.less
  9. 178 0
      src/components/pagination/index.tsx
  10. 18 0
      src/helpers/deep-clone.ts
  11. 37 5
      src/router/routes-admin.ts
  12. 4 0
      src/style/index.css
  13. 1 1
      src/views/App.tsx
  14. 0 0
      src/views/role-auth/images/bg_bottom.png
  15. 0 0
      src/views/role-auth/images/bg_center.png
  16. 0 0
      src/views/role-auth/images/bg_left_bottom.png
  17. 0 0
      src/views/role-auth/images/bg_right_center.png
  18. 0 0
      src/views/role-auth/images/bg_top.png
  19. 0 0
      src/views/role-auth/images/icon_music.png
  20. 0 0
      src/views/role-auth/images/icon_teacher_auth.png
  21. 0 0
      src/views/role-auth/images/midi_money.png
  22. 0 0
      src/views/role-auth/images/midi_upload.png
  23. 0 0
      src/views/role-auth/images/music_main.png
  24. 0 0
      src/views/role-auth/images/num1.png
  25. 0 0
      src/views/role-auth/images/num2.png
  26. 0 0
      src/views/role-auth/images/num3.png
  27. 0 0
      src/views/role-auth/images/num4.png
  28. 0 0
      src/views/role-auth/images/o1.png
  29. 0 0
      src/views/role-auth/images/o2.png
  30. 0 0
      src/views/role-auth/images/o3.png
  31. 0 0
      src/views/role-auth/images/o4.png
  32. 0 0
      src/views/role-auth/images/teacher_main.png
  33. 0 0
      src/views/role-auth/musicAuth/index.module.less
  34. 0 0
      src/views/role-auth/musicAuth/index.tsx
  35. 0 0
      src/views/role-auth/teacherAuth/components/auth/index.module.less
  36. 59 0
      src/views/role-auth/teacherAuth/components/auth/index.tsx
  37. 0 0
      src/views/role-auth/teacherAuth/components/base-info/index.module.less
  38. 24 19
      src/views/role-auth/teacherAuth/components/base-info/index.tsx
  39. 33 0
      src/views/role-auth/teacherAuth/components/cert-brief/index.module.less
  40. 120 0
      src/views/role-auth/teacherAuth/components/cert-brief/index.tsx
  41. 0 0
      src/views/role-auth/teacherAuth/components/cert-info/index.module.less
  42. 43 11
      src/views/role-auth/teacherAuth/components/cert-info/index.tsx
  43. 8 0
      src/views/role-auth/teacherAuth/components/edu-information/index.module.less
  44. 151 0
      src/views/role-auth/teacherAuth/components/edu-information/index.tsx
  45. 2 24
      src/views/role-auth/teacherAuth/index.tsx
  46. 3 3
      src/views/role-auth/teacherAuth/teacherState.ts
  47. 0 36
      src/views/roleAuth/teacherAuth/components/auth/index.tsx
  48. 0 66
      src/views/roleAuth/teacherAuth/components/edu-information/index.tsx
  49. BIN
      src/views/user-info/components/user-menu/images/1-active.png
  50. BIN
      src/views/user-info/components/user-menu/images/1.png
  51. BIN
      src/views/user-info/components/user-menu/images/2-active.png
  52. BIN
      src/views/user-info/components/user-menu/images/2.png
  53. BIN
      src/views/user-info/components/user-menu/images/3-active.png
  54. BIN
      src/views/user-info/components/user-menu/images/3.png
  55. BIN
      src/views/user-info/components/user-menu/images/4-active.png
  56. BIN
      src/views/user-info/components/user-menu/images/4.png
  57. BIN
      src/views/user-info/components/user-menu/images/5-active.png
  58. BIN
      src/views/user-info/components/user-menu/images/5.png
  59. BIN
      src/views/user-info/components/user-menu/images/menu_active.png
  60. 49 0
      src/views/user-info/components/user-menu/index.module.less
  61. 65 0
      src/views/user-info/components/user-menu/index.tsx
  62. 0 0
      src/views/user-info/components/users/index.module.less
  63. 34 0
      src/views/user-info/components/users/index.tsx
  64. 0 0
      src/views/user-info/index.module.less
  65. 21 0
      src/views/user-info/index.tsx
  66. 12 0
      src/views/user-info/live-class/index.module.less
  67. 23 0
      src/views/user-info/live-class/index.tsx
  68. 59 0
      src/views/user-info/model/practice-timer/index.module.less
  69. 270 0
      src/views/user-info/model/practice-timer/index.tsx
  70. 16 0
      src/views/user-info/music-class/index.module.less
  71. 53 0
      src/views/user-info/music-class/index.tsx
  72. 11 0
      src/views/user-info/practice-setting/index.module.less
  73. 213 0
      src/views/user-info/practice-setting/index.tsx
  74. 12 0
      src/views/user-info/video-class/index.module.less
  75. 28 0
      src/views/user-info/video-class/index.tsx
  76. 20 0
      yarn.lock

+ 1 - 0
components.d.ts

@@ -5,6 +5,7 @@ import '@vue/runtime-core'
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
+    Pagination: typeof import('./src/components/pagination/index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
   }

File diff suppressed because it is too large
+ 8583 - 1
package-lock.json


BIN
src/common/images/icon_teacher.png


BIN
src/components/col-upload/images/icon_upload.png


+ 10 - 0
src/components/col-upload/index.module.less

@@ -0,0 +1,10 @@
+.uploadSection {
+  width: 156px;
+  height: 106px;
+  background: #f8faf9;
+  border-radius: 4px;
+  border: 1px solid rgba(45, 199, 170, 0.26);
+  font-size: 14px;
+  color: #2dc7aa;
+  line-height: 20px;
+}

+ 113 - 0
src/components/col-upload/index.tsx

@@ -0,0 +1,113 @@
+import { ElUpload } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import iconUpload from './images/icon_upload.png'
+import request from '@/helpers/request'
+
+export default defineComponent({
+  name: 'col-upload',
+  props: {
+    modelValue: {
+      type: String,
+      default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    bucket: {
+      type: String,
+      default: 'daya'
+    },
+    accept: {
+      type: String,
+      default: 'images/*'
+    },
+    tips: {
+      type: String,
+      default: '请上传图片'
+    },
+    extraTips: {
+      type: String,
+      default: '图片最大不能超过5MB'
+    }
+  },
+  data() {
+    return {
+      ossUploadUrl: 'https://ks3-cn-beijing.ksyuncs.com/' + this.bucket,
+      dataObj: {
+        policy: '',
+        signature: '',
+        key: '',
+        KSSAccessKeyId: '',
+        acl: 'public-read',
+        name: ''
+      },
+      fileList: [] as any
+    }
+  },
+  methods: {
+    handleSuccess() {},
+    handleRemove() {},
+    handleChange() {},
+    handleProgress() {},
+    handleError() {},
+    async beforeUpload(file: any) {
+      // beforeUpload
+      try {
+        let fileName = file.name.replaceAll(' ', '_')
+        let key = new Date().getTime() + fileName
+        let obj = {
+          filename: fileName,
+          bucketName: this.bucket,
+          postData: {
+            filename: fileName,
+            acl: 'public-read',
+            key: key,
+            unknowValueField: []
+          }
+        }
+        const res = await request.post('/api-website/getUploadSign', {
+          data: obj
+        })
+        console.log(res)
+      } catch {}
+    },
+    handleExceed() {}
+  },
+  render() {
+    return (
+      <div class={styles.colUpload}>
+        <ElUpload
+          disabled={this.disabled}
+          action={this.ossUploadUrl}
+          data={this.dataObj}
+          onSuccess={this.handleSuccess}
+          onRemove={this.handleRemove}
+          onChange={this.handleChange}
+          onProgress={this.handleProgress}
+          onError={this.handleError}
+          fileList={this.fileList}
+          showFileList={false}
+          accept={this.accept}
+          beforeUpload={this.beforeUpload}
+          onExceed={this.handleExceed}
+          limit={1}
+          ref="uploadRef"
+        >
+          <div
+            class={[
+              styles.uploadSection,
+              'flex items-center flex-col justify-center'
+            ]}
+          >
+            <img src={iconUpload} class="w-8 h-7 mb-3" />
+            <p>{this.tips}</p>
+          </div>
+        </ElUpload>
+
+        <p class="text-3 text-[#999999] leading-6 pt-1">{this.extraTips}</p>
+      </div>
+    )
+  }
+})

+ 4 - 4
src/components/musicLIstItem/index.tsx

@@ -57,10 +57,10 @@ export default defineComponent({
                 </div>
               </div>
             </div>
-            <div class={classes.right}>
-              <div class={classes.touchButton}>点播</div>
-              <img class={classes.arrow} src={arrow} alt="" />
-            </div>
+          </div>
+          <div class={classes.right}>
+            <div class={classes.touchButton}>点播</div>
+            <img class={classes.arrow} src={arrow} alt="" />
           </div>
         </div>
       </>

+ 11 - 0
src/components/pagination/index.module.less

@@ -0,0 +1,11 @@
+.pagination-container {
+  background: #fff;
+  padding: 24px 16px 32px;
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+}
+.pagination-container.hidden {
+  display: none;
+}

+ 178 - 0
src/components/pagination/index.tsx

@@ -0,0 +1,178 @@
+import { ElPagination } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'pagination',
+  props: {
+    total: {
+      required: true,
+      type: Number
+    },
+    page: {
+      type: Number,
+      default: 1
+    },
+    limit: {
+      type: Number,
+      default: 10
+    },
+    pageSizes: {
+      type: Array,
+      default: [10, 20, 30, 50]
+    },
+    layout: {
+      type: String,
+      default: 'total,sizes,prev, pager, next, jumper'
+    },
+    background: {
+      type: Boolean,
+      default: true
+    },
+    autoScroll: {
+      type: Boolean,
+      default: true
+    },
+    hidden: {
+      type: Boolean,
+      default: false
+    },
+    sync: {
+      type: Boolean,
+      default: false
+    },
+    saveKey: {
+      type: String,
+      default: ''
+    }
+  },
+  methods: {
+    handleSizeChange(val: number) {
+      this.$emit('update:page', 1)
+      // this.$emit('pagination', { page: this.page, limit: val })
+      if (this.autoScroll) {
+        scrollTo(0, 800)
+      }
+    },
+    handleCurrentChange(val: number) {
+      // this.$emit('pagination', { page: val, limit: this.limit })
+      if (this.autoScroll) {
+        scrollTo(0, 800)
+      }
+    }
+  },
+  render() {
+    return (
+      <div
+        class={[styles['pagination-container'], this.hidden && styles.hidden]}
+      >
+        <ElPagination
+          v-model:currentPage={this.page}
+          pageSizes={this.pageSizes as any}
+          pageSize={this.limit}
+          background
+          layout={this.layout}
+          total={this.total}
+          onSise-change={this.handleSizeChange}
+          onCurrent-change={this.handleCurrentChange}
+        />
+      </div>
+    )
+  }
+})
+
+// <template>
+
+// </template>
+// <script lang="ts">
+// import { defineComponent, computed } from 'vue'
+
+// export default defineComponent({
+//   name: 'pagination',
+//   props: {
+//     total: {
+//       required: true,
+//       type: Number
+//     },
+//     page: {
+//       type: Number,
+//       default: 1
+//     },
+//     limit: {
+//       type: Number,
+//       default: 10
+//     },
+//     pageSizes: {
+//       type: Array,
+//       default: () => {
+//         return [10, 20, 30, 50]
+//       }
+//     },
+//     layout: {
+//       type: String,
+//       default: 'total,sizes,prev, pager, next, jumper'
+//     },
+//     background: {
+//       type: Boolean,
+//       default: true
+//     },
+//     autoScroll: {
+//       type: Boolean,
+//       default: true
+//     },
+//     hidden: {
+//       type: Boolean,
+//       default: false
+//     },
+//     sync: {
+//       type: Boolean,
+//       default: false
+//     },
+//     saveKey: {
+//       type: String,
+//       default: ''
+//     }
+//   },
+//   setup(props, { emit }) {
+//     let myCurrentPage = computed({
+//       get() {
+//         return props.page
+//       },
+//       set(val) {
+//         emit('update:page', val)
+//       }
+//     })
+
+//     let myLimit = computed({
+//       get() {
+//         return props.limit
+//       },
+//       set(val) {
+//         emit('update:limit', val)
+//       }
+//     })
+
+//     // 赋值回显
+//     const handleSizeChange = (val: any) => {
+//       emit('update:page', 1)
+//       emit('pagination', { page: myCurrentPage, limit: val })
+//       if (props.autoScroll) {
+//         scrollTo(0, 800)
+//       }
+//     }
+//     // 设置当前页
+//     const handleCurrentChange = (val: any) => {
+//       emit('pagination', { page: val, limit: myLimit })
+//       if (props.autoScroll) {
+//         scrollTo(0, 800)
+//       }
+//     }
+//     return {
+//       myCurrentPage,
+//       myLimit,
+//       handleSizeChange,
+//       handleCurrentChange
+//     }
+//   }
+// })
+// </script>

+ 18 - 0
src/helpers/deep-clone.ts

@@ -0,0 +1,18 @@
+// form: https://www.30secondsofcode.org/js/s/deep-clone
+
+const deepClone = obj => {
+  if (obj === null) return null
+  const clone = Object.assign({}, obj)
+  Object.keys(clone).forEach(
+    key =>
+      (clone[key] =
+        typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
+  )
+  if (Array.isArray(obj)) {
+    clone.length = obj.length
+    return Array.from(clone)
+  }
+  return clone
+}
+
+export default deepClone

+ 37 - 5
src/router/routes-admin.ts

@@ -1,8 +1,8 @@
 // import Auth from '@/views/layout/auth'
 
-type metaType = {
-  isRegister: boolean
-}
+// type metaType = {
+//   isRegister: boolean
+// }
 
 export default [
   {
@@ -34,7 +34,7 @@ export default [
   {
     path: '/teacherAuth',
     name: 'teacherAuth',
-    component: () => import('@/views/roleAuth/teacherAuth'),
+    component: () => import('@/views/role-auth/teacherAuth'),
     meta: {
       title: '老师认证'
     }
@@ -42,10 +42,42 @@ export default [
   {
     path: '/musicAuth',
     name: 'musicAuth',
-    component: () => import('@/views/roleAuth/musicAuth'),
+    component: () => import('@/views/role-auth/musicAuth'),
     meta: {
       title: '音乐人认证'
     }
+  },
+  {
+    path: '/userInfo',
+    name: 'userInfo',
+    component: () => import('@/views/user-info/index'),
+    redirect: '/userInfo/practiceSetting',
+    children: [
+      {
+        path: '/userInfo/practiceSetting',
+        name: 'userInfoPracticeSetting',
+        component: () => import('@/views/user-info/practice-setting'),
+        meta: { title: '陪练课', index: 2 }
+      },
+      {
+        path: '/userInfo/liveClass',
+        name: 'userInfoLiveClass',
+        component: () => import('@/views/user-info/live-class'),
+        meta: { title: '直播课', index: 3 }
+      },
+      {
+        path: '/userInfo/videoClass',
+        name: 'userInfoVideoClass',
+        component: () => import('@/views/user-info/video-class'),
+        meta: { title: '视频课', index: 4 }
+      },
+      {
+        path: '/userInfo/musicClass',
+        name: 'userInfoMusicClass',
+        component: () => import('@/views/user-info/music-class'),
+        meta: { title: '我的曲谱', index: 5 }
+      }
+    ]
   }
   // {
   //   path: '/',

+ 4 - 0
src/style/index.css

@@ -16,4 +16,8 @@
 
 body {
   background: #F6F7F8;
+}
+
+.user-shadow {
+  box-shadow: 0px 2px 7px 0px rgba(0, 0, 0, 0.04);
 }

+ 1 - 1
src/views/App.tsx

@@ -41,7 +41,7 @@ export default defineComponent({
     return (
       <>
         <ColHeader />
-        <ElConfigProvider locale={zhCn}>
+        <ElConfigProvider locale={zhCn} message={{ max: 1 }}>
           <RouterView></RouterView>
         </ElConfigProvider>
         <silder></silder>

+ 0 - 0
src/views/roleAuth/images/bg_bottom.png → src/views/role-auth/images/bg_bottom.png


+ 0 - 0
src/views/roleAuth/images/bg_center.png → src/views/role-auth/images/bg_center.png


+ 0 - 0
src/views/roleAuth/images/bg_left_bottom.png → src/views/role-auth/images/bg_left_bottom.png


+ 0 - 0
src/views/roleAuth/images/bg_right_center.png → src/views/role-auth/images/bg_right_center.png


+ 0 - 0
src/views/roleAuth/images/bg_top.png → src/views/role-auth/images/bg_top.png


+ 0 - 0
src/views/roleAuth/images/icon_music.png → src/views/role-auth/images/icon_music.png


+ 0 - 0
src/views/roleAuth/images/icon_teacher_auth.png → src/views/role-auth/images/icon_teacher_auth.png


+ 0 - 0
src/views/roleAuth/images/midi_money.png → src/views/role-auth/images/midi_money.png


+ 0 - 0
src/views/roleAuth/images/midi_upload.png → src/views/role-auth/images/midi_upload.png


+ 0 - 0
src/views/roleAuth/images/music_main.png → src/views/role-auth/images/music_main.png


+ 0 - 0
src/views/roleAuth/images/num1.png → src/views/role-auth/images/num1.png


+ 0 - 0
src/views/roleAuth/images/num2.png → src/views/role-auth/images/num2.png


+ 0 - 0
src/views/roleAuth/images/num3.png → src/views/role-auth/images/num3.png


+ 0 - 0
src/views/roleAuth/images/num4.png → src/views/role-auth/images/num4.png


+ 0 - 0
src/views/roleAuth/images/o1.png → src/views/role-auth/images/o1.png


+ 0 - 0
src/views/roleAuth/images/o2.png → src/views/role-auth/images/o2.png


+ 0 - 0
src/views/roleAuth/images/o3.png → src/views/role-auth/images/o3.png


+ 0 - 0
src/views/roleAuth/images/o4.png → src/views/role-auth/images/o4.png


+ 0 - 0
src/views/roleAuth/images/teacher_main.png → src/views/role-auth/images/teacher_main.png


+ 0 - 0
src/views/roleAuth/musicAuth/index.module.less → src/views/role-auth/musicAuth/index.module.less


+ 0 - 0
src/views/roleAuth/musicAuth/index.tsx → src/views/role-auth/musicAuth/index.tsx


+ 0 - 0
src/views/roleAuth/teacherAuth/components/auth/index.module.less → src/views/role-auth/teacherAuth/components/auth/index.module.less


+ 59 - 0
src/views/role-auth/teacherAuth/components/auth/index.tsx

@@ -0,0 +1,59 @@
+import ColSteps from '@/components/col-steps'
+import request from '@/helpers/request'
+import {
+  ElButton,
+  ElDatePicker,
+  ElForm,
+  ElFormItem,
+  ElInput,
+  ElRadioButton,
+  ElRadioGroup
+} from 'element-plus'
+import { defineComponent } from 'vue'
+import { teacherState } from '../../teacherState'
+import BaseInfo from '../base-info'
+import CertInfo from '../cert-info'
+import EduInformation from '../edu-information'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'auth',
+  data() {
+    return {
+      radio: '1'
+    }
+  },
+  async mounted() {
+    try {
+      if (teacherState.subjectList.length <= 0) {
+        const res = await request.get('/api-website/subject/subjectSelect')
+        teacherState.subjectList = res.data || []
+      }
+
+      const teacherInfo = await request.get(
+        '/api-website/teacherAuthEntryRecord/getLastRecordByUserId'
+      )
+      teacherState.teacherInfo = teacherInfo.data || {}
+      const info = teacherState.teacherInfo
+      teacherState.teacherCert.introduction = info.introduction
+      teacherState.teacherCert.subjectId = info.subjectId
+        ? info.subjectId.split(',')
+        : []
+      teacherState.teacherCert.graduateSchool = info.graduateSchool
+      teacherState.teacherCert.subject = info.subject
+      teacherState.teacherCert.gradCertificate = info.gradCertificate
+      teacherState.teacherCert.degreeCertificate = info.degreeCertificate
+      teacherState.teacherCert.teacherCertificate = info.teacherCertificate
+    } catch {}
+  },
+  render() {
+    return (
+      <div class="pt-12 px-72 pb-24">
+        <ColSteps type="medium" active={teacherState.active} />
+        {teacherState.active === 0 && <CertInfo />}
+        {teacherState.active === 1 && <BaseInfo />}
+        {teacherState.active === 2 && <EduInformation />}
+      </div>
+    )
+  }
+})

+ 0 - 0
src/views/roleAuth/teacherAuth/components/base-info/index.module.less → src/views/role-auth/teacherAuth/components/base-info/index.module.less


+ 24 - 19
src/views/roleAuth/teacherAuth/components/base-info/index.tsx → src/views/role-auth/teacherAuth/components/base-info/index.tsx

@@ -1,4 +1,12 @@
-import { ElButton, ElForm, ElFormItem, ElInput, ElSelect } from 'element-plus'
+import {
+  ElButton,
+  ElForm,
+  ElFormItem,
+  ElInput,
+  ElOption,
+  ElOptionGroup,
+  ElSelect
+} from 'element-plus'
 import { defineComponent } from 'vue'
 import { teacherState } from '../../teacherState'
 import styles from './index.module.less'
@@ -6,6 +14,7 @@ import styles from './index.module.less'
 export default defineComponent({
   name: 'auth',
   render() {
+    console.log(teacherState.subjectList)
     return (
       <ElForm
         class={[styles.form, 'mx-4 mt-7']}
@@ -25,27 +34,23 @@ export default defineComponent({
             }
           ]}
         >
-          {/* <ElInput
+          <ElSelect
+            multiple
+            filterable
             v-model={teacherState.teacherCert.subjectId}
             placeholder="请选择可教授声部"
-          /> */}
-          <ElSelect multiple filterable>
-            {/* <ElSelect */}
+            class="w-full"
+            multipleLimit={5}
+          >
+            {teacherState.subjectList.map((group: any) => (
+              <ElOptionGroup key={group.id} label={group.name}>
+                {group.subjects &&
+                  group.subjects.map((item: any) => (
+                    <ElOption key={item.id} value={item.id} label={item.name} />
+                  ))}
+              </ElOptionGroup>
+            ))}
           </ElSelect>
-          {/*       <el-select v-model="value" placeholder="Select">
-    <el-option-group
-      v-for="group in options"
-      :key="group.label"
-      :label="group.label"
-    >
-      <el-option
-        v-for="item in group.options"
-        :key="item.value"
-        :label="item.label"
-        :value="item.value"
-      />
-    </el-option-group>
-  </el-select> */}
         </ElFormItem>
         <ElFormItem label="个人简介" labelWidth={'170px'}>
           <ElInput

+ 33 - 0
src/views/role-auth/teacherAuth/components/cert-brief/index.module.less

@@ -0,0 +1,33 @@
+.musicAuth {
+  background: url('../../../images/bg_top.png') no-repeat top left,
+    url('../../../images/bg_left_bottom.png') no-repeat left bottom,
+    url('../../../images/bg_right_center.png') no-repeat right 70%,
+    url('../../../images/teacher_main.png') no-repeat top right;
+  background-size: 212px 126px, 178px 172px, 171px 230px, 682px 458px;
+  background-color: #fff;
+  .txt {
+    position: relative;
+    z-index: 1;
+    @apply pb-5;
+    &::after {
+      content: '';
+      display: block;
+      width: 100%;
+      height: 13px;
+      background: #cbfdd5;
+      position: absolute;
+      bottom: 15px;
+      left: 0;
+      z-index: -1;
+      opacity: 0.63;
+      @apply rounded-xl;
+    }
+  }
+
+  .col {
+    background: #ffffff;
+    box-shadow: 0px 10px 36px 0px rgba(0, 0, 0, 0.13);
+    border-radius: 10px;
+    min-height: 197px;
+  }
+}

+ 120 - 0
src/views/role-auth/teacherAuth/components/cert-brief/index.tsx

@@ -0,0 +1,120 @@
+import { state } from '@/state'
+import { ElButton, ElCol, ElRow } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `../../../images/${fileName}`
+  const modules = import.meta.globEager('../../../images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'cert-info',
+  computed: {
+    auth() {
+      // 音乐人审核状态 UNPAALY、未申请 DOING、审核中 PASS、通过 UNPASS、不通过,可用值:UNPAALY,DOING,PASS,UNPASS
+      let auth = state.user.data?.entryStatus
+      let obj = {
+        text: '立即认证',
+        status: false
+      }
+      switch (auth) {
+        case 'DOING':
+          obj.text = '审核中'
+          obj.status = true
+          break
+        case 'PASS':
+          obj.text = '已认证'
+          obj.status = true
+      }
+      return obj
+    }
+  },
+  data() {
+    return {
+      list: [
+        {
+          logo: getAssetsHomeFile('o1.png'),
+          num: getAssetsHomeFile('num1.png'),
+          title: '线上授课',
+          desc: '认证成为酷乐秀老师后,可设置您的空闲时间为平台中的求学者进行1对1的线上课程指导。'
+        },
+        {
+          logo: getAssetsHomeFile('o2.png'),
+          num: getAssetsHomeFile('num2.png'),
+          title: '个人风采展示',
+          desc: '可发布自己的专业经历、获奖记录及音视频资料对求学者展示,让学员更加深入的了解您的专业技能,从而提高约课率。'
+        },
+        {
+          logo: getAssetsHomeFile('o3.png'),
+          num: getAssetsHomeFile('num3.png'),
+          title: '曲谱上传',
+          desc: '可上传您制作的MIDI乐谱为求学者提供学习曲目的途径,并从中获得收益。'
+        },
+        {
+          logo: getAssetsHomeFile('o4.png'),
+          num: getAssetsHomeFile('num4.png'),
+          title: '收益提现',
+          desc: '在您授课及上传曲谱销售后,经过平台核算,将您获得的收益发放至您的个人账户下,您可随时将自己获得的收益提现。'
+        }
+      ]
+    }
+  },
+  render() {
+    return (
+      <div class={[styles.musicAuth, 'px-[138px]']}>
+        <div class="w-[388px] pt-24 pb-28">
+          <div class="text-[28px] font-semibold pb-5">酷乐秀基本介绍</div>
+          <p class={[styles.txt]}>
+            酷乐秀是一款为器乐学习者提供智能陪练及线上授课撮
+          </p>
+          <p class={[styles.txt]}>
+            合的乐器教学平台,器乐老师可通过自身的专业知识为
+          </p>
+          <p class={[styles.txt, 'inline-block']}>
+            自己带来<span class="font-medium">授课及曲谱销售收益</span>。
+          </p>
+        </div>
+        <div class="pb-20">
+          <h2 class="text-2xl pb-11 text-center font-semibold">认证权益</h2>
+
+          <ElRow class="mb-24" gutter={28}>
+            {this.list.map((item: any) => (
+              <ElCol span={6} class="pr-3 !flex">
+                <div class={[styles.col, 'flex items-center flex-col p-4']}>
+                  <img class="w-[94px] h-[87px]" src={item.logo} />
+
+                  <div class="flex items-center font-[18px] font-medium pb-3 pt-5">
+                    <img class="w-[30px] h-[22px] mr-1" src={item.num} />
+                    {item.title}
+                  </div>
+                  <p class="text-[14px] text-[#666] leading-6 text-justify">
+                    {item.desc}
+                  </p>
+                </div>
+              </ElCol>
+            ))}
+          </ElRow>
+
+          <h2 class="text-2xl pb-10 text-center font-semibold">
+            酷乐秀欢迎您的加入
+          </h2>
+
+          <p class="text-lg text-center mb-10">
+            在艺术的殿堂中,为他人照亮前进的道路,用自己的经验和点拨,传播艺术的种子,获取硕果。
+          </p>
+
+          <div class="text-center">
+            <ElButton
+              type="primary"
+              class="rounded w-40 !h-[38px]"
+              disabled={this.auth.status}
+            >
+              {this.auth.text}
+            </ElButton>
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 0 - 0
src/views/roleAuth/teacherAuth/components/cert-info/index.module.less → src/views/role-auth/teacherAuth/components/cert-info/index.module.less


+ 43 - 11
src/views/roleAuth/teacherAuth/components/cert-info/index.tsx → src/views/role-auth/teacherAuth/components/cert-info/index.tsx

@@ -1,4 +1,6 @@
+import request from '@/helpers/request'
 import { checkIDCard } from '@/helpers/validate'
+import { state } from '@/state'
 import {
   ElButton,
   ElRadioGroup,
@@ -8,7 +10,8 @@ import {
   ElDatePicker,
   ElRadioButton,
   ElCheckbox,
-  ElLink
+  ElLink,
+  ElMessage
 } from 'element-plus'
 import { defineComponent } from 'vue'
 import { teacherState } from '../../teacherState'
@@ -27,6 +30,13 @@ export default defineComponent({
       agreeStatus: false
     }
   },
+  computed: {
+    userAuth() {
+      // 判断是否实名过
+      const users = state.user.data || {}
+      return !!(users.idCardNo && users.realName)
+    }
+  },
   methods: {
     onIdCardValidate() {
       const idCardNo = teacherState.teacherCert.idCardNo
@@ -64,12 +74,28 @@ export default defineComponent({
       return sex
     },
     onSubmit() {
-      ;(this as any).$refs.certForm.validate(async (_: boolean) => {
-        console.log(_)
-        if (_) {
-          teacherState.active = 1
-        }
-      })
+      try {
+        ;(this as any).$refs.certForm.validate(async (_: boolean) => {
+          console.log(_)
+          if (_) {
+            if (!this.agreeStatus) {
+              return ElMessage.error('请阅读并同意协议')
+            }
+            if (!this.userAuth) {
+              const realName = teacherState.teacherCert.realName
+              const idCardNo = teacherState.teacherCert.idCardNo
+              await request.post('/api-auth/user/realNameAuth', {
+                data: {
+                  realName,
+                  idCardNo,
+                  save: true
+                }
+              })
+            }
+            teacherState.active = 1
+          }
+        })
+      } catch {}
     }
   },
   render() {
@@ -167,10 +193,16 @@ export default defineComponent({
                   this.agreeStatus = !this.agreeStatus
                 }}
               >
-                <ElCheckbox
-                  v-model={this.agreeStatus}
-                  class="!mr-2 rounded-full overflow-hidden"
-                />
+                <div
+                  onClick={(e: any) => {
+                    e.stopPropagation()
+                  }}
+                >
+                  <ElCheckbox
+                    v-model={this.agreeStatus}
+                    class="!mr-2 rounded-full overflow-hidden"
+                  />
+                </div>
                 我已阅读并同意
                 <a
                   href="https://dev.colexiu.com/student/#/registerProtocol"

+ 8 - 0
src/views/roleAuth/teacherAuth/components/edu-information/index.module.less → src/views/role-auth/teacherAuth/components/edu-information/index.module.less

@@ -15,5 +15,13 @@
     .el-checkbox__inner {
       @apply rounded-full overflow-hidden;
     }
+
+    .el-dialog {
+      --el-dialog-width: 379px !important;
+    }
+    .el-dialog__header,
+    .el-dialog__body {
+      padding: 0;
+    }
   }
 }

+ 151 - 0
src/views/role-auth/teacherAuth/components/edu-information/index.tsx

@@ -0,0 +1,151 @@
+import {
+  ElButton,
+  ElDialog,
+  ElForm,
+  ElFormItem,
+  ElInput,
+  ElMessage
+} from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import { teacherState } from '../../teacherState'
+import ColUpload from '@/components/col-upload'
+import request from '@/helpers/request'
+import deepClone from '@/helpers/deep-clone'
+import iconTeacherAuth from '../../../images/icon_teacher_auth.png'
+
+export default defineComponent({
+  name: 'auth',
+  data() {
+    return {
+      labelWidth: '140px',
+      loading: false,
+      popupStatus: true
+    }
+  },
+  methods: {
+    onSubmit() {
+      try {
+        ;(this as any).$refs['form'].validate(async (_: boolean) => {
+          if (_) {
+            this.loading = true
+            const params = deepClone(teacherState.teacherCert)
+            params.subjectId = params.subjectId.join(',')
+            await request.post('/api-website/teacherAuthEntryRecord/doApply', {
+              data: params
+            })
+            ElMessage.success('提交成功')
+            this.loading = false
+          }
+        })
+      } catch {
+        this.loading = false
+      }
+    }
+  },
+  render() {
+    return (
+      <ElForm
+        class={[styles.form, 'mx-4 mt-7']}
+        ref="form"
+        size="large"
+        model={teacherState.teacherCert}
+        labelPosition="left"
+      >
+        <ElFormItem
+          labelWidth={this.labelWidth}
+          label="毕业院校(必填)"
+          prop="graduateSchool"
+          rules={[
+            {
+              required: true,
+              message: '请输入您的毕业院校'
+            }
+          ]}
+        >
+          <ElInput
+            v-model={teacherState.teacherCert.graduateSchool}
+            placeholder="请输入您的毕业院校"
+          />
+        </ElFormItem>
+        <ElFormItem labelWidth={this.labelWidth} label="专业(选填)">
+          <ElInput
+            v-model={teacherState.teacherCert.subject}
+            placeholder="请输入您的专业名称"
+          />
+        </ElFormItem>
+        <ElFormItem labelWidth={this.labelWidth} label="毕业证书(选填)">
+          <ColUpload
+            modelValue={teacherState.teacherCert.gradCertificate}
+            accept=".png, .jpg"
+          />
+        </ElFormItem>
+        <ElFormItem labelWidth={this.labelWidth} label="学位证书(选填)">
+          <ElInput
+            v-model={teacherState.teacherCert.degreeCertificate}
+            placeholder="请输入您的毕业院校"
+          />
+        </ElFormItem>
+        <ElFormItem labelWidth={this.labelWidth} label="教师资格证(选填)">
+          <ElInput
+            v-model={teacherState.teacherCert.teacherCertificate}
+            placeholder="请输入您的毕业院校"
+          />
+        </ElFormItem>
+
+        <ElFormItem>
+          <div class="text-center w-full">
+            <ElButton
+              class="!w-40 !h-[38px]"
+              onClick={() => {
+                teacherState.active = 1
+              }}
+            >
+              上一步
+            </ElButton>
+            <ElButton
+              type="primary"
+              class="!w-40 !h-[38px]"
+              onClick={this.onSubmit}
+              loading={this.loading}
+            >
+              提交审核
+            </ElButton>
+          </div>
+        </ElFormItem>
+
+        <ElDialog
+          modelValue={this.popupStatus}
+          onUpdate:modelValue={val => (this.popupStatus = val)}
+          closeOnClickModal={false}
+          closeOnPressEscape={false}
+          showClose
+        >
+          <div class="p-8">
+            <img src={iconTeacherAuth} />
+            <p class="text-center text-[#666] text-base -m-1 pb-6">
+              感谢您的教学热情,小酷将在24小时内
+              <br />
+              完成审核,请留意APP消息及短信获取
+              <br />
+              审核结果。
+            </p>
+
+            <ElButton
+              type="primary"
+              class="w-full rounded-sm"
+              style={{ height: '38px' }}
+              onClick={() => {
+                this.popupStatus = false
+                teacherState.active = 0
+                teacherState.authStatus = false
+              }}
+            >
+              我知道了
+            </ElButton>
+          </div>
+        </ElDialog>
+      </ElForm>
+    )
+  }
+})

+ 2 - 24
src/views/roleAuth/teacherAuth/index.tsx → src/views/role-auth/teacherAuth/index.tsx

@@ -1,9 +1,7 @@
-import request from '@/helpers/request'
 import { state } from '@/state'
 import { defineComponent } from 'vue'
 import Auth from './components/auth'
-import CertInfo from './components/cert-info'
-import styles from './index.module.less'
+import CertBrief from './components/cert-brief'
 import { teacherState } from './teacherState'
 
 export const getAssetsHomeFile = (fileName: string) => {
@@ -29,24 +27,6 @@ export default defineComponent({
         teacherState.active = teacherState.authStatus ? 4 : 1
         return
       }
-
-      // if (teacherState.subjectList.length <= 0) {
-      //   const res = await request.get('/api-website/subject/subjectSelect')
-      //   teacherState.subjectList = res.data || []
-      // }
-
-      const teacherInfo = await request.get(
-        '/api-website/teacherAuthEntryRecord/getLastRecordByUserId'
-      )
-      teacherState.teacherInfo = teacherInfo.data || {}
-      const info = teacherState.teacherInfo
-      teacherState.teacherCert.introduction = info.introduction
-      teacherState.teacherCert.subjectId = info.subjectId
-      teacherState.teacherCert.graduateSchool = info.graduateSchool
-      teacherState.teacherCert.subject = info.subject
-      teacherState.teacherCert.gradCertificate = info.gradCertificate
-      teacherState.teacherCert.degreeCertificate = info.degreeCertificate
-      teacherState.teacherCert.teacherCertificate = info.teacherCertificate
     } catch {
       //
     }
@@ -64,9 +44,7 @@ export default defineComponent({
   render() {
     return (
       <div class="w-[1200px] mt-[72px] mb-[60px] bg-white min-h-full m-auto text-[#333]">
-        {/* {!teacherState.authStatus ? <CertInfo /> : <div>2323</div>} */}
-
-        <Auth />
+        {!teacherState.authStatus ? <CertBrief /> : <Auth />}
       </div>
     )
   }

+ 3 - 3
src/views/roleAuth/teacherAuth/teacherState.ts → src/views/role-auth/teacherAuth/teacherState.ts

@@ -14,8 +14,8 @@ export const teacherState = reactive({
     introduction: '',
     graduateSchool: null,
     subject: null,
-    gradCertificate: null,
-    degreeCertificate: null,
-    teacherCertificate: null
+    gradCertificate: '',
+    degreeCertificate: '',
+    teacherCertificate: ''
   }
 })

+ 0 - 36
src/views/roleAuth/teacherAuth/components/auth/index.tsx

@@ -1,36 +0,0 @@
-import ColSteps from '@/components/col-steps'
-import {
-  ElButton,
-  ElDatePicker,
-  ElForm,
-  ElFormItem,
-  ElInput,
-  ElRadioButton,
-  ElRadioGroup
-} from 'element-plus'
-import { defineComponent } from 'vue'
-import { teacherState } from '../../teacherState'
-import BaseInfo from '../base-info'
-import CertInfo from '../cert-info'
-import EduInformation from '../edu-information'
-import styles from './index.module.less'
-
-export default defineComponent({
-  name: 'auth',
-  data() {
-    return {
-      radio: '1'
-    }
-  },
-  render() {
-    return (
-      <div class="pt-12 px-72 pb-24">
-        <ColSteps type="medium" active={teacherState.active} />
-
-        {teacherState.active === 0 && <CertInfo />}
-        {teacherState.active === 1 && <BaseInfo />}
-        {teacherState.active === 2 && <EduInformation />}
-      </div>
-    )
-  }
-})

+ 0 - 66
src/views/roleAuth/teacherAuth/components/edu-information/index.tsx

@@ -1,66 +0,0 @@
-import { ElButton, ElForm, ElFormItem, ElInput } from 'element-plus'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-import { teacherState } from '../../teacherState'
-
-export default defineComponent({
-  name: 'auth',
-  data() {
-    return {
-      labelWidth: '140px'
-    }
-  },
-  render() {
-    return (
-      <ElForm
-        class={[styles.form, 'mx-4 mt-7']}
-        ref="form"
-        size="large"
-        model={teacherState.teacherCert}
-        labelPosition="left"
-      >
-        <ElFormItem labelWidth={this.labelWidth} label="毕业院校(必填)">
-          <ElInput placeholder="请输入您的毕业院校" />
-        </ElFormItem>
-        <ElFormItem labelWidth={this.labelWidth} label="专业(选填)">
-          <ElInput placeholder="请输入您的专业名称" />
-        </ElFormItem>
-        <ElFormItem labelWidth={this.labelWidth} label="毕业证书(选填)">
-          <ElInput placeholder="请输入您的毕业院校" />
-        </ElFormItem>
-        <ElFormItem labelWidth={this.labelWidth} label="学位证书(选填)">
-          <ElInput placeholder="请输入您的毕业院校" />
-        </ElFormItem>
-        <ElFormItem labelWidth={this.labelWidth} label="教师资格证(选填)">
-          <ElInput placeholder="请输入您的毕业院校" />
-        </ElFormItem>
-
-        <ElFormItem>
-          <div class="text-center w-full">
-            <ElButton
-              class="!w-40 !h-[38px]"
-              onClick={() => {
-                teacherState.active = 1
-              }}
-            >
-              上一步
-            </ElButton>
-            <ElButton
-              type="primary"
-              class="!w-40 !h-[38px]"
-              onClick={() => {
-                ;(this as any).$refs['form'].validate(async (_: boolean) => {
-                  if (_) {
-                    // teacherState.active = 2
-                  }
-                })
-              }}
-            >
-              提交审核
-            </ElButton>
-          </div>
-        </ElFormItem>
-      </ElForm>
-    )
-  }
-})

BIN
src/views/user-info/components/user-menu/images/1-active.png


BIN
src/views/user-info/components/user-menu/images/1.png


BIN
src/views/user-info/components/user-menu/images/2-active.png


BIN
src/views/user-info/components/user-menu/images/2.png


BIN
src/views/user-info/components/user-menu/images/3-active.png


BIN
src/views/user-info/components/user-menu/images/3.png


BIN
src/views/user-info/components/user-menu/images/4-active.png


BIN
src/views/user-info/components/user-menu/images/4.png


BIN
src/views/user-info/components/user-menu/images/5-active.png


BIN
src/views/user-info/components/user-menu/images/5.png


BIN
src/views/user-info/components/user-menu/images/menu_active.png


+ 49 - 0
src/views/user-info/components/user-menu/index.module.less

@@ -0,0 +1,49 @@
+.menuItem {
+  .icon1 {
+    background: url('./images/1.png') no-repeat center;
+    background-size: contain;
+  }
+  .icon2 {
+    background: url('./images/2.png') no-repeat center;
+    background-size: contain;
+  }
+  .icon3 {
+    background: url('./images/3.png') no-repeat center;
+    background-size: contain;
+  }
+  .icon4 {
+    background: url('./images/4.png') no-repeat center;
+    background-size: contain;
+  }
+  .icon5 {
+    background: url('./images/5.png') no-repeat center;
+    background-size: contain;
+  }
+  &.active,
+  &:hover {
+    background: url('./images/menu_active.png') no-repeat left center;
+    background-size: contain;
+    @apply text-white;
+
+    .icon1 {
+      background: url('./images/1-active.png') no-repeat center;
+      background-size: contain;
+    }
+    .icon2 {
+      background: url('./images/2-active.png') no-repeat center;
+      background-size: contain;
+    }
+    .icon3 {
+      background: url('./images/3-active.png') no-repeat center;
+      background-size: contain;
+    }
+    .icon4 {
+      background: url('./images/4-active.png') no-repeat center;
+      background-size: contain;
+    }
+    .icon5 {
+      background: url('./images/5-active.png') no-repeat center;
+      background-size: contain;
+    }
+  }
+}

+ 65 - 0
src/views/user-info/components/user-menu/index.tsx

@@ -0,0 +1,65 @@
+import { ElLink } from 'element-plus'
+import { userInfo } from 'os'
+import { defineComponent } from 'vue'
+import { RouterLink } from 'vue-router'
+import styles from './index.module.less'
+
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `./images/${fileName}`
+  const modules = import.meta.globEager('./images/*')
+  return modules[path].default
+}
+// 个人中心默认地址
+const defaultRoute = 'userInfo'
+
+export default defineComponent({
+  name: 'user-menu',
+  data() {
+    return {}
+  },
+  computed: {
+    activeRoute() {
+      return this.$route.name
+    },
+    menuList() {
+      const hasRoute = this.$router.hasRoute(defaultRoute)
+      // 判断是否有个人中心的菜单
+      if (!hasRoute) {
+        return
+      }
+      const allRouter = this.$router.getRoutes()
+      const userInfoRouter = allRouter.find(item => item.name === defaultRoute)
+      const list: any = []
+      userInfoRouter?.children.forEach((item: any) => {
+        list.push(item)
+      })
+      return list
+    }
+  },
+  mounted() {
+    // console.log(this.$router.getRoutes(), this.$router.currentRoute.getRoutes())
+    // console.log(this.$router)
+    // const currentRoute = this.$router.currentRoute
+    // console.log(currentRoute, currentRoute.value.name)
+    // console.log(this.$router.hasRoute(currentRoute.value.name))
+  },
+  render() {
+    return (
+      <div class="bg-white rounded-[6px] text-center py-6 px-[18px] flex items-center flex-col">
+        {this.menuList.map((item: any) => (
+          <RouterLink
+            to={item.path}
+            class={[
+              'py-2 px-6 mb-1.5 flex items-center text-base text-[#666] w-full last:mb-0 cursor-pointer',
+              styles.menuItem,
+              item.name === this.activeRoute && styles.active
+            ]}
+          >
+            <i class={['w-7 h-7 mr-3.5', styles['icon' + item.meta.index]]}></i>
+            {item.meta.title}
+          </RouterLink>
+        ))}
+      </div>
+    )
+  }
+})

+ 0 - 0
src/views/roleAuth/teacherAuth/index.module.less → src/views/user-info/components/users/index.module.less


+ 34 - 0
src/views/user-info/components/users/index.tsx

@@ -0,0 +1,34 @@
+import { defineComponent } from 'vue'
+import iconTeacher from '@/common/images/icon_teacher.png'
+import { ElTag } from 'element-plus'
+
+export default defineComponent({
+  name: 'users',
+  render() {
+    return (
+      <div class="bg-white rounded-[6px] text-center pt-[30px] pb-8 flex items-center flex-col">
+        <img src={iconTeacher} class="w-[68px] h-[68px] rounded-full" />
+
+        <p class="text-[#1A1A1A] text-lg pt-4">李老师</p>
+
+        <div class="pt-3 pb-6">
+          <ElTag round color="#fff" class="mx-1 !border-[#2DC7AA]">
+            老师认证
+          </ElTag>
+          <ElTag round color="#fff" class="mx-1 !border-[#2DC7AA]">
+            音乐人认证
+          </ElTag>
+        </div>
+
+        <div class="text-[14px] text-[#666] flex items-center justify-center">
+          <span class="pr-3 flex items-center justify-center leading-6">
+            粉丝<b class="text-black text-xl pl-1 pb-[2px]">134</b>
+          </span>
+          <span class="flex items-center justify-center leading-6">
+            帖子<b class="text-black text-xl pl-1 pb-[2px]">124</b>
+          </span>
+        </div>
+      </div>
+    )
+  }
+})

+ 0 - 0
src/views/user-info/index.module.less


+ 21 - 0
src/views/user-info/index.tsx

@@ -0,0 +1,21 @@
+import { defineComponent, render } from 'vue'
+import { RouterView } from 'vue-router'
+import UserMenu from './components/user-menu'
+import Users from './components/users'
+
+export default defineComponent({
+  name: 'user-info',
+  render() {
+    return (
+      <div class="w-[1200px] mt-[30px] mb-14 min-h-full m-auto text-[#333] flex">
+        <div class="w-56 mr-4">
+          <Users class="mb-3 user-shadow" />
+          <UserMenu class="user-shadow" />
+        </div>
+        <div class="w-[960px] bg-white rounded-[6px] user-shadow">
+          <RouterView></RouterView>
+        </div>
+      </div>
+    )
+  }
+})

+ 12 - 0
src/views/user-info/live-class/index.module.less

@@ -0,0 +1,12 @@
+.liveClass {
+  :global {
+    .el-tabs__nav-wrap {
+      @apply px-11;
+    }
+    .el-tabs__item {
+      height: 64px;
+      line-height: 64px;
+      padding: 0 42px;
+    }
+  }
+}

+ 23 - 0
src/views/user-info/live-class/index.tsx

@@ -0,0 +1,23 @@
+import { ElButton, ElTabPane, ElTabs } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'live-class',
+  render() {
+    return (
+      <div class={[styles.liveClass, 'relative']}>
+        <ElButton round type="primary" class="absolute right-11 top-4">
+          新建直播课
+        </ElButton>
+        <ElTabs>
+          <ElTabPane label="进行中" name="ing"></ElTabPane>
+          <ElTabPane label="未上架" name="ing"></ElTabPane>
+          <ElTabPane label="销售中" name="ing"></ElTabPane>
+          <ElTabPane label="已完成" name="ing"></ElTabPane>
+          <ElTabPane label="已取消" name="ing"></ElTabPane>
+        </ElTabs>
+      </div>
+    )
+  }
+})

+ 59 - 0
src/views/user-info/model/practice-timer/index.module.less

@@ -0,0 +1,59 @@
+.timer {
+  overflow: hidden;
+  padding: 0 14px;
+}
+.tips {
+  margin: 12px 0;
+  padding: 15px 12px;
+  background: #ffffff;
+  border-radius: 10px;
+
+  .tipsTitle {
+    font-size: 18px;
+    font-weight: 500;
+    color: #000000;
+    line-height: 25px;
+  }
+
+  .tipsTime {
+    padding-top: 4px;
+    font-size: 14px;
+    color: #ff9e5a;
+    line-height: 22px;
+    span {
+      font-weight: 600;
+    }
+  }
+}
+.timerContainer {
+  background: #ffffff;
+  border-radius: 10px;
+  padding: 0 5px 9px;
+  :global {
+    .van-col {
+      margin-bottom: 5px;
+    }
+  }
+}
+
+.tag {
+  height: 28px;
+  background: #eff6f5;
+  border-radius: 4px;
+  font-size: 14px;
+  font-weight: 500;
+  color: #2dc7aa;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.active {
+  background: #2dc7aa;
+  color: #ffffff;
+}
+
+.select {
+  color: #ffffff !important;
+  background: #ffb752;
+}

+ 270 - 0
src/views/user-info/model/practice-timer/index.tsx

@@ -0,0 +1,270 @@
+import dayjs from 'dayjs'
+import { ElButton, ElCol, ElRow } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'practice-timer',
+  props: {
+    timerObject: {
+      // 传入的数据
+      type: Object,
+      default: {}
+    },
+    onClose: {
+      type: Function,
+      default: () => {}
+    },
+    onChoice: {
+      // 点击选择时间
+      type: Function,
+      default: (item: any) => {}
+    },
+    courseMinutes: {
+      // 课程时长
+      type: Number,
+      default: 25
+    },
+    freeMinutes: {
+      // 空余时长
+      type: Number,
+      default: 5
+    },
+    startSetting: {
+      // 开始设置时间
+      type: String,
+      default: '08:00'
+    },
+    endSetting: {
+      // 结束设置时间
+      type: String,
+      default: '18:00'
+    }
+  },
+  data() {
+    return {
+      timerList: [],
+      list: [] as any,
+      weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+      weekType: [
+        'monday',
+        'tuesday',
+        'wednesday',
+        'thursday',
+        'friday',
+        'saturday',
+        'sunday'
+      ]
+    }
+  },
+  mounted() {
+    this.list = this.timerInit(
+      this.startSetting,
+      this.endSetting,
+      this.courseMinutes + this.freeMinutes || 30
+    )
+    console.log(this.endSetting)
+  },
+  methods: {
+    timerInit(startTime: string, endTime: string, space: number) {
+      let start = dayjs(startTime, 'HH:mm')
+      const end = dayjs(endTime, 'HH:mm')
+
+      const timerList: any = []
+      // 生成一天的时间段
+      while (start.add(space, 'minute').isSameOrBefore(dayjs(end))) {
+        const item = {
+          startTime: start.format('HH:mm'),
+          endTime: start.add(space, 'minute').format('HH:mm'),
+          status: false
+        }
+        // 一周
+        timerList.push(item)
+        start = start.add(space, 'minute')
+      }
+      const list: any = []
+      // 生成一周的时间段
+      timerList.forEach((item: any) => {
+        const weekList: any = []
+        for (let i = 0; i < 7; i++) {
+          weekList.push({
+            ...item
+          })
+        }
+        list.push(weekList)
+      })
+
+      const tempList = this._initData(list)
+      return tempList
+    },
+    _initData(list: Array<any>) {
+      // 回显数据
+      const weekType = this.weekType
+      const timerObject = this.timerObject
+      list.forEach((item: any) => {
+        item.forEach((slot: any, slotIndex: number) => {
+          const dayList = timerObject[weekType[slotIndex]]
+          const startTime = dayjs(slot.startTime, 'HH:mm').format('HH:mm:ss')
+          const isExist = dayList?.some(
+            (course: any) => course.startTime === startTime
+          )
+          isExist && (slot.status = true)
+        })
+      })
+      return list
+    },
+    btnStatus(index: number, type: 'row' | 'col') {
+      if (type === 'row') {
+        return this.list.every((item: any) => {
+          return item[index].status
+        })
+      }
+
+      if (type == 'col') {
+        return this.list[index].every((item: any) => item.status)
+      }
+    },
+    choice(index: number, type: 'row' | 'col', status?: boolean) {
+      if (type === 'row') {
+        this.list.forEach((item: any, i: number) => {
+          const type = !status ? true : false
+          item[index].status = type
+        })
+      }
+
+      if (type == 'col') {
+        this.list[index].forEach((item: any, i: number) => {
+          const type = !status ? true : false
+          item.status = type
+        })
+      }
+    },
+    onSubmit() {
+      const list = this.list
+      const weekList = {
+        monday: [],
+        tuesday: [],
+        wednesday: [],
+        thursday: [],
+        friday: [],
+        saturday: [],
+        sunday: []
+      }
+      const weekType = this.weekType
+      let status = false
+      list.forEach((item: any, i: number) => {
+        item.forEach((times: any, j: number) => {
+          if (times.status) {
+            status = true
+            weekList[weekType[j]].push({
+              startTime: dayjs(times.startTime, 'HH:mm').format('HH:mm:ss'),
+              endTime: dayjs(times.endTime, 'HH:mm')
+                .subtract(this.freeMinutes, 'minute')
+                .format('HH:mm:ss')
+            })
+          }
+        })
+      })
+      this.onChoice && this.onChoice(weekList, status)
+    }
+  },
+  render() {
+    return (
+      <div class={styles.timer}>
+        <div class={styles.tips}>
+          <div class={styles.tipsTitle}>请选择陪练开始时间</div>
+          <div class={styles.tipsTime}>
+            陪练课单课时时长为 <span>{this.courseMinutes}</span> 分钟
+          </div>
+        </div>
+
+        <div class={[styles.timerContainer, 'mb12']}>
+          <ElRow gutter={5}>
+            <ElCol span={3} class={[styles.tag]}></ElCol>
+            {this.weekList.map((item: any) => (
+              <ElCol span={3}>
+                <span class={styles.tag}>{item}</span>
+              </ElCol>
+            ))}
+          </ElRow>
+
+          <ElRow gutter={5} class="pt-1">
+            <ElCol span={3} class={[styles.tag]}></ElCol>
+            {this.weekList.map((item: any, index: number) => (
+              <ElCol span={3}>
+                <span
+                  class={[
+                    styles.tag,
+                    'cursor-pointer',
+                    this.btnStatus(index, 'row') && styles.active
+                  ]}
+                  onClick={() =>
+                    this.choice(index, 'row', this.btnStatus(index, 'row'))
+                  }
+                >
+                  全选
+                </span>
+              </ElCol>
+            ))}
+          </ElRow>
+
+          <div class="h-72 overflow-auto">
+            {this.list.map((item: any, index: number) => (
+              <ElRow gutter={5} class="pt-1">
+                <ElCol span={3}>
+                  <span
+                    class={[
+                      styles.tag,
+                      'cursor-pointer',
+                      this.btnStatus(index, 'col') && styles.active
+                    ]}
+                    onClick={() =>
+                      this.choice(index, 'col', this.btnStatus(index, 'col'))
+                    }
+                  >
+                    全选
+                  </span>
+                </ElCol>
+                {item.map((week: any) => (
+                  <ElCol span={3}>
+                    <span
+                      class={[
+                        styles.tag,
+                        'cursor-pointer',
+                        week.status && styles.select
+                      ]}
+                      style={{ color: '#333333' }}
+                      onClick={() => (week.status = !week.status)}
+                    >
+                      {week.startTime}
+                    </span>
+                  </ElCol>
+                ))}
+              </ElRow>
+            ))}
+          </div>
+        </div>
+
+        {/* <Sticky offsetBottom={0} position="bottom"> */}
+        <div class="text-center pt-3 pb-5">
+          <ElButton
+            class="!w-40 !h-[38px]"
+            onClick={() => {
+              this.onClose()
+            }}
+          >
+            取消
+          </ElButton>
+          <ElButton
+            type="primary"
+            class="!w-40 !h-[38px]"
+            onClick={this.onSubmit}
+          >
+            保存设置
+          </ElButton>
+        </div>
+        {/* </Sticky> */}
+      </div>
+    )
+  }
+})

+ 16 - 0
src/views/user-info/music-class/index.module.less

@@ -0,0 +1,16 @@
+.liveClass {
+  :global {
+    .el-tabs__nav-wrap {
+      @apply px-11;
+    }
+    .el-tabs__item {
+      height: 64px;
+      line-height: 64px;
+      padding: 0 42px;
+    }
+  }
+
+  .musicListItem:hover {
+    box-shadow: 0px 2px 7px 0px rgba(0, 0, 0, 0.04);
+  }
+}

+ 53 - 0
src/views/user-info/music-class/index.tsx

@@ -0,0 +1,53 @@
+import MusicLIstItem from '@/components/musicLIstItem'
+import Pagination from '@/components/pagination'
+import { ElButton, ElPagination, ElTabPane, ElTabs } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'music-class',
+  data() {
+    return {
+      pageInfo: {
+        // 分页规则
+        limit: 10, // 限制显示条数
+        page: 1, // 当前页
+        total: 0, // 总条数
+        page_size: [10, 20, 40, 50] // 选择限制显示条数
+      }
+    }
+  },
+  methods: {
+    getList() {}
+  },
+  render() {
+    return (
+      <div class={[styles.liveClass, 'relative pb-5']}>
+        <ElButton round type="primary" class="absolute right-11 top-4">
+          新建直播课
+        </ElButton>
+        <ElTabs>
+          <ElTabPane label="已上架" name="ing"></ElTabPane>
+          <ElTabPane label="审核中" name="ing"></ElTabPane>
+          <ElTabPane label="审核失败" name="ing"></ElTabPane>
+        </ElTabs>
+
+        <div class="px-[38px]">
+          {[1, 2, 3, 4, 5, 6].map((item: any) => (
+            <MusicLIstItem
+              class={[styles.musicListItem, 'mb-2 px-[14px] rounded-xl']}
+            />
+          ))}
+        </div>
+
+        <Pagination
+          total={this.pageInfo.total}
+          v-model:page={this.pageInfo.page}
+          v-model:limit={this.pageInfo.limit}
+          pageSizes={this.pageInfo.page_size}
+          // pagination={this.getList}
+        />
+      </div>
+    )
+  }
+})

+ 11 - 0
src/views/user-info/practice-setting/index.module.less

@@ -0,0 +1,11 @@
+.setting {
+  :global {
+    .el-dialog {
+      --el-dialog-width: 485px !important;
+    }
+    .el-dialog__header,
+    .el-dialog__body {
+      padding: 0;
+    }
+  }
+}

+ 213 - 0
src/views/user-info/practice-setting/index.tsx

@@ -0,0 +1,213 @@
+import request from '@/helpers/request'
+import { teacherState } from '@/views/role-auth/teacherAuth/teacherState'
+import {
+  ElButton,
+  ElDialog,
+  ElForm,
+  ElFormItem,
+  ElInput,
+  ElOption,
+  ElOptionGroup,
+  ElRadio,
+  ElRadioButton,
+  ElRadioGroup,
+  ElSelect
+} from 'element-plus'
+import { defineComponent } from 'vue'
+import PracticeTimer from '../model/practice-timer'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'practice-setting',
+  data() {
+    return {
+      popupStatus: false,
+      chargeTypeArr: {
+        0: '否',
+        1: '是'
+      },
+      classTimeStatus: false,
+      subjectStatus: false,
+      timerStatus: false,
+      timeSetting: {
+        courseMinutes: 25,
+        freeMinutes: 5,
+        startSetting: '08:00',
+        endSetting: '18:00'
+      },
+      timerObject: {} as any,
+      form: {
+        enableFlag: 1,
+        courseMinutes: null as any,
+        freeMinutes: 0,
+        subjectIdTemp: '',
+        subjectId: [] as any[],
+        subjectPrice: [] as any[],
+        skipHolidayFlag: 1,
+        setting: ''
+      },
+      minutes: [] as any,
+      rate: 0
+    }
+  },
+  async mounted() {
+    try {
+      // 获取手续费和分钟数
+      let config = await request.get(
+        '/api-website/sysConfig/queryByParamNameList',
+        {
+          params: {
+            paramNames:
+              'practice_times_setting,practice_service_fee,course_start_setting,course_end_setting'
+          }
+        }
+      )
+      let configData = config.data || []
+      configData.forEach((item: any) => {
+        if (item.paramName === 'practice_times_setting') {
+          let mins = item.paramValue ? JSON.parse(item.paramValue) : []
+          let tempArr = [] as any
+          mins.forEach((item: any) => {
+            tempArr.push({
+              ...item,
+              name: item.courseMinutes
+            })
+          })
+          this.minutes = [...tempArr]
+        }
+        if (item.paramName === 'practice_service_fee') {
+          this.rate = item.paramValue
+        }
+        if (item.paramName === 'course_start_setting') {
+          this.timeSetting.startSetting = item.paramValue
+        }
+        if (item.paramName === 'course_end_setting') {
+          this.timeSetting.endSetting = item.paramValue
+        }
+      })
+      console.log(this.minutes)
+      if (teacherState.subjectList.length <= 0) {
+        const res = await request.get('/api-website/subject/subjectSelect')
+        teacherState.subjectList = res.data || []
+      }
+    } catch {}
+  },
+  methods: {
+    onChoiceTimer() {}
+  },
+  render() {
+    return (
+      <div class={styles.setting}>
+        <div class="text-base text-[#333] leading-none px-6 py-5 border-b border-b-[#E5E5E5]">
+          陪练课设置
+        </div>
+
+        <ElForm
+          labelPosition="left"
+          labelWidth={'140px'}
+          size="large"
+          model={this.form}
+          ref="form"
+          class="px-44 py-5"
+        >
+          <ElFormItem
+            label="是否开启陪练"
+            prop="enableFlag"
+            rules={[
+              {
+                required: true,
+                message: '请选择是否开启陪练'
+              }
+            ]}
+          >
+            <ElSelect class="w-full" v-model={this.form.enableFlag}>
+              <ElSelect.Option value={1} label={'是'}>
+                是
+              </ElSelect.Option>
+              <ElSelect.Option value={0} label={'否'}>
+                否
+              </ElSelect.Option>
+            </ElSelect>
+          </ElFormItem>
+          <ElFormItem label="可教授声部" prop={'subjectId'} rules={[{}]}>
+            <ElSelect
+              multiple
+              filterable
+              placeholder="请选择可教授声部"
+              class="w-full"
+              multipleLimit={5}
+              v-model={this.form.subjectId}
+            >
+              {teacherState.subjectList.map((group: any) => (
+                <ElOptionGroup key={group.id} label={group.name}>
+                  {group.subjects &&
+                    group.subjects.map((item: any) => (
+                      <ElOption
+                        key={item.id}
+                        value={item.id}
+                        label={item.name}
+                      />
+                    ))}
+                </ElOptionGroup>
+              ))}
+            </ElSelect>
+          </ElFormItem>
+          <ElFormItem label="单课时长">
+            <ElSelect
+              class="w-full"
+              placeholder="请选择单课时时长"
+              v-model={this.form.courseMinutes}
+            ></ElSelect>
+          </ElFormItem>
+          <ElFormItem label="单簧管声部陪练价格">
+            <ElInput
+              v-slots={{
+                append: () => <span class="text-base text-[#333]">元</span>
+              }}
+            />
+          </ElFormItem>
+          <ElFormItem label="是否跳过节假日">
+            <ElRadioGroup v-model={this.form.skipHolidayFlag}>
+              <ElRadioButton label="1">是</ElRadioButton>
+              <ElRadioButton label="0">否</ElRadioButton>
+            </ElRadioGroup>
+          </ElFormItem>
+          <ElFormItem label="陪练时间段">
+            <ElInput
+              readonly
+              class="cursor-pointer"
+              v-model={this.form.setting}
+              placeholder="请选择陪练时间段"
+              suffixIcon={'ArrowDown'}
+            />
+          </ElFormItem>
+        </ElForm>
+
+        <div class="border-t border-t-[#E5E5E5] text-center pt-6 pb-7">
+          <ElButton class="!w-40 !h-[38px]">重围</ElButton>
+          <ElButton type="primary" class="!w-40 !h-[38px]">
+            保存设置
+          </ElButton>
+        </div>
+
+        <ElDialog
+          modelValue={this.popupStatus}
+          onUpdate:modelValue={val => (this.popupStatus = val)}
+          showClose
+        >
+          <PracticeTimer
+            onChoice={this.onChoiceTimer}
+            onClose={() => {
+              this.popupStatus = false
+            }}
+            timerObject={this.timerObject}
+            courseMinutes={Number(this.timeSetting.courseMinutes)}
+            freeMinutes={Number(this.timeSetting.freeMinutes)}
+            startSetting={this.timeSetting.startSetting}
+            endSetting={this.timeSetting.endSetting}
+          />
+        </ElDialog>
+      </div>
+    )
+  }
+})

+ 12 - 0
src/views/user-info/video-class/index.module.less

@@ -0,0 +1,12 @@
+.liveClass {
+  :global {
+    .el-tabs__nav-wrap {
+      @apply px-11;
+    }
+    .el-tabs__item {
+      height: 64px;
+      line-height: 64px;
+      padding: 0 42px;
+    }
+  }
+}

+ 28 - 0
src/views/user-info/video-class/index.tsx

@@ -0,0 +1,28 @@
+import VideoDetailItem from '@/components/videoDetailItem'
+import { ElButton, ElTabPane, ElTabs } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'video-class',
+  render() {
+    return (
+      <div class={[styles.liveClass, 'relative']}>
+        {/* 审核状态(DOING:待审核 PASS:通过 */}
+        {/* UNPASS:未通过),可用值:UNPAALY,DOING,PASS,UNPASS */}
+        <ElButton round type="primary" class="absolute right-11 top-4">
+          新建直播课
+        </ElButton>
+        <ElTabs>
+          <ElTabPane label="进行中" name="ing"></ElTabPane>
+          <ElTabPane label="未上架" name="ing"></ElTabPane>
+          <ElTabPane label="销售中" name="ing"></ElTabPane>
+          <ElTabPane label="已完成" name="ing"></ElTabPane>
+          <ElTabPane label="已取消" name="ing"></ElTabPane>
+        </ElTabs>
+
+        <VideoDetailItem />
+      </div>
+    )
+  }
+})

+ 20 - 0
yarn.lock

@@ -2192,6 +2192,13 @@
     "domhandler" "^4.2.0"
     "entities" "^2.0.0"
 
+"dom7@^4.0.4":
+  "integrity" "sha512-DSSgBzQ4rJWQp1u6o+3FVwMNnT5bzQbMb+o31TjYYeRi05uAcpF8koxdfzeoe5ElzPmua7W7N28YJhF7iEKqIw=="
+  "resolved" "https://registry.npmjs.org/dom7/-/dom7-4.0.4.tgz"
+  "version" "4.0.4"
+  dependencies:
+    "ssr-window" "^4.0.0"
+
 "domelementtype@^2.0.1", "domelementtype@^2.2.0":
   "integrity" "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
   "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz"
@@ -4651,6 +4658,11 @@
   "resolved" "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz"
   "version" "1.1.0"
 
+"ssr-window@^4.0.0", "ssr-window@^4.0.2":
+  "integrity" "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
+  "resolved" "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz"
+  "version" "4.0.2"
+
 "strict-uri-encode@^2.0.0":
   "integrity" "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
   "resolved" "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
@@ -4765,6 +4777,14 @@
   "resolved" "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz"
   "version" "1.0.0"
 
+"swiper@^8.2.4":
+  "integrity" "sha512-TPq64KiZUt8lZY5ZEg75RjToT+RwfLomfKIpcFLy6+UCUp2kL7hHWslLxjFtcFeiwfG67RHFYbJnq6tsothcJQ=="
+  "resolved" "https://registry.npmjs.org/swiper/-/swiper-8.2.4.tgz"
+  "version" "8.2.4"
+  dependencies:
+    "dom7" "^4.0.4"
+    "ssr-window" "^4.0.2"
+
 "systemjs@^6.12.1":
   "integrity" "sha512-hqTN6kW+pN6/qro6G9OZ7ceDQOcYno020zBQKpZQLsJhYTDMCMNfXi/Y8duF5iW+4WWZr42ry0MMkcRGpbwG2A=="
   "resolved" "https://registry.npmjs.org/systemjs/-/systemjs-6.12.1.tgz"

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