@ithinkdt/page
v4.2.1
Published
iThinkDT Page
Readme
@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: '[email protected]', 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'])。
