lex 2 年之前
父节点
当前提交
0855aa6c96
共有 37 个文件被更改,包括 1051 次插入10 次删除
  1. 7 3
      src/components/albumItem/index.tsx
  2. 0 0
      src/components/col-calendar/index.module.less
  3. 9 0
      src/components/col-calendar/index.tsx
  4. 26 1
      src/router/routes-admin.ts
  5. 0 0
      src/views/student-info/components/item/index.module.less
  6. 42 0
      src/views/student-info/components/item/index.tsx
  7. 二进制
      src/views/student-info/components/user-menu/images/1-active.png
  8. 二进制
      src/views/student-info/components/user-menu/images/1.png
  9. 二进制
      src/views/student-info/components/user-menu/images/2-active.png
  10. 二进制
      src/views/student-info/components/user-menu/images/2.png
  11. 二进制
      src/views/student-info/components/user-menu/images/3-active.png
  12. 二进制
      src/views/student-info/components/user-menu/images/3.png
  13. 二进制
      src/views/student-info/components/user-menu/images/4-active.png
  14. 二进制
      src/views/student-info/components/user-menu/images/4.png
  15. 二进制
      src/views/student-info/components/user-menu/images/5-active.png
  16. 二进制
      src/views/student-info/components/user-menu/images/5.png
  17. 二进制
      src/views/student-info/components/user-menu/images/menu_active.png
  18. 49 0
      src/views/student-info/components/user-menu/index.module.less
  19. 63 0
      src/views/student-info/components/user-menu/index.tsx
  20. 0 0
      src/views/student-info/components/users/index.module.less
  21. 35 0
      src/views/student-info/components/users/index.tsx
  22. 二进制
      src/views/student-info/images/hold.png
  23. 二进制
      src/views/student-info/images/pan.png
  24. 二进制
      src/views/student-info/images/start.png
  25. 21 0
      src/views/student-info/index.tsx
  26. 112 0
      src/views/student-info/my-score/album-list.tsx
  27. 20 0
      src/views/student-info/my-score/index.module.less
  28. 36 0
      src/views/student-info/my-score/index.tsx
  29. 146 0
      src/views/student-info/my-score/list.tsx
  30. 13 1
      src/views/user-info/live-class/index.tsx
  31. 372 0
      src/views/user-info/live-operation/course-info/index.tsx
  32. 7 0
      src/views/user-info/live-operation/course-plan/index.module.less
  33. 0 0
      src/views/user-info/live-operation/course-plan/index.tsx
  34. 53 0
      src/views/user-info/live-operation/createState.ts
  35. 35 0
      src/views/user-info/live-operation/index.tsx
  36. 1 1
      src/views/user-info/video-operation/course-info/index.tsx
  37. 4 4
      src/views/user-info/video-operation/index.tsx

+ 7 - 3
src/components/albumItem/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent,toRefs, reactive, onMounted, ref  } from 'vue'
+import { defineComponent, toRefs, reactive, onMounted, ref } from 'vue'
 
 import classes from './index.module.less'
 import hold from './images/hold.png'
@@ -33,12 +33,16 @@ export default defineComponent({
   },
   setup(props) {
     const state = reactive({
-      detail:props.detail
+      detail: props.detail
     })
     return () => (
       <>
         <div class={classes.itemWrap}>
-          <img src={state.detail.albumCoverUrl?state.detail.albumCoverUrl:hold} alt="" class={classes.hold} />
+          <img
+            src={state.detail.albumCoverUrl ? state.detail.albumCoverUrl : hold}
+            alt=""
+            class={classes.hold}
+          />
           <h2>{state.detail.albumName}</h2>
           <div class={classes.itemBottom}>
             <div class={classes.itemBottomL}>

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


+ 9 - 0
src/components/col-calendar/index.tsx

@@ -0,0 +1,9 @@
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'col-calendar',
+  render() {
+    return <></>
+  }
+})

+ 26 - 1
src/router/routes-admin.ts

@@ -29,7 +29,7 @@ export default [
     meta: {
       title: '谱库',
       index: 2
-    },
+    }
   },
   {
     path: '/searchdetail',
@@ -76,6 +76,17 @@ export default [
         meta: { title: '直播课', index: 3 }
       },
       {
+        path: '/userInfo/liveOperation',
+        name: 'userInfoLiveOperation',
+        component: () => import('@/views/user-info/live-operation'),
+        meta: {
+          title: '直播课',
+          index: 4,
+          hidden: true,
+          activeMenu: 'userInfoLiveClass'
+        }
+      },
+      {
         path: '/userInfo/videoClass',
         name: 'userInfoVideoClass',
         component: () => import('@/views/user-info/video-class'),
@@ -110,5 +121,19 @@ export default [
         }
       }
     ]
