fix: 上传图片立即添加到画布

1. 图片上传成功后立即显示在画布中
2. 同时触发自动命名和设置缩略图
3. 移除发送时重复添加图片的逻辑

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name 2026-02-01 12:45:41 +08:00
parent 5e642165df
commit e0366c43fd

View File

@ -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<HTMLInputElement>) => {
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 = '';
};