@basestone/hooks 1.3.6 → 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +423 -29
- package/dist/design-hooks.es.prod.d.ts +6 -7
- package/dist/design-hooks.es.prod.js +107 -106
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,69 +1,463 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @basestone/hooks
|
|
2
2
|
|
|
3
|
+
一套用于 React + Ant Design 应用的生产级 Hooks 库,提供表格管理、模态框管理、表单提交、数据请求等常用功能。
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@basestone/hooks)
|
|
6
|
+
[](https://github.com/base-stone/hooks/blob/master/LICENSE)
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
## ✨ 特性
|
|
9
|
+
|
|
10
|
+
- 🎯 **开箱即用** - 提供常用业务场景的完整解决方案
|
|
11
|
+
- 💪 **TypeScript** - 完整的类型定义和类型推导
|
|
12
|
+
- 🎨 **Ant Design** - 深度集成 Ant Design 组件
|
|
13
|
+
- ⚡️ **性能优化** - 内置性能优化和记忆化
|
|
14
|
+
- 🔧 **灵活配置** - 支持全局配置和局部定制
|
|
7
15
|
|
|
8
|
-
|
|
16
|
+
## 📦 安装
|
|
9
17
|
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @basestone/hooks
|
|
20
|
+
# or
|
|
21
|
+
npm install @basestone/hooks
|
|
22
|
+
# or
|
|
23
|
+
yarn add @basestone/hooks
|
|
10
24
|
```
|
|
11
25
|
|
|
26
|
+
## 📚 Hooks 列表
|
|
12
27
|
|
|
13
|
-
|
|
28
|
+
- [useTableList](#usetablelist---表格管理) - 表格管理(分页、排序、加载等)
|
|
29
|
+
- [useCreateModal](#usecreatemodal---模态框管理) - 模态框/抽屉管理
|
|
30
|
+
- [useSelectOptions](#useselectoptions---下拉选项管理) - 下拉选项管理
|
|
31
|
+
- [useRequestQuery](#userequestquery---数据请求) - 通用数据请求
|
|
32
|
+
- [useFormSubmit](#useformsubmit---表单提交) - 表单提交处理
|
|
33
|
+
- [useMemoizedFn](#usememoizedfn---函数记忆化) - 函数记忆化
|
|
14
34
|
|
|
35
|
+
## 🔧 Claude Code Skill
|
|
15
36
|
|
|
16
|
-
|
|
37
|
+
本库提供了 Claude Code Skill,可以在使用 Claude Code 开发时快速获取帮助和代码示例。
|
|
38
|
+
|
|
39
|
+
查看 [Skill 文档](./.claude/skills/README.md) 了解如何在你的项目中使用。
|
|
40
|
+
|
|
41
|
+
## 📖 使用文档
|
|
17
42
|
|
|
18
|
-
|
|
19
|
-
|
|
43
|
+
|
|
44
|
+
### useTableList - 表格管理
|
|
45
|
+
|
|
46
|
+
提供完整的表格管理功能,包括分页、排序、加载状态、行选择等。
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { useTableList } from '@basestone/hooks'
|
|
50
|
+
import { Table } from 'antd'
|
|
51
|
+
|
|
52
|
+
function UserList() {
|
|
53
|
+
const { tableProps, search, refresh, reset, queryParams, selectedRowKeys } = useTableList({
|
|
54
|
+
queryFn: async (params) => {
|
|
55
|
+
// API 返回格式: { status: 'success', data: { list: [], totalCount: 0 } }
|
|
56
|
+
return await getUserList(params)
|
|
57
|
+
},
|
|
20
58
|
params: {
|
|
21
59
|
orderField: 'createDate',
|
|
22
60
|
orderType: 'DESC',
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
61
|
+
status: 'active'
|
|
62
|
+
},
|
|
63
|
+
rowSelection: true // 启用行选择
|
|
26
64
|
})
|
|
27
|
-
|
|
28
|
-
<Table rowKey="id" scroll={{ x: 'max-content' }} columns={columns} {...tableProps} />
|
|
29
65
|
|
|
66
|
+
return (
|
|
67
|
+
<div>
|
|
68
|
+
<button onClick={() => search({ keyword: 'test' })}>搜索</button>
|
|
69
|
+
<button onClick={() => refresh()}>刷新</button>
|
|
70
|
+
<Table
|
|
71
|
+
rowKey="id"
|
|
72
|
+
scroll={{ x: 'max-content' }}
|
|
73
|
+
columns={columns}
|
|
74
|
+
{...tableProps}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
30
79
|
```
|
|
31
80
|
|
|
32
|
-
|
|
81
|
+
**API:**
|
|
82
|
+
- `tableProps` - Ant Design Table 所需的所有属性
|
|
83
|
+
- `search(params)` - 搜索并重置到第一页
|
|
84
|
+
- `refresh(params)` - 刷新当前页
|
|
85
|
+
- `reset(params)` - 重置查询参数
|
|
86
|
+
- `queryParams` - 当前查询参数
|
|
87
|
+
- `selectedRowKeys` - 选中的行 keys
|
|
33
88
|
|
|
89
|
+
### useCreateModal - 模态框管理
|
|
34
90
|
|
|
35
|
-
|
|
91
|
+
使用 zustand 管理多个模态框的状态,支持 Modal 和 Drawer。
|
|
36
92
|
|
|
37
|
-
|
|
93
|
+
```tsx
|
|
94
|
+
import { useCreateModal } from '@basestone/hooks'
|
|
95
|
+
import { Modal, Drawer } from 'antd'
|
|
38
96
|
|
|
39
|
-
|
|
97
|
+
function UserManagement() {
|
|
98
|
+
const { editModal, viewModal, open, close, toggle } = useCreateModal({
|
|
99
|
+
edit: {
|
|
100
|
+
width: 600,
|
|
101
|
+
title: (data) => data?.id ? '编辑用户' : '创建用户',
|
|
102
|
+
centered: true,
|
|
103
|
+
maskClosable: false
|
|
104
|
+
},
|
|
105
|
+
view: {
|
|
106
|
+
width: 800,
|
|
107
|
+
title: '用户详情',
|
|
108
|
+
placement: 'right' // Drawer 配置
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div>
|
|
114
|
+
<button onClick={() => open('edit', { id: 1, name: 'John' })}>
|
|
115
|
+
编辑用户
|
|
116
|
+
</button>
|
|
40
117
|
|
|
118
|
+
{/* 作为 Modal 使用 */}
|
|
119
|
+
<Modal {...editModal.modalProps}>
|
|
120
|
+
<div>用户数据: {JSON.stringify(editModal.data)}</div>
|
|
121
|
+
</Modal>
|
|
122
|
+
|
|
123
|
+
{/* 作为 Drawer 使用 */}
|
|
124
|
+
<Drawer {...viewModal.drawerProps}>
|
|
125
|
+
<div>用户详情: {JSON.stringify(viewModal.data)}</div>
|
|
126
|
+
</Drawer>
|
|
127
|
+
</div>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
41
130
|
```
|
|
42
131
|
|
|
132
|
+
**API:**
|
|
133
|
+
- `{name}Modal.modalProps` - Modal 组件属性
|
|
134
|
+
- `{name}Modal.drawerProps` - Drawer 组件属性
|
|
135
|
+
- `{name}Modal.visible` - 可见状态
|
|
136
|
+
- `{name}Modal.data` - 传入的数据
|
|
137
|
+
- `open(type, data)` - 打开指定模态框
|
|
138
|
+
- `close(type)` - 关闭指定模态框
|
|
139
|
+
- `toggle(type, data)` - 切换指定模态框
|
|
140
|
+
|
|
43
141
|
|
|
44
|
-
### useSelectOptions
|
|
142
|
+
### useSelectOptions - 下拉选项管理
|
|
45
143
|
|
|
144
|
+
自动获取和管理下拉选项,并创建 value-label 映射。
|
|
46
145
|
|
|
146
|
+
```tsx
|
|
147
|
+
import { useSelectOptions } from '@basestone/hooks'
|
|
148
|
+
import { Select } from 'antd'
|
|
149
|
+
|
|
150
|
+
function UserFilter() {
|
|
151
|
+
const { departmentOptions, departmentMap, loading, refresh } = useSelectOptions({
|
|
152
|
+
queryFn: async (params) => {
|
|
153
|
+
// API 返回格式: { status: 'success', data: [] }
|
|
154
|
+
return await getDepartmentList(params)
|
|
155
|
+
},
|
|
156
|
+
params: { active: true },
|
|
157
|
+
dataKey: 'department', // 会创建 departmentOptions 和 departmentMap
|
|
158
|
+
fieldNames: {
|
|
159
|
+
label: 'departmentName',
|
|
160
|
+
value: 'departmentId'
|
|
161
|
+
},
|
|
162
|
+
transform: (data) => data.filter(item => item.visible) // 可选的数据转换
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<Select
|
|
167
|
+
options={departmentOptions}
|
|
168
|
+
loading={loading}
|
|
169
|
+
onChange={(value) => {
|
|
170
|
+
console.log('选中值:', value)
|
|
171
|
+
console.log('对应标签:', departmentMap.get(value))
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
47
176
|
```
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
177
|
+
|
|
178
|
+
**API:**
|
|
179
|
+
- `{dataKey}Options` - 格式化后的选项数组 `{ label, value, data }`
|
|
180
|
+
- `{dataKey}Map` - value 到 label 的映射 Map
|
|
181
|
+
- `loading` - 加载状态
|
|
182
|
+
- `refresh()` - 刷新选项
|
|
183
|
+
|
|
184
|
+
### useRequestQuery - 数据请求
|
|
185
|
+
|
|
186
|
+
通用的数据请求 Hook,支持 Object 和 Array 类型数据。
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import { useRequestQuery } from '@basestone/hooks'
|
|
190
|
+
|
|
191
|
+
function UserProfile({ userId }) {
|
|
192
|
+
// 请求单个对象
|
|
193
|
+
const { userInfo, setUserInfo, loading, refresh } = useRequestQuery({
|
|
194
|
+
queryFn: async (params) => {
|
|
195
|
+
// API 返回格式: { status: 'success', data: {...} }
|
|
196
|
+
return await getUserInfo(params)
|
|
197
|
+
},
|
|
198
|
+
params: { userId },
|
|
199
|
+
dataKey: 'userInfo',
|
|
200
|
+
dataType: 'Object',
|
|
201
|
+
initialValue: { name: '', email: '' },
|
|
202
|
+
transform: (data) => ({
|
|
203
|
+
...data,
|
|
204
|
+
fullName: `${data.firstName} ${data.lastName}`
|
|
205
|
+
}),
|
|
206
|
+
success: (data) => {
|
|
207
|
+
console.log('数据加载成功:', data)
|
|
208
|
+
}
|
|
52
209
|
})
|
|
53
210
|
|
|
211
|
+
// 请求数组数据
|
|
212
|
+
const { notificationList, setNotificationList } = useRequestQuery({
|
|
213
|
+
queryFn: getNotificationList,
|
|
214
|
+
params: { userId },
|
|
215
|
+
dataKey: 'notificationList',
|
|
216
|
+
dataType: 'Array',
|
|
217
|
+
initialValue: []
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// 手动更新数据
|
|
221
|
+
const handleUpdateName = () => {
|
|
222
|
+
setUserInfo(prev => ({ ...prev, name: '新名字' }))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const markAsRead = (id) => {
|
|
226
|
+
setNotificationList(prev =>
|
|
227
|
+
prev.map(item => item.id === id ? { ...item, read: true } : item)
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
<div>
|
|
233
|
+
{loading ? '加载中...' : (
|
|
234
|
+
<div>
|
|
235
|
+
<h1>{userInfo?.fullName}</h1>
|
|
236
|
+
<p>{userInfo?.email}</p>
|
|
237
|
+
<button onClick={handleUpdateName}>修改名字</button>
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
54
243
|
```
|
|
55
244
|
|
|
56
|
-
|
|
245
|
+
**API:**
|
|
246
|
+
- `{dataKey}` - 请求到的数据
|
|
247
|
+
- `set{DataKey}` - 手动更新数据的函数,支持直接设置或函数式更新
|
|
248
|
+
- `loading` - 加载状态
|
|
249
|
+
- `refresh(params)` - 刷新数据
|
|
250
|
+
|
|
251
|
+
### useFormSubmit - 表单提交
|
|
57
252
|
|
|
253
|
+
处理表单提交,自动管理加载状态和消息提示。
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { useFormSubmit } from '@basestone/hooks'
|
|
257
|
+
import { Form, Input, Button, App } from 'antd'
|
|
258
|
+
|
|
259
|
+
function UserForm({ onSuccess }) {
|
|
260
|
+
const [form] = Form.useForm()
|
|
261
|
+
|
|
262
|
+
const { loading, submit } = useFormSubmit(
|
|
263
|
+
async (values) => {
|
|
264
|
+
// API 返回格式: { status: 'success', info: '操作成功' }
|
|
265
|
+
return await createUser(values)
|
|
266
|
+
},
|
|
267
|
+
(result) => {
|
|
268
|
+
form.resetFields()
|
|
269
|
+
onSuccess?.(result)
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<App> {/* 必需:message API 依赖 */}
|
|
275
|
+
<Form form={form} onFinish={submit}>
|
|
276
|
+
<Form.Item name="name" label="姓名" rules={[{ required: true }]}>
|
|
277
|
+
<Input />
|
|
278
|
+
</Form.Item>
|
|
279
|
+
<Button type="primary" htmlType="submit" loading={loading}>
|
|
280
|
+
提交
|
|
281
|
+
</Button>
|
|
282
|
+
</Form>
|
|
283
|
+
</App>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
58
286
|
```
|
|
59
287
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
288
|
+
**API:**
|
|
289
|
+
- `loading` - 提交中状态
|
|
290
|
+
- `submit(values)` - 提交函数
|
|
291
|
+
|
|
292
|
+
### useMemoizedFn - 函数记忆化
|
|
293
|
+
|
|
294
|
+
创建一个稳定引用的函数,但始终调用最新的实现。
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import { useMemoizedFn } from '@basestone/hooks'
|
|
298
|
+
import { useEffect } from 'react'
|
|
299
|
+
|
|
300
|
+
function Component() {
|
|
301
|
+
const [count, setCount] = useState(0)
|
|
302
|
+
|
|
303
|
+
// 函数引用永不改变,但总是调用最新的实现
|
|
304
|
+
const handleClick = useMemoizedFn(() => {
|
|
305
|
+
console.log('当前 count:', count) // 总是打印最新的 count
|
|
306
|
+
setCount(count + 1)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
// handleClick 不会导致 effect 重新运行
|
|
311
|
+
}, [handleClick])
|
|
312
|
+
|
|
313
|
+
return <button onClick={handleClick}>点击 {count}</button>
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## 🎯 完整示例
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import {
|
|
321
|
+
useTableList,
|
|
322
|
+
useCreateModal,
|
|
323
|
+
useSelectOptions,
|
|
324
|
+
useFormSubmit
|
|
325
|
+
} from '@basestone/hooks'
|
|
326
|
+
import { Table, Modal, Form, Input, Select, Button, Space } from 'antd'
|
|
327
|
+
|
|
328
|
+
function UserManagement() {
|
|
329
|
+
const [form] = Form.useForm()
|
|
330
|
+
|
|
331
|
+
// 表格管理
|
|
332
|
+
const { tableProps, refresh } = useTableList({
|
|
333
|
+
queryFn: getUserList,
|
|
334
|
+
params: { status: 'active' },
|
|
335
|
+
rowSelection: true
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// 模态框管理
|
|
339
|
+
const { editModal, open, close } = useCreateModal({
|
|
340
|
+
edit: {
|
|
341
|
+
width: 600,
|
|
342
|
+
title: (data) => data?.id ? '编辑' : '创建'
|
|
66
343
|
}
|
|
67
344
|
})
|
|
68
345
|
|
|
346
|
+
// 下拉选项
|
|
347
|
+
const { roleOptions } = useSelectOptions({
|
|
348
|
+
queryFn: getRoleList,
|
|
349
|
+
dataKey: 'role',
|
|
350
|
+
fieldNames: { label: 'roleName', value: 'roleId' }
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
// 表单提交
|
|
354
|
+
const { loading, submit } = useFormSubmit(
|
|
355
|
+
async (values) => {
|
|
356
|
+
const api = editModal.data?.id ? updateUser : createUser
|
|
357
|
+
return api({ ...values, id: editModal.data?.id })
|
|
358
|
+
},
|
|
359
|
+
() => {
|
|
360
|
+
close('edit')
|
|
361
|
+
refresh()
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
const columns = [
|
|
366
|
+
{ title: '姓名', dataIndex: 'name' },
|
|
367
|
+
{ title: '邮箱', dataIndex: 'email' },
|
|
368
|
+
{
|
|
369
|
+
title: '操作',
|
|
370
|
+
render: (_, record) => (
|
|
371
|
+
<Space>
|
|
372
|
+
<Button onClick={() => open('edit', record)}>编辑</Button>
|
|
373
|
+
</Space>
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
return (
|
|
379
|
+
<div>
|
|
380
|
+
<Button onClick={() => open('edit')}>创建用户</Button>
|
|
381
|
+
<Table rowKey="id" columns={columns} {...tableProps} />
|
|
382
|
+
|
|
383
|
+
<Modal {...editModal.modalProps}>
|
|
384
|
+
<Form form={form} onFinish={submit} initialValues={editModal.data}>
|
|
385
|
+
<Form.Item name="name" label="姓名" rules={[{ required: true }]}>
|
|
386
|
+
<Input />
|
|
387
|
+
</Form.Item>
|
|
388
|
+
<Form.Item name="roleId" label="角色">
|
|
389
|
+
<Select options={roleOptions} />
|
|
390
|
+
</Form.Item>
|
|
391
|
+
<Button type="primary" htmlType="submit" loading={loading}>
|
|
392
|
+
提交
|
|
393
|
+
</Button>
|
|
394
|
+
</Form>
|
|
395
|
+
</Modal>
|
|
396
|
+
</div>
|
|
397
|
+
)
|
|
398
|
+
}
|
|
69
399
|
```
|
|
400
|
+
|
|
401
|
+
## 🔌 API 响应格式
|
|
402
|
+
|
|
403
|
+
所有请求函数应返回以下格式:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// 成功响应
|
|
407
|
+
{
|
|
408
|
+
status: 'success',
|
|
409
|
+
data: any, // 实际数据
|
|
410
|
+
info?: string // 可选的消息
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// useTableList 专用格式
|
|
414
|
+
{
|
|
415
|
+
status: 'success',
|
|
416
|
+
data: {
|
|
417
|
+
list: any[], // 数据列表
|
|
418
|
+
totalCount: number // 总数
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## 🎨 全局配置
|
|
424
|
+
|
|
425
|
+
### 表格全局配置
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import { configureTableOption } from '@basestone/hooks'
|
|
429
|
+
|
|
430
|
+
configureTableOption({
|
|
431
|
+
sortField: ['orderType', 'orderField'], // 排序字段名
|
|
432
|
+
sortOrder: ['ASC', 'DESC'], // 排序顺序值
|
|
433
|
+
pageSize: 20 // 默认每页条数
|
|
434
|
+
})
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## 💡 最佳实践
|
|
438
|
+
|
|
439
|
+
1. **错误处理**: 所有 hooks 内部处理错误,但应确保 API 返回正确的响应格式
|
|
440
|
+
2. **加载状态**: 利用提供的 loading 状态提升用户体验
|
|
441
|
+
3. **函数记忆化**: 对于传递给子组件的回调,使用 `useMemoizedFn`
|
|
442
|
+
4. **模态框状态**: 模态框状态使用 zustand 全局管理,跨渲染保持
|
|
443
|
+
5. **类型安全**: 使用 TypeScript 泛型获得更好的类型推导
|
|
444
|
+
|
|
445
|
+
## 📄 依赖
|
|
446
|
+
|
|
447
|
+
- `react` ^19.0.0
|
|
448
|
+
- `antd` ^6.0.0
|
|
449
|
+
- `zustand` ^5.0.0
|
|
450
|
+
|
|
451
|
+
## 📝 License
|
|
452
|
+
|
|
453
|
+
MIT
|
|
454
|
+
|
|
455
|
+
## 👨💻 Author
|
|
456
|
+
|
|
457
|
+
leafront (leafront@126.com)
|
|
458
|
+
|
|
459
|
+
## 🔗 相关链接
|
|
460
|
+
|
|
461
|
+
- [GitHub](https://github.com/base-stone/hooks)
|
|
462
|
+
- [NPM](https://www.npmjs.com/package/@basestone/hooks)
|
|
463
|
+
- [Claude Code Skill](./.claude/skills/README.md)
|
|
@@ -66,10 +66,6 @@ export declare interface ModalProps {
|
|
|
66
66
|
onCancel: () => void;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
declare type noop = (this: any, ...args: any[]) => any;
|
|
70
|
-
|
|
71
|
-
declare type PickFunction<T extends noop> = (this: ThisParameterType<T>, ...args: Parameters<T>) => ReturnType<T>;
|
|
72
|
-
|
|
73
69
|
declare type Placement = 'top' | 'bottom' | 'left' | 'right';
|
|
74
70
|
|
|
75
71
|
declare interface QueryOptions<K extends string, T = Record<string, any>> {
|
|
@@ -78,6 +74,7 @@ declare interface QueryOptions<K extends string, T = Record<string, any>> {
|
|
|
78
74
|
dataType: DataType;
|
|
79
75
|
dataKey: K;
|
|
80
76
|
transform?: (data: unknown) => T;
|
|
77
|
+
success?: (data: unknown) => void;
|
|
81
78
|
initialValue?: Record<string, any> | Record<string, any>[];
|
|
82
79
|
}
|
|
83
80
|
|
|
@@ -106,6 +103,8 @@ declare type QueryResult<K extends string, T = Record<string, any>> = {
|
|
|
106
103
|
refresh: (params?: Record<string, any> | undefined) => void;
|
|
107
104
|
} & {
|
|
108
105
|
[P in K]?: T;
|
|
106
|
+
} & {
|
|
107
|
+
[P in SetterKey<K>]: (data: T | ((prevData: T) => T)) => void;
|
|
109
108
|
};
|
|
110
109
|
|
|
111
110
|
declare interface RequestFn {
|
|
@@ -142,6 +141,8 @@ declare type SelectOptionsResult<K extends string, V extends string | number = s
|
|
|
142
141
|
[P in `${K}Map`]?: MapData<V>;
|
|
143
142
|
}>;
|
|
144
143
|
|
|
144
|
+
declare type SetterKey<K extends string> = `set${Capitalize<K>}`;
|
|
145
|
+
|
|
145
146
|
declare interface SuccessFn {
|
|
146
147
|
(data: Record<string, any>): void;
|
|
147
148
|
}
|
|
@@ -198,9 +199,7 @@ export declare function useCreateModal<const T extends Record<string, ModalConfi
|
|
|
198
199
|
|
|
199
200
|
export declare function useFormSubmit(requestFn: RequestFn, successFn: SuccessFn): FormSubmitResult;
|
|
200
201
|
|
|
201
|
-
export declare function
|
|
202
|
-
|
|
203
|
-
export declare function useRequestQuery<T = Record<string, any> | Record<string, any>[] | undefined, const K extends string = string>({ queryFn, params, dataType, dataKey, initialValue, transform }: QueryOptions<K, T>): QueryResult<K, T>;
|
|
202
|
+
export declare function useRequestQuery<T = Record<string, any> | Record<string, any>[] | undefined, const K extends string = string>({ queryFn, params, dataType, dataKey, initialValue, transform, success }: QueryOptions<K, T>): QueryResult<K, T>;
|
|
204
203
|
|
|
205
204
|
export declare function useSelectOptions<V extends string | number = string | number, const K extends string = string>({ queryFn, params, dataKey, fieldNames, transform }: SelectConfig<K>): SelectOptionsResult<K, V>;
|
|
206
205
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { useRef as
|
|
1
|
+
import { useRef as _, useMemo as V, useState as h, useEffectEvent as j, useEffect as T, useId as W, useCallback as X } from "react";
|
|
2
2
|
import { Empty as J, App as K } from "antd";
|
|
3
3
|
import { create as ee } from "zustand";
|
|
4
|
-
var
|
|
4
|
+
var F = { exports: {} }, x = {};
|
|
5
5
|
var H;
|
|
6
6
|
function te() {
|
|
7
|
-
if (H) return
|
|
7
|
+
if (H) return x;
|
|
8
8
|
H = 1;
|
|
9
9
|
var r = /* @__PURE__ */ Symbol.for("react.transitional.element"), a = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
10
10
|
function o(t, s, n) {
|
|
11
|
-
var
|
|
12
|
-
if (n !== void 0 && (
|
|
11
|
+
var d = null;
|
|
12
|
+
if (n !== void 0 && (d = "" + n), s.key !== void 0 && (d = "" + s.key), "key" in s) {
|
|
13
13
|
n = {};
|
|
14
14
|
for (var c in s)
|
|
15
15
|
c !== "key" && (n[c] = s[c]);
|
|
@@ -17,34 +17,34 @@ function te() {
|
|
|
17
17
|
return s = n.ref, {
|
|
18
18
|
$$typeof: r,
|
|
19
19
|
type: t,
|
|
20
|
-
key:
|
|
20
|
+
key: d,
|
|
21
21
|
ref: s !== void 0 ? s : null,
|
|
22
22
|
props: n
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
return
|
|
25
|
+
return x.Fragment = a, x.jsx = o, x.jsxs = o, x;
|
|
26
26
|
}
|
|
27
27
|
var I;
|
|
28
28
|
function se() {
|
|
29
|
-
return I || (I = 1,
|
|
29
|
+
return I || (I = 1, F.exports = te()), F.exports;
|
|
30
30
|
}
|
|
31
|
-
var
|
|
31
|
+
var oe = se();
|
|
32
32
|
function y(r) {
|
|
33
|
-
const a =
|
|
33
|
+
const a = _(r);
|
|
34
34
|
a.current = V(() => r, [r]);
|
|
35
|
-
const o =
|
|
35
|
+
const o = _(void 0);
|
|
36
36
|
return o.current || (o.current = function(...t) {
|
|
37
37
|
return a.current.apply(this, t);
|
|
38
38
|
}), o.current;
|
|
39
39
|
}
|
|
40
|
-
const
|
|
40
|
+
const P = {
|
|
41
41
|
sortField: ["orderType", "orderField"],
|
|
42
42
|
sortOrder: ["ASC", "DESC"],
|
|
43
43
|
pageSize: 10
|
|
44
44
|
};
|
|
45
45
|
function ie(r) {
|
|
46
46
|
Object.keys(r).forEach((a) => {
|
|
47
|
-
|
|
47
|
+
P[a] = r[a];
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
function ce({
|
|
@@ -53,7 +53,7 @@ function ce({
|
|
|
53
53
|
transform: o,
|
|
54
54
|
rowSelection: t
|
|
55
55
|
}) {
|
|
56
|
-
const s =
|
|
56
|
+
const s = P.pageSize, [n, d] = h({
|
|
57
57
|
pagination: {
|
|
58
58
|
showSizeChanger: !0,
|
|
59
59
|
showQuickJumper: !0,
|
|
@@ -67,57 +67,57 @@ function ce({
|
|
|
67
67
|
pageSize: s,
|
|
68
68
|
...a
|
|
69
69
|
}
|
|
70
|
-
}), { pagination: c, list: f, queryParams: e } = n, { pageNo:
|
|
70
|
+
}), { pagination: c, list: f, queryParams: e } = n, { pageNo: u, pageSize: p } = e, [i, v] = h(!0), [g, C] = h([]), R = V(() => {
|
|
71
71
|
if (t)
|
|
72
72
|
return {
|
|
73
73
|
selectedRowKeys: g,
|
|
74
|
-
onChange: (
|
|
74
|
+
onChange: (l) => C(l)
|
|
75
75
|
};
|
|
76
|
-
}, [t, g]),
|
|
77
|
-
(
|
|
78
|
-
),
|
|
79
|
-
const { pageNo: L } =
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
const { data:
|
|
84
|
-
t &&
|
|
85
|
-
list:
|
|
86
|
-
queryParams:
|
|
76
|
+
}, [t, g]), M = y(
|
|
77
|
+
(l) => `共 ${l} 条记录 第 ${u}/${Math.ceil(l / p)} 页 `
|
|
78
|
+
), m = async (l) => {
|
|
79
|
+
const { pageNo: L } = l;
|
|
80
|
+
v(!0);
|
|
81
|
+
const b = { ...a, pageSize: p, ...l };
|
|
82
|
+
l.pageNo === void 0 && (b.pageNo = 1), l.pageSize === void 0 && (b.pageSize = p);
|
|
83
|
+
const { data: z } = await r(b), k = o ? o(z) : z, { list: N = [], totalCount: A = 0 } = k || {};
|
|
84
|
+
t && C([]), d({
|
|
85
|
+
list: N,
|
|
86
|
+
queryParams: b,
|
|
87
87
|
pagination: {
|
|
88
88
|
...c,
|
|
89
89
|
current: L,
|
|
90
|
-
pageSize:
|
|
91
|
-
total:
|
|
90
|
+
pageSize: b.pageSize,
|
|
91
|
+
total: A
|
|
92
92
|
}
|
|
93
|
-
}),
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
}),
|
|
97
|
-
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
}, S = (
|
|
101
|
-
|
|
102
|
-
},
|
|
103
|
-
const { action:
|
|
104
|
-
if (["paginate", "sort"].includes(
|
|
105
|
-
const { current:
|
|
93
|
+
}), v(!1);
|
|
94
|
+
}, E = j(() => {
|
|
95
|
+
m({ ...e, pageNo: 1 });
|
|
96
|
+
}), $ = (l) => {
|
|
97
|
+
m({ ...e, ...l, pageNo: 1 });
|
|
98
|
+
}, O = (l) => {
|
|
99
|
+
m({ ...e, ...l });
|
|
100
|
+
}, S = (l) => {
|
|
101
|
+
m({ ...l, pageSize: p, pageNo: 1 });
|
|
102
|
+
}, D = (l, L, b, z) => {
|
|
103
|
+
const { action: k } = z;
|
|
104
|
+
if (["paginate", "sort"].includes(k)) {
|
|
105
|
+
const { current: N, pageSize: A } = l, { field: G, order: q } = b, [Q, Y] = P.sortField, [U, Z] = P.sortOrder, B = {
|
|
106
106
|
...e,
|
|
107
|
-
[Q]: q ? q === "ascend" ?
|
|
107
|
+
[Q]: q ? q === "ascend" ? U : Z : void 0,
|
|
108
108
|
[Y]: G,
|
|
109
|
-
pageNo:
|
|
110
|
-
pageSize:
|
|
109
|
+
pageNo: N,
|
|
110
|
+
pageSize: A
|
|
111
111
|
};
|
|
112
|
-
|
|
112
|
+
m(B);
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
115
|
return T(() => {
|
|
116
|
-
|
|
116
|
+
E();
|
|
117
117
|
}, []), {
|
|
118
118
|
queryParams: e,
|
|
119
|
-
search: y(
|
|
120
|
-
refresh: y(
|
|
119
|
+
search: y($),
|
|
120
|
+
refresh: y(O),
|
|
121
121
|
reset: y(S),
|
|
122
122
|
selectedRowKeys: g,
|
|
123
123
|
tableProps: {
|
|
@@ -127,15 +127,15 @@ function ce({
|
|
|
127
127
|
rowSelection: R,
|
|
128
128
|
loading: i,
|
|
129
129
|
dataSource: f,
|
|
130
|
-
pagination: { ...c, showTotal:
|
|
131
|
-
onChange: y(
|
|
130
|
+
pagination: { ...c, showTotal: M },
|
|
131
|
+
onChange: y(D),
|
|
132
132
|
locale: {
|
|
133
|
-
emptyText: i ? "" : /* @__PURE__ */
|
|
133
|
+
emptyText: i ? "" : /* @__PURE__ */ oe.jsx(J, { image: J.PRESENTED_IMAGE_SIMPLE })
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
|
-
const
|
|
138
|
+
const w = ee((r) => ({
|
|
139
139
|
modals: {},
|
|
140
140
|
toggleModal: (a, o) => r((t) => {
|
|
141
141
|
const n = t.modals[a]?.visible || !1;
|
|
@@ -166,86 +166,86 @@ const $ = ee((r) => ({
|
|
|
166
166
|
})
|
|
167
167
|
}));
|
|
168
168
|
function le(r) {
|
|
169
|
-
const a =
|
|
170
|
-
T(() => (
|
|
169
|
+
const a = w((e) => e.modals), o = w((e) => e.toggleModal), t = w((e) => e.setModal), s = w((e) => e.clearModals), n = W(), d = _([]), c = Object.keys(r);
|
|
170
|
+
T(() => (d.current = c.map(
|
|
171
171
|
(e) => `${n}-${e}`
|
|
172
172
|
), () => {
|
|
173
|
-
s(
|
|
173
|
+
s(d.current);
|
|
174
174
|
}), []);
|
|
175
175
|
const f = {};
|
|
176
176
|
return c.forEach((e) => {
|
|
177
|
-
const
|
|
177
|
+
const u = `${n}-${e}`, p = a[u] ?? { visible: !1, data: {} }, i = r[e], v = typeof i.title == "function" ? i.title(p.data) : i.title;
|
|
178
178
|
f[`${e}Modal`] = {
|
|
179
|
-
visible:
|
|
180
|
-
data:
|
|
179
|
+
visible: p.visible,
|
|
180
|
+
data: p.data,
|
|
181
181
|
modalProps: {
|
|
182
182
|
width: i.width,
|
|
183
|
-
title:
|
|
184
|
-
open:
|
|
183
|
+
title: v,
|
|
184
|
+
open: p.visible,
|
|
185
185
|
maskClosable: i.maskClosable ?? !1,
|
|
186
186
|
centered: i.centered ?? !0,
|
|
187
187
|
destroyOnHidden: i.destroyOnHidden ?? !0,
|
|
188
|
-
onCancel: i.onCancel ?? (() => t(
|
|
188
|
+
onCancel: i.onCancel ?? (() => t(u, !1))
|
|
189
189
|
},
|
|
190
190
|
drawerProps: {
|
|
191
191
|
size: i.width,
|
|
192
|
-
title:
|
|
193
|
-
open:
|
|
192
|
+
title: v,
|
|
193
|
+
open: p.visible,
|
|
194
194
|
maskClosable: i.maskClosable ?? !0,
|
|
195
195
|
destroyOnHidden: i.destroyOnHidden ?? !0,
|
|
196
196
|
placement: i.placement ?? "right",
|
|
197
|
-
onClose: i.onClose ?? (() => t(
|
|
197
|
+
onClose: i.onClose ?? (() => t(u, !1))
|
|
198
198
|
},
|
|
199
|
-
toggle: (g) => o(
|
|
200
|
-
open: (g) => t(
|
|
201
|
-
close: () => t(
|
|
199
|
+
toggle: (g) => o(u, g),
|
|
200
|
+
open: (g) => t(u, !0, g),
|
|
201
|
+
close: () => t(u, !1)
|
|
202
202
|
};
|
|
203
|
-
}), f.toggle = (e,
|
|
204
|
-
o(`${n}-${e}`,
|
|
205
|
-
}, f.open = (e,
|
|
206
|
-
t(`${n}-${e}`, !0,
|
|
203
|
+
}), f.toggle = (e, u) => {
|
|
204
|
+
o(`${n}-${e}`, u);
|
|
205
|
+
}, f.open = (e, u) => {
|
|
206
|
+
t(`${n}-${e}`, !0, u);
|
|
207
207
|
}, f.close = (e) => {
|
|
208
208
|
t(`${n}-${e}`, !1);
|
|
209
209
|
}, f;
|
|
210
210
|
}
|
|
211
211
|
function ue({ queryFn: r, params: a, dataKey: o, fieldNames: t, transform: s }) {
|
|
212
|
-
const [n,
|
|
212
|
+
const [n, d] = h({
|
|
213
213
|
list: [],
|
|
214
214
|
mapData: /* @__PURE__ */ new Map()
|
|
215
|
-
}), [c, f] = h(!
|
|
215
|
+
}), [c, f] = h(!0), e = async () => {
|
|
216
216
|
f(!0);
|
|
217
|
-
const { data: g, status:
|
|
218
|
-
if (
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
return
|
|
222
|
-
label: S[
|
|
223
|
-
value:
|
|
217
|
+
const { data: g, status: C } = await r(a);
|
|
218
|
+
if (C === "success") {
|
|
219
|
+
const M = (s ? s(g) : g) || [], { label: m, value: E } = t, $ = /* @__PURE__ */ new Map([]), O = M.map((S) => {
|
|
220
|
+
const D = S[E];
|
|
221
|
+
return $.set(D, S[m]), {
|
|
222
|
+
label: S[m],
|
|
223
|
+
value: D,
|
|
224
224
|
data: S
|
|
225
225
|
};
|
|
226
226
|
});
|
|
227
|
-
|
|
227
|
+
d((S) => ({ ...S, list: O, mapData: $ }));
|
|
228
228
|
}
|
|
229
229
|
f(!1);
|
|
230
|
-
},
|
|
230
|
+
}, u = j(e), p = y(() => {
|
|
231
231
|
e();
|
|
232
232
|
});
|
|
233
233
|
T(() => {
|
|
234
|
-
|
|
234
|
+
u();
|
|
235
235
|
}, []);
|
|
236
|
-
const { list: i, mapData:
|
|
236
|
+
const { list: i, mapData: v } = n;
|
|
237
237
|
return {
|
|
238
238
|
loading: c,
|
|
239
|
-
refresh:
|
|
239
|
+
refresh: p,
|
|
240
240
|
[`${o}Options`]: i,
|
|
241
|
-
[`${o}Map`]:
|
|
241
|
+
[`${o}Map`]: v
|
|
242
242
|
};
|
|
243
243
|
}
|
|
244
244
|
function de(r, a) {
|
|
245
|
-
const [o, t] = h(!1), { message: s } = K.useApp(), n = X(async (
|
|
245
|
+
const [o, t] = h(!1), { message: s } = K.useApp(), n = X(async (d) => {
|
|
246
246
|
try {
|
|
247
247
|
t(!0);
|
|
248
|
-
const c = await r(
|
|
248
|
+
const c = await r(d), { status: f, info: e } = c;
|
|
249
249
|
f == "success" && (a(c), s.success(e));
|
|
250
250
|
} catch {
|
|
251
251
|
}
|
|
@@ -262,34 +262,35 @@ function pe({
|
|
|
262
262
|
dataType: o,
|
|
263
263
|
dataKey: t,
|
|
264
264
|
initialValue: s,
|
|
265
|
-
transform: n
|
|
265
|
+
transform: n,
|
|
266
|
+
success: d
|
|
266
267
|
}) {
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
const { data: M, status:
|
|
270
|
-
if (
|
|
271
|
-
const
|
|
272
|
-
|
|
268
|
+
const c = s || (o === "Array" ? [] : /* @__PURE__ */ Object.create({})), [f, e] = h(c), [u, p] = h(!0), i = async (R) => {
|
|
269
|
+
p(!0);
|
|
270
|
+
const { data: M, status: m } = await r(R ?? a), E = M == null ? M : o === "Array" ? [] : /* @__PURE__ */ Object.create({});
|
|
271
|
+
if (m === "success") {
|
|
272
|
+
const $ = n ? n(E) : E;
|
|
273
|
+
d && d(E), e($);
|
|
273
274
|
}
|
|
274
|
-
|
|
275
|
-
},
|
|
275
|
+
p(!1);
|
|
276
|
+
}, v = j(i);
|
|
276
277
|
T(() => {
|
|
277
|
-
|
|
278
|
+
v();
|
|
278
279
|
}, []);
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
})
|
|
280
|
+
const g = y((R) => {
|
|
281
|
+
i(R);
|
|
282
|
+
}), C = `set${t.charAt(0).toUpperCase()}${t.slice(1)}`;
|
|
282
283
|
return {
|
|
283
|
-
[t]:
|
|
284
|
-
|
|
285
|
-
|
|
284
|
+
[t]: f,
|
|
285
|
+
[C]: e,
|
|
286
|
+
loading: u,
|
|
287
|
+
refresh: g
|
|
286
288
|
};
|
|
287
289
|
}
|
|
288
290
|
export {
|
|
289
291
|
ie as configureTableOption,
|
|
290
292
|
le as useCreateModal,
|
|
291
293
|
de as useFormSubmit,
|
|
292
|
-
y as useMemoizedFn,
|
|
293
294
|
pe as useRequestQuery,
|
|
294
295
|
ue as useSelectOptions,
|
|
295
296
|
ce as useTableList
|