refactor(web): split index html into flat fragments
This commit is contained in:
parent
6cdc51a249
commit
4d53ee0337
|
|
@ -1,31 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>PLC Control API.md</title>
|
||||
<link rel="stylesheet" href="/ui/styles.css" />
|
||||
</head>
|
||||
<body class="doc-page">
|
||||
<header class="topbar">
|
||||
<div class="title">PLC Control</div>
|
||||
<div class="topbar-actions">
|
||||
<a class="link-button" href="/ui/">返回监控</a>
|
||||
<div class="status" id="docStatus">加载中...</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="doc-view">
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-head">
|
||||
<h2>API.md</h2>
|
||||
</div>
|
||||
<div class="doc-body">
|
||||
<pre id="docContent">加载中...</pre>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="/ui/api-md.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<div class="drawer-backdrop hidden" id="apiDocDrawer">
|
||||
<aside class="drawer" role="dialog" aria-modal="true" aria-labelledby="apiDocTitle">
|
||||
<div class="drawer-head">
|
||||
<h3 id="apiDocTitle">API.md</h3>
|
||||
<button type="button" class="secondary" id="closeApiDoc">关闭</button>
|
||||
</div>
|
||||
<div class="drawer-body">
|
||||
<aside class="doc-toc">
|
||||
<div class="doc-toc-title">目录</div>
|
||||
<div class="doc-toc-list" id="apiDocToc">加载中...</div>
|
||||
</aside>
|
||||
<div class="markdown-doc" id="apiDocContent">加载中...</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<section class="panel bottom-right">
|
||||
<div class="panel-head">
|
||||
<h2 id="chartTitle">点位曲线</h2>
|
||||
<button class="secondary" id="refreshChart">刷新</button>
|
||||
</div>
|
||||
<div class="chart-panel">
|
||||
<div class="muted" id="chartSummary">点击上方点位表中的一行查看曲线</div>
|
||||
<canvas id="chartCanvas" class="chart-canvas" width="820" height="320"></canvas>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<section class="panel top-left">
|
||||
<div class="panel-head">
|
||||
<h2>设备</h2>
|
||||
<button type="button" id="newEquipmentBtn">+ 新增</button>
|
||||
</div>
|
||||
<div class="toolbar equipment-toolbar">
|
||||
<input id="equipmentKeyword" placeholder="搜索编码或名称" />
|
||||
<button type="button" class="secondary" id="refreshEquipmentBtn">刷新</button>
|
||||
</div>
|
||||
<div class="list equipment-list" id="equipmentList"></div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<section class="panel bottom-middle">
|
||||
<div class="panel-head">
|
||||
<h2>实时日志</h2>
|
||||
</div>
|
||||
<div class="log" id="logView"></div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
<div class="modal hidden" id="equipmentModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>设备配置</h3>
|
||||
<button class="secondary" id="closeEquipmentModal">X</button>
|
||||
</div>
|
||||
<form id="equipmentForm" class="form">
|
||||
<input type="hidden" id="equipmentId" />
|
||||
<label>
|
||||
编码
|
||||
<input id="equipmentCode" required />
|
||||
</label>
|
||||
<label>
|
||||
名称
|
||||
<input id="equipmentName" required />
|
||||
</label>
|
||||
<label>
|
||||
类型
|
||||
<input id="equipmentKind" placeholder="coal_feeder / distributor" />
|
||||
</label>
|
||||
<label>
|
||||
说明
|
||||
<input id="equipmentDescription" />
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="equipmentReset">清空</button>
|
||||
<button type="submit" id="equipmentSubmit">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hidden" id="pointModal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-head">
|
||||
<h3>选择节点创建点位</h3>
|
||||
<button class="secondary" id="closeModal">X</button>
|
||||
</div>
|
||||
<div class="toolbar">
|
||||
<select id="pointSourceSelect"></select>
|
||||
<div class="muted" id="pointSourceNodeCount">Nodes: 0</div>
|
||||
<button id="browseNodes">加载节点</button>
|
||||
<button class="secondary" id="refreshTree">刷新树</button>
|
||||
</div>
|
||||
<div class="tree" id="nodeTree"></div>
|
||||
<div class="modal-foot">
|
||||
<div class="muted" id="selectedCount">已选中 0 个节点</div>
|
||||
<button id="createPoints">创建点位</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hidden" id="sourceModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>Source 配置</h3>
|
||||
<button class="secondary" id="closeSourceModal">X</button>
|
||||
</div>
|
||||
<form id="sourceForm" class="form">
|
||||
<input type="hidden" id="sourceId" />
|
||||
<label>
|
||||
名称
|
||||
<input id="sourceName" required />
|
||||
</label>
|
||||
<label>
|
||||
Endpoint
|
||||
<input id="sourceEndpoint" placeholder="opc.tcp://host:port" required />
|
||||
</label>
|
||||
<label class="check-row">
|
||||
<input type="checkbox" id="sourceEnabled" checked />
|
||||
<span>启用</span>
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="sourceReset">清空</button>
|
||||
<button type="submit" id="sourceSubmit">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hidden" id="pointBindingModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>绑定点位</h3>
|
||||
<button class="secondary" id="closePointBindingModal">X</button>
|
||||
</div>
|
||||
<form id="pointBindingForm" class="form">
|
||||
<input type="hidden" id="bindingPointId" />
|
||||
<label>
|
||||
点位
|
||||
<input id="bindingPointName" disabled />
|
||||
</label>
|
||||
<label>
|
||||
设备
|
||||
<select id="bindingEquipmentId"></select>
|
||||
</label>
|
||||
<label>
|
||||
角色模板
|
||||
<select id="bindingSignalRole"></select>
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="clearPointBinding">清空绑定</button>
|
||||
<button type="submit" id="savePointBinding">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hidden" id="batchBindingModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>批量绑定点位</h3>
|
||||
<button class="secondary" id="closeBatchBindingModal">X</button>
|
||||
</div>
|
||||
<form id="batchBindingForm" class="form">
|
||||
<div class="muted" id="batchBindingSummary">已选中 0 个点位</div>
|
||||
<label>
|
||||
设备
|
||||
<select id="batchBindingEquipmentId"></select>
|
||||
</label>
|
||||
<label>
|
||||
角色模板
|
||||
<select id="batchBindingSignalRole"></select>
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="clearBatchBinding">清空设备和角色</button>
|
||||
<button type="submit" id="saveBatchBinding">批量保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<section class="panel top-right">
|
||||
<div class="panel-head">
|
||||
<h2>点位</h2>
|
||||
<div class="pager">
|
||||
<button class="secondary" id="prevPoints" title="上一页">‹</button>
|
||||
<span id="pointsPageInfo">1 / 1</span>
|
||||
<button class="secondary" id="nextPoints" title="下一页">›</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar point-batch-toolbar">
|
||||
<label class="check-row compact-check">
|
||||
<input type="checkbox" id="toggleAllPoints" />
|
||||
<span>本页全选</span>
|
||||
</label>
|
||||
<div class="muted" id="pointFilterSummary">当前筛选: 全部点位</div>
|
||||
<div class="muted" id="selectedPointCount">已选中 0 个点位</div>
|
||||
<button type="button" class="secondary" id="openPointModal">选入节点</button>
|
||||
<button type="button" class="secondary" id="openBatchBinding">批量绑定设备</button>
|
||||
<button type="button" class="secondary" id="clearSelectedPoints">清空选择</button>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:6%"></th>
|
||||
<th style="width:22%">名称</th>
|
||||
<th style="width:16%">值</th>
|
||||
<th style="width:10%">质量</th>
|
||||
<th style="width:18%">设备/角色</th>
|
||||
<th style="width:21%">更新时间</th>
|
||||
<th style="width:120px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pointList"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<section class="panel bottom-left">
|
||||
<div class="panel-head">
|
||||
<h2>数据源</h2>
|
||||
<button id="openSourceForm">+ 新增</button>
|
||||
</div>
|
||||
<div class="source-panels" id="sourceList"></div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<header class="topbar">
|
||||
<div class="title">PLC Control</div>
|
||||
<div class="topbar-actions">
|
||||
<button type="button" class="secondary" id="clearEquipmentFilter">设备筛选: 全部</button>
|
||||
<button type="button" class="secondary" id="openApiDoc">API.md</button>
|
||||
<div class="status" id="statusText">Ready</div>
|
||||
</div>
|
||||
</header>
|
||||
240
web/index.html
240
web/index.html
|
|
@ -7,241 +7,19 @@
|
|||
<link rel="stylesheet" href="/ui/styles.css?v=20260323e" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="topbar">
|
||||
<div class="title">PLC Control</div>
|
||||
<div class="topbar-actions">
|
||||
<button type="button" class="secondary" id="clearEquipmentFilter">设备筛选: 全部</button>
|
||||
<button type="button" class="secondary" id="openApiDoc">API.md</button>
|
||||
<div class="status" id="statusText">Ready</div>
|
||||
</div>
|
||||
</header>
|
||||
<div data-partial="/ui/html/topbar.html"></div>
|
||||
|
||||
<main class="grid">
|
||||
<section class="panel top-left">
|
||||
<div class="panel-head">
|
||||
<h2>设备</h2>
|
||||
<button type="button" id="newEquipmentBtn">+ 新增</button>
|
||||
</div>
|
||||
<div class="toolbar equipment-toolbar">
|
||||
<input id="equipmentKeyword" placeholder="搜索编码或名称" />
|
||||
<button type="button" class="secondary" id="refreshEquipmentBtn">刷新</button>
|
||||
</div>
|
||||
<div class="list equipment-list" id="equipmentList"></div>
|
||||
</section>
|
||||
|
||||
<section class="panel top-right">
|
||||
<div class="panel-head">
|
||||
<h2>点位</h2>
|
||||
<div class="pager">
|
||||
<button class="secondary" id="prevPoints" title="上一页">‹</button>
|
||||
<span id="pointsPageInfo">1 / 1</span>
|
||||
<button class="secondary" id="nextPoints" title="下一页">›</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar point-batch-toolbar">
|
||||
<label class="check-row compact-check">
|
||||
<input type="checkbox" id="toggleAllPoints" />
|
||||
<span>本页全选</span>
|
||||
</label>
|
||||
<div class="muted" id="pointFilterSummary">当前筛选: 全部点位</div>
|
||||
<div class="muted" id="selectedPointCount">已选中 0 个点位</div>
|
||||
<button type="button" class="secondary" id="openPointModal">选入节点</button>
|
||||
<button type="button" class="secondary" id="openBatchBinding">批量绑定设备</button>
|
||||
<button type="button" class="secondary" id="clearSelectedPoints">清空选择</button>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:6%"></th>
|
||||
<th style="width:22%">名称</th>
|
||||
<th style="width:16%">值</th>
|
||||
<th style="width:10%">质量</th>
|
||||
<th style="width:18%">设备/角色</th>
|
||||
<th style="width:21%">更新时间</th>
|
||||
<th style="width:120px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pointList"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel bottom-left">
|
||||
<div class="panel-head">
|
||||
<h2>数据源</h2>
|
||||
<button id="openSourceForm">+ 新增</button>
|
||||
</div>
|
||||
<div class="source-panels" id="sourceList"></div>
|
||||
</section>
|
||||
|
||||
<section class="panel bottom-middle">
|
||||
<div class="panel-head">
|
||||
<h2>实时日志</h2>
|
||||
</div>
|
||||
<div class="log" id="logView"></div>
|
||||
</section>
|
||||
|
||||
<section class="panel bottom-right">
|
||||
<div class="panel-head">
|
||||
<h2 id="chartTitle">点位曲线</h2>
|
||||
<button class="secondary" id="refreshChart">刷新</button>
|
||||
</div>
|
||||
<div class="chart-panel">
|
||||
<div class="muted" id="chartSummary">点击上方点位表中的一行查看曲线</div>
|
||||
<canvas id="chartCanvas" class="chart-canvas" width="820" height="320"></canvas>
|
||||
</div>
|
||||
</section>
|
||||
<div data-partial="/ui/html/equipment-panel.html"></div>
|
||||
<div data-partial="/ui/html/points-panel.html"></div>
|
||||
<div data-partial="/ui/html/source-panel.html"></div>
|
||||
<div data-partial="/ui/html/logs-panel.html"></div>
|
||||
<div data-partial="/ui/html/chart-panel.html"></div>
|
||||
</main>
|
||||
|
||||
<div class="modal hidden" id="equipmentModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>设备配置</h3>
|
||||
<button class="secondary" id="closeEquipmentModal">X</button>
|
||||
</div>
|
||||
<form id="equipmentForm" class="form">
|
||||
<input type="hidden" id="equipmentId" />
|
||||
<label>
|
||||
编码
|
||||
<input id="equipmentCode" required />
|
||||
</label>
|
||||
<label>
|
||||
名称
|
||||
<input id="equipmentName" required />
|
||||
</label>
|
||||
<label>
|
||||
类型
|
||||
<input id="equipmentKind" placeholder="coal_feeder / distributor" />
|
||||
</label>
|
||||
<label>
|
||||
说明
|
||||
<input id="equipmentDescription" />
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="equipmentReset">清空</button>
|
||||
<button type="submit" id="equipmentSubmit">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div data-partial="/ui/html/modals.html"></div>
|
||||
<div data-partial="/ui/html/api-doc-drawer.html"></div>
|
||||
|
||||
<div class="modal hidden" id="pointModal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-head">
|
||||
<h3>选择节点创建点位</h3>
|
||||
<button class="secondary" id="closeModal">X</button>
|
||||
</div>
|
||||
<div class="toolbar">
|
||||
<select id="pointSourceSelect"></select>
|
||||
<div class="muted" id="pointSourceNodeCount">Nodes: 0</div>
|
||||
<button id="browseNodes">加载节点</button>
|
||||
<button class="secondary" id="refreshTree">刷新树</button>
|
||||
</div>
|
||||
<div class="tree" id="nodeTree"></div>
|
||||
<div class="modal-foot">
|
||||
<div class="muted" id="selectedCount">已选中 0 个节点</div>
|
||||
<button id="createPoints">创建点位</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hidden" id="sourceModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>Source 配置</h3>
|
||||
<button class="secondary" id="closeSourceModal">X</button>
|
||||
</div>
|
||||
<form id="sourceForm" class="form">
|
||||
<input type="hidden" id="sourceId" />
|
||||
<label>
|
||||
名称
|
||||
<input id="sourceName" required />
|
||||
</label>
|
||||
<label>
|
||||
Endpoint
|
||||
<input id="sourceEndpoint" placeholder="opc.tcp://host:port" required />
|
||||
</label>
|
||||
<label class="check-row">
|
||||
<input type="checkbox" id="sourceEnabled" checked />
|
||||
<span>启用</span>
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="sourceReset">清空</button>
|
||||
<button type="submit" id="sourceSubmit">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hidden" id="pointBindingModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>绑定点位</h3>
|
||||
<button class="secondary" id="closePointBindingModal">X</button>
|
||||
</div>
|
||||
<form id="pointBindingForm" class="form">
|
||||
<input type="hidden" id="bindingPointId" />
|
||||
<label>
|
||||
点位
|
||||
<input id="bindingPointName" disabled />
|
||||
</label>
|
||||
<label>
|
||||
设备
|
||||
<select id="bindingEquipmentId"></select>
|
||||
</label>
|
||||
<label>
|
||||
角色模板
|
||||
<select id="bindingSignalRole"></select>
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="clearPointBinding">清空绑定</button>
|
||||
<button type="submit" id="savePointBinding">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hidden" id="batchBindingModal">
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-head">
|
||||
<h3>批量绑定点位</h3>
|
||||
<button class="secondary" id="closeBatchBindingModal">X</button>
|
||||
</div>
|
||||
<form id="batchBindingForm" class="form">
|
||||
<div class="muted" id="batchBindingSummary">已选中 0 个点位</div>
|
||||
<label>
|
||||
设备
|
||||
<select id="batchBindingEquipmentId"></select>
|
||||
</label>
|
||||
<label>
|
||||
角色模板
|
||||
<select id="batchBindingSignalRole"></select>
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="secondary" id="clearBatchBinding">清空设备和角色</button>
|
||||
<button type="submit" id="saveBatchBinding">批量保存</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-backdrop hidden" id="apiDocDrawer">
|
||||
<aside class="drawer" role="dialog" aria-modal="true" aria-labelledby="apiDocTitle">
|
||||
<div class="drawer-head">
|
||||
<h3 id="apiDocTitle">API.md</h3>
|
||||
<button type="button" class="secondary" id="closeApiDoc">关闭</button>
|
||||
</div>
|
||||
<div class="drawer-body">
|
||||
<aside class="doc-toc">
|
||||
<div class="doc-toc-title">目录</div>
|
||||
<div class="doc-toc-list" id="apiDocToc">加载中...</div>
|
||||
</aside>
|
||||
<div class="markdown-doc" id="apiDocContent">加载中...</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/ui/js/app.js?v=20260323e"></script>
|
||||
<script type="module" src="/ui/js/index.js?v=20260323f"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
async function loadPartial(slot) {
|
||||
const response = await fetch(slot.dataset.partial);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load partial: ${slot.dataset.partial}`);
|
||||
}
|
||||
|
||||
const html = await response.text();
|
||||
slot.insertAdjacentHTML("beforebegin", html);
|
||||
slot.remove();
|
||||
}
|
||||
|
||||
async function bootstrapPage() {
|
||||
const slots = Array.from(document.querySelectorAll("[data-partial]"));
|
||||
await Promise.all(slots.map((slot) => loadPartial(slot)));
|
||||
await import("./app.js");
|
||||
}
|
||||
|
||||
bootstrapPage().catch((error) => {
|
||||
document.body.innerHTML = `<pre>${error.message || String(error)}</pre>`;
|
||||
});
|
||||
Loading…
Reference in New Issue