From 8853bfae77440ea98910b20ca8149e18d1d364cd Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Apr 2026 21:52:25 +0800 Subject: [PATCH] fix: harden agent PSD template dialog --- .../agents/AgentGenerationWorkspaceDarkV2.tsx | 58 +++++++++++--- .../agents/AgentPsdTemplateEditorDialog.tsx | 75 ++++++++++++------- components/ui/client-error-boundary.tsx | 24 ++++++ 3 files changed, 118 insertions(+), 39 deletions(-) diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index 4315dd0ed..b5a9e7bc1 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -33,11 +33,19 @@ import { } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; +import { ClientErrorBoundary } from "@/components/ui/client-error-boundary"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { @@ -5823,16 +5831,46 @@ export function AgentGenerationWorkspaceDarkV2({ /> ) : null} - { - if (!open) { - setPsdTemplateDialogSourceUrl(null); - } - }} - sourceImageUrl={psdTemplateDialogSourceUrl} - brandName={workflowContext?.brand || null} - /> + { + if (!open) { + setPsdTemplateDialogSourceUrl(null); + } + }} + > + + + PSD 模板暂时无法打开 + + 已拦截本次客户端异常,避免整个智能体工作台被一起打断。 + + +
+

请关闭弹窗后重试一次。

+

如果仍然出现,优先检查当前品牌下的 PSD 模板配置、图层数据或字体资源是否完整。

+
+
+ + } + > + { + if (!open) { + setPsdTemplateDialogSourceUrl(null); + } + }} + sourceImageUrl={psdTemplateDialogSourceUrl} + brandName={workflowContext?.brand || null} + /> +
{/* Fullscreen Preview */} diff --git a/components/agents/AgentPsdTemplateEditorDialog.tsx b/components/agents/AgentPsdTemplateEditorDialog.tsx index 4c3f23cfe..0e00fabc9 100644 --- a/components/agents/AgentPsdTemplateEditorDialog.tsx +++ b/components/agents/AgentPsdTemplateEditorDialog.tsx @@ -455,6 +455,7 @@ export function AgentPsdTemplateEditorDialog({ : getDefaultAgentPsdImagePlacement(); const fontFaceCss = useMemo(() => buildPreviewFontFaces(selectedTemplate), [selectedTemplate]); const selectedTemplateSelectValue = selectedTemplate?.id; + const hasTemplateOptions = templates.length > 0; useEffect(() => { if (!open) return; @@ -481,24 +482,30 @@ export function AgentPsdTemplateEditorDialog({ setTextAssignments(initialState.textAssignments); setSelectedLayerId(getPreferredSelectedLayerId(selectedTemplate)); - // 自动适配缩放比例(延迟执行确保 DOM 已渲染) - if (selectedTemplate) { - setTimeout(() => { - if (previewViewportRef.current) { - const viewport = previewViewportRef.current; - const padding = 32; - const availableWidth = viewport.clientWidth - padding; - const availableHeight = viewport.clientHeight - padding; - - if (availableWidth > 0 && availableHeight > 0) { - const scaleX = availableWidth / selectedTemplate.width; - const scaleY = availableHeight / selectedTemplate.height; - const fitScale = Math.min(scaleX, scaleY, 1); - setStageScale(fitScale); - } - } - }, 100); + if (!selectedTemplate || typeof window === "undefined") { + return; } + + // 自动适配缩放比例(延迟执行确保 DOM 已渲染) + const timeoutId = window.setTimeout(() => { + if (previewViewportRef.current) { + const viewport = previewViewportRef.current; + const padding = 32; + const availableWidth = viewport.clientWidth - padding; + const availableHeight = viewport.clientHeight - padding; + + if (availableWidth > 0 && availableHeight > 0) { + const scaleX = availableWidth / selectedTemplate.width; + const scaleY = availableHeight / selectedTemplate.height; + const fitScale = Math.min(scaleX, scaleY, 1); + setStageScale(fitScale); + } + } + }, 100); + + return () => { + window.clearTimeout(timeoutId); + }; }, [open, selectedTemplate, sourceImageUrl]); useEffect(() => { @@ -844,18 +851,28 @@ export function AgentPsdTemplateEditorDialog({
- + {hasTemplateOptions ? ( + + ) : ( +
+ {isLoading + ? "正在加载可用模板..." + : normalizedBrandName + ? `当前品牌“${normalizedBrandName}”下暂无可用模板` + : "请先在工作流中选择品牌后再打开 PSD 模板。"} +
+ )}
{selectedTemplate ? ( diff --git a/components/ui/client-error-boundary.tsx b/components/ui/client-error-boundary.tsx index b4dd2f632..ab46948bf 100644 --- a/components/ui/client-error-boundary.tsx +++ b/components/ui/client-error-boundary.tsx @@ -5,12 +5,27 @@ import React from "react"; interface ClientErrorBoundaryProps { children: React.ReactNode; fallback: React.ReactNode; + resetKeys?: unknown[]; } interface ClientErrorBoundaryState { hasError: boolean; } +function areResetKeysEqual(nextKeys?: unknown[], prevKeys?: unknown[]) { + if (nextKeys === prevKeys) { + return true; + } + if (!nextKeys || !prevKeys) { + return false; + } + if (nextKeys.length !== prevKeys.length) { + return false; + } + + return nextKeys.every((key, index) => Object.is(key, prevKeys[index])); +} + export class ClientErrorBoundary extends React.Component< ClientErrorBoundaryProps, ClientErrorBoundaryState @@ -24,6 +39,15 @@ export class ClientErrorBoundary extends React.Component< return { hasError: true }; } + componentDidUpdate(prevProps: ClientErrorBoundaryProps) { + if ( + this.state.hasError && + !areResetKeysEqual(this.props.resetKeys, prevProps.resetKeys) + ) { + this.setState({ hasError: false }); + } + } + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { console.error("[ClientErrorBoundary] Caught client error:", error, errorInfo); }