@hz_yujin/vue-smart-table

Vue 3 ProSmartTable:基于 el-table 全量透传,列 schema 驱动查询 / 表单 / 列管理;表单弹框基于 ProDialog
1,578
Install
npm install @hz_yujin/vue-smart-table
Latest Version:0.1.12
Vue:3
License:MIT
Last Updated:Sep 20, 2026
Links: npm
Maintainer: hz_yujin

@hz_yujin/vue-smart-table

本包位于 monorepo packages/smart-table。仓库根目录见 README。

基于 Element Plus el-table 的 Smart Table:一份列 schema 同时驱动表格列、查询条件与新增/编辑表单,并内置列管理抽屉、分页、骨架屏。

与 @hz_yujin/vue-base-table(VXE)互补,互不影响。

目录


安装

npm install @hz_yujin/vue-smart-table @hz_yujin/vue-dialog element-plus @element-plus/icons-vue

element-plus、@element-plus/icons-vue、@hz_yujin/vue-dialog、vue 为 peerDependencies,需由宿主项目安装。

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import '@hz_yujin/vue-dialog/style.css'
import '@hz_yujin/vue-smart-table/style.css'

import { ProSmartTable } from '@hz_yujin/vue-smart-table'
// 或全局注册
import VueSmartTable from '@hz_yujin/vue-smart-table'

const app = createApp(App)
app.use(ElementPlus, { locale: zhCn })
app.use(VueSmartTable) // 可选
app.mount('#app')

快速开始

<script setup lang="ts">
import { ref } from 'vue'
import {
  ProSmartTable,
  type SmartColumnSchema,
} from '@hz_yujin/vue-smart-table'
import '@hz_yujin/vue-smart-table/style.css'

const columns = ref<SmartColumnSchema[]>([
  {
    id: 'name',
    prop: 'name',
    label: '名称',
    minWidth: 160,
    searchable: true,
    formable: true,
    required: true,
    formType: 'input',
    source: 'fixed',
  },
  {
    id: 'status',
    prop: 'status',
    label: '状态',
    minWidth: 120,
    searchable: true,
    formable: true,
    formType: 'select',
    formOptions: [
      { label: '启用', value: '启用' },
      { label: '禁用', value: '禁用' },
    ],
    source: 'fixed',
  },
])

const data = ref([
  { name: '示例 A', status: '启用' },
  { name: '示例 B', status: '禁用' },
])

function onSearch(model: Record<string, unknown>) {
  console.log('search', model)
}
</script>

<template>
  <ProSmartTable
    v-model:columns="columns"
    :data="data"
    form-title="记录"
    border
    stripe
    @search="onSearch"
    @create="(row) => data.push(row)"
    @update="(row) => console.log('update', row)"
  />
</template>

未在 ProSmartTable 上声明的属性(如 border、stripe、height、row-key)会透传给底层 el-table。


设计思路

区域 由谁驱动
表格列 columns 中 visible !== false 且有 prop 的项
查询区 searchable: true 的列
新增/编辑表单 formable: true 的列
控件类型 formType + formOptions / formProps

业务侧通常只需维护一份 SmartColumnSchema[],再监听 search / create / update / page-change 对接接口。

能力一览

能力 说明
el-table 透传 未声明的 attrs / 默认插槽全部落到 el-table
查询 可收起/展开;默认收起展示前 3 项
新增/编辑 内置弹框 + 必填校验;可用插槽自定义字段
列管理 显隐、拖拽排序、增删列、查询/表单开关、固定列
持久化 默认 persistKey → localStorage;false 关闭;也可自行存服务端
分页 默认开启;local 前端切片 / remote 服务端分页
排序 支持本地 / 远程(sortable: 'custom' 或 remote 分页自动提升)
骨架屏 默认开启;无数据时骨架,有数据刷新转圈
空状态 默认插画 + emptyText;可用 #empty 自定义
序号 / 多选 showSeq / showSelection

SmartColumnSchema

