feat(flowEditor): 实现节点隐藏/显示功能并应用透明度样式

- 在 FlowEditorMain 组件中新增 hiddenNodes 状态管理隐藏节点
- 监听 toggleNodeVisibility 自定义事件更新节点可见性
-为隐藏的节点和边应用 opacity 样式
- 在 customEdge 组件中为标签、下拉框和按钮添加透明度支持
- 在 sideBar 组件中新增眼睛图标按钮控制节点显示/隐藏
- 引入 IconEye 和 IconEyeInvisible 图标用于切换可见性状态
- 更新节点树额外操作按钮渲染逻辑以支持可见性切换
master
钟良源 3 months ago
parent 0b26c2385d
commit dd6fc780a3

@ -139,6 +139,34 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
const { undo, redo, canUndo, canRedo } = useHistory();
const reactFlowId = useMemo(() => new Date().getTime().toString(), []);
// 用于存储隐藏的节点ID
const [hiddenNodes, setHiddenNodes] = React.useState<Set<string>>(new Set());
// 监听自定义事件以隐藏/显示节点
useEffect(() => {
const handleToggleNodeVisibility = (event: CustomEvent) => {
const { appId, isVisible } = event.detail;
if (isVisible) {
// 显示节点 - 从隐藏节点集合中移除
setHiddenNodes(prev => {
const newSet = new Set(prev);
newSet.delete(appId);
return newSet;
});
} else {
// 隐藏节点 - 添加到隐藏节点集合
setHiddenNodes(prev => new Set(prev).add(appId));
}
};
document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
return () => {
document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
};
}, []);
// 从Redux store中获取当前应用的运行状态
const { appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id]
@ -148,6 +176,31 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
// 在应用编排模式下useDefault为false禁用删除功能
const isDeleteDisabled = currentAppIsRunning;
// 监听自定义事件以隐藏/显示节点
useEffect(() => {
const handleToggleNodeVisibility = (event: CustomEvent) => {
const { appId, isVisible } = event.detail;
if (isVisible) {
// 显示节点 - 从隐藏节点集合中移除
setHiddenNodes(prev => {
const newSet = new Set(prev);
newSet.delete(appId);
return newSet;
});
} else {
// 隐藏节点 - 添加到隐藏节点集合
setHiddenNodes(prev => new Set(prev).add(appId));
}
};
document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
return () => {
document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
};
}, []);
// 监听键盘事件实现快捷键
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
@ -188,8 +241,41 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
onContextMenu={(e) => e.preventDefault()}>
<ReactFlow
id={reactFlowId}
nodes={nodes.map(node => ({ ...node, draggable: !currentAppIsRunning }))} // 运行时禁用节点拖拽
edges={edges}
nodes={nodes.map(node => {
// 检查节点是否应该被隐藏
const isHidden = hiddenNodes.has(node.id);
// 应用透明度样式
const style = isHidden ? { opacity: 0.3 } : {};
return {
...node,
draggable: !currentAppIsRunning,
style: {
...node.style,
...style
}
};
})}
edges={edges.map(edge => {
// 检查边连接的节点是否被隐藏
const isSourceHidden = hiddenNodes.has(edge.source);
const isTargetHidden = hiddenNodes.has(edge.target);
// 如果源节点或目标节点被隐藏,则边也应用透明度
const style = (isSourceHidden || isTargetHidden) ? { opacity: 0.3 } : {};
// 更新边的数据,确保选择框也应用透明度
return {
...edge,
style: {
...edge.style,
...style
},
data: {
...edge.data,
...(isSourceHidden || isTargetHidden ? { hidden: true, opacity: 0.3 } : { opacity: 1 })
}
};
})}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
snapToGrid={true}

