当前位置: 首页 > 工具软件 > LogicFlow > 使用案例 >

LogicFlow插件-常用&内置

阳光辉
2023-12-01

Hi~ 大家好,我是小鑫同学,一位长期从事前端开发的编程爱好者,我将使用更为实用的案例输出更多的编程知识,同时我信奉分享是成长的唯一捷径,在这里也希望我的每一篇文章都能成为你技术落地的参考~

1. 准备工作:

1. 初始化项目:

1.1 初始化环境:

使用pnpm create vite创建项目,选择Vue框架及TypeScript变体;

1.2 安装核心模块:

在完成了前面的准备工作后,需要安装非核心的插件依赖模块:

pnpm add @logicflow/core
pnpm add @logicflow/extension

2. 插件注册:

2.1 注册插件到全局:

import { BpmnElement } from '@logicflow/extension';
LogicFlow.use(BpmnElement);

2.2 注册插件到实例:

import LogicFlow from "@logicflow/core";
import { DndPanel, SelectionSelect } from "@logicflow/extension";
import "@logicflow/core/dist/style/index.css";
import "@logicflow/extension/lib/style/index.css";

const lf = new LogicFlow({
  container: document.querySelector("#app"),
  grid: true,
  plugins: [DndPanel, SelectionSelect]
});

2. 配置拖拽面板 DndPanel:

拖拽面板的插件安装完成后就可以为拖拽面板添加元素了,也就是被拖拽的内容,每个元素都具备以下7个可选选项进行控制:

名称类型描述
typestring指定用户鼠标可以拖入的节点类型,包括自定义节点类型, 不传则不会创建节点,只会触发callback
textstring创建节点的文本
propertiesobject创建节点的properties
labelstring拖拽面板节点文本描述
iconstring拖拽面试上显示的图标,可以传入图标url地址或base64编码
classNamestring额外传入可以拖拽项的class, 用于自定义拖拽项的样式
callbackfn用户鼠标按下拖拽项后触发的回调
<script setup lang="ts">
import LogicFlow from "@logicflow/core";
import { DndPanel } from "@logicflow/extension";
import "@logicflow/core/dist/style/index.css";
import "@logicflow/extension/lib/style/index.css";

import { onMounted, ref } from "vue";

const container = ref();
const lf = ref<LogicFlow>();

