@agile-team/wl-skills-ui 1.8.4 → 1.8.6

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/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to **@agile-team/wl-skills-ui** will be documented in this f
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [1.8.6] - 2026-05-17
8
+
9
+ ### Added
10
+
11
+ - **`renderOps` 运行时类型守门**:dev 模式下检测到 `OpItem.type` 不在 `view | edit | del | danger | log | ok | send | chip | link` 集合中时,`console.warn` 一次性提示并按 `link` 兜底渲染。修复下游项目(`strict: false`)传错 type 时图标静默降级为纯文字、毫无察觉的隐性体验问题。production 环境无任何副作用。
12
+
13
+ ## [1.8.5] - 2026-05-17
14
+
15
+ ### Added
16
+
17
+ - **R025**:检测 defineColumns 列中 `options:[]` 纯文本退化,提示升级为 `renderTagSlot` / `renderDictClassifyTag` 彩色标签。
18
+ - **R026**:检测模板中原生 HTML 元素(`<table>/<input>/<select>/<button>/<textarea>`),提示替换为对应 Element Plus 组件以纳入统一风格体系。
19
+ - fixture 测试覆盖 R025/R026(20 条自动化测试)。
20
+
7
21
  ## [1.8.3] - 2026-05-13
8
22
 
9
23
  ### Added
package/README.md CHANGED
@@ -213,15 +213,13 @@ yarn add @agile-team/wl-skills-ui
213
213
 
214
214
  ## 版本亮点
215
215
 
216
- 当前 v1.8.3
217
-
218
- - `scan --only R001,R016` / `--skip R031-R037` 规则级过滤(支持范围展开)
219
- - `exempt init --target src` 智能扫描个性化目录,自动生成 `.wl-exempt.json`
220
- - **修复**搜索区字号统一 12px;必填星号 `* *` 去重
221
- - **scanner fixture 测试集**(16 条,`npm test`)+ **SCSS 链路检查**(`npm run check:scss`)
222
- - `scan --baseline` 一步漂移对比 + **drift.mjs** / MCP `wl_ui_drift`
223
- - **R-rule 单一事实源** `standards/rules.json`(29 条),五向一致性守卫
224
- - **长效治理方案** `docs/governance-long-term.md`
216
+ 当前 v1.8.5
217
+
218
+ - **R025** 语义合规:`options:[]` 退化检测 → 提示升级 `renderTagSlot` / `renderDictClassifyTag`
219
+ - **R026** 原生 HTML 拦截:`<table>/<input>/<select>/<button>/<textarea>` 提示替换 el-* 组件
220
+ - `scan --only/--skip` 规则过滤 + `exempt init` 智能豁免脚手架
221
+ - 搜索区字号统一 12px / 必填星号去重 / 20 fixture 自动化测试
222
+ - `scan --baseline` 漂移对比 + SCSS 链路检查 + R-rule 单一事实源(31 条)
225
223
 
226
224
  历史亮点(v1.7.1):
227
225
 
@@ -166,8 +166,40 @@ function isOpVisible(item) {
166
166
  if (typeof item.show === "function") return item.show();
167
167
  return item.show !== false;
168
168
  }
