react里ag-grid实现树形数据展示

实现效果:

https://ag-grid.com/example-hr/ ag-grid官网demo效果

可以参考官网给的代码react、vue、angular版本都有:

点击demo上面的See on Github即可
https://github.com/ag-grid/ag-grid-demos/blob/main/hr/react/src/HRExample.tsx

实现思路:

通过分析demo的代码,发现核心点是需要把数据转成成一维数组,也就是把数据拉平。

还需要给数组加上一个path关系的属性,这个属性是提供给getDataPath使用的。

需要设置treeData:true。

实现步骤及代码:

1.处理数据,给其加上path路径字段:

我的数据返回的是传统的tree数据,children就是其子项。我需要给每个项加一个字段hierarchy表示其路径数组。

比如:一级就是其本身,二级就是其父级和自己...以此类推。

需要注意的是其路径项的值不能有重复的,需要是唯一的!

比如不可以是:['华夏资本', '战略客户部', '张三','张三']

bash 复制代码
    let data = [
        {
            itemName: "华夏资本",
            dataId: "0",
            children: [
                {
                    itemName: "战略客户部",
                    dataId: "2001",
                    children: [
                        {
                            itemName: "张三",
                            dataId: "10138",
                            children: []
                        },
                        {
                            itemName: "李四",
                            dataId: "10139",
                            children: []
                        }
                    ]
                }
            ]
        },
        {
            itemName: "华夏基金",
            dataId: "1",
            children: [
                {
                    itemName: "销售部门",
                    dataId: "1001",
                    children: [
                        {
                            itemName: "kk",
                            dataId: "10139",
                            children: []
                        },
                        {
                            itemName: "mm",
                            dataId: "10140",
                            children: []
                        }
                    ]
                }
            ]
        }
    ]

封装一个函数处理:

javascript 复制代码
 /**
     * 为树形数据的每个节点添加 hierarchy 字段
     * @param {Array} treeData - 树形数据数组
     * @param {string} nameField - 节点名称字段名,默认为 'itemName'
     * @param {string} childrenField - 子节点字段名,默认为 'children'
     * @param {string} hierarchyField - 层级路径字段名,默认为 'hierarchy'
     * @returns {Array} 添加了 hierarchy 字段的新树形数据
     */
    function addHierarchyToTreeData(treeData, {
        nameField = 'itemName',
        childrenField = 'children',
        hierarchyField = 'hierarchy'
    } = {}) {

        // 深度复制原始数据,避免修改原数据
        const copyTreeData = JSON.parse(JSON.stringify(treeData));

        /**
         * 递归处理树节点
         * @param {Array} nodes - 当前层级的节点数组
         * @param {Array} parentHierarchy - 父节点的层级路径
         */
        function processNodes(nodes, parentHierarchy = []) {
            return nodes.map(node => {
                // 创建当前节点的层级路径
                const currentHierarchy = [...parentHierarchy, node[nameField]];

                // 为当前节点添加 hierarchy 字段
                node[hierarchyField] = currentHierarchy;

                // 如果有子节点,递归处理
                if (node[childrenField] && node[childrenField].length > 0) {
                    node[childrenField] = processNodes(node[childrenField], currentHierarchy);
                }

                return node;
            });
        }

        return processNodes(copyTreeData);
    }
 // 使用方法1:递归版本(返回新对象)
    const dataWithHierarchy = addHierarchyToTreeData(data);
    console.log(dataWithHierarchy);

使用addHierarchyToTreeData,给其增加hierarchy属性,其值就是path路径即层级关系路径。

2.把tree数据拉平为一维数组(扁平化):

因为ag-grid官网demo就是传入的拉平后的数据,所以我们也需要实现把tree数据拉平。

