@agile-team/wl-skills-ui 1.9.4 → 1.9.5

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,19 @@ 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.5] - 2026-07-23
8
+
9
+ ### Fixed
10
+
11
+ - 兼容 Element Plus 2.2 的数字输入框 DOM,修复输入后边框消失、焦点双边框、上下控制器压盖内容等问题。
12
+ - 重构 `jh-drag-row` 横向拖拽手柄为稳定的三点胶囊样式,并恢复上下分栏边界的精细层次。
13
+ - 修正 AG Grid 空数据覆盖层的定位上下文,使“没有可显示的行”在实际可视表格区域内水平、垂直居中。
14
+ - 完善 BaseToolbar 多按钮语义色、禁用态、图标间距以及 BaseQuery 展开/收起按钮视觉。
15
+
16
+ ### Changed
17
+
18
+ - 列表页规范统一为“查询区 → 工具栏 → 列表标题 → 表格 → 分页器”,工具栏独占一行并左对齐。
19
+
7
20
  ## [1.9.4] - 2026-07-22
8
21
 
9
22
  ### 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.4` | 已对齐上述组合的 DOM 假设、客户主题锁、圆角契约与定制页样式边界 |
203
+ | `@agile-team/wl-skills-ui` | `^1.9.5` | 已对齐上述组合的 DOM 假设、客户主题锁、圆角契约与定制页样式边界 |
204
204
 
205
205
  三种识别方式,任选其一:
206
206
 
@@ -214,7 +214,7 @@ yarn add @agile-team/wl-skills-ui
214
214
 
215
215
  ## 版本亮点
216
216
 
217
- 当前 v1.9.4
217
+ 当前 v1.9.5
218
218
 
219
219
  - **Vite 4 运行时兼容**:浏览器 ESM 产物不再暴露可被开发转换误替换的 Node 环境变量成员链,预构建与直接加载均保持合法语法。
220
220
  - **登录页精准退出**:内置识别 `.lp-root` 与 `.session-login`,其内部表单、按钮、焦点和校验态保留登录页自身设计;业务页面仍由包高权重统一管控。
@@ -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.4` | 已对齐 jh-ui 3.1.0 + EP 2.2.6-prod.3 的 DOM 假设、客户主题锁、6px 基础圆角、定制页样式边界与 Vite 4 开发转换 |
14
+ | `@agile-team/wl-skills-ui` | `^1.9.5` | 已对齐 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.4 三方对齐。
38
+ - 集团内项目:**统一锚定 `element-plus@2.2.6-prod.3` + `@jhlc/jh-ui@3.1.0`**,与 wl-skills-ui v1.9.5 三方对齐。
39
39
  - 升级 `element-plus` 到 2.3+ 前,必须同步升级 `@jhlc/jh-ui` 到对应支持版本,否则 `.com-text` / `.el-input__inner` DOM 与 EP `__wrapper` 体系不兼容。
40
40
  - 新项目接入:见 README 「快速接入」章节,默认会按本表声明 peerDependency 范围。
41
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-ui",
3
- "version": "1.9.4",
3
+ "version": "1.9.5",
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",
@@ -59,6 +59,15 @@ el-date-picker 默认宽度不会自动撑满 el-form-item,必须显式设置
59
59
 
60
60
  ---
61
61
 
62
+ ## 表单列数与输入宽度 【中危】
63
+
64
+ - 全宽业务表单默认 4 列,严禁使用 5~8 列把输入框压缩到无法阅读或填写。
65
+ - 单个控件可输入区不足 160px 时降为 3 列;长标签、带单位字段按同样标准判断。
66
+ - 字段较多时使用分区、Tab 或展开/收起,首屏建议展示 4~6 行。
67
+ - 不依赖步进操作的数字输入传入 `controls=false`,不得用 CSS 强行覆盖出第二层焦点边框。
68
+
69
+ ---
70
+
62
71
  ## 完整标准表单示例
63
72
 
64
73
  ```html
@@ -9,13 +9,14 @@ applyTo: "**/*.vue"
9
9
 
10
10
  ## 适用场景
11
11
 
12
- 页面结构为:**搜索栏 → 工具栏(增删按钮) → 数据表格 → 分页器** 的经典 CRUD 列表页。
12
+ 页面结构为:**搜索栏 → 工具栏(增删按钮) → 列表标题 → 数据表格 → 分页器** 的经典 CRUD 列表页。
13
13
 
14
14
  ---
15
15
 
16
16
  ## Detect — 识别列表页
17
17
 
18
18
  ### 方式一:结构特征识别
19
+
19
20
  ```
20
21
  检测条件(满足任意 2 项即判定为列表页):
21
22
  ✓ 根节点或第一子节点含 class="list-page" / "page-wrapper" / "table-page"
@@ -26,6 +27,7 @@ applyTo: "**/*.vue"
26
27
  ```
27
28
 
28
29
  ### 方式二:命令触发
30
+
29
31
  ```
30
32
  用 wl-ui 的 layouts/list-page skill 检查这个文件
31
33
  → AI 加载本 SKILL.md,对当前文件做 Detect → Diagnose → Repair
@@ -37,13 +39,15 @@ applyTo: "**/*.vue"
37
39
 
38
40
  ### 结构层问题
39
41
 
40
- | 问题 | 规则 | 说明 |
41
- |---|---|---|
42
- | 根节点缺少 `.list-page` | L-001 | 应为 `<div class="list-page">` |
43
- | 搜索区未用 `.list-page__query` | L-002 | 内部结构应遵循 BEM |
44
- | 工具栏未用 `.list-page__toolbar` | L-003 | 按钮组应在 `.list-page__toolbar` 内 |
45
- | 表格区未用 `.list-page__table` | L-004 | 表格容器应有独立 class |
46
- | 分页区未用 `.list-page__pagination` | L-005 | 分页器位置需在 **内容区**,不得在 dialog footer |
42
+ | 问题 | 规则 | 说明 |
43
+ | ----------------------------------- | ----- | ------------------------------------------------ |
44
+ | 根节点缺少 `.list-page` | L-001 | 应为 `<div class="list-page">` |
45
+ | 搜索区未用 `.list-page__query` | L-002 | 内部结构应遵循 BEM |
46
+ | 工具栏未用 `.list-page__toolbar` | L-003 | 按钮组应在 `.list-page__toolbar` 内 |
47
+ | 工具栏与标题同排或位于标题下方 | L-006 | 工具栏应在标题上、表格上独占一行并左对齐 |
48
+ | 分页列表标题重复显示“共 N 条” | L-007 | 分页器已有总数,仅非分页短列表可在标题旁显示条数 |
49
+ | 表格区未用 `.list-page__table` | L-004 | 表格容器应有独立 class |
50
+ | 分页区未用 `.list-page__pagination` | L-005 | 分页器位置需在 **内容区**,不得在 dialog footer |
47
51
 
48
52
  ### 内容层问题(复用 scanner 规则)
49
53
 
@@ -66,12 +70,20 @@ applyTo: "**/*.vue"
66
70
  <BaseQuery :params="searchParams" @search="reload" @reset="reset" />
67
71
  </div>
68
72
 
69
- <!-- 工具栏 -->
73
+ <!-- 工具栏:标题上方、表格上方、左对齐,独占一行 -->
70
74
  <div class="list-page__toolbar">
71
- <el-button type="primary" icon="Plus" @click="modal.add()">新增</el-button>
75
+ <el-button type="primary" icon="Plus" @click="modal.add()"
76
+ >新增</el-button
77
+ >
78
+ <el-button type="warning" plain icon="Edit" @click="modal.edit()"
79
+ >修改</el-button
80
+ >
72
81
  <el-button icon="Download" @click="handleExport">导出</el-button>
73
82
  </div>
74
83
 
84
+ <!-- 列表标题:位于按钮组下方,不与按钮同排 -->
85
+ <div class="list-page__title">[列表标题]</div>
86
+
75
87
  <!-- 表格 -->
76
88
  <div class="list-page__table">
77
89
  <BaseTable :hook="page" empty-text="暂无数据" :columns="columns" />
@@ -85,7 +97,7 @@ applyTo: "**/*.vue"
85
97
  </template>
86
98
  ```
