-
Notifications
You must be signed in to change notification settings - Fork 1
Design System
KS-OTO edited this page Sep 21, 2026
·
1 revision
权威规格在仓库里的
docs/design-baseline.md ——
那份文件与代码同步演进,本页只是导读。
设计系统的存在理由是:弹窗的不一致只是模型的投影。模型不统一,弹窗怎么调都会再漂。
| 节 | 内容 |
|---|---|
| 0 | 硬约束:TDesign 原生,token 只用 --td-*
|
| 0.1 | 品牌主题层 src/assets/theme.css(实测取值来源、刻意偏离、对比度基准) |
| 1 | 语义聚类(W / B / P) |
| 2 | 卡型矩阵 |
| 3 | 屏幕策略 |
| 4 | 单位规范 |
| 5 | 精度规范 |
| 6 |
AccountDetail:所有平台同构的账号详情 |
不迁移组件库、不模仿参考站、不新增 --ui-* 平行 token 层。
自定义语义值只写在 src/assets/layout.css 的 :root 并注明理由。
全站唯一的配色入口。亮 / 暗两套品牌主题通过覆盖 TDesign 的设计变量(--td-*)实现,
组件侧零硬编码颜色 —— 换配色只动那一个文件。
几条最值钱的经验:
-
语义色必须钉在「消费级」上,不是随手挑一级。TDesign 的
--td-success-color这类 token 是别名,指向色阶里的某一级(success→-5、error→-6、brand→ 亮-7/ 暗-8)。.t-progress__inner的填充读的就是别名 —— 把亮绿钉在-8而不是-5, 进度条实际拿到的是一个发闷的中绿,而当时所有门禁都是绿的。 -
「填充档」和「文字档」是两个 token,不能混用。
--td-brand-color是填充档 (按钮底、进度条、开关),给的是「上面压白字的色块」。拿它当文字色, 亮色下侥幸可读,暗色下掉到 2.15:1。文字一律走--td-text-color-*。 -
同一个 token 同时当填充与文字时算术上无解:白字压在填充上需要亮度 ≤0.183,
它当字压在卡片面上需要 ≥0.231。本项目的解法是暗色品牌色换成另一个蓝(
#4e7ef9), 这是全站唯一一处亮暗不同色,理由写在theme.css文件头。 -
--td-text-color-anti必须保持白色 —— 它不只用于彩色填充上的字, 还被 tooltip 的文本读(tooltip 底色是--td-gray-color-13,暗色下#242424)。 -
对比度阈值要拿库自己的官方基线当基准:TDesign 官方在浅色
-1背景上的 success / warning 文字只有 2.86 / 2.82:1。要求自己 ≥4.5 会造出一整套不达标的假失败; 正确的判据是「不比自己替换掉的那套更差」。
数值 单位
───────── ────────
110.00 ¥ ← 货币:卡面一律用**符号**(¥ / $)
8.42 万 token ← 计数:数值 + 中文量纲
56.70 (无) ← 币种未知:只显示数值,不猜
- 币种代码 → 符号的映射只维护一份:
src/format.ts的currencySymbol()。 - 单位渲染成独立元素(不是
value字符串的一部分),数值仍是纯数字,可复制、可断言。 -
禁止在标签里重复币种:
CNY 总余额→总余额。 - 币种代码(
CNY/USD)只保留在详情弹窗的「币种」字段。
全站数值只有三种语义,精度由语义决定,不由调用点决定。实现与推导见 src/format.ts:
类别(Metric.kind) |
精度 | 舍入 | 适用 |
|---|---|---|---|
money |
2 位 + 千分位 | 截断向零 | 一切货币读数 |
tokens |
量级缩写(K 档 1 位 / M·B 档 2 位;不足 1K 退回计数精度) | 四舍五入 | token / CREDITS 总量 |
percent |
1 位 | 四舍五入 | 百分比、比率、折算值 |
count |
0 位 + 千分位 | 四舍五入 | 请求次数、天数、个数 |
text / duration
|
原样 | — | 枚举、日期文案、倒计时 |
三条容易踩的:
-
金额必须先做浮点补偿:
1.15 * 100 === 114.99999999999999,直接Math.trunc会得到 1.14(系统性少 1 分钱,实测 13 个样本里 7 个踩坑)。truncateMoney内部已处理。 -
「原样输出」是精度漏洞,任何一档都不能例外。
formatTokens不足 1K 的那一档曾写String(value),于是quota - used算出的206.34719999999652原样印到卡面上。 同一类还有-0(Math.round(-0.04)是 -0)与非有限数(NaN会印成"NaN")。 判据:每个分支都必须落到某个精度上,没有分支允许原样返回输入数字。 -
<t-statistic>只许出现在components/ui/MetricTile.vue且必须显式声明精度。 它不传decimal-places时不是取整,而是原样输出 0–20 位小数 —— 所以金额交给它之前必须已经截断。
状态:已落地。 类型在 src/types.ts,构件库在 src/detail.ts,
10 个适配器与 10 个 Section 都已按这套模型改造完。
| 概念 | 在哪 | 规则 |
|---|---|---|
| 模型 | src/types.ts |
字段恒存在的扁平对象;失败取中性空值 |
| 构件库 | src/detail.ts |
field / metric / table / notice / link / windowQuota / cardsOf
|
| 适配器 | src/components/*Detail.ts |
平台原始响应 → AccountDetail,平台差异只在这里 |
| 骨架组件 | src/components/ui/ |
卡面与弹窗各只此一套 |
cardFace 开关 |
适配器 | 「卡面 vs 弹窗」的唯一开关,别在组件里挑字段 |
cardFace 现有两种用法:同一账号的另一个订阅、只对部分模型生效的限额
(火山日额度就是后者 —— 它只对图片/视频/语音与 Harness 生效,因此卡面只留 5 小时/周/月)。
OverviewTab 不在该模型的范围内(它读原始切片),不要顺手改它。
- 布局只用
src/assets/layout.css的语义化网格原语;禁t-row/t-col(TDesign 栅格 12 列);断点只在layout.css声明。 - 账号列表一律
.grid-cards grid-cards--wide。 -
列策略由个数声明、
App.vue统一施加(组件不判断):shouldSpanFullRow(≥2 整行)、isCompactAccounts(≥5 单列)、metricGridClass(读数 2/3/auto)、windowContainerClass(窗口 ≥3 等分)。改谓词要同时改App.vue绑定与layout.css——utils.test.ts有接线守卫。 - 每一层卡片都铺满上一层给它的高度,所以区块卡 → 账号卡 → 窗口块三层严格等高。
- 弹窗居中必须
placement="center"(TDesign 默认top= 视口 20vh 顶距)。
规格不是靠自觉守住的。src/ 下几组守卫测试盯着最容易静默失效的那几条:
| 文件 | 盯什么 |
|---|---|
theme.test.ts |
正文档位过 AA、反白字下限、进度条填充亮度、圆角阶梯、字面颜色不得外流 |
format.test.ts |
t-statistic 必须声明精度、渲染层禁 toFixed / Math.round / toLocaleString
|
tdesign.test.ts |
组件只从 src/tdesign.ts 的注册表来,禁 app.use(TDesign) 整库导入 |
utils.test.ts |
列策略谓词 ↔ App.vue 绑定 ↔ layout.css 三处接线一致 |
改配色、改精度、改列策略前先跑 bun run test:unit。