小工具知识网

当前位置: 首页 > 行业知识 > vue2中使用jsplumb完成流程图

行业知识

vue2中使用jsplumb完成流程图

2026-10-27 09:13:34 来源:互联网转载

前言

之前的几个demo都是在vue3中写的,虽然可以直接拿去复用。

但是根据有些看客反馈,想用一个vue2版本的,毕竟很多人开发功能的时间都不是特别富裕。大多时候还是用现成的demo更好一些。

这里我就写一个简易版本的demo,可以实现绘制,并且删除连接线和节点等功能,篇幅也不大,适合应急的朋友,哈哈

代码

1.剥离公共的配置  config.js

export const readyConfig = {    Container: 'plumbBox',    anchor: ['Bottom', 'Top', 'Left', 'Right'],    connector: 'Straight',    endpoint: 'Blank',    PaintStyle: { stroke: '#8b8c8d', strokeWidth: 2, outlineStroke: 'transparent', outlineWidth: 10 },    Overlays: [['Arrow', { width: 10, length: 5, location: 0.5, direction: 1 }]],    endpointStyle: { fill: 'lightgray', outlineStroke: 'darkgray', outlineWidth: 2 }}export const sourceConfig = {    filter: '.pointNode',    filterExclude: false,    allowLoopback: true,    maxConnections: -1,    Container: 'plumbBox',    anchor: ['Bottom', 'Top', 'Left', 'Right'],    connector: 'Straight',    endpoint: 'Blank',    PaintStyle: { stroke: '#8b8c8d', strokeWidth: 2, outlineStroke: 'transparent', outlineWidth: 10 },    Overlays: [['Arrow', { width: 10, length: 5, location: 0.5, direction: 1 }]],    endpointStyle: { fill: 'lightgray', outlineStroke: 'darkgray', outlineWidth: 2 }}export const targetConfig = {    filter: '.pointNode',    filterExclude: false,    allowLoopback: true,    maxConnections: -1,    Container: 'plumbBox',    anchor: ['Bottom', 'Top', 'Left', 'Right'],    connector: 'Straight',    endpoint: 'Blank',    PaintStyle: { stroke: '#8b8c8d', strokeWidth: 2, outlineStroke: 'transparent', outlineWidth: 10 },    Overlays: [['Arrow', { width: 10, length: 5, location: 0.5, direction: 1 }]],    endpointStyle: { fill: 'lightgray', outlineStroke: 'darkgray', outlineWidth: 2 }}

其实这些配置都大差不差,之所以分别罗列,是让大家搞明白,画布层级的配置,起点节点的配置以及终点节点的配置都是可以单独去配置,具体配置项可以看我之前的文档。

2.使用简易数据 data.js

export const leftMenuList = [    {        name: "节点1",        id: "app1",    },    {        name: "节点2",        id: "app2",    },    {        name: "节点3",        id: "app3",    },    {        name: "节点4",        id: "app4",    },]

 这里的数据其实是左侧的数据

3.组件部分

            

左侧菜单

downNode(el, item)" > {{ item.name }}

操作提示


左侧拖拽至右侧画布

右键节点是删除节点,左键线条是删除线条

