实现效果:

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);
};