Basic data binding with columns, pagination, and event handling.
// Columns definition
const basicColumns = [
{ key: 'id', name: 'ID' },
{ key: 'caseName', name: 'Case Name' },
{ key: 'taskName', name: 'Task Name' },
{ key: 'userId', name: 'User ID' },
{ key: 'createTime', name: 'Create Time', timestamp: 'unix' },
];
Table borders and column content alignment via border and align props.
// Alignment options: 'left' | 'center' | 'right'
const styleColumns = [
{ key: 'id', name: 'ID', align: 'center' },
{ key: 'caseName', name: 'Case Name', align: 'left' },
{ key: 'userId', name: 'User ID', align: 'right' },
];
Fix columns to left or right using fixed: "left" or fixed: "right".
const fixedColumns = [
{ key: 'id', name: 'ID', fixed: 'left', width: 80 },
{ key: 'caseName', name: 'Case Name', fixed: 'left', width: 200 },
{ key: 'taskName', name: 'Task Name' },
{ key: 'userId', name: 'User ID' },
{ key: 'content', name: 'Content' },
{ key: 'createTime', name: 'Create Time', timestamp: 'unix' },
{ key: 'actions', name: 'Actions', fixed: 'right', width: 120 },
];
Built-in formatting for timestamps, dictionaries, and file sizes.
const formatColumns = [
{ key: 'id', name: 'ID' },
{ key: 'caseName', name: 'Case Name' },
// timestamp: 'unix' formats Unix timestamp (seconds)
{ key: 'createTime', name: 'Create Time', timestamp: 'unix' },
// filesize: true formats bytes to K/M/G
{ key: 'fileSize', name: 'File Size', filesize: true },
// dict uses state.dict for lookup
{ key: 'status', name: 'Status', dict: 'test' },
];
Custom cell content via cellRenderer function. Returns JSX. Critical: must work correctly with the
mini-table probe row for dynamic height measurement.
import { CellRenderer, CellRendererParams } from 'element-plus';
import { ElTag } from 'element-plus';
// Custom cellRenderer returning JSX
const statusRenderer: CellRenderer<T> = ({ cellData }) => {
const status = cellData as string;
const type = status === 'active' ? 'success' : status === 'pending' ? 'warning' : 'info';
return <ElTag type={type}>{status}</ElTag>;
};
const rendererColumns = [
{ key: 'id', name: 'ID' },
{ key: 'caseName', name: 'Case Name' },
{ key: 'status', name: 'Status', cellRenderer: statusRenderer },
{ key: 'actions', name: 'Actions', cellRenderer: actionRenderer },
];
Custom header content via headerCellRenderer function. Must also sync with mini-table header probe.
import { HeaderCellRenderer, HeaderCellRendererParams } from 'element-plus';
// Custom headerCellRenderer returning JSX
const headerRenderer: HeaderCellRenderer<T> = ({ column }) => {
const col: ListTableColumn<T> = column._listTableColumn;
return (
<span>
<span style="color: var(--el-color-primary)">*</span> {col.title}
</span>
);
};
const headerColumns = [
{ key: 'id', name: 'ID', headerCellRenderer },
{ key: 'caseName', name: 'Case Name', headerCellRenderer },
{ key: 'taskName', name: 'Task Name', headerCellRenderer },
];
Dynamic height mode uses a hidden mini-table to probe actual row and header heights.
debug shows estimated values. No rowHeight or estimatedRowHeight needed —
it measures automatically.
// Dynamic height mode (no rowHeight set)
// Mini-table probe automatically measures row height
// ResizeObserver debounces at 50ms to prevent flicker
// Fixed height mode (rowHeight set) - disables probe
// <ListTableV2 :row-height="60" ...> // No probe needed
// Manual estimate mode
// <ListTableV2 :estimated-row-height="50" ...> // Uses this as starting estimate
Fixed height mode via :row-height="60". Disables the mini-table probe. Better performance for large
datasets since no measurement needed.
// Fixed row height - no mini-table probe needed
// Better performance for large datasets
<ListTableV2
:data="data"
:columns="columns"
:row-key="'id'"
:row-height="60"
:border="true"
debug
/>
Control column widths with width (fixed) and minWidth (flexible). Columns without
explicit width use flexGrow: 1 to fill remaining space.
const widthColumns = [
{ key: 'id', name: 'ID', width: 80 }, // Fixed 80px
{ key: 'caseName', name: 'Case Name', minWidth: 200 }, // Min 200px, grows
{ key: 'taskName', name: 'Task Name', minWidth: 150 }, // Min 150px, grows
{ key: 'userId', name: 'User ID', width: 120 }, // Fixed 120px
// Remaining space distributed proportionally
];
Control table dimensions with height, maxHeight, and headerHeight.
Auto-resizer handles width automatically.
// Fixed height with scrolling internally
<ListTableV2 :height="400" ...>
// Max height - table grows but caps at max
<ListTableV2 :max-height="300" ...>
// Custom header height (default is auto-probed)
<ListTableV2 :header-height="60" ...>
Custom cell content via Vue template slots. Use slot: true on column and define
#columnKey template in parent.
// In template
<ListTableV2 :columns="slotColumns" ...>
<template #status="{ row }">
<el-tag>{row.status}</el-tag>
</template>
<template #actions="{ row }">
<el-button>Edit</el-button>
</template>
</ListTableV2>
// In script
const slotColumns = [
{ key: 'id', name: 'ID', slot: true },
{ key: 'caseName', name: 'Case Name' },
{ key: 'status', name: 'Status', slot: true },
{ key: 'actions', name: 'Actions', slot: true },
];
Column headers support i18n via i18n key (uses vue3-i18n).
// The component uses t() from vue3-i18n internally
// Column with i18n key:
const i18nColumns = [
{ key: 'id', name: 'ID' },
{ key: 'caseName', i18n: 'table.props.0' }, // Uses t('table.props.0')
{ key: 'taskName', i18n: 'table.props.1' },
];
All features working together: fixed columns + custom renderers + pagination + dynamic height probe + events. This is the stress test.
// Combined: fixed columns + custom renderers + pagination + i18n
const combinedColumns = [
{ key: 'id', name: 'ID', fixed: 'left', width: 80 },
{ key: 'caseName', i18n: 'table.props.0', fixed: 'left' },
{ key: 'taskName', i18n: 'table.props.1' },
{ key: 'userId', name: 'User', dict: 'test' },
{ key: 'createTime', name: 'Time', timestamp: 'unix', minWidth: 180 },
{ key: 'combinedStatus', name: 'Status', slot: true },
{ key: 'combinedActions', name: 'Actions', fixed: 'right', slot: true, width: 140 },
];
Edge case: custom cellRenderer combined with fixed columns and
pagination. Verifies mini-table probe handles custom renderers correctly across page changes.
// Edge case: cellRenderer + fixed columns + pagination
// Must verify mini-table probe renders custom JSX correctly
const edgeColumns = [
{ key: 'id', fixed: 'left', cellRenderer: edgeIdRenderer },
{ key: 'caseName', cellRenderer: edgeNameRenderer },
{ key: 'priority', cellRenderer: edgePriorityRenderer },
{ key: 'actions', fixed: 'right', cellRenderer: edgeActionsRenderer },
];