Skip to content

Commit 2da3ced

Browse files
author
昔梦
committed
fix:完善自定义节点配置面板等文档,并补充settingWidget方式自定义组件的关闭弹窗进行的表单校验
1 parent 4f475a2 commit 2da3ced

File tree

8 files changed

+501
-18
lines changed

8 files changed

+501
-18
lines changed

Diff for: docs/xflow/FlowProvider.md

+26-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ group:
1111

1212
`<FlowProvider/>` 组件是一个 Context Provider,它使在 `<XFlow/>` 组件之外访问流的内部状态成为可能。我们提供的 `useFlow()``useNodes()``useEdges()` 钩子依赖于这个组件才能工作。
1313

14+
15+
## 基础用法
16+
17+
```js
18+
import { FlowProvider } from '@xrenders/xflow';
19+
20+
export default () => {
21+
return (
22+
<FlowProvider>
23+
<XFlow
24+
initialValues={{ nodes: initialNodes, edges: initialEdges }}
25+
settings={settings as any[]}
26+
nodeSelector={{
27+
showSearch: true,
28+
}}
29+
/>
30+
</FlowProvider>
31+
);
32+
};
33+
```
34+
35+
## 完整示例
1436
<code src="./demo/flow-provider/index.tsx"></code>
1537

1638
## useFlow
@@ -58,13 +80,13 @@ group:
5880
## useNodes
5981

6082
`useNodes()` 钩子用于实时监听节点状态变化。与 `useFlow``getNodes` 不同,`getNodes` 是瞬时值,`useNodes` 返回实时的nodes状态。
61-
62-
83+
6384
## useEdges
6485

65-
`useFlow``getEdges` 是瞬时值。想要监听节点状态,请使用 `useEdges` 钩子来返回实时 edges 状态。
86+
`useFlow``getEdges` 是瞬时值。想要监听节点状态,请使用 `useEdges` 钩子来返回实时 edges 状态。
87+
6688

67-
## 注意
89+
## 注意事项
6890

6991
- 如果你正在使用路由器并且希望流程的状态在不同路由之间保持持久,那么将 `<FlowProvider/>` 组件放置在路由器外部是至关重要的。
7092
- 如果在同一页面上有多个 `<XFlow/>`,则需要为每个 `<XFlow/>` 使用单独的 `<FlowProvider/>`

Diff for: docs/xflow/api.md

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ group:
3434
| -------- | ---------------------------------- | ------------------ | ------ |
3535
| width | 设置配置面板宽度 | `string \| number` | 400 |
3636
| hideDesc | 是否在配置面板中显示节点的描述信息 | `boolean` | false |
37+
| onClose | 配置面板关闭事件 | `(nodeID:string)=>void` | |
38+
3739

3840

3941

@@ -53,6 +55,8 @@ group:
5355
| 属性 | 描述 | 类型 | 默认值 |
5456
| -------------- | ------------------------------------------ | --------- | ------ |
5557
| hideEdgeAddBtn | 是否隐藏两个节点之间,连线上的增加节点按钮 | `boolean` | false |
58+
| deletable | 配置边是否可删除 | `boolean` | false |
59+
5660

5761
## TNodeGroup
5862

Diff for: docs/xflow/custom-node-setting.md

+37
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ group:
1616
## Schema 方式
1717
通过配置节点的 settingSchema 属性,实现节点数据配置项的自定义渲染。Schema方式适合快速实现简单的配置面板。
1818

