@done-coding/admin-core 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +48 -0
  2. package/docs/TECH_SNAPSHOT.md +146 -0
  3. package/docs/specs/2026-05-09-055250-moderate-/345/210/227/350/241/250key/351/205/215/347/275/256/347/202/271/350/267/257/345/276/204/346/224/257/346/214/201/RETROSPECTIVE.md +63 -0
  4. package/docs/specs/2026-05-09-055250-moderate-/345/210/227/350/241/250key/351/205/215/347/275/256/347/202/271/350/267/257/345/276/204/346/224/257/346/214/201/design.md +575 -0
  5. package/docs/specs/2026-05-09-055250-moderate-/345/210/227/350/241/250key/351/205/215/347/275/256/347/202/271/350/267/257/345/276/204/346/224/257/346/214/201/landing-authorizations/2026-05-09-080900-/346/234/2371-/345/205/245/345/256/236/346/226/275/346/216/210/346/235/203.md +107 -0
  6. package/docs/specs/2026-05-09-055250-moderate-/345/210/227/350/241/250key/351/205/215/347/275/256/347/202/271/350/267/257/345/276/204/346/224/257/346/214/201/project-orchestration.md +58 -0
  7. package/docs/specs/2026-05-09-055250-moderate-/345/210/227/350/241/250key/351/205/215/347/275/256/347/202/271/350/267/257/345/276/204/346/224/257/346/214/201/requirements.md +238 -0
  8. package/docs/specs/2026-05-09-055250-moderate-/345/210/227/350/241/250key/351/205/215/347/275/256/347/202/271/350/267/257/345/276/204/346/224/257/346/214/201/tasks.md +290 -0
  9. package/es/_virtual/_plugin-vue_export-helper.mjs +9 -0
  10. package/es/components/display/WatchSize.vue.mjs +50 -0
  11. package/es/components/display/WatchSize.vue2.mjs +4 -0
  12. package/es/components/display/index.mjs +12 -0
  13. package/es/components/form/FormItem.vue.mjs +7 -0
  14. package/es/components/form/FormItem.vue2.mjs +145 -0
  15. package/es/components/form/FormMain.vue.mjs +142 -0
  16. package/es/components/form/FormMain.vue2.mjs +4 -0
  17. package/es/components/form/FormRadioGroup.vue.mjs +36 -0
  18. package/es/components/form/FormRadioGroup.vue2.mjs +4 -0
  19. package/es/components/form/FormSearch.vue.mjs +7 -0
  20. package/es/components/form/FormSearch.vue2.mjs +132 -0
  21. package/es/components/form/FormSelect.vue.mjs +65 -0
  22. package/es/components/form/FormSelect.vue2.mjs +4 -0
  23. package/es/components/form/FormTree.vue.mjs +34 -0
  24. package/es/components/form/FormTree.vue2.mjs +4 -0
  25. package/es/components/form/FormVerifyCode.vue.mjs +72 -0
  26. package/es/components/form/FormVerifyCode.vue2.mjs +4 -0
  27. package/es/components/form/FormVerifyImage.vue.mjs +7 -0
  28. package/es/components/form/FormVerifyImage.vue2.mjs +60 -0
  29. package/es/components/form/index.mjs +32 -0
  30. package/es/components/form/utils.mjs +147 -0
  31. package/es/components/list-page/ListPage.vue.mjs +7 -0
  32. package/es/components/list-page/ListPage.vue2.mjs +194 -0
  33. package/es/components/list-page/index.mjs +14 -0
  34. package/es/components/menu/MenuItemSub.vue.mjs +60 -0
  35. package/es/components/menu/MenuItemSub.vue2.mjs +4 -0
  36. package/es/components/menu/MenuTree.vue.mjs +87 -0
  37. package/es/components/menu/MenuTree.vue2.mjs +4 -0
  38. package/es/components/menu/index.mjs +12 -0
  39. package/es/components/misc/TriggerAutoImport.vue.mjs +10 -0
  40. package/es/components/misc/TriggerAutoImport.vue2.mjs +4 -0
  41. package/es/components/misc/index.mjs +12 -0
  42. package/es/components/modal/ConfirmModal.vue.mjs +8 -0
  43. package/es/components/modal/ConfirmModal.vue2.mjs +141 -0
  44. package/es/components/modal/DetailModal.vue.mjs +67 -0
  45. package/es/components/modal/DetailModal.vue2.mjs +4 -0
  46. package/es/components/modal/index.mjs +17 -0
  47. package/es/components/table/TableMain.vue.mjs +7 -0
  48. package/es/components/table/TableMain.vue2.mjs +204 -0
  49. package/es/components/table/index.mjs +14 -0
  50. package/es/config/list-model.mjs +13 -0
  51. package/es/config/route.mjs +4 -0
  52. package/es/helpers/form.mjs +32 -0
  53. package/es/helpers/route.mjs +60 -0
  54. package/es/helpers/state.mjs +46 -0
  55. package/es/helpers/storage.mjs +16 -0
  56. package/es/hooks/activated.mjs +42 -0
  57. package/es/hooks/feel-size.mjs +30 -0
  58. package/es/hooks/menus-dispatch.mjs +47 -0
  59. package/es/index.mjs +97 -0
  60. package/es/inject/key.mjs +6 -0
  61. package/es/style.css +1 -0
  62. package/es/utils/id.mjs +5 -0
  63. package/es/utils/router.mjs +18 -0
  64. package/es/utils/time.mjs +18 -0
  65. package/package.json +81 -0
  66. package/types/components/display/WatchSize.vue.d.ts +44 -0
  67. package/types/components/display/index.d.ts +5 -0
  68. package/types/components/form/FormItem.vue.d.ts +50 -0
  69. package/types/components/form/FormMain.vue.d.ts +51 -0
  70. package/types/components/form/FormRadioGroup.vue.d.ts +23 -0
  71. package/types/components/form/FormSearch.vue.d.ts +23 -0
  72. package/types/components/form/FormSelect.vue.d.ts +60 -0
  73. package/types/components/form/FormTree.vue.d.ts +42 -0
  74. package/types/components/form/FormVerifyCode.vue.d.ts +32 -0
  75. package/types/components/form/FormVerifyImage.vue.d.ts +22 -0
  76. package/types/components/form/index.d.ts +13 -0
  77. package/types/components/form/types.d.ts +223 -0
  78. package/types/components/form/utils.d.ts +49 -0
  79. package/types/components/list-page/ListPage.vue.d.ts +44 -0
  80. package/types/components/list-page/index.d.ts +6 -0
  81. package/types/components/list-page/types.d.ts +44 -0
  82. package/types/components/menu/MenuItemSub.vue.d.ts +19 -0
  83. package/types/components/menu/MenuTree.vue.d.ts +28 -0
  84. package/types/components/menu/index.d.ts +6 -0
  85. package/types/components/misc/TriggerAutoImport.vue.d.ts +11 -0
  86. package/types/components/misc/index.d.ts +5 -0
  87. package/types/components/modal/ConfirmModal.vue.d.ts +107 -0
  88. package/types/components/modal/DetailModal.vue.d.ts +23 -0
  89. package/types/components/modal/index.d.ts +7 -0
  90. package/types/components/modal/types.d.ts +33 -0
  91. package/types/components/table/TableMain.vue.d.ts +35 -0
  92. package/types/components/table/index.d.ts +6 -0
  93. package/types/components/table/types.d.ts +90 -0
  94. package/types/config/index.d.ts +2 -0
  95. package/types/config/list-model.d.ts +48 -0
  96. package/types/config/route.d.ts +2 -0
  97. package/types/helpers/form.d.ts +11 -0
  98. package/types/helpers/index.d.ts +4 -0
  99. package/types/helpers/route.d.ts +37 -0
  100. package/types/helpers/state.d.ts +26 -0
  101. package/types/helpers/storage.d.ts +10 -0
  102. package/types/hooks/activated.d.ts +8 -0
  103. package/types/hooks/feel-size.d.ts +10 -0
  104. package/types/hooks/index.d.ts +3 -0
  105. package/types/hooks/menus-dispatch.d.ts +33 -0
  106. package/types/index.d.ts +24 -0
  107. package/types/inject/index.d.ts +1 -0
  108. package/types/inject/key.d.ts +11 -0
  109. package/types/types/dot-path.d.ts +13 -0
  110. package/types/types/index.d.ts +3 -0
  111. package/types/types/route.d.ts +92 -0
  112. package/types/types/utility-types.d.ts +8 -0
  113. package/types/utils/id.d.ts +2 -0
  114. package/types/utils/index.d.ts +3 -0
  115. package/types/utils/router.d.ts +8 -0
  116. package/types/utils/time.d.ts +2 -0
