From 927e8107b324d11733e86f3ee498d93f2cd52c23 Mon Sep 17 00:00:00 2001 From: xuzhendong666 <810792608@Outlook.com> Date: Wed, 30 Jun 2021 23:37:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A51.2=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E9=97=B4=E8=8C=83=E5=9B=B4=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E6=94=B9=E5=8A=A8=20(#2183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DateRangePicker.tsx | 56 ++++++++++++----- src/components/MonthRangePicker.tsx | 80 ++++++++++++++++++++++-- src/components/calendar/Calendar.tsx | 17 ++++- src/components/calendar/QuartersView.tsx | 52 +++++---------- 4 files changed, 148 insertions(+), 57 deletions(-) diff --git a/src/components/DateRangePicker.tsx b/src/components/DateRangePicker.tsx index ac6cb634f..14114f546 100644 --- a/src/components/DateRangePicker.tsx +++ b/src/components/DateRangePicker.tsx @@ -45,6 +45,7 @@ export interface DateRangePickerProps extends ThemeProps, LocaleProps { popOverContainer?: any; dateFormat?: string; embed?: boolean; + viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters'; } export interface DateRangePickerState { @@ -54,7 +55,7 @@ export interface DateRangePickerState { endDate?: moment.Moment; } -const availableRanges: {[propName: string]: any} = { +export const availableRanges: {[propName: string]: any} = { 'today': { label: 'Date.today', startDate: (now: moment.Moment) => { @@ -262,6 +263,7 @@ export class DateRangePicker extends React.Component< this.handleKeyPress = this.handleKeyPress.bind(this); this.handlePopOverClick = this.handlePopOverClick.bind(this); this.renderDay = this.renderDay.bind(this); + this.renderQuarter = this.renderQuarter.bind(this); const {format, joinValues, delimiter, value} = this.props; this.state = { @@ -271,11 +273,11 @@ export class DateRangePicker extends React.Component< }; } - componentWillReceiveProps(nextProps: DateRangePickerProps) { + componentDidUpdate(prevProps: DateRangePickerProps) { const props = this.props; - const {value, format, joinValues, delimiter} = nextProps; + const {value, format, joinValues, delimiter} = props; - if (props.value !== value) { + if (prevProps.value !== value) { this.setState({ ...DateRangePicker.unFormatValue(value, format, joinValues, delimiter) }); @@ -384,6 +386,8 @@ export class DateRangePicker extends React.Component< value = value[type === 'start' ? 'startOf' : 'endOf']('minute'); } else if (typeof timeFormat === 'string' && /HH/i.test(timeFormat)) { value = value[type === 'start' ? 'startOf' : 'endOf']('hour'); + } else if (typeof timeFormat === 'string' && /Q/i.test(timeFormat)) { + value = value[type === 'start' ? 'startOf' : 'endOf']('quarter'); } else { value = value[type === 'start' ? 'startOf' : 'endOf']('day'); } @@ -527,8 +531,8 @@ export class DateRangePicker extends React.Component< checkStartIsValidDate(currentDate: moment.Moment) { let {endDate, startDate} = this.state; - - let {minDate, maxDate, minDuration, maxDuration} = this.props; + let {minDate, maxDate, minDuration, maxDuration, viewMode} = this.props; + const precision = viewMode === 'time' ? 'hours' : viewMode || 'day'; maxDate = maxDate && endDate @@ -537,9 +541,9 @@ export class DateRangePicker extends React.Component< : endDate : maxDate || endDate; - if (minDate && currentDate.isBefore(minDate, 'day')) { + if (minDate && currentDate.isBefore(minDate, precision)) { return false; - } else if (maxDate && currentDate.isAfter(maxDate, 'day')) { + } else if (maxDate && currentDate.isAfter(maxDate, precision)) { return false; } else if ( // 如果配置了 minDuration 那么 EndDate - minDuration 之后的天数也不能选 @@ -561,8 +565,8 @@ export class DateRangePicker extends React.Component< checkEndIsValidDate(currentDate: moment.Moment) { let {startDate} = this.state; - - let {minDate, maxDate, minDuration, maxDuration} = this.props; + let {minDate, maxDate, minDuration, maxDuration, viewMode} = this.props; + const precision = viewMode === 'time' ? 'hours' : viewMode || 'day'; minDate = minDate && startDate @@ -571,9 +575,9 @@ export class DateRangePicker extends React.Component< : startDate : minDate || startDate; - if (minDate && currentDate.isBefore(minDate, 'day')) { + if (minDate && currentDate.isBefore(minDate, precision)) { return false; - } else if (maxDate && currentDate.isAfter(maxDate, 'day')) { + } else if (maxDate && currentDate.isAfter(maxDate, precision)) { return false; } else if ( startDate && @@ -606,18 +610,38 @@ export class DateRangePicker extends React.Component< return {currentDate.date()}; } + renderQuarter(props: any, quarter: number, year: number) { + const currentDate = moment().year(year).quarter(quarter); + const {startDate, endDate} = this.state; + + if ( + startDate && + endDate && + currentDate.isBetween(startDate, endDate, 'quarter', '[]') + ) { + props.className += ' rdtBetween'; + } + + return ( + + Q{quarter} + + ); + } + renderCalendar() { const { classPrefix: ns, classnames: cx, dateFormat, timeFormat, + inputFormat, ranges, locale, - embed + embed, + viewMode = 'days' } = this.props; const __ = this.props.translate; - let viewMode: 'days' | 'months' | 'years' | 'time' = 'days'; const {startDate, endDate} = this.state; return ( @@ -630,12 +654,14 @@ export class DateRangePicker extends React.Component< onChange={this.handleStartChange} requiredConfirm={false} dateFormat={dateFormat} + inputFormat={inputFormat} timeFormat={timeFormat} isValidDate={this.checkStartIsValidDate} viewMode={viewMode} input={false} onClose={this.close} renderDay={this.renderDay} + renderQuarter={this.renderQuarter} locale={locale} /> @@ -645,6 +671,7 @@ export class DateRangePicker extends React.Component< onChange={this.handleEndChange} requiredConfirm={false} dateFormat={dateFormat} + inputFormat={inputFormat} timeFormat={timeFormat} viewDate={this.nextMonth} isEndDate @@ -653,6 +680,7 @@ export class DateRangePicker extends React.Component< input={false} onClose={this.close} renderDay={this.renderDay} + renderQuarter={this.renderQuarter} locale={locale} /> diff --git a/src/components/MonthRangePicker.tsx b/src/components/MonthRangePicker.tsx index 23058d6aa..aab6e5a2d 100644 --- a/src/components/MonthRangePicker.tsx +++ b/src/components/MonthRangePicker.tsx @@ -18,6 +18,8 @@ import {noop} from '../utils/helper'; import {LocaleProps, localeable} from '../locale'; import {DateRangePicker} from './DateRangePicker'; import capitalize from 'lodash/capitalize'; +import {ShortCuts, ShortCutDateRange} from './DatePicker'; +import {availableRanges} from './DateRangePicker'; export interface MonthRangePickerProps extends ThemeProps, LocaleProps { className?: string; @@ -27,7 +29,8 @@ export interface MonthRangePickerProps extends ThemeProps, LocaleProps { format: string; utc?: boolean; inputFormat?: string; - // ranges?: string | Array; + timeFormat?: string; + ranges?: string | Array; clearable?: boolean; minDate?: moment.Moment; maxDate?: moment.Moment; @@ -103,11 +106,11 @@ export class MonthRangePicker extends React.Component< }; } - componentWillReceiveProps(nextProps: MonthRangePickerProps) { + componentDidUpdate(prevProps: MonthRangePickerProps) { const props = this.props; - const {value, format, joinValues, delimiter} = nextProps; + const {value, format, joinValues, delimiter} = props; - if (props.value !== value) { + if (prevProps.value !== value) { this.setState({ ...DateRangePicker.unFormatValue(value, format, joinValues, delimiter) }); @@ -275,6 +278,68 @@ export class MonthRangePicker extends React.Component< ); } + selectRannge(range: PlainObject) { + const {closeOnSelect, minDate, maxDate} = this.props; + this.setState( + { + startDate: minDate + ? moment.max(range.startDate(moment()), minDate) + : range.startDate(moment()), + endDate: maxDate + ? moment.min(maxDate, range.endDate(moment())) + : range.endDate(moment()) + }, + closeOnSelect ? this.confirm : noop + ); + } + + renderRanges(ranges: string | Array | undefined) { + if (!ranges) { + return null; + } + const {classPrefix: ns} = this.props; + let rangeArr: Array; + if (typeof ranges === 'string') { + rangeArr = ranges.split(','); + } else { + rangeArr = ranges; + } + const __ = this.props.translate; + + return ( +
    + {rangeArr.map(item => { + if (!item) { + return null; + } + let range: PlainObject = {}; + if (typeof item === 'string') { + range = availableRanges[item]; + range.key = item; + } else if ( + (item as ShortCutDateRange).startDate && + (item as ShortCutDateRange).endDate + ) { + range = { + ...item, + startDate: () => (item as ShortCutDateRange).startDate, + endDate: () => (item as ShortCutDateRange).endDate + }; + } + return ( +
  • this.selectRannge(range)} + key={range.key || range.label} + > + {__(range.label)} +
  • + ); + })} +
