From 94841d4360c69317538c1535a43b0817035c8251 Mon Sep 17 00:00:00 2001 From: kdletters Date: Tue, 16 Jun 2026 16:39:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=BB=E5=B8=83=E5=9B=BE?= =?UTF-8?q?=E5=B1=82=E4=BF=9D=E5=AD=98=E4=BD=93=E7=A7=AF=E8=BF=87=E5=A4=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 画布布局不再写入图片base64正文 加载画布时从项目资源补回图层图片地址 补充大图素材保存布局回归测试 --- .../ImageCanvasEditorView.test.tsx | 43 +++++++++++++++++++ .../image-editor/ImageCanvasEditorView.tsx | 36 +++++++++++----- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/components/image-editor/ImageCanvasEditorView.test.tsx b/src/components/image-editor/ImageCanvasEditorView.test.tsx index 34da2091..7c966ef2 100644 --- a/src/components/image-editor/ImageCanvasEditorView.test.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.test.tsx @@ -1623,6 +1623,49 @@ describe('ImageCanvasEditorView', () => { expect(createEditorAssetMock).not.toHaveBeenCalled(); }); + it('saves canvas layout without embedding image payloads in layer snapshots', async () => { + loadEditorAssetLibraryMock.mockResolvedValueOnce({ + folders: [ + { + folderId: 'project', + label: '项目素材', + sortOrder: 0, + collapsed: false, + systemDefault: true, + }, + ], + assets: [ + { + assetId: 'asset-data-heavy', + folderId: 'project', + label: '大图素材', + imageSrc: 'data:image/png;base64,'.concat('a'.repeat(4000)), + width: 1024, + height: 768, + sourceType: 'uploaded', + }, + ], + }); + render(); + + await screen.findByRole('button', { name: '添加大图素材' }); + fireEvent.click(screen.getByRole('button', { name: '添加大图素材' })); + + await waitFor(() => { + expect(saveEditorProjectLayoutMock).toHaveBeenCalled(); + }); + const layoutCalls = saveEditorProjectLayoutMock.mock.calls; + const lastLayout = layoutCalls.at(-1)?.[1]; + + expect(lastLayout.layers).toEqual( + expect.arrayContaining([ + expect.not.objectContaining({ + src: expect.stringMatching(/^data:image/u), + }), + ]), + ); + }); + it('adds an asset library image to the canvas with pointer dragging', async () => { await renderLoadedEditor(); diff --git a/src/components/image-editor/ImageCanvasEditorView.tsx b/src/components/image-editor/ImageCanvasEditorView.tsx index 17e8059f..fce14e00 100644 --- a/src/components/image-editor/ImageCanvasEditorView.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.tsx @@ -539,7 +539,6 @@ function serializeLayer(layer: CanvasLayer): EditorProjectLayerSnapshot { layerId: layer.id, resourceId: layer.resourceId, title: layer.title, - src: layer.src, x: layer.x, y: layer.y, width: layer.width, @@ -565,10 +564,14 @@ function serializeLayer(layer: CanvasLayer): EditorProjectLayerSnapshot { }; } -function hydrateLayer(snapshot: EditorProjectLayerSnapshot): CanvasLayer | null { +function hydrateLayer( + snapshot: EditorProjectLayerSnapshot, + resourcesById: Map, +): CanvasLayer | null { const resourceId = typeof snapshot.resourceId === 'string' ? snapshot.resourceId : ''; const layerId = typeof snapshot.layerId === 'string' ? snapshot.layerId : ''; - const src = typeof snapshot.src === 'string' ? snapshot.src : ''; + const snapshotSrc = typeof snapshot.src === 'string' ? snapshot.src : ''; + const src = snapshotSrc || resourcesById.get(resourceId)?.imageSrc || ''; const title = typeof snapshot.title === 'string' ? snapshot.title : '画布图片'; if (!resourceId || !layerId || !src) { return null; @@ -1221,8 +1224,14 @@ export function ImageCanvasEditorView() { setProjectTitle(nextProjectTitle); setProjectRenameValue(nextProjectTitle); setViewport(project.viewport); + const resourcesById = new Map( + project.resources.map((resource) => [ + resource.resourceId, + { imageSrc: resource.imageSrc }, + ]), + ); const hydratedLayers = project.layers - .map(hydrateLayer) + .map((layer) => hydrateLayer(layer, resourcesById)) .filter((layer): layer is CanvasLayer => Boolean(layer)); layerCounterRef.current = hydratedLayers.length; setLayers(hydratedLayers); @@ -2099,11 +2108,19 @@ export function ImageCanvasEditorView() { } : currentLayer, ); - if (options.saveLayout) { - void saveProjectLayoutNow(nextLayers).catch(() => {}); - } return nextLayers; }); + if (options.saveLayout) { + const latestLayers = layersRef.current.map((currentLayer) => + currentLayer.id === layer.id + ? { + ...currentLayer, + resourceId: resource.resourceId, + } + : currentLayer, + ); + void saveProjectLayoutNow(latestLayers).catch(() => {}); + } return resource; }) .catch(() => { @@ -2228,9 +2245,7 @@ export function ImageCanvasEditorView() { ); captureCanvasHistory(); setLayers((currentLayers) => { - const nextLayers = [...currentLayers, nextLayer]; - void saveProjectLayoutNow(nextLayers).catch(() => {}); - return nextLayers; + return [...currentLayers, nextLayer]; }); selectSingleLayer(nextLayer.id); setHoveredLayerId(null); @@ -2853,7 +2868,6 @@ export function ImageCanvasEditorView() { } : currentLayer, ); - void saveProjectLayoutNow(nextLayers).catch(() => {}); return nextLayers; }); }