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

vue3-crud-hooks

v1.0.5

Published

基于 Vue 3 和 Element Plus 的 CRUD 工具库

Readme

vue3-crud-hooks

基于 Vue 3 Composition API 和 Element Plus 的 CRUD 业务逻辑复用库,帮助你在中后台项目中快速搭建表格、搜索、表单弹窗等标准功能。

npm version License

贡献指南 · 行为准则


✨ 特性

  • 🚀 声明式 CRUD — 一个 useCrudPage Hook 接管表格查询、分页、搜索、新增、编辑、删除全流程
  • 🧩 开箱组件CustomTable + Pagination 组件与 Hooks 无缝配合,v-bind="tableBindings" 一行完成绑定
  • 🔌 模块解耦 — 可单独使用 useTablePage 管理列表,或单独使用 useFormDialog 管理弹窗
  • 📦 Tree Shaking — 支持子路径按需导入(vue3-crud-hooks/useTablePage),不引入冗余代码
  • 🛠 分层配置apis / table / form / search / advanced 各层独立,兼顾快速开发与复杂场景
  • 🧠 自动检测 — 自动识别后端返回的 rows/data/list 等常见字段名,零配置接入
  • 📝 TypeScript — 完备的类型推导,CustomTable 支持 el-table 原生属性/事件透传(@row-click / highlight-current-row 等)
  • 🔄 数据转换 — 内置数组↔字符串、时间范围、空值清理等转换工具
  • 📢 消息解耦useMessage 抽象消息提示,默认 Element Plus,可替换为任意 UI 库

📦 安装

pnpm add vue3-crud-hooks

需要同级安装 vue@^3element-plus@^2

如果你使用了 CustomTablePagination 组件,建议同时引入组件库样式:

import 'vue3-crud-hooks/style.css'

不引入时,组件功能仍可用,但以下内置样式不会生效:

  • CustomTable 内部分页区域的上边距
  • 操作列中多个 el-link / el-button 之间的间距
  • Pagination 容器的宽度与右对齐布局
  • Paginationhidden 隐藏样式

如果你只使用 Hooks,不使用 CustomTable / Pagination,则不需要额外引入这份样式。


🔨 快速开始

完整 CRUD 页面

一个 Hook + 一个组件,快速搭建带搜索、分页、增删改的完整页面。

<template>
  <div>
    <!-- 搜索栏 -->
    <el-input v-model="searchParams.keyword" placeholder="搜索" style="width: 200px" />
    <el-button type="primary" @click="handleSearch">搜索</el-button>
    <el-button @click="handleReset">重置</el-button>
    <el-button type="primary" @click="openDialog('add')">新增</el-button>

    <!-- 表格 — v-bind 一行绑定所有属性和事件 -->
    <CustomTable v-bind="tableBindings">
      <template #status="{ row }">
        <el-tag :type="row.status === 1 ? 'success' : 'danger'">
          {{ row.status === 1 ? '启用' : '禁用' }}
        </el-tag>
      </template>
    </CustomTable>

    <!-- 新增/编辑弹窗 -->
    <el-dialog v-model="dialogVisible" :title="dialogMode === 'add' ? '新增' : '编辑'">
      <el-form ref="formRef" :model="formData" label-width="80px">
        <el-form-item label="名称">
          <el-input v-model="formData.name" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="handleDialogClose">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { useCrudPage, CustomTable } from 'vue3-crud-hooks'
import { getList, addData, updateData, deleteData } from '@/api/demo'

const {
  searchParams,
  dialogVisible,
  dialogMode,
  formData,
  formRef,
  handleSearch,
  handleReset,
  handleDialogClose,
  openDialog,
  submitForm,
  tableBindings,           // 一键绑定,包含 config / data / loading / 事件
} = useCrudPage({
  apis: {
    list: getList,
    add: addData,
    update: updateData,
    delete: deleteData,
  },
  table: {
    config: {
      selection: true,
      index: true,
      columns: [
        { prop: 'name', label: '名称', minWidth: 120 },
        { prop: 'status', label: '状态', width: 100 },
        { prop: 'createTime', label: '创建时间', width: 180 },
        {
          type: 'action',
          label: '操作',
          width: 150,
          buttons: [
            { btnText: '编辑', event: 'edit', type: 'primary' },
            { btnText: '删除', event: 'delete', type: 'danger' },
          ],
        },
      ],
    },
  },
  form: {
    initialData: { name: '', status: 1 },
  },
})
</script>

