feat: 可配置站点 URL 风格
- 新增 urlStyle 工具与测试:postPath/userPath/sitePagePath 统一生成链接 - 管理端新增 UrlStylePanel 设置面板,settings 服务与 handler 适配 - 全站路由链接与 JSON-LD/sitemap 切换为统一 URL 生成 - middleware 与相关组件适配
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
type SitePageInput,
|
||||
} from "@/lib/api";
|
||||
import { sitePagePublicPath, sitePagePublicURL } from "@/lib/sitePagePath";
|
||||
import { useUrlStyle } from "@/components/SiteBrandProvider";
|
||||
import { toast } from "@/lib/toast";
|
||||
import MarkdownEditor from "@/components/MarkdownEditor";
|
||||
import { AdminField, AdminSegmented, AdminStatusChip, AdminSwitch } from "@/components/admin";
|
||||
@@ -129,6 +130,7 @@ export default function PageComposeClient({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const urlStyle = useUrlStyle();
|
||||
const status = readStatus(searchParams.get("status"));
|
||||
const backHref = listHref(status);
|
||||
const loaded = useMemo(() => toForm(initial), [initial]);
|
||||
@@ -160,7 +162,7 @@ export default function PageComposeClient({
|
||||
};
|
||||
|
||||
const slugPreview = normalizeSlug(form.slug);
|
||||
const publicPath = sitePagePublicPath(slugPreview);
|
||||
const publicPath = sitePagePublicPath(slugPreview, urlStyle);
|
||||
const canViewOnline = savedPage?.published === true;
|
||||
|
||||
async function commit() {
|
||||
@@ -252,7 +254,7 @@ export default function PageComposeClient({
|
||||
setErrors((prev) => ({ ...prev, slug: nextErrors.slug }));
|
||||
return;
|
||||
}
|
||||
const url = sitePagePublicURL(origin || window.location.origin, slug);
|
||||
const url = sitePagePublicURL(origin || window.location.origin, slug, urlStyle);
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
toast("已复制访问地址", "ok");
|
||||
@@ -280,7 +282,7 @@ export default function PageComposeClient({
|
||||
}
|
||||
if (pendingSave?.slugChange) {
|
||||
confirmBits.push(
|
||||
`访问地址将从 ${sitePagePublicPath(baseline.slug)} 改为 ${sitePagePublicPath(normalizeSlug(form.slug))}。系统不会把旧地址转到新地址,已经分享的旧链接会打不开。`
|
||||
`访问地址将从 ${sitePagePublicPath(baseline.slug, urlStyle)} 改为 ${sitePagePublicPath(normalizeSlug(form.slug), urlStyle)}。系统不会把旧地址转到新地址,已经分享的旧链接会打不开。`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -304,7 +306,7 @@ export default function PageComposeClient({
|
||||
<div className="admin-doc-sticky-actions">
|
||||
{canViewOnline && savedPage ? (
|
||||
<a
|
||||
href={sitePagePublicPath(savedPage.slug)}
|
||||
href={sitePagePublicPath(savedPage.slug, urlStyle)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn btn-line btn-sm"
|
||||
@@ -413,7 +415,7 @@ export default function PageComposeClient({
|
||||
</div>
|
||||
<p className="admin-doc-hint" id="page-slug-hint">
|
||||
完整路径 {publicPath || "/p/"}。小写字母、数字和连字符,最多 64 字符。
|
||||
{origin && slugPreview ? ` 线上地址 ${sitePagePublicURL(origin, slugPreview)}` : ""}
|
||||
{origin && slugPreview ? ` 线上地址 ${sitePagePublicURL(origin, slugPreview, urlStyle)}` : ""}
|
||||
</p>
|
||||
{isEdit && baseline.published && slugPreview !== baseline.slug ? (
|
||||
<p className="admin-doc-hint">保存后访问地址会改变。旧链接不会自动跳转。</p>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Ellipsis, ExternalLink, FileText, Plus } from "lucide-react";
|
||||
import { apiAdminDeleteSitePage, type SitePage } from "@/lib/api";
|
||||
import { formatDateTime, formatRelative } from "@/lib/format";
|
||||
import { sitePagePublicPath } from "@/lib/sitePagePath";
|
||||
import { useUrlStyle } from "@/components/SiteBrandProvider";
|
||||
import { toast } from "@/lib/toast";
|
||||
import ConfirmDialog from "@/components/ConfirmDialog";
|
||||
import {
|
||||
@@ -50,6 +51,7 @@ export default function PagesAdmin({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const urlStyle = useUrlStyle();
|
||||
const statusFilter = readStatus(searchParams.get("status"));
|
||||
const [list, setList] = useState(initial);
|
||||
const [loadError] = useState(initError);
|
||||
@@ -166,7 +168,7 @@ export default function PagesAdmin({
|
||||
<tbody>
|
||||
{filtered.map((p) => {
|
||||
const updated = p.updated_at || "";
|
||||
const path = sitePagePublicPath(p.slug);
|
||||
const path = sitePagePublicPath(p.slug, urlStyle);
|
||||
return (
|
||||
<tr key={p.id}>
|
||||
<td data-label="页面">
|
||||
|
||||
Reference in New Issue
Block a user