import { useEffect, useState, useCallback, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { Table, Button, Space, Input, Select, Tag, Tabs, Popconfirm, message, Modal, Form, } from 'antd'; import { PlusOutlined, SearchOutlined, EditOutlined, DeleteOutlined, SendOutlined, CheckOutlined, CloseOutlined, RollbackOutlined, EyeOutlined, } from '@ant-design/icons'; import { articleApi, articleCategoryApi, type ArticleListItem, type ArticleStatus, type ArticleTagItem, } from '../../api/health/articles'; import { useThemeMode } from '../../hooks/useThemeMode'; import { AuthButton } from '../../components/AuthButton'; const STATUS_TABS: { key: string; label: string }[] = [ { key: '', label: '全部' }, { key: 'draft', label: '草稿' }, { key: 'pending_review', label: '待审核' }, { key: 'published', label: '已发布' }, { key: 'rejected', label: '已拒绝' }, ]; const STATUS_CONFIG: Record< string, { label: string; color: string } > = { draft: { label: '草稿', color: 'default' }, pending_review: { label: '待审核', color: 'processing' }, published: { label: '已发布', color: 'success' }, rejected: { label: '已拒绝', color: 'error' }, }; export default function ArticleManageList() { const [articles, setArticles] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(false); const [statusTab, setStatusTab] = useState(''); const [categoryId, setCategoryId] = useState(undefined); const [keyword, setKeyword] = useState(''); const [categories, setCategories] = useState<{ id: string; name: string }[]>([]); const [rejectModalOpen, setRejectModalOpen] = useState(false); const [rejectingArticle, setRejectingArticle] = useState(null); const [rejectForm] = Form.useForm(); const isDark = useThemeMode(); const navigate = useNavigate(); const fetchArticles = useCallback( async (p = page) => { setLoading(true); try { const result = await articleApi.list({ page: p, page_size: 20, status: (statusTab || undefined) as ArticleStatus | undefined, category_id: categoryId, keyword: keyword || undefined, }); setArticles(result.data); setTotal(result.total); } catch { message.error('加载文章列表失败'); } finally { setLoading(false); } }, [page, statusTab, categoryId, keyword], ); const fetchCategories = useCallback(async () => { try { const cats = await articleCategoryApi.list(); setCategories(cats.map((c) => ({ id: c.id, name: c.name }))); } catch { // 分类列表加载失败不阻塞页面 } }, []); useEffect(() => { fetchArticles(); }, [fetchArticles]); useEffect(() => { fetchCategories(); }, [fetchCategories]); const debounceTimer = useRef | null>(null); const debouncedSearch = useCallback((value: string) => { setKeyword(value); if (debounceTimer.current) clearTimeout(debounceTimer.current); debounceTimer.current = setTimeout(() => { setPage(1); }, 300); }, []); const handleDelete = async (id: string) => { try { await articleApi.delete(id); message.success('文章已删除'); fetchArticles(); } catch { message.error('删除失败'); } }; const handleSubmit = async (record: ArticleListItem) => { try { await articleApi.submit(record.id, record.version); message.success('已提交审核'); fetchArticles(); } catch { message.error('提交审核失败'); } }; const handleApprove = async (record: ArticleListItem) => { try { await articleApi.approve(record.id, record.version); message.success('审核通过,文章已发布'); fetchArticles(); } catch { message.error('审核操作失败'); } }; const openRejectModal = (record: ArticleListItem) => { setRejectingArticle(record); rejectForm.resetFields(); setRejectModalOpen(true); }; const handleReject = async (values: { review_note: string }) => { if (!rejectingArticle) return; try { await articleApi.reject( rejectingArticle.id, rejectingArticle.version, values.review_note, ); message.success('已拒绝文章'); setRejectModalOpen(false); fetchArticles(); } catch { message.error('拒绝操作失败'); } }; const handleUnpublish = async (record: ArticleListItem) => { try { await articleApi.unpublish(record.id, record.version); message.success('文章已撤回为草稿'); fetchArticles(); } catch { message.error('撤回操作失败'); } }; const renderActions = (record: ArticleListItem) => ( {record.status === 'draft' && ( <> )} {record.status === 'pending_review' && ( <> )} {record.status === 'published' && ( )} {(record.status === 'draft' || record.status === 'rejected') && ( handleDelete(record.id)} > {/* 筛选栏 */}
} value={keyword} onChange={(e) => debouncedSearch(e.target.value)} allowClear style={{ width: 220, borderRadius: 8 }} />