写在前面 【注意这个是1.X版本Vue2的】
2.X+Vue3的版本见下面
【2.X+Vue3版本】关于AntV-X6,2.X版本在vue3里面的应用【AntV 2.X,Vue3的版本】。 - (jianshu.com)
因为工作需要,写了一个拖拽流程图生成组件。用的是AntV-X6。以前用的jsplumb,后面发现了这个蛮好用的,记录一下。
组件功能
- 可以拖拽添加节点,修改编辑节点样式(默认四个链接桩)
- 可以添加连线,修改连线的样式。给线条添加动态(蚂蚁线)。可以手动调整线条(添加编辑工具)
- 可以添加图片节点。自定义设置图片。
- 导出与反显
项目地址
antv-x6: AntV-X6 图编辑引擎在vue2里面的使用。 (gitee.com)
npm install
npm run serve
项目界面展示
项目过程
1,引入
npm install @antv/[email protected] --save
2,建立一个初始化默认设置的antvSetting.js,方便调用。
// 画布基本设置(这些例子上面都有)
export const configSetting = (Shape) => {
return {
grid: true,
autoResize: true,
translating: { restrict: true },
mousewheel: {
enabled: true,
zoomAtMousePosition: true,
modifiers: 'ctrl',
minScale: 0.5,
maxScale: 3,
},
connecting: {
router: {
name: 'manhattan',
args: {
padding: 1,
},
},
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
anchor: 'center',
connectionPoint: 'anchor',
allowBlank: false,
snap: {
radius: 20,
},
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: '#A2B1C3',
strokeWidth: 2,
targetMarker: {
name: 'block',
width: 12,
height: 8
},
},
},
zIndex: 0,
})
},
validateConnection({ targetMagnet }) {
return !!targetMagnet
},
},
onToolItemCreated({ tool }) {
const handle = tool
const options = handle.options
if (options && options.index % 2 === 1) {
tool.setAttrs({ fill: 'red' })
}
},
highlighting: {
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#5F95FF',
stroke: '#5F95FF',
},
},
},
},
resizing: true,
rotating: true,
selecting: {
enabled: true,
rubberband: true,
showNodeSelectionBox: true,
},
snapline: true,
keyboard: true,
clipboard: true
}
}
/**
* 节点预设类型
* 0椭圆形: defaultOval,
* 1方形: defaultSquare,
* 2圆角矩形: defaultYSquare,
* 3菱形: defaultRhombus,
* 4平行四边形: defaultRhomboid,
* 5圆形: defaultCircle,
* 6图片: otherImage
* 到时候通过传入的type===通过匹配 data里面设置的type获取到相应的节点设置内容
* 编辑的时候也可以通过节点里面的data.type 获取到到底是什么节点进行响应设设置
*/
export const configNodeShape = (type) => {
const nodeShapeList = [{
label: '椭圆形',
/**
*
* 加入data里面的标识type是为了方便编辑的时候找到相对应的类型进行不同的编辑处理
* 另外获取初始对应的设置
*/
data: {
type: 'defaultOval'
},
shape: 'rect',
width: 100,
height: 50,
attrs: {
body: {
rx: 20,
ry: 26,
fill: '#fff',
stroke: '#333'
},
label: {
text: '椭圆形',
fontSize: 16,
fill: '#333'
}
}
},
{
label: '方形',
data: {
type: 'defaultSquare',
},
shape: 'rect',
width: 100,
height: 50,
attrs: {
label: {
text: '方形',
fontSize: 16,
fill: '#333'
},
body: {
fill: '#fff',
stroke: '#333'
}
},
},
{
label: '圆角矩形',
data: {
type: 'defaultYSquare'
},
shape: 'rect',
width: 100,
height: 50,
attrs: {
body: {
rx: 6,
ry: 6,
fill: '#fff',
stroke: '#333'
},
label: {
text: '圆角矩形',
fontSize: 16,
fill: '#333'
}
},
},
{
label: '菱形',
data: {
type: 'defaultRhombus'
},
shape: 'polygon',
width: 120,
height: 50,
attrs: {
body: {
refPoints: '0,10 10,0 20,10 10,20',
fill: '#fff',
stroke: '#333'
},
label: {
text: '菱形',
fontSize: 16,
fill: '#333'
}
},
},
{
label: '平行四边形',
data: {
type: 'defaultRhomboid'
},
shape: 'polygon',
width: 120,
height: 50,
attrs: {
body: {
refPoints: '10,0 40,0 30,20 0,20',
fill: '#fff',
stroke: '#333'
},
label: {
text: '平行四边形',
fontSize: 16,
fill: '#333'
}
}
},
{
label: '圆形',
data: {
type: 'defaultCircle'
},
shape: 'circle',
width: 80,
height: 80,
attrs: {
label: {
text: '圆形',
fontSize: 16,
fill: '#333'
},
body: {
fill: '#fff',
stroke: '#333'
}
}
},
{
label: "图片",
data: {
type: 'otherImage'
},
shape: 'rect',
width: 80,
height: 80,
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'image',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
body: {
stroke: '#5F95FF',
fill: '#5F95FF',
},
image: {
width: 80,
height: 80,
refX: 0,
refY: 0,
xlinkHref: 'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
},
label: {
fontSize: 14,
fill: '#fff',
text: '图片'
},
},
}
]
if(type) {
const obj = nodeShapeList.find(item => {return item.data.type === type})
return obj || nodeShapeList
}
return nodeShapeList
}
/**
* 节点连接桩设置
* 这里设置了上下左右四个
* 并且给style设置了 visibility: 'hidden',默认是隐藏的。
* 如果设置了隐藏记得在画布里面设置鼠标经过显示。
* graph.on('node:mouseenter', () => {
const container = document.getElementById('wrapper')
const ports = container.querySelectorAll('.x6-port-body')
for (let i = 0, len = ports.length; i < len; i = i + 1) {
ports[i].style.visibility = val ? 'visible' : 'hidden'
}
})
* 如果需要常显去掉每个链接桩里面的
style: {
visibility: 'hidden',
},
* 就可以了
*/
export const configNodePorts = () => {
return {
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',
},
]
}
}
// 连线 label 设置
export const configEdgeLabel = (labelText, fontColor, fill, stroke) => {
if(!labelText) return { attrs: { labelText: { text: '' }, labelBody: { fill: '', stroke: '' } } }
return {
markup: [
{
tagName: 'rect',
selector: 'labelBody',
},
{
tagName: 'text',
selector: 'labelText',
},
],
attrs: {
labelText: {
text: labelText || '',
fill: fontColor || '#333',
textAnchor: 'middle',
textVerticalAnchor: 'middle',
},
labelBody: {
ref: 'labelText',
refX: -8,
refY: -5,
refWidth: '100%',
refHeight: '100%',
refWidth2: 16,
refHeight2: 10,
stroke: stroke || '#555',
fill: fill || '#fff',
strokeWidth: 2,
rx: 5,
ry: 5,
},
}
}
}
// 键盘事件
export const graphBindKey = (graph) => {
graph.bindKey(['meta+c', 'ctrl+c'], () => {
const cells = graph.getSelectedCells()
if (cells.length) {
graph.copy(cells)
}
return false
})
graph.bindKey(['meta+x', 'ctrl+x'], () => {
const cells = graph.getSelectedCells()
if (cells.length) {
graph.cut(cells)
}
return false
})
graph.bindKey(['meta+v', 'ctrl+v'], () => {
if (!graph.isClipboardEmpty()) {
const cells = graph.paste({ offset: 32 })
graph.cleanSelection()
graph.select(cells)
}
return false
})
//undo redo
graph.bindKey(['meta+z', 'ctrl+z'], () => {
if (graph.history.canUndo()) {
graph.history.undo()
}
return false
})
graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
if (graph.history.canRedo()) {
graph.history.redo()
}
return false
})
// select all
graph.bindKey(['meta+a', 'ctrl+a'], () => {
const nodes = graph.getNodes()
if (nodes) {
graph.select(nodes)
}
})
//delete
/*
graph.bindKey('delete', () => {
const cells = graph.getSelectedCells()
if (cells.length) {
graph.removeCells(cells)
}
})
*/
// zoom
graph.bindKey(['ctrl+1', 'meta+1'], () => {
const zoom = graph.zoom()
if (zoom < 1.5) {
graph.zoom(0.1)
}
})
graph.bindKey(['ctrl+2', 'meta+2'], () => {
const zoom = graph.zoom()
if (zoom > 0.5) {
graph.zoom(-0.1)
}
})
return graph
}
3,页面上的使用
链接桩常显
{{editTitle}}
预览
{{form.labelText}}
宽
高
字体颜色:
背景颜色:
描边颜色:
删除此{{editTitle === '编辑节点' ? '节点' : '连线'}}
展示页面(反显页面)