hookehuyr

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

在VideoPlayer和AudioPlayer组件中添加互斥播放逻辑,当播放视频时自动暂停音频,反之亦然
为音频播放器添加默认封面图片
更新所有测试图片和音频资源URL为正式CDN地址
添加组件卸载时的清理逻辑
1 <!-- 1 <!--
2 * @Date: 2025-04-07 12:35:35 2 * @Date: 2025-04-07 12:35:35
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-05-16 16:50:23 4 + * @LastEditTime: 2025-05-30 15:26:15
5 * @FilePath: /mlaj/src/components/ui/AudioPlayer.vue 5 * @FilePath: /mlaj/src/components/ui/AudioPlayer.vue
6 * @Description: 音频播放器组件,支持播放控制、进度条调节、音量控制、播放列表等功能 6 * @Description: 音频播放器组件,支持播放控制、进度条调节、音量控制、播放列表等功能
7 --> 7 -->
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
12 <div class="flex flex-col items-center mb-4"> 12 <div class="flex flex-col items-center mb-4">
13 <!-- 歌曲封面 --> 13 <!-- 歌曲封面 -->
14 <div class="w-24 h-24 rounded-lg overflow-hidden mb-2"> 14 <div class="w-24 h-24 rounded-lg overflow-hidden mb-2">
15 - <img :src="currentSong?.cover" alt="封面" class="w-full h-full object-cover"> 15 + <img :src="currentSong?.cover ? currentSong?.cover : 'https://cdn.ipadbiz.cn/mlaj/images/audio_d_cover.jpg'" alt="封面" class="w-full h-full object-cover">
16 </div> 16 </div>
17 17
18 <!-- 歌曲信息 --> 18 <!-- 歌曲信息 -->
...@@ -105,7 +105,7 @@ ...@@ -105,7 +105,7 @@
105 > 105 >
106 <div class="absolute top-0 left-0 right-0 h-[1px] bg-gray-200"></div> 106 <div class="absolute top-0 left-0 right-0 h-[1px] bg-gray-200"></div>
107 <div class="w-16 h-16 rounded-lg overflow-hidden mr-4 flex-shrink-0"> 107 <div class="w-16 h-16 rounded-lg overflow-hidden mr-4 flex-shrink-0">
108 - <img :src="song?.cover" alt="封面" class="w-full h-full object-cover"> 108 + <img :src="song?.cover ? song?.cover : 'https://cdn.ipadbiz.cn/mlaj/images/audio_d_cover.jpg'" alt="封面" class="w-full h-full object-cover">
109 </div> 109 </div>
110 <div class="flex-1"> 110 <div class="flex-1">
111 <div class="font-medium">{{ song?.title }}</div> 111 <div class="font-medium">{{ song?.title }}</div>
...@@ -177,6 +177,9 @@ const loadAudio = async () => { ...@@ -177,6 +177,9 @@ const loadAudio = async () => {
177 } 177 }
178 } 178 }
179 179
180 +// 定义组件事件
181 +const emit = defineEmits(['play', 'pause'])
182 +
180 // 播放控制:切换播放/暂停状态 183 // 播放控制:切换播放/暂停状态
181 const togglePlay = async () => { 184 const togglePlay = async () => {
182 if (isLoading.value) return 185 if (isLoading.value) return
...@@ -186,8 +189,10 @@ const togglePlay = async () => { ...@@ -186,8 +189,10 @@ const togglePlay = async () => {
186 } 189 }
187 if (isPlaying.value) { 190 if (isPlaying.value) {
188 await audio.value?.pause() 191 await audio.value?.pause()
192 + emit('pause', audio.value)
189 } else { 193 } else {
190 await audio.value?.play() 194 await audio.value?.play()
195 + emit('play', audio.value)
191 } 196 }
192 isPlaying.value = !isPlaying.value 197 isPlaying.value = !isPlaying.value
193 } catch (error) { 198 } catch (error) {
...@@ -195,6 +200,19 @@ const togglePlay = async () => { ...@@ -195,6 +200,19 @@ const togglePlay = async () => {
195 } 200 }
196 } 201 }
197 202
203 +// 暴露给父组件的方法
204 +defineExpose({
205 + togglePlay,
206 + pause: () => {
207 + if (isPlaying.value && audio.value) {
208 + audio.value.pause();
209 + isPlaying.value = false;
210 + emit('pause', audio.value);
211 + }
212 + },
213 + isPlaying: () => isPlaying.value
214 +})
215 +
198 // 进度更新 216 // 进度更新
199 const updateProgress = () => { 217 const updateProgress = () => {
200 if (!audio.value) return 218 if (!audio.value) return
...@@ -223,6 +241,8 @@ const prevSong = async () => { ...@@ -223,6 +241,8 @@ const prevSong = async () => {
223 const normalizedVolume = Math.pow(volume.value / 100, 2) 241 const normalizedVolume = Math.pow(volume.value / 100, 2)
224 audio.value.volume = normalizedVolume 242 audio.value.volume = normalizedVolume
225 isPlaying.value = true 243 isPlaying.value = true
244 + // 发射播放事件
245 + emit('play', audio.value)
226 } 246 }
227 } 247 }
228 248
...@@ -241,6 +261,8 @@ const nextSong = async () => { ...@@ -241,6 +261,8 @@ const nextSong = async () => {
241 const normalizedVolume = Math.pow(volume.value / 100, 2) 261 const normalizedVolume = Math.pow(volume.value / 100, 2)
242 audio.value.volume = normalizedVolume 262 audio.value.volume = normalizedVolume
243 isPlaying.value = true 263 isPlaying.value = true
264 + // 发射播放事件
265 + emit('play', audio.value)
244 } 266 }
245 } 267 }
246 268
...@@ -312,6 +334,8 @@ const selectSong = async (index) => { ...@@ -312,6 +334,8 @@ const selectSong = async (index) => {
312 if (audio.value) { 334 if (audio.value) {
313 await audio.value.play() 335 await audio.value.play()
314 isPlaying.value = true 336 isPlaying.value = true
337 + // 发射播放事件
338 + emit('play', audio.value)
315 // 关闭播放列表 339 // 关闭播放列表
316 isPlaylistVisible.value = false 340 isPlaylistVisible.value = false
317 // 滚动到当前播放的音频 341 // 滚动到当前播放的音频
......
...@@ -119,12 +119,12 @@ onBeforeUnmount(() => { ...@@ -119,12 +119,12 @@ onBeforeUnmount(() => {
119 defineExpose({ 119 defineExpose({
120 pause() { 120 pause() {
121 if (player.value) { 121 if (player.value) {
122 - player.value.pause(); 122 + player.value?.pause();
123 } 123 }
124 }, 124 },
125 play() { 125 play() {
126 if (player.value) { 126 if (player.value) {
127 - player.value.play(); 127 + player.value?.play();
128 } 128 }
129 }, 129 },
130 }); 130 });
......
This diff is collapsed. Click to expand it.