ng-zorro的date-range-picker禁用范围外的日期

需求:

日期:支持筛选自然天、自然周、自然月、自然季度

        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
  }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值