From 4690bb3cbcb2e5f11a8a4aefdad25e419c81f622 Mon Sep 17 00:00:00 2001 From: argszero Date: Fri, 11 Sep 2026 17:16:41 +0800 Subject: [PATCH] feat(ui): add shared component layer (stat-card/pill/tag/toolbar/bar-list) and define missing button variants Migrate the prototype's component vocabulary into the live stylesheet and align the DOM producers with it, as the third step of the UI redesign (rant 2026-09-11T16:23:43, rollout order step 3: shared component layer). - buttons: define .btn-secondary and .btn-sm, which markup already used but the stylesheet never declared (they fell back to unstyled buttons) - components: add stat-card, pill (ok/warn/danger/muted/accent), tag, trend, bar-list/bar-row/bar-track/bar-fill, toolbar, search, empty, card-sub, split, spread, legend, check-line - rename producers for one vocabulary: search-box -> search (5 HTML + 2 JS sites), stat -> stat-card, badge -> pill/tag, bar-label/bar -> bar-top/ bar-track/bar-fill - keep .badge as a compatibility alias so out-of-tree markup keeps working - consolidate the .toolbar .search / .search rule pairs and update the responsive section for the renamed classes - prototype component coverage is now 48/48 --- ui/css/style.css | 188 ++++++++++++++++++++++++++++++++++++++++------- ui/index.html | 10 +-- ui/js/app.js | 35 +++++---- 3 files changed, 185 insertions(+), 48 deletions(-) diff --git a/ui/css/style.css b/ui/css/style.css index 55a41b2..6c2a8ba 100644 --- a/ui/css/style.css +++ b/ui/css/style.css @@ -150,6 +150,9 @@ body { } .btn-primary:hover { background: color-mix(in oklch, var(--accent) 86%, var(--fg)); } .btn-ghost { background: transparent; } +/* 次级按钮(原型 .btn-secondary):透明底 + 描边,hover 转 accent 边与柔和底 */ +.btn-secondary { background: transparent; color: var(--text); } +.btn-secondary:hover { border-color: color-mix(in oklch, var(--accent) 55%, var(--border)); background: var(--accent-soft); } .btn-danger { background: transparent; border-color: transparent; @@ -157,6 +160,8 @@ body { } .btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); } .btn-block { width: 100%; } +/* 小尺寸按钮(原型 .btn-sm):侧边栏退出登录等紧凑位置使用 */ +.btn-sm { padding: 5px 11px; font-size: 12.5px; } .btn:disabled { opacity: .5; cursor: not-allowed; pointer-events: none; } /* 按钮按压反馈(rant 18:06:09 E::active 轻微下压;disabled 不触发) */ .btn:active:not(:disabled) { transform: scale(0.98); } @@ -334,23 +339,9 @@ select.input option { background: var(--bg-card); } gap: 14px; margin-bottom: 4px; } -.stat { - background: var(--bg-card); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 16px 18px; - transition: transform .15s ease, border-color .18s ease, box-shadow .18s ease; -} -/* 统计卡 hover 微抬升(rant 18:06:09 E) */ -.stat:hover { - transform: translateY(-2px); - border-color: var(--card-hover-border); - box-shadow: var(--card-shadow); -} -.stat .label { color: var(--text-mute); font-size: 12px; } -.stat .value { font-size: 24px; font-weight: 700; margin-top: 4px; } -.stat .sub { color: var(--text-dim); font-size: 12px; } -.stat.accent .value { color: var(--accent-text); } +/* 统计卡(rant 18:06:09 E hover 微抬升)——原型 .stat-card 为规范名, + 定义见本节末尾「通用组件层」;此处保留 .sub 副说明的历史写法 */ +.stat-card .sub { color: var(--text-dim); font-size: 12px; } /* 交易汇总条(rant 20:39:30 B:紧凑 inline 条,随 tab/筛选联动) */ .tx-summary { display: flex; flex-wrap: wrap; gap: 28px; padding: 12px 16px 10px; border-bottom: 1px solid var(--border); } @@ -438,6 +429,8 @@ select.input option { background: var(--bg-card); } .density-options .check { display: flex; align-items: center; gap: 6px; flex-direction: row; color: var(--text-dim); font-size: 13px; cursor: pointer; } .density-options input { accent-color: var(--accent); } +/* 状态徽标:规范类为原型 .pill-* / .tag(定义见「通用组件层」), + .badge 保留为兼容别名(带语义色点),外部/历史标记仍可直接使用 */ .badge { display: inline-flex; align-items: center; @@ -464,9 +457,13 @@ select.input option { background: var(--bg-card); } .toolbar .input { max-width: 240px; } .toolbar-count { align-self: center; color: var(--text-mute); font-size: 12px; margin-left: auto; } -/* 搜索增强(rant 18:06:09 D:清空 × 按钮 + 关键词高亮) */ -.search-box { position: relative; display: block; } -.search-box .input { padding-right: 30px; } +/* 搜索增强(rant 18:06:09 D:清空 × 按钮 + 关键词高亮; + rant 2026-09-11T16:23:43:类名对齐原型 .search) */ +.search { position: relative; display: block; } +.search .input { padding-right: 30px; } +/* 工具栏内搜索框按原型弹性伸缩(原型 .toolbar .search) */ +.toolbar .search { flex: 1; min-width: 180px; max-width: 320px; } +.toolbar .search .input { max-width: none; } .search-clear { position: absolute; right: 6px; @@ -619,13 +616,7 @@ mark { .mini-item.net .t { color: var(--accent-text); } .bar-list { display: flex; flex-direction: column; gap: 12px; } -.bar-row .bar-label { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 4px; } -.bar-row .bar-label .n { color: var(--text-mute); } -.bar { - height: 8px; border-radius: 999px; - background: var(--bg-hover); overflow: hidden; -} -.bar i { display: block; height: 100%; background: linear-gradient(90deg, var(--accent), color-mix(in oklch, var(--accent) 55%, var(--ok))); border-radius: 999px; } +.bar-row .bar-top .n { color: var(--text-mute); } /* 空状态(rant 15:50:05 A.4:图标 + 文案 + 可选行动按钮) */ .empty-state { @@ -1029,6 +1020,144 @@ mark { } @keyframes spin { to { transform: rotate(360deg); } } +/* ============================================================ + 通用组件层(rant 2026-09-11T16:23:43 第三节) + 原型 docs/prototype/aitokenpool-console.html 的可复用类在此落成, + PR4–PR6 的视图只需组装、不再各自发明样式。 + 命名对齐原型(pill/tag/stat-card/trend/bar-track…), + 线上历史类名保留兼容别名,避免同一次改动里做 DOM 迁移。 + ============================================================ */ + +/* ---- 徽标 / 标签 ---- */ +/* 原型 .pill 系列:无描边、柔和底色、等宽字体(状态类徽标的新形态) */ +.pill { + display: inline-flex; align-items: center; gap: 5px; + padding: 2px 9px; border-radius: 999px; + font-size: 11.5px; font-weight: 600; + font-family: var(--font-mono); letter-spacing: .03em; +} +.pill-ok { background: var(--ok-soft); color: var(--ok); } +.pill-warn { background: var(--warn-soft); color: var(--warn); } +.pill-danger { background: var(--danger-soft); color: var(--danger); } +.pill-muted { background: var(--fg-soft); color: var(--muted); } +.pill-accent { background: var(--accent-soft); color: var(--accent); } + +/* 原型 .tag 系列:细描边胶囊,用于能力/属性标注(旗舰 / 推理 / 读图…) */ +.tag { + display: inline-flex; align-items: center; + padding: 2px 9px; border: 1px solid var(--border); + border-radius: 999px; font-size: 11.5px; color: var(--muted); +} +.tag-accent { border-color: color-mix(in oklch, var(--accent) 45%, var(--border)); color: var(--accent); } + +/* ---- 卡片 / 栅格 ---- */ +/* card-sub:卡片标题下的副说明(原型 .card .card-sub) */ +.card-sub { color: var(--muted); font-size: 12.5px; margin-bottom: 14px; } +/* spread:标题左 / 动作右的一行(原型 .spread) */ +.spread { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; } +/* 三等分栅格 + 主次分栏(原型 .card-grid-3 / .split) */ +.card-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 16px; } +.split { display: grid; grid-template-columns: 1.6fr 1fr; gap: 16px; align-items: start; margin-top: 16px; } +/* 栅格内卡片不再堆叠外边距(原型同名规则) */ +.split > .card + .card, .card-grid > .card + .card, .card-grid-3 > .card + .card { margin-top: 0; } + +/* stat-card:原型统计卡(等宽大数值 + 单位 + 可选 delta); + 作为 .stat 的别名提供,供 PR4–PR6 直接按原型类名书写 */ +.stat-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 16px 18px; + transition: transform .15s ease, border-color .18s ease, box-shadow .18s ease; +} +.stat-card:hover { transform: translateY(-2px); border-color: var(--card-hover-border); box-shadow: var(--card-shadow); } +.stat-card .label { font-size: 12px; color: var(--text-mute); display: flex; align-items: center; gap: 6px; } +.stat-card .value { + font-family: var(--font-mono); font-variant-numeric: tabular-nums; + font-size: 24px; font-weight: 700; line-height: 1.15; margin-top: 4px; +} +.stat-card .value .unit { font-size: 13px; color: var(--text-mute); margin-left: 4px; } +.stat-card .delta { font-size: 11.5px; font-family: var(--font-mono); } +.stat-card .delta.ok { color: var(--ok); } +.stat-card .delta.danger { color: var(--danger-text); } +.stat-card.accent .value { color: var(--accent-text); } +.stat-card.accent { + border-color: color-mix(in oklch, var(--accent) 40%, var(--border)); + background: linear-gradient(180deg, var(--accent-soft), transparent 70%), var(--bg-card); +} + +/* ---- 表格 ---- */ +/* table.dt:原型数据表——等宽大写表头 + 单位数右对齐(.table 的结构化别名) */ +table.dt { width: 100%; border-collapse: collapse; font-size: 13px; } +table.dt th, table.dt td { padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--table-row-border); white-space: nowrap; } +table.dt thead th { + font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .06em; + text-transform: uppercase; color: var(--text-mute); font-weight: 500; +} +table.dt tbody tr:nth-child(even) { background: var(--table-stripe); } +table.dt tbody tr:hover { background: var(--bg-hover); } +table.dt tbody tr:last-child td { border-bottom: 0; } +table.dt td.num, table.dt th.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; } +/* 厂商/模型合并单元格(原型 .provider-cell + .model-name + .dot) */ +.provider-cell { display: flex; align-items: center; gap: 9px; } +.model-name { font-family: var(--mono); font-size: 12.5px; } +.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; } +.dot.muted { background: var(--muted); } + +/* ---- 工具栏 / 搜索 ---- */ +/* .search 的原型定位规则见上方「搜索增强」(线上既有实现即原型形态) */ +.toolbar .grow { flex: 1; } + +/* ---- 表单 ---- */ +/* label.f:原型表单标签(纵向 label + 说明文字) */ +.form label.f { display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; color: var(--text-dim); } +/* 文本域沿用输入框外观(原型 input.i/select.i/textarea.i) */ +textarea.input { resize: vertical; min-height: 76px; } +textarea.input:focus { border-color: var(--accent); } +/* check-line:整行复选(原型 .check-line);.checkbox-line / .checkbox 为其线上历史别名 */ +.check-line { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text); } +.check-line input { accent-color: var(--accent); width: 15px; height: 15px; } +.checkbox, .checkbox-line { flex-direction: row !important; align-items: center; gap: 8px; } +.checkbox input, .checkbox-line input { accent-color: var(--accent); width: 14px; height: 14px; } +/* 交易页自定义时间段的 datetime-local 输入:与 .input 同观感 */ +.tx-range-input { + background: var(--bg-soft); border: 1px solid var(--border); color: var(--text); + border-radius: var(--radius-sm); padding: 8px 12px; font-size: 13px; outline: none; + max-width: 210px; +} +.tx-range-input:focus { border-color: var(--accent); } +/* hint:字段级浅色提示(比 .form-hint 更轻) */ +.hint { font-size: 11.5px; color: color-mix(in oklch, var(--muted) 82%, transparent); } + +/* ---- 图表 ---- */ +/* .trend 双色柱状图(原型 .trend / .trend-col / .trend-pair / .trend-bar / .trend-x / .legend) */ +.trend { display: flex; align-items: flex-end; gap: 10px; height: 150px; padding-top: 10px; } +.trend-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; } +.trend-pair { display: flex; align-items: flex-end; gap: 3px; height: 118px; width: 100%; justify-content: center; } +.trend-bar { width: 9px; border-radius: 3px 3px 0 0; min-height: 2px; } +.trend-bar.consume { background: var(--accent); } +.trend-bar.earn { background: color-mix(in oklch, var(--ok) 80%, var(--muted)); } +.trend-x { font-family: var(--font-mono); font-size: 10px; color: var(--muted); } +.legend { display: flex; gap: 16px; font-size: 11.5px; color: var(--muted); margin-top: 10px; } +.legend i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; margin-right: 5px; } +.legend i.consume { background: var(--accent); } +.legend i.earn { background: color-mix(in oklch, var(--ok) 80%, var(--muted)); } + +/* .bar-track / .bar-fill:原型横向条(.bar > i 的结构化别名,供新视图使用) */ +.bar-track { height: 8px; border-radius: 999px; background: var(--fg-soft); overflow: hidden; } +.bar-fill { height: 100%; border-radius: 999px; background: var(--accent); } +.bar-fill.alt { background: color-mix(in oklch, var(--accent) 45%, var(--muted)); } +/* .bar-row .bar-top:原型「名称左 / 数值右」的行头(线上 .bar-label 为其别名) */ +.bar-row .bar-top { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 5px; } + +/* ---- 空态 ---- */ +/* .empty = .empty-state 的原型别名(纯文案空态,无图标) */ +.empty { text-align: center; color: var(--muted); padding: 40px 20px; font-size: 13px; } +.empty.compact { padding: 16px 8px; } + +/* ---- 面包屑 ---- */ +.crumb { font-family: var(--font-mono); font-size: 11px; color: var(--muted); margin-bottom: 6px; } + /* ---------- 响应式预留 ---------- */ @media (max-width: 900px) { @@ -1046,6 +1175,11 @@ mark { @media (max-width: 560px) { .stat-grid { grid-template-columns: 1fr 1fr; } .card-grid { grid-template-columns: 1fr; } + /* 通用组件层窄屏(原型 responsive):三等分栅格与主次分栏堆叠;趋势图收紧高度 */ + .card-grid-3 { grid-template-columns: 1fr; } + .split { grid-template-columns: 1fr; } + .trend { height: 120px; } + .trend-pair { height: 92px; } .api-key-card { margin-top: 12px; } /* 网关端点卡片窄屏:chip 与 URL 上下排(原型 responsive 规则) */ .gw-ep { grid-template-columns: 1fr; gap: 6px; } diff --git a/ui/index.html b/ui/index.html index 41ba974..263a24e 100644 --- a/ui/index.html +++ b/ui/index.html @@ -191,7 +191,7 @@

模型市场

浏览 / 搜索各家模型,按点数计价

- + @@ -430,7 +430,7 @@

接入方式

API Key

用于本地工具 / 代码接入平台的密钥。独占整行,避免表格字段挤压。

- + @@ -535,7 +535,7 @@

按部门用量

- + @@ -572,7 +572,7 @@

添加部门