独立使用表格

只需列表管理时,可单独使用 useTablePage

<script setup lang="ts">
import { useTablePage, CustomTable } from 'vue3-crud-hooks'
import { fetchUsers } from '@/api/user'

const { tableBindings, handleSearch, handleReset, searchParams } = useTablePage(
  fetchUsers,
  { keyword: '' },
  {
    customTableConfig: {
      columns: [
        { prop: 'name', label: '姓名' },
        { prop: 'email', label: '邮箱' },
      ],
    },
  }
)
</script>

<template>
  <CustomTable v-bind="tableBindings" />
</template>

独立使用组件

也可脱离 Hooks,像使用普通组件一样使用 CustomTable

<template>
  <CustomTable
    :config="config"
    :data="data"
    :loading="loading"
    @pagination="onPagination"
    @action="onAction"
    @row-click="handleRowClick"           <!-- el-table 原生事件直接透传 -->
    highlight-current-row                 <!-- el-table 原生属性直接透传 -->
  />
</template>

<script setup lang="ts">
import { CustomTable } from 'vue3-crud-hooks'
import type { CustomTableConfig } from 'vue3-crud-hooks'

const config: CustomTableConfig = {
  columns: [
    { prop: 'name', label: '名称' },
    { type: 'action', buttons: [{ btnText: '编辑', event: 'edit' }] },
  ],
  pagination: { currentPage: 1, pageSize: 20 },
}
</script>

📚 API 参考

useCrudPage<T>(config: CrudPageConfig<T>)

一站式 CRUD Hook,整合表格查询 + 表单弹窗。

| 返回值 | 说明 | 类型 | |-------|------|------| | tableBindings | 一键绑定CustomTable(含 config/data/loading + 事件) | ComputedRef | | tableData | 表格数据 | Ref<T[]> | | loading | 加载状态 | Ref<boolean> | | deleteLoading | 删除操作加载状态 | Ref<boolean> | | pageInfo | 分页信息 { pageNum, pageSize, total } | Reactive | | searchParams | 搜索参数(响应式,可直接绑定到表单) | Reactive | | selectedRows / selectedIds | 当前选中行数据 / ID 列表 | Ref | | getTableData | 手动刷新列表 | () => Promise<void> | | handleSearch | 搜索(重置页码后刷新) | () => void | | handleReset | 重置搜索条件 | () => void | | handleDelete | 单行删除 | (row: T) => Promise<void> | | handleBatchDelete | 批量删除 | () => Promise<void> | | handleExport | 导出数据 | (options?) => void | | dialogVisible | 弹窗显示状态 | Ref<boolean> | | dialogMode | 弹窗模式 'add' \| 'edit' | Ref | | formData | 表单数据 | Ref<T> | | formRef | 表单实例引用 | Ref<any> | | submitLoading | 提交加载状态 | Ref<boolean> | | formLoading | 编辑回显加载状态 | Ref<boolean> | | openDialog | 打开弹窗 | (mode, row?) => Promise<void> | | submitForm | 提交表单 | () => Promise<void> | | resetForm | 重置表单 | () => void | | handleDialogClose | 关闭弹窗 | () => void |

配置项 CrudPageConfig<T>

