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

@@ -24,10 +24,13 @@ import LevelBadge from "@/components/LevelBadge";
import BadgeChip from "@/components/BadgeChip";
import { PulseCheckin } from "./CheckinCard";
import { formatCompact } from "@/lib/format";
import { getPublicSettingsCached } from "@/lib/serverData";
import { postPath, userPath } from "@/lib/urlStyle";
/* ---------- 1. 板块管理员 ---------- */
export function Moderators({ items }: { items: BoardModerator[] }) {
export async function Moderators({ items }: { items: BoardModerator[] }) {
const urlStyle = (await getPublicSettingsCached()).url_style;
return (
<section className="j13-module" aria-label="板块管理员">
<p className="j13-module-title mb-2">
@@ -41,7 +44,7 @@ export function Moderators({ items }: { items: BoardModerator[] }) {
const name = u.nickname || u.username;
return (
<li key={u.id}>
<Link href={`/u/${u.id}`} className="j13-raillink" data-tip={name}>
<Link href={userPath(urlStyle, u.id)} className="j13-raillink" data-tip={name}>
<Avatar name={name} src={u.avatar || undefined} size={30} />
<span className="min-w-0 truncate text-[13px] font-medium" style={{ color: "var(--ink)" }}>
{name}
@@ -163,7 +166,8 @@ export function BoardPulse({
/* ---------- 4. 本板热门 ---------- */
export function BoardHot({ posts, allowComments }: { posts: PostListItem[]; allowComments: boolean }) {
export async function BoardHot({ posts, allowComments }: { posts: PostListItem[]; allowComments: boolean }) {
const urlStyle = (await getPublicSettingsCached()).url_style;
return (
<section className="j13-module" aria-label="本板热门">
<p className="j13-module-title mb-1.5">
@@ -175,7 +179,7 @@ export function BoardHot({ posts, allowComments }: { posts: PostListItem[]; allo
<ol className="j13-ranklist">
{posts.map((p, i) => (
<li key={p.id}>
<Link href={`/post/${p.id}`} className="j13-rank" data-tip={p.title}>
<Link href={postPath(urlStyle, p.id)} className="j13-rank" data-tip={p.title}>
<span className="j13-rank-no">{i + 1}</span>
<span className="min-w-0 flex-1">
<span className="j13-rank-title truncate">
@@ -203,7 +207,8 @@ export function BoardHot({ posts, allowComments }: { posts: PostListItem[]; allo
/* ---------- 5. 本板活跃 ---------- */
export function BoardActive({ users, allowComments }: { users: ActiveUser[]; allowComments: boolean }) {
export async function BoardActive({ users, allowComments }: { users: ActiveUser[]; allowComments: boolean }) {
const urlStyle = (await getPublicSettingsCached()).url_style;
return (
<section className="j13-module" aria-label="本板活跃成员">
<p className="j13-module-title mb-2">
@@ -223,7 +228,7 @@ export function BoardActive({ users, allowComments }: { users: ActiveUser[]; all
const name = u.nickname || u.username;
return (
<li key={u.id}>
<Link href={`/u/${u.id}`} className="j13-raillink" data-tip={name}>
<Link href={userPath(urlStyle, u.id)} className="j13-raillink" data-tip={name}>
<Avatar name={name} src={u.avatar || undefined} size={30} />
<span className="flex-1 min-w-0">
<span