Ver Fonte

添加音频播放功能

lex-xin há 9 meses atrás
pai
commit
08a6a63f83

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/assets/index-legacy.262d8ad3.js


+ 5 - 0
src/components/col-upload/index.module.less

@@ -34,6 +34,11 @@
       margin-right: 5px;
     }
   }
+
+  .isPreview {
+    text-decoration: underline;
+    color: #14BC9C;
+  }
 }
 
 .fileUpload {

+ 17 - 3
src/components/col-upload/index.tsx

@@ -1,5 +1,5 @@
 import { ElIcon, ElImage, ElLoading, ElMessage, ElUpload } from 'element-plus'
-import { defineComponent, PropType } from 'vue'
+import { defineComponent, PropType, readonly } from 'vue'
 import { Document } from '@element-plus/icons-vue'
 import styles from './index.module.less'
 import iconUpload from './images/icon_upload.png'
@@ -59,8 +59,14 @@ export default defineComponent({
     btnText: {
       type: String,
       default: '上传文件'
+    },
+    // 是否可预览
+    isPreview: {
+      type: Boolean,
+      default: false
     }
   },
+  emits: ['preview'],
   data() {
     return {
       // ossUploadUrl: 'https://ks3-cn-beijing.ksyuncs.com/' + this.bucket,
@@ -190,7 +196,7 @@ export default defineComponent({
     return (
       <div class={[styles.colUpload, 'w-full']}>
         <ElUpload
-          disabled={this.disabled}
+          disabled={this.disabled || this.isPreview}
           action={this.ossUploadUrl}
           data={this.dataObj}
           httpRequest={this.handleSuccess}
@@ -225,6 +231,14 @@ export default defineComponent({
                     styles.uploadFile,
                     this.disabled && 'cursor-not-allowed'
                   ]}
+                  onClick={() => {
+                    if(this.isPreview) {
+                      this.$emit('preview', {
+                        name: this.fileName(this.modelValue),
+                        url: this.modelValue
+                      })
+                    }
+                  }}
                 >
                   <ElIcon size={20}>
                     {this.type === 'music' ? (
@@ -234,7 +248,7 @@ export default defineComponent({
                     )}
                   </ElIcon>
                   <span
-                    class="whitespace-nowrap overflow-hidden text-ellipsis flex-1"
+                    class={["whitespace-nowrap overflow-hidden text-ellipsis flex-1", this.isPreview && styles.isPreview]}
                     style={{ lineHeight: '1.2' }}
                   >
                     {this.fileName(this.modelValue)}

BIN
src/views/user-info/music-operation/images/audio-bg.png


BIN
src/views/user-info/music-operation/images/audio-pause.png


BIN
src/views/user-info/music-operation/images/audio-play.png


BIN
src/views/user-info/music-operation/images/top_bg.png


BIN
src/views/user-info/music-operation/images/top_line.png


+ 5 - 1
src/views/user-info/music-operation/index.module.less

@@ -179,7 +179,7 @@
 
 
 .messageDialog,
-.messageDialog2 {
+.messageDialog2, .messageDialog3 {
   width: 618px;
   border-radius: 18px;
 
@@ -198,3 +198,7 @@
 .messageDialog2 {
   width: 320px;
 }
+
+.messageDialog3 {
+  width: 383px;
+}

+ 17 - 0
src/views/user-info/music-operation/index.tsx

@@ -23,6 +23,7 @@ import styles from './index.module.less'
 import requestOrigin from 'umi-request'
 import { FormatXMLInfo, getXmlInfo } from './music-xml'
 import MessageTip from './message-tip'
+import ListenAudio from './listen-audio'
 
 export type BackgroundMp3 = {
   url?: string
@@ -79,6 +80,8 @@ export default defineComponent({
       music_account_period: 0,
       visibleShow: false,
       visibleShow2: false,
+      visibleAudio: false,
+      fileInfo: {} as any,
       messageTipTitle: '上传须知',
       messageTipType: 'upload' as 'upload' | 'error' | 'origin',
       cbsInstrumentList: [] as any
@@ -390,6 +393,7 @@ export default defineComponent({
               rules={[{ required: true, message: '请选择伴奏' }]}
             >
               <ColUpload
+                isPreview={this.form.mp3Url ? true : false}
                 v-model:modelValue={this.form.mp3Url}
                 bucket={'cloud-coach'}
                 accept={'.mp3'}
@@ -398,6 +402,10 @@ export default defineComponent({
                 type="music"
                 btnText="上传伴奏文件"
                 size={8}
+                onPreview={(fileInfo: any) => {
+                  this.visibleAudio = true;
+                  this.fileInfo = fileInfo
+                }}
                 extraTips="仅支持MP3格式文件,文件最大不能超过8MB"
               />
             </ElFormItem>
@@ -761,6 +769,15 @@ export default defineComponent({
             onConfirm={() => (this.visibleShow2 = false)}
           />
         </ElDialog>
+
+        <ElDialog
+          modelValue={this.visibleAudio}
+          onUpdate:modelValue={val => (this.visibleAudio = val)}
+          destroyOnClose={true}
+          customClass={styles.messageDialog3}
+        >
+          {this.visibleAudio && <ListenAudio fileInfo={this.fileInfo} />}
+        </ElDialog>
       </div>
     )
   }

+ 117 - 0
src/views/user-info/music-operation/listen-audio/index.module.less

@@ -0,0 +1,117 @@
+.listenAudio {
+  background: url('../images/top_bg.png') no-repeat top center;
+  background-size: contain;
+  width: 100%;
+  min-height: 200px;
+
+  .title {
+    padding-top: 20px;
+    padding-bottom: 30px;
+    font-weight: 600;
+    font-size: 18px;
+    color: #000000;
+    text-align: center;
+
+    span {
+      display: inline-block;
+      position: relative;
+      span {
+        position: relative;
+        z-index: 5;
+      }
+      &::after {
+        content: '';
+        display: inline-block;
+        width: 100%;
+        height: 8px;
+        background: url('../images/top_line.png') no-repeat center;
+        background-size: contain;
+        position: absolute;
+        bottom: -2px;
+        left: 0;
+        z-index: -1;
+      }
+    }
+  }
+
+  .container {
+    .img {
+      position: relative;
+      width: 240px;
+      height: 240px;
+      margin: 0 auto;
+      cursor: pointer;
+
+      .audioBg {
+        width: inherit;
+        height: inherit;
+      }
+
+      .iconImg {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        width: 60px;
+        height: 60px;
+      }
+    }
+
+    .slider {
+      // width: 100%;
+      padding: 14px 0 8px;
+      
+
+      :global {
+        .el-slider {
+          --el-slider-height: 4px;
+          --el-slider-button-size: 14px !important;
+          --el-slider-runway-bg-color: #EEEEEE !important;
+          --el-slider-button-wrapper-offset: -16px;
+        }
+        .el-slider__button {
+          background-color: var(--el-color-primary);
+        }
+
+        .van-loading {
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+
+    .audioInfo {
+      padding: 30px;
+      .name {
+        font-weight: 600;
+        font-size: 18px;
+        color: #131415;
+        line-height: 25px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+
+      .time {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        font-size: 16px;
+        color: #777777;
+        line-height: 20px;
+      }
+      audio {
+        visibility: hidden;
+        position: absolute;
+        z-index: -2;
+      }
+      :global {
+        .plyr {
+          visibility: hidden;
+          position: absolute;
+          z-index: -2;
+        }
+      }
+    }
+  }
+}

+ 153 - 0
src/views/user-info/music-operation/listen-audio/index.tsx

@@ -0,0 +1,153 @@
+import {
+  defineComponent,
+  onMounted,
+  onUnmounted,
+  PropType,
+  reactive,
+  ref
+} from 'vue'
+import styles from './index.module.less'
+import iconPause from '../images/audio-pause.png'
+import iconPlay from '../images/audio-play.png'
+import audioBg from '../images/audio-bg.png'
+// import { Slider } from 'vant'
+import 'vant/es/slider/style'
+import Plyr from 'plyr'
+import { ElSlider } from 'element-plus'
+
+type fileType = {
+  url: string
+  name: string
+}
+
+export default defineComponent({
+  name: 'listen-audio',
+  props: {
+    fileInfo: {
+      type: Object as PropType<fileType>
+    }
+  },
+  setup(props) {
+    const audioRef = ref()
+    const audioProtoType = reactive({
+      audioPause: true,
+      duration: 0.01,
+      currentTime: 0
+    })
+
+    const onChangePlay = () => {
+      if (!audioRef.value) return
+      if (audioProtoType.audioPause) {
+        audioRef.value?.play()
+      } else {
+        audioRef.value?.pause()
+      }
+      audioProtoType.audioPause = audioRef.value.paused
+    }
+
+    const handleChangeTime = (val: any) => {
+      if(!audioRef.value) return
+      audioRef.value.currentTime = val
+    }
+
+    // 秒转分
+    const getSecondRPM = (second: number, type?: string) => {
+      if (isNaN(second)) return '00:00'
+      const mm = Math.floor(second / 60)
+        .toString()
+        .padStart(2, '0')
+      const dd = Math.floor(second % 60)
+        .toString()
+        .padStart(2, '0')
+      if (type === 'cn') {
+        return mm + '分' + dd + '秒'
+      } else {
+        return mm + ':' + dd
+      }
+    }
+
+    onMounted(() => {
+      if (!props.fileInfo?.url) return
+      audioRef.value = new Plyr('#audioSrc', {
+        controls: [
+          'play-large',
+          'play',
+          'progress',
+          'current-time',
+          'duration'
+        ],
+        fullscreen: { enabled: false }
+      })
+      audioRef.value.on('loadedmetadata', () => {
+        audioProtoType.duration = audioRef.value.duration
+      })
+      audioRef.value.on('timeupdate', () => {
+        audioProtoType.currentTime = audioRef.value.currentTime || 0
+      })
+      audioRef.value.on('ended', () => {
+        audioProtoType.currentTime = 0
+        audioRef.value.currentTime = 0
+        audioProtoType.audioPause = true
+      })
+    })
+
+    onUnmounted(() => {
+      audioRef.value?.destroy()
+    })
+    return () => (
+      <div class={styles.listenAudio}>
+        <h2 class={styles.title}>
+          <span>
+            <span>预览音频</span>
+          </span>
+        </h2>
+
+        <div class={styles.container}>
+          <div class={styles.img} onClick={onChangePlay}>
+            <img src={audioBg} class={styles.audioBg} />
+            <img
+              src={audioProtoType.audioPause ? iconPause : iconPlay}
+              class={styles.iconImg}
+            />
+          </div>
+
+          <div class={styles.audioInfo}>
+            <audio
+              crossorigin="anonymous"
+              id="audioSrc"
+              src={props.fileInfo?.url}
+              controls="false"
+              preload="metadata"
+              playsinline
+            />
+            <div class={styles.name}>{props.fileInfo?.name}</div>
+            <div class={styles.slider}>
+              {/* <Slider
+                step={0.01}
+                class={styles.timeProgress}
+                v-model={audioProtoType.currentTime}
+                max={audioProtoType.duration}
+                onUpdate:modelValue={val => {
+                  handleChangeTime(val)
+                }}
+              /> */}
+              <ElSlider
+                step={0.01}
+                showTooltip={false}
+                max={audioProtoType.duration}
+                v-model={audioProtoType.currentTime}
+                onUpdate:modelValue={val => {
+                  handleChangeTime(val)
+                }}
+              />
+            </div>
+            <div class={styles.time}>
+              <div>{getSecondRPM(audioProtoType.currentTime)}</div>
+              <div>{getSecondRPM(audioProtoType.duration)}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff