智能巡检——机器人智能巡检——数据分析巡检管理页面

main
yupeng 1 day ago
parent 3d70b254f9
commit b6d3019c06

@ -0,0 +1,483 @@
import { useState } from "react";
import styles from './InspectionTaskPlan.less';
import { Button, Card, Col, Row, Table, Tabs, Input, Space, Modal, Tag, Tree } from "antd";
import { SearchOutlined, DownloadOutlined, PlusOutlined, DownOutlined, RightOutlined, ReloadOutlined } from "@ant-design/icons";
// 自定义树节点图标
const TreeNode = Tree.TreeNode;
// 设备树数据
const deviceTreeData = [
{
title: "407输送机",
key: "407",
expanded: true,
children: [
{
title: "托辊组 (600)",
key: "407_rollers",
children: [
{ title: "1#托辊组", key: "407_roller_1" },
{ title: "2#托辊组", key: "407_roller_2" },
{ title: "3#托辊组", key: "407_roller_3" },
{ title: "4#托辊组", key: "407_roller_4" }
]
},
{
title: "电机 (2)",
key: "407_motors",
children: [
{ title: "1#电机", key: "407_motor_1" },
{ title: "2#电机", key: "407_motor_2" }
]
},
{
title: "减速器 (2)",
key: "407_reducers",
children: [
{ title: "1#减速器", key: "407_reducer_1" },
{ title: "2#减速器", key: "407_reducer_2" }
]
},
{ title: "滚筒 (2)", key: "407_drums" },
{ title: "输送带", key: "407_belt" },
{ title: "物料溜槽", key: "407_chute" }
]
},
{
title: "MA501输送机",
key: "MA501",
expanded: false
},
{
title: "PM105输送机",
key: "PM105",
expanded: false
}
];
const { TabPane } = Tabs;
const { Search } = Input;
// 数据分析巡检管理组件
const DataAnalysisInspectionManagement = () => {
const [activeKey, setActiveKey] = useState('1');
const [query, setQuery] = useState('');
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [detailVisible, setDetailVisible] = useState(false);
const [currentRecord, setCurrentRecord] = useState(null);
const [expandedKeys, setExpandedKeys] = useState(['407', '407_rollers', '407_motors', '407_reducers']);
const [selectedKeys, setSelectedKeys] = useState(['407_roller_1']);
// 处理树节点展开/折叠
const onExpand = (expandedKeysValue) => {
setExpandedKeys(expandedKeysValue);
};
// 处理树节点选择
const onSelect = (selectedKeysValue) => {
setSelectedKeys(selectedKeysValue);
};
// 渲染设备树
const renderDeviceTree = () => {
const renderTreeNodes = (data) => {
return data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode title={item.title} key={item.key} dataRef={item} />;
});
};
return (
<div className={styles.deviceTreeContainer}>
<h3>设备层级导航</h3>
<Tree
expandedKeys={expandedKeys}
selectedKeys={selectedKeys}
onExpand={onExpand}
onSelect={onSelect}
showIcon={false}
>
{renderTreeNodes(deviceTreeData)}
</Tree>
</div>
);
};
// 表格列配置
const columns = [
{
title: '序号',
dataIndex: 'id',
key: 'id',
width: 80,
align: 'center',
},
{
title: '设备/部件',
dataIndex: 'device',
key: 'device',
width: 150,
render: (text) => <span style={{ color: '#006665' }}>{text}</span>,
},
{
title: '运行时长',
dataIndex: 'duration',
key: 'duration',
width: 120,
align: 'center',
},
{
title: '异常类型',
dataIndex: 'type',
key: 'type',
width: 120,
align: 'center',
render: (t) => {
let color = 'green';
if (t === '温度预警') color = 'orange';
if (t === '异常') color = 'red';
return <Tag color={color}>{t}</Tag>;
},
},
{
title: '环境温度',
dataIndex: 'temperature',
key: 'temperature',
width: 120,
align: 'center',
},
{
title: '处理状态',
dataIndex: 'status',
key: 'status',
width: 120,
align: 'center',
render: (status) => {
let color = '#006665';
if (status === '待检修') color = '#ff6600';
return <span style={{ color, fontWeight: 'bold' }}>{status}</span>;
},
},
];
// 表格数据(示例)
const initialData = [
{
key: 1,
id: 1,
device: '407输送机1#托辊组',
duration: '300小时',
type: '无异常',
temperature: '25.5℃',
status: '正常',
},
{
key: 2,
id: 2,
device: '407输送机1#电机',
duration: '280小时',
type: '温度预警',
temperature: '48℃',
status: '待检修',
},
{
key: 3,
id: 3,
device: '407输送机2#托辊组',
duration: '290小时',
type: '无异常',
temperature: '26.2℃',
status: '正常',
},
{
key: 4,
id: 4,
device: '407输送机2#电机',
duration: '275小时',
type: '无异常',
temperature: '45℃',
status: '正常',
},
{
key: 5,
id: 5,
device: '407输送机3#托辊组',
duration: '285小时',
type: '无异常',
temperature: '25.8℃',
status: '正常',
},
{
key: 6,
id: 6,
device: '407输送机1#减速器',
duration: '310小时',
type: '无异常',
temperature: '32℃',
status: '正常',
},
{
key: 7,
id: 7,
device: '407输送机2#减速器',
duration: '305小时',
type: '无异常',
temperature: '33℃',
status: '正常',
},
{
key: 8,
id: 8,
device: '407输送机滚筒',
duration: '315小时',
type: '无异常',
temperature: '28℃',
status: '正常',
},
{
key: 9,
id: 9,
device: '407输送机输送带',
duration: '320小时',
type: '无异常',
temperature: '27℃',
status: '正常',
},
{
key: 10,
id: 10,
device: '407输送物料溜槽',
duration: '300小时',
type: '无异常',
temperature: '26℃',
status: '正常',
},
];
const [data, setData] = useState(initialData);
function openDetail(record) {
setCurrentRecord(record);
setDetailVisible(true);
}
function closeDetail() {
setDetailVisible(false);
setCurrentRecord(null);
}
function handleSearch(value) {
setQuery(value);
// 简单本地过滤示例 — 实际请接后端
const filtered = initialData.filter((r) => r.device.includes(value) || String(r.id) === value);
setData(filtered);
setPage(1);
}
function handleExport() {
// 简单 CSV 导出示例
const headers = ['id', 'device', 'duration', 'type', 'temperature', 'status'];
const rows = data.map((r) => headers.map((h) => (r[h] ?? '')).join(','));
const csv = [headers.join(','), ...rows].join('\n');
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'data_analysis_export.csv';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
return (
<div className={styles['data-analysis-container']}>
<Row gutter={16}>
{/* 左侧设备导航树 */}
<Col xs={24} sm={24} md={6} lg={6} xl={5}>
<Card className={styles.deviceTreeCard}>
{renderDeviceTree()}
</Card>
</Col>
{/* 右侧主要内容区域 */}
<Col xs={24} sm={24} md={18} lg={18} xl={19}>
{/* 设备拓扑和环境监测区域 */}
<Row gutter={16} style={{ marginBottom: 16 }}>
{/* 设备拓扑可视化 */}
<Col xs={24} sm={24} md={12} lg={12} xl={12}>
<Card className={styles.topologyContainer} title="设备拓扑可视化">
<div className={styles.topologyContent}>
<div className={styles.topologyInfo}>
<div className={styles.statusIndicators}>
<div className={styles.statusItem}>
<div className={styles.statusDot} style={{ backgroundColor: '#00ff00' }}></div>
<span>正常</span>
</div>
<div className={styles.statusItem}>
<div className={styles.statusDot} style={{ backgroundColor: '#ffff00' }}></div>
<span>预警</span>
</div>
<div className={styles.statusItem}>
<div className={styles.statusDot} style={{ backgroundColor: '#ff0000' }}></div>
<span>异常</span>
</div>
</div>
<div className={styles.deviceInfo}>
<p>407/输送机</p>
<p>长度 1900mm宽度 2500mm</p>
</div>
</div>
<div className={styles.topologyImage}>
{/* 设备拓扑图占位 */}
<div className={styles.conveyorBelt}></div>
</div>
<div className={styles.topologyControls}>
<Button size="small" className={styles.controlBtn}>+</Button>
<Button size="small" className={styles.controlBtn}>-</Button>
<Button size="small" className={styles.controlBtn}></Button>
</div>
</div>
</Card>
</Col>
{/* 巡检区域环境监测 */}
<Col xs={24} sm={24} md={12} lg={12} xl={12}>
<Card className={styles.environmentContainer} title="巡检区域环境">
<div className={styles.environmentContent}>
<div className={styles.dashboardMetrics}>
<div className={styles.metricItem}>
<div className={styles.metricLabel}>温度</div>
<div className={styles.metricValue}>26°C</div>
</div>
<div className={styles.metricItem}>
<div className={styles.metricLabel}>湿度</div>
<div className={styles.metricValue}>55%RH</div>
</div>
<div className={styles.metricItem}>
<div className={styles.metricLabel}>甲烷</div>
<div className={styles.metricValue}>0.0%</div>
</div>
<div className={styles.metricItem}>
<div className={styles.metricLabel}>一氧化碳</div>
<div className={styles.metricValue}>4.0ppm</div>
</div>
<div className={styles.metricItem}>
<div className={styles.metricLabel}>粉尘(PM2.5)</div>
<div className={styles.metricValue}>16μg/</div>
</div>
</div>
<div className={styles.thermalImageContainer}>
{/* 热成像图占位 */}
<div className={styles.thermalImage}></div>
<div className={styles.thermalControls}>
<Button size="small" className={styles.controlBtn}>+</Button>
<Button size="small" className={styles.controlBtn}>-</Button>
</div>
</div>
</div>
</Card>
</Col>
</Row>
<Card className={styles.dashboardContainer} style={{ marginBottom: 16 }}>
<Row gutter={16} align="middle">
<Col flex="320px">
<Space>
<Search
placeholder="请输入设备名称或编号"
onSearch={handleSearch}
enterButton={<SearchOutlined />}
allowClear
style={{ width: 320 }}
className={styles.searchInput}
/>
<Button type="primary" icon={<PlusOutlined />} className={styles.addBtn} disabled>新增</Button>
<Button type="link" icon={<ReloadOutlined />} onClick={() => { }} className={styles.refreshBtn}>重置</Button>
</Space>
</Col>
<Col flex="auto" style={{ textAlign: 'right' }}>
<div className={styles.dataUpdateTime}>数据更新时间2025-12-15 10:00</div>
</Col>
</Row>
</Card>
<Tabs activeKey={activeKey} onChange={setActiveKey}>
<TabPane tab="历史数据" key="1">
<Card className={styles.thresholdConfigContainer} bordered={false}>
<Table
columns={columns}
dataSource={data}
rowKey="key"
pagination={{
current: page,
pageSize,
total: 85,
showSizeChanger: true,
showTotal: (total) => `${total}`,
pageSizeOptions: ['10', '20', '50'],
onChange: (p, ps) => {
setPage(p);
setPageSize(ps);
},
// 确保分页样式与界面一致
className: styles.tablePagination
}}
bordered
// 添加表格样式
className={styles.historyTable}
// 行悬停效果
onRow={(record) => ({
onClick: () => openDetail(record),
style: {
cursor: 'pointer',
'&:hover': {
backgroundColor: 'rgba(0, 102, 101, 0.05)'
}
}
})}
/>
</Card>
</TabPane>
<TabPane tab="实时监控" key="2">
<Card className={styles.chartContainer} bordered={false} style={{ minHeight: 240 }}>
<h3>实时数据监控</h3>
<p>显示关键指标趋势图和告警列表此处为占位</p>
<div style={{ display: 'flex', gap: 16, marginTop: 12 }}>
<div style={{ flex: 1, minHeight: 120, background: '#fff', borderRadius: 6, padding: 12 }}>图表占位</div>
<div style={{ width: 320, minHeight: 120, background: '#fff', borderRadius: 6, padding: 12 }}>告警列表占位</div>
</div>
</Card>
</TabPane>
</Tabs>
</Col>
</Row>
<Modal visible={detailVisible} title="巡检详情" onCancel={closeDetail} footer={null} width={720}>
{currentRecord ? (
<div>
<p><strong>设备</strong>{currentRecord.device}</p>
<p><strong>运行时长</strong>{currentRecord.duration}</p>
<p><strong>异常类型</strong>{currentRecord.type}</p>
<p><strong>环境温度</strong>{currentRecord.temperature}</p>
<p><strong>处理状态</strong>{currentRecord.status}</p>
<div style={{ marginTop: 12, padding: 12, background: '#fafafa', borderRadius: 4 }}>趋势图/更多信息占位</div>
</div>
) : null}
</Modal>
</div>
);
};
export default DataAnalysisInspectionManagement;

