/* eslint-disable react-hooks/exhaustive-deps, @typescript-eslint/no-explicit-any */ 'use client'; import { ChevronUp, Search, X } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; import { Suspense, useEffect, useMemo, useState } from 'react'; import { addSearchHistory, clearSearchHistory, deleteSearchHistory, getSearchHistory, subscribeToDataUpdates, } from '@/lib/db.client'; import { SearchResult } from '@/lib/types'; import { yellowWords } from '@/lib/yellow'; import PageLayout from '@/components/PageLayout'; import SearchSuggestions from '@/components/SearchSuggestions'; import VideoCard from '@/components/VideoCard'; function SearchPageClient() { // 搜索历史 const [searchHistory, setSearchHistory] = useState([]); // 返回顶部按钮显示状态 const [showBackToTop, setShowBackToTop] = useState(false); const router = useRouter(); const searchParams = useSearchParams(); const [searchQuery, setSearchQuery] = useState(''); const [isLoading, setIsLoading] = useState(false); const [showResults, setShowResults] = useState(false); const [searchResults, setSearchResults] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); // 获取默认聚合设置:只读取用户本地设置,默认为 true const getDefaultAggregate = () => { if (typeof window !== 'undefined') { const userSetting = localStorage.getItem('defaultAggregateSearch'); if (userSetting !== null) { return JSON.parse(userSetting); } } return true; // 默认启用聚合 }; const [viewMode, setViewMode] = useState<'agg' | 'all'>(() => { return getDefaultAggregate() ? 'agg' : 'all'; }); // 聚合后的结果(按标题和年份分组) const aggregatedResults = useMemo(() => { const map = new Map(); searchResults.forEach((item) => { // 使用 title + year + type 作为键,year 必然存在,但依然兜底 'unknown' const key = `${item.title.replaceAll(' ', '')}-${ item.year || 'unknown' }-${item.episodes.length === 1 ? 'movie' : 'tv'}`; const arr = map.get(key) || []; arr.push(item); map.set(key, arr); }); return Array.from(map.entries()).sort((a, b) => { // 优先排序:标题与搜索词完全一致的排在前面 const aExactMatch = a[1][0].title .replaceAll(' ', '') .includes(searchQuery.trim().replaceAll(' ', '')); const bExactMatch = b[1][0].title .replaceAll(' ', '') .includes(searchQuery.trim().replaceAll(' ', '')); if (aExactMatch && !bExactMatch) return -1; if (!aExactMatch && bExactMatch) return 1; // 年份排序 if (a[1][0].year === b[1][0].year) { return a[0].localeCompare(b[0]); } else { // 处理 unknown 的情况 const aYear = a[1][0].year; const bYear = b[1][0].year; if (aYear === 'unknown' && bYear === 'unknown') { return 0; } else if (aYear === 'unknown') { return 1; // a 排在后面 } else if (bYear === 'unknown') { return -1; // b 排在后面 } else { // 都是数字年份,按数字大小排序(大的在前面) return aYear > bYear ? -1 : 1; } } }); }, [searchResults]); useEffect(() => { // 无搜索参数时聚焦搜索框 !searchParams.get('q') && document.getElementById('searchInput')?.focus(); // 初始加载搜索历史 getSearchHistory().then(setSearchHistory); // 监听搜索历史更新事件 const unsubscribe = subscribeToDataUpdates( 'searchHistoryUpdated', (newHistory: string[]) => { setSearchHistory(newHistory); } ); // 获取滚动位置的函数 - 专门针对 body 滚动 const getScrollTop = () => { return document.body.scrollTop || 0; }; // 使用 requestAnimationFrame 持续检测滚动位置 let isRunning = false; const checkScrollPosition = () => { if (!isRunning) return; const scrollTop = getScrollTop(); const shouldShow = scrollTop > 300; setShowBackToTop(shouldShow); requestAnimationFrame(checkScrollPosition); }; // 启动持续检测 isRunning = true; checkScrollPosition(); // 监听 body 元素的滚动事件 const handleScroll = () => { const scrollTop = getScrollTop(); setShowBackToTop(scrollTop > 300); }; document.body.addEventListener('scroll', handleScroll, { passive: true }); return () => { unsubscribe(); isRunning = false; // 停止 requestAnimationFrame 循环 // 移除 body 滚动事件监听器 document.body.removeEventListener('scroll', handleScroll); }; }, []); useEffect(() => { // 当搜索参数变化时更新搜索状态 const query = searchParams.get('q'); if (query) { setSearchQuery(query); fetchSearchResults(query); setShowSuggestions(false); // 保存到搜索历史 (事件监听会自动更新界面) addSearchHistory(query); } else { setShowResults(false); setShowSuggestions(false); } }, [searchParams]); const fetchSearchResults = async (query: string) => { try { setIsLoading(true); const response = await fetch( `/api/search?q=${encodeURIComponent(query.trim())}` ); const data = await response.json(); let results = data.results; if ( typeof window !== 'undefined' && !(window as any).RUNTIME_CONFIG?.DISABLE_YELLOW_FILTER ) { results = results.filter((result: SearchResult) => { const typeName = result.type_name || ''; return !yellowWords.some((word: string) => typeName.includes(word)); }); } setSearchResults( results.sort((a: SearchResult, b: SearchResult) => { // 优先排序:标题与搜索词完全一致的排在前面 const aExactMatch = a.title === query.trim(); const bExactMatch = b.title === query.trim(); if (aExactMatch && !bExactMatch) return -1; if (!aExactMatch && bExactMatch) return 1; // 如果都匹配或都不匹配,则按原来的逻辑排序 if (a.year === b.year) { return a.title.localeCompare(b.title); } else { // 处理 unknown 的情况 if (a.year === 'unknown' && b.year === 'unknown') { return 0; } else if (a.year === 'unknown') { return 1; // a 排在后面 } else if (b.year === 'unknown') { return -1; // b 排在后面 } else { // 都是数字年份,按数字大小排序(大的在前面) return parseInt(a.year) > parseInt(b.year) ? -1 : 1; } } }) ); setShowResults(true); } catch (error) { setSearchResults([]); } finally { setIsLoading(false); } }; // 输入框内容变化时触发,显示搜索建议 const handleInputChange = (e: React.ChangeEvent) => { const value = e.target.value; setSearchQuery(value); if (value.trim()) { setShowSuggestions(true); } else { setShowSuggestions(false); } }; // 搜索框聚焦时触发,显示搜索建议 const handleInputFocus = () => { if (searchQuery.trim()) { setShowSuggestions(true); } }; // 搜索表单提交时触发,处理搜索逻辑 const handleSearch = (e: React.FormEvent) => { e.preventDefault(); const trimmed = searchQuery.trim().replace(/\s+/g, ' '); if (!trimmed) return; // 回显搜索框 setSearchQuery(trimmed); setIsLoading(true); setShowResults(true); setShowSuggestions(false); router.push(`/search?q=${encodeURIComponent(trimmed)}`); // 直接发请求 fetchSearchResults(trimmed); // 保存到搜索历史 (事件监听会自动更新界面) addSearchHistory(trimmed); }; const handleSuggestionSelect = (suggestion: string) => { setSearchQuery(suggestion); setShowSuggestions(false); // 自动执行搜索 setIsLoading(true); setShowResults(true); router.push(`/search?q=${encodeURIComponent(suggestion)}`); fetchSearchResults(suggestion); addSearchHistory(suggestion); }; // 返回顶部功能 const scrollToTop = () => { try { // 根据调试结果,真正的滚动容器是 document.body document.body.scrollTo({ top: 0, behavior: 'smooth', }); } catch (error) { // 如果平滑滚动完全失败,使用立即滚动 document.body.scrollTop = 0; } }; return (
{/* 搜索框 */}
{/* 搜索建议 */} setShowSuggestions(false)} />
{/* 搜索结果或搜索历史 */}
{isLoading ? (
) : showResults ? (
{/* 标题 + 聚合开关 */}

搜索结果

{/* 聚合开关 */}
{viewMode === 'agg' ? aggregatedResults.map(([mapKey, group]) => { return (
); }) : searchResults.map((item) => (
1 ? 'tv' : 'movie'} />
))} {searchResults.length === 0 && (
未找到相关结果
)}
) : searchHistory.length > 0 ? ( // 搜索历史

搜索历史 {searchHistory.length > 0 && ( )}

{searchHistory.map((item) => (
{/* 删除按钮 */}
))}
) : null}
{/* 返回顶部悬浮按钮 */}
); } export default function SearchPage() { return ( ); }