"use client"; import { useCallback, useState } from "react"; import { Activity, Zap, Users, Clock } from "lucide-react"; import { AdminStatCard } from "@/components/admin"; import { AdminChartCard, SingleAreaChart } from "@/components/admin/charts"; import { apiStatsRealtime, type VisitRealtime } from "@/lib/api"; import { CardShell, StatsState, TableEmpty, deviceLabel, formatInt, kindLabel, usePolling } from "../ui"; export default function RealtimePage() { const [data, setData] = useState(null); const [error, setError] = useState(""); const load = useCallback(() => { apiStatsRealtime() .then((d) => { setData(d); setError(""); }) .catch(() => setError("加载失败,请稍后重试")); }, []); usePolling(load, 15_000); if (!data) return ; // 兜底:旧后端可能返回 null(Go nil 切片) const minutes = (data.minutes ?? []).map((m) => ({ // SingleAreaChart 取 date 字段画 X 轴:只留 HH:MM date: m.minute.length >= 16 ? m.minute.slice(11) : m.minute, pv: m.pv, })); const topPages = data.top_pages ?? []; const recent = data.recent ?? []; return (

每 15 秒自动刷新;切回前台标签页时立即刷新。

{error ? : null}
} /> } color="var(--ok)" soft="var(--ok-soft, var(--panel-2))" /> } color="var(--gold)" soft="var(--gold-soft)" /> } color="var(--ink-2)" soft="var(--panel-2)" />
{minutes.length ? ( ) : ( )}
{topPages.length ? (
    {topPages.map((p) => (
  • {p.key || "/"} {formatInt(p.count)}
  • ))}
) : ( )}
{recent.length ? (
    {recent.map((e) => (
  • {new Date(e.created_at).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })} {e.path || "/"} {deviceLabel(e.device)} · {kindLabel(e.kind)}
  • ))}
) : ( )}
); }