升级帖子编辑与 Feed 体验:TipTap 富文本、修订历史、排序与编辑时限。

将 Markdown 编辑器替换为 TipTap WYSIWYG,新增帖子修订记录与 diff 展示;首页支持最新/回复排序与本地缓存;后台可配置编辑时限与锁定帖子;侧边栏整合板块导航并优化 Feed 布局。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
freefire
2026-06-16 03:05:45 +08:00
parent d0555de28e
commit 1d273066b0
72 changed files with 3576 additions and 676 deletions

View File

@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { Database } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Spinner } from '@/components/ui/spinner';
import { notify } from '@/lib/notify';
import { api } from '../../api/client';
@@ -12,14 +13,37 @@ export default function AdminSettingsPage() {
const [settings, setSettings] = useState<AdminSettings | null>(null);
const [loading, setLoading] = useState(true);
const [backing, setBacking] = useState(false);
const [editWindowHours, setEditWindowHours] = useState('24');
const [savingForum, setSavingForum] = useState(false);
useEffect(() => {
if (!ready) return;
api.adminSettings()
.then(setSettings)
.then(s => {
setSettings(s);
setEditWindowHours(String(s.post_edit_window_hours ?? 24));
})
.finally(() => setLoading(false));
}, [ready]);
const handleSaveForumSettings = async () => {
const hours = parseInt(editWindowHours, 10);
if (Number.isNaN(hours) || hours < 0) {
notify.warning('请输入有效的小时数0 表示不限)');
return;
}
setSavingForum(true);
try {
const r = await api.adminUpdateForumSettings({ post_edit_window_hours: hours });
notify.success(r.message);
setSettings(s => s ? { ...s, post_edit_window_hours: r.post_edit_window_hours } : s);
} catch (e: unknown) {
notify.error(e instanceof Error ? e.message : '保存失败');
} finally {
setSavingForum(false);
}
};
const handleBackup = async () => {
setBacking(true);
try {
@@ -45,6 +69,27 @@ export default function AdminSettingsPage() {
<p></p>
</div>
<div className="admin-card">
<div className="admin-card-head"></div>
<p className="admin-card-desc">
0
</p>
<div className="admin-form-row">
<label htmlFor="edit-window-hours"></label>
<Input
id="edit-window-hours"
type="number"
min={0}
value={editWindowHours}
onChange={e => setEditWindowHours(e.target.value)}
className="max-w-[120px]"
/>
<Button onClick={handleSaveForumSettings} loading={savingForum}>
</Button>
</div>
</div>
<div className="admin-card">
<div className="admin-card-head"></div>
<dl className="admin-dl">