@qilitt-mickey/vue3-temp-skill 1.1.46 → 1.1.48
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.
- package/mapping/component/overlay.json +16 -0
- package/mapping/scenes/admin/content.json +61 -2
- package/mapping/scenes/admin/layout.json +6 -2
- package/package.json +1 -1
- package/parser/index.js +161 -12
- package/references/design-apply.md +50 -4
- package/references/cobalt-admin-restore.changeset.json +0 -468
|
@@ -58,6 +58,22 @@
|
|
|
58
58
|
"hover_color": "--el-pagination-hover-color"
|
|
59
59
|
},
|
|
60
60
|
"custom_style_map": {}
|
|
61
|
+
},
|
|
62
|
+
"component.tooltip.base": {
|
|
63
|
+
"type": "global_component",
|
|
64
|
+
"component": "ElTooltip(原生)/ v-tippy(指令)",
|
|
65
|
+
"style_scope": ":root",
|
|
66
|
+
"css_vars": {
|
|
67
|
+
"bg_color": "--el-tooltip-bg-color",
|
|
68
|
+
"text_color": "--el-tooltip-color"
|
|
69
|
+
},
|
|
70
|
+
"custom_style_map": {
|
|
71
|
+
"radius": "border-radius",
|
|
72
|
+
"padding": "padding",
|
|
73
|
+
"font_size": "font-size",
|
|
74
|
+
"max_width": "max-width"
|
|
75
|
+
},
|
|
76
|
+
"$note": "全局提示气泡。项目内提示有**两条来路**——Element Plus 原生 ElTooltip(继承 --el-tooltip-* 令牌,如 ReAvatars 的 el-tooltip)与 v-tippy 指令(ReTableBar / ReCollapsible / ReAnchor / lay-tag 等处使用,走 tippy 主题,**不自动继承 --el-* 令牌**)。本条目面向 ElTooltip;若设计要求覆盖 v-tippy 气泡,须先确认 tippy 主题变量与本节令牌的对应关系,未映射时按「补通道」补主题变量并在交付说明单列申报(**不得假设自动生效**)。本条目为补建通道——此前登记表标 pending。"
|
|
61
77
|
}
|
|
62
78
|
}
|
|
63
79
|
}
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"admin.content.table.operation_bar": {
|
|
75
75
|
"type": "element",
|
|
76
76
|
"selector": ".vts-action-bar",
|
|
77
|
-
"file": "
|
|
77
|
+
"file": "条件通道:ReTableBar 内部工具栏行(标题/按钮/工具三件套所在行)是本条目的唯一合法挂载点;该行出厂**无语义类**,selector 命中前须按 structure_ops 给该行补挂 .vts-action-bar(属补通道,交付说明单列申报)。禁止挂到 ReTableBar 根节点(见 preconditions)。右侧工具三件套源码实证:刷新 svg-icon + el-dropdown 密度(宽松/默认/紧凑)+ el-popover 列设置",
|
|
78
78
|
"style_scope": ".vts-page-card",
|
|
79
79
|
"forms": {
|
|
80
80
|
"with_tools": "工具三件套(刷新/密度/列设置)齐全——设计规范 list-page §4 要求三件套缺一不可(ReTableBar 出厂已具备,此为默认形态)",
|
|
@@ -120,8 +120,10 @@
|
|
|
120
120
|
"css_vars": {
|
|
121
121
|
"link_color": "--el-color-primary",
|
|
122
122
|
"danger_color": "--el-color-danger",
|
|
123
|
-
"divider_color": "--
|
|
123
|
+
"divider_color": "--re-op-divider-color",
|
|
124
|
+
"divider_gap": "--re-op-divider-gap"
|
|
124
125
|
},
|
|
126
|
+
"$note": "操作列分割线为**条件通道**:出厂组件当前**没有**分割线元素——声明 divider_color / divider_gap 时,必须先按 structure_ops 在 ReTableOperate 模板按钮间 add_node `<span class=\"re-table-operate__divider\">`(中性元素,几何与颜色全走令牌:width/height/gap/color 分别挂 --re-op-divider-width/height/gap/color,出厂缺省 0 不可见),并同步给 resolveOperateColumnWidth 公式补分割线占位((limit-1)×(width+2×gap)),否则最后按钮被裁——元素与公式缺一即申报补通道失败。列宽由组件公式推算,业务页禁止写死 width——列宽属业务层,设计侧不声明列宽(操作列除外)。",
|
|
125
127
|
"custom_style_map": {
|
|
126
128
|
"align": "text-align",
|
|
127
129
|
"min_width": "min-width",
|
|
@@ -181,6 +183,7 @@
|
|
|
181
183
|
],
|
|
182
184
|
"preconditions": [
|
|
183
185
|
"Element Plus 表头是「宿主 th.el-table__cell + 内层 .cell 文本盒」两层结构;EP 出厂给内层 .cell 写了 white-space: normal,因此 nowrap / text-overflow 这类文本控制必须落在 inner 层,写在宿主 th 上会被内层覆盖(这是「表头竖排换行」的根因)。高度 / padding / 背景落在 host 层。",
|
|
186
|
+
"几何语境随值迁移:EP 内层 .cell 出厂自带 padding 0 12px,设计的 16px 落到 host 后必须同步把内层 .cell 的 padding 归零,否则实际内边距 16+12=28px/侧 → 列宽被挤、表头标题被省略号截断(类…/包…/操…),视觉不适配常规。数据单元格(td)同理。",
|
|
184
187
|
"表头单元格可能被业务页用 :header-cell-style 注入行内样式,行内样式优先级高于一切类规则;使用本条目时须确认页面未在列上写死行内表头样式,若有则在页面侧移除、改由本条目承载。"
|
|
185
188
|
],
|
|
186
189
|
"priority": {
|
|
@@ -305,6 +308,62 @@
|
|
|
305
308
|
},
|
|
306
309
|
"$note": "设计意图(§11.108-11.109):列宽 72px 居中,缩略盒 40×40 圆角 6px。落地手段:列 className=col-thumb + el-image 40×40"
|
|
307
310
|
},
|
|
311
|
+
"admin.content.table.selection_column": {
|
|
312
|
+
"type": "element",
|
|
313
|
+
"selector": ".vts-table-area .el-table__cell.el-table-column--selection",
|
|
314
|
+
"file": "业务 views(el-table-column type=\"selection\";Element Plus 给选择列表头与单元格自动加 el-table-column--selection 类,无需业务自定义类名)",
|
|
315
|
+
"style_scope": ".vts-page-card",
|
|
316
|
+
"css_vars": {
|
|
317
|
+
"checked_bg": "--el-checkbox-checked-bg-color",
|
|
318
|
+
"checked_border": "--el-checkbox-checked-input-border-color",
|
|
319
|
+
"hover_border": "--el-checkbox-input-border-color-hover"
|
|
320
|
+
},
|
|
321
|
+
"custom_style_map": {
|
|
322
|
+
"text_align": "text-align",
|
|
323
|
+
"padding": "padding"
|
|
324
|
+
},
|
|
325
|
+
"anchors": [
|
|
326
|
+
{ "kind": "class", "layer": "host", "target": ".vts-table-area .el-table__cell.el-table-column--selection" },
|
|
327
|
+
{ "kind": "class", "layer": "inner", "target": ".vts-table-area .el-table__cell.el-table-column--selection > .cell" },
|
|
328
|
+
{ "kind": "css_var", "target": "--el-checkbox-checked-bg-color" }
|
|
329
|
+
],
|
|
330
|
+
"preconditions": [
|
|
331
|
+
"选择列由 el-table-column type=\"selection\" 渲染,Element Plus 自动在表头与单元格上加 el-table-column--selection 类——列未声明 type=selection 时本条目永不命中(项目源码实证:views/table/base/index.vue 已用 <el-table-column type=\"selection\" reserve-selection />)。",
|
|
332
|
+
"列宽与其他业务列同属**业务层**(设计侧不声明列宽);本条目只承载对齐与勾选态配色。"
|
|
333
|
+
],
|
|
334
|
+
"priority": {
|
|
335
|
+
"strategy": "scope_qualified",
|
|
336
|
+
"reason": "选择列无行内样式竞争,作用域限定到 .vts-page-card 即可覆盖。"
|
|
337
|
+
},
|
|
338
|
+
"$note": "设计意图(list-page §11.44 选择列):窄列、居中、勾选态用主题主色。本条目为**补建通道**——此前登记表标 pending(项目侧无承载点位),选择列样式无处可落;勾选色走 EP 原生 --el-checkbox-* 令牌,随主题切换。"
|
|
339
|
+
},
|
|
340
|
+
"admin.content.table.row_density": {
|
|
341
|
+
"type": "element",
|
|
342
|
+
"selector": ".vts-table-area .el-table .el-table__cell",
|
|
343
|
+
"file": "src/components/ReTableBar/src/bar.vue(密度下拉:宽松/默认/紧凑 → el-table size = large/default/small)+ src/styles/element-plus.scss(单元格 padding 承载行高)",
|
|
344
|
+
"style_scope": ".vts-page-card",
|
|
345
|
+
"forms": {
|
|
346
|
+
"density": ["loose", "default", "compact"]
|
|
347
|
+
},
|
|
348
|
+
"css_vars": {},
|
|
349
|
+
"custom_style_map": {
|
|
350
|
+
"row_height": "min-height",
|
|
351
|
+
"cell_padding_y": "padding-block"
|
|
352
|
+
},
|
|
353
|
+
"anchors": [
|
|
354
|
+
{ "kind": "class", "layer": "host", "target": ".vts-table-area .el-table .el-table__cell" },
|
|
355
|
+
{ "kind": "class", "layer": "inner", "target": ".vts-table-area .el-table .el-table__cell > .cell" }
|
|
356
|
+
],
|
|
357
|
+
"preconditions": [
|
|
358
|
+
"行高由「单元格 padding + 内容行高」共同决定(见 admin.content.table.cell)——声明行高档位时必须同时给出 padding 取值,只改其一会得到非预期行高。",
|
|
359
|
+
"出厂密度档位由 ReTableBar 的 size(large/default/small,源码实证 bar.vue 的宽松/默认/紧凑三项)驱动;设计侧只声明档位意图(form.density),具体 padding 由本项目按常规取值落定,不得照抄效果图某实例数据的行高。"
|
|
360
|
+
],
|
|
361
|
+
"priority": {
|
|
362
|
+
"strategy": "scope_qualified",
|
|
363
|
+
"reason": "单元格 padding 属全局样式,作用域限定到 .vts-page-card 即可覆盖。"
|
|
364
|
+
},
|
|
365
|
+
"$note": "设计意图(list-page §11.40 行密度):表格行高三档取一(宽松/默认/紧凑)。本条目为**补建通道**——此前登记表标 pending;语义档位 loose/default/compact ↔ 出厂 size large/default/small,项目侧负责映射。"
|
|
366
|
+
},
|
|
308
367
|
"admin.content.table.area": {
|
|
309
368
|
"type": "component",
|
|
310
369
|
"component": "ElTable(useTableSearch tableHeight 定高)",
|
|
@@ -139,8 +139,12 @@
|
|
|
139
139
|
"bg": "background-color",
|
|
140
140
|
"box_shadow": "box-shadow",
|
|
141
141
|
"gap": "gap",
|
|
142
|
-
"border_color": "border-color"
|
|
143
|
-
|
|
142
|
+
"border_color": "border-color",
|
|
143
|
+
"align_items": "align-items"
|
|
144
|
+
},
|
|
145
|
+
"preconditions": [
|
|
146
|
+
"顶栏内容垂直居中:--header-height 变更(如 54px)后,宿主 .navbar 必须显式 align-items: center——出厂顶栏内元素高度不一(折叠钮 40px / 文字行 / 头像),不显式声明会整体偏上(历史缺口:折叠按钮不垂直居中)。"
|
|
147
|
+
]
|
|
144
148
|
},
|
|
145
149
|
"admin.layout.header.logo_area": {
|
|
146
150
|
"type": "element",
|
package/package.json
CHANGED
package/parser/index.js
CHANGED
|
@@ -84,7 +84,7 @@ function main() {
|
|
|
84
84
|
case "audit":
|
|
85
85
|
return cmdAudit(args, dataset);
|
|
86
86
|
case "verify":
|
|
87
|
-
return cmdVerify(args);
|
|
87
|
+
return cmdVerify(args, dataset);
|
|
88
88
|
case "registry":
|
|
89
89
|
return cmdRegistry(args, dataset);
|
|
90
90
|
default:
|
|
@@ -103,7 +103,11 @@ function printHelp() {
|
|
|
103
103
|
node parser/index.js match --batch <dir> 项目级批量匹配:目录下全部变更集 + 跨文件汇总
|
|
104
104
|
node parser/index.js audit --project <dir> [<cs.json…>] 挂载点存在性 + 死通道静态审计(需目标项目源码)
|
|
105
105
|
node parser/index.js verify --oracle <o.json> --actual <a.json> [--sids a,b]
|
|
106
|
-
渲染态逐点验收:computed style 期望 vs 实测 diff
|
|
106
|
+
渲染态逐点验收:computed style 期望 vs 实测 diff
|
|
107
|
+
node parser/index.js verify --oracle <o.json> --resolve [--sids a,b]
|
|
108
|
+
实测端定位计划:sid → 词典 selector(项目页不打 data-sid 时按此抽取)
|
|
109
|
+
node parser/index.js verify … --from-theme 钴蓝 --to-theme 橘橙
|
|
110
|
+
同构异色复用:按语义槽位把效果图色值归一为本主题色值再比(配合 oracle.theme_mapping)
|
|
107
111
|
node parser/index.js registry <registry.json> [--verbose]
|
|
108
112
|
登记表通道核验:登记项 sid 是否在词典、dims 是否有落点()
|
|
109
113
|
node parser/index.js list [--scene admin] [--verbose] 列出全部语义 ID(--verbose 含字段与形态,供下发词汇约束)
|
|
@@ -675,8 +679,21 @@ function checkOracle(changeset, plan, valueDict) {
|
|
|
675
679
|
};
|
|
676
680
|
const declaredTheme = themeFromValue(changeset.global && changeset.global.brand_primary);
|
|
677
681
|
const mockupTheme = [...themes].find(t => mockup.includes(t)) || null;
|
|
678
|
-
|
|
679
|
-
|
|
682
|
+
|
|
683
|
+
// 同构异色复用:设计侧在 oracle 声明 theme_mapping { "from": "钴蓝", "to": "橘橙" } =
|
|
684
|
+
// 明确本变更集与效果图同场景 / 同页面 / 同角色集合,仅主题色板不同(语义槽位 -1..-10 一一对应)。
|
|
685
|
+
// 此时沿用 from 主题效果图是合法基线:verify 用 --from-theme/--to-theme 把色值归一到语义槽位再比,
|
|
686
|
+
// 几何 / 结构 / 字号等非色属性逐点照比 —— 不允许「只换个颜色就闭环不了」。
|
|
687
|
+
const tm = oracle.theme_mapping && typeof oracle.theme_mapping === "object" ? oracle.theme_mapping : null;
|
|
688
|
+
const declaredReuse = !!(tm && tm.from && tm.to
|
|
689
|
+
&& themes.has(tm.from) && themes.has(tm.to)
|
|
690
|
+
&& declaredTheme === tm.to && mockupTheme === tm.from);
|
|
691
|
+
|
|
692
|
+
if (declaredReuse) {
|
|
693
|
+
console.log(` ↺ 同构异色复用已声明:效果图「${tm.from}」→ 本主题「${tm.to}」(oracle.theme_mapping)。verify 需带 --from-theme ${tm.from} --to-theme ${tm.to} 做色值归一后比对。`);
|
|
694
|
+
}
|
|
695
|
+
else if (declaredTheme && mockupTheme && declaredTheme !== mockupTheme) {
|
|
696
|
+
issues.push(`主题错配:效果图是「${mockupTheme}」、本变更集主色是「${declaredTheme}」。同构异色(只换主题色板、场景/页面/角色集合不变)时,由设计侧在 oracle 声明 theme_mapping: { "from": "${mockupTheme}", "to": "${declaredTheme}" } 即可复用该效果图闭环;若结构 / 形态也变了,则必须换本主题自己的效果图。`);
|
|
680
697
|
}
|
|
681
698
|
if (!mockupTheme && mockup)
|
|
682
699
|
issues.push(`效果图文件名不含任何已知主题词(${[...themes].join("/")}),无法确认主题一致性 —— 请核对是否复用了其它主题的效果图。`);
|
|
@@ -684,8 +701,14 @@ function checkOracle(changeset, plan, valueDict) {
|
|
|
684
701
|
// ② 覆盖度(双向)
|
|
685
702
|
const declaredIds = plan.map(p => p.id);
|
|
686
703
|
const sidSet = new Set(sids);
|
|
704
|
+
// 仅 visible:false 的隐藏点位在效果图上没有可见元素可标 data-sid —— 豁免「无基线」判定
|
|
705
|
+
const hiddenOnly = new Set(
|
|
706
|
+
plan.filter(p => p.change && p.change.visible === false).map(p => p.id),
|
|
707
|
+
);
|
|
687
708
|
const sidNotDeclared = sids.filter(s => !declaredIds.includes(s));
|
|
688
|
-
const declaredWithoutSid = declaredIds.filter(id => !sidSet.has(id));
|
|
709
|
+
const declaredWithoutSid = declaredIds.filter(id => !sidSet.has(id) && !hiddenOnly.has(id));
|
|
710
|
+
if (hiddenOnly.size > 0)
|
|
711
|
+
console.log(` ℹ 隐藏点位(仅 visible:false)${hiddenOnly.size} 处豁免 sid 标注:${[...hiddenOnly].join("、")}`);
|
|
689
712
|
if (sidNotDeclared.length > 0)
|
|
690
713
|
issues.push(`欠声明 ${sidNotDeclared.length} 处:效果图里有、changes[] 未声明 → 该点位永远不可能被还原。\n ${sidNotDeclared.join("、")}`);
|
|
691
714
|
if (declaredWithoutSid.length > 0)
|
|
@@ -823,7 +846,29 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
|
|
|
823
846
|
}
|
|
824
847
|
if (changeset.global && Object.keys(changeset.global).length > 0)
|
|
825
848
|
declared.add(GLOBAL_PSEUDO_ID);
|
|
826
|
-
const
|
|
849
|
+
const declaredSids = new Set(Array.isArray(changeset.oracle?.sids) ? changeset.oracle.sids : []);
|
|
850
|
+
|
|
851
|
+
// 实物闭合:打开 oracle.mockup 这个 HTML,数真实存在的 data-sid(不只看设计侧自报的 oracle.sids)
|
|
852
|
+
const mockupRef = changeset.oracle?.mockup;
|
|
853
|
+
let physicalSids = null;
|
|
854
|
+
let mockupFile = null;
|
|
855
|
+
if (typeof mockupRef === "string" && mockupRef.trim()) {
|
|
856
|
+
const cands = [path.resolve(mockupRef), path.resolve(ROOT, mockupRef), path.resolve(process.cwd(), mockupRef)];
|
|
857
|
+
for (const c of cands) {
|
|
858
|
+
try {
|
|
859
|
+
const html = readFileSync(c, "utf-8");
|
|
860
|
+
// 先剥离 HTML 注释再数 data-sid:注释内的同形串是说明文字、不是点位。
|
|
861
|
+
// 不剥离 → 注释可「兜住」一个实际缺失的标注,闸门全绿而基线为空(详见 mockup-extraction-spec.md §7.1)。
|
|
862
|
+
const stripped = html.replace(/<!--[\s\S]*?-->/g, "");
|
|
863
|
+
physicalSids = new Set([...stripped.matchAll(/data-sid\s*=\s*["']([^"']+)["']/g)].map(m => m[1]));
|
|
864
|
+
mockupFile = c;
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
867
|
+
catch { /* 该候选路径不可读,试下一个 */ }
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
// 比对基准:优先效果图实物;实物读不到时退化为自报清单(并在下方告警)
|
|
871
|
+
const mockupSids = physicalSids || declaredSids;
|
|
827
872
|
|
|
828
873
|
const applicable = items.filter(it => it.always === true || registrySids(it).some(s => mockupSids.has(s)));
|
|
829
874
|
const gaps = applicable.filter(it => !registrySids(it).every(s => declared.has(s)));
|
|
@@ -832,6 +877,23 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
|
|
|
832
877
|
console.log(` 登记表:${registryPath}(登记项 R=${items.length})`);
|
|
833
878
|
console.log(` 适用 A=${applicable.length} / 已兑现 C=${applicable.length - gaps.length} / 缺口 ${gaps.length}`);
|
|
834
879
|
|
|
880
|
+
// 实物校验:标注缺口的权威来源是效果图 HTML 里的 data-sid,而不是自报的 oracle.sids
|
|
881
|
+
let physicalGap = false;
|
|
882
|
+
if (physicalSids) {
|
|
883
|
+
console.log(` 效果图实物:${mockupFile}(实测 data-sid ${physicalSids.size} 个)`);
|
|
884
|
+
const declaredNoPhysical = [...declaredSids].filter(s => !physicalSids.has(s));
|
|
885
|
+
const physicalNoDeclared = [...physicalSids].filter(s => !declaredSids.has(s));
|
|
886
|
+
if (declaredNoPhysical.length > 0) {
|
|
887
|
+
console.log(` ⚠ oracle.sids 声明了 ${declaredNoPhysical.length} 个点位,但效果图实物里没有对应 data-sid(无基线):${declaredNoPhysical.join("、")}`);
|
|
888
|
+
physicalGap = true;
|
|
889
|
+
}
|
|
890
|
+
if (physicalNoDeclared.length > 0)
|
|
891
|
+
console.log(` ℹ 效果图实物另有 ${physicalNoDeclared.length} 个 data-sid 未列入 oracle.sids(自报清单不全,不阻塞):${physicalNoDeclared.join("、")}`);
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
console.log(` ⚠ 未能打开效果图实物(oracle.mockup 不可读)→ 本轮退化为按自报 oracle.sids 校验,实物闭合未生效`);
|
|
895
|
+
}
|
|
896
|
+
|
|
835
897
|
// 效果图标注完整性:恒存在的子项必须能在效果图上找到同名 data-sid,否则基线根本抽不出来
|
|
836
898
|
const unannotated = items.filter(it => it.always === true && !registrySids(it).some(s => mockupSids.has(s)));
|
|
837
899
|
if (unannotated.length > 0) {
|
|
@@ -842,13 +904,20 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
|
|
|
842
904
|
}
|
|
843
905
|
console.log(` ⚠ 效果图标注缺口 ${unannotated.length} 条:这些子项恒存在,却无同名 data-sid → 基线抽取不全`);
|
|
844
906
|
console.log(` 涉及点位:${[...bySid.entries()].map(([s, n]) => `${s}(${n})`).join("、")}`);
|
|
907
|
+
physicalGap = true;
|
|
845
908
|
}
|
|
846
909
|
|
|
847
|
-
if (gaps.length === 0) {
|
|
848
|
-
console.log(` ✔ 覆盖率通过:C = A
|
|
910
|
+
if (gaps.length === 0 && !physicalGap) {
|
|
911
|
+
console.log(` ✔ 覆盖率通过:C = A,无子项被静默丢失;效果图实物标注闭合`);
|
|
849
912
|
return 0;
|
|
850
913
|
}
|
|
851
914
|
|
|
915
|
+
if (gaps.length === 0 && physicalGap) {
|
|
916
|
+
console.log(`\n✘ 效果图实物标注不闭合 → exit 1(oracle.sids 与效果图 HTML 的 data-sid 不一致 / 恒存在子项缺标注)`);
|
|
917
|
+
console.log(` 处置:给效果图对应元素补 data-sid,或把该登记项降级 source: spec(见 mockup-extraction-spec.md §7)`);
|
|
918
|
+
return 1;
|
|
919
|
+
}
|
|
920
|
+
|
|
852
921
|
const byGroup = new Map();
|
|
853
922
|
for (const it of gaps) {
|
|
854
923
|
const g = it.group || "(未分组)";
|
|
@@ -1275,9 +1344,10 @@ function cmdAudit(args, dataset) {
|
|
|
1275
1344
|
* 本命令不做抽取,只做归一化 + 逐点 diff + 三类归因。
|
|
1276
1345
|
*
|
|
1277
1346
|
* CLI:node parser/index.js verify --oracle <oracle.json> --actual <actual.json> [--sids a,b,c]
|
|
1347
|
+
* 实测端定位计划:node parser/index.js verify --oracle <oracle.json> --resolve [--sids a,b,c]
|
|
1278
1348
|
* 退出码:存在缺口 → 1;全部命中 → 0。
|
|
1279
1349
|
*/
|
|
1280
|
-
function cmdVerify(args) {
|
|
1350
|
+
function cmdVerify(args, dataset) {
|
|
1281
1351
|
// 几何 / 盒模型属性:值对不上 = 语境错位(宿主盒 / 内层盒模型 / 层叠上下文不同)
|
|
1282
1352
|
const GEOMETRY_PROPS = new Set([
|
|
1283
1353
|
"display", "position", "top", "right", "bottom", "left", "box-sizing",
|
|
@@ -1301,8 +1371,10 @@ function cmdVerify(args) {
|
|
|
1301
1371
|
};
|
|
1302
1372
|
const oraclePath = get("--oracle");
|
|
1303
1373
|
const actualPath = get("--actual");
|
|
1304
|
-
|
|
1374
|
+
const resolveOnly = args.includes("--resolve");
|
|
1375
|
+
if (!oraclePath || (!resolveOnly && !actualPath)) {
|
|
1305
1376
|
console.error("✖ 缺少参数:node parser/index.js verify --oracle <oracle.json> --actual <actual.json> [--sids a,b,c]");
|
|
1377
|
+
console.error(" 或输出实测端定位计划:node parser/index.js verify --oracle <oracle.json> --resolve [--sids a,b,c]");
|
|
1306
1378
|
process.exit(1);
|
|
1307
1379
|
}
|
|
1308
1380
|
const sidsArg = get("--sids");
|
|
@@ -1335,12 +1407,53 @@ function cmdVerify(args) {
|
|
|
1335
1407
|
};
|
|
1336
1408
|
|
|
1337
1409
|
const oracle = readDump(oraclePath, "效果图期望");
|
|
1338
|
-
const actual = readDump(actualPath, "项目实测");
|
|
1339
1410
|
|
|
1340
1411
|
let sids = Object.keys(oracle);
|
|
1341
1412
|
if (sidFilter)
|
|
1342
1413
|
sids = sids.filter(s => sidFilter.has(s));
|
|
1343
1414
|
|
|
1415
|
+
// 实测端定位闭合:--resolve 输出「sid → 词典 selector」抽取计划。
|
|
1416
|
+
// 效果图端用 data-sid(设计侧自控);项目页端不要求打 data-sid —— 浏览器按本计划的 selector 抽取。
|
|
1417
|
+
if (resolveOnly) {
|
|
1418
|
+
const index = buildMergedIndex(dataset || {});
|
|
1419
|
+
console.log(`\n=== 实测端定位计划(sid → 词典 selector)===`);
|
|
1420
|
+
console.log(`用途:项目页不要求打 data-sid —— 浏览器抽取脚本按下列 selector 定位元素取 computed style 生成 actual.json`);
|
|
1421
|
+
console.log(`selector 挂载点存在性由 audit 静态校验;本计划只回答「按什么定位」。\n`);
|
|
1422
|
+
let missing = 0;
|
|
1423
|
+
for (const sid of sids) {
|
|
1424
|
+
const entry = index[sid];
|
|
1425
|
+
if (!entry) {
|
|
1426
|
+
console.log(` ✘ ${sid} —— 词典无此条目(L3 新点位,先落地并回写临时映射)`);
|
|
1427
|
+
missing++;
|
|
1428
|
+
continue;
|
|
1429
|
+
}
|
|
1430
|
+
const sels = [];
|
|
1431
|
+
if (Array.isArray(entry.anchors)) {
|
|
1432
|
+
for (const a of entry.anchors) {
|
|
1433
|
+
if (a && a.kind === "class" && a.target)
|
|
1434
|
+
sels.push(`[${a.layer || "host"}] ${a.target}`);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
if (sels.length === 0 && entry.selector)
|
|
1438
|
+
sels.push(`[host] ${entry.selector}`);
|
|
1439
|
+
if (sels.length === 0) {
|
|
1440
|
+
console.log(` ✘ ${sid} —— 词典条目无 selector / anchors,无法定位`);
|
|
1441
|
+
missing++;
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
console.log(` · ${sid}`);
|
|
1445
|
+
for (const s of sels)
|
|
1446
|
+
console.log(` ${s}`);
|
|
1447
|
+
}
|
|
1448
|
+
console.log(`\n汇总:${sids.length} 点位,${missing} 个无法定位`);
|
|
1449
|
+
console.log(`抽取属性白名单两端必须一致(padding / font-size / font-weight / text-align / color / background-color / border-radius / gap 等,见 mockup-extraction-spec §3);同构异色复用再加 --from-theme/--to-theme 做色值归一。`);
|
|
1450
|
+
if (missing > 0)
|
|
1451
|
+
process.exitCode = 1;
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
const actual = readDump(actualPath, "项目实测");
|
|
1456
|
+
|
|
1344
1457
|
const normProp = p => String(p).trim().toLowerCase().replace(/_/g, "-");
|
|
1345
1458
|
const normValue = v => {
|
|
1346
1459
|
let s = String(v == null ? "" : v).trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -1350,6 +1463,41 @@ function cmdVerify(args) {
|
|
|
1350
1463
|
return s;
|
|
1351
1464
|
};
|
|
1352
1465
|
|
|
1466
|
+
// 同构异色主题归一:--from-theme 钴蓝 --to-theme 橘橙 时,把效果图的主题色族色值
|
|
1467
|
+
// 按语义译表的槽位(-1..-10)映射为本主题同槽位色值再比对;译表之外的值原样保留。
|
|
1468
|
+
const fromTheme = get("--from-theme");
|
|
1469
|
+
const toTheme = get("--to-theme");
|
|
1470
|
+
let themeAlias = null;
|
|
1471
|
+
if (fromTheme && toTheme) {
|
|
1472
|
+
try {
|
|
1473
|
+
const sv = JSON.parse(readFileSync(path.join(ROOT, "mapping", "basic", "semantic-values.json"), "utf-8"));
|
|
1474
|
+
const slotOf = {};
|
|
1475
|
+
const hexOf = {};
|
|
1476
|
+
for (const t of Object.values(sv.themes || {})) {
|
|
1477
|
+
for (const [name, hex] of Object.entries(t.values || {})) {
|
|
1478
|
+
const m = /^(.+?)-(\d+)$/.exec(name);
|
|
1479
|
+
if (!m)
|
|
1480
|
+
continue;
|
|
1481
|
+
const key = String(hex).toLowerCase();
|
|
1482
|
+
slotOf[`${t.label}|${key}`] = m[2];
|
|
1483
|
+
hexOf[`${t.label}|${m[2]}`] = key;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
themeAlias = (v) => {
|
|
1487
|
+
let s = String(v == null ? "" : v).toLowerCase();
|
|
1488
|
+
s = s.replace(/#([0-9a-f]{3})\b/g, (_, h) => `#${h[0]}${h[0]}${h[1]}${h[1]}${h[2]}${h[2]}`);
|
|
1489
|
+
return s.replace(/#[0-9a-f]{6}/g, (hex) => {
|
|
1490
|
+
const lvl = slotOf[`${fromTheme}|${hex}`];
|
|
1491
|
+
return lvl ? (hexOf[`${toTheme}|${lvl}`] || hex) : hex;
|
|
1492
|
+
});
|
|
1493
|
+
};
|
|
1494
|
+
console.log(`主题归一:效果图「${fromTheme}」→ 本主题「${toTheme}」(按语义槽位映射色值后比对,几何 / 结构照比)`);
|
|
1495
|
+
}
|
|
1496
|
+
catch (e) {
|
|
1497
|
+
console.log(`⚠ 主题译表读取失败(${e.message})—— 本轮不做主题色归一,色值类属性可能出现假缺口`);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1353
1501
|
console.log(`渲染态验收(computed style 逐点比对)`);
|
|
1354
1502
|
console.log(`效果图基线:${path.resolve(oraclePath)}`);
|
|
1355
1503
|
console.log(`项目实测:${path.resolve(actualPath)}`);
|
|
@@ -1385,7 +1533,8 @@ function cmdVerify(args) {
|
|
|
1385
1533
|
attr.selector_miss++;
|
|
1386
1534
|
continue;
|
|
1387
1535
|
}
|
|
1388
|
-
|
|
1536
|
+
const expNorm = themeAlias ? themeAlias(expVal) : expVal;
|
|
1537
|
+
if (normValue(expNorm) === normValue(act[prop])) {
|
|
1389
1538
|
hitProps++;
|
|
1390
1539
|
continue;
|
|
1391
1540
|
}
|
|
@@ -103,11 +103,41 @@ parser match(确定性翻译)→ 施工坐标 → AI 按坐标改代码 →
|
|
|
103
103
|
|
|
104
104
|
## 执行流程(按序,禁止跳步)
|
|
105
105
|
|
|
106
|
-
### 第 0 步 · 版本与场景检查
|
|
106
|
+
### 第 0 步 · 版本与场景检查 · 对接匹配(适配前置)· 运行时清场
|
|
107
|
+
|
|
108
|
+
**0.1 版本与场景检查**
|
|
107
109
|
|
|
108
110
|
- 变更集 `schema` 版本不在支持范围 → 停止,向上游(用户/设计侧)报告版本不匹配,不降级猜测。
|
|
109
111
|
- `scene` 在 `parser list` 场景清单中无对应域 → 报告并列出支持的场景,等待确认。
|
|
110
112
|
|
|
113
|
+
**0.2 对接匹配(适配前置)——把散落的匹配收拢为一个前置阶段**
|
|
114
|
+
|
|
115
|
+
在施工前,把「设计意图 ⇄ 模板能力」整体对一遍,输出匹配结论,供**生成前**决策(而不是落地时才发现对不上)。三步判定:
|
|
116
|
+
|
|
117
|
+
| 级 | 判定 | 依据 |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| **布局级** | `layout_type` 命中标准枚举? | 命中 → 切布局模板池(§5.2);未命中 → 变体派生 / 定制生成骨架(§5.3 / §5.4) |
|
|
120
|
+
| **点位级** | 逐 id 三级匹配 | 一级完整命中 / 二级父级命中 / 三级未命中(§4.1) |
|
|
121
|
+
| **形态级** | `form` 在条目 `forms` 枚举内?`structure_ops` 所需 DOM 钩子是否存在? | 词典 `forms` / 模板源码 / `audit` |
|
|
122
|
+
|
|
123
|
+
**输出三类结论,处置全在项目侧**:
|
|
124
|
+
|
|
125
|
+
| 结论 | 判据 | 处置 |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| 完全匹配 | L1 ∧ form 在枚举 ∧ 无需结构 | 直接施工 |
|
|
128
|
+
| 可派生 | L2 / 变体布局 / form 可映射 | 派生骨架或继承父级后施工 |
|
|
129
|
+
| 需适配 | L3 / 定制布局 / 缺通道 / 需新 DOM | **先补通道 / 结构增量 / 生成骨架,再生成**;交付说明单列申报 |
|
|
130
|
+
|
|
131
|
+
> **总原则(红线):设计为主,项目侧适配。** 设计效果是目标态,模板是待适配的一方。"设计 ↔ 模板对不上"的出口**永远在项目侧**——**不回流设计侧改设计**。只有「设计侧未给值 / 值非法」(**意图缺口**)才回流设计侧。严禁以「模板不支持」为由要求设计改图。
|
|
132
|
+
|
|
133
|
+
**0.3 运行时主题清场(T 通道先于 D/S/C)**
|
|
134
|
+
|
|
135
|
+
主色 / 主题走的是**运行时通道**:`setEpThemeColor()` 把 `--el-color-primary` 写成 `documentElement` 的**内联样式**并持久化到 localStorage(读取优先级 = 浏览器持久化 ?? `platform-config.json`)。**内联样式 + 运行时持久化 ⇒ 设计层 CSS 与词典 selector 都赢不了。**
|
|
136
|
+
|
|
137
|
+
- 落地前先读 localStorage(键 `responsive-layout`)的 `theme` / `epThemeColor`,与本变更集主题**不一致时清 key 或显式重设**;
|
|
138
|
+
- 清场动作与结果写入交付说明(可回溯);
|
|
139
|
+
- **不清场则颜色类差异全部失真**——归因前必须先确认起点已对齐,否则会把「环境残留」误判成「落地失败」。
|
|
140
|
+
|
|
111
141
|
### 第 1 步 · parser 确定性匹配
|
|
112
142
|
|
|
113
143
|
```bash
|
|
@@ -233,6 +263,7 @@ node parser/index.js registry <restore-registry.json> [--verbose] # 只
|
|
|
233
263
|
- 变更集**没给** `layer`、但条目 `preconditions` 指出必须落内层(如 EP 表头 `white-space`)→ 按 `preconditions` 落 inner 层,并在交付说明注明「设计侧未给 layer,按条目前置条件处理」。
|
|
234
264
|
- **内层盒属性族强制校验**:`white-space` / `text-overflow` / `overflow` / `word-break` / `text-align` / `line-height` / `vertical-align` 这组属性**只在内层文本盒生效**——逐字段核对 `layer`;未指认且条目前置条件也没说 → **退回设计侧补声明**,禁止默认落 host(历史缺口:表头「禁止换行」落 host 被内层默认 `normal` 吃掉 → 表头竖排、行高撑大、列内容被压)。
|
|
235
265
|
- 常规分工:高度 / 背景 / padding 落 host;文本换行、省略、对齐落 inner。
|
|
266
|
+
- **几何语境必须随值迁移(双重内边距,历史缺口:表头标题截断成 类…/包…/操…)**:出厂内层文本盒自带 padding 时(EP `.el-table .cell` 出厂 `padding: 0 12px`),把设计的 16px 落到 host 后**必须同步清零内层自带 padding**——否则实际内边距 = 16+12 = 28px/侧,列宽被挤、表头标题被省略号截断、视觉不适配常规。判据:**效果图的距离语义是「列缘到文字」的总距离;宿主/内层两层结构下必须拆成两段落地,两段之和等于设计值**。凡向 host 落 padding/border 类几何值,先查出厂内层盒的自带同族值再决定是否归零。
|
|
236
267
|
|
|
237
268
|
**B2. 出厂形态互斥收口 `suppress`(治「新形态上了、旧形态残留」)**——条目带 `suppress` 时,逐通道执行收口,**收口优先走令牌重映射**(在令牌宿主作用域重声明令牌,零权重战争),令牌不存在才写属性:
|
|
238
269
|
|
|
@@ -315,6 +346,8 @@ html body .sidebar-container { /* 令牌宿主 = 该令牌被消费的
|
|
|
315
346
|
|
|
316
347
|
写入纪律:
|
|
317
348
|
|
|
349
|
+
- **本文件是产物,不是手改对象(流程红线)**:`design-shell.scss` 的每一行都必须由「变更集 + 词典」经本协议产出——发现缺规则时,正确动作是**补词典字段 / 前置条件 / 登记项**让下一次 design-apply 自动生成,**禁止绕过流程直接手写项目文件**。历史规则已落词典的(侧栏令牌 / suppress / 表头内层 nowrap / 双重内边距归零 / 分割线通道),重生成后会自动带出;仅「补通道」(变量消费者 / form 枚举 / 组件中性默认)允许动出厂源码并单列申报。
|
|
350
|
+
|
|
318
351
|
- 每条规则带来源标注(`/* <语义 ID> · <变更条目> */`),换设计时整段可替换;
|
|
319
352
|
- 本文件内新增的 CSS 变量必须**定义 + 引用成对出现**(出厂已有消费者也算),避免死通道;
|
|
320
353
|
- 出厂元素需关闭时(存在性收口)也在这里做:令牌置 `transparent` 或 `display:none`,**绝不删出厂代码行**;
|
|
@@ -344,6 +377,9 @@ html .navbar .navbar-breadcrumb-wrap {
|
|
|
344
377
|
|
|
345
378
|
### 第 4 步 · L3 新模块处理(分级布局)
|
|
346
379
|
|
|
380
|
+
- **验收对象闸(角色集合必须齐)**:验收页面的**角色集合**必须覆盖变更集声明的角色。缺角色的,按第 0.2 步「对接匹配 → 需适配」处置——**项目侧补齐该角色的骨架 / 结构**(走 `crud-pages` 样板 + L3 落地并申报),**不是要求设计侧换页面**。不得用出厂 demo 页充数:demo 页**结构上就没有**标题区 / 批量按钮 / 状态 Tag 这些角色 → 这类差异**无论怎么调 CSS 都消不掉**(不是样式问题,是角色集合不匹配)。
|
|
381
|
+
- **列宽口径(业务层 / 设计层边界)**:表格**业务列宽度属业务层**,设计侧不声明、项目侧不按效果图硬套——效果图里的列宽只是其示例数据下的参考值。**唯一例外是操作列**:宽度由组件公式推算(`resolveOperateColumnWidth`,已含分割线占位),业务页禁止写死。**固定列自查(落完后必做,防「符合规范但不合常规」)**:操作列按钮不被裁、不换行、分割线居中可见;状态列标签完整;表头/单元格标题不被省略号截断——任何一项不满足即返工,禁止用「设计值就是对的」搪塞。
|
|
382
|
+
|
|
347
383
|
- **L3 是施工指令,不是待办**:词典未命中的点位一律当场落地、交付说明申报「补映射」,不留待确认。新模块按「定制布局」三步走:① 基于全局规范生成结构(继承命名/作用域/组件规范)② 原子层字段(色彩/圆角/组件级)从 `basic/` + `component/` 词典借力 ③ 场景专属点位生成后**立即写入 mapping/ 对应场景域**(临时映射),累计复用 ≥3 次建议升级标准(由人工评审)。
|
|
348
384
|
- 生成的新页面必须带构图钩子(`vts-page-card` / `vts-filter-area` / `vts-table-area` 等),保证后续变更集可命中。
|
|
349
385
|
|
|
@@ -378,7 +414,7 @@ html .navbar .navbar-breadcrumb-wrap {
|
|
|
378
414
|
- [ ] **映射通道活性检查(杂必查)**:样式落地引用的每个 CSS 变量必须在项目样式中**存在消费者**(`变量名` 有 `var(--xxx)` 引用点);只定义不消费的变量 = 通道死路,翻译再准也不生效(menu_active_indicator 曾因 --vts-theme-menu-active-before 无消费者而「映射了却没效果」)。施工时 grep 核对,无消费者时改走消费者存在的作用域或补挂载点并申报。
|
|
379
415
|
- [ ] **结构施工兑现检查(structure_ops 逐条对账,与红线同级)**:变更集 `structure_ops[]` 的**每一条 op** 都必须在交付说明给出「op → 落在哪个文件 / 哪个节点 / 怎么落的」;`add_node` 必须真的新增了元素或渲染分支(模板 diff 可验),`bind_source` 必须真的改了绑定(`class-name` / `align` / `formatter`)。出现「声明了 op、代码里找不到对应改动」= 交付不完整,禁止只改 CSS 冒充结构施工(历史缺口:操作列分割线 `add_node` 声明了但从未施工 → 按钮间竖线一直缺失)
|
|
380
416
|
- [ ] **同权重失效检查( · 专查「写了不生效」)**:逐条核对设计层新增规则是否真能取胜——① 对手是组件 `<style scoped>` 时,本条特异度必须**严格高于**它(**平手即错**,因为组件样式在全局之后注入);② 对手带 `!important` 时本条必须同样 `!important`,或改走通道变量。**判定方法(必须真数,不许目测)**:对每条新规则与其对手分别写出特异度三元组 `(a,b,c)` 并**逐位比较(先 b 后 c)**,本条的 `b` 不大于对手即判为「失效」;`html` / `body` 只加 `c` 位,**对手 `b` 更高时加 `html body` 无效**,必须靠**加类**(祖先链补稳定语义类,如 `.el-menu`)把 `b` 抬到相等。同 `b` 时,`c` 更高者胜。③ **出厂用语义令牌间接设置该属性时(`var(--vts-theme-*)`),必须先走「令牌重映射」而不是抢属性**——在令牌宿主作用域重声明令牌即生效、零权重战争;只有无令牌或令牌语义不符时才抢属性。历史缺口全部命中本项:面包屑 `display:none`(与组件 scoped 同为 0,2,0)→ 照旧显示;页签关闭钮 `opacity`(同为 0,5,0)→ 仍要 hover 才显;页签项 `padding`(对手带 `!important`)→ 无论怎么抬特异度都改不动;**侧栏菜单 `color`(`b=2` 输给出厂 `b=3` + `!important`)→ 整栏菜单文字用浅色主题的黑色落在深色侧栏上、字全部消失**
|
|
381
|
-
- [ ] **oracle 基线可用性检查**:变更集带 `oracle` 时——① `oracle.mockup`
|
|
417
|
+
- [ ] **oracle 基线可用性检查**:变更集带 `oracle` 时——① `oracle.mockup` 必须与**本次主题、本次页面**一致;**同构异色复用除外**:变更集声明 `oracle.theme_mapping: { "from": "<效果图主题>", "to": "<本次主题>" }`(同场景 / 同页面 / 同角色集合、仅主题色板不同)时,允许复用 from 主题效果图,verify 必须带 `--from-theme/--to-theme` 做色值归一,**不得因换主题色就判死闭环**;结构 / 形态有差异时禁止复用;② `oracle.sids` 与 `changes[]` **双向闭合**:效果图里有、changes[] 未声明 = **欠声明**(该点位永远不可能被还原);changes[] 已声明、效果图无 sid = **无基线**——**仅 `visible:false` 的隐藏点位豁免**(效果图上无可见元素可标 `data-sid`,parser 已自动豁免并打印清单)。其余两类出现即回流设计侧补齐,**不得直接开工**(`parser match` 已自动报出)
|
|
382
418
|
- [ ] **dark.scss 暗色同步**:新增视觉规则在 `html.dark` 有对应覆盖
|
|
383
419
|
- [ ] **缺口清单只含意图缺口**:清单每一项都必须是「设计侧未给值 / 值非法」且已注明「回流设计侧补齐」;能力缺口(词典缺 ID / 缺字段枚举 / 缺 form)不得出现在清单里——出现即把可落地项搁置
|
|
384
420
|
- [ ] **台账逐行兑现对账(对账闸门)**:把设计侧交付的提取台账逐行过一遍,每行必须有落地结论——已落地(写明落在哪个文件/选择器)或已入缺口清单(写明缺失字段)。存在「台账有行、落地无结论」的行 = 交付不完整,与红线同级
|
|
@@ -393,12 +429,18 @@ html .navbar .navbar-breadcrumb-wrap {
|
|
|
393
429
|
**链路(抽取由浏览器完成,diff 由 parser 完成)**:
|
|
394
430
|
|
|
395
431
|
1. **抽取期望值**:在浏览器打开效果图(`oracle.mockup`,元素已带 `data-sid`),对 `oracle.sids` 中每个 sid 取其代表性元素的 computed style,导出为 `{ "<sid>": { "<css属性>": "<值>" } }` → `oracle.json`。
|
|
396
|
-
2.
|
|
397
|
-
|
|
432
|
+
2. **抽取实测值**:在浏览器打开已落地页面,**按词典 `selector` / `anchors.target` 定位元素**(**项目页不要求打 `data-sid`**)做抽取 → `actual.json`。
|
|
433
|
+
- 先跑 `node parser/index.js verify --oracle oracle.json --resolve` 得到「sid → 词典 selector」定位计划;
|
|
434
|
+
- 抽取脚本(Playwright / 浏览器控制台)按计划 selector 取 `getComputedStyle(el)` 的白名单属性(padding / font-size / font-weight / text-align / color / background-color / border-radius / gap 等);
|
|
435
|
+
- **期望端(效果图)用 `data-sid`、实测端(项目页)用词典 selector——两侧定位方式不必对称**:效果图是设计侧自控产物,项目页是消费方、不感知设计坐标。selector 挂载点错位由 `audit` 先于 `verify` 报出,责任链回到词典;
|
|
436
|
+
- 期望值**优先直接消费设计侧交付的 `oracle.json` 抽取产物**(期望 / 实测同源同口径);无产物时再从 `oracle.mockup` 现场抽取;
|
|
437
|
+
- 两个页面的属性白名单必须一致,否则出现假缺口。
|
|
398
438
|
3. **逐点 diff**:
|
|
399
439
|
```bash
|
|
400
440
|
node parser/index.js verify --oracle oracle.json --actual actual.json
|
|
401
441
|
# 可选 --sids admin.content.table.header_cell,admin.layout.tabs.item 只验部分点位
|
|
442
|
+
# 同构异色复用(oracle.theme_mapping 已声明时必带)——效果图色值按语义槽位归一为本主题色值再比:
|
|
443
|
+
node parser/index.js verify --oracle oracle.json --actual actual.json --from-theme 钴蓝 --to-theme 橘橙
|
|
402
444
|
```
|
|
403
445
|
退出码:存在缺口 → `1`。
|
|
404
446
|
|
|
@@ -416,6 +458,10 @@ html .navbar .navbar-breadcrumb-wrap {
|
|
|
416
458
|
|
|
417
459
|
## 词典维护纪律
|
|
418
460
|
|
|
461
|
+
### 出厂状态漂移(词典假前提 = 事故源,落地前必查)
|
|
462
|
+
|
|
463
|
+
**项目可能随时被还原到出厂**(design-shell 回空壳、`.changeset/*` 消失、本体系此前补的通道全部消失)。因此词典里凡「出厂已具备 X」类表述必须满足:① X 是**真出厂能力**(对照出厂源码可验证);② 由本体系补建的通道一律写成**条件式**——「若缺失,按 structure_ops / 补通道流程补建并申报」,不写「已存在」。**落地前必须跑 `node parser/index.js audit --project <dir>`**:锚点/选择器/变量消费者存在性静态校验,会当场暴露词典假前提(曾发生:词典写「出厂已渲染分割线 / 工具栏行已挂语义类」,项目还原后全部失真 → 规则落空)。
|
|
464
|
+
|
|
419
465
|
- mapping/ 是唯一事实源:施工前 `parser query <id>` 精读条目,**不凭记忆施工**。
|
|
420
466
|
- **禁止编造的是「既有条目的映射」**(改既有 selector / css_vars / 变量指向 = 静默篡改,绝对禁止)。**词典缺 ID / 缺字段枚举不属编造**——那是新点位,按 L3 当场落地并写**新增的**临时映射条目,交付说明申报,等人工评审转正。
|
|
421
467
|
- 「宁可不做,不做错」只适用于**设计侧未给值 / 值非法**的意图缺口;用它搁置能力缺口 = 交付失败。
|
|
@@ -1,468 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schema": "design.changeset/1.0",
|
|
3
|
-
"changeset_version": "1.0.0",
|
|
4
|
-
"mapping_version": "1.0.0",
|
|
5
|
-
"scene": "admin",
|
|
6
|
-
"layout_type": "vertical",
|
|
7
|
-
"global": {
|
|
8
|
-
"brand_primary": "钴蓝-5"
|
|
9
|
-
},
|
|
10
|
-
"changes": [
|
|
11
|
-
{
|
|
12
|
-
"id": "admin.layout.sidebar",
|
|
13
|
-
"style": {
|
|
14
|
-
"width": "208px",
|
|
15
|
-
"collapsed_width": "64px",
|
|
16
|
-
"bg": "#001529"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"id": "admin.layout.sidebar.logo_area",
|
|
21
|
-
"style": {
|
|
22
|
-
"height": "54px",
|
|
23
|
-
"title_font_size": "16px",
|
|
24
|
-
"title_font_weight": "中等",
|
|
25
|
-
"title_color": "#FFFFFF"
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"id": "admin.layout.sidebar.menu",
|
|
30
|
-
"form": "pill",
|
|
31
|
-
"style": {
|
|
32
|
-
"padding_y": "8px",
|
|
33
|
-
"item_height": "40px",
|
|
34
|
-
"font_size": "14px",
|
|
35
|
-
"font_weight": "常规",
|
|
36
|
-
"text_color": "rgba(255,255,255,0.65)",
|
|
37
|
-
"menu_hover_bg": "rgba(255,255,255,0.08)",
|
|
38
|
-
"menu_active_bg": "钴蓝-5",
|
|
39
|
-
"menu_active_indicator": "钴蓝-5"
|
|
40
|
-
},
|
|
41
|
-
"priority": { "strategy": "deep_qualified" }
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"id": "admin.layout.sidebar.collapse_trigger",
|
|
45
|
-
"visible": false
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"id": "admin.layout.header",
|
|
49
|
-
"style": {
|
|
50
|
-
"height": "54px",
|
|
51
|
-
"padding_x": "24px",
|
|
52
|
-
"bg": "#FFFFFF",
|
|
53
|
-
"border_color": "#F0F0F0"
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"id": "admin.layout.header.collapse_trigger",
|
|
58
|
-
"form": "header_inline",
|
|
59
|
-
"style": {
|
|
60
|
-
"btn_width": "40px",
|
|
61
|
-
"btn_height": "40px",
|
|
62
|
-
"icon_size": "16px",
|
|
63
|
-
"border_radius": "6px"
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
"id": "admin.layout.header.logo_area",
|
|
68
|
-
"visible": false
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"id": "admin.layout.header.breadcrumb",
|
|
72
|
-
"visible": false,
|
|
73
|
-
"priority": { "strategy": "deep_qualified" }
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"id": "admin.layout.header.lang_switch",
|
|
77
|
-
"form": "inline_switch",
|
|
78
|
-
"style": {
|
|
79
|
-
"gap": "2px",
|
|
80
|
-
"icon_size": "16px",
|
|
81
|
-
"text_color": "#000000A6"
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"id": "admin.layout.header.fullscreen",
|
|
86
|
-
"style": {
|
|
87
|
-
"icon_size": "16px",
|
|
88
|
-
"width": "32px"
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
"id": "admin.layout.header.setting_button",
|
|
93
|
-
"style": {
|
|
94
|
-
"icon_size": "16px",
|
|
95
|
-
"hover_bg": "#FAFAFA",
|
|
96
|
-
"text_color": "#000000A6"
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
"id": "admin.layout.header.user_profile",
|
|
101
|
-
"form": "greeting_avatar_name",
|
|
102
|
-
"style": {
|
|
103
|
-
"avatar_width": "28px",
|
|
104
|
-
"avatar_height": "28px",
|
|
105
|
-
"avatar_radius": "9999px",
|
|
106
|
-
"name_font_size": "14px",
|
|
107
|
-
"name_font_weight": "中等",
|
|
108
|
-
"greeting_font_size": "14px",
|
|
109
|
-
"gap": "12px",
|
|
110
|
-
"text_color": "#000000E6",
|
|
111
|
-
"hover_bg": "#FAFAFA"
|
|
112
|
-
},
|
|
113
|
-
"structure_ops": [{ "op": "add_node", "node": "问候区" }]
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"id": "admin.layout.tabs",
|
|
117
|
-
"form": "flat",
|
|
118
|
-
"style": {
|
|
119
|
-
"height": "40px",
|
|
120
|
-
"bg": "#FFFFFF",
|
|
121
|
-
"border_bottom": "1px solid #F0F0F0",
|
|
122
|
-
"padding_inline": "0px"
|
|
123
|
-
},
|
|
124
|
-
"priority": { "strategy": "deep_qualified" }
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
"id": "admin.layout.tabs.home_tab",
|
|
128
|
-
"form": "icon_only",
|
|
129
|
-
"style": {
|
|
130
|
-
"width": "40px",
|
|
131
|
-
"height": "40px",
|
|
132
|
-
"icon_size": "16px",
|
|
133
|
-
"icon_color": "钴蓝-5"
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"id": "admin.layout.tabs.item",
|
|
138
|
-
"form": "text_only",
|
|
139
|
-
"style": {
|
|
140
|
-
"card_height": "40px",
|
|
141
|
-
"padding_x": "12px",
|
|
142
|
-
"title_gap": "6px",
|
|
143
|
-
"close_size": "12px",
|
|
144
|
-
"close_opacity": "0.45",
|
|
145
|
-
"close_hover_color": "#FF4D4F",
|
|
146
|
-
"divider_color": "#F0F0F0"
|
|
147
|
-
},
|
|
148
|
-
"priority": { "strategy": "deep_qualified" }
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
"id": "admin.layout.tabs.item_active",
|
|
152
|
-
"form": "text_color_only",
|
|
153
|
-
"style": {
|
|
154
|
-
"text_color": "钴蓝-5",
|
|
155
|
-
"font_weight": "中等"
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
"id": "admin.layout.tabs.divider",
|
|
160
|
-
"style": {
|
|
161
|
-
"width": "1px",
|
|
162
|
-
"height": "100%",
|
|
163
|
-
"bg": "#F0F0F0",
|
|
164
|
-
"opacity": "1"
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
"id": "admin.layout.tabs.context_menu",
|
|
169
|
-
"style": {
|
|
170
|
-
"width": "120px",
|
|
171
|
-
"border_color": "#F0F0F0",
|
|
172
|
-
"radius": "6px"
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
"id": "admin.content.container",
|
|
177
|
-
"form": "fixed"
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
"id": "admin.layout.content",
|
|
181
|
-
"style": {
|
|
182
|
-
"padding": "24px"
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
"id": "component.card.base",
|
|
187
|
-
"style": {
|
|
188
|
-
"bg_color": "#FFFFFF",
|
|
189
|
-
"radius": "8px",
|
|
190
|
-
"border_color": "transparent",
|
|
191
|
-
"box_shadow": "0 2px 8px rgba(0,0,0,0.06)",
|
|
192
|
-
"padding_x": "0px",
|
|
193
|
-
"padding_y": "0px"
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
"id": "admin.content.page_header",
|
|
198
|
-
"style": {
|
|
199
|
-
"margin_bottom": "1px solid #F0F0F0",
|
|
200
|
-
"title_size": "20px",
|
|
201
|
-
"title_weight": "加粗",
|
|
202
|
-
"desc_size": "14px",
|
|
203
|
-
"desc_color": "#000000A6",
|
|
204
|
-
"actions_gap": "8px",
|
|
205
|
-
"padding_x": "20px",
|
|
206
|
-
"padding_y": "20px"
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
"id": "admin.content.filter_area",
|
|
211
|
-
"style": {
|
|
212
|
-
"radius": "6px",
|
|
213
|
-
"padding_y": "20px",
|
|
214
|
-
"padding_x": "20px",
|
|
215
|
-
"margin_bottom": "1px solid #F0F0F0",
|
|
216
|
-
"bg": "#FFFFFF",
|
|
217
|
-
"border_color": "#F0F0F0",
|
|
218
|
-
"font_size": "14px",
|
|
219
|
-
"text_color": "#000000E6",
|
|
220
|
-
"text_align": "右对齐",
|
|
221
|
-
"icon_size": "16px",
|
|
222
|
-
"gap": "20px"
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
"id": "admin.content.table.operation_bar",
|
|
227
|
-
"style": {
|
|
228
|
-
"gap": "8px",
|
|
229
|
-
"padding_y": "20px 20px 8px",
|
|
230
|
-
"btn_height": "32px",
|
|
231
|
-
"btn_radius": "6px",
|
|
232
|
-
"btn_font_size": "14px",
|
|
233
|
-
"font_size": "14px",
|
|
234
|
-
"font_weight": "常规",
|
|
235
|
-
"text_color": "#000000E6"
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
"id": "admin.content.table.area",
|
|
240
|
-
"style": {
|
|
241
|
-
"radius": "8px",
|
|
242
|
-
"header_bg": "#FAFAFA",
|
|
243
|
-
"header_text": "#000000E6",
|
|
244
|
-
"row_hover_bg": "#FAFAFA",
|
|
245
|
-
"bg_color": "#FFFFFF"
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
"id": "admin.content.table.header_cell",
|
|
250
|
-
"layer": { "padding": "host", "text_align": "inner" },
|
|
251
|
-
"style": {
|
|
252
|
-
"height": "48px",
|
|
253
|
-
"padding": "16px",
|
|
254
|
-
"bg": "#FAFAFA",
|
|
255
|
-
"font_size": "14px",
|
|
256
|
-
"font_weight": "中等",
|
|
257
|
-
"text_color": "#000000E6",
|
|
258
|
-
"text_align": "左对齐",
|
|
259
|
-
"border_bottom": "1px solid #F0F0F0",
|
|
260
|
-
"nowrap": "禁止换行",
|
|
261
|
-
"gap": "8px",
|
|
262
|
-
"icon_size": "16px"
|
|
263
|
-
},
|
|
264
|
-
"priority": { "strategy": "self_plus_important" }
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
"id": "admin.content.table.cell",
|
|
268
|
-
"layer": "host",
|
|
269
|
-
"style": {
|
|
270
|
-
"padding": "16px",
|
|
271
|
-
"height": "55px",
|
|
272
|
-
"font_size": "14px",
|
|
273
|
-
"font_weight": "常规",
|
|
274
|
-
"text_color": "#000000E6",
|
|
275
|
-
"border_bottom": "1px solid #F0F0F0",
|
|
276
|
-
"vertical_align": "居中",
|
|
277
|
-
"nowrap": "禁止换行"
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
"id": "admin.content.table.cell_status",
|
|
282
|
-
"form": "filled",
|
|
283
|
-
"style": {
|
|
284
|
-
"bg": "#F6FFED",
|
|
285
|
-
"border_color": "#D9F7BE",
|
|
286
|
-
"text_color": "#52C41A",
|
|
287
|
-
"font_size": "12px",
|
|
288
|
-
"radius": "6px",
|
|
289
|
-
"icon_size": "16px"
|
|
290
|
-
},
|
|
291
|
-
"structure_ops": [{ "op": "render_branch", "node": "状态列" }]
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
"id": "admin.content.table.cell_amount",
|
|
295
|
-
"layer": "inner",
|
|
296
|
-
"style": {
|
|
297
|
-
"align": "右对齐"
|
|
298
|
-
},
|
|
299
|
-
"structure_ops": [{ "op": "bind_source", "node": "金额列", "source": "列类名" }]
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
"id": "admin.content.table.operation_column",
|
|
303
|
-
"style": {
|
|
304
|
-
"link_color": "钴蓝-5",
|
|
305
|
-
"danger_color": "#FF4D4F",
|
|
306
|
-
"divider_color": "#F0F0F0",
|
|
307
|
-
"align": "居中",
|
|
308
|
-
"min_width": "160px",
|
|
309
|
-
"column_width": "160px",
|
|
310
|
-
"nowrap": "禁止换行",
|
|
311
|
-
"bg": "#FFFFFF",
|
|
312
|
-
"box_shadow": "-2px 0 4px rgba(0,0,0,0.06)",
|
|
313
|
-
"font_size": "14px",
|
|
314
|
-
"gap": "8px",
|
|
315
|
-
"radius": "6px",
|
|
316
|
-
"vertical_align": "居中"
|
|
317
|
-
},
|
|
318
|
-
"structure_ops": [
|
|
319
|
-
{ "op": "render_branch", "node": "操作列图标" },
|
|
320
|
-
{ "op": "add_node", "node": "操作列分隔线" }
|
|
321
|
-
]
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
"id": "admin.content.pagination",
|
|
325
|
-
"style": {
|
|
326
|
-
"font_size": "14px",
|
|
327
|
-
"text_color": "#000000A6",
|
|
328
|
-
"gap": "8px",
|
|
329
|
-
"margin_bottom": "20px",
|
|
330
|
-
"padding_y": "8px 20px",
|
|
331
|
-
"active_bg": "钴蓝-5",
|
|
332
|
-
"active_color": "#FFFFFF",
|
|
333
|
-
"border_color": "#D9D9D9",
|
|
334
|
-
"hover_color": "钴蓝-5"
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
"id": "admin.content.empty_state",
|
|
339
|
-
"style": {
|
|
340
|
-
"font_size": "14px",
|
|
341
|
-
"text_color": "#000000A6",
|
|
342
|
-
"icon_size": "40px",
|
|
343
|
-
"padding": "40px"
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
"id": "component.button.primary",
|
|
348
|
-
"style": {
|
|
349
|
-
"bg_color": "钴蓝-5",
|
|
350
|
-
"text_color": "#FFFFFF",
|
|
351
|
-
"border_color": "钴蓝-5",
|
|
352
|
-
"font_weight": "常规",
|
|
353
|
-
"padding_x": "15px",
|
|
354
|
-
"height": "32px",
|
|
355
|
-
"radius": "6px"
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
{
|
|
359
|
-
"id": "component.button.default",
|
|
360
|
-
"style": {
|
|
361
|
-
"bg_color": "#FFFFFF",
|
|
362
|
-
"text_color": "#000000E6",
|
|
363
|
-
"border_color": "#D9D9D9",
|
|
364
|
-
"font_weight": "常规",
|
|
365
|
-
"padding_x": "15px",
|
|
366
|
-
"height": "32px",
|
|
367
|
-
"radius": "6px"
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
"id": "component.button.danger",
|
|
372
|
-
"style": {
|
|
373
|
-
"bg_color": "#FF4D4F",
|
|
374
|
-
"text_color": "#FFFFFF",
|
|
375
|
-
"border_color": "#FF4D4F",
|
|
376
|
-
"font_weight": "常规",
|
|
377
|
-
"padding_x": "12px",
|
|
378
|
-
"height": "32px",
|
|
379
|
-
"radius": "6px"
|
|
380
|
-
}
|
|
381
|
-
},
|
|
382
|
-
{
|
|
383
|
-
"id": "component.tag.status",
|
|
384
|
-
"form": "filled",
|
|
385
|
-
"style": {
|
|
386
|
-
"bg_color": "#F6FFED",
|
|
387
|
-
"text_color": "#52C41A",
|
|
388
|
-
"border_color": "#D9F7BE",
|
|
389
|
-
"font_size": "12px",
|
|
390
|
-
"radius": "6px",
|
|
391
|
-
"icon_size": "16px"
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
{
|
|
395
|
-
"id": "component.input.base",
|
|
396
|
-
"style": {
|
|
397
|
-
"bg_color": "#FFFFFF",
|
|
398
|
-
"text_color": "#000000E6",
|
|
399
|
-
"border_color": "#D9D9D9",
|
|
400
|
-
"radius": "6px",
|
|
401
|
-
"height": "32px",
|
|
402
|
-
"font_size": "14px",
|
|
403
|
-
"icon_size": "16px",
|
|
404
|
-
"padding_x": "12px"
|
|
405
|
-
}
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
"id": "component.pagination.base",
|
|
409
|
-
"style": {
|
|
410
|
-
"height": "32px",
|
|
411
|
-
"gap": "8px",
|
|
412
|
-
"radius": "6px",
|
|
413
|
-
"active_bg": "钴蓝-5",
|
|
414
|
-
"active_color": "#FFFFFF",
|
|
415
|
-
"border_color": "#D9D9D9",
|
|
416
|
-
"text_color": "#000000E6",
|
|
417
|
-
"hover_color": "钴蓝-5",
|
|
418
|
-
"icon_size": "16px"
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
],
|
|
422
|
-
"oracle": {
|
|
423
|
-
"mockup": "project-ui-design/references/page-specs/styles/default/scenes/admin/mockups/list-page/运单管理-钴蓝.html",
|
|
424
|
-
"sids": [
|
|
425
|
-
"admin.layout.sidebar",
|
|
426
|
-
"admin.layout.sidebar.logo_area",
|
|
427
|
-
"admin.layout.sidebar.menu",
|
|
428
|
-
"admin.layout.header",
|
|
429
|
-
"admin.layout.header.collapse_trigger",
|
|
430
|
-
"admin.layout.header.lang_switch",
|
|
431
|
-
"admin.layout.header.fullscreen",
|
|
432
|
-
"admin.layout.header.setting_button",
|
|
433
|
-
"admin.layout.header.user_profile",
|
|
434
|
-
"admin.layout.tabs",
|
|
435
|
-
"admin.layout.tabs.home_tab",
|
|
436
|
-
"admin.layout.tabs.item",
|
|
437
|
-
"admin.layout.tabs.item_active",
|
|
438
|
-
"admin.layout.tabs.divider",
|
|
439
|
-
"admin.layout.content",
|
|
440
|
-
"admin.content.container",
|
|
441
|
-
"component.card.base",
|
|
442
|
-
"admin.content.page_header",
|
|
443
|
-
"admin.content.filter_area",
|
|
444
|
-
"admin.content.table.operation_bar",
|
|
445
|
-
"admin.content.table.area",
|
|
446
|
-
"admin.content.table.header_cell",
|
|
447
|
-
"admin.content.table.cell",
|
|
448
|
-
"admin.content.table.cell_status",
|
|
449
|
-
"admin.content.table.cell_amount",
|
|
450
|
-
"admin.content.table.operation_column",
|
|
451
|
-
"admin.content.pagination",
|
|
452
|
-
"component.button.primary",
|
|
453
|
-
"component.button.default",
|
|
454
|
-
"component.button.danger",
|
|
455
|
-
"component.tag.status",
|
|
456
|
-
"component.input.base",
|
|
457
|
-
"component.pagination.base"
|
|
458
|
-
]
|
|
459
|
-
},
|
|
460
|
-
"coverage": {
|
|
461
|
-
"registry": "project-ui-design/references/page-specs/styles/default/scenes/admin/restore-registry.json",
|
|
462
|
-
"registrable": 186,
|
|
463
|
-
"applicable": 186,
|
|
464
|
-
"covered": 186,
|
|
465
|
-
"gaps": []
|
|
466
|
-
},
|
|
467
|
-
"notes": "钴蓝主题 admin 列表页完整还原变更集(首版,待 verify 验收)。值优先取效果图精确字面量;官方主色走语义值 钴蓝-5。侧栏为深色 #001529(钴蓝主题壳层约定)。cell_thumb 列本页未出现故未声明;sidebar.collapse_trigger / header.logo_area 按出厂中性收口(visible:false)。"
|
|
468
|
-
}
|