refactor(frontend): 重构站点数据面板与签到组件
将原有的站点统计卡片重构为bento布局样式,把签到组件内嵌到站点数据面板中,统一UI风格并简化代码结构
This commit is contained in:
@@ -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} />
|
||||
|
||||
Reference in New Issue
Block a user