修复完成配电操作,且网页可用
This commit is contained in:
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 输出文件名
|
||||
OUTPUT_FILE="web_resources.h"
|
||||
|
||||
# 创建输出文件并添加头文件保护
|
||||
echo "// Auto-generated web resources header" > "$OUTPUT_FILE"
|
||||
echo "#ifndef WEB_RESOURCES_H" >> "$OUTPUT_FILE"
|
||||
echo "#define WEB_RESOURCES_H" >> "$OUTPUT_FILE"
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
|
||||
# 遍历当前目录下的文件
|
||||
for file in *.html *.js *.css; do
|
||||
if [ -f "$file" ]; then
|
||||
# 获取文件名和扩展名
|
||||
filename=$(basename "$file")
|
||||
extension="${filename##*.}"
|
||||
varname="${filename%.*}_${extension}"
|
||||
|
||||
# 使用xxd生成内容并追加到输出文件
|
||||
echo "// Content from $filename" >> "$OUTPUT_FILE"
|
||||
xxd -i "$file" | sed "s/unsigned char .*\[\]/unsigned char ${varname}_content\[\]/" >> "$OUTPUT_FILE"
|
||||
xxd -i "$file" | grep -E "unsigned int .*_len" | sed "s/unsigned int .*_len/unsigned int ${varname}_len/" >> "$OUTPUT_FILE"
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
# 添加文件结尾
|
||||
echo "#endif // WEB_RESOURCES_H" >> "$OUTPUT_FILE"
|
||||
|
||||
echo "Web resources header generation complete. Output: $OUTPUT_FILE"
|
||||
@@ -0,0 +1,196 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>H100能源系统仪表盘</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="dashboard-container">
|
||||
<header class="dashboard-header">
|
||||
<h1><i class="fas fa-bolt"></i> 能源管理系统实时监控</h1>
|
||||
<div class="system-status" id="system-status">
|
||||
<span class="status-indicator"></span>
|
||||
<span>系统运行中</span>
|
||||
<span id="last-update">最后更新: 正在加载...</span>
|
||||
<span class="status-separator">|</span>
|
||||
<span id="device-status">设备: 离线</span>
|
||||
<span class="status-separator">|</span>
|
||||
<span id="comm-status">通信: 断开</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="dashboard-content">
|
||||
<div class="status-cards">
|
||||
<div class="status-card" id="fuel-cell-card">
|
||||
<h2><i class="fas fa-fire"></i> 燃料电池</h2>
|
||||
<div class="card-content">
|
||||
<div class="status-item">
|
||||
<span>状态:</span>
|
||||
<span id="fc-status">正在加载...</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span>输出功率:</span>
|
||||
<span id="output-power-limit">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-card" id="battery-card">
|
||||
<h2><i class="fas fa-battery-three-quarters"></i> 锂电池状态</h2>
|
||||
<div class="card-content">
|
||||
<div class="status-item">
|
||||
<span>动力电池:</span>
|
||||
<span id="dyn-soc">正在加载...</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span>仪表电池:</span>
|
||||
<span id="ins-soc">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-card" id="alarm-card">
|
||||
<h2><i class="fas fa-exclamation-triangle"></i> 报警状态</h2>
|
||||
<div class="card-content">
|
||||
<div class="status-item">
|
||||
<span>燃料电池:</span>
|
||||
<span id="fc-fault-level">正在加载...</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span>动力电池:</span>
|
||||
<span id="dyn-alarm-flag1">正在加载...</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span>仪表电池:</span>
|
||||
<span id="ins-alarm-flag1">正在加载...</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span>配电系统:</span>
|
||||
<span id="dis-alarm-flag1">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data-sections">
|
||||
<button class="collapsible"><i class="fas fa-fire"></i> 燃料电池系统</button>
|
||||
<div class="content">
|
||||
<div class="system-data-grid">
|
||||
<div class="data-item">
|
||||
<span class="data-label">工作模式:</span>
|
||||
<span class="data-value" id="fc-mode">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">运行状态:</span>
|
||||
<span class="data-value" id="fc-status">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">输出功率限制:</span>
|
||||
<span class="data-value" id="output-power-limit">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">故障等级:</span>
|
||||
<span class="data-value" id="fc-fault-level">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="collapsible"><i class="fas fa-battery-full"></i> 动力电池系统</button>
|
||||
<div class="content">
|
||||
<div class="system-data-grid">
|
||||
<div class="data-item">
|
||||
<span class="data-label">SOC:</span>
|
||||
<span class="data-value" id="dyn-soc">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">母线电压:</span>
|
||||
<span class="data-value" id="dyn-voltage-link">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">正极继电器:</span>
|
||||
<span class="data-value" id="dyn-pos-relay">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">负极继电器:</span>
|
||||
<span class="data-value" id="dyn-neg-relay">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">报警标志1:</span>
|
||||
<span class="data-value" id="dyn-alarm-flag1">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="collapsible"><i class="fas fa-battery-quarter"></i> 仪表电池系统</button>
|
||||
<div class="content">
|
||||
<div class="system-data-grid">
|
||||
<div class="data-item">
|
||||
<span class="data-label">SOC:</span>
|
||||
<span class="data-value" id="ins-soc">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">母线电压:</span>
|
||||
<span class="data-value" id="ins-voltage-link">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">正极继电器:</span>
|
||||
<span class="data-value" id="ins-pos-relay">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">负极继电器:</span>
|
||||
<span class="data-value" id="ins-neg-relay">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">报警标志1:</span>
|
||||
<span class="data-value" id="ins-alarm-flag1">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="collapsible"><i class="fas fa-plug"></i> 配电系统</button>
|
||||
<div class="content">
|
||||
<div class="system-data-grid">
|
||||
<div class="data-item">
|
||||
<span class="data-label">高压配电状态:</span>
|
||||
<span class="data-value" id="dis-sys-state">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">A相电压:</span>
|
||||
<span class="data-value" id="dis-high-a-vol">正在加载...</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">B相电压:</span>
|
||||
<span class="data-value" id="dis-high-b-vol">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="collapsible"><i class="fas fa-chart-line"></i> 电流电压曲线</button>
|
||||
<div class="content">
|
||||
<div class="chart-row">
|
||||
<div class="chart-section">
|
||||
<h3>燃料电池电流电压</h3>
|
||||
<div class="chart-container">
|
||||
<canvas id="fcVoltageCurrentChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-section">
|
||||
<h3>电池系统电流电压</h3>
|
||||
<div class="chart-container">
|
||||
<canvas id="batteryVoltageCurrentChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,365 @@
|
||||
// 系统状态常量
|
||||
const STATUS = {
|
||||
NORMAL: 'normal',
|
||||
WARNING: 'warning',
|
||||
DANGER: 'danger'
|
||||
};
|
||||
|
||||
// WebSocket连接
|
||||
let ws = null;
|
||||
let reconnectAttempts = 0;
|
||||
const MAX_RECONNECT_ATTEMPTS = 5;
|
||||
const RECONNECT_DELAY = 5000; // 5秒
|
||||
|
||||
// 初始化WebSocket连接
|
||||
function initWebSocket() {
|
||||
ws = new WebSocket(`ws://${window.location.host}/ws`);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket连接已建立');
|
||||
reconnectAttempts = 0;
|
||||
updateSystemStatus(STATUS.NORMAL, '系统连接正常');
|
||||
updateCommStatus(true);
|
||||
updateDeviceStatus(true);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
updateSystemData(data);
|
||||
updateLastUpdateTime();
|
||||
} catch (error) {
|
||||
console.error('解析数据错误:', error);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket连接已关闭');
|
||||
updateCommStatus(false);
|
||||
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
||||
setTimeout(initWebSocket, RECONNECT_DELAY);
|
||||
reconnectAttempts++;
|
||||
updateSystemStatus(STATUS.WARNING, `连接断开,尝试重新连接 (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
|
||||
} else {
|
||||
updateSystemStatus(STATUS.DANGER, '连接失败,请刷新页面');
|
||||
updateDeviceStatus(false);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('WebSocket错误:', error);
|
||||
updateSystemStatus(STATUS.DANGER, '连接错误');
|
||||
};
|
||||
}
|
||||
|
||||
// 更新设备状态
|
||||
function updateDeviceStatus(isOnline) {
|
||||
const element = document.getElementById('device-status');
|
||||
if (element) {
|
||||
element.textContent = `设备: ${isOnline ? '在线' : '离线'}`;
|
||||
element.className = isOnline ? 'status-normal' : 'status-danger';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新通信状态
|
||||
function updateCommStatus(isConnected) {
|
||||
const element = document.getElementById('comm-status');
|
||||
if (element) {
|
||||
element.textContent = `通信: ${isConnected ? '正常' : '断开'}`;
|
||||
element.className = isConnected ? 'status-normal' : 'status-danger';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新系统状态显示
|
||||
function updateSystemStatus(status, message) {
|
||||
const statusElement = document.getElementById('system-status');
|
||||
const indicator = statusElement.querySelector('.status-indicator');
|
||||
|
||||
statusElement.querySelector('span:last-child').textContent = message;
|
||||
|
||||
// 移除所有状态类
|
||||
indicator.classList.remove('status-normal', 'status-warning', 'status-danger');
|
||||
|
||||
// 添加新状态类
|
||||
switch(status) {
|
||||
case STATUS.NORMAL:
|
||||
indicator.classList.add('status-normal');
|
||||
break;
|
||||
case STATUS.WARNING:
|
||||
indicator.classList.add('status-warning');
|
||||
break;
|
||||
case STATUS.DANGER:
|
||||
indicator.classList.add('status-danger');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最后更新时间
|
||||
function updateLastUpdateTime() {
|
||||
const now = new Date();
|
||||
document.getElementById('last-update').textContent = `最后更新: ${now.toLocaleTimeString()}`;
|
||||
}
|
||||
|
||||
// 更新系统数据
|
||||
function updateSystemData(data) {
|
||||
// 燃料电池数据
|
||||
updateDataElement('fc-mode', data.fc_mode, getFCModeText);
|
||||
updateDataElement('fc-status', data.fc_status, getFCStatusText);
|
||||
updateDataElement('output-power-limit', data.output_power_limit, value => `${value} W`);
|
||||
updateDataElement('fc-fault-level', data.fc_fault_level);
|
||||
|
||||
// 电池数据
|
||||
updateDataElement('dyn-soc', data.dyn_soc, value => `${value.toFixed(1)}%`);
|
||||
updateDataElement('ins-soc', data.ins_soc, value => `${value.toFixed(1)}%`);
|
||||
updateDataElement('dyn-voltage-link', data.dyn_voltage_link, value => `${value.toFixed(2)} V`);
|
||||
updateDataElement('ins-voltage-link', data.ins_voltage_link, value => `${value.toFixed(2)} V`);
|
||||
|
||||
// 更新图表数据
|
||||
updateChartData(
|
||||
data.fc_voltage,
|
||||
data.fc_current,
|
||||
data.dyn_voltage_link,
|
||||
data.dyn_current
|
||||
);
|
||||
|
||||
// 报警数据
|
||||
updateDataElement('dyn-alarm-flag1', data.dyn_alarm_flag1, getAlarmStatusText);
|
||||
updateDataElement('ins-alarm-flag1', data.ins_alarm_flag1, getAlarmStatusText);
|
||||
updateDataElement('dis-alarm-flag1', data.dis_alarm_flag1, getAlarmStatusText);
|
||||
|
||||
// 继电器状态
|
||||
updateRelayStatus('ins-pos-relay', data.ins_realy_state?.pos_relay);
|
||||
updateRelayStatus('ins-neg-relay', data.ins_realy_state?.neg_relay);
|
||||
updateRelayStatus('dyn-pos-relay', data.dyn_realy_state?.pos_relay);
|
||||
updateRelayStatus('dyn-neg-relay', data.dyn_realy_state?.neg_relay);
|
||||
|
||||
// 其他数据更新...
|
||||
// 可以根据需要添加更多数据更新逻辑
|
||||
}
|
||||
|
||||
// 通用数据更新函数
|
||||
function updateDataElement(elementId, value, formatter = null) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element && value !== undefined) {
|
||||
element.textContent = formatter ? formatter(value) : value;
|
||||
element.classList.add('updated');
|
||||
setTimeout(() => element.classList.remove('updated'), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新继电器状态
|
||||
function updateRelayStatus(elementId, status) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element && status !== undefined) {
|
||||
element.textContent = getRelayStatusText(status);
|
||||
element.className = '';
|
||||
|
||||
if (status === 1) {
|
||||
element.classList.add('status-normal');
|
||||
} else {
|
||||
element.classList.add('status-danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FC模式文本转换
|
||||
function getFCModeText(mode) {
|
||||
const modes = {
|
||||
0: '无效',
|
||||
1: '调试',
|
||||
2: '自动',
|
||||
3: '补给'
|
||||
};
|
||||
return modes[mode] || '未知模式';
|
||||
}
|
||||
|
||||
// FC状态文本转换
|
||||
function getFCStatusText(status) {
|
||||
const statuses = {
|
||||
0: '无效',
|
||||
1: '初始化',
|
||||
2: '待机',
|
||||
3: '自检异常',
|
||||
4: '就绪',
|
||||
5: '启动',
|
||||
6: '运行',
|
||||
7: '停机',
|
||||
8: '故障',
|
||||
9: '补给',
|
||||
10: '制氢预热',
|
||||
11: '制氢预热完成',
|
||||
12: '停机维护',
|
||||
13: '维护完成'
|
||||
};
|
||||
return statuses[status] || '未知状态';
|
||||
}
|
||||
|
||||
// 继电器状态文本转换
|
||||
function getRelayStatusText(status) {
|
||||
const statuses = {
|
||||
0: '无效',
|
||||
1: '闭合',
|
||||
2: '断开'
|
||||
};
|
||||
return statuses[status] || '未知状态';
|
||||
}
|
||||
|
||||
// 折叠面板功能
|
||||
function setupCollapsibles() {
|
||||
const coll = document.getElementsByClassName("collapsible");
|
||||
for (let i = 0; i < coll.length; i++) {
|
||||
coll[i].addEventListener("click", function() {
|
||||
this.classList.toggle("active");
|
||||
const content = this.nextElementSibling;
|
||||
if (content.style.maxHeight) {
|
||||
content.style.maxHeight = null;
|
||||
} else {
|
||||
content.style.maxHeight = content.scrollHeight + "px";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 图表初始化
|
||||
let fcChart = null;
|
||||
let batteryChart = null;
|
||||
const MAX_DATA_POINTS = 60; // 保留60个数据点(约1分钟数据)
|
||||
|
||||
function initCharts() {
|
||||
// 燃料电池图表
|
||||
const fcCtx = document.getElementById('fcVoltageCurrentChart').getContext('2d');
|
||||
fcChart = new Chart(fcCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Array(MAX_DATA_POINTS).fill(''),
|
||||
datasets: [
|
||||
{
|
||||
label: '输出电压 (V)',
|
||||
data: [],
|
||||
borderColor: 'rgb(75, 192, 192)',
|
||||
tension: 0.1,
|
||||
yAxisID: 'y'
|
||||
},
|
||||
{
|
||||
label: '输出电流 (A)',
|
||||
data: [],
|
||||
borderColor: 'rgb(255, 99, 132)',
|
||||
tension: 0.1,
|
||||
yAxisID: 'y1'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'left',
|
||||
title: {
|
||||
display: true,
|
||||
text: '电压 (V)'
|
||||
}
|
||||
},
|
||||
y1: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: '电流 (A)'
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 电池系统图表
|
||||
const batteryCtx = document.getElementById('batteryVoltageCurrentChart').getContext('2d');
|
||||
batteryChart = new Chart(batteryCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Array(MAX_DATA_POINTS).fill(''),
|
||||
datasets: [
|
||||
{
|
||||
label: '母线电压 (V)',
|
||||
data: [],
|
||||
borderColor: 'rgb(54, 162, 235)',
|
||||
tension: 0.1,
|
||||
yAxisID: 'y'
|
||||
},
|
||||
{
|
||||
label: '电池电流 (A)',
|
||||
data: [],
|
||||
borderColor: 'rgb(255, 159, 64)',
|
||||
tension: 0.1,
|
||||
yAxisID: 'y1'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'left',
|
||||
title: {
|
||||
display: true,
|
||||
text: '电压 (V)'
|
||||
}
|
||||
},
|
||||
y1: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: '电流 (A)'
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新图表数据
|
||||
function updateChartData(fcVoltage, fcCurrent, batteryVoltage, batteryCurrent) {
|
||||
if (fcChart) {
|
||||
fcChart.data.datasets[0].data.push(fcVoltage);
|
||||
fcChart.data.datasets[1].data.push(fcCurrent);
|
||||
|
||||
if (fcChart.data.datasets[0].data.length > MAX_DATA_POINTS) {
|
||||
fcChart.data.datasets[0].data.shift();
|
||||
fcChart.data.datasets[1].data.shift();
|
||||
}
|
||||
fcChart.update();
|
||||
}
|
||||
|
||||
if (batteryChart) {
|
||||
batteryChart.data.datasets[0].data.push(batteryVoltage);
|
||||
batteryChart.data.datasets[1].data.push(batteryCurrent);
|
||||
|
||||
if (batteryChart.data.datasets[0].data.length > MAX_DATA_POINTS) {
|
||||
batteryChart.data.datasets[0].data.shift();
|
||||
batteryChart.data.datasets[1].data.shift();
|
||||
}
|
||||
batteryChart.update();
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
window.addEventListener('load', () => {
|
||||
initWebSocket();
|
||||
setupCollapsibles();
|
||||
initCharts();
|
||||
updateSystemStatus(STATUS.NORMAL, '系统初始化完成');
|
||||
});
|
||||
@@ -0,0 +1,253 @@
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--secondary-color: #2ecc71;
|
||||
--danger-color: #e74c3c;
|
||||
--warning-color: #f39c12;
|
||||
--dark-color: #2c3e50;
|
||||
--light-color: #ecf0f1;
|
||||
--gray-color: #95a5a6;
|
||||
--white-color: #ffffff;
|
||||
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
--transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f5f7fa;
|
||||
color: var(--dark-color);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.dashboard-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.dashboard-header h1 {
|
||||
color: var(--dark-color);
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.dashboard-header h1 i {
|
||||
margin-right: 10px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.system-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--secondary-color);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
.status-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.status-card {
|
||||
background-color: var(--white-color);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: var(--shadow);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.status-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.status-card h2 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 15px;
|
||||
color: var(--dark-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.status-item:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 折叠面板样式 */
|
||||
.collapsible {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.collapsible:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.collapsible:after {
|
||||
content: '\002B';
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.active:after {
|
||||
content: '\2212';
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0 15px;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.2s ease-out;
|
||||
background-color: var(--white-color);
|
||||
border-radius: 0 0 5px 5px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.system-data-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.data-item {
|
||||
background-color: #f8f9fa;
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-weight: 600;
|
||||
color: var(--dark-color);
|
||||
}
|
||||
|
||||
.data-value {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* 图表样式 */
|
||||
.chart-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.chart-section {
|
||||
background: var(--white-color);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 350px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.chart-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
||||
gap: 15px;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.chart-section {
|
||||
background: var(--white-color);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.chart-section h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 16px;
|
||||
color: var(--dark-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.chart-section h3 i {
|
||||
color: var(--primary-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.status-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 状态颜色 */
|
||||
.status-normal {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
.status-danger {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user