diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index c63742a..e8166fa 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -1,7 +1,7 @@ /* eslint-disable react-hooks/exhaustive-deps, @typescript-eslint/no-explicit-any,@typescript-eslint/no-non-null-assertion,no-empty */ 'use client'; -import { ChevronUp, Search, X } from 'lucide-react'; +import { ChevronUp, RefreshCw, Search, X } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; import React, { startTransition, Suspense, useEffect, useMemo, useRef, useState } from 'react'; @@ -42,6 +42,52 @@ function SearchPageClient() { // 聚合卡片 refs 与聚合统计缓存 const groupRefs = useRef>>(new Map()); const groupStatsRef = useRef>(new Map()); + // 强制刷新状态 + const [forceRefresh, setForceRefresh] = useState(false); + // 是否使用了缓存结果 + const [isFromCache, setIsFromCache] = useState(false); + + // 生成缓存键 + const getCacheKey = (query: string) => { + return `search_cache_${query.trim()}`; + }; + + // 从 sessionStorage 获取缓存的搜索结果 + const getCachedResults = (query: string): SearchResult[] | null => { + if (typeof window === 'undefined') return null; + try { + const cacheKey = getCacheKey(query); + const cached = sessionStorage.getItem(cacheKey); + if (cached) { + return JSON.parse(cached); + } + } catch (error) { + console.error('Failed to get cached results:', error); + } + return null; + }; + + // 保存搜索结果到 sessionStorage + const setCachedResults = (query: string, results: SearchResult[]) => { + if (typeof window === 'undefined') return; + try { + const cacheKey = getCacheKey(query); + sessionStorage.setItem(cacheKey, JSON.stringify(results)); + } catch (error) { + console.error('Failed to cache results:', error); + } + }; + + // 清除指定查询的缓存 + const clearCachedResults = (query: string) => { + if (typeof window === 'undefined') return; + try { + const cacheKey = getCacheKey(query); + sessionStorage.removeItem(cacheKey); + } catch (error) { + console.error('Failed to clear cached results:', error); + } + }; const getGroupRef = (key: string) => { let ref = groupRefs.current.get(key); @@ -407,6 +453,36 @@ function SearchPageClient() { if (query) { setSearchQuery(query); + + const trimmed = query.trim(); + + // 检查是否有缓存且不是强制刷新 + if (!forceRefresh) { + const cachedResults = getCachedResults(trimmed); + if (cachedResults && cachedResults.length > 0) { + // 使用缓存的结果 + setIsLoading(false); // 先设置加载状态为 false + setSearchResults(cachedResults); + setShowResults(true); + setTotalSources(1); + setCompletedSources(1); + setShowSuggestions(false); + setIsFromCache(true); // 标记为缓存结果 + // 保存到搜索历史 + addSearchHistory(query); + return; + } + } + + // 如果是强制刷新,清除缓存 + if (forceRefresh) { + clearCachedResults(trimmed); + setForceRefresh(false); + } + + // 开始新搜索时,重置缓存标记 + setIsFromCache(false); + // 新搜索:关闭旧连接并清空结果 if (eventSourceRef.current) { try { eventSourceRef.current.close(); } catch { } @@ -424,8 +500,6 @@ function SearchPageClient() { setIsLoading(true); setShowResults(true); - const trimmed = query.trim(); - // 每次搜索时重新读取设置,确保使用最新的配置 let currentFluidSearch = useFluidSearch; if (typeof window !== 'undefined') { @@ -495,7 +569,18 @@ function SearchPageClient() { flushTimerRef.current = null; } startTransition(() => { - setSearchResults((prev) => prev.concat(toAppend)); + setSearchResults((prev) => { + const newResults = prev.concat(toAppend); + // 缓存完整的搜索结果 + setCachedResults(trimmed, newResults); + return newResults; + }); + }); + } else { + // 即使没有待写入的缓冲,也缓存当前结果 + setSearchResults((prev) => { + setCachedResults(trimmed, prev); + return prev; }); } setIsLoading(false); @@ -542,6 +627,8 @@ function SearchPageClient() { : (data.results as SearchResult[]); setSearchResults(results); + // 缓存搜索结果 + setCachedResults(trimmed, results); setTotalSources(1); setCompletedSources(1); } @@ -559,7 +646,7 @@ function SearchPageClient() { setShowResults(false); setShowSuggestions(false); } - }, [searchParams]); + }, [searchParams, forceRefresh]); // 组件卸载时,关闭可能存在的连接 useEffect(() => { @@ -603,7 +690,6 @@ function SearchPageClient() { // 回显搜索框 setSearchQuery(trimmed); - setIsLoading(true); setShowResults(true); setShowSuggestions(false); @@ -616,7 +702,6 @@ function SearchPageClient() { setShowSuggestions(false); // 自动执行搜索 - setIsLoading(true); setShowResults(true); router.push(`/search?q=${encodeURIComponent(suggestion)}`); @@ -685,7 +770,6 @@ function SearchPageClient() { // 回显搜索框 setSearchQuery(trimmed); - setIsLoading(true); setShowResults(true); setShowSuggestions(false); @@ -701,20 +785,42 @@ function SearchPageClient() { {showResults ? (
{/* 标题 */} -
+

搜索结果 - {totalSources > 0 && useFluidSearch && ( - - {completedSources}/{totalSources} - - )} - {isLoading && useFluidSearch && ( - - + {isFromCache ? ( + + 缓存 + ) : ( + <> + {totalSources > 0 && useFluidSearch && ( + + {completedSources}/{totalSources} + + )} + {isLoading && useFluidSearch && ( + + + + )} + )}

+ {/* 强制刷新按钮 */} + {searchQuery && ( + + )}
{/* 筛选器 + 聚合开关 同行 */}