{{ item.name }} import { jsPlumb } from "jsplumb";import { readyConfig, sourceConfig, targetConfig } from "./config";import { leftMenuList } from "./data";import { cloneDeep } from "lodash";import draggable from "vuedraggable";export default { name: "flowChart", components: { draggable }, data() { return { //节点列表 leftMenuList: leftMenuList, dataInfo: [], //plumb实例 PlumbInit: null, //关系列表 renderList: [], //画布信息(大小和位置) canvasInfo: null, //鼠标位置精准判定 nodePositionDiff: null, //选中的左侧列表节点 ativeNodeItem: null, //线条的信息 deleteLineInfo: null, }; }, mounted() { this.jsPlumbInit(); this.readyPlumbDataFun("once"); }, methods: { //初始化 jsPlumbInit() { this.PlumbInit = jsPlumb.getInstance(); this.PlumbInit.importDefaults(readyConfig); }, //组织渲染用的数据 readyPlumbDataFun(flag) { this.renderList = []; this.PlumbInit.deleteEveryConnection(); this.PlumbInit.deleteEveryEndpoint(); this.$nextTick(() => { this.dataInfo = cloneDeep(this.dataInfo); //根据数据创建关联信息(连线关系) this.dataInfo.forEach((item) => { if (item.to && item.to.length > 0) { item.to.forEach((item1) => { let nodeConfig = Object.assign( { source: item.id, target: item1, }, readyConfig ); this.renderList.push(nodeConfig); }); } this.makeNodeConfig(item); }); this.readyPlumbNodeFun(flag); }); }, //配置节点的具体信息 makeNodeConfig(item) { this.PlumbInit.setSourceEnabled(item.id, true); this.PlumbInit.setTargetEnabled(item.id, true); this.PlumbInit.makeSource(item.id, sourceConfig); this.PlumbInit.makeTarget(item.id, targetConfig); this.PlumbInit.setDraggable(item.id, true); this.PlumbInit.draggable(item.id, { containment: "parent", stop: function (el) { item.left = el.pos[0]; item.top = el.pos[1]; }, }); }, //渲染页面关系 readyPlumbNodeFun(flag) { this.PlumbInit.ready(() => { this.renderList.forEach((item) => { this.PlumbInit.connect(item); }); if (flag !== "once") { return; } //连线事件 this.PlumbInit.bind("connection", (info) => { const sourceNode = this.dataInfo.find( (item) => item.id === info.sourceId ); if (sourceNode.to.includes(info.targetId)) { return false; } console.log("调用了几次"); sourceNode.to.push(info.targetId); // this.$nextTick(() => { // this.readyPlumbDataFun() // }) return true; }); //点击线条 this.PlumbInit.bind("click", (con) => { this.deleteLineInfo = { source: con.sourceId, target: con.targetId, }; this.deleteLine(this.deleteLineInfo); }); }); }, //plumbNode的样式 nodeStyle(item) { return { position: "absolute", left: item.left + "px", top: item.top + "px", width: "200px", height: "40px", lineHeight: "40px", textAlign: "center", borderLeft: "2px solid blue", borderRadius: "4%", cursor: "pointer", boxShadow: "#eee 3px 3px 3px 3px", }; }, //拖动开始 start() {}, //拖动结束 end(e) { this.refreshCanvas(); // 判断位置 this.judgePosition( this.ativeNodeItem, this.canvasInfo, e.originalEvent.x, e.originalEvent.y ); }, //添加节点 addNode(positionInfo, nodeInfo) { if (this.dataInfo.find((item) => item.id === nodeInfo.id)) { this.$message.error("该节点已经存在"); return; } this.dataInfo.push({ name: nodeInfo.name, id: nodeInfo.id, left: positionInfo.left, top: positionInfo.top, to: [], }); this.$nextTick(() => { this.readyPlumbDataFun(); }); }, //删除节点 deleteNode($event, nodeInfo) { $event.returnValue = false; let index = this.dataInfo.findIndex((item) => item.id === nodeInfo.id); this.dataInfo.splice(index, 1); this.readyPlumbDataFun(); }, //删除线 deleteLine(deleteLineInfo) { let dataInfo = cloneDeep(this.dataInfo); let node = dataInfo.find((val) => val.id === deleteLineInfo.source); let index = node.to.findIndex((val) => val === deleteLineInfo.target); node.to.splice(index, 1); this.dataInfo = null; this.dataInfo = dataInfo; this.readyPlumbDataFun(); }, checkInfo() { console.log(this.dataInfo, "dataInfo"); console.log(this.renderList, "渲染关系"); }, //获取画布信息 refreshCanvas() { this.canvasInfo = document .querySelector("#plumbBox") .getBoundingClientRect(); }, //判断节点拖拽位置 judgePosition(dragNodeInfo, plumbBoxPositionInfo, x, y) { if ( x - this.nodePositionDiff.leftDiff plumbBoxPositionInfo.right || y - this.nodePositionDiff.topDiff plumbBoxPositionInfo.bottom ) { this.$message.error("节点不能拖拽至画布之外"); } else { const positionInfo = { top: y - plumbBoxPositionInfo.top - this.nodePositionDiff.topDiff, left: x - plumbBoxPositionInfo.left - this.nodePositionDiff.leftDiff, }; this.addNode(positionInfo, dragNodeInfo); } }, //鼠标抬起时,距离判定 downNode(el, nodeItem) { this.ativeNodeItem = nodeItem; const mousedownPositionInfo = { x: el.clientX, y: el.clientY }; // 被拖拽节点初始的位置信息 const moveBoxBeforePosition = { x: el.target.getBoundingClientRect().x, y: el.target.getBoundingClientRect().y, }; this.nodePositionDiff = { leftDiff: mousedownPositionInfo.x - moveBoxBeforePosition.x, topDiff: mousedownPositionInfo.y - moveBoxBeforePosition.y, }; console.log(this.nodePositionDiff, "位置判定"); }, },};.flowBox { display: flex; height: 100%;}.leftMenu { width: 300px; /* height: 100%; */ border: 1px solid #1a1919;}h4 { margin-top: 10px; margin-left: 110px;}.leftNode { width: 200px; height: 40px; line-height: 40px; text-align: center; margin: 30px; border: dashed 1px #362c2c; cursor: move;}.plumbBox { flex: 1; /* height: 100%; */ position: relative;}.pointNode { border-radius: 50%; width: 10px; height: 10px; background: royalblue; position: absolute; bottom: -5px; left: 95px;}

各个函数的注释都写好了,操作方法也在其中,最主要的是根据插件暴露的api去领悟其中的每一个方法。

效果

上一篇:轻松Win10系统升级攻略

下一篇:在家带孩子几年了,不知道还能找着工作么?