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 (