From 8c8b1794e454e41a6a304ba01ccf64fce3794d27 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 10 Apr 2026 21:16:57 +0800 Subject: [PATCH] fix: stabilize psd template admin and export render --- .../psd-templates-settings-client.tsx | 65 +++++++++++++++++-- lib/agents/psd-template-renderer.ts | 50 -------------- 2 files changed, 60 insertions(+), 55 deletions(-) diff --git a/app/(admin)/settings/agent-workflow/psd-templates/psd-templates-settings-client.tsx b/app/(admin)/settings/agent-workflow/psd-templates/psd-templates-settings-client.tsx index b365ac17b..eec56f474 100644 --- a/app/(admin)/settings/agent-workflow/psd-templates/psd-templates-settings-client.tsx +++ b/app/(admin)/settings/agent-workflow/psd-templates/psd-templates-settings-client.tsx @@ -66,6 +66,26 @@ import type { AgentWorkflowUiConfig } from "@/types/agent"; const fetcher = (url: string) => fetch(url, { cache: "no-store" }).then((res) => res.json()); +async function fetchPsdTemplateList(url: string): Promise { + const response = await fetch(url, { cache: "no-store" }); + const payload = (await response + .json() + .catch(() => null)) as Partial | null; + + if ( + !response.ok || + !payload || + payload.success !== true || + !Array.isArray(payload.templates) + ) { + throw new Error( + String(payload?.error || "").trim() || "PSD 模板读取失败" + ); + } + + return payload as AgentPsdTemplateListResponse; +} + const SYSTEM_FONT_OPTIONS = [ { id: "__system_sans", label: "系统无衬线", family: "sans-serif" }, { id: "__system_serif", label: "系统衬线", family: "serif" }, @@ -227,9 +247,19 @@ export function PsdTemplatesSettingsClient({ backHref: string; backLabel: string; }) { - const { data, mutate, isLoading } = useSWR( + const { + data, + error: templatesRequestError, + mutate, + isLoading, + } = useSWR( "/api/admin/psd-templates", - fetcher + fetchPsdTemplateList, + { + keepPreviousData: true, + revalidateOnFocus: false, + dedupingInterval: 10_000, + } ); const { data: siteSettingsData, mutate: mutateSiteSettings } = useSWR<{ agentWorkflowUiConfig?: AgentWorkflowUiConfig; @@ -244,11 +274,15 @@ export function PsdTemplatesSettingsClient({ const templates = useMemo(() => (data?.success ? data.templates : []), [data]); const templateListErrorMessage = useMemo(() => { + if (templatesRequestError instanceof Error) { + return templatesRequestError.message.trim() || "PSD 模板读取失败"; + } + if (!data || data.success) { return ""; } return String(data.error || "").trim() || "PSD 模板读取失败"; - }, [data]); + }, [data, templatesRequestError]); const fonts = useMemo(() => (fontsData?.success ? fontsData.fonts : []), [fontsData]); const brandOptions = useMemo( () => @@ -437,6 +471,10 @@ export function PsdTemplatesSettingsClient({ ); useEffect(() => { + if (templateListErrorMessage) { + return; + } + if (!templates.length) { setSelectedTemplateId(null); return; @@ -445,7 +483,7 @@ export function PsdTemplatesSettingsClient({ if (!selectedTemplateId || !templates.some((template) => template.id === selectedTemplateId)) { setSelectedTemplateId(templates[0].id); } - }, [selectedTemplateId, templates]); + }, [selectedTemplateId, templateListErrorMessage, templates]); useEffect(() => { if (!selectedTemplate) { @@ -1721,7 +1759,24 @@ export function PsdTemplatesSettingsClient({ 按品牌查看模板,选择一个模板继续配置槽位。 - {isLoading ? ( + {templateListErrorMessage && templates.length > 0 ? ( +
+ +
+
{`${templateListErrorMessage},当前先显示上次成功加载的模板列表。`}
+ +
+
+ ) : null} + {isLoading && templates.length === 0 ? (
{Array.from({ length: 3 }).map((_, index) => (
(); const fontMap = new Map(fonts.map((font) => [font.id, font])); - const shouldUseDedicatedRenderBase = hasDedicatedRenderBase(template); async function readCachedBuffer(url: string, label = "PSD 模板资源") { const normalizedUrl = String(url || "").trim(); @@ -350,7 +340,6 @@ export async function renderAgentPsdTemplate(params: { } const compositeInputs: Array<{ input: Buffer; left: number; top: number }> = []; - let didAppendDedicatedRenderBase = false; async function appendBufferOverlay(params: { input: Buffer; @@ -408,47 +397,8 @@ export async function renderAgentPsdTemplate(params: { } } - if (shouldUseDedicatedRenderBase && template.renderBaseUrl) { - try { - const renderBaseBuffer = await readCachedBuffer( - template.renderBaseUrl, - "PSD 模板底图" - ); - const normalizedRenderBaseBuffer = await sharp(renderBaseBuffer) - .rotate() - .resize({ - width: canvasWidth, - height: canvasHeight, - fit: "fill", - background: { - r: 0, - g: 0, - b: 0, - alpha: 0, - }, - }) - .png() - .toBuffer(); - didAppendDedicatedRenderBase = await appendBufferOverlay({ - input: normalizedRenderBaseBuffer, - left: 0, - top: 0, - width: canvasWidth, - height: canvasHeight, - }); - } catch (error) { - console.warn("[PSD Template] failed to load dedicated render base during render:", { - templateId: template.id, - error, - }); - } - } - for (const layer of [...template.layers].reverse()) { if (!layer.isEditable) { - if (didAppendDedicatedRenderBase) { - continue; - } await appendStoredLayer(layer); continue; }