@qilitt-mickey/vue3-temp-skill 1.1.41 → 1.1.43

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * A-H 端到端一键回归(作者侧维护工具,不随技能分发)
4
+ * A-L 端到端一键回归(作者侧维护工具,不随技能分发)
5
5
  *
6
6
  * 用法(cwd 任意,脚本内部已固定为 skill 根目录执行):
7
7
  * node parser/regression.js
@@ -15,7 +15,11 @@
15
15
  * E match 负例2(mapping_version 不符) → exit 1
16
16
  * F match 负例3(禁词阻断) → exit 1
17
17
  * G match 旧协议兼容 → exit 0 且输出含 ⚠ 告警
18
- * H build --dry 产物链自检 → exit 0 且输出含 dry-run
18
+ * H match 整页场景还原样例 → exit 0 且输出含 #245BFF(壳层+内容区全模块 L1)
19
+ * I build --dry 产物链自检 → exit 0 且输出含 dry-run
20
+ * J match V3.2 三声明维度 → exit 0 且输出含「结构施工 structure_ops」
21
+ * K audit 挂载点 / 死通道审计 → exit 1(夹具项目故意存在挂载点缺失 / 死通道)
22
+ * L verify 渲染态逐点验收 → exit 1 且三类归因各计其数(夹具含故意缺口)
19
23
  *
20
24
  * 任一步骤失败 → 本脚本 exit 1,并回显该步骤完整输出便于定位。
21
25
  */
@@ -103,12 +107,33 @@ const steps = [
103
107
  args: ["build", "--dry"],
104
108
  expect: { code: 0, includes: "dry-run" },
105
109
  },
110
+ {
111
+ key: "J",
112
+ name: "match V3.2 三声明维度(apply_via / layer / structure_ops)",
113
+ script: INDEX,
114
+ args: ["match", path.join(FIXTURES, "changeset-v32-fields.json")],
115
+ expect: { code: 0, includes: ["结构施工 structure_ops", "验收基线 oracle"] },
116
+ },
117
+ {
118
+ key: "K",
119
+ name: "audit 挂载点 / 死通道审计(夹具项目应报出问题 → exit 1)",
120
+ script: INDEX,
121
+ args: ["audit", "--project", path.join(FIXTURES, "fake-project"), path.join(FIXTURES, "changeset-v32-fields.json")],
122
+ expect: { code: 1, includes: "挂载 / 通道问题" },
123
+ },
124
+ {
125
+ key: "L",
126
+ name: "verify 渲染态逐点验收(夹具含三类故意缺口 → exit 1)",
127
+ script: INDEX,
128
+ args: ["verify", "--oracle", path.join(FIXTURES, "verify", "oracle.json"), "--actual", path.join(FIXTURES, "verify", "actual.json")],
129
+ expect: { code: 1, includes: "选择器落空 1 / 权重被压 1 / 语境错位 2" },
130
+ },
106
131
  ];
107
132
 
108
133
  const results = [];
109
134
  let failed = 0;
110
135
 
111
- console.log("=== A-I 端到端一键回归 ===\n");
136
+ console.log("=== A-L 端到端一键回归 ===\n");
112
137
 
