react+antd日期选择组件二次封装

650 阅读2分钟

react+antd日期选择组件二次封装

react 项目,基于 Ant-Design 的 DatePicker 组件的二次封装,实现以下功能:

  • 预设 昨天,今天,上周,本周,上月,本月 时间范围快捷选择
  • 设置只能选择某天数范围之内的日期,默认无限制
  • 设置某天以后的时间不能选择,默认今天
  • ......

封装组件:

RangeDatePicker.js:

/**
 * 日期选择组件
 * - 设置只能选择limitDay天数范围之内的日期
 * - 设置endOfDay以后的时间不能选择
 */
import React, { useState } from 'react'
import { DatePicker } from 'antd'
import 'moment/locale/zh-cn'
import locale from 'antd/lib/date-picker/locale/zh_CN'
import moment from 'moment'
import { cloneDeep } from 'lodash'

const RangeDatePicker = ({
  value, // 日期
  onChange, // 时间发生变化的回调,发生在用户选择时间时
  showTime = false, // 是否增加时间选择功能(时分秒)
  limitDay = 0, // 只能选择limitDay天数范围之内的日期,默认无限制
  endOfDay = moment().endOf('day'), // endOfDay以后的时间不能选择,默认今天
  ...rest // 其他参数
}) => {
  const { RangePicker } = DatePicker

  const startOfDay = moment()
    .subtract(limitDay, 'days')
    .endOf('day') // 今天之前limitDay天

  const [selectDate, setSelectDate] = useState([startOfDay, endOfDay])
  const [selectvalue, setSelectValue] = useState([startOfDay, endOfDay])

  // 不可选择的日期
  const disabledTaskDate = current => {
    let date = cloneDeep(selectDate)
    if (selectDate && selectDate?.length > 0 && limitDay > 0) {
      // 控制只能选择limitDay天数范围之内的日期
      let startOfStage = date?.[1]?.subtract(limitDay, 'days').endOf('day')
      let endOfStage = date?.[0]?.add(limitDay, 'days').endOf('day')
      if (endOfStage > endOfDay) {
        endOfStage = endOfDay
      }
      return current < startOfStage || current > endOfStage
    } else {
      // endOfDay以后的时间不能选择
      return current > endOfDay
    }
  }

  // 待选日期发生变化的回调
  const onDateChange = dates => {
    if (!dates || !dates?.length) return
    setSelectDate([cloneDeep(dates?.[0]), cloneDeep(dates?.[1])])
  }

  // 弹出日历和关闭日历的回调
  const onDateOpenChange = open => {
    if (open) {
      if (selectvalue && selectvalue?.length) return
      setSelectDate([])
    }
  }

  // 时间发生变化的回调
  const onPickerChange = value => {
    setSelectValue(value)
    onChange(value)
  }

  // 获取预设时间范围快捷选择
  const getRanges = () => {
    // 今天
    const toDayRange =
      moment().startOf('day') <= endOfDay
        ? { 今天: [moment().startOf('day'), moment().endOf('day')] }
        : {}
    // 本周
    const thisWeekRange =
      moment().startOf('week') <= endOfDay ? { 本周: [moment().startOf('week'), endOfDay] } : {}
    // 本月
    const thisMonthRange =
      moment().startOf('month') <= endOfDay ? { 本月: [moment().startOf('month'), endOfDay] } : {}
      
    return {
      // 昨天
      昨天: [
        moment()
          .subtract(1, 'days')
          .startOf('day'),
        moment()
          .subtract(1, 'days')
          .endOf('day'),
      ],
      // 今天
      ...toDayRange,
      // 上周
      上周: [
        moment(new Date())
          .subtract(1, 'week')
          .startOf('week'),
        moment(new Date())
          .subtract(1, 'week')
          .endOf('week'),
      ],
      // 本周
      ...thisWeekRange,
      // 上月
      上月: [
        moment(new Date())
          .subtract(1, 'month')
          .startOf('month'),
        moment(new Date())
          .subtract(1, 'month')
          .endOf('month'),
      ],
      // 本月
      ...thisMonthRange,
    }
  }

  return (
    <RangePicker
      locale={locale} // 国际化配置汉化getPopupContainer={triggerNode => triggerNode.parentNode} // 定义浮层的容器(解决选项框随页面滚动分离问题)
      showTime={
        showTime
          ? {
              hideDisabledOptions: true, // 隐藏禁止选择的选项
              defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')], // 默认时间
            }
          : null
      }
      format={showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'} // 日期格式
      ranges={{
        // 昨天: [
        //   moment()
        //     .subtract(1, 'days')
        //     .startOf('day'),
        //   moment()
        //     .subtract(1, 'days')
        //     .endOf('day'),
        // ],
        // 今天: [moment().startOf('day'), moment().endOf('day')],
        // 上周: [
        //   moment(new Date())
        //     .subtract(1, 'week')
        //     .startOf('week'),
        //   moment(new Date())
        //     .subtract(1, 'week')
        //     .endOf('week'),
        // ],
        // 本周: [moment().startOf('week'), moment().endOf('week')],
        // 上月: [
        //   moment(new Date())
        //     .subtract(1, 'month')
        //     .startOf('month'),
        //   moment(new Date())
        //     .subtract(1, 'month')
        //     .endOf('month'),
        // ],
        // 本月: [moment().startOf('month'), moment().endOf('month')],
        ...getRanges(),
      }}
      value={value} // 日期
      onChange={onPickerChange} // 时间发生变化的回调
      disabledDate={disabledTaskDate} // 不可选择的日期
      onCalendarChange={onDateChange} // 待选日期发生变化的回调
      onOpenChange={onDateOpenChange} // 弹出日历和关闭日历的回调
      {...rest}
    />
  )
}

export default RangeDatePicker

使用组件:

默认使用

import RangeDatePicker from '@/components/RangeDatePicker'
......
<RangeDatePicker onChange={() => {}} />

效果: 在这里插入图片描述

增加时间选择功能(时分秒)

import RangeDatePicker from '@/components/RangeDatePicker'
......
<RangeDatePicker showTime={true} onChange={() => {}} />

效果: 在这里插入图片描述

设置只能选择limitDay天数范围之内的日期

import RangeDatePicker from '@/components/RangeDatePicker'
......
// 只能选择7天数范围之内的日期
<RangeDatePicker limitDay={7} onChange={() => {}} />

效果: 在这里插入图片描述

设置endOfDay以后的时间不能选择

import RangeDatePicker from '@/components/RangeDatePicker'
......
// 昨天以后的时间不能选择
<RangeDatePicker endOfDay={moment().subtract(1, 'days').endOf('day')} onChange={() => {}} />

效果: 在这里插入图片描述