架构设计
# 整体架构
RuleGo-Editor 基于 Vue 3 Composition API + LogicFlow + Element Plus 构建,采用分层架构设计。核心思路是将图形编辑能力(LogicFlow)与业务逻辑(RuleGo 规则链)通过适配层解耦,使编辑器既能独立运行,也能灵活对接后端服务。
+---------------------------------------------------------------+
| RuleGoEditor.vue |
| (主组件 - 组合与编排层) |
+---------------------------------------------------------------+
| |
| +------------------+ +------------------+ +--------------+ |
| | 画布区域 | | AI 对话面板 | | 调试控制台 | |
| | (LogicFlow) | | (ChatPanel) | |(DebugConsole)| |
| +------------------+ +------------------+ +--------------+ |
| |
| +------------------+ +------------------+ +--------------+ |
| | 节点属性面板 | | 边属性面板 | | 组件面板 | |
| |(NodeProperty*) | |(EdgeProperty*) | | (Palette) | |
| +------------------+ +------------------+ +--------------+ |
+---------------------------------------------------------------+
| | |
+-------v----------------------v---------------------v----------+
| Hooks 层 |
| |
| useLogicFlow useGraphData useGraphEvent |
| useEditorSelection useEditorContext useCanvasDebug |
| useDebugWebSocket |
+---------------------------------------------------------------+
| | |
+-------v----------------------v---------------------v----------+
| 数据适配层 |
| |
| RuleGoAdapter NodeAdapter GraphUtils |
+---------------------------------------------------------------+
| | |
+-------v----------------------v---------------------v----------+
| 扩展层 |
| |
| NodeRedExtension (lf-node) ControlExtension (toolbar) |
| 自定义节点注册 工具栏渲染 |
+---------------------------------------------------------------+
| | |
+-------v----------------------v---------------------v----------+
| 基础设施 |
| |
| LogicFlow Element Plus i18n API 层 |
+---------------------------------------------------------------+
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
30
31
32
33
34
35
36
37
38
39
40
41
42
# 核心模块
# 1. 主组件 RuleGoEditor.vue
主组件是整个编辑器的入口和编排中心,负责:
- 布局管理:包含画布区域、属性面板(节点/边的 Drawer 和 Dialog)、AI 对话面板、调试控制台
- 实例化核心对象:创建 I18n、NodeAdapter、RuleGoAdapter 等实例
- Hook 组合:通过 useLogicFlow、useGraphData、useGraphEvent 等 Hook 组合各模块能力
- 上下文注入:通过 provide/inject 向子组件和 Hook 共享编辑器状态
- 键盘快捷键:配置 Delete、Ctrl+C/V 等快捷键
- 生命周期管理:挂载时加载组件并初始化,卸载时清理事件监听
主组件通过 defineExpose 向外部暴露以下方法:
| 方法 | 说明 |
|---|---|
| render | 渲染规则链数据到画布 |
| save | 触发保存操作 |
| getData | 获取当前画布的规则链数据 |
| setLocales | 设置国际化语言包 |
| reloadComponents | 重新加载组件列表 |
# 2. 数据适配层
# RuleGoAdapter.js
负责 RuleGo 数据格式与 LogicFlow 图数据格式之间的双向转换,是编辑器与 RuleGo 后端之间的桥梁。
adapterIn(输入适配):将 RuleGo 格式数据转换为 LogicFlow 可渲染的 { nodes, edges } 格式。
RuleGo 数据格式:
{
ruleChain: { id, name, additionalInfo },
metadata: {
nodes: [...],
endpoints: [...],
connections: [...]
}
}
+-------------------+
| RuleGoAdapter |
| .adapterIn() |
+--------+----------+
|
v
LogicFlow 数据格式:
{
nodes: [{ id, type, x, y, text, properties }],
edges: [{ id, type, sourceNodeId, targetNodeId, text, properties }]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
转换过程中会:
- 自动添加默认输入节点(start-node)
- 根据组件定义确定节点的画布类型(simple-node、endpoint-node、group-node 等)
- 将节点坐标从 additionalInfo.layoutX/layoutY 读取
- 合并 endpoint 连接和普通节点连接
- 处理多关系类型边的标签拼接
adapterOut(输出适配):将 LogicFlow 图数据转换回 RuleGo 格式。
转换过程中会:
- 从 lfId 映射回业务 ID(model.id)
- 将画布坐标写入 additionalInfo.layoutX/layoutY
- 处理输入端节点的路由路径(router.to.path)
- 写入分组节点的 parentGroupId
- 按规则排列节点顺序(第一个节点放在最前面)
# NodeAdapter.js
负责节点组件的适配处理,包括:
- 国际化注入:从 i18n 语言包中读取组件的中文名称、描述、关系类型标签
- 分组归类:将组件按 category 归入不同的侧边栏分组
- 表单校验规则:为 int/float 类型字段自动添加数值校验规则
- 内置选项注入:处理 builtins 中的下拉选项数据(如 nodePool 共享节点池)
- AI Provider 注入:为包含 url+model 字段的组件自动注入 AI 服务商选择下拉框
- 输入节点注入:自动向 endpoints 分组注入 start-node
# GraphUtils.js
图形工具函数集合,提供:
genId()- 生成唯一 ID(基于 nanoid)createEdge()- 创建边数据,自动绑定左右锚点getNodeSeq()- 从节点 ID 提取序号getRelationTypeOptionsFromNode()- 从节点配置动态获取连接类型选项getEndpointConnections()- 解析输入端节点的路由连接关系updateEndpointRouterToPath()- 将输入端边连接写入路由路径validator- 画布数据校验器
# 3. Hooks 层
Hooks 层采用 Vue 3 Composition API 模式,将编辑器的不同关注点拆分为独立的可复用函数。
# useLogicFlow.js - LogicFlow 实例管理
文件路径:src/components/hooks/useLogicFlow.js
职责:
- 创建和销毁 LogicFlow 实例
- 注册插件(SelectionSelect、Snapshot、MiniMap、DynamicGroup、NodeRedExtension、ControlExtension)
- 合并外部传入的 options 配置
- 挂载兼容性方法(getRuleChain、getEditorSetting、rulegoEditor)
- 初始化分组事件监听器
初始化流程:
options (默认配置)
|
v
合并 inputOptions (外部配置)
|
v
new LogicFlow(options)
|
v
挂载兼容方法 + rulegoEditor 命名空间
|
v
返回 lf (shallowRef)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# useGraphData.js - 图形数据管理
文件路径:src/components/hooks/useGraphData.js
职责:
- 组件列表加载(内置组件 + API 动态组件)
- 画布渲染(render)和清空(clear)
- 保存操作(save),包含业务校验和 API 调用
- 操作快照管理(undos/redos)
- 脏数据标记(isDirty)
- 组件热重载(reloadComponents)
组件加载流程:
1. 检查 options.components 是否已有数据
|
+-- 有 --> 直接适配
|
+-- 无 --> 使用 defaultComponents 初始化
|
2. 调用 adapterComponents() 进行国际化和分组适配
|
3. toComponentList() 转为扁平映射 { [type]: componentDef }
|
4. initLfCallback() 初始化 LogicFlow 实例
|
5. 如果 loadComponentsFromApi=true,异步加载 API 组件
|
6. API 数据返回后,重新适配并重建 LF 实例
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# useGraphEvent.js - 图形事件管理
文件路径:src/components/hooks/useGraphEvent.js
职责:
- 注册画布交互事件(节点点击/双击、边点击/双击、空白点击、DND 拖入、节点拖动)
- 注册自定义业务事件(NEW、OPEN、UPDATE、SAVE、SETTING 等)
- 处理历史记录变化(history:change),维护 undos/redos 和 isDirty 状态
- Alt+拖拽:允许将节点从分组中拖出
- 分组折叠状态检测,避免折叠时误触发边添加事件
# useEditorSelection.js - 选择逻辑管理
文件路径:src/components/hooks/useEditorSelection.js
职责:
- 维护当前选中的节点/边及其模型数据
- 管理属性面板的显示状态(Drawer/Dialog 的开关)
- 节点属性提交和取消逻辑
- 边属性提交和取消逻辑
- 删除选中元素(包含业务规则校验:至少保留一个输入端)
- 节点克隆(支持剪贴板和画布内复制)
# useEditorContext.js - 编辑器上下文
文件路径:src/components/hooks/useEditorContext.js
通过 Vue 的 provide/inject 机制实现跨组件和 Hook 的状态共享。
provideEditorContext({
i18n, // 国际化实例
nodeAdapter, // 节点适配器
adapter, // RuleGo 适配器
lf, // LogicFlow 实例 (shallowRef)
container, // 画布容器 DOM 引用
ruleChain, // 当前规则链数据 (ref)
nodeComponents, // 节点组件映射 (ref)
settingOptions, // 编辑器设置 (ref)
options, // 编辑器配置 (reactive)
initData, // 初始化数据 (reactive)
emit, // 事件发射器
})
2
3
4
5
6
7
8
9
10
11
12
13
任何子组件或 Hook 只需调用 useEditorContext() 即可获取上述全部上下文,无需层层传递 props。
# useCanvasDebug.js - 画布调试可视化
文件路径:src/components/hooks/useCanvasDebug.js
职责:
- 监听调试数据事件(DEBUG_DATA),根据 flowType 设置节点状态样式
- processing 状态:蓝色高亮边框
- success 状态:绿色高亮边框
- error 状态:红色高亮边框
- 调试期间标记
_debugHighlighting,防止 history:change 误触发 isDirty - 提供 clearHighlight 方法清除所有调试高亮
# useDebugWebSocket.js - WebSocket 调试连接
文件路径:src/components/hooks/useDebugWebSocket.js
职责:
- 建立与后端的 WebSocket 连接,接收实时调试日志
- 支持自动重连(指数退避,最大 10 次)
- 通过 JWT token 进行身份认证
- 维护连接状态(connecting / connected / disconnected / error)
- 数据缓冲(最多保留 500 条,超出后裁剪)
# 4. 扩展层
# lf-node/index.js - NodeRedExtension
LogicFlow 插件,在构造函数中注册所有自定义节点类型和连线类型:
注册的节点类型:
start-node --> StartNode (默认输入节点)
end-node --> EndNode (结束节点)
endpoint-node --> EndpointNode (输入端节点)
simple-node --> SimpleNode (通用业务节点)
chain-node --> ChainNode (子规则链节点)
group-node --> GroupNode (分组节点)
agent-node --> AgentNode (AI 代理节点)
agent-orchestration-node --> AgentOrchestrationNode (智能体编排节点)
comment-node --> CommentNode (注释节点)
注册的连线类型:
flow-link --> FlowLink (贝塞尔曲线连线)
设置默认边类型为 flow-link
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在 render 方法中创建独立的 Vue 应用实例来渲染组件面板(sidebar/Sidebar.vue),并注册 Element Plus。
# toolbar/index.js - ControlExtension
LogicFlow 插件,在构造函数中创建独立的 Vue 应用实例来渲染工具栏(Toolbar.vue),并注册 Element Plus。
# 5. 自定义节点
所有自定义节点都继承自 BaseNode,遵循 LogicFlow 的 model/view 分离模式。
# BaseNode.js - 基础节点
提供所有节点的公共能力:
Model 层(RedNodeModel):
- 默认尺寸 120x30,圆角 5px
- 定义左右锚点(left 用于连入,right 用于连出)
- 连接校验规则:只允许右侧连出、左侧连入、禁止重复连线、禁止连到输入类节点、禁止跨分组连线
- 文本超长自动截断并显示省略号
- 调试状态样式(processing/success/error 高亮)
View 层(RedNode):
- 渲染矩形节点 + 左侧图标区域 + 分隔线
- 自定义锚点形状(矩形)
# 节点类型继承关系
BaseNode (BaseNode.js)
|
+-- SimpleNode (simple-node) 通用业务节点,带图标
+-- StartNode (start-node) 默认输入节点,仅右侧锚点
+-- EndNode (end-node) 结束节点,仅左侧锚点
+-- EndpointNode (endpoint-node) 输入端节点,仅右侧锚点
+-- ChainNode (chain-node) 子规则链节点,仅左侧锚点
+-- AgentNode (agent-node) AI 代理节点
+-- CommentNode (comment-node) 注释节点,无锚点
RectNode (LogicFlow 内置)
|
+-- AgentOrchestrationNode (agent-orchestration-node) 智能体编排节点
dynamicGroup (LogicFlow 扩展)
|
+-- GroupNode (group-node) 分组节点,支持折叠/展开/调整大小
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# GroupNode.js - 分组节点
基于 LogicFlow 的 DynamicGroup 扩展,额外提供:
- 可折叠/展开,折叠时尺寸 120x30
- 可调整大小,支持拖拽边框
- 子节点限制:禁止 endpoint-node 和 start-node 加入分组
- 锚点动态更新:折叠/展开时实时调整左右锚点位置
- 边重定向:折叠/展开后强制更新相连边的起止点
- Alt+拖拽:按住 Alt 可将节点从分组中拖出
# FlowLink.js - 连线
基于 LogicFlow 的 BezierEdge(贝塞尔曲线),额外提供:
- 调试高亮样式(蓝色加粗)
- 选中/悬浮样式(橙色加粗)
- 文本标签自动换行和背景框
# 6. 配置与国际化
# config/config.js
全局配置管理,包括:
- AI 多轮对话配置(消息历史上限、上下文窗口、存储 key 前缀等)
- AI 服务商列表(Gitee AI、DeepSeek、通义千问、智谱 AI、OpenAI、Ollama)
- 编辑器配置持久化(localStorage)
- 配置 URL 更新
配置优先级:window.rulegoEditorConfig > localStorage > 默认值
# constants/events.js
编辑器自定义事件常量,用于 LogicFlow eventCenter 的事件通信:
命令事件(Toolbar --> 核心):
rulego-editor:new 新建规则链
rulego-editor:open 打开规则链
rulego-editor:update 更新规则链属性
rulego-editor:save 保存
rulego-editor:setting 更新设置
rulego-editor:reset 重置
rulego-editor:deleteSelects 删除选中元素
rulego-editor:showEditPanel 显示编辑面板
rulego-editor:loadComponents 加载组件
通知事件(核心 --> Toolbar/Sidebar):
rulego-editor:saveOk 保存成功
rulego-editor:componentOk 组件加载完成
rulego-editor:sidebarOk 侧边栏就绪
rulego-editor:deleted 元素已删除
调试事件:
rulego-editor:toggleDebugConsole 切换调试控制台
rulego-editor:clearDebugHighlight 清除调试高亮
rulego-editor:debugData 调试数据到达
rulego-editor:toggleRuleChainChat 切换 AI 对话面板
rulego-editor:openNodeDetail 打开节点详情(调试 tab)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# i18n/
国际化文件,采用分模块组织:
src/components/i18n/
zh/
index.js 主入口,导出 locales 对象
ai.js AI 相关文本
endpoint.js 输入端节点文本
extend.js 扩展节点文本
locales_registry.js 扩展语言包注册
2
3
4
5
6
7
I18n 类支持动态注册语言包,允许通过 options.locales 或 extensions 注入自定义国际化文本。
# 数据流
# 数据输入流(RuleGo --> 画布)
外部传入 data prop
|
v
RuleGoAdapter.adapterIn(data)
|
+-- 读取 ruleChain 元数据(id、name、additionalInfo)
+-- 添加默认输入节点(start-node)
+-- 遍历 metadata.nodes,转换为 LF 节点
+-- 遍历 metadata.endpoints,转换为 endpoint-node
+-- 遍历 metadata.connections,转换为边
+-- 处理多关系类型边的标签合并
|
v
{ nodes: [...], edges: [...] }
|
v
lf.render(data) 渲染到画布
|
v
restoreParentGroup() 恢复分组关系
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 数据输出流(画布 --> RuleGo)
lf.getGraphData()
|
v
RuleGoAdapter.adapterOut(lf, logicFlowData)
|
+-- 遍历边,从 lfId 映射回业务 ID
+-- 处理 endpoint 路由路径
+-- 遍历节点,写入坐标到 additionalInfo
+-- 写入分组的 parentGroupId
+-- 排列节点顺序
|
v
{ ruleChain: {...}, metadata: { nodes, endpoints, connections } }
|
v
GraphUtils.validator.validate(data) 校验
|
v
saveWorkflow(id, data) 调用 API 保存
|
v
emit('saveOk', data) 通知外部
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 组件加载流
1. loadComponentsAndInitLf()
|
v
2. 检查 options.components 是否已有数据
|
+-- 有 --> adapterComponents(options.components)
+-- 无 --> adapterComponents(defaultComponents)
|
v
3. NodeAdapter.adapterComponents()
|
+-- 注册 i18n 语言包
+-- 遍历 nodes 和 endpoints
+-- adapterComponentsLocal(): 注入中文 label、描述
+-- adapterBuiltins(): 注入下拉选项
+-- adapterRules(): 添加表单校验规则
+-- 按 category 分组
|
v
4. toComponentList() 转为扁平映射
|
v
5. initLfCallback() 创建 LogicFlow 实例
|
v
6. (可选) 从 API 异步加载组件
|
+-- GET /api/v1/components
+-- 重新适配
+-- 重建 LF 实例
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
30
# 事件通信机制
编辑器内部存在两套事件通信机制:
# 1. Vue 事件(emit)
用于主组件与父组件之间的通信:
RuleGoEditor (子组件)
|
+-- emit('saveOk', data) 保存成功
+-- emit('saveError', data) 保存失败
+-- emit('reset', data) 重置
|
v
父组件 (宿主应用)
2
3
4
5
6
7
8
# 2. LogicFlow eventCenter
用于编辑器内部各模块之间的通信。由于工具栏和侧边栏是独立的 Vue 应用实例(通过 createApp 创建),无法直接通过 Vue 的 provide/inject 共享状态,因此使用 LogicFlow 的 eventCenter 作为事件总线。
Toolbar (独立 Vue 应用)
|
+-- lf.graphModel.eventCenter.emit(EDITOR_EVENTS.SAVE)
|
v
useGraphEvent (事件监听)
|
+-- lf.on(EDITOR_EVENTS.SAVE, () => save())
|
v
useGraphData.save()
2
3
4
5
6
7
8
9
10
11
# 3. provide/inject 上下文
用于主组件内部的 Hook 和子组件之间的状态共享,通过 useEditorContext() 统一获取。
# 扩展机制
# 1. 组件扩展
通过 options.components 传入自定义组件定义,或通过 API 动态加载。组件定义格式参见 节点组件 文档。
# 2. 国际化扩展
通过 options.locales 或 extensions 注入自定义语言包,支持覆盖默认的组件名称、描述、关系类型标签等。
# 3. 工具栏扩展
通过 options.toolbar 配置各按钮的显示/隐藏。工具栏作为独立 Vue 应用渲染,通过 LogicFlow eventCenter 与核心逻辑通信。
# 4. LogicFlow 插件扩展
通过 options.plugins 传入 LogicFlow 插件列表,可注册自定义节点类型、连线类型和扩展功能。
# 5. Token 管理扩展
通过 options.getToken 和 options.clearToken 注入自定义的 token 管理函数,用于对接宿主应用的认证体系。
# 技术要点
# shallowRef 与 LogicFlow 实例
LogicFlow 实例使用 shallowRef 而非 ref 包装,因为 LogicFlow 是一个复杂的类实例,深度响应式代理会导致性能问题和意外行为。
# 独立 Vue 应用实例
侧边栏和工具栏通过 createApp 创建独立的 Vue 应用实例,各自注册 Element Plus。这是因为 LogicFlow 的插件 render 方法需要挂载到 DOM overlay 上,无法直接复用主应用的 Vue 实例。
# 脏数据追踪
通过监听 history:change 事件自动标记 isDirty,在 render 和 save 成功后重置。浏览器关闭时通过 beforeunload 事件提示用户保存。
# 调试高亮隔离
调试高亮期间设置 _debugHighlighting 标记,防止节点样式变更触发 history:change 导致 isDirty 被误设为 true。