Table 表格
展示行列数据。
设计师专属
安装 Kitchen Sketch 插件 💎,两步就可以自动生成 Ant Design 表格组件。
何时使用
- 当有大量结构化的数据需要展现时;
- 当需要对数据进行排序、搜索、分页、自定义操作等复杂行为时。
如何使用
指定表格的数据源 dataSource
为一个数组。
<template>
<a-table :dataSource="dataSource" :columns="columns" />
</template>
<script>
export default {
setup() {
return {
dataSource: [
{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号',
},
{
key: '2',
name: '胡彦祖',
age: 42,
address: '西湖区湖底公园1号',
},
],
columns: [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '住址',
dataIndex: 'address',
key: 'address',
},
],
};
},
};
</script>
代码演示
简单的表格,最后一列是各种操作。
<template>
<a-table :columns="columns" :data-source="data">
<template #name="{ text }">
<a>{{ text }}</a>
</template>
<template #customTitle>
<span>
<smile-outlined />
Name
</span>
</template>
<template #tags="{ text: tags }">
<span>
<a-tag
v-for="tag in tags"
:key="tag"
:color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
>
{{ tag.toUpperCase() }}
</a-tag>
</span>
</template>
<template #action="{ record }">
<span>
<a>Invite 一 {{ record.name }}</a>
<a-divider type="vertical" />
<a>Delete</a>
<a-divider type="vertical" />
<a class="ant-dropdown-link">
More actions
<down-outlined />
</a>
</span>
</template>
</a-table>
</template>
<script lang="ts">
import { SmileOutlined, DownOutlined } from '@ant-design/icons-vue';
import { defineComponent } from 'vue';
const columns = [
{
dataIndex: 'name',
key: 'name',
slots: { title: 'customTitle', customRender: 'name' },
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
slots: { customRender: 'tags' },
},
{
title: 'Action',
key: 'action',
slots: { customRender: 'action' },
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
export default defineComponent({
setup() {
return {
data,
columns,
};
},
components: {
SmileOutlined,
DownOutlined,
},
});
</script>
这个例子通过简单的 ajax 读取方式,演示了如何从服务端读取并展现数据,具有筛选、排序等功能以及页面 loading 效果。开发者可以自行接入其他数据处理方式。
另外,本例也展示了筛选排序功能如何交给服务端实现,列不需要指定具体的 onFilter
和 sorter
函数,而是在把筛选和排序的参数发到服务端来处理。
<template>
<a-table
:columns="columns"
:row-key="record => record.login.uuid"
:data-source="dataSource"
:pagination="pagination"
:loading="loading"
@change="handleTableChange"
>
<template #name="{ text }">{{ text.first }} {{ text.last }}</template>
</a-table>
</template>
<script lang="ts">
import { TableState, TableStateFilters } from 'ant-design-vue/es/table/interface';
import { useRequest } from 'vue-request';
import axios from 'axios';
import { defineComponent, reactive, UnwrapRef } from 'vue';
const columns = [
{
title: 'Name',
dataIndex: 'name',
sorter: true,
width: '20%',
slots: { customRender: 'name' },
},
{
title: 'Gender',
dataIndex: 'gender',
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
width: '20%',
},
{
title: 'Email',
dataIndex: 'email',
},
];
type Pagination = TableState['pagination'];
type APIParams = {
results: number;
page?: number;
sortField?: string;
sortOrder?: number;
[key: string]: any;
};
type APIResult = {
results: {
gender: 'female' | 'male';
name: string;
email: string;
}[];
};
const queryData = (params: APIParams) => {
return axios.get<APIResult>('https://randomuser.me/api', { params: params });
};
export default defineComponent({
setup() {
const pagination: UnwrapRef<Pagination> = reactive({ total: 200 });
const { data: dataSource, run, loading } = useRequest(queryData, {
defaultParams: [
{
results: 10,
},
],
formatResult: res => res.data.results,
});
const handleTableChange = (pag: Pagination, filters: TableStateFilters, sorter: any) => {
console.log(pag);
Object.assign(pagination, pag);
run({
results: pagination.pageSize!,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
return {
dataSource,
pagination,
loading,
columns,
handleTableChange,
};
},
});
</script>
添加表格边框线,页头和页脚。
<template>
<a-table :columns="columns" :data-source="data" bordered>
<template #name="{ text }">
<a>{{ text }}</a>
</template>
<template #title>Header</template>
<template #footer>Footer</template>
</a-table>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{
title: 'Name',
dataIndex: 'name',
slots: { customRender: 'name' },
},
{
title: 'Cash Assets',
className: 'column-money',
dataIndex: 'money',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [
{
key: '1',
name: 'John Brown',
money: '¥300,000.00',
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
money: '¥1,256,000.00',
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
money: '¥120,000.00',
address: 'Sidney No. 1 Lake Park',
},
];
export default defineComponent({
setup() {
return {
data,
columns,
};
},
});
</script>
<style>
th.column-money,
td.column-money {
text-align: right !important;
}
</style>
设置 column.ellipsis
可以让单元格内容根据宽度自动省略。
列头缩略暂不支持和排序筛选一起使用。
<template>
<a-table :columns="columns" :data-source="data">
<template #name="{ text }">
<a>{{ text }}</a>
</template>
</a-table>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
slots: { customRender: 'name' },
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 80,
},
{
title: 'Address',
dataIndex: 'address',
key: 'address 1',
ellipsis: true,
},
{
title: 'Long Column Long Column Long Column',
dataIndex: 'address',
key: 'address 2',
ellipsis: true,
},
{
title: 'Long Column Long Column',
dataIndex: 'address',
key: 'address 3',
ellipsis: true,
},
{
title: 'Long Column',
dataIndex: 'address',
key: 'address 4',
ellipsis: true,
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park, London No. 2 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
export default defineComponent({
setup() {
return {
data,
columns,
};
},
});
</script>
表头只支持列合并,使用 column 里的 colSpan 进行设置。 表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时,设置的表格不会渲染。
<template>
<a-table :columns="columns" :data-source="data" bordered>
<template #name="{ text }">
<a>{{ text }}</a>
</template>
</a-table>
</template>
<script lang="ts">
import { defineComponent, h } from 'vue';
import { ColumnProps } from 'ant-design-vue/es/table/interface';
// In the fifth row, other columns are merged into first column
// by setting it's colSpan to be 0
const renderContent = ({ text, index }: any) => {
const obj = {
children: text,
props: {} as any,
};
if (index === 4) {
obj.props.colSpan = 0;
}
return obj;
};
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
tel: '0571-22098909',
phone: 18889898989,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
tel: '0571-22098333',
phone: 18889898888,
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
tel: '0575-22098909',
phone: 18900010002,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'London No. 2 Lake Park',
},
{
key: '5',
name: 'Jake White',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'Dublin No. 2 Lake Park',
},
];
export default defineComponent({
setup() {
const columns: ColumnProps[] = [
{
title: 'Name',
dataIndex: 'name',
customRender: ({ text, index }) => {
if (index < 4) {
return h('a', { href: 'javascript:;' }, text);
}
return {
children: h('a', { href: 'javascript:;' }, text),
props: {
colSpan: 5,
},
};
},
},
{
title: 'Age',
dataIndex: 'age',
customRender: renderContent,
},
{
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
customRender: ({ text, index }) => {
const obj = {
children: text,
props: {} as any,
};
if (index === 2) {
obj.props.rowSpan = 2;
}
// These two are merged into above cell
if (index === 3) {
obj.props.rowSpan = 0;
}
if (index === 4) {
obj.props.colSpan = 0;
}
return obj;
},
},
{
title: 'Phone',
colSpan: 0,
dataIndex: 'phone',
customRender: renderContent,
},
{
title: 'Address',
dataIndex: 'address',
customRender: renderContent,
},
];
return {
data,
columns,
};
},
});
</script>
通过 filterDropdown
定义自定义的列筛选功能,并实现一个搜索列的示例。
<template>
<a-table :data-source="data" :columns="columns">
<template #filterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
<div style="padding: 8px">
<a-input
ref="searchInput"
:placeholder="`Search ${column.dataIndex}`"
:value="selectedKeys[0]"
style="width: 188px; margin-bottom: 8px; display: block"
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
@pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)"
/>
<a-button
type="primary"
size="small"
style="width: 90px; margin-right: 8px"
@click="handleSearch(selectedKeys, confirm, column.dataIndex)"
>
<template #icon><SearchOutlined /></template>
Search
</a-button>
<a-button size="small" style="width: 90px" @click="handleReset(clearFilters)">
Reset
</a-button>
</div>
</template>
<template #filterIcon="filtered">
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
</template>
<template #customRender="{ text, column }">
<span v-if="searchText && searchedColumn === column.dataIndex">
<template
v-for="(fragment, i) in text
.toString()
.split(new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i'))"
>
<mark
v-if="fragment.toLowerCase() === searchText.toLowerCase()"
class="highlight"
:key="i"
>
{{ fragment }}
</mark>
<template v-else>{{ fragment }}</template>
</template>
</span>
<template v-else>
{{ text }}
</template>
</template>
</a-table>
</template>
<script>
import { SearchOutlined } from '@ant-design/icons-vue';
import { defineComponent, reactive, ref } from 'vue';
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Joe Black',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Jim Green',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
export default defineComponent({
components: {
SearchOutlined,
},
setup() {
const state = reactive({
searchText: '',
searchedColumn: '',
});
const searchInput = ref();
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) =>
record.name.toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => {
console.log(searchInput.value);
searchInput.value.focus();
}, 0);
}
},
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) =>
record.age.toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => {
searchInput.value.focus();
});
}
},
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) =>
record.address.toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => {
searchInput.value.focus();
});
}
},
},
];
const handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm();
console.log(selectedKeys[0]);
state.searchText = selectedKeys[0];
state.searchedColumn = dataIndex;
};
const handleReset = clearFilters => {
clearFilters();
state.searchText = '';
};
return {
data,
columns,
handleSearch,
handleReset,
searchText: '',
searchInput: null,
searchedColumn: '',
};
},
});
</script>
<style scoped>
.highlight {
background-color: rgb(255, 192, 105);
padding: 0px;
}
</style>
Add
带单元格编辑功能的表格。
<template>
<a-button class="editable-add-btn" @click="handleAdd" style="margin-bottom: 8px">Add</a-button>
<a-table bordered :data-source="dataSource" :columns="columns">
<template #name="{ text, record }">
<div class="editable-cell">
<div v-if="editableData[record.key]" class="editable-cell-input-wrapper">
<a-input v-model:value="editableData[record.key].name" @pressEnter="save(record.key)" />
<check-outlined class="editable-cell-icon-check" @click="save(record.key)" />
</div>
<div v-else class="editable-cell-text-wrapper">
{{ text || ' ' }}
<edit-outlined class="editable-cell-icon" @click="edit(record.key)" />
</div>
</div>
</template>
<template #operation="{ record }">
<a-popconfirm
v-if="dataSource.length"
title="Sure to delete?"
@confirm="onDelete(record.key)"
>
<a>Delete</a>
</a-popconfirm>
</template>
</a-table>
</template>
<script lang="ts">
import { computed, defineComponent, reactive, Ref, ref, UnwrapRef } from 'vue';
import { CheckOutlined, EditOutlined } from '@ant-design/icons-vue';
import { cloneDeep } from 'lodash-es';
interface DataItem {
key: string;
name: string;
age: number;
address: string;
}
export default defineComponent({
components: {
CheckOutlined,
EditOutlined,
},
setup() {
const columns = [
{
title: 'name',
dataIndex: 'name',
width: '30%',
slots: { customRender: 'name' },
},
{
title: 'age',
dataIndex: 'age',
},
{
title: 'address',
dataIndex: 'address',
},
{
title: 'operation',
dataIndex: 'operation',
slots: { customRender: 'operation' },
},
];
const dataSource: Ref<DataItem[]> = ref([
{
key: '0',
name: 'Edward King 0',
age: 32,
address: 'London, Park Lane no. 0',
},
{
key: '1',
name: 'Edward King 1',
age: 32,
address: 'London, Park Lane no. 1',
},
]);
const count = computed(() => dataSource.value.length + 1);
const editableData: UnwrapRef<Record<string, DataItem>> = reactive({});
const edit = (key: string) => {
editableData[key] = cloneDeep(dataSource.value.filter(item => key === item.key)[0]);
};
const save = (key: string) => {
Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
delete editableData[key];
};
const onDelete = (key: string) => {
dataSource.value = dataSource.value.filter(item => item.key !== key);
};
const handleAdd = () => {
const newData = {
key: `${count.value}`,
name: `Edward King ${count.value}`,
age: 32,
address: `London, Park Lane no. ${count.value}`,
};
dataSource.value.push(newData);
};
return {
columns,
onDelete,
handleAdd,
dataSource,
editableData,
count,
edit,
save,
};
},
});
</script>
<style lang="less">
.editable-cell {
position: relative;
.editable-cell-input-wrapper,
.editable-cell-text-wrapper {
padding-right: 24px;
}
.editable-cell-text-wrapper {
padding: 5px 24px 5px 5px;
}
.editable-cell-icon,
.editable-cell-icon-check {
position: absolute;
right: 0;
width: 20px;
cursor: pointer;
}
.editable-cell-icon {
margin-top: 4px;
display: none;
}
.editable-cell-icon-check {
line-height: 28px;
}
.editable-cell-icon:hover,
.editable-cell-icon-check:hover {
color: #108ee9;
}
.editable-add-btn {
margin-bottom: 8px;
}
}
.editable-cell:hover .editable-cell-icon {
display: inline-block;
}
</style>
带行编辑功能的表格。
<template>
<a-table :columns="columns" :data-source="dataSource" bordered>
<template v-for="col in ['name', 'age', 'address']" #[col]="{ text, record }" :key="col">
<div>
<a-input
v-if="editableData[record.key]"
v-model:value="editableData[record.key][col]"
style="margin: -5px 0"
/>
<template v-else>
{{ text }}
</template>
</div>
</template>
<template #operation="{ record }">
<div class="editable-row-operations">
<span v-if="editableData[record.key]">
<a @click="save(record.key)">Save</a>
<a-popconfirm title="Sure to cancel?" @confirm="cancel(record.key)">
<a>Cancel</a>
</a-popconfirm>
</span>
<span v-else>
<a @click="edit(record.key)">Edit</a>
</span>
</div>
</template>
</a-table>
</template>
<script lang="ts">
import { cloneDeep } from 'lodash-es';
import { defineComponent, reactive, ref, UnwrapRef } from 'vue';
const columns = [
{
title: 'name',
dataIndex: 'name',
width: '25%',
slots: { customRender: 'name' },
},
{
title: 'age',
dataIndex: 'age',
width: '15%',
slots: { customRender: 'age' },
},
{
title: 'address',
dataIndex: 'address',
width: '40%',
slots: { customRender: 'address' },
},
{
title: 'operation',
dataIndex: 'operation',
slots: { customRender: 'operation' },
},
];
interface DataItem {
key: string;
name: string;
age: number;
address: string;
}
const data: DataItem[] = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i.toString(),
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
export default defineComponent({
setup() {
const dataSource = ref(data);
const editableData: UnwrapRef<Record<string, DataItem>> = reactive({});
const edit = (key: string) => {
editableData[key] = cloneDeep(dataSource.value.filter(item => key === item.key)[0]);
};
const save = (key: string) => {
Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
delete editableData[key];
};
const cancel = (key: string) => {
delete editableData[key];
};
return {
dataSource,
columns,
editingKey: '',
editableData,
edit,
save,
cancel,
};
},
});
</script>
<style scoped>
.editable-row-operations a {
margin-right: 8px;
}
</style>
表格支持树形数据的展示,当数据中有 children
字段时会自动展示为树形表格,如果不需要或配置为其他字段可以用 childrenColumnName
进行配置。 可以通过设置 indentSize
以控制每一层的缩进宽度。
注:暂不支持父子数据递归关联选择。
<template>
<a-table :columns="columns" :data-source="data" :row-selection="rowSelection" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '12%',
},
{
title: 'Address',
dataIndex: 'address',
width: '30%',
key: 'address',
},
];
interface DataItem {
key: number;
name: string;
age: number;
address: string;
children?: DataItem[]
}
const data: DataItem[] = [
{
key: 1,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park',
},
{
key: 12,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park',
children: [
{
key: 121,
name: 'Jimmy Brown',
age: 16,
address: 'New York No. 3 Lake Park',
},
],
},
{
key: 13,
name: 'Jim Green sr.',
age: 72,
address: 'London No. 1 Lake Park',
children: [
{
key: 131,
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park',
children: [
{
key: 1311,
name: 'Jim Green jr.',
age: 25,
address: 'London No. 3 Lake Park',
},
{
key: 1312,
name: 'Jimmy Green sr.',
age: 18,
address: 'London No. 4 Lake Park',
},
],
},
],
},
],
},
{
key: 2,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
];
const rowSelection = {
onChange: (selectedRowKeys: (string | number)[], selectedRows: DataItem[]) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
onSelect: (record: DataItem, selected: boolean, selectedRows: DataItem[]) => {
console.log(record, selected, selectedRows);
},
onSelectAll: (selected: boolean, selectedRows: DataItem[], changeRows: DataItem[]) => {
console.log(selected, selectedRows, changeRows);
},
};
export default defineComponent({
setup() {
return {
data,
columns,
rowSelection,
};
},
});
</script>
当表格内容较多不能一次性完全展示时。
<template>
<a-table :columns="columns" :data-source="data">
<template>
<a>Delete</a>
</template>
<template #expandedRowRender="{ record }">
<p style="margin: 0">
{{ record.description }}
</p>
</template>
</a-table>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Age', dataIndex: 'age', key: 'age' },
{ title: 'Address', dataIndex: 'address', key: 'address' },
{ title: 'Action', dataIndex: '', key: 'x', slots: { customRender: 'action' } },
];
const data = [
{
key: 1,
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
},
{
key: 2,
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
},
{
key: 3,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.',
},
];
export default defineComponent({
setup() {
return {
data,
columns,
};
},
});
</script>
适合同时展示有大量数据和数据列。
若列头与内容不对齐或出现列重复,请指定固定列的宽度
width
。如果指定width
不生效或出现白色垂直空隙,请尝试建议留一列不设宽度以适应弹性布局,或者检查是否有超长连续字段破坏布局。建议指定
scroll.x
为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过scroll.x
。
<template>
<a-table :columns="columns" :data-source="data" :scroll="{ x: 1500, y: 300 }">
<template #action>
<a>action</a>
</template>
</a-table>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{ title: 'Full Name', width: 100, dataIndex: 'name', key: 'name', fixed: 'left' },
{ title: 'Age', width: 100, dataIndex: 'age', key: 'age', fixed: 'left' },
{ title: 'Column 1', dataIndex: 'address', key: '1', width: 150 },
{ title: 'Column 2', dataIndex: 'address', key: '2', width: 150 },
{ title: 'Column 3', dataIndex: 'address', key: '3', width: 150 },
{ title: 'Column 4', dataIndex: 'address', key: '4', width: 150 },
{ title: 'Column 5', dataIndex: 'address', key: '5', width: 150 },
{ title: 'Column 6', dataIndex: 'address', key: '6', width: 150 },
{ title: 'Column 7', dataIndex: 'address', key: '7', width: 150 },
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{
title: 'Action',
key: 'operation',
fixed: 'right',
width: 100,
slots: { customRender: 'action' },
},
];
interface DataItem {
key: number;
name: string;
age: number;
address: string;
}
const data: DataItem[] = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
export default defineComponent({
data() {
return {
data,
columns,
};
},
});
</script>
对于列数很多的数据,可以固定前后的列,横向滚动查看其它数据,需要和 scroll.x
配合使用。
若列头与内容不对齐或出现列重复,请指定固定列的宽度
width
。如果指定width
不生效或出现白色垂直空隙,请尝试建议留一列不设宽度以适应弹性布局,或者检查是否有超长连续字段破坏布局。建议指定
scroll.x
为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过scroll.x
。
<template>
<a-table :columns="columns" :data-source="data" :scroll="{ x: 1300 }">
<template #action>
<a>action</a>
</template>
</a-table>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{ title: 'Full Name', width: 100, dataIndex: 'name', key: 'name', fixed: 'left' },
{ title: 'Age', width: 100, dataIndex: 'age', key: 'age', fixed: 'left' },
{ title: 'Column 1', dataIndex: 'address', key: '1' },
{ title: 'Column 2', dataIndex: 'address', key: '2' },
{ title: 'Column 3', dataIndex: 'address', key: '3' },
{ title: 'Column 4', dataIndex: 'address', key: '4' },
{ title: 'Column 5', dataIndex: 'address', key: '5' },
{ title: 'Column 6', dataIndex: 'address', key: '6' },
{ title: 'Column 7', dataIndex: 'address', key: '7' },
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{
title: 'Action',
key: 'operation',
fixed: 'right',
width: 100,
slots: { customRender: 'action' },
},
];
interface DataItem {
key: string;
name: string;
age: number;
address: string;
}
const data: DataItem[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York Park',
},
{
key: '2',
name: 'Jim Green',
age: 40,
address: 'London Park',
},
];
export default defineComponent({
data() {
return {
data,
columns,
};
},
});
</script>
方便一页内展示大量数据。
需要指定 column 的
width
属性,否则列头和内容可能不对齐。如果指定width
不生效或出现白色垂直空隙,请尝试建议留一列不设宽度以适应弹性布局,或者检查是否有超长连续字段破坏布局。
<template>
<a-table
:columns="columns"
:data-source="data"
:pagination="{ pageSize: 50 }"
:scroll="{ y: 240 }"
/>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{
title: 'Name',
dataIndex: 'name',
width: 150,
},
{
title: 'Age',
dataIndex: 'age',
width: 150,
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [...Array(100)].map((_, i) => ({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
}));
export default defineComponent({
setup() {
return {
data,
columns,
};
},
});
</script>
columns[n]
可以内嵌 children
,以渲染分组表头。
<template>
<a-table
:columns="columns"
:data-source="data"
bordered
size="middle"
:scroll="{ x: 'calc(700px + 50%)', y: 240 }"
/>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
type TableDataType = {
key: number;
name: string;
age: number;
street: string;
building: string;
number: number;
companyAddress: string;
companyName: string;
gender: string;
};
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: 100,
fixed: 'left',
filters: [
{
text: 'Joe',
value: 'Joe',
},
{
text: 'John',
value: 'John',
},
],
onFilter: (value: string, record: TableDataType) => record.name.indexOf(value) === 0,
},
{
title: 'Other',
children: [
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 200,
sorter: (a: TableDataType, b: TableDataType) => a.age - b.age,
},
{
title: 'Address',
children: [
{
title: 'Street',
dataIndex: 'street',
key: 'street',
width: 200,
},
{
title: 'Block',
children: [
{
title: 'Building',
dataIndex: 'building',
key: 'building',
width: 100,
},
{
title: 'Door No.',
dataIndex: 'number',
key: 'number',
width: 100,
},
],
},
],
},
],
},
{
title: 'Company',
children: [
{
title: 'Company Address',
dataIndex: 'companyAddress',
key: 'companyAddress',
width: 200,
},
{
title: 'Company Name',
dataIndex: 'companyName',
key: 'companyName',
},
],
},
{
title: 'Gender',
dataIndex: 'gender',
key: 'gender',
width: 80,
fixed: 'right',
},
];
const data = [...Array(100)].map((_, i) => ({
key: i,
name: 'John Brown',
age: i + 1,
street: 'Lake Park',
building: 'C',
number: 2035,
companyAddress: 'Lake Street 42',
companyName: 'SoftLake Co',
gender: 'M',
}));
export default defineComponent({
setup() {
return {
data,
columns,
};
},
});
</script>
对某一列数据进行筛选,使用列的 filters
属性来指定需要筛选菜单的列,onFilter
用于筛选当前数据,filterMultiple
用于指定多选和单选。 对某一列数据进行排序,通过指定列的 sorter
函数即可启动排序按钮。sorter: function(rowA, rowB) { ... }
, rowA、rowB 为比较的两个行数据。 sortDirections: ['ascend' | 'descend']
改变每列可用的排序方式,切换排序时按数组内容依次切换,设置在 table props 上时对所有列生效。 使用 defaultSortOrder
属性,设置列的默认排序顺序。
<template>
<a-table :columns="columns" :data-source="data" @change="onChange" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
type TableDataType = {
key: string;
name: string;
age: number;
address: string;
};
type PaginationType = {
current: number;
pageSize: number;
};
type FilterType = {
name: string;
address: string;
};
type ColumnType = {
title: string;
dataIndex: string;
filters?: {
text: string;
value: string;
children?: {
text: string;
value: string;
}[];
}[];
onFilter?: (value: string, record: TableDataType) => boolean;
sorter?: (a: TableDataType, b: TableDataType) => number;
sortDirections?: string[];
defaultSortOrder?: string;
filterMultiple?: string[] | boolean;
};
const columns: ColumnType[] = [
{
title: 'Name',
dataIndex: 'name',
filters: [
{
text: 'Joe',
value: 'Joe',
},
{
text: 'Jim',
value: 'Jim',
},
{
text: 'Submenu',
value: 'Submenu',
children: [
{
text: 'Green',
value: 'Green',
},
{
text: 'Black',
value: 'Black',
},
],
},
],
// specify the condition of filtering result
// here is that finding the name started with `value`
onFilter: (value: string, record: TableDataType) => record.name.indexOf(value) === 0,
sorter: (a: TableDataType, b: TableDataType) => a.name.length - b.name.length,
sortDirections: ['descend'],
},
{
title: 'Age',
dataIndex: 'age',
defaultSortOrder: 'descend',
sorter: (a: TableDataType, b: TableDataType) => a.age - b.age,
},
{
title: 'Address',
dataIndex: 'address',
filters: [
{
text: 'London',
value: 'London',
},
{
text: 'New York',
value: 'New York',
},
],
filterMultiple: false,
onFilter: (value: string, record: TableDataType) => record.address.indexOf(value) === 0,
sorter: (a: TableDataType, b: TableDataType) => a.address.length - b.address.length,
sortDirections: ['descend', 'ascend'],
},
];
const data: TableDataType[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
export default defineComponent({
setup() {
const onChange = (pagination: PaginationType, filters: FilterType[], sorter: ColumnType) => {
console.log('params', pagination, filters, sorter);
};
return {
data,
columns,
onChange,
};
},
});
</script>
展示每行数据更详细的信息。
<template>
<a-table :columns="columns" :data-source="data" class="components-table-demo-nested">
<template #operation>
<a>Publish</a>
</template>
<template #expandedRowRender>
<a-table :columns="innerColumns" :data-source="innerData" :pagination="false">
<template #status>
<span>
<a-badge status="success" />
Finished
</span>
</template>
<template #operation>
<span class="table-operation">
<a>Pause</a>
<a>Stop</a>
<a-dropdown>
<template #overlay>
<a-menu>
<a-menu-item>Action 1</a-menu-item>
<a-menu-item>Action 2</a-menu-item>
</a-menu>
</template>
<a>
More
<down-outlined />
</a>
</a-dropdown>
</span>
</template>
</a-table>
</template>
</a-table>
</template>
<script lang="ts">
import { DownOutlined } from '@ant-design/icons-vue';
import { defineComponent } from 'vue';
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Platform', dataIndex: 'platform', key: 'platform' },
{ title: 'Version', dataIndex: 'version', key: 'version' },
{ title: 'Upgraded', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{ title: 'Creator', dataIndex: 'creator', key: 'creator' },
{ title: 'Date', dataIndex: 'createdAt', key: 'createdAt' },
{ title: 'Action', key: 'operation', slots: { customRender: 'operation' } },
];
interface DataItem {
key: number;
name: string;
platform: string;
version: string;
upgradeNum: number;
creator: string;
createdAt: string;
}
const data: DataItem[] = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
name: 'Screem',
platform: 'iOS',
version: '10.3.4.5654',
upgradeNum: 500,
creator: 'Jack',
createdAt: '2014-12-24 23:12:00',
});
}
const innerColumns = [
{ title: 'Date', dataIndex: 'date', key: 'date' },
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Status', key: 'state', slots: { customRender: 'status' } },
{ title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{
title: 'Action',
dataIndex: 'operation',
key: 'operation',
slots: { customRender: 'operation' },
},
];
interface innerDataItem {
key: number;
date: string;
name: string;
upgradeNum: string;
}
const innerData: innerDataItem[] = [];
for (let i = 0; i < 3; ++i) {
innerData.push({
key: i,
date: '2014-12-24 23:12:00',
name: 'This is production name',
upgradeNum: 'Upgraded: 56',
});
}
export default defineComponent({
components: {
DownOutlined,
},
setup() {
return {
data,
columns,
innerColumns,
innerData,
};
},
});
</script>
使用受控属性对筛选和排序状态进行控制。
- columns 中定义了 filteredValue 和 sortOrder 属性即视为受控模式。
- 只支持同时对一列进行排序,请保证只有一列的 sortOrder 属性是生效的。
- 务必指定
column.key
。
<template>
<div>
<div class="table-operations">
<a-button @click="setAgeSort">Sort age</a-button>
<a-button @click="clearFilters">Clear filters</a-button>
<a-button @click="clearAll">Clear filters and sorters</a-button>
</div>
<a-table :columns="columns" :data-source="data" @change="handleChange" />
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from 'vue';
import { TableState, TableStateFilters } from 'ant-design-vue/es/table/interface';
type Pagination = TableState['pagination'];
interface DataItem {
key: string;
name: string;
age: number;
address: string;
}
const data: DataItem[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
export default defineComponent({
setup() {
const filteredInfo = ref();
const sortedInfo = ref();
const columns = computed(() => {
const filtered = filteredInfo.value || {};
const sorted = sortedInfo.value || {};
return [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
filters: [
{ text: 'Joe', value: 'Joe' },
{ text: 'Jim', value: 'Jim' },
],
filteredValue: filtered.name || null,
onFilter: (value: string, record: DataItem) => record.name.includes(value),
sorter: (a: DataItem, b: DataItem) => a.name.length - b.name.length,
sortOrder: sorted.columnKey === 'name' && sorted.order,
ellipsis: true,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
sorter: (a: DataItem, b: DataItem) => a.age - b.age,
sortOrder: sorted.columnKey === 'age' && sorted.order,
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
filters: [
{ text: 'London', value: 'London' },
{ text: 'New York', value: 'New York' },
],
filteredValue: filtered.address || null,
onFilter: (value: string, record: DataItem) => record.address.includes(value),
sorter: (a: DataItem, b: DataItem) => a.address.length - b.address.length,
sortOrder: sorted.columnKey === 'address' && sorted.order,
ellipsis: true,
},
];
});
const handleChange = (pagination: Pagination, filters: TableStateFilters, sorter: any) => {
console.log('Various parameters', pagination, filters, sorter);
filteredInfo.value = filters;
sortedInfo.value = sorter;
};
const clearFilters = () => {
filteredInfo.value = null;
};
const clearAll = () => {
filteredInfo.value = null;
sortedInfo.value = null;
};
const setAgeSort = () => {
sortedInfo.value = {
order: 'descend',
columnKey: 'age',
};
};
return {
data,
columns,
handleChange,
clearFilters,
clearAll,
setAgeSort,
};
},
});
</script>
<style scoped>
.table-operations {
margin-bottom: 16px;
}
.table-operations > button {
margin-right: 8px;
}
</style>
选择后进行操作,完成后清空选择,通过 rowSelection.selectedRowKeys
来控制选中项。
<template>
<div>
<div style="margin-bottom: 16px">
<a-button type="primary" :disabled="!hasSelected" :loading="loading" @click="start">
Reload
</a-button>
<span style="margin-left: 8px">
<template v-if="hasSelected">
{{ `Selected ${selectedRowKeys.length} items` }}
</template>
</span>
</div>
<a-table
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:columns="columns"
:data-source="data"
/>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, reactive, toRefs } from 'vue';
import { ColumnProps } from 'ant-design-vue/es/table/interface';
type Key = ColumnProps['key'];
interface DataType {
key: Key;
name: string;
age: number;
address: string;
}
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data: DataType[] = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
export default defineComponent({
setup() {
const state = reactive<{
selectedRowKeys: Key[];
loading: boolean;
}>({
selectedRowKeys: [], // Check here to configure the default column
loading: false,
});
const hasSelected = computed(() => state.selectedRowKeys.length > 0);
const start = () => {
state.loading = true;
// ajax request after empty completing
setTimeout(() => {
state.loading = false;
state.selectedRowKeys = [];
}, 1000);
};
const onSelectChange = (selectedRowKeys: Key[]) => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
state.selectedRowKeys = selectedRowKeys;
};
return {
data,
columns,
hasSelected,
...toRefs(state),
// func
start,
onSelectChange,
};
},
});
</script>
通过 rowSelection.selections
自定义选择项,默认不显示下拉选项,设为 true
时显示默认选择项。
<template>
<a-table :row-selection="rowSelection" :columns="columns" :data-source="data" />
</template>
<script lang="ts">
import { defineComponent, computed, ref, unref } from 'vue';
import { ColumnProps } from 'ant-design-vue/es/table/interface';
type Key = ColumnProps['key'];
interface DataType {
key: Key;
name: string;
age: number;
address: string;
}
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data: DataType[] = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
export default defineComponent({
setup() {
const selectedRowKeys = ref<Key[]>([]); // Check here to configure the default column
const onSelectChange = (changableRowKeys: Key[]) => {
console.log('selectedRowKeys changed: ', changableRowKeys);
selectedRowKeys.value = changableRowKeys;
};
const rowSelection = computed(() => {
return {
selectedRowKeys: unref(selectedRowKeys),
onChange: onSelectChange,
hideDefaultSelections: true,
selections: [
{
key: 'all-data',
text: 'Select All Data',
onSelect: () => {
selectedRowKeys.value = [...Array(46).keys()]; // 0...45
},
},
{
key: 'odd',
text: 'Select Odd Row',
onSelect: (changableRowKeys: Key[]) => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return false;
}
return true;
});
selectedRowKeys.value = newSelectedRowKeys;
},
},
{
key: 'even',
text: 'Select Even Row',
onSelect: (changableRowKeys: Key[]) => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return true;
}
return false;
});
selectedRowKeys.value = newSelectedRowKeys;
},
},
],
};
});
return {
data,
columns,
selectedRowKeys,
rowSelection,
};
},
});
</script>
第一列是联动的选择框。
默认点击 checkbox 触发选择行为,需要 点击行
触发可参考例子:https://codesandbox.io/s/row-selection-on-click-tr58v
<template>
<a-table :row-selection="rowSelection" :columns="columns" :data-source="data">
<template #name="{ text }">
<a>{{ text }}</a>
</template>
</a-table>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { ColumnProps } from 'ant-design-vue/es/table/interface';
type Key = ColumnProps['key'];
interface DataType {
key: Key;
name: string;
age: number;
address: string;
}
const columns = [
{
title: 'Name',
dataIndex: 'name',
slots: { customRender: 'name' },
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data: DataType[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Disabled User',
age: 99,
address: 'Sidney No. 1 Lake Park',
},
];
export default defineComponent({
setup() {
const rowSelection = {
onChange: (selectedRowKeys: Key[], selectedRows: DataType[]) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
getCheckboxProps: (record: DataType) => ({
disabled: record.name === 'Disabled User', // Column configuration not to be checked
name: record.name,
}),
};
return {
data,
columns,
rowSelection,
};
},
});
</script>
两种紧凑型的列表,小型列表只用于对话框内。
<template>
<div id="components-table-demo-size">
<h4>Middle size table</h4>
<a-table :columns="columns" :data-source="data" size="middle" />
<h4>Small size table</h4>
<a-table :columns="columns" :data-source="data" size="small" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{ title: 'Name', dataIndex: 'name' },
{ title: 'Age', dataIndex: 'age' },
{ title: 'Address', dataIndex: 'address' },
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
];
export default defineComponent({
setup() {
return {
data,
columns,
};
},
});
</script>
<style>
#components-table-demo-size h4 {
margin-bottom: 16px;
}
</style>
利用 rowClassName
自定义带斑马纹的表格。
<template>
<a-table
class="ant-table-striped"
size="middle"
:columns="columns"
:data-source="data"
:rowClassName="(record, index) => (index % 2 === 1 ? 'table-striped' : null)"
/>
<a-table
class="ant-table-striped"
size="middle"
:columns="columns"
:data-source="data"
:rowClassName="(record, index) => (index % 2 === 1 ? 'table-striped' : null)"
bordered
/>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const columns = [
{ title: 'Name', dataIndex: 'name' },
{ title: 'Age', dataIndex: 'age' },
{ title: 'Address', dataIndex: 'address' },
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Ben Kang',
age: 15,
address: 'Sidney No. 1 Lake Park',
},
];
export default defineComponent({
setup() {
return {
data,
columns,
};
},
});
</script>
<style scoped>
.ant-table-striped :deep(.table-striped) {
background-color: #fafafa;
}
</style>
使用 template 风格的 API
这个只是一个描述
columns
的语法糖,所以你不能用其他组件去包裹Column
和ColumnGroup
。
<template>
<a-table :data-source="data">
<a-table-column-group>
<template #title><span style="color: #1890ff">Name</span></template>
<a-table-column key="firstName" data-index="firstName">
<template #title><span style="color: #1890ff">First Name</span></template>
</a-table-column>
<a-table-column key="lastName" title="Last Name" data-index="lastName" />
</a-table-column-group>
<a-table-column key="age" title="Age" data-index="age" />
<a-table-column key="address" title="Address" data-index="address" />
<a-table-column key="tags" title="Tags" data-index="tags">
<template #default="{ text: tags }">
<span>
<a-tag v-for="tag in tags" :key="tag" color="blue">{{ tag }}</a-tag>
</span>
</template>
</a-table-column>
<a-table-column key="action" title="Action">
<template #default="{ record }">
<span>
<a>Action 一 {{ record.firstName }}</a>
<a-divider type="vertical" />
<a>Delete</a>
</span>
</template>
</a-table-column>
</a-table>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
const data = [
{
key: '1',
firstName: 'John',
lastName: 'Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
firstName: 'Jim',
lastName: 'Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
firstName: 'Joe',
lastName: 'Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
export default defineComponent({
setup() {
return {
data,
};
},
});
</script>
API
Table
参数 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
tableLayout | 表格元素的 table-layout 属性,设为 fixed 表示内容不会影响列的布局 | - | ‘auto’ | ‘fixed’ | 无 固定表头/列或使用了 column.ellipsis 时,默认值为 fixed | 1.5.0 |
bordered | 是否展示外边框和列边框 | boolean | false | |
childrenColumnName | 指定树形结构的列名 | string[] | children | |
columns | 表格列的配置描述,具体项见下表 | array | - | |
components | 覆盖默认的 table 元素 | object | - | |
dataSource | 数据数组 | any[] | ||
defaultExpandAllRows | 初始时,是否展开所有行 | boolean | false | |
defaultExpandedRowKeys | 默认展开的行 | string[] | - | |
expandedRowKeys | 展开的行,控制属性 | string[] | - | |
expandedRowRender | 额外的展开行 | Function(record, index, indent, expanded):VNode | #expandedRowRender=”{record, index, indent, expanded}” | - | |
expandIcon | 自定义展开图标 | Function(props):VNode | #expandIcon=”props” | - | |
expandRowByClick | 通过点击行来展开子行 | boolean | false | |
expandIconColumnIndex | 展开的图标显示在哪一列,如果没有 rowSelection ,默认显示在第一列,否则显示在选择框后面 | number | ||
footer | 表格尾部 | Function(currentPageData)|v-slot | ||
indentSize | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | 15 | |
loading | 页面是否加载中 | boolean|object | false | |
locale | 默认文案设置,目前包括排序、过滤、空数据文案 | object | filterConfirm: ‘确定’ filterReset: ‘重置’ emptyText: ‘暂无数据’ | |
pagination | 分页器,参考配置项或 pagination文档,设为 false 时不展示和进行分页 | object | ||
rowClassName | 表格行的类名 | Function(record, index):string | - | |
rowKey | 表格行 key 的取值,可以是字符串或一个函数 | string|Function(record):string | ‘key’ | |
rowSelection | 列表项是否可选择,配置项 | object | null | |
scroll | 设置横向或纵向滚动,也可用于指定滚动区域的宽和高,建议为 x 设置一个数字,如果要设置为 true ,需要配合样式 .ant-table td { white-space: nowrap; } | { x: number | true, y: number } | - | |
showHeader | 是否显示表头 | boolean | true | |
size | 表格大小 | default | middle | small | default | |
title | 表格标题 | Function(currentPageData)|v-slot | ||
customHeaderRow | 设置头部行属性 | Function(column, index) | - | |
customRow | 设置行属性 | Function(record, index) | - | |
getPopupContainer | 设置表格内各类浮层的渲染节点,如筛选菜单 | (triggerNode) => HTMLElement | () => TableHtmlElement | 1.5.0 |
transformCellText | 数据渲染前可以再次改变,一般用户空数据的默认配置,可以通过 ConfigProvider 全局统一配置 | Function({ text, column, record, index }) => any | - | 1.5.4 |
事件
事件名称 | 说明 | 回调参数 |
---|---|---|
expandedRowsChange | 展开的行变化时触发 | Function(expandedRows) |
change | 分页、排序、筛选变化时触发 | Function(pagination, filters, sorter, { currentDataSource }) |
expand | 点击展开图标时触发 | Function(expanded, record) |
customRow 用法
适用于 customRow
customHeaderRow
customCell
customHeaderCell
。遵循Vue jsx语法。
<Table
customRow={(record) => {
return {
xxx... //属性
onClick: (event) => {}, // 点击行
onDblclick: (event) => {},
onContextmenu: (event) => {},
onMouseenter: (event) => {}, // 鼠标移入行
onMouseleave: (event) => {}
};
}}
customHeaderRow={(column) => {
return {
onClick: () => {}, // 点击表头行
};
}}
/>
Column
列描述数据对象,是 columns 中的一项,Column 使用相同的 API。
参数 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
align | 设置列内容的对齐方式 | ‘left’ | ‘right’ | ‘center’ | ‘left’ | |
ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。 设置为 true 时,表格布局将变成 tableLayout=”fixed” 。 | boolean | false | 1.5.0 |
colSpan | 表头列合并,设置为 0 时,不渲染 | number | ||
dataIndex | 列数据在数据项中对应的 key,支持 a.b.c 的嵌套写法 | string | - | |
defaultFilteredValue | 默认筛选值 | string[] | - | 1.5.0 |
filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | VNode | v-slot | - | |
filterDropdownVisible | 用于控制自定义筛选菜单是否可见 | boolean | - | |
filtered | 标识数据是否经过过滤,筛选图标会高亮 | boolean | false | |
filteredValue | 筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组 | string[] | - | |
filterIcon | 自定义 filter 图标。 | VNode | ({filtered: boolean, column: Column}) => vNode |slot | false | |
filterMultiple | 是否多选 | boolean | true | |
filters | 表头的筛选菜单项 | object[] | - | |
fixed | 列是否固定,可选 true (等效于 left) ‘left’ ‘right’ | boolean|string | false | |
key | Vue 需要的 key,如果已经设置了唯一的 dataIndex ,可以忽略这个属性 | string | - | |
customRender | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return 里面可以设置表格行/列合并,可参考 demo 表格行/列合并 | Function({text, record, index}) {}|v-slot | - | |
sorter | 排序函数,本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 true | Function|boolean | - | |
sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 ‘ascend’ ‘descend’ false | boolean|string | - | |
sortDirections | 支持的排序方式,取值为 ‘ascend’ ‘descend’ | Array | [‘ascend’, ‘descend’] | 1.5.0 |
title | 列头显示文字 | string|slot | - | |
width | 列宽度 | string|number | - | |
customCell | 设置单元格属性 | Function(record, rowIndex) | - | |
customHeaderCell | 设置头部单元格属性 | Function(column) | - | |
onFilter | 本地模式下,确定筛选的运行函数, 使用 template 或 jsx 时作为filter 事件使用 | Function | - | |
onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用,使用 template 或 jsx 时作为filterDropdownVisibleChange 事件使用 | function(visible) {} | - | |
slots | 使用 columns 时,可以通过该属性配置支持 slot 的属性,如 slots: { filterIcon: ‘XXX’} | object | - |
ColumnGroup
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
title | 列头显示文字 | string|slot | - |
slots | 使用 columns 时,可以通过该属性配置支持 slot 的属性,如 slots: { title: ‘XXX’} | object | - |
pagination
分页的配置项。
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
position | 指定分页显示的位置 | ‘top’ | ‘bottom’ | ‘both’ | ‘bottom’ |
更多配置项,请查看 Pagination
。
rowSelection
选择功能的配置。
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
columnWidth | 自定义列表选择框宽度 | string|number | - |
columnTitle | 自定义列表选择框标题 | string|VNode | - |
fixed | 把选择框列固定在左边 | boolean | - |
getCheckboxProps | 选择框的默认属性配置 | Function(record) | - |
hideDefaultSelections | 去掉『全选』『反选』两个默认选项 | boolean | false |
selectedRowKeys | 指定选中项的 key 数组,需要和 onChange 进行配合 | string[] | [] |
selections | 自定义选择配置项, 设为 true 时使用默认选择项 | object[]|boolean | true |
type | 多选/单选,checkbox or radio | string | checkbox |
onChange | 选中项发生变化时的回调 | Function(selectedRowKeys, selectedRows) | - |
onSelect | 用户手动选择/取消选择某列的回调 | Function(record, selected, selectedRows, nativeEvent) | - |
onSelectAll | 用户手动选择/取消选择所有列的回调 | Function(selected, selectedRows, changeRows) | - |
onSelectInvert | 用户手动选择反选的回调 | Function(selectedRows) | - |
scroll
参数 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
x | 设置横向滚动,也可用于指定滚动区域的宽和高,可以设置为像素值,百分比,true 和 ‘max-content’ | number | true | - | |
y | 设置纵向滚动,也可用于指定滚动区域的宽和高,可以设置为像素值,百分比,true 和 ‘max-content’ | number | true | - | |
scrollToFirstRowOnChange | 当分页、排序、筛选变化后是否滚动到表格顶部 | boolean | - | 1.5.0 |
selection
自定义选择配置项
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
key | Vue 需要的 key,建议设置 | string | - |
text | 选择项显示的文字 | string|VNode | - |
onSelect | 选择项点击回调 | Function(changeableRowKeys) | - |
注意
在 Table 中,dataSource
和 columns
里的数据值都需要指定 key
值。对于 dataSource
默认将每列数据的 key
属性作为唯一的标识。
如果你的数据没有这个属性,务必使用 rowKey
来指定数据列的主键。若没有指定,控制台会出现缺少 key 的提示,表格组件也会出现各类奇怪的错误。
// 比如你的数据主键是 uid
return <Table rowKey="uid" />;
// 或
return <Table rowKey={record => record.uid} />;