Table 表格
展示行列数据。
何时使用
- 当有大量结构化的数据需要展现时;
- 当需要对数据进行排序、搜索、分页、自定义操作等复杂行为时。
如何使用
指定表格的数据源 dataSource
为一个数组。
代码演示
远程加载数据
这个例子通过简单的 ajax 读取方式,演示了如何从服务端读取并展现数据,具有筛选、排序等功能以及页面 loading 效果。开发者可以自行接入其他数据处理方式。另外,本例也展示了筛选排序功能如何交给服务端实现,列不需要指定具体的 onFilter
和 sorter
函数,而是在把筛选和排序的参数发到服务端来处理。注意,此示例使用 模拟接口,展示数据可能不准确,请打开网络面板查看请求。
<template>
<a-table :columns="columns"
:rowKey="record => record.login.uuid"
:dataSource="data"
:pagination="pagination"
:loading="loading"
@change="handleTableChange"
>
<template slot="name" slot-scope="name">
{{name.first}} {{name.last}}
</template>
</a-table>
</template>
<script>
import reqwest from 'reqwest';
const columns = [{
title: 'Name',
dataIndex: 'name',
sorter: true,
width: '20%',
scopedSlots: { customRender: 'name' },
}, {
title: 'Gender',
dataIndex: 'gender',
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
width: '20%',
}, {
title: 'Email',
dataIndex: 'email',
}];
export default {
mounted() {
this.fetch();
},
data() {
return {
data: [],
pagination: {},
loading: false,
columns,
}
},
methods: {
handleTableChange (pagination, filters, sorter) {
console.log(pagination);
const pager = { ...this.pagination };
pager.current = pagination.current;
this.pagination = pager;
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
},
fetch (params = {}) {
console.log('params:', params);
this.loading = true
reqwest({
url: 'https://randomuser.me/api',
method: 'get',
data: {
results: 10,
...params,
},
type: 'json',
}).then((data) => {
const pagination = { ...this.pagination };
// Read total count from server
// pagination.total = data.totalCount;
pagination.total = 200;
this.loading = false;
this.data = data.results;
this.pagination = pagination;
});
}
},
}
</script>
基本用法
简单的表格,最后一列是各种操作。
<template>
<a-table :columns="columns" :dataSource="data">
<a slot="name" slot-scope="text" href="javascript:;">{{text}}</a>
<span slot="customTitle"><a-icon type="smile-o" /> Name</span>
<span slot="tags" slot-scope="tags">
<a-tag v-for="tag in tags" color="blue" :key="tag">{{tag}}</a-tag>
</span>
<span slot="action" slot-scope="text, record">
<a href="javascript:;">Invite 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="javascript:;">Delete</a>
<a-divider type="vertical" />
<a href="javascript:;" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</a-table>
</template>
<script>
const columns = [{
dataIndex: 'name',
key: 'name',
slots: { title: 'customTitle' },
scopedSlots: { customRender: 'name' },
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
scopedSlots: { customRender: 'tags' },
}, {
title: 'Action',
key: 'action',
scopedSlots: { 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 {
data() {
return {
data,
columns,
}
}
}
</script>
带边框
添加表格边框线,页头和页脚。
<template>
<a-table :columns="columns" :dataSource="data" bordered>
<template slot="name" slot-scope="text">
<a href="javascript:;">{{text}}</a>
</template>
<template slot="title" slot-scope="currentPageData">
Header
</template>
<template slot="footer" slot-scope="currentPageData">
Footer
</template>
</a-table>
</template>
<script>
const columns = [{
title: 'Name',
dataIndex: 'name',
scopedSlots: { 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 {
data() {
return {
data,
columns,
}
}
}
</script>
<style>
th.column-money,
td.column-money {
text-align: right !important;
}
</style>
表格行/列合并
表头只支持列合并,使用 column 里的 colSpan 进行设置。表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时,设置的表格不会渲染。
<template>
<a-table :columns="columns" :dataSource="data" bordered>
<template slot="name" slot-scope="text">
<a href="javascript:;">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="javascript:;">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="javascript:;">Delete</a>
<a-divider type="vertical" />
<a href="javascript:;" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</template>
</a-table>
</template>
<script>
// In the fifth row, other columns are merged into first column
// by setting it's colSpan to be 0
const renderContent = (value, row, index) => {
const obj = {
children: value,
attrs: {},
};
if (index === 4) {
obj.attrs.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 {
data() {
const columns = [{
title: 'Name',
dataIndex: 'name',
customRender: (text, row, index) => {
if (index < 4) {
return <a href="javascript:;">{text}</a>;
}
return {
children: <a href="javascript:;">{text}</a>,
attrs: {
colSpan: 5,
},
};
},
}, {
title: 'Age',
dataIndex: 'age',
customRender: renderContent,
}, {
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
customRender: (value, row, index) => {
const obj = {
children: value,
attrs: {},
};
if (index === 2) {
obj.attrs.rowSpan = 2;
}
// These two are merged into above cell
if (index === 3) {
obj.attrs.rowSpan = 0;
}
if (index === 4) {
obj.attrs.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 :dataSource="data" :columns="columns">
<div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" class='custom-filter-dropdown'>
<a-input
v-ant-ref="c => searchInput = c"
:placeholder="`Search ${column.dataIndex}`"
:value="selectedKeys[0]"
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
@pressEnter="() => handleSearch(selectedKeys, confirm)"
style="width: 188px; margin-bottom: 8px; display: block;"
/>
<a-button
type='primary'
@click="() => handleSearch(selectedKeys, confirm)"
icon="search"
size="small"
style="width: 90px; margin-right: 8px"
>Search</a-button>
<a-button
@click="() => handleReset(clearFilters)"
size="small"
style="width: 90px"
>Reset</a-button>
</div>
<a-icon slot="filterIcon" slot-scope="filtered" type='search' :style="{ color: filtered ? '#108ee9' : undefined }" />
<template slot="customRender" slot-scope="text">
<span v-if="searchText">
<template v-for="(fragment, i) in text.toString().split(new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i'))">
<mark v-if="fragment.toLowerCase() === searchText.toLowerCase()" :key="i" class="highlight">{{fragment}}</mark>
<template v-else>{{fragment}}</template>
</template>
</span>
<template v-else>{{text}}</template>
</template>
</a-table>
</template>
<script>
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 {
data () {
return {
data,
searchText: '',
searchInput: null,
columns: [{
title: 'Name',
dataIndex: 'name',
key: 'name',
scopedSlots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) => record.name.toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
this.searchInput.focus()
},0)
}
},
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
scopedSlots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) => record.age.toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
this.searchInput.focus()
})
}
},
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
scopedSlots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) => record.address.toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
this.searchInput.focus()
})
}
},
}],
}
},
methods: {
handleSearch (selectedKeys, confirm) {
confirm()
this.searchText = selectedKeys[0]
},
handleReset (clearFilters) {
clearFilters()
this.searchText = ''
},
},
}
</script>
<style scoped>
.custom-filter-dropdown {
padding: 8px;