87
99
 
88
- ### SCSS 对应(由 styles/layouts/_list-page.scss 提供)
100
+ ### SCSS 对应(由 styles/layouts/\_list-page.scss 提供)
89
101
 
90
102
  ```scss
91
103
  // 业务侧无需手写,全局 @use 后自动生效
@@ -106,6 +118,6 @@ applyTo: "**/*.vue"
106
118
 
107
119
  ## 关联资源
108
120
 
109
- - 样式实现:[styles/layouts/_list-page.scss](../../../styles/layouts/_list-page.scss)
121
+ - 样式实现:[styles/layouts/\_list-page.scss](../../../styles/layouts/_list-page.scss)
110
122
  - 代码模板:[templates/list-page/TPL-LIST.md](../../../templates/list-page/TPL-LIST.md)
111
123
  - 相关 skills:[vendors/base-table](../../vendors/base-table/SKILL.md) | [vendors/jh-components](../../vendors/jh-components/SKILL.md)
@@ -13,16 +13,16 @@ applyTo: "**/*.vue"
13
13
 
14
14
  所有 `<jh-*>` 标签均归入本 Skill。下表是当前项目集群的代表性基线,不是完整清单。
15
15
 
16
- | 类型 | 子组件 | 标签 | 关键类名 | 治理方式 |
17
- |---|---|---|---|---|
18
- | SCSS 皮肤适配 | `@jhlc/jh-ui` | — | `.com-text` / `.text-line-2` / `.com-input` / `.com-picker` / `.com-reference-picker` / `.com-textarea` / `.com-input-tip` | `styles/vendors/_jh-ui.scss` |
19
- | 专项样式覆盖 | jh-tree | `<jh-tree>` | `.jh-tree` / `.base-tree` | `styles/vendors/_jh-tree.scss` |
20
- | 专项样式覆盖 | jh-pagination | `<jh-pagination>` | `.jh-pagination` | `styles/vendors/_jh-pagination.scss` |
21
- | 专项样式覆盖 | jh-drag-col | `<jh-drag-col>` | `.drag-col-container` / `.drag-left` / `.slider-col` | `styles/vendors/_jh-drag-col.scss` |
22
- | 专项样式覆盖 | jh-drag-row | `<jh-drag-row>` | `.drager_row` / `.drager_top` / `.drager_bottom` / `.slider_row` | `styles/vendors/_jh-drag-row.scss` |
23
- | 通用规则治理 | jh-table | `<jh-table>` | `.jh-table` | 继承 L0 tokens + L1 table 视觉原则 |
24
- | 通用规则治理 | jh-form | `<jh-form>` | `.jh-form` | 继承 L0 tokens + L1 form 视觉原则 |
25
- | 通用规则治理 | 其它 jh-* | `<jh-*>` | `.jh-*` / 组件内部 Element Plus 类 | 先按 jh 通用规则治理,复杂结构再升级专项样式 |
16
+ | 类型 | 子组件 | 标签 | 关键类名 | 治理方式 |
17
+ | ------------- | ------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
18
+ | SCSS 皮肤适配 | `@jhlc/jh-ui` | — | `.com-text` / `.text-line-2` / `.com-input` / `.com-picker` / `.com-reference-picker` / `.com-textarea` / `.com-input-tip` | `styles/vendors/_jh-ui.scss` |
19
+ | 专项样式覆盖 | jh-tree | `<jh-tree>` | `.jh-tree` / `.base-tree` | `styles/vendors/_jh-tree.scss` |
20
+ | 专项样式覆盖 | jh-pagination | `<jh-pagination>` | `.jh-pagination` | `styles/vendors/_jh-pagination.scss` |
21
+ | 专项样式覆盖 | jh-drag-col | `<jh-drag-col>` | `.drag-col-container` / `.drag-left` / `.slider-col` | `styles/vendors/_jh-drag-col.scss` |
22
+ | 专项样式覆盖 | jh-drag-row | `<jh-drag-row>` | `.drager_row` / `.drager_top` / `.drager_bottom` / `.slider_row` | `styles/vendors/_jh-drag-row.scss` |
23
+ | 通用规则治理 | jh-table | `<jh-table>` | `.jh-table` | 继承 L0 tokens + L1 table 视觉原则 |
24
+ | 通用规则治理 | jh-form | `<jh-form>` | `.jh-form` | 继承 L0 tokens + L1 form 视觉原则 |
25
+ | 通用规则治理 | 其它 jh-\* | `<jh-*>` | `.jh-*` / 组件内部 Element Plus 类 | 先按 jh 通用规则治理,复杂结构再升级专项样式 |
26
26
 
27
27
  ### `@jhlc/jh-ui` SCSS 皮肤包识别
28
28
 
@@ -47,7 +47,7 @@ applyTo: "**/*.vue"
47
47
 
48
48
  - ❌ 自行给 `.jh-tree` 写颜色覆盖(应用全局 `vendors/_jh-tree.scss`)
49
49
  - ❌ jh-drag-col 内自定义 padding 破坏拖拽条对齐
50
- - ❌ jh-drag-row 上下分栏缺 `.drager_row { height:100% }` 高度链(拖拽失效),或手柄未走统一 `.slider_row` 细线风格
50
+ - ❌ jh-drag-row 上下分栏缺 `.drager_row { height:100% }` 高度链(拖拽失效),或页面自行重画 `.slider_row`;手柄必须统一使用 `_jh-drag-row.scss` 的细分隔线 + 三点胶囊
51
51
  - ❌ jh-pagination 未对齐到右侧(同 R011)
52
52
  - ❌ jh-form 内不用 `size="small"` 控件(同 R006)
53
53
  - ❌ 发现新的复杂 `<jh-*>` 组件后只在页面局部写补丁,而不沉淀到 L2 Project Vendors
@@ -55,10 +55,12 @@ applyTo: "**/*.vue"
55
55
  ## Repair
56
56
 
57
57
  ### A 类
58
+
58
59
  - 控件 size 缺失 → 补 `size="small"`
59
60
  - 表单内日期选择器无宽度 → `style="width:100%"`(R007)
60
61
 
61
62
  ### B 类
63
+
62
64
  - 直接全局覆盖 `.jh-*` → 改为引入 `wl-skills-ui/styles` 由 vendors 层处理
63
65
  - 新的复杂 `<jh-*>` 组件 → 先判断是否只是 Element Plus 薄封装;若不是,应新增 `styles/vendors/_jh-xxx.scss` 和对应 Skill/检测规则
64
66
 
@@ -2,7 +2,10 @@
2
2
 
3
3
  ## 工具栏按钮(列表页顶部)
4
4
 
5
+ 工具栏必须独占一行,放在列表标题上方、表格上方并左对齐;不得与标题/条数统计挤在同一行。标准顺序为“查询区 → 工具栏 → 列表标题 → 表格 → 分页器”。
6
+
5
7
  ### 顺序规则
8
+
6
9
  1. **新增/新增申请**类按钮永远排第一
7
10
  2. 其余按钮按照操作频次降序排列
8
11
 
@@ -10,29 +13,33 @@
10
13
 
11
14
  主按钮色值严格遵循客户 UI 规范,禁止使用 `#2254F4`、`#4368FF` 或 Element Plus 默认蓝替代:
