add table demo

pull/165/head
tjz 2018-04-01 16:50:31 +08:00
parent a9e51adde6
commit 5a9b756338
15 changed files with 972 additions and 208 deletions

View File

@ -1,5 +1,4 @@
import cloneDeep from 'lodash/cloneDeep'
import VcTable from '../vc-table'
import classNames from 'classnames'
import Pagination from '../pagination'
@ -33,10 +32,8 @@ function stopPropagation (e) {
}
const defaultPagination = {
on: {
change: noop,
showSizeChange: noop,
},
onChange: noop,
onShowSizeChange: noop,
}
/**
@ -91,14 +88,13 @@ export default {
watch: {
pagination (val) {
this.setState(previousState => {
const newPagination = mergeProps(
defaultPagination,
previousState.sPagination,
val,
)
newPagination.props = newPagination.props || {}
newPagination.props.current = newPagination.props.current || 1
newPagination.props.pageSize = newPagination.props.pageSize || 10
const newPagination = {
...defaultPagination,
...previousState.sPagination,
...val,
}
newPagination.current = newPagination.current || 1
newPagination.pageSize = newPagination.pageSize || 10
return { sPagination: val !== false ? newPagination : emptyObject }
})
},
@ -176,17 +172,13 @@ export default {
getDefaultPagination (props) {
const pagination = props.pagination || {}
pagination.props = pagination.props || {}
return this.hasPagination(props)
? mergeProps(
defaultPagination,
pagination,
{
props: {
current: pagination.props.defaultCurrent || pagination.props.current || 1,
pageSize: pagination.props.defaultPageSize || pagination.props.pageSize || 10,
},
}) : {}
? {
...defaultPagination,
...pagination,
current: pagination.defaultCurrent || pagination.current || 1,
pageSize: pagination.defaultPageSize || pagination.pageSize || 10,
} : {}
},
onRow (record, index) {
@ -345,7 +337,7 @@ export default {
if (this.getSortOrderColumns().length === 0) {
this.setState(newState)
}
this.$emit('change', this.prepareParamsArguments({
this.$emit('change', ...this.prepareParamsArguments({
...this.$data,
...newState,
}))
@ -373,9 +365,8 @@ export default {
if (props.pagination) {
// Reset current prop
pagination.props = pagination.props || {}
pagination.props.current = 1
pagination.on.change(pagination.current)
pagination.current = 1
pagination.onChange(pagination.current)
}
const newState = {
@ -395,23 +386,22 @@ export default {
}
// Controlled current prop will not respond user interaction
if (typeof props.pagination === 'object' && props.pagination.props && 'current' in (props.pagination.props)) {
newState.sPagination = mergeProps(pagination, {
props: {
current: this.sPagination.props.current,
},
})
if (typeof props.pagination === 'object' && 'current' in (props.pagination)) {
newState.sPagination = {
...pagination,
current: this.sPagination.current,
}
}
this.setState(newState, () => {
this.store.setState({
selectionDirty: false,
})
this.$emit('change', this.prepareParamsArguments({
this.$emit('change', ...this.prepareParamsArguments({
...this.$data,
selectionDirty: false,
filters,
pagination,
sSelectionDirty: false,
sFilters: filters,
sPagination: pagination,
}))
})
},
@ -523,11 +513,11 @@ export default {
const props = this.$props
const pagination = { ...this.sPagination }
if (current) {
pagination.props.current = current
pagination.current = current
} else {
pagination.props.current = pagination.props.current || 1
pagination.current = pagination.current || 1
}
pagination.on.change(pagination.props.current, ...otherArguments)
pagination.onChange(pagination.current, ...otherArguments)
const newState = {
sPagination: pagination,
@ -535,23 +525,21 @@ export default {
// Controlled current prop will not respond user interaction
if (props.pagination &&
typeof props.pagination === 'object' &&
props.pagination.props &&
'current' in (props.pagination.props)) {
newState.sPagination = mergeProps(pagination, {
props: {
current: this.sPagination.props.current,
},
})
'current' in (props.pagination)) {
newState.sPagination = {
...pagination,
current: this.sPagination.current,
}
}
this.setState(newState)
this.store.setState({
selectionDirty: false,
})
this.$emit('change', this.prepareParamsArguments({
this.$emit('change', ...this.prepareParamsArguments({
...this.$data,
selectionDirty: false,
pagination,
sSelectionDirty: false,
sPagination: pagination,
}))
},
@ -655,7 +643,7 @@ export default {
},
getMaxCurrent (total) {
const { current, pageSize } = this.sPagination.props
const { current, pageSize } = this.sPagination
if ((current - 1) * pageSize >= total) {
return Math.floor((total - 1) / pageSize) + 1
}
@ -738,17 +726,16 @@ export default {
handleShowSizeChange (current, pageSize) {
const pagination = this.sPagination
pagination.on.showSizeChange(current, pageSize)
const nextPagination = mergeProps(pagination, {
props: {
pageSize,
current,
},
})
pagination.onShowSizeChange(current, pageSize)
const nextPagination = {
...pagination,
pageSize,
current,
}
this.setState({ sPagination: nextPagination })
this.$emit('change', this.prepareParamsArguments({
this.$emit('change', ...this.prepareParamsArguments({
...this.$data,
pagination: nextPagination,
sPagination: nextPagination,
}))
},
@ -759,25 +746,24 @@ export default {
}
let size = 'default'
const { sPagination: pagination } = this
if (pagination.props && pagination.props.size) {
size = pagination.props.size
if (pagination.size) {
size = pagination.size
} else if (this.size === 'middle' || this.size === 'small') {
size = 'small'
}
const total = (pagination.props && pagination.props.total) || this.getLocalData().length
const { class: cls, style, on, props } = pagination
const total = pagination.total || this.getLocalData().length
const { class: cls, style, onChange, onShowSizeChange, ...restProps } = pagination // eslint-disable-line
const paginationProps = mergeProps({
key: 'pagination',
class: classNames(cls, `${this.prefixCls}-pagination`),
props: {
...props,
...restProps,
total,
size,
current: this.getMaxCurrent(total),
},
style,
on: {
...on,
change: this.handlePageChange,
showSizeChange: this.handleShowSizeChange,
},
@ -791,12 +777,10 @@ export default {
// Get pagination, filters, sorter
prepareParamsArguments (state) {
const pagination = cloneDeep(state.sPagination)
const pagination = { ...state.sPagination }
// remove useless handle function in Table.onChange
if (pagination.on) {
delete pagination.on.change
delete pagination.on.showSizeChange
}
delete pagination.onChange
delete pagination.onShowSizeChange
const filters = state.sFilters
const sorter = {}
if (state.sSortColumn && state.sSortOrder) {
@ -822,14 +806,14 @@ export default {
let data = this.getLocalData()
let current
let pageSize
const pagProps = this.sPagination.props || {}
const sPagination = this.sPagination
//
if (!this.hasPagination()) {
pageSize = Number.MAX_VALUE
current = 1
} else {
pageSize = pagProps.pageSize
current = this.getMaxCurrent(pagProps.total || data.length)
pageSize = sPagination.pageSize
current = this.getMaxCurrent(sPagination.total || data.length)
}
//

View File

@ -0,0 +1,99 @@
<cn>
#### 远程加载数据
这个例子通过简单的 ajax 读取方式,演示了如何从服务端读取并展现数据,具有筛选、排序等功能以及页面 loading 效果。开发者可以自行接入其他数据处理方式。
另外,本例也展示了筛选排序功能如何交给服务端实现,列不需要指定具体的 `onFilter``sorter` 函数,而是在把筛选和排序的参数发到服务端来处理。
**注意,此示例使用 [模拟接口](https://randomuser.me),展示数据可能不准确,请打开网络面板查看请求。**
</cn>
<us>
#### Ajax
This example shows how to fetch and present data from remote server, and how to implement filtering and sorting in server side by sending related parameters to server.
**Note, this example use [Mock API](https://randomuser.me) that you can look up in Network Console.**
</us>
```html
<template>
<a-table :columns="columns"
:rowKey="record => record.registered"
: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%',
slotScopeName: '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();
},
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;
});
}
},
data() {
return {
data: [],
pagination: {},
loading: false,
columns,
}
}
}
</script>
```

View File

@ -0,0 +1,76 @@
<cn>
#### 基本用法
简单的表格,最后一列是各种操作。
</cn>
<us>
#### basic Usage
Simple table with actions.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data">
<template slot="name" slot-scope="text">
<a href="#">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="#">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="#">Delete</a>
<a-divider type="vertical" />
<a href="#" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</template>
</a-table>
</template>
<script>
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
slotScopeName: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Action',
key: 'action',
slotScopeName: 'action',
}];
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 {
data() {
return {
data,
columns,
}
}
}
</script>
```

View File

@ -0,0 +1,76 @@
<cn>
#### 基本用法
简单的表格,最后一列是各种操作。
</cn>
<us>
#### basic Usage
Simple table with actions.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data">
<template slot="name" slot-scope="text">
<a href="#">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="#">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="#">Delete</a>
<a-divider type="vertical" />
<a href="#" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</template>
</a-table>
</template>
<script>
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
slotScopeName: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Action',
key: 'action',
slotScopeName: 'action',
}];
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 {
data() {
return {
data,
columns,
}
}
}
</script>
```

View File

@ -0,0 +1,76 @@
<cn>
#### 基本用法
简单的表格,最后一列是各种操作。
</cn>
<us>
#### basic Usage
Simple table with actions.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data">
<template slot="name" slot-scope="text">
<a href="#">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="#">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="#">Delete</a>
<a-divider type="vertical" />
<a href="#" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</template>
</a-table>
</template>
<script>
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
slotScopeName: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Action',
key: 'action',
slotScopeName: 'action',
}];
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 {
data() {
return {
data,
columns,
}
}
}
</script>
```

View File

@ -0,0 +1,76 @@
<cn>
#### 基本用法
简单的表格,最后一列是各种操作。
</cn>
<us>
#### basic Usage
Simple table with actions.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data">
<template slot="name" slot-scope="text">
<a href="#">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="#">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="#">Delete</a>
<a-divider type="vertical" />
<a href="#" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</template>
</a-table>
</template>
<script>
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
slotScopeName: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Action',
key: 'action',
slotScopeName: 'action',
}];
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 {
data() {
return {
data,
columns,
}
}
}
</script>
```

View File

@ -0,0 +1,76 @@
<cn>
#### 基本用法
简单的表格,最后一列是各种操作。
</cn>
<us>
#### basic Usage
Simple table with actions.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data">
<template slot="name" slot-scope="text">
<a href="#">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="#">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="#">Delete</a>
<a-divider type="vertical" />
<a href="#" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</template>
</a-table>
</template>
<script>
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
slotScopeName: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Action',
key: 'action',
slotScopeName: 'action',
}];
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 {
data() {
return {
data,
columns,
}
}
}
</script>
```

View File

@ -0,0 +1,76 @@
<cn>
#### 基本用法
简单的表格,最后一列是各种操作。
</cn>
<us>
#### basic Usage
Simple table with actions.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data">
<template slot="name" slot-scope="text">
<a href="#">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="#">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="#">Delete</a>
<a-divider type="vertical" />
<a href="#" class="ant-dropdown-link">
More actions <a-icon type="down" />
</a>
</span>
</template>
</a-table>
</template>
<script>
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
slotScopeName: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Action',
key: 'action',
slotScopeName: 'action',
}];
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 {
data() {
return {
data,
columns,
}
}
}
</script>
```

View File

@ -0,0 +1,71 @@
<cn>
#### 带边框
添加表格边框线,页头和页脚。
</cn>
<us>
#### border, title and footer
Add border, title and footer for table.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data" bordered>
<template slot="name" slot-scope="text">
<a href="#">{{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',
slotScopeName: '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>
```

View File

@ -0,0 +1,143 @@
<cn>
#### 表格行/列合并
表头只支持列合并,使用 column 里的 colSpan 进行设置。
表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时,设置的表格不会渲染。
</cn>
<us>
#### colSpan and rowSpan
Table column title supports `colSpan` that set in `column`.
Table cell supports `colSpan` and `rowSpan` that set in render return object. When each of them is set to `0`, the cell will not be rendered.
</us>
```html
<template>
<a-table :columns="columns" :dataSource="data" bordered>
<template slot="name" slot-scope="text">
<a href="#">{{text}}</a>
</template>
<template slot="action" slot-scope="text, record">
<span>
<a href="#">Action 一 {{record.name}}</a>
<a-divider type="vertical" />
<a href="#">Delete</a>
<a-divider type="vertical" />
<a href="#" 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,
props: {},
};
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 {
data() {
const columns = [{
title: 'Name',
dataIndex: 'name',
customRender: (text, row, index) => {
if (index < 4) {
return <a href="#">{text}</a>;
}
return {
children: <a href="#">{text}</a>,
props: {
colSpan: 5,
},
};
},
}, {
title: 'Age',
dataIndex: 'age',
render: renderContent,
}, {
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
customRender: (value, row, index) => {
const obj = {
children: value,
props: {},
};
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',
render: renderContent,
}, {
title: 'Address',
dataIndex: 'address',
render: renderContent,
}];
return {
data,
columns,
}
}
}
</script>
```

View File

@ -37,142 +37,139 @@ export default {
this.setNeverShown(column)
})
},
componentWillReceiveProps (nextProps) {
const { column } = nextProps
this.setNeverShown(column)
const newState = {}
if ('selectedKeys' in nextProps) {
newState.selectedKeys = nextProps.selectedKeys
}
if ('filterDropdownVisible' in column) {
newState.visible = column.filterDropdownVisible
}
if (Object.keys(newState).length > 0) {
this.setState(newState)
}
watch: {
'column.fixed': function (val) {
this.setNeverShown(this.column)
},
column (val) {
if ('filterDropdownVisible' in val) {
this.sVisible = val.filterDropdownVisible
}
},
selectedKeys (val) {
this.sSelectedKeys = val
},
},
methods: {
},
setNeverShown (column) {
const rootNode = this.$el
const filterBelongToScrollBody = !!closest(rootNode, `.ant-table-scroll`)
if (filterBelongToScrollBody) {
// When fixed column have filters, there will be two dropdown menus
// Filter dropdown menu inside scroll body should never be shown
// To fix https://github.com/ant-design/ant-design/issues/5010 and
// https://github.com/ant-design/ant-design/issues/7909
this.neverShown = !!column.fixed
}
},
setSelectedKeys ({ selectedKeys }) {
this.setState({ sSelectedKeys: selectedKeys })
},
setVisible (visible) {
const { column } = this
if (!('filterDropdownVisible' in column)) {
this.setState({ sVisible: visible })
}
if (column.onFilterDropdownVisibleChange) {
column.onFilterDropdownVisibleChange(visible)
}
},
handleClearFilters () {
this.setState({
sSelectedKeys: [],
}, this.handleConfirm)
},
handleConfirm () {
this.setVisible(false)
this.confirmFilter()
},
onVisibleChange (visible) {
this.setVisible(visible)
if (!visible) {
this.confirmFilter()
}
},
confirmFilter () {
if (this.sSelectedKeys !== this.selectedKeys) {
this.confirmFilter(this.column, this.sSelectedKeys)
}
},
renderMenuItem (item) {
const { column } = this
const multiple = ('filterMultiple' in column) ? column.filterMultiple : true
const input = multiple ? (
<Checkbox checked={this.sSelectedKeys.indexOf(item.value.toString()) >= 0} />
) : (
<Radio checked={this.sSelectedKeys.indexOf(item.value.toString()) >= 0} />
)
return (
<MenuItem key={item.value}>
{input}
<span>{item.text}</span>
</MenuItem>
)
},
hasSubMenu () {
const { column: { filters = [] }} = this
return filters.some(item => !!(item.children && item.children.length > 0))
},
renderMenus (items) {
return items.map(item => {
if (item.children && item.children.length > 0) {
const { sKeyPathOfSelectedItem } = this
const containSelected = Object.keys(sKeyPathOfSelectedItem).some(
key => sKeyPathOfSelectedItem[key].indexOf(item.value) >= 0,
)
const subMenuCls = containSelected ? `${this.dropdownPrefixCls}-submenu-contain-selected` : ''
return (
<SubMenu title={item.text} class={subMenuCls} key={item.value.toString()}>
{this.renderMenus(item.children)}
</SubMenu>
)
setNeverShown (column) {
const rootNode = this.$el
const filterBelongToScrollBody = !!closest(rootNode, `.ant-table-scroll`)
if (filterBelongToScrollBody) {
// When fixed column have filters, there will be two dropdown menus
// Filter dropdown menu inside scroll body should never be shown
// To fix https://github.com/ant-design/ant-design/issues/5010 and
// https://github.com/ant-design/ant-design/issues/7909
this.neverShown = !!column.fixed
}
return this.renderMenuItem(item)
})
},
setSelectedKeys ({ selectedKeys }) {
this.setState({ sSelectedKeys: selectedKeys })
},
setVisible (visible) {
const { column } = this
if (!('filterDropdownVisible' in column)) {
this.setState({ sVisible: visible })
}
if (column.onFilterDropdownVisibleChange) {
column.onFilterDropdownVisibleChange(visible)
}
},
handleClearFilters () {
this.setState({
sSelectedKeys: [],
}, this.handleConfirm)
},
handleConfirm () {
this.setVisible(false)
this.confirmFilter2()
},
onVisibleChange (visible) {
this.setVisible(visible)
if (!visible) {
this.confirmFilter2()
}
},
confirmFilter2 () {
if (this.sSelectedKeys !== this.selectedKeys) {
this.confirmFilter(this.column, this.sSelectedKeys)
}
},
renderMenuItem (item) {
const { column } = this
const multiple = ('filterMultiple' in column) ? column.filterMultiple : true
const input = multiple ? (
<Checkbox checked={this.sSelectedKeys.indexOf(item.value.toString()) >= 0} />
) : (
<Radio checked={this.sSelectedKeys.indexOf(item.value.toString()) >= 0} />
)
return (
<MenuItem key={item.value}>
{input}
<span>{item.text}</span>
</MenuItem>
)
},
hasSubMenu () {
const { column: { filters = [] }} = this
return filters.some(item => !!(item.children && item.children.length > 0))
},
renderMenus (items) {
return items.map(item => {
if (item.children && item.children.length > 0) {
const { sKeyPathOfSelectedItem } = this
const containSelected = Object.keys(sKeyPathOfSelectedItem).some(
key => sKeyPathOfSelectedItem[key].indexOf(item.value) >= 0,
)
const subMenuCls = containSelected ? `${this.dropdownPrefixCls}-submenu-contain-selected` : ''
return (
<SubMenu title={item.text} class={subMenuCls} key={item.value.toString()}>
{this.renderMenus(item.children)}
</SubMenu>
)
}
return this.renderMenuItem(item)
})
},
handleMenuItemClick (info) {
if (info.keyPath.length <= 1) {
return
}
const keyPathOfSelectedItem = this.sKeyPathOfSelectedItem
if (this.sSelectedKeys.indexOf(info.key) >= 0) {
// deselect SubMenu child
delete keyPathOfSelectedItem[info.key]
} else {
// select SubMenu child
keyPathOfSelectedItem[info.key] = info.keyPath
}
this.setState({ keyPathOfSelectedItem })
},
renderFilterIcon () {
const { column, locale, prefixCls } = this
const filterIcon = column.filterIcon
const dropdownSelectedClass = this.selectedKeys.length > 0 ? `${prefixCls}-selected` : ''
return filterIcon ? cloneElement(filterIcon, {
title: locale.filterTitle,
className: classNames(filterIcon.className, {
[`${prefixCls}-icon`]: true,
}),
}) : <Icon title={locale.filterTitle} type='filter' class={dropdownSelectedClass} />
},
},
handleMenuItemClick (info) {
if (info.keyPath.length <= 1) {
return
}
const keyPathOfSelectedItem = this.sKeyPathOfSelectedItem
if (this.sSelectedKeys.indexOf(info.key) >= 0) {
// deselect SubMenu child
delete keyPathOfSelectedItem[info.key]
} else {
// select SubMenu child
keyPathOfSelectedItem[info.key] = info.keyPath
}
this.setState({ keyPathOfSelectedItem })
},
renderFilterIcon () {
const { column, locale, prefixCls } = this
const filterIcon = column.filterIcon
const dropdownSelectedClass = this.selectedKeys.length > 0 ? `${prefixCls}-selected` : ''
return filterIcon ? cloneElement(filterIcon, {
title: locale.filterTitle,
className: classNames(filterIcon.className, {
[`${prefixCls}-icon`]: true,
}),
}) : <Icon title={locale.filterTitle} type='filter' class={dropdownSelectedClass} />
},
render () {
const { column, locale, prefixCls, dropdownPrefixCls, getPopupContainer } = this
// default multiple selection in filter dropdown

View File

@ -3,6 +3,7 @@ import T from './Table'
import { getOptionProps, getKey, getClass,
getStyle, getEvents, getSlotOptions, camelize, getSlots,
} from '../_util/props-util'
const Table = {
name: 'Table',
Column: T.Column,
@ -61,13 +62,19 @@ const Table = {
},
},
render () {
const { $listeners, $slots, normalize } = this
const { $listeners, $slots, normalize, $scopedSlots } = this
const props = getOptionProps(this)
const columns = props.columns ? this.updateColumns(props.columns) : normalize($slots.default)
let { title, footer } = props
const { title: slotTitle, footer: slotFooter } = $scopedSlots
title = title || slotTitle
footer = footer || slotFooter
const tProps = {
props: {
...props,
columns,
title,
footer,
},
on: $listeners,
}

View File

@ -108,8 +108,8 @@ export const TableProps = {
useFixedHeader: PropTypes.bool,
bordered: PropTypes.bool,
showHeader: PropTypes.bool,
footer: PropTypes.any,
title: PropTypes.any,
footer: PropTypes.func,
title: PropTypes.func,
scroll: PropTypes.object,
childrenColumnName: PropTypes.string,
bodyStyle: PropTypes.any,

6
package-lock.json generated
View File

@ -14311,6 +14311,12 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
"reqwest": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/reqwest/-/reqwest-2.0.5.tgz",
"integrity": "sha1-APsVrEkYxBnKgrQ/JMeIguZgOaE=",
"dev": true
},
"resolve": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",

View File

@ -105,6 +105,7 @@
"postcss-loader": "^2.1.2",
"pre-commit": "^1.2.2",
"querystring": "^0.2.0",
"reqwest": "^2.0.5",
"rimraf": "^2.6.2",
"rucksack-css": "^1.0.2",
"selenium-server": "^3.0.1",