|
|
|
|
<template>
|
|
|
|
|
<div class="body">
|
|
|
|
|
<div class="container">
|
|
|
|
|
<!-- 上层:三个模块并排 -->
|
|
|
|
|
<div class="top-section">
|
|
|
|
|
<!-- 预警信息统计 -->
|
|
|
|
|
<div class="panel">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<h2 class="card-title"><i class="fas fa-exclamation-triangle"></i> 预警信息统计1</h2>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stats-container">
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
<div class="stat-label">预警总数</div>
|
|
|
|
|
<div class="stat-value">{{ totalAlerts }}</div>
|
|
|
|
|
<div class="stat-desc">系统检测到的预警总数</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-card warning">
|
|
|
|
|
<div class="stat-label">当前预警</div>
|
|
|
|
|
<div class="stat-value">{{ activeAlerts }}</div>
|
|
|
|
|
<div class="stat-desc">需要处理的预警数量</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-card normal">
|
|
|
|
|
<div class="stat-label">已处理</div>
|
|
|
|
|
<div class="stat-value">{{ resolvedAlerts }}</div>
|
|
|
|
|
<div class="stat-desc">已解决的预警数量</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 检测设备信息 -->
|
|
|
|
|
<div class="panel">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<h2 class="card-title"><i class="fas fa-microchip"></i> 检测设备信息</h2>
|
|
|
|
|
</div>
|
|
|
|
|
<ul class="device-list">
|
|
|
|
|
<li class="device-item" v-for="device in devices" :key="device.id" :class="device.status">
|
|
|
|
|
<div class="device-icon">
|
|
|
|
|
<i :class="device.icon"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="device-info">
|
|
|
|
|
<div class="device-name">{{ device.name }}</div>
|
|
|
|
|
<div class="device-details">{{ device.description }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<span :class="device.status === 'warning' ? 'alert-badge' : 'normal-badge'">
|
|
|
|
|
{{ device.status === 'warning' ? '预警中' : '正常' }}
|
|
|
|
|
</span>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 设备预警信息 -->
|
|
|
|
|
<div class="panel">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<h2 class="card-title"><i class="fas fa-bell"></i> 设备预警信息</h2>
|
|
|
|
|
</div>
|
|
|
|
|
<ul class="device-list">
|
|
|
|
|
<li class="device-item" v-for="alert in alerts" :key="alert.id" :class="alert.status">
|
|
|
|
|
<div class="device-icon">
|
|
|
|
|
<i :class="alert.icon"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="device-info">
|
|
|
|
|
<div class="device-name">{{ alert.deviceName }}</div>
|
|
|
|
|
<div class="device-details">
|
|
|
|
|
<div>预警时间: {{ alert.time }}</div>
|
|
|
|
|
<div>设备位置: {{ alert.location }}</div>
|
|
|
|
|
<div>预警信息: {{ alert.message }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<span :class="alert.status === 'warning' ? 'alert-badge' : 'normal-badge'">
|
|
|
|
|
{{ alert.level === 1 ? '等级1' : '等级0' }}
|
|
|
|
|
</span>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 下层:设备状态监控独立一行 -->
|
|
|
|
|
<div class="bottom-section">
|
|
|
|
|
<div class="card full-width">
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<h2 class="card-title"><i class="fas fa-desktop"></i> 设备状态监控</h2>
|
|
|
|
|
<div class="last-update">最后更新: {{ lastUpdate }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="table-container">
|
|
|
|
|
<table>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th v-for="header in tableHeaders" :key="header">{{ header }}</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr v-for="device in deviceStatus" :key="device.id">
|
|
|
|
|
<td>{{ device.name }}</td>
|
|
|
|
|
<td>{{ device.location }}</td>
|
|
|
|
|
<td>
|
|
|
|
|
<span :class="getCpuStatus(device.cpuUsage)"></span>
|
|
|
|
|
{{ device.cpuUsage }}%
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<span :class="getMemoryStatus(device.memoryUsage)"></span>
|
|
|
|
|
{{ device.memoryUsage }}%
|
|
|
|
|
</td>
|
|
|
|
|
<td>{{ device.temperature }}°C</td>
|
|
|
|
|
<td>
|
|
|
|
|
<div class="health-bar">
|
|
|
|
|
<div class="health-fill" :class="getHealthStatus(device.health)"></div>
|
|
|
|
|
</div>
|
|
|
|
|
{{ device.health }}%
|
|
|
|
|
</td>
|
|
|
|
|
<td>{{ device.uptime }}</td>
|
|
|
|
|
<td>{{ device.collectionTime }}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import PanelGroup from './dashboard/PanelGroup'
|
|
|
|
|
import LineChart from './dashboard/LineChart'
|
|
|
|
|
import RaddarChart from './dashboard/RaddarChart'
|
|
|
|
|
import PieChart from './dashboard/PieChart'
|
|
|
|
|
import BarChart from './dashboard/BarChart'
|
|
|
|
|
import digital from './dashboard/digital'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'Index',
|
|
|
|
|
components: {
|
|
|
|
|
PanelGroup,
|
|
|
|
|
LineChart,
|
|
|
|
|
RaddarChart,
|
|
|
|
|
PieChart,
|
|
|
|
|
BarChart,
|
|
|
|
|
digital
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
totalAlerts: 111,
|
|
|
|
|
activeAlerts: 42,
|
|
|
|
|
resolvedAlerts: 69,
|
|
|
|
|
lastUpdate: new Date().toLocaleString(),
|
|
|
|
|
devices: [
|
|
|
|
|
{ id: 1, name: 'Forecasts', description: '预测分析系统', status: 'normal', icon: 'fas fa-chart-line' },
|
|
|
|
|
{ id: 2, name: 'Gold', description: '核心处理单元', status: 'warning', icon: 'fas fa-server' },
|
|
|
|
|
{ id: 3, name: 'Industries', description: '工业控制系统', status: 'normal', icon: 'fas fa-industry' }
|
|
|
|
|
],
|
|
|
|
|
alerts: [
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
deviceName: 'PLC远程监控系统',
|
|
|
|
|
time: '2021-10-16 14:54:11',
|
|
|
|
|
location: '污水处理监控系统',
|
|
|
|
|
message: '2#电机运行状态等于1',
|
|
|
|
|
level: 1,
|
|
|
|
|
status: 'warning',
|
|
|
|
|
icon: 'fas fa-exclamation-circle'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 2,
|
|
|
|
|
deviceName: 'PLC远程监控系统',
|
|
|
|
|
time: '2021-10-16 14:54:04',
|
|
|
|
|
location: '污水处理监控系统',
|
|
|
|
|
message: '2#电机运行状态报警恢复',
|
|
|
|
|
level: 0,
|
|
|
|
|
status: 'normal',
|
|
|
|
|
icon: 'fas fa-check-circle'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
tableHeaders: ['设备名称', '设备位置', 'CPU使用率', '内存使用率', '温度', '健康度', '设备运行时长', '采集时间'],
|
|
|
|
|
deviceStatus: [
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
name: 'PLC控制系统',
|
|
|
|
|
location: '主控室A区',
|
|
|
|
|
cpuUsage: 45,
|
|
|
|
|
memoryUsage: 62,
|
|
|
|
|
temperature: 42,
|
|
|
|
|
health: 85,
|
|
|
|
|
uptime: '125天8小时',
|
|
|
|
|
collectionTime: '2021-10-16 15:20:00'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 2,
|
|
|
|
|
name: '数据采集器',
|
|
|
|
|
location: '污水处理区',
|
|
|
|
|
cpuUsage: 78,
|
|
|
|
|
memoryUsage: 85,
|
|
|
|
|
temperature: 58,
|
|
|
|
|
health: 65,
|
|
|
|
|
uptime: '89天12小时',
|
|
|
|
|
collectionTime: '2021-10-16 15:22:00'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 3,
|
|
|
|
|
name: '监控服务器',
|
|
|
|
|
location: '数据中心',
|
|
|
|
|
cpuUsage: 32,
|
|
|
|
|
memoryUsage: 45,
|
|
|
|
|
temperature: 38,
|
|
|
|
|
health: 92,
|
|
|
|
|
uptime: '210天3小时',
|
|
|
|
|
collectionTime: '2021-10-16 15:25:00'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 4,
|
|
|
|
|
name: '网络交换机',
|
|
|
|
|
location: '通信机房',
|
|
|
|
|
cpuUsage: 15,
|
|
|
|
|
memoryUsage: 28,
|
|
|
|
|
temperature: 35,
|
|
|
|
|
health: 95,
|
|
|
|
|
uptime: '156天7小时',
|
|
|
|
|
collectionTime: '2021-10-16 15:18:00'
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
getCpuStatus(usage) {
|
|
|
|
|
if (usage < 50) return 'status-indicator status-good';
|
|
|
|
|
if (usage < 80) return 'status-indicator status-warning';
|
|
|
|
|
return 'status-indicator status-critical';
|
|
|
|
|
},
|
|
|
|
|
getMemoryStatus(usage) {
|
|
|
|
|
if (usage < 60) return 'status-indicator status-good';
|
|
|
|
|
if (usage < 85) return 'status-indicator status-warning';
|
|
|
|
|
return 'status-indicator status-critical';
|
|
|
|
|
},
|
|
|
|
|
getHealthStatus(health) {
|
|
|
|
|
if (health >= 80) return 'health-good';
|
|
|
|
|
if (health >= 60) return 'health-warning';
|
|
|
|
|
return 'health-critical';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
|
|
|
|
.body {
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
color: #333;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header h1 {
|
|
|
|
|
color: #2c3e50;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
font-size: 28px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header p {
|
|
|
|
|
color: #7f8c8d;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.container {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.top-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bottom-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.panel {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card {
|
|
|
|
|
background: white;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
padding: 20px;
|
|
|
|
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card:hover {
|
|
|
|
|
transform: translateY(-5px);
|
|
|
|
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
padding-bottom: 10px;
|
|
|
|
|
border-bottom: 1px solid #eaeaea;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-title {
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #2c3e50;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-title i {
|
|
|
|
|
color: #3498db;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stats-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
flex: 1;
|
|
|
|
|
background: linear-gradient(135deg, #3498db, #2c3e50);
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card.warning {
|
|
|
|
|
background: linear-gradient(135deg, #e74c3c, #c0392b);
|
|
|
|
|
box-shadow: 0 4px 8px rgba(231, 76, 60, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card.normal {
|
|
|
|
|
background: linear-gradient(135deg, #2ecc71, #27ae60);
|
|
|
|
|
box-shadow: 0 4px 8px rgba(46, 204, 113, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-value {
|
|
|
|
|
font-size: 32px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
margin: 10px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-label {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 12px 15px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
border-left: 4px solid #3498db;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-item:hover {
|
|
|
|
|
background: #edf2f7;
|
|
|
|
|
transform: translateX(5px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-item.warning {
|
|
|
|
|
border-left-color: #e74c3c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-item.normal {
|
|
|
|
|
border-left-color: #2ecc71;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-icon {
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
background: #3498db;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
margin-right: 15px;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-item.warning .device-icon {
|
|
|
|
|
background: #e74c3c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-item.normal .device-icon {
|
|
|
|
|
background: #2ecc71;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-name {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.device-details {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.alert-badge {
|
|
|
|
|
background: #e74c3c;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.normal-badge {
|
|
|
|
|
background: #2ecc71;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table-container {
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
background: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
th {
|
|
|
|
|
background: #2c3e50;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 15px 12px;
|
|
|
|
|
text-align: left;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
td {
|
|
|
|
|
padding: 12px;
|
|
|
|
|
border-bottom: 1px solid #eaeaea;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tr:hover {
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-indicator {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
margin-right: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-good {
|
|
|
|
|
background-color: #2ecc71;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-warning {
|
|
|
|
|
background-color: #f39c12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-critical {
|
|
|
|
|
background-color: #e74c3c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.health-bar {
|
|
|
|
|
height: 6px;
|
|
|
|
|
background: #ecf0f1;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.health-fill {
|
|
|
|
|
height: 100%;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.health-good {
|
|
|
|
|
background: #2ecc71;
|
|
|
|
|
width: 85%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.health-warning {
|
|
|
|
|
background: #f39c12;
|
|
|
|
|
width: 65%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.health-critical {
|
|
|
|
|
background: #e74c3c;
|
|
|
|
|
width: 40%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.full-width {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 1024px) {
|
|
|
|
|
.top-section {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stats-container {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|