12
15
 
13
- | 状态 | Token | 规范色值 |
14
- |---|---|---|
15
- | 常规 | `--el-color-primary` | `#002A8F` |
16
+ | 状态 | Token | 规范色值 |
17
+ | ------------- | ---------------------------- | --------- |
18
+ | 常规 | `--el-color-primary` | `#002A8F` |
16
19
  | hover / focus | `--el-color-primary-light-1` | `#1A3F9A` |
17
- | active / 点击 | `--el-color-primary-dark-1` | `#002681` |
18
- | disabled | `--el-color-primary-light-7` | `#B2BFDD` |
20
+ | active / 点击 | `--el-color-primary-dark-1` | `#002681` |
21
+ | disabled | `--el-color-primary-light-7` | `#B2BFDD` |
19
22
 
20
23
  平台动态主题不得改写品牌按钮体系。包内 L0 token 使用 `!important`,runtime 主题锁负责恢复后续写入 `document.body.style` 的旧主色及功能色;业务页面不要再用局部选择器与主题锁对冲。
21
24
 
22
- | 操作语义 | type | plain | 说明 |
23
- |----------|-------------|--------|----------------|
24
- | 新增/添加 | `primary` | false | 蓝色填充 |
25
- | 导入 | `primary` | true | 蓝色线框 |
26
- | 导出 | `success` | true | 绿色线框 |
27
- | 删除/作废 | `danger` | false | 红色填充(批量操作慎用)|
28
- | 审批/提交 | `primary` | false | 蓝色填充 |
29
- | 重置/取消 | `default` | false | 默认灰色 |
25
+ | 操作语义 | type | plain | 说明 |
26
+ | -------------- | --------- | ----- | ------------------------ |
27
+ | 新增/添加 | `primary` | false | 蓝色填充 |
28
+ | 保存 | `success` | false | 绿色填充 |
29
+ | 下达/提交/确认 | `primary` | false | 当前流程的主动作 |
30
+ | 修改/调整/变更 | `warning` | true | 橙色轻色面 |
31
+ | 导入/导出/查询 | `default` | true | 中性线框 |
32
+ | 删除/作废 | `danger` | false | 红色填充(批量操作慎用) |
33
+ | 重置/取消 | `default` | true | 默认灰色线框 |
34
+
35
+ 同一按钮组超过 4 个动作时,只允许当前主动作和破坏性动作使用实心按钮;其余语义按钮使用 `plain` 轻色面。每个按钮必须有与动作一致的图标,未知业务动作不得回退为“无图标的 primary”。
30
36
 
31
37
  ```vue
32
38
  <!-- ✅ 工具栏按钮组 -->
33
39
  <el-button type="primary" @click="handleCreate">新增</el-button>
34
- <el-button type="primary" plain @click="handleImport">导入</el-button>
35
- <el-button type="success" plain @click="handleExport">导出</el-button>
40
+ <el-button type="warning" plain icon="Edit" @click="handleEdit">修改</el-button>
41
+ <el-button type="success" icon="Save" @click="handleSave">保存</el-button>
42
+ <el-button icon="Download" @click="handleExport">导出</el-button>
36
43
  ```
37
44
 
38
45
  ---
@@ -44,35 +51,35 @@
44
51
  ```typescript
45
52
  // ✅ 正确
46
53
  renderOps([
47
- { type: 'edit', onClick: () => handleEdit(row) },
48
- { type: 'del', onClick: () => handleVoid(row) },
49
- ])
54
+ { type: "edit", onClick: () => handleEdit(row) },
55
+ { type: "del", onClick: () => handleVoid(row) },
56
+ ]);
50
57
  ```
51
58
 
52
59
  ### 视觉状态
53
60
 
54
- | 状态 | 标准表现 |
55
- |---|---|
56
- | 默认可用 | 柔和主题蓝,透明背景 |
57
- | hover | 查看/记录/提交变主题蓝,编辑/审核变绿,删除/作废变红 |
58
- | 禁用 | 浅灰且 hover 不变色,使用原生 `disabled` |
59
- | 选中行 | 保持默认主题蓝,不因选中自动变为语义色 |
61
+ | 状态 | 标准表现 |
62
+ | -------- | ---------------------------------------------------- |
63
+ | 默认可用 | 柔和主题蓝,透明背景 |
64
+ | hover | 查看/记录/提交变主题蓝,编辑/审核变绿,删除/作废变红 |
65
+ | 禁用 | 浅灰且 hover 不变色,使用原生 `disabled` |
66
+ | 选中行 | 保持默认主题蓝,不因选中自动变为语义色 |
60
67
 
61
68
  ### 按钮标签严格对应原型
62
69
 
63
- | 操作 | 正确标签 | 禁止替换为 |
64
- |--------|---------|----------|
65
- | 修改记录 | **修改** | ~~编辑~~ |
66
- | 软删除 | **作废** | ~~删除~~ |
67
- | 硬删除 | **删除** | ~~移除~~ |
70
+ | 操作 | 正确标签 | 禁止替换为 |
71
+ | -------- | -------- | ---------- |
72
+ | 修改记录 | **修改** | ~~编辑~~ |
73
+ | 软删除 | **作废** | ~~删除~~ |
74
+ | 硬删除 | **删除** | ~~移除~~ |
68
75
 
69
76
  ### 条件显示
70
77
 
71
78
  ```typescript
72
79
  renderOps([
73
- { type: 'del', show: () => canDelete, onClick: () => handleDelete(row) },
74
- { type: 'edit', show: () => canEdit, onClick: () => handleEdit(row) },
75
- ])
80
+ { type: "del", show: () => canDelete, onClick: () => handleDelete(row) },
81
+ { type: "edit", show: () => canEdit, onClick: () => handleEdit(row) },
82
+ ]);
76
83
  ```
77
84
 
78
85
  ---
