pull/8366/merge
yuanyi0821 2025-11-19 02:55:48 +00:00 committed by GitHub
commit e578216369
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 679 additions and 39 deletions

View File

@ -0,0 +1,123 @@
<docs>
---
order: 7
title:
zh-CN: 自动填充和整天模式
en-US: Auto Fill and Whole Day Mode
---
## zh-CN
RangePicker 支持两个新功能
1. `autoFill`双击日期时自动设置为开始和结束日期
2. `isWholeDay` showTime 模式下自动设置开始时间为 00:00:00结束时间为 23:59:59
## en-US
RangePicker supports two new features:
1. `autoFill`: Double-click a date to automatically set it as both start and end date
2. `isWholeDay`: In showTime mode, automatically set start time to 00:00:00 and end time to 23:59:59
</docs>
<template>
<a-space direction="vertical" :size="12">
<div>
<h4>Auto Fill 功能</h4>
<p>双击日期会自动设置为开始和结束日期</p>
<a-range-picker :auto-fill="true" @change="onAutoFillChange" />
</div>
<div>
<h4>Whole Day 功能</h4>
<p> showTime 模式下自动设置整天时间</p>
<a-range-picker
show-time
:is-whole-day="true"
format="YYYY/MM/DD HH:mm:ss"
@change="onWholeDayChange"
/>
</div>
<div>
<h4>组合使用</h4>
<p>同时启用 autoFill isWholeDay</p>
<a-range-picker
show-time
:auto-fill="true"
:is-whole-day="true"
format="YYYY/MM/DD HH:mm:ss"
@change="onCombinedChange"
/>
</div>
</a-space>
</template>
<script lang="ts" setup>
import { Dayjs } from 'dayjs';
type RangeValue = [Dayjs, Dayjs];
const onAutoFillChange = (
values: RangeValue,
dateStrings: [string, string],
currentPreset?: any,
) => {
if (values) {
console.log(
'Auto Fill - From: ',
values[0].format('YYYY-MM-DD'),
', to: ',
values[1].format('YYYY-MM-DD'),
);
console.log('Auto Fill - From: ', dateStrings[0], ', to: ', dateStrings[1]);
if (currentPreset) {
console.log('Auto Fill - Selected preset: ', currentPreset.label);
}
} else {
console.log('Auto Fill - Clear');
}
};
const onWholeDayChange = (
values: RangeValue,
dateStrings: [string, string],
currentPreset?: any,
) => {
if (values && values[0] && values[1]) {
console.log(
'Whole Day - From: ',
values[0].format('YYYY-MM-DD HH:mm:ss'),
', to: ',
values[1].format('YYYY-MM-DD HH:mm:ss'),
);
console.log('Whole Day - From: ', dateStrings[0], ', to: ', dateStrings[1]);
if (currentPreset) {
console.log('Whole Day - Selected preset: ', currentPreset.label);
}
} else {
console.log('Whole Day - Clear');
}
};
const onCombinedChange = (
values: RangeValue,
dateStrings: [string, string],
currentPreset?: any,
) => {
if (values && values[0] && values[1]) {
console.log(
'Combined - From: ',
values[0].format('YYYY-MM-DD HH:mm:ss'),
', to: ',
values[1].format('YYYY-MM-DD HH:mm:ss'),
);
console.log('Combined - From: ', dateStrings[0], ', to: ', dateStrings[1]);
if (currentPreset) {
console.log('Combined - Selected preset: ', currentPreset.label);
}
} else {
console.log('Combined - Clear');
}
};
</script>

View File

@ -17,6 +17,8 @@
<Suffix />
<statusVue />
<placementVue />
<AutoFillWholeDay />
<PresetAutofill />
</demo-sort>
</template>
<script>
@ -36,6 +38,8 @@ import Suffix from './suffix.vue';
import Bordered from './bordered.vue';
import RangePicker from './range-picker.vue';
import placementVue from './placement.vue';
import AutoFillWholeDay from './auto-fill-whole-day.vue';
import PresetAutofill from './preset-autofill.vue';
import statusVue from './status.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
@ -62,6 +66,8 @@ export default defineComponent({
SelectInRnage,
Bordered,
RangePicker,
AutoFillWholeDay,
PresetAutofill,
},
});
</script>

