feat: 管理端板块管理与旧版数据导入,补充部署运营文档

- 新增管理端板块管理页面与后端接口(admin_board)
- 新增旧版数据导入:legacyimport 服务、导入面板、importusers 命令行工具
- 聊天用户卡片、板块图标等 UI 组件与界面优化
- 补充 about/公告/1Panel 部署等文档
- gitignore 排除 dist/ 构建产物与 .agents/ 本地工具目录
This commit is contained in:
2026-09-24 03:37:44 +08:00
parent c1f6a6636b
commit 3e55b5d230
53 changed files with 5078 additions and 409 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { ExternalLink, LogIn, MessageSquare, MessagesSquare, ShieldCheck, Timer } from "lucide-react";
import { ExternalLink, LogIn, MessageSquare, MessagesSquare, ShieldCheck, ShieldOff, Timer } from "lucide-react";
import { useState } from "react";
import {
AdminSettingsGroup,
@@ -27,6 +27,7 @@ export default function AccessSettings({ initial }: { initial: PublicSettings })
const [allowMessages, setAllowMessages] = useState(initial.allow_messages !== false);
const [postLinkNewTab, setPostLinkNewTab] = useState(initial.post_link_new_tab !== false);
const [trustOn, setTrustOn] = useState(initial.trust_reviewed_publish !== false);
const [withoutReviewOn, setWithoutReviewOn] = useState(initial.publish_without_review === true);
const [cooldown, setCooldown] = useState(initial.post_cooldown_hours);
const [savedCooldown, setSavedCooldown] = useState(initial.post_cooldown_hours);
const [togglingComments, setTogglingComments] = useState(false);
@@ -34,6 +35,7 @@ export default function AccessSettings({ initial }: { initial: PublicSettings })
const [togglingMessages, setTogglingMessages] = useState(false);
const [togglingLink, setTogglingLink] = useState(false);
const [togglingTrust, setTogglingTrust] = useState(false);
const [togglingWithoutReview, setTogglingWithoutReview] = useState(false);
const [savingCooldown, setSavingCooldown] = useState(false);
const [moduleDirty, setModuleDirty] = useState(false);
const leave = useUnsavedGuard(moduleDirty);
@@ -103,6 +105,25 @@ export default function AccessSettings({ initial }: { initial: PublicSettings })
}
};
const toggleWithoutReview = async () => {
if (togglingWithoutReview) return;
setTogglingWithoutReview(true);
try {
const res = await apiUpdateSiteSettings({ publish_without_review: !withoutReviewOn });
setWithoutReviewOn(res.publish_without_review === true);
toast(
res.publish_without_review
? "已开启全站免审(发帖与评论直接公开,无需过审记录)"
: "已关闭全站免审(将按过审免审规则决定是否待审)",
"ok",
);
} catch (e) {
toast(e instanceof Error ? e.message : "保存失败");
} finally {
setTogglingWithoutReview(false);
}
};
const toggleTrust = async () => {
if (togglingTrust) return;
setTogglingTrust(true);
@@ -218,18 +239,40 @@ export default function AccessSettings({ initial }: { initial: PublicSettings })
<InstantNote saving={togglingLink} />
</div>
</AdminSettingsRow>
<AdminSettingsRow
label={
<span className="inline-flex items-center gap-1.5">
<ShieldOff size={15} /> 全站免审
</span>
}
hintBelow="所有人发帖与评论直接公开,无需有过审记录;开启后优先生效。"
>
<div className="flex items-center gap-2">
<AdminSwitch
checked={withoutReviewOn}
disabled={togglingWithoutReview}
onChange={() => void toggleWithoutReview()}
label="全站免审"
/>
<InstantNote saving={togglingWithoutReview} />
</div>
</AdminSettingsRow>
<AdminSettingsRow
label={
<span className="inline-flex items-center gap-1.5">
<ShieldCheck size={15} /> 过审免审
</span>
}
hintBelow="用户曾有过审内容后,后续发帖与评论直接公开。"
hintBelow={
withoutReviewOn
? "全站免审已开启,所有内容直接公开,本开关暂不参与判定。"
: "用户曾有过审内容后,后续发帖与评论直接公开。"
}
>
<div className="flex items-center gap-2">
<AdminSwitch
checked={trustOn}
disabled={togglingTrust}
disabled={withoutReviewOn || togglingTrust}
onChange={() => void toggleTrust()}
label="过审免审"
/>