简记|LogicFlow自定义BPMN元素节点

前言

最近公司开始打算研发一款新产品,需要使用到流程编排。在前期技术栈调研下,综合考虑,使用Logicflow。

LogicFlow 提供了自定义节点自定义边, 可以实现满足 BPMN2.0 规范的节点和边。然后在使用数据转换将生成的数据转换为 Activity 需要的格式。

当时在写demo的时候看到这,其实很懵逼。一直没想通那BPMN的自定义节点。

现在想通之后,真的很简单!

自定义节点

它其实就是在自定义节点的时候将type的值设置为BPMN的某个节点类型。比如说,你需要自定义用户任务 (bpmn:userTask)节点,那么就将你自定义的节点type设置为bpmn:userTask

ts 复制代码
export default {
  type: 'bpmn:userTask',
  model: SqlElementHtmlModel,
  view: SqlElementHtmlNode
};

下面看具体示例:

自定义一个html节点,下面是组件代码sql-element.vue

vue 复制代码
<template>
  <div class="sql-element">
    <div class="sql-element__title">
      {{ props.properties!.databaseType || '--' }}
    </div>
    <el-row
      :gutter="20"
      class="sql-element__body"
      v-for="(item, index) in bodyList"
      :key="index"
    >
      <el-col :span="8">
        <div>{{ item.title }}</div>
      </el-col>
      <el-col :span="11">
        <div v-if="item.title === '输入'">
          <span>{{ item.input }}</span> ({{ item.input }} bytes)
        </div>
        <div v-if="item.title === '读/写'">
          <span>{{ item.read }} bytes</span> /
          <span>{{ item.write }} bytes</span>
        </div>
        <div v-if="item.title === '输出'">
          <span>{{ item.output }}</span> ({{ item.output }} bytes)
        </div>
        <div v-if="item.title === '任务/运行'">
          <span>{{ item.task }} / {{ item.operation }}</span>
        </div>
      </el-col>
      <el-col :span="5">
        <div>{{ item.time }} 分钟</div>
      </el-col>
    </el-row>
  </div>
</template>

<script lang="ts" setup>
const props = defineProps({
  model: Object,
  graphModel: Object,
  properties: Object,
  isSelected: Boolean,
  isHovered: Boolean,
  disabled: Boolean
});

console.log(props, 'props-sql-element');

const bodyList = ref([
  {
    title: '输入',
    input: '0',
    time: '5'
  },
  {
    title: '读/写',
    read: '0',
    write: '0',
    time: '5'
  },
  {
    title: '输出',
    output: '0',
    time: '5'
  },
  {
    title: '任务/运行',
    task: '0',
    operation: '00:00:00',
    time: '5'
  }
]);

onMounted(() => {});
</script>

<style lang="less" scoped>
.sql-element {
  width: 100%;
  height: 100%;
  padding: 10px;
  border: 1px solid #ff8b47;
  border-radius: 10px;
  box-sizing: border-box;
  background-color: #fff;
  &__title {
    color: #ff8b47;
  }
  &__body {
    margin-top: 10px;
    font-size: 12px;
    height: 15px;
    line-height: 15px;
  }
}
</style>

sql-element.ts

ts 复制代码
// 导入 HtmlNode 及其模型,为后续继承做准备
import { HtmlNode, HtmlNodeModel } from '@logicflow/core';
// 导入 Vue 相关方法,用于渲染组件
import { createApp, h } from 'vue';
// 导入 SQL 元素的 Vue 组件
import SqlElement from './sql-element.vue';

/**
 * 定义一个 SQL 元素的 HTML 节点类,继承自 HtmlNode
 * 该类负责在 HTML 中渲染 SQL 元素,并处理其交互逻辑
 */
class SqlElementHtmlNode extends HtmlNode {
  isMounted: any; // 标记组件是否已挂载
  r: any; // 渲染函数
  app: any; // Vue 应用实例

  /**
   * 构造函数
   * @param props 传递给节点的属性,包括模型、图模型等
   */
  constructor(props: any) {
    super(props);
    this.isMounted = false;
    // 创建 SQL 元素的渲染函数
    this.r = h(SqlElement, {
      model: props.model,
      graphModel: props.graphModel,
      disabled: props.graphModel.editConfigModel.isSilentMode,
      isSelected: props.model.isSelected,
      isHovered: props.model.isHovered,
      properties: props.model.getProperties(),
      text: props.model.inputData,
      // 定义按钮点击事件处理函数,更新文本内容
      onBtnClick: (i: any) => {
        this.r.component.props.text = String(
          Number(this.r.component.props.text) + Number(i)
        );
      }
    });
    // 创建 Vue 应用实例,并指定渲染函数
    this.app = createApp({
      render: () => this.r
    });
  }

