Browse Source

更新组件

lex 2 years ago
parent
commit
c58b1b9eb5
32 changed files with 1480 additions and 268 deletions
  1. 1 0
      components.d.ts
  2. BIN
      src/common/images/icon_empty.png
  3. 0 0
      src/components/col-empty/index.module.less
  4. 40 0
      src/components/col-empty/index.tsx
  5. 26 1
      src/components/musicLIstItem/index.module.less
  6. 48 44
      src/components/musicLIstItem/index.tsx
  7. 18 101
      src/components/pagination/index.tsx
  8. 120 0
      src/components/pagination/index.vue.temp
  9. 3 3
      src/helpers/request.ts
  10. 11 22
      src/router/routes-admin.ts
  11. 0 0
      src/views/user-info/components/item/index.module.less
  12. 52 0
      src/views/user-info/components/item/index.tsx
  13. 9 2
      src/views/user-info/components/user-menu/index.tsx
  14. 4 0
      src/views/user-info/live-class/index.module.less
  15. 29 6
      src/views/user-info/live-class/index.tsx
  16. 135 0
      src/views/user-info/live-class/list.tsx
  17. 4 0
      src/views/user-info/model/practice-timer/index.tsx
  18. 4 0
      src/views/user-info/music-class/index.module.less
  19. 13 33
      src/views/user-info/music-class/index.tsx
  20. 190 0
      src/views/user-info/music-class/list.tsx
  21. 16 0
      src/views/user-info/practice-setting/index.module.less
  22. 230 46
      src/views/user-info/practice-setting/index.tsx
  23. 5 0
      src/views/user-info/video-class/index.module.less
  24. 25 10
      src/views/user-info/video-class/index.tsx
  25. 142 0
      src/views/user-info/video-class/list.tsx
  26. 0 0
      src/views/user-info/video-operation/course-content/index.module.less
  27. 42 0
      src/views/user-info/video-operation/course-content/index.tsx
  28. 0 0
      src/views/user-info/video-operation/course-info/index.module.less
  29. 167 0
      src/views/user-info/video-operation/course-info/index.tsx
  30. 34 0
      src/views/user-info/video-operation/createState.tsx
  31. 0 0
      src/views/user-info/video-operation/index.module.less
  32. 112 0
      src/views/user-info/video-operation/index.tsx

+ 1 - 0
components.d.ts

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

BIN
src/common/images/icon_empty.png


+ 0 - 0
src/components/col-empty/index.module.less


+ 40 - 0
src/components/col-empty/index.tsx

@@ -0,0 +1,40 @@
+import { defineComponent } from 'vue'
+import empty from '@/common/images/icon_empty.png'
+import { ElButton } from 'element-plus'
+
+export default defineComponent({
+  name: 'col-empty',
+  props: {
+    message: {
+      type: String,
+      default: '暂无数据'
+    },
+    buttonText: {
+      type: String,
+      default: '去首页'
+    },
+    buttonVisibility: {
+      type: Boolean,
+      default: false
+    }
+  },
+  render() {
+    return (
+      <div class="w-full h-full text-center flex items-center justify-center flex-col">
+        <img src={empty} class="w-64 m-auto" />
+
+        <p class="text-base text-[#999999] pt-4 pb-6">{this.message}</p>
+
+        {this.buttonVisibility && (
+          <ElButton
+            type="primary"
+            class="rounded-sm !px-12"
+            style={{ height: '38px' }}
+          >
+            {this.buttonText}
+          </ElButton>
+        )}
+      </div>
+    )
+  }
+})

+ 26 - 1
src/components/musicLIstItem/index.module.less

