Files
jiang13-bbs/frontend/app/admin/settings/SettingsAdmin.tsx
freefire d93635c961 feat: 全站评论开关,并统一后台表单控件
关闭评论后前台隐藏评论能力;外观/设置/公告改用 AdminField 与 Stepper。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 01:22:00 +08:00

165 lines
5.2 KiB
TypeScript

"use client";
import { Globe, Loader2, MessageSquare, Save, UserPlus } from "lucide-react";
import { useState } from "react";
import {
AdminField,
AdminPageHeader,
AdminSection,
AdminSettingRow,
AdminSwitch,
} from "@/components/admin";
import { apiUpdateSiteSettings, type PublicSettings } from "@/lib/api";
import { toast } from "@/lib/toast";
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 [allowComments, setAllowComments] = useState(initial.allow_comments !== false);
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 [togglingComments, setTogglingComments] = useState(false);
const dirty = siteName.trim() !== savedName || siteDescription.trim() !== savedDesc;
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 toggleComments = async () => {
if (togglingComments) return;
const next = !allowComments;
setTogglingComments(true);
try {
const res = await apiUpdateSiteSettings({ allow_comments: next });
setAllowComments(res.allow_comments);
toast(res.allow_comments ? "已开放评论" : "已关闭评论", "ok");
} catch (e) {
toast(e instanceof Error ? e.message : "保存失败");
} finally {
setTogglingComments(false);
}
};
return (
<div className="max-w-3xl">
<AdminPageHeader
icon={Globe}
title="站点设置"
description="配置站点名称、简介与注册/评论开关;主题色、代码折叠与动画请到「外观」页调整"
/>
<div className="flex flex-col gap-4">
<AdminSection title="基本信息" description="用于页眉品牌、页脚版权与 SEO 元信息">
<AdminField
id="site-name"
className="mb-4"
label="站点名称"
maxLength={32}
value={siteName}
onChange={setSiteName}
placeholder="姜十三论坛"
/>
<AdminField
id="site-desc"
className="mb-4"
label="站点简介"
as="textarea"
minHeight="96px"
maxLength={160}
value={siteDescription}
onChange={setSiteDescription}
placeholder="一句话介绍社区定位…"
/>
<div className="flex justify-end">
<button
type="button"
className="btn btn-primary btn-sm"
disabled={!dirty || saving}
onClick={() => void saveIdentity()}
>
{saving ? <Loader2 size={14} className="animate-spin" /> : <Save size={14} />}
保存基本信息
</button>
</div>
</AdminSection>
<AdminSettingRow
title={
<>
<UserPlus size={15} /> 开放注册
</>
}
description="关闭后,游客将无法提交注册;已有账号仍可登录。适用于内测或维护期。"
control={
<AdminSwitch
checked={allowRegister}
disabled={togglingReg}
onChange={() => void toggleRegister()}
label="开放注册"
/>
}
/>
<AdminSettingRow
title={
<>
<MessageSquare size={15} /> 开放评论
</>
}
description="关闭后前台不展示评论入口、评论数量与已有评论,并提示本站不支持评论。适用于备案审核等合规需要。"
control={
<AdminSwitch
checked={allowComments}
disabled={togglingComments}
onChange={() => void toggleComments()}
label="开放评论"
/>
}
/>
</div>
</div>
);
}