refactor(frontend): 重构站点数据面板与签到组件

将原有的站点统计卡片重构为bento布局样式,把签到组件内嵌到站点数据面板中,统一UI风格并简化代码结构
This commit is contained in:
2026-09-14 01:15:59 +08:00
parent 996a870015
commit e8882664fb
4 changed files with 202 additions and 117 deletions

View File

@@ -9,6 +9,8 @@ import {
Megaphone,
BarChart3,
PenLine,
Activity,
TrendingUp,
} from "lucide-react";
import type {
PostListItem,
@@ -19,52 +21,43 @@ import type {
CheckinStatus,
} from "@/lib/api";
import Avatar from "@/components/Avatar";
import CheckinCard from "./CheckinCard";
import { PulseCheckin } from "./CheckinCard";
import { FRIEND_LINKS } from "@/lib/friendLinks";
import { formatCompact, formatShortDate } from "@/lib/format";
/* ---------- 站点数据(2×2 极简统计表) ---------- */
/* ---------- 站点数据(2×2 bento 统计瓦片) ---------- */
function StatCell({
index,
label,
value,
foot,
}: {
index: number;
label: string;
value: string;
foot: string;
}) {
const rightCol = index % 2 === 1;
const bottomRow = index >= 2;
return (
<div
className="min-w-0 py-3.5"
style={{
borderLeft: rightCol ? "1px solid var(--line)" : undefined,
borderTop: bottomRow ? "1px solid var(--line)" : undefined,
paddingLeft: rightCol ? 14 : 0,
paddingRight: rightCol ? 0 : 14,
}}
>
<p className="text-[12px]" style={{ color: "var(--ink-3)" }}>
{label}
</p>
<p
className="mt-1.5 text-[20px] font-semibold leading-none tracking-tight tabular-nums"
style={{ color: "var(--ink)" }}
>
{value}
</p>
<p className="mt-2 text-[11px] tabular-nums" style={{ color: "var(--ink-3)" }}>
{foot}
</p>
</div>
);
}
function PulseCard({ stats }: { stats: OverviewStats }) {
function PulseCard({ stats, checkin }: { stats: OverviewStats; checkin?: CheckinStatus | null }) {
const cells = [
{
icon: <Users size={12.5} />,
label: "注册用户",
value: formatCompact(stats.users),
foot: `+${formatCompact(stats.today_users)} 今日`,
up: true,
},
{
icon: <PenLine size={12.5} />,
label: "话题总数",
value: formatCompact(stats.posts),
foot: `+${formatCompact(stats.today_posts)} 今日`,
up: true,
},
{
icon: <MessageCircle size={12.5} />,
label: "回复总数",
value: formatCompact(stats.comments),
foot: `+${formatCompact(stats.today_comments)} 今日`,
up: true,
},
{
icon: <Activity size={12.5} />,
label: "在线用户",
value: formatCompact(stats.online),
foot: `峰值 ${formatCompact(stats.peak_online)}`,
up: false,
},
];
return (
<section className="j13-module" aria-label="站点数据">
<div className="flex items-center justify-between">
@@ -76,15 +69,29 @@ function PulseCard({ stats }: { stats: OverviewStats }) {
style={{ color: "var(--ink-3)" }}
title="近 5 分钟在线"
>
<span className="h-1.5 w-1.5 rounded-full" style={{ background: "var(--ok)" }} />
<span className="j13-pulse-dot" aria-hidden="true" />
实时
</span>
</div>
<div className="mt-2 grid grid-cols-2">
<StatCell index={0} label="注册用户" value={formatCompact(stats.users)} foot={`+${stats.today_users} 今日`} />
<StatCell index={1} label="话题总数" value={formatCompact(stats.posts)} foot={`+${stats.today_posts} 今日`} />
<StatCell index={2} label="回复总数" value={formatCompact(stats.comments)} foot={`+${stats.today_comments} 今日`} />
<StatCell index={3} label="在线用户" value={formatCompact(stats.online)} foot={`峰值 ${formatCompact(stats.peak_online)}`} />
<div className="j13-pulse mt-3">
{cells.map((c) => (
<div key={c.label} className="j13-pulse-cell">
<p className="j13-pulse-lab" style={{ color: "var(--ink-3)" }}>
<span style={{ color: "var(--accent)" }} aria-hidden="true">
{c.icon}
</span>
{c.label}
</p>
<p className="j13-pulse-num">{c.value}</p>
<p className={`j13-pulse-foot${c.up ? " is-up" : ""}`}>
{c.up && <TrendingUp size={10.5} aria-hidden="true" />}
{c.foot}
</p>
</div>
))}
</div>
<div className="j13-pulse-checkin">
<PulseCheckin initial={checkin} />
</div>
</section>
);
@@ -319,9 +326,8 @@ export function CommunityPanels({
return (
<aside className="hidden w-[280px] shrink-0 lg:block xl:w-[300px]" aria-label="社区面板">
<div className="sticky top-24 space-y-4">
<PulseCard stats={stats} />
<PulseCard stats={stats} checkin={checkin} />
<Announcements items={announcements} />
<CheckinCard initial={checkin} />
<HotRanking posts={hot} />
<NewUsers users={newUsers} />
<ActiveUsers users={activeUsers} />
@@ -332,16 +338,17 @@ export function CommunityPanels({
}
export function CommunityPanelsMobile({
stats,
hot,
activeUsers,
announcements,
newUsers,
checkin,
}: Omit<PanelsProps, "stats">) {
}: PanelsProps) {
return (
<div className="space-y-4 lg:hidden" aria-label="社区面板">
<PulseCard stats={stats} checkin={checkin} />
<Announcements items={announcements} />
<CheckinCard initial={checkin} />
<HotRanking posts={hot} />
<NewUsers users={newUsers} />
<ActiveUsers users={activeUsers} />