使用antv/g6在Vue中实现流程图,前面还用过bpmn插件,效果没有这个好
1. 在项目中使用 NPM 包引入
npm install --save @antv/g6
2. 在需要用的 G6 的 JS 文件中导入:
import G6 from '@antv/g6';
Step 1 创建容器
需要在 HTML 中创建一个用于容纳 G6 绘制的图的容器,通常为 div 标签。G6 在绘制时会在该容器下追加 canvas 标签,然后将图绘制在其中。
<div id="mountNode"></div>
Step 2 数据准备 引入 G6 的数据源为 JSON 格式的对象。该对象中需要有节点(nodes)和边(edges)字段,分别用数组表示:
const data = {
// 点集
nodes: [
{
id: 'node1', // String,该节点存在则必须,节点的唯一标识
x: 100, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
},
{
id: 'node2', // String,该节点存在则必须,节点的唯一标识
x: 300, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
},
],
// 边集
edges: [
{
source: 'node1', // String,必须,起始点 id
target: 'node2', // String,必须,目标点 id
},
],
};
注意
nodes 数组中包含节点对象。每个节点对象中唯一的、必要的 id 以标识不同的节点,x、 y 指定该节点的位置;
edges 数组中包含边对象。source 和 target 是每条边的必要属性,分别代表了该边的起始点 id 与 目标点 id。
点和边的其他属性参见链接:内置节点 和 内置边。
Step 3 创建关系图 创建关系图(实例化)时,至少需要为图设置容器、宽和高。
const graph = new G6.Graph({
container: 'mountNode', // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
width: 800, // Number,必须,图的宽度
height: 500, // Number,必须,图的高度
});
Step 4 配置数据源,渲染
graph.data(data); // 读取 Step 2 中的数据源到图上
graph.render(); // 渲染图
以上是官网最简单的的例子 G6官网
下面是自己根据要求修改的
<template>
<div id="mountNode"></div>
</template>
<script>
import G6 from '@antv/g6'
export default {
data() {
return {
data: {
// 点集
nodes: [
{
id: 'node1', // String,该节点存在则必须,节点的唯一标识
x: 100, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
type: 'rect', // 矩形节点
label: '省生态环境厅', // 矩形节点框内的文字
// 矩形节点框样式
style: {
fill: '#18c298ad',
stroke: '#18c298',
lineWidth: 2,
radius: 7
},
// 节点下的文字
extraText: '[派发]'
},
{
id: 'node2', // String,该节点存在则必须,节点的唯一标识
x: 300, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
type: 'rect',
label: '市生态环境局',
style: {
fill: '#18c298ad',
stroke: '#18c298',
lineWidth: 2,
radius: 7
},
extraText: '[派发]'
},
{
id: 'node3', // String,该节点存在则必须,节点的唯一标识
x: 500, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
type: 'rect',
label: '核查员',
style: {
fill: '#ffc107',
stroke: '#ff9800',
lineWidth: 2,
radius: 7
},
extraText: '[派发]',
text1: '催办'
},
{
id: 'node4', // String,该节点存在则必须,节点的唯一标识
x: 700, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
type: 'rect',
label: '市生态环境局',
style: {
fill: '#18c298ad',
stroke: '#18c298',
lineWidth: 2,
radius: 7
},
extraText: '[派发]'
},
{
id: 'node5', // String,该节点存在则必须,节点的唯一标识
x: 900, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
type: 'rect',
label: '省生态环境厅',
style: {
fill: '#0063ffb5',
stroke: '#0063ff',
lineWidth: 2,
radius: 7
},
extraText: '[派发]'
},
{
id: 'node6', // String,该节点存在则必须,节点的唯一标识
x: 1200, // Number,可选,节点位置的 x 值
y: 200, // Number,可选,节点位置的 y 值
type: 'rect',
label: '完成',
style: {
fill: '#0063ffb5',
stroke: '#0063ff',
lineWidth: 2,
radius: 7
},
// 设置箭头位置 https://antv-g6.gitee.io/zh/docs/manual/middle/elements/nodes/anchorpoint
anchorPoints: [
[0, 0.5],
[0.5, 0.5]
]
},
{
id: 'node7', // String,该节点存在则必须,节点的唯一标识
x: 1200, // Number,可选,节点位置的 x 值
y: 300, // Number,可选,节点位置的 y 值
type: 'rect',
label: '已完成',
style: {
fill: '#18c298ad',
stroke: '#18c298',
lineWidth: 2,
radius: 7
},
anchorPoints: [
[0, 0.5],
[0.5, 0.5]
]
},
{
id: 'node8', // String,该节点存在则必须,节点的唯一标识
x: 1200, // Number,可选,节点位置的 x 值
y: 400, // Number,可选,节点位置的 y 值
type: 'rect',
label: '进行中',
style: {
fill: '#ffc107',
stroke: '#ff9800',
lineWidth: 2,
radius: 7
},
anchorPoints: [
[0, 0.5],
[0.5, 0.5]
]
},
{
id: 'node9', // String,该节点存在则必须,节点的唯一标识
x: 1200, // Number,可选,节点位置的 x 值
y: 500, // Number,可选,节点位置的 y 值
type: 'rect',
label: '未开始',
style: {
fill: '#0063ffb5',
stroke: '#0063ff',
lineWidth: 2,
radius: 7
},
anchorPoints: [
[0.5, 0.5],
[1, 0.5]
]
}
],
// 边集
edges: [
{
source: 'node1', // String,必须,起始点 id
target: 'node2', // String,必须,目标点 id
label: '', // 边的文本
style: {
// endArrow: true,
// startArrow: false,
// 箭头样式
endArrow: {
lineDash: false,
path: G6.Arrow.triangle(10, 10, 2), // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应)
d: 2,
fill: '#18c298ad',
stroke: '#18c298'
// ...
},
fill: '#18c298ad',
stroke: '#18c298'
}
},
{
source: 'node2', // String,必须,起始点 id
target: 'node3', // String,必须,目标点 id
label: '', // 边的文本
style: {
// endArrow: true,
// startArrow: false,
endArrow: {
lineDash: false,
path: G6.Arrow.triangle(10, 10, 2),
d: 2,
fill: '#18c298ad',
stroke: '#18c298'
// ...
},
fill: '#18c298ad',
stroke: '#18c298'
}
},
{
source: 'node3', // String,必须,起始点 id
target: 'node4', // String,必须,目标点 id
label: '', // 边的文本
style: {
// endArrow: true,
// startArrow: false,
endArrow: {
lineDash: false,
path: G6.Arrow.triangle(10, 10, 2),
d: 2,
fill: '#18c298ad',
stroke: '#18c298'
// ...
},
fill: '#18c298ad',
stroke: '#18c298'
}
},
{
source: 'node4', // String,必须,起始点 id
target: 'node3', // String,必须,目标点 id
label: '不合格', // 边的文本
type: 'polyline',
controlPoints: [{ x: 750, y: 250 }, { x: 550, y: 250 }],
style: {
endArrow: {
path: G6.Arrow.triangle(10, 10, 2),
d: 2,
fill: '#18c298ad',
stroke: '#18c298'
}
},
fill: '#18c298ad',
stroke: '#18c298'
},
{
source: 'node4', // String,必须,起始点 id
target: 'node5', // String,必须,目标点 id
label: '合格', // 边的文本
style: {
// endArrow: true,
// startArrow: false,
fill: '#0063ffb5',
stroke: '#0063ff',
endArrow: {
lineDash: false,
path: G6.Arrow.triangle(10, 10, 2),
d: 2,
fill: '#0063ffb5',
stroke: '#0063ff'
// ...
}
}
},
{
source: 'node5', // String,必须,起始点 id
target: 'node6', // String,必须,目标点 id
label: '', // 边的文本
style: {
lineDash: [5, 5, 5],
fill: '#0063ffb5',
stroke: '#0063ff',
endArrow: {
lineDash: false,
path: G6.Arrow.triangle(10, 10, 2),
d: 2,
fill: '#0063ffb5',
stroke: '#0063ffb5'
}
}
},
{
source: 'node6', // String,必须,起始点 id
target: 'node7', // String,必须,目标点 id
label: '', // 边的文本
style: {
lineDash: [5, 5, 5],
fill: '#ddd',
stroke: '#ddd'
},
type: 'hvh'
},
{
source: 'node7', // String,必须,起始点 id
target: 'node8', // String,必须,目标点 id
label: '', // 边的文本
style: {
lineDash: [5, 5, 5],
fill: '#ddd',
stroke: '#ddd'
},
type: 'hvh'
},
{
source: 'node8', // String,必须,起始点 id
target: 'node9', // String,必须,目标点 id
label: '', // 边的文本
style: {
lineDash: [5, 5, 5],
fill: '#ddd',
stroke: '#ddd'
},
type: 'hvh'
}
]
}
}
},
mounted() {
this.getView()
},
methods: {
getView() {
const container = document.getElementById('mountNode')
const width = container.scrollWidth
const height = container.scrollHeight
// const width = window.
const graph = new G6.Graph({
modes: {
default: ['drag-canvas', 'zoom-canvas'] // 允许拖拽画布、放缩画布、拖拽节点
},
container: 'mountNode', // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
fitView: true, // 是否将图适配到画布大小,可以防止超出画布或留白太多。
width: width, // Number,必须,图的宽度
height: height, // Number,必须,图的高度
size: [100, 500]
})
G6.registerNode('diamond', {
options: {
style: {},
stateStyles: {
hover: {},
selected: {}
}
},
/**
* 绘制节点,包含文本
* @param {Object} cfg 节点的配置项
* @param {G.Group} group 图形分组,节点中图形对象的容器
* @return {G.Shape} 返回一个绘制的图形作为 keyShape,通过 node.get('keyShape') 可以获取。
* 关于 keyShape 可参考文档 核心概念-节点/边/Combo-图形 Shape 与 keyShape
*/
draw(cfg, group) {},
/**
* 绘制后的附加操作,默认没有任何操作
* @param {Object} cfg 节点的配置项
* @param {G.Group} group 图形分组,节点中图形对象的容器
*/
afterDraw(cfg, group) {},
/**
* 更新节点,包含文本
* @override
* @param {Object} cfg 节点的配置项
* @param {Node} node 节点
*/
update(cfg, node) {},
/**
* 更新节点后的操作,一般同 afterDraw 配合使用
* @override
* @param {Object} cfg 节点的配置项
* @param {Node} node 节点
*/
afterUpdate(cfg, node) {},
/**
* 响应节点的状态变化。
* 在需要使用动画来响应状态变化时需要被复写,其他样式的响应参见下文提及的 [配置状态样式] 文档
* @param {String} name 状态名称
* @param {Object} value 状态值
* @param {Node} node 节点
*/
setState(name, value, node) {},
/**
* 获取锚点(相关边的连入点)
* @param {Object} cfg 节点的配置项
* @return {Array|null} 锚点(相关边的连入点)的数组,如果为 null,则没有控制点
*/
getAnchorPoints(cfg) {}
},
// 继承内置节点类型的名字,例如基类 'single-node',或 'circle', 'rect' 等
// 当不指定该参数则代表不继承任何内置节点类型
'single-node', // 基于 single-node 为例进行扩展
)
// 动态设置节点样式
// graph.node((node) => {
// console.log(node)
// return {
// id: node.id,
// type: 'rect',
// style: {
// fill: 'blue'
// }
// }
// })
graph.data(this.data) // 读取 Step 2 中的数据源到图上
// 给节点下方展示额外的文字
graph.on('afterrender', () => {
graph.getNodes().forEach(node => {
const { extraText, text1 } = node.getModel()
const bbox = node.getBBox()
const centerX = bbox.minX + bbox.width / 2
const centerY = bbox.minY + bbox.height / 2
const textGroup = graph.get('group').addGroup()
const texta = textGroup.addShape('text', {
attrs: {
text: text1,
x: centerX,
y: centerY + 45,
textAlign: 'center',
textBaseline: 'middle',
type: 'rect',
fill: '#000',
cursor: 'pointer',
fontSize: 12
}
})
textGroup.addShape('text', {
attrs: {
text: extraText,
x: centerX,
y: centerY + 25,
textAlign: 'center',
textBaseline: 'middle',
fill: '#000',
fontSize: 12
}
})
// 添加边框
const bbox1 = texta.getBBox()
if (bbox1.height !== 0) {
textGroup.addShape('rect', {
attrs: {
x: bbox1.x - 4,
y: bbox1.y - 4,
width: bbox1.width + 10,
height: bbox1.height + 8,
stroke: '#000',
lineWidth: 2,
cursor: 'pointer',
radius: 7
}
})
}
// 指定文字的点击事件
texta.on('click', () => {
// 处理点击事件的逻辑
console.log('Label clicked!')
})
})
})
graph.render() // 渲染图
}
}
}
</script>
<style>
#mountNode{
width: 100%;
height: 500px;
}
</style>
最终实现的效果