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}
)