javascript 复制代码
 /**
   * 将树形数据转换为一维数组(扁平化)
   * @param {Array} treeData - 树形数据数组
   * @param {string} childrenField - 子节点字段名,默认为 'children'
   * @returns {Array} 扁平化的一维数组
   */
    function flattenTreeData(treeData, childrenField = 'children') {
        const result = [];

        /**
         * 递归处理节点
         * @param {Object} node - 当前节点
         * @param {Array} parentHierarchy - 父级层级路径(可选,用于验证)
         */
        function processNode(node) {
            // 创建当前节点的副本,不包含子节点
            const { [childrenField]: children, ...nodeWithoutChildren } = node;

            // 将节点添加到结果数组
            result.push(nodeWithoutChildren);

            // 递归处理子节点
            if (children && children.length > 0) {
                for (const child of children) {
                    processNode(child);
                }
            }
        }

        // 处理每个根节点
        for (const rootNode of treeData) {
            processNode(rootNode);
        }

        return result;
    }
    //先进行层级添加
    const dataWithHierarchy = addHierarchyToTreeData(data);
    console.log(dataWithHierarchy);
    //再进行扁平化
    const flatData=flattenTreeData(dataWithHierarchy)
    console.log(flatData);

扁平化最终数据格式如下:

javascript 复制代码
[
    {
        "itemName": "华夏资本",
        "dataId": "0",
        "hierarchy": [
            "华夏资本"
        ]
    },
    {
        "itemName": "战略客户部",
        "dataId": "2001",
        "hierarchy": [
            "华夏资本",
            "战略客户部"
        ]
    },
    {
        "itemName": "张三",
        "dataId": "10138",
        "hierarchy": [
            "华夏资本",
            "战略客户部",
            "张三"
        ]
    },
    {
        "itemName": "李四",
        "dataId": "10139",
        "hierarchy": [
            "华夏资本",
            "战略客户部",
            "李四"
        ]
    },
    {
        "itemName": "华夏基金",
        "dataId": "1",
        "hierarchy": [
            "华夏基金"
        ]
    },
    {
        "itemName": "销售部门",
        "dataId": "1001",
        "hierarchy": [
            "华夏基金",
            "销售部门"
        ]
    },
    {
        "itemName": "kk",
        "dataId": "10139",
        "hierarchy": [
            "华夏基金",
            "销售部门",
            "kk"
        ]
    },
    {
        "itemName": "mm",
        "dataId": "10140",
        "hierarchy": [
            "华夏基金",
            "销售部门",
            "mm"
        ]
    }
]

3.完整代码:

javascript 复制代码
import React, { useEffect, useState } from 'react';
import { AgGridReact } from 'ag-grid-react';
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";



let data = [
    {
        itemName: "华夏资本",
        dataId: "0",
        children: [
            {
                itemName: "战略客户部",
                dataId: "2001",
                children: [
                    {
                        itemName: "张三",
                        dataId: "10138",
                        children: []
                    },
                    {
                        itemName: "李四",
                        dataId: "10139",
                        children: []
                    }
                ]
            }
        ]
    },
    {
        itemName: "华夏基金",
        dataId: "1",
        children: [
            {
                itemName: "销售部门",
                dataId: "1001",
                children: [
                    {
                        itemName: "kk",
                        dataId: "10139",
                        children: []
                    },
                    {
                        itemName: "mm",
                        dataId: "10140",
                        children: []
                    }
                ]
            }
        ]
    }
]



