统一 React 管理后台,修复评论换行与帖子置顶

- /admin/* 全部由 React SPA 渲染,替代旧版 HTML 后台页面
- 新增仪表盘、帖子/评论/用户管理、系统设置与 JSON API
- 帖子详情页支持管理员置顶;评论换行显示修复

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
freefire
2026-06-15 23:06:44 +08:00
parent 9230f7272d
commit d0555de28e
63 changed files with 1289 additions and 284 deletions

View File

@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { ArrowLeft, Plus } from 'lucide-react';
import { Plus } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
@@ -25,7 +25,7 @@ import {
} from '@/components/ui/form';
import { notify } from '@/lib/notify';
import { api } from '../api/client';
import { useAuth } from '../hooks/useAuth';
import { useAdminGuard } from '../layouts/AdminLayout';
import type { Board } from '../api/types';
const boardSchema = z.object({
@@ -38,7 +38,7 @@ type BoardFormValues = z.infer<typeof boardSchema>;
export default function BoardsManagePage() {
const nav = useNavigate();
const { user, loading: authLoading } = useAuth();
const { ready } = useAdminGuard();
const [boards, setBoards] = useState<Board[]>([]);
const [loading, setLoading] = useState(true);
const [modalOpen, setModalOpen] = useState(false);
@@ -59,11 +59,8 @@ export default function BoardsManagePage() {
};
useEffect(() => {
if (authLoading) return;
if (!user) { nav('/login'); return; }
if (user.role !== 'admin') { nav('/'); notify.warning('需要管理员权限'); return; }
load();
}, [user, authLoading, nav]);
if (ready) load();
}, [ready]);
const openCreate = () => {
setEditing(null);
@@ -112,31 +109,26 @@ export default function BoardsManagePage() {
}
};
if (authLoading) {
if (!ready) {
return <div className="flex justify-center py-16"><Spinner size="lg" /></div>;
}
if (!user || user.role !== 'admin') return null;
return (
<div className="page-wrap">
<div className="page-inner-wide">
<Button variant="ghost" className="mb-3" onClick={() => nav('/')}>
<ArrowLeft />
</Button>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 20 }}>
<div className="admin-page">
<div className="admin-page-head">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<h1 className="page-title"></h1>
<p className="page-desc"></p>
<h1></h1>
<p></p>
</div>
<Button onClick={openCreate}>
<Plus />
</Button>
</div>
</div>
<div className="section-card" style={{ padding: 0, overflow: 'hidden' }}>
<div className="admin-card">
{loading ? (
<div className="flex justify-center py-12"><Spinner size="lg" /></div>
) : (
@@ -197,7 +189,6 @@ export default function BoardsManagePage() {
)}
</>
)}
</div>
</div>
<Dialog open={modalOpen} onOpenChange={setModalOpen}>