@zl-ui-kit/components 0.3.1 → 0.3.3
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/README.md +896 -27
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,23 +1,51 @@
|
|
|
1
1
|
# @zl-ui-kit/components
|
|
2
2
|
|
|
3
|
-
ZL UI
|
|
4
|
-
对外 API(props/emits/slots)与参考实现保持一致;样式经 `vite-plugin-lib-inject-css` 随组件自动注入,无需手动引 css。
|
|
3
|
+
ZL UI 业务组件库 —— Element Plus 体系,`Zl` 前缀,47 个业务组件;配 `@zl-ui-kit/composables`(`useRequest` / `useDict`)、`@zl-ui-kit/theme`、`@zl-ui-kit/utils`、`@zl-ui-kit/icons` 使用。
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
**本页即完整使用文档**:安装与四个导入入口、47 个组件用法、`useRequest` 请求体系与 Mock 联调、主题定制(L1/L2)、国际化接入、公共 Hooks 与 Utils —— 全部都在这一页。
|
|
6
|
+
|
|
7
|
+
> 本文档即 UI 组件的**权威文档**,直接在本文件维护;npm 官网包页面渲染的就是它,改完随下一次发版对外生效。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. 简介与包结构
|
|
12
|
+
|
|
13
|
+
`zl-ui` 是一套基于 **Element Plus(EP)** 的企业级 Vue 3 组件库,以 EP 为底座,封装了 47 个 `Zl` 前缀业务组件,并提供请求、主题、图标、工具四套配套能力。
|
|
14
|
+
|
|
15
|
+
| 包 | 职责 |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| [`@zl-ui-kit/components`](https://www.npmjs.com/package/@zl-ui-kit/components) | EP 化业务组件库(`Zl` 前缀,47 个组件) |
|
|
18
|
+
| [`@zl-ui-kit/composables`](https://www.npmjs.com/package/@zl-ui-kit/composables) | `useRequest` 请求体系、`useDict`、微前端通信层 |
|
|
19
|
+
| [`@zl-ui-kit/icons`](https://www.npmjs.com/package/@zl-ui-kit/icons) | 444 个离线 SVG 图标(prefix `zl`,开源图标库) |
|
|
20
|
+
| [`@zl-ui-kit/theme`](https://www.npmjs.com/package/@zl-ui-kit/theme) | 5 套主题 + `defineTheme` 运行时定制(内置 OFL 开源中文字体) |
|
|
21
|
+
| [`@zl-ui-kit/utils`](https://www.npmjs.com/package/@zl-ui-kit/utils) | TS 工具函数库 + Axios 请求封装 |
|
|
22
|
+
|
|
23
|
+
**三个关键约定(使用组件前必读)**
|
|
24
|
+
|
|
25
|
+
1. **图标前缀 `zl:`** —— 所有内置图标以 `zl:` 引用,如 `zl:add-color`、`zl:database-color`。底层为 `@iconify/vue` 离线渲染,无需联网。
|
|
26
|
+
2. **数据型组件走 `useRequest`** —— 表格/卡片分页/树/图表等组件统一通过 `useRequest` 体系请求数据,与线上请求链路同构。
|
|
27
|
+
3. **样式变量双体系** —— 业务层 `--zl-*` 与 Element Plus 层 `--el-*` 协同,主题切换对两类组件同时生效。
|
|
28
|
+
|
|
29
|
+
### 1.1 在业务项目中安装(npm 发布版)
|
|
7
30
|
|
|
8
31
|
```bash
|
|
9
|
-
|
|
32
|
+
# 组件库 + 主题 + 常用配套(hooks / 工具函数)
|
|
33
|
+
npm i @zl-ui-kit/components @zl-ui-kit/composables @zl-ui-kit/utils
|
|
34
|
+
npm i @zl-ui-kit/theme
|
|
35
|
+
|
|
36
|
+
# peer 依赖(组件库不打包它们,需宿主提供)
|
|
37
|
+
npm i vue element-plus @vueuse/core
|
|
10
38
|
```
|
|
11
39
|
|
|
12
|
-
|
|
40
|
+
`@zl-ui-kit/icons` 是 `components` 的**直接依赖**(图标由组件内部消费),会自动装上,无需手写。`composables` / `utils` 虽然也列在 `dependencies` 里,但**只要你想在自己的代码里 import 它们,就请显式安装** —— 理由见下方「按需引入」的提示。
|
|
13
41
|
|
|
14
|
-
|
|
42
|
+
以下组件**不在主入口**,走独立子路径,依赖按需安装(`peerDependenciesMeta.optional`,没装也不会导致主入口编译失败):
|
|
15
43
|
|
|
16
44
|
| 子入口 | 组件 | 需安装 |
|
|
17
45
|
| --- | --- | --- |
|
|
18
46
|
| `@zl-ui-kit/components/chart` | `ZlChart` | `echarts` |
|
|
19
47
|
| `@zl-ui-kit/components/editor` | `ZlEditor` / `ZlSqlEditor` / `useSqlCompletion` | `monaco-editor` |
|
|
20
|
-
| `@zl-ui-kit/components/file-preview` | `ZlFilePreview` / `ZlFileViewer` | `@vue-office
|
|
48
|
+
| `@zl-ui-kit/components/file-preview` | `ZlFilePreview` / `ZlFileViewer` | `@vue-office/docx` `@vue-office/excel` `@vue-office/pdf` `@vue-office/pptx` |
|
|
21
49
|
|
|
22
50
|
```ts
|
|
23
51
|
import { ZlChart } from '@zl-ui-kit/components/chart'
|
|
@@ -25,45 +53,886 @@ import { ZlEditor, ZlSqlEditor } from '@zl-ui-kit/components/editor'
|
|
|
25
53
|
import { ZlFilePreview, ZlFileViewer } from '@zl-ui-kit/components/file-preview'
|
|
26
54
|
```
|
|
27
55
|
|
|
28
|
-
>
|
|
56
|
+
> 例外:`ZlPdfViewer` 没有重型依赖,仍留在主入口,`import { ZlPdfViewer } from '@zl-ui-kit/components'`。
|
|
57
|
+
> `app.use(ZlUI)` 全量注册**不含**上述子入口组件,需要的话手动 `app.component('ZlChart', ZlChart)`。
|
|
58
|
+
|
|
59
|
+
接入:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// main.ts
|
|
63
|
+
import { createApp } from 'vue'
|
|
64
|
+
import ElementPlus from 'element-plus'
|
|
65
|
+
import 'element-plus/dist/index.css'
|
|
66
|
+
import '@zl-ui-kit/theme/themes.css' // 主题变量(--zl-* / --el-*)
|
|
67
|
+
import ZlUI from '@zl-ui-kit/components'
|
|
68
|
+
|
|
69
|
+
createApp(App).use(ElementPlus).use(ZlUI).mount('#app')
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**四个导入入口速查**(最容易搞混的地方,务必区分):
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// 1. 组件 —— Zl 前缀
|
|
76
|
+
import { ZlTable, ZlEmpty } from '@zl-ui-kit/components'
|
|
77
|
+
// 2. hooks —— useRequest / useDict / useLocale / usePageTabs / useZlT …
|
|
78
|
+
import { useRequest, useDict } from '@zl-ui-kit/composables'
|
|
79
|
+
// 3. 工具函数 —— generateUUID / paginate / formatNumber …
|
|
80
|
+
import { generateUUID, paginate } from '@zl-ui-kit/utils'
|
|
81
|
+
// 4. 主题样式 —— 全局 CSS,在入口引入一次即可(不走 JS 入口)
|
|
82
|
+
import '@zl-ui-kit/theme/themes.css'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
四个包各管一件事:**`components` 只有组件和 `install`,不含 hooks 与 utils**;`theme` 除了 `themes.css` 等样式,还导出 `defineTheme` / `setTheme` / `useTheme` 等运行时 API。
|
|
86
|
+
|
|
87
|
+
> ⚠️ **`@zl-ui-kit/components` 不 re-export hooks 和 utils**,三者的入口是各自独立的。虽然 `composables` / `icons` / `utils` 是 `components` 的 `dependencies`、npm 安装时会一并装上,但**不要依赖这种传递关系**去 import —— 在 pnpm 严格模式下子依赖不会被提升,会解析不到。**在业务项目里显式安装**:
|
|
88
|
+
>
|
|
89
|
+
> ```bash
|
|
90
|
+
> npm i @zl-ui-kit/components @zl-ui-kit/composables @zl-ui-kit/utils
|
|
91
|
+
> ```
|
|
92
|
+
|
|
93
|
+
> **版本要求 `>=0.3.2`**
|
|
94
|
+
>
|
|
95
|
+
> - `0.2.0`:tarball 里依赖仍是 `catalog:` 协议,安装报 `Unsupported URL Type "catalog:"`
|
|
96
|
+
> - `0.3.0`:`exports` 指向不存在的 `dist/index.mjs`,装得上但 import 报 `ERR_MODULE_NOT_FOUND`
|
|
97
|
+
> - `0.3.1`:把 micro-app 误设为**必需** peer,纯 Web 应用会被强行拉入 micro-app(约 2.4MB)
|
|
98
|
+
>
|
|
99
|
+
> 这三个版本均已 `deprecate`,请直接用 `0.3.2` 或更新版本。
|
|
100
|
+
|
|
101
|
+
> **`@micro-zoe/micro-app` 是可选依赖(0.3.2 起)**
|
|
102
|
+
>
|
|
103
|
+
> 只在用微前端时才需要装:`npm i @micro-zoe/micro-app`。
|
|
104
|
+
> 不装时 `useLocale` / 主题 / 字典等核心能力照常工作(globalData 降级为 localStorage:
|
|
105
|
+
> `$zl_locale` / `$zl_theme` / `$zl_token`),但 `useMicroApp` / `useMicroAction` / `usePageTabs`
|
|
106
|
+
> 这类微前端 hook 会在调用时抛出带指引的错误,而不是 `Cannot read properties of undefined`。
|
|
107
|
+
|
|
108
|
+
> **两个「看起来像错、其实正常」的现象**
|
|
109
|
+
>
|
|
110
|
+
> 1. 只 import 主入口、没装 `echarts` / `monaco-editor` 时,Vite 会打印
|
|
111
|
+
> `"xxx" is not exported by "__vite-optional-peer-dep:monaco-editor"`。
|
|
112
|
+
> 这是 Vite 对 optional peer 的内置 stub 机制:**警告不阻断构建**,装好对应依赖后警告消失。
|
|
113
|
+
> 2. `@zl-ui-kit/icons` 与 `@zl-ui-kit/utils` 在 **Node 原生 ESM** 下直接 import 会报错
|
|
114
|
+
> (前者是 JSON 缺 import attribute,后者依赖 CJS 包 `file-saver` 的具名导出)。
|
|
115
|
+
> Vite / webpack 打包的 Vue 项目不受影响;做 Node SSR 时把这两个包加进 `ssr.noExternal`。
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 2. 环境准备与运行示例
|
|
120
|
+
|
|
121
|
+
### 2.1 运行环境
|
|
122
|
+
|
|
123
|
+
- Node ≥ 20.20,pnpm ≥ 10(`packageManager` 已锁定 `pnpm@10.34.3`)
|
|
124
|
+
- 仓库根目录执行 `pnpm install`(workspace + catalog 自动解析)
|
|
125
|
+
|
|
126
|
+
### 2.2 启动 playground(组件示例与联调)
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
pnpm --filter @zl-ui-kit/playground dev # http://localhost:3100
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
playground 以 **源码模式** 运行,vite alias 将 `@zl-ui-kit/*` 五包精确指向各自 `src/`:
|
|
133
|
+
|
|
134
|
+
| 别名 | 指向 |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `@zl-ui-kit/utils` | `packages/utils/src/index.ts` |
|
|
137
|
+
| `@zl-ui-kit/composables` | `packages/composables/src/index.ts` |
|
|
138
|
+
| `@zl-ui-kit/components` | `packages/components/src/index.ts` |
|
|
139
|
+
| `@zl-ui-kit/theme`(JS API) | `packages/theme/src/index.ts` |
|
|
140
|
+
| `@zl-ui-kit/icons` | `packages/icons/src/index.mjs` |
|
|
141
|
+
|
|
142
|
+
**效果:直接改任意包的 `src` 源码,playground 即时热更,无需先构建。**
|
|
143
|
+
|
|
144
|
+
> 注意:`@zl-ui-kit/theme/light`、`@zl-ui-kit/theme/themes.css` 等 CSS 子路径不走别名,读取 `src/generated/` 生成物——改主题 token 后需先 `pnpm --filter @zl-ui-kit/theme gen`。
|
|
145
|
+
|
|
146
|
+
### 2.3 主题快速接入(宿主项目)
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
// main.ts
|
|
150
|
+
import '@zl-ui-kit/theme/themes.css' // 全部内置主题(或按需 '@zl-ui-kit/theme/light' 等)
|
|
151
|
+
import { setTheme } from '@zl-ui-kit/theme'
|
|
152
|
+
|
|
153
|
+
setTheme('dark') // 切换主题(light/dark/red-light/red-dark/blue)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 2.4 示例页面索引
|
|
157
|
+
|
|
158
|
+
| 路由 | 页面 | 覆盖内容 |
|
|
159
|
+
| --- | --- | --- |
|
|
160
|
+
| `/` | 首页 | 说明与导航 |
|
|
161
|
+
| `/icons` | 图标集 | 全量图标浏览/搜索,`zl:` 前缀渲染 |
|
|
162
|
+
| `/theme` | 主题工场 | 5 套主题切换 + `defineTheme` 运行时自定义 |
|
|
163
|
+
| `/components` | 组件示例 | ZlTitle/ZlTag/ZlCornerTag/ZlIcon/ZlText/ZlCopy/ZlAutoTooltip/ZlPagination/ZlEmpty |
|
|
164
|
+
| `/table-form` | 表格/表单示例 | ZlTable/ZlSearchTable/ZlForm/ZlDrawer/ZlPopconfirm/ZlSwitch/ZlDescription/ZlAction/ZlIconLabel/ZlIconHover/ZlSearch |
|
|
165
|
+
| `/general` | 通用/布局示例 | ZlAvatar/ZlCheckAll/ZlRadioTag/ZlCheckboxTag/ZlAddSub/ZlSorter/ZlLayoutSwitch/ZlSection/ZlCard/ZlAppSlogan/ZlPermission/ZlHorizontalTable/ZlUpload/ZlTabs/ZlGrid/ZlDynamicComponent/ZlCardPage |
|
|
166
|
+
| `/use-request` | useRequest 联调 | 用 Mock 模块驱动各数据型组件(见第 6 章) |
|
|
167
|
+
| `/search-table` | ZlSearchTable 专项 | 基础用法 / `{ service, options }` 用法 / 暴露方法 / 无查询项 immediate,含分页与查询验证 |
|
|
168
|
+
| `/gallery` | 组件陈列 | 55 个组件逐个渲染(用于批量截图与冒烟) |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 3. 组件总览(47 个业务组件)
|
|
173
|
+
|
|
174
|
+
> 下面按功能分组,列出的是**导出符号**(含同目录变体,如 `ZlCornerTag` / `ZlIconHover` / `ZlActionList`),所以条目数略多于 47;组件目录共 48 个(47 个业务组件 + `ZlI18nProvider`)。
|
|
175
|
+
|
|
176
|
+
### 3.1 通用展示组件
|
|
177
|
+
`ZlIcon` · `ZlTitle` · `ZlTag` · `ZlCornerTag` · `ZlText` · `ZlCopy` · `ZlAutoTooltip` · `ZlEmpty` · `ZlDictTag` · `ZlDescription` · `ZlSection` · `ZlCard` · `ZlAvatar` · `ZlAppSlogan` · `ZlIconLabel` · `ZlIconHover`
|
|
178
|
+
|
|
179
|
+
### 3.2 数据型组件
|
|
180
|
+
`ZlTable` · `ZlSearchTable` · `ZlCardPage` · `ZlPagination` · `ZlTree` · `ZlTreeSelect` · `ZlChart` · `ZlUpload`
|
|
181
|
+
|
|
182
|
+
### 3.3 表单与交互组件
|
|
183
|
+
`ZlForm` · `ZlSearch` · `ZlDrawer` · `ZlPopconfirm` · `ZlSwitch` · `ZlAction` · `ZlActionList` · `ZlRadioTag` · `ZlCheckboxTag` · `ZlCheckAll` · `ZlSorter` · `ZlAddSub` · `ZlLayoutSwitch` · `ZlTabs` · `ZlDynamicComponent` · `ZlGrid`
|
|
184
|
+
|
|
185
|
+
### 3.4 布局与权限
|
|
186
|
+
`ZlAppLayout` · `ZlAppMenu` · `ZlPermission` · `ZlHorizontalTable`
|
|
187
|
+
|
|
188
|
+
### 3.5 文件与编辑
|
|
189
|
+
`ZlFilePreview` · `ZlFileViewer` · `ZlPdfViewer` · `ZlEditor` · `ZlSqlEditor` · `ZlLogin`
|
|
190
|
+
|
|
191
|
+
> 微前端页签类组件(`ZlMicroPage`/`ZlAppTabs`/`ZlPageTabs`/`ZlDevMenu`)已从组件库移除,相关 composables 保留并标注运行前提。
|
|
192
|
+
|
|
193
|
+
### 3.6 国际化基础设施(独立于 47 个业务组件)
|
|
194
|
+
`ZlI18nProvider` —— 国际化根容器,将 Element Plus 语言包经 `<el-config-provider>` 挂载到 Element Plus(详见第 13 章)。
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 4. 图标组件 `ZlIcon`
|
|
199
|
+
|
|
200
|
+
底层为 `@iconify/vue` 离线渲染,图标名以 `zl:` 前缀引用,全部来自内置的 444 个开源图标。
|
|
201
|
+
|
|
202
|
+
```vue
|
|
203
|
+
<ZlIcon icon="zl:add-color" />
|
|
204
|
+
<ZlIcon icon="zl:database-color" :size="20" />
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
| prop | 说明 |
|
|
208
|
+
| --- | --- |
|
|
209
|
+
| `icon` | 图标名,支持 `zl:xxx`(内置)、`https://...`(外链)、普通名(iconify 在线集,需联网) |
|
|
210
|
+
| `size` | 字号/尺寸,默认 `14px` |
|
|
211
|
+
| `color` / `spin` 等 | 透传至底层 iconify-icon |
|
|
212
|
+
|
|
213
|
+
> 图标名须真实存在于 `packages/icons/src/icons.json`,否则不显示。可用 `Object.keys(require('@zl-ui-kit/icons').icons).includes('database-color')` 校验。
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 5. 通用展示组件
|
|
218
|
+
|
|
219
|
+
### ZlTitle — 区块标题
|
|
220
|
+
```vue
|
|
221
|
+
<ZlTitle size="medium" text="资源列表" />
|
|
222
|
+
```
|
|
223
|
+
`size`: `small`(12px) / `medium`(14px) / `large`;自带左侧竖条与可选图标(`zl:help-color`)。
|
|
224
|
+
|
|
225
|
+
### ZlTag — 标签
|
|
226
|
+
```vue
|
|
227
|
+
<ZlTag type="primary" :closable="true" icon="zl:star-color">标签</ZlTag>
|
|
228
|
+
```
|
|
229
|
+
| prop | 取值 |
|
|
230
|
+
| --- | --- |
|
|
231
|
+
| `type` | `primary` / `success` / `warning` / `info` / `danger` |
|
|
232
|
+
| `size` | `mini` / `small` / `medium` |
|
|
233
|
+
| `color` | 自定义色(覆盖 type) |
|
|
234
|
+
| `icon` / `iconColor` | 前置图标 |
|
|
235
|
+
| `closable` / `rounded` | 可关闭 / 圆角 |
|
|
236
|
+
|
|
237
|
+
`ZlCornerTag` 为四角定位标签,语义一致、定位展开为显式四类。
|
|
238
|
+
|
|
239
|
+
### ZlCopy — 一键复制
|
|
240
|
+
```vue
|
|
241
|
+
<ZlCopy :text="row.id" />
|
|
242
|
+
```
|
|
243
|
+
`text`(或插槽文本)经 `copy()` 写入剪贴板,按钮 `size="small" text`。
|
|
244
|
+
|
|
245
|
+
### ZlEmpty — 空状态
|
|
246
|
+
```vue
|
|
247
|
+
<ZlEmpty description="暂无数据" />
|
|
248
|
+
<ZlEmpty :image="customImg" /> <!-- 传入自定义图,缺省为内置轻量 SVG 线稿 -->
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### ZlDictTag — 字典标签
|
|
252
|
+
经 `useDict(dictKey)` 解析独立字典 store(`useDictStore`,Vue3 状态管理缓存,不依赖微前端)。**支持三种数据注入方式**(详见第 12.1.4 节与第 6.3 节 ⑤):
|
|
253
|
+
|
|
254
|
+
```vue
|
|
255
|
+
<!-- 1. 组件级直传(最轻量,无需任何全局配置即可翻译) -->
|
|
256
|
+
<ZlDictTag dict-key="resourceType" :value="row.type"
|
|
257
|
+
:dict="[{ label: '图片', value: '1', icon: 'zl:image' }, { label: '视频', value: '2' }]" />
|
|
258
|
+
|
|
259
|
+
<!-- 2. 全局/局部数据源(应用入口注册一次,全项目共享) -->
|
|
260
|
+
<ZlDictTag dict-key="resourceType" :value="row.type" />
|
|
261
|
+
<ZlDictTag dict-key="resourceType" :value="row.type" simple />
|
|
262
|
+
```
|
|
263
|
+
详见第 6.3 节 ⑤。
|
|
264
|
+
|
|
265
|
+
### 其余
|
|
266
|
+
`ZlText`(文本省略)、`ZlAutoTooltip`(超长省略 + tooltip)、`ZlDescription`(描述列表)、`ZlSection`(分区容器)、`ZlCard`(卡片,背景图已改为纯 CSS)、`ZlAvatar`(+Group)、`ZlAppSlogan`(品牌标语,字体走 `--zl-slogan-font-family`)、`ZlIconLabel`/`ZlIconHover`(图标 + 文字组合)。
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 6. 数据型组件 + useRequest 体系(核心)
|
|
271
|
+
|
|
272
|
+
数据型组件(`ZlTable`/`ZlSearchTable`/`ZlCardPage`/`ZlTree`/`ZlChart`/`ZlUpload` 等)统一通过 **`useRequest` 请求体系** 驱动,做到与线上请求链路同构。playground 另提供 **Mock 模块** 离线联调。
|
|
273
|
+
|
|
274
|
+
### 6.1 useRequest 三件套
|
|
275
|
+
|
|
276
|
+
来源:`@zl-ui-kit/composables`。`service` 统一签名:`(...args) => Promise<R>`。
|
|
277
|
+
|
|
278
|
+
#### `useRequest(service, options?, plugins?)` —— 基础形态
|
|
279
|
+
```ts
|
|
280
|
+
const { loading, data, error, status, run, runAsync, refresh, refreshAsync, cancel, mutate } =
|
|
281
|
+
useRequest(service, { manual: false, defaultParams: [], initialData, onSuccess, onError })
|
|
282
|
+
```
|
|
283
|
+
| 返回/选项 | 说明 |
|
|
284
|
+
| --- | --- |
|
|
285
|
+
| `loading` / `data` / `error` / `status` | 响应式状态(`status`: `''`/`pending`/`finished`/`error`) |
|
|
286
|
+
| `run(...args)` | 同步触发,错误被吞(除非传 `onError`) |
|
|
287
|
+
| `runAsync(...args)` | 返回 Promise,可 await 捕获 |
|
|
288
|
+
| `refresh()` / `refreshAsync()` | 用上次参数重跑 |
|
|
289
|
+
| `cancel()` | 取消进行中请求(结果丢弃) |
|
|
290
|
+
| `mutate(data | prev => R)` | 本地立即改 `data`,不触网 |
|
|
291
|
+
| `options.manual` | `true` 时不自动执行 |
|
|
292
|
+
| `options.defaultParams` / `initialData` | 首参 / 初始占位 |
|
|
293
|
+
| `options.onBefore/onSuccess/onError/onFinally` | 生命周期回调 |
|
|
294
|
+
| `options.loadingDelay` / `refreshDeps` | 延迟 loading / 依赖变更刷新 |
|
|
295
|
+
|
|
296
|
+
#### `useDataRequest(service, { dataKey = 'data' })` —— 自动解包信封
|
|
297
|
+
```ts
|
|
298
|
+
const { data } = useDataRequest(getChartDataApi, { dataKey: 'data' })
|
|
299
|
+
// data.value === 响应里的 data 字段
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
#### `usePaginationRequest(service, { pagination })` —— 分页专用
|
|
303
|
+
额外返回 `data`(行数组) / `total` / `current` / `pageSize` / `changeCurrent` / `changePageSize` / `changePagination`,分页键名(`pageNum/pageSize/total/rows`)可配置,与后端契约对齐时通过 `pagination` 选项或 `setGlobalOptions` 调整。
|
|
304
|
+
|
|
305
|
+
> `ZlSearchTable` / `ZlCardPage` 内部已用 `usePageableRequest`,**直接传 service 即可**,无需在外层再包。
|
|
306
|
+
|
|
307
|
+
### 6.2 Mock 模块(playground 离线联调)
|
|
308
|
+
|
|
309
|
+
位置 `apps/playground/src/mock/`:
|
|
310
|
+
|
|
311
|
+
| 文件 | 内容 |
|
|
312
|
+
| --- | --- |
|
|
313
|
+
| `data.ts` | 内存假数据:`genResources(count=137)`、`dicts`(resourceType/resourceStatus/region)、`treeData`、`chartData` |
|
|
314
|
+
| `services.ts` | Promise service(300~600ms 延迟):`getResourcePage` / `getResourceDetail` / `getTreeData` / `getChartDataApi` / `getDictMap` / `uploadFile` / `submitResourceForm` / `getResourcePageMaybeFail` |
|
|
315
|
+
| `index.ts` | 统一出口 |
|
|
316
|
+
|
|
317
|
+
| service | 返回结构 | 用途 |
|
|
318
|
+
| --- | --- | --- |
|
|
319
|
+
| `getResourcePage(params)` | `{ rows, total, pageNum, pageSize }` | 分页列表,喂 `ZlSearchTable`/`ZlCardPage`/`usePaginationRequest` |
|
|
320
|
+
| `getResourceDetail(id)` | `ResourceItem` | 详情 |
|
|
321
|
+
| `getTreeData()` | `TreeNode[]` | 组织树 |
|
|
322
|
+
| `getChartDataApi()` | `{ code:200, msg, data }` 信封 | 演示 `useDataRequest` 解包 |
|
|
323
|
+
| `getDictMap()` | `Record<string, DictItem[]>` | 经 `setDictSource()` 注册为全局字典数据源 |
|
|
324
|
+
| `uploadFile(formData)` | `{ url }` | 演示 `ZlUpload` |
|
|
325
|
+
| `submitResourceForm(data)` | `{ code:200, msg, data }` | 表单提交 |
|
|
326
|
+
| `getResourcePageMaybeFail(params)` | `fail:true` 时 reject | 演示错误态 |
|
|
327
|
+
|
|
328
|
+
### 6.3 组件对接实战(对应 `/use-request` 页面)
|
|
329
|
+
|
|
330
|
+
#### ① `ZlSearchTable` —— 直接传 service
|
|
331
|
+
```vue
|
|
332
|
+
<ZlSearchTable
|
|
333
|
+
:request="getResourcePage"
|
|
334
|
+
:columns="columns"
|
|
335
|
+
:query-items="queryItems"
|
|
336
|
+
:pagination="{ pageSize: 6 }"
|
|
337
|
+
height="380"
|
|
338
|
+
/>
|
|
339
|
+
```
|
|
340
|
+
内部用 `usePageableRequest` 驱动分页与查询;`columns` 为 `TableColumn[]`,`queryItems` 为 `SearchItem[]`。
|
|
341
|
+
|
|
342
|
+
#### ② `ZlCardPage` —— 直接传 service
|
|
343
|
+
```vue
|
|
344
|
+
<ZlCardPage :request="getResourcePage" card-min-width="200px">
|
|
345
|
+
<template #default="{ data }">
|
|
346
|
+
<el-card>{{ data.name }}</el-card>
|
|
347
|
+
</template>
|
|
348
|
+
</ZlCardPage>
|
|
349
|
+
```
|
|
350
|
+
内部组合 `usePageableRequest + ZlSearch + ZlPagination + ZlEmpty`。
|
|
351
|
+
|
|
352
|
+
#### ③ `ZlTable` + `ZlPagination` —— 自行 `usePaginationRequest`
|
|
353
|
+
```ts
|
|
354
|
+
const req = usePaginationRequest(getResourcePage, { manual: true })
|
|
355
|
+
onMounted(() => req.run({ pageNum: 1, pageSize: 8 }))
|
|
356
|
+
```
|
|
357
|
+
```vue
|
|
358
|
+
<ZlTable :columns="columns" :data="req.data" :loading="req.loading" height="320" />
|
|
359
|
+
<ZlPagination
|
|
360
|
+
:total="req.total" :page-num="req.current" :page-size="req.pageSize"
|
|
361
|
+
@change="({ pageNum, pageSize }) => req.changePagination(pageNum, pageSize)"
|
|
362
|
+
/>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### ④ `ZlSearch` 触发 `run(params)`
|
|
366
|
+
```ts
|
|
367
|
+
const req = usePaginationRequest(getResourcePage, { manual: true })
|
|
368
|
+
function onSearch(params) { req.run({ pageNum: 1, pageSize: 6, ...params }) }
|
|
369
|
+
```
|
|
370
|
+
```vue
|
|
371
|
+
<ZlSearch :items="queryItems" @search="onSearch" />
|
|
372
|
+
```
|
|
373
|
+
`ZlSearch` 自带 `overflowCount`(默认 4,折叠溢出项),`@search` 回传查询参数对象。
|
|
374
|
+
|
|
375
|
+
#### ⑤ `ZlDictTag` —— 注入字典后由 `useDict` 解析
|
|
376
|
+
|
|
377
|
+
字典模块**不依赖任何微前端逻辑**:数据由调用方注入或经 `setDictSource` 注册的加载器获取,缓存在独立的全局字典 store(`useDictStore`,基于 Vue3 `createGlobalState` 单例)中,跨组件共享并复用,避免重复加载。
|
|
378
|
+
|
|
379
|
+
`useDict` 的数据解析优先级(从高到低):**组件级直传 → 局部注入 source → 全局 source**。任一来源命中即写入缓存 `dictStore[key]` 统一解析;同一 key 仅在缓存未命中且未在加载时触发一次加载(结果缓存)。
|
|
380
|
+
|
|
381
|
+
**A. 组件级直传(最轻量,推荐一次性/局部使用)**
|
|
382
|
+
|
|
383
|
+
```vue
|
|
384
|
+
<ZlDictTag dict-key="resourceType" :value="row.type"
|
|
385
|
+
:dict="[{ label: '图片', value: '1', icon: 'zl:image' }, { label: '视频', value: '2' }]" />
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**B. 全局注册数据源(应用入口注册一次,全项目共享,适配不同项目)**
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import { setDictSource } from '@zl-ui-kit/composables'
|
|
392
|
+
// 静态字典表
|
|
393
|
+
setDictSource({ resourceType: [{ label: '图片', value: '1' }], resourceStatus: [{ label: '正常', value: '1' }] })
|
|
394
|
+
// 或按需异步加载(推荐对接各自项目后端)
|
|
395
|
+
setDictSource(async (key) => await api.getDictOptions(key))
|
|
396
|
+
```
|
|
397
|
+
之后任意 `<ZlDictTag dict-key="resourceType" :value="row.type" />` 自动翻译,无需逐个传 `dict`。
|
|
398
|
+
|
|
399
|
+
**C. 局部注入(为某棵组件子树单独指定数据源,覆盖全局)**
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
import { useDictProvider } from '@zl-ui-kit/composables'
|
|
403
|
+
// 子树根组件 setup 中
|
|
404
|
+
useDictProvider({ source: { resourceType: [...] } })
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
```vue
|
|
408
|
+
<ZlDictTag dict-key="resourceType" :value="row.type" />
|
|
409
|
+
<ZlDictTag dict-key="resourceType" :value="row.type" simple />
|
|
410
|
+
```
|
|
411
|
+
> **注意**:`ZlTable` 列上的 `dictKey` 仅作 schema 字段透传,**不会**自动渲染字典标签,需用列插槽自行渲染 `ZlDictTag`(如 demo 中 `#type`/`#status`/`#region` 插槽)。
|
|
412
|
+
|
|
413
|
+
#### ⑥ `ZlUpload` —— 传上传 service
|
|
414
|
+
```vue
|
|
415
|
+
<ZlUpload v-model="uploadModel" :request="uploadFile" accept=".png,.jpg,.pdf" :max-size="5" :limit="3" />
|
|
416
|
+
```
|
|
417
|
+
`request` 接收 `FormData` 返回 `{ url }`;`maxSize` 单位 MB。
|
|
418
|
+
|
|
419
|
+
#### ⑦ `ZlTree` —— `useRequest` 绑定 `data`
|
|
420
|
+
```ts
|
|
421
|
+
const treeReq = useRequest(getTreeData)
|
|
422
|
+
// 必须提取为顶层 ref,否则模板中取到的是 Ref 对象本身(见 6.2 注意事项)
|
|
423
|
+
const { data: treeData, loading: treeLoading } = treeReq
|
|
424
|
+
```
|
|
425
|
+
```vue
|
|
426
|
+
<ZlTree v-if="!treeLoading" :data="treeData" :props="{ label: 'title', children: 'children' }" />
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### ⑧ `ZlChart` —— `useDataRequest` 解包后构建 option
|
|
430
|
+
`ZlChart` 显式 prop 为 `series`,其余配置(title/xAxis/yAxis/tooltip/grid)经 attrs 透传,故用 `v-bind` 传完整 echarts option:
|
|
431
|
+
```ts
|
|
432
|
+
const chartReq = useDataRequest(getChartDataApi, { dataKey: 'data' })
|
|
433
|
+
const chartOption = computed(() => {
|
|
434
|
+
const d = chartReq.data.value
|
|
435
|
+
if (!d) return {}
|
|
436
|
+
return {
|
|
437
|
+
tooltip: { trigger: 'axis' },
|
|
438
|
+
xAxis: { type: 'category', data: d.categories },
|
|
439
|
+
yAxis: { type: 'value' },
|
|
440
|
+
series: [{ type: 'bar', data: d.counts }],
|
|
441
|
+
}
|
|
442
|
+
})
|
|
443
|
+
```
|
|
444
|
+
```vue
|
|
445
|
+
<ZlChart v-bind="chartOption" />
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
#### ⑨ `useRequest` 生命周期面板
|
|
449
|
+
```ts
|
|
450
|
+
const req = useRequest(getResourcePageMaybeFail, { defaultParams: [{ pageNum: 1, pageSize: 5 }] })
|
|
451
|
+
req.refresh() // 重跑
|
|
452
|
+
req.mutate(prev => ({ ...prev, rows: [...] })) // 本地改数据,不触网
|
|
453
|
+
req.cancel() // 取消进行中请求
|
|
454
|
+
// fail:true 时 service reject → req.error,可渲染错误态
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### 6.4 常见坑位
|
|
458
|
+
|
|
459
|
+
1. **`dictKey` 不会自动渲染**:`ZlTable` 列上的 `dictKey` 不渲染标签,必须列插槽包 `ZlDictTag`。
|
|
460
|
+
2. **⚠️ 嵌套在返回对象里的 ref 不会自动解包**(高频踩坑):`useRequest` 返回的是**普通对象**,其 `data`/`loading` 等属性是 ref。Vue 模板**只对顶层 ref 自动解包**,写在 `req.data` 这种嵌套形式里取到的是 **Ref 对象本身**,不是值——`v-if="req.loading"` 会恒为 `true`(Ref 对象是 truthy),导致骨架屏永不消失;`:data="req.data"` 传下去的是 Ref 对象,子组件拿不到数组。
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
// ❌ 错误:模板里取到的是 Ref 对象
|
|
464
|
+
const req = useRequest(getList)
|
|
465
|
+
```
|
|
466
|
+
```vue
|
|
467
|
+
<Table v-if="!req.loading" :data="req.data" />
|
|
468
|
+
```
|
|
469
|
+
```ts
|
|
470
|
+
// ✅ 正确:提取为顶层 ref,模板中自动解包
|
|
471
|
+
const req = useRequest(getList)
|
|
472
|
+
const { data: listData, loading: listLoading } = req
|
|
473
|
+
```
|
|
474
|
+
```vue
|
|
475
|
+
<Table v-if="!listLoading" :data="listData" />
|
|
476
|
+
```
|
|
477
|
+
方法调用不受影响(事件处理器中可直接写 `req.refresh()`,无需解包)。
|
|
478
|
+
3. **`ZlChart` 传参**:用 `series` prop + 其余 attrs 透传(推荐 `v-bind` 传整个 option),勿只传 `:option`。
|
|
479
|
+
4. **图标名必须存在**:字典/组件 `icon` 用的名字须在 `icons.json` 真实存在。
|
|
480
|
+
5. **分页键名对齐**:后端用 `current/pageSize/list` 而非 `pageNum/pageSize/rows` 时,用 `pagination` 选项或 `setGlobalOptions` 覆盖。
|
|
481
|
+
|
|
482
|
+
---
|
|
29
483
|
|
|
30
|
-
##
|
|
484
|
+
## 7. 表单与交互组件
|
|
31
485
|
|
|
486
|
+
### ZlForm —— schema 驱动
|
|
32
487
|
```ts
|
|
33
|
-
|
|
488
|
+
const items: FormItem[] = [
|
|
489
|
+
{ model: 'name', label: '名称', component: 'el-input', props: { placeholder: '请输入' }, rules: [{ required: true }] },
|
|
490
|
+
{ model: 'type', label: '类型', component: 'el-select', props: { options: [...] } },
|
|
491
|
+
{ model: 'enable', label: '启用', component: 'ZlSwitch' },
|
|
492
|
+
]
|
|
493
|
+
```
|
|
494
|
+
```vue
|
|
495
|
+
<ZlForm :model="form" :items="items" label-width="auto" />
|
|
496
|
+
```
|
|
497
|
+
`FormItem` 关键字段:`model`(必填) / `label` / `component`(组件名或组件) / `props`(自动包装 `update:modelValue` 回写) / `slots` / `rules` / `defaultValue` / `visible`(布尔或函数) / `tooltip`(label 后帮助图标 + tooltip)。
|
|
498
|
+
|
|
499
|
+
### ZlSearch —— 查询条
|
|
500
|
+
见 6.3 ④。`items: SearchItem[]`,`SearchItem` 继承 `FormItem` 并扩展 `toLabel`(值→条件标签) 与 `toParams`(值→查询参数合并)。
|
|
501
|
+
|
|
502
|
+
### ZlDrawer / ZlPopconfirm / ZlSwitch
|
|
503
|
+
- `ZlDrawer`:自绘 footer(okLoading/hideCancel/readonly 语义保持),`loading`→`v-loading`。
|
|
504
|
+
- `ZlPopconfirm`:基于 `el-popover` 自绘面板,保留 title/content 双插槽与确认/取消事件。
|
|
505
|
+
- `ZlSwitch`:`active-value`/`inactive-value` 包装,文本在侧(`checked-text`)。
|
|
506
|
+
|
|
507
|
+
### ZlAction / ZlActionList —— 操作列与操作列表
|
|
508
|
+
基于 `ActionItem`(label/icon/onClick/disabled/divider 等),`ZlTable` 列 `actions` 即渲染 `ZlActionList`。
|
|
509
|
+
|
|
510
|
+
### ZlRadioTag / ZlCheckboxTag / ZlCheckAll / ZlSorter / ZlAddSub / ZlLayoutSwitch
|
|
511
|
+
- `ZlRadioTag`/`ZlCheckboxTag`:隐藏原生 input,标签本体即 `ZlTag`,checked 由 model 推导;`ZlCheckboxTag` 支持 exclusive。
|
|
512
|
+
- `ZlCheckAll`:全选态 computed 推导。
|
|
513
|
+
- `ZlSorter`/`ZlSorterList`:排序控件。
|
|
514
|
+
- `ZlAddSub`:加减步进。
|
|
515
|
+
- `ZlLayoutSwitch`:布局形态切换(el-radio-button + `zl:` 图标)。
|
|
516
|
+
|
|
517
|
+
### ZlTabs / ZlDynamicComponent / ZlGrid
|
|
518
|
+
- `ZlTabs`:`el-tabs` 封装,schema `{ key, title, lazy }`,extra 经外层绝对定位承载。
|
|
519
|
+
- `ZlDynamicComponent`:schema→VNode 渲染引擎,默认 `el-input`。
|
|
520
|
+
- `ZlGrid`(+`ZlGridItem`):自绘栅格,label 自适应/跨列/省略提示/字典。
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## 8. 布局与权限
|
|
525
|
+
|
|
526
|
+
### ZlAppLayout / ZlAppMenu
|
|
527
|
+
- `ZlAppLayout`:horizontal/vertical 双形态布局骨架,折叠按钮 `el-button circle`。
|
|
528
|
+
- `ZlAppMenu`(+Item):`el-menu` 重写(index=menu.name),主题经 `--zl-menu-*` CSS 变量对接 `--el-menu-*`。
|
|
529
|
+
|
|
530
|
+
### ZlPermission
|
|
531
|
+
配合 `usePermissionProvider` 提供权限上下文,`usePermission` 校验;样例见 `/general`。
|
|
532
|
+
|
|
533
|
+
### ZlHorizontalTable
|
|
534
|
+
`el-row`/`el-col`(24 栅格)实现的横向描述表。
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## 9. 文件与编辑
|
|
539
|
+
|
|
540
|
+
- `ZlFilePreview` / `ZlFileViewer` / `ZlPdfViewer`:文件预览(含 PDF),样例见 `/general`。
|
|
541
|
+
- `ZlEditor` / `ZlSqlEditor`(+`useSqlCompletion`):富文本 / SQL 编辑器。
|
|
542
|
+
- `ZlLogin`(+`LoginForm`+`SlideChecker`):登录框,背景改半透明 + `backdrop-filter`。
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## 10. 主题定制(@zl-ui-kit/theme)
|
|
547
|
+
|
|
548
|
+
机制:`body[data-theme='<名>']` CSS 变量作用域 + `--zl-*`(业务) / `--el-*`(EP) 双体系。内置 5 套:`light`(默认) / `dark` / `red-light` / `red-dark` / `blue`。
|
|
549
|
+
|
|
550
|
+
> 主题负责「视觉」的定义与覆盖;**「文案」的同等人格能力见第 13 章**(`createZlI18n`,与 `defineTheme` 对称)。
|
|
551
|
+
|
|
552
|
+
### L1 变量直覆(小幅微调)
|
|
553
|
+
```css
|
|
554
|
+
body {
|
|
555
|
+
--zl-primary-6: #3056d3;
|
|
556
|
+
--el-color-primary: #3056d3;
|
|
557
|
+
--el-border-radius-base: 6px;
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### L2 运行时定义主题(换肤/多租户推荐)
|
|
562
|
+
```ts
|
|
563
|
+
import { defineTheme, setTheme, enableThemePersistence, useTheme } from '@zl-ui-kit/theme'
|
|
564
|
+
defineTheme({ name: 'brand-purple', base: 'light', colors: { primary: '#722ED1', danger: '#f53f3f' }, apply: true })
|
|
565
|
+
setTheme('brand-purple')
|
|
566
|
+
enableThemePersistence() // 可选:localStorage 记住选择(key: zl-theme)
|
|
567
|
+
const { theme, themes, setTheme } = useTheme() // 组件内响应式
|
|
568
|
+
```
|
|
569
|
+
自定义主题与内置主题变量完全同构,色阶派生算法与 EP theme-chalk 对齐,支持任意数量并存切换;`tokens` 参数可覆盖任意底层 token 深度定制。
|
|
34
570
|
|
|
35
|
-
|
|
571
|
+
### L3 构建时生成主题(深度定制,规划中)
|
|
572
|
+
需求 §12-Q14:将提供 CLI 产出与内置同构的完整 CSS,当前可先用 L2 `tokens` 覆盖近似。
|
|
573
|
+
|
|
574
|
+
### SCSS 工具(编译期定制)
|
|
575
|
+
```scss
|
|
576
|
+
@use '@zl-ui-kit/theme/scss/colors.scss' as *;
|
|
577
|
+
.my-card { @include colorGradient(primary, to right); color: var(--zl-primary-6); }
|
|
36
578
|
```
|
|
579
|
+
- `scss/colors.scss`:`colorForEach` / `colorGradient`
|
|
580
|
+
- `scss/map.scss`:`mapGet`
|
|
581
|
+
- `scss/theme.scss`:`theme()` mixin(为每套主题生成选择器块)
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## 11. 调试与常见问题
|
|
586
|
+
|
|
587
|
+
### 各包常用命令
|
|
588
|
+
| 包 | 生成 | 构建 | 单测 |
|
|
589
|
+
| --- | --- | --- | --- |
|
|
590
|
+
| @zl-ui-kit/icons | `pnpm --filter @zl-ui-kit/icons gen`(svg → icons.json,prefix `zl`) | 同 gen | — |
|
|
591
|
+
| @zl-ui-kit/theme | `pnpm --filter @zl-ui-kit/theme gen`(_json → generated/) | `build`(gen + tsup) | — |
|
|
592
|
+
| @zl-ui-kit/utils | — | `build`(tsup) | `test:unit`(vitest) |
|
|
593
|
+
| @zl-ui-kit/composables | — | `build`(tsup) | — |
|
|
594
|
+
| @zl-ui-kit/components | — | `build`(vite lib + dts + 样式注入) | — |
|
|
595
|
+
|
|
596
|
+
### 文档维护位置
|
|
597
|
+
|
|
598
|
+
本文件(`packages/components/README.md`)就是 UI 组件的**唯一权威文档**,直接在这里维护 —— npm 官网渲染的就是它,不再另存一份 Markdown(原 `docs/UI组件使用文档.md` 已删除)。
|
|
599
|
+
|
|
600
|
+
- 新增 / 变更组件:补 §3 总览 + 对应章节用法;
|
|
601
|
+
- 新增公共 Hook 或能力:补 §12;
|
|
602
|
+
- 新增接入能力(i18n、主题等):独立成章(见第 10 / 13 章);
|
|
603
|
+
- 其余四包 README 只保留本包速查,并在顶部指向本页。
|
|
604
|
+
|
|
605
|
+
### 常见问题
|
|
606
|
+
- **改了 theme 的 `_json/*.json` 不生效**:先 `pnpm --filter @zl-ui-kit/theme gen`(生成 `src/generated/`),再刷新 playground。
|
|
607
|
+
- **新增 svg 图标**:放入 `packages/icons/src/svg/`,执行 icons 的 `gen` 后重启 playground。
|
|
608
|
+
- **`@zl-ui-kit/theme/scss/*.scss` 找不到**:确认已执行 theme `gen`(`generated/_constants.scss` 是其依赖)。
|
|
609
|
+
- **改源码不热更**:确认 playground 走源码模式(alias 指向 `src/`),勿在产物模式下调试。
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## 12. 公共 Hooks 与 Utils 方法
|
|
614
|
+
|
|
615
|
+
`@zl-ui-kit/composables` 与 `@zl-ui-kit/utils` 除支撑组件外,也作为**独立的公共能力**供业务项目直接使用。以下集中列出全部导出,组件用法见前文章节。
|
|
616
|
+
|
|
617
|
+
### 12.1 `@zl-ui-kit/composables` —— 公共 Hooks
|
|
618
|
+
|
|
619
|
+
```ts
|
|
620
|
+
import { useRequest, useMicroGlobalData, useDict /* … */ } from '@zl-ui-kit/composables'
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
#### 12.1.1 请求体系 `useRequest`(插件与全局配置)
|
|
624
|
+
|
|
625
|
+
核心 API 见第 6 章。此处补充**插件**与**全局配置**:
|
|
37
626
|
|
|
38
|
-
|
|
627
|
+
- **内置插件自动装载**,通过 `options` 开启(无需 import):
|
|
628
|
+
- **loading**:`loadingDelay`(ms,延迟显示 loading,避免闪烁) / `loadingKeep`(ms,最短保持时间)
|
|
629
|
+
- **ready**:`ready: Ref<boolean>`,为假时拦截请求,为真后自动 `run`(适合依赖前置条件就绪后再请求)
|
|
630
|
+
- **refreshDeps**:`refreshDeps: WatchSource | WatchSource[]`,依赖变化自动 `refresh`;可用 `refreshDepsAction` 自定义动作
|
|
631
|
+
- **download**:`outputFile: MaybeRefOrGetter<string>`,`service` 返回 `Blob`(非 JSON)时自动下载保存
|
|
632
|
+
- **自定义插件**:`defineUseRequestPlugin(plugin)` 返回 `PluginImplementType`,经 `useRequest(service, options, [myPlugin])` 第三参注入,可实现 `onBefore / onRequest / onSuccess / onError / onFinally / onCancel / onMutate` 钩子。
|
|
633
|
+
- **全局默认项**:
|
|
634
|
+
- `setGlobalOptions({ pagination?, loadingDelay?, loadingKeep? })` —— 全局生效。常用于**统一后端分页键名**(默认 `pageNum / pageSize / total / rows`,后端用 `current/pageSize/list` 时在此覆盖)。
|
|
635
|
+
- `useRequestProvider(options)` —— 在父组件 `provide` 一个作用域默认项(优先级:局部 > 作用域 > 全局)。
|
|
39
636
|
|
|
40
637
|
```ts
|
|
41
|
-
|
|
42
|
-
import {
|
|
638
|
+
// 全局统一分页键名(在应用入口调用一次)
|
|
639
|
+
import { setGlobalOptions } from '@zl-ui-kit/composables'
|
|
640
|
+
setGlobalOptions({ pagination: { currentKey: 'current', totalKey: 'totalCount', dataKey: 'list' } })
|
|
43
641
|
|
|
44
|
-
|
|
642
|
+
// 自定义插件示例
|
|
643
|
+
const logPlugin = defineUseRequestPlugin((inst) => ({
|
|
644
|
+
onSuccess(data) { console.log('[req]', inst.params.value, data) },
|
|
645
|
+
}))
|
|
646
|
+
const { data } = useRequest(getList, {}, [logPlugin])
|
|
45
647
|
```
|
|
46
648
|
|
|
47
|
-
|
|
649
|
+
#### 12.1.2 微前端通信(基于 `@micro-zoe/micro-app`)
|
|
650
|
+
|
|
651
|
+
> 运行前提:微前端相关 hook 需要 `@micro-zoe/micro-app`(**0.3.2 起为可选依赖**,`npm i @micro-zoe/micro-app`)。
|
|
652
|
+
> 缺失时这些 hook 会抛出带安装指引的错误。
|
|
653
|
+
>
|
|
654
|
+
> `useMicroGlobalData` 是例外:它在检测不到 micro-app 时**自动降级**为本地存储模式
|
|
655
|
+
> (`$zl_locale` / `$zl_theme` / `$zl_token`),因此 `useLocale`、主题、字典在纯 Web 应用中照常可用。
|
|
656
|
+
> `enableGlobalDataMock(true)` 仍然保留,用于开发态强制走 mock 数据。
|
|
657
|
+
|
|
658
|
+
| Hook / 函数 | 返回 / 签名 | 说明 |
|
|
659
|
+
| --- | --- | --- |
|
|
660
|
+
| `useMicroApp()` | `{ apps, isMicroBase(), isMicroChild(), updateApps(), startMicroApp(config?) }` | 微应用身份判断、启动、活跃应用列表(全局单例) |
|
|
661
|
+
| `useMicroAction(action, appName?)` | `(...args) => void` | 向子应用(或指定 app,缺省广播全部)派发动作事件(前缀 `action__`) |
|
|
662
|
+
| `useMicroActionAsync(action, appName?)` | `(...args) => Promise` | 派发并等待 Promise 化结果(主/子应用语义不同) |
|
|
663
|
+
| `useMicroListener()`(共享单例) | `{ on, off, once }` | 监听动作事件 |
|
|
664
|
+
| `useMicroResultListener(action, callback)` | `void` | 监听动作执行结果 |
|
|
665
|
+
| `dispatchMicroAction(action, appName, ...data)` | `Promise<void>` | 向指定子应用派发动作(回调式) |
|
|
666
|
+
|
|
667
|
+
#### 12.1.3 全局数据 `useMicroGlobalData`
|
|
668
|
+
|
|
669
|
+
```ts
|
|
670
|
+
const g = useMicroGlobalData()
|
|
671
|
+
```
|
|
672
|
+
响应式字段(均为 `WritableComputedRef`):`locale` / `theme` / `token` / `isI18nKeyVisible` / `roles` / `permissions` / `user` / `localeMessages` / `elLocaleMessages` / `config`。(注:字典数据已独立为 `useDictStore`,不再挂在全局数据上。)
|
|
673
|
+
|
|
674
|
+
方法:`getConfig(key, default?)` / `isConfig(key, compareValue='true')` / `setAndWaitUntil({ set?, until? })`(设置并等待条件成立)/ `resetUser()`。
|
|
675
|
+
存储键:`$zl_locale` / `$zl_theme` / `$zl_token`(经 `useStorage` 持久化,`theme` 缺省 `light`)。
|
|
48
676
|
|
|
49
677
|
```ts
|
|
50
|
-
|
|
51
|
-
import {
|
|
52
|
-
|
|
678
|
+
// 无 micro-app 环境会自动降级为本地存储模式,无需额外调用(0.3.2 起)
|
|
679
|
+
import { useMicroGlobalData } from '@zl-ui-kit/composables'
|
|
680
|
+
const g = useMicroGlobalData() // locale/theme/token 等全局数据(与字典无关)
|
|
681
|
+
|
|
682
|
+
// 如需强制使用 mock 数据(例如 playground 调试),仍可显式开启,须在首次调用前执行
|
|
683
|
+
import { enableGlobalDataMock } from '@zl-ui-kit/composables'
|
|
684
|
+
enableGlobalDataMock(true)
|
|
53
685
|
```
|
|
54
686
|
|
|
55
|
-
|
|
687
|
+
`enableGlobalDataMock(enabled = true)`:显式开启开发态 mock。**不是**使用本 hook 的必要条件 ——
|
|
688
|
+
检测不到 micro-app 时会自动降级,只有想强制 mock 时才需要调用。
|
|
689
|
+
|
|
690
|
+
> 字典数据已独立:经 `setDictSource()` 注册(见 12.1.4),由 `useDictStore` 缓存,不再通过 `useMicroGlobalData().dictStore`。
|
|
691
|
+
|
|
692
|
+
#### 12.1.4 字典与国际化
|
|
693
|
+
|
|
694
|
+
- `useDictStore`(全局字典 store,基于 Vue3 `createGlobalState` 单例):`dictStore`(缓存 `Record<key, DictItem[]>`)、`loadingKeys`、`setDictSource(source|null)`、`getDictSource()`、`ensureDict(key, localSource?)`。同一 key 仅在缓存未命中且未在加载时触发一次加载,结果缓存复用。
|
|
695
|
+
- `useDict(key, options?)` → `ComputedRef<DictItem[]>`(附带 `.label: Record<value,label>`):读 `useDictStore` 并解析。数据解析优先级:组件级 `options.data` → 局部 `useDictProvider({ source })` → 全局 `setDictSource(source)`。`DictItem = { label, value, icon?, type? }`(亦可用 `type DictItem` 导入)。
|
|
696
|
+
- `setDictSource(source | null)`:全局注册字典数据源(应用入口调用一次)。`source` 为静态表 `Record<string, DictItem[]>` 或函数 `(key) => DictItem[] | Promise<DictItem[]>`;传 `null` 清空。`getDictSource()` 可读取当前值。
|
|
697
|
+
- `useDictProvider(source)`:子树根提供局部字典数据源(覆盖全局,非微前端注入)。
|
|
698
|
+
- `useLocale(options?, loader?)` → `{ i18n, locale, isI18nKeyVisible }`:创建与全局语言联动的 vue-i18n 实例;`loader(locale)` 按需返回 `{ localeMessages, elLocaleMessages }`(业务包 + Element Plus locale)。`elLocaleMessages` 写入全局数据后,**必须**由 `ZlI18nProvider` 经 `<el-config-provider>` 挂载到 Element Plus 才生效(分页 / 日期选择器等内置组件才本地化)。详见第 13 章。
|
|
699
|
+
- `useZlT()` → `{ t }`:组件内部渐进式翻译的安全桥梁。`t(key, fallback?, params?)` 在宿主已 `app.use(i18n)` 时委托 vue-i18n 解析(`params` 用于 `{name}` 命名插值),未命中 key 或宿主未装 i18n 时降级为 `fallback`(缺省返回 key),**绝不抛错**,从而可在不破坏存量消费方的前提下逐步把硬编码中文替换为 `t('zl.xxx','中文兜底')`。语言包 key 规范:`zl.<组件>.<语义>`。
|
|
700
|
+
- `mergeMessages(base, ...overrides)`:i18n 语言包深度合并,后者覆盖前者、未提供的 key 保留原值(按 `locale → key` 结构)。用于消费者「只覆盖部分文案」。
|
|
701
|
+
- `createZlI18n(options?)`(来自 `@zl-ui-kit/components`):消费者侧 i18n 工厂,**自动包含组件库内置语言包 `zlMessages`**,支持 `messages` 覆盖 / 新增语言、`elLocales` 提供 EP 语言包、`includeZlMessages` 关闭默认包。与主题的 `defineTheme` 地位对等。
|
|
702
|
+
- `zlMessages` / `zlZhMessages` / `zlEnMessages`(来自 `@zl-ui-kit/components`):组件库内置语言包(zh / zh-cn / en),可被覆盖或整包替换。
|
|
703
|
+
- `ZlI18nProvider`(新增,来自 `@zl-ui-kit/components`):国际化根容器。用法 `<ZlI18nProvider><App/></ZlI18nProvider>`,在应用根包裹一次,把全局 `elLocaleMessages` 绑定到 Element Plus。
|
|
56
704
|
|
|
57
|
-
|
|
705
|
+
#### 12.1.5 全局消息 `useGlobalMessage`
|
|
58
706
|
|
|
59
|
-
|
|
707
|
+
```ts
|
|
708
|
+
const { Message, Modal, Notification } = useGlobalMessage()
|
|
709
|
+
Message.success('保存成功')
|
|
710
|
+
// Modal 在 ElMessageBox 基础上扩展了 confirmPromise
|
|
711
|
+
const r = await Modal.confirmPromise({ title: '删除', message: '确认删除?' },
|
|
712
|
+
async (ctx) => { await doDelete() }, // onOk
|
|
713
|
+
async (ctx) => { /* onCancel */ })
|
|
714
|
+
// r: 'Ok' | 'Cancel' | false(onOk 抛错且 throwOnOk=false 时)
|
|
715
|
+
```
|
|
716
|
+
`throwOnOk` / `throwOnCancel` 控制错误是否向上抛出(默认吞错 resolve(false) / resolve('Cancel'))。
|
|
60
717
|
|
|
61
|
-
|
|
718
|
+
#### 12.1.6 页签管理 `usePageTabs`
|
|
62
719
|
|
|
63
|
-
|
|
720
|
+
```ts
|
|
721
|
+
const { roots, activeRoot, pageTabs, current, addPageTab, removePageTab, removeCurrentTab, clearPageTab, setCurrentTab, createPageTabsRoot, setRootChild } = usePageTabs()
|
|
722
|
+
```
|
|
723
|
+
micro-app 多应用页签模型(root 树 → 扁平页签 → 当前激活页签)。另含 `micros`、`activeRootKey` 只读属性。
|
|
64
724
|
|
|
65
|
-
|
|
725
|
+
#### 12.1.7 其它 Hooks
|
|
726
|
+
|
|
727
|
+
- `createCacheComputed(getter)`:带缓存的 computed 工厂,同一对象结果缓存、作用域销毁自动清理;返回函数带 `.clear()`。
|
|
728
|
+
- `useSash(refName, options?)` → `{ sashElRef }`:拖拽分隔条交互;`options` 含 `start / onMousedown / onMousemove({dx,dy,start}) / onMouseup / onDoubleClick`。
|
|
729
|
+
- `useServiceType({ platformKey='zl.service.type', blockchainKey='blockchain.service.type' })` → `{ isPlatform, isBlockchain }`:从 `globalData.config` 判断平台版/区块链版(键名可配)。
|
|
730
|
+
- `useUKey()`(共享单例)→ `{ sn, getSn, getUser, getDevice, isUserExists, getCSR, writeCRT, sendSign, stopHeartbeat }`:UKey 硬件签名流程封装。**运行前提**:需本机 UKey 厂商 HTTP 服务(默认 `127.0.0.1:8080`,端口经 `globalData.config['zl.feature.shufflePort']` 可配)。无硬件环境的项目不应调用。
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
### 12.2 `@zl-ui-kit/utils` —— 工具方法
|
|
735
|
+
|
|
736
|
+
```ts
|
|
737
|
+
import { createRequest, deepCopy, formatFilesize /* … */ } from '@zl-ui-kit/utils'
|
|
738
|
+
import request from '@zl-ui-kit/utils' // 默认导出一个开箱即用的 createRequest 实例
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
#### 12.2.1 网络请求封装 `createRequest`
|
|
742
|
+
|
|
743
|
+
`createRequest(options?)` → `AxiosInstance`:在 axios 之上封装 —— 防重复请求(200ms 内同 key 拦截,`config.ignoreRepeat` 跳过)、`qs` 参数序列化、`{cipher}` **AES 响应自动解密**、统一业务错误回调(成功码默认 `200`)。
|
|
744
|
+
|
|
745
|
+
- `RequestOptions` 扩展项:`successCode` / `enableCipher`(默认 true) / `throwOnRepeat`(默认 true) / `onRepeat` / `onError(code, source, extra?)` / `onResponse(data, res)` / `logDecrypt`。
|
|
746
|
+
- `RequestError`:`{ code, message, extra }`;网络/HTTP 错误码 `95xx`(`9501` 网络 / `9502` 超时 / `9503` HTTP 状态)。
|
|
747
|
+
- `CRYPTO_CIPHER_CODE = '{cipher}{1}'`:AES 密文响应前缀标识。
|
|
748
|
+
- `setupRequest(instance, onRequest?, onError?)`:为已有实例追加拦截(子应用/宿主扩展)。
|
|
749
|
+
|
|
750
|
+
```ts
|
|
751
|
+
const api = createRequest({ baseURL: '/api', successCode: 200, enableCipher: true })
|
|
752
|
+
api.get('/resources', { params: { pageNum: 1, pageSize: 10 } })
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
#### 12.2.2 数组 / 对象 / 类型判断
|
|
756
|
+
|
|
757
|
+
| 函数 | 说明 |
|
|
758
|
+
| --- | --- |
|
|
759
|
+
| `convertArrayToTree(list, { id='id', pid='pid', children='children' })` | 扁平数组转树(基于 id/pid,支持乱序输入) |
|
|
760
|
+
| `ensureArray(val)` | 非数组值包装为单元素数组 |
|
|
761
|
+
| `deepCopy(obj)` / `shallowCopy(obj)` | 深拷贝 / 浅拷贝(内部用 `hasOwn`) |
|
|
762
|
+
| `hasOwn(obj, key)` | `Object.prototype.hasOwnProperty` 封装 |
|
|
763
|
+
| `isObject` / `isPlainObject` / `isArray` / `isFunction` / `isPromise` / `isBoolean` / `isNumber` / `isDefined` | 类型判断(TS 类型守卫) |
|
|
764
|
+
|
|
765
|
+
#### 12.2.3 函数 / 加密 / DOM / 文件 / 微前端 URL / 杂项
|
|
766
|
+
|
|
767
|
+
**函数**
|
|
768
|
+
- `debounce(fn, delay = 300)`:防抖。
|
|
769
|
+
- `traverseTree(tree, cb, { children = 'children' })`:深度遍历树结构数组。
|
|
770
|
+
- `withResolvers<T>()`:`Promise.withResolvers` 兼容实现,返回 `{ resolve, reject, promise }`。
|
|
771
|
+
|
|
772
|
+
**加密**(CryptoJS AES-CBC-PKCS7)
|
|
773
|
+
- `aesEncrypt(plain, aesKey = DEFAULT_AES_KEY, aesIvKey = DEFAULT_AES_IV_KEY)` / `aesDecrypt(cipher, ...)`
|
|
774
|
+
- 默认密钥常量:`DEFAULT_AES_KEY` / `DEFAULT_AES_IV_KEY`、`DATABASE_AES_KEY` / `DATABASE_AES_IV_KEY`(均可自行传入覆盖)。
|
|
775
|
+
|
|
776
|
+
**DOM**
|
|
777
|
+
- `getRelativeRect(target, relative)` → `{ top, bottom, left, right, width, height }`:元素相对参照元素的矩形。
|
|
778
|
+
- `getStyle(el, prop)`:读取样式(含计算样式回退;`float` → `cssFloat`)。
|
|
779
|
+
|
|
780
|
+
**文件**
|
|
781
|
+
- `fileSave`(re-export `file-saver` 的 `saveAs`)。
|
|
782
|
+
- `joinFilePrefix(path, prefix = '/_file')`:拼接文件服务前缀路径。
|
|
783
|
+
- `downloadFileByUrl(url, prefix?)`:创建临时 `<a>` 触发浏览器下载。
|
|
784
|
+
- `fileUrlPreviewType(path)` → `'image' | 'pdf' | 'word' | 'ppt' | 'excel'`(按扩展名判断)。
|
|
785
|
+
- `formatFilesize(size, { precision = 2, units })`:文件大小格式化(B/KB/MB/GB)。
|
|
786
|
+
|
|
787
|
+
**微前端 URL**
|
|
788
|
+
- `formatMicroUrl(url, { dev?, origins? })` → `{ micro, URL, url, fullpath }`:从路径首段提取微应用标识;`dev` 态可将微应用源映射为本机 dev server(如 `{ dsc: 'http://localhost:5204' }`)。
|
|
789
|
+
|
|
790
|
+
**杂项**
|
|
791
|
+
- `generateUUID()`:UUID v4。
|
|
792
|
+
- `getRandomInt(min, max)`:`[min, max]` 区间随机整数。
|
|
793
|
+
- `paginate(data, pageSize, pageNum)` → `{ rows, pageNum, total }`:内存数组静态分页。
|
|
794
|
+
- `formatNumber(num)`:千分位格式化(保留小数)。
|
|
795
|
+
- `getIndex(rowIndex, pageNum, pageSize)`:计算表格当前页第 `rowIndex` 行的全局序号(从 1 开始)。
|
|
796
|
+
|
|
797
|
+
---
|
|
798
|
+
|
|
799
|
+
## 13. 国际化接入(i18n)
|
|
800
|
+
|
|
801
|
+
组件库提供完整的 i18n 能力,使**业务文案**与 **Element Plus 内置组件**(分页 / 日期选择 / Popconfirm 等)随宿主语言切换。推荐用 `@zl-ui-kit/components` 的 `createZlI18n` 一键接入(内置语言包 + 覆盖),根组件包 `ZlI18nProvider`;组件内部用户可见文案通过 `useZlT` 渐进式抽取。**消费者可在自己项目里定义、覆盖、扩展语言**,能力与主题(`defineTheme`)对等。
|
|
802
|
+
|
|
803
|
+
### 13.1 三层结构
|
|
804
|
+
|
|
805
|
+
| 层 | 来源 | 负责 |
|
|
806
|
+
| --- | --- | --- |
|
|
807
|
+
| 业务文案多语言 | 宿主经 `createZlI18n`(或底层 `useLocale`)安装的 vue-i18n 实例 | `t('zl.xxx')` 解析内置包 + 覆盖包 |
|
|
808
|
+
| Element Plus 内置组件本地化 | `ZlI18nProvider` → `<el-config-provider>` | 绑定 `elLocaleMessages` 到 EP |
|
|
809
|
+
| 调试开关 | `isI18nKeyVisible` | 开启后所有 `t()` 直接显示 key,便于排查漏翻 |
|
|
810
|
+
|
|
811
|
+
> ⚠️ **关键:仅 `useLocale` 不够。** `elLocaleMessages` 由 `useLocale` 的 `loader` 写入全局数据,但必须由 `ZlI18nProvider` 经 `<el-config-provider>` 真正挂载到 Element Plus,否则分页器等内置组件不会翻译。
|
|
812
|
+
|
|
813
|
+
### 13.2 宿主接入(main.ts,推荐 `createZlI18n`)
|
|
814
|
+
|
|
815
|
+
`@zl-ui-kit/components` 的 `createZlI18n` 会**自动包含组件库内置语言包 `zlMessages`(zh / zh-cn / en)**,消费者只需用 `messages` 写要覆盖/新增的 key,`elLocales` 提供 Element Plus 语言包。
|
|
816
|
+
|
|
817
|
+
```ts
|
|
818
|
+
import { createZlI18n } from '@zl-ui-kit/components'
|
|
819
|
+
import { enableGlobalDataMock, useMicroGlobalData } from '@zl-ui-kit/composables'
|
|
820
|
+
import en from 'element-plus/es/locale/lang/en'
|
|
821
|
+
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
|
822
|
+
|
|
823
|
+
// 开发态无 micro-app 时开启本地 mock(须在首次 useMicroGlobalData 前调用)
|
|
824
|
+
enableGlobalDataMock(true)
|
|
825
|
+
|
|
826
|
+
// 指定初始语言,避免 createI18n 取到非法 locale
|
|
827
|
+
const { locale } = useMicroGlobalData()
|
|
828
|
+
locale.value = 'zh'
|
|
829
|
+
|
|
830
|
+
const { i18n } = createZlI18n({
|
|
831
|
+
// 只覆盖想改的 key;未写的走组件库内置默认包
|
|
832
|
+
messages: {
|
|
833
|
+
zh: { 'zl.login.username': '账号' }, // 覆盖已有语言
|
|
834
|
+
en: { 'zl.empty.noData': 'Nothing here yet' },
|
|
835
|
+
ja: { 'zl.empty.noData': 'データがありません' }, // 新增语言
|
|
836
|
+
},
|
|
837
|
+
// Element Plus 语言包(随 locale 自动切换,由 <ZlI18nProvider> 挂载)
|
|
838
|
+
elLocales: { zh: zhCn, en },
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
createApp(App).use(i18n).use(ElementPlus).use(ZlUi).use(router).mount('#app')
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
- `elLocales` 的 value 须是 Element Plus 语言包模块(`element-plus/es/locale/lang/<locale>`)。
|
|
845
|
+
- **合并优先级(低 → 高):内置 `zlMessages` → `loader` 返回的业务包 → 消费者 `messages` 覆盖项。**
|
|
846
|
+
- 需完全自定义加载逻辑(如远端按需拉取)时,改用底层 `useLocale(options, loader)`(见 §12.1.4);`createZlI18n` 内部即是对它的封装,并额外处理「默认包 + 覆盖 + EP 包」的合并。
|
|
847
|
+
|
|
848
|
+
### 13.3 根容器挂载(App.vue)
|
|
849
|
+
|
|
850
|
+
```vue
|
|
851
|
+
<script setup lang="ts">
|
|
852
|
+
import { useMicroGlobalData } from '@zl-ui-kit/composables'
|
|
853
|
+
import { ZlI18nProvider } from '@zl-ui-kit/components'
|
|
854
|
+
const { locale, isI18nKeyVisible } = useMicroGlobalData()
|
|
855
|
+
</script>
|
|
856
|
+
|
|
857
|
+
<template>
|
|
858
|
+
<!-- 必须:将 elLocaleMessages 绑定到 Element Plus -->
|
|
859
|
+
<ZlI18nProvider>
|
|
860
|
+
<router-view />
|
|
861
|
+
</ZlI18nProvider>
|
|
862
|
+
</template>
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
`ZlI18nProvider` 在应用根(或子树根)包裹一次即可。切换 `locale` 会触发 `useLocale` 重新加载业务包与 EP 语言包;勾选 `isI18nKeyVisible` 后所有 `t()` 直接显示 key,便于排查漏翻(playground 已在侧边栏内置「中 / EN」切换与「显示 key」开关)。
|
|
866
|
+
|
|
867
|
+
### 13.4 组件内渐进式翻译 `useZlT`
|
|
868
|
+
|
|
869
|
+
组件作者用 `useZlT` 替代硬编码文案,在「宿主未装 i18n」时安全降级,绝不抛错:
|
|
870
|
+
|
|
871
|
+
```ts
|
|
872
|
+
import { useZlT } from '@zl-ui-kit/composables'
|
|
873
|
+
const { t } = useZlT()
|
|
874
|
+
// 用户可见文案
|
|
875
|
+
const text = t('zl.empty.noData', '暂无相关数据')
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
- 宿主已 `app.use(i18n)`:委托 vue-i18n 全局 `t` 翻译;未命中 key 时回退到 fallback。
|
|
879
|
+
- 宿主未接入 i18n:直接返回 fallback(第二个参数)/ key,不影响功能。
|
|
880
|
+
|
|
881
|
+
> **语言包 key 规范**:`zl.<组件>.<语义>`,例如 `zl.empty.noData`、`zl.login.username`。新增文案请同步补充 zh / en 两份语言包。
|
|
882
|
+
|
|
883
|
+
### 13.5 useLocale 注册修复说明
|
|
884
|
+
|
|
885
|
+
`useLocale` 的 `watch(localeMessages)` 原实现强依赖 `messages.locale` 字段,导致标准「按 locale 分桶」结构(`{ zh:{...}, en:{...} }`)整体被跳过、动态语言包无法注册。现兼容两种形态:**分桶**(推荐)/ **带 `locale` 字段**;组件级 `options.messages` 现整体写入(不再只取首个 locale)。
|
|
886
|
+
|
|
887
|
+
### 13.6 消费者定制 i18n(与主题对齐)
|
|
888
|
+
|
|
889
|
+
主题让消费者「定义并覆盖视觉」,国际化让消费者「定义并覆盖文案」——二者地位对等:
|
|
890
|
+
|
|
891
|
+
| 能力 | 主题(`@zl-ui-kit/theme`) | 国际化(`@zl-ui-kit/components` + `@zl-ui-kit/composables`) |
|
|
892
|
+
| --- | --- | --- |
|
|
893
|
+
| 内置默认 | 5 套主题(light / dark / red-light / red-dark / blue) | `zlMessages`(zh / zh-cn / en) |
|
|
894
|
+
| 运行时定义 | `defineTheme({ name, base, colors, tokens })` | `createZlI18n({ messages })` 覆盖 / 新增语言 |
|
|
895
|
+
| 引用 / 切换 | `setTheme('dark')` / `useTheme()` | `locale.value = 'en'` / `useLocale()` 返回的 `locale` |
|
|
896
|
+
| 持久化 | `enableThemePersistence()` | `useMicroGlobalData().locale` 经 `$zl_locale` 持久化 |
|
|
897
|
+
| 细粒度覆盖 | CSS 变量 L1 覆写(`--zl-primary-6`) | `mergeMessages` / 直接覆盖单个 key |
|
|
898
|
+
| 挂载点 | `body[data-theme]` | `<ZlI18nProvider>`(EP locale)+ `app.use(i18n)`(业务文案) |
|
|
899
|
+
|
|
900
|
+
**三种覆盖方式(按力度递增)**
|
|
901
|
+
|
|
902
|
+
1. **单 key 覆盖(最常用)** —— `createZlI18n({ messages: { zh: { 'zl.drawer.confirm': '好的' } } })`
|
|
903
|
+
2. **整包替换** —— `createZlI18n({ messages: myFullPacks, includeZlMessages: false })`
|
|
904
|
+
3. **手工合并** —— 直接用 `mergeMessages(zlMessages, myOverrides)` 得到最终包,再交给 `useLocale`
|
|
905
|
+
|
|
906
|
+
```ts
|
|
907
|
+
// 方式 3:完全掌控合并结果
|
|
908
|
+
import { zlMessages } from '@zl-ui-kit/components'
|
|
909
|
+
import { mergeMessages, useLocale } from '@zl-ui-kit/composables'
|
|
910
|
+
|
|
911
|
+
const messages = mergeMessages(zlMessages, {
|
|
912
|
+
zh: { 'zl.upload.nameSpace': '文件名不能有空格' },
|
|
913
|
+
})
|
|
914
|
+
const { i18n } = useLocale({ messages })
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
**内置可覆盖 key 一览**(`zl.<组件>.<语义>`)
|
|
918
|
+
|
|
919
|
+
| 组件 | key | 默认(zh) |
|
|
920
|
+
| --- | --- | --- |
|
|
921
|
+
| ZlEmpty | `zl.empty.noData` | 暂无相关数据 |
|
|
922
|
+
| ZlDrawer | `zl.drawer.cancel` / `confirm` / `close` | 取消 / 确定 / 关闭 |
|
|
923
|
+
| ZlPopconfirm | `zl.popconfirm.confirm` / `cancel` | 确定 / 取消 |
|
|
924
|
+
| ZlLayoutSwitch | `zl.layoutSwitch.grid` / `table` | 网格 / 表格 |
|
|
925
|
+
| ZlUpload | `zl.upload.accept` / `limit` / `maxSize` / `nameSpace` | 仅支持{accept}格式的文件 / 最多可上传{limit}个 / 大小不超过{maxSize}MB / 文件名中不得包含空格 |
|
|
926
|
+
| ZlSearch | `zl.search.search` / `filterConditions` / `all` | 查询 / 筛选条件 / 全部 |
|
|
927
|
+
| ZlLogin | `zl.login.username` / `password` / `usernamePlaceholder` / `passwordPlaceholder` / `slideRequired` / `slideText` / `slideSuccess` / `submit` / `welcome` | 用户名 / 密码 / 请输入用户名 / 请输入密码 / 请将滑块移至右侧 / 请滑动到右侧 / 验证通过 / 登 录 / 欢迎登录 |
|
|
928
|
+
| ZlFilePreview | `zl.filePreview.typeError` / `fetchError` | 获取文件类型失败 / 文件获取失败 |
|
|
929
|
+
| ZlAction | `zl.action.more` | 更多 |
|
|
930
|
+
| ZlCheckAll | `zl.checkAll.selectAll` | 全选 |
|
|
931
|
+
| ZlCopy | `zl.copy.copy` / `success` / `failed` | 复制 / 复制成功 / 复制失败 |
|
|
932
|
+
| ZlDescription | `zl.description.empty` | 暂无 |
|
|
933
|
+
| ZlGrid | `zl.grid.empty` | 暂无 |
|
|
934
|
+
| ZlSqlEditor | `zl.sqlEditor.invalid` | 请检查SQL语法 |
|
|
66
935
|
|
|
67
|
-
|
|
936
|
+
> **props 优先于 i18n**:组件显式传入的 props(如 `ZlPopconfirm` 的 `confirmButtonText`、`ZlDescription`/`ZlGrid` 的 `emptyText`)优先;i18n 只决定「未传时的默认文案」。新增文案请同步补 `packages/components/src/locales/{zh-cn,en}.ts`。
|
|
68
937
|
|
|
69
|
-
|
|
938
|
+
> **当前未接入 i18n 的中文**:仅剩开发态告警(`ZlDynamicComponent` 的 `console.warn`、`ZlPermission` 的 `throw new Error('权限格式错误')`、`ZlEditor` 的 worker 未配置提示)——这些不面向终端用户,保持中文以便排查。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zl-ui-kit/components",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ZL UI 业务组件库(Element Plus 体系,Zl 前缀)——首批:Icon/AutoTooltip/Tag/CornerTag/Text/Title/Copy/Empty/Pagination/DictTag",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"file-type": "^20.4.1",
|
|
40
40
|
"dt-sql-parser": "^4.3.1",
|
|
41
41
|
"sql-formatter": "^15.4.2",
|
|
42
|
-
"@zl-ui-kit/composables": "0.3.
|
|
43
|
-
"@zl-ui-kit/utils": "0.3.
|
|
44
|
-
"@zl-ui-kit/icons": "0.3.
|
|
42
|
+
"@zl-ui-kit/composables": "0.3.3",
|
|
43
|
+
"@zl-ui-kit/utils": "0.3.3",
|
|
44
|
+
"@zl-ui-kit/icons": "0.3.3"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@vueuse/core": "^12.3.0",
|
|
@@ -98,10 +98,10 @@
|
|
|
98
98
|
"@vue-office/pdf": "^2.0.10",
|
|
99
99
|
"@vue-office/pptx": "1.0.1",
|
|
100
100
|
"echarts": "^5.6.0",
|
|
101
|
-
"@jichuang/tsconfig": "1.0.0",
|
|
102
|
-
"@zl-ui-kit/theme": "0.3.1",
|
|
103
101
|
"@jichuang/vite-config": "1.0.0",
|
|
104
|
-
"@jichuang/
|
|
102
|
+
"@jichuang/tsconfig": "1.0.0",
|
|
103
|
+
"@jichuang/eslint-config": "1.0.0",
|
|
104
|
+
"@zl-ui-kit/theme": "0.3.3"
|
|
105
105
|
},
|
|
106
106
|
"repository": {
|
|
107
107
|
"type": "git",
|