不受限时间段的日期范围选择器DatePicker

简介: 不受限时间段的日期范围选择器DatePicker封装记录全过程。

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

在过去的日子里,Antd使用得心易手,也不曾会吐槽一些设计不当的地方,印象比较深刻的就是Antd的DatePicker日期范围选择器只能选择连续月的时间区间,这一点其实也是让我一直头疼的,既然这段时间在设计自己的React组件库——React-View-UI,做到日期选择器,也不妨将自己不太满意的一个功能点给完善了一下,如图是Antd的日期选择器:

在这里插入图片描述
如图所示是React-View-UI的日期选择器:
在这里插入图片描述
组件源码index.tsx如下:

import React, {
   
    useEffect, FC, memo, useState, useCallback } from 'react'
import {
   
    DoubleLeftOutlined, LeftOutlined, DoubleRightOutlined, RightOutlined, SwapRightOutlined } from '@ant-design/icons'
import Input from '../../Input'
import './index.module.less'

interface RangeProps {
   
   

}
const RangeDatePicker: FC<RangeProps> = (props) => {
   
   
    const [startDate, setStartDate] = useState({
   
   
        startYear: new Date().getFullYear(),
        startMonth: new Date().getMonth() + 1,
        startDay: new Date().getDate()
    })
    const [endDate, setEndDate] = useState({
   
   
        endYear: new Date().getFullYear(),
        endMonth: new Date().getMonth() + 2,
        endDay: new Date().getDate()
    })
    const [startTime, setStartTime] = useState('');
    const [endTime, setEndTime] = useState('')
    const [startMonthFirstDay, setStartMonthFirstDay] = useState(0);     //本月第一天是周几
    const [endMonthFirstDay, setEndMonthFirstDay] = useState(0);     //本月第一天是周几
    const [startDayListArray, setStartDayListArray] = useState<Array<number>>([]);        //start月的日历
    const [endDayListArray, setEndDayListArray] = useState<Array<number>>([]);        //end月的日历
    let activeBorderDom: Element | null = document.querySelector('.activeBorder');

    useEffect(() => {
   
   
        const {
   
    startYear, startMonth } = startDate;
        const {
   
    endYear, endMonth } = endDate;
        const startFirstDay = new Date(`${startYear}/${startMonth}/1`).getDay();
        const endFirstDay = new Date(`${endYear}/${endMonth}/1`).getDay();
        const startTotalDay = new Date(startYear, startMonth, 0).getDate()
        const endTotalDay = new Date(endYear, endMonth, 0).getDate()
        const startDayList = new Array(startFirstDay).fill('');
        const endDayList = new Array(endFirstDay).fill('');
        for (let i = 1; i < startTotalDay + 1; i++) {
   
   
            startDayList.push(i)
        }
        for (let i = 1; i < endTotalDay + 1; i++) {
   
   
            endDayList.push(i)
        }
        setStartDayListArray(startDayList);
        setStartMonthFirstDay(startFirstDay);
        setEndDayListArray(endDayList);
        setEndMonthFirstDay(endFirstDay);
    }, [startDate.startYear, startDate.startMonth, endDate.endYear, endDate.endMonth])

    const startIptFocus = () => {
   
   
        console.log(activeBorderDom);
        (activeBorderDom as any).style.left = "0";
    }
    const endIptFocus = () => {
   
   
        (activeBorderDom as any).style.left = "190px";
    }
    const preYear = (type: string) => {
   
            //切换上一年
        if (type == "start") {
   
   
            setStartDate(old => {
   
   
                old.startYear = old.startYear - 1;
                return {
   
    ...old }
            })
        } else if (type == "end") {
   
   
            if (endDate.endYear > startDate.startYear) {
   
   
                setEndDate(old => {
   
   
                    old.endYear = old.endYear - 1;
                    return {
   
    ...old };
                })
            }
        }
    }
    const nextYear = (type: string) => {
   
           //切换下一年
        if (type == "start") {
   
   
            if (startDate.startYear < endDate.endYear) {
   
   
                setStartDate(old => {
   
   
                    old.startYear = old.startYear + 1;
                    return {
   
    ...old }
                })
            }
        } else if (type == "end") {
   
   
            setEndDate(old => {
   
   
                old.endYear = old.endYear + 1;
                return {
   
    ...old };
            })
        }
    }
    const preMonth = (type: string) => {
   
           //切换上一个月
        if(type == "start") {
   
   
            setStartDate(old => {
   
   
                if(old.startMonth == 1) {
   
   
                    old.startMonth = 12;
                    old.startYear -= 1;
                } else {
   
   
                    old.startMonth -= 1;
                }
                return {
   
   ...old};
            })
        } else if(type == "end") {
   
   
            if(endDate.endYear == startDate.startYear && endDate.endMonth == startDate.startMonth) {
   
   
                return;
            } else {
   
   
                setEndDate(old => {
   
   
                    if(old.endMonth == 1) {
   
   
                        old.endMonth = 12;
                        old.endYear -= 1;
                    } else {
   
   
                        old.endMonth -= 1;
                    }
                    if(old.endDay < startDate.startDay) {
   
   
                        old.endDay = startDate.startDay;
                    }
                    return {
   
   ...old};
                })
            }
        }
    }
    const nextMonth = (type: string) => {
   
         //切换下一个月
        console.log(type)
        if(type == "start") {
   
   
            if(endDate.endYear == startDate.startYear && endDate.endMonth == startDate.startMonth) {
   
   
                return;
            } else {
   
   
                setStartDate(old => {
   
   
                    if(old.startMonth == 12) {
   
   
                        old.startMonth = 1;
                        old.startYear += 1;
                    } else {
   
   
                        old.startMonth += 1;
                    }
                    return {
   
   ...old};
                })
            }
        } else if(type == "end") {
   
   
            setEndDate(old => {
   
   
                if(old.endMonth == 12) {
   
   
                    old.endMonth = 1;
                    old.endYear += 1;
                } else {
   
   
                    old.endMonth += 1;
                }
                return {
   
   ...old};
            })
        }
    }
    const chooseStartDay = (day: number | string) => {
   
             //选择开始日期
        if(day == "") return;
        setStartDate(old => {
   
   
            old.startDay = day as number;
            return {
   
   ...old};
        })
        setStartTime(`${startDate.startYear}-${startDate.startMonth}-${
     
     day}`)
        if(startDate.startYear == endDate.endYear && startDate.startMonth == endDate.endMonth) {
   
   
            if(day > endDate.endDay) {
   
   
                setEndDate(old => {
   
   
                    old.endDay = day as number;
                    return {
   
   ...old};
                })
            }
        }
    }
    const chooseEndDay = (day: number | string) => {
   
               //选择结束日期
        if(startDate.startYear == endDate.endYear && startDate.startMonth == endDate.endMonth) {
   
   
            if(day < startDate.startDay) {
   
   
                return;
            }
        }
        setEndDate(old => {
   
   
            old.endDay = day as number;
            return {
   
   ...old};
        })
        setEndTime(`${endDate.endYear}-${endDate.endMonth}-${
     
     day}`)
    }
    const activeStyles = () => {
   
                           //选中的样式
        return {
   
   
            activeDay: {
   
   
                color: "#fff",
                background: "#1890FF",
                fontWeight: "bold"
            }
        }
    }
    const disabledClass = useCallback((day: number | string) => {
   
   
        if(day == "") {
   
   
            return "white"
        }
        if(startDate.startYear == endDate.endYear && startDate.startMonth == endDate.endMonth) {
   
   
            if(day < startDate.startDay) {
   
   
                return "disabled-day"
            }
            return "day-box";
        }
        return "day-box";
    }, [startDate, endDate])
    return (
        <div className="range">
            <div className="rangePicker">
                <Input placeholder="请输入开始日期" defaultValue={
   
   startTime} handleIptFocus={
   
   startIptFocus} />
                <SwapRightOutlined style={
   
   {
   
    color: "#cccccc", fontSize: "20px" }} />
                <Input placeholder="请输入结束日期" defaultValue={
   
   endTime} handleIptFocus={
   
   endIptFocus} />
                <div className="activeBorder"></div>
            </div>
            <div className="date-box">
                <div className="left">
                    <div className="top-bar">
                        <div className="icon">
                            <DoubleLeftOutlined style={
   
   {
   
    cursor: "pointer" }} onClick={
   
   () => preYear('start')} />
                            <LeftOutlined style={
   
   {
   
    marginLeft: "10px", cursor: "pointer" }} onClick={
   
   () => preMonth('start')} />
                        </div>
                        <div className="info">
                            {
   
   startDate.startYear}{
   
   startDate.startMonth}</div>
                        <div>
                            <RightOutlined style={
   
   {
   
    cursor: "pointer" }} onClick={
   
   () => nextMonth('start')}/>
                            <DoubleRightOutlined style={
   
   {
   
    marginLeft: "10px", cursor: "pointer" }} onClick={
   
   () => nextYear('start')} />

                        </div>
                    </div>
                    <div className="week">
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                    </div>
                    <div className="day-list">
                        {
   
   
                            startDayListArray.map((i: string | number) => {
   
   
                                return (
                                    <div className={
   
   i == "" ? "white" : "box-list"} style={
   
   i == startDate.startDay ? activeStyles().activeDay : {
   
   }} onClick={
   
   () => chooseStartDay(Number(i))}>
                                        {
   
   i}
                                    </div>
                                )
                            })
                        }
                    </div>
                </div>
                <div className="right">
                    <div className="top-bar">
                        <div>
                            <DoubleLeftOutlined style={
   
   {
   
    cursor: "pointer" }} onClick={
   
   () => preYear('end')} />
                            <LeftOutlined style={
   
   {
   
    marginLeft: "10px", cursor: "pointer" }} onClick={
   
   () => preMonth('end')} />
                        </div>
                        <div className="info">
                            {
   
   endDate.endYear}{
   
   endDate.endMonth}</div>

                        <div className="icon">
                            <RightOutlined style={
   
   {
   
    cursor: "pointer" }} onClick={
   
   () => nextMonth('end')} />
                            <DoubleRightOutlined style={
   
   {
   
    marginLeft: "10px", cursor: "pointer" }} onClick={
   
   () => nextYear('end')} />
                        </div>
                    </div>
                    <div className="week">
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                        <div></div>
                    </div>
                    <div className="day-list">
                        {
   
   
                            endDayListArray.map(i => {
   
   
                                return (
                                    <div className={
   
   disabledClass(i)} style={
   
   i == endDate.endDay ? activeStyles().activeDay : {
   
   }} onClick={
   
   () => chooseEndDay(Number(i))}>
                                        {
   
   i}
                                    </div>
                                )
                            })
                        }
                    </div>
                </div>
            </div>
        </div>
    )
}