function KpiManagement() {
    const [tabData, setTabData] = useState([])
    /**
     * 为树形数据的每个节点添加 hierarchy 字段
     * @param {Array} treeData - 树形数据数组
     * @param {string} nameField - 节点名称字段名,默认为 'itemName'
     * @param {string} childrenField - 子节点字段名,默认为 'children'
     * @param {string} hierarchyField - 层级路径字段名,默认为 'hierarchy'
     * @returns {Array} 添加了 hierarchy 字段的新树形数据
     */
    const addHierarchyToTreeData = (treeData, {
        nameField = 'itemName',
        childrenField = 'children',
        hierarchyField = 'hierarchy'
    } = {}) => {
        // 深度复制原始数据,避免修改原数据
        const copyTreeData = JSON.parse(JSON.stringify(treeData));

        /**
         * 递归处理树节点
         * @param {Array} nodes - 当前层级的节点数组
         * @param {Array} parentHierarchy - 父节点的层级路径
         */
        function processNodes(nodes, parentHierarchy = []) {
            return nodes.map(node => {
                // 创建当前节点的层级路径
                const currentHierarchy = [...parentHierarchy, node[nameField]];

                // 为当前节点添加 hierarchy 字段
                node[hierarchyField] = currentHierarchy;

                // 如果有子节点,递归处理
                if (node[childrenField] && node[childrenField].length > 0) {
                    node[childrenField] = processNodes(node[childrenField], currentHierarchy);
                }

                return node;
            });
        }

        return processNodes(copyTreeData);
    }
    /**
  * 将树形数据转换为一维数组(扁平化)
  * @param {Array} treeData - 树形数据数组
  * @param {string} childrenField - 子节点字段名,默认为 'children'
  * @returns {Array} 扁平化的一维数组
  */
    const flattenTreeData = (treeData, childrenField = 'children') => {
        const result = [];

        /**
         * 递归处理节点
         * @param {Object} node - 当前节点
         * @param {Array} parentHierarchy - 父级层级路径(可选,用于验证)
         */
        function processNode(node) {
            // 创建当前节点的副本,不包含子节点
            const { [childrenField]: children, ...nodeWithoutChildren } = node;

            // 将节点添加到结果数组
            result.push(nodeWithoutChildren);

            // 递归处理子节点
            if (children && children.length > 0) {
                for (const child of children) {
                    processNode(child);
                }
            }
        }

        // 处理每个根节点
        for (const rootNode of treeData) {
            processNode(rootNode);
        }

        return result;
    }
    useEffect(() => {
        const newData = addHierarchyToTreeData(data);
        const flatData = flattenTreeData(newData);
        setTabData(flatData);
    }, [])


    const gridOptions1 = {
        // 1. 启用树形数据模式
        treeData: true,

        // 2. 定义如何从数据中获取层级路径(扁平数组格式必需)
        getDataPath: (data) => {
            // 假设你的数据中有一个 'hierarchy' 字段是路径数组
            return data.hierarchy;
        },

        // 3. 定义列
        columnDefs: [
            // 分组列会自动生成,用于显示树形结构和展开/折叠图标
            { headerName: '员工姓名', field: 'itemName' },
        ],

        // 5. 可选:自定义自动生成的分组列(通常是最左侧那列)
        autoGroupColumnDef: {
            headerName: '组织架构', // 更改分组列标题
            width: 300,
            cellRendererParams: {
                suppressCount: true, // 是否在分组旁显示子节点数量
            }
        },

        // 6. 可选:设置默认展开层级
        groupDefaultExpanded: 1, // 1默认展开第一层节点 -1表示默认展开所有
    };
    return (
        <div className="ag-theme-alpine" style={{ height: 400, width: 600 }}>
            <AgGridReact
                gridOptions={gridOptions1}
                rowData={tabData}
            />
        </div>
    );
}

export default KpiManagement;

优化点可以把gridOptions、columnDefs用useMemo优化一下,避免多次创建。值得注意的是columnDefs如果不用useMemo可能会出现下面的bug:ag-grid 列搜索时自动失去焦点

javascript 复制代码
const gridOptions1 = useMemo(() => ({
        // 1. 启用树形数据模式
        treeData: true,

        // 2. 定义如何从数据中获取层级路径(扁平数组格式必需)
        getDataPath: (data) => {
            // 假设你的数据中有一个 'hierarchy' 字段是路径数组
            return data.hierarchy;
        },

        // 3. 定义列
        columnDefs: [
            // 分组列会自动生成,用于显示树形结构和展开/折叠图标
            { headerName: '员工姓名', field: 'itemName' },
        ],

        // 5. 可选:自定义自动生成的分组列(通常是最左侧那列)
        autoGroupColumnDef: {
            headerName: '组织架构', // 更改分组列标题
            width: 300,
            cellRendererParams: {
                suppressCount: true, // 是否在分组旁显示子节点数量
            }
        },

        // 6. 可选:设置默认展开层级
        groupDefaultExpanded: 1, // 1默认展开第一层节点 -1表示默认展开所有
    }), []); // 空依赖数组,表示只在组件挂载时创建一次

如果你想默认展开所有节点只需要把groupDefaultExpanded设置为-1:

javascript 复制代码
 groupDefaultExpanded: -1, // 0表示不展开 1默认展开第一层节点 -1表示默认展开所有 

groupDefaultExpanded也可以是任何层级,比如2就是展开2级节点。
groupDefaultExpanded api文档

也可以设置isGroupOpenByDefault 允许分组默认展开,可以设置需要展开的项:

比如:只想让"战略客户部"展开

javascript 复制代码
const isGroupOpenByDefault = (params) => {
        const key = params?.rowNode?.key;
        const destPath = ['战略客户部'];
        return destPath.includes(key);
    };
相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax