Skip to content

Commit b8f34a2

Browse files
committed
feat: 迭代开发
2 parents afd5e52 + 323cd52 commit b8f34a2

File tree

21 files changed

+294
-180
lines changed

21 files changed

+294
-180
lines changed

docs/xflow/api.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ group:
4040

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

7986

8087
## THandle

packages/x-flow/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@xrenders/xflow",
3-
"version": "1.0.6-beta.14",
3+
"version": "1.0.6",
44
"description": "一款功能强大、易用灵活的流程编辑器框架,帮助你轻松构建复杂的工作流和流程产品",
55
"keywords": [
66
"xflow"

packages/x-flow/src/XFlow.tsx

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,7 @@ const XFlow: FC<FlowProps> = memo(props => {
8787
const { settingMap, globalConfig, readOnly } = useContext(ConfigContext);
8888
const [openPanel, setOpenPanel] = useState<boolean>(true);
8989
const [openLogPanel, setOpenLogPanel] = useState<boolean>(true);
90-
9190
const { onNodeClick, onEdgeClick, zoomOnScroll = true, panOnScroll = false, preventScrolling = true } = props;
92-
9391
const nodeEditorRef = useRef(null);
9492

9593
useEffect(() => {
@@ -204,24 +202,37 @@ const XFlow: FC<FlowProps> = memo(props => {
204202
setEdges(newEdges);
205203
});
206204

207-
const handleNodeValueChange = debounce((data: any) => {
208-
for (let node of nodes) {
209-
if (node.id === data.id) {
210-
node.data = {
211-
...node?.data,
212-
...data?.values,
213-
};
214-
break;
215-
}
205+
const handleNodeValueChange = debounce((data: any, id: string) => {
206+
// for (let node of nodes) {
207+
// if (node.id === data.id) {
208+
// node.data = {
209+
// ...node?.data,
210+
// ...data?.values,
211+
// };
212+
// break;
213+
// }
214+
// }
215+
// setNodes([...nodes], false);
216+
// 同时更新 activeNode 状态,确保面板数据同步
217+
if (activeNode && activeNode.id === id) {
218+
setActiveNode({
219+
...activeNode,
220+
values: {
221+
...activeNode.values,
222+
...data,
223+
},
224+
});
216225
}
217-
setNodes([...nodes], false);
218226
}, 200);
219227

220228
const nodeTypes = useMemo(() => {
221229
return {
222230
custom: (props: any) => {
223231
const { data, id, ...rest } = props;
224232
const { _nodeType, _status, ...restData } = data || {};
233+
const nodeSetting = settingMap[_nodeType] || {};
234+
const showPanel = nodeSetting?.nodePanel?.showPanel ?? true;
235+
225236
return (
226237
<CustomNode
227238
{...rest}
@@ -244,7 +255,11 @@ const XFlow: FC<FlowProps> = memo(props => {
244255
values: { ...restData },
245256
_status,
246257
});
247-
setOpenPanel(true);
258+
if (!showPanel) {
259+
setOpenPanel(false);
260+
} else {
261+
setOpenPanel(true);
262+
}
248263
setOpenLogPanel(true);
249264
}}
250265
/>
@@ -261,16 +276,16 @@ const XFlow: FC<FlowProps> = memo(props => {
261276
onChange={handleNodeValueChange}
262277
nodeType={activeNode?._nodeType}
263278
id={activeNode?.id}
264-
265279
/>
266280
);
281+
// JSON.stringify(activeNode)
267282
}, [activeNode?.id]);
268283

269284
const NodeLogWrap = useMemo(() => {
270285
return (
271286
<NodeLogPanel
272287
data={activeNode?.values}
273-
onChange={handleNodeValueChange}
288+
// onChange={handleNodeValueChange}
274289
nodeType={activeNode?._nodeType}
275290
id={activeNode?.id}
276291
node={activeNode}
@@ -287,19 +302,6 @@ const XFlow: FC<FlowProps> = memo(props => {
287302
const deletable = globalConfig?.edge?.deletable ?? true;
288303
const panelonClose = globalConfig?.nodePanel?.onClose;
289304

290-
const getNodesJ = nodes => {
291-
const result = nodes.map(item => {
292-
const { data, ...rest } = item;
293-
const { _nodeType, ...restData } = data;
294-
return {
295-
...rest,
296-
data: restData,
297-
type: _nodeType,
298-
};
299-
});
300-
return result;
301-
};
302-
303305
return (
304306
<div id="xflow-container" ref={workflowContainerRef}>
305307
<ReactFlow
@@ -310,9 +312,8 @@ const XFlow: FC<FlowProps> = memo(props => {
310312
edges={edges}
311313
minZoom={0.3}
312314
zoomOnScroll={zoomOnScroll}
313-
panOnScroll={panOnScroll} // 禁用滚动平移
314-
preventScrolling={preventScrolling} // 允许页面滚动
315-
315+
panOnScroll={panOnScroll} // 禁用滚动平移
316+
preventScrolling={preventScrolling} // 允许页面滚动
316317
defaultEdgeOptions={{
317318
type: 'buttonedge',
318319
style: {
@@ -321,7 +322,7 @@ const XFlow: FC<FlowProps> = memo(props => {
321322
markerEnd: {
322323
type: MarkerType.ArrowClosed, // 箭头
323324
width: 18,
324-
height:18
325+
height: 18,
325326
},
326327
deletable: deletable, //默认连线属性受此项控制
327328
}}

packages/x-flow/src/components/CustomNode/index.less

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,17 @@
4141
width: 2px;
4242
height: 10px;
4343
display: block;
44-
margin: 11px 0 8px 15px;
44+
margin: 11px 0 8px 17px;
45+
}
46+
47+
.handle-connected-target::after{
48+
margin: 11px 0 8px 11px;
49+
}
50+
51+
.handle-disconnected {
52+
&::after {
53+
background-color: transparent !important; // 未连接时透明
54+
}
4555
}
4656

4757
.react-flow__handle:hover {

packages/x-flow/src/components/CustomNode/index.tsx

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { MoreOutlined } from '@ant-design/icons';
22
import { Handle, Position, useReactFlow } from '@xyflow/react';
33
import { Dropdown, Menu, message } from 'antd';
4-
54
import { ItemType } from 'antd/es/menu/interface';
65
import classNames from 'classnames';
76
import { isFunction } from 'lodash';
@@ -44,7 +43,7 @@ export default memo((props: any) => {
4443
widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];
4544
const [isHovered, setIsHovered] = useState(false);
4645
const reactflow = useReactFlow();
47-
const { addEdges, mousePosition } =
46+
const { edges,nodes,addEdges, mousePosition } =
4847
useStore(
4948
(state: any) => ({
5049
nodes: state.nodes,
@@ -62,6 +61,30 @@ export default memo((props: any) => {
6261
const connectable = readOnly ? false : isConnectable;
6362
const nodeSetting = settingMap[type] || {};
6463
const nodeClassName = nodeSetting?.className || '';
64+
// 判断左侧Handle是否已连接
65+
const isTargetHandleConnected = useMemo(() => {
66+
return (edges || [])?.some(edge => edge.target === id);
67+
}, [edges, id]);
68+
69+
const isSourceHandleConnected = useMemo(() => {
70+
if (isSwitchNode) {
71+
// 对于Switch节点,需要检查每个sourceHandle是否已连接
72+
if (type === 'Switch' && Array.isArray(data.list)) {
73+
return data.list.some(item =>
74+
edges.some(edge => edge.source === id && edge.sourceHandle === item._id)
75+
);
76+
}
77+
// 对于Parallel节点,需要检查每个sourceHandle是否已连接
78+
if (type === 'Parallel' && Array.isArray(data.list)) {
79+
return data.list.some(item =>
80+
edges.some(edge => edge.source === id && edge.sourceHandle === item._id)
81+
);
82+
}
83+
return false;
84+
}
85+
// 对于普通节点,检查是否有从该节点出发的边
86+
return edges.some(edge => edge.source === id);
87+
}, [edges, id, type, data.list, isSwitchNode]);
6588

6689
// 增加节点并进行联系
6790
const handleAddNode = (data: any, sourceHandle?: string) => {
@@ -249,6 +272,7 @@ export default memo((props: any) => {
249272
overlay: menu,
250273
};
251274
}, [menuItem, isEnd]);
275+
252276
return (
253277
<div
254278
className={classNames('xflow-node-container', {
@@ -268,6 +292,11 @@ export default memo((props: any) => {
268292
type="target"
269293
position={targetPosition}
270294
isConnectable={connectable}
295+
className={classNames({
296+
'handle-connected': isTargetHandleConnected,
297+
'handle-disconnected': !isTargetHandleConnected,
298+
"handle-connected-target":true
299+
})}
271300
// isConnectableStart={isConnectableStart}
272301
// isConnectableEnd={isConnectableEnd}
273302
/>
@@ -329,14 +358,14 @@ export default memo((props: any) => {
329358
selected={selected}
330359
isHovered={isHovered}
331360
handleAddNode={handleAddNode}
361+
isConnected={isSourceHandleConnected}
332362
// isConnectableStart={isConnectableStart}
333363
// isConnectableEnd={isConnectableEnd}
334364
/>
335365
</>
336366
)}
337367
</Fragment>
338368
}
339-
340369
</div>
341370
);
342371
});

packages/x-flow/src/components/CustomNode/sourceHandle.tsx

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { PlusOutlined } from '@ant-design/icons';
22
import { Handle } from '@xyflow/react';
33
import { Tooltip } from 'antd';
4+
import classNames from 'classnames';
45
import React, { ComponentProps, memo, useContext, useMemo, useRef, useState } from 'react';
56
import NodeSelectPopover from '../NodesPopover';
67
import { ConfigContext } from '../../models/context';
7-
8+
import './index.less';
89

910
export type HandleProps = ComponentProps<typeof Handle>
1011

@@ -16,13 +17,14 @@ export default memo((props:Partial<HandleProps> & Record<string,any> ) => {
1617
isHovered,
1718
handleAddNode,
1819
switchTitle,
20+
isConnected, // 是否有连接的节点
1921
...rest
2022
} = props;
2123
const [isShowTooltip, setIsShowTooltip] = useState(false);
2224
const [openNodeSelectPopover, setOpenNodeSelectPopover] = useState(false);
2325
const popoverRef = useRef(null);
24-
const { antdVersion,globalConfig } = useContext(ConfigContext);
25-
const handleProps = globalConfig?.handle || {}
26+
const { antdVersion, globalConfig } = useContext(ConfigContext);
27+
const handleProps = globalConfig?.handle || {};
2628

2729
const toolTipVersionProps = useMemo(() => {
2830
if (antdVersion === 'V5') {
@@ -51,37 +53,45 @@ export default memo((props:Partial<HandleProps> & Record<string,any> ) => {
5153
setOpenNodeSelectPopover(true);
5254
}}
5355
{...rest}
56+
className={classNames(
57+
{
58+
'handle-disconnected': !isConnected,
59+
},
60+
rest.className
61+
)}
5462
>
55-
{(selected || isHovered || openNodeSelectPopover ) && (
63+
{(selected || isHovered || openNodeSelectPopover) && (
5664
<>
5765
{switchTitle && (
5866
<div className="xflow-node-switch-title">{switchTitle}</div>
5967
)}
60-
{isConnectable && <div className="xflow-node-add-box">
61-
<NodeSelectPopover
62-
placement="right"
63-
addNode={handleAddNode}
64-
ref={popoverRef}
65-
onNodeSelectPopoverChange={val => setOpenNodeSelectPopover(val)}
66-
>
67-
<Tooltip
68-
title="点击添加节点"
69-
arrow={false}
70-
overlayInnerStyle={{
71-
background: '#fff',
72-
color: '#354052',
73-
fontSize: '12px',
74-
}}
75-
color='#fff'
76-
{...toolTipVersionProps}
77-
getPopupContainer={() =>
78-
document.getElementById('xflow-container') as HTMLElement
79-
}
68+
{isConnectable && (
69+
<div className="xflow-node-add-box">
70+
<NodeSelectPopover
71+
placement="right"
72+
addNode={handleAddNode}
73+
ref={popoverRef}
74+
onNodeSelectPopoverChange={val => setOpenNodeSelectPopover(val)}
8075
>
81-
<PlusOutlined style={{ color: '#fff', fontSize: 10 }} />
82-
</Tooltip>
83-
</NodeSelectPopover>
84-
</div>}
76+
<Tooltip
77+
title="点击添加节点"
78+
// arrow={false}
79+
overlayInnerStyle={{
80+
background: '#fff',
81+
color: '#354052',
82+
fontSize: '12px',
83+
}}
84+
color="#fff"
85+
{...toolTipVersionProps}
86+
getPopupContainer={() =>
87+
document.getElementById('xflow-container') as HTMLElement
88+
}
89+
>
90+
<PlusOutlined style={{ color: '#fff', fontSize: 10 }} />
91+
</Tooltip>
92+
</NodeSelectPopover>
93+
</div>
94+
)}
8595
</>
8696
)}
8797
</Handle>

0 commit comments

Comments
 (0)