const patternItems = [
    {
      type: 'circle',
      text: '开始',
      label: '开始节点',
      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAAXNSR0IArs4c6QAABQVJREFUWEfNWF1oHFUU/s5sUhVTyc5GUBqlpDObWol9UYr2webNIqQgQrA1LdZgsjNNpBZbsQHrTy2VaqXdnU2qVhEsgpW2CmkFSQtqffGvNBKT2QYLUhCzs9EoKN2ZI3d2NzvZ7M9ksxEH9mXvOd/55tw7557vEKp8moZ+vt120i0SuIWZwyBaDYcCAE8yMEmgyfrlDV/8uvW2v6oJQQtxajz8fSMtu7kTRJ0EtPvyJRqGg/NSHY9M9ajf+fIB4ItY4+DkWsm2uyChE4xmD/iomx3GFEk0Jf5nh5uY0ESglQDf47FNA4jaaUR/71evVCJYkVjIMHUmvASG7AYGvpWYTzmSdCYVUUbLBbg1llAcwkYGbwQgfgIhCeaopbfuK+dblpgcT5wA82M5QgAdS2nKsUpvW2xdjo13gqQ+AOvFOgGfJjW1oxRWSWJyPHEVzHe6IEwDSV3ZXw2hQp9QLLGXiV/J/n/N0tQVxXCLEpONiR8AWpvJFG9JaeETtSCVwwgaE5sJ9EFmZ/knSw/fVYg/j5gcN3eC8YYwtDS14hlcDGHZMDnr/6alqTu9WHMChwbNTezgtGvg4AFrh/r1YgJX8pWj5v2QcDGTOedF7wcxSyw4dKWN0s4wCM0gPGNF1MOVgGuxnt8hTtppWpcrJbPE5Jh5CIRdohykNPXeWgT1iyEb5pfZr3V2S11ioqJLNzZcFsWTQT1+SkJjfGwloW4Xk/P2dG/rJb8kypSSDwGkpQDWiRvCJRaMTfQQ0eBCsiXHxveBpBcATDI721J6q3jrqh/ZMM8CeChXmjLEDHNE3H3MvD+lhwf8oHuIiWL5GzNttXTlnB/fYjbBmPkcEQ4wcCGlqe0Uio+vYJZ+EcaSJN031bvqGz/gXmJZ+78hBbqs3paTfvwLbeRBcw0c/OjymLFvocaj5gYpgPMAxixNXeMXtAixrCtvt7Twu35xvHayYYq7924wbSTZMLsBvMXAJylN3eQXsDQxcQ9Sf1JTjvrFytkFDfMMAR3M0CkUmzjIRLvBOG7p6pN+wcoRExgEPJ/U1AN+8YSdHDPfAWE7GK+TbCQ+BvgRInotGVH2+AWqREzgODbap/vUC34xQ/HEQWbeDaZTJMfNj8B4dCmIEfMTST38XnXEjImXARqo9VaC6HK6/oYNf3TfYfklNmcrQ3Gzixnv1/LwA7joLAtsnu5uueqXVLae5g9/MJ5YT8yiao9amtrmF6jMGfvsunPTlpkdzUm/WDk72UhccnWCKBdChjn29WtikYnaKvXxsyD5Kykfn/mkbNPjiX71n4WSEvrAJjaFX33D8oY5VxIxDyT1sK8Wel7GCMetiP9yU0g8ZCT6GHwERMNWRHm4Fpc4QHTEiihPLzRLBVV/2FVSTM9aunKo6rYnaCSeIvAQE72aiih7F0Uqo6Dmtz3Zqvv/axQFMVdtsz0ihG0t5VqlTOblXInWWgC4qhuIul/oEsi2QpJzZVwJMTJbBjzq+7+Sb8VUeXHB61HhSyHj5sg2oKgaLz0i8KjxWso5r6AupcKzbVPp4+kFEUKllkMVAPPUt5dJxRGAq85tRF0hnB1DgflcIBA4XUkfiD6ebXQQ4UGhgDKBazCGyr1BVqVvKzK4G2PALD64wyq3f88/tR3cedNbzahTyDGJ6XOq47M1H3UWO4VC9tlpSZUCUIhZZZIUsGj188Nh6c/0V1N7Vs9UKrLF1v8FJ5yEXcWLNvkAAAAASUVORK5CYII=',
    },
    {
      type: 'rect',
      label: '用户任务',
      icon: 'data:image/png;base64,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',
    },
    {
      type: 'circle',
      text: '结束',
      label: '结束节点',
      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAAXNSR0IArs4c6QAAA+ZJREFUWEfNmF+oFHUUx7/nt3rpIeTO3MQMfLGZNRBS0JesBy1D8soVMyqKnrypO5tCPSRk4A1UsIcCb/tb/FMPgoJRkaXlg5ebgvmSiILi3d/US0Rl985chB5aunNidnZ2Z9bdnb17dZuFfdj9nT+fOef3O+fMj5DSD6WUC3MC6x9VazOCdSaqfOF5IEGTgjHpCdjOTvNWtw8+azDtk4mNRJkhCAyBeXGC45tE+JY9uujkjfOzgewYTCvaTxNjF8CvzMZBXZZOM2HUzRmXO9HvCEwrlvYT094Gg3cBXCHgEjP+ZBJ/+OvE3qNEWMTgNSB6Hoy+mB7jYydvvpMElwg2INVJBl6rGSIa85gPTz9SPo+Xl5fbOVhw/Fc9U/7nJYC3E7CqbgNXnJy5pp1uWzBdKm5QftOxzONJT9tsXZP2dgIfia45ltnSf8sFvah+BOOp0BAjs8K1lt7oBirU0aT9AoG/C38TcGrKMl9vZrMpmF5QH4HwdqjQ7sm6AY1mgokPuLns+4127gHTZGmQQGfrkaKNrmV83w1AKx1N/vIkYeZ6zQfRM42nNQ42zvP0W/YlUJBCBu1wLeNoKwd6YWIEQjA8j5z8spHZwMf3HJ12LOPVqH4MrOKIxL4AClddy1zdzpkm1TgBa32ZbtKtSfVTeFoZ8czEwDSpzhAw1Em0fJm5g9VPKoNHXSu7O3Iw6jHRpXIB9INQ/nf+Q4vvDi9xHmTEHj6sFvbNw28A5jNgu5Zp3gPWXyitFETXggU+51jZTUl7Zq4R8+3rUo0BeDbI0swK13qiUpJqqRwoqjeYcaL6594pyzzYC7ABqd5j4IDvSwCbJy3zmxiYVrT3E3PQDxnDTt78tBdgekFtAyHoJoRtTs78LAamy9IXAG0NuMQm13r8XC/ANPnzIMGr1E1m3uPmsx82gNlfAvzi/wpGtMfNGXGwgULpEBO9220qk6LrzWDd9C7zh0a5WCqB2pBQ2/y6VMMAjnW7+bsFi25+Yt46lc9+FUulP7+LDMaDVNLXrmVsSXLml4skmXCdZ/BB04hJ5Y/cG3y5aFTrld/vk7ftv6sT57Rzp7wII+0HwU6hWsp9frNPn+z7C8ACEP3u5IzHQtl4r5T2WYAHK/TA5ulqTZkzQAsD/VINCeBMsK/piJM3djYHizRxEI05OWP9g4Ly7epF+wKYn6twed6g+9ay6BAZcd0w9iBySu43YPSwAQljj+88nYNiNSypHK3DlKXyZaQGl8bXtxAulS+8IVwqrwjqcCm8VInWsNRdQzUrsNXGbxCzySQMwGMmcVuAJ5hwtacXd/e7A7Syl3gN1SuQRj//ARDQB0UouH96AAAAAElFTkSuQmCC',
    }
  ]
