分组节点
分组节点 (GroupNode) 是 RuleGo-Editor 中一种特殊的节点类型,可以包含其他节点。主要用于实现循环和分组逻辑,如 for 循环、while 循环、groupFilter 过滤器组、groupAction 节点组等场景。
# 分组节点类型
| 节点类型 | 说明 | 典型用途 |
|---|---|---|
| for | for 循环节点 | 对数据集合进行遍历处理 |
| while | while 循环节点 | 条件循环处理 |
| groupFilter | 过滤器组节点 | 多个过滤条件组合 |
| groupAction | 节点组节点 | 多个动作并行执行 |
# 分组节点特性
# 基本特性
分组节点具有以下核心特性:
| 特性 | 默认值 | 说明 |
|---|---|---|
| 默认尺寸 | 300x200 | 节点初始宽高 |
| resizable | true | 支持拖拽调整大小 |
| collapsible | true | 支持折叠/展开子节点 |
| isRestrict | true | 子节点限制在组内移动 |
| autoResize | true | 根据子节点自动调整大小 |
# 锚点配置
分组节点的锚点布局:
- 左侧锚点:用于输入连接,接收来自上游节点的数据
- 右侧锚点:用于输出连接,向下游节点传递数据
- 折叠时:锚点位置自动调整到节点边缘,保持连接可用
┌─────────────────────────┐
──> │ │ ──>
│ Group Node │
──> │ │ ──>
└─────────────────────────┘
2
3
4
5
# 子节点限制
以下节点类型不能加入分组:
| 节点类型 | 原因 |
|---|---|
| endpoint-node | 端点节点作为独立入口,不应被分组 |
| start-node | 起始节点作为流程起点,不应被分组 |
尝试将这些节点拖入分组时,编辑器会自动拒绝该操作。此外,带「组外连出线」的节点也不能拖入分组(边界守卫,见循环组入口与边界守卫)。
# 分组节点事件
分组节点在节点加入或移除时会触发相应事件,用于自动维护分组字段。
# group:add-node 事件
当节点被拖入分组时触发:
lf.on('group:add-node', ({ data, childId }) => {
// data: 分组节点的数据
// childId: 被添加的子节点 ID
// 自动处理:
// 1. 子节点 additionalInfo.parentGroupId 被设置为分组节点 ID
// 2. 分组字段(如 groupAction.nodeIds)被自动更新
// 3. for/while 循环组:入组连线自动改接到循环容器;入口未设置时首条入组连线成为起点连线
})
2
3
4
5
6
7
8
9
# group:remove-node 事件
当节点从分组移除时触发:
lf.on('group:remove-node', ({ data, childId }) => {
// data: 分组节点的数据
// childId: 被移除的子节点 ID
// 自动处理:
// 1. 子节点 additionalInfo.parentGroupId 被清除
// 2. 分组字段被自动更新
})
2
3
4
5
6
7
8
# 事件使用示例
// 监听节点加入分组,执行自定义逻辑
lf.on('group:add-node', ({ data, childId }) => {
console.log(`节点 ${childId} 加入分组 ${data.id}`)
// 可在此处执行额外的业务逻辑
// 如:验证节点类型、更新外部状态等
})
// 监听节点移除分组
lf.on('group:remove-node', ({ data, childId }) => {
console.log(`节点 ${childId} 从分组 ${data.id} 移除`)
})
2
3
4
5
6
7
8
9
10
11
12
# 分组字段自动填充
分组字段自动填充功能会在节点加入/移除分组时,自动更新分组节点的配置字段。
# 配置方式
在组件的 i18n 配置文件(i18n/zh/index.js)中,通过 component.strategy 字段配置填充策略:
{
'groupAction': {
nodeIds: {
label: '组内节点ID列表',
component: {
type: 'select',
multiple: true,
optionsSource: 'canvasNodes',
strategy: 'allNodes' // 填充策略
}
},
nodeType: 'group-node',
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 填充策略
| 策略 | 说明 | 配置字段示例 |
|---|---|---|
| allNodes | 填充所有子节点,始终跟踪成员列表 | nodeIds: ["node_1", "node_2"] |
# allNodes 策略
适用于需要指定多个节点的场景,如 groupAction / groupFilter 的 nodeIds 字段:
- 收集组内所有节点的 ID
- 节点加入时自动添加到列表,节点移除时自动从列表移除
for/while 的
do字段不走自动填充:多根成员时按坐标等规则猜测会静默选错循环入口,自动推导已移除。入口由画布上的「起点连线」手势设置,见下节。
# 循环组入口与边界守卫
循环体入口记录在 for/while 节点 configuration 的 do 字段。画布上分组左上角渲染一个「起点」伪节点(纯视觉元素,不进 LF 模型、不进 DSL),它通往入口成员的连线即「起点连线」:
- 设置/更改入口:从起点伪节点拖线到组内成员;或点一下起点再点成员节点(Esc / 点空白取消拾取)
- 表单只读展示:节点配置面板的「处理节点ID」由起点连线决定,不可直接编辑
- 拖入即接:成员拖入循环组时若入口未设置,首条入组连线自动成为起点连线
引擎侧循环体 = 从 do 节点沿连线可达的子图,分组矩形只是视觉容器——组内外跨接会让组外节点被逐轮迭代执行、组内节点被主链路直触。因此编辑器对跨界连线做边界守卫:
| 操作 | 行为 |
|---|---|
| 拖入带「组外连出线」的节点 | 拒绝入组,提示后还原 |
| 拖出带「组内连线」的节点 | 拒绝出组,提示后平移还原 |
| 组内外节点间新建连线 | 拦截;嵌套分组的成员间放行,两个并列分组间拦截 |
| 成员拖入循环组时已有「组外→成员」连线 | 修复式搬入:连线自动改接到循环容器,成为循环触发线 |
| 成员与所在循环容器互连 | 自动移除并提示(组内节点不能触发所在循环、循环结束不能回流组内) |
存量跨组连线不拦截:加载历史 DSL 时原样保留(带内部标记跳过校验)。入口指向非成员时
do字段不清空(撤销恢复成员后连线自动回来),渲染层不画线、保存校验兜底。
# 使用示例
# for 循环节点
for 循环节点用于遍历数据集合:
- 从组件面板拖入 for 节点到画布
- 在 for 节点内部添加处理节点(如 filter、transform 等)
- 从分组左上角「起点」伪节点拖线到入口成员,设置循环体入口(写入
do字段) - 配置 for 节点的其他属性(循环表达式等)
┌─ for ──────────────────────────┐
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ filter │──>│ transform│ │
│ └─────────┘ └─────────┘ │
│ │
└─────────────────────────────────┘
2
3
4
5
6
7
生成的数据结构:
{
"type": "for",
"configuration": {
"do": "node_filter_1"
}
}
2
3
4
5
6
# groupAction 节点组
groupAction 节点用于并行执行多个动作:
- 从组件面板拖入 groupAction 节点
- 在组内添加多个动作节点
- 系统自动将所有节点 ID 填充到
nodeIds字段 - 连接组的输入输出端口
┌─ groupAction ─────────────┐
│ │
──> │ ┌─────────┐ │ ──>
│ │ action1 │ │
│ └─────────┘ │
│ ┌─────────┐ │
│ │ action2 │ │
│ └─────────┘ │
│ │
└───────────────────────────┘
2
3
4
5
6
7
8
9
10
生成的数据结构:
{
"type": "groupAction",
"configuration": {
"nodeIds": ["node_action1_1", "node_action2_1"]
}
}
2
3
4
5
6
# groupFilter 过滤器组
groupFilter 节点用于组合多个过滤条件:
- 拖入 groupFilter 节点
- 添加多个 filter 节点到组内
- 根据业务需求配置过滤逻辑
# 数据格式
分组节点及其子节点的完整数据格式:
{
"nodes": [
{
"id": "group_1",
"type": "for",
"x": 400,
"y": 200,
"properties": {
"width": 300,
"height": 200,
"collapsed": false
},
"configuration": {
"do": "node_1"
}
},
{
"id": "node_1",
"type": "filter",
"x": 420,
"y": 250,
"properties": {},
"additionalInfo": {
"parentGroupId": "group_1"
}
}
],
"edges": []
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 关键字段说明
| 字段 | 位置 | 说明 |
|---|---|---|
| parentGroupId | 子节点 additionalInfo | 标识节点所属的分组 ID |
| do | for 节点 configuration | 循环体入口成员 ID,由画布「起点连线」设置 |
| nodeIds | groupAction 节点 configuration | allNodes 策略的所有节点 ID 列表 |
| collapsed | 分组节点 properties | 分组是否折叠 |
| width/height | 分组节点 properties | 分组的当前尺寸 |
# 注意事项
- 子节点移动限制:当
isRestrict为 true 时,子节点不能被拖出分组边界 - 自动调整大小:
autoResize启用时,分组大小会根据子节点位置自动扩展 - 折叠状态:折叠分组时,子节点会被隐藏,但连接关系保持不变
- 删除分组:删除分组节点时,组内子节点会被一同删除
- 复制分组:复制分组节点时,组内子节点会被一同复制,并生成新的 ID
# 已知限制
- 嵌套分组不跨保存/重载持久化:保存后重新打开,嵌套层级不保留,需重新编组
- undo 不还原组成员:撤销操作不会把成员从分组中移除/恢复,需手动调整