فهرست منبع

Merge branch 'colexiu-navbar' into ponline

lex 1 سال پیش
والد
کامیت
d7cc8f8376
68فایلهای تغییر یافته به همراه4610 افزوده شده و 4347 حذف شده
  1. 106 102
      src/business-components/subject-list/index.module.less
  2. 1 1
      src/business-components/subject-list/index.tsx
  3. 30 25
      src/components/col-header/index.module.less
  4. 180 176
      src/components/col-header/index.tsx
  5. 15 2
      src/student/main.ts
  6. 8 0
      src/student/member-center/member-active.module.less
  7. 22 1
      src/student/member-center/member-active.tsx
  8. 2 0
      src/student/share-active/track-review-activity/index.tsx
  9. 328 326
      src/student/teacher-dependent/teacher-follow.tsx
  10. 53 52
      src/student/trade/index.tsx
  11. 337 336
      src/student/trade/list/index.tsx
  12. 10 1
      src/teacher/cash-protocol/index.tsx
  13. 9 5
      src/teacher/extend-plan/detail.tsx
  14. 2 0
      src/teacher/extend-plan/index.tsx
  15. 2 0
      src/teacher/income-consus/index.tsx
  16. 2 0
      src/teacher/leaderboard/index.tsx
  17. 259 257
      src/teacher/live-class/create-components/course.tsx
  18. 2 2
      src/teacher/live-class/create.module.less
  19. 109 106
      src/teacher/live-class/create.tsx
  20. 2 0
      src/teacher/live-class/live-detail.tsx
  21. 5 1
      src/teacher/main.ts
  22. 179 176
      src/teacher/music-cert/index.tsx
  23. 2 0
      src/teacher/music/upload/index.tsx
  24. 91 89
      src/teacher/my-fans/index.tsx
  25. 6 5
      src/teacher/my-sheetMusic/index.module.less
  26. 13 0
      src/teacher/my-sheetMusic/index.tsx
  27. 231 228
      src/teacher/open-live/index.tsx
  28. 1 1
      src/teacher/piano-room/course-record/index.tsx
  29. 464 461
      src/teacher/practice-class/practice-setting.tsx
  30. 7 3
      src/teacher/review/list.tsx
  31. 2 0
      src/teacher/share-page/track-review-activity/index.tsx
  32. 111 105
      src/teacher/teacher-cert/cert-info.tsx
  33. 222 220
      src/teacher/teacher-cert/cert-two.tsx
  34. 236 231
      src/teacher/teacher-cert/index.tsx
  35. 31 24
      src/teacher/teacher-cert/teacher-cert-update/index.tsx
  36. 269 257
      src/teacher/video-class/class-content.tsx
  37. 4 2
      src/teacher/video-class/class-info.tsx
  38. 2 0
      src/teacher/video-class/create-submit.tsx
  39. 50 46
      src/teacher/video-class/create.module.less
  40. 201 199
      src/teacher/video-class/create.tsx
  41. 5 1
      src/teacher/video-class/model/music-album/index.tsx
  42. 5 0
      src/tenant/main.ts
  43. 2 0
      src/views/404/index.tsx
  44. 4 2
      src/views/article-center/help-center-detail.tsx
  45. 3 0
      src/views/article-center/help-center.tsx
  46. 72 70
      src/views/article-center/special-detail.tsx
  47. 128 126
      src/views/article-center/special.tsx
  48. 70 68
      src/views/article-center/theory-detail.tsx
  49. 6 4
      src/views/article-center/theory.tsx
  50. 2 0
      src/views/cart/cart-confirm-agin/index.tsx
  51. 5 3
      src/views/cart/cart-confirm/index.tsx
  52. 2 0
      src/views/contactus/index.tsx
  53. 13 2
      src/views/coupons/index.tsx
  54. 9 9
      src/views/music/album-detail/index.tsx
  55. 5 2
      src/views/music/album/index.tsx
  56. 18 2
      src/views/music/list/index.module.less
  57. 3 3
      src/views/music/list/index.tsx
  58. 2 0
      src/views/music/look-album-list/index.tsx
  59. 2 0
      src/views/music/personal/index.tsx
  60. 5 2
      src/views/music/search/header.tsx
  61. 16 4
      src/views/music/search/index.module.less
  62. 2 0
      src/views/preview-protocol/index.tsx
  63. 381 381
      src/views/protocol/privacy.tsx
  64. 219 219
      src/views/protocol/register.tsx
  65. 2 0
      src/views/scan-login/index.tsx
  66. 2 0
      src/views/share-page/track-review-activity/subject-song.tsx
  67. 19 9
      src/views/shop-mall/goods-list/index.tsx
  68. 2 0
      src/views/shop-mall/shop-order-detail/index.tsx

+ 106 - 102
src/business-components/subject-list/index.module.less

@@ -1,102 +1,106 @@
-.subjects {
-  padding: 15px 0 0;
-  background: #f6f8f9;
-  min-height: calc(100vh - 15px);
-  .subjectContainer {
-    min-height: calc(100vh - 95px);
-  }
-
-  .subjectMaxLength {
-    margin: 0 14px 10px;
-    background: linear-gradient(139deg, #fff6ee 0%, #ffecdd 100%) #ffffff;
-    border-radius: 10px;
-    padding: 7px 11px;
-    background: #ffffff;
-    font-size: 14px;
-    color: #ff9e5a;
-    line-height: 22px;
-  }
-  .title {
-    padding: 12px 0;
-    margin: 0 15px;
-    color: #333;
-    font-size: 16px;
-    display: flex;
-    align-items: center;
-    &::before {
-      content: ' ';
-      display: inline-block;
-      width: 3px;
-      height: 16px;
-      background: #2dc7aa;
-      border-radius: 3px;
-      margin-right: 8px;
-      vertical-align: text-bottom;
-    }
-  }
-
-  .subject-list {
-    display: flex;
-    align-items: center;
-    // justify-content: space-between;
-    // justify-content: center;
-    flex-wrap: wrap;
-    padding: 0 10px;
-
-    .subject-item {
-      position: relative;
-      width: 108px;
-      height: 108px;
-      margin-right: 5px;
-      margin-left: 5px;
-      margin-bottom: 10px;
-      border-radius: 7px;
-      overflow: hidden;
-    }
-
-    .topBg {
-      position: absolute;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      background: linear-gradient(
-        180deg,
-        rgba(0, 0, 0, 0) 0%,
-        rgba(0, 0, 0, 0.54) 100%
-      );
-    }
-
-    .checkbox {
-      position: absolute;
-      right: 7px;
-      top: 7px;
-    }
-
-    .name {
-      position: absolute;
-      bottom: 7px;
-      left: 7px;
-      font-size: 16px;
-      font-weight: 500;
-      color: #ffffff;
-      line-height: 22px;
-    }
-
-    :global {
-      .van-checkbox__icon,
-      .van-radio__icon {
-        height: 22px;
-        .van-icon {
-          border: 0;
-          background-color: transparent;
-        }
-      }
-      .van-checkbox__icon--checked .van-icon,
-      .van-radio__icon--checked .van-icon {
-        background-color: transparent;
-        border: transparent;
-      }
-    }
-  }
-}
+.subjects {
+  padding: 15px 0 0;
+  background: #f6f8f9;
+  min-height: calc(100vh - 15px);
+
+  .subjectContainer {
+    min-height: calc(100vh - 95px);
+  }
+
+  .subjectMaxLength {
+    margin: 0 14px 10px;
+    background: linear-gradient(139deg, #fff6ee 0%, #ffecdd 100%) #ffffff;
+    border-radius: 10px;
+    padding: 7px 11px;
+    background: #ffffff;
+    font-size: 14px;
+    color: #ff9e5a;
+    line-height: 22px;
+  }
+
+  .title {
+    padding: 12px 0;
+    margin: 0 15px;
+    color: #333;
+    font-size: 16px;
+    display: flex;
+    align-items: center;
+
+    &::before {
+      content: ' ';
+      display: inline-block;
+      width: 3px;
+      height: 16px;
+      background: #2dc7aa;
+      border-radius: 3px;
+      margin-right: 8px;
+      vertical-align: text-bottom;
+    }
+  }
+
+  .subject-list {
+    display: flex;
+    align-items: center;
+    // justify-content: space-between;
+    // justify-content: center;
+    flex-wrap: wrap;
+    padding: 0 10px;
+
+    .subject-item {
+      position: relative;
+      width: 108px;
+      height: 108px;
+      margin-right: 5px;
+      margin-left: 5px;
+      margin-bottom: 10px;
+      border-radius: 7px;
+      overflow: hidden;
+    }
+
+    .topBg {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background: linear-gradient(180deg,
+          rgba(0, 0, 0, 0) 0%,
+          rgba(0, 0, 0, 0.54) 100%);
+    }
+
+    .checkbox {
+      position: absolute;
+      right: 7px;
+      top: 7px;
+    }
+
+    .name {
+      position: absolute;
+      bottom: 7px;
+      left: 7px;
+      font-size: 16px;
+      font-weight: 500;
+      color: #ffffff;
+      line-height: 22px;
+    }
+
+    :global {
+
+      .van-checkbox__icon,
+      .van-radio__icon {
+        height: 22px;
+
+        .van-icon {
+          border: 0;
+          background-color: transparent;
+        }
+      }
+
+      .van-checkbox__icon--checked .van-icon,
+      .van-radio__icon--checked .van-icon {
+        background-color: transparent;
+        border: transparent;
+      }
+    }
+  }
+}

+ 1 - 1
src/business-components/subject-list/index.tsx

@@ -21,7 +21,7 @@ export default defineComponent({
   props: {
     onChoice: {
       type: Function,
-      default: (item: any) => { }
+      default: (item: any) => {}
     },
     choiceSubjectIds: {
       type: Array,

+ 30 - 25
src/components/col-header/index.module.less

@@ -1,25 +1,30 @@
-.colHeader {
-  --van-font-weight-bold: 600;
-  :global {
-    .van-nav-bar__title,
-    .van-icon {
-      color: inherit;
-    }
-    .van-nav-bar__right {
-      & > div {
-        line-height: 0;
-      }
-    }
-  }
-  &.transparent {
-    background-color: transparent;
-  }
-}
-
-.headerSection {
-  // min-height: var(--van-nav-bar-height);
-  position: relative;
-  // :global(.van-nav-bar--fixed) {
-  //   box-shadow: 10px 10px 10px var(--box-shadow-color);
-  // }
-}
+.colHeader {
+  --van-font-weight-bold: 600;
+
+  :global {
+
+    .van-nav-bar__title,
+    .van-icon {
+      color: inherit;
+    }
+
+    .van-nav-bar__right {
+      &>div {
+        line-height: 0;
+      }
+    }
+  }
+
+  &.transparent {
+    background-color: transparent;
+  }
+}
+
+.headerSection {
+  // min-height: var(--van-nav-bar-height);
+  position: relative;
+  --van-nav-bar-arrow-size: 22px;
+  // :global(.van-nav-bar--fixed) {
+  //   box-shadow: 10px 10px 10px var(--box-shadow-color);
+  // }
+}

+ 180 - 176
src/components/col-header/index.tsx

@@ -1,176 +1,180 @@
-import { postMessage } from '@/helpers/native-message'
-import { browser } from '@/helpers/utils'
-import { NavBar } from 'vant'
-import { defineComponent, PropType, Teleport } from 'vue'
-import styles from './index.module.less'
-
-type backIconColor = 'black' | 'white'
-
-export default defineComponent({
-  name: 'col-header',
-  props: {
-    title: String,
-    isBack: {
-      type: Boolean,
-      default: false
-    },
-    backIconColor: {
-      // 返回按钮颜色
-      type: String as PropType<backIconColor>,
-      default: 'black'
-    },
-    isFixed: {
-      type: Boolean,
-      default: true
-    },
-    styleName: {
-      type: Object,
-      default: () => ({})
-    },
-    titleClass: String,
-    background: {
-      type: String,
-      default: 'white'
-    },
-    color: {
-      type: String,
-      default: '#323233'
-    },
-    rightText: String,
-    onClickRight: {
-      type: Function,
-      default: () => {}
-    },
-    border: {
-      type: Boolean,
-      default: true
-    },
-    onHeaderBack: {
-      // 头部高度设置后返回
-      type: Function,
-      default: () => {}
-    }
-  },
-  watch: {
-    backIconColor() {
-      // 设置返回按钮颜色
-      postMessage({
-        api: 'backIconChange',
-        content: { iconStyle: this.backIconColor }
-      })
-    }
-  },
-  data() {
-    return {
-      headerTitle: null as any,
-      navBarHeight: 0, // 顶部导航栏高度
-      titleHeight: 44 // 顶部导航高度(默认44px)
-    }
-  },
-  mounted() {
-    this.headerTitle = this.title || this.$route.meta.title
-    this.navBarInit(() => {
-      this.onHeaderBack && this.onHeaderBack()
-    })
-  },
-  unmounted() {
-    // 设置是否显示导航栏 0 显示 1 不显示
-    postMessage({ api: 'setBarStatus', content: { status: 1 } })
-    // 设置返回按钮颜色
-    postMessage({
-      api: 'backIconChange',
-      content: { iconStyle: 'black' as backIconColor }
-    })
-  },
-  methods: {
-    navBarInit(callBack?: Function) {
-      // 设置是否显示导航栏 0 显示 1 不显示
-      postMessage({ api: 'setBarStatus', content: { status: 0 } })
-      // 设置返回按钮颜色
-      postMessage({
-        api: 'backIconChange',
-        content: { iconStyle: this.backIconColor || 'black' }
-      })
-
-      let sNavHeight = sessionStorage.getItem('navHeight')
-      let sTitleHeight = sessionStorage.getItem('titleHeight')
-      if (sNavHeight && sTitleHeight) {
-        this.navBarHeight = Number(sNavHeight)
-        callBack && callBack()
-      } else {
-        postMessage({ api: 'getNavHeight' }, res => {
-          const { content } = res as any
-          const dpi = content.dpi || 2
-          if (content.navHeight) {
-            const navHeight = content.navHeight / dpi
-            sessionStorage.setItem('navHeight', String(navHeight))
-            this.navBarHeight = navHeight
-          }
-          if (content.titleHeight) {
-            // 导航栏的高度
-            const titleHeight = content.titleHeight / dpi
-            sessionStorage.setItem('titleHeight', String(titleHeight))
-            this.titleHeight = titleHeight
-          }
-          callBack && callBack()
-        })
-      }
-      !browser().isApp && callBack && callBack()
-    },
-    onClickLeft() {
-      this.$router.back()
-    },
-    clickRight() {
-      this.onClickRight && this.onClickRight()
-    }
-  },
-  render() {
-    return (
-      <div>
-        {this.$slots.content ? (
-          <div
-            style={{
-              paddingTop: `${this.navBarHeight}px`,
-              background: this.background
-            }}
-            class={styles.headerSection}
-          >
-            {this.$slots.content(this.navBarHeight)}
-          </div>
-        ) : (
-          <>
-            <div
-              // style={{ paddingTop: `${this.navBarHeight}px` }}
-              style={{
-                minHeight: `calc(var(--van-nav-bar-height) + ${this.navBarHeight}px)`
-              }}
-              class={styles.headerSection}
-            >
-              <NavBar
-                title={this.headerTitle}
-                class={[styles.colHeader]}
-                style={{
-                  background: this.background,
-                  color: this.color,
-                  paddingTop: `${this.navBarHeight}px`,
-                  zIndex: 99
-                }}
-                left-arrow={this.isBack}
-                rightText={this.rightText}
-                fixed={this.isFixed}
-                border={this.border}
-                onClick-right={this.clickRight}
-                onClick-left={this.onClickLeft}
-                v-slots={{
-                  right: () =>
-                    (this.$slots.right && this.$slots.right()) || this.rightText
-                }}
-              ></NavBar>
-            </div>
-            {this.$slots.default ? this.$slots.default() : null}
-          </>
-        )}
-      </div>
-    )
-  }
-})
+import { postMessage } from '@/helpers/native-message'
+import { browser } from '@/helpers/utils'
+import { NavBar } from 'vant'
+import { defineComponent, PropType, Teleport } from 'vue'
+import styles from './index.module.less'
+
+type backIconColor = 'black' | 'white'
+
+export default defineComponent({
+  name: 'col-header',
+  props: {
+    title: String,
+    isBack: {
+      type: Boolean,
+      default: true
+    },
+    backIconColor: {
+      // 返回按钮颜色
+      type: String as PropType<backIconColor>,
+      default: 'black'
+    },
+    isFixed: {
+      type: Boolean,
+      default: true
+    },
+    styleName: {
+      type: Object,
+      default: () => ({})
+    },
+    titleClass: String,
+    background: {
+      type: String,
+      default: 'white'
+    },
+    color: {
+      type: String,
+      default: '#323233'
+    },
+    rightText: String,
+    onClickRight: {
+      type: Function,
+      default: () => {}
+    },
+    border: {
+      type: Boolean,
+      default: true
+    },
+    onHeaderBack: {
+      // 头部高度设置后返回
+      type: Function,
+      default: () => {}
+    }
+  },
+  watch: {
+    backIconColor() {
+      // 设置返回按钮颜色
+      postMessage({
+        api: 'backIconChange',
+        content: { iconStyle: this.backIconColor }
+      })
+    }
+  },
+  data() {
+    return {
+      headerTitle: null as any,
+      navBarHeight: 0, // 顶部导航栏高度
+      titleHeight: 44 // 顶部导航高度(默认44px)
+    }
+  },
+  mounted() {
+    this.headerTitle = this.title || this.$route.meta.title
+    this.navBarInit(() => {
+      this.onHeaderBack && this.onHeaderBack()
+    })
+  },
+  unmounted() {
+    // 设置是否显示导航栏 0 显示 1 不显示
+    postMessage({ api: 'setBarStatus', content: { status: 1 } })
+    // 设置返回按钮颜色
+    postMessage({
+      api: 'backIconChange',
+      content: { iconStyle: 'black' as backIconColor }
+    })
+  },
+  methods: {
+    navBarInit(callBack?: Function) {
+      // 设置是否显示导航栏 0 显示 1 不显示
+      postMessage({ api: 'setBarStatus', content: { status: 0 } })
+      // 设置返回按钮颜色
+      postMessage({
+        api: 'backIconChange',
+        content: { iconStyle: this.backIconColor || 'black' }
+      })
+
+      const sNavHeight = sessionStorage.getItem('navHeight')
+      const sTitleHeight = sessionStorage.getItem('titleHeight')
+      if (sNavHeight && sTitleHeight) {
+        this.navBarHeight = Number(sNavHeight)
+        callBack && callBack()
+      } else {
+        postMessage({ api: 'getNavHeight' }, res => {
+          const { content } = res as any
+          const dpi = content.dpi || 2
+          if (content.navHeight) {
+            const navHeight = content.navHeight / dpi
+            sessionStorage.setItem('navHeight', String(navHeight))
+            this.navBarHeight = navHeight
+          }
+          if (content.titleHeight) {
+            // 导航栏的高度
+            const titleHeight = content.titleHeight / dpi
+            sessionStorage.setItem('titleHeight', String(titleHeight))
+            this.titleHeight = titleHeight
+          }
+          callBack && callBack()
+        })
+      }
+      !browser().isApp && callBack && callBack()
+    },
+    onClickLeft() {
+      if (browser().isApp) {
+        postMessage({ api: 'goBack' })
+      } else {
+        this.$router.back()
+      }
+    },
+    clickRight() {
+      this.onClickRight && this.onClickRight()
+    }
+  },
+  render() {
+    return (
+      <div>
+        {this.$slots.content ? (
+          <div
+            style={{
+              paddingTop: `${this.navBarHeight}px`,
+              background: this.background
+            }}
+            class={styles.headerSection}
+          >
+            {this.$slots.content(this.navBarHeight)}
+          </div>
+        ) : (
+          <>
+            <div
+              // style={{ paddingTop: `${this.navBarHeight}px` }}
+              style={{
+                minHeight: `calc(var(--van-nav-bar-height) + ${this.navBarHeight}px)`
+              }}
+              class={styles.headerSection}
+            >
+              <NavBar
+                title={this.headerTitle}
+                class={[styles.colHeader]}
+                style={{
+                  background: this.background,
+                  color: this.color,
+                  paddingTop: `${this.navBarHeight}px`,
+                  zIndex: 99
+                }}
+                left-arrow={this.isBack}
+                rightText={this.rightText}
+                fixed={this.isFixed}
+                border={this.border}
+                onClick-right={this.clickRight}
+                onClick-left={this.onClickLeft}
+                v-slots={{
+                  right: () =>
+                    (this.$slots.right && this.$slots.right()) || this.rightText
+                }}
+              ></NavBar>
+            </div>
+            {this.$slots.default ? this.$slots.default() : null}
+          </>
+        )}
+      </div>
+    )
+  }
+})

+ 15 - 2
src/student/main.ts

@@ -4,7 +4,11 @@ import dayjs from 'dayjs'
 import 'dayjs/locale/zh-cn'
 import router from '../router/index-student'
 import vueFilter from '@/helpers/vueFilter'
-import { listenerMessage, postMessage, promisefiyPostMessage } from '@/helpers/native-message'
+import {
+  listenerMessage,
+  postMessage,
+  promisefiyPostMessage
+} from '@/helpers/native-message'
 
 import 'normalize.css'
 
@@ -16,6 +20,12 @@ const app = createApp(App)
 
 // import Vconsole from 'vconsole'
 // const vconsole = new Vconsole()
+// 设置是否显示导航栏 0 显示 1 不显示
+postMessage({ api: 'setBarStatus', content: { status: 0 } })
+postMessage({
+  api: 'backIconChange',
+  content: { backIconHide: true }
+})
 postMessage(
   {
     api: 'getVersion'
@@ -46,7 +56,10 @@ if (browser().isOrchestraStudent) {
   })
 
   // 从缓存里面获取token
-  promisefiyPostMessage({ api: 'getCache', content: { key: 'h5-colexiu-token' } }).then((res: any) => {
+  promisefiyPostMessage({
+    api: 'getCache',
+    content: { key: 'h5-colexiu-token' }
+  }).then((res: any) => {
     const content = res.content
     if (content.value) {
       setAuth(content.value)

+ 8 - 0
src/student/member-center/member-active.module.less

@@ -2,6 +2,14 @@
   font-size: 0;
   background-color: #ebe2b9;
 
+  .backIcon {
+    position: fixed;
+    top: 20px;
+    font-size: 22px;
+    color: #323233;
+    left: 0.42667rem;
+  }
+
   &>img {
     width: 100%;
     line-height: 0;

+ 22 - 1
src/student/member-center/member-active.tsx

@@ -1,21 +1,42 @@
-import { defineComponent } from 'vue'
+import { defineComponent, onMounted, ref } from 'vue'
 import styles from './member-active.module.less'
 import p1 from './images/active/1.png'
 import p2 from './images/active/2.png'
 import p3 from './images/active/3.png'
 import btn from './images/active/btn.png'
 import { useRouter } from 'vue-router'
+import { postMessage } from '@/helpers/native-message'
+import { Icon } from 'vant'
 
 export default defineComponent({
   setup() {
     const router = useRouter()
+    const navBarHeight = ref(0)
 
     const onDetail = () => {
       router.push('/memberCenter')
     }
 
+    onMounted(() => {
+      postMessage({ api: 'getNavHeight' }, res => {
+        const { content } = res as any
+        const dpi = content.dpi || 2
+        if (content.navHeight) {
+          const navHeight = content.navHeight / dpi
+          navBarHeight.value = navHeight
+        }
+      })
+    })
     return () => (
       <div class={styles.memberActive}>
+        <Icon
+          name="arrow-left"
+          class={styles.backIcon}
+          style={{
+            top: `calc(${navBarHeight.value}px + 12px)`
+          }}
+        />
+
         <img src={p1} />
         <img src={p2} />
         <img src={p3} />

+ 2 - 0
src/student/share-active/track-review-activity/index.tsx

@@ -23,6 +23,7 @@ import { orderStatus } from '@/views/order-detail/orderStatus'
 import { difficulty } from '@/constant'
 import { state } from '@/state'
 import iconTeacher from '@/common/images/icon_teacher.png'
+import ColHeader from '@/components/col-header'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -361,6 +362,7 @@ export default defineComponent({
           backgroundSize: 'contain'
         }}
       >
+        <ColHeader />
         <img class={styles.bannerImg} src={this.activeInfo.subjectUrl} />
         <div class={styles.reviewContainer}>
           <div class={[styles.section, styles.activeTime]}>

+ 328 - 326
src/student/teacher-dependent/teacher-follow.tsx

@@ -1,326 +1,328 @@
-import { Cell, Dialog, Icon, Image, List, Rate, Sticky, Toast } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './model/teacher-header.module.less'
-import iconTeacher from '@common/images/icon_teacher.png'
-import musicCert from '@common/images/music_cert.png'
-import teacherCert from '@common/images/teacher_cert.png'
-import request from '@/helpers/request'
-import ColResult from '@/components/col-result'
-import { postMessage } from '@/helpers/native-message'
-import ColSearch from '@/components/col-search'
-import IconXueli from '@common/images/icon-xueli.png'
-import IconJiaozi from '@common/images/icon-jiaozi.png'
-import dayjs from 'dayjs'
-export const getAssetsHomeFile = (fileName: string) => {
-  const path = `./images/${fileName}`
-  const modules = import.meta.globEager('./images/*')
-  return modules[path].default
-}
-
-export default defineComponent({
-  name: 'teacher-follow',
-  data() {
-    return {
-      userInfo: {} as any,
-      starGrade: 0,
-      subjectNameList: [],
-      list: [],
-      dataShow: true, // 判断是否有数据
-      loading: false,
-      finished: false,
-      params: {
-        username: '',
-        page: 1,
-        rows: 20
-      }
-    }
-  },
-  mounted() {
-    this.getList()
-  },
-  methods: {
-    getSubjectNameList(subject: string) {
-      const subjectList = subject.split(',')
-      return subjectList || []
-    },
-    onSearch(val: string) {
-      this.params.username = val
-      this.onSort()
-    },
-    onSort() {
-      this.params.page = 1
-      this.list = []
-      this.dataShow = true // 判断是否有数据
-      this.loading = false
-      this.finished = false
-      this.getList()
-    },
-    async getList() {
-      try {
-        const params = this.params
-        const res = await request.post('/api-student/student/queryMyFollow', {
-          data: {
-            ...params
-          }
-        })
-        this.loading = false
-        const result = res.data || {}
-        // 处理重复请求数据
-        if (this.list.length > 0 && result.pageNo === 1) {
-          return
-        }
-        this.list = this.list.concat(result.rows || [])
-        this.finished = result.pageNo >= result.totalPage
-        this.params.page = result.pageNo + 1
-        this.dataShow = this.list.length > 0
-      } catch {
-        this.dataShow = false
-        this.finished = true
-      }
-    },
-    async onUnLike(item: any) {
-      Dialog.confirm({
-        message: '确定取消关注吗?',
-        confirmButtonColor: 'var(--van-primary)'
-      }).then(async () => {
-        try {
-          await request.get('/api-student/teacher/starOrUnStar', {
-            params: {
-              userId: item.userId,
-              starStatus: 0
-            }
-          })
-          Toast('取消关注成功')
-          setTimeout(() => {
-            this.onSort()
-          }, 1000)
-        } catch {}
-      })
-    },
-    // 检验是否有对应徽章
-    checkBadge(type: string, item: any) {
-      // tag : 老师点亮图标
-      // STYLE:个人风采
-      // VIDEO:视频课
-      // LIVE:直播课,
-      // MUSIC:曲目 逗号隔开
-      let status = false
-      switch (type) {
-        case 'STYLE':
-        case 'VIDEO':
-        case 'LIVE':
-        case 'MUSIC':
-          if (item.tag) {
-            status = item.tag.indexOf(type) > -1
-          }
-          break
-        case 'VIP':
-          if (item.membershipStartTime && item.membershipEndTime) {
-            const startTime = dayjs(item.membershipStartTime).valueOf()
-            const endTime = dayjs(item.membershipEndTime).valueOf()
-            const nowTime = dayjs().valueOf()
-            status = nowTime >= startTime && nowTime <= endTime
-          } else {
-            status = false
-          }
-          break
-        case 'DEGREE':
-          status = item.degreeFlag ? true : false
-          break
-        case 'TEACHER':
-          status = item.teacherFlag ? true : false
-          break
-        default:
-          status = false
-          break
-      }
-      return status
-    }
-  },
-  render() {
-    return (
-      <div class={styles.teacherFollow}>
-        <Sticky position="top" offsetTop={0}>
-          <ColSearch onSearch={this.onSearch} />
-        </Sticky>
-        {this.dataShow ? (
-          <List
-            class={styles.liveList}
-            v-model:loading={this.loading}
-            immediateCheck={false}
-            finished={this.finished}
-            finishedText="没有更多了"
-          >
-            {this.list.map((item: any) => (
-              <div
-                class={[styles.headerCount, styles.headerFollow]}
-                onClick={(e: MouseEvent) => {
-                  e.stopPropagation()
-                  sessionStorage.removeItem('teacherHomeTabs')
-                  this.$router.push({
-                    path: '/teacherHome',
-                    query: {
-                      teacherId: item.userId,
-                      tabs: 'single'
-                    }
-                  })
-                }}
-              >
-                <div class={styles.followContainer}>
-                  <div class={styles.teacherContent}>
-                    <div
-                      class={styles.teacherIcon}
-                      onClick={e => {
-                        // 判断是否在直播中
-                        if (item.liveFlag === 1) {
-                          e.stopPropagation()
-                          postMessage({
-                            api: 'joinLiveRoom',
-                            content: {
-                              roomId: item.roomUid,
-                              teacherId: item.userId
-                            }
-                          })
-                        }
-                      }}
-                    >
-                      <Image
-                        class={[
-                          styles.avatar,
-                          this.checkBadge('VIP', item.teacher) &&
-                            styles.avatarActive
-                        ]}
-                        round
-                        src={item.avatar || iconTeacher}
-                        fit="cover"
-                      />
-
-                      {item.liveFlag === 1 ? (
-                        <p class={[styles.liveTag, styles.liveTagFollow]}>
-                          直播中
-                        </p>
-                      ) : (
-                        <Image
-                          class={styles.teacherIconVip}
-                          src={
-                            this.checkBadge('VIP', item.teacher)
-                              ? getAssetsHomeFile('vip_active.png')
-                              : getAssetsHomeFile('vip_default.png')
-                          }
-                        />
-                      )}
-                    </div>
-                  </div>
-
-                  <div>
-                    <div class={styles.teacherInfo}>
-                      <div class={styles.teacherInfoName}>
-                        {item.userName || `游客${item.userId || ''}`}
-                      </div>
-                      {this.checkBadge('DEGREE', item.teacher) && (
-                        <img src={IconXueli} />
-                      )}
-                      {this.checkBadge('TEACHER', item.teacher) && (
-                        <img src={IconJiaozi} />
-                      )}
-                    </div>
-                    <div class={styles.piNameSubject}>
-                      {/* <Image
-                        class={styles.subjectSection}
-                        src={getAssetsHomeFile('icon_subject.png')}
-                        fit="contain"
-                      /> */}
-                      <div class={styles.subjectList}>
-                        {this.getSubjectNameList(item.subjectName).map(
-                          (item: any) => (
-                            <span class={styles.subject}>{item}</span>
-                          )
-                        )}
-                      </div>
-                    </div>
-                  </div>
-                </div>
-                <div class={styles.teacherHonor}>
-                  <div>勋章:</div>
-                  <div class={styles.teacherIcons}>
-                    <Image
-                      class={styles.iconOther}
-                      src={
-                        this.checkBadge('STYLE', item.teacher)
-                          ? getAssetsHomeFile('cert_active.png')
-                          : getAssetsHomeFile('cert_default.png')
-                      }
-                    />
-                    <Image
-                      class={styles.iconOther}
-                      src={
-                        this.checkBadge('VIDEO', item.teacher)
-                          ? getAssetsHomeFile('video_active.png')
-                          : getAssetsHomeFile('video_default.png')
-                      }
-                    />
-                    <Image
-                      class={styles.iconOther}
-                      src={
-                        this.checkBadge('LIVE', item.teacher)
-                          ? getAssetsHomeFile('live_active.png')
-                          : getAssetsHomeFile('live_default.png')
-                      }
-                    />
-                    <Image
-                      class={styles.iconOther}
-                      src={
-                        this.checkBadge('MUSIC', item.teacher)
-                          ? getAssetsHomeFile('music_active.png')
-                          : getAssetsHomeFile('music_default.png')
-                      }
-                    />
-                  </div>
-                  <div class={styles.score}>评分: </div>
-                  <div class={styles.level}>
-                    {item.starGrade ? (
-                      <Rate
-                        readonly
-                        modelValue={item.starGrade}
-                        iconPrefix="iconfont"
-                        color="#FFC459"
-                        void-icon="star_default"
-                        icon="star_active"
-                        size={15}
-                      />
-                    ) : (
-                      <span style={{ fontSize: '12px', color: '#999999' }}>
-                        暂无评分
-                      </span>
-                    )}
-                  </div>
-                </div>
-                <div class={[styles['teacher-bottom'], styles.followFans]}>
-                  <div class={styles['teacher-data']}>
-                    <div class={styles['teacher-data_item']}>
-                      粉丝 <span>{item.fansNum || 0}</span>
-                    </div>
-                    <div class={styles['teacher-data_item']}>
-                      已上课时 <span>{item.expTime || 0}</span>
-                    </div>
-                  </div>
-                  <div
-                    class={styles.unlinkeBtn}
-                    onClick={(e: MouseEvent) => {
-                      e.stopPropagation()
-                      this.onUnLike(item)
-                    }}
-                  >
-                    取消关注
-                  </div>
-                </div>
-              </div>
-            ))}
-          </List>
-        ) : (
-          <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无关注" />
-        )}
-      </div>
-    )
-  }
-})
+import { Cell, Dialog, Icon, Image, List, Rate, Sticky, Toast } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './model/teacher-header.module.less'
+import iconTeacher from '@common/images/icon_teacher.png'
+import musicCert from '@common/images/music_cert.png'
+import teacherCert from '@common/images/teacher_cert.png'
+import request from '@/helpers/request'
+import ColResult from '@/components/col-result'
+import { postMessage } from '@/helpers/native-message'
+import ColSearch from '@/components/col-search'
+import IconXueli from '@common/images/icon-xueli.png'
+import IconJiaozi from '@common/images/icon-jiaozi.png'
+import dayjs from 'dayjs'
+import ColHeader from '@/components/col-header'
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `./images/${fileName}`
+  const modules = import.meta.globEager('./images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'teacher-follow',
+  data() {
+    return {
+      userInfo: {} as any,
+      starGrade: 0,
+      subjectNameList: [],
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      params: {
+        username: '',
+        page: 1,
+        rows: 20
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getSubjectNameList(subject: string) {
+      const subjectList = subject.split(',')
+      return subjectList || []
+    },
+    onSearch(val: string) {
+      this.params.username = val
+      this.onSort()
+    },
+    onSort() {
+      this.params.page = 1
+      this.list = []
+      this.dataShow = true // 判断是否有数据
+      this.loading = false
+      this.finished = false
+      this.getList()
+    },
+    async getList() {
+      try {
+        const params = this.params
+        const res = await request.post('/api-student/student/queryMyFollow', {
+          data: {
+            ...params
+          }
+        })
+        this.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+    },
+    async onUnLike(item: any) {
+      Dialog.confirm({
+        message: '确定取消关注吗?',
+        confirmButtonColor: 'var(--van-primary)'
+      }).then(async () => {
+        try {
+          await request.get('/api-student/teacher/starOrUnStar', {
+            params: {
+              userId: item.userId,
+              starStatus: 0
+            }
+          })
+          Toast('取消关注成功')
+          setTimeout(() => {
+            this.onSort()
+          }, 1000)
+        } catch {}
+      })
+    },
+    // 检验是否有对应徽章
+    checkBadge(type: string, item: any) {
+      // tag : 老师点亮图标
+      // STYLE:个人风采
+      // VIDEO:视频课
+      // LIVE:直播课,
+      // MUSIC:曲目 逗号隔开
+      let status = false
+      switch (type) {
+        case 'STYLE':
+        case 'VIDEO':
+        case 'LIVE':
+        case 'MUSIC':
+          if (item.tag) {
+            status = item.tag.indexOf(type) > -1
+          }
+          break
+        case 'VIP':
+          if (item.membershipStartTime && item.membershipEndTime) {
+            const startTime = dayjs(item.membershipStartTime).valueOf()
+            const endTime = dayjs(item.membershipEndTime).valueOf()
+            const nowTime = dayjs().valueOf()
+            status = nowTime >= startTime && nowTime <= endTime
+          } else {
+            status = false
+          }
+          break
+        case 'DEGREE':
+          status = item.degreeFlag ? true : false
+          break
+        case 'TEACHER':
+          status = item.teacherFlag ? true : false
+          break
+        default:
+          status = false
+          break
+      }
+      return status
+    }
+  },
+  render() {
+    return (
+      <div class={styles.teacherFollow}>
+        <Sticky position="top" offsetTop={0}>
+          <ColHeader border={false} />
+          <ColSearch onSearch={this.onSearch} />
+        </Sticky>
+        {this.dataShow ? (
+          <List
+            class={styles.liveList}
+            v-model:loading={this.loading}
+            immediateCheck={false}
+            finished={this.finished}
+            finishedText="没有更多了"
+          >
+            {this.list.map((item: any) => (
+              <div
+                class={[styles.headerCount, styles.headerFollow]}
+                onClick={(e: MouseEvent) => {
+                  e.stopPropagation()
+                  sessionStorage.removeItem('teacherHomeTabs')
+                  this.$router.push({
+                    path: '/teacherHome',
+                    query: {
+                      teacherId: item.userId,
+                      tabs: 'single'
+                    }
+                  })
+                }}
+              >
+                <div class={styles.followContainer}>
+                  <div class={styles.teacherContent}>
+                    <div
+                      class={styles.teacherIcon}
+                      onClick={e => {
+                        // 判断是否在直播中
+                        if (item.liveFlag === 1) {
+                          e.stopPropagation()
+                          postMessage({
+                            api: 'joinLiveRoom',
+                            content: {
+                              roomId: item.roomUid,
+                              teacherId: item.userId
+                            }
+                          })
+                        }
+                      }}
+                    >
+                      <Image
+                        class={[
+                          styles.avatar,
+                          this.checkBadge('VIP', item.teacher) &&
+                            styles.avatarActive
+                        ]}
+                        round
+                        src={item.avatar || iconTeacher}
+                        fit="cover"
+                      />
+
+                      {item.liveFlag === 1 ? (
+                        <p class={[styles.liveTag, styles.liveTagFollow]}>
+                          直播中
+                        </p>
+                      ) : (
+                        <Image
+                          class={styles.teacherIconVip}
+                          src={
+                            this.checkBadge('VIP', item.teacher)
+                              ? getAssetsHomeFile('vip_active.png')
+                              : getAssetsHomeFile('vip_default.png')
+                          }
+                        />
+                      )}
+                    </div>
+                  </div>
+
+                  <div>
+                    <div class={styles.teacherInfo}>
+                      <div class={styles.teacherInfoName}>
+                        {item.userName || `游客${item.userId || ''}`}
+                      </div>
+                      {this.checkBadge('DEGREE', item.teacher) && (
+                        <img src={IconXueli} />
+                      )}
+                      {this.checkBadge('TEACHER', item.teacher) && (
+                        <img src={IconJiaozi} />
+                      )}
+                    </div>
+                    <div class={styles.piNameSubject}>
+                      {/* <Image
+                        class={styles.subjectSection}
+                        src={getAssetsHomeFile('icon_subject.png')}
+                        fit="contain"
+                      /> */}
+                      <div class={styles.subjectList}>
+                        {this.getSubjectNameList(item.subjectName).map(
+                          (item: any) => (
+                            <span class={styles.subject}>{item}</span>
+                          )
+                        )}
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div class={styles.teacherHonor}>
+                  <div>勋章:</div>
+                  <div class={styles.teacherIcons}>
+                    <Image
+                      class={styles.iconOther}
+                      src={
+                        this.checkBadge('STYLE', item.teacher)
+                          ? getAssetsHomeFile('cert_active.png')
+                          : getAssetsHomeFile('cert_default.png')
+                      }
+                    />
+                    <Image
+                      class={styles.iconOther}
+                      src={
+                        this.checkBadge('VIDEO', item.teacher)
+                          ? getAssetsHomeFile('video_active.png')
+                          : getAssetsHomeFile('video_default.png')
+                      }
+                    />
+                    <Image
+                      class={styles.iconOther}
+                      src={
+                        this.checkBadge('LIVE', item.teacher)
+                          ? getAssetsHomeFile('live_active.png')
+                          : getAssetsHomeFile('live_default.png')
+                      }
+                    />
+                    <Image
+                      class={styles.iconOther}
+                      src={
+                        this.checkBadge('MUSIC', item.teacher)
+                          ? getAssetsHomeFile('music_active.png')
+                          : getAssetsHomeFile('music_default.png')
+                      }
+                    />
+                  </div>
+                  <div class={styles.score}>评分: </div>
+                  <div class={styles.level}>
+                    {item.starGrade ? (
+                      <Rate
+                        readonly
+                        modelValue={item.starGrade}
+                        iconPrefix="iconfont"
+                        color="#FFC459"
+                        void-icon="star_default"
+                        icon="star_active"
+                        size={15}
+                      />
+                    ) : (
+                      <span style={{ fontSize: '12px', color: '#999999' }}>
+                        暂无评分
+                      </span>
+                    )}
+                  </div>
+                </div>
+                <div class={[styles['teacher-bottom'], styles.followFans]}>
+                  <div class={styles['teacher-data']}>
+                    <div class={styles['teacher-data_item']}>
+                      粉丝 <span>{item.fansNum || 0}</span>
+                    </div>
+                    <div class={styles['teacher-data_item']}>
+                      已上课时 <span>{item.expTime || 0}</span>
+                    </div>
+                  </div>
+                  <div
+                    class={styles.unlinkeBtn}
+                    onClick={(e: MouseEvent) => {
+                      e.stopPropagation()
+                      this.onUnLike(item)
+                    }}
+                  >
+                    取消关注
+                  </div>
+                </div>
+              </div>
+            ))}
+          </List>
+        ) : (
+          <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无关注" />
+        )}
+      </div>
+    )
+  }
+})

+ 53 - 52
src/student/trade/index.tsx

@@ -1,52 +1,53 @@
-import { Cell, Sticky, Tab, Tabs } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-import List from './list'
-import { useRect } from '@vant/use'
-import { useEventTracking } from '@/helpers/hooks'
-
-export default defineComponent({
-  name: 'tradeRecord',
-  data() {
-    return {
-      active: 'buy',
-      height: 44
-    }
-  },
-  mounted() {
-    this.$nextTick(() => {
-      const { height } = useRect((this as any).$refs.tabs)
-      // console.log(height, (this as any).$refs.tabs)
-      this.height = height
-    })
-    useEventTracking('交易记录')
-  },
-  render() {
-    return (
-      <div class={styles.tradeRecord}>
-        <Tabs
-          v-model:active={this.active}
-          color="var(--van-primary)"
-          sticky
-          lineWidth={28}
-        >
-          <Tab
-            name="buy"
-            v-slots={{
-              title: () => (
-                <div class={styles.tab} ref="tabs">
-                  购买记录
-                </div>
-              )
-            }}
-          >
-            <List height={this.height} />
-          </Tab>
-          <Tab name="refund" title="退费记录">
-            <List height={this.height} type="refund" />
-          </Tab>
-        </Tabs>
-      </div>
-    )
-  }
-})
+import { Cell, Sticky, Tab, Tabs } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import List from './list'
+import { useRect } from '@vant/use'
+import { useEventTracking } from '@/helpers/hooks'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
+
+export default defineComponent({
+  name: 'tradeRecord',
+  data() {
+    return {
+      active: 'buy',
+      height: 44
+    }
+  },
+  mounted() {
+    // this.$nextTick(() => {
+    //   const { height } = useRect((this as any).$refs.tabs)
+    //   this.height = height
+    // })
+    useEventTracking('交易记录')
+  },
+  render() {
+    return (
+      <div class={styles.tradeRecord}>
+        <TheSticky
+          position="top"
+          onBarHeight={(height: any) => {
+            this.height = height
+          }}
+        >
+          <ColHeader isFixed={false} />
+          <Tabs
+            v-model:active={this.active}
+            color="var(--van-primary)"
+            sticky
+            lineWidth={28}
+          >
+            <Tab name="buy" title="购买记录"></Tab>
+            <Tab name="refund" title="退费记录"></Tab>
+          </Tabs>
+        </TheSticky>
+
+        {this.active === 'buy' && <List height={this.height} />}
+        {this.active === 'refund' && (
+          <List height={this.height} type="refund" />
+        )}
+      </div>
+    )
+  }
+})

+ 337 - 336
src/student/trade/list/index.tsx

@@ -1,336 +1,337 @@
-import { defineComponent, PropType } from 'vue'
-import styles from './index.module.less'
-import {
-  ActionSheet,
-  Cell,
-  CellGroup,
-  DatetimePicker,
-  Icon,
-  Popup,
-  Sticky,
-  Image,
-  List,
-  Button,
-  Dialog,
-  Toast
-} from 'vant'
-import { formatterDate } from '@/helpers/utils'
-import { goodsType, orderType, returnType } from '@/constant'
-
-import iconTeacher from '@common/images/icon_teacher.png'
-import request from '@/helpers/request'
-import dayjs from 'dayjs'
-import ColResult from '@/components/col-result'
-import { orderStatus } from '@/views/order-detail/orderStatus'
-import { tradeOrder } from '../tradeOrder'
-
-export default defineComponent({
-  name: 'list',
-  props: {
-    type: {
-      type: String as PropType<'buy' | 'refund'>,
-      default: 'buy'
-    },
-    height: {
-      type: Number,
-      default: 44
-    }
-  },
-  data() {
-    return {
-      timeStatus: false,
-      currentDate: new Date(),
-      typeStatus: false,
-      // 订单状态 WAIT_PAY 待支付 PAYING 支付中 PAID 已付款 CLOSE 已关闭 FAIL 支付失败 (多选用,分割)
-      actions: [
-        { name: '全部' },
-        { name: '待支付', status: 'WAIT_PAY' },
-        { name: '支付中', status: 'PAYING' },
-        { name: '已付款', status: 'PAID' },
-        { name: '已关闭', status: 'CLOSE' },
-        { name: '支付失败', status: 'FAIL' }
-      ],
-      list: [],
-      dataShow: true, // 判断是否有数据
-      loading: false,
-      finished: false,
-      searchName: '全部',
-      params: {
-        status: '',
-        page: 1,
-        rows: 20
-      }
-    }
-  },
-  methods: {
-    async getList() {
-      if (this.loading) return
-      this.loading = true
-      try {
-        const params = {
-          ...this.params,
-          searchDate: dayjs(this.currentDate).format('YYYY-MM')
-        }
-
-        const url =
-          this.type === 'buy'
-            ? '/api-student/userOrder/page'
-            : '/api-student/userOrderRefunds/page'
-        const { code, data } = await request.post(url, {
-          data: {
-            ...params,
-            dateTime: this.type === 'refund' ? params.searchDate : undefined,
-            timeType: this.type === 'refund' ? 'MONTH' : undefined
-          }
-        })
-        if (code === 200) {
-          const result = data || {}
-          this.list = this.list.concat(result.rows || [])
-          this.finished = result.pageNo >= result.totalPage
-          this.params.page = result.pageNo + 1
-          this.dataShow = this.list.length > 0
-        }
-      } catch {
-        this.dataShow = false
-        this.finished = true
-      }
-      this.loading = false
-    },
-    onDetail(item: any) {
-      if (this.type === 'refund') return
-      this.$router.push({
-        path: '/tradeDetail',
-        query: {
-          orderNo: item.orderNo,
-          path: 'tradeRecord'
-        }
-      })
-    },
-    onConfirm(date: Date) {
-      this.currentDate = date
-      this.timeStatus = false
-      this.onSearch()
-    },
-    onSelect(item: any) {
-      this.params.status = item.status
-      this.searchName = item.name
-      this.onSearch()
-    },
-    onSearch() {
-      this.dataShow = true
-      this.loading = false
-      this.finished = false
-      this.list = []
-      this.params.page = 1
-      this.getList()
-    },
-    async onCancelPay(item: any) {
-      // orderPay: {
-      //   cancelUrl: '/api-student/userOrder/orderCancel',
-      //   payUrl: '/api-student/userOrder/orderPay'
-      // }
-      Dialog.confirm({
-        message: '是否取消订单?',
-        confirmButtonText: '确定',
-        confirmButtonColor: 'var(--van-primary)',
-        cancelButtonText: '取消'
-      }).then(async () => {
-        try {
-          await request.post('/api-student/userOrder/orderCancel', {
-            data: {
-              orderNo: item.orderNo
-            }
-          })
-          // Toast('取消成功')
-          this.onSearch()
-        } catch {}
-      })
-    },
-    async onPay(item: any) {
-      try {
-        const res = await request.get(
-          `/api-student/userOrder/detailByOrderNo/${item.orderNo}`
-        )
-        const result = res.data
-        tradeOrder(result, () => {
-          this.$router.push({
-            path: '/orderDetail',
-            query: {
-              orderType: result.orderType
-            }
-          })
-        })
-      } catch {}
-    }
-  },
-  render() {
-    return (
-      <div class={styles.tradeList}>
-        <Sticky position="top" offsetTop={this.height}>
-          <Cell
-            center
-            style={{ backgroundColor: '#F7F8F9' }}
-            v-slots={{
-              title: () => (
-                <div
-                  class={styles.searchTime}
-                  onClick={() => {
-                    this.timeStatus = true
-                  }}
-                >
-                  <span>
-                    {(this as any).$filters.dateFormat(
-                      this.currentDate,
-                      'YYYY-MM'
-                    )}
-                  </span>
-                  <Icon
-                    classPrefix="iconfont"
-                    name="down"
-                    size={12}
-                    color="var(--van-primary)"
-                  />
-                </div>
-              ),
-              value: () => {
-                if (this.type === 'buy') {
-                  return (
-                    <div
-                      class={styles.searchType}
-                      onClick={() => {
-                        this.typeStatus = true
-                      }}
-                    >
-                      <span>{this.searchName}</span>
-                      <Icon
-                        classPrefix="iconfont"
-                        name="down"
-                        size={12}
-                        color="var(--van-primary)"
-                      />
-                    </div>
-                  )
-                }
-                return null
-              }
-            }}
-          ></Cell>
-        </Sticky>
-        {this.dataShow ? (
-          <List
-            loading={this.loading}
-            finished={this.finished}
-            finishedText=" "
-            class={[styles.list]}
-            onLoad={this.getList}
-          >
-            {this.list.map((item: any) => (
-              <CellGroup
-                border={false}
-                onClick={() => {
-                  this.onDetail(item)
-                }}
-              >
-                <Cell
-                  title={dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
-                  value={
-                    this.type === 'buy'
-                      ? orderType[item.status]
-                      : item.operateReason
-                  }
-                  valueClass={styles.tradeType}
-                />
-                <Cell
-                  border={false}
-                  v-slots={{
-                    title: () => (
-                      <div class={styles.title}>
-                        <span>{item.orderName}</span>
-                        <span class={styles.desc}>
-                          {goodsType[item.orderType]}
-                        </span>
-                      </div>
-                    ),
-                    default: () => (
-                      <div class={styles.content}>
-                        <span class={styles.price}>
-                          ¥
-                          {this.type === 'buy'
-                            ? (this as any).$filters.moneyFormat(
-                                item.actualPrice
-                              )
-                            : (this as any).$filters.moneyFormat(
-                                item.actualAmount
-                              )}
-                        </span>
-                      </div>
-                    )
-                  }}
-                />
-                {item.status === 'PAYING' || item.status === 'WAIT_PAY' ? (
-                  <div class={styles.btnList}>
-                    <Button
-                      size="small"
-                      round
-                      onClick={(e: any) => {
-                        e.stopPropagation()
-                        this.onCancelPay(item)
-                      }}
-                    >
-                      取消订单
-                    </Button>
-                    <Button
-                      size="small"
-                      round
-                      type="primary"
-                      onClick={(e: any) => {
-                        e.stopPropagation()
-                        this.onPay(item)
-                      }}
-                    >
-                      继续支付
-                    </Button>
-                  </div>
-                ) : null}
-              </CellGroup>
-            ))}
-          </List>
-        ) : (
-          <ColResult
-            btnStatus={false}
-            classImgSize="SMALL"
-            tips={this.type === 'buy' ? '暂无购买记录' : '暂无退款记录'}
-          />
-        )}
-
-        <Popup
-          v-model:show={this.timeStatus}
-          position="bottom"
-          round
-          closeOnPopstate
-        >
-          <DatetimePicker
-            type="year-month"
-            v-model={this.currentDate}
-            formatter={formatterDate}
-            onCancel={() => {
-              this.timeStatus = false
-            }}
-            onConfirm={this.onConfirm}
-          />
-        </Popup>
-
-        <ActionSheet
-          v-model:show={this.typeStatus}
-          actions={this.actions}
-          closeOnClickAction
-          cancelText="取消"
-          onSelect={this.onSelect}
-          onCancel={() => {
-            this.typeStatus = false
-          }}
-        />
-      </div>
-    )
-  }
-})
+import { defineComponent, PropType } from 'vue'
+import styles from './index.module.less'
+import {
+  ActionSheet,
+  Cell,
+  CellGroup,
+  DatetimePicker,
+  Icon,
+  Popup,
+  Sticky,
+  Image,
+  List,
+  Button,
+  Dialog,
+  Toast
+} from 'vant'
+import { formatterDate } from '@/helpers/utils'
+import { goodsType, orderType, returnType } from '@/constant'
+
+import iconTeacher from '@common/images/icon_teacher.png'
+import request from '@/helpers/request'
+import dayjs from 'dayjs'
+import ColResult from '@/components/col-result'
+import { orderStatus } from '@/views/order-detail/orderStatus'
+import { tradeOrder } from '../tradeOrder'
+
+export default defineComponent({
+  name: 'list',
+  props: {
+    type: {
+      type: String as PropType<'buy' | 'refund'>,
+      default: 'buy'
+    },
+    height: {
+      type: Number,
+      default: 44
+    }
+  },
+  data() {
+    return {
+      timeStatus: false,
+      currentDate: new Date(),
+      typeStatus: false,
+      // 订单状态 WAIT_PAY 待支付 PAYING 支付中 PAID 已付款 CLOSE 已关闭 FAIL 支付失败 (多选用,分割)
+      actions: [
+        { name: '全部' },
+        { name: '待支付', status: 'WAIT_PAY' },
+        { name: '支付中', status: 'PAYING' },
+        { name: '已付款', status: 'PAID' },
+        { name: '已关闭', status: 'CLOSE' },
+        { name: '支付失败', status: 'FAIL' }
+      ],
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      searchName: '全部',
+      params: {
+        status: '',
+        page: 1,
+        rows: 20
+      }
+    }
+  },
+  methods: {
+    async getList() {
+      if (this.loading) return
+      this.loading = true
+      try {
+        const params = {
+          ...this.params,
+          searchDate: dayjs(this.currentDate).format('YYYY-MM')
+        }
+
+        const url =
+          this.type === 'buy'
+            ? '/api-student/userOrder/page'
+            : '/api-student/userOrderRefunds/page'
+        const { code, data } = await request.post(url, {
+          data: {
+            ...params,
+            dateTime: this.type === 'refund' ? params.searchDate : undefined,
+            timeType: this.type === 'refund' ? 'MONTH' : undefined
+          }
+        })
+        if (code === 200) {
+          const result = data || {}
+          this.list = this.list.concat(result.rows || [])
+          this.finished = result.pageNo >= result.totalPage
+          this.params.page = result.pageNo + 1
+          this.dataShow = this.list.length > 0
+        }
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+      this.loading = false
+    },
+    onDetail(item: any) {
+      if (this.type === 'refund') return
+      this.$router.push({
+        path: '/tradeDetail',
+        query: {
+          orderNo: item.orderNo,
+          path: 'tradeRecord'
+        }
+      })
+    },
+    onConfirm(date: Date) {
+      this.currentDate = date
+      this.timeStatus = false
+      this.onSearch()
+    },
+    onSelect(item: any) {
+      this.params.status = item.status
+      this.searchName = item.name
+      this.onSearch()
+    },
+    onSearch() {
+      this.dataShow = true
+      this.loading = false
+      this.finished = false
+      this.list = []
+      this.params.page = 1
+      this.getList()
+    },
+    async onCancelPay(item: any) {
+      // orderPay: {
+      //   cancelUrl: '/api-student/userOrder/orderCancel',
+      //   payUrl: '/api-student/userOrder/orderPay'
+      // }
+      Dialog.confirm({
+        message: '是否取消订单?',
+        confirmButtonText: '确定',
+        confirmButtonColor: 'var(--van-primary)',
+        cancelButtonText: '取消'
+      }).then(async () => {
+        try {
+          await request.post('/api-student/userOrder/orderCancel', {
+            data: {
+              orderNo: item.orderNo
+            }
+          })
+          // Toast('取消成功')
+          this.onSearch()
+        } catch {}
+      })
+    },
+    async onPay(item: any) {
+      try {
+        const res = await request.get(
+          `/api-student/userOrder/detailByOrderNo/${item.orderNo}`
+        )
+        const result = res.data
+        tradeOrder(result, () => {
+          this.$router.push({
+            path: '/orderDetail',
+            query: {
+              orderType: result.orderType
+            }
+          })
+        })
+      } catch {}
+    }
+  },
+  render() {
+    console.log(this.height, 'height')
+    return (
+      <div class={styles.tradeList}>
+        <Sticky position="top" offsetTop={this.height}>
+          <Cell
+            center
+            style={{ backgroundColor: '#F7F8F9' }}
+            v-slots={{
+              title: () => (
+                <div
+                  class={styles.searchTime}
+                  onClick={() => {
+                    this.timeStatus = true
+                  }}
+                >
+                  <span>
+                    {(this as any).$filters.dateFormat(
+                      this.currentDate,
+                      'YYYY-MM'
+                    )}
+                  </span>
+                  <Icon
+                    classPrefix="iconfont"
+                    name="down"
+                    size={12}
+                    color="var(--van-primary)"
+                  />
+                </div>
+              ),
+              value: () => {
+                if (this.type === 'buy') {
+                  return (
+                    <div
+                      class={styles.searchType}
+                      onClick={() => {
+                        this.typeStatus = true
+                      }}
+                    >
+                      <span>{this.searchName}</span>
+                      <Icon
+                        classPrefix="iconfont"
+                        name="down"
+                        size={12}
+                        color="var(--van-primary)"
+                      />
+                    </div>
+                  )
+                }
+                return null
+              }
+            }}
+          ></Cell>
+        </Sticky>
+        {this.dataShow ? (
+          <List
+            loading={this.loading}
+            finished={this.finished}
+            finishedText=" "
+            class={[styles.list]}
+            onLoad={this.getList}
+          >
+            {this.list.map((item: any) => (
+              <CellGroup
+                border={false}
+                onClick={() => {
+                  this.onDetail(item)
+                }}
+              >
+                <Cell
+                  title={dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
+                  value={
+                    this.type === 'buy'
+                      ? orderType[item.status]
+                      : item.operateReason
+                  }
+                  valueClass={styles.tradeType}
+                />
+                <Cell
+                  border={false}
+                  v-slots={{
+                    title: () => (
+                      <div class={styles.title}>
+                        <span>{item.orderName}</span>
+                        <span class={styles.desc}>
+                          {goodsType[item.orderType]}
+                        </span>
+                      </div>
+                    ),
+                    default: () => (
+                      <div class={styles.content}>
+                        <span class={styles.price}>
+                          ¥
+                          {this.type === 'buy'
+                            ? (this as any).$filters.moneyFormat(
+                                item.actualPrice
+                              )
+                            : (this as any).$filters.moneyFormat(
+                                item.actualAmount
+                              )}
+                        </span>
+                      </div>
+                    )
+                  }}
+                />
+                {item.status === 'PAYING' || item.status === 'WAIT_PAY' ? (
+                  <div class={styles.btnList}>
+                    <Button
+                      size="small"
+                      round
+                      onClick={(e: any) => {
+                        e.stopPropagation()
+                        this.onCancelPay(item)
+                      }}
+                    >
+                      取消订单
+                    </Button>
+                    <Button
+                      size="small"
+                      round
+                      type="primary"
+                      onClick={(e: any) => {
+                        e.stopPropagation()
+                        this.onPay(item)
+                      }}
+                    >
+                      继续支付
+                    </Button>
+                  </div>
+                ) : null}
+              </CellGroup>
+            ))}
+          </List>
+        ) : (
+          <ColResult
+            btnStatus={false}
+            classImgSize="SMALL"
+            tips={this.type === 'buy' ? '暂无购买记录' : '暂无退款记录'}
+          />
+        )}
+
+        <Popup
+          v-model:show={this.timeStatus}
+          position="bottom"
+          round
+          closeOnPopstate
+        >
+          <DatetimePicker
+            type="year-month"
+            v-model={this.currentDate}
+            formatter={formatterDate}
+            onCancel={() => {
+              this.timeStatus = false
+            }}
+            onConfirm={this.onConfirm}
+          />
+        </Popup>
+
+        <ActionSheet
+          v-model:show={this.typeStatus}
+          actions={this.actions}
+          closeOnClickAction
+          cancelText="取消"
+          onSelect={this.onSelect}
+          onCancel={() => {
+            this.typeStatus = false
+          }}
+        />
+      </div>
+    )
+  }
+})

+ 10 - 1
src/teacher/cash-protocol/index.tsx

@@ -5,6 +5,8 @@ import styles from './index.module.less'
 import UserAuth from '@/views/order-detail/userAuth/index'
 import { postMessage } from '@/helpers/native-message'
 import { state } from '@/state'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
 
 export default defineComponent({
   name: 'cash-protocol',
@@ -79,12 +81,19 @@ export default defineComponent({
   render() {
     return (
       <div class={styles.cashProtocol}>
+        <TheSticky position="top">
+          <ColHeader />
+        </TheSticky>
         {this.realStatus ? (
           <>
             <iframe
               src={this.url}
               frameborder="0"
-              style={{ width: '100%', height: '100vh', border: 'none' }}
+              style={{
+                width: '100%',
+                height: 'calc(100vh - var(--header-height))',
+                border: 'none'
+              }}
             ></iframe>
             <Button
               type="primary"

+ 9 - 5
src/teacher/extend-plan/detail.tsx

@@ -4,16 +4,20 @@ import styles from './index.module.less'
 import p1 from './images/1.png'
 import p2 from './images/2.png'
 import p3 from './images/3.png'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'detail',
   render() {
     return (
-      <div class={styles.detail}>
-        <img src={p1} />
-        <img src={p2} />
-        <img src={p3} />
-      </div>
+      <>
+        <ColHeader />
+        <div class={styles.detail}>
+          <img src={p1} />
+          <img src={p2} />
+          <img src={p3} />
+        </div>
+      </>
     )
   }
 })

+ 2 - 0
src/teacher/extend-plan/index.tsx

@@ -24,6 +24,7 @@ import ColResult from '@/components/col-result'
 import dayjs from 'dayjs'
 import { bizStatus, postStatus } from '@/constant'
 import iconStudent from '@/common/images/icon_student.png'
+import ColHeader from '@/components/col-header'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -158,6 +159,7 @@ export default defineComponent({
   render() {
     return (
       <div style={{ overflow: 'hidden' }}>
+        <ColHeader />
         <div class={styles.allNumber}>
           {/* <img
             src={tipBg}

+ 2 - 0
src/teacher/income-consus/index.tsx

@@ -72,6 +72,7 @@ import { lineChartOption, pieChartOption } from './echarts'
 import request from '@/helpers/request'
 import dayjs from 'dayjs'
 import { formatterDate, moneyFormat } from '@/helpers/utils'
+import ColHeader from '@/components/col-header'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -269,6 +270,7 @@ export default defineComponent({
   render() {
     return (
       <div style={{ overflow: 'hidden' }}>
+        <ColHeader />
         <div class={styles.incomeConsus}>
           <Cell
             class={styles.income}

+ 2 - 0
src/teacher/leaderboard/index.tsx

@@ -17,6 +17,7 @@ import IconAvator from '@/common/images/icon_teacher.png'
 import request from '@/helpers/request'
 import { useRoute, useRouter } from 'vue-router'
 import { state as userInfo } from '@/state'
+import ColHeader from '@/components/col-header'
 
 interface IMusicItem {
   loaded: boolean
@@ -104,6 +105,7 @@ export default defineComponent({
     const imgRef = ref()
     return () => (
       <div class={styles.leaderboard}>
+        <ColHeader />
         <div class={styles.container}>
           <div class={styles.headImg} ref={imgRef}>
             <Image

+ 259 - 257
src/teacher/live-class/create-components/course.tsx

@@ -1,257 +1,259 @@
-import ColField from '@/components/col-field'
-import ColFieldGroup from '@/components/col-field-group'
-import ColPopup from '@/components/col-popup'
-import SubjectModel from '@/business-components/subject-list'
-import { ActionSheet, Button, Field, Form, Sticky } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './course.module.less'
-import { createState } from './createState'
-import {
-  verifiyNumberInteger,
-  verifyNumberIntegerAndFloat
-} from '@/helpers/toolsValidate'
-import request from '@/helpers/request'
-import { state } from '@/state'
-
-// 校验函数返回 true 表示校验通过,false 表示不通过
-export const validator = val => {
-  console.log(val)
-  if (Number(val) <= 0) {
-    return '课程组售价必须大于0'
-  } else {
-    return true
-  }
-}
-
-export default defineComponent({
-  name: 'course',
-  data() {
-    return {
-      subjectStatus: false,
-      classTimeStatus: false,
-      checked: false
-    }
-  },
-  computed: {
-    choiceSubjectIds() {
-      // 选择的科目编号
-      let ids = createState.live.subjectId
-        ? Number(createState.live.subjectId)
-        : 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
-    },
-    calcSingleRatePrice() {
-      let rate = createState.rate || 0
-      let nums = createState.live.courseNum
-      let price = createState.live.coursePrice || 0
-      return nums ? ((price / nums) * (1 - rate / 100)).toFixed(2) : 0
-    },
-    calcRatePrice() {
-      // 计算手续费
-      let rate = createState.rate || 0
-      let price = createState.live.coursePrice || 0
-      return (price - (rate / 100) * price).toFixed(2)
-    },
-    disabled() {
-      return createState.live.courseGroupId ? true : false
-    }
-  },
-  async mounted() {
-    try {
-      const resVersion = await request.post('/api-teacher/open/appVersion', {
-        data: {
-          platform:
-            state.platformType === 'STUDENT' ? 'ios-student' : 'ios-teacher',
-          version: state.version
-        }
-      })
-      this.checked = resVersion.data.check ? true : false
-      // 审核版本金额默认为0
-      if (this.checked) {
-        createState.live.coursePrice = 0
-      }
-    } catch {
-      //
-    }
-  },
-  methods: {
-    onChoice(id: number) {
-      createState.live.subjectId = id
-      this.subjectStatus = false
-    },
-    onFormatter(val: any) {
-      return verifyNumberIntegerAndFloat(val)
-    },
-    onFormatterInt(val: any) {
-      if (val && val >= 1) {
-        return verifiyNumberInteger(val)
-      } else {
-        return ''
-      }
-    },
-    onSelect(action: any) {
-      createState.live.singleCourseMinutes =
-        Number(action.name || 0) + Number(action.freeMinutes || 0)
-      createState.live.singleMins = Number(action.name || 0)
-      createState.live.freeMinutes = Number(action.freeMinutes || 0)
-    }
-  },
-  render() {
-    return (
-      <Form
-        class={styles.classInfo}
-        onSubmit={() => (createState.active = 2)}
-        scrollToError
-      >
-        <ColFieldGroup>
-          <ColField title="课程名称" required>
-            <Field
-              v-model={createState.live.name}
-              name="name"
-              maxlength={20}
-              placeholder="请输入您的课程名称"
-              rules={[{ required: true, message: '请输入您的课程名称' }]}
-              disabled={this.disabled}
-            />
-          </ColField>
-          <ColField title="课程声部" required>
-            <Field
-              modelValue={this.lessonSubjectName}
-              name="subjectId"
-              readonly
-              isLink
-              disabled={this.disabled}
-              onClick={() => {
-                if (createState.live.courseGroupId) {
-                  return
-                }
-                this.subjectStatus = true
-              }}
-              rules={[{ required: true, message: '请选择课程声部' }]}
-              placeholder="请选择课程声部"
-            />
-          </ColField>
-
-          <ColField title="课程介绍" required border={false}>
-            <Field
-              v-model={createState.live.courseIntroduce}
-              name="courseIntroduce"
-              placeholder="请输入课程介绍"
-              rows="3"
-              maxlength={200}
-              showWordLimit
-              autosize
-              rules={[{ required: true, message: '请输入课程介绍' }]}
-              type="textarea"
-            />
-          </ColField>
-        </ColFieldGroup>
-
-        <ColFieldGroup>
-          <ColField title="课时数" required>
-            <Field
-              v-model={createState.live.courseNum}
-              name="courseNum"
-              placeholder="请输入您的课时数"
-              formatter={this.onFormatterInt}
-              type="number"
-              maxlength={2}
-              disabled={this.disabled}
-              rules={[{ required: true, message: '请输入您的课时数' }]}
-              v-slots={{
-                button: () => <span>课时</span>
-              }}
-            />
-          </ColField>
-          <ColField title="单课时时长" required>
-            <Field
-              modelValue={createState.live.singleMins}
-              name="singleMins"
-              readonly
-              disabled={this.disabled}
-              isLink
-              onClick={() => {
-                if (!createState.live.courseGroupId) {
-                  this.classTimeStatus = true
-                }
-              }}
-              rules={[{ required: true, message: '请选择单课时时长' }]}
-              placeholder="请选择单课时时长"
-            />
-          </ColField>
-          {/* 是审核版本才会显示金额 */}
-          {!this.checked && (
-            <ColField title="课程组售价" required>
-              <Field
-                v-model={createState.live.coursePrice}
-                name="coursePrice"
-                placeholder="请输入您的课程组售价"
-                formatter={this.onFormatter}
-                type="number"
-                maxlength={8}
-                disabled={this.disabled}
-                rules={[
-                  { required: true, validator, message: '请输入您的课程组售价' }
-                ]}
-                v-slots={{
-                  button: () => <span>元</span>
-                }}
-              />
-            </ColField>
-          )}
-        </ColFieldGroup>
-
-        <div class={styles['class-info-tip']}>
-          <p>扣除手续费后您的课程预计收入为:</p>
-          <p>
-            单课时<span>{this.calcSingleRatePrice}</span>元/人
-          </p>
-          <p>
-            课程组总收入<span>{this.calcRatePrice}</span>元/人
-          </p>
-          <p>您的课程收入将在课程结束后结算到您的账户中</p>
-        </div>
-
-        <Sticky offsetBottom={0} position="bottom">
-          <div class={'btnGroup'}>
-            <Button block round type="primary" native-type="submit">
-              下一步
-            </Button>
-          </div>
-        </Sticky>
-
-        <ColPopup v-model={this.subjectStatus}>
-          <SubjectModel
-            selectType="Radio"
-            single
-            subjectList={createState.subjectList}
-            choiceSubjectIds={this.choiceSubjectIds}
-            onChoice={this.onChoice}
-          />
-        </ColPopup>
-
-        <ActionSheet
-          v-model:show={this.classTimeStatus}
-          actions={createState.minutes}
-          cancelText="取消"
-          closeOnClickAction
-          onSelect={this.onSelect}
-        />
-      </Form>
-    )
-  }
-})
+import ColField from '@/components/col-field'
+import ColFieldGroup from '@/components/col-field-group'
+import ColPopup from '@/components/col-popup'
+import SubjectModel from '@/business-components/subject-list'
+import { ActionSheet, Button, Field, Form, Sticky } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './course.module.less'
+import { createState } from './createState'
+import {
+  verifiyNumberInteger,
+  verifyNumberIntegerAndFloat
+} from '@/helpers/toolsValidate'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import ColHeader from '@/components/col-header'
+
+// 校验函数返回 true 表示校验通过,false 表示不通过
+export const validator = val => {
+  console.log(val)
+  if (Number(val) <= 0) {
+    return '课程组售价必须大于0'
+  } else {
+    return true
+  }
+}
+
+export default defineComponent({
+  name: 'course',
+  data() {
+    return {
+      subjectStatus: false,
+      classTimeStatus: false,
+      checked: false
+    }
+  },
+  computed: {
+    choiceSubjectIds() {
+      // 选择的科目编号
+      const ids = createState.live.subjectId
+        ? Number(createState.live.subjectId)
+        : 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
+    },
+    calcSingleRatePrice() {
+      const rate = createState.rate || 0
+      const nums = createState.live.courseNum
+      const price = createState.live.coursePrice || 0
+      return nums ? ((price / nums) * (1 - rate / 100)).toFixed(2) : 0
+    },
+    calcRatePrice() {
+      // 计算手续费
+      const rate = createState.rate || 0
+      const price = createState.live.coursePrice || 0
+      return (price - (rate / 100) * price).toFixed(2)
+    },
+    disabled() {
+      return createState.live.courseGroupId ? true : false
+    }
+  },
+  async mounted() {
+    try {
+      const resVersion = await request.post('/api-teacher/open/appVersion', {
+        data: {
+          platform:
+            state.platformType === 'STUDENT' ? 'ios-student' : 'ios-teacher',
+          version: state.version
+        }
+      })
+      this.checked = resVersion.data.check ? true : false
+      // 审核版本金额默认为0
+      if (this.checked) {
+        createState.live.coursePrice = 0
+      }
+    } catch {
+      //
+    }
+  },
+  methods: {
+    onChoice(id: number) {
+      createState.live.subjectId = id
+      this.subjectStatus = false
+    },
+    onFormatter(val: any) {
+      return verifyNumberIntegerAndFloat(val)
+    },
+    onFormatterInt(val: any) {
+      if (val && val >= 1) {
+        return verifiyNumberInteger(val)
+      } else {
+        return ''
+      }
+    },
+    onSelect(action: any) {
+      createState.live.singleCourseMinutes =
+        Number(action.name || 0) + Number(action.freeMinutes || 0)
+      createState.live.singleMins = Number(action.name || 0)
+      createState.live.freeMinutes = Number(action.freeMinutes || 0)
+    }
+  },
+  render() {
+    return (
+      <Form
+        class={styles.classInfo}
+        onSubmit={() => (createState.active = 2)}
+        scrollToError
+      >
+        <ColFieldGroup>
+          <ColField title="课程名称" required>
+            <Field
+              v-model={createState.live.name}
+              name="name"
+              maxlength={20}
+              placeholder="请输入您的课程名称"
+              rules={[{ required: true, message: '请输入您的课程名称' }]}
+              disabled={this.disabled}
+            />
+          </ColField>
+          <ColField title="课程声部" required>
+            <Field
+              modelValue={this.lessonSubjectName}
+              name="subjectId"
+              readonly
+              isLink
+              disabled={this.disabled}
+              onClick={() => {
+                if (createState.live.courseGroupId) {
+                  return
+                }
+                this.subjectStatus = true
+              }}
+              rules={[{ required: true, message: '请选择课程声部' }]}
+              placeholder="请选择课程声部"
+            />
+          </ColField>
+
+          <ColField title="课程介绍" required border={false}>
+            <Field
+              v-model={createState.live.courseIntroduce}
+              name="courseIntroduce"
+              placeholder="请输入课程介绍"
+              rows="3"
+              maxlength={200}
+              showWordLimit
+              autosize
+              rules={[{ required: true, message: '请输入课程介绍' }]}
+              type="textarea"
+            />
+          </ColField>
+        </ColFieldGroup>
+
+        <ColFieldGroup>
+          <ColField title="课时数" required>
+            <Field
+              v-model={createState.live.courseNum}
+              name="courseNum"
+              placeholder="请输入您的课时数"
+              formatter={this.onFormatterInt}
+              type="number"
+              maxlength={2}
+              disabled={this.disabled}
+              rules={[{ required: true, message: '请输入您的课时数' }]}
+              v-slots={{
+                button: () => <span>课时</span>
+              }}
+            />
+          </ColField>
+          <ColField title="单课时时长" required>
+            <Field
+              modelValue={createState.live.singleMins}
+              name="singleMins"
+              readonly
+              disabled={this.disabled}
+              isLink
+              onClick={() => {
+                if (!createState.live.courseGroupId) {
+                  this.classTimeStatus = true
+                }
+              }}
+              rules={[{ required: true, message: '请选择单课时时长' }]}
+              placeholder="请选择单课时时长"
+            />
+          </ColField>
+          {/* 是审核版本才会显示金额 */}
+          {!this.checked && (
+            <ColField title="课程组售价" required>
+              <Field
+                v-model={createState.live.coursePrice}
+                name="coursePrice"
+                placeholder="请输入您的课程组售价"
+                formatter={this.onFormatter}
+                type="number"
+                maxlength={8}
+                disabled={this.disabled}
+                rules={[
+                  { required: true, validator, message: '请输入您的课程组售价' }
+                ]}
+                v-slots={{
+                  button: () => <span>元</span>
+                }}
+              />
+            </ColField>
+          )}
+        </ColFieldGroup>
+
+        <div class={styles['class-info-tip']}>
+          <p>扣除手续费后您的课程预计收入为:</p>
+          <p>
+            单课时<span>{this.calcSingleRatePrice}</span>元/人
+          </p>
+          <p>
+            课程组总收入<span>{this.calcRatePrice}</span>元/人
+          </p>
+          <p>您的课程收入将在课程结束后结算到您的账户中</p>
+        </div>
+
+        <Sticky offsetBottom={0} position="bottom">
+          <div class={'btnGroup'}>
+            <Button block round type="primary" native-type="submit">
+              下一步
+            </Button>
+          </div>
+        </Sticky>
+
+        <ColPopup v-model={this.subjectStatus}>
+          <ColHeader title="选择声部" />
+          <SubjectModel
+            selectType="Radio"
+            single
+            subjectList={createState.subjectList}
+            choiceSubjectIds={this.choiceSubjectIds}
+            onChoice={this.onChoice}
+          />
+        </ColPopup>
+
+        <ActionSheet
+          v-model:show={this.classTimeStatus}
+          actions={createState.minutes}
+          cancelText="取消"
+          closeOnClickAction
+          onSelect={this.onSelect}
+        />
+      </Form>
+    )
+  }
+})

+ 2 - 2
src/teacher/live-class/create.module.less

@@ -1,5 +1,5 @@
 .live-create {
   :global(.van-sticky--fixed) {
-    box-shadow: 10px 10px 10px var(--box-shadow-color);
+    // box-shadow: 10px 10px 10px var(--box-shadow-color);
   }
-}
+}

+ 109 - 106
src/teacher/live-class/create.tsx

@@ -1,106 +1,109 @@
-import { defineComponent } from 'vue'
-import Steps from './create-components/steps'
-import { createState } from './create-components/createState'
-import Course from './create-components/course'
-import CoursePlan from './create-components/course-plan'
-import CourseStart from './create-components/course-start'
-import Detail from './create-components/detail'
-import { Sticky } from 'vant'
-import Arrange from './create-components/arrange'
-import request from '@/helpers/request'
-import styles from './create.module.less'
-
-export default defineComponent({
-  name: 'LiveCreate',
-  async mounted() {
-    try {
-      // 获取手续费和分钟数
-      let config = await request.get(
-        '/api-teacher/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-teacher/teacher/querySubject')
-      createState.subjectList = teacher.data || []
-    } catch (err: any) {
-      console.log(err)
-    }
-    this.getLiveClassDetail()
-  },
-  methods: {
-    // 获取直播课详情
-    async getLiveClassDetail() {
-      const groupId = this.$route.query.groupId
-      if (!groupId) return
-      const res = await request.get(
-        `/api-teacher/courseGroup/queryLiveCourseInfo?groupId=${groupId}`
-      )
-      console.log(res, createState)
-      if (res.code == 200) {
-        const data = res.data
-        createState.live.courseGroupId = data.courseGroupId
-        createState.live.teacherId = data.teacherId
-        createState.live.name = data.courseGroupName
-        createState.live.subjectId =
-          (
-            createState.subjectList.find(
-              (n: any) => n.name === data.subjectName
-            ) as any
-          )?.id || ''
-        createState.live.courseIntroduce = data.courseIntroduce
-        createState.live.courseNum = data.courseNum
-        createState.live.singleMins = data.singleCourseMinutes
-        createState.live.coursePrice = data.coursePrice
-        createState.live.coursePlanList = data.planList
-        createState.live.salesStartDate = data.salesStartDate
-        createState.live.salesEndDate = data.salesEndDate
-        createState.live.mixStudentNum = data.mixStudentNum
-        createState.live.backgroundPic = data.backgroundPic
-      }
-    }
-  },
-  render() {
-    return (
-      <div class={styles['live-create']}>
-        {createState.active !== 5 && (
-          <Sticky position="top" offsetTop={0}>
-            <Steps
-              style={{ backgroundColor: '#f6f8f9', paddingBottom: '12px' }}
-            />
-          </Sticky>
-        )}
-
-        {createState.active === 1 && <Course />}
-
-        {createState.active == 2 && <CoursePlan />}
-
-        {createState.active == 3 && <Arrange />}
-
-        {createState.active == 4 && <CourseStart />}
-
-        {createState.active == 5 && <Detail />}
-      </div>
-    )
-  }
-})
+import { defineComponent } from 'vue'
+import Steps from './create-components/steps'
+import { createState } from './create-components/createState'
+import Course from './create-components/course'
+import CoursePlan from './create-components/course-plan'
+import CourseStart from './create-components/course-start'
+import Detail from './create-components/detail'
+import { Sticky } from 'vant'
+import Arrange from './create-components/arrange'
+import request from '@/helpers/request'
+import styles from './create.module.less'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
+
+export default defineComponent({
+  name: 'LiveCreate',
+  async mounted() {
+    try {
+      // 获取手续费和分钟数
+      const config = await request.get(
+        '/api-teacher/sysConfig/queryByParamNameList',
+        {
+          params: {
+            paramNames: 'live_service_rate,live_time_setting'
+          }
+        }
+      )
+      const configData = config.data || []
+      configData.forEach((item: any) => {
+        if (item.paramName === 'live_time_setting') {
+          const mins = item.paramValue ? JSON.parse(item.paramValue) : []
+          const 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
+        }
+      })
+
+      const teacher = await request.post('/api-teacher/teacher/querySubject')
+      createState.subjectList = teacher.data || []
+    } catch (err: any) {
+      console.log(err)
+    }
+    this.getLiveClassDetail()
+  },
+  methods: {
+    // 获取直播课详情
+    async getLiveClassDetail() {
+      const groupId = this.$route.query.groupId
+      if (!groupId) return
+      const res = await request.get(
+        `/api-teacher/courseGroup/queryLiveCourseInfo?groupId=${groupId}`
+      )
+      console.log(res, createState)
+      if (res.code == 200) {
+        const data = res.data
+        createState.live.courseGroupId = data.courseGroupId
+        createState.live.teacherId = data.teacherId
+        createState.live.name = data.courseGroupName
+        createState.live.subjectId =
+          (
+            createState.subjectList.find(
+              (n: any) => n.name === data.subjectName
+            ) as any
+          )?.id || ''
+        createState.live.courseIntroduce = data.courseIntroduce
+        createState.live.courseNum = data.courseNum
+        createState.live.singleMins = data.singleCourseMinutes
+        createState.live.coursePrice = data.coursePrice
+        createState.live.coursePlanList = data.planList
+        createState.live.salesStartDate = data.salesStartDate
+        createState.live.salesEndDate = data.salesEndDate
+        createState.live.mixStudentNum = data.mixStudentNum
+        createState.live.backgroundPic = data.backgroundPic
+      }
+    }
+  },
+  render() {
+    return (
+      <div class={styles['live-create']}>
+        <TheSticky position="top">
+          <ColHeader border={false} />
+          {createState.active !== 5 && (
+            <Steps
+              style={{ backgroundColor: '#f6f8f9', paddingBottom: '12px' }}
+            />
+          )}
+        </TheSticky>
+
+        {createState.active === 1 && <Course />}
+
+        {createState.active == 2 && <CoursePlan />}
+
+        {createState.active == 3 && <Arrange />}
+
+        {createState.active == 4 && <CourseStart />}
+
+        {createState.active == 5 && <Detail />}
+      </div>
+    )
+  }
+})

+ 2 - 0
src/teacher/live-class/live-detail.tsx

@@ -13,6 +13,7 @@ import dayjs from 'dayjs'
 import { Button, Popup, Sticky, Tab, Tabs, Toast } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './live-detail.module.less'
+import ColHeader from '@/components/col-header'
 interface IProps {
   courseTime: string
   coursePlan: string
@@ -196,6 +197,7 @@ export default defineComponent({
   render() {
     return (
       <div class={[styles['live-detail'], 'mb12']}>
+        <ColHeader />
         <UserDetail userInfo={this.userInfo} />
         <SectionDetail border>
           <p class={styles.introduction}>{this.userInfo.lessonDesc}</p>

+ 5 - 1
src/teacher/main.ts

@@ -25,7 +25,11 @@ const app = createApp(App)
 //     };
 //   }
 // });
-
+postMessage({ api: 'setBarStatus', content: { status: 0 } })
+postMessage({
+  api: 'backIconChange',
+  content: { backIconHide: true }
+})
 postMessage(
   {
     api: 'getVersion'

+ 179 - 176
src/teacher/music-cert/index.tsx

@@ -1,176 +1,179 @@
-import { postMessage } from '@/helpers/native-message'
-import { defineComponent } from 'vue'
-import { Button, Dialog, Image, Sticky, Toast } from 'vant'
-import styles from './index.module.less'
-import request from '@/helpers/request'
-import { state } from '@/state'
-import ColResult from '@/components/col-result'
-import ColHeader from '@/components/col-header'
-// import ColHeader from "@/components/col-header";
-
-/**
- * 动态引入文件
- * @param fileName 文件名
- * @param path 文件路径
- */
-export const getAssetsHomeFile = (fileName: string) => {
-  const path = `./images/${fileName}`
-  const modules = import.meta.globEager('./images/*')
-  return modules[path].default
-}
-
-export default defineComponent({
-  name: 'music-cert',
-  data() {
-    return {
-      authStatus: null
-    }
-  },
-  mounted() {
-    postMessage({ api: 'setBarStatus', content: { status: 0 } })
-    // 音乐人审核状态 0、未申请 UNPAALY、已申请 DOING、审核中 PASS、通过 UNPASS、不通过
-    const musicianAuthStatus = state.user.data?.musicianAuthStatus
-    if (musicianAuthStatus) {
-      state.musicCertStatus =
-        musicianAuthStatus === 'DOING' || musicianAuthStatus === 'PASS'
-          ? true
-          : false
-    }
-    // 如果申请过,则显示标题
-    if (state.musicCertStatus) {
-      postMessage({ api: 'setBarStatus', content: { status: 1 } })
-    }
-
-    // 如果没有达人认证,则需要先去认证
-    if (!state.user.data?.entryFlag) {
-      Dialog.alert({
-        message: '您尚未完成达人认证,达人认证后才可开通音乐人权限哦!',
-        confirmButtonColor: '#2DC7AA'
-      }).then(() => {
-        this.$router.replace('/teacherCert')
-      })
-    }
-  },
-  unmounted() {
-    postMessage({ api: 'setBarStatus', content: { status: 1 } })
-  },
-  methods: {
-    async onClick() {
-      try {
-        await request.post('/api-teacher/teacherAuthMusicianRecord/doApply', {})
-        Toast('申请认证成功')
-        state.musicCertStatus = true
-        postMessage({ api: 'setBarStatus', content: { status: 1 } })
-      } catch {}
-    }
-  },
-  render() {
-    return (
-      <>
-        {state.musicCertStatus ? (
-          <ColResult
-            classImgSize="CERT"
-            type="musicCert"
-            style={{ paddingTop: '60px' }}
-            tips={
-              state.user.data?.musicianAuthStatus === 'PASS'
-                ? '您提交的音乐人认证已申请成功'
-                : '您已成功提交音乐人认证申请我们将在2个工作日内与您取得联系'
-            }
-          />
-        ) : (
-          <div class={styles['music-cert']}>
-            <ColHeader
-              background="transparent"
-              backIconColor="white"
-              color="#fff"
-              border={false}
-            />
-            <div class={styles.certWrapper}>
-              <div class={[styles['cert-section'], 'mb12']}>
-                <div class={styles.title}>来成为酷乐秀音乐人吧!</div>
-
-                <div class={styles.item}>
-                  <div class={styles.content}>
-                    <div class={styles.subTitle}>
-                      <img src={getAssetsHomeFile('1.png')} />
-                      <span>教学视频分享达人</span>
-                    </div>
-                    <div class={styles.subConent}>
-                      发布您制作的教学视频内容,展现不同类型的学习方式
-                    </div>
-                  </div>
-                  <img
-                    src={getAssetsHomeFile('logo2.png')}
-                    class={styles.logo}
-                  />
-                </div>
-
-                <div class={styles.item}>
-                  <div class={styles.content}>
-                    <div class={styles.subTitle}>
-                      <img src={getAssetsHomeFile('2.png')} />
-                      <span>演奏Mlog达人</span>
-                    </div>
-                    <div class={styles.subConent}>
-                      发布自己演奏的音乐Mlog,让学员深入了解您的专业技能,从而提升您的关注率
-                    </div>
-                  </div>
-                  <img
-                    src={getAssetsHomeFile('logo3.png')}
-                    class={styles.logo}
-                  />
-                </div>
-
-                <div class={styles.item}>
-                  <div class={styles.content}>
-                    <div class={styles.subTitle}>
-                      <img src={getAssetsHomeFile('3.png')} />
-                      <span>直播up达人</span>
-                    </div>
-                    <div class={styles.subConent}>
-                      利用平台专为器乐演奏研发的直播教室,可进行一对一或开启直播间在线分内容,技巧等
-                    </div>
-                  </div>
-                  <img
-                    src={getAssetsHomeFile('logo1.png')}
-                    class={styles.logo}
-                  />
-                </div>
-
-                <div class={styles.item}>
-                  <div class={styles.content}>
-                    <div class={styles.subTitle}>
-                      <img src={getAssetsHomeFile('4.png')} />
-                      <span>乐谱歌单达人</span>
-                    </div>
-                    <div class={styles.subConent}>
-                      上传您制作的MIDI乐谱,结合小酷Ai给音乐爱好者自主练习的途径
-                    </div>
-                  </div>
-                  <img
-                    src={getAssetsHomeFile('logo4.png')}
-                    class={styles.logo}
-                  />
-                </div>
-              </div>
-
-              <Sticky position="bottom" offsetBottom={0}>
-                <div class={styles['btn-group']}>
-                  {/* <Button round block type="primary" disabled={this.authStatus === '1'} onClick={this.onClick}>立即认证</Button> */}
-                  <div
-                    class={[
-                      styles.btn,
-                      this.authStatus === '1' ? styles.disabled : null
-                    ]}
-                    onClick={this.onClick}
-                  ></div>
-                </div>
-              </Sticky>
-            </div>
-          </div>
-        )}
-      </>
-    )
-  }
-})
+import { postMessage } from '@/helpers/native-message'
+import { defineComponent } from 'vue'
+import { Button, Dialog, Image, Sticky, Toast } from 'vant'
+import styles from './index.module.less'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import ColResult from '@/components/col-result'
+import ColHeader from '@/components/col-header'
+// import ColHeader from "@/components/col-header";
+
+/**
+ * 动态引入文件
+ * @param fileName 文件名
+ * @param path 文件路径
+ */
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `./images/${fileName}`
+  const modules = import.meta.globEager('./images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'music-cert',
+  data() {
+    return {
+      authStatus: null
+    }
+  },
+  mounted() {
+    postMessage({ api: 'setBarStatus', content: { status: 0 } })
+    // 音乐人审核状态 0、未申请 UNPAALY、已申请 DOING、审核中 PASS、通过 UNPASS、不通过
+    const musicianAuthStatus = state.user.data?.musicianAuthStatus
+    if (musicianAuthStatus) {
+      state.musicCertStatus =
+        musicianAuthStatus === 'DOING' || musicianAuthStatus === 'PASS'
+          ? true
+          : false
+    }
+    // 如果申请过,则显示标题
+    if (state.musicCertStatus) {
+      postMessage({ api: 'setBarStatus', content: { status: 1 } })
+    }
+
+    // 如果没有达人认证,则需要先去认证
+    if (!state.user.data?.entryFlag) {
+      Dialog.alert({
+        message: '您尚未完成达人认证,达人认证后才可开通音乐人权限哦!',
+        confirmButtonColor: '#2DC7AA'
+      }).then(() => {
+        this.$router.replace('/teacherCert')
+      })
+    }
+  },
+  unmounted() {
+    postMessage({ api: 'setBarStatus', content: { status: 1 } })
+  },
+  methods: {
+    async onClick() {
+      try {
+        await request.post('/api-teacher/teacherAuthMusicianRecord/doApply', {})
+        Toast('申请认证成功')
+        state.musicCertStatus = true
+        postMessage({ api: 'setBarStatus', content: { status: 1 } })
+      } catch {}
+    }
+  },
+  render() {
+    return (
+      <>
+        {state.musicCertStatus ? (
+          <>
+            <ColHeader border={false} />
+            <ColResult
+              classImgSize="CERT"
+              type="musicCert"
+              style={{ paddingTop: '60px' }}
+              tips={
+                state.user.data?.musicianAuthStatus === 'PASS'
+                  ? '您提交的音乐人认证已申请成功'
+                  : '您已成功提交音乐人认证申请我们将在2个工作日内与您取得联系'
+              }
+            />
+          </>
+        ) : (
+          <div class={styles['music-cert']}>
+            <ColHeader
+              background="transparent"
+              backIconColor="white"
+              color="#fff"
+              border={false}
+            />
+            <div class={styles.certWrapper}>
+              <div class={[styles['cert-section'], 'mb12']}>
+                <div class={styles.title}>来成为酷乐秀音乐人吧!</div>
+
+                <div class={styles.item}>
+                  <div class={styles.content}>
+                    <div class={styles.subTitle}>
+                      <img src={getAssetsHomeFile('1.png')} />
+                      <span>教学视频分享达人</span>
+                    </div>
+                    <div class={styles.subConent}>
+                      发布您制作的教学视频内容,展现不同类型的学习方式
+                    </div>
+                  </div>
+                  <img
+                    src={getAssetsHomeFile('logo2.png')}
+                    class={styles.logo}
+                  />
+                </div>
+
+                <div class={styles.item}>
+                  <div class={styles.content}>
+                    <div class={styles.subTitle}>
+                      <img src={getAssetsHomeFile('2.png')} />
+                      <span>演奏Mlog达人</span>
+                    </div>
+                    <div class={styles.subConent}>
+                      发布自己演奏的音乐Mlog,让学员深入了解您的专业技能,从而提升您的关注率
+                    </div>
+                  </div>
+                  <img
+                    src={getAssetsHomeFile('logo3.png')}
+                    class={styles.logo}
+                  />
+                </div>
+
+                <div class={styles.item}>
+                  <div class={styles.content}>
+                    <div class={styles.subTitle}>
+                      <img src={getAssetsHomeFile('3.png')} />
+                      <span>直播up达人</span>
+                    </div>
+                    <div class={styles.subConent}>
+                      利用平台专为器乐演奏研发的直播教室,可进行一对一或开启直播间在线分内容,技巧等
+                    </div>
+                  </div>
+                  <img
+                    src={getAssetsHomeFile('logo1.png')}
+                    class={styles.logo}
+                  />
+                </div>
+
+                <div class={styles.item}>
+                  <div class={styles.content}>
+                    <div class={styles.subTitle}>
+                      <img src={getAssetsHomeFile('4.png')} />
+                      <span>乐谱歌单达人</span>
+                    </div>
+                    <div class={styles.subConent}>
+                      上传您制作的MIDI乐谱,结合小酷Ai给音乐爱好者自主练习的途径
+                    </div>
+                  </div>
+                  <img
+                    src={getAssetsHomeFile('logo4.png')}
+                    class={styles.logo}
+                  />
+                </div>
+              </div>
+
+              <Sticky position="bottom" offsetBottom={0}>
+                <div class={styles['btn-group']}>
+                  {/* <Button round block type="primary" disabled={this.authStatus === '1'} onClick={this.onClick}>立即认证</Button> */}
+                  <div
+                    class={[
+                      styles.btn,
+                      this.authStatus === '1' ? styles.disabled : null
+                    ]}
+                    onClick={this.onClick}
+                  ></div>
+                </div>
+              </Sticky>
+            </div>
+          </div>
+        )}
+      </>
+    )
+  }
+})

+ 2 - 0
src/teacher/music/upload/index.tsx

@@ -42,6 +42,7 @@ import UploadIcon from './upload.svg'
 import ColUpload from '@/components/col-upload'
 import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
 import { state } from '@/state'
+import ColHeader from '@/components/col-header'
 
 export type BackgroundMp3 = {
   url?: string
@@ -471,6 +472,7 @@ export default defineComponent({
     const browserInfo = browser()
     return (
       <Form class={styles.form} onSubmit={this.submit} onFailed={this.failed}>
+        <ColHeader />
         {this.reason && (
           <NoticeBar wrapable scrollable={false} text={this.reason} />
         )}

+ 91 - 89
src/teacher/my-fans/index.tsx

@@ -1,89 +1,91 @@
-import { Cell, Image, List } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-import iconStudent from '@common/images/icon_student.png'
-import request from '@/helpers/request'
-import ColResult from '@/components/col-result'
-
-export default defineComponent({
-  name: 'my-fans',
-  data() {
-    return {
-      list: [],
-      dataShow: true, // 判断是否有数据
-      loading: false,
-      finished: false,
-      params: {
-        page: 1,
-        rows: 20
-      }
-    }
-  },
-  mounted() {},
-  methods: {
-    async getList() {
-      try {
-        let params = this.params
-        const res = await request.post('/api-teacher/teacher/queryMyFans', {
-          data: {
-            ...params
-          }
-        })
-        this.loading = false
-        const result = res.data || {}
-        // 处理重复请求数据
-        if (this.list.length > 0 && result.pageNo === 1) {
-          return
-        }
-        this.list = this.list.concat(result.rows || [])
-        this.finished = result.pageNo >= result.totalPage
-        this.params.page = result.pageNo + 1
-        this.dataShow = this.list.length > 0
-      } catch {
-        this.dataShow = false
-        this.finished = true
-      }
-    }
-  },
-  render() {
-    return (
-      <div>
-        {this.dataShow ? (
-          <List
-            v-model:loading={this.loading}
-            finished={this.finished}
-            finishedText=" "
-            class={[styles.liveList, 'mb12']}
-            onLoad={this.getList}
-          >
-            {this.list.map((item: any) => (
-              <Cell
-                class={styles.myFans}
-                titleStyle={{ paddingLeft: '8px' }}
-                v-slots={{
-                  icon: () => (
-                    <Image
-                      class={styles.userImg}
-                      src={item.avatar || iconStudent}
-                      fit="cover"
-                    />
-                  ),
-                  title: () => (
-                    <div class={styles.userInfo}>
-                      <div class={styles.userName}> {item.userName} </div>
-                      <div class={styles.piNameSubject}>
-                        <span class={styles.subject}>{item.subjectName}</span>
-                      </div>
-                    </div>
-                  )
-                }}
-              />
-            ))}
-          </List>
-        ) : (
-          <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无粉丝" />
-        )}
-      </div>
-    )
-  }
-})
+import { Cell, Image, List } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import iconStudent from '@common/images/icon_student.png'
+import request from '@/helpers/request'
+import ColResult from '@/components/col-result'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'my-fans',
+  data() {
+    return {
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      params: {
+        page: 1,
+        rows: 20
+      }
+    }
+  },
+  mounted() {},
+  methods: {
+    async getList() {
+      try {
+        const params = this.params
+        const res = await request.post('/api-teacher/teacher/queryMyFans', {
+          data: {
+            ...params
+          }
+        })
+        this.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+    }
+  },
+  render() {
+    return (
+      <div>
+        <ColHeader />
+        {this.dataShow ? (
+          <List
+            v-model:loading={this.loading}
+            finished={this.finished}
+            finishedText=" "
+            class={[styles.liveList, 'mb12']}
+            onLoad={this.getList}
+          >
+            {this.list.map((item: any) => (
+              <Cell
+                class={styles.myFans}
+                titleStyle={{ paddingLeft: '8px' }}
+                v-slots={{
+                  icon: () => (
+                    <Image
+                      class={styles.userImg}
+                      src={item.avatar || iconStudent}
+                      fit="cover"
+                    />
+                  ),
+                  title: () => (
+                    <div class={styles.userInfo}>
+                      <div class={styles.userName}> {item.userName} </div>
+                      <div class={styles.piNameSubject}>
+                        <span class={styles.subject}>{item.subjectName}</span>
+                      </div>
+                    </div>
+                  )
+                }}
+              />
+            ))}
+          </List>
+        ) : (
+          <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无粉丝" />
+        )}
+      </div>
+    )
+  }
+})

+ 6 - 5
src/teacher/my-sheetMusic/index.module.less

@@ -1,9 +1,10 @@
-.container{
-  min-height: calc(100vh - var(--van-tabs-line-height) - 20px);
-  
+.container {
+  min-height: calc(100vh - var(--van-tabs-line-height) - 20px - var(--header-height));
+
 }
-:global{
-  .van-tabs__wrap{
+
+:global {
+  .van-tabs__wrap {
     position: sticky;
     top: 0;
     z-index: 10;

+ 13 - 0
src/teacher/my-sheetMusic/index.tsx

@@ -7,6 +7,8 @@ import Personal from '@/views/music/personal/personal'
 import Collection from '@/views/music/personal/collection'
 import Album from '@/views/music/personal/album'
 import AlbumMy from '@/views/music/personal/album-my'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
 
 export default defineComponent({
   name: 'mySheetMusic',
@@ -16,12 +18,21 @@ export default defineComponent({
     const personal = ref()
     const collection = ref()
     const practice = ref()
+    const height = ref<any>('auto')
     onMounted(() => {
       useEventTracking('我的乐谱')
     })
     return () => {
       return (
         <div>
+          <TheSticky
+            position="top"
+            onBarHeight={(h: any) => {
+              height.value = h
+            }}
+          >
+            <ColHeader />
+          </TheSticky>
           <Tabs
             animated
             swipeable
@@ -29,6 +40,8 @@ export default defineComponent({
             lineWidth={20}
             v-model:active={activeTab.value}
             onChange={val => (activeTab.value = val)}
+            sticky
+            offsetTop={height.value}
           >
             <Tab title="购买单曲" name="personal">
               <div class={styles.container}>

+ 231 - 228
src/teacher/open-live/index.tsx

@@ -1,228 +1,231 @@
-import { defineComponent } from 'vue'
-import { Button, Cell, Dialog, Icon, Image, Rate, Sticky, Toast } from 'vant'
-import ColHeader from '@/components/col-header'
-import styles from './index.module.less'
-import { state } from '@/state'
-import { postMessage } from '@/helpers/native-message'
-
-import tips from './images/icon_tips.png'
-import banner1 from './images/1.png'
-import banner2 from './images/2.png'
-import banner3 from './images/3.png'
-import iconTeacher from '@common/images/icon_teacher.png'
-import musicCert from '@common/images/music_cert.png'
-import teacherCert from '@common/images/teacher_cert.png'
-
-import request from '@/helpers/request'
-import ColResult from '@/components/col-result'
-
-export default defineComponent({
-  name: 'live-cert',
-  computed: {
-    users() {
-      return state.user.data
-    },
-    btnStatus() {
-      // this.users?.fansNum
-      // this.users?.expTime
-      const users: any = this.users
-      const fansNum: number = this.fansNum
-      const expTime: number = this.overClassNum
-      return users?.fansNum >= fansNum && users?.expTime >= expTime
-    }
-  },
-  data() {
-    return {
-      fansNum: 0,
-      overClassNum: 0
-    }
-  },
-  async mounted() {
-    const liveFlag = state.user.data?.liveFlag
-    state.openLiveStatus = liveFlag === 1 ? true : false
-    try {
-      const res = await request.get(
-        '/api-teacher/sysConfig/queryByParamNameList',
-        {
-          params: {
-            paramNames: 'open_live_fans_num,open_list_over_class'
-          }
-        }
-      )
-      const result = res.data
-      result.forEach((item: any) => {
-        if (item.paramName === 'open_live_fans_num') {
-          this.fansNum = Number(item.paramValue)
-        } else if (item.paramName === 'open_list_over_class') {
-          this.overClassNum = Number(item.paramValue)
-        }
-      })
-
-      // 如果没有达人认证,则需要先去认证
-      if (!state.user.data?.entryFlag) {
-        Dialog.alert({
-          message: '您尚未完成达人认证,达人认证后才可开通直播权限哦!',
-          confirmButtonColor: '#2DC7AA'
-        }).then(() => {
-          this.$router.replace('/teacherCert')
-        })
-      }
-    } catch {
-      //
-    }
-  },
-  methods: {
-    async onOpenLive() {
-      try {
-        await request.get('/api-teacher/teacher/openLive', {})
-        Toast('开通成功')
-        setTimeout(() => {
-          postMessage({ api: 'back' })
-        }, 1000)
-      } catch {
-        //
-      }
-    }
-  },
-  render() {
-    return (
-      <>
-        {state.openLiveStatus ? (
-          <ColResult
-            classImgSize="CERT"
-            type="liveCert"
-            style={{ paddingTop: '60px' }}
-            tips={'开通成功,快去开启直播或创建直播课吧'}
-          />
-        ) : (
-          <div class={styles['open-live']}>
-            <ColHeader background="transparent" border={false} title=" " />
-
-            <Cell
-              class={styles['open-teacher-info']}
-              border={false}
-              v-slots={{
-                icon: () => (
-                  <Image
-                    class={styles.userLogo}
-                    src={this.users?.headUrl || iconTeacher}
-                    fit="cover"
-                  />
-                )
-              }}
-            >
-              <div class={styles['teacher-info']}>
-                <div class={styles['teacher-name']}>
-                  {this.users?.username || `游客${this.users?.userId}`}
-
-                  {/* {this.users?.entryFlag === 1 && (
-                    <Image
-                      class={styles.cert}
-                      src={teacherCert}
-                      fit="contain"
-                    />
-                  )}
-                  {this.users?.musicianFlag === 1 && (
-                    <Image class={styles.cert} src={musicCert} fit="contain" />
-                  )} */}
-                </div>
-                <div class={styles.level}>
-                  {this.users?.starGrade ? (
-                    <Rate
-                      modelValue={this.users?.starGrade}
-                      iconPrefix="iconfont"
-                      color="#FFC459"
-                      void-icon="star_default"
-                      icon="star_active"
-                      size={15}
-                    />
-                  ) : (
-                    <span style={{ fontSize: '12px', color: '#999999' }}>
-                      暂无评分
-                    </span>
-                  )}
-                </div>
-              </div>
-            </Cell>
-
-            <div class={styles['teacher-desc']}>
-              <div class={[styles.teacherItem]}>
-                <div class={styles.content}>粉丝</div>
-                <div class={styles.title}>
-                  {/* {this.users?.fansNum}/ */}
-                  {this.fansNum}
-                </div>
-              </div>
-              <div class={styles.teacherItem} style={{ textAlign: 'right' }}>
-                <div class={styles.content}>已上课时</div>
-                <div class={styles.title}>
-                  {/* {this.users?.expTime}/ */}
-                  {this.overClassNum}
-                </div>
-              </div>
-            </div>
-
-            {!this.btnStatus ? (
-              <div class={styles['open-tips']}>
-                <Icon name={tips} size="16" />
-                <p>你尚未达到开通直播的条件</p>
-              </div>
-            ) : null}
-
-            <div class={styles['open-content']}>
-              {/* <h2>开通直播能为您带来什么?</h2>
-              <p class={styles['cert-text']}>
-                酷乐秀对平台入驻的优秀老师开放直播及直播课程的功能,帮助老师拓展获客渠道及教学场景。
-              </p>
-              <p class={styles['cert-img']}>
-                <Image
-                  src={banner1}
-                  width="100%"
-                  height="150px"
-                  fit="contain"
-                />
-              </p> */}
-              <h2>直播</h2>
-              <p class={styles['cert-text']}>
-                开通直播功能后,您可以创建自己的直播间开启直播。
-              </p>
-              <p class={styles['cert-img']}>
-                <Image
-                  src={banner2}
-                  width="100%"
-                  height="150px"
-                  fit="contain"
-                />
-              </p>
-              <h2>直播课</h2>
-              <p class={styles['cert-text']}>
-                您可制定教学方案设置直播课程,学员购买后,您可在直播间对购买直播课的学员进行直播教学,直播课程无人数上限,可极大的提高课程收入。
-              </p>
-              <p class={styles['cert-img']}>
-                <Image
-                  src={banner3}
-                  width="100%"
-                  height="150px"
-                  fit="contain"
-                />
-              </p>
-            </div>
-
-            <Sticky offsetBottom={0} position="bottom">
-              <div class={[styles['header-content'], 'btnGroup']}>
-                <Button
-                  round
-                  plain
-                  block
-                  disabled={!this.btnStatus}
-                  class={styles.openBtn}
-                  onClick={this.onOpenLive}
-                ></Button>
-              </div>
-            </Sticky>
-          </div>
-        )}
-      </>
-    )
-  }
-})
+import { defineComponent } from 'vue'
+import { Button, Cell, Dialog, Icon, Image, Rate, Sticky, Toast } from 'vant'
+import ColHeader from '@/components/col-header'
+import styles from './index.module.less'
+import { state } from '@/state'
+import { postMessage } from '@/helpers/native-message'
+
+import tips from './images/icon_tips.png'
+import banner1 from './images/1.png'
+import banner2 from './images/2.png'
+import banner3 from './images/3.png'
+import iconTeacher from '@common/images/icon_teacher.png'
+import musicCert from '@common/images/music_cert.png'
+import teacherCert from '@common/images/teacher_cert.png'
+
+import request from '@/helpers/request'
+import ColResult from '@/components/col-result'
+
+export default defineComponent({
+  name: 'live-cert',
+  computed: {
+    users() {
+      return state.user.data
+    },
+    btnStatus() {
+      // this.users?.fansNum
+      // this.users?.expTime
+      const users: any = this.users
+      const fansNum: number = this.fansNum
+      const expTime: number = this.overClassNum
+      return users?.fansNum >= fansNum && users?.expTime >= expTime
+    }
+  },
+  data() {
+    return {
+      fansNum: 0,
+      overClassNum: 0
+    }
+  },
+  async mounted() {
+    const liveFlag = state.user.data?.liveFlag
+    state.openLiveStatus = liveFlag === 1 ? true : false
+    try {
+      const res = await request.get(
+        '/api-teacher/sysConfig/queryByParamNameList',
+        {
+          params: {
+            paramNames: 'open_live_fans_num,open_list_over_class'
+          }
+        }
+      )
+      const result = res.data
+      result.forEach((item: any) => {
+        if (item.paramName === 'open_live_fans_num') {
+          this.fansNum = Number(item.paramValue)
+        } else if (item.paramName === 'open_list_over_class') {
+          this.overClassNum = Number(item.paramValue)
+        }
+      })
+
+      // 如果没有达人认证,则需要先去认证
+      if (!state.user.data?.entryFlag) {
+        Dialog.alert({
+          message: '您尚未完成达人认证,达人认证后才可开通直播权限哦!',
+          confirmButtonColor: '#2DC7AA'
+        }).then(() => {
+          this.$router.replace('/teacherCert')
+        })
+      }
+    } catch {
+      //
+    }
+  },
+  methods: {
+    async onOpenLive() {
+      try {
+        await request.get('/api-teacher/teacher/openLive', {})
+        Toast('开通成功')
+        setTimeout(() => {
+          postMessage({ api: 'back' })
+        }, 1000)
+      } catch {
+        //
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        {state.openLiveStatus ? (
+          <>
+            <ColHeader />
+            <ColResult
+              classImgSize="CERT"
+              type="liveCert"
+              style={{ paddingTop: '60px' }}
+              tips={'开通成功,快去开启直播或创建直播课吧'}
+            />
+          </>
+        ) : (
+          <div class={styles['open-live']}>
+            <ColHeader background="transparent" border={false} title=" " />
+
+            <Cell
+              class={styles['open-teacher-info']}
+              border={false}
+              v-slots={{
+                icon: () => (
+                  <Image
+                    class={styles.userLogo}
+                    src={this.users?.headUrl || iconTeacher}
+                    fit="cover"
+                  />
+                )
+              }}
+            >
+              <div class={styles['teacher-info']}>
+                <div class={styles['teacher-name']}>
+                  {this.users?.username || `游客${this.users?.userId}`}
+
+                  {/* {this.users?.entryFlag === 1 && (
+                    <Image
+                      class={styles.cert}
+                      src={teacherCert}
+                      fit="contain"
+                    />
+                  )}
+                  {this.users?.musicianFlag === 1 && (
+                    <Image class={styles.cert} src={musicCert} fit="contain" />
+                  )} */}
+                </div>
+                <div class={styles.level}>
+                  {this.users?.starGrade ? (
+                    <Rate
+                      modelValue={this.users?.starGrade}
+                      iconPrefix="iconfont"
+                      color="#FFC459"
+                      void-icon="star_default"
+                      icon="star_active"
+                      size={15}
+                    />
+                  ) : (
+                    <span style={{ fontSize: '12px', color: '#999999' }}>
+                      暂无评分
+                    </span>
+                  )}
+                </div>
+              </div>
+            </Cell>
+
+            <div class={styles['teacher-desc']}>
+              <div class={[styles.teacherItem]}>
+                <div class={styles.content}>粉丝</div>
+                <div class={styles.title}>
+                  {/* {this.users?.fansNum}/ */}
+                  {this.fansNum}
+                </div>
+              </div>
+              <div class={styles.teacherItem} style={{ textAlign: 'right' }}>
+                <div class={styles.content}>已上课时</div>
+                <div class={styles.title}>
+                  {/* {this.users?.expTime}/ */}
+                  {this.overClassNum}
+                </div>
+              </div>
+            </div>
+
+            {!this.btnStatus ? (
+              <div class={styles['open-tips']}>
+                <Icon name={tips} size="16" />
+                <p>你尚未达到开通直播的条件</p>
+              </div>
+            ) : null}
+
+            <div class={styles['open-content']}>
+              {/* <h2>开通直播能为您带来什么?</h2>
+              <p class={styles['cert-text']}>
+                酷乐秀对平台入驻的优秀老师开放直播及直播课程的功能,帮助老师拓展获客渠道及教学场景。
+              </p>
+              <p class={styles['cert-img']}>
+                <Image
+                  src={banner1}
+                  width="100%"
+                  height="150px"
+                  fit="contain"
+                />
+              </p> */}
+              <h2>直播</h2>
+              <p class={styles['cert-text']}>
+                开通直播功能后,您可以创建自己的直播间开启直播。
+              </p>
+              <p class={styles['cert-img']}>
+                <Image
+                  src={banner2}
+                  width="100%"
+                  height="150px"
+                  fit="contain"
+                />
+              </p>
+              <h2>直播课</h2>
+              <p class={styles['cert-text']}>
+                您可制定教学方案设置直播课程,学员购买后,您可在直播间对购买直播课的学员进行直播教学,直播课程无人数上限,可极大的提高课程收入。
+              </p>
+              <p class={styles['cert-img']}>
+                <Image
+                  src={banner3}
+                  width="100%"
+                  height="150px"
+                  fit="contain"
+                />
+              </p>
+            </div>
+
+            <Sticky offsetBottom={0} position="bottom">
+              <div class={[styles['header-content'], 'btnGroup']}>
+                <Button
+                  round
+                  plain
+                  block
+                  disabled={!this.btnStatus}
+                  class={styles.openBtn}
+                  onClick={this.onOpenLive}
+                ></Button>
+              </div>
+            </Sticky>
+          </div>
+        )}
+      </>
+    )
+  }
+})

+ 1 - 1
src/teacher/piano-room/course-record/index.tsx

@@ -84,7 +84,7 @@ export default defineComponent({
   render() {
     return (
       <>
-        {/* <ColHeader /> */}
+        <ColHeader />
         <Row class={[styles.countTimer, 'van-hairline--bottom']}>
           <Col span={9}>
             <p class={styles.timer}>

+ 464 - 461
src/teacher/practice-class/practice-setting.tsx

@@ -1,461 +1,464 @@
-import ColField from '@/components/col-field'
-import ColFieldGroup from '@/components/col-field-group'
-import SubjectModel from '@/business-components/subject-list'
-import ColPopup from '@/components/col-popup'
-import request from '@/helpers/request'
-import { postMessage } from '@/helpers/native-message'
-import {
-  Form,
-  Radio,
-  RadioGroup,
-  Tag,
-  Sticky,
-  Button,
-  Field,
-  ActionSheet,
-  CheckboxGroup,
-  Checkbox,
-  Dialog,
-  Toast
-} from 'vant'
-import { defineComponent } from 'vue'
-import styles from './practice-setting.module.less'
-import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
-import Timer from './model/timer'
-
-export default defineComponent({
-  name: 'PracticeSetting',
-  data() {
-    return {
-      subjectList: [],
-      chargeTypeArr: {
-        0: '否',
-        1: '是'
-      },
-      classTimeStatus: false,
-      subjectStatus: false,
-      timerStatus: false,
-      timeSetting: {
-        courseMinutes: 25,
-        freeMinutes: 5,
-        startSetting: '08:00',
-        endSetting: '18:00'
-      },
-      timerObject: {} as any,
-      form: {
-        enableFlag: 1,
-        courseMinutes: null as any,
-        freeMinutes: 0,
-        subjectIdTemp: '',
-        subjectId: [] as any[],
-        subjectPrice: [] as any[],
-        skipHolidayFlag: 1,
-        setting: ''
-      },
-      minutes: [] as any,
-      rate: 0
-    }
-  },
-  computed: {
-    choiceSubjectId() {
-      const form = this.form as any
-      const ids = form.subjectIdTemp ? form.subjectIdTemp.split(',') : []
-      return ids.map((item: any) => Number(item)) || []
-    }
-  },
-  async mounted() {
-    try {
-      // 获取手续费和分钟数
-      let config = await request.get(
-        '/api-teacher/sysConfig/queryByParamNameList',
-        {
-          params: {
-            paramNames:
-              'practice_times_setting,practice_service_fee,course_start_setting,course_end_setting'
-          }
-        }
-      )
-      let configData = config.data || []
-      configData.forEach((item: any) => {
-        if (item.paramName === 'practice_times_setting') {
-          let mins = item.paramValue ? JSON.parse(item.paramValue) : []
-          let tempArr = [] as any
-          mins.forEach((item: any) => {
-            tempArr.push({
-              ...item,
-              name: item.courseMinutes
-            })
-          })
-          this.minutes = [...tempArr]
-        }
-        if (item.paramName === 'practice_service_fee') {
-          this.rate = item.paramValue
-        }
-        if (item.paramName === 'course_start_setting') {
-          this.timeSetting.startSetting = item.paramValue
-        }
-        if (item.paramName === 'course_end_setting') {
-          this.timeSetting.endSetting = item.paramValue
-        }
-      })
-      //
-      let teacher = await request.post('/api-teacher/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: {
-    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('请选择单课时时长')
-          return
-        }
-        this.timeSetting.courseMinutes = Number(form.courseMinutes)
-        this.timeSetting.freeMinutes = Number(form.freeMinutes)
-        this.timerStatus = true
-      } catch {}
-    },
-    onChoiceTimer(item: any, status: boolean) {
-      this.form.setting = status ? '已设置' : ''
-      this.timerObject = item
-      this.timerStatus = false
-    },
-    onChoice(item: any) {
-      console.log(item)
-      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(val: any) {
-      return verifyNumberIntegerAndFloat(val)
-    },
-    async onSubmit() {
-      try {
-        const form = this.form
-        form.subjectPrice.forEach((item: any) => {
-          item.subjectName = this.getSubjectName(item.subjectId)
-        })
-        await request.post('/api-teacher/teacherFreeTime/upSet', {
-          data: {
-            ...form,
-            ...this.timerObject
-          }
-        })
-        Toast('设置成功')
-        setTimeout(() => {
-          postMessage({ api: 'back', content: {} })
-        }, 500)
-      } catch {}
-    }
-  },
-  render() {
-    return (
-      <Form style={{ paddingTop: '15px' }} onSubmit={this.onSubmit}>
-        <ColFieldGroup>
-          <ColField title="是否开启陪练课" required border={false}>
-            <RadioGroup
-              class={styles['radio-group']}
-              modelValue={this.form.enableFlag}
-              onUpdate:modelValue={val => (this.form.enableFlag = val)}
-            >
-              {['1', '0'].map((item: string) => {
-                const isActive = Number(item) === Number(this.form.enableFlag)
-                const type = isActive ? 'primary' : 'default'
-                return (
-                  <Radio class={styles.radio} name={item}>
-                    <Tag size="large" plain={isActive} type={type}>
-                      {this.chargeTypeArr[item]}
-                    </Tag>
-                  </Radio>
-                )
-              })}
-            </RadioGroup>
-          </ColField>
-          <ColField title="可教授乐器" required>
-            {this.form.subjectPrice && this.form.subjectPrice.length > 0 && (
-              <CheckboxGroup
-                modelValue={this.form.subjectId}
-                class={styles['checkbox-group']}
-                disabled
-                onClick={() => {
-                  this.subjectStatus = true
-                }}
-              >
-                {this.form.subjectPrice.map((item: any) => (
-                  <Checkbox class={styles.checkbox}>
-                    <Tag
-                      plain={true}
-                      type={'primary'}
-                      round
-                      closeable
-                      size="large"
-                      style={{ backgroundColor: '#E9FFF8' }}
-                      onClick={e => {
-                        e.stopPropagation()
-                        e.preventDefault()
-                      }}
-                      onClose={e => {
-                        e.stopPropagation()
-                        e.preventDefault()
-                        Dialog.confirm({
-                          title: '提示',
-                          message: '您是否要删除选择的乐器?',
-                          confirmButtonColor: 'var(--van-primary)'
-                        }).then(() => {
-                          const index = this.form.subjectId.indexOf(
-                            item.subjectId
-                          )
-                          if (index !== -1) {
-                            this.form.subjectId.splice(index, 1)
-                          }
-
-                          const index2 = this.form.subjectPrice.findIndex(
-                            (subject: any) =>
-                              subject.subjectId === item.subjectId
-                          )
-                          if (index2 !== -1) {
-                            this.form.subjectPrice.splice(index2, 1)
-                          }
-                          this.form.subjectIdTemp =
-                            this.form.subjectId.join(',')
-                        })
-                      }}
-                    >
-                      {this.getSubjectName(item.subjectId)}
-                      {/* {item.subjectName} */}
-                    </Tag>
-                  </Checkbox>
-                ))}
-              </CheckboxGroup>
-            )}
-            {!this.form.subjectPrice.length && (
-              <Field
-                v-model={this.form.subjectIdTemp}
-                name="courseMinutes"
-                readonly
-                onClick={() => {
-                  this.subjectStatus = true
-                }}
-                rules={[{ required: true, message: '请选择可教授乐器' }]}
-                placeholder="请选择可教授乐器"
-              />
-            )}
-          </ColField>
-          <ColField title="单课时时长" required>
-            <Field
-              v-model={this.form.courseMinutes}
-              name="courseMinutes"
-              readonly
-              isLink
-              onClick={() => {
-                this.classTimeStatus = true
-              }}
-              rules={[{ required: true, message: '请选择单课时时长' }]}
-              placeholder="请选择单课时时长"
-              v-slots={{
-                button: () => <span>分钟</span>
-              }}
-            />
-          </ColField>
-        </ColFieldGroup>
-        {this.form.subjectPrice && this.form.subjectPrice.length > 0 && (
-          <ColFieldGroup>
-            {this.form.subjectPrice.map((item: any) => (
-              <ColField
-                title={`${this.getSubjectName(item.subjectId)}声部陪练价格`}
-                required
-              >
-                <Field
-                  v-model={item.subjectPrice}
-                  name="singleMins"
-                  type="number"
-                  labelWidth={'auto'}
-                  label={`${this.form.courseMinutes || 0}分钟 / `}
-                  rules={[
-                    {
-                      required: true,
-                      message: `请选择声部陪练价格`
-                    }
-                  ]}
-                  formatter={this.onFormatter}
-                  maxlength={8}
-                  placeholder={`请选择声部陪练价格`}
-                  v-slots={{
-                    button: () => <span>元</span>
-                  }}
-                />
-              </ColField>
-            ))}
-          </ColFieldGroup>
-        )}
-
-        <ColFieldGroup>
-          <ColField title="可陪练时间段">
-            <Field
-              modelValue={this.form.setting}
-              name="singleMins"
-              readonly
-              isLink
-              onClick={this.onTimer}
-              placeholder="未设置"
-            />
-          </ColField>
-        </ColFieldGroup>
-
-        <ColFieldGroup>
-          <ColField required title="是否跳过节假日" border={false}>
-            <RadioGroup
-              class={styles['radio-group']}
-              modelValue={this.form.skipHolidayFlag}
-              onUpdate:modelValue={val => (this.form.skipHolidayFlag = val)}
-            >
-              {['1', '0'].map((item: string) => {
-                const isActive =
-                  Number(item) === Number(this.form.skipHolidayFlag)
-                const type = isActive ? 'primary' : 'default'
-                return (
-                  <Radio class={styles.radio} name={item}>
-                    <Tag size="large" plain={isActive} type={type}>
-                      {this.chargeTypeArr[item]}
-                    </Tag>
-                  </Radio>
-                )
-              })}
-            </RadioGroup>
-          </ColField>
-        </ColFieldGroup>
-
-        <Sticky offsetBottom={0} position="bottom">
-          <div class={'btnGroup'}>
-            <Button block round type="primary" native-type="submit">
-              提交
-            </Button>
-          </div>
-        </Sticky>
-
-        <ColPopup v-model={this.subjectStatus} destroy>
-          <SubjectModel
-            max={5}
-            single
-            subjectList={this.subjectList}
-            choiceSubjectIds={this.choiceSubjectId}
-            onChoice={this.onChoice}
-          />
-        </ColPopup>
-
-        <ColPopup v-model={this.timerStatus} destroy>
-          <Timer
-            onChoice={this.onChoiceTimer}
-            timerObject={this.timerObject}
-            courseMinutes={Number(this.timeSetting.courseMinutes)}
-            freeMinutes={Number(this.timeSetting.freeMinutes)}
-            startSetting={this.timeSetting.startSetting}
-            endSetting={this.timeSetting.endSetting}
-          />
-        </ColPopup>
-
-        <ActionSheet
-          v-model:show={this.classTimeStatus}
-          actions={this.minutes}
-          cancelText="取消"
-          closeOnClickAction
-          onSelect={this.onSelect}
-        />
-      </Form>
-    )
-  }
-})
+import ColField from '@/components/col-field'
+import ColFieldGroup from '@/components/col-field-group'
+import SubjectModel from '@/business-components/subject-list'
+import ColPopup from '@/components/col-popup'
+import request from '@/helpers/request'
+import { postMessage } from '@/helpers/native-message'
+import {
+  Form,
+  Radio,
+  RadioGroup,
+  Tag,
+  Sticky,
+  Button,
+  Field,
+  ActionSheet,
+  CheckboxGroup,
+  Checkbox,
+  Dialog,
+  Toast
+} from 'vant'
+import { defineComponent } from 'vue'
+import styles from './practice-setting.module.less'
+import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
+import Timer from './model/timer'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'PracticeSetting',
+  data() {
+    return {
+      subjectList: [],
+      chargeTypeArr: {
+        0: '否',
+        1: '是'
+      },
+      classTimeStatus: false,
+      subjectStatus: false,
+      timerStatus: false,
+      timeSetting: {
+        courseMinutes: 25,
+        freeMinutes: 5,
+        startSetting: '08:00',
+        endSetting: '18:00'
+      },
+      timerObject: {} as any,
+      form: {
+        enableFlag: 1,
+        courseMinutes: null as any,
+        freeMinutes: 0,
+        subjectIdTemp: '',
+        subjectId: [] as any[],
+        subjectPrice: [] as any[],
+        skipHolidayFlag: 1,
+        setting: ''
+      },
+      minutes: [] as any,
+      rate: 0
+    }
+  },
+  computed: {
+    choiceSubjectId() {
+      const form = this.form as any
+      const ids = form.subjectIdTemp ? form.subjectIdTemp.split(',') : []
+      return ids.map((item: any) => Number(item)) || []
+    }
+  },
+  async mounted() {
+    try {
+      // 获取手续费和分钟数
+      const config = await request.get(
+        '/api-teacher/sysConfig/queryByParamNameList',
+        {
+          params: {
+            paramNames:
+              'practice_times_setting,practice_service_fee,course_start_setting,course_end_setting'
+          }
+        }
+      )
+      const configData = config.data || []
+      configData.forEach((item: any) => {
+        if (item.paramName === 'practice_times_setting') {
+          const mins = item.paramValue ? JSON.parse(item.paramValue) : []
+          const tempArr = [] as any
+          mins.forEach((item: any) => {
+            tempArr.push({
+              ...item,
+              name: item.courseMinutes
+            })
+          })
+          this.minutes = [...tempArr]
+        }
+        if (item.paramName === 'practice_service_fee') {
+          this.rate = item.paramValue
+        }
+        if (item.paramName === 'course_start_setting') {
+          this.timeSetting.startSetting = item.paramValue
+        }
+        if (item.paramName === 'course_end_setting') {
+          this.timeSetting.endSetting = item.paramValue
+        }
+      })
+      //
+      const teacher = await request.post('/api-teacher/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) : []
+        }
+
+        const tempIds: any = []
+        const 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: {
+    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('请选择单课时时长')
+          return
+        }
+        this.timeSetting.courseMinutes = Number(form.courseMinutes)
+        this.timeSetting.freeMinutes = Number(form.freeMinutes)
+        this.timerStatus = true
+      } catch {}
+    },
+    onChoiceTimer(item: any, status: boolean) {
+      this.form.setting = status ? '已设置' : ''
+      this.timerObject = item
+      this.timerStatus = false
+    },
+    onChoice(item: any) {
+      console.log(item)
+      const tempItem = item || []
+      this.form.subjectId = tempItem
+      this.form.subjectIdTemp = tempItem.join(',') || ''
+      const 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(val: any) {
+      return verifyNumberIntegerAndFloat(val)
+    },
+    async onSubmit() {
+      try {
+        const form = this.form
+        form.subjectPrice.forEach((item: any) => {
+          item.subjectName = this.getSubjectName(item.subjectId)
+        })
+        await request.post('/api-teacher/teacherFreeTime/upSet', {
+          data: {
+            ...form,
+            ...this.timerObject
+          }
+        })
+        Toast('设置成功')
+        setTimeout(() => {
+          postMessage({ api: 'back', content: {} })
+        }, 500)
+      } catch {}
+    }
+  },
+  render() {
+    return (
+      <Form style={{ paddingTop: '15px' }} onSubmit={this.onSubmit}>
+        <ColHeader />
+        <ColFieldGroup>
+          <ColField title="是否开启陪练课" required border={false}>
+            <RadioGroup
+              class={styles['radio-group']}
+              modelValue={this.form.enableFlag}
+              onUpdate:modelValue={val => (this.form.enableFlag = val)}
+            >
+              {['1', '0'].map((item: string) => {
+                const isActive = Number(item) === Number(this.form.enableFlag)
+                const type = isActive ? 'primary' : 'default'
+                return (
+                  <Radio class={styles.radio} name={item}>
+                    <Tag size="large" plain={isActive} type={type}>
+                      {this.chargeTypeArr[item]}
+                    </Tag>
+                  </Radio>
+                )
+              })}
+            </RadioGroup>
+          </ColField>
+          <ColField title="可教授乐器" required>
+            {this.form.subjectPrice && this.form.subjectPrice.length > 0 && (
+              <CheckboxGroup
+                modelValue={this.form.subjectId}
+                class={styles['checkbox-group']}
+                disabled
+                onClick={() => {
+                  this.subjectStatus = true
+                }}
+              >
+                {this.form.subjectPrice.map((item: any) => (
+                  <Checkbox class={styles.checkbox}>
+                    <Tag
+                      plain={true}
+                      type={'primary'}
+                      round
+                      closeable
+                      size="large"
+                      style={{ backgroundColor: '#E9FFF8' }}
+                      onClick={e => {
+                        e.stopPropagation()
+                        e.preventDefault()
+                      }}
+                      onClose={e => {
+                        e.stopPropagation()
+                        e.preventDefault()
+                        Dialog.confirm({
+                          title: '提示',
+                          message: '您是否要删除选择的乐器?',
+                          confirmButtonColor: 'var(--van-primary)'
+                        }).then(() => {
+                          const index = this.form.subjectId.indexOf(
+                            item.subjectId
+                          )
+                          if (index !== -1) {
+                            this.form.subjectId.splice(index, 1)
+                          }
+
+                          const index2 = this.form.subjectPrice.findIndex(
+                            (subject: any) =>
+                              subject.subjectId === item.subjectId
+                          )
+                          if (index2 !== -1) {
+                            this.form.subjectPrice.splice(index2, 1)
+                          }
+                          this.form.subjectIdTemp =
+                            this.form.subjectId.join(',')
+                        })
+                      }}
+                    >
+                      {this.getSubjectName(item.subjectId)}
+                      {/* {item.subjectName} */}
+                    </Tag>
+                  </Checkbox>
+                ))}
+              </CheckboxGroup>
+            )}
+            {!this.form.subjectPrice.length && (
+              <Field
+                v-model={this.form.subjectIdTemp}
+                name="courseMinutes"
+                readonly
+                onClick={() => {
+                  this.subjectStatus = true
+                }}
+                rules={[{ required: true, message: '请选择可教授乐器' }]}
+                placeholder="请选择可教授乐器"
+              />
+            )}
+          </ColField>
+          <ColField title="单课时时长" required>
+            <Field
+              v-model={this.form.courseMinutes}
+              name="courseMinutes"
+              readonly
+              isLink
+              onClick={() => {
+                this.classTimeStatus = true
+              }}
+              rules={[{ required: true, message: '请选择单课时时长' }]}
+              placeholder="请选择单课时时长"
+              v-slots={{
+                button: () => <span>分钟</span>
+              }}
+            />
+          </ColField>
+        </ColFieldGroup>
+        {this.form.subjectPrice && this.form.subjectPrice.length > 0 && (
+          <ColFieldGroup>
+            {this.form.subjectPrice.map((item: any) => (
+              <ColField
+                title={`${this.getSubjectName(item.subjectId)}声部陪练价格`}
+                required
+              >
+                <Field
+                  v-model={item.subjectPrice}
+                  name="singleMins"
+                  type="number"
+                  labelWidth={'auto'}
+                  label={`${this.form.courseMinutes || 0}分钟 / `}
+                  rules={[
+                    {
+                      required: true,
+                      message: `请选择声部陪练价格`
+                    }
+                  ]}
+                  formatter={this.onFormatter}
+                  maxlength={8}
+                  placeholder={`请选择声部陪练价格`}
+                  v-slots={{
+                    button: () => <span>元</span>
+                  }}
+                />
+              </ColField>
+            ))}
+          </ColFieldGroup>
+        )}
+
+        <ColFieldGroup>
+          <ColField title="可陪练时间段">
+            <Field
+              modelValue={this.form.setting}
+              name="singleMins"
+              readonly
+              isLink
+              onClick={this.onTimer}
+              placeholder="未设置"
+            />
+          </ColField>
+        </ColFieldGroup>
+
+        <ColFieldGroup>
+          <ColField required title="是否跳过节假日" border={false}>
+            <RadioGroup
+              class={styles['radio-group']}
+              modelValue={this.form.skipHolidayFlag}
+              onUpdate:modelValue={val => (this.form.skipHolidayFlag = val)}
+            >
+              {['1', '0'].map((item: string) => {
+                const isActive =
+                  Number(item) === Number(this.form.skipHolidayFlag)
+                const type = isActive ? 'primary' : 'default'
+                return (
+                  <Radio class={styles.radio} name={item}>
+                    <Tag size="large" plain={isActive} type={type}>
+                      {this.chargeTypeArr[item]}
+                    </Tag>
+                  </Radio>
+                )
+              })}
+            </RadioGroup>
+          </ColField>
+        </ColFieldGroup>
+
+        <Sticky offsetBottom={0} position="bottom">
+          <div class={'btnGroup'}>
+            <Button block round type="primary" native-type="submit">
+              提交
+            </Button>
+          </div>
+        </Sticky>
+
+        <ColPopup v-model={this.subjectStatus} destroy>
+          <ColHeader />
+          <SubjectModel
+            max={5}
+            single
+            subjectList={this.subjectList}
+            choiceSubjectIds={this.choiceSubjectId}
+            onChoice={this.onChoice}
+          />
+        </ColPopup>
+
+        <ColPopup v-model={this.timerStatus} destroy>
+          <Timer
+            onChoice={this.onChoiceTimer}
+            timerObject={this.timerObject}
+            courseMinutes={Number(this.timeSetting.courseMinutes)}
+            freeMinutes={Number(this.timeSetting.freeMinutes)}
+            startSetting={this.timeSetting.startSetting}
+            endSetting={this.timeSetting.endSetting}
+          />
+        </ColPopup>
+
+        <ActionSheet
+          v-model:show={this.classTimeStatus}
+          actions={this.minutes}
+          cancelText="取消"
+          closeOnClickAction
+          onSelect={this.onSelect}
+        />
+      </Form>
+    )
+  }
+})

+ 7 - 3
src/teacher/review/list.tsx

@@ -8,6 +8,8 @@ import { useRoute, useRouter } from 'vue-router'
 import ColResult from '@/components/col-result'
 import styles from '../../views/music/list/index.module.less'
 import { getRandomKey, musicBuy } from '../../views/music/music'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
 
 const noop = () => {}
 
@@ -73,7 +75,7 @@ export default defineComponent({
           }
         })
         if (data.value) {
-          let result = (data.value?.rows || []).concat(res.data.rows || [])
+          const result = (data.value?.rows || []).concat(res.data.rows || [])
           data.value.rows = result
         }
         data.value = data.value || res.data
@@ -127,9 +129,10 @@ export default defineComponent({
           error={isError.value}
         >
           {!hideSearch && (
-            <Sticky class={styles.sticky}>
+            <TheSticky>
+              <ColHeader border={false} />
               <Search onSearch={onSearch} />
-            </Sticky>
+            </TheSticky>
           )}
 
           {data.value && data.value.rows.length
@@ -165,6 +168,7 @@ export default defineComponent({
           onClosed={() => (detail.value = null)}
           onClose={() => (visivle.value = false)}
           teleport="body"
+          round
         >
           <Field
             label="曲目名称"

+ 2 - 0
src/teacher/share-page/track-review-activity/index.tsx

@@ -7,6 +7,7 @@ import dayjs from 'dayjs'
 import ShareModel from './share-model'
 import { postMessage } from '@/helpers/native-message'
 import { state } from '@/state'
+import ColHeader from '@/components/col-header'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -144,6 +145,7 @@ export default defineComponent({
           backgroundSize: 'contain'
         }}
       >
+        <ColHeader />
         <img class={styles.bannerImg} src={this.activeInfo.subjectUrl} />
         <div class={styles.reviewContainer}>
           <div class={[styles.section, styles.activeTime]}>

+ 111 - 105
src/teacher/teacher-cert/cert-info.tsx

@@ -1,105 +1,111 @@
-import ColHeader from '@/components/col-header'
-import { useEventListener } from '@vant/use'
-import { Button, Icon, Image, Sticky } from 'vant'
-import { defineComponent, Transition } from 'vue'
-import styles from './cert-info.module.less'
-import { teacherState } from './teacherState'
-/**
- * 动态引入文件
- * @param fileName 文件名
- * @param path 文件路径
- */
-export const getAssetsHomeFile = (fileName: string) => {
-  const path = `./images/${fileName}`
-  const modules = import.meta.globEager('./images/*')
-  return modules[path].default
-}
-
-export default defineComponent({
-  name: 'cert-info',
-  // data() {
-  //   return {
-  //     headStatus: false
-  //   }
-  // },
-  methods: {
-    onClick() {
-      if (teacherState.authStatus) {
-        return
-      }
-      teacherState.authStatus = true
-    }
-  },
-  render() {
-    return (
-      <div class={styles['cert-info']}>
-        <div class={styles.certWrapper}>
-          <div class={styles.section}>
-            <div class={styles.title}>
-              <img src={getAssetsHomeFile('1.png')} />
-              什么是酷乐秀音乐达人?
-            </div>
-
-            <p class={styles.content}>
-              酷乐秀音乐人是酷乐秀优质的内容创作提供者,包含乐谱歌单达人,教学视频分享达人,演奏MLog达人,直播up达人
-            </p>
-
-            <div class={styles.operation}>
-              <div class={styles.item}>
-                <img src={getAssetsHomeFile('logo1.png')} />
-                <p>乐谱歌单</p>
-              </div>
-              <div class={styles.item}>
-                <img src={getAssetsHomeFile('logo2.png')} />
-                <p>教学视频</p>
-              </div>
-              <div class={styles.item}>
-                <img src={getAssetsHomeFile('logo3.png')} />
-                <p>演奏MLog</p>
-              </div>
-              <div class={styles.item}>
-                <img src={getAssetsHomeFile('logo4.png')} />
-                <p>直播up达人</p>
-              </div>
-            </div>
-          </div>
-
-          <div class={styles.section}>
-            <div class={styles.title}>
-              <img src={getAssetsHomeFile('2.png')} />
-              酷乐秀能为达人提供哪些服务?
-            </div>
-
-            <p class={styles.content}>
-              酷乐秀提供现金激励,商业分成等福利政策,帮助达人内容变现,
-              提供流量扶持等政策,帮助达人推广优质內容。
-            </p>
-          </div>
-
-          <div class={styles.section}>
-            <div class={styles.title}>
-              <img src={getAssetsHomeFile('3.png')} />
-              成为酷乐秀达人的意义是什么?
-            </div>
-
-            <p class={styles.content}>
-              酷乐秀给所有热爱音乐热爱分享热爱转播技能的达人一个分享交流创作的平台,让音乐圈无圈,让爱好者无界。
-            </p>
-          </div>
-        </div>
-
-        <Sticky position="bottom" offsetBottom={0}>
-          <div class={styles['btn-group']}>
-            <div
-              class={[
-                styles.btn,
-                teacherState.authStatus ? styles.disabled : null
-              ]}
-              onClick={this.onClick}
-            ></div>
-          </div>
-        </Sticky>
-      </div>
-    )
-  }
-})
+import ColHeader from '@/components/col-header'
+import { useEventListener } from '@vant/use'
+import { Button, Icon, Image, Sticky } from 'vant'
+import { defineComponent, Transition } from 'vue'
+import styles from './cert-info.module.less'
+import { teacherState } from './teacherState'
+/**
+ * 动态引入文件
+ * @param fileName 文件名
+ * @param path 文件路径
+ */
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `./images/${fileName}`
+  const modules = import.meta.globEager('./images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'cert-info',
+  // data() {
+  //   return {
+  //     headStatus: false
+  //   }
+  // },
+  methods: {
+    onClick() {
+      if (teacherState.authStatus) {
+        return
+      }
+      teacherState.authStatus = true
+    }
+  },
+  render() {
+    return (
+      <div class={styles['cert-info']}>
+        <ColHeader
+          background="transparent"
+          border={false}
+          title=" "
+          // color="white"
+        />
+        <div class={styles.certWrapper}>
+          <div class={styles.section}>
+            <div class={styles.title}>
+              <img src={getAssetsHomeFile('1.png')} />
+              什么是酷乐秀音乐达人?
+            </div>
+
+            <p class={styles.content}>
+              酷乐秀音乐人是酷乐秀优质的内容创作提供者,包含乐谱歌单达人,教学视频分享达人,演奏MLog达人,直播up达人
+            </p>
+
+            <div class={styles.operation}>
+              <div class={styles.item}>
+                <img src={getAssetsHomeFile('logo1.png')} />
+                <p>乐谱歌单</p>
+              </div>
+              <div class={styles.item}>
+                <img src={getAssetsHomeFile('logo2.png')} />
+                <p>教学视频</p>
+              </div>
+              <div class={styles.item}>
+                <img src={getAssetsHomeFile('logo3.png')} />
+                <p>演奏MLog</p>
+              </div>
+              <div class={styles.item}>
+                <img src={getAssetsHomeFile('logo4.png')} />
+                <p>直播up达人</p>
+              </div>
+            </div>
+          </div>
+
+          <div class={styles.section}>
+            <div class={styles.title}>
+              <img src={getAssetsHomeFile('2.png')} />
+              酷乐秀能为达人提供哪些服务?
+            </div>
+
+            <p class={styles.content}>
+              酷乐秀提供现金激励,商业分成等福利政策,帮助达人内容变现,
+              提供流量扶持等政策,帮助达人推广优质內容。
+            </p>
+          </div>
+
+          <div class={styles.section}>
+            <div class={styles.title}>
+              <img src={getAssetsHomeFile('3.png')} />
+              成为酷乐秀达人的意义是什么?
+            </div>
+
+            <p class={styles.content}>
+              酷乐秀给所有热爱音乐热爱分享热爱转播技能的达人一个分享交流创作的平台,让音乐圈无圈,让爱好者无界。
+            </p>
+          </div>
+        </div>
+
+        <Sticky position="bottom" offsetBottom={0}>
+          <div class={styles['btn-group']}>
+            <div
+              class={[
+                styles.btn,
+                teacherState.authStatus ? styles.disabled : null
+              ]}
+              onClick={this.onClick}
+            ></div>
+          </div>
+        </Sticky>
+      </div>
+    )
+  }
+})

+ 222 - 220
src/teacher/teacher-cert/cert-two.tsx

@@ -1,220 +1,222 @@
-import { defineComponent } from 'vue'
-import ColField from '@/components/col-field'
-import SubjectModel from '../../business-components/subject-list'
-import styles from './cert-two.module.less'
-import { Button, Col, Dialog, Field, Icon, Popup, Row, Tag } from 'vant'
-import { teacherState } from './teacherState'
-
-import icon1 from './images/icon_1.png'
-import icon2 from './images/icon_2.png'
-import icon3 from './images/icon_3.png'
-import ColPopup from '@/components/col-popup'
-import request from '@/helpers/request'
-import ColFieldGroup from '@/components/col-field-group'
-import ColUploadVideo from '@/components/col-upload-video'
-import ColUpload from '@/components/col-upload'
-
-export default defineComponent({
-  name: 'cert-two',
-  data() {
-    return {
-      // choiceSubject: [],
-      subjectStatus: false
-    }
-  },
-  computed: {
-    choiceSubjectIds() {
-      // 选择的科目编号
-      let ids = teacherState.teacherCert.subjectId
-        ? teacherState.teacherCert.subjectId.split(',')
-        : []
-      ids = ids.map((item: any) => Number(item))
-      return ids
-    },
-    subjectList() {
-      // 学科列表
-      return teacherState.subjectList || []
-    },
-    choiceSubject() {
-      // 选择的科目
-      let tempArr: any[] = []
-      console.log(this.choiceSubjectIds, this.subjectList)
-      this.subjectList.forEach((parent: any) => {
-        parent.subjects &&
-          parent.subjects.forEach((sub: any) => {
-            if (this.choiceSubjectIds.includes(sub.id)) {
-              tempArr.push(sub as never)
-            }
-          })
-      })
-      console.log(tempArr, 'tempArr')
-      return tempArr
-    }
-  },
-  methods: {
-    onChoice(item: any) {
-      teacherState.teacherCert.subjectId = item.join(',') || ''
-
-      this.subjectStatus = false
-    },
-    async onSubjectOpen() {
-      this.subjectStatus = true
-    }
-  },
-  render() {
-    return (
-      <div>
-        <ColFieldGroup class={styles.items}>
-          <ColField
-            title="可教授乐器(可多选)"
-            border={false}
-            class={styles.itemSubject}
-            required
-            v-slots={{
-              icon: () => <Icon name={icon1} size="24" />,
-              right: () => (
-                <Button
-                  class={styles.select}
-                  round
-                  type="primary"
-                  size="small"
-                  onClick={this.onSubjectOpen}
-                >
-                  选择
-                </Button>
-              )
-            }}
-          >
-            {this.choiceSubject && this.choiceSubject.length > 0 ? (
-              <div class={styles['tag-list']}>
-                {this.choiceSubject.map((item: any) => (
-                  <Tag
-                    type="primary"
-                    plain
-                    round
-                    closeable
-                    size="large"
-                    style={{ backgroundColor: '#E9FFF8' }}
-                    onClose={() => {
-                      Dialog.confirm({
-                        title: '提示',
-                        message: '请确认是否删除?',
-                        confirmButtonColor: 'var(--van-primary)'
-                      }).then(() => {
-                        let ids = teacherState.teacherCert.subjectId
-                          ? teacherState.teacherCert.subjectId.split(',')
-                          : []
-                        ids = ids.map((item: any) => Number(item))
-                        const index = ids.indexOf(item.id)
-                        if (index !== -1) {
-                          ids.splice(index, 1)
-                        }
-                        teacherState.teacherCert.subjectId = ids.join(',')
-                      })
-                    }}
-                  >
-                    {item.name}
-                  </Tag>
-                ))}
-              </div>
-            ) : null}
-          </ColField>
-        </ColFieldGroup>
-
-        <ColFieldGroup class={styles.items}>
-          <ColField
-            title="个人简介"
-            border={false}
-            v-slots={{
-              icon: () => <Icon name={icon2} size="24" />,
-              right: () => (
-                <div class={styles.limit}>
-                  {teacherState.teacherCert.introduction?.length || 0}/200
-                </div>
-              )
-            }}
-          >
-            <Field
-              v-model={teacherState.teacherCert.introduction}
-              rows="3"
-              autosize
-              maxlength={200}
-              style={{ fontSize: '13px' }}
-              type="textarea"
-              placeholder="例:毕业于中国音乐学院长笛专业,曾获得中国青年管乐演奏大赛一等奖,具有8年教学经验,能够将专业知识通过简单易懂的方式教授给学员。"
-            />
-          </ColField>
-        </ColFieldGroup>
-
-        <ColFieldGroup class={styles.items}>
-          {teacherState.teacherCert.styleVideo.map((item: any) => (
-            <ColField
-              title="个人风采"
-              required
-              border={false}
-              v-slots={{
-                icon: () => <Icon name={icon3} size="24" />
-              }}
-            >
-              <Row
-                justify="space-between"
-                style={{ width: '100%', paddingTop: '12px' }}
-              >
-                <Col span={12}>
-                  <Field
-                    style={{ padding: 0 }}
-                    name="videoUrl"
-                    rules={[{ required: true, message: '请上传课程视频' }]}
-                    v-slots={{
-                      input: () => (
-                        <ColUploadVideo
-                          // bucket="video-course"
-                          v-model={item.videoUrl}
-                          // v-model:posterUrl={item.posterUrl}
-                          class={styles.upload}
-                          tips="点击上传视频"
-                        />
-                      )
-                    }}
-                  />
-                </Col>
-                <Col span={12}>
-                  <Field
-                    style={{ padding: 0 }}
-                    name="coverUrl"
-                    rules={[{ required: true, message: '请上传视频封面' }]}
-                    error
-                    v-slots={{
-                      input: () => (
-                        <ColUpload
-                          class={styles.upload}
-                          cropper
-                          // bucket="video-course"
-                          options={{
-                            fixedNumber: [1.77, 1],
-                            autoCropWidth: 750,
-                            autoCropHeight: 424
-                          }}
-                          v-model={item.cover}
-                          tips="点击上传视频封面"
-                        />
-                      )
-                    }}
-                  />
-                </Col>
-              </Row>
-            </ColField>
-          ))}
-        </ColFieldGroup>
-
-        <ColPopup v-model={this.subjectStatus} destroy>
-          <SubjectModel
-            subjectList={this.subjectList}
-            choiceSubjectIds={this.choiceSubjectIds}
-            onChoice={this.onChoice}
-          />
-        </ColPopup>
-      </div>
-    )
-  }
-})
+import { defineComponent } from 'vue'
+import ColField from '@/components/col-field'
+import SubjectModel from '../../business-components/subject-list'
+import styles from './cert-two.module.less'
+import { Button, Col, Dialog, Field, Icon, Popup, Row, Tag } from 'vant'
+import { teacherState } from './teacherState'
+
+import icon1 from './images/icon_1.png'
+import icon2 from './images/icon_2.png'
+import icon3 from './images/icon_3.png'
+import ColPopup from '@/components/col-popup'
+import request from '@/helpers/request'
+import ColFieldGroup from '@/components/col-field-group'
+import ColUploadVideo from '@/components/col-upload-video'
+import ColUpload from '@/components/col-upload'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'cert-two',
+  data() {
+    return {
+      // choiceSubject: [],
+      subjectStatus: false
+    }
+  },
+  computed: {
+    choiceSubjectIds() {
+      // 选择的科目编号
+      let ids = teacherState.teacherCert.subjectId
+        ? teacherState.teacherCert.subjectId.split(',')
+        : []
+      ids = ids.map((item: any) => Number(item))
+      return ids
+    },
+    subjectList() {
+      // 学科列表
+      return teacherState.subjectList || []
+    },
+    choiceSubject() {
+      // 选择的科目
+      const tempArr: any[] = []
+      console.log(this.choiceSubjectIds, this.subjectList)
+      this.subjectList.forEach((parent: any) => {
+        parent.subjects &&
+          parent.subjects.forEach((sub: any) => {
+            if (this.choiceSubjectIds.includes(sub.id)) {
+              tempArr.push(sub as never)
+            }
+          })
+      })
+      console.log(tempArr, 'tempArr')
+      return tempArr
+    }
+  },
+  methods: {
+    onChoice(item: any) {
+      teacherState.teacherCert.subjectId = item.join(',') || ''
+
+      this.subjectStatus = false
+    },
+    async onSubjectOpen() {
+      this.subjectStatus = true
+    }
+  },
+  render() {
+    return (
+      <div>
+        <ColFieldGroup class={styles.items}>
+          <ColField
+            title="可教授乐器(可多选)"
+            border={false}
+            class={styles.itemSubject}
+            required
+            v-slots={{
+              icon: () => <Icon name={icon1} size="24" />,
+              right: () => (
+                <Button
+                  class={styles.select}
+                  round
+                  type="primary"
+                  size="small"
+                  onClick={this.onSubjectOpen}
+                >
+                  选择
+                </Button>
+              )
+            }}
+          >
+            {this.choiceSubject && this.choiceSubject.length > 0 ? (
+              <div class={styles['tag-list']}>
+                {this.choiceSubject.map((item: any) => (
+                  <Tag
+                    type="primary"
+                    plain
+                    round
+                    closeable
+                    size="large"
+                    style={{ backgroundColor: '#E9FFF8' }}
+                    onClose={() => {
+                      Dialog.confirm({
+                        title: '提示',
+                        message: '请确认是否删除?',
+                        confirmButtonColor: 'var(--van-primary)'
+                      }).then(() => {
+                        let ids = teacherState.teacherCert.subjectId
+                          ? teacherState.teacherCert.subjectId.split(',')
+                          : []
+                        ids = ids.map((item: any) => Number(item))
+                        const index = ids.indexOf(item.id)
+                        if (index !== -1) {
+                          ids.splice(index, 1)
+                        }
+                        teacherState.teacherCert.subjectId = ids.join(',')
+                      })
+                    }}
+                  >
+                    {item.name}
+                  </Tag>
+                ))}
+              </div>
+            ) : null}
+          </ColField>
+        </ColFieldGroup>
+
+        <ColFieldGroup class={styles.items}>
+          <ColField
+            title="个人简介"
+            border={false}
+            v-slots={{
+              icon: () => <Icon name={icon2} size="24" />,
+              right: () => (
+                <div class={styles.limit}>
+                  {teacherState.teacherCert.introduction?.length || 0}/200
+                </div>
+              )
+            }}
+          >
+            <Field
+              v-model={teacherState.teacherCert.introduction}
+              rows="3"
+              autosize
+              maxlength={200}
+              style={{ fontSize: '13px' }}
+              type="textarea"
+              placeholder="例:毕业于中国音乐学院长笛专业,曾获得中国青年管乐演奏大赛一等奖,具有8年教学经验,能够将专业知识通过简单易懂的方式教授给学员。"
+            />
+          </ColField>
+        </ColFieldGroup>
+
+        <ColFieldGroup class={styles.items}>
+          {teacherState.teacherCert.styleVideo.map((item: any) => (
+            <ColField
+              title="个人风采"
+              required
+              border={false}
+              v-slots={{
+                icon: () => <Icon name={icon3} size="24" />
+              }}
+            >
+              <Row
+                justify="space-between"
+                style={{ width: '100%', paddingTop: '12px' }}
+              >
+                <Col span={12}>
+                  <Field
+                    style={{ padding: 0 }}
+                    name="videoUrl"
+                    rules={[{ required: true, message: '请上传课程视频' }]}
+                    v-slots={{
+                      input: () => (
+                        <ColUploadVideo
+                          // bucket="video-course"
+                          v-model={item.videoUrl}
+                          // v-model:posterUrl={item.posterUrl}
+                          class={styles.upload}
+                          tips="点击上传视频"
+                        />
+                      )
+                    }}
+                  />
+                </Col>
+                <Col span={12}>
+                  <Field
+                    style={{ padding: 0 }}
+                    name="coverUrl"
+                    rules={[{ required: true, message: '请上传视频封面' }]}
+                    error
+                    v-slots={{
+                      input: () => (
+                        <ColUpload
+                          class={styles.upload}
+                          cropper
+                          // bucket="video-course"
+                          options={{
+                            fixedNumber: [1.77, 1],
+                            autoCropWidth: 750,
+                            autoCropHeight: 424
+                          }}
+                          v-model={item.cover}
+                          tips="点击上传视频封面"
+                        />
+                      )
+                    }}
+                  />
+                </Col>
+              </Row>
+            </ColField>
+          ))}
+        </ColFieldGroup>
+
+        <ColPopup v-model={this.subjectStatus} destroy>
+          <ColHeader />
+          <SubjectModel
+            subjectList={this.subjectList}
+            choiceSubjectIds={this.choiceSubjectIds}
+            onChoice={this.onChoice}
+          />
+        </ColPopup>
+      </div>
+    )
+  }
+})

+ 236 - 231
src/teacher/teacher-cert/index.tsx

@@ -1,231 +1,236 @@
-import { Button, Sticky, Toast } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-import CertInfo from './cert-info'
-import ColProtocol from '@/components/col-protocol'
-import { teacherState } from './teacherState'
-import Steps from './steps'
-import CertOne from './cert-one'
-import CertTwo from './cert-two'
-import CertThree from './cert-three'
-import { checkIDCard } from '@/helpers/validate'
-import request from '@/helpers/request'
-import ColResult from '@/components/col-result'
-import { state } from '@/state'
-
-export default defineComponent({
-  name: 'teacherCert',
-  data() {
-    const query = this.$route.query
-    return {
-      agreeStatus: false
-    }
-  },
-  async mounted() {
-    try {
-      // 老师入驻状态 0、未申请 UNPAALY、未申请 DOING、审核中 PASS、通过 UNPASS、不通过
-      const entryStatus = state.user.data?.entryStatus || 0
-      teacherState.authStatus =
-        entryStatus === 'DOING' || entryStatus === 'PASS' ? true : false
-      // 如果已经认证,则不用获取声部信息
-      if (teacherState.authStatus) {
-        teacherState.active = teacherState.authStatus ? 4 : 1
-        return
-      }
-
-      if (teacherState.subjectList.length <= 0) {
-        const res = await request.get('/api-teacher/subject/subjectSelect')
-        teacherState.subjectList = res.data || []
-      }
-
-      const teacherInfo = await request.get(
-        '/api-teacher/teacherAuthEntryRecord/getLastRecordByUserId'
-      )
-      teacherState.teacherInfo = teacherInfo.data || {}
-      const info = teacherState.teacherInfo
-      teacherState.teacherCert.introduction = info.introduction
-      teacherState.teacherCert.subjectId = info.subjectId
-      teacherState.teacherCert.graduateSchool = info.graduateSchool
-      teacherState.teacherCert.subject = info.subject
-      teacherState.teacherCert.styleVideo = info.styleVideoJson
-        ? JSON.parse(info.styleVideoJson)
-        : [{ videoUrl: '', cover: '' }]
-      teacherState.teacherCert.gradCertificate = info.gradCertificate
-      teacherState.teacherCert.degreeCertificate = info.degreeCertificate
-      teacherState.teacherCert.teacherCertificate = info.teacherCertificate
-    } catch {
-      //
-    }
-  },
-  computed: {
-    authStatus(): Boolean {
-      return !!teacherState.authStatus
-    },
-    userAuth() {
-      // 判断是否实名过
-      const users = state.user.data || {}
-      return !!(users.idCardNo && users.realName)
-    }
-  },
-  methods: {
-    async next() {
-      const realName = teacherState.teacherCert.realName
-      if (!realName) {
-        Toast('请填写真实姓名')
-        return
-      }
-      const idCardNo = teacherState.teacherCert.idCardNo
-      if (!this.userAuth) {
-        if (!checkIDCard(idCardNo || '')) {
-          Toast('请填写正确的身份证号码')
-          return false
-        }
-      }
-
-      if (!this.agreeStatus) {
-        Toast('请阅读并同意协议')
-        return
-      }
-
-      if (!teacherState.teacherCert.birthdate) {
-        Toast('请选择出生日期')
-        return
-      }
-
-      try {
-        if (!this.userAuth) {
-          await request.post('/api-teacher/teacher/realNameAuth', {
-            data: {
-              realName,
-              idCardNo,
-              contract: true,
-              save: true
-            }
-          })
-        }
-        teacherState.active = 2
-      } catch {}
-    },
-    next2() {
-      if (!teacherState.teacherCert.subjectId) {
-        Toast('请选择教授科目')
-        return
-      }
-      if (!teacherState.teacherCert.styleVideo[0].videoUrl) {
-        Toast('请上传个人风采视频')
-        return
-      }
-      if (!teacherState.teacherCert.styleVideo[0].cover) {
-        Toast('请上传个人风采封面')
-        return
-      }
-      teacherState.active = 3
-    },
-    async onSubmit() {
-      try {
-        const graduateSchool = teacherState.teacherCert.graduateSchool
-        if (!graduateSchool) {
-          Toast('请输入您的毕业院校')
-          return
-        }
-        const subject = teacherState.teacherCert.subject
-        if (!subject) {
-          Toast('请输入您的专业')
-          return
-        }
-        const teacherCert = teacherState.teacherCert
-        teacherCert.styleVideoJson = JSON.stringify(teacherCert.styleVideo)
-        await request.post('/api-teacher/teacherAuthEntryRecord/doApply', {
-          data: teacherState.teacherCert
-        })
-        Toast('提交成功')
-        teacherState.active = 4
-      } catch {
-        //
-      }
-    },
-    prev() {
-      teacherState.active = teacherState.active - 1
-    }
-  },
-  render() {
-    return (
-      <div class={styles['teacher-cert']}>
-        {!teacherState.authStatus ? (
-          <CertInfo />
-        ) : (
-          <div>
-            {teacherState.active != 4 ? (
-              <Steps style={{ marginBottom: '12px' }} />
-            ) : null}
-            {teacherState.active === 1 ? (
-              <>
-                <CertOne />
-                <div class={'btnGroup'}>
-                  <ColProtocol
-                    v-model={this.agreeStatus}
-                    prototcolType="REGISTER"
-                    style={{ paddingLeft: 0, paddingRight: 0 }}
-                  />
-                  <Button
-                    block
-                    round
-                    onClick={this.next}
-                    type="primary"
-                    text="下一步"
-                  />
-                </div>
-              </>
-            ) : null}
-            {teacherState.active === 2 ? (
-              <>
-                <CertTwo />
-                <div class={['btnGroup', 'btnMore']}>
-                  <Button block round type="primary" plain onClick={this.prev}>
-                    上一步
-                  </Button>
-                  <Button
-                    block
-                    round
-                    onClick={this.next2}
-                    type="primary"
-                    text="下一步"
-                  />
-                </div>
-              </>
-            ) : null}
-            {teacherState.active === 3 ? (
-              <>
-                <CertThree />
-                {/* <Sticky position="bottom" offsetBottom={0}> */}
-                <div class={['btnGroup', 'btnMore']}>
-                  <Button block round type="primary" plain onClick={this.prev}>
-                    上一步
-                  </Button>
-                  <Button
-                    block
-                    round
-                    onClick={this.onSubmit}
-                    type="primary"
-                    text="提交审核"
-                  />
-                </div>
-                {/* </Sticky> */}
-              </>
-            ) : null}
-
-            {/* 提交完数据之后显示状态页 */}
-            {teacherState.active === 4 ? (
-              <ColResult
-                type="teacherCert"
-                style={{ paddingTop: '60px' }}
-                classImgSize="CERT"
-                tips="感谢您的申请,小酷将在24小时内完成审核,请留意APP消息及短信获取审核结果。"
-              />
-            ) : null}
-          </div>
-        )}
-      </div>
-    )
-  }
-})
+import { Button, Sticky, Toast } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import CertInfo from './cert-info'
+import ColProtocol from '@/components/col-protocol'
+import { teacherState } from './teacherState'
+import Steps from './steps'
+import CertOne from './cert-one'
+import CertTwo from './cert-two'
+import CertThree from './cert-three'
+import { checkIDCard } from '@/helpers/validate'
+import request from '@/helpers/request'
+import ColResult from '@/components/col-result'
+import { state } from '@/state'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'teacherCert',
+  data() {
+    const query = this.$route.query
+    return {
+      agreeStatus: false
+    }
+  },
+  async mounted() {
+    try {
+      // 老师入驻状态 0、未申请 UNPAALY、未申请 DOING、审核中 PASS、通过 UNPASS、不通过
+      const entryStatus = state.user.data?.entryStatus || 0
+      teacherState.authStatus =
+        entryStatus === 'DOING' || entryStatus === 'PASS' ? true : false
+      // 如果已经认证,则不用获取声部信息
+      if (teacherState.authStatus) {
+        teacherState.active = teacherState.authStatus ? 4 : 1
+        return
+      }
+
+      if (teacherState.subjectList.length <= 0) {
+        const res = await request.get('/api-teacher/subject/subjectSelect')
+        teacherState.subjectList = res.data || []
+      }
+
+      const teacherInfo = await request.get(
+        '/api-teacher/teacherAuthEntryRecord/getLastRecordByUserId'
+      )
+      teacherState.teacherInfo = teacherInfo.data || {}
+      const info = teacherState.teacherInfo
+      teacherState.teacherCert.introduction = info.introduction
+      teacherState.teacherCert.subjectId = info.subjectId
+      teacherState.teacherCert.graduateSchool = info.graduateSchool
+      teacherState.teacherCert.subject = info.subject
+      teacherState.teacherCert.styleVideo = info.styleVideoJson
+        ? JSON.parse(info.styleVideoJson)
+        : [{ videoUrl: '', cover: '' }]
+      teacherState.teacherCert.gradCertificate = info.gradCertificate
+      teacherState.teacherCert.degreeCertificate = info.degreeCertificate
+      teacherState.teacherCert.teacherCertificate = info.teacherCertificate
+    } catch {
+      //
+    }
+  },
+  computed: {
+    authStatus(): boolean {
+      return !!teacherState.authStatus
+    },
+    userAuth() {
+      // 判断是否实名过
+      const users = state.user.data || {}
+      return !!(users.idCardNo && users.realName)
+    }
+  },
+  methods: {
+    async next() {
+      const realName = teacherState.teacherCert.realName
+      if (!realName) {
+        Toast('请填写真实姓名')
+        return
+      }
+      const idCardNo = teacherState.teacherCert.idCardNo
+      if (!this.userAuth) {
+        if (!checkIDCard(idCardNo || '')) {
+          Toast('请填写正确的身份证号码')
+          return false
+        }
+      }
+
+      if (!this.agreeStatus) {
+        Toast('请阅读并同意协议')
+        return
+      }
+
+      if (!teacherState.teacherCert.birthdate) {
+        Toast('请选择出生日期')
+        return
+      }
+
+      try {
+        if (!this.userAuth) {
+          await request.post('/api-teacher/teacher/realNameAuth', {
+            data: {
+              realName,
+              idCardNo,
+              contract: true,
+              save: true
+            }
+          })
+        }
+        teacherState.active = 2
+      } catch {}
+    },
+    next2() {
+      if (!teacherState.teacherCert.subjectId) {
+        Toast('请选择教授科目')
+        return
+      }
+      if (!teacherState.teacherCert.styleVideo[0].videoUrl) {
+        Toast('请上传个人风采视频')
+        return
+      }
+      if (!teacherState.teacherCert.styleVideo[0].cover) {
+        Toast('请上传个人风采封面')
+        return
+      }
+      teacherState.active = 3
+    },
+    async onSubmit() {
+      try {
+        const graduateSchool = teacherState.teacherCert.graduateSchool
+        if (!graduateSchool) {
+          Toast('请输入您的毕业院校')
+          return
+        }
+        const subject = teacherState.teacherCert.subject
+        if (!subject) {
+          Toast('请输入您的专业')
+          return
+        }
+        const teacherCert = teacherState.teacherCert
+        teacherCert.styleVideoJson = JSON.stringify(teacherCert.styleVideo)
+        await request.post('/api-teacher/teacherAuthEntryRecord/doApply', {
+          data: teacherState.teacherCert
+        })
+        Toast('提交成功')
+        teacherState.active = 4
+      } catch {
+        //
+      }
+    },
+    prev() {
+      teacherState.active = teacherState.active - 1
+    }
+  },
+  render() {
+    return (
+      <div class={styles['teacher-cert']}>
+        {!teacherState.authStatus ? (
+          <CertInfo />
+        ) : (
+          <div>
+            {teacherState.active != 4 ? (
+              <>
+                <ColHeader />
+                <Steps style={{ marginBottom: '12px' }} />
+              </>
+            ) : null}
+            {teacherState.active === 1 ? (
+              <>
+                <CertOne />
+                <div class={'btnGroup'}>
+                  <ColProtocol
+                    v-model={this.agreeStatus}
+                    prototcolType="REGISTER"
+                    style={{ paddingLeft: 0, paddingRight: 0 }}
+                  />
+                  <Button
+                    block
+                    round
+                    onClick={this.next}
+                    type="primary"
+                    text="下一步"
+                  />
+                </div>
+              </>
+            ) : null}
+            {teacherState.active === 2 ? (
+              <>
+                <CertTwo />
+                <div class={['btnGroup', 'btnMore']}>
+                  <Button block round type="primary" plain onClick={this.prev}>
+                    上一步
+                  </Button>
+                  <Button
+                    block
+                    round
+                    onClick={this.next2}
+                    type="primary"
+                    text="下一步"
+                  />
+                </div>
+              </>
+            ) : null}
+            {teacherState.active === 3 ? (
+              <>
+                <CertThree />
+
+                <div class={['btnGroup', 'btnMore']}>
+                  <Button block round type="primary" plain onClick={this.prev}>
+                    上一步
+                  </Button>
+                  <Button
+                    block
+                    round
+                    onClick={this.onSubmit}
+                    type="primary"
+                    text="提交审核"
+                  />
+                </div>
+              </>
+            ) : null}
+
+            {teacherState.active === 4 ? (
+              <>
+                <ColHeader />
+                <ColResult
+                  type="teacherCert"
+                  style={{ paddingTop: '60px' }}
+                  classImgSize="CERT"
+                  tips="感谢您的申请,小酷将在24小时内完成审核,请留意APP消息及短信获取审核结果。"
+                />
+              </>
+            ) : null}
+          </div>
+        )}
+      </div>
+    )
+  }
+})

+ 31 - 24
src/teacher/teacher-cert/teacher-cert-update/index.tsx

@@ -7,6 +7,7 @@ import { defineComponent } from 'vue'
 import teacherCert from '..'
 import CertThree from '../cert-three'
 import { teacherState } from '../teacherState'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'teacher-cert-update',
@@ -78,31 +79,37 @@ export default defineComponent({
     return (
       <>
         {this.authStatus ? (
-          <ColResult
-            type="teacherCert"
-            style={{ paddingTop: '60px' }}
-            classImgSize="CERT"
-            tips="感谢您的申请,小酷将在24小时内完成审核,请留意APP消息及短信获取审核结果。"
-          />
+          <>
+            <ColHeader />
+            <ColResult
+              type="teacherCert"
+              style={{ paddingTop: '60px' }}
+              classImgSize="CERT"
+              tips="感谢您的申请,小酷将在24小时内完成审核,请留意APP消息及短信获取审核结果。"
+            />
+          </>
         ) : (
-          <div
-            style={{
-              paddingTop: '12px'
-            }}
-          >
-            <CertThree />
-            <ColSticky position="bottom">
-              <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
-                <Button
-                  block
-                  round
-                  onClick={this.onSubmit}
-                  type="primary"
-                  text="提交审核"
-                />
-              </div>
-            </ColSticky>
-          </div>
+          <>
+            <ColHeader />
+            <div
+              style={{
+                paddingTop: '12px'
+              }}
+            >
+              <CertThree />
+              <ColSticky position="bottom">
+                <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
+                  <Button
+                    block
+                    round
+                    onClick={this.onSubmit}
+                    type="primary"
+                    text="提交审核"
+                  />
+                </div>
+              </ColSticky>
+            </div>
+          </>
         )}
       </>
     )

+ 269 - 257
src/teacher/video-class/class-content.tsx

@@ -1,257 +1,269 @@
-import ColField from '@/components/col-field'
-import ColFieldGroup from '@/components/col-field-group'
-import ColPopup from '@/components/col-popup'
-import ColUpload from '@/components/col-upload'
-import ColUploadVideo from '@/components/col-upload-video'
-import {
-  Button,
-  Col,
-  Dialog,
-  Field,
-  Form,
-  Icon,
-  Row,
-  Sticky,
-  Switch
-} from 'vant'
-import { defineComponent } from 'vue'
-import styles from './class-content.module.less'
-import { createState } from './createState'
-import MusicAlbum from './model/music-album'
-
-export default defineComponent({
-  name: 'ClassContent',
-  data() {
-    return {
-      url: '',
-      checked: null,
-      musicStatus: false,
-      selectItem: {} as any // 选中的课程
-    }
-  },
-  methods: {
-    onSubmit(values: any) {
-      createState.active = 3
-    },
-    addItem() {
-      createState.lessonList.push({
-        videoTitle: '',
-        videoContent: '',
-        videoUrl: '',
-        coverUrl: '',
-        relationList: [],
-        posterUrl: '' // 视频封面图
-      })
-    },
-    removeItem(index: number) {
-      // 最少一节课
-      if (createState.lessonList.length <= 1) return
-      Dialog.confirm({
-        title: '操作',
-        message: `确定删除该条数据吗?`,
-        confirmButtonColor: '#2DC7AA'
-      }).then(() => {
-        createState.lessonList.splice(index, 1)
-      })
-    },
-    getName(item: any) {
-      const relation =
-        item.relationList.length > 0 ? item.relationList[0] : null
-      return relation
-        ? (relation.relationMusicAlbum === 'ALBUM' ? '专辑:' : '曲目:') +
-            relation.musicAlbumName
-        : ''
-    }
-  },
-  render() {
-    return (
-      <Form
-        class={styles['class-content']}
-        onSubmit={this.onSubmit}
-        scrollToError
-      >
-        <div class={styles.createVideoTips}>
-          您可为每个视频关联曲目或专辑作为本课程教学内容推荐
-        </div>
-        {createState.lessonList.map((item: any, index: number) => (
-          <>
-            <div class={styles.titleSection}>
-              <span class={styles.title}>第{index + 1}课</span>
-              <Icon
-                name="delete-o"
-                style={{ fontWeight: 600 }}
-                class={
-                  createState.lessonList.length <= 1 ? styles.disabled : null
-                }
-                onClick={() => this.removeItem(index)}
-                size={20}
-              />
-            </div>
-            <ColFieldGroup>
-              <ColField title="课程标题" required>
-                <Field
-                  v-model={item.videoTitle}
-                  maxlength={50}
-                  rules={[{ required: true, message: '请输入课程标题' }]}
-                  name="videoTitle"
-                  placeholder="请输入您的课程标题"
-                />
-              </ColField>
-              <ColField title="课程内容" required>
-                <Field
-                  v-model={item.videoContent}
-                  type="textarea"
-                  rows="2"
-                  autosize
-                  showWordLimit
-                  maxlength={200}
-                  rules={[{ required: true, message: '请输入课程内容' }]}
-                  name="videoContent"
-                  placeholder="请输入您的课程内容"
-                />
-              </ColField>
-              <ColField title="课程视频及视频封面" required border={false}>
-                <Row
-                  justify="space-between"
-                  style={{ width: '100%', paddingTop: '12px' }}
-                >
-                  <Col span={12}>
-                    <Field
-                      style={{ padding: 0 }}
-                      name="videoUrl"
-                      rules={[{ required: true, message: '请上传课程视频' }]}
-                      v-slots={{
-                        input: () => (
-                          <ColUploadVideo
-                            bucket="video-course"
-                            v-model={item.videoUrl}
-                            v-model:posterUrl={item.posterUrl}
-                            class={styles.upload}
-                            tips="点击上传视频"
-                          />
-                        )
-                      }}
-                    />
-                  </Col>
-                  <Col span={12}>
-                    <Field
-                      style={{ padding: 0 }}
-                      name="coverUrl"
-                      rules={[{ required: true, message: '请上传课程封面' }]}
-                      error
-                      v-slots={{
-                        input: () => (
-                          <ColUpload
-                            class={styles.upload}
-                            cropper
-                            bucket="video-course"
-                            options={{
-                              fixedNumber: [1.77, 1],
-                              autoCropWidth: 750,
-                              autoCropHeight: 424
-                            }}
-                            v-model={item.coverUrl}
-                            tips="点击上传视频封面"
-                          />
-                        )
-                      }}
-                    />
-                  </Col>
-                </Row>
-              </ColField>
-              {/* <van-icon name="clear" /> */}
-              <ColField title="关联曲目或专辑">
-                <Field
-                  modelValue={
-                    item.relationList.length > 0 &&
-                    item.relationList[0].musicAlbumId
-                      ? this.getName(item)
-                      : ''
-                  }
-                  readonly
-                  isLink
-                  clickable={false}
-                  clearable
-                  rightIcon={
-                    item.relationList.length > 0 &&
-                    item.relationList[0].musicAlbumId
-                      ? 'clear'
-                      : ''
-                  }
-                  onClick-right-icon={(e: MouseEvent) => {
-                    e.stopPropagation()
-                    item.relationList[0].musicAlbumId = 0
-                    item.relationList[0].musicAlbumName = ''
-                  }}
-                  onClick={() => {
-                    this.selectItem = item
-                    this.musicStatus = true
-                  }}
-                  placeholder="请选择关联曲目或专辑"
-                />
-              </ColField>
-            </ColFieldGroup>
-          </>
-        ))}
-
-        <Button
-          class={styles['add-item']}
-          block
-          icon="add-o"
-          onClick={this.addItem}
-        >
-          添加课程
-        </Button>
-
-        <Sticky offsetBottom={0} position="bottom" zIndex={999999}>
-          <div class={['btnGroup', 'btnMore']}>
-            <Button
-              block
-              round
-              type="primary"
-              plain
-              onClick={() => {
-                createState.active = 1
-              }}
-            >
-              上一步
-            </Button>
-            <Button block round type="primary" native-type="submit">
-              提交
-            </Button>
-          </div>
-        </Sticky>
-
-        <ColPopup v-model={this.musicStatus} zIndex={999999}>
-          <MusicAlbum
-            subjectId={createState.lessonGroup.lessonSubject}
-            onSelect={(item: any) => {
-              this.musicStatus = false
-              if (this.selectItem.relationList.length > 0) {
-                this.selectItem.relationList[0].musicAlbumId = item.id
-                this.selectItem.relationList[0].musicAlbumName =
-                  item.selectType === 'ALBUM'
-                    ? item.albumName
-                    : item.musicSheetName
-                this.selectItem.relationList[0].relationMusicAlbum =
-                  item.selectType
-              } else {
-                this.selectItem.relationList = [
-                  {
-                    musicAlbumId: item.id,
-                    musicAlbumName:
-                      item.selectType === 'ALBUM'
-                        ? item.albumName
-                        : item.musicSheetName,
-                    relationMusicAlbum: item.selectType,
-                    useRelationType: 'RECOMMEND'
-                  }
-                ]
-              }
-            }}
-          />
-        </ColPopup>
-      </Form>
-    )
-  }
-})
+import ColField from '@/components/col-field'
+import ColFieldGroup from '@/components/col-field-group'
+import ColPopup from '@/components/col-popup'
+import ColUpload from '@/components/col-upload'
+import ColUploadVideo from '@/components/col-upload-video'
+import {
+  Button,
+  Col,
+  Dialog,
+  Field,
+  Form,
+  Icon,
+  Row,
+  Sticky,
+  Switch
+} from 'vant'
+import { defineComponent } from 'vue'
+import styles from './class-content.module.less'
+import { createState } from './createState'
+import MusicAlbum from './model/music-album'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
+
+export default defineComponent({
+  name: 'ClassContent',
+  data() {
+    return {
+      url: '',
+      checked: null,
+      musicStatus: false,
+      selectItem: {} as any, // 选中的课程
+      heightV: 0
+    }
+  },
+  methods: {
+    onSubmit(values: any) {
+      createState.active = 3
+    },
+    addItem() {
+      createState.lessonList.push({
+        videoTitle: '',
+        videoContent: '',
+        videoUrl: '',
+        coverUrl: '',
+        relationList: [],
+        posterUrl: '' // 视频封面图
+      })
+    },
+    removeItem(index: number) {
+      // 最少一节课
+      if (createState.lessonList.length <= 1) return
+      Dialog.confirm({
+        title: '操作',
+        message: `确定删除该条数据吗?`,
+        confirmButtonColor: '#2DC7AA'
+      }).then(() => {
+        createState.lessonList.splice(index, 1)
+      })
+    },
+    getName(item: any) {
+      const relation =
+        item.relationList.length > 0 ? item.relationList[0] : null
+      return relation
+        ? (relation.relationMusicAlbum === 'ALBUM' ? '专辑:' : '曲目:') +
+            relation.musicAlbumName
+        : ''
+    }
+  },
+  render() {
+    return (
+      <Form
+        class={styles['class-content']}
+        onSubmit={this.onSubmit}
+        scrollToError
+      >
+        <div class={styles.createVideoTips}>
+          您可为每个视频关联曲目或专辑作为本课程教学内容推荐
+        </div>
+        {createState.lessonList.map((item: any, index: number) => (
+          <>
+            <div class={styles.titleSection}>
+              <span class={styles.title}>第{index + 1}课</span>
+              <Icon
+                name="delete-o"
+                style={{ fontWeight: 600 }}
+                class={
+                  createState.lessonList.length <= 1 ? styles.disabled : null
+                }
+                onClick={() => this.removeItem(index)}
+                size={20}
+              />
+            </div>
+            <ColFieldGroup>
+              <ColField title="课程标题" required>
+                <Field
+                  v-model={item.videoTitle}
+                  maxlength={50}
+                  rules={[{ required: true, message: '请输入课程标题' }]}
+                  name="videoTitle"
+                  placeholder="请输入您的课程标题"
+                />
+              </ColField>
+              <ColField title="课程内容" required>
+                <Field
+                  v-model={item.videoContent}
+                  type="textarea"
+                  rows="2"
+                  autosize
+                  showWordLimit
+                  maxlength={200}
+                  rules={[{ required: true, message: '请输入课程内容' }]}
+                  name="videoContent"
+                  placeholder="请输入您的课程内容"
+                />
+              </ColField>
+              <ColField title="课程视频及视频封面" required border={false}>
+                <Row
+                  justify="space-between"
+                  style={{ width: '100%', paddingTop: '12px' }}
+                >
+                  <Col span={12}>
+                    <Field
+                      style={{ padding: 0 }}
+                      name="videoUrl"
+                      rules={[{ required: true, message: '请上传课程视频' }]}
+                      v-slots={{
+                        input: () => (
+                          <ColUploadVideo
+                            bucket="video-course"
+                            v-model={item.videoUrl}
+                            v-model:posterUrl={item.posterUrl}
+                            class={styles.upload}
+                            tips="点击上传视频"
+                          />
+                        )
+                      }}
+                    />
+                  </Col>
+                  <Col span={12}>
+                    <Field
+                      style={{ padding: 0 }}
+                      name="coverUrl"
+                      rules={[{ required: true, message: '请上传课程封面' }]}
+                      error
+                      v-slots={{
+                        input: () => (
+                          <ColUpload
+                            class={styles.upload}
+                            cropper
+                            bucket="video-course"
+                            options={{
+                              fixedNumber: [1.77, 1],
+                              autoCropWidth: 750,
+                              autoCropHeight: 424
+                            }}
+                            v-model={item.coverUrl}
+                            tips="点击上传视频封面"
+                          />
+                        )
+                      }}
+                    />
+                  </Col>
+                </Row>
+              </ColField>
+              {/* <van-icon name="clear" /> */}
+              <ColField title="关联曲目或专辑">
+                <Field
+                  modelValue={
+                    item.relationList.length > 0 &&
+                    item.relationList[0].musicAlbumId
+                      ? this.getName(item)
+                      : ''
+                  }
+                  readonly
+                  isLink
+                  clickable={false}
+                  clearable
+                  rightIcon={
+                    item.relationList.length > 0 &&
+                    item.relationList[0].musicAlbumId
+                      ? 'clear'
+                      : ''
+                  }
+                  onClick-right-icon={(e: MouseEvent) => {
+                    e.stopPropagation()
+                    item.relationList[0].musicAlbumId = 0
+                    item.relationList[0].musicAlbumName = ''
+                  }}
+                  onClick={() => {
+                    this.selectItem = item
+                    this.musicStatus = true
+                  }}
+                  placeholder="请选择关联曲目或专辑"
+                />
+              </ColField>
+            </ColFieldGroup>
+          </>
+        ))}
+
+        <Button
+          class={styles['add-item']}
+          block
+          icon="add-o"
+          onClick={this.addItem}
+        >
+          添加课程
+        </Button>
+
+        <Sticky offsetBottom={0} position="bottom" zIndex={999999}>
+          <div class={['btnGroup', 'btnMore']}>
+            <Button
+              block
+              round
+              type="primary"
+              plain
+              onClick={() => {
+                createState.active = 1
+              }}
+            >
+              上一步
+            </Button>
+            <Button block round type="primary" native-type="submit">
+              提交
+            </Button>
+          </div>
+        </Sticky>
+
+        <ColPopup v-model={this.musicStatus} zIndex={999999}>
+          <TheSticky
+            position="top"
+            onBarHeight={(h: any) => {
+              this.heightV = h
+            }}
+          >
+            <ColHeader />
+          </TheSticky>
+          <MusicAlbum
+            offsetTop={this.heightV}
+            subjectId={createState.lessonGroup.lessonSubject}
+            onSelect={(item: any) => {
+              this.musicStatus = false
+              if (this.selectItem.relationList.length > 0) {
+                this.selectItem.relationList[0].musicAlbumId = item.id
+                this.selectItem.relationList[0].musicAlbumName =
+                  item.selectType === 'ALBUM'
+                    ? item.albumName
+                    : item.musicSheetName
+                this.selectItem.relationList[0].relationMusicAlbum =
+                  item.selectType
+              } else {
+                this.selectItem.relationList = [
+                  {
+                    musicAlbumId: item.id,
+                    musicAlbumName:
+                      item.selectType === 'ALBUM'
+                        ? item.albumName
+                        : item.musicSheetName,
+                    relationMusicAlbum: item.selectType,
+                    useRelationType: 'RECOMMEND'
+                  }
+                ]
+              }
+            }}
+          />
+        </ColPopup>
+      </Form>
+    )
+  }
+})

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

@@ -26,6 +26,7 @@ import { createState } from './createState'
 import activeButtonIcon from '@common/images/icon_checkbox.png'
 import inactiveButtonIcon from '@common/images/icon_checkbox_default.png'
 import { state } from '@/state'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'ClassInfo',
@@ -60,8 +61,8 @@ export default defineComponent({
     },
     calcRatePrice() {
       // 计算手续费
-      let rate = createState.rate || 0
-      let price = createState.lessonGroup.lessonPrice || 0
+      const rate = createState.rate || 0
+      const price = createState.lessonGroup.lessonPrice || 0
       return (price - (rate / 100) * price).toFixed(2)
     }
   },
@@ -322,6 +323,7 @@ export default defineComponent({
         </Sticky>
 
         <ColPopup v-model={this.subjectStatus} destroy>
+          <ColHeader />
           <SubjectModel
             selectType="Radio"
             single

+ 2 - 0
src/teacher/video-class/create-submit.tsx

@@ -8,6 +8,7 @@ import { createState } from './createState'
 import { state } from '@/state'
 import request from '@/helpers/request'
 import { postMessage } from '@/helpers/native-message'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'CreateSubmit',
@@ -80,6 +81,7 @@ export default defineComponent({
   render() {
     return (
       <div class={[styles.createSubmit]}>
+        <ColHeader />
         <UserDetail userInfo={this.userInfo} />
         <SectionDetail>
           <p class={styles.introduction}>{this.userInfo.lessonDesc}</p>

+ 50 - 46
src/teacher/video-class/create.module.less

@@ -1,46 +1,50 @@
-.video-create {
-  .gridName {
-    font-size: 14px;
-    font-weight: 500;
-    color: #b4b4b4;
-    line-height: 20px;
-    padding-left: 8px;
-    &.active {
-      color: var(--van-primary);
-    }
-  }
-  :global {
-    // .van-grid {
-    // }
-
-    .van-sticky--fixed {
-      box-shadow: 10px 10px 10px var(--box-shadow-color);
-    }
-  }
-}
-
-.gridColumn {
-  padding-left: 14px;
-  padding-bottom: 12px;
-  background-color: #f6f8f9;
-
-  :global {
-    .van-grid-item {
-      padding-right: 14px;
-
-      &:first-child {
-        padding-right: 10px;
-      }
-    }
-    .van-grid-item__content {
-      padding-top: 7px;
-      padding-bottom: 7px;
-      border-radius: 10px;
-      overflow: hidden;
-    }
-    .van-badge__wrapper {
-      display: flex;
-      align-items: center;
-    }
-  }
-}
+.video-create {
+  .gridName {
+    font-size: 14px;
+    font-weight: 500;
+    color: #b4b4b4;
+    line-height: 20px;
+    padding-left: 8px;
+
+    &.active {
+      color: var(--van-primary);
+    }
+  }
+
+  :global {
+    // .van-grid {
+    // }
+
+    .van-sticky--fixed {
+      // box-shadow: 10px 10px 10px var(--box-shadow-color);
+    }
+  }
+}
+
+.gridColumn {
+  padding-left: 14px;
+  padding-bottom: 12px;
+  background-color: #f6f8f9;
+
+  :global {
+    .van-grid-item {
+      padding-right: 14px;
+
+      &:first-child {
+        padding-right: 10px;
+      }
+    }
+
+    .van-grid-item__content {
+      padding-top: 7px;
+      padding-bottom: 7px;
+      border-radius: 10px;
+      overflow: hidden;
+    }
+
+    .van-badge__wrapper {
+      display: flex;
+      align-items: center;
+    }
+  }
+}

+ 201 - 199
src/teacher/video-class/create.tsx

@@ -1,199 +1,201 @@
-import { Grid, GridItem, Icon, Sticky } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './create.module.less'
-import ClassInfo from './class-info'
-import ClassContent from './class-content'
-import { createState } from './createState'
-import request from '@/helpers/request'
-
-import nameActive from './images/icon_name_active.png'
-import education from './images/icon_education.png'
-import educationActive from './images/icon_education_active.png'
-import CreateSubmit from './create-submit'
-
-export default defineComponent({
-  name: 'Create',
-  async created() {
-    const query = this.$route.query
-    createState.groupId = Number(query.groupId) || 0
-    // 判断是否是编辑
-    if (!createState.groupId) {
-      return false
-    }
-    try {
-      document.title = '修改视频课'
-      createState.loadingStatus = true
-      const res = await request.get(
-        '/api-teacher/videoLessonGroup/selectVideoLesson',
-        {
-          params: {
-            groupId: createState.groupId
-          }
-        }
-      )
-      const result = res.data
-      const {
-        auditStatus,
-        lessonCoverUrl,
-        lessonPrice,
-        lessonDesc,
-        lessonSubject,
-        lessonName,
-        relationType,
-        id,
-        ...group
-      } = result.lessonGroup
-      // 判断模板图片是否在模板列表中,如果不在则是用户自己上传的图片
-      const statusUrl = createState.templateList.includes(lessonCoverUrl)
-        ? true
-        : false
-      createState.lessonGroup = {
-        id: id,
-        lessonName: lessonName,
-        lessonSubject: lessonSubject,
-        lessonDesc: lessonDesc,
-        lessonPrice: lessonPrice,
-        relationType: 'RECOMMEND',
-        lessonCoverTemplateUrl: statusUrl ? lessonCoverUrl : '',
-        lessonCoverUrl: statusUrl ? '' : lessonCoverUrl
-      }
-      createState.lessonList = []
-      result.detailList &&
-        result.detailList.forEach((item: any) => {
-          const tempInfo = item.musicAlbumInfos || []
-          const relationList = tempInfo.map((info: any) => {
-            return {
-              relationMusicAlbum: info.relationType,
-              musicAlbumName: info.name,
-              musicAlbumId: info.musicAlbumId,
-              relationId: info.id,
-              useRelationType: 'RECOMMEND'
-            }
-          })
-          createState.lessonList.push({
-            videoTitle: item.videoTitle,
-            videoContent: item.videoContent,
-            videoUrl: item.videoUrl,
-            coverUrl: item.coverUrl,
-            relationList,
-            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-teacher/sysConfig/queryByParamName',
-      //   {
-      //     params: {
-      //       paramName: 'video_lesson_service_fee'
-      //     }
-      //   }
-      // )
-      request
-        .get('/api-teacher/sysConfig/queryByParamNameList', {
-          params: {
-            paramNames: 'video_lesson_service_fee,video_account_period'
-          }
-        })
-        .then((res: any) => {
-          console.log(res, 'res')
-          const data = res.data || []
-          data.forEach((item: any) => {
-            if (item.paramName === 'video_lesson_service_fee') {
-              createState.rate = item.paramValue
-            } else if (item.paramName === 'video_account_period') {
-              createState.video_account_period = item.paramValue
-            }
-          })
-        })
-    } catch {}
-  },
-  render() {
-    return (
-      <div class={styles['video-create']}>
-        {createState.active <= 2 ? (
-          <Sticky position="top" offsetTop={0} zIndex={999999}>
-            <Grid
-              border={false}
-              style={{ paddingTop: '15px' }}
-              direction="horizontal"
-              columnNum="2"
-              class={styles.gridColumn}
-            >
-              <GridItem
-                v-slots={{
-                  default: () => (
-                    <>
-                      <Icon name={nameActive} size={38} />
-                      <span
-                        class={[
-                          styles.gridName,
-                          createState.active >= 1 ? styles.active : null
-                        ]}
-                      >
-                        课程信息
-                      </span>
-                    </>
-                  )
-                }}
-              />
-              <GridItem
-                v-slots={{
-                  default: () => (
-                    <>
-                      <Icon
-                        name={
-                          createState.active === 2 ? educationActive : education
-                        }
-                        size={38}
-                      />
-                      <span
-                        class={[
-                          styles.gridName,
-                          createState.active === 2 ? styles.active : null
-                        ]}
-                      >
-                        课程内容
-                      </span>
-                    </>
-                  )
-                }}
-              />
-            </Grid>
-          </Sticky>
-        ) : null}
-        {/* 课程信息 */}
-        {createState.active === 1 ? (
-          <>
-            <ClassInfo />
-          </>
-        ) : null}
-        {/* 课程内容 */}
-        {createState.active === 2 ? (
-          <>
-            <ClassContent />
-          </>
-        ) : null}
-        {/* 预览 */}
-        {createState.active === 3 ? (
-          <>
-            <CreateSubmit />
-          </>
-        ) : null}
-      </div>
-    )
-  }
-})
+import { Grid, GridItem, Icon, Sticky } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './create.module.less'
+import ClassInfo from './class-info'
+import ClassContent from './class-content'
+import { createState } from './createState'
+import request from '@/helpers/request'
+
+import nameActive from './images/icon_name_active.png'
+import education from './images/icon_education.png'
+import educationActive from './images/icon_education_active.png'
+import CreateSubmit from './create-submit'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'Create',
+  async created() {
+    const query = this.$route.query
+    createState.groupId = Number(query.groupId) || 0
+    // 判断是否是编辑
+    if (!createState.groupId) {
+      return false
+    }
+    try {
+      document.title = '修改视频课'
+      createState.loadingStatus = true
+      const res = await request.get(
+        '/api-teacher/videoLessonGroup/selectVideoLesson',
+        {
+          params: {
+            groupId: createState.groupId
+          }
+        }
+      )
+      const result = res.data
+      const {
+        auditStatus,
+        lessonCoverUrl,
+        lessonPrice,
+        lessonDesc,
+        lessonSubject,
+        lessonName,
+        relationType,
+        id,
+        ...group
+      } = result.lessonGroup
+      // 判断模板图片是否在模板列表中,如果不在则是用户自己上传的图片
+      const statusUrl = createState.templateList.includes(lessonCoverUrl)
+        ? true
+        : false
+      createState.lessonGroup = {
+        id: id,
+        lessonName: lessonName,
+        lessonSubject: lessonSubject,
+        lessonDesc: lessonDesc,
+        lessonPrice: lessonPrice,
+        relationType: 'RECOMMEND',
+        lessonCoverTemplateUrl: statusUrl ? lessonCoverUrl : '',
+        lessonCoverUrl: statusUrl ? '' : lessonCoverUrl
+      }
+      createState.lessonList = []
+      result.detailList &&
+        result.detailList.forEach((item: any) => {
+          const tempInfo = item.musicAlbumInfos || []
+          const relationList = tempInfo.map((info: any) => {
+            return {
+              relationMusicAlbum: info.relationType,
+              musicAlbumName: info.name,
+              musicAlbumId: info.musicAlbumId,
+              relationId: info.id,
+              useRelationType: 'RECOMMEND'
+            }
+          })
+          createState.lessonList.push({
+            videoTitle: item.videoTitle,
+            videoContent: item.videoContent,
+            videoUrl: item.videoUrl,
+            coverUrl: item.coverUrl,
+            relationList,
+            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-teacher/sysConfig/queryByParamName',
+      //   {
+      //     params: {
+      //       paramName: 'video_lesson_service_fee'
+      //     }
+      //   }
+      // )
+      request
+        .get('/api-teacher/sysConfig/queryByParamNameList', {
+          params: {
+            paramNames: 'video_lesson_service_fee,video_account_period'
+          }
+        })
+        .then((res: any) => {
+          console.log(res, 'res')
+          const data = res.data || []
+          data.forEach((item: any) => {
+            if (item.paramName === 'video_lesson_service_fee') {
+              createState.rate = item.paramValue
+            } else if (item.paramName === 'video_account_period') {
+              createState.video_account_period = item.paramValue
+            }
+          })
+        })
+    } catch {}
+  },
+  render() {
+    return (
+      <div class={styles['video-create']}>
+        {createState.active <= 2 ? (
+          <Sticky position="top" offsetTop={0} zIndex={999999}>
+            <ColHeader border={false} />
+            <Grid
+              border={false}
+              style={{ paddingTop: '15px' }}
+              direction="horizontal"
+              columnNum="2"
+              class={styles.gridColumn}
+            >
+              <GridItem
+                v-slots={{
+                  default: () => (
+                    <>
+                      <Icon name={nameActive} size={38} />
+                      <span
+                        class={[
+                          styles.gridName,
+                          createState.active >= 1 ? styles.active : null
+                        ]}
+                      >
+                        课程信息
+                      </span>
+                    </>
+                  )
+                }}
+              />
+              <GridItem
+                v-slots={{
+                  default: () => (
+                    <>
+                      <Icon
+                        name={
+                          createState.active === 2 ? educationActive : education
+                        }
+                        size={38}
+                      />
+                      <span
+                        class={[
+                          styles.gridName,
+                          createState.active === 2 ? styles.active : null
+                        ]}
+                      >
+                        课程内容
+                      </span>
+                    </>
+                  )
+                }}
+              />
+            </Grid>
+          </Sticky>
+        ) : null}
+        {/* 课程信息 */}
+        {createState.active === 1 ? (
+          <>
+            <ClassInfo />
+          </>
+        ) : null}
+        {/* 课程内容 */}
+        {createState.active === 2 ? (
+          <>
+            <ClassContent />
+          </>
+        ) : null}
+        {/* 预览 */}
+        {createState.active === 3 ? (
+          <>
+            <CreateSubmit />
+          </>
+        ) : null}
+      </div>
+    )
+  }
+})

+ 5 - 1
src/teacher/video-class/model/music-album/index.tsx

@@ -11,6 +11,10 @@ import styles from './index.module.less'
 export default defineComponent({
   name: 'music-album',
   props: {
+    offsetTop: {
+      type: Number,
+      default: 0
+    },
     subjectId: {
       type: Number,
       required: true
@@ -124,7 +128,7 @@ export default defineComponent({
   render() {
     return (
       <div class={styles.musicAlbum}>
-        <Sticky position="top" offsetTop={0}>
+        <Sticky position="top" offsetTop={this.offsetTop || 0}>
           <ColSearch
             onSearch={this.onSearch}
             placeholder="请输入曲目或专辑名称"

+ 5 - 0
src/tenant/main.ts

@@ -17,6 +17,11 @@ const app = createApp(App)
 
 // import Vconsole from 'vconsole'
 // const vconsole = new Vconsole()
+postMessage({ api: 'setBarStatus', content: { status: 0 } })
+postMessage({
+  api: 'backIconChange',
+  content: { backIconHide: true }
+})
 postMessage(
   {
     api: 'getVersion'

+ 2 - 0
src/views/404/index.tsx

@@ -6,12 +6,14 @@ import { Button, Image } from 'vant'
 import { postMessage } from '@/helpers/native-message'
 import { browser } from '@/helpers/utils'
 import { state } from '@/state'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'NotFound',
   render() {
     return (
       <div class={styles.f404}>
+        <ColHeader />
         <Image src={state.projectType === 'tenant' ? img404Tenant : img404} />
         <p>页面找不到了</p>
         <Button

+ 4 - 2
src/views/article-center/help-center-detail.tsx

@@ -37,6 +37,7 @@ export default defineComponent({
       }
 
       const documentTitle = catalogId == 2 ? '公告详情' : 'ㅤ' //'    ‌‍‎‏ '
+      this.documentTitle = documentTitle
       document.title = documentTitle
     } catch {}
     useEventTracking('帮助中心')
@@ -66,8 +67,7 @@ export default defineComponent({
   render() {
     return (
       <div class={styles['help-center-detail']}>
-        {this.catalogType === 'ANALYSIS' && (
-          // <ColHeader title={this.documentTitle} isBack />
+        {this.catalogType === 'ANALYSIS' ? (
           <NavBar
             title={this.documentTitle}
             left-arrow
@@ -76,6 +76,8 @@ export default defineComponent({
               this.$router.back()
             }}
           ></NavBar>
+        ) : (
+          <ColHeader title={' '} />
         )}
 
         <h2>{this.detail.title}</h2>

+ 3 - 0
src/views/article-center/help-center.tsx

@@ -5,6 +5,7 @@ import { defineComponent } from 'vue'
 import ColSearch from '@/components/col-search'
 import ColResult from '@/components/col-result'
 import { useEventTracking } from '@/helpers/hooks'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'help-center',
@@ -24,6 +25,7 @@ export default defineComponent({
       loading: false,
       finished: false,
       // 1:帮助中心,2:公告管理
+      pageTitle: title,
       params: {
         catalogIds: query.catalogType || 1,
         title: '',
@@ -94,6 +96,7 @@ export default defineComponent({
     return (
       <div>
         <Sticky offsetTop={0} position="top" class={'mb12'}>
+          <ColHeader isFixed={false} title={this.pageTitle} border={false} />
           <ColSearch
             type={state.projectType === 'tenant' ? 'tenant' : 'person'}
             onSearch={this.onSearch}

+ 72 - 70
src/views/article-center/special-detail.tsx

@@ -1,70 +1,72 @@
-import { useEventTracking } from '@/helpers/hooks'
-import request from '@/helpers/request'
-import dayjs from 'dayjs'
-import { ImagePreview } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './help-center-detail.module.less'
-
-export default defineComponent({
-  name: 'special-detail',
-  data() {
-    return {
-      detail: {
-        title: '',
-        createTime: '',
-        content: ''
-      }
-    }
-  },
-  async mounted() {
-    try {
-      const query = this.$route.query
-      const res = await request.get('/api-cms/news/query/' + query.id)
-      let { title, createTime, content } = res.data
-      this.detail = {
-        title,
-        createTime: dayjs(createTime).format('YYYY-MM-DD HH:mm:ss'),
-        content
-      }
-    } catch {}
-    useEventTracking('热门资讯')
-  },
-  methods: {
-    onShowImg(target: any) {
-      const { localName } = target.srcElement
-      if (localName !== 'img') {
-        return
-      }
-      let startPosition = 0
-      const domList = document.querySelectorAll('.msgWrap img')
-      let imgList = Array.from(domList).map((item: any, index: number) => {
-        if (target.srcElement == item) {
-          startPosition = index
-        }
-        return item.src
-      })
-
-      ImagePreview({
-        images: imgList,
-        startPosition: startPosition,
-        closeable: true
-      })
-    }
-  },
-  render() {
-    return (
-      <div class={styles['help-center-detail']}>
-        <h2>{this.detail.title}</h2>
-        <div class={styles.titleInfo}>
-          <p>酷乐秀</p>
-          <p>{this.detail.createTime}</p>
-        </div>
-        <div
-          class={[styles.imgContent, 'msgWrap']}
-          // onClick={this.onShowImg}
-          v-html={this.detail.content}
-        ></div>
-      </div>
-    )
-  }
-})
+import { useEventTracking } from '@/helpers/hooks'
+import request from '@/helpers/request'
+import dayjs from 'dayjs'
+import { ImagePreview } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './help-center-detail.module.less'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'special-detail',
+  data() {
+    return {
+      detail: {
+        title: '',
+        createTime: '',
+        content: ''
+      }
+    }
+  },
+  async mounted() {
+    try {
+      const query = this.$route.query
+      const res = await request.get('/api-cms/news/query/' + query.id)
+      const { title, createTime, content } = res.data
+      this.detail = {
+        title,
+        createTime: dayjs(createTime).format('YYYY-MM-DD HH:mm:ss'),
+        content
+      }
+    } catch {}
+    useEventTracking('热门资讯')
+  },
+  methods: {
+    onShowImg(target: any) {
+      const { localName } = target.srcElement
+      if (localName !== 'img') {
+        return
+      }
+      let startPosition = 0
+      const domList = document.querySelectorAll('.msgWrap img')
+      const imgList = Array.from(domList).map((item: any, index: number) => {
+        if (target.srcElement == item) {
+          startPosition = index
+        }
+        return item.src
+      })
+
+      ImagePreview({
+        images: imgList,
+        startPosition: startPosition,
+        closeable: true
+      })
+    }
+  },
+  render() {
+    return (
+      <div class={styles['help-center-detail']}>
+        <ColHeader />
+        <h2>{this.detail.title}</h2>
+        <div class={styles.titleInfo}>
+          <p>酷乐秀</p>
+          <p>{this.detail.createTime}</p>
+        </div>
+        <div
+          class={[styles.imgContent, 'msgWrap']}
+          // onClick={this.onShowImg}
+          v-html={this.detail.content}
+        ></div>
+      </div>
+    )
+  }
+})

+ 128 - 126
src/views/article-center/special.tsx

@@ -1,126 +1,128 @@
-import { state } from '@/state'
-import request from '@/helpers/request'
-import { Cell, List, Sticky, Image, CellGroup } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './special.module.less'
-import ColSearch from '@/components/col-search'
-import ColResult from '@/components/col-result'
-import { useEventTracking } from '@/helpers/hooks'
-
-export default defineComponent({
-  name: 'special',
-  data() {
-    const query = this.$route.query
-    return {
-      list: [],
-      dataShow: true, // 判断是否有数据
-      loading: false,
-      finished: false,
-      // 1热门资讯,2开屏广告,3闪页管理,4轮播图管理 5按钮管理 6 乐理章节
-      params: {
-        search: '',
-        type: query.type || 1,
-        platformType: state.platformType,
-        page: 1,
-        rows: 20
-      }
-    }
-  },
-  async mounted() {
-    useEventTracking('热门资讯')
-  },
-  methods: {
-    async getList() {
-      try {
-        let params = this.params
-        const res = await request.post('/api-cms/news/page', {
-          data: {
-            ...params
-          }
-        })
-        this.loading = false
-        const result = res.data || {}
-        // 处理重复请求数据
-        if (this.list.length > 0 && result.pageNo === 1) {
-          return
-        }
-        this.list = this.list.concat(result.rows || [])
-        this.finished = result.pageNo >= result.totalPage
-        this.params.page = result.pageNo + 1
-        this.dataShow = this.list.length > 0
-      } catch {
-        this.dataShow = false
-        this.finished = true
-      }
-    },
-    onSearch(val: string) {
-      this.params.search = val
-      this.params.page = 1
-      this.list = []
-      this.dataShow = true // 判断是否有数据
-      this.loading = false
-      this.finished = false
-      this.getList()
-    },
-    onDetail(item: any) {
-      if (item.linkUrl) {
-        window.location.href = item.linkUrl
-      } else {
-        this.$router.push({
-          path: 'specialDetail',
-          query: {
-            id: item.id
-          }
-        })
-      }
-    }
-  },
-  render() {
-    return (
-      <div class={styles['special']}>
-        <Sticky offsetTop={0} position="top" class={'mb12'}>
-          <ColSearch onSearch={this.onSearch} />
-        </Sticky>
-        {this.dataShow ? (
-          <List
-            class={styles.videoList}
-            v-model:loading={this.loading}
-            finished={this.finished}
-            finishedText="没有更多了"
-            onLoad={this.getList}
-          >
-            {this.list.map((item: any) => (
-              <CellGroup
-                inset
-                class={'mb12'}
-                onClick={() => {
-                  this.onDetail(item)
-                }}
-              >
-                <Cell
-                  title={item.title}
-                  titleClass={['van-ellipsis', styles.title]}
-                  v-slots={{
-                    label: () => (
-                      <div class={styles.label}>
-                        <div class={styles.time}>{item.createTime}</div>
-
-                        <Image
-                          class={styles.imgCover}
-                          src={item.coverImage}
-                          fit="cover"
-                        />
-                      </div>
-                    )
-                  }}
-                />
-              </CellGroup>
-            ))}
-          </List>
-        ) : (
-          <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无内容" />
-        )}
-      </div>
-    )
-  }
-})
+import { state } from '@/state'
+import request from '@/helpers/request'
+import { Cell, List, Sticky, Image, CellGroup } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './special.module.less'
+import ColSearch from '@/components/col-search'
+import ColResult from '@/components/col-result'
+import { useEventTracking } from '@/helpers/hooks'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'special',
+  data() {
+    const query = this.$route.query
+    return {
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      // 1热门资讯,2开屏广告,3闪页管理,4轮播图管理 5按钮管理 6 乐理章节
+      params: {
+        search: '',
+        type: query.type || 1,
+        platformType: state.platformType,
+        page: 1,
+        rows: 20
+      }
+    }
+  },
+  async mounted() {
+    useEventTracking('热门资讯')
+  },
+  methods: {
+    async getList() {
+      try {
+        const params = this.params
+        const res = await request.post('/api-cms/news/page', {
+          data: {
+            ...params
+          }
+        })
+        this.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) {
+          return
+        }
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+    },
+    onSearch(val: string) {
+      this.params.search = val
+      this.params.page = 1
+      this.list = []
+      this.dataShow = true // 判断是否有数据
+      this.loading = false
+      this.finished = false
+      this.getList()
+    },
+    onDetail(item: any) {
+      if (item.linkUrl) {
+        window.location.href = item.linkUrl
+      } else {
+        this.$router.push({
+          path: 'specialDetail',
+          query: {
+            id: item.id
+          }
+        })
+      }
+    }
+  },
+  render() {
+    return (
+      <div class={styles['special']}>
+        <Sticky offsetTop={0} position="top" class={'mb12'}>
+          <ColHeader border={false} />
+          <ColSearch onSearch={this.onSearch} />
+        </Sticky>
+        {this.dataShow ? (
+          <List
+            class={styles.videoList}
+            v-model:loading={this.loading}
+            finished={this.finished}
+            finishedText="没有更多了"
+            onLoad={this.getList}
+          >
+            {this.list.map((item: any) => (
+              <CellGroup
+                inset
+                class={'mb12'}
+                onClick={() => {
+                  this.onDetail(item)
+                }}
+              >
+                <Cell
+                  title={item.title}
+                  titleClass={['van-ellipsis', styles.title]}
+                  v-slots={{
+                    label: () => (
+                      <div class={styles.label}>
+                        <div class={styles.time}>{item.createTime}</div>
+
+                        <Image
+                          class={styles.imgCover}
+                          src={item.coverImage}
+                          fit="cover"
+                        />
+                      </div>
+                    )
+                  }}
+                />
+              </CellGroup>
+            ))}
+          </List>
+        ) : (
+          <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无内容" />
+        )}
+      </div>
+    )
+  }
+})

+ 70 - 68
src/views/article-center/theory-detail.tsx

@@ -1,68 +1,70 @@
-import request from '@/helpers/request'
-import dayjs from 'dayjs'
-import { ImagePreview } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './help-center-detail.module.less'
-
-export default defineComponent({
-  name: 'theory-detail',
-  data() {
-    return {
-      detail: {
-        title: '',
-        createTime: '',
-        content: ''
-      }
-    }
-  },
-  async mounted() {
-    try {
-      const query = this.$route.query
-      const res = await request.get('/api-cms/news/query/' + query.id)
-      let { title, createTime, content } = res.data
-      this.detail = {
-        title,
-        createTime: dayjs(createTime).format('YYYY-MM-DD HH:mm:ss'),
-        content
-      }
-    } catch {}
-  },
-  methods: {
-    onShowImg(target: any) {
-      const { localName } = target.srcElement
-      if (localName !== 'img') {
-        return
-      }
-      let startPosition = 0
-      const domList = document.querySelectorAll('.msgWrap img')
-      let imgList = Array.from(domList).map((item: any, index: number) => {
-        if (target.srcElement == item) {
-          startPosition = index
-        }
-        return item.src
-      })
-
-      ImagePreview({
-        images: imgList,
-        startPosition: startPosition,
-        closeable: true
-      })
-    }
-  },
-  render() {
-    return (
-      <div class={styles['help-center-detail']}>
-        <h2>{this.detail.title}</h2>
-        <div class={styles.titleInfo}>
-          <p>酷乐秀</p>
-          <p>{this.detail.createTime}</p>
-        </div>
-        <div
-          class={[styles.imgContent, 'msgWrap']}
-          // onClick={this.onShowImg}
-          v-html={this.detail.content}
-        ></div>
-      </div>
-    )
-  }
-})
+import request from '@/helpers/request'
+import dayjs from 'dayjs'
+import { ImagePreview } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './help-center-detail.module.less'
+import ColHeader from '@/components/col-header'
+
+export default defineComponent({
+  name: 'theory-detail',
+  data() {
+    return {
+      detail: {
+        title: '',
+        createTime: '',
+        content: ''
+      }
+    }
+  },
+  async mounted() {
+    try {
+      const query = this.$route.query
+      const res = await request.get('/api-cms/news/query/' + query.id)
+      const { title, createTime, content } = res.data
+      this.detail = {
+        title,
+        createTime: dayjs(createTime).format('YYYY-MM-DD HH:mm:ss'),
+        content
+      }
+    } catch {}
+  },
+  methods: {
+    onShowImg(target: any) {
+      const { localName } = target.srcElement
+      if (localName !== 'img') {
+        return
+      }
+      let startPosition = 0
+      const domList = document.querySelectorAll('.msgWrap img')
+      const imgList = Array.from(domList).map((item: any, index: number) => {
+        if (target.srcElement == item) {
+          startPosition = index
+        }
+        return item.src
+      })
+
+      ImagePreview({
+        images: imgList,
+        startPosition: startPosition,
+        closeable: true
+      })
+    }
+  },
+  render() {
+    return (
+      <div class={styles['help-center-detail']}>
+        <ColHeader />
+        <h2>{this.detail.title}</h2>
+        <div class={styles.titleInfo}>
+          <p>酷乐秀</p>
+          <p>{this.detail.createTime}</p>
+        </div>
+        <div
+          class={[styles.imgContent, 'msgWrap']}
+          // onClick={this.onShowImg}
+          v-html={this.detail.content}
+        ></div>
+      </div>
+    )
+  }
+})

+ 6 - 4
src/views/article-center/theory.tsx

@@ -15,6 +15,7 @@ import ColResult from '@/components/col-result'
 import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
 import { state } from '@/state'
 import { useEventTracking } from '@/helpers/hooks'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'special',
@@ -36,11 +37,11 @@ export default defineComponent({
     }
   },
   mounted() {
-    let theoryStr = sessionStorage.getItem('theoryCache')
+    const theoryStr = sessionStorage.getItem('theoryCache')
     if (theoryStr) {
       const theory = JSON.parse(theoryStr)
       this.theory = theory
-      let activeNames = theory.activeNames.split(',').map(item => item * 1)
+      const activeNames = theory.activeNames.split(',').map(item => item * 1)
       this.activeNames = activeNames
       this.params.search = theory.search || ''
     }
@@ -50,7 +51,7 @@ export default defineComponent({
   methods: {
     async getList() {
       try {
-        let params = this.params
+        const params = this.params
         const res = await request.post('/api-cms/music/theory/app/page', {
           data: {
             ...params
@@ -111,7 +112,7 @@ export default defineComponent({
         //     id: item.id
         //   }
         // })
-        let client = state.platformType === 'STUDENT' ? 'student' : 'teacher'
+        const client = state.platformType === 'STUDENT' ? 'student' : 'teacher'
         postMessage({
           api: 'openWebView',
           content: {
@@ -127,6 +128,7 @@ export default defineComponent({
     return (
       <div class={[styles['theory'], 'theory']}>
         <Sticky offsetTop={0} position="top" class={'mb12'}>
+          <ColHeader border={false} />
           <ColSearch onSearch={this.onSearch} modelValue={this.params.search} />
         </Sticky>
         {this.dataShow ? (

+ 2 - 0
src/views/cart/cart-confirm-agin/index.tsx

@@ -13,6 +13,7 @@ import UserAuth from '@/views/order-detail/userAuth'
 import ColResult from '@/components/col-result'
 import { moneyFormat } from '@/helpers/utils'
 import UseCoupons, { couponEnum } from '@/views/order-detail/use-coupons'
+import ColHeader from '@/components/col-header'
 export default defineComponent({
   name: 'cartConfirmAgin',
   setup() {
@@ -79,6 +80,7 @@ export default defineComponent({
     }
     return () => (
       <>
+        <ColHeader />
         {list.length ? (
           <div class={styles.cartConfirm}>
             <div class={styles.cartConfirmBox}>

+ 5 - 3
src/views/cart/cart-confirm/index.tsx

@@ -26,6 +26,7 @@ import {
   removeListenerMessage
 } from '@/helpers/native-message'
 import UseCoupons, { couponEnum } from '@/views/order-detail/use-coupons'
+import ColHeader from '@/components/col-header'
 export default defineComponent({
   name: 'cartConfirm',
   setup() {
@@ -36,7 +37,7 @@ export default defineComponent({
       loading.value = true
       if (route.query.cartIds) {
         try {
-          let { code, data } = await request.post(
+          const { code, data } = await request.post(
             '/api-mall-portal/order/generateConfirmOrder',
             {
               params: {
@@ -135,7 +136,7 @@ export default defineComponent({
         couponId: orderInfo.couponId
       }
       try {
-        let { code, data } = await request.post(
+        const { code, data } = await request.post(
           '/api-mall-portal/order/generateOrder',
           { data: body }
         )
@@ -164,6 +165,7 @@ export default defineComponent({
     }
     return () => (
       <>
+        <ColHeader />
         {loading.value ? null : (
           <div>
             {cartConfirm.cartPromotionItemList.length ? (
@@ -197,7 +199,7 @@ export default defineComponent({
                     title="运费"
                     value={moneyFormat(cartConfirm.calcAmount.freightAmount)}
                   ></Cell>
-                  
+
                     {/* <Cell border={false} title="优惠卷" value="暂无可用优惠卷"></Cell>
               <Cell border={false} title="乐乐币抵扣" value={"-¥" + cartConfirm.calcAmount.promotionAmount}></Cell> */}
                     <UseCoupons

+ 2 - 0
src/views/contactus/index.tsx

@@ -6,6 +6,7 @@ import kefu2 from './image/kefu2.png'
 import { Button, Toast } from 'vant'
 import { promisefiyPostMessage } from '@/helpers/native-message'
 import { state } from '@/state'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'contactus',
@@ -44,6 +45,7 @@ export default defineComponent({
 
     return () => (
       <div class={styles.container}>
+        <ColHeader />
         <div class={styles.topWrap}>
           <div class={styles.title}>
             如您在使用

+ 13 - 2
src/views/coupons/index.tsx

@@ -5,6 +5,8 @@ import { Tab, Tabs } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
 import List from './list'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
 
 export default defineComponent({
   name: 'coupon-container',
@@ -13,7 +15,8 @@ export default defineComponent({
       couponCount: {
         total: 0,
         useState: 'USABLE'
-      } as any
+      } as any,
+      height: 'auto'
     }
   },
   async mounted() {
@@ -33,7 +36,15 @@ export default defineComponent({
   render() {
     return (
       <div class={styles.coupons}>
-        <Tabs color="#2DC7AA" lineWidth={44} sticky>
+        <TheSticky
+          position="top"
+          onBarHeight={(height: any) => {
+            this.height = height
+          }}
+        >
+          <ColHeader isFixed={false} />
+        </TheSticky>
+        <Tabs color="#2DC7AA" lineWidth={44} sticky offsetTop={this.height}>
           <Tab
             title={`可使用${
               this.couponCount.total > 0

+ 9 - 9
src/views/music/album-detail/index.tsx

@@ -112,17 +112,17 @@ export default defineComponent({
         if (y.value > 20) {
           background.value = `rgba(255, 255, 255)`
           color.value = 'black'
-          postMessage({
-            api: 'backIconChange',
-            content: { iconStyle: 'black' }
-          })
+          // postMessage({
+          //   api: 'backIconChange',
+          //   content: { iconStyle: 'black' }
+          // })
         } else {
           background.value = 'transparent'
           color.value = '#fff'
-          postMessage({
-            api: 'backIconChange',
-            content: { iconStyle: 'white' }
-          })
+          // postMessage({
+          //   api: 'backIconChange',
+          //   content: { iconStyle: 'white' }
+          // })
         }
       })
 
@@ -253,7 +253,7 @@ export default defineComponent({
 
     /** 分享曲谱列表, 最大数量4 */
     const shareMusicList = computed(() => {
-      return rows.value.length > 4 ? rows.value.slice(0, 4) : rows.value
+      return rows.value.length > 4 ? rows.value.slice(0, 2) : rows.value
     })
     return () => {
       return (

+ 5 - 2
src/views/music/album/index.tsx

@@ -12,6 +12,8 @@ import SelectSubject from '../search/select-subject'
 import { SubjectEnum, useSubjectId } from '@/helpers/hooks'
 import MusicGrid from '../component/music-grid'
 import { useAsyncState } from '@vueuse/core'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
 
 export default defineComponent({
   name: 'Album',
@@ -212,7 +214,8 @@ export default defineComponent({
             error={isError.value}
           >
             {!hideSearch && (
-              <Sticky class={styles.sticky}>
+              <TheSticky class={styles.sticky}>
+                <ColHeader border={false} />
                 <Search
                   modelValue={params.search}
                   onSearch={onSearch}
@@ -252,7 +255,7 @@ export default defineComponent({
                     <Tab title={tag.name} name={tag.id}></Tab>
                   ))}
                 </Tabs>
-              </Sticky>
+              </TheSticky>
             )}
             {data.value && data.value.rows.length ? (
               <div class={styles.musicGrid}>

+ 18 - 2
src/views/music/list/index.module.less

@@ -1,6 +1,6 @@
 .sticky {
   :global(.van-sticky--fixed) {
-    box-shadow: 10px 10px 10px var(--box-shadow-color);
+    // box-shadow: 10px 10px 10px var(--box-shadow-color);
   }
 
   :global {
@@ -22,26 +22,32 @@
   font-weight: 600;
   color: #999999;
   line-height: 22px;
+
   .line {
     margin: 0 15px;
     width: 1px;
     height: 10px;
     background: #e8e8e8;
   }
+
   .active {
     color: #333333;
   }
 }
+
 .label {
   margin-right: 8px;
   font-size: 14px;
   color: #fff;
+
   :global {
+
     .van-list__loading,
     .van-list__finished-text,
     .van-list__error-text {
       width: 100%;
     }
+
     .iconfont-down {
       margin-left: 4px;
     }
@@ -71,17 +77,21 @@
       background-color: transparent;
       padding: 0;
     }
+
     .van-tab {
       font-size: 16px;
       font-weight: bold;
       color: #fff;
     }
+
     .van-tab--shrink {
       padding: 0;
       margin: 15px;
     }
+
     .van-tab--active {
       color: #fff;
+
       &::after {
         content: ' ';
         display: inline-block;
@@ -94,10 +104,12 @@
         left: 2%;
         transition: all ease 0.3s;
       }
+
       .van-tab__text {
         z-index: 1;
       }
     }
+
     .van-tabs__line {
       height: 0;
       // bottom: 30px;
@@ -113,9 +125,11 @@
   align-items: center;
   font-size: 14px;
   font-weight: 600;
+
   span {
     padding-top: 3px;
   }
+
   :global {
     .van-switch {
       margin-right: 5px;
@@ -123,6 +137,7 @@
       // background: rgba(255, 255, 255, 0.2);
       background: rgba(158, 198, 250, 1);
     }
+
     .van-switch__node {
       background: rgba(255, 255, 255, 0.51);
       // background: rgba(158, 198, 250, 1);
@@ -131,9 +146,10 @@
     .van-switch--on {
       // background: rgba(255, 255, 255, 0.8);
       background: rgba(98, 196, 171, 1);
+
       .van-switch__node {
         background: rgba(255, 255, 255, 1);
       }
     }
   }
-}
+}

+ 3 - 3
src/views/music/list/index.tsx

@@ -18,6 +18,7 @@ import { useAsyncState } from '@vueuse/core'
 import bgImg from './icons/bgImg.png'
 import iconSearch from './icons/icon_search.png'
 import { browser } from '@/helpers/utils'
+import TheSticky from '@/components/the-sticky'
 
 const noop = () => {
   //
@@ -286,8 +287,7 @@ export default defineComponent({
         <>
           {!hideSearch && (
             <>
-              {' '}
-              <Sticky class={styles.sticky}>
+              <TheSticky class={styles.sticky}>
                 <ColHeader
                   background="transparent"
                   isFixed={false}
@@ -361,7 +361,7 @@ export default defineComponent({
                     <Tab title={tag.name} name={tag.id}></Tab>
                   ))}
                 </Tabs>
-              </Sticky>
+              </TheSticky>
               <img class={styles.bgImg} src={bgImg} />
             </>
           )}

+ 2 - 0
src/views/music/look-album-list/index.tsx

@@ -1,5 +1,6 @@
 import { defineComponent } from 'vue'
 import Album from '../album'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'look-album-list',
@@ -14,6 +15,7 @@ export default defineComponent({
   render() {
     return (
       <>
+        <ColHeader />
         <Album
           hideSearch={true}
           defauleParams={{

+ 2 - 0
src/views/music/personal/index.tsx

@@ -10,6 +10,7 @@ import AlbumMy from './album-my'
 import styles from './index.module.less'
 import { getRandomKey } from '../music'
 import { useEventTracking } from '@/helpers/hooks'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'MusicPersonal',
@@ -28,6 +29,7 @@ export default defineComponent({
     return () => {
       return (
         <div class={styles.personal}>
+          <ColHeader />
           <Practice
             ref={practice}
             onFavorite={() => {

+ 5 - 2
src/views/music/search/header.tsx

@@ -16,6 +16,8 @@ import { getRandomKey } from '../music'
 import SelectSubject from './select-subject'
 import { SubjectEnum, useSubjectId } from '@/helpers/hooks'
 import { state } from '@/state'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
 
 export const mitter = mitt()
 
@@ -124,7 +126,8 @@ export default defineComponent({
     return () => {
       return (
         <div class={styles.search}>
-          <Sticky class={styles.sticky}>
+          <TheSticky class={styles.sticky}>
+            <ColHeader border={false} />
             <Search
               modelValue={keyword.value}
               // showAction
@@ -169,7 +172,7 @@ export default defineComponent({
                 <Tab title="专辑" name="album"></Tab>
               </Tabs>
             )}
-          </Sticky>
+          </TheSticky>
           {words.value.length > 0 && route.path === '/music-songbook/search' && (
             <div class={classNames(styles.keywords, 'van-hairline--bottom')}>
               <div class={styles.content}>

+ 16 - 4
src/views/music/search/index.module.less

@@ -9,32 +9,40 @@
 
   .sticky {
     :global(.van-sticky--fixed) {
-      box-shadow: 10px 10px 10px var(--box-shadow-color);
+      // box-shadow: 10px 10px 10px var(--box-shadow-color);
     }
-    > div {
+
+    >div {
       background-color: var(--base-bg);
     }
   }
+
   .title {
     padding-top: 16px;
+
     :global(.van-cell__value) {
       font-size: 12px;
     }
   }
+
   .keywords {
     margin-top: 10px;
     padding: 0 14px;
     padding-bottom: 10px;
     display: flex;
     align-items: center;
+
     .content::-webkit-scrollbar {
-      display: none; /* Chrome Safari */
+      display: none;
+      /* Chrome Safari */
     }
+
     .content {
       flex: 1;
       overflow: hidden;
       overflow-x: auto;
       display: flex;
+
       .searchKeyword {
         --van-tag-default-color: white;
         --van-tag-text-color: #333;
@@ -49,18 +57,22 @@
       font-size: 16px;
     }
   }
+
   .label {
     margin-right: 8px;
     font-size: 14px;
+
     :global {
+
       .van-list__loading,
       .van-list__finished-text,
       .van-list__error-text {
         width: 100%;
       }
+
       .iconfont-down {
         margin-left: 4px;
       }
     }
   }
-}
+}

+ 2 - 0
src/views/preview-protocol/index.tsx

@@ -1,3 +1,4 @@
+import ColHeader from '@/components/col-header'
 import request from '@/helpers/request'
 import { state } from '@/state'
 import { defineComponent } from 'vue'
@@ -31,6 +32,7 @@ export default defineComponent({
   render() {
     return (
       <div id="mProtocol">
+        <ColHeader />
         <div v-html={this.protocolHTML}></div>
       </div>
     )

+ 381 - 381
src/views/protocol/privacy.tsx

@@ -1,381 +1,381 @@
-import ColHeader from '@/components/col-header'
-import { browser } from '@/helpers/utils'
-import { state } from '@/state'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-
-export default defineComponent({
-  name: 'register',
-  data() {
-    const query = this.$route.query
-    return {
-      showHeader: query.showHeader || '0'
-    }
-  },
-  computed: {
-    name() {
-      return state.platformType === 'TEACHER' ? '酷乐秀学院' : '酷乐秀'
-    }
-  },
-  mounted() {
-    document.title = this.name + '隐私协议'
-  },
-  render() {
-    return (
-      <>
-        {this.showHeader === '1' && !browser().isApp && <ColHeader isBack />}
-        <div class={styles.container}>
-          <h2 style={{ textAlign: 'center' }}>《{this.name}隐私协议》</h2>
-          版本更新时间 <br />
-          更新日期:2022年4月18日
-          <br />
-          生效日期:2022年7月13日
-          <br />
-          公司主体:武汉酷乐秀网络科技有限公司
-          <br />
-          版本更新提示
-          <br />
-          我们可能适时修订本《隐私政策》的条款,该修订构成本《隐私政策》的一部分。如该修订造成您在本《隐私政策》下权利的实质减少,我们将在修订生效前通过在主页上显著位置提示或向您发送电子邮件或以其他方式通知您。在该种情况下,若您继续使用我们的服务,即表示同意受经修订的本《隐私政策》的约束。
-          <br />
-          引言
-          <br />
-          {this.name}是由武汉酷乐秀网络科技有限公司(以下简称“{this.name}
-          ”)为您提供的一款找谱,智能练琴,社交学习平台,问答于一体的综合音乐服务产品。
-          {this.name}
-          十分重视用户的个人信息和数据。您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明,在使用我们的服务时,我们如何收集、使用、储存和分享这些信息,以及我们为您提供的访问、更新、控制和保护这些信息的方式。本《隐私政策》与您所使用的
-          {this.name}
-          服务息息相关,希望您仔细阅读,在需要时,按照本《隐私政策》的指引,作出您认为适当的选择。本《隐私政策》中涉及的相关技术词汇,我们尽量以简明扼要的表述,并提供进一步说明的链接,以便您的理解。
-          <br />
-          您使用或继续使用我们的服务,即意味着同意我们按照本《隐私政策》收集、使用、储存和分享您的相关信息。
-          <br />
-          我们如何收集的信息
-          <br />
-          您在使用我们的产品与/或服务时,我们需要/可能需要收集和使用您的一些个人信息,我们收集和使用的您的个人信息类型包括两种: 第一种:我们产品与/或服务的核心业务功能所必需的信息:此类信息为产品与/或服务正常运行的必备信息,您须授权我们收集。如您拒绝提供,您将无法正常使用我们的产品与/或服务 第二种:我们产品与/或服务的附加业务功能可能需要收集的信息:此信息为非核心业务功能所需的信息,您可以选择是否授权我们收集。如您拒绝提供,将导致附加业务功能无法实现或无法达到我们拟达到的效果,但不影响您对核心业务功能的正常使用。
-          <br />
-          <h2>(一)账号注册/登录功能</h2>
-          <br />
-          当您使用账号注册功能时,我们会收集由您主动提供给我们的一些单独或者结合识别您实名身份的信息,包括:手机号码、验证码匹配结果,并创建密码。您的密码将以加密形式进行自动存储、传输、验证,我们不会以明文方式存储、传输、验证您的密码。您在保管、输入、使用您的密码时,应当对物理环境、电子环境审慎评估,以防密码外泄。我们收集这些信息是用以完成注册程序、为您持续稳定提供专属于注册用户的产品与/或服务,并保护您的账号安全。您应知悉,手机号码和验证码匹配结果属于您的个人敏感信息,我们收集该类信息是为了满足相关法律法规的要求,如您拒绝提供可能导致您无法使用我们的此功能,请您谨慎考虑后再提供。
-          <br />
-          需要说明的是,我们的一些产品支持您使用第三方平台的账号(例如:微信)进行登录,如您使用第三方平台的账号登录的,我们将根据您的授权获取该第三方账号下的相关信息(包括:昵称、头像,具体以您的授权内容为准)以及身份验证信息(个人敏感信息)。我们收集这些信息是用于为您提供账号登录服务以及保障您的账号安全,防范安全风险。如您拒绝授权此类信息的,您将无法使用第三方平台的账号登录我们平台,但不影响我们为您提供的其他产品和服务的正常使用。
-          <br />
-          <h2>(二)服务内容展示/浏览/播放/下载/上传功能</h2>
-          <br />
-          我们的产品与/或服务为您提供曲谱、视频、帖子服务内容的展示、浏览、播放、下载和上传功能,在此过程中,我们会根据您使用我们产品与/或服务的具体操作收集您的一些信息,包括如下个人信息:
-          <br />
-          设备信息:包括设备MAC地址、唯一设备识别码、登录IP地址、设备型号、设备名称、设备标识、浏览器类型和设置、语言设置、操作系统和应用程序版本、接入网络的方式、网络质量数据、移动网络信息(包括运营商名称)、产品版本号、设备所在位置相关信息(包括您授权我们获取的地理位置信息)。为了收集上述基本的个人设备信息,我们将会申请访问您的设备信息的权限并根据您的授权获取相关信息。
-          <br />
-          日志信息:当您使用我们的产品与/或服务时,我们会自动收集您的个人上网记录,并作为有关操作日志、服务日志保存,包括您的浏览记录、点赞/分享/评论/互动记录、收藏/关注记录、播放记录、播放时长、访问日期和时间。
-          <br />
-          获取运行中的进程:当您使用我们的产品与/或服务时,我们会不定期获取当前设备正在运行中的进程,以用来判定本App是否在前台运行,从而使App可以正常开展业务逻辑。获取的进程信息,仅用来做App是否在前台运行的判定,不会对该信息进行存储、上传、转发操作。
-          <br />
-          录音信息:当前App的核心功能是通过录音信息分析来获取频率信息,所以我们需要获取您的设备录音(麦克风)的相关权限,以用来完成当前声音频率的识别。如果您不允许App使用录音权限,该App将无法正常使用。App获取录音信息后仅用于声音分析,不会对录音信息进行存储、上传操作。
-          <br />
-          软件安装列表:近期出现不法分子通过反编译、套壳一些非法手段生成盗版的“
-          {this.name}
-          ”App,可能会导致您的个人信息泄露,个人权益受损。获取软件安装列表是为了从安装列表中获取到“
-          {this.name}
-          ”App的安装包名,包体大小,以此来判定app是否已被盗版攻击,以更好的保护您的个人权益和隐私。
-          <br />
-          我们收集这些信息是为了向您提供我们最核心的服务内容展示/播放/下载服务,如您拒绝提供上述信息和/或权限将可能导致您无法使用我们的产品与服务。请您知悉,单独的设备信息、日志信息无法识别您的身份信息。
-          <br />
-          <h2>(三)信息统计、消息推送、分享、支付</h2>
-          <br />
-          为了完成支付、统计、推送、地图,分享功能,我们还集成了其他的SDK,如您在我们平台上使用这类由第三方提供的服务时,您同意将由其直接收集和处理您的信息(如以嵌入代码、插件形式)。目前我们产品中包含的第三方SDK服务以及隐私政策详情请参阅《
-          {this.name}第三方SDK目录及其隐私政策》。
-          <br />
-          我们对您的信息承担保密义务,除以下情形外,未经您同意,我们以及我们的关联公司不会与任何第三方分享您的个人信息:
-          <br />
-          1.极光推送 SDK(https://www.jiguang.cn/) <br />
-          提供方名称:极光 <br />
-          场景描述:针对App的业务实现推送功能 <br />
-          收集方式:调用系统相关接口自动采集
-          <br />
-          个人信息类型:设备信息、网络信息与位置信息、应用信息
-          <br />
-          个人信息字段范围:(最终用户的硬件型号、硬件序列号操作系统版本、设备配置、唯一设备标识符、国际移动设备身份码IMEI/MEID、SIM卡信息IMSI、iOS系统广告标识符(IDFA)、安卓系统广告标识符(OAID)、Android
-          Id、设备Mac地址)、设备位置信息(通过GPS、蓝牙或Wi-Fi信号获得的位置信息)以及设备状态信息(如设备应用安装列表)、应用信息(应用崩溃信息、通知开关状态、软件安装列表、运行中的进程、传感器信息等相关信息)
-          <br />
-          用途或目的:用于统计详细崩溃日志以及行为分析。
-          <br />
-          网络信息:IP地址、WiFi信息、基站信息等相关信息
-          <br />
-          用途或目的:消息推送服务 <br />
-          是否为必要信息:是
-          <br />
-          信息处理方式:采用去标识化方式对个人信息进行脱敏展示 <br />
-          隐私政策: https://www.jiguang.cn/license/privacy
-          <br />
-          2.腾讯Bugly SDK
-          <br />
-          提供方名称:腾讯
-          <br />
-          场景描述:针对App的各维度的统计功能
-          <br />
-          收集方式:调用系统相关接口自动采集
-          <br />
-          个人信息类型:设备信息、设备连接信息、日志信息、粗略位置信息
-          <br />
-          个人信息字段范围:(最终用户的硬件型号、硬件序列号操作系统版本、设备配置、唯一设备标识符、国际移动设备身份码IMEI/MEID、SIM卡信息IMSI、iOS系统广告标识符(IDFA)、安卓系统广告标识符(OAID)、Android
-          Id、设备Mac地址)、设备位置信息(通过GPS、蓝牙或Wi-Fi信号获得的位置信息)以及设备状态信息(如设备应用安装列表)、应用信息(应用崩溃信息、通知开关状态、软件安装列表、运行中的进程、传感器信息等相关信息)、设备参数及系统信息(设备类型、设备型号、操作系统及硬件相关信息)
-          <br />
-          网络信息:IP地址、WiFi信息、基站信息等相关信息
-          <br />
-          用途或目的:用于统计详细崩溃日志以及行为分析。
-          <br />
-          是否为必要信息:是
-          <br />
-          信息处理方式:采用去标识化方式对个人信息进行脱敏展示
-          <br />
-          隐私政策:
-          https://privacy.qq.com/document/priview/fbd2c3f898df4c1c869925dd49d57827
-          <br />
-          3.支付宝SDK
-          <br />
-          提供方:支付宝(中国)网络技术有限公司
-          <br />
-          场景描述:登录、支付。
-          <br />
-          收集方式:调用系统相关接口自动采集
-          <br />
-          个人信息类型:设备信息、位置信息、网络信息、个人通信信息
-          <br />
-          个人信息字段:设备信息:设备标识符(IMEI、IDFA、Android
-          ID、MAC、OAID等相关信息)、应用信息(应用崩溃信息、通知开关状态、软件列表等相关信息)、设备参数及系统信息(设备类型、设备型号、操作系统及硬件相关信息)
-          <br />
-          网络信息:IP地址,WiFi信息,基站信息等相关信息
-          <br />
-          用途或目的:为了让您更安全、便捷地登录本应用,可以选择使用支付宝第三方账号授权登录本应用,方便购买本应用中的付费服务。
-          <br />
-          是否为必要信息:是
-          <br />
-          信息处理方式:采用去标识化方式对个人信息进行脱敏展示
-          <br />
-          隐私政策: https://render.alipay.com/p/c/k2cx0tg8
-          <br />
-          4.微信SDK
-          <br />
-          提供方:腾讯
-          <br />
-          场景描述:登录、支付。
-          <br />
-          收集方式:调用系统相关接口自动采集
-          <br />
-          个人信息类型:个人常用设备信息、位置信息、网络信息、个人通信信息
-          <br />
-          个人信息字段:设备信息:设备标识符(IMEI、IDFA、Android
-          ID、MAC、OAID等相关信息)、应用信息(应用崩溃信息、通知开关状态、软件列表等相关信息)
-          <br />
-          网络信息:IP地址,WiFi信息,基站信息等相关信息
-          <br />
-          用途或目的:为了让您更安全、便捷地登录本应用,可以选择使用支付宝第三方账号授权登录本应用,方便购买本应用中的付费服务。
-          <br />
-          是否为必要信息:是
-          <br />
-          信息处理方式:采用去标识化方式对个人信息进行脱敏展示
-          <br />
-          隐私政策:
-          https://open.weixin.qq.com/cgi-bin/frame?t=news/protocol_developer_tmpl
-          <br />
-          5.友盟SDK
-          <br />
-          服务类型:统计分析及分享
-          <br /> 收集个人信息类型:设备信息(IMEI/MAC/Android
-          ID/IDFA/OpenUDID/GUID/SIM卡IMSI/地理位置等)
-          <br />
-          隐私政策: https://www.umeng.com/page/policy
-          <br />
-          <h2>(四)信息制作、发布、上传、交流互动功能</h2>
-          <br />
-          当您在我们的部分产品与/或服务中使用视频剪辑创作、上传、发布、社区发帖、平台内交流互动、点赞、评论、分享服务/功能时,除注册登录账户之外,您可能会主动提供相关图文/视频内容、互动信息(包括但不限于帖子、点赞/评论/分享/交流互动信息)。我们会自动收集您的上述信息,并展示您的昵称、头像、发布的信息内容。请您知悉,您发布的信息中可能包含他人的个人信息,请您务必取得他人的合法授权,避免非法泄露他人的个人信息。如您不同意我们收集上述信息,您将无法使用我们的信息发布功能,但不影响您使用我们为您提供的其他产品和服务。
-          <br />
-          摄像头权限:当您需要上传个人中心头像跟背景图片或者上传帖子内容里面的图片、视频的时候,拍照功能需要调取您的摄像头权限,如果您拒绝授权,可能会影响您上传个人中心头像以及背景图片,发布帖子,由此带来的不便请您理解。
-          <br />
-          <h2>(五)下单与交付</h2>
-          <br />
-          当您在我们的产品与/或服务中购买商品或服务的,我们需要根据商品或服务类型收集如下部分或全部的个人信息,包括:交易商品或服务信息、收货人信息(收货人姓名、收货地址及其联系电话)(个人敏感信息)、交易金额、下单时间、订单商户、订单编号、订单状态、支付方式、支付账号、支付状态(个人敏感信息),我们收集这些信息是为了帮助您顺利完成交易、保障您的交易安全、查询订单信息、提供客户服务。
-          <br />
-          <h2>(六)客服、其他用户响应功能</h2>
-          <br />
-          当您与我们的客服互动时或使用其他用户响应功能时(包括:在线提交意见反馈、与在线/人工客服沟通、提出我们的产品与/或服务的售后申请、行使您的相关个人信息控制权、其他客户投诉和需求),为了您的账号与系统安全,我们可能需要您先行提供账号信息,并与您之前的个人信息相匹配以验证您的用户身份。在您使用客服或其他用户响应功能时,我们可能还会需要收集您的如下个人敏感信息:联系方式(您与我们联系时使用的电话号码/电子邮箱或您向我们主动提供的其他联系方式)、您与我们的沟通信息(包括文字/图片/音视频/通话记录形式)、与您需求相关联的其他必要信息。我们收集这些信息是为了调查事实与帮助您解决问题,如您拒绝提供可能导致您无法使用我们的客服用户响应机制。
-          <br />
-          <h2>(七)产品安全保障功能</h2>
-          <br />
-          我们需要收集您的一些信息来保障您使用我们的产品与/或服务时的账号与系统安全,并协助提升我们的产品与/服务的安全性和可靠性,以防产生任何危害用户、
-          {this.name}
-          、社会的行为,包括您的如下个人信息:账号登录地、个人常用设备信息(例如:硬件型号、设备MAC地址、IMEI、IMSI)、登录IP地址、产品版本号、语言模式、浏览记录、网络使用习惯、服务故障信息,以及个人敏感信息:交易信息、会员实名认证信息。我们会根据上述信息来综合判断您账号、账户及交易风险、进行身份验证、客户服务、检测及防范安全事件、诈骗监测、存档和备份用途,并依法采取必要的记录、审计、分析、处置措施,一旦我们检测出存在或疑似存在账号安全风险时,我们会使用相关信息进行安全验证与风险排除,确保我们向您提供的产品和服务的安全性,以用来保障您的权益不受侵害。同时,当发生账号或系统安全问题时,我们会收集这些信息来优化我们的产品和服务。
-          <br />
-          此外,为确保您设备操作环境的安全以及提供我们的产品与/或服务所必需,防止恶意程序和反作弊,我们会在您同意本《隐私政策》后获取您设备上已安装或正在运行的必要的应用/软件列表信息(包括应用/软件来源、应用/软件总体运行情况、崩溃情况、使用频率)。请您知悉,单独的应用/软件列表信息无法识别您的特定身份。
-          <br />
-          例外情形,另外,您充分理解并同意,我们在以下情况下收集、使用您的个人信息无需您的授权同意:
-          <br />
-          与我们履行法律法规规定的义务相关的; 与国家安全、国防安全直接相关的;
-          <br />
-          与公共安全、公共卫生、重大公共利益直接相关的;
-          <br />
-          与犯罪侦查、起诉、审判和判决执行直接相关的;
-          <br />
-          出于维护您或其他个人的生命、财产重大合法权益但又很难得到您本人同意的;
-          <br />
-          所收集的信息是您自行向社会公开的或者是从合法公开的渠道(如合法的新闻报道、政府信息公开渠道)中收集到的;
-          <br />
-          根据与您签订和履行相关协议或其他书面文件所必需的;
-          <br />
-          用于维护我们的产品与/或服务的安全稳定运行所必需的,例如发现、处置产品与/或服务的故障
-          <br />
-          有权机关的要求、法律法规规定的其他情形。 我们如何使用Cookie和同类技术
-          <br />
-          (一)关于Cookie和同类技术
-          <br />
-          Cookie是包含字符串的小文件,在您登入和使用网站或其他网络内容时发送、存放在您的计算机、移动设备或其他装置内(通常经过加密)。Cookie同类技术是可用于与Cookie类似用途的其他技术,例如:Web
-          <br />
-          Beacon、Proxy、嵌入式脚本。
-          <br />
-          目前,我们主要使用Cookie收集您的个人信息。您知悉并同意,随着技术的发展和我们产品和服务的进一步完善,我们也可能会使用Cookie同类技术
-          <br />
-          (二)我们如何使用Cookie和同类技术
-          <br />
-          在您使用我们的产品与/或服务时,我们可能会使用Cookie和同类技术收集您的一些个人信息,包括:您访问网站的习惯、您的浏览信息、您的登录信息
-          <br />
-          如果您的浏览器允许,您可以通过您的浏览器的设置以管理、(部分/全部)拒绝Cookie与/或同类技术;或删除已经储存在您的计算机、移动设备或其他装置内的Cookie与/或同类技术,从而实现我们无法全部或部分追踪您的个人信息。您如需详细了解如何更改浏览器设置,请具体查看您使用的浏览器的相关设置页面。您理解并知悉:我们的某些产品/服务只能通过使用Cookie或同类技术才可得到实现,如您拒绝使用或删除的,您可能将无法正常使用我们的相关产品与/或服务或无法通过我们的产品与/或服务获得最佳的服务体验,同时也可能会对您的信息保护和账号安全性造成一定的影响。
-          <br />
-          我们如何共享、转让、公开披露您的个人信息
-          <br />
-          除以下情形外,未经您同意,我们以及我们的关联公司不会与任何第三方分享您的个人信息:
-          <br />
-          我们以及我们的关联公司,可能将您的个人信息与我们的关联公司、合作伙伴及第三方服务供应商、承包商及代理(例如代表我们发出电子邮件或推送通知的通讯服务提供商、为我们提供位置数据的地图服务供应商)分享(他们可能并非位于您所在的法域),用作下列用途:
-          <br />
-          向您提供我们的服务; 实现“我们可能如何使用信息”部分所述目的;
-          <br />
-          履行我们在《酷乐秀服务协议》或本《隐私政策》中的义务和行使我们的权利;
-          <br />
-          理解、维护和改善我们的服务。
-          <br />
-          如我们或我们的关联公司与任何上述第三方分享您的个人信息,我们将努力确保该第三方在使用您的个人信息时遵守本《隐私政策》及我们要求其遵守的其他适当的保密和安全措施。
-          <br />
-          随着我们业务的持续发展,我们以及我们的关联公司有可能进行合并、收购、资产转让或类似的交易,您的个人信息有可能作为此类交易的一部分而被转移。我们将在转移前通知您。
-          <br />
-          我们或我们的关联公司还可能为以下需要而保留、保存或披露您的个人信息:
-          <br />
-          遵守适用的法律法规; 遵守法院命令或其他法律程序的规定;
-          <br />
-          遵守相关政府机关的要求;
-          <br />
-          为遵守适用的法律法规、维护社会公共利益,或保护我们的客户、我们或我们的集团公司、其他用户或雇员的人身和财产安全或合法权益所合理必需的用途。
-          <br />
-          您对个人信息享有的控制权
-          <br />
-          您对我们产品与/或服务中的您的个人信息享有多种方式的控制权,包括:您可以访问、更正/修改、删除您的个人信息,也可以撤回之前作出的对您个人信息的同意,同时您也可以注销您的账号。为便于您行使您的上述控制权,我们在产品的相关功能页面为您提供了操作指引和操作设置,您可以自行进行操作,如您在操作过程中有疑惑或困难的可以通过文末的方式联系我们来进行控制,我们会及时为您处理。
-          <br />
-          (一)访问权
-          <br />
-          您可以在我们的产品与/或服务中查询或访问您的相关个人信息,包括:
-          <br />
-          账号信息:您可以通过相关产品页面随时登录您的个人账号,随时查询或访问您的账号中的个人资料信息,包括:头像、昵称、性别、个性签名。
-          <br />
-          使用信息:您可以通过相关产品页面随时访问您的使用信息,包括:收藏记录、观看历史、离线下载记录、搜索记录、上传内容、订单信息。
-          <br />
-          其他信息:如您在此访问过程中遇到操作问题的或如需获取其他前述无法获知的个人信息内容,您可通过文末提供的方式联系我们,我们将在核实您的身份后在合理期限内向您提供,但法律法规另有规定的或本政策另有约定的除外。
-          <br />
-          (二)更正/修改权
-          <br />
-          您可以在我们的产品与/或服务中更正/修改您的相关个人信息。为便于您行使您的上述权利,我们为您提供了在线自行更正/修改和向我们提出更正/修改申请两种方式。
-          <br />
-          对于您的部分个人信息,我们在产品的相关功能页面为您提供了操作指引和操作设置,您可以直接进行更正/修改,例如:“头像/昵称/性别/个性签名”信息在“手机端APP”中的更正/修改路径为:我的—设置;
-          <br />
-          对于您在行使上述权利过程中遇到的困难,或其他可能未/无法向您提供在线自行更正/修改权限的,
-          <br />
-          经对您的身份进行验证,且更正不影响信息的客观性和准确性的情况下,您有权对错误或不完整的信息作出更正或修改,或在特定情况下,尤其是数据错误时,通过我们公布的反馈与报错措施将您的更正/修改申请提交给我们,要求我们更正或修改您的数据,但法律法规另有规定的除外。但出于安全性和身份识别的考虑,您可能无法修改注册时提交的某些初始注册信息。
-          <br />
-          (三)注销权
-          <br />
-          我们为您提供账号注销的多种途径,您可以通过在线申请注销或联系我们的客服或通过其他我们公示的方式申请注销您的账号。在您注销账号后,您将无法再以此账号登录和使用
-          {this.name}
-          旗下的相关产品与服务;该账号在{this.name}
-          的产品与服务使用期间已产生的但未消耗完毕的权益及未来的预期利益全部权益将被清除;该账号下的内容、信息、数据、记录将会被删除或匿名化处理(但法律法规另有规定或监管部门另有要求的除外);同时,账号一旦注销超过一定时间,将无法恢复。
-          <br />
-          (四)提前获知产品与/或服务停止运营权
-          <br />
-          我们将持续为您提供优质服务,若因特殊原因导致我们的部分或全部产品与/或服务被迫停止运营,我们将提前在显著位置或向您发送推送消息或以其他方式通知您,并将停止对您个人信息的收集,同时在超出法律法规规定的必需且最短期限后,我们将会对所持有的您的个人信息进行删除或匿名化处理。
-          <br />
-          (五)帮助反馈权
-          <br />
-          我们为您提供了多种反馈渠道,联系客服,联系电话,帮助反馈。
-          <br />
-          我们如何存储和保护您的个人信息
-          <br />
-          (一)个人信息的存储
-          <br />
-          存储地点:我们依照法律法规的规定,将您的个人信息存储于中华人民共和国境内。目前我们不存在跨境存储您的个人信息或向境外提供个人信息的场景。如需跨境存储或向境外提供个人信息的,我们会单独向您明确告知(包括出境的目的、接收方、使用方式与范围、使用内容、安全保障措施、安全风险)并再次征得您的授权同意,并严格要求接收方按照本《隐私政策》以及法律法规相关要求来处理您的个人信息;
-          <br />
-          存储期限:我们在为提供我们的产品和服务之目的所必需且最短的期间内保留您的个人信息,例如:当您使用我们的注册登录及会员功能时,我们需要收集您的手机号码,且在您提供后并在您使用该功能期间,我们需要持续为您保留您的手机号码,以向您正常提供该功能、保障您的账号和系统安全。在超出上述存储期限后,我们会对您的个人信息进行删除或匿名化处理。但您行使删除权、注销账号的或法律法规另有规定的除外(例如:《电子商务法》规定:商品和服务信息、交易信息保存时间自交易完成之日起不少于三年)。
-          <br />
-          (二)个人信息的保护措施
-          <br />
-          我们一直
-          <br />
-          都极为重视保护用户的个人信息安全,为此我们采用了符合行业标准的安全技术措施及组织和管理措施保护措施以最大程度降低您的信息被泄露、毁损、误用、非授权访问、非授权披露和更改的风险。
-          未成年人保护
-          <br />
-          {this.name}
-          一直非常注重对未成年人的保护,致力于践行我们的企业社会责任。
-          <br />
-          {this.name}
-          的绝大部分产品与/或服务主要面向成年人提供,针对这部分产品与/或服务,我们不会主动直接向未成年人收集其个人信息,如未成年人需要使用的,应首先取得其监护人的同意(包括本政策),在监护人同意后和指导下进行使用、提交个人信息;我们希望监护人亦能积极的教育和引导未成年人增强个人信息保护意识和能力,保护未成年人个人信息安全。
-          {this.name}
-          会严格履行法律规定的未成年人保护义务与责任,我们只会在法律允许、监护人同意或保护未成年人所必要的情况下收集、使用、共享、转让或披露未成年人个人信息,如果我们发现未成年人在未事先获得其监护人同意的情况下使用了我们的产品与/或服务的,我们会尽最大努力与监护人取得联系,并在监护人要求下尽快删除相关未成年人个人信息。
-          <br />
-          本《隐私政策》的更新
-          <br />
-          我们鼓励您在每次使用我们的产品或服务时都查阅我们的《隐私政策》。为了给您提供更好的服务,我们会根据产品的更新情况及法律法规的相关要求更新本《隐私政策》的条款,该更新构成本《隐私政策》的一部分。如该更新造成您在本《隐私政策》下权利的实质减少或重大变更,我们将在本政策生效前通过在显著位置提示或向您发送推送消息或以其他方式通知您,若您继续使用我们的服务,即表示您充分阅读、理解并同意受经修订的《隐私政策》的约束。为保障您的合法权益,我们建议您可以定期在我们平台的设置页面中查看本政策。
-          <br />
-          上述的“重大变更”包括但不限于:
-          <br />
-          我们的服务模式发生重大变化。如处理个人信息的目的、处理的个人信息的类型、个人信息的使用方式;
-          <br />
-          我们在所有权结构、组织架构方面发生重大变化。如业务调整、破产并购引起的所有者变更;
-          <br />
-          个人信息共享、转让或公开披露的主要对象发生变化;
-          <br />
-          您参与个人信息处理方面的权利及其行使方式发生重大变化;
-          <br />
-          我们负责处理个人信息安全的责任部门、联络方式及投诉渠道发生变化时;
-          <br />
-          个人信息安全影响评估报告表明存在高风险时;
-          <br />
-          其他重要的或可能严重影响您的个人权益的情况发生时。
-          <br />
-          如何联系我们
-          <br />
-          (一)如您对本《隐私政策》的执行或使用我们的服务时遇到的与隐私保护相关的事宜有任何问题(包括问题咨询、投诉),我们专门为您提供了多种反馈通道,希望为您提供满意的解决方案:
-          <br />
-          在线客服/其他在线意见反馈通道:您可与我们平台上产品功能页面的在线客服联系或者在线提交意见反馈;
-          <br />
-          人工客服通道:您可以拨打我们的任何一部客服电话与我们联系(400-018-5077);
-          <br />
-          (二)我们会在收到您的意见及建议后,并在验证您的用户身份后的15个工作日内或法律法规规定的期限内尽快向您回复,一般情况下,我们不会因此对您收取服务费。但是,在以下情形下,您理解并知悉,我们将无法响应您的请求:
-          <br />
-          与我们履行法律法规规定的义务相关的; 与国家安全、国防安全直接相关的
-          <br />
-          与公共安全、公共卫生、重大公共利益直接相关的
-          <br />
-          与犯罪侦查、起诉和审判有关的;
-          <br />
-          有充分证据表明您存在主观恶意或滥用权利的;
-          <br />
-          出于维护您或其他个人的生命、财产重大权益但又难得到本人授权同意的;
-          <br />
-          响应您的请求将导致您或其他个人、组织的合法权益受到严重损害的;
-          <br />
-          涉及商业秘密的; 法律法规规定的其他情形。
-          <br />
-          其他
-          <br />
-          (一)本《隐私政策》的解释及争议解决均应适用中华人民共和国大陆地区法律。如就本政策的签订、履行发生任何争议的,双方应尽量友好协商解决;协商不成时,任何一方均可向被告住所地享有管辖权的人民法院提起诉讼。
-          <br />
-          (二)本《隐私政策》的标题仅为方便及阅读而设,并不影响正文其中任何规定的含义或解释。
-          <br />
-        </div>
-      </>
-    )
-  }
-})
+import ColHeader from '@/components/col-header'
+import { browser } from '@/helpers/utils'
+import { state } from '@/state'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'register',
+  data() {
+    const query = this.$route.query
+    return {
+      showHeader: query.showHeader || '0'
+    }
+  },
+  computed: {
+    name() {
+      return state.platformType === 'TEACHER' ? '酷乐秀学院' : '酷乐秀'
+    }
+  },
+  mounted() {
+    document.title = this.name + '隐私协议'
+  },
+  render() {
+    return (
+      <>
+        <ColHeader />
+        <div class={styles.container}>
+          <h2 style={{ textAlign: 'center' }}>《{this.name}隐私协议》</h2>
+          版本更新时间 <br />
+          更新日期:2022年4月18日
+          <br />
+          生效日期:2022年7月13日
+          <br />
+          公司主体:武汉酷乐秀网络科技有限公司
+          <br />
+          版本更新提示
+          <br />
+          我们可能适时修订本《隐私政策》的条款,该修订构成本《隐私政策》的一部分。如该修订造成您在本《隐私政策》下权利的实质减少,我们将在修订生效前通过在主页上显著位置提示或向您发送电子邮件或以其他方式通知您。在该种情况下,若您继续使用我们的服务,即表示同意受经修订的本《隐私政策》的约束。
+          <br />
+          引言
+          <br />
+          {this.name}是由武汉酷乐秀网络科技有限公司(以下简称“{this.name}
+          ”)为您提供的一款找谱,智能练琴,社交学习平台,问答于一体的综合音乐服务产品。
+          {this.name}
+          十分重视用户的个人信息和数据。您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明,在使用我们的服务时,我们如何收集、使用、储存和分享这些信息,以及我们为您提供的访问、更新、控制和保护这些信息的方式。本《隐私政策》与您所使用的
+          {this.name}
+          服务息息相关,希望您仔细阅读,在需要时,按照本《隐私政策》的指引,作出您认为适当的选择。本《隐私政策》中涉及的相关技术词汇,我们尽量以简明扼要的表述,并提供进一步说明的链接,以便您的理解。
+          <br />
+          您使用或继续使用我们的服务,即意味着同意我们按照本《隐私政策》收集、使用、储存和分享您的相关信息。
+          <br />
+          我们如何收集的信息
+          <br />
+          您在使用我们的产品与/或服务时,我们需要/可能需要收集和使用您的一些个人信息,我们收集和使用的您的个人信息类型包括两种: 第一种:我们产品与/或服务的核心业务功能所必需的信息:此类信息为产品与/或服务正常运行的必备信息,您须授权我们收集。如您拒绝提供,您将无法正常使用我们的产品与/或服务 第二种:我们产品与/或服务的附加业务功能可能需要收集的信息:此信息为非核心业务功能所需的信息,您可以选择是否授权我们收集。如您拒绝提供,将导致附加业务功能无法实现或无法达到我们拟达到的效果,但不影响您对核心业务功能的正常使用。
+          <br />
+          <h2>(一)账号注册/登录功能</h2>
+          <br />
+          当您使用账号注册功能时,我们会收集由您主动提供给我们的一些单独或者结合识别您实名身份的信息,包括:手机号码、验证码匹配结果,并创建密码。您的密码将以加密形式进行自动存储、传输、验证,我们不会以明文方式存储、传输、验证您的密码。您在保管、输入、使用您的密码时,应当对物理环境、电子环境审慎评估,以防密码外泄。我们收集这些信息是用以完成注册程序、为您持续稳定提供专属于注册用户的产品与/或服务,并保护您的账号安全。您应知悉,手机号码和验证码匹配结果属于您的个人敏感信息,我们收集该类信息是为了满足相关法律法规的要求,如您拒绝提供可能导致您无法使用我们的此功能,请您谨慎考虑后再提供。
+          <br />
+          需要说明的是,我们的一些产品支持您使用第三方平台的账号(例如:微信)进行登录,如您使用第三方平台的账号登录的,我们将根据您的授权获取该第三方账号下的相关信息(包括:昵称、头像,具体以您的授权内容为准)以及身份验证信息(个人敏感信息)。我们收集这些信息是用于为您提供账号登录服务以及保障您的账号安全,防范安全风险。如您拒绝授权此类信息的,您将无法使用第三方平台的账号登录我们平台,但不影响我们为您提供的其他产品和服务的正常使用。
+          <br />
+          <h2>(二)服务内容展示/浏览/播放/下载/上传功能</h2>
+          <br />
+          我们的产品与/或服务为您提供曲谱、视频、帖子服务内容的展示、浏览、播放、下载和上传功能,在此过程中,我们会根据您使用我们产品与/或服务的具体操作收集您的一些信息,包括如下个人信息:
+          <br />
+          设备信息:包括设备MAC地址、唯一设备识别码、登录IP地址、设备型号、设备名称、设备标识、浏览器类型和设置、语言设置、操作系统和应用程序版本、接入网络的方式、网络质量数据、移动网络信息(包括运营商名称)、产品版本号、设备所在位置相关信息(包括您授权我们获取的地理位置信息)。为了收集上述基本的个人设备信息,我们将会申请访问您的设备信息的权限并根据您的授权获取相关信息。
+          <br />
+          日志信息:当您使用我们的产品与/或服务时,我们会自动收集您的个人上网记录,并作为有关操作日志、服务日志保存,包括您的浏览记录、点赞/分享/评论/互动记录、收藏/关注记录、播放记录、播放时长、访问日期和时间。
+          <br />
+          获取运行中的进程:当您使用我们的产品与/或服务时,我们会不定期获取当前设备正在运行中的进程,以用来判定本App是否在前台运行,从而使App可以正常开展业务逻辑。获取的进程信息,仅用来做App是否在前台运行的判定,不会对该信息进行存储、上传、转发操作。
+          <br />
+          录音信息:当前App的核心功能是通过录音信息分析来获取频率信息,所以我们需要获取您的设备录音(麦克风)的相关权限,以用来完成当前声音频率的识别。如果您不允许App使用录音权限,该App将无法正常使用。App获取录音信息后仅用于声音分析,不会对录音信息进行存储、上传操作。
+          <br />
+          软件安装列表:近期出现不法分子通过反编译、套壳一些非法手段生成盗版的“
+          {this.name}
+          ”App,可能会导致您的个人信息泄露,个人权益受损。获取软件安装列表是为了从安装列表中获取到“
+          {this.name}
+          ”App的安装包名,包体大小,以此来判定app是否已被盗版攻击,以更好的保护您的个人权益和隐私。
+          <br />
+          我们收集这些信息是为了向您提供我们最核心的服务内容展示/播放/下载服务,如您拒绝提供上述信息和/或权限将可能导致您无法使用我们的产品与服务。请您知悉,单独的设备信息、日志信息无法识别您的身份信息。
+          <br />
+          <h2>(三)信息统计、消息推送、分享、支付</h2>
+          <br />
+          为了完成支付、统计、推送、地图,分享功能,我们还集成了其他的SDK,如您在我们平台上使用这类由第三方提供的服务时,您同意将由其直接收集和处理您的信息(如以嵌入代码、插件形式)。目前我们产品中包含的第三方SDK服务以及隐私政策详情请参阅《
+          {this.name}第三方SDK目录及其隐私政策》。
+          <br />
+          我们对您的信息承担保密义务,除以下情形外,未经您同意,我们以及我们的关联公司不会与任何第三方分享您的个人信息:
+          <br />
+          1.极光推送 SDK(https://www.jiguang.cn/) <br />
+          提供方名称:极光 <br />
+          场景描述:针对App的业务实现推送功能 <br />
+          收集方式:调用系统相关接口自动采集
+          <br />
+          个人信息类型:设备信息、网络信息与位置信息、应用信息
+          <br />
+          个人信息字段范围:(最终用户的硬件型号、硬件序列号操作系统版本、设备配置、唯一设备标识符、国际移动设备身份码IMEI/MEID、SIM卡信息IMSI、iOS系统广告标识符(IDFA)、安卓系统广告标识符(OAID)、Android
+          Id、设备Mac地址)、设备位置信息(通过GPS、蓝牙或Wi-Fi信号获得的位置信息)以及设备状态信息(如设备应用安装列表)、应用信息(应用崩溃信息、通知开关状态、软件安装列表、运行中的进程、传感器信息等相关信息)
+          <br />
+          用途或目的:用于统计详细崩溃日志以及行为分析。
+          <br />
+          网络信息:IP地址、WiFi信息、基站信息等相关信息
+          <br />
+          用途或目的:消息推送服务 <br />
+          是否为必要信息:是
+          <br />
+          信息处理方式:采用去标识化方式对个人信息进行脱敏展示 <br />
+          隐私政策: https://www.jiguang.cn/license/privacy
+          <br />
+          2.腾讯Bugly SDK
+          <br />
+          提供方名称:腾讯
+          <br />
+          场景描述:针对App的各维度的统计功能
+          <br />
+          收集方式:调用系统相关接口自动采集
+          <br />
+          个人信息类型:设备信息、设备连接信息、日志信息、粗略位置信息
+          <br />
+          个人信息字段范围:(最终用户的硬件型号、硬件序列号操作系统版本、设备配置、唯一设备标识符、国际移动设备身份码IMEI/MEID、SIM卡信息IMSI、iOS系统广告标识符(IDFA)、安卓系统广告标识符(OAID)、Android
+          Id、设备Mac地址)、设备位置信息(通过GPS、蓝牙或Wi-Fi信号获得的位置信息)以及设备状态信息(如设备应用安装列表)、应用信息(应用崩溃信息、通知开关状态、软件安装列表、运行中的进程、传感器信息等相关信息)、设备参数及系统信息(设备类型、设备型号、操作系统及硬件相关信息)
+          <br />
+          网络信息:IP地址、WiFi信息、基站信息等相关信息
+          <br />
+          用途或目的:用于统计详细崩溃日志以及行为分析。
+          <br />
+          是否为必要信息:是
+          <br />
+          信息处理方式:采用去标识化方式对个人信息进行脱敏展示
+          <br />
+          隐私政策:
+          https://privacy.qq.com/document/priview/fbd2c3f898df4c1c869925dd49d57827
+          <br />
+          3.支付宝SDK
+          <br />
+          提供方:支付宝(中国)网络技术有限公司
+          <br />
+          场景描述:登录、支付。
+          <br />
+          收集方式:调用系统相关接口自动采集
+          <br />
+          个人信息类型:设备信息、位置信息、网络信息、个人通信信息
+          <br />
+          个人信息字段:设备信息:设备标识符(IMEI、IDFA、Android
+          ID、MAC、OAID等相关信息)、应用信息(应用崩溃信息、通知开关状态、软件列表等相关信息)、设备参数及系统信息(设备类型、设备型号、操作系统及硬件相关信息)
+          <br />
+          网络信息:IP地址,WiFi信息,基站信息等相关信息
+          <br />
+          用途或目的:为了让您更安全、便捷地登录本应用,可以选择使用支付宝第三方账号授权登录本应用,方便购买本应用中的付费服务。
+          <br />
+          是否为必要信息:是
+          <br />
+          信息处理方式:采用去标识化方式对个人信息进行脱敏展示
+          <br />
+          隐私政策: https://render.alipay.com/p/c/k2cx0tg8
+          <br />
+          4.微信SDK
+          <br />
+          提供方:腾讯
+          <br />
+          场景描述:登录、支付。
+          <br />
+          收集方式:调用系统相关接口自动采集
+          <br />
+          个人信息类型:个人常用设备信息、位置信息、网络信息、个人通信信息
+          <br />
+          个人信息字段:设备信息:设备标识符(IMEI、IDFA、Android
+          ID、MAC、OAID等相关信息)、应用信息(应用崩溃信息、通知开关状态、软件列表等相关信息)
+          <br />
+          网络信息:IP地址,WiFi信息,基站信息等相关信息
+          <br />
+          用途或目的:为了让您更安全、便捷地登录本应用,可以选择使用支付宝第三方账号授权登录本应用,方便购买本应用中的付费服务。
+          <br />
+          是否为必要信息:是
+          <br />
+          信息处理方式:采用去标识化方式对个人信息进行脱敏展示
+          <br />
+          隐私政策:
+          https://open.weixin.qq.com/cgi-bin/frame?t=news/protocol_developer_tmpl
+          <br />
+          5.友盟SDK
+          <br />
+          服务类型:统计分析及分享
+          <br /> 收集个人信息类型:设备信息(IMEI/MAC/Android
+          ID/IDFA/OpenUDID/GUID/SIM卡IMSI/地理位置等)
+          <br />
+          隐私政策: https://www.umeng.com/page/policy
+          <br />
+          <h2>(四)信息制作、发布、上传、交流互动功能</h2>
+          <br />
+          当您在我们的部分产品与/或服务中使用视频剪辑创作、上传、发布、社区发帖、平台内交流互动、点赞、评论、分享服务/功能时,除注册登录账户之外,您可能会主动提供相关图文/视频内容、互动信息(包括但不限于帖子、点赞/评论/分享/交流互动信息)。我们会自动收集您的上述信息,并展示您的昵称、头像、发布的信息内容。请您知悉,您发布的信息中可能包含他人的个人信息,请您务必取得他人的合法授权,避免非法泄露他人的个人信息。如您不同意我们收集上述信息,您将无法使用我们的信息发布功能,但不影响您使用我们为您提供的其他产品和服务。
+          <br />
+          摄像头权限:当您需要上传个人中心头像跟背景图片或者上传帖子内容里面的图片、视频的时候,拍照功能需要调取您的摄像头权限,如果您拒绝授权,可能会影响您上传个人中心头像以及背景图片,发布帖子,由此带来的不便请您理解。
+          <br />
+          <h2>(五)下单与交付</h2>
+          <br />
+          当您在我们的产品与/或服务中购买商品或服务的,我们需要根据商品或服务类型收集如下部分或全部的个人信息,包括:交易商品或服务信息、收货人信息(收货人姓名、收货地址及其联系电话)(个人敏感信息)、交易金额、下单时间、订单商户、订单编号、订单状态、支付方式、支付账号、支付状态(个人敏感信息),我们收集这些信息是为了帮助您顺利完成交易、保障您的交易安全、查询订单信息、提供客户服务。
+          <br />
+          <h2>(六)客服、其他用户响应功能</h2>
+          <br />
+          当您与我们的客服互动时或使用其他用户响应功能时(包括:在线提交意见反馈、与在线/人工客服沟通、提出我们的产品与/或服务的售后申请、行使您的相关个人信息控制权、其他客户投诉和需求),为了您的账号与系统安全,我们可能需要您先行提供账号信息,并与您之前的个人信息相匹配以验证您的用户身份。在您使用客服或其他用户响应功能时,我们可能还会需要收集您的如下个人敏感信息:联系方式(您与我们联系时使用的电话号码/电子邮箱或您向我们主动提供的其他联系方式)、您与我们的沟通信息(包括文字/图片/音视频/通话记录形式)、与您需求相关联的其他必要信息。我们收集这些信息是为了调查事实与帮助您解决问题,如您拒绝提供可能导致您无法使用我们的客服用户响应机制。
+          <br />
+          <h2>(七)产品安全保障功能</h2>
+          <br />
+          我们需要收集您的一些信息来保障您使用我们的产品与/或服务时的账号与系统安全,并协助提升我们的产品与/服务的安全性和可靠性,以防产生任何危害用户、
+          {this.name}
+          、社会的行为,包括您的如下个人信息:账号登录地、个人常用设备信息(例如:硬件型号、设备MAC地址、IMEI、IMSI)、登录IP地址、产品版本号、语言模式、浏览记录、网络使用习惯、服务故障信息,以及个人敏感信息:交易信息、会员实名认证信息。我们会根据上述信息来综合判断您账号、账户及交易风险、进行身份验证、客户服务、检测及防范安全事件、诈骗监测、存档和备份用途,并依法采取必要的记录、审计、分析、处置措施,一旦我们检测出存在或疑似存在账号安全风险时,我们会使用相关信息进行安全验证与风险排除,确保我们向您提供的产品和服务的安全性,以用来保障您的权益不受侵害。同时,当发生账号或系统安全问题时,我们会收集这些信息来优化我们的产品和服务。
+          <br />
+          此外,为确保您设备操作环境的安全以及提供我们的产品与/或服务所必需,防止恶意程序和反作弊,我们会在您同意本《隐私政策》后获取您设备上已安装或正在运行的必要的应用/软件列表信息(包括应用/软件来源、应用/软件总体运行情况、崩溃情况、使用频率)。请您知悉,单独的应用/软件列表信息无法识别您的特定身份。
+          <br />
+          例外情形,另外,您充分理解并同意,我们在以下情况下收集、使用您的个人信息无需您的授权同意:
+          <br />
+          与我们履行法律法规规定的义务相关的; 与国家安全、国防安全直接相关的;
+          <br />
+          与公共安全、公共卫生、重大公共利益直接相关的;
+          <br />
+          与犯罪侦查、起诉、审判和判决执行直接相关的;
+          <br />
+          出于维护您或其他个人的生命、财产重大合法权益但又很难得到您本人同意的;
+          <br />
+          所收集的信息是您自行向社会公开的或者是从合法公开的渠道(如合法的新闻报道、政府信息公开渠道)中收集到的;
+          <br />
+          根据与您签订和履行相关协议或其他书面文件所必需的;
+          <br />
+          用于维护我们的产品与/或服务的安全稳定运行所必需的,例如发现、处置产品与/或服务的故障
+          <br />
+          有权机关的要求、法律法规规定的其他情形。 我们如何使用Cookie和同类技术
+          <br />
+          (一)关于Cookie和同类技术
+          <br />
+          Cookie是包含字符串的小文件,在您登入和使用网站或其他网络内容时发送、存放在您的计算机、移动设备或其他装置内(通常经过加密)。Cookie同类技术是可用于与Cookie类似用途的其他技术,例如:Web
+          <br />
+          Beacon、Proxy、嵌入式脚本。
+          <br />
+          目前,我们主要使用Cookie收集您的个人信息。您知悉并同意,随着技术的发展和我们产品和服务的进一步完善,我们也可能会使用Cookie同类技术
+          <br />
+          (二)我们如何使用Cookie和同类技术
+          <br />
+          在您使用我们的产品与/或服务时,我们可能会使用Cookie和同类技术收集您的一些个人信息,包括:您访问网站的习惯、您的浏览信息、您的登录信息
+          <br />
+          如果您的浏览器允许,您可以通过您的浏览器的设置以管理、(部分/全部)拒绝Cookie与/或同类技术;或删除已经储存在您的计算机、移动设备或其他装置内的Cookie与/或同类技术,从而实现我们无法全部或部分追踪您的个人信息。您如需详细了解如何更改浏览器设置,请具体查看您使用的浏览器的相关设置页面。您理解并知悉:我们的某些产品/服务只能通过使用Cookie或同类技术才可得到实现,如您拒绝使用或删除的,您可能将无法正常使用我们的相关产品与/或服务或无法通过我们的产品与/或服务获得最佳的服务体验,同时也可能会对您的信息保护和账号安全性造成一定的影响。
+          <br />
+          我们如何共享、转让、公开披露您的个人信息
+          <br />
+          除以下情形外,未经您同意,我们以及我们的关联公司不会与任何第三方分享您的个人信息:
+          <br />
+          我们以及我们的关联公司,可能将您的个人信息与我们的关联公司、合作伙伴及第三方服务供应商、承包商及代理(例如代表我们发出电子邮件或推送通知的通讯服务提供商、为我们提供位置数据的地图服务供应商)分享(他们可能并非位于您所在的法域),用作下列用途:
+          <br />
+          向您提供我们的服务; 实现“我们可能如何使用信息”部分所述目的;
+          <br />
+          履行我们在《酷乐秀服务协议》或本《隐私政策》中的义务和行使我们的权利;
+          <br />
+          理解、维护和改善我们的服务。
+          <br />
+          如我们或我们的关联公司与任何上述第三方分享您的个人信息,我们将努力确保该第三方在使用您的个人信息时遵守本《隐私政策》及我们要求其遵守的其他适当的保密和安全措施。
+          <br />
+          随着我们业务的持续发展,我们以及我们的关联公司有可能进行合并、收购、资产转让或类似的交易,您的个人信息有可能作为此类交易的一部分而被转移。我们将在转移前通知您。
+          <br />
+          我们或我们的关联公司还可能为以下需要而保留、保存或披露您的个人信息:
+          <br />
+          遵守适用的法律法规; 遵守法院命令或其他法律程序的规定;
+          <br />
+          遵守相关政府机关的要求;
+          <br />
+          为遵守适用的法律法规、维护社会公共利益,或保护我们的客户、我们或我们的集团公司、其他用户或雇员的人身和财产安全或合法权益所合理必需的用途。
+          <br />
+          您对个人信息享有的控制权
+          <br />
+          您对我们产品与/或服务中的您的个人信息享有多种方式的控制权,包括:您可以访问、更正/修改、删除您的个人信息,也可以撤回之前作出的对您个人信息的同意,同时您也可以注销您的账号。为便于您行使您的上述控制权,我们在产品的相关功能页面为您提供了操作指引和操作设置,您可以自行进行操作,如您在操作过程中有疑惑或困难的可以通过文末的方式联系我们来进行控制,我们会及时为您处理。
+          <br />
+          (一)访问权
+          <br />
+          您可以在我们的产品与/或服务中查询或访问您的相关个人信息,包括:
+          <br />
+          账号信息:您可以通过相关产品页面随时登录您的个人账号,随时查询或访问您的账号中的个人资料信息,包括:头像、昵称、性别、个性签名。
+          <br />
+          使用信息:您可以通过相关产品页面随时访问您的使用信息,包括:收藏记录、观看历史、离线下载记录、搜索记录、上传内容、订单信息。
+          <br />
+          其他信息:如您在此访问过程中遇到操作问题的或如需获取其他前述无法获知的个人信息内容,您可通过文末提供的方式联系我们,我们将在核实您的身份后在合理期限内向您提供,但法律法规另有规定的或本政策另有约定的除外。
+          <br />
+          (二)更正/修改权
+          <br />
+          您可以在我们的产品与/或服务中更正/修改您的相关个人信息。为便于您行使您的上述权利,我们为您提供了在线自行更正/修改和向我们提出更正/修改申请两种方式。
+          <br />
+          对于您的部分个人信息,我们在产品的相关功能页面为您提供了操作指引和操作设置,您可以直接进行更正/修改,例如:“头像/昵称/性别/个性签名”信息在“手机端APP”中的更正/修改路径为:我的—设置;
+          <br />
+          对于您在行使上述权利过程中遇到的困难,或其他可能未/无法向您提供在线自行更正/修改权限的,
+          <br />
+          经对您的身份进行验证,且更正不影响信息的客观性和准确性的情况下,您有权对错误或不完整的信息作出更正或修改,或在特定情况下,尤其是数据错误时,通过我们公布的反馈与报错措施将您的更正/修改申请提交给我们,要求我们更正或修改您的数据,但法律法规另有规定的除外。但出于安全性和身份识别的考虑,您可能无法修改注册时提交的某些初始注册信息。
+          <br />
+          (三)注销权
+          <br />
+          我们为您提供账号注销的多种途径,您可以通过在线申请注销或联系我们的客服或通过其他我们公示的方式申请注销您的账号。在您注销账号后,您将无法再以此账号登录和使用
+          {this.name}
+          旗下的相关产品与服务;该账号在{this.name}
+          的产品与服务使用期间已产生的但未消耗完毕的权益及未来的预期利益全部权益将被清除;该账号下的内容、信息、数据、记录将会被删除或匿名化处理(但法律法规另有规定或监管部门另有要求的除外);同时,账号一旦注销超过一定时间,将无法恢复。
+          <br />
+          (四)提前获知产品与/或服务停止运营权
+          <br />
+          我们将持续为您提供优质服务,若因特殊原因导致我们的部分或全部产品与/或服务被迫停止运营,我们将提前在显著位置或向您发送推送消息或以其他方式通知您,并将停止对您个人信息的收集,同时在超出法律法规规定的必需且最短期限后,我们将会对所持有的您的个人信息进行删除或匿名化处理。
+          <br />
+          (五)帮助反馈权
+          <br />
+          我们为您提供了多种反馈渠道,联系客服,联系电话,帮助反馈。
+          <br />
+          我们如何存储和保护您的个人信息
+          <br />
+          (一)个人信息的存储
+          <br />
+          存储地点:我们依照法律法规的规定,将您的个人信息存储于中华人民共和国境内。目前我们不存在跨境存储您的个人信息或向境外提供个人信息的场景。如需跨境存储或向境外提供个人信息的,我们会单独向您明确告知(包括出境的目的、接收方、使用方式与范围、使用内容、安全保障措施、安全风险)并再次征得您的授权同意,并严格要求接收方按照本《隐私政策》以及法律法规相关要求来处理您的个人信息;
+          <br />
+          存储期限:我们在为提供我们的产品和服务之目的所必需且最短的期间内保留您的个人信息,例如:当您使用我们的注册登录及会员功能时,我们需要收集您的手机号码,且在您提供后并在您使用该功能期间,我们需要持续为您保留您的手机号码,以向您正常提供该功能、保障您的账号和系统安全。在超出上述存储期限后,我们会对您的个人信息进行删除或匿名化处理。但您行使删除权、注销账号的或法律法规另有规定的除外(例如:《电子商务法》规定:商品和服务信息、交易信息保存时间自交易完成之日起不少于三年)。
+          <br />
+          (二)个人信息的保护措施
+          <br />
+          我们一直
+          <br />
+          都极为重视保护用户的个人信息安全,为此我们采用了符合行业标准的安全技术措施及组织和管理措施保护措施以最大程度降低您的信息被泄露、毁损、误用、非授权访问、非授权披露和更改的风险。
+          未成年人保护
+          <br />
+          {this.name}
+          一直非常注重对未成年人的保护,致力于践行我们的企业社会责任。
+          <br />
+          {this.name}
+          的绝大部分产品与/或服务主要面向成年人提供,针对这部分产品与/或服务,我们不会主动直接向未成年人收集其个人信息,如未成年人需要使用的,应首先取得其监护人的同意(包括本政策),在监护人同意后和指导下进行使用、提交个人信息;我们希望监护人亦能积极的教育和引导未成年人增强个人信息保护意识和能力,保护未成年人个人信息安全。
+          {this.name}
+          会严格履行法律规定的未成年人保护义务与责任,我们只会在法律允许、监护人同意或保护未成年人所必要的情况下收集、使用、共享、转让或披露未成年人个人信息,如果我们发现未成年人在未事先获得其监护人同意的情况下使用了我们的产品与/或服务的,我们会尽最大努力与监护人取得联系,并在监护人要求下尽快删除相关未成年人个人信息。
+          <br />
+          本《隐私政策》的更新
+          <br />
+          我们鼓励您在每次使用我们的产品或服务时都查阅我们的《隐私政策》。为了给您提供更好的服务,我们会根据产品的更新情况及法律法规的相关要求更新本《隐私政策》的条款,该更新构成本《隐私政策》的一部分。如该更新造成您在本《隐私政策》下权利的实质减少或重大变更,我们将在本政策生效前通过在显著位置提示或向您发送推送消息或以其他方式通知您,若您继续使用我们的服务,即表示您充分阅读、理解并同意受经修订的《隐私政策》的约束。为保障您的合法权益,我们建议您可以定期在我们平台的设置页面中查看本政策。
+          <br />
+          上述的“重大变更”包括但不限于:
+          <br />
+          我们的服务模式发生重大变化。如处理个人信息的目的、处理的个人信息的类型、个人信息的使用方式;
+          <br />
+          我们在所有权结构、组织架构方面发生重大变化。如业务调整、破产并购引起的所有者变更;
+          <br />
+          个人信息共享、转让或公开披露的主要对象发生变化;
+          <br />
+          您参与个人信息处理方面的权利及其行使方式发生重大变化;
+          <br />
+          我们负责处理个人信息安全的责任部门、联络方式及投诉渠道发生变化时;
+          <br />
+          个人信息安全影响评估报告表明存在高风险时;
+          <br />
+          其他重要的或可能严重影响您的个人权益的情况发生时。
+          <br />
+          如何联系我们
+          <br />
+          (一)如您对本《隐私政策》的执行或使用我们的服务时遇到的与隐私保护相关的事宜有任何问题(包括问题咨询、投诉),我们专门为您提供了多种反馈通道,希望为您提供满意的解决方案:
+          <br />
+          在线客服/其他在线意见反馈通道:您可与我们平台上产品功能页面的在线客服联系或者在线提交意见反馈;
+          <br />
+          人工客服通道:您可以拨打我们的任何一部客服电话与我们联系(400-018-5077);
+          <br />
+          (二)我们会在收到您的意见及建议后,并在验证您的用户身份后的15个工作日内或法律法规规定的期限内尽快向您回复,一般情况下,我们不会因此对您收取服务费。但是,在以下情形下,您理解并知悉,我们将无法响应您的请求:
+          <br />
+          与我们履行法律法规规定的义务相关的; 与国家安全、国防安全直接相关的
+          <br />
+          与公共安全、公共卫生、重大公共利益直接相关的
+          <br />
+          与犯罪侦查、起诉和审判有关的;
+          <br />
+          有充分证据表明您存在主观恶意或滥用权利的;
+          <br />
+          出于维护您或其他个人的生命、财产重大权益但又难得到本人授权同意的;
+          <br />
+          响应您的请求将导致您或其他个人、组织的合法权益受到严重损害的;
+          <br />
+          涉及商业秘密的; 法律法规规定的其他情形。
+          <br />
+          其他
+          <br />
+          (一)本《隐私政策》的解释及争议解决均应适用中华人民共和国大陆地区法律。如就本政策的签订、履行发生任何争议的,双方应尽量友好协商解决;协商不成时,任何一方均可向被告住所地享有管辖权的人民法院提起诉讼。
+          <br />
+          (二)本《隐私政策》的标题仅为方便及阅读而设,并不影响正文其中任何规定的含义或解释。
+          <br />
+        </div>
+      </>
+    )
+  }
+})

+ 219 - 219
src/views/protocol/register.tsx

@@ -1,219 +1,219 @@
-import ColHeader from '@/components/col-header'
-import { browser } from '@/helpers/utils'
-import { state } from '@/state'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-
-export default defineComponent({
-  name: 'register',
-  data() {
-    const query = this.$route.query
-    return {
-      showHeader: query.showHeader || '0'
-    }
-  },
-  computed: {
-    name() {
-      return state.platformType === 'TEACHER' ? '酷乐秀学院' : '酷乐秀'
-    }
-  },
-  render() {
-    return (
-      <>
-        {this.showHeader === '1' && !browser().isApp && <ColHeader isBack />}
-        <div class={styles.container}>
-          欢迎来到酷乐秀,酷乐秀 隶属于武汉酷乐秀网络科技有限公司。我们开发并运营的程序有:酷乐秀、酷乐秀学院,以上app均符合遵守本协议。以下协议中所指的酷乐秀,代表以上程序中的任意程序。
-          <br />
-          请您仔细阅读以下条款,如果您对本协议的任何条款表示异议,您可以选择不进入酷乐秀。当您注册成功,无论是进入酷乐秀,还是在酷乐秀上发布任何内容(即「内容」),均意味着您(即「用户」)完全接受本协议项下的全部条款。
-          <br />
-          使用协议
-          <br />
-          1.用户注册成功后,酷乐秀将给予每个用户一个用户帐号及相应的密码,该用户帐号和密码由用户负责保管;用户应当对以其用户帐号进行的所有活动和事件负法律责任。
-          <br />
-          2.用户须对在酷乐秀的注册信息的真实性、合法性、有效性承担全部责任,用户不得冒充他人;不得利用他人的名义发布任何信息;不得恶意使用注册帐号导致其他用户误认;否则酷乐秀有权立即停止提供服务,收回其帐号并由用户独自承担由此而产生的一切法律责任。
-          <br />
-          3.用户直接或通过各类方式(如 RSS 源和站外
-          API引用等)间接使用酷乐秀服务和数据的行为,都将被视作已无条件接受本协议全部内容;若用户对本协议的任何条款存在异议,请停止使用酷乐秀所提供的全部服务。
-          <br />
-          4.酷乐秀是一个信息分享、传播及获取的平台,用户通过酷乐秀发表的信息为公开的信息,其他第三方均可以通过酷乐秀获取用户发表的信息,用户对任何信息的发表即认可该信息为公开的信息,并单独对此行为承担法律责任;任何用户不愿被其他第三人获知的信息都不应该在酷乐秀上进行发表。
-          <br />
-          5.为了更好地维护乐谱,酷乐秀会将认证为“达人”的用户上传的高质量乐谱设置成VIP。
-          <br />
-          6.您的上传行为代表您同意您上传的作品在本站内的公开发布与传播并授权本站使用您上传的作品。任何第三方的转载行为与本站无关。
-          <br />
-          7.酷乐秀如果认为您上传的内容不适当,有权进行删除或修改,甚至对情节严重者进行封号处理。
-          <br />
-          8.酷乐秀会员账号、曲谱、教程视频等资源均不能二次转售,否则将对实施以上行为的账号终身封号,且将通过法律途径追究其责任,追偿酷乐秀的损失。用户账号被封后,酷乐秀会提供1次人工申诉机会,请按照要求填写相关的信息,完成备案后,如符合条件可以解封;如不符合条件,我们将会通过邮件告知您。
-          <br />
-          9.酷乐秀账号当前有效5台设备上登录使用,每天不得超过其中的4台。如果违规将由系统判定,给予警告和处罚。提醒用户账号切勿共享,因共享一定会违规。
-          <br />
-          10.用户承诺不得以任何方式利用酷乐秀直接或间接从事违反中国法律、以及社会公德的行为,酷乐秀有权对违反上述承诺的内容予以删除。用户账号被封后,酷乐秀会提供1次人工申诉机会,请按照要求填写相关的信息,完成备案后,如符合条件可以解封;如不符合条件,我们将会通过邮件告知您。
-          <br />
-          11.用户不得利用酷乐秀服务制作、上载、复制、发布、传播或者转载如下内容:
-          <br />
-          11.1反对宪法所确定的基本原则的;
-          <br />
-          11.2危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;
-          <br />
-          11.3损害国家荣誉和利益的;
-          <br />
-          11.4煽动民族仇恨、民族歧视,破坏民族团结的;
-          <br />
-          11.5破坏国家宗教政策,宣扬邪教和封建迷信的;
-          <br />
-          11.6散布谣言,扰乱社会秩序,破坏社会稳定的;
-          <br />
-          11.7散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;
-          <br />
-          11.8侮辱或者诽谤他人,侵害他人合法权益的;
-          <br />
-          11.9含有法律、行政法规禁止的其他内容的信息。
-          <br />
-          11.10禁止使用国家领导人、亲属名字作为昵称,含隐晦名称等。
-          <br />
-          1.11包含以上情况的内容不得出现在:用户名、昵称、个人签名、帖子中、评论中、回复中,以及任何形式可能出现在酷乐秀的内容中。
-          <br />
-          当您在酷乐秀发表内容前,您已做过实名认证,如您的行为包含违规的内容,我们将及时向国家相关部门上报您的违规行为。
-          <br />
-          当您出现任何违规行为,我们将有权删除您的账号和相关的评论内容,并且终身不可恢复。
-          <br />
-          12.本站不是知识产权专业机构,不具备审查、判断作品是否侵权的能力,也不具备对用户所发布内容的合法性、真实性及其品质等进行审查的能力。任何用户应当为自己上传文件的行为独立完全承担法律责任。因您的个人行为所产生的一切争议和纠纷以及诉讼,与本站无关。
-          <br />
-          13.酷乐秀有权对用户使用酷乐秀的情况进行审查和监督,如用户在使用酷乐秀时违反任何上述规定,酷乐秀或其授权的人有权要求用户改正或直接采取一切必要的措施(包括但不限于更改或删除用户张贴的内容、暂停或终止用户使用酷乐秀的权利)以减轻用户不当行为造成的影响。
-          <br />
-          14.在您充值酷乐秀会员、购买课程、商城商品等服务前,请您充分确认后再购买,商品一旦售出后,除重复支付、7天内质量问题(需有证据证明)可退全款外,其他情况支持退残值,计算方法会员产品购买日起消耗3元每日,残值=实际已付-已使用;
-          <br />
-          15.购买课酬退费按支付总额扣除20%服务费后,按实际剩余课程数全额退款,计算方法举例课程组10节已完成2节,退款金额=支付总金额*80%/10*8;
-          <br />
-          16.音像制品,如视频内容,乐谱,专辑单品购买不予退款。
-          <br />
-          17.商城购买乐器产品根据乐器实际属性向商城客服协商退款方式,最终解释权归商城所有;
-          <br />
-          18.因本平台app需通过不断升级来适配苹果、安卓、微软等操作系统的升级,可能会导致较低版本的设备不再支持安装、运行,属于正常合理的情况。如果出现这种情况,表示您的硬件需要升级了。此种情况下,充值的VIP仍然可用,同时支持残值部分办理退款。(残值计算方法,见第14条)
-          <br />
-          侵权举报 <br />
-          1.处理原则
-          <br />
-          酷乐秀作为知识讨论社区,高度重视自由表达和企业正当权利的平衡。依照法律规定删除违法信息是酷乐秀社区的法定义务,酷乐秀社区亦未与任何中介机构合作开展此项业务。
-          <br />
-          2.受理范围
-          <br />
-          受理酷乐秀社区内侵犯企业或个人合法权益的侵权举报,包括但不限于涉及个人隐私、造谣与诽谤、商业侵权。
-          <br />
-          2.1涉及个人隐私:发布内容中直接涉及身份信息,如个人姓名、家庭住址、身份证号码、工作单位、私人电话等详细个人隐私;
-          <br />
-          2.2造谣、诽谤:发布内容中指名道姓(包括自然人和企业)的直接谩骂、侮辱、虚构中伤、恶意诽谤等;
-          <br />
-          2.3商业侵权:泄露企业商业机密及其他根据保密协议不能公开讨论的内容。
-          <br />
-          3.权利人
-          <br />
-          3.1权利人对涉嫌侵权内容拥有商标权、著作权或其他依法可以行使权利的权属证明;如果举报人非权利人,请举报人提供代表企业进行举报的书面授权证明。
-          <br />
-          3.2充分、明确地描述侵犯了权利人合法权益的内容,提供涉嫌侵权内容在酷乐秀上的具体页面地址,指明涉嫌侵权内容中的哪些内容侵犯了上述列明的权利人的合法权益;
-          <br />
-          3.3权利人具体的联络信息,包括姓名、身份证或护照复印件(对自然人)、单位登记证明复印件(对单位)、通信地址、电话号码、传真和电子邮件;
-          <br />
-          3.4在侵权举报中加入如下关于举报内容真实性的声明:
-          <br />
-          3.4.1我本人为所举报内容的合法权利人;
-          <br />
-          3.4.2我举报的发布在酷乐秀社区中的内容侵犯了本人相应的合法权益;
-          <br />
-          3.4.3如果本侵权举报内容不完全属实,本人将承担由此产生的一切法律责任。
-          <br />
-          4.处理流程
-          <br />
-          4.1出于网络社区的监督属性,并非所有申请都必须受理。酷乐秀自收到举报邮件七个工作日内处理完毕并给出回复。处理期间,不提供任何电话、邮件及其他方式的查询服务。
-          <br />
-          4.2出现酷乐秀已经删除或处理的内容,但是百度、谷歌等搜索引擎依然可以搜索到的现象,是因为百度、谷歌等搜索引擎自带缓存,此类问题酷乐秀无权也无法处理,因此相关申请不予受理。
-          <br />
-          4.3此为酷乐秀社区唯一的官方的侵权投诉渠道,暂不提供其他方式处理此业务。
-          <br />
-          4.4用户在酷乐秀中的商业行为引发的法律纠纷,由交易双方自行处理,与酷乐秀无关。
-          <br />
-          知识产权
-          <br />
-          1.酷乐秀是一个信息获取、分享及传播的平台,我们尊重和鼓励酷乐秀用户创作的内容,认识到保护知识产权对酷乐秀生存与发展的重要性,承诺将保护知识产权作为酷乐秀运营的基本原则之一。
-          <br />
-          2.用户在酷乐秀上发布的原创视频,帖子,发表的原创回答和评论,著作权均归用户本人所有。用户可授权第三方以任何方式使用,不需要得到酷乐秀的同意。
-          <br />
-          3.用户不得修改、改编、翻译酷乐秀服务所使用的软件、技术、材料等,或者创作与之相关的派生作品,不得通过反向工程、反编译、反汇编或其他类似行为获得其的源代码,否则由此引起的一切法律后果由用户负责,酷乐秀将依法追究违约方的法律责任。
-          <br />
-          4.用户不得恶意修改、复制、传播酷乐秀服务所使用的软件、技术、材料等。否则,用户自行承担因此而造成对其他人的损害,或者造成对酷乐秀公司形象损害,要承担相应的法律责任。
-          <br />
-          5.用户不得擅自删除、掩盖或更改酷乐秀的版权声明、商标或其它权利声明。酷乐秀平台所有设计图样以及其他图样、产品及服务名称,均为酷乐秀所享有的商标、标识。任何人不得使用、复制或用作其他用途。
-          <br />
-          6.为了促进知识的分享和传播,用户将其在酷乐秀上发表的全部内容,授予酷乐秀免费的、不可撤销的、非精编使用许可,酷乐秀有权将该内容用于酷乐秀各种形态的产品和服务上,包括但不限于网站以及发表的应用或其他互联网产品。
-          <br />
-          7.在酷乐秀上传或发表的内容,用户应保证其为著作权人或已取得合法授权,并且该内容不会侵犯任何第三方的合法权益。如果第三方提出关于著作权的异议,酷乐秀有权根据实际情况删除相关的内容有权追究用户的法律责任,给酷乐秀或任何第三方造成损失的,用户应负责全额赔偿。
-          <br />
-          8.如果任何第三方侵犯了酷乐秀用户相关的权利,用户同意授权酷乐秀或其指定的代理人代表酷乐秀自身或用户对该第三方提出警告、投诉、发起行政执法、诉讼、进行上诉,或谈判和解,并且用户同意在酷乐秀认为必要的情况下参与共同维权。
-          <br />
-          9.酷乐秀有权但无义务对用户发布的内容进行审核,有权根据相关证据结合《侵权责任法》、《信息网络传播权保护条例》等法律法规及酷乐秀社区指导原则对侵权信息进行处理。
-          <br />
-          10.酷乐秀对其自制内容和其他通过授权取得的独占内容享有完全知识产权,未经酷乐秀许可,任何单位和个人不得私自转载、传播和提供观看服务或者有其他侵犯酷乐秀知识产权的行为。否则,酷乐秀将追究侵权行为人的法律责任。
-          <br />
-          酷乐秀所有和享有的知识产权,不因用户的任何使用行为而发生权利转移。
-          <br />
-          免责申明
-          <br />
-          1.酷乐秀对于任何包含、经由或连接、下载或从任何与有关本网络服务所获得的任何内容、信息不声明或保证其正确性或可靠性;
-          <br />
-          2.用户在酷乐秀发表的内容仅表明其个人的立场和观点,并不代表酷乐秀的立场或观点。作为内容的发表者,需自行对所发表内容负责,因所发表内容引发的一切纠纷,由该内容的发表者承担全部法律及连带责任。酷乐秀不承担任何法律及连带责任。
-          <br />
-          3.酷乐秀对如下事项不做担保(包括但不限于):
-          <br />
-          3.1酷乐秀提供的网站、客户端等软件虽然均已经过酷乐秀测试,但由于技术本身的局限性,酷乐秀不能保证其与其他软硬件、系统完全兼容。如果出现不兼容的情况,用户可将情况报告酷乐秀,以获得技术支持。如果无法解决问题,用户可以选择卸载、停止使用酷乐秀服务。
-          <br />
-          3.2用酷乐秀服务涉及到Internet服务,可能会受到各个环节不稳定因素的影响。因不可抗力、黑客攻击、系统不稳定、网络中断、用户关机、通信线路等原因,均可能造成酷乐秀服务中断或不能满足用户要求的情况。酷乐秀不保证酷乐秀服务适合用户的使用要求。
-          <br />
-          3.3由于酷乐秀提供的客户端等软件可以通过网络途径下载、传播,因此对于从非酷乐秀指定官方站点下载、非酷乐秀指定途径获得的酷乐秀服务相关软件,酷乐秀无法保证其是否感染计算机病毒、是否隐藏有伪装的木马程序等黑客软件,也不承担用户由此遭受的一切直接或间接损害赔偿等法律责任。
-          <br />
-          3.4酷乐秀不做任何与酷乐秀服务、产品的安全性、可靠性、及时性和性能有关的担保。
-          <br />
-          3.5酷乐秀不保证其提供的任何产品、服务或其他材料符合用户的期望。
-          <br />
-          4.用户使用经由酷乐秀服务下载或取得的任何资料,其风险由用户自行负担,因该使用而导致用户电脑系统损坏或资料流失,用户应负完全责任
-          <br />
-          5.基于以下原因而造成的利润、商业信誉、资料损失或其他有形或无形损失,酷乐秀不承担任何直接、间接、附带、衍生或惩罚性的赔偿:
-          <br />
-          5.1酷乐秀服务全部或部分无法使用;
-          <br />
-          5.2经由酷乐秀服务购买或取得的任何产品、资料或服务;
-          <br />
-          5.3用户资料遭到未授权的使用或修改;
-          <br /> 5.4其他与酷乐秀服务相关的事宜
-          <br />
-          6.用户应妥善保管自己的账号和密码,加强密码安全性,谨防账号泄露或被盗。因用户账号被泄露或被盗而造成的任何损失,酷乐秀不承担补偿责任。用户因电信和网通部门的通讯线路故障、网络或电脑故障、系统不稳定、不可抗力(如服务器宕机)等非酷乐秀原因造成账号、账号内财产等丢失、减少的,酷乐秀不承担补偿等责任。
-          <br />
-          7.用户理解并同意自主选择免费下载和使用酷乐秀服务,风险自负,包括但不限于用户使用酷乐秀服务过程中的行为,以及因使用酷乐秀服务产生的一切后果。如因下载或使用酷乐秀服务而对计算机系统造成的损坏或数据的丢失等,用户须自行承担全部责任。
-          <br />
-          8.本站所有乐谱均为用户自行制作和上传,其所上传数字内容的版权问题,由上传者自行负责,本站不对用户上传的数字内容承担任何责任。因版权原因导致乐谱下架,本站不承担因此带来的任何责任,不支持以版权下架原因导致不能看谱、下载曲谱而申请退费。
-          <br />
-          9.本站十分重视网络版权及其他知识产权以及用户权益的保护,如果您发现有用户上传的内容侵犯了您相关权益,请即以电话(4008851569)或通过app内即时通讯功能联系客服的方式告知本站,并提供以下材料:1)、著作权人的身份证明,包括身份证、法人执照、营业执照等有效身份证件;2)、著作权权属证明,如著作权登记证书等;3)、侵权情况证明,包括被控侵权信息的内容等。本站工作人员会在收到相关材料并经相关机构核实后,在一个工作日内将所涉及的侵权内容删除。
-          <br />
-          10.如果您认为酷乐秀侵害了您的相关权益,同意自愿选择武汉仲裁委员会进行仲裁以解决争议。
-          <br />
-          隐私政策
-          <br />
-          1.酷乐秀注重保护用户的个人信息及个人隐私。个人信息是指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。您在下载、安装、启动、浏览、注册、登录、使用酷乐秀的产品与/或服务时,酷乐秀将按照平台公布的《酷乐秀隐私政策》的约定处理和保护您的个人信息,因此希望您能够仔细阅读、充分理解《酷乐秀隐私政策》的全文,并在需要时,按照《酷乐秀隐私政策》的指引,作出您认为适当的选择。
-          <br />
-          2.您应当在仔细阅读、充分理解《酷乐秀隐私政策》后使用酷乐秀的产品与/或服务,如果您不同意政策的内容,将可能导致酷乐秀的产品与/或服务无法正常运行,或者无法达到酷乐秀拟达到的服务效果。您使用或继续使用酷乐秀提供的产品与/或服务的行为,都表示您充分理解和同意《酷乐秀隐私政策》(包括更新版本)的全部内容。
-          <br />
-          如您对《酷乐秀隐私政策》或对您的个人信息相关内容有任何问题(包括问题咨询、投诉等),请即以电话(4008851569)或通过app内即时通讯功能联系客服的方式告知本站。
-          <br />
-          协议修改
-          <br />
-          1.根据互联网的发展和有关法律、法规及规范性文件的变化,或者因业务发展需要,酷乐秀有权对本协议的条款作出修改或变更,一旦本协议的内容发生变动,酷乐秀将会直接在酷乐秀网站上公布修改之后的协议内容,该公布行为视为酷乐秀已经通知用户修改内容。酷乐秀也可采用通知的传送方式,提示用户协议条款的修改、服务变更、或其它重要事项。
-          <br />
-          2.如果不同意酷乐秀对本协议相关条款所做的修改,用户有权并应当停止使用酷乐秀。如果用户继续使用酷乐秀,则视为用户接受酷乐秀对本协议相关条款所做的修改。
-          <br />
-          上次版本更新时间:2022年4月18日
-          <br /> 本次版本更新时间:2022年7月13日
-          <br />
-        </div>
-      </>
-    )
-  }
-})
+import ColHeader from '@/components/col-header'
+import { browser } from '@/helpers/utils'
+import { state } from '@/state'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'register',
+  data() {
+    const query = this.$route.query
+    return {
+      showHeader: query.showHeader || '0'
+    }
+  },
+  computed: {
+    name() {
+      return state.platformType === 'TEACHER' ? '酷乐秀学院' : '酷乐秀'
+    }
+  },
+  render() {
+    return (
+      <>
+        <ColHeader />
+        <div class={styles.container}>
+          欢迎来到酷乐秀,酷乐秀 隶属于武汉酷乐秀网络科技有限公司。我们开发并运营的程序有:酷乐秀、酷乐秀学院,以上app均符合遵守本协议。以下协议中所指的酷乐秀,代表以上程序中的任意程序。
+          <br />
+          请您仔细阅读以下条款,如果您对本协议的任何条款表示异议,您可以选择不进入酷乐秀。当您注册成功,无论是进入酷乐秀,还是在酷乐秀上发布任何内容(即「内容」),均意味着您(即「用户」)完全接受本协议项下的全部条款。
+          <br />
+          使用协议
+          <br />
+          1.用户注册成功后,酷乐秀将给予每个用户一个用户帐号及相应的密码,该用户帐号和密码由用户负责保管;用户应当对以其用户帐号进行的所有活动和事件负法律责任。
+          <br />
+          2.用户须对在酷乐秀的注册信息的真实性、合法性、有效性承担全部责任,用户不得冒充他人;不得利用他人的名义发布任何信息;不得恶意使用注册帐号导致其他用户误认;否则酷乐秀有权立即停止提供服务,收回其帐号并由用户独自承担由此而产生的一切法律责任。
+          <br />
+          3.用户直接或通过各类方式(如 RSS 源和站外
+          API引用等)间接使用酷乐秀服务和数据的行为,都将被视作已无条件接受本协议全部内容;若用户对本协议的任何条款存在异议,请停止使用酷乐秀所提供的全部服务。
+          <br />
+          4.酷乐秀是一个信息分享、传播及获取的平台,用户通过酷乐秀发表的信息为公开的信息,其他第三方均可以通过酷乐秀获取用户发表的信息,用户对任何信息的发表即认可该信息为公开的信息,并单独对此行为承担法律责任;任何用户不愿被其他第三人获知的信息都不应该在酷乐秀上进行发表。
+          <br />
+          5.为了更好地维护乐谱,酷乐秀会将认证为“达人”的用户上传的高质量乐谱设置成VIP。
+          <br />
+          6.您的上传行为代表您同意您上传的作品在本站内的公开发布与传播并授权本站使用您上传的作品。任何第三方的转载行为与本站无关。
+          <br />
+          7.酷乐秀如果认为您上传的内容不适当,有权进行删除或修改,甚至对情节严重者进行封号处理。
+          <br />
+          8.酷乐秀会员账号、曲谱、教程视频等资源均不能二次转售,否则将对实施以上行为的账号终身封号,且将通过法律途径追究其责任,追偿酷乐秀的损失。用户账号被封后,酷乐秀会提供1次人工申诉机会,请按照要求填写相关的信息,完成备案后,如符合条件可以解封;如不符合条件,我们将会通过邮件告知您。
+          <br />
+          9.酷乐秀账号当前有效5台设备上登录使用,每天不得超过其中的4台。如果违规将由系统判定,给予警告和处罚。提醒用户账号切勿共享,因共享一定会违规。
+          <br />
+          10.用户承诺不得以任何方式利用酷乐秀直接或间接从事违反中国法律、以及社会公德的行为,酷乐秀有权对违反上述承诺的内容予以删除。用户账号被封后,酷乐秀会提供1次人工申诉机会,请按照要求填写相关的信息,完成备案后,如符合条件可以解封;如不符合条件,我们将会通过邮件告知您。
+          <br />
+          11.用户不得利用酷乐秀服务制作、上载、复制、发布、传播或者转载如下内容:
+          <br />
+          11.1反对宪法所确定的基本原则的;
+          <br />
+          11.2危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;
+          <br />
+          11.3损害国家荣誉和利益的;
+          <br />
+          11.4煽动民族仇恨、民族歧视,破坏民族团结的;
+          <br />
+          11.5破坏国家宗教政策,宣扬邪教和封建迷信的;
+          <br />
+          11.6散布谣言,扰乱社会秩序,破坏社会稳定的;
+          <br />
+          11.7散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;
+          <br />
+          11.8侮辱或者诽谤他人,侵害他人合法权益的;
+          <br />
+          11.9含有法律、行政法规禁止的其他内容的信息。
+          <br />
+          11.10禁止使用国家领导人、亲属名字作为昵称,含隐晦名称等。
+          <br />
+          1.11包含以上情况的内容不得出现在:用户名、昵称、个人签名、帖子中、评论中、回复中,以及任何形式可能出现在酷乐秀的内容中。
+          <br />
+          当您在酷乐秀发表内容前,您已做过实名认证,如您的行为包含违规的内容,我们将及时向国家相关部门上报您的违规行为。
+          <br />
+          当您出现任何违规行为,我们将有权删除您的账号和相关的评论内容,并且终身不可恢复。
+          <br />
+          12.本站不是知识产权专业机构,不具备审查、判断作品是否侵权的能力,也不具备对用户所发布内容的合法性、真实性及其品质等进行审查的能力。任何用户应当为自己上传文件的行为独立完全承担法律责任。因您的个人行为所产生的一切争议和纠纷以及诉讼,与本站无关。
+          <br />
+          13.酷乐秀有权对用户使用酷乐秀的情况进行审查和监督,如用户在使用酷乐秀时违反任何上述规定,酷乐秀或其授权的人有权要求用户改正或直接采取一切必要的措施(包括但不限于更改或删除用户张贴的内容、暂停或终止用户使用酷乐秀的权利)以减轻用户不当行为造成的影响。
+          <br />
+          14.在您充值酷乐秀会员、购买课程、商城商品等服务前,请您充分确认后再购买,商品一旦售出后,除重复支付、7天内质量问题(需有证据证明)可退全款外,其他情况支持退残值,计算方法会员产品购买日起消耗3元每日,残值=实际已付-已使用;
+          <br />
+          15.购买课酬退费按支付总额扣除20%服务费后,按实际剩余课程数全额退款,计算方法举例课程组10节已完成2节,退款金额=支付总金额*80%/10*8;
+          <br />
+          16.音像制品,如视频内容,乐谱,专辑单品购买不予退款。
+          <br />
+          17.商城购买乐器产品根据乐器实际属性向商城客服协商退款方式,最终解释权归商城所有;
+          <br />
+          18.因本平台app需通过不断升级来适配苹果、安卓、微软等操作系统的升级,可能会导致较低版本的设备不再支持安装、运行,属于正常合理的情况。如果出现这种情况,表示您的硬件需要升级了。此种情况下,充值的VIP仍然可用,同时支持残值部分办理退款。(残值计算方法,见第14条)
+          <br />
+          侵权举报 <br />
+          1.处理原则
+          <br />
+          酷乐秀作为知识讨论社区,高度重视自由表达和企业正当权利的平衡。依照法律规定删除违法信息是酷乐秀社区的法定义务,酷乐秀社区亦未与任何中介机构合作开展此项业务。
+          <br />
+          2.受理范围
+          <br />
+          受理酷乐秀社区内侵犯企业或个人合法权益的侵权举报,包括但不限于涉及个人隐私、造谣与诽谤、商业侵权。
+          <br />
+          2.1涉及个人隐私:发布内容中直接涉及身份信息,如个人姓名、家庭住址、身份证号码、工作单位、私人电话等详细个人隐私;
+          <br />
+          2.2造谣、诽谤:发布内容中指名道姓(包括自然人和企业)的直接谩骂、侮辱、虚构中伤、恶意诽谤等;
+          <br />
+          2.3商业侵权:泄露企业商业机密及其他根据保密协议不能公开讨论的内容。
+          <br />
+          3.权利人
+          <br />
+          3.1权利人对涉嫌侵权内容拥有商标权、著作权或其他依法可以行使权利的权属证明;如果举报人非权利人,请举报人提供代表企业进行举报的书面授权证明。
+          <br />
+          3.2充分、明确地描述侵犯了权利人合法权益的内容,提供涉嫌侵权内容在酷乐秀上的具体页面地址,指明涉嫌侵权内容中的哪些内容侵犯了上述列明的权利人的合法权益;
+          <br />
+          3.3权利人具体的联络信息,包括姓名、身份证或护照复印件(对自然人)、单位登记证明复印件(对单位)、通信地址、电话号码、传真和电子邮件;
+          <br />
+          3.4在侵权举报中加入如下关于举报内容真实性的声明:
+          <br />
+          3.4.1我本人为所举报内容的合法权利人;
+          <br />
+          3.4.2我举报的发布在酷乐秀社区中的内容侵犯了本人相应的合法权益;
+          <br />
+          3.4.3如果本侵权举报内容不完全属实,本人将承担由此产生的一切法律责任。
+          <br />
+          4.处理流程
+          <br />
+          4.1出于网络社区的监督属性,并非所有申请都必须受理。酷乐秀自收到举报邮件七个工作日内处理完毕并给出回复。处理期间,不提供任何电话、邮件及其他方式的查询服务。
+          <br />
+          4.2出现酷乐秀已经删除或处理的内容,但是百度、谷歌等搜索引擎依然可以搜索到的现象,是因为百度、谷歌等搜索引擎自带缓存,此类问题酷乐秀无权也无法处理,因此相关申请不予受理。
+          <br />
+          4.3此为酷乐秀社区唯一的官方的侵权投诉渠道,暂不提供其他方式处理此业务。
+          <br />
+          4.4用户在酷乐秀中的商业行为引发的法律纠纷,由交易双方自行处理,与酷乐秀无关。
+          <br />
+          知识产权
+          <br />
+          1.酷乐秀是一个信息获取、分享及传播的平台,我们尊重和鼓励酷乐秀用户创作的内容,认识到保护知识产权对酷乐秀生存与发展的重要性,承诺将保护知识产权作为酷乐秀运营的基本原则之一。
+          <br />
+          2.用户在酷乐秀上发布的原创视频,帖子,发表的原创回答和评论,著作权均归用户本人所有。用户可授权第三方以任何方式使用,不需要得到酷乐秀的同意。
+          <br />
+          3.用户不得修改、改编、翻译酷乐秀服务所使用的软件、技术、材料等,或者创作与之相关的派生作品,不得通过反向工程、反编译、反汇编或其他类似行为获得其的源代码,否则由此引起的一切法律后果由用户负责,酷乐秀将依法追究违约方的法律责任。
+          <br />
+          4.用户不得恶意修改、复制、传播酷乐秀服务所使用的软件、技术、材料等。否则,用户自行承担因此而造成对其他人的损害,或者造成对酷乐秀公司形象损害,要承担相应的法律责任。
+          <br />
+          5.用户不得擅自删除、掩盖或更改酷乐秀的版权声明、商标或其它权利声明。酷乐秀平台所有设计图样以及其他图样、产品及服务名称,均为酷乐秀所享有的商标、标识。任何人不得使用、复制或用作其他用途。
+          <br />
+          6.为了促进知识的分享和传播,用户将其在酷乐秀上发表的全部内容,授予酷乐秀免费的、不可撤销的、非精编使用许可,酷乐秀有权将该内容用于酷乐秀各种形态的产品和服务上,包括但不限于网站以及发表的应用或其他互联网产品。
+          <br />
+          7.在酷乐秀上传或发表的内容,用户应保证其为著作权人或已取得合法授权,并且该内容不会侵犯任何第三方的合法权益。如果第三方提出关于著作权的异议,酷乐秀有权根据实际情况删除相关的内容有权追究用户的法律责任,给酷乐秀或任何第三方造成损失的,用户应负责全额赔偿。
+          <br />
+          8.如果任何第三方侵犯了酷乐秀用户相关的权利,用户同意授权酷乐秀或其指定的代理人代表酷乐秀自身或用户对该第三方提出警告、投诉、发起行政执法、诉讼、进行上诉,或谈判和解,并且用户同意在酷乐秀认为必要的情况下参与共同维权。
+          <br />
+          9.酷乐秀有权但无义务对用户发布的内容进行审核,有权根据相关证据结合《侵权责任法》、《信息网络传播权保护条例》等法律法规及酷乐秀社区指导原则对侵权信息进行处理。
+          <br />
+          10.酷乐秀对其自制内容和其他通过授权取得的独占内容享有完全知识产权,未经酷乐秀许可,任何单位和个人不得私自转载、传播和提供观看服务或者有其他侵犯酷乐秀知识产权的行为。否则,酷乐秀将追究侵权行为人的法律责任。
+          <br />
+          酷乐秀所有和享有的知识产权,不因用户的任何使用行为而发生权利转移。
+          <br />
+          免责申明
+          <br />
+          1.酷乐秀对于任何包含、经由或连接、下载或从任何与有关本网络服务所获得的任何内容、信息不声明或保证其正确性或可靠性;
+          <br />
+          2.用户在酷乐秀发表的内容仅表明其个人的立场和观点,并不代表酷乐秀的立场或观点。作为内容的发表者,需自行对所发表内容负责,因所发表内容引发的一切纠纷,由该内容的发表者承担全部法律及连带责任。酷乐秀不承担任何法律及连带责任。
+          <br />
+          3.酷乐秀对如下事项不做担保(包括但不限于):
+          <br />
+          3.1酷乐秀提供的网站、客户端等软件虽然均已经过酷乐秀测试,但由于技术本身的局限性,酷乐秀不能保证其与其他软硬件、系统完全兼容。如果出现不兼容的情况,用户可将情况报告酷乐秀,以获得技术支持。如果无法解决问题,用户可以选择卸载、停止使用酷乐秀服务。
+          <br />
+          3.2用酷乐秀服务涉及到Internet服务,可能会受到各个环节不稳定因素的影响。因不可抗力、黑客攻击、系统不稳定、网络中断、用户关机、通信线路等原因,均可能造成酷乐秀服务中断或不能满足用户要求的情况。酷乐秀不保证酷乐秀服务适合用户的使用要求。
+          <br />
+          3.3由于酷乐秀提供的客户端等软件可以通过网络途径下载、传播,因此对于从非酷乐秀指定官方站点下载、非酷乐秀指定途径获得的酷乐秀服务相关软件,酷乐秀无法保证其是否感染计算机病毒、是否隐藏有伪装的木马程序等黑客软件,也不承担用户由此遭受的一切直接或间接损害赔偿等法律责任。
+          <br />
+          3.4酷乐秀不做任何与酷乐秀服务、产品的安全性、可靠性、及时性和性能有关的担保。
+          <br />
+          3.5酷乐秀不保证其提供的任何产品、服务或其他材料符合用户的期望。
+          <br />
+          4.用户使用经由酷乐秀服务下载或取得的任何资料,其风险由用户自行负担,因该使用而导致用户电脑系统损坏或资料流失,用户应负完全责任
+          <br />
+          5.基于以下原因而造成的利润、商业信誉、资料损失或其他有形或无形损失,酷乐秀不承担任何直接、间接、附带、衍生或惩罚性的赔偿:
+          <br />
+          5.1酷乐秀服务全部或部分无法使用;
+          <br />
+          5.2经由酷乐秀服务购买或取得的任何产品、资料或服务;
+          <br />
+          5.3用户资料遭到未授权的使用或修改;
+          <br /> 5.4其他与酷乐秀服务相关的事宜
+          <br />
+          6.用户应妥善保管自己的账号和密码,加强密码安全性,谨防账号泄露或被盗。因用户账号被泄露或被盗而造成的任何损失,酷乐秀不承担补偿责任。用户因电信和网通部门的通讯线路故障、网络或电脑故障、系统不稳定、不可抗力(如服务器宕机)等非酷乐秀原因造成账号、账号内财产等丢失、减少的,酷乐秀不承担补偿等责任。
+          <br />
+          7.用户理解并同意自主选择免费下载和使用酷乐秀服务,风险自负,包括但不限于用户使用酷乐秀服务过程中的行为,以及因使用酷乐秀服务产生的一切后果。如因下载或使用酷乐秀服务而对计算机系统造成的损坏或数据的丢失等,用户须自行承担全部责任。
+          <br />
+          8.本站所有乐谱均为用户自行制作和上传,其所上传数字内容的版权问题,由上传者自行负责,本站不对用户上传的数字内容承担任何责任。因版权原因导致乐谱下架,本站不承担因此带来的任何责任,不支持以版权下架原因导致不能看谱、下载曲谱而申请退费。
+          <br />
+          9.本站十分重视网络版权及其他知识产权以及用户权益的保护,如果您发现有用户上传的内容侵犯了您相关权益,请即以电话(4008851569)或通过app内即时通讯功能联系客服的方式告知本站,并提供以下材料:1)、著作权人的身份证明,包括身份证、法人执照、营业执照等有效身份证件;2)、著作权权属证明,如著作权登记证书等;3)、侵权情况证明,包括被控侵权信息的内容等。本站工作人员会在收到相关材料并经相关机构核实后,在一个工作日内将所涉及的侵权内容删除。
+          <br />
+          10.如果您认为酷乐秀侵害了您的相关权益,同意自愿选择武汉仲裁委员会进行仲裁以解决争议。
+          <br />
+          隐私政策
+          <br />
+          1.酷乐秀注重保护用户的个人信息及个人隐私。个人信息是指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。您在下载、安装、启动、浏览、注册、登录、使用酷乐秀的产品与/或服务时,酷乐秀将按照平台公布的《酷乐秀隐私政策》的约定处理和保护您的个人信息,因此希望您能够仔细阅读、充分理解《酷乐秀隐私政策》的全文,并在需要时,按照《酷乐秀隐私政策》的指引,作出您认为适当的选择。
+          <br />
+          2.您应当在仔细阅读、充分理解《酷乐秀隐私政策》后使用酷乐秀的产品与/或服务,如果您不同意政策的内容,将可能导致酷乐秀的产品与/或服务无法正常运行,或者无法达到酷乐秀拟达到的服务效果。您使用或继续使用酷乐秀提供的产品与/或服务的行为,都表示您充分理解和同意《酷乐秀隐私政策》(包括更新版本)的全部内容。
+          <br />
+          如您对《酷乐秀隐私政策》或对您的个人信息相关内容有任何问题(包括问题咨询、投诉等),请即以电话(4008851569)或通过app内即时通讯功能联系客服的方式告知本站。
+          <br />
+          协议修改
+          <br />
+          1.根据互联网的发展和有关法律、法规及规范性文件的变化,或者因业务发展需要,酷乐秀有权对本协议的条款作出修改或变更,一旦本协议的内容发生变动,酷乐秀将会直接在酷乐秀网站上公布修改之后的协议内容,该公布行为视为酷乐秀已经通知用户修改内容。酷乐秀也可采用通知的传送方式,提示用户协议条款的修改、服务变更、或其它重要事项。
+          <br />
+          2.如果不同意酷乐秀对本协议相关条款所做的修改,用户有权并应当停止使用酷乐秀。如果用户继续使用酷乐秀,则视为用户接受酷乐秀对本协议相关条款所做的修改。
+          <br />
+          上次版本更新时间:2022年4月18日
+          <br /> 本次版本更新时间:2022年7月13日
+          <br />
+        </div>
+      </>
+    )
+  }
+})

+ 2 - 0
src/views/scan-login/index.tsx

@@ -4,6 +4,7 @@ import iconPc from './images/icon_pc.png'
 import { Button } from 'vant'
 import { postMessage } from '@/helpers/native-message'
 import request from '@/helpers/request'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'ScanLogin',
@@ -43,6 +44,7 @@ export default defineComponent({
   render() {
     return (
       <div class={styles.scanLogin}>
+        <ColHeader />
         <img src={iconPc} class={styles.iconPc} />
 
         <div class={styles.sure}>酷乐秀登录确认</div>

+ 2 - 0
src/views/share-page/track-review-activity/subject-song.tsx

@@ -2,6 +2,7 @@ import { defineComponent } from 'vue'
 import styles from './index.module.less'
 import { Cell, CellGroup, Picker, Popup, Sticky, Image } from 'vant'
 import request from '@/helpers/request'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'subject-song',
@@ -65,6 +66,7 @@ export default defineComponent({
     return (
       <div class={styles.subjectSong}>
         <Sticky position="top">
+          <ColHeader />
           <Cell
             class={styles.subjectSearch}
             title={`${this.subjectName} 声部`}

+ 19 - 9
src/views/shop-mall/goods-list/index.tsx

@@ -7,6 +7,7 @@ import GoodsFilterList from '../modal/goods-filter-list'
 import ColSearch from '@/components/col-search'
 import request from '@/helpers/request'
 import { useEventTracking } from '@/helpers/hooks'
+import ColHeader from '@/components/col-header'
 export default defineComponent({
   name: 'goods-list',
   data() {
@@ -39,7 +40,7 @@ export default defineComponent({
     getProductAttributeCategory() {
       if (this.productCategory.id) {
         if (this.productCategory.children) {
-          let child = (this.productCategory.children[
+          const child = (this.productCategory.children[
             this.productCategory.active
           ] || {}) as any
           return child.id || ''
@@ -52,7 +53,7 @@ export default defineComponent({
   async mounted() {
     this.$nextTick(() => {
       if (this.$route.query.input === 'focus') {
-        let input: HTMLInputElement = document.querySelector(
+        const input: HTMLInputElement = document.querySelector(
           '.van-field__control'
         ) as HTMLInputElement
         input.focus()
@@ -108,7 +109,7 @@ export default defineComponent({
     },
 
     onToggleTag() {
-      let tabList = this.$refs.tabList as any
+      const tabList = this.$refs.tabList as any
       if (tabList.loading) return
       // this.productCategory.active = i
       this.$nextTick(() => {
@@ -118,7 +119,7 @@ export default defineComponent({
 
     onClearTag(key: string) {
       this[key] = { id: 0, name: '' }
-      let goodsFilter = this.$refs.goodsFilter as any
+      const goodsFilter = this.$refs.goodsFilter as any
       if (goodsFilter) {
         if (key === 'productCategory') {
           goodsFilter.setParams('productCategorySmallVoList')
@@ -133,7 +134,7 @@ export default defineComponent({
       })
     },
     onSearch() {
-      let tabList = this.$refs.tabList as any
+      const tabList = this.$refs.tabList as any
       tabList.onSearch()
     },
     searchBtn(keyword?: string) {
@@ -148,6 +149,7 @@ export default defineComponent({
     return (
       <div>
         <Sticky>
+          <ColHeader border={false} />
           <ColSearch modelValue={this.keyword} onSearch={this.searchBtn} />
           <Row class={styles['filter-top']} align="center">
             <Col
@@ -193,7 +195,7 @@ export default defineComponent({
               销量
             </Col> */}
             <Col
-              style={{marginLeft: 'auto'}}
+              style={{ marginLeft: 'auto' }}
               span={6}
               class={styles.filterBtn}
               onClick={() => (this.filterListShow = true)}
@@ -203,9 +205,17 @@ export default defineComponent({
             </Col>
           </Row>
 
-          {this.productCategory.id && this.productCategory.children && this.productCategory.children.length ? (
-            <Tabs class={styles['filter-productCategory']} shrink ellipsis={false} v-model:active={this.productCategory.active} onChange={() => this.onToggleTag()}>
-              {this.productCategory && 
+          {this.productCategory.id &&
+          this.productCategory.children &&
+          this.productCategory.children.length ? (
+            <Tabs
+              class={styles['filter-productCategory']}
+              shrink
+              ellipsis={false}
+              v-model:active={this.productCategory.active}
+              onChange={() => this.onToggleTag()}
+            >
+              {this.productCategory &&
                 this.productCategory.children.map((n: any, i: number) => (
                   <Tab
                     name={i}

+ 2 - 0
src/views/shop-mall/shop-order-detail/index.tsx

@@ -16,6 +16,7 @@ import styles from './index.module.less'
 import { orderState } from '@/views/shop-mall/shop-mall'
 import { dateFormat, moneyFormat } from '@/helpers/utils'
 import ColSticky from '@/components/col-sticky'
+import ColHeader from '@/components/col-header'
 
 export default defineComponent({
   name: 'shop-order-detail',
@@ -139,6 +140,7 @@ export default defineComponent({
 
     return () => (
       <>
+        <ColHeader />
         {items.value.length ? (
           <>
             <div class={styles.shopOrderDetail}>