From e1a9017cd1337a71727c3633b0ef7da961f599ac Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Tue, 3 Nov 2026 12:46:27 +0800 Subject: [PATCH] =?UTF-8?q?docs(design):=20=E6=96=B0=E5=A2=9E=20UI=20demo?= =?UTF-8?q?=20=E9=A1=B5=E9=9D=A2=EF=BC=8C=E4=BE=9B=20UI=20=E7=B2=BE?= =?UTF-8?q?=E4=BF=AE=E8=AF=84=E5=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/design/ui-demo/:控件总览、表单、卡片、列表与表格,以及工作台、读标两个组合页 - 只用设计规范的变量和组件;新增组件样式放在 demo.css,清单见 index - index 列出 UI 精修待定问题 Q1–Q12 - design/README 与 AGENTS.md 进度同步 Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 3 +- docs/design/README.md | 1 + docs/design/ui-demo/cards.html | 240 +++++++++++ docs/design/ui-demo/controls.html | 492 ++++++++++++++++++++++ docs/design/ui-demo/demo.css | 524 ++++++++++++++++++++++++ docs/design/ui-demo/demo.js | 275 +++++++++++++ docs/design/ui-demo/forms.html | 300 ++++++++++++++ docs/design/ui-demo/index.html | 95 +++++ docs/design/ui-demo/lists.html | 295 +++++++++++++ docs/design/ui-demo/page-reading.html | 226 ++++++++++ docs/design/ui-demo/page-workbench.html | 133 ++++++ 11 files changed, 2583 insertions(+), 1 deletion(-) create mode 100644 docs/design/ui-demo/cards.html create mode 100644 docs/design/ui-demo/controls.html create mode 100644 docs/design/ui-demo/demo.css create mode 100644 docs/design/ui-demo/demo.js create mode 100644 docs/design/ui-demo/forms.html create mode 100644 docs/design/ui-demo/index.html create mode 100644 docs/design/ui-demo/lists.html create mode 100644 docs/design/ui-demo/page-reading.html create mode 100644 docs/design/ui-demo/page-workbench.html diff --git a/AGENTS.md b/AGENTS.md index 2236bef..702fd88 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -142,4 +142,5 @@ samples/ - 2026-11-03:应用图标改为印泥铺满、不留白边,圆角按 Apple 连续曲率圆角由系统裁(B6);VI 手册 v1.1 第 22 页改为 iPhone 18 Pro 主屏幕模拟(`docs/design/vi/mockup/`,Apple 素材不进 git,机身需本人同意 Apple Design Resources 许可后放入)。 - 2026-11-03:分析参考项目标擎 BidEngine 的架构、提示词和模型路由,整理出可参考的做法和避坑清单(`docs/architecture/ref-bid-engine-analysis.md`),留给技术架构阶段用。 - 2026-11-03:技术架构设计 v0.2(`docs/architecture/architecture.md`)。T2–T5 已确认:Python 单语言、桌面端做客户端外壳、SaaS 先 Compose 后 K8s、云无关。之后确认了 AR1–AR3、AR5–AR9,前端改为 Vue 3;AR4 复议后改为引入 Valkey(已确认);AR10 定为不引入带样式的 UI 框架,用 Reka UI 打底。选型全部确认。编码前先做技术验证 S1–S4。 -- 下一步:用原型找种子用户走查(顺带问 G15 苏采云客户端的分册对应),再进入 UI 设计精修。 +- 2026-11-03:种子用户走查结束,没有问题。进入 UI 精修前先做 UI demo(`docs/design/ui-demo/`,6 个页面):控件、表单、卡片、列表,以及工作台、读标两个组合页;新增组件清单和待定问题 Q1–Q12 见其 index。 +- 下一步:UI 精修,先定 UI demo 里的 Q1–Q12。 diff --git a/docs/design/README.md b/docs/design/README.md index 2323003..4a0ce5f 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -10,6 +10,7 @@ | [system/tokens.css](system/tokens.css) | 设计变量(原型和前端直接引用) | v0.1 | | [system/components.css](system/components.css) | 组件样式参考实现(预览页和原型共用) | v0.1 | | [system/components.html](system/components.html) | 组件预览页(用样本条款演示) | v0.1 | +| [ui-demo/](ui-demo/index.html) | UI demo:控件总览、表单、卡片、列表与表格,以及工作台、读标两个组合页;附新增组件清单和 UI 精修待定问题 Q1–Q12 | 供 UI 精修评审 | ## 速查 diff --git a/docs/design/ui-demo/cards.html b/docs/design/ui-demo/cards.html new file mode 100644 index 0000000..3ca86e6 --- /dev/null +++ b/docs/design/ui-demo/cards.html @@ -0,0 +1,240 @@ + + + + + +卡片 · 标博士 UI demo + + + + + + + +
+
+
+

③ 卡片

+

卡片是白底 + 1px 分隔线 + 2px 圆角,不加阴影(层次靠分隔线和底色)。卡片里的按钮一律用次按钮或文字按钮,把主按钮留给页面级操作。

+ +
+ + +
+
01

项目卡

规范 §7.9 · 卡片视图(表格视图见 ④)
+
+
+
某市政务服务中心智慧大厅建设项目
某市政务服务管理办公室 · 政府采购 · 公开招标
已检查
+
+
投标截止11月20日 09:30
剩 17 天
+
所投包采购包 1
+
就绪未就绪
+
+
致命3重要3提示2第 2 次检查 · 11月2日
+
+
+
+
某区智慧社区平台运维服务
某区民政局 · 政府采购 · 竞争性磋商
已检查
+
+
响应截止剩 2 天 5 小时
+
所投包全部
+
就绪就绪
+
+
致命 —重要1第 3 次检查 · 11月3日
+
+
+
+
某县数据中心机房改造项目
某县大数据管理局 · 招标投标
编制中
+
+
投标截止今天 17:00 截止
+
所投标段标段 1
+
清单草稿 v1
+
+
跟进中编制中已检查已递交
+
待确认 2 条
+
+
+
+
+
某区智慧校园一期
某区教育局 · 政府采购 · 公开招标
已递交
+
+
投标截止已截止
+
所投包全部
+
递交10月28日
+
+
✓王敏10月28日 09:12标记已递交
+
+
+
+
+
+ +

↑ 加载中(骨架屏)

+
+ +
+
  • 致命项目卡左侧加 3px 致命红竖线(与问题行一致),方便在卡片墙里扫读;状态用中性描边标签,不用彩色。
  • 术语跟着体系走:磋商项目写「响应截止」「响应文件」,招标投标体系写「标段」(§9.1)。
+
+ + +
+
02

统计格

规范 §4.2 Figure 30/36
+
+
进行中的项目4

已递交 1 · 已归档 1

+
7 天内截止2

最近:今天 17:00

+
待处理的致命问题3

在 1 个项目里

+
本期解析剩余37次

检查剩 12 次 · 查看明细

+
+
  • 统计数字一律墨色,即使是「致命问题 3」也不染红:红色留给标签,避免一屏红色太多。
+
+ + +
+
03

额度卡

规范 §7.10 · §8.3 · 红线 9
+
+
+

本期额度

专业版 · 至 2027-10-31
+
+
解析剩余37/ 50

已用 13 次

+
检查剩余12/ 20

已用 8 次

+
+ +
+
+

检查额度

已用 80%
+ 检查剩余4/ 20 次 +
+

已用 80%。同一项目 24 小时内复查不扣额度。

+
提醒每期只出现一次查看流水
+
+
+

检查额度

已用完
+ 检查剩余0/ 20 次 +
+

本期检查额度已用完,续费或下期发放后可继续。

+
查看流水
+
+
+
顶栏常驻解析37检查12解析37检查4检查用到 80% 时,顶栏描边变赭色
+
+ + +
+
04

任务进度卡

规范 §7.11
+
+
+

正在解析招标文件

已用时 1 分 40 秒
+

ZC-2026-0418 采购文件.pdf · 186 页

+
    +
  1. ✓识别文件格式
  2. +
  3. ✓提取文字186 页
  4. +
  5. ✓识别分包2 个包
  6. +
  7. 提取清单62%
  8. +
  9. 核对出处
  10. +
+
62%
+

可以离开本页,完成后会发站内通知。

+
取消后退回 1 次解析额度
+
+
+

解析失败

11月2日 14:31
+

关于 ZC-2026-0418 的更正公告.pdf

+
    +
  1. ✓识别文件格式
  2. +
  3. !提取文字
  4. +
  5. 识别分包
  6. +
  7. 提取清单
  8. +
+
!

文件已加密,无法读取文字。已退回 1 次解析额度。

请上传未加密的版本,或在 PDF 阅读器里另存一份。
+
E-20261102-3M8A
+
+
+
+

解析完成

用时 3 分 12 秒
+
+
提取要求
26 条 · 其中 ★ 4 条
+
待确认
2 条没有可靠出处
+
分包
2 个采购包
+
+
已扣 1 次解析
+
+
+

检查排队中

前面还有 2 个任务 · 约 4 分钟
+
+
+
+
+ + +
+
05

AI 摘要卡

规范 §7.16 · §8.1 · 红线 1、2
+
+
+
AI 摘要

项目要点

未确认 · 每一条都可以点出处核对
+
+
+
法律体系
政府采购 · 公开招标(按「投标无效」口径检查)
+
预算 / 限价
采购包 1:286.00 万元(最高限价同预算)第 2 页
+
评标方法
综合评分法:价格 10、技术 40、实施方案 20、售后 10、认证 6、业绩 8第 31 页
+
联合体
不接受第 2 页
+
保证金
不收取第 21 页
+
样品
未找到要求 · 已查找:第二章、第三章、第六章
+
+
+
判断有误?
+
+
+

项目要点

✓王敏11月2日 14:20确认无误
+
+
+
法律体系
政府采购 · 公开招标(按「投标无效」口径检查)
+
预算 / 限价
采购包 1:286.00 万元(最高限价同预算)第 2 页
+
评标方法
综合评分法:价格 10、技术 40、实施方案 20、售后 10、认证 6、业绩 8第 31 页
+
联合体
不接受 已修改 · 查看记录第 2 页
+
保证金
不收取第 21 页
+
样品
未找到要求 · 已查找:第二章、第三章、第六章
+
+
+
+
+
+
  • 确认前:「AI 摘要」蓝铅笔标签 + 左侧虚线;确认后:标签换成绿色署名,虚线变墨色实线,正文本来就是墨色。
  • 找不到的项写「未找到 · 已查找:…」,不猜测、不留空。胜率之类的数据不足时不展示(红线 5)。
+
+ + +
+
06

关键日期卡

规范 §7.9 倒计时 · §9.3
+
+
+

关键日期

以补遗 1 更正后的时间为准
+
    +
  • 10-28采购文件公告发布
  • +
  • 11-09询问、质疑截止公告期满后 7 个工作日 · 剩 6 天第 6 页
  • +
  • 11-20 09:30投标截止 / 开标剩 17 天 · 已被补遗 1 更新,原为 11-18 09:30补遗 1 · 第 1 页
  • +
  • 2027-02-18投标有效期至截止日起 90 天第 21 页
  • +
+

MVP 只在站内显示倒计时,不发截止提醒。

+
+
+

7 天内截止

全部项目
+
    +
  • 今天 17:00某县数据中心机房改造项目剩 6 小时 · 编制中标段 1
  • +
  • 11-05 14:30某区智慧社区平台运维服务剩 2 天 5 小时 · 就绪全部
  • +
+
  • > 72 小时:墨色「11月20日 09:30 · 剩 17 天」;≤ 72 小时:赭色小字;≤ 24 小时:赭色加粗;已过:辅助灰「已截止」。
  • 截止临近不用红色、不闪烁。
+
+
+
+
+ + + + + diff --git a/docs/design/ui-demo/controls.html b/docs/design/ui-demo/controls.html new file mode 100644 index 0000000..065eaf9 --- /dev/null +++ b/docs/design/ui-demo/controls.html @@ -0,0 +1,492 @@ + + + + + +控件总览 · 标博士 UI demo + + + + + + + +
+
+
+

① 控件总览

+

每个控件按「默认 → 悬停 → 按下 → 聚焦 → 禁用 → 加载中 / 错误」陈列。灰色小字是状态名;标「可以点」的是可交互的。示意数据取自原型的「某市政务服务中心智慧大厅建设项目」。

+ +
+ + +
+
01

按钮

规范 §7.1 · §8.3
+
+

类型 × 状态

+ + + + + + + +
默认悬停按下键盘聚焦禁用加载中
主按钮
次按钮
文字按钮(不用)
+
    +
  • 一屏只有一个主按钮。没有红色的危险按钮:删除用次按钮 + 确认对话框。
  • +
  • 加载中把文字换成「正在…」,两段文字叠在同一格里,按较宽的定宽,切换时按钮不跳动;不加转圈图标。
  • +
  • 主按钮悬停变浅 8%(用 color-mix 计算,不新增颜色变量);按下回到墨色并加一道内阴影。
  • +
