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

  • Post author:
  • Post category:其他




在过去的日子里,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传参设计,没有看到…勿喷~



版权声明:本文为m0_46995864原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。