interface CrudPageConfig<T> {
  apis: {
    list: (params: any) => Promise<any>       // 列表查询接口
    add: (data: T) => Promise<any>            // 新增接口
    update: (data: T) => Promise<any>         // 编辑接口
    delete?: (id: any) => Promise<any>        // 删除接口
    batchDelete?: (ids: any[]) => Promise<any> // 批量删除接口
    get?: (id: any) => Promise<any>           // 获取详情接口(编辑时回显)
    export?: Function                         // 导出接口
  }
  table: {
    config?: CustomTableConfig                // CustomTable 列配置
    idKey?: string                            // 主键字段名,默认 'id'
    dataKey?: string                          // 响应数据字段名,默认 'rows'
    totalKey?: string                         // 响应总数字段名,默认 'total'
    autoFetch?: boolean                       // 是否自动获取数据
    autoDetect?: boolean                      // 是否自动检测响应结构
    exportUrl?: string                        // 导出下载 URL
    confirmMessage?: string                   // 删除确认提示
    batchConfirmMessage?: string              // 批量删除确认提示
    onCustomAction?: (event, row, index) => void // 自定义事件处理器
  }
  form: {
    initialData: T                            // 表单初始值
    rules?: any                               // 表单校验规则
    beforeSubmit?: (data: T) => any           // 提交前转换
    afterGet?: (data: any) => T               // 获取详情后转换
    onAfterSubmit?: () => void                // 提交成功后回调
    onSubmitSuccess?: (res, mode, formData) => void  // 提交成功回调
  }
  search?: {
    initialData: Record<string, any>          // 搜索表单初始值
    beforeSearch?: (params: any) => any       // 搜索前参数转换
  }
  advanced?: {
    arrayFields?: string[]                    // 数组字段(自动 string[] ↔ 逗号分隔)
    timeFields?: Array<{ field, prefix }>     // 时间范围字段
    messageApi?: Partial<MessageApi>          // 自定义消息 API
    onDeleteSuccess?: (row: T) => void
    onBatchDeleteSuccess?: (rows: T[]) => void
  }
}

useTablePage<T>(fetchApi, searchForm?, config?, deleteConfig?, exportConfig?)

独立表格管理 Hook。

| 参数 | 类型 | 说明 | |------|------|------| | fetchApi | (params) => Promise<any> | 列表查询接口 | | searchForm | Record<string, any> | 搜索表单初始值 | | config | TablePageConfig | 配置项(见下文) | | deleteConfig | DeleteConfig | 删除配置 | | exportConfig | ExportConfig | 导出配置 |

TablePageConfig

| 属性 | 类型 | 说明 | |------|------|------| | customTableConfig | CustomTableConfig | 用于生成 CustomTable 组件的配置 | | dataKey | string | 响应数据字段名,默认 'rows' | | totalKey | string | 响应总数字段名,默认 'total' | | autoDetect | boolean | 自动检测响应结构,默认 true | | autoFetch | boolean | 自动获取数据,默认 true | | beforeSearch | (params) => any | 搜索前参数转换,返回 false 阻止请求 | | arrayFields | string[] | 数组字段,搜索时自动 join(',') | | timeFields | Array | 时间范围字段,自动拆分为 startAt/endAt | | exportUrl | string | 导出下载 URL(未配置 exportFunction 时使用) |


useFormDialog<T>(config: FormDialogConfig<T>)

独立表单弹窗管理 Hook。

| 返回值 | 说明 | |--------|------| | dialogVisible / dialogMode | 弹窗状态 | | formData / formRef | 表单数据与引用 | | submitLoading / formLoading | 加载状态 | | openDialog(mode, row?) | 打开弹窗(编辑模式自动回显) | | submitForm() | 验证 + 转换 + 提交 | | resetForm() | 重置表单 | | handleDialogClose() | 关闭弹窗 |


CustomTable 组件

Props

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | config | CustomTableConfig \| null | { columns: [] } | 表格配置(列定义、分页、序号等) | | data | any[] | [] | 表格数据 | | loading | boolean | false | 加载状态 | | props | Partial<TableProps> | {} | 透传给 el-table 的属性(border, stripe 等) |

所有未声明的属性和事件(如 @row-clickhighlight-current-row@cell-click)会自动透传到内层 el-table,像使用原生 el-table 一样使用即可。

