@hz_yujin/vue-smart-table
@hz_yujin/vue-smart-table
本包位于 monorepo
packages/smart-table。仓库根目录见 README。
基于 Element Plus el-table 的 Smart Table:一份列 schema 同时驱动表格列、查询条件与新增/编辑表单,并内置列管理抽屉、分页、骨架屏。
与 @hz_yujin/vue-base-table(VXE)互补,互不影响。
目录
- 安装
- 快速开始
- 设计思路
- SmartColumnSchema
- Props
- Events
- 插槽
- 方法(expose)
- 分页
- 远程列表(推荐写法)
- 排序
- 列管理与持久化
- 表单自定义 / 嵌套弹框
- 骨架屏与加载
- 全局注册
- 工具函数
安装
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 一致的模式:
v-model:pagination+mode: 'remote'@search/@reset(重置也会走search)回到第 1 页并拉数@page-change更新页码后拉数@create/@update调接口后刷新- 自定义「新增」时设
: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
A directive what used sortablejs to add drag support for element-plus's table co...
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 | |
| 0.1.6 | MIT | |
| 0.1.5 | MIT | |
| 0.1.4 | MIT | |
| 0.1.2 | MIT |