+
+
+
+

尺寸

+
+
  • 28:表格行内 · 32:默认 · 40:登录页、空状态
+
+
+

带图标 · 只有图标

+
+ + + + +
+
+
  • 图标必须和文字一起出现;只有图标时要有 aria-label 和悬停提示。
+
+
+

会扣点的按钮 可以点

+
扣 1 次检查 · 剩 12 次
+
24 小时内复查,不扣额度
+
剩 0 次 · 按钮仍可点
+
+
+
+ + +
+
02

输入框与文本域

规范 §7.13
+
+
+
默认(占位符用辅助灰)
+
悬停
+
聚焦(2px 墨色焦点环 + 2px 间隙)
+
已填写
+
错误(赭色,不用红)
!手机号应为 11 位数字
+
禁用
+
只读(系统识别结果,可复制)
+
带说明文字
天
从投标截止日起算,招标文件第 21 页
+
+
+
+
+

文本域 · 字数 可以输入

+
+ + +
报告和处理记录里会显示这段原因。0 / 200
+
+
+
+

搜索框

+ +
+ +
  • 搜索框的放大镜只做辅助,占位符写清楚能搜什么。
  • 输入框高 32,左右内边距 10;数值输入用 Barlow 等宽数字。
+
+
+
+ + +
+
03

下拉选择

AR10 · Reka UI Select 打底
+
+
+
未选择
+
+
+
已选择 · 可以点
+
+
+
展开(浮层:4px 圆角 + 阴影)
+
+
+
错误
+
+
+ !请选择资质类型,查标时按类型核对
+
禁用
由招标文件识别,在项目设置里修改
+
表格行内(28 高)
+
+
+
  • 键盘:↑ ↓ 移动、Enter 选中、Esc 收起。选中项加粗并在右侧打 ✓(形状 + 字重,不只靠颜色)。
  • 不可选的项用禁用灰,并在右侧写原因。
+
+
+ + +
+
04

日期选择

规范 §9.3 · AR10
+
+
+

收起 · 可以点

+
+
+ 以补遗 1 的更正为准 · 剩 17 天
+
+
+
+
+ !发证日期晚于有效期,请核对证书
+
+
+

展开

+
+
  • 一周从周一开始;数字用 Barlow 等宽。
  • 今天:控件灰描边;选中:墨底白字;关键日期:下方小圆点。
+
+
+

有不可选的日期

+
+
  • 不可选的日期加删除线(不只靠变灰)。
  • 界面显示「11月20日 09:30」,输入框里用「2026-11-20」便于核对和粘贴。
+
+
+
+ + +
+
05

复选 · 单选 · 开关

规范 §8.2 批量勾选
+
+
+

复选框

+
+
未选
+
已选
+
半选(表头全选)
+
聚焦
+
禁用
+
禁用 · 已选
+
+
+ + +
+
+
+

单选

+
+
未选
+
已选
+
禁用
+
禁用 · 已选
+
+ 单选卡片(选项需要说明时) +
+ + +
+
+
+

开关 可以点

+
+
关
+
开
+
禁用 · 关
+
禁用 · 开
+
+
+ + +
+
  • 规范没有定义开关。这里沿用 2px 圆角做成方形滑块(不用胶囊形),旁边写「开 / 关」,不只靠颜色。
+
+
+
+ + +
+
06

级别标签 · 状态标签 · AI 标识 · 署名

规范 §3.3 · §7.2 · §7.5 · §7.16
+
+
+

问题级别(实心 / 描边 / 无框)

+
致命重要提示计数致命3重要3提示3
+
参数判断疑似不满足疑似不满足满足需人工判断未找到响应
+

项目状态 · 就绪 · 复查标记

+
跟进中编制中已检查已递交已归档
+
未就绪就绪新增沿用本次已解决
+
清单状态草稿 v3已确认 v2✓王敏11月2日 15:12确认
+
分类资格条件无效条款技术参数采购包 1通用
+
+
+

AI 产出 → 人工确认(颜色说明谁说的)

+
AI 摘要AI 初稿AI 提取人工添加补遗 1已修改 · 查看记录
+
+
AI 产出:蓝铅笔标签 + 左侧虚线 +
AI 摘要未确认预算 418.00 万元,分 2 个采购包;采购包 1 最高限价 286.00 万元。招标文件 · 第 2 页
+
人工确认后:墨色正文 + 实线 + 绿色署名 +
预算 418.00 万元,分 2 个采购包;采购包 1 最高限价 286.00 万元。招标文件 · 第 2 页 +
✓王敏11月2日 14:20确认无误
+
问题处理后的署名行 +
✓王敏11月2日 14:20已修改撤销
+
–王敏11月2日 14:26已忽略:招标文件允许撤销
+
+
+
+
+ + +
+
07

出处

规范 §7.3 · 最重要的组件
+
+
+

招标原文 → 投标原文

+
招标文件 · 第 41 页★ 2U机架式服务器,CPU:配置≥2颗海光架构32C 2.7GHz,内存: ≥512G DDR4,系统盘: 2块≥480G SSD……
+
技术册 · 第 12 页第 12 行 内存:256G DDR4 无偏离
+
  • 页码用 Barlow;原句照录,不加空格、不改标点。
+
+
+

「未找到」也是出处

+
招标文件 · 第 44 页★ 支持硬件BFD,最小报文发送间隔≤50ms,提供相关证明材料(包含但不限于产品官网公开参数截图证明等)。
+
投标文件 · 未找到

证明材料未找到 · 已查找:技术册(全部章节)、商务册(附件)

+
  • 竖线改虚线,写明找过哪些分册、哪些章节。
+
+
+

OCR · 截断 · 当前选中

+
商务册 · 第 3 页OCR投标人(电子签章):南京某信息技术有限公司  日期:2026年11月25日
+
招标文件 · 第 23 页2.财务状况报告,至少提供:(1)本项目开标时间前6个月内任一时间截点的资产负债表的扫描件1份;(2)本项目开标时间前6个月内任何1月(不含开标当月)利润表月报表的扫描件1份。3.本项目开标时间前6个月内任何1月的依法缴纳税收和社会保障资金的相关材料的扫描件。
+ +
  • 选中的出处竖线加粗到 4px。超过 3 行截断,「展开原文」看全句。
+
+
+
+ + +
+
08

提醒(页内 Alert)

规范 §7.12 · §7.15
+
+
+
i

清单未确认,检查结果以当前清单为准。

+
!

本期检查额度已用 80%。剩 4 次,额度用完后续费或下期发放后可继续。

查看额度
+
!

投标截止还剩 2 天 5 小时。还有 3 项致命问题待处理。

截止临近用赭色,不用红色;只提醒,不拦截。
+
+
+
!

解析失败:文件已加密。已退回 1 次解析额度。请上传未加密的版本。

关于 ZC-2026-0418 的更正公告.pdf
+
i

出了点问题,请稍后重试。本次操作未扣额度。

错误编号 E-20261103-7Q4K · 联系客服时请提供
+
✓

报告已导出:智慧大厅建设项目-辅助检查报告-第 2 次.pdf

+
+
+
  • 提醒放在相关内容上方,不自动消失。系统错误不用红色,用中性提醒 + 错误编号。
+
+ + +
+
09

轻提示(Toast)

规范 §7.12
+
+
+

样式

+
+
✓清单 v3 已确认
+
✓已标记「已修改」 · 复查时会再查
+
已复制错误编号
+
+
+
+

触发 可以点

+
+
  • 只用于操作结果;4 秒后消失,鼠标悬停时暂停计时。
  • 可撤销的操作在轻提示里给「撤销」;失败不用轻提示,用页内提醒说清原因。
  • 多条从下往上堆叠,屏幕阅读器会播报。
+
+
+
+ + +
+
10

对话框

规范 §7.14 · 只用于不可撤销的操作
+
+ +
+

打开真实对话框 可以点

+
+ + + +
+
  • 宽 480(确认)/ 640(表单),圆角 4。
  • 标题写要做的事,正文写后果;主操作在右,按钮写动作「删除项目」,不写「确定」。
  • 删除没有红色:靠输入项目名确认。Esc 关闭,焦点回到触发按钮。
+
+
+
+ + +
+
11

标签页 · 分段控件 · 分页

规范 §5.3
+
+
+

标签页 可以点,← → 切换

+
+ + + + +
+

项目要点、关键日期、文件和检查记录。

+ + +
+
+

分段控件(筛选) 可以点

+
+
+
+
+
+

分页 可以点

+ +
  • 当前页用墨色描边 + 加粗(不填色,避免和主按钮抢)。每页条数固定 20 / 50,不做无限滚动,方便「第几页第几条」沟通。
+
+
+ + +
+
12

空状态 · 骨架屏 · 进度

规范 §7.10 · §7.11 · §7.15
+
+
+

空状态

+
还没有招标文件

上传后,标博士会逐条提取要求,每条都附原文出处。

扣 1 次解析 · 剩 37 次
+
+
+

骨架屏(加载清单)

+
+
+
+
+
+
+
  • 不用转圈占满屏。骨架做缓慢的明暗变化(1.6 秒),系统开了「减少动态效果」时静止。
+
+
+

分阶段进度

+
    +
  1. ✓识别文件格式
  2. +
  3. ✓提取文字186 页
  4. +
  5. ✓识别分包2 个包
  6. +
  7. 提取清单62%
  8. +
  9. 核对出处
  10. +
+
62%
+

可以离开本页,完成后会发站内通知。

+
+
+
+
检查剩余 · 正常12/ 20 次

已用 8 次

+
检查剩余 · 已用 80%4/ 20 次

已用 80%,剩 4 次

+
检查剩余 · 已用完0/ 20 次

本期检查额度已用完,续费或下期发放后可继续

+
+
+ + +
+
13

提示气泡 · 下拉菜单 · 快捷键

规范 §6 · §8.2
+
+
+

提示气泡

+
就绪
+
+
  • 处理三按钮的悬停提示写清含义(§7.4)。墨底白字,4px 圆角。
+
+
+

下拉菜单(行内更多操作) 可以点

+
+
+
+
+
+
+
+

快捷键

+ + + + + + + + +
J / K下一条 / 上一条
O · Enter打开当前出处
1 2 3确认无误 / 已修改 / 忽略
U撤销当前条
?快捷键说明
+

在「④ 列表与表格」和「⑥ 读标」页可以实际用 J / K。