interface SmartColumnSchema {
  /** 稳定唯一 id(拖拽 / 删除 / 持久化必填) */
  id: string
  /** 对应 el-table-column 的 prop / 数据字段 */
  prop: string
  /** 列标题 */
  label: string
  minWidth?: number
  width?: number | string
  /** 是否可排序;远程分页下 true 会自动提升为 'custom' */
  sortable?: boolean | 'custom'
  /** 是否作为查询条件 */
  searchable?: boolean
  /** 是否出现在新增/编辑表单 */
  formable?: boolean
  /** 表单是否必填(仅 formable 时有效) */
  required?: boolean
  /** 表单 / 查询控件类型 */
  formType?: SmartFormControlType
  /** select / radio / cascader / tree-select 等选项 */
  formOptions?: SmartSelectOption[]
  /** 查询占位符 */
  searchPlaceholder?: string
  /** 固定列 */
  fixed?: boolean | 'left' | 'right'
  /** 是否在表格中显示,默认 true */
  visible?: boolean
  /** fixed:业务预置;custom:列管理新增 */
  source?: 'fixed' | 'custom'
  /** 是否允许删除(fixed 默认不可删,custom 默认可删) */
  deletable?: boolean
  /** 透传给 el-table-column */
  columnProps?: Record<string, unknown>
  /** 透传给表单控件 */
  formProps?: Record<string, unknown>
}

formType 一览

formType 说明
input 输入框(默认)
password 密码
textarea 多行文本
number 数字
select / select-multiple 下拉单选 / 多选
radio 单选
checkbox / checkbox-group 复选 / 复选组
switch 开关
date / datetime 日期 / 日期时间
daterange / datetimerange 日期范围 / 日期时间范围(亦支持 date-range / datetime-range 别名)
time / timerange 时间 / 时间范围(亦支持 time-range)
month / year / week 月 / 年 / 周
cascader 级联
tree-select 树选择
autocomplete 自动完成
rate / slider / color 评分 / 滑块 / 颜色

选项结构:

interface SmartSelectOption {
  label: string
  value: string | number | boolean
  disabled?: boolean
  children?: SmartSelectOption[] // cascader / tree-select
}

列配置示例

const columns: SmartColumnSchema[] = [
  {
    id: 'workshop',
    prop: 'workshop',
    label: '车间',
    minWidth: 120,
    searchable: true,
    formable: true,
    required: true,
    formType: 'tree-select',
    formOptions: [
      {
        label: '东区',
        value: 'east',
        children: [
          { label: '一车间', value: '一车间' },
          { label: '二车间', value: '二车间' },
        ],
      },
    ],
    formProps: { checkStrictly: false, filterable: true },
    source: 'fixed',
    deletable: false,
  },
  {
    id: 'duration',
    prop: 'duration',
    label: '持续时长(分)',
    minWidth: 120,
    sortable: 'custom',
    searchable: true,
    formable: true,
    formType: 'number',
    formProps: { min: 0, max: 999, controlsPosition: 'right' },
    source: 'fixed',
  },
]

Props

Prop 类型 默认值 说明
data Record<string, unknown>[] [] 表格数据
columns SmartColumnSchema[] [] 列 schema;支持 v-model:columns
showToolbar boolean true 是否展示工具栏
showColumnEditor boolean true 是否展示「编辑列」按钮
columnEditorColumns ProColumnEditorVisibleColumns {} 列管理显隐;source / prop / formable / required / add / remove 默认均不显示,传 true 才展示;label: true 允许改标题(固定列默认禁用)
showRefresh boolean true 是否展示刷新按钮
showFullscreen boolean true 是否展示全屏按钮
showCreate boolean true 是否展示默认「新增」按钮
showSeq boolean true 是否展示序号列
seqWidth number | string 60 序号列宽度
showSelection boolean false 是否展示多选列
showSearch boolean true 是否展示查询区
searchCollapsible boolean true 查询区是否可收起展开
searchCollapseVisibleCount number 3 收起时展示的字段数
searchDefaultExpanded boolean false 查询区默认是否展开
persistKey string | false 'pro-smart-table' 本地持久化 key;多表请传不同 key;false 关闭
emptyText string '暂无数据' 空数据文案;默认展示插画 + 文案
formTitle string '' 弹框标题前缀,如「告警」→「新增告警」
formWidth number | string '45%' 弹框宽度
loading boolean false 加载中(骨架 / 转圈)
skeleton boolean | ProSmartTableSkeletonConfig true 骨架屏;false 关闭
pagination false | ProSmartTablePagination {} 分页;false 关闭

