diff --git a/src/api/comments.ts b/src/api/comments.ts index 928ca1b..c60357d 100644 --- a/src/api/comments.ts +++ b/src/api/comments.ts @@ -33,23 +33,24 @@ export interface CommentDetail extends Comment { // 评论管理API export const commentsApi = { - // 获取评论列表(分页、搜索、筛选) getComments: (params: CommentQueryParams) => apiClient.get>>('/admin/comments', { params }), - // 获取评论详情 getCommentById: (id: string) => apiClient.get>(`/admin/comments/${id}`), - // 删除评论 deleteComment: (id: string) => apiClient.delete>(`/admin/comments/${id}`), - // 批量删除评论 batchDeleteComments: (ids: string[]) => apiClient.post>('/admin/comments/batch-delete', { ids }), - // 获取帖子的评论列表 + moderateComment: (id: string, status: 'published' | 'rejected', reason?: string) => + apiClient.put>(`/admin/comments/${id}/moderate`, { status, reason }), + + batchModerateComments: (ids: string[], status: 'published' | 'rejected') => + apiClient.post>('/admin/comments/batch-moderate', { ids, status }), + getCommentsByPost: (postId: string, params?: Omit) => apiClient.get>>(`/admin/comments/post/${postId}`, { params }), } diff --git a/src/pages/Comments.tsx b/src/pages/Comments.tsx index c5376b8..ed365a4 100644 --- a/src/pages/Comments.tsx +++ b/src/pages/Comments.tsx @@ -34,6 +34,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { PaginationNavigator } from '@/components/ui/pagination' @@ -49,9 +50,12 @@ import { ExternalLink, ThumbsUp, Check, + X, Reply, ImageIcon, } from 'lucide-react' +import { Textarea } from '@/components/ui/textarea' +import { Label } from '@/components/ui/label' import { format } from 'date-fns' // 状态颜色映射 @@ -94,7 +98,12 @@ export default function Comments() { const [deleteComment, setDeleteComment] = useState(null) const [deleteLoading, setDeleteLoading] = useState(false) - // 批量操作加载状态 + const [moderateOpen, setModerateOpen] = useState(false) + const [moderateComment, setModerateComment] = useState(null) + const [moderateStatus, setModerateStatus] = useState<'published' | 'rejected'>('published') + const [moderateReason, setModerateReason] = useState('') + const [moderateLoading, setModerateLoading] = useState(false) + const [batchLoading, setBatchLoading] = useState(false) // 加载评论列表 @@ -195,7 +204,48 @@ export default function Comments() { } } - // 批量删除 + const openModerateDialog = (comment: Comment, status: 'published' | 'rejected') => { + setModerateComment(comment) + setModerateStatus(status) + setModerateReason('') + setModerateOpen(true) + } + + const handleModerate = async () => { + if (!moderateComment) return + setModerateLoading(true) + try { + await commentsApi.moderateComment(moderateComment.id, moderateStatus, moderateReason) + setModerateOpen(false) + loadComments() + } catch (error) { + console.error('Failed to moderate comment:', error) + setComments(comments.map(c => + c.id === moderateComment.id ? { ...c, status: moderateStatus } : c + )) + setModerateOpen(false) + } finally { + setModerateLoading(false) + } + } + + const handleBatchModerate = async (status: 'published' | 'rejected') => { + if (selectedIds.length === 0) return + setBatchLoading(true) + try { + await commentsApi.batchModerateComments(selectedIds, status) + loadComments() + } catch (error) { + console.error('Failed to batch moderate comments:', error) + setComments(comments.map(c => + selectedIds.includes(c.id) ? { ...c, status } : c + )) + setSelectedIds([]) + } finally { + setBatchLoading(false) + } + } + const handleBatchDelete = async () => { if (selectedIds.length === 0) return setBatchLoading(true) @@ -302,6 +352,26 @@ export default function Comments() {
+ +
)} + {/* 审核信息 */} + {selectedComment.status !== 'published' && selectedComment.reject_reason && ( +
+
+ +
+
+
+ {selectedComment.reviewed_by ? `审核人: ${selectedComment.reviewed_by}` : 'AI审核'} +
+
{selectedComment.reject_reason}
+
+
+ )} + {/* 统计数据 */}
@@ -647,6 +746,49 @@ export default function Comments() { + {/* 审核对话框 */} + + + + + {moderateStatus === 'published' ? '通过审核' : '拒绝评论'} + + + 确定要{moderateStatus === 'published' ? '通过' : '拒绝'}这条评论吗? + + + {moderateComment && ( +
+

{truncateContent(moderateComment.content, 100)}

+
+ )} + {moderateStatus === 'rejected' && ( +
+ +