详解《基于 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,
});
目录
相关文章
|
8天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端框架
【10月更文挑战第34天】在数字化时代,后端开发如同一座桥梁,连接着用户界面与数据处理的两端。本文将通过Node.js这一轻量级、高效的平台,带领读者领略后端框架的魅力。我们将从基础概念出发,逐步深入到实战应用,最后探讨如何通过代码示例来巩固学习成果,使读者能够在理论与实践之间架起自己的桥梁。
|
1月前
|
JavaScript 前端开发 中间件
探索后端技术:Node.js与Express框架的完美融合
【10月更文挑战第7天】 在当今数字化时代,Web应用已成为日常生活不可或缺的一部分。本文将深入探讨后端技术的两大重要角色——Node.js和Express框架,分析它们如何通过其独特的特性和优势,为现代Web开发提供强大支持。我们将从Node.js的非阻塞I/O和事件驱动机制,到Express框架的简洁路由和中间件特性,全面解析它们的工作原理及应用场景。此外,本文还将分享一些实际开发中的小技巧,帮助你更有效地利用这些技术构建高效、可扩展的Web应用。无论你是刚入门的新手,还是经验丰富的开发者,相信这篇文章都能为你带来新的启发和思考。
|
2月前
|
Web App开发 JavaScript 前端开发
构建高效后端服务:Node.js与Express框架的实战指南
【9月更文挑战第6天】在数字化时代的潮流中,后端开发作为支撑现代Web和移动应用的核心,其重要性不言而喻。本文将深入浅出地介绍如何使用Node.js及其流行的框架Express来搭建一个高效、可扩展的后端服务。通过具体的代码示例和实践技巧,我们将探索如何利用这两个强大的工具提升开发效率和应用性能。无论你是后端开发的新手还是希望提高现有项目质量的老手,这篇文章都将为你提供有价值的见解和指导。
|
1月前
|
JavaScript 前端开发 API
Vue.js:现代前端开发的强大框架
【10月更文挑战第11天】Vue.js:现代前端开发的强大框架
65 41
|
14天前
|
JavaScript 中间件 API
Node.js进阶:Koa框架下的RESTful API设计与实现
【10月更文挑战第28天】本文介绍了如何在Koa框架下设计与实现RESTful API。首先概述了Koa框架的特点,接着讲解了RESTful API的设计原则,包括无状态和统一接口。最后,通过一个简单的博客系统示例,详细展示了如何使用Koa和koa-router实现常见的CRUD操作,包括获取、创建、更新和删除文章。
35 4
|
21天前
|
Web App开发 JavaScript 中间件
构建高效后端服务:Node.js与Express框架的完美结合
【10月更文挑战第21天】本文将引导你走进Node.js和Express框架的世界,探索它们如何共同打造一个高效、可扩展的后端服务。通过深入浅出的解释和实际代码示例,我们将一起理解这一组合的魅力所在,并学习如何利用它们来构建现代Web应用。
40 1
|
1月前
|
设计模式 JavaScript 前端开发
浅谈JavaScript 框架在现代 Web 开发中的作用
浅谈JavaScript 框架在现代 Web 开发中的作用
38 12
|
9天前
|
Web App开发 JavaScript 前端开发
构建高效后端服务:Node.js与Express框架的实践
【10月更文挑战第33天】在数字化时代的浪潮中,后端服务的效率和可靠性成为企业竞争的关键。本文将深入探讨如何利用Node.js和Express框架构建高效且易于维护的后端服务。通过实践案例和代码示例,我们将揭示这一组合如何简化开发流程、优化性能,并提升用户体验。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用技巧。
|
11天前
|
Web App开发 JavaScript 中间件
构建高效后端服务:Node.js与Express框架的融合之道
【10月更文挑战第31天】在追求快速、灵活和高效的后端开发领域,Node.js与Express框架的结合如同咖啡遇见了奶油——完美融合。本文将带你探索这一组合如何让后端服务搭建变得既轻松又充满乐趣,同时确保你的应用能够以光速运行。
20 0
|
1月前
|
JSON JavaScript 前端开发
Node.js Express 框架
10月更文挑战第7天
27 2