feat: dashboard with real-time alarm feed

This commit is contained in:
tian 2026-05-08 12:59:44 +08:00
parent 8a335b6f73
commit c7da7c2290
2 changed files with 40 additions and 39 deletions

View File

@ -760,6 +760,9 @@ func (u *UI) pageDashboard(w http.ResponseWriter, r *http.Request) {
}
}
data.Title = "总览"
if u.alarmCollector != nil {
data.AlarmRecords = u.alarmCollector.GetRecent(5)
}
data.Tasks = nil
if u.tasks != nil {
data.Tasks = u.tasks.ListTasks()

View File

@ -1,68 +1,66 @@
{{define "dashboard"}}
<div class="card">
<div class="section-title">
<div>
<h2>全局 KPI</h2>
</div>
{{if .AlarmRecords}}
<div class="card" style="border-left:3px solid var(--red)">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
<h3 class="title-with-icon" style="margin:0">{{icon "bell"}}<span>最近告警</span></h3>
<a class="btn ghost" href="/ui/alarms" style="font-size:11px">查看全部</a>
</div>
<div class="stats">
<div class="stat"><div class="k">设备总数</div><div class="v">{{.DeviceCount}}</div><div class="hint">已纳管的边缘设备</div></div>
<div class="stat"><div class="k">在线率</div><div class="v">{{.OnlineCount}} / {{.DeviceCount}}</div><div class="hint">在线设备占比</div></div>
<div class="stat"><div class="k">离线节点</div><div class="v">{{.OfflineCount}}</div><div class="hint">需要检查网络或设备服务</div></div>
<div class="stat"><div class="k">执行中任务</div><div class="v">{{.RunningTaskCount}}</div><div class="hint">正在下发或控制节点</div></div>
<div style="display:flex;flex-wrap:wrap;gap:8px">
{{range .AlarmRecords}}
<div style="padding:8px 12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-soft);min-width:200px">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px">
<span style="font-weight:500;font-size:12px">{{.RuleName}}</span>
<span class="muted small">{{.Channel}}</span>
</div>
<div class="muted small mono">{{.Timestamp}}</div>
</div>
{{end}}
</div>
</div>
{{end}}
<div class="stats">
<div class="stat"><div class="k">设备总数</div><div class="v">{{.DeviceCount}}</div><div class="hint">已纳管的边缘设备</div></div>
<div class="stat"><div class="k">在线率</div><div class="v">{{.OnlineCount}} / {{.DeviceCount}}</div><div class="hint">在线设备占比</div></div>
<div class="stat"><div class="k">离线节点</div><div class="v">{{.OfflineCount}}</div><div class="hint">需要检查网络或设备服务</div></div>
<div class="stat"><div class="k">执行中任务</div><div class="v">{{.RunningTaskCount}}</div><div class="hint">正在下发或控制节点</div></div>
</div>
{{if .AttentionDevices}}
<div class="card">
<h2>异常设备</h2>
<div class="table-wrap" style="margin-top:10px">
<table>
<thead>
<tr><th>节点</th><th>状态</th><th>管理地址</th><th>最后心跳</th><th>操作</th></tr>
</thead>
<thead><tr><th>节点</th><th>状态</th><th>管理地址</th><th>最后心跳</th><th>操作</th></tr></thead>
<tbody>
{{range .AttentionDevices}}
<tr>
<td><a class="mono" href="/ui/devices/{{.DeviceID}}">{{.DeviceName}}</a><div class="muted small mono">{{.DeviceID}}</div></td>
<td><span class="pill bad">离线</span></td>
<td class="mono">{{.IP}}:{{.AgentPort}}</td>
<td>{{ago .LastSeenMs}}</td>
<td><a class="btn ghost" href="/ui/devices/{{.DeviceID}}">查看</a></td>
</tr>
<tr>
<td><a class="mono" href="/ui/devices/{{.DeviceID}}">{{.DeviceName}}</a><div class="muted small mono">{{.DeviceID}}</div></td>
<td><span class="pill bad">离线</span></td>
<td class="mono">{{.IP}}:{{.AgentPort}}</td>
<td>{{ago .LastSeenMs}}</td>
<td><a class="btn ghost" href="/ui/devices/{{.DeviceID}}">查看</a></td>
</tr>
{{else}}
<tr><td colspan="5" class="muted">暂无需要关注的节点。</td></tr>
<tr><td colspan="5" class="muted">暂无需要关注的节点。</td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{end}}
<div class="card">
<h2>最近任务</h2>
<div class="table-wrap" style="margin-top:10px">
<table>
<thead>
<tr><th>任务标识</th><th>操作</th><th>状态</th><th>节点数</th></tr>
</thead>
<thead><tr><th>任务标识</th><th>操作</th><th>状态</th><th>节点数</th></tr></thead>
<tbody>
{{range .Tasks}}
<tr>
<td><a class="mono" href="/ui/tasks/{{.ID}}">{{.ID}}</a></td>
<td>
{{if eq .Type "config_apply"}}下发设备分配
{{else if eq .Type "reload"}}重载识别服务
{{else if eq .Type "rollback"}}回滚识别配置
{{else if eq .Type "media_start"}}启动视频分析服务
{{else if eq .Type "media_restart"}}重启视频分析服务
{{else if eq .Type "media_stop"}}停止视频分析服务
{{else}}<span class="mono">{{.Type}}</span>{{end}}
</td>
<td>
{{if eq .Status "success"}}<span class="pill ok">成功</span>
{{else if eq .Status "failed"}}<span class="pill bad">失败</span>
{{else if eq .Status "running"}}<span class="pill run">执行中</span>
{{else}}<span class="pill">待执行</span>{{end}}
</td>
<td>{{taskActionLabel .Type}}</td>
<td><span class="{{taskStatusClass .Status}}">{{taskStatusLabel .Status}}</span></td>
<td>{{len .DeviceIDs}}</td>
</tr>
{{else}}