+  },
+  {
+    path: '/studentInfo',
+    name: 'studentInfo',
+    component: () => import('@/views/student-info/index'),
+    redirect: '/studentInfo/myScore',
+    children: [
+      {
+        path: '/studentInfo/myScore',
+        name: 'studentInfoMyScore',
+        component: () => import('@/views/student-info/my-score'),
+        meta: { title: '我的曲谱', index: 5 }
+      }
+    ]
   }
 ]

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


+ 42 - 0
src/views/student-info/components/item/index.tsx

@@ -0,0 +1,42 @@
+import { ElImage } from 'element-plus'
+import { defineComponent } from 'vue'
+import pan from '../../images/pan.png'
+import start from '../../images/start.png'
+import hold from '../../images/hold.png'
+
+export default defineComponent({
+  name: 'item',
+  props: {
+    item: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  render() {
+    const item = this.item
+    return (
+      <div class="rounded-sm w-[168px] cursor-pointer overflow-hidden relative transition-all">
+        <ElImage
+          class="w-full h-[168px] align-middle"
+          fit="cover"
+          src={item.albumCoverUrl || hold}
+        />
+
+        <div class="text-lg text-black whitespace-nowrap text-ellipsis overflow-hidden py-1">
+          {item.albumName}
+        </div>
+
+        <div class="flex justify-between text-sm text-[#999]">
+          <div class="flex items-center">
+            <img src={pan} class="w-[18px] h-[18px] mr-1" />
+            <span>{item.musicSheetCount}首</span>
+          </div>
+          <div class="flex items-center ">
+            <img src={start} class="w-[18px] h-[18px] mr-1" />
+            <span>{item.albumFavoriteCount}收藏</span>
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

二进制
src/views/student-info/components/user-menu/images/1-active.png


二进制
src/views/student-info/components/user-menu/images/1.png


二进制
src/views/student-info/components/user-menu/images/2-active.png


二进制
src/views/student-info/components/user-menu/images/2.png


二进制
src/views/student-info/components/user-menu/images/3-active.png


二进制
src/views/student-info/components/user-menu/images/3.png


二进制
src/views/student-info/components/user-menu/images/4-active.png


二进制
src/views/student-info/components/user-menu/images/4.png


二进制
src/views/student-info/components/user-menu/images/5-active.png


二进制
src/views/student-info/components/user-menu/images/5.png


二进制
src/views/student-info/components/user-menu/images/menu_active.png


+ 49 - 0
src/views/student-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;
+    }
+  }
+}

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

@@ -0,0 +1,63 @@
+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 = 'studentInfo'
+
+export default defineComponent({
+  name: 'user-menu',
+  data() {
+    return {}
+  },
+  computed: {
+    activeRoute() {
+      return this.$route.name
+    },
+    acitveMenuRoute() {
+      return this.$route.meta.activeMenu
+    },
+    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) => {
+        if (!item.meta.hidden) {
+          list.push(item)
+        }
+      })
+      return list
+    }
+  },
+  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 ||
+                item.name === this.acitveMenuRoute) &&
+                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/student-info/components/users/index.module.less


+ 35 - 0
src/views/student-info/components/users/index.tsx

@@ -0,0 +1,35 @@
+import { defineComponent } from 'vue'
+import iconTeacher from '@/common/images/icon_teacher.png'
+import { ElTag } from 'element-plus'
+import { state } from '@/state'
+import { userInfo } from 'os'
+
+export default defineComponent({
+  name: 'users',
+  computed: {
+    userInfo() {
+      return state.user.data
+    }
+  },
+  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">{this.userInfo.username}</p>
+
+        <div class="text-[14px] text-[#666] flex items-center justify-center">
+          <span class="flex items-center justify-center leading-6">
+            关注
+            <b class="text-black text-xl pl-1 pb-[2px]">
+              {this.userInfo.fansNum || 0}
+            </b>
+          </span>
+          {/* <span class="pl-3 flex items-center justify-center leading-6">
+            帖子<b class="text-black text-xl pl-1 pb-[2px]">124</b>
+          </span> */}
+        </div>
+      </div>
+    )
+  }
+})

