目录
前言
最近这段时间实在是太忙了,项目都赶的很急,文章内容都是抽空一次一次的写点,所以更新的很慢,望大家理解。
经过前面一些章节的学习铺垫,大家对 LogicFlow
都有了一个基本的认识,同时也进行到了最关键,大家最关心一章:如何创建自定义节点,并实现节点拖拽。
自定义节点的出现就是补齐系统中内置节点类型不够,样式单一等问题,那么如何创建一个自定义的节点呢?
接来下讲解的就是如何创建一个自定义节点,讲的比较浅显,但是也能满足基本的业务需求,如果大家在项目中对这块有更深入的需求,可以参考 官网节点Node介绍。
LogicFlow 介绍
LogicFlow 是一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端研发自定义开发各种逻辑编排场景,如流程图、ER图、BPMN流程等。在工作审批配置、机器人逻辑编排、无代码平台流程配置都有较好的应用。
创建自定义Node前置知识
如果要在画布中使用,就需要一个节点,不论这个节点是内置的还是自定义的,他都得存在。
那这个 节点是如何向外暴露的呢?又如何引入的呢?
暴露的自定义节点对象
下面这段代码就是创建自定义节点的时候,向外暴露的数据结构。
export default {
type: '', // 节点类型
view: '', // 节点组件显示逻辑,例如重写锚点(version > v1.1.7),自定义HTML节点(使用的比较多)
model: '' // 节点组件对应的逻辑,例如规则校验,节点样式属性,形状属性,连线桩点位设置等
}
我们可以看到,这就是一个常见的对象,总共就暴露了三个属性,分别是 type
, view
, model
,至于他们的作用我已经写好了。
在自定义一个节点的时候,我们需要定义节点的 model
和 view
。
这是因为 LogicFlow
基于 MVVM
模式,需要通过重写定义 model
上获取样式相关的方法和重写 view
上的 getShape
来定义更复杂的节点外观,使用 setHtml
自定义一些节点。
Vue / React 自定义节点区别
-
Vue
import { HtmlNode, HtmlNodeModel } from "@logicflow/core"; class DemoNodeModel extends HtmlNode{ // 相关逻辑 } class DemoNodeModel extends HtmlNodeModel { // 相关逻辑 }
-
React
import { RectNode, RectNodeModel } from "@logicflow/core"; class DemoNodeModel extends RectNode{ // 与 Vue 基本一致.... } class DemoNodeModel extends RectNodeModel { // 与 Vue 基本一致.... }
目标效果
根据自己的业务需求,在合理的位置创建一个以当前 节点名称
或者 节点类型
命名的 js文件
,例如这里创建一个DemoNode.js文件。
注意:当然你也可以命名其他的,只要自己认识就行,没有特别要求。
创建这个组件就简单了,与我们在 Vue
或者 React
中创建组件一般无二,没啥区别。
下图在 Vue
环境下创建的一个 DemoNode组件
,能够看到已经将左侧的节点拖拽至右侧的画布中,接下来我们将实现这个功能。
实现目标
实现自定义节点到节点拖拽的顺序与官方有些不一样,官网是 以大见小
,我这里是 以小见大
。
这样写有个区别哈,那么按照官网的步骤从外到里一步一步的深入,那没问题,就是缺啥加啥,但是需要对这个 LogicFlow
有个整体的了解,对于刚刚开始的同学来讲,可能会有点不够友好,出了问题也不知道该怎么解决。
所以针对这种情况,我选择以小见大
,先把准备工作做好了,然后进行各自引入使用,最后实现整体功能,下面就正式开始讲解,代码已经贴上。
目录介绍
根据自己的需求,在合适的目录下创建一个文件夹,用于存放所有的自定义节点文件,目的是便于统一管理,我这里创建的自定义节点比较多,重点关注 DemoNode
相关的文件,例如下图框选文件夹:
创建自定义节点组件
<template>
<div class="demo-wrp">
<div class="icon-div">
<img class="demo-icon" src="@/assets/lfCanvas/singleCrowd.svg" alt="">
</div>
<div class="desc-div">
<p class="title">DEMO_NODE</p>
<p class="title-1">数量:<span>1000</span></p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
};
},
};
</script>
<style lang="less" scoped>
.demo-wrp{
width: 240px;
height: 80px;
border: 1px solid rgba(255, 180, 30, 1);
background: rgba(255, 180, 30, 0.15);
padding: 12px 24px;
border-radius: 8px;
display: flex;
gap: 8px;
.icon-div{
width: 40px;
height: 40px;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 5px;
background: linear-gradient(180deg, #fedc47 0%, rgba(230, 180, 14, 0.8) 100%);
.demo-icon{
width: 16px;
height: 20px;
}
}
.desc-div{
height: 52px;
.title{
font-family: PingFang SC;
font-size: 16px;
font-weight: 600;
line-height: 24px;
text-align: left;
color: rgb(255, 180, 30);
white-space: nowrap;
width: 130px;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 4px;
}
.title-1{
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 24px;
text-align: left;
color: rgb(255, 180, 30);
white-space: nowrap;
width: 130px;
text-overflow: ellipsis;
}
}
}
</style>
补全自定义节点逻辑
还记得前面的前置知识 【暴露的自定义节点对象】 那段不,这里补充的就是那个暴露对象的一些逻辑。
export default {
type: '', // 节点类型,根据自己的业务需求,但是要保证与菜单的按钮节点类型保持一致
view: '', // 节点组件显示逻辑,例如重写锚点(version > v1.1.7),自定义HTML节点(使用的比较多)
model: '' // 节点组件对应的逻辑,例如规则校验,节点样式属性,形状属性,连线桩点位设置等
}
接下来开始补全:
import Vue from 'vue';
import { HtmlNode, HtmlNodeModel } from "@logicflow/core";
// 引入自定义节点vue文件
import ComponentNode from './vComp/DemoNode.vue';
// 定义节点在画布中的大小,一般是在vue中写的多少,这里就写多少,主要是用于选中状态和计算设置桩位置(这里的桩就是连线所用的)
const width = 240;
const height = 80;
// 将自定义的demoNode.vue 文件通过 h函数引人使用
class ExcludeNodeView extends HtmlNode {
constructor(props) {
super(props);
this.isMounted = false;
this.vueItem = null;
}
setHtml(rootEl) {
if (!this.isMounted) {
const { properties, text: { value } } = this.props.model;
const newModel = this.props.model;
const newGraphModel = this.props.graphModel;
const node = document.createElement('div');
rootEl.appendChild(node);
const Profile = Vue.extend({
render (h) {
/*
* 重点
* h方法是 LogicFlow 对外暴露的渲染函数,其用法与react、vue的createElement一致。
* 格式:h(nodeName, attributes, [...children])
*/
return h(ComponentNode, {
props: {
/**
* 可以携带一些这个节点的一些属性等数据,
* 这样在 DemoNode.vue 中可以通过 props 接收到这些数据用于界面展示等相关逻辑处理
*/
// ....
},
});
},
});
// 将节点挂载到vue对象上
this.vueItem = new Profile();
this.vueItem.$mount(node);
this.isMounted = true;
}
}
}
class ExcludeNodeModel extends HtmlNodeModel {
// 设置一些属性
setAttributes() {
// 设置宽高
this.width = width;
this.height = height;
// 自定义节点禁用修改文本信息
this.text.editable = false;
}
}
// 暴露的节点对象
export default {
type: 'DEMONODE',
view: ExcludeNodeView,
model: ExcludeNodeModel,
};
注册节点
已经成功创建节点组件,但还不能投入使用,那怎么办呢?
不要着急,接来下就是讲解怎么将这个节点投入使用。
-
创建注册自定义节点文件
Register.js
import DemoNode from '自定义节点组件路径'; // 注册方式 二选一 即可, 一般批量注册比较常用 export const RegisterCustomElement = (lf) => { // 注册单个 lf.register(DemoNode); // 批量注册 lf.batchRegister([ DemoNode, ]) }
-
统一管理节点
如果要使用自定义的节点需要将这些节点进行注册才行,接下来就是介绍如何实现注册,这里将注册节点单独拎出来放到创建的Register.js
文件中,在需要的地方统一引用。// 注册 lf 需要使用的组件(左侧菜单显示项) import DemoNode from './Nodes/computeObj/DemoNode'; // demoNode /* * 注册节点 * 注意需要传递 LogicFlow 实例对象 */ export const RegisterCustomElement = (lf) => { lf.register(DemoNode); // demo };
-
引入文件实现统一注册
还记得前面
专栏四、LogicFlow 自定义左侧菜单Menu
吧,在mounted
中对logicFlow
进行了一些初始化操作,所以这次对自定义组件进行注册就在这个里面。<template> <div class="demo-wrp"> <div class="demo-nemu"> <!-- 左侧菜单 --> <LeftMenu /> </div> <div class="demo-container"> <div class="demo-control-wrp"></div> <div class="demo-canvas-wrp" ref="diagram"></div> </div> </div> </template> <script> // 引入LogicFlow核心包 import LogicFlow from '@logicflow/core'; import '@logicflow/core/dist/style/index.css'; // 引入组件 import LeftMenu from './components/LeftMenu'; // ******** 注册自定义节点 ******** import { RegisterCustomElement } from './lf_omponents/Register'; export default ({ name: 'demo', data () { return { // ******** logicFlow 实例对象 ******** lf: null, // 模拟数据 mockData: { // ... } } }, components: { LeftMenu, }, mounted () { const that = this; // 创建实例 const lf = new LogicFlow({ container: this.$refs.diagram, // ... 其他的一些配置 }); // ------------------------------------ 重点 -------------------------------------------- // ******** 存储全局对象,便于其他地方使用到 LogicFlow 实例对象 ******** that.lf = lf; // ******** 注册自定义节点 ******** RegisterCustomElement(lf); // -------------------------------------------------------------------------------- /* 开启渲染 如果不要模拟数据,直接使用 lf.render() 即可。 **/ lf.render(this.mockData); // 渲染到视图中心为止,否则在左上角显示 lf.translateCenter(); }, }) </script> <style scoped lang="less"> // 样式... </style>
实现拖拽功能
自定义业务节点到这里就结束了,但是能够使用了吗?
明确的说还不能,还差将左侧的按钮拖拽(添加)到画布中的这么个过程。
<template>
<div class="demo-wrp">
<div class="demo-nemu">
<!-- ******************* 重点 ******************* -->
<LeftMenu @dragInNode="$_dragInNode"/>
</div>
<div class="demo-container">
<div class="demo-control-wrp"></div>
<div class="demo-canvas-wrp" ref="diagram"></div>
</div>
</div>
</template>
<script>
// 引入LogicFlow核心包
import LogicFlow from '@logicflow/core';
import '@logicflow/core/dist/style/index.css';
// 引入组件
import LeftMenu from './components/LeftMenu';
// ******** 注册自定义节点 ********
import { RegisterCustomElement } from './lf_omponents/Register';
export default ({
name: 'demo',
data () {
return {
// ******** logicFlow 实例对象 ********
lf: null,
// 模拟数据
mockData: {
// ...
}
}
},
components: {
LeftMenu,
},
mounted () {
// ....
},
// ------------------------------------ 重点 --------------------------------------------
methods: {
// 拖拽节点
$_dragInNode(data) {
const that = this;
that.lf.dnd.startDrag({
...data,
});
},
}
// --------------------------------------------------------------------------------
})
</script>
<style scoped lang="less">
// 样式...
</style>
到这一步,就完全实现了从 自定义节点 到实现菜单 节点拖拽 到画布中这整个关键流程,还剩下一些其他的功能,那都相对来讲要简单的多了,无外乎都是一些增加功能的事情,两个字 简单。
最后
创建自定义节点 并实现从左侧菜单 拖拽节点 到右侧画布中到这里就全部完成。
里面可能有一些被省略掉导致看不明白的,麻烦各位再关联前面文章看看,因为每个专栏都不可能把整体的代码都展示到这里,太长也很乱,主要是一些重要的内容即可,代码太多也没啥特别的意义,看着还容易眼花缭乱。
也有可能代码中有一些瑕疵,麻烦在评论区多多指正。
看到这里如果觉得还行,麻烦点赞,搜藏。