Events

| 事件 | 参数 | 说明 | |------|------|------| | pagination | { currentPage, pageSize } | 分页改变 | | action | (event, row, index) | 操作按钮点击 | | selection-change | (selection) | 多选改变 | | sort-change | ({ column, prop, order }) | 排序改变 | | filter-change | (filters) | 筛选改变 |

Slots

| 名称 | 作用域 | 说明 | |------|--------|------| | [prop] | { row, index, column } | 自定义列内容 | | [prop]-header | { column, $index } | 自定义表头 | | action | { row, index } | 自定义操作列 | | append | — | 表格底部插槽 |

列配置 CustomTableConfig

interface CustomTableConfig {
  columns: Array<{
    prop?: string            // 字段名
    label?: string           // 列标题
    width?: string | number
    minWidth?: string | number
    fixed?: boolean | 'left' | 'right'
    type?: 'default' | 'selection' | 'index' | 'expand' | 'action'
    align?: 'left' | 'center' | 'right'
    sortable?: boolean | 'custom'
    resizable?: boolean
    filters?: Array<{ text: string; value: any }>
    filterMethod?: (value: any, row) => boolean
    formatter?: (row, column, cellValue, index) => any
    buttons?: Array<{           // 操作列按钮(仅 type='action' 时有效)
      btnText: string
      event: string
      btnType?: 'link' | 'button'
      type?: 'primary' | 'success' | 'warning' | 'danger'
      disabled?: boolean | ((row) => boolean)
      visible?: (row) => boolean
      props?: Record<string, any>
    }>
    // 更多 el-table-column 原生属性(showOverflowTooltip, className 等)
    [key: string]: any
  }>
  selection?: boolean | Record<string, any>   // 是否展示选择列
  index?: boolean | Record<string, any>       // 是否展示序号列(自动翻页连续序号)
  pagination?: boolean | PaginationConfig     // 分页配置
  props?: Record<string, any>                 // 透传 el-table 属性
}

分页配置 PaginationConfig

interface PaginationConfig {
  currentPage?: number       // 当前页码,默认 1
  pageSize?: number          // 每页条数,默认 10
  total?: number             // 总条数
  pageSizes?: number[]       // 可选的每页条数选项
  layout?: string            // 分页布局
  autoScroll?: boolean       // 翻页时自动滚动到顶部
  align?: string             // 对齐方式,默认 'right'
}

Pagination 组件

独立的 Element Plus 分页封装,支持 v-bind 透传:

<Pagination :total="100" :currentPage="1" :pageSize="20" @pagination="onPageChange" />

useDataTransform

数据转换工具函数:

| 方法 | 说明 | |------|------| | arrayToString(data, fields) | 数组字段转逗号字符串(提交用) | | stringToArray(data, fields) | 逗号字符串转数组(回显用) | | processTimeRange(params, field, prefix?) | 时间范围拆分为 beginTime/endTime | | cleanEmptyFields(data, fields?) | 清理空值字段 | | convertNumbers(data, fields) | 字段转数字类型 |

useMessage

消息提示解耦层,默认使用 Element Plus 的 ElMessage / ElMessageBox,可通过 messageApi 替换为任意 UI 库:

const message = useMessage({
  success: (msg) => myUI.showSuccess(msg),
  error: (msg) => myUI.showError(msg),
  confirm: (msg) => myUI.showConfirm(msg),
})

📦 子路径导入

import { useCrudPage } from 'vue3-crud-hooks'
import { useTablePage } from 'vue3-crud-hooks/useTablePage'
import { useFormDialog } from 'vue3-crud-hooks/useFormDialog'
import { CustomTable } from 'vue3-crud-hooks/CustomTable'
import { Pagination } from 'vue3-crud-hooks/Pagination'
import { useDataTransform } from 'vue3-crud-hooks/useDataTransform'
import { useMessage } from 'vue3-crud-hooks/useMessage'

📄 License

MIT License © 2025 YongHangPu