Fix search card virtualization and result count

This commit is contained in:
ShiGuangAlex
2026-04-11 17:06:33 +08:00
parent 7ba91a0293
commit e92e49f04f
2 changed files with 273 additions and 103 deletions
+74 -39
View File
@@ -355,19 +355,21 @@ function SearchPageClient() {
return normalizedTitle.includes(normalizedQuery);
};
const allExactSearchResults = useMemo(() => {
if (!exactSearch) return searchResults;
return searchResults.filter((item) =>
titleContainsQuery(item.title, currentQueryRef.current)
);
}, [searchResults, searchQuery, exactSearch]);
// 聚合后的结果(按标题和年份分组)
const aggregatedResults = useMemo(() => {
// 首先应用精确搜索过滤
const filteredResults = exactSearch
? searchResults.filter((item) =>
titleContainsQuery(item.title, currentQueryRef.current)
)
: searchResults;
//===== 阶段1:按 normalizedTitle-type 初步分组 =====
const preliminaryMap = new Map<string, SearchResult[]>();
filteredResults.forEach((item) => {
allExactSearchResults.forEach((item) => {
const normalizedTitle = normalizeTitle(item.title);
const type = getType(item);
const preliminaryKey = `${normalizedTitle}-${type}`;
@@ -428,7 +430,7 @@ function SearchPageClient() {
return keyOrder.map(
(key) => [key, finalMap.get(key)!] as [string, SearchResult[]]
);
}, [searchResults, exactSearch]);
}, [allExactSearchResults]);
// 当聚合结果变化时,如果某个聚合已存在,则调用其卡片 ref 的 set 方法增量更新
useEffect(() => {
@@ -641,14 +643,7 @@ function SearchPageClient() {
const filteredAllResults = useMemo(() => {
const { source, title, year, yearOrder } = filterAll;
// 首先应用精确搜索过滤
const exactSearchFiltered = exactSearch
? searchResults.filter((item) =>
titleContainsQuery(item.title, currentQueryRef.current)
)
: searchResults;
const filtered = exactSearchFiltered.filter((item) => {
const filtered = allExactSearchResults.filter((item) => {
if (source !== 'all' && item.source !== source) return false;
if (title !== 'all' && item.title !== title) return false;
if (year !== 'all' && item.year !== year) return false;
@@ -677,7 +672,7 @@ function SearchPageClient() {
? a.title.localeCompare(b.title)
: b.title.localeCompare(a.title);
});
}, [searchResults, filterAll, searchQuery, exactSearch]);
}, [allExactSearchResults, filterAll, searchQuery]);
// 聚合:应用筛选与排序
const filteredAggResults = useMemo(() => {
@@ -734,6 +729,30 @@ function SearchPageClient() {
filteredAllResults.length,
]);
const resultCountMeta = useMemo(() => {
const isAggregateView = viewMode === 'agg';
const visibleCount = isAggregateView
? filteredAggResults.length
: filteredAllResults.length;
const totalCount = isAggregateView
? aggregatedResults.length
: allExactSearchResults.length;
return {
visibleCount,
totalCount,
isFiltered: visibleCount !== totalCount,
modeLabel: isAggregateView ? '聚合结果' : '搜索结果',
unit: isAggregateView ? '组' : '条',
};
}, [
viewMode,
filteredAggResults.length,
filteredAllResults.length,
aggregatedResults.length,
allExactSearchResults.length,
]);
useEffect(() => {
if (typeof window !== 'undefined') {
localStorage.setItem('searchResultDisplayMode', resultDisplayMode);
@@ -1570,28 +1589,44 @@ function SearchPageClient() {
<>
{/* 影视搜索结果 */}
{/* 标题 */}
<div className='mb-4 flex items-center justify-between'>
<h2 className='text-xl font-bold text-gray-800 dark:text-gray-200'>
{isFromCache ? (
<span className='ml-2 rounded-md bg-green-50 px-2 py-0.5 text-xs font-medium text-green-600 dark:bg-green-900/30 dark:text-green-400'>
<div className='mb-4 flex items-start justify-between gap-4'>
<div className='min-w-0'>
<h2 className='text-xl font-bold text-gray-800 dark:text-gray-200'>
{isFromCache ? (
<span className='ml-2 rounded-md bg-green-50 px-2 py-0.5 text-xs font-medium text-green-600 dark:bg-green-900/30 dark:text-green-400'>
</span>
) : (
<>
{totalSources > 0 && useFluidSearch && (
<span className='ml-2 text-sm font-normal text-gray-500 dark:text-gray-400'>
{completedSources}/{totalSources}
</span>
)}
{isLoading && useFluidSearch && (
<span className='ml-2 inline-block align-middle'>
<span className='inline-block h-3 w-3 animate-spin rounded-full border-2 border-gray-300 border-t-green-500'></span>
</span>
)}
</>
)}
</h2>
<div className='mt-2 flex flex-wrap items-center gap-2 text-xs'>
<span className='inline-flex items-center rounded-full bg-gray-100 px-2.5 py-1 font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200'>
{resultCountMeta.modeLabel}{' '}
{resultCountMeta.visibleCount.toLocaleString()}{' '}
{resultCountMeta.unit}
</span>
) : (
<>
{totalSources > 0 && useFluidSearch && (
<span className='ml-2 text-sm font-normal text-gray-500 dark:text-gray-400'>
{completedSources}/{totalSources}
</span>
)}
{isLoading && useFluidSearch && (
<span className='ml-2 inline-block align-middle'>
<span className='inline-block h-3 w-3 animate-spin rounded-full border-2 border-gray-300 border-t-green-500'></span>
</span>
)}
</>
)}
</h2>
{resultCountMeta.isFiltered && (
<span className='inline-flex items-center rounded-full bg-white/80 px-2.5 py-1 font-medium text-gray-500 ring-1 ring-gray-200 dark:bg-gray-900/70 dark:text-gray-400 dark:ring-gray-700'>
{' '}
{resultCountMeta.totalCount.toLocaleString()}{' '}
{resultCountMeta.unit}
</span>
)}
</div>
</div>
{searchQuery && (
<button
onClick={() => {