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

如何在React中基于Material UI实现双范围年月滑块

React双范围年月日期滑块实现方案(基于Material UI)

核心实现思路

利用Material UI的Slider组件(原生支持双拇指范围选择),将年月转换为可计算的数值(从起始年份开始的累计月份数),通过数值映射实现日期范围的选择与展示,同时自定义样式匹配需求。

完整代码实现

import { useState } from 'react';
import { Slider, Typography, Box } from '@mui/material';

// 年月转数值:将指定年月转换为从起始年份开始的累计月份数
const dateToValue = (year, month, startYear) => (year - startYear) * 12 + month;

// 数值转年月:将累计月份数还原为对应年月
const valueToDate = (value, startYear) => {
  const year = startYear + Math.floor(value / 12);
  const month = value % 12;
  return { year, month };
};

// 年月格式化:转换为友好显示文本
const formatDateLabel = ({ year, month }) => `${year}年${month + 1}月`;

const DateRangeSlider = () => {
  // 定义可选日期范围:示例为2018-2024年
  const startYear = 2018;
  const endYear = 2024;
  const totalMonths = (endYear - startYear) * 12;

  // 初始选中范围:2020年1月 至 2023年6月
  const initialValue = [dateToValue(2020, 0, startYear), dateToValue(2023, 5, startYear)];
  const [value, setValue] = useState(initialValue);

  const handleChange = (_, newValue) => setValue(newValue);

  // 生成年份标记:每一年显示一个标签
  const yearMarks = Array.from({ length: endYear - startYear + 1 }, (_, i) => ({
    value: dateToValue(startYear + i, 0, startYear),
    label: `${startYear + i}年`,
  }));

  return (
    <Box sx={{ width: '85%', margin: '2rem auto' }}>
      <Typography gutterBottom align="center" variant="subtitle1">年月范围选择</Typography>
      <Slider
        value={value}
        onChange={handleChange}
        min={0}
        max={totalMonths}
        step={1} // 步长为1个月
        marks={yearMarks}
        valueLabelDisplay="auto" // 拖拽时显示当前选中的年月
        valueLabelFormat={(val) => formatDateLabel(valueToDate(val, startYear))}
        sx={{
          '& .MuiSlider-thumb': {
            width: 18,
            height: 18,
            backgroundColor: '#2196f3',
            '&:hover, &.Mui-focusVisible': {
              boxShadow: '0 0 0 8px rgba(33, 150, 243, 0.15)',
            },
          },
          '& .MuiSlider-track': {
            height: 8,
            borderRadius: 4,
            backgroundColor: '#2196f3',
          },
          '& .MuiSlider-rail': {
            height: 8,
            borderRadius: 4,
            backgroundColor: '#eaeaea',
          },
          '& .MuiSlider-markLabel': {
            fontSize: 13,
            color: '#666',
            marginTop: 1,
          },
          '& .MuiSlider-mark': {
            backgroundColor: '#ccc',
            height: 8,
            width: 1,
          },
        }}
      />
      <Box sx={{ display: 'flex', justifyContent: 'space-between', marginTop: '1.2rem' }}>
        <Typography variant="body2" color="text.secondary">起始:{formatDateLabel(valueToDate(value[0], startYear))}</Typography>
        <Typography variant="body2" color="text.secondary">结束:{formatDateLabel(valueToDate(value[1], startYear))}</Typography>
      </Box>
    </Box>
  );
};

export default DateRangeSlider;

样式自定义说明

  • 轨道与拇指样式:通过sx属性覆盖MUI默认样式,调整轨道高度、圆角、颜色,拇指大小与hover效果,匹配参考图的视觉风格。
  • 标记优化:如果需要更细致的刻度(比如每半年一个标记),可以在yearMarks基础上添加无标签的小标记,示例:
    const halfYearMarks = Array.from({ length: totalMonths + 1 }, (_, i) => {
      if (i % 6 === 0) return null;
      return { value: i, label: '' };
    }).filter(Boolean);
    // 合并到marks中:marks={[...yearMarks, ...halfYearMarks]}
    
  • 边界控制:Slider组件会自动维护双拇指的顺序,确保起始值始终小于等于结束值,无需额外处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:42:08