4

最好用的流程编辑器bpmn-js系列之Modeler

 2 years ago
source link: https://blog.ops-coffee.cn/s/zyfo4hqxpklu6qtb-vg4ig
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

最好用的流程编辑器bpmn-js系列之Modeler

最好用的流程编辑器bpmn-js系列文章

上一篇文章『最好用的流程编辑器bpmn-js系列之本地文件』介绍了如何将本地的bpmn文件渲染到流程编辑器以及将编辑器内的流程作为图片或bpmn文件保存到本地,这篇文章将会介绍流程编辑界面的一些优化

以下演示代码基于上一节搭建好的vue环境,使用bpmn版本为当前最新版7.3.0

7.3.0之后版本的回调写法

文章开头先说一下我在写本系列教程中除了阅读部分源码和论坛外,还参考了很多网上的优秀文章,在此感谢这些无私的贡献者,目前网上大多数教程都是基于7.3.0之前的版本写的,在使用importXML等方法时用了如下callback的方式

modeler.importXML(xml, (err, warnings) => {
  // ...
});

但当将bpmn-js我升级到了7.3.0之后,再使用上边的方式就会出现报错

Error: Passing callbacks to importXML is deprecated and will be removed in a future major release.

提示这种回调方式不建议使用,并且会在后续版本中移除,推荐如下这样的写法来代替

try {
    const result = await modeler.importXML(xml);
    const { warnings } = result;
    console.log(warnings);
} catch (err) {
    console.log(err.message, err.warnings);
}

除了importXML方法外,其他需要使用上边这种写法的还有importDefinitionsopensaveXMLsaveSVGcreateDiagram

在流程编辑的过程中,会出现操作撤销和恢复撤销的需求,bpmn-js提供了redoundo方法来实现,使用比较简单

先添加两个按钮

<li>
  <a href="javascript:" class="active" @click="handlerUndo" title="撤销操作">撤销</a>
</li>
<li>
  <a href="javascript:" class="active" @click="handlerRedo" title="恢复操作">恢复</a>
</li>

然后编写对应的方法

handlerRedo() {
  this.bpmnModeler.get("commandStack").redo();
},
handlerUndo() {
  this.bpmnModeler.get("commandStack").undo();
}

当流程比较复杂,元素较多的时候,我们需要放大流程,关注于流程的某一块编辑,这时候就需要用到bpmn-js提供的zoom方法来实现流程图的放大缩小

添加三个按钮,分别为放大、缩小、还原

<li>
  <a href="javascript:" class="active" @click="handlerZoom(0.1)" title="放大">放大</a>
</li>
<li>
  <a href="javascript:" class="active" @click="handlerZoom(-0.1)" title="缩小">缩小</a>
</li>
<li>
  <a href="javascript:" class="active" @click="handlerZoom(0)" title="还原">还原</a>
</li>

然后编写对应的方法handlerZoom来控制放大和缩小,放大缩小实际上是通过zoom方法来实现的,zoom方法接收一个num参数,这个参数为显示的比例,默认为1,放大就是增加这个值,缩小就是减小这个值,还原就是将这个值重置为1

handlerZoom(radio) {
  const newScale = !radio ? 1.0 : this.scale + radio;
  this.bpmnModeler.get("canvas").zoom(newScale);

  this.scale = newScale;
}

需要注意的是这里的this.scale就是默认值1,需要在data中定义这个变量

bpmn-js除了提供如上一些方法能方便操作画布元素外,还提供键盘快捷键的操作支持,可通过在创建BpmnModeler时添加keybord配置来实现

this.bpmnModeler = new BpmnModeler({
  container: canvas,
  keyboard: {
    bindTo: window
  }
});

目前bpmn-js支持如下一些快捷键操作

  • ctrl + z : 撤销
  • ctrl + y : 恢复
  • ctrl + c : 复制
  • ctrl + v : 粘贴
  • ctrl + + : 放大
  • ctrl + - : 缩小
  • ctrl + 0 : 恢复
  • ctrl + del : 删除
  • ctrl + 箭头 : 上下左右移动

除了键盘快捷键外,bpmn-js也支持ctrl+鼠标滚轮来控制放大缩小

许多流程图软件的背景都是网格状,bpmn-js如何添加网格背景呢?修改相应class的css即可

.containers {
  background: white;
  overflow: auto;
  background-image: linear-gradient(
      90deg,
      rgba(220, 220, 220, 0.5) 6%,
      transparent 0
    ),
    linear-gradient(rgba(192, 192, 192, 0.5) 6%, transparent 0);
  background-size: 12px 12px;
  width: 100%;
  height: calc(100vh - 82px);
  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
}

如果你想修改或禁用bpmn-js的某些默认功能,例如你不想要左侧的工具栏Palette,则可以通过additionalModules选项来实现,additionalModules允许你使用自定义模块来修改或替换现有功能

