You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

623 lines
19 KiB

<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> 预警信息统计</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>
10 months ago
</template>
10 months ago
<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'
import { statistic, byEquType, listMonitor, alertMessageTimes } from "@/api/index/index"
import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
10 months ago
export default {
name: 'Index',
components: {
PanelGroup,
LineChart,
RaddarChart,
PieChart,
BarChart,
digital
},
10 months ago
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'
},
{
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'
},
{
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'
}
]
}
10 months ago
},
methods: {
getCpuStatus(usage) {
if (usage < 50) return 'status-indicator status-good';
if (usage < 80) return 'status-indicator status-warning';
return 'status-indicator status-critical';
10 months ago
},
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';
},
getstatistic() {
this.loading = true
statistic().then(response => {
console.log("%c 🎹: getList -> response ", "font-size:16px;background-color:#624a64;color:white;", response)
this.loading = false
}
)
},
getbyEquType() {
this.loading = true
byEquType().then(response => {
this.loading = false
}
)
},
getlistMonitor() {
this.loading = true
listMonitor({id:11}).then(response => {
this.loading = false
}
)
},
getalertMessageTimes() {
this.loading = true
alertMessageTimes().then(response => {
this.loading = false
}
)
},
},
created() {
this.getstatistic()
this.getbyEquType()
this.getlistMonitor()
this.getalertMessageTimes()
},
}
</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>