五、LogicFlow 创建自定义节点并实现节点拖拽

前言

最近这段时间实在是太忙了,项目都赶的很急,文章内容都是抽空一次一次的写点,所以更新的很慢,望大家理解。

经过前面一些章节的学习铺垫,大家对 LogicFlow 都有了一个基本的认识,同时也进行到了最关键,大家最关心一章:如何创建自定义节点,并实现节点拖拽

自定义节点的出现就是补齐系统中内置节点类型不够,样式单一等问题,那么如何创建一个自定义的节点呢?

接来下讲解的就是如何创建一个自定义节点,讲的比较浅显,但是也能满足基本的业务需求,如果大家在项目中对这块有更深入的需求,可以参考 官网节点Node介绍

LogicFlow 介绍

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

创建自定义Node前置知识

如果要在画布中使用,就需要一个节点,不论这个节点是内置的还是自定义的,他都得存在。

那这个 节点是如何向外暴露的呢?又如何引入的呢?

暴露的自定义节点对象

下面这段代码就是创建自定义节点的时候,向外暴露的数据结构。

export default {
	type: '', // 节点类型
	view: '', // 节点组件显示逻辑,例如重写锚点(version > v1.1.7),自定义HTML节点(使用的比较多)
	model: '' // 节点组件对应的逻辑,例如规则校验,节点样式属性,形状属性,连线桩点位设置等
}

我们可以看到,这就是一个常见的对象,总共就暴露了三个属性,分别是 type, view, model,至于他们的作用我已经写好了。

在自定义一个节点的时候,我们需要定义节点的 modelview

这是因为 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,
};

注册节点

已经成功创建节点组件,但还不能投入使用,那怎么办呢?

不要着急,接来下就是讲解怎么将这个节点投入使用。

  1. 创建注册自定义节点文件 Register.js
    在这里插入图片描述

    
    import DemoNode from '自定义节点组件路径';
    
    // 注册方式 二选一 即可, 一般批量注册比较常用
    export const RegisterCustomElement = (lf) => {
    	
    	// 注册单个
    	lf.register(DemoNode);
    
    	// 批量注册
    	lf.batchRegister([
    		DemoNode,
    	])
    	
    }
    
    
  2. 统一管理节点
    如果要使用自定义的节点需要将这些节点进行注册才行,接下来就是介绍如何实现注册,这里将注册节点单独拎出来放到创建的 Register.js 文件中,在需要的地方统一引用。

    // 注册 lf 需要使用的组件(左侧菜单显示项)
    import DemoNode from './Nodes/computeObj/DemoNode'; // demoNode
    
    /*
      * 注册节点
      * 注意需要传递 LogicFlow 实例对象
    */
    export const RegisterCustomElement = (lf) => {
      lf.register(DemoNode); // demo
    };
    
    
  3. 引入文件实现统一注册

    还记得前面 专栏四、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>

到这一步,就完全实现了从 自定义节点 到实现菜单 节点拖拽 到画布中这整个关键流程,还剩下一些其他的功能,那都相对来讲要简单的多了,无外乎都是一些增加功能的事情,两个字 简单

最后

创建自定义节点 并实现从左侧菜单 拖拽节点 到右侧画布中到这里就全部完成。

里面可能有一些被省略掉导致看不明白的,麻烦各位再关联前面文章看看,因为每个专栏都不可能把整体的代码都展示到这里,太长也很乱,主要是一些重要的内容即可,代码太多也没啥特别的意义,看着还容易眼花缭乱。

也有可能代码中有一些瑕疵,麻烦在评论区多多指正

看到这里如果觉得还行,麻烦点赞搜藏

在这里插入图片描述

Vue3 中结合 LogicFlow 可以用来实现动态流程图的展示,其中不同节点的颜色可以通过数据驱动的方式控制。LogicFlow 是一个轻量级的 JavaScript 动画库,专门用于绘制流程图、状态机等图形。 以下是基本步骤: 1. 安装依赖:首先需要安装 `logic-flow` 和 Vue 的集成库如 `vue-logic-flow` 或者手动引入 LogicFlow。 ```bash npm install logic-flow vue-logic-flow --save ``` 2. 初始化 LogicFlow 组件:在 Vue 组件中,创建一个 Flow 组件,并配置其初始数据结构,包括节点和连线。 ```html <template> <lf-flow :model="flowModel" @node-entered="onNodeEntered"></lf-flow> </template> <script> import { Flow } from 'vue-logic-flow'; export default { components: { Flow, }, data() { return { flowModel: { nodes: [], // 初始化节点数组 edges: [] // 初始化连线数组 } }; }, methods: { onNodeEntered(node) { // 当节点进入视图时,可以获取到该节点并设置颜色 node.color = this.getNodeColor(node.data.id); // 节点数据包含一个标识 id }, getNodeColor(id) { // 根据 id 计算并返回对应的颜色,例如通过一个映射对象或 API 查询 // 示例: const colorMap = { 'node-type-1': 'red', 'node-type-2': 'blue', // ... }; return colorMap[id] || 'default-color'; } } }; </script> ``` 3. 更新节点颜色:每当节点的数据有变化,比如节点类型改变时,你可以更新对应的 `getNodeColor` 函数的结果,然后逻辑流会自动更新节点的样式。 4. 配合 CSS:定义 CSS 规则,关联节点 ID 和颜色。 ```css .lf-node[node-id='node-type-1'] { background-color: red; } .lf-node[node-id='node-type-2'] { background-color: blue; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值