3588AdminBackend/internal/web/ui/templates/device.html

191 lines
11 KiB
HTML

{{define "device"}}
{{template "device_header" .}}
<div class="card">
<div class="section-title">
<div>
<h2>设备工作台</h2>
<div class="muted small">单设备查看、服务控制、配置应用、模型资源和日志指标都收敛在这里完成。</div>
</div>
<div class="actions">
<a class="btn ghost" href="/ui/devices">返回设备列表</a>
<a class="btn ghost" href="/ui/devices/{{.Device.DeviceID}}/config-preview">{{icon "preview"}}<span>打开配置预览</span></a>
<a class="btn ghost" href="/ui/devices/{{.Device.DeviceID}}/logs?limit=200">诊断日志</a>
</div>
</div>
<div class="summary-strip control-summary">
<div class="summary-chip">
<div class="summary-chip-label">设备状态</div>
<div class="summary-chip-value">{{if .Device.Online}}在线{{else}}离线{{end}}</div>
</div>
<div class="summary-chip">
<div class="summary-chip-label">当前配置</div>
<div class="summary-chip-value">{{if and .ConfigStatus .ConfigStatus.Metadata.ConfigID}}{{.ConfigStatus.Metadata.ConfigID}}{{else if .PersistedConfig}}{{.PersistedConfig.ConfigID}}{{else}}待读取{{end}}</div>
</div>
<div class="summary-chip">
<div class="summary-chip-label">服务状态</div>
<div class="summary-chip-value">{{if and .ConfigStatus .ConfigStatus.MediaServer.Running}}运行中{{else}}未确认{{end}}</div>
</div>
</div>
</div>
<div class="card">
<div class="actions compact">
<a class="btn ghost" href="#device-overview">概览</a>
<a class="btn ghost" href="#device-runtime">运行与服务</a>
<a class="btn ghost" href="#device-config">设备配置</a>
<a class="btn ghost" href="#device-models">模型与资源</a>
<a class="btn ghost" href="#device-observability">日志与指标</a>
</div>
</div>
<div class="detail-grid" id="device-overview">
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "overview"}}<span>概览</span></h2>
</div>
{{if .Device.Online}}<span class="pill ok">在线</span>{{else}}<span class="pill bad">离线</span>{{end}}
</div>
<div class="info-list">
<div><span>设备名称</span><strong>{{displayDeviceName .Device .ConfigStatus}}</strong></div>
<div><span>设备 ID</span><strong class="mono">{{.Device.DeviceID}}</strong></div>
<div><span>管理地址</span><strong class="mono">{{.Device.IP}}:{{.Device.AgentPort}}</strong></div>
<div><span>视频端口</span><strong class="mono">{{.Device.MediaPort}}</strong></div>
<div><span>最后心跳</span><strong>{{ago .Device.LastSeenMs}}</strong></div>
<div><span>版本</span><strong class="mono">{{if .Device.Version}}{{.Device.Version}}{{else}}-{{end}}</strong></div>
<div><span>当前业务配置</span><strong>{{if and .ConfigStatus .ConfigStatus.Metadata.BusinessName}}{{.ConfigStatus.Metadata.BusinessName}}{{else if .PersistedConfig}}{{.PersistedConfig.ProfileName}}{{else}}-{{end}}</strong></div>
<div><span>通道名</span><strong>{{if and .ConfigStatus .ConfigStatus.Metadata.InstanceName}}{{.ConfigStatus.Metadata.InstanceName}}{{else if .Device.InstanceName}}{{.Device.InstanceName}}{{else}}-{{end}}</strong></div>
</div>
</div>
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "config"}}<span>当前运行配置</span></h2>
</div>
{{if .ConfigStatus}}{{if .ConfigStatus.OK}}<span class="pill ok">已读取</span>{{else}}<span class="pill warn">异常</span>{{end}}{{else}}<span class="pill bad">未读取</span>{{end}}
</div>
{{if .ConfigStatus}}
<div class="info-list">
<div><span>配置 ID</span><strong class="mono">{{if .ConfigStatus.Metadata.ConfigID}}{{.ConfigStatus.Metadata.ConfigID}}{{else}}未标记{{end}}</strong></div>
<div><span>配置版本</span><strong class="mono">{{if .ConfigStatus.Metadata.ConfigVersion}}{{.ConfigStatus.Metadata.ConfigVersion}}{{else}}未标记{{end}}</strong></div>
<div><span>模板</span><strong>{{if .ConfigStatus.Metadata.Template}}{{.ConfigStatus.Metadata.Template}}{{else}}-{{end}}</strong></div>
<div><span>业务配置</span><strong>{{if .ConfigStatus.Metadata.Profile}}{{.ConfigStatus.Metadata.Profile}}{{else}}-{{end}}</strong></div>
<div><span>叠加项</span><strong class="mono">{{if .ConfigStatus.Metadata.Overlays}}{{range $i, $name := .ConfigStatus.Metadata.Overlays}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}</strong></div>
<div><span>配置文件</span><strong class="mono">{{.ConfigStatus.ConfigPath}}</strong></div>
<div><span>配置 SHA</span><strong class="mono">{{shortHash .ConfigStatus.Sha256}}</strong></div>
</div>
{{else}}
{{if .PersistedConfig}}
<div class="info-list">
<div><span>配置 ID</span><strong class="mono">{{if .PersistedConfig.ConfigID}}{{.PersistedConfig.ConfigID}}{{else}}未标记{{end}}</strong></div>
<div><span>配置版本</span><strong class="mono">{{if .PersistedConfig.ConfigVersion}}{{.PersistedConfig.ConfigVersion}}{{else}}未标记{{end}}</strong></div>
<div><span>模板</span><strong>{{if .PersistedConfig.TemplateName}}{{.PersistedConfig.TemplateName}}{{else}}-{{end}}</strong></div>
<div><span>业务配置</span><strong>{{if .PersistedConfig.ProfileName}}{{.PersistedConfig.ProfileName}}{{else}}-{{end}}</strong></div>
<div><span>叠加项</span><strong class="mono">{{if .PersistedConfig.OverlaysJSON}}{{.PersistedConfig.OverlaysJSON}}{{else}}-{{end}}</strong></div>
<div><span>最近下发任务</span><strong class="mono">{{if .PersistedConfig.LastAppliedTaskID}}{{.PersistedConfig.LastAppliedTaskID}}{{else}}-{{end}}</strong></div>
</div>
{{else}}
<div class="empty-state compact">
<div class="empty-title">暂未读到配置状态</div>
<div class="muted">{{if .ConfigStatusErr}}{{.ConfigStatusErr}}{{else}}设备未返回配置摘要。{{end}}</div>
</div>
{{end}}
{{end}}
</div>
</div>
<div class="control-grid" id="device-runtime">
<section class="panel-block">
<div class="panel-head">
<div>
<h3 class="title-with-icon">{{icon "service"}}<span>运行与服务</span></h3>
</div>
{{if and .ConfigStatus .ConfigStatus.MediaServer.Running}}<span class="pill ok">运行中</span>{{else}}<span class="pill warn">待确认</span>{{end}}
</div>
<div class="info-list compact-list">
<div><span>媒体服务</span><strong>{{if and .ConfigStatus .ConfigStatus.MediaServer.Running}}运行中{{else}}未确认{{end}}</strong></div>
<div><span>上一份配置</span><strong class="mono">{{if and .ConfigStatus .ConfigStatus.PreviousConfig .ConfigStatus.PreviousConfig.Metadata.ConfigID}}{{.ConfigStatus.PreviousConfig.Metadata.ConfigID}}{{else}}-{{end}}</strong></div>
</div>
<div class="actions stack">
<form method="post" action="/ui/devices/{{.Device.DeviceID}}/action">
<input type="hidden" name="action" value="media_start" />
<input type="hidden" name="return_to" value="config" />
<button type="submit" class="secondary">启动服务</button>
</form>
<form method="post" action="/ui/devices/{{.Device.DeviceID}}/action">
<input type="hidden" name="action" value="media_restart" />
<input type="hidden" name="return_to" value="config" />
<button type="submit" class="primary">重启服务</button>
</form>
<form method="post" action="/ui/devices/{{.Device.DeviceID}}/action">
<input type="hidden" name="action" value="media_stop" />
<input type="hidden" name="return_to" value="config" />
<button type="submit" class="danger">停止服务</button>
</form>
</div>
</section>
<section class="panel-block" id="device-config">
<div class="panel-head">
<div>
<h3 class="title-with-icon">{{icon "apply"}}<span>设备配置</span></h3>
</div>
<a class="btn secondary" href="/ui/devices/{{.Device.DeviceID}}/config-preview">{{icon "preview"}}<span>编辑和上传候选配置</span></a>
</div>
<div class="info-list compact-list">
<div><span>当前配置</span><strong class="mono">{{if and .ConfigStatus .ConfigStatus.Metadata.ConfigID}}{{.ConfigStatus.Metadata.ConfigID}}{{else if .PersistedConfig}}{{.PersistedConfig.ConfigID}}{{else}}待读取{{end}}</strong></div>
<div><span>候选配置</span><strong class="mono">{{if and .ConfigStatus .ConfigStatus.Candidate .ConfigStatus.Candidate.Exists}}{{if .ConfigStatus.Candidate.Metadata.ConfigID}}{{.ConfigStatus.Candidate.Metadata.ConfigID}}{{else}}已存在{{end}}{{else}}未上传{{end}}</strong></div>
</div>
<div class="actions">
<form method="post" action="/ui/devices/{{.Device.DeviceID}}/config-candidate/apply">
<input type="hidden" name="return_to" value="config" />
<button type="submit" class="primary">应用候选配置</button>
</form>
<form method="post" action="/ui/devices/{{.Device.DeviceID}}/action">
<input type="hidden" name="action" value="rollback" />
<input type="hidden" name="return_to" value="config" />
<button type="submit" class="secondary">回滚到上一份</button>
</form>
</div>
</section>
<section class="panel-block" id="device-models">
<div class="panel-head">
<div>
<h3 class="title-with-icon">{{icon "assets"}}<span>模型与资源</span></h3>
</div>
<a class="btn secondary" href="/ui/models">进入模型管理</a>
</div>
<div class="info-list compact-list">
<div><span>模型入口</span><strong>通过模型管理页上传到设备</strong></div>
<div><span>人脸库</span><strong>通过识别配置与资源页维护</strong></div>
</div>
<div class="actions">
<form method="post" action="/ui/devices/{{.Device.DeviceID}}/face-gallery/reload">
<button type="submit" class="secondary">重载人脸库</button>
</form>
<a class="btn ghost" href="/ui/assets">查看识别配置资产</a>
</div>
</section>
<section class="panel-block" id="device-observability">
<div class="panel-head">
<div>
<h3 class="title-with-icon">{{icon "result"}}<span>日志与指标</span></h3>
</div>
</div>
<div class="actions stack">
<a class="btn ghost" href="/ui/devices/{{.Device.DeviceID}}/logs?limit=200">诊断日志</a>
<a class="btn ghost" href="/ui/devices/{{.Device.DeviceID}}/graphs">运行指标</a>
<a class="btn ghost" href="/ui/api">高级调试</a>
</div>
{{if .ResultTitle}}<div class="muted small" style="margin-top:12px">{{.ResultTitle}}</div>{{end}}
{{if .Message}}<div class="msg">{{.Message}}</div>{{end}}
{{if .Error}}<div class="error">{{.Error}}</div>{{end}}
{{if .RawText}}<pre style="margin-top:10px">{{.RawText}}</pre>{{end}}
</section>
</div>
{{end}}