npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ithinkdt/page

v4.2.1

Published

iThinkDT Page

Readme

@ithinkdt/page

许可证

MIT

安装

npm i @ithinkdt/page

需要您自行安装依赖 vue@3vue-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 | 响应式数据(远程分页模式下含 totalrecords) | | 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?) | 自定义正则,patternRegExp 或正则字符串 | | isRequiredRule(rule) | 判断是否为 required 规则 |

message 参数为 (value, params) => stringoptions.trigger 默认 'blur'required 默认 ['input', 'blur', 'change'])。