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