antV G6流程图在Vue中的使用

更新内容

前言

最近我司项目中需要加入流程图制作功能,于是乎百度各种找可视化绘制拓扑图的轮子,大部分都是国外的,看文档太吃力,不过好在最终让我发现了AntV G6流程图图表库,最新版为2.0,不过编辑器在2.0版本还没有进行开源,所以只能退而求其次,使用了1.2.8版本。希望2.0版本的编辑器尽早开源,在交互方面1.2.8版本还是差了一些。

技术栈

效果图

流程图.gif

引入

另外需要引入Element-UI

index.html中进行了全局引用

1
2

<script src="./static/plugin/g6.min.js"></script>

实例代码

仿照2.0版本的编辑器将G6作为了一个组件使用,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<template>
<div id="flowChart">
<div class="operating">
<div class="btn-group">
<div class="btn" @click="addCircle" title="起始节点">
<i class="iconfont icon-circle-oeps"></i>
</div>
<div class="btn" @click="addRect" title="常规节点">
<i class="iconfont icon-square-oeps"></i>
</div>
<div class="btn" @click="addRhombus" title="条件节点">
<i class="iconfont icon-square-ling"></i>
</div>
</div>
<div class="btn-group">
<div class="btn" @click="addLine" title="直线">
<i class="iconfont icon-zhixian"></i>
</div>
<div class="btn" @click="addSmooth" title="曲线">
<i class="iconfont icon-quxian"></i>
</div>
<div class="btn" @click="addArrowLine" title="箭头直线">
<i class="iconfont icon-jiantouzhixian"></i>
</div>
<div class="btn" @click="addArrowSmooth" title="箭头曲线">
<i class="iconfont icon-jiantouquxian"></i>
</div>
</div>
<div class="btn-group">
<div class="btn" @click="changeMode('edit')" title="选择模式">
<i class="iconfont icon-chose"></i>
</div>
<div class="btn" @click="changeMode('drag')" title="拖拽模式">
<i class="iconfont icon-move"></i>
</div>
</div>
<div class="btn-group">
<div class="btn" @click="del" style="margin-top: 5px;" title="删除">
<i class="el-icon-delete"></i>
</div>
<div class="btn" @click="save" title="保存">
<i class="iconfont icon-baocun"></i>
</div>
</div>
<div class="btn-group">
<el-input size="mini" v-model="workflowName" placeholder="请输入流图名称..."></el-input>
</div>
</div>
<div class="info">
<div class="title">
<span>{{infoTitle}}属性</span>
</div>
<div class="content">
<el-checkbox v-if="isBlank === true" v-model="checked">网格对齐</el-checkbox>
<el-form v-else label-position="left" label-width="60px">
<el-form-item v-if="isNode !== true" label="动作">
<el-select v-model="action" size="mini" filterable placeholder="绑定动作" value="">
<el-option
v-for="item in actionList"
:key="item.id"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item> <!-- 线-->
<el-form-item v-if="isNode === true" ref="inputFocus" label="名称">
<el-input size="mini" v-model="name"></el-input>
</el-form-item>
<el-form-item v-if="isNode === true" label="功能">
<el-select v-model="func" size="mini" filterable placeholder="绑定功能" value="">
<el-option
v-for="item in funcList"
:key="item.id"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item v-if="isNode === true" label="账号">
<el-select v-model="account" size="mini" filterable multiple
collapse-tags placeholder="绑定账号" value="">
<el-option
v-for="item in accountList"
:key="item.id"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item v-if="isNode === true" label="流图">
<el-select v-model="workflow" size="mini" filterable clearable placeholder="绑定流图" value="">
<el-option
v-for="item in workflowList"
:key="item.id"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item v-if="isNode === true" label="类型">
<el-select v-model="nodeType" size="mini" filterable placeholder="请选择类型" value="">
<el-option
v-for="item in nodeTypeList"
:key="item.id"
:label="item.label"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="颜色">
<el-color-picker v-model="color"></el-color-picker>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
<script>

