@ithinkdt/page 4.0.18 → 4.0.19

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 CHANGED
@@ -1 +1,546 @@
1
1
  # @ithinkdt/page
2
+
3
+ ## 许可证
4
+
5
+ MIT
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm i @ithinkdt/page
11
+ ```
12
+
13
+ > 需要您自行安装依赖 `vue@3` 与 `vue-router@4`。
14
+
15
+ ## 介绍
16
+
17
+ `@ithinkdt/page` 提供 iThinkDT 页面开发的核心组合式 API,包括:
18
+
19
+ - 数据源管理(远程分页/本地数据)
20
+ - 前端分页
21
+ - 表单管理(查询筛选、编辑表单、表单弹窗)
22
+ - 表格列定义与自定义列
23
+ - CRUD 操作(新增、编辑、查看、删除)
24
+ - 描述详情展示
25
+ - 模态框(对话框/抽屉)
26
+ - 表单校验规则
27
+
28
+ ## 初始化
29
+
30
+ ### 注册插件
31
+
32
+ ```ts
33
+ import { createApp } from 'vue'
34
+ import pagePlugin from '@ithinkdt/page'
35
+
36
+ const app = createApp(App)
37
+
38
+ app.use(pagePlugin, {
39
+ i18n,
40
+ getConfirmRenderer: () => renderConfirm,
41
+ getModalRenderer: () => renderModal,
42
+ getFormRenderer: () => renderForm,
43
+ getFormItemRenderer: {
44
+ text: () => renderTextInput,
45
+ },
46
+ getDescriptionRenderer: {
47
+ text: () => (value) => value,
48
+ },
49
+ getTableActionsRenderer: () => renderTableActions,
50
+ defaultPageSize: 20,
51
+ defaultFilterCached: true,
52
+ })
53
+ ```
54
+
55
+ ## 组合式 API
56
+
57
+ ### `useDs` 数据源
58
+
59
+ 用于管理远程分页数据或本地数据列表,支持增删改操作:
60
+
61
+ ```ts
62
+ import { useDs } from '@ithinkdt/page'
63
+ import { userApi } from '@/api/user'
64
+
65
+ const ds = useDs(
66
+ async ({ currentPage, pageSize, sortField, sortOrder }, filter) => {
67
+ const res = await userApi.page({ currentPage, pageSize, sortField, sortOrder, filter })
68
+ return res
69
+ },
70
+ { pagination: 'remote', defaultPageSize: 20 },
71
+ )
72
+
73
+ // 刷新数据
74
+ ds.pull({ currentPage: 1, pageSize: 20 })
75
+
76
+ // 本地插入一条
77
+ ds.insert({ id: 'new', name: '张三' })
78
+
79
+ // 本地更新一条
80
+ ds.set('id-xxx', { name: '李四' })
81
+
82
+ // 本地删除一条
83
+ ds.remove('id-xxx')
84
+ ```
85
+
86
+ ### `useFilterHelper` 查询筛选表单
87
+
88
+ ```ts
89
+ import { useFilterHelper } from '@ithinkdt/page'
90
+
91
+ const { model, items, reset, validate } = useFilterHelper(
92
+ ({ it, model, validate }) => [
93
+ it('name', '用户名', 'text', { placeholder: '请输入用户名' }),
94
+ it('status', '状态', 'select', { selectProps: { options: statusOptions } }),
95
+ ],
96
+ { cached: true },
97
+ )
98
+ ```
99
+
100
+ ### `useFormHelper` 表单管理
101
+
102
+ ```ts
103
+ import { useFormHelper } from '@ithinkdt/page'
104
+
105
+ const { model, items, validate } = useFormHelper(
106
+ ({ it, model, validate, group, reset }) => [
107
+ it('name', '名称', 'text', { required: true }),
108
+ it('email', '邮箱', 'text', { rule: email('请输入有效邮箱') }),
109
+ group(computed(() => model.country === 'China'), [
110
+ it('phone', '手机号', 'text'),
111
+ ]),
112
+ ],
113
+ {
114
+ initial: { name: '' },
115
+ onChange(name, value) {
116
+ console.log(`${name} changed:`, value)
117
+ },
118
+ },
119
+ )
120
+
121
+ // 校验
122
+ const [valid] = await validate()
123
+ if (valid) {
124
+ await submit(model)
125
+ }
126
+ ```
127
+
128
+ ### `useFormModal` 表单弹窗
129
+
130
+ 将表单放入模态框中展示,常用于编辑场景:
131
+
132
+ ```ts
133
+ import { useFormModal } from '@ithinkdt/page'
134
+
135
+ const { open, close } = useFormModal({
136
+ title: '编辑用户',
137
+ type: 'dialog',
138
+ width: 600,
139
+ items: ({ it, model }) => [
140
+ it('name', '名称', 'text', { required: true }),
141
+ ],
142
+ onSubmit: async (data) => {
143
+ await userApi.save(data)
144
+ },
145
+ initial: { name: '' },
146
+ })
147
+
148
+ // 打开弹窗,传入初始数据
149
+ await open({ id: '1', name: '张三' })
150
+ ```
151
+
152
+ ### `useSimpleCrud` CRUD 操作
153
+
154
+ 封装新增、编辑、查看、删除的标准流程:
155
+
156
+ ```ts
157
+ import { useSimpleCrud } from '@ithinkdt/page'
158
+
159
+ const { onAdd, onEdit, onView, onDel } = useSimpleCrud({
160
+ get: id => userApi.get(id),
161
+ save: data => userApi.save(data),
162
+ delete: id => userApi.delete(id),
163
+ // 增删改查用统一的表单
164
+ items: ({ it }) => [
165
+ it('name', '名称', 'text', { required: true }),
166
+ it('status', '状态', 'select'),
167
+ it('name', '名称', 'text'),
168
+ it('email', '邮箱', 'text'),
169
+ ],
170
+ // 差别较大的时候,可以分开定义
171
+ createItems: ({ it }) => [
172
+ it('name', '名称', 'text', { required: true }),
173
+ ],
174
+ editItems: ({ it, model }) => [
175
+ it('name', '名称', 'text', { required: true }),
176
+ it('status', '状态', 'select'),
177
+ ],
178
+ viewItems: ({ it }) => [
179
+ it('name', '名称', 'text'),
180
+ it('email', '邮箱', 'text'),
181
+ ],
182
+ })
183
+
184
+ // 新增
185
+ await onAdd({ name: '张三' })
186
+
187
+ // 编辑
188
+ await onEdit('id-xxx')
189
+
190
+ // 查看
191
+ await onView('id-xxx')
192
+
193
+ // 删除(带确认弹窗)
194
+ await onDel('id-xxx')
195
+ ```
196
+
197
+ ### `useDeleteHelper` 删除确认
198
+
199
+ ```ts
200
+ import { useDeleteHelper } from '@ithinkdt/page'
201
+
202
+ const { onDel } = useDeleteHelper({
203
+ delete: ids => Array.isArray(ids) ? userApi.deleteBatch(ids) : userApi.delete(id),
204
+ })
205
+
206
+ // 单个删除
207
+ await onDel('id-xxx')
208
+
209
+ // 批量删除
210
+ await onDel(['id-1', 'id-2'])
211
+
212
+ // 自定义提示
213
+ await onDel('id-xxx', '确认删除', '该操作不可撤销')
214
+ ```
215
+
216
+ ### `useTableHelper` 表格列定义
217
+
218
+ ```ts
219
+ import { useTableHelper, calcActionWidth } from '@ithinkdt/page'
220
+
221
+ const { columns, custom } = useTableHelper(
222
+ ({ col, cols, group }) => [
223
+ col('index', '#', (_, __, i) => i + 1, { width: 60 }),
224
+ col('name', '用户名', { width: 120 }),
225
+ col('email', '邮箱', 'text', { ellipsis: true }),
226
+ col('status', '状态', { width: 100, render: v => v ? '启用' : '禁用' }),
227
+ cols('操作', [
228
+ col('$edit', '', (_, record) => h('Button', { onClick: () => onEdit(record) }, '编辑')),
229
+ col('$delete', '', (_, record) => h('Button', { onClick: () => onDel(record) }, '删除')),
230
+ ], { fixed: 'right' }),
231
+ ],
232
+ {
233
+ index: i => i + (params.currentPage - 1) * params.pageSize,
234
+ selectable: canSelect && (record) => record.status === 'selectable',
235
+ customizable: true,
236
+ actions: [
237
+ { preset: 'edit', onClick: (record) => onEdit(record) },
238
+ { preset: 'delete', onClick: (record) => onDel(record) },
239
+ ],
240
+ },
241
+ )
242
+ ```
243
+
244
+ ### `useDescriptionsHelper` 描述详情
245
+
246
+ ```ts
247
+ import { useDescriptionsHelper } from '@ithinkdt/page'
248
+
249
+ const { items, model, reset } = useDescriptionsHelper(
250
+ ({ it, group }) => [
251
+ it('name', '用户名', 'text'),
252
+ it('email', '邮箱'),
253
+ it('status', '状态', (value) => value ? '启用' : '禁用'),
254
+ group('contact', '联系方式', [
255
+ it('phone', '手机号', 'text'),
256
+ it('address', '地址', 'text'),
257
+ ]),
258
+ ],
259
+ )
260
+
261
+ // 设置数据
262
+ reset({ name: '张三', email: 'zhangsan@example.com', phone: '13800000000' })
263
+ ```
264
+
265
+ ### `useModal` 模态框
266
+
267
+ ```ts
268
+ import { useModal, useModalRef } from '@ithinkdt/page'
269
+
270
+ const xxx = ref('123')
271
+ const modal = useModal({
272
+ type: 'drawer',
273
+ title: '编辑',
274
+ width: 600,
275
+ content: () => <MyForm xxx={xxx.value} />,
276
+ onConfirm: async () => {
277
+ await submit()
278
+ },
279
+ })
280
+
281
+ // 打开
282
+ await modal.open()
283
+
284
+ // 关闭
285
+ modal.close()
286
+ ```
287
+
288
+ ### `useDataPagination` 前端分页
289
+
290
+ ```ts
291
+ import { useDataPagination } from '@ithinkdt/page'
292
+ import { ref } from 'vue'
293
+
294
+ const allData = ref([...])
295
+ const { data, pagination, paginate } = useDataPagination(allData, { pageSize: 10 })
296
+
297
+ // data 为当前页数据,pagination 为分页状态
298
+ ```
299
+
300
+ ## 校验规则
301
+
302
+ `@ithinkdt/page/rules` 提供常用的表单校验规则:
303
+
304
+ ```ts
305
+ import { required, min, max, email, phone, url, idNo, chinese, noChinese } from '@ithinkdt/page/rules'
306
+
307
+ const rules = {
308
+ name: required('请输入名称'),
309
+ age: min(0, val => '年龄不能小于0'),
310
+ email: email('请输入有效邮箱'),
311
+ phone: phone('请输入有效手机号'),
312
+ }
313
+ ```
314
+
315
+ ## API 参考
316
+
317
+ ### `useDs(fetch, options?)`
318
+
319
+ | 参数 | 说明 |
320
+ |------|------|
321
+ | `fetch` | `(sortParams, filter) => Promise<{ total, records } \| T[]>`,数据获取函数 |
322
+ | `options.pagination` | `'remote'` 远程分页,`false` 本地数据 |
323
+ | `options.defaultPageSize` | 默认分页大小,默认 `10` |
324
+ | `options.keyField` | 主键字段名,默认 `'key'` |
325
+ | `options.defaultSortField` | 默认排序字段 |
326
+ | `options.defaultSortOrder` | 默认排序方向,`'asc'` / `'desc'` |
327
+ | `options.shallow` | 是否使用 `shallowReactive`,默认 `true` |
328
+ | `options.immediate` | 是否立即执行 fetch,默认 `false` |
329
+ | `options.resetOnFetch` | fetch 时是否先重置为空,默认 `false` |
330
+
331
+ | 属性/方法 | 说明 |
332
+ |-----------|------|
333
+ | `state` | 响应式数据(远程分页模式下含 `total` 与 `records`) |
334
+ | `loading` | 加载状态 |
335
+ | `error` | 错误信息 |
336
+ | `execute(...params)` | 执行 fetch 刷新数据 |
337
+ | `get(key)` | 根据主键获取数据项 |
338
+ | `insert(item, index?)` | 插入数据项 |
339
+ | `set(key, item)` | 更新数据项 |
340
+ | `remove(key)` | 删除数据项 |
341
+
342
+ ### `useFilterHelper(items, options?)`
343
+
344
+ | 参数 | 说明 |
345
+ |------|------|
346
+ | `items` | `(helper) => FormItemOptions[]`,表单项定义函数 |
347
+ | `options.initial` | 表单初始值 |
348
+ | `options.rules` | 校验规则对象或函数 |
349
+ | `options.onChange` | `(name, value) => void`,字段变更回调 |
350
+ | `options.cached` | 是否缓存筛选表单,默认取插件 `defaultFilterCached` |
351
+ | `options.cacheVersion` | 缓存版本号,默认 `1` |
352
+ | `options.customizable` | 是否允许自定义筛选项显隐 |
353
+
354
+ | 属性/方法 | 说明 |
355
+ |-----------|------|
356
+ | `model` | 响应式表单数据 |
357
+ | `items` | 响应式表单项列表 |
358
+ | `reset(initial?)` | 重置表单 |
359
+ | `validate()` | 校验表单 |
360
+ | `invalid` | 是否校验不通过 |
361
+ | `validation` | 校验结果详情 |
362
+ | `custom(modify)` | 自定义筛选项显隐/排序 |
363
+
364
+ ### `useFormHelper(items, options?)`
365
+
366
+ | 参数 | 说明 |
367
+ |------|------|
368
+ | `items` | `(helper) => FormItemOptions[]`,表单项定义函数 |
369
+ | `options.initial` | 表单初始值 |
370
+ | `options.rules` | 校验规则对象或函数 |
371
+ | `options.onChange` | `(name, value) => void`,字段变更回调 |
372
+
373
+ | 属性/方法 | 说明 |
374
+ |-----------|------|
375
+ | `model` | 响应式表单数据 |
376
+ | `items` | 响应式表单项列表 |
377
+ | `reset(initial?)` | 重置表单(`overwrite=true` 覆盖,`reinit=true` 同时重建表单项) |
378
+ | `reinit()` | 重新初始化表单项 |
379
+ | `validate()` | 校验表单 |
380
+ | `invalid` | 是否校验不通过 |
381
+ | `validation` | 校验结果详情 |
382
+ | `beforeSubmit(prop)` | 提交前校验(支持单字段或数组) |
383
+
384
+ ### `useFormModal(options)`
385
+
386
+ | 参数 | 说明 |
387
+ |------|------|
388
+ | `options.title` | 弹窗标题 |
389
+ | `options.type` | `'dialog'` / `'drawer'`,弹窗类型 |
390
+ | `options.width` | 弹窗宽度 |
391
+ | `options.items` | `(helper) => FormItemOptions[]`,表单项定义函数 |
392
+ | `options.initial` | 表单初始值 |
393
+ | `options.rules` | 校验规则 |
394
+ | `options.onSubmit` | `(data) => Promise<void>`,提交回调 |
395
+ | `options.loading` | 加载状态 |
396
+ | `options.readonly` | 是否只读 |
397
+ | `options.maskClosable` | 点击遮罩是否关闭,默认 `false` |
398
+ | `options.closable` | 是否显示关闭按钮,默认 `true` |
399
+
400
+ | 属性/方法 | 说明 |
401
+ |-----------|------|
402
+ | `open(initial?, title?)` | 打开表单弹窗 |
403
+ | `close()` | 关闭弹窗 |
404
+
405
+ ### `useSimpleCrud(options)`
406
+
407
+ | 参数 | 说明 |
408
+ |------|------|
409
+ | `options.get` | `(id) => Promise<Entity>`,获取详情 |
410
+ | `options.post` / `save` | `(data) => Promise<Entity>`,新增 |
411
+ | `options.put` | `(data) => Promise<Entity>`,编辑 |
412
+ | `options.delete` | `(id) => Promise<void>`,删除 |
413
+ | `options.createItems` | 新建表单的表单项定义函数 |
414
+ | `options.editItems` | 编辑表单的表单项定义函数 |
415
+ | `options.viewItems` | 查看表单的表单项定义函数 |
416
+ | `options.items` / `formItems` | 新建和编辑共用的表单项定义函数 |
417
+ | `options.width` | 弹窗宽度,可按 type 返回不同宽度 |
418
+ | `options.cols` | 表单列数,可按 type 返回不同列数 |
419
+ | `options.modalType` | `'dialog'` / `'drawer'`,可按 type 返回不同类型 |
420
+ | `options.deleteButtonText` | 删除按钮文案 |
421
+
422
+ | 方法 | 说明 |
423
+ |------|------|
424
+ | `onAdd(initial?, title?)` | 新增 |
425
+ | `onEdit(dataOrKey, title?)` | 编辑 |
426
+ | `onView(dataOrKey, title?)` | 查看 |
427
+ | `onDel(keyOrKeys, title?, tip?)` | 删除 |
428
+
429
+ ### `useDeleteHelper(options)`
430
+
431
+ | 参数 | 说明 |
432
+ |------|------|
433
+ | `options.delete` | `(ids) => Promise<void>`,删除请求函数 |
434
+ | `options.keyField` | 主键字段名,默认使用插件注入的 `keyField` |
435
+ | `options.deleteButtonText` | 删除按钮文案 |
436
+ | `options.renderDelete` | 自定义删除确认内容渲染函数 |
437
+
438
+ | 方法 | 说明 |
439
+ |------|------|
440
+ | `onDel(keyOrKeys, title?, tip?)` | 删除(带确认弹窗) |
441
+
442
+ ### `useTableHelper(columns, options?)`
443
+
444
+ | 参数 | 说明 |
445
+ |------|------|
446
+ | `columns` | `(helper) => TableColumnOptions[]`,列定义函数 |
447
+ | `options.index` | 是否显示序号列或自定义序号渲染函数 `(i, record) => VNodeChild` |
448
+ | `options.indexTitle` | 序号列标题,默认 `'#'` |
449
+ | `options.selectable` | 是否可选择或自定义可选择判断函数 `(record) => boolean` |
450
+ | `options.selectType` | `'multiple'` / `'single'`,选择类型 |
451
+ | `options.actions` | 操作按钮列表,内置预设 `{ preset: 'edit' \| 'view' \| 'delete' }` |
452
+ | `options.actionTitle` | 操作列标题,默认 `'操作'` |
453
+ | `options.actionWidth` | 操作列宽度,字符串或根据文案计算宽度函数 |
454
+ | `options.actionHidden` | 是否隐藏操作列 |
455
+ | `options.customizable` | 是否允许自定义列显隐/固定/宽度/排序 |
456
+ | `options.expandable` | 是否可展开行或自定义判断函数 `(record) => boolean` |
457
+ | `options.renderExpand` | `(record, i) => VNodeChild`,展开行渲染函数 |
458
+
459
+ | 属性/方法 | 说明 |
460
+ |-----------|------|
461
+ | `columns` | 响应式列定义 |
462
+ | `custom(modify)` | 自定义列显隐/固定/宽度/排序;传 `true` 重置为默认 |
463
+ | `dataMode` | 数据模式(`all` / `selection`) |
464
+ | `reinit()` | 重新初始化列定义 |
465
+
466
+ ### `useDescriptionsHelper(items)`
467
+
468
+ | 参数 | 说明 |
469
+ |------|------|
470
+ | `items` | `(helper) => DescriptionItem[]`,描述项定义函数 |
471
+
472
+ | 属性/方法 | 说明 |
473
+ |-----------|------|
474
+ | `items` | 响应式描述项列表 |
475
+ | `model` | 响应式数据 |
476
+ | `reset(model?, reinit?)` | 重置数据(`reinit=true` 同时重建描述项) |
477
+ | `reinit()` | 重新初始化描述项 |
478
+
479
+ ### `useModal(options)`
480
+
481
+ | 参数 | 说明 |
482
+ |------|------|
483
+ | `options.type` | `'dialog'` / `'drawer'`,类型 |
484
+ | `options.title` | 标题 |
485
+ | `options.content` | 内容 |
486
+ | `options.width` | 宽度 |
487
+ | `options.height` | 高度 |
488
+ | `options.confirmText` | 确认按钮文案 |
489
+ | `options.cancelText` | 取消按钮文案 |
490
+ | `options.confirmLoading` | 确认按钮加载状态 |
491
+ | `options.cancelLoading` | 取消按钮加载状态 |
492
+ | `options.onConfirm` | `() => Promise<boolean \| void>`,确认回调,返回 `false` 阻止关闭 |
493
+ | `options.onCancel` | `() => Promise<boolean \| void>`,取消回调 |
494
+ | `options.onClose` | `() => Promise<boolean \| void>`,关闭回调 |
495
+ | `options.closable` | 是否显示关闭按钮,默认 `true` |
496
+ | `options.maskClosable` | 点击遮罩是否关闭,默认 `false` |
497
+ | `options.footer` | 自定义底部内容,`null` 隐藏底部 |
498
+
499
+ | 方法 | 说明 |
500
+ |------|------|
501
+ | `open(title?)` | 打开模态框 |
502
+ | `close()` | 关闭模态框 |
503
+
504
+ ### `useModalRef()`
505
+
506
+ | 属性/方法 | 说明 |
507
+ |-----------|------|
508
+ | `inModal` | 是否在模态框上下文内 |
509
+ | `visible` | 模态框可见状态 |
510
+ | `close()` | 关闭模态框 |
511
+ | `setTitle(title)` | 设置模态框标题 |
512
+
513
+ ### `useDataPagination(data, options?)`
514
+
515
+ | 参数 | 说明 |
516
+ |------|------|
517
+ | `data` | `MaybeRef<T[]>`,原始数据列表 |
518
+ | `options.pageSize` | 每页条数,默认取插件 `defaultPageSize` |
519
+ | `options.currentPage` | 初始页码,默认 `1` |
520
+ | `options.updateOnChange` | 数据变化时是否重置到第一页,默认 `true` |
521
+
522
+ | 属性/方法 | 说明 |
523
+ |-----------|------|
524
+ | `data` | 当前页数据 |
525
+ | `pagination` | 分页状态(`pageSize`, `currentPage`) |
526
+ | `paginate(params)` | 切换分页 |
527
+
528
+ ### 校验规则(`@ithinkdt/page/rules`)
529
+
530
+ | 方法 | 说明 |
531
+ |------|------|
532
+ | `required(message, options?)` | 必填校验,`options.required` 控制是否必填,`options.trigger` 触发时机 |
533
+ | `min(min, message, options?)` | 最小值/最小长度,`min` 为最小阈值 |
534
+ | `max(max, message, options?)` | 最大值/最大长度,`max` 为最大阈值 |
535
+ | `minmax(min, max, message, options?)` | 同时校验最小值/最大长度与最大值/最大长度 |
536
+ | `email(message, options?)` | 邮箱格式 |
537
+ | `phone(message, options?)` | 手机号格式(`1` 开头 11 位数字) |
538
+ | `url(message, options?)` | URL 格式 |
539
+ | `idNo(message, options?)` | 身份证格式 |
540
+ | `plateNo(message, options?)` | 车牌号格式 |
541
+ | `chinese(message, options?)` | 纯中文 |
542
+ | `noChinese(message, options?)` | 不含中文 |
543
+ | `pattern(pattern, message, options?)` | 自定义正则,`pattern` 为 `RegExp` 或正则字符串 |
544
+ | `isRequiredRule(rule)` | 判断是否为 `required` 规则 |
545
+
546
+ > `message` 参数为 `(value, params) => string`,`options.trigger` 默认 `'blur'`(`required` 默认 `['input', 'blur', 'change']`)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ithinkdt/page",
3
- "version": "4.0.18",
3
+ "version": "4.0.19",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "iThinkDT Page",
@@ -39,19 +39,19 @@
39
39
  },
40
40
  "sideEffects": false,
41
41
  "dependencies": {
42
- "@vueuse/core": "^14.2.1",
43
- "nanoid": "^5.1.7",
44
- "@ithinkdt/common": "^4.0.7"
42
+ "@vueuse/core": "^14.3.0",
43
+ "nanoid": "^5.1.11",
44
+ "@ithinkdt/common": "^4.0.8"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "vue": ">=3.5",
48
48
  "vue-router": ">=4.5"
49
49
  },
50
50
  "devDependencies": {
51
- "typescript": "~6.0.2",
52
- "vite": "^8.0.8",
53
- "vue": "^3.5.32",
54
- "vue-router": "^5.0.4"
51
+ "typescript": "~6.0.3",
52
+ "vite": "^8.0.10",
53
+ "vue": "^3.5.34",
54
+ "vue-router": "^5.0.6"
55
55
  },
56
56
  "scripts": {
57
57
  "release": "pnpm publish --no-git-checks"
package/src/form.d.ts CHANGED
@@ -124,7 +124,7 @@ export interface FormItemHelper<Model extends {}> extends FromValidation<Model>
124
124
  ): FormItemOptions<Partial<Model>>
125
125
  group(
126
126
  this: void,
127
- visible: MaybeRef<boolean>,
127
+ visibility: MaybeRef<boolean>,
128
128
  items: (FormItemOptions<Partial<Model>> | Falsely)[]
129
129
  ): FormItemOptions<Partial<Model>>[]
130
130
  }
@@ -162,7 +162,25 @@ export declare function useFormHelper<Model extends {}>(
162
162
  options?: FormHelperOptions<Model>,
163
163
  ): FormHelper<Model>
164
164
 
165
+ export interface FilterHelperOptions<Model extends {}> extends FormHelperOptions<Model> {
166
+ /**
167
+ * 自定义
168
+ */
169
+ customizable?: boolean | string | undefined
170
+ /**
171
+ * 缓存表单
172
+ */
173
+ cached?: boolean | string | undefined
174
+ /**
175
+ * 缓存版本,默认 1
176
+ */
177
+ cacheVersion?: number | undefined
178
+ }
179
+
165
180
  export interface FilterHelper<Model extends {}> extends FormHelper<Model> {
181
+ /**
182
+ * 自定义表单项
183
+ */
166
184
  custom(this: void, modify: {
167
185
  key: string
168
186
  hidden?: boolean | undefined
@@ -173,20 +191,7 @@ export interface FilterHelper<Model extends {}> extends FormHelper<Model> {
173
191
 
174
192
  export declare function useFilterHelper<Model extends {}>(
175
193
  items: ((helper: FormItemHelper<Model>) => (FormItemOptions<Partial<Model>> | Falsely)[]),
176
- options?: FormHelperOptions<Model> & {
177
- /**
178
- * 缓存表单
179
- */
180
- cached?: boolean | string | undefined
181
- /**
182
- * 缓存版本,默认 1
183
- */
184
- cacheVersion?: number
185
- /**
186
- * 自定义
187
- */
188
- customizable?: boolean | string | undefined
189
- },
194
+ options?: FilterHelperOptions<Model>,
190
195
  ): FilterHelper<Model>
191
196
 
192
197
  export interface FormRenderExtraOptions {
package/src/form.js CHANGED
@@ -1,4 +1,4 @@
1
- import { toReactive, useSessionStorage, useStorageAsync } from '@vueuse/core'
1
+ import { StorageSerializers, toReactive, useStorage, useStorageAsync } from '@vueuse/core'
2
2
  import { nanoid } from 'nanoid'
3
3
  import { computed, h, hasInjectionContext, inject, reactive, readonly, ref, shallowReactive, shallowRef, toRaw, unref } from 'vue'
4
4
  import { useRoute } from 'vue-router'
@@ -93,11 +93,11 @@ export function useFormHelper(items, { initial: initial0, rules, onChange, inFil
93
93
  return { ...options, name, render: render0, label, __key: nanoid(), beforeSubmit }
94
94
  }
95
95
 
96
- const group = (visible, items) => {
96
+ const group = (visibility, items) => {
97
97
  return items.map((item) => {
98
98
  return {
99
99
  ...item,
100
- hidden: computed(() => unref(visible) == false || unref(item.hidden)),
100
+ hidden: computed(() => unref(visibility) == false || unref(item.hidden)),
101
101
  }
102
102
  })
103
103
  }
@@ -320,7 +320,7 @@ export function useFormHelper(items, { initial: initial0, rules, onChange, inFil
320
320
  }
321
321
 
322
322
  export function useFilterHelper(items, { customizable = false, cached, cacheVersion = 1, ...options } = {}) {
323
- const { defaultFilterCached, customizationStorage = localStorage } = (hasInjectionContext() ? inject(PAGE_INJECTION, pageInjection) : pageInjection) ?? {}
323
+ const { defaultFilterCached, customizationStorage = localStorage, cacheStorage = sessionStorage } = (hasInjectionContext() ? inject(PAGE_INJECTION, pageInjection) : pageInjection) ?? {}
324
324
  let cacheFn
325
325
  const returns = useFormHelper(items, {
326
326
  ...options,
@@ -367,7 +367,10 @@ export function useFilterHelper(items, { customizable = false, cached, cacheVers
367
367
  customizable = 'filter-form-customization#' + useRoute().name
368
368
  }
369
369
  const customs = customizable
370
- ? useStorageAsync(customizable, getDefaultCustom, customizationStorage, { onReady: reinit })
370
+ ? useStorageAsync(customizable, getDefaultCustom, customizationStorage, {
371
+ onReady: reinit,
372
+ serializer: StorageSerializers.object,
373
+ })
371
374
  : ref(getDefaultCustom())
372
375
 
373
376
  if (!customizable) {
@@ -382,10 +385,13 @@ export function useFilterHelper(items, { customizable = false, cached, cacheVers
382
385
  if (cached) {
383
386
  if (cacheVersion > 1) {
384
387
  for (let i = 1; i < cacheVersion; i++) {
385
- sessionStorage.removeItem(cached + '-v' + i)
388
+ cacheStorage.removeItem(cached + '-v' + i)
386
389
  }
387
390
  }
388
- const cachedModel = useSessionStorage(cached + '-v' + cacheVersion, {})
391
+ const cachedModel = useStorage(cached + '-v' + cacheVersion, { ...returns.model }, cacheStorage, {
392
+ mergeDefaults: true,
393
+ serializer: StorageSerializers.object,
394
+ })
389
395
  cachedModel.value && returns.reset(cachedModel.value)
390
396
 
391
397
  cacheFn = (model) => {
package/src/plugin.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { StorageLikeAsync } from '@vueuse/core'
1
+ import { StorageLike, StorageLikeAsync } from '@vueuse/core'
2
2
  import { DefineComponent, InjectionKey, VNodeChild } from 'vue'
3
3
 
4
4
  import { I18nComposable } from '@ithinkdt/common/i18n'
@@ -56,6 +56,11 @@ export interface PageOptions extends PresetDataSourceOptions {
56
56
  * 自定义数据存储实现,默认使用 localStorage
57
57
  */
58
58
  customizationStorage?: StorageLikeAsync
59
+
60
+ /**
61
+ * 自定义缓存实现,默认使用 sessionStorage
62
+ */
63
+ cacheStorage?: StorageLike
59
64
  }
60
65
 
61
66
  /**
package/src/table.d.ts CHANGED
@@ -118,7 +118,7 @@ export interface TableColumnHelper<Model extends {}> {
118
118
  ): TableColumnOptions<Model>
119
119
  group(
120
120
  this: void,
121
- hidden: MaybeRef<boolean>,
121
+ visibility: MaybeRef<boolean>,
122
122
  columns: (TableColumnOptions<Model> | Falsely)[]
123
123
  ): TableColumnOptions<Model>[]
124
124
  }
package/src/table.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useStorageAsync } from '@vueuse/core'
1
+ import { StorageSerializers, useStorageAsync } from '@vueuse/core'
2
2
  import { nanoid } from 'nanoid'
3
3
  import { computed, hasInjectionContext, inject, reactive, ref, shallowReactive, shallowRef, toValue, unref } from 'vue'
4
4
  import { useRoute } from 'vue-router'
@@ -112,11 +112,11 @@ export function useTableHelper(columns, { customizable, actions, actionWidth, ac
112
112
  ...options,
113
113
  }
114
114
  }
115
- const group = (visible, cols) => {
115
+ const group = (visibility, cols) => {
116
116
  return cols.map((col) => {
117
117
  return {
118
118
  ...col,
119
- hidden: computed(() => unref(visible) === false || unref(col.hidden)),
119
+ hidden: computed(() => unref(visibility) === false || unref(col.hidden)),
120
120
  }
121
121
  })
122
122
  }
@@ -278,7 +278,10 @@ export function useTableHelper(columns, { customizable, actions, actionWidth, ac
278
278
  customizable = 'table-column-customization#' + useRoute().name
279
279
  }
280
280
  const customs = customizable
281
- ? useStorageAsync(customizable, getDefaultCustom, customizationStorage, { onReady: reinit })
281
+ ? useStorageAsync(customizable, getDefaultCustom, customizationStorage, {
282
+ onReady: reinit,
283
+ serializer: StorageSerializers.object,
284
+ })
282
285
  : ref(getDefaultCustom())
283
286
 
284
287
  if (!customizable) {