From aa4fbdd158db56d89e36d0b0dc134cdf46db015e Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 24 Jan 2026 21:15:28 +0800 Subject: [PATCH] feat: show processing state for image operations in canvas - Add toolbar above selected image for quick access to editing tools - Create new node with 'generating' status before processing - Update node status to 'completed' or 'failed' after processing - Allow overflow visible for toolbar to display outside node bounds --- components/canvas/CanvasWorkspace.tsx | 2 + components/canvas/nodes/ImageNode.tsx | 182 ++++++++++++++++++++++---- 2 files changed, 160 insertions(+), 24 deletions(-) diff --git a/components/canvas/CanvasWorkspace.tsx b/components/canvas/CanvasWorkspace.tsx index 6ba597bb7..57c23625b 100644 --- a/components/canvas/CanvasWorkspace.tsx +++ b/components/canvas/CanvasWorkspace.tsx @@ -182,6 +182,7 @@ export function CanvasWorkspace() { "absolute", isSelected && "ring-2 ring-primary ring-offset-2" )} + style={{ overflow: 'visible' }} // 允许工具栏显示在节点外部 onClick={(e: React.MouseEvent) => { e.stopPropagation(); selectNode(node.id, e.shiftKey); @@ -191,6 +192,7 @@ export function CanvasWorkspace() { initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} className="w-full h-full" + style={{ overflow: 'visible' }} // 允许工具栏显示在节点外部 > {node.type === 'image' && ( diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index ce2af1e87..ebfb02fd1 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -101,6 +101,19 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { e.target.value = ''; }; + // 获取操作的中文名称 + const getActionName = (action: string): string => { + const names: Record = { + 'rmbg': '抠图', + 'enhance': '变清晰', + 'extend': '图片扩展', + 'resize': '尺寸调整', + 'angle': '角度编辑', + 'scene-fusion': '场景裂变', + }; + return names[action] || action; + }; + // 处理图片编辑操作 - 集成我们现有的 API const handleAction = async (action: string) => { if (!data.src) { @@ -122,7 +135,25 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { return; } - setIsProcessing(true); + const actionName = getActionName(action); + + // 先创建一个"处理中"状态的节点,显示在原图右侧 + const newNodeId = addNode({ + type: 'image', + position: { + x: node.position.x + node.size.width + 20, + y: node.position.y + }, + size: { width: node.size.width, height: node.size.height }, + data: { + src: '', // 暂时没有图片 + status: 'generating', + prompt: `${actionName}处理中...`, + originalSrc: data.src, + }, + connections: [node.id], + }); + let result; try { @@ -137,37 +168,52 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { result = await imageGenerationService.extendImage(data.src); break; default: - setIsProcessing(false); + // 删除创建的节点 + if (newNodeId) deleteNode(newNodeId); return; } if (result?.success && result.data?.url) { - // 创建新的图片节点显示处理结果 - addNode({ - type: 'image', - position: { - x: node.position.x + node.size.width + 20, - y: node.position.y - }, - size: { width: node.size.width, height: node.size.height }, - data: { - src: result.data.url, - status: 'completed', - prompt: `${action} 处理结果`, - originalSrc: data.src, - }, - connections: [node.id], - }); - - toast.success(`${action} 处理完成`); + // 更新节点为完成状态 + if (newNodeId) { + updateNode(newNodeId, { + data: { + src: result.data.url, + status: 'completed', + prompt: `${actionName}结果`, + originalSrc: data.src, + }, + }); + } + toast.success(`${actionName}处理完成`); } else { + // 更新节点为失败状态 + if (newNodeId) { + updateNode(newNodeId, { + data: { + src: '', + status: 'failed', + prompt: `${actionName}失败`, + originalSrc: data.src, + }, + }); + } toast.error(result?.error || "处理失败"); } } catch (error) { console.error(`${action} 处理失败:`, error); + // 更新节点为失败状态 + if (newNodeId) { + updateNode(newNodeId, { + data: { + src: '', + status: 'failed', + prompt: `${actionName}失败`, + originalSrc: data.src, + }, + }); + } toast.error("处理失败,请重试"); - } finally { - setIsProcessing(false); } }; @@ -262,8 +308,96 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { )} - {/* 悬浮操作栏 */} - {isHovered && data.src && !isProcessing && ( + {/* 选中时显示的顶部工具栏 - 和聊天窗口中的功能一致 */} + {isSelected && data.src && !isProcessing && ( +
e.stopPropagation()} + > + + + + + + +
+ + +
+ )} + + {/* 悬浮时的更多操作按钮 (右上角) */} + {isHovered && data.src && !isProcessing && !isSelected && (