fix: 移除 previewTaskId 自动清除 + 从预览响应提取 source_edit_plan_id #1483

Merged
auto-approve-bot merged 1 commits from fix/preview-reuse-v2 into develop 2026-08-24 16:46:02 +08:00
6 changed files with 35 additions and 9 deletions
+2
View File
@@ -38,6 +38,8 @@ export interface CreatePreviewResponse {
is_preview: boolean
resolution: string
created_at: string
/** 后端自动关联的编辑计划 ID(用于 fallback 路径传递 source_edit_plan_id */
source_edit_plan_id?: string
}
/** 预览任务详情响应 */
+9 -9
View File
@@ -9,7 +9,7 @@
* - 标题样式通过 CSS 层实时叠加,所见即所得
* - 最终成片仍走后端 FFmpeg 渲染(Step7 确认生成)
*/
import React, { useMemo, useEffect } from "react"
import React, { useMemo } from "react"
import { Modal, message } from "antd"
import { useNavigate } from "react-router-dom"
import type { VoiceClone } from "@/api/voice-clone"
@@ -79,6 +79,8 @@ const GeneratePage: React.FC = () => {
setPreviewModalOpen,
previewTaskId,
setPreviewTaskId,
storedSourceEditPlanId,
setStoredSourceEditPlanId,
} = formState
/* ── 标题样式回调(Step5 样式面板 + 右侧预览 CSS 层共用) ── */
@@ -101,13 +103,10 @@ const GeneratePage: React.FC = () => {
() => (materialMode === "auto" ? smartSelectedIds : selectedMaterials),
[materialMode, smartSelectedIds, selectedMaterials],
)
// 素材、模板或生成参数变化时,清除旧的 previewTaskId,防止 Step7 复用过期预览产物
// confirmGeneration 无法覆盖 duration/ratio/title_config,所以这些参数变化后必须重新创建预览
const titleKey = JSON.stringify(titleSettings)
useEffect(() => {
setPreviewTaskId(null)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [previewAssetIds, selectedTemplate, duration, videoRatio, titleKey, bgm, setPreviewTaskId])
// previewTaskId 不再因设置变化而自动清除。
// 用户修改设置后,confirmGeneration 会检测参数是否匹配:
// 匹配则复用产物秒出;不匹配则后端自动 fallback 到 createGenerationTask 重新渲染。
// 只有当用户明确返回 Step5 重新预览时,才会创建新的预览任务并覆盖 previewTaskId。
const previewAssetsEnabled = currentStep >= 4 && previewAssetIds.length > 0
const {
@@ -182,7 +181,7 @@ const GeneratePage: React.FC = () => {
autoSubtitles,
bgm,
generateCount,
sourceEditPlanId,
sourceEditPlanId: storedSourceEditPlanId || sourceEditPlanId,
previewTaskId,
})
@@ -227,6 +226,7 @@ const GeneratePage: React.FC = () => {
activePreset={styleUpdaters.activePreset}
titlePresets={styleUpdaters.titlePresets}
onPreviewTaskCreated={setPreviewTaskId}
onSourceEditPlanIdExtracted={setStoredSourceEditPlanId}
coverSettings={coverSettings}
onCoverSettingsChange={setCoverSettings}
duration={duration}
@@ -78,6 +78,8 @@ export interface GenerateStepContentProps {
presetVoices: PresetVoiceItem[]
/** 预览任务创建回调——传递给 Step6CoverSettings */
onPreviewTaskCreated?: (taskId: string) => void
/** 从预览响应中提取到 source_edit_plan_id 时的回调 */
onSourceEditPlanIdExtracted?: (planId: string) => void
}
export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) => {
@@ -124,6 +126,7 @@ export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) =
onDismissError,
presetVoices,
onPreviewTaskCreated,
onSourceEditPlanIdExtracted,
} = props
/* 当前模板的 segments,传给 Step2 构建 clips */
@@ -194,6 +197,7 @@ export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) =
selectedTemplate={selectedTemplate}
titleSettings={titleSettings}
onPreviewTaskCreated={onPreviewTaskCreated}
onSourceEditPlanIdExtracted={onSourceEditPlanIdExtracted}
/>
)
case 7:
@@ -18,6 +18,8 @@ interface Step6CoverSettingsProps {
titleSettings?: import("../types").TitleSettings
/** 预览任务创建回调——将 task_id 暴露给父组件供 confirmGeneration 复用 */
onPreviewTaskCreated?: (taskId: string) => void
/** 从预览响应中提取到 source_edit_plan_id 时的回调 */
onSourceEditPlanIdExtracted?: (planId: string) => void
}
const Step6CoverSettings: React.FC<Step6CoverSettingsProps> = (props) => {
@@ -46,6 +48,7 @@ const Step6CoverSettings: React.FC<Step6CoverSettingsProps> = (props) => {
selectedTemplate: props.selectedTemplate,
titleSettings: props.titleSettings,
onPreviewTaskCreated: props.onPreviewTaskCreated,
onSourceEditPlanIdExtracted: props.onSourceEditPlanIdExtracted,
})
const handleAutoGenerate = () => {
@@ -99,6 +99,10 @@ export interface GenerateFormState {
/** 预览任务 ID(由 useStep6Cover 创建后写入,供 useGenerateVideo 复用) */
previewTaskId: string | null
setPreviewTaskId: (id: string | null) => void
/** 从预览响应中提取的 source_edit_plan_id(供 fallback 路径使用) */
storedSourceEditPlanId: string | null
setStoredSourceEditPlanId: (planId: string | null) => void
}
export const useGenerateFormState = (): GenerateFormState => {
@@ -167,6 +171,9 @@ export const useGenerateFormState = (): GenerateFormState => {
/* ── 预览任务 IDuseStep6Cover 创建预览时写入,useGenerateVideo 复用) ── */
const [previewTaskId, setPreviewTaskId] = useState<string | null>(null)
/* ── 从预览响应中提取的 source_edit_plan_id(供 fallback 路径使用) ── */
const [storedSourceEditPlanId, setStoredSourceEditPlanId] = useState<string | null>(null)
/* ── 从 URL / 编辑计划加载配置 ── */
usePlanConfigLoader({
editPlanId,
@@ -217,5 +224,7 @@ export const useGenerateFormState = (): GenerateFormState => {
setPreviewModalOpen,
previewTaskId,
setPreviewTaskId,
storedSourceEditPlanId,
setStoredSourceEditPlanId,
}
}
@@ -28,6 +28,8 @@ interface UseStep6CoverProps {
titleSettings?: TitleSettings
/** 预览任务创建回调——将 task_id 暴露给父组件供 confirmGeneration 复用 */
onPreviewTaskCreated?: (taskId: string) => void
/** 从预览响应中提取到 source_edit_plan_id 时的回调 */
onSourceEditPlanIdExtracted?: (planId: string) => void
}
export function useStep6Cover({
@@ -38,6 +40,7 @@ export function useStep6Cover({
selectedTemplate = "",
titleSettings,
onPreviewTaskCreated,
onSourceEditPlanIdExtracted,
}: UseStep6CoverProps) {
const [generating, setGenerating] = useState(false)
// 防竞态:记录当前预览生成的参数指纹,任务完成时校验一致性
@@ -181,6 +184,10 @@ export function useStep6Cover({
const currentFingerprint = JSON.stringify({ selectedTemplate, assetIds, titleSettings })
if (previewResp.task_id && previewParamsRef.current === currentFingerprint) {
onPreviewTaskCreated?.(previewResp.task_id)
// 提取后端自动关联的 source_edit_plan_id,供 fallback 路径使用
if (previewResp.source_edit_plan_id) {
onSourceEditPlanIdExtracted?.(previewResp.source_edit_plan_id)
}
}
// 轮询等待预览渲染完成:递归 setTimeout 避免请求重叠 + 120s 超时兜底
await new Promise<void>((resolve, reject) => {
@@ -295,6 +302,7 @@ export function useStep6Cover({
duration,
titleSettings,
onPreviewTaskCreated,
onSourceEditPlanIdExtracted,
])
// ── 模板操作方法 ──