hookehuyr

feat(播放器): 实现音视频互斥播放功能并优化默认封面

在VideoPlayer和AudioPlayer组件中添加互斥播放逻辑,当播放视频时自动暂停音频,反之亦然
为音频播放器添加默认封面图片
更新所有测试图片和音频资源URL为正式CDN地址
添加组件卸载时的清理逻辑
<!--
* @Date: 2025-04-07 12:35:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-05-16 16:50:23
* @LastEditTime: 2025-05-30 15:26:15
* @FilePath: /mlaj/src/components/ui/AudioPlayer.vue
* @Description: 音频播放器组件,支持播放控制、进度条调节、音量控制、播放列表等功能
-->
......@@ -12,7 +12,7 @@
<div class="flex flex-col items-center mb-4">
<!-- 歌曲封面 -->
<div class="w-24 h-24 rounded-lg overflow-hidden mb-2">
<img :src="currentSong?.cover" alt="封面" class="w-full h-full object-cover">
<img :src="currentSong?.cover ? currentSong?.cover : 'https://cdn.ipadbiz.cn/mlaj/images/audio_d_cover.jpg'" alt="封面" class="w-full h-full object-cover">
</div>
<!-- 歌曲信息 -->
......@@ -21,7 +21,7 @@
<p class="text-xs text-gray-500">{{ currentSong?.artist }}</p>
</div>
</div>
</div>
<!-- 进度条与时间:显示当前播放时间、总时长和可拖动的进度条 -->
<div class="mt-4 mb-6">
......@@ -105,7 +105,7 @@
>
<div class="absolute top-0 left-0 right-0 h-[1px] bg-gray-200"></div>
<div class="w-16 h-16 rounded-lg overflow-hidden mr-4 flex-shrink-0">
<img :src="song?.cover" alt="封面" class="w-full h-full object-cover">
<img :src="song?.cover ? song?.cover : 'https://cdn.ipadbiz.cn/mlaj/images/audio_d_cover.jpg'" alt="封面" class="w-full h-full object-cover">
</div>
<div class="flex-1">
<div class="font-medium">{{ song?.title }}</div>
......@@ -177,6 +177,9 @@ const loadAudio = async () => {
}
}
// 定义组件事件
const emit = defineEmits(['play', 'pause'])
// 播放控制:切换播放/暂停状态
const togglePlay = async () => {
if (isLoading.value) return
......@@ -186,8 +189,10 @@ const togglePlay = async () => {
}
if (isPlaying.value) {
await audio.value?.pause()
emit('pause', audio.value)
} else {
await audio.value?.play()
emit('play', audio.value)
}
isPlaying.value = !isPlaying.value
} catch (error) {
......@@ -195,6 +200,19 @@ const togglePlay = async () => {
}
}
// 暴露给父组件的方法
defineExpose({
togglePlay,
pause: () => {
if (isPlaying.value && audio.value) {
audio.value.pause();
isPlaying.value = false;
emit('pause', audio.value);
}
},
isPlaying: () => isPlaying.value
})
// 进度更新
const updateProgress = () => {
if (!audio.value) return
......@@ -223,6 +241,8 @@ const prevSong = async () => {
const normalizedVolume = Math.pow(volume.value / 100, 2)
audio.value.volume = normalizedVolume
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
}
}
......@@ -241,6 +261,8 @@ const nextSong = async () => {
const normalizedVolume = Math.pow(volume.value / 100, 2)
audio.value.volume = normalizedVolume
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
}
}
......@@ -312,6 +334,8 @@ const selectSong = async (index) => {
if (audio.value) {
await audio.value.play()
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
// 关闭播放列表
isPlaylistVisible.value = false
// 滚动到当前播放的音频
......
......@@ -119,12 +119,12 @@ onBeforeUnmount(() => {
defineExpose({
pause() {
if (player.value) {
player.value.pause();
player.value?.pause();
}
},
play() {
if (player.value) {
player.value.play();
player.value?.play();
}
},
});
......
This diff is collapsed. Click to expand it.