• 【前端开发】基于logicFlow可视化流程库改造的流程引擎教程


    先看效果图

     第一步

    // 安装以下依赖
    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,邀请进流程设计器解决方案群

  • 相关阅读:
    堆和栈的区别
    今天开通博客了!
    【转】Perl中的正则表达式
    【转】Windows server 2008远程桌面轻松搞定
    【转】彻底删除0KB顽固文件或文件夹的方法
    【转】Java URL Encoding and Decoding
    【转】一个女留学生在美国的七年
    【转】风雨20年:我所积累的20条编程经验
    【转】深入浅出REST
    【转】Python正则表达式指南
  • 原文地址:https://www.cnblogs.com/xiaohuizhang/p/15307837.html
Copyright © 2020-2023  润新知