@qilitt-mickey/vue3-temp-skill 1.1.50 → 1.1.51

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qilitt-mickey/vue3-temp-skill",
3
- "version": "1.1.50",
3
+ "version": "1.1.51",
4
4
  "type": "module",
5
5
  "description": "Vue 3 企业级中后台项目开发规范技能包 — core-kernel 架构、按需功能模块",
6
6
  "bin": {
@@ -41,7 +41,9 @@
41
41
  { "id": "admin.layout.tabs", "form": "chrome", "style": { "bg": "#FFFFFF" } },
42
42
  {
43
43
  "id": "admin.content.table.header_cell",
44
- "style": { "height": "48px", "bg": "#FAFAFA", "font_weight": "中等" }
44
+ "style": { "height": "48px", "padding": "16px", "bg": "#FAFAFA", "font_weight": "中等", "text_align": "左对齐", "nowrap": "不换行" },
45
+ "layer": { "height": "host", "padding": "host", "bg": "host", "font_weight": "host", "text_align": "inner", "nowrap": "inner" },
46
+ "suppress_inner": true
45
47
  },
46
48
  {
47
49
  "id": "admin.content.table.cell",
@@ -54,6 +56,7 @@
54
56
  "form": "filled",
55
57
  "style": { "bg": "#E8F3FF", "text_color": "钴蓝-5", "radius": "小圆角" }
56
58
  },
59
+ { "id": "component.tag.status", "form": "filled", "style": { "height": "24px", "radius": "小圆角", "bg_color": "#F6FFED" } },
57
60
  {
58
61
  "id": "admin.content.page_header",
59
62
  "style": { "title_size": "20px", "title_weight": "加粗", "desc_color": "钴蓝-3", "actions_gap": "8px" }
@@ -74,7 +77,6 @@
74
77
  { "id": "admin.content.table.area", "style": { "header_bg": "#FAFAFA", "row_hover_bg": "#FAFAFA", "radius": "小圆角" } },
75
78
  { "id": "admin.content.table.cell_amount", "style": { "align": "right", "font_variant_numeric": "等宽数字" } },
76
79
  { "id": "admin.content.table.cell_thumb", "style": { "thumb_width": "40px", "thumb_height": "40px", "thumb_radius": "小圆角" } },
77
- { "id": "component.tag.status", "form": "filled", "style": { "height": "24px", "radius": "小圆角" } },
78
80
  { "id": "component.button.danger", "style": { "bg_color": "错误色", "border_color": "错误色", "text_color": "#FFFFFF" } },
79
81
  { "id": "admin.content.pagination", "style": { "active_bg": "钴蓝-5", "active_color": "#FFFFFF" } }
80
82
  ],
@@ -104,10 +106,10 @@
104
106
  ]
105
107
  },
106
108
  "coverage": {
107
- "registry": "registry-sample.json",
108
- "registrable": 6,
109
- "applicable": 4,
110
- "covered": 4,
109
+ "registry": "references/page-specs/styles/default/scenes/admin/restore-registry.json",
110
+ "registrable": 186,
111
+ "applicable": 186,
112
+ "covered": 186,
111
113
  "gaps": []
112
114
  },
