feat: 画布图片添加到对话框改为手动触发
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

- 在图片工具栏添加"添加到对话"按钮
- 移除选中图片时自动添加标签的逻辑
- 通过 canvas store 的 pendingChatAttachments 实现跨组件通信
- 用户点击按钮后才会将图片添加到对话框,避免标签越来越多
This commit is contained in:
Your Name 2026-02-02 10:54:03 +08:00
parent ba99ad212e
commit 52867404f7
3 changed files with 63 additions and 14 deletions

View File

@ -108,6 +108,8 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
const nodes = useCanvasStore((state) => state.nodes);
const selectedNodeIds = useCanvasStore((state) => state.selectedNodeIds);
const clearSelection = useCanvasStore((state) => state.clearSelection);
const pendingChatAttachments = useCanvasStore((state) => state.pendingChatAttachments);
const clearPendingChatAttachments = useCanvasStore((state) => state.clearPendingChatAttachments);
// 获取纯 UUID(去掉 canvas- 前缀)用于 API 调用
const rawChatId = chatId.startsWith('canvas-')
@ -159,33 +161,30 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
}
}, []);
// 当选中画布中的图片时,自动添加到附件并插入标签
// 🆕 监听 pendingChatAttachments,当用户点击"添加到对话"按钮时添加标签
useEffect(() => {
if (selectedImageNodes.length > 0) {
const newAttachments: Attachment[] = selectedImageNodes.map(node => {
const data = node.data as any;
return {
url: data.src,
name: data.prompt || '画布图片',
contentType: 'image/jpeg',
};
});
if (pendingChatAttachments.length > 0) {
// 只添加还没在附件中的图片
setAttachments(prev => {
const existingUrls = new Set(prev.map(a => a.url));
const toAdd = newAttachments.filter(a => !existingUrls.has(a.url));
const toAdd = pendingChatAttachments.filter(a => !existingUrls.has(a.url));
if (toAdd.length > 0) {
// 插入图片标签到编辑器
toAdd.forEach((att, i) => {
insertImageTagInEditor(att.url, att.name, prev.length + i);
});
// 聚焦到输入框
setTimeout(() => {
(textareaRef.current as HTMLDivElement | null)?.focus();
}, 0);
return [...prev, ...toAdd];
}
return prev;
});
// 清空待添加列表
clearPendingChatAttachments();
}
}, [selectedImageNodes.length, selectedNodeIds, insertImageTagInEditor]);
}, [pendingChatAttachments, insertImageTagInEditor, clearPendingChatAttachments]);
// 获取模型列表
const { data: modelsData } = useSWR<{

View File

@ -2,7 +2,7 @@
import React, { useState, useRef, useEffect } from "react";
import { createPortal } from "react-dom";
import { ImagePlus, Loader2, AlertCircle, MoreVertical, Scissors, Sparkles, Expand, Maximize2, Move3D, Layers, Download, Trash2, RefreshCw, Pencil } from "lucide-react";
import { ImagePlus, Loader2, AlertCircle, MoreVertical, Scissors, Sparkles, Expand, Maximize2, Move3D, Layers, Download, Trash2, RefreshCw, Pencil, MessageSquarePlus } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
@ -53,6 +53,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
const updateNode = useCanvasStore((state) => state.updateNode);
const deleteNode = useCanvasStore((state) => state.deleteNode);
const addNode = useCanvasStore((state) => state.addNode);
const addPendingChatAttachment = useCanvasStore((state) => state.addPendingChatAttachment);
// 获取工具栏位置 - 在渲染时直接计算
const getToolbarPosition = () => {
@ -572,6 +573,28 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
}}
onClick={(e) => e.stopPropagation()}
>
{/* 🆕 添加到对话 */}
<Button
variant="ghost"
size="sm"
className="h-9 px-3 text-xs gap-1.5 rounded-full hover:bg-blue-50 text-blue-600 hover:text-blue-700"
onClick={() => {
addPendingChatAttachment({
url: data.src!,
name: data.prompt || '画布图片',
contentType: 'image/jpeg',
});
toast.success('已添加到对话框');
}}
title="添加到对话框"
>
<MessageSquarePlus className="h-4 w-4" />
<span>对话</span>
</Button>
{/* 分隔线 */}
<div className="h-5 w-px bg-gray-200 mx-1" />
{/* 放大/高清 */}
<Button
variant="ghost"

View File

@ -22,6 +22,13 @@ const defaultViewport: Viewport = {
zoom: 1,
};
// 待添加到对话框的图片附件
interface PendingChatAttachment {
url: string;
name: string;
contentType: string;
}
// Store 状态接口
interface CanvasStore {
// 项目信息
@ -43,6 +50,9 @@ interface CanvasStore {
isPanelOpen: boolean;
isGenerating: boolean;
// 🆕 待添加到对话框的图片(用于从画布添加图片到对话)
pendingChatAttachments: PendingChatAttachment[];
// 项目操作
setProject: (id: string | null, title?: string) => void;
setProjectTitle: (title: string) => void;
@ -96,6 +106,10 @@ interface CanvasStore {
getNode: (id: string) => CanvasNode | undefined;
getSelectedNodes: () => CanvasNode[];
getNodesByType: (type: NodeType) => CanvasNode[];
// 🆕 对话框附件操作
addPendingChatAttachment: (attachment: PendingChatAttachment) => void;
clearPendingChatAttachments: () => void;
}
export const useCanvasStore = create<CanvasStore>()(
@ -114,6 +128,7 @@ export const useCanvasStore = create<CanvasStore>()(
historyIndex: -1,
isPanelOpen: true,
isGenerating: false,
pendingChatAttachments: [], // 🆕 待添加到对话框的图片
// 项目操作
setProject: (id, title) => set((state) => {
@ -369,6 +384,18 @@ export const useCanvasStore = create<CanvasStore>()(
},
getNodesByType: (type) => get().nodes.filter(n => n.type === type),
// 🆕 对话框附件操作
addPendingChatAttachment: (attachment) => set((state) => {
// 避免重复添加
if (!state.pendingChatAttachments.some(a => a.url === attachment.url)) {
state.pendingChatAttachments.push(attachment);
}
}),
clearPendingChatAttachments: () => set((state) => {
state.pendingChatAttachments = [];
}),
})),
{
name: 'canvas-storage',