View File

@ -0,0 +1,154 @@
<docs>
---
order: 8
title:
zh-CN: Preset自动回填
en-US: Preset Auto Fill
---
## zh-CN
RangePicker 现在支持preset自动回填功能当传入的value包含preset信息时会根据preset自动计算对应的日期范围
## en-US
RangePicker now supports preset auto fill functionality. When the passed value contains preset information, it will automatically calculate the corresponding date range based on the preset.
</docs>
<template>
<a-space direction="vertical" :size="12">
<div>
<h4>Preset自动回填功能</h4>
<p>当传入的value包含preset信息时会根据preset自动计算日期范围</p>
<p>
<strong>当前设置</strong>
is-whole-day={{ isWholeDay }}show-time=true
</p>
<p>
<strong>行为</strong>
当isWholeDay为false时点击preset时会使用当前时间的时分秒而不是00:00:00 - 23:59:59
</p>
<a-range-picker
v-model:value="rangeValue"
show-time
:is-whole-day="isWholeDay"
format="YYYY/MM/DD HH:mm:ss"
:presets="rangePresets"
@change="onRangeChange"
/>
</div>
<div>
<h4>切换is-whole-day</h4>
<p>
<a-button @click="toggleIsWholeDay">is-whole-day</a-button>
</p>
<h4>测试按钮</h4>
<a-space>
<a-button @click="setTodayPreset">preset</a-button>
<a-button @click="setLast7DaysPreset">7preset</a-button>
<a-button @click="setLast30DaysPreset">30preset</a-button>
<a-button @click="setCurrentTimePreset">preset</a-button>
<a-button @click="clearValue"></a-button>
</a-space>
</div>
<div>
<h4>当前值</h4>
<pre>{{ JSON.stringify(rangeValue, null, 2) }}</pre>
</div>
</a-space>
</template>
<script lang="ts" setup>
import dayjs, { Dayjs } from 'dayjs';
import { ref } from 'vue';
type RangeValue = [Dayjs, Dayjs] | [Dayjs, Dayjs, any];
const isWholeDay = ref(false);
const rangeValue = ref<RangeValue | null>(null);
const onRangeChange = (values: RangeValue, dateStrings: [string, string], currentPreset?: any) => {
if (values) {
console.log('From: ', values[0], ', to: ', values[1]);
console.log('From: ', dateStrings[0], ', to: ', dateStrings[1]);
if (currentPreset) {
console.log('Selected preset key: ', currentPreset.key);
console.log('Selected preset label: ', currentPreset.label);
} else {
console.log('Manual selection (no preset)');
}
} else {
console.log('Clear');
}
};
const rangePresets = ref([
{
label: '今天',
value: [dayjs().startOf('day'), dayjs().endOf('day')],
key: 'today',
},
{
label: '最近7天',
value: [dayjs().subtract(7, 'day').startOf('day'), dayjs().endOf('day')],
key: 'last7days',
},
{
label: '最近30天',
value: [dayjs().subtract(30, 'day').startOf('day'), dayjs().endOf('day')],
key: 'last30days',
},
{
label: '最近90天',
value: [dayjs().subtract(90, 'day').startOf('day'), dayjs().endOf('day')],
key: 'last90days',
},
]);
const setTodayPreset = () => {
const todayPreset = rangePresets.value.find(p => p.key === 'today');
if (todayPreset) {
// presetvalueRangePickerpreset
rangeValue.value = [dayjs(), dayjs(), todayPreset] as any;
}
};
const setLast7DaysPreset = () => {
const last7DaysPreset = rangePresets.value.find(p => p.key === 'last7days');
if (last7DaysPreset) {
// presetvalueRangePickerpreset
rangeValue.value = [dayjs(), dayjs(), last7DaysPreset] as any;
}
};
const setLast30DaysPreset = () => {
const last30DaysPreset = rangePresets.value.find(p => p.key === 'last30days');
if (last30DaysPreset) {
// presetvalueRangePickerpreset
rangeValue.value = [dayjs(), dayjs(), last30DaysPreset] as any;
}
};
const setCurrentTimePreset = () => {
// preset
const currentTimePreset = {
label: '当前时间',
value: [dayjs(), dayjs()], // 使startOf/endOf
key: 'currentTime',
};
// presetvalueRangePickerpreset
rangeValue.value = [dayjs(), dayjs(), currentTimePreset] as any;
};
const clearValue = () => {
rangeValue.value = null;
};
const toggleIsWholeDay = () => {
isWholeDay.value = !isWholeDay.value;
};
</script>

