@agile-team/wl-skills-ui 1.6.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/CHANGELOG.md +207 -0
- package/README.md +623 -0
- package/SKILL.md +632 -0
- package/bin/wl-ui.js +794 -0
- package/design/spec/color.md +41 -0
- package/design/spec/spacing.md +28 -0
- package/design/spec/typography.md +29 -0
- package/design/tokens/base.css +144 -0
- package/design/tokens/index.css +8 -0
- package/dist/ag-grid-override.scss +1 -0
- package/dist/element.scss +1 -0
- package/dist/index.scss +1 -0
- package/dist/portal.scss +1 -0
- package/dist/style-override.scss +1 -0
- package/dist/tokens.css +144 -0
- package/es/chunk-GBB7LDKQ.js +261 -0
- package/es/chunk-MDI2SCUP.js +281 -0
- package/es/chunk-QDTNMM7W.js +286 -0
- package/es/chunk-YLNAFAWE.js +233 -0
- package/es/common-preset.d.ts +38 -0
- package/es/common-preset.js +157 -0
- package/es/index.d.ts +32 -0
- package/es/index.js +1 -0
- package/es/presets/security.d.ts +16 -0
- package/es/presets/security.js +99 -0
- package/es/renderers-BFRSp2BH.d.ts +51 -0
- package/es/renderers-DMZbi_Gs.d.ts +51 -0
- package/es/renderers-DWhUlI2y.d.ts +47 -0
- package/es/types-04qLKHMt.d.ts +62 -0
- package/es/types-BrCPjAH6.d.ts +62 -0
- package/es/types-QZ1gy9KX.d.ts +63 -0
- package/examples/migration-operations-to-renderOps.md +107 -0
- package/examples/wk-exempt.example.json +30 -0
- package/mcp/server.js +394 -0
- package/package.json +130 -0
- package/reference/SelectPopupCom.vue +634 -0
- package/reference/ag-cell-renders.ts +644 -0
- package/reference/define-columns.ts +185 -0
- package/reference/jhlc.d.ts +1 -0
- package/scanner/coverage.mjs +287 -0
- package/scanner/exempt.mjs +127 -0
- package/scanner/fix.mjs +216 -0
- package/scanner/index.mjs +403 -0
- package/scanner/integration.mjs +155 -0
- package/scanner/report.mjs +459 -0
- package/scanner/rules/_shared.mjs +134 -0
- package/scanner/rules/button.mjs +97 -0
- package/scanner/rules/color.mjs +118 -0
- package/scanner/rules/componentFamily.mjs +184 -0
- package/scanner/rules/dialog.mjs +42 -0
- package/scanner/rules/form.mjs +86 -0
- package/scanner/rules/index.mjs +58 -0
- package/scanner/rules/table.mjs +170 -0
- package/scanner/rules/tag.mjs +245 -0
- package/scanner/snapshot.mjs +198 -0
- package/skills/_flows/full-audit.md +65 -0
- package/skills/_flows/legacy-skin-align.md +71 -0
- package/skills/_flows/new-project-init.md +80 -0
- package/skills/_flows/progressive-migrate.md +49 -0
- package/skills/_meta/_compat/README.md +47 -0
- package/skills/_meta/_compat/editors.json +76 -0
- package/skills/_meta/_compat/headers/agents.txt +7 -0
- package/skills/_meta/_compat/headers/claude-code.txt +7 -0
- package/skills/_meta/_compat/headers/cline.txt +7 -0
- package/skills/_meta/_compat/headers/cursor-mdc.txt +5 -0
- package/skills/_meta/_compat/headers/github-copilot.txt +5 -0
- package/skills/_meta/_compat/headers/kiro.txt +4 -0
- package/skills/_meta/_compat/headers/qoder.txt +4 -0
- package/skills/_meta/_compat/headers/trae.txt +3 -0
- package/skills/_meta/_compat/headers/windsurf.txt +5 -0
- package/skills/_meta/_detection.md +81 -0
- package/skills/_meta/_registry.md +96 -0
- package/skills/element/component-family/SKILL.md +71 -0
- package/skills/element/el-dialog/SKILL.md +124 -0
- package/skills/element/el-form/SKILL.md +84 -0
- package/skills/element/el-table/SKILL.md +86 -0
- package/skills/element/el-tag/SKILL.md +127 -0
- package/skills/layouts/detail-page/SKILL.md +99 -0
- package/skills/layouts/form-dialog/SKILL.md +128 -0
- package/skills/layouts/list-page/SKILL.md +111 -0
- package/skills/layouts/tree-list/SKILL.md +108 -0
- package/skills/ops/audit/SKILL.md +109 -0
- package/skills/ops/fix/SKILL.md +52 -0
- package/skills/ops/fix/USAGE.md +24 -0
- package/skills/ops/migrate/SKILL.md +157 -0
- package/skills/ops/scan/SKILL.md +71 -0
- package/skills/ops/scan/USAGE.md +16 -0
- package/skills/runtime/design-tokens/SKILL.md +88 -0
- package/skills/runtime/design-tokens/USAGE.md +19 -0
- package/skills/runtime/migration/SKILL.md +106 -0
- package/skills/runtime/migration/USAGE.md +34 -0
- package/skills/runtime/style-align/SKILL.md +124 -0
- package/skills/runtime/style-align/USAGE.md +52 -0
- package/skills/vendors/ag-grid/SKILL.md +94 -0
- package/skills/vendors/base-table/SKILL.md +57 -0
- package/skills/vendors/c-components/SKILL.md +36 -0
- package/skills/vendors/custom-wrappers/SKILL.md +45 -0
- package/skills/vendors/jh-components/SKILL.md +42 -0
- package/skills/vendors/unknown-wrapper/SKILL.md +44 -0
- package/standards/engineering/01-import-order.md +46 -0
- package/standards/engineering/02-naming.md +54 -0
- package/standards/engineering/03-scss-structure.md +59 -0
- package/standards/ui/01-table.md +111 -0
- package/standards/ui/02-button.md +83 -0
- package/standards/ui/03-form.md +118 -0
- package/standards/ui/04-tag-status.md +110 -0
- package/standards/ui/05-dialog-pagination.md +118 -0
- package/styles/element/_card.scss +26 -0
- package/styles/element/_descriptions.scss +22 -0
- package/styles/element/_dialog.scss +43 -0
- package/styles/element/_drawer.scss +22 -0
- package/styles/element/_feedback.scss +52 -0
- package/styles/element/_form.scss +22 -0
- package/styles/element/_navigation.scss +36 -0
- package/styles/element/_overlay.scss +32 -0
- package/styles/element/_pagination.scss +16 -0
- package/styles/element/_steps.scss +32 -0
- package/styles/element/_table.scss +13 -0
- package/styles/element/_tabs.scss +40 -0
- package/styles/element/_tree.scss +27 -0
- package/styles/element/_upload.scss +34 -0
- package/styles/element/index.scss +17 -0
- package/styles/index.scss +21 -0
- package/styles/layouts/_detail-page.scss +27 -0
- package/styles/layouts/_form-dialog.scss +19 -0
- package/styles/layouts/_list-page.scss +47 -0
- package/styles/layouts/_tree-list.scss +33 -0
- package/styles/layouts/index.scss +6 -0
- package/styles/presets/element-only.scss +6 -0
- package/styles/presets/full.scss +12 -0
- package/styles/presets/security.scss +37 -0
- package/styles/presets/skin.scss +14 -0
- package/styles/presets/tokens-only.scss +7 -0
- package/styles/tokens/index.scss +79 -0
- package/styles/vendors/_ag-grid.scss +273 -0
- package/styles/vendors/_base-components.scss +480 -0
- package/styles/vendors/_base-query-toolbar.scss +253 -0
- package/styles/vendors/_base-table.scss +41 -0
- package/styles/vendors/_c-components.scss +34 -0
- package/styles/vendors/_custom-wrappers.scss +30 -0
- package/styles/vendors/_jh-drag-col.scss +63 -0
- package/styles/vendors/_jh-pagination.scss +81 -0
- package/styles/vendors/_jh-tree.scss +196 -0
- package/styles/vendors/_portal.scss +114 -0
- package/styles/vendors/index.scss +17 -0
- package/templates/ag-grid-page/TPL-AG-GRID.md +76 -0
- package/templates/form-dialog/TPL-FORM-DIALOG.md +165 -0
- package/templates/list-page/TPL-LIST.md +139 -0
- package/templates/tree-list/TPL-TREE-LIST.md +176 -0
- package/templates/ui-optimization-report/TPL-UI-OPTIM-REPORT.md +343 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineColumns — 全局列自动渲染器
|
|
3
|
+
*
|
|
4
|
+
* 原理:维护一张「字段名 → 渲染配置」的全局映射表(COLUMN_AUTO_MAP)。
|
|
5
|
+
* 调用 defineColumns(colList) 时,对每一列检查 name 是否命中映射:
|
|
6
|
+
* - 命中 → 自动补充 width / fixed / defaultNode / defaultSlot
|
|
7
|
+
* - 未命中 → 原样返回
|
|
8
|
+
* - 用户在列定义里手写了对应属性 → 手写优先(不会被覆盖)
|
|
9
|
+
*
|
|
10
|
+
* 使用方式(替换原来的 return [...]):
|
|
11
|
+
* import { defineColumns } from '@/util/define-columns';
|
|
12
|
+
*
|
|
13
|
+
* columnsDef(): TableColumnDesc<any>[] {
|
|
14
|
+
* return defineColumns([
|
|
15
|
+
* { label: '启用状态', name: 'enableStatus' }, // 自动:width=80 fixed=right 彩色Tag
|
|
16
|
+
* { label: '风险分级', name: 'riskLevel' }, // 自动:width=90 fixed=right 彩色Tag
|
|
17
|
+
* { label: '风险编号', name: 'riskNo' }, // 自动:蓝色圆角徽标
|
|
18
|
+
* { label: '评价级别', name: 'ratingLevel' }, // 自动:彩色圆形徽标
|
|
19
|
+
* { label: '审批状态', name: 'approvalStatus' }, // 自动:彩色Tag
|
|
20
|
+
* ]);
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* 如需某列不走自动逻辑,手写 defaultNode/defaultSlot 即可覆盖自动配置:
|
|
24
|
+
* { label: '风险分级', name: 'riskLevel', defaultNode: ({ row }: { row: any }) => myCustomRender(row) }
|
|
25
|
+
*
|
|
26
|
+
* 新增全局字段映射 → 只改本文件的 COLUMN_AUTO_MAP,全项目所有页面立即生效。
|
|
27
|
+
*/
|
|
28
|
+
import type { TableColumnDesc } from "@jhlc/common-core/src/components/table/base-table/type";
|
|
29
|
+
import {
|
|
30
|
+
renderEnableStatus,
|
|
31
|
+
renderAuditStatus,
|
|
32
|
+
renderVerifyStatus,
|
|
33
|
+
renderRiskLevel,
|
|
34
|
+
renderBadge,
|
|
35
|
+
renderRatingLevel,
|
|
36
|
+
renderTaskStatus,
|
|
37
|
+
renderPlanStatus,
|
|
38
|
+
renderFlagNormal,
|
|
39
|
+
renderRiskStatus,
|
|
40
|
+
renderCorrectStatus,
|
|
41
|
+
renderPermitStatus,
|
|
42
|
+
renderTrainStatus,
|
|
43
|
+
renderCredentialStatus,
|
|
44
|
+
renderQuestionStatus
|
|
45
|
+
} from "./ag-cell-renders";
|
|
46
|
+
|
|
47
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
48
|
+
// 全局字段渲染映射表
|
|
49
|
+
// key = 字段名(与 columnsDef 中的 name 完全匹配)
|
|
50
|
+
// value = 要自动补充的列配置(不含 label,label 由页面自定义)
|
|
51
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
52
|
+
const COLUMN_AUTO_MAP: Record<string, Partial<TableColumnDesc<any>>> = {
|
|
53
|
+
// ── 启用/停用 ────────────────────────────────────────────────────────────
|
|
54
|
+
enableStatus: {
|
|
55
|
+
width: 80,
|
|
56
|
+
fixed: "right",
|
|
57
|
+
defaultNode: ({ row }: { row: any }) =>
|
|
58
|
+
renderEnableStatus(row.enableStatus),
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// ── 审批状态 ─────────────────────────────────────────────────────────────
|
|
62
|
+
approvalStatus: {
|
|
63
|
+
width: 90,
|
|
64
|
+
fixed: "right",
|
|
65
|
+
defaultNode: ({ row }: { row: any }) =>
|
|
66
|
+
renderAuditStatus(row.approvalStatus),
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// ── 核实状态 ─────────────────────────────────────────────────────────────
|
|
70
|
+
verifyStatus: {
|
|
71
|
+
width: 90,
|
|
72
|
+
fixed: "right",
|
|
73
|
+
defaultNode: ({ row }: { row: any }) =>
|
|
74
|
+
renderVerifyStatus(row.verifyStatus),
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// ── 风险分级(aq_risk_level 字典:1重大/2较大/3一般/4低)──────────────────
|
|
78
|
+
riskLevel: {
|
|
79
|
+
width: 90,
|
|
80
|
+
fixed: "right",
|
|
81
|
+
defaultNode: ({ row }: { row: any }) => renderRiskLevel(row.riskLevel),
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// ── 风险编号:蓝色圆角徽标 ───────────────────────────────────────────────
|
|
85
|
+
riskNo: {
|
|
86
|
+
defaultSlot: ({ row }: { row: any }) => renderBadge(row.riskNo),
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// ── 检查表编号:蓝色圆角徽标 ─────────────────────────────────────────────
|
|
90
|
+
checkNo: {
|
|
91
|
+
defaultSlot: ({ row }: { row: any }) => renderBadge(row.checkNo),
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// ── 评价级别(LEC/MES 等):彩色圆形徽标 ────────────────────────────────
|
|
95
|
+
ratingLevel: {
|
|
96
|
+
width: 80,
|
|
97
|
+
defaultSlot: ({ row }: { row: any }) => renderRatingLevel(row.ratingLevel),
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// ── 排查/检查任务状态(aq_task_check_status / aq_check_info_status)────────
|
|
101
|
+
taskStatus: {
|
|
102
|
+
width: 90,
|
|
103
|
+
fixed: "right",
|
|
104
|
+
defaultNode: ({ row }: { row: any }) => renderTaskStatus(row.taskStatus),
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// ── 计划状态(aq_plan_status: 1=启用, 0=停用)────────────────────────────
|
|
108
|
+
planStatus: {
|
|
109
|
+
width: 90,
|
|
110
|
+
fixed: "right",
|
|
111
|
+
defaultNode: ({ row }: { row: any }) => renderPlanStatus(row.planStatus),
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// ── 排查/检查结果(aq_handle_result: 1=正常, 2=异常)────────────────────
|
|
115
|
+
flagNormal: {
|
|
116
|
+
width: 90,
|
|
117
|
+
fixed: "right",
|
|
118
|
+
defaultNode: ({ row }: { row: any }) => renderFlagNormal(row.flagNormal),
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// ── 隐患状态(aq_risk_status)────────────────────────────────────────────
|
|
122
|
+
riskStatus: {
|
|
123
|
+
width: 90,
|
|
124
|
+
fixed: "right",
|
|
125
|
+
defaultNode: ({ row }: { row: any }) => renderRiskStatus(row.riskStatus),
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// ── 整改状态(aq_correct_status)──────────────────────────────────────────
|
|
129
|
+
correctStatus: {
|
|
130
|
+
width: 90,
|
|
131
|
+
fixed: "right",
|
|
132
|
+
defaultNode: ({ row }: { row: any }) =>
|
|
133
|
+
renderCorrectStatus(row.correctStatus),
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// ── 危险作业票状态(permitStatus: 1=草稿, 2=待审批, 3=作业中, 4=已完工, 5=已撤销)──
|
|
137
|
+
permitStatus: {
|
|
138
|
+
width: 90,
|
|
139
|
+
fixed: "right",
|
|
140
|
+
defaultNode: ({ row }: { row: any }) =>
|
|
141
|
+
renderPermitStatus(row.permitStatus),
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// ── 培训状态(trainStatus: 1=未完成, 2=已完成)────────────────────────────
|
|
145
|
+
trainStatus: {
|
|
146
|
+
width: 90,
|
|
147
|
+
fixed: "right",
|
|
148
|
+
defaultNode: ({ row }: { row: any }) => renderTrainStatus(row.trainStatus),
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// ── 证书状态(credentialStatus: 1=有效, 2=即将过期, 3=已过期)──────────────
|
|
152
|
+
credentialStatus: {
|
|
153
|
+
width: 90,
|
|
154
|
+
fixed: "right",
|
|
155
|
+
defaultNode: ({ row }: { row: any }) =>
|
|
156
|
+
renderCredentialStatus(row.credentialStatus),
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// ── 题库状态(unifyQuestionStatus: 1=草稿, 2=已发布, 3=已停用)──────────────
|
|
160
|
+
unifyQuestionStatus: {
|
|
161
|
+
width: 90,
|
|
162
|
+
fixed: "right",
|
|
163
|
+
defaultNode: ({ row }: { row: any }) =>
|
|
164
|
+
renderQuestionStatus(row.unifyQuestionStatus),
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
169
|
+
// 主函数
|
|
170
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
171
|
+
export function defineColumns<T = any>(
|
|
172
|
+
columns: TableColumnDesc<T>[]
|
|
173
|
+
): TableColumnDesc<T>[] {
|
|
174
|
+
return columns.map((col) => {
|
|
175
|
+
// 多选列:统一宽度 55px(页面已手写 width 时不覆盖)
|
|
176
|
+
if ((col as any).type === "selection" && !(col as any).width) {
|
|
177
|
+
return { width: 55, ...(col as any) } as unknown as TableColumnDesc<T>;
|
|
178
|
+
}
|
|
179
|
+
if (!col.name) return col;
|
|
180
|
+
const auto = COLUMN_AUTO_MAP[col.name as string];
|
|
181
|
+
if (!auto) return col;
|
|
182
|
+
// 自动配置 < 手写配置(手写的属性不被覆盖)
|
|
183
|
+
return { ...auto, ...col } as TableColumnDesc<T>;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Type stubs moved to types/jhlc.d.ts
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
const ELEMENT_SKILL_MAP = {
|
|
2
|
+
"el-table": "element/el-table",
|
|
3
|
+
"el-table-column": "element/el-table",
|
|
4
|
+
"el-form": "element/el-form",
|
|
5
|
+
"el-form-item": "element/el-form",
|
|
6
|
+
"el-input": "element/el-form",
|
|
7
|
+
"el-select": "element/el-form",
|
|
8
|
+
"el-date-picker": "element/el-form",
|
|
9
|
+
"el-button": "element/el-button",
|
|
10
|
+
"el-dialog": "element/el-dialog",
|
|
11
|
+
"el-message-box": "element/el-dialog",
|
|
12
|
+
"el-tag": "element/el-tag",
|
|
13
|
+
"el-pagination": "element/el-pagination",
|
|
14
|
+
"el-card": "element/el-card",
|
|
15
|
+
"el-tabs": "element/el-tabs",
|
|
16
|
+
"el-tab-pane": "element/el-tabs",
|
|
17
|
+
"el-descriptions": "element/el-descriptions",
|
|
18
|
+
"el-descriptions-item": "element/el-descriptions",
|
|
19
|
+
"el-tree": "element/el-tree",
|
|
20
|
+
"el-drawer": "element/el-drawer",
|
|
21
|
+
"el-upload": "element/el-upload",
|
|
22
|
+
"el-steps": "element/el-steps",
|
|
23
|
+
"el-step": "element/el-steps",
|
|
24
|
+
"el-popover": "element/el-overlay",
|
|
25
|
+
"el-tooltip": "element/el-overlay",
|
|
26
|
+
"el-dropdown": "element/el-overlay",
|
|
27
|
+
"el-menu": "element/el-navigation",
|
|
28
|
+
"el-menu-item": "element/el-navigation",
|
|
29
|
+
"el-sub-menu": "element/el-navigation",
|
|
30
|
+
"el-breadcrumb": "element/el-navigation",
|
|
31
|
+
"el-empty": "element/el-feedback",
|
|
32
|
+
"el-result": "element/el-feedback",
|
|
33
|
+
"el-alert": "element/el-feedback",
|
|
34
|
+
"el-badge": "element/el-feedback",
|
|
35
|
+
"el-avatar": "element/el-feedback",
|
|
36
|
+
"el-timeline": "element/el-feedback",
|
|
37
|
+
"el-collapse": "element/el-feedback",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const VENDOR_PATTERNS = [
|
|
41
|
+
{
|
|
42
|
+
pattern: /<\/?Base[A-Z][\w-]*/g,
|
|
43
|
+
vendor: "Base*",
|
|
44
|
+
skill: "vendors/base-table",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
pattern: /<\/?base-[\w-]+/g,
|
|
48
|
+
vendor: "base-*",
|
|
49
|
+
skill: "vendors/base-table",
|
|
50
|
+
},
|
|
51
|
+
{ pattern: /<\/?jh-[\w-]+/g, vendor: "jh-*", skill: "vendors/jh-components" },
|
|
52
|
+
{ pattern: /<\/?C_[\w-]+/g, vendor: "C_*", skill: "vendors/c-components" },
|
|
53
|
+
{ pattern: /<\/?c-[\w-]+/g, vendor: "c-*", skill: "vendors/c-components" },
|
|
54
|
+
{
|
|
55
|
+
pattern: /ag-grid|agGrid|\.ag-root-wrapper/g,
|
|
56
|
+
vendor: "AG Grid",
|
|
57
|
+
skill: "vendors/ag-grid",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const LAYOUT_PATTERNS = [
|
|
62
|
+
{
|
|
63
|
+
pattern: /class=["'][^"']*list-page[^"']*["']/g,
|
|
64
|
+
layout: "list-page",
|
|
65
|
+
skill: "layouts/list-page",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
pattern: /class=["'][^"']*(tree-list|drag-col-container)[^"']*["']/g,
|
|
69
|
+
layout: "tree-list",
|
|
70
|
+
skill: "layouts/tree-list",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
pattern: /<el-dialog[\s\S]*?<el-form\b/g,
|
|
74
|
+
layout: "form-dialog",
|
|
75
|
+
skill: "layouts/form-dialog",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
pattern: /<el-descriptions\b|class=["'][^"']*detail-page[^"']*["']/g,
|
|
79
|
+
layout: "detail-page",
|
|
80
|
+
skill: "layouts/detail-page",
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const BUSINESS_SCENARIO_PATTERNS = [
|
|
85
|
+
{
|
|
86
|
+
pattern: /<el-form[\s\S]*?<el-table\b|<BaseQuery\b|<base-query\b/g,
|
|
87
|
+
scenario: "query-table",
|
|
88
|
+
skill: "layouts/list-page",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
pattern:
|
|
92
|
+
/<BaseToolbar\b|<base-toolbar\b|class=["'][^"']*(toolbar|operation-bar|table-tools)[^"']*["']/g,
|
|
93
|
+
scenario: "toolbar-actions",
|
|
94
|
+
skill: "layouts/list-page",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
pattern: /<el-tree[\s\S]*?(<el-table\b|<BaseTable\b|<base-table\b)/g,
|
|
98
|
+
scenario: "tree-table",
|
|
99
|
+
skill: "layouts/tree-list",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
pattern: /<el-dialog[\s\S]*?<el-form\b/g,
|
|
103
|
+
scenario: "dialog-form",
|
|
104
|
+
skill: "layouts/form-dialog",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
pattern: /<el-drawer[\s\S]*?(<el-form\b|<el-descriptions\b)/g,
|
|
108
|
+
scenario: "drawer-detail",
|
|
109
|
+
skill: "layouts/detail-page",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
pattern:
|
|
113
|
+
/<el-descriptions\b[\s\S]*?<el-card\b|<el-card\b[\s\S]*?<el-descriptions\b/g,
|
|
114
|
+
scenario: "detail-card",
|
|
115
|
+
skill: "layouts/detail-page",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
pattern: /<el-tabs[\s\S]*?(<el-table\b|<el-descriptions\b|<el-form\b)/g,
|
|
119
|
+
scenario: "tab-workbench",
|
|
120
|
+
skill: "element/component-family",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
pattern: /<el-upload\b/g,
|
|
124
|
+
scenario: "attachment-upload",
|
|
125
|
+
skill: "element/component-family",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
pattern: /<el-steps\b|<el-timeline\b/g,
|
|
129
|
+
scenario: "process-flow",
|
|
130
|
+
skill: "element/component-family",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
pattern: /<el-empty\b|<el-result\b|<el-alert\b/g,
|
|
134
|
+
scenario: "feedback-state",
|
|
135
|
+
skill: "element/component-family",
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
function uniqueSorted(values) {
|
|
140
|
+
return [...new Set(values)].sort();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function collectElementTags(template) {
|
|
144
|
+
const tags = [];
|
|
145
|
+
const re = /<\/?(el-[a-z0-9-]+)/g;
|
|
146
|
+
let match;
|
|
147
|
+
while ((match = re.exec(template)) !== null) tags.push(match[1]);
|
|
148
|
+
return tags;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function createCoverageCollector() {
|
|
152
|
+
const files = {};
|
|
153
|
+
const element = [];
|
|
154
|
+
const vendors = [];
|
|
155
|
+
const layouts = [];
|
|
156
|
+
const businessScenarios = [];
|
|
157
|
+
const skills = [];
|
|
158
|
+
|
|
159
|
+
function addFile(file, template, content = template) {
|
|
160
|
+
const fileElement = collectElementTags(template);
|
|
161
|
+
const fileVendors = [];
|
|
162
|
+
const fileLayouts = [];
|
|
163
|
+
const fileBusinessScenarios = [];
|
|
164
|
+
const fileSkills = [];
|
|
165
|
+
|
|
166
|
+
for (const tag of fileElement) {
|
|
167
|
+
element.push(tag);
|
|
168
|
+
if (ELEMENT_SKILL_MAP[tag]) {
|
|
169
|
+
skills.push(ELEMENT_SKILL_MAP[tag]);
|
|
170
|
+
fileSkills.push(ELEMENT_SKILL_MAP[tag]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const item of VENDOR_PATTERNS) {
|
|
175
|
+
item.pattern.lastIndex = 0;
|
|
176
|
+
if (item.pattern.test(content)) {
|
|
177
|
+
vendors.push(item.vendor);
|
|
178
|
+
skills.push(item.skill);
|
|
179
|
+
fileVendors.push(item.vendor);
|
|
180
|
+
fileSkills.push(item.skill);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for (const item of LAYOUT_PATTERNS) {
|
|
185
|
+
item.pattern.lastIndex = 0;
|
|
186
|
+
if (item.pattern.test(template)) {
|
|
187
|
+
layouts.push(item.layout);
|
|
188
|
+
skills.push(item.skill);
|
|
189
|
+
fileLayouts.push(item.layout);
|
|
190
|
+
fileSkills.push(item.skill);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for (const item of BUSINESS_SCENARIO_PATTERNS) {
|
|
195
|
+
item.pattern.lastIndex = 0;
|
|
196
|
+
if (item.pattern.test(template)) {
|
|
197
|
+
businessScenarios.push(item.scenario);
|
|
198
|
+
skills.push(item.skill);
|
|
199
|
+
fileBusinessScenarios.push(item.scenario);
|
|
200
|
+
fileSkills.push(item.skill);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
files[file] = {
|
|
205
|
+
element: uniqueSorted(fileElement),
|
|
206
|
+
vendors: uniqueSorted(fileVendors),
|
|
207
|
+
layouts: uniqueSorted(fileLayouts),
|
|
208
|
+
businessScenarios: uniqueSorted(fileBusinessScenarios),
|
|
209
|
+
recommendedSkills: uniqueSorted(fileSkills),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function result() {
|
|
214
|
+
return {
|
|
215
|
+
element: uniqueSorted(element),
|
|
216
|
+
vendors: uniqueSorted(vendors),
|
|
217
|
+
layouts: uniqueSorted(layouts),
|
|
218
|
+
businessScenarios: uniqueSorted(businessScenarios),
|
|
219
|
+
recommendedSkills: uniqueSorted(skills),
|
|
220
|
+
files,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return { addFile, result };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function recommendFlows({ issues = [], coverage = {} } = {}) {
|
|
228
|
+
const categories = new Set(issues.map((i) => i.category));
|
|
229
|
+
const rules = new Set(issues.map((i) => i.rule));
|
|
230
|
+
const flows = [];
|
|
231
|
+
const nextActions = [];
|
|
232
|
+
|
|
233
|
+
if (issues.length === 0) {
|
|
234
|
+
flows.push("full-audit");
|
|
235
|
+
nextActions.push(
|
|
236
|
+
"保持 wk-skills-ui 样式入口,定期运行 wks_ui_scan 做只读审计",
|
|
237
|
+
);
|
|
238
|
+
} else {
|
|
239
|
+
flows.push("legacy-skin-align");
|
|
240
|
+
nextActions.push("先运行 wks_ui_scan --mode skin 确认 L0/L1/L2 样式偏差");
|
|
241
|
+
nextActions.push(
|
|
242
|
+
"修复前运行 wks_ui_fix_dry_run 预览改动,不直接写入业务文件",
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (categories.has("color") || categories.has("token")) {
|
|
247
|
+
flows.push("progressive-migrate");
|
|
248
|
+
nextActions.push(
|
|
249
|
+
"将硬编码颜色迁移到 runtime/design-tokens 或 Element Plus token",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (rules.has("R013") || rules.has("R021") || rules.has("R022")) {
|
|
254
|
+
flows.push("progressive-migrate");
|
|
255
|
+
nextActions.push(
|
|
256
|
+
"表格列定义建议升级为 defineColumns + renderOps,并补齐 BaseTable render-type/cid",
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (coverage.businessScenarios?.length > 0) {
|
|
261
|
+
nextActions.push(
|
|
262
|
+
`已识别 B 端业务场景:${coverage.businessScenarios.join(", ")},建议按场景加载对应 layout/element Skill 做统一治理`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const shouldUseKit =
|
|
267
|
+
rules.has("R013") ||
|
|
268
|
+
rules.has("R021") ||
|
|
269
|
+
rules.has("R022") ||
|
|
270
|
+
coverage.layouts?.length > 0 ||
|
|
271
|
+
(coverage.businessScenarios || []).some((item) =>
|
|
272
|
+
["query-table", "tree-table", "dialog-form"].includes(item),
|
|
273
|
+
);
|
|
274
|
+
return {
|
|
275
|
+
recommendedFlows: uniqueSorted(flows),
|
|
276
|
+
nextActions: uniqueSorted(nextActions),
|
|
277
|
+
kitBridge: {
|
|
278
|
+
needed: shouldUseKit,
|
|
279
|
+
reason: shouldUseKit
|
|
280
|
+
? "扫描结果涉及页面结构、BaseTable 或操作列规范,建议在视觉统一后使用 wl-skills-kit 做规范化生成/修复。"
|
|
281
|
+
: "当前更适合由 wk-skills-ui 先完成样式绝对管控,无需强制切换到 wl-skills-kit。",
|
|
282
|
+
commands: shouldUseKit
|
|
283
|
+
? ["wl-skills validate-page <page>", "wl-skills doctor-ui <project>"]
|
|
284
|
+
: [],
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanner/exempt.mjs — 豁免配置
|
|
3
|
+
*
|
|
4
|
+
* 支持两种配置方式:
|
|
5
|
+
* 1. 项目根目录下 .wk-exempt.json
|
|
6
|
+
* 2. CLI --exempt <path> 指定配置文件
|
|
7
|
+
*
|
|
8
|
+
* 配置格式:
|
|
9
|
+
* {
|
|
10
|
+
* "exemptPaths": [
|
|
11
|
+
* "src/views/security/big-screen/**",
|
|
12
|
+
* "src/views/security/map-view/**",
|
|
13
|
+
* "src/views/security/flow-designer/**",
|
|
14
|
+
* "src/views/security/topology/**",
|
|
15
|
+
* "src/views/security/dashboard/**"
|
|
16
|
+
* ],
|
|
17
|
+
* "exemptRules": {
|
|
18
|
+
* "R016": ["src/views/security/big-screen/**"],
|
|
19
|
+
* "R017": ["src/views/security/big-screen/**"]
|
|
20
|
+
* },
|
|
21
|
+
* "exemptCategories": ["dashboard", "big-screen", "map", "chart", "topology", "flow-designer", "3d", "canvas", "report-designer", "rich-editor"],
|
|
22
|
+
* "description": "大屏、地图、图谱、流程设计器等强个性化页面豁免风格统一扫描"
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
26
|
+
import { join, resolve } from "node:path";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 简易 glob 匹配(仅支持 ** 和 * 通配符)
|
|
30
|
+
*/
|
|
31
|
+
function matchGlob(filePath, pattern) {
|
|
32
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
33
|
+
const escaped = pattern
|
|
34
|
+
.replace(/\\/g, "/")
|
|
35
|
+
.replace(/[.+^${}()|[\]]/g, "\\$&")
|
|
36
|
+
.replace(/\*\*/g, "{{GLOBSTAR}}")
|
|
37
|
+
.replace(/\*/g, "[^/]*")
|
|
38
|
+
.replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
39
|
+
return new RegExp(`^${escaped}$`).test(normalized);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 加载豁免配置
|
|
44
|
+
* @param {string} projectRoot
|
|
45
|
+
* @param {string} [exemptPath] CLI 指定的配置文件路径
|
|
46
|
+
* @returns {object} { exemptPaths, exemptRules, exemptCategories, isExempt(file, rule?) }
|
|
47
|
+
*/
|
|
48
|
+
export function loadExemptConfig(projectRoot, exemptPath) {
|
|
49
|
+
const defaultPath = join(projectRoot, ".wk-exempt.json");
|
|
50
|
+
const configPath = exemptPath ? resolve(exemptPath) : defaultPath;
|
|
51
|
+
|
|
52
|
+
const empty = {
|
|
53
|
+
exemptPaths: [],
|
|
54
|
+
exemptRules: {},
|
|
55
|
+
exemptCategories: [],
|
|
56
|
+
isExempt: () => false,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (!existsSync(configPath)) return empty;
|
|
60
|
+
|
|
61
|
+
let config;
|
|
62
|
+
try {
|
|
63
|
+
config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
64
|
+
} catch {
|
|
65
|
+
console.warn(`[wk-scan] 豁免配置解析失败: ${configPath}`);
|
|
66
|
+
return empty;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const exemptPaths = config.exemptPaths || [];
|
|
70
|
+
const exemptRules = config.exemptRules || {};
|
|
71
|
+
const exemptCategories = config.exemptCategories || [];
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 判断文件+规则是否被豁免
|
|
75
|
+
* @param {string} relFile 相对路径
|
|
76
|
+
* @param {string} [ruleId] 规则 ID
|
|
77
|
+
*/
|
|
78
|
+
function isExempt(relFile, ruleId) {
|
|
79
|
+
// 全局路径豁免
|
|
80
|
+
if (exemptPaths.some((p) => matchGlob(relFile, p))) return true;
|
|
81
|
+
// 规则级路径豁免
|
|
82
|
+
if (ruleId && exemptRules[ruleId]) {
|
|
83
|
+
if (exemptRules[ruleId].some((p) => matchGlob(relFile, p))) return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { exemptPaths, exemptRules, exemptCategories, isExempt };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 生成默认豁免配置模板
|
|
93
|
+
*/
|
|
94
|
+
export function generateExemptTemplate() {
|
|
95
|
+
return JSON.stringify(
|
|
96
|
+
{
|
|
97
|
+
exemptPaths: [
|
|
98
|
+
"src/views/**/big-screen/**",
|
|
99
|
+
"src/views/**/dashboard/**",
|
|
100
|
+
"src/views/**/map-view/**",
|
|
101
|
+
"src/views/**/topology/**",
|
|
102
|
+
"src/views/**/flow-designer/**",
|
|
103
|
+
"src/views/**/report-designer/**",
|
|
104
|
+
"src/views/**/chart/**",
|
|
105
|
+
"src/views/**/3d/**",
|
|
106
|
+
"src/views/**/canvas/**",
|
|
107
|
+
],
|
|
108
|
+
exemptRules: {},
|
|
109
|
+
exemptCategories: [
|
|
110
|
+
"dashboard",
|
|
111
|
+
"big-screen",
|
|
112
|
+
"map",
|
|
113
|
+
"chart",
|
|
114
|
+
"topology",
|
|
115
|
+
"flow-designer",
|
|
116
|
+
"3d",
|
|
117
|
+
"canvas",
|
|
118
|
+
"report-designer",
|
|
119
|
+
"rich-editor",
|
|
120
|
+
],
|
|
121
|
+
description:
|
|
122
|
+
"大屏、地图、图谱、流程设计器等强个性化页面豁免风格统一扫描",
|
|
123
|
+
},
|
|
124
|
+
null,
|
|
125
|
+
2,
|
|
126
|
+
);
|
|
127
|
+
}
|