diff --git a/components/agents/AgentPsdTemplateEditorDialog.tsx b/components/agents/AgentPsdTemplateEditorDialog.tsx index 812a9d605..e4e22e7fa 100644 --- a/components/agents/AgentPsdTemplateEditorDialog.tsx +++ b/components/agents/AgentPsdTemplateEditorDialog.tsx @@ -579,6 +579,7 @@ function AssignedPsdLayerPreviewImage({ width, quality, style, + onLoadComplete, }: { sourceUrl: string; alt: string; @@ -586,6 +587,7 @@ function AssignedPsdLayerPreviewImage({ width?: number; quality?: number; style?: CSSProperties; + onLoadComplete?: (sourceUrl: string) => void; }) { const fallbackUrls = useMemo( () => buildAssignedPsdLayerPreviewFallbackUrls(sourceUrl, { width, quality }), @@ -631,13 +633,48 @@ function AssignedPsdLayerPreviewImage({ current + 1 < fallbackUrls.length ? current + 1 : current ) } - onLoad={() => setHasLoaded(true)} + onLoad={() => { + setHasLoaded(true); + onLoadComplete?.(sourceUrl); + }} src={src} style={style} /> ); } +function PsdAssignedImageLoadingOverlay({ + pendingCount, + zIndex, +}: { + pendingCount: number; + zIndex: number; +}) { + return ( +
+
+
+
+
+
+ +
+
+ 正在套入商品图 +
+
+ {pendingCount > 1 + ? `正在加载 ${pendingCount} 个图片图层,完成后自动显示模板预览。` + : "原图加载中,完成后自动显示模板预览。"} +
+
+
+ ); +} + function getScrollAreaViewportElement(root: HTMLDivElement | null) { return ( (root?.querySelector( @@ -1851,6 +1888,9 @@ export function AgentPsdTemplateEditorDialog({ const [imageSizes, setImageSizes] = useState< Record >({}); + const [loadedAssignedPreviewUrls, setLoadedAssignedPreviewUrls] = useState< + Record + >({}); const [layerOrderIds, setLayerOrderIds] = useState([]); const [layerPositionOffsets, setLayerPositionOffsets] = useState< Record @@ -2255,6 +2295,33 @@ export function AgentPsdTemplateEditorDialog({ return nextAssignments; }, [editableImageLayers, imageAssignments, selectedTemplate, sourceImageUrl]); + const pendingAssignedPreviewLayerIds = useMemo( + () => + orderedVisiblePreviewLayers + .filter((layer): layer is AgentPsdTemplateImageLayerSlot => + isAgentPsdImageLayerSlot(layer) + ) + .map((layer) => { + const assignedUrl = String( + effectiveImageAssignments[layer.id] || "" + ).trim(); + if (!assignedUrl) { + return ""; + } + + return loadedAssignedPreviewUrls[layer.id] === assignedUrl + ? "" + : layer.id; + }) + .filter(Boolean), + [ + effectiveImageAssignments, + loadedAssignedPreviewUrls, + orderedVisiblePreviewLayers, + ] + ); + const assignedPreviewLoadingOverlayZIndex = + (orderedPreviewLayers.length + 12) * PSD_LAYER_Z_INDEX_STEP; const effectiveTextAssignments = useMemo(() => { const nextAssignments: Record = {}; @@ -2872,6 +2939,7 @@ export function AgentPsdTemplateEditorDialog({ setImageAssignments({}); setImagePlacements({}); setImageSizes({}); + setLoadedAssignedPreviewUrls({}); setTextAssignments({}); setTextColorAssignments({}); setTextPositionOffsets({}); @@ -2915,6 +2983,7 @@ export function AgentPsdTemplateEditorDialog({ setImageAssignments(initialState.imageAssignments); setImagePlacements(initialState.imagePlacements); setLayerOrderIds(initialState.layerOrderIds); + setLoadedAssignedPreviewUrls({}); setLayerPositionOffsets(initialState.layerPositionOffsets); setTextAssignments(initialState.textAssignments); setTextColorAssignments(initialState.textColorAssignments); @@ -3966,6 +4035,18 @@ export function AgentPsdTemplateEditorDialog({ .psd-layer-visibility-scroll::-webkit-scrollbar-thumb:hover { background: rgba(16, 185, 129, 0.75); } + .psd-assigned-preview-shimmer { + transform: translateX(-110%); + animation: psd-assigned-preview-shimmer 1.55s ease-in-out infinite; + } + @keyframes psd-assigned-preview-shimmer { + 0% { + transform: translateX(-110%); + } + 100% { + transform: translateX(210%); + } + } `, }} /> @@ -4398,6 +4479,22 @@ export function AgentPsdTemplateEditorDialog({ {assignedUrl ? ( { + setLoadedAssignedPreviewUrls( + (current) => { + if ( + current[layer.id] === loadedUrl + ) { + return current; + } + + return { + ...current, + [layer.id]: loadedUrl, + }; + } + ); + }} quality={74} sourceUrl={layerImageUrl} style={ @@ -4911,6 +5008,13 @@ export function AgentPsdTemplateEditorDialog({ ); })}
+ + {pendingAssignedPreviewLayerIds.length > 0 ? ( + + ) : null}
)