diff --git a/src/app/music/MusicClient.tsx b/src/app/music/MusicClient.tsx
index 00243b1..189a9b3 100644
--- a/src/app/music/MusicClient.tsx
+++ b/src/app/music/MusicClient.tsx
@@ -1884,20 +1884,14 @@ export default function MusicClient({ children: _children }: { children?: React.
}
if (pathname === '/music/search') {
- const source = normalizeSource(searchParams.get('source') || currentSource);
const keyword = searchParams.get('q') || '';
- setCurrentSource(source);
setCurrentView('search');
setCurrentPlaylistTitle(keyword ? `搜索: ${keyword}` : '搜索');
setSearchKeyword(keyword);
- if (keyword) {
- void searchSongs(keyword, false);
- } else {
- setSongs([]);
- setActiveSearchKeyword('');
- setSearchPage(1);
- setSearchHasMore(false);
- }
+ setSongs([]);
+ setActiveSearchKeyword('');
+ setSearchPage(1);
+ setSearchHasMore(false);
return;
}
@@ -2627,48 +2621,6 @@ export default function MusicClient({ children: _children }: { children?: React.
音乐
-
-
-
-
- {musicSources.map((source) => (
-
- ))}
-
@@ -3530,61 +3482,6 @@ export default function MusicClient({ children: _children }: { children?: React.
)}
- {showSourceMenu && (
-
-
- )}
-
{/* Add to Playlist Modal */}
-
- {musicSources.map((source) => (
- router.push(`/music/rankings?source=${source.key}`)}
- className={`rounded-lg border px-3 py-1.5 text-xs font-bold ${currentSource === source.key ? 'border-green-500 bg-green-500 text-white' : 'border-white/10 bg-white/5 text-zinc-400'}`}
- >
- {source.label}
-
- ))}
-
-
-
排行榜
+
+
排行榜
+
+ {musicSources.map((source) => {
+ const active = currentSource === source.key;
+ return (
+ router.push(`/music/rankings?source=${source.key}`)}
+ className={`shrink-0 rounded-full px-3 py-1.5 text-xs font-bold transition-all ${
+ active
+ ? 'bg-gradient-to-r from-green-500 to-emerald-400 text-white shadow-lg shadow-green-500/25'
+ : 'text-zinc-400 hover:bg-white/10 hover:text-white'
+ }`}
+ >
+ {source.label}
+
+ );
+ })}
+
{loading ?
: playlists.length > 0 ? (
diff --git a/src/app/music/search/page.tsx b/src/app/music/search/page.tsx
index f041d47..b47737f 100644
--- a/src/app/music/search/page.tsx
+++ b/src/app/music/search/page.tsx
@@ -5,7 +5,7 @@ import { useRouter, useSearchParams } from 'next/navigation';
import { playMusicList } from '@/lib/music/actions';
import { MusicLoadingIndicator, type Song } from '../MusicClient';
import SongList from '../SongList';
-import { mapSong, normalizeSource } from '@/lib/music/shared';
+import { mapSong, musicSources, normalizeSource } from '@/lib/music/shared';
export default function MusicSearchPage() {
const router = useRouter();
@@ -13,21 +13,28 @@ export default function MusicSearchPage() {
const source = normalizeSource(searchParams.get('source'));
const q = searchParams.get('q') || '';
const [keyword, setKeyword] = useState(q);
+ const [selectedSource, setSelectedSource] = useState(source);
const [songs, setSongs] = useState
([]);
const [loading, setLoading] = useState(false);
+ const [showSourceMenu, setShowSourceMenu] = useState(false);
useEffect(() => {
+ setSelectedSource(source);
setKeyword(q);
if (!q) {
setSongs([]);
return;
}
+ const controller = new AbortController();
setLoading(true);
- fetch(`/api/music/v2/search?source=${source}&q=${encodeURIComponent(q)}&page=1&limit=20`)
+ fetch(`/api/music/v2/search?source=${source}&q=${encodeURIComponent(q)}&page=1&limit=20`, { signal: controller.signal })
.then((res) => res.json())
.then((data) => setSongs((data.data?.list || []).map(mapSong)))
- .catch(() => setSongs([]))
+ .catch((error) => {
+ if (error?.name !== 'AbortError') setSongs([]);
+ })
.finally(() => setLoading(false));
+ return () => controller.abort();
}, [source, q]);
const submit = () => {
@@ -35,16 +42,81 @@ export default function MusicSearchPage() {
if (next) router.push(`/music/search?source=${source}&q=${encodeURIComponent(next)}`);
};
+ const changeSource = (nextSource: string) => {
+ const normalizedSource = normalizeSource(nextSource);
+ const next = keyword.trim() || q;
+ setSelectedSource(normalizedSource);
+ setShowSourceMenu(false);
+ router.push(`/music/search?source=${normalizedSource}${next ? `&q=${encodeURIComponent(next)}` : ''}`);
+ };
+
+ const currentSourceLabel = musicSources.find((item) => item.key === selectedSource)?.label || '音源';
+
return (
-
+
-
setKeyword(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && submit()} className="h-full w-full border-0 bg-transparent pl-10 pr-4 text-sm text-white outline-none placeholder:text-zinc-500" placeholder="搜索歌曲或艺术家..." />
+
setKeyword(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && submit()} className="h-full w-full border-0 bg-transparent pl-10 pr-28 text-sm text-white outline-none placeholder:text-zinc-500" placeholder="搜索歌曲或艺术家..." />
+
+
+
setShowSourceMenu((open) => !open)}
+ className="flex h-8 min-w-[84px] items-center justify-between gap-1 rounded-lg border border-white/10 bg-zinc-900/90 pl-3 pr-2 text-xs font-medium text-white shadow-inner shadow-white/5 transition-colors hover:bg-zinc-800"
+ aria-haspopup="listbox"
+ aria-expanded={showSourceMenu}
+ aria-label="选择音源"
+ >
+ {currentSourceLabel}
+
+
+ {showSourceMenu && (
+ <>
+
setShowSourceMenu(false)}
+ aria-label="关闭音源菜单"
+ />
+
+ {musicSources.map((item) => {
+ const active = item.key === selectedSource;
+ return (
+
changeSource(item.key)}
+ className={`flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-xs transition-colors ${
+ active ? 'bg-green-500/20 text-green-300' : 'text-zinc-300 hover:bg-white/10 hover:text-white'
+ }`}
+ role="option"
+ aria-selected={active}
+ >
+ {item.label}
+ {active && (
+
+ )}
+
+ );
+ })}
+
+ >
+ )}
+
+
-
搜索
+
+
+