"use client"; import { useState } from "react"; import { Code2, Globe, Loader2, Save, UserPlus, } from "lucide-react"; import { apiUpdateSiteSettings, type PublicSettings, } from "@/lib/api"; import { toast } from "@/lib/toast"; function Switch({ checked, disabled, onChange, label, }: { checked: boolean; disabled?: boolean; onChange: () => void; label: string; }) { return ( ); } export default function SettingsAdmin({ initial }: { initial: PublicSettings }) { const [siteName, setSiteName] = useState(initial.site_name); const [siteDescription, setSiteDescription] = useState(initial.site_description); const [allowRegister, setAllowRegister] = useState(initial.allow_register); const [codeAutoFold, setCodeAutoFold] = useState( initial.code_block_auto_fold !== false ); const [foldLines, setFoldLines] = useState( Number.isFinite(initial.code_block_fold_lines) ? initial.code_block_fold_lines : 5 ); const [savedFoldLines, setSavedFoldLines] = useState( Number.isFinite(initial.code_block_fold_lines) ? initial.code_block_fold_lines : 5 ); const [savedName, setSavedName] = useState(initial.site_name); const [savedDesc, setSavedDesc] = useState(initial.site_description); const [saving, setSaving] = useState(false); const [togglingReg, setTogglingReg] = useState(false); const [togglingFold, setTogglingFold] = useState(false); const [savingFoldLines, setSavingFoldLines] = useState(false); const dirty = siteName.trim() !== savedName || siteDescription.trim() !== savedDesc; const foldLinesDirty = foldLines !== savedFoldLines; const saveIdentity = async () => { if (saving) return; const name = siteName.trim(); if (!name) { toast("站点名称不能为空"); return; } setSaving(true); try { const res = await apiUpdateSiteSettings({ site_name: name, site_description: siteDescription.trim(), }); setSiteName(res.site_name); setSiteDescription(res.site_description); setSavedName(res.site_name); setSavedDesc(res.site_description); toast("站点信息已保存", "ok"); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setSaving(false); } }; const toggleRegister = async () => { if (togglingReg) return; const next = !allowRegister; setTogglingReg(true); try { const res = await apiUpdateSiteSettings({ allow_register: next }); setAllowRegister(res.allow_register); toast(res.allow_register ? "已开放注册" : "已关闭注册", "ok"); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setTogglingReg(false); } }; const toggleCodeFold = async () => { if (togglingFold) return; const next = !codeAutoFold; setTogglingFold(true); try { const res = await apiUpdateSiteSettings({ code_block_auto_fold: next }); setCodeAutoFold(res.code_block_auto_fold); toast( res.code_block_auto_fold ? `已开启代码块自动折叠(超过 ${res.code_block_fold_lines} 行)` : "已关闭代码块自动折叠", "ok" ); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setTogglingFold(false); } }; const saveFoldLines = async () => { if (savingFoldLines) return; const raw = Math.round(Number(foldLines)); const n = Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 5; setSavingFoldLines(true); try { const res = await apiUpdateSiteSettings({ code_block_fold_lines: n }); setFoldLines(res.code_block_fold_lines); setSavedFoldLines(res.code_block_fold_lines); toast( res.code_block_fold_lines === 0 ? "折叠预览已设为 0 行(无预览)" : `折叠阈值已设为超过 ${res.code_block_fold_lines} 行`, "ok" ); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setSavingFoldLines(false); } }; return (
配置站点名称、简介与注册开关;主题色请到「外观」页调整
用于页眉品牌、页脚版权与 SEO 元信息
setSiteName(e.target.value)} placeholder="姜十三论坛" />关闭后,游客将无法提交注册;已有账号仍可登录。适用于内测或维护期。
开启后,帖子与评论中超过阈值行数的 Markdown 代码块默认折叠,读者可点「展开全部」。关闭则始终完整展示。
代码块行数超过该值时折叠;折叠后约露出同样行数。设为 0 表示无预览。范围 0–100,默认 5。