修正年份筛选和卡片视图来源数量显示

This commit is contained in:
mtvpls
2026-03-19 13:06:32 +08:00
parent 986ab4b500
commit 88d453c0f9
2 changed files with 147 additions and 46 deletions
+144 -45
View File
@@ -460,82 +460,181 @@ function SearchPageClient() {
// 构建筛选选项 // 构建筛选选项
const filterOptions = useMemo(() => { const filterOptions = useMemo(() => {
const sourcesSet = new Map<string, string>(); const exactSearchFiltered = exactSearch
const titlesSet = new Set<string>(); ? searchResults.filter((item) =>
const yearsSet = new Set<string>(); titleContainsQuery(item.title, currentQueryRef.current)
)
: searchResults;
searchResults.forEach((item) => { const buildSourceOptions = (
if ( sourceEntries: Array<{ source: string; source_name: string }>
item.source && ) => [
item.source_name &&
item.source.trim() !== '' &&
item.source_name.trim() !== ''
) {
sourcesSet.set(item.source, item.source_name);
}
if (item.title && item.title.trim() !== '') titlesSet.add(item.title);
if (item.year && item.year.trim() !== '') yearsSet.add(item.year);
});
const sourceOptions: { label: string; value: string }[] = [
{ label: '全部来源', value: 'all' }, { label: '全部来源', value: 'all' },
...Array.from(sourcesSet.entries()) ...Array.from(
new Map(
sourceEntries
.filter(
(item) =>
item.source &&
item.source_name &&
item.source.trim() !== '' &&
item.source_name.trim() !== ''
)
.map((item) => [item.source, item.source_name])
).entries()
)
.sort((a, b) => { .sort((a, b) => {
// 判断是否为 openlist
const aIsOpenList = a[0] === 'openlist'; const aIsOpenList = a[0] === 'openlist';
const bIsOpenList = b[0] === 'openlist'; const bIsOpenList = b[0] === 'openlist';
// 判断是否为 emby 源(包括 emby 和 emby_xxx 格式)
const aIsEmby = a[0] === 'emby' || a[0].startsWith('emby_'); const aIsEmby = a[0] === 'emby' || a[0].startsWith('emby_');
const bIsEmby = b[0] === 'emby' || b[0].startsWith('emby_'); const bIsEmby = b[0] === 'emby' || b[0].startsWith('emby_');
// 优先级:OpenList(100) > Emby(90) > 其他(0)
const aPriority = aIsOpenList ? 100 : aIsEmby ? 90 : 0; const aPriority = aIsOpenList ? 100 : aIsEmby ? 90 : 0;
const bPriority = bIsOpenList ? 100 : bIsEmby ? 90 : 0; const bPriority = bIsOpenList ? 100 : bIsEmby ? 90 : 0;
if (aPriority !== bPriority) { if (aPriority !== bPriority) {
return bPriority - aPriority; // 降序排列 return bPriority - aPriority;
} }
// 同优先级内按名称排序
return a[1].localeCompare(b[1]); return a[1].localeCompare(b[1]);
}) })
.map(([value, label]) => ({ label, value })), .map(([value, label]) => ({ label, value })),
]; ];
const titleOptions: { label: string; value: string }[] = [ const buildTitleOptions = (titles: string[]) => [
{ label: '全部标题', value: 'all' }, { label: '全部标题', value: 'all' },
...Array.from(titlesSet.values()) ...Array.from(new Set(titles))
.filter((title) => title && title.trim() !== '')
.sort((a, b) => a.localeCompare(b)) .sort((a, b) => a.localeCompare(b))
.map((t) => ({ label: t, value: t })), .map((title) => ({ label: title, value: title })),
]; ];
// 年份: 将 unknown 放末尾 const buildYearOptions = (years: string[]) => {
const years = Array.from(yearsSet.values()); const yearSet = Array.from(
const knownYears = years new Set(years.filter((year) => year && year.trim() !== ''))
.filter((y) => y !== 'unknown') );
.sort((a, b) => parseInt(b) - parseInt(a)); const knownYears = yearSet
const hasUnknown = years.includes('unknown'); .filter((year) => year !== 'unknown')
const yearOptions: { label: string; value: string }[] = [ .sort((a, b) => parseInt(b) - parseInt(a));
{ label: '全部年份', value: 'all' }, const hasUnknown = yearSet.includes('unknown');
...knownYears.map((y) => ({ label: y, value: y })),
...(hasUnknown ? [{ label: '未知', value: 'unknown' }] : []), return [
]; { label: '全部年份', value: 'all' },
...knownYears.map((year) => ({ label: year, value: year })),
...(hasUnknown ? [{ label: '未知', value: 'unknown' }] : []),
];
};
const allForSourceOptions = exactSearchFiltered.filter((item) => {
if (filterAll.title !== 'all' && item.title !== filterAll.title)
return false;
if (filterAll.year !== 'all' && item.year !== filterAll.year)
return false;
return true;
});
const allForTitleOptions = exactSearchFiltered.filter((item) => {
if (filterAll.source !== 'all' && item.source !== filterAll.source)
return false;
if (filterAll.year !== 'all' && item.year !== filterAll.year)
return false;
return true;
});
const allForYearOptions = exactSearchFiltered.filter((item) => {
if (filterAll.source !== 'all' && item.source !== filterAll.source)
return false;
if (filterAll.title !== 'all' && item.title !== filterAll.title)
return false;
return true;
});
const aggForSourceOptions = aggregatedResults.filter(([_, group]) => {
const gTitle = group[0]?.title ?? '';
const gYear = group[0]?.year ?? 'unknown';
if (filterAgg.title !== 'all' && gTitle !== filterAgg.title) return false;
if (filterAgg.year !== 'all' && gYear !== filterAgg.year) return false;
return true;
});
const aggForTitleOptions = aggregatedResults.filter(([_, group]) => {
const gYear = group[0]?.year ?? 'unknown';
const hasSource =
filterAgg.source === 'all'
? true
: group.some((item) => item.source === filterAgg.source);
if (!hasSource) return false;
if (filterAgg.year !== 'all' && gYear !== filterAgg.year) return false;
return true;
});
const aggForYearOptions = aggregatedResults.filter(([_, group]) => {
const gTitle = group[0]?.title ?? '';
const hasSource =
filterAgg.source === 'all'
? true
: group.some((item) => item.source === filterAgg.source);
if (!hasSource) return false;
if (filterAgg.title !== 'all' && gTitle !== filterAgg.title) return false;
return true;
});
const categoriesAll: SearchFilterCategory[] = [ const categoriesAll: SearchFilterCategory[] = [
{ key: 'source', label: '来源', options: sourceOptions }, {
{ key: 'title', label: '标题', options: titleOptions }, key: 'source',
{ key: 'year', label: '年份', options: yearOptions }, label: '来源',
options: buildSourceOptions(
allForSourceOptions.map((item) => ({
source: item.source,
source_name: item.source_name,
}))
),
},
{
key: 'title',
label: '标题',
options: buildTitleOptions(
allForTitleOptions.map((item) => item.title)
),
},
{
key: 'year',
label: '年份',
options: buildYearOptions(allForYearOptions.map((item) => item.year)),
},
]; ];
const categoriesAgg: SearchFilterCategory[] = [ const categoriesAgg: SearchFilterCategory[] = [
{ key: 'source', label: '来源', options: sourceOptions }, {
{ key: 'title', label: '标题', options: titleOptions }, key: 'source',
{ key: 'year', label: '年份', options: yearOptions }, label: '来源',
options: buildSourceOptions(
aggForSourceOptions.flatMap(([_, group]) =>
group.map((item) => ({
source: item.source,
source_name: item.source_name,
}))
)
),
},
{
key: 'title',
label: '标题',
options: buildTitleOptions(
aggForTitleOptions.map(([_, group]) => group[0]?.title ?? '')
),
},
{
key: 'year',
label: '年份',
options: buildYearOptions(
aggForYearOptions.map(([_, group]) => group[0]?.year ?? 'unknown')
),
},
]; ];
return { categoriesAll, categoriesAgg }; return { categoriesAll, categoriesAgg };
}, [searchResults]); }, [searchResults, aggregatedResults, exactSearch, filterAll, filterAgg]);
// 非聚合:应用筛选与排序 // 非聚合:应用筛选与排序
const filteredAllResults = useMemo(() => { const filteredAllResults = useMemo(() => {
+3 -1
View File
@@ -1077,7 +1077,9 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
return ( return (
<div <div
className='absolute bottom-2 right-2 opacity-0 transition-all duration-300 ease-in-out delay-75 sm:group-hover:opacity-100' className={`absolute bottom-1 right-1 sm:bottom-2 sm:right-2 transition-all duration-300 ease-in-out delay-75 ${
from === 'search' ? 'opacity-100' : 'opacity-0 sm:group-hover:opacity-100'
}`}
style={{ style={{
WebkitUserSelect: 'none', WebkitUserSelect: 'none',
userSelect: 'none', userSelect: 'none',