未传 height / max-height 时,表格铺满父级剩余高度,表头固定、表体滚动(透传 height 可覆盖,如 height="520")。

skeleton 配置

interface ProSmartTableSkeletonConfig {
  rows?: number      // 骨架行数,默认取 pageSize(上限 10),否则 5
  animated?: boolean // 默认 true
  onlyEmpty?: boolean // 默认 true:仅无数据时用骨架,有数据刷新仍用转圈
}

pagination 配置

interface ProSmartTablePagination {
  currentPage?: number
  pageSize?: number // 默认 20
  total?: number
  pageSizes?: number[]
  layout?: string // 默认 total, sizes, prev, pager, next, jumper
  background?: boolean
  size?: 'large' | 'default' | 'small' // 默认 small
  pagerCount?: number
  /** local:前端切片;remote:data 为当前页,需传 total */
  mode?: 'local' | 'remote'
}

未显式传 mode 时:有 total 走 remote,否则 local。


Events

事件 参数 说明
update:columns SmartColumnSchema[] 列变更(配合 v-model:columns)
columns-change SmartColumnSchema[] 列管理保存成功
columns-reset — 列管理「恢复默认」;业务侧应清空服务端配置
search model 点击查询,或重置后也会再抛一次 search
reset — 点击重置
create model 新增弹框确认(已过必填校验)
update model 编辑弹框确认(已过必填校验)
page-change { currentPage, pageSize } 页码 / 每页条数变化
update:pagination ProSmartTablePagination 分页受控同步
refresh — 点击刷新
fullscreen-change boolean 全屏状态变化
sort-change { column, prop, order } 透传 el-table;远程排序请监听此事件

reset 后组件会清空查询模型并发出 search(带空条件),远程列表可统一在 @search 里拉数。


插槽

插槽 作用域 说明
toolbar-left — 工具栏左侧;覆盖默认「新增」时可配合 show-create={false}
toolbar-right — 工具栏右侧(刷新 / 全屏 / 编辑列按钮之前)
col-{prop} el-table 单元格 scope 自定义列渲染,如 #col-status
(默认插槽) — 追加额外 el-table-column(如操作列)
form-{prop} { model, column, value, mode } 覆盖某个表单字段;mode: create | edit
form-extra { model, mode, visible } 写在弹框内部,用于嵌套二级弹框
pagination — 完全自定义分页
skeleton — 自定义骨架屏
empty — 空数据自定义内容(不传时使用默认插画空状态)

自定义列 + 操作列

<ProSmartTable :data="data" :columns="columns" ...>
  <template #col-status="{ row }">
    <el-tag :type="row.status === '启用' ? 'success' : 'info'">
      {{ row.status }}
    </el-tag>
  </template>

  <el-table-column label="操作" width="140" fixed="right">
    <template #default="{ row }">
      <el-button link type="primary" @click="tableRef?.openEdit(row)">
        编辑
      </el-button>
    </template>
  </el-table-column>
</ProSmartTable>

方法(expose)

interface ProSmartTableExpose {
  tableRef: TableInstance | null // 底层 el-table
  openColumnEditor: () => void
  openCreate: () => void
  openEdit: (row: Record<string, unknown>) => void
  getSearchModel: () => Record<string, unknown>
  getColumns: () => SmartColumnSchema[]
  isFullscreen: boolean
  enterFullscreen: () => void
  exitFullscreen: () => void
  toggleFullscreen: () => void
}
<script setup lang="ts">
import { ref } from 'vue'
import type { ProSmartTableExpose } from '@hz_yujin/vue-smart-table'

const tableRef = ref<ProSmartTableExpose>()

function onEdit(row: Record<string, unknown>) {
  tableRef.value?.openEdit(row)
}
</script>

<template>
  <ProSmartTable ref="tableRef" ... />
</template>

分页

本地分页(默认)

不传 total 时,组件对 data 做前端切片:

<ProSmartTable :data="allRows" :columns="columns" />

远程分页

<script setup lang="ts">
import { ref } from 'vue'
import type { ProSmartTablePagination } from '@hz_yujin/vue-smart-table'

