Merge branch 'main' of http://39.174.50.29:15300/jiangxucong/dq-sms
commit
488f9df361
@ -0,0 +1,3 @@
|
||||
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.3666 0.194824C13.8289 0.194824 14.2047 0.562877 14.2047 1.01668V13.1826H2.05037V13.5729C2.05037 14.0993 2.46262 14.5295 2.99029 14.5832L3.09759 14.5876H13.9945V15.8052H3.09759C1.8714 15.8052 0.869921 14.8787 0.797366 13.709L0.792969 13.5729V1.01646C0.792969 0.563097 1.1685 0.194824 1.63131 0.194824H13.3664H13.3666ZM3.89086 7.48377C3.89086 9.44628 5.48575 11.0506 7.43375 11.0506C9.38175 11.0506 10.9766 9.44518 10.9766 7.48267H3.89108V7.48377H3.89086ZM7.43397 2.13161C7.07867 2.13161 6.72535 2.16723 6.37115 2.27452V4.62817H5.66252V2.55859C5.01811 2.88612 4.4778 3.38688 4.1023 4.00458C3.7268 4.62228 3.53099 5.33245 3.53688 6.05531H2.82715V6.76899H12.0375V6.05531H11.3302C11.3302 4.52109 10.4795 3.20103 9.20322 2.55859V4.62817H8.4957V2.27562C8.15055 2.17492 7.79238 2.12601 7.43287 2.13051L7.43397 2.13161Z" fill="white" fill-opacity="0.45"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 963 B |
@ -0,0 +1,3 @@
|
||||
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.3666 0.194824C13.8289 0.194824 14.2047 0.562877 14.2047 1.01668V13.1826H2.05037V13.5729C2.05037 14.0993 2.46262 14.5295 2.99029 14.5832L3.09759 14.5876H13.9945V15.8052H3.09759C1.8714 15.8052 0.869921 14.8787 0.797366 13.709L0.792969 13.5729V1.01646C0.792969 0.563097 1.1685 0.194824 1.63131 0.194824H13.3664H13.3666ZM3.89086 7.48377C3.89086 9.44628 5.48575 11.0506 7.43375 11.0506C9.38175 11.0506 10.9766 9.44518 10.9766 7.48267H3.89108V7.48377H3.89086ZM7.43397 2.13161C7.07867 2.13161 6.72535 2.16723 6.37115 2.27452V4.62817H5.66252V2.55859C5.01811 2.88612 4.4778 3.38688 4.1023 4.00458C3.7268 4.62228 3.53099 5.33245 3.53688 6.05531H2.82715V6.76899H12.0375V6.05531H11.3302C11.3302 4.52109 10.4795 3.20103 9.20322 2.55859V4.62817H8.4957V2.27562C8.15055 2.17492 7.79238 2.12601 7.43287 2.13051L7.43397 2.13161Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 943 B |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 122 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.097 8.03631C9.28795 8.84538 9.41235 10.1834 10.128 11.0856C10.2836 11.3036 10.2214 11.428 10.128 11.5525C10.097 11.5836 9.97246 11.7391 9.94127 11.7703C9.81688 11.9571 9.78586 12.237 9.81688 12.3926C9.87908 12.6414 10.128 12.6728 10.3148 12.6728C10.7193 12.7038 11.1549 12.6728 11.5594 12.6728H12.7728C13.0218 12.6728 13.2086 12.6104 13.3019 12.3926C13.3953 12.1437 13.3641 11.9257 13.1774 11.7391L12.9906 11.5524C12.8352 11.3968 12.8352 11.2412 12.9906 11.0856L13.3641 10.6189C13.9242 9.71664 13.7376 8.5654 12.8972 7.8809C12.0573 7.22724 10.8437 7.28961 10.097 8.03631ZM14.9821 13.7308C14.9821 13.2951 14.7953 13.0771 14.3598 13.0771H8.75903C8.32331 13.0771 8.13672 13.2639 8.13672 13.6996V14.3219H14.9821V13.7308Z" fill="white"/>
|
||||
<path d="M9.10177 12.3926C9.07076 12.0503 9.16414 11.677 9.38191 11.3968C9.38191 11.3658 9.41293 11.3658 9.44411 11.3346C9.07059 10.8057 8.85298 10.1834 8.85298 9.56108C8.8218 8.7832 9.10177 8.06752 9.59968 7.53842C10.1286 7.00949 10.8131 6.72952 11.56 6.72952C11.9023 6.72952 12.2445 6.79172 12.5868 6.91628V1.65936C12.5868 1.28584 12.2757 0.974854 11.9023 0.974854H2.56755C2.22522 1.00587 1.91406 1.31702 1.91406 1.65936V13.6995C1.91406 14.073 2.22522 14.384 2.59857 14.384H7.48381V13.7308C7.48381 13.3263 7.6082 13.0151 7.82614 12.7972C8.04392 12.5484 8.38609 12.455 8.79062 12.455C8.884 12.3928 9.00856 12.3928 9.10177 12.3928V12.3926ZM3.87453 4.17977C3.87453 3.96183 4.06129 3.77523 4.27906 3.77523H9.41326C9.63103 3.77523 9.81763 3.96199 9.81763 4.17977C9.81763 4.39754 9.63087 4.58413 9.41309 4.58413H4.27889C4.06112 4.55312 3.87436 4.39737 3.87436 4.17977H3.87453ZM7.82631 7.32232C7.82631 7.54026 7.63955 7.72685 7.42178 7.72685H4.27889C4.06112 7.72685 3.87436 7.54009 3.87436 7.32232C3.87436 7.10454 4.06112 6.91795 4.27889 6.91795H7.42161C7.67057 6.94896 7.82614 7.10471 7.82614 7.32232H7.82631ZM7.82631 5.73553C7.82631 5.95331 7.63955 6.14007 7.42178 6.14007H4.27889C4.06112 6.14007 3.87436 5.95331 3.87436 5.73553C3.87436 5.51776 4.06112 5.331 4.27889 5.331H7.42161C7.67057 5.36218 7.82614 5.51776 7.82614 5.73553H7.82631ZM14.3295 15.224H9.03958C8.884 15.224 8.72842 15.0996 8.72842 14.9129C8.72842 14.7573 8.85298 14.6017 9.03958 14.6017H14.3293C14.4849 14.6017 14.6405 14.7573 14.6405 14.9129C14.6405 15.0996 14.5159 15.224 14.3293 15.224H14.3295Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.5633 10.5654C10.8511 10.2501 9.77943 9.43494 8.21372 9.15469C8.61417 8.72587 8.90992 8.05633 9.2207 7.2628C9.40139 6.80308 9.3653 6.41118 9.3653 5.85227C9.3653 5.44032 9.44266 4.7791 9.33952 4.41556C8.99501 3.18709 8.12092 2.84875 7.09964 2.84875C6.07702 2.84875 5.2036 3.18906 4.8589 4.41834C4.75853 4.78288 4.83564 5.44171 4.83564 5.85227C4.83564 6.41217 4.80537 6.80584 4.98646 7.26579C5.29963 8.06347 5.59888 8.73226 5.99675 9.15967C4.44474 9.44328 3.44071 10.2535 2.73305 10.5678C1.26871 11.2219 1.30477 11.9379 1.30477 11.9379V13.1513L13.0724 13.1491V11.9379C13.0724 11.9379 13.0336 11.2191 11.5633 10.5654ZM11.035 3.1224H15.6894V4.14106H11.035V3.1224ZM11.035 5.3529H13.8804V6.38911H11.035V5.3529Z" fill="white" fill-opacity="0.45"/>
|
||||
<path d="M11.0312 7.58337H15.106V8.61959H11.0312V7.58337Z" fill="white" fill-opacity="0.45"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 950 B |
@ -0,0 +1,4 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.5633 10.5654C10.8511 10.2501 9.77943 9.43494 8.21372 9.15469C8.61417 8.72587 8.90992 8.05633 9.2207 7.2628C9.40139 6.80308 9.3653 6.41118 9.3653 5.85227C9.3653 5.44032 9.44266 4.7791 9.33952 4.41556C8.99501 3.18709 8.12092 2.84875 7.09964 2.84875C6.07702 2.84875 5.2036 3.18906 4.8589 4.41834C4.75853 4.78288 4.83564 5.44171 4.83564 5.85227C4.83564 6.41217 4.80537 6.80584 4.98646 7.26579C5.29963 8.06347 5.59888 8.73226 5.99675 9.15967C4.44474 9.44328 3.44071 10.2535 2.73305 10.5678C1.26871 11.2219 1.30477 11.9379 1.30477 11.9379V13.1513L13.0724 13.1491V11.9379C13.0724 11.9379 13.0336 11.2191 11.5633 10.5654ZM11.035 3.1224H15.6894V4.14106H11.035V3.1224ZM11.035 5.3529H13.8804V6.38911H11.035V5.3529Z" fill="white"/>
|
||||
<path d="M11.0312 7.58337H15.106V8.61959H11.0312V7.58337Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 910 B |
@ -0,0 +1,486 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Button, Card, Row, Col, Input, Select, Space, Tag, Divider } from 'antd';
|
||||
import { SearchOutlined, FilterOutlined, ExportOutlined, LocateOutlined, UserOutlined, ClockCircleOutlined, EnvironmentOutlined }
|
||||
from '@ant-design/icons';
|
||||
import StandardTable from '@/components/StandardTable';
|
||||
import styles from './PersonnelLocation.less';
|
||||
import datadictionary from "@/utils/dataDictionary";
|
||||
import gisTemp from '@/assets/img/gisTemp.svg';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Search } = Input;
|
||||
|
||||
// 模拟数据
|
||||
const mockLocationData = {
|
||||
list: [
|
||||
{
|
||||
id: '001',
|
||||
name: '张三',
|
||||
empId: '10001',
|
||||
position: '管理人员',
|
||||
department: '安全部',
|
||||
location: 'A区安控室',
|
||||
time: '2025-09-03 21:32',
|
||||
status: '正常'
|
||||
},
|
||||
{
|
||||
id: '002',
|
||||
name: '李四',
|
||||
empId: '10002',
|
||||
position: '管理人员',
|
||||
department: '设备部',
|
||||
location: 'A区发电室',
|
||||
time: '2025-09-03 21:30',
|
||||
status: '预警'
|
||||
},
|
||||
{
|
||||
id: '003',
|
||||
name: '王五',
|
||||
empId: '10003',
|
||||
position: '管理人员',
|
||||
department: '设备部',
|
||||
location: 'A区发电室',
|
||||
time: '2025-09-03 21:29',
|
||||
status: '预警'
|
||||
},
|
||||
{
|
||||
id: '004',
|
||||
name: '赵六',
|
||||
empId: '10004',
|
||||
position: '管理人员',
|
||||
department: '设备部',
|
||||
location: 'A区发电室',
|
||||
time: '2025-09-03 21:28',
|
||||
status: '告警'
|
||||
},
|
||||
{
|
||||
id: '005',
|
||||
name: '孙七',
|
||||
empId: '10005',
|
||||
position: '管理人员',
|
||||
department: '设备部',
|
||||
location: 'A区发电室',
|
||||
time: '2025-09-03 21:27',
|
||||
status: '正常'
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
total: 120,
|
||||
current: 1,
|
||||
pageSize: 5,
|
||||
},
|
||||
};
|
||||
|
||||
// 历史轨迹数据
|
||||
const historyTrailData = [
|
||||
{
|
||||
title: '进入危险区域管理',
|
||||
time: '2025-09-03 20:30',
|
||||
location: 'A区发电室'
|
||||
},
|
||||
{
|
||||
title: '长时间停留',
|
||||
time: '2025-09-03 20:15',
|
||||
location: 'A区发电室'
|
||||
},
|
||||
{
|
||||
title: '异常行为',
|
||||
time: '2025-09-03 19:45',
|
||||
location: 'A区发电室'
|
||||
},
|
||||
{
|
||||
title: '工作开始',
|
||||
time: '2025-09-03 08:00',
|
||||
location: 'A区发电室'
|
||||
},
|
||||
];
|
||||
|
||||
// 人员警告信息数据
|
||||
const personnelWarningData = [
|
||||
{
|
||||
title: '电池低电量',
|
||||
time: '2025-09-03 21:35',
|
||||
location: 'A区发电室',
|
||||
level: 'warning'
|
||||
},
|
||||
{
|
||||
title: '信号丢失',
|
||||
time: '2025-09-03 21:30',
|
||||
location: 'B区地下室',
|
||||
level: 'error'
|
||||
},
|
||||
{
|
||||
title: '超过活动范围',
|
||||
time: '2025-09-03 21:25',
|
||||
location: 'C区仓库',
|
||||
level: 'warning'
|
||||
},
|
||||
{
|
||||
title: '静止时间过长',
|
||||
time: '2025-09-03 21:20',
|
||||
location: 'D区会议室',
|
||||
level: 'warning'
|
||||
},
|
||||
];
|
||||
|
||||
class PersonnelLocation extends PureComponent {
|
||||
state = {
|
||||
selectedRows: [],
|
||||
formValues: {},
|
||||
selectedPerson: null,
|
||||
};
|
||||
|
||||
// 表格列定义
|
||||
columns = [
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 100,
|
||||
render: (text) => (
|
||||
<span style={{ fontSize: '12px' }}>{text}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '工号',
|
||||
dataIndex: 'empId',
|
||||
key: 'empId',
|
||||
width: 100,
|
||||
render: (text) => (
|
||||
<span style={{ fontSize: '12px' }}>{text}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '岗位',
|
||||
dataIndex: 'position',
|
||||
key: 'position',
|
||||
width: 120,
|
||||
render: (text) => (
|
||||
<span style={{ fontSize: '12px' }}>{text}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '所属部门',
|
||||
dataIndex: 'department',
|
||||
key: 'department',
|
||||
width: 120,
|
||||
render: (text) => (
|
||||
<span style={{ fontSize: '12px' }}>{text}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '当前位置',
|
||||
dataIndex: 'location',
|
||||
key: 'location',
|
||||
width: 150,
|
||||
render: (text) => (
|
||||
<span style={{ fontSize: '12px' }}>{text}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
dataIndex: 'time',
|
||||
key: 'time',
|
||||
width: 150,
|
||||
render: (text) => (
|
||||
<span style={{ fontSize: '12px' }}>{text}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
render: (text) => {
|
||||
if (text === '正常') {
|
||||
return <Tag className={styles['status-tag'] + ' ' + styles['status-normal']}>{text}</Tag>;
|
||||
} else if (text === '预警') {
|
||||
return <Tag className={styles['status-tag'] + ' ' + styles['status-warning']}>{text}</Tag>;
|
||||
} else {
|
||||
return <Tag className={styles['status-tag'] + ' ' + styles['status-error']}>{text}</Tag>;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 80,
|
||||
fixed: 'right',
|
||||
render: (_, record) => (
|
||||
<a
|
||||
style={{
|
||||
color: 'rgba(36, 114, 214, 1)',
|
||||
fontSize: '12px'
|
||||
}}
|
||||
onClick={() => this.handleLocate(record)}
|
||||
>
|
||||
定位
|
||||
</a>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 获取地图配置选项
|
||||
getMapChartOption = () => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '人员分布',
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
symbolSize: 20,
|
||||
data: [
|
||||
{ name: '张三', value: [116.404, 39.915], itemStyle: { color: '#52C41A' } },
|
||||
{ name: '李四', value: [116.414, 39.925], itemStyle: { color: '#FAAD14' } },
|
||||
{ name: '王五', value: [116.424, 39.935], itemStyle: { color: '#FAAD14' } },
|
||||
{ name: '赵六', value: [116.434, 39.945], itemStyle: { color: '#FF4D4F' } },
|
||||
{ name: '孙七', value: [116.444, 39.955], itemStyle: { color: '#52C41A' } },
|
||||
],
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
geo: {
|
||||
map: 'beijing',
|
||||
label: {
|
||||
show: true
|
||||
},
|
||||
roam: true,
|
||||
itemStyle: {
|
||||
areaColor: '#f0f2f5',
|
||||
borderColor: '#999'
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
areaColor: '#e6f7ff'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 处理表格变化
|
||||
handleTableChange = (pagination, sorter) => {
|
||||
const { dispatch } = this.props;
|
||||
const { formValues } = this.state;
|
||||
|
||||
const params = {
|
||||
currentPage: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
...formValues
|
||||
};
|
||||
|
||||
sorter.field && (params.sorter = `${sorter.field}_${sorter.order}`);
|
||||
};
|
||||
|
||||
// 处理行选择
|
||||
handleSelectRows = rows => {
|
||||
this.setState({
|
||||
selectedRows: rows
|
||||
});
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
handleSearch = values => {
|
||||
this.setState({
|
||||
formValues: values
|
||||
});
|
||||
};
|
||||
|
||||
// 处理定位
|
||||
handleLocate = (record) => {
|
||||
this.setState({
|
||||
selectedPerson: record
|
||||
});
|
||||
// 这里可以添加地图定位的逻辑
|
||||
console.log('定位人员:', record);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
selectedRows,
|
||||
selectedPerson
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Card bordered={false} className={styles['card-container']}>
|
||||
{/* 主要内容区域 - 地图、历史轨迹和人员警告在同一行 */}
|
||||
<Row gutter={16} style={{ marginBottom: 15 }}>
|
||||
{/* 左侧:地图显示(占一半宽度) */}
|
||||
<Col span={12}>
|
||||
<Card title="人员定位管理" className={styles['map-card']}>
|
||||
<div className={styles['map-container']}>
|
||||
{/* 由于缺少实际地图组件,这里使用模拟的地图容器 */}
|
||||
<div className={styles['mock-map']}>
|
||||
<div className={styles['map-legend']}>
|
||||
<div className={styles['legend-item']}>
|
||||
<div className={styles['legend-dot']} style={{ backgroundColor: '#52C41A' }}></div>
|
||||
<span>正常 (45)</span>
|
||||
</div>
|
||||
<div className={styles['legend-item']}>
|
||||
<div className={styles['legend-dot']} style={{ backgroundColor: '#FAAD14' }}></div>
|
||||
<span>预警 (32)</span>
|
||||
</div>
|
||||
<div className={styles['legend-item']}>
|
||||
<div className={styles['legend-dot']} style={{ backgroundColor: '#FF4D4F' }}></div>
|
||||
<span>告警 (12)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['map-content']}>
|
||||
<img
|
||||
src={gisTemp}
|
||||
alt="GIS地图"
|
||||
className={styles['map-image']}
|
||||
/>
|
||||
{/* 地图上的标记点 */}
|
||||
<div className={styles['map-marker']} style={{ top: '15%', left: '25%', backgroundColor: '#52C41A' }}>
|
||||
<span className={styles['marker-tooltip']}>张三 - A区安控室</span>
|
||||
</div>
|
||||
<div className={styles['map-marker']} style={{ top: '30%', left: '40%', backgroundColor: '#FAAD14' }}>
|
||||
<span className={styles['marker-tooltip']}>李四 - A区发电室</span>
|
||||
</div>
|
||||
<div className={styles['map-marker']} style={{ top: '45%', left: '55%', backgroundColor: '#FAAD14' }}>
|
||||
<span className={styles['marker-tooltip']}>王五 - A区发电室</span>
|
||||
</div>
|
||||
<div className={styles['map-marker']} style={{ top: '60%', left: '70%', backgroundColor: '#FF4D4F' }}>
|
||||
<span className={styles['marker-tooltip']}>赵六 - A区发电室</span>
|
||||
</div>
|
||||
<div className={styles['map-marker']} style={{ top: '75%', left: '85%', backgroundColor: '#52C41A' }}>
|
||||
<span className={styles['marker-tooltip']}>孙七 - A区发电室</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 筛选栏 - 移至地图卡片内部 */}
|
||||
<Divider style={{ margin: '16px 0' }} />
|
||||
<div className={styles['table-toolbar-container']}>
|
||||
<Row justify="space-between" align="middle">
|
||||
<Col>
|
||||
<Space>
|
||||
<Search
|
||||
placeholder="搜索姓名、工号..."
|
||||
className={styles['search-input']}
|
||||
onSearch={this.handleSearch}
|
||||
/>
|
||||
<span style={{ marginLeft: 20 }}>安全区域</span>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
className={styles['filter-select']}
|
||||
defaultValue="all"
|
||||
>
|
||||
<Option value="all">全部区域</Option>
|
||||
<Option value="high">高风险</Option>
|
||||
<Option value="medium">中风险</Option>
|
||||
<Option value="low">低风险</Option>
|
||||
</Select>
|
||||
<span style={{ marginLeft: 20 }}>人员类型</span>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
className={styles['filter-select']}
|
||||
defaultValue="all"
|
||||
>
|
||||
<Option value="all">全部人员</Option>
|
||||
<Option value="safety">安全人员</Option>
|
||||
<Option value="manager">管理人员</Option>
|
||||
<Option value="operator">操作维护人员</Option>
|
||||
<Option value="other">其他人员</Option>
|
||||
</Select>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col>
|
||||
<Space>
|
||||
<Button
|
||||
icon={<FilterOutlined />}
|
||||
className={styles['button-style']}
|
||||
>
|
||||
筛选
|
||||
</Button>
|
||||
<Button
|
||||
icon={<ExportOutlined />}
|
||||
className={styles['button-style']}
|
||||
>
|
||||
导出
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* 中间:历史轨迹(占四分之一宽度) */}
|
||||
<Col span={6}>
|
||||
<Card title="历史轨迹追踪" className={styles['trail-card']}>
|
||||
<div className={styles['trail-list']}>
|
||||
{historyTrailData.map((item, index) => (
|
||||
<div key={index} className={styles['trail-item']}>
|
||||
<div className={styles['trail-icon']}>
|
||||
<ClockCircleOutlined />
|
||||
</div>
|
||||
<div className={styles['trail-content']}>
|
||||
<div className={styles['trail-title']}>{item.title}</div>
|
||||
<div className={styles['trail-info']}>
|
||||
<span>{item.time}</span>
|
||||
<span>{item.location}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* 右侧:人员警告信息(占四分之一宽度) */}
|
||||
<Col span={6}>
|
||||
<Card title="人员警告信息" className={styles['warning-card']}>
|
||||
<div className={styles['warning-list']}>
|
||||
{personnelWarningData.map((item, index) => (
|
||||
<div key={index} className={styles['warning-item'] + ' ' + styles[`warning-${item.level}`]}>
|
||||
<div className={styles['warning-icon']}>
|
||||
{item.level === 'error' ? <Tag color="error">严重</Tag> : <Tag color="warning">警告</Tag>}
|
||||
</div>
|
||||
<div className={styles['warning-content']}>
|
||||
<div className={styles['warning-title']}>{item.title}</div>
|
||||
<div className={styles['warning-info']}>
|
||||
<span>{item.time}</span>
|
||||
<span>{item.location}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* 底部表格区域 */}
|
||||
<div className={styles['table-container']}>
|
||||
<Card title="人员实时位置信息" className={styles['location-table-card']}>
|
||||
<StandardTable
|
||||
rowKey={'id'}
|
||||
selectedRows={selectedRows}
|
||||
loading={false}
|
||||
data={mockLocationData}
|
||||
columns={this.columns}
|
||||
onSelectRow={this.handleSelectRows}
|
||||
onChange={this.handleTableChange}
|
||||
scroll={{ x: 1200 }}
|
||||
showTotal={(total, range) => `共 ${total} 条`}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PersonnelLocation;
|
||||
@ -0,0 +1,452 @@
|
||||
// Ant Design 5.x不再需要导入less主题文件
|
||||
// 直接定义我们需要的主题变量
|
||||
@primary-color: rgba(36, 114, 214, 1); // 与全局样式保持一致
|
||||
@success-color: #52c41a;
|
||||
@warning-color: #faad14;
|
||||
@error-color: #ff4d4f;
|
||||
@bg-color: #f7f8fa;
|
||||
@card-bg: #ffffff;
|
||||
@border-color: #e8e8e8;
|
||||
@text-primary: #333333;
|
||||
@text-secondary: #666666;
|
||||
@text-tertiary: #999999;
|
||||
|
||||
// 阴影效果
|
||||
@shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
@shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
@shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
|
||||
// 动画效果
|
||||
@transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@transition-hover: all 0.2s ease-in-out;
|
||||
|
||||
.card-container {
|
||||
padding: 16px;
|
||||
background-color: @bg-color;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// 顶部工具栏样式
|
||||
.table-toolbar-container {
|
||||
padding: 12px 16px;
|
||||
background-color: @card-bg;
|
||||
border-radius: 8px;
|
||||
box-shadow: @shadow-sm;
|
||||
}
|
||||
|
||||
// 搜索和筛选控件样式
|
||||
.search-input {
|
||||
width: 240px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
transition: @transition-hover;
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 140px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
transition: @transition-hover;
|
||||
}
|
||||
|
||||
// 按钮样式
|
||||
.button-style {
|
||||
margin-left: 12px;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
transition: @transition-hover;
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: @shadow-md;
|
||||
}
|
||||
}
|
||||
|
||||
// 地图卡片样式
|
||||
.map-card {
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: @shadow-md;
|
||||
transition: @transition-hover;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&:hover {
|
||||
box-shadow: @shadow-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.map-container {
|
||||
height: calc(100% - 120px);
|
||||
}
|
||||
|
||||
.mock-map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #f0f2f5 0%, #e6f7ff 100%);
|
||||
border-radius: 0 0 8px 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 地图图例样式
|
||||
.map-legend {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
box-shadow: @shadow-md;
|
||||
z-index: 10;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
color: @text-secondary;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.legend-dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
// 地图内容样式
|
||||
.map-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.map-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
|
||||
// 地图标记点样式
|
||||
.map-marker {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transform: translate(-50%, -50%);
|
||||
box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.9);
|
||||
transition: @transition-hover;
|
||||
&:hover {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.95);
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
|
||||
// 标记点提示样式
|
||||
.marker-tooltip {
|
||||
position: absolute;
|
||||
top: -32px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
display: none;
|
||||
animation: fadeIn 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(5px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.map-marker:hover .marker-tooltip {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// 统计卡片样式
|
||||
.stats-card {
|
||||
margin-bottom: 16px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: @shadow-md;
|
||||
transition: @transition-hover;
|
||||
&:hover {
|
||||
box-shadow: @shadow-lg;
|
||||
}
|
||||
}
|
||||
|
||||
// 历史轨迹卡片样式
|
||||
.trail-card {
|
||||
height: 100%;
|
||||
min-height: 240px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: @shadow-md;
|
||||
transition: @transition-hover;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&:hover {
|
||||
box-shadow: @shadow-lg;
|
||||
}
|
||||
}
|
||||
|
||||
// 人员警告卡片样式
|
||||
.warning-card {
|
||||
height: 100%;
|
||||
min-height: 240px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: @shadow-md;
|
||||
transition: @transition-hover;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&:hover {
|
||||
box-shadow: @shadow-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.trail-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
// 轨迹项样式
|
||||
.trail-item {
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: @transition-hover;
|
||||
&:hover {
|
||||
background-color: #fafafa;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid @primary-color;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.trail-icon {
|
||||
margin-right: 12px;
|
||||
color: @primary-color;
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.trail-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.trail-title {
|
||||
font-size: 14px;
|
||||
color: @text-primary;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.trail-info {
|
||||
font-size: 12px;
|
||||
color: @text-tertiary;
|
||||
}
|
||||
|
||||
.trail-info span {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
// 表格容器样式
|
||||
.table-container {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
// 位置表格卡片样式
|
||||
.location-table-card {
|
||||
min-height: 400px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: @shadow-md;
|
||||
transition: @transition-hover;
|
||||
&:hover {
|
||||
box-shadow: @shadow-lg;
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动条样式优化
|
||||
.trail-list::-webkit-scrollbar,
|
||||
.warning-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.trail-list::-webkit-scrollbar-track,
|
||||
.warning-list::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.trail-list::-webkit-scrollbar-thumb,
|
||||
.warning-list::-webkit-scrollbar-thumb {
|
||||
background: #d9d9d9;
|
||||
border-radius: 3px;
|
||||
transition: @transition-hover;
|
||||
&:hover {
|
||||
background: #bfbfbf;
|
||||
}
|
||||
}
|
||||
|
||||
// Ant Design 表格样式覆盖
|
||||
.ant-table {
|
||||
.ant-table-thead > tr > th {
|
||||
background-color: #fafafa;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
.ant-table-tbody > tr:hover > td {
|
||||
background-color: #f5f8ff !important;
|
||||
}
|
||||
.ant-table-pagination {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
// 状态标签样式
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
&.status-normal {
|
||||
background-color: #f6ffed;
|
||||
color: @success-color;
|
||||
border: 1px solid #b7eb8f;
|
||||
}
|
||||
&.status-warning {
|
||||
background-color: #fff7e6;
|
||||
color: @warning-color;
|
||||
border: 1px solid #ffd591;
|
||||
}
|
||||
&.status-error {
|
||||
background-color: #fff1f0;
|
||||
color: @error-color;
|
||||
border: 1px solid #ffccc7;
|
||||
}
|
||||
}
|
||||
|
||||
// 人员警告列表样式
|
||||
.warning-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
// 警告项样式
|
||||
.warning-item {
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: @transition-hover;
|
||||
&:hover {
|
||||
background-color: #fafafa;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid @primary-color;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&.warning-warning {
|
||||
border-left: 3px solid @warning-color;
|
||||
}
|
||||
&.warning-error {
|
||||
border-left: 3px solid @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.warning-title {
|
||||
font-size: 14px;
|
||||
color: @text-primary;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.warning-info {
|
||||
font-size: 12px;
|
||||
color: @text-tertiary;
|
||||
}
|
||||
|
||||
.warning-info span {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
// 响应式布局适配
|
||||
@media (max-width: 1200px) {
|
||||
.search-input {
|
||||
width: 200px;
|
||||
}
|
||||
.filter-select {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card-container {
|
||||
padding: 8px;
|
||||
}
|
||||
.table-toolbar-container {
|
||||
padding: 12px 8px;
|
||||
}
|
||||
.search-input {
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.filter-select {
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.button-style {
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.map-card {
|
||||
min-height: 300px;
|
||||
}
|
||||
.trail-card {
|
||||
height: auto;
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,501 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Table, Card, Row, Col, Tag, Button, Statistic, Select, Input, Space } from 'antd';
|
||||
import { SearchOutlined, CheckOutlined, ExclamationCircleOutlined, FileTextOutlined, BarChartOutlined, BellOutlined, AlertOutlined } from '@ant-design/icons';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
import styles from './SpecialOperationPermit.less';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Column } = Table;
|
||||
|
||||
// 模拟数据 - 统计卡片数据
|
||||
const statsData = [
|
||||
{
|
||||
title: '作业总数',
|
||||
count: 126,
|
||||
icon: <FileTextOutlined />,
|
||||
status: '正常',
|
||||
color: '#52c41a'
|
||||
},
|
||||
{
|
||||
title: '技能合格率',
|
||||
count: '23%',
|
||||
icon: <CheckOutlined />,
|
||||
status: '待提升',
|
||||
color: '#ff7875'
|
||||
},
|
||||
{
|
||||
title: '作业完成率',
|
||||
count: '67%',
|
||||
icon: <BarChartOutlined />,
|
||||
status: '正常',
|
||||
color: '#52c41a'
|
||||
},
|
||||
{
|
||||
title: '风险防控数',
|
||||
count: 16,
|
||||
icon: <BellOutlined />,
|
||||
status: '较上月持平',
|
||||
color: '#1890ff'
|
||||
}
|
||||
];
|
||||
|
||||
// 模拟数据 - 趋势分析图表
|
||||
const trendAnalysisData = {
|
||||
title: {
|
||||
text: '趋势分析',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
legend: {
|
||||
data: ['技能合格率', '作业完成率', '风险防控数'],
|
||||
bottom: 0,
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['10-01', '10-02', '10-03', '10-04', '10-05', '10-06', '10-07'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
max: 100,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '技能合格率',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
data: [25, 23, 28, 26, 30, 23, 25],
|
||||
itemStyle: { color: '#ff7a75' },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0, color: 'rgba(255, 122, 117, 0.8)'
|
||||
}, {
|
||||
offset: 1, color: 'rgba(255, 122, 117, 0.1)'
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '作业完成率',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
data: [65, 68, 70, 65, 62, 67, 69],
|
||||
itemStyle: { color: '#9254de' },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0, color: 'rgba(146, 84, 222, 0.8)'
|
||||
}, {
|
||||
offset: 1, color: 'rgba(146, 84, 222, 0.1)'
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '风险防控数',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
data: [15, 18, 16, 14, 17, 16, 19],
|
||||
itemStyle: { color: '#40a9ff' },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0, color: 'rgba(64, 169, 255, 0.8)'
|
||||
}, {
|
||||
offset: 1, color: 'rgba(64, 169, 255, 0.1)'
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
// 模拟数据 - 作业类型分布图表
|
||||
const operationTypeData = {
|
||||
title: {
|
||||
text: '作业类型分布',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: ['动火作业', '高处作业', '有限空间作业', '临时用电', '断路作业'],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '作业类型',
|
||||
type: 'pie',
|
||||
radius: '60%',
|
||||
data: [
|
||||
{
|
||||
value: 35,
|
||||
name: '动火作业',
|
||||
itemStyle: { color: '#1890ff' }
|
||||
},
|
||||
{
|
||||
value: 25,
|
||||
name: '高处作业',
|
||||
itemStyle: { color: '#52c41a' }
|
||||
},
|
||||
{
|
||||
value: 20,
|
||||
name: '有限空间作业',
|
||||
itemStyle: { color: '#faad14' }
|
||||
},
|
||||
{
|
||||
value: 15,
|
||||
name: '临时用电',
|
||||
itemStyle: { color: '#f5222d' }
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
name: '断路作业',
|
||||
itemStyle: { color: '#722ed1' }
|
||||
}
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 模拟数据 - 作业列表数据
|
||||
const operationListData = [
|
||||
{
|
||||
key: '1',
|
||||
operationType: '动火作业',
|
||||
operationNo: 'DH-20230906-002',
|
||||
applicant: '张小龙',
|
||||
location: '2号车间',
|
||||
startTime: '2025-09-03 03:23:12',
|
||||
warningDept: '生产一部',
|
||||
warningType: '气体浓度超标',
|
||||
riskLevel: '高风险',
|
||||
status: '进行中',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
operationType: '高处作业',
|
||||
operationNo: 'GC-20230906-002',
|
||||
applicant: '李小龙',
|
||||
location: '2号车间',
|
||||
startTime: '2025-09-03 03:23:12',
|
||||
warningDept: '生产一部',
|
||||
warningType: '气体浓度超标',
|
||||
riskLevel: '较高风险',
|
||||
status: '未开始',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
operationType: '有限空间作业',
|
||||
operationNo: 'YX-20230906-002',
|
||||
applicant: '张小虎',
|
||||
location: '2号车间',
|
||||
startTime: '2025-09-03 03:23:12',
|
||||
warningDept: '生产一部',
|
||||
warningType: '气体浓度超标',
|
||||
riskLevel: '中风险',
|
||||
status: '未开始',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
operationType: '临时用电',
|
||||
operationNo: 'LS-20230906-002',
|
||||
applicant: '张小龙',
|
||||
location: '2号车间',
|
||||
startTime: '2025-09-03 03:23:12',
|
||||
warningDept: '生产一部',
|
||||
warningType: '气体浓度超标',
|
||||
riskLevel: '低风险',
|
||||
status: '进行中',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
operationType: '动火作业',
|
||||
operationNo: 'DH-20230906-002',
|
||||
applicant: '张小龙',
|
||||
location: '2号车间',
|
||||
startTime: '2025-09-03 03:23:12',
|
||||
warningDept: '生产一部',
|
||||
warningType: '气体浓度超标',
|
||||
riskLevel: '高风险',
|
||||
status: '进行中',
|
||||
},
|
||||
];
|
||||
|
||||
// 状态标签渲染
|
||||
const renderStatusTag = (status) => {
|
||||
switch (status) {
|
||||
case '进行中':
|
||||
return <Tag color="blue">{status}</Tag>;
|
||||
case '未开始':
|
||||
return <Tag color="default">{status}</Tag>;
|
||||
case '已完成':
|
||||
return <Tag color="green">{status}</Tag>;
|
||||
case '已暂停':
|
||||
return <Tag color="orange">{status}</Tag>;
|
||||
default:
|
||||
return <Tag>{status}</Tag>;
|
||||
}
|
||||
};
|
||||
|
||||
// 风险等级标签渲染
|
||||
const renderRiskLevelTag = (level) => {
|
||||
switch (level) {
|
||||
case '高风险':
|
||||
return <Tag color="red">{level}</Tag>;
|
||||
case '较高风险':
|
||||
return <Tag color="orange">{level}</Tag>;
|
||||
case '中风险':
|
||||
return <Tag color="yellow">{level}</Tag>;
|
||||
case '低风险':
|
||||
return <Tag color="green">{level}</Tag>;
|
||||
default:
|
||||
return <Tag>{level}</Tag>;
|
||||
}
|
||||
};
|
||||
|
||||
const SpecialOperationPermit = () => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [filteredData, setFilteredData] = useState(operationListData);
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = (value) => {
|
||||
setSearchText(value);
|
||||
const filtered = operationListData.filter(item =>
|
||||
item.operationNo.includes(value) ||
|
||||
item.applicant.includes(value) ||
|
||||
item.location.includes(value)
|
||||
);
|
||||
setFilteredData(filtered);
|
||||
setCurrentPage(1);
|
||||
};
|
||||
|
||||
// 处理操作按钮
|
||||
const handleViewDetails = (record) => {
|
||||
// 查看详情逻辑
|
||||
console.log('查看详情:', record);
|
||||
};
|
||||
|
||||
const handleApprove = (record) => {
|
||||
// 审批逻辑
|
||||
console.log('审批:', record);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{/* 顶部统计卡片 */}
|
||||
<Row gutter={16} className={styles.statsRow}>
|
||||
{statsData.map((stat, index) => (
|
||||
<Col key={index} xs={24} sm={12} md={6}>
|
||||
<Card className={styles.statCard}>
|
||||
<div className={styles.statContent}>
|
||||
<div className={styles.statIcon}>
|
||||
{React.cloneElement(stat.icon, { style: { color: stat.color } })}
|
||||
</div>
|
||||
<div className={styles.statInfo}>
|
||||
<div className={styles.statTitle}>{stat.title}</div>
|
||||
<div className={styles.statCount}>{stat.count}</div>
|
||||
<div className={styles.statStatus} style={{ color: stat.color }}>{stat.status}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
|
||||
{/* 中间图表区域 */}
|
||||
<Row gutter={16} className={styles.chartRow}>
|
||||
{/* 监控预警 */}
|
||||
<Col xs={24} md={6} lg={6}>
|
||||
<Card title="监控预警" className={styles.chartCard}>
|
||||
<div className={styles.alarmList}>
|
||||
<div className={styles.alarmItem}>
|
||||
<div className={styles.alarmIcon} style={{ backgroundColor: '#FF4D4F' }}>
|
||||
<AlertOutlined style={{ color: '#fff' }} />
|
||||
</div>
|
||||
<div className={styles.alarmInfo}>
|
||||
<div className={styles.alarmTitle}>高风险气体超标</div>
|
||||
<div className={styles.alarmDesc}>测试气体浓度超出安全标准值25.0%</div>
|
||||
<div className={styles.alarmTime}>5分钟前</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.alarmItem}>
|
||||
<div className={styles.alarmIcon} style={{ backgroundColor: '#FF7A45' }}>
|
||||
<AlertOutlined style={{ color: '#fff' }} />
|
||||
</div>
|
||||
<div className={styles.alarmInfo}>
|
||||
<div className={styles.alarmTitle}>高温作业防护缺失</div>
|
||||
<div className={styles.alarmDesc}>作业人员未按规定佩戴防护装备</div>
|
||||
<div className={styles.alarmTime}>30分钟前</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.alarmItem}>
|
||||
<div className={styles.alarmIcon} style={{ backgroundColor: '#FFC53D' }}>
|
||||
<AlertOutlined style={{ color: '#fff' }} />
|
||||
</div>
|
||||
<div className={styles.alarmInfo}>
|
||||
<div className={styles.alarmTitle}>受限空间超时</div>
|
||||
<div className={styles.alarmDesc}>作业人员在受限空间内停留超过规定时间</div>
|
||||
<div className={styles.alarmTime}>60分钟前</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.alarmItem}>
|
||||
<div className={styles.alarmIcon} style={{ backgroundColor: '#1890FF' }}>
|
||||
<AlertOutlined style={{ color: '#fff' }} />
|
||||
</div>
|
||||
<div className={styles.alarmInfo}>
|
||||
<div className={styles.alarmTitle}>受限空间作业超时</div>
|
||||
<div className={styles.alarmDesc}>受限空间作业已超规定时间30分钟</div>
|
||||
<div className={styles.alarmTime}>90分钟前</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* 趋势分析图表 */}
|
||||
<Col xs={24} md={18} lg={8}>
|
||||
<Card title="趋势分析" className={styles.chartCard}>
|
||||
<ReactECharts option={trendAnalysisData} className={styles.chart} />
|
||||
</Card>
|
||||
</Col>
|
||||
{/* 作业类型分布图表 */}
|
||||
<Col xs={24} md={24} lg={10}>
|
||||
<Card title="作业类型分布" className={styles.chartCard}>
|
||||
<ReactECharts option={operationTypeData} className={styles.chart} />
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* 作业列表区域 */}
|
||||
<Card title="作业管理列表" className={styles.tableCard}>
|
||||
{/* 搜索和筛选 */}
|
||||
<div className={styles.searchFilter}>
|
||||
<Space>
|
||||
<Input
|
||||
placeholder="搜索作业编号、申请人、作业地点..."
|
||||
prefix={<SearchOutlined />}
|
||||
allowClear
|
||||
style={{ width: 300 }}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
/>
|
||||
<Select placeholder="作业类型" style={{ width: 120 }}>
|
||||
<Option value="all">全部</Option>
|
||||
<Option value="dh">动火作业</Option>
|
||||
<Option value="gc">高处作业</Option>
|
||||
<Option value="yx">有限空间作业</Option>
|
||||
<Option value="ls">临时用电</Option>
|
||||
<Option value="dl">断路作业</Option>
|
||||
</Select>
|
||||
<Select placeholder="状态" style={{ width: 100 }}>
|
||||
<Option value="all">全部</Option>
|
||||
<Option value="progress">进行中</Option>
|
||||
<Option value="not-started">未开始</Option>
|
||||
<Option value="completed">已完成</Option>
|
||||
<Option value="paused">已暂停</Option>
|
||||
</Select>
|
||||
<Select placeholder="风险等级" style={{ width: 100 }}>
|
||||
<Option value="all">全部</Option>
|
||||
<Option value="high">高风险</Option>
|
||||
<Option value="medium-high">较高风险</Option>
|
||||
<Option value="medium">中风险</Option>
|
||||
<Option value="low">低风险</Option>
|
||||
</Select>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
{/* 作业列表表格 */}
|
||||
<Table
|
||||
dataSource={filteredData}
|
||||
pagination={{
|
||||
current: currentPage,
|
||||
pageSize: pageSize,
|
||||
total: filteredData.length,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total, range) => `共 ${total} 条 ${range[0]}-${range[1]}`
|
||||
}}
|
||||
rowKey="key"
|
||||
size="small"
|
||||
className={styles.operationTable}
|
||||
>
|
||||
<Column title="作业类型" dataIndex="operationType" key="operationType" width={100} />
|
||||
<Column title="作业编号" dataIndex="operationNo" key="operationNo" width={150} />
|
||||
<Column title="申请人" dataIndex="applicant" key="applicant" width={80} />
|
||||
<Column title="作业地点" dataIndex="location" key="location" width={100} />
|
||||
<Column title="开始时间" dataIndex="startTime" key="startTime" width={160} />
|
||||
<Column title="预警部门" dataIndex="warningDept" key="warningDept" width={100} />
|
||||
<Column title="预警类型" dataIndex="warningType" key="warningType" width={120} />
|
||||
<Column
|
||||
title="风险等级"
|
||||
dataIndex="riskLevel"
|
||||
key="riskLevel"
|
||||
width={80}
|
||||
render={(text) => renderRiskLevelTag(text)}
|
||||
/>
|
||||
<Column
|
||||
title="状态"
|
||||
dataIndex="status"
|
||||
key="status"
|
||||
width={80}
|
||||
render={(text) => renderStatusTag(text)}
|
||||
/>
|
||||
<Column
|
||||
title="操作"
|
||||
key="action"
|
||||
width={120}
|
||||
fixed="right"
|
||||
render={(text, record) => (
|
||||
<Space size="middle">
|
||||
<Button size="small" type="link" onClick={() => handleViewDetails(record)}>查看</Button>
|
||||
<Button size="small" type="link" onClick={() => handleApprove(record)}>审批</Button>
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpecialOperationPermit;
|
||||
@ -0,0 +1,207 @@
|
||||
/* 主容器样式 */
|
||||
.container {
|
||||
padding: 16px;
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
.statsRow {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.statCard {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e8e8e8;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
height: 100%;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
.statContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.statIcon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #e6f7ff;
|
||||
border-radius: 4px;
|
||||
font-size: 24px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.statInfo {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.statTitle {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.statCount {
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.statStatus {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 图表区域样式 */
|
||||
.chartRow {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.chartCard {
|
||||
margin-bottom: 16px;
|
||||
height: 360px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 监控预警样式 */
|
||||
.alarmList {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #d9d9d9;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.alarmItem {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 8px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: #f0f0f0;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.alarmIcon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.alarmInfo {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.alarmTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.alarmDesc {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.alarmTime {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* 表格区域样式 */
|
||||
.tableCard {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.searchFilter {
|
||||
margin-bottom: 16px;
|
||||
padding-top: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.operationTable {
|
||||
.ant-table-thead > tr > th {
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr > td {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.searchFilter {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.searchFilter > * {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 250px;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue