feat(HEL-546): 数据中枢 A/B/C 三方向落地为可用页面
- 基于第七版稳定基线(be647bb)重构 admin 前端为模块化架构:
tokens.css(设计token唯一来源) + shared.css(公共组件) + core.js(状态/API/事件总线/轮询/路由)
+ main.js(入口) + layouts/{flowline,ledger,strata}.{css,js}(三方向独立布局)
+ layouts/shared.js(三方向共用数据视图与操作绑定)
- 三个页面(A装配线/B值班台账/C地层剖面)通过 ?layout= 查询参数独立可达、可刷新、可前进后退导航,
共享登录态、真实后端数据(overview/sources/jobs/batches/datasets/audit)、错误处理与主题
- 六大板块(总览/数据源/调度任务/盘后发布/数据集/审计)在三个方向均可查看与操作(探测/触发/回滚/补数)
- 修正健康状态语义:CircuitBreaker closed/half_open/open 与适配器 ok/error/empty/unconfigured/unknown
统一归一化为 ok/warn/error/unconfigured/unknown 供三方向一致展示
- 事件驱动动效:A 水平接力光梭、B 行级高亮+实时调用跑马灯、C 纵向贯穿光点+分层标记;
统一使用 transform/opacity/WAAPI,避免 transition:all,支持 prefers-reduced-motion 静态降级
- 修复响应式布局在 1024/1280 断点因 CSS Grid 默认 min-width:auto 被内部宽表格撑爆轨道的问题
- 修复布局切换/前进后退时残留 setTimeout 回调在 unmount 后访问 null root 导致的报错(mounted 标志位+
safeTimeout+统一清理定时器)
- 自测:Playwright 全断点(1440/1280/1024)x 双主题矩阵截图、90 次连续布局切换+前进后退压力测试无报错、
探测/触发/回滚danger操作流程验证、reduced-motion 验证;后端 pytest 全量 133 用例通过,无回归
- 未改动:登录/鉴权契约、后端 API、'问天'冻结区、生产数据/权限
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
be647bbaba
commit
011ecc0d1a
@@ -0,0 +1,91 @@
|
||||
"use strict";
|
||||
/* ==========================================================================
|
||||
xiaobai-datahub 管理后台 · 启动壳
|
||||
会话/登录/改密/登出/主题按钮/退出确认 —— 与具体布局无关,三页共用同一份。
|
||||
========================================================================== */
|
||||
(function () {
|
||||
const C = window.Core;
|
||||
const { $ } = C;
|
||||
|
||||
function show(id) {
|
||||
["login-view", "change-view", "shell"].forEach((key) => { $(key).hidden = key !== id; });
|
||||
}
|
||||
|
||||
async function boot() {
|
||||
try {
|
||||
const session = await C.api("/admin/api/session");
|
||||
C.state.csrf = session.csrf;
|
||||
$("who").textContent = session.username;
|
||||
if (session.must_change) { show("change-view"); return; }
|
||||
show("shell");
|
||||
enterShell();
|
||||
} catch {
|
||||
show("login-view");
|
||||
}
|
||||
}
|
||||
|
||||
$("login-form").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const form = new FormData(event.target);
|
||||
$("login-error").hidden = true;
|
||||
try {
|
||||
const result = await C.api("/admin/api/login", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ username: form.get("username"), password: form.get("password") }),
|
||||
});
|
||||
C.state.csrf = result.csrf;
|
||||
if (result.must_change) show("change-view");
|
||||
else { show("shell"); enterShell(); }
|
||||
} catch (err) {
|
||||
$("login-error").hidden = false;
|
||||
$("login-error").textContent = err.message;
|
||||
}
|
||||
});
|
||||
|
||||
$("change-form").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const form = new FormData(event.target);
|
||||
try {
|
||||
await C.api("/admin/api/change-password", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ current: form.get("current"), new_password: form.get("new_password") }),
|
||||
});
|
||||
show("shell");
|
||||
enterShell();
|
||||
} catch (err) {
|
||||
$("change-error").hidden = false;
|
||||
$("change-error").textContent = err.message;
|
||||
}
|
||||
});
|
||||
|
||||
$("logout-btn").addEventListener("click", async () => {
|
||||
await C.api("/admin/api/logout", { method: "POST", body: "{}" });
|
||||
C.Poller.stopAll();
|
||||
Object.values(window.HUB_LAYOUTS || {}).forEach((impl) => { try { impl.unmount && impl.unmount(); } catch { /* ignore */ } });
|
||||
show("login-view");
|
||||
});
|
||||
|
||||
$("theme-btn").addEventListener("click", () => C.toggleTheme());
|
||||
C.Bus.on("theme", (theme) => { $("theme-btn").textContent = theme === "night" ? "日间" : "夜间"; });
|
||||
|
||||
function updateCrumb() {
|
||||
const el = $("crumb");
|
||||
if (!el) return;
|
||||
if (!C.state.online) { el.textContent = "网络已断开 · 已暂停实时"; return; }
|
||||
if (!C.state.visible) { el.textContent = "已切至后台 · 已暂停动效"; return; }
|
||||
const d = C.state.data.overview;
|
||||
el.textContent = d ? `8766 · ${d.trade_date} · 持续运转` : "8766 · 四源汇流 · 持续运转";
|
||||
}
|
||||
C.Bus.on("runtime", updateCrumb);
|
||||
C.Bus.on("data", updateCrumb);
|
||||
|
||||
function enterShell() {
|
||||
C.bootTheme();
|
||||
C.applyReduced(C.REDUCE_MQ.matches);
|
||||
C.Poller.startAll();
|
||||
updateCrumb();
|
||||
C.Router.boot();
|
||||
}
|
||||
|
||||
boot();
|
||||
})();
|
||||
Reference in New Issue
Block a user