浏览代码

Merge branch 'colexiu1.3' into jenkins

lex 1 年之前
父节点
当前提交
b5422eebff
共有 36 个文件被更改,包括 1698 次插入631 次删除
  1. 4 1
      src/components/col-search/index.tsx
  2. 214 215
      src/components/col-upload-video/index.tsx
  3. 237 238
      src/components/col-upload/index.tsx
  4. 16 0
      src/router/routes-tenant.ts
  5. 5 3
      src/teacher/share-page/share-album/index.tsx
  6. 3 3
      src/teacher/share-page/share-music/index.tsx
  7. 1 1
      src/tenant/music/album-detail/index.tsx
  8. 14 1
      src/tenant/music/album/index.tsx
  9. 43 22
      src/tenant/music/component/music-grid/index.tsx
  10. 2 3
      src/tenant/music/component/song/index.tsx
  11. 41 2
      src/tenant/music/list/index.tsx
  12. 2 3
      src/tenant/music/music-detail/index.tsx
  13. 1 2
      src/tenant/music/music-detail/new-index.tsx
  14. 47 1
      src/tenant/music/search/all-search.module.less
  15. 6 1
      src/tenant/music/search/all-search.tsx
  16. 168 26
      src/tenant/music/search/header.tsx
  17. 二进制
      src/tenant/music/search/icon-search.png
  18. 139 0
      src/tenant/music/search/index.module.less
  19. 11 23
      src/tenant/music/search/index.tsx
  20. 129 0
      src/tenant/music/search/music-swiper.tsx
  21. 153 0
      src/tenant/music/search/result-all-search.tsx
  22. 133 0
      src/tenant/music/search/search-result.tsx
  23. 0 1
      src/tenant/music/search/select-subject.tsx
  24. 11 1
      src/tenant/music/train-list/index.module.less
  25. 55 46
      src/tenant/music/train-list/index.tsx
  26. 12 9
      src/tenant/music/train-tool/index.tsx
  27. 2 0
      src/tenant/ranking-list/index.module.less
  28. 9 0
      src/tenant/ranking-list/index.tsx
  29. 152 0
      src/tenant/trade/detail.tsx
  30. 59 0
      src/tenant/trade/index.module.less
  31. 1 1
      src/tenant/trade/index.tsx
  32. 5 3
      src/views/music/music-detail/index.tsx
  33. 2 2
      src/views/tenantStudentRejest/index.module.less
  34. 10 6
      src/views/tenantStudentRejest/index.tsx
  35. 2 2
      src/views/tenantTeacherRejest/index.module.less
  36. 9 15
      src/views/tenantTeacherRejest/index.tsx

+ 4 - 1
src/components/col-search/index.tsx

@@ -59,7 +59,7 @@ export default defineComponent({
       default: iconSearch
     }
   },
-  emits: ['click'],
+  emits: ['click', 'input'],
   watch: {
     modelValue() {
       this.search = this.modelValue
@@ -82,6 +82,9 @@ export default defineComponent({
           placeholder={this.placeholder}
           disabled={this.disabled}
           autofocus={this.autofocus}
+          onUpdate:modelValue={(val: any) => {
+            this.$emit('input', val)
+          }}
           onSearch={(val: string) => {
             this.onSearch(val)
           }}

+ 214 - 215
src/components/col-upload-video/index.tsx

@@ -1,215 +1,214 @@
-import request from '@/helpers/request'
-import { Icon, Toast, Uploader, Image } from 'vant'
-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 { getOssUploadUrl, state } from '@/state'
-
-export default defineComponent({
-  name: 'ColUploadVideo',
-  props: {
-    modelValue: String,
-    posterUrl: String,
-    tips: {
-      type: String,
-      default: '点击上传'
-    },
-    nativeUpload: {
-      // 是否使用原生上传, 且当前环境为app才会生效
-      type: Boolean,
-      default: true
-    },
-    size: {
-      type: Number,
-      default: 30
-    },
-    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)
-      if (!isLt2M) {
-        Toast(`上传视频大小不能超过 ${this.size}MB`)
-        return false
-      }
-      return true
-    },
-    beforeDelete(file: any, detail: { index: any }) {
-      // this.dataModel.splice(detail.index, 1)
-      return true
-    },
-    async afterRead(file: any, detail: any) {
-      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)
-        await umiRequest(getOssUploadUrl(this.bucket), {
-          method: 'POST',
-          data: formData
-        })
-        const uploadUrl = getOssUploadUrl(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) {
-      this.$emit('update:modelValue', null)
-      e.stopPropagation()
-    },
-    onNativeUpload() {
-      postMessage(
-        { api: 'chooseFile', content: { type: 'video', bucket: this.bucket } },
-        (res: any) => {
-          // this.posterUrlInner = res.firstFrameImg
-          this.$emit('update:modelValue', res.fileUrl)
-          // this.$emit('update:posterUrl', res.firstFrameImg)
-        }
-      )
-    },
-    getVideoBase64(url: string) {
-      return new Promise(function (resolve) {
-        let dataURL = ''
-        const video = document.createElement('video')
-        video.setAttribute('crossOrigin', 'anonymous') // 处理跨域
-        video.setAttribute('src', url)
-        video.setAttribute('preload', 'auto')
-        video.addEventListener('loadeddata', function () {
-          console.log(video, 'video loadeddata')
-          const canvas = document.createElement('canvas')
-          console.log('video.clientWidth', video.videoWidth) // 视频宽
-          console.log('video.clientHeight', video.videoHeight) // 视频高
-          const width = video.videoWidth || 750 // canvas的尺寸和图片一样
-          const height = video.videoHeight || 500 // 设置默认宽高为 750 * 500
-          canvas.width = width
-          canvas.height = height
-          ;(canvas as any)
-            .getContext('2d')
-            .drawImage(video, 0, 0, width, height) // 绘制canvas
-          dataURL = canvas.toDataURL('image/jpeg') // 转换为base64
-          resolve(dataURL)
-        })
-      })
-    }
-  },
-  render() {
-    useCustomFieldValue(() => this.modelValue)
-    return (
-      <div class={styles['uploader-section']}>
-        {this.modelValue && this.deletable ? (
-          <Icon
-            name="cross"
-            onClick={this.onClose}
-            class={styles['img-close']}
-          />
-        ) : null}
-        {browser().isApp && this.nativeUpload ? (
-          <div onClick={this.onNativeUpload} style={{ height: '100%' }}>
-            {this.modelValue ? (
-              <video
-                ref="videoUpload"
-                class={styles.uploadImg}
-                src={this.modelValue + '#t=1,4'}
-                // poster={iconUploadPoster}
-              />
-            ) : (
-              <div class={styles.uploader}>
-                <Icon name={iconUploader} size="32" />
-                <p class={styles.uploaderText}>{this.tips}</p>
-              </div>
-            )}
-          </div>
-        ) : (
-          <>
-            {/* @ts-ignore */}
-            <Uploader
-              accept=".mp4"
-              afterRead={this.afterRead}
-              beforeRead={this.beforeRead}
-              beforeDelete={this.beforeDelete}
-              v-slots={{
-                default: () =>
-                  this.modelValue ? (
-                    <video
-                      ref="videoUpload"
-                      class={styles.uploadImg}
-                      src={this.modelValue + '#t=1,4'}
-                      // poster={this.posterUrl || ''}
-                    />
-                  ) : (
-                    <div class={styles.uploader}>
-                      <Icon name={iconUploader} size="32" />
-                      <p class={styles.uploaderText}>{this.tips}</p>
-                    </div>
-                  )
-              }}
-            />
-          </>
-        )}
-      </div>
-    )
-  }
-})
+import request from '@/helpers/request'
+import { Icon, Toast, Uploader, Image } from 'vant'
+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 { getOssUploadUrl, state } from '@/state'
+
+export default defineComponent({
+  name: 'ColUploadVideo',
+  props: {
+    modelValue: String,
+    posterUrl: String,
+    tips: {
+      type: String,
+      default: '点击上传'
+    },
+    nativeUpload: {
+      // 是否使用原生上传, 且当前环境为app才会生效
+      type: Boolean,
+      default: true
+    },
+    size: {
+      type: Number,
+      default: 30
+    },
+    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)
+      if (!isLt2M) {
+        Toast(`上传视频大小不能超过 ${this.size}MB`)
+        return false
+      }
+      return true
+    },
+    beforeDelete(file: any, detail: { index: any }) {
+      // this.dataModel.splice(detail.index, 1)
+      return true
+    },
+    async afterRead(file: any, detail: any) {
+      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
+            }
+          }
+        })
+        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
+        }
+        const formData = new FormData()
+        for (const key in obj) {
+          formData.append(key, obj[key])
+        }
+        formData.append('file', file.file)
+        await umiRequest(getOssUploadUrl(this.bucket), {
+          method: 'POST',
+          data: formData
+        })
+        const uploadUrl = getOssUploadUrl(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) {
+      this.$emit('update:modelValue', null)
+      e.stopPropagation()
+    },
+    onNativeUpload() {
+      postMessage(
+        { api: 'chooseFile', content: { type: 'video', bucket: this.bucket } },
+        (res: any) => {
+          // this.posterUrlInner = res.firstFrameImg
+          this.$emit('update:modelValue', res.fileUrl)
+          // this.$emit('update:posterUrl', res.firstFrameImg)
+        }
+      )
+    },
+    getVideoBase64(url: string) {
+      return new Promise(function (resolve) {
+        let dataURL = ''
+        const video = document.createElement('video')
+        video.setAttribute('crossOrigin', 'anonymous') // 处理跨域
+        video.setAttribute('src', url)
+        video.setAttribute('preload', 'auto')
+        video.addEventListener('loadeddata', function () {
+          console.log(video, 'video loadeddata')
+          const canvas = document.createElement('canvas')
+          console.log('video.clientWidth', video.videoWidth) // 视频宽
+          console.log('video.clientHeight', video.videoHeight) // 视频高
+          const width = video.videoWidth || 750 // canvas的尺寸和图片一样
+          const height = video.videoHeight || 500 // 设置默认宽高为 750 * 500
+          canvas.width = width
+          canvas.height = height
+          ;(canvas as any)
+            .getContext('2d')
+            .drawImage(video, 0, 0, width, height) // 绘制canvas
+          dataURL = canvas.toDataURL('image/jpeg') // 转换为base64
+          resolve(dataURL)
+        })
+      })
+    }
+  },
+  render() {
+    useCustomFieldValue(() => this.modelValue)
+    return (
+      <div class={styles['uploader-section']}>
+        {this.modelValue && this.deletable ? (
+          <Icon
+            name="cross"
+            onClick={this.onClose}
+            class={styles['img-close']}
+          />
+        ) : null}
+        {browser().isApp && this.nativeUpload ? (
+          <div onClick={this.onNativeUpload} style={{ height: '100%' }}>
+            {this.modelValue ? (
+              <video
+                ref="videoUpload"
+                class={styles.uploadImg}
+                src={this.modelValue + '#t=1,4'}
+                // poster={iconUploadPoster}
+              />
+            ) : (
+              <div class={styles.uploader}>
+                <Icon name={iconUploader} size="32" />
+                <p class={styles.uploaderText}>{this.tips}</p>
+              </div>
+            )}
+          </div>
+        ) : (
+          <>
+            {/* @ts-ignore */}
+            <Uploader
+              accept=".mp4"
+              afterRead={this.afterRead}
+              beforeRead={this.beforeRead}
+              beforeDelete={this.beforeDelete}
+              v-slots={{
+                default: () =>
+                  this.modelValue ? (
+                    <video
+                      ref="videoUpload"
+                      class={styles.uploadImg}
+                      src={this.modelValue + '#t=1,4'}
+                      // poster={this.posterUrl || ''}
+                    />
+                  ) : (
+                    <div class={styles.uploader}>
+                      <Icon name={iconUploader} size="32" />
+                      <p class={styles.uploaderText}>{this.tips}</p>
+                    </div>
+                  )
+              }}
+            />
+          </>
+        )}
+      </div>
+    )
+  }
+})

