|
@@ -1,34 +1,35 @@
|
|
|
<template>
|
|
|
- <div class="glowe-audio">
|
|
|
- <div class="audio">
|
|
|
- <div class="icon-div">
|
|
|
- <SvgIcon @click="playPauseAudio" :name="audioData.playing ? 'ele-VideoPause' : 'ele-VideoPlay'"
|
|
|
- :title="audioData.playing ? '暂停' : '播放'" class="mr10 icon" size="24px"
|
|
|
- color="var(--el-color-primary)" />
|
|
|
- <div class="time-wrap">
|
|
|
- <span class="time">{{ formatDuraton(Math.round(audioData.currentTime))
|
|
|
- }}/{{ formatDuraton(Math.round(audioData.duration)) }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="slider-wrap ml10 mr10" ref="progessRef">
|
|
|
- <el-slider v-model="audioData.percentage" @input="inputProgress" @change="changeProgress" :format-tooltip="formatTooltip">
|
|
|
- </el-slider>
|
|
|
- </div>
|
|
|
-
|
|
|
- <el-dropdown trigger="hover">
|
|
|
- <SvgIcon :name="icon" class="mr5 ml5 download" size="18px" color="var(--el-color-primary)"
|
|
|
- @click="mute" />
|
|
|
- <template #dropdown>
|
|
|
- <el-dropdown-menu>
|
|
|
- <el-slider v-model="audioData.voice" vertical height="100px" @change="changeSound" />
|
|
|
- </el-dropdown-menu>
|
|
|
- </template>
|
|
|
- </el-dropdown>
|
|
|
- <SvgIcon name="ele-Download" class="download ml5" size="18px" color="var(--el-color-primary)"
|
|
|
- @click="downLoad" title="下载" />
|
|
|
- </div>
|
|
|
- <audio :src="audioData.url" preload="auto" @ended="audioEnded" @timeupdate="audioTimeupdate" @loadeddata="audioLoadeddata" ref="audioRef"></audio>
|
|
|
- </div>
|
|
|
+ <div class="glowe-audio">
|
|
|
+ <div class="audio">
|
|
|
+ <div class="icon-div">
|
|
|
+ <SvgIcon
|
|
|
+ @click="playPauseAudio"
|
|
|
+ :name="audioData.playing ? 'ele-VideoPause' : 'ele-VideoPlay'"
|
|
|
+ :title="audioData.playing ? '暂停' : '播放'"
|
|
|
+ class="mr10 cursor-pointer"
|
|
|
+ size="24px"
|
|
|
+ color="var(--el-color-primary)"
|
|
|
+ />
|
|
|
+ <div class="time-wrap">
|
|
|
+ <span class="time">{{ formatDuraton(Math.round(audioData.currentTime)) }}/{{ formatDuraton(Math.round(audioData.duration)) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="slider-wrap ml10 mr10" ref="progessRef">
|
|
|
+ <el-slider v-model="audioData.percentage" @input="inputProgress" @change="changeProgress" :format-tooltip="formatTooltip"> </el-slider>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-dropdown trigger="hover">
|
|
|
+ <SvgIcon :name="icon" class="mr5 ml5 cursor-pointer" size="18px" color="var(--el-color-primary)" @click="mute" />
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-slider v-model="audioData.voice" vertical height="100px" @change="changeSound" />
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ <SvgIcon name="ele-Download" class="cursor-pointer ml5" size="18px" color="var(--el-color-primary)" @click="downLoad" title="下载" />
|
|
|
+ </div>
|
|
|
+ <audio :src="audioData.url" preload="auto" @ended="audioEnded" @timeupdate="audioTimeupdate" @loadeddata="audioLoadeddata" ref="audioRef"></audio>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
<script lang="ts" name="GloweAudio" setup>
|
|
|
import { reactive, ref, computed, watch } from 'vue';
|
|
@@ -36,56 +37,56 @@ import { formatDuraton } from '/@/utils/formatTime';
|
|
|
|
|
|
// 定义父组件传过来的值
|
|
|
const props = defineProps({
|
|
|
- url: {
|
|
|
- type: String,
|
|
|
- required: true,
|
|
|
- },
|
|
|
- fileName: {
|
|
|
- type: String,
|
|
|
- required: false,
|
|
|
- default: "测试文件"
|
|
|
- }
|
|
|
-})
|
|
|
+ url: {
|
|
|
+ type: String,
|
|
|
+ required: true,
|
|
|
+ },
|
|
|
+ fileName: {
|
|
|
+ type: String,
|
|
|
+ required: false,
|
|
|
+ default: '测试文件',
|
|
|
+ },
|
|
|
+});
|
|
|
|
|
|
// 定义变量内容
|
|
|
const audioData = reactive<any>({
|
|
|
- url: props.url,
|
|
|
- playing: false,
|
|
|
- duration: 0, // 音频总时长
|
|
|
- currentTime: 0, // 当前播放的时间
|
|
|
- percentage: 0, //进度条百分比
|
|
|
- voice: 50, //音量
|
|
|
- muteState: false, //是否静音
|
|
|
+ url: props.url,
|
|
|
+ playing: false,
|
|
|
+ duration: 0, // 音频总时长
|
|
|
+ currentTime: 0, // 当前播放的时间
|
|
|
+ percentage: 0, //进度条百分比
|
|
|
+ voice: 50, //音量
|
|
|
+ muteState: false, //是否静音
|
|
|
});
|
|
|
|
|
|
const audioRef = ref();
|
|
|
// 播放或暂停音频
|
|
|
const playPauseAudio = () => {
|
|
|
- if (audioData.playing) {
|
|
|
- audioPause();
|
|
|
- } else {
|
|
|
- audioPlay();
|
|
|
- }
|
|
|
+ if (audioData.playing) {
|
|
|
+ audioPause();
|
|
|
+ } else {
|
|
|
+ audioPlay();
|
|
|
+ }
|
|
|
};
|
|
|
const icon = computed(() => {
|
|
|
- if (audioData.muteState) {
|
|
|
- return 'iconfont icon-guanbiyinliang'
|
|
|
- } else {
|
|
|
- return 'iconfont icon-jiadayinliang'
|
|
|
- }
|
|
|
-})
|
|
|
+ if (audioData.muteState) {
|
|
|
+ return 'iconfont icon-guanbiyinliang';
|
|
|
+ } else {
|
|
|
+ return 'iconfont icon-jiadayinliang';
|
|
|
+ }
|
|
|
+});
|
|
|
// 计算百分比的分子
|
|
|
function getPercentage(num: number | string, den: number | string): number {
|
|
|
- const numerator = typeof num === 'number' ? num : parseFloat(num);
|
|
|
- const denominator = typeof den === 'number' ? den : parseFloat(den);
|
|
|
- return Math.round((numerator / denominator) * 10000) / 100;
|
|
|
+ const numerator = typeof num === 'number' ? num : parseFloat(num);
|
|
|
+ const denominator = typeof den === 'number' ? den : parseFloat(den);
|
|
|
+ return Math.round((numerator / denominator) * 10000) / 100;
|
|
|
}
|
|
|
/**
|
|
|
* 更新进度条
|
|
|
* @param percentage 百分比
|
|
|
*/
|
|
|
function updateProgressBar(percentage: number) {
|
|
|
- audioData.percentage = percentage;
|
|
|
+ audioData.percentage = percentage;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -94,156 +95,128 @@ function updateProgressBar(percentage: number) {
|
|
|
|
|
|
// 音频播放结束
|
|
|
const audioEnded = () => {
|
|
|
- audioData.playing = false;
|
|
|
+ audioData.playing = false;
|
|
|
};
|
|
|
// 播放进度:表示audio正在播放,currentTime在更新
|
|
|
const audioTimeupdate = (e: any) => {
|
|
|
- audioData.currentTime = e.target.currentTime;
|
|
|
- progressBarBycurrentTime();
|
|
|
+ audioData.currentTime = e.target.currentTime;
|
|
|
+ progressBarBycurrentTime();
|
|
|
};
|
|
|
// 当媒体音频第一帧加载完成时
|
|
|
const audioLoadeddata = (e: any) => {
|
|
|
- audioData.duration = e.target.duration;
|
|
|
-
|
|
|
+ audioData.duration = e.target.duration;
|
|
|
};
|
|
|
|
|
|
// 播放
|
|
|
function audioPlay() {
|
|
|
- audioRef.value.play();
|
|
|
- audioData.playing = true;
|
|
|
+ audioRef.value.play();
|
|
|
+ audioData.playing = true;
|
|
|
}
|
|
|
|
|
|
// 暂停播放
|
|
|
function audioPause() {
|
|
|
- audioRef.value.pause();
|
|
|
- audioData.playing = false;
|
|
|
+ audioRef.value.pause();
|
|
|
+ audioData.playing = false;
|
|
|
}
|
|
|
|
|
|
// 进度条和音频播放进度进行关联
|
|
|
function progressBarBycurrentTime() {
|
|
|
- const progress = getPercentage(audioData.currentTime, audioData.duration);
|
|
|
- updateProgressBar(progress);
|
|
|
+ const progress = getPercentage(audioData.currentTime, audioData.duration);
|
|
|
+ updateProgressBar(progress);
|
|
|
}
|
|
|
|
|
|
// 改变进度 暂停 使用鼠标拖曳时,活动过程实时触发
|
|
|
const inputProgress = (val: number) => {
|
|
|
- audioData.percentage = val;
|
|
|
- audioData.currentTime = audioData.duration * (val / 100);
|
|
|
- audioRef.value.currentTime = audioData.duration * (val / 100);
|
|
|
- audioRef.value.pause();
|
|
|
-}
|
|
|
+ audioData.percentage = val;
|
|
|
+ audioData.currentTime = audioData.duration * (val / 100);
|
|
|
+ audioRef.value.currentTime = audioData.duration * (val / 100);
|
|
|
+ audioRef.value.pause();
|
|
|
+};
|
|
|
// 使用鼠标拖曳时,只在松开鼠标后触发 继续播放
|
|
|
-const changeProgress = ()=>{
|
|
|
- if(audioData.playing) audioPlay();
|
|
|
-}
|
|
|
+const changeProgress = () => {
|
|
|
+ if (audioData.playing) audioPlay();
|
|
|
+};
|
|
|
// 格式化提示
|
|
|
const formatTooltip = (val: number) => {
|
|
|
- return formatDuraton(Math.round(audioData.duration * (val / 100)))
|
|
|
-}
|
|
|
+ return formatDuraton(Math.round(audioData.duration * (val / 100)));
|
|
|
+};
|
|
|
// 改变音量
|
|
|
const changeSound = (val: number) => {
|
|
|
- if (val > 0) {
|
|
|
- audioData.muteState = false;
|
|
|
- } else {
|
|
|
- audioData.muteState = true;
|
|
|
- }
|
|
|
- audioRef.value.volume = val / 100;
|
|
|
- audioData.voice = val;
|
|
|
-}
|
|
|
+ if (val > 0) {
|
|
|
+ audioData.muteState = false;
|
|
|
+ } else {
|
|
|
+ audioData.muteState = true;
|
|
|
+ }
|
|
|
+ audioRef.value.volume = val / 100;
|
|
|
+ audioData.voice = val;
|
|
|
+};
|
|
|
// 监听声音大小
|
|
|
watch(
|
|
|
- () => audioData.muteState,
|
|
|
- (val) => {
|
|
|
- if (val) {
|
|
|
- audioRef.value.volume = audioData.voice = 0;
|
|
|
- } else {
|
|
|
- audioRef.value.volume = .5;
|
|
|
- audioData.voice = 50;
|
|
|
- }
|
|
|
- },
|
|
|
- {
|
|
|
- deep: true,
|
|
|
- }
|
|
|
+ () => audioData.muteState,
|
|
|
+ (val) => {
|
|
|
+ if (val) {
|
|
|
+ audioRef.value.volume = audioData.voice = 0;
|
|
|
+ } else {
|
|
|
+ audioRef.value.volume = 0.5;
|
|
|
+ audioData.voice = 50;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ deep: true,
|
|
|
+ }
|
|
|
);
|
|
|
// 静音
|
|
|
const mute = () => {
|
|
|
- audioData.muteState = !audioData.muteState;
|
|
|
-}
|
|
|
+ audioData.muteState = !audioData.muteState;
|
|
|
+};
|
|
|
// 下载
|
|
|
const downLoad = () => {
|
|
|
-
|
|
|
- // // eslint-disable-next-line no-unused-vars
|
|
|
- // let blobType = 'application/force-download' // 设置blob请求头
|
|
|
- // // eslint-disable-next-line no-unused-vars
|
|
|
- // let blob = new Blob([res.data], {type: res.data.type}) // 创建blob 设置blob文件类型 data 设置为后端返回的文件(例如mp3,jpeg) type:这里设置后端返回的类型 为 mp3
|
|
|
- // let downa = document.createElement('a') // 创建A标签
|
|
|
- // // eslint-disable-next-line no-unused-vars
|
|
|
- // let href = window.URL.createObjectURL(blob) // 创建下载的链接
|
|
|
- // downa.href = props.url // 下载地址
|
|
|
- // downa.download = props?.fileName ?? '' // 下载文件名
|
|
|
- // document.body.appendChild(downa)
|
|
|
- // downa.click() // 模拟点击A标签
|
|
|
- // document.body.removeChild(downa) // 下载完成移除元素
|
|
|
- // window.URL.revokeObjectURL(href) // 释放blob对象
|
|
|
-
|
|
|
-
|
|
|
- // const elink = document.createElement('a')
|
|
|
- // elink.href = props.url
|
|
|
- // elink.setAttribute('download', props?.fileName ?? '')
|
|
|
- // elink.style.display = 'none'
|
|
|
- // document.body.appendChild(elink)
|
|
|
- // setTimeout(() => {
|
|
|
- // elink.click()
|
|
|
- // document.body.removeChild(elink)
|
|
|
- // }, 66)
|
|
|
- window.open(props.url)
|
|
|
-}
|
|
|
+ // // eslint-disable-next-line no-unused-vars
|
|
|
+ // let blobType = 'application/force-download' // 设置blob请求头
|
|
|
+ // // eslint-disable-next-line no-unused-vars
|
|
|
+ // let blob = new Blob([res.data], {type: res.data.type}) // 创建blob 设置blob文件类型 data 设置为后端返回的文件(例如mp3,jpeg) type:这里设置后端返回的类型 为 mp3
|
|
|
+ // let downa = document.createElement('a') // 创建A标签
|
|
|
+ // // eslint-disable-next-line no-unused-vars
|
|
|
+ // let href = window.URL.createObjectURL(blob) // 创建下载的链接
|
|
|
+ // downa.href = props.url // 下载地址
|
|
|
+ // downa.download = props?.fileName ?? '' // 下载文件名
|
|
|
+ // document.body.appendChild(downa)
|
|
|
+ // downa.click() // 模拟点击A标签
|
|
|
+ // document.body.removeChild(downa) // 下载完成移除元素
|
|
|
+ // window.URL.revokeObjectURL(href) // 释放blob对象
|
|
|
+
|
|
|
+ // const elink = document.createElement('a')
|
|
|
+ // elink.href = props.url
|
|
|
+ // elink.setAttribute('download', props?.fileName ?? '')
|
|
|
+ // elink.style.display = 'none'
|
|
|
+ // document.body.appendChild(elink)
|
|
|
+ // setTimeout(() => {
|
|
|
+ // elink.click()
|
|
|
+ // document.body.removeChild(elink)
|
|
|
+ // }, 66)
|
|
|
+ window.open(props.url);
|
|
|
+};
|
|
|
</script>
|
|
|
<style scoped lang="scss">
|
|
|
.glowe-audio {
|
|
|
- .audio {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- height: 48px;
|
|
|
- .icon-div {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- border-radius: 100%;
|
|
|
- margin-right: 10px;
|
|
|
-
|
|
|
- .icon {
|
|
|
- cursor: pointer;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .slider-wrap {
|
|
|
- position: relative;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- min-width: 200px;
|
|
|
- .circle {
|
|
|
- position: absolute;
|
|
|
- background: var(--el-color-white);
|
|
|
- border-style: solid;
|
|
|
- border-width: 2px;
|
|
|
- border-radius: 50%;
|
|
|
- box-sizing: border-box;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- border-color: var(--el-color-primary);
|
|
|
- width: 16px;
|
|
|
- height: 16px;
|
|
|
- cursor: pointer;
|
|
|
- user-select: none;
|
|
|
- transform: translate(-50%);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .download {
|
|
|
- cursor: pointer;
|
|
|
- }
|
|
|
- }
|
|
|
+ .audio {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 48px;
|
|
|
+ .icon-div {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 100%;
|
|
|
+ margin-right: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .slider-wrap {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ min-width: 200px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|