@@ -1,4 +1,16 @@
 .itemWrap {
+  --music-list-item-background-color: #fff;
+  --music-list-item-title-color: #333;
+  --music-list-item-desc-color: #333;
+  --music-list-item-mate-color: #6a6a6a;
+  --music-list-item-border-color: #f1f1f1;
+  --music-list-item-vip-bg: #fff1cd;
+  --music-list-item-vip-color: #ff6c00;
+  --music-list-item-free-bg: #fff1e7;
+  --music-list-item-free-color: #ff4700;
+  --music-list-item-charge-bg: #e1f0ff;
+  --music-list-item-charge-color: #0086ff;
+
   height: 94px;
   background: #ffffff;
   border-radius: 12px;
@@ -73,9 +85,22 @@
       background: #e1f0ff;
       border-radius: 12px;
       line-height: 22px;
-      color: #0086FF;
+      color: #0086ff;
       text-align: center;
       font-size: 13px;
+
+      &.vip {
+        background-color: var(--music-list-item-vip-bg);
+        color: var(--music-list-item-vip-color);
+      }
+      &.free {
+        background-color: var(--music-list-item-free-bg);
+        color: var(--music-list-item-free-color);
+      }
+      &.charge {
+        background-color: var(--music-list-item-charge-bg);
+        color: var(--music-list-item-charge-color);
+      }
     }
     .arrow {
       width: 20px;

+ 48 - 44
src/components/musicLIstItem/index.tsx

@@ -5,61 +5,65 @@ import detaile from './images/detaile.png'
 import music from './images/music.png'
 import arrow from './images/arrow.png'
 
+type Props = {
+  addName: String
+  addUserAvatar: String
+  musicSheetName: String
+  subjectNames: String
+  composer: String
+  chargeType: String
+}
+const chargeTypes = {
+  CHARGE: '点播',
+  FREE: '免费',
+  VIP: 'VIP'
+}
+
 export default defineComponent({
   name: 'musicLIstItem',
   props: {
-    detail: {
-      type: Object,
-      default: {
-        albumRow: 0,
-        auditStatus: '',
-        chargeType: '',
-        createBy: 0,
-        idAndName: '',
-        musicTagIds: '',
-        order: '',
-        page: 0,
-        platform: '',
-        rows: 0,
-        search: '',
-        sheetRow: 0,
-        sort: '',
-        sourceType: '',
-        state: '',
-        subjectIds: '',
-        version: ''
-      }
+    item: {
+      type: Object as () => Props,
+      default: () => ({})
     }
   },
-  setup(props) {
-    const state = reactive({
-      detail:props.detail
-    })
+  setup(props: any) {
+    const item = props.item
     return () => (
-      <>
-        <div>
-          <div class={classes.itemWrap}>
-            <div class={classes.left}>
-              <div class={classes.imgWrap}>
-                <img src={music} alt="" />
-              </div>
-              <div class={classes.textWrap}>
-                <p>
-                 {state.detail.musicSheetName} <span>作曲: {state.detail.composer}</span>
-                </p>
-                <div class={classes.authorInfo}>
-                  <img class={classes.icon} src={ state.detail.addUserAvatar?state.detail.addUserAvatar: icon} alt="" />
-                  <span class={ classes.authorName}>{state.detail.addName}</span>
-                  <div class={classes.tagList}>
-                    <div class={classes.tag}>单簧管</div>
-                    <div class={classes.tag}>圆号</div>
-                  </div>
+      <div>
+        <div class={classes.itemWrap}>
+          <div class={classes.left}>
+            <div class={classes.imgWrap}>
+              <img src={music} alt="" />
+            </div>
+            <div class={classes.textWrap}>
+              <p>
+                {item.musicSheetName}
+                <span>作曲: {item.composer}</span>
+              </p>
+              <div class={classes.authorInfo}>
+                <img
+                  class={classes.icon}
+                  src={item.addUserAvatar || icon}
+                  alt=""
+                />
+                <span class={classes.authorName}>{item.addName}</span>
+                <div class={classes.tagList}>
+                  <div class={classes.tag}>{item.subjectNames}</div>
+                  {/* <div class={classes.tag}>圆号</div> */}
                 </div>
               </div>
             </div>
           </div>
           <div class={classes.right}>
-            <div class={classes.touchButton}>点播</div>
+            <div
+              class={[
+                classes.touchButton,
+                classes[item.chargeType.toLocaleLowerCase()]
+              ]}
+            >
+              {chargeTypes[item.chargeType]}
+            </div>
             <img class={classes.arrow} src={arrow} alt="" />
           </div>
         </div>

+ 18 - 101
src/components/pagination/index.tsx

@@ -23,7 +23,7 @@ export default defineComponent({
     },
     layout: {
       type: String,
-      default: 'total,sizes,prev, pager, next, jumper'
+      default: 'total, sizes, prev, pager, next, jumper'
     },
     background: {
       type: Boolean,
@@ -44,17 +44,24 @@ export default defineComponent({
     saveKey: {
       type: String,
       default: ''
+    },
+    pagination: {
+      type: Function,
+      default: () => {}
     }
   },
   methods: {
     handleSizeChange(val: number) {
+      console.log(val, 'headleSizeChange')
       this.$emit('update:page', 1)
-      // this.$emit('pagination', { page: this.page, limit: val })
+      this.$emit('update:limit', val)
+      this.pagination()
       if (this.autoScroll) {
         scrollTo(0, 800)
       }
     },
     handleCurrentChange(val: number) {
+      console.log(val, 'val')
       // this.$emit('pagination', { page: val, limit: this.limit })
       if (this.autoScroll) {
         scrollTo(0, 800)
@@ -62,17 +69,23 @@ export default defineComponent({
     }
   },
   render() {
+    // console.log(this.pageSizes)
     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}
+          // @ts-ignore
+          onUpdate:pageSize={(val: number) => {
+            this.handleSizeChange(val)
+          }}
+          pageSizes={this.pageSizes as number[]}
           total={this.total}
+          hideOnSinglePage={true}
+          background
+          layout="total, sizes, prev, pager, next, jumper"
           onSise-change={this.handleSizeChange}
           onCurrent-change={this.handleCurrentChange}
         />
@@ -80,99 +93,3 @@ export default defineComponent({
     )
   }
 })
-
-// <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>

+ 120 - 0
src/components/pagination/index.vue.temp

@@ -0,0 +1,120 @@
+<template>
+  <div :class="{ hidden: hidden }" class="pagination-container">
+    <el-pagination
+      v-model:current-page="myCurrentPage"
+      size="mini"
+      :page-sizes="pageSizes"
+      v-model:page-size="myLimit"
+      :layout="layout"
+      :total="total"
+      v-bind="$attrs"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</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>
+<style scoped lang="scss">
+.pagination-container {
+  background: #fff;
+  padding: 32px 16px;
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+}
+.pagination-container.hidden {
+  display: none;
+}
+</style>

+ 3 - 3
src/helpers/request.ts

@@ -72,7 +72,7 @@ request.interceptors.response.use(
 
       if (data.code === 403 || data.code === 401) {
         // window.location.href = location.origin
-        ElMessage.error(msg)
+        // ElMessage.error(msg)
       }
 
       if (!(data.code === 403 || data.code === 401)) {
@@ -80,8 +80,8 @@ request.interceptors.response.use(
       }
 
       if (data.code === 403) {
-        // window.location.href = location.origin
-        // ElMessage.error(msg)
+        window.location.href = location.origin
+        ElMessage.error('登录已过期,请重新登录')
       }
       throw new Error(msg)
     }

+ 11 - 22
src/router/routes-admin.ts

@@ -81,6 +81,17 @@ export default [
         meta: { title: '视频课', index: 4 }
       },
       {
+        path: '/userInfo/videoOperation',
+        name: 'userInfoVideoOperation',
+        component: () => import('@/views/user-info/video-operation'),
+        meta: {
+          title: '视频课',
+          index: 4,
+          hidden: true,
+          activeMenu: 'userInfoVideoClass'
+        }
+      },
+      {
         path: '/userInfo/musicClass',
         name: 'userInfoMusicClass',
         component: () => import('@/views/user-info/music-class'),
@@ -88,26 +99,4 @@ export default [
       }
     ]
   }
-  // {
-  //   path: '/',
-  //   component: Auth,
-  //   children: [
-  //     {
-  //       path: '/login',
-  //       name: 'login',
-  //       component: () => import('@/views/layout/login'),
-  //       meta: {
-  //         isRegister: false
-  //       } as metaType
-  //     }
-  //   ]
-  // }
-  // {
-  //   path: '/:pathMatch(.*)*',
-  //   component: () => import('@/views/404'),
-  //   meta: {
-  //     title: '404 Not Fund',
-  //     platform: 'STUDENT'
-  //   }
-  // }
 ]

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


+ 52 - 0
src/views/user-info/components/item/index.tsx

@@ -0,0 +1,52 @@
+import { ElImage } from 'element-plus'
+import { defineComponent } from 'vue'
+import iconTeacher from '@/common/images/icon_teacher.png'
+
+export default defineComponent({
+  name: 'item',
+  props: {
+    item: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  render() {
+    const item = this.item
+    return (
+      <div class="border border-[#f5f5f5] rounded-sm w-[262px] h-[302px] m-auto overflow-hidden relative hover:shadow-md transition-all">
+        {item.subjectName && (
+          <div class="absolute top-2 right-3 bg-black/40 text-white text-sm rounded-sm px-3 h-7 flex items-center justify-center z-10">
+            {item.subjectName}
+          </div>
+        )}
+        <ElImage
+          class="w-full h-[175px] align-middle"
+          fit="cover"
+          src={item.backgroundPic}
+        />
+        <div class="mx-2.5 py-3.5 border-b border-b-[#F2F2F2]">
+          <div class="text-lg leading-none font-medium whitespace-nowrap overflow-hidden text-ellipsis">
+            {item.courseGroupName}
+          </div>
+          <div class="text-sm text-[#999] pt-2.5">
+            {item.studentCount}人已购买
+          </div>
+        </div>
+
+        <div class="mx-2.5 py-4 flex items-center justify-between">
+          <div class="flex items-center">
+            <ElImage
+              class="w-[22px] h-[22px] align-middle rounded-full overflow-hidden mr-2"
+              object-fit="cover"
+              src={item.avatar || iconTeacher}
+            />
+            <span>{item.teacherName}</span>
+          </div>
+          <div class="font-medium text-[15px] leading-5 text-[#2DC7AA]">
+            ¥{item.coursePrice}/{item.courseNum}课时
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 9 - 2
src/views/user-info/components/user-menu/index.tsx

@@ -21,6 +21,9 @@ export default defineComponent({
     activeRoute() {
       return this.$route.name
     },
+    acitveMenuRoute() {
+      return this.$route.meta.activeMenu
+    },
     menuList() {
       const hasRoute = this.$router.hasRoute(defaultRoute)
       // 判断是否有个人中心的菜单
@@ -31,7 +34,9 @@ export default defineComponent({
       const userInfoRouter = allRouter.find(item => item.name === defaultRoute)
       const list: any = []
       userInfoRouter?.children.forEach((item: any) => {
-        list.push(item)
+        if (!item.meta.hidden) {
+          list.push(item)
+        }
       })
       return list
     }
@@ -52,7 +57,9 @@ export default defineComponent({
             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
+              (item.name === this.activeRoute ||
+                item.name === this.acitveMenuRoute) &&
+                styles.active
             ]}
           >
             <i class={['w-7 h-7 mr-3.5', styles['icon' + item.meta.index]]}></i>

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

@@ -8,5 +8,9 @@
       line-height: 64px;
       padding: 0 42px;
     }
+    .el-tabs__nav-wrap::after {
+      height: 1px;
+      background: #eeeeee;
+    }
   }
 }

+ 29 - 6
src/views/user-info/live-class/index.tsx

@@ -1,22 +1,45 @@
+import Pagination from '@/components/pagination'
 import { ElButton, ElTabPane, ElTabs } from 'element-plus'
 import { defineComponent } from 'vue'
+import Item from '../components/item'
 import styles from './index.module.less'
+import List from './list'
 
 export default defineComponent({
   name: 'live-class',
+  data() {
+    return {
+      activeName: 'ING'
+    }
+  },
+  methods: {
+    getList() {}
+  },
   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 v-model={this.activeName}>
+          <ElTabPane label="进行中" name="ING">
+            {this.activeName === 'ING' && <List groupStatus="ING" />}
+          </ElTabPane>
+          <ElTabPane label="未上架" name="NOT_SALE">
+            {this.activeName === 'NOT_SALE' && <List groupStatus="NOT_SALE" />}
+          </ElTabPane>
+          <ElTabPane label="销售中" name="APPLY">
+            {this.activeName === 'APPLY' && <List groupStatus="APPLY" />}
+          </ElTabPane>
+          <ElTabPane label="已完成" name="COMPLETE">
+            {this.activeName === 'COMPLETE' && <List groupStatus="COMPLETE" />}
+          </ElTabPane>
+          <ElTabPane label="已取消" name="CANCEL">
+            {this.activeName === 'CANCEL' && <List groupStatus="CANCEL" />}
+          </ElTabPane>
         </ElTabs>
+        {/* 课程组状态 ING(进行中) NOT_SALE(未开售,未上架) APPLY(报名中,销售中)
+        COMPLETE(已完成) CANCEL(已取消) */}
       </div>
     )
   }

+ 135 - 0
src/views/user-info/live-class/list.tsx

@@ -0,0 +1,135 @@
+import ColEmpty from '@/components/col-empty'
+import Pagination from '@/components/pagination'
+import request from '@/helpers/request'
+import { ElSkeleton, ElSkeletonItem } from 'element-plus'
+import { defineComponent } from 'vue'
+import Item from '../components/item'
+
+export default defineComponent({
+  name: 'list',
+  props: {
+    groupStatus: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      pageInfo: {
+        // 分页规则
+        limit: 10, // 限制显示条数
+        page: 1, // 当前页
+        total: 0, // 总条数
+        page_size: [10, 20, 40, 50] // 选择限制显示条数
+      },
+      list: [] as any[],
+      loading: false,
+      dataShow: false // 是否有数据
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      this.loading = true
+      try {
+        const { data } = await request.post(
+          '/api-website/courseGroup/queryPageCourseGroup',
+          {
+            data: {
+              groupStatus: this.groupStatus,
+              page: this.pageInfo.page,
+              rows: this.pageInfo.limit
+            }
+          }
+        )
+        this.list = data.rows || []
+        this.pageInfo.total = data.total
+        if (data.total <= 0) {
+          this.dataShow = true
+        }
+      } catch {}
+      if (this.dataShow) {
+        this.loading = false
+      } else {
+        setTimeout(() => {
+          this.loading = false
+        }, 500)
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        <div class="flex flex-wrap px-10">
+          <ElSkeleton
+            loading={this.loading}
+            animated
+            class="flex"
+            count={3}
+            v-slots={{
+              template: () => (
+                <div class="w-1/3">
+                  <div class="flex flex-col pt-8 w-[262px] m-auto">
+                    <ElSkeletonItem
+                      variant="image"
+                      style={{ width: '100%', height: '175px' }}
+                    ></ElSkeletonItem>
+                    <div class="mx-2.5 py-3.5 border-b border-b-[#F2F2F2]">
+                      <div class="text-lg leading-none font-medium whitespace-nowrap overflow-hidden text-ellipsis">
+                        <ElSkeletonItem variant="h3"></ElSkeletonItem>
+                      </div>
+                      <div class="text-sm text-[#999] pt-2.5">
+                        <ElSkeletonItem
+                          variant="p"
+                          style={{ width: '50%' }}
+                        ></ElSkeletonItem>
+                      </div>
+                    </div>
+
+                    <div class="mx-2.5 py-4 flex items-center">
+                      <ElSkeletonItem
+                        variant="circle"
+                        style={{
+                          width: '22px',
+                          height: '22px',
+                          marginRight: '5px'
+                        }}
+                      ></ElSkeletonItem>
+                      <ElSkeletonItem
+                        variant="p"
+                        style={{ width: '20%' }}
+                      ></ElSkeletonItem>
+                      <p style={{ width: '45%' }}></p>
+                      <ElSkeletonItem
+                        variant="p"
+                        style={{ width: '20%' }}
+                      ></ElSkeletonItem>
+                    </div>
+                  </div>
+                </div>
+              )
+            }}
+          >
+            {this.list.map((item: any) => (
+              <div class="w-1/3 pt-8">
+                <Item class="cursor-pointer" item={item} />
+              </div>
+            ))}
+          </ElSkeleton>
+        </div>
+
+        <Pagination
+          total={this.pageInfo.total}
+          v-model:page={this.pageInfo.page}
+          limit={this.pageInfo.limit}
+          pageSizes={this.pageInfo.page_size}
+          pagination={this.getList}
+        />
+
+        {this.dataShow && <ColEmpty />}
+      </>
+    )
+  }
+})

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

@@ -1,4 +1,8 @@
 import dayjs from 'dayjs'
+import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
+import customParseFormat from 'dayjs/plugin/customParseFormat'
+dayjs.extend(customParseFormat)
+dayjs.extend(isSameOrBefore)
 import { ElButton, ElCol, ElRow } from 'element-plus'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'

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

@@ -8,6 +8,10 @@
       line-height: 64px;
       padding: 0 42px;
     }
+    .el-tabs__nav-wrap::after {
+      height: 1px;
+      background: #eeeeee;
+    }
   }
 
   .musicListItem:hover {

+ 13 - 33
src/views/user-info/music-class/index.tsx

@@ -1,52 +1,32 @@
-import MusicLIstItem from '@/components/musicLIstItem'
-import Pagination from '@/components/pagination'
-import { ElButton, ElPagination, ElTabPane, ElTabs } from 'element-plus'
+import { ElButton, ElTabPane, ElTabs } from 'element-plus'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
+import List from './list'
 
 export default defineComponent({
   name: 'music-class',
   data() {
     return {
-      pageInfo: {
-        // 分页规则
-        limit: 10, // 限制显示条数
-        page: 1, // 当前页
-        total: 0, // 总条数
-        page_size: [10, 20, 40, 50] // 选择限制显示条数
-      }
+      activeName: 'PASS'
     }
   },
-  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 v-model={this.activeName}>
+          <ElTabPane label="已上架" name="PASS">
+            {this.activeName === 'PASS' && <List auditStatus="PASS" />}
+          </ElTabPane>
+          <ElTabPane label="审核中" name="DOING">
+            {this.activeName === 'DOING' && <List auditStatus="DOING" />}
+          </ElTabPane>
+          <ElTabPane label="审核失败" name="UNPASS">
+            {this.activeName === 'UNPASS' && <List auditStatus="UNPASS" />}
+          </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>
     )
   }

+ 190 - 0
src/views/user-info/music-class/list.tsx

@@ -0,0 +1,190 @@
+import ColEmpty from '@/components/col-empty'
+import MusicLIstItem from '@/components/musicLIstItem'
+import Pagination from '@/components/pagination'
+import request from '@/helpers/request'
+import styles from './index.module.less'
+import { ElSkeleton, ElSkeletonItem } from 'element-plus'
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'list',
+  props: {
+    auditStatus: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      pageInfo: {
+        // 分页规则
+        limit: 10, // 限制显示条数
+        page: 1, // 当前页
+        total: 0, // 总条数
+        page_size: [10, 20, 40, 50] // 选择限制显示条数
+      },
+      list: [] as any[],
+      loading: false,
+      dataShow: false // 是否有数据
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      this.loading = true
+      try {
+        const { data } = await request.post(
+          '/api-website/music/sheet/teacher/list',
+          {
+            data: {
+              auditStatus: this.auditStatus,
+              page: this.pageInfo.page,
+              rows: this.pageInfo.limit
+            }
+          }
+        )
+        this.list = data.rows || []
+        this.pageInfo.total = data.total
+        if (data.total <= 0) {
+          this.dataShow = true
+        }
+      } catch {}
+      if (this.dataShow) {
+        this.loading = false
+      } else {
+        setTimeout(() => {
+          this.loading = false
+        }, 500)
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        <div class="px-[38px]">
+          <ElSkeleton
+            loading={this.loading}
+            animated
+            class=" w-full m-auto px-[14px] flex items-center flex-col"
+            count={3}
+            v-slots={{
+              template: () => (
+                <div class="h-[94px] flex items-center justify-between w-full mb-2">
+                  <div class="w-2/3 flex items-center">
+                    <ElSkeletonItem
+                      variant="circle"
+                      style={{ width: '66px', height: '66px' }}
+                    ></ElSkeletonItem>
+                    <div class="w-1/2 pl-2">
+                      <ElSkeletonItem variant="h3"></ElSkeletonItem>
+                      <ElSkeletonItem
+                        variant="p"
+                        style={{ width: '50%' }}
+                      ></ElSkeletonItem>
+                    </div>
+                  </div>
+                  <ElSkeletonItem
+                    variant="p"
+                    style={{ width: '20%' }}
+                  ></ElSkeletonItem>
+                </div>
+              )
+            }}
+          >
+            {this.list.map((item: any) => (
+              <MusicLIstItem
+                item={{
+                  addName: item.addName,
+                  addUserAvatar: item.addUserAvatar,
+                  musicSheetName: item.musicSheetName,
+                  subjectNames: item.subjectNames,
+                  composer: item.composer,
+                  chargeType: item.chargeType
+                }}
+                class={[
+                  styles.musicListItem,
+                  'mb-2 px-[14px] rounded-xl cursor-pointer'
+                ]}
+              />
+            ))}
+          </ElSkeleton>
+        </div>
+
+        <Pagination
+          total={this.pageInfo.total}
+          v-model:page={this.pageInfo.page}
+          limit={this.pageInfo.limit}
+          pageSizes={this.pageInfo.page_size}
+          pagination={this.getList}
+        />
+        {/* <div class="flex flex-wrap px-10">
+          {this.list.map((item: any) => (
+            <div class="w-1/3 pt-8">
+              <ElSkeleton
+                loading={this.loading}
+                animated
+                style={{ width: '262px', margin: '0 auto' }}
+                count={1}
+                v-slots={{
+                  template: () => (
+                    <>
+                      <ElSkeletonItem
+                        variant="image"
+                        style={{ width: '100%', height: '175px' }}
+                      ></ElSkeletonItem>
+                      <div class="mx-2.5 py-3.5 border-b border-b-[#F2F2F2]">
+                        <div class="text-lg leading-none font-medium whitespace-nowrap overflow-hidden text-ellipsis">
+                          <ElSkeletonItem variant="h3"></ElSkeletonItem>
+                        </div>
+                        <div class="text-sm text-[#999] pt-2.5">
+                          <ElSkeletonItem
+                            variant="p"
+                            style={{ width: '50%' }}
+                          ></ElSkeletonItem>
+                        </div>
+                      </div>
+
+                      <div class="mx-2.5 py-4 flex items-center">
+                        <ElSkeletonItem
+                          variant="circle"
+                          style={{
+                            width: '22px',
+                            height: '22px',
+                            marginRight: '5px'
+                          }}
+                        ></ElSkeletonItem>
+                        <ElSkeletonItem
+                          variant="p"
+                          style={{ width: '20%' }}
+                        ></ElSkeletonItem>
+                        <p style={{ width: '45%' }}></p>
+                        <ElSkeletonItem
+                          variant="p"
+                          style={{ width: '20%' }}
+                        ></ElSkeletonItem>
+                      </div>
+                    </>
+                  )
+                }}
+              >
+                <Item class="cursor-pointer" item={item} />
+              </ElSkeleton>
+            </div>
+          ))}
+        </div>
+
+        <Pagination
+          total={this.pageInfo.total}
+          v-model:page={this.pageInfo.page}
+          limit={this.pageInfo.limit}
+          pageSizes={this.pageInfo.page_size}
+          pagination={this.getList}
+        /> */}
+
+        {this.dataShow && <ColEmpty />}
+      </>
+    )
+  }
+})

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

@@ -7,5 +7,21 @@
     .el-dialog__body {
       padding: 0;
     }
+
+    .el-radio-button__inner {
+      border: var(--el-border);
+      border-radius: var(--el-border-radius-base) !important;
+      width: 100%;
+      padding: 11px 19px !important;
+    }
+    .el-radio-button__original-radio:checked + .el-radio-button__inner {
+      background-color: #e9fff8;
+      color: var(--el-color-primary);
+      box-shadow: none;
+    }
+
+    .el-checkbox__inner {
+      @apply rounded-full overflow-hidden;
+    }
   }
 }

+ 230 - 46
src/views/user-info/practice-setting/index.tsx

@@ -1,4 +1,5 @@
 import request from '@/helpers/request'
+import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
 import { teacherState } from '@/views/role-auth/teacherAuth/teacherState'
 import {
   ElButton,
@@ -6,6 +7,7 @@ import {
   ElForm,
   ElFormItem,
   ElInput,
+  ElMessage,
   ElOption,
   ElOptionGroup,
   ElRadio,
@@ -21,7 +23,7 @@ export default defineComponent({
   name: 'practice-setting',
   data() {
     return {
-      popupStatus: false,
+      subjectList: [],
       chargeTypeArr: {
         0: '否',
         1: '是'
@@ -44,7 +46,7 @@ export default defineComponent({
         subjectId: [] as any[],
         subjectPrice: [] as any[],
         skipHolidayFlag: 1,
-        setting: ''
+        setting: '未设置'
       },
       minutes: [] as any,
       rate: 0
@@ -85,15 +87,157 @@ export default defineComponent({
           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 || []
+
+      let teacher = await request.post('/api-website/teacher/querySubject')
+      this.subjectList = teacher.data || []
+
+      // 获取课程设置
+      const setting = await request.post(
+        '/api-teacher/teacherFreeTime/getDetail',
+        {
+          data: {
+            defaultFlag: 1
+          }
+        }
+      )
+      const sr = setting.data
+      if (sr) {
+        this.timeSetting.courseMinutes = sr.courseMinutes
+        this.timeSetting.freeMinutes = sr.freeMinutes
+
+        this.timerObject = {
+          monday: sr.monday ? JSON.parse(sr.monday) : [],
+          tuesday: sr.tuesday ? JSON.parse(sr.tuesday) : [],
+          wednesday: sr.wednesday ? JSON.parse(sr.wednesday) : [],
+          thursday: sr.thursday ? JSON.parse(sr.thursday) : [],
+          friday: sr.friday ? JSON.parse(sr.friday) : [],
+          saturday: sr.saturday ? JSON.parse(sr.saturday) : [],
+          sunday: sr.sunday ? JSON.parse(sr.sunday) : []
+        }
+
+        let tempIds: any = []
+        let tempPrices: any = []
+        const subjectPrice = sr.subjectPrice || []
+        subjectPrice.forEach((item: any) => {
+          tempIds.push(item.subjectId)
+          tempPrices.push({
+            subjectId: item.subjectId,
+            subjectPrice: item.subjectPrice,
+            subjectName: item.subjectName
+          })
+        })
+        const to = this.timerObject
+        this.form = {
+          enableFlag: sr.enableFlag,
+          courseMinutes: sr.courseMinutes,
+          freeMinutes: sr.freeMinutes,
+          subjectIdTemp: tempIds.join(','),
+          subjectId: tempIds,
+          subjectPrice: tempPrices,
+          skipHolidayFlag: sr.skipHolidayFlag,
+          setting:
+            to.monday.length > 0 ||
+            to.tuesday.length > 0 ||
+            to.wednesday.length > 0 ||
+            to.thursday.length > 0 ||
+            to.friday.length > 0 ||
+            to.saturday.length > 0 ||
+            to.sunday.length > 0
+              ? '已设置'
+              : '未设置'
+        }
       }
     } catch {}
   },
   methods: {
-    onChoiceTimer() {}
+    onSelect(item: any) {
+      // 如果分钟数不同,则清空
+      if (this.form.courseMinutes !== item.courseMinutes) {
+        this.timerObject = {}
+        this.form.setting = '未设置'
+      }
+      this.form.courseMinutes = item.courseMinutes
+      this.form.freeMinutes = item.freeMinutes
+    },
+    async onTimer() {
+      try {
+        const form = this.form
+        if (!form.courseMinutes) {
+          // Toast('请选择单课时时长')
+          ElMessage.error('请选择单课时时长')
+          return
+        }
+        this.timeSetting.courseMinutes = Number(form.courseMinutes)
+        this.timeSetting.freeMinutes = Number(form.freeMinutes)
+        this.timerStatus = true
+      } catch {}
+    },
+    onChoiceTimer(item: any, status: boolean) {
+      console.log(item, 'item')
+      this.form.setting = status ? '已设置' : ''
+      this.timerObject = item
+      this.timerStatus = false
+    },
+    onChoice(item: any) {
+      console.log(item, '2323')
+      const tempItem = item || []
+      this.form.subjectId = tempItem
+      this.form.subjectIdTemp = tempItem.join(',') || ''
+      let subjectPriceList = [...this.form.subjectPrice]
+      tempItem.forEach((item: any) => {
+        const index = subjectPriceList.findIndex(
+          (subject: any) => subject.subjectId === item
+        )
+        if (index === -1) {
+          subjectPriceList.push({
+            subjectId: item,
+            subjectPrice: null as any,
+            subjectName: ''
+          })
+        }
+      })
+
+      const temp: any = []
+      subjectPriceList.forEach((item: any) => {
+        const isExist = tempItem.some(
+          (subjectId: any) => subjectId === item.subjectId
+        )
+        isExist && temp.push(item)
+      })
+      this.form.subjectPrice = temp
+      this.subjectStatus = false
+    },
+    getSubjectName(id: any) {
+      const subject: any = this.subjectList.find((item: any) => item.id === id)
+      return subject ? subject.name : ''
+    },
+    onFormatter(e: any) {
+      console.log(verifyNumberIntegerAndFloat(e.target.value))
+      e.target.value = verifyNumberIntegerAndFloat(e.target.value)
+    },
+    async onSubmit() {
+      ;(this as any).$refs.form.validate(async (_: boolean) => {
+        if (_) {
+          try {
+            const form = this.form
+            form.subjectPrice.forEach((item: any) => {
+              item.subjectName = this.getSubjectName(item.subjectId)
+            })
+            form.setting = form.setting === '未设置' ? '' : form.setting
+            await request.post('/api-website/teacherFreeTime/upSet', {
+              data: {
+                ...form,
+                ...this.timerObject
+              }
+            })
+            ElMessage.success('设置成功')
+            setTimeout(() => {
+              postMessage({ api: 'back', content: {} })
+            }, 500)
+          } catch {}
+        }
+      })
+    }
   },
   render() {
     return (
@@ -104,7 +248,7 @@ export default defineComponent({
 
         <ElForm
           labelPosition="left"
-          labelWidth={'140px'}
+          labelWidth={'180px'}
           size="large"
           model={this.form}
           ref="form"
@@ -129,7 +273,17 @@ export default defineComponent({
               </ElSelect.Option>
             </ElSelect>
           </ElFormItem>
-          <ElFormItem label="可教授声部" prop={'subjectId'} rules={[{}]}>
+          <ElFormItem
+            label="可教授声部"
+            prop={'subjectId'}
+            rules={[
+              {
+                required: true,
+                message: '请选择可教授声部',
+                trigger: 'change'
+              }
+            ]}
+          >
             <ElSelect
               multiple
               filterable
@@ -137,68 +291,98 @@ export default defineComponent({
               class="w-full"
               multipleLimit={5}
               v-model={this.form.subjectId}
+              onChange={this.onChoice}
             >
-              {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>
+              {this.subjectList.map((item: any) => (
+                <ElOption key={item.id} value={item.id} label={item.name} />
               ))}
             </ElSelect>
           </ElFormItem>
-          <ElFormItem label="单课时长">
+          <ElFormItem
+            label="单课时长"
+            prop="courseMinutes"
+            rules={[
+              {
+                required: true,
+                message: '请选择单课时长',
+                trigger: 'change'
+              }
+            ]}
+          >
             <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>
-              }}
-            />
+            >
+              {this.minutes.map((item: any) => (
+                <ElOption key={item.courseMinutes} value={item.courseMinutes}>
+                  {item.name}
+                </ElOption>
+              ))}
+            </ElSelect>
           </ElFormItem>
+          {this.form.subjectPrice.map((item: any, index: number) => (
+            <ElFormItem
+              label={`${this.getSubjectName(item.subjectId)}陪练价格`}
+              prop={`subjectPrice.${index}.subjectPrice`}
+              rules={[
+                {
+                  required: true,
+                  message: `请选择声部陪练价格`
+                }
+              ]}
+            >
+              <ElInput
+                // @ts-ignore
+                onKeyup={this.onFormatter}
+                type="text"
+                placeholder="请输入陪练价格"
+                v-model={item.subjectPrice}
+                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>
+              <ElRadioButton label="1" class="mr-3 w-24">
+                是
+              </ElRadioButton>
+              <ElRadioButton label="0" class="w-24">
+                否
+              </ElRadioButton>
             </ElRadioGroup>
           </ElFormItem>
           <ElFormItem label="陪练时间段">
-            <ElInput
-              readonly
-              class="cursor-pointer"
-              v-model={this.form.setting}
-              placeholder="请选择陪练时间段"
-              suffixIcon={'ArrowDown'}
-            />
+            <div onClick={this.onTimer} class="w-full">
+              <ElInput
+                readonly
+                class="cursor-pointer"
+                v-model={this.form.setting}
+                placeholder="请选择陪练时间段"
+                suffixIcon={'ArrowDown'}
+              />
+            </div>
           </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 class="!w-40 !h-[38px]">重置</ElButton>
+          <ElButton
+            type="primary"
+            class="!w-40 !h-[38px]"
+            onClick={this.onSubmit}
+          >
             保存设置
           </ElButton>
         </div>
 
-        <ElDialog
-          modelValue={this.popupStatus}
-          onUpdate:modelValue={val => (this.popupStatus = val)}
-          showClose
-        >
+        <ElDialog modelValue={this.timerStatus} showClose>
           <PracticeTimer
             onChoice={this.onChoiceTimer}
             onClose={() => {
-              this.popupStatus = false
+              this.timerStatus = false
             }}
             timerObject={this.timerObject}
             courseMinutes={Number(this.timeSetting.courseMinutes)}

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

@@ -8,5 +8,10 @@
       line-height: 64px;
       padding: 0 42px;
     }
+
+    .el-tabs__nav-wrap::after {
+      height: 1px;
+      background: #eeeeee;
+    }
   }
 }

+ 25 - 10
src/views/user-info/video-class/index.tsx

@@ -2,26 +2,41 @@ import VideoDetailItem from '@/components/videoDetailItem'
 import { ElButton, ElTabPane, ElTabs } from 'element-plus'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
+import List from './list'
 
 export default defineComponent({
   name: 'video-class',
+  data() {
+    return {
+      activeName: 'DOING'
+    }
+  },
   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
+          round
+          type="primary"
+          class="absolute right-11 top-4 z-10"
+          onClick={() => {
+            this.$router.push('/userInfo/videoOperation')
+          }}
+        >
+          新建视频课
         </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 v-model={this.activeName}>
+          <ElTabPane label="已上架" name="DOING">
+            {this.activeName === 'DOING' && <List auditStatus="DOING" />}
+          </ElTabPane>
+          <ElTabPane label="审核中" name="PASS">
+            {this.activeName === 'PASS' && <List auditStatus="PASS" />}
+          </ElTabPane>
+          <ElTabPane label="审核失败" name="UNPASS">
+            {this.activeName === 'UNPASS' && <List auditStatus="UNPASS" />}
+          </ElTabPane>
         </ElTabs>
-
-        <VideoDetailItem />
       </div>
     )
   }

+ 142 - 0
src/views/user-info/video-class/list.tsx

@@ -0,0 +1,142 @@
+import ColEmpty from '@/components/col-empty'
+import Pagination from '@/components/pagination'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { ElSkeleton, ElSkeletonItem } from 'element-plus'
+import { defineComponent } from 'vue'
+import Item from '../components/item'
+
+export default defineComponent({
+  name: 'list',
+  props: {
+    auditStatus: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      pageInfo: {
+        // 分页规则
+        limit: 10, // 限制显示条数
+        page: 1, // 当前页
+        total: 0, // 总条数
+        page_size: [10, 20, 40, 50] // 选择限制显示条数
+      },
+      list: [] as any[],
+      loading: true,
+      dataShow: false // 是否有数据
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      this.loading = true
+      try {
+        const { data } = await request.post(
+          '/api-website/videoLessonGroup/page',
+          {
+            data: {
+              auditStatus: this.auditStatus,
+              page: this.pageInfo.page,
+              rows: this.pageInfo.limit,
+              teacherId: state.user.data?.userId
+            }
+          }
+        )
+        this.list = data.rows || []
+        this.pageInfo.total = data.total
+        if (data.total <= 0) {
+          this.dataShow = true
+        }
+      } catch {}
+      setTimeout(() => {
+        this.loading = false
+      }, 500)
+    }
+  },
+  render() {
+    return (
+      <>
+        <div class="flex flex-wrap px-10">
+          {this.list.map((item: any) => (
+            <div class="w-1/3 pt-8">
+              <ElSkeleton
+                loading={this.loading}
+                animated
+                style={{ width: '262px', margin: '0 auto' }}
+                count={1}
+                v-slots={{
+                  template: () => (
+                    <>
+                      <ElSkeletonItem
+                        variant="image"
+                        // class="w-full h-[175px]"
+                        style={{ width: '100%', height: '175px' }}
+                      ></ElSkeletonItem>
+                      <div class="mx-2.5 py-3.5 border-b border-b-[#F2F2F2]">
+                        <div class="text-lg leading-none font-medium whitespace-nowrap overflow-hidden text-ellipsis">
+                          <ElSkeletonItem variant="h3"></ElSkeletonItem>
+                        </div>
+                        <div class="text-sm text-[#999] pt-2.5">
+                          <ElSkeletonItem
+                            variant="p"
+                            style={{ width: '50%' }}
+                          ></ElSkeletonItem>
+                        </div>
+                      </div>
+
+                      <div class="mx-2.5 py-4 flex items-center">
+                        <ElSkeletonItem
+                          variant="circle"
+                          style={{
+                            width: '22px',
+                            height: '22px',
+                            marginRight: '5px'
+                          }}
+                        ></ElSkeletonItem>
+                        <ElSkeletonItem
+                          variant="p"
+                          style={{ width: '20%' }}
+                        ></ElSkeletonItem>
+                        <p style={{ width: '45%' }}></p>
+                        <ElSkeletonItem
+                          variant="p"
+                          style={{ width: '20%' }}
+                        ></ElSkeletonItem>
+                      </div>
+                    </>
+                  )
+                }}
+              >
+                <Item
+                  item={{
+                    backgroundPic: item.lessonCoverUrl,
+                    courseGroupName: item.lessonName,
+                    studentCount: item.countStudent,
+                    avatar: item.avatar,
+                    teacherName: item.username,
+                    coursePrice: item.lessonPrice,
+                    courseNum: item.lessonCount
+                  }}
+                />
+              </ElSkeleton>
+            </div>
+          ))}
+        </div>
+
+        <Pagination
+          total={this.pageInfo.total}
+          v-model:page={this.pageInfo.page}
+          limit={this.pageInfo.limit}
+          pageSizes={this.pageInfo.page_size}
+          pagination={this.getList}
+        />
+
+        {this.dataShow && <ColEmpty />}
+      </>
+    )
+  }
+})

+ 0 - 0
src/views/user-info/video-operation/course-content/index.module.less


+ 42 - 0
src/views/user-info/video-operation/course-content/index.tsx

@@ -0,0 +1,42 @@
+import { ElButton, ElForm, ElFormItem, ElInput } from 'element-plus'
+import { defineComponent } from 'vue'
+import { createState } from '../createState'
+
+export default defineComponent({
+  name: 'course-content',
+  render() {
+    return (
+      <>
+        <ElForm
+          class="px-[200px] pb-10 pt-7"
+          size="large"
+          labelWidth={'90px'}
+          labelPosition="left"
+        >
+          <ElFormItem label="课程名称">
+            <ElInput placeholder="请输入课程名称" />
+          </ElFormItem>
+        </ElForm>
+        <div class="border-t border-t-[#E5E5E5] text-center pt-6 pb-7">
+          <ElButton
+            class="!w-40 !h-[38px]"
+            onClick={() => {
+              createState.active = 1
+            }}
+          >
+            上一步
+          </ElButton>
+          <ElButton
+            type="primary"
+            class="!w-40 !h-[38px]"
+            onClick={() => {
+              createState.active = 1
+            }}
+          >
+            下一步
+          </ElButton>
+        </div>
+      </>
+    )
+  }
+})

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


+ 167 - 0
src/views/user-info/video-operation/course-info/index.tsx

@@ -0,0 +1,167 @@
+import ColUpload from '@/components/col-upload'
+import request from '@/helpers/request'
+import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
+import {
+  ElButton,
+  ElCol,
+  ElForm,
+  ElFormItem,
+  ElImage,
+  ElInput,
+  ElOption,
+  ElRadio,
+  ElRadioGroup,
+  ElRow,
+  ElSelect,
+  ElTabPane,
+  ElTabs
+} from 'element-plus'
+import { defineComponent } from 'vue'
+import { createState } from '../createState'
+
+export default defineComponent({
+  name: 'course-info',
+  computed: {
+    choiceSubjectIds() {
+      // 选择的科目编号
+      let ids = createState.lessonGroup.lessonSubject
+        ? Number(createState.lessonGroup.lessonSubject)
+        : null
+      return ids ? [ids] : []
+    },
+    subjectList() {
+      // 学科列表
+      return createState.subjectList || []
+    },
+    lessonSubjectName() {
+      // 选择的科目
+      let tempStr = ''
+      this.subjectList.forEach((item: any) => {
+        if (this.choiceSubjectIds.includes(item.id)) {
+          tempStr = item.name
+        }
+      })
+      return tempStr
+    },
+    calcRatePrice() {
+      // 计算手续费
+      let rate = createState.rate || 0
+      let price = createState.lessonGroup.lessonPrice || 0
+      return (price - (rate / 100) * price).toFixed(2)
+    }
+  },
+  async mounted() {
+    try {
+      if (createState.subjectList.length <= 0) {
+        const res = await request.post('/api-website/teacher/querySubject')
+        createState.subjectList = res.data || []
+      }
+    } catch {
+      //
+    }
+  },
+  methods: {
+    onFormatter(val: any) {
+      return verifyNumberIntegerAndFloat(val)
+    },
+    tabChange(name: number) {
+      ;(this as any).$refs.form.resetValidation('lessonCoverTemplateUrl')
+      ;(this as any).$refs.form.resetValidation('lessonCoverUrl')
+      createState.tabIndex = name
+    },
+    selectImg(val: string) {
+      createState.lessonGroup.lessonCoverUrl = ''
+      createState.lessonGroup.lessonCoverTemplateUrl = val
+    }
+  },
+  render() {
+    return (
+      <>
+        <ElForm
+          class="px-[200px] pb-10 pt-7"
+          size="large"
+          labelWidth={'90px'}
+          labelPosition="left"
+        >
+          <ElFormItem label="课程名称">
+            <ElInput placeholder="请输入课程名称" />
+          </ElFormItem>
+          <ElFormItem label="课程声部">
+            <ElSelect class="w-full" placeholder="请选择课程声部">
+              {createState.subjectList.map((item: any) => (
+                <ElOption value={item.id}>{item.name}</ElOption>
+              ))}
+            </ElSelect>
+          </ElFormItem>
+          <ElFormItem label="课程介绍">
+            <ElInput
+              placeholder="请输入课程介绍"
+              type="textarea"
+              // @ts-ignore
+              maxlength={200}
+              rows={4}
+              showWordLimit
+            />
+          </ElFormItem>
+          <ElFormItem label="课程组售价">
+            <ElInput
+              placeholder="请输入课程组售价"
+              formatter={this.onFormatter}
+              v-slots={{
+                append: () => <span class="text-base text-[#333]">元</span>
+              }}
+            />
+          </ElFormItem>
+          <div class="text-sm text-[#999] pl-[90px] leading-relaxed pb-2">
+            <p>扣除手续费后您的课程预计收入为: </p>
+            {/* <p>
+              单课时<span class="px-1 text-[#FF4E19]">19</span>元/人
+            </p> */}
+            <p>
+              课程组总收入
+              <span class="px-1 text-[#FF4E19]">{this.calcRatePrice}</span>元/人
+            </p>
+            <p>您的课程收入将在课程结束后结算到您的账户中 </p>
+          </div>
+          <ElFormItem label="课程封面" class="!mb-0">
+            <ElTabs v-model={createState.tabIndex}>
+              <ElTabPane label="图片模板" name={1}></ElTabPane>
+              <ElTabPane label="自定义模板" name={2}></ElTabPane>
+            </ElTabs>
+          </ElFormItem>
+          <ElFormItem>
+            {/* <ElRadioGroup>
+              <ElRow>
+                {createState.templateList.map((item: any) => (
+                  <ElCol span={10} class="mb-3 cursor-pointer">
+                    <div class="w-40 relative rounded-xl overflow-hidden border">
+                      <ElImage src={item} class="align-middle" />
+                      <ElRadio
+                        label={item}
+                        class="!absolute bottom-2 right-0 !h-auto"
+                      >
+                        {''}
+                      </ElRadio>
+                    </div>
+                  </ElCol>
+                ))}
+              </ElRow>
+            </ElRadioGroup> */}
+            <ColUpload />
+          </ElFormItem>
+        </ElForm>
+        <div class="border-t border-t-[#E5E5E5] text-center pt-6 pb-7">
+          <ElButton
+            type="primary"
+            class="!w-40 !h-[38px]"
+            onClick={() => {
+              createState.active = 1
+            }}
+          >
+            下一步
+          </ElButton>
+        </div>
+      </>
+    )
+  }
+})

+ 34 - 0
src/views/user-info/video-operation/createState.tsx

@@ -0,0 +1,34 @@
+import { reactive } from 'vue'
+
+export const createState = reactive({
+  groupId: 0,
+  active: 0,
+  tabIndex: 1,
+  loadingStatus: false,
+  rate: 0, // 手续费
+  subjectList: [], // 声部列表
+  templateList: [
+    'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJdc.png',
+    'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJdl.png',
+    'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJdK.png',
+    'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJeA.png'
+  ], // 模板列表
+  lessonGroup: {
+    id: null,
+    lessonName: '',
+    lessonSubject: null as any,
+    lessonDesc: '',
+    lessonPrice: null as any,
+    lessonCoverUrl: '',
+    lessonCoverTemplateUrl: ''
+  },
+  lessonList: [
+    {
+      videoTitle: '',
+      videoContent: '',
+      videoUrl: '',
+      coverUrl: '',
+      posterUrl: '' // 视频封面图
+    }
+  ]
+})

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


+ 112 - 0
src/views/user-info/video-operation/index.tsx

@@ -0,0 +1,112 @@
+import ColSteps from '@/components/col-steps'
+import request from '@/helpers/request'
+import { defineComponent } from 'vue'
+import CourseContent from './course-content'
+import CourseInfo from './course-info'
+import { createState } from './createState'
+
+export default defineComponent({
+  name: 'video-operation',
+  props: {
+    type: {
+      type: String,
+      default: 'create'
+    }
+  },
+  async created() {
+    const query = this.$route.query
+    createState.groupId = Number(query.groupId) || 0
+    // 判断是否是编辑
+    if (!createState.groupId) {
+      return false
+    }
+    try {
+      createState.loadingStatus = true
+      const res = await request.get(
+        '/api-website/videoLessonGroup/selectVideoLesson',
+        {
+          params: {
+            groupId: createState.groupId
+          }
+        }
+      )
+      const result = res.data
+      const {
+        auditStatus,
+        lessonCoverUrl,
+        lessonPrice,
+        lessonDesc,
+        lessonSubject,
+        lessonName,
+        id,
+        ...group
+      } = result.lessonGroup
+      // 判断模板图片是否在模板列表中,如果不在则是用户自己上传的图片
+      let statusUrl = createState.templateList.includes(lessonCoverUrl)
+        ? true
+        : false
+      createState.lessonGroup = {
+        id: id,
+        lessonName: lessonName,
+        lessonSubject: lessonSubject,
+        lessonDesc: lessonDesc,
+        lessonPrice: lessonPrice,
+        lessonCoverTemplateUrl: statusUrl ? lessonCoverUrl : '',
+        lessonCoverUrl: statusUrl ? '' : lessonCoverUrl
+      }
+      createState.lessonList = []
+      result.detailList &&
+        result.detailList.forEach((item: any) => {
+          createState.lessonList.push({
+            videoTitle: item.videoTitle,
+            videoContent: item.videoContent,
+            videoUrl: item.videoUrl,
+            coverUrl: item.coverUrl,
+            posterUrl: item.posterUrl // 视频封面图
+          })
+        })
+      createState.loadingStatus = false
+    } catch {}
+    if (
+      createState.lessonGroup.lessonCoverUrl &&
+      !createState.templateList.includes(createState.lessonGroup.lessonCoverUrl)
+    ) {
+      createState.tabIndex = 2
+    } else {
+      createState.tabIndex = 1
+    }
+  },
+  async mounted() {
+    try {
+      const sysConfig = await request.get(
+        '/api-website/sysConfig/queryByParamName',
+        {
+          params: {
+            paramName: 'video_lesson_service_fee'
+          }
+        }
+      )
+      createState.rate = sysConfig.data.paramValue
+    } catch {}
+  },
+  render() {
+    return (
+      <>
+        <div class="text-base text-[#333] leading-none px-6 py-5 border-b border-b-[#E5E5E5]">
+          {this.type === 'create' ? '新建视频课' : '编辑视频课'}
+        </div>
+
+        <div class="pt-12">
+          <ColSteps
+            class="px-[200px]"
+            type="large"
+            active={createState.active}
+          />
+
+          {createState.active === 0 && <CourseInfo />}
+          {createState.active === 1 && <CourseContent />}
+        </div>
+      </>
+    )
+  }
+})