From f1e62240e0d804ac03d8ca28616621f3c312f31f Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sat, 31 Mar 2018 21:11:02 +0800 Subject: [PATCH] add table demo --- components/index.js | 5 + components/style.js | 1 + components/table/Table.jsx | 1500 ++++++++++++++------------- components/table/createBodyRow.jsx | 2 +- components/table/demo/basic.md | 59 ++ components/table/filterDropdown.jsx | 3 +- components/table/interface.js | 5 +- examples/routes.js | 2 +- 8 files changed, 824 insertions(+), 753 deletions(-) create mode 100644 components/table/demo/basic.md diff --git a/components/index.js b/components/index.js index 5b19a3170..654b206c8 100644 --- a/components/index.js +++ b/components/index.js @@ -132,4 +132,9 @@ import DatePicker from './date-picker' const { MonthPicker, RangePicker, WeekPicker } = DatePicker export { DatePicker, MonthPicker, RangePicker, WeekPicker } +import Table from './table' +const { Column: TableColumn, ColumnGroup: TableColumnGroup } = Table + +export { Table, TableColumn, TableColumnGroup } + export { default as version } from './version' diff --git a/components/style.js b/components/style.js index 94578a116..032e7069f 100644 --- a/components/style.js +++ b/components/style.js @@ -34,3 +34,4 @@ import './steps/style' import './breadcrumb/style' import './calendar/style' import './date-picker/style' +import './table/style' diff --git a/components/table/Table.jsx b/components/table/Table.jsx index 0832a6245..d53f5dffc 100755 --- a/components/table/Table.jsx +++ b/components/table/Table.jsx @@ -1,4 +1,5 @@ +import cloneDeep from 'lodash/cloneDeep' import VcTable from '../vc-table' import classNames from 'classnames' import Pagination from '../pagination' @@ -16,6 +17,7 @@ import ColumnGroup from './ColumnGroup' import createBodyRow from './createBodyRow' import { flatArray, treeMap, flatFilter } from './util' import { initDefaultProps, mergeProps, getOptionProps } from '../_util/props-util' +import BaseMixin from '../_util/BaseMixin' import { TableProps, } from './interface' @@ -47,12 +49,12 @@ export default { name: 'Table', Column, ColumnGroup, - + mixins: [BaseMixin], props: initDefaultProps(TableProps, { dataSource: [], prefixCls: 'ant-table', useFixedHeader: false, - rowSelection: null, + // rowSelection: null, size: 'large', loading: false, bordered: false, @@ -148,802 +150,804 @@ export default { this.createComponents(val, preVal) }, }, - - getCheckboxPropsByItem (item, index) { - const { rowSelection = {}} = this - if (!rowSelection.getCheckboxProps) { - return {} - } - const key = this.getRecordKey(item, index) - // Cache checkboxProps - if (!this.CheckboxPropsCache[key]) { - this.CheckboxPropsCache[key] = rowSelection.getCheckboxProps(item) - } - return this.CheckboxPropsCache[key] - }, - - getDefaultSelection () { - const { rowSelection = {}} = this - if (!rowSelection.getCheckboxProps) { - return [] - } - return this.getFlatData() - .filter((item, rowIndex) => this.getCheckboxPropsByItem(item, rowIndex).defaultChecked) - .map((record, rowIndex) => this.getRecordKey(record, rowIndex)) - }, - - 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, - }, - }) : {} - }, - - onRow (record, index) { - const { prefixCls, customRow } = this - const custom = customRow ? customRow(record, index) : {} - return mergeProps(custom, { - props: { - prefixCls, - store: this.store, - rowKey: this.getRecordKey(record, index), - }, - }) - }, - - setSelectedRowKeys (selectedRowKeys, { selectWay, record, checked, changeRowKeys }) { - const { rowSelection = {}, $listeners: { - rowSelectionChange, - rowSelectionSelect, - rowSelectionSelectAll, - rowSelectionSelectInvert, - }} = this - if (rowSelection && !('selectedRowKeys' in rowSelection)) { - this.store.setState({ selectedRowKeys }) - } - const data = this.getFlatData() - if (!rowSelectionChange && !rowSelection[selectWay]) { - return - } - const selectedRows = data.filter( - (row, i) => selectedRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, - ) - this.$emit('rowSelectionChange', selectedRowKeys, selectedRows) - - if (selectWay === 'onSelect' && rowSelectionSelect) { - this.$emit('rowSelectionSelect', record, checked, selectedRows) - } else if (selectWay === 'onSelectAll' && rowSelectionSelectAll) { - const changeRows = data.filter( - (row, i) => changeRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, - ) - this.$emit('rowSelectionSelectAll', checked, selectedRows, changeRows) - } else if (selectWay === 'onSelectInvert' && rowSelectionSelectInvert) { - this.$emit('rowSelectionSelectInvert', selectedRowKeys) - } - }, - - hasPagination () { - return this.pagination !== false - }, - - isFiltersChanged (filters) { - let filtersChanged = false - if (Object.keys(filters).length !== Object.keys(this.sFilters).length) { - filtersChanged = true - } else { - Object.keys(filters).forEach(columnKey => { - if (filters[columnKey] !== this.sFilters[columnKey]) { - filtersChanged = true - } - }) - } - return filtersChanged - }, - - getSortOrderColumns (columns) { - return flatFilter( - columns || this.columns || [], - (column) => 'sortOrder' in column, - ) - }, - - getFilteredValueColumns (columns) { - return flatFilter( - columns || this.columns || [], - (column) => typeof column.filteredValue !== 'undefined', - ) - }, - - getFiltersFromColumns (columns) { - const filters = {} - this.getFilteredValueColumns(columns).forEach((col) => { - const colKey = this.getColumnKey(col) - filters[colKey] = col.filteredValue - }) - return filters - }, - - getDefaultSortOrder (columns) { - const definedSortState = this.getSortStateFromColumns(columns) - - const defaultSortedColumn = flatFilter(columns || [], (column) => column.defaultSortOrder != null)[0] - - if (defaultSortedColumn && !definedSortState.sortColumn) { - return { - sSortColumn: defaultSortedColumn, - sSortOrder: defaultSortedColumn.defaultSortOrder, + methods: { + getCheckboxPropsByItem (item, index) { + const { rowSelection = {}} = this + if (!rowSelection.getCheckboxProps) { + return {} } - } - - return definedSortState - }, - - getSortStateFromColumns (columns) { - // return first column which sortOrder is not falsy - const sortedColumn = this.getSortOrderColumns(columns).filter((col) => col.sortOrder)[0] - - if (sortedColumn) { - return { - sSortColumn: sortedColumn, - sSortOrder: sortedColumn.sortOrder, + const key = this.getRecordKey(item, index) + // Cache checkboxProps + if (!this.CheckboxPropsCache[key]) { + this.CheckboxPropsCache[key] = rowSelection.getCheckboxProps(item) } - } + return this.CheckboxPropsCache[key] + }, - return { - sSortColumn: null, - sSortOrder: null, - } - }, - - getSorterFn () { - const { sSortOrder: sortOrder, sSortColumn: sortColumn } = this - if (!sortOrder || !sortColumn || - typeof sortColumn.sorter !== 'function') { - return - } - - return (a, b) => { - const result = sortColumn.sorter(a, b) - if (result !== 0) { - return (sortOrder === 'descend') ? -result : result + getDefaultSelection () { + const { rowSelection = {}} = this + if (!rowSelection.getCheckboxProps) { + return [] } - return 0 - } - }, + return this.getFlatData() + .filter((item, rowIndex) => this.getCheckboxPropsByItem(item, rowIndex).defaultChecked) + .map((record, rowIndex) => this.getRecordKey(record, rowIndex)) + }, - toggleSortOrder (order, column) { - let { sSortOrder: sortOrder, sSortColumn: sortColumn } = this - // 只同时允许一列进行排序,否则会导致排序顺序的逻辑问题 - const isSortColumn = this.isSortColumn(column) - if (!isSortColumn) { // 当前列未排序 - sortOrder = order - sortColumn = column - } else { // 当前列已排序 - if (sortOrder === order) { // 切换为未排序状态 - sortOrder = '' - sortColumn = null - } else { // 切换为排序状态 - sortOrder = order - } - } - const newState = { - sSortOrder: sortOrder, - sSortColumn: sortColumn, - } - - // Controlled - if (this.getSortOrderColumns().length === 0) { - this.setState(newState) - } - this.$emit('change', this.prepareParamsArguments({ - ...this.$data, - ...newState, - })) - }, - - handleFilter (column, nextFilters) { - const props = this.$props - const pagination = { ...this.sPagination } - const filters = { - ...this.sFilters, - [this.getColumnKey(column)]: nextFilters, - } - // Remove filters not in current columns - const currentColumnKeys = [] - treeMap(this.columns, c => { - if (!c.children) { - currentColumnKeys.push(this.getColumnKey(c)) - } - }) - Object.keys(filters).forEach((columnKey) => { - if (currentColumnKeys.indexOf(columnKey) < 0) { - delete filters[columnKey] - } - }) - - if (props.pagination) { - // Reset current prop + getDefaultPagination (props) { + const pagination = props.pagination || {} pagination.props = pagination.props || {} - pagination.props.current = 1 - pagination.on.change(pagination.current) - } + return this.hasPagination(props) + ? mergeProps( + defaultPagination, + pagination, + { + props: { + current: pagination.props.defaultCurrent || pagination.props.current || 1, + pageSize: pagination.props.defaultPageSize || pagination.props.pageSize || 10, + }, + }) : {} + }, - const newState = { - sPagination: pagination, - sFilters: {}, - } - const filtersToSetState = { ...filters } - // Remove filters which is controlled - this.getFilteredValueColumns().forEach((col) => { - const columnKey = this.getColumnKey(col) - if (columnKey) { - delete filtersToSetState[columnKey] - } - }) - if (Object.keys(filtersToSetState).length > 0) { - newState.sFilters = filtersToSetState - } - - // 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, { + onRow (record, index) { + const { prefixCls, customRow } = this + const custom = customRow ? customRow(record, index) : {} + return mergeProps(custom, { props: { - current: this.sPagination.props.current, + prefixCls, + store: this.store, + rowKey: this.getRecordKey(record, index), }, }) - } + }, + + setSelectedRowKeys (selectedRowKeys, { selectWay, record, checked, changeRowKeys }) { + const { rowSelection = {}, $listeners: { + rowSelectionChange, + rowSelectionSelect, + rowSelectionSelectAll, + rowSelectionSelectInvert, + }} = this + if (rowSelection && !('selectedRowKeys' in rowSelection)) { + this.store.setState({ selectedRowKeys }) + } + const data = this.getFlatData() + if (!rowSelectionChange && !rowSelection[selectWay]) { + return + } + const selectedRows = data.filter( + (row, i) => selectedRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, + ) + this.$emit('rowSelectionChange', selectedRowKeys, selectedRows) + + if (selectWay === 'onSelect' && rowSelectionSelect) { + this.$emit('rowSelectionSelect', record, checked, selectedRows) + } else if (selectWay === 'onSelectAll' && rowSelectionSelectAll) { + const changeRows = data.filter( + (row, i) => changeRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, + ) + this.$emit('rowSelectionSelectAll', checked, selectedRows, changeRows) + } else if (selectWay === 'onSelectInvert' && rowSelectionSelectInvert) { + this.$emit('rowSelectionSelectInvert', selectedRowKeys) + } + }, + + hasPagination () { + return this.pagination !== false + }, + + isFiltersChanged (filters) { + let filtersChanged = false + if (Object.keys(filters).length !== Object.keys(this.sFilters).length) { + filtersChanged = true + } else { + Object.keys(filters).forEach(columnKey => { + if (filters[columnKey] !== this.sFilters[columnKey]) { + filtersChanged = true + } + }) + } + return filtersChanged + }, + + getSortOrderColumns (columns) { + return flatFilter( + columns || this.columns || [], + (column) => 'sortOrder' in column, + ) + }, + + getFilteredValueColumns (columns) { + return flatFilter( + columns || this.columns || [], + (column) => typeof column.filteredValue !== 'undefined', + ) + }, + + getFiltersFromColumns (columns) { + const filters = {} + this.getFilteredValueColumns(columns).forEach((col) => { + const colKey = this.getColumnKey(col) + filters[colKey] = col.filteredValue + }) + return filters + }, + + getDefaultSortOrder (columns) { + const definedSortState = this.getSortStateFromColumns(columns) + + const defaultSortedColumn = flatFilter(columns || [], (column) => column.defaultSortOrder != null)[0] + + if (defaultSortedColumn && !definedSortState.sortColumn) { + return { + sSortColumn: defaultSortedColumn, + sSortOrder: defaultSortedColumn.defaultSortOrder, + } + } + + return definedSortState + }, + + getSortStateFromColumns (columns) { + // return first column which sortOrder is not falsy + const sortedColumn = this.getSortOrderColumns(columns).filter((col) => col.sortOrder)[0] + + if (sortedColumn) { + return { + sSortColumn: sortedColumn, + sSortOrder: sortedColumn.sortOrder, + } + } + + return { + sSortColumn: null, + sSortOrder: null, + } + }, + + getSorterFn () { + const { sSortOrder: sortOrder, sSortColumn: sortColumn } = this + if (!sortOrder || !sortColumn || + typeof sortColumn.sorter !== 'function') { + return + } + + return (a, b) => { + const result = sortColumn.sorter(a, b) + if (result !== 0) { + return (sortOrder === 'descend') ? -result : result + } + return 0 + } + }, + + toggleSortOrder (order, column) { + let { sSortOrder: sortOrder, sSortColumn: sortColumn } = this + // 只同时允许一列进行排序,否则会导致排序顺序的逻辑问题 + const isSortColumn = this.isSortColumn(column) + if (!isSortColumn) { // 当前列未排序 + sortOrder = order + sortColumn = column + } else { // 当前列已排序 + if (sortOrder === order) { // 切换为未排序状态 + sortOrder = '' + sortColumn = null + } else { // 切换为排序状态 + sortOrder = order + } + } + const newState = { + sSortOrder: sortOrder, + sSortColumn: sortColumn, + } + + // Controlled + if (this.getSortOrderColumns().length === 0) { + this.setState(newState) + } + this.$emit('change', this.prepareParamsArguments({ + ...this.$data, + ...newState, + })) + }, + + handleFilter (column, nextFilters) { + const props = this.$props + const pagination = { ...this.sPagination } + const filters = { + ...this.sFilters, + [this.getColumnKey(column)]: nextFilters, + } + // Remove filters not in current columns + const currentColumnKeys = [] + treeMap(this.columns, c => { + if (!c.children) { + currentColumnKeys.push(this.getColumnKey(c)) + } + }) + Object.keys(filters).forEach((columnKey) => { + if (currentColumnKeys.indexOf(columnKey) < 0) { + delete filters[columnKey] + } + }) + + if (props.pagination) { + // Reset current prop + pagination.props = pagination.props || {} + pagination.props.current = 1 + pagination.on.change(pagination.current) + } + + const newState = { + sPagination: pagination, + sFilters: {}, + } + const filtersToSetState = { ...filters } + // Remove filters which is controlled + this.getFilteredValueColumns().forEach((col) => { + const columnKey = this.getColumnKey(col) + if (columnKey) { + delete filtersToSetState[columnKey] + } + }) + if (Object.keys(filtersToSetState).length > 0) { + newState.sFilters = filtersToSetState + } + + // 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, + }, + }) + } + + this.setState(newState, () => { + this.store.setState({ + selectionDirty: false, + }) + this.$emit('change', this.prepareParamsArguments({ + ...this.$data, + selectionDirty: false, + filters, + pagination, + })) + }) + }, + + handleSelect (record, rowIndex, e) { + const checked = e.target.checked + const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() + let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) + const key = this.getRecordKey(record, rowIndex) + if (checked) { + selectedRowKeys.push(this.getRecordKey(record, rowIndex)) + } else { + selectedRowKeys = selectedRowKeys.filter((i) => key !== i) + } + this.store.setState({ + selectionDirty: true, + }) + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: 'onSelect', + record, + checked, + }) + }, + + handleRadioSelect (record, rowIndex, e) { + const checked = e.target.checked + const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() + let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) + const key = this.getRecordKey(record, rowIndex) + selectedRowKeys = [key] + this.store.setState({ + selectionDirty: true, + }) + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: 'onSelect', + record, + checked, + }) + }, + + handleSelectRow (selectionKey, index, onSelectFunc) { + const data = this.getFlatCurrentPageData() + const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() + const selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) + const changeableRowKeys = data + .filter((item, i) => !this.getCheckboxPropsByItem(item, i).disabled) + .map((item, i) => this.getRecordKey(item, i)) + + const changeRowKeys = [] + let selectWay = '' + let checked + // handle default selection + switch (selectionKey) { + case 'all': + changeableRowKeys.forEach(key => { + if (selectedRowKeys.indexOf(key) < 0) { + selectedRowKeys.push(key) + changeRowKeys.push(key) + } + }) + selectWay = 'onSelectAll' + checked = true + break + case 'removeAll': + changeableRowKeys.forEach(key => { + if (selectedRowKeys.indexOf(key) >= 0) { + selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1) + changeRowKeys.push(key) + } + }) + selectWay = 'onSelectAll' + checked = false + break + case 'invert': + changeableRowKeys.forEach(key => { + if (selectedRowKeys.indexOf(key) < 0) { + selectedRowKeys.push(key) + } else { + selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1) + } + changeRowKeys.push(key) + selectWay = 'onSelectInvert' + }) + break + default: + break + } + + this.store.setState({ + selectionDirty: true, + }) + // when select custom selection, callback selections[n].onSelect + const { rowSelection } = this + let customSelectionStartIndex = 2 + if (rowSelection && rowSelection.hideDefaultSelections) { + customSelectionStartIndex = 0 + } + if (index >= customSelectionStartIndex && typeof onSelectFunc === 'function') { + return onSelectFunc(changeableRowKeys) + } + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: selectWay, + checked, + changeRowKeys, + }) + }, + + handlePageChange (current, ...otherArguments) { + const props = this.$props + const pagination = { ...this.sPagination } + if (current) { + pagination.props.current = current + } else { + pagination.props.current = pagination.props.current || 1 + } + pagination.on.change(pagination.props.current, ...otherArguments) + + const newState = { + sPagination: pagination, + } + // 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, + }, + }) + } + this.setState(newState) - this.setState(newState, () => { this.store.setState({ selectionDirty: false, }) this.$emit('change', this.prepareParamsArguments({ ...this.$data, selectionDirty: false, - filters, pagination, })) - }) - }, + }, - handleSelect (record, rowIndex, e) { - const checked = e.target.checked - const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() - let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) - const key = this.getRecordKey(record, rowIndex) - if (checked) { - selectedRowKeys.push(this.getRecordKey(record, rowIndex)) - } else { - selectedRowKeys = selectedRowKeys.filter((i) => key !== i) - } - this.store.setState({ - selectionDirty: true, - }) - this.setSelectedRowKeys(selectedRowKeys, { - selectWay: 'onSelect', - record, - checked, - }) - }, + renderSelectionBox (type) { + return (_, record, index) => { + const rowIndex = this.getRecordKey(record, index) // 从 1 开始 + const props = this.getCheckboxPropsByItem(record, index) + const handleChange = (e) => { + type === 'radio' ? this.handleRadioSelect(record, rowIndex, e) + : this.handleSelect(record, rowIndex, e) + } + const selectionBoxProps = mergeProps({ + props: { + type, + store: this.store, + rowIndex, + defaultSelection: this.getDefaultSelection(), + }, + on: { + change: handleChange, + }, + }, props) - handleRadioSelect (record, rowIndex, e) { - const checked = e.target.checked - const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() - let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) - const key = this.getRecordKey(record, rowIndex) - selectedRowKeys = [key] - this.store.setState({ - selectionDirty: true, - }) - this.setSelectedRowKeys(selectedRowKeys, { - selectWay: 'onSelect', - record, - checked, - }) - }, + return ( + + + + ) + } + }, - handleSelectRow (selectionKey, index, onSelectFunc) { - const data = this.getFlatCurrentPageData() - const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() - const selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) - const changeableRowKeys = data - .filter((item, i) => !this.getCheckboxPropsByItem(item, i).disabled) - .map((item, i) => this.getRecordKey(item, i)) + getRecordKey (record, index) { + const rowKey = this.rowKey + const recordKey = (typeof rowKey === 'function') + ? rowKey(record, index) : record[rowKey] + warning(recordKey !== undefined, + 'Each record in dataSource of table should have a unique `key` prop, or set `rowKey` to an unique primary key,', + ) + return recordKey === undefined ? index : recordKey + }, - const changeRowKeys = [] - let selectWay = '' - let checked - // handle default selection - switch (selectionKey) { - case 'all': - changeableRowKeys.forEach(key => { - if (selectedRowKeys.indexOf(key) < 0) { - selectedRowKeys.push(key) - changeRowKeys.push(key) + getPopupContainer () { + return this.$el + }, + + renderRowSelection (locale) { + const { prefixCls, rowSelection } = this + const columns = this.columns.concat() + if (rowSelection) { + const data = this.getFlatCurrentPageData().filter((item, index) => { + if (rowSelection.getCheckboxProps) { + return !this.getCheckboxPropsByItem(item, index).disabled } + return true }) - selectWay = 'onSelectAll' - checked = true - break - case 'removeAll': - changeableRowKeys.forEach(key => { - if (selectedRowKeys.indexOf(key) >= 0) { - selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1) - changeRowKeys.push(key) + const selectionColumnClass = classNames(`${prefixCls}-selection-column`, { + [`${prefixCls}-selection-column-custom`]: rowSelection.selections, + }) + const selectionColumn = { + key: 'selection-column', + render: this.renderSelectionBox(rowSelection.type), + className: selectionColumnClass, + fixed: rowSelection.fixed, + } + if (rowSelection.type !== 'radio') { + const checkboxAllDisabled = data.every((item, index) => this.getCheckboxPropsByItem(item, index).disabled) + selectionColumn.title = ( + + ) + } + if ('fixed' in rowSelection) { + selectionColumn.fixed = rowSelection.fixed + } else if (columns.some(column => column.fixed === 'left' || column.fixed === true)) { + selectionColumn.fixed = 'left' + } + if (columns[0] && columns[0].key === 'selection-column') { + columns[0] = selectionColumn + } else { + columns.unshift(selectionColumn) + } + } + return columns + }, + + getColumnKey (column, index) { + return column.key || column.dataIndex || index + }, + + getMaxCurrent (total) { + const { current, pageSize } = this.sPagination.props + if ((current - 1) * pageSize >= total) { + return Math.floor((total - 1) / pageSize) + 1 + } + return current + }, + + isSortColumn (column) { + const { sSortColumn: sortColumn } = this + if (!column || !sortColumn) { + return false + } + return this.getColumnKey(sortColumn) === this.getColumnKey(column) + }, + + renderColumnsDropdown (columns, locale) { + const { prefixCls, dropdownPrefixCls } = this + const { sSortOrder: sortOrder } = this + return treeMap(columns, (originColumn, i) => { + const column = { ...originColumn } + const key = this.getColumnKey(column, i) + let filterDropdown + let sortButton + if ((column.filters && column.filters.length > 0) || column.filterDropdown) { + const colFilters = this.sFilters[key] || [] + filterDropdown = ( + + ) + } + if (column.sorter) { + const isSortColumn = this.isSortColumn(column) + if (isSortColumn) { + column.className = classNames(column.className, { + [`${prefixCls}-column-sort`]: sortOrder, + }) } - }) - selectWay = 'onSelectAll' - checked = false - break - case 'invert': - changeableRowKeys.forEach(key => { - if (selectedRowKeys.indexOf(key) < 0) { - selectedRowKeys.push(key) - } else { - selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1) - } - changeRowKeys.push(key) - selectWay = 'onSelectInvert' - }) - break - default: - break - } + const isAscend = isSortColumn && sortOrder === 'ascend' + const isDescend = isSortColumn && sortOrder === 'descend' + sortButton = ( +
+ this.toggleSortOrder('ascend', column)} + > + + + this.toggleSortOrder('descend', column)} + > + + +
+ ) + } + column.title = ( + + {column.title} + {sortButton} + {filterDropdown} + + ) - this.store.setState({ - selectionDirty: true, - }) - // when select custom selection, callback selections[n].onSelect - const { rowSelection } = this - let customSelectionStartIndex = 2 - if (rowSelection && rowSelection.hideDefaultSelections) { - customSelectionStartIndex = 0 - } - if (index >= customSelectionStartIndex && typeof onSelectFunc === 'function') { - return onSelectFunc(changeableRowKeys) - } - this.setSelectedRowKeys(selectedRowKeys, { - selectWay: selectWay, - checked, - changeRowKeys, - }) - }, + if (sortButton || filterDropdown) { + column.className = classNames(`${prefixCls}-column-has-filters`, column.className) + } - handlePageChange (current, ...otherArguments) { - const props = this.$props - const pagination = { ...this.sPagination } - if (current) { - pagination.props.current = current - } else { - pagination.props.current = pagination.props.current || 1 - } - pagination.on.change(pagination.props.current, ...otherArguments) + return column + }) + }, - const newState = { - sPagination: pagination, - } - // 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, { + handleShowSizeChange (current, pageSize) { + const pagination = this.sPagination + pagination.on.showSizeChange(current, pageSize) + const nextPagination = mergeProps(pagination, { props: { - current: this.sPagination.props.current, + pageSize, + current, }, }) - } - this.setState(newState) + this.setState({ sPagination: nextPagination }) + this.$emit('change', this.prepareParamsArguments({ + ...this.$data, + pagination: nextPagination, + })) + }, - this.store.setState({ - selectionDirty: false, - }) - this.$emit('change', this.prepareParamsArguments({ - ...this.$data, - selectionDirty: false, - pagination, - })) - }, - - renderSelectionBox (type) { - return (_, record, index) => { - const rowIndex = this.getRecordKey(record, index) // 从 1 开始 - const props = this.getCheckboxPropsByItem(record, index) - const handleChange = (e) => { - type === 'radio' ? this.handleRadioSelect(record, rowIndex, e) - : this.handleSelect(record, rowIndex, e) + renderPagination () { + // 强制不需要分页 + if (!this.hasPagination()) { + return null } - const selectionBoxProps = mergeProps({ + let size = 'default' + const { sPagination: pagination } = this + if (pagination.props && pagination.props.size) { + size = pagination.props.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 paginationProps = mergeProps({ + key: 'pagination', + class: classNames(cls, `${this.prefixCls}-pagination`), props: { - type, - store: this.store, - rowIndex, - defaultSelection: this.getDefaultSelection(), + ...props, + total, + size, + current: this.getMaxCurrent(total), }, + style, on: { - change: handleChange, + ...on, + change: this.handlePageChange, + showSizeChange: this.handleShowSizeChange, }, - }, props) - - return ( - - - - ) - } - }, - - getRecordKey (record, index) { - const rowKey = this.rowKey - const recordKey = (typeof rowKey === 'function') - ? rowKey(record, index) : record[rowKey] - warning(recordKey !== undefined, - 'Each record in dataSource of table should have a unique `key` prop, or set `rowKey` to an unique primary key,', - ) - return recordKey === undefined ? index : recordKey - }, - - getPopupContainer () { - return this.$el - }, - - renderRowSelection (locale) { - const { prefixCls, rowSelection } = this - const columns = this.columns.concat() - if (rowSelection) { - const data = this.getFlatCurrentPageData().filter((item, index) => { - if (rowSelection.getCheckboxProps) { - return !this.getCheckboxPropsByItem(item, index).disabled - } - return true }) - const selectionColumnClass = classNames(`${prefixCls}-selection-column`, { - [`${prefixCls}-selection-column-custom`]: rowSelection.selections, - }) - const selectionColumn = { - key: 'selection-column', - render: this.renderSelectionBox(rowSelection.type), - className: selectionColumnClass, - fixed: rowSelection.fixed, - } - if (rowSelection.type !== 'radio') { - const checkboxAllDisabled = data.every((item, index) => this.getCheckboxPropsByItem(item, index).disabled) - selectionColumn.title = ( - - ) - } - if ('fixed' in rowSelection) { - selectionColumn.fixed = rowSelection.fixed - } else if (columns.some(column => column.fixed === 'left' || column.fixed === true)) { - selectionColumn.fixed = 'left' - } - if (columns[0] && columns[0].key === 'selection-column') { - columns[0] = selectionColumn - } else { - columns.unshift(selectionColumn) - } - } - return columns - }, + return (total > 0) ? ( + + ) : null + }, - getColumnKey (column, index) { - return column.key || column.dataIndex || index - }, - - getMaxCurrent (total) { - const { current, pageSize } = this.sPagination - if ((current - 1) * pageSize >= total) { - return Math.floor((total - 1) / pageSize) + 1 - } - return current - }, - - isSortColumn (column) { - const { sSortColumn: sortColumn } = this - if (!column || !sortColumn) { - return false - } - return this.getColumnKey(sortColumn) === this.getColumnKey(column) - }, - - renderColumnsDropdown (columns, locale) { - const { prefixCls, dropdownPrefixCls } = this - const { sSortOrder: sortOrder } = this - return treeMap(columns, (originColumn, i) => { - const column = { ...originColumn } - const key = this.getColumnKey(column, i) - let filterDropdown - let sortButton - if ((column.filters && column.filters.length > 0) || column.filterDropdown) { - const colFilters = this.sFilters[key] || [] - filterDropdown = ( - - ) + // Get pagination, filters, sorter + prepareParamsArguments (state) { + const pagination = cloneDeep(state.sPagination) + // remove useless handle function in Table.onChange + if (pagination.on) { + delete pagination.on.change + delete pagination.on.showSizeChange } - if (column.sorter) { - const isSortColumn = this.isSortColumn(column) - if (isSortColumn) { - column.className = classNames(column.className, { - [`${prefixCls}-column-sort`]: sortOrder, - }) + const filters = state.sFilters + const sorter = {} + if (state.sSortColumn && state.sSortOrder) { + sorter.column = state.sSortColumn + sorter.order = state.sSortOrder + sorter.field = state.sSortColumn.dataIndex + sorter.columnKey = this.getColumnKey(state.sSortColumn) + } + return [pagination, filters, sorter] + }, + + findColumn (myKey) { + let column + treeMap(this.columns, c => { + if (this.getColumnKey(c) === myKey) { + column = c } - const isAscend = isSortColumn && sortOrder === 'ascend' - const isDescend = isSortColumn && sortOrder === 'descend' - sortButton = ( -
- this.toggleSortOrder('ascend', column)} - > - - - this.toggleSortOrder('descend', column)} - > - - -
- ) - } - column.title = ( - - {column.title} - {sortButton} - {filterDropdown} - - ) - - if (sortButton || filterDropdown) { - column.className = classNames(`${prefixCls}-column-has-filters`, column.className) - } - + }) return column - }) - }, + }, - handleShowSizeChange (current, pageSize) { - const pagination = this.sPagination - pagination.on.showSizeChange(current, pageSize) - const nextPagination = mergeProps(pagination, { - props: { - pageSize, - current, - }, - }) - this.setState({ sPagination: nextPagination }) - this.$emit('change', this.prepareParamsArguments({ - ...this.$data, - pagination: nextPagination, - })) - }, - - renderPagination () { - // 强制不需要分页 - if (!this.hasPagination()) { - return null - } - let size = 'default' - const { sPagination: pagination } = this - if (pagination.props && pagination.props.size) { - size = pagination.props.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 paginationProps = mergeProps({ - key: 'pagination', - class: classNames(cls, `${this.prefixCls}-pagination`), - props: { - ...props, - total, - size, - current: this.getMaxCurrent(total), - }, - style, - on: { - ...on, - change: this.handlePageChange, - showSizeChange: this.handleShowSizeChange, - }, - }) - return (total > 0) ? ( - - ) : null - }, - - // Get pagination, filters, sorter - prepareParamsArguments (state) { - const pagination = { ...state.sPagination } - // remove useless handle function in Table.onChange - if (pagination.on) { - delete pagination.on.change - delete pagination.on.showSizeChange - } - const filters = state.sFilters - const sorter = {} - if (state.sSortColumn && state.sSortOrder) { - sorter.column = state.sSortColumn - sorter.order = state.sSortOrder - sorter.field = state.sSortColumn.dataIndex - sorter.columnKey = this.getColumnKey(state.sSortColumn) - } - return [pagination, filters, sorter] - }, - - findColumn (myKey) { - let column - treeMap(this.columns, c => { - if (this.getColumnKey(c) === myKey) { - column = c + getCurrentPageData () { + let data = this.getLocalData() + let current + let pageSize + const pagProps = this.sPagination.props || {} + // 如果没有分页的话,默认全部展示 + if (!this.hasPagination()) { + pageSize = Number.MAX_VALUE + current = 1 + } else { + pageSize = pagProps.pageSize + current = this.getMaxCurrent(pagProps.total || data.length) } - }) - return column - }, - getCurrentPageData () { - let data = this.getLocalData() - let current - let pageSize - // 如果没有分页的话,默认全部展示 - if (!this.hasPagination()) { - pageSize = Number.MAX_VALUE - current = 1 - } else { - pageSize = this.sPagination.pageSize - current = this.getMaxCurrent(this.sPagination.total || data.length) - } - - // 分页 - // --- - // 当数据量少于等于每页数量时,直接设置数据 - // 否则进行读取分页数据 - if (data.length > pageSize || pageSize === Number.MAX_VALUE) { - data = data.filter((_, i) => { - return i >= (current - 1) * pageSize && i < current * pageSize - }) - } - return data - }, - - getFlatData () { - return flatArray(this.getLocalData()) - }, - - getFlatCurrentPageData () { - return flatArray(this.getCurrentPageData()) - }, - - recursiveSort (data, sorterFn) { - const { childrenColumnName = 'children' } = this - return data.sort(sorterFn).map((item) => (item[childrenColumnName] ? { - ...item, - [childrenColumnName]: this.recursiveSort(item[childrenColumnName], sorterFn), - } : item)) - }, - - getLocalData () { - const { dataSource, sFilters: filters } = this - let data = dataSource || [] - // 优化本地排序 - data = data.slice(0) - const sorterFn = this.getSorterFn() - if (sorterFn) { - data = this.recursiveSort(data, sorterFn) - } - // 筛选 - if (filters) { - Object.keys(filters).forEach((columnKey) => { - const col = this.findColumn(columnKey) - if (!col) { - return - } - const values = filters[columnKey] || [] - if (values.length === 0) { - return - } - const onFilter = col.onFilter - data = onFilter ? data.filter(record => { - return values.some(v => onFilter(v, record)) - }) : data - }) - } - return data - }, - - createComponents (components, prevComponents) { - const bodyRow = components && components.body && components.body.row - const preBodyRow = prevComponents && prevComponents.body && prevComponents.body.row - if (!this.components || bodyRow !== preBodyRow) { - this.components = { ...components } - this.components.body = { - ...components.body, - row: createBodyRow(bodyRow), + // 分页 + // --- + // 当数据量少于等于每页数量时,直接设置数据 + // 否则进行读取分页数据 + if (data.length > pageSize || pageSize === Number.MAX_VALUE) { + data = data.filter((_, i) => { + return i >= (current - 1) * pageSize && i < current * pageSize + }) } - } - }, + return data + }, - renderTable (contextLocale, loading) { - const locale = { ...contextLocale, ...this.locale } - const { prefixCls, showHeader, ...restProps } = getOptionProps(this) - const data = this.getCurrentPageData() - const expandIconAsCell = this.expandedRowRender && this.expandIconAsCell !== false + getFlatData () { + return flatArray(this.getLocalData()) + }, - const classString = classNames({ - [`${prefixCls}-${this.size}`]: true, - [`${prefixCls}-bordered`]: this.bordered, - [`${prefixCls}-empty`]: !data.length, - [`${prefixCls}-without-column-header`]: !showHeader, - }) + getFlatCurrentPageData () { + return flatArray(this.getCurrentPageData()) + }, - let columns = this.renderRowSelection(locale) - columns = this.renderColumnsDropdown(columns, locale) - columns = columns.map((column, i) => { - const newColumn = { ...column } - newColumn.key = this.getColumnKey(newColumn, i) - return newColumn - }) - let expandIconColumnIndex = (columns[0] && columns[0].key === 'selection-column') ? 1 : 0 - if ('expandIconColumnIndex' in restProps) { - expandIconColumnIndex = restProps.expandIconColumnIndex - } - const vcTableProps = { - key: 'table', - props: { - ...restProps, - customRow: this.customRow, - components: this.components, - prefixCls, - data, - columns, - showHeader, - expandIconColumnIndex, - expandIconAsCell, - emptyText: !(loading.props && loading.spinning) && locale.emptyText, - }, - on: this.$listeners, - class: classString, - } - return ( - - ) + recursiveSort (data, sorterFn) { + const { childrenColumnName = 'children' } = this + return data.sort(sorterFn).map((item) => (item[childrenColumnName] ? { + ...item, + [childrenColumnName]: this.recursiveSort(item[childrenColumnName], sorterFn), + } : item)) + }, + + getLocalData () { + const { dataSource, sFilters: filters } = this + let data = dataSource || [] + // 优化本地排序 + data = data.slice(0) + const sorterFn = this.getSorterFn() + if (sorterFn) { + data = this.recursiveSort(data, sorterFn) + } + // 筛选 + if (filters) { + Object.keys(filters).forEach((columnKey) => { + const col = this.findColumn(columnKey) + if (!col) { + return + } + const values = filters[columnKey] || [] + if (values.length === 0) { + return + } + const onFilter = col.onFilter + data = onFilter ? data.filter(record => { + return values.some(v => onFilter(v, record)) + }) : data + }) + } + return data + }, + + createComponents (components = {}, prevComponents) { + const bodyRow = components && components.body && components.body.row + const preBodyRow = prevComponents && prevComponents.body && prevComponents.body.row + if (!this.customComponents || bodyRow !== preBodyRow) { + this.customComponents = { ...components } + this.customComponents.body = { + ...components.body, + row: createBodyRow(bodyRow), + } + } + }, + + renderTable (contextLocale, loading) { + const locale = { ...contextLocale, ...this.locale } + const { prefixCls, showHeader, ...restProps } = getOptionProps(this) + const data = this.getCurrentPageData() + const expandIconAsCell = this.expandedRowRender && this.expandIconAsCell !== false + + const classString = classNames({ + [`${prefixCls}-${this.size}`]: true, + [`${prefixCls}-bordered`]: this.bordered, + [`${prefixCls}-empty`]: !data.length, + [`${prefixCls}-without-column-header`]: !showHeader, + }) + + let columns = this.renderRowSelection(locale) + columns = this.renderColumnsDropdown(columns, locale) + columns = columns.map((column, i) => { + const newColumn = { ...column } + newColumn.key = this.getColumnKey(newColumn, i) + return newColumn + }) + let expandIconColumnIndex = (columns[0] && columns[0].key === 'selection-column') ? 1 : 0 + if ('expandIconColumnIndex' in restProps) { + expandIconColumnIndex = restProps.expandIconColumnIndex + } + const vcTableProps = { + key: 'table', + props: { + ...restProps, + customRow: this.onRow, + components: this.customComponents, + prefixCls, + data, + columns, + showHeader, + expandIconColumnIndex, + expandIconAsCell, + emptyText: !(loading.props && loading.spinning) && locale.emptyText, + }, + on: this.$listeners, + class: classString, + } + return ( + + ) + }, }, render () { diff --git a/components/table/createBodyRow.jsx b/components/table/createBodyRow.jsx index c3c0dc494..daa106140 100644 --- a/components/table/createBodyRow.jsx +++ b/components/table/createBodyRow.jsx @@ -44,7 +44,7 @@ export default function createTableRow (Component = 'tr') { render () { const className = { - [`${this.props.prefixCls}-row-selected`]: this.selected, + [`${this.prefixCls}-row-selected`]: this.selected, } return ( diff --git a/components/table/demo/basic.md b/components/table/demo/basic.md new file mode 100644 index 000000000..325d0a2df --- /dev/null +++ b/components/table/demo/basic.md @@ -0,0 +1,59 @@ + +#### 基本用法 +简单的表格,最后一列是各种操作。 + + + +#### basic Usage +Simple table with actions. + + +```html + + +``` diff --git a/components/table/filterDropdown.jsx b/components/table/filterDropdown.jsx index cbc15e465..3844f62ad 100755 --- a/components/table/filterDropdown.jsx +++ b/components/table/filterDropdown.jsx @@ -8,7 +8,8 @@ import Checkbox from '../checkbox' import Radio from '../radio' import FilterDropdownMenuWrapper from './FilterDropdownMenuWrapper' import { FilterMenuProps } from './interface' -import { initDefaultProps, cloneElement } from '../_util/props-util' +import { initDefaultProps } from '../_util/props-util' +import { cloneElement } from '../_util/vnode' import BaseMixin from '../_util/BaseMixin' export default { diff --git a/components/table/interface.js b/components/table/interface.js index bea5f786f..55693f9ae 100644 --- a/components/table/interface.js +++ b/components/table/interface.js @@ -81,9 +81,9 @@ export const TableRowSelection = { export const TableProps = { prefixCls: PropTypes.string, dropdownPrefixCls: PropTypes.string, - rowSelection: PropTypes.shape(TableRowSelection).loose, + rowSelection: PropTypes.oneOfType([PropTypes.shape(TableRowSelection).loose, null]), pagination: PropTypes.oneOfType([PropTypes.shape(PaginationProps).loose, PropTypes.bool]), - size: PropTypes.oneOf(['default', 'middle', 'small']), + size: PropTypes.oneOf(['default', 'middle', 'small', 'large']), dataSource: PropTypes.array, components: PropTypes.object, columns: PropTypes.array, @@ -178,6 +178,7 @@ export const FilterMenuProps = { prefixCls: PropTypes.string, dropdownPrefixCls: PropTypes.string, getPopupContainer: PropTypes.func, + handleFilter: PropTypes.func, } // export interface FilterMenuState { diff --git a/examples/routes.js b/examples/routes.js index 9ea148250..674f13d15 100644 --- a/examples/routes.js +++ b/examples/routes.js @@ -3,7 +3,7 @@ const AsyncComp = () => { const hashs = window.location.hash.split('/') const d = hashs[hashs.length - 1] return { - component: import(`../components/vc-table/demo/${d}`), + component: import(`../components/table/demo/${d}`), } } export default [