在vue项目中使用AntV X6构建流程图
绘制的流程页面如下:

一、先在页面绘制两个div,放置左侧模型栏与右侧画布
<template>
<div >
<div class="content">
<div class="app-stencil" ref="stencilContainer">
</div>
<div class="app-content" id="flowContainer" ref="container">
</div>
</div>
</div>
</template>
二、js代码
1、引入X6
import {Graph, Shape, Addon, FunctionExt} from '@antv/x6';
const {Stencil} = Addon
const {Rect} = Shape
2、初始化画布
this.graph = new Graph({}) //配置可参考官网
3、左侧模型栏
const stencil = new Stencil({}) //配置可参考官网
4、节点链接桩的配置
data() {
//先在data里面定义,在绘制的时候直接使用
ports : {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
},
items: [
{
group: 'top',
},
{
group: 'right',
},
{
group: 'bottom',
},
{
group: 'left',
},
],
},
}
5、绘制节点的两种方式:
(1)第一种
const u3 = new Rect({
width: 80,
height: 50,
attrs: {
text: {text: '资产审核员', fill: 'end'},
body: {
stroke: '#1890FF',
fill: 'rgba(24, 144, 255, 0.1)',
rx: 2,
ry: 2,
strokeWidth: 1,
},
label: {
fontSize: 14,
fill: '#666',
fontWeight: 400,
}
},
ports: {
...this.ports
}
})
(2)第二种
Graph.registerNode(
'custom-polygon',
{
inherit: 'polygon',
width: 80,
height: 50,
attrs: {
body: {
strokeWidth: 1,
stroke: '#FFAC32',
fill: 'rgba(255, 172, 50, 0.1)',
},
text: {
fontSize: 14,
fill: '#666666',
},
},
ports: {
...this.ports,
items: [
{
group: 'top',
},
{
group: 'bottom',
},
{
group: 'right',
},
{
group: 'left',
},
],
},
},
true,
)
const a1 = this.graph.createNode({
shape: 'custom-polygon',
attrs: {
body: {
refPoints: '0,10 10,0 20,10 10,20',
},
},
label: '判断',
})
6、流程数据转化
this.graph.toJSON().cells//获取流程图的数据
this.graph.fromJSON(this.cellsData)//将数据转为流程图需要的格式
7、常用事件
this.graph.on('node:mouseenter', ({ node }) => {})//节点-鼠标滑过
this.graph.on('node:mouseleave', ({node}) => {})//节点-鼠标离开
this.graph.on('edge:mouseenter', ({ edge }) => {})//边-鼠标滑过
this.graph.on('edge:mouseleave', ({ edge }) => {})//边-鼠标离开
this.graph.on('node:click', ({ node }) => {})//节点-点击
this.graph.on('node:added', ({ node }) => {})//节点-添加
this.graph.on('node:removed', ({ node })=> {})//节点-删除
this.graph.on('edge:added', ({ edge})=> {})//边-添加
this.graph.on('edge:removed', ({ edge})=> {})//边-删除
this.graph.on('edge:click', ({ edge }) => {})//边-点击
this.graph.getNodes()//获取所有节点
this.graph.getEdges()//获取所有边
this.graph.getCellById(id)//通过id获取边/节点
edge.setLabels(['通过'])//设置边的label属性
8、节点/边添加删除操作
this.graph.on('node:mouseenter', ({ node }) => {
//节点-鼠标进入添加删除操作
const ports = container.querySelectorAll(
'.x6-port-body'
)
this.showPorts(ports, true)
node.addTools({
name: 'button-remove',
args: {
x: 0,
y: 0,
offset: { x: 10, y: 10 },
},
})
})
9、画布缩放
sizeExpend(){
//放大
this.graph.zoom(0.2)
},
sizeShrink(){
//缩小
this.graph.zoom(-0.2)
},
sizeInit(){
//复原
this.graph.centerContent()
this.graph.zoom(0.2, {
absolute:true,
minScale: 1,
maxScale: 5,
scaleGrid: 0.5,
})
}
10、代码如下
methods:{
init() {
this.graph = new Graph({
container: this.$refs.container,
width: '100%',
height: '100%',
grid: {
size: 10,
visible: true,
type: 'doubleMesh',
args: [
{
color: '#f5f5f5',
thickness: 1
},
{
color: '#f8f8f8',
thickness: 1,
factor: 4
}
]
},
snapline: {
enabled: true,
sharp: true,
},
mousewheel: {
enabled: true,
modifiers: ['ctrl', 'meta'],
minScale: 0.5,
maxScale: 2
},
// 画布调整
selecting: {
enabled: true,
multiple: true,
rubberband: true,
movable: true,
showNodeSelectionBox: true
},
connecting: {
anchor: 'center',
connectionPoint: 'anchor',
allowBlank: false,
highlight: true,
snap: true,
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: '#ccc',
strokeWidth: 1,
targetMarker: {
name: 'classic',
size: 8
}
},
},
label: {
attrs: {
label: {
// text:'ok',
fill: '#A2B1C3',
fontSize: 12,
},
},
},
router: {
name: 'manhattan'
},
zIndex: 0
})
},
validateConnection({
sourceView,
targetView,
sourceMagnet,
targetMagnet
}) {
if (sourceView === targetView) {
return false
}
if (!sourceMagnet) {
return false
}
if (!targetMagnet) {
return false
}
return true
}
},
highlighting: {
magnetAvailable: {
name: 'stroke',
args: {
padding: 4,
attrs: {
strokeWidth: 4,
stroke: 'rgba(223,234,255)'
}
}
}
},
snapline: true,
history: true,
clipboard: {
enabled: true
},
keyboard: {
enabled: true
},
embedding: {
enabled: true,
findParent({node}) {
const bbox = node.getBBox()
return this.getNodes().filter((node) => {
// 只有 data.parent 为 true 的节点才是父节点
const data = node.getData()
if (data && data.parent) {
const targetBBox = node.getBBox()
return bbox.isIntersectWithRect(targetBBox)
}
return false
})
}
}
})
// graph.isPannable() // 画布是否可以平移
// graph.enablePanning() // 启用画布平移
this.graph.centerContent()
/******************************** 左侧模型栏 ****************************/
const stencil = new Stencil({
title: '组件',
target: this.graph,
search: false, // 搜索
collapsable: false,
stencilGraphWidth: 240,
stencilGraphHeight: 250,
groups: [
{
name: 'processLibrary',
title: '流程事件',
},
{
name: 'staffPool',
title: '流程操作',
},
],
})
this.$refs.stencilContainer.appendChild(stencil.container)
const u3 = new Rect({
width: 80,
height: 50,
attrs: {
text: {text: '资产审核员', fill: 'end'},
body: {
stroke: '#1890FF',
fill: 'rgba(24, 144, 255, 0.1)',
rx: 2,
ry: 2,
strokeWidth: 1,
},
label: {
fontSize: 14,
fill: '#666',
fontWeight: 400,
}
},
ports: {
...this.ports
}
})
const u4 = new Rect({
width: 80,
height: 50,
attrs: {
text: {text: '审核员', fill: 'end'},
body: {
stroke: '#A318FF',
fill: 'rgba(163, 24, 255, 0.1)',
rx: 28,
ry: 28,
strokeWidth: 1,
},
label: {
fontSize: 14,
fill: '#666',
fontWeight: 400,
}
},
ports: {
...this.ports
}
})
stencil.load([u3,u4], 'processLibrary')
Graph.registerNode(
'custom-polygon',
{
inherit: 'polygon',
width: 80,
height: 50,
attrs: {
body: {
strokeWidth: 1,
stroke: '#FFAC32',
fill: 'rgba(255, 172, 50, 0.1)',
},
text: {
fontSize: 14,
fill: '#666666',
},
},
ports: {
...this.ports,
items: [
{
group: 'top',
},
{
group: 'bottom',
},
{
group: 'right',
},
{
group: 'left',
},
],
},
},
true,
)
const a1 = this.graph.createNode({
shape: 'custom-polygon',
attrs: {
body: {
refPoints: '0,10 10,0 20,10 10,20',
},
},
label: '判断',
})
stencil.load([a1], 'staffPool')
this.graph.fromJSON(this.cellsData);
let nodeArr = this.graph.getNodes()
this.configForm = nodeArr.map((item)=>{
let obj = {}
obj.id = item.id
obj.text = item.store.data.attrs.text.text
return obj
})
/************************************** 绑定事件 *********************************/
const container = document.getElementById('flowContainer')
},
showPorts(ports, show) {
for (let i = 0, len = ports.length; i < len; i = i + 1) {
ports[i].style.visibility = show ? 'visible' : 'hidden'
}
},
}