feat(Playroom): add lyrics display functionality with animations and preferences management

This commit is contained in:
Astrian Zheng 2025-05-26 19:34:13 +10:00
parent 95d00616d0
commit 588182e888
Signed by: Astrian
SSH Key Fingerprint: SHA256:rVnhx3DAKjujCwWE13aDl7uV6+9U1MvydLkNRXJrBiA
2 changed files with 134 additions and 42 deletions

View File

@ -41,7 +41,7 @@ const songInfo = useTemplateRef('songInfo')
const playButton = useTemplateRef('playButton') const playButton = useTemplateRef('playButton')
const presentQueueListDialog = ref(false) const presentQueueListDialog = ref(false)
// const presentLyrics = ref(false) const presentLyrics = ref(false)
onMounted(async () => { onMounted(async () => {
Draggable.create(progressBarThumb.value, { Draggable.create(progressBarThumb.value, {
@ -55,7 +55,7 @@ onMounted(async () => {
} }
}) })
thumbUpdate() thumbUpdate()
setupEntranceAnimations() setupEntranceAnimations()
}) })
@ -115,13 +115,13 @@ function setupEntranceAnimations() {
if (controllerRef.value) { if (controllerRef.value) {
gsap.fromTo(controllerRef.value.children, gsap.fromTo(controllerRef.value.children,
{ opacity: 0, y: 30, scale: 0.95 }, { opacity: 0, y: 30, scale: 0.95 },
{ {
opacity: 1, y: 0, scale: 1, opacity: 1, y: 0, scale: 1,
duration: 0.6, ease: "power2.out", stagger: 0.1 duration: 0.6, ease: "power2.out", stagger: 0.1
} }
) )
} }
if (lyricsSection.value) { if (lyricsSection.value) {
gsap.fromTo(lyricsSection.value, gsap.fromTo(lyricsSection.value,
{ opacity: 0, x: 50 }, { opacity: 0, x: 50 },
@ -147,7 +147,7 @@ function handlePlayPause() {
function toggleShuffle() { function toggleShuffle() {
playQueueStore.playMode.shuffle = !playQueueStore.playMode.shuffle playQueueStore.playMode.shuffle = !playQueueStore.playMode.shuffle
playQueueStore.shuffleCurrent = false playQueueStore.shuffleCurrent = false
nextTick(() => { nextTick(() => {
const shuffleBtn = playQueueDialog.value?.querySelector('.flex-1:first-child') as HTMLElement const shuffleBtn = playQueueDialog.value?.querySelector('.flex-1:first-child') as HTMLElement
if (shuffleBtn) { if (shuffleBtn) {
@ -167,7 +167,7 @@ function toggleRepeat() {
}) })
} }
}) })
switch (playQueueStore.playMode.repeat) { switch (playQueueStore.playMode.repeat) {
case 'off': playQueueStore.playMode.repeat = 'all'; break case 'off': playQueueStore.playMode.repeat = 'all'; break
case 'all': playQueueStore.playMode.repeat = 'single'; break case 'all': playQueueStore.playMode.repeat = 'single'; break
@ -177,17 +177,17 @@ function toggleRepeat() {
function makePlayQueueListPresent() { function makePlayQueueListPresent() {
presentQueueListDialog.value = true presentQueueListDialog.value = true
nextTick(() => { nextTick(() => {
if (!playQueueDialogContainer.value || !playQueueDialog.value) return if (!playQueueDialogContainer.value || !playQueueDialog.value) return
const tl = gsap.timeline() const tl = gsap.timeline()
tl.to(playQueueDialogContainer.value, { tl.to(playQueueDialogContainer.value, {
backgroundColor: '#17171780', duration: 0.3, ease: 'power2.out' backgroundColor: '#17171780', duration: 0.3, ease: 'power2.out'
}).to(playQueueDialog.value, { }).to(playQueueDialog.value, {
x: 0, duration: 0.4, ease: 'power3.out' x: 0, duration: 0.4, ease: 'power3.out'
}, '<0.1') }, '<0.1')
if (playQueueDialog.value.children.length > 0) { if (playQueueDialog.value.children.length > 0) {
tl.fromTo(playQueueDialog.value.children, tl.fromTo(playQueueDialog.value.children,
{ opacity: 0, x: -20 }, { opacity: 0, x: -20 },
@ -201,7 +201,7 @@ function makePlayQueueListDismiss() {
presentQueueListDialog.value = false presentQueueListDialog.value = false
return return
} }
const tl = gsap.timeline({ const tl = gsap.timeline({
onComplete: () => { onComplete: () => {
presentQueueListDialog.value = false presentQueueListDialog.value = false
@ -213,19 +213,19 @@ function makePlayQueueListDismiss() {
} }
} }
}) })
if (playQueueDialog.value.children.length > 0) { if (playQueueDialog.value.children.length > 0) {
tl.to(playQueueDialog.value.children, { tl.to(playQueueDialog.value.children, {
opacity: 0, x: -20, duration: 0.2, ease: 'power2.in', stagger: 0.03 opacity: 0, x: -20, duration: 0.2, ease: 'power2.in', stagger: 0.03
}) })
} }
tl.to(playQueueDialog.value, { tl.to(playQueueDialog.value, {
x: -384, duration: 0.3, ease: 'power2.in' x: -384, duration: 0.3, ease: 'power2.in'
}, playQueueDialog.value.children.length > 0 ? '<0.1' : '0') }, playQueueDialog.value.children.length > 0 ? '<0.1' : '0')
.to(playQueueDialogContainer.value, { .to(playQueueDialogContainer.value, {
backgroundColor: 'transparent', duration: 0.2, ease: 'power2.in' backgroundColor: 'transparent', duration: 0.2, ease: 'power2.in'
}, '<') }, '<')
} }
function getCurrentTrack() { function getCurrentTrack() {
@ -236,6 +236,70 @@ function getCurrentTrack() {
} }
} }
watch(() => [preferences.presentLyrics, getCurrentTrack().song.lyricUrl], (newValue, oldValue) => {
const [showLyrics, hasLyricUrl] = newValue
const [prevShowLyrics, _prevHasLyricUrl] = oldValue || [false, null]
// Show lyrics when both conditions are met
if (showLyrics && hasLyricUrl) {
presentLyrics.value = true
nextTick(() => {
const tl = gsap.timeline()
tl.from(controllerRef.value, {
marginRight: '-40rem',
}).fromTo(lyricsSection.value,
{ opacity: 0, x: 50, scale: 0.95 },
{ opacity: 1, x: 0, scale: 1, duration: 0.5, ease: "power2.out" },
"-=0.3"
)
})
}
// Hide lyrics with different animations based on reason
else if (presentLyrics.value) {
let animationConfig
// If lyrics were toggled off
if (prevShowLyrics && !showLyrics) {
animationConfig = {
opacity: 0, x: -50, scale: 0.95,
duration: 0.3, ease: "power2.in"
}
}
// If no lyrics available (song changed)
else if (!hasLyricUrl) {
animationConfig = {
opacity: 0, y: -20, scale: 0.98,
duration: 0.3, ease: "power1.in"
}
}
// Default animation
else {
animationConfig = {
opacity: 0, x: -50,
duration: 0.3, ease: "power2.in"
}
}
const tl = gsap.timeline({
onComplete: () => {
presentLyrics.value = false
}
})
tl.to(controllerRef.value, {
marginLeft: '44rem',
duration: 0.3, ease: "power2.out"
})
.to(lyricsSection.value, animationConfig, '<')
.set(lyricsSection.value, {
opacity: 1, x: 0, y: 0, scale: 1 // Reset for next time
})
.set(controllerRef.value, {
marginLeft: '0rem' // Reset for next time
})
}
}, { immediate: true })
onUnmounted(() => { onUnmounted(() => {
}) })
@ -247,7 +311,7 @@ watch(() => playQueueStore.currentIndex, () => {
ease: "power2.inOut", yoyo: true, repeat: 1 ease: "power2.inOut", yoyo: true, repeat: 1
}) })
} }
if (songInfo.value) { if (songInfo.value) {
gsap.fromTo(songInfo.value, gsap.fromTo(songInfo.value,
{ opacity: 0, y: 10 }, { opacity: 0, y: 10 },
@ -267,15 +331,15 @@ watch(() => playQueueStore.currentIndex, () => {
<!-- Main content area - new centered flex layout --> <!-- Main content area - new centered flex layout -->
<div class="absolute top-0 left-0 flex justify-center h-screen w-screen overflow-y-auto z-10 select-none"> <div class="absolute top-0 left-0 flex justify-center h-screen w-screen overflow-y-auto z-10 select-none">
<div class="flex items-center justify-center gap-16 h-fit my-auto" ref="mainContainer"> <div class="flex items-center justify-center gap-16 h-fit my-auto" ref="mainContainer">
<!-- Controller area --> <!-- Controller area -->
<div class="flex flex-col w-96 gap-4" ref="controllerRef"> <div class="flex flex-col w-96 gap-4" ref="controllerRef">
<!-- Album cover with enhanced hover effect --> <!-- Album cover with enhanced hover effect -->
<div ref="albumCover" class="relative"> <div ref="albumCover" class="relative">
<img :src="getCurrentTrack().album?.coverUrl" <img :src="getCurrentTrack().album?.coverUrl"
class="rounded-2xl shadow-2xl border border-white/20 w-96 h-96 transition-transform duration-300 hover:scale-105" /> class="rounded-2xl shadow-2xl border border-white/20 w-96 h-96 transition-transform duration-300 hover:scale-105" />
</div> </div>
<!-- Song info with enhanced styling --> <!-- Song info with enhanced styling -->
<div class="flex justify-between items-center gap-2" ref="songInfo"> <div class="flex justify-between items-center gap-2" ref="songInfo">
<div class="relative flex-auto w-0"> <div class="relative flex-auto w-0">
@ -301,7 +365,8 @@ watch(() => playQueueStore.currentIndex, () => {
</div> </div>
</div> </div>
<button class="h-10 w-10 flex justify-center items-center rounded-full bg-black/10 backdrop-blur-3xl transition-all duration-200 hover:bg-black/20 hover:scale-110" <button
class="h-10 w-10 flex justify-center items-center rounded-full bg-black/10 backdrop-blur-3xl transition-all duration-200 hover:bg-black/20 hover:scale-110"
ref="favoriteButton"> ref="favoriteButton">
<span class="text-white"> <span class="text-white">
<StarEmptyIcon :size="6" /> <StarEmptyIcon :size="6" />
@ -312,7 +377,8 @@ watch(() => playQueueStore.currentIndex, () => {
<!-- Progress section --> <!-- Progress section -->
<div class="flex flex-col gap-1" ref="progressSection"> <div class="flex flex-col gap-1" ref="progressSection">
<!-- ...existing progress bar code... --> <!-- ...existing progress bar code... -->
<div class="w-full p-[0.125rem] bg-white/20 shadow-[0_.125rem_1rem_0_#00000010] rounded-full backdrop-blur-3xl"> <div
class="w-full p-[0.125rem] bg-white/20 shadow-[0_.125rem_1rem_0_#00000010] rounded-full backdrop-blur-3xl">
<div class="w-full" ref="progressBarContainer"> <div class="w-full" ref="progressBarContainer">
<div class="w-2 h-2 bg-white rounded-full shadow-md" ref="progressBarThumb" /> <div class="w-2 h-2 bg-white rounded-full shadow-md" ref="progressBarThumb" />
</div> </div>
@ -321,7 +387,8 @@ watch(() => playQueueStore.currentIndex, () => {
<div class="w-full flex justify-between"> <div class="w-full flex justify-between">
<!-- ...existing time display code... --> <!-- ...existing time display code... -->
<div class="font-medium flex-1 text-left relative"> <div class="font-medium flex-1 text-left relative">
<span class="text-black blur-lg absolute top-0">{{ timeFormatter(Math.floor(playQueueStore.currentTime)) }}</span> <span
class="text-black blur-lg absolute top-0">{{ timeFormatter(Math.floor(playQueueStore.currentTime)) }}</span>
<span class="text-white/90">{{ timeFormatter(Math.floor(playQueueStore.currentTime)) }}</span> <span class="text-white/90">{{ timeFormatter(Math.floor(playQueueStore.currentTime)) }}</span>
</div> </div>
<div class="text-xs text-center relative"> <div class="text-xs text-center relative">
@ -330,9 +397,11 @@ watch(() => playQueueStore.currentIndex, () => {
</div> </div>
<div class="flex flex-1"> <div class="flex flex-1">
<div class="flex-1" /> <div class="flex-1" />
<button class="text-white/90 font-medium text-right relative transition-colors duration-200 hover:text-white" <button
class="text-white/90 font-medium text-right relative transition-colors duration-200 hover:text-white"
@click="preferences.displayTimeLeft = !preferences.displayTimeLeft"> @click="preferences.displayTimeLeft = !preferences.displayTimeLeft">
<span class="text-black blur-lg absolute top-0">{{ `${preferences.displayTimeLeft ? '-' : ''}${timeFormatter(preferences.displayTimeLeft ? Math.floor(playQueueStore.duration) - Math.floor(playQueueStore.currentTime) : playQueueStore.duration)}` }}</span> <span
class="text-black blur-lg absolute top-0">{{ `${preferences.displayTimeLeft ? '-' : ''}${timeFormatter(preferences.displayTimeLeft ? Math.floor(playQueueStore.duration) - Math.floor(playQueueStore.currentTime) : playQueueStore.duration)}` }}</span>
<span>{{ `${preferences.displayTimeLeft ? '-' : ''}${timeFormatter(preferences.displayTimeLeft ? Math.floor(playQueueStore.duration) - Math.floor(playQueueStore.currentTime) : playQueueStore.duration)}` }}</span> <span>{{ `${preferences.displayTimeLeft ? '-' : ''}${timeFormatter(preferences.displayTimeLeft ? Math.floor(playQueueStore.duration) - Math.floor(playQueueStore.currentTime) : playQueueStore.duration)}` }}</span>
</button> </button>
</div> </div>
@ -342,7 +411,8 @@ watch(() => playQueueStore.currentIndex, () => {
<!-- Control buttons --> <!-- Control buttons -->
<div class="w-full flex justify-between items-center" ref="controlButtons"> <div class="w-full flex justify-between items-center" ref="controlButtons">
<div class="flex-1 text-left flex gap-1"> <div class="flex-1 text-left flex gap-1">
<button class="h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110" <button
class="h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110"
ref="volumeButton"> ref="volumeButton">
<div class="w-6 h-6 relative"> <div class="w-6 h-6 relative">
<span class="text-black blur-md absolute top-0 left-0"> <span class="text-black blur-md absolute top-0 left-0">
@ -353,7 +423,8 @@ watch(() => playQueueStore.currentIndex, () => {
</span> </span>
</div> </div>
</button> </button>
<button class="text-white h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110" <button
class="text-white h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110"
@click="makePlayQueueListPresent" ref="queueButton"> @click="makePlayQueueListPresent" ref="queueButton">
<div class="w-6 h-6 relative"> <div class="w-6 h-6 relative">
<span class="text-black blur-md absolute top-0 left-0"> <span class="text-black blur-md absolute top-0 left-0">
@ -367,7 +438,8 @@ watch(() => playQueueStore.currentIndex, () => {
</div> </div>
<div class="flex-2 text-center align-center justify-center gap-2 flex"> <div class="flex-2 text-center align-center justify-center gap-2 flex">
<button class="text-white flex-1 h-10 flex justify-center items-center rounded-lg hover:bg-white/25 transition-all duration-200 hover:scale-105" <button
class="text-white flex-1 h-10 flex justify-center items-center rounded-lg hover:bg-white/25 transition-all duration-200 hover:scale-105"
@click="playPrevious" ref="prevButton"> @click="playPrevious" ref="prevButton">
<div class="w-8 h-8 relative"> <div class="w-8 h-8 relative">
<span class="text-black/80 blur-lg absolute top-0 left-0"> <span class="text-black/80 blur-lg absolute top-0 left-0">
@ -379,7 +451,8 @@ watch(() => playQueueStore.currentIndex, () => {
</div> </div>
</button> </button>
<button class="text-white flex-1 h-10 flex justify-center items-center rounded-lg hover:bg-white/25 transition-all duration-200" <button
class="text-white flex-1 h-10 flex justify-center items-center rounded-lg hover:bg-white/25 transition-all duration-200"
@click="handlePlayPause" ref="playButton"> @click="handlePlayPause" ref="playButton">
<!-- ...existing play/pause icon code... --> <!-- ...existing play/pause icon code... -->
<div v-if="playQueueStore.isPlaying"> <div v-if="playQueueStore.isPlaying">
@ -412,7 +485,8 @@ watch(() => playQueueStore.currentIndex, () => {
</div> </div>
</button> </button>
<button class="text-white flex-1 h-10 flex justify-center items-center rounded-lg hover:bg-white/25 transition-all duration-200 hover:scale-105" <button
class="text-white flex-1 h-10 flex justify-center items-center rounded-lg hover:bg-white/25 transition-all duration-200 hover:scale-105"
@click="playNext" ref="nextButton"> @click="playNext" ref="nextButton">
<div class="w-8 h-8 relative"> <div class="w-8 h-8 relative">
<span class="text-black/80 blur-lg absolute top-0 left-0"> <span class="text-black/80 blur-lg absolute top-0 left-0">
@ -427,8 +501,9 @@ watch(() => playQueueStore.currentIndex, () => {
<div class="flex-1 text-right flex gap-1"> <div class="flex-1 text-right flex gap-1">
<div class="flex-1" /> <div class="flex-1" />
<button class="text-white h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110" <button
ref="lyricsButton"> class="text-white h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110"
ref="lyricsButton" @click="preferences.presentLyrics = !preferences.presentLyrics">
<div class="w-6 h-6 relative"> <div class="w-6 h-6 relative">
<span class="text-black blur-md absolute top-0 left-0"> <span class="text-black blur-md absolute top-0 left-0">
<ChatBubbleQuoteIcon :size="6" /> <ChatBubbleQuoteIcon :size="6" />
@ -438,7 +513,8 @@ watch(() => playQueueStore.currentIndex, () => {
</span> </span>
</div> </div>
</button> </button>
<button class="text-white h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110" <button
class="text-white h-8 w-8 flex justify-center items-center rounded-full hover:bg-white/25 transition-all duration-200 hover:scale-110"
ref="moreButton"> ref="moreButton">
<div class="w-6 h-6 relative"> <div class="w-6 h-6 relative">
<span class="text-black blur-sm absolute top-0 left-0"> <span class="text-black blur-sm absolute top-0 left-0">
@ -454,9 +530,8 @@ watch(() => playQueueStore.currentIndex, () => {
</div> </div>
<!-- Lyrics section - full screen height --> <!-- Lyrics section - full screen height -->
<div class="w-[40rem] h-screen" ref="lyricsSection"> <div class="w-[40rem] h-screen" ref="lyricsSection" v-if="presentLyrics">
<ScrollingLyrics :lrcSrc="getCurrentTrack().song.lyricUrl ?? undefined" <ScrollingLyrics :lrcSrc="getCurrentTrack().song.lyricUrl ?? undefined" class="h-full" ref="scrollingLyrics" />
class="h-full" ref="scrollingLyrics" />
</div> </div>
</div> </div>
</div> </div>
@ -464,23 +539,27 @@ watch(() => playQueueStore.currentIndex, () => {
<!-- Queue list dialog with enhanced animations --> <!-- Queue list dialog with enhanced animations -->
<dialog :open="presentQueueListDialog" class="z-20 w-screen h-screen" @click="makePlayQueueListDismiss" <dialog :open="presentQueueListDialog" class="z-20 w-screen h-screen" @click="makePlayQueueListDismiss"
ref="playQueueDialogContainer" style="background-color: transparent;"> ref="playQueueDialogContainer" style="background-color: transparent;">
<div class="w-96 h-screen bg-neutral-900/80 shadow-[0_0_16px_0_rgba(0,0,0,0.5)] backdrop-blur-2xl pt-8 flex flex-col transform -translate-x-96" <div
class="w-96 h-screen bg-neutral-900/80 shadow-[0_0_16px_0_rgba(0,0,0,0.5)] backdrop-blur-2xl pt-8 flex flex-col transform -translate-x-96"
@click.stop ref="playQueueDialog"> @click.stop ref="playQueueDialog">
<div class="flex justify-between mx-8 mb-4"> <div class="flex justify-between mx-8 mb-4">
<div class="text-white font-medium text-2xl">播放队列</div> <div class="text-white font-medium text-2xl">播放队列</div>
<button class="text-white w-9 h-9 bg-neutral-800/80 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center transition-all duration-200 hover:bg-neutral-700/80 hover:scale-110" <button
class="text-white w-9 h-9 bg-neutral-800/80 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center transition-all duration-200 hover:bg-neutral-700/80 hover:scale-110"
@click="makePlayQueueListDismiss"> @click="makePlayQueueListDismiss">
<XIcon :size="4" /> <XIcon :size="4" />
</button> </button>
</div> </div>
<div class="flex gap-2 mx-8 mb-4"> <div class="flex gap-2 mx-8 mb-4">
<button class="flex-1 h-9 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center transition-all duration-200 hover:scale-105" <button
class="flex-1 h-9 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center transition-all duration-200 hover:scale-105"
:class="playQueueStore.playMode.shuffle ? 'bg-[#ffffffaa] text-neutral-700' : 'text-white bg-neutral-800/80'" :class="playQueueStore.playMode.shuffle ? 'bg-[#ffffffaa] text-neutral-700' : 'text-white bg-neutral-800/80'"
@click="toggleShuffle"> @click="toggleShuffle">
<ShuffleIcon :size="4" /> <ShuffleIcon :size="4" />
</button> </button>
<button class="flex-1 h-9 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center transition-all duration-200 hover:scale-105" <button
class="flex-1 h-9 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex justify-center items-center transition-all duration-200 hover:scale-105"
:class="playQueueStore.playMode.repeat === 'off' ? 'text-white bg-neutral-800/80' : 'bg-[#ffffffaa] text-neutral-700'" :class="playQueueStore.playMode.repeat === 'off' ? 'text-white bg-neutral-800/80' : 'bg-[#ffffffaa] text-neutral-700'"
@click="toggleRepeat"> @click="toggleRepeat">
<CycleTwoArrowsIcon :size="4" v-if="playQueueStore.playMode.repeat !== 'single'" /> <CycleTwoArrowsIcon :size="4" v-if="playQueueStore.playMode.repeat !== 'single'" />

View File

@ -10,6 +10,8 @@ declare global {
export const usePreferences = defineStore('preferences', () => { export const usePreferences = defineStore('preferences', () => {
const displayTimeLeft = ref<boolean>(false) const displayTimeLeft = ref<boolean>(false)
const presentLyrics = ref<boolean>(false)
const isLoaded = ref(false) const isLoaded = ref(false)
const storageType = ref<'chrome' | 'localStorage' | 'memory'>('chrome') const storageType = ref<'chrome' | 'localStorage' | 'memory'>('chrome')
const debugInfo = ref<string[]>([]) const debugInfo = ref<string[]>([])
@ -153,10 +155,11 @@ export const usePreferences = defineStore('preferences', () => {
addDebugInfo('开始初始化偏好设置...') addDebugInfo('开始初始化偏好设置...')
try { try {
const value = await getStoredValue('displayTimeLeft', false) const displayTimeLeftValue = await getStoredValue('displayTimeLeft', false)
displayTimeLeft.value = value as boolean displayTimeLeft.value = displayTimeLeftValue as boolean
const presentLyricsValue = await getStoredValue('presentLyrics', false)
presentLyrics.value = presentLyricsValue as boolean
isLoaded.value = true isLoaded.value = true
addDebugInfo(`✅ 偏好设置初始化完成: displayTimeLeft = ${value}`)
} catch (error) { } catch (error) {
addDebugInfo(`❌ 初始化失败: ${error}`) addDebugInfo(`❌ 初始化失败: ${error}`)
displayTimeLeft.value = false displayTimeLeft.value = false
@ -174,6 +177,15 @@ export const usePreferences = defineStore('preferences', () => {
} }
} }
}) })
watch(presentLyrics, async (val) => {
if (isLoaded.value) {
try {
await setStoredValue('presentLyrics', val)
} catch (error) {
addDebugInfo(`❌ 监听器保存失败: ${error}`)
}
}
})
// 手动保存函数(用于调试) // 手动保存函数(用于调试)
const manualSave = async () => { const manualSave = async () => {
@ -204,6 +216,7 @@ export const usePreferences = defineStore('preferences', () => {
return { return {
displayTimeLeft, displayTimeLeft,
presentLyrics,
isLoaded, isLoaded,
storageType, storageType,
debugInfo, debugInfo,