antd 两个 DatePicker 组件实现起止时间相互约束的流程

Posted 毕小宝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了antd 两个 DatePicker 组件实现起止时间相互约束的流程相关的知识,希望对你有一定的参考价值。

背景

一个基于 React 前端技术的项目,在做搜索功能时,虽然 antd 有 DateRange 插件,但技术选择用的是两个 DatePicker ,所以需要自己实现起止时间的约束:

  1. 开始时间必须小于结束时间
  2. 结束时间必须大于开始时间,且不超过当日

实现流程

第一,定义两个成员变量和两个 state 属性:

  1. 两个记录当前日期插件选中的时间的变量;
  2. 两个 state 时间变量,初始化日期插件的变量。

第二,起止时间插件各自用到的两个回调函数:

  1. onChangeStart ,开始时间,选择时间变更后,记录对应值
  2. onChangeEnd ,结束时间,选择时间变更后,记录对应值
  3. disableDateStart ,开始时间回调
  4. disableDateEnd ,结束时间的回调

第一,全局变量定义

组件构造函数定义两个成员变量用来记录当前选择的日期,注意是成员变量,而不是 state 的两个属性。

constructor(props) {
	let now = new Date();
	let year = now.getFullYear();
	let month = now.getMonth() + 1 < 10 ? '0' + (now.getMonth() + 1) : now.getMonth() + 1;
	let day = now.getDate() < 10 ? '0' + now.getDate() : now.getDate();
	let defaultStartTime = year + "-" + month + "-" + day + " 00:00:00";
	let defaultEndTime = year + "-" + month + "-" + day + " 23:59:59";

	// 页面需要的常量:当天时间起止值
	this.todayStart = moment(defaultStartTime);
	this.todayEnd = moment(defaultEndTime);

	// 页面 state 数据
	this.state = {
		startTime: this.todayStart,
		endTime: this.todayEnd,
	}

	// 由于 state.xxx 属性设置后容易出现 undefined ,所以用全局类变量
	this.currentStartTime = this.todayStart;
	this.currentEndTime = this.todayEnd; 
}

第二,日期插件引用

组件引用两个日期插件,指定初始值和各自的回调:

<Form ref={this.formRef} onFinish={this.onFinish}>
	<div>开始时间</div>
	<Form.Item name="startTime" initialValue={this.state.startTime}>
		<DatePicker
			showTime={{ format: 'HH:mm:ss' }}
			format="YYYY-MM-DD HH:mm:ss"
			allowClear={false}
			onChange={ this.dateChangeStart}
			disabledDate={this.disabledDateStart}
		/>
	</Form.Item>
	<div>结束时间</div>
	<Form.Item name="endTime" initialValue={this.state.endTime}>
		<DatePicker
			showTime={{ format: 'HH:mm:ss' }}
			format="YYYY-MM-DD HH:mm:ss"
			onChange={ this.dateChangeEnd}
			disabledDate={this.disabledDateEnd}
			allowClear={false}
		/>
        </Form.Item>
</Form>

第三,日期变动回调定义

为起止时间插件,定义 onChange 回调,如果选择了新日期,则记录到全局变量中。

// 点击都会触发该函数,未选择时是 undefiend
dateChangeStart = (current) => {
	if(current != undefined) {
		this.currentStartTime = current; // 修改全局变量,通知日期插件
	}
}

dateChangeEnd = (current) => {
	if(current != undefined) {
		this.currentEndTime= current; // 修改全局变量,通知日期插件
	}
}

第四,禁用时间回调定义

开始时间插件的 disabledDate 根据当前选中的结束日期值设定禁用时间,同理,结束时间插件根据开始时间和当天设定禁用范围。

详细函数定义为:

// 开始时间范围:不能包含今天以后的,且不能超过结束时间
disabledDateStart = (current) => {
	const endValue = this.currentEndTime;//获取结束时间
	if (!endValue) {//如果开始日期或者结束日期都没有选择,限定今日之后的不允许选择
		return current && current.valueOf() > this.todayEnd.valueOf();
	}

	//用户选择了结束时间,则开始时间要小于结束时间
	return current.valueOf() > endValue.valueOf();
}

disabledDateEnd = (current) => {
	const startValue = this.currentStartTime;
	const currentcurrent = this.currentEndTime;

	// 如果用户未选择开始时间,则不能选今天以后的
	if (!startValue) {
		return current && current.valueOf() > this.todayEnd.valueOf();
	}

	// 时间范围在开始时间和当日之间
	return current.valueOf() <= startValue.valueOf() || 
		current.valueOf() > this.todayEnd.valueOf();
}

运行效果

选定开始和结束时间后,查看各自插件的选择范围:
在这里插入图片描述

启示录

思路很简单,就是 disableDate 回调要利用当前日期插件选择的时间,来确定范围。

有一个坑点就是 state 变量,感觉它是异步的,即使刚刚设置了值,disableDate 回调取到的却是 undefined

最初为了记录选中日期,将其设置为 state.curerntStartTime ,结果取值时会出现 undefined 的情况,页面选择几次日期后,禁用时间范围就乱套了。

结论:要实现两个日期插件的选择范围联动,函数中引用的变量应该定义为 this.xxx 属性,而非 this.state.xxx

这可能跟 state 的作用有关吧,它主要是动态回显组件值的,数值更新有滞后!

以上是关于antd 两个 DatePicker 组件实现起止时间相互约束的流程的主要内容,如果未能解决你的问题,请参考以下文章

react+antd,DatePicker组件只选择年月日,得到的时间戳不是当前日期0点

typescript使用antd中RangePicker组件实现时间限制 当前时间的前一年(365天)

typescript使用antd中RangePicker组件实现时间限制 当前时间的前一年(365天)

typescript使用antd中RangePicker组件实现时间限制 当前时间的前一年(365天)

在使用antd遇到的不常见问题(一)

antd DatePicker 为英文时的另类解法