import { useCallback, useEffect, useState, type FormEvent } from 'react' import { discoverMovies, searchMovies } from '../api/movies' import { CatalogDetail } from '../components/CatalogDetail' import { CatalogTile } from '../components/CatalogTile' import { SiteFooter } from '../components/SiteFooter' import { SiteNav } from '../components/SiteNav' import { loadSeen, type SeenEntry } from '../lib/seen' import type { CandidateMovie } from '../types' import { GENRE_OPTIONS } from '../types' type Mode = 'search' | 'discover' const SORT_OPTIONS = [ { value: 'popularity.desc', label: '热度' }, { value: 'vote_average.desc', label: '评分' }, { value: 'primary_release_date.desc', label: '新片优先' }, ] as const const LANG_OPTIONS = [ { value: '', label: '语言不限' }, { value: 'zh', label: '华语' }, { value: 'en', label: '英语' }, { value: 'ja', label: '日语' }, { value: 'ko', label: '韩语' }, ] as const export function BrowsePage() { const [mode, setMode] = useState('search') const [query, setQuery] = useState('') const [year, setYear] = useState('') const [genres, setGenres] = useState([]) const [lang, setLang] = useState('') const [sortBy, setSortBy] = useState('popularity.desc') const [yearGte, setYearGte] = useState('') const [maxRuntime, setMaxRuntime] = useState('') const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [results, setResults] = useState(null) const [selected, setSelected] = useState(null) const [seenList, setSeenList] = useState(() => loadSeen()) const runDiscover = useCallback(async () => { setLoading(true) setError(null) try { const res = await discoverMovies({ with_genres: genres.length ? genres.join(',') : undefined, year_gte: yearGte ? Number(yearGte) : undefined, max_runtime: maxRuntime ? Number(maxRuntime) : undefined, with_original_language: lang || undefined, sort_by: sortBy, page: 1, }) setResults(res.data) setSelected(null) } catch (err) { setResults(null) setError(err instanceof Error ? err.message : '发现失败') } finally { setLoading(false) } }, [genres, yearGte, maxRuntime, lang, sortBy]) useEffect(() => { if (mode !== 'discover') return void runDiscover() }, [mode, runDiscover]) async function onSearchSubmit(e: FormEvent) { e.preventDefault() const q = query.trim() if (!q) return setLoading(true) setError(null) try { const res = await searchMovies(q, year ? Number(year) : undefined) setResults(res.data) setSelected(null) } catch (err) { setResults(null) setError(err instanceof Error ? err.message : '搜索失败') } finally { setLoading(false) } } function toggleGenre(g: string) { setGenres((prev) => prev.includes(g) ? prev.filter((x) => x !== g) : [...prev, g].slice(0, 3), ) } const seenIds = new Set(seenList.map((e) => e.id)) return (

片库

找一部确认今晚

搜片名,或按类型与年份筛选。点海报看详情,可标记已看。

{mode === 'search' ? (
void onSearchSubmit(e)}> setQuery(e.target.value)} placeholder="片名、关键词,例如:盗梦空间" autoComplete="off" aria-label="搜索关键词" /> setYear(e.target.value)} placeholder="年份" min={1900} max={2100} aria-label="上映年份" />
) : (
{GENRE_OPTIONS.map((g) => ( ))}
)} {error && (

{error}

)}
{loading && !results &&

正在连接片库…

} {results && results.length === 0 && (

没有符合条件的影片,换个关键词或放宽筛选试试。

)} {results && results.map((m, i) => ( ))} {!loading && results == null && mode === 'search' && (

输入片名确认年份与海报,或切换到「条件发现」按口味逛一圈。

)}
{selected && ( setSelected(null)} onSeenChange={setSeenList} /> )}
{seenList.length > 0 && (

已标记 {seenList.length} 部已看;返回智能推荐时会自动带入排除列表。

)}
) }