@ -135,7 +135,8 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
fontSize: 12,
pointerEvents: 'all'
pointerEvents: 'all',
opacity: style?.opacity || 1 // 应用透明度样式到标签容器
}}
className="nodrag nopan"
>
@ -148,7 +149,8 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
border: isOpen ? '1px solid #1890ff' : '1px solid #d9d9d9',
borderRadius: 4,
backgroundColor: '#fff',
position: 'relative'
position: 'relative',
opacity: style?.opacity || 1 // 应用透明度样式到下拉框
}}
>
<div
@ -178,7 +180,8 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
backgroundColor: '#fff',
zIndex: 1000,
maxHeight: 200,
overflowY: 'auto'
overflowY: 'auto',
opacity: style?.opacity || 1 // 应用透明度样式到下拉列表
}}
>
{eventTopicList.map((option: { value: string; label: string }) => (
@ -204,6 +207,7 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
{hovered && Object.keys(displayData).length === 0 && lineType !== 'data' && (
<EdgeAddNodeButton
onClick={(e) => handleEdgeAddNode(e)}
style={{ opacity: style?.opacity || 1 }} // 应用透明度样式到添加节点按钮
/>
)}
</div>

@ -12,9 +12,17 @@ import {
Menu,
Popconfirm
} from '@arco-design/web-react';
import { IconApps, IconMore, IconDelete, IconEdit } from '@arco-design/web-react/icon';
import {
IconApps,
IconMore,
IconDelete,
IconEdit,
IconEye,
IconSearch,
IconPlus,
IconEyeInvisible
} from '@arco-design/web-react/icon';
import { menuData1, menuData2 } from './config/menuData';
import { IconSearch, IconPlus } from '@arco-design/web-react/icon';
import { Selected } from '@/pages/ideContainer/types';
import { useDispatch, useSelector } from 'react-redux';
import {
@ -197,6 +205,8 @@ const SideBar: React.FC<SideBarProps> = ({
y: 0,
nodeData: null
}); // 添加右键菜单状态
// 用于存储隐藏的节点ID
const [hiddenNodes, setHiddenNodes] = useState<Set<string>>(new Set());
const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用第一个 ResizeBox 容器
const contextMenuRef = useRef<HTMLDivElement>(null); // 右键菜单引用
const { menuData, info, canvasDataMap } = useSelector(state => state.ideContainer);
@ -551,12 +561,35 @@ const SideBar: React.FC<SideBarProps> = ({
};
}, []);
// 监听自定义事件以更新隐藏节点状态
useEffect(() => {
const handleToggleNodeVisibility = (event: CustomEvent) => {
const { appId, isVisible } = event.detail;
if (isVisible) {
// 显示节点 - 从隐藏节点集合中移除
setHiddenNodes(prev => {
const newSet = new Set(prev);
newSet.delete(appId);
return newSet;
});
} else {
// 隐藏节点 - 添加到隐藏节点集合
setHiddenNodes(prev => new Set(prev).add(appId));
}
};
document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
return () => {
document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
};
}, []);
// 渲染节点的额外操作按钮
const renderNodeExtra = (node) => {
// 只有当 node.dataRef.id 存在时才渲染操作按钮
if (!node.dataRef?.id) {
return null;
}
if (!node.dataRef?.id) return null;
const dropList = (
<Menu>
@ -622,6 +655,44 @@ const SideBar: React.FC<SideBarProps> = ({
);
};
const renderNodeExtraEye = (node) => {
// console.log('node:', node);
// 只有当 node.dataRef.id 存在时才渲染操作按钮
if (!node.dataRef?.id) return null;
// 检查节点当前是否被隐藏
const isNodeHidden = hiddenNodes?.has(node.dataRef.id);
function handleEyeClick(e) {
e.stopPropagation();
// 发送自定义事件,通知流程图组件隐藏/显示节点
const event = new CustomEvent('toggleNodeVisibility', {
detail: {
appId: node.dataRef.id,
isVisible: isNodeHidden // 如果当前是隐藏的,点击后应该显示
}
});
document.dispatchEvent(event);
}
return (
<div
style={{
position: 'absolute',
right: 8,
fontSize: 20,
fontWeight: 700,
top: 10,
color: '#000000',
cursor: 'pointer'
}}
onClick={handleEyeClick}
>
{isNodeHidden ? <IconEye /> : <IconEyeInvisible />}
</div>
);
};
return (
<div
className={styles['sider']}
@ -699,7 +770,7 @@ const SideBar: React.FC<SideBarProps> = ({
}
}}
style={{ background: 'transparent' }} // 移除背景色
renderExtra={selected?.parentKey === 'appList' ? renderNodeExtra : null}
renderExtra={selected?.parentKey === 'appList' ? renderNodeExtra : renderNodeExtraEye}
>
{renderMenuItems(filteredMenu[activeKey]?.children)}
</Tree>

Loading…
Cancel
Save