修复即将上映需多次点击才显示,即将上映增加预告片

This commit is contained in:
mtvpls
2026-06-02 10:34:13 +08:00
parent 135466b260
commit 16e48bef8b
6 changed files with 454 additions and 7 deletions
+81 -6
View File
@@ -10,6 +10,7 @@ import {
Radio,
Sparkles,
Trash2,
Youtube,
} from 'lucide-react';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
@@ -50,6 +51,8 @@ import DetailPanel from '@/components/DetailPanel';
import { ImagePlaceholder } from '@/components/ImagePlaceholder';
import ImageViewer from '@/components/ImageViewer';
import MobileActionSheet from '@/components/MobileActionSheet';
import TrailerPickerDialog from '@/components/TrailerPickerDialog';
import type { TMDBVideoItem } from '@/lib/tmdb.client';
export interface VideoCardProps {
id?: string;
@@ -170,6 +173,10 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
const [showDetailPanel, setShowDetailPanel] = useState(false);
const [showImageViewer, setShowImageViewer] = useState(false);
const [showUpcomingInfo, setShowUpcomingInfo] = useState(false); // 控制即将上映倒计时的显示
const [showTrailerPicker, setShowTrailerPicker] = useState(false);
const [trailerLoading, setTrailerLoading] = useState(false);
const [trailerError, setTrailerError] = useState<string | null>(null);
const [trailerVideos, setTrailerVideos] = useState<TMDBVideoItem[]>([]);
const [displayPoster, setDisplayPoster] = useState(processedPoster);
// 检查AI功能是否启用
@@ -664,6 +671,43 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
return configs[from] || configs.search;
}, [from, isAggregate, douban_id, rate, isUpcoming]);
const upcomingReleaseText = useMemo(() => {
if (!isUpcoming || daysUntilRelease === null) return undefined;
if (daysUntilRelease > 0) return `${daysUntilRelease}天后上映`;
if (daysUntilRelease === 0) return '今日上映';
return '已上映';
}, [isUpcoming, daysUntilRelease]);
const openTrailerPicker = useCallback(async () => {
if (!actualTitle) return;
setShowMobileActions(false);
setTrailerError(null);
setTrailerLoading(true);
setShowTrailerPicker(true);
try {
const params = new URLSearchParams();
if (tmdb_id) params.set('id', String(tmdb_id));
if (actualSearchType) params.set('type', actualSearchType);
if (actualTitle) params.set('title', actualTitle);
if (actualYear) params.set('year', actualYear);
const res = await fetch(`/api/tmdb/videos?${params.toString()}`);
const data = await res.json();
if (!res.ok) {
throw new Error(data?.error || '获取预告片失败');
}
setTrailerVideos(data.videos || []);
} catch (err) {
setTrailerVideos([]);
setTrailerError(err instanceof Error ? err.message : '获取预告片失败');
} finally {
setTrailerLoading(false);
}
}, [actualSearchType, actualTitle, actualYear, tmdb_id]);
// 移动端操作菜单配置
const mobileActions = useMemo(() => {
const actions = [];
@@ -796,6 +840,17 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
});
}
// 预告片操作:仅即将上映卡片显示
if (origin !== 'live' && isUpcoming && actualTitle) {
actions.push({
id: 'trailer',
label: '预告片',
icon: <Youtube size={20} />,
onClick: openTrailerPicker,
color: 'default' as const,
});
}
// 详情页面按钮(直播源不显示详情)
if (origin !== 'live') {
actions.push({
@@ -848,6 +903,11 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
handlePlayInNewTab,
aiEnabled,
actualTitle,
actualSearchType,
isUpcoming,
origin,
tmdb_id,
openTrailerPicker,
]);
return (
@@ -1039,7 +1099,6 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
{/* 播放按钮或上映倒计时 */}
{isUpcoming && daysUntilRelease !== null ? (
<div
data-button='true'
className={`absolute inset-0 flex items-center justify-center transition-all duration-300 ease-in-out ${
showUpcomingInfo
? 'opacity-100 scale-100'
@@ -1067,11 +1126,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
} as React.CSSProperties
}
>
{daysUntilRelease > 0
? `${daysUntilRelease}天后上映`
: daysUntilRelease === 0
? '今日上映'
: '已上映'}
{upcomingReleaseText}
</div>
</div>
) : (
@@ -1963,11 +2018,31 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
currentEpisode={currentEpisode}
totalEpisodes={actualEpisodes}
origin={origin}
description={upcomingReleaseText}
onPosterClick={() => {
setShowImageViewer(true);
}}
/>
<TrailerPickerDialog
isOpen={showTrailerPicker}
title={actualTitle}
loading={trailerLoading}
error={trailerError}
videos={trailerVideos}
onClose={() => setShowTrailerPicker(false)}
onRetry={openTrailerPicker}
onSelect={(video) => {
window.open(
`https://www.youtube.com/watch?v=${video.key}`,
'_blank',
'noopener,noreferrer'
);
setShowTrailerPicker(false);
}}
/>
{/* AI问片面板 - 只在打开或正在流式响应时渲染 */}
{aiEnabled && (showAIChat || isAIStreaming) && (
<AIChatPanel