feat(HEL-560): 数据中枢接管数据源/模型池/会员,注册改一次性邀请码

主站
- 新增 m0006 invite_codes 迁移;注册强制邀请码(首个管理员除外),消码与建号
  同一事务,并发提交只有一个能成功
- 新增 /api/hub-admin/* 服务端点(共享 HUB_ADMIN_TOKEN,先于鉴权校验),供数据
  中枢桥接读写会话/密码/模型池/会员/邀请码,并提供供应商模型列表拉取
- 前端:注册表单加邀请码(桌面 login、index.html、移动端);「系统管理」改为
  「数据中枢」入口指向 8766,原模型池与会员管理分区移除,仅留「行情管理」;
  随之清理陈旧 CSS

数据中枢
- 取消独立账号:删除 hub_admin/hub_sessions 与登录、改密、锁定逻辑,改为校验
  主站 xiaobai_session,仅管理员可进,CSRF 由会话派生,危险操作二次确认走主站
- 控制台新增数据源凭证可编辑区(原有内容一项不删)、供应商制模型池(自动拉取
  /models,失败退回卡内手动录入)、会员管理与邀请码页
- 日夜双主题:颜色收敛为同名 token 换值,SVG 改用 inline style 以吃到变量

自测
- 主站 verify_baseline 通过(498 项);数据中枢 235 项通过
- tools/verify_datahub_console.py 端到端跑通两服务真实对话;
  tools/verify_datahub_console_ui.py 浏览器跑通门禁/凭证/模型池/会员/主题/1030 窄屏

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
施工员
2026-09-16 11:44:09 +08:00
co-authored by multica-agent
parent 3203574b6a
commit 3eaa36a8d5
69 changed files with 3678 additions and 1573 deletions
+268 -109
View File
@@ -8,7 +8,8 @@
}
/* ================= index.css 逐行照抄(去掉 tailwind 指令) ================= */
:root {
:root,
:root[data-theme="night"] {
--ink: #050810;
--panel: #0c1220;
--panel2: #111a2b;
@@ -21,6 +22,74 @@
--txt: #d7e1f0;
--mut: #8b9bb4;
--dim: #54637e;
/* HEL-560: neutrals that used to be repeated as literals across the
stylesheet and the inline styles in app.js. They are variables so the
day theme below can remap the whole console in one place. */
--txt-strong: #e8f1ff;
--txt-soft: #a9bcd6;
--faint: #3d4c66;
--off: #334155;
--off2: #475569;
--scroll-hover: #31406b;
--on-accent: #0a1420;
--rd-text: #fbd2d2;
--mint-text: #cdf5e4;
--line-soft: rgba(26, 37, 64, 0.55);
--grid-line: rgba(26, 37, 64, 0.35);
--scan-line: rgba(255, 255, 255, 0.022);
--panel-grad-a: rgba(17, 26, 43, 0.6);
--panel-grad-b: rgba(12, 18, 32, 0.9);
--drawer-grad-a: rgba(17, 26, 43, 0.98);
--drawer-grad-b: rgba(9, 13, 23, 0.99);
--chrome-bg: rgba(5, 8, 16, 0.88);
--chrome-bg-strong: rgba(5, 8, 16, 0.92);
--btn-bg: rgba(17, 26, 43, 0.5);
--mask: rgba(2, 4, 10, 0.58);
--shadow: rgba(0, 0, 0, 0.35);
--shadow-strong: rgba(0, 0, 0, 0.4);
--flash-ink: #e8fbff;
}
/* 日间主题(HEL-558 定稿映射):同一组变量名换一套值,accent 保持同色相族
并加深以满足白底对比度,不新增变量名、不新增组件。 */
:root[data-theme="day"] {
--ink: #eef2f8;
--panel: #ffffff;
--panel2: #f3f7fc;
--line: #d9e2ee;
--line2: #c3d0e2;
--cy: #0e7490;
--mint: #047857;
--amb: #b45309;
--rd: #dc2626;
--txt: #17263e;
--mut: #49607f;
--dim: #7d8da6;
--txt-strong: #0b1a2f;
--txt-soft: #33507a;
--faint: #93a3bb;
--off: #b6c3d6;
--off2: #a3b2c8;
--scroll-hover: #a9b8cd;
--on-accent: #ffffff;
--rd-text: #7f1d1d;
--mint-text: #065f46;
--line-soft: rgba(195, 208, 226, 0.65);
--grid-line: rgba(148, 168, 196, 0.28);
--scan-line: rgba(23, 38, 62, 0.014);
--panel-grad-a: rgba(255, 255, 255, 0.96);
--panel-grad-b: rgba(243, 247, 252, 0.98);
--drawer-grad-a: rgba(255, 255, 255, 0.99);
--drawer-grad-b: rgba(243, 247, 252, 0.99);
--chrome-bg: rgba(238, 242, 248, 0.9);
--chrome-bg-strong: rgba(238, 242, 248, 0.94);
--btn-bg: rgba(255, 255, 255, 0.75);
--mask: rgba(23, 38, 62, 0.32);
--shadow: rgba(23, 38, 62, 0.14);
--shadow-strong: rgba(23, 38, 62, 0.18);
--flash-ink: #06384a;
}
html, body, #root { height: 100%; }
@@ -51,8 +120,8 @@ body {
}
.bg-gridlines {
background-image:
linear-gradient(rgba(26, 37, 64, 0.35) 1px, transparent 1px),
linear-gradient(90deg, rgba(26, 37, 64, 0.35) 1px, transparent 1px);
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 48px 48px;
mask-image: radial-gradient(120% 90% at 50% 0%, black 30%, transparent 90%);
-webkit-mask-image: radial-gradient(120% 90% at 50% 0%, black 30%, transparent 90%);
@@ -60,8 +129,8 @@ body {
.scanlines {
background: repeating-linear-gradient(
to bottom,
rgba(255, 255, 255, 0.022) 0px,
rgba(255, 255, 255, 0.022) 1px,
var(--scan-line) 0px,
var(--scan-line) 1px,
transparent 1px,
transparent 3px
);
@@ -79,7 +148,7 @@ body {
/* ---------- panels ---------- */
.panel {
background: linear-gradient(180deg, rgba(17, 26, 43, 0.6), rgba(12, 18, 32, 0.9));
background: linear-gradient(180deg, var(--panel-grad-a), var(--panel-grad-b));
border: 1px solid var(--line);
border-radius: 6px;
position: relative;
@@ -106,8 +175,8 @@ body {
.led.slow { background: var(--amb); box-shadow: 0 0 6px 1px rgba(251,191,36,.7); }
.led.fail { background: var(--rd); box-shadow: 0 0 6px 1px rgba(248,113,113,.8); }
.led.rev { background: var(--cy); box-shadow: 0 0 6px 1px rgba(34,211,238,.7); }
.led.plan { background: #475569; box-shadow: none; }
.led.off { background: transparent; border: 1px dashed #475569; box-shadow: none; }
.led.plan { background: var(--off2); box-shadow: none; }
.led.off { background: transparent; border: 1px dashed var(--off2); box-shadow: none; }
.led.pulse::after {
content: ""; position: absolute; inset: -4px; border-radius: 9999px;
border: 1px solid currentColor; opacity: .6;
@@ -137,7 +206,7 @@ body {
/* ---------- flash on value change ---------- */
.flash { animation: flash .45s ease-out; }
@keyframes flash {
0% { background: rgba(34, 211, 238, 0.22); color: #e8fbff; }
0% { background: rgba(34, 211, 238, 0.22); color: var(--flash-ink); }
100% { background: transparent; }
}
@@ -175,7 +244,7 @@ body {
border: 1px solid var(--line2);
color: var(--mut);
padding: 4px 12px; border-radius: 4px;
background: rgba(17,26,43,.5);
background: var(--btn-bg);
transition: all .15s ease;
cursor: pointer;
font-family: inherit;
@@ -192,7 +261,7 @@ body {
color: var(--dim); font-weight: 500; text-align: left;
padding: 7px 10px; border-bottom: 1px solid var(--line);
}
.dtable td { padding: 7px 10px; border-bottom: 1px solid rgba(26,37,64,.55); vertical-align: middle; }
.dtable td { padding: 7px 10px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
.dtable tr:last-child td { border-bottom: none; }
.dtable tbody tr { transition: background .12s ease; }
.dtable tbody tr:hover { background: rgba(34,211,238,.04); }
@@ -201,7 +270,7 @@ body {
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: var(--ink); }
::-webkit-scrollbar-thumb { background: var(--line2); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #31406b; }
::-webkit-scrollbar-thumb:hover { background: var(--scroll-hover); }
* { scrollbar-width: thin; scrollbar-color: var(--line2) var(--ink); }
/* ---------- reduced motion ---------- */
@@ -226,66 +295,66 @@ body {
.scanlines.z50 { opacity: .7; }
/* ---------- 顶栏 ---------- */
.hdr { position: sticky; top: 0; z-index: 30; border-bottom: 1px solid #1a2540; background: rgba(5,8,16,.88); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.hdr { position: sticky; top: 0; z-index: 30; border-bottom: 1px solid var(--line); background: var(--chrome-bg); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.hdr-in { display: flex; align-items: center; gap: 12px; padding: 0 16px; height: 48px; }
.hdr-in > * { flex: none; }
.hdr-in > .flex1 { flex: 1; }
.brand { display: flex; flex-direction: column; line-height: 1; flex: none; white-space: nowrap; }
.brand .b1 { font-size: 13px; font-weight: 600; color: #e8f1ff; letter-spacing: .06em; }
.brand .b2 { font-size: 9px; color: #54637e; letter-spacing: .26em; margin-top: 4px; }
.vsep { width: 1px; height: 16px; background: #1a2540; margin: 0 4px; display: inline-block; flex: none; }
.brand .b1 { font-size: 13px; font-weight: 600; color: var(--txt-strong); letter-spacing: .06em; }
.brand .b2 { font-size: 9px; color: var(--dim); letter-spacing: .26em; margin-top: 4px; }
.vsep { width: 1px; height: 16px; background: var(--line); margin: 0 4px; display: inline-block; flex: none; }
.nav { display: flex; align-items: center; gap: 4px; margin-left: 8px; flex: none; }
.navbtn { position: relative; padding: 6px 14px; font-size: 12px; letter-spacing: .08em; border-radius: 4px; transition: all .15s; background: none; border: none; cursor: pointer; color: #8b9bb4; font-family: inherit; }
.navbtn:hover { color: #d7e1f0; background: rgba(34,211,238,.04); }
.navbtn.act { color: #22d3ee; background: rgba(34,211,238,.08); box-shadow: inset 0 0 0 1px rgba(34,211,238,.35), 0 0 14px rgba(34,211,238,.12); }
.navbtn { position: relative; padding: 6px 14px; font-size: 12px; letter-spacing: .08em; border-radius: 4px; transition: all .15s; background: none; border: none; cursor: pointer; color: var(--mut); font-family: inherit; }
.navbtn:hover { color: var(--txt); background: rgba(34,211,238,.04); }
.navbtn.act { color: var(--cy); background: rgba(34,211,238,.08); box-shadow: inset 0 0 0 1px rgba(34,211,238,.35), 0 0 14px rgba(34,211,238,.12); }
.navbtn .tri { margin-right: 4px; font-size: 9px; display: none; }
.navbtn.act .tri { display: inline; }
.navbtn .en { margin-left: 6px; font-size: 9px; letter-spacing: .14em; color: #3d4c66; }
.navbtn .en { margin-left: 6px; font-size: 9px; letter-spacing: .14em; color: var(--faint); }
.navbtn.act .en { color: rgba(34,211,238,.6); }
.flex1 { flex: 1; }
.mdtag { display: none; align-items: center; gap: 8px; border-radius: 3px; border: 1px solid rgba(34,211,238,.4); background: rgba(34,211,238,.07); padding: 4px 8px; font-size: 10px; letter-spacing: .14em; color: #22d3ee; }
.mdtag { display: none; align-items: center; gap: 8px; border-radius: 3px; border: 1px solid rgba(34,211,238,.4); background: rgba(34,211,238,.07); padding: 4px 8px; font-size: 10px; letter-spacing: .14em; color: var(--cy); }
@media (min-width: 768px) { .mdtag { display: flex; } }
@media (max-width: 1150px) { .mdtag { display: none; } }
.livespan { display: none; align-items: center; gap: 6px; font-size: 10px; letter-spacing: .18em; color: #34d399; }
.livespan { display: none; align-items: center; gap: 6px; font-size: 10px; letter-spacing: .18em; color: var(--mint); }
@media (min-width: 640px) { .livespan { display: flex; } }
.livedot { width: 6px; height: 6px; border-radius: 9999px; background: #34d399; box-shadow: 0 0 6px 1px rgba(52,211,153,.7); }
.clock { font-size: 12px; color: #8b9bb4; letter-spacing: .05em; white-space: nowrap; }
.clock .csep { margin: 0 6px; color: #243152; }
.clock .ct { color: #d7e1f0; }
.clock .cc { color: #22d3ee; }
.demotag { display: inline-flex; align-items: center; gap: 4px; border-radius: 3px; border: 1px solid rgba(251,191,36,.45); background: rgba(251,191,36,.06); color: #fbbf24; padding: 2px 6px; font-size: 10px; letter-spacing: .08em; white-space: nowrap; }
.livedot { width: 6px; height: 6px; border-radius: 9999px; background: var(--mint); box-shadow: 0 0 6px 1px rgba(52,211,153,.7); }
.clock { font-size: 12px; color: var(--mut); letter-spacing: .05em; white-space: nowrap; }
.clock .csep { margin: 0 6px; color: var(--line2); }
.clock .ct { color: var(--txt); }
.clock .cc { color: var(--cy); }
.demotag { display: inline-flex; align-items: center; gap: 4px; border-radius: 3px; border: 1px solid rgba(251,191,36,.45); background: rgba(251,191,36,.06); color: var(--amb); padding: 2px 6px; font-size: 10px; letter-spacing: .08em; white-space: nowrap; }
/* ---------- 雷达 logo ---------- */
.radar { position: relative; flex: none; border-radius: 9999px; border: 1px solid rgba(34,211,238,.5); overflow: hidden; box-shadow: 0 0 12px rgba(34,211,238,.25), inset 0 0 8px rgba(34,211,238,.15); }
.radar .sweep { position: absolute; inset: 0; }
.radar .ring1 { position: absolute; border-radius: 9999px; border: 1px solid rgba(34,211,238,.3); }
.radar .ring2 { position: absolute; border-radius: 9999px; border: 1px solid rgba(34,211,238,.2); }
.radar .center { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 3px; height: 3px; border-radius: 9999px; background: #22d3ee; box-shadow: 0 0 6px 2px rgba(34,211,238,.8); }
.radar .center { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 3px; height: 3px; border-radius: 9999px; background: var(--cy); box-shadow: 0 0 6px 2px rgba(34,211,238,.8); }
.radar .blip { position: absolute; width: 2.5px; height: 2.5px; border-radius: 9999px; }
/* ---------- EVENT TAPE ---------- */
.tape { position: fixed; bottom: 0; left: 0; right: 0; height: 32px; z-index: 40; display: flex; align-items: center; border-top: 1px solid #1a2540; background: rgba(5,8,16,.92); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); overflow: hidden; }
.tape-label { flex: none; display: flex; align-items: center; gap: 6px; padding: 0 12px; height: 100%; border-right: 1px solid #1a2540; font-size: 10px; letter-spacing: .18em; color: #54637e; }
.tape { position: fixed; bottom: 0; left: 0; right: 0; height: 32px; z-index: 40; display: flex; align-items: center; border-top: 1px solid var(--line); background: var(--chrome-bg-strong); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); overflow: hidden; }
.tape-label { flex: none; display: flex; align-items: center; gap: 6px; padding: 0 12px; height: 100%; border-right: 1px solid var(--line); font-size: 10px; letter-spacing: .18em; color: var(--dim); }
.tape-view { flex: 1; overflow: hidden; position: relative; }
.tape-item { display: inline-flex; align-items: center; gap: 8px; margin: 0 20px; font-size: 10.5px; letter-spacing: .04em; }
.tape-item .tt { color: #54637e; }
.tape-item .ts { color: #22d3ee; }
.tape-item .tm { color: #8b9bb4; }
.tape-item .tok { color: #34d399; }
.tape-item .tbad { color: #f87171; }
.tape-item .tsep { color: #1a2540; margin-left: 12px; }
.tape-item .tt { color: var(--dim); }
.tape-item .ts { color: var(--cy); }
.tape-item .tm { color: var(--mut); }
.tape-item .tok { color: var(--mint); }
.tape-item .tbad { color: var(--rd); }
.tape-item .tsep { color: var(--line); margin-left: 12px; }
/* ---------- 主区 ---------- */
.main { position: relative; z-index: 10; padding: 12px 16px 48px; max-width: 1720px; margin: 0 auto; }
.col { display: flex; flex-direction: column; gap: 12px; }
/* ---------- tag / pill ---------- */
.tag { display: inline-flex; align-items: center; gap: 4px; border-radius: 3px; border: 1px solid #243152; padding: 2px 6px; font-size: 10px; letter-spacing: .08em; line-height: 1.5; color: #8b9bb4; white-space: nowrap; }
.tag.cy { border-color: rgba(34,211,238,.45); color: #22d3ee; background: rgba(34,211,238,.06); }
.tag.amb { border-color: rgba(251,191,36,.45); color: #fbbf24; background: rgba(251,191,36,.06); }
.tag.rd { border-color: rgba(248,113,113,.45); color: #f87171; background: rgba(248,113,113,.07); }
.tag.mint { border-color: rgba(52,211,153,.45); color: #34d399; background: rgba(52,211,153,.06); }
.tag.dashed { border-style: dashed; border-color: #243152; color: #54637e; }
.tag { display: inline-flex; align-items: center; gap: 4px; border-radius: 3px; border: 1px solid var(--line2); padding: 2px 6px; font-size: 10px; letter-spacing: .08em; line-height: 1.5; color: var(--mut); white-space: nowrap; }
.tag.cy { border-color: rgba(34,211,238,.45); color: var(--cy); background: rgba(34,211,238,.06); }
.tag.amb { border-color: rgba(251,191,36,.45); color: var(--amb); background: rgba(251,191,36,.06); }
.tag.rd { border-color: rgba(248,113,113,.45); color: var(--rd); background: rgba(248,113,113,.07); }
.tag.mint { border-color: rgba(52,211,153,.45); color: var(--mint); background: rgba(52,211,153,.06); }
.tag.dashed { border-style: dashed; border-color: var(--line2); color: var(--dim); }
.minidot { width: 6px; height: 6px; border-radius: 9999px; display: inline-block; }
.spill { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; white-space: nowrap; }
@@ -293,21 +362,21 @@ body {
.hero { padding: 16px 20px; display: flex; flex-wrap: wrap; align-items: center; gap: 16px 32px; }
.hero-l { display: flex; flex-direction: column; gap: 8px; min-width: 300px; }
.hero-title { font-size: 22px; font-weight: 600; letter-spacing: .025em; }
.hero-title .dot { color: #54637e; margin: 0 8px; }
.hero-title .dot { color: var(--dim); margin: 0 8px; }
.hero-tags { display: flex; align-items: center; gap: 8px; }
.hero-sub { font-size: 11px; color: #54637e; }
.hero-sub .sep { margin: 0 8px; color: #243152; }
.hero-sub { font-size: 11px; color: var(--dim); }
.hero-sub .sep { margin: 0 8px; color: var(--line2); }
.kpis { display: flex; align-items: flex-start; gap: 32px; padding-right: 8px; }
.kpi { display: flex; flex-direction: column; gap: 4px; }
.kpi .kv { font-size: 26px; line-height: 1; font-weight: 600; letter-spacing: -.025em; color: #e8f1ff; }
.kpi .ks { font-size: 10px; color: #54637e; }
.kpi .kv { font-size: 26px; line-height: 1; font-weight: 600; letter-spacing: -.025em; color: var(--txt-strong); }
.kpi .ks { font-size: 10px; color: var(--dim); }
/* ---------- 数据集卡 ---------- */
.ds-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
@media (min-width: 640px) { .ds-grid { grid-template-columns: repeat(5, 1fr); } }
@media (min-width: 1280px) { .ds-grid { grid-template-columns: repeat(10, 1fr); } }
.dsc { border-radius: 5px; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; transition: all .15s; cursor: default; border: 1px solid #1a2540; }
.dsc:hover { transform: translateY(-1px); border-color: #243152; }
.dsc { border-radius: 5px; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; transition: all .15s; cursor: default; border: 1px solid var(--line); }
.dsc:hover { transform: translateY(-1px); border-color: var(--line2); }
.dsc.fail { border-color: rgba(248,113,113,.55); box-shadow: 0 0 18px rgba(248,113,113,.14); }
.dsc.fail:hover { border-color: rgba(248,113,113,.55); }
.dsc.slow { border-color: rgba(251,191,36,.4); }
@@ -315,8 +384,8 @@ body {
.dsc.review { border-color: rgba(34,211,238,.45); box-shadow: 0 0 14px rgba(34,211,238,.1); }
.dsc.review:hover { border-color: rgba(34,211,238,.45); }
.dsc-top { display: flex; align-items: center; justify-content: space-between; }
.dsc-name { font-size: 12px; font-weight: 500; color: #e8f1ff; }
.dsc-note { font-size: 10px; color: #54637e; line-height: 1.6; min-height: 28px; }
.dsc-name { font-size: 12px; font-weight: 500; color: var(--txt-strong); }
.dsc-note { font-size: 10px; color: var(--dim); line-height: 1.6; min-height: 28px; }
/* ---------- 总览下半网格 ---------- */
.ov-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@@ -325,8 +394,8 @@ body {
.legend { display: flex; align-items: center; gap: 12px; font-size: 10px; letter-spacing: .1em; }
.legend span { display: flex; align-items: center; gap: 4px; }
.jit { padding: 0 4px; border-radius: 3px; }
.obs-note { font-size: 10px; color: #fbbf24; }
.site-foot { padding: 10px 12px; border-top: 1px solid #1a2540; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; font-size: 10px; color: #54637e; }
.obs-note { font-size: 10px; color: var(--amb); }
.site-foot { padding: 10px 12px; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; font-size: 10px; color: var(--dim); }
.tbtn.mini { padding: 3px 8px; }
/* ---------- 时间线 ---------- */
@@ -336,35 +405,35 @@ body {
.now-r { animation: nowR 1.6s ease-in-out infinite; }
/* ---------- 异常卡 ---------- */
.inc { display: flex; align-items: center; gap: 12px; border-radius: 4px; border: 1px solid #1a2540; border-left-width: 3px; padding: 10px 12px; }
.inc.fail { border-left-color: #f87171; background: rgba(248,113,113,.05); }
.inc.slow { border-left-color: #fbbf24; background: rgba(251,191,36,.04); }
.inc.off { border-left-color: #334155; background: transparent; border-style: dashed; border-color: #243152; border-left-width: 3px; border-left-style: solid; border-left-color: #334155; }
.inc-title { font-size: 12px; color: #e8f1ff; }
.inc-meta { font-size: 10px; color: #54637e; margin-top: 2px; }
.inc { display: flex; align-items: center; gap: 12px; border-radius: 4px; border: 1px solid var(--line); border-left-width: 3px; padding: 10px 12px; }
.inc.fail { border-left-color: var(--rd); background: rgba(248,113,113,.05); }
.inc.slow { border-left-color: var(--amb); background: rgba(251,191,36,.04); }
.inc.off { border-left-color: var(--off); background: transparent; border-style: dashed; border-color: var(--line2); border-left-width: 3px; border-left-style: solid; border-left-color: var(--off); }
.inc-title { font-size: 12px; color: var(--txt-strong); }
.inc-meta { font-size: 10px; color: var(--dim); margin-top: 2px; }
/* ---------- Sources 页 ---------- */
.strip { padding: 12px 20px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 24px; }
.src-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 12px 16px; border-bottom: 1px solid #1a2540; }
.src-name { font-size: 15px; font-weight: 600; color: #e8f1ff; letter-spacing: .025em; }
.src-role { font-size: 10px; color: #54637e; letter-spacing: .1em; }
.src-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; padding: 8px 16px; border-bottom: 1px solid #1a2540; font-size: 11px; color: #8b9bb4; }
.src-meta .msep { color: #243152; }
.src-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
.src-name { font-size: 15px; font-weight: 600; color: var(--txt-strong); letter-spacing: .025em; }
.src-role { font-size: 10px; color: var(--dim); letter-spacing: .1em; }
.src-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; padding: 8px 16px; border-bottom: 1px solid var(--line); font-size: 11px; color: var(--mut); }
.src-meta .msep { color: var(--line2); }
.mx-grid { display: grid; grid-template-columns: 1fr; gap: 16px 24px; padding: 16px; }
@media (min-width: 768px) { .mx-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1280px) { .mx-grid { grid-template-columns: repeat(3, 1fr); } }
.mx-grid > div { min-width: 0; }
.mx-grid table { table-layout: auto; max-width: 100%; }
.offbox { border-radius: 4px; border: 1px dashed #243152; padding: 24px 16px; text-align: center; font-size: 11px; color: #54637e; }
.offbox { border-radius: 4px; border: 1px dashed var(--line2); padding: 24px 16px; text-align: center; font-size: 11px; color: var(--dim); }
.rsv-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 768px) { .rsv-grid { grid-template-columns: repeat(3, 1fr); } }
.rsv { border-radius: 6px; border: 1px dashed #243152; padding: 16px; display: flex; align-items: center; gap: 12px; color: #54637e; }
.rsv { border-radius: 6px; border: 1px dashed var(--line2); padding: 16px; display: flex; align-items: center; gap: 12px; color: var(--dim); }
@keyframes spin { to { transform: rotate(360deg); } }
.animate-spin { display: inline-block; animation: spin 1s linear infinite; }
/* ---------- Lineage 页 ---------- */
.lin-cols { display: flex; justify-content: space-between; padding: 4px 24px 0; font-size: 10px; letter-spacing: .2em; color: #54637e; }
.lin-hint { position: absolute; left: 24px; bottom: 4px; font-size: 10px; color: #54637e; letter-spacing: .1em; }
.lin-cols { display: flex; justify-content: space-between; padding: 4px 24px 0; font-size: 10px; letter-spacing: .2em; color: var(--dim); }
.lin-hint { position: absolute; left: 24px; bottom: 4px; font-size: 10px; color: var(--dim); letter-spacing: .1em; }
@keyframes gnodePulse { 0%,100% { opacity: 1; } 50% { opacity: .55; } }
.gnode-pulse { animation: gnodePulse 1.8s ease-in-out infinite; }
/* 真实调用事件脉冲(HEL-529 数据修正第 6 项):血缘图来源节点在真实调用
@@ -387,10 +456,10 @@ table.dtable tbody tr:nth-child(3n) .spark-end { animation-delay: -.35s; }
.ds-grid .dsc:nth-child(2n) .spark-end { animation-delay: -.5s; }
.ds-grid .dsc:nth-child(3n) .spark-end { animation-delay: -.9s; }
.ds-grid .dsc:nth-child(5n) .spark-end { animation-delay: -.2s; }
.lin-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid #1a2540; }
.lin-sel, .lin-q { background: #0c1220; border: 1px solid #243152; border-radius: 4px; font-size: 11px; color: #8b9bb4; padding: 6px 8px; outline: none; font-family: inherit; }
.lin-q { color: #d7e1f0; padding: 6px 10px; width: 220px; }
.lin-q::placeholder { color: #3d4c66; }
.lin-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--line); }
.lin-sel, .lin-q { background: var(--panel); border: 1px solid var(--line2); border-radius: 4px; font-size: 11px; color: var(--mut); padding: 6px 8px; outline: none; font-family: inherit; }
.lin-q { color: var(--txt); padding: 6px 10px; width: 220px; }
.lin-q::placeholder { color: var(--faint); }
.lin-sel:focus, .lin-q:focus { border-color: rgba(34,211,238,.5); }
.lin-scroll { overflow-x: auto; }
/* HEL-529:宽档下由 app.js 的 fitLineageTable() 按剩余可视高度写入
@@ -399,7 +468,7 @@ table.dtable tbody tr:nth-child(3n) .spark-end { animation-delay: -.35s; }
.lin-scroll { overflow-y: auto; }
.lin-table { min-width: 1080px; }
.lin-table-n { display: none; }
.lin-table-n .sub { display: block; font-size: 10px; color: #54637e; margin-top: 2px; }
.lin-table-n .sub { display: block; font-size: 10px; color: var(--dim); margin-top: 2px; }
/* ---------- Lineage 页压缩(仅血缘页,其他页不受影响) ---------- */
#mainEl[data-page="lineage"] .col { gap: 8px; }
#mainEl[data-page="lineage"] { padding-bottom: 36px; }
@@ -407,13 +476,122 @@ table.dtable tbody tr:nth-child(3n) .spark-end { animation-delay: -.35s; }
#mainEl[data-page="lineage"] .lin-filter { padding: 6px 12px; }
#mainEl[data-page="lineage"] .dtable th { padding: 5px 10px; }
#mainEl[data-page="lineage"] .dtable td { padding: 4px 10px; }
/* =================================================================
HEL-560 新增组件:表单原子、数据源凭证可编辑区、模型池供应商卡、
会员/邀请码表、主站会话门禁。全部复用上方 token,日夜两套主题
靠同名变量换值,不另写一套覆盖样式。
================================================================= */
/* ---------- 表单原子(按钮 / 字段 / 提示) ---------- */
.pbtn {
font-size: 11px; letter-spacing: .08em; border: 1px solid var(--cy);
color: var(--on-accent); background: var(--cy); padding: 6px 14px; border-radius: 4px;
cursor: pointer; font-family: inherit; font-weight: 600; transition: filter .15s ease;
}
.pbtn:hover { filter: brightness(1.08); }
.pbtn:disabled { opacity: .45; cursor: not-allowed; filter: none; }
.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field > .lab { margin: 0; }
.field input, .field select, .cred-line input, .vend-manual input, .dtoolbar select {
background: var(--panel); border: 1px solid var(--line2); border-radius: 4px;
color: var(--txt); font-family: inherit; font-size: 12px; padding: 7px 9px; outline: none;
min-width: 0; width: 100%;
}
.field input:focus, .field select:focus, .cred-line input:focus, .vend-manual input:focus {
border-color: var(--cy);
}
.field input:disabled, .field select:disabled { opacity: .55; cursor: not-allowed; }
.field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; }
.grid-1 { grid-template-columns: minmax(0, 1fr); }
.form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.form-hint { font-size: 10.5px; color: var(--dim); letter-spacing: .04em; line-height: 1.6; }
.muted { font-size: 11px; color: var(--dim); line-height: 1.6; margin: 0; }
/* ---------- 凭证可编辑区(数据源卡内,只增不删) ---------- */
.cred-box { padding: 12px 16px; border-bottom: 1px solid var(--line); display: flex; flex-direction: column; gap: 7px; }
.cred-box:last-child { border-bottom: none; }
.cred-flag { color: var(--cy); letter-spacing: .06em; }
.cred-line { display: flex; align-items: center; gap: 10px; }
.cred-line input { flex: 1 1 auto; }
.cred-acts { display: flex; align-items: center; gap: 8px; flex: none; }
.cred-feet { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 16px; }
/* ---------- 模型池:供应商卡 ---------- */
.vend-hd { flex-wrap: wrap; gap: 8px; justify-content: flex-start; }
.vend-name { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600; color: var(--txt-strong); letter-spacing: .02em; }
.vend-body { display: flex; flex-direction: column; gap: 9px; }
/* 样图:地址 / Key 并排,操作按钮贴在同一行右端并与输入框底边对齐 */
.vend-line { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 14px; }
.vend-line > .field { flex: 1 1 260px; }
.vend-acts { display: flex; align-items: center; gap: 8px; flex: none; padding-top: 19px; }
.route-line { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; }
.route-line > .field { flex: 0 1 260px; }
.vend-note { display: flex; align-items: center; gap: 6px; font-size: 10.5px; color: var(--mut); letter-spacing: .04em; }
.vend-note.bad { color: var(--rd); }
.vend-fetch-list { display: flex; flex-direction: column; gap: 5px; padding: 2px 0 2px 2px; }
.vend-pick { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--txt); }
.vend-pick input { accent-color: var(--cy); }
.vend-pick .added { font-size: 10px; color: var(--dim); letter-spacing: .06em; }
.vend-manual {
display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px;
padding: 10px 12px; border: 1px dashed var(--line2); border-radius: 4px;
}
.vend-manual > .lab { margin: 0 0 4px; }
.vend-manual .field { flex: 1 1 220px; }
.model-row {
display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
padding: 9px 12px; border: 1px solid var(--line); border-radius: 4px; background: var(--btn-bg);
}
.model-row .mr-main { flex: 1 1 220px; min-width: 0; }
.model-row .mr-name { font-size: 12px; color: var(--txt-strong); font-weight: 600; display: flex; align-items: center; gap: 7px; }
.model-row .mr-sub { font-size: 10px; color: var(--cy); margin-top: 3px; word-break: break-all; }
.model-row .mr-test { display: inline-flex; align-items: center; gap: 8px; flex: none; }
.model-row .mr-state { font-size: 11px; color: var(--mut); white-space: nowrap; }
/* ---------- 会员 / 邀请码 ---------- */
.wide-scroll { overflow-x: auto; }
.wide-table { min-width: 720px; }
.cell-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.invite-code { letter-spacing: .06em; color: var(--txt-strong); white-space: nowrap; }
.invite-note { font-size: 10px; color: var(--dim); }
.table-foot {
display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
gap: 4px 16px; padding: 8px 16px; border-top: 1px solid var(--line); font-size: 10.5px; color: var(--dim);
}
/* ---------- 主站会话门禁 ---------- */
.gate-panel { width: 100%; max-width: 420px; padding: 28px 26px 24px; text-align: left; }
.gate-panel h1 { font-size: 16px; font-weight: 600; color: var(--txt-strong); margin: 0 0 8px; letter-spacing: .02em; }
.gate-panel p { font-size: 11.5px; color: var(--mut); line-height: 1.8; margin: 0 0 18px; }
.gate-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.gate-actions .pbtn, .gate-actions .tbtn { text-decoration: none; display: inline-flex; align-items: center; }
@media (max-width: 1100px) {
.field-row { grid-template-columns: minmax(0, 1fr); }
.form-actions { width: 100%; }
/* 样图 1030:输入框独占一行,按钮整排落到下一行,不再与输入框挤同一行 */
.cred-line { flex-direction: column; align-items: stretch; }
.cred-acts { width: 100%; }
.cred-acts .tbtn, .cred-acts .pbtn { flex: 1 1 auto; text-align: center; }
.cred-feet { flex-direction: column; align-items: flex-start; }
.vend-hd { padding: 10px 12px; }
.vend-line, .route-line { flex-direction: column; align-items: stretch; }
.vend-acts { width: 100%; padding-top: 0; }
.vend-acts .tbtn, .vend-acts .pbtn { flex: 1 1 auto; text-align: center; }
.route-line > .field { flex: 1 1 auto; }
.model-row { flex-direction: column; align-items: stretch; }
.model-row .mr-test { justify-content: flex-start; }
.model-row > .tbtn { align-self: flex-start; }
.vend-manual { flex-direction: column; align-items: stretch; }
.vend-manual > .tbtn { align-self: flex-start; }
.src-head { row-gap: 6px; }
.hdr-in { flex-wrap: wrap; height: auto; padding-top: 6px; padding-bottom: 6px; row-gap: 6px; }
.hdr-in > .flex1 { flex: 1 1 100%; height: 0; }
.mdtag { display: flex; }
.lin-table { display: none; }
.lin-table-n { display: table; width: 100%; }
}
.row-fail { background: rgba(248,113,113,.045); }
.row-slow { background: rgba(251,191,36,.03); }
.row-off { opacity: .55; }
@@ -424,55 +602,36 @@ table.dtable tbody tr:nth-child(3n) .spark-end { animation-delay: -.35s; }
/* =================================================================
以下为 HEL-529 第十二版生产实现补充样式(非 Kimi 打样内容):
登录/改密视图、抽屉(调度任务/盘后发布/审计合并入口)、危险操作弹层。
门禁视图、抽屉(调度任务/盘后发布/审计合并入口)、危险操作弹层。
全部复用上方 token--ink/--panel/--line/--cy/--mint/--amb/--rd 等),
不引入新色值、不新增动效缓动函数。
HEL-560:原独立登录/改密表单随账号体系下线一并移除,仅留门禁复用的
.auth-wrap 居中容器。
================================================================= */
/* ---------- 登录 / 改密(保留原有表单结构,套用新 token ---------- */
.auth-wrap {
min-height: 100%; display: flex; align-items: center; justify-content: center;
padding: 24px;
}
.auth-panel {
width: 100%; max-width: 380px; padding: 28px 26px 24px;
}
.auth-panel h1 { font-size: 16px; font-weight: 600; color: #e8f1ff; margin: 0 0 6px; letter-spacing: .02em; }
.auth-panel .muted { font-size: 11px; color: var(--dim); line-height: 1.6; margin: 0 0 18px; }
.auth-panel form { display: flex; flex-direction: column; gap: 12px; }
.auth-panel label { display: flex; flex-direction: column; gap: 6px; font-size: 11px; color: var(--mut); letter-spacing: .04em; }
.auth-panel input {
background: #0c1220; border: 1px solid var(--line2); border-radius: 4px;
color: var(--txt); font-family: inherit; font-size: 13px; padding: 8px 10px; outline: none;
}
.auth-panel input:focus { border-color: rgba(34,211,238,.55); box-shadow: 0 0 0 2px rgba(34,211,238,.12); }
.auth-panel button[type="submit"] {
margin-top: 4px; font-size: 12px; letter-spacing: .08em; border: 1px solid rgba(34,211,238,.5);
color: #0a1420; background: var(--cy); padding: 9px 12px; border-radius: 4px; cursor: pointer;
font-family: inherit; font-weight: 600; transition: filter .15s ease;
}
.auth-panel button[type="submit"]:hover { filter: brightness(1.08); }
.auth-panel .error { color: var(--rd); font-size: 11px; margin: 2px 0 0; }
/* ---------- 抽屉(调度任务 / 盘后发布 / 审计) ---------- */
.drawer-mask {
position: fixed; inset: 0; background: rgba(2,4,10,.55); z-index: 60;
position: fixed; inset: 0; background: var(--mask); z-index: 60;
opacity: 0; pointer-events: none; transition: opacity .2s ease-out;
}
.drawer-mask.open { opacity: 1; pointer-events: auto; }
.drawer {
position: fixed; top: 0; right: 0; bottom: 0; width: min(720px, 92vw);
background: linear-gradient(180deg, rgba(17,26,43,.98), rgba(9,13,23,.99));
background: linear-gradient(180deg, var(--drawer-grad-a), var(--drawer-grad-b));
border-left: 1px solid var(--line); z-index: 61; display: flex; flex-direction: column;
transform: translateX(100%); transition: transform .28s cubic-bezier(.16,1,.3,1);
box-shadow: -18px 0 40px rgba(0,0,0,.4);
box-shadow: -18px 0 40px var(--shadow-strong);
}
.drawer-mask.open .drawer { transform: translateX(0); }
.drawer-hd {
display: flex; align-items: center; gap: 10px; padding: 14px 18px;
border-bottom: 1px solid var(--line); flex: none;
}
.drawer-hd .ttl { font-size: 13px; font-weight: 600; color: #e8f1ff; letter-spacing: .04em; }
.drawer-hd .ttl { font-size: 13px; font-weight: 600; color: var(--txt-strong); letter-spacing: .04em; }
.drawer-hd .sub { font-size: 10px; color: var(--dim); }
.drawer-tabs { display: flex; gap: 4px; padding: 10px 18px 0; flex: none; }
.drawer-tab {
@@ -492,13 +651,13 @@ table.dtable tbody tr:nth-child(3n) .spark-end { animation-delay: -.35s; }
.drawer-section .lab { margin-bottom: 8px; }
.dtoolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.dtoolbar input {
background: #0c1220; border: 1px solid var(--line2); border-radius: 4px; color: var(--txt);
background: var(--panel); border: 1px solid var(--line2); border-radius: 4px; color: var(--txt);
font-family: inherit; font-size: 11px; padding: 5px 8px; outline: none;
}
/* ---------- 危险操作确认弹层 ---------- */
.modal-mask {
position: fixed; inset: 0; background: rgba(2,4,10,.6); z-index: 70;
position: fixed; inset: 0; background: var(--mask); z-index: 70;
display: none; align-items: center; justify-content: center; padding: 20px;
}
.modal-mask.open { display: flex; }
@@ -510,33 +669,33 @@ table.dtable tbody tr:nth-child(3n) .spark-end { animation-delay: -.35s; }
.modal-box p { margin: 0 0 12px; font-size: 11px; color: var(--mut); line-height: 1.7; }
.modal-box label { display: flex; flex-direction: column; gap: 5px; font-size: 11px; color: var(--mut); margin-bottom: 10px; }
.modal-box input {
background: #0c1220; border: 1px solid var(--line2); border-radius: 4px; color: var(--txt);
background: var(--panel); border: 1px solid var(--line2); border-radius: 4px; color: var(--txt);
font-family: inherit; font-size: 12px; padding: 7px 9px; outline: none;
}
.modal-box input:focus { border-color: rgba(248,113,113,.55); }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 6px; }
.modal-actions .tbtn.danger { color: var(--rd); border-color: rgba(248,113,113,.5); }
.modal-actions .tbtn.danger:hover { color: #fff; background: rgba(248,113,113,.18); box-shadow: 0 0 10px rgba(248,113,113,.2); }
.modal-actions .tbtn.danger:hover { color: var(--txt-strong); background: rgba(248,113,113,.18); box-shadow: 0 0 10px rgba(248,113,113,.2); }
.modal-err { color: var(--rd); font-size: 11px; margin: 6px 0 0; min-height: 14px; }
/* ---------- 小工具 ---------- */
.opbtn {
font-size: 10px; letter-spacing: .06em; border: 1px solid var(--line2); color: var(--mut);
background: rgba(17,26,43,.5); padding: 3px 9px; border-radius: 3px; cursor: pointer; font-family: inherit;
background: var(--btn-bg); padding: 3px 9px; border-radius: 3px; cursor: pointer; font-family: inherit;
}
.opbtn:hover { color: var(--cy); border-color: rgba(34,211,238,.5); }
.opbtn.danger { color: var(--rd); border-color: rgba(248,113,113,.4); }
.opbtn.danger:hover { color: #fff; background: rgba(248,113,113,.14); }
.opbtn.danger:hover { color: var(--txt-strong); background: rgba(248,113,113,.14); }
.empty-hint { text-align: center; padding: 22px 0; color: var(--dim); font-size: 11px; }
.toast {
position: fixed; left: 50%; bottom: 44px; transform: translate(-50%, 8px); z-index: 80;
background: var(--panel2); border: 1px solid var(--line2); border-radius: 5px; padding: 9px 16px;
font-size: 12px; color: var(--txt); opacity: 0; pointer-events: none; transition: opacity .18s ease-out, transform .18s ease-out;
box-shadow: 0 8px 24px rgba(0,0,0,.35);
box-shadow: 0 8px 24px var(--shadow);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
.toast.err { border-color: rgba(248,113,113,.5); color: #fbd2d2; }
.toast.ok { border-color: rgba(52,211,153,.5); color: #cdf5e4; }
.toast.err { border-color: rgba(248,113,113,.5); color: var(--rd-text); }
.toast.ok { border-color: rgba(52,211,153,.5); color: var(--mint-text); }
/* 减少动态:抽屉/弹层的滑入过渡也一并归零,保持与其余过渡同一门禁 */
.reduce-motion .drawer,