Skip to content

Commit b9d3ca4

Browse files
authored
Merge pull request #73 from anyone-yuren/revert-72-revert-71-rancong_branch
Revert "Revert 71 rancong branch"
2 parents 696039b + ed7e920 commit b9d3ca4

File tree

59 files changed

+4592
-1332
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4592
-1332
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { FlowEditor } from 'ui';
2+
import { Node, Edge } from 'reactflow';
3+
4+
export default () => {
5+
const initialNodes: Node[] = [
6+
{
7+
id: '1',
8+
type: 'custom',
9+
position: { x: 100, y: 100 },
10+
data: { label: '开始', description: '流程开始节点', type: 'start', status: 'active' },
11+
},
12+
{
13+
id: '2',
14+
type: 'custom',
15+
position: { x: 400, y: 100 },
16+
data: { label: '处理', description: '数据处理节点', type: 'process', status: 'active' },
17+
},
18+
{
19+
id: '3',
20+
type: 'custom',
21+
position: { x: 700, y: 100 },
22+
data: { label: '结束', description: '流程结束节点', type: 'end', status: 'active' },
23+
},
24+
];
25+
26+
const initialEdges: Edge[] = [
27+
{
28+
id: 'e1-2',
29+
source: '1',
30+
target: '2',
31+
animated: true,
32+
},
33+
{
34+
id: 'e2-3',
35+
source: '2',
36+
target: '3',
37+
animated: true,
38+
},
39+
];
40+
41+
const handleSave = (nodes: Node[], edges: Edge[]) => {
42+
console.log('保存流程数据:', { nodes, edges });
43+
};
44+
45+
return (
46+
<>
47+
<FlowEditor initialNodes={initialNodes} initialEdges={initialEdges} onSave={handleSave} />
48+
</>
49+
);
50+
};
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import { Loading } from 'ui';
2+
import { Center } from 'react-layout-kit';
3+
import React from 'react';
4+
import { Button, Space, Switch, Card, Radio, ColorPicker, Input } from 'antd';
5+
6+
export default () => {
7+
const [loading, setLoading] = React.useState(true);
8+
const [fullscreen, setFullscreen] = React.useState(false);
9+
const [size, setSize] = React.useState<'small' | 'default' | 'large'>('default');
10+
const [color, setColor] = React.useState('#1890ff');
11+
const [tip, setTip] = React.useState('加载中...');
12+
13+
return (
14+
<Space direction="vertical" size="large" style={{ width: '100%' }}>
15+
{/* API 属性控制面板 */}
16+
<Card title="API 属性配置" style={{ marginBottom: '16px' }}>
17+
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
18+
{/* loading 属性 */}
19+
<div>
20+
<Space>
21+
<Switch checked={loading} onChange={(e) => setLoading(e)} />
22+
<strong>loading:</strong> <span>{loading ? 'true' : 'false'}</span>
23+
<span style={{ color: '#999' }}>// 加载状态 | boolean | 默认值: true</span>
24+
</Space>
25+
</div>
26+
27+
{/* fullscreen 属性 */}
28+
<div>
29+
<Space>
30+
<Switch checked={fullscreen} onChange={(e) => setFullscreen(e)} />
31+
<strong>fullscreen:</strong> <span>{fullscreen ? 'true' : 'false'}</span>
32+
<span style={{ color: '#999' }}>// 是否全屏遮罩 | boolean | 默认值: false</span>
33+
</Space>
34+
</div>
35+
36+
{/* size 属性 */}
37+
<div>
38+
<Space>
39+
<strong>size:</strong>
40+
<Radio.Group value={size} onChange={(e) => setSize(e.target.value)}>
41+
<Radio.Button value="small">small</Radio.Button>
42+
<Radio.Button value="default">default</Radio.Button>
43+
<Radio.Button value="large">large</Radio.Button>
44+
</Radio.Group>
45+
<span style={{ color: '#999' }}>// 加载动画大小 | 'small' | 'default' | 'large' | 默认值: 'default'</span>
46+
</Space>
47+
</div>
48+
49+
{/* color 属性 */}
50+
<div>
51+
<Space>
52+
<strong>color:</strong>
53+
<ColorPicker value={color} onChange={(value) => setColor(value.toHexString())} />
54+
<span>{color}</span>
55+
<span style={{ color: '#999' }}>// 加载动画颜色 | string | 默认值: '#1890ff'</span>
56+
</Space>
57+
</div>
58+
59+
{/* tip 属性 */}
60+
<div>
61+
<Space>
62+
<strong>tip:</strong>
63+
<Input value={tip} onChange={(e) => setTip(e.target.value)} style={{ width: 200 }} />
64+
<span style={{ color: '#999' }}>// 加载提示文字 | string | 默认值: '加载中...'</span>
65+
</Space>
66+
</div>
67+
</Space>
68+
</Card>
69+
70+
{/* Loading 组件演示 */}
71+
<Card title="Loading 组件演示">
72+
<Center style={{ height: '30vh', border: '1px dashed #ccc', borderRadius: '8px' }}>
73+
<Loading
74+
loading={loading}
75+
fullscreen={fullscreen}
76+
tip={tip}
77+
size={size}
78+
color={color}
79+
>
80+
<div style={{ padding: '20px', background: '#f0f2f5', borderRadius: '8px' }}>
81+
<h3>内容区域</h3>
82+
<p>当 loading 为 false 时,这里会显示实际内容</p>
83+
<p>当前时间: {new Date().toLocaleString()}</p>
84+
</div>
85+
</Loading>
86+
</Center>
87+
</Card>
88+
89+
{/* 快捷操作按钮 */}
90+
<Card title="快捷操作">
91+
<Space wrap>
92+
<Button type="primary" onClick={() => setLoading(!loading)}>
93+
切换 loading 状态
94+
</Button>
95+
<Button onClick={() => setFullscreen(!fullscreen)}>
96+
切换全屏模式
97+
</Button>
98+
<Button onClick={() => setSize('small')}>
99+
小尺寸
100+
</Button>
101+
<Button onClick={() => setSize('default')}>
102+
默认尺寸
103+
</Button>
104+
<Button onClick={() => setSize('large')}>
105+
大尺寸
106+
</Button>
107+
<Button onClick={() => setColor('#1890ff')}>
108+
蓝色
109+
</Button>
110+
<Button onClick={() => setColor('#52c41a')}>
111+
绿色
112+
</Button>
113+
<Button onClick={() => setColor('#ff4d4f')}>
114+
红色
115+
</Button>
116+
</Space>
117+
</Card>
118+
119+
{/* API 说明 */}
120+
<Card title="API 属性说明">
121+
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
122+
<thead>
123+
<tr style={{ borderBottom: '2px solid #f0f0f0' }}>
124+
<th style={{ padding: '12px', textAlign: 'left', background: '#fafafa' }}>属性名</th>
125+
<th style={{ padding: '12px', textAlign: 'left', background: '#fafafa' }}>描述</th>
126+
<th style={{ padding: '12px', textAlign: 'left', background: '#fafafa' }}>类型</th>
127+
<th style={{ padding: '12px', textAlign: 'left', background: '#fafafa' }}>默认值</th>
128+
</tr>
129+
</thead>
130+
<tbody>
131+
<tr style={{ borderBottom: '1px solid #f0f0f0' }}>
132+
<td style={{ padding: '12px' }}><code>loading</code></td>
133+
<td style={{ padding: '12px' }}>加载状态</td>
134+
<td style={{ padding: '12px' }}><code>boolean</code></td>
135+
<td style={{ padding: '12px' }}><code>true</code></td>
136+
</tr>
137+
<tr style={{ borderBottom: '1px solid #f0f0f0' }}>
138+
<td style={{ padding: '12px' }}><code>tip</code></td>
139+
<td style={{ padding: '12px' }}>加载提示文字</td>
140+
<td style={{ padding: '12px' }}><code>string</code></td>
141+
<td style={{ padding: '12px' }}><code>'加载中...'</code></td>
142+
</tr>
143+
<tr style={{ borderBottom: '1px solid #f0f0f0' }}>
144+
<td style={{ padding: '12px' }}><code>size</code></td>
145+
<td style={{ padding: '12px' }}>加载动画大小</td>
146+
<td style={{ padding: '12px' }}><code>'small' | 'default' | 'large'</code></td>
147+
<td style={{ padding: '12px' }}><code>'default'</code></td>
148+
</tr>
149+
<tr style={{ borderBottom: '1px solid #f0f0f0' }}>
150+
<td style={{ padding: '12px' }}><code>color</code></td>
151+
<td style={{ padding: '12px' }}>加载动画颜色</td>
152+
<td style={{ padding: '12px' }}><code>string</code></td>
153+
<td style={{ padding: '12px' }}><code>'#1890ff'</code></td>
154+
</tr>
155+
<tr style={{ borderBottom: '1px solid #f0f0f0' }}>
156+
<td style={{ padding: '12px' }}><code>fullscreen</code></td>
157+
<td style={{ padding: '12px' }}>是否全屏遮罩</td>
158+
<td style={{ padding: '12px' }}><code>boolean</code></td>
159+
<td style={{ padding: '12px' }}><code>false</code></td>
160+
</tr>
161+
<tr style={{ borderBottom: '1px solid #f0f0f0' }}>
162+
<td style={{ padding: '12px' }}><code>className</code></td>
163+
<td style={{ padding: '12px' }}>自定义类名</td>
164+
<td style={{ padding: '12px' }}><code>string</code></td>
165+
<td style={{ padding: '12px' }}><code>-</code></td>
166+
</tr>
167+
<tr style={{ borderBottom: '1px solid #f0f0f0' }}>
168+
<td style={{ padding: '12px' }}><code>style</code></td>
169+
<td style={{ padding: '12px' }}>自定义样式</td>
170+
<td style={{ padding: '12px' }}><code>React.CSSProperties</code></td>
171+
<td style={{ padding: '12px' }}><code>-</code></td>
172+
</tr>
173+
<tr>
174+
<td style={{ padding: '12px' }}><code>children</code></td>
175+
<td style={{ padding: '12px' }}>子元素内容</td>
176+
<td style={{ padding: '12px' }}><code>React.ReactNode</code></td>
177+
<td style={{ padding: '12px' }}><code>-</code></td>
178+
</tr>
179+
</tbody>
180+
</table>
181+
</Card>
182+
</Space>
183+
);
184+
};
185+
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
title: 流程编辑器
3+
atomId: FlowEditor
4+
package: ui
5+
description: 基于react-flow的动态流程编辑器组件
6+
group:
7+
title: 功能
8+
---
9+
10+
# 流程编辑器
11+
12+
> 基于react-flow的动态流程编辑器组件,支持节点拖拽、连接、编辑等功能
13+
14+
安装`pnpm add ui --filter @gbeata/admin-docs`
15+
<code src="./demos/floweditor"></code>
16+
17+
## 功能特性
18+
19+
- 🎨 **可视化编辑**:拖拽节点创建流程图
20+
- 🔗 **节点连接**:通过拖拽节点上的锚点来连接节点
21+
-**添加连接节点**:选中节点后,通过"添加连接节点"按钮快速创建并连接新节点
22+
- ✏️ **节点编辑**:点击节点直接在节点上编辑节点信息,无需弹窗
23+
- 📝 **表格数据**:支持动态添加/删除表格行,每行包含键值对输入
24+
- 🎯 **单选选项**:提供单选按钮组,支持多个选项选择
25+
- 🎯 **多种节点类型**:支持默认、开始、结束、条件、处理等多种节点类型
26+
- 💾 **数据保存**:支持保存流程图数据
27+
- 🗑️ **清空功能**:一键清空所有节点和连线
28+
29+
## API
30+
31+
| 属性名 | 描述 | 类型 | 默认值 |
32+
| --- | --- | --- | --- |
33+
| initialNodes | 初始节点数据 | `Node<NodeData>[]` | `[]` |
34+
| initialEdges | 初始连线数据 | `Edge[]` | `[]` |
35+
| onNodesChange | 节点变化回调 | `(nodes: Node<NodeData>[]) => void` | - |
36+
| onEdgesChange | 连线变化回调 | `(edges: Edge[]) => void` | - |
37+
| onSave | 保存回调 | `(nodes: Node<NodeData>[], edges: Edge[]) => void` | - |
38+
39+
## NodeData 类型
40+
41+
| 属性名 | 描述 | 类型 | 默认值 |
42+
| --- | --- | --- | --- |
43+
| label | 节点名称 | `string` | - |
44+
| description | 节点描述 | `string` | - |
45+
| type | 节点类型 | `string` | `'default'` |
46+
| status | 节点状态 | `string` | `'active'` |
47+
| tableData | 表格数据 | `Array<{ key: string; value: string }>` | `[{ key: '', value: '' }]` |
48+
| radioValue | 单选值 | `string` | `'A'` |
49+
50+
## 节点类型
51+
52+
- `default` - 默认节点
53+
- `start` - 开始节点
54+
- `end` - 结束节点
55+
- `condition` - 条件节点
56+
- `process` - 处理节点
57+
58+
## 节点状态
59+
60+
- `active` - 活跃
61+
- `disabled` - 禁用
62+
- `completed` - 完成
63+
- `error` - 错误
64+
65+
## 使用示例
66+
67+
### 基础用法
68+
69+
```tsx
70+
import { FlowEditor } from 'ui';
71+
72+
function App() {
73+
return <FlowEditor />;
74+
}
75+
```
76+
77+
### 带初始数据
78+
79+
```tsx
80+
import { FlowEditor } from 'ui';
81+
import { Node, Edge } from 'reactflow';
82+
83+
const initialNodes: Node[] = [
84+
{
85+
id: '1',
86+
type: 'custom',
87+
position: { x: 0, y: 0 },
88+
data: { label: '开始', description: '流程开始', type: 'start', status: 'active' },
89+
},
90+
];
91+
92+
const initialEdges: Edge[] = [];
93+
94+
function App() {
95+
return <FlowEditor initialNodes={initialNodes} initialEdges={initialEdges} />;
96+
}
97+
```
98+
99+
### 监听变化
100+
101+
```tsx
102+
import { FlowEditor } from 'ui';
103+
104+
function App() {
105+
const handleNodesChange = (nodes) => {
106+
console.log('节点变化:', nodes);
107+
};
108+
109+
const handleEdgesChange = (edges) => {
110+
console.log('连线变化:', edges);
111+
};
112+
113+
const handleSave = (nodes, edges) => {
114+
console.log('保存数据:', { nodes, edges });
115+
};
116+
117+
return (
118+
<FlowEditor
119+
onNodesChange={handleNodesChange}
120+
onEdgesChange={handleEdgesChange}
121+
onSave={handleSave}
122+
/>
123+
);
124+
}
125+
```
126+
127+
## 操作说明
128+
129+
1. **添加节点**:点击"添加节点"按钮,会在画布上随机位置创建一个新节点
130+
2. **连接节点**:从一个节点的右侧锚点(蓝色圆点)拖拽到另一个节点的左侧锚点即可创建连线
131+
3. **添加连接节点**:选中某个节点后,点击"添加连接节点"按钮,会自动创建一个新节点并与当前节点连接
132+
4. **编辑节点**:点击任意节点,节点会切换到编辑模式,可以直接在节点上修改节点的名称、描述、类型和状态,再次点击节点可退出编辑模式
133+
5. **保存流程**:点击"保存流程"按钮,会触发`onSave`回调,将当前节点和连线数据传递出去
134+
6. **清空画布**:点击"清空"按钮,会清除所有节点和连线
135+
136+
## 注意事项
137+
138+
- 确保已安装`reactflow`依赖
139+
- 组件高度固定为600px,如需调整高度可通过CSS覆盖
140+
- 节点最小宽度为200px,以容纳编辑表单

0 commit comments

Comments
 (0)