feat(javaTest): add dispatch audit visualizer

This commit is contained in:
zgw
2026-08-05 16:09:52 +08:00
parent f80f92dca3
commit 953f78d97e
6 changed files with 1590 additions and 2 deletions

View File

@@ -0,0 +1,219 @@
<!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="light">
<link rel="icon" href="data:,">
<title>javaTest 调度路径审计台</title>
<link rel="stylesheet" href="/assets/app.css">
</head>
<body>
<div class="app-shell">
<header id="app-header" class="app-header">
<div class="brand-block">
<span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span>
<div>
<h1>调度路径审计台</h1>
<p id="boundary-text">离线内存仿真</p>
</div>
</div>
<div class="header-status">
<span id="service-status" class="status-chip status-pending">服务连接中</span>
<span id="acceptance-status" class="status-chip status-neutral">尚未验收</span>
<span id="map-version" class="meta-value">地图 --</span>
</div>
</header>
<nav id="mode-tabs" class="mode-tabs" aria-label="审计模式">
<button type="button" class="mode-tab is-active" data-mode="point" aria-selected="true">A → B 单任务</button>
<button type="button" class="mode-tab" data-mode="history" aria-selected="false">240 条历史任务</button>
<button type="button" class="mode-tab" data-mode="safety" aria-selected="false">安全审计</button>
</nav>
<section id="point-controls" class="control-band" aria-label="单任务参数">
<form id="point-form" class="point-form">
<label class="field field-site">
<span>起点 A</span>
<input id="source-site" name="sourceSite" list="site-options" autocomplete="off" required>
</label>
<button id="swap-sites" type="button" class="icon-button" title="交换起点和终点" aria-label="交换起点和终点">⇄</button>
<label class="field field-site">
<span>终点 B</span>
<input id="target-site" name="targetSite" list="site-options" autocomplete="off" required>
</label>
<datalist id="site-options"></datalist>
<label class="field">
<span>任务类型</span>
<select id="task-type" name="taskType">
<option value="INBOUND">入库</option>
<option value="OUTBOUND">出库</option>
<option value="RELOCATION">移位</option>
</select>
</label>
<label class="field field-compact">
<span>优先级</span>
<input id="priority" name="priority" type="number" min="0" max="9" value="1" required>
</label>
<label class="field">
<span>加速</span>
<select id="acceleration" name="acceleration">
<option value="100">100×</option>
<option value="10">10×</option>
</select>
</label>
<label class="field field-fault">
<span>故障预设</span>
<select id="fault-preset" name="faultPreset">
<option value="NONE">无故障</option>
<option value="PLC_FIRST_SEND_FAILURE">PLC 首次发送失败</option>
<option value="VEHICLE_RESTART">车辆重启</option>
<option value="LOW_BATTERY">全车低电量后恢复</option>
<option value="DISCONNECT_RECONNECT">全车断线后重连</option>
</select>
</label>
<button id="run-point" type="submit" class="primary-button"><span aria-hidden="true">▶</span>运行调度</button>
</form>
</section>
<section id="history-controls" class="history-band" hidden>
<div class="history-actions">
<div>
<strong>真实历史负载</strong>
<span id="history-run-state">尚未运行</span>
</div>
<div class="button-row">
<button id="export-result" type="button" class="secondary-button" disabled>导出证据</button>
<button id="run-history" type="button" class="primary-button"><span aria-hidden="true">▶</span>运行 240 条</button>
</div>
</div>
<div id="history-kpis" class="kpi-strip"></div>
<div class="table-toolbar">
<label class="search-field"><span class="sr-only">搜索任务</span><input id="task-search" type="search" placeholder="任务号 / 站点 / 车辆"></label>
<select id="task-status-filter" aria-label="按任务状态筛选">
<option value="ALL">全部状态</option>
<option value="COMPLETED">完成</option>
<option value="WAITING">等待</option>
<option value="FAILED">失败</option>
</select>
<span id="task-filter-count" class="table-count">0 条</span>
</div>
<div class="table-scroll history-table-scroll">
<table class="data-table history-table">
<thead><tr><th>任务</th><th>A → B</th><th>优先级</th><th>历史车</th><th>模拟车</th><th>路径</th><th>状态</th><th>等待</th><th>完成</th></tr></thead>
<tbody id="history-task-body"></tbody>
</table>
</div>
</section>
<main id="task-workspace" class="workspace">
<section id="route-stage" class="route-stage" aria-labelledby="route-title">
<div class="section-bar">
<div>
<span class="section-kicker">真实地图坐标</span>
<h2 id="route-title">算法规划路径</h2>
</div>
<div class="route-tools">
<label class="inline-field"><span>楼层</span><select id="floor-select"></select></label>
<label class="toggle-field"><input id="show-historical" type="checkbox" checked><span>历史参考</span></label>
</div>
</div>
<div class="route-legend" aria-label="路径图例">
<span><i class="legend-line approach"></i>接近路径</span>
<span><i class="legend-line service"></i>A → B 规划</span>
<span><i class="legend-line traversed"></i>已通过</span>
<span><i class="legend-line historical"></i>历史参考</span>
<span><i class="legend-dot hold"></i>HOLD</span>
</div>
<div class="route-canvas-wrap">
<svg id="route-map" viewBox="0 0 1000 560" role="img" aria-label="调度中心规划路径">
<defs>
<pattern id="map-grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" class="grid-line"></path>
</pattern>
</defs>
<rect width="1000" height="560" class="map-background"></rect>
</svg>
<div id="route-empty" class="empty-state">
<strong>等待调度结果</strong>
<span>路径证据将在本区域显示</span>
</div>
<div id="loading-state" class="loading-state" hidden>
<span class="activity-indicator" aria-hidden="true"></span>
<strong id="loading-label">正在运行调度</strong>
</div>
</div>
</section>
<aside id="task-inspector" class="task-inspector" aria-label="任务检查器">
<div class="section-bar inspector-heading">
<div><span class="section-kicker">任务证据</span><h2 id="inspector-task-id">未选择任务</h2></div>
<span id="task-status" class="status-chip status-neutral">--</span>
</div>
<dl class="evidence-list">
<div><dt>业务端点</dt><dd id="evidence-endpoints">--</dd></div>
<div><dt>中心选车</dt><dd id="evidence-vehicle">--</dd></div>
<div><dt>历史车辆</dt><dd id="evidence-history-vehicle">--</dd></div>
<div><dt>首次分配</dt><dd id="evidence-assigned-time">--</dd></div>
<div><dt>等待时间</dt><dd id="evidence-waiting">--</dd></div>
<div><dt>完成时间</dt><dd id="evidence-completion">--</dd></div>
<div><dt>HOLD / 重试</dt><dd id="evidence-hold-retry">--</dd></div>
<div><dt>PLC 尝试</dt><dd id="evidence-plc">--</dd></div>
</dl>
<div class="path-proof">
<span>调度中心 plannedPath</span>
<output id="planned-path-output">--</output>
</div>
<div class="reason-proof">
<span>终态 / 当前原因</span>
<output id="reason-output">--</output>
</div>
</aside>
</main>
<section id="timeline-panel" class="timeline-panel" aria-labelledby="timeline-title">
<div class="timeline-controls">
<div>
<span class="section-kicker">逐 tick 证据</span>
<h2 id="timeline-title">调度时间线</h2>
</div>
<div class="playback-buttons" role="group" aria-label="时间线播放控制">
<button id="step-back" type="button" class="icon-button" title="上一个事件" aria-label="上一个事件">◀</button>
<button id="play-timeline" type="button" class="icon-button" title="播放" aria-label="播放">▶</button>
<button id="pause-timeline" type="button" class="icon-button" title="暂停" aria-label="暂停">Ⅱ</button>
<button id="step-forward" type="button" class="icon-button" title="下一个事件" aria-label="下一个事件">▶</button>
</div>
<span id="tick-readout" class="tick-readout">tick --</span>
<input id="timeline-slider" type="range" min="0" max="0" value="0" aria-label="调度 tick">
<label class="inline-field speed-field"><span>速度</span><select id="playback-speed"><option value="900">0.5×</option><option value="450" selected>1×</option><option value="220">2×</option><option value="90">5×</option></select></label>
</div>
<div class="table-toolbar timeline-filter-bar">
<select id="event-type-filter" aria-label="按事件类型筛选"><option value="ALL">全部事件</option></select>
<span id="event-count" class="table-count">0 个事件</span>
</div>
<div class="table-scroll event-table-scroll">
<table class="data-table event-table">
<thead><tr><th>tick</th><th>事件</th><th>车辆</th><th>原因</th><th>移动</th></tr></thead>
<tbody id="event-table-body"></tbody>
</table>
</div>
</section>
<section id="safety-view" class="safety-view" hidden>
<div class="safety-header">
<div><span class="section-kicker">验收门禁</span><h2>资源安全与幂等</h2></div>
<button id="export-safety" type="button" class="secondary-button" disabled>导出证据</button>
</div>
<div id="safety-metrics" class="audit-grid"></div>
<div class="reason-columns">
<section><h3>HOLD 原因</h3><div id="hold-reasons" class="reason-list"></div></section>
<section><h3>PLC 重试原因</h3><div id="retry-reasons" class="reason-list"></div></section>
<section><h3>失败原因</h3><div id="failure-reasons" class="reason-list"></div></section>
</div>
</section>
<div id="toast" class="toast" role="status" aria-live="polite" hidden></div>
</div>
<script src="/assets/app.js" defer></script>
</body>
</html>