@blueking/flow-canvas 0.0.1-beta.1 → 0.0.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +528 -29
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +983 -992
- package/dist/runtime/canvas-runtime-core.vue.d.ts +1 -6
- package/dist/runtime/overlay-manager.d.ts +1 -2
- package/dist/runtime/use-node-hover.d.ts +3 -1
- package/dist/shell/node-actions-toolbar.vue.d.ts +4 -0
- package/dist/shell/toolbar-items.d.ts +3 -1
- package/dist/style.css +1 -1
- package/dist/types/flow-model.d.ts +1 -1
- package/dist/types/overlay.d.ts +0 -14
- package/dist/types/schema.d.ts +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,17 +24,59 @@ npm install @antv/x6-plugin-dnd # 拖拽建节点(DND)功能需要
|
|
|
24
24
|
|
|
25
25
|
## 快速开始
|
|
26
26
|
|
|
27
|
+
### 零配置启动
|
|
28
|
+
|
|
29
|
+
使用 `createDefaultSchema` 可零配置启动一个可拖拽、可连线的画布:
|
|
30
|
+
|
|
31
|
+
```vue
|
|
32
|
+
<template>
|
|
33
|
+
<CanvasLayout :editor="editor" :palette-items="paletteItems">
|
|
34
|
+
<CanvasRuntime :editor="editor" @ui-event="handleUiEvent" />
|
|
35
|
+
<CanvasToolbar :editor="editor" />
|
|
36
|
+
</CanvasLayout>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import {
|
|
41
|
+
useCanvasEditor,
|
|
42
|
+
CanvasRuntime,
|
|
43
|
+
CanvasLayout,
|
|
44
|
+
CanvasToolbar,
|
|
45
|
+
createDefaultSchema,
|
|
46
|
+
createEmptyFlowModel,
|
|
47
|
+
selectionPlugin,
|
|
48
|
+
snaplinePlugin,
|
|
49
|
+
type CanvasUiEvent,
|
|
50
|
+
} from '@blueking/flow-canvas';
|
|
51
|
+
import '@blueking/flow-canvas/style';
|
|
52
|
+
|
|
53
|
+
const { schema, paletteItems } = createDefaultSchema();
|
|
54
|
+
|
|
55
|
+
const editor = useCanvasEditor({
|
|
56
|
+
initialFlowModel: createEmptyFlowModel(),
|
|
57
|
+
schema,
|
|
58
|
+
plugins: [selectionPlugin(), snaplinePlugin()],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
function handleUiEvent(event: CanvasUiEvent) {
|
|
62
|
+
console.log('UI 事件:', event.type);
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
传入 `editor` prop 后,`CanvasLayout` 会自动在侧边栏渲染内置节点面板 `CanvasNodePalette`,用户可直接拖拽节点到画布。
|
|
68
|
+
|
|
69
|
+
### 自定义 Schema
|
|
70
|
+
|
|
71
|
+
如需完全控制节点/边类型,可手动定义 `CanvasSchema`:
|
|
72
|
+
|
|
27
73
|
```vue
|
|
28
74
|
<template>
|
|
29
75
|
<CanvasLayout>
|
|
30
76
|
<template #sidebar>
|
|
31
|
-
|
|
77
|
+
<MySidebar :editor="editor" />
|
|
32
78
|
</template>
|
|
33
|
-
<CanvasRuntime :editor="editor" @ui-event="handleUiEvent"
|
|
34
|
-
<template #node-overlay="{ node, screenAnchors, api }">
|
|
35
|
-
<!-- 节点悬浮操作按钮 -->
|
|
36
|
-
</template>
|
|
37
|
-
</CanvasRuntime>
|
|
79
|
+
<CanvasRuntime :editor="editor" @ui-event="handleUiEvent" />
|
|
38
80
|
<CanvasToolbar :editor="editor" />
|
|
39
81
|
</CanvasLayout>
|
|
40
82
|
</template>
|
|
@@ -157,7 +199,7 @@ npm install @antv/x6-plugin-dnd # 拖拽建节点(DND)功能需要
|
|
|
157
199
|
|
|
158
200
|
```typescript
|
|
159
201
|
interface FlowModel {
|
|
160
|
-
version
|
|
202
|
+
version?: '1.0'; // 数据模型版本标识,可选,未传时引擎自动补全为 '1.0'
|
|
161
203
|
nodes: Record<string, FlowNodeModel>; // 节点字典,key = nodeId
|
|
162
204
|
edges: Record<string, FlowEdgeModel>; // 边字典,key = edgeId
|
|
163
205
|
meta?: Record<string, unknown>; // 全局元数据
|
|
@@ -197,7 +239,6 @@ interface CanvasNodeDefinition {
|
|
|
197
239
|
getSize: (node) => { width; height };
|
|
198
240
|
getPorts?: (node) => FlowPortModel[];
|
|
199
241
|
getBehavior?: (node, ctx) => NodeBehaviorConfig;
|
|
200
|
-
getOverlayAnchors?: (node) => NodeOverlayAnchors;
|
|
201
242
|
x6CellConfig?: Record<string, unknown>; // 透传给 X6 的额外配置
|
|
202
243
|
}
|
|
203
244
|
```
|
|
@@ -223,7 +264,137 @@ interface CanvasEdgeDefinition {
|
|
|
223
264
|
|
|
224
265
|
> **注意:** `labelRenderer` 字段在类型定义中存在但标记为 `@experimental`,当前版本尚未完整实现。如需自定义边标签样式,建议通过 `x6EdgeConfig.defaultLabel` 配置 X6 原生标签 markup。
|
|
225
266
|
|
|
226
|
-
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## createDefaultSchema — 开箱即用的 Schema 工厂
|
|
270
|
+
|
|
271
|
+
`createDefaultSchema` 提供内置的节点类型和边类型定义,可零配置快速启动画布。
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { createDefaultSchema } from '@blueking/flow-canvas';
|
|
275
|
+
|
|
276
|
+
const { schema, paletteItems } = createDefaultSchema(options?);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### DefaultSchemaOptions
|
|
280
|
+
|
|
281
|
+
| 参数 | 类型 | 默认 | 说明 |
|
|
282
|
+
| --------------- | ----------------------------------------- | ------------- | --------------------------------------------- |
|
|
283
|
+
| nodeTypes | `Record<string, DefaultNodeTypeConfig>` | 7 种内置类型 | 自定义节点类型列表,传入后替换内置类型 |
|
|
284
|
+
| defaultEdgeType | `string` | `'manhattan'` | 默认连线类型,内置 `'manhattan'` 和 `'bezier'` |
|
|
285
|
+
| edgeTypes | `Record<string, CanvasEdgeDefinition>` | - | 额外/覆盖的边类型定义,会与内置类型合并 |
|
|
286
|
+
|
|
287
|
+
### 返回值
|
|
288
|
+
|
|
289
|
+
| 属性 | 类型 | 说明 |
|
|
290
|
+
| ------------ | ------------------- | -------------------------------------------- |
|
|
291
|
+
| schema | `CanvasSchema` | 可直接传给 `useCanvasEditor` 的 Schema 定义 |
|
|
292
|
+
| paletteItems | `NodePaletteItem[]` | 可传给 `CanvasLayout` 的 `paletteItems` prop |
|
|
293
|
+
|
|
294
|
+
### 内置节点类型
|
|
295
|
+
|
|
296
|
+
不传 `nodeTypes` 参数时,默认包含 7 种节点:
|
|
297
|
+
|
|
298
|
+
| 类型 | 标签 | 尺寸 |
|
|
299
|
+
| ---------------------------- | ------------ | --------- |
|
|
300
|
+
| `start` | 开始 | 88 × 40 |
|
|
301
|
+
| `end` | 结束 | 88 × 40 |
|
|
302
|
+
| `empty` | 空节点 | 240 × 48 |
|
|
303
|
+
| `parallel-gateway` | 并行网关 | 64 × 64 |
|
|
304
|
+
| `branch-gateway` | 分支网关 | 64 × 64 |
|
|
305
|
+
| `converge-gateway` | 汇聚网关 | 64 × 64 |
|
|
306
|
+
| `conditional-parallel-gateway` | 条件并行网关 | 64 × 64 |
|
|
307
|
+
|
|
308
|
+
每种节点均使用内置 `DefaultNode` 组件渲染,自带上/右/下/左四个连接端口。
|
|
309
|
+
|
|
310
|
+
自定义节点类型列表:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const { schema, paletteItems } = createDefaultSchema({
|
|
314
|
+
nodeTypes: {
|
|
315
|
+
task: { label: '任务节点', width: 180, height: 60 },
|
|
316
|
+
approval: { label: '审批节点', icon: 'my-icon-class', width: 200, height: 60 },
|
|
317
|
+
notification: { label: '通知节点', width: 160, height: 50 },
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 连线类型配置
|
|
323
|
+
|
|
324
|
+
`createDefaultSchema` 内置两种连线类型:
|
|
325
|
+
|
|
326
|
+
| 类型 | 路由 | 连接器 | 说明 |
|
|
327
|
+
| ----------------------- | ----------- | ---------------------- | -------------------- |
|
|
328
|
+
| `manhattan`(默认) | `manhattan` | `rounded`(radius: 8)| 直角折线,圆角转弯 |
|
|
329
|
+
| `bezier` | 无 | `smooth` | 贝塞尔曲线,平滑 S 形 |
|
|
330
|
+
|
|
331
|
+
**切换为贝塞尔曲线连线:**
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
const { schema, paletteItems } = createDefaultSchema({
|
|
335
|
+
defaultEdgeType: 'bezier',
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**使用自定义边类型:**
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
const { schema, paletteItems } = createDefaultSchema({
|
|
343
|
+
defaultEdgeType: 'myEdge',
|
|
344
|
+
edgeTypes: {
|
|
345
|
+
myEdge: {
|
|
346
|
+
connector: 'normal',
|
|
347
|
+
style: (_edge, state) => ({
|
|
348
|
+
stroke: state.hovered ? 'red' : 'gray',
|
|
349
|
+
strokeWidth: 2,
|
|
350
|
+
}),
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**手动定义边类型(不使用 `createDefaultSchema`):**
|
|
357
|
+
|
|
358
|
+
在手动编写 `CanvasSchema` 时,同样可以通过 `edgeTypes` 和 `defaultEdgeType` 配置连线样式:
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
const schema: CanvasSchema = {
|
|
362
|
+
nodeTypes: { /* ... */ },
|
|
363
|
+
edgeTypes: {
|
|
364
|
+
bezier: {
|
|
365
|
+
connector: { name: 'smooth' },
|
|
366
|
+
style: (_edge, state) => ({
|
|
367
|
+
stroke: state.hovered ? '#3a84ff' : '#abb5cc',
|
|
368
|
+
strokeWidth: 2,
|
|
369
|
+
}),
|
|
370
|
+
x6EdgeConfig: {
|
|
371
|
+
attrs: {
|
|
372
|
+
line: {
|
|
373
|
+
stroke: '#abb5cc',
|
|
374
|
+
strokeWidth: 2,
|
|
375
|
+
targetMarker: { name: 'block', width: 8, height: 8 },
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
manhattan: {
|
|
381
|
+
router: { name: 'manhattan', args: { padding: 10, maxDirectionChange: 90 } },
|
|
382
|
+
connector: { name: 'rounded', args: { radius: 8 } },
|
|
383
|
+
style: (_edge, state) => ({
|
|
384
|
+
stroke: state.hovered ? '#3a84ff' : '#abb5cc',
|
|
385
|
+
strokeWidth: 2,
|
|
386
|
+
}),
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
defaultEdgeType: 'bezier',
|
|
390
|
+
};
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### 默认连线与端口样式
|
|
394
|
+
|
|
395
|
+
- **端口**:半径 6px,背景 `#3a84ff`,1px 白色描边。默认隐藏(`visibility: hidden`),hover 节点时显示;拖拽连线时自动显示所有节点端口。
|
|
396
|
+
- **连线**:宽度 2px,颜色 `#abb5cc`,hover 变 `#3a84ff`,平角箭头(`block` marker)。`zIndex: -1` 确保在节点下方,不阻挡端口交互。
|
|
397
|
+
- **连接点**:`connectionPoint: 'anchor'` + `anchor: 'center'`,连线直达端口中心,紧贴节点。
|
|
227
398
|
|
|
228
399
|
---
|
|
229
400
|
|
|
@@ -291,7 +462,6 @@ editor.executeCommand({
|
|
|
291
462
|
|
|
292
463
|
| Slot | Props | 说明 |
|
|
293
464
|
| --------------- | ------------------------------------------------ | ----------------------------------------------------------- |
|
|
294
|
-
| node-overlay | `{ node, screenAnchors, api }` | 节点悬浮层,鼠标移入节点时显示 |
|
|
295
465
|
| quick-add-panel | `{ node, api, insertNodeToRight, closePopover }` | 快捷添加弹层内容,点击右侧"+"按钮时显示(默认显示提示文字) |
|
|
296
466
|
|
|
297
467
|
#### 节点快捷操作工具栏
|
|
@@ -329,13 +499,14 @@ editor.executeCommand({
|
|
|
329
499
|
| `copyInsertDisabled` | `true` → 复制并插入按钮置灰 |
|
|
330
500
|
| `disconnectDisabled` | `true` → 断开连线按钮置灰 |
|
|
331
501
|
| `debugDisabled` | `true` → 调试按钮置灰 |
|
|
502
|
+
| `actionsOffset` | `{ x?: number; y?: number }` — 工具栏位置偏移(px),相对于默认位置(节点右下角) |
|
|
332
503
|
|
|
333
504
|
按钮有效可见性 = 全局 `show*` AND 逐节点 `*able !== false`;
|
|
334
505
|
按钮禁用状态 = 逐节点 `*Disabled === true`。
|
|
335
506
|
|
|
336
507
|
操作触发后通过 `@ui-event` 发出对应事件:`node.action.delete` / `node.action.copy` / `node.action.copy-insert` / `node.action.disconnect` / `node.action.debug`。
|
|
337
508
|
|
|
338
|
-
####
|
|
509
|
+
#### 节点快捷添加按钮(Quick Add)
|
|
339
510
|
|
|
340
511
|
hover 节点时,右侧端口显示为"+"按钮,支持点击弹出面板和拖拽发起连线。通过 `quickAdd` prop 进行全局配置:
|
|
341
512
|
|
|
@@ -373,22 +544,226 @@ hover 节点时,右侧端口显示为"+"按钮,支持点击弹出面板和
|
|
|
373
544
|
|
|
374
545
|
操作触发后通过 `@ui-event` 发出对应事件:`node.quick-add`(弹层打开时)/ `node.action.quick-insert`(插入成功时)。
|
|
375
546
|
|
|
547
|
+
**完整的 quick-add-panel 使用示例:**
|
|
548
|
+
|
|
549
|
+
```vue
|
|
550
|
+
<template>
|
|
551
|
+
<CanvasLayout :editor="editor" :palette-items="paletteItems">
|
|
552
|
+
<CanvasRuntime :editor="editor" :quick-add="{ enabled: true }" @ui-event="handleUiEvent">
|
|
553
|
+
<template #quick-add-panel="{ node, insertNodeToRight, closePopover }">
|
|
554
|
+
<div class="quick-add-menu">
|
|
555
|
+
<div class="quick-add-menu__title">选择节点类型</div>
|
|
556
|
+
<div
|
|
557
|
+
v-for="item in quickAddNodeList"
|
|
558
|
+
:key="item.type"
|
|
559
|
+
class="quick-add-menu__item"
|
|
560
|
+
@click="handleQuickAdd(item, insertNodeToRight, closePopover)">
|
|
561
|
+
<i :class="item.icon" />
|
|
562
|
+
<span>{{ item.label }}</span>
|
|
563
|
+
</div>
|
|
564
|
+
</div>
|
|
565
|
+
</template>
|
|
566
|
+
</CanvasRuntime>
|
|
567
|
+
<CanvasToolbar :editor="editor" />
|
|
568
|
+
</CanvasLayout>
|
|
569
|
+
</template>
|
|
570
|
+
|
|
571
|
+
<script setup lang="ts">
|
|
572
|
+
import {
|
|
573
|
+
useCanvasEditor,
|
|
574
|
+
CanvasRuntime,
|
|
575
|
+
CanvasLayout,
|
|
576
|
+
CanvasToolbar,
|
|
577
|
+
createDefaultSchema,
|
|
578
|
+
createEmptyFlowModel,
|
|
579
|
+
generateId,
|
|
580
|
+
type FlowNodeModel,
|
|
581
|
+
} from '@blueking/flow-canvas';
|
|
582
|
+
import '@blueking/flow-canvas/style';
|
|
583
|
+
|
|
584
|
+
const { schema, paletteItems } = createDefaultSchema();
|
|
585
|
+
const editor = useCanvasEditor({ initialFlowModel: createEmptyFlowModel(), schema });
|
|
586
|
+
|
|
587
|
+
const quickAddNodeList = [
|
|
588
|
+
{ type: 'empty', label: '空节点', icon: 'flow-canvas-icon canvas-jiedi' },
|
|
589
|
+
{ type: 'parallel-gateway', label: '并行网关', icon: 'flow-canvas-icon canvas-bingxingwangguan' },
|
|
590
|
+
];
|
|
591
|
+
|
|
592
|
+
function handleQuickAdd(
|
|
593
|
+
item: { type: string; label: string },
|
|
594
|
+
insertNodeToRight: (node: Omit<FlowNodeModel, 'position'>) => void,
|
|
595
|
+
closePopover: () => void,
|
|
596
|
+
) {
|
|
597
|
+
insertNodeToRight({
|
|
598
|
+
id: generateId(),
|
|
599
|
+
type: item.type,
|
|
600
|
+
label: item.label,
|
|
601
|
+
});
|
|
602
|
+
closePopover();
|
|
603
|
+
}
|
|
604
|
+
</script>
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
通过 `getBehavior` 可动态控制哪些节点显示"+"按钮:
|
|
608
|
+
|
|
609
|
+
```typescript
|
|
610
|
+
const schema: CanvasSchema = {
|
|
611
|
+
nodeTypes: {
|
|
612
|
+
end: {
|
|
613
|
+
component: EndNode,
|
|
614
|
+
getSize: () => ({ width: 88, height: 40 }),
|
|
615
|
+
getBehavior: () => ({
|
|
616
|
+
quickAddEnabled: false, // 结束节点不显示"+"按钮
|
|
617
|
+
deletable: false,
|
|
618
|
+
}),
|
|
619
|
+
},
|
|
620
|
+
task: {
|
|
621
|
+
component: TaskNode,
|
|
622
|
+
getSize: () => ({ width: 180, height: 60 }),
|
|
623
|
+
getBehavior: (node, ctx) => {
|
|
624
|
+
const hasOutEdge = Object.values(ctx.flowModel.edges)
|
|
625
|
+
.some(e => e.source.nodeId === node.id);
|
|
626
|
+
return {
|
|
627
|
+
quickAddEnabled: !hasOutEdge, // 已有出边时隐藏"+"按钮
|
|
628
|
+
};
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
```
|
|
634
|
+
|
|
376
635
|
### CanvasLayout
|
|
377
636
|
|
|
378
|
-
|
|
637
|
+
可选的布局骨架组件,提供侧边栏 + 主画布 + 底栏的三栏布局。
|
|
638
|
+
|
|
639
|
+
| Prop | 类型 | 默认 | 说明 |
|
|
640
|
+
| ---------------- | --------------------- | ----------- | ---------------------------------------------------- |
|
|
641
|
+
| sidebarCollapsed | `boolean` | `false` | 侧边栏是否收起 |
|
|
642
|
+
| sidebarWidth | `number` | `260` | 侧边栏宽度(px) |
|
|
643
|
+
| hideSidebar | `boolean` | `false` | 是否隐藏侧边栏 |
|
|
644
|
+
| hideFooter | `boolean` | `false` | 是否隐藏底部栏 |
|
|
645
|
+
| editor | `CanvasEditorContext` | `undefined` | 编辑器实例,传入后自动渲染内置 `CanvasNodePalette` |
|
|
646
|
+
| paletteItems | `NodePaletteItem[]` | `undefined` | 节点面板项列表,配合 `editor` 使用自定义面板节点列表 |
|
|
647
|
+
|
|
648
|
+
| Emit | 参数 | 说明 |
|
|
649
|
+
| ----------------------- | --------- | ---------------- |
|
|
650
|
+
| update:sidebarCollapsed | `boolean` | 侧边栏收起状态变更 |
|
|
651
|
+
|
|
652
|
+
| Slot | 说明 |
|
|
653
|
+
| ------- | ------------------------------------------------------------- |
|
|
654
|
+
| sidebar | 侧边栏内容(传入后覆盖内置 `CanvasNodePalette` 默认面板) |
|
|
655
|
+
| default | 主画布区域(放置 `CanvasRuntime` + `CanvasToolbar`) |
|
|
656
|
+
| footer | 底部栏 |
|
|
657
|
+
|
|
658
|
+
#### 侧边栏的三种使用方式
|
|
659
|
+
|
|
660
|
+
**方式一:自动渲染内置节点面板**
|
|
661
|
+
|
|
662
|
+
传入 `editor` prop 且不提供 `sidebar` slot,`CanvasLayout` 自动在侧边栏渲染 `CanvasNodePalette`,用户可从面板拖拽节点到画布:
|
|
663
|
+
|
|
664
|
+
```vue
|
|
665
|
+
<CanvasLayout :editor="editor" :palette-items="paletteItems">
|
|
666
|
+
<CanvasRuntime :editor="editor" />
|
|
667
|
+
</CanvasLayout>
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
通过 `paletteItems` prop 自定义面板中显示的节点类型列表:
|
|
671
|
+
|
|
672
|
+
```vue
|
|
673
|
+
<script setup lang="ts">
|
|
674
|
+
import { createDefaultSchema, useCanvasEditor, createEmptyFlowModel } from '@blueking/flow-canvas';
|
|
675
|
+
|
|
676
|
+
const { schema, paletteItems } = createDefaultSchema();
|
|
677
|
+
const editor = useCanvasEditor({ initialFlowModel: createEmptyFlowModel(), schema });
|
|
678
|
+
|
|
679
|
+
// 也可以手动指定面板项,只展示部分节点类型
|
|
680
|
+
const customPaletteItems = [
|
|
681
|
+
{ type: 'start', label: '开始节点', icon: 'flow-canvas-icon canvas-kaishi' },
|
|
682
|
+
{ type: 'end', label: '结束节点', icon: 'flow-canvas-icon canvas-stop' },
|
|
683
|
+
{ type: 'empty', label: '空节点', icon: 'flow-canvas-icon canvas-jiedi' },
|
|
684
|
+
];
|
|
685
|
+
</script>
|
|
686
|
+
|
|
687
|
+
<template>
|
|
688
|
+
<CanvasLayout :editor="editor" :palette-items="customPaletteItems">
|
|
689
|
+
<CanvasRuntime :editor="editor" />
|
|
690
|
+
</CanvasLayout>
|
|
691
|
+
</template>
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**方式二:通过 sidebar slot 完全自定义侧边栏**
|
|
379
695
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
696
|
+
```vue
|
|
697
|
+
<template>
|
|
698
|
+
<CanvasLayout>
|
|
699
|
+
<template #sidebar>
|
|
700
|
+
<div class="my-sidebar">
|
|
701
|
+
<h3>节点库</h3>
|
|
702
|
+
<div
|
|
703
|
+
v-for="item in nodeItems"
|
|
704
|
+
:key="item.type"
|
|
705
|
+
ref="dndRefs"
|
|
706
|
+
class="my-sidebar__item"
|
|
707
|
+
:data-type="item.type">
|
|
708
|
+
{{ item.label }}
|
|
709
|
+
</div>
|
|
710
|
+
</div>
|
|
711
|
+
</template>
|
|
712
|
+
<CanvasRuntime :editor="editor" />
|
|
713
|
+
</CanvasLayout>
|
|
714
|
+
</template>
|
|
715
|
+
|
|
716
|
+
<script setup lang="ts">
|
|
717
|
+
import { ref, watch } from 'vue';
|
|
718
|
+
import { useCanvasEditor, CanvasRuntime, CanvasLayout, generateId } from '@blueking/flow-canvas';
|
|
719
|
+
|
|
720
|
+
const nodeItems = [
|
|
721
|
+
{ type: 'task', label: '任务' },
|
|
722
|
+
{ type: 'approval', label: '审批' },
|
|
723
|
+
];
|
|
724
|
+
|
|
725
|
+
// 通过 api.registerDndSource 注册拖拽源
|
|
726
|
+
watch(() => editor.api.value, (api) => {
|
|
727
|
+
if (!api) return;
|
|
728
|
+
for (const item of nodeItems) {
|
|
729
|
+
const el = document.querySelector(`[data-type="${item.type}"]`);
|
|
730
|
+
if (el) {
|
|
731
|
+
api.registerDndSource(el as HTMLElement, () => ({
|
|
732
|
+
id: generateId(),
|
|
733
|
+
type: item.type,
|
|
734
|
+
label: item.label,
|
|
735
|
+
position: { x: 0, y: 0 },
|
|
736
|
+
}));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
</script>
|
|
741
|
+
```
|
|
386
742
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
743
|
+
**方式三:隐藏侧边栏**
|
|
744
|
+
|
|
745
|
+
```vue
|
|
746
|
+
<CanvasLayout hide-sidebar>
|
|
747
|
+
<CanvasRuntime :editor="editor" />
|
|
748
|
+
</CanvasLayout>
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### CanvasNodePalette
|
|
752
|
+
|
|
753
|
+
内置的节点面板组件,支持拖拽节点到画布(DnD)。通常由 `CanvasLayout` 自动渲染,也可以单独使用。
|
|
754
|
+
|
|
755
|
+
| Prop | 类型 | 必填 | 说明 |
|
|
756
|
+
| ------ | --------------------- | ---- | ---------------------------------------- |
|
|
757
|
+
| editor | `CanvasEditorContext` | 是 | 编辑器实例 |
|
|
758
|
+
| items | `NodePaletteItem[]` | 否 | 节点列表,不传时从 schema.nodeTypes 生成 |
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
interface NodePaletteItem {
|
|
762
|
+
type: string; // 节点类型,对应 schema.nodeTypes 的 key
|
|
763
|
+
label: string; // 显示名称
|
|
764
|
+
icon?: string; // CSS 图标类名
|
|
765
|
+
}
|
|
766
|
+
```
|
|
392
767
|
|
|
393
768
|
### CanvasToolbar
|
|
394
769
|
|
|
@@ -404,12 +779,20 @@ hover 节点时,右侧端口显示为"+"按钮,支持点击弹出面板和
|
|
|
404
779
|
|
|
405
780
|
工厂函数,用于获取默认工具栏项列表,便于自定义组合。
|
|
406
781
|
|
|
782
|
+
撤销/重做(`undo` / `redo`)默认隐藏,可通过 `include` 恢复显示。
|
|
783
|
+
|
|
407
784
|
```typescript
|
|
408
785
|
import { createDefaultToolbarItems } from '@blueking/flow-canvas';
|
|
409
786
|
|
|
410
|
-
//
|
|
411
|
-
const items =
|
|
412
|
-
|
|
787
|
+
// 默认不含撤销/重做
|
|
788
|
+
const items = createDefaultToolbarItems();
|
|
789
|
+
|
|
790
|
+
// 恢复显示撤销/重做
|
|
791
|
+
const itemsWithHistory = createDefaultToolbarItems({ include: ['undo', 'redo'] });
|
|
792
|
+
|
|
793
|
+
// 恢复撤销/重做,同时排除搜索,追加自定义项
|
|
794
|
+
const customItems = [
|
|
795
|
+
...createDefaultToolbarItems({ include: ['undo', 'redo'], exclude: ['search'] }),
|
|
413
796
|
{ id: 'my-tool', type: 'custom', icon: 'my-icon-class', onClick: handleClick },
|
|
414
797
|
];
|
|
415
798
|
```
|
|
@@ -428,6 +811,114 @@ const items = [
|
|
|
428
811
|
|
|
429
812
|
---
|
|
430
813
|
|
|
814
|
+
## 自定义节点组件
|
|
815
|
+
|
|
816
|
+
节点的渲染由 `CanvasNodeDefinition.component` 指定的 Vue 组件控制。组件内可通过 `inject('getNode')` 获取 X6 Node 实例,读取 FlowNodeModel 数据。
|
|
817
|
+
|
|
818
|
+
### 基础节点组件
|
|
819
|
+
|
|
820
|
+
```vue
|
|
821
|
+
<!-- my-task-node.vue -->
|
|
822
|
+
<template>
|
|
823
|
+
<div class="my-task-node" :class="{ 'is-selected': selected }">
|
|
824
|
+
<i v-if="icon" :class="icon" class="my-task-node__icon" />
|
|
825
|
+
<span class="my-task-node__label">{{ label }}</span>
|
|
826
|
+
</div>
|
|
827
|
+
</template>
|
|
828
|
+
|
|
829
|
+
<script setup lang="ts">
|
|
830
|
+
import { inject, ref, onMounted, onBeforeUnmount, computed } from 'vue';
|
|
831
|
+
import type { FlowNodeModel } from '@blueking/flow-canvas';
|
|
832
|
+
|
|
833
|
+
const getNode = inject<() => any>('getNode')!;
|
|
834
|
+
|
|
835
|
+
const nodeModel = ref<FlowNodeModel>();
|
|
836
|
+
const selected = ref(false);
|
|
837
|
+
|
|
838
|
+
const label = computed(() => nodeModel.value?.label ?? '');
|
|
839
|
+
const icon = computed(() => (nodeModel.value?.extensions as any)?.icon ?? '');
|
|
840
|
+
|
|
841
|
+
onMounted(() => {
|
|
842
|
+
const node = getNode();
|
|
843
|
+
nodeModel.value = node.getData() as FlowNodeModel;
|
|
844
|
+
|
|
845
|
+
node.on('change:data', ({ current }: { current: FlowNodeModel }) => {
|
|
846
|
+
nodeModel.value = current;
|
|
847
|
+
});
|
|
848
|
+
});
|
|
849
|
+
</script>
|
|
850
|
+
|
|
851
|
+
<style scoped>
|
|
852
|
+
.my-task-node {
|
|
853
|
+
display: flex;
|
|
854
|
+
align-items: center;
|
|
855
|
+
gap: 8px;
|
|
856
|
+
width: 100%;
|
|
857
|
+
height: 100%;
|
|
858
|
+
padding: 0 16px;
|
|
859
|
+
background: #fff;
|
|
860
|
+
border: 1px solid #dcdee5;
|
|
861
|
+
border-radius: 4px;
|
|
862
|
+
box-sizing: border-box;
|
|
863
|
+
cursor: pointer;
|
|
864
|
+
}
|
|
865
|
+
.my-task-node.is-selected {
|
|
866
|
+
border-color: #3a84ff;
|
|
867
|
+
}
|
|
868
|
+
</style>
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
### 在 Schema 中注册节点组件
|
|
872
|
+
|
|
873
|
+
```typescript
|
|
874
|
+
import MyTaskNode from './components/my-task-node.vue';
|
|
875
|
+
import MyGatewayNode from './components/my-gateway-node.vue';
|
|
876
|
+
|
|
877
|
+
const schema: CanvasSchema = {
|
|
878
|
+
nodeTypes: {
|
|
879
|
+
task: {
|
|
880
|
+
component: MyTaskNode,
|
|
881
|
+
getSize: () => ({ width: 180, height: 60 }),
|
|
882
|
+
getPorts: () => [
|
|
883
|
+
{ id: 'left', group: 'left' },
|
|
884
|
+
{ id: 'right', group: 'right' },
|
|
885
|
+
],
|
|
886
|
+
getBehavior: (node, ctx) => ({
|
|
887
|
+
deletable: true,
|
|
888
|
+
copyable: true,
|
|
889
|
+
quickAddEnabled: true,
|
|
890
|
+
}),
|
|
891
|
+
},
|
|
892
|
+
gateway: {
|
|
893
|
+
component: MyGatewayNode,
|
|
894
|
+
getSize: () => ({ width: 64, height: 64 }),
|
|
895
|
+
getPorts: () => [
|
|
896
|
+
{ id: 'top', group: 'top' },
|
|
897
|
+
{ id: 'right', group: 'right' },
|
|
898
|
+
{ id: 'bottom', group: 'bottom' },
|
|
899
|
+
{ id: 'left', group: 'left' },
|
|
900
|
+
],
|
|
901
|
+
getBehavior: () => ({
|
|
902
|
+
copyable: false,
|
|
903
|
+
showActions: true,
|
|
904
|
+
}),
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
edgeTypes: {
|
|
908
|
+
default: {
|
|
909
|
+
connector: { name: 'smooth' },
|
|
910
|
+
style: (_edge, state) => ({
|
|
911
|
+
stroke: state.hovered ? '#3a84ff' : '#abb5cc',
|
|
912
|
+
strokeWidth: 2,
|
|
913
|
+
}),
|
|
914
|
+
},
|
|
915
|
+
},
|
|
916
|
+
defaultEdgeType: 'default',
|
|
917
|
+
};
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
---
|
|
921
|
+
|
|
431
922
|
## CanvasApi
|
|
432
923
|
|
|
433
924
|
`editor.api.value` 在 `CanvasRuntime` 挂载后可用,之前为 `null`。
|
|
@@ -649,7 +1140,7 @@ editor.history.clear();
|
|
|
649
1140
|
| 类型 | 说明 |
|
|
650
1141
|
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
651
1142
|
| `CanvasSchema` | 画布配置(nodeTypes / edgeTypes) |
|
|
652
|
-
| `CanvasNodeDefinition` | 节点类型定义(component / getSize / getPorts / getBehavior
|
|
1143
|
+
| `CanvasNodeDefinition` | 节点类型定义(component / getSize / getPorts / getBehavior) |
|
|
653
1144
|
| `CanvasEdgeDefinition` | 边类型定义(router / connector / style / labelDraggable)。`labelRenderer` 字段为 `@experimental`,当前未实现 |
|
|
654
1145
|
| `CanvasCallbackContext` | 运行时上下文(api / flowModel / history / mode) |
|
|
655
1146
|
| `CanvasMode` | 模式:`'edit'` / `'readonly'` / `'thumbnail'` |
|
|
@@ -698,6 +1189,16 @@ editor.history.clear();
|
|
|
698
1189
|
| `ConnectionValidator` | 连接校验函数类型 |
|
|
699
1190
|
| `ConnectionValidateContext` | 连接校验上下文 |
|
|
700
1191
|
| `ConnectionValidateResult` | 连接校验结果 |
|
|
1192
|
+
| `NodePaletteItem` | 节点面板项(type / label / icon) |
|
|
1193
|
+
|
|
1194
|
+
### 默认 Schema
|
|
1195
|
+
|
|
1196
|
+
| 类型 | 说明 |
|
|
1197
|
+
| ------------------------- | ------------------------------------- |
|
|
1198
|
+
| `DefaultNodeTypeConfig` | `createDefaultSchema` 的节点配置项 |
|
|
1199
|
+
| `DefaultSchemaOptions` | `createDefaultSchema` 的选项 |
|
|
1200
|
+
| `DefaultSchemaResult` | `createDefaultSchema` 的返回值 |
|
|
1201
|
+
| `DefaultToolbarItemsOptions` | `createDefaultToolbarItems` 的选项 |
|
|
701
1202
|
|
|
702
1203
|
### 历史与 Overlay
|
|
703
1204
|
|
|
@@ -706,8 +1207,6 @@ editor.history.clear();
|
|
|
706
1207
|
| `CanvasHistory` | 历史管理器接口 |
|
|
707
1208
|
| `CanvasHistoryOptions` | 历史配置(maxHistorySize) |
|
|
708
1209
|
| `OverlayManager` | Overlay 管理器接口 |
|
|
709
|
-
| `NodeOverlayAnchors` | 节点 Overlay 锚点定义 |
|
|
710
|
-
| `OverlayAnchor` | 单个锚点坐标 |
|
|
711
1210
|
|
|
712
1211
|
### 编辑器
|
|
713
1212
|
|