From cc589654264cdd6ca34ded5f6f25e4049fc9520a Mon Sep 17 00:00:00 2001 From: yiminghe Date: Fri, 31 Jul 2015 10:40:23 +0800 Subject: [PATCH] fix table --- components/table/filterMenu.jsx | 52 +++-- components/table/index.jsx | 400 ++++++++++++++++++-------------- 2 files changed, 263 insertions(+), 189 deletions(-) diff --git a/components/table/filterMenu.jsx b/components/table/filterMenu.jsx index 45e6189417..470540951b 100644 --- a/components/table/filterMenu.jsx +++ b/components/table/filterMenu.jsx @@ -1,38 +1,47 @@ import React from 'react'; import Menu from 'rc-menu'; -export default React.createClass({ +var FilterMenu = React.createClass({ getInitialState() { return { - selectedFilters: [] + selectedFilters: this.props.selectedFilters }; }, + componentWillReceiveProps(nextProps){ + this.setState({ + selectedFilters: nextProps.selectedFilters + }); + }, getDefaultProps() { return { - handleFilter: function() {}, + handleFilter: function () { + }, column: null }; }, - handleSelectFilter: function(selected) { - this.state.selectedFilters.push(selected); + handleSelectFilter: function (selected) { this.setState({ - selectedFilters: this.state.selectedFilters + selectedFilters: this.state.selectedFilters.concat(selected) }); }, - handleDeselectFilter: function(key) { + handleDeselectFilter: function (key) { var index = this.state.selectedFilters.indexOf(key); if (index !== -1) { - this.state.selectedFilters.splice(index, 1); + var selectedFilters = this.state.selectedFilters.concat(); + selectedFilters.splice(index, 1); + this.setState({ + selectedFilters: selectedFilters + }); } - this.setState({ - selectedFilters: this.state.selectedFilters - }); }, handleClearFilters() { this.setState({ selectedFilters: [] }); }, + handleConfirm(){ + this.props.confirmFilter(this.props.column, this.state.selectedFilters); + }, renderMenus(items) { let menuItems = items.map((item) => { return {item.text}; @@ -41,33 +50,34 @@ export default React.createClass({ }, render() { let column = this.props.column; - column.selectedFilters = this.state.selectedFilters; return + prefixCls="ant-dropdown-menu" + className="ant-table-filter-dropdown" + onSelect={this.handleSelectFilter} + onDeselect={this.handleDeselectFilter} + selectedKeys={this.state.selectedFilters}> {this.renderMenus(column.filters)} + onClick={this.handleConfirm}> 确定 + onClick={this.handleClearFilters}> 清空 ; } }); + +export default FilterMenu; diff --git a/components/table/index.jsx b/components/table/index.jsx index 357d4b56fc..9ff5e5f37c 100644 --- a/components/table/index.jsx +++ b/components/table/index.jsx @@ -6,22 +6,33 @@ import Checkbox from '../checkbox'; import FilterMenu from './filterMenu'; import Pagination from '../pagination'; import objectAssign from 'object-assign'; +import equals from 'is-equal-shallow'; -export default React.createClass({ - getInitialState() { - this.initDataSource(this.props.dataSource); +function noop() { +} - let noPagination = (this.props.pagination === false); - let pagination = this.initPagination(this.props.pagination); +function defaultResolve(data) { + return data || []; +} +export default React.createClass({ + getInitialState() { return { + // 减少状态 selectedRowKeys: [], - loading: false, - pagination: pagination, - noPagination: noPagination, - data: [] + // only for remote + data: [], + filters: {}, + loading: !this.isLocalDataSource(), + sortColumn: '', + sortOrder: '', + sorter: null, + pagination: this.hasPagination() ? objectAssign({ + pageSize: 10 + }, this.props.pagination) : {} }; }, + getDefaultProps() { return { prefixCls: 'ant-table', @@ -30,43 +41,42 @@ export default React.createClass({ size: 'normal' }; }, - componentWillReceiveProps(nextProps) { - if ('pagination' in nextProps) { - let noPagination = (nextProps.pagination === false); + + componentWillReceiveProps(nextProps){ + if (('pagination' in nextProps) && nextProps.pagination !== false) { this.setState({ - pagination: this.initPagination(nextProps.pagination), - noPagination: noPagination + pagination: objectAssign({}, this.state.pagination, nextProps.pagination) }); } - if ('dataSource' in nextProps) { - this.initDataSource(nextProps.dataSource); + if (!this.isLocalDataSource()) { + if (!equals(nextProps, this.props)) { + this.setState({ + selectedRowKeys: [], + loading: true + }, this.fetch); + } + } + if (nextProps.columns !== this.props.columns) { this.setState({ - data: nextProps.dataSource + filters: {} }); } }, - initDataSource(dataSource) { - // 支持两种模式 - if (Array.isArray(dataSource)) { - this.mode = 'local'; - // 保留原来的数据 - this.originDataSource = dataSource.slice(0); - } else { - this.mode = 'remote'; - this.dataSource = objectAssign({ - resolve: function(data) { - return data || []; - }, - getParams: function() {}, - getPagination: function() {} - }, dataSource); + hasPagination(pagination){ + if (pagination === undefined) { + pagination = this.props.pagination; } + return pagination !== false; + }, + isLocalDataSource(){ + return Array.isArray(this.props.dataSource); }, - initPagination(pagination) { + getRemoteDataSource(){ return objectAssign({ - pageSize: 10, - total: this.props.dataSource.length - }, pagination); + resolve: defaultResolve, + getParams: noop, + getPagination: noop + }, this.props.dataSource); }, toggleSortOrder(order, column) { let sortColumn = this.state.sortColumn; @@ -89,8 +99,8 @@ export default React.createClass({ sortColumn.className = 'ant-table-column-sort'; } } - if (this.mode === 'local') { - sorter = function() { + if (this.isLocalDataSource()) { + sorter = function () { let result = column.sorter.apply(this, arguments); if (sortOrder === 'ascend') { return result; @@ -99,87 +109,94 @@ export default React.createClass({ } }; } - this.setState({ + this.fetch({ sortOrder: sortOrder, sortColumn: sortColumn, sorter: sorter - }, this.fetch); + }); }, - handleFilter(column) { - let columnIndex = this.props.columns.indexOf(column); - let filterFns = []; - if (this.mode === 'local') { - filterFns[columnIndex] = function(record) { - if (column.selectedFilters.length === 0) { - return true; - } - return column.selectedFilters.some(function(value) { - return column.onFilter.call(this, value, record); - }); - }; - } - this.setState({ - filterFns: filterFns - }, this.fetch); + handleFilter(column, filters) { + filters = objectAssign({}, this.state.filters, { + [this.getColumnKey(column)]: filters + }); + this.fetch({ + selectedRowKeys: [], + filters: filters + }); }, - handleSelect(rowIndex, e) { + handleSelect(record, rowIndex, e) { let checked = e.target.checked; + let selectedRowKeys = this.state.selectedRowKeys.concat(); + let key = this.getRecordKey(record, rowIndex); if (checked) { - this.state.selectedRowKeys.push(rowIndex); + selectedRowKeys.push(this.getRecordKey(record, rowIndex)); } else { - this.state.selectedRowKeys = this.state.selectedRowKeys.filter(function(i) { - return rowIndex !== i; + selectedRowKeys = selectedRowKeys.filter((i) => { + return key !== i; }); } this.setState({ - selectedRowKeys: this.state.selectedRowKeys + selectedRowKeys: selectedRowKeys }); if (this.props.rowSelection.onSelect) { - let currentRow = this.state.data[rowIndex - 1]; - let selectedRows = this.state.data.filter((row, i) => { - return this.state.selectedRowKeys.indexOf(i + 1) >= 0; + let data = this.getCurrentPageData(); + let selectedRows = data.filter((row, i) => { + return selectedRowKeys.indexOf(this.getRecordKey(row, i)) >= 0; }); - this.props.rowSelection.onSelect(currentRow, checked, selectedRows); + this.props.rowSelection.onSelect(record, checked, selectedRows); } }, handleSelectAllRow(e) { let checked = e.target.checked; - let selectedRowKeys = checked ? this.state.data.map(function(item, i) { - return i + 1; - }) : []; + let data = this.getCurrentPageData(); + let selectedRowKeys = checked ? data.map((item, i) => { + return this.getRecordKey(item, i); + }) : []; this.setState({ selectedRowKeys: selectedRowKeys }); if (this.props.rowSelection.onSelectAll) { - let selectedRows = this.state.data.filter((row, i) => { - return selectedRowKeys.indexOf(i + 1) >= 0; + let selectedRows = data.filter((row, i) => { + return selectedRowKeys.indexOf(this.getRecordKey(row, i)) >= 0; }); this.props.rowSelection.onSelectAll(checked, selectedRows); } }, handlePageChange(current) { - let pagination = this.state.pagination || {}; + let pagination = objectAssign({}, this.state.pagination); if (current) { pagination.current = current; } else { pagination.current = pagination.current || 1; } - this.setState({ + this.fetch({ + // 防止内存泄漏,只维持当页 + selectedRowKeys: [], pagination: pagination - }, this.fetch); + }); }, renderSelectionCheckBox(value, record, index) { - let rowIndex = index + 1; // 从 1 开始 + let rowIndex = this.getRecordKey(record, index); // 从 1 开始 let checked = this.state.selectedRowKeys.indexOf(rowIndex) >= 0; - return ; + return ; + }, + getRecordKey(record, index){ + return record.key || index; }, renderRowSelection() { - var columns = this.props.columns; + let columns = this.props.columns.concat(); if (this.props.rowSelection) { - let checked = this.state.data.every(function(item, i) { - return this.state.selectedRowKeys.indexOf(i + 1) >= 0; - }, this); - let checkboxAll = ; + let data = this.getCurrentPageData(); + let checked; + if (!data.length) { + checked = false; + } else { + checked = data.every((item, i) => { + let key = this.getRecordKey(item, i); + return this.state.selectedRowKeys.indexOf(key) >= 0; + }); + } + let checkboxAll = ; let selectionColumn = { key: 'selection-column', title: checkboxAll, @@ -188,7 +205,7 @@ export default React.createClass({ className: 'ant-table-selection-column' }; if (columns[0] && - columns[0].key === 'selection-column') { + columns[0].key === 'selection-column') { columns[0] = selectionColumn; } else { columns.unshift(selectionColumn); @@ -196,22 +213,32 @@ export default React.createClass({ } return columns; }, - renderColumnsDropdown() { - return this.props.columns.map((column) => { - if (!column.originTitle) { - column.originTitle = column.title; - } + + getCurrentPageData(){ + return this.isLocalDataSource() ? this.getLocalDataPaging() : this.state.data; + }, + + getColumnKey(column){ + return column.key || column.dataIndex; + }, + + renderColumnsDropdown(columns) { + return columns.map((column) => { + column = objectAssign({}, column); + let key = this.getColumnKey(column); let filterDropdown, menus, sortButton; if (column.filters && column.filters.length > 0) { - column.selectedFilters = column.selectedFilters || []; - menus = ; + let colFilters = this.state.filters[key] || []; + menus = ; let dropdownSelectedClass = ''; - if (column.selectedFilters && column.selectedFilters.length > 0) { + if (colFilters.length > 0) { dropdownSelectedClass = 'ant-table-filter-selected'; } filterDropdown = + closeOnSelect={false} + overlay={menus}> ; } @@ -220,20 +247,21 @@ export default React.createClass({ sortButton =
+ title="升序排序" + onClick={this.toggleSortOrder.bind(this, 'ascend', column)}> + title="降序排序" + onClick={this.toggleSortOrder.bind(this, 'descend', column)}>
; } + let originalTitle = column.title; column.title = [ - column.originTitle, + originalTitle, sortButton, filterDropdown ]; @@ -242,129 +270,165 @@ export default React.createClass({ }, renderPagination() { // 强制不需要分页 - if (this.state.noPagination) { - return ''; + if (!this.hasPagination()) { + return null; } let classString = 'ant-table-pagination'; if (this.props.size === 'small') { classString += ' mini'; } + let total; + if (this.isLocalDataSource()) { + total = this.getLocalData().length; + } return ; }, - prepareParamsArguments() { + prepareParamsArguments(state) { // 准备筛选、排序、分页的参数 let pagination; let filters = {}; let sorter = {}; - pagination = this.state.pagination; - this.props.columns.forEach(function(column) { - if (column.dataIndex && column.selectedFilters && - column.selectedFilters.length > 0) { - filters[column.dataIndex] = column.selectedFilters; + pagination = state.pagination; + this.props.columns.forEach((column) => { + let colFilters = state.filters[this.getColumnKey(column)] || []; + if (colFilters.length > 0) { + filters[this.getColumnKey(column)] = colFilters; } }); - if (this.state.sortColumn && this.state.sortOrder && - this.state.sortColumn.dataIndex) { - sorter.field = this.state.sortColumn.dataIndex; - sorter.order = this.state.sortOrder; + if (state.sortColumn && + state.sortOrder && + state.sortColumn.dataIndex) { + sorter.field = state.sortColumn.dataIndex; + sorter.order = state.sortOrder; } return [pagination, filters, sorter]; }, - fetch() { - if (this.mode === 'remote') { + + fetch(newState) { + if (this.isLocalDataSource()) { + if (newState) { + this.setState(newState); + } + } else { + let state = objectAssign({}, this.state, newState); + if (newState || !this.state.loading) { + this.setState(objectAssign({ + loading: true + }, newState)); + } // remote 模式使用 this.dataSource - let dataSource = this.dataSource; - this.setState({ - loading: true - }); + let dataSource = this.getRemoteDataSource(); jQuery.ajax({ url: dataSource.url, - data: dataSource.getParams.apply(this, this.prepareParamsArguments()) || {}, + data: dataSource.getParams.apply(this, this.prepareParamsArguments(state)) || {}, headers: dataSource.headers, dataType: 'json', success: (result) => { if (this.isMounted()) { let pagination = objectAssign( - this.state.pagination, + state.pagination, dataSource.getPagination.call(this, result) ); this.setState({ + loading: false, data: dataSource.resolve.call(this, result), - pagination: pagination, - loading: false + pagination: pagination }); } }, error: () => { this.setState({ - loading: false + loading: false, + data: [] }); } }); + } + }, + + findColumn(myKey){ + return this.props.columns.filter((c) => { + return this.getColumnKey(c) === myKey; + })[0]; + }, + + getLocalDataPaging(){ + let data = this.getLocalData(); + let current, pageSize; + let state = this.state; + // 如果没有分页的话,默认全部展示 + if (!this.hasPagination()) { + pageSize = Number.MAX_VALUE; + current = 1; } else { - let data = this.props.dataSource; - let current, pageSize; - // 如果没有分页的话,默认全部展示 - if (this.state.noPagination) { - pageSize = Number.MAX_VALUE; - current = 1; - } else { - pageSize = this.state.pagination.pageSize; - current = this.state.pagination.current; - } - // 排序 - if (this.state.sortOrder && this.state.sorter) { - data = data.sort(this.state.sorter); - } else { - data = this.originDataSource.slice(); - } - // 筛选 - if (this.state.filterFns) { - this.state.filterFns.forEach(function(filterFn) { - if (typeof filterFn === 'function') { - data = data.filter(filterFn); - } - }); - } - // 分页 - // --- - // 当数据量少于每页数量时,直接设置数据 - // 否则进行读取分页数据 - if (data.length > pageSize || pageSize === Number.MAX_VALUE) { - data = data.filter(function(item, i) { - if (i >= (current - 1) * pageSize && - i < current * pageSize) { - return item; - } + pageSize = state.pagination.pageSize; + current = state.pagination.current; + } + // 分页 + // --- + // 当数据量少于每页数量时,直接设置数据 + // 否则进行读取分页数据 + if (data.length > pageSize || pageSize === Number.MAX_VALUE) { + data = data.filter((item, i) => { + if (i >= (current - 1) * pageSize && + i < current * pageSize) { + return item; + } + }); + } + return data; + }, + + getLocalData(){ + let state = this.state; + let data = this.props.dataSource; + // 排序 + if (state.sortOrder && state.sorter) { + data = data.sort(state.sorter); + } + // 筛选 + if (state.filters) { + Object.keys(state.filters).forEach((columnKey) => { + let col = this.findColumn(columnKey); + let values = state.filters[columnKey] || []; + data = data.filter((record) => { + return values.some((v)=> { + return col.onFilter(v, record); + }); }); - } - // 完成数据 - this.setState({ - data: data }); } + return data; }, + componentDidMount() { - this.handlePageChange(); + if (!this.isLocalDataSource()) { + this.fetch(); + } }, - render() { - this.props.columns = this.renderRowSelection(); - var classString = ''; - if (this.state.loading) { + render() { + let data = this.getCurrentPageData(); + let columns = this.renderRowSelection(); + let classString = ''; + if (this.state.loading && this.isLocalDataSource()) { classString += ' ant-table-loading'; } if (this.props.size === 'small') { classString += ' ant-table-small'; } - + columns = this.renderColumnsDropdown(columns); return
- +
{this.renderPagination()} ; } -- GitLab