新增系统健康快照并更新优化文档

This commit is contained in:
liuwei
2026-04-30 15:07:03 +08:00
parent ce38f66b7b
commit 83910b287b
3 changed files with 401 additions and 3 deletions

View File

@@ -125,6 +125,36 @@
</el-col>
</el-row>
<el-row :gutter="16" class="health-row">
<el-col :span="24">
<el-card class="health-overview-card" shadow="hover">
<div class="section-heading section-heading--stack">
<div>
<h3>系统健康快照</h3>
<p>把连接状态、插件运行、异常数量与转图运行时集中到一个面板里。</p>
</div>
<div class="health-overview-meta">
<span class="health-overview-meta__label">最近刷新</span>
<span class="health-overview-meta__value">{% raw %}{{ healthSummary.timestamp || '-' }}{% endraw %}</span>
</div>
</div>
<div class="health-grid">
<div v-for="card in healthCards" :key="card.key" class="health-item" :class="`health-item--${card.status}`">
<div class="health-item__head">
<span class="health-item__title">{% raw %}{{ card.title }}{% endraw %}</span>
<span class="health-item__badge" :class="`health-item__badge--${card.status}`">
{% raw %}{{ getHealthStatusText(card.status) }}{% endraw %}
</span>
</div>
<div class="health-item__value">{% raw %}{{ card.value }}{% endraw %}</div>
<div class="health-item__summary">{% raw %}{{ card.summary }}{% endraw %}</div>
<div v-if="card.extra" class="health-item__extra">{% raw %}{{ card.extra }}{% endraw %}</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="16" class="stats-highlight-row">
<el-col :span="8">
<el-card class="insight-card" shadow="hover">
@@ -304,6 +334,35 @@
uptime: 0,
timestamp: '-'
},
healthSummary: {
timestamp: '-',
robot: {
status: 'warning',
running: false,
nickname: '',
wxid: '',
summary: '加载中...'
},
plugins: {
status: 'warning',
total: 0,
running: 0,
error: 0,
summary: '加载中...'
},
errors: {
status: 'warning',
recent_24h_count: 0,
summary: '加载中...'
},
md2img: {
status: 'warning',
healthy: false,
runtime_ready: false,
browser_ready: false,
summary: '加载中...'
}
},
groups: [],
selectedGroupForHourlyTrend: '',
hourlyTrendDays: 1,
@@ -336,15 +395,56 @@
result += minutes + 'M';
return result;
},
healthCards() {
// 首页健康卡片统一在这里做展示层映射,模板只负责渲染,避免 HTML 中堆太多业务判断。
const robot = this.healthSummary.robot || {};
const plugins = this.healthSummary.plugins || {};
const errors = this.healthSummary.errors || {};
const md2img = this.healthSummary.md2img || {};
return [
{
key: 'robot',
title: '机器人连接',
status: robot.status || 'warning',
value: robot.running ? '在线' : '离线',
summary: robot.summary || '暂无状态',
extra: robot.wxid ? `标识:${robot.wxid}` : ''
},
{
key: 'plugins',
title: '插件运行',
status: plugins.status || 'warning',
value: `${plugins.running || 0} / ${plugins.total || 0}`,
summary: plugins.summary || '暂无状态',
extra: `异常 ${plugins.error || 0}`
},
{
key: 'errors',
title: '最近异常',
status: errors.status || 'warning',
value: `${errors.recent_24h_count || 0}`,
summary: errors.summary || '暂无状态',
extra: '统计窗口:近 24 小时'
},
{
key: 'md2img',
title: 'Markdown 转图',
status: md2img.status || 'warning',
value: md2img.healthy ? '就绪' : '待检查',
summary: md2img.summary || '暂无状态',
extra: `Runtime ${md2img.runtime_ready ? '已就绪' : '未就绪'} / Browser ${md2img.browser_ready ? '已就绪' : '未就绪'}`
}
];
}
},
mounted() {
this.currentView = '1';
this.loadData();
this.loadSystemInfo();
this.refreshRuntimeSnapshot();
this.loadCurrentUserInfo();
this.loadGroups();
this.systemInfoTimer = setInterval(this.loadSystemInfo, 30000);
this.systemInfoTimer = setInterval(this.refreshRuntimeSnapshot, 30000);
},
beforeDestroy() {
if (this.systemInfoTimer) {
@@ -358,6 +458,11 @@
this.loadPluginStats(days);
this.loadPluginTrend(days);
},
refreshRuntimeSnapshot() {
// 系统资源与健康聚合都属于运行态信息,统一刷新可以降低后续维护成本。
this.loadSystemInfo();
this.loadSystemHealth();
},
loadSystemInfo() {
axios.get('/api/system_info')
.then(response => {
@@ -372,11 +477,30 @@
console.error('加载系统信息出错:', error);
});
},
loadSystemHealth() {
axios.get('/api/system_health_summary')
.then(response => {
if (response.data.success) {
this.healthSummary = response.data.data || this.healthSummary;
}
})
.catch(error => {
console.error('加载系统健康摘要出错:', error);
});
},
renderSystemCharts() {
this.renderPieChart('cpuChart', this.systemInfo.cpu_usage, 'CPU使用率');
this.renderPieChart('memoryChart', this.systemInfo.memory_usage, '内存使用率');
this.renderPieChart('diskChart', this.systemInfo.disk_usage, '磁盘使用率');
},
getHealthStatusText(status) {
const statusMap = {
healthy: '健康',
warning: '关注',
danger: '异常'
};
return statusMap[status] || '未知';
},
renderPieChart(chartId, usageValue, label) {
const ctx = document.getElementById(chartId);
if (!ctx) return;
@@ -816,12 +940,132 @@
}
.hero-row,
.health-row,
.metric-extended-row,
.stats-highlight-row,
.chart-row {
margin-bottom: 16px;
}
.health-overview-card .el-card__body {
padding: 20px !important;
}
.section-heading--stack {
align-items: flex-start;
}
.health-overview-meta {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
border-radius: 14px;
background: rgba(248, 250, 252, 0.92);
border: 1px solid rgba(148, 163, 184, 0.12);
color: #64748b;
font-size: 13px;
}
.health-overview-meta__label {
color: #94a3b8;
}
.health-overview-meta__value {
color: #0f172a;
font-weight: 600;
}
.health-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
margin-top: 18px;
}
.health-item {
padding: 18px;
border-radius: 18px;
border: 1px solid rgba(148, 163, 184, 0.14);
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(248,250,252,0.88));
min-height: 156px;
}
.health-item--healthy {
box-shadow: inset 0 0 0 1px rgba(16, 185, 129, 0.10);
}
.health-item--warning {
box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.10);
}
.health-item--danger {
box-shadow: inset 0 0 0 1px rgba(239, 68, 68, 0.10);
}
.health-item__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 18px;
}
.health-item__title {
font-size: 14px;
color: #475569;
font-weight: 600;
}
.health-item__badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 50px;
padding: 4px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
}
.health-item__badge--healthy {
color: #047857;
background: rgba(16, 185, 129, 0.12);
}
.health-item__badge--warning {
color: #b45309;
background: rgba(245, 158, 11, 0.14);
}
.health-item__badge--danger {
color: #b91c1c;
background: rgba(239, 68, 68, 0.14);
}
.health-item__value {
font-size: 28px;
line-height: 1.1;
font-weight: 700;
color: #0f172a;
margin-bottom: 12px;
}
.health-item__summary {
font-size: 13px;
line-height: 1.7;
color: #475569;
}
.health-item__extra {
margin-top: 12px;
padding-top: 12px;
border-top: 1px dashed rgba(148, 163, 184, 0.22);
font-size: 12px;
color: #94a3b8;
word-break: break-all;
}
.stats-highlight-row {
display: flex;
flex-wrap: wrap;
@@ -1107,6 +1351,7 @@
@media (max-width: 1200px) {
.hero-row > .el-col,
.health-row > .el-col,
.metric-extended-row > .el-col,
.stats-highlight-row > .el-col,
.chart-row > .el-col {
@@ -1116,6 +1361,10 @@
.metric-grid > .el-col {
width: 50% !important;
}
.health-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 900px) {
@@ -1125,6 +1374,7 @@
}
.hero-row,
.health-row,
.metric-extended-row,
.stats-highlight-row,
.chart-row {
@@ -1158,6 +1408,10 @@
.metric-grid > .el-col {
width: 100% !important;
}
.health-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
@@ -1186,7 +1440,8 @@
.hero-card,
.chart-card,
.insight-card,
.metric-card {
.metric-card,
.health-item {
min-height: auto;
}
@@ -1234,6 +1489,7 @@
}
.hero-row,
.health-row,
.metric-extended-row,
.stats-highlight-row,
.chart-row {
@@ -1253,6 +1509,18 @@
font-size: 12px;
}
.health-overview-card .el-card__body {
padding: 14px !important;
}
.health-item {
padding: 14px;
}
.health-item__value {
font-size: 24px;
}
.chart-container--large,
.chart-container--panel {
height: 220px;