fix: 对齐监控概览双通道口径文案与诊断抽样

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-31 08:58:03 +08:00
parent 34f3d088ad
commit a91d917fa9
5 changed files with 189 additions and 25 deletions

View File

@@ -79,23 +79,29 @@ function GeoRankTable({
cities,
asns,
mode,
mapMode,
rankMode,
onRankMode,
hasCountryHits,
}: {
cities: MonitorCityItem[];
asns: MonitorASNItem[];
mode: MapMode;
rankMode: RankMode;
onRankMode: (m: RankMode) => void;
/** 近 30 日是否已有国家级浏览量(用于区分「无 PV 地理」与缺库) */
hasCountryHits: boolean;
}) {
const cityRows = mode === 'china'
? cities.filter((i) => CHINA_CODES.has((i.country || '').toUpperCase()))
: cities;
const rows = rankMode === 'city' ? cityRows : asns;
const emptyHint = rankMode === 'city'
? '放置 IP2Location DB3 BIN 后可见城市排行'
: '放置 GeoLite2-ASN.mmdb 后可见运营商排行';
? (hasCountryHits
? '需前台路由产生带城市的浏览量;仅请求日志不会进入此排行。请确认已放置 IP2Location DB3 BIN。'
: '需前台路由产生浏览量后才会有城市排行;仅请求日志不会点亮此处。')
: (hasCountryHits
? '需浏览量写入 ASN请确认已放置 GeoLite2-ASN.mmdb。仅请求日志不会进入此排行。'
: '需前台路由产生浏览量后才会有运营商排行;仅请求日志不会点亮此处。');
return (
<div className="admin-monitor-rank">
@@ -117,7 +123,7 @@ function GeoRankTable({
</div>
{rows.length === 0 ? (
<div className="admin-monitor-geo-empty">
<p></p>
<p></p>
<p className="admin-monitor-muted">{emptyHint}</p>
</div>
) : (
@@ -282,18 +288,24 @@ export default function AdminMonitorPage() {
const metrics = useMemo(() => {
if (!overview) return [];
const pvHint = '前台浏览';
const accessHintShort = '服务端请求';
const accessHintFull = '服务端请求(含 API重启后重计';
return [
{ label: '浏览量', value: formatNum(overview.pageviews) },
{ label: '访客数', value: formatNum(overview.visitors) },
{ label: '独立 IP', value: formatNum(overview.unique_ips) },
{ label: '流量', value: formatBytes(overview.traffic) },
{ label: '蜘蛛', value: formatNum(overview.bots) },
{ label: '请求数', value: formatNum(overview.requests) },
{ label: '4xx', value: formatNum(overview.status_4xx) },
{ label: '5xx', value: formatNum(overview.status_5xx) },
{ label: '浏览量', hint: pvHint, title: pvHint, value: formatNum(overview.pageviews) },
{ label: '访客数', hint: pvHint, title: pvHint, value: formatNum(overview.visitors) },
{ label: '独立 IP', hint: accessHintShort, title: accessHintFull, value: formatNum(overview.unique_ips) },
{ label: '流量', hint: accessHintShort, title: accessHintFull, value: formatBytes(overview.traffic) },
{ label: '蜘蛛', hint: accessHintShort, title: accessHintFull, value: formatNum(overview.bots) },
{ label: '请求数', hint: accessHintShort, title: accessHintFull, value: formatNum(overview.requests) },
{ label: '4xx', hint: accessHintShort, title: accessHintFull, value: formatNum(overview.status_4xx) },
{ label: '5xx', hint: accessHintShort, title: accessHintFull, value: formatNum(overview.status_5xx) },
];
}, [overview]);
const hasCountryHits = (geo?.countries?.length || 0) > 0;
const hasRegionHits = (geo?.regions?.length || 0) > 0;
const saveSettings = async () => {
if (!settings) return;
setSaving(true);
@@ -352,7 +364,7 @@ export default function AdminMonitorPage() {
</h1>
<p className="admin-page-desc">
/访 pageview访 /api
/访访 pageview访 /api
</p>
</div>
</div>
@@ -391,8 +403,11 @@ export default function AdminMonitorPage() {
</div>
<div className="admin-monitor-today-grid">
{metrics.map((m) => (
<div key={m.label} className="admin-monitor-today-item">
<div className="admin-monitor-today-label">{m.label}</div>
<div key={m.label} className="admin-monitor-today-item" title={m.title}>
<div className="admin-monitor-today-label">
{m.label}
<span className="admin-monitor-today-hint">{m.hint}</span>
</div>
<div className="admin-monitor-today-value">{m.value}</div>
</div>
))}
@@ -404,7 +419,13 @@ export default function AdminMonitorPage() {
<div className="admin-card-head admin-monitor-map-head">
<div className="admin-monitor-today-head" style={{ marginBottom: 0 }}>
<span className="admin-monitor-section-bar" aria-hidden />
<h2 className="admin-monitor-section-title">访30 </h2>
<div>
<h2 className="admin-monitor-section-title">访</h2>
<p className="admin-monitor-map-sub">
· 30
{mapMode === 'china' ? ' · 按省/区填色' : ' · 按国家填色'}
</p>
</div>
</div>
<div className="admin-monitor-seg" role="group" aria-label="地图范围">
<button
@@ -428,7 +449,11 @@ export default function AdminMonitorPage() {
{mapMode === 'world' ? (
<MonitorWorldMap items={geoCountries(geo)} />
) : (
<MonitorChinaMap regions={geo?.regions || []} />
<MonitorChinaMap
regions={geo?.regions || []}
hasCountryHits={hasCountryHits}
hasRegionHits={hasRegionHits}
/>
)}
</div>
<div className="admin-monitor-map-side">
@@ -438,6 +463,7 @@ export default function AdminMonitorPage() {
mode={mapMode}
rankMode={rankMode}
onRankMode={setRankMode}
hasCountryHits={hasCountryHits}
/>
</div>
</div>
@@ -554,10 +580,10 @@ export default function AdminMonitorPage() {
</thead>
<tbody>
{logs.map((row) => {
const placeBits = [row.region, row.city, row.as_org].filter(Boolean);
const geoBits = [
row.country ? countryLabel(row.country) : '',
row.city || '',
row.as_org || '',
...placeBits,
].filter(Boolean);
return (
<tr key={row.id}>
@@ -568,9 +594,9 @@ export default function AdminMonitorPage() {
<td className="admin-table-mono">
{row.ip || '—'}
{row.is_bot ? ' · bot' : ''}
{(row.city || row.as_org) ? (
{placeBits.length > 0 ? (
<div className="admin-monitor-muted" style={{ fontSize: 12 }}>
{[row.city, row.as_org].filter(Boolean).join(' · ')}
{placeBits.join(' · ')}
</div>
) : null}
</td>