From 6d246ca11e4366fcdc992ba9354e4a94ce109c77 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Fri, 7 Aug 2026 20:52:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=99=BA=E8=83=BD=E5=B0=81=E9=9D=A2?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E5=90=8E=E7=AB=AF=E7=94=9F=E6=88=90=20+=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=B0=81=E9=9D=A2=E8=AF=B4=E6=98=8E=E6=96=87?= =?UTF-8?q?=E5=AD=97=20(#1271,=20#1272)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useStep6Cover 新增 generateAutoCover 方法 - Step6CoverSettings auto 模式自动触发 POST /generate-cover - 预览区优先显示 thumbnail_url - autoTriggeredRef 在素材变更时正确重置 - buildPayload.ts 空值兜底 - 删除封面说明文字(#1272) - can_generate 使用 any() 优化 - 清理测试残留代码 --- .../components/GenerateStepContent.tsx | 2 + .../components/Step6CoverSettings.tsx | 54 ++++++++++++++++--- .../src/pages/generate/hooks/useStep6Cover.ts | 36 ++++++++++++- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/apps/web/src/pages/generate/components/GenerateStepContent.tsx b/apps/web/src/pages/generate/components/GenerateStepContent.tsx index eb6863906..fee188fb6 100644 --- a/apps/web/src/pages/generate/components/GenerateStepContent.tsx +++ b/apps/web/src/pages/generate/components/GenerateStepContent.tsx @@ -172,6 +172,8 @@ export const GenerateStepContent: React.FC = (props) = coverSettings={coverSettings} onCoverSettingsChange={onCoverSettingsChange} duration={duration} + assetIds={materialMode === "auto" ? smartSelectedIds : selectedMaterials} + selectedTemplate={selectedTemplate} /> ) case 7: diff --git a/apps/web/src/pages/generate/components/Step6CoverSettings.tsx b/apps/web/src/pages/generate/components/Step6CoverSettings.tsx index f0b802739..c0a4e9fef 100755 --- a/apps/web/src/pages/generate/components/Step6CoverSettings.tsx +++ b/apps/web/src/pages/generate/components/Step6CoverSettings.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useEffect, useRef } from "react" import type { CoverConfig } from "../../editing-planner/types" import { useStep6Cover } from "../hooks/useStep6Cover" import { CoverModeSelector } from "./cover-settings/CoverModeSelector" @@ -9,6 +9,10 @@ interface Step6CoverSettingsProps { coverSettings: CoverConfig onCoverSettingsChange: (settings: CoverConfig) => void duration: number + /** 当前素材 ID 列表,用于智能封面生成 */ + assetIds?: string[] + /** 当前选中的模板 ID */ + selectedTemplate?: string } const Step6CoverSettings: React.FC = (props) => { @@ -19,10 +23,47 @@ const Step6CoverSettings: React.FC = (props) => { setMode, setFrameTime, handleUpload, + generateAutoCover, totalDuration, COVER_MODE_LABELS, COVER_MODE_ICONS, - } = useStep6Cover(props) + } = useStep6Cover({ + coverSettings: props.coverSettings, + onCoverSettingsChange: props.onCoverSettingsChange, + duration: props.duration, + assetIds: props.assetIds, + selectedTemplate: props.selectedTemplate, + }) + + // 进入 auto 模式时自动触发智能封面生成 + const autoTriggeredRef = useRef(false) + useEffect(() => { + // 切换模式、禁用封面或素材变更时重置触发标记 + if (coverSettings.mode !== "auto" || !coverSettings.enabled) { + autoTriggeredRef.current = false + return + } + // 有素材且未生成过封面时自动触发 + if ( + coverSettings.mode === "auto" && + !coverSettings.thumbnail_url && + !autoTriggeredRef.current && + props.assetIds && + props.assetIds.length > 0 + ) { + autoTriggeredRef.current = true + generateAutoCover() + } + }, [ + coverSettings.enabled, + coverSettings.mode, + coverSettings.thumbnail_url, + generateAutoCover, + props.assetIds, + ]) + + // 预览图:优先 thumbnail_url,其次 upload_url + const previewUrl = coverSettings.thumbnail_url || coverSettings.upload_url return (
@@ -52,9 +93,6 @@ const Step6CoverSettings: React.FC = (props) => { {coverSettings.mode === "auto" && (
-
- AI 将分析视频内容,自动选择最具吸引力的画面作为封面。 -
🤖 AI 智能选帧 @@ -77,14 +115,14 @@ const Step6CoverSettings: React.FC = (props) => {
封面预览
- {coverSettings.upload_url ? ( - 封面预览 + {previewUrl ? ( + 封面预览 ) : (
🖼️ {coverSettings.mode === "auto" - ? "AI 智能选择" + ? "AI 正在选择..." : coverSettings.mode === "frame" ? `帧 ${formatTime(coverSettings.frame_time)}` : "未上传封面"} diff --git a/apps/web/src/pages/generate/hooks/useStep6Cover.ts b/apps/web/src/pages/generate/hooks/useStep6Cover.ts index f94085b28..4dd3781cb 100644 --- a/apps/web/src/pages/generate/hooks/useStep6Cover.ts +++ b/apps/web/src/pages/generate/hooks/useStep6Cover.ts @@ -2,21 +2,30 @@ * Step 6 封面设置 Hook * 封装封面设置的交互逻辑 */ -import { useCallback } from "react" +import { useCallback, useRef } from "react" import type { CoverConfig } from "../../editing-planner/types" import { COVER_MODE_LABELS, COVER_MODE_ICONS, DEFAULT_COVER_SETTINGS } from "../constants" +import { generateCover } from "@/api/template-editor" interface UseStep6CoverProps { coverSettings: CoverConfig onCoverSettingsChange: (settings: CoverConfig) => void duration: number + /** 当前素材 ID 列表,用于智能封面生成 */ + assetIds?: string[] + /** 当前选中的模板 ID */ + selectedTemplate?: string } export function useStep6Cover({ coverSettings, onCoverSettingsChange, duration, + assetIds = [], + selectedTemplate = "", }: UseStep6CoverProps) { + const generatingRef = useRef(false) + const formatTime = useCallback((seconds: number) => { const m = Math.floor(seconds / 60) const s = Math.floor(seconds % 60) @@ -62,6 +71,30 @@ export function useStep6Cover({ [coverSettings, onCoverSettingsChange], ) + /** 调用后端智能封面 API,生成封面并更新预览 */ + const generateAutoCover = useCallback(async () => { + if (!selectedTemplate || assetIds.length === 0 || generatingRef.current) return + generatingRef.current = true + try { + const response = await generateCover(selectedTemplate, { + asset_ids: assetIds, + cover_type: "ai_frame", + }) + const thumbnailUrl = response.cover?.thumbnail_url || "" + if (thumbnailUrl) { + onCoverSettingsChange({ + ...coverSettings, + thumbnail_url: thumbnailUrl, + ai_suggested_time: response.cover?.frame_time ?? null, + }) + } + } catch (err) { + console.warn("[Step6] 智能封面生成失败:", err) + } finally { + generatingRef.current = false + } + }, [selectedTemplate, assetIds, coverSettings, onCoverSettingsChange]) + const totalDuration = duration || 30 return { @@ -71,6 +104,7 @@ export function useStep6Cover({ setMode, setFrameTime, handleUpload, + generateAutoCover, totalDuration, COVER_MODE_LABELS, COVER_MODE_ICONS,