this.bpmnModeler = new CustomModeler({
    container: canvas,
    additionalModules: [
      {
         // 禁用滚轮滚动
        zoomScroll: ["value", ""],
        // 禁止拖动线
        bendpoints: ["value", ""],
        // 禁用左侧面板
        paletteProvider: ["value", ""],
        // 禁止点击节点出现contextPad
        contextPadProvider: ["value", ""],
        // 禁止双击节点出现label编辑框
        labelEditingProvider: ["value", ""]
      }
    ]
});

bpmn-js基本使用文章中我们讲到了bpmn-js有两个模式Modeler编辑模式和Vierer预览模式,实际上有很多小伙伴并不使用bpmn-js提供的Viewer模式,而是通过禁用以上这些组件来达到类似Viewer展示的模式

很多时候我们需要监听用户的操作并给予相应的反馈,例如在bpmn-js本地文件文章中关于监听变化下载文件的介绍中,我们需要监听到流程的变化,并实时将数据补充到a标签中,用到了如下代码

this.bpmnModeler.on("commandStack.changed", opscoffee)

这里的bpmnModeler.on就是个监听方法,能监听到流程的变化,除此之外bpmn-js还提供了eventBus来帮助我们做事件监听,用法如下

export default {
  ...
  methods: {
    init() {
      const canvas = this.$refs.canvas;
      this.bpmnModeler = new BpmnModeler({
        container: canvas
      });

      this.createNewDiagram();
    },
    async createNewDiagram() {
      try {
        const result = await this.bpmnModeler.importXML(this.xmlStr);
        const { warnings } = result;
        console.log(warnings);

        this.success();
      } catch (err) {
        console.log(err.message, err.warnings);
      }
    },
    success() {
      this.addEventBusListener();
    },
    addEventBusListener() {
      const that = this;
      const eventBus = this.bpmnModeler.get("eventBus");

      eventBus.on("element.click", function(e) {
        console.log("eventBusListener", e);
      });
    }
  }
};
</script>

createNewDiagram渲染完成后调用success方法,success中添加了addEventBusListener监听方法,然后通过eventBus.on来实现对事件的监听,例如这里监听了element.click点击事件

监听到事件后就很方便的进行后续的操作了,例如我想判断如果用户点击了UserTask任务,那么就打印任务id、type、name,则可以这样实现

addEventBusListener() {
  const that = this;
  const eventBus = this.bpmnModeler.get("eventBus");

  eventBus.on("element.click", function(e) {
    that.elementClick(e);
  });
},
elementClick(e) {
  if (e.element.businessObject.$type === "bpmn:UserTask") {
    console.log(
      "这是一个用户节点",
      e.element.businessObject.id,
      e.element.businessObject.$type,
      e.element.businessObject.name
    );
  }
}

那么究竟有哪些事件类型呢?我们可以通过bpmnModeler.get("eventBus")方法来获取,常用的有如下这些

  • canvas.destroy
  • canvas.init
  • canvas.viewbox.changed
  • canvas.viewbox.changing
  • connect.end
  • connection.added
  • connection.changed
  • connection.remove
  • connection.removed
  • create.end
  • diagram.clear
  • diagram.destroy
  • diagram.init
  • element.changed
  • element.click
  • element.hover
  • element.marker.update
  • element.out
  • elements.changed
  • interactionEvents.createHit
  • interactionEvents.updateHit
  • render.connection
  • render.getConnectionPath
  • render.getShapePath
  • render.shape
  • selection.changed
  • shape.added
  • shape.changed
  • shape.move.end
  • shape.remove
  • shape.removed

当我们需要获取流程中的节点时,可以使用如下方法来获取全部节点或检索指定类型的节点

获取全部节点

bpmnModeler.get("elementRegistry").getAll()

获取指定ID节点

bpmnModeler.get("elementRegistry").get(ID)

通过modeling的updateProperties方法可以修改节点属性,例如这里修改节点nameops-coffee.cn

const modeling = this.bpmnModeler.get("modeling");

const element = this.bpmnModeler
  .get("elementRegistry")
  .get("Activity_1w1vj9r");

modeling.updateProperties(element, {
  name: "ops-coffee.cn"
});

接触bpmn-js不久,且第一次用VUE,边学边写,文章难免出错,各位多多包含。想要打造一个好用的适合自己的流程编辑器,需要了解的内容比较多,bpmn-js会分多篇文章来介绍,下一篇介绍bpmn-js的Viewer模式下的节点颜色修改、鼠标拖动等内容,欢迎关注

部分小伙伴对流程编辑器不了解,或是对BPMN不了解,我搭建了个在线的Demo: https://bpmn.ops-coffee.cn,点击链接即可轻松体验,建议PC端打开效果更好


能看到这里一定是真爱,关注一下吧

wx.sou1.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK