先看效果图
第一步
// 安装以下依赖 yarn add @logicflow/core yarn add @logicflow/extension yarn add vue-json-pretty
主要页面业务源码参考
<template> <div class="logic-flow-view"> <!-- 辅助工具栏 --> <Control class="demo-control" v-if="lf" :lf="lf" :catTurboData="true" @catData="$_catData" @catTurboData="$_catTurboData" ></Control> <!-- 节点面板 --> <NodePanel :lf="lf" :nodeList="nodeList"></NodePanel> <!-- 画布 --> <div id="LF-Turbo"></div> <!-- 数据查看面板 --> <el-dialog title="数据" :visible.sync="dataVisible" width="50%"> <DataDialog :graphData="graphData"></DataDialog> </el-dialog> <!-- 编辑节点信息 --> <el-dialog title="属性" :visible.sync="nodeInfo" width="70%"> <div :key="nodeKey"> <div class="node-content"> <el-form label-width="86px" :model="nodeObjProperties" class="demo-form-inline"> <el-row :gutter="40"> <el-col :span="12"> <el-form-item label="编号"> <el-input v-model="nodeObjProperties.overrideid" disabled placeholder="请输入"></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="名称"> <el-input v-model="nodeObjProperties.name" placeholder="请输入"></el-input> </el-form-item> </el-col> </el-row> <el-row :gutter="40" v-if="isNode"> <el-col :span="12"> <el-form-item label="分配角色"> <el-select v-model="nodeObjProperties.taskroleids" placeholder="请选择"> <el-option v-for="item in assignRole" :key="item.roleId" :label="item.roleName" :value="item.roleId" > </el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="阶段策略"> <!-- <el-input readonly placeholder="点击设置" @click="stageStrategyFun"></el-input> --> <el-button @click="stageStrategyFun">点击设置</el-button> </el-form-item> </el-col> </el-row> <el-row :gutter="40"> <el-col :span="12" v-if="isNode"> <el-form-item label="多实例类型"> <el-select v-model="nodeObjProperties.multiinstance_type" placeholder="请选择"> <el-option v-for="item in multiinstanceTypeList" :key="item.value" :label="item.label" :value="item.value" > </el-option> </el-select> </el-form-item> </el-col> <el-col :span="12" v-if="!isNode"> <el-form-item label="流转策略"> <el-button @click="sequenseflowStrategyFun">点击设置</el-button> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="说明"> <el-input v-model="nodeObjProperties.des" placeholder="请输入"></el-input> </el-form-item> </el-col> </el-row> </el-form> </div> </div> <span slot="footer" class="dialog-footer"> <el-button @click="nodeInfo = false">取 消</el-button> <el-button type="primary" @click="editNodeFun">确 定</el-button> </span> </el-dialog> </div> </template> <script lang="ts"> import { Vue, Component, Ref, Watch, Inject, Provide, Prop } from 'vue-property-decorator' import LogicFlow from '@logicflow/core' // BpmnXmlAdapter import { Menu, Snapshot, BpmnElement, InsertNodeInPolyline } from '@logicflow/extension' import '@logicflow/core/dist/style/index.css' import '@logicflow/extension/lib/style/index.css' import NodePanel from './LFComponents/NodePanel.vue' import Control from './LFComponents/Control.vue' import DataDialog from './LFComponents/DataDialog.vue' import { toTurboData, toLogicflowData } from './AdpterForTurbo' import { BpmnNode } from './config' import qs from 'qs' import demoDataNew from './zdata' @Component({ name: 'LF', components: { NodePanel, Control, DataDialog } }) export default class LF extends Vue { @Prop() private flowData!: any @Prop() private flowModelInit!: any @Prop() private assignRole!: any[] nodeObj: any = {} lf: any = {} graphData: any = null dataVisible = false // 画布样式 config: any = { background: { // color: '#f7f9ff' }, grid: { size: 10, visible: true, type: 'mesh', config: { color: '#dcdcdc', // 设置网格的颜色 thickness: '2' // 设置网格线的宽度 } }, keyboard: { enabled: true }, style: { rect: { radius: 16 }, edgeText: { background: { fill: '#fff' } } }, // adjustEdge: true, textEdit: true, isSilentMode: false, // edgeType: 'bezier', snapline: true, edgeTextDraggable: true, guards: { beforeClone(data: object) { console.log('beforeClone', data) return true }, beforeDelete(data: object) { // 可以根据data数据判断是否允许删除,允许返回true,不允许返回false // 文档: http://logic-flow.org/guide/basic/keyboard.html#%E5%A6%82%E4%BD%95%E9%98%BB%E6%AD%A2%E5%88%A0%E9%99%A4%E6%88%96%E8%80%85%E6%8B%B7%E8%B4%9D%E8%A1%8C%E4%B8%BA console.log('beforeDelete', data) // _this.$message('不允许删除', 'error') return true } } } nodeList = BpmnNode nodeInfo = false nodeKey = 0 taskroleidsList = [] multiinstanceTypeList = [ { id: 1, label: 'None', value: 'None' }, { id: 2, label: 'Parallel', value: 'Parallel' }, { id: 3, label: 'Sequential', value: 'Sequential' } ] nodeData = [] nodeObjProperties: any = {} isNode = true graphDataBefore: any = {} mounted() { this.$_initLf() } /** * 流程初始化 */ $_initLf() { const _this = this LogicFlow.use(Menu) LogicFlow.use(Snapshot) // 支持在连接线上插入节点 LogicFlow.use(InsertNodeInPolyline) // 支持转bpmnXml格式 // LogicFlow.use(BpmnXmlAdapter) // 使用bpmn插件,引入bpmn元素,这些元素可以在turbo中转换后使用 LogicFlow.use(BpmnElement) const lf = new LogicFlow({ ...this.config, container: document.querySelector('#LF-Turbo') as HTMLElement }) this.lf = lf || {} // 添加属性菜单 lf.addMenuConfig({ nodeMenu: [ { text: '属性', callback(node: any) { _this.showInfoDialog(node, true) } } ], edgeMenu: [ { text: '属性', callback(edge: any) { _this.showInfoDialog(edge, false) } } ] }) // 设置主题 lf.setTheme({ circle: { r: 15, stroke: '#000000', outlineColor: '#88f', strokeWidth: 2 }, rect: { outlineColor: '#88f', strokeWidth: 2, 100, height: 80, radius: 6, color: '#1890ff', opcity: 0.6 }, polygon: { strokeWidth: 2, points: '20, 0, 40, 20, 20, 40, 0, 20' }, polyline: { stroke: '#000000', hoverStroke: '#000000', selectedStroke: '#000000', outlineColor: '#88f', strokeWidth: 1 }, nodeText: { color: '#000000' }, edgeText: { color: '#000000', background: { fill: '#f7f9ff' } } }) this.$_registerNode() // 设置边类型bpmn:sequenceFlow为默认类型 // lf.setDefaultEdgeType('bpmn:sequenceFlow') this.$_render() } /** * 自定义节点注册 */ $_registerNode() { // registerStart(this.lf) // registerUser(this.lf) // registerEnd(this.lf) // registerDownload(this.lf) // registerPolyline(this.lf) // registerTask(this.lf) // registerConnect(this.lf) this.$_render() } /** * 事件初始化 */ $_LfEvent() { this.lf.on('node:click', (res: any) => {}) this.lf.on('edge:click', (res: any) => {}) // this.lf.on('blank:click', () => { // this.nodeInfo = false // }) } /** * 属性展示弹窗 * @param item 属性对象 * @param isNodeType 是否节点类型 */ showInfoDialog(item: any, isNodeType: boolean) { const data = item data.properties.name = data.text && data.text.value data.properties.id = data.id if (!data.text) { data.text = {} } this.isNode = isNodeType // this.lf.setNodeData(data) this.nodeObjProperties = JSON.parse(JSON.stringify(data.properties)) this.nodeObj = data this.nodeInfo = true this.nodeKey += 1 } /** * render初始化 */ $_render() { console.log(this.flowData, 'flowData') // 调用toLogicflowData将数据转换为LogicFlow内部识别的数据结构 // const lFData = toLogicflowData(this.flowData || {}) const lFData = toLogicflowData(demoDataNew) console.log(lFData, 'lFData') // 兼容老数据 对折现拉直处理 // lFData.edges.forEach((j: any) => { // // const pointsListArr = (j.pointsList && j.pointsList.slice(0)) || [] // // j.pointsList = [pointsListArr[0], pointsListArr[pointsListArr.length - 1]] // j.pointsList = // j.pointsList && // j.pointsList.filter((q: any) => { // return q.x === j.startPoint.x || q.y === j.startPoint.y || q.x === j.endPoint.x || q.y === j.endPoint.y // }) // }) this.lf.render(lFData) this.$_LfEvent() } /** * 查看数据 */ $_catData() { this.$data.graphData = this.$data.lf.getGraphData() this.$data.dataVisible = true } /** * 生成随机数 */ randNum() { let rand = '' for (let i = 0; i < 19; i++) { rand += Math.floor(Math.random() * 10) } return rand } /** * 保存 */ $_catTurboData() { this.graphDataBefore = this.$data.lf.getGraphData() this.reNodeData() this.reEdgeData() // json重组为借口需要格式 this.graphData = toTurboData(this.graphDataBefore) // this.graphData = this.graphDataBefore console.log(this.graphData, 'graphData--2') localStorage.setItem('submitData', JSON.stringify(this.graphData)) this.reFormData() } /** * 节点属性添加 */ reNodeData() { this.graphDataBefore.nodes = this.graphDataBefore.nodes.map((q: any) => { const overrideidVal = `${q.type.substring(5, q.type.length)}_${this.randNum()}` const propertiesVal = { overrideid: overrideidVal, usertaskassignment: { assignment: { type: 'static', assignee: `assignee_variable_${overrideidVal}` } }, multiinstance_collection: `assignee_${overrideidVal}`, multiinstance_variable: `assignee_variable_${overrideidVal}` } q.properties = Object.assign(q.properties, propertiesVal) // 添加节点ourgoing let outgoingArr: any = [] this.graphDataBefore.edges.forEach((j: any) => { if (q.id === j.sourceNodeId && j.targetNodeId) { outgoingArr.push({ resourceId: j.targetNodeId }) } }) q.outgoing = outgoingArr return q }) } /** * 连接线属性添加 */ reEdgeData() { this.graphDataBefore.edges = this.graphDataBefore.edges.map((q: any) => { const overrideidVal = `${q.type.substring(5, q.type.length)}_${this.randNum()}` const propertiesVal = { overrideid: overrideidVal, usertaskassignment: { assignment: { type: 'static', assignee: '${assignee_variable_' + 'overrideidVal' + '}' } }, multiinstance_collection: `assignee_${overrideidVal}`, multiinstance_variable: `assignee_variable_${overrideidVal}` } q.properties = Object.assign(q.properties, propertiesVal) return q }) } /** * 重组参数为接口需要 */ reFormData() { const { name, description, key, lastUpdated, lastUpdatedBy, modelId, version } = this.flowModelInit const xmlJson = { modelId: this.flowData.modelId || '0', properties: this.flowData.properties, ...this.graphData } const formData = { modeltype: 'model', json_xml: JSON.stringify(xmlJson), name: name, description: description, key: key, lastUpdated: lastUpdated, newversion: true, common: '' } this.$emit('saveFlow', qs.stringify(formData)) this.$data.dataVisible = true } /** * 编辑节点属性 */ editNodeFun() { this.nodeObj.text.value = this.nodeObjProperties.name this.nodeObj.properties = this.nodeObjProperties if (this.isNode) { this.lf.setNodeData(this.nodeObj) } else { this.lf.setEdgeData(this.nodeObj) } console.log(this.nodeObj, 'this.nodeObj') this.nodeInfo = false } /** * 阶段策略 */ stageStrategyFun() { this.$emit('stageStrategy') } /** * 流转策略 */ sequenseflowStrategyFun() { this.$emit('sequenseflowStrategy') } } </script> <style scoped lang="scss"> .logic-flow-view { height: 100%; position: relative; } .demo-title { text-align: center; margin: 20px; } .demo-control { position: absolute; top: 10px; right: 10px; z-index: 2; } #LF-Turbo { 100%; height: 100%; outline: none; } .time-plus { cursor: pointer; } .add-panel { position: absolute; z-index: 11; background-color: white; padding: 10px 5px; } .el-drawer__body { height: 80%; overflow: auto; margin-top: -30px; z-index: 3; } ::v-deep .node-item-icon { margin: auto; } .node-panel { margin-left: 8px; left: 6px !important; } .node-content { margin-top: 14px; } ::v-deep .el-form--inline .el-form-item__content, .el-select { 100%; } ::v-deep .el-dialog { 700px !important; } </style>
具体根据业务需求
demo见我的仓库 https://gitee.com/zh888/logicflow-vue-bpm-demo-ing (注意:这个demo不是上面我写的业务源码参考,仅仅是我用过的参考demo)
花了一个多月时间研究改造的流程引擎实现方案,都是基于vue实现,简单上手即可运行,开发不易,若对你有用请鼓励下我,谢谢,也可加我vx:844271163,邀请进流程设计器解决方案群