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

React MUI X Date Picker界面自定义及年份限制问题求助

React + Material UI Date Picker 问题解决

问题说明

  • 调整Date Picker操作按钮(确认、清除、取消、今日)的显示位置
  • 限制年份选择范围,避免展示完整年份滚轮;使用requireInput时出现错误,需替代方案

原始代码

import React from 'react';
import Rating from '@mui/material/Rating';
import './MyMUIStyles.scss';
import { Dayjs } from 'dayjs';
import dayjs from 'dayjs';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { StaticDatePicker } from '@mui/x-date-pickers/StaticDatePicker';
import { PickersActionBarProps } from '@mui/x-date-pickers/PickersActionBar';
import RestaurantIcon from '@mui/icons-material/Restaurant';
import {
  PickersLayoutProps,
  usePickerLayout,
  pickersLayoutClasses,
  PickersLayoutRoot,
  PickersLayoutContentWrapper,
} from '@mui/x-date-pickers/PickersLayout';
import { DateView } from '@mui/x-date-pickers/models';
import { YearPicker } from '@mui/x-date-pickers/YearPicker';

function ActionList(props: PickersActionBarProps) {
  const { onAccept, onClear, onCancel, onSetToday } = props;
  const actions = [
    { text: 'Accept', method: onAccept },
    { text: 'Clear', method: onClear },
    { text: 'Cancel', method: onCancel },
    { text: 'Today', method: onSetToday },
  ];
  return (
    <List>
      {actions.map(({ text, method }) => (
        <ListItem key={text} disablePadding>
          <ListItemButton onClick={method}>
            <ListItemText primary={text} />
          </ListItemButton>
        </ListItem>
      ))}
    </List>
  );
}

function RestaurantHeader() {
  return (
    <Box
      sx={{
        gridColumn: 1,
        gridRow: 1,
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
      }}
    >
      <RestaurantIcon />
    </Box>
  );
}

function CustomLayout(props: PickersLayoutProps<Dayjs | null, Dayjs, DateView>) {
  const { toolbar, tabs, content, actionBar } = usePickerLayout(props);

  return (
    <PickersLayoutRoot
      ownerState={props}
      sx={{
        overflow: 'auto',
        [`.${pickersLayoutClasses.actionBar}`]: {
          gridColumn: 1,
          gridRow: 2,
        },
        [`.${pickersLayoutClasses.toolbar}`]: {
          gridColumn: 2,
          gridRow: 1,
        },
      }}
    >
      <RestaurantHeader />
      {toolbar}
      {actionBar}
      <PickersLayoutContentWrapper className={pickersLayoutClasses.contentWrapper}>
        {tabs}
        {content}
      </PickersLayoutContentWrapper>
    </PickersLayoutRoot>
  );
}

function MyAllMUITest() {
  const [value, setValue] = React.useState<Dayjs | null>(null);

  return (
    <div>
      <LocalizationProvider dateAdapter={AdapterDayjs}>
        <DatePicker
          value={value}
          onChange={(newValue) => setValue(newValue)}
          slots={{
            layout: CustomLayout,
            actionBar: ActionList,
          }}
        />
      </LocalizationProvider>
      <Rating
        className="rating-box"
        defaultValue={2}
        icon={
          <img src="/assets/images/icon/Star-Filled.png" alt="filled star" />
        }
        emptyIcon={
          <img src="/assets/images/icon/Star-Outline.png" alt="empty star" />
        }
      />
    </div>
  );
}
export default MyAllMUITest;

解决方案

1. 迁移操作按钮位置

弹窗内调整位置

你已通过slots.layout自定义布局,只需修改CustomLayout中PickersLayoutRoot的sx配置,调整actionBar的Grid属性即可改变位置:

  • 比如将按钮移到弹窗底部,占满所有列:
[`.${pickersLayoutClasses.actionBar}`]: {
  gridColumn: 1 / -1,
  gridRow: 3,
  display: 'flex',
  justifyContent: 'flex-end',
  gap: 1
}

完全移出弹窗到页面其他位置

如果要把按钮放到Date Picker弹窗外部,可通过父组件状态控制:

function MyAllMUITest() {
  const [value, setValue] = React.useState<Dayjs | null>(null);
  const [open, setOpen] = React.useState(false);

  return (
    <div>
      <LocalizationProvider dateAdapter={AdapterDayjs}>
        <DatePicker
          value={value}
          onChange={(newValue) => setValue(newValue)}
          open={open}
          onOpen={() => setOpen(true)}
          onClose={() => setOpen(false)}
          slots={{ layout: CustomLayout }}
          slotProps={{ actionBar: { hidden: true } }} // 隐藏默认弹窗内按钮
        />
      </LocalizationProvider>
      {/* 外部操作按钮 */}
      <Box sx={{ mt: 2, gap: 1, display: 'flex' }}>
        <button onClick={() => setOpen(true)}>打开选择器</button>
        <button onClick={() => {setValue(dayjs()); setOpen(false);}}>设置今日</button>
        <button onClick={() => {setValue(null); setOpen(false);}}>清除</button>
        <button onClick={() => setOpen(false)}>取消</button>
      </Box>
      <Rating className="rating-box" defaultValue={2} ... />
    </div>
  );
}

2. 限制年份范围

方法1:minDate + maxDate(推荐)

直接通过DatePicker的minDate和maxDate属性设置允许的日期区间,年份滚轮会自动过滤超出范围的选项:

minDate={dayjs('2010-01-01')}
maxDate={dayjs('2025-12-31')}

方法2:自定义yearPicker插槽

如果需要指定固定年份列表,而非连续区间,可自定义年份选择器:

const CustomYearPicker = (props) => {
  const allowedYears = [2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025];
  return (
    <YearPicker
      {...props}
      years={allowedYears.map(year => dayjs(`${year}-01-01`))}
    />
  );
};
// 在DatePicker的slots中添加
slots={{ yearPicker: CustomYearPicker }}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 00:26:11