diff --git a/web/src/components/UsageOverview.tsx b/web/src/components/UsageOverview.tsx index 718b3be..5d3e29c 100644 --- a/web/src/components/UsageOverview.tsx +++ b/web/src/components/UsageOverview.tsx @@ -126,7 +126,7 @@ function UsageOverviewView({ audience, overview, loading, error, periodDays, onP
{metricMode === "duration" ? "聊天活动" : "费用活动"}{overview?.scope.device_name || overview?.scope.household_name || overview?.scope.tenant_name || "当前范围"}
{(["daily", "weekly", "cumulative"] as DensityMode[]).map((mode) => )}
-
{heatmap.months.map((item) => {item.label})}
+
{heatmap.months.map((item) => {item.label})}
一三五
{heatmap.cells.map((cell) => cell.date ?
@@ -163,7 +163,7 @@ function useUsageOverviewRequest(accessToken: string, periodDays: number, load: } function buildHeatmap(overview: UsageOverview | undefined, metric: MetricMode, density: DensityMode) { - if (!overview) return { cells: [] as HeatCell[], months: [] as Array<{ key: string; label: string }> }; + if (!overview) return { cells: [] as HeatCell[], months: [] as MonthMarker[], weeks: 0 }; const daily = new Map(overview.daily.map((item) => [item.date, item])); const start = dateOnly(new Date(overview.from)); const end = dateOnly(new Date(overview.to)); @@ -193,15 +193,22 @@ function buildHeatmap(overview: UsageOverview | undefined, metric: MetricMode, d const padding = (start.getDay() + 6) % 7; const cells: HeatCell[] = Array.from({ length: padding }, (_, index) => ({ key: `pad-${index}` })); values.forEach((item) => cells.push({ ...item, key: item.date, level: level(item.value) })); - const months: Array<{ key: string; label: string }> = []; - values.forEach((item) => { + const months: MonthMarker[] = []; + values.forEach((item, index) => { const key = item.date.slice(0, 7); - if (!months.some((month) => month.key === key)) months.push({ key, label: `${Number(key.slice(5))}月` }); + if (!months.some((month) => month.key === key)) { + months.push({ key, label: `${Number(key.slice(5))}月`, week: Math.floor((padding + index) / 7) + 1 }); + } }); - return { cells, months }; + // A range can start on the last few days of a month. In that case the first + // two labels would occupy adjacent week columns and overlap, so retain the + // first complete month label instead. + if (months.length > 1 && months[1].week - months[0].week < 3) months.shift(); + return { cells, months, weeks: Math.ceil((padding + values.length) / 7) }; } type HeatCell = { key: string; date?: string; rawDuration?: number; rawCost?: number; count?: number; value?: number; level?: number }; +type MonthMarker = { key: string; label: string; week: number }; function dateOnly(value: Date) { return new Date(value.getFullYear(), value.getMonth(), value.getDate()); } function localDateKey(value: Date) { return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, "0")}-${String(value.getDate()).padStart(2, "0")}`; } diff --git a/web/src/styles.css b/web/src/styles.css index 6e48e53..238e4e7 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -3235,7 +3235,8 @@ textarea:focus, .usage-density-tabs button { min-height: 30px !important; padding: 0 7px !important; border-radius: 7px !important; color: #96969c !important; background: transparent !important; font-size: 12px; font-weight: 550; } .usage-density-tabs button.active { color: #25252a !important; background: #f1f1f3 !important; } .usage-heatmap-scroll { overflow-x: auto; padding: 2px 2px 10px; scrollbar-width: thin; } -.usage-months { display: flex; justify-content: space-between; min-width: 790px; margin: 0 0 9px 30px; color: #919197; font-size: 10px; } +.usage-months { display: grid; grid-auto-rows: 14px; gap: 4px; width: max-content; margin: 0 0 9px 30px; color: #919197; font-size: 10px; } +.usage-months span { overflow: visible; white-space: nowrap; } .usage-heatmap-wrap { display: flex; align-items: flex-start; gap: 10px; min-width: 820px; } .usage-weekdays { display: grid; grid-template-rows: repeat(3,18px); gap: 8px; width: 20px; padding-top: 7px; color: #aaaab0; font-size: 9px; } .usage-heatmap { display: grid; grid-auto-flow: column; grid-auto-columns: 11px; grid-template-rows: repeat(7,11px); gap: 4px; width: max-content; min-height: 101px; }