113
138
  for (const step of steps) {
114
139
  const { status, stdout, stderr } = spawnSync(
@@ -120,7 +145,11 @@ for (const step of steps) {
120
145
  const code = status ?? -1;
121
146
 
122
147
  const codeOk = code === step.expect.code;
123
- const textOk = step.expect.includes ? output.includes(step.expect.includes) : true;
148
+ const wanted = step.expect.includes === undefined
149
+ ? []
150
+ : (Array.isArray(step.expect.includes) ? step.expect.includes : [step.expect.includes]);
151
+ const missing = wanted.filter(w => !output.includes(w));
152
+ const textOk = missing.length === 0;
124
153
  const ok = codeOk && textOk;
125
154
 
126
155
  if (ok) results.push(`${step.key}✔`);
@@ -129,7 +158,7 @@ for (const step of steps) {
129
158
  results.push(`${step.key}✘`);
130
159
  console.error(`✖ ${step.key} · ${step.name}`);
131
160
  if (!codeOk) console.error(` 期望 exit ${step.expect.code},实际 exit ${code}`);
132
- if (!textOk) console.error(` 输出未包含预期片段:${step.expect.includes}`);
161
+ if (!textOk) console.error(` 输出未包含预期片段:${missing.join("、")}`);
133
162
  console.error(" ───── 该步骤完整输出 ─────");
134
163
  for (const line of output.trimEnd().split("\n")) console.error(` ${line}`);
135
164
  console.error("");
@@ -141,4 +170,4 @@ if (failed > 0) {
141
170
  console.error(`\n✖ ${failed} 步未通过,见上方详情`);
142
171
  process.exit(1);
143
172
  }
144
- console.log("\n✔ A-I 全部通过,mapping/parser 可提交或发版");
173
+ console.log("\n✔ A-L 全部通过,mapping/parser 可提交或发版");
@@ -5,6 +5,11 @@
5
5
  * 1. 字段完整性(必须字段存在且类型正确)
6
6
  * 2. 枚举合法性(type/forms 值域)
7
7
  * 3. 层级一致性(children 引用的 ID 必须存在于同级或同场景映射集)
8
+ * 4. V3.2 三字段结构:anchors(kind/layer 枚举 + target 必填)、
9
+ * preconditions(非空字符串数组)、priority(strategy 枚举)
10
+ *
11
+ * 说明:selector / anchors.target 的「挂载点存在性」需要在**目标项目源码**里查证,
12
+ * 本文件只做映射数据的自洽校验;存在性检查由 index.js 的 audit 子命令完成。
8
13
  *
9
14
  * 校验不通过:抛出带文件定位的错误并 exit 1(阻断构建)。
10
15
  *
@@ -26,6 +31,15 @@ const REQUIRED_FIELDS = {
26
31
  /** 语义 ID 格式:小写字母数字 + 下划线 + 点分层级,最多 4 段 */
27
32
  const ID_PATTERN = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*){1,3}$/;
28
33
 
34
+ /** anchors 承载通道类型枚举(class = 选择器锚点 / css_var = CSS 变量锚点 / attr = 属性锚点) */
35
+ const ANCHOR_KINDS = ["class", "css_var", "attr"];
36
+
37
+ /** 落点层级枚举:host = 宿主盒;inner = 内层文本盒(专治「值对了、位置不对」) */
38
+ const LAYER_ENUM = ["host", "inner"];
39
+
40
+ /** 覆写优先级策略枚举(design-apply 提权依据) */
41
+ const PRIORITY_ENUM = ["scope_qualified", "self_plus_important", "deep_qualified"];
42
+
29
43
  export function validateMappingDataset(dataset) {
30
44
  const errors = [];
31
45
  const warnings = [];
@@ -86,6 +100,61 @@ export function validateMappingDataset(dataset) {
86
100
  errors.push(`[${file}] ${id}.css_vars.${k} 值「${v}」应为 CSS 变量名(-- 开头)`);
87
101
  }
88
102
  }
103
+
104
+ // anchors(V3.2 §3.2):条目可承载的全部通道,含落点层级 layer
105
+ if (entry.anchors !== undefined) {
106
+ if (!Array.isArray(entry.anchors)) {
107
+ errors.push(`[${file}] ${id}.anchors 必须为数组`);
108
+ }
109
+ else {
110
+ let hasClassAnchor = false;
111
+ for (const [i, a] of entry.anchors.entries()) {
112
+ const at = `${id}.anchors[${i}]`;
113
+ if (!a || typeof a !== "object") {
114
+ errors.push(`[${file}] ${at} 必须为对象`);
115
+ continue;
116
+ }
117
+ if (!ANCHOR_KINDS.includes(a.kind))
118
+ errors.push(`[${file}] ${at}.kind「${a.kind}」不在枚举 ${ANCHOR_KINDS.join("/")} 内`);
119
+ if (a.kind === "class")
120
+ hasClassAnchor = true;
121
+ if (a.layer !== undefined && !LAYER_ENUM.includes(a.layer))
122
+ errors.push(`[${file}] ${at}.layer「${a.layer}」不在枚举 ${LAYER_ENUM.join("/")} 内`);
123
+ if (typeof a.target !== "string" || a.target.trim() === "")
124
+ errors.push(`[${file}] ${at}.target 必填且为非空字符串`);
125
+ else if (a.kind === "css_var" && !a.target.trim().startsWith("--"))
126
+ errors.push(`[${file}] ${at}.target「${a.target}」为 css_var 锚点时应为 CSS 变量名(-- 开头)`);
127
+ }
128
+ if (entry.anchors.length > 0 && !hasClassAnchor)
129
+ warnings.push(`[${file}] ${id}.anchors 无 class 锚点,样式类字段可能找不到落点选择器`);
130
+ }
131
+ }
132
+
133
+ // preconditions(V3.2 §3.2):几何 / 取值成立的宿主前提,非空字符串数组
134
+ if (entry.preconditions !== undefined) {
135
+ if (!Array.isArray(entry.preconditions) || entry.preconditions.length === 0) {
136
+ errors.push(`[${file}] ${id}.preconditions 必须为非空字符串数组`);
137
+ }
138
+ else {
139
+ for (const [i, p] of entry.preconditions.entries()) {
140
+ if (typeof p !== "string" || p.trim() === "")
141
+ errors.push(`[${file}] ${id}.preconditions[${i}] 必须为非空字符串`);
142
+ }
143
+ }
144
+ }
145
+
146
+ // priority(V3.2 §4.6.4):覆写优先级策略
147
+ if (entry.priority !== undefined) {
148
+ if (typeof entry.priority !== "object" || entry.priority === null) {
149
+ errors.push(`[${file}] ${id}.priority 必须为对象`);
150
+ }
151
+ else {
152
+ if (!PRIORITY_ENUM.includes(entry.priority.strategy))
153
+ errors.push(`[${file}] ${id}.priority.strategy「${entry.priority.strategy}」不在枚举 ${PRIORITY_ENUM.join("/")} 内`);
154
+ if (entry.priority.reason !== undefined && typeof entry.priority.reason !== "string")
155
+ errors.push(`[${file}] ${id}.priority.reason 必须为字符串`);
156
+ }
157
+ }
89
158
  }
90
159
  }
91
160
  }
@@ -22,10 +22,11 @@ tags: [code-review, quality, checklist, lint, typescript, vue3, security, perfor
22
22
  - 闸门:已执行 code-quality(生成后)
23
23
  - 改动文件:`path/a`、`path/b`、…
24
24
  - 清单:选型 ✅ / 类型 ✅ / 组件 ✅ / API ✅ / 安全 ✅ / 业务 ✅ / 列表→详情 ✅或N/A / 列表布局 ✅或N/A / 风格 ✅
25
+ - 设计落地:通道活性 ✅ / 锚点契约 ✅ / 结构施工 ✅或N/A / 覆写提权 ✅ / 渲染态验收 ✅或N/A(无 oracle 时 N/A)
25
26
  - 硬失败:无 | 已拦截并修正:…
26
27
  - 已修正:…(无则写「无」)
27
28
  - 命令:`pnpm lint:fix` → …;`pnpm type-check` → …
28
- - 待确认:…
29
+ - 待确认:仅限新依赖审批 / 需用户决策的外部动作(无则写「无」);设计变更集落地项一律不得列此(见 design-apply「零缺口交付」)
29
30
  ```
30
31
 
31
32
  ## 硬失败(对照落盘文件;命中即未通过)
@@ -49,6 +50,10 @@ lint / type-check **过了也不能放过**下列项。发现即改代码,禁
49
50
  | `element-plus.scss` / `sidebar.scss` 等样式消费层 | `var(--font-size-*)` 用于 `line-height` / `height` 等非字号属性 | 查 TSB:`classifyToken` + `PROPERTY_FORBIDDEN`;`line-height` → `--line-height` 或 `--el-menu-item-height` |
50
51
  | `element-plus.scss` 内视觉规则(shadow/hover/transition) | 出现非令牌引用的裸 hex / px / rgba 值 | 全部改为 `var()` 引用令牌 |
51
52
  | 视觉规则新增 | `dark.scss` 缺少对应暗色覆盖 | 每条新规则在 `html.dark` 内有对应覆盖 |
53
+ | 设计变更集落地(`design-apply`) | 声明已落地但节点无对应锚点(`anchor.kind` 目标不存在:类未绑 / 变量无消费者 / 属性未挂) | 先补锚点(属「补通道」申报);锚点缺失即为挂载点缺口,禁止用更宽选择器蒙混 |
54
+ | 同上 | `apply_via` 为 `class_name` / `attribute` / `slot_content` / `data_source` 之一,却只写了 CSS 而没改结构/绑定 | 按 `structure_ops` 施工(改模板 / 绑 `class-name` / 改 formatter / 挂 `data-*`),CSS 换不掉数据驱动点位 |
55
+ | 同上 | 全局覆写与组件 `scoped` 同权重生效失败(收口 `visible:false` / opacity / 几何覆盖不生效) | 按 `priority` 声明提权(`scope_qualified` / `self_plus_important` / `deep_qualified`),不得靠「后置加载」赌注入顺序 |
56
+ | 同上 | 词典 `selector` / `css_vars` 指向的选择器在真实 DOM 不存在(死通道 / 死令牌) | 跑 `node parser/index.js audit` 复核;死令牌双删、死通道补消费者挂载点 |
52
57
 
53
58
  ## 选型与硬约束
54
59
 
@@ -125,10 +130,12 @@ lint / type-check **过了也不能放过**下列项。发现即改代码,禁
125
130
 
126
131
  ### 高度模式与结构约束(改动内容区高度 / DOM 结构时强制)
127
132
 
128
- - [ ] 定高页靠 `vts-page-card` / `vts-page-fill` / `data-content-mode="fixed"` 三通道之一,禁止 `calc(100vh - …)` / JS 测顶栏
129
- - [ ] `data-content-mode` 声明在路由组件根节点(`class="main-content"` 处),值仅 `fixed` / `scroll`;与钩子类并存时 fixed 为主
130
- - [ ] `useTableSearch` 结构约束不破坏:`content` 绑定定高容器;`otherRef` 在表格上方;分页与表格同容器且在其下方;`multipleTableRef` 绑定 el-table(详见 hook 源码 JSDoc「结构约束」节)
131
- - [ ] 设计变更集(`admin.content.container` `mode` 字段)翻译为 `data-content-mode` 属性,不得改用其他自定义属性名
133
+ - [ ] 定高页靠 `data-content-mode="fixed"` / `vts-page-card` / `vts-page-fill` 三通道之一,禁止 `calc(100vh - …)` / JS 测顶栏
134
+ - [ ] `data-content-mode` 声明在路由组件根节点(`class="main-content"` 处),值仅 `fixed` / `scroll`;未声明时走自动探测兜底(自动分支带 `:not([data-content-mode])` 守卫,与显式声明互斥)
135
+ - [ ] **填满链完整**:定高页从页面根到表格/画布容器,每层需填满的节点都带 `data-fill`(横向分栏用 `data-fill="row"`);换了视觉容器时属性已平移到新容器,未丢失
136
+ - [ ] **结构层零改动**:`lay-content/index.vue` 的 `.main-content[data-content-mode]` 与 `element-plus.scss` 的 `[data-fill]` 未被设计还原改写(换容器走属性平移,不重写结构层);亦无「为保高度而放弃设计还原」
137
+ - [ ] `useTableSearch` 结构约束不破坏:`content` 绑定定高容器(换容器后仍绑);`otherRef` 在表格上方;分页与表格同容器且在其下方;`multipleTableRef` 绑定 el-table(详见 hook 源码 JSDoc「结构约束」节)
138
+ - [ ] 设计变更集(`admin.content.container` 的 `form` 字段,`fixed` / `scroll` / `auto`)翻译为 `data-content-mode` 属性,不得改用其他自定义属性名
132
139
 
133
140
  ### 设计变更集合规(design-apply 时强制,禁止以 N/A 跳过)
134
141
 
@@ -138,7 +145,19 @@ lint / type-check **过了也不能放过**下列项。发现即改代码,禁
138
145
  - [ ] 两层样式全部落在条目 `style_scope` 作用域内,无全局泄漏、无 `!important` 新增
139
146
  - [ ] 无内联硬编码色值;全部走 CSS 变量(css_vars 层)或 `:deep()` 隔离(custom_style_map 层)
140
147
  - [ ] 新增视觉规则在 `dark.scss` `html.dark` 有对应覆盖
141
- - [ ] 映射缺失按「待补充清单」上报,未当场编造映射;mapping 临时新增后 `node parser/index.js build --dry` 通过
148
+ - [ ] 能力缺口(词典缺 ID / 缺字段枚举 / 缺 form)已当场落地并写临时映射,未进清单;`缺口清单` 只含意图缺口(设计侧未给值 / 值非法)且已注明回流设计侧;mapping 临时新增后 `node parser/index.js build --dry` 通过
149
+ - [ ] 设计层落点文件 `src/styles/design-shell.scss` 存在且经 `index.scss` 末位 `@use` 加载(缺失即自举重建并申报补通道)
150
+ - [ ] **映射通道活性**:本轮用到的词典条目,其 `selector` / `css_vars` 在真实 DOM 可命中、CSS 变量有消费者(已跑 `audit` 或逐条核对,无死通道 / 死令牌)
151
+ - [ ] **锚点契约**:变更集 `anchor` 声明的锚点存在;`layer=inner` 的点位已解析到内层文本盒(`.cell` / 文本节点宿主),未错落在宿主盒上
152
+ - [ ] **结构施工**:`apply_via` 非 `style_override` 的点位已按 `structure_ops` 改结构/绑定(`class-name` / formatter / `data-*` / 渲染分支),未只用 CSS 冒充落地;申报了结构增量施工
153
+ - [ ] **覆写提权**:`priority` 声明的点位覆写已生效(收口 / 几何覆盖实测生效),全局规则未被组件 `scoped` 反压
154
+ - [ ] **模板源码注释中性**:对模板文件的改动中,注释只描述「这段代码做什么」;语义 ID / 协议步骤号 / 缺口·自举·申报等面向 AI 的纪律条款不得写入模板源码注释(一律留在技能侧文档)
155
+
156
+ ### 渲染态验收(变更集含 `oracle` / 效果图有 `data-sid` 时强制)
157
+
158
+ - [ ] 项目同名 `data-sid` 元素已渲染,抽取 computed style 与效果图期望值逐点 diff
159
+ - [ ] diff 结果无未解释缺口;有缺口者已按三类(选择器落空 / 权重被压 / 语境错位)归因并修正,未用 `!important` 掩盖
160
+ - [ ] 验收结果写入最终回复(命中 / 缺口 / 归因 / 修正),不得只报「已完成」
142
161
 
143
162
  ## 代码风格
144
163
 
@@ -34,31 +34,56 @@ useTableSearch(formQuery, searchFn, url)
34
34
  | 长表单 / 详情 / 说明类 | 根节点 `min-height: 100%`(或 `:style="{ minHeight: '100%' }"`);**禁止** `vts-page-fill` / `h-full` / `overflow-hidden`;超出由 `.app-main` 内 `el-scrollbar` 滚动 |
35
35
  | 非 Card 整页填满(地图/流程图/大屏) | 根节点加 `class="vts-page-fill"` + `height: 100%` / `h-full`;**仅此类定高场景** |
36
36
  | Card 内边距 | 一律走 `--vts-card-padding`(桥接 `--card-padding`);换规范 / `--vts-margin` 变化由壳层 flex 链 + Hook 自动跟随 |
37
+ | 设计还原换容器(卡 → 通栏 / 自绘) | 根节点声明 `data-content-mode="fixed"`;新容器及其「体」层挂 `data-fill`;`content` ref 仍绑该容器;视觉(底色/圆角/边框/内边距)全落设计层——**不得因换容器而放弃设计还原,也不得为保高度而拒绝换容器** |
37
38
 
38
- ## 高度模式(data-content-mode 显式通道)
39
+ ## 高度模式(结构通道:`data-content-mode` + `data-fill`)
39
40
 
40
- 壳层(`lay-content`)支持在页面根元素(`class="main-content"` 所在节点,即路由组件根)上声明 `data-content-mode`,**优先级高于 `:has()` 钩子类自动探测**:
41
+ ### 两个通道,一条链
42
+
43
+ | 通道 | 落在哪 | 管什么 |
44
+ |------|--------|--------|
45
+ | `data-content-mode` | 页面根节点(`class="main-content"` 所在节点,即路由组件根) | **滚动归属**:整页滚动还是内容区内部滚动 |
46
+ | `data-fill` | 从根到表格/画布容器之间,每个「需要吃掉父级剩余高度」的层级节点 | **填满剩余高度**(`flex:1 1 auto; min-height:0`);`data-fill="row"` 为横向分栏 |
47
+
48
+ ```scss
49
+ /* 出厂已实现(element-plus.scss),页面只需挂属性 */
50
+ [data-fill] { display:flex; flex:1 1 auto; flex-direction:column; min-height:0; overflow:hidden; }
51
+ [data-fill='row'] { flex-direction: row; }
52
+ ```
53
+
54
+ ### 滚动归属
55
+
56
+ 壳层(`lay-content`)在页面根元素上识别 `data-content-mode`,**优先级高于 `:has()` 钩子类自动探测**:
41
57
 
42
58
  | 声明 | 效果 | 适用 |
43
59
  |------|------|------|
44
- | `data-content-mode="fixed"` | 强制定高页:收回 `el-scrollbar__view` / `.grow` 祖先链,内容区仅内部滚动 | 设计变更集指定定高、或页面结构特殊需绕过 `:has()` 探测 |
45
- | `data-content-mode="scroll"` | 强制滚动页:整页滚动,**防止内部误用钩子类触发定高** | 页面含 `vts-page-card` 但实际想整页滚动等冲突场景 |
46
- | 不声明 | 走 `:has(.vts-page-card \| .vts-page-fill)` 自动探测(默认,向后兼容) | 绝大多数页面 |
60
+ | `data-content-mode="fixed"` | 定高页:收回 `el-scrollbar__view` / `.grow` 祖先链,内容区仅内部滚动 | 设计变更集指定定高、或页面结构特殊需绕过 `:has()` 探测 |
61
+ | `data-content-mode="scroll"` | 滚动页:整页滚动,**防止内部误用钩子类触发定高** | 页面含 `vts-page-card` 但实际想整页滚动等冲突场景 |
62
+ | 不声明 | 走 `:has(.vts-page-card \| .vts-page-fill)` 自动探测(默认,向后兼容存量页) | 绝大多数页面 |
47
63
 
48
64
  规则:
49
65
 
50
66
  1. 新建列表页**不需要**显式声明——`vts-page-card` 钩子类自动触发定高;
51
- 2. 设计变更集(语义 ID `admin.content.container`,`mode: fixed \| scroll`)通过 parser 翻译为本属性;
52
- 3. 禁止同时写钩子类 + `scroll` 声明之外的其他组合产生歧义;`fixed` 与 `vts-page-card` 并存时二者等效(fixed 为主);
67
+ 2. 设计变更集(语义 ID `admin.content.container`,`form: fixed \| scroll`)通过 parser 翻译为本属性;
68
+ 3. 显式声明与自动探测**互斥**(自动分支带 `:not([data-content-mode])` 守卫)——显式 `scroll` 的页面内含 `.vts-page-card` 时,定高分支不得反压;
53
69
  4. `useTableSearch` 几何实测要求 `content` 容器处于定高链上——用 `data-content-mode="fixed"` 替代 `vts-page-card` 时同样成立。
54
70
 
55
- > **壳层原理**(`lay-content`):`.main-content` 用 `padding: var(--vts-margin)`(不用 margin);默认可滚动页 `el-scrollbar__view` / `.grow` 为 `min-height:100%; height:auto`;定高页 `.main-content:has(.vts-page-card|.vts-page-fill)` 为 `height: 100%` 并收回祖先链;`.vts-page-card` 填满。`html/body/#app/.app-wrapper/.main-container/.app-main` 均为 `height:100%; overflow:hidden`。禁止业务页 `100vh` 减顶栏定高。
71
+ ### 结构 / 视觉解耦(设计还原保命口径)
72
+
73
+ 定高链 = 「**谁定高**」(`data-content-mode`,根节点)+「**谁吸收剩余高度**」(`data-fill`,逐层节点),**只认属性、不认类名**。因此:
74
+
75
+ - **视觉容器可以整体替换**:`el-card.vts-page-card` → 通栏 `div` / 自绘容器都行,只要把 `data-fill` 平移到新容器(及其「体」层)上,高度链不断、`tableHeight` 实测照常;
76
+ - **视觉改(底色 / 圆角 / 边框 / 内边距 / 阴影 / 内部排布)落在设计层**(`src/styles/design-shell.scss` 或页面 scoped),**禁止改写**壳层结构层(`lay-content` 的 `.main-content[data-content-mode]`、`element-plus.scss` 的 `[data-fill]`)来换取还原效果;
77
+ - **禁止反向妥协**:「为了保证高度正常所以保留出厂卡片/内边距不改」= 放弃设计还原,同样不接受。正确做法是换容器 + 平移 `data-fill`。
78
+
79
+ > **壳层原理**(`lay-content`):`.main-content` 用 `padding: var(--vts-margin)`(不用 margin);默认可滚动页 `el-scrollbar__view` / `.grow` 为 `min-height:100%; height:auto`;定高页 `.main-content[data-content-mode='fixed']`(或未声明时 `:has(.vts-page-card|.vts-page-fill)` 兜底)为 `height: 100%` 并收回祖先链;`.vts-page-card` 与其 `el-card__body` 是 `data-fill` 的出厂实施例,填满。`html/body/#app/.app-wrapper/.main-container/.app-main` 均为 `height:100%; overflow:hidden`。禁止业务页 `100vh` 减顶栏定高。
80
+
56
81
 
57
82
  ## 列表标准样板(唯一完整示例)
58
83
 
59
84
  | # | 必须 |
60
85
  |---|------|
61
- | L1 | `el-card` + `class="vts-page-card"` + `ref="content"`(禁止挂 `div`) |
86
+ | L1 | `el-card` + `class="vts-page-card"` + `data-fill`(定高链挂载点,换容器时随容器平移)+ `ref="content"`(禁止挂 `div`) |
62
87
  | L2 | 不要写 `height: calc(100vh - …)` / 不要用 JS 测顶栏定高(壳层 + `.vts-page-card` 已填满) |
63
88
  | L3 | 查询区 `class="vts-filter-area"`(含容器感+分隔线,等效旧 `bg-bg_color p-5`);展示区 `ReTableBar` 外包 `.vts-table-area` |
64
89
  | L4 | `ReGrid` `:gap="[0,20]"` + cols;表单项 `w-[100%] !mr-0`;`@should-show-collapse` |
@@ -146,6 +171,7 @@ function handleSizeChange(size: number) {
146
171
  <div>
147
172
  <el-card
148
173
  ref="content"
174
+ data-fill
149
175
  class="vts-page-card"
150
176
  shadow="never"
151
177
  >
@@ -52,6 +52,8 @@ parser match(确定性翻译)→ 施工坐标 → AI 按坐标改代码 →
52
52
 
53
53
  **出厂文件仅在「通道缺失」时才可改动**,且须同时满足:① 补的是能力不是结论(补变量消费者、补 form 枚举、修层叠优先级);② 在交付说明中单列「补通道」条目写明缺失点与修复内容。
54
54
 
55
+ **模板源码注释保持中性(防污染)**:源码注释只写「这段代码做什么」——给后续维护者看。**禁止把面向 AI 的规范写进模板源码**:语义 ID、变更条目、本协议步骤号、缺口 / 自举 / 申报 / 「设计还原禁止改写本段」这类纪律条款,一律留在技能侧(`crud-pages.md` 结构通道 / `code-quality.md` 清单 / 本节),不得写进 `lay-*.vue`、`element-plus.scss`、`sidebar.scss`、`index.scss`、`design-shell.scss` 等模板文件。判据:**技能侧改了内容不需要动模板 ⇒ 正确**;改一条纪律还要去改模板注释 ⇒ 放错层。落地时技能侧只承诺「这四类通道的能力」(`data-content-mode` / `data-fill` / `design-shell.scss` 落点 / 构图钩子),具体契约与纪律全部由技能文档承载。
56
+
55
57
  ## 输入契约(变更集 Schema v1.0)
56
58
 
57
59
  ```jsonc
@@ -68,7 +70,12 @@ parser match(确定性翻译)→ 施工坐标 → AI 按坐标改代码 →
68
70
  "visible": true, // 可选:隐藏用 false(v-if/display,不删代码)
69
71
  "position": "right", // 可选:相对方位(left/right/top/bottom)
70
72
  "form": "card", // 可选:形态切换(见条目 forms 枚举)
71
- "style": { "menu_bg": "钴蓝-9" } // 可选:抽象样式字段(词典 css_vars/custom_style_map key;值 = 语义名或 hex)
73
+ "style": { "menu_bg": "钴蓝-9" }, // 可选:抽象样式字段(词典 css_vars/custom_style_map key;值 = 语义名或 hex)
74
+ "apply_via": "admin.layout.sidebar.menu", // V3.2 可选:本声明引用的锚点来源条目(缺省 = 自身;值 = 语义 ID,不是选择器)
75
+ "layer": { "menu_text": "host" }, // V3.2 可选:落点层级(host 宿主盒 / inner 内层文本盒);可为字符串(整条生效)或「字段 → 层级」对象
76
+ "structure_ops": [ // V3.2 可选:结构施工声明(数据 / 结构驱动点位必须给,只写 CSS = 无效声明)
77
+ { "op": "render_branch", "node": "status_tag", "source": "row.status" }
78
+ ]
72
79
  }
73
80
  ],
74
81
  "pending": [ // 新增 ID 通道:parser 解析 function/parent 供 L3 施工
@@ -77,13 +84,22 @@ parser match(确定性翻译)→ 施工坐标 → AI 按坐标改代码 →
77
84
  }
78
85
  ```
79
86
 
87
+ **V3.2 三个声明维度**(补齐「由哪个通道落地 / 被压时怎么赢 / 需要改结构时落在哪」):
88
+
89
+ | 字段 | 作用 | 值域 |
90
+ |---|---|---|
91
+ | `apply_via` | 指认本声明使用的**锚点来源条目**(在哪条词条的 `anchors` 上落地) | 语义 ID 字符串(2~4 段小写下划线);不是选择器 / 变量名 |
92
+ | `layer` | 指认样式落在**宿主盒**还是**内层文本盒** | `"host"` / `"inner"`;或 `{ "<style字段>": "host"|"inner" }` |
93
+ | `structure_ops[]` | 需要**结构施工**时声明「改哪个语义节点 / 数据源、怎么改」 | `op` ∈ `add_node` / `remove_node` / `move_node` / `render_branch` / `bind_source`;`node` / `source` / `from` / `to` 为语义名 |
94
+
80
95
  硬约束:
81
96
  - 变更集**只含变更点位**——未提到的点位一律不动。
82
97
  - `style` 字段为**抽象视觉语义**(`menu_bg`、`radius`),不得出现 CSS 变量名、选择器、`!important`、技术栈词汇。值口径:官方 5 套主题用语义名(`钴蓝-5`,译表见 `mapping/basic/semantic-values.json`);库外临时主题直接 hex + `notes` 申报派生口径。
83
98
  - `position` 用相对方位词,不用像素坐标。
84
99
  - `visible: false` 只做隐藏,**永不删除代码**。
85
- - `pending` 数组声明的是设计侧新点位,直接归入 L3 处理(不是错误)。**注意:pending 不是施工指令**——L3 施工的内容以 `changes[]` 中同 ID 新条目声明的意图为准;设计侧只登记 pending、未在 changes[] 声明意图的点位,落地时按出厂现状处理并在待补充清单中反馈「该点位设计意图缺失」。
86
- - parser 校验已实现:schema 硬校验、mapping_version 缺失/不匹配拒绝、禁词与 hex 位置校验、语义 ID 命名校验。
100
+ - `apply_via` / `layer` / `structure_ops` 与 `style` 同属**语义声明**,同样不得出现选择器、CSS 变量名、DOM 词、技术栈词:`apply_via` 必须是语义 ID(锚点名)、`layer` 只能取 `host` / `inner`、`structure_ops[].op` 只能取五枚举。
101
+ - `pending` 数组声明的是设计侧新点位,直接归入 L3 处理(不是错误)。**注意:pending 不是施工指令**——L3 施工的内容以 `changes[]` 中同 ID 新条目声明的意图为准;设计侧只登记 pending、未在 changes[] 声明意图的点位,落地时按出厂现状处理并在缺口清单中反馈「该点位设计意图缺失」。
102
+ - parser 校验已实现:schema 硬校验、mapping_version 缺失/不匹配拒绝、禁词与 hex 位置校验、语义 ID 命名校验、`layer` / `apply_via` / `structure_ops` 结构校验。
87
103
 
88
104
  ## 执行流程(按序,禁止跳步)
89
105
 
@@ -107,8 +123,45 @@ node parser/index.js match --batch <changesets-dir>
107
123
  |------|------|------|
108
124
  | **L1 完整命中** | ID 在词典中直接命中 | 按条目施工坐标直接落地(预期 100% 还原) |
109
125
  | **L2 父级命中** | ID 的父模块命中 | 继承父模块样式约定,按子级定位施工(85-90% 还原) |
110
- | **L3 未命中** | 全新点位 | 进「待补充清单」,见第 4 步 |
111
- | **待补充(字段级)** | ID 命中但 `style` 某字段两层映射都查不到 | **跳过不猜**,列入待补充清单反馈 |
126
+ | **L3 未命中** | 词典无此 ID(新增点位) | **当场按第 4 步定制布局落地**并写入 mapping 临时映射;只有设计侧未给完整意图(无值 / 值非法)才进缺口清单 |
127
+ | **字段级未命中** | ID 命中但 `style` 某字段两层映射都查不到 | 值合法且选择器可定位 → **当场生成作用域内自定义规则落设计层文件并申报**;只有「值非法 / 无法定位」才进缺口清单 |
128
+
129
+ **零缺口交付(禁止中途停顿)**:落地一次跑完,**不得因「词典没建这个 ID / 这个字段没枚举 / 这个值要不要确认」而停下来问用户**。缺口只有两类,去向各不相同:
130
+
131
+ | 缺口类型 | 判据 | 处理 |
132
+ |---|---|---|
133
+ | **能力缺口**(词典缺 ID / 缺字段枚举 / 缺 form) | 设计侧意图完整、值合法 | **项目侧当场落地**:按 L3 定制布局施工并写 mapping 临时映射;出厂文件仅在「补通道」时可动,交付说明单列申报。**进清单 = 把可落地项搁置 = 交付失败** |
134
+ | **意图缺口**(设计侧没给值 / 值非法) | 变更集里本就没有这个值 | 才可进「缺口清单」,交付说明写明「回流设计侧补齐」——**不是问用户要不要做** |
135
+
136
+ `缺口清单` 只承载第二类,且必须给出点位 ID + 缺失字段名。
137
+
138
+ **转换计划新增四类输出(V3.2)**:每条 L1/L2 点位除样式指令外,还会输出——
139
+
140
+ | 输出 | 含义 | 施工含义 |
141
+ |---|---|---|
142
+ | `▸ 承载通道 anchors` | 该点位可落地的宿主盒 / 内层文本盒 / CSS 变量锚点 | 落点以这里为准,不要自己猜选择器 |
143
+ | `▸ 承载通道来源 apply_via` | 本声明引用的锚点来源条目 | 落到该条目的锚点上(而非 change.id 自身的) |
144
+ | `▸ 结构施工 structure_ops` | 需要改数据 / 模板结构的事项 | **必须改动数据或模板分支**,禁止只写 CSS |
145
+ | `▸ 前置条件 preconditions` | 值成立依赖的宿主前提 | 施工前必须满足,否则「值对了也不生效」 |
146
+ | `▸ 覆写优先级 priority` | 该点位的提权策略 | 按策略取胜,禁止逐点硬堆 `!important` |
147
+
148
+ **验收基线输出**:转换计划末尾另打印 `=== 验收基线 oracle ===`(效果图路径 + 待比对点位)。有它 → 落地后必须跑第 6 步 `verify`;没有它 → 输出提示,把「补 oracle」回流设计侧(场景还原应给)。
149
+
150
+ ### 第 1 步附加 · 挂载点审计(V3.2,静态查三类隐性缺口)
151
+
152
+ ```bash
153
+ node parser/index.js audit --project <目标项目根目录> [<changeset.json…>]
154
+ ```
155
+
156
+ 不开浏览器即可查出的三类历史高发缺口:
157
+
158
+ | 检查 | 抓什么 | 对应历史缺口 |
159
+ |---|---|---|
160
+ | **挂载点缺失 / 可疑** | `selector` / `anchors.target` 里的类名在项目里**只在样式文件出现、模板没挂**,或**完全找不到** | 列未绑 `class-name` → 金额列右对齐 / 缩略图尺寸永不生效;操作栏规则有定义无引用 |
161
+ | **死通道** | `anchors` 里的 CSS 变量在项目中**无 `var()` 消费者** | 变量有定义无引用,翻译再准也不生效 |
162
+ | **词典无此条目** | 变更集引用了词典未建的 ID | L3 新增点位须当场落地并回写临时映射 |
163
+
164
+ 审计报错 = **能力缺口**:当场按 `structure_ops` 补挂载 / 补消费者,交付说明单列申报;不得以「词典没建」为由跳过。审计通过 ≠ 还原达标——**静态审计查不出「值对了、位置不对」**,那要靠第 6 步渲染态验收。
112
165
 
113
166
  ### 第 2 步 · 结构先行(visible / position / form)
114
167
 
@@ -117,13 +170,54 @@ node parser/index.js match --batch <changesets-dir>
117
170
  - `visible: false` → 按条目 `visible_prop`(如 `platform-config.json` 的 `HideTabs`)或组件 `v-if` 隐藏;无现成通道时 `display: none`,作用域限定在条目 `style_scope` 内。**不删除任何代码行。**
118
171
  - `position` → 优先用 flex 顺序(`order` / DOM 位置调整),禁止绝对定位像素坐标。
119
172
  - `form` → 按条目 `forms` 枚举切换(如 tabs 的 `chrome`/`card` 走 `platform-config.json ShowModel`;内容区滚动模式走 `admin.content.container` 的 form(`fixed`/`scroll`/`auto` → 路由组件根节点 `data-content-mode` 属性,`auto` 为省略即钩子类探测)。枚举外的形态 → 报告,不猜。
173
+ - **结构维度缺失即申报,禁止「只改令牌冒充落地」**:变更集把结构/方位/有无类意图写进 `style`(如指示条只在 `style.menu_active_indicator` 出现、无对应 `form`)时,按「结构意图缺失」处理——交付说明申报「设计侧需补 form + 几何 + 色值」,不得只覆盖颜色令牌交差(历史事故:竖条只换令牌 → 图上有、页面上没有)。
120
174
  - `form` 的落点按上文「落地三落点判据」执行:**有 DOM/配置通道走通道,无通道需新节点走结构增量,纯视觉形态走设计层样式文件**。当某形态在本项目只有样式表达(如菜单指示条方位)时,该 form 允许落到样式层——这与「禁止把内容区高度模式降级成 SCSS」不冲突,判据是**该形态有没有非样式通道**。
121
175
  - `layout_type` → 改 `platform-config.json Layout` 字段,值域 `vertical/horizontal/mix`。**大屏全屏沉浸式等独立页不走此通道**(设计侧不填 `layout_type`,点位走 L3 落地)。
122
176
 
123
- **存在性收口(通用机制,第 2 步随结构处理)**:变更集未声明的**出厂多余元素**(设计效果图上不存在的按钮/图标/入口,如顶栏设置按钮)→ 按各条目口径处理:有条目者在对应点位上以 `visible:false` 收口(代码保留,`v-if`/`display:none`);无条目者列入待补充清单申报「设计侧未声明,按无此元素收口」,同样以隐藏通道落地。禁止既不声明也不收口、让出厂元素顶替设计效果(顶栏设置按钮漏收口已实证:效果图无此按钮,落地后仍显示)。反向同理:**设计声明了而出厂没有的元素**(如欢迎区问候语)必须按条目 `$note`/设计规范补齐结构(结构通道豁免底线二),缺失 = 未交付。
177
+ **存在性收口(通用机制,第 2 步随结构处理)**:变更集未声明的**出厂多余元素**(设计效果图上不存在的按钮/图标/入口,如顶栏设置按钮)→ 按各条目口径处理:有条目者在对应点位上以 `visible:false` 收口(代码保留,`v-if`/`display:none`);无条目者列入缺口清单申报「设计侧未声明,按无此元素收口」,同样以隐藏通道落地。禁止既不声明也不收口、让出厂元素顶替设计效果(顶栏设置按钮漏收口已实证:效果图无此按钮,落地后仍显示)。反向同理:**设计声明了而出厂没有的元素**(如欢迎区问候语)必须按条目 `$note`/设计规范补齐结构(结构通道豁免底线二),缺失 = 未交付。
124
178
 
125
179
  **结构/样式图层分工**:`form`(高度模式、形态枚举)是**结构意图**,必须落到 DOM 属性/组件开关/配置字段(如 `data-content-mode`、`ShowModel`);**禁止把结构意图降级翻译成 SCSS 变量写进样式文件**(form=scroll 只写 `--vts-margin` = 未施工,已实证)。样式文件(如 design-shell.scss)只承载 css_vars/custom_style_map 翻译产物;壳层通用文件禁止代打页面级结构属性(lay-content 硬编码 `data-content-mode="scroll"` 曾压掉 `:has()` 自动定高探测,useTableSearch 高度链断裂致列表区整体崩坏——已回退,禁止复活)。
126
180
 
181
+ **结构链不可侵(高度链保命红线)**:内容区高度链是**结构层**,已与视觉解耦,两侧各有边界——
182
+
183
+ | 结构层(禁止改写) | 视觉层(设计自由落点) |
184
+ |---|---|
185
+ | `data-content-mode`(滚动归属,**页面根节点属性**) | 页面容器形态(卡 / 通栏 / 自绘)、底色、圆角、边框、内边距、阴影 |
186
+ | `data-fill`(填满剩余高度,**逐层节点属性**) | 容器内部排布(标题区 / 筛选区 / 表格区的位置与间距) |
187
+
188
+ - 设计要求换掉出厂卡片容器(去卡片、改通栏、改内部排布)→ **照做,不打折**:页面根声明 `data-content-mode`,把 `data-fill` **平移**到新容器及其「体」层,视觉全落设计层文件。此路径下 `tableHeight` 几何实测照常成立。
189
+ - **双向禁止**:① 以「保高度」为由拒绝改容器/内边距/去卡片(= 放弃设计还原);② 为凑还原效果改写壳层结构层规则、或在视觉层重写/压掉 `[data-fill]` / `.main-content[data-content-mode]`(= 高度链断裂,返工项)。
190
+
191
+ #### 第 2 步附加 · 结构施工(structure_ops)与优先级策略(V3.2)
192
+
193
+ **A. 结构施工 `structure_ops`(数据 / 结构驱动点位)**——有些点位**换不掉也写不出来**,因为呈现由数据源或模板结构决定。变更集给出 `structure_ops` 时必须照做,**只写 CSS = 无效声明**:
194
+
195
+ | `op` | 含义 | 施工动作 |
196
+ |---|---|---|
197
+ | `add_node` | 新增语义节点 | 在模板按 `form` 保形插入(不删原代码),落点见「落地三落点判据」第 2 条 |
198
+ | `remove_node` | 移除语义节点 | 按 `visible:false` 收口(隐藏不删);确需物理移除时先确认无业务耦合 |
199
+ | `move_node` | 调整节点位置 | 优先 flex `order` / DOM 位置调整,禁止绝对定位像素坐标 |
200
+ | `render_branch` | 补渲染分支 | 例:状态列 `formatter` 返回纯文本 → 改为 `el-tag` 分支(`source` 指出数据来源) |
201
+ | `bind_source` | 绑定数据源 | 例:金额列补 `class-name` / `align`、缩略图列补 `class-name` —— 属数据侧结论,**改 CSS 不算** |
202
+
203
+ **判据一句话**:**这个点位的呈现,改 CSS 能改吗?** 改不了(图标在数据里、Tag 在模板分支里、类名没绑)→ 必须走 `structure_ops`;能改(颜色 / 圆角 / 间距)→ 走第 3 步样式落地。
204
+
205
+ **B. 落点层级 `layer`(专治「值对了、位置不对」)**——同一点位的样式可能落在**宿主盒**或**内层文本盒**,两者不通用:
206
+
207
+ - 变更集给了 `layer` → 严格按 `layer` 从条目 `anchors` 取对应选择器(parser 已在转换计划里给出 `@ … · host/inner 层`)。
208
+ - 变更集**没给** `layer`、但条目 `preconditions` 指出必须落内层(如 EP 表头 `white-space`)→ 按 `preconditions` 落 inner 层,并在交付说明注明「设计侧未给 layer,按条目前置条件处理」。
209
+ - 常规分工:高度 / 背景 / padding 落 host;文本换行、省略、对齐落 inner。
210
+
211
+ **C. 覆写优先级 `priority`(禁止逐点硬堆 `!important`)**——条目 `priority.strategy` 给出本点位该怎么赢:
212
+
213
+ | 策略 | 施工口径 |
214
+ |---|---|
215
+ | `scope_qualified` | 用条目 `style_scope` 作用域限定即可覆盖 |
216
+ | `self_plus_important` | 目标处于**行内 style**(`:header-cell-style` 等)竞争下 → 先移除页面行内样式;无法移除时,作用域内提权并在交付说明申报 |
217
+ | `deep_qualified` | 目标与**组件 scoped 规则同权重** → 提高选择器权重或走深度选择器显式取胜(**不能只靠「后置加载」**) |
218
+
219
+ > **为什么必须显式处理**:同权重时胜负由样式注入顺序决定、不可控;靠「加 `!important` / 加宽选择器」逐点硬压,会把互不相干的点位耦合起来、副作用无人发现 → **累积成漂移而不是收敛**(这正是「越改越乱」的机理)。
220
+
127
221
  ### 第 3 步 · 样式落地(两层转换 + 语义值译表)
128
222
 
129
223
  对每个 `style` 字段,按 parser 转换指令执行(parser 已把语义值解析为精确值,如 `钴蓝-5` → `#245BFF`)。**冲突裁决规则(免问):凡变更集声明与项目出厂现状/模板既有口径冲突,一律按变更集执行——变更集是唯一施工依据,不反问、不逐次确认。**
@@ -141,8 +235,8 @@ node parser/index.js match --batch <changesets-dir>
141
235
  .navbar :deep(.el-dropdown-link img) { width: 36px; height: 36px; }
142
236
  ```
143
237
  `sub_selectors` 有定义的字段(如 `avatar_*` → img、`name_*` → p)按对应选择器路由。
144
- 3. **两层都未命中** → 不写任何样式,进待补充清单。
145
- 4. **值未命中译表且非 hex**(parser 会提示)→ 施工前向用户确认精确值,不猜。
238
+ 3. **两层都未命中** → 值合法且选择器可定位时,当场生成作用域内自定义规则落设计层文件并在交付说明申报;仅「值非法 / 无法定位」才进缺口清单。
239
+ 4. **值未命中译表且非 hex**(parser 会提示)→ 能按数值字面量 / hex / 上下文判定的当场落地并申报;仅完全无法判定时进缺口清单、回流设计侧补齐(**不向用户反问**)。
146
240
 
147
241
  全局令牌(`global` 字段,**整套主题切换优先通道**)→ `design-tokens.scss` 对应变量(`basic/` 词典翻译,语义值走 `mapping/basic/semantic-values.json` 译表),主色走 `setEpThemeColor()` 链路。**壳层模块底色注意**:顶栏底色不走 global(无独立令牌),须以 `{"id": "admin.layout.header", "style": {"bg": "<值>"}}` 场景条目声明(scoped 覆盖 .navbar 底色);侧栏底色两者皆可——global 走 `sidebar_bg`(--sidebar-bg 令牌链),场景条目走 `admin.layout.sidebar` 的 `bg`(--vts-theme-menu-bg)。
148
242
 
@@ -156,10 +250,11 @@ node parser/index.js match --batch <changesets-dir>
156
250
  - 本文件内新增的 CSS 变量必须**定义 + 引用成对出现**(出厂已有消费者也算),避免死通道;
157
251
  - 出厂元素需关闭时(存在性收口)也在这里做:令牌置 `transparent` 或 `display:none`,**绝不删出厂代码行**;
158
252
  - 出厂 `--vts-theme-menu-active-before` 默认 `transparent` 且无消费者 → 声明 `form=indicator_*` 时必须在此文件内**同时**落地「关胶囊令牌 `menu_active_bg: transparent`」与「新建 `::after` 消费者」,缺一即未施工成功。
253
+ - **自举(项目被还原到出厂时必做,幂等)**:文件不存在 → 新建(仅保留文件头纪律注释,无任何设计结论)+ 在 `src/styles/index.scss` **末位**补 `@use 'design-shell';`(必须末位,靠后置压出厂)。此动作属「补通道」,交付说明单列申报。**文件缺失时径直把设计规则写进 `element-plus.scss` / `sidebar.scss` / `theme.scss` = 污染出厂文件,禁止**(这也是「设计还原度为零」的常见成因:规则无处可落)。
159
254
 
160
255
  ### 第 4 步 · L3 新模块处理(分级布局)
161
256
 
162
- - 新模块按「定制布局」三步走:① 基于全局规范生成结构(继承命名/作用域/组件规范)② 原子层字段(色彩/圆角/组件级)从 `basic/` + `component/` 词典借力 ③ 场景专属点位生成后**立即写入 mapping/ 对应场景域**(临时映射),累计复用 ≥3 次建议升级标准(由人工评审)。
257
+ - **L3 是施工指令,不是待办**:词典未命中的点位一律当场落地、交付说明申报「补映射」,不留待确认。新模块按「定制布局」三步走:① 基于全局规范生成结构(继承命名/作用域/组件规范)② 原子层字段(色彩/圆角/组件级)从 `basic/` + `component/` 词典借力 ③ 场景专属点位生成后**立即写入 mapping/ 对应场景域**(临时映射),累计复用 ≥3 次建议升级标准(由人工评审)。
163
258
  - 生成的新页面必须带构图钩子(`vts-page-card` / `vts-filter-area` / `vts-table-area` 等),保证后续变更集可命中。
164
259
 
165
260
  ### 第 5 步 · 合规闸门(变更集专属检查)
@@ -173,7 +268,7 @@ node parser/index.js match --batch <changesets-dir>
173
268
  - [ ] 未改动业务逻辑层(API/状态/事件——**底线二**;样式与结构通道内的 DOM 调整除外)
174
269
  - [ ] 两层样式全部落在 `style_scope` 作用域内,无全局泄漏
175
270
  - [ ] **类名冲突检查(AI 施工硬性步骤)**:新增 class 之前先在目标文件与同作用域全局样式中检索同名类——已存在时禁止直接追加重复定义,按三选一处理并在交付说明申报:① 语义相同 → **合并**进既有规则;② 语义不同 → 改用带点位前缀的新类名(如 `vts-<语义id末段>`);③ 确需覆盖 → 走作用域内更高优先级(不写 `!important`)。重复类名(尤其跨 `<style>` 块的 scoped 样式重复定义)会触发编辑器/构建报错,**本项不通过不得交付**
176
- - [ ] **出厂中性检查(架构红线,不通过必须回退重做)**:diff 中不得出现「为表达本次设计而改写出厂文件」的产物——典型症状:把指示条方位/问候语 DOM/某个具体尺寸直接写进 `sidebar.scss`、`element-plus.scss`、`lay-navbar/index.vue`、`lay-content/index.vue` 等出厂载体;在出厂 `<component>` 上硬编码 `data-content-mode`/样式钩子。出厂文件若被改动,只能是「补通道」(补变量消费者 / 补 form 枚举 / 修层叠优先级,如已修的 `:has()` 与显式模式优先级反转),且必须在交付说明单列「补通道」条目写明缺失点;非此情形一律回退,改由 `design-shell.scss` + 变更集驱动
271
+ - [ ] **出厂中性检查(架构红线,不通过必须回退重做)**:diff 中不得出现「为表达本次设计而改写出厂文件」的产物——典型症状:把指示条方位/问候语 DOM/某个具体尺寸直接写进 `sidebar.scss`、`element-plus.scss`、`lay-navbar/index.vue`、`lay-content/index.vue` 等出厂载体;在出厂 `<component>` 上硬编码 `data-content-mode`/样式钩子。出厂文件若被改动,只能是「补通道」(补变量消费者 / 补 form 枚举 / 修层叠优先级,如已修的 `:has()` 与显式模式优先级反转),且必须在交付说明单列「补通道」条目写明缺失点;非此情形一律回退,改由 `design-shell.scss` + 变更集驱动。**模板源码注释保持中性**:diff 中不得出现「语义 ID / 本协议步骤号 / 面向 AI 的纪律条款」写进模板源码注释(如 `lay-content/index.vue`、`element-plus.scss`、`design-shell.scss` 的文件头与段落注释);这类内容只允许存在于技能侧文档
177
272
  - [ ] **文本溢出回归检查**:改动侧栏菜单/顶栏/页签等含文字的模块后,必须确认「长文本显示」未被破坏(菜单项名称完整可见,必要时保留 ellipsis 溢出策略而不是隐藏/截断);宽度相关字段(width/min-width/padding)改动后逐项目视验证
178
273
  - [ ] **形态与存在性目视验证(历史漏还原高发区,声明即必须验证,没做视同未交付)**:变更集声明了以下意图时,逐项在页面上确认真实生效——
179
274
  - 语言切换 form=inline_switch → 顶栏出现「中|EN」并排文字(不再是纯图标)
@@ -181,19 +276,55 @@ node parser/index.js match --batch <changesets-dir>
181
276
  - 侧栏 `menu_active_*` / `form=indicator_*` → **按变更集声明的 form 逐项核对**(indicator_left → 左侧竖条;indicator_right → 右侧竖条;indicator_underline → 底部横线;pill → 胶囊底色;none → 无指示):方位/粗细/长度/圆角与变更集 style 字段一致,且出厂胶囊已关闭(`--vts-theme-menu-active-bg: transparent`)、`--vts-theme-menu-active-before` 消费者真实建成。**禁止用「左侧 3px」这类历史/词典结论替代当次声明**——设计换了就必须是新的方位与数值
182
277
  - 顶栏未声明点位(设置按钮等)→ 已收口不显示;用户区 → 按 `form` 核对组合形态(`avatar_name` / `greeting_avatar_name` / `greeting_name` / `avatar_only` / `name_only`),其中 greeting 为结构增量节点(出厂无此 DOM,需 AI 按 form 插入后再验)
183
278
  - 页签图标 → 按 `form` 核对(item 的 `text_only`/`with_icon`,home_tab 的 `icon_only`/`text`),出处是路由 `meta.icon` 数据或本次隐藏规则,不是源码常量
184
- - 内容区 form=scroll/fixed → `data-content-mode` 属性落在页面根节点(不是样式文件),列表页 `:has(.vts-page-card)` 自动定高未被压掉——**列表页必验:表头/行/分页在视口内、表格高度正常(无拉伸/塌陷/双滚动条)**
279
+ - 内容区 form=scroll/fixed → `data-content-mode` 属性落在**页面根节点**(不是样式文件、不是壳层组件),列表页自动定高未被压掉;填满链逐层带 `data-fill`(详见下方「高度链存活检查」)
185
280
  - 操作栏 form=with_tools/left_batch → 刷新/密度/列设置三件套齐全;批量删除红底/导出白底/未选中置灰
186
281
  - 操作列 form=text_link → **业务列定义的 icon 字段已移除**(纯文字无图标,只改 CSS 保留图标 = 未还原);按钮单行不换行(列宽 ≥160px + nowrap);删除项红色;≥3 个折叠「更多」带箭头
187
282
  - tabs form → 页签形态切换生效(chrome/card);激活态表现(有无下划线 / 是否仅文字变色 / 字重)按当次变更集 style 字段核对,**不以历史设计稿口径为准**
283
+ - [ ] **高度链存活检查(结构层不被设计改动压垮,也不被"保高度"绑架)**:定高页(`form=fixed` 或列表页自动定高)还原后逐项确认——
284
+ - `data-content-mode` 落在**页面根节点**;从页面根到表格/画布容器**每层需填满的节点都带 `data-fill`**(换了视觉容器时属性已平移;横向分栏用 `data-fill="row"`);
285
+ - `content` ref 仍绑在定高容器上(换容器后未丢),`otherRef` 仍在表格上方、分页仍在表格正下方;
286
+ - 表头 / 行 / 分页都在视口内,仅表格体内滚动,无外层滚动条、无拉伸 / 塌陷 / 双滚动条,`tableHeight > 0`;
287
+ - **结构层 diff 为空**:`lay-content/index.vue` 的 `.main-content[data-content-mode]` 与 `element-plus.scss` 的 `[data-fill]` 未被改动;同时确认没有「为保高度而放弃设计还原」(出厂卡片 / 内边距 / 内部排布原样未动而变更集声明了改动)——两个方向任一命中即返工
188
288
  - [ ] **映射通道活性检查(杂必查)**:样式落地引用的每个 CSS 变量必须在项目样式中**存在消费者**(`变量名` 有 `var(--xxx)` 引用点);只定义不消费的变量 = 通道死路,翻译再准也不生效(menu_active_indicator 曾因 --vts-theme-menu-active-before 无消费者而「映射了却没效果」)。施工时 grep 核对,无消费者时改走消费者存在的作用域或补挂载点并申报。
189
289
  - [ ] **dark.scss 暗色同步**:新增视觉规则在 `html.dark` 有对应覆盖
190
- - [ ] 待补充清单已如实反馈(含点位 ID + 缺失字段名),未擅自补映射
290
+ - [ ] **缺口清单只含意图缺口**:清单每一项都必须是「设计侧未给值 / 值非法」且已注明「回流设计侧补齐」;能力缺口(词典缺 ID / 缺字段枚举 / 缺 form)不得出现在清单里——出现即把可落地项搁置
291
+ - [ ] **台账逐行兑现对账(对账闸门)**:把设计侧交付的提取台账逐行过一遍,每行必须有落地结论——已落地(写明落在哪个文件/选择器)或已入缺口清单(写明缺失字段)。存在「台账有行、落地无结论」的行 = 交付不完整,与红线同级
292
+ - [ ] **零搁置交付**:最终回复不含「待确认」类停顿项;交付说明的「补映射 / 补通道 / 缺口回流」三段齐全(无内容写「无」)
191
293
  - [ ] `node parser/index.js build --dry` 通过(mapping 若有临时新增)
192
294
 
295
+ ### 第 6 步 · 渲染态验收(变更集含 `oracle` 时强制 · 闭环回路)
296
+
297
+ 第 5 步是静态自查,查不出「值对了、位置不对 / 写了没生效」。**只要变更集带 `oracle`(效果图基线),落地后必须做机器逐点比对**——这是「1:1 还原」能被证明、且不再漂移的唯一手段。
298
+
299
+ **链路(抽取由浏览器完成,diff 由 parser 完成)**:
300
+
301
+ 1. **抽取期望值**:在浏览器打开效果图(`oracle.mockup`,元素已带 `data-sid`),对 `oracle.sids` 中每个 sid 取其代表性元素的 computed style,导出为 `{ "<sid>": { "<css属性>": "<值>" } }` → `oracle.json`。
302
+ 2. **抽取实测值**:在浏览器打开已落地页面,对同名 `data-sid` 元素做同样抽取 → `actual.json`。
303
+ > 抽取脚本可用 Playwright / 浏览器控制台执行:`document.querySelectorAll('[data-sid]')` 遍历,读取 `getComputedStyle(el)` 的白名单属性(padding / font-size / font-weight / text-align / color / background-color / border-radius / gap 等)。两个页面的属性白名单必须一致,否则出现假缺口。
304
+ 3. **逐点 diff**:
305
+ ```bash
306
+ node parser/index.js verify --oracle oracle.json --actual actual.json
307
+ # 可选 --sids admin.content.table.header_cell,admin.layout.tabs.item 只验部分点位
308
+ ```
309
+ 退出码:存在缺口 → `1`。
310
+
311
+ **归因与处置(三类,禁止用 `!important` 掩盖)**:
312
+
313
+ | 归因 | 触发 | 处置 |
314
+ |---|---|---|
315
+ | **选择器落空** | oracle 有点位、actual 无(或某属性取不到) | 补挂载点:按 `structure_ops` 挂类 / 补 `data-sid` / 确认元素已渲染(属能力缺口,交付说明申报) |
316
+ | **权重被压** | 颜色 / 字重 / 透明度 / 阴影类属性值不符 | 按词典 `priority` 声明的策略提权(`scope_qualified` / `self_plus_important` / `deep_qualified`) |
317
+ | **语境错位** | 几何 / 盒模型属性(padding / 尺寸 / 对齐 / 行高)值不符 | 校准 `layer`(`host` vs `inner`)——值对了落错层 |
318
+
319
+ - 验收结果写入最终回复(命中 N / 缺口 M / 归因分布 / 修正)与 `code-quality` 结果块;**不得只报「已完成」**。
320
+ - 变更集无 `oracle` 时输出 `N/A`(无基线),并在交付说明注明「无验收基线」——这是设计侧应补的项,回流设计侧。
321
+ - 静态审计(audit)与渲染态验收(verify)**互补**:audit 查「通道是否存在」,verify 查「渲染结果是否一致」,两者都过才算闭环。
322
+
193
323
  ## 词典维护纪律
194
324
 
195
325
  - mapping/ 是唯一事实源:施工前 `parser query <id>` 精读条目,**不凭记忆施工**。
196
- - 映射缺失 = 上报待补充清单,**宁可不做,不做错**——禁止 AI 当场编造映射。
326
+ - **禁止编造的是「既有条目的映射」**(改既有 selector / css_vars / 变量指向 = 静默篡改,绝对禁止)。**词典缺 ID / 缺字段枚举不属编造**——那是新点位,按 L3 当场落地并写**新增的**临时映射条目,交付说明申报,等人工评审转正。
327
+ - 「宁可不做,不做错」只适用于**设计侧未给值 / 值非法**的意图缺口;用它搁置能力缺口 = 交付失败。
197
328
  - 临时映射转正须人工评审;禁止静默修改既有映射条目的 selector/css_vars。
198
329
 
199
330
  ## 环境备注
@@ -203,7 +334,7 @@ node parser/index.js match --batch <changesets-dir>
203
334
  ## 端到端回归(改 parser/mapping 后必跑)
204
335
 
205
336
  ```bash
206
- node parser/regression.js # 一键跑完 A-I 九步(自动断言退出码与关键输出,任一失败 exit 1 并回显详情)
337
+ node parser/regression.js # 一键跑完 A-L 十二步(自动断言退出码与关键输出,任一失败 exit 1 并回显详情)
207
338
  npm run regression # 等价写法
208
339
  ```
209
340
 
@@ -219,9 +350,12 @@ node parser/index.js match parser/fixtures/changeset-neg3.json # F. 负例3
219
350
  node parser/index.js match parser/fixtures/changeset-legacy.json # G. 旧协议:exit 0 + layout 键/schema 历史值两条告警
220
351
  node parser/index.js match parser/fixtures/changeset-fullpage-restore.json # H. 整页场景还原样例:exit 0;壳层+内容区全模块 L1 命中;数值字面量(40px 等)透传无 ⚠
221
352
  node parser/index.js build --dry # I. 产物链自检
353
+ node parser/index.js match parser/fixtures/changeset-v32-fields.json # J. V3.2 声明维:exit 0;输出「结构施工 structure_ops」与 layer / apply_via
354
+ node parser/index.js audit --project parser/fixtures/fake-project parser/fixtures/changeset-v32-fields.json # K. 静态审计:夹具项目存在挂载点缺失/死通道 → exit 1
355
+ node parser/index.js verify --oracle parser/fixtures/verify/oracle.json --actual parser/fixtures/verify/actual.json # L. 渲染态验收:夹具含三类故意缺口 → exit 1;归因计数 选择器落空1/权重被压1/语境错位2
222
356
  ```
223
357
 
224
- 通过标准:regression.js 输出 `A✔ B✔ … I✔` 且 exit 0;分步跑时各步符合注释预期。夹具含义见各文件 `$comment`。
358
+ 通过标准:regression.js 输出 `A✔ B✔ … L✔` 且 exit 0;分步跑时各步符合注释预期。夹具含义见各文件 `$comment`。
225
359
 
226
360
  ## 配套工具(作者/维护侧)
227
361