Files
yovision/Sense/internal/console/assets/index.html
T
QiuSW 7db707e596
Harness governance / validate (pull_request) Has been cancelled
feat(sense): add loopback NVR management console
2026-08-11 14:38:22 +08:00

159 lines
12 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.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<title>YoVision Sense · 接入运维工作台</title>
<link rel="stylesheet" href="/sense-console/app.css">
<script defer src="/sense-console/app.js"></script>
</head>
<body>
<a class="skip-link" href="#main-content">跳到主要内容</a>
<div class="engineering-banner" role="note">
回环工程控制台 · 非录像/回放 · 不代表生产公网入口
</div>
<div class="shell">
<aside class="sidebar" aria-label="Sense 主导航">
<div class="brand">
<span class="brand-mark" aria-hidden="true">S</span>
<span><strong>YoVision Sense</strong><small>接入运维工作台</small></span>
</div>
<nav class="primary-nav">
<button type="button" data-view="overview" aria-current="page"><span aria-hidden="true">◫</span>运行总览</button>
<button type="button" data-view="monitor"><span aria-hidden="true">▷</span>实时监控 <span class="nav-count" id="previewCount">0/4</span></button>
<button type="button" data-view="devices"><span aria-hidden="true">▣</span>设备 <span class="nav-count" id="deviceCount">0</span></button>
<button type="button" data-view="onboarding"><span aria-hidden="true">⇧</span>接入任务</button>
<button type="button" data-view="operations"><span aria-hidden="true">⌁</span>运维中心 <span class="nav-count" id="issueCount">0</span></button>
</nav>
<div class="sidebar-foot">
<span class="status-dot" aria-hidden="true"></span>
<span id="sessionState">尚未建立会话</span>
</div>
</aside>
<div class="workspace">
<header class="topbar">
<div>
<span class="eyebrow">当前站点</span>
<strong id="siteContext">未选择</strong>
</div>
<div class="topbar-actions">
<span class="connection-pill" id="connectionPill"><span class="connection-dot" aria-hidden="true"></span><span id="connectionText">未连接</span></span>
<button type="button" class="button secondary" id="changeContext">连接设置</button>
</div>
</header>
<div class="degraded-banner" id="degradedBanner" role="status" hidden>
<strong id="degradedTitle">无法读取最新状态</strong>
<span id="degradedMessage">未知不会显示为在线,请检查会话后重试。</span>
<button type="button" class="button ghost" id="retryLoad">重试</button>
</div>
<main id="main-content" tabindex="-1">
<section class="view active" data-page="overview" aria-labelledby="overviewTitle">
<div class="page-heading">
<div><span class="eyebrow">SENSE / OVERVIEW</span><h1 id="overviewTitle">运行总览</h1><p>先看已加载事实,再进入设备或运维中心处理。</p></div>
<button type="button" class="button primary" data-view-target="devices">查看设备</button>
</div>
<div class="metric-grid" aria-label="当前页运行指标">
<article class="metric-card"><span>视频配额</span><strong id="quotaMetric">—</strong><small id="quotaHint">连接后读取 Bell 投影</small></article>
<article class="metric-card"><span>当前页设备</span><strong id="pageMetric">0</strong><small>默认每页 16,不代表站点总数</small></article>
<article class="metric-card"><span>当前页已收敛</span><strong id="convergedMetric">0</strong><small>期望态与实际态分开计算</small></article>
<article class="metric-card warning"><span>当前页需关注</span><strong id="attentionMetric">0</strong><small>失败、离线或尚未收敛</small></article>
</div>
<div class="content-grid">
<article class="panel">
<div class="panel-heading"><div><h2>接入健康</h2><p>只汇总当前设备页,不推断未加载设备。</p></div><button type="button" class="button ghost" data-view-target="operations">进入运维中心</button></div>
<div class="health-list" id="overviewHealth"><div class="empty-compact">建立会话后显示真实状态。</div></div>
</article>
<article class="panel boundary-card">
<span class="boundary-label">本纵切边界</span>
<h2>实时监看,不替代客户 NVR</h2>
<p>常态录像仍留在客户现有 NVR;当前仅验证 Sense 设备管理面和 MediaMTX 按需播放。录像计划、录像索引与回放未实现。</p>
<ul><li>同时最多 4 路预览</li><li>刷新后 Bearer token 自动丢失</li><li>Sense 与播放端均限回环地址</li></ul>
</article>
</div>
</section>
<section class="view" data-page="monitor" aria-labelledby="monitorTitle" hidden>
<div class="page-heading">
<div><span class="eyebrow">SENSE / LIVE</span><h1 id="monitorTitle">实时监控</h1><p>从当前页设备中选择,只有明确在线且已收敛的视频设备可预览。</p></div>
<div class="heading-actions"><button type="button" class="button secondary" id="stopAll">停止全部</button><button type="button" class="button primary" id="startPreview">开始预览</button></div>
</div>
<div class="monitor-toolbar panel">
<div><strong>已选择 <span id="selectionCount">0</span> / 4</strong><span>默认不自动播放,不加载未选择设备。</span></div>
<button type="button" class="button ghost" data-view-target="devices">选择设备</button>
</div>
<div class="preview-grid" id="previewGrid" aria-live="polite">
<div class="preview-empty"><span aria-hidden="true">▷</span><strong>尚未启动预览</strong><p>前往设备页选择最多 4 路运行中的视频设备。</p></div>
</div>
</section>
<section class="view" data-page="devices" aria-labelledby="devicesTitle" hidden>
<div class="page-heading">
<div><span class="eyebrow">SENSE / DEVICES</span><h1 id="devicesTitle">设备</h1><p>统一台账按模态与能力展示;列表只读取脱敏 Control API 字段。</p></div>
<button type="button" class="button primary" id="refreshDevices">刷新状态</button>
</div>
<section class="panel filter-panel" aria-label="设备筛选">
<label><span>当前页搜索</span><input type="search" id="deviceSearch" placeholder="名称 / 序列号" autocomplete="off"></label>
<label><span>模态</span><select id="modalityFilter"><option value="">全部模态</option><option value="video">视频</option><option value="radar">雷达</option><option value="contact">门磁</option><option value="button">按钮</option><option value="wearable">可穿戴</option><option value="other">其他</option></select></label>
<label><span>期望态</span><select id="desiredFilter"><option value="">全部期望态</option><option value="enabled">启用</option><option value="disabled">停用</option></select></label>
<label><span>实际态</span><select id="actualFilter"><option value="">全部实际态</option><option value="online">在线</option><option value="offline">离线</option><option value="failed">失败</option><option value="pending">收敛中</option></select></label>
<button type="button" class="button secondary" id="clearFilters">清除筛选</button>
</section>
<div class="table-panel panel">
<div class="table-status" id="deviceTableStatus" role="status">请先建立会话。</div>
<div class="device-table-wrap">
<table>
<thead><tr><th scope="col">预览</th><th scope="col">设备</th><th scope="col">模态 / 能力</th><th scope="col">状态</th><th scope="col">期望 / 实际</th><th scope="col">Area</th><th scope="col">最后变化</th></tr></thead>
<tbody id="deviceRows"></tbody>
</table>
</div>
<div class="device-cards" id="deviceCards"></div>
<div class="pagination"><span id="pageLabel">第 1 页 · 每页 16</span><div><button type="button" class="button secondary" id="prevPage" disabled>上一页</button><button type="button" class="button secondary" id="nextPage" disabled>下一页</button></div></div>
</div>
</section>
<section class="view" data-page="onboarding" aria-labelledby="onboardingTitle" hidden>
<div class="page-heading"><div><span class="eyebrow">SENSE / ONBOARDING</span><h1 id="onboardingTitle">接入任务</h1><p>批量导入仍是已确认需求,但不属于本次设备与实时监看纵切。</p></div></div>
<article class="panel honest-empty">
<span class="feature-state">尚未实现</span><h2>批量导入与逐项激活将在独立任务完成</h2>
<p>当前 Control API 支持单设备创建和最多 128 项的批量启停,但没有冻结批量 CSV 创建契约。本页不会用假上传或假成功伪装交付。</p>
<div class="next-list"><span><strong>已具备</strong>设备查询、期望态修改、批量启停与结果查询</span><span><strong>后续补齐</strong>模板下载、逐行校验、只重试失败项和任务历史</span></div>
</article>
</section>
<section class="view" data-page="operations" aria-labelledby="operationsTitle" hidden>
<div class="page-heading">
<div><span class="eyebrow">SENSE / OPERATIONS</span><h1 id="operationsTitle">运维中心</h1><p>仅展示接入控制面的事实,不混入 Bell 业务预警。</p></div>
<a class="button secondary" href="/metrics" target="_blank" rel="noreferrer">打开低基数指标</a>
</div>
<div class="operations-grid">
<article class="panel"><div class="panel-heading"><div><h2>当前页对账差异</h2><p>失败、退避与尚未观察到当前 generation。</p></div><button type="button" class="button ghost" id="refreshOperations">刷新</button></div><div id="operationIssues" class="health-list"><div class="empty-compact">建立会话后显示。</div></div></article>
<article class="panel boundary-card"><span class="boundary-label">数据边界</span><h2>完整运维 API 尚未冻结</h2><p>分片、边缘隧道、补传、孤儿扫描和运维告警已经有后端能力或设计,但本纵切只消费设备 Control API;未取到的数据不会显示为正常。</p></article>
</div>
</section>
</main>
</div>
</div>
<nav class="mobile-nav" aria-label="移动端主导航">
<button type="button" data-view="overview" aria-current="page">总览</button><button type="button" data-view="monitor">监控</button><button type="button" data-view="devices">设备</button><button type="button" data-view="onboarding">接入</button><button type="button" data-view="operations">运维</button>
</nav>
<dialog id="contextDialog" aria-labelledby="contextTitle">
<form method="dialog" id="contextForm">
<div class="dialog-heading"><div><span class="eyebrow">LOOPBACK SESSION</span><h2 id="contextTitle">连接 Sense Control API</h2></div><button type="button" class="icon-button" id="closeContext" aria-label="关闭连接设置">×</button></div>
<p>Site ID 只用于当前页面上下文;Bearer token 仅保存在内存,刷新页面后自动丢失。</p>
<label><span>Site ID</span><input id="siteInput" name="site" required maxlength="128" autocomplete="off" placeholder="例如 school-main"></label>
<label><span>Bearer token</span><input id="tokenInput" name="token" type="password" required autocomplete="off" placeholder="不会保存或回显"></label>
<div class="form-error" id="contextError" role="alert" hidden></div>
<div class="dialog-actions"><button type="button" class="button secondary" id="cancelContext">取消</button><button type="submit" class="button primary" id="connectButton">连接并读取</button></div>
</form>
</dialog>
<div class="toast" id="toast" role="status" aria-live="polite" hidden></div>
</body>
</html>