@agile-team/wl-skills-ui 1.9.6 → 1.9.8
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 +22 -0
- package/README.md +10 -3
- package/docs/compat-matrix.md +2 -2
- package/es/{chunk-MPJ6QDYX.js → chunk-XDGTJ7LE.js} +21 -8
- package/es/common-preset.js +2 -2
- package/es/index.js +2 -2
- package/es/presets/security.js +1 -1
- package/package.json +1 -1
- package/reference/define-columns.ts +38 -6
- package/scanner/fix.mjs +97 -5
- package/scanner/report.mjs +2 -0
- package/scanner/rules/button.mjs +55 -1
- package/scanner/rules/table.mjs +36 -1
- package/skills/element/el-form/SKILL.md +12 -0
- package/skills/element/el-table/SKILL.md +21 -2
- package/skills/layouts/list-page/SKILL.md +6 -0
- package/skills/vendors/base-table/SKILL.md +4 -1
- package/standards/rules.json +25 -0
- package/standards/ui/01-table.md +21 -0
- package/standards/ui/02-button.md +21 -1
- package/standards/ui/03-form.md +25 -0
- package/styles/element/_form.scss +82 -9
- package/styles/element/_table.scss +9 -0
- package/styles/vendors/_ag-grid.scss +1 -0
- package/styles/vendors/_base-components.scss +21 -17
- package/styles/vendors/_base-query-toolbar.scss +115 -11
- package/templates/ag-grid-page/TPL-AG-GRID.md +2 -1
- package/templates/list-page/TPL-LIST.md +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ 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.9.8] - 2026-07-29
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- 统一紧凑业务表单 label、输入值、选择值、placeholder、textarea 与数字值为 12px,并继续豁免登录页和显式定制区域。
|
|
12
|
+
- 修复 textarea 聚焦后品牌色边框不清晰,以及数字输入框在不同 wrapper DOM、hover + focus 组合态下边框消失或双描边的问题。
|
|
13
|
+
- 补强原生 Element Table 与 AG Grid 长文本单行省略样式,继续由 R039 / `defineColumns()` 负责悬停完整提示与业务例外。
|
|
14
|
+
- 修复 BaseToolbar 分裂下拉按钮的箭头独立色块与菜单项内嵌按钮描边;规则仅命中 `action-button-wrap` 和 `action-dropdown-menu`,不影响普通按钮组与普通下拉菜单。
|
|
15
|
+
|
|
16
|
+
## [1.9.7] - 2026-07-28
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- 新增 R038 创建类主按钮门禁与自动修复:新增/新建/添加/创建类操作统一使用客户主题 `primary` 填充按钮,自动移除误用的 `plain`。
|
|
21
|
+
- 新增 R039 表格长文本门禁与自动修复:原生 `el-table-column` 普通数据列统一补齐 `show-overflow-tooltip`。
|
|
22
|
+
- `defineColumns()` 为 BaseTable/AG Grid 普通文本列递归补齐超长省略与悬停完整提示;结构列、自定义渲染列、换行列及显式配置保持业务声明。
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- 表格样式仅对启用 overflow tooltip 的单元格补齐单行省略,避免影响 Tag、操作列和自定义内容。
|
|
27
|
+
- 列表页、按钮、Element Table、BaseTable 规范与模板同步声明主按钮和长文本交互契约。
|
|
28
|
+
|
|
7
29
|
## [1.9.6] - 2026-07-23
|
|
8
30
|
|
|
9
31
|
### Fixed
|
package/README.md
CHANGED
|
@@ -200,7 +200,7 @@ yarn add @agile-team/wl-skills-ui
|
|
|
200
200
|
|---|---|---|
|
|
201
201
|
| `element-plus` | **`2.2.6-prod.3`** | 集团 jh- 定制版;EP 2.3.0 起引入 `.el-input__wrapper`,与 jh-ui 3.x 不兼容 |
|
|
202
202
|
| `@jhlc/jh-ui` | **`3.1.0`** | SCSS 皮肤包,`.com-text` label 包裹 + `.has-colon` 冒号注入(wl-skills-ui 已统一屏蔽表单冒号) |
|
|
203
|
-
| `@agile-team/wl-skills-ui` | `^1.9.
|
|
203
|
+
| `@agile-team/wl-skills-ui` | `^1.9.8` | 已对齐上述组合的 DOM 假设、客户主题锁、圆角契约与定制页样式边界 |
|
|
204
204
|
|
|
205
205
|
三种识别方式,任选其一:
|
|
206
206
|
|
|
@@ -214,8 +214,13 @@ yarn add @agile-team/wl-skills-ui
|
|
|
214
214
|
|
|
215
215
|
## 版本亮点
|
|
216
216
|
|
|
217
|
-
当前 v1.9.
|
|
217
|
+
当前 v1.9.8:
|
|
218
218
|
|
|
219
|
+
- **主操作按钮不再漏色**:新增/新建/添加/创建类按钮强制使用客户主题 `primary` 填充样式,扫描器可识别并自动修复漏写 `type` 或误带 `plain` 的存量代码。
|
|
220
|
+
- **长文本交互统一**:普通数据列在空间不足时单行省略,悬停显示完整内容;Tag、操作列、自定义渲染和主动换行列不被机械覆盖。
|
|
221
|
+
- **紧凑表单字号闭环**:业务表单 label、输入值、选择值、placeholder、textarea 与数字输入统一为 12px;登录页和显式定制区域继续保持项目自身设计。
|
|
222
|
+
- **输入边框状态闭环**:textarea 聚焦始终显示品牌色边框;数字输入兼容 Element Plus 新旧及混合 wrapper DOM,默认、hover、focus、错误、禁用态均只保留一层正确边框。
|
|
223
|
+
- **分裂操作保持一体**:BaseToolbar 的主动作与下拉箭头共用外轮廓和状态色,下拉菜单中的业务按钮扁平化为菜单项;规则精准限定在平台工具栏结构,不污染普通按钮组和普通下拉菜单。
|
|
219
224
|
- **Vite 4 运行时兼容**:浏览器 ESM 产物不再暴露可被开发转换误替换的 Node 环境变量成员链,预构建与直接加载均保持合法语法。
|
|
220
225
|
- **登录页精准退出**:内置识别 `.lp-root` 与 `.session-login`,其内部表单、按钮、焦点和校验态保留登录页自身设计;业务页面仍由包高权重统一管控。
|
|
221
226
|
- **通用定制页边界**:其他定制页在根节点增加 `.wl-ui-skin-exempt` 或 `data-wl-ui-skin="off"` 即可退出组件级强覆盖,不需要删除全局化妆包。
|
|
@@ -676,7 +681,7 @@ export const myRules = [
|
|
|
676
681
|
|
|
677
682
|
## 规范清单
|
|
678
683
|
|
|
679
|
-
### UI 规则(R001-
|
|
684
|
+
### UI 规则(R001-R039,按 layer 自动分组)
|
|
680
685
|
|
|
681
686
|
| Rule | Layer | Vendor | 说明 |
|
|
682
687
|
| ---- | ----- | --------- | ------------------------------------ |
|
|
@@ -707,6 +712,8 @@ export const myRules = [
|
|
|
707
712
|
| R035 | L1 | element | el-upload 建议配置 tip/限制说明 |
|
|
708
713
|
| R036 | L1 | element | el-steps 建议明确状态来源 |
|
|
709
714
|
| R037 | L1 | element | 空/异常反馈建议统一操作入口 |
|
|
715
|
+
| R038 | L1 | element | 创建类主按钮缺 primary 填充主题色 |
|
|
716
|
+
| R039 | L1 | element | 普通数据列缺省略与悬停完整提示 |
|
|
710
717
|
|
|
711
718
|
### 工程规范
|
|
712
719
|
|
package/docs/compat-matrix.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| `element-plus` | **`2.2.6-prod.3`** | 集团 jh- 定制版,搭配 `@jhlc/jh-ui` 使用,DOM 仍为 `.el-input > .el-input__inner` 直挂结构(**EP 2.3.0 起才引入 `.el-input__wrapper`**) |
|
|
12
12
|
| `@jhlc/jh-ui` | **`3.1.0`** | SCSS 皮肤包,`.com-text` label 包裹、`.has-colon ::after` 冒号注入、`.el-form-item--default { margin-bottom: 24px }` 等强约束 |
|
|
13
13
|
| `@jhlc/common-core` | `3.1.0` 或 `3.1.0-prod.x` | 基础 util/types,`@jhlc/jh-ui` 间接依赖 |
|
|
14
|
-
| `@agile-team/wl-skills-ui` | `^1.9.
|
|
14
|
+
| `@agile-team/wl-skills-ui` | `^1.9.8` | 已对齐 jh-ui 3.1.0 + EP 2.2.6-prod.3 的 DOM 假设、客户主题锁、6px 基础圆角、紧凑表单、分裂按钮、长文本提示、定制页样式边界与 Vite 4 开发转换 |
|
|
15
15
|
|
|
16
16
|
## 项目集群当前实测
|
|
17
17
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
## 适配建议
|
|
37
37
|
|
|
38
|
-
- 集团内项目:**统一锚定 `element-plus@2.2.6-prod.3` + `@jhlc/jh-ui@3.1.0`**,与 wl-skills-ui v1.9.
|
|
38
|
+
- 集团内项目:**统一锚定 `element-plus@2.2.6-prod.3` + `@jhlc/jh-ui@3.1.0`**,与 wl-skills-ui v1.9.8 三方对齐。
|
|
39
39
|
- 升级 `element-plus` 到 2.3+ 前,必须同步升级 `@jhlc/jh-ui` 到对应支持版本,否则 `.com-text` / `.el-input__inner` DOM 与 EP `__wrapper` 体系不兼容。
|
|
40
40
|
- 新项目接入:见 README 「快速接入」章节,默认会按本表声明 peerDependency 范围。
|
|
41
41
|
|
|
@@ -279,24 +279,37 @@ function clearColumnAutoMap(name) {
|
|
|
279
279
|
}
|
|
280
280
|
function defineColumns(columns) {
|
|
281
281
|
return columns.map((col) => {
|
|
282
|
-
const
|
|
282
|
+
const normalized = Array.isArray(col.children) ? {
|
|
283
|
+
...col,
|
|
284
|
+
children: defineColumns(col.children)
|
|
285
|
+
} : col;
|
|
286
|
+
const fieldName = normalized.name ?? normalized.label ?? "";
|
|
283
287
|
const preset = COLUMN_AUTO_MAP[fieldName];
|
|
284
|
-
const hasRenderer =
|
|
285
|
-
if (hasRenderer) return
|
|
286
|
-
if (!preset && isDictColumn(
|
|
288
|
+
const hasRenderer = normalized.defaultNode !== void 0 || normalized.defaultSlot !== void 0;
|
|
289
|
+
if (hasRenderer) return normalized;
|
|
290
|
+
if (!preset && isDictColumn(normalized)) {
|
|
287
291
|
return {
|
|
288
|
-
...
|
|
289
|
-
defaultSlot: ({ row }) => renderDictClassifyTag(
|
|
292
|
+
...normalized,
|
|
293
|
+
defaultSlot: ({ row }) => renderDictClassifyTag(
|
|
294
|
+
row?.[fieldName],
|
|
295
|
+
String(normalized.logicValue)
|
|
296
|
+
)
|
|
290
297
|
};
|
|
291
298
|
}
|
|
292
|
-
if (!preset) return
|
|
293
|
-
return { ...preset, ...
|
|
299
|
+
if (!preset) return withOverflowTooltip(normalized);
|
|
300
|
+
return withOverflowTooltip({ ...preset, ...normalized });
|
|
294
301
|
});
|
|
295
302
|
}
|
|
296
303
|
function isDictColumn(col) {
|
|
297
304
|
const logicType = String(col.logicType ?? "").toLowerCase();
|
|
298
305
|
return Boolean(col.logicValue) && logicType.includes("dict");
|
|
299
306
|
}
|
|
307
|
+
function withOverflowTooltip(col) {
|
|
308
|
+
if (!col.name || col.showOverflowTooltip !== void 0 || col.tooltipValueGetter !== void 0 || col.wrapText === true || col.autoHeight === true || ["selection", "index", "radio", "expand"].includes(String(col.type ?? "")) || col.defaultNode !== void 0 || col.defaultSlot !== void 0) {
|
|
309
|
+
return col;
|
|
310
|
+
}
|
|
311
|
+
return { ...col, showOverflowTooltip: true };
|
|
312
|
+
}
|
|
300
313
|
registerColumnAutoMaps({
|
|
301
314
|
enableStatus: {
|
|
302
315
|
width: 80,
|
package/es/common-preset.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { renderTagNode, renderClassifyTag, installBrandThemeLock, registerColumnAutoMaps, renderRatingLevel, renderBadge } from './chunk-
|
|
2
|
-
export { registerDictColorMap, registerDictColorMaps, renderDictClassifyTag, setDictResolver } from './chunk-
|
|
1
|
+
import { renderTagNode, renderClassifyTag, installBrandThemeLock, registerColumnAutoMaps, renderRatingLevel, renderBadge } from './chunk-XDGTJ7LE.js';
|
|
2
|
+
export { registerDictColorMap, registerDictColorMaps, renderDictClassifyTag, setDictResolver } from './chunk-XDGTJ7LE.js';
|
|
3
3
|
|
|
4
4
|
// runtime/presets/common.ts
|
|
5
5
|
var RISK_LEVEL_MAP = {
|
package/es/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { registerColumnAutoMaps } from './chunk-
|
|
2
|
-
export { AUDIT_STATUS_MAP, BRAND_THEME_TOKENS, ENABLE_STATUS_MAP, RATING_LEVEL_COLORS, VERIFY_STATUS_MAP, clearColumnAutoMap, defineColumns, getColumnAutoMap, installBrandThemeLock, registerColumnAutoMap, registerColumnAutoMaps, registerDictColorMap, registerDictColorMaps, renderAuditStatus, renderBadge, renderClassifyTag, renderCountBadge, renderDangerText, renderDictClassifyTag, renderEnableStatus, renderOps, renderRatingLevel, renderTagNode, renderTagSlot, renderVerifyStatus, setDictResolver } from './chunk-
|
|
1
|
+
import { registerColumnAutoMaps } from './chunk-XDGTJ7LE.js';
|
|
2
|
+
export { AUDIT_STATUS_MAP, BRAND_THEME_TOKENS, ENABLE_STATUS_MAP, RATING_LEVEL_COLORS, VERIFY_STATUS_MAP, clearColumnAutoMap, defineColumns, getColumnAutoMap, installBrandThemeLock, registerColumnAutoMap, registerColumnAutoMaps, registerDictColorMap, registerDictColorMaps, renderAuditStatus, renderBadge, renderClassifyTag, renderCountBadge, renderDangerText, renderDictClassifyTag, renderEnableStatus, renderOps, renderRatingLevel, renderTagNode, renderTagSlot, renderVerifyStatus, setDictResolver } from './chunk-XDGTJ7LE.js';
|
|
3
3
|
|
|
4
4
|
// runtime/presets/registry.ts
|
|
5
5
|
var _installedPresets = /* @__PURE__ */ new Set();
|
package/es/presets/security.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderTagNode, installBrandThemeLock, registerDictColorMaps, registerColumnAutoMaps } from '../chunk-
|
|
1
|
+
import { renderTagNode, installBrandThemeLock, registerDictColorMaps, registerColumnAutoMaps } from '../chunk-XDGTJ7LE.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.9.
|
|
3
|
+
"version": "1.9.8",
|
|
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",
|
|
@@ -172,14 +172,46 @@ export function defineColumns<T = any>(
|
|
|
172
172
|
columns: TableColumnDesc<T>[]
|
|
173
173
|
): TableColumnDesc<T>[] {
|
|
174
174
|
return columns.map((col) => {
|
|
175
|
+
const normalized = Array.isArray(col.children)
|
|
176
|
+
? ({
|
|
177
|
+
...col,
|
|
178
|
+
children: defineColumns(col.children),
|
|
179
|
+
} as TableColumnDesc<T>)
|
|
180
|
+
: col;
|
|
175
181
|
// 多选列:统一宽度 55px(页面已手写 width 时不覆盖)
|
|
176
|
-
if ((
|
|
177
|
-
return {
|
|
182
|
+
if ((normalized as any).type === "selection" && !(normalized as any).width) {
|
|
183
|
+
return {
|
|
184
|
+
width: 55,
|
|
185
|
+
...(normalized as any),
|
|
186
|
+
} as unknown as TableColumnDesc<T>;
|
|
178
187
|
}
|
|
179
|
-
if (!
|
|
180
|
-
const auto = COLUMN_AUTO_MAP[
|
|
181
|
-
if (!auto) return
|
|
188
|
+
if (!normalized.name) return normalized;
|
|
189
|
+
const auto = COLUMN_AUTO_MAP[normalized.name as string];
|
|
190
|
+
if (!auto) return withOverflowTooltip(normalized);
|
|
182
191
|
// 自动配置 < 手写配置(手写的属性不被覆盖)
|
|
183
|
-
return {
|
|
192
|
+
return withOverflowTooltip({
|
|
193
|
+
...auto,
|
|
194
|
+
...normalized,
|
|
195
|
+
} as TableColumnDesc<T>);
|
|
184
196
|
});
|
|
185
197
|
}
|
|
198
|
+
|
|
199
|
+
function withOverflowTooltip<T>(
|
|
200
|
+
col: TableColumnDesc<T>
|
|
201
|
+
): TableColumnDesc<T> {
|
|
202
|
+
if (
|
|
203
|
+
!col.name ||
|
|
204
|
+
col.showOverflowTooltip !== undefined ||
|
|
205
|
+
col.tooltipValueGetter !== undefined ||
|
|
206
|
+
col.wrapText === true ||
|
|
207
|
+
col.autoHeight === true ||
|
|
208
|
+
["selection", "index", "radio", "expand"].includes(
|
|
209
|
+
String((col as any).type ?? "")
|
|
210
|
+
) ||
|
|
211
|
+
col.defaultNode !== undefined ||
|
|
212
|
+
col.defaultSlot !== undefined
|
|
213
|
+
) {
|
|
214
|
+
return col;
|
|
215
|
+
}
|
|
216
|
+
return { ...col, showOverflowTooltip: true };
|
|
217
|
+
}
|
package/scanner/fix.mjs
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* - el-input / el-select: 缺 size="small" → 添加
|
|
8
8
|
* - el-date-picker: 缺 style="width:100%" → 添加
|
|
9
9
|
* - el-table: 缺 empty-text="暂无数据" → 添加
|
|
10
|
+
* - 创建类主按钮: 缺 primary / 误用 plain → 纠正
|
|
11
|
+
* - el-table 普通数据列: 缺 overflow tooltip → 添加
|
|
10
12
|
* - <style>/<template> 内可映射 hex 颜色 → 替换为 var(--el-color-*)
|
|
11
13
|
*/
|
|
12
14
|
import { readFileSync, writeFileSync, readdirSync } from "node:fs";
|
|
@@ -14,6 +16,15 @@ import { join, relative, resolve } from "node:path";
|
|
|
14
16
|
import { createSnapshot } from "./snapshot.mjs";
|
|
15
17
|
import { TOKEN_MAP } from "./rules/_shared.mjs";
|
|
16
18
|
|
|
19
|
+
const CREATE_ACTION_LABEL =
|
|
20
|
+
/(?:新增|新建|添加|创建)(?:申请|记录|数据|客户|项目|任务|明细|行)?/;
|
|
21
|
+
|
|
22
|
+
function hasTrueBooleanAttr(tagText, attr) {
|
|
23
|
+
return new RegExp(
|
|
24
|
+
`(?:^|\\s)(?::${attr}\\s*=\\s*["']true["']|${attr}(?:\\s*=\\s*["'](?:true|)["'])?)(?=\\s|/?>)`,
|
|
25
|
+
).test(tagText);
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
const FIXES = {
|
|
18
29
|
"el-input": [{ attr: "size", value: "small" }],
|
|
19
30
|
"el-select": [{ attr: "size", value: "small" }],
|
|
@@ -37,6 +48,8 @@ export const FIXED_RULE_IDS = Object.freeze([
|
|
|
37
48
|
"R016",
|
|
38
49
|
"R017",
|
|
39
50
|
"R021",
|
|
51
|
+
"R038",
|
|
52
|
+
"R039",
|
|
40
53
|
]);
|
|
41
54
|
|
|
42
55
|
function parseTag(content, pos, tagName) {
|
|
@@ -57,10 +70,24 @@ function parseTag(content, pos, tagName) {
|
|
|
57
70
|
return { text: content.slice(pos), end: content.length };
|
|
58
71
|
}
|
|
59
72
|
|
|
60
|
-
function addAttrIfMissing(
|
|
73
|
+
function addAttrIfMissing(
|
|
74
|
+
tagText,
|
|
75
|
+
tagName,
|
|
76
|
+
attr,
|
|
77
|
+
value,
|
|
78
|
+
replaceStatic = false,
|
|
79
|
+
booleanAttr = false,
|
|
80
|
+
) {
|
|
61
81
|
// 已存在(含动态绑定 :attr=)则跳过
|
|
82
|
+
const escapedAttr = attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
83
|
+
if (
|
|
84
|
+
booleanAttr &&
|
|
85
|
+
new RegExp(`(?:^|\\s):?${escapedAttr}(?=\\s|=|/?>)`).test(tagText)
|
|
86
|
+
) {
|
|
87
|
+
return { text: tagText, changed: false };
|
|
88
|
+
}
|
|
62
89
|
const re = new RegExp(
|
|
63
|
-
`:?${
|
|
90
|
+
`:?${escapedAttr}\\s*=`,
|
|
64
91
|
);
|
|
65
92
|
if (re.test(tagText)) {
|
|
66
93
|
if (!replaceStatic) return { text: tagText, changed: false };
|
|
@@ -71,6 +98,12 @@ function addAttrIfMissing(tagText, tagName, attr, value, replaceStatic = false)
|
|
|
71
98
|
const text = tagText.replace(staticRe, `$1"${value}"`);
|
|
72
99
|
return { text, changed: text !== tagText };
|
|
73
100
|
}
|
|
101
|
+
if (booleanAttr) {
|
|
102
|
+
return {
|
|
103
|
+
text: tagText.replace(`<${tagName}`, `<${tagName} ${attr}`),
|
|
104
|
+
changed: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
74
107
|
const newTag = tagText.replace(
|
|
75
108
|
`<${tagName}`,
|
|
76
109
|
`<${tagName} ${attr}="${value}"`,
|
|
@@ -105,8 +138,26 @@ function fixTemplateAttrs(content) {
|
|
|
105
138
|
if (tagName === "el-table-column" && text.includes('type="selection"')) {
|
|
106
139
|
fixes.push({ attr: "header-align", value: "center" });
|
|
107
140
|
}
|
|
108
|
-
|
|
109
|
-
|
|
141
|
+
if (
|
|
142
|
+
tagName === "el-table-column" &&
|
|
143
|
+
/(?:^|\s):?prop\s*=/.test(text) &&
|
|
144
|
+
!/(?:^|\s)type\s*=\s*["'](?:selection|index|expand)["']/.test(text)
|
|
145
|
+
) {
|
|
146
|
+
fixes.push({
|
|
147
|
+
attr: "show-overflow-tooltip",
|
|
148
|
+
value: "",
|
|
149
|
+
booleanAttr: true,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
for (const { attr, value, replaceStatic, booleanAttr } of fixes) {
|
|
153
|
+
const r = addAttrIfMissing(
|
|
154
|
+
text,
|
|
155
|
+
tagName,
|
|
156
|
+
attr,
|
|
157
|
+
value,
|
|
158
|
+
replaceStatic,
|
|
159
|
+
booleanAttr,
|
|
160
|
+
);
|
|
110
161
|
text = r.text;
|
|
111
162
|
if (r.changed) changes++;
|
|
112
163
|
}
|
|
@@ -116,6 +167,44 @@ function fixTemplateAttrs(content) {
|
|
|
116
167
|
return { content: result, changes };
|
|
117
168
|
}
|
|
118
169
|
|
|
170
|
+
function fixPrimaryActionButtons(content) {
|
|
171
|
+
let changes = 0;
|
|
172
|
+
const result = content.replace(
|
|
173
|
+
/<el-button\b[\s\S]*?<\/el-button>/g,
|
|
174
|
+
(button) => {
|
|
175
|
+
const openEnd = button.indexOf(">");
|
|
176
|
+
if (openEnd < 0) return button;
|
|
177
|
+
let opening = button.slice(0, openEnd + 1);
|
|
178
|
+
const label = button.slice(openEnd + 1, button.lastIndexOf("</el-button>"));
|
|
179
|
+
if (!CREATE_ACTION_LABEL.test(label)) return button;
|
|
180
|
+
if (/:type\s*=/.test(opening)) return button;
|
|
181
|
+
if (hasTrueBooleanAttr(opening, "link") || hasTrueBooleanAttr(opening, "text"))
|
|
182
|
+
return button;
|
|
183
|
+
|
|
184
|
+
const typeResult = addAttrIfMissing(
|
|
185
|
+
opening,
|
|
186
|
+
"el-button",
|
|
187
|
+
"type",
|
|
188
|
+
"primary",
|
|
189
|
+
true,
|
|
190
|
+
);
|
|
191
|
+
opening = typeResult.text;
|
|
192
|
+
if (typeResult.changed) changes++;
|
|
193
|
+
|
|
194
|
+
const withoutPlain = opening
|
|
195
|
+
.replace(/\s+:plain\s*=\s*["']true["']/g, "")
|
|
196
|
+
.replace(/\s+plain\s*=\s*["'](?:true|)["']/g, "")
|
|
197
|
+
.replace(/\s+plain(?=\s|\/?>)/g, "");
|
|
198
|
+
if (withoutPlain !== opening) {
|
|
199
|
+
opening = withoutPlain;
|
|
200
|
+
changes++;
|
|
201
|
+
}
|
|
202
|
+
return opening + button.slice(openEnd + 1);
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
return { content: result, changes };
|
|
206
|
+
}
|
|
207
|
+
|
|
119
208
|
function fixHexColors(content) {
|
|
120
209
|
let changes = 0;
|
|
121
210
|
let result = content;
|
|
@@ -180,9 +269,12 @@ export function runFix({
|
|
|
180
269
|
const r1 = fixTemplateAttrs(content);
|
|
181
270
|
content = r1.content;
|
|
182
271
|
changes += r1.changes;
|
|
183
|
-
const r2 =
|
|
272
|
+
const r2 = fixPrimaryActionButtons(content);
|
|
184
273
|
content = r2.content;
|
|
185
274
|
changes += r2.changes;
|
|
275
|
+
const r3 = fixHexColors(content);
|
|
276
|
+
content = r3.content;
|
|
277
|
+
changes += r3.changes;
|
|
186
278
|
if (changes > 0 && content !== original) {
|
|
187
279
|
pending.push({ filePath, original, content, changes });
|
|
188
280
|
changedAbsPaths.push(filePath);
|
package/scanner/report.mjs
CHANGED
|
@@ -63,6 +63,8 @@ const RULE_NAME = {
|
|
|
63
63
|
R018: "<script> 块硬编码 hex 颜色",
|
|
64
64
|
R021: 'BaseTable 缺少 render-type="agGrid"',
|
|
65
65
|
R022: "BaseTable 缺少唯一 cid",
|
|
66
|
+
R038: "创建类主操作缺少 primary 填充主题色",
|
|
67
|
+
R039: "普通数据列缺少超长省略与悬停完整提示",
|
|
66
68
|
R031: "详情/统计卡片建议使用统一场景 class",
|
|
67
69
|
R032: "el-tabs 建议明确页面场景",
|
|
68
70
|
R033: "el-descriptions 建议使用 bordered 或统一容器",
|
package/scanner/rules/button.mjs
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
/** scanner/rules/button.mjs — 按钮规则:R004 R005 R015 */
|
|
1
|
+
/** scanner/rules/button.mjs — 按钮规则:R004 R005 R015 R038 */
|
|
2
2
|
import { lineOf, issue, findTags } from "./_shared.mjs";
|
|
3
3
|
|
|
4
|
+
const CREATE_ACTION_LABEL = /(?:新增|新建|添加|创建)(?:申请|记录|数据|客户|项目|任务|明细|行)?/;
|
|
5
|
+
|
|
6
|
+
function buttonContent(template, tag) {
|
|
7
|
+
const start = tag.index + tag.text.length;
|
|
8
|
+
const end = template.indexOf("</el-button>", start);
|
|
9
|
+
return end < 0 ? "" : template.slice(start, end);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hasTrueBooleanAttr(tagText, attr) {
|
|
13
|
+
return new RegExp(
|
|
14
|
+
`(?:^|\\s)(?::${attr}\\s*=\\s*["']true["']|${attr}(?:\\s*=\\s*["'](?:true|)["'])?)(?=\\s|/?>)`,
|
|
15
|
+
).test(tagText);
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
export const buttonRules = [
|
|
5
19
|
// R004: 操作列文字按钮
|
|
6
20
|
{
|
|
@@ -59,6 +73,46 @@ export const buttonRules = [
|
|
|
59
73
|
},
|
|
60
74
|
},
|
|
61
75
|
|
|
76
|
+
// R038: 新增/新建/添加/创建类主操作必须使用填充主题色
|
|
77
|
+
{
|
|
78
|
+
id: "R038",
|
|
79
|
+
category: "button",
|
|
80
|
+
severity: "error",
|
|
81
|
+
name: "创建类主操作缺少 primary 填充主题色",
|
|
82
|
+
check(template, file, lineOffset) {
|
|
83
|
+
const issues = [];
|
|
84
|
+
for (const tag of findTags(template, "el-button")) {
|
|
85
|
+
const label = buttonContent(template, tag);
|
|
86
|
+
if (!CREATE_ACTION_LABEL.test(label)) continue;
|
|
87
|
+
if (/:type\s*=/.test(tag.text)) continue;
|
|
88
|
+
if (
|
|
89
|
+
hasTrueBooleanAttr(tag.text, "link") ||
|
|
90
|
+
hasTrueBooleanAttr(tag.text, "text")
|
|
91
|
+
) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const isPrimary = /(?:^|\s)type\s*=\s*["']primary["']/.test(
|
|
95
|
+
tag.text,
|
|
96
|
+
);
|
|
97
|
+
const isPlain = hasTrueBooleanAttr(tag.text, "plain");
|
|
98
|
+
if (!isPrimary || isPlain) {
|
|
99
|
+
issues.push(
|
|
100
|
+
issue(
|
|
101
|
+
file,
|
|
102
|
+
lineOf(template, tag.index, lineOffset),
|
|
103
|
+
"R038",
|
|
104
|
+
"button",
|
|
105
|
+
"error",
|
|
106
|
+
"新增/新建/添加/创建类主操作必须是 primary 填充按钮",
|
|
107
|
+
'设置 type="primary" 并移除 plain;行内次级操作应显式使用 link/text',
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return issues;
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
|
|
62
116
|
// R015: 弹窗嵌套表格操作列用 el-button link
|
|
63
117
|
{
|
|
64
118
|
id: "R015",
|
package/scanner/rules/table.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** scanner/rules/table.mjs — 表格相关规则:R001 R002 R003 R014 R021 R022 */
|
|
1
|
+
/** scanner/rules/table.mjs — 表格相关规则:R001 R002 R003 R014 R021 R022 R039 */
|
|
2
2
|
import { lineOf, issue, findTags } from "./_shared.mjs";
|
|
3
3
|
|
|
4
4
|
export const tableRules = [
|
|
@@ -35,6 +35,41 @@ export const tableRules = [
|
|
|
35
35
|
},
|
|
36
36
|
},
|
|
37
37
|
|
|
38
|
+
// R039: 原生 el-table 普通数据列必须支持超长省略与悬停完整提示
|
|
39
|
+
{
|
|
40
|
+
id: "R039",
|
|
41
|
+
category: "table",
|
|
42
|
+
severity: "warning",
|
|
43
|
+
name: "el-table 普通数据列缺少 show-overflow-tooltip",
|
|
44
|
+
check(template, file, lineOffset) {
|
|
45
|
+
const issues = [];
|
|
46
|
+
for (const tag of findTags(template, "el-table-column")) {
|
|
47
|
+
if (!/(?:^|\s):?prop\s*=/.test(tag.text)) continue;
|
|
48
|
+
if (
|
|
49
|
+
/(?:^|\s)type\s*=\s*["'](?:selection|index|expand)["']/.test(
|
|
50
|
+
tag.text,
|
|
51
|
+
)
|
|
52
|
+
) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (/(?:^|\s):?show-overflow-tooltip(?:\s|=|\/?>)/.test(tag.text))
|
|
56
|
+
continue;
|
|
57
|
+
issues.push(
|
|
58
|
+
issue(
|
|
59
|
+
file,
|
|
60
|
+
lineOf(template, tag.index, lineOffset),
|
|
61
|
+
"R039",
|
|
62
|
+
"table",
|
|
63
|
+
"warning",
|
|
64
|
+
"普通数据列缺少超长省略与悬停完整内容提示",
|
|
65
|
+
"添加 show-overflow-tooltip;BaseTable/AG Grid 列统一使用 defineColumns() 自动补齐",
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return issues;
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
|
|
38
73
|
// R002: el-table 缺少 empty-text
|
|
39
74
|
{
|
|
40
75
|
id: "R002",
|
|
@@ -59,6 +59,18 @@ el-date-picker 默认宽度不会自动撑满 el-form-item,必须显式设置
|
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
|
62
|
+
## 表单字号与状态边框一致性 【中危】
|
|
63
|
+
|
|
64
|
+
- 紧凑业务表单的 label、输入值、选择值、picker、textarea、数字值和 placeholder
|
|
65
|
+
统一使用 `--wk-form-font-size: 12px`;不要在某一种组件上单独保留 13px/14px。
|
|
66
|
+
- textarea focus 必须是清晰的品牌色实线与轻外环,不能只改 `border-color` 或依赖
|
|
67
|
+
平台默认阴影。
|
|
68
|
+
- 数字输入框必须同时检查社区版“wrapper 子节点”和 jh-ui“wrapper 与
|
|
69
|
+
input-number 同节点”两种 DOM,并确保只存在一层可见边框。
|
|
70
|
+
- 登录页和显式定制页继续使用自身样式,不套用上述高权重规则。
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
62
74
|
## 表单列数与输入宽度 【中危】
|
|
63
75
|
|
|
64
76
|
- 全宽业务表单默认 4 列,严禁使用 5~8 列把输入框压缩到无法阅读或填写。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: |
|
|
3
3
|
表格组件规范 Skill — el-table / BaseTable / el-table-column 的对齐、空状态、selection 列标准。
|
|
4
|
-
覆盖规则:R001 R002 R003 R014。
|
|
4
|
+
覆盖规则:R001 R002 R003 R014 R039。
|
|
5
5
|
applyTo: "**/*.vue"
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -55,6 +55,19 @@ applyTo: "**/*.vue"
|
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
58
|
+
## R039 — 普通数据列必须超长省略并悬停显示完整内容 【中危】
|
|
59
|
+
|
|
60
|
+
```diff
|
|
61
|
+
- <el-table-column prop="customerName" label="客户名称" align="center" />
|
|
62
|
+
+ <el-table-column prop="customerName" label="客户名称" align="center" show-overflow-tooltip />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
BaseTable / AG Grid 使用 `defineColumns()` 后由 runtime 自动为普通文本列补齐
|
|
66
|
+
`showOverflowTooltip: true`。自定义渲染、结构列、换行列不强制;需要主动关闭时
|
|
67
|
+
显式声明 `showOverflowTooltip: false`。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
58
71
|
## 完整标准写法示例
|
|
59
72
|
|
|
60
73
|
```html
|
|
@@ -67,7 +80,13 @@ applyTo: "**/*.vue"
|
|
|
67
80
|
header-align="center"
|
|
68
81
|
/>
|
|
69
82
|
<el-table-column type="index" align="center" width="60" label="序号" />
|
|
70
|
-
<el-table-column
|
|
83
|
+
<el-table-column
|
|
84
|
+
align="center"
|
|
85
|
+
prop="name"
|
|
86
|
+
label="名称"
|
|
87
|
+
min-width="120"
|
|
88
|
+
show-overflow-tooltip
|
|
89
|
+
/>
|
|
71
90
|
<el-table-column align="center" prop="status" label="状态" width="90">
|
|
72
91
|
<template #default="{ row }">
|
|
73
92
|
<!-- 使用 renderTagSlot 渲染彩色 Tag,见 tag-status/SKILL.md -->
|
|
@@ -53,6 +53,8 @@ applyTo: "**/*.vue"
|
|
|
53
53
|
|
|
54
54
|
- R001 ~ R003:表格列对齐 + empty-text
|
|
55
55
|
- R005:工具栏按钮缺 icon
|
|
56
|
+
- R038:新增/新建/添加/创建类主按钮缺 primary 填充主题色
|
|
57
|
+
- R039:普通数据列缺少省略号与悬停完整内容提示
|
|
56
58
|
- R006:搜索区输入框缺 `size="small"`
|
|
57
59
|
- R011:分页在 footer 内(严重)
|
|
58
60
|
|
|
@@ -97,6 +99,10 @@ applyTo: "**/*.vue"
|
|
|
97
99
|
</template>
|
|
98
100
|
```
|
|
99
101
|
|
|
102
|
+
“新增”类按钮必须保持 `type="primary"` 且不得带 `plain`;生成完成后运行
|
|
103
|
+
`wl-ui scan` / `wl-ui fix` 复核 R038。表格列统一由 `defineColumns()` 包裹,
|
|
104
|
+
普通文本列的 R039 行为由 runtime 自动补齐。
|
|
105
|
+
|
|
100
106
|
### SCSS 对应(由 styles/layouts/\_list-page.scss 提供)
|
|
101
107
|
|
|
102
108
|
```scss
|
|
@@ -28,12 +28,14 @@ applyTo: "**/*.vue"
|
|
|
28
28
|
- ❌ 用 `<el-button type="primary">` 当操作按钮(应用 `is-text` 文字按钮,沿用 R004)
|
|
29
29
|
- ❌ 列头未居中(同 R001)
|
|
30
30
|
- ❌ 缺 `empty-text="暂无数据"`(同 R003)
|
|
31
|
+
- ❌ 普通文本列未通过 `defineColumns()` 获得超长省略与悬停完整提示(同 R039)
|
|
31
32
|
- ❌ 在 `<BaseTable>` 外层包裹自定义 padding 容器,破坏整体节奏
|
|
32
33
|
|
|
33
34
|
## Repair(修复)
|
|
34
35
|
|
|
35
36
|
### A 类(自动修)
|
|
36
37
|
- 缺失 `empty-text` → 补 `empty-text="暂无数据"`
|
|
38
|
+
- 普通文本列统一经 `defineColumns()` 自动补 `showOverflowTooltip: true`
|
|
37
39
|
- 操作列按钮 `type="primary"` → 改为 `link` + `is-text`
|
|
38
40
|
- 内联 hex → CSS 变量
|
|
39
41
|
|
|
@@ -51,7 +53,8 @@ applyTo: "**/*.vue"
|
|
|
51
53
|
|
|
52
54
|
## 全局样式来源
|
|
53
55
|
|
|
54
|
-
`styles/vendors/_base-table.scss` —
|
|
56
|
+
`styles/vendors/_base-table.scss` — 已注入容器圆角、表头颜色、操作按钮间距;
|
|
57
|
+
`runtime.defineColumns()` 统一控制普通文本列的 overflow tooltip。
|
|
55
58
|
项目侧无需重复写样式,遵循 DOM 约定即可。
|
|
56
59
|
|
|
57
60
|
## 未来源码迁移
|
package/standards/rules.json
CHANGED
|
@@ -107,6 +107,17 @@
|
|
|
107
107
|
"scanner": "scanner/rules/button.mjs",
|
|
108
108
|
"skills": ["layouts/list-page"]
|
|
109
109
|
},
|
|
110
|
+
{
|
|
111
|
+
"id": "R038",
|
|
112
|
+
"category": "button",
|
|
113
|
+
"severity": "error",
|
|
114
|
+
"title": "新增/新建/添加/创建类主操作必须使用 primary 填充按钮",
|
|
115
|
+
"rationale": "创建类动作是列表页首要操作,必须稳定使用客户主题深蓝填充,禁止漏写 type 或误用 plain 退化为线框。",
|
|
116
|
+
"appliesTo": ["el-button[label~=新增|新建|添加|创建]"],
|
|
117
|
+
"autoFixable": true,
|
|
118
|
+
"scanner": "scanner/rules/button.mjs",
|
|
119
|
+
"skills": ["layouts/list-page"]
|
|
120
|
+
},
|
|
110
121
|
{
|
|
111
122
|
"id": "R006",
|
|
112
123
|
"category": "form",
|
|
@@ -313,6 +324,20 @@
|
|
|
313
324
|
"scanner": "scanner/rules/table.mjs",
|
|
314
325
|
"skills": ["vendors/base-table"]
|
|
315
326
|
},
|
|
327
|
+
{
|
|
328
|
+
"id": "R039",
|
|
329
|
+
"category": "table",
|
|
330
|
+
"severity": "warning",
|
|
331
|
+
"title": "普通数据列必须支持超长省略与悬停完整提示",
|
|
332
|
+
"rationale": "列宽不足时统一单行省略,悬停显示完整内容,避免长名称撑破布局或信息不可读。",
|
|
333
|
+
"appliesTo": [
|
|
334
|
+
"el-table-column[prop]",
|
|
335
|
+
"BaseTable defineColumns ordinary data column"
|
|
336
|
+
],
|
|
337
|
+
"autoFixable": true,
|
|
338
|
+
"scanner": "scanner/rules/table.mjs",
|
|
339
|
+
"skills": ["element/el-table", "vendors/base-table"]
|
|
340
|
+
},
|
|
316
341
|
{
|
|
317
342
|
"id": "R031",
|
|
318
343
|
"category": "family",
|
package/standards/ui/01-table.md
CHANGED
|
@@ -32,6 +32,27 @@
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
+
## 规则 R039:长文本统一省略并悬停显示完整内容
|
|
36
|
+
|
|
37
|
+
原生 Element Plus 普通数据列必须声明 `show-overflow-tooltip`:
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<el-table-column
|
|
41
|
+
prop="customerName"
|
|
42
|
+
label="客户名称"
|
|
43
|
+
min-width="160"
|
|
44
|
+
align="center"
|
|
45
|
+
show-overflow-tooltip
|
|
46
|
+
/>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
BaseTable / AG Grid 列必须通过 `defineColumns()` 声明。普通文本列会自动补
|
|
50
|
+
`showOverflowTooltip: true`;自定义渲染列、selection/index/expand、`wrapText`
|
|
51
|
+
或 `autoHeight` 列不会被强制。业务可显式设置 `showOverflowTooltip: false`
|
|
52
|
+
退出。
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
35
56
|
## 规则 R004:操作列使用 renderOps / jh-op-btn
|
|
36
57
|
|
|
37
58
|
操作列按钮**不得**直接使用 `<el-button>` 或裸文本,必须通过 `renderOps` 渲染:
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
### 类型规则
|
|
13
13
|
|
|
14
|
+
**R038 强制约束**:凡按钮文案表达“新增 / 新建 / 添加 / 创建”主操作,
|
|
15
|
+
必须使用 `type="primary"` 的填充样式,不得漏写 `type`,也不得同时设置
|
|
16
|
+
`plain`。只有明确属于行内次级交互时,才允许显式改用 `link` / `text`。
|
|
17
|
+
`wl-ui scan` 会报错,`wl-ui fix` 可机械纠正静态按钮。
|
|
18
|
+
|
|
14
19
|
主按钮色值严格遵循客户 UI 规范,禁止使用 `#2254F4`、`#4368FF` 或 Element Plus 默认蓝替代:
|
|
15
20
|
|
|
16
21
|
| 状态 | Token | 规范色值 |
|
|
@@ -36,7 +41,7 @@
|
|
|
36
41
|
|
|
37
42
|
```vue
|
|
38
43
|
<!-- ✅ 工具栏按钮组 -->
|
|
39
|
-
<el-button type="primary" @click="handleCreate">新增</el-button>
|
|
44
|
+
<el-button type="primary" icon="Plus" @click="handleCreate">新增</el-button>
|
|
40
45
|
<el-button type="warning" plain icon="Edit" @click="handleEdit">修改</el-button>
|
|
41
46
|
<el-button type="success" icon="Save" @click="handleSave">保存</el-button>
|
|
42
47
|
<el-button icon="Download" @click="handleExport">导出</el-button>
|
|
@@ -44,6 +49,21 @@
|
|
|
44
49
|
|
|
45
50
|
---
|
|
46
51
|
|
|
52
|
+
## BaseToolbar 分裂下拉按钮
|
|
53
|
+
|
|
54
|
+
当一个工具栏动作同时包含主动作 `onClick` 与子动作 `children` 时,BaseToolbar
|
|
55
|
+
会生成 split-button。它在交互语义上仍是两个点击区域,但视觉上必须表现为一个
|
|
56
|
+
完整按钮:
|
|
57
|
+
|
|
58
|
+
- 主动作与箭头段共用同一背景色、hover/focus/active 状态和外轮廓。
|
|
59
|
+
- 箭头段不得拥有独立圆角、外边距、阴影或 Element Plus 默认分割线。
|
|
60
|
+
- `action-dropdown-menu` 中的 jh-button 必须扁平为菜单内容,边框和背景由
|
|
61
|
+
`el-dropdown-menu__item` 统一承担。
|
|
62
|
+
- 上述规则不得扩散到普通 `el-button-group`、普通 `el-dropdown-menu` 或页面
|
|
63
|
+
自定义编辑器工具栏。
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
47
67
|
## 操作列按钮(表格行内)
|
|
48
68
|
|
|
49
69
|
统一使用 `renderOps` 图标按钮系统,**禁止** `<el-button>` 在行内使用:
|
package/standards/ui/03-form.md
CHANGED
|
@@ -67,6 +67,31 @@ textarea 不强制 26px 高度,只继承统一圆角、字体和状态样式
|
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|
|
70
|
+
## 规则:紧凑业务表单字号统一为 12px
|
|
71
|
+
|
|
72
|
+
页面正文仍使用 14px;业务表单的 label、输入值、选择值、picker、textarea、
|
|
73
|
+
数字输入框和 placeholder 统一使用:
|
|
74
|
+
|
|
75
|
+
```scss
|
|
76
|
+
--wk-form-font-size: 12px;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
该规则同时覆盖 Element Plus 2.2 直挂 input DOM、2.3+ wrapper DOM 与 jh-* 封装,
|
|
80
|
+
登录页及 `.wl-ui-skin-exempt` / `data-wl-ui-skin="off"` 定制页不参与覆盖。
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 规则:textarea 与数字输入框只保留一层状态边框
|
|
85
|
+
|
|
86
|
+
- textarea 默认使用中性 1px 边框,hover 使用 hover 边框色,focus 强制使用品牌色
|
|
87
|
+
1px 实线与轻外环,不能只依赖平台已有 box-shadow。
|
|
88
|
+
- 数字输入框兼容 `.el-input-number > .el-input__wrapper` 和
|
|
89
|
+
`.el-input-number.el-input__wrapper` 两种 DOM;由 wrapper 层绘制唯一边框,
|
|
90
|
+
内部 input 不得再画第二层边框。
|
|
91
|
+
- error / disabled 状态继续分别使用危险色和禁用色,不得被 focus 覆盖。
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
70
95
|
## 规则:表单 label 不强制冒号
|
|
71
96
|
|
|
72
97
|
Element Plus 原生控件、picker 类控件和 jh-\* 封装控件的 label 后不强制追加 `:`。`@jhlc/jh-ui` 的 `.has-colon .com-text:after` / `.text-line-2:after` 冒号注入由 wl-skills-ui 统一屏蔽,避免 input / select / picker 之间出现有的带冒号、有的不带冒号。
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
html body {
|
|
6
6
|
--wk-form-control-radius: var(--el-border-radius-base, 6px);
|
|
7
7
|
--wk-form-control-height: 26px;
|
|
8
|
+
--wk-form-font-size: 12px !important;
|
|
8
9
|
--wk-form-label-control-gap: 16px;
|
|
9
10
|
--wk-form-label-line-height: 22px;
|
|
10
11
|
}
|
|
@@ -24,6 +25,31 @@ html body {
|
|
|
24
25
|
padding-right: var(--wk-form-label-control-gap) !important;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
// ── 紧凑业务表单字号统一 ──────────────────────────────────────────────────
|
|
29
|
+
// 正文仍使用 14px;这里只治理表单 label、控件值与 placeholder。
|
|
30
|
+
&.el-form-item__label,
|
|
31
|
+
&.el-form-item__label > .com-text,
|
|
32
|
+
&.el-form-item__label .com-text,
|
|
33
|
+
&.el-form-item__label > .text-line-2,
|
|
34
|
+
&.el-form-item__label .text-line-2,
|
|
35
|
+
&.el-input__inner,
|
|
36
|
+
&.el-textarea__inner,
|
|
37
|
+
&.el-select__selected-item,
|
|
38
|
+
&.el-select__placeholder,
|
|
39
|
+
&.el-select__tags-text,
|
|
40
|
+
&.el-date-editor .el-range-input,
|
|
41
|
+
&.el-date-editor .el-range-separator,
|
|
42
|
+
&.el-radio__label,
|
|
43
|
+
&.el-checkbox__label {
|
|
44
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&.el-input__inner::placeholder,
|
|
48
|
+
&.el-textarea__inner::placeholder,
|
|
49
|
+
&.el-date-editor .el-range-input::placeholder {
|
|
50
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
51
|
+
}
|
|
52
|
+
|
|
27
53
|
&.el-input__wrapper,
|
|
28
54
|
&.el-select__wrapper,
|
|
29
55
|
&.el-select .el-select__wrapper,
|
|
@@ -47,9 +73,10 @@ html body {
|
|
|
47
73
|
line-height: calc(var(--wk-form-control-height) - 2px) !important;
|
|
48
74
|
}
|
|
49
75
|
|
|
50
|
-
// common-core
|
|
51
|
-
//
|
|
52
|
-
//
|
|
76
|
+
// common-core / jh-ui 有两种数字框 DOM:
|
|
77
|
+
// A. .el-input-number > .el-input > .el-input__wrapper(社区版)
|
|
78
|
+
// B. .el-input-number.el-input__wrapper > .el-input(jh 同节点 wrapper)
|
|
79
|
+
// 两种结构都只允许 wrapper 层绘制唯一可见边框。
|
|
53
80
|
&.el-input-number,
|
|
54
81
|
&.el-input-number:focus,
|
|
55
82
|
&.el-input-number:focus-within {
|
|
@@ -61,6 +88,11 @@ html body {
|
|
|
61
88
|
outline: none !important;
|
|
62
89
|
box-sizing: border-box !important;
|
|
63
90
|
background: transparent !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
&.el-input-number:not(.el-input__wrapper),
|
|
94
|
+
&.el-input-number:not(.el-input__wrapper):focus,
|
|
95
|
+
&.el-input-number:not(.el-input__wrapper):focus-within {
|
|
64
96
|
box-shadow: none !important;
|
|
65
97
|
}
|
|
66
98
|
|
|
@@ -73,7 +105,8 @@ html body {
|
|
|
73
105
|
box-shadow: none !important;
|
|
74
106
|
}
|
|
75
107
|
|
|
76
|
-
&.el-input-number .el-input__wrapper
|
|
108
|
+
&.el-input-number .el-input__wrapper,
|
|
109
|
+
&.el-input-number.el-input__wrapper {
|
|
77
110
|
width: 100% !important;
|
|
78
111
|
height: 100% !important;
|
|
79
112
|
min-height: 0 !important;
|
|
@@ -86,23 +119,35 @@ html body {
|
|
|
86
119
|
transition: box-shadow 0.15s ease !important;
|
|
87
120
|
}
|
|
88
121
|
|
|
89
|
-
&.el-input-number:not(.is-disabled):hover .el-input__wrapper
|
|
122
|
+
&.el-input-number:not(.is-disabled):hover .el-input__wrapper,
|
|
123
|
+
&.el-input-number.el-input__wrapper:not(.is-disabled):hover {
|
|
90
124
|
box-shadow:
|
|
91
125
|
0 0 0 1px var(--el-border-color-hover, #c0c4cc) inset !important;
|
|
92
126
|
}
|
|
93
127
|
|
|
94
|
-
&.el-input-number .el-input__wrapper.is-focus,
|
|
95
|
-
&.el-input-number:focus-within .el-input__wrapper
|
|
128
|
+
&.el-input-number:not(.is-disabled) .el-input__wrapper.is-focus,
|
|
129
|
+
&.el-input-number:not(.is-disabled):focus-within .el-input__wrapper,
|
|
130
|
+
&.el-input-number.el-input__wrapper:not(.is-disabled).is-focus,
|
|
131
|
+
&.el-input-number.el-input__wrapper:not(.is-disabled):focus,
|
|
132
|
+
&.el-input-number.el-input__wrapper:not(.is-disabled):focus-within {
|
|
96
133
|
box-shadow:
|
|
97
134
|
0 0 0 1.5px var(--el-color-primary, #002a8f) inset !important;
|
|
98
135
|
}
|
|
99
136
|
|
|
100
|
-
&.el-input-number.is-disabled .el-input__wrapper
|
|
137
|
+
&.el-input-number.is-disabled .el-input__wrapper,
|
|
138
|
+
&.el-input-number.el-input__wrapper.is-disabled {
|
|
101
139
|
background: var(--el-disabled-bg-color, #f5f7fa) !important;
|
|
102
140
|
box-shadow:
|
|
103
141
|
0 0 0 1px var(--el-disabled-border-color, #e4e7ed) inset !important;
|
|
104
142
|
}
|
|
105
143
|
|
|
144
|
+
// 极少数过渡版本会同时输出“同节点 wrapper + 子 wrapper”;
|
|
145
|
+
// 此时由外层绘制边框,明确清掉内层,防止双描边。
|
|
146
|
+
&.el-input-number.el-input__wrapper > .el-input > .el-input__wrapper {
|
|
147
|
+
background: transparent !important;
|
|
148
|
+
box-shadow: none !important;
|
|
149
|
+
}
|
|
150
|
+
|
|
106
151
|
&.el-input-number .el-input__inner,
|
|
107
152
|
&.el-input-number .el-input__inner:focus,
|
|
108
153
|
&.el-input-number .el-input__inner:focus-visible {
|
|
@@ -139,7 +184,35 @@ html body {
|
|
|
139
184
|
-moz-appearance: textfield !important;
|
|
140
185
|
}
|
|
141
186
|
|
|
142
|
-
|
|
187
|
+
// ── textarea 默认 / hover / focus ─────────────────────────────────────────
|
|
188
|
+
// focus 使用“品牌色实线 + 轻外环”,不依赖平台原有 border-width/box-shadow。
|
|
189
|
+
&.el-textarea > .el-textarea__inner,
|
|
190
|
+
&.com-input > .el-textarea > .el-textarea__inner,
|
|
191
|
+
&.com-textarea > .el-textarea > .el-textarea__inner {
|
|
192
|
+
border: 1px solid var(--el-border-color, #dcdfe6) !important;
|
|
143
193
|
border-radius: var(--wk-form-control-radius) !important;
|
|
194
|
+
box-shadow: none !important;
|
|
195
|
+
transition:
|
|
196
|
+
border-color 0.15s ease,
|
|
197
|
+
box-shadow 0.15s ease !important;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
&.el-textarea:not(.is-disabled) > .el-textarea__inner:hover:not(:focus),
|
|
201
|
+
&.com-input > .el-textarea:not(.is-disabled) > .el-textarea__inner:hover:not(:focus),
|
|
202
|
+
&.com-textarea > .el-textarea:not(.is-disabled) > .el-textarea__inner:hover:not(:focus) {
|
|
203
|
+
border-color: var(--el-border-color-hover, #c0c4cc) !important;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
&.el-textarea.is-focus > .el-textarea__inner,
|
|
207
|
+
&.el-textarea > .el-textarea__inner:focus,
|
|
208
|
+
&.el-textarea > .el-textarea__inner:focus-visible,
|
|
209
|
+
&.com-input > .el-textarea.is-focus > .el-textarea__inner,
|
|
210
|
+
&.com-input > .el-textarea > .el-textarea__inner:focus,
|
|
211
|
+
&.com-textarea > .el-textarea.is-focus > .el-textarea__inner,
|
|
212
|
+
&.com-textarea > .el-textarea > .el-textarea__inner:focus {
|
|
213
|
+
border: 1px solid var(--el-color-primary, #002a8f) !important;
|
|
214
|
+
outline: none !important;
|
|
215
|
+
box-shadow:
|
|
216
|
+
0 0 0 2px rgba(var(--el-color-primary-rgb, 0, 42, 143), 0.12) !important;
|
|
144
217
|
}
|
|
145
218
|
}
|
|
@@ -12,6 +12,15 @@ html body .el-table {
|
|
|
12
12
|
--el-table-tr-bg-color: var(--el-fill-color-lighter, #fafafa);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// 仅命中 show-overflow-tooltip 生成的单元格,不压制 Tag/操作列等自定义内容。
|
|
16
|
+
.el-table__body .cell.el-tooltip {
|
|
17
|
+
min-width: 0;
|
|
18
|
+
max-width: 100%;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
text-overflow: ellipsis;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
.el-table__empty-block {
|
|
16
25
|
display: flex;
|
|
17
26
|
align-items: center;
|
|
@@ -453,20 +453,11 @@
|
|
|
453
453
|
&.el-textarea {
|
|
454
454
|
.el-textarea__inner {
|
|
455
455
|
border-radius: 6px !important;
|
|
456
|
-
font-size:
|
|
456
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
457
457
|
line-height: 1.65 !important;
|
|
458
458
|
padding: 7px 10px !important;
|
|
459
459
|
resize: vertical !important; // 只允许垂直拉伸,不允许水平
|
|
460
460
|
transition: border-color 0.15s, box-shadow 0.15s;
|
|
461
|
-
|
|
462
|
-
&:hover:not(:focus) {
|
|
463
|
-
border-color: var(--el-border-color-hover, #c0c4cc) !important;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
&:focus {
|
|
467
|
-
border-color: var(--el-color-primary) !important;
|
|
468
|
-
box-shadow: 0 0 0 2px rgba(var(--el-color-primary-rgb), 0.12) !important;
|
|
469
|
-
}
|
|
470
461
|
}
|
|
471
462
|
|
|
472
463
|
// 字数统计:更小、更淡、不遮挡内容
|
|
@@ -495,8 +486,7 @@
|
|
|
495
486
|
&.el-select__wrapper.is-focused,
|
|
496
487
|
&.el-select__wrapper:focus-within,
|
|
497
488
|
&.el-date-editor.el-input__wrapper.is-focus,
|
|
498
|
-
&.el-date-editor.el-input__wrapper:focus-within
|
|
499
|
-
&.el-textarea__inner:focus {
|
|
489
|
+
&.el-date-editor.el-input__wrapper:focus-within {
|
|
500
490
|
box-shadow: 0 0 0 1.5px var(--el-color-primary, #002a8f) inset !important;
|
|
501
491
|
}
|
|
502
492
|
|
|
@@ -514,13 +504,13 @@
|
|
|
514
504
|
&.el-form.el-form--label-left .el-form-item__label,
|
|
515
505
|
&.el-form.el-form--label-right .el-form-item__label {
|
|
516
506
|
align-items: baseline !important;
|
|
517
|
-
font-size: 12px !important;
|
|
507
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
518
508
|
line-height: var(--wk-form-label-line-height, 22px) !important;
|
|
519
509
|
}
|
|
520
510
|
|
|
521
511
|
&.el-form.el-form--label-left .el-form-item__label > .com-text,
|
|
522
512
|
&.el-form.el-form--label-right .el-form-item__label > .com-text {
|
|
523
|
-
font-size: 12px !important;
|
|
513
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
524
514
|
line-height: var(--wk-form-label-line-height, 22px) !important;
|
|
525
515
|
}
|
|
526
516
|
|
|
@@ -529,7 +519,7 @@
|
|
|
529
519
|
&.el-form.el-form--label-right .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label::before {
|
|
530
520
|
content: "*" !important;
|
|
531
521
|
color: var(--el-color-danger, #f56c6c) !important;
|
|
532
|
-
font-size: 12px !important;
|
|
522
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
533
523
|
font-weight: 400 !important;
|
|
534
524
|
line-height: var(--wk-form-label-line-height, 22px) !important;
|
|
535
525
|
display: inline-block !important;
|
|
@@ -545,7 +535,7 @@
|
|
|
545
535
|
&.el-form.el-form--label-right .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label > .com-text::before {
|
|
546
536
|
content: "*" !important;
|
|
547
537
|
color: var(--el-color-danger, #f56c6c) !important;
|
|
548
|
-
font-size: 12px !important;
|
|
538
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
549
539
|
font-weight: 400 !important;
|
|
550
540
|
line-height: var(--wk-form-label-line-height, 22px) !important;
|
|
551
541
|
display: inline-block !important;
|
|
@@ -572,7 +562,7 @@
|
|
|
572
562
|
&.el-form.el-form--label-right .el-form-item.is-required:not(.is-no-asterisk).asterisk-right > .el-form-item__label::after {
|
|
573
563
|
content: "*" !important;
|
|
574
564
|
color: var(--el-color-danger, #f56c6c) !important;
|
|
575
|
-
font-size: 12px !important;
|
|
565
|
+
font-size: var(--wk-form-font-size, 12px) !important;
|
|
576
566
|
font-weight: 400 !important;
|
|
577
567
|
line-height: var(--wk-form-label-line-height, 22px) !important;
|
|
578
568
|
display: inline-block !important;
|
|
@@ -606,6 +596,20 @@
|
|
|
606
596
|
.el-textarea__inner {
|
|
607
597
|
box-shadow: 0 0 0 1px var(--el-color-danger, #f56c6c) inset !important;
|
|
608
598
|
}
|
|
599
|
+
|
|
600
|
+
// InputNumber 的 focus 兼容规则权重较高;非禁用 error 必须最终覆盖 focus。
|
|
601
|
+
.el-input-number:not(.is-disabled) .el-input__wrapper,
|
|
602
|
+
.el-input-number.el-input__wrapper:not(.is-disabled) {
|
|
603
|
+
box-shadow:
|
|
604
|
+
0 0 0 1px var(--el-color-danger, #f56c6c) inset !important;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// 过渡版双 wrapper 由外层表达 error,内层不得出现第二道危险色边框。
|
|
608
|
+
.el-input-number.el-input__wrapper:not(.is-disabled)
|
|
609
|
+
> .el-input
|
|
610
|
+
> .el-input__wrapper {
|
|
611
|
+
box-shadow: none !important;
|
|
612
|
+
}
|
|
609
613
|
}
|
|
610
614
|
}
|
|
611
615
|
|
|
@@ -354,7 +354,7 @@
|
|
|
354
354
|
cursor: not-allowed !important;
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
// ──
|
|
357
|
+
// ── 工具栏下拉 / 分裂按钮 ──────────────────────────────────────────────────
|
|
358
358
|
.base-toolbar-box .el-dropdown > .el-button,
|
|
359
359
|
.base-toolbar-box .el-button-group .el-button {
|
|
360
360
|
height: 24px !important;
|
|
@@ -363,25 +363,68 @@
|
|
|
363
363
|
font-weight: 500;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
|
|
366
|
+
// BaseToolbar 仅在“主动作有 onClick 且还有 children”时生成 split-button。
|
|
367
|
+
// 精确限定 action-button-wrap,避免影响页面自行编排的普通 el-button-group。
|
|
368
|
+
.base-toolbar-box
|
|
369
|
+
.action-button-wrap
|
|
370
|
+
> .el-dropdown
|
|
371
|
+
> .el-button-group {
|
|
367
372
|
display: inline-flex !important;
|
|
368
373
|
align-items: center !important;
|
|
369
374
|
vertical-align: middle !important;
|
|
375
|
+
gap: 0 !important;
|
|
376
|
+
overflow: hidden;
|
|
377
|
+
border-radius: var(--el-border-radius-base) !important;
|
|
370
378
|
|
|
371
|
-
.el-button {
|
|
379
|
+
> .el-button {
|
|
372
380
|
border-radius: 0 !important;
|
|
373
381
|
margin: 0 !important;
|
|
382
|
+
box-shadow: none !important;
|
|
374
383
|
}
|
|
375
384
|
|
|
376
|
-
.el-button:first-child {
|
|
385
|
+
> .el-button:first-child {
|
|
377
386
|
border-top-left-radius: var(--el-border-radius-base) !important;
|
|
378
387
|
border-bottom-left-radius: var(--el-border-radius-base) !important;
|
|
388
|
+
border-top-right-radius: 0 !important;
|
|
389
|
+
border-bottom-right-radius: 0 !important;
|
|
390
|
+
border-right-color: transparent !important;
|
|
379
391
|
}
|
|
380
392
|
|
|
381
|
-
.el-button
|
|
393
|
+
> .el-button.el-dropdown__caret-button {
|
|
394
|
+
width: 24px !important;
|
|
395
|
+
min-width: 24px !important;
|
|
396
|
+
padding: 0 !important;
|
|
397
|
+
margin-left: 0 !important;
|
|
398
|
+
border-left: 0 !important;
|
|
399
|
+
border-top-left-radius: 0 !important;
|
|
400
|
+
border-bottom-left-radius: 0 !important;
|
|
382
401
|
border-top-right-radius: var(--el-border-radius-base) !important;
|
|
383
402
|
border-bottom-right-radius: var(--el-border-radius-base) !important;
|
|
384
|
-
|
|
403
|
+
|
|
404
|
+
// Element Plus 默认用 ::before 画分割线,会让箭头看起来像独立色块。
|
|
405
|
+
&::before {
|
|
406
|
+
display: none !important;
|
|
407
|
+
content: none !important;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 鼠标或键盘进入任一分段时,两段同步使用同一状态色,视觉保持一个按钮。
|
|
412
|
+
&:hover > .el-button:not(.is-disabled):not([disabled]),
|
|
413
|
+
&:focus-within > .el-button:not(.is-disabled):not([disabled]) {
|
|
414
|
+
color: var(--el-button-hover-text-color) !important;
|
|
415
|
+
border-color: var(--el-button-hover-border-color) !important;
|
|
416
|
+
background-color: var(--el-button-hover-bg-color) !important;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
&:hover > .el-button:first-child:not(.is-disabled):not([disabled]),
|
|
420
|
+
&:focus-within > .el-button:first-child:not(.is-disabled):not([disabled]) {
|
|
421
|
+
border-right-color: transparent !important;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
&:active > .el-button:not(.is-disabled):not([disabled]) {
|
|
425
|
+
color: var(--el-button-active-text-color, #ffffff) !important;
|
|
426
|
+
border-color: var(--el-button-active-border-color) !important;
|
|
427
|
+
background-color: var(--el-button-active-bg-color) !important;
|
|
385
428
|
}
|
|
386
429
|
}
|
|
387
430
|
|
|
@@ -393,23 +436,84 @@
|
|
|
393
436
|
margin: 0 !important;
|
|
394
437
|
}
|
|
395
438
|
|
|
396
|
-
|
|
439
|
+
// BaseToolbar V1 会在 action-dropdown-menu 的 item 内再渲染一个 jh-button。
|
|
440
|
+
// 只扁平化这个已知结构,不影响普通 el-dropdown 和业务自定义下拉。
|
|
441
|
+
html body .el-dropdown-menu.action-dropdown-menu .el-dropdown-menu__item {
|
|
397
442
|
display: flex;
|
|
398
443
|
align-items: center;
|
|
399
444
|
gap: 6px;
|
|
400
445
|
min-height: 28px;
|
|
401
446
|
line-height: 1.3;
|
|
447
|
+
padding: 0 !important;
|
|
402
448
|
color: var(--el-text-color-regular, #606266);
|
|
403
449
|
}
|
|
404
450
|
|
|
405
|
-
|
|
406
|
-
|
|
451
|
+
html
|
|
452
|
+
body
|
|
453
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
454
|
+
.el-dropdown-menu__item
|
|
455
|
+
> .el-button {
|
|
456
|
+
width: 100% !important;
|
|
457
|
+
height: 28px !important;
|
|
458
|
+
min-height: 28px !important;
|
|
459
|
+
justify-content: flex-start !important;
|
|
460
|
+
margin: 0 !important;
|
|
461
|
+
padding: 0 10px !important;
|
|
462
|
+
border: 0 !important;
|
|
463
|
+
border-radius: inherit !important;
|
|
464
|
+
color: inherit !important;
|
|
465
|
+
background: transparent !important;
|
|
466
|
+
box-shadow: none !important;
|
|
467
|
+
font-size: 12px !important;
|
|
468
|
+
font-weight: 400 !important;
|
|
469
|
+
line-height: 1.3 !important;
|
|
470
|
+
transform: none !important;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
html
|
|
474
|
+
body
|
|
475
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
476
|
+
.el-dropdown-menu__item
|
|
477
|
+
> .el-button:hover,
|
|
478
|
+
html
|
|
479
|
+
body
|
|
480
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
481
|
+
.el-dropdown-menu__item
|
|
482
|
+
> .el-button:focus,
|
|
483
|
+
html
|
|
484
|
+
body
|
|
485
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
486
|
+
.el-dropdown-menu__item
|
|
487
|
+
> .el-button:active {
|
|
488
|
+
color: inherit !important;
|
|
489
|
+
border: 0 !important;
|
|
490
|
+
background: transparent !important;
|
|
491
|
+
box-shadow: none !important;
|
|
492
|
+
transform: none !important;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
html
|
|
496
|
+
body
|
|
497
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
498
|
+
.el-dropdown-menu__item:not(.is-disabled):hover,
|
|
499
|
+
html
|
|
500
|
+
body
|
|
501
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
502
|
+
.el-dropdown-menu__item:not(.is-disabled):focus {
|
|
407
503
|
color: var(--el-color-primary, #002a8f) !important;
|
|
408
504
|
background-color: rgba(var(--el-color-primary-rgb), 0.08) !important;
|
|
409
505
|
}
|
|
410
506
|
|
|
411
|
-
|
|
412
|
-
|
|
507
|
+
html
|
|
508
|
+
body
|
|
509
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
510
|
+
.el-dropdown-menu__item
|
|
511
|
+
.el-icon,
|
|
512
|
+
html
|
|
513
|
+
body
|
|
514
|
+
.el-dropdown-menu.action-dropdown-menu
|
|
515
|
+
.el-dropdown-menu__item
|
|
516
|
+
svg {
|
|
413
517
|
color: currentColor !important;
|
|
414
518
|
fill: currentColor;
|
|
415
519
|
flex: 0 0 auto;
|
|
@@ -24,7 +24,7 @@ columnsDef(): TableColumnDesc<any>[] {
|
|
|
24
24
|
|
|
25
25
|
// ── 数据列(已注册字段自动映射,无需 defaultNode)──
|
|
26
26
|
{ name: 'riskNo', label: '编号', width: 100 }, // → renderBadge
|
|
27
|
-
{ name: 'name', label: '名称', minWidth: 150 },
|
|
27
|
+
{ name: 'name', label: '名称', minWidth: 150 }, // → 自动省略 + 悬停完整提示
|
|
28
28
|
{ name: 'riskLevel', label: '风险分级', width: 90 }, // → renderRiskLevel Tag
|
|
29
29
|
{ name: 'riskStatus', label: '状态', width: 90 }, // → renderRiskStatus Tag
|
|
30
30
|
|
|
@@ -74,3 +74,4 @@ toolbarDef() {
|
|
|
74
74
|
2. 操作列用 `renderOps([...])` — 图标按钮 + 自动 stopPropagation
|
|
75
75
|
3. selection 列必须同时设置 `align: 'center'` + `headerAlign: 'center'`(R014)
|
|
76
76
|
4. 分类字段(type/level)用 `renderClassifyTag`,状态字段用 `renderTagNode`
|
|
77
|
+
5. `defineColumns()` 自动为普通文本列补 `showOverflowTooltip: true`;自定义渲染、结构列和换行列除外
|
|
@@ -107,6 +107,7 @@ const modal = ref();
|
|
|
107
107
|
function columnsDef() {
|
|
108
108
|
return defineColumns([
|
|
109
109
|
{ type: "index", label: "序号", width: 60, align: "center" },
|
|
110
|
+
// 普通文本列由 defineColumns 自动补 showOverflowTooltip: true
|
|
110
111
|
{ name: "name", label: "名称", minWidth: 120 },
|
|
111
112
|
{ name: "status", label: "状态", width: 80 }, // enableStatus 已自动映射
|
|
112
113
|
{ name: "remark", label: "备注", minWidth: 160 },
|
|
@@ -141,3 +142,5 @@ async function handleDel(id: string) {
|
|
|
141
142
|
3. `enableStatus` 字段在 `installCommonPreset()` 后自动渲染为彩色 Tag,无需手写 defaultNode
|
|
142
143
|
4. 工具栏按钮必须带 `icon`(R005 规则)
|
|
143
144
|
5. 标准顺序固定为“查询区 → 工具栏 → 列表标题 → 表格 → 分页器”;工具栏不得放到标题右侧或标题下方
|
|
145
|
+
6. 新增/新建/添加/创建类主按钮必须 `type="primary"` 且不得使用 `plain`(R038)
|
|
146
|
+
7. 普通文本列由 `defineColumns()` 自动启用省略号与悬停完整提示;原生 `el-table-column` 必须加 `show-overflow-tooltip`(R039)
|