From b0ea3565ee3d388eb10a99b5a9bee189f0d7c326 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sat, 5 Sep 2026 08:38:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(#1677):=20AI=20Review=20=E9=98=BB=E5=A1=9E?= =?UTF-8?q?=E9=A1=B9=E4=BF=AE=E5=A4=8D=20=E2=80=94=20=E9=85=8D=E9=9F=B3?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=88=87=E6=8D=A2=E5=90=8C=E6=AD=A5+?= =?UTF-8?q?=E9=A2=84=E8=A7=88=E6=A0=87=E9=A2=98=E5=8F=8C=E9=87=8D=E5=8F=A0?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 配音同步 effect 响应模式切换:独立→共用时所有变体刷成共用配音; 独立模式下共用配音变化仅同步变体[0],不覆盖用户单独选择 - 批量预览不再传 titles/title_config 给后端:标题由 CSS 浮层实时叠加, 避免服务器预览视频烧录标题后与前端浮层双重显示; buildPreviewRequest 移除 titleSettings 依赖及 eslint-disable; 预览指纹不含 titles,标题文字/样式变化不触发重渲染 --- apps/web/src/pages/generate/GeneratePage.tsx | 56 ++++++++----------- .../pages/generate/hooks/useBatchPreview.ts | 4 +- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/apps/web/src/pages/generate/GeneratePage.tsx b/apps/web/src/pages/generate/GeneratePage.tsx index 258867c2a..a97b6ba02 100644 --- a/apps/web/src/pages/generate/GeneratePage.tsx +++ b/apps/web/src/pages/generate/GeneratePage.tsx @@ -90,21 +90,28 @@ const GeneratePage: React.FC = () => { const isBatch = previewCount > 1 /* ── 配音选择同步:共用配音 ↔ 变体数组 ── */ - // 共用配音变化且独立数组[0]为空时,填充到所有变体(独立模式下用户已单独选择的不覆盖) - const prevSharedVoiceRef = useRef(selectedVoice) + // 触发场景:①共用配音变化 ②批量模式进入/退出 ③独立→共用切换(需把所有变体刷成共用配音) + // 独立模式下:仅同步变体[0](其选择器绑定共用配音),用户单独选择的其他变体不覆盖 + const prevVoiceSyncRef = useRef({ + voice: selectedVoice, + batch: isBatch, + perVideo: voiceModePerVideo, + }) useEffect(() => { - if (prevSharedVoiceRef.current === selectedVoice) return - prevSharedVoiceRef.current = selectedVoice - if (!isBatch || !voiceModePerVideo) { - // 共用模式:所有变体跟随共用配音 - setVoiceLibraryIds((prev) => - prev.map((id, i) => (i === 0 ? selectedVoice : id || selectedVoice)), - ) - } else { - setVoiceLibraryIds((prev) => prev.map((id, i) => (i === 0 ? selectedVoice : id))) + const prev = prevVoiceSyncRef.current + const voiceChanged = prev.voice !== selectedVoice + const modeChanged = prev.batch !== isBatch || prev.perVideo !== voiceModePerVideo + prevVoiceSyncRef.current = { voice: selectedVoice, batch: isBatch, perVideo: voiceModePerVideo } + if (!voiceChanged && !modeChanged) return + if (!isBatch) return + if (!voiceModePerVideo) { + // 共用模式(含刚从独立切回):所有变体跟随共用配音,未选择的补默认值 + setVoiceLibraryIds((prevIds) => prevIds.map((id) => id || selectedVoice)) + } else if (voiceChanged) { + // 独立模式下共用配音变化:仅同步变体[0](与共用选择器绑定),其余不覆盖 + setVoiceLibraryIds((prevIds) => prevIds.map((id, i) => (i === 0 ? selectedVoice : id))) } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedVoice]) + }, [selectedVoice, isBatch, voiceModePerVideo, setVoiceLibraryIds]) /* ── 数量选择弹窗 ── */ const [countModalOpen, setCountModalOpen] = useState(false) @@ -220,22 +227,8 @@ const GeneratePage: React.FC = () => { ? { voice_library_ids: voiceLibraryIds.map((id) => id || voiceLibraryId) } : {}), preview_count: previewCount, - titles: previewTitles.some((t) => t?.trim()) ? previewTitles : [], - // 标题样式全局共用(文字由 titles[] 按变体独立提供,后端会把变体文字合并进 title_config) - ...(titleSettings.title || previewTitles[0] - ? { - title_config: { - text: previewTitles[0] || titleSettings.title, - font: titleSettings.font, - font_size: titleSettings.size, - font_color: titleSettings.color, - position: titleSettings.position, - bold: titleSettings.bold, - stroke: titleSettings.stroke, - shadow: titleSettings.shadow, - }, - } - : {}), + // 批量预览不传 titles/title_config:标题文字与样式由前端 CSS 浮层实时叠加 + // (用户改标题/样式即时可见,无需重渲染);正式生成时才把标题烧录进成片 duration: duration || undefined, bgm_config: { enabled: bgm !== false, @@ -255,14 +248,11 @@ const GeneratePage: React.FC = () => { voiceModePerVideo, voiceLibraryIds, previewCount, - previewTitles, duration, bgm, bgmConfig, storedSourceEditPlanId, sourceEditPlanId, - // eslint-disable-next-line react-hooks/exhaustive-deps - titleSettings, ]) const { @@ -388,10 +378,10 @@ const GeneratePage: React.FC = () => { } // 批量模式停留在步骤4,右侧网格显示生成进度,完成后点"下一步"进封面 }, [ - previewTitles, isBatch, selectedVariantIds.length, previewReady, + previewTitles, handleGenerate, setCurrentStep, ]) diff --git a/apps/web/src/pages/generate/hooks/useBatchPreview.ts b/apps/web/src/pages/generate/hooks/useBatchPreview.ts index 991561d67..e8aacae52 100644 --- a/apps/web/src/pages/generate/hooks/useBatchPreview.ts +++ b/apps/web/src/pages/generate/hooks/useBatchPreview.ts @@ -51,9 +51,10 @@ const POLL_TIMEOUT = 180_000 const MAX_NETWORK_RETRIES = 2 /** - * 对配置参数做指纹,用于检测配置是否变化(标题文字变化不触发重渲染,仅CSS浮层叠加) + * 对配置参数做指纹,用于检测配置是否变化(标题文字/样式变化不触发重渲染,仅CSS浮层叠加) */ function buildFingerprint(req: CreatePreviewRequest): string { + // 不含 titles/title_config:标题文字与样式由 CSS 浮层实时叠加,变化不触发重渲染 return JSON.stringify({ t: req.template_id, a: [...(req.asset_ids || [])].sort(), @@ -62,7 +63,6 @@ function buildFingerprint(req: CreatePreviewRequest): string { vs: req.voice_library_ids, pc: req.preview_count, b: req.bgm_config, - titles: req.titles, }) }