feat: add doubanlink in search page

This commit is contained in:
shinya
2025-08-23 01:55:19 +08:00
parent cb5df1919b
commit eadd93fde6
2 changed files with 35 additions and 9 deletions

View File

@@ -67,7 +67,23 @@ function SearchPageClient() {
return res;
})();
const source_names = Array.from(new Set(group.map((g) => g.source_name).filter(Boolean))) as string[];
return { episodes, source_names };
const douban_id = (() => {
const countMap = new Map<number, number>();
group.forEach((g) => {
if (g.douban_id && g.douban_id > 0) {
countMap.set(g.douban_id, (countMap.get(g.douban_id) || 0) + 1);
}
});
let max = 0;
let res: number | undefined;
countMap.forEach((v, k) => {
if (v > max) { max = v; res = k; }
});
return res;
})();
return { episodes, source_names, douban_id };
};
// 过滤器:非聚合与聚合
const [filterAll, setFilterAll] = useState<{ source: string; title: string; year: string; yearOrder: 'none' | 'asc' | 'desc' }>({
@@ -183,6 +199,9 @@ function SearchPageClient() {
if (prevNames !== nextNames) {
ref.current.setSourceNames(stats.source_names);
}
if (prev.douban_id !== stats.douban_id) {
ref.current.setDoubanId(stats.douban_id);
}
groupStatsRef.current.set(mapKey, stats);
}
});
@@ -748,12 +767,12 @@ function SearchPageClient() {
const title = group[0]?.title || '';
const poster = group[0]?.poster || '';
const year = group[0]?.year || 'unknown';
const { episodes, source_names } = computeGroupStats(group);
const { episodes, source_names, douban_id } = computeGroupStats(group);
const type = episodes === 1 ? 'movie' : 'tv';
// 如果该聚合第一次出现,写入初始统计
if (!groupStatsRef.current.has(mapKey)) {
groupStatsRef.current.set(mapKey, { episodes, source_names });
groupStatsRef.current.set(mapKey, { episodes, source_names, douban_id });
}
return (
@@ -767,6 +786,7 @@ function SearchPageClient() {
year={year}
episodes={episodes}
source_names={source_names}
douban_id={douban_id}
query={
searchQuery.trim() !== title
? searchQuery.trim()

View File

@@ -51,6 +51,7 @@ export interface VideoCardProps {
export type VideoCardHandle = {
setEpisodes: (episodes?: number) => void;
setSourceNames: (names?: string[]) => void;
setDoubanId: (id?: number) => void;
};
const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard(
@@ -89,6 +90,9 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
const [dynamicSourceNames, setDynamicSourceNames] = useState<string[] | undefined>(
source_names
);
const [dynamicDoubanId, setDynamicDoubanId] = useState<number | undefined>(
douban_id
);
useEffect(() => {
setDynamicEpisodes(episodes);
@@ -98,16 +102,21 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
setDynamicSourceNames(source_names);
}, [source_names]);
useEffect(() => {
setDynamicDoubanId(douban_id);
}, [douban_id]);
useImperativeHandle(ref, () => ({
setEpisodes: (eps?: number) => setDynamicEpisodes(eps),
setSourceNames: (names?: string[]) => setDynamicSourceNames(names),
setDoubanId: (id?: number) => setDynamicDoubanId(id),
}));
const actualTitle = title;
const actualPoster = poster;
const actualSource = source;
const actualId = id;
const actualDoubanId = douban_id;
const actualDoubanId = dynamicDoubanId;
const actualEpisodes = dynamicEpisodes;
const actualYear = year;
const actualQuery = query || '';
@@ -321,7 +330,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
showPlayButton: true,
showHeart: true, // 移动端菜单中需要显示收藏选项
showCheckCircle: false,
showDoubanLink: false,
showDoubanLink: true, // 移动端菜单中显示豆瓣链接
showRating: false,
showYear: true,
},
@@ -663,10 +672,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
{/* 年份徽章 */}
{config.showYear && actualYear && actualYear !== 'unknown' && actualYear.trim() !== '' && (
<div
className={`absolute top-2 bg-black/50 text-white text-xs font-medium px-2 py-1 rounded backdrop-blur-sm shadow-sm transition-all duration-300 ease-out group-hover:opacity-90 ${config.showDoubanLink && actualDoubanId && actualDoubanId !== 0
? 'left-2 group-hover:left-11'
: 'left-2'
}`}
className={`absolute top-2 bg-black/50 text-white text-xs font-medium px-2 py-1 rounded backdrop-blur-sm shadow-sm transition-all duration-300 ease-out group-hover:opacity-90 left-2`}
style={{
WebkitUserSelect: 'none',
userSelect: 'none',