React项目实战:用AntV G6打造可折叠组织架构图(附完整代码)

React项目实战:用AntV G6打造可折叠组织架构图(附完整代码)

在构建现代企业级后台管理系统时,数据可视化往往是提升管理效率和决策直观性的关键一环。想象一下,当你需要向管理层清晰展示一个拥有数百个节点、层级复杂的组织架构,或者梳理一个大型项目的任务依赖关系时,静态的列表或树形控件就显得力不从心了。这时,一个支持动态交互、可自由缩放与折叠的可视化图表,不仅能清晰呈现全局结构,还能让用户通过点击探索细节,体验上有着质的飞跃。

这正是AntV G6这类专业图可视化引擎的用武之地。它并非一个简单的图表库,而是一个为复杂关系网络和层级结构量身定制的解决方案。结合React的组件化思想,我们可以将复杂的图可视化逻辑封装成清晰、可复用的业务组件,无缝集成到现有的技术栈中。本文将带你超越基础的“Hello World”示例,深入探讨如何在React项目中,利用G6实现一个功能完备、性能优良且具备良好交互体验的可折叠组织架构图。我们会从项目搭建开始,逐步深入到自定义节点、交互逻辑、状态管理以及性能优化等实战层面,并提供可直接用于生产环境的代码模块。

1. 项目环境搭建与核心依赖

在开始编码之前,确保你的开发环境已经就绪。我们假设你已有一个基于Create React App或Vite创建的React项目。如果没有,可以使用以下命令快速初始化一个:

# 使用 Vite (推荐,速度更快)
npm create vite@latest my-g6-project -- --template react
cd my-g6-project
npm install

# 或使用 Create React App
npx create-react-app my-g6-project
cd my-g6-project

接下来,安装本文所需的核心依赖。除了AntV G6本身,我们还会引入一些辅助库来提升开发体验。

npm install @antv/g6
npm install --save-dev @types/antv__g6 # 如果你使用TypeScript

注意:AntV G6的API和功能迭代较快,建议在安装前查阅其官方文档以确认最新版本和兼容性。本文示例基于G6 4.x版本。

一个清晰的项目结构有助于长期维护。建议为G6相关的组件和工具创建独立的目录:

src/
├── components/
│   ├── OrgChart/           # 组织架构图主组件
│   │   ├── index.tsx
│   │   ├── OrgChart.tsx
│   │   ├── customNode.ts   # 自定义节点定义
│   │   └── types.ts        # TypeScript类型定义
│   └── ...
├── hooks/                  # 自定义Hook,如图表实例管理
│   └── useG6Graph.ts
├── utils/                 # 数据处理工具函数
│   └── dataTransform.ts
└── App.tsx

2. 数据建模与G6图实例初始化

可视化始于数据。一个典型的组织架构数据是嵌套的树形结构。我们首先定义其TypeScript接口,并准备一份模拟数据。

1. 定义数据结构types.ts 中,我们明确节点的结构:

export interface OrgNode {
  id: string;          // 唯一标识
  label: string;       // 显示名称
  name?: string;       // 真实姓名
  title?: string;      // 职位
  department?: string; // 部门
  avatar?: string;     // 头像URL
  children?: OrgNode[]; // 子节点
  collapsed?: boolean;  // 是否已折叠(用于内部状态管理)
}

export type OrgChartData = OrgNode;

2. 准备模拟数据 在组件内部或单独的数据文件中,构造一份具有多层嵌套的模拟数据:

const mockData: OrgNode = {
  id: 'root',
  label: '首席执行官 (CEO)',
  name: '张伟',
  title: 'CEO',
  children: [
    {
      id: 'tech',
      label: '技术部',
      department: 'CTO Office',
      children: [
        { id: 'fe', label: '前端组', name: '李静', title: '前端负责人' },
        { 
          id: 'be', 
          label: '后端组', 
          name: '王强', 
          title: '后端架构师',
          children: [
            { id: 'be-java', label: 'Java服务', name: '赵明' },
            { id: 'be-go', label: 'Go服务', name: '孙丽' }
          ]
        },
        { id: 'qa', label: '测试组', name: '周涛', title: 'QA经理' }
      ]
    },
    {
      id: 'product',
      label: '产品部',
      name: '陈曦',
      title: '产品总监',
      children: [
        { id: 'pm', label: '产品经理组', name: '吴浩' },
        { id: 'design', label: '设计组', name: '郑爽' }
      ]
    }
    // ... 更多部门
  ]
};

3. 初始化G6图实例 这是最核心的一步。我们在React组件的 useEffect 钩子中创建并配置G6实例。关键点在于理解各项配置的作用。

import React, { useEffect, useRef } from 'react';
import G6 from '@antv/g6';

const OrgChart = ({ data }) => {
  const containerRef = useRef(null);
  const graphRef = useRef(null);

  useEffect(() => {
    if (!containerRef.current || graphRef.current) return;

    const container = containerRef.current;
    const width = container.scrollWidth;
    const height = container.scrollHeight || 600;

    // 1. 注册自定义节点和边(下一节详述)
    registerCustomNode();
    registerCustomEdge();

    // 2. 创建图实例
    const graph = new G6.TreeGraph({
      container: container,
      width,
      height,
      linkCenter: true, // 连线对准节点中心
      modes: {
        default: [
          'drag-canvas',   // 拖拽画布
          'zoom-canvas',   // 缩放画布
          'drag-node',     // 拖拽节点
          {
            type: 'tooltip', // 鼠标悬停提示
            formatText: (model) => {
              return `姓名: ${model.name || 'N/A'}\n职位: ${model.title || model.label}`;
            },
          }
        ],
      },
      defaultNode: {
        type: 'org-node', // 使用我们即将注册的自定义节点类型
        size: [180, 80],  // 节点默认大小
      },
      defaultEdge: {
        type: 'org-edge', // 自定义边类型
        style: {
          stroke: '#A3B1BF',
          lineWidth: 1.5,
        },
      },
      layout: {
        type: 'compactBox', // 紧凑树布局
        direction: 'TB',     // 从上到下 (Top to Bottom)
        getHeight: () => 40,
        getWidth: () => 16,
        getVGap: () => 50,   // 垂直间距
        getHGap: () => 100,  // 水平间距
        radial: false,       // 非辐射状
      },
      fitView: true,         // 初始化时自适应视图
      animate: true,         // 启用布局动画
    });

    graphRef.current = graph;

    // 3. 读取数据并渲染
    graph.data(data
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值