| name | el-plus-crud-table |
| description | Data-driven table component with multi-level headers, pagination, tabs, row selection, export, summary rows, and tree expansion |
| version | 1.0.0 |
| origin | el-plus-crud |
| tags | ["vue3","element-plus","table","data-driven","pagination","multi-level-header"] |
ElPlusTable — Data-Driven Table
ElPlusTable is the core table component. Configure columns, data fetching, toolbar, pagination, tabs, tree expansion, and summary rows through ITableConfig. Supports multi-level headers, row/column merging, cross-page row selection, Excel export, and column visibility persistence.
When to Activate
- Using
<ElPlusTable> component
- Writing or modifying
ITableConfig / IColumnItem configuration
- Configuring multi-level headers, toolbar search, pagination, or tabs
- Implementing Excel export, row selection, summary rows, or tree expansion
- Calling expose methods:
reload / changeSelect / resetSelect / resetQuery
Core Concepts
ITableConfig Structure
interface ITableConfig {
tbName?: string
fetch?: IFetch<any>
fetchMap?: IFetchTableMap
column?: Array<IColumnItem>
toolbar?: ITableToolbar
tabConf?: ITableTabConf
queryMap?: any
explan?: IExplanConfig
summaryConf?: ISummaryConf
statistic?: IStatisticConfig
maxHeight?: number
tableAttr?: any
}
fetch and fetchMap
The fetch function must return a Promise. The response is parsed using fetchMap:
const tableConfig: ITableConfig = {
fetch: api.getList,
fetchMap: {
list: 'data.list',
page: { total: 'data.totalCount', current: 'data.pageNum' }
}
}
Multi-Level Headers
Use children for recursive nested headers. ElPlusTableColumn renders recursively.
const columns: Array<IColumnItem> = [
{ prop: 'name', label: '姓名', width: '120' },
{
label: '联系信息',
children: [
{ prop: 'phone', label: '手机号', width: '130' },
{ prop: 'email', label: '邮箱', width: '180' }
]
}
]
Column Types
| type | Description | Use Case |
|---|
| (default) | Text display | Regular data columns |
btns | Action buttons | Edit, delete, view operations |
image | Image preview | Avatar, photo columns |
tag | Tag display (el-tag) | Status columns with color-coded labels |
status | Status indicator | Active/inactive status |
link | Clickable link | External links, detail links |
Column Type: tag
Tag columns render an el-tag per cell. Controlled by three key properties:
tagType — el-tag color theme ('primary' | 'success' | 'warning' | 'danger' | 'info'):
tagType: (value) => value === 1 ? 'success' : 'danger'
tagType: 'statusColor'
format — Display text inside the tag:
format: (value) => ({ 0: '禁用', 1: '启用' }[value] || '未知')
format: 'statusLabel'
color — Custom color (overrides tagType color, table column only):
color: '#67C23A'
color: (value, row, field) => value === 1 ? '#67C23A' : '#F56C6C'
color: ['#67C23A', '#F56C6C']
attrs — Pass-through to el-tag:
attrs: { effect: 'dark', round: true, size: 'small' }
Toolbar (ITableToolbar)
interface ITableToolbar {
btns?: Array<any>
btnRight?: boolean
formConfig?: IFormConfig
export?: IExportConfig | Array<IExportConfig>
collapsible?: boolean
}
The toolbar search form uses ElPlusForm in isTable mode internally.
Row Selection (type='selection')
<ElPlusTable type="selection" :selectList="defaultSelected" @selection="onSelectionChange" />
- Cross-page selection:
allSelectRowList accumulates selections across pages
changeSelect(items, isSelect): Programmatically select/deselect rows
resetSelect(): Reset to initial selectList
- Auto-refreshes checkbox state on page load
Tabs (ITableTabConf)
const tabConf: ITableTabConf = {
prop: 'status',
tabs: [
{ label: '全部', value: '' },
{ label: '启用', value: 1, key: 'activeCount' },
{ label: '禁用', value: 0, key: 'inactiveCount' }
],
fetch: api.getTabCounts,
queryMap: { type: 'user' }
}
Excel Export (IExportConfig)
const exportConfig: IExportConfig = {
label: '导出Excel',
url: '/api/export/users',
name: '用户列表',
method: 'post',
isAuth: true,
tokenKey: 'Authorization',
noQuery: false,
beforeRequest: (data) => ({ ...data, extra: 'value' })
}
Summary Row (ISummaryConf)
const summaryConf: ISummaryConf = {
prop: 'amount,count',
label: '合计,总数',
format: 'yuan',
sumFn: (tableData, selectedRows) => '¥1,234.56',
hstyle: { fontWeight: 'bold' },
vif: () => tableData.value.length > 0
}
Row/Column Merging
const columns: Array<IColumnItem> = [
{ prop: 'department', label: '部门', isRowSpan: true, rsProps: ['department'] },
{ prop: 'name', label: '姓名' },
{ prop: 'monday', label: '周一', isColSpan: true },
{ prop: 'tuesday', label: '周二', isColSpan: true }
]
Column Visibility Persistence
When tbName is set, column show/hide settings are persisted to localStorage with key {storagePrefix}{tbName}.
Expose Methods
const tableRef = ref()
await tableRef.value.reload()
tableRef.value.tableData
tableRef.value.changeSelect(items, true)
tableRef.value.resetSelect()
tableRef.value.initCol()
tableRef.value.resetQuery()
tableRef.value.nextPage()
tableRef.value.hasNextPage()
Events
| Event | Payload | Description |
|---|
selection | selectedRows[] | Selection changed (cross-page accumulated) |
select | selection[], row | Single row selected/deselected |
selectAll | selection[], isRemove | Select all on current page |
tabChange | tabValue, initColFn | Tab switched |
queryChange | queryData | Search conditions changed |
expandChange | row, expanded | Row expanded/collapsed |
inited | tableData[] | Data loaded (debounced 400ms) |
headerReset | formData | Search form reset |
loadDataEnd | rawResponse | Raw API response received |
Code Examples
Basic Table
<ElPlusTable :tableConfig="tableConfig" />
const tableConfig: ITableConfig = {
fetch: api.getUserList,
column: [
{ prop: 'name', label: '姓名', width: '120' },
{ prop: 'phone', label: '手机号', width: '130' },
{ prop: 'email', label: '邮箱' },
{
type: 'btns', label: '操作', width: '180', fixed: 'right',
btns: [
{ label: '编辑', on: { click: (row) => handleEdit(row) } },
{ label: '删除', confirm: '确定删除?', on: { click: (row) => handleDelete(row) } }
]
}
]
}
Toolbar with Search Form
const tableConfig: ITableConfig = {
fetch: api.getUserList,
toolbar: {
collapsible: true,
formConfig: {
formDesc: {
keyword: { type: 'input', label: '关键词' },
status: { type: 'select', label: '状态', options: [{ l: '启用', v: 1 }, { l: '禁用', v: 0 }] }
},
column: 3,
collapsedRows: 1,
beforeRequest: (data) => {
if (data.keyword) data.name = data.keyword
delete data.keyword
return data
}
},
btns: [{ label: '新增', type: 'primary', on: { click: handleAdd } }]
},
column: [...]
}
Row Selection with Batch Operations
<ElPlusTable
type="selection"
:tableConfig="tableConfig"
:selectList="selectedRows"
@selection="onSelection"
/>
const selectedRows = ref([])
const onSelection = (rows: any[]) => { selectedRows.value = rows }
const batchDelete = async () => {
const ids = selectedRows.value.map(r => r.id)
await api.batchDelete({ ids })
tableRef.value.reload()
}
Action Column with Quick Button Types
{
type: 'btns', label: '操作', width: '200', fixed: 'right',
btns: [
{ label: '查看', type: 'primary', text: true, on: { click: (row) => viewDetail(row) } },
{ label: '编辑', type: 'warning', text: true, on: { click: (row) => editRow(row) } },
{ label: '删除', type: 'danger', text: true, confirm: '确定删除该记录?', on: { click: (row) => deleteRow(row) } }
]
}
Remote Data for Tabs with Counts
const tableConfig: ITableConfig = {
fetch: api.getOrders,
tabConf: {
prop: 'status',
tabs: [
{ label: '全部', value: '' },
{ label: (d) => `待付款(${d.pending || 0})`, value: 0, key: 'pending' },
{ label: (d) => `已付款(${d.paid || 0})`, value: 1, key: 'paid' }
],
fetch: api.getOrderCounts
},
column: [...]
}
Excel Export
toolbar: {
export: {
label: '导出',
url: '/api/users/export',
name: '用户数据',
method: 'post',
isAuth: true,
tokenKey: 'token'
}
}
Anti-Patterns
FAIL: Manually manage pagination
const page = ref(1)
const fetchData = () => api.getList({ current: page.value, size: 10 })
PASS: Let ElPlusTable manage pagination
const tableConfig: ITableConfig = {
fetch: api.getList
}
FAIL: Ignore fetchMap causing empty data
fetch: api.getList
PASS: Configure fetchMap or set global default in ICRUDConfig.table
fetch: api.getList,
fetchMap: { list: 'data.list', page: { total: 'data.total' } }
app.use(elPlusCrud, { table: { list: 'data.list', page: { total: 'data.total' } } })
FAIL: Use v-for to render columns manually
<el-table-column v-for="col in columns" :prop="col.prop" :label="col.label" />
PASS: Configure via ITableConfig.column
const tableConfig: ITableConfig = { column: columns }
Best Practices
- Configure
fetchMap globally in ICRUDConfig.table — avoid repeating in every table
- Fix action columns to the right:
fixed: 'right' with type: 'btns'
- Set
tbName on business tables to enable column visibility persistence
- Use
toolbar.collapsible + formConfig.collapsedRows for forms with many search fields
fetch must return data matching fetchMap structure, or data won't render
- Use
queryMap for static extra conditions, beforeRequest for dynamic transformations
Related Skills
el-plus-crud-config — ITableConfig, IColumnItem, IFetchTableMap type definitions (direct)
el-plus-crud-form — toolbar search form uses ElPlusForm isTable mode (indirect)