1100 lines
34 KiB
Vue
Executable File
1100 lines
34 KiB
Vue
Executable File
|
|
import VcTable from '../vc-table'
|
|
import classNames from 'classnames'
|
|
import shallowEqual from 'shallowequal'
|
|
import Pagination from '../pagination'
|
|
import Icon from '../icon'
|
|
import Spin from '../spin'
|
|
import LocaleReceiver from '../locale-provider/LocaleReceiver'
|
|
import defaultLocale from '../locale-provider/default'
|
|
import warning from '../_util/warning'
|
|
import FilterDropdown from './filterDropdown'
|
|
import createStore from './createStore'
|
|
import SelectionBox from './SelectionBox'
|
|
import SelectionCheckboxAll from './SelectionCheckboxAll'
|
|
import Column from './Column'
|
|
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'
|
|
|
|
function noop () {
|
|
}
|
|
|
|
function stopPropagation (e) {
|
|
e.stopPropagation()
|
|
if (e.nativeEvent && e.nativeEvent.stopImmediatePropagation) {
|
|
e.nativeEvent.stopImmediatePropagation()
|
|
}
|
|
}
|
|
|
|
function getRowSelection (props) {
|
|
return props.rowSelection || {}
|
|
}
|
|
|
|
const defaultPagination = {
|
|
onChange: noop,
|
|
onShowSizeChange: noop,
|
|
}
|
|
|
|
/**
|
|
* Avoid creating new object, so that parent component's shouldComponentUpdate
|
|
* can works appropriately。
|
|
*/
|
|
const emptyObject = {}
|
|
|
|
export default {
|
|
name: 'Table',
|
|
Column,
|
|
ColumnGroup,
|
|
mixins: [BaseMixin],
|
|
props: initDefaultProps(TableProps, {
|
|
dataSource: [],
|
|
prefixCls: 'ant-table',
|
|
useFixedHeader: false,
|
|
// rowSelection: null,
|
|
size: 'default',
|
|
loading: false,
|
|
bordered: false,
|
|
indentSize: 20,
|
|
locale: {},
|
|
rowKey: 'key',
|
|
showHeader: true,
|
|
}),
|
|
|
|
// CheckboxPropsCache: {
|
|
// [key: string]: any;
|
|
// };
|
|
// store: Store;
|
|
// columns: ColumnProps<T>[];
|
|
// components: TableComponents;
|
|
|
|
data () {
|
|
// this.columns = props.columns || normalizeColumns(props.children)
|
|
|
|
this.createComponents(this.components)
|
|
this.CheckboxPropsCache = {}
|
|
|
|
this.store = createStore({
|
|
selectedRowKeys: getRowSelection(this.$props).selectedRowKeys || [],
|
|
selectionDirty: false,
|
|
})
|
|
return {
|
|
...this.getDefaultSortOrder(this.columns),
|
|
// 减少状态
|
|
sFilters: this.getFiltersFromColumns(),
|
|
sPagination: this.getDefaultPagination(this.$props),
|
|
pivot: undefined,
|
|
}
|
|
},
|
|
watch: {
|
|
pagination: {
|
|
handler (val) {
|
|
this.setState(previousState => {
|
|
const newPagination = {
|
|
...defaultPagination,
|
|
...previousState.sPagination,
|
|
...val,
|
|
}
|
|
newPagination.current = newPagination.current || 1
|
|
newPagination.pageSize = newPagination.pageSize || 10
|
|
return { sPagination: val !== false ? newPagination : emptyObject }
|
|
})
|
|
},
|
|
deep: true,
|
|
},
|
|
rowSelection: {
|
|
handler (val) {
|
|
if (val &&
|
|
'selectedRowKeys' in val) {
|
|
this.store.setState({
|
|
selectedRowKeys: val.selectedRowKeys || [],
|
|
})
|
|
const { rowSelection } = this
|
|
if (rowSelection && (
|
|
val.getCheckboxProps !== rowSelection.getCheckboxProps
|
|
)) {
|
|
this.CheckboxPropsCache = {}
|
|
}
|
|
}
|
|
},
|
|
deep: true,
|
|
},
|
|
dataSource (val) {
|
|
this.store.setState({
|
|
selectionDirty: false,
|
|
})
|
|
this.CheckboxPropsCache = {}
|
|
},
|
|
columns (val) {
|
|
if (this.getSortOrderColumns(val).length > 0) {
|
|
const sortState = this.getSortStateFromColumns(val)
|
|
if (sortState.sSortColumn !== this.sSortColumn ||
|
|
sortState.sSortOrder !== this.sSortOrder) {
|
|
this.setState(sortState)
|
|
}
|
|
}
|
|
|
|
const filteredValueColumns = this.getFilteredValueColumns(val)
|
|
if (filteredValueColumns.length > 0) {
|
|
const filtersFromColumns = this.getFiltersFromColumns(val)
|
|
const newFilters = { ...this.sFilters }
|
|
Object.keys(filtersFromColumns).forEach(key => {
|
|
newFilters[key] = filtersFromColumns[key]
|
|
})
|
|
if (this.isFiltersChanged(newFilters)) {
|
|
this.setState({ sFilters: newFilters })
|
|
}
|
|
}
|
|
},
|
|
components (val, preVal) {
|
|
this.createComponents(val, preVal)
|
|
},
|
|
},
|
|
methods: {
|
|
getCheckboxPropsByItem (item, index) {
|
|
const rowSelection = getRowSelection(this.$props)
|
|
if (!rowSelection.getCheckboxProps) {
|
|
return { props: {}}
|
|
}
|
|
const key = this.getRecordKey(item, index)
|
|
// Cache checkboxProps
|
|
if (!this.CheckboxPropsCache[key]) {
|
|
this.CheckboxPropsCache[key] = rowSelection.getCheckboxProps(item)
|
|
}
|
|
this.CheckboxPropsCache[key].props = this.CheckboxPropsCache[key].props || {}
|
|
return this.CheckboxPropsCache[key]
|
|
},
|
|
|
|
getDefaultSelection () {
|
|
const rowSelection = getRowSelection(this.$props)
|
|
if (!rowSelection.getCheckboxProps) {
|
|
return []
|
|
}
|
|
return this.getFlatData()
|
|
.filter((item, rowIndex) => this.getCheckboxPropsByItem(item, rowIndex).props.defaultChecked)
|
|
.map((record, rowIndex) => this.getRecordKey(record, rowIndex))
|
|
},
|
|
|
|
getDefaultPagination (props) {
|
|
const pagination = props.pagination || {}
|
|
return this.hasPagination(props)
|
|
? {
|
|
...defaultPagination,
|
|
...pagination,
|
|
current: pagination.defaultCurrent || pagination.current || 1,
|
|
pageSize: pagination.defaultPageSize || pagination.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, selectionInfo) {
|
|
const { selectWay, record, checked, changeRowKeys, nativeEvent } = selectionInfo
|
|
const rowSelection = getRowSelection(this.$props)
|
|
if (rowSelection && !('selectedRowKeys' in rowSelection)) {
|
|
this.store.setState({ selectedRowKeys })
|
|
}
|
|
const data = this.getFlatData()
|
|
if (!rowSelection.onChange && !rowSelection[selectWay]) {
|
|
return
|
|
}
|
|
const selectedRows = data.filter(
|
|
(row, i) => selectedRowKeys.indexOf(this.getRecordKey(row, i)) >= 0,
|
|
)
|
|
if (rowSelection.onChange) {
|
|
rowSelection.onChange(selectedRowKeys, selectedRows)
|
|
}
|
|
if (selectWay === 'onSelect' && rowSelection.onSelect) {
|
|
rowSelection.onSelect(record, checked, selectedRows, nativeEvent)
|
|
} else if (selectWay === 'onSelectMultiple' && rowSelection.onSelectMultiple) {
|
|
const changeRows = data.filter(
|
|
(row, i) => changeRowKeys.indexOf(this.getRecordKey(row, i)) >= 0,
|
|
)
|
|
rowSelection.onSelectMultiple(checked, selectedRows, changeRows)
|
|
} else if (selectWay === 'onSelectAll' && rowSelection.onSelectAll) {
|
|
const changeRows = data.filter(
|
|
(row, i) => changeRowKeys.indexOf(this.getRecordKey(row, i)) >= 0,
|
|
)
|
|
rowSelection.onSelectAll(checked, selectedRows, changeRows)
|
|
} else if (selectWay === 'onSelectInvert' && rowSelection.onSelectInvert) {
|
|
rowSelection.onSelectInvert(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 (state) {
|
|
const { sSortOrder: sortOrder, sSortColumn: sortColumn } = state || this.$data
|
|
if (!sortOrder || !sortColumn ||
|
|
typeof sortColumn.sorter !== 'function') {
|
|
return
|
|
}
|
|
|
|
return (a, b) => {
|
|
const result = sortColumn.sorter(a, b, sortOrder)
|
|
if (result !== 0) {
|
|
return (sortOrder === 'descend') ? -result : result
|
|
}
|
|
return 0
|
|
}
|
|
},
|
|
isSameColumn (a, b) {
|
|
if (a && b && a.key && a.key === b.key) {
|
|
return true
|
|
}
|
|
return a === b || shallowEqual(a, b, (value, other) => {
|
|
if (typeof value === 'function' && typeof other === 'function') {
|
|
return value === other || value.toString() === other.toString()
|
|
}
|
|
})
|
|
},
|
|
|
|
toggleSortOrder (column) {
|
|
if (!column.sorter) {
|
|
return
|
|
}
|
|
const { sSortOrder: sortOrder, sSortColumn: sortColumn } = this
|
|
// 只同时允许一列进行排序,否则会导致排序顺序的逻辑问题
|
|
let newSortOrder
|
|
// 切换另一列时,丢弃 sortOrder 的状态
|
|
const oldSortOrder = this.isSameColumn(sortColumn, column) ? sortOrder : undefined
|
|
// 切换排序状态,按照降序/升序/不排序的顺序
|
|
if (!oldSortOrder) {
|
|
newSortOrder = 'ascend'
|
|
} else if (oldSortOrder === 'ascend') {
|
|
newSortOrder = 'descend'
|
|
} else {
|
|
newSortOrder = undefined
|
|
}
|
|
const newState = {
|
|
sSortOrder: newSortOrder,
|
|
sSortColumn: newSortOrder ? column : null,
|
|
}
|
|
|
|
// 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.current = 1
|
|
pagination.onChange(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' && '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.$data,
|
|
sSelectionDirty: false,
|
|
sFilters: filters,
|
|
sPagination: pagination,
|
|
}))
|
|
})
|
|
},
|
|
|
|
handleSelect (record, rowIndex, e) {
|
|
const checked = e.target.checked
|
|
const nativeEvent = e.nativeEvent
|
|
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection()
|
|
let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection)
|
|
const key = this.getRecordKey(record, rowIndex)
|
|
const { pivot } = this.$data
|
|
const rows = this.getFlatCurrentPageData()
|
|
let realIndex = rowIndex
|
|
if (this.$props.expandedRowRender) {
|
|
realIndex = rows.findIndex(row => this.getRecordKey(row, rowIndex) === key)
|
|
}
|
|
if (nativeEvent.shiftKey && pivot !== undefined && realIndex !== pivot) {
|
|
const changeRowKeys = []
|
|
const direction = Math.sign(pivot - realIndex)
|
|
const dist = Math.abs(pivot - realIndex)
|
|
let step = 0
|
|
while (step <= dist) {
|
|
const i = realIndex + (step * direction)
|
|
step += 1
|
|
const row = rows[i]
|
|
const rowKey = this.getRecordKey(row, i)
|
|
const checkboxProps = this.getCheckboxPropsByItem(row, i)
|
|
if (!checkboxProps.disabled) {
|
|
if (selectedRowKeys.includes(rowKey)) {
|
|
if (!checked) {
|
|
selectedRowKeys = selectedRowKeys.filter((j) => rowKey !== j)
|
|
changeRowKeys.push(rowKey)
|
|
}
|
|
} else if (checked) {
|
|
selectedRowKeys.push(rowKey)
|
|
changeRowKeys.push(rowKey)
|
|
}
|
|
}
|
|
}
|
|
|
|
this.setState({ pivot: realIndex })
|
|
this.store.setState({
|
|
selectionDirty: true,
|
|
})
|
|
this.setSelectedRowKeys(selectedRowKeys, {
|
|
selectWay: 'onSelectMultiple',
|
|
record,
|
|
checked,
|
|
changeRowKeys,
|
|
nativeEvent,
|
|
})
|
|
} else {
|
|
if (checked) {
|
|
selectedRowKeys.push(this.getRecordKey(record, realIndex))
|
|
} else {
|
|
selectedRowKeys = selectedRowKeys.filter((i) => key !== i)
|
|
}
|
|
this.setState({ pivot: realIndex })
|
|
this.store.setState({
|
|
selectionDirty: true,
|
|
})
|
|
this.setSelectedRowKeys(selectedRowKeys, {
|
|
selectWay: 'onSelect',
|
|
record,
|
|
checked,
|
|
changeRowKeys: void (0),
|
|
nativeEvent,
|
|
})
|
|
}
|
|
},
|
|
|
|
handleRadioSelect (record, rowIndex, e) {
|
|
const checked = e.target.checked
|
|
const nativeEvent = e.nativeEvent
|
|
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,
|
|
changeRowKeys: void (0),
|
|
nativeEvent,
|
|
})
|
|
},
|
|
|
|
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).props.disabled)
|
|
.map((item, i) => this.getRecordKey(item, i))
|
|
|
|
const changeRowKeys = []
|
|
let selectWay = 'onSelectAll'
|
|
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,
|
|
checked,
|
|
changeRowKeys,
|
|
})
|
|
},
|
|
|
|
handlePageChange (current, ...otherArguments) {
|
|
const props = this.$props
|
|
const pagination = { ...this.sPagination }
|
|
if (current) {
|
|
pagination.current = current
|
|
} else {
|
|
pagination.current = pagination.current || 1
|
|
}
|
|
pagination.onChange(pagination.current, ...otherArguments)
|
|
|
|
const newState = {
|
|
sPagination: pagination,
|
|
}
|
|
// Controlled current prop will not respond user interaction
|
|
if (props.pagination &&
|
|
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.$data,
|
|
sSelectionDirty: false,
|
|
sPagination: pagination,
|
|
}))
|
|
},
|
|
|
|
renderSelectionBox (type) {
|
|
return (_, record, index) => {
|
|
const rowKey = this.getRecordKey(record, index) // 从 1 开始
|
|
const props = this.getCheckboxPropsByItem(record, index)
|
|
const handleChange = (e) => {
|
|
type === 'radio' ? this.handleRadioSelect(record, index, e)
|
|
: this.handleSelect(record, index, e)
|
|
}
|
|
const selectionBoxProps = mergeProps({
|
|
props: {
|
|
type,
|
|
store: this.store,
|
|
rowIndex: rowKey,
|
|
defaultSelection: this.getDefaultSelection(),
|
|
},
|
|
on: {
|
|
change: handleChange,
|
|
},
|
|
}, props)
|
|
|
|
return (
|
|
<span onClick={stopPropagation}>
|
|
<SelectionBox
|
|
{...selectionBoxProps}
|
|
/>
|
|
</span>
|
|
)
|
|
}
|
|
},
|
|
|
|
getRecordKey (record, index) {
|
|
const { rowKey } = this
|
|
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` of Table 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).props.disabled
|
|
}
|
|
return true
|
|
})
|
|
const selectionColumnClass = classNames(`${prefixCls}-selection-column`, {
|
|
[`${prefixCls}-selection-column-custom`]: rowSelection.selections,
|
|
})
|
|
const selectionColumn = {
|
|
key: 'selection-column',
|
|
customRender: this.renderSelectionBox(rowSelection.type),
|
|
className: selectionColumnClass,
|
|
fixed: rowSelection.fixed,
|
|
width: rowSelection.columnWidth,
|
|
title: rowSelection.columnTitle,
|
|
}
|
|
if (rowSelection.type !== 'radio') {
|
|
const checkboxAllDisabled = data.every((item, index) => this.getCheckboxPropsByItem(item, index).props.disabled)
|
|
selectionColumn.title = selectionColumn.title || (
|
|
<SelectionCheckboxAll
|
|
store={this.store}
|
|
locale={locale}
|
|
data={data}
|
|
getCheckboxPropsByItem={this.getCheckboxPropsByItem}
|
|
getRecordKey={this.getRecordKey}
|
|
disabled={checkboxAllDisabled}
|
|
prefixCls={prefixCls}
|
|
onSelect={this.handleSelectRow}
|
|
selections={rowSelection.selections}
|
|
hideDefaultSelections={rowSelection.hideDefaultSelections}
|
|
getPopupContainer={this.getPopupContainer}
|
|
/>
|
|
)
|
|
}
|
|
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
|
|
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, sFilters: filters } = this
|
|
return treeMap(columns, (column, i) => {
|
|
const key = this.getColumnKey(column, i)
|
|
let filterDropdown
|
|
let sortButton
|
|
const isSortColumn = this.isSortColumn(column)
|
|
if ((column.filters && column.filters.length > 0) || column.filterDropdown) {
|
|
const colFilters = key in filters ? filters[key] : []
|
|
filterDropdown = (
|
|
<FilterDropdown
|
|
_propsSymbol={Symbol()}
|
|
locale={locale}
|
|
column={column}
|
|
selectedKeys={colFilters}
|
|
confirmFilter={this.handleFilter}
|
|
prefixCls={`${prefixCls}-filter`}
|
|
dropdownPrefixCls={dropdownPrefixCls || 'ant-dropdown'}
|
|
getPopupContainer={this.getPopupContainer}
|
|
key='filter-dropdown'
|
|
/>
|
|
)
|
|
}
|
|
if (column.sorter) {
|
|
// const isSortColumn = this.isSortColumn(column)
|
|
// if (isSortColumn) {
|
|
// column.className = classNames(column.className, {
|
|
// [`${prefixCls}-column-sort`]: sortOrder,
|
|
// })
|
|
// }
|
|
const isAscend = isSortColumn && sortOrder === 'ascend'
|
|
const isDescend = isSortColumn && sortOrder === 'descend'
|
|
sortButton = (
|
|
<div class={`${prefixCls}-column-sorter`} key='sorter'>
|
|
<Icon
|
|
class={`${prefixCls}-column-sorter-up ${isAscend ? 'on' : 'off'}`}
|
|
type='caret-up'
|
|
theme='filled'
|
|
/>
|
|
<Icon
|
|
class={`${prefixCls}-column-sorter-down ${isDescend ? 'on' : 'off'}`}
|
|
type='caret-down'
|
|
theme='filled'
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
return {
|
|
...column,
|
|
className: classNames(column.className, {
|
|
[`${prefixCls}-column-has-actions`]: sortButton || filterDropdown,
|
|
[`${prefixCls}-column-has-filters`]: filterDropdown,
|
|
[`${prefixCls}-column-has-sorters`]: sortButton,
|
|
[`${prefixCls}-column-sort`]: isSortColumn && sortOrder,
|
|
}),
|
|
title: [
|
|
<div
|
|
key='title'
|
|
title={sortButton ? locale.sortTitle : undefined}
|
|
class={sortButton ? `${prefixCls}-column-sorters` : undefined}
|
|
onClick={() => this.toggleSortOrder(column)}
|
|
>
|
|
{this.renderColumnTitle(column.title)}
|
|
{sortButton}
|
|
</div>,
|
|
filterDropdown,
|
|
],
|
|
}
|
|
// column.title = (
|
|
// <span key={key}>
|
|
// {column.title}
|
|
// {sortButton}
|
|
// {filterDropdown}
|
|
// </span>
|
|
// )
|
|
|
|
// if (sortButton || filterDropdown) {
|
|
// column.className = classNames(`${prefixCls}-column-has-filters`, column.className)
|
|
// }
|
|
|
|
// return column
|
|
})
|
|
},
|
|
renderColumnTitle (title) {
|
|
const { sFilters: filters, sSortOrder: sortOrder } = this.$data
|
|
// https://github.com/ant-design/ant-design/issues/11246#issuecomment-405009167
|
|
if (title instanceof Function) {
|
|
return title({
|
|
filters,
|
|
sortOrder,
|
|
})
|
|
}
|
|
return title
|
|
},
|
|
handleShowSizeChange (current, pageSize) {
|
|
const pagination = this.sPagination
|
|
pagination.onShowSizeChange(current, pageSize)
|
|
const nextPagination = {
|
|
...pagination,
|
|
pageSize,
|
|
current,
|
|
}
|
|
this.setState({ sPagination: nextPagination })
|
|
this.$emit('change', ...this.prepareParamsArguments({
|
|
...this.$data,
|
|
sPagination: nextPagination,
|
|
}))
|
|
},
|
|
|
|
renderPagination (paginationPosition) {
|
|
// 强制不需要分页
|
|
if (!this.hasPagination()) {
|
|
return null
|
|
}
|
|
let size = 'default'
|
|
const { sPagination: pagination } = this
|
|
if (pagination.size) {
|
|
size = pagination.size
|
|
} else if (this.size === 'middle' || this.size === 'small') {
|
|
size = 'small'
|
|
}
|
|
const position = pagination.position || 'bottom'
|
|
const total = pagination.total || this.getLocalData().length
|
|
const { class: cls, style, onChange, onShowSizeChange, ...restProps } = pagination // eslint-disable-line
|
|
const paginationProps = mergeProps({
|
|
key: `pagination-${paginationPosition}`,
|
|
class: classNames(cls, `${this.prefixCls}-pagination`),
|
|
props: {
|
|
...restProps,
|
|
total,
|
|
size,
|
|
current: this.getMaxCurrent(total),
|
|
},
|
|
style,
|
|
on: {
|
|
change: this.handlePageChange,
|
|
showSizeChange: this.handleShowSizeChange,
|
|
},
|
|
})
|
|
return (total > 0 && (position === paginationPosition || position === 'both')) ? (
|
|
<Pagination
|
|
{...paginationProps}
|
|
/>
|
|
) : null
|
|
},
|
|
|
|
// Get pagination, filters, sorter
|
|
prepareParamsArguments (state) {
|
|
const pagination = { ...state.sPagination }
|
|
// remove useless handle function in Table.onChange
|
|
delete pagination.onChange
|
|
delete pagination.onShowSizeChange
|
|
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)
|
|
}
|
|
const extra = {
|
|
currentDataSource: this.getLocalData(state),
|
|
}
|
|
|
|
return [pagination, filters, sorter, extra]
|
|
},
|
|
|
|
findColumn (myKey) {
|
|
let column
|
|
treeMap(this.columns, c => {
|
|
if (this.getColumnKey(c) === myKey) {
|
|
column = c
|
|
}
|
|
})
|
|
return column
|
|
},
|
|
|
|
getCurrentPageData () {
|
|
let data = this.getLocalData()
|
|
let current
|
|
let pageSize
|
|
const sPagination = this.sPagination
|
|
// 如果没有分页的话,默认全部展示
|
|
if (!this.hasPagination()) {
|
|
pageSize = Number.MAX_VALUE
|
|
current = 1
|
|
} else {
|
|
pageSize = sPagination.pageSize
|
|
current = this.getMaxCurrent(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 (state) {
|
|
const currentState = state || this.$data
|
|
const { sFilters: filters } = currentState
|
|
const { dataSource } = this.$props
|
|
let data = dataSource || []
|
|
// 优化本地排序
|
|
data = data.slice(0)
|
|
const sorterFn = this.getSorterFn(currentState)
|
|
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.row || bodyRow !== preBodyRow) {
|
|
this.row = createBodyRow(bodyRow)
|
|
}
|
|
this.customComponents = {
|
|
...components,
|
|
body: {
|
|
...components.body,
|
|
row: this.row,
|
|
},
|
|
}
|
|
},
|
|
|
|
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.props.spinning) && locale.emptyText,
|
|
},
|
|
on: this.$listeners,
|
|
class: classString,
|
|
}
|
|
return (
|
|
<VcTable
|
|
{...vcTableProps}
|
|
/>
|
|
)
|
|
},
|
|
},
|
|
|
|
render () {
|
|
const { prefixCls } = this
|
|
const data = this.getCurrentPageData()
|
|
|
|
let loading = this.loading
|
|
if (typeof loading === 'boolean') {
|
|
loading = {
|
|
props: {
|
|
spinning: loading,
|
|
},
|
|
}
|
|
} else {
|
|
loading = {
|
|
props: { ...loading },
|
|
}
|
|
}
|
|
|
|
const table = (
|
|
<LocaleReceiver
|
|
componentName='Table'
|
|
defaultLocale={defaultLocale.Table}
|
|
children={(locale) => this.renderTable(locale, loading)}
|
|
/>
|
|
)
|
|
|
|
// if there is no pagination or no data,
|
|
// the height of spin should decrease by half of pagination
|
|
const paginationPatchClass = (this.hasPagination() && data && data.length !== 0)
|
|
? `${prefixCls}-with-pagination` : `${prefixCls}-without-pagination`
|
|
const spinProps = {
|
|
...loading,
|
|
class: loading.props && loading.props.spinning ? `${paginationPatchClass} ${prefixCls}-spin-holder` : '',
|
|
}
|
|
return (
|
|
<div
|
|
class={classNames(`${prefixCls}-wrapper`)}
|
|
>
|
|
<Spin
|
|
{...spinProps}
|
|
>
|
|
{this.renderPagination('top')}
|
|
{table}
|
|
{this.renderPagination('bottom')}
|
|
</Spin>
|
|
</div>
|
|
)
|
|
},
|
|
}
|