详解《基于 javascript 的流程图编辑框架LogicFlow

简介: 详解《基于 javascript 的流程图编辑框架LogicFlow
1、LogicFlow 是什么

LogicFlow 是一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow 支持前端研发自定义开发各种逻辑编排场景,如流程图、ER 图、BPMN 流程等。在工作审批配置、机器人逻辑编排、无代码平台流程配置都有较好的应用。

目前,LogicFlow 已经支持了客服业务下 IVR、工单流转、智能机器人等多个运营系统,在各系统不同的流程配置需求中得到了验证。

2、LogicFlow 的能力和特性
2.1、快速搭建流程图编辑器

提供了一个流程图编辑所必需的各项能力,这也是 LogicFlow 的基础能力:

  • 图的绘制能力。基于 SVG 来绘制形状各异的节点和线,并提供了基础的节点(矩形、圆形、多边形等)和线(直线、折线、曲线)
  • 各类交互能力,让图动起来。根据节点、线、图的各类鼠标事件(hover、点击、拖拽等)做出反应。比如节点拖拽、拖拽创建边、线的调整、双击节点编辑文本等
  • 提升编辑效率的能力。提供网格、对齐线,上一步、下一步,键盘快捷键,图放大缩小等配套能力,帮助用户提升编辑效率
  • 提供了丰富的 API ,宿主研发通过 API 传参调用和监听事件的方式,与 LogicFlow 完成交互
    通过以上能力,前端研发可以低成本、快速的搭建起流程编辑的应用,提供流畅的产品交互。下面是通过 LogicFlow 内置的节点和配套能力,做的流程图示例:
2.2、基于业务场景拓展

当基础能力无法满足业务需求的时候,便需要基于业务场景拓展。这也是 LogicFlow 能支持客服侧多个系统的关键所在。

  • 设置图上所有元素的样式,比如各种节点、线、锚点、箭头、对齐线的大小颜色等,满足对前端样式调整的需求
  • API 拓展。支持在 LogicFlow 上注册自定义的方法,比如通过 API 拓展提供图片下载的方法
  • 自定义节点、线。内置的矩形、圆形等图形类节点往往无法满足实际的业务需求,需要定义具有业务意义的节点。LogicFlow 提供了 的方式让用户定制具有自定义图形、业务数据的节点,比如流程审批场景中的 “审批” 节点
  • 拓展组件。LogicFlow 在 SVG 图层上提供了 HTML 层和一系列坐标转换逻辑,并支持在 HTML 层注册组件。宿主研发可以通过 LogicFlow 的 API,基于任何 View 框架开发组件,比如节点的右键菜单、控制面板等
  • 数据转换 adapter。LogicFlow 默认导出的图数据不一定适合所有业务,此时可以通过 adapter API,在图数据从 LogicFlow 输入、输出的时候做自定义转换,比如转换成 BPMN 规范的图数据
  • 内置部分拓展能力。基于上述拓展能力,我们还单独提供了 extension 的包,用来存放客服业务下沉淀出的具有通用性的节点、组件等,比如面向 BPMN 规范的节点和数据 adapter,默认菜单。注意 extension 可以单独安装,并支持按需引入。

基于上述拓展的能力,前端研发能够根据实际业务场景的需求,灵活的开发出所需的节点、组件等。下面有两个基于 LogicFlow 拓展能力做出的流程图:

BPMN: http://logic-flow.org/examples/#/extension/bpmn

审批流程:http://logic-flow.org/examples/#/extension/approve

3、安装使用
3.1、使用 npm 引入
npm install @logicflow/core
npm install @logicflow/extension
3.2、绘制一个简单的流程图
import LogicFlow from "@logicflow/core";
import "@logicflow/core/dist/style/index.css";
const lf = new LogicFlow({
  container: document.querySelector("#app"),
  grid: true,
});
lf.render({
  nodes: [
    {
      id: "1",
      type: "rect",
      x: 100,
      y: 100,
      text: "节点1",
    },
    {
      id: "2",
      type: "circle",
      x: 300,
      y: 200,
      text: "节点2",
    },
  ],
  edges: [
    {
      sourceNodeId: "1",
      targetNodeId: "2",
      type: "polyline",
      text: "连线",
    },
  ],
});
3.3、在 vue项目中使用 logicflow

