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

MUI Rich Tree View 父节点自定义折叠/展开图标样式,叶子节点隐藏该图标及背景的实现方案

MUI Rich Tree View 父节点自定义折叠/展开图标样式,叶子节点隐藏该图标及背景的实现方案

我来帮你搞定这个问题~你的核心需求是区分父节点(含子节点)和叶子节点,只给父节点渲染带圆形背景的展开/折叠图标,叶子节点完全隐藏这个图标容器,还能自定义末端图标对吧?我们可以通过「动态判断节点是否有子节点」+「条件化样式/图标渲染」来解决,下面是具体的实现步骤和完整代码:

问题根源分析

你之前尝试添加.has-children类但没生效,是因为MUI的TreeItem不会自动给父节点标记这个类名,需要我们自己根据节点的children属性手动判断,再传递给样式组件。

实现思路

  1. 判断节点类型:每个节点从item props中获取数据,检查是否存在非空的children数组,以此区分父节点和叶子节点。
  2. 条件化样式:给StyledTreeItem传入hasChildren props,针对性地给父节点加圆形背景样式,给叶子节点隐藏图标容器。
  3. 自定义叶子节点图标:可选,根据节点类型给叶子节点添加专属的末端图标(比如文档/文件夹图标)。

完整代码实现

import * as React from 'react';
import Box from '@mui/material/Box';
import { styled, alpha } from '@mui/material/styles';
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import { TreeItem, treeItemClasses } from '@mui/x-tree-view/TreeItem';
// 导入你需要的自定义图标
import FolderIcon from '@mui/icons-material/Folder';
import DescriptionIcon from '@mui/icons-material/Description';

const MUI_X_PRODUCTS = [
  { id: 'grid', label: 'Data Grid', children: [
    { id: 'grid-community', label: '@mui/x-data-grid' },
    { id: 'grid-pro', label: '@mui/x-data-grid-pro' },
    { id: 'grid-premium', label: '@mui/x-data-grid-premium' },
  ]},
  { id: 'pickers', label: 'Date and Time Pickers', children: [
    { id: 'pickers-community', label: '@mui/x-date-pickers' },
    { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' },
  ]},
  { id: 'charts', label: 'Charts', children: [{ id: 'charts-community', label: '@mui/x-charts' }]},
  { id: 'tree-view', label: 'Tree View', children: [{ id: 'tree-view-community', label: '@mui/x-tree-view' }]},
];

// 1. 创建支持hasChildren条件的StyledTreeItem
const StyledTreeItem = styled(TreeItem, {
  shouldForwardProp: (prop) => prop !== 'hasChildren' // 过滤掉hasChildren,避免传递给DOM引发警告
})(({ theme, hasChildren }) => ({
  // 基础通用样式
  color: theme.palette.grey[200],
  [`& .${treeItemClasses.content}`]: {
    borderRadius: theme.spacing(0.5),
    padding: theme.spacing(0.5, 1),
    margin: theme.spacing(0.2, 0),
    [`& .${treeItemClasses.label}`]: {
      fontSize: '0.8rem',
      fontWeight: 500,
    },
  },
  // 父节点专属:带圆形背景的展开/折叠图标容器
  ...(hasChildren && {
    [`& .${treeItemClasses.iconContainer}`]: {
      borderRadius: '50%',
      backgroundColor: theme.palette.mode === 'light'
        ? alpha(theme.palette.primary.main, 0.25)
        : theme.palette.primary.dark,
      width: theme.spacing(3),
      height: theme.spacing(3),
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      color: theme.palette.mode === 'dark'
        ? theme.palette.primary.contrastText
        : 'inherit',
    },
  }),
  // 叶子节点专属:隐藏展开/折叠图标容器
  ...(!hasChildren && {
    [`& .${treeItemClasses.iconContainer}`]: {
      display: 'none', // 完全隐藏图标容器,避免留白
    },
  }),
  // 子节点缩进样式
  [`& .${treeItemClasses.groupTransition}`]: {
    marginLeft: 15,
    paddingLeft: 18,
    borderLeft: `1px dashed ${alpha(theme.palette.text.primary, 0.4)}`,
  },
  // 亮色模式适配
  ...theme.applyStyles('light', {
    color: theme.palette.grey[800],
  }),
}));

// 2. 包装TreeItem,自动判断节点是否有子节点
const CustomTreeItem = (props) => {
  const { item, ...rest } = props;
  // 判断是否为父节点:存在children且数组非空
  const hasChildren = !!(item.children && item.children.length > 0);
  
  // 可选:给叶子节点自定义末端图标(可根据节点id/label区分类型)
  const getLeafEndIcon = () => {
    // 这里可以根据节点的id或label判断是文档还是文件夹,示例简单区分
    return item.label.startsWith('@mui/x-') 
      ? <DescriptionIcon fontSize="small" /> 
      : <FolderIcon fontSize="small" />;
  };

  return (
    <StyledTreeItem
      hasChildren={hasChildren}
      {...rest}
      item={item}
      // 只有叶子节点显示自定义endIcon
      endIcon={!hasChildren ? getLeafEndIcon() : undefined}
    />
  );
};

export default function CustomStyling() {
  return (
    <Box sx={{ minHeight: 352, minWidth: 250 }}>
      <RichTreeView
        defaultExpandedItems={['grid']}
        slots={{ item: CustomTreeItem }} // 使用我们自定义的Item组件
        items={MUI_X_PRODUCTS}
      />
    </Box>
  );
}

关键细节说明

  • hasChildren判断逻辑:!!(item.children && item.children.length > 0) 确保只有真正有子节点的才会被标记为父节点,避免空数组被误判。
  • 样式隔离:通过shouldForwardProp过滤hasChildren props,避免它被传递到DOM元素上引发警告。
  • 叶子节点图标:你可以完全自定义getLeafEndIcon函数,根据节点的id、label或其他属性返回不同的图标,满足业务需求。
  • 图标容器隐藏:叶子节点通过display: none隐藏iconContainer,彻底移除展开/折叠图标和其容器,不会留下多余留白。

这样修改后,父节点会显示带圆形背景的展开/折叠图标,叶子节点完全隐藏这个图标容器,还能展示你自定义的末端图标,完美符合你的需求~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 14:23:04