import React, { useCallback, useEffect, useState } from 'react'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Paper from '@mui/material/Paper'; import Tabs from '@mui/material/Tabs'; import Tab from '@mui/material/Tab'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import Button from '@mui/material/Button'; import Chip from '@mui/material/Chip'; import IconButton from '@mui/material/IconButton'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; import TableCell from '@mui/material/TableCell'; import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; import RefreshIcon from '@mui/icons-material/Refresh'; import CheckIcon from '@mui/icons-material/Check'; import CloseIcon from '@mui/icons-material/Close'; import DeleteIcon from '@mui/icons-material/Delete'; import { listPendingComments, listAllComments, approveComment, rejectComment, deleteComment } from '../../api/blog.js'; import { showSnack } from '../snack.jsx'; const PAGE_SIZE = 20; /** 时间格式化:yyyy-mm-dd hh:mm */ function fmtTime(t) { if (!t) return ''; return String(t).replace('T', ' ').slice(0, 16); } /** 内容截断(单行预览):换行折叠 + 超长省略 */ function clip(content, max = 60) { const s = String(content || '').replace(/\s+/g, ' ').trim(); return s.length > max ? s.slice(0, max) + '…' : s; } /** 状态 chip */ function StatusChip({ status }) { if (status === 'approved') return ; if (status === 'rejected') return ; return ; } /** 评论管理:待审核队列 + 全部评论分页列表(status=all) */ export default function CommentManage() { const [tab, setTab] = useState(0); const [pending, setPending] = useState([]); const [busy, setBusy] = useState(false); // 全部评论(分页) const [allList, setAllList] = useState([]); const [allPage, setAllPage] = useState(1); const [allTotal, setAllTotal] = useState(0); const [allTotalPages, setAllTotalPages] = useState(0); const [allLoading, setAllLoading] = useState(false); const load = useCallback(() => { setBusy(true); listPendingComments() .then((list) => setPending(list || [])) .catch((e) => showSnack(e.message, 'error')) .finally(() => setBusy(false)); }, []); useEffect(() => { load(); }, [load]); const loadAll = useCallback((page) => { setAllLoading(true); listAllComments({ status: 'all', page, pageSize: PAGE_SIZE }) .then((res) => { setAllList((res && res.list) || []); setAllTotal(res ? res.total || 0 : 0); setAllTotalPages(res ? res.totalPages || 0 : 0); setAllPage(page); }) .catch((e) => showSnack(e.message, 'error')) .finally(() => setAllLoading(false)); }, []); useEffect(() => { if (tab === 1) loadAll(1); }, [tab, loadAll]); const act = async (id, fn, okMsg) => { setBusy(true); try { await fn(id); setPending((list) => list.filter((c) => c.id !== id)); showSnack(okMsg); } catch (e) { showSnack(e.message, 'error'); } setBusy(false); }; const actAll = async (id, fn, okMsg, reloadPage = allPage) => { setBusy(true); try { await fn(id); showSnack(okMsg); // 同步待审核徽标 + 刷新当前页;若删除后本页空且非第一页则回退一页 setPending((list) => list.filter((c) => c.id !== id)); const remaining = allList.filter((c) => c.id !== id).length; loadAll(remaining === 0 && reloadPage > 1 ? reloadPage - 1 : reloadPage); } catch (e) { showSnack(e.message, 'error'); } setBusy(false); }; const totalPages = Math.max(1, allTotalPages); return ( 评论管理 (tab === 0 ? load() : loadAll(allPage))} title="刷新" disabled={busy || allLoading}> setTab(v)}> {tab === 0 ? ( pending.length === 0 ? ( 暂无待审核评论 开启「评论审核模式」后,新评论会先进这里等待审核 ) : ( {pending.map((c) => ( {c.author_name || '匿名'} {fmtTime(c.created_at)} {c.content} ))} ) ) : ( <> 作者 文章 内容 状态 时间 操作 {allLoading && allList.length === 0 ? ( 加载中... ) : allList.length === 0 ? ( 暂无评论 ) : allList.map((c) => ( {c.author_name || '匿名'} {c.post_title || `#${c.post_id}`} {clip(c.content)} {fmtTime(c.created_at)} {c.status === 'pending' && ( <> )} actAll(c.id, deleteComment, '已删除')}> ))}
第 {allPage} / {totalPages} 页 · 共 {allTotal} 条 )}
); }