feat(flowEditor): 优化节点添加功能和界面

- 新增分组标签页,优化节点分类展示
- 改进边添加节点功能,支持在特定位置添加- 调整节点添加按钮样式和布局
- 优化画布点击事件处理,清除节点添加状态
master
钟良源 5 months ago
parent c0f7ffabf8
commit 79ae68ce92

@ -1,7 +1,9 @@
import React from 'react'; import React from 'react';
import { Menu } from '@arco-design/web-react'; import { Menu, Tabs } from '@arco-design/web-react';
import { localNodeData } from '@/pages/flowEditor/sideBar/config/localNodeData'; import { localNodeData } from '@/pages/flowEditor/sideBar/config/localNodeData';
const TabPane = Tabs.TabPane;
interface AddNodeMenuProps { interface AddNodeMenuProps {
onAddNode: (nodeType: string) => void; onAddNode: (nodeType: string) => void;
position?: { x: number; y: number }; // 用于画布上下文菜单 position?: { x: number; y: number }; // 用于画布上下文菜单
@ -10,7 +12,7 @@ interface AddNodeMenuProps {
const AddNodeMenu: React.FC<AddNodeMenuProps> = ({ const AddNodeMenu: React.FC<AddNodeMenuProps> = ({
onAddNode onAddNode
}) => { }) => {
// 按分组组织节点数据 // 按分组组织节点数据
const groupedNodes = localNodeData.reduce((acc, node) => { const groupedNodes = localNodeData.reduce((acc, node) => {
if (!acc[node.nodeGroup]) { if (!acc[node.nodeGroup]) {
@ -26,13 +28,26 @@ const AddNodeMenu: React.FC<AddNodeMenuProps> = ({
// 分组名称映射 // 分组名称映射
const groupNames: Record<string, string> = { const groupNames: Record<string, string> = {
'common': '系统组件' 'common': '系统组件',
// 可以根据需要添加更多分组 'application': '应用组件',
// 'application': '应用组件', 'composite': '复合组件'
// 'composite': '复合组件'
}; };
return ( return (
<div
style={{
backgroundColor: '#ffffff',
padding: '10px',
borderRadius: '10px',
maxHeight: '400px',
display: 'flex',
flexDirection: 'column'
}}
>
<Tabs defaultActiveTab="common" style={{ flex: '0 0 auto' }}>
{Object.entries(groupedNodes).map(([group, nodes]) => (
<TabPane key={group} title={groupNames[group] || group}>
<div style={{ maxHeight: '300px', overflowY: 'auto' }}>
<Menu <Menu
style={{ style={{
width: 200, width: 200,
@ -42,13 +57,6 @@ const AddNodeMenu: React.FC<AddNodeMenuProps> = ({
}} }}
mode="vertical" mode="vertical"
hasCollapseButton={false} hasCollapseButton={false}
>
{Object.entries(groupedNodes).map(([group, nodes]) => (
<Menu.SubMenu
key={group}
title={groupNames[group] || group}
popup
trigger="hover"
> >
{nodes.map((node) => ( {nodes.map((node) => (
<Menu.Item <Menu.Item
@ -63,9 +71,12 @@ const AddNodeMenu: React.FC<AddNodeMenuProps> = ({
{node.nodeName} {node.nodeName}
</Menu.Item> </Menu.Item>
))} ))}
</Menu.SubMenu>
))}
</Menu> </Menu>
</div>
</TabPane>
))}
</Tabs>
</div>
); );
}; };

@ -15,7 +15,7 @@ const CustomEdge: React.FC<EdgeProps> = ({
selected, selected,
data data
}) => { }) => {
const [edgePath, labelX, labelY] = getBezierPath({ const [edgePath, labelX, labelY, offsetX, offsetY] = getBezierPath({
sourceX, sourceX,
sourceY, sourceY,
sourcePosition, sourcePosition,
@ -31,8 +31,7 @@ const CustomEdge: React.FC<EdgeProps> = ({
const { setEdges } = useReactFlow(); const { setEdges } = useReactFlow();
// 边点击处理函数 // 边点击处理函数
const handleEdgeAddNode = () => { const handleEdgeAddNode = (e) => {
console.log('handleEdgeAddNode called for edge:', id);
// 更新边的数据,触发边上添加节点的流程 // 更新边的数据,触发边上添加节点的流程
setEdges(eds => eds.map(edge => { setEdges(eds => eds.map(edge => {
if (edge.id === id) { if (edge.id === id) {
@ -40,7 +39,9 @@ const CustomEdge: React.FC<EdgeProps> = ({
...edge, ...edge,
data: { data: {
...edge.data, ...edge.data,
addNodeTrigger: true addNodeTrigger: true,
clientX: e.clientX,
clientY: e.clientY
} }
}; };
} }
@ -71,20 +72,11 @@ const CustomEdge: React.FC<EdgeProps> = ({
> >
{hovered && ( {hovered && (
<EdgeAddNodeButton <EdgeAddNodeButton
onClick={handleEdgeAddNode} onClick={(e) => handleEdgeAddNode(e)}
/> />
)} )}
</div> </div>
</EdgeLabelRenderer> </EdgeLabelRenderer>
{/* 悬停时显示的高亮线条 */}
{hovered && (
<path
d={edgePath}
fill="none"
stroke="#1890ff"
strokeWidth={2}
/>
)}
</> </>
); );
}; };

@ -3,14 +3,14 @@ import { Button } from '@arco-design/web-react';
import { IconPlus } from '@arco-design/web-react/icon'; import { IconPlus } from '@arco-design/web-react/icon';
interface EdgeAddNodeButtonProps { interface EdgeAddNodeButtonProps {
onClick: () => void; onClick: (e) => void;
style?: React.CSSProperties; style?: React.CSSProperties;
} }
const EdgeAddNodeButton: React.FC<EdgeAddNodeButtonProps> = ({ const EdgeAddNodeButton: React.FC<EdgeAddNodeButtonProps> = ({
onClick, onClick,
style style
}) => { }) => {
return ( return (
<div <div
style={{ style={{
@ -26,12 +26,12 @@ const EdgeAddNodeButton: React.FC<EdgeAddNodeButtonProps> = ({
icon={<IconPlus />} icon={<IconPlus />}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onClick(); onClick(e);
}} }}
style={{ style={{
width: 20, width: 12,
height: 20, height: 12,
minWidth: 20, minWidth: 12,
padding: 0, padding: 0,
borderRadius: '50%', borderRadius: '50%',
display: 'flex', display: 'flex',

@ -2,6 +2,8 @@ import React from 'react';
import { Menu } from '@arco-design/web-react'; import { Menu } from '@arco-design/web-react';
import AddNodeMenu from './addNodeMenu'; import AddNodeMenu from './addNodeMenu';
const SubMenu = Menu.SubMenu;
interface PaneContextMenuProps { interface PaneContextMenuProps {
onAddNode: (nodeType: string, position: { x: number; y: number }) => void; onAddNode: (nodeType: string, position: { x: number; y: number }) => void;
position: { x: number; y: number }; position: { x: number; y: number };
@ -18,7 +20,6 @@ const PaneContextMenu: React.FC<PaneContextMenuProps> = ({
return ( return (
<Menu <Menu
mode="pop"
style={{ style={{
minWidth: 200, minWidth: 200,
border: '1px solid #e4e7ed', border: '1px solid #e4e7ed',
@ -26,17 +27,16 @@ const PaneContextMenu: React.FC<PaneContextMenuProps> = ({
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)' boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)'
}} }}
> >
<Menu.SubMenu <SubMenu
key="add-node" key="add-node"
title="添加节点" title="添加节点"
popup popup
triggerProps={{
popupStyle: { height: 'auto', maxHeight: 'none' }
}}
> >
<div style={{
padding: '4px 0'
}}>
<AddNodeMenu onAddNode={handleAddNode} /> <AddNodeMenu onAddNode={handleAddNode} />
</div> </SubMenu>
</Menu.SubMenu>
</Menu> </Menu>
); );
}; };

@ -289,8 +289,10 @@ const FlowEditor: React.FC = () => {
// 监听边的变化,处理添加节点的触发 // 监听边的变化,处理添加节点的触发
useEffect(() => { useEffect(() => {
const edgeToAddNode = edges.find(edge => edge.data?.addNodeTrigger); const edgeToAddNode = edges.find(edge => edge.data?.addNodeTrigger);
const pane = reactFlowWrapper.current?.getBoundingClientRect();
if (edgeToAddNode) { if (edgeToAddNode) {
console.log('Triggering add node for edge:', edgeToAddNode.id); edgeToAddNode.data.y = (edgeToAddNode.data.clientY as number) - pane.top;
edgeToAddNode.data.x = (edgeToAddNode.data.clientX as number) - pane.left;
setEdgeForNodeAdd(edgeToAddNode); setEdgeForNodeAdd(edgeToAddNode);
// 清除触发标志 // 清除触发标志
@ -380,7 +382,12 @@ const FlowEditor: React.FC = () => {
); );
// 点击画布其他区域关闭菜单 // 点击画布其他区域关闭菜单
const onPaneClick = useCallback(() => setMenu(null), [setMenu]); const onPaneClick = useCallback(() => {
setMenu(null);
// 关闭添加节点菜单
setEdgeForNodeAdd(null);
setPositionForNodeAdd(null);
}, [setMenu]);
// 关闭编辑弹窗 // 关闭编辑弹窗
const closeEditModal = useCallback(() => { const closeEditModal = useCallback(() => {
@ -711,14 +718,19 @@ const FlowEditor: React.FC = () => {
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: edgeForNodeAdd ? '50%' : (positionForNodeAdd?.y || 0), top: edgeForNodeAdd ? (edgeForNodeAdd.data?.y as number || 0) : (positionForNodeAdd?.y || 0),
left: edgeForNodeAdd ? '50%' : (positionForNodeAdd?.x || 0), left: edgeForNodeAdd ? ((edgeForNodeAdd.data?.x as number || 0) + 20) : (positionForNodeAdd?.x || 0),
zIndex: 1000, zIndex: 1000,
transform: edgeForNodeAdd ? 'translate(-50%, -50%)' : 'none' transform: 'none'
}} }}
> >
<AddNodeMenu <AddNodeMenu
onAddNode={handleAddNode} onAddNode={(nodeType) => {
handleAddNode(nodeType);
// 关闭菜单
setEdgeForNodeAdd(null);
setPositionForNodeAdd(null);
}}
position={positionForNodeAdd || undefined} position={positionForNodeAdd || undefined}
edgeId={edgeForNodeAdd?.id} edgeId={edgeForNodeAdd?.id}
/> />

Loading…
Cancel
Save