diff --git a/src/features/ecommerce/EcommercePage.tsx b/src/features/ecommerce/EcommercePage.tsx index 812db3b..e5e99b3 100644 --- a/src/features/ecommerce/EcommercePage.tsx +++ b/src/features/ecommerce/EcommercePage.tsx @@ -4,6 +4,7 @@ CloudUploadOutlined, CloseOutlined, DeleteOutlined, + DownloadOutlined, EditOutlined, FireOutlined, FileImageOutlined, @@ -38,6 +39,7 @@ import EcommerceSetPanel from "./panels/EcommerceSetPanel"; import EcommerceTryOnPanel from "./panels/EcommerceTryOnPanel"; import EcommerceClonePanel from "./panels/EcommerceClonePanel"; import { ecommerceOssScopes, saveUnifiedEcommerceGenerationRecord, deleteEcommerceGenerationRecord } from "./ecommerceGenerationPersistence"; +import { downloadResultAsset } from "../workbench/workbenchDownload"; const smartCutoutColorPresets = [ "#ffffff", @@ -349,6 +351,14 @@ interface EcommerceHistoryRecord { replicateLevel: CloneReplicateLevelKey; } +interface ProductSetPreviewSelection { + src: string; + label: string; + nodeId?: string; + cardId?: string; + removable?: boolean; +} + interface EcommerceImagePromptOptions { gender?: string; age?: string; @@ -1377,7 +1387,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { const [productSetStatus, setProductSetStatus] = useState("idle"); const [productSetResultImages, setProductSetResultImages] = useState([]); const [isSetUploadDragging, setIsSetUploadDragging] = useState(false); - const [selectedProductSetPreview, setSelectedProductSetPreview] = useState<{ src: string; label: string } | null>(null); + const [selectedProductSetPreview, setSelectedProductSetPreview] = useState(null); const [showHostingModal, setShowHostingModal] = useState(false); const [productImages, setProductImages] = useState([]); const [activeQuickTool, setActiveQuickTool] = useState<"cutout" | "detail" | "watermark" | "image-edit" | "translate" | null>(null); @@ -3836,8 +3846,39 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { lastFailedActionRef.current = () => handleSetGenerate(); }; - const openProductSetPreview = (card: { src: string; label: string }) => { - setSelectedProductSetPreview(card); + const openProductSetPreview = (card: { id?: string; src: string; label: string }, options?: { nodeId?: string; removable?: boolean }) => { + setSelectedProductSetPreview({ + src: card.src, + label: card.label, + cardId: card.id, + nodeId: options?.nodeId, + removable: Boolean(options?.removable && options.nodeId && card.id), + }); + }; + + const handleDownloadCanvasResult = async (card: { src: string; label: string }) => { + try { + await downloadResultAsset(card.src, card.label || "generated-image", false); + toast.success("已开始下载图片"); + } catch (error) { + toast.error(error instanceof Error ? error.message : "下载图片失败"); + } + }; + + const removeCanvasResult = (nodeId: string, cardId: string) => { + setCanvasNodes((current) => + current + .map((node) => (node.id === nodeId ? { ...node, results: node.results.filter((card) => card.id !== cardId) } : node)) + .filter((node) => node.sourceImage || node.results.length > 0), + ); + setResults((current) => current.filter((card) => card.id !== cardId)); + toast.success("已从当前视图移除"); + }; + + const removeSelectedProductSetPreview = (preview: ProductSetPreviewSelection) => { + if (!preview.nodeId || !preview.cardId) return; + removeCanvasResult(preview.nodeId, preview.cardId); + setSelectedProductSetPreview(null); }; const handleDetailAiWrite = () => { @@ -4141,24 +4182,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { setPreviewOffset({ x: 0, y: 0 }); previewOffsetRef.current = { x: 0, y: 0 }; - - requestAnimationFrame(() => requestAnimationFrame(() => { - const container = previewSurfaceRef.current; - if (!container) return; - const containerRect = container.getBoundingClientRect(); - const node = container.querySelector(".clone-ai-canvas-node") as HTMLElement | null; - if (!node) return; - const nodeRect = node.getBoundingClientRect(); - const nodeCenterY = nodeRect.top + nodeRect.height / 2 - containerRect.top; - const visibleCenterY = containerRect.height / 2; - const offsetY = visibleCenterY - nodeCenterY; - const nodeCenterX = nodeRect.left + nodeRect.width / 2 - containerRect.left; - const visibleCenterX = containerRect.width / 2; - const offsetX = visibleCenterX - nodeCenterX; - const finalOffset = { x: offsetX, y: offsetY }; - setPreviewOffset(finalOffset); - previewOffsetRef.current = finalOffset; - })); }; const handleNewEcommerceConversation = () => { @@ -4982,7 +5005,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {