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

如何基于Material UI开发多级下拉选择器?附示例数据

基于Material UI的三级下拉选择器实现

以下是适配你提供的menuData数据源的三级嵌套下拉选择器实现,基于Material UI组件构建:

import React, { useState } from 'react';
import { Select, MenuItem, List, Collapse, Typography, Box } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';

const menuData = [
  {
    mainItem: "FRESHWATER FISH",
    mainId: "main001",
    subMenu: [
      {
        subId: "sub001",
        subItem: "Discus",
      },
      {
        subId: "sub002",
        subItem: "Gold Fish",
        cateItem: [
          {
            cateId: "cate001",
            cateItem: "Oranda Goldfish",
          },
          {
            cateId: "cate002",
            cateItem: "Black Oranda Goldfish",
          },
        ],
      },
    ],
  },
  {
    mainItem: "MARINE FISH",
    mainId: "main002",
  },
  {
    mainItem: "FRESHWATER PLANTS",
    mainId: "main003",
  },
];

const TieredSelect = () => {
  const [selectedValue, setSelectedValue] = useState(null);
  const [expandedMainIds, setExpandedMainIds] = useState(new Set());
  const [expandedSubIds, setExpandedSubIds] = useState(new Set());

  // 切换主菜单展开状态
  const toggleMainExpand = (mainId) => {
    setExpandedMainIds(prev => {
      const newSet = new Set(prev);
      newSet.has(mainId) ? newSet.delete(mainId) : newSet.add(mainId);
      return newSet;
    });
  };

  // 切换子菜单展开状态
  const toggleSubExpand = (subId) => {
    setExpandedSubIds(prev => {
      const newSet = new Set(prev);
      newSet.has(subId) ? newSet.delete(subId) : newSet.add(subId);
      return newSet;
    });
  };

  // 处理选项选中
  const handleSelect = (value) => {
    setSelectedValue(value);
  };

  // 匹配选中值的显示文本
  const getDisplayText = (selected) => {
    if (!selected) return '请选择';
    let displayText = selected;
    menuData.forEach(main => {
      if (main.mainId === selected) displayText = main.mainItem;
      else if (main.subMenu) {
        main.subMenu.forEach(sub => {
          if (sub.subId === selected) displayText = `${main.mainItem} > ${sub.subItem}`;
          else if (sub.cateItem) {
            sub.cateItem.forEach(cate => {
              if (cate.cateId === selected) displayText = `${main.mainItem} > ${sub.subItem} > ${cate.cateItem}`;
            });
          }
        });
      }
    });
    return displayText;
  };

  return (
    <Box sx={{ minWidth: 240 }}>
      <Select
        value={selectedValue || ''}
        onChange={(e) => handleSelect(e.target.value)}
        displayEmpty
        sx={{ width: '100%' }}
        renderValue={(selected) => <Typography>{getDisplayText(selected)}</Typography>}
      >
        <MenuItem value="" disabled>请选择</MenuItem>
        <List>
          {menuData.map(main => (
            <React.Fragment key={main.mainId}>
              <MenuItem
                onClick={() => main.subMenu ? toggleMainExpand(main.mainId) : handleSelect(main.mainId)}
                sx={{ justifyContent: 'space-between' }}
              >
                <Typography>{main.mainItem}</Typography>
                {main.subMenu && (
                  expandedMainIds.has(main.mainId) ? <ExpandLessIcon /> : <ExpandMoreIcon />
                )}
              </MenuItem>
              {main.subMenu && expandedMainIds.has(main.mainId) && (
                <Collapse in timeout="auto" unmountOnExit>
                  <List component="div" disablePadding sx={{ pl: 4 }}>
                    {main.subMenu.map(sub => (
                      <React.Fragment key={sub.subId}>
                        <MenuItem
                          onClick={() => sub.cateItem ? toggleSubExpand(sub.subId) : handleSelect(sub.subId)}
                          sx={{ justifyContent: 'space-between' }}
                        >
                          <Typography>{sub.subItem}</Typography>
                          {sub.cateItem && (
                            expandedSubIds.has(sub.subId) ? <ExpandLessIcon /> : <ExpandMoreIcon />
                          )}
                        </MenuItem>
                        {sub.cateItem && expandedSubIds.has(sub.subId) && (
                          <Collapse in timeout="auto" unmountOnExit>
                            <List component="div" disablePadding sx={{ pl: 4 }}>
                              {sub.cateItem.map(cate => (
                                <MenuItem
                                  key={cate.cateId}
                                  value={cate.cateId}
                                  onClick={() => handleSelect(cate.cateId)}
                                >
                                  <Typography>{cate.cateItem}</Typography>
                                </MenuItem>
                              ))}
                            </List>
                          </Collapse>
                        )}
                      </React.Fragment>
                    ))}
                  </List>
                </Collapse>
              )}
            </React.Fragment>
          ))}
        </List>
      </Select>
    </Box>
  );
};

export default TieredSelect;

实现说明

  • 支持三级菜单的展开/收起,通过Collapse组件实现平滑过渡
  • 选中后自动拼接完整层级路径文本展示
  • 点击无下级菜单的选项直接完成选中,有下级菜单的选项则切换展开状态
  • 使用Set管理展开菜单ID,避免状态冲突

内容的提问来源于stack exchange,提问作者shiran sandaruwan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 19:57:23