From dd6fc780a30b1754c0be417beba8e6974c210096 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 15:57:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(flowEditor):=20=E5=AE=9E=E7=8E=B0=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E9=9A=90=E8=97=8F/=E6=98=BE=E7=A4=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B9=B6=E5=BA=94=E7=94=A8=E9=80=8F=E6=98=8E=E5=BA=A6?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 FlowEditorMain 组件中新增 hiddenNodes 状态管理隐藏节点 - 监听 toggleNodeVisibility 自定义事件更新节点可见性 -为隐藏的节点和边应用 opacity 样式 - 在 customEdge 组件中为标签、下拉框和按钮添加透明度支持 - 在 sideBar 组件中新增眼睛图标按钮控制节点显示/隐藏 - 引入 IconEye 和 IconEyeInvisible 图标用于切换可见性状态 - 更新节点树额外操作按钮渲染逻辑以支持可见性切换 --- src/pages/flowEditor/FlowEditorMain.tsx | 90 ++++++++++++++++++- .../flowEditor/components/customEdge.tsx | 10 ++- src/pages/ideContainer/sideBar.tsx | 83 +++++++++++++++-- 3 files changed, 172 insertions(+), 11 deletions(-) diff --git a/src/pages/flowEditor/FlowEditorMain.tsx b/src/pages/flowEditor/FlowEditorMain.tsx index 4b72d44..2769ae1 100644 --- a/src/pages/flowEditor/FlowEditorMain.tsx +++ b/src/pages/flowEditor/FlowEditorMain.tsx @@ -139,6 +139,34 @@ const FlowEditorMain: React.FC = (props) => { const { undo, redo, canUndo, canRedo } = useHistory(); const reactFlowId = useMemo(() => new Date().getTime().toString(), []); + // 用于存储隐藏的节点ID + const [hiddenNodes, setHiddenNodes] = React.useState>(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 = (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 = (props) => { onContextMenu={(e) => e.preventDefault()}> ({ ...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} diff --git a/src/pages/flowEditor/components/customEdge.tsx b/src/pages/flowEditor/components/customEdge.tsx index df19e51..c7705c2 100644 --- a/src/pages/flowEditor/components/customEdge.tsx +++ b/src/pages/flowEditor/components/customEdge.tsx @@ -135,7 +135,8 @@ const DataDisplayEdge: React.FC = ({ 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 = ({ border: isOpen ? '1px solid #1890ff' : '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', - position: 'relative' + position: 'relative', + opacity: style?.opacity || 1 // 应用透明度样式到下拉框 }} >
= ({ 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 = ({ {hovered && Object.keys(displayData).length === 0 && lineType !== 'data' && ( handleEdgeAddNode(e)} + style={{ opacity: style?.opacity || 1 }} // 应用透明度样式到添加节点按钮 /> )}
diff --git a/src/pages/ideContainer/sideBar.tsx b/src/pages/ideContainer/sideBar.tsx index 1adbb5b..4074abe 100644 --- a/src/pages/ideContainer/sideBar.tsx +++ b/src/pages/ideContainer/sideBar.tsx @@ -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 = ({ y: 0, nodeData: null }); // 添加右键菜单状态 + // 用于存储隐藏的节点ID + const [hiddenNodes, setHiddenNodes] = useState>(new Set()); const resizeBoxRef = useRef(null); // 引用第一个 ResizeBox 容器 const contextMenuRef = useRef(null); // 右键菜单引用 const { menuData, info, canvasDataMap } = useSelector(state => state.ideContainer); @@ -551,12 +561,35 @@ const SideBar: React.FC = ({ }; }, []); + // 监听自定义事件以更新隐藏节点状态 + 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 = ( @@ -622,6 +655,44 @@ const SideBar: React.FC = ({ ); }; + 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 ( +
+ {isNodeHidden ? : } +
+ ); + }; + return (
= ({ } }} style={{ background: 'transparent' }} // 移除背景色 - renderExtra={selected?.parentKey === 'appList' ? renderNodeExtra : null} + renderExtra={selected?.parentKey === 'appList' ? renderNodeExtra : renderNodeExtraEye} > {renderMenuItems(filteredMenu[activeKey]?.children)}