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

如何基于选项属性用MUI Autocomplete渲染两个并排结果列表?

解决方案

要实现按type属性分隔为并排双列表的搜索结果,核心思路是自定义Autocomplete的下拉面板(Paper),手动对选项分组并通过Flex布局实现并排展示,同时保留原有的交互逻辑。以下是具体修改步骤和代码:

1. 导入所需MUI组件

确保你已经导入了这些组件(如果没有的话):

import { Paper, Box, Typography, List, ListItem, ListItemText } from '@mui/material';

2. 分组处理选项数据

在组件内部,将原options按type拆分为两个数组:

const bikeOptions = options.filter(opt => opt.type === bikeSmooveboxType);
const stationOptions = options.filter(opt => opt.type === stationTransmiterType);

3. 自定义下拉面板(renderPaper)

给Autocomplete添加renderPaper属性,替换默认的垂直列表布局,改为并排双列:

<Autocomplete
  freeSolo={true}
  id="equipment-searchbar"
  onOpen={() => setOpen(true)}
  onClose={() => setOpen(false)}
  clearOnBlur={false}
  isOptionEqualToValue={(option, value) => option.name === value.name}
  getOptionLabel={(option) => option.title || ''}
  filterOptions={x => x}
  options={options}
  // 移除原来的groupBy,改为手动分组
  onInputChange={onInputChange}
  open={open}
  onChange={(event: any, option: any) => {
    let searchData = (document.getElementById("equipment-searchbar") as HTMLInputElement).value
    if (option !== null && option.globalSearch !== true) {
      switch (option.type) {
        case bikeSmooveboxType:
          redirect("bike/" + option.title)
          break;
        case stationTransmiterType:
          redirect("station/" + option.code)
          break;
      }
    }
  }}
  loading={loading}
  // 新增:自定义下拉面板
  renderPaper={(params) => {
    const { inputValue } = params;
    // 处理freeSolo模式下的自定义输入选项
    const hasFreeSoloOption = freeSolo && inputValue !== '' 
      && !options.some(opt => getOptionLabel(opt) === inputValue);

    return (
      <Paper {...params} sx={{ 
        display: 'flex', 
        gap: 2, 
        p: 2, 
        maxWidth: 800,
        flexWrap: 'wrap' // 小屏幕自动换行
      }}>
        {/* 自由输入选项(可选) */}
        {hasFreeSoloOption && (
          <Box width="100%" mb={2}>
            <Typography variant="subtitle2" fontWeight="bold" mb={1}>
              搜索内容
            </Typography>
            <ListItem
              button
              {...params.getOptionProps({ option: { title: inputValue, globalSearch: true } })}
              sx={{ 
                backgroundColor: params.focusedOption?.title === inputValue 
                  ? 'rgba(0,0,0,0.04)' 
                  : 'transparent' 
              }}
            >
              <ListItemText primary={`"${inputValue}"`} />
            </ListItem>
          </Box>
        )}

        {/* 第一列:自行车设备 */}
        <Box flex={1} minWidth={200}>
          <Typography variant="subtitle2" fontWeight="bold" mb={1}>
            自行车设备
          </Typography>
          <List>
            {bikeOptions.map((option) => (
              <ListItem
                key={option.name || option.title}
                button
                {...params.getOptionProps({ option })}
                sx={{
                  backgroundColor: params.focusedOption === option 
                    ? 'rgba(0,0,0,0.04)' 
                    : 'transparent',
                  '&:hover': { backgroundColor: 'rgba(0,0,0,0.04)' }
                }}
              >
                <ListItemText primary={option.title} />
              </ListItem>
            ))}
          </List>
        </Box>

        {/* 第二列:站点设备 */}
        <Box flex={1} minWidth={200}>
          <Typography variant="subtitle2" fontWeight="bold" mb={1}>
            站点设备
          </Typography>
          <List>
            {stationOptions.map((option) => (
              <ListItem
                key={option.name || option.code}
                button
                {...params.getOptionProps({ option })}
                sx={{
                  backgroundColor: params.focusedOption === option 
                    ? 'rgba(0,0,0,0.04)' 
                    : 'transparent',
                  '&:hover': { backgroundColor: 'rgba(0,0,0,0.04)' }
                }}
              >
                <ListItemText primary={option.title} />
              </ListItem>
            ))}
          </List>
        </Box>
      </Paper>
    );
  }}
  renderInput={(params) => (
    <TextField
      className="inputRounded"
      variant="outlined"
      label="Rechercher dans les équipements"
      {...params}
      InputProps={{
        ...params.InputProps,
        endAdornment: (
          <React.Fragment>
            <SearchIcon/>
            {params.InputProps.endAdornment}
          </React.Fragment>
        ),
      }}
    />
  )}
/>

关键说明

  • params.getOptionProps({ option }):自动绑定选项的点击、选中、键盘导航等交互逻辑,确保和默认Autocomplete行为一致。
  • Flex布局:通过display: flex让两列并排,flex:1实现宽度平分,minWidth保证小屏幕下的可读性。
  • 高亮样式:通过params.focusedOption判断当前聚焦的选项,设置和MUI默认一致的背景色。
  • FreeSolo兼容:保留了自由输入的选项展示,确保原有功能不受影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 09:39:25