"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 { apiAdminCreateAnnouncement, apiAdminUpdateAnnouncement, type Announcement, type AnnouncementColor, type AnnouncementInput, } from "@/lib/api"; import { toast } from "@/lib/toast"; import MarkdownEditor from "@/components/MarkdownEditor"; import { AdminSegmented, AdminSwitch } from "@/components/admin"; import { ANNOUNCEMENT_COLORS } from "./announcementColors"; type PublishKey = "published" | "draft"; type FormState = { title: string; content: string; tag: string; tag_color: AnnouncementColor; published: boolean; pinned: boolean; }; function toForm(a?: Announcement | null): FormState { return { title: a?.title ?? "", content: a?.content ?? "", tag: a?.tag ?? "公告", tag_color: a?.tag_color ?? "blue", published: a?.published ?? true, pinned: a?.pinned ?? false, }; } export default function AnnouncementComposeClient({ mode, initial, }: { mode: "create" | "edit"; initial?: Announcement | 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 payload: AnnouncementInput = { title: current.title.trim(), content: current.content.trim(), tag: current.tag.trim() || "公告", tag_color: current.tag_color, published: current.published, pinned: current.pinned, }; if (!payload.title || !payload.content) { toast("标题和内容不能为空"); return; } setSaving(true); try { if (isEdit) { await apiAdminUpdateAnnouncement(initial.id, payload); toast("公告已更新", "ok"); } else { await apiAdminCreateAnnouncement(payload); toast(payload.published ? "公告已发布" : "草稿已保存", "ok"); } router.push("/admin/announcements"); router.refresh(); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setSaving(false); } } return (

{isEdit ? "编辑公告" : "新建公告"}

返回列表

发布设置

patchForm({ tag: e.target.value })} placeholder="公告" />

标签颜色

{ANNOUNCEMENT_COLORS.map((c) => (

发布状态

role="group" ariaLabel="发布状态" value={form.published ? "published" : "draft"} onChange={(key) => patchForm({ published: key === "published" })} options={[ { key: "published", label: "立即发布" }, { key: "draft", label: "存为草稿" }, ]} />

置顶公告

置顶后优先显示在首页右栏

patchForm((f) => ({ ...f, pinned: !f.pinned }))} label="置顶公告" />

标题与正文

patchForm({ title: e.target.value })} placeholder="请输入公告标题" aria-label="公告标题" />
patchForm({ content })} allowHide allowReplyHide={false} placeholder="请输入公告正文,支持 Markdown…" rows={18} onSubmitShortcut={() => void save()} />
取消
); }