export default {
name: "index",
components: {},
mounted() {
this.initG6();
},
props: {
actionList: {
type: Array, default: []
},
funcList: {
type: Array, default: []
},
accountList: {
type: Array, default: []
},
workflowList: {
type: Array, default: []
},
nodeTypeList: {
type: Array, default: () => {
return [
{id: 0, label: '普通节点'},
{id: 1, label: '入口节点'},
{id: 2, label: '出口节点'}
]
}
}
},
data() {
return {
action: '',
name: '',
func: '',
account: '',
workflow: '',
nodeType: 0,
color: '',

net: '',
Util: '',
workflowName: '',
activation: '', //当前激活的节点
isNode: false, //当前是节点
isBlank: true, //当前是空白区
checked: true, //网格对齐
infoTitle: '画布',//属性标题
oldColor: '', //获取节点本身颜色
type: '', //有值为编辑状态
}
},
methods: {
initG6() {
let self = this;
self.Util = G6.Util;
let grid;
if (self.checked) {
grid = {
forceAlign: true, // 是否支持网格对齐
cell: 25, // 网格大小
};
} else {
grid = null;
}
self.net = new G6.Net({
id: 'flowChart', // 容器ID
mode: 'edit',
grid: grid,
/*width: 500, // 画布宽*/
height: 800 // 画布高
});
/*self.net.tooltip({
title: '信息', // @type {String} 标题
split: ':', // @type {String} 分割符号
dx: 0, // @type {Number} 水平偏移
dy: 0 // @type {Number} 竖直偏移
});*/

/**
*点击空白处
*/
self.net.on('click', (ev) => {
if (!self.Util.isNull(ev.item)) {
self.isBlank = false
} else {
self.isBlank = true;
self.infoTitle = '画布'
}
});
/**
*点击节点
*/
self.net.on('itemclick', function (ev) {
self.isNode = self.Util.isNode(ev.item); //是否为Node
self.activation = ev.item;
if (self.isNode) {
/* 激活节点后节点名称input聚焦*/
self.$nextTick(()=>{
self.$refs.inputFocus.$el.querySelector('input').focus();
});
self.infoTitle = '节点';
self.name = ev.item.get('model').label;
self.func = ev.item.get('model').func;
self.account = ev.item.get('model').account || [];
self.workflow = ev.item.get('model').workflow;
self.nodeType = ev.item.get('model').nodeType;
} else {
self.infoTitle = '边';
self.action = ev.item.get('model').action;
}
self.color = self.oldColor;
});
/**
* 鼠标移入移出事件改变颜色
*/
self.net.on('itemmouseenter', ev => {
const item = ev.item;
self.oldColor = item.get('model').color; //获取节点颜色
self.net.update(item, {
color: '#108EE9',
});
self.net.refresh();
});
self.net.on('itemmouseleave', ev => {
const item = ev.item;
self.net.update(item, {
color: self.oldColor
});
self.net.refresh();
});
/**
* 提示信息
*/
/* self.net.node().tooltip(['label', 'func', 'role', 'color']);
self.net.edge().tooltip(['label', 'color']);*/
/**
* 渲染
*/
/*self.net.source(self.nodes, self.edges);*/ //加载资源数据
self.net.render();
},
addCircle() {
this.net.beginAdd('node', {
shape: 'circle',
nodeType: 0
})
},//添加起始节点
addRect() {
this.net.beginAdd('node', {
shape: 'rect',
nodeType: 0
})
},//添加常规节点
addRhombus() {
this.net.beginAdd('node', {
shape: 'rhombus',
nodeType: 0
})
}, //添加条件节点
addLine() {
this.net.beginAdd('edge', {
shape: 'line'
});
}, //添加直线
addSmooth() {
this.net.beginAdd('edge', {
shape: 'smooth'
})
}, //添加曲线
addArrowSmooth() {
this.net.beginAdd('edge', {
shape: 'smoothArrow'
})
}, //添加箭头曲线
addArrowLine() {
this.net.beginAdd('edge', {
shape: 'arrow'
});
}, //添加箭头直线
addPolyLine() {
this.net.beginAdd('edge', {
shape: 'polyLineFlow'
});
}, //添加折线
changeMode(mode) {
this.net.changeMode(mode)
}, //拖拽与编辑模式的切换
del() {
this.net.del()
},//删除
save() {
/* 验证流图名称*/
if (this.workflowName !== '') {
let data = this.net.save();
if (data.source.nodes.length === 0) {
this.$message({type: 'error', message: '流图内容不能为空'});
return false
} else if (data.source.nodes.length - data.source.edges.length > 1) {
this.$message({type: 'error', message: '流图中连线异常'});
return false
}
/* 验证节点名称*/
for (let item of data.source.nodes) {
if (item.label === '' || item.label === null || item.label === undefined) {
this.$message({type: 'error', message: '节点名称不能为空'});
return false
}
}
data.source['name'] = this.workflowName;
/*let json = JSON.stringify(data, null, 2);*/
this.$emit('saveData', data.source, this.type);
} else {
this.$message({type: 'error', message: '流图名称不能为空'})
}
/*console.log(saveData, json);*/
},//保存
update() {
if (this.activation.get('type') === 'node') {
this.net.update(this.activation, {
label: this.name,
func: this.func,
account: this.account,
workflow: this.workflow,
nodeType: this.nodeType,
color: this.color
});
} else {
/* 根据ID取出label*/
let label = this.actionList.map(item => {
if (item.id === this.action) {
return item.label
}
}).join('');
this.net.update(this.activation, {
label: label,
color: this.color,
action: this.action
});
}
}, //更新节点
clearView() {
this.type = '';
this.workflowName = '';
this.net.changeData()
}, //清空视图
source(nodes, edges, name, type) {
this.type = type;
this.workflowName = name;
this.net.changeData(nodes, edges)
}, //更新数据
},
watch: {
/**
* 监听输入框
*/
action: function () {
this.update()
},
name: function () {
this.update()
},
func: function () {
this.update()
},
account: function () {
this.update()
},
workflow: function () {
this.update()
},
nodeType: function () {
this.update()
},
color: function () {
this.update()
},
/**
* 网格切换
*/
checked: function () {
let _saveData = this.net.save();
this.net.destroy(); //销毁画布
this.initG6();
this.net.read(_saveData);
this.net.render()
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<style rel="stylesheet/scss" lang="scss" scoped>
#flowChart {
border: 1px solid #ebeef5;
position: relative;
overflow: hidden;
}

.operating {
position: absolute;
z-index: 99;
background-color: #ffffff;
padding: 20px 10px;
box-shadow: 1px 1px 4px 0 #0a0a0a2e;
}

.info {
position: absolute;
right: 0;
z-index: 99;
box-shadow: 1px 1px 4px 0 #0a0a0a2e;
.title {
height: 40px;
padding-left: 10px;
border-top: 1px solid #DCE3E8;
border-bottom: 1px solid #DCE3E8;
border-left: 1px solid #DCE3E8;
background: rgb(235, 238, 242);
line-height: 40px;
span {
font-size: 14px;
}
}
.content {
background: rgba(247, 249, 251, 0.45);
width: 200px;
height: 800px;
border-left: 1px solid #E6E9ED;
padding: 10px;
}
}

.btn-group {
border-right: 1px solid #efefef;
display: inline-block;
padding-left: 10px;
padding-right: 14px;
&:last-of-type {
border-right: 0;
}
.btn {
display: inline-block;
margin: 2px;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
border: 1px solid rgba(233, 233, 233, 0);
i {
font-size: 20px;
}
&:hover {
border: 1px solid #E9E9E9;
color: #767A85;
border-radius: 2px;
background: #FAFAFE;
}
}
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>

流图属性

参数 说明 类型 可选值 默认值
actionList 动作数据 Array —— []
funcList 功能数据 Array —— []
accountList 账号数据 Array —— []
workflowList 流图数据 Array —— []
nodeTypeList 节点类型数据 Array —— [{id: 0, label: '普通节点'},{id: 1, label: '入口节点'},{id: 2, label: '出口节点'}]

所有属性接收的数据格式需要与nodeTypeList的默认值相同

流图事件

事件名 说明 参数
saveData 当用户手动点击保存触发事件 source,type

参数type可为空,在此项目中主要用来区分新建编辑

流图方法

方法名 说明 参数
clearView 清空当前视图 ——
source 渲染数据 nodes, edges, name, type

参数type与事件中相同,参数name的作用是用来取流图名

参考文档

使用 G6关系图类库 开发流程图工具

旧版本G6 1.x API 文档

新版本G6 2.x API 文档

文章作者: Gorkys
文章链接: http://tingtas.com/posts/9379e170/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Tingtas
打赏
  • 微信
  • 支付宝

评论