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

先看效果图

【前端开发】基于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,
        width: 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 {
  width: 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 {
  width: 100%;
}
::v-deep .el-dialog {
  width: 700px !important;
}
</style>

具体根据业务需求

demo见我的仓库 https://gitee.com/zh888/logicflow-vue-bpm-demo-ing (注意:这个demo不是上面我写的业务源码参考,仅仅是我用过的参考demo)

 

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

上一篇:深度探索-Redis复制


下一篇:解决nginx转发websocket报400错误