feat: 添加全员公告发布页面,支持撰写和广播系统通知并查看历史记录。

This commit is contained in:
2026-01-29 13:54:13 +08:00
parent a8dc4d2f06
commit 9c901447af

View File

@@ -1,15 +1,44 @@
import React, { useState } from 'react';
import { Form, Input, Select, Button, message, Card, Alert, Row, Col } from 'antd';
import React, { useState, useEffect } from 'react';
import { Form, Input, Select, Button, message, Card, Alert, Row, Col, Empty, Spin } from 'antd';
import { Icon } from '@iconify/react';
import request from '../../utils/request';
const { TextArea } = Input;
const { Option } = Select;
interface Announcement {
id: number;
title: string;
content: string;
type: string;
sent_count: number;
created_at: string;
}
const NotificationPublish: React.FC = () => {
const [loading, setLoading] = useState(false);
const [historyLoading, setHistoryLoading] = useState(false);
const [history, setHistory] = useState<Announcement[]>([]);
const [form] = Form.useForm();
const fetchHistory = async () => {
setHistoryLoading(true);
try {
const res: any = await request.get('/notifications/announcements?limit=5');
if (res && res.announcements) {
setHistory(res.announcements);
}
} catch (error) {
console.error('获取历史记录失败:', error);
} finally {
setHistoryLoading(false);
}
};
useEffect(() => {
fetchHistory();
}, []);
const onFinish = async (values: any) => {
setLoading(true);
try {
@@ -19,6 +48,7 @@ const NotificationPublish: React.FC = () => {
style: { marginTop: '10vh' }
});
form.resetFields();
fetchHistory(); // Refresh history after publish
} catch (error) {
console.error('发布失败:', error);
} finally {
@@ -26,6 +56,16 @@ const NotificationPublish: React.FC = () => {
}
};
const formatTime = (timeStr: string) => {
const date = new Date(timeStr);
return date.toLocaleString('zh-CN', {
month: 'numeric',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
return (
<div style={{ maxWidth: 1000, margin: '0 auto' }}>
<div style={{ marginBottom: 32 }}>
@@ -113,7 +153,7 @@ const NotificationPublish: React.FC = () => {
<Col lg={10}>
<Alert
title="安全提示"
description="全员公告将穿透所有用户的勿扰模式(如果适用),发送前请仔细检查文案拼写和逻辑,发布后无法撤回。"
description="全员公告将穿透所有用户的勿扰模式,发布后无法撤回,请反复通过侧边栏历史核对文案。"
type="info"
showIcon
icon={<Icon icon="solar:info-circle-bold-duotone" width="24" />}
@@ -127,36 +167,43 @@ const NotificationPublish: React.FC = () => {
<span></span>
</div>
}
extra={<Button type="link" size="small" onClick={fetchHistory} loading={historyLoading}></Button>}
variant="borderless"
>
{[
{ title: '系统性能优化完成', time: '1小时前', type: 'info' },
{ title: '夜间数据库升级', time: '昨天', type: 'alert' },
{ title: '欢迎新用户加入', time: '3天前', type: 'system' }
].map((item, idx) => (
<div key={idx} style={{
<Spin spinning={historyLoading}>
{history.length === 0 ? (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无发布记录" />
) : (
history.map((item, idx) => (
<div key={item.id} style={{
padding: '12px 0',
borderBottom: idx === 2 ? 'none' : '1px solid #f1f5f9',
borderBottom: idx === history.length - 1 ? 'none' : '1px solid #f1f5f9',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<div>
<div style={{ fontWeight: 500, fontSize: 14 }}>{item.title}</div>
<div style={{ fontSize: 12, color: '#94a3b8' }}>{item.time}</div>
<div style={{ overflow: 'hidden', marginRight: 12 }}>
<div style={{ fontWeight: 500, fontSize: 14, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{item.title}
</div>
<div style={{ fontSize: 12, color: '#94a3b8' }}>
{formatTime(item.created_at)} · {item.sent_count}
</div>
</div>
<div style={{
padding: '2px 8px',
borderRadius: 4,
fontSize: 11,
fontSize: 10,
flexShrink: 0,
background: item.type === 'alert' ? '#fee2e2' : item.type === 'info' ? '#dcfce7' : '#e0e7ff',
color: item.type === 'alert' ? '#ef4444' : item.type === 'info' ? '#10b981' : '#6366f1'
}}>
{item.type.toUpperCase()}
</div>
</div>
))}
<Button type="link" block style={{ marginTop: 12 }}></Button>
))
)}
</Spin>
</Card>
</Col>
</Row>