onMounted(() => {
  lf.value = new LogicFlow({
    container: container.value,
    grid: true,
    plugins: [DndPanel]
  })
  lf.value.extension.dndPanel.setPatternItems(patternItems);
  lf.value.render();
})
</script>

3. 配置框选 SelectionSelect:

框选和拖动画布两个功能不能同时支持,stopMoveGraph开启时(禁止拖动画布)则支持框选,反之失效;

3.1 启用、禁用、默认:

默认配置:

new LogicFlow({stopMoveGraph: true});

开启方式:

lf.extension.selectionSelect.openSelectionSelect();

关闭方式:

lf.extension.selectionSelect.closeSelectionSelect();

3.2 配置灵敏区域:

  • 默认需要框选整个节点才选中节点
  • 默认需要框选边的起点、终点才选中边

使用setSelectionSense函数调整:

lf.extension.selectionSelect.setSelectionSense(false, true);
参数默认值描述
isWholeEdgetrue是否要边的起点终点都在选区范围才算选中
isWholeNodetrue是否要节点的全部点都在选区范围才算选中

3.3 结合 DndPanel 控制框选功能:

框选功能和拖拽画布虽然不能同时支持,但可以在使用时灵活控制启用和禁用来切换;

通过在Dndpanel示例的patternItems数据中新增下面的配置,通过点击元素后触发的callback事件启用框选,并监听选中事件后关闭框选来完成功能的切换;

const patternItems = [{
  label: '选区',
  icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAAXNSR0IArs4c6QAAAUdJREFUWEftV9ENwiAQPXAB7Q7GMWzX0TiDdgZjHcd2DNMd1AHsYWgCuTbFlFw/aANfDfCOxzt4PQQE2kSgvCAS881MVGwyxTZF/egHUw3kn9O21P10XCisXsfdRfevr3UqVnA22Pdhm5nvoZh0nK7nTKUOIgBSOhkbyCgxO64wp8TkCtpNKYCSLpwUtaLx+uMLIqYw17tBlKVRLLnXZ0C0incUk2iVNv0abzFCtqnmK0ZS5XuI+/OT2/MCHGJcAlx89DFfBf/ahQlGfcp3gaEzpoTcD/mcv12EdPg7BhsSMZdPTZFKGoP63KhUcglw8fOzi9ahSXNJ7qvM2LjjqouQDn+wt9JVCPqmbnKD5RLg4ud3K7k75uLnfytBSoVfqGxp7fA5/UqSrtLaYDgVbHwlkXfnqJ94sA9e7q3i4qOP+SoYFVuMYj+QKUM2eJOYrwAAAABJRU5ErkJggg==',
  callback: () => {
    lf.value!.extension.selectionSelect.openSelectionSelect();
    lf.value!.once('selection:selected', () => {
      lf.value!.extension.selectionSelect.closeSelectionSelect();
    });
  }
}]

4. 配置分组 Group:

