需求:
日期:支持筛选自然天、自然周、自然月、自然季度
1.日期范围选择器,日期段跨度最大 3 个月,最小 1 天
2.周范围选择器,最大跨度 12 周,最小 1 周
3.月份范围选择器:月份段跨度最大 3 个月,最小 1 月
效果图
日期范围选择器:
周范围选择器:
月范围选择器:
组件代码和实现思路
nz-range-picker组件提供了nzOnCalendarChange方法与nzDisabledDate属性,nzOnCalendarChange可以获取组件的日期数组,nzDisabledDate可以禁用指定值,但是nzOnCalendarChange返回的数组中并不会指明是开始日期还是结束日期,返回值可能是长度为0,1,2的数组,所以无法判断当前的选择是开始日期还是结束日期,也就无法确认禁用范围。
以下是解决思路
<nz-range-picker
#groupDatePicker
[nzDisabledDate]="disableFutureDates"
class="time-picker"
[nzMode]="groupParams.dateType"
[(ngModel)]="groupParams.timeRange"
(nzOnCalendarChange)="onDateChange()"
>
</nz-range-picker>
groupParams = new GroupSecondmentDetailsInit(); //初始化参数
moduleType = 'group'
@ViewChild("groupDatePicker") groupDatePicker: NzDatePickerComponent;
chooseStartDate: Date | string = ''
chooseEndDate:Date | string = ''
chooseType: NzDateMode = 'date'
//组件选中时间
onDateChange() {
this.chooseType = this.groupDatePicker.nzMode
if(this.moduleType === 'group') {
this.chooseStartDate = this.groupDatePicker.inputValue[0] ?? ''
this.chooseEndDate = this.groupDatePicker.inputValue[1] ?? ''
}
}
//查询参数类
class GroupSecondmentDetailsInit {
constructor() {
}
start: String | Date | null = null
end: String | Date | null = null
dateType:String = 'date'
keyword:String = ''
_timeRange: any[] = []
set timeRange(value) {
this.start = value[0] ?? null
this.end = value[1] ?? null
this._timeRange = value
}
get timeRange() {
return this._timeRange
}
}
可以直接拿选择器组件对象中的inputValue属性,它在未选择时为空数组,一旦选择,一定会保证数组长度为2,如果只选择了开始日期,则[null,'日期'],反之只选择了结束日期,则为['日期',null]。
因此,可以判断需要禁用的范围,代码如下
disableFutureDates = (current: any): boolean =>{
let threeMonthsBefore = null
let threeMonthsLater = null
if(this.chooseType === 'date'){
//date以月处理
if(this.chooseEndDate) {
//结束日期的三个月前
threeMonthsBefore = new Date(this.chooseEndDate)
threeMonthsBefore.setMonth(threeMonthsBefore.getMonth() - 3)
}
if(this.chooseStartDate) {
//开始日期的三个月后
threeMonthsLater = new Date(this.chooseStartDate)
threeMonthsLater.setMonth(threeMonthsLater.getMonth() + 3)
}
//如果是date类型,选择器的current会把时间默认为当前选择的时间
// Date格式化出的时间默认为8点,所以需要把current的时间也格式化为8点,避免出现计算错误
current = new Date(current.setHours(8,0,0,0))
}else if(this.chooseType === 'week') {
//week类型以周处理
if(this.chooseEndDate) {
//结束日期的12周前
//以Sunday为一周的开始,所以需要先把结束日期转换为周一,但是由于范围为12周,所以需要在转换为周一后再减去一天
const [year, week] = (this.chooseEndDate as string).split('-').map(Number);
threeMonthsBefore = moment().year(year).week(week).startOf('isoWeek').clone().subtract(11,'weeks').subtract(1,'days')
console.log("结束日期",threeMonthsBefore)
}
if(this.chooseStartDate) {
//开始日期的12周后
//以Sunday为一周的开始,所以需要先把结束日期转换为周一,但是由于范围为12周,所以需要在转换为周一后再减去一天
const [year, week] = (this.chooseStartDate as string).split('-').map(Number);
threeMonthsLater = moment().year(year).week(week).endOf('isoWeek').clone().add(11,'weeks').subtract(1,'days')
}
}else if(this.chooseType === 'month') {
//month类型以月处理
if(this.chooseEndDate) {
//结束日期的三个月前
//以月的开始为1号,所以需要先把结束日期转换为月的开始,但是由于范围为3个月,所以需要在转换为月的开始后再减去一天
threeMonthsBefore = new Date(moment(this.chooseEndDate).clone().subtract(3,'months').add(1,'days').toDate())
}
if(this.chooseStartDate) {
//开始日期的三个月后
//以月的开始为1号,所以需要先把开始日期转换为月的开始,但是由于范围为3个月,所以需要在转换为月的开始后再减去一天
threeMonthsLater = new Date(moment(this.chooseStartDate).clone().add(3,'months').subtract(1,'days').toDate())
}
//转换current为统一格式,消除时分秒的影响
current = new Date(moment(current).clone().startOf('month').toDate())
}
let maxLimitFlag: boolean = false //大于最大日期的标识
let minLimitFlag: boolean = false //小于最大日期的标识
if(threeMonthsLater){
//从开始日期计算
maxLimitFlag = current > threeMonthsLater
}
if(threeMonthsBefore){
//从结束日期计算
minLimitFlag = current < threeMonthsBefore
}
return maxLimitFlag || minLimitFlag
}
此中有很多逻辑,详见注释。
此时还有个问题没解决,如果点击组件的清除按钮,是不会触发nzOnCalendarChange事件的,需要去监听该组件的inputValue(ps:为什么不直接用组件双向绑定的变量groupParams.timeRange呢,因为该变量只有在选择完开始日期和结束日期后,才会被赋值,在选择途中是不会被赋值的,所以如果监听该变量,开始结束日期会一直被清空,见代码),在angular的更新生命周期ngDoCheck中清空选择日期,从而更新disabled计算
ngDoCheck(): void {
//如果日期组件清空
if(!this.groupDatePicker?.inputValue?.length && this.moduleType === 'group'){
this.chooseStartDate = ''
this.chooseEndDate = ''
}
}
当然,在更换选择器类型的时候也需要清空开始和结束日期
//更改时间选择模式
dateTypeChange($event,key) {
//清除选择器的限制条件
this.chooseStartDate = ''
this.chooseEndDate = ''
this[`${key}Params`].timeRange = [] //该处代表groupParams.timeRange
}