View File

@ -40,25 +40,31 @@ const onChange = (date: Dayjs) => {
console.log('Clear');
}
};
const onRangeChange = (dates: RangeValue, dateStrings: string[]) => {
if (dates) {
console.log('From: ', dates[0], ', to: ', dates[1]);
const onRangeChange = (values: RangeValue, dateStrings: [string, string], currentPreset?: any) => {
if (values) {
console.log('From: ', values[0], ', to: ', values[1]);
console.log('From: ', dateStrings[0], ', to: ', dateStrings[1]);
if (currentPreset) {
console.log('Selected preset key: ', currentPreset.key);
console.log('Selected preset label: ', currentPreset.label);
} else {
console.log('Manual selection (no preset)');
}
} else {
console.log('Clear');
}
};
const presets = ref([
{ label: 'Yesterday', value: dayjs().add(-1, 'd') },
{ label: 'Last Week', value: dayjs().add(-7, 'd') },
{ label: 'Last Month', value: dayjs().add(-1, 'month') },
{ label: 'Yesterday', value: dayjs().add(-1, 'd'), key: 'yesterday' },
{ label: 'Last Week', value: dayjs().add(-7, 'd'), key: 'lastweek' },
{ label: 'Last Month', value: dayjs().add(-1, 'month'), key: 'lastmonth' },
]);
const rangePresets = ref([
{ label: 'Last 7 Days', value: [dayjs().add(-7, 'd'), dayjs()] },
{ label: 'Last 14 Days', value: [dayjs().add(-14, 'd'), dayjs()] },
{ label: 'Last 30 Days', value: [dayjs().add(-30, 'd'), dayjs()] },
{ label: 'Last 90 Days', value: [dayjs().add(-90, 'd'), dayjs()] },
{ label: 'Last 7 Days', value: [dayjs().add(-7, 'd'), dayjs()], key: 'last7days' },
{ label: 'Last 14 Days', value: [dayjs().add(-14, 'd'), dayjs()], key: 'last14days' },
{ label: 'Last 30 Days', value: [dayjs().add(-30, 'd'), dayjs()], key: 'last30days' },
{ label: 'Last 90 Days', value: [dayjs().add(-90, 'd'), dayjs()], key: 'last90days' },
]);
</script>

View File

@ -13,7 +13,7 @@ import useConfigInject from '../../config-provider/hooks/useConfigInject';
import classNames from '../../_util/classNames';
import type { CommonProps, RangePickerProps } from './props';
import { commonProps, rangePickerProps } from './props';
import type { PanelMode, RangeValue } from '../../vc-picker/interface';
import type { PanelMode, RangeValue, RangePickerOnChange } from '../../vc-picker/interface';
import type { RangePickerSharedProps } from '../../vc-picker/RangePicker';
import { FormItemInputContext, useInjectFormItemContext } from '../../form/FormItemContext';
import omit from '../../_util/omit';
@ -84,13 +84,18 @@ export default function generateRangePicker<DateType, ExtraProps = {}>(
pickerRef.value?.blur();
},
});
const maybeToStrings = (dates: DateType[]) => {
const maybeToStrings = (dates: RangeValue<DateType>) => {
return props.valueFormat ? generateConfig.toString(dates, props.valueFormat) : dates;
};
const onChange = (dates: RangeValue<DateType>, dateStrings: [string, string]) => {
const values = maybeToStrings(dates);
emit('update:value', values);
emit('change', values, dateStrings);
const onChange: RangePickerOnChange<DateType> = (values, formatStrings) => {
const [startValue, endValue, currentPreset] = values;
const [startStr, endStr] = formatStrings;
const dates: RangeValue<DateType> = [startValue, endValue];
const dateStrings: [string, string] = [startStr, endStr];
const processedValues = maybeToStrings(dates);
emit('update:value', processedValues);
emit('change', processedValues, dateStrings, currentPreset);
formItemContext.onFieldChange();
};
const onOpenChange = (open: boolean) => {
@ -109,7 +114,7 @@ export default function generateRangePicker<DateType, ExtraProps = {}>(
emit('panelChange', values, modes);
};
const onOk = (dates: DateType[]) => {
const value = maybeToStrings(dates);
const value = props.valueFormat ? generateConfig.toString(dates, props.valueFormat) : dates;
emit('ok', value);
};
const onCalendarChange: RangePickerSharedProps<DateType>['onCalendarChange'] = (

View File

@ -269,8 +269,13 @@ export interface RangePickerProps<DateType> {
onChange?: (
value: RangeValue<DateType> | RangeValue<string> | null,
dateString: [string, string],
currentPreset?: any,
) => void;
'onUpdate:value'?: (value: RangeValue<DateType> | RangeValue<string> | null) => void;
/** 双击日期时自动设置为开始和结束日期 */
autoFill?: boolean;
/** 在 showTime 模式下,是否设置为整天(开始时间 00:00:00结束时间 23:59:59 */
isWholeDay?: boolean;
onCalendarChange?: (
values: RangeValue<DateType> | RangeValue<string>,
formatString: [string, string],

View File

@ -961,6 +961,7 @@ const genPickerStyle: GenerateStyle<PickerToken> = token => {
controlItemBgHover,
presetsWidth,
presetsMaxWidth,
fontWeightStrong,
} = token;
return [
@ -1326,6 +1327,12 @@ const genPickerStyle: GenerateStyle<PickerToken> = token => {
'&:hover': {
background: controlItemBgHover,
},
[`&${componentCls}-preset-active`]: {
background: controlItemBgActive,
color: colorPrimary,
fontWeight: fontWeightStrong,
},
},
},
},

View File

@ -186,7 +186,7 @@ function injectSorter<RecordType>(
const cell = (column.customHeaderCell && column.customHeaderCell(col)) || {};
const originOnClick = cell.onClick;
const originOKeyDown = cell.onKeydown;
cell.onClick = (event: MouseEvent) => {
cell.onClick = (event: PointerEvent) => {
triggerSorter({
column,
key: columnKey,

View File

@ -23,6 +23,9 @@ export type PanelContextProps = {
/** Only used for TimePicker and this is a deprecated prop */
defaultOpenValue?: Ref<any>;
/** Double click state for RangePicker */
isDoubleClickRef?: Ref<boolean>;
};
const PanelContextKey: InjectionKey<PanelContextProps> = Symbol('PanelContextProps');

View File

@ -1,13 +1,18 @@
import { defineComponent } from 'vue';
import type { PresetDate } from './interface';
export default defineComponent({
name: 'PresetPanel',
props: {
prefixCls: String,
presets: {
type: Array,
type: Array as () => PresetDate<any>[],
default: () => [],
},
currentPreset: {
type: Object as () => PresetDate<any> | null,
default: null,
},
onClick: Function,
onHover: Function,
},
@ -19,21 +24,24 @@ export default defineComponent({
return (
<div class={`${props.prefixCls}-presets`}>
<ul>
{props.presets.map(({ label, value }, index) => (
{props.presets.map(preset => (
<li
key={index}
key={preset.key}
class={{
[`${props.prefixCls}-preset-active`]: props.currentPreset?.key === preset.key,
}}
onClick={e => {
e.stopPropagation();
props.onClick(value);
props.onClick(preset.value, preset);
}}
onMouseenter={() => {
props.onHover?.(value);
props.onHover?.(preset.value);
}}
onMouseleave={() => {
props.onHover?.(null);
}}
>
{label}
{preset.label}
</li>
))}
</ul>

View File

@ -5,6 +5,7 @@ import type {
RangeValue,
EventValue,
PresetDate,
RangePickerOnChange,
} from './interface';
import type { PickerBaseProps, PickerDateProps, PickerTimeProps } from './Picker';
import type { SharedTimeProps } from './panels/TimePanel';
@ -108,7 +109,7 @@ export type RangePickerSharedProps<DateType> = {
separator?: VueNode;
allowEmpty?: [boolean, boolean];
mode?: [PanelMode, PanelMode];
onChange?: (values: RangeValue<DateType>, formatString: [string, string]) => void;
onChange?: RangePickerOnChange<DateType>;
onCalendarChange?: (
values: RangeValue<DateType>,
formatString: [string, string],
@ -133,6 +134,10 @@ export type RangePickerSharedProps<DateType> = {
nextIcon?: VueNode;
superPrevIcon?: VueNode;
superNextIcon?: VueNode;
/** 双击日期时自动设置为开始和结束日期 */
autoFill?: boolean;
/** 在 showTime 模式下,是否设置为整天(开始时间 00:00:00结束时间 23:59:59 */
isWholeDay?: boolean;
};
type OmitPickerProps<Props> = Omit<
@ -258,11 +263,15 @@ function RangerPicker<DateType>() {
'nextIcon',
'superPrevIcon',
'superNextIcon',
'autoFill',
'isWholeDay',
] as any,
setup(props, { attrs, expose }) {
const needConfirmButton = computed(
() => (props.picker === 'date' && !!props.showTime) || props.picker === 'time',
);
const isDoubleClickRef = ref(false);
const presets = computed(() => props.presets);
const ranges = computed(() => props.ranges);
const presetList = usePresets(presets, ranges);
@ -311,12 +320,91 @@ function RangerPicker<DateType>() {
const [mergedValue, setInnerValue] = useMergedState<RangeValue<DateType>>(null, {
value: toRef(props, 'value'),
defaultValue: props.defaultValue,
postState: values =>
props.picker === 'time' && !props.order
postState: values => {
// presetvalue [date, date, preset]
if (
values &&
Array.isArray(values) &&
(values as any).length === 3 &&
(values as any)[2]
) {
const preset = (values as any)[2];
// presetvalue使presetvalue
if (preset.value && Array.isArray(preset.value) && preset.value.length === 2) {
const presetValues = preset.value;
// preset.value
let startValue =
typeof presetValues[0] === 'function' ? presetValues[0]() : presetValues[0];
let endValue =
typeof presetValues[1] === 'function' ? presetValues[1]() : presetValues[1];
// props.isWholeDaytruefalse使
if (!props.isWholeDay) {
const now = props.generateConfig.getNow();
const currentHour = props.generateConfig.getHour(now);
const currentMinute = props.generateConfig.getMinute(now);
const currentSecond = props.generateConfig.getSecond(now);
startValue = props.generateConfig.setHour(
props.generateConfig.setMinute(
props.generateConfig.setSecond(startValue, currentSecond),
currentMinute,
),
currentHour,
);
endValue = props.generateConfig.setHour(
props.generateConfig.setMinute(
props.generateConfig.setSecond(endValue, currentSecond),
currentMinute,
),
currentHour,
);
}
if (startValue && endValue) {
// preset
setCurrentPreset(preset);
// preset
return props.picker === 'time' && !props.order
? [startValue, endValue]
: reorderValues([startValue, endValue], props.generateConfig);
}
}
}
// value
return props.picker === 'time' && !props.order
? values
: reorderValues(values, props.generateConfig),
: reorderValues(values, props.generateConfig);
},
});
// ========================= Current Preset =========================
const [currentPreset, setCurrentPreset] = useState<PresetDate<RangeValue<DateType>> | null>(
null,
);
// preset
const checkAndSetPreset = (values: RangeValue<DateType>) => {
if (!values || !values[0] || !values[1]) {
setCurrentPreset(null);
return;
}
const matchedPreset = presetList.value.find(preset => {
if (!preset.value || !preset.value[0] || !preset.value[1]) {
return false;
}
return (
isEqual(props.generateConfig, values[0], preset.value[0]) &&
isEqual(props.generateConfig, values[1], preset.value[1])
);
});
setCurrentPreset(matchedPreset || null);
};
// =========================== View Date ===========================
// Config view panel
const [startViewDate, endViewDate, setViewDate] = useRangeViewDates({
@ -489,7 +577,11 @@ function RangerPicker<DateType>() {
}, 0);
}
function triggerChange(newValue: RangeValue<DateType>, sourceIndex: 0 | 1) {
function triggerChange(
newValue: RangeValue<DateType>,
sourceIndex: 0 | 1,
fromPreset = false,
) {
let values = newValue;
let startValue = getValue(values, 0);
let endValue = getValue(values, 1);
@ -539,8 +631,88 @@ function RangerPicker<DateType>() {
}
}
// Handle isWholeDay: set time to 00:00:00 for start and 23:59:59 for end when showTime is true
if (props.isWholeDay && showTime && values && values[0] && values[1]) {
const startDate = values[0];
const endDate = values[1];
// Set start time to 00:00:00
const startWithTime = generateConfig.setHour(
generateConfig.setMinute(generateConfig.setSecond(startDate, 0), 0),
0,
);
// Set end time to 23:59:59
const endWithTime = generateConfig.setHour(
generateConfig.setMinute(generateConfig.setSecond(endDate, 59), 59),
23,
);
values = [startWithTime, endWithTime];
}
// presetpresetvalue
if (
fromPreset &&
currentPreset.value &&
Array.isArray(currentPreset.value) &&
currentPreset.value.length === 2
) {
const presetValues = currentPreset.value;
// preset.value
const presetStartValue =
typeof presetValues[0] === 'function' ? presetValues[0]() : presetValues[0];
const presetEndValue =
typeof presetValues[1] === 'function' ? presetValues[1]() : presetValues[1];
if (presetStartValue && presetEndValue) {
// isWholeDay
if (props.isWholeDay && showTime) {
const startWithTime = generateConfig.setHour(
generateConfig.setMinute(generateConfig.setSecond(presetStartValue, 0), 0),
0,
);
const endWithTime = generateConfig.setHour(
generateConfig.setMinute(generateConfig.setSecond(presetEndValue, 59), 59),
23,
);
values = [startWithTime, endWithTime];
} else if (showTime) {
// isWholeDayshowTime使
const now = generateConfig.getNow();
const currentHour = generateConfig.getHour(now);
const currentMinute = generateConfig.getMinute(now);
const currentSecond = generateConfig.getSecond(now);
const startWithCurrentTime = generateConfig.setHour(
generateConfig.setMinute(
generateConfig.setSecond(presetStartValue, currentSecond),
currentMinute,
),
currentHour,
);
const endWithCurrentTime = generateConfig.setHour(
generateConfig.setMinute(
generateConfig.setSecond(presetEndValue, currentSecond),
currentMinute,
),
currentHour,
);
values = [startWithCurrentTime, endWithCurrentTime];
} else {
// showTimepreset
values = [presetStartValue, presetEndValue];
}
}
}
setSelectedValue(values);
// preset currentPreset
if (!fromPreset) {
setCurrentPreset(null);
}
const startStr =
values && values[0]
? formatValue(values[0], { generateConfig, locale, format: formatList.value[0] })
@ -575,7 +747,12 @@ function RangerPicker<DateType>() {
(!isEqual(generateConfig, getValue(mergedValue.value, 0), startValue) ||
!isEqual(generateConfig, getValue(mergedValue.value, 1), endValue))
) {
onChange(values, [startStr, endStr]);
// presetpreset
const presetToPass = fromPreset ? currentPreset.value : currentPreset.value;
onChange(
[startValue, endValue, presetToPass],
[startStr, endStr, presetToPass?.key || null],
);
}
}
@ -718,7 +895,7 @@ function RangerPicker<DateType>() {
) {
return false;
}
triggerChange(selectedValue.value, index);
triggerChange(selectedValue.value, index, false);
resetText();
},
onCancel: () => {
@ -822,6 +999,11 @@ function RangerPicker<DateType>() {
setSelectedValue(mergedValue.value);
});
// mergedValue preset
watch(mergedValue, newValue => {
checkAndSetPreset(newValue);
});
// ============================ Warning ============================
if (process.env.NODE_ENV !== 'production') {
watchEffect(() => {
@ -887,6 +1069,37 @@ function RangerPicker<DateType>() {
};
}
// Handle isWholeDay: set default time values for start and end
if (props.isWholeDay && showTime) {
const now = generateConfig.getNow();
let defaultTime: DateType;
if (mergedActivePickerIndex.value === 0) {
// Start time: 00:00:00
defaultTime = generateConfig.setHour(
generateConfig.setMinute(generateConfig.setSecond(now, 0), 0),
0,
);
} else {
// End time: 23:59:59
defaultTime = generateConfig.setHour(
generateConfig.setMinute(generateConfig.setSecond(now, 59), 59),
23,
);
}
if (typeof showTime === 'object') {
panelShowTime = {
...showTime,
defaultValue: defaultTime,
};
} else {
panelShowTime = {
defaultValue: defaultTime,
};
}
}
let panelDateRender: DateRender<DateType> | null = null;
if (dateRender) {
panelDateRender = ({ current: date, today }) =>
@ -969,11 +1182,46 @@ function RangerPicker<DateType>() {
}
const onContextSelect = (date: DateType, type: 'key' | 'mouse' | 'submit') => {
const values = updateValues(selectedValue.value, date, mergedActivePickerIndex.value);
let values = updateValues(selectedValue.value, date, mergedActivePickerIndex.value);
const currentIndex = mergedActivePickerIndex.value;
const isDoubleClick = isDoubleClickRef.value;
const shouldSwitch = type === 'mouse' && needConfirmButton.value && isDoubleClick;
if (type === 'submit' || (type !== 'key' && !needConfirmButton.value)) {
// Reset double click state
isDoubleClickRef.value = false;
// Handle autoFill: when double-clicking and autoFill is enabled, set the same date for both start and end
if (props.autoFill && isDoubleClick && type === 'mouse') {
values = [date, date];
}
if (type === 'submit' || (type !== 'key' && !needConfirmButton.value) || shouldSwitch) {
// triggerChange will also update selected values
triggerChange(values, mergedActivePickerIndex.value);
triggerChange(values, mergedActivePickerIndex.value, false);
// If autoFill is enabled and we have both values, close the panel
if (
props.autoFill &&
isDoubleClick &&
type === 'mouse' &&
values &&
values[0] &&
values[1]
) {
triggerOpen(false, mergedActivePickerIndex.value);
} else if (shouldSwitch) {
// If double click, switch to next input
// But check if both inputs are complete, if so don't switch to avoid animation before popup closes
const startValue = getValue(values, 0);
const endValue = getValue(values, 1);
const bothValuesComplete = startValue && endValue;
if (!bothValuesComplete) {
const nextIndex = ((currentIndex + 1) % 2) as 0 | 1;
setMergedActivePickerIndex(nextIndex);
}
}
// clear hover value style
if (mergedActivePickerIndex.value === 0) {
onStartLeave();
@ -993,6 +1241,7 @@ function RangerPicker<DateType>() {
hideRanges: computed(() => true),
onSelect: onContextSelect,
open: mergedOpen,
isDoubleClickRef,
});
return () => {
@ -1051,7 +1300,7 @@ function RangerPicker<DateType>() {
onOk: () => {
if (getValue(selectedValue.value, mergedActivePickerIndex.value)) {
// triggerChangeOld(selectedValue.value);
triggerChange(selectedValue.value, mergedActivePickerIndex.value);
triggerChange(selectedValue.value, mergedActivePickerIndex.value, false);
if (onOk) {
onOk(selectedValue.value);
}
@ -1106,8 +1355,66 @@ function RangerPicker<DateType>() {
<PresetPanel
prefixCls={prefixCls}
presets={presetList.value}
onClick={nextValue => {
triggerChange(nextValue, null);
currentPreset={currentPreset.value}
onClick={(nextValue, preset) => {
setCurrentPreset(preset);
// presetvalue使presetvalue
let valuesToUse = nextValue;
if (preset.value && Array.isArray(preset.value) && preset.value.length === 2) {
const presetValues = preset.value;
// preset.value
const presetStartValue =
typeof presetValues[0] === 'function' ? presetValues[0]() : presetValues[0];
const presetEndValue =
typeof presetValues[1] === 'function' ? presetValues[1]() : presetValues[1];
if (presetStartValue && presetEndValue) {
// isWholeDay
if (props.isWholeDay && props.showTime) {
const startWithTime = props.generateConfig.setHour(
props.generateConfig.setMinute(
props.generateConfig.setSecond(presetStartValue, 0),
0,
),
0,
);
const endWithTime = props.generateConfig.setHour(
props.generateConfig.setMinute(
props.generateConfig.setSecond(presetEndValue, 59),
59,
),
23,
);
valuesToUse = [startWithTime, endWithTime];
} else if (props.showTime) {
// isWholeDayshowTime使
const now = props.generateConfig.getNow();
const currentHour = props.generateConfig.getHour(now);
const currentMinute = props.generateConfig.getMinute(now);
const currentSecond = props.generateConfig.getSecond(now);
const startWithCurrentTime = props.generateConfig.setHour(
props.generateConfig.setMinute(
props.generateConfig.setSecond(presetStartValue, currentSecond),
currentMinute,
),
currentHour,
);
const endWithCurrentTime = props.generateConfig.setHour(
props.generateConfig.setMinute(
props.generateConfig.setSecond(presetEndValue, currentSecond),
currentMinute,
),
currentHour,
);
valuesToUse = [startWithCurrentTime, endWithCurrentTime];
} else {
// showTimepreset
valuesToUse = [presetStartValue, presetEndValue];
}
}
}
triggerChange(valuesToUse, null, true);
triggerOpen(false, mergedActivePickerIndex.value);
}}
onHover={hoverValue => {
@ -1184,7 +1491,7 @@ function RangerPicker<DateType>() {
values = updateValues(values, null, 1);
}
triggerChange(values, null);
triggerChange(values, null, false);
triggerOpen(false, mergedActivePickerIndex.value);
}}
class={`${prefixCls}-clear`}

View File

@ -22,6 +22,7 @@ export default function usePresets<T>(
return {
label,
value: newValues,
key: label, // 添加 key 属性
};
});
}

View File

@ -112,4 +112,11 @@ export type CustomFormat<DateType> = (value: DateType) => string;
export interface PresetDate<T> {
label: VueNode;
value: T;
key: string; // 重要需要用key来高亮选中状态
}
// 扩展的 onChange 回调类型values 和 formatString 都包含第三个 preset 元素
export type RangePickerOnChange<DateType> = (
values: [DateType | null, DateType | null, PresetDate<RangeValue<DateType>> | null],
formatString: [string, string, string | null],
) => void;

View File

@ -49,7 +49,7 @@ function PanelBody<DateType>(_props: PanelBodyProps<DateType>) {
titleCell,
headerCells,
} = useMergeProps(_props);
const { onDateMouseenter, onDateMouseleave, mode } = useInjectPanel();
const { onDateMouseenter, onDateMouseleave, mode, isDoubleClickRef } = useInjectPanel();
const cellPrefixCls = `${prefixCls}-cell`;
@ -99,6 +99,14 @@ function PanelBody<DateType>(_props: PanelBodyProps<DateType>) {
onSelect(currentDate);
}
}}
onDblclick={e => {
e.stopPropagation();
if (!disabled && isDoubleClickRef) {
//
isDoubleClickRef.value = true;
onSelect(currentDate);
}
}}
onMouseenter={() => {
if (!disabled && onDateMouseenter) {
onDateMouseenter(currentDate);