export default memo(RangeDatePicker);

这其实也只是一个最初实现,接下来还会结合一些使用者想法为props提供一些参数,进行更加方便的调用以及多功能的设计。
对于Antd,也有可能是笔者不够细心,也许也提供了这样的props传参设计,没有看到....勿喷~

目录
相关文章
SwiftUI—方便用户选择日期的DatePicker日期拾取器
SwiftUI—方便用户选择日期的DatePicker日期拾取器
1663 0
SwiftUI—方便用户选择日期的DatePicker日期拾取器
|
5月前
Element UI【组件拓展】el-datetime-picker-before 禁止选择未来时间的日期时间选择器(精确到时分秒)
Element UI【组件拓展】el-datetime-picker-before 禁止选择未来时间的日期时间选择器(精确到时分秒)
178 1
|
7月前
左右可以滑动半年的超级日历,支持日历部分收起和自动重定向为北京时间
左右可以滑动半年的超级日历,支持日历部分收起和自动重定向为北京时间
50 0
|
JavaScript 前端开发
JavaScript获取默认显示当日日期
JavaScript获取默认显示当日日期
77 0
日历控件input框默认显示当日日期
日历控件input框默认显示当日日期
82 0
|
JavaScript
关于Element-Ui 时间范围选择器DatePicker禁用范围日期问题
饿了么组件库的[DatePicker](https://element.eleme.cn/#/zh-CN/component/date-picker) 日期范围选择,
|
JavaScript 前端开发 UED
【组件封装】显示实时时间和星期几·附文字特效
【组件封装】显示实时时间和星期几·附文字特效
【组件封装】显示实时时间和星期几·附文字特效
C# DateTimePicker控件如何精确设置显示时分秒
C# DateTimePicker控件如何精确设置显示时分秒
589 0
C# DateTimePicker控件如何精确设置显示时分秒
|
JavaScript 前端开发
在页面中添加两个 <select> 标签,用来显示年份和月份;同时添加两个 <ul> 标签,一个用来显示星期,另一个用来显示日期 在 JavaScript 脚本中动态添加年份和月份,获取当前日期的年份
在页面中添加两个 <select> 标签,用来显示年份和月份;同时添加两个 <ul> 标签,一个用来显示星期,另一个用来显示日期 在 JavaScript 脚本中动态添加年份和月份,获取当前日期的年份
350 0
在页面中添加两个 <select> 标签,用来显示年份和月份;同时添加两个 <ul> 标签,一个用来显示星期,另一个用来显示日期 在 JavaScript 脚本中动态添加年份和月份,获取当前日期的年份