@@ -90,6 +97,7 @@ renderOps([
90
97
  ```
91
98
 
92
99
  CSS 全局保证 footer 右对齐(已在 `dist/style-override.scss` 中定义):
100
+
93
101
  ```scss
94
102
  .el-dialog__footer .dialog-footer {
95
103
  display: flex;
@@ -6,10 +6,7 @@
6
6
 
7
7
  ```vue
8
8
  <!-- ❌ 错误:100px 会截断9字标签 -->
9
- <el-form :model="form" label-width="100px">
10
-
11
- <!-- ✅ 正确 -->
12
- <el-form :model="form" label-width="150px">
9
+ <el-form :model="form" label-width="100px"></el-form>
13
10
  ```
14
11
 
15
12
  ---
@@ -21,11 +18,7 @@
21
18
  ```vue
22
19
  <!-- ❌ 错误 -->
23
20
  <el-input v-model="form.name" />
24
- <el-select v-model="form.type">
25
-
26
- <!-- ✅ 正确 -->
27
- <el-input v-model="form.name" size="small" />
28
- <el-select v-model="form.type" size="small">
21
+ <el-select v-model="form.type"></el-select>
29
22
  ```
30
23
 
31
24
  ---
@@ -76,12 +69,19 @@ textarea 不强制 26px 高度,只继承统一圆角、字体和状态样式
76
69
 
77
70
  ## 规则:表单 label 不强制冒号
78
71
 
79
- Element Plus 原生控件、picker 类控件和 jh-* 封装控件的 label 后不强制追加 `:`。`@jhlc/jh-ui` 的 `.has-colon .com-text:after` / `.text-line-2:after` 冒号注入由 wl-skills-ui 统一屏蔽,避免 input / select / picker 之间出现有的带冒号、有的不带冒号。
72
+ Element Plus 原生控件、picker 类控件和 jh-\* 封装控件的 label 后不强制追加 `:`。`@jhlc/jh-ui` 的 `.has-colon .com-text:after` / `.text-line-2:after` 冒号注入由 wl-skills-ui 统一屏蔽,避免 input / select / picker 之间出现有的带冒号、有的不带冒号。
80
73
 
81
74
  ---
82
75
 
83
76
  ## 布局标准
84
77
 
78
+ ### 列数与可用宽度
79
+
80
+ - 独立作业页/实绩页的主表单最多 4 列;不得为了“一行塞完”使用 5~8 列。
81
+ - 单个控件的实际可输入宽度应不小于 160px;标签较长、单位较多或容器变窄时,应主动降为 3 列。
82
+ - 字段很多时优先分区、Tab 或“展开更多”,不要压缩输入框。默认区域建议控制在 4~6 行。
83
+ - 数字输入若业务不依赖步进操作,应设置 `controls=false`,避免步进按钮挤占内容宽度。
84
+
85
85
  ### 搜索区(列表页顶部)
86
86
 
87
87
  ```vue
@@ -105,12 +105,7 @@ Element Plus 原生控件、picker 类控件和 jh-* 封装控件的 label 后
105
105
  ### 弹窗表单(新增/修改)
106
106
 
107
107
  ```vue
108
- <el-form
109
- ref="formRef"
110
- :model="form"
111
- :rules="rules"
112
- label-width="150px"
113
- >
108
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="150px">
114
109
  <el-row :gutter="20">
115
110
  <el-col :span="12">
116
111
  <el-form-item label="名称" prop="name">
@@ -135,11 +130,11 @@ Element Plus 原生控件、picker 类控件和 jh-* 封装控件的 label 后
135
130
 
136
131
  ## 复杂表单判断
137
132
 
138
- | 条件 | 方案 |
139
- |-----|------|
140
- | 字段 ≤ 15,无子表 | 弹窗(`el-dialog`) |
133
+ | 条件 | 方案 |
134
+ | ----------------------- | ------------------------- |
135
+ | 字段 ≤ 15,无子表 | 弹窗(`el-dialog`) |
141
136
  | 字段 > 15,或含多个子表 | 独立路由页(`/xxx-form`) |
142
- | Tab > 3 个 | 独立路由页 |
137
+ | Tab > 3 个 | 独立路由页 |
143
138
 
144
139
  ---
145
140
 
@@ -147,8 +142,8 @@ Element Plus 原生控件、picker 类控件和 jh-* 封装控件的 label 后
147
142
 
148
143
  ```typescript
149
144
  const rules = {
150
- name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
151
- type: [{ required: true, message: '请选择类型', trigger: 'change' }],
152
- date: [{ required: true, message: '请选择日期', trigger: 'change' }],
153
- }
145
+ name: [{ required: true, message: "请输入名称", trigger: "blur" }],
146
+ type: [{ required: true, message: "请选择类型", trigger: "change" }],
147
+ date: [{ required: true, message: "请选择日期", trigger: "change" }],
148
+ };
154
149
  ```
@@ -47,6 +47,24 @@ html body {
47
47
  line-height: calc(var(--wk-form-control-height) - 2px) !important;
48
48
  }
49
49
 
50
+ // number 输入只由 `.el-input__wrapper` 绘制焦点环。浏览器原生 outline
51
+ // 或内部 input 的 box-shadow 会与 wrapper 叠成双边框,并压到步进按钮分隔线。
52
+ &.el-input-number,
53
+ &.el-input-number:focus,
54
+ &.el-input-number:focus-within,
55
+ &.el-input-number .el-input,
56
+ &.el-input-number .el-input__inner,
57
+ &.el-input-number .el-input__inner:focus,
58
+ &.el-input-number .el-input__inner:focus-visible {
59
+ outline: none !important;
60
+ }
61
+
62
+ &.el-input-number .el-input__inner,
63
+ &.el-input-number .el-input__inner:focus {
64
+ border: 0 !important;
65
+ box-shadow: none !important;
66
+ }
67
+
50
68
  &.el-textarea__inner {
51
69
  border-radius: var(--wk-form-control-radius) !important;
52
70
  }
@@ -6,6 +6,7 @@
6
6
  // <div class="list-page">
7
7
  // <div class="list-page__search"> 搜索区(el-form inline)
8
8
  // <div class="list-page__toolbar"> 工具栏(新增/导入/导出按钮)
9
+ // <div class="list-page__title"> 列表标题(位于按钮组下方)
9
10
  // <div class="list-page__table"> 表格区(BaseTable / el-table)
10
11
  // <div class="list-page__pager"> 分页区(el-pagination)
11
12
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -8,17 +8,17 @@
8
8
  .ag-root-wrapper {
9
9
  --ag-background-color: #ffffff;
10
10
  --ag-odd-row-background-color: #ffffff;
11
- --ag-header-background-color: #fafafa;
11
+ --ag-header-background-color: #fafafa;
12
12
  --ag-header-foreground-color: #374151;
13
13
  --ag-border-color: rgba(0, 0, 0, 0.06);
14
- --ag-row-border-color: rgba(0, 0, 0, 0.06);
15
- --ag-row-hover-color: rgba(var(--el-color-primary-rgb), 0.06);
16
- --ag-selected-row-background-color: rgba(var(--el-color-primary-rgb), 0.10);
17
- --ag-range-selection-border-color: rgba(var(--el-color-primary-rgb), 0.4);
14
+ --ag-row-border-color: rgba(0, 0, 0, 0.06);
15
+ --ag-row-hover-color: rgba(var(--el-color-primary-rgb), 0.06);
16
+ --ag-selected-row-background-color: rgba(var(--el-color-primary-rgb), 0.1);
17
+ --ag-range-selection-border-color: rgba(var(--el-color-primary-rgb), 0.4);
18
18
  --ag-font-size: 13px;
19
19
  --ag-font-family: inherit;
20
20
  --ag-row-height: 26px;
21
- --ag-header-height: 36px;
21
+ --ag-header-height: 36px;
22
22
  --ag-cell-horizontal-padding: 8px;
23
23
  --ag-checkbox-checked-color: var(--el-color-primary);
24
24
  --ag-input-focus-border-color: var(--el-color-primary);
@@ -32,8 +32,8 @@
32
32
  // ── 表头 ──────────────────────────────────────────────────────────────────────
33
33
  .ag-header {
34
34
  background-color: #fafafa !important;
35
- border-bottom: 1px solid rgba(0, 0, 0, 0.06) !important;
36
- min-height: 36px !important;
35
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06) !important;
36
+ min-height: 36px !important;
37
37
  }
38
38
 
39
39
  .ag-header-row {
@@ -51,8 +51,8 @@
51
51
  align-items: center !important;
52
52
 
53
53
  &::after {
54
- background-color: rgba(0, 0, 0, 0.06) !important;
55
- }
54
+ background-color: rgba(0, 0, 0, 0.06) !important;
55
+ }
56
56
  }
57
57
 
58
58
  .ag-header-cell-comp-wrapper {
@@ -91,7 +91,9 @@
91
91
  transition: background-color 0.15s ease;
92
92
  }
93
93
 
94
- .ag-row-even { background-color: #ffffff; }
94
+ .ag-row-even {
95
+ background-color: #ffffff;
96
+ }
95
97
  .ag-row-odd {
96
98
  background-color: #ffffff;
97
99
  }
@@ -102,7 +104,7 @@
102
104
  }
103
105
 
104
106
  .ag-row.ag-row-selected {
105
- background-color: rgba(var(--el-color-primary-rgb), 0.10) !important;
107
+ background-color: rgba(var(--el-color-primary-rgb), 0.1) !important;
106
108
  }
107
109
 
108
110
  // ── 单元格 ────────────────────────────────────────────────────────────────────
@@ -124,7 +126,9 @@
124
126
  }
125
127
 
126
128
  // 复选框单元格(header 与 body 使用相同规则,确保视觉对齐)
127
- .ag-cell.ag-checkbox-cell { padding: 0 !important; }
129
+ .ag-cell.ag-checkbox-cell {
130
+ padding: 0 !important;
131
+ }
128
132
 
129
133
  // 多选列表头:精准命中 ag-header-select-all,与 body checkbox 保持相同居中方式
130
134
  .ag-header-cell:has(.ag-header-select-all) {
@@ -145,20 +149,30 @@
145
149
  }
146
150
 
147
151
  // 固定列分割线
148
- .ag-pinned-right-cols-container { border-left: 1px solid var(--el-border-color-light); }
149
- .ag-pinned-left-cols-container { border-right: 1px solid var(--el-border-color-light); }
152
+ .ag-pinned-right-cols-container {
153
+ border-left: 1px solid var(--el-border-color-light);
154
+ }
155
+ .ag-pinned-left-cols-container {
156
+ border-right: 1px solid var(--el-border-color-light);
157
+ }
150
158
 
151
159
  // ── 空数据占位 ────────────────────────────────────────────────────────────────
152
160
  .ag-overlay-no-rows-wrapper {
153
- --wk-empty-hint: '未找到匹配数据,请调整筛选条件后重试';
161
+ --wk-empty-hint: "未找到匹配数据,请调整筛选条件后重试";
162
+ position: absolute !important;
163
+ inset: 0 !important;
164
+ width: 100% !important;
165
+ height: 100% !important;
166
+ min-height: 0 !important;
167
+ box-sizing: border-box;
154
168
  display: flex !important;
155
169
  flex-direction: column !important;
156
170
  align-items: center !important;
157
171
  justify-content: center !important;
158
- padding: 56px 0;
172
+ padding: 20px 0 !important;
159
173
 
160
174
  &::before {
161
- content: '';
175
+ content: "";
162
176
  display: block;
163
177
  width: 100px;
164
178
  height: 84px;
@@ -173,7 +187,7 @@
173
187
  content: var(--wk-empty-hint);
174
188
  display: block;
175
189
  font-size: 12px;
176
- color: var(--el-text-color-placeholder, #B0B7C3);
190
+ color: var(--el-text-color-placeholder, #b0b7c3);
177
191
  margin-top: 6px;
178
192
  letter-spacing: 0.01em;
179
193
  }
@@ -187,36 +201,94 @@
187
201
  }
188
202
 
189
203
  // ── 滚动条 ────────────────────────────────────────────────────────────────────
190
- .ag-body-viewport::-webkit-scrollbar { width: 4px; }
191
- .ag-body-viewport::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.15); border-radius: 2px; }
192
- .ag-body-viewport::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.3); }
193
- .ag-body-viewport::-webkit-scrollbar-track { background: transparent; }
204
+ .ag-body-viewport::-webkit-scrollbar {
205
+ width: 4px;
206
+ }
207
+ .ag-body-viewport::-webkit-scrollbar-thumb {
208
+ background-color: rgba(0, 0, 0, 0.15);
209
+ border-radius: 2px;
210
+ }
211
+ .ag-body-viewport::-webkit-scrollbar-thumb:hover {
212
+ background-color: rgba(0, 0, 0, 0.3);
213
+ }
214
+ .ag-body-viewport::-webkit-scrollbar-track {
215
+ background: transparent;
216
+ }
194
217
 
