@kordar/easyui-tpl 2.0.4 → 2.0.6
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 +1089 -29
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/dist/index.umd.cjs +1 -1
- package/dist/index.umd.cjs.map +1 -1
- package/dist/public/zh_CN.json +3 -0
- package/dist/types/components/RememberMeBox.d.ts +8 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/service/ResourceService.d.ts +2 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,56 +1,1116 @@
|
|
|
1
1
|
# @kordar/easyui-tpl
|
|
2
2
|
|
|
3
|
-
基于 rc-easyui
|
|
3
|
+
基于 `rc-easyui`、`@kordar/easyui` 与 `@kordar-lib/*` 生态构建的后台模板工程。项目内置布局、菜单/标签联动、权限校验、国际化、后台资源管理页面示例,可直接作为 React 管理后台项目的起步模板,也适合作为二次封装 EasyUI 业务组件的参考工程。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
5
|
+
<a id="toc"></a>
|
|
6
|
+
## 目录
|
|
7
|
+
|
|
8
|
+
- [项目定位与特性](#project-overview)
|
|
9
|
+
- [快速开始](#quick-start)
|
|
10
|
+
- [项目结构总览](#project-structure)
|
|
11
|
+
- [初始化与运行机制](#bootstrap)
|
|
12
|
+
- [导出模块清单](#exports)
|
|
13
|
+
- [核心组件模块说明与使用方法](#core-modules)
|
|
14
|
+
- [业务页面模块说明](#view-modules)
|
|
15
|
+
- [组件模块扩展开发指南](#extension-guide)
|
|
16
|
+
- [样式、国际化与主题说明](#style-i18n-theme)
|
|
17
|
+
- [本地验证与文档自检](#verification)
|
|
18
|
+
- [许可证](#license)
|
|
19
|
+
|
|
20
|
+
<a id="project-overview"></a>
|
|
21
|
+
## 项目定位与特性
|
|
22
|
+
|
|
23
|
+
本模板聚焦“后台基础壳 + 通用业务页 + EasyUI 交互封装”三类能力:
|
|
24
|
+
|
|
25
|
+
- 布局能力:`BaseLayout` 提供头部工具区、侧边栏、标签页、内容区联动
|
|
26
|
+
- 列表与表单:`useLocalTable`、`useTable`、`PaginationPanel`、通用表单与表格工具函数
|
|
27
|
+
- 权限体系:基于 `@kordar-lib/base` 与 reducers 的权限校验、按钮显隐、页面跳转保护
|
|
28
|
+
- 国际化:`@kordar-lib/i18n` 集成,内置 `en` / `zh_CN` 示例语言包
|
|
29
|
+
- 主题切换:通过 `SettingBarItem` 与 `@kordar/easyui` 的 `useTheme()` 打通本地主题切换
|
|
30
|
+
- 示例页面:提供用户、角色、权限、字典、路由、设置、登录、首页、大屏设计等后台页面
|
|
31
|
+
|
|
32
|
+
适用场景:
|
|
33
|
+
|
|
34
|
+
- 快速搭建基于 `rc-easyui` 的 React 管理后台
|
|
35
|
+
- 沉淀企业后台“布局 + 表格 + 权限 + 国际化”的统一工程模板
|
|
36
|
+
- 作为二次封装 `@kordar/easyui` 业务组件的宿主工程
|
|
37
|
+
|
|
38
|
+
<a id="quick-start"></a>
|
|
39
|
+
## 快速开始
|
|
40
|
+
|
|
41
|
+
### 安装
|
|
11
42
|
|
|
12
|
-
## 安装
|
|
13
43
|
```bash
|
|
14
44
|
npm install
|
|
15
45
|
```
|
|
16
46
|
|
|
17
|
-
|
|
47
|
+
### 本地开发
|
|
48
|
+
|
|
18
49
|
```bash
|
|
19
50
|
npm run -w @kordar/easyui-tpl dev
|
|
20
51
|
```
|
|
21
|
-
默认入口:/examples/main.tsx(Vite)
|
|
22
52
|
|
|
23
|
-
|
|
53
|
+
默认 Vite 入口为 [`examples/main.tsx`](./examples/main.tsx)。
|
|
54
|
+
|
|
55
|
+
### 构建
|
|
56
|
+
|
|
24
57
|
```bash
|
|
25
58
|
npm run -w @kordar/easyui-tpl build
|
|
26
59
|
```
|
|
27
|
-
产物输出到 dist(包含 ESM、CJS 与 index.min.css)。
|
|
28
60
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
61
|
+
构建产物输出到 `dist`,包含:
|
|
62
|
+
|
|
63
|
+
- ESM:`dist/index.js`
|
|
64
|
+
- CJS:`dist/index.umd.cjs`
|
|
65
|
+
- 样式:`dist/index.min.css`
|
|
66
|
+
|
|
67
|
+
### 最小接入示例
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import React from 'react'
|
|
71
|
+
import ReactDOM from 'react-dom/client'
|
|
72
|
+
import { BrowserRouter } from 'react-router-dom'
|
|
73
|
+
import { Provider } from 'react-redux'
|
|
74
|
+
import { PersistGate } from 'redux-persist/integration/react'
|
|
75
|
+
|
|
76
|
+
import App from './App'
|
|
77
|
+
import './init'
|
|
78
|
+
import './example.scss'
|
|
79
|
+
import { persistor, store } from './store'
|
|
80
|
+
|
|
81
|
+
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
|
82
|
+
<Provider store={store}>
|
|
83
|
+
<PersistGate loading={null} persistor={persistor}>
|
|
84
|
+
<BrowserRouter>
|
|
85
|
+
<App />
|
|
86
|
+
</BrowserRouter>
|
|
87
|
+
</PersistGate>
|
|
88
|
+
</Provider>
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
<a id="project-structure"></a>
|
|
93
|
+
## 项目结构总览
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
easyui-tpl/
|
|
97
|
+
├── src/
|
|
98
|
+
│ ├── components/ # 基础组件与工具条组件
|
|
99
|
+
│ ├── composable/ # 页面启动、表格、菜单与刷新 Hook
|
|
100
|
+
│ ├── hoc/ # 文档/配置增强 HOC
|
|
101
|
+
│ ├── service/ # Admin/Rbac/Resource/Auth/Config 服务层
|
|
102
|
+
│ ├── util/ # 表单、表格、懒加载、辅助函数
|
|
103
|
+
│ ├── views/ # 后台业务页面模块
|
|
104
|
+
│ ├── init.ts # request / api 注册入口
|
|
105
|
+
│ └── index.ts # 对外导出入口
|
|
106
|
+
├── examples/ # 可运行示例工程(入口、路由、Store、Mock)
|
|
107
|
+
├── public/ # i18n 语言包
|
|
108
|
+
├── README.md
|
|
109
|
+
└── vite.config.ts
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
目录职责说明:
|
|
113
|
+
|
|
114
|
+
- [`src/components`](./src/components):布局、语言切换、主题切换、分页、提示、用户菜单等
|
|
115
|
+
- [`src/composable`](./src/composable):应用启动、菜单与标签联动、本地/远程表格、路由跳转、刷新控制等
|
|
116
|
+
- [`src/service`](./src/service):统一封装资源接口、认证接口、配置接口
|
|
117
|
+
- [`src/views`](./src/views):开箱即用的后台页面示例
|
|
118
|
+
- [`examples`](./examples):本地演示应用与调试入口
|
|
119
|
+
|
|
120
|
+
<a id="bootstrap"></a>
|
|
121
|
+
## 初始化与运行机制
|
|
122
|
+
|
|
123
|
+
### 1. 安装 request 与 API 映射
|
|
124
|
+
|
|
125
|
+
[`examples/init.ts`](./examples/init.ts) 展示了语言包、EasyUI locale 包、接口地址与 request 实例的初始化方式:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { install } from '../src'
|
|
129
|
+
import { request } from './util/http'
|
|
130
|
+
|
|
131
|
+
install({
|
|
132
|
+
request,
|
|
133
|
+
apis: {
|
|
134
|
+
menus: 'menus',
|
|
135
|
+
loginSubmit: 'login/submit',
|
|
136
|
+
adminInfo: 'resource/admin/info',
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 2. 应用启动
|
|
142
|
+
|
|
143
|
+
根组件通过 `useAppStarter()` 完成语言包同步与登录态重定向:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
import { LocaleProvider, Messager } from 'rc-easyui'
|
|
147
|
+
import { useAppStarter } from '../src'
|
|
148
|
+
import { alertMessagerRef, interactiveMessagerRef } from '@kordar/easyui'
|
|
149
|
+
|
|
150
|
+
function App() {
|
|
151
|
+
const { easyuiLangPkg } = useAppStarter()
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<LocaleProvider locale={easyuiLangPkg?.value}>
|
|
155
|
+
<>
|
|
156
|
+
<BaseRouter />
|
|
157
|
+
<Messager ref={alertMessagerRef} />
|
|
158
|
+
<Messager ref={interactiveMessagerRef} />
|
|
159
|
+
</>
|
|
160
|
+
</LocaleProvider>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 3. 页面渲染链路
|
|
166
|
+
|
|
167
|
+
典型页面渲染流程如下:
|
|
168
|
+
|
|
169
|
+
1. `install()` 注册 `request` 与 API 表
|
|
170
|
+
2. `useAppStarter()` 载入当前语言包并执行自动跳转
|
|
171
|
+
3. `BaseLayout` 读取菜单、固定欢迎页、权限列表
|
|
172
|
+
4. `useMenuAndTabData()` 维护侧栏和标签页联动
|
|
173
|
+
5. 业务页通过 `withDocHoc()` 注入 `pageConfig / fetchConfig`
|
|
174
|
+
6. 表格页通过 `useLocalTable()` 或 `useTable()` 管理分页、搜索、刷新与权限
|
|
175
|
+
|
|
176
|
+
<a id="exports"></a>
|
|
177
|
+
## 导出模块清单
|
|
178
|
+
|
|
179
|
+
[`src/index.ts`](./src/index.ts) 当前主要导出如下能力:
|
|
180
|
+
|
|
181
|
+
### 组件与 HOC
|
|
182
|
+
|
|
183
|
+
| 模块 | 导出名 | 说明 |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| 布局组件 | `BaseLayout` | 后台主布局壳 |
|
|
186
|
+
| 工具条组件 | `LocaleBarItem` | 语言切换菜单 |
|
|
187
|
+
| 工具条组件 | `PaginationPanel` | 分页栏桥接组件 |
|
|
188
|
+
| 工具条组件 | `SettingBarItem` | 主题切换入口 |
|
|
189
|
+
| 提示组件 | `Tooltip` | 文本溢出提示组件 |
|
|
190
|
+
| HOC | `withDocHoc` | 页面语言与后台配置注入 |
|
|
191
|
+
| Context | `AppLayoutContext` | 布局上下文 |
|
|
192
|
+
|
|
193
|
+
### Hook / 工具 / 服务 / 页面
|
|
194
|
+
|
|
195
|
+
| 分类 | 导出名 | 说明 |
|
|
196
|
+
| --- | --- | --- |
|
|
197
|
+
| Hook | `useMenuAndTabData` | 菜单与标签页联动 |
|
|
198
|
+
| Hook | `useAppStarter` | 应用启动初始化 |
|
|
199
|
+
| Hook | `useLocalTable` | 本地表格状态封装 |
|
|
200
|
+
| Hook | `useTable` | 远程表格状态封装 |
|
|
201
|
+
| Hook | `useAppRedirect` | 路由跳转保护 |
|
|
202
|
+
| 服务 | `resourceService` / `rbacService` / `adminService` / `configService` | 资源、权限、管理员、配置接口封装 |
|
|
203
|
+
| 页面 | `AdminView` / `RolesView` / `PermissionsView` / `DictView` 等 | 业务页面模块 |
|
|
204
|
+
|
|
205
|
+
<a id="core-modules"></a>
|
|
206
|
+
## 核心组件模块说明与使用方法
|
|
207
|
+
|
|
208
|
+
本章只说明模板工程内部已经封装完成、并能直接在项目中复用的核心模块。所有示例均基于当前仓库真实源码组织,并优先沿用 [`examples`](./examples) 中的使用方式。
|
|
209
|
+
|
|
210
|
+
### BaseLayout
|
|
211
|
+
|
|
212
|
+
**源码位置**
|
|
213
|
+
|
|
214
|
+
- [`src/components/BaseLayout.tsx`](./src/components/BaseLayout.tsx)
|
|
215
|
+
|
|
216
|
+
**功能定位**
|
|
217
|
+
|
|
218
|
+
- 提供后台统一骨架:头部栏、侧边栏、标签页、主内容区、页脚
|
|
219
|
+
- 负责菜单树加载、标签页同步、当前路由切换与权限加载
|
|
220
|
+
- 适合作为整个后台应用的根布局组件
|
|
221
|
+
|
|
222
|
+
**适用场景**
|
|
223
|
+
|
|
224
|
+
- 多菜单、多标签页的管理后台
|
|
225
|
+
- 需要统一接入用户栏、语言切换、主题切换的项目
|
|
226
|
+
- 需要把业务页面通过路由 `Outlet` 渲染到主工作区的工程
|
|
227
|
+
|
|
228
|
+
**基础参数**
|
|
229
|
+
|
|
230
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
231
|
+
| --- | --- | --- | --- |
|
|
232
|
+
| `footer` | `React.ReactNode` | `undefined` | 传入页脚区域内容 |
|
|
233
|
+
| `...props` | `any` | `-` | 其余属性原样透传给 `@kordar/easyui` 的 `FullBodyLayout` |
|
|
234
|
+
|
|
235
|
+
固定内部配置:
|
|
236
|
+
|
|
237
|
+
- `tabsCfg = { border: false, plain: true, scrollable: true, narrow: false }`
|
|
238
|
+
- `sideCfg = { border: false, animate: true, multiple: true, showCollapsedText: false }`
|
|
239
|
+
|
|
240
|
+
**基础调用**
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { BaseLayout } from '@kordar/easyui-tpl'
|
|
244
|
+
|
|
245
|
+
export default function AppLayout() {
|
|
246
|
+
return (
|
|
247
|
+
<BaseLayout footer={<span>Powered by Kordar</span>}>
|
|
248
|
+
{/* BaseLayout 内部会自行渲染 TabsPanel 与 <Outlet /> */}
|
|
249
|
+
</BaseLayout>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**进阶调用**
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
import { Layout } from 'antd'
|
|
258
|
+
import { TabsPanel } from '@kordar/easyui'
|
|
259
|
+
import { BaseLayout } from '@kordar/easyui-tpl'
|
|
260
|
+
|
|
261
|
+
export default function CustomLayout() {
|
|
262
|
+
return (
|
|
263
|
+
<BaseLayout footer={<span>Footer Area</span>} className="app-layout">
|
|
264
|
+
<Layout style={{ padding: '4px', height: 48 }}>
|
|
265
|
+
<TabsPanel data={[]} tabsCfg={{ scrollable: true }} />
|
|
266
|
+
</Layout>
|
|
267
|
+
<Layout style={{ padding: 12, height: '100%' }}>
|
|
268
|
+
<div>这里放自定义业务面板</div>
|
|
269
|
+
</Layout>
|
|
270
|
+
</BaseLayout>
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**事件列表**
|
|
276
|
+
|
|
277
|
+
| 事件 / 回调 | 触发时机 | 回调参数 | 说明 |
|
|
278
|
+
| --- | --- | --- | --- |
|
|
279
|
+
| `toggle(value)` | 点击折叠/展开侧边栏 | `value: boolean` | 由内部调用 `setSidebarCollapsed()` 更新状态 |
|
|
280
|
+
| `sidebarCfg.onItemClick(selection)` | 点击侧栏菜单项 | `selection: SideBarItem` | 内部映射到 `onMenuSelection()` |
|
|
281
|
+
|
|
282
|
+
说明:`BaseLayout` 本身没有额外向外暴露 React 事件 props,但会透传底层 `FullBodyLayout` 的能力。
|
|
283
|
+
|
|
284
|
+
**实例方法**
|
|
285
|
+
|
|
286
|
+
- 无额外对外 `ref` 方法
|
|
287
|
+
|
|
288
|
+
**预览区域**
|
|
289
|
+
|
|
290
|
+
> 建议运行 `npm run dev` 后进入示例应用,重点观察:
|
|
291
|
+
> 1. 左侧菜单切换
|
|
292
|
+
> 2. 顶部 `User / Locale / Theme` 工具条
|
|
293
|
+
> 3. 标签页关闭与当前路由联动
|
|
294
|
+
|
|
295
|
+
### LocaleBarItem
|
|
296
|
+
|
|
297
|
+
**源码位置**
|
|
298
|
+
|
|
299
|
+
- [`src/components/LocaleBarItem.tsx`](./src/components/LocaleBarItem.tsx)
|
|
300
|
+
|
|
301
|
+
**功能定位**
|
|
302
|
+
|
|
303
|
+
- 提供头部语言切换菜单
|
|
304
|
+
- 基于 `@kordar/easyui` 的 `useLocale(true)` 切换当前语言,并刷新页面资源
|
|
305
|
+
|
|
306
|
+
**适用场景**
|
|
307
|
+
|
|
308
|
+
- 中英文后台
|
|
309
|
+
- 需要把 `rc-easyui` locale 与业务语言包同步切换的工程
|
|
310
|
+
|
|
311
|
+
**基础参数**
|
|
312
|
+
|
|
313
|
+
该组件当前不接收额外 props。
|
|
314
|
+
|
|
315
|
+
**基础调用**
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
import { LocaleBarItem } from '@kordar/easyui-tpl'
|
|
319
|
+
|
|
320
|
+
export default function HeaderRight() {
|
|
321
|
+
return <LocaleBarItem />
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**进阶调用**
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import { BaseLayout, LocaleBarItem } from '@kordar/easyui-tpl'
|
|
329
|
+
|
|
330
|
+
export default function LayoutWithLocale() {
|
|
331
|
+
return (
|
|
332
|
+
<BaseLayout
|
|
333
|
+
slots={{
|
|
334
|
+
barRight: <LocaleBarItem />,
|
|
335
|
+
}}
|
|
336
|
+
/>
|
|
337
|
+
)
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**事件列表**
|
|
342
|
+
|
|
343
|
+
| 事件 / 回调 | 触发时机 | 回调参数 | 说明 |
|
|
344
|
+
| --- | --- | --- | --- |
|
|
345
|
+
| `onMenuItemClick` | 选择语言项时 | `key: string` | 内部绑定到 `changeLocale()` |
|
|
346
|
+
|
|
347
|
+
**实例方法**
|
|
348
|
+
|
|
349
|
+
- 无额外实例方法
|
|
350
|
+
|
|
351
|
+
**预览区域**
|
|
352
|
+
|
|
353
|
+
> 运行示例后,点击头部语言图标,验证 `MenuButton` 展开、语言切换以及文案刷新效果。
|
|
354
|
+
|
|
355
|
+
### SettingBarItem
|
|
356
|
+
|
|
357
|
+
**源码位置**
|
|
358
|
+
|
|
359
|
+
- [`src/components/SettingBarItem.tsx`](./src/components/SettingBarItem.tsx)
|
|
360
|
+
|
|
361
|
+
**功能定位**
|
|
362
|
+
|
|
363
|
+
- 提供主题切换抽屉入口
|
|
364
|
+
- 使用 `DrawerPanel + Panel + RadioButton` 构建主题配置面板
|
|
365
|
+
|
|
366
|
+
**适用场景**
|
|
367
|
+
|
|
368
|
+
- 需要支持多主题后台
|
|
369
|
+
- 希望把主题切换能力统一放在顶部工具区
|
|
370
|
+
|
|
371
|
+
**基础参数**
|
|
372
|
+
|
|
373
|
+
该组件当前不接收额外 props。
|
|
374
|
+
|
|
375
|
+
**基础调用**
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
import { SettingBarItem } from '@kordar/easyui-tpl'
|
|
379
|
+
|
|
380
|
+
export default function HeaderTheme() {
|
|
381
|
+
return <SettingBarItem />
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**进阶调用**
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
import { SettingBarItem } from '@kordar/easyui-tpl'
|
|
389
|
+
|
|
390
|
+
function RightBar() {
|
|
391
|
+
return (
|
|
392
|
+
<>
|
|
393
|
+
<SettingBarItem />
|
|
394
|
+
</>
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**事件列表**
|
|
400
|
+
|
|
401
|
+
| 事件 / 回调 | 触发时机 | 回调参数 | 说明 |
|
|
402
|
+
| --- | --- | --- | --- |
|
|
403
|
+
| `onClick` | 点击调色板按钮时 | `MouseEvent` | 内部调用 `ref.current.toggle()` 打开抽屉 |
|
|
404
|
+
| `onChange` | 主题单选框切换时 | `checked: boolean` | 内部触发 `changeTheme(value)` |
|
|
405
|
+
|
|
406
|
+
**实例方法**
|
|
407
|
+
|
|
408
|
+
- 对外无 `ref` 方法
|
|
409
|
+
- 内部依赖 `DrawerPanel` 的 `toggle()` 方法控制开关
|
|
410
|
+
|
|
411
|
+
**预览区域**
|
|
412
|
+
|
|
413
|
+
> 建议截取“主题抽屉展开 + 多个 RadioButton 主题项”的界面作为组件截图。
|
|
414
|
+
|
|
415
|
+
### PaginationPanel
|
|
416
|
+
|
|
417
|
+
**源码位置**
|
|
418
|
+
|
|
419
|
+
- [`src/components/PaginationPanel.tsx`](./src/components/PaginationPanel.tsx)
|
|
420
|
+
- [`src/util/interface.ts`](./src/util/interface.ts)
|
|
421
|
+
|
|
422
|
+
**功能定位**
|
|
423
|
+
|
|
424
|
+
- 把表格上下文中的分页状态桥接到 `rc-easyui/Pagination`
|
|
425
|
+
- 统一分页栏配置,降低各页面重复样板代码
|
|
426
|
+
|
|
427
|
+
**适用场景**
|
|
428
|
+
|
|
429
|
+
- 所有基于 `TableContext` 的列表页
|
|
430
|
+
- 使用 `useLocalTable()` 或 `useTable()` 的分页列表页面
|
|
431
|
+
|
|
432
|
+
**基础参数**
|
|
433
|
+
|
|
434
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
435
|
+
| --- | --- | --- | --- |
|
|
436
|
+
| `ctx` | `TableContext` | 必填 | 需要提供 `pageChange(pageNumber, pageSize)` 方法 |
|
|
437
|
+
| `ctxState` | `TableContextState` | 必填 | 分页状态容器,提供 `total/pageNumber/pageSize/layout/pageSizes` |
|
|
438
|
+
|
|
439
|
+
**基础调用**
|
|
440
|
+
|
|
441
|
+
```tsx
|
|
442
|
+
import { PaginationPanel } from '@kordar/easyui-tpl'
|
|
443
|
+
|
|
444
|
+
function PageFooter({ ctx, ctxState }: any) {
|
|
445
|
+
return <PaginationPanel ctx={ctx} ctxState={ctxState} />
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**进阶调用**
|
|
450
|
+
|
|
451
|
+
```tsx
|
|
452
|
+
import { PaginationPanel, useTable } from '@kordar/easyui-tpl'
|
|
453
|
+
|
|
454
|
+
function UserTablePage() {
|
|
455
|
+
const [ctx, ctxState] = useTable({ defaultPageSize: 20 }, '')
|
|
456
|
+
|
|
457
|
+
return (
|
|
458
|
+
<>
|
|
459
|
+
<div style={{ minHeight: 320 }}>表格区域</div>
|
|
460
|
+
<PaginationPanel ctx={ctx} ctxState={ctxState} />
|
|
461
|
+
</>
|
|
462
|
+
)
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**事件列表**
|
|
467
|
+
|
|
468
|
+
| 事件 / 回调 | 触发时机 | 回调参数 | 说明 |
|
|
469
|
+
| --- | --- | --- | --- |
|
|
470
|
+
| `ctx.pageChange(pageNumber, pageSize)` | 分页变更时 | `pageNumber: number, pageSize: number` | 由 `Pagination.onPageChange` 转发 |
|
|
471
|
+
|
|
472
|
+
**实例方法**
|
|
473
|
+
|
|
474
|
+
- 无额外实例方法
|
|
475
|
+
|
|
476
|
+
**预览区域**
|
|
477
|
+
|
|
478
|
+
> 建议在列表页底部截图分页条,重点保留页码、总数、刷新按钮区域。
|
|
479
|
+
|
|
480
|
+
### Tooltip
|
|
481
|
+
|
|
482
|
+
**源码位置**
|
|
483
|
+
|
|
484
|
+
- [`src/components/Tooltip.tsx`](./src/components/Tooltip.tsx)
|
|
485
|
+
|
|
486
|
+
**功能定位**
|
|
487
|
+
|
|
488
|
+
- 为溢出文本提供轻量级提示层
|
|
489
|
+
- 支持 hover / click / focus 三种触发方式
|
|
490
|
+
- 支持自动翻转、跟随鼠标、深浅主题切换
|
|
491
|
+
|
|
492
|
+
**适用场景**
|
|
493
|
+
|
|
494
|
+
- 表格超长字段
|
|
495
|
+
- 面包屑或标题溢出展示
|
|
496
|
+
- 需要轻量级 tooltip 且不依赖第三方重型提示组件的场景
|
|
497
|
+
|
|
498
|
+
**基础参数**
|
|
499
|
+
|
|
500
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
501
|
+
| --- | --- | --- | --- |
|
|
502
|
+
| `text` | `ReactNode` | 必填 | Tooltip 展示内容 |
|
|
503
|
+
| `children` | `ReactNode` | 必填 | 被包裹的触发节点 |
|
|
504
|
+
| `position` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | 提示层方向 |
|
|
505
|
+
| `trigger` | `'hover' \| 'click' \| 'focus'` | `'hover'` | 触发方式 |
|
|
506
|
+
| `delay` | `number` | `50` | 延迟显示时间,单位 ms |
|
|
507
|
+
| `maxWidth` | `number` | `250` | 最大宽度 |
|
|
508
|
+
| `theme` | `'dark' \| 'light'` | `'dark'` | 样式主题 |
|
|
509
|
+
| `followMouse` | `boolean` | `false` | 是否跟随鼠标 |
|
|
510
|
+
| `autoFlip` | `boolean` | `true` | 空间不足时是否自动翻转方向 |
|
|
511
|
+
|
|
512
|
+
**基础调用**
|
|
513
|
+
|
|
514
|
+
```tsx
|
|
515
|
+
import { Tooltip } from '@kordar/easyui-tpl'
|
|
516
|
+
|
|
517
|
+
export default function NameCell() {
|
|
518
|
+
return (
|
|
519
|
+
<Tooltip text="这是完整内容" position="bottom">
|
|
520
|
+
<span style={{ display: 'inline-block', width: 120, overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
|
521
|
+
这是完整内容
|
|
522
|
+
</span>
|
|
523
|
+
</Tooltip>
|
|
524
|
+
)
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**进阶调用**
|
|
529
|
+
|
|
530
|
+
```tsx
|
|
531
|
+
import { Tooltip } from '@kordar/easyui-tpl'
|
|
532
|
+
|
|
533
|
+
export default function HoverCard() {
|
|
534
|
+
return (
|
|
535
|
+
<Tooltip
|
|
536
|
+
text={<div>可跟随鼠标展示更长的提示内容</div>}
|
|
537
|
+
trigger="hover"
|
|
538
|
+
position="right"
|
|
539
|
+
followMouse
|
|
540
|
+
theme="light"
|
|
541
|
+
maxWidth={320}
|
|
542
|
+
>
|
|
543
|
+
<div style={{ width: 180 }}>将鼠标移动到这里</div>
|
|
544
|
+
</Tooltip>
|
|
545
|
+
)
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**事件列表**
|
|
550
|
+
|
|
551
|
+
该组件不对外暴露额外回调 props,内部通过以下 DOM 事件驱动显示逻辑:
|
|
552
|
+
|
|
553
|
+
- `onMouseEnter / onMouseLeave`
|
|
554
|
+
- `onMouseMove`
|
|
555
|
+
- `onClick`
|
|
556
|
+
- `onFocus / onBlur`
|
|
557
|
+
|
|
558
|
+
**实例方法**
|
|
559
|
+
|
|
560
|
+
- 无额外实例方法
|
|
561
|
+
|
|
562
|
+
**预览区域**
|
|
563
|
+
|
|
564
|
+
> 建议在表格超长文本场景截图,展示默认隐藏、悬停后浮层出现的前后对比效果。
|
|
565
|
+
|
|
566
|
+
### withDocHoc
|
|
567
|
+
|
|
568
|
+
**源码位置**
|
|
569
|
+
|
|
570
|
+
- [`src/hoc/withDocHoc.tsx`](./src/hoc/withDocHoc.tsx)
|
|
571
|
+
|
|
572
|
+
**功能定位**
|
|
573
|
+
|
|
574
|
+
- 为业务页面统一注入:
|
|
575
|
+
- `pageConfig`:当前页面对应语言包
|
|
576
|
+
- `fetchConfig`:页面初始化配置数据
|
|
577
|
+
- `fetchVersion`:配置重新拉取后的版本号
|
|
578
|
+
- 自动同步 `document.title`
|
|
579
|
+
|
|
580
|
+
**适用场景**
|
|
581
|
+
|
|
582
|
+
- 页面需要按语言包动态生成标题、按钮文案、表格列名
|
|
583
|
+
- 页面需要在加载时请求配置项,如字典、枚举、状态列表
|
|
584
|
+
|
|
585
|
+
**基础参数**
|
|
586
|
+
|
|
587
|
+
`withDocHoc()` 接收一个 `DocHocProps` 配置对象:
|
|
588
|
+
|
|
589
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
590
|
+
| --- | --- | --- | --- |
|
|
591
|
+
| `name` | `string` | 必填 | 页面名称,对应语言包 `Page/<name>` |
|
|
592
|
+
| `fetchConfig` | `() => Promise<any>` | `undefined` | 页面初始化配置请求函数 |
|
|
593
|
+
|
|
594
|
+
**基础调用**
|
|
595
|
+
|
|
596
|
+
```tsx
|
|
597
|
+
import { withDocHoc } from '@kordar/easyui-tpl'
|
|
598
|
+
|
|
599
|
+
const RolesPage = ({ pageConfig, fetchConfig }: any) => {
|
|
600
|
+
return <div>{pageConfig.title}</div>
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export default withDocHoc({ name: 'Roles' })(RolesPage)
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**进阶调用**
|
|
607
|
+
|
|
608
|
+
```tsx
|
|
609
|
+
import { resourceService, withDocHoc } from '@kordar/easyui-tpl'
|
|
610
|
+
|
|
611
|
+
const RouterPage = ({ pageConfig, fetchConfig, fetchVersion }: any) => {
|
|
612
|
+
return <div>{fetchVersion}: {pageConfig.title}</div>
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export default withDocHoc({
|
|
616
|
+
name: 'Router',
|
|
617
|
+
fetchConfig: () => resourceService.configs('router'),
|
|
618
|
+
})(RouterPage)
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
**注入属性**
|
|
622
|
+
|
|
623
|
+
| 属性 | 类型 | 说明 |
|
|
624
|
+
| --- | --- | --- |
|
|
625
|
+
| `pageConfig` | `Record<string, any>` | 当前页面语言包配置 |
|
|
626
|
+
| `fetchConfig` | `Record<string, any>` | 后台返回的配置数据 |
|
|
627
|
+
| `fetchVersion` | `number` | 配置版本号,适合触发下游重渲染 |
|
|
628
|
+
|
|
629
|
+
**事件列表**
|
|
630
|
+
|
|
631
|
+
- 无额外对外事件
|
|
632
|
+
|
|
633
|
+
**实例方法**
|
|
634
|
+
|
|
635
|
+
- 无额外实例方法
|
|
636
|
+
|
|
637
|
+
### AppLayoutContext
|
|
638
|
+
|
|
639
|
+
**源码位置**
|
|
640
|
+
|
|
641
|
+
- [`src/components/LayoutContext.ts`](./src/components/LayoutContext.ts)
|
|
642
|
+
|
|
643
|
+
**功能定位**
|
|
644
|
+
|
|
645
|
+
- 用于在布局层向下传递 `dispatch`、`useAppSelector` 和 `location` 等上下文能力
|
|
646
|
+
- 主要服务于布局区内部或扩展组件读取全局状态
|
|
647
|
+
|
|
648
|
+
**上下文结构**
|
|
649
|
+
|
|
650
|
+
| 字段 | 类型 | 说明 |
|
|
651
|
+
| --- | --- | --- |
|
|
652
|
+
| `dispatch` | `AppDispatch` | Redux dispatch |
|
|
653
|
+
| `useAppSelector` | `TypedUseSelectorHook<RootState>` | 类型化 selector |
|
|
654
|
+
| `location` | `Location` | 当前路由信息 |
|
|
655
|
+
|
|
656
|
+
**基础调用**
|
|
657
|
+
|
|
658
|
+
```tsx
|
|
659
|
+
import { useContext } from 'react'
|
|
660
|
+
import { AppLayoutContext } from '@kordar/easyui-tpl'
|
|
661
|
+
|
|
662
|
+
export default function LayoutConsumer() {
|
|
663
|
+
const ctx = useContext(AppLayoutContext)
|
|
664
|
+
return <div>{String(!!ctx.dispatch)}</div>
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**事件 / 方法**
|
|
669
|
+
|
|
670
|
+
- 无对外事件
|
|
671
|
+
- 无实例方法
|
|
672
|
+
|
|
673
|
+
### UserBarItem(内部组件)
|
|
674
|
+
|
|
675
|
+
**源码位置**
|
|
676
|
+
|
|
677
|
+
- [`src/components/UserBarItem.tsx`](./src/components/UserBarItem.tsx)
|
|
678
|
+
|
|
679
|
+
**说明**
|
|
680
|
+
|
|
681
|
+
- 当前未在 [`src/index.ts`](./src/index.ts) 中导出
|
|
682
|
+
- 由 `BaseLayout` 内部使用,负责显示当前用户名与退出登录菜单
|
|
683
|
+
|
|
684
|
+
**行为特征**
|
|
685
|
+
|
|
686
|
+
- 读取 `state.user.info`
|
|
687
|
+
- 点击 `Exit` 后调用 `$confirm()`
|
|
688
|
+
- 退出时清理布局状态并跳转到 `/login`
|
|
689
|
+
|
|
690
|
+
如需对外复用,建议先在 `src/index.ts` 中显式导出,并补充 props 类型定义。
|
|
691
|
+
|
|
692
|
+
### useAppStarter
|
|
693
|
+
|
|
694
|
+
**源码位置**
|
|
695
|
+
|
|
696
|
+
- [`src/composable/useAppStarter.ts`](./src/composable/useAppStarter.ts)
|
|
697
|
+
|
|
698
|
+
**功能定位**
|
|
699
|
+
|
|
700
|
+
- 应用根组件启动专用 Hook
|
|
701
|
+
- 同步当前语言包并执行自动登录态重定向
|
|
702
|
+
|
|
703
|
+
**函数签名**
|
|
704
|
+
|
|
705
|
+
```ts
|
|
706
|
+
function useAppStarter(): { easyuiLangPkg?: PkgItem }
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
**返回值**
|
|
710
|
+
|
|
711
|
+
| 字段 | 类型 | 说明 |
|
|
712
|
+
| --- | --- | --- |
|
|
713
|
+
| `easyuiLangPkg` | `PkgItem \| undefined` | 当前激活的 EasyUI 语言包 |
|
|
714
|
+
|
|
715
|
+
**基础调用**
|
|
716
|
+
|
|
717
|
+
```tsx
|
|
718
|
+
const { easyuiLangPkg } = useAppStarter()
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### useMenuAndTabData
|
|
722
|
+
|
|
723
|
+
**源码位置**
|
|
724
|
+
|
|
725
|
+
- [`src/composable/useMenuAndTabData.ts`](./src/composable/useMenuAndTabData.ts)
|
|
726
|
+
|
|
727
|
+
**功能定位**
|
|
728
|
+
|
|
729
|
+
- 维护菜单、标签页、当前选中项、固定欢迎页等布局核心状态
|
|
730
|
+
- 是 `BaseLayout` 侧栏和标签页联动的核心实现
|
|
731
|
+
|
|
732
|
+
**函数签名**
|
|
733
|
+
|
|
734
|
+
```ts
|
|
735
|
+
function useMenuAndTabData(menuservice: TabMenuService<number>)
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
**返回值**
|
|
739
|
+
|
|
740
|
+
| 字段 | 说明 |
|
|
741
|
+
| --- | --- |
|
|
742
|
+
| `collapsed` | 侧栏折叠状态 |
|
|
743
|
+
| `menuList` | 当前侧栏菜单树 |
|
|
744
|
+
| `menuAll` | 全量菜单树 |
|
|
745
|
+
| `onMenuSelection` | 菜单点击回调 |
|
|
746
|
+
| `menuArea` | 顶部分组菜单 |
|
|
747
|
+
| `setMenuAreaId` | 切换当前菜单分区 |
|
|
748
|
+
| `choiceId` | 当前选中菜单 ID |
|
|
749
|
+
| `menuTabList` | 当前标签页列表 |
|
|
750
|
+
| `onTabClosed` | 关闭标签页 |
|
|
751
|
+
| `onTabSelected` | 选中标签页 |
|
|
752
|
+
|
|
753
|
+
### useLocalTable / useTable
|
|
754
|
+
|
|
755
|
+
**源码位置**
|
|
756
|
+
|
|
757
|
+
- [`src/composable/useLocalTable.ts`](./src/composable/useLocalTable.ts)
|
|
758
|
+
- [`src/composable/useRemoteTable.ts`](./src/composable/useRemoteTable.ts)
|
|
759
|
+
|
|
760
|
+
**功能定位**
|
|
761
|
+
|
|
762
|
+
- `useLocalTable`:本地数据表格状态封装
|
|
763
|
+
- `useTable`:远程分页表格状态封装
|
|
764
|
+
- 两者都会自动注入权限校验器和用户版本号,用于页面刷新控制
|
|
765
|
+
|
|
766
|
+
**函数签名**
|
|
767
|
+
|
|
768
|
+
```ts
|
|
769
|
+
function useLocalTable(cfg: LocalTableConfig, dependency?: any)
|
|
770
|
+
function useTable(cfg: LocalTableConfig, dependency?: any)
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**默认表格状态**
|
|
774
|
+
|
|
775
|
+
```ts
|
|
776
|
+
{
|
|
777
|
+
data: [],
|
|
778
|
+
pageNumber: 1,
|
|
779
|
+
pageSize: cfg.defaultPageSize ?? 20,
|
|
780
|
+
total: 0,
|
|
781
|
+
loading: false,
|
|
782
|
+
border: false,
|
|
783
|
+
pageSizes: [10, 20, 30, 40, 50],
|
|
784
|
+
layout: ['list', 'sep', 'first', 'prev', 'links', 'next', 'last', 'sep', 'refresh', 'info'],
|
|
785
|
+
style: { width: '100%', height: '100%' },
|
|
786
|
+
selectionValues: []
|
|
787
|
+
}
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
**基础调用**
|
|
791
|
+
|
|
792
|
+
```tsx
|
|
793
|
+
import { useTable } from '@kordar/easyui-tpl'
|
|
794
|
+
|
|
795
|
+
const [ctx, ctxState] = useTable(
|
|
796
|
+
{
|
|
797
|
+
defaultPageSize: 20,
|
|
798
|
+
checkAccessItems: [],
|
|
799
|
+
},
|
|
800
|
+
''
|
|
801
|
+
)
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### useRefreshKey
|
|
805
|
+
|
|
806
|
+
**源码位置**
|
|
807
|
+
|
|
808
|
+
- [`src/composable/useRefreshKey.ts`](./src/composable/useRefreshKey.ts)
|
|
809
|
+
|
|
810
|
+
**功能定位**
|
|
811
|
+
|
|
812
|
+
- 通过自增 key 的方式强制触发下游组件刷新
|
|
813
|
+
|
|
814
|
+
**函数签名**
|
|
815
|
+
|
|
816
|
+
```ts
|
|
817
|
+
function useRefreshKey(): [number, Function]
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
**基础调用**
|
|
821
|
+
|
|
822
|
+
```tsx
|
|
823
|
+
const [refreshKey, forceRefresh] = useRefreshKey()
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### useAppRedirect
|
|
827
|
+
|
|
828
|
+
**源码位置**
|
|
829
|
+
|
|
830
|
+
- [`src/composable/useAppRedirect.ts`](./src/composable/useAppRedirect.ts)
|
|
831
|
+
|
|
832
|
+
**功能定位**
|
|
833
|
+
|
|
834
|
+
- 封装应用登录态跳转与历史地址恢复逻辑
|
|
835
|
+
- 基于 `@kordar-components/react` 的 `useAppRedirect` 再封装
|
|
836
|
+
|
|
837
|
+
**函数签名**
|
|
838
|
+
|
|
839
|
+
```ts
|
|
840
|
+
function useAppRedirect()
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
**内部依赖**
|
|
844
|
+
|
|
845
|
+
- `useLocation()`
|
|
846
|
+
- `state.app.historyLocation`
|
|
847
|
+
- `Admin.isGuest()`
|
|
848
|
+
- `App.saveHistoryLocation(location)`
|
|
849
|
+
|
|
850
|
+
<a id="view-modules"></a>
|
|
851
|
+
## 业务页面模块说明
|
|
852
|
+
|
|
853
|
+
模板内置了可直接复用的后台页面模块,主要用于快速搭建基础系统:
|
|
854
|
+
|
|
855
|
+
| 页面模块 | 导出名 | 场景说明 | 实现入口 |
|
|
856
|
+
| --- | --- | --- | --- |
|
|
857
|
+
| 管理员 | `AdminView` | 管理员列表、搜索、编辑、授权入口 | [`src/views/admin/index.tsx`](./src/views/admin/index.tsx) |
|
|
858
|
+
| 首页 | `HomeView` | 后台首页占位页 | [`src/views/home/index.tsx`](./src/views/home/index.tsx) |
|
|
859
|
+
| 设置 | `SettingView` | 系统设置列表与编辑 | [`src/views/setting/index.tsx`](./src/views/setting/index.tsx) |
|
|
860
|
+
| 登录 | `LoginView` | 登录页与登录配置读取 | [`src/views/login/index.tsx`](./src/views/login/index.tsx) |
|
|
861
|
+
| 角色 | `RolesView` | 角色列表、编辑、授权 | [`src/views/roles/index.tsx`](./src/views/roles/index.tsx) |
|
|
862
|
+
| 权限 | `PermissionsView` | 权限列表、编辑、快速新增 | [`src/views/permissions/index.tsx`](./src/views/permissions/index.tsx) |
|
|
863
|
+
| 字典 | `DictView` | 字典列表与详情项联动 | [`src/views/dict/index.tsx`](./src/views/dict/index.tsx) |
|
|
864
|
+
| 路由 | `RouterView` | 路由资源管理 | [`src/views/router/index.tsx`](./src/views/router/index.tsx) |
|
|
865
|
+
| 404 | `NotFoundView` | 未匹配页面 | [`src/views/page/404.tsx`](./src/views/page/404.tsx) |
|
|
866
|
+
| 大屏设计 | `ScreenDesign` | 大屏设计示例入口 | [`src/views/srceen/design/index.tsx`](./src/views/srceen/design/index.tsx) |
|
|
867
|
+
|
|
868
|
+
这些页面大多数都采用以下统一模式:
|
|
869
|
+
|
|
870
|
+
- 使用 `withDocHoc({ name, fetchConfig })` 注入页面语言包与配置数据
|
|
871
|
+
- 使用 `resourceService.configs(name)` 或相似服务读取枚举配置
|
|
872
|
+
- 通过 `TableContext` 管理搜索、分页、刷新和选择状态
|
|
873
|
+
|
|
874
|
+
<a id="extension-guide"></a>
|
|
875
|
+
## 组件模块扩展开发指南
|
|
876
|
+
|
|
877
|
+
### 1. 自定义组件目录创建规范
|
|
878
|
+
|
|
879
|
+
建议遵循现有目录分层,不要把业务组件直接堆在根目录:
|
|
880
|
+
|
|
881
|
+
```text
|
|
882
|
+
src/
|
|
883
|
+
├── components/
|
|
884
|
+
│ └── MyWidget.tsx
|
|
885
|
+
├── composable/
|
|
886
|
+
│ └── useMyWidget.ts
|
|
887
|
+
├── service/
|
|
888
|
+
│ └── MyWidgetService.ts
|
|
889
|
+
├── util/
|
|
890
|
+
│ └── my-widget.ts
|
|
891
|
+
└── views/
|
|
892
|
+
└── my-widget/
|
|
893
|
+
├── index.tsx
|
|
894
|
+
├── search.tsx
|
|
895
|
+
├── table.tsx
|
|
896
|
+
├── form.tsx
|
|
897
|
+
└── context.ts
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
推荐规则:
|
|
901
|
+
|
|
902
|
+
- 通用组件放 `src/components`
|
|
903
|
+
- 页面级状态 Hook 放 `src/composable`
|
|
904
|
+
- 接口调用封装放 `src/service`
|
|
905
|
+
- 页面级模块放 `src/views/<module>`
|
|
906
|
+
- 最终记得在 [`src/index.ts`](./src/index.ts) 注册导出
|
|
907
|
+
|
|
908
|
+
### 2. 基础模板编写规范
|
|
909
|
+
|
|
910
|
+
推荐采用当前仓库统一风格:
|
|
911
|
+
|
|
912
|
+
```tsx
|
|
913
|
+
import React, { FC } from 'react'
|
|
914
|
+
|
|
915
|
+
export interface MyWidgetProps {
|
|
916
|
+
title?: string
|
|
917
|
+
onChange?: (value: string) => void
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const MyWidget: FC<MyWidgetProps> = ({ title = 'Demo', onChange }) => {
|
|
921
|
+
return <div className="k-my-widget">{title}</div>
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
export default MyWidget
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
规范建议:
|
|
928
|
+
|
|
929
|
+
- 优先使用明确的 `Props` 类型,不要长期保留 `FC<any>`
|
|
930
|
+
- 组件名、文件名、导出名保持一致
|
|
931
|
+
- 复杂业务逻辑下沉到 Hook 或 util,不把所有流程堆在组件体内
|
|
932
|
+
- 与后台语义强相关的交互,优先封装为回调而不是在外层拼装
|
|
933
|
+
|
|
934
|
+
### 3. 样式隔离实现要求
|
|
935
|
+
|
|
936
|
+
当前工程使用全局 SCSS 与第三方主题样式混合方式,扩展时建议:
|
|
937
|
+
|
|
938
|
+
- 组件类名前缀统一使用 `k-` 或业务模块前缀,避免污染 `rc-easyui` 默认类
|
|
939
|
+
- 通用样式合并到 [`src/index.scss`](./src/index.scss) 或组件自有样式文件
|
|
940
|
+
- 避免直接覆盖 `rc-easyui` 全局基础类,优先加作用域父类
|
|
941
|
+
- 主题相关样式优先挂靠 `html[theme='xxx']` 或模板现有主题机制
|
|
942
|
+
|
|
943
|
+
建议写法:
|
|
944
|
+
|
|
945
|
+
```scss
|
|
946
|
+
.k-my-widget {
|
|
947
|
+
display: flex;
|
|
948
|
+
|
|
949
|
+
.k-my-widget__title {
|
|
950
|
+
font-weight: 600;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
### 4. 属性注册、事件封装、逻辑扩展流程
|
|
956
|
+
|
|
957
|
+
推荐扩展流程:
|
|
958
|
+
|
|
959
|
+
1. 在组件内定义清晰的 `Props` 接口
|
|
960
|
+
2. 把对外可变项收敛为 props,而不是依赖外部全局变量
|
|
961
|
+
3. 把交互动作通过 `onXxx` 回调暴露出去
|
|
962
|
+
4. 将数据加载、分页、刷新、权限判断提取到 Hook
|
|
963
|
+
5. 在 `src/index.ts` 中补齐导出
|
|
964
|
+
6. 在 `examples` 中补一个可运行演示页面用于调试
|
|
965
|
+
|
|
966
|
+
一个标准回调封装示例:
|
|
967
|
+
|
|
968
|
+
```tsx
|
|
969
|
+
export interface SwitchPanelProps {
|
|
970
|
+
value?: boolean
|
|
971
|
+
onChange?: (value: boolean) => void
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
const SwitchPanel: FC<SwitchPanelProps> = ({ value = false, onChange }) => {
|
|
975
|
+
const handleToggle = () => onChange?.(!value)
|
|
976
|
+
return <button onClick={handleToggle}>{String(value)}</button>
|
|
977
|
+
}
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### 5. 通用工具函数、混入逻辑复用方法
|
|
981
|
+
|
|
982
|
+
当前仓库已提供若干可复用工具,建议优先复用而不是重复实现:
|
|
983
|
+
|
|
984
|
+
| 工具模块 | 位置 | 适用场景 |
|
|
985
|
+
| --- | --- | --- |
|
|
986
|
+
| `resourceAccessItems()` | [`src/util/form.ts`](./src/util/form.ts) | 生成资源类页面的权限点配置 |
|
|
987
|
+
| `removeEventFn()` / `resourceRemoveEvent()` | [`src/util/form.ts`](./src/util/form.ts) | 封装删除确认与成功刷新逻辑 |
|
|
988
|
+
| `permissionTree()` / `roleTree()` | [`src/util/helper.ts`](./src/util/helper.ts) | 权限树、角色树数据转换 |
|
|
989
|
+
| `fetchConfigOptions()` | [`src/util/helper.ts`](./src/util/helper.ts) | 将后台配置转成下拉选项 |
|
|
990
|
+
| `renderTooltip()` / `renderFetchConfigValue()` | [`src/util/tables.tsx`](./src/util/tables.tsx) | 表格列渲染 |
|
|
991
|
+
| `Lazy()` | [`src/util/lazy.tsx`](./src/util/lazy.tsx) | 页面级懒加载包装 |
|
|
992
|
+
|
|
993
|
+
复用建议:
|
|
994
|
+
|
|
995
|
+
- 表格列展示优先复用 `renderTooltip()`,统一长文本处理方式
|
|
996
|
+
- 资源增删改页面优先复用 `resourceAccessItems()` 和 `resourceRemoveEvent()`
|
|
997
|
+
- 页面配置项转换优先复用 `fetchConfigOptions()`,避免重复拼装 `{ value, text }`
|
|
998
|
+
|
|
999
|
+
### 6. 扩展组件的单元测试要求
|
|
1000
|
+
|
|
1001
|
+
当前包脚本中预留了:
|
|
1002
|
+
|
|
1003
|
+
```bash
|
|
1004
|
+
npm test
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
虽然仓库内尚未补齐完整测试目录,但新增组件时建议遵循以下要求:
|
|
1008
|
+
|
|
1009
|
+
- 至少覆盖渲染成功、关键 props、生效回调三个层面
|
|
1010
|
+
- 涉及分页、弹层、菜单选择的组件,重点校验事件分发是否正确
|
|
1011
|
+
- 涉及权限隐藏的组件,重点校验不同权限组合下的 UI 结果
|
|
1012
|
+
- Hook 至少校验默认状态、状态流转和边界输入
|
|
1013
|
+
|
|
1014
|
+
建议测试目录:
|
|
1015
|
+
|
|
1016
|
+
```text
|
|
1017
|
+
src/components/__tests__/
|
|
1018
|
+
src/composable/__tests__/
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
建议测试技术栈:
|
|
1022
|
+
|
|
1023
|
+
- `Jest`
|
|
1024
|
+
- `@testing-library/react`
|
|
1025
|
+
- `jsdom`
|
|
1026
|
+
|
|
1027
|
+
### 7. 本地调试流程
|
|
1028
|
+
|
|
1029
|
+
推荐调试流程:
|
|
1030
|
+
|
|
1031
|
+
1. 在 `src/` 中实现或修改组件
|
|
1032
|
+
2. 在 [`examples`](./examples) 中创建一个最小可复现入口
|
|
1033
|
+
3. 执行 `npm run dev` 启动 Vite
|
|
1034
|
+
4. 通过浏览器逐项验证布局、事件、权限、国际化、主题切换
|
|
1035
|
+
5. 完成后执行 `npm run build` 验证产物构建
|
|
1036
|
+
|
|
1037
|
+
可参考的调试入口:
|
|
1038
|
+
|
|
1039
|
+
- [`examples/App.tsx`](./examples/App.tsx)
|
|
1040
|
+
- [`examples/router/index.tsx`](./examples/router/index.tsx)
|
|
1041
|
+
- [`examples/components/admin_layout.tsx`](./examples/components/admin_layout.tsx)
|
|
1042
|
+
|
|
1043
|
+
<a id="style-i18n-theme"></a>
|
|
1044
|
+
## 样式、国际化与主题说明
|
|
1045
|
+
|
|
1046
|
+
### 关键依赖(外部)
|
|
1047
|
+
|
|
1048
|
+
- React 生态:`react`、`react-dom`、`react-router`、`react-router-dom`、`react-i18next`
|
|
1049
|
+
- UI:`rc-easyui`、`rc-drawer`、`@kordar/easyui`、`@kordar/react-screen`
|
|
1050
|
+
- 工具:`axios`、`lodash-es`
|
|
1051
|
+
- Kordar 库:`@kordar-lib/base`、`@kordar-lib/i18n`、`@kordar-lib/menus`、`@kordar-lib/reducers`、`@kordar-lib/request`
|
|
1052
|
+
|
|
1053
|
+
### 样式
|
|
1054
|
+
|
|
1055
|
+
示例样式在 [`examples/example.scss`](./examples/example.scss) 中已引入:
|
|
34
1056
|
|
|
35
|
-
## 样式
|
|
36
|
-
示例样式在 examples/example.scss 中已引入:
|
|
37
1057
|
```scss
|
|
38
1058
|
@import '@kordar-lib/iconfont/dist/index.min.css';
|
|
39
1059
|
@import '@kordar/easyui/dist/index.min.css';
|
|
40
1060
|
@import '@kordar/react-screen/dist/index.min.css';
|
|
41
1061
|
```
|
|
42
|
-
index.html 会按本地持久化主题加载 rc-easyui 主题与 react.css。
|
|
43
1062
|
|
|
44
|
-
|
|
45
|
-
examples/init.ts 展示了语言包与请求初始化的方式。你可以按需接入实际后端接口,完善登录、菜单、权限等配置。
|
|
1063
|
+
`index.html` 会按本地持久化主题加载 `rc-easyui` 主题与 `react.css`。
|
|
46
1064
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
- examples
|
|
1065
|
+
### 国际化
|
|
1066
|
+
|
|
1067
|
+
- 语言包文件:
|
|
1068
|
+
- [`public/en.json`](./public/en.json)
|
|
1069
|
+
- [`public/zh_CN.json`](./public/zh_CN.json)
|
|
1070
|
+
- 初始化入口:[`examples/init.ts`](./examples/init.ts)
|
|
1071
|
+
- 运行时切换入口:[`src/components/LocaleBarItem.tsx`](./src/components/LocaleBarItem.tsx)
|
|
1072
|
+
|
|
1073
|
+
### 初始化(示例)
|
|
1074
|
+
|
|
1075
|
+
[`examples/init.ts`](./examples/init.ts) 展示了语言包与请求初始化的方式。你可以按需接入实际后端接口,完善登录、菜单、权限等配置。
|
|
1076
|
+
|
|
1077
|
+
<a id="verification"></a>
|
|
1078
|
+
## 本地验证与文档自检
|
|
1079
|
+
|
|
1080
|
+
### 本次文档改写的示例校验原则
|
|
1081
|
+
|
|
1082
|
+
- 所有组件示例均优先基于当前仓库真实源码组织
|
|
1083
|
+
- 导入路径均以当前 [`src/index.ts`](./src/index.ts) 的真实导出为准
|
|
1084
|
+
- 内部组件如 `UserBarItem` 已明确标注“未对外导出”
|
|
1085
|
+
|
|
1086
|
+
### 建议本地验证命令
|
|
1087
|
+
|
|
1088
|
+
```bash
|
|
1089
|
+
npm run dev
|
|
1090
|
+
npm run build
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
### 链接自检清单
|
|
1094
|
+
|
|
1095
|
+
- 本 README 中所有文件链接均使用当前仓库内真实存在的相对路径
|
|
1096
|
+
- 目录跳转使用显式锚点,避免标题重命名导致的 TOC 失效
|
|
1097
|
+
- 未引入不存在的截图文件或外部失效链接
|
|
1098
|
+
|
|
1099
|
+
### 截图预览区域使用建议
|
|
1100
|
+
|
|
1101
|
+
为避免 README 中引用不存在的静态资源导致链接失效,当前文档采用“预览区域说明”方式预留截图位置。建议在你们正式沉淀文档时:
|
|
1102
|
+
|
|
1103
|
+
1. 从 `examples` 运行页面截取组件真实界面
|
|
1104
|
+
2. 统一放入例如 `docs/readme-assets/` 目录
|
|
1105
|
+
3. 将本 README 中的“预览区域”说明替换为真实图片链接
|
|
53
1106
|
|
|
1107
|
+
示例格式:
|
|
1108
|
+
|
|
1109
|
+
```md
|
|
1110
|
+

|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
<a id="license"></a>
|
|
54
1114
|
## 许可证
|
|
55
|
-
MIT
|
|
56
1115
|
|
|
1116
|
+
MIT
|