From 50bdaae26a1d0b7b7037a7e8ac2ec572b7448b67 Mon Sep 17 00:00:00 2001 From: saopig1 <4x7sw862st@gmail.com> Date: Thu, 2 Jul 2026 00:22:45 +0800 Subject: [PATCH] feat(spotify): frontend plumbing (source tab, badge, auth status) Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/components/SongCard.vue | 9 +++++++-- web/src/components/SourceTabs.vue | 1 + web/src/stores/player.ts | 21 +++++++++++++-------- web/src/stores/sourceTabs.ts | 2 +- web/src/styles/variables.scss | 2 ++ 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/web/src/components/SongCard.vue b/web/src/components/SongCard.vue index 9dbd3de..e79889d 100644 --- a/web/src/components/SongCard.vue +++ b/web/src/components/SongCard.vue @@ -7,8 +7,8 @@ {{ song.name }} {{ song.platform === 'bilibili' ? 'B站' : song.platform === 'qq' ? 'QQ' : song.platform === 'youtube' ? 'YouTube' : song.platform === 'local' ? '本地' : song.platform === 'kugou' ? '酷狗' : '网易云' }} + :class="song.platform === 'bilibili' ? 'badge-bilibili' : song.platform === 'qq' ? 'badge-qq' : song.platform === 'youtube' ? 'badge-youtube' : song.platform === 'local' ? 'badge-local' : song.platform === 'kugou' ? 'badge-kugou' : song.platform === 'spotify' ? 'badge-spotify' : 'badge-netease'" + >{{ song.platform === 'bilibili' ? 'B站' : song.platform === 'qq' ? 'QQ' : song.platform === 'youtube' ? 'YouTube' : song.platform === 'local' ? '本地' : song.platform === 'kugou' ? '酷狗' : song.platform === 'spotify' ? 'Spotify' : '网易云' }}
{{ song.artist }}
@@ -145,6 +145,11 @@ function formatDuration(seconds: number): string { color: var(--brand-kugou); } +.badge-spotify { + background: var(--brand-spotify-12); + color: var(--brand-spotify); +} + .song-artist { font-size: 12px; color: var(--text-secondary); diff --git a/web/src/components/SourceTabs.vue b/web/src/components/SourceTabs.vue index ef4b02d..0b33372 100644 --- a/web/src/components/SourceTabs.vue +++ b/web/src/components/SourceTabs.vue @@ -25,6 +25,7 @@ const LABELS: Record = { netease: '网易云', qq: 'QQ', kugou: '酷狗', + spotify: 'Spotify', }; defineProps<{ diff --git a/web/src/stores/player.ts b/web/src/stores/player.ts index 6abc868..496eb7d 100644 --- a/web/src/stores/player.ts +++ b/web/src/stores/player.ts @@ -10,10 +10,10 @@ export interface Song { album: string; duration: number; coverUrl: string; - platform: 'netease' | 'qq' | 'bilibili' | 'youtube' | 'local' | 'kugou'; + platform: 'netease' | 'qq' | 'bilibili' | 'youtube' | 'local' | 'kugou' | 'spotify'; } -export type Source = 'netease' | 'qq' | 'kugou'; +export type Source = 'netease' | 'qq' | 'kugou' | 'spotify'; export interface BotStatus { id: string; @@ -100,11 +100,11 @@ export const usePlayerStore = defineStore('player', { theme: 'dark' as 'dark' | 'light', // Home page cache, split by source - recommendPlaylists: { netease: [] as PlaylistItem[], qq: [] as PlaylistItem[], kugou: [] as PlaylistItem[] }, - dailySongs: { netease: [] as Song[], qq: [] as Song[], kugou: [] as Song[] }, - userPlaylists: { netease: [] as PlaylistItem[], qq: [] as PlaylistItem[], kugou: [] as PlaylistItem[] }, + recommendPlaylists: { netease: [] as PlaylistItem[], qq: [] as PlaylistItem[], kugou: [] as PlaylistItem[], spotify: [] as PlaylistItem[] }, + dailySongs: { netease: [] as Song[], qq: [] as Song[], kugou: [] as Song[], spotify: [] as Song[] }, + userPlaylists: { netease: [] as PlaylistItem[], qq: [] as PlaylistItem[], kugou: [] as PlaylistItem[], spotify: [] as PlaylistItem[] }, bilibiliPopular: [] as Song[], - authStatus: { netease: false, qq: false, kugou: false }, + authStatus: { netease: false, qq: false, kugou: false, spotify: false }, lastFetchTime: 0, // Favorited playlists (fetched from server, isolated per WebUI user) @@ -158,6 +158,7 @@ export const usePlayerStore = defineStore('player', { if (this.authStatus.netease) s.push('netease'); if (this.authStatus.qq) s.push('qq'); if (this.authStatus.kugou) s.push('kugou'); + if (this.authStatus.spotify) s.push('spotify'); return s; }, }, @@ -572,23 +573,27 @@ export const usePlayerStore = defineStore('player', { // Always check auth status first — if it changed since the cached // fetch (e.g., user logged in/out as a different account), the // cached playlists belong to a different user and we MUST refetch. - const [neAuthRes, qqAuthRes, kugouAuthRes] = await Promise.allSettled([ + const [neAuthRes, qqAuthRes, kugouAuthRes, spAuthRes] = await Promise.allSettled([ axios.get('/api/auth/status', { params: { platform: 'netease' } }), axios.get('/api/auth/status', { params: { platform: 'qq' } }), axios.get('/api/auth/status', { params: { platform: 'kugou' } }), + axios.get('/api/auth/status', { params: { platform: 'spotify' } }), ]); const newAuth = { netease: neAuthRes.status === 'fulfilled' && !!neAuthRes.value.data?.loggedIn, qq: qqAuthRes.status === 'fulfilled' && !!qqAuthRes.value.data?.loggedIn, kugou: kugouAuthRes.status === 'fulfilled' && !!kugouAuthRes.value.data?.loggedIn, + spotify: spAuthRes.status === 'fulfilled' && !!spAuthRes.value.data?.loggedIn, }; const authChanged = newAuth.netease !== this.authStatus.netease || newAuth.qq !== this.authStatus.qq || - newAuth.kugou !== this.authStatus.kugou; + newAuth.kugou !== this.authStatus.kugou || + newAuth.spotify !== this.authStatus.spotify; this.authStatus.netease = newAuth.netease; this.authStatus.qq = newAuth.qq; this.authStatus.kugou = newAuth.kugou; + this.authStatus.spotify = newAuth.spotify; // Favorites are user-local and cheap; always refresh them, even on a // home-data cache hit, so hearts stay correct across tabs/sessions. diff --git a/web/src/stores/sourceTabs.ts b/web/src/stores/sourceTabs.ts index f3c7472..adf19d7 100644 --- a/web/src/stores/sourceTabs.ts +++ b/web/src/stores/sourceTabs.ts @@ -28,7 +28,7 @@ function readAll(): Partial> { export function loadTabSource(key: TabKey, fallback: Source = 'netease'): Source { const all = readAll(); const v = all[key]; - return v === 'netease' || v === 'qq' || v === 'kugou' ? v : fallback; + return v === 'netease' || v === 'qq' || v === 'kugou' || v === 'spotify' ? v : fallback; } export function saveTabSource(key: TabKey, value: Source): void { diff --git a/web/src/styles/variables.scss b/web/src/styles/variables.scss index 1e2d32a..031868a 100644 --- a/web/src/styles/variables.scss +++ b/web/src/styles/variables.scss @@ -82,6 +82,8 @@ --brand-youtube-12: rgba(255, 0, 0, 0.12); --brand-kugou: #2ca2f9; --brand-kugou-12: rgba(44, 162, 249, 0.12); + --brand-spotify: #1DB954; + --brand-spotify-12: rgba(29, 185, 84, 0.12); } // Dark theme (default)