前置
这一篇主要是对 LogicFlow
的一些功能及配置相关的介绍(大多是理论性的东西,有些是照抄的官网),然后在最后基于 Vue2
实现一个最最基础的Demo,如果对理论性的东西有免疫,建议直接跳到实现基础 demo目录,下一章节是基于这个Demo针对性的进行扩展功能。
LogicFlow 介绍
LogicFlow 是一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端研发自定义开发各种逻辑编排场景,如流程图、ER图、BPMN流程等。在工作审批配置、机器人逻辑编排、无代码平台流程配置都有较好的应用。
LogicFlow基础配置
引入方式
为了更细致一些,所以此段引入方式完全就是把官网的介绍给照搬了过来。在项目中引入 LogicFlow
相关文件的方式有如下两种方式:
-
命令安装(推荐)
- npm
npm install @logicflow/core --save # 插件包(不使用插件时不需要引入) npm install @logicflow/extension --save
- yarn
yarn add @logicflow/core # 插件包(不使用插件时不需要引入) yarn add @logicflow/extension
- pnpm
pnpm add @logicflow/core # 插件包(不使用插件时不需要引入) pnpm add @logicflow/extension
- npm
-
通过CDN引入
由于
LogicFlow
本身会有一些预置样式,所以除了需要引入js包
外还需要引入css包
。-
2.0 版本以前的CDN包的引入方式
(版本小于 2.0 使用)
<!-- 引入 core包和对应css --> <script src="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.27/dist/logic-flow.js"></script> <link href="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.27/dist/style/index.css" rel="stylesheet"> <!-- 引入 extension包和对应css(不使用插件时不需要引入) --> <!-- 值得注意的是:2.0版本之前,插件的脚本包是分开导出的 --> <!-- 因此引入某个组件,引用路径需要具体到包名,就像下文引入Menu插件这样👇🏻 --> <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension@1.2.27/lib/Menu.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@logicflow/extension@1.2.27/lib/style/index.css" />
-
2.0版本之后的CDN包的引入方式
(版本大于 2.0 使用)
<!-- 引入 core包和对应css --> <script src="https://cdn.jsdelivr.net/npm/@logicflow/core/dist/index.min.js"></script> <link href="https://cdn.jsdelivr.net/npm/@logicflow/core/lib/style/index.min.css" rel="stylesheet"> <!-- 引入 extension包和对应css(不使用插件时不需要引入) --> <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension/dist/index.min.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@logicflow/extension/lib/style/index.min.css" />
-
注意:具体使用那个方式,主要还是看自己的需求,我这里采用的是通过 npm 下载依赖包的方式。
另外具体的使用就不再过多的叙述,因为在当前的大环境下,只要有一些编程基础对于上面两种使用方式都不陌生,如果不太了解的话,建议先学习一下前端三大件: HTML,CSS,JS。
核心包
通过上面的 引入方式就能够看得出来,带有 core
的是核心包,带有 extension
的是插件包。
基础概念
实例(配置项)
一般创建实例的地方就是你当前界面展示的地方,这个得根据你自己的业务需求来,不过需要注意的是,在Vue或者React中,常规做法都是直接创建一个组件,然后在这个组件中处理相应的一应事项,使用的时候只需要引入这个组件即可,这大大提高复用率和降低项目体积,这可是好处多多。
// 创建实例
const lf = new LogicFlow({
container: '', // 画布挂载DOM节点
// ...其他配置项
})
上图创建的只是本地得一个测试demo,所以就没有将LogicFlow放到components目录下,望理解。
关于这个目录,这里简单的介绍一下,熟悉Vue的都知道界面文件一般都在 pages 目录下,当然也可以根据自己的喜好自定义,没有强制一说。
LogicFlow文件夹
:index.vue:
是demo组件的入口文件index.less:
对应的一些样式文件components:
是demo相关的一些公共组件,例如自定义的节点(按钮)LogicFlow.js:
是 lf 实例的一些相关配置
节点
LogicFlow 是基于 svg 做的流程图编辑框架,所以我们的节点和连线都是 svg 基本形状,对 LogicFlow 节点样式的修改,也就是对 svg 基本形状的修改。
-
内置节点
LogicFlow内部存在如下 **7种** 基础节点,分别为: - 矩形 --- `rect` - 圆形 --- `circle` - 椭圆 --- `ellipse` - 多边形 --- `polygon` - 菱形 --- `diamond` - 文本 --- `text` - HTML --- `html`
-
自定义节点(重点)
LogicFlow是基于继承来实现自定义节点、边。
开发者可以继承 LogicFlow 内置的节点,然后利用面向对象的机制 重写,
啥叫重写,就是在model或者view里面使用它的方法再次重写一次相关属性或者判断条件及其他相关逻辑。
这是官网贴的一张图,我估计大部分的开发者是没那个心思看这个的,即使看了,也是看的一头雾水。
很多同学都是苦恼于自定义节点这一步,后面章节会着重讲解如何创建一个自定义节点并投入使用。
而在实际的项目中,几乎100%是都是自定义节点,原因至少有以下几点:
- 内置的太少,不够用
- 跟项目中的风格不一致
- 私有的一些业务逻辑不好处理
边(节点与节点之间的连线)
- 边类型
- 内置边
- 直线(line)
- 直角折线(polyline)
- 贝塞尔曲线(bezier)
// 直线 import { LineEdge, PolylineEdgeModel } from "@logicflow/core"; // 折线 import { PolylineEdge, PolylineEdgeModel } from "@logicflow/core"; // 贝塞尔曲线 import { BezierEdge, BezierEdgeModel } from "@logicflow/core";
- 内置边
以上就是在项目中引用三种边的方式,具体可以查看官网描述。
背景
创建画布时,通过 background 选项来设置画布的背景层样式,支持透传任何样式属性到背景层。默认值为 false 表示没有背景
。
// ts定义背景中的一些属性及对应的类型
type BackgroundConfig = {
backgroundImage?: string,
backgroundColor?: string,
backgroundRepeat?: string,
backgroundPosition?: string,
backgroundSize?: string,
backgroundOpacity?: number,
filter?: string, // 滤镜
[key: any]: any,
};
// lf 实例
const lf = new LogicFlow({
background: false | BackgroundConfig,
});
网格
网格是指渲染/移动节点的最小单位。
网格最主要的作用是在移动节点的时候,保证每个节点中心点的位置都是在网格上。这样更有利于节点直接的对齐。一般来说,网格的间隔越大,在编辑流程图的时候,节点就更好对齐;网格的间隔越小,拖动节点的感觉就更加流畅。
网格默认关闭,渲染/移动最小单位为 1px。
若开启网格,则网格默认大小为 20px,渲染节点时表示以 20 为最小单位对齐到网络,移动节点时表示每次移动最小距离为 20px。
注意:在设置节点坐标时会按照网格的大小来对坐标进行转换,如设置中心点位置{ x: 124, y: 138 } 的节点渲染到画布后的实际位置为 { x: 120, y: 140 }。所以使用 LogicFlow 替换项目中旧的流程设计器时,需要对历史数据的坐标进行处理。
-
开启网格并应用默认属性:
// lf 实例 const lf = new LogicFlow({ grid: true, }); // 等同于默认属性如下 const lf = new LogicFlow({ grid: { size: 20, visible: true, type: "dot", config: { color: "#ababab", thickness: 1, }, }, });
-
设置网格属性
支持设置网格大小、类型、网格线颜色和宽度等属性。export type GridOptions = { size?: number // 设置网格大小 visible?: boolean, // 设置是否可见,若设置为false则不显示网格线但是仍然保留size栅格的效果 type?: 'dot' | 'mesh', // 设置网格类型,目前支持 dot 点状和 mesh 线状两种 config?: { color: string, // 设置网格的颜色 thickness?: number, // 设置网格线的宽度 } };
主题
LogicFlow 提供了设置主题的方法,便于用户统一设置其 内部
所有元素的样式。
设置方式有两种:
-
初始化LogicFlow时作为
配置
传入// 方法1:new LogicFlow时作为配置传入 const config = { domId: 'app', width: 1000, height: 800, style: { // 设置默认主题样式 rect: { // 矩形样式 ... }, circle: { // 圆形样式 ... }, nodeText: { // 节点文本样式 ... }, edgeText: { // 边文本样式 ... }, anchor: { // 锚点样式 ... } ... } } const lf = new LogicFlow(config);
-
初始化后,调用LogicFlow的
setTheme 方法
// 方法2: 调用LogicFlow的setTheme方法 lf.setTheme({ // 设置默认主题样式 rect: { // 矩形样式 ... }, circle: { // 圆形样式 ... }, nodeText: { // 节点文本样式 ... }, edgeText: { // 边文本样式 ... }, anchor: { // 锚点样式 ... } ... })
这里多说一句,这里仅仅是指使用内置的一些节点,边等元素的样式修改,但是在实际项目中,用到的可能性不大,大多都是自定义节点,也就有了自己的一些样式,这里权当看看。
当然项目中有些地方确实仅仅需要很少的改动就能够实现需求,那么就没必要再创建自定元素。
这个最终还是要根据自己的需求来平衡是否要使用主题功能。
事件
lf
实例上提供 on
方法支持监听事件。
-
监听事件,只监听一次
lf.once('aaa', () => { console.log('只执行一次') })
-
取消监听
lf.off('aaa', () => { console.log('取消监听事件') })
-
触发监听事件
lf.emit('', )
-
节点 / 边(连线)
点击事件lf.on("node:click, edge:click", (data) => { console.log('节点或者边的点击事件::', data); })
注意:
"node:click,edge:click"
可以拆分开单独使用。// 节点点击事件 lf.on("node:click", (data) => { console.log('节点点击事件::', data); }) // 边(连线)点击事件 lf.on("edge:click", (data) => { console.log('边点击事件::', data); })
-
节点 / 边(连线)
双击事件,同上可拆分使用lf.on('node:dbclick, edge:dbclick', (data) => { console.log('data::', data) });
-
键盘事件
设置键盘快捷键 Keyboard快捷键 说明 cmd + c
或ctrl + c
复制节点 cmd + v
或ctrl + v
粘贴节点 cmd + z
或ctrl + z
撤销操作 cmd + y
或ctrl + y
回退操作 backspace
或delete
删除操作 这是快捷键的基础公共配置,所有的键盘配置及处理事件全部在
shortcuts
下进行配置处理。
可以创建一个export const KeyboardConfig = { keyboard: { enabled: true, // 开启快捷键,默认关闭 shortcuts: [] } }
注意:下面代码中的 $LF 和 $Vue 分别是 lf 实例,Vue 实例对象,主要目的是便于在对应的快捷键回调中使用。
{ keys: ["backspace", "delete"], callback: (a, b) => { // callback 接受 a, b 两个参数,分别是事件Event, 触发键盘Key $Vue .$confirm("您确定要删除选中区域内的所有节点和边?", "提示", { confirmButtonText: "确认", cancelButtonText: "取消", type: "warning", }) .then(() => { const SELECT_ELEMENTS_OBJ = $LF.getSelectElements(); console.log("删除数据::", a, b, SELECT_ELEMENTS_OBJ, $Vue); const { edges, nodes } = SELECT_ELEMENTS_OBJ; { // 取消所有元素的选中状态 $LF.clearSelectElements(); // 删除边 if (edges.length > 0) { edges.forEach((edge) => { $LF.deleteEdge(edge["id"]); }); } // 删除节点 if (nodes.length > 0) { nodes.forEach((node) => { $LF.deleteNode(node["id"]); }); } } $Vue.$message({ type: "success", message: "删除成功!", }); }) .catch(() => { $Vue.$message({ type: "error", message: "删除失败!", }); }); }, }
{ keys: ["ctrl + c"], callback: () => { const SELECT_ELEMENTS_OBJ = $LF.getSelectElements(); const { nodes } = SELECT_ELEMENTS_OBJ; // 复制节点 // 注意:不能复制边(节点与节点之间的连线) if (nodes.length > 0) { nodes.forEach((node) => { $LF.cloneNode(node["id"]); }); } }, }
插件包
除了上面核心包以外,还有一些是非核心的部分,那么这部分就会作为 LogicFlow 的扩展功能使用,这部分的功能全部给放到了插件 @logicflow/extension
包中,便于用户根据自己的需求按需引用即可。
如果要使用需要下载对应版本的插件包,具体方式请查看前面的 引入方式
。
实现基础Demo
理论性的东西基本就结束了,接下来是实现一个最最基础的demo,然后其他的一些功能全部依托于这个基础进行扩展修改。
<template>
<div class="demo-canvas-wrp" ref="diagram"></div>
</template>
<script>
// 引入LogicFlow核心包
import LogicFlow from '@logicflow/core';
import '@logicflow/core/dist/style/index.css';
export default ({
name: 'demo',
data () {
return {
// 模拟数据
mockData: {
nodes: [
{ id: "1", type: "rect", x: 100, y: 100, text: "节点1" },
{ id: "2", type: "circle", x: 300, y: 100, text: "节点2" },
],
edges: [
{
sourceNodeId: "1",
targetNodeId: "2",
type: "polyline",
text: "连线",
startPoint: {
x: 140,
y: 100,
},
endPoint: {
x: 250,
y: 100,
},
},
],
}
}
},
mounted () {
// 创建实例
const lf = new LogicFlow({
container: this.$refs.diagram,
// ... 其他的一些配置
})
/*
开启渲染
如果不要模拟数据,直接使用 lf.render() 即可。
**/
lf.render(this.mockData);
// 渲染到视图中心为止,否则在左上角显示
lf.translateCenter();
}
})
</script>
<style scoped>
.demo-canvas-wrp{
width: 100%;
height: 100%;
}
</style>
看这段代码就能够发现上手是真的简单,不需要额外过多一些心智负担。
以证清白,上图说话
最后
这篇文章写得有些乏味,基本都是一些理论性的东西,虽然但是,有那个时间的还是建议 粗浅 看下理论部分(也就是说粗略的过一眼,有个印象),否则在后面具体的开发过程中难免有些配置不知如何下手,浪费大量的时间精力。
到这里基本就结束了,希望能够给你带来一些帮助。
如果看完对你有所帮助,希望能够点赞,收藏。