@ithinkdt/page
许可证
MIT
安装
npm i @ithinkdt/page
需要您自行安装依赖 vue@3 与 vue-router@4。
介绍
@ithinkdt/page 提供 iThinkDT 页面开发的核心组合式 API,包括:
- 数据源管理(远程分页/本地数据)
- 前端分页
- 表单管理(查询筛选、编辑表单、表单弹窗)
- 表格列定义与自定义列
- CRUD 操作(新增、编辑、查看、删除)
- 描述详情展示
- 模态框(对话框/抽屉)
- 表单校验规则
初始化
注册插件
import { createApp } from 'vue'
import pagePlugin from '@ithinkdt/page'
const app = createApp(App)
app.use(pagePlugin, {
i18n,
getConfirmRenderer: () => renderConfirm,
getModalRenderer: () => renderModal,
getFormRenderer: () => renderForm,
getFormItemRenderer: {
text: () => renderTextInput,
},
getDescriptionRenderer: {
text: () => (value) => value,
},
getTableActionsRenderer: () => renderTableActions,
defaultPageSize: 20,
defaultFilterCached: true,
})
组合式 API
useDs 数据源
用于管理远程分页数据或本地数据列表,支持增删改操作:
import { useDs } from '@ithinkdt/page'
import { userApi } from '@/api/user'
const ds = useDs(
async ({ currentPage, pageSize, sortField, sortOrder }, filter) => {
const res = await userApi.page({ currentPage, pageSize, sortField, sortOrder, filter })
return res
},
{ pagination: 'remote', defaultPageSize: 20 },
)
// 刷新数据
ds.pull({ currentPage: 1, pageSize: 20 })
// 本地插入一条
ds.insert({ id: 'new', name: '张三' })
// 本地更新一条
ds.set('id-xxx', { name: '李四' })
// 本地删除一条
ds.remove('id-xxx')
useFilterHelper 查询筛选表单
import { useFilterHelper } from '@ithinkdt/page'
const { model, items, reset, validate } = useFilterHelper(
({ it, model, validate }) => [
it('name', '用户名', 'text', { placeholder: '请输入用户名' }),
it('status', '状态', 'select', { selectProps: { options: statusOptions } }),
],
{ cached: true },
)
useFormHelper 表单管理
import { useFormHelper } from '@ithinkdt/page'
const { model, items, validate } = useFormHelper(
({ it, model, validate, group, reset }) => [
it('name', '名称', 'text', { required: true }),
it('email', '邮箱', 'text', { rule: email('请输入有效邮箱') }),
group(computed(() => model.country === 'China'), [
it('phone', '手机号', 'text'),
]),
],
{
initial: { name: '' },
onChange(name, value) {
console.log(`${name} changed:`, value)
},
},
)
// 校验
const [valid] = await validate()
if (valid) {
await submit(model)
}
useFormModal 表单弹窗
将表单放入模态框中展示,常用于编辑场景:
import { useFormModal } from '@ithinkdt/page'
const { open, close } = useFormModal({
title: '编辑用户',
type: 'dialog',
width: 600,
items: ({ it, model }) => [
it('name', '名称', 'text', { required: true }),
],
onSubmit: async (data) => {
await userApi.save(data)
},
initial: { name: '' },
})
// 打开弹窗,传入初始数据
await open({ id: '1', name: '张三' })
useSimpleCrud CRUD 操作
封装新增、编辑、查看、删除的标准流程:
import { useSimpleCrud } from '@ithinkdt/page'
const { onAdd, onEdit, onView, onDel } = useSimpleCrud({
get: id => userApi.get(id),
save: data => userApi.save(data),
delete: id => userApi.delete(id),
// 增删改查用统一的表单
items: ({ it }) => [
it('name', '名称', 'text', { required: true }),
it('status', '状态', 'select'),
it('name', '名称', 'text'),
it('email', '邮箱', 'text'),
],
// 差别较大的时候,可以分开定义
createItems: ({ it }) => [
it('name', '名称', 'text', { required: true }),
],
editItems: ({ it, model }) => [
it('name', '名称', 'text', { required: true }),
it('status', '状态', 'select'),
],
viewItems: ({ it }) => [
it('name', '名称', 'text'),
it('email', '邮箱', 'text'),
],
})
// 新增
await onAdd({ name: '张三' })
// 编辑
await onEdit('id-xxx')
// 查看
await onView('id-xxx')
// 删除(带确认弹窗)
await onDel('id-xxx')
useDeleteHelper 删除确认
import { useDeleteHelper } from '@ithinkdt/page'
const { onDel } = useDeleteHelper({
delete: ids => Array.isArray(ids) ? userApi.deleteBatch(ids) : userApi.delete(id),
})
// 单个删除
await onDel('id-xxx')
// 批量删除
await onDel(['id-1', 'id-2'])
// 自定义提示
await onDel('id-xxx', '确认删除', '该操作不可撤销')
useTableHelper 表格列定义
import { useTableHelper, calcActionWidth } from '@ithinkdt/page'
const { columns, custom } = useTableHelper(
({ col, cols, group }) => [
col('index', '#', (_, __, i) => i + 1, { width: 60 }),
col('name', '用户名', { width: 120 }),
col('email', '邮箱', 'text', { ellipsis: true }),
col('status', '状态', { width: 100, render: v => v ? '启用' : '禁用' }),
cols('操作', [
col('$edit', '', (_, record) => h('Button', { onClick: () => onEdit(record) }, '编辑')),
col('$delete', '', (_, record) => h('Button', { onClick: () => onDel(record) }, '删除')),
], { fixed: 'right' }),
],
{
index: i => i + (params.currentPage - 1) * params.pageSize,
selectable: canSelect && (record) => record.status === 'selectable',
customizable: true,
actions: [
{ preset: 'edit', onClick: (record) => onEdit(record) },
{ preset: 'delete', onClick: (record) => onDel(record) },
],
},
)
useDescriptionsHelper 描述详情
import { useDescriptionsHelper } from '@ithinkdt/page'
const { items, model, reset } = useDescriptionsHelper(
({ it, group }) => [
it('name', '用户名', 'text'),
it('email', '邮箱'),
it('status', '状态', (value) => value ? '启用' : '禁用'),
group('contact', '联系方式', [
it('phone', '手机号', 'text'),
it('address', '地址', 'text'),
]),
],
)
// 设置数据
reset({ name: '张三', email: 'zhangsan@example.com', phone: '13800000000' })
useModal 模态框
import { useModal, useModalRef } from '@ithinkdt/page'
const xxx = ref('123')
const modal = useModal({
type: 'drawer',
title: '编辑',
width: 600,
content: () => <MyForm xxx={xxx.value} />,
onConfirm: async () => {
await submit()
},
})
// 打开
await modal.open()
// 关闭
modal.close()
useDataPagination 前端分页
import { useDataPagination } from '@ithinkdt/page'
import { ref } from 'vue'
const allData = ref([...])
const { data, pagination, paginate } = useDataPagination(allData, { pageSize: 10 })
// data 为当前页数据,pagination 为分页状态
校验规则
@ithinkdt/page/rules 提供常用的表单校验规则:
import { required, min, max, email, phone, url, idNo, chinese, noChinese } from '@ithinkdt/page/rules'
const rules = {
name: required('请输入名称'),
age: min(0, val => '年龄不能小于0'),
email: email('请输入有效邮箱'),
phone: phone('请输入有效手机号'),
}
API 参考
useDs(fetch, options?)
| 参数 |
说明 |
fetch |
(sortParams, filter) => Promise<{ total, records } | T[]>,数据获取函数 |
options.pagination |
'remote' 远程分页,false 本地数据 |
options.defaultPageSize |
默认分页大小,默认 10 |
options.keyField |
主键字段名,默认 'key' |
options.defaultSortField |
默认排序字段 |
options.defaultSortOrder |
默认排序方向,'asc' / 'desc' |
options.shallow |
是否使用 shallowReactive,默认 true |
options.immediate |
是否立即执行 fetch,默认 false |
options.resetOnFetch |
fetch 时是否先重置为空,默认 false |
| 属性/方法 |
说明 |
state |
响应式数据(远程分页模式下含 total 与 records) |
loading |
加载状态 |
error |
错误信息 |
execute(...params) |
执行 fetch 刷新数据 |
get(key) |
根据主键获取数据项 |
insert(item, index?) |
插入数据项 |
set(key, item) |
更新数据项 |
remove(key) |
删除数据项 |
useFilterHelper(items, options?)
| 参数 |
说明 |
items |
(helper) => FormItemOptions[],表单项定义函数 |
options.initial |
表单初始值 |
options.rules |
校验规则对象或函数 |
options.onChange |
(name, value) => void,字段变更回调 |
options.cached |
是否缓存筛选表单,默认取插件 defaultFilterCached |
options.cacheVersion |
缓存版本号,默认 1 |
options.customizable |
是否允许自定义筛选项显隐 |
| 属性/方法 |
说明 |
model |
响应式表单数据 |
items |
响应式表单项列表 |
reset(initial?) |
重置表单 |
validate() |
校验表单 |
invalid |
是否校验不通过 |
validation |
校验结果详情 |
custom(modify) |
自定义筛选项显隐/排序 |
useFormHelper(items, options?)
| 参数 |
说明 |
items |
(helper) => FormItemOptions[],表单项定义函数 |
options.initial |
表单初始值 |
options.rules |
校验规则对象或函数 |
options.onChange |
(name, value) => void,字段变更回调 |
| 属性/方法 |
说明 |
model |
响应式表单数据 |
items |
响应式表单项列表 |
reset(initial?) |
重置表单(overwrite=true 覆盖,reinit=true 同时重建表单项) |
reinit() |
重新初始化表单项 |
validate() |
校验表单 |
invalid |
是否校验不通过 |
validation |
校验结果详情 |
beforeSubmit(prop) |
提交前校验(支持单字段或数组) |
useFormModal(options)
| 参数 |
说明 |
options.title |
弹窗标题 |
options.type |
'dialog' / 'drawer',弹窗类型 |
options.width |
弹窗宽度 |
options.items |
(helper) => FormItemOptions[],表单项定义函数 |
options.initial |
表单初始值 |
options.rules |
校验规则 |
options.onSubmit |
(data) => Promise<void>,提交回调 |
options.loading |
加载状态 |
options.readonly |
是否只读 |
options.maskClosable |
点击遮罩是否关闭,默认 false |
options.closable |
是否显示关闭按钮,默认 true |
| 属性/方法 |
说明 |
open(initial?, title?) |
打开表单弹窗 |
close() |
关闭弹窗 |
useSimpleCrud(options)
| 参数 |
说明 |
options.get |
(id) => Promise<Entity>,获取详情 |
options.post / save |
(data) => Promise<Entity>,新增 |
options.put |
(data) => Promise<Entity>,编辑 |
options.delete |
(id) => Promise<void>,删除 |
options.createItems |
新建表单的表单项定义函数 |
options.editItems |
编辑表单的表单项定义函数 |
options.viewItems |
查看表单的表单项定义函数 |
options.items / formItems |
新建和编辑共用的表单项定义函数 |
options.width |
弹窗宽度,可按 type 返回不同宽度 |
options.cols |
表单列数,可按 type 返回不同列数 |
options.modalType |
'dialog' / 'drawer',可按 type 返回不同类型 |
options.deleteButtonText |
删除按钮文案 |
| 方法 |
说明 |
onAdd(initial?, title?) |
新增 |
onEdit(dataOrKey, title?) |
编辑 |
onView(dataOrKey, title?) |
查看 |
onDel(keyOrKeys, title?, tip?) |
删除 |
useDeleteHelper(options)
| 参数 |
说明 |
options.delete |
(ids) => Promise<void>,删除请求函数 |
options.keyField |
主键字段名,默认使用插件注入的 keyField |
options.deleteButtonText |
删除按钮文案 |
options.renderDelete |
自定义删除确认内容渲染函数 |
| 方法 |
说明 |
onDel(keyOrKeys, title?, tip?) |
删除(带确认弹窗) |
useTableHelper(columns, options?)
| 参数 |
说明 |
columns |
(helper) => TableColumnOptions[],列定义函数 |
options.index |
是否显示序号列或自定义序号渲染函数 (i, record) => VNodeChild |
options.indexTitle |
序号列标题,默认 '#' |
options.selectable |
是否可选择或自定义可选择判断函数 (record) => boolean |
options.selectType |
'multiple' / 'single',选择类型 |
options.actions |
操作按钮列表,内置预设 { preset: 'edit' | 'view' | 'delete' } |
options.actionTitle |
操作列标题,默认 '操作' |
options.actionWidth |
操作列宽度,字符串或根据文案计算宽度函数 |
options.actionHidden |
是否隐藏操作列 |
options.customizable |
是否允许自定义列显隐/固定/宽度/排序 |
options.expandable |
是否可展开行或自定义判断函数 (record) => boolean |
options.renderExpand |
(record, i) => VNodeChild,展开行渲染函数 |
| 属性/方法 |
说明 |
columns |
响应式列定义 |
custom(modify) |
自定义列显隐/固定/宽度/排序;传 true 重置为默认 |
dataMode |
数据模式(all / selection) |
reinit() |
重新初始化列定义 |
useDescriptionsHelper(items)
| 参数 |
说明 |
items |
(helper) => DescriptionItem[],描述项定义函数 |
| 属性/方法 |
说明 |
items |
响应式描述项列表 |
model |
响应式数据 |
reset(model?, reinit?) |
重置数据(reinit=true 同时重建描述项) |
reinit() |
重新初始化描述项 |
useModal(options)
| 参数 |
说明 |
options.type |
'dialog' / 'drawer',类型 |
options.title |
标题 |
options.content |
内容 |
options.width |
宽度 |
options.height |
高度 |
options.confirmText |
确认按钮文案 |
options.cancelText |
取消按钮文案 |
options.confirmLoading |
确认按钮加载状态 |
options.cancelLoading |
取消按钮加载状态 |
options.onConfirm |
() => Promise<boolean | void>,确认回调,返回 false 阻止关闭 |
options.onCancel |
() => Promise<boolean | void>,取消回调 |
options.onClose |
() => Promise<boolean | void>,关闭回调 |
options.closable |
是否显示关闭按钮,默认 true |
options.maskClosable |
点击遮罩是否关闭,默认 false |
options.footer |
自定义底部内容,null 隐藏底部 |
| 方法 |
说明 |
open(title?) |
打开模态框 |
close() |
关闭模态框 |
useModalRef()
| 属性/方法 |
说明 |
inModal |
是否在模态框上下文内 |
modalType |
模态框类型, dialog | drawer |
visible |
模态框可见状态 |
close() |
关闭模态框 |
setTitle(title) |
设置模态框标题 |
useDataPagination(data, options?)
| 参数 |
说明 |
data |
MaybeRef<T[]>,原始数据列表 |
options.pageSize |
每页条数,默认取插件 defaultPageSize |
options.currentPage |
初始页码,默认 1 |
options.updateOnChange |
数据变化时是否重置到第一页,默认 true |
| 属性/方法 |
说明 |
data |
当前页数据 |
pagination |
分页状态(pageSize, currentPage) |
paginate(params) |
切换分页 |
校验规则(@ithinkdt/page/rules)
| 方法 |
说明 |
required(message, options?) |
必填校验,options.required 控制是否必填,options.trigger 触发时机 |
min(min, message, options?) |
最小值/最小长度,min 为最小阈值 |
max(max, message, options?) |
最大值/最大长度,max 为最大阈值 |
minmax(min, max, message, options?) |
同时校验最小值/最大长度与最大值/最大长度 |
email(message, options?) |
邮箱格式 |
phone(message, options?) |
手机号格式(1 开头 11 位数字) |
url(message, options?) |
URL 格式 |
idNo(message, options?) |
身份证格式 |
plateNo(message, options?) |
车牌号格式 |
chinese(message, options?) |
纯中文 |
noChinese(message, options?) |
不含中文 |
pattern(pattern, message, options?) |
自定义正则,pattern 为 RegExp 或正则字符串 |
isRequiredRule(rule) |
判断是否为 required 规则 |
message 参数为 (value, params) => string,options.trigger 默认 'blur'(required 默认 ['input', 'blur', 'change'])。