refactor(player): 重构播放器组件并优化音乐状态管理

- 优化了音乐收藏和点赞状态的管理逻辑
- 重构了播放器组件中的多个函数,提高了代码可读性和维护性
- 添加了 MusicStatusDTO 类,用于封装音乐状态信息
- 优化了歌词显示和滚动逻辑
- 简化了部分代码结构,提高了执行效率
This commit is contained in:
ikmkj
2025-07-26 00:08:07 +08:00
parent 439d4b7588
commit a37a47bdab
3 changed files with 96 additions and 203 deletions

View File

@@ -0,0 +1,24 @@
package com.test.musichouduan.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
/**
* 音乐状态DTO用于封装收藏和点赞状态
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MusicStatusDTO {
/**
* 是否已收藏
*/
private boolean collected;
/**
* 是否已点赞
*/
private boolean liked;
}

View File

@@ -92,10 +92,10 @@
</div> </div>
<div class="collect-button" v-if="currentMusic" style="margin-right: 15px;"> <div class="collect-button" v-if="currentMusic" style="margin-right: 15px;">
<el-tooltip :content="currentMusic.isCollected ? '取消收藏' : '收藏'" placement="top"> <el-tooltip :content="currentMusic.collected ? '取消收藏' : '收藏'" placement="top">
<el-button circle @click="toggleCollect"> <el-button circle @click="toggleCollect">
<el-icon> <el-icon>
<component :is="currentMusic.isCollected ? 'StarFilled' : 'Star'" /> <component :is="currentMusic.collected ? 'StarFilled' : 'Star'" />
</el-icon> </el-icon>
</el-button> </el-button>
</el-tooltip> </el-tooltip>
@@ -288,6 +288,10 @@ import { usePlayerStore } from '../stores/player'
import { useUserStore } from '../stores/user' import { useUserStore } from '../stores/user'
import { processMusicUrl, toggleCollectMusic, getMusicStatus } from '../api/music' import { processMusicUrl, toggleCollectMusic, getMusicStatus } from '../api/music'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import {
VideoPlay, VideoPause, Back, Right, List, Close, Delete,
Mute, VideoCamera, Microphone, Star, StarFilled, ArrowDown
} from '@element-plus/icons-vue'
const router = useRouter() const router = useRouter()
const userStore = useUserStore() const userStore = useUserStore()
@@ -320,22 +324,18 @@ const loopMode = computed(() => playerStore.getLoop)
// 获取歌手名称 // 获取歌手名称
const getSingerNames = computed(() => { const getSingerNames = computed(() => {
if (!currentMusic.value) return '' if (!currentMusic.value) return ''
if (currentMusic.value.singerNames && currentMusic.value.singerNames.length > 0) { if (currentMusic.value.singerNames && currentMusic.value.singerNames.length > 0) {
return currentMusic.value.singerNames.join('、') return currentMusic.value.singerNames.join('、')
} }
return currentMusic.value.singer || '未知歌手' return currentMusic.value.singer || '未知歌手'
}) })
// 获取音乐的歌手名称 // 获取音乐的歌手名称
const getSingerNamesFromMusic = (music) => { const getSingerNamesFromMusic = (music) => {
if (!music) return '' if (!music) return ''
if (music.singerNames && music.singerNames.length > 0) { if (music.singerNames && music.singerNames.length > 0) {
return music.singerNames.join('、') return music.singerNames.join('、')
} }
return music.singer || '未知歌手' return music.singer || '未知歌手'
} }
@@ -353,70 +353,48 @@ const volumeIcon = computed(() => {
// 循环模式图标 // 循环模式图标
const loopModeIcon = computed(() => { const loopModeIcon = computed(() => {
switch (loopMode.value) { switch (loopMode.value) {
case 'single': case 'single': return 'Refresh'
return 'Refresh' case 'list': return 'Sort'
case 'list': default: return 'Connection'
return 'Sort'
default:
return 'Connection'
} }
}) })
// 循环模式文本 // 循环模式文本
const loopModeText = computed(() => { const loopModeText = computed(() => {
switch (loopMode.value) { switch (loopMode.value) {
case 'single': case 'single': return '单曲循环'
return '单曲循环' case 'list': return '列表循环'
case 'list': default: return '顺序播放'
return '列表循环'
default:
return '顺序播放'
} }
}) })
// 解析歌词 // 解析歌词
const parsedLyric = computed(() => { const parsedLyric = computed(() => {
if (!currentMusic.value || !currentMusic.value.lyric) { if (!currentMusic.value || !currentMusic.value.lyric) return []
return []
}
const lyric = currentMusic.value.lyric const lyric = currentMusic.value.lyric
const lines = lyric.split('\n') const lines = lyric.split('\n')
const timeRegExp = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/g const timeRegExp = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/g
const result = [] const result = []
for (const line of lines) { for (const line of lines) {
// 使用正则表达式的g标志匹配所有时间标记
const timeMatches = [...line.matchAll(timeRegExp)] const timeMatches = [...line.matchAll(timeRegExp)]
if (timeMatches.length > 0) { if (timeMatches.length > 0) {
// 取第一个时间标记作为歌词时间 const match = timeMatches
const match = timeMatches[0] const min = parseInt(match)
const min = parseInt(match[1]) const sec = parseInt(match)
const sec = parseInt(match[2]) const ms = parseInt(match)
const ms = parseInt(match[3])
const time = min * 60 + sec + ms / 1000 const time = min * 60 + sec + ms / 1000
// 删除所有时间标记
let text = line.replace(timeRegExp, '').trim() let text = line.replace(timeRegExp, '').trim()
// 只有当文本非空时才添加到结果中
if (text) { if (text) {
result.push({ time, text }) result.push({ time, text })
} }
} }
} }
// 按时间排序
return result.sort((a, b) => a.time - b.time) return result.sort((a, b) => a.time - b.time)
}) })
// 格式化时间 // 格式化时间
const formatTime = (seconds) => { const formatTime = (seconds) => {
if (isNaN(seconds) || seconds < 0) { if (isNaN(seconds) || seconds < 0) return '00:00'
return '00:00'
}
const mins = Math.floor(seconds / 60) const mins = Math.floor(seconds / 60)
const secs = Math.floor(seconds % 60) const secs = Math.floor(seconds % 60)
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
@@ -425,10 +403,8 @@ const formatTime = (seconds) => {
// 格式化日期 // 格式化日期
const formatDate = (dateStr) => { const formatDate = (dateStr) => {
if (!dateStr) return '' if (!dateStr) return ''
try { try {
const date = new Date(dateStr) return new Date(dateStr).toLocaleDateString()
return date.toLocaleDateString()
} catch (e) { } catch (e) {
return dateStr return dateStr
} }
@@ -443,31 +419,19 @@ const togglePlay = () => {
} }
} }
// 上一首 // 上一首/下一首
const prevSong = () => { const prevSong = () => playerStore.playPrev()
playerStore.playPrev() const nextSong = () => playerStore.playNext()
}
// 下一首 // 静音切换
const nextSong = () => {
playerStore.playNext()
}
// 切换静音
const toggleMute = () => { const toggleMute = () => {
if (volume.value > 0) { playerStore.setVolume(volume.value > 0 ? 0 : 80)
playerStore.setVolume(0)
} else {
playerStore.setVolume(80)
}
} }
// 处理音量变化 // 音量变化
const handleVolumeChange = (val) => { const handleVolumeChange = (val) => playerStore.setVolume(val)
playerStore.setVolume(val)
}
// 切换循环模式 // 循环模式切换
const toggleLoopMode = () => { const toggleLoopMode = () => {
const modes = ['none', 'list', 'single'] const modes = ['none', 'list', 'single']
const currentIndex = modes.indexOf(loopMode.value) const currentIndex = modes.indexOf(loopMode.value)
@@ -475,23 +439,19 @@ const toggleLoopMode = () => {
playerStore.setLoop(modes[nextIndex]) playerStore.setLoop(modes[nextIndex])
} }
// 切换收藏状态 // 收藏/取消收藏
const toggleCollect = async () => { const toggleCollect = async () => {
if (!userStore.isLoggedIn) { if (!userStore.isLoggedIn) {
ElMessage.warning('请先登录') ElMessage.warning('请先登录')
router.push('/login') router.push('/login')
return return
} }
if (!currentMusic.value || !currentMusic.value.id) return
if (!currentMusic.value || !currentMusic.value.id) {
return
}
try { try {
const res = await toggleCollectMusic(currentMusic.value.id) const res = await toggleCollectMusic(currentMusic.value.id)
if (res.code === 200) { if (res.code === 200) {
// 直接使用后端返回的最新状态 playerStore.updateCurrentMusicStatus({ collected: res.data })
playerStore.updateCurrentMusicStatus({ collected: res.data });
ElMessage.success(res.message || (res.data ? '收藏成功' : '已取消收藏')) ElMessage.success(res.message || (res.data ? '收藏成功' : '已取消收藏'))
} else { } else {
ElMessage.error(res.message || '操作失败') ElMessage.error(res.message || '操作失败')
@@ -502,157 +462,61 @@ const toggleCollect = async () => {
} }
} }
// 切换播放列表显示 // 播放列表相关
const togglePlaylist = () => { const togglePlaylist = () => playerStore.setShowPlaylist(!showPlaylist.value)
playerStore.setShowPlaylist(!showPlaylist.value) const playFromPlaylist = (index) => playerStore.playFromPlaylist(index)
} const removeFromPlaylist = (index) => playerStore.removeFromPlaylist(index)
const clearPlaylist = () => playerStore.clearPlaylist()
// 从播放列表播放 // 展开/收起播放器
const playFromPlaylist = (index) => {
playerStore.playFromPlaylist(index)
}
// 从播放列表移除
const removeFromPlaylist = (index) => {
playerStore.removeFromPlaylist(index)
}
// 清空播放列表
const clearPlaylist = () => {
playerStore.clearPlaylist()
}
// 切换展开状态
const toggleExpand = () => { const toggleExpand = () => {
isExpanded.value = !isExpanded.value isExpanded.value = !isExpanded.value
document.body.style.overflow = isExpanded.value ? 'hidden' : ''
// 当展开播放器时,禁止外部页面滚动
if (isExpanded.value) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
} }
// 跳转到音乐详情页面 // 进度条处理
const goToMusicDetail = () => { const isUserSeeking = ref(false)
if (currentMusic.value && currentMusic.value.id) {
router.push(`/music/${currentMusic.value.id}`)
}
}
// 处理进度条变化
const handleSliderChange = (val) => { const handleSliderChange = (val) => {
// 暂停监听时间更新,避免冲突
isUserSeeking.value = true isUserSeeking.value = true
playerStore.setCurrentTime(val) playerStore.setCurrentTime(val)
// 延迟恢复监听,确保播放器有时间响应 setTimeout(() => { isUserSeeking.value = false }, 500)
setTimeout(() => {
isUserSeeking.value = false
}, 500) // 增加延迟时间,确保播放器有足够时间响应
} }
const handleSliderInput = () => { isUserSeeking.value = true }
const formatSliderTooltip = (val) => formatTime(val)
// 处理进度条输入(拖动过程中) // 监听 Pinia store 的时间更新
const handleSliderInput = (val) => {
isUserSeeking.value = true
}
// 格式化进度条提示文本
const formatSliderTooltip = (val) => {
return formatTime(val)
}
// 标记用户是否正在拖动进度条
const isUserSeeking = ref(false)
// 监听当前时间变化,更新滑块位置
watch(currentTime, (newVal) => { watch(currentTime, (newVal) => {
// 只有当用户不在拖动进度条时才更新滑块位置
if (!isUserSeeking.value) { if (!isUserSeeking.value) {
sliderTime.value = newVal sliderTime.value = newVal
} }
// 更新歌词
// 更新当前歌词索引
if (parsedLyric.value.length > 0) { if (parsedLyric.value.length > 0) {
let foundMatch = false let newIndex = parsedLyric.value.findIndex(line => line.time > newVal) - 1
for (let i = 0; i < parsedLyric.value.length; i++) { if (newIndex < 0) newIndex = parsedLyric.value.length - 1
// 如果当前时间小于歌词时间,则使用前一行歌词 if (currentLyricIndex.value !== newIndex) {
if (newVal < parsedLyric.value[i].time) { currentLyricIndex.value = newIndex
if (i > 0 && currentLyricIndex.value !== i - 1) { if (isExpanded.value) scrollToActiveLyric()
currentLyricIndex.value = i - 1
if (isExpanded.value) {
scrollToActiveLyric()
}
}
foundMatch = true
break
}
// 如果是最后一行歌词
else if (i === parsedLyric.value.length - 1) {
if (currentLyricIndex.value !== i) {
currentLyricIndex.value = i
if (isExpanded.value) {
scrollToActiveLyric()
}
}
foundMatch = true
}
}
// 如果没有找到匹配的歌词,则重置到第一行
if (!foundMatch && currentLyricIndex.value !== -1) {
currentLyricIndex.value = -1
} }
} }
}) })
// 滚动到当前歌词 // 滚动到当前歌词
const scrollToActiveLyric = async () => { const scrollToActiveLyric = async () => {
await nextTick()
if (lyricScrollbar.value && currentLyricIndex.value >= 0) { if (lyricScrollbar.value && currentLyricIndex.value >= 0) {
await nextTick() const element = lyricLines.value[currentLyricIndex.value]
try { if (element) {
// 使用ref获取当前歌词元素 lyricScrollbar.value.setScrollTop(element.offsetTop - 150)
const element = lyricLines.value[currentLyricIndex.value]
if (element) {
// 尝试多种方法来确保滚动生效
// 方法 1: 使用scrollIntoView
element.scrollIntoView({ behavior: 'smooth', block: 'center' })
// 方法 2: 使用el-scrollbar的方法
if (lyricScrollbar.value.setScrollTop) {
lyricScrollbar.value.setScrollTop(element.offsetTop - 150, 300)
}
// 方法 3: 直接设置scrollTop
if (lyricScrollbar.value.wrap) {
setTimeout(() => {
lyricScrollbar.value.wrap.scrollTop = element.offsetTop - 150
}, 10)
}
}
} catch (error) {
console.error('Failed to scroll to active lyric:', error)
} }
} }
} }
// 监听当前音乐变化处理音乐URL和状态 // 监听当前播放歌曲的变化
watch(currentMusic, (newMusic) => { watch(currentMusic, (newMusic) => {
if (newMusic && newMusic.id) { if (newMusic && newMusic.id) {
// 处理音乐URL // 处理 URL
const processedUrl = processMusicUrl(newMusic.url) playerStore.updateMusicUrl(newMusic.id, processMusicUrl(newMusic.url))
if (processedUrl !== newMusic.url) { playerStore.updateMusicCover(newMusic.id, processMusicUrl(newMusic.cover))
playerStore.updateMusicUrl(newMusic.id, processedUrl)
}
// 处理封面URL
if (newMusic.cover) {
const processedCover = processMusicUrl(newMusic.cover)
if (processedCover !== newMusic.cover) {
playerStore.updateMusicCover(newMusic.id, processedCover)
}
}
// 主动获取最新的收藏和点赞状态 // 主动获取最新的收藏和点赞状态
if (userStore.isLoggedIn) { if (userStore.isLoggedIn) {
@@ -663,22 +527,15 @@ watch(currentMusic, (newMusic) => {
liked: res.data.liked liked: res.data.liked
}); });
} }
}).catch(err => { }).catch(err => console.error("获取播放器音乐状态失败:", err));
console.error("获取播放器音乐状态失败:", err);
});
} else { } else {
// 未登录则重置状态
playerStore.updateCurrentMusicStatus({ collected: false, liked: false }); playerStore.updateCurrentMusicStatus({ collected: false, liked: false });
} }
// 重置歌词索引和元素引用 // 重置歌词
currentLyricIndex.value = -1 currentLyricIndex.value = -1
lyricLines.value = {} lyricLines.value = {}
if (isExpanded.value) expandedActiveTab.value = 'lyric'
// 如果展开了播放器,切换到歌词标签页
if (isExpanded.value) {
expandedActiveTab.value = 'lyric'
}
} }
}) })
</script> </script>

View File

@@ -280,6 +280,18 @@ export const usePlayerStore = defineStore('player', {
if (index !== -1) { if (index !== -1) {
this.playlist[index].cover = cover this.playlist[index].cover = cover
} }
},
// 更新当前音乐的状态(如收藏、点赞)
updateCurrentMusicStatus(status) {
if (this.currentMusic) {
this.currentMusic = { ...this.currentMusic, ...status };
const index = this.playlist.findIndex(item => item.id === this.currentMusic.id);
if (index !== -1) {
this.playlist[index] = { ...this.playlist[index], ...status };
}
}
} }
} }
}) })