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