@ -250,3 +250,317 @@
-7px 17px 18px 0px rgba(145, 145, 145, 0.04), -7px 17px 18px 0px rgba(145, 145, 145, 0.04),
-15px 37px 24px 0px rgba(145, 145, 145, 0.03); -15px 37px 24px 0px rgba(145, 145, 145, 0.03);
} }
// 设备树样式
.deviceTreeCard {
height: calc(100vh - 200px);
overflow-y: auto;
background: rgba(255, 255, 255, 0.3);
border: 1px solid;
border-image-source: conic-gradient(from 102.21deg at 52.75% 38.75%, rgba(249, 249, 249, 0.5) -32.95deg, rgba(64, 64, 64, 0.5) 10.52deg, rgba(64, 64, 64, 0.35) 32.12deg, #FFFFFF 60.28deg, rgba(255, 255, 255, 0.5) 107.79deg, rgba(64, 64, 64, 0.35) 187.59deg, #F9F9F9 207.58deg, #FFFFFF 287.31deg, rgba(249, 249, 249, 0.5) 327.05deg, rgba(64, 64, 64, 0.5) 370.52deg);
backdrop-filter: blur(15px);
box-shadow: -2px 4px 10px 0px rgba(145, 145, 145, 0.05),
-7px 17px 18px 0px rgba(145, 145, 145, 0.04),
-15px 37px 24px 0px rgba(145, 145, 145, 0.03);
}
.deviceTreeContainer {
h3 {
font-size: 16px;
font-weight: bold;
color: #006665;
margin-bottom: 16px;
}
:global(.ant-tree) {
background: transparent;
}
:global(.ant-tree-node-content-wrapper) {
color: #006665;
}
:global(.ant-tree-node-selected) {
background-color: rgba(0, 102, 101, 0.1) !important;
}
}
// 拓扑可视化样式
.topologyContainer {
height: 300px;
background: rgba(255, 255, 255, 0.3);
border: 1px solid;
border-image-source: conic-gradient(from 102.21deg at 52.75% 38.75%, rgba(249, 249, 249, 0.5) -32.95deg, rgba(64, 64, 64, 0.5) 10.52deg, rgba(64, 64, 64, 0.35) 32.12deg, #FFFFFF 60.28deg, rgba(255, 255, 255, 0.5) 107.79deg, rgba(64, 64, 64, 0.35) 187.59deg, #F9F9F9 207.58deg, #FFFFFF 287.31deg, rgba(249, 249, 249, 0.5) 327.05deg, rgba(64, 64, 64, 0.5) 370.52deg);
backdrop-filter: blur(15px);
box-shadow: -2px 4px 10px 0px rgba(145, 145, 145, 0.05),
-7px 17px 18px 0px rgba(145, 145, 145, 0.04),
-15px 37px 24px 0px rgba(145, 145, 145, 0.03);
}
.topologyContent {
height: 100%;
display: flex;
flex-direction: column;
}
.topologyInfo {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.statusIndicators {
display: flex;
gap: 16px;
}
.statusItem {
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
}
.statusDot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.deviceInfo {
text-align: right;
font-size: 14px;
color: #006665;
p {
margin: 0;
}
}
.topologyImage {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 4px;
margin-bottom: 16px;
}
.conveyorBelt {
width: 80%;
height: 60px;
background-color: #e0e0e0;
border-radius: 4px;
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: repeating-linear-gradient(90deg,
transparent,
transparent 20px,
rgba(0, 0, 0, 0.1) 20px,
rgba(0, 0, 0, 0.1) 22px);
border-radius: 4px;
}
}
.topologyControls {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.controlBtn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
background-color: rgba(0, 102, 101, 0.1);
border: 1px solid rgba(0, 102, 101, 0.3);
color: #006665;
&:hover {
background-color: rgba(0, 102, 101, 0.2);
}
}
/* 环境监测样式 */
.environmentContainer {
.ant-card-head {
background-color: #006665;
color: #fff;
}
.ant-card-body {
padding: 16px;
}
}
.dashboardMetrics {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 16px;
.metricItem {
flex: 1;
min-width: 80px;
text-align: center;
background-color: rgba(255, 255, 255, 0.9);
padding: 8px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.metricLabel {
font-size: 12px;
color: #666;
margin-bottom: 4px;
}
.metricValue {
font-size: 16px;
font-weight: bold;
color: #006665;
}
}
}
.thermalImageContainer {
position: relative;
width: 100%;
height: 160px;
background-color: #f0f2f5;
border-radius: 4px;
overflow: hidden;
.thermalImage {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #006665 0%, #00b3b1 50%, #ff6b35 100%);
opacity: 0.8;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 14px;
}
.thermalControls {
position: absolute;
bottom: 8px;
right: 8px;
display: flex;
gap: 4px;
.controlBtn {
width: 28px;
height: 28px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 102, 101, 0.8);
border-color: rgba(0, 102, 101, 0.8);
color: #fff;
font-size: 16px;
&:hover {
background-color: rgba(0, 102, 101, 1);
border-color: rgba(0, 102, 101, 1);
color: #fff;
}
}
}
}
/* 历史数据表格样式 */
.historyTable {
width: 100%;
border-collapse: separate;
border-spacing: 0;
.ant-table-thead>tr>th {
background-color: rgba(0, 102, 101, 0.1);
color: #006665;
font-weight: bold;
border: 1px solid rgba(0, 102, 101, 0.2);
}
.ant-table-tbody>tr>td {
border: 1px solid rgba(0, 102, 101, 0.2);
vertical-align: middle;
}
.ant-table-tbody>tr:hover>td {
background-color: rgba(0, 102, 101, 0.05);
}
}
/* 表格分页样式 */
.tablePagination {
.ant-pagination-item-active a {
color: #006665;
border-color: #006665;
}
.ant-pagination-item-active {
border-color: #006665;
}
.ant-pagination-item:hover {
border-color: #006665;
}
.ant-pagination-item:hover a {
color: #006665;
}
}
/* 数据更新时间样式 */
.dataUpdateTime {
font-size: 14px;
color: #666;
margin-top: 8px;
}
/* 搜索框和按钮样式 */
.searchContainer {
.ant-input {
border-color: rgba(0, 102, 101, 0.2);
}
.ant-input:focus {
border-color: #006665;
box-shadow: 0 0 0 2px rgba(0, 102, 101, 0.2);
}
.ant-btn-primary {
background-color: #006665;
border-color: #006665;
&:hover {
background-color: #00807f;
border-color: #00807f;
}
}
.ant-btn {
border-color: #006665;
color: #006665;
&:hover {
border-color: #00807f;
color: #00807f;
background-color: rgba(0, 102, 101, 0.05);
}
}
}

@ -20,6 +20,8 @@ import inspectionBg from '@/assets/img/Rectangle 34624130.svg'
import taskPlanBg from '@/assets/img/image 674 1.svg' import taskPlanBg from '@/assets/img/image 674 1.svg'
import { DeleteOutlined, DownOutlined, EditOutlined, ExportOutlined, EyeOutlined, PlusOutlined, RedoOutlined, ReloadOutlined as IconRefresh, SearchOutlined as IconSearch } from "@ant-design/icons"; import { DeleteOutlined, DownOutlined, EditOutlined, ExportOutlined, EyeOutlined, PlusOutlined, RedoOutlined, ReloadOutlined as IconRefresh, SearchOutlined as IconSearch } from "@ant-design/icons";
import TemperatureHumidityGauges from '../ReusableGauges/ReusableGauges'; import TemperatureHumidityGauges from '../ReusableGauges/ReusableGauges';
// 新增导入
import DataAnalysisInspectionManagement from './DataAnalysisInspectionManagement';
const { Search } = Input const { Search } = Input
// TaskCard 组件 - 用于显示巡检任务卡片 // TaskCard 组件 - 用于显示巡检任务卡片
@ -2493,6 +2495,10 @@ const items = [
label: <MenuBg text={'巡检管理'}></MenuBg>, label: <MenuBg text={'巡检管理'}></MenuBg>,
key: '巡检管理', key: '巡检管理',
}, },
{
label: <MenuBg text={'数据分析巡检管理'}></MenuBg>,
key: '数据分析巡检管理',
},
] ]
const InspectionManagement = () => { const InspectionManagement = () => {
const [activeTab, setActiveTab] = useState('实时监控'); const [activeTab, setActiveTab] = useState('实时监控');
@ -2873,6 +2879,7 @@ const list = {
'智能巡检范围监控': <SmartInspectionRange />, '智能巡检范围监控': <SmartInspectionRange />,
'巡检内容': <SmartInspectionContent />, '巡检内容': <SmartInspectionContent />,
'任务规划与执行流程': <TaskPlanningFlow />, '任务规划与执行流程': <TaskPlanningFlow />,
'数据分析巡检管理': <DataAnalysisInspectionManagement />, // 添加新组件
} }
const InspectionTaskPlan = () => { const InspectionTaskPlan = () => {
const [current, setCurrent] = useState('智能巡检范围监控') const [current, setCurrent] = useState('智能巡检范围监控')

Loading…
Cancel
Save