fix: 上传图片立即添加到画布
1. 图片上传成功后立即显示在画布中 2. 同时触发自动命名和设置缩略图 3. 移除发送时重复添加图片的逻辑 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
5e642165df
commit
e0366c43fd
@ -259,7 +259,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
|||||||
// 标记是否已经为当前画布设置过标题(避免重复调用)
|
// 标记是否已经为当前画布设置过标题(避免重复调用)
|
||||||
const hasSetCanvasTitle = useRef(false);
|
const hasSetCanvasTitle = useRef(false);
|
||||||
|
|
||||||
// 处理发送
|
// 处理发送(图片已在上传时添加到画布,这里只发送消息)
|
||||||
const handleSend = useCallback(async () => {
|
const handleSend = useCallback(async () => {
|
||||||
if (!input.trim() && attachments.length === 0) return;
|
if (!input.trim() && attachments.length === 0) return;
|
||||||
if (!selectedModelId) {
|
if (!selectedModelId) {
|
||||||
@ -267,40 +267,17 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取第一张图片的 URL(用于自动命名和缩略图)
|
|
||||||
const firstImageAttachment = attachments.find(a => a.contentType?.startsWith('image/'));
|
|
||||||
|
|
||||||
// 构建 parts 数组:文件在前,文本在后(与 multimodal-input 一致)
|
// 构建 parts 数组:文件在前,文本在后(与 multimodal-input 一致)
|
||||||
const parts: any[] = [];
|
const parts: any[] = [];
|
||||||
|
|
||||||
// 添加文件部分(作为 file 类型的 part)
|
// 添加文件部分(作为 file 类型的 part)
|
||||||
// 同时将用户上传的图片添加到画布
|
attachments.forEach((a) => {
|
||||||
attachments.forEach((a, index) => {
|
|
||||||
parts.push({
|
parts.push({
|
||||||
type: "file" as const,
|
type: "file" as const,
|
||||||
mediaType: a.contentType,
|
mediaType: a.contentType,
|
||||||
name: a.name,
|
name: a.name,
|
||||||
url: a.url,
|
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,
|
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("");
|
setInput("");
|
||||||
setAttachments([]);
|
setAttachments([]);
|
||||||
}, [input, attachments, selectedModelId, sendMessage, addNode, nodes.length, chatId]);
|
}, [input, attachments, selectedModelId, sendMessage]);
|
||||||
|
|
||||||
// 处理文件选择
|
// 处理文件选择 - 上传后立即添加到画布
|
||||||
const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
if (!files) return;
|
if (!files) return;
|
||||||
|
|
||||||
|
let firstImageUrl: string | null = null;
|
||||||
|
|
||||||
for (const file of Array.from(files)) {
|
for (const file of Array.from(files)) {
|
||||||
if (!file.type.startsWith('image/')) {
|
if (!file.type.startsWith('image/')) {
|
||||||
toast.error('请选择图片文件');
|
toast.error('请选择图片文件');
|
||||||
@ -395,17 +329,86 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
const imageUrl = data.url;
|
||||||
|
|
||||||
|
// 记录第一张图片的 URL(用于自动命名和缩略图)
|
||||||
|
if (!firstImageUrl) {
|
||||||
|
firstImageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到附件列表
|
||||||
setAttachments(prev => [...prev, {
|
setAttachments(prev => [...prev, {
|
||||||
url: data.url,
|
url: imageUrl,
|
||||||
name: file.name,
|
name: file.name,
|
||||||
contentType: file.type,
|
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) {
|
} catch (error) {
|
||||||
toast.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 = '';
|
e.target.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user