架构设计
# 整体架构
RuleGo-Editor 基于 Vue 3 Composition API + LogicFlow + Element Plus 构建,采用两层分层架构:
- 画布层(
src/components/):单画布编辑能力。核心是RuleGoEditor.vue,把图形编辑能力(LogicFlow)与业务逻辑(RuleGo 规则链)通过适配层解耦。可独立使用。 - 工作区层(
src/workspace/):在画布之上套一层 IDE 外壳(顶栏 + 三 dock 面板容器 + 多 tab)。只有工作区层依赖画布层,反向不依赖——这是核心分层纪律,保证画布层可被任意宿主单独复用。
依赖只向上流动:components/ 不 import workspace/。PanelContext(传给面板的只读上下文)刻意不含 tab 池与 editor 实例表,工作区改内部实现不会波及面板。
+---------------------------------------------------------------+
| 工作区层 (src/workspace/) |
| |
| RuleGoWorkspace.vue (顶栏 + 三 dock + 多 tab,mode:'app') |
| ├─ TabBar / ChainSwitcher / GlobalSearch |
| ├─ drawers (Info / Settings / Users) |
| └─ composables (useTabPool/useDrafts/useServerStatus/...) |
+---------------------------------------------------------------+
| 依赖(单向:工作区 → 画布) |
+-------v-------------------------------------------------------+
| 画布层 (src/components/) |
| |
| +---------------------------------------------------------+ |
| | RuleGoEditor.vue | |
| | (画布主组件 - 组合与编排层) | |
| | 画布(LogicFlow) / AI 面板 / 调试控制台 / 属性面板 / Palette | |
| +---------------------------------------------------------+ |
| | shell/ RuleGoShell.vue (dock 布局外壳,可单独用) | |
| | panel/ PanelDock / PanelShell / useDockLayout | |
| | panels/ registry.js (BUILTIN_PANELS) / 内置面板 | |
| | core/ editorCapabilities (能力树) / actions / ... | |
| +---------------------------------------------------------+ |
+---------------------------------------------------------------+
| | |
+-------v----------------------v---------------------v----------+
| Hooks 层 (画布) |
| useLogicFlow useGraphData useGraphEvent useEditorSelection |
| useEditorContext useCanvasDebug useDebugWebSocket ... |
+---------------------------------------------------------------+
| | |
+-------v----------------------v---------------------v----------+
| 数据适配层 / 扩展层 / 基础设施 |
| RuleGoAdapter NodeAdapter GraphUtils |
| lf-node (NodeRedExtension/NodeRegistryExtension) toolbar |
| LogicFlow Element Plus i18n API 层 (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
下面先讲工作区层(重构新增),再讲画布层的核心模块。
# 工作区层(src/workspace/)
工作区层在画布之上套一层 IDE 外壳,全权管理规则链生命周期(加载/保存/草稿/dirty 跟踪/会话恢复)。宿主只需 <RuleGoWorkspace :options="..." /> 一行接入。不想引入完整工作区的宿主,可只用画布层的 RuleGoShell + RuleGoEditor(形态 A),或用 useSingleChainShell 自己组装面板上下文。
# RuleGoWorkspace.vue —— 工作区容器
文件路径:src/workspace/RuleGoWorkspace.vue
职责:
- 三 dock 面板容器布局:左 dock(资源管理 + 组件库)、右 dock(信息 + 配置 + 日志 + 文档 + 调试 + 诊断)、底 dock(运行记录 + 调试控制台)
- 多 tab 池:8 个上限 + LRU 卸载 + pinned + 拖拽重排(
useTabPool) - 顶栏:连接灯 / 保存 / 部署 / 用户菜单(
workspace.topBar控制) - 链切换与搜索:Ctrl+P 链切换浮层(
ChainSwitcher)、Ctrl+Shift+F 跨链搜索(GlobalSearch) - 生命周期托管:链加载/保存、本地草稿(
useDrafts)、保存冲突检测(useSaveConflict)、会话恢复、连通性探测(useServerStatus) - 能力开关消费:根据
capabilities.workspace.*决定渲染哪些外壳元素与面板
defineExpose 暴露:openChain(id) 命令式打开链、isDirty 等。
# RuleGoShell.vue —— dock 布局外壳
文件路径:src/components/shell/RuleGoShell.vue(位于画布层,单链与多链共用)
只提供「顶栏 + 三 dock + 画布槽」,不含画布本身——画布通过默认 slot 插入。形态 A 的宿主用它拿到 dock 面板,同时自己管规则链生命周期。配合 useSingleChainShell 从一个 RuleGoEditor 实例 ref 派生面板所需的 ctx。
# 能力树与面板注册表
# core/editorCapabilities.js —— 能力树
文件路径:src/components/core/editorCapabilities.js
用一组开关表达编辑器的「形态」:
- 粗粒度 mode:
'standalone'/'app'/'embedded'/'readonly',每个 mode 是一组能力默认值(profile) - 细粒度 capabilities:
ui.*(toolbar/sidebar/minimap/commandPalette/contextMenu/header/statusBar/chat)、editor.*(readOnly)、features.*(hostOwnsChainOpen)、persistence.*(saveToServer/syncUrlHash/warnOnUnload)、workspace.*(topBar/explorer/palette/.../admin)
resolveCapabilities(props) 按「显式 capabilities > 老 props(options.toolbar.showXxx / updateUrl / disabled)> mode 基线」优先级合并出完整能力对象。老集成方零改动。
# panels/registry.js —— 面板注册表
文件路径:src/components/panels/registry.js
dock 内容由 BUILTIN_PANELS 驱动(10 个内置面板:explorer/palette/information/config/run/log/doc/history/debug/problems)。能力开关 capabilities.workspace.* 只是「内置面板开/关」的简写。宿主加自己的面板必须走注册表:
import { RuleGoWorkspace, BUILTIN_PANELS } from '@rulego/editor'
;<RuleGoWorkspace :panels="[...BUILTIN_PANELS, myPanel]" />
2
面板按 PanelDefinition 契约实现(id/dock/title/order/capability/component/props(ctx)/on(ctx)/key(ctx))。PanelContext 只读暴露 activeChainId/chains/selection/lf/dsl/offline/...,刻意不暴露 tab 池与工作区内部状态——守分层纪律。resolvePanels(panels, capabilities) 负责「按 dock 分组 + 能力过滤 + 用户显隐 + 排序」。
# panel/ —— 面板原语
文件路径:src/components/panel/
PanelDock.vue—— 单个 dock 容器(左/右/底,可调尺寸/折叠)PanelShell.vue—— 单面板壳(标题栏 + 折叠 + 动作位)useDockLayout.js—— dock 布局状态(尺寸/开合/折叠/显隐),存 localStorage,支持命名空间隔离
这些原语从工作区层下沉到画布层,宿主自建外壳时也可单独复用。
# workspace composables
| composable | 文件路径 | 职责 |
|---|---|---|
useTabPool | workspace/composables/useTabPool.js | 多 tab 池:8 上限 + LRU 卸载 + pinned + 拖拽重排 |
useDrafts | workspace/composables/useDrafts.js | 本地草稿,按 username.chainId 分键 |
useServerStatus | workspace/composables/useServerStatus.js | 后端连通性探测(必须传 baseUrl),provide 一份供面板 inject |
useSaveConflict | workspace/composables/useSaveConflict.js | 保存冲突检测:fingerprintOf / isConflicting(内容指纹弱乐观锁) |
useNotify | workspace/composables/useNotify.js | 按 id 去重的通知:createNotifier / errorIdOf(离线时同类错误折叠) |
useWorkspaceBridge | workspace/composables/useWorkspaceBridge.js | 工作区与画布实例的桥接 |
画布层另导出 useSingleChainShell(components/hooks/useSingleChainShell.js):从一个 RuleGoEditor 实例 ref 派生面板所需的 ctx,让形态 A 宿主不必引入完整工作区就能用 dock 面板。
# 画布层核心模块
以下模块均在
src/components/下,可脱离工作区层独立使用。
# 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:import 导入规则链
rulego-editor:update 更新规则链属性
rulego-editor:save 保存
rulego-editor:setting 更新设置
rulego-editor:reset 重置
rulego-editor:deleteSelects 删除选中元素
rulego-editor:showEditPanel 显示编辑面板
rulego-editor:fullScreen 切换全屏
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)
搜索 / 文档 / 诊断 / 面板折叠:
rulego-editor:search 跨链搜索
rulego-editor:toggleDocBrowser 切换文档浏览器
rulego-editor:toggleTopologyPanel 切换链路诊断面板
rulego-editor:topologyProblems 拓扑问题列表
rulego-editor:toggleSidebar 切换组件面板折叠(可带 {expanded})
rulego-editor:sidebarToggled 折叠态回传(把手点击/Ctrl+B 同样发)
扩展点:
rulego-editor:start 编辑器启动完成
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
完整事件常量表与监听方式见 事件系统。注意区分两套通信:LogicFlow eventCenter(编辑器内部各独立 Vue 应用实例之间)与 Vue emit(
RuleGoEditor对宿主,如saveOk/dirty-change/open-chain等)。
# 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。