x6-graph-plugin
v1.0.0
Published
基于 AntV X6 的 Vue 3 图组件库,提供 `X6Graph` 组件。
Readme
x6-graph-plugin
基于 AntV X6 的 Vue 3 图组件库,提供 X6Graph 组件。
本文档整合了以下内容:
- 快速安装与接入说明
X6Graph.vue和X6GraphManager.js的 API- 组件实践——基于
载荷资源管控系统(商⭐)->进程拓扑模块的 computed 自动更新机制说明
1. 组件概述
X6Graph 是一个 Vue 3 组件,内部封装 X6GraphManager,用于:
- 初始化 / 销毁 X6 Graph
- 通过
nodes/edges数据驱动图内容 - 输出节点/连线交互事件
- 在容器尺寸变化时自动重绘并可选自动适配
- 支持“消息驱动 + computed”自动刷新图
适用于拓扑图、状态图、连线关系图等场景。
2. 开发与构建
npm install
npm run dev
npm run build
npm run build:lib3. 安装
npm i x6-graph-plugin宿主项目需要安装 vue(peer 依赖)。
4. 快速使用
4.1 按需引入组件
<template>
<X6Graph
:nodes="nodes"
:edges="edges"
:graph-options="graphOptions"
:plugins="plugins"
:auto-fit="true"
:fit-options="{ centerX: null, centerY: null, zoom: 0.8 }"
@nodeClick="onNodeClick"
@edgeClick="onEdgeClick"
/>
</template>
<script setup>
import { X6Graph } from "x6-graph-plugin";
import "x6-graph-plugin/style.css";
const nodes = [];
const edges = [];
const graphOptions = {};
const plugins = {};
const onNodeClick = (args) => {};
const onEdgeClick = (args) => {};
</script>4.2 插件方式注册
import { createApp } from "vue";
import X6GraphPlugin from "x6-graph-plugin";
import "x6-graph-plugin/style.css";
const app = createApp(App);
app.use(X6GraphPlugin);5. X6Graph 组件 API
5.1 Props(必填与默认值)
X6Graph.vue中所有 props 都是可选;不传会使用默认值。
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| graphOptions | Object | 否 | {} | 透传到 X6 Graph 的配置(会与内置默认配置合并) |
| plugins | Object | 否 | {} | 插件开关配置:selection/snapline/history/keyboard/transform |
| nodes | Array | 否 | [] | 节点配置数组,建议每个节点都提供稳定 id |
| edges | Array | 否 | [] | 连线配置数组,强烈建议每条边都提供稳定 id |
| autoFit | Boolean | 否 | true | 每次加载数据/尺寸变化后是否自动适配 |
| fitOptions | Object | 否 | { centerX: null, centerY: null, zoom: 0.8 } | 缩放适配参数 |
| showHint | Boolean | 否 | true | 是否显示左上角提示“按住 Shift 拖动画布,Ctrl+滚轮 缩放” |
graphOptions 在组件层默认是 {},但实际会与 X6GraphManager 的内置默认配置合并。默认项如下:
{
background: { color: "rgba(10, 22, 44, 0.85)" },
grid: {
size: 10,
visible: true,
type: "dot",
args: { color: "#2b3a55", thickness: 1 }
},
panning: {
enabled: true,
modifiers: "shift"
},
mousewheel: {
enabled: true,
modifiers: ["ctrl", "meta"],
minScale: 0.5,
maxScale: 10
},
interacting: {
nodeMovable: true,
edgeLabelMovable: true,
resizing: false
},
autoResize: true
}plugins 的内置默认值如下(组件层默认是 {},最终同样会合并):
{
selection: true,
snapline: true,
history: true,
keyboard: true,
transform: true
}plugins 为 true 时的插件内部默认参数
selection: new Selection({ multiple: true, rubberband: true, movable: true })
snapline: new Snapline({ enabled: true, sharp: true })
history: new History()
keyboard: new Keyboard()
transform: new Transform({ rotating: false })5.2 关键必填项说明(避免误用)
组件层必填
- 无强制必填项(都有默认值)。
业务层强烈建议必填
nodes[].id:用于稳定增量更新、删除和回调定位。edges[].id:用于连线差分更新;不传会退化为“清空旧边并重建”。edges[].source/target:创建边时应提供有效连接目标。
fitOptions使用规则- 当
centerX和centerY都有值时:按zoom+ 中心点定位。 - 否则:走
zoomToFit({ padding: 20 })。
- 当
事件模型
- 推荐仅使用一种:组件事件(
@nodeClick/@edgeClick)或配置回调(onClick)。 - 两种同时绑定同一逻辑可能重复触发。
- 推荐仅使用一种:组件事件(
5.3 Events(defineEmits)
| 事件名 | 参数 | 说明 |
|---|---|---|
| init | graphManager | 图初始化并首次加载后触发 |
| nodeClick | args | 节点点击 |
| nodeDblClick | args | 节点双击 |
| edgeClick | args | 连线点击 |
| graphChange | args | 图变化事件 |
5.4 Expose(通过组件 ref)
| 字段/方法 | 说明 |
|---|---|
| graphManager | X6GraphManager 实例(初始化前为 null) |
| loadData() | 手动触发一次 updateGraph({ nodes, edges }) |
| handleResize() | 手动触发尺寸重算与可选 autoFit |
推荐通过
@init获取管理器实例,时机更稳定。
6. 数据结构规范(nodes / edges)
6.1 前提说明
节点或边的属性配置,本组件并没有封装,所以相关属性仍是AntV X6原生属性,具体请查看官方文档
https://x6-v2.antv.vision/api/model/node
以下是本组件使用的AntV X6的版本信息
- "@antv/x6": "^2.18.1",
- "@antv/x6-plugin-history": "^2.2.4",
- "@antv/x6-plugin-keyboard": "^2.2.3",
- "@antv/x6-plugin-scroller": "^2.0.10",
- "@antv/x6-plugin-selection": "^2.2.2",
- "@antv/x6-plugin-snapline": "^2.1.7",
- "@antv/x6-plugin-transform": "^2.1.8"
6.2 节点 nodes[] 常用字段
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | string \| number | 必填且稳定唯一 |
| shape | string | 节点形状 |
| x/y | number | 节点位置 |
| width/height | number | 节点尺寸 |
| attrs | Object | 样式(支持深合并) |
| interacting | Object | 交互配置 |
| ports | Object | 端口配置 |
| onClick | Function | 节点点击回调(配置回调模型) |
| onDblClick | Function | 节点双击回调 |
| onDoubleClick | Function | 双击回调兼容别名 |
节点默认值(X6GraphManager.createNode)
当某个节点字段未传时,会使用以下默认值:
{
shape: "rect",
width: 100,
height: 60,
attrs: {
body: {
rx: 4,
ry: 4,
stroke: "#333",
fill: "#fff"
},
label: {
text: externalNodeId || "",
fill: "#333",
fontSize: 14
}
},
interacting: {
nodeMovable: true,
edgeLabelMovable: true,
resizing: false
}
}说明:节点
id在内部会做字符串规范化(例如1会转为"1")。
6.3 连线 edges[] 常用字段
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | string \| number | 建议必填且稳定唯一 |
| source | string \| { cell, port } | 源节点/端口 |
| target | string \| { cell, port } | 目标节点/端口 |
| router | Object | 路由策略 |
| connector | Object | 连接器策略 |
| zIndex | number | 层级 |
| attrs | Object | 样式(支持深合并) |
| labels | Array | 边标签 |
| data | Object | 自定义数据 |
| onClick | Function | 连线点击回调(配置回调模型) |
连线默认值(X6GraphManager.createEdge)
当某个连线字段未传时,会使用以下默认值:
{
router: { name: "orth" },
connector: { name: "rounded" },
zIndex: 2,
attrs: {
line: {
strokeWidth: 2,
stroke: "#999",
targetMarker: null
}
}
}额外行为:
- 若未提供
edge.id,管理器会自动生成临时 id(内部计数)写入edge.data.edgeId。 - 该临时 id 可用于点击回调命中,但不适合作为长期稳定差分 key,建议给定唯一id。
7. 更新机制(updateGraph)
X6Graph 通过 watch(() => [props.nodes, props.edges], { deep: true }) 自动调用:
graphManager.updateGraph({ nodes, edges })7.1 节点更新策略
- 新数据有、旧图无 -> 创建
- 新旧都有 -> 增量更新(只改传入字段)
- 旧图有、新数据无 -> 删除
attrs以顶层 key 深合并x/y/width/height仅在字段非undefined时更新- 用户拖拽后的节点位置会缓存,后续消息更新可避免被旧坐标覆盖
7.2 连线更新策略
- 当所有边都有稳定
id:按id做差分保留/删除/更新 - 当存在边缺失
id:退化为“清空旧边并全量重建” attrs/source/target/router/connector/zIndex仅对传入字段生效
7.3 其他内部默认/约定行为
- ID 规范化:节点和边的
id会统一转字符串比较,避免1与"1"被视为不同。 - attrs 深合并规则:只对你传入的
attrs顶层 key 执行深合并,未传 key 保留原值。 - 拖拽保持:节点触发
node:moved后会记录位置,后续外部更新若仍带旧x/y,可避免把节点拉回初始点。
8. 两种事件回调模型
为兼容不同用法,支持两种事件模型,建议项目内统一一种。
模型 A:组件事件(推荐)
@nodeClick@nodeDblClick@edgeClick
通常通过 args.cell.id 判断当前节点/连线。
模型 B:配置回调(可选)
- 节点配置中写
onClick/onDblClick - 连线配置中写
onClick
注意
如果模型 A + 模型 B 同时写了同一套逻辑,可能重复触发。
9. X6GraphManager API
9.1 生命周期
new X6GraphManager(options)
options.container: 容器 DOM(必填)options.graphOptions: 图配置(可选)options.plugins: 插件配置(可选)
init(): boolean
- 初始化 Graph,挂载插件并绑定内部事件
destroy(): void
- 销毁图并清空缓存(节点、连线、事件、拖拽位置)
9.2 数据 API
createNode(config)createNodes(nodes)getNode(nodeId)updateNode(nodeId, updates)removeNode(nodeId)createEdge(config)createEdges(edges)updateEdge(edgeId, updates)removeEdge(edgeId)clear()updateGraph({ nodes, edges })
9.3 视图 API
zoomFit(centerX, centerY, zoom = 0.8): numberresize(width, height): void
9.4 事件 API
on(event, handler)off(event, handler)emit(event, data)
10. 结合载荷资源管控系统(商⭐)进程拓扑 的自动更新功能说明
该项目中的进程拓扑采用了“消息驱动 + computed 渲染”模式,流程如下:
- 通过
pubSub.subscribe(...)监听多类消息(多波束、地检、综合处理器、数字仿真、地址变更) - 在回调中更新响应式状态:
statusDatagroundDetectionStatusDataintegratedProcessorStatusDatadsStatusDataqueryDateMap
graphNodes与graphEdges(computed)基于状态自动重算X6Graph接收新nodes/edges,触发内部watchgraphManager.updateGraph(...)自动把变化同步到画布
即:不需要手动调用刷新,消息变化即可自动更新图。
注:本项目中也将其核心文件移入供参考,文件路径如下
x6-graph/src/components/UsedDemoOfPps/index.vue
实战建议
1.初始化加载效果-对于需要后端返回初始化数据的情况
- 在页面初始化时,如果后端接口没有及时返回状态信息,就会导致节点等已按你给定的初始值在页面上展示, 而正确的状态要在得到后端状态数据时才能在正确显示,所以推荐做"加载中"处理,这点也可参考上面提到的文件
11. 最小接入示例
<template>
<X6Graph
:nodes="graphNodes"
:edges="graphEdges"
:graph-options="graphOptions"
:plugins="plugins"
@init="onInit"
@nodeClick="onNodeClick"
/>
</template>
<script setup>
import { ref, computed } from "vue";
import X6Graph from "@/components/KydX6Graph/X6Graph.vue";
const graphManagerRef = ref(null);
const status = ref({ online: 1 });
const graphNodes = computed(() => [
{
id: "a",
x: 80,
y: 80,
width: 140,
height: 80,
attrs: {
body: { fill: "#123", stroke: status.value.online ? "#00ff00" : "#ff0000" },
label: { text: "节点A", fill: "#fff" },
},
},
]);
const graphEdges = computed(() => [
{
id: "e-a-b",
source: "a",
target: "b",
attrs: { line: { stroke: "#00ff00", strokeWidth: 2 } },
},
]);
const graphOptions = {};
const plugins = {};
const onInit = (manager) => {
graphManagerRef.value = manager;
};
const onNodeClick = (args) => {
console.log("node click:", args.cell?.id);
};
</script>12. FAQ
Q1: 为什么更新 edges 会偶尔闪动?
当 edges 存在缺失 id 的项时,updateGraph 会走“删旧重建”策略。请为每条边补稳定 id。
Q2: 可以同时用 @nodeClick 和 node.onClick 吗?
可以,但不建议同一逻辑双绑定,避免重复执行。
Q3: 如何外部手动触发适配?
可通过组件 ref 调用 handleResize(),或在 @init 拿到 manager 后调用 manager.zoomFit(...)。
13. 作者及版本信息
- author: lixiang
- version: 1.0.1
- date: 2026/3/20
- author: lixiang
- version: 1.0.2
- date: 2026/3/26
