import type { Metadata } from "next"; import Link from "next/link"; import { cookies } from "next/headers"; import { Users, PenLine, MessageCircle, Activity, Megaphone, Palette, ChevronRight, UserPlus, LayoutGrid, ClipboardCheck, } from "lucide-react"; import { fetchOverview, type OverviewResponse } from "@/lib/api"; import { authCookieHeader } from "@/lib/cookies"; import { getMeCached } from "@/lib/serverData"; import { canModerateAny, isAdminOrAbove, isSuperOrOwner } from "@/lib/roles"; import Avatar from "@/components/Avatar"; import { formatRelative } from "@/lib/format"; export const metadata: Metadata = { title: "管理仪表盘", }; // 精确千分位(管理员视角需要真实数值,不用首页的紧凑 K/M) const fmt = (n: number) => n.toLocaleString("zh-CN"); function greeting(hour: number) { if (hour < 6) return "凌晨好"; if (hour < 12) return "上午好"; if (hour < 14) return "中午好"; if (hour < 18) return "下午好"; return "晚上好"; } // 统计 Bento 卡:总量 + 今日增量,语义色与全站令牌一致 function StatCard({ label, value, foot, icon, color, soft, }: { label: string; value: string; foot: string; icon: React.ReactNode; color: string; soft: string; }) { return (

{label}

{value}

{foot}

); } // 快捷管理入口:整卡可点(stretch-link),图标 + 标题 + 描述 + 箭头 function QuickLink({ href, icon, title, desc, soft, color, }: { href: string; icon: React.ReactNode; title: string; desc: string; soft: string; color: string; }) { return (

{title}

{desc}

); } function PanelHead({ title, extra }: { title: string; extra?: string }) { return (
{title} {extra && ( {extra} )}
); } export default async function AdminDashboardPage() { const cookie = authCookieHeader(await cookies()); // 与 layout 的 /me 请求共享 React cache;概览失败降级,不阻塞仪表盘骨架 const [me, overview] = await Promise.all([ getMeCached(cookie || undefined), fetchOverview(cookie).catch(() => null), ]); const now = new Date(); const dateLabel = new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "long", day: "numeric", weekday: "long", }).format(now); const stats = overview?.stats; const boards = overview?.boards ?? []; const newUsers = overview?.new_users ?? []; const topBoards = [...boards].sort((a, b) => b.post_count - a.post_count).slice(0, 6); const maxBoardCount = Math.max(1, ...topBoards.map((b) => b.post_count)); return (
{/* 页头 */}

{greeting(now.getHours())},{me.user?.nickname}

{dateLabel} · 这里是站点运营全貌

{/* 统计 Bento */}
} color="var(--accent)" soft="var(--accent-soft)" /> } color="var(--clay)" soft="var(--clay-soft)" /> } color="var(--ok)" soft="var(--ok-soft)" /> } color="var(--gold)" soft="var(--gold-soft)" />
{/* 快捷入口(按角色权限过滤,与侧边栏一致;后端路由另有守卫) */}

快捷管理

{canModerateAny(me.user) && ( } title="内容审核" desc="审核普通用户提交的帖子与评论,通过后内容公开可见" soft="var(--ok-soft)" color="var(--ok)" /> )} {isAdminOrAbove(me.user?.role) && ( } title="公告管理" desc="发布、编辑与下线站点公告,支持标签颜色与草稿状态" soft="var(--clay-soft)" color="var(--clay)" /> )} {isSuperOrOwner(me.user?.role) && ( } title="用户权限" desc="查看全站成员,授予管理角色与板块授权,封禁违规账号" soft="var(--accent-soft)" color="var(--accent)" /> )} {isSuperOrOwner(me.user?.role) && ( } title="外观设置" desc="配置全站主题强调色,实时预览后保存并对所有用户生效" soft="var(--gold-soft)" color="var(--gold)" /> )}
{/* 成员与板块 */}
{/* 最新注册成员 */}
{newUsers.length === 0 ? (

暂无成员数据

) : ( newUsers.slice(0, 6).map((u) => ( {u.nickname} @{u.username} )) )}
{/* 板块分布 */}
{topBoards.length === 0 ? (

暂无板块数据

) : ( topBoards.map((b) => ( {b.name} {fmt(b.post_count)} {/* 相对占比迷你条(纯装饰) */} )) )}
); }