feat: 添加全员公告发布页面,支持撰写和广播系统通知并查看历史记录。
This commit is contained in:
@@ -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={{
|
||||
padding: '12px 0',
|
||||
borderBottom: idx === 2 ? '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>
|
||||
<div style={{
|
||||
padding: '2px 8px',
|
||||
borderRadius: 4,
|
||||
fontSize: 11,
|
||||
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 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 === history.length - 1 ? 'none' : '1px solid #f1f5f9',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<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: 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>
|
||||
))
|
||||
)}
|
||||
</Spin>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
Reference in New Issue
Block a user