二进制
src/views/student-info/images/hold.png


二进制
src/views/student-info/images/pan.png


二进制
src/views/student-info/images/start.png


+ 21 - 0
src/views/student-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>
+    )
+  }
+})

+ 112 - 0
src/views/student-info/my-score/album-list.tsx

@@ -0,0 +1,112 @@
+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'
+import Item from '../components/item'
+
+export default defineComponent({
+  name: 'list',
+  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.get(
+          '/api-website/music/album/favorite',
+          {
+            params: {
+              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
+        }, 200)
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        <div class="px-2">
+          <ElSkeleton
+            loading={this.loading}
+            animated
+            class="px-[14px] flex items-center flex-row justify-between"
+            count={5}
+            v-slots={{
+              template: () => (
+                <div class="flex items-center flex-col justify-between w-[168px]">
+                  <ElSkeletonItem
+                    variant="image"
+                    style={{ width: '168px', height: '168px' }}
+                  ></ElSkeletonItem>
+                  <ElSkeletonItem
+                    variant="p"
+                    style={{ width: '100%', margin: '4px 0' }}
+                  ></ElSkeletonItem>
+                  <div class="flex w-full justify-between">
+                    <ElSkeletonItem
+                      variant="p"
+                      style={{ width: '35%' }}
+                    ></ElSkeletonItem>
+
+                    <ElSkeletonItem
+                      variant="p"
+                      style={{ width: '35%' }}
+                    ></ElSkeletonItem>
+                  </div>
+                </div>
+              )
+            }}
+          >
+            {this.list.map((item: any) => (
+              <div class="w-1/5">
+                <Item class="m-auto" 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 />}
+      </>
+    )
+  }
+})

+ 20 - 0
src/views/student-info/my-score/index.module.less

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

+ 36 - 0
src/views/student-info/my-score/index.tsx

@@ -0,0 +1,36 @@
+import { ElTabPane, ElTabs } from 'element-plus'
+import { defineComponent } from 'vue'
+import AlbumList from './album-list'
+import styles from './index.module.less'
+import List from './list'
+
+export default defineComponent({
+  name: 'live-class',
+  data() {
+    return {
+      activeName: 'ALBUM'
+    }
+  },
+  methods: {
+    getList() {}
+  },
+  render() {
+    return (
+      <div class={[styles.myScore, 'relative']}>
+        <ElTabs v-model={this.activeName}>
+          <ElTabPane label="我的乐谱" name="MYSCORE">
+            {this.activeName === 'MYSCORE' && <List auditStatus="MYSCORE" />}
+          </ElTabPane>
+          <ElTabPane label="收藏乐谱" name="COLLECTION">
+            {this.activeName === 'COLLECTION' && (
+              <List auditStatus="COLLECTION" />
+            )}
+          </ElTabPane>
+          <ElTabPane label="收藏专辑" name="ALBUM">
+            {this.activeName === 'ALBUM' && <AlbumList />}
+          </ElTabPane>
+        </ElTabs>
+      </div>
+    )
+  }
+})

+ 146 - 0
src/views/student-info/my-score/list.tsx

@@ -0,0 +1,146 @@
+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: ''
+    },
+    onNumber: {
+      type: Function,
+      default: (data: any) => {}
+    }
+  },
+  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 url =
+          this.auditStatus === 'MYSCORE'
+            ? '/api-website/music/sheet/my'
+            : '/api-website/music/sheet/favorite'
+        const { data } = await request.get(url, {
+          params: {
+            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
+        }, 200)
+      }
+    }
+  },
+  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
+                onClick={(item: any) => {
+                  if (this.auditStatus === 'UNPASS') {
+                    console.log(item)
+                    this.$router.push({
+                      path: '/userInfo/musicOperation',
+                      query: {
+                        type: 'update',
+                        id: item.id
+                      }
+                    })
+                  } else {
+                    // 跳转对应详情
+                  }
+                }}
+                item={{
+                  id: item.id,
+                  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}
+        />
+
+        {this.dataShow && <ColEmpty />}
+      </>
+    )
+  }
+})

+ 13 - 1
src/views/user-info/live-class/index.tsx

