import { useEffect, useState, useMemo, useCallback, useRef } from 'react'; import { Table, Button, Tag, Space, Modal, Typography, message, theme } from 'antd'; import { CheckOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import { listMessages, markRead, markAllRead, deleteMessage, type MessageInfo, type MessageQuery } from '../../api/messages'; const { Paragraph } = Typography; interface Props { queryFilter?: MessageQuery; } const priorityStyles: Record = { urgent: { bg: '#FEF2F2', color: '#DC2626', text: '紧急' }, important: { bg: '#FFFBEB', color: '#D97706', text: '重要' }, normal: { bg: '#EEF2FF', color: '#4F46E5', text: '普通' }, }; export default function NotificationList({ queryFilter }: Props) { const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(false); const { token } = theme.useToken(); const isDark = token.colorBgContainer === '#111827' || token.colorBgContainer === 'rgb(17, 24, 39)'; const fetchData = useCallback(async (p = page, filter?: MessageQuery) => { setLoading(true); try { const result = await listMessages({ page: p, page_size: 20, ...filter }); setData(result.data); setTotal(result.total); } catch { message.error('加载消息列表失败'); } finally { setLoading(false); } }, [page]); const filterKey = useMemo(() => JSON.stringify(queryFilter), [queryFilter]); const isFirstRender = useRef(true); useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; fetchData(1, queryFilter); } }, [filterKey, fetchData, queryFilter]); const handleMarkRead = async (id: string) => { try { await markRead(id); fetchData(page, queryFilter); } catch { message.error('操作失败'); } }; const handleMarkAllRead = async () => { try { await markAllRead(); fetchData(page, queryFilter); message.success('已全部标记为已读'); } catch { message.error('操作失败'); } }; const handleDelete = async (id: string) => { try { await deleteMessage(id); fetchData(page, queryFilter); message.success('已删除'); } catch { message.error('删除失败'); } }; const showDetail = (record: MessageInfo) => { Modal.info({ title: record.title, width: 520, content: (
{record.body}
{record.created_at}
), }); if (!record.is_read) { handleMarkRead(record.id); } }; const columns: ColumnsType = [ { title: '标题', dataIndex: 'title', key: 'title', render: (text: string, record) => ( showDetail(record)} > {!record.is_read && ( )} {text} ), }, { title: '优先级', dataIndex: 'priority', key: 'priority', width: 90, render: (p: string) => { const info = priorityStyles[p] || { bg: '#F1F5F9', color: '#64748B', text: p }; return ( {info.text} ); }, }, { title: '发送者', dataIndex: 'sender_type', key: 'sender_type', width: 80, render: (s: string) => {s === 'system' ? '系统' : '用户'}, }, { title: '状态', dataIndex: 'is_read', key: 'is_read', width: 80, render: (r: boolean) => ( {r ? '已读' : '未读'} ), }, { title: '时间', dataIndex: 'created_at', key: 'created_at', width: 180, render: (v: string) => ( {v} ), }, { title: '操作', key: 'actions', width: 120, render: (_: unknown, record) => ( {!record.is_read && (
{ setPage(p); fetchData(p, queryFilter); }, showTotal: (t) => `共 ${t} 条记录`, }} /> ); }