  /**
   * 将 HTML 内容设置到指定的根元素上
   * @param rootEl 根元素
   */
  setHtml(rootEl: any) {
    if (!this.isMounted) {
      this.isMounted = true;
      const node = document.createElement('div');
      rootEl.appendChild(node);
      this.app.mount(node);
    } else {
      this.r.component.props.properties = this.props.model.getProperties();
    }
  }

  /**
   * 获取节点文本内容
   * 对于 SQL 元素,返回 null,因为其内容由特定组件渲染
   * @returns {null}
   */
  getText() {
    return null;
  }
}

/**
 * 定义一个 SQL 元素的 HTML 模型类,继承自 HtmlNodeModel
 * 该类主要设置节点的属性和样式
 */
class SqlElementHtmlModel extends HtmlNodeModel {
  /**
   * 设置节点属性
   * 包括宽度、高度、文本编辑属性等
   */
  setAttributes() {
    this.width = 300;
    this.height = 160;
    this.text.editable = false;
    // this.inputData = this.text.value;
  }

  // 定义节点只有左右两个锚点. 锚点位置通过中心点和宽度算出来。
  getDefaultAnchor() {
    const { width, x, y, id } = this;
    return [
      {
        x: x - width / 2,
        y,
        name: 'left',
        id: `${id}_0`
      },
      {
        x: x + width / 2,
        y,
        name: 'right',
        id: `${id}_1`
      }
    ];
  }

  /**
   * 获取节点轮廓样式
   * 覆盖父类方法,设置 stroke 属性为 none,以适应特定的视觉效果
   * @returns {object} 节点轮廓样式
   */
  getOutlineStyle() {
    const style = super.getOutlineStyle();
    style.stroke = 'none';
    style.hover!.stroke = 'none';
    return style;
  }
}

// 导出默认对象,定义 SQL 元素的类型、模型和视图
export default {
  type: 'lxbpmn:ExecuteSQL',
  model: SqlElementHtmlModel,
  view: SqlElementHtmlNode
};

然后在进行注册这个节点

ts 复制代码
import CustomSqlElement from '@/components/custom/sql-element';

const lf = new LogicFlow({
    container: customBpmnRef.value,
    grid: true,
    hideAnchors: false, // 设置为 true 隐藏连接点
    stopZoomGraph: true, // 设置为 true 禁止缩放画布
    stopScrollGraph: true, // 设置为 true 禁止滚动画布
    snapline: true // 对齐线
  });

lf.batchRegister([CustomSqlElement]);

扩展BPMN节点

起先,我跟着这里走,我也自定义了一个名为bpmn:scriptTask的自定义节点,并注册了,但是在拖拽出来时发现不对,节点是个icon不是我自定义的节点

排查时发现是因为在注册时先注册了自定义节点,然后又注册了receiveTask,导致被覆盖了。

其实可以不用执行lf.register(receiveTask),只需要register了你自定义的节点就可以了,但是要注意,节点的type需要是你扩展的节点类型

相关推荐
乐多_L38 分钟前
使用vue3框架vue-next-admin导出表格excel(带图片)
前端·javascript·vue.js
南望无一1 小时前
React Native 0.70.x如何从本地安卓源码(ReactAndroid)构建
前端·react native
Mike_188702783511 小时前
1688代采下单API接口使用指南:实现商品采集与自动化下单
前端·python·自动化
鲨鱼辣椒️面1 小时前
HTML视口动画
前端·html
一小路一1 小时前
Go Web 开发基础:从入门到实战
服务器·前端·后端·面试·golang
堇舟1 小时前
HTML第一节
前端·html
纯粹要努力1 小时前
前端跨域问题及解决方案
前端·javascript·面试
小刘不知道叫啥1 小时前
React源码揭秘 | 启动入口
前端·react.js·前端框架
kidding7231 小时前
uniapp引入uview组件库(可以引用多个组件)
前端·前端框架·uni-app·uview
合法的咸鱼1 小时前
uniapp 使用unplugin-auto-import 后, vue文件报红问题
前端·vue.js·uni-app