浏览代码

添加功能

1、上传文件直接上传去金山云
2、上传调用原生api添加桶名称
3、智能陪练曲目购买
4、智能陪练帮助中心
5、智能陪练投屏引导
6、开通直播提示逻辑修改
7、处理视屏课详情老师头像变型
lex 3 年之前
父节点
当前提交
2b4d3d32bb
共有 39 个文件被更改,包括 817 次插入117 次删除
  1. 1 0
      src/business-components/user-detail/index.tsx
  2. 62 6
      src/components/col-upload-video/index.tsx
  3. 67 16
      src/components/col-upload/index.tsx
  4. 50 42
      src/router/routes-common.ts
  5. 1 0
      src/state.ts
  6. 1 0
      src/teacher/live-class/create-components/course-start.tsx
  7. 40 28
      src/teacher/music/upload/index.tsx
  8. 1 1
      src/teacher/open-live/index.tsx
  9. 12 2
      src/teacher/piano-room/index.tsx
  10. 2 0
      src/teacher/video-class/class-content.tsx
  11. 1 0
      src/teacher/video-class/class-info.tsx
  12. 78 0
      src/views/article-center/components/andoird-guide.module.less
  13. 190 0
      src/views/article-center/components/android-guide.tsx
  14. 84 0
      src/views/article-center/components/ios-guide.module.less
  15. 153 0
      src/views/article-center/components/ios-guide.tsx
  16. 0 0
      src/views/article-center/guide.module.less
  17. 25 0
      src/views/article-center/guide.tsx
  18. 1 1
      src/views/article-center/help-center.tsx
  19. 二进制
      src/views/article-center/images/1.png
  20. 二进制
      src/views/article-center/images/10.png
  21. 二进制
      src/views/article-center/images/11.png
  22. 二进制
      src/views/article-center/images/12.png
  23. 二进制
      src/views/article-center/images/13.png
  24. 二进制
      src/views/article-center/images/14.png
  25. 二进制
      src/views/article-center/images/2.png
  26. 二进制
      src/views/article-center/images/3.png
  27. 二进制
      src/views/article-center/images/4.png
  28. 二进制
      src/views/article-center/images/5.png
  29. 二进制
      src/views/article-center/images/6.png
  30. 二进制
      src/views/article-center/images/7.png
  31. 二进制
      src/views/article-center/images/8.png
  32. 二进制
      src/views/article-center/images/9.png
  33. 二进制
      src/views/article-center/images/guide.png
  34. 4 0
      src/views/article-center/theory.module.less
  35. 5 2
      src/views/article-center/theory.tsx
  36. 11 12
      src/views/cart/components/address/index.tsx
  37. 21 1
      src/views/order-detail/index.tsx
  38. 6 6
      src/views/order-detail/orderStatus.ts
  39. 1 0
      vite.config.ts

+ 1 - 0
src/business-components/user-detail/index.tsx

