From cc64df3e70eafcfe57de874b34fd90df317d9b8a Mon Sep 17 00:00:00 2001 From: justme Date: Tue, 20 Oct 2026 20:44:04 +0800 Subject: [PATCH] [CI] Keep line cards visible and time windows current; add 24h to 96h ranges --- web/display/src/LineStrips.tsx | 20 ++++++++++---------- web/display/src/display.css | 16 ++-------------- web/display/src/pages/Overview.tsx | 6 +++--- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/web/display/src/LineStrips.tsx b/web/display/src/LineStrips.tsx index e0587e2..c580d4a 100644 --- a/web/display/src/LineStrips.tsx +++ b/web/display/src/LineStrips.tsx @@ -9,6 +9,9 @@ export const OVERVIEW_RANGES = [ { value: '3h', label: '近 3 小时', hours: 3, step: '5m', stepMin: 5 }, { value: '6h', label: '近 6 小时', hours: 6, step: '10m', stepMin: 10 }, { value: '12h', label: '近 12 小时', hours: 12, step: '15m', stepMin: 15 }, + { value: '24h', label: '近 24 小时', hours: 24, step: '30m', stepMin: 30 }, + { value: '48h', label: '近 48 小时', hours: 48, step: '1h', stepMin: 60 }, + { value: '96h', label: '近 96 小时', hours: 96, step: '2h', stepMin: 120 }, ] as const export type OverviewRange = (typeof OVERVIEW_RANGES)[number]['value'] @@ -17,12 +20,12 @@ type Level = 'ok' | 'warn' | 'bad' | 'none' const levelOf = (loss: number | null | undefined): Level => (loss === null || loss === undefined ? 'none' : loss <= 0.5 ? 'ok' : loss < 3 ? 'warn' : 'bad') -const clock = (ms: number) => new Date(ms).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', hour12: false }) +const clock = (ms: number, showDate: boolean) => new Date(ms).toLocaleString('zh-CN', { ...(showDate ? { month: '2-digit', day: '2-digit' } : {}), hour: '2-digit', minute: '2-digit', hour12: false }) interface Props { lineId: string range: OverviewRange - /** 近 3 小时的汇总(左上、右上的数字)。 */ + /** 所选时间范围的汇总(左上、右上的数字)。 */ m: Record } @@ -30,7 +33,7 @@ interface Props { export function LineStrips({ lineId, range, m }: Props) { const info = OVERVIEW_RANGES.find((r) => r.value === range) ?? OVERVIEW_RANGES[0] const series = useQuery( - () => api.get(`/api/public/lines/${encodeURIComponent(lineId)}/series`, { range: info.value, anchor: 'latest', step: info.step, preset: 'quic,tcp', metrics: 'loss.up,loss.down,loss.total,rtt.p50,rtt.p95,rtt.p99,tcp.retrans,tcp.rtt.p50' }), + () => api.get(`/api/public/lines/${encodeURIComponent(lineId)}/series`, { range: info.value, anchor: 'now', step: info.step, preset: 'quic,tcp', metrics: 'loss.up,loss.down,loss.total,rtt.p50,rtt.p95,rtt.p99,tcp.retrans,tcp.rtt.p50' }), [lineId, info.value], 30_000, ) @@ -50,7 +53,7 @@ export function LineStrips({ lineId, range, m }: Props) { const r = bars.getBoundingClientRect() setHover(Math.min(t.length - 1, Math.max(0, Math.floor(((e.clientX - r.left) / r.width) * t.length)))) } - const common = { t, ready: Boolean(s), cells: Math.round((info.hours * 60) / info.stepMin), stepText: `${info.stepMin} 分钟`, hoursText: `${info.hours} 小时`, hover } + const common = { t, ready: Boolean(s), cells: Math.round((info.hours * 60) / info.stepMin), stepText: info.stepMin >= 60 ? `${info.stepMin / 60} 小时` : `${info.stepMin} 分钟`, hoursText: `${info.hours} 小时`, hover } const at = shown !== null && t[shown] !== undefined ? shown : null const lossUp = col('loss.up') const lossDown = col('loss.down') @@ -62,12 +65,9 @@ export function LineStrips({ lineId, range, m }: Props) { const retransValue = m['tcp.retrans'] const retransTone = levelOf(retransValue) const retransText = retransValue === null || retransValue === undefined ? '重传 —' : `${fmtPct(retransValue)} 重传` - const hasMeasurements = Object.values(s?.values ?? {}).some((values) => values.some((value) => value !== null && value !== undefined)) return (
setHover(null)}> - {s && !hasMeasurements ? ( -
近 {info.hours} 小时暂无测量数据
- ) :
+
QUIC{fmtMs(m['rtt.p50'])}悬停看 p95 / p99 {series.error ? 数据未更新 : null} @@ -105,12 +105,12 @@ export function LineStrips({ lineId, range, m }: Props) { ))}
-
} +
{at !== null ? ( <> - {clock(t[at])}–{clock(t[at] + info.stepMin * 60_000)} + {clock(t[at], info.hours >= 24)}–{clock(t[at] + info.stepMin * 60_000, info.hours >= 24)}
QUIC Datagram diff --git a/web/display/src/display.css b/web/display/src/display.css index cfde1c1..7b65ad7 100644 --- a/web/display/src/display.css +++ b/web/display/src/display.css @@ -1839,14 +1839,8 @@ background: color-mix(in srgb, var(--app-ink) 7%, transparent); } -.agent-card .line-link.disconnected, -.agent-card .line-link.disconnected:hover { - filter: grayscale(1); - opacity: 0.62; -} - -.line-range.disconnected-label { - color: var(--app-ink-faint); +.line-range.connection-issue { + color: var(--app-warn); } @@ -2019,12 +2013,6 @@ input { color: var(--app-ink-faint); } -.line-no-data { - padding: 8px 2px 2px; - color: var(--app-ink-faint); - font-size: 12px; -} - /* 线路面板:按三网分组 */ .carrier-group + .carrier-group { margin-top: 18px; diff --git a/web/display/src/pages/Overview.tsx b/web/display/src/pages/Overview.tsx index a31a111..55b7078 100644 --- a/web/display/src/pages/Overview.tsx +++ b/web/display/src/pages/Overview.tsx @@ -152,7 +152,7 @@ function LineCard({ l, range, infos }: { l: OverviewLineDto; range: OverviewRang ? 'QUIC 正在连接' : null return ( - +
{probeInfo?.country ? : null} @@ -163,7 +163,7 @@ function LineCard({ l, range, infos }: { l: OverviewLineDto; range: OverviewRang {l.name} {connectionIssue ? `${connectionIssue} · ${rangeLabel}` : rangeLabel} @@ -179,7 +179,7 @@ export function Overview() { const [sort, setSort] = useState('name') const { hosts, live } = useHosts() const infos = useNodeInfos() - // 近多久的汇总和走势:3 / 6 / 12 小时,记在链接里(?range=6h) + // 汇总和走势使用同一个时间范围,记在链接里(例如 ?range=48h)。 const [params, setParams] = useSearchParams() const ipVersion = ipVersionOf(params.get('ip_version')) const ipv6 = useQuery(() => ipVersion === 'ipv6' ? api.get>('/api/public/ipv6') : Promise.resolve(undefined), [ipVersion], 15_000)