diff --git a/components/table/Table.jsx b/components/table/Table.jsx index d53f5dffc..3219dc6f8 100755 --- a/components/table/Table.jsx +++ b/components/table/Table.jsx @@ -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) } // 分页 diff --git a/components/table/demo/ajax.md b/components/table/demo/ajax.md new file mode 100644 index 000000000..a79a065a9 --- /dev/null +++ b/components/table/demo/ajax.md @@ -0,0 +1,99 @@ + +#### 远程加载数据 +这个例子通过简单的 ajax 读取方式,演示了如何从服务端读取并展现数据,具有筛选、排序等功能以及页面 loading 效果。开发者可以自行接入其他数据处理方式。 +另外,本例也展示了筛选排序功能如何交给服务端实现,列不需要指定具体的 `onFilter` 和 `sorter` 函数,而是在把筛选和排序的参数发到服务端来处理。 +**注意,此示例使用 [模拟接口](https://randomuser.me),展示数据可能不准确,请打开网络面板查看请求。** + + + +#### 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.** + + +```html + + +``` diff --git a/components/table/demo/basic.4.md b/components/table/demo/basic.4.md new file mode 100644 index 000000000..332ec9843 --- /dev/null +++ b/components/table/demo/basic.4.md @@ -0,0 +1,76 @@ + +#### 基本用法 +简单的表格,最后一列是各种操作。 + + + +#### basic Usage +Simple table with actions. + + +```html + + +``` diff --git a/components/table/demo/basic.5.md b/components/table/demo/basic.5.md new file mode 100644 index 000000000..332ec9843 --- /dev/null +++ b/components/table/demo/basic.5.md @@ -0,0 +1,76 @@ + +#### 基本用法 +简单的表格,最后一列是各种操作。 + + + +#### basic Usage +Simple table with actions. + + +```html + + +``` diff --git a/components/table/demo/basic.6.md b/components/table/demo/basic.6.md new file mode 100644 index 000000000..332ec9843 --- /dev/null +++ b/components/table/demo/basic.6.md @@ -0,0 +1,76 @@ + +#### 基本用法 +简单的表格,最后一列是各种操作。 + + + +#### basic Usage +Simple table with actions. + + +```html + + +``` diff --git a/components/table/demo/basic.7.md b/components/table/demo/basic.7.md new file mode 100644 index 000000000..332ec9843 --- /dev/null +++ b/components/table/demo/basic.7.md @@ -0,0 +1,76 @@ + +#### 基本用法 +简单的表格,最后一列是各种操作。 + + + +#### basic Usage +Simple table with actions. + + +```html + + +``` diff --git a/components/table/demo/basic.8.md b/components/table/demo/basic.8.md new file mode 100644 index 000000000..332ec9843 --- /dev/null +++ b/components/table/demo/basic.8.md @@ -0,0 +1,76 @@ + +#### 基本用法 +简单的表格,最后一列是各种操作。 + + + +#### basic Usage +Simple table with actions. + + +```html + + +``` diff --git a/components/table/demo/basic.9.md b/components/table/demo/basic.9.md new file mode 100644 index 000000000..332ec9843 --- /dev/null +++ b/components/table/demo/basic.9.md @@ -0,0 +1,76 @@ + +#### 基本用法 +简单的表格,最后一列是各种操作。 + + + +#### basic Usage +Simple table with actions. + + +```html + + +``` diff --git a/components/table/demo/bordered.md b/components/table/demo/bordered.md new file mode 100644 index 000000000..9de3f49c3 --- /dev/null +++ b/components/table/demo/bordered.md @@ -0,0 +1,71 @@ + +#### 带边框 +添加表格边框线,页头和页脚。 + + + +#### border, title and footer +Add border, title and footer for table. + + +```html + + + +``` diff --git a/components/table/demo/colspan-rowspan.md b/components/table/demo/colspan-rowspan.md new file mode 100644 index 000000000..46289664e --- /dev/null +++ b/components/table/demo/colspan-rowspan.md @@ -0,0 +1,143 @@ + +#### 表格行/列合并 +表头只支持列合并,使用 column 里的 colSpan 进行设置。 +表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时,设置的表格不会渲染。 + + + +#### 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. + + +```html + + +``` diff --git a/components/table/filterDropdown.jsx b/components/table/filterDropdown.jsx index 3844f62ad..203ea0515 100755 --- a/components/table/filterDropdown.jsx +++ b/components/table/filterDropdown.jsx @@ -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 ? ( - = 0} /> - ) : ( - = 0} /> - ) - - return ( - - {input} - {item.text} - - ) - }, - - 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 ( - - {this.renderMenus(item.children)} - - ) + 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 ? ( + = 0} /> + ) : ( + = 0} /> + ) + + return ( + + {input} + {item.text} + + ) + }, + + 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 ( + + {this.renderMenus(item.children)} + + ) + } + 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, + }), + }) : + }, }, - 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, - }), - }) : - }, render () { const { column, locale, prefixCls, dropdownPrefixCls, getPopupContainer } = this // default multiple selection in filter dropdown diff --git a/components/table/index.jsx b/components/table/index.jsx index 3bb03589c..0a5bbd559 100644 --- a/components/table/index.jsx +++ b/components/table/index.jsx @@ -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, } diff --git a/components/table/interface.js b/components/table/interface.js index 1d21f4411..e46a7a772 100644 --- a/components/table/interface.js +++ b/components/table/interface.js @@ -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, diff --git a/package-lock.json b/package-lock.json index e1b9b05d3..dc96015fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 81d5ce805..c5ae2dd47 100644 --- a/package.json +++ b/package.json @@ -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",