|
|
|
|
<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" :class="alertMessageData.yearCount == 0?'normal':''">
|
|
|
|
|
<div class="stat-label">年预警总数</div>
|
|
|
|
|
<div class="stat-value">{{ alertMessageData.yearCount }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-card" :class="alertMessageData.monthCount == 0?'normal':''">
|
|
|
|
|
<div class="stat-label">月预警总数</div>
|
|
|
|
|
<div class="stat-value">{{ alertMessageData.monthCount }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 等级预警 -->
|
|
|
|
|
<div class="stat-card" :class="i.count == 0?'normal':i.count>10?'warning':''" v-for="i in alertMessageData.levelCount" :key="i.id">
|
|
|
|
|
<div class="stat-label">{{ `等级${i.level}预警` }}</div>
|
|
|
|
|
<div class="stat-value">{{ i.count }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 设备预警 -->
|
|
|
|
|
<div class="stat-card" :class="i.count == 0?'normal':i.count>10?'warning':''" v-for="i in alertMessageData.typeCount" :key="i.id">
|
|
|
|
|
<div class="stat-label">{{ `${i.name}预警` }}</div>
|
|
|
|
|
<div class="stat-value">{{ i.count }}</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">
|
|
|
|
|
<PieChart height="250px" :chartData="byEquTypeData"/>
|
|
|
|
|
</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>
|
|
|
|
|
<el-empty description="暂无预警" v-if="alerts.length == 0"></el-empty>
|
|
|
|
|
<ul class="device-list" v-infinite-scroll="loadMore" :infinite-scroll-disabled="isLoading || noMore" infinite-scroll-distance="50" infinite-scroll-delay="300">
|
|
|
|
|
<li class="device-item" v-for="alert in alerts" :key="alert.id" :class="alert.status === 'UNHANDLED' ? 'warning' : 'normal'" >
|
|
|
|
|
<div class="device-icon">
|
|
|
|
|
<i :class="alert.status === 'UNHANDLED' ? 'fas fa-exclamation-circle' : 'fas fa-check-circle'"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="device-info">
|
|
|
|
|
<div class="device-name">{{ alert.name }}</div>
|
|
|
|
|
<div class="device-details">
|
|
|
|
|
<div>预警时间: {{ parseTime(alert.occurTime) }}</div>
|
|
|
|
|
<div>设备位置: {{ alert.location }}</div>
|
|
|
|
|
<div>预警信息: {{ alert.message }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<span :class="alert.status === 'UNHANDLED' ? 'alert-badge' : 'normal-badge'">
|
|
|
|
|
{{ '等级'+alert.level }}
|
|
|
|
|
</span>
|
|
|
|
|
</li>
|
|
|
|
|
<p v-if="isLoading">加载中...</p>
|
|
|
|
|
<p v-if="noMore">没有更多了</p>
|
|
|
|
|
</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">
|
|
|
|
|
|
|
|
|
|
<el-empty description="暂无预警" v-if="tableHeaders.length == 0"></el-empty>
|
|
|
|
|
<table v-else>
|
|
|
|
|
<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.cpuUtilization || 0)"></span>
|
|
|
|
|
{{ device.cpuUtilization || 0 }}%
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<span :class="getMemoryStatus(device.memoryUtilization || 0)"></span>
|
|
|
|
|
{{ device.memoryUtilization || 0 }}%
|
|
|
|
|
</td>
|
|
|
|
|
<td>{{ device.temperature || 0 }}°C</td>
|
|
|
|
|
<td>
|
|
|
|
|
<el-tag :hit="false" :type="device.health == '1'?'success':device.health == '2'?'':device.health == '3'?'danger':device.health == '4'?'warning':'info'"> {{ device.health == '1'?'健康':device.health == '2'?'良好':device.health == '3'?'一般':device.health == '4'?'需维护':'未获取' }} </el-tag>
|
|
|
|
|
</td>
|
|
|
|
|
<td>{{ formatDuration(device.contWorkPeriod) }}</td>
|
|
|
|
|
<td>{{ parseTime(device.gatherTime) || '--' }}</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'
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'Index',
|
|
|
|
|
components: {
|
|
|
|
|
PanelGroup,
|
|
|
|
|
LineChart,
|
|
|
|
|
RaddarChart,
|
|
|
|
|
PieChart,
|
|
|
|
|
BarChart,
|
|
|
|
|
digital
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
totalAlerts: 111,
|
|
|
|
|
activeAlerts: 42,
|
|
|
|
|
resolvedAlerts: 69,
|
|
|
|
|
lastUpdate: new Date().toLocaleString(),
|
|
|
|
|
|
|
|
|
|
isLoading: false, // 加载状态
|
|
|
|
|
noMore: false, // 是否无更多数据
|
|
|
|
|
page: 1, // 当前页码
|
|
|
|
|
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: [],
|
|
|
|
|
// 统计预警信息次数
|
|
|
|
|
alertMessageData: [],
|
|
|
|
|
|
|
|
|
|
byEquTypeData: [],
|
|
|
|
|
|
|
|
|
|
tableHeaders: ['设备名称', '设备位置', 'CPU使用率', '内存使用率', '温度', '健康度', '设备运行时长', '采集时间'],
|
|
|
|
|
deviceStatus: []
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
formatDuration(input) {// 类型安全校验
|
|
|
|
|
if (typeof input !== 'string') return "0天0小时";
|
|
|
|
|
|
|
|
|
|
const match = input.match(/(\d+)\s*days?,\s*(\d+):/i);
|
|
|
|
|
return match ? `${match[1]}天${match[2]}小时` : "0天0小时";
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
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';
|
|
|
|
|
},
|
|
|
|
|
getbyEquType() {
|
|
|
|
|
this.loading = true
|
|
|
|
|
byEquType().then(response => {
|
|
|
|
|
this.loading = false
|
|
|
|
|
this.byEquTypeData = response.data
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
getlistMonitor() {
|
|
|
|
|
this.loading = true
|
|
|
|
|
listMonitor({pageSize: 100}).then(response => {
|
|
|
|
|
this.loading = false
|
|
|
|
|
this.deviceStatus = response.rows
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
getalertMessageTimes() {
|
|
|
|
|
this.loading = true
|
|
|
|
|
alertMessageTimes().then(response => {
|
|
|
|
|
this.alertMessageData = response.data
|
|
|
|
|
this.loading = false
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
async loadMore() {
|
|
|
|
|
if (this.isLoading || this.noMore) return;
|
|
|
|
|
this.isLoading = true;
|
|
|
|
|
try {
|
|
|
|
|
const res = await statistic({page:this.page,status:'UNHANDLED'}); // 模拟API请求
|
|
|
|
|
if (res.rows.length < 10) { // 假设每页10条
|
|
|
|
|
this.noMore = true;
|
|
|
|
|
} else {
|
|
|
|
|
this.alerts = [...this.alerts, ...res.rows];
|
|
|
|
|
this.page++;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('加载失败:', error);
|
|
|
|
|
} finally {
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
created() {
|
|
|
|
|
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: grid;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
gap: 15px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
overflow-x: hidden;
|
|
|
|
|
height: 255px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
flex: 1;
|
|
|
|
|
background: linear-gradient(135deg, #3498db, #2c3e50);
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
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;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
overflow-x: hidden;
|
|
|
|
|
max-height: 255px;
|
|
|
|
|
padding-left: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.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>
|