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

@@ -36,6 +36,8 @@ import {
import { isOwner, ROLES } from "@/lib/roles";
import { formatDate, formatRelative } from "@/lib/format";
import { realtime } from "@/lib/realtime";
import { userPath } from "@/lib/urlStyle";
import { useUrlStyle } from "@/components/SiteBrandProvider";
import Avatar from "@/components/Avatar";
import RoleBadge from "@/components/RoleBadge";
import Modal from "@/components/Modal";
@@ -302,6 +304,7 @@ export default function UsersAdmin({
currentUserId: number;
currentUserRole: string;
}) {
const urlStyle = useUrlStyle();
const [users, setUsers] = useState<AdminUser[]>(initial?.users ?? []);
const [summary, setSummary] = useState<AdminUserSummary>(
initial?.summary ?? {
@@ -686,7 +689,7 @@ export default function UsersAdmin({
return (
<div className="flex items-center gap-1.5 flex-wrap">
{auditBtn}
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<Link href={userPath(urlStyle, u.id)} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<ExternalLink size={13} />
主页
</Link>
@@ -697,7 +700,7 @@ export default function UsersAdmin({
return (
<div className="flex items-center gap-1.5 flex-wrap">
{auditBtn}
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<Link href={userPath(urlStyle, u.id)} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<ExternalLink size={13} />
主页
</Link>
@@ -718,7 +721,7 @@ export default function UsersAdmin({
return (
<div className="flex items-center gap-1.5 flex-wrap">
{auditBtn}
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<Link href={userPath(urlStyle, u.id)} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<ExternalLink size={13} />
主页
</Link>

View File

@@ -14,7 +14,9 @@ import {
} from "@/lib/api";
import { formatDate, formatRelative } from "@/lib/format";
import Avatar from "@/components/Avatar";
import { useUrlStyle } from "@/components/SiteBrandProvider";
import RoleBadge from "@/components/RoleBadge";
import { postPath, userPath } from "@/lib/urlStyle";
import {
AdminEmpty,
AdminPager,
@@ -72,6 +74,7 @@ export default function UserAuditClient({
initialPostsTotal: number;
initError: string;
}) {
const urlStyle = useUrlStyle();
const [profile] = useState(initial);
const [tab, setTab] = useState<TabKey>("posts");
const [loading, setLoading] = useState(false);
@@ -231,7 +234,7 @@ export default function UserAuditClient({
</p>
</div>
<Link
href={`/u/${u.id}`}
href={userPath(urlStyle, u.id)}
className="btn btn-line btn-sm gap-1.5 shrink-0"
target="_blank"
>
@@ -318,7 +321,7 @@ export default function UserAuditClient({
</p>
) : (
<Link
href={`/post/${p.id}`}
href={postPath(urlStyle, p.id)}
className="text-[14px] font-medium hover:underline"
style={{ color: "var(--ink)" }}
>
@@ -353,7 +356,7 @@ export default function UserAuditClient({
<span className="text-[11.5px] meta">帖子 #{c.post_id}</span>
) : (
<Link
href={`/post/${c.post_id}#comment-${c.id}`}
href={`${postPath(urlStyle, c.post_id)}#comment-${c.id}`}
className="text-[11.5px] hover:underline"
style={{ color: "var(--accent)" }}
>