refactor: show mileage data freshness

This commit is contained in:
kkfluous
2026-08-07 15:50:24 +08:00
parent de2a0896c4
commit 8c6a3ad831
3 changed files with 38 additions and 4 deletions
+12 -4
View File
@@ -23,6 +23,7 @@ import {
parseMileageDrillContext,
type MileageDrillContext,
} from './drill-context';
import { formatMileageSnapshotTime } from './data-freshness';
const HIGH_MILEAGE_ALERT_TARGETS = new Set([
'交投40辆4.5T普货',
@@ -494,7 +495,7 @@ export default function MonitoringView() {
const [pageLoading, setPageLoading] = useState(true);
const [pageError, setPageError] = useState<{ message: string; scope: 'first' | 'more' } | null>(null);
const [manualRefreshing, setManualRefreshing] = useState(false);
const [lastRefreshedAt, setLastRefreshedAt] = useState<Date | null>(null);
const [dataUpdatedAt, setDataUpdatedAt] = useState<string | null>(null);
const [showBackToTop, setShowBackToTop] = useState(false);
const [relativeNow, setRelativeNow] = useState(() => Date.now());
const PAGE_SIZE = 50;
@@ -599,7 +600,7 @@ export default function MonitoringView() {
setTotal(d.total);
setPage(1);
setHasMore(d.page < d.totalPages);
setLastRefreshedAt(new Date());
setDataUpdatedAt(d.updatedAt);
}).catch(error => {
setPageError({
message: error instanceof Error ? error.message : '里程数据加载失败,请稍后重试',
@@ -651,6 +652,7 @@ export default function MonitoringView() {
setVehicles(prev => [...prev, ...d.vehicles]);
setPage(nextPage);
setHasMore(nextPage < d.totalPages);
setDataUpdatedAt(d.updatedAt);
}).catch(error => {
setPageError({
message: error instanceof Error ? error.message : '更多车辆加载失败,请稍后重试',
@@ -762,6 +764,7 @@ export default function MonitoringView() {
setFullscreenVehicles(d.vehicles);
setFullscreenStats(d.stats);
setFilterOptions(d.filters);
setDataUpdatedAt(d.updatedAt);
}).catch(() => {}).finally(() => setFullscreenLoading(false));
}, [isFullscreen, sortBy, sortOrder, searchTerm, filterDept, filterCustomer, filterRentStatus, filterPlatePrefix, filterTargetNames, filterRegion, filterPlates, rangeStart, rangeEnd, fullscreenRefresh, filterBrands, sourcePriority]);
@@ -1078,14 +1081,19 @@ export default function MonitoringView() {
</div>
<div className="mt-1 flex items-center gap-1.5">
<span className="h-1.5 w-1.5 rounded-full bg-blue-500" />
<span className="text-[9px] font-bold uppercase tracking-tight text-slate-400"> OneOS </span>
<span
className="truncate text-[9px] font-bold tracking-tight text-slate-400"
title={`OneOS 分钟级快照 · 数据截至 ${formatMileageSnapshotTime(dataUpdatedAt)}`}
>
OneOS · {formatMileageSnapshotTime(dataUpdatedAt)}
</span>
</div>
</div>
</div>
<div className="flex flex-shrink-0 items-center gap-1.5">
<span className="hidden text-[9px] font-bold tabular-nums text-slate-400 sm:inline">
{lastRefreshedAt ? lastRefreshedAt.toLocaleTimeString('zh-CN', { hour12: false }) : '--:--:--'}
{formatMileageSnapshotTime(dataUpdatedAt)}
</span>
<button
type="button"
@@ -0,0 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { formatMileageSnapshotTime } from './data-freshness.js';
test('formats snapshot instants in Asia/Shanghai', () => {
assert.equal(formatMileageSnapshotTime('2026-08-07T07:45:06.000Z'), '15:45:06');
});
test('keeps historical date snapshots explicit', () => {
assert.equal(formatMileageSnapshotTime('2026-08-06'), '2026-08-06');
assert.equal(formatMileageSnapshotTime(null), '--:--:--');
});
+14
View File
@@ -0,0 +1,14 @@
export function formatMileageSnapshotTime(value: string | null): string {
if (!value) return '--:--:--';
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
return new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
hourCycle: 'h23',
}).format(date);
}