- 基于第七版稳定基线(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>
137 lines
7.7 KiB
CSS
137 lines
7.7 KiB
CSS
/* xiaobai-datahub 数据中枢后台 —— A/B/C 三方向共用组件层
|
||
只依赖 tokens.css 的变量;三个布局文件(flowline.css / ledger.css / strata.css)
|
||
只写各自独有的排布,不得重复定义这里已有的颜色/组件规则。 */
|
||
* { box-sizing: border-box; }
|
||
html, body { margin: 0; height: 100%; }
|
||
body {
|
||
background: var(--bg0); color: var(--t1); font-family: var(--font-cn);
|
||
font-size: var(--fs-body); -webkit-font-smoothing: antialiased;
|
||
}
|
||
.num, .mono { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
|
||
.mono { font-family: var(--font-mono); }
|
||
.muted { color: var(--t3); }
|
||
.error, .fail { color: var(--error); }
|
||
.ok { color: var(--alive); }
|
||
.warn { color: var(--act); }
|
||
button { font-family: var(--font-cn); cursor: pointer; }
|
||
|
||
/* ---------- 区段标签:mono 小字大写,是整套系统的刻度尺 ---------- */
|
||
.sec-label {
|
||
font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: .14em;
|
||
text-transform: uppercase; color: var(--t3);
|
||
}
|
||
|
||
/* ---------- 卡片:只靠底色阶梯和 1px 发丝线分层 ---------- */
|
||
.card { background: var(--bg1); border: 1px solid var(--line); border-radius: var(--r-card); }
|
||
|
||
/* ---------- 状态 chip ---------- */
|
||
.chip {
|
||
display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-small);
|
||
line-height: 1; padding: 4px 8px; border-radius: var(--r-chip); border: 1px solid var(--line);
|
||
color: var(--t2); white-space: nowrap;
|
||
}
|
||
.chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
|
||
.chip.ok { color: var(--alive); background: var(--alive-soft); border-color: transparent; }
|
||
.chip.err { color: var(--error); background: var(--error-soft); border-color: transparent; }
|
||
.chip.warn { color: var(--act); background: var(--act-soft); border-color: transparent; }
|
||
.chip.unknown { color: var(--unknown); }
|
||
.chip.unconfigured { color: var(--unconfigured); border-style: dashed; background: transparent; }
|
||
.chip.info { color: var(--action); background: var(--action-soft); border-color: transparent; }
|
||
|
||
/* ---------- 双灯:LINK=链路健康(低亮常亮),ACT=真实活动(事件时短闪) ---------- */
|
||
.lamp { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex: none; transition: background var(--dur-fast) var(--ease-out); }
|
||
.lamp.link.on { background: var(--alive); box-shadow: 0 0 6px rgba(60,203,154,.55); }
|
||
.lamp.link.off { background: var(--bg3); }
|
||
.lamp.link.err { background: var(--error); box-shadow: 0 0 6px rgba(229,83,75,.55); }
|
||
.lamp.act { background: var(--bg3); }
|
||
.lamp.act.on { background: var(--act); box-shadow: 0 0 7px rgba(240,167,60,.8); }
|
||
.lamp.act.on.err { background: var(--error); box-shadow: 0 0 7px rgba(229,83,75,.8); }
|
||
.lamp-tag { font-family: var(--font-mono); font-size: 9px; color: var(--t3); letter-spacing: .08em; }
|
||
|
||
/* ---------- 按钮:蓝色只给"可操作" ---------- */
|
||
.btn {
|
||
font-family: var(--font-cn); font-size: var(--fs-small); color: var(--action);
|
||
background: var(--action-soft); border: 1px solid transparent; border-radius: var(--r-chip);
|
||
padding: 5px 12px; cursor: pointer; transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
|
||
}
|
||
.btn:hover { background: var(--action); color: #fff; }
|
||
.btn:active { transform: translateY(1px); }
|
||
.btn:disabled { opacity: .5; cursor: not-allowed; }
|
||
.btn.ghost { background: transparent; border-color: var(--line); color: var(--t2); }
|
||
.btn.ghost:hover { border-color: var(--action); color: var(--action); background: transparent; }
|
||
.btn.ghost.active { border-color: var(--action); color: var(--action); background: var(--action-soft); }
|
||
.btn.danger { color: var(--error); background: var(--error-soft); }
|
||
.btn.danger:hover { background: var(--error); color: #fff; }
|
||
.btn.mini { padding: 3px 8px; font-size: 11px; }
|
||
|
||
/* ---------- 表格 ---------- */
|
||
table.grid { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
|
||
table.grid th {
|
||
text-align: left; font-weight: 500; color: var(--t3); font-size: 11px;
|
||
padding: 6px 10px; border-bottom: 1px solid var(--line); white-space: nowrap;
|
||
}
|
||
table.grid td { padding: 6px 10px; color: var(--t2); border-bottom: 1px solid var(--line-soft); white-space: nowrap; }
|
||
table.grid td.k { color: var(--t1); }
|
||
table.grid tr:last-child td { border-bottom: none; }
|
||
|
||
/* ---------- 登录 / 改密(三页共用,跟布局无关) ---------- */
|
||
.auth-panel {
|
||
max-width: 420px; margin: 12vh auto; padding: 30px 32px; border-radius: 14px;
|
||
background: var(--glass); border: 1px solid var(--line); backdrop-filter: blur(14px);
|
||
box-shadow: var(--shadow-pop);
|
||
}
|
||
.auth-panel h1 { margin: 0 0 6px; font-size: 21px; }
|
||
.auth-panel label { display: block; margin: 12px 0; font-size: 13px; color: var(--t2); }
|
||
.auth-panel input {
|
||
width: 100%; margin-top: 6px; padding: 9px 11px; border: 1px solid var(--line); border-radius: 8px;
|
||
background: var(--bg2); color: var(--t1); font-family: var(--font-cn);
|
||
}
|
||
.auth-panel input:focus { outline: 2px solid var(--action); outline-offset: 1px; }
|
||
.auth-panel .badge-sim { display: inline-block; margin-bottom: 10px; font-size: 11px; padding: 3px 10px; border-radius: 999px; letter-spacing: .1em; color: var(--act); border: 1px solid var(--act); opacity: .85; }
|
||
.auth-panel button[type="submit"] {
|
||
background: var(--action); color: #fff; border: 0; border-radius: 8px; padding: 8px 16px; font-size: 13px;
|
||
}
|
||
|
||
/* ---------- 顶栏:品牌 + 交易日 + 阶段 + A/B/C 切换 + 全局控制 ---------- */
|
||
#topbar {
|
||
height: 48px; display: flex; align-items: center; gap: 12px; padding: 0 16px;
|
||
border-bottom: 1px solid var(--line); background: var(--bg1); position: sticky; top: 0; z-index: 20;
|
||
}
|
||
#topbar .title { font-size: 15px; font-weight: 600; color: var(--t1); white-space: nowrap; }
|
||
#topbar .title em { font-style: normal; color: var(--action); }
|
||
#topbar .vdiv { width: 1px; height: 16px; background: var(--line); flex: none; }
|
||
#topbar .tdate { font-size: 13px; color: var(--t2); white-space: nowrap; }
|
||
#topbar .spacer { flex: 1; }
|
||
#topbar .who { font-size: 12px; color: var(--t2); white-space: nowrap; }
|
||
|
||
/* A/B/C 切换:一直可见,写清中文名称,当前页状态明确 */
|
||
#layoutSwitch {
|
||
display: flex; align-items: center; gap: 2px; padding: 3px; border-radius: 8px;
|
||
background: var(--bg2); border: 1px solid var(--line); flex: none;
|
||
}
|
||
#layoutSwitch button {
|
||
font-family: var(--font-cn); font-size: 12.5px; color: var(--t2); background: transparent;
|
||
border: 0; border-radius: 6px; padding: 6px 11px; white-space: nowrap;
|
||
transition: background var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out);
|
||
}
|
||
#layoutSwitch button .k { font-family: var(--font-mono); font-weight: 700; margin-right: 5px; opacity: .7; }
|
||
#layoutSwitch button[aria-current="page"] {
|
||
background: var(--action); color: #fff; box-shadow: var(--shadow-pop);
|
||
}
|
||
#layoutSwitch button[aria-current="page"] .k { opacity: 1; }
|
||
#layoutSwitch button:not([aria-current="page"]):hover { background: var(--bg3); color: var(--t1); }
|
||
|
||
/* ---------- 主内容容器 ---------- */
|
||
#page-root { min-height: calc(100vh - 48px); background: var(--bg0); }
|
||
.page-shell { padding: var(--sp4); max-width: 1584px; margin: 0 auto; }
|
||
|
||
/* 三方向表格容器统一命名以 Table 结尾:窄列/1024 档宁可局部横向滚动,
|
||
也不可撑破外层 grid/flex 轨道导致整页错位。 */
|
||
[id$="Table"] { overflow-x: auto; }
|
||
|
||
/* ---------- reduced-motion 全局兜底:不留半成品动效 ---------- */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
* { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
|
||
}
|
||
html.reduced * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
|