// ============================================================ // AnnouncementDrawer — 公告侧拉抽屉(SPA 内部弹出) // 数据来源:GET /api/v1/announcements // ============================================================ import { useEffect, useState, useCallback } from 'react'; import { Drawer, List, Typography, Empty, Tag, Spin } from 'antd'; import { NotificationOutlined } from '@ant-design/icons'; import { fetchAnnouncements, type Announcement } from '@/services/modules'; const { Text, Paragraph } = Typography; // ---------- 辅助 ---------- /** 公告类型 → Tag 颜色映射 */ const typeColorMap: Record = { system: 'blue', feature: 'purple', activity: 'orange', maintenance: 'red', }; /** 公告类型 → 中文标签 */ const typeLabelMap: Record = { system: '系统', feature: '功能', activity: '活动', maintenance: '维护', }; /** 格式化日期(YYYY-MM-DD) */ function formatDate(iso: string): string { try { return new Date(iso).toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', }); } catch { return iso; } } // ---------- 组件 ---------- interface AnnouncementDrawerProps { open: boolean; onClose: () => void; } export function AnnouncementDrawer({ open, onClose }: AnnouncementDrawerProps) { const [list, setList] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // 打开抽屉时请求公告列表 const loadAnnouncements = useCallback(async () => { setLoading(true); setError(null); try { const data = await fetchAnnouncements(); // 仅展示有效公告 setList(data.filter((item) => item.is_active)); } catch (err) { setError((err as Error).message || '加载公告失败'); setList([]); } finally { setLoading(false); } }, []); useEffect(() => { if (open) { loadAnnouncements(); } }, [open, loadAnnouncements]); // ---- 渲染 ---- return ( 公告 } placement="right" size={380} open={open} onClose={onClose} styles={{ body: { padding: 0 } }} > {loading ? (
) : error ? (
) : list.length === 0 ? (
) : ( (
{/* 标题 + 类型标签 */}
{item.pinned && '📌 '} {item.title} {typeLabelMap[item.type] || item.type}
{/* 内容 */} {item.content} {/* 日期 */} {formatDate(item.created_at)}
)} /> )}
); }