fix(ui): 统计区间文案在日视图恒为空、天数按小时桶误报

v1.9.0 加的「统计区间:起 → 止 (Nd)」正是用来解释"周 > 月"的,但它自己
在日视图上从来不出现在屏幕上。

原因:区间终点从最后一个 bucket 的标签拼出来,而 bucket 粒度随视图变化
—— 日视图按小时("2026-10-03T09"),周/月按天("2026-10-01")。代码无条件
拼 "T00:00:00Z",日视图就成了 "2026-10-03T09T00:00:00Z",Date 拒绝该值,
toISOString() 抛 RangeError,整个赋值语句丢失。页面无任何报错,只是那行
字不见了——恰好是用户最常看的"今日"视图。

第二个问题同源:天数直接用了 st.buckets.length。日视图 5 个桶是 5 个小时,
界面却显示 "(5d)"。改为按窗口起止算天数。

CDP 实测(修复后):
  day   统计区间: 2026-10-03 → 2026-10-03 (1d)   ← 原为空,且曾误报 5d
  week  统计区间: 2026-09-28 → 2026-10-03 (6d)
  month 统计区间: 2026-10-01 → 2026-10-03 (3d)
  all  (空,正确:终身累计没有窗口)

判据一条 + 变异:恢复原来的盲目拼接 → 判红。

Co-Authored-By: ModelRouter <noreply@modelrouter.dev>
This commit is contained in:
JianFeeeee
2026-10-03 12:48:17 +08:00
parent 97864895d4
commit dac0b4ce93
2 changed files with 56 additions and 10 deletions

View File

@ -2016,16 +2016,41 @@ const pnote = $("#period-note");
// like a bug unless the dates are on screen — so print them.
const prange = $("#period-range");
if (prange && st.period && st.period !== "all" && st.from) {
const d0 = new Date(st.from);
const d1 = st.buckets && st.buckets.length
? new Date(st.buckets[st.buckets.length - 1].bucket + "T00:00:00Z")
: d0;
const f = (d) =>
d.toISOString().slice(0, 10);
prange.textContent =
t("periodRange") +
" " + f(d0) + " → " + f(d1) +
(st.buckets ? " (" + st.buckets.length + "d)" : "");
// Bucket labels are hourly for a day ("2026-10-03T09") and daily
// for a week or month ("2026-10-01"). Appending "T00:00:00Z" to
// the hourly form builds "2026-10-03T09T00:00:00Z", which Date
// rejects — toISOString() then throws and the whole assignment is
// lost, leaving the range line silently empty on the day view (the
// one people look at most).
const f = (d) => d.toISOString().slice(0, 10);
const label = st.buckets && st.buckets.length
? st.buckets[st.buckets.length - 1].bucket
: "";
let endMs = 0;
if (/^\d{4}-\d{2}-\d{2}$/.test(label)) {
endMs = new Date(label + "T00:00:00Z").getTime();
} else if (/^\d{4}-\d{2}-\d{2}T\d{2}$/.test(label)) {
// An hourly bucket covers its hour, so the day it belongs to is
// what the range should end on.
endMs = new Date(label + ":00:00Z").getTime();
}
const from = new Date(st.from);
const end = endMs ? new Date(endMs) : from;
// The bucket count is not a day count: the day view buckets by
// HOUR, so "(5d)" for five hours of today was simply wrong. Days
// covered comes from the window itself, which is what the reader
// is trying to establish.
let days = 1;
if (endMs) {
days = Math.max(
1,
Math.round((endMs - from.getTime()) / 86400000) + 1,
);
}
const tail = " (" + days + "d)";
prange.textContent = endMs
? t("periodRange") + " " + f(from) + " → " + f(end) + tail
: t("periodRange") + " " + f(from) + tail;
} else if (prange) {
prange.textContent = "";
}