+ ); + } + clearValue(e: React.MouseEvent) { e.preventDefault(); e.stopPropagation(); @@ -375,7 +440,7 @@ export class MonthRangePicker extends React.Component< } renderCalendar() { - const {classPrefix: ns, classnames: cx, locale, embed} = this.props; + const {classPrefix: ns, classnames: cx, locale, embed, ranges, inputFormat, timeFormat} = this.props; const __ = this.props.translate; const viewMode: 'months' = 'months'; const dateFormat = 'YYYY-MM'; @@ -383,12 +448,15 @@ export class MonthRangePicker extends React.Component< return (
+ {this.renderRanges(ranges)} JSX.Element; + renderQuarter?: ( + props: any, + quartar: number, + year?: number, + date?: moment.Moment + ) => JSX.Element; } class BaseDatePicker extends ReactDatePicker { @@ -106,7 +112,7 @@ class BaseDatePicker extends ReactDatePicker { .startOf(type), currentView: nextViews[type] }); - this.props.onViewModeChange!(nextViews[type]); + this.props.onViewModeChange?.(nextViews[type]); }; }; @@ -135,6 +141,7 @@ class BaseDatePicker extends ReactDatePicker { date = viewDate .clone() .quarter(parseInt(target.getAttribute('data-value')!, 10)) + .startOf('quarter') .date(currentDate.date()); } else if (target.className.indexOf('rdtYear') !== -1) { date = viewDate @@ -173,12 +180,18 @@ class BaseDatePicker extends ReactDatePicker { render() { const Component = CustomCalendarContainer as any; + const viewProps = this.getComponentProps(); + + if (this.props.viewMode === 'quarters') { + [viewProps.updateOn, viewProps.renderQuarter] = ['quarters', this.props.renderQuarter]; + } + return (
diff --git a/src/components/calendar/QuartersView.tsx b/src/components/calendar/QuartersView.tsx index 331d6d0ea..f9895f88c 100644 --- a/src/components/calendar/QuartersView.tsx +++ b/src/components/calendar/QuartersView.tsx @@ -1,3 +1,4 @@ +import moment from 'moment'; import React from 'react'; import {localeable, LocaleProps} from '../../locale'; import {ThemeProps} from '../../theme'; @@ -21,12 +22,10 @@ export interface QuarterViewProps extends LocaleProps, ThemeProps { showView: (view: string) => () => void; updateSelectedDate: (e: any, close?: boolean) => void; renderQuarter: any; - isValidDate: any; + isValidDate: (date: moment.Moment) => boolean; } export class QuarterView extends React.Component { - alwaysValidDate: any; - renderYear() { const __ = this.props.translate; const showYearHead = !/^mm$/i.test(this.props.inputFormat || ''); @@ -67,47 +66,24 @@ export class QuarterView extends React.Component { } renderQuarters() { let date = this.props.selectedDate, - month = this.props.viewDate.month(), + quarter = this.props.viewDate.quarter(), year = this.props.viewDate.year(), rows = [], i = 1, - months = [], + quarters = [], renderer = this.props.renderQuarter || this.renderQuarter, isValid = this.props.isValidDate || this.alwaysValidDate, classes, props: any, - currentMonth: moment.Moment, - isDisabled, - noOfDaysInMonth, - daysInMonth, - validDay, - // Date is irrelevant because we're only interested in month - irrelevantDate = 1; + isDisabled; + while (i < 5) { classes = 'rdtQuarter'; - currentMonth = this.props.viewDate - .clone() - .set({year: year, quarter: i, date: irrelevantDate}); - - noOfDaysInMonth = currentMonth.endOf('quarter').format('Q'); - daysInMonth = Array.from( - {length: parseInt(noOfDaysInMonth, 10)}, - function (e, i) { - return i + 1; - } - ); - - validDay = daysInMonth.find(function (d) { - var day = currentMonth.clone().set('date', d); - return isValid(day); - }); - - isDisabled = validDay === undefined; + isDisabled = !isValid(moment(`${year}-${i}`, 'YYYY-Q')); if (isDisabled) classes += ' rdtDisabled'; - if (date && i === date.quarter() && year === date.year()) - classes += ' rdtActive'; + if (date && i === date.quarter() && year === date.year()) classes += ' rdtActive'; props = { 'key': i, @@ -122,13 +98,13 @@ export class QuarterView extends React.Component { : this.props.setDate('quarter'); } - months.push(renderer(props, i, year, date && date.clone())); + quarters.push(renderer(props, i, year, date && date.clone())); - if (months.length === 2) { + if (quarters.length === 2) { rows.push( - React.createElement('tr', {key: month + '_' + rows.length}, months) + React.createElement('tr', {key: quarter + '_' + rows.length}, quarters) ); - months = []; + quarters = []; } i++; @@ -154,6 +130,10 @@ export class QuarterView extends React.Component { this.props.updateSelectedDate(event); }; + alwaysValidDate() { + return true; + } + render() { const {classnames: cx} = this.props;