Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions packages/components/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ import React, { forwardRef, useCallback, useEffect } from 'react';

import classNames from 'classnames';
import dayjs from 'dayjs';
import { isDate } from 'lodash-es';
import { isArray, isDate } from 'lodash-es';

import { formatDate, formatTime, getDefaultFormat, parseToDayjs } from '@tdesign/common-js/date-picker/format';
import { addMonth, covertToDate, extractTimeObj, isSame, subtractMonth } from '@tdesign/common-js/date-picker/utils';
import {
addMonth,
covertToDate,
extractTimeObj,
isSame,
subtractMonth,
getRangeBounds,
} from '@tdesign/common-js/date-picker/utils';

import useConfig from '../hooks/useConfig';
import useDefaultProps from '../hooks/useDefaultProps';
Expand Down Expand Up @@ -51,6 +58,7 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
needConfirm,
multiple,
label,
range,
disableTime,
onClear,
onPick,
Expand Down Expand Up @@ -126,8 +134,16 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
setInputValue(formatDate(dateValue, { format }));

if (popupVisible) {
setYear(parseToDayjs(value as DateValue, format).year());
setMonth(parseToDayjs(value as DateValue, format).month());
if (((props.range && isArray(props.range)) || props.panelActiveDate) && !value) {
const rangeBounds = getRangeBounds(props.range);
const yearFromRange = rangeBounds.min?.getFullYear() ?? rangeBounds.max?.getFullYear();
const monthFromRange = rangeBounds.min?.getMonth() ?? rangeBounds.max?.getMonth();
setYear((props.panelActiveDate?.year ?? yearFromRange) as number);
setMonth(props.panelActiveDate?.month ? Number(props.panelActiveDate?.month) - 1 : monthFromRange);
} else {
setYear(parseToDayjs(value as DateValue, format).year());
setMonth(parseToDayjs(value as DateValue, format).month());
}
setTime(formatTime(value, format, timeFormat, defaultTime));
} else {
setIsHoverCell(false);
Expand Down Expand Up @@ -371,6 +387,7 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
popupVisible,
needConfirm,
multiple,
range,
onCellClick,
onCellMouseEnter,
onCellMouseLeave,
Expand Down
83 changes: 77 additions & 6 deletions packages/components/date-picker/DateRangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import {
isValidDate,
parseToDayjs,
} from '@tdesign/common-js/date-picker/format';
import { addMonth, extractTimeObj, subtractMonth } from '@tdesign/common-js/date-picker/utils';
import { addMonth, extractTimeObj, getRangeBounds, subtractMonth } from '@tdesign/common-js/date-picker/utils';
import log from '@tdesign/common-js/log/index';
import { isArray, isFunction } from 'lodash-es';
import useConfig from '../hooks/useConfig';
import useDefaultProps from '../hooks/useDefaultProps';
import useLatest from '../hooks/useLatest';
Expand All @@ -28,6 +29,7 @@ import type {
TdDateRangePickerProps,
DatePickerYearChangeTrigger,
DatePickerMonthChangeTrigger,
PickerDateRange,
} from './type';

export interface DateRangePickerProps extends TdDateRangePickerProps, StyledProps {}
Expand All @@ -50,6 +52,8 @@ const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>((origin
presetsPlacement,
panelPreselection,
cancelRangeSelectLimit,
range,
panelActiveDate,
onPick,
disableTime,
needConfirm,
Expand Down Expand Up @@ -152,14 +156,80 @@ const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>((origin

// 空数据重置为当前年月
if (!value.length) {
const { year: defaultYear, month: defaultMonth } = initYearMonthTime({ value, mode, format, enableTimePicker });
setYear(defaultYear);
setMonth(defaultMonth);
if ((range && isArray(range)) || panelActiveDate) {
let startRange = range as PickerDateRange;
let endRange = range as PickerDateRange;
if (isArray(range)) {
const [first, second] = range;
if (isArray(first) || isFunction(first) || isArray(second) || isFunction(second)) {
startRange = first as PickerDateRange;
endRange = second as PickerDateRange;
}
}

const startRangeBounds = getRangeBounds(startRange);
const endRangeBounds = getRangeBounds(endRange);

const startYearFromRange = startRangeBounds.min?.getFullYear() ?? startRangeBounds.max?.getFullYear();
const startMonthFromRange = startRangeBounds.min?.getMonth() ?? startRangeBounds.max?.getMonth();

const endYearFromRange = endRangeBounds.min?.getFullYear() ?? endRangeBounds.max?.getFullYear();
const endMonthFromRange = endRangeBounds.min?.getMonth() ?? endRangeBounds.max?.getMonth();

let startPanelActiveDate = panelActiveDate as any;
let endPanelActiveDate = panelActiveDate as any;
if (isArray(panelActiveDate)) {
[startPanelActiveDate, endPanelActiveDate] = panelActiveDate;
}

const leftYear = (startPanelActiveDate?.year ?? startYearFromRange) as number;
const leftMonth = startPanelActiveDate?.month ? Number(startPanelActiveDate?.month) - 1 : startMonthFromRange;
const rightYear = (endPanelActiveDate?.year ?? endYearFromRange) as number;
const rightMonth = endPanelActiveDate?.month ? Number(endPanelActiveDate?.month) - 1 : endMonthFromRange;

// 获取默认值作为兜底
const { year: defaultYear, month: defaultMonth } = initYearMonthTime({
value,
mode,
format,
enableTimePicker,
});

const nextYear = [leftYear ?? defaultYear[0], rightYear ?? defaultYear[1]];
const nextMonth = [leftMonth ?? defaultMonth[0], rightMonth ?? defaultMonth[1]];

// 修正:如果左右面板年月完全一致且未显式指定不同范围,则偏移面板
if (nextYear[0] === nextYear[1] && nextMonth[0] === nextMonth[1] && !enableTimePicker) {
if (startRange === endRange) {
if (mode === 'year') nextYear[1] += 10;
else if (mode === 'month' || mode === 'quarter') nextYear[1] += 1;
else nextMonth[1] += 1;
}
}
setYear(nextYear);
setMonth(nextMonth);
} else {
const { year: defaultYear, month: defaultMonth } = initYearMonthTime({
value,
mode,
format,
enableTimePicker,
});

setYear(defaultYear);
setMonth(defaultMonth);
}
} else if (value.length === 2 && (!enableTimePicker || isSwitchTimeMode)) {
handleSyncPanelValue(value);
} else {
setYear(value.map((v: string) => parseToDayjs(v, format).year()));
setMonth(value.map((v: string) => parseToDayjs(v, format).month()));
let nextYear = value.map((v: string) => parseToDayjs(v, format).year());
if (nextYear.length === 1) nextYear = [nextYear[0], nextYear[0]];

let nextMonth = value.map((v: string) => parseToDayjs(v, format).month());
if (nextMonth.length === 1) nextMonth = [nextMonth[0], Math.min(nextMonth[0] + 1, 11)];

setYear(nextYear);
setMonth(nextMonth);
}
} else {
setActiveIndex(0);
Expand Down Expand Up @@ -451,6 +521,7 @@ const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>((origin
panelPreselection,
year,
month,
range,
mode,
format,
presets,
Expand Down
60 changes: 60 additions & 0 deletions packages/components/date-picker/_example/range.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { DatePicker, DateRangePicker, Space } from 'tdesign-react';
import dayjs from 'dayjs';

export default function DatePickerRangeDemo() {
// 仅允许今天到未来 90 天(返回 true 表示可选)
const rangeFn = (d: Date) => {
const now = dayjs().startOf('day');
const target = dayjs(d).startOf('day');
const diff = target.diff(now, 'day');
return diff >= 0 && diff <= 90;
};

return (
<Space direction="vertical">
<DatePicker
range={['2000-01-01', '2010-12-31']}
panelActiveDate={{
year: 2005,
month: 10,
}}
placeholder="2000-2010之间的日期"
/>
<DateRangePicker
range={['2000-01-01', '2010-12-31']}
panelActiveDate={{
year: 2005,
month: 10,
}}
placeholder="2000-2010之间的日期"
/>

{/* 2026开始,不限制结束 */}
<DatePicker
range={['2026-01-01', null]}
panelActiveDate={{
year: 2005,
month: 10,
}}
placeholder="2026开始,不限制结束"
/>
<DateRangePicker
range={['2026-01-01', null]}
panelActiveDate={{
year: 2005,
month: 10,
}}
placeholder="2026开始,不限制结束"
/>

{/* 2026开始,不限制结束 */}
<DatePicker range={['2026-01-01', null]} placeholder="2026开始,不限制结束" />
<DateRangePicker range={['2026-01-01', null]} placeholder="2026开始,不限制结束" />

{/* 未来 90 天 */}
<DatePicker range={rangeFn} placeholder="未来 90 天" />
<DateRangePicker range={rangeFn} placeholder="未来 90 天" />
</Space>
);
}
Loading
Loading