@@ -86,6 +86,7 @@ export default defineComponent({
                 <Image
                   class={styles.avatar}
                   src={this.userInfo.headUrl || defaultIcon}
+                  fit="cover"
                 />
               ),
               title: () => (

+ 62 - 6
src/components/col-upload-video/index.tsx

@@ -4,10 +4,11 @@ import { defineComponent } from 'vue'
 import styles from './index.module.less'
 import { useCustomFieldValue } from '@vant/use'
 import { browser } from '@/helpers/utils'
-
+import umiRequest from 'umi-request'
 import iconUploader from '@common/images/icon_uploader_video.png'
 import iconUploadPoster from '@common/images/icon_upload_poster.png'
 import { postMessage } from '@/helpers/native-message'
+import { state } from '@/state'
 
 export default defineComponent({
   name: 'ColUploadVideo',
@@ -30,12 +31,16 @@ export default defineComponent({
     deletable: {
       type: Boolean,
       default: true
+    },
+    bucket: {
+      type: String,
+      default: 'daya'
     }
   },
   methods: {
     beforeRead(file: any) {
       const isLt2M = file.size / 1024 / 1024 < this.size
-      console.log(this.size)
+      // console.log(this.size)
       if (!isLt2M) {
         Toast(`上传视频大小不能超过 ${this.size}MB`)
         return false
@@ -50,15 +55,66 @@ export default defineComponent({
       try {
         file.status = 'uploading'
         file.message = '上传中...'
+        // 获取签名
+        const signUrl =
+          state.platformType === 'TEACHER'
+            ? '/api-teacher/getUploadSign'
+            : '/api-student/getUploadSign'
+
+        const fileName = file.file.name.replaceAll(' ', '_')
+        const key = new Date().getTime() + fileName
+        console.log(file)
+
+        const res = await request.post(signUrl, {
+          data: {
+            filename: fileName,
+            bucketName: this.bucket,
+            postData: {
+              filename: fileName,
+              acl: 'public-read',
+              key: key,
+              unknowValueField: []
+            }
+          }
+        })
+        Toast.loading({
+          message: '加载中...',
+          forbidClick: true,
+          loadingType: 'spinner',
+          duration: 0
+        })
+        const obj = {
+          policy: res.data.policy,
+          signature: res.data.signature,
+          key: key,
+          KSSAccessKeyId: res.data.kssAccessKeyId,
+          acl: 'public-read',
+          name: fileName
+        }
         let formData = new FormData()
+        for (let key in obj) {
+          formData.append(key, obj[key])
+        }
         formData.append('file', file.file)
-        let res = await request.post('/api-teacher/uploadFile', {
+        await umiRequest(state.ossUploadUrl + this.bucket, {
+          method: 'POST',
           data: formData
         })
-        const url = res.data.url
-        this.$emit('update:modelValue', url)
+        console.log(state.ossUploadUrl + this.bucket + '/' + key)
+        const uploadUrl = state.ossUploadUrl + this.bucket + '/' + key
+        Toast.clear()
+        this.$emit('update:modelValue', uploadUrl)
+        // this.onUploadChange(uploadUrl)
+        // let formData = new FormData()
+        // formData.append('file', file.file)
+        // let res = await request.post('/api-teacher/uploadFile', {
+        //   data: formData
+        // })
+        // const url = res.data.url
+        // this.$emit('update:modelValue', uploadUrl)
       } catch (error) {
         //
+        console.log(error)
       }
     },
     onClose(e: any) {
@@ -67,7 +123,7 @@ export default defineComponent({
     },
     onNativeUpload() {
       postMessage(
-        { api: 'chooseFile', content: { type: 'video' } },
+        { api: 'chooseFile', content: { type: 'video', bucket: this.bucket } },
         (res: any) => {
           // this.posterUrlInner = res.firstFrameImg
           this.$emit('update:modelValue', res.fileUrl)

+ 67 - 16
src/components/col-upload/index.tsx

@@ -4,9 +4,10 @@ import styles from './index.module.less'
 import ColCropper from '../col-cropper'
 import { useCustomFieldValue } from '@vant/use'
 import { postMessage } from '@/helpers/native-message'
-
+import umiRequest from 'umi-request'
 import iconUploader from '@common/images/icon_uploader.png'
 import request from '@/helpers/request'
+import { state } from '@/state'
 
 export default defineComponent({
   name: 'col-upload',
@@ -43,12 +44,19 @@ export default defineComponent({
     onUploadChange: {
       type: Function,
       default: (url: string) => {}
+    },
+    bucket: {
+      type: String,
+      default: 'daya'
     }
   },
   methods: {
     nativeUpload() {
       postMessage(
-        { api: 'chooseFile', content: { type: 'img', max: 1 } },
+        {
+          api: 'chooseFile',
+          content: { type: 'img', max: 1, bucket: this.bucket }
+        },
         (res: any) => {
           this.$emit('update:modelValue', res.fileUrl)
         }
@@ -70,15 +78,10 @@ export default defineComponent({
       try {
         file.status = 'uploading'
         file.message = '上传中...'
-        let formData = new FormData()
-        formData.append('file', file.file)
-        let res = await request.post('/api-teacher/uploadFile', {
-          data: formData
-        })
-        this.$emit('update:modelValue', res.data.url)
-        this.onUploadChange(res.data.url)
+        await this.uploadFile(file.file)
       } catch (error) {
         //
+        Toast.clear()
       }
     },
     onClose(e: any) {
@@ -87,18 +90,66 @@ export default defineComponent({
       e.stopPropagation()
     },
     async getFile(file: any) {
+      try {
+        await this.uploadFile(file)
+      } catch {
+        //
+      }
+    },
+    async uploadFile(file: any) {
       // 上传文件
       try {
+        // 获取签名
+        const signUrl =
+          state.platformType === 'TEACHER'
+            ? '/api-teacher/getUploadSign'
+            : '/api-student/getUploadSign'
+
+        const fileName = file.name.replaceAll(' ', '_')
+        const key = new Date().getTime() + fileName
+        console.log(file)
+
+        const res = await request.post(signUrl, {
+          data: {
+            filename: fileName,
+            bucketName: this.bucket,
+            postData: {
+              filename: fileName,
+              acl: 'public-read',
+              key: key,
+              unknowValueField: []
+            }
+          }
+        })
+        Toast.loading({
+          message: '加载中...',
+          forbidClick: true,
+          loadingType: 'spinner',
+          duration: 0
+        })
+        const obj = {
+          policy: res.data.policy,
+          signature: res.data.signature,
+          key: key,
+          KSSAccessKeyId: res.data.kssAccessKeyId,
+          acl: 'public-read',
+          name: fileName
+        }
         let formData = new FormData()
-        formData.append('file', file)
-        let res = await request.post('/api-teacher/uploadFile', {
+        for (let key in obj) {
+          formData.append(key, obj[key])
+        }
+        formData.append('file', file, fileName)
+        await umiRequest(state.ossUploadUrl + this.bucket, {
+          method: 'POST',
           data: formData
         })
-        this.$emit('update:modelValue', res.data.url)
-        this.onUploadChange(res.data.url)
-      } catch {
-        //
-      }
+        console.log(state.ossUploadUrl + this.bucket + '/' + key)
+        const uploadUrl = state.ossUploadUrl + this.bucket + '/' + key
+        Toast.clear()
+        this.$emit('update:modelValue', uploadUrl)
+        this.onUploadChange(uploadUrl)
+      } catch (error) {}
     }
   },
   render() {

+ 50 - 42
src/router/routes-common.ts

@@ -41,48 +41,6 @@ export const router = [
     }
   },
   {
-    path: '/helpCenter',
-    name: 'helpCenter',
-    component: () => import('@/views/article-center/help-center')
-  },
-  {
-    path: '/helpCenterDetail',
-    name: 'helpCenterDetail',
-    component: () => import('@/views/article-center/help-center-detail')
-  },
-  {
-    path: '/special',
-    name: 'special',
-    component: () => import('@/views/article-center/special'),
-    meta: {
-      title: '资讯列表'
-    }
-  },
-  {
-    path: '/specialDetail',
-    name: 'specialDetail',
-    component: () => import('@/views/article-center/special-detail'),
-    meta: {
-      title: '资讯详情'
-    }
-  },
-  {
-    path: '/articleTheory',
-    name: 'articleTheory',
-    component: () => import('@/views/article-center/theory'),
-    meta: {
-      title: '乐理列表'
-    }
-  },
-  {
-    path: '/theoryDetail',
-    name: 'theoryDetail',
-    component: () => import('@/views/article-center/theory-detail'),
-    meta: {
-      title: '乐理详情'
-    }
-  },
-  {
     path: '/shopMall',
     name: 'shopMall',
     component: () => import('@/views/shop-mall/index'),
@@ -165,5 +123,55 @@ export const rootRouter = [
     meta: {
       title: '酷乐秀隐私政策'
     }
+  },
+  {
+    path: '/guide',
+    name: 'guide',
+    component: () => import('@/views/article-center/guide'),
+    meta: {
+      title: '投屏引导'
+    }
+  },
+  {
+    path: '/helpCenter',
+    name: 'helpCenter',
+    component: () => import('@/views/article-center/help-center')
+  },
+  {
+    path: '/helpCenterDetail',
+    name: 'helpCenterDetail',
+    component: () => import('@/views/article-center/help-center-detail')
+  },
+  {
+    path: '/special',
+    name: 'special',
+    component: () => import('@/views/article-center/special'),
+    meta: {
+      title: '资讯列表'
+    }
+  },
+  {
+    path: '/specialDetail',
+    name: 'specialDetail',
+    component: () => import('@/views/article-center/special-detail'),
+    meta: {
+      title: '资讯详情'
+    }
+  },
+  {
+    path: '/articleTheory',
+    name: 'articleTheory',
+    component: () => import('@/views/article-center/theory'),
+    meta: {
+      title: '乐理列表'
+    }
+  },
+  {
+    path: '/theoryDetail',
+    name: 'theoryDetail',
+    component: () => import('@/views/article-center/theory-detail'),
+    meta: {
+      title: '乐理详情'
+    }
   }
 ]

+ 1 - 0
src/state.ts

@@ -8,6 +8,7 @@ export const state = reactive({
     data: {} as any
   },
   platformType: '' as 'STUDENT' | 'TEACHER',
+  ossUploadUrl: 'https://ks3-cn-beijing.ksyuncs.com/',
   musicCertStatus: false as boolean, // 是否音乐认证
   openLiveStatus: false as boolean // 是否开通直播
 })

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

@@ -235,6 +235,7 @@ export default defineComponent({
                     <Col span={12} class={styles.imgContainer}>
                       <ColUpload
                         cropper
+                        bucket="live-rewind"
                         options={{
                           fixedNumber: [3, 2],
                           autoCropWidth: 750,

+ 40 - 28
src/teacher/music/upload/index.tsx

@@ -226,44 +226,56 @@ export default defineComponent({
     },
     naiveXMLFile() {
       this.xmlFileLoading = true
-      postMessage({ api: 'chooseFile', content: { type: 'xml' } }, evt => {
-        // @ts-ignore
-        this.xmlFileUrl = evt?.fileUrl || this.xmlFileUrl || ''
-        this.xmlFileLoading = false
-        if (this.xmlFileUrl) {
-          requestOrigin(this.xmlFileUrl).then(
-            res => (this.formated = getXmlInfo(res))
-          )
+      postMessage(
+        { api: 'chooseFile', content: { type: 'xml', bucket: 'cloud-coach' } },
+        evt => {
+          // @ts-ignore
+          this.xmlFileUrl = evt?.fileUrl || this.xmlFileUrl || ''
+          this.xmlFileLoading = false
+          if (this.xmlFileUrl) {
+            requestOrigin(this.xmlFileUrl).then(
+              res => (this.formated = getXmlInfo(res))
+            )
+          }
         }
-      })
+      )
     },
     naiveMidFile() {
       this.midiLoading = true
-      postMessage({ api: 'chooseFile', content: { type: 'midi' } }, evt => {
-        // @ts-ignore
-        this.midiUrl = evt?.fileUrl || this.midiUrl || ''
-        this.midiLoading = false
-        // this.midiUrl = path
-      })
+      postMessage(
+        { api: 'chooseFile', content: { type: 'midi', bucket: 'cloud-coach' } },
+        evt => {
+          // @ts-ignore
+          this.midiUrl = evt?.fileUrl || this.midiUrl || ''
+          this.midiLoading = false
+          // this.midiUrl = path
+        }
+      )
     },
     naiveMp3File() {
       this.mp3Loading = true
-      postMessage({ api: 'chooseFile', content: { type: 'mp3' } }, evt => {
-        // @ts-ignore
-        this.mp3Url = evt?.fileUrl || this.mp3Url || ''
-        this.mp3Loading = false
-        // this.midiUrl = path
-      })
+      postMessage(
+        { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } },
+        evt => {
+          // @ts-ignore
+          this.mp3Url = evt?.fileUrl || this.mp3Url || ''
+          this.mp3Loading = false
+          // this.midiUrl = path
+        }
+      )
     },
     naiveBGMp3File() {
       this.bgmp3Loading = true
-      postMessage({ api: 'chooseFile', content: { type: 'mp3' } }, evt => {
-        this.bgmp3Url
-        // @ts-ignore
-        this.bgmp3Url = evt?.fileUrl || this.bgmp3Url || ''
-        this.bgmp3Loading = false
-        // this.midiUrl = path
-      })
+      postMessage(
+        { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } },
+        evt => {
+          this.bgmp3Url
+          // @ts-ignore
+          this.bgmp3Url = evt?.fileUrl || this.bgmp3Url || ''
+          this.bgmp3Loading = false
+          // this.midiUrl = path
+        }
+      )
     },
     fileName(name = '') {
       return name.split('/').pop()

+ 1 - 1
src/teacher/open-live/index.tsx

@@ -163,7 +163,7 @@ export default defineComponent({
               </div>
             </div>
 
-            {!this.users.liveFlag ? (
+            {!this.btnStatus ? (
               <div class={styles['open-tips']}>
                 <Icon name={tips} size="16" />
                 <p>你尚未达到开通直播的条件</p>

+ 12 - 2
src/teacher/piano-room/index.tsx

@@ -24,6 +24,7 @@ import Share from './model/share'
 import ColPopup from '@/components/col-popup'
 import StudentInfo from './model/student-info'
 import StudentConfirm from './model/student-info/student-confirm'
+import { useEventListener, useWindowScroll } from '@vueuse/core'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -69,12 +70,21 @@ export default defineComponent({
       timeUpdateTimer: new Date(),
       studentStatus: false,
       studentConfirm: false,
-      studentChangeObject: {} as any
+      studentChangeObject: {} as any,
+      background: 'transparent'
     }
   },
   async mounted() {
     await this._init()
     await this.getList()
+    useEventListener(document, 'scroll', evt => {
+      const { y } = useWindowScroll()
+      if (y.value > 45) {
+        this.background = '#fff'
+      } else {
+        this.background = 'transparent'
+      }
+    })
   },
   methods: {
     async _init() {
@@ -195,7 +205,7 @@ export default defineComponent({
       <>
         <div class={styles.roomHeader}>
           <ColHeader
-            background="transparent"
+            background={this.background}
             rightText="课程记录"
             onClickRight={() => {
               this.$router.push('/courseRecord')

+ 2 - 0
src/teacher/video-class/class-content.tsx

@@ -106,6 +106,7 @@ export default defineComponent({
                       v-slots={{
                         input: () => (
                           <ColUploadVideo
+                            bucket="video-course"
                             v-model={item.videoUrl}
                             v-model:posterUrl={item.posterUrl}
                             class={styles.upload}
@@ -126,6 +127,7 @@ export default defineComponent({
                           <ColUpload
                             class={styles.upload}
                             cropper
+                            bucket="video-course"
                             options={{
                               fixedNumber: [3, 2],
                               autoCropWidth: 750,

+ 1 - 0
src/teacher/video-class/class-info.tsx

@@ -253,6 +253,7 @@ export default defineComponent({
                     <Col span={12} class={styles.imgContainer}>
                       <ColUpload
                         cropper
+                        bucket="video-course"
                         options={{
                           fixedNumber: [3, 2],
                           autoCropWidth: 750,

+ 78 - 0
src/views/article-center/components/andoird-guide.module.less

@@ -0,0 +1,78 @@
+.topTitle {
+  position: relative;
+  h2 {
+    font-weight: bold;
+    color: #00201c;
+    line-height: 18px;
+    font-size: 17px;
+    padding-left: 22px;
+    color: #00201c;
+    position: relative;
+    z-index: 20;
+  }
+}
+.wrap {
+  box-sizing: border-box;
+  padding: 37px 0;
+  background-color: #fff;
+
+  .wrapInfo {
+    padding: 0 22px;
+    section {
+      margin-top: 30px;
+      margin-bottom: 50px;
+      .bigP {
+        font-weight: bold;
+        color: #00201c;
+        font-size: 16px;
+        line-height: 20px;
+      }
+      p {
+        font-size: 13px;
+        line-height: 20px;
+        margin-bottom: 20px;
+      }
+    }
+    h3 {
+      font-weight: bold;
+      color: #00201c;
+      font-size: 16px;
+      line-height: 20px;
+    }
+    .blod {
+      font-weight: bold;
+    }
+    .red {
+      color: #ff0000;
+    }
+  }
+}
+
+.dot {
+  position: absolute;
+  width: 10px;
+  height: 17px;
+  background: #00c2b5;
+  opacity: 0.53;
+  border-radius: 1px;
+  top: -7px;
+  left: 0;
+}
+.little {
+  display: inline-block;
+  width: 4px;
+  height: 4px;
+  background: #00c2b5;
+  opacity: 0.53;
+  right: 0;
+}
+.imgWrap {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-around;
+  align-items: center;
+  .img {
+    margin-bottom: 20px;
+    width: 190px;
+  }
+}

+ 190 - 0
src/views/article-center/components/android-guide.tsx

@@ -0,0 +1,190 @@
+import { defineComponent } from 'vue'
+import styles from './andoird-guide.module.less'
+
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `../images/${fileName}`
+  const modules = import.meta.globEager('../images/*')
+  return modules[path].default
+}
+
+const infoMsg = {
+  meizu: {
+    img1: getAssetsHomeFile('4.png'),
+    img2: getAssetsHomeFile('5.png'),
+    img3: getAssetsHomeFile('6.png'),
+    title1: '打开“设置”页面,点击“更多连接方式”按钮:',
+    title2: '点击“投射屏幕”',
+    title3:
+      '打开“投射屏幕”开关,即可看到可以投屏的设备列表,选择您的设备进行连接。'
+  },
+  xiaomi: {
+    img1: getAssetsHomeFile('7.png'),
+    img2: getAssetsHomeFile('8.png'),
+    img3: getAssetsHomeFile('9.png'),
+    title1: '打开“设置”页面,点击“连接与共享”按钮:',
+    title2: '点击“投屏”:',
+    title3:
+      '打开“打开投屏”开关,即可看到可以投屏的设备列表,选择您的设备进行连接。'
+  },
+  vivo: {
+    img1: getAssetsHomeFile('12.png'),
+    img2: getAssetsHomeFile('13.png'),
+    img3: getAssetsHomeFile('14.png'),
+    title1: '打开“设置”页面,点击“其他网络与连接”按钮:',
+    title2: '点击“手机投屏”:',
+    title3:
+      '打开“手机投屏”开关,即可看到可以投屏的设备列表,选择您的设备进行连接。'
+  },
+  huawei: {
+    img1: getAssetsHomeFile('1.png'),
+    img2: getAssetsHomeFile('2.png'),
+    img3: getAssetsHomeFile('3.png'),
+    title1: '打开“设置”页面,点击“更多连接”按钮:',
+    title2: '点击“手机投屏”:',
+    title3:
+      '打开“无线投屏”开关,即可看到可以投屏的设备列表,选择您的设备进行连接。'
+  }
+}
+export default defineComponent({
+  name: 'adnroid-guide',
+  data() {
+    return {
+      brand: 'huawei'
+    }
+  },
+  mounted() {
+    var ua = navigator.userAgent.split('(')[1].split(')')[0]
+    this.brand = ''
+    var phone = [/MZ/gi, /mi/gi, /vivo/gi]
+    if (phone[0].test(ua)) {
+      this.brand = 'meizu'
+    } else if (phone[1].test(ua)) {
+      this.brand = 'xiaomi'
+    } else if (phone[2].test(ua)) {
+      this.brand = 'vivo'
+    } else {
+      this.brand = 'huawei'
+    }
+  },
+  render() {
+    return (
+      <div class={styles.wrap}>
+        <div class={styles.topTitle}>
+          <h2>通过镜像方式显示手机或平板上的内容</h2>
+        </div>
+        <section>
+          <img style={{ width: '100%' }} src={getAssetsHomeFile('guide.png')} />
+        </section>
+        <div class={styles.wrapInfo}>
+          <section>
+            <h3>第1步</h3>
+            <p>将您的手机或平板连接到您智能电视机所在的同一无线局域网。</p>
+            <h3>第2步</h3>
+            <p>{infoMsg[this.brand]['title1']}</p>
+            <div class={styles.imgWrap}>
+              <img
+                class={styles.img}
+                src={infoMsg[this.brand]['img1']}
+                alt=""
+              />
+            </div>
+
+            <h3>第3步</h3>
+            <p>{infoMsg[this.brand]['title2']}</p>
+            <div class={styles.imgWrap}>
+              <img
+                class={styles.img}
+                src={infoMsg[this.brand]['img2']}
+                alt=""
+              />
+            </div>
+
+            <h3>第4步</h3>
+            <p>{infoMsg[this.brand]['title3']}</p>
+            <div class={styles.imgWrap}>
+              <img
+                class={styles.img}
+                src={infoMsg[this.brand]['img3']}
+                alt=""
+              />
+            </div>
+          </section>
+          <section>
+            <p class={[styles.blod, styles.bigP]}>如果音乐意外停止:</p>
+            <p>
+              如果在这台设备上使用语音助手或进行其他任务,则可能会导致所有音频设备停止播放音乐
+            </p>
+            <p class={[styles.blod, styles.bigP]}>
+              如果您看到视频但听不到声音:
+            </p>
+            <p>
+              如果您听不到任何声音,则请确保手机设备和电视机/听筒的音量都已调高,而且没有静音。
+            </p>
+            <p>
+              请检查响铃/静音开关。如果开关设为静音,您会看到一条橙色的线。切换开关以开启响铃。
+            </p>
+            <p class={[styles.blod, styles.bigP]}>如果内容中断或网络卡顿:</p>
+            <p>
+              如果 Wi-Fi
+              信号欠佳或受到附近设备(例如,微波炉或婴儿监视器)的干扰尝试完成以下步骤:
+            </p>
+            <p>移开或关闭其他可能造成干扰的设备。</p>
+            <p>
+              如果您正尝试使用“隔空播放”将内容流化到智能电视,请尝试使用以太网线缆(而不是通过
+              Wi-Fi)将智能电视直接连接到路由器。
+            </p>
+            <p class={(styles.blod, styles.red)}>
+              *当您锁定设备、将其置于睡眠模式或切换到其他应用时,“隔空播放”连接可能会中断。
+            </p>
+          </section>
+          <section>
+            <p class={[styles.blod, styles.bigP]}>
+              如果“隔空播放”或屏幕镜像在您的设备上无法使用:
+            </p>
+            <p>1.确保您的设备都已开机且彼此距离较近。</p>
+            <p>2.请重新启动您要与“隔空播放”或屏幕镜像配合使用的设备。</p>
+            <p>3.以上方式尝试了仍无法搜到要使用的智能电视:</p>
+            <p class={(styles.blod, styles.red)}>
+              将您的设备连接到同一个 Wi-Fi 网络
+            </p>
+            <p>
+              首先需要确认电视与手机是否连接了同一个Wi-Fi
+              ,电视可以插网线,但必须是同一个路由器的,如果不确认,就把电视网线拔了改为连接Wi-Fi
+              。
+            </p>
+            <p class={[styles.blod, styles.bigP]}>
+              如果同一个Wi-Fi 也无法搜索到需要投屏的设备:
+            </p>
+            <p>
+              原因1·可能电视本身没有投屏功能(如果以前投屏过,也是可以判断为电视是支持投屏的。)
+            </p>
+            <p>A.是不是智能电视?</p>
+            <p>B.能不能自己安装软件?</p>
+            <p>C.是不是安卓系统?</p>
+            <p>D.能不能连接WiFi?</p>
+            <p>请确认以上四点。一般16年以后买的电视,99%都已经是智能电视了</p>
+          </section>
+          <section>
+            <p class={[styles.blod, styles.bigP]}>
+              如果是智能电视或者智能投影仪,但是没有投屏功能怎么办?
+            </p>
+            <p>
+              解决办法:自己安装一个投屏软件进去(幕享、傲软投屏、乐播投屏......)。相当于更新了电视投屏功能。也能解决这个问题。
+            </p>
+            <p>如果是老电视,老投影仪,老机顶盒怎么办?</p>
+            <p>这个也不是没有办法,电视最值钱的就是屏幕了。</p>
+            <p>
+              所以屏幕我们不要浪费了,继续使用,此时我们通过互联网机顶盒,从机顶盒应用商店安装投屏软件,也同样可以
+              进行投屏。
+            </p>
+            <p>a.是不是智能机顶盒?</p>
+            <p>b.能不能自己安装软件?</p>
+            <p>
+              c.是不是安卓系统?买回来之后利用HDMI线连接老电视,再从机顶盒应用商店下载投屏软件,就可以使用投屏了。
+            </p>
+          </section>
+        </div>
+      </div>
+    )
+  }
+})

+ 84 - 0
src/views/article-center/components/ios-guide.module.less

@@ -0,0 +1,84 @@
+.marginB33 {
+  margin-bottom: 33px;
+  h2 {
+    padding-left: 0 !important;
+  }
+}
+.wrap {
+  box-sizing: border-box;
+  padding: 37px 0;
+  background-color: #fff;
+
+  .topTitle {
+    position: relative;
+    h2 {
+      font-weight: bold;
+      color: #00201c;
+      line-height: 18px;
+      font-size: 17px;
+      padding-left: 22px;
+      color: #00201c;
+      position: relative;
+      z-index: 20;
+    }
+  }
+  .wrapInfo {
+    padding: 0 22px;
+    section {
+      // margin-top: 0.3rem;
+      margin-bottom: 45px;
+      .bigP {
+        font-weight: bold;
+        color: #00201c;
+        font-size: 16px;
+        line-height: 20px;
+      }
+      p {
+        font-size: 13px;
+        line-height: 20px;
+        margin-bottom: 20px;
+      }
+    }
+    h3 {
+      font-weight: bold;
+      color: #00201c;
+      font-size: 16px;
+      line-height: 20px;
+    }
+    .blod {
+      font-weight: bold;
+    }
+    .red {
+      color: #ff0000;
+    }
+  }
+}
+
+.dot {
+  position: absolute;
+  width: 10px;
+  height: 17px;
+  background: #00c2b5;
+  opacity: 0.53;
+  border-radius: 1px;
+  top: -7px;
+  left: 0;
+}
+.little {
+  display: inline-block;
+  width: 4px;
+  height: 4px;
+  background: #00c2b5;
+  opacity: 0.53;
+  right: 0;
+}
+.imgWrap {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-around;
+  align-items: center;
+  .img {
+    margin-bottom: 20px;
+    width: 190px;
+  }
+}

+ 153 - 0
src/views/article-center/components/ios-guide.tsx

@@ -0,0 +1,153 @@
+import { defineComponent } from 'vue'
+import styles from './ios-guide.module.less'
+
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `../images/${fileName}`
+  const modules = import.meta.globEager('../images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'ios-guide',
+  render() {
+    return (
+      <div class={styles.wrap}>
+        <div class={styles.topTitle}>
+          <h2>通过镜像方式显示 iPhone或iPad上的内容</h2>
+        </div>
+
+        <img style={{ width: '100%' }} src={getAssetsHomeFile('guide.png')} />
+
+        <div class={styles.wrapInfo}>
+          <section>
+            <h3>第1步</h3>
+            <p>
+              通过镜像方式显示 iPhone或iPad上的内容将您的 iPhone或iPad
+              连接到您的 Apple TV 或兼容“隔空播放
+              2”的智能电视机所在的同一无线局域网。
+            </p>
+            <h3>第2步</h3>
+            <p>
+              打开“控制中心”:
+              <br />
+              在 iPhone X 或更新机型或者装有 iPadOS 13 或更高版本的 iPad
+              上:从屏幕右上角向下轻扫。
+              <br />在 iPhone 8 或更早机型或者 iOS 11
+              或更低版本上:从屏幕底部边缘向上轻扫。
+            </p>
+            <h3>第3步</h3>
+            <p>轻点 “屏幕镜像”。(iOS 11 之前版本:AirPlay 镜像)</p>
+            <h3>第4步</h3>
+            <p>从列表中选择您的 Apple TV 或兼容“隔空播放 2”的智能电视机</p>
+            <h3>第5步</h3>
+            <p>
+              如果电视机屏幕上出现“隔空播放”密码,请在 iOS 或 iPadOS
+              设备上输入这个密码。
+            </p>
+            <h3>第6步</h3>
+            <p>
+              要停止镜像您的 iOS 或 iPadOS
+              设备,请打开“控制中心”,轻点“屏幕镜像”,然后轻点“停止镜像”。
+            </p>
+          </section>
+          <div class={[styles.topTitle, styles.marginB33]}>
+            <h2>iOS 10控制中心图片:</h2>
+          </div>
+          <section>
+            <img
+              src={getAssetsHomeFile('11.png')}
+              style={{ width: '100%' }}
+              alt=""
+            />
+          </section>
+          <div class={[styles.topTitle, styles.marginB33]}>
+            <h2>iOS 10之后版本控制中心图片:</h2>
+          </div>
+          <section>
+            <img
+              src={getAssetsHomeFile('10.png')}
+              style={{ width: '100%' }}
+              alt=""
+            />
+          </section>
+          <section>
+            <p class={[styles.blod, styles.bigP]}>如果音乐意外停止:</p>
+            <p>
+              如果在这台设备上使用 Siri
+              或进行其他任务,则可能会导致所有音频设备停止播放音乐
+            </p>
+            <p class={[styles.blod, styles.bigP]}>
+              如果您看到视频但听不到声音:
+            </p>
+            <p>
+              如果您听不到任何声音,则请确保 iOS
+              设备和电视机/听筒的音量都已调高,而且没有静音。
+            </p>
+            <p>
+              请检查响铃/静音开关。如果开关设为静音,您会看到一条橙色的线。切换开关以开启响铃。
+            </p>
+            <p class={[styles.blod, styles.bigP]}>如果内容中断或网络卡顿:</p>
+            <p>
+              如果 Wi-Fi
+              信号欠佳或受到附近设备(例如,微波炉或婴儿监视器)的干扰尝试完成以下步骤:
+            </p>
+            <p>移开或关闭其他可能造成干扰的设备。</p>
+            <p>
+              如果您正尝试使用“隔空播放”将内容流化到智能电视,请尝试使用以太网线缆(而不是通过
+              Wi-Fi)将智能电视直接连接到路由器。
+            </p>
+            <p class={[styles.blod, styles.red]}>
+              *当您锁定设备、将其置于睡眠模式或切换到其他应用时,“隔空播放”连接可能会中断。
+            </p>
+          </section>
+          <section>
+            <p class={[styles.blod, styles.bigP]}>
+              如果“隔空播放”或屏幕镜像在您的设备上无法使用:
+            </p>
+            <p>1.确保您的设备都已开机且彼此距离较近。</p>
+            <p>2.请重新启动您要与“隔空播放”或屏幕镜像配合使用的设备。</p>
+            <p>3.以上方式尝试了仍无法搜到要使用的智能电视:</p>
+            <p class={[styles.blod, styles.red]}>
+              将您的设备连接到同一个 Wi-Fi 网络
+            </p>
+            <p>
+              首先需要确认电视与手机是否连接了同一个Wi-Fi
+              ,电视可以插网线,但必须是同一个路由器的,如果不确认,就把电视网线拔了改为连接Wi-Fi
+              。
+            </p>
+            <p class={[styles.blod, styles.bigP]}>
+              如果同一个Wi-Fi 也无法搜索到需要投屏的设备:
+            </p>
+            <p>
+              原因1·可能电视本身没有投屏功能(如果以前投屏过,也是可以判断为电视是支持投屏的。)
+            </p>
+            <p>A.是不是智能电视?</p>
+            <p>B.能不能自己安装软件?</p>
+            <p>C.是不是安卓系统?</p>
+            <p>D.能不能连接WiFi?</p>
+            <p>请确认以上四点。一般16年以后买的电视,99%都已经是智能电视了</p>
+          </section>
+          <section>
+            <p class={[styles.blod, styles.bigP]}>
+              如果是智能电视或者智能投影仪,但是没有投屏功能怎么办?
+            </p>
+            <p>
+              解决办法:自己安装一个投屏软件进去(幕享、傲软投屏、乐播投屏......)。相当于更新了电视投屏功能。也能解决这个问题。
+            </p>
+            <p>如果是老电视,老投影仪,老机顶盒怎么办?</p>
+            <p>这个也不是没有办法,电视最值钱的就是屏幕了。</p>
+            <p>
+              所以屏幕我们不要浪费了,继续使用,此时我们通过互联网机顶盒,从机顶盒应用商店安装投屏软件,也同样可以
+              进行投屏。
+            </p>
+            <p>a.是不是智能机顶盒?</p>
+            <p>b.能不能自己安装软件?</p>
+            <p>
+              c.是不是安卓系统?买回来之后利用HDMI线连接老电视,再从机顶盒应用商店下载投屏软件,就可以使用投屏了。
+            </p>
+          </section>
+        </div>
+      </div>
+    )
+  }
+})

+ 0 - 0
src/views/article-center/guide.module.less


+ 25 - 0
src/views/article-center/guide.tsx

@@ -0,0 +1,25 @@
+import { browser } from '@/helpers/utils'
+import { defineComponent } from 'vue'
+import IosGuide from './components/ios-guide'
+import AndroidGuide from './components/android-guide'
+
+export default defineComponent({
+  name: 'guide',
+  data() {
+    return {
+      client: 'ios'
+    }
+  },
+  mounted() {
+    // if (browser().android) {
+    //   this.client = 'android'
+    // } else if (browser().iPhone) {
+    //   this.client = 'ios'
+    // } else {
+    //   this.client = 'web'
+    // }
+  },
+  render() {
+    return <>{this.client === 'ios' ? <IosGuide /> : <AndroidGuide />}</>
+  }
+})

+ 1 - 1
src/views/article-center/help-center.tsx

@@ -22,7 +22,7 @@ export default defineComponent({
         catalogIds: query.catalogType || 1,
         title: '',
         status: 1,
-        catalogType: state.platformType,
+        catalogType: query.platformType || state.platformType,
         page: 1,
         rows: 20
       }

二进制
src/views/article-center/images/1.png


二进制
src/views/article-center/images/10.png


二进制
src/views/article-center/images/11.png


二进制
src/views/article-center/images/12.png


二进制
src/views/article-center/images/13.png


二进制
src/views/article-center/images/14.png


二进制
src/views/article-center/images/2.png


二进制
src/views/article-center/images/3.png


二进制
src/views/article-center/images/4.png


二进制
src/views/article-center/images/5.png


二进制
src/views/article-center/images/6.png


二进制
src/views/article-center/images/7.png


二进制
src/views/article-center/images/8.png


二进制
src/views/article-center/images/9.png


二进制
src/views/article-center/images/guide.png


+ 4 - 0
src/views/article-center/theory.module.less

@@ -39,6 +39,10 @@
   height: 175px;
 }
 
+.musicTitle {
+  width: 230px;
+}
+
 .theory {
   --van-collapse-item-content-padding: 0;
 

+ 5 - 2
src/views/article-center/theory.tsx

@@ -103,9 +103,10 @@ export default defineComponent({
                 <CollapseItem
                   title={parent.name}
                   name={parent.id}
+                  center
                   v-slots={{
                     title: () => (
-                      <div class={styles.groupTitle}>
+                      <div class={[styles.groupTitle]}>
                         {parent.url && (
                           <Image
                             src={parent.url}
@@ -114,7 +115,9 @@ export default defineComponent({
                           />
                         )}
 
-                        {parent.name}
+                        <p class={['van-ellipsis', styles.musicTitle]}>
+                          {parent.name}
+                        </p>
                       </div>
                     )
                   }}

+ 11 - 12
src/views/cart/components/address/index.tsx

@@ -1,15 +1,9 @@
 import { Cell, Icon } from 'vant'
-import {
-  defineComponent,
-  computed,
-  PropType
-} from 'vue'
+import { defineComponent, computed, PropType } from 'vue'
 import styles from './index.module.less'
 import iconAddress from '@views/shop-mall/images/icon-address.png'
-import {  addressType } from '../../cart'
-import {
-  postMessage
-} from '@/helpers/native-message'
+import { addressType } from '../../cart'
+import { postMessage } from '@/helpers/native-message'
 
 export default defineComponent({
   name: 'cart-address',
@@ -22,7 +16,7 @@ export default defineComponent({
       type: Boolean,
       default: true
     },
-    setAddress:{
+    setAddress: {
       type: Function,
       default: (n: any) => {}
     }
@@ -45,7 +39,6 @@ export default defineComponent({
       })
     }
 
-
     return () => (
       <>
         <Cell
@@ -58,7 +51,13 @@ export default defineComponent({
               <div>
                 <span class={styles.userName}>{props.item.name}</span>
                 <span class={styles.phone}>
-                  {props.item && props.item.phoneNumber && props.item.phoneNumber.replace(/^(\d{3})\d{4}(\d+)/,"$1****$2") || '去填写收货地址'}
+                  {(props.item &&
+                    props.item.phoneNumber &&
+                    props.item.phoneNumber.replace(
+                      /^(\d{3})\d{4}(\d+)/,
+                      '$1****$2'
+                    )) ||
+                    '去填写收货地址'}
                 </span>
               </div>
             ),

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

@@ -25,6 +25,7 @@ import OrderVip from './order-vip'
 import OrderMusic from './order-music'
 import { moneyFormat } from '@/helpers/utils'
 import OrderPinao from './order-pinao'
+import { getMusicDetail } from '@/student/trade/tradeOrder'
 
 export default defineComponent({
   name: 'order-detail',
@@ -32,6 +33,7 @@ export default defineComponent({
     const query = this.$route.query
     return {
       orderType: query.orderType,
+      id: query.id,
       agreeStatus: false,
       popupShow: false,
       paymentStatus: false,
@@ -104,7 +106,25 @@ export default defineComponent({
       })
     }
   },
-  mounted() {
+  async mounted() {
+    // 判断是否是曲目购买(只有智能陪练才会有入口),其它地方不会有入口
+    if (this.orderType == 'MUSIC' && this.id) {
+      try {
+        const item = await getMusicDetail(this.id)
+        orderStatus.orderObject.orderType = 'MUSIC'
+        orderStatus.orderObject.orderName = item.musicSheetName
+        orderStatus.orderObject.orderDesc = item.musicSheetName
+        orderStatus.orderObject.actualPrice = item.musicPrice
+        orderStatus.orderObject.orderList = [
+          {
+            orderType: 'MUSIC',
+            goodsName: item.musicSheetName,
+            actualPrice: item.musicPrice,
+            ...item
+          }
+        ]
+      } catch {}
+    }
     this.orderPrice = orderStatus.orderObject.actualPrice || 0
   },
   methods: {

+ 6 - 6
src/views/order-detail/orderStatus.ts

@@ -11,12 +11,12 @@ type orderType =
   | ''
 export const orderStatus = reactive({
   orderType: '' as orderType, // 购买类型
-  liveInfo: {} as any, // 直播购买信息
-  videoInfo: {} as any, // 视频购买信息
-  practiceInfo: {} as any, // 视频购买信息
-  vipInfo: {} as any, // 会员购买信息
-  musicInfo: {} as any, // 单曲购买信息
-  orderList: [] as Array<any>, // 存放订单详情,可以是多个商品组合
+  // liveInfo: {} as any, // 直播购买信息
+  // videoInfo: {} as any, // 视频购买信息
+  // practiceInfo: {} as any, // 视频购买信息
+  // vipInfo: {} as any, // 会员购买信息
+  // musicInfo: {} as any, // 单曲购买信息
+  // orderList: [] as Array<any>, // 存放订单详情,可以是多个商品组合
   orderInfo: {
     // 订单信息
     orderNo: '',

+ 1 - 0
vite.config.ts

@@ -46,6 +46,7 @@ export default defineConfig({
     host: '0.0.0.0',
     port: 5000,
     strictPort: true,
+    cors: true,
     proxy: {
       '/api-auth': {
         target: proxyUrl,