赞
踩
hello,大家好,我是前端小老弟儿,最近老弟儿接到这样一个需求,实现一个流程图,可以连线,右键操作,以及删除连线等,如下所示得流程图。使用得插件是: jsPlumb;所以就简单的介绍一下jsPlumb基本使用
jsPlumb是一个强大得JavaScript连线库,提供html元素的拖放、连线等功能,可绘制不同类型、样式的连线,适用于开发web页面的图表、建模工具等。同时也支持vue2.0 ,react和Angular 。
该框架适用于必须回值图表得web应用程序,比如流程图设计,组织架构图设计等。
npm install jsplumb
import { jsPlumb } from "jsplumb";
jsplumb的基本方法:
jsPlumb.ready(function() {
...
// your jsPlumb related init code goes here
...
});
jsPlumb.batch(function() {
// import here
for (var i = 0, j = connections.length; i < j; i++) {
jsPlumb.connect(connections[i]);
}
});
// 连线事件
jsPlumb.bind("connection", (info, event) => {
this.bindLinkEvent(info.connection);
this.data.links.push([info.sourceId, info.targetId]);
});
this.flowInst = jsPlumb.getInstance();
同时,getInstance方法接受一个参数,可以更改实例的配置
this.flowInst = jsPlumb.getInstance({
Connector : [ "Bezier", { curviness: 150 } ],
Anchors : [ "TopCenter", "BottomCenter" ],
...
});
this.flowInst.connect({
source: 'item_left', // 源
target: 'item_right', // 目标
endpoint: 'Dot' // 线的类型
})
// id: 增加端点得id
// common:端点的配置信息
this.flowInst.addEndpoint(id,{common})
this.flowInst.addEndpoint('item_left', {
anchors: ['Right']
})
let common = {
containment?: string
start?: (params:DragEventCallbackOptions) => void
drag?: (params:DragEventCallbackOptions) => void
stop?: (params:DragEventCallbackOptions) => void
cursor?: string
zIndex?: number
}
this.flowInst.draggable(node._id, {common}); //
此方法有两个参数:
第一个参数为可拖拽节点的id,
第二个参数为函数对象,有6个参数,start,drag,stop 三个函数中可以获取元素节点位置等。
基本配置
{ Anchor: "BottomCenter", //锚点位置,如left,top,bottom等;对任何没有声明描点的Endpoint设置锚点,用于source及诶单或target节点 Anchors: [ null, null ], //连线的source和target Anchor ConnectionsDetachable: true, //连线是否可用鼠标分离 ConnectionOverlays: [ //连线的叠加组件,如箭头、标签 ["Arrow", { //箭头参数设置 location: 1, visible:true, width:11, length:11, id:"ARROW", events:{ click:function() { } } } ], [ "Label", { //标签参数设置 location: 0.1, id: "label", cssClass: "aLabel", //hover时label的样式名 events:{ tap:function() { } }, visible: true }] ], Connector: "Bezier", //连线的类型,流程图(Flowchart)、贝塞尔曲线等 Container: null, //父级元素id;假如页面元素所在上层不同,最外层父级一定要设置 DoNotThrowErrors: false, //如果请求不存在的Anchor、Endpoint或Connector,是否抛异常 DragOptions: {cursor: 'pointer', zIndex: 2000}, //通过jsPlumb.draggable拖拽元素时的默认参数设置 DropOptions: { }, //target Endpoint放置时的默认参数设置 Endpoint: "Dot", //端点(锚点)的样式声明 Endpoints: [ null, null ], //用jsPlumb.connect创建连接时,source端点和target端点的样式设置 EndpointOverlays: [ ], //端点的叠加物 EndpointStyle: { fill : "#456" }, //端点的默认样式 EndpointStyles: [ null, null ], //连线的source和target端点的样式 EndpointHoverStyle: { fill: "#ec9f2e" }, //端点hover时的样式 EndpointHoverStyles: [ null, null ], //连线的source和target端点hover时的样式 HoverPaintStyle: {stroke: "#ec9f2e" }, //连线hover时的样式 LabelStyle: { color: "black" }, //标签的默认样式,用css写法。 LogEnabled: false, //是否开启jsPlumb内部日志 Overlays: [ ], //连线和端点的叠加物 MaxConnections: 1, //端点支持的最大连接数 PaintStyle: { lineWidth : 8, stroke : "#456" }, //连线样式 ReattachConnections: false, //是否重新连接使用鼠标分离的线? RenderMode: "svg", //默认渲染模式 Scope: "jsPlumb_DefaultScope" //范围,具有相同scope的点才可连接? }
具体实现方案
data示例
data: { links: [ [ "46fda179-5947-4fe7-90aa-588a61b85694", "ecebf461-4532-4840-8672-017857686335", ], [ "ecebf461-4532-4840-8672-017857686335", "245057e5-a747-49e0-9e66-9fb537d351a3", ], [ "245057e5-a747-49e0-9e66-9fb537d351a3", "690c46fb-c8d9-4c7f-af35-89c30deb9b98", ], ], nodes: [ { _id: "46fda179-5947-4fe7-90aa-588a61b85694", name: "START", description: "START", descStr: "", class: "", pos: [313.33333333333337, 160], endpoints: [{ isSource: true, maxConnections: -1 }], nodeType: "START", contextMenus: [ { icon: "el-icon-delete", text: "删除", event: "delete" }, ], }, { _id: "ecebf461-4532-4840-8672-017857686335", name: "ONE", description: "ONE", descStr: "ONE", class: "", pos: [325, 314], endpoints: [{}, {}], nodeType: "ONE", contextMenus: [ { icon: "el-icon-delete", text: "删除", event: "delete" }, { icon: "el-icon-edit", text: "编辑", event: "edit" }, { icon: "el-icon-edit", text: "修改描述", event: "desc" }, { icon: "fa fa-clone", text: "克隆", event: "copy" }, ], }, { _id: "245057e5-a747-49e0-9e66-9fb537d351a3", name: "TWO", description: "TWO", descStr: "TWO", class: "", pos: [328.33333333333337, 456.6666666666667], endpoints: [{}, {}], nodeType: "TWO", contextMenus: [ { icon: "el-icon-delete", text: "删除", event: "delete" }, { icon: "el-icon-edit", text: "编辑", event: "edit" }, { icon: "el-icon-edit", text: "修改描述", event: "desc" }, { icon: "fa fa-clone", text: "克隆", event: "copy" }, ], }, { _id: "690c46fb-c8d9-4c7f-af35-89c30deb9b98", name: "END", description: "END", descStr: "", class: "", pos: [355, 650], endpoints: [{ isSource: false, isTarget: true, maxConnections: 1 }], nodeType: "END", contextMenus: [ { icon: "el-icon-delete", text: "删除", event: "delete" }, ], }, ], props: { position: [-34, -49], }, },
mounted中:
mounted() {
this.container = this.$el.querySelector(".workflow-container");
jsPlumb.ready(() => {
this.createFlow();
});
}
methods:
createFlow() { console.log('绘制工作流') this.flowInst = jsPlumb.getInstance(this.defaultOption); this.flowInst.setContainer(this.container); // suspend drawing and initialise. this.flowInst.batch(() => { this.drawNodes(); for (const link of this.data.links) { const conn = this.flowInst.connect({ uuids: [link[0] + "Bottom", link[1] + "Top"], }); //绑定右键事件 this.bindLinkEvent(conn); } }); //建立连接事件,更新到data 连线事件 this.flowInst.bind("connection", (info, event) => { this.bindLinkEvent(info.connection); this.data.links.push([info.sourceId, info.targetId]); }); //删除连接事件,更新到data this.flowInst.bind("connectionDetached", (info, event) => { for (let [k, o] of this.data.links.entries()) { if (o[0] === info.sourceId && o[1] === info.targetId) { this.data.links.splice(k, 1); break; } } }); // //更改连接事件,更新到data。更改连接时也会触发connection事件 this.flowInst.bind("connectionMoved", (info, event) => { for (let [k, o] of this.data.links.entries()) { if ( o[0] === info.originalSourceId && o[1] === info.originalTargetId ) { this.data.links.splice(k, 1); break; } } }); this.flowInst.setZoom(this.zoom); },
绘制元素
drawNodes() { let vm = this; for (let node of this.data.nodes) { let str = `<div id="${node._id}" class="point node-canselect"> <div class="title">${node.name}</div> <div class="desc">${node.nodeType}</div>`; //任务运维中,针对可优化的节点,展示'可优化'的logo str = str + "</div>"; let dom = $(`${str}`); // node.pos[0]node.pos[1] dom.css({ left: node.pos[0] + "px", top: node.pos[1] + "px", }); //绑定右键事件 if (!_.isEmpty(node.contextMenus)) { dom.contextmenu((event) => { this.nodeMenu.pos = this.getContextMenuPos(event); this.nodeMenu.obj = node; this.nodeMenu.visible = true; this.connMenu.visible = false; this.rightButtonVisible = false; return false; }); } //绑定双击事件 dom.dblclick(() => { this.$emit("nodeDblclickEvent", node); }); this.container.append(dom.get(0)); if (_.isArray(node.endpoints)) { for (let [index, endpoint] of node.endpoints.entries()) { let p = { connectorStyle: { strokeWidth: 4, stroke: "#61B7CF", joinstyle: "round", }, dragAllowedWhenFull: false, isTarget: index === 0, isSource: index === 1, ...endpoint, }; p.anchor = p.isSource ? "Bottom" : p.isTarget ? "Top" : ""; p.uuid = node._id + p.anchor; this.flowInst.addEndpoint(node._id, p); } } if (!this.option.readonly) { this.flowInst.draggable(node._id, { stop: function(event) { vm.nodeDrop(event.el, event.pos); }, }); } } },
最后实现的功能和样式如下:
实现删除,连线功能
可以任意拖拽
参考文献:
jsplumb 中文基础教程: https://wdd.js.org/jsplumb-chinese-tutorial/#/?id=_1-jsplumb-%e4%b8%ad%e6%96%87%e5%9f%ba%e7%a1%80%e6%95%99%e7%a8%8b
jsPlumb Toolkit Documentation: https://docs.jsplumbtoolkit.com/toolkit/current/index.html
JsPlumb.js使用总结: https://nanastef.github.io/jsPlumb/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。