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

如何在切换编辑模式时保持MUI Autocomplete的样式一致?

问题:编辑模式下Autocomplete组件内TextField样式异常

进入editMode(编辑模式)时,Autocomplete组件内的TextField样式会发生变化,选择下拉选项时也会出现同样问题。尝试修改Autocomplete内的TextField以及getOptionsLabel,但效果更差。期望进入编辑模式后,TextField的样式与非编辑模式完全一致,但目前无法找到样式差异的原因——两者信息相同,但布局位置不同。

  • 非编辑模式目标样式:左侧缩略图+右侧上下排列的文本(上方为libraryRef,下方为composition、manufactureDetailName和weight的组合文本),整体居中对齐
  • 编辑模式异常样式:Autocomplete内的TextField文本布局混乱,位置偏移,与非编辑模式排版不一致

相关代码

// Format the value for the Autocomplete component
const formatValue = (option: Material): ReactElement => (
  <Stack px={3} py={2} width={1}>
    <Typography variant="bodySmall.default">
      {option.libraryRef}
    </Typography>
    <Typography variant="captions.default" color={alpha(palette.primary.main, 0.7)}>
      {option.composition}
      {'manufactureDetail' in option && option.manufactureDetail?.manufactureDetailName ? ` • ${option.manufactureDetail.manufactureDetailName}` : ''}
      {' | '}
      {option.weight}
    </Typography>
  </Stack>
);

const handleChange = (
  event: React.SyntheticEvent,
  newValue: Material | { id: string; libraryRef: string; composition: string; weight: string } | null,
): void => {
  if (newValue && 'materialSubcategory' in newValue) {
    setSelectedMaterial(newValue);
  }
};

return (
  <Stack
    direction="row"
    height={1}
    flexGrow={1}
    alignItems="center"
    boxSizing="border-box"
  >
    {isEditMode ? (
      <Autocomplete
        groupBy={(option) => {
        if ('materialSubcategory' in option && option.materialSubcategory) {
          return option.materialSubcategory.materialSubcategoryName;
        }
        return '';
        }}
        value={selectedMaterial ?? value}
        options={[{ id: 'on-the-fly', libraryRef: 'Material On The Fly', composition: '', weight: '' }, ...materials]}
        getOptionLabel={(option) => `${option.libraryRef} | ${option.composition}${'manufactureDetail' in option && option.manufactureDetail?.manufactureDetailName ? ` • ${option.manufactureDetail.manufactureDetailName}` : ''} | ${option.weight}`}
        renderInput={(params: AutocompleteRenderInputParams): ReactElement => (
          // This is what we see when the dropdown is closed
          <Stack direction="row">
            <TextField
              {...params}
              placeholder={!value ? '' : 'Select Material'}
              inputProps={{
                ...params.inputProps,
                readOnly: false,
              }}
              InputProps={{
                ...params.InputProps,
                disableUnderline: true,
                startAdornment: (
                  <ImagePlaceholder
                    sx={{
                      width: THUMBNAIL_SIZE,
                      minWidth: THUMBNAIL_SIZE,
                      height: THUMBNAIL_SIZE,
                      marginRight: 3,
                    }}
                  />
                ),
              }}
              sx={{
                [`& .${autocompleteClasses.inputRoot}`]: {
                  ['& input']: {
                    ...theme.typography['bodySmall.default'],
                  },
                },
                [`& .${autocompleteClasses.popupIndicator}`]: {
                  right: 12,
                  display: 'flex',
                  alignItems: 'center',
                  width: '16px !important',
                  height: '16px !important',
                  ['&:hover']: {
                  backgroundColor: 'transparent',
                  },
                },
              }}
            />
          </Stack>
        )}
        renderOption={(props, option) => (
          <>
            {/*this is the materials grouped by MSC name*/}
            <MaterialOption
              option={option}
              props={props}
            />
            {/*this is the material on the fly option with the plus icon*/}
            {'id' in option && option.id === 'on-the-fly' && (
              <MaterialPickerGroup
                option={option}
                props={props}
                setShowMaterialOnTheFlyDialog={setShowMaterialOnTheFlyDialog}
              />
            )}
          </>
        )}
    ) : (
      <Stack direction="row">
        <ImagePlaceholder sx={{ width: THUMBNAIL_SIZE, minWidth: THUMBNAIL_SIZE, height: THUMBNAIL_SIZE }} />
        {formatValue(value)}
      </Stack>
    )}

解决方案

1. 复用非编辑模式的文本渲染逻辑

核心问题是编辑模式用getOptionLabel生成的纯文本,无法还原非编辑模式formatValue的多层文本结构和样式。修改renderInput,直接复用formatValue来渲染输入框内容:

renderInput={(params: AutocompleteRenderInputParams): ReactElement => {
  const currentValue = params.value as Material;
  return (
    <Stack direction="row" height={1} alignItems="center" flexGrow={1}>
      <ImagePlaceholder
        sx={{
          width: THUMBNAIL_SIZE,
          minWidth: THUMBNAIL_SIZE,
          height: THUMBNAIL_SIZE,
          marginRight: 3,
        }}
      />
      {/* 复用formatValue渲染内容,无值时显示占位符 */}
      {currentValue ? formatValue(currentValue) : (
        <Typography variant="bodySmall.default" color="text.secondary">
          Select Material
        </Typography>
      )}
      {/* 保留下拉箭头 */}
      {params.InputProps.endAdornment}
    </Stack>
  );
}}

2. 隐藏原生输入框干扰

由于已经自定义了显示内容,不需要TextField的原生输入框,可将其隐藏:

renderInput={(params) => (
  <TextField
    {...params}
    inputProps={{
      ...params.inputProps,
      style: { display: 'none' }, // 隐藏原生输入框
    }}
    InputProps={{
      ...params.InputProps,
      disableUnderline: true,
      startAdornment: null, // 移除默认startAdornment,自定义外层Stack处理
      children: null,
    }}
    sx={{
      width: '100%',
      [`& .${autocompleteClasses.popupIndicator}`]: {
        right: 12,
        display: 'flex',
        alignItems: 'center',
        width: '16px !important',
        height: '16px !important',
        '&:hover': {
          backgroundColor: 'transparent',
        },
      },
    }}
  />
)}

3. 统一外层容器样式

确保编辑模式下的Autocomplete外层容器和非编辑模式保持完全一致的布局属性:

{isEditMode ? (
  <Stack direction="row" height={1} flexGrow={1} alignItems="center" boxSizing="border-box">
    <Autocomplete
      // ...其他属性
    />
  </Stack>
) : (
  // 非编辑模式原有代码
)}

4. 对齐value类型

确保selectedMaterial和value的类型一致,避免渲染时的异常:

value={selectedMaterial ?? (value as Material)}

关键原因总结

  • 非编辑模式用自定义Stack+Typography组合实现排版,编辑模式依赖TextField原生输入框的文本渲染,两者样式规则(行高、间距、对齐)完全不同
  • getOptionLabel生成的纯文本无法还原formatValue的多层文本结构,导致布局偏移

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 18:32:08