195
- .ag-body-horizontal-scroll-viewport::-webkit-scrollbar { height: 4px; }
196
- .ag-body-horizontal-scroll-viewport::-webkit-scrollbar-track { background: transparent; }
197
- .ag-body-horizontal-scroll-viewport::-webkit-scrollbar-thumb { background-color: transparent; border-radius: 2px; }
198
- .ag-body-horizontal-scroll-viewport:hover::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.2); }
199
- .ag-body-horizontal-scroll-viewport:hover::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.35); }
218
+ .ag-body-horizontal-scroll-viewport::-webkit-scrollbar {
219
+ height: 4px;
220
+ }
221
+ .ag-body-horizontal-scroll-viewport::-webkit-scrollbar-track {
222
+ background: transparent;
223
+ }
224
+ .ag-body-horizontal-scroll-viewport::-webkit-scrollbar-thumb {
225
+ background-color: transparent;
226
+ border-radius: 2px;
227
+ }
228
+ .ag-body-horizontal-scroll-viewport:hover::-webkit-scrollbar-thumb {
229
+ background-color: rgba(0, 0, 0, 0.2);
230
+ }
231
+ .ag-body-horizontal-scroll-viewport:hover::-webkit-scrollbar-thumb:hover {
232
+ background-color: rgba(0, 0, 0, 0.35);
233
+ }
200
234
 
201
235
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
202
236
  // 表格加载动画 — 高质感定制 loading overlay
203
237
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
204
238
  @keyframes ag-spin-outer {
205
- 0% { stroke-dashoffset: 230; }
206
- 50% { stroke-dashoffset: 80; }
207
- 100% { stroke-dashoffset: 230; }
239
+ 0% {
240
+ stroke-dashoffset: 230;
241
+ }
242
+ 50% {
243
+ stroke-dashoffset: 80;
244
+ }
245
+ 100% {
246
+ stroke-dashoffset: 230;
247
+ }
208
248
  }
