From 0bcacde296048898621feb7f8229ab6769293e34 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sat, 15 Aug 2026 16:41:41 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=AD=A5=E9=AA=A4=E9=A1=BA?= =?UTF-8?q?=E5=BA=8F=E8=B0=83=E6=95=B4=20-=20=E6=A0=87=E9=A2=98(4)=20?= =?UTF-8?q?=E2=86=92=20=E9=A2=84=E8=A7=88(5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - constants.ts: 步骤定义从 模板→素材→配音→预览→标题→封面→确认 改为 模板→素材→配音→标题→预览→封面→确认 - GenerateStepContent.tsx: case 4 渲染 Step4TitleSettings,case 5 渲染 Step5GeneratePreview - useStepNavigation.ts: 标题步骤(4)校验 title,预览步骤(5)校验 previewReady - useStep4Preview.ts → useStep5Preview.ts: 新增 titleSettings prop, createPreview 调用增加标题字段 - Step4GeneratePreview.tsx → Step5GeneratePreview.tsx: 重命名 - api/generation/types.ts: CreatePreviewRequest 新增标题烧录参数 - GeneratePage.tsx: 预览面板 Step5+ 显示,结果面板 Step6+ 显示 - PreviewVideoPanel.tsx: Canvas 标题叠加保留 - 测试文件同步更新引用 --- apps/web/src/api/generation/types.ts | 9 +++ apps/web/src/pages/generate/GeneratePage.tsx | 57 ++++++++++--------- .../components/GenerateStepContent.tsx | 24 ++++---- .../generate/components/PreviewVideoPanel.tsx | 2 +- ...tePreview.tsx => Step5GeneratePreview.tsx} | 10 ++-- apps/web/src/pages/generate/constants.ts | 4 +- ...{useStep4Preview.ts => useStep5Preview.ts} | 29 ++++++++-- .../pages/generate/hooks/useStepNavigation.ts | 10 ++-- .../src/test/pages/generate/smoke.test.tsx | 4 +- .../generate/step4-preview.smoke.test.tsx | 17 ------ .../generate/step5-preview.smoke.test.tsx | 17 ++++++ 11 files changed, 106 insertions(+), 77 deletions(-) rename apps/web/src/pages/generate/components/{Step4GeneratePreview.tsx => Step5GeneratePreview.tsx} (98%) rename apps/web/src/pages/generate/hooks/{useStep4Preview.ts => useStep5Preview.ts} (92%) delete mode 100644 apps/web/src/test/pages/generate/step4-preview.smoke.test.tsx create mode 100644 apps/web/src/test/pages/generate/step5-preview.smoke.test.tsx diff --git a/apps/web/src/api/generation/types.ts b/apps/web/src/api/generation/types.ts index f8c06f7f8..599174ad6 100755 --- a/apps/web/src/api/generation/types.ts +++ b/apps/web/src/api/generation/types.ts @@ -13,6 +13,15 @@ export interface CreatePreviewRequest { video_title?: string duration?: number video_ratio?: string + /* 标题烧录参数(可选,传入后 ASS 渲染标题到预览视频中) */ + title_text?: string + title_font?: string + title_size?: number + title_color?: string + title_position?: string + title_bold?: boolean + title_stroke?: boolean + title_shadow?: boolean bgm_config?: { enabled: boolean preset_id?: string diff --git a/apps/web/src/pages/generate/GeneratePage.tsx b/apps/web/src/pages/generate/GeneratePage.tsx index 7d2e1a597..aebbf47a6 100644 --- a/apps/web/src/pages/generate/GeneratePage.tsx +++ b/apps/web/src/pages/generate/GeneratePage.tsx @@ -1,6 +1,6 @@ /** * 智能剪辑页面 — V22 多预览 + 配音前置 - * 7 步向导:选择模板 → 选择素材 → 选择配音 → 生成预览 → 选择标题 → 选择封面 → 确认生成 + * 7 步向导:选择模板 → 选择素材 → 选择配音 → 选择标题 → 生成预览 → 选择封面 → 确认生成 * 左右布局:左侧 generate-form + 右侧 generate-preview * 主组件仅保留整体布局与事件编排 * 状态管理 → hooks/useGenerateFormState @@ -24,7 +24,7 @@ import GenerateStepActions from "./components/GenerateStepActions" import { useGenerateFormState } from "./hooks/useGenerateFormState" import { useStepNavigation } from "./hooks/useStepNavigation" import { useGenerateVideo } from "./hooks/useGenerateVideo" -import { useStep4Preview } from "./hooks/useStep4Preview" +import { useStep5Preview } from "./hooks/useStep5Preview" import "./generate.css" const GeneratePage: React.FC = () => { @@ -96,8 +96,8 @@ const GeneratePage: React.FC = () => { return id ? [id] : [] }, [voiceMode, selectedVoice, selectedClonedVoice]) - /* ── Step4 预览生成(多预览 + voice_ids) ── */ - const step4Preview = useStep4Preview({ + /* ── Step5 预览生成(多预览 + voice_ids) ── */ + const step5Preview = useStep5Preview({ templates: userTemplates, selectedTemplate, materialMode, @@ -108,6 +108,7 @@ const GeneratePage: React.FC = () => { voiceIds: previewVoiceIds, voiceLibraryId: selectedVoice || undefined, previewCount, + titleSettings, }) /* ── 步骤导航 ── */ @@ -119,7 +120,7 @@ const GeneratePage: React.FC = () => { selectedMaterials, smartSelectedIds, titleSettings, - previewReady: step4Preview.canProceed, + previewReady: step5Preview.canProceed, }) /* ── 视频生成核心逻辑 ── */ @@ -150,7 +151,7 @@ const GeneratePage: React.FC = () => { autoSubtitles, bgm, generateCount, - previewTaskId: step4Preview.selectedTaskId, + previewTaskId: step5Preview.selectedTaskId, }) /* ================================================================ @@ -207,20 +208,20 @@ const GeneratePage: React.FC = () => { onDismissError={handleDismissError} presetVoices={presetVoices} videoRatio={videoRatio} - /* Step4 多预览 */ + /* Step5 多预览 */ previewCount={previewCount} onPreviewCountChange={setPreviewCount} - previewItems={step4Preview.items} - previewSelectedIndex={step4Preview.selectedIndex} - onSelectPreview={step4Preview.setSelectedIndex} - previewOverallStatus={step4Preview.previewStatus} - previewOverallError={step4Preview.previewError} - previewOverallProgress={step4Preview.progress} - previewAnyGenerating={step4Preview.anyGenerating} - previewTemplateName={step4Preview.templateName} - previewMaterialCount={step4Preview.materialCount} - onGeneratePreview={step4Preview.generatePreview} - onRegeneratePreview={step4Preview.regeneratePreview} + previewItems={step5Preview.items} + previewSelectedIndex={step5Preview.selectedIndex} + onSelectPreview={step5Preview.setSelectedIndex} + previewOverallStatus={step5Preview.previewStatus} + previewOverallError={step5Preview.previewError} + previewOverallProgress={step5Preview.progress} + previewAnyGenerating={step5Preview.anyGenerating} + previewTemplateName={step5Preview.templateName} + previewMaterialCount={step5Preview.materialCount} + onGeneratePreview={step5Preview.generatePreview} + onRegeneratePreview={step5Preview.regeneratePreview} /> { {/* ════ 右侧:预览 + 生成结果 ════ */}
- {/* 预览视频面板(Step4+ 常驻,展示选中的预览) */} - {currentStep >= 4 && ( + {/* 预览视频面板(Step5+ 常驻,展示选中的预览) */} + {currentStep >= 5 && ( = 5 ? titleSettings : undefined} + titleSettings={currentStep >= 4 ? titleSettings : undefined} /> )} - {/* 正式生成结果(Step5+ 才显示) */} - {currentStep >= 5 && ( + {/* 正式生成结果(Step6+ 才显示) */} + {currentStep >= 6 && ( = (props) = ) case 4: return ( - + ) + case 5: + return ( + = (props) = onRegeneratePreview={onRegeneratePreview} /> ) - case 5: - return ( - - ) case 6: return ( = ({ +const Step5GeneratePreview: React.FC = ({ templateName: _templateName, materialCount: _materialCount, videoRatio, @@ -291,4 +291,4 @@ const Step4GeneratePreview: React.FC = ({ ) } -export default Step4GeneratePreview +export default Step5GeneratePreview diff --git a/apps/web/src/pages/generate/constants.ts b/apps/web/src/pages/generate/constants.ts index fc93ace66..74a7a3bf9 100644 --- a/apps/web/src/pages/generate/constants.ts +++ b/apps/web/src/pages/generate/constants.ts @@ -32,8 +32,8 @@ export const STEPS = [ { key: 1, label: "选择模板" }, { key: 2, label: "选择素材" }, { key: 3, label: "选择配音" }, - { key: 4, label: "生成预览" }, - { key: 5, label: "选择标题" }, + { key: 4, label: "选择标题" }, + { key: 5, label: "生成预览" }, { key: 6, label: "选择封面" }, { key: 7, label: "确认生成" }, ] diff --git a/apps/web/src/pages/generate/hooks/useStep4Preview.ts b/apps/web/src/pages/generate/hooks/useStep5Preview.ts similarity index 92% rename from apps/web/src/pages/generate/hooks/useStep4Preview.ts rename to apps/web/src/pages/generate/hooks/useStep5Preview.ts index cbbe8342a..460e9d7b1 100644 --- a/apps/web/src/pages/generate/hooks/useStep4Preview.ts +++ b/apps/web/src/pages/generate/hooks/useStep5Preview.ts @@ -1,5 +1,5 @@ /** - * Step 4 生成预览 Hook(支持多预览 + voice_ids) + * Step 5 生成预览 Hook(支持多预览 + voice_ids) * 调用 /generation/preview 接口创建多个预览任务,轮询状态直到全部完成 */ import { useState, useCallback, useMemo, useEffect, useRef } from "react" @@ -8,6 +8,7 @@ import { updateEditPlan } from "@/api/template-editor/editPlans" import type { PreviewTaskResponse, PreviewStatus as ApiPreviewStatus } from "@/api/generation" import type { EditingTemplate } from "@/api/editing-planner" import { safeExtractError } from "./generate-video/errorUtils" +import type { TitleSettings } from "../types" /** 安全地将值转为字符串,防止对象被直接渲染导致 React Error #31 */ const safeString = (val: unknown, fallback: string): string => { @@ -26,7 +27,7 @@ const safeNumber = (val: unknown, fallback = 0): number => { return fallback } -interface UseStep4PreviewProps { +interface UseStep5PreviewProps { templates: EditingTemplate[] selectedTemplate: string materialMode: "manual" | "auto" @@ -40,6 +41,8 @@ interface UseStep4PreviewProps { voiceLibraryId?: string /** 要生成的预览数量 */ previewCount?: number + /** 标题设置(传递给后端,让预览视频包含标题) */ + titleSettings?: TitleSettings } export type PreviewStatus = "idle" | "pending" | "generating" | "ready" | "error" @@ -78,7 +81,7 @@ const createInitialItem = (index: number): PreviewItem => ({ progress: 0, }) -export function useStep4Preview({ +export function useStep5Preview({ templates, selectedTemplate, materialMode, @@ -89,7 +92,8 @@ export function useStep4Preview({ voiceIds, voiceLibraryId, previewCount = 1, -}: UseStep4PreviewProps) { + titleSettings, +}: UseStep5PreviewProps) { const templateName = useMemo( () => templates.find((t) => t.id === selectedTemplate)?.name ?? "未选择", [templates, selectedTemplate], @@ -153,6 +157,7 @@ export function useStep4Preview({ duration, videoRatio, voiceIds: [...(voiceIds || [])].sort().join(","), + titleSettings: titleSettings?.title || "", }) useEffect(() => { @@ -164,6 +169,7 @@ export function useStep4Preview({ duration, videoRatio, [...(voiceIds || [])].sort().join(","), + titleSettings?.title || "", ].join("|") const prevKey = [ @@ -174,6 +180,7 @@ export function useStep4Preview({ prevDepsRef.current.duration, prevDepsRef.current.videoRatio, prevDepsRef.current.voiceIds, + prevDepsRef.current.titleSettings, ].join("|") if (prevKey !== currentKey && items.some((it) => it.status !== "idle")) { @@ -191,6 +198,7 @@ export function useStep4Preview({ duration, videoRatio, voiceIds: [...(voiceIds || [])].sort().join(","), + titleSettings: titleSettings?.title || "", } // eslint-disable-next-line react-hooks/exhaustive-deps }, [ @@ -202,6 +210,7 @@ export function useStep4Preview({ videoRatio, voiceIds, previewCount, + titleSettings, ]) // 组件卸载时清理所有轮询 @@ -349,6 +358,15 @@ export function useStep4Preview({ video_ratio: videoRatio, voice_ids: voiceIds && voiceIds.length > 0 ? voiceIds : undefined, voice_library_id: voiceLibraryId || undefined, + // 标题信息 + title_text: titleSettings?.title || undefined, + title_font: titleSettings?.font || undefined, + title_size: titleSettings?.size || undefined, + title_color: titleSettings?.color || undefined, + title_position: titleSettings?.position || undefined, + title_bold: titleSettings?.bold || undefined, + title_stroke: titleSettings?.stroke || undefined, + title_shadow: titleSettings?.shadow || undefined, }) if (startTimeRef.current === 0) return @@ -374,6 +392,7 @@ export function useStep4Preview({ voiceIds, voiceLibraryId, previewCount, + titleSettings, clearPollTimer, pollPreviewStatus, ]) @@ -450,4 +469,4 @@ export function useStep4Preview({ } } -export default useStep4Preview +export default useStep5Preview diff --git a/apps/web/src/pages/generate/hooks/useStepNavigation.ts b/apps/web/src/pages/generate/hooks/useStepNavigation.ts index 29b4ed6f1..13e0b7355 100644 --- a/apps/web/src/pages/generate/hooks/useStepNavigation.ts +++ b/apps/web/src/pages/generate/hooks/useStepNavigation.ts @@ -1,7 +1,7 @@ /** * GeneratePage 步骤导航 * 管理步骤切换与各步骤的前置校验 - * 步骤顺序:模板(1) → 素材(2) → 配音(3) → 预览(4) → 标题(5) → 封面(6) → 确认(7) + * 步骤顺序:模板(1) → 素材(2) → 配音(3) → 标题(4) → 预览(5) → 封面(6) → 确认(7) */ import { message } from "antd" import type { TitleSettings } from "../types" @@ -49,12 +49,12 @@ export const useStepNavigation = (options: UseStepNavigationOptions): UseStepNav return } // Step3 配音:配音为可选项,不强制校验,用户可跳过 - if (currentStep === 4 && !previewReady) { - message.warning("请先生成剪辑预览") + if (currentStep === 4 && !titleSettings.title.trim()) { + message.warning("请选择或输入标题") return } - if (currentStep === 5 && !titleSettings.title.trim()) { - message.warning("请选择或输入标题") + if (currentStep === 5 && !previewReady) { + message.warning("请先生成剪辑预览") return } if (currentStep < 7) { diff --git a/apps/web/src/test/pages/generate/smoke.test.tsx b/apps/web/src/test/pages/generate/smoke.test.tsx index c4553f49d..0439dd54e 100755 --- a/apps/web/src/test/pages/generate/smoke.test.tsx +++ b/apps/web/src/test/pages/generate/smoke.test.tsx @@ -17,7 +17,7 @@ import "@/api/generation/types" // 直接引入所有 Step 组件,建立完整依赖链 import "@/pages/generate/GeneratePage" import "@/pages/generate/components/Step2MaterialSelect" -import "@/pages/generate/components/Step4GeneratePreview" +import "@/pages/generate/components/Step5GeneratePreview" import "@/pages/generate/components/Step4TitleSettings" import "@/pages/generate/components/Step5VoiceSelect" import "@/pages/generate/components/PreviewVideoPanel" @@ -47,7 +47,7 @@ describe("GeneratePage module smoke test", () => { }) }) import "@/pages/generate/hooks/useGenerateVideo" -import "@/pages/generate/hooks/useStep4Preview" +import "@/pages/generate/hooks/useStep5Preview" import "@/pages/generate/hooks/generate-video/useGenerationPolling" import "@/pages/generate/hooks/useGenerateFormState" import "@/pages/generate/hooks/useGenerateFormState/useTemplateSelection" diff --git a/apps/web/src/test/pages/generate/step4-preview.smoke.test.tsx b/apps/web/src/test/pages/generate/step4-preview.smoke.test.tsx deleted file mode 100644 index 57da83454..000000000 --- a/apps/web/src/test/pages/generate/step4-preview.smoke.test.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Step4GeneratePreview smoke test - * 确保 vitest related 模式能匹配到第4步预览生成相关文件的改动 - */ -import { describe, it, expect } from "vitest" - -import "@/pages/generate/components/Step4GeneratePreview" -import "@/pages/generate/hooks/useStep4Preview" -import "@/pages/generate/hooks/useStepNavigation" -import "@/pages/generate/components/GenerateStepContent" -import "@/pages/generate/GeneratePage" - -describe("Step4GeneratePreview module smoke test", () => { - it("should load all step4 preview modules", () => { - expect(true).toBe(true) - }) -}) diff --git a/apps/web/src/test/pages/generate/step5-preview.smoke.test.tsx b/apps/web/src/test/pages/generate/step5-preview.smoke.test.tsx new file mode 100644 index 000000000..f6388debf --- /dev/null +++ b/apps/web/src/test/pages/generate/step5-preview.smoke.test.tsx @@ -0,0 +1,17 @@ +/** + * Step5GeneratePreview smoke test + * 确保 vitest related 模式能匹配到第5步预览生成相关文件的改动 + */ +import { describe, it, expect } from "vitest" + +import "@/pages/generate/components/Step5GeneratePreview" +import "@/pages/generate/hooks/useStep5Preview" +import "@/pages/generate/hooks/useStepNavigation" +import "@/pages/generate/components/GenerateStepContent" +import "@/pages/generate/GeneratePage" + +describe("Step5GeneratePreview module smoke test", () => { + it("should load all step5 preview modules", () => { + expect(true).toBe(true) + }) +}) -- 2.54.0