import { useState, useEffect } from 'react'; 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 { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Badge } from '@/components/ui/badge'; import { Spinner } from '@/components/ui/spinner'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form'; import { notify } from '@/lib/notify'; import { api } from '../api/client'; import { useAuth } from '../hooks/useAuth'; import type { Board } from '../api/types'; const boardSchema = z.object({ name: z.string().min(1, '请输入名称').max(64), description: z.string().max(500).optional(), sort_order: z.coerce.number().min(0), }); type BoardFormValues = z.infer; export default function BoardsManagePage() { const nav = useNavigate(); const { user, loading: authLoading } = useAuth(); const [boards, setBoards] = useState([]); const [loading, setLoading] = useState(true); const [modalOpen, setModalOpen] = useState(false); const [editing, setEditing] = useState(null); const [submitting, setSubmitting] = useState(false); const form = useForm({ resolver: zodResolver(boardSchema), defaultValues: { name: '', description: '', sort_order: 1 }, }); const load = () => { setLoading(true); api.boards() .then(d => setBoards(d.boards ?? [])) .catch(e => notify.error(e.message)) .finally(() => setLoading(false)); }; useEffect(() => { if (authLoading) return; if (!user) { nav('/login'); return; } if (user.role !== 'admin') { nav('/'); notify.warning('需要管理员权限'); return; } load(); }, [user, authLoading, nav]); const openCreate = () => { setEditing(null); form.reset({ name: '', description: '', sort_order: boards.length + 1 }); setModalOpen(true); }; const openEdit = (board: Board) => { setEditing(board); form.reset({ name: board.name, description: board.description ?? '', sort_order: board.sort_order, }); setModalOpen(true); }; const handleSubmit = async (values: BoardFormValues) => { setSubmitting(true); try { if (editing) { await api.updateBoard(editing.id, values); notify.success('板块已更新'); } else { await api.createBoard(values); notify.success('板块已创建'); } setModalOpen(false); load(); window.dispatchEvent(new Event('boards-refresh')); } catch (e: unknown) { notify.error(e instanceof Error ? e.message : '操作失败'); } finally { setSubmitting(false); } }; const handleDelete = async (id: number) => { try { await api.deleteBoard(id); notify.success('板块已删除'); load(); window.dispatchEvent(new Event('boards-refresh')); } catch (e: unknown) { notify.error(e instanceof Error ? e.message : '删除失败'); } }; if (authLoading) { return
; } if (!user || user.role !== 'admin') return null; return (

板块管理

创建、编辑或删除论坛板块,用户发帖前需先有板块

{loading ? (
) : ( <> ID 名称 简介 排序 帖子数 操作 {boards.map(board => ( {board.id} {board.name} {board.description} {board.sort_order} {board.post_count ?? 0}
确定删除该板块? 删除后该板块下的帖子将无法通过板块筛选,此操作不可撤销。 取消 handleDelete(board.id)}> 删除
))}
{boards.length === 0 && (

还没有板块,点击右上角创建第一个

)} )}
{editing ? '编辑板块' : '新建板块'}
( 板块名称 )} /> ( 简介