import React, { useEffect, useState } from 'react'; import { Button, Card, Form, Input, Modal, Popconfirm, Select, Space, Table, Tag, Typography, message, Empty, } from 'antd'; import { BellOutlined, PlusOutlined, DeleteOutlined, SendOutlined, EyeOutlined, TeamOutlined, } from '@ant-design/icons'; import ReactQuill from 'react-quill-new'; import 'react-quill-new/dist/quill.snow.css'; import { getAdminNotifications, createAdminNotification, deleteAdminNotification, getAdminUsers, getNotificationReadUsers } from '../api'; import { formatDate } from '../utils/formatDate'; interface NotificationRecord { id: string; title: string; content: string; type: string; target: string; targetUserId: string | null; createdAt: string; } interface UserOption { id: string; username: string; } interface ReadUser { userId: string; username: string; readAt: string; } const AdminNotificationManager: React.FC = () => { const [notifications, setNotifications] = useState([]); const [loading, setLoading] = useState(false); const [users, setUsers] = useState([]); const [modalOpen, setModalOpen] = useState(false); const [form] = Form.useForm(); const [readModal, setReadModal] = useState<{ open: boolean; notifId: string; title: string }>({ open: false, notifId: '', title: '' }); const [readUsers, setReadUsers] = useState([]); const [readLoading, setReadLoading] = useState(false); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [total, setTotal] = useState(0); const load = async () => { setLoading(true); try { const res = await getAdminNotifications(page, pageSize); const userRes = await getAdminUsers(1, 1000); const userList = userRes.items || []; const userMap: Record = {}; userList.forEach((u: any) => { userMap[u.id] = u.username; }); const items = (res.items || []).map((n: any) => ({ id: n.id, title: n.title, content: n.content, type: n.type, targetUserId: n.userId || null, target: n.userId ? (userMap[n.userId] || n.userId) : '全部用户', createdAt: n.createdAt, })); setNotifications(items); setTotal(res.total || 0); setUsers(userList.map((u: any) => ({ id: u.id, username: u.username }))); } catch { message.error('加载通知列表失败'); } finally { setLoading(false); } }; useEffect(() => { load(); }, [page, pageSize]); const handlePageChange = (p: number, ps: number) => { setPage(p); setPageSize(ps); }; const handleSend = async () => { try { const values = await form.validateFields(); await createAdminNotification({ title: values.title, content: values.content, type: values.type || 'system', target_user_id: values.target_user_id || undefined, }); message.success('消息已发送'); setModalOpen(false); form.resetFields(); load(); } catch { /* validation */ } }; const handleDelete = async (id: string) => { try { await deleteAdminNotification(id); message.success('已删除'); load(); } catch (e: any) { message.error(e?.message || '删除失败'); } }; const handleViewRead = async (notifId: string, title: string) => { setReadModal({ open: true, notifId, title }); setReadLoading(true); try { const res = await getNotificationReadUsers(notifId); setReadUsers(res.items || []); } catch { message.error('加载已读列表失败'); } finally { setReadLoading(false); } }; const getTypeColor = (type: string) => { switch (type) { case 'system': return 'blue'; case 'credit': return 'orange'; case 'promo': return 'purple'; default: return 'default'; } }; const columns = [ { title: '标题', dataIndex: 'title', width: 200, render: (v: string) => {v}, }, { title: '内容', dataIndex: 'content', ellipsis: true, render: (v: string) =>
, }, { title: '类型', dataIndex: 'type', width: 80, render: (v: string) => { const labels: Record = { system: '系统', credit: '积分', promo: '活动' }; return {labels[v] || v}; }, }, { title: '发送目标', dataIndex: 'target', width: 120, render: (v: string) => ( {v} ), }, { title: '发送时间', dataIndex: 'createdAt', width: 160, render: (v: string) => formatDate(v), }, { title: '操作', key: 'action', width: 180, render: (_: any, r: NotificationRecord) => ( handleDelete(r.id)}> ), }, ]; return (
消息推送管理 共 {total} 条消息
`共 ${t} 条消息`, }} scroll={{ x: 900 }} /> {/* Send Notification Modal */} 发送消息} open={modalOpen} onOk={handleSend} onCancel={() => { setModalOpen(false); form.resetFields(); }} okText="发送" cancelText="取消" width={520} >
v && v !== '


' ? Promise.resolve() : Promise.reject('请输入内容') }]}>
({ value: u.id, label: u.username }))} />
{/* Read Users Modal */} 已读用户 — {readModal.title}} open={readModal.open} onCancel={() => { setReadModal({ open: false, notifId: '', title: '' }); setReadUsers([]); }} footer={null} width={480} > {readLoading ? (
加载中...
) : readUsers.length === 0 ? ( ) : (
共 {readUsers.length} 人已读
{v} }, { title: '已读时间', dataIndex: 'readAt', width: 180, render: (v: string) => formatDate(v) }, ]} /> )} ); }; export default AdminNotificationManager;