fix: harden agent PSD template dialog
This commit is contained in:
parent
d02416d1b2
commit
8853bfae77
@ -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}
|
||||
|
||||
<AgentPsdTemplateEditorDialog
|
||||
open={Boolean(psdTemplateDialogSourceUrl)}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setPsdTemplateDialogSourceUrl(null);
|
||||
}
|
||||
}}
|
||||
sourceImageUrl={psdTemplateDialogSourceUrl}
|
||||
brandName={workflowContext?.brand || null}
|
||||
/>
|
||||
<ClientErrorBoundary
|
||||
resetKeys={[psdTemplateDialogSourceUrl || "", workflowContext?.brand || ""]}
|
||||
fallback={
|
||||
<Dialog
|
||||
open={Boolean(psdTemplateDialogSourceUrl)}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setPsdTemplateDialogSourceUrl(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
overlayClassName="bg-black/85 backdrop-blur-sm"
|
||||
className="max-w-lg border border-white/10 bg-[#09090b] text-white"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>PSD 模板暂时无法打开</DialogTitle>
|
||||
<DialogDescription>
|
||||
已拦截本次客户端异常,避免整个智能体工作台被一起打断。
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3 text-sm leading-6 text-white/72">
|
||||
<p>请关闭弹窗后重试一次。</p>
|
||||
<p>如果仍然出现,优先检查当前品牌下的 PSD 模板配置、图层数据或字体资源是否完整。</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
}
|
||||
>
|
||||
<AgentPsdTemplateEditorDialog
|
||||
open={Boolean(psdTemplateDialogSourceUrl)}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setPsdTemplateDialogSourceUrl(null);
|
||||
}
|
||||
}}
|
||||
sourceImageUrl={psdTemplateDialogSourceUrl}
|
||||
brandName={workflowContext?.brand || null}
|
||||
/>
|
||||
</ClientErrorBoundary>
|
||||
|
||||
{/* Fullscreen Preview */}
|
||||
<AnimatePresence>
|
||||
|
||||
@ -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({
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>选择模板</Label>
|
||||
<Select value={selectedTemplateSelectValue} onValueChange={setSelectedTemplateId}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={isLoading ? "加载模板中..." : "请选择模板"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{templates.map((template) => (
|
||||
<SelectItem key={template.id} value={template.id}>
|
||||
{template.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{hasTemplateOptions ? (
|
||||
<Select value={selectedTemplateSelectValue} onValueChange={setSelectedTemplateId}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={isLoading ? "加载模板中..." : "请选择模板"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{templates.map((template) => (
|
||||
<SelectItem key={template.id} value={template.id}>
|
||||
{template.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
<div className="rounded-xl border border-dashed border-white/15 bg-white/[0.02] px-3 py-2 text-sm text-muted-foreground">
|
||||
{isLoading
|
||||
? "正在加载可用模板..."
|
||||
: normalizedBrandName
|
||||
? `当前品牌“${normalizedBrandName}”下暂无可用模板`
|
||||
: "请先在工作流中选择品牌后再打开 PSD 模板。"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{selectedTemplate ? (
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user