事件通知

main
wangyunfei 1 month ago
parent f006238f3a
commit fbb67a8f94

@ -1,16 +1,407 @@
import React from 'react'; import React, { useState } from 'react';
import { Input, Select, Button, Checkbox, Tag } from 'antd';
import {
EditOutlined,
VideoCameraOutlined,
SendOutlined,
ClockCircleOutlined,
LeftOutlined,
PlayCircleOutlined,
InfoCircleOutlined,
RightOutlined
} from '@ant-design/icons';
import StandardTable from '@/components/StandardTable';
import styles from './EventNotification.less'; import styles from './EventNotification.less';
const { TextArea } = Input;
const { Option } = Select;
const EventNotification = () => { const EventNotification = () => {
const [eventTitle, setEventTitle] = useState('');
const [eventType, setEventType] = useState('');
const [eventLevel, setEventLevel] = useState('');
const [eventDescription, setEventDescription] = useState('');
const [location, setLocation] = useState('');
const [videoRecord, setVideoRecord] = useState('');
const [pushTargets, setPushTargets] = useState({
'director-li': false,
'captain-zhang': true,
'professor-wang': true
});
const [pushMethod, setPushMethod] = useState({
'sms': true,
'email': false
});
// 事件列表数据
const eventListData = [
{
key: '1',
title: '交通事故(A301国道K125段)',
time: '2023-04-17 14:45',
status: '重大',
statusColor: '#ff4d4f',
currentStatus: '处理中',
id: 'EVT-20230615-008'
},
{
key: '2',
title: '火灾事故(南城区商业街)',
time: '2023-04-16 09:30',
status: '较大',
statusColor: '#ff9800',
currentStatus: '已解决',
id: 'EVT-20230614-005'
},
{
key: '3',
title: '化学品泄漏(工业区A区)',
time: '2023-04-15 16:20',
status: '一般',
statusColor: '#36cfc9',
currentStatus: '已解决',
id: 'EVT-20230613-003'
},
{
key: '4',
title: '自然灾害(台风影响)',
time: '2023-04-14 11:15',
status: '较大',
statusColor: '#ff9800',
currentStatus: '已解决',
id: 'EVT-20230612-001'
}
];
// 推送记录表格数据
const pushRecordData = {
list: [
{
key: '1',
eventId: '#ALRT-20230512-001',
eventName: '城南小区火灾事故',
pushTime: '2025-10-20 01:32:25',
recipient: '消防救援支队、社区',
pushMethod: '短信、系统消息',
status: '已送达',
statusType: 'delivered'
},
{
key: '2',
eventId: '#ALRT-20230512-002',
eventName: '城西路段积水报告',
pushTime: '2025-10-18 21:15:07',
recipient: '市政管理处、交警',
pushMethod: '系统消息',
status: '已送达',
statusType: 'delivered'
},
{
key: '3',
eventId: '#ALRT-20230512-003',
eventName: '国道交通事故',
pushTime: '2025-10-15 12:24:25',
recipient: '交警、急救中心',
pushMethod: '短信、电话',
status: '发送中',
statusType: 'sending'
}
],
pagination: {
current: 3,
currentPage: 3,
pageSize: 10,
total: 48,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total}`
}
};
// 推送记录表格列定义
const pushRecordColumns = [
{
title: '事件编号',
dataIndex: 'eventId',
key: 'eventId',
width: 180
},
{
title: '事件名称',
dataIndex: 'eventName',
key: 'eventName',
width: 180
},
{
title: '推送时间',
dataIndex: 'pushTime',
key: 'pushTime',
width: 180
},
{
title: '接收对象',
dataIndex: 'recipient',
key: 'recipient',
width: 200
},
{
title: '推送方式',
dataIndex: 'pushMethod',
key: 'pushMethod',
width: 150
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (text, record) => {
let color = '#52c41a';
if (record.statusType === 'sending') color = '#ff9800';
return <Tag color={color}>{text}</Tag>;
}
},
{
title: '操作',
key: 'action',
width: 100,
render: () => (
<Button type="link" size="small">查看</Button>
)
}
];
const handlePushTargetChange = (key, checked) => {
setPushTargets(prev => ({
...prev,
[key]: checked
}));
};
const handlePushMethodChange = (key, checked) => {
setPushMethod(prev => ({
...prev,
[key]: checked
}));
};
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.content}> {/* A块顶部区域高度60%,背景透明 */}
<h2>事件通知</h2> <div className={styles.blockA}>
{/* 这里可以添加具体的内容 */} {/* a块事件信息编辑 */}
<div className={styles.blockA_a}>
<div className={styles.cardHeader}>
<EditOutlined className={styles.headerIcon} />
<span className={styles.headerText}>事件信息编辑</span>
</div>
<div className={styles.cardContent}>
<div className={styles.formItem}>
<label className={styles.label}>事件标题</label>
<Input
placeholder="请输入事件标题"
value={eventTitle}
onChange={(e) => setEventTitle(e.target.value)}
/>
</div>
<div className={styles.formItem}>
<label className={styles.label}>事件类型</label>
<Select
placeholder="请选择事件类型"
value={eventType}
onChange={setEventType}
suffixIcon={<RightOutlined />}
>
<Option value="traffic">交通事故</Option>
<Option value="fire">火灾事故</Option>
<Option value="chemical">化学品泄漏</Option>
<Option value="natural">自然灾害</Option>
</Select>
</div>
<div className={styles.formItem}>
<label className={styles.label}>事件级别</label>
<Select
placeholder="请选择事件级别"
value={eventLevel}
onChange={setEventLevel}
suffixIcon={<RightOutlined />}
>
<Option value="major">重大</Option>
<Option value="significant">较大</Option>
<Option value="general">一般</Option>
</Select>
</div>
<div className={styles.formItem}>
<label className={styles.label}>事件描述</label>
<TextArea
placeholder="请详细描述事件情况..."
rows={4}
value={eventDescription}
onChange={(e) => setEventDescription(e.target.value)}
/>
</div>
<Button type="primary" className={styles.saveButton}>
保存事件信息
</Button>
</div>
</div>
{/* b块视频核实 */}
<div className={styles.blockA_b}>
<div className={styles.cardHeader}>
<VideoCameraOutlined className={styles.headerIcon} />
<span className={styles.headerText}>视频核实</span>
</div>
<div className={styles.cardContent}>
<div className={styles.formItem}>
<label className={styles.label}>事发地点</label>
<Input
placeholder="请输入事发地点"
value={location}
onChange={(e) => setLocation(e.target.value)}
/>
</div>
<div className={styles.formItem}>
<label className={styles.label}>视频监控画面</label>
<div className={styles.videoContainer}>
<div className={styles.videoPlaceholder}>
{/* 视频占位图 */}
<div className={styles.videoImage}>
<div className={styles.cityscape}></div>
</div>
<div className={styles.videoControls}>
<LeftOutlined className={styles.controlIcon} />
<PlayCircleOutlined className={styles.controlIcon} />
<InfoCircleOutlined className={styles.controlIcon} />
<RightOutlined className={styles.controlIcon} />
</div>
</div>
<div className={styles.videoButtons}>
<Button type="primary">搜索监控点</Button>
<Button type="primary">播放实时画面</Button>
</div>
</div>
</div>
<div className={styles.formItem}>
<label className={styles.label}>视频核实记录</label>
<TextArea
placeholder="记录视频核实情况..."
rows={3}
value={videoRecord}
onChange={(e) => setVideoRecord(e.target.value)}
/>
</div>
</div>
</div>
{/* c块信息推送 */}
<div className={styles.blockA_c}>
<div className={styles.cardHeader}>
<SendOutlined className={styles.headerIcon} />
<span className={styles.headerText}>信息推送</span>
</div>
<div className={styles.cardContent}>
<div className={styles.formItem}>
<label className={styles.label}>推送对象</label>
<div className={styles.checkboxGroup}>
<Checkbox
checked={pushTargets['director-li']}
onChange={(e) => handlePushTargetChange('director-li', e.target.checked)}
>
现场指挥中心-李主任
</Checkbox>
<Checkbox
checked={pushTargets['captain-zhang']}
onChange={(e) => handlePushTargetChange('captain-zhang', e.target.checked)}
>
消防救援队-张队长
</Checkbox>
<Checkbox
checked={pushTargets['professor-wang']}
onChange={(e) => handlePushTargetChange('professor-wang', e.target.checked)}
>
应急专家组-王教授
</Checkbox>
</div>
</div>
<div className={styles.formItem}>
<label className={styles.label}>事件类型</label>
<Select
placeholder="选择应急预案"
suffixIcon={<RightOutlined />}
>
<Option value="plan1">应急预案1</Option>
<Option value="plan2">应急预案2</Option>
</Select>
</div>
<div className={styles.formItem}>
<label className={styles.label}>推送方式</label>
<div className={styles.checkboxGroupHorizontal}>
<Checkbox
checked={pushMethod['sms']}
onChange={(e) => handlePushMethodChange('sms', e.target.checked)}
>
短信
</Checkbox>
<Checkbox
checked={pushMethod['email']}
onChange={(e) => handlePushMethodChange('email', e.target.checked)}
>
邮件
</Checkbox>
</div>
</div>
<Button type="primary" className={styles.pushButton}>
立即推送
</Button>
</div>
</div>
{/* d块事件列表 */}
<div className={styles.blockA_d}>
<div className={styles.blockA_d_header}>
<div className={styles.titleBar}></div>
<span className={styles.titleText}>事件列表</span>
</div>
<div className={styles.eventList}>
{eventListData.map(item => (
<div key={item.key} className={styles.eventItem}>
<div className={styles.eventItemHeader}>
<span className={styles.eventTitle}>{item.title}</span>
<Tag color={item.statusColor}>{item.status}</Tag>
</div>
<div className={styles.eventItemFooter}>
<div className={styles.eventTime}>
<ClockCircleOutlined className={styles.timeIcon} />
<span>{item.time}</span>
</div>
<div className={styles.eventStatus}>
<span className={styles.currentStatus}>{item.currentStatus}</span>
<span className={styles.eventId}>ID: {item.id}</span>
</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* B块推送记录 */}
<div className={styles.blockB}>
<div className={styles.blockB_header}>
<div className={styles.titleBar}></div>
<span className={styles.titleText}>推送记录</span>
</div>
<div className={styles.tableContent}>
<StandardTable
data={pushRecordData}
columns={pushRecordColumns}
rowKey="key"
/>
</div>
</div> </div>
</div> </div>
); );
}; };
export default EventNotification; export default EventNotification;

@ -1,12 +1,478 @@
.container { .container {
width: 100%; width: 100%;
height: 100%; height: 100vh;
padding: 20px; padding: 0;
margin: 0;
.content { display: flex;
flex-direction: column;
gap: 15px;
box-sizing: border-box;
background-color: #f0f2f5;
// A块顶部区域高度60%,背景透明
.blockA {
height: 60%;
width: 100%;
padding: 0;
margin: 0;
background-color: transparent;
display: flex;
gap: 15px;
box-sizing: border-box;
// a块事件信息编辑
.blockA_a {
width: 25%;
padding: 0;
margin: 0;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
.cardHeader {
height: 40px;
background-color: #556FEB;
display: flex;
align-items: center;
padding: 0 15px;
gap: 8px;
.headerIcon {
color: #fff;
font-size: 16px;
}
.headerText {
color: #fff;
font-size: 14px;
font-weight: 500;
}
}
.cardContent {
flex: 1;
padding: 15px;
display: flex;
flex-direction: column;
gap: 15px;
overflow-y: auto;
.formItem {
display: flex;
flex-direction: column;
gap: 8px;
.label {
font-size: 14px;
color: #333;
font-weight: 500;
}
:global(.ant-input),
:global(.ant-select-selector),
:global(.ant-input-affix-wrapper) {
border-radius: 4px;
}
}
.saveButton {
width: 100%;
margin-top: auto;
background-color: #556FEB;
border-color: #556FEB;
}
}
}
// b块视频核实
.blockA_b {
width: 25%;
padding: 0;
margin: 0;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
.cardHeader {
height: 40px;
background-color: #556FEB;
display: flex;
align-items: center;
padding: 0 15px;
gap: 8px;
.headerIcon {
color: #fff;
font-size: 16px;
}
.headerText {
color: #fff;
font-size: 14px;
font-weight: 500;
}
}
.cardContent {
flex: 1;
padding: 15px;
display: flex;
flex-direction: column;
gap: 15px;
overflow-y: auto;
.formItem {
display: flex;
flex-direction: column;
gap: 8px;
.label {
font-size: 14px;
color: #333;
font-weight: 500;
}
.videoContainer {
display: flex;
flex-direction: column;
gap: 10px;
.videoPlaceholder {
width: 100%;
border-radius: 4px;
overflow: hidden;
.videoImage {
width: 100%;
height: 150px;
position: relative;
overflow: hidden;
.cityscape {
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 50%, rgba(255, 200, 100, 0.4) 0%, transparent 50%),
radial-gradient(circle at 60% 30%, rgba(100, 150, 255, 0.3) 0%, transparent 50%),
radial-gradient(circle at 80% 70%, rgba(255, 150, 100, 0.3) 0%, transparent 50%),
linear-gradient(180deg, #1a1a2e 0%, #16213e 50%, #0f1419 100%);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40%;
background:
linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.6) 100%),
repeating-linear-gradient(
90deg,
transparent,
transparent 20px,
rgba(255, 255, 255, 0.1) 20px,
rgba(255, 255, 255, 0.1) 22px
);
}
&::after {
content: '';
position: absolute;
top: 10%;
left: 0;
right: 0;
height: 70%;
background-image:
linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.05) 20%, transparent 25%, transparent 45%, rgba(255, 255, 255, 0.05) 50%, transparent 55%, transparent 75%, rgba(255, 255, 255, 0.05) 80%, transparent 100%),
repeating-linear-gradient(
0deg,
transparent,
transparent 8px,
rgba(255, 255, 255, 0.03) 8px,
rgba(255, 255, 255, 0.03) 10px
);
}
}
}
.videoControls {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
padding: 10px;
background-color: #f5f5f5;
.controlIcon {
font-size: 18px;
color: #666;
cursor: pointer;
&:hover {
color: #556FEB;
}
}
}
}
.videoButtons {
display: flex;
gap: 10px;
:global(.ant-btn) {
flex: 1;
background-color: #556FEB;
border-color: #556FEB;
}
}
}
}
}
}
// c块信息推送
.blockA_c {
width: 25%;
padding: 0;
margin: 0;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
.cardHeader {
height: 40px;
background-color: #556FEB;
display: flex;
align-items: center;
padding: 0 15px;
gap: 8px;
.headerIcon {
color: #fff;
font-size: 16px;
}
.headerText {
color: #fff;
font-size: 14px;
font-weight: 500;
}
}
.cardContent {
flex: 1;
padding: 15px;
display: flex;
flex-direction: column;
gap: 15px;
overflow-y: auto;
.formItem {
display: flex;
flex-direction: column;
gap: 8px;
.label {
font-size: 14px;
color: #333;
font-weight: 500;
}
.checkboxGroup {
display: flex;
flex-direction: column;
gap: 10px;
:global(.ant-checkbox-wrapper) {
font-size: 14px;
color: #333;
}
}
.checkboxGroupHorizontal {
display: flex;
gap: 20px;
:global(.ant-checkbox-wrapper) {
font-size: 14px;
color: #333;
}
}
}
.pushButton {
width: 100%;
margin-top: auto;
background-color: #556FEB;
border-color: #556FEB;
}
}
}
// d块事件列表
.blockA_d {
width: 25%;
padding: 0;
margin: 0;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
.blockA_d_header {
padding: 15px;
display: flex;
align-items: center;
gap: 8px;
background-color: #fff;
.titleBar {
width: 2px;
height: 16px;
background-color: #2E4CD4;
flex-shrink: 0;
}
.titleText {
font-size: 16px;
font-weight: bold;
color: #333333;
}
}
.eventList {
flex: 1;
padding: 0 15px 15px 15px;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 15px;
overflow-y: auto;
.eventItem {
padding: 12px;
border: 1px solid #e8e8e8;
border-radius: 4px;
display: flex;
flex-direction: column;
gap: 10px;
background-color: #fafafa;
.eventItemHeader {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 10px;
.eventTitle {
flex: 1;
font-size: 14px;
font-weight: 500;
color: #333;
line-height: 1.4;
}
:global(.ant-tag) {
margin: 0;
flex-shrink: 0;
font-size: 12px;
}
}
.eventItemFooter {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
.eventTime {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #666;
.timeIcon {
font-size: 12px;
}
}
.eventStatus {
display: flex;
align-items: center;
gap: 10px;
font-size: 12px;
.currentStatus {
color: #666;
}
.eventId {
color: #999;
}
}
}
}
}
}
}
// B块推送记录
.blockB {
height: 40%;
width: 100%;
padding: 0;
margin: 0;
background-color: #fff; background-color: #fff;
padding: 20px; display: flex;
border-radius: 8px; flex-direction: column;
box-sizing: border-box;
.blockB_header {
padding: 15px;
display: flex;
align-items: center;
gap: 8px;
background-color: #fff;
.titleBar {
width: 2px;
height: 16px;
background-color: #2E4CD4;
flex-shrink: 0;
}
.titleText {
font-size: 16px;
font-weight: bold;
color: #333333;
}
}
.tableContent {
flex: 1;
padding: 0 15px 15px 15px;
overflow: hidden;
display: flex;
flex-direction: column;
:global(.ant-table-wrapper) {
flex: 1;
overflow: hidden;
}
:global(.ant-table-container) {
height: 100%;
}
:global(.ant-table-body) {
overflow-y: auto;
}
}
} }
} }

@ -101,6 +101,7 @@ const EventWarningManagement = () => {
top: '5%', top: '5%',
left: 'center', left: 'center',
itemGap: 30, itemGap: 30,
icon: 'line',
textStyle: { textStyle: {
color: '#666666', color: '#666666',
fontSize: 12 fontSize: 12

@ -35,6 +35,7 @@
margin: 0; margin: 0;
background-color: #ffffff; background-color: #ffffff;
border-radius: 8px; border-radius: 8px;
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex; display: flex;
gap: 15px; gap: 15px;
box-sizing: border-box; box-sizing: border-box;
@ -86,6 +87,7 @@
padding: 20px; padding: 20px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(208, 225, 255, 0.6) 100%); background: linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(208, 225, 255, 0.6) 100%);
border-radius: 8px; border-radius: 8px;
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.cardIcon { .cardIcon {
flex-shrink: 0; flex-shrink: 0;
@ -141,6 +143,7 @@
margin: 0; margin: 0;
background-color: #ffffff; background-color: #ffffff;
border-radius: 8px; border-radius: 8px;
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px; padding: 20px;
@ -172,6 +175,7 @@
margin: 0; margin: 0;
background-color: #ffffff; background-color: #ffffff;
border-radius: 8px; border-radius: 8px;
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px; padding: 20px;
@ -197,6 +201,7 @@
margin: 0; margin: 0;
background-color: #ffffff; background-color: #ffffff;
border-radius: 8px; border-radius: 8px;
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px; padding: 20px;

Loading…
Cancel
Save