feat: add doubanlink in search page

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

View File

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

View File

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