import { debounceTime, auditTime } from 'rxjs/operators'; import { NotifyService, NotifyOptions } from '@farris/ui-notify'; import { DateTimeHelperService } from '@farris/ui-common/date'; import { Component, OnInit, Input, EventEmitter, Output, ViewChild, forwardRef, SimpleChanges, ElementRef, HostListener, OnChanges, HostBinding, Injector, ComponentRef, ViewContainerRef, ComponentFactoryResolver, Renderer2, OnDestroy, AfterViewInit, ApplicationRef, ChangeDetectorRef } from '@angular/core'; import { NG_VALUE_ACCESSOR, ControlValueAccessor, NgControl } from '@angular/forms'; import { FarrisDatePickerDirective } from './farris-datepicker.input'; import { IMyOptions, IMyDate, IMyDateModel, IMyShortcuts, IMyDateFormat, IMyCalendarViewChanged, IMyRangeDateSelection } from './interfaces/public-api'; import { DefaultView, ShowType, CalToggle } from './enums/public-api'; import { UtilService, DefaultConfigService, DatepickerLocaleService, DatePickerService } from './services/public-api'; import { LocaleService } from '@farris/ui-locale'; import { format, isValid, parse } from 'date-fns'; import { CalendarComponent } from './components/calendar/calendar.component'; import { CLICK, EMPTY_STR } from './constants/constants'; import { of, Subject, Subscription } from 'rxjs'; const FARRIS_DP_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FarrisDatepickerComponent), multi: true }; @Component({ selector: 'farris-datepicker', exportAs: 'farris-datepicker', templateUrl: './farris-datepicker.component.html', styles: [ ` :host { display: block; } :host .datepicker-clear { position: absolute; height: 100%; right: 0; top: 0; z-index: 100; } ` ], providers: [FARRIS_DP_VALUE_ACCESSOR, UtilService, DefaultConfigService, LocaleService] }) export class FarrisDatepickerComponent implements OnInit, ControlValueAccessor, OnChanges, OnDestroy, AfterViewInit { @HostBinding('class') cls = 'f-cmp-datepicker f-cmp-inputgroup'; // 是否禁用该组件 @Input() disabled = false; // 是否只读 @Input() readonly = false; // 是否只能选择数据,不可键入 @Input() editable = true; // 是否使用日期范围输入模式 @Input() dateRange = false; // 是否使用时间输入框 @Input() showTime = false; // 显示类型 @Input() showType: ShowType = ShowType.all; // 本地语言 @Input() locale = 'zh-cn'; // 日期格式 @Input() set dateFormat(value: string) { if (value) { // value 格式形如 yyyy-MM-dd HH:mm:ss // const formArr = 'yyyy-MM-dd HH:mm:ss'.split(' '); value = value.replace(/^\s+|\s+$/g, ''); // 去掉左右空格 value = value.replace(/\s+/g, ' '); // 将中间的多个连续空格,替换为1个空格 const formArr = value.split(' '); this._dateFormat = formArr[0]; // 处理'yyyy-MM-dd h:mm:ss a'即12小时显示的情况 if (formArr.length === 3) { this._timeFormat = 'h:mm:ss a'; } else { this._timeFormat = formArr[1]; } } // this._dateFormat = this._dateFormat ? (this._dateFormat + '').replace(/m/g, 'M') : ''; if (this._dateFormat) { const mm = ['yyyy-mm', 'yyyy/mm', 'yyyy年mm', 'mm/yyyy']; const _mm = mm.find(m => this._dateFormat.includes(m)); if (_mm) { this._dateFormat = this._dateFormat.replace(_mm, _mm.replace(/m/g, 'M')); } if (this._timeFormat && this.showTime) { this._dateFormat += ' ' + this._timeFormat; if (this._timeFormat.indexOf('时') > -1) { this._timeFormat = this._timeFormat.replace('时', ':').replace('分', ':').replace('秒', ''); } } } // this.dateOpts.dateFormat = this._dateFormat; } get dateFormat(): string { return this._dateFormat; } // 是否使用默认值 @Input() useDefault = false; @Input() returnType: 'Object' | 'Date' | 'String' = 'String'; @Input() returnFormat: string; // placeholder @Input() placeholder = ''; @Input() beginPlaceholder = ''; @Input() endPlaceholder = ''; /** 输入值变化后关闭选择面板。 默认为 true */ @Input() whenValueChangedThenCloseSelectorPanel = true; // 最小日期 // 加入接收时分秒功能 @Input() set minDate(val: any) { const type = Object.prototype.toString.call(val); let date: Date; switch (type) { case '[object Date]': date = val; break; case '[object String]': if (val) { date = new Date(val); } else { date = new Date(1840, 1, 1, 0, 0, 0); } break; case '[object Null]': date = new Date(1840, 1, 1, 0, 0, 0); break; // case null: // date = new Date(1840, 1, 1, 0, 0, 0); // break; case '[object Undefined]': date = new Date(1840, 1, 1, 0, 0, 0); break; default: date = new Date(1840, 1, 1, 0, 0, 0); break; } if (isValid(date)) { const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); const hour = date.getHours(); const minute = date.getMinutes(); const second = date.getSeconds(); this._minDate = { year, month, day, hour, minute, second }; } } get minDate() { return this._minDate; } // 最大日期 // 加入接收时分秒功能 @Input() set maxDate(val: any) { const type = Object.prototype.toString.call(val); let date: Date; switch (type) { case '[object Date]': date = val; break; case '[object String]': if (val) { date = new Date(val); } else { date = new Date(10000, 1, 1, 0, 0, 0); } break; case '[object Null]': date = new Date(10000, 1, 1, 0, 0, 0); break; // case null: // date = new Date(10000, 1, 1, 0, 0, 0); // break; case '[object Undefined]': date = new Date(10000, 1, 1, 0, 0, 0); break; default: date = new Date(10000, 1, 1, 0, 0, 0); break; } if (isValid(date)) { const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); const hour = date.getHours(); const minute = date.getMinutes(); const second = date.getSeconds(); this._maxDate = { year, month, day, hour, minute, second }; } } get maxDate() { return this._maxDate; } // 需要高亮显示的日期 @Input() highlightDates: Array = []; // 禁用区域的日期开始时间 @Input() disableDateRangesBegin: IMyDate = { year: 0, month: 0, day: 0 }; // 禁用区域的日期结束时间 @Input() disableDateRangesEnd: IMyDate = { year: 0, month: 0, day: 0 }; // 禁用的日期 @Input() disableDates: Array = []; // 一周中被禁用的日子 @Input() disableWeekdays: Array = []; // 被标记的日期 @Input() markDates: Array = []; // 被标记的日期的显示颜色 @Input() markDatesColor = '#59a1ff'; // 是否显示第几周 @Input() showWeekNumbers = false; // 是否标记周末 @Input() isMarkWeekends = false; // 被标记周末的显示颜色 @Input() markWeekendsColor = '#59a1ff'; // 日期范围模式下input的显示分割符合 @Input() dateRangeDatesDelimiter = '~'; // 配置左侧 sidebar 快捷键 @Input() shortcuts: Array = []; // 日期范围选择时,默认结束月份为当前月份 + monthRangeValue @Input() monthRangeValue = 1; // 日期范围:开始日期 @Input() set beginValue(value: string | Date) { value = value ? value : ''; if (value instanceof Date) { value = this.dateTo(value); this.returnType = 'Date'; } else { if (value.indexOf('T') > -1) { value = value.replace('T', ' '); } this.returnType = 'String'; } this._beginValue = value; if (this._initFinished) { this.setValue(this._beginValue + this.dateRangeDatesDelimiter + this._endValue); } } get beginValue() { return this.dateFrom(this._beginValue, true); } // 日期范围:结束日期 @Input() set endValue(value: string | Date) { value = value ? value : ''; if (value instanceof Date) { value = this.dateTo(value); this.returnType = 'Date'; } else { if (value.indexOf('T') > -1) { value = value.replace('T', ' '); } this.returnType = 'String'; } this._endValue = value; if (this._initFinished) { this.setValue(this._beginValue + this.dateRangeDatesDelimiter + this._endValue); } } get endValue() { return this.dateFrom(this._endValue, true); } // clear 事件 @Output() clear = new EventEmitter(); // valueChanged 事件 @Output() valueChange = new EventEmitter(); // focus 事件 @Output() focus = new EventEmitter(); // blur 事件 @Output() blur = new EventEmitter(); // beginValueChange @Output() beginValueChange = new EventEmitter(); // endValueChange @Output() endValueChange = new EventEmitter(); // PrevFocus dateRange 第一个日期 @Output() PrevFocus = new EventEmitter(); // NextFocus dateRange 第二个日期 @Output() NextFocus = new EventEmitter(); // close 事件 @Output() close = new EventEmitter(); @ViewChild(FarrisDatePickerDirective) dp: FarrisDatePickerDirective; @ViewChild('clearIcon') clearIcon: ElementRef; @ViewChild('calendar', { read: ViewContainerRef }) calendarRef: ViewContainerRef; @ViewChild('onPrevFocus') PFocus: ElementRef; @ViewChild('onNextFocus') NFocus: ElementRef; @ViewChild('dateInput') dateInput: ElementRef; private cRef: ComponentRef = null; private _timeFormat = ''; private _value = ''; public _beginValue = ''; public _endValue = ''; private _initFinished = false; private _timer: any; private _dateFormat: string; private _minDate: IMyDate = { year: 1840, month: 1, day: 1, hour: 0, minute: 0, second: 0 }; private _maxDate: IMyDate = { year: 10000, month: 1, day: 1, hour: 0, minute: 0, second: 0 }; set value(v) { this._value = v ? v : ''; } get value() { return this._value; } dateOpts: IMyOptions = {}; defaultConfig: IMyOptions; localDateOpts: IMyOptions; localeConfig: DatepickerLocaleService; localeService: LocaleService; // 开始输入控件聚焦 beginFocus = false; // 结束输入控件聚焦 endFocus = false; // 整体聚焦 totalFocus = false; originTime: any; // 是否字符超长显示Tip isActiveTip = false; private vcRef: ViewContainerRef; private cfr: ComponentFactoryResolver; private renderer: Renderer2; private datePicekrService: DatePickerService; private _realValue = null; private _realRangeValue = null; private notifySer: NotifyService; private formatedValue = ''; dtService: DateTimeHelperService = null; private _applicationRef: ApplicationRef; private _ngControl: NgControl; _updateOn = 'change'; // 日期选择后设置为 true, blur 事件将不会重复执行valueChanged; // 键盘修改时,blur 事件中会执行一次 valueChanged 后,将此值设为 false; private _valueChangeEmitted = false; private _mousedownEvent = null; private closeCalendarHandler = null; private cdr: ChangeDetectorRef = null; /** 缓存通过点击日历的数据,用于的格式化格式不标准时,格式化数据。 */ private _SELECT_DATE_ = null; onModelChange = (obj: any) => { }; onModelTouched = () => { }; constructor( private el: ElementRef, private utilService: UtilService, private defaultConfigService: DefaultConfigService, private injector: Injector ) { this.defaultConfig = this.defaultConfigService.getDefaultConfig(); if (this.injector) { this.localeService = this.injector.get(LocaleService); this.dtService = this.injector.get(DateTimeHelperService); this.notifySer = this.injector.get(NotifyService, null); if (this.notifySer) { this.notifySer.config.position = 'top-center'; } } this.localeConfig = new DatepickerLocaleService(); this.vcRef = this.injector.get(ViewContainerRef, null); this.cfr = this.injector.get(ComponentFactoryResolver, null); this.renderer = this.injector.get(Renderer2, null); this._applicationRef = this.injector.get(ApplicationRef); this.cdr = this.injector.get(ChangeDetectorRef); // this.datePicekrService = this.injector.get(DatePickerService, null) } ngOnInit(): void { this._ngControl = this.injector.get(NgControl, null); this.initPlaceholder(); this.mergeDateOptions(); if (this._beginValue && this._endValue) { this.setValue(this._beginValue + this.dateRangeDatesDelimiter + this._endValue); } this._initFinished = true; this.datePicekrService = new DatePickerService(this.dateOpts); } private initPlaceholder() { if (this.localeService) { this.locale = this.localeService.getValue('locale'); this.localDateOpts = this.localeConfig.getLocaleOptions(this.locale); this.placeholder = this.placeholder ? this.placeholder : this.localDateOpts.placeholder ? this.localDateOpts.placeholder : '请选择日期'; if (!this.beginPlaceholder) { this.beginPlaceholder = this.localDateOpts.range.begin || this.placeholder; } if (!this.endPlaceholder) { this.endPlaceholder = this.localDateOpts.range.end || this.placeholder; } } } private mergeDateOptions() { this.dateOpts = { ...this.defaultConfig, ...this.buildOptions() }; if (this.dateOpts.dateFormat) { if (!this._dateFormat) { this.dateFormat = this.dateOpts.dateFormat; this.dateOpts.dateFormat = this.dateFormat; } } if (this.dateOpts.returnFormat) { if (!this.returnFormat) { this.returnFormat = this.dateOpts.returnFormat; } } } ngOnChanges(changes: SimpleChanges) { if (changes.locale && changes.locale.isFirstChange()) { // this.dp.setLocaleOptions(); this.localDateOpts = this.localeConfig.getLocaleOptions(this.locale); } if (changes) { let flag = false; const inputs = ['showTime', 'showType', 'dateRange', 'maxDate', 'minDate', 'monthRangeValue']; Object.keys(changes).forEach(e => { if (!changes[e].isFirstChange() && (this.dateOpts[e] || inputs.includes(e))) { // this[e] = changes[e].currentValue; flag = true; } }); if (flag) { this.dateOpts = { ...this.defaultConfig, ...this.buildOptions() }; } } if ( changes.readonly && !changes.readonly.currentValue && !changes.readonly.firstChange && !this.value && this.useDefault ) { this.setDefaultValue(); } } ngAfterViewInit() { if (this._ngControl && this._ngControl.control) { this._updateOn = this._ngControl.control.updateOn; } this._timer = setTimeout(() => { this.setDefaultValue(); }); } ngOnDestroy(): void { clearTimeout(this._timer); this.closeSelector(); } @HostListener('mouseenter', ['$event']) onMouseEnter(event) { if (this.value && !this.readonly && !this.disabled) { if (this.value) { this.clearIcon.nativeElement.style.visibility = ''; } if (this.dateOpts.dateRange) { if (this.dateOpts.showTime) { this.isActiveTip = this.el.nativeElement.getBoundingClientRect().width < 300; } else { this.isActiveTip = this.el.nativeElement.getBoundingClientRect().width < 190; } } else { if (this.dateOpts.showTime) { this.isActiveTip = this.el.nativeElement.getBoundingClientRect().width < 170; } else { this.isActiveTip = this.el.nativeElement.getBoundingClientRect().width < 120; } } } } @HostListener('mouseleave', ['$event']) onMouseLeave(event) { if (!this.readonly && !this.disabled) { this.clearIcon.nativeElement.style.visibility = 'hidden'; } } onBlur(e) { this.totalFocus = false; let _runblur = true; if (this.value) { try { this.value = this.transform(this.value); const valid = this._isValid(this.value); if (!valid) { this.clearDate(); _runblur = false; } } catch (e) { this.clearDate(); _runblur = false; } } const updateModel = () => { if (!this._valueChangeEmitted) { this.onNgModelChange(this.value, null, true); this.onModelTouched(); } }; if (this.editable && _runblur) { if (this['inDatagrid']) { updateModel(); } else { setTimeout(updateModel, 250); } } this._valueChangeEmitted = false; this._mousedownEvent(); this._mousedownEvent = null; this.blur.emit(); } onFocus(e) { this.totalFocus = true; this.registerMouseDownHandle(); this.focus.emit(); } onInputClick(event) { event.stopPropagation(); if (!this.readonly && !this.disabled) { this.registerMouseDownHandle(); this.toggleCalendar(false); } } private registerMouseDownHandle() { if (!this._mousedownEvent) { this._mousedownEvent = this.renderer.listen(document, 'mousedown', (e: any) => { if (e.target.closest('.f-cmp-datepicker') || e.target.closest('.f-datepicker-container') || e.target.className.indexOf('f-icon-date') > -1) { if (this.dateInput) { this.renderer.setStyle(this.dateInput.nativeElement, 'unselectable', 'on'); } e.preventDefault(); } }); } } setDefaultValue() { if (!this.readonly && !this.value && this.useDefault) { const now = new Date(); let nowValue = ''; if (!this.dateOpts.dateRange) { nowValue = this.dateTo(now); } else { nowValue = this.dateTo(now) + this.dateRangeDatesDelimiter + this.dateTo(new Date(now.setMonth(now.getMonth() + 1))); } if (this.datePicekrService.validate(nowValue)) { this.value = nowValue; this.onNgModelChange(this.value); this.onModelTouched(); } } } clearDate($event: MouseEvent = null) { if ($event) { $event.stopPropagation(); } this._SELECT_DATE_ = null; if (!this.disabled) { this._realRangeValue = null; this._realValue = null; this.onDateChanged({ isRange: this.dateOpts.dateRange, singleDate: { date: this.utilService.resetDate(), jsDate: null, formatted: EMPTY_STR, epoc: 0 }, dateRange: { beginDate: this.utilService.resetDate(), beginJsDate: null, beginEpoc: 0, endDate: this.utilService.resetDate(), endJsDate: null, endEpoc: 0, formatted: EMPTY_STR } }); this.closeSelector(CalToggle.CloseByCalBtn); this.onModelChange(null); this.clear.emit(); if (this.clearIcon) { this.clearIcon.nativeElement.style.visibility = 'hidden'; } } } onSingleInputNgModelChange($event) { this.value = $event || ''; this._realValue = this.value ? this.dateFrom($event) : ''; this.closeSelector(); } onNgModelChange(value: any, realdate = null, emitValueChange = false) { // this.value = value; if (this.dateRange || this.showType == ShowType.selectWeek) { const tmpArr = value ? value.split(this.dateRangeDatesDelimiter) : ['', '']; const _beginValue = this.transform(tmpArr[0], this.returnFormat); const _endValue = this.transform(tmpArr[1], this.returnFormat); this._beginValue = this.transform(tmpArr[0]); this._endValue = this.transform(tmpArr[1]); value = _beginValue && _endValue ? _beginValue + this.dateRangeDatesDelimiter + _endValue : ''; this._realRangeValue = value; this.onModelChange(value); this.cdr.detectChanges(); this.beginValueChange.emit(this._beginValue); this.endValueChange.emit(this._endValue); const p = { date: null, formatted: this.value, returnFormatted: this._realRangeValue, cmpRef: this }; this.valueChange.emit(p); } else { if (value === '') { this.value = ''; this._onModelChange(null, emitValueChange); return; } if (realdate) { this._realValue = this.getRealReturnFormatted(realdate); this._onModelChange(this._realValue, emitValueChange); if (this.dateInput && this.dateInput.nativeElement.value !== this.value) { this.renderer.setProperty(this.dateInput.nativeElement, 'value', this.value); } } else { if (this._isValid(value)) { this._realValue = this.getRealReturnFormatted(realdate); if (this.dateInput) { this.renderer.setProperty(this.dateInput.nativeElement, 'value', this.value); } this._onModelChange(this._realValue, emitValueChange); } } } } private _onModelChange(realValue, emit) { this._realValue = realValue; this.onModelChange(realValue); if (emit) { const p = { date: this.utilService.isDateValid(this.value, this.dateOpts, true), formatted: this.value, returnFormatted: realValue, cmpRef: this }; if (this.valueChange.observers.length) { if (this.whenValueChangedThenCloseSelectorPanel) { this.closeSelector(); } this.valueChange.emit(p); } } } private getRealReturnFormatted(realdate: any) { if (realdate) { if (this.returnType === 'Date') { return realdate; } else { if (this.returnFormat) { return this.dtService.formatTo(realdate, this.returnFormat); } return realdate; } } else { return this.dateFrom(this._realValue || this.value) || ''; } } onDateRangeInputBlur(event: any) { this.setValue(this._beginValue + this.dateRangeDatesDelimiter + this._endValue); this.onNgModelChange(this._realRangeValue || this.value); } openCalendar(): void { if (this.disabled) { return; } let val = this._realValue; if (this.dateRange) { val = this._realRangeValue; } else { if (this.returnType === 'Date') { val = this.transform(val, this.returnFormat); } } if (this.cRef === null) { this.cRef = this.calendarRef.createComponent( this.cfr.resolveComponentFactory(CalendarComponent) ); this.datePicekrService.appendSelector(this.cRef.location.nativeElement); this.datePicekrService.registerScrollEvent(() => { this.closeSelector(); }); this.cRef.instance.initialize( this.dateOpts, '', this.datePicekrService.getSelectorPosition(this.el.nativeElement, this.cRef), val, (dm: IMyDateModel, close: boolean) => { if (this.dateOpts.dateRange && this.showTime && dm.dateRange.endJsDate < dm.dateRange.beginJsDate) { // const { year: beginYear, month: beginMonth, day: beginDay } = {...dm.dateRange.beginDate}; // const { year: endYear, month: endMonth, day: endDay } = {...dm.dateRange.endDate}; // error message: The end time must not be earlier than the start time // error message: 結束時間不得早于開始時間 const msg = this.localDateOpts.message['101']; if (this.notifySer) { this.notifySer.warning(msg); } else { alert(msg); } return; } this.onDateChanged(dm); if (close) { this.closeSelector(CalToggle.CloseByDateSel); } }, (cvc: IMyCalendarViewChanged) => { // this.emitCalendarChanged(cvc); }, (rds: IMyRangeDateSelection) => { // this.emitRangeDateSelection(rds); }, () => { this.closeSelector(CalToggle.CloseByEsc); } ); // this.cdr.detectChanges(); } else { this.datePicekrService.appendSelector(this.cRef.location.nativeElement); } this.cRef.changeDetectorRef.detectChanges(); // this.preventClose = false; } closeSelector(reason?: number): void { if (this.cRef !== null) { this.calendarRef.remove(this.calendarRef.indexOf(this.cRef.hostView)); this.cRef = null; // this.cdr.detectChanges(); const container = document.querySelector('.date-overlay-container'); if (container.childElementCount) { container.remove(); } this.renderer.setStyle(container, 'pointer-events', ''); } this.totalFocus = false; if (this.dateOpts.dateRange) { this.beginFocus = false; this.endFocus = false; } if (this.closeCalendarHandler) { this.closeCalendarHandler(); this.closeCalendarHandler = null; } if (this.datePicekrService) { this.datePicekrService.removeMouseEvent(); } } toggleCalendar(close: boolean, emit = true) { if (this.disabled) { this.totalFocus = false; if (this.dateOpts.dateRange) { this.beginFocus = false; this.endFocus = false; } return; } if (this.cRef === null) { if (!this.closeCalendarHandler) { this.closeCalendarHandler = this.renderer.listen(document, CLICK, () => this.closeSelector(CalToggle.CloseByOutClick)); } this.totalFocus = true; this.openCalendar(); const container = document.querySelector('.date-overlay-container'); this.renderer.setStyle(container, 'pointer-events', 'auto'); } else { let flag = true; const container = document.querySelector('.date-overlay-container'); if (container && this.cRef.location) { if (container.hasChildNodes()) { container.childNodes.forEach(el => { if (el === this.cRef.location.nativeElement) { flag = false; } }); } } if (flag) { this.renderer.setStyle(container, 'pointer-events', 'auto'); this.totalFocus = true; this.openCalendar(); } else { this.totalFocus = false; if (this.dateOpts.dateRange) { this.beginFocus = false; this.endFocus = false; } // document.removeEventListener(CLICK, this.onClickHidden); close && this.closeSelector(CalToggle.CloseByCalBtn); // this.renderer.setStyle(container, 'pointer-events', '') if (emit) { this.close.emit(); } } } } onDateChanged(event: IMyDateModel) { this._SELECT_DATE_ = event; let valChangeParams: any = ''; if (event.singleDate) { this.value = event.singleDate.formatted; this.formatedValue = event.singleDate.formatted; const returnFormatted = this.getRealReturnFormatted(event.singleDate.jsDate); valChangeParams = { ...event.singleDate, formatted: event.singleDate.formatted, returnFormatted }; const { year, month, day, hour, minute, second } = event.singleDate.date; if (month && (this.dateFormat.indexOf('yyyy') === -1 || this.dateFormat.indexOf('dd') === -1)) { const nd = new Date( year || new Date().getFullYear(), month - 1, day || 1, hour || 0, minute || 0, second || 0 ); event.singleDate.jsDate = nd; } this.onNgModelChange(returnFormatted, event.singleDate.jsDate); } else if (event.dateRange) { this.value = event.dateRange.formatted; this.formatedValue = event.dateRange.formatted; const returnFormatted = event.dateRange.returnFormatted; // let returnFormatted = this.dateFrom(event.dateRange.returnFormatted); valChangeParams = { ...event.dateRange, formatted: event.dateRange.formatted, returnFormatted }; this.onNgModelChange(returnFormatted, event.dateRange); } this.onModelTouched(); this._valueChangeEmitted = true; this.valueChange.emit(valChangeParams); } buildOptions() { let dateOpts: any; if (!this.localDateOpts) { this.localDateOpts = this.localeConfig.getLocaleOptions(this.locale); } if (this.showType === ShowType.noDateAndMonth) { this.localDateOpts.dateFormat = 'yyyy'; this.localDateOpts.returnFormat = 'yyyy'; } const _showTime = (Number(this.showType) === ShowType.all) ? this.showTime : false; dateOpts = { dateRange: this.dateRange, showTime: _showTime, showType: Number(this.showType), dateFormat: this.dateFormat ? this.dateFormat : this.localDateOpts.dateFormat, returnFormat: this.returnFormat ? this.returnFormat : this.localDateOpts.returnFormat, minYear: this._minDate.year, maxYear: this._maxDate.year, highlightDates: this.highlightDates, disableDates: this.disableDates, showWeekNumbers: this.showWeekNumbers, disableDateRanges: [ { begin: this.disableDateRangesBegin, end: this.disableDateRangesEnd } ], disableUntil: this._minDate, disableSince: this._maxDate, disableWeekdays: this.disableWeekdays, markDates: [ { dates: this.markDates, color: this.markDatesColor } ], markWeekends: { marked: this.isMarkWeekends, color: this.markWeekendsColor }, dateRangeDatesDelimiter: this.dateRangeDatesDelimiter, shortcuts: this.shortcuts, timeFormat: this._timeFormat, monthRangeValue: this.monthRangeValue }; switch (Number(this.showType)) { case ShowType.noDate: dateOpts.defaultView = DefaultView.Month; break; case ShowType.noDateAndMonth: dateOpts.defaultView = DefaultView.Year; // dateOpts.dateFormat = 'yyyy'; // dateOpts.returnFormat = 'yyyy'; // this.dateFormat = 'yyyy'; break; case ShowType.selectWeek: dateOpts.showWeekNumbers = true; dateOpts.dateRange = true; dateOpts.firstDayOfWeek = 'mo'; break; // default: // this.returnType = 'String'; } return { ...this.localDateOpts, ...dateOpts }; } dateTo(d: any) { const dateFormat = this.dateOpts.dateFormat ? this.dateOpts.dateFormat : this.defaultConfig.dateFormat; const monthLabels = this.dateOpts.monthLabels ? this.dateOpts.monthLabels : this.defaultConfig.monthLabels; const showTime = this.dateOpts.showTime ? this.dateOpts.showTime : this.defaultConfig.showTime; const year = d.getFullYear(); const month = d.getMonth() + 1; const day = d.getDate(); const hour = d.getHours(); const minute = d.getMinutes(); const second = d.getSeconds(); let date: IMyDate; if (!showTime) { date = { year, month, day }; this.originTime = { hour, minute, second }; } else { date = { year, month, day, hour, minute, second }; } return this.utilService.formatDate(date, dateFormat, monthLabels); } // 返回真实日期 Date 类型 dateFrom(str: any, ignorRange = false) { if (!str) { return ''; } const _this = this; const dateRange = this.dateOpts.dateRange ? this.dateOpts.dateRange : this.defaultConfig.dateRange; const dateFormat = this.dateOpts.dateFormat ? this.dateOpts.dateFormat : this.defaultConfig.dateFormat; const returnFormat = this.dateOpts.returnFormat ? this.dateOpts.returnFormat : this.defaultConfig.returnFormat; const showTime = this.dateOpts.showTime ? this.dateOpts.showTime : this.defaultConfig.showTime; const delimeters: Array = returnFormat.match(/[^(DdMmYy)]{1,}/g); let dateValue: Array; const getValue = this.utilService.getDateValue; if ((dateRange || this.dateOpts.showType === ShowType.selectWeek) && !ignorRange) { const tmpBegin = str.split(this.dateRangeDatesDelimiter)[0]; const tmpEnd = str.split(this.dateRangeDatesDelimiter)[1]; let tmpBeginDate = this.utilService.isDateValid(tmpBegin, this.dateOpts, true); let tmpEndDate = this.utilService.isDateValid(tmpEnd, this.dateOpts, true); tmpBeginDate = this.utilService.isInitializedDate(tmpBeginDate) ? tmpBeginDate : this.dateOpts.disableUntil; tmpEndDate = this.utilService.isInitializedDate(tmpEndDate) ? tmpEndDate : this.dateOpts.disableSince; return ( this.utilService.formatDate(tmpBeginDate, returnFormat, this.defaultConfig.monthLabels) + this.dateRangeDatesDelimiter + this.utilService.formatDate(tmpEndDate, returnFormat, this.defaultConfig.monthLabels) ); } else { if (this.returnType === 'Date') { if (str && str instanceof Date) { return str; } return convert(str, getValue, this.originTime); } else { const tmpDate = this.utilService.isDateValid(str, this.dateOpts, true); return this.utilService.formatDate(tmpDate, returnFormat, this.defaultConfig.monthLabels); } } function convert(v, _getValue: any, originTime = { hour: 0, minute: 0, second: 0 }) { let fmt = returnFormat; if (_this.returnType === 'Date') { fmt = 'yyyy-MM-dd' + (showTime ? ' HH:mm:ss' : ''); } if (showTime) { const date = v.split(' ')[0]; let time = v.split(' ')[1]; dateValue = _getValue(date, fmt, delimeters); if (time) { time = time.replace(/[时,分]/g, ':').replace(/[秒]/, ''); const [h, m, s] = time.split(':'); const hour = h; const minute = m; const second = s ? s : ''; dateValue[3] = { value: hour, format: 'hh' }; dateValue[4] = { value: minute, format: 'mm' }; if (second) { dateValue[5] = { value: second, format: 'ss' }; } } } else { const date = v.split(' ')[0]; dateValue = _getValue(date, fmt, delimeters); } let year = Number(dateValue[0] ? dateValue[0].value : 1970); const yearValue = dateValue[0]; if (yearValue.format) { if (!yearValue.value || yearValue.value.length !== yearValue.format.length) { year = null; } } const month = Number(dateValue[1] ? dateValue[1].value : 1) - 1; const day = Number(dateValue[2] ? dateValue[2].value : 1); const _hour = dateValue[3] ? Number(dateValue[3].value) : originTime.hour; const _minute = dateValue[4] ? Number(dateValue[4].value) : originTime.minute; const _second = dateValue[5] ? Number(dateValue[5].value) : originTime.second; if (!year) { return null; } return new Date(year, month, day, _hour, _minute, _second); } } transform(value: any, _fmt = this.dateOpts.dateFormat) { value = value ? value : ''; const { returnFormat, dateFormat, showTime } = this.dateOpts; let _tmpDate = null; if (typeof value === 'string') { _tmpDate = parse(value, returnFormat, new Date()); if (!isValid(_tmpDate)) { if (this._SELECT_DATE_ && this._SELECT_DATE_.singleDate) { _tmpDate = this._SELECT_DATE_.singleDate.jsDate; } } } else { if (value instanceof Date) { _tmpDate = value; } } let formattedVal = value; if (isValid(_tmpDate)) { formattedVal = this.dtService.formatTo(_tmpDate, _fmt); } else { // 兼容年月日,年月,年格式 value = value.replace(/[年,月]/g, '-').replace(/[日]/, ''); if (value[value.length - 1] === '-') { value = value.substr(0, value.length - 1); } if (isValid(new Date(value))) { formattedVal = this.dtService.formatTo(value, _fmt); } else { value = ''; } } if (value) { if (this.dateFrom(value, true) === '') { const tmpDate = this.utilService.isDateValid( value, Object.assign({}, this.dateOpts, { dateFormat: returnFormat }) ); formattedVal = this.utilService.formatDate(tmpDate, _fmt, this.defaultConfig.monthLabels); } } return formattedVal; } setValue(value: string) { if (this.value !== value) { if (this.dateOpts.dateRange) { const _tmpDateArr = value.split(this.dateRangeDatesDelimiter); const beginStr = _tmpDateArr[0]; const endStr = _tmpDateArr[1]; this._beginValue = this.transform(beginStr); this._endValue = this.transform(endStr); this.value = this._beginValue && this._endValue ? this._beginValue + this.dateRangeDatesDelimiter + this._endValue : ''; } else { if (value) { this.value = this.transform(value); } else { this.value = ''; } } } } updateValue(value: string) { if (this.dateOpts.dateRange) { const _tmpDateArr = value.split(this.dateRangeDatesDelimiter); const beginStr = _tmpDateArr[0]; const endStr = _tmpDateArr[1]; this._beginValue = this.transform(beginStr); this._endValue = this.transform(endStr); this.value = this._beginValue && this._endValue ? this._beginValue + this.dateRangeDatesDelimiter + this._endValue : ''; } else { this.value = this.transform(value); } } getValueByType(val) { if (val instanceof Date) { this.returnType = 'Date'; // return this.dateTo(val); return val; } else if (val && val instanceof Object) { const { begin, end } = val; const beginStr = begin instanceof Date ? this.dateTo(begin) : ''; const endStr = begin instanceof Date ? this.dateTo(end) : ''; this.returnType = 'Object'; return beginStr && endStr ? beginStr + this.dateRangeDatesDelimiter + endStr : ''; } else if (val && typeof val === 'string') { if (val.indexOf('T') > -1) { val = val.replace('T', ' '); } this.returnType = 'String'; return val; } else { return ''; } } writeValue(val: any): void { this._realValue = val; if (this.dateRange) { this._realRangeValue = val; } this.setValue(this.getValueByType(val)); } registerOnChange(fn: any): void { this.onModelChange = fn; } registerOnTouched(fn: any): void { this.onModelTouched = fn; } setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled; } onPrevFocus(value: boolean): void { this.beginFocus = true; if (this.endFocus == true) { this.endFocus = !this.endFocus; } this.PrevFocus.emit(); } onNextFocus(value: boolean): void { this.endFocus = true; if (this.beginFocus == true) { this.beginFocus = !this.beginFocus; } this.NextFocus.emit(); } private _convert2dateString(val: string, realValue = false) { if (val) { let t = val.replace(/[年,月]/g, '-').replace(/[日,号]/g, ' ').replace(/[时,点,分,分钟]/g, ':').replace('秒', ''); if (realValue) { const objDate = this.utilService.isDateValid(val, this.dateOpts, true); if (objDate) { let { year, month, day, hour, minute, second} = objDate; if (!year) { year = new Date().getFullYear(); } if (!month) { month = new Date().getMonth(); } if (!day) { day = 1; } if (!hour) { hour = 0; } if (!minute) { minute = 0; } if (!second) { second = 0; } return `${year}-${month}-${day} ${hour}:${minute}:${second}`; } } if (t[t.length - 1] === '-') { // t = t.substr(0, t.length - 1); t += '01'; } if (t[t.length - 1] === ':') { t = t.substr(0, t.length - 1); } return t.replace(/^\s+|\s+$/g, ''); } return val; } private _isValid(value: any) { value = this._convert2dateString(value); if (value) { if (this.dateFormat.indexOf('yyyy') === -1 || this.dateFormat.indexOf('dd') === -1) { const _tmpDate = parse(this.value, this.dateFormat, new Date()); if (this._realValue) { if (this.returnType === 'Date') { return isValid(this._realValue); } else { return isValid(new Date(this._convert2dateString(this._realValue, true))); } } const d = this.utilService.isDateValid(value, this.dateOpts); if (d) { if (d.year || d.month) { const cdate = new Date(); const year = cdate.getFullYear(); const str = (this.dateOpts.returnFormat || 'yyyy-MM-dd') .replace('yyyy', '' + (d.year || year)).replace('MM', '' + d.month).replace('dd', '1'); return isValid(new Date(str)); } return false; } return isValid(value); } else { const reg = /^\d{1,}$/; if (reg.test(value) && this.dateFormat.indexOf('yyyyMMdd') > -1) { // 验证传入的格式为 yyyyMMddHHmmss const _r = this._isValid2(value); if (_r.isValided) { this._realValue = this.getRealReturnFormatted(_r.date); this.value = this.transform(_r.date); } return _r.isValided; } else { const _tmpDate = parse(value, this.dateFormat, new Date()); const d = this.utilService.isDateValid(this.value, this.dateOpts); return isValid(_tmpDate) || this.utilService.isInitializedDate(d); } } } return false; } private _isValid2(value: string): any { let year = 0; let month = 0; let day = 0; let hour = 0; let minute = 0; let seconds = 0; const df = this.dateFormat; if (df.includes('yyyy')) { if (value) { year = +value.slice(0, 4); value = value.slice(4); } else { return false; } } if (df.includes('MM')) { if (value) { month = +value.slice(0, 2) - 1; value = value.slice(2); if (month < 0) { month = 0; } } else { return false; } } if (df.includes('dd')) { if (value) { day = +value.slice(0, 2); value = value.slice(2); } else { return false; } } if (df.includes('HH')) { if (value) { hour = +value.slice(0, 2); value = value.slice(2); } } if (df.includes('mm')) { if (value) { minute = +value.slice(0, 2); value = value.slice(2); } } if (df.includes('ss')) { if (value) { seconds = +value.slice(0, 2); } } const d = new Date(year, month, day, hour, minute, seconds); return { isValided: isValid(d), date: d }; } }