@qilitt-mickey/vue3-temp-skill 1.1.76 → 1.2.2
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/README.md +99 -82
- package/SKILL.md +65 -28
- package/bin/cli.js +5 -6
- package/mapping/admin.json +60 -0
- package/package.json +5 -13
- package/references/adapters/_new-adapter.md +88 -0
- package/references/adapters/vue-antdv.md +110 -0
- package/references/adapters/vue-element-plus.md +132 -0
- package/references/design-apply.md +259 -122
- package/scripts/check-landing.mjs +419 -0
- package/scripts/modules.json +2 -2
- package/dist/mapping-type.d.ts +0 -33
- package/dist/theme-vars.js +0 -62
- package/dist/theme-vars.ts +0 -113
- package/dist/theme.css +0 -73
- package/mapping/basic/color.json +0 -35
- package/mapping/basic/radius.json +0 -17
- package/mapping/basic/semantic-values.json +0 -72
- package/mapping/basic/spacing.json +0 -21
- package/mapping/basic/typography.json +0 -21
- package/mapping/component/button.json +0 -90
- package/mapping/component/card.json +0 -31
- package/mapping/component/input.json +0 -42
- package/mapping/component/overlay.json +0 -79
- package/mapping/component/pagination.json +0 -34
- package/mapping/component/table.json +0 -42
- package/mapping/component/tag.json +0 -48
- package/mapping/layout/breakpoint.json +0 -20
- package/mapping/layout/shell-geometry.json +0 -26
- package/mapping/scenes/admin/content.json +0 -497
- package/mapping/scenes/admin/layout.json +0 -577
- package/parser/fixtures/changeset-fullpage-restore.json +0 -259
- package/parser/fixtures/changeset-gap-block.json +0 -24
- package/parser/fixtures/changeset-legacy.json +0 -11
- package/parser/fixtures/changeset-neg1.json +0 -8
- package/parser/fixtures/changeset-neg2.json +0 -10
- package/parser/fixtures/changeset-neg3.json +0 -22
- package/parser/fixtures/changeset-neg4-g8.json +0 -15
- package/parser/fixtures/changeset-neg5-g8-stub.json +0 -26
- package/parser/fixtures/changeset-neg6-g9-identity.json +0 -39
- package/parser/fixtures/changeset-neg7-g10-dims.json +0 -57
- package/parser/fixtures/changeset-neg8-g11-oracle.json +0 -57
- package/parser/fixtures/changeset-neg9-g6-token-chain.json +0 -34
- package/parser/fixtures/changeset-pos-g6-token-chain.json +0 -35
- package/parser/fixtures/changeset-pos-theme-neutral.json +0 -264
- package/parser/fixtures/changeset-pos-theme-nometa.json +0 -260
- package/parser/fixtures/changeset-pos.json +0 -50
- package/parser/fixtures/changeset-registry-gap.json +0 -29
- package/parser/fixtures/changeset-v32-fields.json +0 -71
- package/parser/fixtures/fake-project/src/styles/app.scss +0 -5
- package/parser/fixtures/fake-project/src/views/list.vue +0 -13
- package/parser/fixtures/registry-bad.json +0 -11
- package/parser/fixtures/registry-g6-token-chain.json +0 -10
- package/parser/fixtures/registry-sample.json +0 -15
- package/parser/fixtures/verify/actual-clean.json +0 -35
- package/parser/fixtures/verify/actual.json +0 -33
- package/parser/fixtures/verify/mockup-baseline-admin-list.html +0 -27
- package/parser/fixtures/verify/mockup-g6-content.html +0 -12
- package/parser/fixtures/verify/mockup-neutral.html +0 -26
- package/parser/fixtures/verify/mockup-nometa.html +0 -26
- package/parser/fixtures/verify/oracle.json +0 -37
- package/parser/fixtures/verify/snapshot-latest.json +0 -103
- package/parser/generator.js +0 -178
- package/parser/index.js +0 -2706
- package/parser/loader.js +0 -66
- package/parser/override.js +0 -119
- package/parser/regression.js +0 -301
- package/parser/validator.js +0 -206
- package/parser/watch.js +0 -103
- package/references/steps/maintenance.md +0 -31
- package/references/steps/regression.md +0 -49
- package/references/steps/step0-preflight.md +0 -58
- package/references/steps/step1-match.md +0 -67
- package/references/steps/step1b-audit.md +0 -23
- package/references/steps/step1c-registry.md +0 -29
- package/references/steps/step2-structure.md +0 -73
- package/references/steps/step3-style.md +0 -38
- package/references/steps/step3b-shell.md +0 -63
- package/references/steps/step3c-priority.md +0 -26
- package/references/steps/step4-l3.md +0 -14
- package/references/steps/step5-gates.md +0 -65
- package/references/steps/step6-verify.md +0 -77
- package/scripts/apply-final-gate.mjs +0 -170
- package/scripts/check.mjs +0 -207
- package/scripts/design-audit.mjs +0 -1707
- package/scripts/validate.mjs +0 -106
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 契约落地自检 —— 判断「契约 → 样式 → 挂载」三个闭环在哪一环断了
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* node check-landing.mjs --project <项目根目录>
|
|
8
|
+
* node check-landing.mjs --project . --json
|
|
9
|
+
*
|
|
10
|
+
* 退出码: 0 = 六项全过 / 1 = 有 FAIL 项
|
|
11
|
+
*
|
|
12
|
+
* 设计原则: 纯读取 + 确定性判断。不猜测、不修改、不联网。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
|
|
18
|
+
const IGNORE_DIRS = new Set([
|
|
19
|
+
"node_modules", "dist", ".git", ".vite", "coverage",
|
|
20
|
+
"__pycache__", ".idea", ".vscode", ".workbuddy", "unpackage",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const CSS_FILES = ["ui-tokens.css", "ui-roles.css", "ui-layout.css"];
|
|
24
|
+
const SOURCE_EXT = new Set([".vue", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
25
|
+
const STYLE_EXT = new Set([".css", ".scss", ".less"]);
|
|
26
|
+
|
|
27
|
+
const EXIT_OK = 0;
|
|
28
|
+
const EXIT_FAIL = 1;
|
|
29
|
+
|
|
30
|
+
// ────────────────────────────── 参数解析
|
|
31
|
+
|
|
32
|
+
function parseArgs(argv) {
|
|
33
|
+
const out = { project: process.cwd(), json: false };
|
|
34
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35
|
+
const a = argv[i];
|
|
36
|
+
if (a === "--project" || a === "-p") {
|
|
37
|
+
const v = argv[i + 1];
|
|
38
|
+
if (!v || v.startsWith("--")) {
|
|
39
|
+
console.error("错误: --project 后面必须跟项目根目录路径");
|
|
40
|
+
process.exit(EXIT_FAIL);
|
|
41
|
+
}
|
|
42
|
+
out.project = path.resolve(v);
|
|
43
|
+
i++;
|
|
44
|
+
} else if (a === "--json") {
|
|
45
|
+
out.json = true;
|
|
46
|
+
} else if (a === "--help" || a === "-h") {
|
|
47
|
+
console.log("用法: node check-landing.mjs --project <项目根目录> [--json]");
|
|
48
|
+
process.exit(EXIT_OK);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ────────────────────────────── 文件遍历
|
|
55
|
+
|
|
56
|
+
function walk(dir, extSet, acc = []) {
|
|
57
|
+
let entries;
|
|
58
|
+
try {
|
|
59
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
60
|
+
} catch {
|
|
61
|
+
return acc;
|
|
62
|
+
}
|
|
63
|
+
for (const e of entries) {
|
|
64
|
+
if (e.isDirectory()) {
|
|
65
|
+
if (IGNORE_DIRS.has(e.name)) continue;
|
|
66
|
+
walk(path.join(dir, e.name), extSet, acc);
|
|
67
|
+
} else if (e.isFile()) {
|
|
68
|
+
if (extSet === null || extSet.has(path.extname(e.name).toLowerCase())) {
|
|
69
|
+
acc.push(path.join(dir, e.name));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return acc;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readSafe(p) {
|
|
77
|
+
try {
|
|
78
|
+
return fs.readFileSync(p, "utf-8");
|
|
79
|
+
} catch {
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function rel(root, p) {
|
|
85
|
+
return path.relative(root, p).split(path.sep).join("/");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function exists(p) {
|
|
89
|
+
try {
|
|
90
|
+
fs.statSync(p);
|
|
91
|
+
return true;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ────────────────────────────── 契约侧
|
|
98
|
+
|
|
99
|
+
function findContract(projectRoot) {
|
|
100
|
+
const designDir = path.join(projectRoot, ".design");
|
|
101
|
+
if (!exists(designDir)) return { file: null, reason: "项目根下没有 .design/ 目录" };
|
|
102
|
+
|
|
103
|
+
let names;
|
|
104
|
+
try {
|
|
105
|
+
names = fs.readdirSync(designDir);
|
|
106
|
+
} catch {
|
|
107
|
+
return { file: null, reason: ".design/ 目录无法读取" };
|
|
108
|
+
}
|
|
109
|
+
const hit = names.find((n) => /^visual-contract.*\.json$/.test(n));
|
|
110
|
+
if (!hit) {
|
|
111
|
+
return { file: null, reason: ".design/ 下没有 visual-contract-*.json" };
|
|
112
|
+
}
|
|
113
|
+
return { file: path.join(designDir, hit), name: hit, reason: null };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function inspectContract(contract) {
|
|
117
|
+
const problems = [];
|
|
118
|
+
const warns = [];
|
|
119
|
+
const info = {};
|
|
120
|
+
|
|
121
|
+
if (!contract || typeof contract !== "object") {
|
|
122
|
+
return { ok: false, problems: ["契约内容不是合法 JSON 对象"], warns, info };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
info.schema = contract.schema || "(缺失)";
|
|
126
|
+
if (contract.schema !== "ui.visual-contract/1.0") {
|
|
127
|
+
warns.push(`schema 是 "${contract.schema}",期望 "ui.visual-contract/1.0"`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const meta = contract.meta || {};
|
|
131
|
+
info.scene = meta.scene || "(缺失)";
|
|
132
|
+
info.pageTypes = Array.isArray(meta.pageTypes) ? meta.pageTypes.join(",") : "(缺失)";
|
|
133
|
+
info.mockup = (meta.source && meta.source.mockup) || "(缺失)";
|
|
134
|
+
if (!meta.scene) problems.push("meta.scene 缺失 —— 项目侧无法判断用哪套角色与布局预期");
|
|
135
|
+
if (!meta.source || !meta.source.mockup) {
|
|
136
|
+
problems.push("meta.source.mockup 缺失 —— 目视对照没有比对目标");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const colorTokens = (contract.tokens && contract.tokens.color) || {};
|
|
140
|
+
info.tokenCount = Object.keys(colorTokens).length;
|
|
141
|
+
if (info.tokenCount === 0) problems.push("tokens.color 为空 —— 令牌层没有值");
|
|
142
|
+
|
|
143
|
+
const roles = contract.roles && typeof contract.roles === "object" ? contract.roles : {};
|
|
144
|
+
const roleNames = Object.keys(roles);
|
|
145
|
+
info.roleCount = roleNames.length;
|
|
146
|
+
const emptyRoles = roleNames.filter((n) => {
|
|
147
|
+
const r = roles[n] || {};
|
|
148
|
+
const hasHost = Array.isArray(r.host) && r.host.length > 0;
|
|
149
|
+
const hasInner = Array.isArray(r.inner) && r.inner.length > 0;
|
|
150
|
+
return !hasHost && !hasInner;
|
|
151
|
+
});
|
|
152
|
+
info.emptyRoles = emptyRoles;
|
|
153
|
+
if (roleNames.length === 0) {
|
|
154
|
+
problems.push("roles 为空 —— 角色层没抽出来,三份 CSS 只能生成空壳");
|
|
155
|
+
} else if (emptyRoles.length > 0) {
|
|
156
|
+
problems.push(`这些角色没有任何声明: ${emptyRoles.join(", ")}`);
|
|
157
|
+
}
|
|
158
|
+
if (roleNames.length > 0 && roleNames.length < 5) {
|
|
159
|
+
warns.push(`契约只有 ${roleNames.length} 个角色,很可能设计侧抽取不完整(一页通常 20+ 个角色)`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const layout = contract.layout || {};
|
|
163
|
+
const areas = Array.isArray(layout.areas) ? layout.areas : [];
|
|
164
|
+
info.areaCount = areas.length;
|
|
165
|
+
info.grid = layout.grid || "(缺失)";
|
|
166
|
+
if (areas.length === 0) {
|
|
167
|
+
problems.push("layout.areas 为空 —— 布局层没定,骨架无法生成");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const pages = contract.pages || {};
|
|
171
|
+
info.pageTypesDeclared = Object.keys(pages).join(",");
|
|
172
|
+
if (Object.keys(pages).length === 0) {
|
|
173
|
+
warns.push("pages 为空 —— 没有区域到角色的绑定,区域内部不知道该用什么角色");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return { ok: problems.length === 0, problems, warns, info, roleNames };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ────────────────────────────── CSS 侧
|
|
180
|
+
|
|
181
|
+
function findGeneratedCss(projectRoot) {
|
|
182
|
+
const found = { files: [], pathOf: {} };
|
|
183
|
+
const allFiles = walk(projectRoot, null, []);
|
|
184
|
+
for (const name of CSS_FILES) {
|
|
185
|
+
const hits = allFiles.filter((p) => path.basename(p) === name);
|
|
186
|
+
if (hits.length > 0) {
|
|
187
|
+
found.files.push(hits[0]);
|
|
188
|
+
found.pathOf[name] = hits[0];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return found;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function checkImported(projectRoot, cssFileAbs) {
|
|
195
|
+
const base = path.basename(cssFileAbs);
|
|
196
|
+
const sources = walk(projectRoot, SOURCE_EXT, []);
|
|
197
|
+
const refs = [];
|
|
198
|
+
for (const s of sources) {
|
|
199
|
+
const content = readSafe(s);
|
|
200
|
+
if (content.includes(base)) refs.push(rel(projectRoot, s));
|
|
201
|
+
}
|
|
202
|
+
const styleRefs = [];
|
|
203
|
+
for (const s of walk(projectRoot, STYLE_EXT, [])) {
|
|
204
|
+
if (path.basename(s) === base) continue;
|
|
205
|
+
const content = readSafe(s);
|
|
206
|
+
if (content.includes(base)) styleRefs.push(rel(projectRoot, s));
|
|
207
|
+
}
|
|
208
|
+
return { imported: refs.length > 0 || styleRefs.length > 0, refs, styleRefs };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ────────────────────────────── 挂载侧
|
|
212
|
+
|
|
213
|
+
function expectedClassName(roleName) {
|
|
214
|
+
return (
|
|
215
|
+
"role-" +
|
|
216
|
+
roleName
|
|
217
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
218
|
+
.replace(/\./g, "-")
|
|
219
|
+
.replace(/_/g, "-")
|
|
220
|
+
.toLowerCase()
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function checkMounting(projectRoot, roleNames) {
|
|
225
|
+
const vueFiles = walk(projectRoot, SOURCE_EXT, []);
|
|
226
|
+
const roleClassRe = /\brole-[a-z0-9]+(?:-[a-z0-9]+)*/g;
|
|
227
|
+
const usedClasses = new Map();
|
|
228
|
+
for (const s of vueFiles) {
|
|
229
|
+
const content = readSafe(s);
|
|
230
|
+
const hits = content.match(roleClassRe);
|
|
231
|
+
if (!hits) continue;
|
|
232
|
+
for (const h of hits) {
|
|
233
|
+
if (!usedClasses.has(h)) usedClasses.set(h, new Set());
|
|
234
|
+
usedClasses.get(h).add(rel(projectRoot, s));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const expected = roleNames.map(expectedClassName);
|
|
239
|
+
const mounted = [];
|
|
240
|
+
const missing = [];
|
|
241
|
+
for (let i = 0; i < roleNames.length; i++) {
|
|
242
|
+
if (usedClasses.has(expected[i])) mounted.push(expected[i]);
|
|
243
|
+
else missing.push(`${roleNames[i]} → .${expected[i]}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const layoutClassRe = /\bui-(?:page|region-[a-z0-9]+(?:-[a-z0-9]+)*)\b/g;
|
|
247
|
+
const layoutClasses = new Set();
|
|
248
|
+
for (const s of vueFiles) {
|
|
249
|
+
const content = readSafe(s);
|
|
250
|
+
const hits = content.match(layoutClassRe);
|
|
251
|
+
if (hits) for (const h of hits) layoutClasses.add(h);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
scannedFileCount: vueFiles.length,
|
|
256
|
+
mounted,
|
|
257
|
+
missing,
|
|
258
|
+
layoutClasses: [...layoutClasses],
|
|
259
|
+
hasLayout: layoutClasses.has("ui-page"),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ────────────────────────────── 主流程
|
|
264
|
+
|
|
265
|
+
function run(projectRoot) {
|
|
266
|
+
const checks = [];
|
|
267
|
+
const add = (id, title, status, detail, fix) =>
|
|
268
|
+
checks.push({ id, title, status, detail, fix });
|
|
269
|
+
|
|
270
|
+
if (!exists(projectRoot)) {
|
|
271
|
+
console.error(`项目根目录不存在: ${projectRoot}`);
|
|
272
|
+
process.exit(EXIT_FAIL);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// 1. 契约存在
|
|
276
|
+
const cf = findContract(projectRoot);
|
|
277
|
+
if (!cf.file) {
|
|
278
|
+
add("contract-found", "契约文件存在", "FAIL", cf.reason,
|
|
279
|
+
"设计侧没有落盘契约。检查是否在项目根目录运行、是否完成了第 6 步「出契约」。");
|
|
280
|
+
return { checks, ok: false, projectRoot };
|
|
281
|
+
}
|
|
282
|
+
add("contract-found", "契约文件存在", "PASS", `.design/${cf.name}`, null);
|
|
283
|
+
|
|
284
|
+
// 2. 契约完整
|
|
285
|
+
let contract = null;
|
|
286
|
+
let parsed = null;
|
|
287
|
+
try {
|
|
288
|
+
contract = JSON.parse(readSafe(cf.file));
|
|
289
|
+
} catch (e) {
|
|
290
|
+
add("contract-valid", "契约内容完整", "FAIL", `JSON 解析失败: ${e.message}`,
|
|
291
|
+
"契约是手工编辑过的吗?重新用设计侧生成一份。");
|
|
292
|
+
return { checks, ok: false, projectRoot };
|
|
293
|
+
}
|
|
294
|
+
parsed = inspectContract(contract);
|
|
295
|
+
add(
|
|
296
|
+
"contract-valid",
|
|
297
|
+
"契约内容完整",
|
|
298
|
+
parsed.ok ? "PASS" : "FAIL",
|
|
299
|
+
`场景=${parsed.info.scene} 页型=${parsed.info.pageTypes} 角色=${parsed.info.roleCount} 区域=${parsed.info.areaCount}`,
|
|
300
|
+
parsed.problems.length ? parsed.problems.join(";") : null
|
|
301
|
+
);
|
|
302
|
+
for (const w of parsed.warns || []) {
|
|
303
|
+
add(`contract-warn-${checks.length}`, "契约告警", "WARN", w, null);
|
|
304
|
+
}
|
|
305
|
+
if (!parsed.ok) {
|
|
306
|
+
return { checks, ok: false, projectRoot, contractInfo: parsed.info };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// 3. 三份 CSS 生成
|
|
310
|
+
const css = findGeneratedCss(projectRoot);
|
|
311
|
+
const missingCss = CSS_FILES.filter((n) => !css.pathOf[n]);
|
|
312
|
+
add(
|
|
313
|
+
"css-generated",
|
|
314
|
+
"三份 CSS 已生成",
|
|
315
|
+
missingCss.length === 0 ? "PASS" : "FAIL",
|
|
316
|
+
missingCss.length === 0
|
|
317
|
+
? CSS_FILES.map((n) => rel(projectRoot, css.pathOf[n])).join(" ")
|
|
318
|
+
: `缺: ${missingCss.join(", ")}`,
|
|
319
|
+
missingCss.length
|
|
320
|
+
? "项目侧未完成第 3/4/5 步。按 design-apply.md 的翻译规则生成这三份文件。"
|
|
321
|
+
: null
|
|
322
|
+
);
|
|
323
|
+
if (missingCss.length === 0) {
|
|
324
|
+
// 4. CSS 被引入
|
|
325
|
+
const unimported = [];
|
|
326
|
+
const importDetail = [];
|
|
327
|
+
for (const n of CSS_FILES) {
|
|
328
|
+
const r = checkImported(projectRoot, css.pathOf[n]);
|
|
329
|
+
if (!r.imported) unimported.push(n);
|
|
330
|
+
importDetail.push(`${n}:${r.imported ? "已引入 " + (r.refs[0] || r.styleRefs[0]) : "未引入"}`);
|
|
331
|
+
}
|
|
332
|
+
add(
|
|
333
|
+
"css-imported",
|
|
334
|
+
"三份 CSS 被入口引入",
|
|
335
|
+
unimported.length === 0 ? "PASS" : "FAIL",
|
|
336
|
+
importDetail.join(" | "),
|
|
337
|
+
unimported.length
|
|
338
|
+
? `${unimported.join(", ")} 生成了但没被任何文件 import —— 浏览器不会加载它们。在样式入口 import 一次。`
|
|
339
|
+
: null
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 5. 角色类挂载
|
|
344
|
+
const mount = checkMounting(projectRoot, parsed.roleNames);
|
|
345
|
+
const mountRatio = parsed.roleNames.length
|
|
346
|
+
? mount.mounted.length / parsed.roleNames.length
|
|
347
|
+
: 0;
|
|
348
|
+
add(
|
|
349
|
+
"roles-mounted",
|
|
350
|
+
"角色类挂到模板上",
|
|
351
|
+
mount.mounted.length === parsed.roleNames.length ? "PASS"
|
|
352
|
+
: mount.mounted.length === 0 ? "FAIL" : "WARN",
|
|
353
|
+
`契约 ${parsed.roleNames.length} 个角色,模板挂载 ${mount.mounted.length} 个(覆盖率 ${(mountRatio * 100).toFixed(0)}%);扫描 ${mount.scannedFileCount} 个源文件`,
|
|
354
|
+
mount.mounted.length === 0
|
|
355
|
+
? "没有任何 .vue 文件出现 role-* 类名 —— 三份 CSS 全是死样式(写了规则但没元素承载)。按 adapter 的挂载映射给组件加 class。"
|
|
356
|
+
: mount.missing.length
|
|
357
|
+
? `未挂载 ${mount.missing.length} 个角色(CSS 已生成但页面上没有对应 class,视觉不会变): ${mount.missing.slice(0, 6).join(" | ")}${mount.missing.length > 6 ? " …" : ""}`
|
|
358
|
+
: null
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
// 6. 布局类挂载
|
|
362
|
+
add(
|
|
363
|
+
"layout-mounted",
|
|
364
|
+
"布局类挂到容器上",
|
|
365
|
+
mount.hasLayout ? "PASS" : "FAIL",
|
|
366
|
+
mount.hasLayout
|
|
367
|
+
? `布局类: ${mount.layoutClasses.join(", ")}`
|
|
368
|
+
: "没有任何 .vue 文件出现 .ui-page —— 布局层未接入,骨架仍是项目原有结构",
|
|
369
|
+
mount.hasLayout ? null : "布局组件需加 .ui-page 与 .ui-region-* 类,见 adapter 的「布局占位容器」。"
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
const ok = checks.every((c) => c.status === "PASS");
|
|
373
|
+
return { checks, ok, projectRoot, contractInfo: parsed.info, mount };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ────────────────────────────── 输出
|
|
377
|
+
|
|
378
|
+
function printHuman(result) {
|
|
379
|
+
const L = [];
|
|
380
|
+
L.push("");
|
|
381
|
+
L.push("契约落地自检");
|
|
382
|
+
L.push("=".repeat(64));
|
|
383
|
+
L.push(`项目根: ${result.projectRoot}`);
|
|
384
|
+
L.push("");
|
|
385
|
+
const mark = { PASS: "PASS", WARN: "WARN", FAIL: "FAIL" };
|
|
386
|
+
for (const c of result.checks) {
|
|
387
|
+
L.push(`[${mark[c.status]}] ${c.id} · ${c.title}`);
|
|
388
|
+
if (c.detail) L.push(` ${c.detail}`);
|
|
389
|
+
if (c.fix) L.push(` 修复 → ${c.fix}`);
|
|
390
|
+
}
|
|
391
|
+
L.push("");
|
|
392
|
+
L.push("-".repeat(64));
|
|
393
|
+
const firstFail = result.checks.find((c) => c.status === "FAIL");
|
|
394
|
+
if (result.ok) {
|
|
395
|
+
L.push("结论: 六项全过 —— 契约、样式、挂载三个闭环均已接通。");
|
|
396
|
+
} else if (firstFail) {
|
|
397
|
+
L.push(`结论: 断在「${firstFail.title}」。先修这一项,重跑本脚本看下一项。`);
|
|
398
|
+
L.push("");
|
|
399
|
+
L.push("提醒: 项目侧的修正动作是「改契约或改 adapter 后重新生成」,");
|
|
400
|
+
L.push(" 不要直接改已生成的 CSS —— 下次重新生成会被覆盖。");
|
|
401
|
+
} else {
|
|
402
|
+
L.push("结论: 有 WARN 项,落地链路未全覆盖。");
|
|
403
|
+
}
|
|
404
|
+
L.push("");
|
|
405
|
+
process.stdout.write(L.join("\n") + "\n");
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function main() {
|
|
409
|
+
const args = parseArgs(process.argv.slice(2));
|
|
410
|
+
const result = run(args.project);
|
|
411
|
+
if (args.json) {
|
|
412
|
+
console.log(JSON.stringify(result, null, 2));
|
|
413
|
+
} else {
|
|
414
|
+
printHuman(result);
|
|
415
|
+
}
|
|
416
|
+
process.exit(result.ok ? EXIT_OK : EXIT_FAIL);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
main();
|
package/scripts/modules.json
CHANGED
|
@@ -100,9 +100,9 @@
|
|
|
100
100
|
"packages": []
|
|
101
101
|
},
|
|
102
102
|
"design-apply": {
|
|
103
|
-
"description": "
|
|
103
|
+
"description": "视觉契约落地:读契约 → 选 adapter → 生成令牌/角色/布局三份 CSS → 挂载目视对照",
|
|
104
104
|
"load": ["design-apply", "code-quality"],
|
|
105
|
-
"keywords": ["
|
|
105
|
+
"keywords": ["视觉契约", "契约", "visual-contract", "设计落地", "设计稿", "效果图还原", "UI还原", "换肤", "改主题", "全系统风格", "设计文件", "adapter", "角色样式"],
|
|
106
106
|
"packages": []
|
|
107
107
|
}
|
|
108
108
|
}
|
package/dist/mapping-type.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// AUTO-GENERATED by vue3-temp-skill parser/generator.js — DO NOT EDIT
|
|
2
|
-
|
|
3
|
-
/** 语义 ID 映射条目(结构与 mapping/scenes/**.json 条目一致) */
|
|
4
|
-
export interface SemanticMappingEntry {
|
|
5
|
-
type: 'module' | 'component' | 'element' | 'global_component' | 'global_token';
|
|
6
|
-
selector?: string;
|
|
7
|
-
component?: string;
|
|
8
|
-
file?: string;
|
|
9
|
-
style_scope: string;
|
|
10
|
-
default_props?: Record<string, unknown>;
|
|
11
|
-
children?: string[];
|
|
12
|
-
forms?: Record<string, string>;
|
|
13
|
-
visible_prop?: string;
|
|
14
|
-
css_vars?: Record<string, string>;
|
|
15
|
-
custom_style_map?: Record<string, string>;
|
|
16
|
-
[key: string]: unknown;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** 设计变更集(design.changeset/1.0 输入,兼容历史值 design-changeset/v1) */
|
|
20
|
-
export interface DesignChangeset {
|
|
21
|
-
schema: 'design.changeset/1.0' | 'design-changeset/v1';
|
|
22
|
-
mapping_version?: string;
|
|
23
|
-
scene: string;
|
|
24
|
-
layout_type?: string;
|
|
25
|
-
global?: Record<string, Record<string, string | number>>;
|
|
26
|
-
changes: Array<{
|
|
27
|
-
id: string;
|
|
28
|
-
visible?: boolean;
|
|
29
|
-
position?: Record<string, string>;
|
|
30
|
-
form?: string;
|
|
31
|
-
style?: Record<string, string | number>;
|
|
32
|
-
}>;
|
|
33
|
-
}
|
package/dist/theme-vars.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// AUTO-GENERATED by vue3-temp-skill parser/generator.js — DO NOT EDIT
|
|
2
|
-
// 链路 A 运行时入口:themeVars + overrideMapping(业务级覆写)
|
|
3
|
-
// 用法:import { themeVars, overrideMapping } from '@qilitt-mickey/vue3-temp-skill/dist/theme-vars.js';
|
|
4
|
-
// const custom = overrideMapping(themeVars, { color: { brandPrimary: '#ff6600' } });
|
|
5
|
-
export const themeVars = {
|
|
6
|
-
brandPrimary: '',
|
|
7
|
-
brandHover: '',
|
|
8
|
-
brandActive: '',
|
|
9
|
-
sidebarBg: '',
|
|
10
|
-
successColor: '',
|
|
11
|
-
warningColor: '',
|
|
12
|
-
errorColor: '',
|
|
13
|
-
textPrimary: '',
|
|
14
|
-
textSecondary: '',
|
|
15
|
-
textTertiary: '',
|
|
16
|
-
textDisabled: '',
|
|
17
|
-
borderColor: '',
|
|
18
|
-
borderColorSecondary: '',
|
|
19
|
-
componentBg: '',
|
|
20
|
-
layoutBg: '',
|
|
21
|
-
primaryBg: '',
|
|
22
|
-
radiusSm: '4px',
|
|
23
|
-
radiusBase: '6px',
|
|
24
|
-
radiusLg: '8px',
|
|
25
|
-
dialogRadius: '10px',
|
|
26
|
-
switchRadius: '10px',
|
|
27
|
-
spaceUnit: '4px',
|
|
28
|
-
spacingSm: '8px',
|
|
29
|
-
spacingMd: '16px',
|
|
30
|
-
spacingLg: '24px',
|
|
31
|
-
contentPadding: '8px',
|
|
32
|
-
cardPadding: '24px',
|
|
33
|
-
controlPaddingX: '12px',
|
|
34
|
-
buttonPaddingX: '15px',
|
|
35
|
-
headerPaddingX: '16px',
|
|
36
|
-
fontFamily: '系统栈(PingFang SC / Microsoft YaHei 等)',
|
|
37
|
-
fontSizeSm: '12px',
|
|
38
|
-
fontSizeBase: '14px',
|
|
39
|
-
fontSizeLg: '16px',
|
|
40
|
-
fontSizeXl: '20px',
|
|
41
|
-
fontWeightRegular: '400',
|
|
42
|
-
fontWeightMedium: '500',
|
|
43
|
-
fontWeightSemibold: '600',
|
|
44
|
-
lineHeight: '1.5715',
|
|
45
|
-
bpMobile: '',
|
|
46
|
-
bpCollapse: '',
|
|
47
|
-
bpExpand: '',
|
|
48
|
-
unocssSm: '',
|
|
49
|
-
unocssMd: '',
|
|
50
|
-
unocssLg: '',
|
|
51
|
-
unocssXl: '',
|
|
52
|
-
headerHeight: '48px',
|
|
53
|
-
tabsHeight: '32px',
|
|
54
|
-
sidebarWidth: '208px',
|
|
55
|
-
sidebarCollapsedWidth: '64px',
|
|
56
|
-
sidebarCollapseBarHeight: '40px',
|
|
57
|
-
layoutFooterHeight: '48px',
|
|
58
|
-
tabsItemHeight: '46px',
|
|
59
|
-
menuItemHeight: '40px',
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export { overrideMapping, overrideMappings } from '../parser/override.js';
|
package/dist/theme-vars.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// AUTO-GENERATED by vue3-temp-skill parser/generator.js — DO NOT EDIT
|
|
2
|
-
export const themeVars = {
|
|
3
|
-
/** --brand-primary */
|
|
4
|
-
/** --brand-hover */
|
|
5
|
-
/** --brand-active */
|
|
6
|
-
/** --sidebar-bg */
|
|
7
|
-
/** --success-color */
|
|
8
|
-
/** --warning-color */
|
|
9
|
-
/** --error-color */
|
|
10
|
-
/** --text-primary */
|
|
11
|
-
/** --text-secondary */
|
|
12
|
-
/** --text-tertiary */
|
|
13
|
-
/** --text-disabled */
|
|
14
|
-
/** --border-color */
|
|
15
|
-
/** --border-color-secondary */
|
|
16
|
-
/** --component-bg */
|
|
17
|
-
/** --layout-bg */
|
|
18
|
-
/** --primary-bg */
|
|
19
|
-
/** --radius-sm → --border-radius-sm 出厂 4px */
|
|
20
|
-
/** --radius-base → --border-radius-base 出厂 6px */
|
|
21
|
-
/** --radius-lg → --border-radius-lg 出厂 8px */
|
|
22
|
-
/** --dialog-border-radius 出厂 10px */
|
|
23
|
-
/** --switch-border-radius 出厂 10px */
|
|
24
|
-
/** --space-unit 出厂 4px */
|
|
25
|
-
/** --spacing-sm 出厂 8px */
|
|
26
|
-
/** --spacing-md 出厂 16px */
|
|
27
|
-
/** --spacing-lg 出厂 24px */
|
|
28
|
-
/** --layout-content-padding → --vts-margin 出厂 8px */
|
|
29
|
-
/** --card-padding → --vts-card-padding 出厂 24px */
|
|
30
|
-
/** --control-padding-x 出厂 12px */
|
|
31
|
-
/** --button-padding-x 出厂 15px */
|
|
32
|
-
/** --header-padding-x 出厂 16px */
|
|
33
|
-
/** --font-family → --el-font-family 出厂 系统栈(PingFang SC / Microsoft YaHei 等) */
|
|
34
|
-
/** --font-size-sm → --el-font-size-small 出厂 12px */
|
|
35
|
-
/** --font-size-base → --el-font-size-base 出厂 14px */
|
|
36
|
-
/** --font-size-lg → --el-font-size-large 出厂 16px */
|
|
37
|
-
/** --font-size-xl → --el-font-size-extra-large 出厂 20px */
|
|
38
|
-
/** --font-weight-regular → --el-font-weight-primary 出厂 400 */
|
|
39
|
-
/** --font-weight-medium 出厂 500 */
|
|
40
|
-
/** --font-weight-semibold 出厂 600 */
|
|
41
|
-
/** --line-height 出厂 1.5715 */
|
|
42
|
-
/** value token */
|
|
43
|
-
/** value token */
|
|
44
|
-
/** value token */
|
|
45
|
-
/** value token */
|
|
46
|
-
/** value token */
|
|
47
|
-
/** value token */
|
|
48
|
-
/** value token */
|
|
49
|
-
/** --header-height → --vts-bar-height 出厂 48px */
|
|
50
|
-
/** --tabs-height → --vts-tabs-height 出厂 32px */
|
|
51
|
-
/** --sidebar-width 出厂 208px */
|
|
52
|
-
/** --sidebar-collapsed-width 出厂 64px */
|
|
53
|
-
/** --sidebar-collapse-bar-height 出厂 40px */
|
|
54
|
-
/** --layout-footer-height 出厂 48px */
|
|
55
|
-
/** --tabs-item-height 出厂 46px */
|
|
56
|
-
/** --el-menu-item-height 出厂 40px */
|
|
57
|
-
brandPrimary: '' as string,
|
|
58
|
-
brandHover: '' as string,
|
|
59
|
-
brandActive: '' as string,
|
|
60
|
-
sidebarBg: '' as string,
|
|
61
|
-
successColor: '' as string,
|
|
62
|
-
warningColor: '' as string,
|
|
63
|
-
errorColor: '' as string,
|
|
64
|
-
textPrimary: '' as string,
|
|
65
|
-
textSecondary: '' as string,
|
|
66
|
-
textTertiary: '' as string,
|
|
67
|
-
textDisabled: '' as string,
|
|
68
|
-
borderColor: '' as string,
|
|
69
|
-
borderColorSecondary: '' as string,
|
|
70
|
-
componentBg: '' as string,
|
|
71
|
-
layoutBg: '' as string,
|
|
72
|
-
primaryBg: '' as string,
|
|
73
|
-
radiusSm: '4px' as string,
|
|
74
|
-
radiusBase: '6px' as string,
|
|
75
|
-
radiusLg: '8px' as string,
|
|
76
|
-
dialogRadius: '10px' as string,
|
|
77
|
-
switchRadius: '10px' as string,
|
|
78
|
-
spaceUnit: '4px' as string,
|
|
79
|
-
spacingSm: '8px' as string,
|
|
80
|
-
spacingMd: '16px' as string,
|
|
81
|
-
spacingLg: '24px' as string,
|
|
82
|
-
contentPadding: '8px' as string,
|
|
83
|
-
cardPadding: '24px' as string,
|
|
84
|
-
controlPaddingX: '12px' as string,
|
|
85
|
-
buttonPaddingX: '15px' as string,
|
|
86
|
-
headerPaddingX: '16px' as string,
|
|
87
|
-
fontFamily: '系统栈(PingFang SC / Microsoft YaHei 等)' as string,
|
|
88
|
-
fontSizeSm: '12px' as string,
|
|
89
|
-
fontSizeBase: '14px' as string,
|
|
90
|
-
fontSizeLg: '16px' as string,
|
|
91
|
-
fontSizeXl: '20px' as string,
|
|
92
|
-
fontWeightRegular: '400' as string,
|
|
93
|
-
fontWeightMedium: '500' as string,
|
|
94
|
-
fontWeightSemibold: '600' as string,
|
|
95
|
-
lineHeight: '1.5715' as string,
|
|
96
|
-
bpMobile: '' as string,
|
|
97
|
-
bpCollapse: '' as string,
|
|
98
|
-
bpExpand: '' as string,
|
|
99
|
-
unocssSm: '' as string,
|
|
100
|
-
unocssMd: '' as string,
|
|
101
|
-
unocssLg: '' as string,
|
|
102
|
-
unocssXl: '' as string,
|
|
103
|
-
headerHeight: '48px' as string,
|
|
104
|
-
tabsHeight: '32px' as string,
|
|
105
|
-
sidebarWidth: '208px' as string,
|
|
106
|
-
sidebarCollapsedWidth: '64px' as string,
|
|
107
|
-
sidebarCollapseBarHeight: '40px' as string,
|
|
108
|
-
layoutFooterHeight: '48px' as string,
|
|
109
|
-
tabsItemHeight: '46px' as string,
|
|
110
|
-
menuItemHeight: '40px' as string,
|
|
111
|
-
} as const;
|
|
112
|
-
|
|
113
|
-
export type ThemeVarKey = keyof typeof themeVars;
|