Appearance
介绍与安装
介绍
Admin Pro Table 是基于 Vue 3 和 Element Plus 的表格与表单组件库,组件统一以 Pro 为前缀,提供:
- ProTable:集成搜索表单、表格、分页与增删改的列表页方案,通过配置即可完成常见 CRUD 页面。
- ProFormPopup:配置化表单弹层,支持弹窗 / 抽屉 / 整页三种模式,可与 ProTable 配合使用,也可单独使用。
- ProFormContent:仅表单主体(无弹层、无底部按钮),用于自有容器场景。
- ProFormItem:单表单项,用于自定义布局或与 ProFormContent 组合。
- ProSearchForm:搜索区表单,可独立于 ProTable 使用。
适用于后台管理、数据管理等需要列表 + 搜索 + 新增/编辑表单的场景。
依赖
需已安装 Vue 3、Element Plus、@element-plus/icons-vue(版本要求见包 peerDependencies)。
安装
bash
# pnpm
pnpm install admin-pro-table
# npm
npm install admin-pro-table
# yarn
yarn add admin-pro-table使用
在入口文件中注册组件并引入样式(推荐使用默认安装,会注册全部 Pro 组件):
javascript
import { createApp } from 'vue'
import AdminProTable from 'admin-pro-table'
import 'admin-pro-table/dist/style.css'
import App from './App.vue'
const app = createApp(App)
app.use(AdminProTable)按需引入时:
javascript
import { ProTable, ProFormPopup, ProSearchForm, ProForm } from 'admin-pro-table'
import 'admin-pro-table/dist/style.css'
app.component('ProTable', ProTable)
app.component('ProFormPopup', ProFormPopup)
app.component('ProSearchForm', ProSearchForm)
app.component('ProForm', ProForm)在页面中通过 search-config、table-config、form-config 传入配置即可使用:
vue
<template>
<pro-table
:search-config="searchConfig"
:table-config="tableConfig"
:form-config="formConfig"
/>
</template>
<script setup>
// 定义 searchConfig、tableConfig、formConfig...
</script>AI 智能助手支持
本组件库内置 AI 使用指南(CLAUDE.md),安装后 AI 助手(如 Claude Code、Cursor 等)会自动读取并理解组件的完整配置结构,无需阅读文档即可准确帮你写配置。
对开发者的好处
- AI 能直接写出正确的
tableConfig/formConfig/searchConfig - 遇到问题时 AI 能给出精准的配置建议
- 组件升级后 AI 立即理解新 API,无需手动更新文档
工作原理
CLAUDE.md 是随包发布的机器可读文档,AI 工具在处理项目时会自动识别并加载,使 AI 能够准确理解组件的配置接口和行为逻辑。
示例
安装后,你可以直接告诉 AI:
「帮我写一个用户管理列表页,包含按姓名和状态搜索,有新增、编辑、删除功能,状态用 tag 显示」
AI 会根据 CLAUDE.md 中的配置规范,直接生成完整的 searchConfig、tableConfig、formConfig 配置代码。
全局配置
安装时可通过 app.use(AdminProTable, options) 的第二个参数传入全局配置。全局配置适合放项目级默认值,例如 列表请求 requestConfig、接口响应格式、分页字段名、表格默认样式、搜索表单布局、弹窗表单布局、上传配置、权限校验等。
javascript
import { createApp } from 'vue'
import AdminProTable from 'admin-pro-table'
import 'admin-pro-table/dist/style.css'
import App from './App.vue'
import { uploadFile } from '@/api/common'
import { useUserStore } from '@/stores/user'
const app = createApp(App)
app.use(AdminProTable, {
// 列表接口响应格式:统一把任意接口结果转换成 { list, total }
// 推荐放在 requestConfig 中;旧版顶层 responseFormat 仍兼容
requestConfig: {
responseFormat: (res) => ({
list: res.data?.records ?? res.data?.list ?? [],
total: res.data?.total ?? 0
}),
paramsFormat: (params) => ({ ...params, source: 'admin' }),
pageParams: true,
filterEmptyParams: true,
pageKey: 'current',
pageSizeKey: 'size',
defaultPageSize: 20
},
pageSizes: [10, 20, 50, 100],
// 接口成功判断:表单新增/编辑/保存成功后会读取 message 并提示
successCode: 0,
codeKey: 'code',
messageKey: 'message',
// 表格默认属性,单个表格可用 tableConfig.tableProps 覆盖
tableProps: {
border: true,
stripe: true,
size: 'default'
},
// 分页底栏默认配置,单个表格可用 tableConfig.footerConfig 覆盖
footerConfig: {
show: true,
showPagination: true,
showRefresh: true,
pageSizes: [10, 20, 50, 100, 200],
paginationLayout: 'total, sizes, prev, pager, next, jumper'
},
// 工具栏默认配置,单个表格可用 tableConfig.toolbarConfig 覆盖
toolbarConfig: {
showTools: true, // 右侧三件套总开关
showRefreshTool: true,
showSearchToggle: true, // 无 searchConfig 的页面仍不会显示
showColumnSetting: true
// columnSettingKey 建议按页面单独配,不要全局共用
},
// 操作列默认配置,单个表格可用 tableConfig.operationConfig 覆盖
// 只想改操作列按钮尺寸时用 operationConfig.size,不要用全局 buttonSize(会改所有内置按钮)
operationConfig: {
size: 'small',
width: 140
},
// 表格区域外边框默认,页面 tableConfig.bordered 优先(同搜索区 searchConfig.bordered)
tableConfig: {
bordered: false
},
// 搜索表单默认配置,单个页面 searchConfig 优先级更高
searchConfig: {
labelWidth: '80px',
span: 8,
gutter: 16,
collapse: true,
collapsedRows: 1,
enterSearch: true,
actionsConfig: {
show: true,
showSearch: true,
showReset: true,
searchText: '查询',
resetText: '重置'
}
},
// 新增/编辑弹层表单默认配置,单个页面 formConfig 优先级更高
formConfig: {
mode: 'dialog',
width: '640px',
labelWidth: '100px',
labelPosition: 'right',
span: 24,
formAttrs: {
statusIcon: true,
scrollToError: true
},
footer: {
show: true,
showCancel: true,
showSubmit: true,
submitText: '确定',
cancelText: '取消',
align: 'right'
}
},
// 上传默认配置:ProForm / ProFormPopup / ProSearchForm 内的 upload 字段都会继承
uploadConfig: {
api: uploadFile,
limit: 5,
maxSize: 10,
accept: 'image/*',
valueFormat: 'array',
triggerText: '上传文件'
},
// 权限校验:用于工具栏按钮和操作列按钮
hasPermission: (code) => {
const userStore = useUserStore()
const codes = Array.isArray(code) ? code : [code]
return codes.some((item) => userStore.permissions.includes(item))
},
// 全局组件尺寸(详见 /pro-table/size);主题色由项目 Element Plus 配置统一控制
size: 'default',
buttonSize: 'default',
// 空数据默认文案
emptyText: '暂无数据'
})
app.mount('#app')配置项说明
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| requestConfig | 列表请求全局默认配置,支持 params、paramsFormat、beforeRequest、afterRequest、responseFormat、pageParams、pageKey、pageSizeKey、defaultPageSize、filterEmptyParams、autoFetch、requestCompleted 等,单表 tableConfig.requestConfig 可覆盖;params 对象会与全局浅合并 | Object | - |
| responseFormat | 列表接口返回数据格式化,接收接口原始响应,返回 { list, total };推荐放入 requestConfig.responseFormat,顶层写法仍兼容 | Function | 内置解析 { data: { list, total } }、{ list, total }、{ data: [] }、{ list: [] } 或直接返回数组 |
| filterEmptyParams | 列表请求是否默认过滤空搜索条件;推荐放入 requestConfig.filterEmptyParams,顶层写法仍兼容 | Boolean | true |
| pageParams | 列表请求是否默认自动携带分页参数;推荐放入 requestConfig.pageParams,顶层写法仍兼容 | Boolean | true |
| defaultPageSize | 列表首次请求默认每页条数;推荐放入 requestConfig.defaultPageSize,顶层写法仍兼容 | Number | 20 |
| pageKey | 分页「当前页」请求参数名;推荐放入 requestConfig.pageKey,顶层写法仍兼容 | String | 'page' |
| pageSizeKey | 分页「每页条数」请求参数名;推荐放入 requestConfig.pageSizeKey,顶层写法仍兼容 | String | 'pageSize' |
| pageSizes | 分页器每页条数候选值 | Array | [10, 20, 50, 100] |
| footerConfig | 表格底栏默认配置,支持 show、showPagination、showRefresh、pageSizes、paginationLayout | Object | - |
| toolbarConfig | 工具栏全局默认配置,支持 showTools、showRefreshTool、showSearchToggle、showColumnSetting、columnSettingKey 等;页面 tableConfig.toolbarConfig 优先。详见 toolbarConfig | Object | - |
| operationConfig | 操作列全局默认配置,支持 size、width、showEdit、showDelete、showCopy 等;页面 tableConfig.operationConfig 优先。仅改操作列按钮尺寸用 { size: 'small' },详见 尺寸 | Object | - |
| tableConfig | 表格壳层全局默认,目前支持 bordered(表格区域外边框);页面 tableConfig.bordered 优先 | Object | - |
| tableProps | 表格全局默认属性,支持 Element Plus el-table 的所有属性,如 border、stripe、size、height | Object | - |
| size | 全局组件尺寸:large / default / small,自动合并到 tableProps 与 formAttrs,详见 尺寸 | String | - |
| buttonSize | 内置按钮尺寸(搜索、工具栏、操作列、表单底栏等),默认同 size | String | - |
| searchConfig | 搜索表单全局默认配置,如 labelWidth、span、adaptive、bordered、gutter、collapse、actionsConfig、uploadConfig | Object | - |
| formConfig | 表单弹层 / 独立表单全局默认配置,如 mode、width、labelWidth、span、formAttrs、footer、uploadConfig | Object | - |
| uploadConfig | 上传字段全局默认配置,可被 formConfig.uploadConfig、searchConfig.uploadConfig、字段级 field.uploadConfig 覆盖 | Object | - |
| hasPermission | 权限校验函数,工具栏和操作列配置权限码时会调用 | Function | - |
| emptyText | 表格空数据提示,也用于列空值默认展示 | String | '暂无数据,请稍后重试~' |
| successCode | 表单接口成功状态码,String(res[codeKey]) === String(successCode) 时提示成功消息 | Number | String | 1 |
| codeKey | 接口状态码字段名 | String | 'code' |
| messageKey | 接口消息字段名,成功提示优先读取该字段 | String | 'msg' |
覆盖优先级
全局配置只作为默认值,页面上的局部配置优先级更高:
text
字段级配置 > 当前页面配置 > 全局配置 > 组件内置默认值常见覆盖关系:
javascript
app.use(AdminProTable, {
tableProps: { border: true, stripe: true, size: 'default' },
searchConfig: { labelWidth: '80px', span: 8, gutter: 16 },
formConfig: { labelWidth: '100px', span: 24 },
uploadConfig: { limit: 5, maxSize: 10 }
})javascript
const tableConfig = {
// 覆盖全局 tableProps.size,其余 border / stripe 仍继承全局配置
tableProps: { size: 'small' }
}
const searchConfig = {
// 覆盖全局 span,其余 labelWidth / gutter 仍继承全局配置
span: 12,
fields: [
{ label: '姓名', key: 'name', component: 'input' }
]
}
const formConfig = {
// 覆盖全局 width / labelWidth,其余 span / footer 等仍继承全局配置
width: '800px',
labelWidth: '120px',
fields: [
{
label: '头像',
key: 'avatar',
component: 'upload',
// 字段级上传配置优先级最高
uploadConfig: { limit: 1, accept: 'image/*', valueFormat: 'url' }
}
]
}全局 requestConfig
列表请求的通用配置(响应解析、分页字段、参数格式化等)建议统一放在全局 requestConfig 中,单表只需配置 api 和业务差异:
javascript
app.use(AdminProTable, {
requestConfig: {
responseFormat: (res) => ({
list: res.data?.records ?? res.data?.list ?? [],
total: res.data?.total ?? 0
}),
paramsFormat: (params) => ({ ...params, source: 'admin' }),
pageKey: 'current',
pageSizeKey: 'size',
defaultPageSize: 20,
pageParams: true,
filterEmptyParams: true
}
})javascript
const tableConfig = {
requestConfig: {
api: getUserList,
params: { type: 'user' } // 与全局 params 浅合并
}
}合并优先级:
text
局部 tableConfig.requestConfig > 全局 requestConfig > 旧版顶层字段 > 组件内置默认值旧版顶层
pageKey、responseFormat、filterEmptyParams等写法仍兼容,但新项目建议统一使用requestConfig。详见 全局 requestConfig。
接口响应格式配置
ProTable 的列表请求需要得到 { list, total }。如果你的接口不是默认结构,可以在全局 requestConfig.responseFormat 中统一转换。
例如后端返回:
javascript
{
code: 0,
message: 'success',
data: {
records: [
{ id: 1, name: 'Tom' }
],
total: 32
}
}可全局配置:
javascript
app.use(AdminProTable, {
requestConfig: {
responseFormat: (res) => ({
list: res.data?.records ?? [],
total: res.data?.total ?? 0
})
},
successCode: 0,
codeKey: 'code',
messageKey: 'message'
})如果接口响应需要先做预处理,再解析列表数据,可以配合 afterRequest 和 responseFormat:
javascript
const tableConfig = {
requestConfig: {
api: getOrderList,
afterRequest: (res) => res.result,
responseFormat: (res) => ({
list: res.items ?? [],
total: res.count ?? 0
})
}
}afterRequest 用于预处理接口原始响应,responseFormat 用于把响应转换成表格需要的 { list, total }。执行顺序是:api → afterRequest → responseFormat。
单个表格接口结构特殊时,也可以只在当前表格覆盖:
javascript
const tableConfig = {
requestConfig: {
api: getOrderList,
responseFormat: (res) => ({
list: res.result?.items ?? [],
total: res.result?.count ?? 0
})
},
columns: [
{ label: '订单号', key: 'orderNo' },
{ label: '金额', key: 'amount' }
]
}分页参数配置
默认请求参数为:
javascript
{
page: 1,
pageSize: 20,
...searchParams
}如果后端需要 current / size,在全局 requestConfig 中配置:
javascript
app.use(AdminProTable, {
requestConfig: {
pageKey: 'current',
pageSizeKey: 'size',
defaultPageSize: 20
}
})某个接口单独使用其他字段名时,可在 requestConfig 中覆盖:
javascript
const tableConfig = {
requestConfig: {
api: getList,
pageKey: 'pageNo',
pageSizeKey: 'limit',
defaultPageSize: 50
}
}如果接口没有分页,不希望自动携带分页字段,可以关闭分页参数。关闭后会同时自动隐藏分页器:
javascript
// 全局关闭:所有 ProTable 默认都不传分页字段
app.use(AdminProTable, {
requestConfig: { pageParams: false }
})
// 单个表格关闭:仅当前列表不传分页字段
const tableConfig = {
requestConfig: {
api: getDictList,
pageParams: false
}
}局部配置优先于全局配置,因此全局关闭后,某个分页接口仍可单独开启:
javascript
const tableConfig = {
requestConfig: {
api: getPagedList,
pageParams: true
}
}pageParams 和 footerConfig.showPagination 的职责不同:pageParams: false 表示接口不分页,会不传分页参数并强制隐藏分页器;footerConfig.showPagination: false 只控制分页器显示,不影响请求是否携带分页参数。
请求参数格式化
搜索条件默认会过滤空字符串、null、undefined 和空数组。如果接口需要接收空值,可在全局 requestConfig 中关闭:
javascript
app.use(AdminProTable, {
requestConfig: { filterEmptyParams: false }
})也可以只对单个表格关闭:
javascript
const tableConfig = {
requestConfig: {
api: getUserList,
filterEmptyParams: false
}
}paramsFormat 可在全局或单表配置,用于统一转换最终请求参数。它会在固定参数、分页参数、搜索条件合并后执行,并早于 beforeRequest:
javascript
// 全局:所有表格请求统一补充字段
app.use(AdminProTable, {
requestConfig: {
paramsFormat: (params) => ({ ...params, source: 'admin' })
}
})
// 单表:覆盖或追加转换逻辑
const tableConfig = {
requestConfig: {
api: getUserList,
paramsFormat: (params) => ({
...params,
status: params.status === '' ? undefined : Number(params.status)
})
}
}paramsFormat 适合做字段改名、类型转换、补充固定字段;如果需要按条件取消请求,使用 beforeRequest 并返回 false。
全局搜索表单默认值
适合统一搜索区布局、按钮文案、折叠行为:
javascript
app.use(AdminProTable, {
searchConfig: {
labelWidth: '84px',
span: 8,
gutter: 16,
collapse: true,
collapsedRows: 1,
defaultCollapsed: false,
enterSearch: true,
actionsConfig: {
show: true,
showSearch: true,
showReset: true,
searchText: '搜索',
resetText: '清空'
}
}
})页面中只需要写差异部分:
javascript
const searchConfig = {
model: { name: '', status: '' },
fields: [
{ label: '姓名', key: 'name', component: 'input' },
{
label: '状态',
key: 'status',
component: 'select',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
}
]
}全局表单默认值
适合统一弹窗宽度、表单 label、底部按钮和 Element Plus Form 属性:
javascript
app.use(AdminProTable, {
formConfig: {
mode: 'dialog',
width: '720px',
labelWidth: '110px',
labelPosition: 'right',
span: 24,
formAttrs: {
statusIcon: true,
scrollToError: true,
validateOnRuleChange: false
},
footer: {
show: true,
showCancel: true,
showSubmit: true,
showSave: false,
cancelText: '取消',
submitText: '提交',
saveBtnTitle: '保存草稿',
align: 'right'
}
}
})页面表单可以覆盖局部配置:
javascript
const formConfig = {
width: '900px',
labelWidth: '130px',
span: 12,
model: { name: '', phone: '', remark: '' },
fields: [
{ label: '姓名', key: 'name', component: 'input' },
{ label: '手机号', key: 'phone', component: 'input' },
{ label: '备注', key: 'remark', component: 'textarea', rows: 3, span: 24 }
],
add: { title: '新增用户', api: createUser },
edit: { title: '编辑用户', api: updateUser }
}全局上传配置
uploadConfig 可以配置在三个层级:
text
字段级 field.uploadConfig > 当前表单/搜索 uploadConfig > 全局 uploadConfig全局配置示例:
javascript
app.use(AdminProTable, {
uploadConfig: {
api: async (file, formData) => {
const data = new FormData()
data.append('file', file)
data.append('bizType', formData.type || 'default')
const res = await uploadFile(data)
return {
url: res.data.url,
name: res.data.name || file.name
}
},
limit: 5,
maxSize: 10,
accept: 'image/*',
valueFormat: 'array',
triggerText: '点击上传',
showName: true
}
})字段覆盖示例:
javascript
fields: [
{
label: '头像',
key: 'avatar',
component: 'upload',
uploadConfig: {
limit: 1,
accept: 'image/*',
valueFormat: 'url',
triggerText: '上传头像'
}
},
{
label: '附件',
key: 'files',
component: 'upload',
uploadConfig: {
limit: 10,
accept: '.pdf,.doc,.docx,.xlsx',
valueFormat: 'array'
}
}
]权限校验配置
配置 hasPermission 后,工具栏按钮和操作列按钮可以通过权限码控制显示。
javascript
app.use(AdminProTable, {
hasPermission: (code) => {
const permissions = JSON.parse(localStorage.getItem('permissions') || '[]')
const codes = Array.isArray(code) ? code : [code]
return codes.some((item) => permissions.includes(item))
}
})页面中配置权限码:
javascript
const tableConfig = {
toolbarConfig: {
showAdd: true,
showDelete: true,
addPermission: 'user:add',
deletePermission: 'user:delete'
},
operationConfig: {
showEdit: true,
showDelete: true,
showCopy: true,
editPermission: 'user:edit',
deletePermission: (row) => row.systemBuiltIn ? '' : 'user:delete',
copyPermission: ['user:add', 'user:copy']
}
}未配置权限码,或未配置全局
hasPermission时,按钮默认显示。
完整页面示例
下面示例展示全局配置后,页面只保留业务差异配置:
vue
<template>
<ProTable
:search-config="searchConfig"
:table-config="tableConfig"
:form-config="formConfig"
>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
{{ row.status === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</ProTable>
</template>
<script setup>
import { reactive } from 'vue'
import { getUserList, createUser, updateUser, deleteUser } from '@/api/user'
const searchConfig = reactive({
model: { name: '', status: '' },
fields: [
{ label: '姓名', key: 'name', component: 'input' },
{
label: '状态',
key: 'status',
component: 'select',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
}
]
})
const tableConfig = reactive({
requestConfig: {
api: getUserList,
params: { source: 'admin' }
},
columns: [
{ label: '姓名', key: 'name', minWidth: 120 },
{ label: '状态', key: 'status', slot: 'status', width: 100 },
{ label: '创建时间', key: 'createTime', width: 180 }
],
rowKey: 'id',
toolbarConfig: {
showAdd: true,
showDelete: true,
addPermission: 'user:add',
deletePermission: 'user:delete'
},
operationConfig: {
showEdit: true,
showDelete: true,
editPermission: 'user:edit',
deletePermission: 'user:delete'
},
deleteConfig: {
api: deleteUser,
idField: 'id',
paramKey: 'id'
}
})
const formConfig = reactive({
model: { name: '', phone: '', status: 1, avatar: '', remark: '' },
fields: [
{ label: '姓名', key: 'name', component: 'input', rules: [{ required: true, message: '请输入姓名' }] },
{ label: '手机号', key: 'phone', component: 'input' },
{
label: '状态',
key: 'status',
component: 'select',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
},
{
label: '头像',
key: 'avatar',
component: 'upload',
uploadConfig: { limit: 1, valueFormat: 'url' }
},
{ label: '备注', key: 'remark', component: 'textarea', rows: 3, span: 24 }
],
add: { title: '新增用户', api: createUser },
edit: { title: '编辑用户', api: updateUser }
})
</script>