feat(feeder): add three-tab UI (ops / app-config / platform-config)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-04-17 13:34:18 +08:00
parent c961fc0298
commit 9955498e24
6 changed files with 60 additions and 10 deletions

View File

@ -133,6 +133,16 @@ body {
grid-template-rows: 1fr 260px;
}
.grid-app-config {
display: grid;
gap: 1px;
height: calc(100vh - var(--topbar-h));
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.grid-app-config .panel.app-config-main { grid-column: 1; grid-row: 1; }
/* config view slot assignments */
.grid-config .panel.top-left { grid-column: 1; grid-row: 1; }
.grid-config .panel.top-right { grid-column: 2 / 4; grid-row: 1; }
@ -1315,6 +1325,11 @@ button.danger:hover { background: var(--danger-hover); }
grid-template-rows: auto auto auto auto;
height: auto;
}
.grid-app-config {
grid-template-columns: 1fr;
grid-template-rows: auto;
height: auto;
}
body { height: auto; overflow: auto; }
.panel.top-left { min-height: 200px; }
.panel.top-right { min-height: 300px; }

View File

@ -1,8 +1,9 @@
<header class="topbar">
<div class="title">PLC Control</div>
<div class="title">投煤器布料机控制系统</div>
<div class="tab-bar">
<button type="button" class="tab-btn active" id="tabOps">运维</button>
<button type="button" class="tab-btn" id="tabConfig">配置</button>
<button type="button" class="tab-btn" id="tabAppConfig">应用配置</button>
<button type="button" class="tab-btn" id="tabConfig">平台配置</button>
</div>
<div class="topbar-actions">
<button type="button" class="secondary" id="openReadmeDoc">README.md</button>

View File

@ -0,0 +1,10 @@
<section class="panel app-config-main">
<div class="panel-head">
<h2>控制单元配置</h2>
<div class="toolbar">
<button type="button" class="secondary" id="refreshUnitBtn2">刷新</button>
<button type="button" id="newUnitBtn2">+ 新增</button>
</div>
</div>
<div class="list unit-config-list" id="unitConfigList"></div>
</section>

View File

@ -15,8 +15,9 @@
<div data-partial="/ui/html/points-panel.html"></div>
<div data-partial="/ui/html/source-panel.html"></div>
<div data-partial="/ui/html/log-stream-panel.html"></div>
<div data-partial="/ui/html/logs-panel.html"></div>
<div data-partial="/ui/html/chart-panel.html"></div>
<div data-partial="/ui/html/unit-panel.html"></div>
<div data-partial="/ui/html/logs-panel.html"></div>
</main>
<div data-partial="/ui/html/modals.html"></div>

View File

@ -35,29 +35,37 @@ import { loadSources, saveSource } from "./sources.js";
import { closeUnitModal, loadUnits, openCreateUnitModal, resetUnitForm, renderUnits, saveUnit } from "./units.js";
let _configLoaded = false;
let _appConfigLoaded = false;
function switchView(view) {
state.activeView = view;
const main = document.querySelector("main");
main.className = view === "ops" ? "grid-ops" : "grid-config";
main.className =
view === "ops" ? "grid-ops" :
view === "app-config" ? "grid-app-config" :
"grid-config";
dom.tabOps.classList.toggle("active", view === "ops");
dom.tabAppConfig.classList.toggle("active", view === "app-config");
dom.tabConfig.classList.toggle("active", view === "config");
// config-only panels
// config-only panels (platform config view)
["top-left", "top-right", "bottom-left", "bottom-right"].forEach((cls) => {
const el = main.querySelector(`.panel.${cls}`);
if (el) el.classList.toggle("hidden", view === "ops");
if (el) el.classList.toggle("hidden", view !== "config");
});
// bottom-mid is log-stream in config, hidden in ops
const logStreamPanel = main.querySelector(".panel.bottom-mid");
if (logStreamPanel) logStreamPanel.classList.toggle("hidden", view === "ops");
if (logStreamPanel) logStreamPanel.classList.toggle("hidden", view !== "config");
// ops-only panels
const opsMain = main.querySelector(".panel.ops-main");
const opsBottom = main.querySelector(".panel.ops-bottom");
if (opsMain) opsMain.classList.toggle("hidden", view === "config");
if (opsBottom) opsBottom.classList.toggle("hidden", view === "config");
if (opsMain) opsMain.classList.toggle("hidden", view !== "ops");
if (opsBottom) opsBottom.classList.toggle("hidden", view !== "ops");
// app-config-only panels
const appConfigMain = main.querySelector(".panel.app-config-main");
if (appConfigMain) appConfigMain.classList.toggle("hidden", view !== "app-config");
if (view === "config") {
startLogs();
@ -71,6 +79,13 @@ function switchView(view) {
} else {
stopLogs();
}
if (view === "app-config") {
if (!_appConfigLoaded) {
_appConfigLoaded = true;
withStatus(loadUnits());
}
}
}
function bindEvents() {
@ -165,8 +180,12 @@ function bindEvents() {
});
dom.tabOps.addEventListener("click", () => switchView("ops"));
dom.tabAppConfig.addEventListener("click", () => switchView("app-config"));
dom.tabConfig.addEventListener("click", () => switchView("config"));
dom.refreshUnitBtn2.addEventListener("click", () => withStatus(loadUnits()));
dom.newUnitBtn2.addEventListener("click", openCreateUnitModal);
document.addEventListener("equipments-updated", () => {
renderUnits();
// Re-fetch units so embedded equipment data stays in sync with config changes.

View File

@ -7,6 +7,7 @@ export const dom = {
batchStartAutoBtn: byId("batchStartAutoBtn"),
batchStopAutoBtn: byId("batchStopAutoBtn"),
tabOps: byId("tabOps"),
tabAppConfig: byId("tabAppConfig"),
tabConfig: byId("tabConfig"),
opsUnitList: byId("opsUnitList"),
opsEquipmentArea: byId("opsEquipmentArea"),
@ -65,6 +66,9 @@ export const dom = {
equipmentList: byId("equipmentList"),
refreshUnitBtn: byId("refreshUnitBtn"),
newUnitBtn: byId("newUnitBtn"),
refreshUnitBtn2: byId("refreshUnitBtn2"),
newUnitBtn2: byId("newUnitBtn2"),
unitConfigList: byId("unitConfigList"),
closeUnitModalBtn: byId("closeUnitModal"),
closeEquipmentModalBtn: byId("closeEquipmentModal"),
refreshEventBtn: byId("refreshEventBtn"),