209
249
  @keyframes ag-spin-inner {
210
- 0% { stroke-dashoffset: 150; }
211
- 50% { stroke-dashoffset: 40; }
212
- 100% { stroke-dashoffset: 150; }
250
+ 0% {
251
+ stroke-dashoffset: 150;
252
+ }
253
+ 50% {
254
+ stroke-dashoffset: 40;
255
+ }
256
+ 100% {
257
+ stroke-dashoffset: 150;
258
+ }
259
+ }
260
+ @keyframes ag-rotate-cw {
261
+ to {
262
+ transform: rotate(360deg);
263
+ }
264
+ }
265
+ @keyframes ag-rotate-ccw {
266
+ to {
267
+ transform: rotate(-360deg);
268
+ }
269
+ }
270
+ @keyframes ag-pulse-dot {
271
+ 0%,
272
+ 100% {
273
+ r: 3.5;
274
+ opacity: 0.6;
275
+ }
276
+ 50% {
277
+ r: 5;
278
+ opacity: 1;
279
+ }
213
280
  }
214
- @keyframes ag-rotate-cw { to { transform: rotate(360deg); } }
215
- @keyframes ag-rotate-ccw { to { transform: rotate(-360deg); } }
216
- @keyframes ag-pulse-dot { 0%, 100% { r: 3.5; opacity: 0.6; } 50% { r: 5; opacity: 1; } }
217
281
  @keyframes ag-dot-bounce {
218
- 0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
219
- 40% { transform: translateY(-5px); opacity: 1; }
282
+ 0%,
283
+ 80%,
284
+ 100% {
285
+ transform: translateY(0);
286
+ opacity: 0.4;
287
+ }
288
+ 40% {
289
+ transform: translateY(-5px);
290
+ opacity: 1;
291
+ }
220
292
  }
221
293
 
222
294
  .ag-overlay-loading-wrapper {
@@ -239,11 +311,12 @@
239
311
  // 用 ::before 注入 SVG 动画徽标
240
312
  .ag-overlay-loading-center {
241
313
  &::before {
242
- content: '';
314
+ content: "";
243
315
  display: block;
244
316
  width: 52px;
245
317
  height: 52px;
246
- background: url("data:image/svg+xml,%3Csvg width='52' height='52' viewBox='0 0 52 52' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='26' cy='26' r='22' stroke='%23F2F5FF' stroke-width='2.5'/%3E%3Ccircle cx='26' cy='26' r='22' stroke='%232254F4' stroke-width='2.5' stroke-linecap='round' stroke-dasharray='138' stroke-dashoffset='230' style='transform-origin:center;animation:ag-spin-outer 1.4s ease-in-out infinite,ag-rotate-cw 1.8s linear infinite'/%3E%3Ccircle cx='26' cy='26' r='13' stroke='%23D3DDFD' stroke-width='2'/%3E%3Ccircle cx='26' cy='26' r='13' stroke='%237A98F8' stroke-width='2' stroke-linecap='round' stroke-dasharray='82' stroke-dashoffset='150' style='transform-origin:center;animation:ag-spin-inner 1.0s ease-in-out infinite,ag-rotate-ccw 1.2s linear infinite'/%3E%3Ccircle cx='26' cy='26' r='3.5' fill='%232254F4' style='transform-origin:center;animation:ag-pulse-dot 1.6s ease-in-out infinite'/%3E%3C/svg%3E") center/contain no-repeat;
318
+ background: url("data:image/svg+xml,%3Csvg width='52' height='52' viewBox='0 0 52 52' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='26' cy='26' r='22' stroke='%23F2F5FF' stroke-width='2.5'/%3E%3Ccircle cx='26' cy='26' r='22' stroke='%232254F4' stroke-width='2.5' stroke-linecap='round' stroke-dasharray='138' stroke-dashoffset='230' style='transform-origin:center;animation:ag-spin-outer 1.4s ease-in-out infinite,ag-rotate-cw 1.8s linear infinite'/%3E%3Ccircle cx='26' cy='26' r='13' stroke='%23D3DDFD' stroke-width='2'/%3E%3Ccircle cx='26' cy='26' r='13' stroke='%237A98F8' stroke-width='2' stroke-linecap='round' stroke-dasharray='82' stroke-dashoffset='150' style='transform-origin:center;animation:ag-spin-inner 1.0s ease-in-out infinite,ag-rotate-ccw 1.2s linear infinite'/%3E%3Ccircle cx='26' cy='26' r='3.5' fill='%232254F4' style='transform-origin:center;animation:ag-pulse-dot 1.6s ease-in-out infinite'/%3E%3C/svg%3E")
319
+ center/contain no-repeat;
247
320
  }
248
321
 
249
322
  // 省略号文字(AG Grid 默认插入"Loading...",此处做字体优化)
@@ -258,7 +331,7 @@
258
331
  // 注:AG Grid 加载文本由 overlayLoadingTemplate 控制,
259
332
  // 这里的动画是纯 CSS 加持,无需改 JS
260
333
  .ag-overlay-loading-center::after {
261
- content: '';
334
+ content: "";
262
335
  display: flex;
263
336
  gap: 4px;
264
337
  align-items: center;
@@ -266,9 +339,21 @@
266
339
  height: 12px;
267
340
  // 三个点用 box-shadow 模拟
268
341
  width: 36px;
269
- background: radial-gradient(circle at 6px 6px, var(--el-color-primary) 3.5px, transparent 3.5px),
270
- radial-gradient(circle at 18px 6px, var(--el-color-primary) 3.5px, transparent 3.5px),
271
- radial-gradient(circle at 30px 6px, var(--el-color-primary) 3.5px, transparent 3.5px);
342
+ background: radial-gradient(
343
+ circle at 6px 6px,
344
+ var(--el-color-primary) 3.5px,
345
+ transparent 3.5px
346
+ ),
347
+ radial-gradient(
348
+ circle at 18px 6px,
349
+ var(--el-color-primary) 3.5px,
350
+ transparent 3.5px
351
+ ),
352
+ radial-gradient(
353
+ circle at 30px 6px,
354
+ var(--el-color-primary) 3.5px,
355
+ transparent 3.5px
356
+ );
272
357
  animation: ag-dot-bounce 1.2s ease-in-out infinite;
273
358
  opacity: 0.7;
274
359
  }
@@ -33,21 +33,24 @@
33
33
  }
34
34
  }
35
35
 
36
- // ── 搜索区字号统一 12px(紧凑密度,与 label / button 一致)────────────────
37
- .el-form-item__label {
38
- font-size: 12px !important;
39
- }
36
+ // ── 搜索区字号统一 12px(紧凑密度,与 label / button 一致)────────────────
37
+ .el-form-item__label {
38
+ font-size: 12px !important;
39
+ }
40
40
  .el-input__inner {
41
41
  font-size: 12px;
42
42
  color: var(--el-text-color-primary);
43
- &::placeholder { color: var(--el-text-color-placeholder); font-size: 12px; }
44
- }
45
-
46
- .el-select__placeholder,
47
- .el-select__tags-text,
48
- .el-select .el-select__selected-item {
43
+ &::placeholder {
44
+ color: var(--el-text-color-placeholder);
49
45
  font-size: 12px;
50
46
  }
47
+ }
48
+
49
+ .el-select__placeholder,
50
+ .el-select__tags-text,
51
+ .el-select .el-select__selected-item {
52
+ font-size: 12px;
53
+ }
51
54
  // ── 选择框 ────────────────────────────────────────────────────────────────