+ 237 - 238
src/components/col-upload/index.tsx

@@ -1,238 +1,237 @@
-import { Icon, Image, Toast, Uploader } from 'vant'
-import { defineComponent } from 'vue'
-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 { getOssUploadUrl, state } from '@/state'
-
-export default defineComponent({
-  name: 'col-upload',
-  props: {
-    modelValue: String,
-    tips: {
-      type: String,
-      default: '点击上传'
-    },
-    deletable: {
-      type: Boolean,
-      default: true
-    },
-    native: {
-      // 是否原生上传
-      type: Boolean,
-      default: false
-    },
-    cropper: {
-      // 是否进行裁切
-      type: Boolean,
-      default: false
-    },
-    options: {
-      // 裁切需要参数
-      type: Object,
-      default: {}
-    },
-    uploadSize: {
-      // 上传图片大小
-      type: Number,
-      default: 5
-    },
-    onUploadChange: {
-      type: Function,
-      default: (url: string) => {}
-    },
-    bucket: {
-      type: String,
-      default: 'daya'
-    }
-  },
-  methods: {
-    nativeUpload() {
-      postMessage(
-        {
-          api: 'chooseFile',
-          content: { type: 'img', max: 1, bucket: this.bucket }
-        },
-        (res: any) => {
-          console.log(res, 'fileUrl')
-          this.$emit('update:modelValue', res.fileUrl)
-        }
-      )
-    },
-    beforeRead(file: any) {
-      console.log(file, 'beforeRead')
-      const isLt2M = file.size / 1024 / 1024 < this.uploadSize
-      if (!isLt2M) {
-        Toast(`上传文件大小不能超过 ${this.uploadSize}MB`)
-        return false
-      }
-      return true
-    },
-    beforeDelete(file: any, detail: { index: any }) {
-      // this.dataModel.splice(detail.index, 1)
-      return true
-    },
-    async afterRead(file: any, detail: any) {
-      try {
-        file.status = 'uploading'
-        file.message = '上传中...'
-        await this.uploadFile(file.file)
-      } catch (error) {
-        //
-        console.log(error, '2323')
-        Toast.clear()
-      }
-    },
-    onClose(e: any) {
-      this.$emit('update:modelValue', null)
-      this.onUploadChange()
-      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'
-        let tempName = file.name || ''
-        const fileName = tempName && tempName.replace(/ /gi, '_')
-        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, fileName)
-        await umiRequest(getOssUploadUrl(this.bucket), {
-          method: 'POST',
-          data: formData
-        })
-        console.log(getOssUploadUrl(this.bucket) + key)
-        const uploadUrl = getOssUploadUrl(this.bucket) + key
-        Toast.clear()
-        this.$emit('update:modelValue', uploadUrl)
-        this.onUploadChange(uploadUrl)
-      } catch (error) {
-        console.log(error, 'uploadFile')
-      }
-    }
-  },
-  render() {
-    useCustomFieldValue(() => this.modelValue)
-    return (
-      <div class={styles['uploader-section']}>
-        {this.modelValue && this.deletable ? (
-          <Icon
-            name="cross"
-            onClick={this.onClose}
-            class={styles['img-close']}
-          />
-        ) : null}
-        {this.cropper && !this.native ? (
-          <div class={styles['col-uploader']}>
-            {this.modelValue ? (
-              <Image
-                fit="cover"
-                position="center"
-                class={styles.uploadImg}
-                src={this.modelValue}
-              />
-            ) : (
-              <div class={styles.uploader}>
-                <Icon name={iconUploader} size="32" />
-                <p class={styles.uploaderText}>{this.tips}</p>
-              </div>
-            )}
-            <ColCropper option={this.options} getFile={this.getFile} />
-          </div>
-        ) : this.native ? (
-          <div
-            style={{
-              display: 'flex',
-              alignItems: 'center',
-              justifyContent: 'center',
-              height: '100%'
-            }}
-            onClick={this.nativeUpload}
-          >
-            {this.modelValue ? (
-              <Image
-                fit="cover"
-                position="center"
-                class={styles.uploadImg}
-                src={this.modelValue}
-              />
-            ) : (
-              <div class={styles.uploader}>
-                <Icon name={iconUploader} size="32" />
-                <p class={styles.uploaderText}>{this.tips}</p>
-              </div>
-            )}
-          </div>
-        ) : (
-          <Uploader
-            afterRead={this.afterRead}
-            beforeRead={this.beforeRead}
-            beforeDelete={this.beforeDelete}
-            v-slots={{
-              default: () =>
-                this.modelValue ? (
-                  <Image
-                    fit="cover"
-                    position="center"
-                    class={styles.uploadImg}
-                    src={this.modelValue}
-                  />
-                ) : (
-                  <div class={styles.uploader}>
-                    <Icon name={iconUploader} size="32" />
-                    <p class={styles.uploaderText}>{this.tips}</p>
-                  </div>
-                )
-            }}
-          />
-        )}
-      </div>
-    )
-  }
-})
+import { Icon, Image, Toast, Uploader } from 'vant'
+import { defineComponent } from 'vue'
+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 { getOssUploadUrl, state } from '@/state'
+
+export default defineComponent({
+  name: 'col-upload',
+  props: {
+    modelValue: String,
+    tips: {
+      type: String,
+      default: '点击上传'
+    },
+    deletable: {
+      type: Boolean,
+      default: true
+    },
+    native: {
+      // 是否原生上传
+      type: Boolean,
+      default: false
+    },
+    cropper: {
+      // 是否进行裁切
+      type: Boolean,
+      default: false
+    },
+    options: {
+      // 裁切需要参数
+      type: Object,
+      default: {}
+    },
+    uploadSize: {
+      // 上传图片大小
+      type: Number,
+      default: 5
+    },
+    onUploadChange: {
+      type: Function,
+      default: (url: string) => {}
+    },
+    bucket: {
+      type: String,
+      default: 'daya'
+    }
+  },
+  methods: {
+    nativeUpload() {
+      postMessage(
+        {
+          api: 'chooseFile',
+          content: { type: 'img', max: 1, bucket: this.bucket }
+        },
+        (res: any) => {
+          console.log(res, 'fileUrl')
+          this.$emit('update:modelValue', res.fileUrl)
+        }
+      )
+    },
+    beforeRead(file: any) {
+      console.log(file, 'beforeRead')
+      const isLt2M = file.size / 1024 / 1024 < this.uploadSize
+      if (!isLt2M) {
+        Toast(`上传文件大小不能超过 ${this.uploadSize}MB`)
+        return false
+      }
+      return true
+    },
+    beforeDelete(file: any, detail: { index: any }) {
+      // this.dataModel.splice(detail.index, 1)
+      return true
+    },
+    async afterRead(file: any, detail: any) {
+      try {
+        file.status = 'uploading'
+        file.message = '上传中...'
+        await this.uploadFile(file.file)
+      } catch (error) {
+        //
+        console.log(error, '2323')
+        Toast.clear()
+      }
+    },
+    onClose(e: any) {
+      this.$emit('update:modelValue', null)
+      this.onUploadChange()
+      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 tempName = file.name || ''
+        const fileName = tempName && tempName.replace(/ /gi, '_')
+        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
+            }
+          }
+        })
+        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
+        }
+        const formData = new FormData()
+        for (const key in obj) {
+          formData.append(key, obj[key])
+        }
+        formData.append('file', file, fileName)
+        await umiRequest(getOssUploadUrl(this.bucket), {
+          method: 'POST',
+          data: formData
+        })
+        console.log(getOssUploadUrl(this.bucket) + key)
+        const uploadUrl = getOssUploadUrl(this.bucket) + key
+        Toast.clear()
+        this.$emit('update:modelValue', uploadUrl)
+        this.onUploadChange(uploadUrl)
+      } catch (error) {
+        console.log(error, 'uploadFile')
+      }
+    }
+  },
+  render() {
+    useCustomFieldValue(() => this.modelValue)
+    return (
+      <div class={styles['uploader-section']}>
+        {this.modelValue && this.deletable ? (
+          <Icon
+            name="cross"
+            onClick={this.onClose}
+            class={styles['img-close']}
+          />
+        ) : null}
+        {this.cropper && !this.native ? (
+          <div class={styles['col-uploader']}>
+            {this.modelValue ? (
+              <Image
+                fit="cover"
+                position="center"
+                class={styles.uploadImg}
+                src={this.modelValue}
+              />
+            ) : (
+              <div class={styles.uploader}>
+                <Icon name={iconUploader} size="32" />
+                <p class={styles.uploaderText}>{this.tips}</p>
+              </div>
+            )}
+            <ColCropper option={this.options} getFile={this.getFile} />
+          </div>
+        ) : this.native ? (
+          <div
+            style={{
+              display: 'flex',
+              alignItems: 'center',
+              justifyContent: 'center',
+              height: '100%'
+            }}
+            onClick={this.nativeUpload}
+          >
+            {this.modelValue ? (
+              <Image
+                fit="cover"
+                position="center"
+                class={styles.uploadImg}
+                src={this.modelValue}
+              />
+            ) : (
+              <div class={styles.uploader}>
+                <Icon name={iconUploader} size="32" />
+                <p class={styles.uploaderText}>{this.tips}</p>
+              </div>
+            )}
+          </div>
+        ) : (
+          <Uploader
+            afterRead={this.afterRead}
+            beforeRead={this.beforeRead}
+            beforeDelete={this.beforeDelete}
+            v-slots={{
+              default: () =>
+                this.modelValue ? (
+                  <Image
+                    fit="cover"
+                    position="center"
+                    class={styles.uploadImg}
+                    src={this.modelValue}
+                  />
+                ) : (
+                  <div class={styles.uploader}>
+                    <Icon name={iconUploader} size="32" />
+                    <p class={styles.uploaderText}>{this.tips}</p>
+                  </div>
+                )
+            }}
+          />
+        )}
+      </div>
+    )
+  }
+})

