关于Antd-DatePicker-RangePicker的日期限定范围选择

3,736 阅读3分钟

前言

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情 >>

问题

最近工作中需要优化一个日/月时间范围选择模块,将原先的无限制日期范围选择,修改为选择日期的时候,将日期限定在90天以内,也就是说当你优先选择结束日期时,往前推90天,当你优先选择开始时间时,往后推90天。当选择月份的时候,同理往前推2个月和向后推两个月。如图:

  • 优先选择开始日期(8.1-10.30) 日.jpg
  • 优先选择结束日期(5.9-8.7) 日2.jpg

  • 优先选择开始月份(1-3) 月.jpg
  • 优先选择结束月份(4-6) 月1.jpg

实现过程

先将官网的案例复制过来,具体地址-->Antd-DatePicker-选择不超过七天的范围

然后针对自己项目中的范围规则进行修改

  • 待选日期发生变化的回调的值,获得的值是一个数组,下标为0的则是开始时间,下标为1的则是结束时间
const [dates, setDates] = useState(null); 
  • 设置空值
const [hackValue, setHackValue] = useState(null);   
  • 获取到的日期的值
const [value, setValue] = useState(null); 
  • 不可选择的日期
const disabledDate = (current) => { 
    if (!dates) { 
        return false;   // 开始和结束日期都未选则所有日期都可选
    }
    // 如果优先选择开始日期,根据当前为日期表还是月份表,利用monent的diff计算可选日期和开始时间的差值,返回boolean类型
    const tooLate = dates[0] && (
        form.getFieldValue('dateType') === 'month'
             ? current.diff(dates[0], 'months') > 1 
             : current.diff(dates[0], 'days') > 90
    )
    // 如果优先选择结束日期,同样根据当前为日期表还是月份表,利用monent的diff计算可选日期和开始时间的差值,返回boolean类型
    const tooEarly = dates[1] && (
         form.getFieldValue('dateType') === 'month'
            ? dates[1] && dates[1].diff(current, 'month') > 1
            : dates[1] && dates[1].diff(current, 'days') > 90
    )
    return !!tooEarly || !!tooLate; 
};
  • 弹出日历和关闭日历的回调
const onOpenChange = (open) => { // open为boolean值,为true时则日历打开,false则日历关闭
    if (open) { 
        // 因为本身需要有默认值,所以重新打开日历时需要先将默认值清空
        form.setFieldsValue({
            date: null
        });
        setHackValue([null, null]); 
        setDates([null, null]); 
     } else { 
         setHackValue(null); 
     } 
  };
  • 默认值,为月份时默认展示前6个月,为日期是展示近10天
    const showMonth = [moment(new Date()).subtract(6, 'months'), moment(new Date())]
    const showDate = [moment(new Date()).subtract(10, 'days'), moment(new Date())]
  • RangePicker组件属性
 <Form.Item
    label="统计时间"
    name="date"
    initialValue={form.getFieldValue('dateType') === 'month' ? showMonth : showDate}
>
    <RangePicker
        allowClear     // 是否可清除
        value={hackValue || value}    // 获取选择的日期
        disabledDate={disabledDate}   // 不可选择的日期
        onOpenChange={onOpenChange}    // 弹出日历和关闭日历的回调
        onChange={val => setValue(val)}   // 时间发生变化的回调
        onCalendarChange={val => setDates(val)}   // 待选日期发生变化的回调
        picker={form.getFieldValue('dateType') === 'month' ? 'month' : 'date'}
        getPopupContainer={(triggerNode: { parentNode: any; }) => triggerNode.parentNode}   // 控制弹出框不随页面滚动而滚动
    />
</Form.Item>
  • radio组控制日历为日期还是月份
    const changeDateType = (e: any) => {
        form.setFieldsValue({
            date: e.target.value === 'month' ? showMonth : showDate
        });
    };
  <Form.Item name="dateType" initialValue='date'>
    <Radio.Group onChange={changeDateType}>
        <Radio value="date">按日</Radio>
        <Radio value="month">按月</Radio>
    </Radio.Group>
  </Form.Item>

总结

这里主要是设定的默认值没有及时清空,导致每次打开日历,在月份进行计算的时候始终会有bug,一直以为是moment的diff算法有问题,这个算法在进行向前或者向后推三个月是大于1目前还在了解,后面了解到再做补充,有知情的大佬可以评论区留言,谢谢。