52
55
  .el-select .el-select__wrapper {
53
56
  border-radius: var(--el-border-radius-base) !important;
@@ -84,6 +87,19 @@
84
87
  justify-content: flex-end;
85
88
  gap: 8px;
86
89
  padding-bottom: 8px;
90
+
91
+ // BaseQuery 的第 3 个按钮固定为展开/收起;使用轻量胶囊与查询/重置拉开层级。
92
+ > .el-button:nth-child(3):last-child {
93
+ color: var(--el-color-primary) !important;
94
+ border-color: var(--el-color-primary-light-7) !important;
95
+ border-radius: 999px !important;
96
+ background-color: var(--el-color-primary-light-9) !important;
97
+
98
+ &:hover {
99
+ border-color: var(--el-color-primary-light-5) !important;
100
+ background-color: var(--el-color-primary-light-8) !important;
101
+ }
102
+ }
87
103
  }
88
104
 
89
105
  // 查询按钮(type=primary)→ 品牌蓝填充
@@ -96,16 +112,20 @@
96
112
  padding: 0 10px !important;
97
113
  font-size: 12px !important;
98
114
  background-color: var(--el-color-primary, #002a8f) !important;
99
- border-color: var(--el-color-primary, #002a8f) !important;
115
+ border-color: var(--el-color-primary, #002a8f) !important;
100
116
  color: #ffffff !important;
101
- transition: background-color 0.15s, box-shadow 0.15s;
117
+ transition:
118
+ background-color 0.15s,
119
+ box-shadow 0.15s;
102
120
 
103
121
  &:hover {
104
122
  background-color: var(--el-color-primary-light-1, #1a3f9a) !important;
105
- border-color: var(--el-color-primary-light-1, #1a3f9a) !important;
123
+ border-color: var(--el-color-primary-light-1, #1a3f9a) !important;
106
124
  box-shadow: 0 2px 6px rgba(var(--el-color-primary-rgb), 0.18);
107
125
  }
108
- &:active { opacity: 0.85; }
126
+ &:active {
127
+ opacity: 0.85;
128
+ }
109
129
  }
110
130
 
111
131
  // 重置按钮(type=default)→ 轻线框样式
@@ -118,7 +138,10 @@
118
138
  color: var(--el-text-color-regular) !important;
119
139
  border-color: var(--el-border-color) !important;
120
140
  background-color: #ffffff !important;
121
- transition: border-color 0.15s, color 0.15s, background-color 0.15s;
141
+ transition:
142
+ border-color 0.15s,
143
+ color 0.15s,
144
+ background-color 0.15s;
122
145
 
123
146
  &:hover {
124
147
  color: var(--el-color-primary) !important;
@@ -144,6 +167,7 @@
144
167
  .base-toolbar-content {
145
168
  display: flex !important;
146
169
  align-items: center !important;
170
+ justify-content: flex-start !important;
147
171
  flex-wrap: wrap !important;
148
172
  gap: 8px !important;
149
173
  margin: 0 !important;
@@ -174,7 +198,10 @@
174
198
  padding: 0 8px !important;
175
199
  margin: 0 !important; // 覆盖 jh-ui 的 margin-right:8px
176
200
  letter-spacing: 0.01em;
177
- transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
201
+ transition:
202
+ background-color 0.15s,
203
+ border-color 0.15s,
204
+ box-shadow 0.15s;
178
205
 
179
206
  // 图标 + 文字间距
180
207
  .el-icon + span,
@@ -196,6 +223,18 @@
196
223
  &:active {
197
224
  opacity: 0.85;
198
225
  }
226
+
227
+ &.is-plain {
228
+ color: var(--el-color-primary, #002a8f) !important;
229
+ border-color: var(--el-color-primary-light-5) !important;
230
+ background-color: rgba(var(--el-color-primary-rgb), 0.06) !important;
231
+
232
+ &:hover {
233
+ color: #ffffff !important;
234
+ border-color: var(--el-color-primary, #002a8f) !important;
235
+ background-color: var(--el-color-primary, #002a8f) !important;
236
+ }
237
+ }
199
238
  }
200
239
 
201
240
  // ── 危险操作:填充红(删除/作废/驳回)───────────────────────────────
@@ -213,6 +252,18 @@
213
252
  &:active {
214
253
  opacity: 0.85;
215
254
  }
255
+
256
+ &.is-plain {
257
+ color: var(--el-color-danger, #bb2d3f) !important;
258
+ border-color: var(--el-color-danger-light-5, #df9ca5) !important;
259
+ background-color: var(--el-color-danger-light-9, #fdf2f3) !important;
260
+
261
+ &:hover {
262
+ color: #ffffff !important;
263
+ border-color: var(--el-color-danger, #bb2d3f) !important;
264
+ background-color: var(--el-color-danger, #bb2d3f) !important;
265
+ }
266
+ }
216
267
  }
217
268
 
218
269
  // ── 警告操作:填充橙(提醒/预警相关)───────────────────────────────
@@ -227,10 +278,24 @@
227
278
  border-color: var(--el-color-warning-light-3, #f1b85a) !important;
228
279
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
229
280
  }
281
+
282
+ &.is-plain {
283
+ color: var(--el-color-warning, #ea9a13) !important;
284
+ border-color: var(--el-color-warning-light-5, #f4c97f) !important;
285
+ background-color: var(--el-color-warning-light-9, #fef8ed) !important;
286
+
287
+ &:hover {
288
+ color: #ffffff !important;
289
+ border-color: var(--el-color-warning, #ea9a13) !important;
290
+ background-color: var(--el-color-warning, #ea9a13) !important;
291
+ }
292
+ }
230
293
  }
231
294
 
232
295
  // ── 次级操作:线框灰→hover 品牌色(导入/导出/模板下载)──────────────
233
- &.el-button--default:not(.el-button--primary):not(.el-button--danger):not(.el-button--warning) {
296
+ &.el-button--default:not(.el-button--primary):not(.el-button--danger):not(
297
+ .el-button--warning
298
+ ) {
234
299
  color: var(--el-text-color-regular) !important;
235
300
  border-color: var(--el-border-color) !important;
236
301
  background-color: #ffffff !important;
@@ -254,6 +319,18 @@
254
319
  border-color: var(--el-color-success-light-3, #6bcb95) !important;
255
320
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
256
321
  }
322
+
323
+ &.is-plain {
324
+ color: var(--el-color-success, #2bb268) !important;
325
+ border-color: var(--el-color-success-light-5, #8dd8ae) !important;
326
+ background-color: var(--el-color-success-light-9, #effaf4) !important;
327
+
328
+ &:hover {
329
+ color: #ffffff !important;
330
+ border-color: var(--el-color-success, #2bb268) !important;
331
+ background-color: var(--el-color-success, #2bb268) !important;
332
+ }
333
+ }
257
334
  }
258
335
  }
259
336
  }
@@ -298,12 +375,12 @@
298
375
 
299
376
  .el-button:first-child {
300
377
  border-top-left-radius: var(--el-border-radius-base) !important;
301
- border-bottom-left-radius: var(--el-border-radius-base) !important;
378
+ border-bottom-left-radius: var(--el-border-radius-base) !important;
302
379
  }
303
380
 
304
381
  .el-button:last-child {
305
382
  border-top-right-radius: var(--el-border-radius-base) !important;
306
- border-bottom-right-radius: var(--el-border-radius-base) !important;
383
+ border-bottom-right-radius: var(--el-border-radius-base) !important;
307
384
  margin-left: -1px !important;
308
385
  }
309
386
  }
@@ -1,78 +1,83 @@
1
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2
- // jh-drag-row 上下分栏拖拽手柄 — 与 _jh-drag-col 视觉同源
3
- //
4
- // 背景
5
- // `jh-drag-row`(来自 @jhlc/common-core)是主从表/上下双表的首选布局组件
6
- // (见 wl-skills-kit standards/14)。其拖拽手柄 `.slider_row` 默认使用 jh-ui
7
- // 内置蓝(#a7caec),与项目集群统一细线手柄风格不一致。
8
- //
9
- // 本文件把 `.slider_row` 对齐到与 `_jh-drag-col.scss` 同一套极简细线 +
10
- // grip dots 设计语言,只是方向改为水平分割线(row-resize)。
11
- //
12
- // 源码 CSS 类名参考(common-core/src/components/drag-row/drag-row.vue)
13
- // .drager_row(主容器)/ .drager_top(上区)/ .drager_bottom(下区)/ .slider_row(手柄)
14
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
15
-
16
- // ── 上下拖拽分割条 — 极简细线 + grip dots(垂直方向)────────────────────────
1
+ // jh-drag-row 上下分栏拖拽手柄
2
+ // 统一为“细分隔线 + 三点胶囊”,既保留足够的拖拽热区,也避免视觉喧宾夺主。
17
3
  html .drager_row > .slider_row {
4
+ position: relative;
5
+ height: 20px !important;
6
+ min-height: 20px !important;
7
+ flex: 0 0 20px !important;
8
+ cursor: row-resize !important;
18
9
  background: linear-gradient(
19
10
  to bottom,
20
- transparent 4px,
21
- var(--el-border-color-light) 4px,
22
- var(--el-border-color-light) 5px,
23
- transparent 5px
11
+ transparent 9px,
12
+ var(--el-border-color-lighter) 9px,
13
+ var(--el-border-color-lighter) 10px,
14
+ transparent 10px
24
15
  ) !important;
25
- transition: background 0.2s !important;
16
+ transition: background-color 0.18s ease !important;
26
17
 
27
- &::before { display: none !important; }
18
+ &::before {
19
+ position: absolute !important;
20
+ z-index: 1;
21
+ top: 50% !important;
22
+ left: 50% !important;
23
+ display: block !important;
24
+ width: 92px !important;
25
+ min-width: 92px !important;
26
+ max-width: 92px !important;
27
+ height: 16px !important;
28
+ content: "" !important;
29
+ transform: translate(-50%, -50%) !important;
30
+ border: 1px solid var(--el-border-color) !important;
31
+ border-radius: 999px !important;
32
+ background: var(--el-bg-color) !important;
33
+ box-shadow: 0 1px 2px rgb(15 23 42 / 5%) !important;
34
+ transition:
35
+ border-color 0.18s ease,
36
+ box-shadow 0.18s ease !important;
37
+ }
28
38
 
29
39
  &::after {
30
- content: '' !important;
31
40
  position: absolute !important;
32
- left: 50% !important;
33
- right: auto !important;
41
+ z-index: 2;
34
42
  top: 50% !important;
35
- transform: translate(-50%, -50%) !important;
43
+ left: 50% !important;
44
+ display: block !important;
36
45
  width: 4px !important;
46
+ min-width: 4px !important;
47
+ max-width: 4px !important;
37
48
  height: 4px !important;
38
- min-height: unset !important;
39
- max-height: unset !important;
49
+ min-height: 4px !important;
50
+ max-height: 4px !important;
51
+ content: "" !important;
52
+ transform: translate(-50%, -50%) !important;
40
53
  border-radius: 50% !important;
41
- background-color: var(--el-border-color) !important;
42
- box-shadow: -8px 0 0 var(--el-border-color), 8px 0 0 var(--el-border-color) !important;
43
- transition: background-color 0.2s, box-shadow 0.2s !important;
44
- opacity: 0.6;
54
+ background: var(--el-text-color-placeholder) !important;
55
+ box-shadow:
56
+ -12px 0 0 var(--el-text-color-placeholder),
57
+ 12px 0 0 var(--el-text-color-placeholder) !important;
58
+ opacity: 0.72;
59
+ transition:
60
+ background-color 0.18s ease,
61
+ box-shadow 0.18s ease !important;
45
62
  }
46
63
 
47
- &:hover {
48
- background: linear-gradient(
49
- to bottom,
50
- rgba(var(--el-color-primary-rgb), 0.06) 3px,
51
- var(--el-color-primary) 3px,
52
- var(--el-color-primary) 7px,
53
- rgba(var(--el-color-primary-rgb), 0.06) 7px
54
- ) !important;
64
+ &:hover,
65
+ &:active {
66
+ &::before {
67
+ border-color: var(--el-color-primary-light-5) !important;
68
+ box-shadow: 0 2px 6px rgb(15 23 42 / 10%) !important;
69
+ }
55
70
 
56
71
  &::after {
57
- background-color: var(--el-color-primary) !important;
58
- box-shadow: -8px 0 0 var(--el-color-primary), 8px 0 0 var(--el-color-primary) !important;
72
+ background: var(--el-color-primary) !important;
73
+ box-shadow:
74
+ -12px 0 0 var(--el-color-primary),
75
+ 12px 0 0 var(--el-color-primary) !important;
59
76
  opacity: 1;
60
77
  }
61
78
  }
62
-
63
- &:active {
64
- background: linear-gradient(
65
- to bottom,
66
- rgba(var(--el-color-primary-rgb), 0.1) 2.5px,
67
- var(--el-color-primary) 2.5px,
68
- var(--el-color-primary) 7.5px,
69
- rgba(var(--el-color-primary-rgb), 0.1) 7.5px
70
- ) !important;
71
- }
72
79
  }
73
80
 
74
- // ── 容器高度链兜底:确保上下区域能正常撑满(kit standards/14 强制项)────────
75
- // 父容器/Tab 已由业务页负责设高度;此处仅确保 .drager_row 自身不塌缩。
76
81
  html .drager_row {
77
82
  min-height: 0;
78
83
  }
@@ -56,6 +56,9 @@ src/views/[模块]/
56
56
  >
57
57
  </div>
58
58
 
59
+ <!-- 列表标题:位于按钮组下方,独占一行 -->
60
+ <div class="list-title">[列表标题]</div>
61
+
59
62
  <!-- 表格 -->
60
63
  <BaseTable :hook="page" empty-text="暂无数据" :columns="columnsDef()" />
61
64
 
@@ -137,3 +140,4 @@ async function handleDel(id: string) {
137
140
  2. `usePageHook` 需替换为项目实际的分页 Hook
138
141
  3. `enableStatus` 字段在 `installCommonPreset()` 后自动渲染为彩色 Tag,无需手写 defaultNode
139
142
  4. 工具栏按钮必须带 `icon`(R005 规则)
143
+ 5. 标准顺序固定为“查询区 → 工具栏 → 列表标题 → 表格 → 分页器”;工具栏不得放到标题右侧或标题下方