@agile-team/wl-skills-ui 1.9.3 → 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 +20 -0
- package/README.md +3 -2
- package/docs/compat-matrix.md +2 -2
- package/es/{chunk-KPCUDXLS.js → chunk-MPJ6QDYX.js} +2 -2
- 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/skills/element/el-form/SKILL.md +9 -0
- package/skills/layouts/list-page/SKILL.md +24 -12
- package/skills/vendors/jh-components/SKILL.md +13 -11
- package/standards/ui/02-button.md +40 -32
- package/standards/ui/03-form.md +19 -24
- package/styles/element/_form.scss +18 -0
- package/styles/layouts/_list-page.scss +1 -0
- package/styles/vendors/_ag-grid.scss +130 -45
- package/styles/vendors/_base-query-toolbar.scss +96 -19
- package/styles/vendors/_jh-drag-row.scss +59 -54
- package/templates/list-page/TPL-LIST.md +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ 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
|
+
|
|
20
|
+
## [1.9.4] - 2026-07-22
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- 修复浏览器运行时通过成员链读取 Node 环境变量时被 Vite 4 开发转换误替换为非法语法的问题;改用安全的动态属性访问,兼容依赖预构建与直接 ESM 加载。
|
|
25
|
+
- 发布校验新增浏览器产物扫描,阻止 `process.env.NODE_ENV` 成员链再次进入 ESM 产物。
|
|
26
|
+
|
|
7
27
|
## [1.9.3] - 2026-07-22
|
|
8
28
|
|
|
9
29
|
### 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.5` | 已对齐上述组合的 DOM 假设、客户主题锁、圆角契约与定制页样式边界 |
|
|
204
204
|
|
|
205
205
|
三种识别方式,任选其一:
|
|
206
206
|
|
|
@@ -214,8 +214,9 @@ yarn add @agile-team/wl-skills-ui
|
|
|
214
214
|
|
|
215
215
|
## 版本亮点
|
|
216
216
|
|
|
217
|
-
当前 v1.9.
|
|
217
|
+
当前 v1.9.5:
|
|
218
218
|
|
|
219
|
+
- **Vite 4 运行时兼容**:浏览器 ESM 产物不再暴露可被开发转换误替换的 Node 环境变量成员链,预构建与直接加载均保持合法语法。
|
|
219
220
|
- **登录页精准退出**:内置识别 `.lp-root` 与 `.session-login`,其内部表单、按钮、焦点和校验态保留登录页自身设计;业务页面仍由包高权重统一管控。
|
|
220
221
|
- **通用定制页边界**:其他定制页在根节点增加 `.wl-ui-skin-exempt` 或 `data-wl-ui-skin="off"` 即可退出组件级强覆盖,不需要删除全局化妆包。
|
|
221
222
|
- **主题锁不降级**:豁免只作用于 Element Plus / vendor 的直接组件覆盖;全局客户品牌 token 和平台动态主题防反覆盖继续生效。定制页若需独立色,可在自己的根节点重新声明 token 或直接编写局部样式。
|
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.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.
|
|
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
|
|
|
@@ -181,8 +181,8 @@ var __opWarned = /* @__PURE__ */ new Set();
|
|
|
181
181
|
function warnUnknownOpType(item) {
|
|
182
182
|
let isDev = true;
|
|
183
183
|
try {
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
184
|
+
const nodeEnv = globalThis["process"]?.["env"]?.["NODE_ENV"];
|
|
185
|
+
if (nodeEnv === "production") {
|
|
186
186
|
isDev = false;
|
|
187
187
|
}
|
|
188
188
|
} catch {
|
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-MPJ6QDYX.js';
|
|
2
|
+
export { registerDictColorMap, registerDictColorMaps, renderDictClassifyTag, setDictResolver } from './chunk-MPJ6QDYX.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-MPJ6QDYX.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-MPJ6QDYX.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-MPJ6QDYX.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.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`
|
|
43
|
-
| 搜索区未用 `.list-page__query`
|
|
44
|
-
| 工具栏未用 `.list-page__toolbar`
|
|
45
|
-
|
|
|
46
|
-
|
|
|
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()"
|
|
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
|
|
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
|
|
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` | —
|
|
19
|
-
| 专项样式覆盖
|
|
20
|
-
| 专项样式覆盖
|
|
21
|
-
| 专项样式覆盖
|
|
22
|
-
| 专项样式覆盖
|
|
23
|
-
| 通用规则治理
|
|
24
|
-
| 通用规则治理
|
|
25
|
-
| 通用规则治理
|
|
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% }`
|
|
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
|
-
| 状态
|
|
14
|
-
|
|
15
|
-
| 常规
|
|
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`
|
|
18
|
-
| disabled
|
|
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
|
-
| 操作语义
|
|
23
|
-
|
|
24
|
-
| 新增/添加
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
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="
|
|
35
|
-
<el-button type="success"
|
|
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:
|
|
48
|
-
{ type:
|
|
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
|
-
| 禁用
|
|
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:
|
|
74
|
-
{ type:
|
|
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;
|
package/standards/ui/03-form.md
CHANGED
|
@@ -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
|
|
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,无子表
|
|
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:
|
|
151
|
-
type:
|
|
152
|
-
date:
|
|
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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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 {
|
|
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 {
|
|
149
|
-
|
|
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:
|
|
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, #
|
|
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
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
.ag-body-viewport::-webkit-scrollbar-
|
|
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
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
.ag-body-horizontal-scroll-viewport
|
|
199
|
-
|
|
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%
|
|
206
|
-
|
|
207
|
-
|
|
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%
|
|
211
|
-
|
|
212
|
-
|
|
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%,
|
|
219
|
-
|
|
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")
|
|
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(
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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 {
|
|
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
|
-
|
|
115
|
+
border-color: var(--el-color-primary, #002a8f) !important;
|
|
100
116
|
color: #ffffff !important;
|
|
101
|
-
transition:
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
21
|
-
var(--el-border-color-
|
|
22
|
-
var(--el-border-color-
|
|
23
|
-
transparent
|
|
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.
|
|
16
|
+
transition: background-color 0.18s ease !important;
|
|
26
17
|
|
|
27
|
-
&::before {
|
|
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
|
-
|
|
33
|
-
right: auto !important;
|
|
41
|
+
z-index: 2;
|
|
34
42
|
top: 50% !important;
|
|
35
|
-
|
|
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:
|
|
39
|
-
max-height:
|
|
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
|
|
42
|
-
box-shadow:
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
58
|
-
box-shadow:
|
|
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. 标准顺序固定为“查询区 → 工具栏 → 列表标题 → 表格 → 分页器”;工具栏不得放到标题右侧或标题下方
|