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

329 lines
16 KiB
HTML

{{define "asset_tabs"}}
<div class="card-tabs asset-tab-wrap">
<ul class="nav nav-tabs asset-tabs" role="tablist" aria-label="配置管理页面">
<li class="nav-item" role="presentation">
<a href="/ui/assets" class="nav-link{{if eq .AssetTab "overview"}} active{{end}}" role="tab" {{if eq .AssetTab "overview"}}aria-selected="true"{{else}}aria-selected="false"{{end}}>总览</a>
</li>
<li class="nav-item" role="presentation">
<a href="/ui/assets/video-sources" class="nav-link{{if eq .AssetTab "video-sources"}} active{{end}}" role="tab" {{if eq .AssetTab "video-sources"}}aria-selected="true"{{else}}aria-selected="false"{{end}}>视频源</a>
</li>
<li class="nav-item" role="presentation">
<a href="/ui/assets/templates" class="nav-link{{if eq .AssetTab "templates"}} active{{end}}" role="tab" {{if eq .AssetTab "templates"}}aria-selected="true"{{else}}aria-selected="false"{{end}}>识别模板</a>
</li>
<li class="nav-item" role="presentation">
<a href="/ui/assets/integrations" class="nav-link{{if eq .AssetTab "integrations"}} active{{end}}" role="tab" {{if eq .AssetTab "integrations"}}aria-selected="true"{{else}}aria-selected="false"{{end}}>第三方服务</a>
</li>
<li class="nav-item" role="presentation">
<a href="/ui/assets/overlays" class="nav-link{{if eq .AssetTab "overlays"}} active{{end}}" role="tab" {{if eq .AssetTab "overlays"}}aria-selected="true"{{else}}aria-selected="false"{{end}}>调试参数</a>
</li>
</ul>
<div class="tab-content">
<div class="card tab-pane active show asset-tab-card">
<div class="card-body asset-panel-body">
{{end}}
{{define "asset_tabs_end"}}
</div>
</div>
</div>
{{end}}
{{define "assets"}}
{{template "asset_tabs" .}}
{{if eq .AssetTab "integrations"}}
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "service"}}<span>第三方服务列表</span></h2>
</div>
<div class="actions compact">
<a class="btn secondary" href="/ui/assets/integrations?new=1">{{icon "apply"}}<span>新增服务</span></a>
{{if .AssetIntegration.Name}}
<a class="btn secondary" href="/ui/assets/integrations?name={{.AssetIntegration.Name}}&edit=1">{{icon "edit"}}<span>编辑</span></a>
<form method="post" action="/ui/assets/integrations/{{.AssetIntegration.Name}}/delete">
<button class="btn secondary" type="submit">删除</button>
</form>
{{end}}
</div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>服务名称</th>
<th>服务类型</th>
<th>描述</th>
<th>地址摘要</th>
<th>状态</th>
<th>引用数量</th>
</tr>
</thead>
<tbody>
{{range .AssetIntegrations}}
<tr>
<td><a class="mono" href="/ui/assets/integrations?name={{.Name}}">{{.Name}}</a></td>
<td>{{.TypeLabel}}</td>
<td>{{if .Description}}{{.Description}}{{else}}-{{end}}</td>
<td class="mono">{{if .AddressSummary}}{{.AddressSummary}}{{else}}-{{end}}</td>
<td>{{if .Enabled}}启用{{else}}停用{{end}}</td>
<td>{{.RefCount}}</td>
</tr>
{{else}}
<tr><td colspan="6"><div class="empty-state compact"><div class="empty-title">还没有第三方服务</div></div></td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{if .AssetIntegration}}
<form method="post" action="/ui/assets/integrations">
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "service"}}<span>{{if .AssetIntegrationEditing}}第三方服务编辑{{else}}第三方服务详情{{end}}</span></h2>
</div>
</div>
<div class="field-grid">
<label><span>服务名称</span><input name="name" value="{{.AssetIntegration.Name}}" {{if .AssetIntegrationEditing}}autofocus{{else}}readonly{{end}} /></label>
<label>
<span>服务类型</span>
<select name="type" {{if not .AssetIntegrationEditing}}disabled{{end}}>
<option value="object_storage" {{if eq .AssetIntegration.Type "object_storage"}}selected{{end}}>对象存储</option>
<option value="token_service" {{if eq .AssetIntegration.Type "token_service"}}selected{{end}}>认证服务</option>
<option value="alarm_service" {{if eq .AssetIntegration.Type "alarm_service"}}selected{{end}}>告警服务</option>
</select>
</label>
<label><span>描述</span><input name="description" value="{{.AssetIntegration.Description}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>启用</span><select name="enabled" {{if not .AssetIntegrationEditing}}disabled{{end}}><option value="1" {{if .AssetIntegration.Enabled}}selected{{end}}>启用</option><option value="0" {{if not .AssetIntegration.Enabled}}selected{{end}}>停用</option></select></label>
</div>
<div class="field-grid">
<label><span>对象存储地址</span><input class="mono" name="endpoint" value="{{if .AssetIntegration.ObjectStorage}}{{.AssetIntegration.ObjectStorage.Endpoint}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>Bucket</span><input class="mono" name="bucket" value="{{if .AssetIntegration.ObjectStorage}}{{.AssetIntegration.ObjectStorage.Bucket}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>Access Key</span><input class="mono" name="access_key" value="{{if .AssetIntegration.ObjectStorage}}{{.AssetIntegration.ObjectStorage.AccessKey}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>Secret Key</span><input class="mono" name="secret_key" value="{{if .AssetIntegration.ObjectStorage}}{{.AssetIntegration.ObjectStorage.SecretKey}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
</div>
<div class="field-grid">
<label><span>Token 获取地址</span><input class="mono" name="get_token_url" value="{{if .AssetIntegration.TokenService}}{{.AssetIntegration.TokenService.GetTokenURL}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>认证用户名</span><input name="username" value="{{if .AssetIntegration.TokenService}}{{.AssetIntegration.TokenService.Username}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>认证密码</span><input name="password" value="{{if .AssetIntegration.TokenService}}{{.AssetIntegration.TokenService.Password}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>认证租户编码</span><input class="mono" name="tenant_code" value="{{if .AssetIntegration.TokenService}}{{.AssetIntegration.TokenService.TenantCode}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
</div>
<div class="field-grid">
<label><span>消息上报地址</span><input class="mono" name="put_message_url" value="{{if .AssetIntegration.AlarmService}}{{.AssetIntegration.AlarmService.PutMessageURL}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>告警用户名</span><input name="alarm_username" value="{{if .AssetIntegration.AlarmService}}{{.AssetIntegration.AlarmService.Username}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>告警密码</span><input name="alarm_password" value="{{if .AssetIntegration.AlarmService}}{{.AssetIntegration.AlarmService.Password}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
<label><span>告警租户编码</span><input class="mono" name="alarm_tenant_code" value="{{if .AssetIntegration.AlarmService}}{{.AssetIntegration.AlarmService.TenantCode}}{{end}}" {{if not .AssetIntegrationEditing}}readonly{{end}} /></label>
</div>
{{if .AssetIntegrationEditing}}
<div class="actions">
<button type="submit">{{icon "apply"}}<span>保存</span></button>
</div>
{{end}}
</div>
</form>
{{end}}
{{else if eq .AssetTab "video-sources"}}
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "device"}}<span>视频源列表</span></h2>
</div>
<div class="actions compact">
<a class="btn secondary" href="/ui/assets/video-sources?new=1">{{icon "apply"}}<span>新增视频源</span></a>
{{if .AssetVideoSource.Name}}
<a class="btn secondary" href="/ui/assets/video-sources?name={{.AssetVideoSource.Name}}&edit=1">{{icon "edit"}}<span>编辑</span></a>
<form method="post" action="/ui/assets/video-sources/{{.AssetVideoSource.Name}}/delete">
<button class="btn secondary" type="submit">删除</button>
</form>
{{end}}
</div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>视频源名称</th>
<th>类型</th>
<th>区域</th>
<th>输入地址</th>
<th>分辨率</th>
<th>帧率</th>
<th>引用数量</th>
</tr>
</thead>
<tbody>
{{range .AssetVideoSources}}
<tr>
<td><a class="mono" href="/ui/assets/video-sources?name={{.Name}}">{{.Name}}</a></td>
<td>{{.SourceTypeLabel}}</td>
<td>{{if .Area}}{{.Area}}{{else}}-{{end}}</td>
<td class="mono">{{if .Config.URL}}{{.Config.URL}}{{else}}-{{end}}</td>
<td>{{if .Config.Resolution}}{{.Config.Resolution}}{{else}}-{{end}}</td>
<td class="mono">{{if .Config.FPS}}{{.Config.FPS}}{{else}}-{{end}}</td>
<td>{{.RefCount}}</td>
</tr>
{{else}}
<tr><td colspan="7"><div class="empty-state compact"><div class="empty-title">还没有视频源</div></div></td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{if .AssetVideoSource}}
<form method="post" action="/ui/assets/video-sources">
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "device"}}<span>{{if .AssetVideoSourceEditing}}视频源编辑{{else}}视频源详情{{end}}</span></h2>
</div>
</div>
<div class="field-grid">
<label><span>视频源名称</span><input name="name" value="{{.AssetVideoSource.Name}}" {{if .AssetVideoSourceEditing}}autofocus{{else}}readonly{{end}} /></label>
<label>
<span>类型</span>
<select name="source_type" {{if not .AssetVideoSourceEditing}}disabled{{end}}>
<option value="rtsp" {{if eq .AssetVideoSource.SourceType "rtsp"}}selected{{end}}>RTSP</option>
<option value="rtmp" {{if eq .AssetVideoSource.SourceType "rtmp"}}selected{{end}}>RTMP</option>
<option value="file" {{if eq .AssetVideoSource.SourceType "file"}}selected{{end}}>文件</option>
<option value="usb_camera" {{if eq .AssetVideoSource.SourceType "usb_camera"}}selected{{end}}>USB 摄像头</option>
</select>
</label>
<label><span>区域</span><input name="area" value="{{.AssetVideoSource.Area}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>描述</span><input name="description" value="{{.AssetVideoSource.Description}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
</div>
<div class="field-grid">
<label class="full"><span>输入地址</span><input class="mono" name="url" value="{{.AssetVideoSource.Config.URL}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>标准分辨率</span><input name="resolution" value="{{.AssetVideoSource.Config.Resolution}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>像素尺寸</span><input class="mono" name="frame_size" value="{{.AssetVideoSource.Config.FrameSize}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>帧率</span><input class="mono" name="fps" value="{{.AssetVideoSource.Config.FPS}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>视频格式</span><input name="video_format" value="{{.AssetVideoSource.Config.VideoFormat}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>焦距</span><input name="focal_length" value="{{.AssetVideoSource.Config.FocalLength}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>安装高度</span><input name="mount_height" value="{{.AssetVideoSource.Config.MountHeight}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
<label><span>安装角度</span><input name="mount_angle" value="{{.AssetVideoSource.Config.MountAngle}}" {{if not .AssetVideoSourceEditing}}readonly{{end}} /></label>
</div>
{{if .AssetVideoSourceEditing}}
<div class="actions">
<button type="submit">{{icon "apply"}}<span>保存</span></button>
<a class="btn secondary" href="/ui/assets/video-sources?name={{.AssetVideoSource.Name}}">{{icon "close"}}<span>取消</span></a>
</div>
{{end}}
</div>
</form>
{{end}}
{{else}}
<div class="stats">
<div class="stat accent-teal">
<div class="k metric-label">{{icon "template"}}<span>识别模板</span></div>
<div class="v">{{len .AssetTemplates}}</div>
<div class="hint">{{if .ConfigSources.Root}}{{.ConfigSources.Root}}{{else}}标准模板与配置均存储在本地数据库{{end}}</div>
</div>
<div class="stat accent-green">
<div class="k metric-label">{{icon "device"}}<span>视频源</span></div>
<div class="v">{{len .AssetVideoSources}}</div>
<div class="hint">可复用输入流配置</div>
</div>
<div class="stat accent-slate">
<div class="k metric-label">{{icon "overlay"}}<span>调试参数</span></div>
<div class="v">{{len .AssetOverlays}}</div>
<div class="hint">调试与敏感度变化</div>
</div>
<div class="stat accent-amber">
<div class="k metric-label">{{icon "service"}}<span>第三方服务</span></div>
<div class="v">{{len .AssetIntegrations}}</div>
<div class="hint">告警、对象存储和认证服务</div>
</div>
</div>
<div class="quad-grid">
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "template"}}<span>识别模板</span></h2>
</div>
</div>
<div class="asset-list">
{{range .AssetTemplates}}
<a class="asset-row asset-link" href="/ui/assets/templates/{{.Name}}">
<span class="mono">{{.Name}}</span>
<span class="muted small">{{.NodeCount}}节点 / {{.EdgeCount}}连线</span>
</a>
{{else}}
<div class="empty-state compact">
<div class="empty-title">还没有模板</div>
</div>
{{end}}
</div>
</div>
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "device"}}<span>视频源</span></h2>
</div>
</div>
<div class="asset-list">
{{range .AssetVideoSources}}
<a class="asset-row asset-link" href="/ui/assets/video-sources?name={{.Name}}">
<span class="mono">{{.Name}}</span>
<span class="muted small">{{if .Area}}{{.Area}} / {{end}}{{if .Config.Resolution}}{{.Config.Resolution}}{{else}}未设置分辨率{{end}}</span>
</a>
{{else}}
<div class="empty-state compact">
<div class="empty-title">还没有视频源</div>
</div>
{{end}}
</div>
</div>
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "service"}}<span>第三方服务</span></h2>
</div>
</div>
<div class="asset-list">
{{range .AssetIntegrations}}
<a class="asset-row asset-link" href="/ui/assets/integrations">
<span>{{.Name}}</span>
<span class="muted small">{{if .AddressSummary}}{{.AddressSummary}}{{else}}{{.TypeLabel}}{{end}}</span>
</a>
{{else}}
<div class="empty-state compact">
<div class="empty-title">还没有第三方服务</div>
</div>
{{end}}
</div>
</div>
<div class="card">
<div class="section-title">
<div>
<h2 class="title-with-icon">{{icon "overlay"}}<span>调试参数</span></h2>
</div>
</div>
<div class="asset-list">
{{range .AssetOverlays}}
<a class="asset-row asset-link" href="/ui/assets/overlays/{{.Name}}">
<span>{{.Name}}</span>
<span class="muted small">{{.OverrideTargetNum}} 个目标</span>
</a>
{{else}}
<div class="empty-state compact">
<div class="empty-title">还没有调试参数</div>
</div>
{{end}}
</div>
</div>
</div>
{{end}}
{{if .Error}}<div class="error">{{.Error}}</div>{{end}}
{{template "asset_tabs_end" .}}
{{end}}