注意:初始化 LogicFlow 实例的时候,传入的参数 container,必须要 dom 上存在这个节点,不然会报错请检查 container 参数是否有效。

<template>
  <div class="container" ref="container"></div>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/dist/style/index.css";
export default {
  mounted() {
    this.lf = new LogicFlow({
      container: this.$refs.container,
      grid: true,
    });
    this.lf.render({
      nodes: [
        {
          id: "1",
          type: "rect",
          x: 100,
          y: 100,
          text: { x: 100, y: 100, value: "节点1" },
        },
        {
          id: "2",
          type: "circle",
          x: 300,
          y: 200,
          text: { x: 300, y: 200, value: "节点2" },
        },
      ],
      edges: [
        {
          sourceNodeId: "1",
          targetNodeId: "2",
          type: "polyline",
        },
      ],
    });
  },
};
</script>
<style scoped>
.container {
  width: 1000px;
  height: 500px;
}
</style>

效果:

4、核心功能
4.1、LogicFlow 实例
  1. 创建一个实例
    每一个流程设计界面,就是一个 LogicFlow 的实例。为了统一术语,我们后面统一在代码层面将LogicFlow的实例写作lf。
<style>
  #container {
    width: 1000px;
    height: 500px;
  }
</style>
<div id="container"></div>
const lf = new LogicFlow({
  container: document.querySelector("#container"),

当创建一个实例的时候,我们需要传递初始化 LogicFlow 实例的配置项。LogicFlow 支持非常丰富的初始化配置项,但是只有 LogicFlow 画布初始化时挂载的 DOM 节点container参数是必填的。完整的配置项参见 LogicFlow API

  1. LogicFlow 的图数据

在 LogicFlow 里面,我们把流程图看做是由节点和连线组成的图。所以我们采用如下数据结构来表示 LogicFlow 的图数据。

const graphData = {
  nodes: [
    {
      id: "node_id_1",
      type: "rect",
      x: 100,
      y: 100,
      text: { x: 100, y: 100, value: "节点1" },
      properties: {},
    },
    {
      id: "node_id_2",
      type: "circle",
      x: 200,
      y: 300,
      text: { x: 300, y: 300, value: "节点2" },
      properties: {},
    },
  ],
  edges: [
    {
      id: "edge_id",
      type: "polyline",
      sourceNodeId: "node_id_1",
      targetNodeId: "node_id_2",
      text: { x: 139, y: 200, value: "连线" },
      startPoint: { x: 100, y: 140 },
      endPoint: { x: 200, y: 250 },
      pointsList: [
        { x: 100, y: 140 },
        { x: 100, y: 200 },
        { x: 200, y: 200 },
        { x: 200, y: 250 },
      ],
      properties: {},
    },
  ],
};

为什么节点文本还要有坐标,直接用节点的坐标不行吗?

text是可以直接用字符串,这个时候,如果是节点的文本,我们会自动采用节点坐标作为节点文本坐标,如果是连接文本,我们会基于不同的连线类型计算一个合适的坐标作为节点坐标。

在有些应用场景下,我们文本是可以拖动的,为了保持一致,我们 LogicFlow 导出的文本数据都会带上坐标。

连线 startPoint、endPoint 数据和 pointsList 为什么是重复的?

目前,在 LogicFlow 内部内置了line, polyline,

bezier三种基础连线,这三种连线都有startPoint、endPoint数据。但是其中line导出的数据是不会带上pointsList。对于polyline,

pointsList表示折线所有的点。对于bezier,pointsList表示[‘起点’, ‘第一个控制点’,‘第二个控制点’,

‘终点’]。

properties 是用来做什么的? properties 的 LogicFlow 保留给具体业务场景使用的数据。

例如:在审批流场景,我们定义某个节点,这个节点通过了,节点为绿色,不通过节点为红色。那么节点的数据描述可以为:

{   type: 'apply',  
   properties: {
    isPass: true  
    } } 

type 的含义是什么?

type 表示节点或者连线的类型,这里的类型不仅可以是rect,polyline这种 LogicFlow

内置的基础类型,也可以是用户基于基础类型自定义的类型。

  1. 将图数据渲染到画布上
lf.render(graphData);
4.2、节点 Node

LogicFlow 的内置了一些基础节点,开发者在实际应用场景中,可以基于这些基础节点,定义符合自己业务逻辑的节点。

认识 LogicFlow 的基础节点

LogicFlow 是基于 svg 做的流程图编辑框架,所以我们的节点和连线都是 svg 基本形状,对 LogicFlow 节点样式的修改,也就是对 svg 基本形状的修改。LogicFlow 内部存在 7 种基础节点,分别为:

  • 矩形:rect
  • 圆形: circle
  • 椭圆: ellipse
  • 多边形: polygon
  • 菱形: diamond
  • 文本: text
  • HTML: html
4.3、边 Edge

和节点一样,LogicFlow 也内置一些基础的边。LogicFlow 的内置边包括:

  • 直线(line)
  • 直角折线(polyline)
  • 贝塞尔曲线(bezier)
4.4、主题 Theme

LogicFlow 提供了设置主题的方法,便于用户统一设置其内部所有元素的样式。

设置方式有两种:

  • 初始化LogicFlow时作为配置传入
  • 初始化后,调用LogicFlow的 setTheme 方法
4.5、事件 Event

当我们使用鼠标或其它方式与画布交互时,会触发的对应的事件。通过监听这些事件,可以获取其在触发时所产生的数据,根据这些数据来实现需要的功能。详细可监听事件见事件 API

  1. 监听事件
    lf实例上提供on方法支持监听事件。
lf.on("node:dnd-add", (data) => {});

LogicFlow 支持用逗号分割事件名。

lf.on("node:click,edge:click", (data) => {});
  1. 自定义事件
    除了 lf 上支持的监听事件外,还可以使用eventCenter对象来监听和触发事件。eventCenter是一个graphModel上的一个属性。所以在自定义节点的时候,我们可以使用eventCenter触发自定义事件。
class ButtonNode extends HtmlNode {
  setHtml(rootEl) {
    const { properties } = this.props.model;
    const el = document.createElement("div");
    el.className = "uml-wrapper";
    const html = `
      <div>
        <div class="uml-head">Head</div>
        <div class="uml-body">
          <div><button onclick="setData()">+</button> ${properties.name}</div>
          <div>${properties.body}</div>
        </div>
        <div class="uml-footer">
          <div>setHead(Head $head)</div>
          <div>setBody(Body $body)</div>
        </div>
      </div>
    `;
    el.innerHTML = html;
    rootEl.innerHTML = "";
    rootEl.appendChild(el);
    window.setData = () => {
      const { graphModel, model } = this.props;
      graphModel.eventCenter.emit("custom:button-click", model);
    };
  }
}
4.6、网格 Grid

网格是指渲染/移动节点的最小单位。网格最主要的作用是在移动节点的时候,保证每个节点中心点的位置都是在网格上。这样更有利于节点直接的对齐。一般来说,网格的间隔越大,在编辑流程图的时候,节点就更好对齐;网格的间隔越小,拖动节点的感觉就更加流畅。

网格默认关闭,渲染/移动最小单位为 1px,若开启网格,则网格默认大小为 20px,渲染节点时表示以 20 为最小单位对齐到网络,移动节点时表示每次移动最小距离为 20px。

注意在设置节点坐标时会按照网格的大小来对坐标进行转换,如设置中心点位置{ x: 124, y: 138 } 的节点渲染到画布后的实际位置为

{ x: 120, y: 140 }。所以使用 LogicFlow 替换项目中旧的流程设计器时,需要对历史数据的坐标进行处理。

提示在实际开发中,如果期望节点既可以中心对齐,也可以按照两边对齐。那么自定义节点的宽高需要是 grid 的偶数倍。也就是假设 grid 为

20,那么所有的节点宽度最好是 20、40、80、120 这种偶数倍的宽度。

  1. 开启网格
    在创建画布的时候通过配置 grid 来设置网格属性

开启网格并应用默认属性:

const lf = new LogicFlow({
  grid: true,
});
// 等同于默认属性如下
const lf = new LogicFlow({
  grid: {
    size: 20,
    visible: true,
    type: "dot",
    config: {
      color: "#ababab",
      thickness: 1,
    },
  },
});
  1. 设置网格属性
    支持设置网格大小、类型、网格线颜色和宽度等属性。
export type GridOptions = {
  size?: number  // 设置网格大小
  visible?: boolean,  // 设置是否可见,若设置为false则不显示网格线但是仍然保留size栅格的效果
  type?: 'dot' | 'mesh', // 设置网格类型,目前支持 dot 点状和 mesh 线状两种
  config?: {
    color: string,  // 设置网格的颜色
    thickness?: number,  // 设置网格线的宽度
  }
};
4.7、背景 Background

提供可以修改画布背景的方法,包括背景颜色或背景图片,背景层位于画布的最底层。

创建画布时,通过 background 选项来设置画布的背景层样式,支持透传任何样式属性到背景层。默认值为 false 表示没有背景。

const lf = new LogicFlow({
  background: false | BackgroundConfig,
});
type BackgroundConfig = {
  backgroundImage?: string,
  backgroundColor?: string,
  backgroundRepeat?: string,
  backgroundPosition?: string,
  backgroundSize?: string,
  backgroundOpacity?: number,
  filter?: string, // 滤镜
  [key: any]: any,
};

配置项
设置背景颜色

const lf = new LogicFlow({
  // ...
  background: {
    backgroundImage: "url(../asserts/img/grid.svg)",
    backgroundRepeat: "repeat",
  },
});
4.8、拖拽创建节点 Dnd

在流程图编辑场景中比起通过代码配置创建节点,我们可能更需要通过图形用户界面来操作创建流程图,这时候就可以通过拖拽的方式来实现。

拖拽需要结合图形面板来实现,步骤:创建面板 → 拖拽初始化 → 监听 drop 事件创建节点

示例如下:

lf.dnd.startDrag({
  type,
  text: `${type}节点`,
});

通过上面的代码可以看出,将节点通过div标签+css样式的方式绘制到面板中,并为其绑定onMouseDown事件,当拖拽图形时,会触发lf.dnd.startDrag函数,表示开始拖拽,并传入选中图形的配置,startDrag入参格式:

lf.dnd.startDrag(nodeConfig: NodeConfig):void
type NodeConfig = {
  id?: string; // 不建议直接传id, logicflow id不允许重复
  type: string;
  text?: TextConfig;
  properties?: Record<string, unknown>;
};

拖拽结束鼠标松开时,将当前鼠标的位置转换为画布上的坐标,并以此为节点的中心点坐标x、y,合并拖拽节点传入的nodeConfig,监听到 drop 事件后会调用lf.addNode方法创建节点。

使用拖拽面板插件

LogicFlow 在 extension 中内置了拖拽面板插件,如果不想自定义图形面板,可以使用此插件快速实现。

4.9、键盘快捷键 Keyboard

快捷键配置

通过创建 LogicFlow 实例时传入 options 的 keyboard 属性可以开启快捷键, 可以只配置 enabled 属性,为 true 时,代表开启默认的快捷键。

const lf = new LogicFlow({
  container: document.querySelector("#app"),
  keyboard: {
    enabled: true,
  },
});

内置快捷键功能

参考不同的产品,内置了复制,粘贴,redo/undo,删除 的快捷键。

快捷键 功能

cmd + c 或 ctrl + c 复制节点

cmd + v 或 ctrl + v 粘贴节点

cmd + z 或 ctrl + z 撤销操作

cmd + y 或 ctrl + y 回退操作

backspace 删除操作

自定义快捷键

shortcuts 则可以定义用户自定义的一组快捷键 值得一提的是 keys 的规则,与mousetrap一致。

我们已自定义删除功能为例,在删除之前添加一个确认操作。

const lf = new LogicFlow({
  // ...
  keyboard: {
    enabled: true,
    shortcuts: [
      {
        keys: ["backspace"],
        callback: () => {
          const r = window.confirm("确定要删除吗?");
          if (r) {
            const elements = lf.getSelectElements(true);
            lf.clearSelectElements();
            elements.edges.forEach((edge) => lf.deleteEdge(edge.id));
            elements.nodes.forEach((node) => lf.deleteNode(node.id));
          }
        },
      },
    ],
  },
});
4.10、对齐线 Snapline

对齐线能够在节点移动过程中,将移动节点的位置与画布中其他节点位置进行对比,辅助位置调整。位置对比有如下两个方面。

  • 节点中心位置
  • 节点的边框

对齐线使用

普通编辑模式下,默认开启对齐线,也可通过配置进行关闭。

在静默模式下,无法移动节点,所以关闭了对齐线功能,无法通过配置开启。

// 关闭对齐线功能
const lf = new LogicFlow({
  snapline: false,
});

对齐线样式设置

对齐线的样式包括颜色和宽度,可以通过设置主题的方式进行修改。

// 默认配置
{
  stroke: '#1E90FF',
  strokeWidth: 1,
}
// 修改对齐线样式
lf.setTheme({
  snapline: {
    stroke: '#1E90FF', // 对齐线颜色
    strokeWidth: 1, // 对齐线宽度
  },
})
4.11、设置图编辑方式

LogicFlow 提供了非常多的控制图如何编辑的配置,详情见editConfigModel。

初始化

LogicFlow 支持在初始化的时候传入很多配置参数,图编辑的配置也可以在初始化的时候传入。

const lf = new LogicFlow({
  stopZoomGraph: true, // 禁止缩放
  stopScrollGraph: true, // 禁止鼠标滚动移动画布
});

更新图编辑方式

lf.updateEditConfig({
  stopZoomGraph: false,
  stopScrollGraph: false,
});

静默模式

画布的静默模式可以简单理解为“只读”模式,这种模式下,画布中的节点和边不可移动,不可进行文案修改,没有锚点。

提示

静默模式只是 LogicFlow 内置的流程图编辑控制的一种快捷方式。

// 开启静默模式
const lf = new LogicFlow({
  isSilentMode: true,
});
目录
相关文章
|
1月前
|
开发框架 JavaScript 安全
js开发:请解释什么是Express框架,以及它在项目中的作用。
Express是Node.js的Web开发框架,简化路由管理,支持HTTP请求处理。它采用中间件系统增强功能,如日志和错误处理,集成多种模板引擎(EJS、Jade、Pug)用于HTML渲染,并提供安全中间件提升应用安全性。其可扩展性允许选用合适插件扩展功能,加速开发进程。
|
3月前
|
前端开发 JavaScript 开发者
什么是 Angular 框架中的 Zone.js
什么是 Angular 框架中的 Zone.js
27 0
|
4月前
|
开发框架 JSON JavaScript
Node.js教程-express框架
Node.js教程-express框架
39 1
|
14天前
|
JavaScript 前端开发 API
框架分析(3)-Vue.js
框架分析(3)-Vue.js
|
1月前
|
前端开发 JavaScript 开发者
编程笔记 html5&css&js 014 网页布局框架
编程笔记 html5&css&js 014 网页布局框架
|
1月前
|
前端开发 JavaScript API
编程笔记 html5&css&js 011 HTML内连框架
编程笔记 html5&css&js 011 HTML内连框架
|
2月前
|
设计模式 前端开发 JavaScript
开源专访:Strve.js框架作者Vam
开源专访:Strve.js框架作者Vam
40 2
|
2月前
|
JavaScript 前端开发 开发者
JavaScript——周技能检测——菜单编辑——2022年11月22日(考完)
JavaScript——周技能检测——菜单编辑——2022年11月22日(考完)
25 0
|
2月前
|
开发框架 JavaScript 前端开发
比较两个突出的node.js框架:koa和express
接上文讲述了 koa框架,这边文章比较一下这两个突出的node.js框架:koa和express
|
2月前
|
JavaScript 前端开发
【前端开发】一文带你精通Vue.js框架(七)
【前端开发】一文带你精通Vue.js框架(七)
34 0