169
+ var KNOWN_OP_TYPES = /* @__PURE__ */ new Set([
170
+ "view",
171
+ "edit",
172
+ "del",
173
+ "danger",
174
+ "log",
175
+ "ok",
176
+ "send",
177
+ "chip",
178
+ "link"
179
+ ]);
180
+ var __opWarned = /* @__PURE__ */ new Set();
181
+ function warnUnknownOpType(item) {
182
+ let isDev = true;
183
+ try {
184
+ const g = globalThis;
185
+ if (g.process && g.process.env && g.process.env.NODE_ENV === "production") {
186
+ isDev = false;
187
+ }
188
+ } catch {
189
+ }
190
+ if (!isDev) return;
191
+ const t = item.type;
192
+ if (t && KNOWN_OP_TYPES.has(t)) return;
193
+ const key = String(t);
194
+ if (__opWarned.has(key)) return;
195
+ __opWarned.add(key);
196
+ console.warn(
197
+ '[@agile-team/wl-skills-ui renderOps] \u68C0\u6D4B\u5230\u672A\u77E5\u7684 type="' + key + '"\uFF0C\u5C06\u6309 link \u515C\u5E95\u6E32\u67D3\u4E3A\u6587\u5B57\u6309\u94AE\u3002\n \u8BF7\u6539\u4E3A\uFF1Aview | edit | del | danger | log | ok | send | chip | link\n \u8BE6\u89C1 runtime/core/types.ts OpItem \u5B9A\u4E49\u3002'
198
+ );
199
+ }
169
200
  function renderOps(items) {
170
201
  const visible = items.filter(isOpVisible);
202
+ if (visible.length) visible.forEach(warnUnknownOpType);
171
203
  const iconItems = visible.filter((i) => i.type in ICON_PRESETS);
172
204
  const otherItems = visible.filter((i) => !(i.type in ICON_PRESETS));
173
205
  const nodes = [];
@@ -1,5 +1,5 @@
1
- import { renderTagNode, renderClassifyTag, registerColumnAutoMaps, renderRatingLevel, renderBadge } from './chunk-QDTNMM7W.js';
2
- export { registerDictColorMap, registerDictColorMaps, renderDictClassifyTag, setDictResolver } from './chunk-QDTNMM7W.js';
1
+ import { renderTagNode, renderClassifyTag, registerColumnAutoMaps, renderRatingLevel, renderBadge } from './chunk-FMVZ54BY.js';
2
+ export { registerDictColorMap, registerDictColorMaps, renderDictClassifyTag, setDictResolver } from './chunk-FMVZ54BY.js';
3
3
 
4
4
  // runtime/presets/common.ts
5
5
  var RISK_LEVEL_MAP = {
package/es/index.js CHANGED
@@ -1 +1 @@
1
- export { AUDIT_STATUS_MAP, ENABLE_STATUS_MAP, RATING_LEVEL_COLORS, VERIFY_STATUS_MAP, clearColumnAutoMap, defineColumns, getColumnAutoMap, registerColumnAutoMap, registerColumnAutoMaps, registerDictColorMap, registerDictColorMaps, renderAuditStatus, renderBadge, renderClassifyTag, renderCountBadge, renderDangerText, renderDictClassifyTag, renderEnableStatus, renderOps, renderRatingLevel, renderTagNode, renderTagSlot, renderVerifyStatus, setDictResolver } from './chunk-QDTNMM7W.js';
1
+ export { AUDIT_STATUS_MAP, ENABLE_STATUS_MAP, RATING_LEVEL_COLORS, VERIFY_STATUS_MAP, clearColumnAutoMap, defineColumns, getColumnAutoMap, registerColumnAutoMap, registerColumnAutoMaps, registerDictColorMap, registerDictColorMaps, renderAuditStatus, renderBadge, renderClassifyTag, renderCountBadge, renderDangerText, renderDictClassifyTag, renderEnableStatus, renderOps, renderRatingLevel, renderTagNode, renderTagSlot, renderVerifyStatus, setDictResolver } from './chunk-FMVZ54BY.js';
@@ -1,4 +1,4 @@
1
- import { renderTagNode, registerDictColorMaps, registerColumnAutoMaps } from '../chunk-QDTNMM7W.js';
1
+ import { renderTagNode, registerDictColorMaps, registerColumnAutoMaps } from '../chunk-FMVZ54BY.js';
2
2
 
3
3
  // runtime/presets/security.ts
4
4
  var VIOLATION_STATUS_MAP = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-ui",
3
- "version": "1.8.4",
3
+ "version": "1.8.6",
4
4
  "description": "企业级 UI 风格对齐框架 — Vue + Element Plus 项目通用化妆/原生双模式(tokens / element / vendors / layouts / runtime / scanner / fixer / skills)",
5
5
  "type": "module",
6
6
  "main": "./es/index.js",
@@ -0,0 +1,7 @@
1
+ <template><BaseTable :columns="columns" /></template>
2
+ <script setup>
3
+ const columns = [
4
+ { label: "状态", name: "status", options: STATUS_OPTIONS },
5
+ { label: "类型", name: "type", options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }] },
6
+ ]
7
+ </script>
@@ -0,0 +1,7 @@
1
+ <template><BaseTable :columns="columns" /></template>
2
+ <script setup>
3
+ const columns = [
4
+ { label: "状态", name: "status", defaultSlot: ({ row }) => renderDictClassifyTag(row.status, 'dictKey') },
5
+ { label: "名称", name: "name" },
6
+ ]
7
+ </script>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <div>
3
+ <table>
4
+ <tr><td>数据</td></tr>
5
+ </table>
6
+ <input type="text" v-model="name" />
7
+ <select v-model="type">
8
+ <option value="a">A</option>
9
+ </select>
10
+ <button @click="submit">提交</button>
11
+ <textarea v-model="remark"></textarea>
12
+ </div>
13
+ </template>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <el-form>
3
+ <el-form-item label="名称">
4
+ <el-input v-model="name" />
5
+ </el-form-item>
6
+ <el-form-item label="类型">
7
+ <el-select v-model="type"><el-option label="A" value="a" /></el-select>
8
+ </el-form-item>
9
+ <el-button type="primary">提交</el-button>
10
+ </el-form>
11
+ </template>
@@ -17,6 +17,7 @@ import { colorRules } from "./color.mjs";
17
17
  import { dialogRules } from "./dialog.mjs";
