safesight-control/internal/web/ui/templates/alarms.html

173 lines
8.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{define "alarms"}}
<div class="card">
<form method="get" action="/ui/alarms" style="display:flex;gap:8px;align-items:center;margin-bottom:12px;justify-content:space-between;flex-wrap:wrap">
<span style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="date" name="from" value="{{.SelectedFrom}}" style="width:130px;font-size:12px">
<span class="muted small"></span>
<input type="date" name="to" value="{{.SelectedTo}}" style="width:130px;font-size:12px">
{{if .AlarmFilterDevices}}
<select name="device" onchange="this.form.submit()" style="font-size:11px;padding:2px 4px;width:130px;flex-shrink:0">
<option value="">全部设备</option>
{{range .AlarmFilterDevices}}
<option value="{{.}}" {{if eq . $.SelectedAlarmDevice}}selected{{end}}>{{shortID .}}</option>
{{end}}
</select>
{{end}}
{{if .AlarmFilterRuleTypes}}
<select name="rule_type" onchange="this.form.submit()" style="font-size:11px;padding:2px 4px;width:120px;flex-shrink:0">
<option value="">全部类型</option>
{{range .AlarmFilterRuleTypes}}
<option value="{{.}}" {{if eq . $.SelectedAlarmRuleType}}selected{{end}}>{{ruleLabel .}}</option>
{{end}}
</select>
{{end}}
<button class="btn" style="font-size:12px;padding:4px 12px">筛选</button>
<a href="/ui/alarms" class="btn ghost" style="font-size:11px">今天</a>
<a href="/ui/alarms?from={{.WeekStart}}&to={{.WeekEnd}}" class="btn ghost" style="font-size:11px">本周</a>
<a href="/ui/alarms?from={{.MonthStart}}&to={{.MonthEnd}}" class="btn ghost" style="font-size:11px">本月</a>
<a href="/ui/alarms?from=2020-01-01&to={{.SelectedTo}}" class="btn ghost" style="font-size:11px">全部</a>
{{if .AlarmRecords}}
<a class="btn ghost js-export-csv" style="font-size:11px;margin-left:8px">导出 CSV</a>
{{end}}
</span>
<select name="per_page" onchange="this.form.submit()" style="font-size:11px;padding:2px 4px;width:90px;flex-shrink:0">
<option value="10" {{if eq .PerPage 10}}selected{{end}}>10条/页</option>
<option value="20" {{if eq .PerPage 20}}selected{{end}}>20条/页</option>
<option value="50" {{if eq .PerPage 50}}selected{{end}}>50条/页</option>
<option value="100" {{if eq .PerPage 100}}selected{{end}}>100条/页</option>
</select>
</form>
<div class="table-wrap">
{{if .AlarmRecords}}
<table>
<thead>
<tr>
<th>时间</th>
<th>设备</th>
<th>通道</th>
<th>规则</th>
<th>目标</th>
<th>置信度</th>
<th>等级</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{{range .AlarmRecords}}
<tr id="alarm-{{.ID}}">
<td class="mono small">{{.Timestamp}}</td>
<td>
<a href="/ui/devices/{{.DeviceID}}">{{shortID .DeviceID}}</a>
</td>
<td>{{.Channel}}</td>
<td>
<span class="pill warn">{{ruleLabel .RuleName}}</span>
</td>
<td>{{if .ObjectLabel}}{{.ObjectLabel}}{{else}}-{{end}}</td>
<td>{{if .Confidence}}{{printf "%.2f" .Confidence}}{{else}}-{{end}}</td>
<td>
<span class="pill {{severityClass .Severity}}">{{severityLabel .Severity}}</span>
</td>
<td>
<span class="pill {{statusClass .Status}}">{{statusLabel .Status}}</span>
</td>
<td>
<span style="display:flex;gap:4px;align-items:center;flex-wrap:wrap">
<button class="btn ghost" style="font-size:10px;padding:2px 6px;min-height:22px" data-alarm-action="acknowledge" data-alarm-id="{{.ID}}">确认</button>
<button class="btn ghost" style="font-size:10px;padding:2px 6px;min-height:22px" data-alarm-action="resolve" data-alarm-id="{{.ID}}">关闭</button>
<select style="font-size:10px;padding:1px 2px;width:70px;min-height:22px" data-alarm-action="severity" data-alarm-id="{{.ID}}" data-current="{{.Severity}}">
<option value="info" {{if eq .Severity "info"}}selected{{end}}>信息</option>
<option value="warning" {{if eq .Severity "warning"}}selected{{end}}>警告</option>
<option value="critical" {{if eq .Severity "critical"}}selected{{end}}>严重</option>
</select>
{{if .SnapshotURL}}
<a href="{{.SnapshotURL}}" target="_blank" class="btn ghost" style="font-size:10px;padding:2px 6px;min-height:22px">截图</a>
{{end}}
{{if .ClipURL}}
<a href="{{.ClipURL}}" target="_blank" class="btn ghost" style="font-size:10px;padding:2px 6px;min-height:22px">视频</a>
{{end}}
</span>
</td>
</tr>
{{end}}
</tbody>
</table>
{{if gt .TotalAlarmPages 1}}
<div style="margin-top:12px;display:flex;gap:4px;align-items:center">
<span class="muted small" style="margin-right:8px">{{.TotalAlarmCount}} 条,{{.TotalAlarmPages}} 页</span>
{{$from := $.SelectedFrom}}{{$to := $.SelectedTo}}{{$pp := $.PerPage}}{{$dev := $.SelectedAlarmDevice}}{{$rt := $.SelectedAlarmRuleType}}
{{if gt .CurrentPage 1}}
<a href="/ui/alarms?from={{$from}}&to={{$to}}&per_page={{$pp}}&device={{$dev}}&rule_type={{$rt}}" class="btn ghost" style="font-size:11px;padding:2px 6px">«</a>
<a href="/ui/alarms?from={{$from}}&to={{$to}}&per_page={{$pp}}&page={{sub .CurrentPage 1}}&device={{$dev}}&rule_type={{$rt}}" class="btn ghost" style="font-size:11px;padding:2px 6px"></a>
{{end}}
{{range .PageNumbers}}
{{if eq . -1}}
<span class="muted small" style="padding:2px 4px"></span>
{{else}}
<a href="/ui/alarms?from={{$from}}&to={{$to}}&per_page={{$pp}}&page={{.}}&device={{$dev}}&rule_type={{$rt}}" class="btn {{if eq . $.CurrentPage}}primary{{else}}ghost{{end}}" style="font-size:11px;padding:2px 8px">{{.}}</a>
{{end}}
{{end}}
{{if lt .CurrentPage .TotalAlarmPages}}
<a href="/ui/alarms?from={{$from}}&to={{$to}}&per_page={{$pp}}&page={{add .CurrentPage 1}}&device={{$dev}}&rule_type={{$rt}}" class="btn ghost" style="font-size:11px;padding:2px 6px"></a>
<a href="/ui/alarms?from={{$from}}&to={{$to}}&per_page={{$pp}}&page={{.TotalAlarmPages}}&device={{$dev}}&rule_type={{$rt}}" class="btn ghost" style="font-size:11px;padding:2px 6px">»</a>
{{end}}
</div>
{{end}}
{{else}}
<div class="empty-state">
<div class="empty-title">暂无告警记录</div>
<div class="muted">所选日期范围内没有告警记录。</div>
</div>
{{end}}
</div>
</div>
<script>
(function(){
function postAlarmAction(id, action, body) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/ui/alarms/" + id + "/" + action);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onload = function() {
if (xhr.status === 204) {
window.location.reload();
}
};
xhr.send(body || "");
}
document.querySelectorAll("[data-alarm-action]").forEach(function(el) {
var action = el.getAttribute("data-alarm-action");
var id = el.getAttribute("data-alarm-id");
if (action === "acknowledge" || action === "resolve") {
el.addEventListener("click", function() {
postAlarmAction(id, action);
});
} else if (action === "severity") {
el.addEventListener("change", function() {
postAlarmAction(id, "severity", "severity=" + encodeURIComponent(el.value));
});
}
});
})();
</script>
<script>
(function(){
var exportBtn = document.querySelector(".js-export-csv");
if (exportBtn) {
exportBtn.addEventListener("click", function(){
var params = new URLSearchParams(window.location.search);
var exportParams = new URLSearchParams();
if (params.get("from")) exportParams.set("from", params.get("from"));
if (params.get("to")) exportParams.set("to", params.get("to"));
if (params.get("device")) exportParams.set("device", params.get("device"));
if (params.get("rule_type")) exportParams.set("rule_type", params.get("rule_type"));
window.location.href = "/api/alarms/export?" + exportParams.toString();
});
}
})();
</script>
{{end}}