LogicFlow 流程图框架

Siona

LogicFlow 流程图框架

LogicFlow 官网open in new window

文档:https://site.logic-flow.cn/ 仓库地址:https://github.com/didi/LogicFlow 关于 issue:https://github.com/didi/LogicFlow/issues 相关文章:https://site.logic-flow.cn/docs/#/zh/article/article01 CodeSandbox 示例:https://codesandbox.io/p/sandbox/logicflow-base26-ztpvtv

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

LogicFlow 分为:

  • core 包 - 核心包
  • extension 包 - 插件包(不使用插件时不需要引入)
  • engine 包 - 执行引擎

1. 安装依赖

# npm
npm install @logicflow/core --save
npm install @logicflow/extension --save

# yarn
yarn add @logicflow/core
yarn add @logicflow/extension

2. 初始化画布

<template>
    <div class="container" ref="container"></div>
</template>

<script setup lang="ts">
    import LogicFlow from "@logicflow/core";
    import "@logicflow/core/dist/style/index.css";
    import "@logicflow/extension/lib/style/index.css";
    import {onMounted, ref} from "vue";
    import {Menu, MiniMap, SelectionSelect} from "@logicflow/extension";
    import UserTask from "@/node/UserTaskNode.ts"

    const container = ref(null);

    // 图数据
    const graphData = {
        nodes: [
            {
                id: "node_id_1",
                type: "UserTask",
                x: 100,
                y: 100,
                text: {x: 100, y: 100, value: "节点1"},
                properties: {
                    isPass: true
                },
            },
            {
                id: "node_id_2",
                type: "circle",
                x: 200,
                y: 300,
                text: {x: 200, y: 300, value: "节点2"},
                properties: {
                    isPass: false
                },
            },
        ],
        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: {},
            },
        ],
    };

    // 渲染画布
    let lf;
    onMounted(() => {
        lf = new LogicFlow({
            // container: document.querySelector('.logic-container'),
            container: container.value,
            autoExpand: false,
            hoverOutline: false,
            edgeSelectedOutline: false,
            stopScrollGraph: true,
            stopZoomGraph: true,
            stopMoveGraph: true,
            adjustEdgeStartAndEnd: true,
            multipleSelectKey: "meta",
            plugins: [MiniMap, SelectionSelect, Menu],
            keyboard: {
                enabled: true,
                shortcuts: [
                    {
                        keys: "backspace",
                        callback: () => {
                            const {edges} = lf.getSelectElements();
                            // 默认只支持删除选中连线
                            // bug :选中连线,可以删除,但是连线没有点击选中效果
                            if (edges && edges.length === 1) {
                                lf.deleteEdge(edges[0].id);
                            }
                        },
                    },
                ],
            },
            grid: true
        });

        // 注册自定义节点、边
        lf.register(UserTask)

        // 将图数据渲染到画布上
        lf.render(graphData);

    })
</script>

<style scoped>
    .container {
        padding: 1vh 1vw;
        width: 98vw;
        height: 98vh;
    }
</style>

4. 自定义节点

(1)定义节点

// src/node/UserTaskNode.ts
// UserTaskNode 用户任务节点

import {RectNode, RectNodeModel} from "@logicflow/core";

class UserTaskModel extends RectNodeModel {

    // 自定义节点的形状属性
    initNodeData(data) {
        super.initNodeData(data);
        this.width = 100;
        this.height = 80;
        this.radius = 5;
    }

    // 自定义节点的样式属性
    getNodeStyle() {
        const style = super.getNodeStyle();
        // 重新定义 UserTask 边框为蓝色 stroke: blue。
        style.stroke = "blue";
        style.strokeDasharray = "3 3";
        return style;
    }
}

class UserTaskView extends RectNode {
}

export default {
    type: "UserTask",
    view: UserTaskView,
    model: UserTaskModel,
};

从上面的代码,可以看到,在自定义一个节点的时候,我们需要定义节点的 modelview

这是因为由于 LogicFlow 基于 MVVM 模式,所有自定义节点和连线的时候,我们需要自定义 view 和 model。

大多数情况下,需要通过重写定义 model 上获取样式相关的方法 和 重写 view 上的 getShape 来定义更复杂的节点外观。

注意如果不在model中设置形状属性,而是直接在view中直接定义生成图形的宽高这种形状属性,会出现锚点位置、 outline 大小不正确的情况。同时,连线的位置也可能会出现错乱。

(2)注册节点

import UserTask from "@/node/UserTaskNode.ts"

// 注册自定义节点、边
// 只注册一个
lf.register(UserTask)

// 批量注册
lf.batchRegister([UserTask, start, end])

(3)基于 properties 属性自定义节点样式

在实际业务中,存在这样的情况,例如在审批场景中,自定义的审批节点存在 3 种状态:

一种是流程还没有走到这个节点的默认状态,一种是流程审批通过状态,一种是审批不通过的驳回状态。

在外观上我们需要对不同的状态显示不同的颜色。LogicFlow 的图数据中提到,不论是节点还是边, LogicFlow 都保留了 properties 字段,用于给开发者存放自己的业务属性。

示例如下,properties 的 status 属性就是一个自定义的业务属性,开发者在自定义节点样式的时候, 可以基于 properties 中的属性来控制节点显示不同的样式。

// src/node/UserTaskNode.ts
class UserTaskModel extends RectNodeModel {
    initNodeData(data) {
        super.initNodeData(data);
        this.width = 100;
        this.height = 80;
        this.radius = 5;
    }

    getNodeStyle() {
        const style = super.getNodeStyle();
        const properties = this.properties;
        if (properties.status === "pass") {
            style.stroke = "green";
        } else if (properties.status === "reject") {
            style.stroke = "red";
        } else {
            style.stroke = "rgb(24, 125, 255)";
        }
        return style;
    }
}

提示如果不了解为什么 this.properties 打印出来是一个 Proxy 对象, 无法看到属性。 请查看 issue https://github.com/didi/LogicFlow/issues/530open in new window

效果图:

自定义节点样式.png
自定义节点样式.png

(4)进阶:自定义节点的 view【定义更加复杂的节点】

LogicFlow 在自定义节点的 model 时,可以定义节点的基础形状、样式等属性。 但是当开发者需要一个更加复杂的节点时,可以使用 LogicFlow 提供的自定义节点 view 的方式。

5. 自定义边

6. 保存数据

<template>
    <button @click="saveGraphData">保存数据</button>
    <div class="container" ref="container"></div>
</template>
<script setup lang="ts">
    // 保存数据
    const saveGraphData = () => {
        const graphData = lf.getGraphData();
        console.log("保存数据:", graphData)
    };
</script>

7. 事件 Event

当我们使用鼠标或其它方式与画布交互时,会触发的对应的事件。 通过监听这些事件,可以获取其在触发时所产生的数据,根据这些数据来实现需要的功能。详细可监听事件见 事件 APIopen in new window

8. 主题 Theme

9. 拖拽面板 DndPanel

Last Updated 5/23/2024, 9:54:44 AM