From 0b60c4299b56895239f8bf0f240f12b4dac16770 Mon Sep 17 00:00:00 2001 From: freefire Date: Thu, 17 Sep 2026 21:15:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=AC=E6=96=87=E4=B8=89=E6=A0=8F?= =?UTF-8?q?=E5=8D=95=E9=A1=B5=E5=B8=83=E5=B1=80=EF=BC=8C=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=97=B6=E9=97=B4=E7=BA=BF=E7=9B=AE=E5=BD=95=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E4=B8=8E=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 公告/单页对齐帖子详情三栏壳;时间线按日锚点可跳转,条目改为日志行并用暖金日期区分 feat 色。 Co-authored-by: Cursor --- frontend/app/announcement/[id]/page.tsx | 151 +++++---- frontend/app/announcements/page.tsx | 95 +++++- frontend/app/globals.css | 302 +++++++++++++++--- frontend/app/p/[slug]/page.tsx | 134 +++++--- frontend/app/page.tsx | 5 +- frontend/app/post/[id]/page.tsx | 33 +- frontend/components/MarkdownBody.tsx | 5 +- frontend/components/MarkdownBodyClient.tsx | 6 +- frontend/components/PostToc.tsx | 5 +- frontend/components/home/CommunityPanels.tsx | 69 ++-- frontend/components/markdown/MdTimeline.tsx | 59 +++- .../components/site-doc/SiteDocAnnoNav.tsx | 82 +++++ .../components/site-doc/SiteDocBreadcrumb.tsx | 32 ++ .../components/site-doc/SiteDocIdentity.tsx | 61 ++++ .../components/site-doc/SiteDocMasthead.tsx | 94 ++++++ .../components/site-doc/SiteDocPageNav.tsx | 57 ++++ frontend/components/site-doc/SiteDocShell.tsx | 42 +++ frontend/components/site-doc/index.ts | 8 + frontend/lib/pageGlyph.ts | 20 ++ frontend/lib/toc.ts | 102 ++++++ 20 files changed, 1140 insertions(+), 222 deletions(-) create mode 100644 frontend/components/site-doc/SiteDocAnnoNav.tsx create mode 100644 frontend/components/site-doc/SiteDocBreadcrumb.tsx create mode 100644 frontend/components/site-doc/SiteDocIdentity.tsx create mode 100644 frontend/components/site-doc/SiteDocMasthead.tsx create mode 100644 frontend/components/site-doc/SiteDocPageNav.tsx create mode 100644 frontend/components/site-doc/SiteDocShell.tsx create mode 100644 frontend/components/site-doc/index.ts create mode 100644 frontend/lib/pageGlyph.ts create mode 100644 frontend/lib/toc.ts diff --git a/frontend/app/announcement/[id]/page.tsx b/frontend/app/announcement/[id]/page.tsx index f0380f6..5af0375 100644 --- a/frontend/app/announcement/[id]/page.tsx +++ b/frontend/app/announcement/[id]/page.tsx @@ -1,13 +1,21 @@ import type { Metadata } from "next"; -import Link from "next/link"; import { cookies } from "next/headers"; import { notFound } from "next/navigation"; -import { ChevronRight, Megaphone } from "lucide-react"; -import { fetchAnnouncementDetail } from "@/lib/api"; +import { Megaphone } from "lucide-react"; +import { fetchAnnouncementDetail, fetchOverview } from "@/lib/api"; import { authCookieHeader } from "@/lib/cookies"; -import { formatDate } from "@/lib/format"; -import { getMeCached } from "@/lib/serverData"; +import { getMeCached, getPublicSettingsCached } from "@/lib/serverData"; +import { extractToc, estimateReadingMinutes, markdownHeadingToc } from "@/lib/toc"; import MarkdownBody from "@/components/MarkdownBody"; +import PostToc from "@/components/PostToc"; +import { + SiteDocShell, + SiteDocIdentity, + SiteDocPageNav, + SiteDocAnnoNav, + SiteDocMasthead, + SiteDocBreadcrumb, +} from "@/components/site-doc"; interface PageProps { params: Promise<{ id: string }>; @@ -32,14 +40,35 @@ export async function generateMetadata({ params }: PageProps): Promise export default async function AnnouncementPage({ params }: PageProps) { const { id } = await params; const cookie = authCookieHeader(await cookies()); - const [data, me] = await Promise.all([ + const [data, me, overview, settings] = await Promise.all([ fetchAnnouncementDetail(id, cookie || undefined).catch(() => null), getMeCached(cookie || undefined), + fetchOverview(cookie || undefined).catch(() => null), + getPublicSettingsCached(), ]); if (!data) notFound(); const a = data.announcement; + const annId = Number(id); const loginHref = `/login?redirect=${encodeURIComponent(`/announcement/${id}`)}`; + const sidebarPages = overview?.sidebar_pages ?? []; + const announcements = overview?.announcements ?? []; + const siteLead = settings.site_description?.trim() || undefined; + const toc = extractToc(a.content); + const headingToc = markdownHeadingToc(toc); + const readingMinutes = estimateReadingMinutes(a.content, a.title.length); + + let headingIndex = 0; + const headingRenderer = (Tag: "h2" | "h3") => + function Heading({ children }: { children?: React.ReactNode }) { + const item = headingToc[headingIndex++]; + return ( + + {children} + + ); + }; + const jsonLd = { "@context": "https://schema.org", "@type": "Article", @@ -50,69 +79,79 @@ export default async function AnnouncementPage({ params }: PageProps) { keywords: a.tag, }; + const left = ( + <> + + + + ); + + const right = ( + <> + {toc.length > 0 ? : null} + + + ); + return ( -
+ <>