4.1 默认分组&数据格式:

LF渲染下面的graphData数据将得到一个默认的分组区域,它很像一个rect节点;

const graphData = {
  nodes: [
    {
      type: 'group',
      x: 300,
      y: 300
    }
  ]
}

group节点通过特有的children属性可以管理其该包含的子节点;

const graphData = {
  nodes: [
    {
      type: 'group',
      x: 300,
      y: 300,
      children: ["node_2"]
    },
    {
      id: 'node_2',
      type: 'circle',
      x: 300,
      y: 300
    }
  ]
}

4.2 自定义分组:

准备自定义分组模板:

import {GroupNode} from "@logicflow/extension";

class MyGroup extends GroupNode.view { }

class MyGroupModel extends GroupNode.model { }

export default {
    type: "my-group",
    model: MyGroupModel,
    view: MyGroup
};

优先注册&使用:

<script setup lang="ts">
import LogicFlow from "@logicflow/core";
import { Group } from "@logicflow/extension";
import "@logicflow/core/dist/style/index.css";
import "@logicflow/extension/lib/style/index.css";
// 导入模块
import MyGroup from "./MyGroup";

import { onMounted, ref } from "vue";

const container = ref();
const lf = ref<LogicFlow>();
const graphData = {
  nodes: [
    {
      type: 'my-group',
      x: 300,
      y: 300,
      children: ["node_2"]
    },
    {
      id: 'node_2',
      type: 'circle',
      x: 300,
      y: 300
    }
  ]
}
onMounted(() => {
  lf.value = new LogicFlow({
    container: container.value,
    grid: true,
    plugins: [Group],
  })
  // 注册自定义分组
  lf.value.register(MyGroup);
  lf.value.render(graphData);
})
</script>

自定义样式,重写getNodeStyle函数:

getNodeStyle() {
    const style = super.getNodeStyle();
    style.stroke = "#AEAFAE";
    style.strokeWidth = 1;
    return style;
}

自定义形状,重写initNodeData函数:

initNodeData(data: any) {
    super.initNodeData(data);
    this.isRestrict = true;
    this.resizable = true;
    this.width = 480;
    this.height = 280;
}

4.3 状态属性设置:

groupModel除节点通用的属性外,还有下面这些状态属性的支持,建议在setAttributes函数配置;

setAttributes(): void {
    this.isRestrict = true;
    this.resizable = true;
    this.foldable = true;
}
名称类型描述
isRestrictboolean是否限制分组子节点拖出分组,默认false
resizableboolean分组是否支持手动调整大小,默认false
foldableboolean分组是否显示展开收起按钮,默认false
widthnumber分组宽度
heightnumber分组高度
foldedWidthnumber分组折叠后的宽度
foldedHeightnumber分组折叠后的高度
isFoldedboolean只读,表示分组是否被折叠。
isGroupboolean只读,永远为true, 用于识别model为group。

4.4 提供的方法:

addChild:

将某个节点设置为分组的子节点。注意,此方法只会添加关系,不会自动将节点移动到分组里面。

const groupModel = lf.getNodeModelById('group_id');
const node = lf.addNode({
  type: 'rect',
  x: groupModel.x,
  y: groupModel.y
});
groupModel.addChild(node.id);

removeChild:

从分组中移除某个子节点。

const groupModel = lf.getNodeModelById('group_id');
groupModel.removeChild('node_id_1');

foldGroup:

收起分组, 参数为true表示收起分组、false表示展开分组

const groupModel = lf.getNodeModelById('group_id');
groupModel.foldGroup(true);

isAllowAppendIn(nodeData):

校验是否允许传入节点添加到此分组中,默认所有的节点都可以。

class MyGroupModel extends GroupNode.model {

    isAllowAppendIn(nodeData: any): boolean {
        // 设置只允许circle节点被添加到此分组中
        return nodeData.type === 'circle'
    }
}

getAddableOutlineStyle:

设置拖动节点到分组上时,分组高亮的提示效果样式。

class MyGroupModel extends GroupNode.model {

    getAddableOutlineStyle() {
        const style = super.getAddableOutlineStyle();
        style.stroke = '#AEAFAE';
        style.strokeDasharray = '3 3';
        return style;
    }
}

如果看完觉得有收获,欢迎点赞、评论、分享支持一下。你的支持和肯定,是我坚持写作的动力~

 类似资料: