关系图只能这么简单了!

775 阅读2分钟

如果还有更简单的实现,欢迎指教。

概览

实现

step1: 依赖安装

npm i @ant-design/graphs
// OR
yarn add @ant-desing/graphs

step2: 组件实现,为了不占用篇幅,我们仅用三条数据

import React from 'react';
import { DecompositionTreeGraph } from '@ant-design/graphs';

export const TreeGraph = () => {
  const data = {
    id: 'A0',
    value: {
      title: '订单金额',
      items: [
        {
          text: '3031万',
        },
      ],
    },
    children: [
      {
        id: 'A1',
        value: {
          title: '华南',
          items: [
            {
              text: '1152万',
            },
            {
              text: '占比',
              value: '30%',
            },
          ],
        },
      },
      {
        id: 'A2',
        value: {
          title: '华北',
          items: [
            {
              text: '595万',
            },
            {
              text: '占比',
              value: '30%',
              icon: 'https://gw.alipayobjects.com/zos/antfincdn/iFh9X011qd/7797962c-04b6-4d67-9143-e9d05f9778bf.png',
            },
          ],
        },
      },
    ],
  };

  const config = {
    data,
  };

  return <DecompositionTreeGraph {...config} />;
};

功能介绍

上面案例虽然简答,但也难满足业务需求,我们需要润色并丰富相关功能,由于功能配置较多,以下介绍都只是提及,详情参看官网

交互

marker:给节点加上一些 marker 用于展开收起交互

  const config = {
    data,
    markerCfg: (cfg) => {
      const { children } = cfg;
      return {
        show: true,
        collapsed: !children?.length,
      };
    },
  };

behaviors:图表默认会内置画布缩放、节点拖拽等交互,也可以自己指定

  const config = {
    data,
    markerCfg: (cfg) => {
      const { children } = cfg;
      return {
        show: true,
        collapsed: !children?.length,
      };
    },
    behaviors: ['drag-canvas', 'zoom-canvas', 'drag-node'],
  };

Jul-26-2022 20-55-20.gif

布局调整

默认是水平向右,垂直向下的情况请也比较多,调整布局会涉及出桩和入桩位置的调整。

  const config = {
    data,
    layout: {
      direction: 'TB',
      getVGap: () => {
        return 40;
      },
    },
    nodeCfg: {
      anchorPoints:[[0.5, 0],[0.5,1]]
    },
    edgeCfg: {
      type: 'polyline',
    },   
  };

image.png

事件

可以通过 graph.on 与 graph.off 进行绑定/解绑监听函数,支持各种事件。

  const config = {
    data,
    onReady: (graph)=>{
     graph.on('node:click', (e)=>{
       console.log(e.item.getModel())
     })
    },
  };

异步请求

一次展示所有节点不切实际,正常情况下都会异步加载,我们看下 DecompositionTreeGraph 是如何实现的,点击 + 号即可看到效果。

  const fetchData = () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(
          [1, 2].map(() => ({
            id: 'A2' + Math.random().toString(),
            value: {
              title: '异步节点' + Math.random().toString(),
              items: [
                {
                  text: '595万',
                },
                {
                  text: '占比',
                  value: '50%',
                },
              ],
            },
          })),
        );
      }, 1000);
    });
  };

  const getChildren = async () => {
    return await fetchData();
  };

  const config = {
    data,
    nodeCfg: {
      getChildren,
    },
    markerCfg: (cfg) => {
      const { children } = cfg;
      return {
        show: true,
        collapsed: !children?.length,
      };
    },
  };

Jul-27-2022 19-07-43.gif

自定义节点

这么丑的节点,你能忍?反正我是不能忍!当然,节点样式和边样式完全可以自定义配置,也可 通过回调的方式个性化设置。

 const config = {
   data,
   nodeCfg: {
      title: {
        containerStyle: {
          fill: 'transparent',
        },
        style: {
          fill: '#000',
        },
      },
      items: {
        containerStyle: {
          fill: '#fff',
        },
        style: (cfg, group, type) => {
          const styles = {
            icon: {
              width: 10,
              height: 10,
            },
            value: {
              fill: '#52c41a',
            },
            text: {
              fill: '#aaa',
            },
          };
          return styles[type];
        },
      },
      style: {
        stroke: '#40a9ff',
        strokeWidth: 1
      },
    },
    edgeCfg: {
      style: (item, graph) => {
        /**
         * graph.findById(item.target).getModel()
         * item.source: 获取 source 数据
         * item.target: 获取 target 数据
         */
        // console.log(graph.findById(item.source).getModel());
        return {
          stroke: '#40a9ff',
          lineWidth: Math.random() * 10 + 1,
          strokeOpacity: 0.5,
        };
      },
      edgeStateStyles: false,
    },
  };

好像还是很丑!算了,总之,支持各种个性化配置!

image.png

自定义节点

业务需求各异,自定义节点也显的比较重要,这里需要一些 canvas 常识,详见

image.png

总结

缺点很多,但也支持了无数项目,欢迎大家来一起修 issues!