19+
:::info
20+
注意:使用 FormRender 的 schema方式配置,只能用`{{ }}` 函数表达式和`dependencies`方式实现简单联动,不能使用`watch`监听的形式实现复杂联动,如果需要使用`watch`监听的形式请使用[`settingWidget`自定义组件](#widget-方式)
21+
:::
22+
1923
### 基础示例
24+
如果schema中设置了`required:true`必填项,则无法关闭和切换节点配置面板。如下方:开始节点中的必填项,试下点击开始节点后直接关闭配置面板的效果。
2025

2126
```jsx
2227
import { Input } from 'antd';
@@ -132,3 +137,35 @@ const customWidget = ({ value, onChange,...rest }) => {
132137
133138
### 基础示例
134139
<code src="./demo/custom-flow/index.tsx"></code>
140+
141+
### 复杂联动示例
142+
以下示例了如何在`settingWidget`中使用复杂联动,以及如何在弹窗关闭时增加必填校验。
143+
144+
需要暴露`validateForm`方法,以便关闭弹窗时进行必填校验。
145+
```js
146+
export const AdvancedSettingWidget = forwardRef<
147+
AdvancedSettingWidgetRef,
148+
AdvancedSettingWidgetProps
149+
>(({ value, onChange, readOnly }, ref) => {
150+
// ...组件实现
151+
useImperativeHandle(ref, () => ({
152+
validateForm: async () => {
153+
return await form
154+
.validateFields()
155+
.then(() => true)
156+
.catch(() => false);
157+
},
158+
}));
159+
});
160+
```
161+
162+
<code src="./demo/custom-flow/advancedLinkageCase"></code>
163+
164+
这个示例适用于以下场景:
165+
166+
- 需要根据不同配置类型显示不同表单项的场景
167+
- 表单项之间存在复杂联动关系的场景
168+
- 需要动态添加表单项的场景
169+
- 需要实现严格表单验证的场景
170+
171+
通过这个示例,你可以了解如何使用 settingWidget 方式实现复杂的表单交互,以及如何处理表单验证、数据联动等高级需求。这种方式比简单的 Schema 方式更灵活,能够满足更复杂的业务需求。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import { Button, Space } from 'antd';
2+
import FormRender, { useForm } from 'form-render';
3+
import React, { forwardRef, useImperativeHandle } from 'react';
4+
5+
export interface AdvancedSettingWidgetProps {
6+
value: any;
7+
onChange: (value: any) => void;
8+
readOnly?: boolean;
9+
}
10+
11+
export interface AdvancedSettingWidgetRef {
12+
validateForm: () => Promise<boolean>;
13+
}
14+
15+
export const AdvancedSettingWidget = forwardRef<
16+
AdvancedSettingWidgetRef,
17+
AdvancedSettingWidgetProps
18+
>(({ value, onChange, readOnly }, ref) => {
19+
const form = useForm();
20+
21+
// 暴露验证方法
22+
useImperativeHandle(ref, () => ({
23+
validateForm: async () => {
24+
return await form
25+
.validateFields()
26+
.then(() => {
27+
return true;
28+
})
29+
.catch(err => {
30+
return false;
31+
});
32+
},
33+
}));
34+
35+
const schema = {
36+
type: 'object',
37+
properties: {
38+
name: {
39+
title: '节点名称',
40+
type: 'string',
41+
required: true,
42+
},
43+
formType: {
44+
title: '配置类型',
45+
type: 'string',
46+
enum: ['simple', 'complex'],
47+
enumNames: ['简单配置', '复杂配置'],
48+
widget: 'radio',
49+
required: true,
50+
},
51+
simpleInput: {
52+
title: '简单输入',
53+
type: 'string',
54+
hidden: '{{formData.formType !== "simple"}}',
55+
required: true,
56+
},
57+
complexConfig: {
58+
title: '复杂配置',
59+
type: 'object',
60+
hidden: '{{formData.formType !== "complex"}}',
61+
properties: {
62+
field1: {
63+
title: '字段1',
64+
type: 'string',
65+
},
66+
field2: {
67+
title: '字段2',
68+
type: 'string',
69+
description: '字段1有值时才能输入',
70+
disabled: '{{!formData.complexConfig?.field1}}',
71+
},
72+
},
73+
},
74+
conditions: {
75+
title: '条件配置',
76+
type: 'array',
77+
widget: 'cardList',
78+
items: {
79+
type: 'object',
80+
properties: {
81+
name: {
82+
title: '条件名称',
83+
type: 'string',
84+
required: true,
85+
},
86+
operator: {
87+
title: '操作符',
88+
type: 'string',
89+
enum: ['equals', 'contains', 'greater', 'less'],
90+
enumNames: ['等于', '包含', '大于', '小于'],
91+
required: true,
92+
},
93+
value: {
94+
title: '条件值',
95+
type: 'string',
96+
required: true,
97+
},
98+
},
99+
},
100+
},
101+
},
102+
};
103+
104+
// 监听表单变化
105+
const watch = {
106+
'#': formData => {
107+
onChange(formData);
108+
},
109+
name: val => {
110+
console.log('监听节点名称的变化', val);
111+
},
112+
'complexConfig.field1': value => {
113+
// 当field1变化时,如果为空则清空field2
114+
if (!value) {
115+
const values = form.getValues();
116+
form.setValues({
117+
...values,
118+
complexConfig: {
119+
...values.complexConfig,
120+
field2: '',
121+
},
122+
});
123+
}
124+
},
125+
};
126+
127+
// 添加条件按钮
128+
const addCondition = () => {
129+
const values = form.getValues();
130+
const conditions = values.conditions || [];
131+
form.setValues({
132+
...values,
133+
conditions: [
134+
...conditions,
135+
{
136+
name: '',
137+
operator: 'equals',
138+
value: '',
139+
},
140+
],
141+
});
142+
};
143+
144+
React.useEffect(() => {
145+
if (value) {
146+
form.setValues(value);
147+
}
148+
}, [value]);
149+
150+
return (
151+
<div>
152+
<FormRender
153+
form={form}
154+
schema={schema}
155+
watch={watch}
156+
readOnly={readOnly}
157+
/>
158+
{!readOnly && (
159+
<div style={{ marginTop: '16px' }}>
160+
<Space>
161+
<Button type="primary" onClick={addCondition}>
162+
添加条件
163+
</Button>
164+
</Space>
165+
</div>
166+
)}
167+
</div>
168+
);
169+
});
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React, { useState } from 'react';
2+
import XFlow from '@xrenders/xflow';
3+
import { AdvancedSettingWidget } from './customWidget';
4+
import setting from './setting';
5+
import { Button, Switch } from 'antd';
6+
7+
export default () => {
8+
const [readOnly, setReadonly] = useState(false);
9+
10+
const nodes = [
11+
{
12+
id: 'start',
13+
type: 'Start',
14+
data: {
15+
title: '开始',
16+
},
17+
position: {
18+
x: 200,
19+
y: 200,
20+
}
21+
},
22+
{
23+
id: '1',
24+
type: 'LLM',
25+
data: {
26+
name: '高级节点',
27+
formType: 'simple',
28+
simpleInput: '',
29+
complexConfig: {
30+
field1: '',
31+
field2: '',
32+
},
33+
conditions: []
34+
},
35+
position: {
36+
x: 500,
37+
y: 200,
38+
}
39+
},
40+
{
41+
id: '2',
42+
type: 'End',
43+
data: {},
44+
position: {
45+
x: 800,
46+
y: 200,
47+
}
48+
}
49+
];
50+
51+
const edges = [
52+
{ source: 'start', target: '1', id: 'e0' },
53+
{ source: '1', target: '2', id: 'e1' }
54+
];
55+
56+
return (
57+
<div style={{ height: '600px' }}>
58+
<div style={{ marginBottom: '16px' }}>
59+
<span> 查看只读状态 <Switch checked={readOnly} onChange={setReadonly} /></span>
60+
</div>
61+
<XFlow
62+
initialValues={{ nodes, edges }}
63+
settings={setting}
64+
widgets={{ AdvancedSettingWidget }}
65+
readOnly={readOnly}
66+
style={{ marginTop: '20px' }}
67+
globalConfig={{
68+
nodePanel: {
69+
onClose: (nodeID) => {
70+
console.log("关闭节点",nodeID)
71+
}
72+
}
73+
}}
74+
/>
75+
</div>
76+
);
77+
};

0 commit comments

Comments
 (0)