您好,登录后才能下订单哦!
这篇文章主要介绍“vue+jsplumb实现工作流程图的方法”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vue+jsplumb实现工作流程图的方法”文章能帮助大家解决问题。
先写了一个demo,大概样子如下:
官网文档Home | jsPlumb Toolkit Documentation
先安装插件
npm install jsplumb --save
安装panzoom,主要用于鼠标滚轮缩放流程图
npm install panzoom --save
在需要的页面引入插件
import panzoom from 'panzoom' import { jsPlumb } from 'jsplumb'
接下来先写布局
父组件
<template> <div class="workflow"> <div class="flow_region"> <div id="flowWrap" ref="flowWrap" class="flow-wrap" @drop="drop($event)" @dragover="allowDrop($event)"> <div id="flow"> <flowNode v-for="item in data.nodeList" :id="item.id" :key="item.id" :node="item" @setNodeName="setNodeName" @changeLineState="changeLineState" /> </div> </div> </div> </div> </template>
flowNode是子组件
<template> <div ref="node" class="node-item" :class="{ isStart: node.type === 'start', isEnd: node.type === 'end', 'common-circle-node':node.type === 'start' || node.type === 'end' || node.type === 'event', 'common-rectangle-node':node.type === 'common' || node.type === 'freedom' || node.type === 'child-flow', 'common-diamond-node':node.type === 'gateway', 'common-x-lane-node':node.type === 'x-lane', 'common-y-lane-node':node.type === 'y-lane' }" :style="{ top: node.y + 'px', left: node.x + 'px' }" @click="setNotActive" @mouseenter="showAnchor" @mouseleave="hideAnchor" > <div class="nodeName">{{ node.nodeName }}</div> </div> </template>
样式主要是子组件的,父组件样式随意就行
<style lang="less" scoped> @labelColor: #409eff; @nodeSize: 20px; @viewSize: 10px; .node-item { position: absolute; display: flex; height: 40px; width: 120px; justify-content: center; align-items: center; border: 1px solid #b7b6b6; border-radius: 4px; cursor: move; box-sizing: content-box; font-size: 12px; z-index: 9995; &:hover { z-index: 9998; .delete-btn{ display: block; } } .log-wrap{ width: 40px; height: 40px; border-right: 1px solid #b7b6b6; } .nodeName { flex-grow: 1; width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center; } .node-anchor { display: flex; position: absolute; width: @nodeSize; height: @nodeSize; align-items: center; justify-content: center; border-radius: 10px; cursor: crosshair; z-index: 9999; background: -webkit-radial-gradient(sandybrown 10%, white 30%, #9a54ff 60%); } .anchor-top{ top: calc((@nodeSize / 2)*-1); left: 50%; margin-left: calc((@nodeSize/2)*-1); } .anchor-right{ top: 50%; right: calc((@nodeSize / 2)*-1); margin-top: calc((@nodeSize / 2)*-1); } .anchor-bottom{ bottom: calc((@nodeSize / 2)*-1); left: 50%; margin-left: calc((@nodeSize / 2)*-1); } .anchor-left{ top: 50%; left: calc((@nodeSize / 2)*-1); margin-top: calc((@nodeSize / 2)*-1); } } .active{ border: 1px dashed @labelColor; box-shadow: 0px 5px 9px 0px rgba(0,0,0,0.5); } .common-circle-node{ border-radius: 50%; height: 60px; width: 60px; } </style>
页面样式写完,接下来写插件配置
jsPlumb.ready() 是一个钩子函数,它会在 jsPlumb 准备完毕时执行。
连接线的建立是通过 jsPlumb.connect() 方法实现的。该方法接受一个对象作为配置项。其中包含了与上述概念一一对应的配置项,以及一些额外的样式。
source: 源对象,可以是对象的 id 属性、Element 对象或者 Endpoint 对象。
target: 目标对象,可以是对象的 id 属性、Element 对象或者 Endpoint 对象。
anchor: 是一个数组,数组中每一项定义一个锚点。
初始化方法
init() { this.jsPlumb.ready(() => { // 导入默认配置 this.jsPlumb.importDefaults(this.jsplumbSetting) // 完成连线前的校验 this.jsPlumb.bind('beforeDrop', evt => { const res = () => { } // 此处可以添加是否创建连接的校验, 返回 false 则不添加; return res }) this.loadEasyFlow() // 会使整个jsPlumb立即重绘。 this.jsPlumb.setSuspendDrawing(false, true) }) this.initPanZoom() }, // 加载流程图 loadEasyFlow() { // 初始化节点 for (let i = 0; i < this.data.nodeList.length; i++) { const node = this.data.nodeList[i] // 设置源点,可以拖出线连接其他节点 this.jsPlumb.makeSource(node.id, this.jsplumbSourceOptions) // // 设置目标点,其他源点拖出的线可以连接该节点 this.jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions) // this.jsPlumb.draggable(node.id); this.draggableNode(node.id) } // 初始化连线 this.jsPlumb.unbind('connection') // 取消连接事件 console.log(this.data.lineList) for (let i = 0; i < this.data.lineList.length; i++) { const line = this.data.lineList[i] const conn = this.jsPlumb.connect( { source: line.sourceId, target: line.targetId, paintStyle: { stroke: line.cls.linkColor, strokeWidth: 2 // strokeWidth: line.cls.linkThickness } }, this.jsplumbConnectOptions ) conn.setLabel({ label: line.label, cssClass: `linkLabel ${line.id}` }) } },
this.data 是需要渲染的数据,放在文章末尾,具体数据按照接口实际返回的来写
this.jsplumbSourceOptions 是jsplumb配置信息,新建一个文件编写,具体如下:
export const jsplumbSetting = { grid: [10, 10], // 动态锚点、位置自适应 anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle'], Container: 'flow', // 连线的样式 StateMachine、Flowchart,有四种默认类型:Bezier(贝塞尔曲线),Straight(直线),Flowchart(流程图),State machine(状态机) Connector: ['Flowchart', { cornerRadius: 5, alwaysRespectStubs: true, stub: 5 }], // 鼠标不能拖动删除线 ConnectionsDetachable: false, // 删除线的时候节点不删除 DeleteEndpointsOnDetach: false, // 连线的端点 // Endpoint: ["Dot", {radius: 5}], Endpoint: [ 'Rectangle', { height: 10, width: 10 } ], // 线端点的样式 EndpointStyle: { fill: 'rgba(255,255,255,0)', outlineWidth: 1 }, LogEnabled: false, // 是否打开jsPlumb的内部日志记录 // 绘制线 PaintStyle: { stroke: '#409eff', strokeWidth: 2 }, HoverPaintStyle: { stroke: '#409eff' }, // 绘制箭头 Overlays: [ [ 'Arrow', { width: 8, length: 8, location: 1 } ] ], RenderMode: 'svg' } // jsplumb连接参数 export const jsplumbConnectOptions = { isSource: true, isTarget: true, // 动态锚点、提供了4个方向 Continuous、AutoDefault anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle'] } export const jsplumbSourceOptions = { filter: '.node-anchor', // 触发连线的区域 /* "span"表示标签,".className"表示类,"#id"表示元素id*/ filterExclude: false, anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle'], allowLoopback: false } export const jsplumbTargetOptions = { filter: '.node-anchor', /* "span"表示标签,".className"表示类,"#id"表示元素id*/ filterExclude: false, anchor: ['TopCenter', 'RightMiddle', 'BottomCenter', 'LeftMiddle'], allowLoopback: false }
在父组件引入配置文件和方法
import { jsplumbSetting, jsplumbConnectOptions, jsplumbSourceOptions, jsplumbTargetOptions } from './config/commonConfig'
接下来在上面说的初始化方法文件里面配置鼠标滚轮缩放插件的方法 this.initPanZoom():
// 鼠标滚动放大缩小 initPanZoom() { const mainContainer = this.jsPlumb.getContainer() const mainContainerWrap = mainContainer.parentNode const pan = panzoom(mainContainer, { smoothScroll: false, bounds: true, // autocenter: true, zoomDoubleClickSpeed: 1, minZoom: 0.5, maxZoom: 2, // 设置滚动缩放的组合键,默认不需要组合键 beforeWheel: (e) => { // console.log(e) // let shouldIgnore = !e.ctrlKey // return shouldIgnore }, beforeMouseDown: function(e) { // allow mouse-down panning only if altKey is down. Otherwise - ignore var shouldIgnore = e.ctrlKey return shouldIgnore } }) this.jsPlumb.mainContainerWrap = mainContainerWrap this.jsPlumb.pan = pan // 缩放时设置jsPlumb的缩放比率 pan.on('zoom', e => { const { scale } = e.getTransform() this.jsPlumb.setZoom(scale) }) pan.on('panend', (e) => { }) // 平移时设置鼠标样式 mainContainerWrap.style.cursor = 'grab' mainContainerWrap.addEventListener('mousedown', function wrapMousedown() { this.style.cursor = 'grabbing' mainContainerWrap.addEventListener('mouseout', function wrapMouseout() { this.style.cursor = 'grab' }) }) mainContainerWrap.addEventListener('mouseup', function wrapMouseup() { this.style.cursor = 'grab' }) },
大功告成,data的数据放在这里,测试使用:
{ "FlowJson": { "nodeList": [ { "type": "start", "nodeName": "已新建", "id": "start-HiXWf8wsAcrWXjAAXVWc6AQk00000001", "node_code": "已新建", "trigger_event": "", "branch_flow": "", "icon": "play-circle", "x": 175, "y": 60, "width": 50, "height": 50 }, { "type": "freedom", "nodeName": "待审批", "id": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004", "node_code": "待审批", "trigger_event": "", "branch_flow": "", "icon": "sync", "x": 330, "y": 160, "width": 50, "height": 120 }, { "type": "end", "nodeName": "已通过", "id": "end-JjRvtD5J2GIJKCn8MF7IYwxh00000999", "node_code": "已通过", "trigger_event": "", "branch_flow": "", "icon": "stop", "x": 330, "y": 360, "width": 50, "height": 50 }, { "type": "end", "nodeName": "审批拒绝", "id": "end-J1DMScH5YjSKyk0HeNkbt62F00010001", "node_code": "审批拒绝", "trigger_event": "", "branch_flow": "", "icon": "stop", "x": 500, "y": 350, "width": 50, "height": 50 } ], "linkList": [ { "type": "link", "id": "link-BpI6ZuX1bJywz5SEi3R5QaWoi7g3QiSr", "sourceId": "start-HiXWf8wsAcrWXjAAXVWc6AQk00000001", "targetId": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004", "label": "LINE000000", "role": [], "organize": [], "audit_role": [], "audit_organize": [], "audit_organize_same": "0", "audit_dealer_same": "0", "audit_dealers": [], "notice": "0", "plug": "", "pass_option": "pass", "row_par_json": "", "judge_fields": "", "auth_at": "", "auth_user": "", "auth_stat": "", "auth_mark": "", "cls": { "linkType": "Flowchart", "linkColor": "#008000", "linkThickness": 4 } }, { "type": "link", "id": "link-5xJWzGlkIpUCsjmpfgesJxAOMHwkPlno", "sourceId": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004", "targetId": "end-J1DMScH5YjSKyk0HeNkbt62F00010001", "label": "LINE000001", "role": [], "organize": [], "audit_role": [ "PROJECT_SUPPORT_PLAN_CODE" ], "audit_organize": [], "audit_organize_same": "0", "audit_dealer_same": "0", "audit_dealers": [], "notice": "0", "plug": "", "pass_option": "reject", "row_par_json": "", "judge_fields": "", "auth_at": "", "auth_user": "", "auth_stat": "", "auth_mark": "", "cls": { "linkType": "Flowchart", "linkColor": "#808080", "linkThickness": 1 } }, { "type": "link", "id": "link-g05V3usXa86wAtpcMkvGzybdBlpasMjU", "sourceId": "freedom-YakFJzZ5VSp3Gec6ZULD2JDK00000004", "targetId": "end-JjRvtD5J2GIJKCn8MF7IYwxh00000999", "label": "LINE000002", "role": [], "organize": [], "audit_role": [ "PROJECT_SUPPORT_PLAN_CODE" ], "audit_organize": [], "audit_organize_same": "0", "audit_dealer_same": "0", "audit_dealers": [], "notice": "0", "plug": "", "pass_option": "approve", "row_par_json": "", "judge_fields": "", "auth_at": "", "auth_user": "", "auth_stat": "", "auth_mark": "", "cls": { "linkType": "Flowchart", "linkColor": "#808080", "linkThickness": 1 } } ] } }
关于“vue+jsplumb实现工作流程图的方法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。