feat: 关于本站对齐站点公告,增加全部与管理入口
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
156
frontend/app/about/page.tsx
Normal file
156
frontend/app/about/page.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import type { Metadata } from "next";
|
||||
import { cookies } from "next/headers";
|
||||
import Link from "next/link";
|
||||
import { Info } from "lucide-react";
|
||||
import {
|
||||
fetchPublishedSitePages,
|
||||
fetchOverview,
|
||||
type SitePageSummary,
|
||||
} from "@/lib/api";
|
||||
import { authCookieHeader } from "@/lib/cookies";
|
||||
import { getMeCached, getPublicSettingsCached } from "@/lib/serverData";
|
||||
import { isAdminOrAbove } from "@/lib/roles";
|
||||
import { formatDate } from "@/lib/format";
|
||||
import { pageGlyph } from "@/lib/pageGlyph";
|
||||
import {
|
||||
SiteDocShell,
|
||||
SiteDocIdentity,
|
||||
SiteDocAnnoNav,
|
||||
SiteDocBreadcrumb,
|
||||
} from "@/components/site-doc";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "关于本站",
|
||||
description: "站点说明、政策与帮助文档",
|
||||
};
|
||||
|
||||
function PageRow({
|
||||
p,
|
||||
showDivider,
|
||||
}: {
|
||||
p: SitePageSummary;
|
||||
showDivider: boolean;
|
||||
}) {
|
||||
const Icon = pageGlyph(p.slug);
|
||||
return (
|
||||
<Link
|
||||
href={`/p/${p.slug}`}
|
||||
className="block px-5 sm:px-6 py-4 sm:py-5 transition-colors hover:bg-[var(--accent-soft)] focus-visible:bg-[var(--accent-soft)]"
|
||||
style={showDivider ? { borderTop: "1px solid var(--line)" } : undefined}
|
||||
>
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<span
|
||||
className="w-9 h-9 rounded-lg flex items-center justify-center shrink-0 mt-0.5"
|
||||
style={{ background: "var(--accent-soft)", color: "var(--accent)" }}
|
||||
aria-hidden
|
||||
>
|
||||
<Icon size={16} strokeWidth={2} />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="flex items-center gap-2 flex-wrap">
|
||||
<h2
|
||||
className="text-[16px] sm:text-[17px] font-semibold leading-snug tracking-tight"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{p.title}
|
||||
</h2>
|
||||
<time className="meta text-[12px] tabular-nums ml-auto" dateTime={p.updated_at}>
|
||||
{formatDate(p.updated_at || p.created_at)}
|
||||
</time>
|
||||
</span>
|
||||
{p.excerpt ? (
|
||||
<p className="mt-1.5 text-[13.5px] leading-relaxed line-clamp-2" style={{ color: "var(--ink-2)" }}>
|
||||
{p.excerpt}
|
||||
</p>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function AboutIndexPage() {
|
||||
const cookie = authCookieHeader(await cookies());
|
||||
let pages: SitePageSummary[] = [];
|
||||
let error = "";
|
||||
try {
|
||||
const res = await fetchPublishedSitePages();
|
||||
pages = res.pages ?? [];
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : "加载页面失败";
|
||||
}
|
||||
|
||||
const [overview, settings, me] = await Promise.all([
|
||||
fetchOverview().catch(() => null),
|
||||
getPublicSettingsCached(),
|
||||
getMeCached(cookie || undefined),
|
||||
]);
|
||||
const announcements = overview?.announcements ?? [];
|
||||
const siteLead = settings.site_description?.trim() || undefined;
|
||||
const canManagePages = !!me.user && isAdminOrAbove(me.user.role);
|
||||
|
||||
const left = <SiteDocIdentity kind="about" lead={siteLead} />;
|
||||
const right = <SiteDocAnnoNav items={announcements} />;
|
||||
|
||||
return (
|
||||
<SiteDocShell
|
||||
left={left}
|
||||
right={right}
|
||||
breadcrumb={
|
||||
<SiteDocBreadcrumb
|
||||
items={[
|
||||
{ href: "/", label: "首页" },
|
||||
{ label: "关于本站" },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
mobileExtras={
|
||||
announcements.length > 0 ? <SiteDocAnnoNav items={announcements} /> : null
|
||||
}
|
||||
>
|
||||
<header className="mb-5 flex items-end justify-between gap-3">
|
||||
<div>
|
||||
<h1
|
||||
className="text-[24px] sm:text-[28px] font-extrabold tracking-tight flex items-center gap-2"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
<Info size={26} style={{ color: "var(--accent)" }} aria-hidden />
|
||||
关于本站
|
||||
</h1>
|
||||
<p className="meta mt-2">
|
||||
{error
|
||||
? "加载失败"
|
||||
: pages.length > 0
|
||||
? `共 ${pages.length} 篇站点文档`
|
||||
: "站点说明与政策文档"}
|
||||
</p>
|
||||
</div>
|
||||
{canManagePages ? (
|
||||
<Link
|
||||
href="/admin/pages"
|
||||
className="meta text-[13px] shrink-0 mb-0.5 transition-colors hover:text-[var(--accent)]"
|
||||
>
|
||||
管理
|
||||
</Link>
|
||||
) : null}
|
||||
</header>
|
||||
|
||||
{error ? (
|
||||
<p className="alert-error">{error}</p>
|
||||
) : pages.length === 0 ? (
|
||||
<div className="panel p-10 text-center">
|
||||
<p className="font-medium" style={{ color: "var(--ink)" }}>
|
||||
暂无介绍
|
||||
</p>
|
||||
<p className="meta mt-1.5">有新文档时会显示在这里</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="panel overflow-hidden">
|
||||
{pages.map((p, i) => (
|
||||
<PageRow key={p.id} p={p} showDivider={i > 0} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</SiteDocShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user