diff --git a/src/shared/ui/TimeRangeFilter.jsx b/src/shared/ui/TimeRangeFilter.jsx index ce19f9a..430a356 100644 --- a/src/shared/ui/TimeRangeFilter.jsx +++ b/src/shared/ui/TimeRangeFilter.jsx @@ -29,7 +29,14 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时 const [error, setError] = useState(""); const open = Boolean(anchorEl); const days = useMemo(() => calendarDays(viewDate), [viewDate]); - const selectedDate = dateForField(draft, activeField, viewDate); + // 结束时间尚未选择时沿用开始时间作为时分基准,避免自动切换到结束字段后, + // 时分下拉意外跳回当前月 1 日的 00:00。 + const selectedDate = dateForField( + draft, + activeField, + activeField === "end" && draft.startMs ? new Date(draft.startMs) : viewDate, + ); + const rangeDistance = formatRangeDistance(draft.startMs, draft.endMs, withTime); const hasRange = Boolean(range.startMs || range.endMs); useEffect(() => { @@ -71,24 +78,33 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时 }; const activateField = (field) => { setActiveField(field); - setViewDate(dateForField(draft, field, viewDate)); + setViewDate( + dateForField(draft, field, field === "end" && draft.startMs ? new Date(draft.startMs) : viewDate), + ); setError(""); }; const changeMonth = (offset) => { setViewDate((current) => new Date(current.getFullYear(), current.getMonth() + offset, 1)); }; const changeDay = (day) => { + const editingField = activeField; setDraft((current) => { - const base = dateForField(current, activeField, viewDate); + const base = dateForField(current, editingField, viewDate); const timeParts = withTime ? [base.getHours(), base.getMinutes(), 0, 0] - : activeField === "start" + : editingField === "start" ? [0, 0, 0, 0] : [23, 59, 59, 999]; const nextDate = new Date(day.getFullYear(), day.getMonth(), day.getDate(), ...timeParts); - return setRangeField(current, activeField, nextDate.getTime()); + return setRangeField(current, editingField, nextDate.getTime()); }); setViewDate(new Date(day.getFullYear(), day.getMonth(), 1)); + setError(""); + if (editingField === "start") { + // 日期区间遵循“先起点、后终点”的连续操作:开始日期落点后直接进入结束日期选择, + // 用户无需再回到顶部手动点击结束字段。 + setActiveField("end"); + } }; const changeTime = (unit, nextValue) => { setDraft((current) => { @@ -171,6 +187,17 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时 onClick={() => activateField("end")} /> +
+ + + {formatDayPosition(draft.startMs) || "待选择"} + + {rangeDistance} + + {formatDayPosition(draft.endMs) || "待选择"} + + +
changeMonth(-1)}> @@ -180,7 +207,7 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时
-
+
{weekLabels.map((weekday) => ( {weekday} @@ -188,16 +215,24 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时 ))} {days.map((day) => { const isOutside = day.getMonth() !== viewDate.getMonth(); - const isSelected = sameDay(day, selectedDate) && Boolean(draft[`${activeField}Ms`]); + const isRangeStart = sameDayMs(day, draft.startMs); + const isRangeEnd = sameDayMs(day, draft.endMs); + const isInRange = isDayInRange(day, draft.startMs, draft.endMs); + const rangeEdge = + isRangeStart && isRangeEnd ? "始末" : isRangeStart ? "始" : isRangeEnd ? "末" : ""; return (