113
115
  "pending": [
package/parser/index.js CHANGED
@@ -395,12 +395,16 @@ function matchOne(file, dataset, batchSummary, opts = {}) {
395
395
  // oracle 验收基线(:设计侧给的效果图 + 待比对点位;项目侧落地后跑 verify 逐点 diff)
396
396
  // mockup 相对路径解析以「变更集同目录」为第一基准(协议约定 oracle 产物与变更集同目录),
397
397
  // 其次 cwd / 技能根(历史行为兼容)。
398
- checkOracle(changeset, plan, valueDict, { strict: mode === "restore", filePath: file });
398
+ // G11 oracle 基线闸:restore 模式下返回值非 0 = 阻断(与 G8 同级)
399
+ const oracleGaps = checkOracle(changeset, plan, valueDict, { strict: mode === "restore", filePath: file }) || 0;
399
400
  checkCoverage(changeset, { strict: mode === "restore" });
400
401
  // 登记表覆盖率(子项级细闸):以登记表为权威、当场重算 A/C,设计侧自报的 coverage 不参与
401
- const regGaps = checkRegistryCoverage(changeset, plan, opts);
402
+ // strict(mode=restore)同时驱动 G9 登记表身份闸与 G10 dims 兑现闸
403
+ const regGaps = checkRegistryCoverage(changeset, plan, { ...opts, strict: mode === "restore" });
402
404
  if (batchSummary)
403
- batchSummary.registryGaps += regGaps;
405
+ batchSummary.registryGaps += regGaps + oracleGaps;
406
+ else if (regGaps + oracleGaps > 0)
407
+ process.exitCode = 1;
404
408
 
405
409
  // 输出转换计划
406
410
  console.log(`\n=== 三级匹配结果(scene=${changeset.scene},${plan.length} 条)===`);
@@ -735,11 +739,12 @@ function priorityRequirement(strategy) {
735
739
  function checkOracle(changeset, plan, valueDict, opts = {}) {
736
740
  const oracle = changeset.oracle;
737
741
  if (!oracle || typeof oracle !== "object") {
738
- if (opts.strict)
742
+ if (opts.strict) {
739
743
  console.error(`\n✖ mode=restore 下 oracle 缺失(G8 模式闸要求,见变更集校验段)——基线三件套不完整,先回设计侧补齐。`);
740
- else
741
- console.log(`\n提示:变更集未提供 oracle 验收基线(场景还原应给)—— 落地后无法做 computed style 逐点比对,还原度不可证明,建议回流设计侧补齐。`);
742
- return;
744
+ return 1; // G11:restore 下基线缺失 = 阻断,不再是提示后继续
745
+ }
746
+ console.log(`\n提示:变更集未提供 oracle 验收基线(场景还原应给)—— 落地后无法做 computed style 逐点比对,还原度不可证明,建议回流设计侧补齐。`);
747
+ return 0;
743
748
  }
744
749
 
745
750
  const sids = Array.isArray(oracle.sids) ? oracle.sids : [];
@@ -819,11 +824,22 @@ function checkOracle(changeset, plan, valueDict, opts = {}) {
819
824
 
820
825
  if (issues.length === 0) {
821
826
  console.log(` ✔ oracle 校验通过:主题一致、覆盖度双向闭合`);
822
- return;
827
+ return 0;
828
+ }
829
+ // G11 oracle 基线硬闸(2026-09-21 机制化):restore 模式下基线问题从「⚠ 软提示」升格为阻断。
830
+ // 历史缺口:主题错配 / 欠声明 / 无基线只打 ⚠ 就继续——基线断了仍绿灯交付,
831
+ // verify 无从做 1:1 证明,「细节没还原到位」在交付时不可见。
832
+ if (opts.strict) {
833
+ console.error(`\n✖ oracle 基线问题(G11 · ${issues.length} 类 → 拒绝执行):`);
834
+ for (const it of issues)
835
+ console.error(` ✘ ${it}`);
836
+ console.error(` 处置:按上列各条回设计侧补齐基线(换本主题效果图 / 补 sids 声明 / 声明 theme_mapping 同构异色复用)后重跑。`);
837
+ return 1;
823
838
  }
824
839
  console.log(`\n⚠ oracle 基线问题(${issues.length} 类):`);
825
840
  for (const it of issues)
826
841
  console.log(` ✘ ${it}`);
842
+ return 0;
827
843
  }
828
844
 
829
845
  /**
@@ -867,7 +883,7 @@ function checkCoverage(changeset, opts = {}) {
867
883
  console.log(` ✘ ${it}`);
868
884
  }
869
885
 
870
- /** 依次尝试候选路径,返回首个存在者(相对路径基准:变更集所在目录 → 当前工作目录 → 技能仓库根) */
886
+ /** 依次尝试候选路径,返回首个存在者(相对路径基准:变更集所在目录 → 当前工作目录 → 技能仓库根 → 兄弟设计技能 project-ui-design 根) */
871
887
  function resolveFirstExisting(raw, filePath) {
872
888
  if (!raw || typeof raw !== "string")
873
889
  return null;
@@ -877,6 +893,10 @@ function resolveFirstExisting(raw, filePath) {
877
893
  if (filePath)
878
894
  bases.push(path.dirname(filePath));
879
895
  bases.push(process.cwd(), ROOT);
896
+ // 兄弟技能根:真实设计侧登记表/效果图沉淀在 project-ui-design(设计侧 page-specs),
897
+ // 跨库引用时允许相对该根解析(两侧技能目录同级部署是标准布局)。
898
+ const sibling = path.resolve(ROOT, "..", "project-ui-design");
899
+ bases.push(sibling);
880
900
  for (const base of bases) {
881
901
  const candidate = path.resolve(base, raw);
882
902
  if (existsSync(candidate))
@@ -919,6 +939,12 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
919
939
  console.error(`\n✖ 登记表文件不存在:${opts.registryArg}`);
920
940
  return 1;
921
941
  }
942
+ if (opts.strict) {
943
+ // G9 前置缺口:mode=restore 却解析不到登记表实物(G8 只验了字符串非空)
944
+ console.error(`\n✖ G9 登记表身份闸:mode=restore 但登记表实物不存在(coverage.registry="${raw}" 解析失败)→ 拒绝执行。`);
945
+ console.error(` 覆盖率分母必须来自「方案 × 场景」的真实登记表,否则壳层/表格细粒度点位从分母消失,欠声明不可数。`);
946
+ return 1;
947
+ }
922
948
  console.log(`\n提示:未提供登记表(--registry 或 coverage.registry)—— 组级台账只防「整组忘光」,组内子项被静默丢失无法计数,建议场景还原带登记表。`);
923
949
  return 0;
924
950
  }
@@ -931,6 +957,23 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
931
957
  console.error(`\n✖ 登记表读取/解析失败:${registryPath} —— ${e.message}`);
932
958
  return 1;
933
959
  }
960
+
961
+ // G9 登记表身份闸(2026-09-21 机制化):restore 模式下,登记表必须属于「本变更集声明的方案 × 场景」,
962
+ // 且必须是有 items 的真登记表。历史缺口:G8 只验 coverage.registry 是非空字符串——
963
+ // 拿 6 条的回归夹具 registry-sample.json、甚至别的方案/场景的登记表都能过 G8,
964
+ // 覆盖率分母(R=186 的壳层+表格子项)被偷换成小表,「面包屑/顶栏图标/表头 nowrap」这类点位
965
+ // 从分母里消失 → 欠声明不可数 → 细节还原缺口一路绿灯。凡「身份对不上的表」视同没有表。
966
+ const scheme = typeof changeset.scheme === "string" ? changeset.scheme : "";
967
+ const scene = typeof changeset.scene === "string" ? changeset.scene : "";
968
+ const regScheme = typeof registry.design_scheme === "string" ? registry.design_scheme : "";
969
+ const regScene = typeof registry.scene === "string" ? registry.scene : "";
970
+ const identityMismatch = (regScheme && scheme && regScheme !== scheme) || (regScene && scene && regScene !== scene);
971
+ if (opts.strict && (identityMismatch || !regScheme || !regScene)) {
972
+ console.error(`\n✖ G9 登记表身份闸:登记表 ${path.basename(registryPath)} 属于 scheme="${regScheme || "?"}"/scene="${regScene || "?"}",与变更集 scheme="${scheme || "?"}"/scene="${scene || "?"}" 不一致(或缺身份字段)→ 拒绝执行。`);
973
+ console.error(` 处置:coverage.registry 改指「${scheme} × ${scene}」方案的登记表(如设计侧 page-specs/styles/<方案>/scenes/<场景>/restore-registry.json)。`);
974
+ return 1;
975
+ }
976
+
934
977
  const items = Array.isArray(registry.items) ? registry.items : [];
935
978
  if (items.length === 0) {
936
979
  console.error(`\n✖ 登记表不含 items:${registryPath}`);
@@ -972,11 +1015,56 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
972
1015
  const mockupSids = physicalSids || declaredSids;
973
1016
 
974
1017
  const applicable = items.filter(it => it.always === true || registrySids(it).some(s => mockupSids.has(s)));
975
- const gaps = applicable.filter(it => !registrySids(it).every(s => declared.has(s)));
1018
+
1019
+ // G10 dims 兑现闸(2026-09-21 机制化):「兑现」从「ID 出现」升格为「ID 出现 且 dims 维度全部出现」。
1020
+ // 历史缺口:只数 ID——声明了 admin.content.table.header_cell 却漏掉 nowrap / font_weight /
1021
+ // text_align 等关键维度也算「已兑现」→ 表头竖排(nowrap 丢失)、字重不对(font_weight 丢失)
1022
+ // 这类「点位在、细节丢」的症状一路绿灯。核对口径:dims 中的 form 看 change.form;
1023
+ // 其余维度看 change.style ∪ change.layer 的键;source:none(登记即兑现)、global 伪点位
1024
+ // (全局通道字段名与登记 dims 不同构)、visible:false 隐藏点位不做 dims 机检。
1025
+ const hiddenIds = new Set((changeset.changes || []).filter(c => c && c.visible === false).map(c => c.id));
1026
+ const missingDimsOf = (it, change) => {
1027
+ const dims = it.dims || [];
1028
+ const styleKeys = new Set([...Object.keys(change.style || {}), ...Object.keys(change.layer || {})]);
1029
+ const missing = [];
1030
+ if (dims.includes("form") && change.form === undefined)
1031
+ missing.push("form");
1032
+ for (const d of dims.filter(d => d !== "form")) {
1033
+ if (!styleKeys.has(d))
1034
+ missing.push(d);
1035
+ }
1036
+ return missing;
1037
+ };
1038
+ const dimsGaps = []; // { item, missing }
1039
+ const gaps = applicable.filter(it => {
1040
+ const sids = registrySids(it);
1041
+ if (!sids.every(s => declared.has(s)))
1042
+ return true; // ID 级欠声明(原有缺口口径)
1043
+ if (it.source === "none")
1044
+ return false;
1045
+ const primary = sids.find(s => s !== GLOBAL_PSEUDO_ID);
1046
+ if (!primary)
1047
+ return false; // global 伪点位条目
1048
+ if (hiddenIds.has(primary))
1049
+ return false; // visible:false 点位 dims 豁免(与 oracle 隐藏点位豁免同口径)
1050
+ const change = (changeset.changes || []).find(c => c.id === primary);
1051
+ if (!change)
1052
+ return false; // 经 pending/visible 兑现且无 style 意图条目 → dims 由台账人工闸承载
1053
+ const missing = missingDimsOf(it, change);
1054
+ if (missing.length === 0)
1055
+ return false;
1056
+ dimsGaps.push({ item: it, missing });
1057
+ return false;
1058
+ });
976
1059
 
977
1060
  console.log(`\n=== 登记表覆盖率(子项级细闸 · 当场重算)===`);
978
1061
  console.log(` 登记表:${registryPath}(登记项 R=${items.length})`);
979
- console.log(` 适用 A=${applicable.length} / 已兑现 C=${applicable.length - gaps.length} / 缺口 ${gaps.length}`);
1062
+ console.log(` 适用 A=${applicable.length} / 已兑现 C=${applicable.length - gaps.length} / 缺口 ${gaps.length}${dimsGaps.length > 0 ? ` / dims 缺口 ${dimsGaps.length}(G10)` : ""}`);
1063
+ if (dimsGaps.length > 0) {
1064
+ console.log(` ⚠ dims 兑现缺口 ${dimsGaps.length} 条(ID 已声明、dims 维度未给全):`);
1065
+ for (const { item, missing } of dimsGaps.slice(0, 10))
1066
+ console.log(` ${item.rule} ${registrySids(item).join(" + ")} 缺 dims=[${missing.join(",")}]`);
1067
+ }
980
1068
 
981
1069
  // 实物校验:标注缺口的权威来源是效果图 HTML 里的 data-sid,而不是自报的 oracle.sids
982
1070
  let physicalGap = false;
@@ -1008,17 +1096,24 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
1008
1096
  physicalGap = true;
1009
1097
  }
1010
1098
 
1011
- if (gaps.length === 0 && !physicalGap) {
1012
- console.log(` ✔ 覆盖率通过:C = A,无子项被静默丢失;效果图实物标注闭合`);
1099
+ if (gaps.length === 0 && dimsGaps.length === 0 && !physicalGap) {
1100
+ console.log(` ✔ 覆盖率通过:C = A,无子项被静默丢失;dims 维度全兑现(G10);效果图实物标注闭合`);
1013
1101
  return 0;
1014
1102
  }
1015
1103
 
1016
- if (gaps.length === 0 && physicalGap) {
1104
+ if (gaps.length === 0 && dimsGaps.length === 0 && physicalGap) {
1017
1105
  console.log(`\n✘ 效果图实物标注不闭合 → exit 1(oracle.sids 与效果图 HTML 的 data-sid 不一致 / 恒存在子项缺标注)`);
1018
1106
  console.log(` 处置:给效果图对应元素补 data-sid,或把该登记项降级 source: spec(见 mockup-extraction-spec.md §7)`);
1019
1107
  return 1;
1020
1108
  }
1021
1109
 
1110
+ if (dimsGaps.length > 0 && opts.strict) {
1111
+ console.log(`\n✘ dims 兑现缺口(G10 · ID 已声明但 dims 维度未给全,共 ${dimsGaps.length} 条 → exit 1):`);
1112
+ for (const { item, missing } of dimsGaps)
1113
+ console.log(` ${item.rule} ${registrySids(item).join(" + ")} 缺 dims=[${missing.join(",")}]`);
1114
+ console.log(` 处置:在对应 changes[] 条目的 style/layer 补齐所缺维度取值(值来源见 source 字段);「点位在、细节丢」正是样式细节不还原的机制成因。`);
1115
+ }
1116
+
1022
1117
  const byGroup = new Map();
1023
1118
  for (const it of gaps) {
1024
1119
  const g = it.group || "(未分组)";
@@ -1035,7 +1130,8 @@ function checkRegistryCoverage(changeset, plan, opts = {}) {
1035
1130
  if (!opts.verbose && list.length > shown.length)
1036
1131
  console.log(` …(同组其余 ${list.length - shown.length} 条,加 --verbose 展开)`);
1037
1132
  }
1038
- return gaps.length;
1133
+ // G10 dims 缺口计入返回值:strict 模式下 =「交付不完整」与欠声明同级
1134
+ return gaps.length + (opts.strict ? dimsGaps.length : 0);
1039
1135
  }
1040
1136
 
1041
1137
  /**