18
18
  import { tagRules } from "./tag.mjs";
19
19
  import { componentFamilyRules } from "./componentFamily.mjs";
20
+ import { semanticRules } from "./semantic.mjs";
20
21
 
21
22
  export const BUILT_IN_RULES = [
22
23
  ...tableRules,
@@ -26,6 +27,7 @@ export const BUILT_IN_RULES = [
26
27
  ...dialogRules,
27
28
  ...tagRules,
28
29
  ...componentFamilyRules,
30
+ ...semanticRules,
29
31
  ];
30
32
 
31
33
  const _externalRules = [];
@@ -0,0 +1,94 @@
1
+ /** scanner/rules/semantic.mjs — 语义合规规则:R025 R026
2
+ *
3
+ * R025: defineColumns 列定义中 options:[] 退化为纯文本(应用 renderTagSlot / renderDictClassifyTag)
4
+ * R026: 模板中使用原生 HTML 元素(<table>/<input>/<select>/<button>/<textarea>),
5
+ * 应替换为 Element Plus 组件以纳入统一风格体系
6
+ */
7
+ import { lineOf, issue, findTags } from "./_shared.mjs";
8
+
9
+ export const semanticRules = [
10
+ // R025: options:[] 退化检测
11
+ {
12
+ id: "R025",
13
+ category: "table",
14
+ severity: "warning",
15
+ name: "defineColumns 列使用 options:[] 纯文本渲染状态/分类(应使用 renderTagSlot / renderDictClassifyTag)",
16
+ checkScript(script, file, lineOffset) {
17
+ const issues = [];
18
+ const lines = script.split("\n");
19
+ for (let i = 0; i < lines.length; i++) {
20
+ // 检测 options: [...] 或 options: SOME_VAR
21
+ if (!/\boptions\s*:\s*[\[A-Z]/.test(lines[i])) continue;
22
+ // 排除 parseArgs / CLI 等非列定义上下文
23
+ const blockStart = Math.max(0, i - 8);
24
+ const blockEnd = Math.min(lines.length, i + 5);
25
+ const block = lines.slice(blockStart, blockEnd).join("\n");
26
+ // 必须在列定义上下文中(有 label: 或 name: 或 prop:)
27
+ if (!/\blabel\s*:/.test(block) && !/\bname\s*:/.test(block) && !/\bprop\s*:/.test(block)) continue;
28
+ // 已有渲染器则跳过
29
+ if (/defaultSlot|defaultNode|renderTag|renderDictClassify|renderBadge|renderEnable|cellRenderer/.test(block)) continue;
30
+ // 排除 type: "selection" / "index" / "expand"
31
+ if (/type\s*:\s*["'](selection|index|expand)["']/.test(block)) continue;
32
+ const labelMatch = block.match(/label\s*:\s*["']([^"']+)["']/);
33
+ const label = labelMatch ? labelMatch[1] : "(未知列)";
34
+ issues.push({
35
+ file,
36
+ line: lineOffset + i + 1,
37
+ rule: "R025",
38
+ category: "table",
39
+ severity: "warning",
40
+ description: `列 "${label}" 使用 options:[] 纯文本渲染,视觉无颜色区分`,
41
+ suggestion: `替换为 defaultSlot: ({ row }) => renderDictClassifyTag(row.xxx, 'dictKey') 或 renderTagSlot(row.xxx, MAP),移除 options`,
42
+ });
43
+ }
44
+ return issues;
45
+ },
46
+ },
47
+
48
+ // R026: 原生 HTML 元素应换 Element Plus 组件
49
+ {
50
+ id: "R026",
51
+ category: "form",
52
+ severity: "warning",
53
+ name: "模板使用原生 HTML 元素,应替换为 Element Plus 组件以纳入统一风格体系",
54
+ check(template, file, lineOffset) {
55
+ const issues = [];
56
+ const NATIVE_TO_EL = {
57
+ table: { el: "el-table", reason: "原生 <table> 不受 wl-skills-ui 表格样式覆盖" },
58
+ input: { el: "el-input", reason: "原生 <input> 不受统一 size/密度/圆角控制" },
59
+ select: { el: "el-select", reason: "原生 <select> 不受统一下拉样式控制" },
60
+ textarea: { el: "el-input type=\"textarea\"", reason: "原生 <textarea> 不受统一输入框样式控制" },
61
+ button: { el: "el-button", reason: "原生 <button> 不受统一按钮样式控制" },
62
+ };
63
+ for (const [nativeTag, { el, reason }] of Object.entries(NATIVE_TO_EL)) {
64
+ // 匹配 <table / <input / <select 等原生标签,排除 el-table / el-input 等
65
+ const re = new RegExp(`<${nativeTag}(?:\\s|>|/)`, "g");
66
+ let m;
67
+ while ((m = re.exec(template)) !== null) {
68
+ // 排除已是组件前缀的情况:el-table, el-input 等
69
+ const before = template.slice(Math.max(0, m.index - 10), m.index);
70
+ if (/[-\w]$/.test(before)) continue;
71
+ // 排除注释
72
+ const lineStart = template.lastIndexOf("\n", m.index) + 1;
73
+ const lineText = template.slice(lineStart, m.index);
74
+ if (/<!--/.test(lineText) && !(/-->/.test(lineText))) continue;
75
+ if (/^\s*\/\//.test(lineText)) continue;
76
+ // 排除 slot/template 内嵌的特殊情况(如 <table> 在 rich-text/markdown 中)
77
+ // 但不做过度排除,让开发者自行决定豁免
78
+ issues.push(
79
+ issue(
80
+ file,
81
+ lineOf(template, m.index, lineOffset),
82
+ "R026",
83
+ "form",
84
+ "warning",
85
+ `${reason},建议替换为 <${el}>`,
86
+ `将 <${nativeTag}> 替换为 <${el}>,确保纳入 wl-skills-ui 风格体系`,
87
+ ),
88
+ );
89
+ }
90
+ }
91
+ return issues;
92
+ },
93
+ },
94
+ ];
@@ -387,6 +387,30 @@
387
387
  "autoFixable": false,
388
388
  "scanner": "scanner/rules/componentFamily.mjs",
389
389
  "skills": ["element/component-family"]
390
+ },
391
+ {
392
+ "id": "R025",
393
+ "category": "table",
394
+ "severity": "warning",
395
+ "title": "defineColumns 列禁止用 options:[] 纯文本渲染状态/分类",
396
+ "rationale": "options:[] 是 BaseTable 内建的最简字典渲染,只出文字无颜色区分;应升级为 renderTagSlot / renderDictClassifyTag 以获得彩色标签。",
397
+ "appliesTo": ["defineColumns", "columnsDef"],
398
+ "autoFixable": false,
399
+ "manualReason": "需确定字典 key 和颜色映射",
400
+ "scanner": "scanner/rules/semantic.mjs",
401
+ "skills": ["vendors/base-table", "element/el-table"]
402
+ },
403
+ {
404
+ "id": "R026",
405
+ "category": "form",
406
+ "severity": "warning",
407
+ "title": "模板禁止使用原生 HTML 元素(table/input/select/button/textarea)",
408
+ "rationale": "原生 HTML 元素不受 wl-skills-ui 样式体系覆盖(size/密度/圆角/token),必须替换为对应 Element Plus 组件。",
409
+ "appliesTo": ["<table>", "<input>", "<select>", "<button>", "<textarea>"],
410
+ "autoFixable": false,
411
+ "manualReason": "需根据业务场景选择正确的 el-* 组件和属性",
412
+ "scanner": "scanner/rules/semantic.mjs",
413
+ "skills": ["element/el-form", "element/el-table"]
390
414
  }
391
415
  ]
392
416
  }