From 0240e2f58be1579b12f886366363e4c1226b5279 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Wed, 12 Aug 2026 16:49:16 +0800 Subject: [PATCH] =?UTF-8?q?fix(#1341):=20=E4=BF=AE=E5=A4=8D=E6=99=BA?= =?UTF-8?q?=E8=83=BD=E5=89=AA=E8=BE=91=E5=B0=81=E9=9D=A2=E7=94=9F=E6=88=90?= =?UTF-8?q?=E7=9A=84=203=20=E4=B8=AA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A. generatingRef 卡死 → 改用 useState + 300秒超时保护 B. Step6 缺少进度弹窗 → 添加 Modal + Spin 进度提示 C. 预览视频 URL 没回写 → 预览完成后保存到 plan.config.rendered_storage_key --- apps/web/src/api/template-editor/types.ts | 4 ++++ .../generate/components/Step6CoverSettings.tsx | 10 ++++++++++ .../pages/generate/hooks/useStep4Preview.ts | 10 ++++++++++ .../src/pages/generate/hooks/useStep6Cover.ts | 18 +++++++++++++----- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/apps/web/src/api/template-editor/types.ts b/apps/web/src/api/template-editor/types.ts index b9ef82639..a00f84e50 100644 --- a/apps/web/src/api/template-editor/types.ts +++ b/apps/web/src/api/template-editor/types.ts @@ -118,6 +118,10 @@ export interface EditPlanConfig { generate_count?: number /** 素材模式 */ material_mode?: string + /** 预览视频 URL(封面生成用) */ + rendered_storage_key?: string + /** 生成任务 ID */ + generation_task_id?: string } /* ── 模板草稿主体 ── */ diff --git a/apps/web/src/pages/generate/components/Step6CoverSettings.tsx b/apps/web/src/pages/generate/components/Step6CoverSettings.tsx index 5929f20e5..7ea7d87c3 100755 --- a/apps/web/src/pages/generate/components/Step6CoverSettings.tsx +++ b/apps/web/src/pages/generate/components/Step6CoverSettings.tsx @@ -1,4 +1,5 @@ import React from "react" +import { Modal, Spin } from "antd" import type { CoverConfig } from "../../editing-planner/types" import { useStep6Cover } from "../hooks/useStep6Cover" import Button from "@/components/ui/Button" @@ -18,6 +19,7 @@ interface Step6CoverSettingsProps { const Step6CoverSettings: React.FC = (props) => { const { coverSettings, + generating, generateAutoCover, showCoverSettings, setShowCoverSettings, @@ -95,6 +97,14 @@ const Step6CoverSettings: React.FC = (props) => { template={editingTemplate} onSave={handleSaveTemplate} /> + + {/* AI 生成封面进度弹窗 */} + +
+ +

AI 正在生成封面,请稍候...

+
+
) } diff --git a/apps/web/src/pages/generate/hooks/useStep4Preview.ts b/apps/web/src/pages/generate/hooks/useStep4Preview.ts index 150853bb0..59cbfd180 100644 --- a/apps/web/src/pages/generate/hooks/useStep4Preview.ts +++ b/apps/web/src/pages/generate/hooks/useStep4Preview.ts @@ -4,6 +4,7 @@ */ import { useState, useCallback, useMemo, useEffect, useRef } from "react" import { createPreview, getPreviewStatus } from "@/api/generation" +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" @@ -250,6 +251,15 @@ export function useStep4Preview({ it.index === index ? { ...it, status: "ready", result, progress: 100 } : it, ), ) + + // 保存预览视频 URL 到 plan config,供封面生成使用 + if (result.videoUrl && selectedTemplate) { + updateEditPlan(selectedTemplate, { + config: { rendered_storage_key: result.videoUrl } as any, + }).catch((err) => { + console.warn("[Step4] 保存预览视频URL到plan config失败:", err) + }) + } return } diff --git a/apps/web/src/pages/generate/hooks/useStep6Cover.ts b/apps/web/src/pages/generate/hooks/useStep6Cover.ts index 765632cf4..f6ec9f2e5 100644 --- a/apps/web/src/pages/generate/hooks/useStep6Cover.ts +++ b/apps/web/src/pages/generate/hooks/useStep6Cover.ts @@ -30,7 +30,7 @@ export function useStep6Cover({ assetIds = [], selectedTemplate = "", }: UseStep6CoverProps) { - const generatingRef = useRef(false) + const [generating, setGenerating] = useState(false) // ── 封面设置弹窗状态 ── const [showCoverSettings, setShowCoverSettings] = useState(false) @@ -67,7 +67,7 @@ export function useStep6Cover({ /** 调用后端智能封面 API,生成封面并更新预览 */ const generateAutoCover = useCallback(async () => { - if (generatingRef.current) { + if (generating) { message.warning("封面正在生成中,请稍候...") return } @@ -82,12 +82,17 @@ export function useStep6Cover({ return } - generatingRef.current = true + setGenerating(true) + // 超时保护:300 秒后强制重置,防止 state 卡死导致按钮永久失效 + const timeoutId = setTimeout(() => { + setGenerating(false) + }, 300000) try { const response = await generateCover(selectedTemplate, { asset_ids: assetIds, cover_type: "ai_frame", }) + clearTimeout(timeoutId) const thumbnailUrl = response.cover?.image_url || "" if (thumbnailUrl) { onCoverSettingsChange({ @@ -100,12 +105,14 @@ export function useStep6Cover({ message.warning("封面生成未返回图片,请重试") } } catch (err) { + clearTimeout(timeoutId) console.warn("[Step6] 智能封面生成失败:", err) message.error("封面生成失败,请稍后重试") } finally { - generatingRef.current = false + clearTimeout(timeoutId) + setGenerating(false) } - }, [selectedTemplate, assetIds, coverSettings, onCoverSettingsChange]) + }, [selectedTemplate, assetIds, coverSettings, onCoverSettingsChange, generating]) // ── 模板操作方法 ── const handleSelectTemplate = useCallback((id: string) => { @@ -165,6 +172,7 @@ export function useStep6Cover({ return { coverSettings, + generating, generateAutoCover, totalDuration, showCoverSettings, -- 2.54.0