@blueking/flow-canvas
v0.0.13
Published
通用流程画布编辑引擎
Downloads
705
Readme
@blueking/flow-canvas 使用文档
概述
@blueking/flow-canvas 是一个通用流程画布编辑引擎,基于 AntV X6 构建,提供数据驱动的图编辑能力。适用于 DAG / 流程图 / 工作流等场景。
补充文档:
- 使用说明:
docs/usage.md - 设计说明:
docs/design.md - 测试计划:
docs/testing.md
安装
npm install @blueking/flow-canvas项目中另外还需要安装以下前置依赖
npm install @antv/x6 @antv/x6-plugin-minimap @antv/x6-plugin-selection @antv/x6-plugin-snapline @antv/x6-plugin-dnd @antv/x6-vue-shape modern-screenshot vue快速开始
零配置启动
使用 createDefaultSchema 可零配置启动一个可拖拽、可连线的画布:
<template>
<CanvasLayout :editor="editor" :palette-items="paletteItems">
<CanvasRuntime :editor="editor" @ui-event="handleUiEvent" />
<CanvasToolbar :editor="editor" />
</CanvasLayout>
</template>
<script setup lang="ts">
import {
useCanvasEditor,
CanvasRuntime,
CanvasLayout,
CanvasToolbar,
createDefaultSchema,
createEmptyFlowModel,
searchPlugin,
selectionPlugin,
snaplinePlugin,
type CanvasUiEvent,
} from '@blueking/flow-canvas';
import '@blueking/flow-canvas/style';
const { schema, paletteItems } = createDefaultSchema();
const editor = useCanvasEditor({
initialFlowModel: createEmptyFlowModel(),
schema,
plugins: [selectionPlugin(), snaplinePlugin(), searchPlugin()],
});
function handleUiEvent(event: CanvasUiEvent) {
console.log('UI 事件:', event.type);
}
</script>传入 editor prop 后,CanvasLayout 会自动在侧边栏渲染内置节点面板 CanvasNodePalette,用户可直接拖拽节点到画布。
自定义 Schema
如需完全控制节点/边类型,可手动定义 CanvasSchema:
<template>
<CanvasLayout>
<template #sidebar>
<MySidebar :editor="editor" />
</template>
<CanvasRuntime :editor="editor" @ui-event="handleUiEvent" />
<CanvasToolbar :editor="editor" />
</CanvasLayout>
</template>
<script setup lang="ts">
import {
useCanvasEditor,
CanvasRuntime,
CanvasLayout,
CanvasToolbar,
createBuiltinEdgeTypes,
selectionPlugin,
snaplinePlugin,
connectionValidatorPlugin,
type CanvasSchema,
type CanvasUiEvent,
type FlowModel,
} from '@blueking/flow-canvas';
import '@blueking/flow-canvas/style';
import MyTaskNode from './components/my-task-node.vue';
const builtinEdgeTypes = createBuiltinEdgeTypes();
const schema: CanvasSchema = {
nodeTypes: {
task: {
component: MyTaskNode,
getSize: () => ({ width: 180, height: 60 }),
},
},
edgeTypes: {
bezier: builtinEdgeTypes.bezier,
},
defaultEdgeType: 'bezier',
};
const initialFlowModel: FlowModel = {
version: '1.0',
nodes: {
node1: { id: 'node1', type: 'task', position: { x: 100, y: 200 }, label: '任务 1' },
node2: { id: 'node2', type: 'task', position: { x: 400, y: 200 }, label: '任务 2' },
},
edges: {
edge1: {
id: 'edge1',
source: { nodeId: 'node1', portId: 'right' },
target: { nodeId: 'node2', portId: 'left' },
},
},
};
const editor = useCanvasEditor({
initialFlowModel,
schema,
plugins: [
selectionPlugin(),
snaplinePlugin(),
connectionValidatorPlugin((ctx) => {
if (ctx.sourceNode.id === ctx.targetNode.id) {
return { valid: false, reason: '不允许自环连线' };
}
return { valid: true };
}),
],
onFlowModelChange(event) {
console.log('流程变更:', event.flowModel);
},
});
function handleUiEvent(event: CanvasUiEvent) {
if (event.type === 'node.click') {
console.log('点击了节点:', event.nodeId);
}
}
</script>这个示例刻意省略了 getPorts,因为当前版本在未提供 getPorts / node.ports 时会自动补齐默认四向端口,普通自定义节点通常只需要关注 component 和 getSize。
核心 API
useCanvasEditor(options)
创建画布编辑器实例。
CanvasEditorOptions
| 参数 | 类型 | 必填 | 默认 | 说明 |
| ----------------- | ---------------------- | ---- | -------- | --------------------------------------- |
| initialFlowModel | FlowModel | 是 | - | 初始流程模型 |
| schema | CanvasSchema | 是 | - | 节点/边类型定义 |
| plugins | CanvasPlugin[] | 否 | [] | 插件列表 |
| mode | CanvasMode | 否 | 'edit' | 'edit' / 'readonly' / 'thumbnail' |
| historyOptions | CanvasHistoryOptions | 否 | - | 历史配置 |
| idGenerator | IdGenerator | 否 | 内置生成 | 自定义节点/边 ID 生成函数 |
| onCommandResult | (result) => void | 否 | - | 命令执行结果回调 |
| onFlowModelChange | (event) => void | 否 | - | FlowModel 变更回调 |
CanvasEditorContext(返回值)
| 属性/方法 | 类型 | 说明 |
| ------------------------- | --------------------------------------------- | ------------------------------ |
| flowModel | Readonly<Ref<FlowModel>> | 当前流程模型(响应式) |
| history | CanvasHistory | 历史管理器 |
| schema | CanvasSchema | Schema 定义 |
| mode | Ref<CanvasMode> | 当前模式 |
| selectionMode | Ref<boolean> | 是否启用框选模式 |
| api | Ref<CanvasApi \| null> | 画布 API(Runtime 挂载后可用) |
| toolbarItems | Ref<CanvasToolbarItem[]> | 插件聚合的工具栏项 |
| idGenerator | IdGenerator | 节点/边 ID 生成函数 |
| extendedApi | Record<string, Function> | 插件扩展的 API |
| executeCommand(envelope) | (CommandEnvelope) => CommandExecutionResult | 执行命令 |
| replaceFlowModel(model) | (FlowModel) => void | 整体替换流程模型 |
| setMode(mode) | (CanvasMode) => void | 切换模式 |
| setSelectionMode(enabled) | (boolean) => void | 切换框选模式 |
FlowModel 数据结构
interface FlowModel {
version?: '1.0'; // 数据模型版本标识,可选,未传时引擎自动补全为 '1.0'
nodes: Record<string, FlowNodeModel>; // 节点字典,key = nodeId
edges: Record<string, FlowEdgeModel>; // 边字典,key = edgeId
meta?: Record<string, unknown>; // 全局元数据
extensions?: Record<string, unknown>; // 扩展数据
}
interface FlowNodeModel {
id: string;
type: string; // 对应 schema.nodeTypes 的 key
position: { x: number; y: number };
label?: string;
ports?: FlowPortModel[]; // 业务自定义端口;未提供 getPorts() 时优先读取,缺省时引擎自动补四向默认端口
payload?: Record<string, unknown>; // 业务数据
extensions?: Record<string, unknown>; // 扩展数据
}
interface FlowEdgeModel {
id: string;
type?: string; // 对应 schema.edgeTypes 的 key
source: { nodeId: string; portId?: string };
target: { nodeId: string; portId?: string };
labels?: FlowEdgeLabelModel[];
payload?: Record<string, unknown>;
extensions?: Record<string, unknown>;
}CanvasSchema 配置
节点类型定义
interface CanvasNodeDefinition {
component: Component; // Vue 组件,渲染节点内容
getSize: (node) => { width; height };
getPorts?: (node) => FlowPortModel[];
getBehavior?: (node, ctx) => NodeBehaviorConfig;
x6CellConfig?: Record<string, unknown>; // 透传给 X6 的额外配置
}节点 Vue 组件内可通过 inject('getNode') 获取 X6 Node 实例,读取 node.getData() 访问 FlowNodeModel。
端口来源与 getPorts 选型
getSize 是必填;getPorts 和 getBehavior 都是可选。
运行时解析节点端口的优先级如下:
definition.getPorts(node)node.ports- 默认四向端口(top/right/bottom/left)
这意味着:
- 不写
getPorts且node.ports也未提供时,引擎会自动补一套默认四向端口,位置位于四条边中点 - 默认 quick-add 会直接绑定到这套默认端口中的
group: 'right',因此普通自定义节点只定义component/getSize/getBehavior也能保持默认右侧交互 - 引擎内置的是四个方向端口组的默认位置和样式模板;默认兜底端口也复用这套约定
- 如果你想显式禁用端口,需要返回空数组,例如
getPorts: () => []或设置node.ports = []
推荐使用方式:
- 端口结构按节点类型固定时,用
getPorts - 端口结构需要根据当前节点数据动态计算时,用
getPorts - 端口本身属于业务数据、需要随
FlowModel持久化或导入导出时,用node.ports - 如果同时提供了
getPorts和node.ports,运行时以前者为准
边类型定义
interface CanvasEdgeDefinition {
router?: string | { name: string; args?: Record<string, unknown> };
connector?: string | { name: string; args?: Record<string, unknown> };
style?: (edge, state: EdgeRenderState) => EdgeStyle; // 动态样式
labelDraggable?: boolean;
x6EdgeConfig?: Record<string, unknown>; // 透传给 X6 的额外配置
// @experimental — 尚未完整实现,请勿在生产环境依赖
// labelRenderer?: Component;
}EdgeRenderState 包含 selected、highlighted、hovered 三个布尔值,style() 可据此返回不同的 stroke、strokeWidth、strokeDasharray。
注意:
labelRenderer字段在类型定义中存在但标记为@experimental,当前版本尚未完整实现。如需自定义边标签样式,建议通过x6EdgeConfig.defaultLabel配置 X6 原生标签 markup。
createDefaultSchema — 开箱即用的 Schema 工厂
createDefaultSchema 提供内置的节点类型和边类型定义,可零配置快速启动画布。
import { createDefaultSchema } from '@blueking/flow-canvas';
const { schema, paletteItems } = createDefaultSchema(options?);DefaultSchemaOptions
| 参数 | 类型 | 默认 | 说明 |
| --------------- | --------------------------------------- | ------------- | ---------------------------------------------- |
| nodeTypes | Record<string, DefaultNodeTypeConfig> | 7 种内置类型 | 自定义节点类型列表,传入后替换内置类型 |
| defaultEdgeType | string | 'manhattan' | 默认连线类型,内置 'manhattan' 和 'bezier' |
| edgeTypes | Record<string, CanvasEdgeDefinition> | - | 额外/覆盖的边类型定义,会与内置类型合并 |
返回值
| 属性 | 类型 | 说明 |
| ------------ | ------------------- | -------------------------------------------- |
| schema | CanvasSchema | 可直接传给 useCanvasEditor 的 Schema 定义 |
| paletteItems | NodePaletteItem[] | 可传给 CanvasLayout 的 paletteItems prop |
内置节点类型
不传 nodeTypes 参数时,默认包含 7 种节点:
| 类型 | 标签 | 尺寸 |
| ------------------------------ | ------------ | -------- |
| start | 开始 | 88 × 40 |
| end | 结束 | 88 × 40 |
| empty | 空节点 | 240 × 48 |
| parallel-gateway | 并行网关 | 64 × 64 |
| branch-gateway | 分支网关 | 64 × 64 |
| converge-gateway | 汇聚网关 | 64 × 64 |
| conditional-parallel-gateway | 条件并行网关 | 64 × 64 |
每种节点均使用内置 DefaultNode 组件渲染,自带上/右/下/左四个连接端口。
自定义节点类型列表:
const { schema, paletteItems } = createDefaultSchema({
nodeTypes: {
task: { label: '任务节点', width: 180, height: 60 },
approval: { label: '审批节点', icon: 'my-icon-class', width: 200, height: 60 },
notification: { label: '通知节点', width: 160, height: 50 },
},
});连线类型配置
createDefaultSchema 内置两种连线类型:
| 类型 | 路由 | 连接器 | 说明 |
| ------------------- | ----------- | ---------------------- | --------------------- |
| manhattan(默认) | manhattan | rounded(radius: 8) | 直角折线,圆角转弯 |
| bezier | 无 | smooth | 贝塞尔曲线,平滑 S 形 |
切换为贝塞尔曲线连线:
const { schema, paletteItems } = createDefaultSchema({
defaultEdgeType: 'bezier',
});使用自定义边类型:
const { schema, paletteItems } = createDefaultSchema({
defaultEdgeType: 'myEdge',
edgeTypes: {
myEdge: {
connector: 'normal',
style: (_edge, state) => ({
stroke: state.hovered ? 'red' : 'gray',
strokeWidth: 2,
}),
},
},
});在自定义 Schema 中复用内置边类型:
当你没有使用 createDefaultSchema(),而是像适配层那样手写 CanvasSchema 时,可以通过 createBuiltinEdgeTypes() 直接复用引擎内置的 manhattan / bezier 定义,避免在业务包里重复拷贝一份。
import { createBuiltinEdgeTypes, type CanvasSchema } from '@blueking/flow-canvas';
const builtinEdgeTypes = createBuiltinEdgeTypes();
const schema: CanvasSchema = {
nodeTypes: {
/* ... */
},
edgeTypes: {
default: { ...builtinEdgeTypes.manhattan, labelDraggable: true },
...builtinEdgeTypes,
},
defaultEdgeType: 'bezier',
};手动定义边类型(不使用 createDefaultSchema):
在手动编写 CanvasSchema 时,同样可以通过 edgeTypes 和 defaultEdgeType 配置连线样式:
const schema: CanvasSchema = {
nodeTypes: {
/* ... */
},
edgeTypes: {
bezier: {
connector: { name: 'smooth' },
style: (_edge, state) => ({
stroke: state.hovered ? '#3a84ff' : '#abb5cc',
strokeWidth: 2,
}),
x6EdgeConfig: {
attrs: {
line: {
stroke: '#abb5cc',
strokeWidth: 2,
targetMarker: { name: 'block', width: 8, height: 8 },
},
},
},
},
manhattan: {
router: { name: 'manhattan', args: { padding: 10, maxDirectionChange: 90 } },
connector: { name: 'rounded', args: { radius: 8 } },
style: (_edge, state) => ({
stroke: state.hovered ? '#3a84ff' : '#abb5cc',
strokeWidth: 2,
}),
},
},
defaultEdgeType: 'bezier',
};默认连线与端口样式
- 端口:半径 6px,背景
#3a84ff,1px 白色描边。默认隐藏(visibility: hidden),hover 节点时显示;拖拽连线时自动显示所有节点端口。 - 连线:宽度 2px,颜色
#abb5cc,hover 变#3a84ff,平角箭头(blockmarker)。zIndex: -1确保在节点下方,不阻挡端口交互。 - 连接点:
connectionPoint: 'anchor'+anchor: 'center',连线直达端口中心,紧贴节点。
命令系统
所有 FlowModel 变更必须通过命令执行。每条命令通过 CommandEnvelope 包装,一个信封可包含多条原子命令,作为一次事务整体执行(撤销时整体回退)。
import { generateId } from '@blueking/flow-canvas';添加节点和连线:
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '添加节点',
timestamp: Date.now(),
commands: [
{ type: 'node.add', node: { id: 'n1', type: 'task', position: { x: 100, y: 100 }, label: '审批' } },
{
type: 'edge.add',
edge: { id: 'e1', source: { nodeId: 'n0', portId: 'right' }, target: { nodeId: 'n1', portId: 'left' } },
},
],
});删除节点(自动级联删除关联边):
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '删除节点',
timestamp: Date.now(),
commands: [{ type: 'node.remove', nodeId: 'n1' }],
});删除连线:
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '删除连线',
timestamp: Date.now(),
commands: [{ type: 'edge.remove', edgeId: 'e1' }],
});移动节点:
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '移动节点',
timestamp: Date.now(),
commands: [{ type: 'node.move', nodeId: 'n1', position: { x: 300, y: 200 } }],
});更新节点结构字段(label、type、ports 等,不含 payload/extensions):
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '重命名节点',
timestamp: Date.now(),
commands: [{ type: 'node.update', nodeId: 'n1', patch: { label: '新名称' } }],
});按路径更新节点 payload(业务数据):
// 设置 payload.config.timeout = 30
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '更新超时配置',
timestamp: Date.now(),
commands: [{ type: 'node.set-payload', nodeId: 'n1', path: ['config', 'timeout'], value: 30 }],
});
// 设置整个 payload.config 对象
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '更新节点配置',
timestamp: Date.now(),
commands: [{ type: 'node.set-payload', nodeId: 'n1', path: ['config'], value: { timeout: 30, retryable: true } }],
});
// 删除 payload 中的某个字段(value 传 undefined)
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '清除超时配置',
timestamp: Date.now(),
commands: [{ type: 'node.set-payload', nodeId: 'n1', path: ['config', 'timeout'], value: undefined }],
});重连边端点:
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '重连边',
timestamp: Date.now(),
commands: [
{
type: 'edge.reconnect',
edgeId: 'e1',
target: { nodeId: 'n2', portId: 'left' }, // 只改 target,source 不变
},
],
});更新边标签:
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '更新边标签',
timestamp: Date.now(),
commands: [{ type: 'edge.label.update', edgeId: 'e1', labelId: 'label1', patch: { text: '条件成立' } }],
});更新全局 meta:
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '更新全局变量',
timestamp: Date.now(),
commands: [{ type: 'model.set-meta', path: ['variables', 'env'], value: 'production' }],
});批量操作(一个信封包含多条命令,一次性执行):
editor.executeCommand({
id: generateId(),
source: 'user:panel',
label: '批量删除选中',
timestamp: Date.now(),
commands: [
{ type: 'edge.remove', edgeId: 'e1' },
{ type: 'edge.remove', edgeId: 'e2' },
{ type: 'node.remove', nodeId: 'n1' },
{ type: 'node.remove', nodeId: 'n2' },
],
});处理命令执行结果:
const result = editor.executeCommand({
/* ... */
});
if (result.status === 'applied') {
console.log('执行成功,新模型:', result.flowModel);
} else if (result.status === 'rejected') {
console.warn('被插件拒绝:', result.error?.reason);
} else if (result.status === 'invalid') {
console.error('违反约束:', result.error?.reason);
}命令类型一览
| 命令 | 说明 |
| --------------------- | ------------------------------------------- |
| node.add | 添加节点 |
| node.move | 移动节点位置 |
| node.remove | 删除节点(自动级联删除关联边) |
| node.update | 更新节点结构字段(不含 payload/extensions) |
| node.set-payload | 按路径更新节点 payload |
| node.set-extensions | 按路径更新节点 extensions |
| edge.add | 添加边 |
| edge.remove | 删除边 |
| edge.reconnect | 重连边端点 |
| edge.update | 更新边结构字段 |
| edge.set-payload | 按路径更新边 payload |
| edge.label.update | 更新边标签 |
| model.set-meta | 按路径更新全局 meta |
path 参数使用 string[] 格式:{ path: ['config', 'timeout'], value: 30 }。
CommandSource 类型
'user:drag' | 'user:keyboard' | 'user:toolbar' | 'user:quick-add' | 'user:panel' | 'plugin' | 'system' | 'system:replace'
组件
CanvasRuntime
核心渲染组件,负责创建 X6 Graph 并双向桥接 FlowModel。
| Prop | 类型 | 说明 |
| ---------------------------- | -------------------------------------------- | ---------------------------------------------------- |
| editor | CanvasEditorContext | 编辑器实例(必填) |
| graphOptions | Record<string, unknown> | 额外 X6 Graph 选项 |
| nodeActions | NodeActionsConfig | 节点快捷操作工具栏配置(见下方详细说明) |
| quickAdd | QuickAddConfig | 快捷添加按钮配置(默认绑定右侧端口,见下方详细说明) |
| getConnectionExcludedNodeIds | (sourceNodeId: string) => Iterable<string> | 拖拽连线时返回不应显示端口的节点 ID 集合 |
| Emit | 参数 | 说明 |
| -------- | --------------- | -------------------- |
| ui-event | CanvasUiEvent | 非命令类 UI 交互事件 |
| Slot | Props | 说明 |
| --------------- | ------------------------------------------------ | ------------------------------------------------------------------- |
| quick-add-panel | { node, api, insertNodeToRight, closePopover } | 快捷添加弹层内容,点击绑定端口上的"+"按钮时显示(默认显示提示文字) |
节点快捷操作工具栏
CanvasRuntime 内置节点快捷操作工具栏,hover 节点时在右下角显示。通过 nodeActions prop 进行全局配置:
<CanvasRuntime :editor="editor" :node-actions="{ showDebug: true, showCopy: false }" />NodeActionsConfig(全局开关)
| 属性 | 类型 | 默认 | 说明 |
| ---------------- | --------- | ------- | ---------------------------- |
| showDebug | boolean | false | 是否显示调试按钮 |
| showDelete | boolean | true | 是否显示删除按钮 |
| showCopy | boolean | true | 是否显示复制按钮 |
| showCopyInsert | boolean | true | 是否显示复制并插入按钮 |
| showDisconnect | boolean | true | 是否显示断开连线按钮 |
| insertGap | number | 100 | 向右插入节点的水平间距(px) |
NodeBehaviorConfig 中的工具栏相关字段(逐节点控制)
通过 schema getBehavior 返回值控制每个节点类型的工具栏行为:
| 属性 | 说明 |
| --------------------- | --------------------------------------------------------------------------------- |
| showActions | false → 该节点类型不显示整个工具栏 |
| deletable | false → 隐藏删除按钮 |
| copyable | false → 隐藏复制和复制并插入按钮 |
| disconnectable | false → 隐藏断开连线按钮 |
| debuggable | false → 隐藏调试按钮(需全局 showDebug 也为 true) |
| deleteDisabled | true → 删除按钮可见但置灰不可点击 |
| copyDisabled | true → 复制按钮置灰 |
| copyInsertDisabled | true → 复制并插入按钮置灰 |
| disconnectDisabled | true → 断开连线按钮置灰 |
| debugDisabled | true → 调试按钮置灰 |
| connectable | false → 此节点端口不允许发起连线(控制 magnet 属性) |
| targetable | false → 拖拽连线期间不显示此节点端口(不可作为连线目标) |
| bringToFrontOnDrag | false → 拖拽时不临时置顶节点 z-index(默认 true) |
| hidePortForQuickAdd | false → quick-add 激活时不隐藏对应方向的连接端口(默认 true) |
| actionsOffset | { x?: number; y?: number } — 工具栏位置偏移(px),相对于默认位置(节点右下角) |
按钮有效可见性 = 全局 show* AND 逐节点 *able !== false;
按钮禁用状态 = 逐节点 *Disabled === true。
操作触发后通过 @ui-event 发出对应事件:node.action.delete / node.action.copy / node.action.copy-insert / node.action.disconnect / node.action.debug。
节点快捷添加按钮(Quick Add)
hover 节点时,指定端口会显示为"+"按钮,支持点击弹出面板和拖拽发起连线。默认绑定右侧 group: 'right' 端口。通过 quickAdd prop 进行全局配置:
<CanvasRuntime :editor="editor" :quick-add="{ enabled: true, portGroup: 'right' }">
<template #quick-add-panel="{ node, api, insertNodeToRight, closePopover }">
<!-- 自定义节点选择面板 -->
</template>
</CanvasRuntime>QuickAddConfig(全局配置)
| 属性 | 类型 | 默认 | 说明 |
| ----------------- | -------------------- | --------- | ------------------------------------------------------------------------------------------------- |
| enabled | boolean | true | 全局开关 |
| portGroup | string | 'right' | quick-add 默认绑定的端口分组;节点不存在该分组端口时不显示 quick-add |
| getPort | function | - | 精确指定当前节点使用哪个 port;优先级高于 portGroup |
| insertDirection | string \| function | - | 点击 quick-add 插入节点时使用的方向;默认跟随当前绑定 port 的标准方向,不可推断时回退为 'right' |
| autoPanOnOpen | boolean | true | 点击打开 quick-add 面板时,如弹层会被浏览器右侧遮住,则自动左移画布使其完整显示 |
| autoPanPadding | number | 16 | autoPanOnOpen 生效时,弹层与浏览器右边缘保留的安全间距(px) |
NodeBehaviorConfig 中的快捷添加相关字段(逐节点控制)
| 属性 | 说明 |
| ----------------- | --------------------------------------------------------------- |
| quickAddEnabled | false → 该节点不显示"+"按钮(如结束节点、已有出边的开始节点) |
生效规则:显示 = global.enabled !== false AND perNode.quickAddEnabled !== false。
对于未显式提供 getPorts / node.ports 的自定义节点,quick-add 会落到引擎自动补齐的默认四向端口上,因此默认右侧交互仍然可用。
getPort(node, ports) 优先级高于 portGroup,适合一个节点存在多个同分组 port,或端口选择依赖节点数据的场景。
insertDirection 用于控制点击 quick-add 后的插入方向:
- 不传时:优先跟随当前绑定 port 的标准方向(
top/right/bottom/left) - 如果当前绑定 port 不是标准方向:回退为
'right' - 传字符串时:固定使用该方向
- 传函数时:可根据节点和当前绑定 port 动态决定方向
portGroup / getPort 只控制 quick-add 绑定到哪个端口、"+"按钮显示在哪个端口上,以及拖拽从哪个端口发起。
slot 中提供的 insertNodeToRight helper 出于兼容仍保留这个名字,但它现在会遵循 insertDirection 的配置,不再总是向右插入。
自定义 quick-add 端口选择:
<CanvasRuntime
:editor="editor"
:quick-add="{
enabled: true,
portGroup: 'right',
getPort: (node, ports) => {
if (node.type === 'gateway') {
return ports.find((port) => port.id === 'branch_out_main') ?? null;
}
return null; // 返回 null 时退回使用 portGroup
},
insertDirection: (node, port) => {
if (node.type === 'gateway' && port?.group === 'bottom') {
return 'bottom';
}
return null; // 返回 null 时按默认推导
},
}" />quick-add-panel Slot Props
| Prop | 类型 | 说明 |
| ------------------- | ------------------------------------------------- | ---------------------------------------------------------------- |
| node | FlowNodeModel | 当前触发的源节点 |
| api | CanvasApi | 画布 API |
| insertNodeToRight | (node: Omit<FlowNodeModel, 'position'>) => void | 快捷插入方法(自动连线 + 事件);方向遵循 insertDirection 配置 |
| closePopover | () => void | 关闭弹层 |
操作触发后通过 @ui-event 发出对应事件:node.quick-add(弹层打开时)/ node.action.quick-insert(插入成功时)。
完整的 quick-add-panel 使用示例:
<template>
<CanvasLayout :editor="editor" :palette-items="paletteItems">
<CanvasRuntime :editor="editor" :quick-add="{ enabled: true, portGroup: 'right' }" @ui-event="handleUiEvent">
<template #quick-add-panel="{ node, insertNodeToRight, closePopover }">
<div class="quick-add-menu">
<div class="quick-add-menu__title">选择节点类型</div>
<div
v-for="item in quickAddNodeList"
:key="item.type"
class="quick-add-menu__item"
@click="handleQuickAdd(item, insertNodeToRight, closePopover)">
<i :class="item.icon" />
<span>{{ item.label }}</span>
</div>
</div>
</template>
</CanvasRuntime>
<CanvasToolbar :editor="editor" />
</CanvasLayout>
</template>
<script setup lang="ts">
import {
useCanvasEditor,
CanvasRuntime,
CanvasLayout,
CanvasToolbar,
createDefaultSchema,
createEmptyFlowModel,
generateId,
type FlowNodeModel,
} from '@blueking/flow-canvas';
import '@blueking/flow-canvas/style';
const { schema, paletteItems } = createDefaultSchema();
const editor = useCanvasEditor({ initialFlowModel: createEmptyFlowModel(), schema });
const quickAddNodeList = [
{ type: 'empty', label: '空节点', icon: 'flow-canvas-icon canvas-jiedi' },
{ type: 'parallel-gateway', label: '并行网关', icon: 'flow-canvas-icon canvas-bingxingwangguan' },
];
function handleQuickAdd(
item: { type: string; label: string },
insertNodeToRight: (node: Omit<FlowNodeModel, 'position'>) => void,
closePopover: () => void,
) {
insertNodeToRight({
id: generateId(),
type: item.type,
label: item.label,
});
closePopover();
}
</script>通过 getBehavior 可动态控制哪些节点显示"+"按钮:
const schema: CanvasSchema = {
nodeTypes: {
end: {
component: EndNode,
getSize: () => ({ width: 88, height: 40 }),
getBehavior: () => ({
quickAddEnabled: false, // 结束节点不显示"+"按钮
deletable: false,
}),
},
task: {
component: TaskNode,
getSize: () => ({ width: 180, height: 60 }),
getBehavior: (node, ctx) => {
const hasOutEdge = Object.values(ctx.flowModel.edges).some((e) => e.source.nodeId === node.id);
return {
quickAddEnabled: !hasOutEdge, // 已有出边时隐藏"+"按钮
};
},
},
},
};CanvasLayout
可选的布局骨架组件,提供侧边栏 + 主画布 + 底栏的三栏布局。
| Prop | 类型 | 默认 | 说明 |
| ---------------- | --------------------- | ----------- | ---------------------------------------------------- |
| sidebarCollapsed | boolean | false | 侧边栏是否收起 |
| sidebarWidth | number | 260 | 侧边栏宽度(px) |
| hideSidebar | boolean | false | 是否隐藏侧边栏 |
| hideFooter | boolean | false | 是否隐藏底部栏 |
| editor | CanvasEditorContext | undefined | 编辑器实例,传入后自动渲染内置 CanvasNodePalette |
| paletteItems | NodePaletteItem[] | undefined | 节点面板项列表,配合 editor 使用自定义面板节点列表 |
| Emit | 参数 | 说明 |
| ----------------------- | --------- | ------------------ |
| update:sidebarCollapsed | boolean | 侧边栏收起状态变更 |
| Slot | 说明 |
| ------- | --------------------------------------------------------- |
| sidebar | 侧边栏内容(传入后覆盖内置 CanvasNodePalette 默认面板) |
| default | 主画布区域(放置 CanvasRuntime + CanvasToolbar) |
| footer | 底部栏 |
侧边栏的三种使用方式
方式一:自动渲染内置节点面板
传入 editor prop 且不提供 sidebar slot,CanvasLayout 自动在侧边栏渲染 CanvasNodePalette,用户可从面板拖拽节点到画布:
<CanvasLayout :editor="editor" :palette-items="paletteItems">
<CanvasRuntime :editor="editor" />
</CanvasLayout>通过 paletteItems prop 自定义面板中显示的节点类型列表:
<script setup lang="ts">
import { createDefaultSchema, useCanvasEditor, createEmptyFlowModel } from '@blueking/flow-canvas';
const { schema, paletteItems } = createDefaultSchema();
const editor = useCanvasEditor({ initialFlowModel: createEmptyFlowModel(), schema });
// 也可以手动指定面板项,只展示部分节点类型
const customPaletteItems = [
{ type: 'start', label: '开始节点', icon: 'flow-canvas-icon canvas-kaishi' },
{ type: 'end', label: '结束节点', icon: 'flow-canvas-icon canvas-stop' },
{ type: 'empty', label: '空节点', icon: 'flow-canvas-icon canvas-jiedi' },
];
</script>
<template>
<CanvasLayout :editor="editor" :palette-items="customPaletteItems">
<CanvasRuntime :editor="editor" />
</CanvasLayout>
</template>方式二:通过 sidebar slot 完全自定义侧边栏
<template>
<CanvasLayout>
<template #sidebar>
<div class="my-sidebar">
<h3>节点库</h3>
<div v-for="item in nodeItems" :key="item.type" ref="dndRefs" class="my-sidebar__item" :data-type="item.type">
{{ item.label }}
</div>
</div>
</template>
<CanvasRuntime :editor="editor" />
</CanvasLayout>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useCanvasEditor, CanvasRuntime, CanvasLayout, generateId } from '@blueking/flow-canvas';
const nodeItems = [
{ type: 'task', label: '任务' },
{ type: 'approval', label: '审批' },
];
// 通过 api.registerDndSource 注册拖拽源
watch(
() => editor.api.value,
(api) => {
if (!api) return;
for (const item of nodeItems) {
const el = document.querySelector(`[data-type="${item.type}"]`);
if (el) {
api.registerDndSource(el as HTMLElement, () => ({
id: generateId(),
type: item.type,
label: item.label,
position: { x: 0, y: 0 },
}));
}
}
},
);
</script>方式三:隐藏侧边栏
<CanvasLayout hide-sidebar>
<CanvasRuntime :editor="editor" />
</CanvasLayout>CanvasNodePalette
内置的节点面板组件,支持拖拽节点到画布(DnD)。通常由 CanvasLayout 自动渲染,也可以单独使用。
| Prop | 类型 | 必填 | 说明 |
| ------ | --------------------- | ---- | ---------------------------------------- |
| editor | CanvasEditorContext | 是 | 编辑器实例 |
| items | NodePaletteItem[] | 否 | 节点列表,不传时从 schema.nodeTypes 生成 |
interface NodePaletteItem {
type: string; // 节点类型,对应 schema.nodeTypes 的 key
label: string; // 显示名称
icon?: string; // CSS 图标类名
}CanvasToolbar
可选的工具栏组件。默认浮动在画布左上角,内置下载、缩放、重置等核心操作;search / minimap 由对应插件注入后显示。
| Prop | 类型 | 默认 | 说明 |
| ------- | ---------------------- | ---------- | --------------------------------------- |
| editor | CanvasEditorContext | - | 编辑器实例(必填) |
| items | CanvasToolbarItem[] | 内置默认项 | 工具栏项列表,传入后完全替换默认项 |
| exclude | BuiltinToolbarType[] | - | 排除特定默认项(仅在未传 items 时生效) |
createDefaultToolbarItems(options?)
工厂函数,用于获取默认工具栏项列表,便于自定义组合。
撤销/重做(undo / redo)默认隐藏,可通过 include 恢复显示。
import { createDefaultToolbarItems } from '@blueking/flow-canvas';
// 默认不含撤销/重做
const items = createDefaultToolbarItems();
// 恢复显示撤销/重做
const itemsWithHistory = createDefaultToolbarItems({ include: ['undo', 'redo'] });
// 恢复撤销/重做,追加自定义项
const customItems = [
...createDefaultToolbarItems({ include: ['undo', 'redo'] }),
{ id: 'my-tool', type: 'custom', icon: 'my-icon-class', onClick: handleClick },
];CanvasToolbarItem 字段
| 字段 | 类型 | 说明 |
| ----------- | -------------------------------------- | ----------------------------------------- |
| id | string | 唯一标识 |
| type | BuiltinToolbarType \| 'custom' | 操作类型 |
| icon | string | CSS 类名(字体图标) |
| component | Component | Vue 组件,渲染在按钮内部作为图标/内容区域 |
| text | string | 无图标时的兜底显示文字 |
| description | string | 操作说明,有值时 hover 显示 tooltip 气泡 |
| onClick | (ctx: CanvasCallbackContext) => void | 点击回调 |
通过 CSS 变量定制工具栏样式与位置
CanvasToolbar 通过 CSS 自定义属性暴露了位置和外观的定制接口,使用方在父元素上设置对应变量即可覆盖默认值:
| CSS 变量 | 默认值 | 说明 |
| --------------------------------- | --------------------------- | -------------------- |
| --flow-canvas-toolbar-top | 16px | 工具栏距画布顶部距离 |
| --flow-canvas-toolbar-left | 16px | 工具栏距画布左侧距离 |
| --flow-canvas-toolbar-right | auto | 工具栏距画布右侧距离 |
| --flow-canvas-toolbar-bottom | auto | 工具栏距画布底部距离 |
| --flow-canvas-toolbar-bg | #565e7a | 工具栏背景色 |
| --flow-canvas-toolbar-color | #f5f7fa | 工具栏文字/图标颜色 |
| --flow-canvas-toolbar-hover-bg | rgba(255, 255, 255, 0.15) | 按钮 hover 背景色 |
| --flow-canvas-toolbar-active-bg | rgba(255, 255, 255, 0.25) | 按钮激活态背景色 |
示例 — 将工具栏移至画布底部居中,并更换背景色:
<template>
<CanvasLayout :editor="editor" :palette-items="paletteItems" class="my-canvas-wrapper">
<CanvasRuntime :editor="editor" />
<CanvasToolbar :editor="editor" />
</CanvasLayout>
</template>
<style scoped>
.my-canvas-wrapper {
--flow-canvas-toolbar-top: auto;
--flow-canvas-toolbar-bottom: 16px;
--flow-canvas-toolbar-left: 50%;
--flow-canvas-toolbar-right: auto;
--flow-canvas-toolbar-bg: #1a1a2e;
}
</style>自定义节点组件
节点的渲染由 CanvasNodeDefinition.component 指定的 Vue 组件控制。组件内可通过 inject('getNode') 获取 X6 Node 实例,读取 FlowNodeModel 数据。
基础节点组件
<!-- my-task-node.vue -->
<template>
<div class="my-task-node" :class="{ 'is-selected': selected }">
<i v-if="icon" :class="icon" class="my-task-node__icon" />
<span class="my-task-node__label">{{ label }}</span>
</div>
</template>
<script setup lang="ts">
import { inject, ref, onMounted, onBeforeUnmount, computed } from 'vue';
import type { FlowNodeModel } from '@blueking/flow-canvas';
const getNode = inject<() => any>('getNode')!;
const nodeModel = ref<FlowNodeModel>();
const selected = ref(false);
const label = computed(() => nodeModel.value?.label ?? '');
const icon = computed(() => (nodeModel.value?.extensions as any)?.icon ?? '');
onMounted(() => {
const node = getNode();
nodeModel.value = node.getData() as FlowNodeModel;
node.on('change:data', ({ current }: { current: FlowNodeModel }) => {
nodeModel.value = current;
});
});
</script>
<style scoped>
.my-task-node {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
height: 100%;
padding: 0 16px;
background: #fff;
border: 1px solid #dcdee5;
border-radius: 4px;
box-sizing: border-box;
cursor: pointer;
}
.my-task-node.is-selected {
border-color: #3a84ff;
}
</style>在 Schema 中注册节点组件
import { createBuiltinEdgeTypes } from '@blueking/flow-canvas';
import MyTaskNode from './components/my-task-node.vue';
import MyGatewayNode from './components/my-gateway-node.vue';
const builtinEdgeTypes = createBuiltinEdgeTypes();
const schema: CanvasSchema = {
nodeTypes: {
task: {
component: MyTaskNode,
getSize: () => ({ width: 180, height: 60 }),
getBehavior: (node, ctx) => ({
deletable: true,
copyable: true,
quickAddEnabled: true,
}),
},
gateway: {
component: MyGatewayNode,
getSize: () => ({ width: 64, height: 64 }),
getPorts: () => [
{ id: 'top', group: 'top' },
{ id: 'right', group: 'right' },
{ id: 'bottom', group: 'bottom' },
{ id: 'left', group: 'left' },
],
getBehavior: () => ({
copyable: false,
showActions: true,
}),
},
},
edgeTypes: {
bezier: builtinEdgeTypes.bezier,
},
defaultEdgeType: 'bezier',
};上面这个例子里,task 故意不写 getPorts,直接复用引擎默认四向端口;gateway 保留 getPorts,用于演示“当节点需要显式控制端口拓扑时再覆盖默认行为”。
CanvasApi
editor.api.value 在 CanvasRuntime 挂载后可用,之前为 null。
| 方法 | 说明 |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| zoomIn() / zoomOut() | 缩放 |
| zoomTo(value) | 缩放到指定比例 |
| zoomToFit() | 自适应画布 |
| getZoom() | 获取当前缩放比例 |
| centerContent() | 居中画布内容 |
| scrollToNode(nodeId) | 滚动到指定节点 |
| getSelection() | 获取当前选区 { nodeIds, edgeIds } |
| getSelectionBounds(selection?) | 获取当前选区或指定选区的整体包围盒,用于挂载选区工具条等 overlay |
| selectNodes(ids) | 选中指定节点 |
| selectEdges(ids) | 选中指定边 |
| clearSelection() | 清除选区 |
| deleteSelection(options?) | 统一删除当前选区或指定选区;按 schema behavior 过滤不可删元素,默认成功后清空选区 |
| registerDndSource(el, factory) | 注册 DND 拖拽源,返回清理函数 |
| startConnection(nodeId, portId?) | 从指定节点/端口发起连线 |
| highlightNodes(ids) / highlightEdges(ids) | 高亮指定元素 |
| clearHighlight() | 清除所有高亮 |
| exportAsImage(options?) | 导出为 PNG Blob(基于 modern-screenshot,需安装为 peer dependency) |
| overlay | OverlayManager 实例,提供节点屏幕定位 |
| insertNodeToRight(sourceNodeId, node, options?) | 默认在指定节点右侧插入新节点;传 options.direction 后可改为上/右/下/左方向,并支持自动连线和重叠检测 |
| getContextMenuItems(position) | 收集插件提供的右键菜单项 |
| onGraphEvent(event, handler) | 监听 X6 底层事件,返回取消函数 |
| unsafeGetGraph() | 获取 X6 Graph 实例(谨慎使用) |
内置插件
框选模式
selectionMode 只表示“是否启用框选模式”,不等价于“是否允许节点被选中”。
selectionMode = true空白区域左键拖拽启用 rubberband 框选。selectionMode = false空白区域左键拖拽恢复为画布平移。- 无论
selectionMode是否开启 普通点击选中、selectNodes()、selectEdges()仍然可用。
editor.setSelectionMode(true);
editor.setSelectionMode(false);selectionPlugin(options?)
接入 X6 的通用选区能力,支持多选、框选和 _selectable 节点过滤。
selectionPlugin({
rubberband: true, // 框选,默认 true
multiple: true, // 多选,默认 true
movable: true, // 选中后可拖拽移动,默认 true
});注意:
selectionPlugin()负责注册 X6 Selection 能力。selectionMode由CanvasRuntime在运行时控制 rubberband / panning 的切换。selectionPlugin()本身不负责决定当前是否处于框选模式。
snaplinePlugin(options?)
节点拖拽时显示对齐辅助线。
snaplinePlugin({ tolerance: 10 }); // 对齐容差,默认 10connectionValidatorPlugin(validator)
连线校验。
connectionValidatorPlugin((ctx) => {
// ctx.flowModel: 当前 FlowModel
// ctx.sourceNode / targetNode: 源/目标节点
// ctx.existingEdges: 已有边列表
if (ctx.sourceNode.id === ctx.targetNode.id) {
return { valid: false, reason: '不允许自环' };
}
return { valid: true };
});searchPlugin(options?)
工具栏搜索节点。点击搜索图标后弹出面板,支持按关键字过滤节点并通过 Enter / 点击快速定位到目标节点。
searchPlugin({
placeholder: '搜索节点名称',
getNodeMeta(node) {
const stageName = typeof node.payload?.stageName === 'string' ? node.payload.stageName : '';
return {
label: node.label ?? '',
subtitle: stageName || undefined,
keywords: [node.label ?? '', stageName],
};
},
});minimapPlugin(options?)
小地图。传入 container 后启用。
minimapPlugin({
container: document.getElementById('minimap'),
width: 200,
height: 160,
});clipboardPlugin()
Cmd/Ctrl+C 复制选中节点/边,Cmd/Ctrl+V 粘贴(生成新 ID、偏移位置)。
自定义插件
const myPlugin: CanvasPlugin = {
name: 'my-plugin',
priority: 50,
// editor 阶段:仅有 flowModel / history / schema
install(ctx) {},
// runtime 阶段:api / graph 已可用
attachRuntime(ctx) {
ctx.api.onGraphEvent('cell:mouseenter', () => {
/* ... */
});
},
detachRuntime() {},
// 命令拦截/增强(不可变管道)
transformCommand(envelope, preview, ctx) {
// 返回新 envelope = 修改后的命令
// 返回 { rejected: true, reason: '...' } = 拒绝
// 返回 null = 丢弃
return envelope;
},
// 命令执行后
afterCommand(envelope, prevModel, newModel, ctx) {},
// UI 事件
onUiEvent(event, ctx) {},
onKeyboardShortcut(event, ctx) {
return false;
},
onSelectionChange(selection, ctx) {},
// 视觉装饰
decorateNode(node, ctx) {
if (node.payload?.hasError) {
return { borderColor: '#ff4d4d', badge: { text: '!', color: '#ff4d4d' } };
}
},
decorateEdge(edge, ctx) {},
// 工具栏扩展
provideToolbarItems(ctx) {
return [{ id: 'my-tool', type: 'custom', text: '自定义工具', onClick: () => {} }];
},
// API 扩展
extendApi(api, ctx) {
return {
doSomething: () => {
/* ... */
},
};
},
};工具函数与底层 API
| 导出 | 说明 |
| ------------------------------------------------- | --------------------------------------------------------------------------------- |
| createEmptyFlowModel() | 创建一个空的 FlowModel(version: '1.0', 空 nodes/edges) |
| createBuiltinEdgeTypes() | 返回引擎内置的 manhattan / bezier 边类型定义,适合自定义 Schema 直接复用 |
| generateId() | 生成唯一 ID |
| applyCanvasCommand(model, cmd) | 纯函数 reducer,将单条命令应用到 FlowModel,返回新 FlowModel |
| createCanvasHistory(initialFlowModel, options?) | 创建独立的历史管理器实例(通常由 useCanvasEditor 内部调用,高级场景可直接使用) |
| IdGenerator | (type: 'node' \| 'edge') => string,自定义 ID 生成函数类型 |
| CanvasConstraintError | 命令违反 FlowModel 不变量时抛出的错误类(如重复 ID、悬挂引用等) |
| CanvasSchemaError | 未知节点/边类型等 Schema 级错误类 |
import { createEmptyFlowModel, generateId } from '@blueking/flow-canvas';
const emptyModel = createEmptyFlowModel();
// { version: '1.0', nodes: {}, edges: {} }撤销/重做
editor.history.undo();
editor.history.redo();
editor.history.canUndo.value; // boolean
editor.history.canRedo.value; // boolean
editor.history.clear();事件类型 (CanvasUiEvent)
| type | 附加字段 | 说明 | | ------------------------ | ----------------------- | ---------------------- | | node.click | nodeId | 节点点击 | | node.dblclick | nodeId | 节点双击 | | node.contextmenu | nodeId, position | 节点右键 | | edge.click | edgeId | 边点击 | | edge.label.click | edgeId, labelId | 边标签点击 | | blank.click | position | 空白区域点击 | | blank.contextmenu | position | 空白区域右键 | | selection.change | nodeIds, edgeIds | 选区变更 | | node.action.delete | nodeId | 快捷删除节点 | | node.action.copy | sourceNodeId, newNodeId | 快捷复制节点 | | node.action.copy-insert | sourceNodeId, newNodeId | 快捷复制并插入 | | node.action.disconnect | nodeId, edgeIds | 快捷断开连线 | | node.action.debug | nodeId | 快捷调试 | | node.quick-add | nodeId, position | 快捷添加弹层打开 | | node.action.quick-insert | sourceNodeId, newNodeId | 快捷插入节点 | | toolbar.auto-layout | — | 自动排版(由外层实现) |
公开类型参考
以下类型从包入口公开导出,供 TypeScript 接入方使用。
数据模型
| 类型 | 说明 |
| -------------------- | ---------------------------- |
| FlowModel | 流程模型,编辑态唯一权威状态 |
| FlowNodeModel | 节点模型 |
| FlowEdgeModel | 边模型 |
| FlowPortModel | 端口模型 |
| FlowEdgeLabelModel | 边标签模型 |
Schema 与渲染
| 类型 | 说明 |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| CanvasSchema | 画布配置(nodeTypes / edgeTypes) |
| CanvasNodeDefinition | 节点类型定义(component / getSize / getPorts / getBehavior) |
| CanvasEdgeDefinition | 边类型定义(router / connector / style / labelDraggable)。labelRenderer 字段为 @experimental,当前未实现 |
| CanvasCallbackContext | 运行时上下文(api / flowModel / history / mode) |
| CanvasMode | 模式:'edit' / 'readonly' / 'thumbnail' |
| NodeBehaviorConfig | 节点行为配置(showActions / draggable / selectable / deletable / connectable / targetable / copyable / disconnectable / debuggable / quickAddEnabled / bringToFrontOnDrag / hidePortForQuickAdd + 各按钮 Disabled 状态) |
| EdgeRenderState | 边渲染状态(selected / highlighted / hovered) |
| EdgeStyle | 边样式(stroke / strokeWidth / strokeDasharray) |
命令系统
| 类型 | 说明 |
| ----------------------------------- | --------------------------------------------- |
| CanvasCommand | 原子命令联合类型(node.add / edge.remove 等) |
| CommandEnvelope | 命令信封(id / source / commands[]) |
| CommandSource | 命令来源标识 |
| CommandExecutionResult | 命令执行结果 |
| CommandExecutionError | 命令执行错误详情 |
| CommandPreview | 命令预览接口(用于 transformCommand) |
| CommandRejection | 命令拒绝结果 |
| FlowModelChangeEvent | FlowModel 变更事件 |
| CanvasUiEvent | UI 交互事件联合类型 |
| ScreenPosition / CanvasPosition | 坐标类型 |
插件系统
| 类型 | 说明 |
| ---------------------- | -------------------------------------------------------------------------- |
| CanvasPlugin | 插件接口(install / attachRuntime / transformCommand / decorateNode 等) |
| EditorPluginContext | editor 阶段插件上下文(flowModel / history / schema / executeCommand) |
| RuntimePluginContext | runtime 阶段插件上下文(继承 EditorPluginContext + api / graph / overlay) |
| CanvasSelection | 选区数据(nodeIds / edgeIds) |
| NodeDecoration | 节点装饰(className / badge / borderColor) |
| EdgeDecoration | 边装饰(className / strokeColor) |
| ContextMenuItem | 右键菜单项 |
API 与工具栏
| 类型 | 说明 |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| CanvasApi | 画布公共 API 接口(含 insertNodeToRight) |
| CanvasToolbarItem | 工具栏项配置 |
| BuiltinToolbarType | 内置工具类型('undo' / 'redo' / 'select' / 'auto-layout' / 'search' / 'minimap' / 'export' / 'zoom-in' / 'zoom-out' / 'reset' 等) |
| NodeActionsConfig | 节点快捷操作工具栏全局配置(showDebug / showDelete / showCopy / showCopyInsert / showDisconnect / insertGap) |
| QuickAddConfig | 快捷添加按钮全局配置(enabled / portGroup / getPort / insertDirection) |
| QuickAddPortResolver | quick-add 端口选择回调类型;返回具体 port 后,优先级高于 portGroup |
| QuickAddInsertDirectionResolver | quick-add 插入方向回调类型;返回具体方向后,优先级高于默认方向推导 |
| InsertDirection | 节点插入方向类型('top' \| 'right' \| 'bottom' \| 'left') |
| InsertNodeOptions | insertNodeToRight 选项(autoWireEdges / gap / source / label / direction) |
| ExportOptions | 导出选项(backgroundColor / padding / quality / scale) |
| DeleteSelectionOptions | deleteSelection 选项(selection / source / label / clearSelectionAfterApply) |
| ConnectionValidator | 连接校验函数类型 |
| ConnectionValidateContext | 连接校验上下文 |
| ConnectionValidateResult | 连接校验结果 |
| NodePaletteItem | 节点面板项(type / label / icon) |
默认 Schema
| 类型 | 说明 |
| ---------------------------- | ---------------------------------- |
| DefaultNodeTypeConfig | createDefaultSchema 的节点配置项 |
| DefaultSchemaOptions | createDefaultSchema 的选项 |
| DefaultSchemaResult | createDefaultSchema 的返回值 |
| DefaultToolbarItemsOptions | createDefaultToolbarItems 的选项 |
历史与 Overlay
| 类型 | 说明 |
| ---------------------- | -------------------------- |
| CanvasHistory | 历史管理器接口 |
| CanvasHistoryOptions | 历史配置(maxHistorySize) |
| OverlayManager | Overlay 管理器接口 |
编辑器
| 类型 | 说明 |
| --------------------- | ------------------------------ |
| CanvasEditorContext | useCanvasEditor() 返回值类型 |
| CanvasEditorOptions | useCanvasEditor() 参数类型 |
插件选项
| 类型 | 说明 |
| ------------------------ | -------------------- |
| SelectionPluginOptions | selectionPlugin 配置 |
| SnaplinePluginOptions | snaplinePlugin 配置 |
| SearchPluginOptions | searchPlugin 配置 |
| MinimapPluginOptions | minimapPlugin 配置 |
