MUI Rich Tree View 父节点自定义折叠/展开图标样式,叶子节点隐藏该图标及背景的实现方案
MUI Rich Tree View 父节点自定义折叠/展开图标样式,叶子节点隐藏该图标及背景的实现方案
我来帮你搞定这个问题~你的核心需求是区分父节点(含子节点)和叶子节点,只给父节点渲染带圆形背景的展开/折叠图标,叶子节点完全隐藏这个图标容器,还能自定义末端图标对吧?我们可以通过「动态判断节点是否有子节点」+「条件化样式/图标渲染」来解决,下面是具体的实现步骤和完整代码:
问题根源分析
你之前尝试添加.has-children类但没生效,是因为MUI的TreeItem不会自动给父节点标记这个类名,需要我们自己根据节点的children属性手动判断,再传递给样式组件。
实现思路
- 判断节点类型:每个节点从
itemprops中获取数据,检查是否存在非空的children数组,以此区分父节点和叶子节点。 - 条件化样式:给StyledTreeItem传入
hasChildrenprops,针对性地给父节点加圆形背景样式,给叶子节点隐藏图标容器。 - 自定义叶子节点图标:可选,根据节点类型给叶子节点添加专属的末端图标(比如文档/文件夹图标)。
完整代码实现
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过滤hasChildrenprops,避免它被传递到DOM元素上引发警告。 - 叶子节点图标:你可以完全自定义
getLeafEndIcon函数,根据节点的id、label或其他属性返回不同的图标,满足业务需求。 - 图标容器隐藏:叶子节点通过
display: none隐藏iconContainer,彻底移除展开/折叠图标和其容器,不会留下多余留白。
这样修改后,父节点会显示带圆形背景的展开/折叠图标,叶子节点完全隐藏这个图标容器,还能展示你自定义的末端图标,完美符合你的需求~
内容来源于stack exchange
相关产品推荐
相关产品推荐

