前言:
jsplumb 有2个版本一个Toolkit Edition(付费版),另外一个就是Community Edition(社区版本)。Toolkit Edition版本功能集成的比较丰富,社区版本的就差好多,很多功能都没有,需要我们自己去添加,当然了自己添加多多少少有些麻烦,而且也不完善。但是我们还是用Community Edition(社区版本),毕竟不收费,没办法,下边所说的版本默认都是以社区版。
最近公司项目有用到这个流程图,所以也就看到了这个框架,这个框架是英文版本的,地址:https://jsplumbtoolkit.com/community/doc/home.html(可以用浏览器翻译了看)。他的缺陷就是文档不全,api感觉也有点乱,实例交代的也不清楚,github地址是:https://github.com/jsplumb/jsplumb (里面有demo,自己可以下载运行,多动手试试)。如果只是简单的画个图,这个框架是没有什么问题的,demo里也有,但是如果要实现高级的动能呢鞥,还是得多多尝试。此文也是记录一下我自己用到的一些功能,很多我还没用到,用到了在慢慢补充。

上图也就是我这次用到的jsplumb实现的功能,连接线能够拖拽生成,也可以删除,编辑label。
1、数据结构
{
"nodes": [{ //节点集合
"icon": "el-icon-loading",
"id": "start",
"nodeStyle": {
"top": 100,
"left": 200
},
"text": "开始",
"type": "circle"
}, {
"icon": "el-icon-upload",
"id": "end",
"nodeStyle": {
"top": 300,
"left": 400
},
"text": "结束",
"type": "circle"
}] ,
"connections": [{ //连接线集合
"sourceId": "start",
"targetId": "end",
"label":"编辑"
}]
}
jsplumb实例里面的数据结构就是这样的,这里我们沿用他的数据结构,你也可以自己定义自己想的数据结构,但是对比起来这个结构是最清晰也最方便的。
2、初始化
jsplumb在DOM渲染完毕之后才会执行,所以需要为jsplumb的执行代码绑定一个ready事件:
jsPlumb.ready(function() {
// your jsPlumb related init code goes here
});
jsplumb默认是注册在浏览器窗口的,将整个页面提供给我们作为一个实例,但我们也可以使用getInstance方法建立页面中一个独立的实例:
var _instance = jsPlumb.getInstance();
3、功能实现(允许哪些元素拖拽,允许拆卸连接)
let instance = jsPlumb.getInstance({
PaintStyle:{
strokeWidth:2,
stroke:"#567567",
}
})
//拖拽功能
var els = document.querySelectorAll(".box");//.box是允许拖拽的元素class类名
instance.draggable(els,{
containment:true,
filter: ".ep",//除去不能拖拽的,这里是个class类名
});
//不允许拆卸连接,不设置的话默认是可以的
instance.importDefaults({
ConnectionsDetachable:false
});
4、连线监听事件(拖动connection 事件)
// 监听拖动connection 事件,判断是否有重复链接
instance.bind("beforeDrop", function(info) {
// info.connection.getOverlay("label").setLabel(info.connection.id);
// 判断是否已有该连接
let isSame = true;
//下边的forEach循环就是处理数据结构里的connections不能自己跟自己连线。当然你也可以处理其他
_this.chartData.connections.forEach(item => {
if ((item.targetId === info.targetId && item.sourceId === info.sourceId) || (item.targetId === info.sourceId && item.sourceId === info.targetId)) {
isSame = false;
}
});
if (isSame) {
//允许连线后处理的情况
} else {
alert("不允许重复连接!");
}
return isSame;//这里返回了ture就会自定进行连线。
});
5、上图实现的完整代码
下边代码就是实现上图的,需要指出的是运用了vue,但是里面掺杂了jquery,和jquery-ui,其实不想用这2个的,但是项目紧,之前项目也用到了,所以就延续了。还有就是上面代码是我自己的测试代码,写的可能有些杂乱,就是测试一个一个功能而写,写的有点乱。
还有一个想说的就是之前想实现,缩放,引入了panzoom.js,流程图也实现了滚动鼠标放大放小,但是有个问题就是滚动鼠标放大放小后如果拖动单个元素或者连线,你就会发现鼠标点对不齐了,这点还没有解决,如果有好的方案,可以告知我下。Toolkit Edition(付费版)的这些功能都有,就不会出现这样的问题。
<template>
<div id="test6" style="height:100%;position:relative">
<section id="focal" style="position:relative;overflow:hidden;610px;height:610px;background:#fff;border:1px solid red">
<div class="parent" id="parent" style="height:100%;">
<div class="panzoom" id="panzoom" style="border:1px solid blue;6000px;height:6000px; transform:translate(-50%, -50%);position:absolute;">
<div class="box" :id="item.id" :style="{'top':item.nodeStyle.top+'px','left':item.nodeStyle.left+'px'}" v-for="item in chartData.nodes" :key="item.id">
<i :class="item.icon" class="oldIcon" :title="item.text"></i>
<i class="el-icon-circle-close" style="display:none" :title="item.text" :id="item.id"></i>
<div class="ep"></div>
</div>
</div>
</div>
</section>
<div class="source">
<ul>
<li v-for="(item,index) in list" :id="item.id" :key="index" class="sourceLi" :disabled="true" :data-icon="item.icon" :data-text="item.text" :data-type="item.type">{{item.text}}</li>
</ul>
</div>
<el-dialog
title="修改label名称"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<el-input v-model="labelName" placeholder="请输入"></el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="changeNote">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import ChartNode from "@/components/ChartNode";
export default {
name: "test6",
data() {
return {
dialogVisible:false,
labelName:"",
curSourceId:'',
curTargetId:'',
addLabelText:'',//拖拽后生成的连线label文字
jsp:null,
myscale:1,
curScreen:[],//当前屏幕宽高
chartData: {
nodes: [],
connections: [],//{ "targetId": "box2", "sourceId": "box1" }
props: {},
screen:[610,610]//提交屏幕宽高
},
list: [
{
icon: "el-icon-goods",
text: "伴随车牌",
type: "circle",
id:'li1'
},
{
icon: "el-icon-bell",
text: "常住人口筛选",
type: "diamond",
id:"li2"
},
{
icon: "el-icon-date",
text: "伴随imsi",
type: "circle",
id:"li3"
}
]
};
},
mounted() {
let _this = this
jsPlumb.ready(function() {
var $section = $('#focal');
var $panzoom = $section.find('.panzoom').panzoom({
minScale: 0.3,
maxScale:2,
eventNamespace: ".panzoom",
$zoomRange: $(".jtk-endpoint"),
$set: $section.find('.jtk-overlay'),
eventsListenerElement: document.querySelector('.box')
});
$(document).on('mouseover','.box,.jtk-draggable,.jtk-overlay,.ep',function(