@@ -18,7 +18,19 @@ export default defineComponent({
   render() {
     return (
       <div class={[styles.liveClass, 'relative']}>
-        <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({
+              path: '/userInfo/liveOperation',
+              query: {
+                type: 'create'
+              }
+            })
+          }}
+        >
           新建直播课
         </ElButton>
         <ElTabs v-model={this.activeName}>

+ 372 - 0
src/views/user-info/live-operation/course-info/index.tsx

@@ -0,0 +1,372 @@
+import ColCropper from '@/components/col-cropper'
+import styles from './index.module.less'
+import request from '@/helpers/request'
+import {
+  verifiyNumberInteger,
+  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'
+import { scrollAnimation } from '@/util/scroll'
+
+export default defineComponent({
+  name: 'course-info',
+  data() {
+    return {
+      url: '',
+      calcRatePrice: 0 as any,
+      calcSingleRatePrice: 0 as any
+    }
+  },
+  computed: {
+    subjectList() {
+      // 学科列表
+      return createState.subjectList || []
+    }
+  },
+  async mounted() {
+    try {
+      // 获取手续费和分钟数
+      let config = await request.get(
+        '/api-website/sysConfig/queryByParamNameList',
+        {
+          params: {
+            paramNames: 'live_service_rate,live_time_setting'
+          }
+        }
+      )
+      let configData = config.data || []
+      configData.forEach((item: any) => {
+        if (item.paramName === 'live_time_setting') {
+          let mins = item.paramValue ? JSON.parse(item.paramValue) : []
+          let tempArr = [] as any
+          mins.forEach((item: any) => {
+            tempArr.push({
+              ...item,
+              name: item.courseMinutes
+            })
+          })
+          createState.minutes = [...tempArr]
+        }
+        if (item.paramName === 'live_service_rate') {
+          createState.rate = item.paramValue
+        }
+      })
+
+      let teacher = await request.post('/api-website/teacher/querySubject')
+      createState.subjectList = teacher.data || []
+    } catch (err: any) {
+      console.log(err)
+    }
+  },
+  methods: {
+    // onChoice(id: number) {
+    //   createState.live.subjectId = id
+    //   this.subjectStatus = false
+    // },
+    onFormaterCourse(e: any) {
+      e.target.value = verifiyNumberInteger(e.target.value)
+      let rate = createState.rate || 0
+      let nums = createState.live.courseNum
+      let tempPrice = createState.live.coursePrice || 0
+      this.calcSingleRatePrice = nums
+        ? ((tempPrice / nums) * (1 - rate / 100)).toFixed(2)
+        : 0
+    },
+    onFormatter(e: any) {
+      e.target.value = verifyNumberIntegerAndFloat(e.target.value)
+
+      // 计算手续费
+      let rate = createState.rate || 0
+      let price = e.target.value || 0
+      this.calcRatePrice = (price - (rate / 100) * price).toFixed(2)
+
+      let nums = createState.live.courseNum
+      // let tempPrice = createState.live.coursePrice || 0
+      this.calcSingleRatePrice = nums
+        ? ((price / nums) * (1 - rate / 100)).toFixed(2)
+        : 0
+    },
+    tabChange(name: number) {
+      ;(this as any).$refs.form.clearValidate('lessonCoverTemplateUrl')
+      ;(this as any).$refs.form.clearValidate('lessonCoverUrl')
+      createState.tabIndex = name
+    },
+    selectImg(val: string) {
+      // createState.live.lessonCoverUrl = ''
+      // createState.live.lessonCoverTemplateUrl = val
+    }
+  },
+  render() {
+    return (
+      <>
+        <ElForm
+          class="px-[200px] pb-10 pt-7"
+          size="large"
+          ref="form"
+          labelWidth={'100px'}
+          labelPosition="left"
+          model={createState.live}
+        >
+          <ElFormItem
+            label="课程名称"
+            prop="name"
+            rules={[
+              {
+                required: true,
+                message: '请输入课程名称'
+              }
+            ]}
+          >
+            <ElInput
+              v-model={createState.live.name}
+              placeholder="请输入课程名称"
+            />
+          </ElFormItem>
+          <ElFormItem
+            label="课程声部"
+            prop="subjectId"
+            rules={[
+              {
+                required: true,
+                message: '请选择课程声部'
+              }
+            ]}
+          >
+            <ElSelect
+              class="w-full"
+              v-model={createState.live.subjectId}
+              placeholder="请选择课程声部"
+            >
+              {createState.subjectList.map((item: any) => (
+                <ElOption key={item.id} value={item.id} label={item.name} />
+              ))}
+            </ElSelect>
+          </ElFormItem>
+          <ElFormItem
+            label="课程介绍"
+            prop="courseIntroduce"
+            rules={[
+              {
+                required: true,
+                message: '请输入课程介绍'
+              }
+            ]}
+          >
+            <ElInput
+              placeholder="请输入课程介绍"
+              v-model={createState.live.courseIntroduce}
+              type="textarea"
+              // @ts-ignore
+              maxlength={200}
+              rows={4}
+              showWordLimit
+            />
+          </ElFormItem>
+
+          <ElFormItem
+            label="课时数"
+            prop="courseNum"
+            rules={[
+              {
+                required: true,
+                message: '请输入课时数'
+              }
+            ]}
+          >
+            <ElInput
+              placeholder="请输入课时数"
+              v-model={createState.live.courseNum}
+              // @ts-ignore
+              onKeyup={this.onFormaterCourse}
+              maxlength={3}
+              v-slots={{
+                append: () => <span class="text-base text-[#333]">课时</span>
+              }}
+            />
+          </ElFormItem>
+          <ElFormItem
+            label="单课时长"
+            prop="singleMins"
+            rules={[
+              {
+                required: true,
+                message: '请选择单课时长'
+              }
+            ]}
+          >
+            <ElSelect
+              class="w-full"
+              v-model={createState.live.singleMins}
+              placeholder="请选择单课时长"
+            >
+              {createState.minutes.map((item: any) => (
+                <ElOption
+                  key={item.courseMinutes}
+                  value={item.courseMinutes}
+                  label={item.name}
+                />
+              ))}
+            </ElSelect>
+          </ElFormItem>
+
+          <ElFormItem
+            label="课程组售价"
+            prop="coursePrice"
+            rules={[
+              {
+                required: true,
+                message: '请输入课程组售价'
+              }
+            ]}
+          >
+            <ElInput
+              placeholder="请输入课程组售价"
+              v-model={createState.live.coursePrice}
+              // @ts-ignore
+              onKeyup={this.onFormatter}
+              maxlength={9}
+              v-slots={{
+                append: () => <span class="text-base text-[#333]">元</span>
+              }}
+            />
+          </ElFormItem>
+          <div class="text-sm text-[#999] pl-[100px] leading-relaxed pb-2">
+            <p>扣除手续费后您的课程预计收入为:</p>
+            <p>
+              单课时
+              <span class="px-1 text-[#FF4E19]">
+                {this.calcSingleRatePrice}
+              </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}
+              class={styles.tabs}
+              onTab-change={(name: any) => {
+                this.tabChange(name)
+              }}
+            >
+              <ElTabPane label="图片模板" name={1}></ElTabPane>
+              <ElTabPane label="自定义模板" name={2}></ElTabPane>
+            </ElTabs>
+          </ElFormItem>
+
+          {createState.tabIndex === 1 && (
+            <ElFormItem
+              prop="lessonCoverTemplateUrl"
+              rules={[
+                {
+                  required: true,
+                  message: '请上传课程封面'
+                }
+              ]}
+            >
+              <ElRadioGroup v-model={createState.live.lessonCoverTemplateUrl}>
+                <ElRow>
+                  {createState.templateList.map((item: any) => (
+                    <ElCol span={10} class="mb-3 cursor-pointer">
+                      <div
+                        class="w-40 relative rounded-xl overflow-hidden border"
+                        onClick={() => {
+                          this.selectImg(item)
+                        }}
+                      >
+                        <ElImage src={item} class="align-middle" />
+                        <ElRadio
+                          label={item}
+                          class="!absolute bottom-2 right-0 !h-auto z-10"
+                        >
+                          {''}
+                        </ElRadio>
+                      </div>
+                    </ElCol>
+                  ))}
+                </ElRow>
+              </ElRadioGroup>
+            </ElFormItem>
+          )}
+
+          {createState.tabIndex === 2 && (
+            <ElFormItem
+              prop="lessonCoverUrl"
+              rules={[
+                {
+                  required: true,
+                  message: '请上传课程封面',
+                  trigger: 'change'
+                }
+              ]}
+            >
+              <ColCropper
+                modelValue={createState.live.lessonCoverUrl}
+                bucket="video-course"
+                cropUploadSuccess={(data: any) => {
+                  createState.live.lessonCoverUrl = data
+                  createState.live.lessonCoverTemplateUrl = ''
+                }}
+                options={{
+                  fixedNumber: [3, 2],
+                  autoCropWidth: 300,
+                  autoCropHeight: 200
+                }}
+              />
+            </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
+              return
+              ;(this as any).$refs.form.validate(async (valid: boolean) => {
+                if (valid) {
+                  createState.active = 1
+                  const currentY =
+                    document.documentElement.scrollTop ||
+                    document.body.scrollTop
+                  scrollAnimation(currentY, 0)
+                } else {
+                  this.$nextTick(() => {
+                    let isError = document.getElementsByClassName('is-error')
+                    isError[0].scrollIntoView({
+                      block: 'center',
+                      behavior: 'smooth'
+                    })
+                  })
+                  return false
+                }
+              })
+            }}
+          >
+            下一步
+          </ElButton>
+        </div>
+      </>
+    )
+  }
+})

+ 7 - 0
src/views/user-info/live-operation/course-plan/index.module.less

@@ -0,0 +1,7 @@
+.tabs {
+  :global {
+    .el-tabs__nav-wrap::after {
+      background-color: transparent;
+    }
+  }
+}

+ 0 - 0
src/views/user-info/live-operation/course-plan/index.tsx


+ 53 - 0
src/views/user-info/live-operation/createState.ts

@@ -0,0 +1,53 @@
+import { reactive } from 'vue'
+
+export const basePlan = {
+  plan: '',
+  startTime: '',
+  endTime: '',
+  classNum: 1
+}
+
+const original = () => {
+  return {
+    subjectList: [], // 声部列表
+    active: 0,
+    rate: 0,
+    minutes: [] as any,
+    tabIndex: 1,
+    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'
+    ], // 模板列表
+    selectCourseList: [] as any, // 选择课程列表
+    coursePlanStatus: false, // 是否有锁课程
+    live: {
+      name: '',
+      subjectId: null as any,
+      courseIntroduce: '',
+      courseNum: null as any,
+      singleCourseMinutes: 0,
+      singleMins: null as any,
+      freeMinutes: 0,
+      coursePrice: null as any,
+      salesStartDate: '',
+      salesEndDate: '',
+      mixStudentNum: null as any,
+      backgroundPic: '',
+      backgroundPicTemplate: '',
+      coursePlanList: [
+        {
+          ...basePlan
+        }
+      ]
+    }
+  }
+}
+
+export const createState = reactive(original())
+
+// 重置对象
+export const resestState = () => {
+  Object.assign(createState, original())
+}

+ 35 - 0
src/views/user-info/live-operation/index.tsx

@@ -0,0 +1,35 @@
+import ColSteps from '@/components/col-steps'
+import request from '@/helpers/request'
+import { defineComponent } from 'vue'
+import CourseInfo from './course-info'
+import { createState } from './createState'
+
+export default defineComponent({
+  name: 'live-operatoin',
+  data() {
+    const query = this.$route.query
+    return {
+      type: query.type || 'create'
+    }
+  },
+  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="small"
+            active={createState.active}
+          />
+
+          {createState.active === 0 && <CourseInfo />}
+          {/* {createState.active === 1 && <CourseContent />} */}
+        </div>
+      </>
+    )
+  }
+})

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

