Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/xflow/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ group:

| 属性 | 描述 | 类型 | 默认值 |
| -------- | ---------------------------------- | ------------------ | ------ |
| showPanel | 是否展示配置面板 | `boolean` | `true` |
| width | 设置配置面板宽度 | `string \| number` | 400 |
| hideDesc | 是否在配置面板中显示节点的描述信息 | `boolean` | false |
| onClose | 配置面板关闭事件 | `(nodeID:string)=>void` | |
Expand Down Expand Up @@ -75,6 +76,12 @@ group:
| hideAddNode | 是否隐藏增加节点功能 | `boolean` | false |
| hideAnnotate | 是否隐藏注释节点功能 | `boolean` | false |
| hideUndoRedoBtns | 是否隐藏撤销和重做按钮 | `boolean` | false |
| hideZoomInOutBtns | 是否隐藏缩放按钮 | `boolean` | false |
| hideControlBtns | 是否隐藏所有控制按钮 | `boolean` | false |
| hideAutoLayout | 是否隐藏整理画布功能 | `boolean` | false |
| hideFullscreen | 是否隐藏全屏功能 | `boolean` | false |
| hideInteractionMode | 是否隐藏指针和手形工具切换功能 | `boolean` | false |



## THandle
Expand Down
65 changes: 33 additions & 32 deletions packages/x-flow/src/XFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ const XFlow: FC<FlowProps> = memo(props => {
const { settingMap, globalConfig, readOnly } = useContext(ConfigContext);
const [openPanel, setOpenPanel] = useState<boolean>(true);
const [openLogPanel, setOpenLogPanel] = useState<boolean>(true);

const { onNodeClick, onEdgeClick, zoomOnScroll = true, panOnScroll = false, preventScrolling = true } = props;

const nodeEditorRef = useRef(null);

useEffect(() => {
Expand Down Expand Up @@ -204,24 +202,37 @@ const XFlow: FC<FlowProps> = memo(props => {
setEdges(newEdges);
});

const handleNodeValueChange = debounce((data: any) => {
for (let node of nodes) {
if (node.id === data.id) {
node.data = {
...node?.data,
...data?.values,
};
break;
}
const handleNodeValueChange = debounce((data: any, id: string) => {
// for (let node of nodes) {
// if (node.id === data.id) {
// node.data = {
// ...node?.data,
// ...data?.values,
// };
// break;
// }
// }
// setNodes([...nodes], false);
// 同时更新 activeNode 状态,确保面板数据同步
if (activeNode && activeNode.id === id) {
setActiveNode({
...activeNode,
values: {
...activeNode.values,
...data,
},
});
}
setNodes([...nodes], false);
}, 200);

const nodeTypes = useMemo(() => {
return {
custom: (props: any) => {
const { data, id, ...rest } = props;
const { _nodeType, _status, ...restData } = data || {};
const nodeSetting = settingMap[_nodeType] || {};
const showPanel = nodeSetting?.nodePanel?.showPanel ?? true;

return (
<CustomNode
{...rest}
Expand All @@ -244,7 +255,11 @@ const XFlow: FC<FlowProps> = memo(props => {
values: { ...restData },
_status,
});
setOpenPanel(true);
if (!showPanel) {
setOpenPanel(false);
} else {
setOpenPanel(true);
}
setOpenLogPanel(true);
}}
/>
Expand All @@ -261,16 +276,16 @@ const XFlow: FC<FlowProps> = memo(props => {
onChange={handleNodeValueChange}
nodeType={activeNode?._nodeType}
id={activeNode?.id}

/>
);
// JSON.stringify(activeNode)
}, [activeNode?.id]);

const NodeLogWrap = useMemo(() => {
return (
<NodeLogPanel
data={activeNode?.values}
onChange={handleNodeValueChange}
// onChange={handleNodeValueChange}
nodeType={activeNode?._nodeType}
id={activeNode?.id}
node={activeNode}
Expand All @@ -287,19 +302,6 @@ const XFlow: FC<FlowProps> = memo(props => {
const deletable = globalConfig?.edge?.deletable ?? true;
const panelonClose = globalConfig?.nodePanel?.onClose;

const getNodesJ = nodes => {
const result = nodes.map(item => {
const { data, ...rest } = item;
const { _nodeType, ...restData } = data;
return {
...rest,
data: restData,
type: _nodeType,
};
});
return result;
};

return (
<div id="xflow-container" ref={workflowContainerRef}>
<ReactFlow
Expand All @@ -310,9 +312,8 @@ const XFlow: FC<FlowProps> = memo(props => {
edges={edges}
minZoom={0.3}
zoomOnScroll={zoomOnScroll}
panOnScroll={panOnScroll} // 禁用滚动平移
preventScrolling={preventScrolling} // 允许页面滚动

panOnScroll={panOnScroll} // 禁用滚动平移
preventScrolling={preventScrolling} // 允许页面滚动
defaultEdgeOptions={{
type: 'buttonedge',
style: {
Expand All @@ -321,7 +322,7 @@ const XFlow: FC<FlowProps> = memo(props => {
markerEnd: {
type: MarkerType.ArrowClosed, // 箭头
width: 18,
height:18
height: 18,
},
deletable: deletable, //默认连线属性受此项控制
}}
Expand Down
12 changes: 11 additions & 1 deletion packages/x-flow/src/components/CustomNode/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,17 @@
width: 2px;
height: 10px;
display: block;
margin: 11px 0 8px 15px;
margin: 11px 0 8px 17px;
}

.handle-connected-target::after{
margin: 11px 0 8px 11px;
}

.handle-disconnected {
&::after {
background-color: transparent !important; // 未连接时透明
}
}

.react-flow__handle:hover {
Expand Down
35 changes: 32 additions & 3 deletions packages/x-flow/src/components/CustomNode/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { MoreOutlined } from '@ant-design/icons';
import { Handle, Position, useReactFlow } from '@xyflow/react';
import { Dropdown, Menu, message } from 'antd';

import { ItemType } from 'antd/es/menu/interface';
import classNames from 'classnames';
import { isFunction } from 'lodash';
Expand Down Expand Up @@ -44,7 +43,7 @@ export default memo((props: any) => {
widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];
const [isHovered, setIsHovered] = useState(false);
const reactflow = useReactFlow();
const { addEdges, mousePosition } =
const { edges,nodes,addEdges, mousePosition } =
useStore(
(state: any) => ({
nodes: state.nodes,
Expand All @@ -62,6 +61,30 @@ export default memo((props: any) => {
const connectable = readOnly ? false : isConnectable;
const nodeSetting = settingMap[type] || {};
const nodeClassName = nodeSetting?.className || '';
// 判断左侧Handle是否已连接
const isTargetHandleConnected = useMemo(() => {
return (edges || [])?.some(edge => edge.target === id);
}, [edges, id]);

const isSourceHandleConnected = useMemo(() => {
if (isSwitchNode) {
// 对于Switch节点,需要检查每个sourceHandle是否已连接
if (type === 'Switch' && Array.isArray(data.list)) {
return data.list.some(item =>
edges.some(edge => edge.source === id && edge.sourceHandle === item._id)
);
}
// 对于Parallel节点,需要检查每个sourceHandle是否已连接
if (type === 'Parallel' && Array.isArray(data.list)) {
return data.list.some(item =>
edges.some(edge => edge.source === id && edge.sourceHandle === item._id)
);
}
return false;
}
// 对于普通节点,检查是否有从该节点出发的边
return edges.some(edge => edge.source === id);
}, [edges, id, type, data.list, isSwitchNode]);

// 增加节点并进行联系
const handleAddNode = (data: any, sourceHandle?: string) => {
Expand Down Expand Up @@ -249,6 +272,7 @@ export default memo((props: any) => {
overlay: menu,
};
}, [menuItem, isEnd]);

return (
<div
className={classNames('xflow-node-container', {
Expand All @@ -268,6 +292,11 @@ export default memo((props: any) => {
type="target"
position={targetPosition}
isConnectable={connectable}
className={classNames({
'handle-connected': isTargetHandleConnected,
'handle-disconnected': !isTargetHandleConnected,
"handle-connected-target":true
})}
// isConnectableStart={isConnectableStart}
// isConnectableEnd={isConnectableEnd}
/>
Expand Down Expand Up @@ -329,14 +358,14 @@ export default memo((props: any) => {
selected={selected}
isHovered={isHovered}
handleAddNode={handleAddNode}
isConnected={isSourceHandleConnected}
// isConnectableStart={isConnectableStart}
// isConnectableEnd={isConnectableEnd}
/>
</>
)}
</Fragment>
}

</div>
);
});
66 changes: 38 additions & 28 deletions packages/x-flow/src/components/CustomNode/sourceHandle.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { PlusOutlined } from '@ant-design/icons';
import { Handle } from '@xyflow/react';
import { Tooltip } from 'antd';
import classNames from 'classnames';
import React, { ComponentProps, memo, useContext, useMemo, useRef, useState } from 'react';
import NodeSelectPopover from '../NodesPopover';
import { ConfigContext } from '../../models/context';

import './index.less';

export type HandleProps = ComponentProps<typeof Handle>

Expand All @@ -16,13 +17,14 @@ export default memo((props:Partial<HandleProps> & Record<string,any> ) => {
isHovered,
handleAddNode,
switchTitle,
isConnected, // 是否有连接的节点
...rest
} = props;
const [isShowTooltip, setIsShowTooltip] = useState(false);
const [openNodeSelectPopover, setOpenNodeSelectPopover] = useState(false);
const popoverRef = useRef(null);
const { antdVersion,globalConfig } = useContext(ConfigContext);
const handleProps = globalConfig?.handle || {}
const { antdVersion, globalConfig } = useContext(ConfigContext);
const handleProps = globalConfig?.handle || {};

const toolTipVersionProps = useMemo(() => {
if (antdVersion === 'V5') {
Expand Down Expand Up @@ -51,37 +53,45 @@ export default memo((props:Partial<HandleProps> & Record<string,any> ) => {
setOpenNodeSelectPopover(true);
}}
{...rest}
className={classNames(
{
'handle-disconnected': !isConnected,
},
rest.className
)}
>
{(selected || isHovered || openNodeSelectPopover ) && (
{(selected || isHovered || openNodeSelectPopover) && (
<>
{switchTitle && (
<div className="xflow-node-switch-title">{switchTitle}</div>
)}
{isConnectable && <div className="xflow-node-add-box">
<NodeSelectPopover
placement="right"
addNode={handleAddNode}
ref={popoverRef}
onNodeSelectPopoverChange={val => setOpenNodeSelectPopover(val)}
>
<Tooltip
title="点击添加节点"
arrow={false}
overlayInnerStyle={{
background: '#fff',
color: '#354052',
fontSize: '12px',
}}
color='#fff'
{...toolTipVersionProps}
getPopupContainer={() =>
document.getElementById('xflow-container') as HTMLElement
}
{isConnectable && (
<div className="xflow-node-add-box">
<NodeSelectPopover
placement="right"
addNode={handleAddNode}
ref={popoverRef}
onNodeSelectPopoverChange={val => setOpenNodeSelectPopover(val)}
>
<PlusOutlined style={{ color: '#fff', fontSize: 10 }} />
</Tooltip>
</NodeSelectPopover>
</div>}
<Tooltip
title="点击添加节点"
// arrow={false}
overlayInnerStyle={{
background: '#fff',
color: '#354052',
fontSize: '12px',
}}
color="#fff"
{...toolTipVersionProps}
getPopupContainer={() =>
document.getElementById('xflow-container') as HTMLElement
}
>
<PlusOutlined style={{ color: '#fff', fontSize: 10 }} />
</Tooltip>
</NodeSelectPopover>
</div>
)}
</>
)}
</Handle>
Expand Down
Loading
Loading