美文网首页代码改变世界
React + antv/g6实现流程图功能

React + antv/g6实现流程图功能

作者: 燕自浩 | 来源:发表于2023-08-16 10:59 被阅读0次

    前言:由于首次接触到antv的g6流程图这个功能,比较好奇然后就浅浅的用一下,其一给需要用到的小伙伴一个简单的案例,其二特此记录。
    官方大大地址
    第一步:按照官方大大的示例乖乖下载即可,官方提供了两种引入方式,如下

    1. 在项目中使用 NPM 包引入(当前pnpm和yarn也可以)

    npm install --save @antv/g6
    

    Step 1: 使用命令行在项目目录下执行以下命令:

    npm install --save @antv/g6
    

    Step 2: 在需要用的 G6 的 JS 文件中导入:

    import G6 from '@antv/g6';
    

    2. 在 HTML 中使用 CDN 引入

    // version <= 3.2
    <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-{$version}/build/g6.js"></script>
    
    // version >= 3.3
    <script src="https://gw.alipayobjects.com/os/lib/antv/g6/{$version}/dist/g6.min.js"></script>
    
    // version >= 4.0
    <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script>
    

    ⚠️ 注意:

    const data = {
      // 点集
      nodes: [
        {
          id: 'node1', // String,该节点存在则必须,节点的唯一标识
          x: 100, // Number,可选,节点位置的 x 值
          y: 200, // Number,可选,节点位置的 y 值
        },
        {
          id: 'node2', // String,该节点存在则必须,节点的唯一标识
          x: 300, // Number,可选,节点位置的 x 值
          y: 200, // Number,可选,节点位置的 y 值
        },
      ],
      // 边集
      edges: [
        {
          source: 'node1', // String,必须,起始点 id
          target: 'node2', // String,必须,目标点 id
        },
      ],
    };
    

    从官方提供的数据之中可以看出每一个节点都需要一个x,y,左边来确定节点在画布上的具体位置,显而易见这个x,y坐标对于后端开发人员根本不需要维护,那我们前端如何计算出x,y坐标而去确定位置呢,那我们肯定期望有一种工具可以自动的去计算每个节点的x,y坐标自动在画布上画出来,答案肯定是有的,接下来介绍我们的关键技术点Plugins,它就是帮助我们自动的计算x,y然后自动的画出来,然后拿出我的代码案例。
    第一步:引入 g6的同时还用引入Plugins

    import G6 from '@antv/g6';
    import Plugins from '@antv/g6-plugins';
    

    第二步:处理从后端拿到的数据修改成我们可以直接使用的数据(仅供参考,这一步需要根据后端返回的数据自行调整)

    useEffect(() => {
        // 存储所有节点关系
        const nodes = [];
        // 存储所有节点间的指向关系
        const edges = [];
        tasks?.forEach(item => {
          nodes.push({
            shape: 'square',
            label: item.taskName,
            color: 'black',
            id: item.id,
          });
        });
        if (tasks?.length) {
          // 添加开始节点
          nodes.unshift({
            shape: 'circle',
            label: '开始', // 节点名称
            color: 'red', // 设置节点背景色
            id: 'start', // 本节点id
          });
          // 添加结束节点
          nodes.push({
            shape: 'circle',
            label: '结束', // 节点名称
            color: 'red', // 设置节点背景色
            id: 'end', // 本节点id
          });
          // 将开始节点和节点连接起来
          heads?.forEach(item => {
            edges.push({
              shape: 'arrow',
              source: 'start',
              target: item,
              id: `start-${item}`,
            });
          });
          // 将结束节点和节点连接起来
          tails?.forEach(item => {
            edges.push({
              shape: 'arrow',
              source: item,
              target: 'end',
              id: `${item}-end`,
            });
          });
        }
        // 将中间节点连接起来
        relations?.forEach(item => {
          edges.push({
            shape: 'arrow',
            source: item.preProcessTaskDefinitionId,
            target: item.postProcessTaskDefinitionId,
            id: `${item.id}`,
          });
        });
    

    第三步:配置G6画布

        const dagre = new Plugins['layout.dagre']({
          rankdir: 'TB',
          nodesep: 50,
          ranksep: 50,
        });
        const net = new G6.Net({
          id: 'c1', // 容器ID
          fitView: 'cc',
          mode: 'none',
          plugins: [dagre],
        });
    // 重中之重配置plugins就是让x,y坐标自动生成
    

    第四步:载入数据

    net.source(nodes, edges);
    

    第五步:渲染关系图

    net.render();
    

    优化点:加入如下代码是为了每次绘制前先清除上一次的

    flowRef.current.innerHTML = '';
    

    完整代码:

    import React, { useEffect, useRef, useMemo } from 'react';
    import request from '@/request';
    import { useLocation } from 'react-router-dom';
    import { useRequest } from 'ahooks';
    import qs from 'query-string';
    import G6 from '@antv/g6';
    import Plugins from '@antv/g6-plugins';
    
    const DagProcessHistory = () => {
      const { search } = useLocation();
      const { id, dagBaseInfoReadOnly } = qs.parse(search);
      const flowRef = useRef(null);
    
      // 获取详情数据
      const { data } = useRequest(() => request.viewFlowApi(id), {
        refreshDeps: [id],
      });
    
      const { tasks, relations, heads, tails } = data?.data ?? {};
    
      useEffect(() => {
        // 存储所有节点关系
        const nodes = [];
        // 存储所有节点间的指向关系
        const edges = [];
        tasks?.forEach(item => {
          nodes.push({
            shape: 'square',
            label: item.taskName,
            color: 'black',
            id: item.id,
          });
        });
        if (tasks?.length) {
          // 添加开始节点
          nodes.unshift({
            shape: 'circle',
            label: '开始', // 节点名称
            color: 'red', // 设置节点背景色
            id: 'start', // 本节点id
          });
          // 添加结束节点
          nodes.push({
            shape: 'circle',
            label: '结束', // 节点名称
            color: 'red', // 设置节点背景色
            id: 'end', // 本节点id
          });
          // 将开始节点和节点连接起来
          heads?.forEach(item => {
            edges.push({
              shape: 'arrow',
              source: 'start',
              target: item,
              id: `start-${item}`,
            });
          });
          // 将结束节点和节点连接起来
          tails?.forEach(item => {
            edges.push({
              shape: 'arrow',
              source: item,
              target: 'end',
              id: `${item}-end`,
            });
          });
        }
        // 将中间节点连接起来
        relations?.forEach(item => {
          edges.push({
            shape: 'arrow',
            source: item.preProcessTaskDefinitionId,
            target: item.postProcessTaskDefinitionId,
            id: `${item.id}`,
          });
        });
        flowRef.current.innerHTML = '';
        // eslint-disable-next-line new-cap
        const dagre = new Plugins['layout.dagre']({
          rankdir: 'TB',
          nodesep: 50,
          ranksep: 50,
        });
        // 配置G6画布
        const net = new G6.Net({
          id: 'c1', // 容器ID
          fitView: 'cc',
          mode: 'none',
          plugins: [dagre],
        });
        // 载入数据
        net.source(nodes, edges);
        // 渲染关系图
        net.render();
      }, [tasks, relations]);
    
      return (
        <div style={{ marginTop: '20px' }} id="c1" ref={flowRef} />
      );
    };
    
    export default DagProcessHistory;
    
    

    最终效果图


    图一
    图二

    我们的需求是需要开始节点和结束节点的所以手动将节点中加入了开始节点和结束节点,大家可以根据实际需求自行调整,本篇文章仅为简单demo和记录。

    相关文章

      网友评论

        本文标题:React + antv/g6实现流程图功能

        本文链接:https://www.haomeiwen.com/subject/onpkmdtx.html