@@ -184,7 +184,7 @@ export default defineComponent({
               }}
             />
           </ElFormItem>
-          <div class="text-sm text-[#999] pl-[90px] leading-relaxed pb-2">
+          <div class="text-sm text-[#999] pl-[100px] leading-relaxed pb-2">
             <p>扣除手续费后您的课程预计收入为: </p>
             <p>
               课程组总收入

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

@@ -46,7 +46,7 @@ export default defineComponent({
       // 判断模板图片是否在模板列表中,如果不在则是用户自己上传的图片
       let statusUrl = createState.templateList.includes(lessonCoverUrl)
         ? true
-        : false;
+        : false
 
       createState.lessonGroup = {
         id: id,
@@ -58,7 +58,7 @@ export default defineComponent({
         lessonCoverUrl: statusUrl ? '' : lessonCoverUrl
       }
 
-      createState.lessonList = [];
+      createState.lessonList = []
 
       result.detailList &&
         result.detailList.forEach((item: any) => {
@@ -71,7 +71,7 @@ export default defineComponent({
           })
         })
       createState.loadingStatus = false
-    } catch { }
+    } catch {}
     if (
       createState.lessonGroup.lessonCoverUrl &&
       !createState.templateList.includes(createState.lessonGroup.lessonCoverUrl)
@@ -92,7 +92,7 @@ export default defineComponent({
         }
       )
       createState.rate = sysConfig.data.paramValue
-    } catch { }
+    } catch {}
   },
   render() {
     return (