您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~
在过去的日子里,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传参设计,没有看到....勿喷~
- Concis组件库线上链接:http://react-view-ui.com:92
- github:https://github.com/fengxinhhh/Concis
- npm:https://www.npmjs.com/package/concis