+
+
+
+
+ + + + + diff --git a/docs/design/ui-demo/demo.css b/docs/design/ui-demo/demo.css new file mode 100644 index 0000000..00a78bd --- /dev/null +++ b/docs/design/ui-demo/demo.css @@ -0,0 +1,524 @@ +/* 标博士 · UI demo 样式(2026-11-03) + * 依赖 ../system/tokens.css 和 ../system/components.css,只用 tokens.css 的变量,不写裸色值。 + * 分两部分: + * A. 演示页排版:只属于 demo 页面(章节标题、状态陈列格),不进组件库; + * B. 新增组件:design-system.md 有描述、但 components.css 里还没有的组件。UI 精修定稿后再并入 components.css。 + * 新增组件清单见 index.html。 */ + +/* ===================================================================== + * A. 演示页排版 + * ===================================================================== */ +body.demo{min-width:1280px} +.d-top{position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:var(--sp-8);height:52px;padding:0 var(--sp-8);background:var(--bg-page);border-bottom:1px solid var(--border-subtle)} +.d-top .brand img{height:26px;display:block} +.d-top .tag{margin-left:calc(-1 * var(--sp-5))} +.d-nav{display:flex;gap:var(--sp-1);margin-left:auto} +.d-nav a{display:flex;align-items:center;height:52px;padding:0 var(--sp-3);color:var(--text-secondary);text-decoration:none;white-space:nowrap} +.d-nav a:hover{color:var(--text-primary)} +.d-nav a.on{color:var(--text-primary);font-weight:var(--fw-bold);box-shadow:inset 0 -2px 0 var(--c-ink)} +.d-page{max-width:calc(var(--content-max) + 2 * var(--sp-8));margin:0 auto;padding:var(--sp-10) var(--sp-8) var(--sp-16)} +.d-head h1{font:var(--fw-bold) var(--fs-h1)/var(--lh-h1) var(--font-serif);margin:0} +.d-head p{color:var(--text-secondary);margin:var(--sp-2) 0 0;max-width:60em} +.d-toc{display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4);margin-top:var(--sp-4);font-size:var(--fs-caption)} +.d-toc a{color:var(--text-secondary);text-underline-offset:3px} +.d-toc a:hover{color:var(--text-primary)} +.d-sec{margin-top:var(--sp-12);scroll-margin-top:72px} +.d-sec-h{display:flex;align-items:baseline;gap:var(--sp-3);border-bottom:2px solid var(--border-strong);padding-bottom:var(--sp-2);margin-bottom:var(--sp-5)} +.d-sec-h h2{font:var(--fw-bold) var(--fs-h2)/var(--lh-h2) var(--font-serif);margin:0} +.d-sec-h .no{font:var(--fw-bold) var(--fs-title)/1 var(--font-figures);color:var(--text-secondary)} +.d-sec-h .ref{margin-left:auto;font-size:var(--fs-caption);color:var(--text-secondary)} +.d-grid{display:grid;gap:var(--sp-6);align-items:start} +.d-grid.c2{grid-template-columns:repeat(2,minmax(0,1fr))} +.d-grid.c3{grid-template-columns:repeat(3,minmax(0,1fr))} +.d-grid.eq{align-items:stretch} +.d-grid.c-side{grid-template-columns:minmax(0,1fr) 360px} +.d-box{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:var(--sp-5) var(--sp-6)} +.d-box > h3{font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans);margin:0 0 var(--sp-4);display:flex;gap:var(--sp-2);align-items:baseline} +.d-box > h3 small{font-weight:var(--fw-regular);font-size:var(--fs-caption);color:var(--text-secondary)} +.d-box.flat{background:none;border-style:dashed;border-color:var(--border-control)} +.d-states{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--sp-5) var(--sp-5)} +.d-states.wide{grid-template-columns:repeat(auto-fill,minmax(220px,1fr))} +.d-state{display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-2);min-width:0} +.d-state > small,.d-cap{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.d-state .field{width:100%} +.d-rules{margin:var(--sp-4) 0 0;padding:var(--sp-3) 0 0 1.2em;border-top:1px solid var(--border-subtle);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.d-rules li + li{margin-top:2px} +.d-label{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);margin-bottom:var(--sp-2)} +.d-stack > * + *{margin-top:var(--sp-4)} +.d-row{display:flex;flex-wrap:wrap;gap:var(--sp-3);align-items:center} +.d-row + .d-row{margin-top:var(--sp-3)} +.d-matrix{border-collapse:collapse;width:100%} +.d-matrix th{font-weight:var(--fw-regular);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);text-align:left;padding:0 var(--sp-3) var(--sp-2) 0;white-space:nowrap} +.d-matrix td{padding:var(--sp-2) var(--sp-3) var(--sp-2) 0;vertical-align:middle} +.d-matrix td:first-child{font-size:var(--fs-caption);color:var(--text-secondary);white-space:nowrap;width:72px} +.d-open{position:relative;padding-bottom:var(--sp-2)} /* 静态展开的浮层,占位用 */ +.d-open .menu,.d-open .cal{position:static;margin-top:var(--sp-1)} + +/* 目录页 */ +.d-cards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-4)} +.d-link{display:block;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:var(--sp-4) var(--sp-5);color:var(--text-primary);text-decoration:none} +.d-link:hover{border-color:var(--border-control)} +.d-link b{display:flex;gap:var(--sp-2);align-items:baseline;font-size:var(--fs-title);line-height:var(--lh-title)} +.d-link b .fig{color:var(--text-secondary);font-size:var(--fs-title)} +.d-link p{margin:var(--sp-2) 0 0;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)} + +/* ===================================================================== + * B. 新增组件 + * ===================================================================== */ + +/* ---- B1 图标(Lucide 线性,1.5px 描边,16 / 20px,§6) ---- */ +.ic{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;vertical-align:-3px} +.ic.lg{width:20px;height:20px} +.ic.xl{width:32px;height:32px;stroke-width:1.25} + +/* ---- B2 页面框架:侧边栏 + 顶栏(§5.3) ---- */ +body.app{display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr);min-height:100vh;min-width:1280px} +.sidebar{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;border-right:1px solid var(--border-subtle);background:var(--bg-page);padding:var(--sp-5) var(--sp-3)} +.sidebar .brand{display:block;padding:0 var(--sp-2) var(--sp-6)} +.sidebar .brand img{height:30px;display:block} +.nav{display:flex;flex-direction:column;gap:2px} +.nav a{display:flex;align-items:center;gap:var(--sp-3);height:36px;padding:0 var(--sp-3);border-radius:var(--radius-sm);color:var(--text-primary);text-decoration:none} +.nav a:hover{background:var(--bg-hover)} +.nav a.on{background:var(--bg-surface);box-shadow:inset 2px 0 0 var(--c-ink);font-weight:var(--fw-bold)} +.nav a .fig{margin-left:auto;font-weight:var(--fw-medium);color:var(--text-secondary)} +.nav .sub{margin:var(--sp-5) var(--sp-3) var(--sp-2);font-size:var(--fs-caption);color:var(--text-secondary)} +.sidebar-foot{margin-top:auto;padding:var(--sp-3);border-top:1px solid var(--border-subtle);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.sidebar-foot b{display:block;color:var(--text-primary);font-size:var(--fs-body)} +.main{min-width:0;display:flex;flex-direction:column} +.top{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:var(--sp-4);height:52px;padding:0 var(--sp-8);background:var(--bg-page);border-bottom:1px solid var(--border-subtle)} +.crumbs{display:flex;gap:var(--sp-2);color:var(--text-secondary);min-width:0;white-space:nowrap;overflow:hidden} +.crumbs a{color:var(--text-secondary);text-decoration:none} +.crumbs a:hover{color:var(--text-primary);text-decoration:underline;text-underline-offset:3px} +.crumbs b{color:var(--text-primary);font-weight:var(--fw-regular);overflow:hidden;text-overflow:ellipsis} +.top-right{margin-left:auto;display:flex;align-items:center;gap:var(--sp-3)} +.quota-chip{display:flex;gap:var(--sp-3);align-items:center;height:28px;padding:0 var(--sp-3);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);color:var(--text-secondary);text-decoration:none;font-size:var(--fs-caption);background:var(--bg-surface)} +.quota-chip.warn{border-color:var(--warn);color:var(--warn-text)} +.quota-chip .fig{color:var(--text-primary);font-size:var(--fs-body);margin-left:3px} +.content{padding:var(--sp-6) var(--sp-8) var(--sp-16);max-width:calc(var(--content-max) + 2 * var(--sp-8));width:100%} +.page-title{display:flex;align-items:flex-end;gap:var(--sp-4);margin-bottom:var(--sp-5)} +.page-title h1{font:var(--fw-bold) var(--fs-h1)/var(--lh-h1) var(--font-serif);margin:0} +.page-title p{margin:var(--sp-1) 0 0;color:var(--text-secondary)} +.page-title .actions{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center} +.section-h{display:flex;align-items:center;gap:var(--sp-3);margin:var(--sp-8) 0 var(--sp-3)} +.section-h h2{font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans);margin:0} +.section-h .more{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center} + +/* 项目页头 */ +.proj-head{padding:var(--sp-5) var(--sp-8) 0;border-bottom:1px solid var(--border-subtle);background:var(--bg-page)} +.proj-head .line1{display:flex;align-items:flex-start;gap:var(--sp-4)} +.proj-head h1{font:var(--fw-bold) var(--fs-h1)/var(--lh-h1) var(--font-serif);margin:0} +.proj-head .meta{margin-top:var(--sp-1);color:var(--text-secondary);display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4)} +.proj-head .facts{margin-left:auto;display:flex;gap:var(--sp-6);text-align:right;white-space:nowrap} +.proj-head .facts small{display:block;font-size:var(--fs-caption);color:var(--text-secondary)} + +/* ---- B3 按钮补充状态(§7.1) ---- */ +.btn:active,.btn.is-active{background:var(--bg-sunken);border-color:var(--c-ink)} +.btn.is-hover{background:var(--bg-hover)} +.btn.primary.is-hover{background:color-mix(in srgb,var(--c-ink) 92%,var(--c-white))} +.btn.primary:active,.btn.primary.is-active{background:var(--c-ink);box-shadow:inset 0 2px 0 color-mix(in srgb,var(--c-white) 18%,transparent)} +.btn.text:active,.btn.text.is-active{background:var(--bg-sunken);border-color:transparent} +.btn.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px} +.btn .ic{margin:0 -2px} +/* 加载中:两个文字叠在同一格,按宽的那个定宽,切换时宽度不变 */ +.btn.swap{display:inline-grid;place-items:center} +.btn.swap > span{grid-area:1/1;white-space:nowrap} +.btn.swap > .busy,.btn.swap.is-loading > .idle{visibility:hidden} +.btn.swap.is-loading > .busy{visibility:visible} +.btn.is-loading{cursor:progress} +.btn.primary.is-loading{background:color-mix(in srgb,var(--c-ink) 80%,var(--c-white));border-color:transparent} +.btn.is-loading:not(.primary){color:var(--text-secondary)} +.icon-btn{display:inline-grid;place-items:center;width:32px;height:32px;border-radius:var(--radius-sm);color:var(--text-primary);background:none;border:1px solid transparent;cursor:pointer;position:relative;flex:none} +.icon-btn:hover{background:var(--bg-hover)} +.icon-btn.bordered{border-color:var(--border-control);background:var(--bg-surface)} +.icon-btn.sm{width:28px;height:28px} +.icon-btn .dot{position:absolute;top:6px;right:6px;width:6px;height:6px;border-radius:50%;background:var(--c-ink)} +.icon-btn[disabled]{color:var(--text-disabled);cursor:not-allowed;background:none} +.btn-group{display:inline-flex} +.btn-group .btn + .btn{margin-left:-1px} +.btn-group .btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0} +.btn-group .btn:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0} + +/* ---- B4 输入框、文本域、带单位、搜索(§7.13) ---- */ +.input{width:100%;transition:border-color var(--dur-fast) var(--ease)} +.input:hover{border-color:var(--c-ink)} +.input:focus,.input.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px;border-color:var(--c-ink)} +.input.is-hover{border-color:var(--c-ink)} +.input[disabled],.input.is-disabled{background:var(--bg-sunken);border-color:var(--bg-sunken);color:var(--text-disabled);cursor:not-allowed} +.input[disabled]::placeholder{color:var(--text-disabled)} +.input[readonly]{background:var(--bg-sunken);border-color:var(--border-subtle)} +.input[readonly]:focus{outline-color:var(--border-control)} +textarea.input{height:auto;min-height:80px;padding:var(--sp-2) 10px;resize:vertical;line-height:var(--lh-body)} +select.input{appearance:none;padding-right:28px;cursor:pointer} +.sel-wrap{position:relative;display:block} +.sel-wrap > .ic{position:absolute;right:8px;top:8px;pointer-events:none;color:var(--text-secondary)} +.field label{display:flex;gap:var(--sp-1);align-items:baseline} +.field label small{margin-left:2px} +.field .msg-row{display:flex;gap:var(--sp-3);align-items:baseline} +.field .msg-row .msg{flex:1} +.field .count{font:var(--fw-medium) var(--fs-caption)/var(--lh-caption) var(--font-figures);font-variant-numeric:tabular-nums;color:var(--text-secondary);white-space:nowrap} +.field.err .count{color:var(--warn-text)} +.field.err textarea.input,.field.err .select{border-color:var(--warn);box-shadow:inset 0 0 0 1px var(--warn)} +.field.dis label{color:var(--text-disabled)} +.field.fill{max-width:none} +.field + .field{margin-top:0} +.input-unit{display:flex} +.input-unit .input{border-top-right-radius:0;border-bottom-right-radius:0;font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-size:15px} +.input-unit .unit{display:flex;align-items:center;padding:0 10px;border:1px solid var(--border-control);border-left:0;border-radius:0 var(--radius-sm) var(--radius-sm) 0;background:var(--bg-sunken);color:var(--text-secondary);white-space:nowrap} +.search{position:relative;display:block} +.search > .ic{position:absolute;left:10px;top:8px;color:var(--text-secondary);pointer-events:none} +.search .input{padding-left:32px} +.search .kbd{position:absolute;right:8px;top:6px} + +/* 表单布局 */ +.form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-5) var(--sp-6)} +.form .span2{grid-column:1 / -1} +.form .field{max-width:none} +.form-sec{margin:0;padding:0;border:0;min-width:0} +.form-sec + .form-sec{margin-top:var(--sp-8);padding-top:var(--sp-6);border-top:1px solid var(--border-subtle)} +.form-sec > legend{float:left;width:100%;padding:0;margin:0 0 var(--sp-4);font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans)} +.form-sec > legend small{font-weight:var(--fw-regular);font-size:var(--fs-caption);color:var(--text-secondary);margin-left:var(--sp-2)} +.form-sec > legend + *{clear:both} +.form-foot{display:flex;gap:var(--sp-3);align-items:center;margin-top:var(--sp-8);padding-top:var(--sp-5);border-top:1px solid var(--border-subtle)} +.form-foot .cost{margin-right:auto} +.err-sum{margin-bottom:var(--sp-5)} +.err-sum ul{margin:var(--sp-1) 0 0;padding-left:1.2em} +.err-sum a{color:var(--warn-text);text-underline-offset:3px} + +/* ---- B5 下拉选择 / 下拉菜单(浮层,§5.1 只有浮层有阴影) ---- */ +.pop{position:relative;display:block} +.pop.inline{display:inline-block} +.select{display:flex;align-items:center;gap:var(--sp-2);width:100%;height:var(--control-h);padding:0 8px 0 10px;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);font:var(--fs-body) var(--font-sans);color:var(--text-primary);text-align:left;cursor:pointer} +.select:hover{border-color:var(--c-ink)} +.select[aria-expanded="true"],.select.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px;border-color:var(--c-ink)} +.select .val{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.select .ph{color:var(--text-secondary)} +.select > .ic{color:var(--text-secondary);transition:transform var(--dur-fast) var(--ease)} +.select[aria-expanded="true"] > .ic{transform:rotate(180deg)} +.select[disabled]{background:var(--bg-sunken);border-color:var(--bg-sunken);color:var(--text-disabled);cursor:not-allowed} +.select.sm{height:var(--control-h-sm)} +.menu{position:absolute;z-index:45;top:calc(100% + 4px);left:0;min-width:100%;max-height:280px;overflow:auto;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);padding:var(--sp-1) 0;margin:0;list-style:none} +.menu.right{left:auto;right:0} +.menu [role=option],.menu [role=menuitem]{display:flex;align-items:center;gap:var(--sp-2);min-height:32px;padding:5px var(--sp-3);cursor:pointer;white-space:nowrap} +.menu [role=option]:hover,.menu [role=menuitem]:hover,.menu .is-active{background:var(--bg-hover)} +.menu [aria-selected="true"]{font-weight:var(--fw-bold)} +.menu [aria-selected="true"]::after{content:"✓";margin-left:auto;padding-left:var(--sp-4)} +.menu [aria-disabled="true"]{color:var(--text-disabled);cursor:not-allowed;background:none} +.menu .grp{padding:var(--sp-2) var(--sp-3) 2px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.menu hr{border:0;border-top:1px solid var(--border-subtle);margin:var(--sp-1) 0} +.menu small{color:var(--text-secondary);font-size:var(--fs-caption);margin-left:auto;padding-left:var(--sp-4)} +.menu .ic{color:var(--text-secondary)} + +/* ---- B6 日期选择(§9.3 日期格式) ---- */ +.date-trigger{font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-size:15px} +.date-trigger .ph{font-family:var(--font-sans);font-size:var(--fs-body)} +.cal{position:absolute;z-index:45;top:calc(100% + 4px);left:0;width:280px;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);padding:var(--sp-3)} +.cal-h{display:flex;align-items:center;gap:var(--sp-1);margin-bottom:var(--sp-2)} +.cal-h b{flex:1;text-align:center;font-weight:var(--fw-bold)} +.cal-h b .fig{font-size:15px} +.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center} +.cal-grid .wd{font-size:var(--fs-caption);line-height:28px;color:var(--text-secondary)} +.cal-grid button{position:relative;height:32px;border:1px solid transparent;border-radius:var(--radius-sm);background:none;font:var(--fw-medium) 15px/1 var(--font-figures);font-variant-numeric:tabular-nums;color:var(--text-primary);cursor:pointer} +.cal-grid button:hover{background:var(--bg-hover)} +.cal-grid button.out{color:var(--text-disabled)} +.cal-grid button.today{border-color:var(--border-control)} +.cal-grid button.mark::after{content:"";position:absolute;left:50%;bottom:3px;width:4px;height:4px;margin-left:-2px;border-radius:50%;background:var(--c-ink)} +.cal-grid button[aria-selected="true"]{background:var(--c-ink);color:var(--text-on-dark);border-color:var(--c-ink)} +.cal-grid button[aria-selected="true"].mark::after{background:var(--c-white)} +.cal-grid button[disabled]{color:var(--text-disabled);cursor:not-allowed;background:none;text-decoration:line-through} +.cal-f{display:flex;align-items:center;gap:var(--sp-2);margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--border-subtle);font-size:var(--fs-caption);color:var(--text-secondary)} +.cal-f .btn{margin-left:auto} +.cal-f .time{width:72px;height:var(--control-h-sm);font-family:var(--font-figures);font-size:15px;text-align:center} +.dt-pair{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:var(--sp-2)} +.field.err .dt-pair > .input:not([aria-invalid]){border-color:var(--border-control);box-shadow:none} + +/* ---- B7 复选框、单选、开关(§8.2 批量勾选) ---- */ +.ck{appearance:none;flex:none;display:inline-grid;place-content:center;width:16px;height:16px;margin:0;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);cursor:pointer;vertical-align:-3px} +.ck:hover{border-color:var(--c-ink)} +.ck::after{content:"";display:none} +.ck:checked,.ck[type=checkbox]:indeterminate{background:var(--c-ink);border-color:var(--c-ink)} +.ck:checked::after{display:block;width:8px;height:4px;border:solid var(--c-white);border-width:0 0 1.75px 1.75px;transform:translateY(-1px) rotate(-45deg)} +.ck[type=checkbox]:indeterminate::after{display:block;width:8px;height:0;border-top:1.75px solid var(--c-white);transform:none} +.ck[type=radio]{border-radius:50%} +.ck[type=radio]:checked{background:var(--bg-surface);border-color:var(--c-ink)} +.ck[type=radio]:checked::after{width:8px;height:8px;border:0;border-radius:50%;background:var(--c-ink);transform:none} +.ck:disabled{background:var(--bg-sunken);border-color:var(--c-rule);cursor:not-allowed} +.ck:disabled:checked{background:var(--c-muted);border-color:var(--c-muted)} +.ck[type=radio]:disabled:checked{background:var(--bg-sunken)} +.ck[type=radio]:disabled:checked::after{background:var(--c-muted)} +.ck.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px} +.choice{display:inline-flex;gap:var(--sp-2);align-items:flex-start;cursor:pointer;line-height:var(--lh-body)} +.choice .ck{margin-top:3px} +.choice small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)} +.choice.dis{color:var(--text-disabled);cursor:not-allowed} +.choice.dis small{color:var(--text-disabled)} +.choices{display:flex;flex-direction:column;gap:var(--sp-2)} +.choices.h{flex-direction:row;flex-wrap:wrap;gap:var(--sp-2) var(--sp-6)} +.choice-card{display:flex;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3) var(--sp-4);border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);cursor:pointer} +.choice-card:has(:checked){border-color:var(--c-ink);box-shadow:inset 0 0 0 1px var(--c-ink)} +.choice-card .ck{margin-top:3px} +.choice-card b{display:block} +.choice-card small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)} +/* 开关:沿用 2px 圆角,不做胶囊形(§5.1) */ +.switch{display:inline-flex;align-items:center;gap:var(--sp-2);padding:0;border:0;background:none;font:var(--fs-body) var(--font-sans);color:var(--text-primary);cursor:pointer} +.switch .track{position:relative;width:32px;height:18px;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);transition:background var(--dur-fast) var(--ease)} +.switch .track::after{content:"";position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:1px;background:var(--c-muted);transition:transform var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease)} +.switch[aria-checked="true"] .track{background:var(--c-ink);border-color:var(--c-ink)} +.switch[aria-checked="true"] .track::after{transform:translateX(14px);background:var(--c-white)} +.switch .st{font-size:var(--fs-caption);color:var(--text-secondary);min-width:2em} +.switch[disabled]{color:var(--text-disabled);cursor:not-allowed} +.switch[disabled] .track{background:var(--bg-sunken);border-color:var(--c-rule)} +.switch[disabled] .track::after{background:var(--c-rule)} +.switch[disabled][aria-checked="true"] .track{background:var(--c-muted);border-color:var(--c-muted)} + +/* ---- B8 分段控件、标签页(§5.3 页签、§7.5 分类) ---- */ +.seg{display:inline-flex;border:1px solid var(--border-control);border-radius:var(--radius-sm);overflow:hidden;background:var(--bg-surface)} +.seg button{height:28px;padding:0 10px;border:0;background:none;font:var(--fs-body) var(--font-sans);color:var(--text-primary);cursor:pointer;display:flex;gap:6px;align-items:center;white-space:nowrap} +.seg button:hover{background:var(--bg-hover)} +.seg button + button{border-left:1px solid var(--border-subtle)} +.seg button[aria-pressed="true"]{background:var(--c-ink);color:var(--text-on-dark)} +.seg button[aria-pressed="true"] .fig{color:var(--text-on-dark)} +.seg button[aria-pressed="true"] .sev.hint,.seg button[aria-pressed="true"] .sev.major{background:var(--c-white)} +.seg .fig{font-weight:var(--fw-medium);color:var(--text-secondary)} +.seg .sev .fig{color:inherit} +.tabs{display:flex;gap:var(--sp-6);border-bottom:1px solid var(--border-subtle)} +.tabs.flush{border-bottom:0} +.tabs [role=tab]{display:flex;gap:6px;align-items:center;margin-bottom:-1px;padding:var(--sp-2) 0 10px;border:0;border-bottom:2px solid transparent;background:none;font:var(--fs-body) var(--font-sans);color:var(--text-secondary);cursor:pointer;text-decoration:none;white-space:nowrap} +.tabs [role=tab]:hover{color:var(--text-primary)} +.tabs [role=tab][aria-selected="true"]{color:var(--text-primary);font-weight:var(--fw-bold);border-bottom-color:var(--c-ink)} +.tabs [role=tab][aria-disabled="true"]{color:var(--text-disabled);cursor:not-allowed} +.tabs [role=tab] > .fig{font-weight:var(--fw-medium)} +.tabpanel{padding-top:var(--sp-4)} + +/* ---- B9 分页 ---- */ +.pagination{display:flex;align-items:center;gap:var(--sp-1);margin-top:var(--sp-3);font-size:var(--fs-caption);color:var(--text-secondary)} +.pagination .total{margin-right:auto} +.pagination .fig{font-weight:var(--fw-medium)} +.pagination button.pg{min-width:28px;height:28px;padding:0 6px;border:1px solid transparent;border-radius:var(--radius-sm);background:none;font:var(--fw-medium) 15px/1 var(--font-figures);font-variant-numeric:tabular-nums;color:var(--text-primary);cursor:pointer} +.pagination button.pg:hover{background:var(--bg-hover)} +.pagination button.pg[aria-current="page"]{border-color:var(--c-ink);font-weight:var(--fw-bold);background:var(--bg-surface)} +.pagination button.pg[disabled]{color:var(--text-disabled);cursor:not-allowed;background:none} +.pagination .gap{width:20px;text-align:center} +.pagination .jump{display:flex;align-items:center;gap:6px;margin-left:var(--sp-4)} +.pagination .jump .input{width:52px;height:28px;text-align:center;font-family:var(--font-figures);font-size:15px} + +/* ---- B10 轻提示 Toast(§7.12:只用于操作结果,4 秒消失,悬停暂停) ---- */ +.toasts{position:fixed;left:50%;bottom:var(--sp-8);transform:translateX(-50%);z-index:60;display:flex;flex-direction:column;gap:var(--sp-2);align-items:center;pointer-events:none} +.toast{display:flex;gap:var(--sp-3);align-items:center;min-height:40px;padding:var(--sp-2) var(--sp-4);background:var(--c-ink);color:var(--text-on-dark);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);pointer-events:auto;white-space:nowrap} +.toast .ok{display:inline-grid;place-items:center;width:16px;height:16px;border-radius:50%;background:var(--c-green);color:var(--c-white);font-size:11px;line-height:1} +.toast a,.toast button.lnk{color:var(--text-on-dark);text-decoration:underline;text-underline-offset:3px;background:none;border:0;font:inherit;cursor:pointer;padding:0} +.toast .sep{width:1px;align-self:stretch;margin:2px 0;background:color-mix(in srgb,var(--c-white) 24%,transparent)} +.toast.static{position:static} + +/* ---- B11 对话框(§7.14) ---- */ +.scrim{position:fixed;inset:0;z-index:50;display:grid;place-items:center;background:color-mix(in srgb,var(--c-ink) 32%,transparent)} +.dialog{position:relative;width:480px;max-width:calc(100vw - 32px);max-height:calc(100vh - 64px);overflow:auto;background:var(--bg-surface);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);padding:var(--sp-6)} +.dialog.wide{width:640px} +.dialog.static{position:static;max-width:none;box-shadow:var(--shadow-overlay)} +.dialog h2{font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans);margin:0 var(--sp-8) var(--sp-3) 0} +.dialog .x{position:absolute;top:var(--sp-4);right:var(--sp-4)} +.dialog .body > p{margin:0 0 var(--sp-3)} +.dialog .body > .field{margin-top:var(--sp-4)} +.dialog .foot{display:flex;justify-content:flex-end;align-items:center;gap:var(--sp-2);margin-top:var(--sp-6)} +.dialog .foot .left{margin-right:auto} + +/* ---- B12 提醒补充(§7.12、§7.15) ---- */ +.alert{align-items:flex-start} +.alert > div{flex:1;min-width:0} +.alert .act{margin-left:auto;flex:none;display:flex;gap:var(--sp-2);align-items:center} +.alert .sub{display:block;margin-top:2px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.alert .fig{font-weight:var(--fw-medium)} +.errno{font:var(--fw-medium) var(--fs-caption)/1 var(--font-figures);color:var(--text-secondary);letter-spacing:.02em} + +/* ---- B13 提示气泡(悬停 / 聚焦显示,§6 只有图标的按钮要有悬停提示) ---- */ +[data-tip]{position:relative} +[data-tip]:hover::after,[data-tip]:focus-visible::after,[data-tip].tip-on::after{content:attr(data-tip);position:absolute;z-index:55;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);max-width:280px;width:max-content;padding:5px var(--sp-2);border-radius:var(--radius-md);background:var(--c-ink);color:var(--text-on-dark);font:var(--fw-regular) var(--fs-caption)/var(--lh-caption) var(--font-sans);white-space:normal;text-align:left;box-shadow:var(--shadow-overlay);pointer-events:none} +[data-tip].tip-below:hover::after,[data-tip].tip-below.tip-on::after{bottom:auto;top:calc(100% + 6px)} +[data-tip].tip-start:hover::after,[data-tip].tip-start.tip-on::after{left:0;transform:none} +[data-tip].tip-left:hover::after,[data-tip].tip-left.tip-on::after{left:auto;right:0;transform:none} + +/* ---- B14 空状态、骨架屏、进度(§7.11、§7.15) ---- */ +.empty{display:flex;flex-direction:column;align-items:center;text-align:center;padding:var(--sp-12) var(--sp-6)} +.empty > .ic{color:var(--text-secondary);margin-bottom:var(--sp-3)} +.empty b{font-size:var(--fs-title);line-height:var(--lh-title)} +.empty p{margin:var(--sp-1) 0 var(--sp-5);color:var(--text-secondary);max-width:30em} +.empty.sm{padding:var(--sp-8) var(--sp-4)} +.empty.sm p{margin-bottom:var(--sp-3)} +.skel{display:block;height:12px;border-radius:var(--radius-sm);background:var(--bg-sunken);animation:skel 1.6s var(--ease) infinite alternate} +.skel.t{height:16px;width:40%} +.skel.w60{width:60%} .skel.w80{width:80%} .skel.w30{width:30%} .skel.w20{width:20%} +.skel + .skel{margin-top:var(--sp-2)} +.skel-row{display:grid;grid-template-columns:24px minmax(0,1fr) 120px;gap:var(--sp-3);padding:var(--sp-3) 0;border-top:1px solid var(--border-subtle)} +.skel-row:first-child{border-top:0} +@keyframes skel{from{opacity:1}to{opacity:.45}} +@media (prefers-reduced-motion:reduce){.skel{animation:none}} +.progress{display:flex;align-items:center;gap:var(--sp-3)} +.progress .bar{flex:1;margin:0} +.progress .fig{font-weight:var(--fw-medium);min-width:3em;text-align:right} +.steps .fig{font-weight:var(--fw-medium);color:var(--text-secondary);margin-left:auto} +.steps li > span:nth-child(2){flex:1} +.steps .fail .dot{border-color:var(--warn);color:var(--warn-text);font-weight:var(--fw-bold)} +.steps .fail{color:var(--warn-text)} + +/* ---- B15 杂项小件 ---- */ +.kbd{display:inline-block;min-width:20px;padding:0 5px;border:1px solid var(--border-control);border-bottom-width:2px;border-radius:3px;font:var(--fw-medium) var(--fs-caption)/18px var(--font-figures);text-align:center;background:var(--bg-surface);color:var(--text-primary)} +.muted{color:var(--text-secondary)} +.nw{white-space:nowrap} +.cap{font-size:var(--fs-caption);line-height:var(--lh-caption)} +.fig.m{font-weight:var(--fw-medium)} +.kv{display:grid;grid-template-columns:96px minmax(0,1fr);gap:var(--sp-2) var(--sp-4);margin:0} +.kv dt{color:var(--text-secondary)} +.kv dd{margin:0} +.chip{display:inline-flex;align-items:center;gap:var(--sp-1);height:24px;padding:0 4px 0 8px;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);font-size:var(--fs-caption);white-space:nowrap} +.chip button{display:inline-grid;place-items:center;width:18px;height:18px;padding:0;border:0;border-radius:var(--radius-sm);background:none;color:var(--text-secondary);cursor:pointer} +.chip button:hover{background:var(--bg-hover);color:var(--text-primary)} +.drop{display:flex;flex-direction:column;align-items:center;gap:var(--sp-1);border:1.5px dashed var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);padding:var(--sp-8) var(--sp-6);text-align:center;cursor:pointer} +.drop:hover,.drop.is-over{border-color:var(--c-ink);background:var(--bg-hover)} +.drop > .ic{color:var(--text-secondary)} +.drop b{font-size:var(--fs-title);line-height:var(--lh-title)} +.drop span{color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)} +.file-row{display:grid;grid-template-columns:40px minmax(0,1fr) auto;gap:var(--sp-3);align-items:center;padding:var(--sp-3) var(--sp-4);background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm)} +.file-row + .file-row{margin-top:var(--sp-2)} +.file-row .ext{font:var(--fw-bold) 11px/1 var(--font-figures);border:1px solid var(--border-control);border-radius:var(--radius-sm);padding:4px 0;text-align:center} +.file-row b{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.file-row .acts{display:flex;gap:var(--sp-1);align-items:center} +.flowline{display:flex;align-items:center} +.flowline .st{display:flex;align-items:center;gap:var(--sp-2);color:var(--text-secondary);white-space:nowrap} +.flowline .st i{width:10px;height:10px;border-radius:50%;border:1.5px solid var(--border-control);flex:none} +.flowline .st.done{color:var(--text-primary)} +.flowline .st.done i{background:var(--c-ink);border-color:var(--c-ink)} +.flowline .st.now{color:var(--text-primary);font-weight:var(--fw-bold)} +.flowline .st.now i{border:3px solid var(--c-ink)} +.flowline .ln{flex:1;min-width:16px;height:1px;background:var(--border-control);margin:0 var(--sp-2)} +.flowline.sm{font-size:var(--fs-caption)} +.flowline.sm .st i{width:8px;height:8px} +.flowline.sm .st.now i{border-width:2.5px} + +/* ---- B16 AI 产出块(§3.3 左侧虚线竖线、§7.16) ---- */ +.ai-block{border-left:1.5px dashed var(--ai);padding:var(--sp-1) 0 var(--sp-1) var(--sp-3)} +.ai-block.done{border-left:2px solid var(--c-ink)} +.ai-label{display:flex;gap:var(--sp-2);align-items:center;margin-bottom:var(--sp-2);font-size:var(--fs-caption);color:var(--text-secondary)} +.flag.box{border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:0 5px;line-height:18px} +.cite-src.inline{display:inline;margin-left:6px;white-space:nowrap} +.sign.ign{color:var(--text-secondary)} +.sign.sm{font-size:var(--fs-caption);margin-top:var(--sp-2)} +.more-cites{display:inline-block;margin-top:var(--sp-2);font-size:var(--fs-caption);color:var(--ai);background:none;border:0;padding:0;cursor:pointer;font-family:var(--font-sans)} +.more-cites:hover{text-decoration:underline;text-underline-offset:3px} +.cite q.clamp{-webkit-line-clamp:3} +.cite .expand{font-size:var(--fs-caption);color:var(--ai);background:none;border:0;padding:0;cursor:pointer;font-family:var(--font-sans)} + +/* ---- B17 卡片 ---- */ +.card-h{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:var(--sp-3)} +.card-h h3{margin:0;font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans)} +.card-h .more{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center} +.card.tight{padding:var(--sp-4) var(--sp-5)} +.card-foot{display:flex;gap:var(--sp-2);align-items:center;margin:var(--sp-4) calc(-1 * var(--sp-6)) calc(-1 * var(--sp-5));padding:var(--sp-3) var(--sp-6);border-top:1px solid var(--border-subtle)} +.card.tight .card-foot{margin:var(--sp-4) calc(-1 * var(--sp-5)) calc(-1 * var(--sp-4));padding:var(--sp-3) var(--sp-5)} +.card-foot .r{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center} +/* 项目卡 */ +.pcard{display:flex;flex-direction:column;gap:var(--sp-3);position:relative} +.pcard.is-fatal::before{content:"";position:absolute;left:-1px;top:var(--sp-5);bottom:var(--sp-5);width:3px;background:var(--sev-fatal)} +.pcard .pc-top{display:flex;gap:var(--sp-3);align-items:flex-start} +.pcard .pc-top > div{flex:1;min-width:0} +.pcard .card-foot{margin-top:auto} +.pcard .facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-3);padding:var(--sp-3) 0;border-top:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle)} +.pcard .facts small{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.pcard .facts > div{min-width:0} +/* 统计格 */ +.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--sp-4)} +.stat{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:var(--sp-4) var(--sp-5)} +.stat small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)} +.stat .big{font:var(--fw-bold) var(--fs-figure)/var(--lh-figure) var(--font-figures);font-variant-numeric:tabular-nums} +.stat .unit{color:var(--text-secondary);margin-left:4px} +.stat p{margin:var(--sp-1) 0 0;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.stat p.due-soon,.stat p.due-today{color:var(--warn-text)} +/* 额度卡 */ +.quota small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)} +.quota .q2{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-5)} +.quota p{margin:0;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.quota p.warn{color:var(--warn-text)} +/* 关键日期 */ +.dates{list-style:none;margin:0;padding:0} +.dates li{display:grid;grid-template-columns:88px minmax(0,1fr) auto;gap:var(--sp-3);align-items:baseline;padding:var(--sp-2) 0;border-top:1px solid var(--border-subtle)} +.dates li:first-child{border-top:0} +.dates li > .fig{font-weight:var(--fw-medium);font-size:15px} +.dates li.past{color:var(--text-secondary)} +.dates li.key > span:nth-child(2) > b{font-weight:var(--fw-bold)} +.dates .left{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)} +.dates .left.due-soon,.dates .left.due-today{color:var(--warn-text)} +.dates .cite-src{white-space:nowrap} + +/* ---- B18 表格补充:排序、勾选、选中行、批量栏、空结果(§7.7、§8.2) ---- */ +.toolbar{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-3);flex-wrap:wrap} +.toolbar .right{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center} +.toolbar .search{width:260px} +.toolbar .pop .select{width:auto;min-width:132px} +.filters-on{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;margin:calc(-1 * var(--sp-1)) 0 var(--sp-3);font-size:var(--fs-caption);color:var(--text-secondary)} +table.data th.sortable{padding:0} +.th-sort{display:flex;align-items:center;gap:4px;width:100%;padding:var(--sp-2) var(--sp-3);border:0;background:none;font:inherit;color:inherit;cursor:pointer;text-align:left;white-space:nowrap} +.th-sort:hover{color:var(--text-primary)} +.th-sort .ic{width:14px;height:14px;color:var(--text-disabled)} +th[aria-sort] .th-sort{color:var(--text-primary)} +th[aria-sort] .th-sort .ic{color:var(--text-primary)} +table.data th.r .th-sort{justify-content:flex-end} +table.data .ckc{width:40px;padding-right:0} +table.data.hover tbody tr{cursor:pointer} +table.data.hover tbody tr:hover td{background:var(--bg-hover)} +table.data tbody tr.sel td{background:var(--bg-sunken)} +table.data tbody tr.sel td:first-child{box-shadow:inset 2px 0 0 var(--c-ink)} +table.data td.empty-cell{height:auto;padding:0} +table.data td .cap{color:var(--text-secondary)} +table.data .acts{white-space:nowrap;text-align:right} +table.data td.mid{vertical-align:middle} +table.data tr.is-fatal td:first-child{box-shadow:inset 3px 0 0 var(--sev-fatal)} +.batchbar{position:sticky;top:calc(52px + var(--sp-2));z-index:10;display:flex;align-items:center;gap:var(--sp-3);height:44px;padding:0 var(--sp-4);margin-bottom:var(--sp-2);background:var(--bg-surface);border:1px solid var(--c-ink);border-radius:var(--radius-sm)} +.batchbar b .fig{font-weight:var(--fw-bold)} +.batchbar .r{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center} +.list-box{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm)} +.list-box > .lb-h{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-1) var(--sp-4);border-bottom:1px solid var(--border-subtle);background:var(--bg-sunken);font-size:var(--fs-caption);color:var(--text-secondary)} +.list-box > .lb-h .r{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center} +.list-box .item{padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--border-subtle)} +.list-box .item:first-of-type{border-top:0} +.list-box .item.has-ck{grid-template-columns:20px 24px minmax(0,1fr) auto} +.list-box .item .ck{margin-top:3px} +.item.sel{background:var(--ai-soft);box-shadow:inset 3px 0 0 var(--ai)} +.item.focus{box-shadow:inset 0 0 0 2px var(--c-ink)} +.item[tabindex]{cursor:pointer;outline:none} +.item[tabindex]:hover{background:var(--bg-hover)} +.item.sel:hover{background:var(--ai-soft)} +.item .upd{margin-top:6px;display:flex;gap:var(--sp-2);align-items:center;font-size:var(--fs-caption)} +.item .upd .btn{height:24px;font-size:var(--fs-caption)} + +/* 问题行的紧凑版本(列表页) */ +.finding .ckw{position:absolute;left:var(--sp-2);top:calc(var(--sp-5) + 2px)} +.finding.has-ck{padding-left:var(--sp-8)} +.finding.focus{box-shadow:inset 0 0 0 2px var(--c-ink)} +.u4{margin-top:var(--sp-3);padding:var(--sp-3) var(--sp-4);background:var(--bg-sunken);border-radius:var(--radius-sm)} +.u4 .field{max-width:none} +.u4 .row{margin-top:var(--sp-2)} +.group-h{display:flex;align-items:center;gap:var(--sp-2);width:100%;padding:var(--sp-3) var(--sp-6);border:0;border-top:1px solid var(--border-subtle);background:var(--bg-sunken);color:var(--text-secondary);font:var(--fs-body) var(--font-sans);cursor:pointer;text-align:left} +.group-h:hover{color:var(--text-primary)} + +.verdict.hint{color:var(--sev-hint)} + +/* ---- B19 对照布局:分类导航 + 列表 + 原文查看器(§5.3、§7.8) ---- */ +.split{display:grid;grid-template-columns:200px minmax(0,1fr) minmax(420px,45%);flex:1;min-height:0} +.cats{border-right:1px solid var(--border-subtle);padding:var(--sp-4) var(--sp-3);overflow:auto} +.cats button{display:flex;width:100%;align-items:center;justify-content:space-between;height:34px;padding:0 var(--sp-3);border:0;background:none;border-radius:var(--radius-sm);font:var(--fs-body) var(--font-sans);color:var(--text-primary);cursor:pointer;text-align:left} +.cats button:hover{background:var(--bg-hover)} +.cats button[aria-current="true"]{background:var(--bg-surface);box-shadow:inset 2px 0 0 var(--c-ink);font-weight:var(--fw-bold)} +.cats button .fig{font-weight:var(--fw-medium);color:var(--text-secondary)} +.cats button.ai,.cats button.ai .fig{color:var(--ai)} +.cats hr{border:0;border-top:1px solid var(--border-subtle);margin:var(--sp-3) 0} +.cats .hint{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);padding:0 var(--sp-3);margin:0} +.viewer{min-height:0} +.viewer-bar{flex-wrap:wrap;min-height:44px} +.viewer-bar .doc-name{display:flex;gap:var(--sp-2);align-items:baseline;min-width:0} +.viewer-bar .doc-file{color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px} +.viewer-body{flex:1;overflow:auto;padding:var(--sp-4) 0} +.viewer .sheet{min-height:560px;margin:0 auto;width:calc(100% - 48px);max-width:640px;position:relative;background:var(--c-white);color:var(--text-primary)} +.sheet .pno{position:absolute;bottom:14px;left:0;right:0;text-align:center;font:var(--fs-caption) var(--font-figures);color:var(--text-secondary)} +.sheet .h{font-weight:var(--fw-bold);margin:4px 0 10px} +.sheet .faint{color:var(--text-secondary)} +.sheet .stub{height:9px;background:var(--bg-sunken);margin:12px 0;border-radius:1px} +.sheet .stub.s{width:60%} +.viewer-note{font-size:var(--fs-caption);color:var(--text-secondary);padding:var(--sp-2) var(--sp-6) 0;margin:0;text-align:center} diff --git a/docs/design/ui-demo/demo.js b/docs/design/ui-demo/demo.js new file mode 100644 index 0000000..028aa55 --- /dev/null +++ b/docs/design/ui-demo/demo.js @@ -0,0 +1,275 @@ +/* 标博士 · UI demo 公共脚本:图标、页面框架、轻提示、对话框、下拉、日期、开关、标签页、排序。 + * 只演示交互,不连后端。正式实现里这些交互件由 Reka UI 打底(AR10)。 */ +(function () { + const $ = (s, el = document) => el.querySelector(s); + const $$ = (s, el = document) => [...el.querySelectorAll(s)]; + const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); + + /* Lucide 图标(ISC 许可)路径 */ + const ICONS = { + grid: '', + folder: '', + cert: '', + gauge: '', + bell: '', + help: '', + x: '', + plus: '', + upload: '', + download: '', + file: '', + search: '', + "chev-down": '', + "chev-left": '', + "chev-right": '', + sort: '', + "arrow-up": '', + "arrow-down": '', + calendar: '', + check: '', + more: '', + filter: '', + inbox: '', + clock: '', + trash: '', + pencil: '', + history: '', + keyboard: '', + refresh: '', + send: '', + list: '', + paperclip: '', + book: '', + archive: '', + layers: '' + }; + const icon = (n, cls = "") => ``; + function hydrate(root = document) { + $$("i[data-ic]", root).forEach((el) => { el.outerHTML = icon(el.dataset.ic, el.className); }); + } + + /* ---------- demo 顶部导航(控件页) ---------- */ + const PAGES = [ + ["index.html", "目录"], ["controls.html", "① 控件"], ["forms.html", "② 表单"], ["cards.html", "③ 卡片"], + ["lists.html", "④ 列表与表格"], ["page-workbench.html", "⑤ 工作台"], ["page-reading.html", "⑥ 读标"] + ]; + function demoTop(cur) { + const h = $("[data-demo-top]"); + if (!h) return; + h.className = "d-top"; + h.innerHTML = `标博士UI demo + `; + } + + /* ---------- 产品页面框架(组合页) ---------- */ + function shell(opt) { + const nav = [["page-workbench.html", "grid", "工作台", "workbench"], ["#", "cert", "资质库", "quals"], ["#", "gauge", "额度", "quota"]] + .map(([h, i, t, k]) => `${icon(i, "lg")}${t}`).join(""); + const crumbs = (opt.crumbs || []).map((c, i, a) => i < a.length - 1 ? `${c[0]}/` : `${c[0]}`).join(""); + document.body.classList.add("app"); + document.body.insertAdjacentHTML("afterbegin", ``); + const main = $("main"); + main.classList.add("main"); + main.insertAdjacentHTML("afterbegin", `
${crumbs}
+
+ 解析37检查12 + + +
`); + $("[data-notice]").onclick = () => toast("解析完成:补遗 1 已并入清单(新增 2 条,更新 1 条)。"); + $("[data-keys]").onclick = keysDialog; + } + function keysDialog() { + dialog({ title: "快捷键", cancel: null, ok: "知道了", + body: ` + + + + +
J / K下一条 / 上一条
O 或 Enter在原文查看器里打开当前出处
1 2 3确认无误 / 已修改 / 忽略(3 会打开原因输入框)
U撤销当前条的处理
?显示本说明
` }); + } + + /* ---------- 轻提示:4 秒消失,悬停暂停 ---------- */ + function toast(msg, opt = {}) { + let box = $(".toasts"); + if (!box) { box = document.createElement("div"); box.className = "toasts"; box.setAttribute("role", "status"); box.setAttribute("aria-live", "polite"); document.body.appendChild(box); } + const t = document.createElement("div"); + t.className = "toast"; + t.innerHTML = `${opt.ok === false ? "" : ''}${msg}${opt.undo ? '' : ""}`; + box.appendChild(t); + let left = 4000, start = Date.now(), timer = setTimeout(() => t.remove(), left); + t.addEventListener("mouseenter", () => { clearTimeout(timer); left -= Date.now() - start; }); + t.addEventListener("mouseleave", () => { start = Date.now(); timer = setTimeout(() => t.remove(), Math.max(left, 1200)); }); + if (opt.undo) $("[data-undo]", t).onclick = () => { t.remove(); opt.undo(); }; + } + + /* ---------- 对话框:Esc 关闭、焦点回到触发处 ---------- */ + function dialog({ title, body, ok = "确定", cancel = "取消", wide = false, onOk, onOpen, footLeft = "" }) { + const back = document.activeElement; + const s = document.createElement("div"); + s.className = "scrim"; + s.innerHTML = ``; + document.body.appendChild(s); + document.body.style.overflow = "hidden"; + const close = () => { s.remove(); document.body.style.overflow = ""; document.removeEventListener("keydown", onKey, true); if (back && back.focus) back.focus(); }; + const onKey = (e) => { + if (e.key === "Escape") { e.stopPropagation(); close(); } + if (e.key === "Tab") { // 焦点锁定在对话框内 + const f = $$("button:not([disabled]), input:not([disabled]), textarea, select, a[href]", s); + if (!f.length) return; + if (e.shiftKey && document.activeElement === f[0]) { e.preventDefault(); f[f.length - 1].focus(); } + else if (!e.shiftKey && document.activeElement === f[f.length - 1]) { e.preventDefault(); f[0].focus(); } + } + }; + document.addEventListener("keydown", onKey, true); + s.addEventListener("mousedown", (e) => { if (e.target === s) close(); }); + $("[data-x]", s).onclick = close; + if (cancel) $("[data-cancel]", s).onclick = close; + $("[data-ok]", s).onclick = () => { if (!onOk || onOk(s) !== false) close(); }; + hydrate(s); init(s); + if (onOpen) onOpen(s); + const f = $("textarea, input:not([type=checkbox]):not([type=radio]), [data-ok]", s); + if (f) f.focus(); + return s; + } + + /* ---------- 下拉选择(listbox)与下拉菜单 ---------- */ + function closePops(except) { + $$(".pop.open").forEach((p) => { if (p !== except) { p.classList.remove("open"); const t = $("[aria-expanded]", p); if (t) t.setAttribute("aria-expanded", "false"); const m = $(".menu,.cal", p); if (m) m.hidden = true; } }); + } + function initSelect(p) { + const trg = $(".select, [data-menu-trigger]", p), menu = $(".menu", p); + if (!trg || !menu || p.dataset.ready) return; + p.dataset.ready = 1; + menu.hidden = true; + const opts = () => $$('[role=option]:not([aria-disabled="true"]), [role=menuitem]:not([aria-disabled="true"])', menu); + let ai = -1; + const mark = (i) => { opts().forEach((o, k) => o.classList.toggle("is-active", k === i)); ai = i; const o = opts()[i]; if (o) o.scrollIntoView({ block: "nearest" }); }; + const open = () => { closePops(p); p.classList.add("open"); menu.hidden = false; trg.setAttribute("aria-expanded", "true"); const sel = opts().findIndex((o) => o.getAttribute("aria-selected") === "true"); mark(sel < 0 ? 0 : sel); }; + const close = () => { p.classList.remove("open"); menu.hidden = true; trg.setAttribute("aria-expanded", "false"); }; + const pick = (o) => { + if (o.getAttribute("role") === "option") { + $$("[role=option]", menu).forEach((x) => x.setAttribute("aria-selected", x === o ? "true" : "false")); + const v = $(".val", trg); if (v) v.innerHTML = o.dataset.label || o.firstChild.textContent; + p.closest(".field")?.classList.remove("err"); + p.dispatchEvent(new CustomEvent("change", { detail: o.dataset.value || o.textContent })); + } else if (o.dataset.toast) toast(o.dataset.toast); + close(); trg.focus(); + }; + trg.addEventListener("click", (e) => { e.stopPropagation(); p.classList.contains("open") ? close() : open(); }); + trg.addEventListener("keydown", (e) => { + const n = opts().length; + if (["ArrowDown", "ArrowUp"].includes(e.key)) { e.preventDefault(); if (!p.classList.contains("open")) return open(); mark((ai + (e.key === "ArrowDown" ? 1 : n - 1)) % n); } + else if (e.key === "Enter" || e.key === " ") { if (p.classList.contains("open") && ai >= 0) { e.preventDefault(); pick(opts()[ai]); } } + else if (e.key === "Escape" && p.classList.contains("open")) { e.stopPropagation(); close(); } + }); + menu.addEventListener("click", (e) => { const o = e.target.closest('[role=option], [role=menuitem]'); if (o && o.getAttribute("aria-disabled") !== "true") { e.stopPropagation(); pick(o); } }); + } + + /* ---------- 日期选择 ---------- */ + const WD = ["一", "二", "三", "四", "五", "六", "日"]; + function calHTML(y, m, sel, opt = {}) { + const first = new Date(y, m, 1), start = (first.getDay() + 6) % 7, days = new Date(y, m + 1, 0).getDate(); + const prevDays = new Date(y, m, 0).getDate(); + let cells = ""; + for (let i = 0; i < 42; i++) { + const d = i - start + 1; + let dd, out = false, mm = m; + if (d < 1) { dd = prevDays + d; out = true; mm = m - 1; } else if (d > days) { dd = d - days; out = true; mm = m + 1; } else dd = d; + const date = new Date(y, mm, dd), key = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; + const cls = [out ? "out" : "", key === opt.today ? "today" : "", (opt.marks || []).includes(key) ? "mark" : ""].join(" "); + cells += ``; + if (i === 34 && d >= days) break; + } + return `
${y} 年 ${m + 1} 月
+
${WD.map((w) => `${w}`).join("")}${cells}
+
${opt.time ? `时间 ` : '点圆点的日期是本项目的关键日期'}
`; + } + function initDate(p) { + if (p.dataset.ready) return; + p.dataset.ready = 1; + const trg = $(".select", p), cal = $(".cal", p); + let [y, m] = (p.dataset.month || "2026-11").split("-").map(Number); m -= 1; + let sel = p.dataset.value || ""; + const opt = { today: "2026-11-3", marks: (p.dataset.marks || "").split(",").filter(Boolean), time: p.dataset.time, min: p.dataset.min ? new Date(...p.dataset.min.split("-").map((v, i) => i === 1 ? v - 1 : +v)) : null }; + const render = () => { cal.innerHTML = calHTML(y, m, sel, opt); }; + const fmt = (k) => { const [a, b, c] = k.split("-"); return `${a}-${String(b).padStart(2, "0")}-${String(c).padStart(2, "0")}`; }; + render(); + if (!p.classList.contains("d-open")) cal.hidden = true; + trg?.addEventListener("click", (e) => { e.stopPropagation(); const o = !p.classList.contains("open"); closePops(p); p.classList.toggle("open", o); cal.hidden = !o; trg.setAttribute("aria-expanded", o); }); + cal.addEventListener("click", (e) => { + e.stopPropagation(); + const b = e.target.closest("button"); if (!b) return; + if (b.dataset.cm) { m += +b.dataset.cm; if (m < 0) { m = 11; y--; } if (m > 11) { m = 0; y++; } render(); return; } + if (b.dataset.today) { y = 2026; m = 10; sel = opt.today; } + else if (b.dataset.d) sel = b.dataset.d; + else return; + render(); + if (trg) { $(".val", trg).innerHTML = fmt(sel); p.closest(".field")?.classList.remove("err"); if (!p.classList.contains("d-open")) { p.classList.remove("open"); cal.hidden = true; trg.setAttribute("aria-expanded", "false"); trg.focus(); } } + p.dispatchEvent(new CustomEvent("change", { detail: fmt(sel) })); + }); + } + + /* ---------- 开关、分段、标签页、半选、排序、加载按钮 ---------- */ + function init(root = document) { + $$(".pop[data-select], .pop[data-menu]", root).forEach(initSelect); + $$(".pop[data-date]", root).forEach(initDate); + $$(".switch:not([data-ready])", root).forEach((s) => { + s.dataset.ready = 1; + const upd = () => { const st = $(".st", s); if (st) st.textContent = s.getAttribute("aria-checked") === "true" ? "开" : "关"; }; + upd(); + s.addEventListener("click", () => { if (s.disabled) return; s.setAttribute("aria-checked", s.getAttribute("aria-checked") === "true" ? "false" : "true"); upd(); }); + }); + $$(".seg:not([data-ready])", root).forEach((g) => { + g.dataset.ready = 1; + g.addEventListener("click", (e) => { const b = e.target.closest("button"); if (!b) return; $$("button", g).forEach((x) => x.setAttribute("aria-pressed", x === b)); g.dispatchEvent(new CustomEvent("change", { detail: b.dataset.v })); }); + }); + $$("[role=tablist]:not([data-ready])", root).forEach((tl) => { + tl.dataset.ready = 1; + const tabs = $$("[role=tab]", tl); + const sel = (t) => { tabs.forEach((x) => { const on = x === t; x.setAttribute("aria-selected", on); x.tabIndex = on ? 0 : -1; const pn = x.getAttribute("aria-controls"); if (pn) document.getElementById(pn).hidden = !on; }); }; + tabs.forEach((t) => t.addEventListener("click", () => { if (t.getAttribute("aria-disabled") !== "true") sel(t); })); + tl.addEventListener("keydown", (e) => { + const i = tabs.indexOf(document.activeElement); if (i < 0) return; + const d = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0; if (!d) return; + e.preventDefault(); let j = i; do { j = (j + d + tabs.length) % tabs.length; } while (tabs[j].getAttribute("aria-disabled") === "true"); + tabs[j].focus(); sel(tabs[j]); + }); + }); + $$("input[data-indeterminate]", root).forEach((c) => { c.indeterminate = true; }); + $$("[data-loading]:not([data-ready])", root).forEach((b) => { + b.dataset.ready = 1; + b.addEventListener("click", () => { + if (b.classList.contains("is-loading")) return; + b.classList.add("is-loading"); b.setAttribute("aria-busy", "true"); + setTimeout(() => { b.classList.remove("is-loading"); b.removeAttribute("aria-busy"); if (b.dataset.loading) toast(b.dataset.loading); }, 2200); + }); + }); + } + document.addEventListener("click", () => closePops()); + document.addEventListener("keydown", (e) => { if (e.key === "Escape") closePops(); }); + + /* 可排序表格:th[data-sort] 里放 button.th-sort;按 data-sort 指定的列取 td 的 data-v 或文字 */ + function sortable(table, onSort) { + $$("th[data-sort]", table).forEach((th) => { + const b = $(".th-sort", th); + b.insertAdjacentHTML("beforeend", icon("sort")); + b.addEventListener("click", () => { + const dir = th.getAttribute("aria-sort") === "ascending" ? "descending" : "ascending"; + $$("th[aria-sort]", table).forEach((x) => { x.removeAttribute("aria-sort"); $("svg", x).outerHTML = icon("sort"); }); + th.setAttribute("aria-sort", dir); + $("svg", th).outerHTML = icon(dir === "ascending" ? "arrow-up" : "arrow-down"); + onSort(th.dataset.sort, dir); + }); + }); + } + + window.Demo = { $, $$, esc, icon, hydrate, demoTop, shell, toast, dialog, init, sortable, keysDialog, calHTML }; + document.addEventListener("DOMContentLoaded", () => { hydrate(); init(); }); +})(); diff --git a/docs/design/ui-demo/forms.html b/docs/design/ui-demo/forms.html new file mode 100644 index 0000000..df54ee9 --- /dev/null +++ b/docs/design/ui-demo/forms.html @@ -0,0 +1,300 @@ + + + + + +表单 · 标博士 UI demo + + + + + + + + +
+
+
+

② 表单

+

三张真实业务表单:新建项目、资质录入、处理问题时的「忽略并注明原因」。标签在上,必填写「(必填)」,错误用赭色边框 + 下方赭色小字 + 「!」图标,离开输入框时校验、提交时汇总。

+ +
+ + +
+
01

新建项目

规范 §7.13 · §8.3 · FR-M2-01
+
+
+ + +
+ 项目信息招标文件解析后会自动补全,可以先只填名称 +
+
只在你的工作台里显示,可以用简称
+
+
+
+
+ + + +
+
+
+
+
+
+ 用于工作台的倒计时;补遗更正后会提示你核对
+
+
+ +
+ 招标文件PDF、Word 或扫描件;补遗可以以后追加 +
+
拖入或点击上传招标文件单个文件不超过 200 MB · 扩展名不可信时以文件内容为准
+
+
+
+
+ +
+ 扣 1 次解析 · 剩 37 次 · 解析失败、超时自动退回 + + +
+
+ +
+
+

试一试

+
    +
  1. 什么都不填,直接点「创建并开始解析」:顶部汇总 + 每个字段下方说明,点汇总里的链接跳到字段。
  2. +
  3. 在项目名称里输入再清空,离开输入框时就会校验。
  4. +
  5. 点上传区,会加入两个示意文件,其中一个扩展名和内容不符。
  6. +
  7. 全部填好后提交:按钮变成「正在创建…」,宽度不变,表单整体禁用。
  8. +
+
+
+

规则

+
    +
  • 必填写「(必填)」,不用红色星号;选填项多的表单反过来写「(选填)」。
  • +
  • 错误说明写清楚怎么改,不写「格式错误」。
  • +
  • 会扣点的提交按钮,左侧直接写扣多少、还剩多少。
  • +
  • 文件扩展名和内容不符只提醒,不拦截(样本分析 07 的结论)。
  • +
+
+
+
+
+ + +
+
02

同一字段的状态对照

规范 §7.13
+
+
+
正常
+
聚焦
+
错误
!请填写证书编号,查标时用它核对附件
+
禁用
已被 2 个已递交项目引用,不能修改
+
+
+
+
+

填写中

正常状态
+
+
+
+
+
不扣额度
+
+
+

提交中

整表禁用,按钮换成「正在…」,宽度不变
+
+
+
+
+
+
+
不扣额度
+
+
+
+ + +
+
03

资质录入

FR-M6 最小资质库 · 红线 5 不编造
+
+ +
+
+

打开真实对话框 可以点

+ +

空表单直接保存会看到错误状态;「附件」可以稍后补录,列表里显示「待补录」占位,不生成内容。

+
+
+

提醒和错误的区别

+
    +
  • 错误(赭色边框):必须改了才能保存,例如漏填证书编号。
  • +
  • 提醒(只有赭色小字,边框不变):可以保存,但查标会提示,例如证书在投标截止日前到期。
  • +
  • 到期判断以每个项目的投标截止日为准,不以今天为准。
  • +
+
+
+
+
+ + +
+
04

忽略并注明原因 · 致命问题的确认说明

规范 §7.4 · §7.13 · U4
+
+
+
+
重要开标一览表与分项价格表合计不一致
+

开标一览表 287.60 万元,分项价格表合计 286.80 万元,相差 0.80 万元。按招标文件规定,两者不一致时以开标一览表为准,不作无效处理;但会影响价格评审。

+
+
+ +
+ +
!至少写 5 个字,说明为什么不处理3 / 200
+
+
「已忽略」不等于确认无误,报告里单独列出
+
+
+ +
+
+
+
致命授权委托书的单位签章位置未发现盖章新增
+

商务册第 5 页《法定代表人授权委托书》的「投标人(电子签章)」位置是空的。本检查只看有无,不鉴定真伪。

+
+
+ + +
对致命问题说「没问题」,需要写一句说明;说明会同时作为误报反馈提交。30 / 200
+
+
+
+
+ +
+
+
+
+ + + + + diff --git a/docs/design/ui-demo/index.html b/docs/design/ui-demo/index.html new file mode 100644 index 0000000..f42a4c1 --- /dev/null +++ b/docs/design/ui-demo/index.html @@ -0,0 +1,95 @@ + + + + + +标博士 UI demo + + + + + + + + +
+
+
+

标博士 · UI demo(2026-11-03)

+

用已确定的界面风格(方向 A「校样」、设计规范 v0.1)把常见控件和它们组合成的页面摆出来,用来决定 UI 精修的方向。颜色、字号、间距只用 system/tokens.css 的变量;已有组件直接用 system/components.css,规范里有描述但还没实现的组件放在本目录的 demo.css。不用任何 UI 框架和 CDN(AR10),图标是 Lucide 风格的内联 SVG(U3)。

+
cd docs && python3 -m http.server 8767
+# 打开 http://127.0.0.1:8767/design/ui-demo/   (设计基准 1440 宽,最小 1280)
+
+ +
+
01

页面

示意数据:政府采购 · IT 集成,复用原型 prototype/assets/data.js
+
+ ①控件总览

按钮(类型 × 6 种状态、尺寸、扣点说明)、输入框 / 文本域 / 下拉选择 / 日期选择 / 复选 / 单选 / 开关、级别标签、AI 标识与署名、出处 5 种形态、页内提醒、轻提示、对话框、标签页、分段、分页、空状态、骨架屏、进度、提示气泡、下拉菜单、快捷键。

+ ②表单

新建项目(离开时校验 + 提交汇总 + 提交中整表禁用)、同一字段的正常 / 聚焦 / 错误 / 禁用 / 提交中对照、资质录入(错误与「到期提醒」的区别)、忽略并注明原因、致命问题的「确认无误」说明(U4)。

+ ③卡片

项目卡(致命 / 临近截止 / 今天截止 / 已递交 / 加载中 / 新建)、统计格、额度卡(正常 / 80% / 用完)、任务进度卡(进行中 / 失败退回 / 完成 / 排队)、AI 摘要卡(确认前后)、关键日期卡。

+ ④列表与表格

项目列表(筛选、搜索、表头排序、空结果)、清单列表(分类、★ 筛选、筛选标签、J / K)、问题列表(级别筛选、只对「提示」批量、J / K + 1 / 2 / 3、已处理折叠)、额度流水、参数对照表、两种空结果。

+ ⑤组合页 · 工作台

侧边栏 + 顶栏框架;截止提醒、统计格、项目表格、待处理的致命问题,右侧任务进度、额度、最近处理。看「列表」布局的密度。

+ ⑥组合页 · 读标

项目页头 + 页签、清单状态与确认、分类导航、清单(待确认区、补遗更新、搜索)、原文查看器并排高亮。可以用 J / K 逐条看原文。看「对照」布局的密度。

+
+

资质库、额度页属于和工作台相同的「列表」布局,控件已在 ②③④ 覆盖,没有单独做组合页。

+
+ +
+
02

新增组件清单

都在 demo.css 的 B 部分;精修定稿后并入 system/components.css
+ + + + + + + + + + + + + + + + + + + + + + + +
编号组件类名依据说明
B1图标.ic§6 · U3Lucide 路径内联,1.5px 描边,16 / 20 / 32
B2页面框架.sidebar .top .crumbs .quota-chip .content .page-title .proj-head§5.3从原型 app.css 搬过来,去掉裸色值
B3按钮补充状态.btn.swap .is-loading .icon-btn .btn-group§7.1按下态、加载中定宽换字、只有图标的按钮、按钮组
B4输入框补充.input:focus/[disabled]/[readonly] .input-unit .search .form .form-sec .err-sum§7.13聚焦 / 禁用 / 只读、带单位、搜索、两列表单、错误汇总、字数
B5下拉选择 · 下拉菜单.select .menu .popAR10选中项加粗 + ✓,不可选项写原因;正式实现用 Reka UI Select / DropdownMenu
B6日期选择.cal .dt-pair .date-trigger§9.3 · AR10周一开始、Barlow 数字、今天描边、选中墨底、关键日期圆点、不可选删除线
B7复选 · 单选 · 单选卡片 · 开关.ck .choice .choice-card .switch§8.2墨色勾选、半选;开关是方形滑块 + 开 / 关文字
B8分段控件 · 标签页.seg .tabs [role=tab]§5.3 · §7.5← → 切换、不可用页签
B9分页.pagination—规范未定义;当前页墨色描边
B10轻提示.toasts .toast§7.124 秒消失、悬停暂停、可撤销、堆叠
B11对话框.scrim .dialog .wide§7.14480 / 640,焦点锁定,Esc 关闭,焦点回到触发处
B12提醒补充.alert .act .sub .errno§7.12 · §7.15右侧操作、副说明、错误编号
B13提示气泡[data-tip]§6墨底白字;正式实现用 Reka UI Tooltip
B14空状态 · 骨架屏 · 进度.empty .skel .skel-row .progress .steps .fail§7.11 · §7.15骨架缓慢明暗变化,减少动态效果时静止
B15杂项.kbd .kv .chip .drop .file-row .flowline§8.2快捷键、键值表、可移除的筛选标签、上传区、文件行、状态流
B16AI 产出块.ai-block .ai-label .sign.ign .more-cites§3.3 · §7.16左侧虚线 → 确认后墨色实线;「已忽略」灰色署名
B17卡片.card-h .card-foot .pcard .stats .stat .quota .dates§7.9 · §7.10项目卡、统计格、额度卡、关键日期
B18表格补充.toolbar .th-sort .batchbar .list-box .filters-on§7.7 · §8.2排序、勾选、选中行、批量栏、空结果行、问题行勾选
B19对照布局.split .cats .viewer-body§5.3 · §7.8分类导航 + 列表 + 原文查看器
+
+ +
+
03

做的过程中发现的规范缺口 · 待拍板

UI 精修阶段逐条确认
+ + + + + + + + + + + + + + + + +
#问题demo 现在的做法需要决定
Q1额度流水的「+」用了定稿绿沿用 components.css 的 .flow td.plus(绿)§3.4 说绿色只表示「有人确认过」。建议改成墨色「+」,靠符号区分
Q2AI 产出的正文是什么颜色正文始终墨色,只用「AI」标签 + 左侧虚线区分§7.16 写「确认后文字变为墨色」,暗示确认前不是墨色;大段蓝字阅读负担重,建议明确「正文始终墨色」
Q3「选中」用什么颜色读标清单选中 = 蓝铅笔浅底(和原文高亮对应);表格勾选 = 凹陷灰 + 墨色竖线蓝色只属于系统和出处(§3.4)。读标选中是否保留蓝色,需统一
Q4开关、分页、下拉、日期选择的视觉开关做成方形滑块 + 开 / 关文字;分页当前页描边;下拉选中加粗 + ✓;不可选日期加删除线规范第 7 节没有这几个组件,确认后补进规范
Q5参数对照表加「级别」列多了一列级别标签,判断词颜色有形状兜底§7.7 的表没有这一列;只靠判断词颜色对色弱不够
Q6悬停、按下、遮罩缺颜色变量用 color-mix() 从墨色算;components.css 的 #34404D、原文页的 #222 #888 是裸色值建议 tokens.css 增加 --c-ink-hover、--scrim 等语义变量
Q7字阶外的字号Barlow 数字用了 13 / 15px(components.css 已有)§4.2 字阶里没有,建议加 --fs-figure-sm 等变量
Q8骨架屏动不动1.6 秒缓慢明暗变化;减少动态效果时静止和「不闪烁」原则是否冲突
Q9顶栏额度 80% 后是否变色顶栏额度描边变赭色§7.10 只写了额度条,没写顶栏
Q10日期的输入格式界面显示「11月20日 09:30」,输入框用「2026-11-20」§9.3 只规定了显示格式
Q11项目卡片视图做了项目卡,工作台仍用表格§7.9 只定义了项目行;要不要提供卡片视图
Q12卡片 / 页面里同时出现多个主按钮组合页每屏一个主按钮;demo 陈列页为了对比会出现多个表单页、对话框里的「保存」是否也算「一屏一个」
+
+
+ + + + diff --git a/docs/design/ui-demo/lists.html b/docs/design/ui-demo/lists.html new file mode 100644 index 0000000..6c33c55 --- /dev/null +++ b/docs/design/ui-demo/lists.html @@ -0,0 +1,295 @@ + + + + + +列表与表格 · 标博士 UI demo + + + + + + + + +
+
+
+

④ 列表与表格

+

筛选栏 + 表格 / 列表。表头凹陷灰底 + 2px 墨色底线,行高 40;数字用 Barlow 等宽。演示筛选、排序、批量勾选、键盘逐条处理和空结果。

+ +
+ + +
+
01

项目列表

规范 §7.9 · 可以筛选、搜索、点表头排序
+
+
+
+
+
+
+ + + +
所投包状态就绪
+

默认按截止时间升序,截止临近的排在前面。「就绪」只在「已检查」显示,只表示致命问题都已处理,不代表投标文件没有问题。

+
+ + +
+
02

清单列表

规范 §7.5 · 点这块后按 J / K 移动、Enter 打开出处
+
+
+ +
J K 移动 · Enter 打开出处
+
+
+
+
+ + +
+
03

问题列表

规范 §7.4 · §8.2 · 只有「提示」级可以批量
+
+
+ + + + +
+ 只显示待处理的问题;已处理的在列表底部。 +
+
+ +
+
+ + +
+
04

额度流水

规范 §7.10 · 红线 9 额度即货币
+
+
+
+
+
+ + + + + + + + + + + + +
时间类型额度数量余额关联项目 / 任务说明
11-02 14:31退回解析+137智慧大厅建设项目 · 解析任务 PJ-0417解析失败:文件已加密
└11-02 14:28预扣解析−136智慧大厅建设项目 · 解析任务 PJ-0417和上一行的退回对应
11-02 14:20—检查012智慧大厅建设项目 · 第 2 次检查24 小时内复查,不扣
11-02 10:05结算解析−137智慧大厅建设项目 · 补遗 1
11-01 16:00结算检查−112智慧大厅建设项目 · 第 1 次检查
11-01 10:40结算解析−138智慧大厅建设项目 · 招标文件
11-01 09:30人工调整检查+213—运营:系统异常补偿,工单 GD-1101
11-01 09:00发放解析 / 检查+50 / +2050 / 20专业版开通(对公转账)开通单 KT-1101
+ +
  • 增加用定稿绿「+」(这里绿色表示「入账」,是否允许请在精修时拍板,见目录页的待定问题),扣减用墨色「−」;退回记录和对应的预扣记录相邻显示。
+
+ + +
+
05

参数对照表

规范 §7.7
+ + + + + + + + + + + +
参数项要求投标响应判断级别出处
★ 应用服务器 · 内存≥ 512G DDR4256G DDR4疑似不满足致命第 41 页 ↔ 技术册 第 12 页
★ 应用服务器 · 系统盘2 块 ≥ 960G SSD
补遗 1 更新,原为 ≥ 480G
2 块 960G SSD满足补遗 1 第 1 页 ↔ 技术册 第 12 页
★ 应用服务器 · CPU≥ 2 颗 32C 2.7GHz2 颗 7380 32C 2.7GHz满足第 41 页 ↔ 技术册 第 12 页
★ 核心交换机 · BFD 间隔≤ 50ms3.3ms满足第 44 页 ↔ 技术册 第 58 页
★ 核心交换机 · 证明材料官网参数截图等—未找到响应重要第 44 页 ↔ 已查找:技术册、商务册附件
▲ 核心交换机 · 交换容量≥ 19.2Tbps19.2Tbps / 48Tbps需人工判断提示第 44 页 ↔ 技术册 第 58 页
排队叫号一体机 · 显示屏≥ 21.5 英寸21 英寸疑似不满足提示第 47 页 ↔ 技术册 第 77 页
+
  • 判断只用四个词:满足 / 疑似不满足 / 未找到响应 / 需人工判断。「满足」用墨色普通字,不用绿色。
  • 要求和响应列用 Barlow,数值左对齐。规范的表没有「级别」列,这里加了一列标签,让判断词的颜色有形状兜底(色弱)——是否保留请拍板。
+
+ + +
+
06

空结果

规范 §7.15
+
+
+ 筛选后没有结果:写清楚条件,给「清除筛选」 + +
项目所投包投标截止状态
没有符合条件的项目

「已归档」里没有名称含「医保」的项目。

+
+
+ 问题全部处理完:说清楚意味着什么,不庆祝 +
待处理的问题都已处理

致命问题已全部处理,项目为「就绪」。这只表示每一项都有人处理过,不代表投标文件没有问题。

+
+
+
+
+
+ + + + + diff --git a/docs/design/ui-demo/page-reading.html b/docs/design/ui-demo/page-reading.html new file mode 100644 index 0000000..81465db --- /dev/null +++ b/docs/design/ui-demo/page-reading.html @@ -0,0 +1,226 @@ + + + + + +读标 · 标博士 UI demo + + + + + + + + +
+
+
+
+

某市政务服务中心智慧大厅建设项目

+
某市政务服务管理办公室政府采购 · 公开招标投 采购包 1(共 2 个包)ZC-2026-0418
+
+
+
投标截止11月20日 09:30 · 剩 17 天
+
状态已检查
+
就绪未就绪
+
+
+ +
+
+

项目检查清单

+
+
+ 反馈:有遗漏的要求 + + +
+
+
+ +
+
+
+
+ + + + + + diff --git a/docs/design/ui-demo/page-workbench.html b/docs/design/ui-demo/page-workbench.html new file mode 100644 index 0000000..3fd7d8c --- /dev/null +++ b/docs/design/ui-demo/page-workbench.html @@ -0,0 +1,133 @@ + + + + + +工作台 · 标博士 UI demo + + + + + + + + +
+
+
+

工作台

11月3日 周二 · 按投标截止时间排序,截止临近的排在前面。

+
扣 1 次解析 · 剩 37 次
+
+ +
!

某县数据中心机房改造项目今天 17:00 截止。清单还是草稿,还没有做过检查。

去确认清单
+ +
+
进行中的项目4

已递交 1 · 已归档 1

+
7 天内截止2

最近:今天 17:00

+
待处理的致命问题3

在 1 个项目里

+
待确认的清单条目4

在 2 个项目里

+
+ +
+
+
+
+
+
+ + + +
项目投标截止状态就绪致命
+

「就绪」只表示致命问题都已处理,不代表投标文件没有问题。

+ +

待处理的致命问题

致命3去查标页处理
+
+
致命
报价超过采购包 1 的最高限价智慧大厅建设项目 · 开标一览表 287.60 万元,限价 286.00 万元
报价册 · 第 1 页
+
致命
★ 条款疑似不满足:应用服务器内存智慧大厅建设项目 · 响应 256G,要求 ≥ 512G · 上次标记为已修改,问题仍在
技术册 · 第 12 页
+
致命
授权委托书的单位签章位置未发现盖章智慧大厅建设项目 · 只看有无,不鉴定真伪
商务册 · 第 5 页
+
+
+ +
+
+

正在进行的任务

+
解析补遗 2提取清单
+
某市医保信息系统等保测评服务
+
62%
+
检查排队中 · 智慧社区平台运维 · 前面还有 1 个任务
+

可以离开本页,完成后会发站内通知。

+
+ +
+

本期额度

至 2027-10-31
+
+
解析剩余37/ 50
+
检查剩余12/ 20
+
+ +
+ +
+

最近处理

全部记录
+
    +
  • 王敏 ✓ 已修改「授权委托书签章」14:26
  • +
  • 王敏 确认了清单 v2 · 智慧大厅11-02
  • +
  • 系统 完成第 2 次检查 · 致命 311-02
  • +
  • 系统 并入补遗 1 · 更新 2 条11-02
  • +
  • 李工 忽略「其他项目名称」:业绩介绍11-01
  • +
+
+
+
+
+
+ + + + +