You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React实现员工层级展开功能求助:基于Manager ID构建树形列表

解决方案

核心问题分析

你当前的代码无法实现层级渲染,主要原因是没有将扁平的员工数据转换为树形结构,且使用了全局的展开状态,无法支持多级独立展开。以下是完整的修复方案:


步骤1:将扁平数据转换为树形结构

首先需要把API返回的扁平数组转换为层级结构,每个员工节点包含下属列表:

const buildEmployeeTree = (employees) => {
  const employeeMap = {};
  const rootEmployees = [];

  // 先将所有员工存入映射表,方便快速查找
  employees.forEach(emp => {
    employeeMap[emp.id] = { ...emp, children: [] };
  });

  // 为每个员工找到对应的上级,构建层级关系
  employees.forEach(emp => {
    if (emp.manager_id && employeeMap[emp.manager_id]) {
      employeeMap[emp.manager_id].children.push(employeeMap[emp.id]);
    } else {
      // 无manager_id的员工作为根节点
      rootEmployees.push(employeeMap[emp.id]);
    }
  });

  return rootEmployees;
};

步骤2:创建递归的员工列表组件

每个员工项维护自己的展开状态,支持递归渲染下属:

const EmployeeItem = ({ employee }) => {
  const [open, setOpen] = useState(false);

  const handleClick = () => {
    setOpen(!open);
  };

  return (
    <>
      <ListItem>
        <ListItemButton onClick={handleClick}>
          <ListItemText primary={employee.name} />
          {/* 仅当有下属时显示展开/收起图标 */}
          {employee.children.length > 0 && (
            open ? <ExpandLess /> : <ExpandMore />
          )}
        </ListItemButton>
      </ListItem>
      {/* 展开时渲染下属列表,添加缩进区分层级 */}
      <Collapse in={open} timeout="auto" unmountOnExit>
        <List component="div" disablePadding sx={{ pl: 4 }}>
          {employee.children.map(child => (
            <EmployeeItem key={child.id} employee={child} />
          ))}
        </List>
      </Collapse>
    </>
  );
};

步骤3:完整的主组件代码

整合数据处理和组件渲染逻辑:

import React, { useEffect, useState } from "react";
import {
  Collapse,
  List,
  ListItem,
  ListItemButton,
  ListItemText,
} from "@mui/material";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";

const buildEmployeeTree = (employees) => {
  const employeeMap = {};
  const rootEmployees = [];

  employees.forEach(emp => {
    employeeMap[emp.id] = { ...emp, children: [] };
  });

  employees.forEach(emp => {
    if (emp.manager_id && employeeMap[emp.manager_id]) {
      employeeMap[emp.manager_id].children.push(employeeMap[emp.id]);
    } else {
      rootEmployees.push(employeeMap[emp.id]);
    }
  });

  return rootEmployees;
};

const EmployeeItem = ({ employee }) => {
  const [open, setOpen] = useState(false);

  const handleClick = () => {
    setOpen(!open);
  };

  return (
    <>
      <ListItem>
        <ListItemButton onClick={handleClick}>
          <ListItemText primary={employee.name} />
          {employee.children.length > 0 && (
            open ? <ExpandLess /> : <ExpandMore />
          )}
        </ListItemButton>
      </ListItem>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <List component="div" disablePadding sx={{ pl: 4 }}>
          {employee.children.map(child => (
            <EmployeeItem key={child.id} employee={child} />
          ))}
        </List>
      </Collapse>
    </>
  );
};

export default function NewEmp() {
  const [employeeTree, setEmployeeTree] = useState([]);

  useEffect(() => {
    fetch(
      "https://opensheet.elk.sh/1gH5Kle-styszcHF2G0H8l1w1nDt1RhO9NHNCpHhKK0M/employees"
    )
      .then((res) => res.json())
      .then((data) => {
        const tree = buildEmployeeTree(data);
        setEmployeeTree(tree);
      });
  }, []);

  return (
    <List sx={{ width: "40%" }}>
      {employeeTree.map(emp => (
        <EmployeeItem key={emp.id} employee={emp} />
      ))}
    </List>
  );
}

关键修复说明

  • 树形结构转换:解决扁平数据无法直接层级渲染的问题,每个员工节点包含下属列表。
  • 独立展开状态:每个员工项维护自己的open状态,支持多级独立展开/收起。
  • 交互优化:仅当员工有下属时显示展开图标,子列表添加缩进直观区分层级。

内容的提问来源于stack exchange,提问作者subrath s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 23:27:28