"use client"; import { useRef, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { ArrowLeft, Loader2, Save } from "lucide-react"; import { apiAdminCreateSitePage, apiAdminUpdateSitePage, type SitePage, type SitePageInput, } from "@/lib/api"; import { toast } from "@/lib/toast"; import MarkdownEditor from "@/components/MarkdownEditor"; import { AdminSegmented, AdminSwitch } from "@/components/admin"; type PublishKey = "published" | "draft"; type FormState = { slug: string; title: string; content: string; excerpt: string; published: boolean; show_in_sidebar: boolean; sort_order: number; }; function toForm(p?: SitePage | null): FormState { return { slug: p?.slug ?? "", title: p?.title ?? "", content: p?.content ?? "", excerpt: p?.excerpt ?? "", published: p?.published ?? true, show_in_sidebar: p?.show_in_sidebar ?? false, sort_order: p?.sort_order ?? 0, }; } /** slug 仅允许小写字母数字与连字符 */ function normalizeSlug(raw: string): string { return raw .trim() .toLowerCase() .replace(/[^a-z0-9-]+/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); } export default function PageComposeClient({ mode, initial, }: { mode: "create" | "edit"; initial?: SitePage | null; }) { const router = useRouter(); const [form, setForm] = useState(() => toForm(initial)); const formRef = useRef(form); const [saving, setSaving] = useState(false); const isEdit = mode === "edit" && initial != null; const patchForm = (patch: Partial | ((prev: FormState) => FormState)) => { setForm((prev) => { const next = typeof patch === "function" ? patch(prev) : { ...prev, ...patch }; formRef.current = next; return next; }); }; async function save() { if (saving) return; const current = formRef.current; const slug = normalizeSlug(current.slug); const payload: SitePageInput = { slug, title: current.title.trim(), content: current.content.trim(), excerpt: current.excerpt.trim(), published: current.published, show_in_sidebar: current.show_in_sidebar, sort_order: current.sort_order, }; if (!payload.slug || !payload.title || !payload.content) { toast("slug、标题和内容不能为空"); return; } setSaving(true); try { if (isEdit) { await apiAdminUpdateSitePage(initial.id, payload); toast("页面已更新", "ok"); } else { await apiAdminCreateSitePage(payload); toast(payload.published ? "页面已发布" : "草稿已保存", "ok"); } router.push("/admin/pages"); router.refresh(); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setSaving(false); } } return (

{isEdit ? "编辑单页" : "新建单页"}

返回列表

页面设置

/p/ patchForm({ slug: e.target.value })} onBlur={() => patchForm((f) => ({ ...f, slug: normalizeSlug(f.slug) })) } placeholder="about" />