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);
    };
相关推荐
栀秋66637 分钟前
就地编辑功能开发指南:从代码到体验的优雅蜕变
前端·javascript·代码规范
国服第二切图仔38 分钟前
Electron for 鸿蒙PC项目实战案例 - 连连看小游戏
前端·javascript·electron·鸿蒙pc
社恐的下水道蟑螂1 小时前
深度探索 JavaScript 的 OOP 编程之道:从基础到进阶
前端·javascript·架构
1_2_3_1 小时前
前端模块联邦介绍
前端
申阳1 小时前
Day 19:02. 基于 SpringBoot4 开发后台管理系统-项目初始化
前端·后端·程序员
学习路上_write1 小时前
FREERTOS_任务通知——使用
java·前端·javascript
Y淑滢潇潇1 小时前
RHCE Day 7 SHELL概述和基本功能
linux·前端·rhce
www_stdio1 小时前
深入理解 Promise 与 JavaScript 原型链:从基础到实践
前端·javascript·promise