const pagination = ref<ProSmartTablePagination>({
  currentPage: 1,
  pageSize: 10,
  total: 0,
  mode: 'remote',
  pageSizes: [10, 20, 50],
})

async function onPageChange({ currentPage, pageSize }) {
  pagination.value = { ...pagination.value, currentPage, pageSize }
  await fetchList()
}
</script>

<template>
  <ProSmartTable
    :data="list"
    :columns="columns"
    v-model:pagination="pagination"
    :loading="loading"
    @page-change="onPageChange"
    @search="onSearch"
  />
</template>

关闭分页::pagination="false"。


远程列表(推荐写法)

与 playground SmartTableDemo 一致的模式:

  1. v-model:pagination + mode: 'remote'
  2. @search / @reset(重置也会走 search)回到第 1 页并拉数
  3. @page-change 更新页码后拉数
  4. @create / @update 调接口后刷新
  5. 自定义「新增」时设 :show-create="false",用 #toolbar-left + openCreate()
const lastSearchModel = ref<Record<string, unknown>>({})
const pagination = ref<ProSmartTablePagination>({
  currentPage: 1,
  pageSize: 10,
  total: 0,
  mode: 'remote',
})

async function onSearch(model: Record<string, unknown>) {
  lastSearchModel.value = { ...model }
  pagination.value = { ...pagination.value, currentPage: 1 }
  await fetchList()
}

async function onPageChange(payload: { currentPage: number; pageSize: number }) {
  pagination.value = { ...pagination.value, ...payload }
  await fetchList()
}

async function fetchList() {
  loading.value = true
  try {
    const res = await api.list({
      ...lastSearchModel.value,
      currentPage: pagination.value.currentPage,
      pageSize: pagination.value.pageSize,
    })
    tableData.value = res.list
    pagination.value = {
      ...pagination.value,
      total: res.total,
      currentPage: res.currentPage,
      pageSize: res.pageSize,
    }
  } finally {
    loading.value = false
  }
}

查询 / 重置时组件会 silent 把页码置 1(不额外抛 page-change),避免连打两次接口;业务只需在 @search 里请求即可。


排序

  • 本地:sortable: true,el-table 本地排序即可。
  • 远程:设 sortable: 'custom',或分页为 remote 时 sortable: true 会自动提升为 custom,监听 @sort-change:
function onSortChange(payload: {
  prop: string
  order: 'ascending' | 'descending' | null
}) {
  sortState.value = !payload.prop || !payload.order
    ? {}
    : {
        sortBy: payload.prop,
        sortOrder: payload.order === 'ascending' ? 'asc' : 'desc',
      }
  pagination.value = { ...pagination.value, currentPage: 1 }
  void fetchList()
}

sort-change 由组件显式转发,不会再落入 attrs 重复绑定到 el-table。


列管理与持久化

右上角「编辑列」打开 ProColumnEditor,可:

  • 显隐、拖拽排序
  • 改 field / 标题 / 最小宽(field 默认隐藏;固定列标题默认禁用)
  • 开关:排序 / 查询 / 表单 / 必填(表单、必填默认隐藏)
  • 改表单类型、左右固定
  • 表单类型旁「配置」:按类型补充 formOptions / formProps(选项、min/max、valueFormat、占位符等)
  • 新增自定义列、删除(受 deletable 控制)
  • 「恢复默认」

未勾选(隐藏)的列:编辑控件禁用,但勾选框与删除仍可用(无需先勾选再删)。

已填写过的配置按钮会以 primary 高亮;切换表单类型时会清理与新类型不兼容的 options / formProps。

列管理元列显隐

columnEditorColumns 控制列管理中 类型 / field / 表单 / 必填 / 新增列 / 删除 是否展示,默认均不显示;固定列标题默认只读,自定义列或 label: true 可改。隐藏 field 时若开启 add,新增列会自动生成 custom_n。

<!-- 默认:类型 / field / 表单 / 必填 / 新增列 / 删除 均不显示,标题只读 -->
<ProSmartTable v-model:columns="columns" :data="data" />

<!-- 需要完整列管理能力时再打开 -->
<ProSmartTable
  v-model:columns="columns"
  :data="data"
  :column-editor-columns="{
    source: true,
    prop: true,
    label: true,
    formable: true,
    required: true,
    add: true,
    remove: true,
  }"
/>

