polish(admin): 抽出后台控件包并统一各页布局与交互
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,6 +9,8 @@ import {
|
||||
MessageSquareText,
|
||||
FileText,
|
||||
Users,
|
||||
LayoutDashboard,
|
||||
Coins,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
fetchOverview,
|
||||
@@ -19,6 +21,7 @@ import { authCookieHeader } from "@/lib/cookies";
|
||||
import { getMeCached } from "@/lib/serverData";
|
||||
import { canModerateAny, isSuperOrOwner, isAdminOrAbove } from "@/lib/roles";
|
||||
import Avatar from "@/components/Avatar";
|
||||
import { AdminPageHeader, AdminStatCard } from "@/components/admin";
|
||||
import { formatRelative } from "@/lib/format";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -53,7 +56,7 @@ function PanelHead({ title, extra }: { title: string; extra?: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
/** 待办工作区:待审帖子 / 评论数量,整块进入审核页 */
|
||||
/** 待办:待审帖子 / 评论,整块进入审核页 */
|
||||
function PendingWorkPanel({ counts }: { counts: PendingCounts }) {
|
||||
const total = counts.posts + counts.comments;
|
||||
const idle = total === 0;
|
||||
@@ -70,7 +73,7 @@ function PendingWorkPanel({ counts }: { counts: PendingCounts }) {
|
||||
background: idle ? "var(--tint)" : "var(--ok-soft)",
|
||||
color: idle ? "var(--ink-3)" : "var(--ok)",
|
||||
}}
|
||||
aria-hidden="true"
|
||||
aria-hidden
|
||||
>
|
||||
<ClipboardCheck size={20} />
|
||||
</span>
|
||||
@@ -81,30 +84,26 @@ function PendingWorkPanel({ counts }: { counts: PendingCounts }) {
|
||||
</p>
|
||||
{idle ? (
|
||||
<span
|
||||
className="text-[11.5px] px-2 py-0.5 rounded-full"
|
||||
style={{ background: "var(--tint)", color: "var(--ink-3)" }}
|
||||
className="admin-status is-muted"
|
||||
>
|
||||
暂无待审
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className="text-[11.5px] px-2 py-0.5 rounded-full tabular-nums"
|
||||
style={{ background: "var(--ok-soft)", color: "var(--ok)" }}
|
||||
>
|
||||
<span className="admin-status is-ok tabular-nums">
|
||||
{fmt(total)} 项待处理
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap gap-3 sm:gap-5">
|
||||
<span className="inline-flex items-center gap-1.5 text-[13px]" style={{ color: "var(--ink-2)" }}>
|
||||
<FileText size={14} style={{ color: "var(--ink-3)" }} aria-hidden="true" />
|
||||
<FileText size={14} style={{ color: "var(--ink-3)" }} aria-hidden />
|
||||
待审帖子
|
||||
<strong className="tabular-nums font-extrabold" style={{ color: "var(--ink)" }}>
|
||||
{fmt(counts.posts)}
|
||||
</strong>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5 text-[13px]" style={{ color: "var(--ink-2)" }}>
|
||||
<MessageSquareText size={14} style={{ color: "var(--ink-3)" }} aria-hidden="true" />
|
||||
<MessageSquareText size={14} style={{ color: "var(--ink-3)" }} aria-hidden />
|
||||
待审评论
|
||||
<strong className="tabular-nums font-extrabold" style={{ color: "var(--ink)" }}>
|
||||
{fmt(counts.comments)}
|
||||
@@ -152,31 +151,35 @@ export default async function AdminDashboardPage() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-5">
|
||||
<h1
|
||||
className="text-[22px] sm:text-[26px] font-extrabold tracking-tight"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{greeting(now.getHours())},{me.user?.nickname}
|
||||
</h1>
|
||||
<p className="meta mt-1.5 text-[12.5px]">{dateLabel} · 站点管理</p>
|
||||
</div>
|
||||
<AdminPageHeader
|
||||
icon={LayoutDashboard}
|
||||
title={`${greeting(now.getHours())},${me.user?.nickname ?? ""}`}
|
||||
description={`${dateLabel} · 站点管理`}
|
||||
/>
|
||||
|
||||
{canMod && pendingCounts && <PendingWorkPanel counts={pendingCounts} />}
|
||||
|
||||
{canEconomy && (
|
||||
<Link
|
||||
href="/admin/economy"
|
||||
className="panel mb-4 px-4 sm:px-5 py-4 flex items-center justify-between gap-3 transition-colors hover:bg-[var(--panel-2)]"
|
||||
>
|
||||
<div>
|
||||
<p className="font-medium text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
积分与类型帖看板
|
||||
</p>
|
||||
<p className="meta text-[12.5px] mt-0.5">余额合计、悬赏托管、类型分布与最近流水</p>
|
||||
</div>
|
||||
<ChevronRight size={18} style={{ color: "var(--ink-3)" }} />
|
||||
</Link>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-4">
|
||||
<Link href="/admin/economy" className="block hover-lift rounded-[var(--r)]">
|
||||
<AdminStatCard
|
||||
label="积分看板"
|
||||
value="查看"
|
||||
hint="余额合计、悬赏托管与流水"
|
||||
icon={<Coins size={16} />}
|
||||
color="var(--gold)"
|
||||
soft="var(--gold-soft)"
|
||||
/>
|
||||
</Link>
|
||||
{stats && (
|
||||
<AdminStatCard
|
||||
label="注册成员"
|
||||
value={fmt(stats.users)}
|
||||
hint="全站用户总量"
|
||||
icon={<Users size={16} />}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-4 items-start">
|
||||
@@ -201,16 +204,10 @@ export default async function AdminDashboardPage() {
|
||||
size={34}
|
||||
/>
|
||||
<span className="min-w-0 flex-1 leading-tight">
|
||||
<span
|
||||
className="block text-[13.5px] truncate"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
<span className="block text-[13.5px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{u.nickname}
|
||||
</span>
|
||||
<span
|
||||
className="block text-[11.5px] truncate"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
<span className="block text-[11.5px] truncate" style={{ color: "var(--ink-3)" }}>
|
||||
@{u.username}
|
||||
</span>
|
||||
</span>
|
||||
@@ -218,7 +215,7 @@ export default async function AdminDashboardPage() {
|
||||
className="shrink-0 inline-flex items-center gap-1 text-[11.5px]"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
<UserPlus size={12} aria-hidden="true" />
|
||||
<UserPlus size={12} aria-hidden />
|
||||
{formatRelative(u.created_at)}加入
|
||||
</span>
|
||||
</Link>
|
||||
@@ -233,9 +230,9 @@ export default async function AdminDashboardPage() {
|
||||
color: "var(--accent)",
|
||||
}}
|
||||
>
|
||||
<Users size={14} aria-hidden="true" />
|
||||
<Users size={14} aria-hidden />
|
||||
前往用户权限
|
||||
<ChevronRight size={14} aria-hidden="true" />
|
||||
<ChevronRight size={14} aria-hidden />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
@@ -255,16 +252,13 @@ export default async function AdminDashboardPage() {
|
||||
<span
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center shrink-0"
|
||||
style={{ background: "var(--panel-2)", color: "var(--accent)" }}
|
||||
aria-hidden="true"
|
||||
aria-hidden
|
||||
>
|
||||
<LayoutGrid size={14} />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="flex items-center justify-between gap-2">
|
||||
<span
|
||||
className="text-[13.5px] truncate"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
<span className="text-[13.5px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{b.name}
|
||||
</span>
|
||||
<span
|
||||
@@ -277,7 +271,7 @@ export default async function AdminDashboardPage() {
|
||||
<span
|
||||
className="block mt-1.5 h-1 rounded-full overflow-hidden"
|
||||
style={{ background: "var(--tint)" }}
|
||||
aria-hidden="true"
|
||||
aria-hidden
|
||||
>
|
||||
<span
|
||||
className="block h-full rounded-full"
|
||||
|
||||
Reference in New Issue
Block a user