+ 16 - 0
src/router/routes-tenant.ts

@@ -186,6 +186,14 @@ export default [
         }
       },
       {
+        path: '/goodsDetail',
+        name: 'goodsDetail',
+        component: () => import('@/tenant/trade/detail'),
+        meta: {
+          title: '订单详情'
+        }
+      },
+      {
         path: '/music-songbook',
         component: () => import('@/tenant/music/search/header'),
         meta: {
@@ -207,6 +215,14 @@ export default [
             meta: {
               title: '乐谱库'
             }
+          },
+          {
+            path: '/music-songbook/result',
+            name: 'musicSongresult',
+            component: () => import('@/tenant/music/search/search-result'),
+            meta: {
+              title: '搜索结果'
+            }
           }
         ]
       },

+ 5 - 3
src/teacher/share-page/share-album/index.tsx

@@ -46,9 +46,11 @@ export default defineComponent({
     const color = ref<string>('#fff')
     const heightInfo = ref<any>('auto')
 
-    const tmpUrl = `${location.origin}/student/#/music-album-detail/${
-      route.query.id
-    }?${qs.stringify(route.query)}`
+    const tmpUrl = `${location.origin}/${
+      route.query.p == 'tenant' ? 'tenant.html' : 'student/'
+    }#/music-album-detail/${route.query.id}?${qs.stringify(route.query)}`
+
+    console.log(tmpUrl, 'tmpUrl')
     const jumpUrl = ref<string>(tmpUrl)
 
     const FetchList = async (id?: any) => {

+ 3 - 3
src/teacher/share-page/share-music/index.tsx

@@ -94,9 +94,9 @@ export default defineComponent({
       radio: 'staff' // staff first fixed
     })
 
-    const tmpUrl = `${location.origin}/student/#/music-detail?${qs.stringify(
-      route.query
-    )}`
+    const tmpUrl = `${location.origin}/${
+      route.query.p == 'tenant' ? 'tenant' : 'student'
+    }/#/music-detail?${qs.stringify(route.query)}`
     const jumpUrl = ref<string>(tmpUrl)
 
     const colors: any = {

+ 1 - 1
src/tenant/music/album-detail/index.tsx

@@ -232,7 +232,7 @@ export default defineComponent({
           shareDiscount.value = res.data.discount || 0
         }
       }
-      shareUrl.value = `${location.origin}/teacher#/shareAblum?id=${id}&recomUserId=${userId}&activityId=${activityId}&userType=${state.platformType}`
+      shareUrl.value = `${location.origin}/teacher/#/shareAblum?id=${id}&recomUserId=${userId}&activityId=${activityId}&userType=${state.platformType}&p=tenant`
       // console.log(shareUrl.value, 'shareUrl')
       shareStatus.value = true
     }

+ 14 - 1
src/tenant/music/album/index.tsx

@@ -26,9 +26,19 @@ export default defineComponent({
     defauleParams: {
       type: Object,
       default: () => ({})
+    },
+    showLight: {
+      type: Boolean,
+      default: false
+    },
+    /** 需要高亮的文案 */
+    lightText: {
+      type: String,
+      default: ''
     }
   },
-  setup({ hideSearch, defauleParams }, { expose }) {
+  setup({ hideSearch, defauleParams, showLight, lightText }, { expose }) {
+    const lightTextC = ref(lightText)
     const { isLoading, state } = useAsyncState(
       request(baseState.platformApi + '/MusicTag/tree', {
         params: {
@@ -105,6 +115,7 @@ export default defineComponent({
     const onSearch = (value: string) => {
       params.page = 1
       params.search = value
+      lightTextC.value = value
       data.value = null
       FetchList()
     }
@@ -319,6 +330,8 @@ export default defineComponent({
             {data.value && data.value.rows.length ? (
               <div class={styles.musicGrid}>
                 <MusicGrid
+                  showLight={showLight}
+                  lightText={lightTextC.value}
                   list={data.value.rows}
                   onGoto={(n: any) => {
                     router.push({

+ 43 - 22
src/tenant/music/component/music-grid/index.tsx

@@ -10,6 +10,15 @@ export default defineComponent({
       type: Boolean,
       default: false
     },
+    showLight: {
+      type: Boolean,
+      default: false
+    },
+    /** 需要高亮的文案 */
+    lightText: {
+      type: String,
+      default: ''
+    },
     list: {
       type: Array as any,
       default: () => []
@@ -20,30 +29,42 @@ export default defineComponent({
     return () => (
       <div class={styles.theMusicGrid}>
         <Grid border={false} columnNum={3}>
-          {props.list.map((n: any) => (
-            <GridItem>
-              <div class={styles.item} onClick={() => emit('goto', n)}>
-                <div class={styles.imgWrap}>
-                  {n.paymentType === 'CHARGE' && !props.isHiddenTag && (
-                    <span class={styles.albumType}>付费</span>
-                  )}
-                  <Image
-                    class={styles.image}
-                    width="100%"
-                    height="100%"
-                    fit="cover"
-                    src={n.albumCoverUrl}
-                  />
-                  <div class={styles.model}>
-                    <Icon name={IconXin} />
-                    <span class={styles.num}>{n.albumFavoriteCount}人</span>
+          {props.list.map((n: any) => {
+            let name = ''
+            console.log(props.lightText, 'props.lightText')
+            if (props.lightText) {
+              name = n.albumName.replace(
+                props.lightText,
+                `<span style="color: #FE2451">${props.lightText}</span>`
+              )
+            } else {
+              name = n.albumName
+            }
+            return (
+              <GridItem>
+                <div class={styles.item} onClick={() => emit('goto', n)}>
+                  <div class={styles.imgWrap}>
+                    {n.paymentType === 'CHARGE' && !props.isHiddenTag && (
+                      <span class={styles.albumType}>付费</span>
+                    )}
+                    <Image
+                      class={styles.image}
+                      width="100%"
+                      height="100%"
+                      fit="cover"
+                      src={n.albumCoverUrl}
+                    />
+                    <div class={styles.model}>
+                      <Icon name={IconXin} />
+                      <span class={styles.num}>{n.albumFavoriteCount}人</span>
+                    </div>
                   </div>
+                  <div class={styles.title} v-html={name}></div>
+                  {/* <div class={styles.des}>共{n.musicSheetCount}首</div> */}
                 </div>
-                <div class={styles.title}>{n.albumName}</div>
-                {/* <div class={styles.des}>共{n.musicSheetCount}首</div> */}
-              </div>
-            </GridItem>
-          ))}
+              </GridItem>
+            )
+          })}
         </Grid>
       </div>
     )

+ 2 - 3
src/tenant/music/component/song/index.tsx

@@ -85,9 +85,8 @@ export default defineComponent({
                 <div class={styles.top}>
                   <span
                     class={[styles.title, props.musicNameClass, 'van-ellipsis']}
-                  >
-                    {n.musicSheetName}
-                  </span>
+                    v-html={n.musicSheetName}
+                  ></span>
                   <span
                     class={[styles.singer, props.authorClass, 'van-ellipsis']}
                   >

+ 41 - 2
src/tenant/music/list/index.tsx

@@ -20,6 +20,7 @@ import iconSearch from './icons/icon_search.png'
 import iconFree from './icons/icon-free.png'
 import { browser } from '@/helpers/utils'
 import TheSticky from '@/components/the-sticky'
+import { tmpdir } from 'os'
 
 const noop = () => {
   //
@@ -47,12 +48,30 @@ export default defineComponent({
     myself: {
       type: Boolean,
       default: false
+    },
+    showLight: {
+      type: Boolean,
+      default: false
+    },
+    /** 需要高亮的文案 */
+    lightText: {
+      type: String,
+      default: ''
     }
   },
   setup(
-    { hideSearch, defauleParams, onItemClick, teacherId, myself },
+    {
+      hideSearch,
+      defauleParams,
+      onItemClick,
+      teacherId,
+      myself,
+      showLight,
+      lightText
+    },
     { expose }
   ) {
+    const lightTextC = ref(lightText)
     const { isLoading, state } = useAsyncState(
       request(baseState.platformApi + '/MusicTag/tree', {
         params: {
@@ -142,6 +161,7 @@ export default defineComponent({
     const onSearch = (value: string) => {
       params.page = 1
       params.search = value
+      lightTextC.value = value
       data.value = null
       FetchList()
     }
@@ -158,6 +178,10 @@ export default defineComponent({
         idAndName: params.search,
         createBy: teacherId
       }
+      if (hideSearch) {
+        tempParams.idAndName = tempParams.search
+        tempParams.search = null
+      }
       if (exquisiteFlag.value) {
         tempParams.chargeType = 'FREE'
       }
@@ -176,6 +200,21 @@ export default defineComponent({
           data.value.rows = result
         }
         data.value = data.value || res.data
+
+        console.log(showLight, 'showLight', lightTextC.value)
+        if (showLight) {
+          const temp = data.value.rows || []
+          temp.forEach((item: any) => {
+            if (lightTextC.value) {
+              item.musicSheetName = item.musicSheetName.replace(
+                lightTextC.value,
+                `<span style="color: #FE2451">${lightTextC.value}</span>`
+              )
+            }
+          })
+          data.value.rows = temp
+          console.log(temp, 'temp')
+        }
         params.page = res.data.pageNo + 1
         finished.value = res.data.pageNo >= res.data.totalPage
       } catch (error) {
@@ -371,7 +410,7 @@ export default defineComponent({
               <img class={styles.bgImg} src={bgImg} />
             </div>
           )}
-          <div class={styles.alumnList}>
+          <div class={[styles.alumnList, 'searchClass']}>
             <List
               loading={loading.value}
               finished={finished.value}

+ 2 - 3
src/tenant/music/music-detail/index.tsx

@@ -204,8 +204,7 @@ export default defineComponent({
           postData: {
             filename: fileName,
             acl: 'public-read',
-            key: keyTime,
-            unknowValueField: []
+            key: keyTime
           }
         }
 
@@ -497,7 +496,7 @@ export default defineComponent({
         })
         let url =
           location.origin +
-          `/teacher/#/shareMusic?id=${musicDetail.value?.id}&recomUserId=${state.user.data?.userId}&userType=${state.platformType}`
+          `/teacher/#/shareMusic?id=${musicDetail.value?.id}&recomUserId=${state.user.data?.userId}&userType=${state.platformType}&p=tenant`
         // 判断是否有活动
         if (res.data.discount === 1) {
           url += `&activityId=${res.data.activityId}`

+ 1 - 2
src/tenant/music/music-detail/new-index.tsx

@@ -192,8 +192,7 @@ export default defineComponent({
           postData: {
             filename: fileName,
             acl: 'public-read',
-            key: keyTime,
-            unknowValueField: []
+            key: keyTime
           }
         }
 

+ 47 - 1
src/tenant/music/search/all-search.module.less

@@ -1 +1,47 @@
-.albumSection {}
+.albumSection {
+  background: url('../../images/album-bg.png') no-repeat top center #fff;
+  background-size: contain;
+  border-radius: 16px;
+  margin: 12px 13px;
+  padding: 12px 12px 0;
+
+  .albumTitle {
+    font-size: 16px;
+    font-weight: 500;
+    color: #131415;
+    line-height: 22px;
+    padding-bottom: 24px;
+  }
+}
+
+.searchAllTitle {
+  background: #fff;
+
+  .albumTitle {
+    border-bottom: 1px solid #F2F2F2;
+    padding-bottom: 12px;
+    margin-bottom: 12px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .more {
+    font-size: 12px;
+    color: #AAAAAA;
+    line-height: 17px;
+  }
+
+  &.musicGroup {
+    .albumTitle {
+      margin-bottom: 0;
+    }
+  }
+
+  :global {
+    .searchClass {
+      margin: 0;
+      padding: 0;
+    }
+  }
+}

+ 6 - 1
src/tenant/music/search/all-search.tsx

@@ -3,6 +3,7 @@ import styles from './all-search.module.less'
 import { useRoute, useRouter } from 'vue-router'
 import MusicGrid from '../component/music-grid'
 import request from '@/helpers/request'
+import MusicSwiper from './music-swiper'
 
 export default defineComponent({
   name: 'MusicSearch',
@@ -29,7 +30,6 @@ export default defineComponent({
             rows: 3
           }
         })
-        console.log(data)
         state.albumList = data.rows || []
       } catch {
         //
@@ -44,6 +44,7 @@ export default defineComponent({
     return () => (
       <div class={styles.allSearch}>
         <div class={styles.albumSection}>
+          <div class={styles.albumTitle}>热门专辑</div>
           <div class={styles.musicGrid}>
             <MusicGrid
               list={state.albumList}
@@ -58,6 +59,10 @@ export default defineComponent({
             />
           </div>
         </div>
+
+        <div class={styles.musicGroup}>
+          <MusicSwiper defauleParams={props.defauleParams} />
+        </div>
       </div>
     )
   }

+ 168 - 26
src/tenant/music/search/header.tsx

@@ -6,9 +6,9 @@ import {
   onBeforeRouteUpdate
 } from 'vue-router'
 import { defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'
-import mitt from 'mitt'
+import mitt from 'eventemitter3'
 import Search from '@/components/col-search'
-import { useLocalStorage } from '@vueuse/core'
+import { useLocalStorage, useThrottleFn } from '@vueuse/core'
 import styles from './index.module.less'
 import classNames from 'classnames'
 import { getRandomKey } from '../music'
@@ -17,11 +17,35 @@ import { SubjectEnum, useSubjectId } from '@/helpers/hooks'
 import { state } from '@/state'
 import TheSticky from '@/components/the-sticky'
 import bgImg from '../../images/bg-image-search.png'
+import iconSearch from './icon-search.png'
+import request from '@/helpers/request'
+import { browser } from '@/helpers/utils'
+import ColHeader from '@/components/col-header'
+import ColResult from '@/components/col-result'
+import { postMessage } from '@/helpers/native-message'
 
-export const mitter = mitt()
+export const mitter = new mitt()
 
 const selectTagRef = ref()
 
+export const openWebViewOrWeb = (url: string, callback: any) => {
+  if (browser().isApp) {
+    postMessage({
+      api: 'openWebView',
+      content: {
+        url: url, //`${location.origin}/tenant/#/music-album-detail/${item.id}`,
+        orientation: 1,
+        isHideTitle: false
+      }
+    })
+  } else {
+    // router.push({
+    //   path: '/music-album-detail/' + item.id
+    // })
+    callback && callback()
+  }
+}
+
 export default defineComponent({
   name: 'MusicSearchHeader',
   setup() {
@@ -47,16 +71,22 @@ export default defineComponent({
       }
     }
 
-    const searchInputRef = ref()
     localStorage.setItem('behaviorId', getRandomKey())
     const router = useRouter()
     const route = useRoute()
+    const searchResultStatus = ref(false)
     const keyword = ref('')
     const tagids = ref('')
     const words = useLocalStorage<string[]>('music-search', [])
     const activeTab = ref('all')
 
-    onBeforeRouteUpdate(() => {
+    if (route.path === '/music-songbook/result') {
+      keyword.value = route.query.search as string
+      tagids.value = ''
+      activeTab.value = 'all'
+    }
+
+    onBeforeRouteUpdate((to, form) => {
       const getSubject: any = useSubjectId(SubjectEnum.SEARCH)
       subject.name = getSubject.name || '全部声部'
       subject.id = getSubject.id
@@ -70,6 +100,12 @@ export default defineComponent({
           console.log(error)
         }
       }
+      if (to.path === '/music-songbook/result') {
+        keyword.value = to.query.search as string
+        console.log(keyword.value, 'value', route.query)
+        tagids.value = ''
+        activeTab.value = 'all'
+      }
       return true
     })
 
@@ -77,7 +113,33 @@ export default defineComponent({
       mitter.emit('changeTab', val)
     })
 
+    // 输入时搜索
+    const searchList = ref<any>([])
+    const onInputSearch = useThrottleFn(async (val: any) => {
+      try {
+        const { data } = await request.post('/api-student/music/sheet/search', {
+          hideLoading: true,
+          data: {
+            subjectId: subjects.id,
+            name: val,
+            rows: 10
+          }
+        })
+        const tempMusics = data.musicNames || []
+        tempMusics.forEach((item: any, index: number) => {
+          if (index < 10) {
+            item.name = item.name.replace(val, `<span>${val}</span>`)
+          }
+        })
+        searchList.value = tempMusics
+        searchResultStatus.value = keyword.value ? true : false
+      } catch {
+        //
+      }
+    }, 300)
+
     const onSearch = val => {
+      console.log('object :>> ', val)
       keyword.value = val
       const indexOf = words.value.indexOf(val)
       if (indexOf > -1) {
@@ -85,10 +147,24 @@ export default defineComponent({
       }
       if (val) {
         words.value.unshift(val)
-        console.log(words.value.length, 'words.value.length')
+        // console.log(words.value.length, 'words.value.length')
         words.value.length = Math.min(words.value.length, 10)
+        if (route.path === '/music-songbook/search') defaultClose()
+      }
+      // mitter.emit('search', val)
+      if (route.path !== '/music-songbook/result') {
+        router.replace({
+          path: '/music-songbook/result',
+          query: {
+            search: val
+          }
+        })
+        searchResultStatus.value = false
+        searchList.value = []
+      } else {
+        searchResultStatus.value = false
+        mitter.emit('search', val)
       }
-      mitter.emit('search', val)
     }
 
     const onComfirmSubject = (item: any) => {
@@ -107,8 +183,6 @@ export default defineComponent({
       subject.show = false
     }
 
-    onMounted(() => {})
-
     const getSubject: any = useSubjectId(SubjectEnum.SEARCH)
     const subject = reactive({
       show: false,
@@ -159,18 +233,36 @@ export default defineComponent({
       })
     }
     // 首先调用默认收起的方法
-    defaultClose()
+    if (route.path === '/music-songbook/search') defaultClose()
 
     return () => {
       return (
         <div class={styles.search}>
           <div class={styles.sticky}>
             <TheSticky position="top">
+              <ColHeader isFixed={false} background="transparent" title=" " />
               <Search
                 modelValue={keyword.value}
                 background="transparent"
-                ref={searchInputRef}
-                onSearch={onSearch}
+                onInput={(val: any) => {
+                  keyword.value = val
+                  if (val) {
+                    onInputSearch(val)
+                  } else {
+                    searchList.value = []
+                    searchResultStatus.value = false
+                    if (route.path === '/music-songbook/result') {
+                      router.replace('/music-songbook/search')
+                    }
+                  }
+                }}
+                onSearch={(val: any) => {
+                  if (!val) return
+                  keyword.value = val
+                  console.log(val, 'val')
+                  onSearch(val)
+                  // searchResultStatus.value = true
+                }}
                 type="tenant"
                 v-slots={{
                   left: () => (
@@ -189,19 +281,22 @@ export default defineComponent({
                   )
                 }}
               />
-              {/* {route.path === '/music-songbook/search' && (
-              <Tabs
-                color="var(--van-primary)"
-                background="transparent"
-                lineWidth={20}
-                shrink
-                v-model:active={activeTab.value}
-                onChange={val => (activeTab.value = val)}
-              >
-                <Tab title="单曲" name="songe"></Tab>
-                <Tab title="专辑" name="album"></Tab>
-              </Tabs>
-            )} */}
+              {route.path === '/music-songbook/result' &&
+                !searchResultStatus.value && (
+                  <Tabs
+                    color="var(--van-primary)"
+                    background="transparent"
+                    lineWidth={20}
+                    shrink
+                    class={styles.tagTabs}
+                    v-model:active={activeTab.value}
+                    onChange={val => (activeTab.value = val)}
+                  >
+                    <Tab title="综合" name="all"></Tab>
+                    <Tab title="单曲" name="songe"></Tab>
+                    <Tab title="专辑" name="album"></Tab>
+                  </Tabs>
+                )}
             </TheSticky>
             <img class={styles.bgImg} src={bgImg} />
           </div>
@@ -223,7 +318,10 @@ export default defineComponent({
                       round
                       class={[styles.searchKeyword, 'van-ellipsis']}
                       key={item}
-                      onClick={() => onSearch(item)}
+                      onClick={() => {
+                        keyword.value = item
+                        onSearch(item)
+                      }}
                     >
                       {item}
                     </Tag>
@@ -277,6 +375,50 @@ export default defineComponent({
               onComfirm={onComfirmSubject}
             />
           </Popup>
+
+          <div
+            class={[styles.searchResult]}
+            style={{ display: searchResultStatus.value ? 'block' : 'none' }}
+          >
+            <div class={styles.searchGroups}>
+              {searchList.value.map((item: any) => (
+                <div
+                  class={styles.searchItem}
+                  onClick={() => {
+                    if (item.type === 'ALBUM') {
+                      openWebViewOrWeb(
+                        `${location.origin}/tenant/#/music-album-detail/${item.id}`,
+                        () => {
+                          router.push({
+                            path: '/music-album-detail/' + item.id
+                          })
+                        }
+                      )
+                    } else {
+                      openWebViewOrWeb(
+                        `${location.origin}/tenant/#/music-detail?id=${item.id}`,
+                        () => {
+                          router.push({
+                            path: '/music-detail',
+                            query: {
+                              id: item.id
+                            }
+                          })
+                        }
+                      )
+                    }
+                  }}
+                >
+                  <img src={iconSearch} class={styles.iconSearch} />
+                  <span class={styles.rName} v-html={item.name}></span>
+                </div>
+              ))}
+
+              {searchList.value.length <= 0 && (
+                <ColResult tips="暂无搜索结果" btnStatus={false} />
+              )}
+            </div>
+          </div>
         </div>
       )
     }

二进制
src/tenant/music/search/icon-search.png


+ 139 - 0
src/tenant/music/search/index.module.less

@@ -4,6 +4,7 @@
   --van-cell-text-color: #333;
   --van-cell-value-color: #999;
   --van-cell-icon-size: 10px;
+  --van-nav-bar-height: 20px;
 
   --base-bg: #f6f8f9;
 
@@ -150,6 +151,7 @@
   --van-cell-text-color: #333;
   --van-cell-value-color: #999;
   --van-cell-icon-size: 10px;
+  --van-tab-line-height: 22px;
 
   :global {
     .van-tab {
@@ -168,7 +170,144 @@
       height: 4px;
       background: linear-gradient(90deg, #FF3C81 0%, rgba(255, 118, 166, 0.5) 100%) !important;
       border-radius: 36px 36px 0px 0px;
+      bottom: 22px;
     }
 
   }
+}
+
+.hotMusic {
+
+  padding-bottom: 20px;
+
+  .swipeItem .swipeChild {
+    // padding-left: 14px;
+    margin: 0 7px;
+  }
+
+  .swipeItem:last-child .swipeChild {
+    margin-right: 14px;
+  }
+
+  .swipeItem:first-child .swipeChild {
+    margin-left: 14px;
+  }
+
+
+  .swipeItem {
+    border-radius: 10px;
+    overflow: hidden;
+  }
+
+  .swipeChild {
+    background: url('../../images/music-bg.png') no-repeat top center #fff;
+    background-size: contain;
+  }
+
+  .swipeTitle {
+    font-size: 16px;
+    font-weight: 500;
+    color: #131415;
+    line-height: 22px;
+    padding: 12px 12px 16px;
+  }
+
+  .swipeContent {
+    padding: 0 16px 8px;
+    border-radius: 10px;
+  }
+
+  .swipe {
+    font-size: 14px;
+    color: #6A6C77;
+    line-height: 20px;
+    max-width: 80%;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    padding: 8px 0;
+
+    .num {
+      display: inline-block;
+      font-size: 16px;
+      font-family: PingFangSC-Medium, PingFang SC;
+      font-weight: 600;
+
+      color: #8A8C95;
+      // margin-right: 20px;
+      width: 21px;
+    }
+
+    .hot {
+      display: inline-block;
+      width: 16px;
+      line-height: 16px;
+      text-align: center;
+      background: #FE2451;
+      border-radius: 4px;
+      font-size: 12px;
+      font-weight: 500;
+      color: #FFFFFF;
+      vertical-align: middle;
+      margin-left: 4px;
+      transform: scale(0.9);
+    }
+
+    &.swipeTop {
+      color: #131415;
+
+      .num {
+        color: #FE2451;
+      }
+    }
+  }
+}
+
+
+// 搜索结果样式
+.searchResult {
+  position: fixed;
+  top: 0;
+  z-index: 98;
+  left: 0;
+  right: 0;
+  overflow: hidden;
+  bottom: 0;
+  padding-top: var(--header-height);
+  background: url('../../images/bg-image-search.png') no-repeat top center #fafafa;
+  background-size: 100% 214px;
+
+}
+
+.searchGroups {
+  padding-top: 10px;
+  height: 100%;
+  overflow-y: auto;
+  overflow-x: hidden;
+  font-size: 15px;
+  color: #131415;
+  line-height: 21px;
+
+  .searchItem {
+    display: flex;
+    align-items: center;
+    padding: 0 12px 20px;
+
+    .rName {
+      width: 90%;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+
+    span span {
+      color: #FE2451;
+    }
+  }
+
+  .iconSearch {
+    width: 16px;
+    height: 16px;
+    margin-right: 10px;
+  }
 }

+ 11 - 23
src/tenant/music/search/index.tsx

@@ -1,13 +1,13 @@
 import { defineComponent, onMounted, onUnmounted, ref } from 'vue'
 import { useLocalStorage } from '@vueuse/core'
 import AlbumList from '../album'
-import MusicList from '../list'
 import styles from './index.module.less'
 import { useRoute, useRouter } from 'vue-router'
 import { getRandomKey } from '../music'
 import { mitter } from './header'
 import { SubjectEnum, useSubjectId } from '@/helpers/hooks'
 import AllSearch from './all-search'
+import MusicSwiper from './music-swiper'
 
 export default defineComponent({
   name: 'MusicSearch',
@@ -27,6 +27,7 @@ export default defineComponent({
     subject.value = getSubject.id
 
     const onSearch = val => {
+      console.log(val, 'val')
       keyword.value = val
       const indexOf = words.value.indexOf(val)
       if (indexOf > -1) {
@@ -34,7 +35,7 @@ export default defineComponent({
       }
       if (val) {
         words.value.unshift(val)
-        words.value.length = Math.min(words.value.length, 5)
+        words.value.length = Math.min(words.value.length, 10)
       }
       const activeRef = activeTab.value === 'album' ? albumList : musicList
       ;(activeRef.value as any).onSearch?.(val)
@@ -94,33 +95,20 @@ export default defineComponent({
               hideSearch
               ref={albumList}
               defauleParams={{
-                // search: keyword.value,
-                // tagids: tagids.value,
                 albumTagIds: tagids.value,
                 subjectIds: subject.value
               }}
             />
           )}
           {activeTab.value === 'songe' && (
-            <MusicList
-              hideSearch
-              ref={musicList}
-              onItemClick={(item: any) => {
-                router.push({
-                  path: '/music-detail',
-                  query: {
-                    id: item.id,
-                    albumId: route.params.id
-                  }
-                })
-              }}
-              defauleParams={{
-                // search: keyword.value,
-                // tagids: tagids.value,
-                musicTagIds: tagids.value,
-                subjectIds: subject.value
-              }}
-            />
+            <div class={styles.musicGroup}>
+              <MusicSwiper
+                defauleParams={{
+                  musicTagIds: tagids.value,
+                  subjectIds: subject.value
+                }}
+              />
+            </div>
           )}
         </div>
       )

+ 129 - 0
src/tenant/music/search/music-swiper.tsx

@@ -0,0 +1,129 @@
+import { Cell, Icon, Swipe, SwipeItem } from 'vant'
+import styles from './index.module.less'
+import { defineComponent, onMounted, reactive, ref } from 'vue'
+import { useRouter } from 'vue-router'
+import request from '@/helpers/request'
+import { openDefaultWebView } from '@/state'
+// import event from '../../event'
+// import Song from '../component/song'
+
+export default defineComponent({
+  name: 'music-list',
+  props: {
+    title: {
+      type: String,
+      default: '最热曲目'
+    },
+    music: {
+      type: Array,
+      default: () => {
+        return []
+      }
+    },
+    defauleParams: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  setup(props) {
+    const router = useRouter()
+    const state = reactive({
+      musicList: [] as any
+    })
+    const arrChange = (num: number, arr: any) => {
+      const newArr = [] as any
+      arr.forEach((item: any, index: number) => {
+        if (index <= 11) {
+          newArr.push(item)
+        }
+      })
+      return newArr
+    }
+
+    const getMusicList = async () => {
+      try {
+        // 曲谱
+        const music = await request.post(
+          '/api-student/music/sheet/appMusicSheet',
+          {
+            data: {
+              ...props.defauleParams
+            }
+          }
+        )
+        const musicData = music.data || []
+        state.musicList = [
+          arrChange(12, musicData.topMusicSheet || []),
+          arrChange(12, musicData.newMusicSheet || []),
+          arrChange(12, musicData.hotMusicSheet || [])
+        ]
+      } catch {
+        //
+      }
+    }
+
+    const onDetail = (item: any) => {
+      const url =
+        location.origin + location.pathname + '#/music-detail?id=' + item.id
+      openDefaultWebView(url, () => {
+        router.push({
+          path: '/music-detail',
+          query: {
+            id: item.id
+          }
+        })
+      })
+    }
+
+    onMounted(() => {
+      getWidth()
+      getMusicList()
+    })
+
+    const swipeWidth = ref(260)
+    const swipeShow = ref(false)
+    const getWidth = () => {
+      swipeShow.value = false
+      const clientWidth =
+        document.body.clientWidth > 750 ? 750 : document.body.clientWidth
+
+      swipeWidth.value = clientWidth - 120
+      swipeShow.value = true
+    }
+    return () => (
+      <>
+        <div class={styles.hotMusic}>
+          {swipeShow.value && (
+            <Swipe showIndicators={false} loop={false} width={swipeWidth.value}>
+              {state.musicList.map((item: any, index: number) => (
+                <SwipeItem class={styles.swipeItem}>
+                  <div class={styles.swipeChild}>
+                    <div class={styles.swipeTitle}>
+                      {index === 0 && '推荐曲目'}
+                      {index === 1 && '最新曲目'}
+                      {index === 2 && '最热曲目'}
+                    </div>
+                    <div class={styles.swipeContent}>
+                      {item.map((child: any, j: number) => (
+                        <div
+                          class={[styles.swipe, j <= 2 && styles.swipeTop]}
+                          onClick={() => onDetail(child)}
+                        >
+                          <div class={styles.num}>{j + 1}</div>
+                          {child.musicSheetName}
+                          {child.exquisiteFlag === 'YES' && (
+                            <span class={styles.hot}>热</span>
+                          )}
+                        </div>
+                      ))}
+                    </div>
+                  </div>
+                </SwipeItem>
+              ))}
+            </Swipe>
+          )}
+        </div>
+      </>
+    )
+  }
+})

+ 153 - 0
src/tenant/music/search/result-all-search.tsx

@@ -0,0 +1,153 @@
+import { defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue'
+import styles from './all-search.module.less'
+import { useRoute, useRouter } from 'vue-router'
+import MusicGrid from '../component/music-grid'
+import request from '@/helpers/request'
+import MusicList from '../list'
+import { Icon } from 'vant'
+import { mitter, openWebViewOrWeb } from './header'
+import ColResult from '@/components/col-result'
+
+export default defineComponent({
+  name: 'MusicSearch',
+  props: {
+    defauleParams: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  emits: ['confirm'],
+  setup(props) {
+    const route = useRoute()
+    const router = useRouter()
+    const state = reactive({
+      albumList: [] as any,
+      loading: false,
+      defauleParams: props.defauleParams as any
+    })
+
+    const getAlbumList = async () => {
+      state.loading = true
+      try {
+        const { data } = await request.post('/api-student/music/album/list', {
+          data: {
+            ...state.defauleParams,
+            page: 1,
+            rows: 3
+          }
+        })
+        state.albumList = data.rows || []
+      } catch {
+        //
+      }
+      state.loading = false
+    }
+
+    const musicList = ref(null)
+    const onSearch = (val: any) => {
+      state.defauleParams.idAndName = val
+      getAlbumList()
+      const activeRef = musicList
+      ;(activeRef.value as any).onSearch?.(val)
+    }
+
+    // music-songbook/search
+    onMounted(() => {
+      getAlbumList()
+
+      mitter.on('search', onSearch)
+    })
+
+    onUnmounted(() => {
+      mitter.off('search', onSearch)
+    })
+    return () => (
+      <div class={styles.allSearch}>
+        <div class={[styles.albumSection, styles.searchAllTitle]}>
+          <div class={styles.albumTitle}>
+            <span>热门专辑</span>
+
+            <span
+              class={styles.more}
+              onClick={() => {
+                openWebViewOrWeb(
+                  `${location.origin}/tenant/#/music-album`,
+                  () => {
+                    router.push({
+                      path: '/music-album'
+                    })
+                  }
+                )
+              }}
+            >
+              更多 <Icon name="arrow" />
+            </span>
+          </div>
+          <div class={styles.musicGrid}>
+            <MusicGrid
+              showLight
+              lightText={state.defauleParams.idAndName}
+              list={state.albumList}
+              onGoto={(n: any) => {
+                openWebViewOrWeb(
+                  `${location.origin}/tenant/#/music-album-detail/${n.id}`,
+                  () => {
+                    router.push({
+                      name: 'music-album-detail',
+                      params: {
+                        id: n.id
+                      }
+                    })
+                  }
+                )
+              }}
+            />
+
+            {state.albumList.length <= 0 && !state.loading && (
+              <ColResult
+                tips="暂无专辑"
+                btnStatus={false}
+                classImgSize="SMALL"
+              />
+            )}
+          </div>
+        </div>
+
+        <div
+          class={[
+            styles.albumSection,
+            styles.searchAllTitle,
+            styles.musicGroup
+          ]}
+        >
+          <div class={styles.albumTitle}>单曲</div>
+          {/* <MusicSwiper defauleParams={props.defauleParams} /> */}
+          <MusicList
+            hideSearch
+            ref={musicList}
+            onItemClick={(item: any) => {
+              openWebViewOrWeb(
+                `${location.origin}/tenant/#/music-detail?id=${item.id}`,
+                () => {
+                  router.push({
+                    path: '/music-detail',
+                    query: {
+                      id: item.id
+                    }
+                  })
+                }
+              )
+            }}
+            showLight
+            lightText={state.defauleParams.idAndName}
+            defauleParams={{
+              ...props.defauleParams,
+              idAndName: props.defauleParams.search
+              // search: null
+            }}
+          />
+        </div>
+      </div>
+    )
+  }
+})

+ 133 - 0
src/tenant/music/search/search-result.tsx

@@ -0,0 +1,133 @@
+import { defineComponent, onMounted, onUnmounted, ref } from 'vue'
+import { useLocalStorage } from '@vueuse/core'
+import AlbumList from '../album'
+import styles from './index.module.less'
+import { useRoute, useRouter } from 'vue-router'
+import { getRandomKey } from '../music'
+import { mitter } from './header'
+import { SubjectEnum, useSubjectId } from '@/helpers/hooks'
+import AllSearch from './result-all-search'
+import MusicList from '../list'
+
+export default defineComponent({
+  name: 'MusicSearch',
+  emits: ['confirm'],
+  setup() {
+    localStorage.setItem('behaviorId', getRandomKey())
+    const route = useRoute()
+    const router = useRouter()
+    const keyword = ref(route.query.search || '')
+    const tagids = ref(route.query.tagids || '')
+    const subject = ref()
+    const tagVisibility = ref(false)
+    const words = useLocalStorage<string[]>('music-search', [])
+    const activeTab = ref('all')
+
+    const getSubject: any = useSubjectId(SubjectEnum.SEARCH)
+    subject.value = getSubject.id
+
+    const onSearch = val => {
+      if (activeTab.value !== 'all') {
+        keyword.value = val
+        const indexOf = words.value.indexOf(val)
+        if (indexOf > -1) {
+          words.value.splice(indexOf, 1)
+        }
+        if (val) {
+          words.value.unshift(val)
+          words.value.length = Math.min(words.value.length, 10)
+        }
+        const activeRef = activeTab.value === 'album' ? albumList : musicList
+        ;(activeRef.value as any)?.onSearch?.(val)
+      }
+    }
+
+    const onComfirm = tags => {
+      const data = Object.values(tags).flat().filter(Boolean).join(',')
+      tagids.value = data
+      const activeRef = activeTab.value === 'album' ? albumList : musicList
+      ;(activeRef.value as any).onComfirm?.(tags)
+      tagVisibility.value = false
+    }
+
+    const onConfirmSubject = (item: any) => {
+      subject.value = item.id
+      const activeRef = activeTab.value === 'album' ? albumList : musicList
+      ;(activeRef.value as any).onComfirmSubject?.(item)
+    }
+
+    const albumList = ref(null)
+    const musicList = ref(null)
+
+    const changeTab = (val: any) => {
+      activeTab.value = val
+    }
+
+    onMounted(() => {
+      mitter.on('changeTab', changeTab)
+      mitter.on('search', onSearch)
+      mitter.on('confirm', onComfirm)
+      mitter.on('confirmSubject', onConfirmSubject)
+    })
+
+    onUnmounted(() => {
+      mitter.off('changeTab', changeTab)
+      mitter.off('search', onSearch)
+      mitter.off('confirm', onComfirm)
+      mitter.off('confirmSubject', onConfirmSubject)
+    })
+
+    return () => {
+      return (
+        <div class={styles.search}>
+          {activeTab.value === 'all' && (
+            <AllSearch
+              defauleParams={{
+                idAndName: keyword.value,
+                albumTagIds: tagids.value,
+                subjectIds: subject.value
+              }}
+            />
+          )}
+          {activeTab.value === 'album' && (
+            <AlbumList
+              hideSearch
+              showLight
+              lightText={keyword.value as any}
+              ref={albumList}
+              defauleParams={{
+                search: keyword.value,
+                albumTagIds: tagids.value,
+                subjectIds: subject.value
+              }}
+            />
+          )}
+          {activeTab.value === 'songe' && (
+            <div class={styles.musicGroup}>
+              <MusicList
+                hideSearch
+                showLight
+                lightText={keyword.value as any}
+                defauleParams={{
+                  idAndName: keyword.value,
+                  albumTagIds: tagids.value,
+                  subjectIds: subject.value
+                }}
+                ref={musicList}
+                onItemClick={(item: any) => {
+                  router.push({
+                    path: '/music-detail',
+                    query: {
+                      id: item.id,
+                      albumId: route.params.id
+                    }
+                  })
+                }}
+              />
+            </div>
+          )}
+        </div>
+      )
+    }
+  }
+})

+ 0 - 1
src/tenant/music/search/select-subject.tsx

@@ -41,7 +41,6 @@ export default defineComponent({
     }
   },
   mounted() {
-    console.log(this.subject, '12')
     this.subject = {
       name: this.searchParams.id ? this.searchParams.name : '全部声部',
       id: this.searchParams.id || ''

+ 11 - 1
src/tenant/music/train-list/index.module.less

@@ -38,6 +38,16 @@
     .van-dropdown-menu__title:after {
       border-color: transparent transparent rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.4);
     }
+
+    .van-dropdown-item__content {
+      border-radius: 0px 0px 20px 20px;
+
+    }
+
+  }
+
+  .titleActive {
+    color: #FE2451;
   }
 }
 
@@ -164,7 +174,7 @@
 }
 
 .searchResult {
-  padding: 16px 16px 0;
+  padding: 16px 16px 12px;
   overflow: hidden;
   margin-bottom: 20px;
 

+ 55 - 46
src/tenant/music/train-list/index.tsx

@@ -34,19 +34,31 @@ export default defineComponent({
     const route = useRoute()
     const router = useRouter()
     //
+
+    const subjectType = route.query.subjectType || ''
+    let title = ''
+    if (subjectType === 'SUBJECT') {
+      title = '声部练习'
+    } else if (subjectType === 'MUSIC') {
+      title = '独奏曲目'
+    } else if (subjectType === 'ENSEMBLE') {
+      title = '合奏练习'
+    }
     const params = reactive({
-      search: (route.query.search as string) || '',
-      subjectType: (route.query.subjectType as string) || '',
+      keyword: (route.query.search as string) || '',
+      subjectType: subjectType,
       page: 1,
       subjectId: null,
       level: '',
-      type: ''
+      type: '',
+      title: title
     })
     const data = ref<any>(null)
     const loading = ref(false)
     const finished = ref(false)
     const isError = ref(false)
     const searchObj = ref<any>({})
+    const searchRef = ref()
 
     const apiSuffix = ref(
       baseState.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
@@ -54,7 +66,7 @@ export default defineComponent({
 
     const onSearch = (value: string) => {
       params.page = 1
-      params.search = value
+      params.keyword = value
       data.value = null
       FetchList()
     }
@@ -102,13 +114,6 @@ export default defineComponent({
       // SUBJECT: '声部练习',
       // MUSIC: '独奏曲目',
       // ENSEMBLE: '合奏练习'
-      if (params.subjectType === 'SUBJECT') {
-        document.title = '声部练习'
-      } else if (params.subjectType === 'MUSIC') {
-        document.title = '独奏曲目'
-      } else if (params.subjectType === 'ENSEMBLE') {
-        document.title = '合奏练习'
-      }
       loading.value = true
       await getSelectCondition()
       await FetchList()
@@ -123,6 +128,7 @@ export default defineComponent({
                 background="transparent"
                 isFixed={false}
                 border={false}
+                title={title}
                 color="#131415"
               />
               <Search
@@ -134,7 +140,15 @@ export default defineComponent({
                 v-slots={{
                   left: () => (
                     <DropdownMenu>
-                      <DropdownItem title="筛选">
+                      <DropdownItem
+                        titleClass={
+                          params.subjectId || params.type || params.level
+                            ? styles.titleActive
+                            : ''
+                        }
+                        title="筛选"
+                        ref={searchRef}
+                      >
                         <div
                           class={styles.searchResult}
                           style={{ maxHeight: '45vh', overflowY: 'auto' }}
@@ -153,8 +167,7 @@ export default defineComponent({
                                   {searchObj.value.subjects.map(
                                     (subject: any) => {
                                       const isActive =
-                                        subject.id ===
-                                        Number(params.subjectId || null)
+                                        subject.id === params.subjectId
                                       const type = isActive
                                         ? 'primary'
                                         : 'default'
@@ -165,8 +178,7 @@ export default defineComponent({
                                           type={type}
                                           round
                                           onClick={() => {
-                                            console.log(subject, '1212')
-                                            // this.subject = { ...subject }
+                                            params.subjectId = subject.id
                                           }}
                                         >
                                           {subject.name}
@@ -201,8 +213,7 @@ export default defineComponent({
                                           type={type}
                                           round
                                           onClick={() => {
-                                            console.log(subject, '1212')
-                                            // this.subject = { ...subject }
+                                            params.level = subject
                                           }}
                                         >
                                           {subject}
@@ -236,8 +247,7 @@ export default defineComponent({
                                         type={type}
                                         round
                                         onClick={() => {
-                                          console.log(subject, '1212')
-                                          // this.subject = { ...subject }
+                                          params.type = subject
                                         }}
                                       >
                                         {subject}
@@ -247,34 +257,33 @@ export default defineComponent({
                                 </div>
                               </>
                             )}
+                        </div>
 
-                          <Sticky position="bottom" offsetBottom={0}>
-                            <div class={['btnGroup', 'btnMore']}>
-                              <Button
-                                type="primary"
-                                plain
-                                round
-                                onClick={() => {
-                                  params.subjectId = null
-                                  params.level = ''
-                                  params.type = ''
-                                }}
-                              >
-                                重 置
-                              </Button>
+                        <div class={['btnGroup', 'btnMore']}>
+                          <Button
+                            type="primary"
+                            plain
+                            round
+                            onClick={() => {
+                              params.subjectId = null
+                              params.level = ''
+                              params.type = ''
+                            }}
+                          >
+                            重 置
+                          </Button>
 
-                              <Button
-                                type="primary"
-                                round
-                                block
-                                onClick={() => {
-                                  // this.onComfirm({ ...this.subject })
-                                }}
-                              >
-                                确 认
-                              </Button>
-                            </div>
-                          </Sticky>
+                          <Button
+                            type="primary"
+                            round
+                            block
+                            onClick={() => {
+                              onSearch('')
+                              searchRef.value?.toggle()
+                            }}
+                          >
+                            确 认
+                          </Button>
                         </div>
                       </DropdownItem>
                     </DropdownMenu>

+ 12 - 9
src/tenant/music/train-tool/index.tsx

@@ -85,13 +85,12 @@ export default defineComponent({
 
     const getDetails = async () => {
       try {
-        const { data } = await request.post(
-          apiSuffix.value +
-            '/userTenantAlbumRecord/detail?albumId=' +
-            state.albumId
-        )
+        let url = apiSuffix.value + '/userTenantAlbumRecord/detail'
+        if (state.albumId) {
+          url = url + '?albumId=' + state.albumId
+        }
+        const { data } = await request.post(url)
         state.details = data || {}
-        console.log(state.details, 'details')
         state.buyList.forEach((item: any, index: number) => {
           item.salePrice = (index + 1) * data.salePrice
           item.costPrice = (index + 1) * data.costPrice
@@ -172,8 +171,10 @@ export default defineComponent({
         }
       })
 
+      state.loading = true
       await getDetails()
       await FetchList()
+      state.loading = false
     })
 
     const onSubmit = async () => {
@@ -326,8 +327,10 @@ export default defineComponent({
             }}
           >
             {state.subjectCounts && <Tab title="声部练习" name="SUBJECT"></Tab>}
-            {state.musicCounts && <Tab title="合奏练习" name="ENSEMBLE"></Tab>}
-            {state.ensembleCounts && <Tab title="独奏曲目" name="MUSIC"></Tab>}
+            {state.ensembleCounts && (
+              <Tab title="合奏练习" name="ENSEMBLE"></Tab>
+            )}
+            {state.musicCounts && <Tab title="独奏曲目" name="MUSIC"></Tab>}
           </Tabs>
 
           <div class={styles.alumnList}>
@@ -365,7 +368,7 @@ export default defineComponent({
             </List>
           </div>
         </div>
-        {!state.loading && !state.details.ifBuy && (
+        {baseState.platformType === 'STUDENT' && (
           <TheSticky position="bottom">
             <div class={styles.btnGroup}>
               <Button

+ 2 - 0
src/tenant/ranking-list/index.module.less

@@ -15,4 +15,6 @@
       background-size: 100% 214px;
     }
   }
+
+  // --van-nav-bar-height: 0px;
 }

+ 9 - 0
src/tenant/ranking-list/index.tsx

@@ -15,8 +15,17 @@ export default defineComponent({
               background="transparent"
               isFixed={false}
               border={false}
+              title=" "
               color="#131415"
             />
+
+            {/* <van-tabs v-model:active="active">
+              <van-tab title="标签 1">内容 1</van-tab>
+              <van-tab title="标签 2" disabled>
+                内容 2
+              </van-tab>
+              <van-tab title="标签 3">内容 3</van-tab>
+            </van-tabs> */}
           </TheSticky>
           <img class={styles.bgImg} src={bgImg} />
         </div>

+ 152 - 0
src/tenant/trade/detail.tsx

@@ -0,0 +1,152 @@
+import { moneyFormat } from '@/helpers/utils'
+import dayjs from 'dayjs'
+import { CellGroup, Cell, Image, Row, Col } from 'vant'
+import { defineComponent, onMounted, ref } from 'vue'
+import { state } from '@/state'
+import request from '@/helpers/request'
+import { useRoute } from 'vue-router'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
+import styles from './index.module.less'
+import { orderType } from '@/constant'
+import iconMember from '@common/images/icon_member.png'
+
+export default defineComponent({
+  name: 'trade-detail',
+  setup() {
+    const route = useRoute()
+    const result = ref<any>({})
+    const getOrder = async (status?: true) => {
+      try {
+        const urlFix =
+          state.platformType === 'TEACHER' ? '/api-teacher' : '/api-student'
+        const res = await request.get(
+          `${urlFix}/userOrder/detailByOrderNo/${route.query.orderNo}`,
+          {
+            hideLoading: status
+          }
+        )
+        console.log(res.data)
+        result.value = res.data
+      } catch {
+        //
+      }
+    }
+
+    onMounted(() => {
+      getOrder()
+    })
+    return () => (
+      <div>
+        <TheSticky>
+          <ColHeader isFixed={false} border={false} color="#131415" />
+        </TheSticky>
+        <div class={styles.tradeList}>
+          <CellGroup border={false} class={styles.orderDetailGroup}>
+            <Cell
+              border={false}
+              title={dayjs(result.value.createTime).format('YYYY-MM-DD HH:mm')}
+              value={orderType[result.value.status]}
+              valueClass={styles.tradeType}
+            />
+            {result.value.orderDetailList &&
+              result.value.orderDetailList.map((orderDetail: any) => (
+                <Cell
+                  border={false}
+                  class={styles.orderSection}
+                  v-slots={{
+                    icon: () => (
+                      <Image
+                        src={
+                          orderDetail.goodType === 'VIP'
+                            ? iconMember
+                            : orderDetail.bizInfo?.bizCover
+                        }
+                        class={styles.tradeLogo}
+                      />
+                    ),
+                    title: () => (
+                      <div class={styles.goodsSection}>
+                        <div class={[styles.title]}>
+                          <span class={[styles.name, 'van-ellipsis']}>
+                            {orderDetail.bizInfo?.bizName}
+                          </span>
+                          <span class={styles.desc}>
+                            ¥{moneyFormat(orderDetail.actualPrice)}
+                          </span>
+                        </div>
+
+                        <div class={styles.description}>
+                          <span class={[styles.d, 'van-ellipsis']}>
+                            {orderDetail.bizInfo?.bizDesc}
+                          </span>
+                          {orderDetail.goodType !== 'VIP' && (
+                            <>
+                              {orderDetail.goodType === 'TENANT_ALBUM' ? (
+                                <span class={styles.t}>
+                                  x{orderDetail.bizInfo?.bizValidTime}个月
+                                </span>
+                              ) : (
+                                <span class={styles.t}>永久</span>
+                              )}
+                            </>
+                          )}
+                        </div>
+
+                        {orderDetail.bizInfo?.bizMusicCount && (
+                          <span class={styles.songLength}>
+                            共{orderDetail.bizInfo?.bizMusicCount}首
+                          </span>
+                        )}
+                      </div>
+                    )
+                  }}
+                />
+              ))}
+
+            <div class={styles.paymentPrice}>
+              {['PAYING', 'WAIT_PAY'].includes(result.value.status)
+                ? '需付款'
+                : '实付款'}
+              <span>
+                <i>¥</i>
+                {moneyFormat(result.value.actualPrice)}
+              </span>
+            </div>
+          </CellGroup>
+
+          <div class={styles.optionGroup}>
+            <Row class={styles.optionRow}>
+              <Col span="8" offset={1}>
+                订单号:
+              </Col>
+              <Col span="14">{result.value.orderNo}</Col>
+              <Col span="1"> </Col>
+            </Row>
+            <Row class={styles.optionRow}>
+              <Col span="8" offset={1}>
+                交易流水号:
+              </Col>
+              <Col span="14">{result.value.transNo}</Col>
+              <Col span="1"> </Col>
+            </Row>
+            <Row class={styles.optionRow}>
+              <Col span="8" offset={1}>
+                创建时间:
+              </Col>
+              <Col span="14">{result.value.createTime}</Col>
+              <Col span="1"> </Col>
+            </Row>
+            <Row class={styles.optionRow}>
+              <Col span="8" offset={1}>
+                付款时间:
+              </Col>
+              <Col span="14">{result.value.payTime}</Col>
+              <Col span="1"> </Col>
+            </Row>
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 59 - 0
src/tenant/trade/index.module.less

@@ -253,4 +253,63 @@
       margin-left: 10px;
     }
   }
+}
+
+
+.orderDetailGroup {
+  margin: 0 12px 12px;
+  background: #FFFFFF;
+  border-radius: 10px;
+}
+
+.optionGroup {
+  background: #FFFFFF;
+  border-radius: 10px;
+  margin: 12px;
+  padding-top: 15px;
+}
+
+.optionRow {
+  line-height: 26px;
+  display: flex;
+  position: relative;
+  padding-bottom: 15px;
+  box-sizing: border-box;
+  color: #333333;
+  font-size: 14px;
+
+  :global {
+    .van-col {
+      display: flex;
+      line-height: 1.5;
+    }
+
+    .van-col--8 {
+      color: #131415;
+      justify-content: flex-start;
+    }
+
+    .van-col--14 {
+      color: #777777;
+      justify-content: flex-end;
+      word-break: break-word;
+      /* 文本行的任意字内断开 */
+      word-wrap: break-word;
+      /* IE */
+      white-space: -moz-pre-wrap;
+      /* Mozilla */
+      white-space: -hp-pre-wrap;
+      /* HP printers */
+      white-space: -o-pre-wrap;
+      /* Opera 7 */
+      white-space: -pre-wrap;
+      /* Opera 4-6 */
+      white-space: pre;
+      /* CSS2 */
+      white-space: pre-wrap;
+      /* CSS 2.1 */
+      white-space: pre-line;
+      /* CSS 3 (and 2.1 as well, actually) */
+    }
+  }
 }

+ 1 - 1
src/tenant/trade/index.tsx

@@ -91,7 +91,7 @@ export default defineComponent({
     const onDetail = (item: any) => {
       if (state.type === 'refund') return
       router.push({
-        path: '/tradeDetail',
+        path: '/goodsDetail',
         query: {
           orderNo: item.orderNo,
           path: 'tradeRecord'

+ 5 - 3
src/views/music/music-detail/index.tsx

@@ -204,8 +204,7 @@ export default defineComponent({
           postData: {
             filename: fileName,
             acl: 'public-read',
-            key: keyTime,
-            unknowValueField: []
+            key: keyTime
           }
         }
 
@@ -607,7 +606,10 @@ export default defineComponent({
                 ),
                 title: () => (
                   <div class={styles.info}>
-                    <h4 class="van-ellipsis">
+                    <h4
+                      class="van-ellipsis"
+                      // onClick={() => handleGotoMusicScore(musicDetail.value)}
+                    >
                       {musicDetail.value?.musicSheetName}
                     </h4>
                     <p

+ 2 - 2
src/views/tenantStudentRejest/index.module.less

@@ -6,7 +6,7 @@
     position: absolute;
     top: 0;
     left: 0;
-    width: 375px;
+    width: 100%;
     height: 891px;
   }
   .rejectLogo {
@@ -169,7 +169,7 @@
 .sendBtn {
   border: none;
   background-color: transparent;
-  width: 70px;
+  width: 80px;
   color: #fe2451;
   font-size: 14px;
   padding: 0;

+ 10 - 6
src/views/tenantStudentRejest/index.tsx

@@ -49,6 +49,8 @@ export default defineComponent({
       genderList:[{text:'男',value:'1'},{text:'女',value:'0'}],
       showSuccess:false,
       secondConfirm:false,
+      minDate: new Date(1980, 1, 1),
+      maxDate: new Date(),
     });
     const handleSubmit = async() => {
       console.log(forms, 'forms')
@@ -71,7 +73,7 @@ export default defineComponent({
         Toast('请选择声部')
       }
 
-        const res = await request.post('/api-tenant/open/student/save',{ data: { ... forms}})
+        const res = await request.post('/api-tenant/open/student/save',{ data: { ... forms},hideLoading:true})
         console.log(res)
       if(res.code == 200){
         data.showSuccess = true
@@ -166,7 +168,7 @@ export default defineComponent({
 
     const submitSecond = async()=>{
       try{
-        const res = await request.post('/api-tenant/open/student/save',{ data: { ... forms,updateTenant:true}})
+        const res = await request.post('/api-tenant/open/student/save',{ data: { ... forms,updateTenant:true},hideLoading:true})
         data.showSuccess=true
         data.secondConfirm=false
 
@@ -177,12 +179,12 @@ export default defineComponent({
     }
     return () =>
       <>< div class={styles.videoClass} >
-        <ColHeader
+        {/* <ColHeader
           class={styles.classHeader}
           border={false}
           isFixed={false}
           background="#fff"
-        />
+        /> */}
         <div class={styles.resjetStudentWrap}>
           <img src={rejectLogo} class={styles.rejectLogo} alt="" />
           <img src={studentText} class={styles.studentText} alt="" />
@@ -343,7 +345,9 @@ export default defineComponent({
           onClose={() => (data.dateState = false)}
           onClosed={() => (data.dateState = false)}
         >
-          <DatetimePicker  type="date" v-model:value={forms.birthdate} onCancel={() => { data.dateState = false }} onConfirm={confirmDate}></DatetimePicker>
+          <DatetimePicker
+          min-date={data.minDate}
+  max-date={data.maxDate} type="date" v-model:value={forms.birthdate} onCancel={() => { data.dateState = false }} onConfirm={confirmDate}></DatetimePicker>
         </Popup>
 
         <Popup
@@ -379,7 +383,7 @@ export default defineComponent({
         <div class={styles.secondWrap}>
           <h2>提示</h2>
           <p>当前账号已存在 <span>【机构名称】</span> ,是否</p>
-          <p>确认更换到 <span>【机构名称】</span>吗? </p>
+          <p>确认更换到 <span>{data.schoolName}</span>吗? </p>
           <div class={styles.buttonWrap}>
             <div class={styles.closeBtn} onClick={()=>{data.secondConfirm = false}}> 取消</div>
             <div  class={styles.submitBtn} onClick={submitSecond}>确定</div>

+ 2 - 2
src/views/tenantTeacherRejest/index.module.less

@@ -6,7 +6,7 @@
     position: absolute;
     top: 0;
     left: 0;
-    width: 375px;
+    width: 100%;
     height: 891px;
   }
   .rejectLogo {
@@ -169,7 +169,7 @@
 .sendBtn {
   border: none;
   background-color: transparent;
-  width: 70px;
+  width: 80px;
   color: #fe2451;
   font-size: 14px;
   padding: 0;

+ 9 - 15
src/views/tenantTeacherRejest/index.tsx

@@ -184,12 +184,6 @@ export default defineComponent({
     return () => (
       <>
         <div class={styles.videoClass}>
-          <ColHeader
-            class={styles.classHeader}
-            border={false}
-            isFixed={false}
-            background="#fff"
-          />
           <div class={styles.resjetStudentWrap}>
             <img src={rejectLogo} class={styles.rejectLogo} alt="" />
             <img src={studentText} class={styles.studentText} alt="" />
@@ -213,9 +207,9 @@ export default defineComponent({
                       maxlength={20}
                       v-model={forms.username}
 
-                      // onUpdate: modelValue={(val: string) => {
-                      //   forms.nickname = val.trim();
-                      // }}
+                    // onUpdate: modelValue={(val: string) => {
+                    //   forms.nickname = val.trim();
+                    // }}
                     />
 
                     <Field
@@ -256,9 +250,9 @@ export default defineComponent({
                       maxlength={20}
                       v-model={forms.realName}
 
-                      // onUpdate: modelValue={(val: string) => {
-                      //   forms.nickname = val.trim();
-                      // }}
+                    // onUpdate: modelValue={(val: string) => {
+                    //   forms.nickname = val.trim();
+                    // }}
                     />
                     <Field
                       class={styles.noArrow}
@@ -268,9 +262,9 @@ export default defineComponent({
                       maxlength={20}
                       v-model={forms.idCardNo}
 
-                      // onUpdate: modelValue={(val: string) => {
-                      //   forms.nickname = val.trim();
-                      // }}
+                    // onUpdate: modelValue={(val: string) => {
+                    //   forms.nickname = val.trim();
+                    // }}
                     />
 
                     <Field