@@ -0,0 +1,575 @@
1
+ ---
2
+ 任务等级: Moderate
3
+ 日期: 2026-05-09
4
+ 审核状态: 已通过
5
+ reviewer: architect 自查(用户 2026-05-09 裁决简化设计审核——不走 reviewer subagent,不走开发/测试/产品上下游预审)
6
+ ---
7
+
8
+ # 技术设计文档:列表 KEY 配置点路径支持
9
+
10
+ > 简化说明:本任务设计审核被用户裁决为「架构师同 agent 自查自纠」单步流程;
11
+ > 故 `reviewer` 字段不含 `superpowers:code-reviewer` 与上下游预审。
12
+ > 自查清单见末尾「自查记录」节,全部 ✓ 后 frontmatter 标记「已通过」。
13
+
14
+ ---
15
+
16
+ ## 1. 变更范围防火墙
17
+
18
+ ### Direct Targets(计划新建/修改)
19
+
20
+ | # | 文件路径 | 类型 | 对应 REQ |
21
+ |---|---|---|---|
22
+ | 1 | `packages/core/src/types/dot-path.ts` | **新建** | REQ-1 |
23
+ | 2 | `packages/core/src/config/list-model.ts` | **新建** | REQ-2 / REQ-3 |
24
+ | 3 | `packages/core/src/index.ts` | 修改(追加 re-export) | REQ-1 / REQ-2 / REQ-3 |
25
+ | 4 | `packages/app/src/config/request.ts` | 修改(删 5 项 + re-export) | REQ-2 |
26
+ | 5 | `packages/app/src/components/table/types.ts` | 修改(重写 2 类型) | REQ-4 |
27
+ | 6 | `packages/app/src/components/table/TableMain.vue` | 修改(4 处运行时改造) | REQ-5 |
28
+ | 7 | `packages/core/docs/TECH_SNAPSHOT.md` | **新建** | REQ-6 |
29
+ | 8 | `packages/core/README.md` | 修改(追加进阶用法节) | REQ-7 |
30
+
31
+ > 实际计划修改 7 个文件 + 新建 3 个 = 8 处变更点(REQ-1 ~ REQ-7 全覆盖;其中 `core/src/index.ts` 同时承担 REQ-1/2/3 的 re-export 出口)。
32
+
33
+ ### Collateral Reads(仅供参考、[MUST NOT] 修改)
34
+
35
+ | 文件 | 用途 |
36
+ |---|---|
37
+ | `packages/app/src/config/index.ts`(barrel) | 验证 `@/config` re-export 链路 |
38
+ | `packages/app/src/views/**/*.vue`(业务页) | 验证 `TableApiResult<T>` 业务侧消费方写法是否需要补 `as UnwrapRef<T[]>` |
39
+ | `packages/core/vite.config.ts` / `tsconfig.json` | 验证 `core/src/types/dot-path.ts` 与 `core/src/config/list-model.ts` 是否被构建打包 |
40
+ | `packages/core/package.json` | 已确认 `lodash` 在 peerDependencies 行 71,无需新增 dependencies |
41
+
42
+ ### Out-of-Scope([MUST NOT] 触碰的敏感区)
43
+
44
+ | # | 区域 | 理由(对应 NG)|
45
+ |---|---|---|
46
+ | 1 | `packages/app/src/config/request.ts` 中 `APP_REQUEST_CONFIG` / `APP_API_BUSINESS_MODEL_KEY_CONFIG` / `AppApiBusinessModel` | NG-1 / NG-4:业务壳层永不下沉 core,本次保持 app 内现状不动 |
47
+ | 2 | `packages/app/src/components/table/` 与 `packages/app/src/views/list-page/` 组件本体 | NG-3:本次仅前置升级 KEY 配置能力,组件迁移留期 #2 |
48
+ | 3 | `packages/core/src/components/**` 现有 4 子目录(form / menu / display / misc) | 范围外,本次新增的是 `src/types/` + `src/config/` 两个**新目录** |
49
+ | 4 | core/app 双份配置漂移检查(CI / lint rule) | NG-6:取消双份策略后根因消除 |
50
+ | 5 | KEY 配置数组路径形态(`["a","b"]`)/ IoC 注入式 KEY 配置 | NG-2 / NG-5:YAGNI |
51
+ | 6 | 列表/分页类型中手写嵌套字面量 | NG-7:必须从 `typeof KEY_CONFIG` 推导 |
52
+
53
+ ### Self-Healing Rule
54
+
55
+ WHEN 验证失败(`vue-tsc` 类型不通 / `pnpm build` 失败 / 业务页 dev 报错),[MUST] 分析日志尝试修复一次;二次失败则停下汇报。
56
+
57
+ ---
58
+
59
+ ## 2. 架构决策记录 (ADR)
60
+
61
+ ### ADR-1:4 工具 2 层架构(基础 / 应用)
62
+
63
+ | 维度 | 内容 |
64
+ |---|---|
65
+ | **决策** | dot-path 工具拆分为 2 层:基础层(`DotToObject` / `UnionToIntersection`)+ 应用层(`BuildListModel` / `BuildListParams`),全部归属 `packages/core/src/types/dot-path.ts` 单文件 |
66
+ | **理由** | ① 基础层是纯 TypeScript 字符串路径推导,与列表领域无关,未来其他领域(如表单 schema、URL query)也可复用;② 应用层是基于基础层组合的列表领域专用工具,给 list-model.ts 提供高阶接口,避免 list-model.ts 直接耦合 `infer + UnionToIntersection` 实现细节 |
67
+ | **备选方案 A** | 只提供 `BuildListModel` / `BuildListParams` 2 个高阶工具,不暴露基础层 |
68
+ | **A 否决理由** | TableMain.vue 的 `TableApiResult<T>` 只需要"items + page.totalRecord"两字段子集,不需要 page.pageSize、page.page、items 全套——若只暴露 BuildListModel 高阶工具,强制带入完整 4 KEY 推导,会让 TableApiResult 必须用 `Pick<>` 二次裁剪。直接用基础层 `DotToObject + UnionToIntersection` 自由组合反而更灵活 |
69
+ | **备选方案 B** | 4 工具拆 4 文件(`dot-to-object.ts` / `union-to-intersection.ts` / `build-list-model.ts` / `build-list-params.ts`) |
70
+ | **B 否决理由** | 4 工具总行数 ≤ 50,强制分文件徒增 import 噪音;用户已锁定单文件方案 |
71
+ | **状态** | 活跃 |
72
+
73
+ ### ADR-2:core/src/config/list-model.ts 单一来源 + app re-export 切源
74
+
75
+ | 维度 | 内容 |
76
+ |---|---|
77
+ | **决策** | `APP_API_LIST_MODEL_KEY_CONFIG` / `AppApiListModel` / `AppApiListParamsModel` / `AppApiListPageParamsKey` / `AppNoPageParamsKey` 5 项的**唯一定义点**位于 `packages/core/src/config/list-model.ts`;`packages/app/src/config/request.ts` 删除原定义改为从 `@done-coding/admin-core` 整段 `export { ... } from` 转出,维持 `@/config` 消费面不破 |
78
+ | **理由** | ① 消除 core/app 双份漂移根因(NG-6 取消 CI 检查的前提);② core 自身后续的 list-page / table 组件迁移(期 #2)需要内部直接引用,不能从 app 反向 import;③ app 端通过 re-export 而非 import 改写,减少业务页面改动面 |
79
+ | **备选方案 A** | 双份维护 + CI 检查保证一致性 |
80
+ | **A 否决理由** | NG-6:用户裁决取消双份策略;治标不治本 |
81
+ | **备选方案 B** | core 提供,app 端业务页改 import path 至 `@done-coding/admin-core` |
82
+ | **B 否决理由** | 需要全仓库改业务页 import;用户裁决:app `request.ts` 内部 re-export 即可,业务面零改动 |
83
+ | **状态** | 活跃 |
84
+
85
+ ### ADR-3:DUCK 最小化 + `typeof KEY_CONFIG` 推导贯彻全域(NG-7 硬约束)
86
+
87
+ | 维度 | 内容 |
88
+ |---|---|
89
+ | **决策** | 所有列表/分页相关类型(`AppApiListModel` / `AppApiListParamsModel` / `AppApiListPageParamsKey` / `TableApiParams` / `TableApiResult`)的字段名 [MUST] 从 `typeof APP_API_LIST_MODEL_KEY_CONFIG.XXX_KEY` 读取,**禁止手写嵌套字面量**(如直接 `{ items: T[]; page: { totalRecord: number } }`) |
90
+ | **理由** | KEY runtime 取值并非永久不变——消费方可通过发新版或 `pnpm patch` 重写 4 KEY 取值;手写字面量会让 runtime 改而类型不跟进,等于自废 dot-path 推导能力 |
91
+ | **备选方案 A** | 类型层手写字面量,运行时层用 lodash get/set |
92
+ | **A 否决理由** | 类型与运行时一旦不一致,pnpm patch 场景下消费方将获得错误的类型提示;NG-7 显式禁止 |
93
+ | **状态** | 活跃 |
94
+
95
+ ### ADR-4:取消 core/app 双份镜像,pnpm patch 作为消费方自定义分页字段的逃生通道
96
+
97
+ | 维度 | 内容 |
98
+ |---|---|
99
+ | **决策** | 不引入 IoC 注入(plugin install 选项 / createAdminCore 工厂等);消费方需自定义分页 KEY 时通过 `pnpm patch @done-coding/admin-core` 重写已发布产物中的 `APP_API_LIST_MODEL_KEY_CONFIG` 取值 |
100
+ | **理由** | ① IoC 注入需要给所有列表类型工具提供运行时配置出口,复杂度溢出;② 真实业务场景中分页字段是公司级约定,跨项目变更频率极低;③ pnpm patch 是 monorepo 工具链层标准能力,零代码侵入;④ 用户裁决:一个月内不考虑 IoC(NG-2) |
101
+ | **备选方案 A** | createAdminCore 工厂 + plugin install 选项注入 KEY |
102
+ | **A 否决理由** | NG-2 + 当前列表类型工具均为静态类型推导,IoC 化需要全部改成运行时 generic 工厂;改造面过大 |
103
+ | **状态** | 活跃 |
104
+
105
+ ### ADR-5:业务壳层(code/data/message + APP_REQUEST_CONFIG)永不下沉 core
106
+
107
+ | 维度 | 内容 |
108
+ |---|---|
109
+ | **决策** | `APP_REQUEST_CONFIG`(含 basePath 环境变量 + timeout)/ `APP_API_BUSINESS_MODEL_KEY_CONFIG`(code/message/data 业务码外壳)/ `AppApiBusinessModel<T>` 三项保留在 `packages/app/src/config/request.ts`,不下沉 core |
110
+ | **理由** | ① 业务码字段名(code/data/message)属公司级业务约定,不同后端可能用 errno/result/msg;② basePath 来自 `import.meta.env.VITE_API_BASE_PATH` 是 app 构建期常量,core 包构建期不可知;③ NG-1 硬约束:业务壳层留 app |
111
+ | **备选方案 A** | 与列表 KEY 配置一起下沉 core,通过 IoC 注入 |
112
+ | **A 否决理由** | NG-1 + NG-2 双重否决;核心理由——列表分页是 admin 通用结构,业务码是公司业务特征,性质不同 |
113
+ | **状态** | 活跃 |
114
+
115
+ ---
116
+
117
+ ## 3. 关键技术点设计
118
+
119
+ ### 3.1 REQ-1:dot-path.ts(新建)
120
+
121
+ **路径:** `packages/core/src/types/dot-path.ts`
122
+
123
+ **实现样例(直接采纳 requirements.md 锁定方案):**
124
+
125
+ ```ts
126
+ /**
127
+ * 列表 KEY 配置接口
128
+ * 约束消费方传入的 KEY_CONFIG 对象 shape,后续 BuildListModel / BuildListParams 工具基于本接口推导
129
+ */
130
+ export interface ListModelKeyConfig {
131
+ PAGE_SIZE_KEY: string;
132
+ CURRENT_PAGE_KEY: string;
133
+ TOTAL_KEY: string;
134
+ LIST_KEY: string;
135
+ }
136
+
137
+ /**
138
+ * 字符串点路径转嵌套对象类型
139
+ * @example
140
+ * DotToObject<"page.page", number> -> { page: { page: number } }
141
+ * DotToObject<"items", T[]> -> { items: T[] }
142
+ * DotToObject<"a.b.c", string> -> { a: { b: { c: string } } }
143
+ */
144
+ export type DotToObject<P extends string, V> = P extends `${infer Head}.${infer Tail}`
145
+ ? { [K in Head]: DotToObject<Tail, V> }
146
+ : { [K in P]: V };
147
+
148
+ /**
149
+ * 联合类型转交叉类型
150
+ * 用于多个 DotToObject 结果合并:{ page: { page: number } } | { page: { pageSize: number } }
151
+ * -> { page: { page: number; pageSize: number } }
152
+ */
153
+ export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
154
+ k: infer I,
155
+ ) => void
156
+ ? I
157
+ : never;
158
+
159
+ /**
160
+ * 基于 KEY_CONFIG 联合推导完整列表响应模型
161
+ * @example
162
+ * BuildListModel<typeof APP_API_LIST_MODEL_KEY_CONFIG, User>
163
+ * -> { items: User[]; page: { page: number; pageSize: number; totalRecord: number } }
164
+ */
165
+ export type BuildListModel<KC extends ListModelKeyConfig, T> = UnionToIntersection<
166
+ | DotToObject<KC["LIST_KEY"], T[]>
167
+ | DotToObject<KC["TOTAL_KEY"], number>
168
+ | DotToObject<KC["CURRENT_PAGE_KEY"], number>
169
+ | DotToObject<KC["PAGE_SIZE_KEY"], number>
170
+ >;
171
+
172
+ /**
173
+ * 基于 KEY_CONFIG 联合推导列表请求参数模型
174
+ * @example
175
+ * BuildListParams<typeof APP_API_LIST_MODEL_KEY_CONFIG, { keyword?: string }>
176
+ * -> { keyword?: string } & { page: { page: number; pageSize: number } }
177
+ */
178
+ export type BuildListParams<
179
+ KC extends ListModelKeyConfig,
180
+ P extends Record<string, any> = Record<string, any>,
181
+ > = P &
182
+ UnionToIntersection<
183
+ DotToObject<KC["CURRENT_PAGE_KEY"], number> | DotToObject<KC["PAGE_SIZE_KEY"], number>
184
+ >;
185
+ ```
186
+
187
+ **类型断言验证(开发实施时手写到验证脚本):**
188
+
189
+ ```ts
190
+ type Equal<X, Y> =
191
+ (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
192
+
193
+ type _A1 = Equal<DotToObject<"page.page", number>, { page: { page: number } }>; // true
194
+ type _A2 = Equal<DotToObject<"items", string[]>, { items: string[] }>; // true
195
+ type _A3 = Equal<
196
+ UnionToIntersection<{ a: { x: 1 } } | { a: { y: 2 } }>,
197
+ { a: { x: 1; y: 2 } }
198
+ >; // true
199
+ ```
200
+
201
+ ### 3.2 REQ-2 + REQ-3:list-model.ts(新建)
202
+
203
+ **路径:** `packages/core/src/config/list-model.ts`
204
+
205
+ **实现样例(直接采纳 requirements.md 锁定方案):**
206
+
207
+ ```ts
208
+ import type { BuildListModel, BuildListParams } from "../types/dot-path";
209
+
210
+ /** 应用接口列表模型配置(core 单一来源) */
211
+ export const APP_API_LIST_MODEL_KEY_CONFIG = Object.freeze({
212
+ /** 当前页码 key */
213
+ CURRENT_PAGE_KEY: "page.page" as const,
214
+ /** 每页条数 key */
215
+ PAGE_SIZE_KEY: "page.pageSize" as const,
216
+ /** 总数 key */
217
+ TOTAL_KEY: "page.totalRecord" as const,
218
+ /** 数据列表 key */
219
+ LIST_KEY: "items" as const,
220
+ });
221
+
222
+ /** 应用接口列表模型 */
223
+ export type AppApiListModel<T = any> = BuildListModel<typeof APP_API_LIST_MODEL_KEY_CONFIG, T>;
224
+
225
+ /** 应用接口列表分页参数 key */
226
+ export type AppApiListPageParamsKey =
227
+ | typeof APP_API_LIST_MODEL_KEY_CONFIG.PAGE_SIZE_KEY
228
+ | typeof APP_API_LIST_MODEL_KEY_CONFIG.CURRENT_PAGE_KEY;
229
+
230
+ /** 应用 排除分页参数的 key */
231
+ export type AppNoPageParamsKey = Exclude<string, AppApiListPageParamsKey>;
232
+
233
+ /** 应用接口列表请求参数模型 */
234
+ export type AppApiListParamsModel<
235
+ P extends Record<AppNoPageParamsKey, any> = Record<AppNoPageParamsKey, any>,
236
+ > = BuildListParams<typeof APP_API_LIST_MODEL_KEY_CONFIG, Partial<P>>;
237
+ ```
238
+
239
+ **core/src/index.ts 追加 re-export(在 `export * from "./types"` 之后追加 2 行):**
240
+
241
+ ```ts
242
+ // 已有:
243
+ // export * from "./types";
244
+
245
+ // 追加:
246
+ export * from "./types/dot-path";
247
+ export * from "./config/list-model";
248
+ ```
249
+
250
+ > 注意:`./types` 当前若已 barrel-export 全部 `./types/*`,则 `./types/dot-path` 会被自动覆盖;本设计 [MUST] 在实施时检查 `core/src/types/index.ts`(若存在)是否已 `export * from "./dot-path"`。若 `core/src/types/` 当前无 barrel 文件,则在 `core/src/index.ts` 显式 re-export 子文件。
251
+
252
+ ### 3.3 REQ-4:app/components/table/types.ts 重写
253
+
254
+ **改造点(仅 `TableApiParams` + `TableApiResult` 2 类型,其他 7 类型不动):**
255
+
256
+ ```ts
257
+ // 改造前
258
+ import type { APP_API_LIST_MODEL_KEY_CONFIG, AppApiListModel } from "@/config";
259
+
260
+ export type TableApiParams<
261
+ SQ extends Record<string, any> = Record<string, any>,
262
+ > = SQ & {
263
+ [APP_API_LIST_MODEL_KEY_CONFIG.CURRENT_PAGE_KEY]: number;
264
+ [APP_API_LIST_MODEL_KEY_CONFIG.PAGE_SIZE_KEY]: number;
265
+ };
266
+
267
+ export type TableApiResult<
268
+ T extends Record<string, any> = Record<string, any>,
269
+ > = Pick<
270
+ AppApiListModel<T>,
271
+ | typeof APP_API_LIST_MODEL_KEY_CONFIG.TOTAL_KEY
272
+ | typeof APP_API_LIST_MODEL_KEY_CONFIG.LIST_KEY
273
+ >;
274
+
275
+ // 改造后
276
+ import type {
277
+ APP_API_LIST_MODEL_KEY_CONFIG,
278
+ BuildListParams,
279
+ DotToObject,
280
+ UnionToIntersection,
281
+ } from "@/config";
282
+
283
+ /** 表格请求参数 = 静态参数 SQ + KEY_CONFIG 推导出的 page.page / page.pageSize 嵌套 */
284
+ export type TableApiParams<
285
+ SQ extends Record<string, any> = Record<string, any>,
286
+ > = BuildListParams<typeof APP_API_LIST_MODEL_KEY_CONFIG, SQ>;
287
+
288
+ /** 表格响应结果 = items 列表 + page.totalRecord 总数(仅 2 字段子集,不要 page.page / page.pageSize)*/
289
+ export type TableApiResult<
290
+ T extends Record<string, any> = Record<string, any>,
291
+ > = UnionToIntersection<
292
+ | DotToObject<typeof APP_API_LIST_MODEL_KEY_CONFIG.LIST_KEY, T[]>
293
+ | DotToObject<typeof APP_API_LIST_MODEL_KEY_CONFIG.TOTAL_KEY, number>
294
+ >;
295
+ ```
296
+
297
+ **等价验证(vue-tsc 通过后人工核对):**
298
+
299
+ | 类型 | 推导结果 |
300
+ |---|---|
301
+ | `TableApiParams<{ keyword?: string }>` | `{ keyword?: string } & { page: { page: number; pageSize: number } }` |
302
+ | `TableApiResult<{ id: number }>` | `{ items: { id: number }[] } & { page: { totalRecord: number } }` |
303
+
304
+ **[MUST NOT] 改动的 7 类型:** `TableMainInstance` / `TableColumnDefaultScope` / `TableColumnComponentProps` / `TableColumnHeaderScope` / `ExtractElTableColumnProps` / `ElTableColumnProps` / `TableMainProps`。
305
+
306
+ **`@/config` re-export 链路:** REQ-2 已要求 `app/src/config/request.ts` 从 core re-export 5 项;本 REQ 新增 import 的 `BuildListParams` / `DotToObject` / `UnionToIntersection` 同样需在 `app/src/config/request.ts` 内补 re-export,或通过 `app/src/config/index.ts` barrel 直接 `export * from "@done-coding/admin-core"` 一次到位(实施时择优)。
307
+
308
+ ### 3.4 REQ-5:TableMain.vue 4 处运行时改造
309
+
310
+ **改造点 1+2(`params` computed 构造,行 161-167):**
311
+
312
+ ```ts
313
+ // 改造前
314
+ const params = computed<TableApiParams<SQ>>(() => {
315
+ return {
316
+ ...(props.query! || {}),
317
+ [APP_API_LIST_MODEL_KEY_CONFIG.PAGE_SIZE_KEY]: pageSize.value,
318
+ [APP_API_LIST_MODEL_KEY_CONFIG.CURRENT_PAGE_KEY]: currentPage.value,
319
+ };
320
+ });
321
+
322
+ // 改造后
323
+ const params = computed<TableApiParams<SQ>>(() => {
324
+ const merged = _cloneDeep(props.query || {}) as TableApiParams<SQ>;
325
+ _set(merged, APP_API_LIST_MODEL_KEY_CONFIG.PAGE_SIZE_KEY, pageSize.value);
326
+ _set(merged, APP_API_LIST_MODEL_KEY_CONFIG.CURRENT_PAGE_KEY, currentPage.value);
327
+ return merged;
328
+ });
329
+ ```
330
+
331
+ **改造点 3+4(响应数据读取,行 212-215):**
332
+
333
+ ```ts
334
+ // 改造前
335
+ list.value = res[APP_API_LIST_MODEL_KEY_CONFIG.LIST_KEY] as UnwrapRef<T[]>;
336
+ total.value = res[APP_API_LIST_MODEL_KEY_CONFIG.TOTAL_KEY];
337
+
338
+ // 改造后
339
+ list.value = _get(res, APP_API_LIST_MODEL_KEY_CONFIG.LIST_KEY) as UnwrapRef<T[]>;
340
+ total.value = _get(res, APP_API_LIST_MODEL_KEY_CONFIG.TOTAL_KEY);
341
+ ```
342
+
343
+ **imports 调整(脚本顶部新增 2 行,保留现有 `_cloneDeep` / `_omit`):**
344
+
345
+ ```ts
346
+ import _cloneDeep from "lodash/cloneDeep";
347
+ import _omit from "lodash/omit";
348
+ import _set from "lodash/set"; // 新增
349
+ import _get from "lodash/get"; // 新增
350
+ ```
351
+
352
+ **关键约束验证:** `props.query` 已含 `{ page: { query: ..., orderBy: ... } }` 时,`_cloneDeep(props.query)` + `_set(merged, "page.page", N)` 仅更新 `page.page` 与 `page.pageSize` 字段,不覆盖 `page.query` / `page.orderBy` —— lodash set 自动 merge 中间对象,不打覆盖。
353
+
354
+ ### 3.5 REQ-6:core docs/TECH_SNAPSHOT.md 起步版骨架
355
+
356
+ **路径:** `packages/core/docs/TECH_SNAPSHOT.md`(新建)
357
+
358
+ **4 章节要点(实施时按本骨架填充):**
359
+
360
+ ```markdown
361
+ # @done-coding/admin-core 技术架构快照
362
+
363
+ ## 1. 设计边界
364
+
365
+ ### 1.1 包含范围(✓)
366
+ - 通用 admin 基础设施组件(form / menu / display / misc)
367
+ - 通用列表分页结构(APP_API_LIST_MODEL_KEY_CONFIG / AppApiListModel<T>)
368
+ - dot-path 类型工具(DotToObject / UnionToIntersection / BuildListModel / BuildListParams)
369
+ - 通用 hooks / utils / types
370
+
371
+ ### 1.2 排除范围(✗,业务壳层永不下沉)
372
+ - 业务码外壳(APP_API_BUSINESS_MODEL_KEY_CONFIG / AppApiBusinessModel<T>)
373
+ - 业务接口配置(APP_REQUEST_CONFIG,含 basePath / timeout)
374
+ - 业务路由 / 业务枚举
375
+ - IoC 注入式 KEY 配置(一个月内不引入)
376
+
377
+ ## 2. 已知约束
378
+
379
+ ### 2.1 分页 KEY 固化
380
+ - 4 KEY 取值在 core 内固化为公司级约定(page.page / page.pageSize / page.totalRecord / items)
381
+ - 类型层与运行时层均从 typeof KEY_CONFIG 推导,禁止手写嵌套字面量(NG-7)
382
+
383
+ ### 2.2 pnpm patch 逃生通道
384
+ - 消费方自定义分页字段时通过 pnpm patch @done-coding/admin-core 重写已发布产物的 KEY 取值
385
+ - 最小步骤:
386
+ 1. pnpm patch @done-coding/admin-core
387
+ 2. 修改 patch 目录下 es/index.mjs / lib/index.cjs / types/index.d.ts 中 APP_API_LIST_MODEL_KEY_CONFIG 取值
388
+ 3. pnpm patch-commit <patch-dir>
389
+ 4. 重启 dev / 重新 build
390
+ - 警示:不通过业务运行时配置覆盖 KEY,会失去类型推导能力(typeof 推导依赖编译期常量)
391
+
392
+ ## 3. 类型工具说明
393
+
394
+ ### 3.1 DotToObject<P, V>
395
+ - 字符串点路径转嵌套对象类型
396
+ - 输入:`"a.b.c"` + `V` → 输出:`{ a: { b: { c: V } } }`
397
+
398
+ ### 3.2 UnionToIntersection<U>
399
+ - 联合类型转交叉
400
+ - 用于多个 DotToObject 结果合并
401
+
402
+ ### 3.3 BuildListModel<KC, T>
403
+ - 基于 KEY_CONFIG 联合推导完整列表响应模型
404
+ - 输入:`typeof KEY_CONFIG` + `T` → 输出:`{ items: T[]; page: { page; pageSize; totalRecord } }`
405
+
406
+ ### 3.4 BuildListParams<KC, P>
407
+ - 基于 KEY_CONFIG 联合推导列表请求参数模型
408
+ - 输入:`typeof KEY_CONFIG` + `P` → 输出:`P & { page: { page; pageSize } }`
409
+
410
+ ## 4. 架构决策记录 (ADR)
411
+
412
+ ### ADR-1:4 工具 2 层架构(基础 + 应用)
413
+ (同本任务 design.md ADR-1 摘要)
414
+
415
+ ### ADR-2:core 单一来源 + app re-export
416
+ (同本任务 design.md ADR-2 摘要)
417
+
418
+ ### ADR-3:DUCK 最小化 + typeof KEY_CONFIG 推导
419
+ (同本任务 design.md ADR-3 摘要)
420
+
421
+ ### ADR-4:取消双份 + pnpm patch 逃生通道
422
+ (同本任务 design.md ADR-4 摘要)
423
+
424
+ ### ADR-5:业务壳层永不下沉 core
425
+ (同本任务 design.md ADR-5 摘要)
426
+ ```
427
+
428
+ ### 3.6 REQ-7:core README.md 进阶用法节骨架
429
+
430
+ **追加节(在现有「使用」节之后):**
431
+
432
+ ```markdown
433
+ ## 进阶用法
434
+
435
+ ### 自定义分页字段(pnpm patch 逃生通道)
436
+
437
+ `@done-coding/admin-core` 的列表分页 KEY 配置(`APP_API_LIST_MODEL_KEY_CONFIG`)在 core 包内**固化为公司级约定**:
438
+
439
+ ```ts
440
+ {
441
+ CURRENT_PAGE_KEY: "page.page",
442
+ PAGE_SIZE_KEY: "page.pageSize",
443
+ TOTAL_KEY: "page.totalRecord",
444
+ LIST_KEY: "items",
445
+ }
446
+ ```
447
+
448
+ 若消费方后端约定不同(如 `current` / `size` / `total` / `list`),通过 pnpm patch 在工具链层重写:
449
+
450
+ ```bash
451
+ # 1. 创建 patch
452
+ pnpm patch @done-coding/admin-core
453
+
454
+ # 2. 编辑临时目录下的 es/index.mjs / lib/index.cjs / types/index.d.ts
455
+ # 将 APP_API_LIST_MODEL_KEY_CONFIG 取值改为消费方约定
456
+ # (注意类型 d.ts 中的字符串字面量也要同步改)
457
+
458
+ # 3. 提交 patch
459
+ pnpm patch-commit <临时目录>
460
+
461
+ # 4. 重启 dev / 重新 build
462
+ ```
463
+
464
+ **[MUST NOT]** 在业务运行时通过 plugin 配置 / 全局变量 / 工厂函数覆盖 KEY 取值——会让 `typeof KEY_CONFIG` 类型推导失效(编译期常量),等于自废 dot-path 工具能力。详见 [TECH_SNAPSHOT 已知约束](./docs/TECH_SNAPSHOT.md#22-pnpm-patch-逃生通道)。
465
+ ```
466
+
467
+ ---
468
+
469
+ ## 4. 开发范式 / 参考模块
470
+
471
+ | 范式 | 来源 / 参考 |
472
+ |---|---|
473
+ | 单文件类型工具 | `packages/core/src/types/dot-path.ts`(新建,本任务) |
474
+ | `Object.freeze` + `as const` 配置常量 | 现有 `packages/app/src/config/request.ts` 已有 `APP_API_BUSINESS_MODEL_KEY_CONFIG` |
475
+ | `BuildListModel` / `BuildListParams` 高阶组合 | 本次新建,无既有参考 |
476
+ | lodash `_set` / `_get` 操作点路径 | `packages/app/src/components/table/TableMain.vue` 已用 `_cloneDeep` / `_omit`,新增 2 工具同族 |
477
+ | core 包内子目录 barrel re-export | 现有 `packages/core/src/index.ts` 行 8-14(`export * from "./components/form"` 等) |
478
+ | `Pick<>` / `Exclude<>` / `Partial<>` 工具类型组合 | 现有 `packages/app/src/components/table/types.ts` 已用 |
479
+
480
+ ---
481
+
482
+ ## 5. 风险登记表
483
+
484
+ | # | 风险 | 严重度 | 应对策略 | 验证方式 |
485
+ |---|---|---|---|---|
486
+ | **R1** | 跨包 d.ts 同步:core 改后 app dev/build 是否能自动消费最新类型?vite-plugin-dts 是否 watch 模式? | 中 | core 的 `dev` 脚本已是 `vite build -w -m hotBuild`(package.json 行 28),watch 模式启用;首次切源时 [MUST] 先在 core 目录跑 `pnpm build` 生成 d.ts,再启 app dev,确保 `node_modules/@done-coding/admin-core/types/index.d.ts` 已含新导出 | 实施 TASK-3.1:`pnpm -F @done-coding/admin-core build` 后再启 app `pnpm dev`,验证 vue-tsc 无报错 |
487
+ | **R2** | lodash 依赖归属:core 是否需要显式声明 lodash 依赖? | 低 | 已确认:`packages/core/package.json` 行 71 已在 peerDependencies 声明 `lodash: ^4.17.21`;类型 `@types/lodash` 已在 devDependencies 行 51。core 内 import `lodash/get` 等子模块类型可解析 | 实施 TASK-1.1:检查 `core/src/types/dot-path.ts` 内**纯类型**实现不需要 import lodash;list-model.ts 同样仅类型,无需 import lodash。core 包内本任务实际不引入新的 lodash runtime import |
488
+ | **R3** | app re-export 后 vue-tsc 类型链路是否正确解析?`@/config` barrel 多层转出 + tsconfig paths 是否产生类型推导黑洞 | 中 | 实施前 [MUST] Read `packages/app/src/config/index.ts` 确认 barrel 形态;改造后 vue-tsc 跑全量 + 抽样业务页(Page1.vue 类)vue-tsc 验证;若出现类型推导降级(推导成 `any`),降级方案:业务页直接 `import { APP_API_LIST_MODEL_KEY_CONFIG } from "@done-coding/admin-core"` 跳过 barrel | TASK-3.1 / TASK-3.2 类型断言 + 业务页抽样 |
489
+ | **R4** | pnpm patch 重写 KEY runtime 时 typeof 推导是否随之更新? | 中 | typeof 推导基于 d.ts 中的字符串字面量类型;pnpm patch 同步改写 `types/index.d.ts` 中 `APP_API_LIST_MODEL_KEY_CONFIG` 的 `as const` 字面量;README 进阶用法节明文提示「3 文件同步改」(es / lib / types) | 本任务不实测 patch(验证由消费方接收),但 README 文档需含明文警示 |
490
+ | **R5** | TableApiResult 改用 `DotToObject + UnionToIntersection` 后,业务侧 `as UnwrapRef<T[]>` 断言是否仍编译通过? | 低 | `_get(res, "items")` 返回 `T[] \| undefined`(lodash get 类型签名),断言 `as UnwrapRef<T[]>` 在类型层强转,与改造前 `res["items"] as UnwrapRef<T[]>` 等价 | TASK-3.1 vue-tsc + TASK-3.3 业务页 dev 回归 |
491
+ | **R6** | `_cloneDeep(props.query)` 类型断言到 `TableApiParams<SQ>` 时,TS 可能因结构不完整报错(缺 `page` 字段) | 中 | 用 `as TableApiParams<SQ>` 强制断言(merged 在 `_set` 后会补齐 page 子对象);这是合理的"过程态"断言。若 TS 仍拒绝,降级为 `as unknown as TableApiParams<SQ>` 二次断言 | TASK-2.3 vue-tsc 验证 |
492
+
493
+ ---
494
+
495
+ ## 6. 任务粒度预案(tasks.md 拆分草案)
496
+
497
+ 3 批共 11 TASK,每个 TASK 满足 ≤ 3 文件 / ≤ 5 步骤。
498
+
499
+ ### 批次一:core 类型工具 + 配置 + 文档骨架(5 TASK)
500
+
501
+ | TASK | 描述 | 文件数 | 步骤数 | 可并行 |
502
+ |---|---|---|---|---|
503
+ | TASK-1.1 | 新建 `core/src/types/dot-path.ts`(4 工具 + ListModelKeyConfig 接口) | 1 | 5(ListModelKeyConfig / DotToObject / UnionToIntersection / BuildListModel / BuildListParams) | 与 1.2 串行(1.2 import 1.1)|
504
+ | TASK-1.2 | 新建 `core/src/config/list-model.ts`(const + 4 type) | 1 | 5(const + AppApiListModel + AppApiListPageParamsKey + AppNoPageParamsKey + AppApiListParamsModel) | 依赖 1.1 |
505
+ | TASK-1.3 | 修改 `core/src/index.ts` 追加 2 行 re-export | 1 | 2 | 依赖 1.1+1.2 |
506
+ | TASK-1.4 | 新建 `core/docs/TECH_SNAPSHOT.md`(4 章节起步版) | 1 | 4 | 与 1.1-1.3 并行 ✓ |
507
+ | TASK-1.5 | 修改 `core/README.md` 追加进阶用法节 | 1 | 1 | 与 1.1-1.3 并行 ✓ |
508
+
509
+ **批次一里程碑:** core 包 `pnpm build` 通过,`types/index.d.ts` 含 `APP_API_LIST_MODEL_KEY_CONFIG` / `AppApiListModel` / `BuildListModel` / `BuildListParams` 等导出。
510
+
511
+ ### 批次二:app 切源 + table 改造(3 TASK)
512
+
513
+ | TASK | 描述 | 文件数 | 步骤数 | 可并行 |
514
+ |---|---|---|---|---|
515
+ | TASK-2.1 | 修改 `app/src/config/request.ts` 删除 5 项 + 从 core re-export | 1 | 3(删 5 项 / 加 import + re-export / 验证 `@/config` 消费面) | 依赖批次一 |
516
+ | TASK-2.2 | 修改 `app/src/components/table/types.ts` 重写 `TableApiParams` + `TableApiResult` | 1 | 3(改 import / 重写 TableApiParams / 重写 TableApiResult) | 依赖 2.1 |
517
+ | TASK-2.3 | 修改 `app/src/components/table/TableMain.vue` 4 处运行时改造 | 1 | 5(加 2 import / 改 params 改造点 1 / 改 params 改造点 2 / 改响应 LIST 读取 / 改响应 TOTAL 读取) | 依赖 2.2 |
518
+
519
+ **批次二里程碑:** app 包 `pnpm build` + `vue-tsc` 通过;类型抽样验证 `TableApiParams<{keyword?: string}>` 推导为预期形态。
520
+
521
+ ### 批次三:验证(3 TASK)
522
+
523
+ | TASK | 描述 | 文件数 | 步骤数 | 可并行 |
524
+ |---|---|---|---|---|
525
+ | TASK-3.1 | vue-tsc 类型断言验证(Equal 单元测试 + 业务页全量类型扫描) | 0(仅运行验证) | 3(写临时 .ts 类型断言文件 / 跑 vue-tsc / 删临时文件) | 依赖批次二 |
526
+ | TASK-3.2 | `pnpm build` 在 core + app 双向通过 | 0 | 2(`pnpm -F @done-coding/admin-core build` / `pnpm -F app build`) | 依赖批次二 |
527
+ | TASK-3.3 | 业务页 dev 回归(如 Page1.vue / 抽样 1-2 个 list-page 业务页) | 0 | 3(启 app dev / 触发表格分页+搜索+排序 / 对比改造前后行为) | 依赖批次二 |
528
+
529
+ **总计:** 11 TASK,每个 TASK ≤ 3 文件(实际全部 ≤ 1 文件)+ ≤ 5 步骤 ✓。
530
+
531
+ ---
532
+
533
+ ## 7. 验证策略
534
+
535
+ ### 7.1 类型层验证
536
+
537
+ | 验证点 | 命令 | 预期 |
538
+ |---|---|---|
539
+ | dot-path 4 工具 Equal 类型断言 | TASK-3.1 临时 `.ts` 文件 + `pnpm -F @done-coding/admin-core build` | vue-tsc 通过;3 个 `Equal` 类型推导为 `true` |
540
+ | AppApiListModel 推导验证 | 同上 | `Equal<AppApiListModel<{id:1}>, {items:{id:1}[]; page:{page:number; pageSize:number; totalRecord:number}}>` = true |
541
+ | TableApiParams 推导验证 | 同上 | `Equal<TableApiParams<{keyword?:string}>, {keyword?:string} & {page:{page:number; pageSize:number}}>` = true |
542
+ | TableApiResult 推导验证 | 同上 | `Equal<TableApiResult<{id:number}>, {items:{id:number}[]} & {page:{totalRecord:number}}>` = true |
543
+
544
+ ### 7.2 构建层验证
545
+
546
+ | 验证点 | 命令 | 预期 |
547
+ |---|---|---|
548
+ | core 包构建 | `pnpm -F @done-coding/admin-core build` | exit 0;产物 `packages/core/types/index.d.ts` 含 4 工具 + 5 项配置/类型 |
549
+ | app 包构建 | `pnpm -F app build` | exit 0;产物正常打包 |
550
+
551
+ ### 7.3 运行时层验证
552
+
553
+ | 验证点 | 操作 | 预期 |
554
+ |---|---|---|
555
+ | 业务页表格分页 | 启 app dev → 打开抽样 list-page → 切换页码 | 请求参数 body 含 `{ page: { page: N, pageSize: 20, ...原 page 字段 } }`;响应解析正确显示列表 |
556
+ | 业务页表格搜索 | 同上 → 输入搜索条件 | 请求参数中 `props.query` 字段(如 `keyword`)正确合入;分页字段正确 |
557
+ | 业务页表格排序 | 同上 → 切换排序列 | `props.query.page.orderBy`(若有)保留;不被 `_set("page.page", N)` 覆盖 |
558
+ | `props.query` 含 `page.query` | mock `props.query = { page: { query: {...} } }` | `_set("page.page", N)` 后 merged 同时含 `page.query` 与 `page.page` / `page.pageSize` |
559
+
560
+ ---
561
+
562
+ ## 8. 自查记录(架构师同 agent 自查清单)
563
+
564
+ | # | 自查项 | 状态 |
565
+ |---|---|---|
566
+ | 1 | design.md 与 requirements.md 一致性(无矛盾、无遗漏 REQ) | ✓ REQ-1 ~ REQ-7 全部覆盖到 §3.1-3.6;NG-1 ~ NG-7 全部映射到 §1.3 Out-of-Scope 与 ADR-3/4/5 |
567
+ | 2 | DUCK + typeof KEY_CONFIG 推导原则贯彻每一行类型设计 | ✓ AppApiListModel / AppApiListParamsModel / TableApiParams / TableApiResult 全部从 `typeof APP_API_LIST_MODEL_KEY_CONFIG.XXX_KEY` 推导;零手写嵌套字面量;ADR-3 + NG-7 双重锁定 |
568
+ | 3 | 变更范围防火墙完整(4 段都填) | ✓ §1 含 Direct Targets(8 处)/ Collateral Reads(4 项)/ Out-of-Scope(6 项)/ Self-Healing Rule |
569
+ | 4 | 5 条 ADR 齐全 + 每条理由完整 | ✓ ADR-1 ~ ADR-5 全部含决策 / 理由 / 备选方案 / 否决理由 / 状态 5 字段 |
570
+ | 5 | 风险登记 ≥ 4 条 + 每条含缓解策略 | ✓ R1 ~ R6 共 6 条,每条含描述 / 严重度 / 应对策略 / 验证方式 |
571
+ | 6 | 任务粒度满足 ≤ 3 文件 / ≤ 5 步骤(每个 TASK 自检) | ✓ §6 共 11 TASK,每个 TASK 实际均 ≤ 1 文件 + ≤ 5 步骤 |
572
+ | 7 | U+FFFD 乱码自检 | ✓ 全文无 U+FFFD 字符 |
573
+ | 8 | 与 NG-1 ~ NG-7 无任何冲突 | ✓ NG-1 (业务壳层) → §1.3 #1 + ADR-5;NG-2 (IoC) → ADR-4;NG-3 (组件本体) → §1.3 #2;NG-4 (request.ts) → §1.3 #1;NG-5 (数组路径) → §1.3 #5;NG-6 (CI 检查) → §1.3 #4;NG-7 (字面量) → ADR-3 + §1.3 #6 |
574
+
575
+ **自查结论:** 8 项全 ✓ → frontmatter 已标记「审核状态: 已通过」。