feat: 可配置站点 URL 风格

- 新增 urlStyle 工具与测试:postPath/userPath/sitePagePath 统一生成链接
- 管理端新增 UrlStylePanel 设置面板,settings 服务与 handler 适配
- 全站路由链接与 JSON-LD/sitemap 切换为统一 URL 生成
- middleware 与相关组件适配
This commit is contained in:
2026-09-27 04:01:42 +08:00
parent 1d862a0dd3
commit 7a44c1f2cf
64 changed files with 1183 additions and 200 deletions

View File

@@ -15,6 +15,7 @@ import {
} from "@/components/site-doc";
import { requestCanonical } from "@/lib/canonicalMeta";
import { canonicalPage } from "@/lib/siteUrl";
import { announcementPath, type UrlStyle } from "@/lib/urlStyle";
interface PageProps {
searchParams: Promise<{ page?: string }>;
@@ -42,11 +43,11 @@ function plainSummary(md: string, max = 120): string {
return text.length > max ? `${text.slice(0, max)}…` : text;
}
function FeaturedAnnouncement({ a }: { a: Announcement }) {
function FeaturedAnnouncement({ a, urlStyle }: { a: Announcement; urlStyle: UrlStyle }) {
const summary = plainSummary(a.content, 180);
return (
<Link
href={`/announcement/${a.id}`}
href={announcementPath(urlStyle, a.id)}
className="block px-5 sm:px-6 pt-5 sm:pt-6 pb-5 transition-colors hover:bg-[var(--panel-2)] focus-visible:bg-[var(--panel-2)]"
>
<div className="flex items-center gap-2 flex-wrap mb-3">
@@ -77,11 +78,11 @@ function FeaturedAnnouncement({ a }: { a: Announcement }) {
);
}
function AnnouncementRow({ a, showDivider }: { a: Announcement; showDivider: boolean }) {
function AnnouncementRow({ a, showDivider, urlStyle }: { a: Announcement; showDivider: boolean; urlStyle: UrlStyle }) {
const summary = plainSummary(a.content);
return (
<Link
href={`/announcement/${a.id}`}
href={announcementPath(urlStyle, a.id)}
className="block px-5 sm:px-6 py-4 sm:py-5 transition-colors hover:bg-[var(--panel-2)] focus-visible:bg-[var(--panel-2)]"
style={showDivider ? { borderTop: "1px solid var(--line)" } : undefined}
>
@@ -131,6 +132,7 @@ export default async function AnnouncementsListPage({ searchParams }: PageProps)
]);
const sidebarPages = overview?.sidebar_pages ?? [];
const siteLead = settings.site_description?.trim() || undefined;
const urlStyle = settings.url_style;
const canManagePages = !!me.user && isAdminOrAbove(me.user.role);
// 首页置顶条目作头图;其余走列表行(置顶已在头图时不再重复)
@@ -178,9 +180,9 @@ export default async function AnnouncementsListPage({ searchParams }: PageProps)
</div>
) : (
<>
{featured ? <FeaturedAnnouncement a={featured} /> : null}
{featured ? <FeaturedAnnouncement a={featured} urlStyle={urlStyle} /> : null}
{rest.map((a, i) => (
<AnnouncementRow key={a.id} a={a} showDivider={!!featured || i > 0} />
<AnnouncementRow key={a.id} a={a} showDivider={!!featured || i > 0} urlStyle={urlStyle} />
))}
</>
)}