diff --git a/apps/web/src/pages/generate/GeneratePage.tsx b/apps/web/src/pages/generate/GeneratePage.tsx index 8d47b17e5..ffd783cd5 100644 --- a/apps/web/src/pages/generate/GeneratePage.tsx +++ b/apps/web/src/pages/generate/GeneratePage.tsx @@ -8,7 +8,7 @@ * - 步骤 6 封面从最终成片中智能选帧(MediaKit) * - 点"确认生成"时调用 createGenerationTask 创建一次服务器渲染任务 */ -import React, { useMemo, useState, useEffect, useRef } from "react" +import React, { useMemo, useState, useEffect, useRef, useCallback } from "react" import { message } from "antd" import { useNavigate } from "react-router-dom" import type { VoiceClone } from "@/api/voice-clone" @@ -217,6 +217,22 @@ const GeneratePage: React.FC = () => { }, }) + /* ── 步骤4「确认生成视频」:校验标题/预览 → 创建最终渲染任务 → 成功后进入步骤5 ── */ + const handleConfirmGenerate = useCallback(async () => { + if (!titleSettings.title.trim()) { + message.warning("请选择或输入标题") + return + } + if (!previewReady) { + message.warning("预览视频正在加载,请稍候") + return + } + const ok = await handleGenerate() + if (ok) { + setCurrentStep(5) + } + }, [titleSettings.title, previewReady, handleGenerate, setCurrentStep]) + /* ── 步骤导航 ── */ const { goNext, goPrev } = useStepNavigation({ currentStep, @@ -300,7 +316,7 @@ const GeneratePage: React.FC = () => { currentStep={currentStep} onPrev={goPrev} onNext={goNext} - onGenerate={handleGenerate} + onConfirmGenerate={handleConfirmGenerate} generating={generating} generated={generated} generateError={generateError} diff --git a/apps/web/src/pages/generate/components/GenerateStepActions.tsx b/apps/web/src/pages/generate/components/GenerateStepActions.tsx index 22edc0f67..d95136a9b 100644 --- a/apps/web/src/pages/generate/components/GenerateStepActions.tsx +++ b/apps/web/src/pages/generate/components/GenerateStepActions.tsx @@ -1,14 +1,19 @@ /** * GeneratePage 步骤底部操作按钮 + * + * 步骤 1~3:上一步 / 下一步 + * 步骤 4(标题+预览):上一步 / 确认生成视频(点击后直接创建最终渲染任务,成功后跳转步骤5) + * 步骤 5(确认生成):上一步 / 下一步(渲染中禁用,渲染完成后可进入封面) + * 步骤 6(选择封面):仅上一步 */ import React from "react" -import { ThunderboltOutlined } from "@ant-design/icons" export interface GenerateStepActionsProps { currentStep: number onPrev: () => void onNext: () => void - onGenerate: () => void + /** 步骤4:确认生成视频(校验 + 创建渲染任务 + 成功后进入步骤5) */ + onConfirmGenerate: () => void | Promise generating: boolean generated: boolean generateError: string | null @@ -18,36 +23,65 @@ export const GenerateStepActions: React.FC = ({ currentStep, onPrev, onNext, - onGenerate, + onConfirmGenerate, generating, generated, generateError, }) => { + const renderPrimaryButton = () => { + /* 步骤 4:确认生成视频(触发按钮在标题页) */ + if (currentStep === 4) { + if (generating) { + return ( + + ) + } + if (generateError) { + return ( + + ) + } + if (generated) { + return ( + + ) + } + return ( + + ) + } + + /* 步骤 5:渲染中禁用,完成后下一步进入封面 */ + if (currentStep === 5) { + return ( + + ) + } + + /* 步骤 6(最后一步):无主按钮 */ + return null + } + return (
- {currentStep < 6 ? ( - - ) : ( - - )} + {renderPrimaryButton()}
) } diff --git a/apps/web/src/pages/generate/components/step7-confirm/GenerationStatus.tsx b/apps/web/src/pages/generate/components/step7-confirm/GenerationStatus.tsx index 901e2c3d7..3b4a7abe7 100755 --- a/apps/web/src/pages/generate/components/step7-confirm/GenerationStatus.tsx +++ b/apps/web/src/pages/generate/components/step7-confirm/GenerationStatus.tsx @@ -25,10 +25,21 @@ const GenerationStatus: React.FC = ({ onRetry, onDismissError, }) => { - if (!generating && !generated && !generateError) return null - return (
+ {!generating && !generated && !generateError && ( +
+
+
🎬
+
+
尚未开始生成视频
+
+ 请返回「选择标题」步骤,点击「确认生成视频」开始渲染最终视频 +
+
+
+
+ )} {generating && (
diff --git a/apps/web/src/pages/generate/hooks/useGenerateVideo.ts b/apps/web/src/pages/generate/hooks/useGenerateVideo.ts index c56486f26..6ed60aefc 100755 --- a/apps/web/src/pages/generate/hooks/useGenerateVideo.ts +++ b/apps/web/src/pages/generate/hooks/useGenerateVideo.ts @@ -44,12 +44,13 @@ export function useGenerateVideo(props: UseGenerateVideoProps) { onFailed: handleFailed, }) - /* ── 生成视频 ── */ - const generate = useCallback(async () => { + /* ── 生成视频 ── + 返回 true 表示任务创建成功并已开始轮询;false 表示校验未通过或创建失败 */ + const generate = useCallback(async (): Promise => { const errorMsg = validateGenerateInputs(props) if (errorMsg) { message.warning(errorMsg) - return + return false } setGenerating(true) @@ -142,7 +143,9 @@ export function useGenerateVideo(props: UseGenerateVideoProps) { const finalMsg = translateError(backendMsg) setGenerateError(finalMsg) message.error(finalMsg) + return false } + return true }, [props, clearTimer, startPolling, selectedTemplate]) const retry = useCallback(() => { diff --git a/apps/web/src/pages/generate/hooks/useStep7Generate.ts b/apps/web/src/pages/generate/hooks/useStep7Generate.ts index 0cc44c1c7..d17f45e56 100644 --- a/apps/web/src/pages/generate/hooks/useStep7Generate.ts +++ b/apps/web/src/pages/generate/hooks/useStep7Generate.ts @@ -87,7 +87,9 @@ export function useStep7Generate({ } const handleScrollToPreview = () => { - const el = document.querySelector(".xx-preview-section") + const el = + document.querySelector(".xx-inline-video-player") || + document.querySelector(".xx-preview-section") el?.scrollIntoView({ behavior: "smooth", block: "start" }) }