本地持久化

默认开启:列管理保存后写入 localStorage(key 默认 pro-smart-table);「恢复默认」会清除该 key。

<!-- 默认即持久化;多表请传不同 key -->
<ProSmartTable persist-key="alarm-list" v-model:columns="columns" ... />

<!-- 关闭本地持久化(例如完全走服务端) -->
<ProSmartTable :persist-key="false" v-model:columns="columns" ... />

服务端持久化

<ProSmartTable
  v-model:columns="columns"
  @columns-change="(cols) => saveTableColumns(cols)"
  @columns-reset="() => saveTableColumns([])"
/>

「恢复默认」后组件会应用内置默认列(首次传入的 columns 快照)并发出 columns-reset。业务侧建议把服务端配置存成 [],下次读取空数组时回落默认列。


表单自定义 / 嵌套弹框

<ProSmartTable ...>
  <!-- 覆盖某个表单字段 -->
  <template #form-deviceName="{ model }">
    <el-input :model-value="model.deviceName" readonly placeholder="请选择设备">
      <template #append>
        <el-button @click="pickerVisible = true">选择</el-button>
      </template>
    </el-input>
  </template>

  <!-- 二级弹框写在 form-extra 内(父级已是 ProDialog,可自动识别嵌套并隐藏下层遮罩) -->
  <template #form-extra="{ model }">
    <ProDialog v-model="pickerVisible" title="选择设备" show-footer>
      <!-- 选中后写回 model.deviceName / model.deviceCode -->
    </ProDialog>
  </template>
</ProSmartTable>

新增/编辑表单弹框基于 ProDialog,遮罩与 @hz_yujin/vue-dialog 一致(浅色半透明 + 轻微毛玻璃);列管理抽屉同样使用该遮罩样式。

  • #form-{prop}:model / column / value / mode(create | edit)
  • #form-extra:model / mode / visible

必填:required: true 时,null / undefined / 空串 / 纯空格 / 空数组视为未填;0 / false / 数字 id 合法。


骨架屏与加载

场景 表现
loading=true 且无数据 默认骨架屏(onlyEmpty: true)
loading=true 且已有数据 表格上转圈
点击刷新 先发 refresh,并等到外部 loading 结束再停动画
点击全屏 整表区域铺满视口;再次点击或按 Esc 退出
<!-- 关闭骨架 -->
<ProSmartTable :skeleton="false" :loading="loading" />

<!-- 自定义 -->
<ProSmartTable :skeleton="{ rows: 8, animated: true, onlyEmpty: true }" />

全局注册

import VueSmartTable from '@hz_yujin/vue-smart-table'
app.use(VueSmartTable)

注册后可直接使用 <ProSmartTable> / <ProColumnEditor>。

也可按需引入:

import {
  ProSmartTable,
  ProColumnEditor,
  SMART_FORM_CONTROL_OPTIONS,
  createColumnId,
  normalizeColumn,
  cloneColumns,
  createEmptyColumn,
  resolveFormControlType,
} from '@hz_yujin/vue-smart-table'

工具函数

函数 说明
createColumnId(prefix?) 生成列 id
normalizeColumn(col) 补齐默认值(visible / formType / deletable 等)
cloneColumns(cols) 深拷贝并 normalize
createEmptyColumn(partial?) 新建 custom 列
resolveFormControlType(type) 统一别名(如 date-range → daterange)
SMART_FORM_CONTROL_OPTIONS 列管理里表单类型下拉选项

本地开发

# 在 monorepo 根目录
pnpm --filter @hz_yujin/vue-smart-table build
# playground 演示页:SmartTableDemo
pnpm --filter playground dev

License

MIT

Related Packages

z-crud-table

A powerful and flexible CRUD table component for Vue 3 and Element Plus.

5,151
c-admin-kit

基于 Vue 3 + Element Plus 的中后台企业级高阶通用组件与 Hooks 套件(强制使用 c-...

472
element-plus-table-dragable

A directive what used sortablejs to add drag support for element-plus's table co...

19,522 14
innoboxrr-vue-datatable

Datatable para vue3

2,321 0

Version History

Version Released License
0.1.12 MIT
0.1.11 MIT
0.1.10 MIT
0.1.9 MIT
0.1.7 MIT