diff --git a/apps/web/src/pages/generate/GeneratePage.tsx b/apps/web/src/pages/generate/GeneratePage.tsx index aebbf47a6..492f13680 100644 --- a/apps/web/src/pages/generate/GeneratePage.tsx +++ b/apps/web/src/pages/generate/GeneratePage.tsx @@ -71,6 +71,20 @@ const GeneratePage: React.FC = () => { setPreviewModalOpen, } = formState + /* ── 查询视频素材,用于 Step4 标题预览背景 ── */ + const { data: videoAssets = [] } = useQuery({ + queryKey: ["generate-video-assets"], + queryFn: () => getAssetsByKind("video", { limit: 50 }), + }) + + // 获取第一个选中素材的 URL + const sourceVideoUrl = useMemo(() => { + const firstId = selectedMaterials[0] + if (!firstId) return undefined + const asset = videoAssets.find((a) => a.id === firstId) + return asset?.file_url + }, [selectedMaterials, videoAssets]) + /* ── 克隆声音 ── */ const { clones: clonedVoices, addClone, hasProcessing } = useCloneProgress() @@ -237,8 +251,8 @@ const GeneratePage: React.FC = () => { {/* ════ 右侧:预览 + 生成结果 ════ */}
- {/* 预览视频面板(Step5+ 常驻,展示选中的预览) */} - {currentStep >= 5 && ( + {/* 预览视频面板(Step4+ 常驻,Step4 显示标题预览,Step5+ 显示预览视频) */} + {currentStep >= 4 && ( { videoRatio={videoRatio} onRegenerate={step5Preview.regeneratePreview} titleText={titleSettings.title} - titleSettings={currentStep >= 4 ? titleSettings : undefined} + titleSettings={titleSettings} + showTitlePreview={currentStep === 4} + sourceVideoUrl={sourceVideoUrl} /> )} diff --git a/apps/web/src/pages/generate/components/PreviewVideoPanel.tsx b/apps/web/src/pages/generate/components/PreviewVideoPanel.tsx index 65abecc01..03d63024e 100644 --- a/apps/web/src/pages/generate/components/PreviewVideoPanel.tsx +++ b/apps/web/src/pages/generate/components/PreviewVideoPanel.tsx @@ -24,10 +24,14 @@ interface PreviewVideoPanelProps { progress: number videoRatio: string onRegenerate: () => void - /** 标题文字(Step5 起传入) */ + /** 标题文字 */ titleText?: string - /** 标题样式设置(Step5 起传入) */ + /** 标题样式设置 */ titleSettings?: TitleSettings + /** Step4 标题预览模式 */ + showTitlePreview?: boolean + /** 素材视频 URL(用于 Step4 标题预览背景) */ + sourceVideoUrl?: string } /* ── 组件 ── */ @@ -41,6 +45,8 @@ export const PreviewVideoPanel: React.FC = ({ onRegenerate, titleText, titleSettings, + showTitlePreview, + sourceVideoUrl, }) => { const hasPreview = previewStatus === "ready" && previewResult const isLoading = previewStatus === "pending" || previewStatus === "generating" @@ -170,101 +176,132 @@ export const PreviewVideoPanel: React.FC = ({ return (
-

预览视频

- {hasPreview && 480p 预览版} +

{showTitlePreview ? "标题预览" : "预览视频"}

+ {hasPreview && !showTitlePreview && 480p 预览版}
- {/* 空状态:还没生成预览 */} - {previewStatus === "idle" && ( + {/* Step4 标题预览模式 */} + {showTitlePreview && titleSettings && titleText && ( +
+ +
+ )} + + {/* Step4 但无标题时的空状态 */} + {showTitlePreview && (!titleText || !titleSettings) && (
-

暂无预览

-

在第 3 步生成预览后在此查看

+

请输入标题

+

在左侧设置标题后,这里会实时预览效果

)} - {/* 生成中 */} - {isLoading && ( -
-
-
- -

- {previewStatus === "pending" ? "排队中..." : `生成中 ${progress}%`} -

+ {/* 非 Step4 模式才显示以下内容 */} + {!showTitlePreview && ( + <> + {/* 空状态:还没生成预览 */} + {previewStatus === "idle" && ( +
+ +

暂无预览

+

在第 3 步生成预览后在此查看

-
-
-
-
-
- )} - - {/* 生成失败 */} - {isError && ( -
-
-

预览生成失败

-
-

- {typeof previewError === "string" && previewError ? previewError : "请重试"} -

- -
- )} - - {/* 预览成功 + Canvas 标题叠加 */} - {hasPreview && ( -
-
-
- {showTitlePreview && ( - )} -
- )} - {/* 预览信息 */} - {hasPreview && previewResult && ( -
-
- 时长 - - {(typeof previewResult.duration === "number" ? previewResult.duration : 0).toFixed(1)}{" "} - 秒 - -
-
- 片段数 - {previewResult.clipCount} 段 -
-
- 比例 - {videoRatio} -
-
+ {/* 生成中 */} + {isLoading && ( +
+
+
+ +

+ {previewStatus === "pending" ? "排队中..." : `生成中 ${progress}%`} +

+
+
+
+
+
+
+ )} + + {/* 生成失败 */} + {isError && ( +
+
+

预览生成失败

+
+

+ {typeof previewError === "string" && previewError ? previewError : "请重试"} +

+ +
+ )} + + {/* 预览成功 + Canvas 标题叠加 */} + {hasPreview && ( +
+
+
+ {showTitlePreview && ( + + )} +
+ )} + + {/* 预览信息 */} + {hasPreview && previewResult && ( +
+
+ 时长 + + {(typeof previewResult.duration === "number" + ? previewResult.duration + : 0 + ).toFixed(1)}{" "} + 秒 + +
+
+ 片段数 + {previewResult.clipCount} 段 +
+
+ 比例 + {videoRatio} +
+
+ )} + )}
) diff --git a/apps/web/src/pages/generate/components/Step4TitleSettings.tsx b/apps/web/src/pages/generate/components/Step4TitleSettings.tsx index c7bd31526..131548fb1 100644 --- a/apps/web/src/pages/generate/components/Step4TitleSettings.tsx +++ b/apps/web/src/pages/generate/components/Step4TitleSettings.tsx @@ -112,11 +112,6 @@ const Step4TitleSettings: React.FC = (props) => { )} - {/* 标题实时预览 - 仅当有标题文字时显示 */} - {t.titleSettings.title && ( - - )} - {/* 样式面板始终可见,两种模式下都可调整标题展示样式 */} = ({ titleText, titleSettings, - videoRatio = "16:9", + videoRatio = "9:16", + sourceVideoUrl, }) => { const containerRef = useRef(null) const canvasRef = useRef(null) @@ -142,12 +145,31 @@ const TitlePreviewCanvas: React.FC = ({ style={{ width: "100%", aspectRatio: parseAspect(videoRatio), - background: "linear-gradient(135deg, #1a1a2e, #16213e, #0f3460)", + background: sourceVideoUrl + ? "#000" + : "linear-gradient(135deg, #1a1a2e, #16213e, #0f3460)", borderRadius: 8, overflow: "hidden", position: "relative", }} > + {sourceVideoUrl && ( +