feat(S6-P21): 组件族参数化(13族/44成员/导航族15文件同源/79→48概念组件)

【本次核心 · S6-P21】
- 族层数据:families.json + 44 份契约注入 family/familyRole/familyParams;
  data.json / data.js / site/details 同步。13 族 / 44 成员 / 35 独立 → 概念组件 79→48。
- 79 个 slug 全保留、集合逐一不变(铁律 5 对外承诺未破);frameworks 仍 395 文件、薄壳仍 79。
- 归族判据为契约中可核对字段(semanticTypeCandidates 重叠 / anatomy 为同一骨架子集 /
  变体维度同构 / doNotInvent 显式从属声明),每族 mergeBasis 写明依据,不按名字猜。
- 实现层合并(导航族端到端切片):tools/gen-family-impl.mjs 从 5 端模板生成
  TopMenu / SideMenu / MixedNavigation 共 15 文件,参数 direction=top|side|mixed;
  三份 CSS md5 完全相同 = 一份样式表服务三个组件。
- 新增 tools/gen-families.mjs、tools/gen-family-impl.mjs、tools/verify-families.mjs、
  tools/lib/family-model.mjs、tools/lib/family-impl/nav-menu/*.tpl。

【同时清掉此前已完成但未提交的批次】
生成物(data.json / data.js / site/sources / site/components 薄壳 / sitemap.xml / tests 报告)
跨阶段交织,无法拆成互相自洽的多个提交,故按既有批量风格合并提交:
- Package:三端可 import(S5-P18)+ 发布到私有 npm 源
- Docs site:导航语言改下拉(S5-P19)、详情页代码块默认展开、中英切换完整性
- Security:生产部署链审计修复(2026-09-19)+ 线上部署
- Theme modes 日间/夜间/自动;S1-P4 data.js 瘦身;S2-P5 暗色;S2-P6 跨端一致性;
  S2-P7 行为断言;S2-P9 FAQ;S3-P8 RTL;S3-P9 契约缺口解释层;S4-P12 发布流程
- 补入 tools/pack-deploy.mjs、run-site-smoke.mjs、verify-*.mjs,.dockerignore、
  安全审计修复与待决策项.md

【验收】
- node tools/verify-families.mjs → OK: 族层端到端一致(13 族 / 44 成员 / 79 组件不变 / 395 文件不变)
- node tools/verify-cross-platform.mjs → 79/79 identical(HEAD 基线 high 44)
- node tools/run-regression.mjs → 100%(79/79 页,1017/1017 断言,N/A 34),连跑 8 次一致,0 超时
- 逐页实测:topmenu / sidemenu / mixednavigation 各 13/13,帧内 direction 参数正确,0 JS 错误
- 零运行时依赖 OK;build-site.ps1 ASCII-only OK

【未纳入】site/components/<slug>/ 平台薄壳 316 个 —— 历史从未跟踪且属构建产物,保持现状。
This commit is contained in:
aurora-admin
2026-09-20 03:32:31 +08:00
parent 7e3d442dab
commit eb25feedaf
372 changed files with 16928 additions and 12810 deletions
+177
View File
@@ -1017,6 +1017,132 @@ OK: build-dist 可调用
---
### S6-P21 · 组件族参数化(同源组件收敛为「1 基座 + N 参数」)| 预算 ~2 天 | 优先级 P1
**目标**:把"同一交互/结构各自手写一遍"的组件收敛为「1 个基座组件 + N 个参数取值」,消除重复身份,同时**不改变组件身份(slug)、不减少 `data.json` 的组件数**。
**依赖**:无(纯新增层,不动既有对外承诺)。
**背景(实测)**:79 个组件里有 44 个能按语义归入 13 个族。判据不是名字相似,而是契约里可核对的四类字段:`semanticTypeCandidates` 重叠、`anatomy` 为同一骨架的子集、变体维度同构、以及 `doNotInvent` 里的显式从属声明 —— 例如 `alertmodal` 与 `confirmmodal` 的 `doNotInvent` 原文写着「弹窗尺寸档位(见 Modal 契约)」,`steps` 与 `steplist` 的 `semanticTypeCandidates` 完全相同(均为 `steps|wizard`)。
**硬约束(决定了实现形态)**:
- `tools/pack-deploy.mjs:137-139` 硬断言 `site/components` 薄壳数 === 79、`frameworks` 实现文件数 === 395;
- `tools/precompute.mjs:38-39` 硬断言 `COMPONENT_COUNT = 79` / `SOURCE_FILE_COUNT = 395`;
- `tools/verify-cross-platform.mjs:695`、`tools/verify-package-import.mjs` 断言 79。
因此「新增族内核文件」与「删除变体文件」两条路都不可行。实现层合并只能走**一份参数化模板 → 生成 N 组自包含产物**(与 `tests/_template.html → tests/<slug>.html × 79` 同一套哲学)。
**步骤**:
1. `tools/lib/family-model.mjs`:13 族 / 44 成员的唯一真源;每族必写 `mergeBasis`(实测依据)与 `paramSurface`(参数名/类型/取值/默认值)。
2. `tools/gen-families.mjs`:生成 `families.json`;对 44 份契约做**定点注入**(只在 `"slug"` 行后插 `family`/`familyRole`/`familyParams`,不重排 JSON、不覆盖他人未提交改动)。
3. `build-site.ps1` + `tools/precompute.mjs`:把族层输出到 `data.json`(顶层 `families` + 每组件 `family` 三元组)、`data.js`、`site/details/<slug>.json`。
4. `tools/lib/family-impl/nav-menu/*.tpl` + `tools/gen-family-impl.mjs`:族实现生成器(带"不覆盖非生成物的脏文件"护栏)。
5. `tools/verify-families.mjs`:端到端验证(模型 ↔ 契约 ↔ data.json ↔ details ↔ 395/79 硬约束)。
**✅ 完成** — 验收输出(原样粘贴):
```
$ node tools/gen-families.mjs --dry-run
[dry-run] 族 13 / 登记成员 44 / 契约改动 44 / 已最新 0
独立组件(不属任何族):35 个 — button select tag breadcrumb tree dropdown popconfirm segmented transfer rate slider collapse …
OK: 族层数据一致
$ npm run build:site
families: 13 families, 44 member components
family layer: 13 families, 44/79 components carry family metadata
thin shells: 79, platform shells: 316, sitemap.xml: 396 urls
all components complete (5-form files + category)
[precompute] 完整性校验:79/79 个组件,395/395 个源文件
[precompute] 数据.js: 1147 KB → 122 KB
$ node tools/verify-families.mjs
族 13 个(其中已参数化实现 1 个)
族成员 44 个组件
独立组件 35 个
概念组件数 13(族)+ 35(独立)= 48 个,替代原本 79 个并列组件
契约注入 44/44
frameworks 395 文件(79 x 5,未增未删)
测试页 79 薄壳
· nav-menu 3 成员 → 1 基座(净减 2)
· table 6 成员 → 1 基座(净减 5)
· masked-input 7 成员 → 1 基座(净减 6)
· modal-shell 5 成员 → 1 基座(净减 4)
· preference-switcher 3 成员 → 1 基座(净减 2)
· card-shell 4 成员 → 1 基座(净减 3)
· feedback-page 3 成员 → 1 基座(净减 2)
· loading-state 3 成员 → 1 基座(净减 2)
· steps / tabs / notice / selection-card / combobox 各 2 成员 → 1 基座(各净减 1)
details 携带族字段:44/44
OK: 族层端到端一致(13 族 / 44 成员 / 79 组件不变 / 395 文件不变)
概念组件:79 → 48(合并掉 31 个重复身份)
$ node tools/gen-family-impl.mjs --only=nav-menu
[write] 族 nav-menu:写入 15 / 已最新 0 / 跳过 0
OK: 族实现与模板一致
$ md5sum frameworks/TopMenu.css frameworks/SideMenu.css frameworks/MixedNavigation.css
9f98c53aa3b2449ecc1fc42e34635129 *frameworks/TopMenu.css
9f98c53aa3b2449ecc1fc42e34635129 *frameworks/SideMenu.css
9f98c53aa3b2449ecc1fc42e34635129 *frameworks/MixedNavigation.css
# 三份逐字节相同 = 一份样式表服务三个组件(差异只在根元素的 data-direction 参数)
$ node tools/verify-cross-platform.mjs
检查组件: 79
完全一致: 79
有差异 : 0 (high 0 / medium 0 / low 0)
# 基线与口径(两个数不能混为一谈,否则会把自己的改动说大成整体改善):
# 已提交 HEAD(tests/cross-platform-report.json) identical 2 / differing 77 / high 44
# 本任务改动前的工作区 identical 79 / differing 0
# 本任务改造后 identical 79 / differing 0(持平)
# 改造中途曾出现 76/79:族模板最初把方向写成对象字面量 { direction: 'side' },Vue 端
# class 提取器会把其中的字符串值收作变体记号,H5/JSX 端不会。探针实验确认后用独立常量
# FAMILY_DIRECTION = 'side' 承载方向,四端恢复 79/79 —— 是改代码对齐既有约定,
# 不是放宽校验脚本。
$ node tools/run-regression.mjs
passRate 100% | pages 79 (all-pass 79) | assertions 1017/1017 | N/A 34
[OK] 全部通过
# 导航族逐页实测(playwright 打开 tests/<slug>.html,点「运行断言」后读帧内真实 DOM)
[topmenu] 总数 13 | 通过 13 | 失败 0 | 跳过 0
帧内: 3 个 .aa-menu / direction=[top,top,top] / 菜单项 30 / role+tabindex 齐备=true / JS 错误: 无
[sidemenu] 总数 13 | 通过 13 | 失败 0 | 跳过 0
帧内: 3 个 .aa-menu / direction=[side,side,side] / 菜单项 30 / role+tabindex 齐备=true / JS 错误: 无
[mixednavigation] 总数 13 | 通过 13 | 失败 0 | 跳过 0
帧内: 3 个 .aa-menu / direction=[mixed,mixed,mixed] / 菜单项 22 / role+tabindex 齐备=true / JS 错误: 无
# mixednavigation 菜单项少 8 个是因为 mixed 的侧栏只渲染当前一级的 children(22 = 一级 5 + 二级 3 + 案例 2/3 各 7)
$ node -e "...零依赖验收(AGENTS.md 铁律 1 原文脚本)"
zero-dep OK
```
**改动文件**:
- `.design_library/aurora-admin/families.json` — 新建,族模型快照(25 202 bytes,build-site 消费)
- `.design_library/aurora-admin/components/*.json` — 44 份各 +3 行(`family`/`familyRole`/`familyParams`),其余字节不变
- `tools/lib/family-model.mjs` — 新建,13 族真源(含 mergeBasis 与 paramSurface)
- `tools/gen-families.mjs` — 新建,契约层生成器(`--dry-run` / `--check` / 幂等)
- `tools/lib/family-impl/nav-menu/menu.{html,css,jsx,vue2,vue3}.tpl` — 新建,导航族 5 端参数化模板
- `tools/gen-family-impl.mjs` — 新建,实现层生成器(脏文件护栏 + `--check`)
- `tools/verify-families.mjs` — 新建,族层端到端验证
- `build-site.ps1` — 读 families.json;`data.json` 增顶层 `families` 与每组件族字段(`families` 以原始 JSON 文本直插,避开 PS 5.1 的 PSCustomObject 序列化与空数组展开两个陷阱;仍为 ASCII-only)
- `tools/precompute.mjs` — `site/details/<slug>.json` 增 `family`/`familyRole`/`familyParams`
- `frameworks/{TopMenu,SideMenu,MixedNavigation}.{html,css,jsx,vue2.vue,vue3.vue}` — 15 个文件改为族模板生成物(三份 CSS md5 相同:一份样式表服务三个组件)
**规划偏差**:
- 原设想「抽一个共享 Menu 内核,三个变体薄壳 import 它」→ 实际不可行:会同时打破 395 文件数、`build-dist` 打包与 `verify-package-import` 的 79 导出断言,并牺牲"单文件可拷贝"。改为**模板生成**:15 个产物保持自包含,重复消除在源头(改一份模板 + 重跑 = 三端同步)。
- 原计划把实现层一次覆盖多族 → 实际只做 `nav-menu` 一族作为端到端切片,其余 12 族先落契约层。原因:实现层每族都要重写 5 端并过 9 项断言矩阵,一次做完无法逐族验证。
- `FAMILY_PARAMS` 从 JSON 字面量改为 JS 对象 + 只保留实现真正消费的参数(`direction`/`collapsed`):JSON 形态会让参数名/枚举值被跨端 class 提取器当成类记号。
**发现的新问题(已登记,未在本任务顺手修)**:
- **S6-P22(建议)**:`tools/verify-cross-platform.mjs` 的 class 提取器端间不对称 —— 同一份代码形态,Vue 端会把 `const X = { k: 'v' }` 的字符串值收作变体记号,H5/JSX 端不会。本次已用「方向独立常量」绕开(四端回到 79/79),但提取器本身未修;下一个族(如 `masked-input` 的 `mask`/`format` 一定是对象字面量里的字符串)还会再撞一次。建议统一三端口径:要么都在对象字面量里展开枚举,要么都不展开。
- **S6-P23(建议)**:其余 12 族的实现层合并(`table` 净减 5、`masked-input` 净减 6 收益最大)。族模板按现有生成器结构新增 `<family>/menu.*.tpl` 即可,数据层无需改动。
- **S6-P24(建议)**:`MixedNavigation.css` 原文件同时写了 `border-inline-start` 与 `border-left-color`(逻辑属性 + 物理属性混用,RTL 下两侧指示条表现不一致);族模板已统一为逻辑属性,但未做 RTL 实测。
**回归**:100%(1017/1017)/ 八次连跑一致 / 0 超时。
---
## 四 · 显式不做(及理由)
> 这份清单同样重要 —— 没有它,执行模型会以为"漏了",或者在错误的时机自作主张。
@@ -1111,6 +1237,52 @@ P3(CI,无依赖最快见效)
---
## 四 · 2026-09-19 安全审计(衍生任务与已完成项)
> 起因:对仓库 + `192.168.5.7` 线上部署做全量安全与缺陷审计(报告与逐项证据见 `安全审计修复与待决事项.md`)。
> 已修复项在 CHANGELOG `[Unreleased] → Security` 有记录;下面只列**衍生任务包**与状态。
| ID | 任务 | 阶段 | 优先级 | 预算 | 状态 |
|---|---|---|---|---|---|
| S5-P13 | 部署可复现化 | S5 | **P0** | 2 h | ✅ **已完成**(`tools/pack-deploy.mjs` 以 `.dockerignore` 为唯一真源 + 硬断言;AGENTS §九 流程;2026-09-19 实际部署验证 6/6 断言 + 396/396 URL) |
| S5-P14 | 容器运行期加固 | S5 | P2 | 1 h | ⚪ **待决策**(需停机窗口):`read_only` + `tmpfs`、`cap_drop: [ALL]`、`no-new-privileges`、基础镜像 digest 固定 |
| S5-P15 | 对外承诺口径统一 | S5 | P1 | 2 h | ⚪ **待决策**:契约数量三处说法不一致(`llms.txt`「79 个」/`AGENTS.md`「79」/`index.json`「6 个核心」),线上实际只发布 6 份核心 + `index.json` |
| S5-P16 | 正式域名与 SEO | S5 | P2 | 1 h | ⚪ **待决策**:`sitemap.xml` 与 `package.json` 仍是 `YOUR-ACCOUNT.github.io` 占位;`build-site.ps1` 已支持 `SITE_URL_BASE`,但 `tools/verify-site-routing.mjs` 目前把占位 URL 写成了断言,换域名需同步改 |
| S5-P17 | 回归偶发失败的可追溯性 | S5 | P1 | 1 h | ⚪ **待开工**:2026-09-19 连跑 27 次中出现 2 次 `1016/1017`(78/79 页全通过),但 `tests/report.json` 的 `failedPages`/`timedOut` 为空、也未记录是哪条断言失败,导致无法定位。需让 runner 在非满分时落盘失败断言标识(页面 + `data-assert` + 期望/实际),并保留上一次报告不被覆盖 |
| S5-P18 | 组件库可 import(三端入口) | S5 | **P0** | 3 h | ✅ **已完成**(`dist/react|vue3|vue2/index.js` 各 79 组件 + `exports`/`peerDependencies`;顺带修掉 RangeQuickPicker 的 const 重赋值与 CodeInput 的 emit 遮蔽——两处都会让组件无法编译;`tools/verify-package-import.mjs` 20 checks;Vite + Vue3/React 真实工程 `file:` 安装后构建并浏览器实测通过) |
**已知但暂不处理**(用户 2026-09-19 决定):不对外公用 —— 线上保持 `127.0.0.1:3311` 回环,需 SSH 隧道访问;不加反代、不加域名、不加认证。
### S5-P15 补充说明
线上 `/.design_library/aurora-admin/components/` 只有 `button/card/input/modal/select/table.json` + `index.json`;本地是 79 份 + `index.json`。三处文档说法互相矛盾,需先定「对外到底承诺几份契约」,再统一 `llms.txt` / `AGENTS.md` / `index.json` 与线上发布内容。
### S5-P16 补充说明
`sitemap.xml` 的 396 条 URL 本身在线上全部 200(静态页无死链),问题只在文件本身的域名是占位值,且此前未 `COPY` 进镜像(已修)。换真域名时注意 `tools/verify-site-routing.mjs:48` 的断言。
---
## 五 · 2026-09-20 文档站导航(衍生任务与已完成项)
| ID | 任务 | 阶段 | 优先级 | 预算 | 状态 |
|---|---|---|---|---|---|
| S5-P19 | 导航语言选择改为下拉(美化) | S5 | P1 | 2 h | ✅ **已完成**(原「中/EN 双段」按钮换成令牌化下拉:地球图标 + 当前语言 + 箭头触发器,186px 卡片菜单含标题分隔线、ZH/EN 角标、对勾选中态;listbox ARIA + 方向键/Home/End/Esc/Tab/点击外部全路径;暗色令牌继承,选中项对比度 4.57、标题 6.50;≤1100px 收起语言名到 56px,实测 ≥860px 顶栏 0 溢出) |
| S5-P20 | 顶栏在 ≤820px 溢出(移动端导航无方案) | S5 | P2 | 2 h | ⚪ **待开工**:实测 820px 顶栏溢出 33px、768px 85px、600px 253px;≤900px 已无侧栏且**没有任何汉堡菜单**,等于移动端无法导航。这一层是既有缺口(移除语言控件后 820px 仍溢出 33px,可压缩仿真亦不变),与 S5-P19 无关。需要的是移动端导航方案(抽屉/汉堡 + 顶栏分段折叠),而非继续微调间距 |
| S5-P21 | 发布到私有 npm 源 | S5 | P1 | 1 h | ✅ **已完成**(`gitea.mymoyu.top` 的 npm registry,匿名可读;发布 3 个可互换包名 `@root/ui`(推荐,支持一行 `.npmrc`)/ `chunyu-ui` / `aurora-admin-design`;Vite+Vue3 真实工程实测安装→构建→浏览器渲染通过)。**待决**:是否也发布到公共 npm(本机无凭据,需用户 `npm login` 或 token;`chunyu-ui`/`aurora-admin-design` 在公共 npm 均未被占用) |
**S5-P20 实测数据**(2026-09-20,`http://127.0.0.1:3311/site/index.html#/component/button`):
| 视口宽 | 顶栏溢出 | 说明 |
|---|---|---|
| ≥1100 | 0 | 语言名 + 图标(115px) |
| 1024 / 900 / 860 | 0 | 语言名收起(56px),链接近距收到 8px |
| 820 | 33px | 移除语言控件仍溢出 33px → 与语言控件无关 |
| 768 | 85px | |
| 600 | 253px | |
---
## 附 · 规划修正记录
> 执行中发现规划与实际不符时,**以实测为准**,在此登记。
@@ -1122,4 +1294,9 @@ P3(CI,无依赖最快见效)
| 2026-09-11 | P2 | 包名可用性未知 | registry 查询:`aurora-admin-design` **未被占用** | 补入「前置验证已完成」 |
| 2026-09-11 | P4 | 只说「改成 fetch」,未识别难点 | `app.js` 有 **10 处**消费 `sources`,其中 2 处是**渲染期同步解析** | 补入「依赖分析」与三阶段执行建议 |
| 2026-09-11 | P2/P4 | 验收命令有 3 处会误判(npm 不可用时误报 OK 等) | 实测触发 | 已改写为显式判断 exit code |
| 2026-09-19 | PLAN §7/§8 | 「不改 `site/dev-server.js`(已工作良好)」 | 单个 `GET /site/%00` 触发 `ERR_INVALID_ARG_VALUE` 未捕获异常,进程退出 | **以实测为准**:修复该文件(NUL 双重拦截 + `readFile` try/catch + `AA_PORT`),并新增 `tools/verify-dev-server.mjs`(20 checks / 3 条反例断言);PLAN 两处已标注 ⚠️ 规划修正 |
| 2026-09-19 | 部署方式 | 未规定(实际为手工拷贝到 `/opt/aurora-admin`) | 手工拷贝导致 ① `.dockerignore` 未随行 → 规范原文入镜像并对外 200;② 曾修好的 `absolute_redirect off` 被覆盖丢失 | 新增任务包 S5-P13:以 `.dockerignore` 为唯一真源的 `tools/pack-deploy.mjs` + AGENTS §九 标准流程;2026-09-19 已按该流程实际部署并验收 |
| 2026-09-19 | TESTING 基线 | 1009 通过 / 35 N/A / 共 1044 条断言 | 工作树实测 **1017 通过 / 0 失败 / 34 N/A / 共 1051**(连跑 10 次一致) | 更新 TESTING.md 与 AGENTS.md §八 的数字并标注口径来源 |
| 2026-09-20 | S1-P2 | 目标写「能拿到令牌 + 组件样式」,据此实现为 CSS/HTML 分发包 | 用户预期是**组件库**(`import { AaButton } from '.../vue3'`);实测 `dist/` 无任何可 import 组件,`main` 指向 CSS 文件 | **以用户预期为准**:补三端入口 + `exports` + `peerDependencies`,登记为 S5-P18 并已完成。原 S1-P2 验收命令全部仍通过,未破坏既有承诺 |
| 2026-09-20 | 5 端实现 | AGENTS 写「79 组件 × 5 端实现」 | 真编译时发现 `RangeQuickPicker`(React/Vue3)与 `CodeInput`(Vue3)**无法编译**——此前从未真编译过,结构断言测不出 | 已修;新增 `tools/verify-package-import.mjs` 的编译级检查(真编译 + 真渲染)防止复发 |