From e0366c43fda54f70eda02a0df634a8ab129da363 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 1 Feb 2026 12:45:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=8A=E4=BC=A0=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E7=AB=8B=E5=8D=B3=E6=B7=BB=E5=8A=A0=E5=88=B0=E7=94=BB=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 图片上传成功后立即显示在画布中 2. 同时触发自动命名和设置缩略图 3. 移除发送时重复添加图片的逻辑 Co-authored-by: Cursor --- components/canvas/CanvasChatPanel.tsx | 149 +++++++++++++------------- 1 file changed, 76 insertions(+), 73 deletions(-) diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index f974dc3..b2a9b76 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -259,7 +259,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF // 标记是否已经为当前画布设置过标题(避免重复调用) const hasSetCanvasTitle = useRef(false); - // 处理发送 + // 处理发送(图片已在上传时添加到画布,这里只发送消息) const handleSend = useCallback(async () => { if (!input.trim() && attachments.length === 0) return; if (!selectedModelId) { @@ -267,40 +267,17 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF return; } - // 获取第一张图片的 URL(用于自动命名和缩略图) - const firstImageAttachment = attachments.find(a => a.contentType?.startsWith('image/')); - // 构建 parts 数组:文件在前,文本在后(与 multimodal-input 一致) const parts: any[] = []; // 添加文件部分(作为 file 类型的 part) - // 同时将用户上传的图片添加到画布 - attachments.forEach((a, index) => { + attachments.forEach((a) => { parts.push({ type: "file" as const, mediaType: a.contentType, name: a.name, url: a.url, }); - - // 将用户上传的图片添加到画布 - if (a.contentType?.startsWith('image/')) { - const existingNodes = nodes.length; - const x = 100 + ((existingNodes + index) % 4) * 320; - const y = 100 + Math.floor((existingNodes + index) / 4) * 320; - - addNode({ - type: 'image', - position: { x, y }, - size: { width: 300, height: 300 }, - data: { - src: a.url, - status: 'completed', - prompt: '用户上传', - }, - connections: [], - }); - } }); // 添加文本部分 @@ -315,60 +292,17 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF parts: parts, }); - if (attachments.length > 0) { - toast.success(`已将 ${attachments.length} 张图片添加到画布`); - } - - // 如果是第一次上传图片,自动识别并设置画布标题和缩略图 - if (firstImageAttachment && !hasSetCanvasTitle.current) { - hasSetCanvasTitle.current = true; - - // 异步调用 AI 识别图片主体并更新画布 - (async () => { - try { - // 调用 auto-title API 识别图片主体 - const response = await fetch('/api/canvas/auto-title', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ imageUrl: firstImageAttachment.url }), - }); - - if (response.ok) { - const data = await response.json(); - const title = data.title || '未命名画布'; - - // 提取纯 UUID(去掉 canvas- 前缀) - const rawId = chatId.startsWith('canvas-') - ? chatId.replace('canvas-', '') - : chatId; - - // 更新画布标题和缩略图 - await fetch(`/api/canvas/${rawId}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - title: title, - thumbnail: firstImageAttachment.url, - }), - }); - - console.log('[CanvasChat] 画布已自动命名为:', title); - } - } catch (error) { - console.error('[CanvasChat] 自动命名失败:', error); - } - })(); - } - setInput(""); setAttachments([]); - }, [input, attachments, selectedModelId, sendMessage, addNode, nodes.length, chatId]); + }, [input, attachments, selectedModelId, sendMessage]); - // 处理文件选择 + // 处理文件选择 - 上传后立即添加到画布 const handleFileSelect = async (e: React.ChangeEvent) => { const files = e.target.files; if (!files) return; + let firstImageUrl: string | null = null; + for (const file of Array.from(files)) { if (!file.type.startsWith('image/')) { toast.error('请选择图片文件'); @@ -395,17 +329,86 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF } const data = await response.json(); + const imageUrl = data.url; + // 记录第一张图片的 URL(用于自动命名和缩略图) + if (!firstImageUrl) { + firstImageUrl = imageUrl; + } + + // 添加到附件列表 setAttachments(prev => [...prev, { - url: data.url, + url: imageUrl, name: file.name, contentType: file.type, }]); + + // 立即将图片添加到画布 + const existingNodes = nodes.length; + const x = 100 + (existingNodes % 4) * 320; + const y = 100 + Math.floor(existingNodes / 4) * 320; + + addNode({ + type: 'image', + position: { x, y }, + size: { width: 300, height: 300 }, + data: { + src: imageUrl, + status: 'completed', + prompt: '用户上传', + }, + connections: [], + }); + + toast.success('图片已添加到画布'); + } catch (error) { toast.error('图片上传失败'); } } + // 如果是第一次上传图片,自动识别并设置画布标题和缩略图 + if (firstImageUrl && !hasSetCanvasTitle.current) { + hasSetCanvasTitle.current = true; + + // 异步调用 AI 识别图片主体并更新画布 + (async () => { + try { + // 调用 auto-title API 识别图片主体 + const response = await fetch('/api/canvas/auto-title', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ imageUrl: firstImageUrl }), + }); + + if (response.ok) { + const data = await response.json(); + const title = data.title || '未命名画布'; + + // 提取纯 UUID(去掉 canvas- 前缀) + const rawId = chatId.startsWith('canvas-') + ? chatId.replace('canvas-', '') + : chatId; + + // 更新画布标题和缩略图 + await fetch(`/api/canvas/${rawId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + title: title, + thumbnail: firstImageUrl, + }), + }); + + console.log('[CanvasChat] 画布已自动命名为:', title); + toast.success(`画布已命名为: ${title}`); + } + } catch (error) { + console.error('[CanvasChat] 自动命名失败:', error); + } + })(); + } + e.target.value = ''; };