useTable
useTable 通过配置驱动 ElTable,你可以使用列定义数组代替模板中的多层 el-table-column 嵌套,并通过控制器统一管理表格状态。
这种方式在列较多、交互复杂或需要动态切换列配置的场景中更具可维护性。
基础表格
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const [Table] = useTable({
columns: [
{ prop: 'date', label: 'Date', width: 180 },
{ prop: 'name', label: 'Name', width: 180 },
{ prop: 'address', label: 'Address' },
],
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
});
</script>
<template>
<Table style="width: 100%" />
</template>带斑马纹表格
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const [Table] = useTable({
columns: [
{ prop: 'date', label: 'Date', width: 180 },
{ prop: 'name', label: 'Name', width: 180 },
{ prop: 'address', label: 'Address' },
],
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
stripe: true,
});
</script>
<template>
<Table style="width: 100%" />
</template>带边框表格
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const [Table] = useTable({
columns: [
{ prop: 'date', label: 'Date', width: 180 },
{ prop: 'name', label: 'Name', width: 180 },
{ prop: 'address', label: 'Address' },
],
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
border: true,
});
</script>
<template>
<Table style="width: 100%" />
</template>带状态表格
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
interface User {
date: string;
name: string;
address: string;
}
const [Table] = useTable<User>({
columns: [
{ prop: 'date', label: 'Date', width: 180 },
{ prop: 'name', label: 'Name', width: 180 },
{ prop: 'address', label: 'Address' },
],
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
rowClassName({ rowIndex }) {
if (rowIndex === 1) {
return 'warning-row';
} else if (rowIndex === 3) {
return 'success-row';
}
return '';
},
});
</script>
<template>
<Table style="width: 100%" />
</template>
<style>
.el-table .warning-row {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
.el-table .success-row {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
</style>固定表头
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const [Table] = useTable({
height: 250,
columns: [
{ prop: 'date', label: 'Date', width: 180 },
{ prop: 'name', label: 'Name', width: 180 },
{ prop: 'address', label: 'Address' },
],
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-08',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-06',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-07',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
});
</script>
<template>
<Table style="width: 100%" />
</template>固定列
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const handleClick = () => {
console.log('click');
};
const [Table] = useTable({
columns: [
{ fixed: true, prop: 'date', label: 'Date', width: 150 },
{ prop: 'name', label: 'Name', width: 120 },
{ prop: 'state', label: 'State', width: 120 },
{ prop: 'city', label: 'City', width: 120 },
{ prop: 'address', label: 'Address', width: 600 },
{ prop: 'zip', label: 'Zip', width: 120 },
{
fixed: 'right',
label: 'Operations',
minWidth: 120,
slot: 'operations',
},
],
data: [
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
tag: 'Home',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
tag: 'Office',
},
{
date: '2016-05-04',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
tag: 'Home',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
tag: 'Office',
},
],
});
</script>
<template>
<Table style="width: 100%">
<template #operations>
<el-button link type="primary" size="small" @click="handleClick">
Detail
</el-button>
<el-button link type="primary" size="small">Edit</el-button>
</template>
</Table>
</template>固定列和表头
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const [Table] = useTable({
height: 250,
columns: [
{ fixed: true, prop: 'date', label: 'Date', width: 150 },
{ prop: 'name', label: 'Name', width: 120 },
{ prop: 'state', label: 'State', width: 120 },
{ prop: 'city', label: 'City', width: 320 },
{ prop: 'address', label: 'Address', width: 600 },
{ prop: 'zip', label: 'Zip' },
],
data: [
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-04',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-08',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-06',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-07',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
],
});
</script>
<template>
<Table style="width: 100%" />
</template>流体高度
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
import dayjs from 'dayjs';
const now = new Date();
const tableData = [
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
];
const deleteRow = (index: number) => {
tableData.splice(index, 1);
setData(tableData);
};
const onAddItem = () => {
now.setDate(now.getDate() + 1);
tableData.push({
date: dayjs(now).format('YYYY-MM-DD'),
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
});
setData(tableData);
};
const [Table, { setData }] = useTable({
maxHeight: 250,
data: tableData,
columns: [
{ fixed: true, prop: 'date', label: 'Date', width: 150 },
{ prop: 'name', label: 'Name', width: 120 },
{ prop: 'state', label: 'State', width: 120 },
{ prop: 'city', label: 'City', width: 120 },
{ prop: 'address', label: 'Address', width: 600 },
{ prop: 'zip', label: 'Zip', width: 120 },
{
fixed: 'right',
label: 'Operations',
minWidth: 120,
slot: 'operations',
},
],
});
</script>
<template>
<Table style="width: 100%">
<template #operations="scope">
<el-button
link
type="primary"
size="small"
@click.prevent="deleteRow(scope.$index)"
>
Remove
</el-button>
</template>
</Table>
<el-button class="mt-4" style="width: 100%" @click="onAddItem">
Add Item
</el-button>
</template>多级表头
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const [Table] = useTable({
columns: [
{ prop: 'date', label: 'Date', width: 150 },
{
label: 'Delivery Info',
children: [
{ prop: 'name', label: 'Name', width: 120 },
{
label: 'Address Info',
children: [
{ prop: 'state', label: 'State', width: 120 },
{ prop: 'city', label: 'City', width: 120 },
{ prop: 'address', label: 'Address' },
{ prop: 'zip', label: 'Zip', width: 120 },
],
},
],
},
],
data: [
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-04',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-08',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-06',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-07',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
],
});
</script>
<template>
<Table style="width: 100%" />
</template>单选
vue
<script setup lang="ts">
import { ref } from 'vue';
import { useTable } from 'element-hooks';
interface User {
date: string;
name: string;
address: string;
}
const tableData: User[] = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
];
const currentRow = ref<User | null>(null);
const [Table, { instance }] = useTable({
data: tableData,
highlightCurrentRow: true,
columns: [
{ type: 'index', width: 50 },
{ prop: 'date', label: 'Date', width: 120 },
{ prop: 'name', label: 'Name', width: 120 },
{ prop: 'address', label: 'Address' },
],
onCurrentChange(val: User | null) {
currentRow.value = val;
},
});
const setCurrent = (row?: User) => {
instance.value?.setCurrentRow(row);
};
</script>
<template>
<Table style="width: 100%" />
<div style="margin-top: 20px">
<el-button @click="setCurrent(tableData[1])"> Select second row </el-button>
<el-button @click="setCurrent()"> Clear selection </el-button>
</div>
</template>多选
vue
<script setup lang="ts">
import { ref } from 'vue';
import { useTable } from 'element-hooks';
interface User {
id: number;
date: string;
name: string;
address: string;
}
const tableData: User[] = [
{
id: 1,
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 2,
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 3,
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 4,
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 5,
date: '2016-05-08',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 6,
date: '2016-05-06',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 7,
date: '2016-05-07',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
];
const multipleSelection = ref<User[]>([]);
const selectable = (row: User) => ![1, 2].includes(row.id);
const [Table, { instance }] = useTable({
data: tableData,
rowKey: 'id',
columns: [
{ type: 'selection', selectable, width: 55 },
{ label: 'Date', width: 120, slot: 'date' },
{ prop: 'name', label: 'Name', width: 120 },
{ prop: 'address', label: 'Address' },
],
onSelectionChange: (val: User[]) => {
multipleSelection.value = val;
},
});
const toggleSelection = (rows?: User[], ignoreSelectable?: boolean) => {
if (rows) {
rows.forEach(row => {
instance.value?.toggleRowSelection(row, undefined, ignoreSelectable);
});
} else {
instance.value?.clearSelection();
}
};
</script>
<template>
<Table style="width: 100%">
<template #date="scope">{{ scope.row.date }}</template>
</Table>
<div style="margin-top: 20px">
<el-button @click="toggleSelection([tableData[1], tableData[2]])">
Toggle selection status of second and third rows
</el-button>
<el-button @click="toggleSelection([tableData[1], tableData[2]], false)">
Toggle selection status based on selectable
</el-button>
<el-button @click="toggleSelection()">Clear selection</el-button>
</div>
</template>排序
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
interface User {
date: string;
name: string;
address: string;
}
const [Table] = useTable<User>({
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
defaultSort: { prop: 'date', order: 'descending' },
columns: [
{ prop: 'date', label: 'Date', sortable: true, width: 180 },
{ prop: 'name', label: 'Name', width: 180 },
{
prop: 'address',
label: 'Address',
formatter(row) {
return row.address;
},
},
],
});
</script>
<template>
<Table style="width: 100%" />
</template>筛选
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
interface User {
date: string;
name: string;
address: string;
tag: string;
}
const tableData: User[] = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
tag: 'Home',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
tag: 'Office',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
tag: 'Home',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
tag: 'Office',
},
];
const [Table, { instance }] = useTable({
data: tableData,
rowKey: 'date',
columns: [
{
prop: 'date',
label: 'Date',
sortable: true,
width: 180,
columnKey: 'date',
filters: [
{ text: '2016-05-01', value: '2016-05-01' },
{ text: '2016-05-02', value: '2016-05-02' },
{ text: '2016-05-03', value: '2016-05-03' },
{ text: '2016-05-04', value: '2016-05-04' },
],
filterMethod(value, row, column) {
const property = column['property'] as keyof User;
return row[property] === value;
},
},
{ prop: 'name', label: 'Name', width: 180 },
{
prop: 'address',
label: 'Address',
formatter(row) {
return row.address;
},
},
{
prop: 'tag',
label: 'Tag',
width: 100,
filters: [
{ text: 'Home', value: 'Home' },
{ text: 'Office', value: 'Office' },
],
filterMethod(value, row) {
return row.tag === value;
},
filterPlacement: 'bottom-end',
slot: 'tag',
},
],
});
const resetDateFilter = () => {
instance.value?.clearFilter(['date']);
};
const clearAllFilters = () => {
instance.value?.clearFilter();
};
</script>
<template>
<div style="margin-bottom: 20px">
<el-button @click="resetDateFilter">reset date filter</el-button>
<el-button @click="clearAllFilters">reset all filters</el-button>
</div>
<Table style="width: 100%">
<template #tag="scope">
<el-tag
:type="scope.row.tag === 'Home' ? 'primary' : 'success'"
disable-transitions
>
{{ scope.row.tag }}
</el-tag>
</template>
</Table>
</template>自定义列模板
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
import { Timer } from '@element-plus/icons-vue';
interface User {
date: string;
name: string;
address: string;
}
const handleEdit = (index: number, row: User) => {
console.log(index, row);
};
const handleDelete = (index: number, row: User) => {
console.log(index, row);
};
const [Table] = useTable<User>({
columns: [
{ label: 'Date', width: 180, slot: 'date' },
{ label: 'Name', width: 180, slot: 'name' },
{ label: 'Operations', slot: 'operations' },
],
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
});
</script>
<template>
<Table style="width: 100%">
<template #date="scope">
<div style="display: flex; align-items: center">
<el-icon><timer /></el-icon>
<span style="margin-left: 10px">{{ scope.row.date }}</span>
</div>
</template>
<template #name="scope">
<el-popover effect="light" trigger="hover" placement="top" width="auto">
<template #default>
<div>name: {{ scope.row.name }}</div>
<div>address: {{ scope.row.address }}</div>
</template>
<template #reference>
<el-tag>{{ scope.row.name }}</el-tag>
</template>
</el-popover>
</template>
<template #operations="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">
Edit
</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>
Delete
</el-button>
</template>
</Table>
</template>自定义表头
vue
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useTable } from 'element-hooks';
interface User {
date: string;
name: string;
address: string;
}
const search = ref('');
const tableData: User[] = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'John',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Morgan',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Jessy',
address: 'No. 189, Grove St, Los Angeles',
},
];
const handleEdit = (index: number, row: User) => {
console.log(index, row);
};
const handleDelete = (index: number, row: User) => {
console.log(index, row);
};
const [Table, { setData }] = useTable({
data: tableData,
columns: [
{ label: 'Date', prop: 'date' },
{ label: 'Name', prop: 'name' },
{
align: 'right',
slots: { header: 'operationsHeader', default: 'operations' },
},
],
});
watch(search, val => {
const filtered = tableData.filter(
data => !val || data.name.toLowerCase().includes(val.toLowerCase()),
);
setData(filtered);
});
</script>
<template>
<Table style="width: 100%">
<template #operationsHeader>
<el-input v-model="search" size="small" placeholder="Type to search" />
</template>
<template #operations="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">
Edit
</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>
Delete
</el-button>
</template>
</Table>
</template>展开行
switch parent border:
switch child border:
preserve expanded:
State:
City:
Address:
Zip:
Family
No Data
vue
<script setup lang="ts">
import { ref } from 'vue';
import { useTable } from 'element-hooks';
const parentBorder = ref(false);
const childBorder = ref(false);
const preserveExpanded = ref(false);
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
family: [
{
name: 'Jerry',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Spike',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Tyke',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
],
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
family: [
{
name: 'Jerry',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Spike',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Tyke',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
],
},
{
date: '2016-05-04',
name: 'Tom',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
family: [
{
name: 'Jerry',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Spike',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Tyke',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
],
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
family: [
{
name: 'Jerry',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Spike',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Tyke',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
],
},
{
date: '2016-05-08',
name: 'Tom',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
family: [
{
name: 'Jerry',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Spike',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Tyke',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
],
},
{
date: '2016-05-06',
name: 'Tom',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
family: [
{
name: 'Jerry',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Spike',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Tyke',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
],
},
{
date: '2016-05-07',
name: 'Tom',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
family: [
{
name: 'Jerry',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Spike',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
{
name: 'Tyke',
state: 'California',
city: 'San Francisco',
address: '3650 21st St, San Francisco',
zip: 'CA 94114',
},
],
},
];
const [Table] = useTable({
data: tableData,
columns: [
{ type: 'expand', slot: 'expand' },
{ label: 'Date', prop: 'date' },
{ label: 'Name', prop: 'name' },
],
});
const [ChildTable] = useTable({
columns: [
{ label: 'Name', prop: 'name' },
{ label: 'State', prop: 'state' },
{ label: 'City', prop: 'city' },
{ label: 'Address', prop: 'address' },
{ label: 'Zip', prop: 'zip' },
],
});
</script>
<template>
<div style="margin-bottom: 20px">
switch parent border: <el-switch v-model="parentBorder" /> switch child
border: <el-switch v-model="childBorder" /> preserve expanded:
<el-switch v-model="preserveExpanded" />
</div>
<Table
:border="parentBorder"
:preserve-expanded-content="preserveExpanded"
style="width: 100%"
>
<template #expand="props">
<div style="padding: 20px">
<p>State: {{ props.row.state }}</p>
<p>City: {{ props.row.city }}</p>
<p>Address: {{ props.row.address }}</p>
<p>Zip: {{ props.row.zip }}</p>
<h3>Family</h3>
<ChildTable :data="props.row.family" :border="childBorder" />
</div>
</template>
</Table>
</template>树形数据与懒加载
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
interface User {
id: number;
date: string;
name: string;
address: string;
hasChildren?: boolean;
children?: User[];
}
const tableData: User[] = [
{
id: 1,
date: '2016-05-02',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 2,
date: '2016-05-04',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 3,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
children: [
{
id: 31,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 32,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
],
},
{
id: 4,
date: '2016-05-03',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
];
const tableDataLazy: User[] = [
{
id: 1,
date: '2016-05-02',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 2,
date: '2016-05-04',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 3,
date: '2016-05-01',
name: 'wangxiaohu',
hasChildren: true,
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 4,
date: '2016-05-03',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
];
const load = (
_row: User,
_treeNode: unknown,
resolve: (data: User[]) => void,
) => {
setTimeout(() => {
resolve([
{
id: 31,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 32,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
]);
}, 1000);
};
const [Table] = useTable({
data: tableData,
rowKey: 'id',
border: true,
defaultExpandAll: true,
columns: [
{ prop: 'date', label: 'Date', sortable: true },
{ prop: 'name', label: 'Name', sortable: true },
{ prop: 'address', label: 'Address', sortable: true },
],
});
const [LazyTable] = useTable({
data: tableDataLazy,
rowKey: 'id',
border: true,
lazy: true,
load,
treeProps: { children: 'children', hasChildren: 'hasChildren' },
columns: [
{ prop: 'date', label: 'Date' },
{ prop: 'name', label: 'Name' },
{ prop: 'address', label: 'Address' },
],
});
</script>
<template>
<div>
<Table style="width: 100%; margin-bottom: 20px" />
<LazyTable style="width: 100%" />
</div>
</template>自定义索引
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
const [Table] = useTable({
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
columns: [
{
type: 'index',
index(index: number) {
return index * 2;
},
},
{ prop: 'date', label: 'Date', width: 180 },
{ prop: 'name', label: 'Name', width: 180 },
{ prop: 'address', label: 'Address' },
],
});
</script>
<template>
<Table style="width: 100%" />
</template>表格布局
vue
<script setup lang="ts">
import { ref } from 'vue';
import { useTable } from 'element-hooks';
const tableLayout = ref<'fixed' | 'auto'>('fixed');
const [Table] = useTable({
data: [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
],
columns: [
{ prop: 'date', label: 'Date' },
{ prop: 'name', label: 'Name' },
{ prop: 'address', label: 'Address' },
],
});
</script>
<template>
<div style="margin-bottom: 20px">
<el-radio-group v-model="tableLayout">
<el-radio-button value="fixed">fixed</el-radio-button>
<el-radio-button value="auto">auto</el-radio-button>
</el-radio-group>
</div>
<Table :table-layout="tableLayout" style="width: 100%" />
</template>合并列或行
vue
<script setup lang="ts">
import type { TableColumnCtx } from 'element-plus';
import { useTable } from 'element-hooks';
interface User {
id: string;
name: string;
amount1: string;
amount2: string;
amount3: number;
}
interface SpanMethodProps {
row: User;
column: TableColumnCtx<User>;
rowIndex: number;
columnIndex: number;
}
const tableData: User[] = [
{
id: '12987122',
name: 'Tom',
amount1: '234',
amount2: '3.2',
amount3: 10,
},
{
id: '12987123',
name: 'Tom',
amount1: '165',
amount2: '4.43',
amount3: 12,
},
{
id: '12987124',
name: 'Tom',
amount1: '324',
amount2: '1.9',
amount3: 9,
},
{
id: '12987125',
name: 'Tom',
amount1: '621',
amount2: '2.2',
amount3: 17,
},
{
id: '12987126',
name: 'Tom',
amount1: '539',
amount2: '4.1',
amount3: 15,
},
];
const [Table] = useTable({
data: tableData,
spanMethod({ rowIndex, columnIndex }: SpanMethodProps) {
if (rowIndex % 2 === 0) {
if (columnIndex === 0) {
return [1, 2];
} else if (columnIndex === 1) {
return [0, 0];
}
}
},
border: true,
columns: [
{ prop: 'id', label: 'ID', width: 180 },
{ prop: 'name', label: 'Name' },
{ prop: 'amount1', sortable: true, label: 'Amount 1' },
{ prop: 'amount2', sortable: true, label: 'Amount 2' },
{ prop: 'amount3', sortable: true, label: 'Amount 3' },
],
});
const [ObjectSpanTable] = useTable({
data: tableData,
spanMethod({ rowIndex, columnIndex }: SpanMethodProps) {
if (columnIndex === 0) {
if (rowIndex % 2 === 0) {
return {
rowspan: 2,
colspan: 1,
};
} else {
return {
rowspan: 0,
colspan: 0,
};
}
}
},
border: true,
columns: [
{ prop: 'id', label: 'ID', width: 180 },
{ prop: 'name', label: 'Name' },
{ prop: 'amount1', label: 'Amount 1' },
{ prop: 'amount2', label: 'Amount 2' },
{ prop: 'amount3', label: 'Amount 3' },
],
});
</script>
<template>
<Table style="width: 100%" />
<ObjectSpanTable style="width: 100%; margin-top: 20px" />
</template>自定义悬浮提示
vue
<script setup lang="ts">
import { h } from 'vue';
import { ElLink, type TableTooltipData } from 'element-plus';
import { useTable } from 'element-hooks';
type TableData = {
address: string;
tags: string[];
url: string;
};
const tableData: TableData[] = [
{
address: 'Lohrbergstr. 86c, Süd Lilli, Saarland',
tags: ['Office', 'Home', 'Park', 'Garden'],
url: 'https://github.com/element-plus/element-plus/issues',
},
{
address: '760 A Street, South Frankfield, Illinois',
tags: ['error', 'warning', 'success', 'info'],
url: 'https://github.com/element-plus/element-plus/pulls',
},
{
address: 'Arnold-Ohletz-Str. 41a, Alt Malinascheid, Thüringen',
tags: ['one', 'two', 'three', 'four', 'five'],
url: 'https://github.com/element-plus/element-plus/discussions',
},
{
address: '23618 Windsor Drive, West Ricardoview, Idaho',
tags: ['blue', 'white', 'dark', 'gray', 'red', 'bright'],
url: 'https://github.com/element-plus/element-plus/actions',
},
];
const tableRowFormatter = (data: TableTooltipData<TableData>) => {
return `${data.cellValue}: table formatter`;
};
const withVNode = (data: TableTooltipData<TableData>) => {
return h(ElLink, { type: 'primary', href: data.cellValue }, () =>
h('span', null, data.cellValue),
);
};
const [Table] = useTable({
data: tableData,
showOverflowTooltip: true,
tooltipFormatter: tableRowFormatter as any,
columns: [
{ prop: 'address', label: 'extends table formatter', width: 240 },
{
prop: 'tags',
label: 'formatter object',
width: 240,
tooltipFormatter: (({ row }: any) => row.tags.join(', ')) as any,
slot: 'tags',
},
{
prop: 'url',
label: 'with vnode',
width: 240,
tooltipFormatter: withVNode as any,
},
],
});
</script>
<template>
<Table style="width: 100%">
<template #tags="{ row }">
<el-tag
v-for="tag in row.tags"
:key="tag"
style="margin-right: 5px"
type="primary"
>
{{ tag }}
</el-tag>
</template>
</Table>
</template>始终显示悬浮提示
vue
<script setup lang="ts">
import { useTable } from 'element-hooks';
interface User {
date: string;
name: string;
address: string;
}
const [Table] = useTable<User>({
columns: [
{ type: 'selection', width: 55 },
{ prop: 'date', label: 'Date', width: 120, slot: 'date' },
{ property: 'name', label: 'Name', width: 120 },
{
property: 'address',
label: 'use show-overflow-tooltip',
width: 240,
showOverflowTooltip: true,
},
{ property: 'address', label: 'Address' },
],
data: [
{
date: '2016-05-04',
name: 'Aleyna Kutzner',
address: 'Lohrbergstr. 86c, Süd Lilli, Saarland',
},
{
date: '2016-05-03',
name: 'Helen Jacobi',
address: '760 A Street, South Frankfield, Illinois',
},
{
date: '2016-05-02',
name: 'Brandon Deckert',
address: 'Arnold-Ohletz-Str. 41a, Alt Malinascheid, Thüringen',
},
{
date: '2016-05-01',
name: 'Margie Smith',
address: '23618 Windsor Drive, West Ricardoview, Idaho',
},
],
});
</script>
<template>
<Table style="width: 100%">
<template #date="scope">{{ scope.row.date }}</template>
</Table>
</template>尾部合计行
vue
<script setup lang="ts">
import type { TableColumnCtx } from 'element-plus';
import type { VNode } from 'vue';
import { useTable } from 'element-hooks';
import { h } from 'vue';
interface Product {
id: string;
name: string;
amount1: string;
amount2: string;
amount3: number;
}
interface SummaryMethodProps<T extends Record<string, any> = Product> {
columns: TableColumnCtx<T>[];
data: T[];
}
const tableData: Product[] = [
{
id: '12987122',
name: 'Tom',
amount1: '234',
amount2: '3.2',
amount3: 10,
},
{
id: '12987123',
name: 'Tom',
amount1: '165',
amount2: '4.43',
amount3: 12,
},
{
id: '12987124',
name: 'Tom',
amount1: '324',
amount2: '1.9',
amount3: 9,
},
{
id: '12987125',
name: 'Tom',
amount1: '621',
amount2: '2.2',
amount3: 17,
},
{
id: '12987126',
name: 'Tom',
amount1: '539',
amount2: '4.1',
amount3: 15,
},
];
const [Table] = useTable({
data: tableData,
border: true,
showSummary: true,
columns: [
{ prop: 'id', label: 'ID', width: 180 },
{ prop: 'name', label: 'Name' },
{ prop: 'amount1', sortable: true, label: 'Amount 1' },
{ prop: 'amount2', sortable: true, label: 'Amount 2' },
{ prop: 'amount3', sortable: true, label: 'Amount 3' },
],
});
const [CustomSummaryTable] = useTable({
data: tableData,
border: true,
height: 200,
summaryMethod(param: SummaryMethodProps) {
const { columns, data } = param;
const sums: (string | VNode)[] = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = h('div', { style: { textDecoration: 'underline' } }, [
'Total Cost',
]);
return;
}
const values = data.map(item =>
Number(item[column.property as keyof Product]),
);
if (!values.every(value => Number.isNaN(value))) {
sums[index] = `$ ${values.reduce((prev, curr) => {
const value = Number(curr);
if (!Number.isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0)}`;
} else {
sums[index] = 'N/A';
}
});
return sums;
},
showSummary: true,
columns: [
{ prop: 'id', label: 'ID', width: 180 },
{ prop: 'name', label: 'Name' },
{ prop: 'amount1', label: 'Cost 1 ($)' },
{ prop: 'amount2', label: 'Cost 2 ($)' },
{ prop: 'amount3', label: 'Cost 3 ($)' },
],
});
</script>
<template>
<Table style="width: 100%" />
<CustomSummaryTable style="width: 100%; margin-top: 20px" />
</template>API
Options
useTable 的配置项继承自 Element Plus ElTable 的 Props,并额外增加以下字段:
columns:TableColumn[]—— 核心配置,用于声明表格列。data:T[]—— 表格数据。
TableColumn
属性与 ElTableColumn 的 props 一致,同时额外支持:
| 字段 | 说明 | 类型 |
|---|---|---|
slot | 默认插槽名 | string |
slots | 具名插槽配置 (default, header, filterIcon, expand) | Record<string, string> |
children | 子列配置 (用于多级表头) | TableColumn[] |
Controller
| 方法 | 说明 | 参数 |
|---|---|---|
setState | 动态更新表格整体配置 | (state: Partial<TableOptions>) => void |
setColumns | 动态更新列定义 | (columns: TableColumn[]) => void |
setData | 动态更新表格数据 | (data: T[]) => void |
getData | 获取当前数据快照 | () => T[] |
instance | 内部 ElTable 实例(可调用排序、选择等原生方法) | Ref<TableInstance> |