feat: update table

pull/802/head
wangxueliang 6 years ago
parent e411d42d47
commit 3e704f9e2f

@ -24,6 +24,7 @@ import {
getAllProps,
} from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin';
import { ConfigConsumerProps } from '../config-provider';
import { TableProps } from './interface';
function noop() {}
@ -44,6 +45,8 @@ const defaultPagination = {
onShowSizeChange: noop,
};
const ROW_SELECTION_COLUMN_WIDTH = '62px';
/**
* Avoid creating new object, so that parent component's shouldComponentUpdate
* can works appropriately
@ -57,7 +60,6 @@ export default {
mixins: [BaseMixin],
props: initDefaultProps(TableProps, {
dataSource: [],
prefixCls: 'ant-table',
useFixedHeader: false,
// rowSelection: null,
size: 'default',
@ -67,8 +69,12 @@ export default {
locale: {},
rowKey: 'key',
showHeader: true,
sortDirections: ['ascend', 'descend'],
}),
inject: {
configProvider: { default: () => ({}) },
},
// CheckboxPropsCache: {
// [key: string]: any;
// };
@ -86,6 +92,7 @@ export default {
selectedRowKeys: getRowSelection(this.$props).selectedRowKeys || [],
selectionDirty: false,
});
this.prevRowSelection = this.rowSelection ? {...this.rowSelection} : this.rowSelection;
return {
...this.getDefaultSortOrder(this.columns),
//
@ -120,7 +127,12 @@ export default {
if (rowSelection && val.getCheckboxProps !== rowSelection.getCheckboxProps) {
this.CheckboxPropsCache = {};
}
} else if(val && !this.prevRowSelection) {
this.store.setState({
selectedRowKeys: [],
});
}
this.prevRowSelection = val ? {...val} : val;
},
deep: true,
},
@ -185,19 +197,31 @@ export default {
},
getDefaultPagination(props) {
const pagination = props.pagination || {};
const pagination = typeof props.pagination === 'object' ? props.pagination : {};
let current;
if ('current' in pagination) {
current = pagination.current;
} else if ('defaultCurrent' in pagination) {
current = pagination.defaultCurrent;
}
let pageSize;
if ('pageSize' in pagination) {
pageSize = pagination.pageSize;
} else if ('defaultPageSize' in pagination) {
pageSize = pagination.defaultPageSize;
}
return this.hasPagination(props)
? {
...defaultPagination,
...pagination,
current: pagination.defaultCurrent || pagination.current || 1,
pageSize: pagination.defaultPageSize || pagination.pageSize || 10,
current: current || 1,
pageSize: pageSize || 10,
}
: {};
},
onRow(record, index) {
const { prefixCls, customRow } = this;
onRow(prefixCls, record, index) {
const { customRow } = this;
const custom = customRow ? customRow(record, index) : {};
return mergeProps(custom, {
props: {
@ -346,18 +370,18 @@ export default {
if (!column.sorter) {
return;
}
const sortDirections = column.sortDirections || this.sortDirections;
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';
if (this.isSameColumn(sortColumn, column) && sortOrder !== undefined) {
// sortDirections
const methodIndex = sortDirections.indexOf(sortOrder) + 1;
newSortOrder =
methodIndex === sortDirections.length ? undefined : sortDirections[methodIndex];
} else {
newSortOrder = undefined;
newSortOrder = sortDirections[0];
}
const newState = {
sSortOrder: newSortOrder,
@ -683,8 +707,15 @@ export default {
return this.$el;
},
renderRowSelection(locale) {
const { prefixCls, rowSelection, childrenColumnName } = this;
generatePopupContainerFunc() {
const { scroll } = this.$props;
// Use undefined to let rc component use default logic.
return scroll ? this.getPopupContainer : undefined;
},
renderRowSelection(prefixCls, locale) {
const { rowSelection, childrenColumnName } = this;
const columns = this.columns.concat();
if (rowSelection) {
const data = this.getFlatCurrentPageData(childrenColumnName).filter((item, index) => {
@ -701,7 +732,7 @@ export default {
customRender: this.renderSelectionBox(rowSelection.type),
className: selectionColumnClass,
fixed: rowSelection.fixed,
width: rowSelection.columnWidth,
width: rowSelection.columnWidth || ROW_SELECTION_COLUMN_WIDTH,
title: rowSelection.columnTitle,
};
if (rowSelection.type !== 'radio') {
@ -720,7 +751,7 @@ export default {
onSelect={this.handleSelectRow}
selections={rowSelection.selections}
hideDefaultSelections={rowSelection.hideDefaultSelections}
getPopupContainer={this.getPopupContainer}
getPopupContainer={this.generatePopupContainerFunc()}
/>
);
}
@ -758,15 +789,14 @@ export default {
return this.getColumnKey(sortColumn) === this.getColumnKey(column);
},
renderColumnsDropdown(columns, locale) {
const { prefixCls, dropdownPrefixCls } = this;
renderColumnsDropdown(prefixCls, dropdownPrefixCls, columns, locale) {
const { sSortOrder: sortOrder, sFilters: filters } = this;
return treeMap(columns, (column, i) => {
const key = this.getColumnKey(column, i);
let filterDropdown;
let sortButton;
let customHeaderCell = column.customHeaderCell;
const sortTitle = this.getColumnTitle(column.title, {}) || locale.sortTitle;
// const sortTitle = this.getColumnTitle(column.title, {}) || locale.sortTitle;
const isSortColumn = this.isSortColumn(column);
if ((column.filters && column.filters.length > 0) || column.filterDropdown) {
const colFilters = key in filters ? filters[key] : [];
@ -779,26 +809,34 @@ export default {
confirmFilter={this.handleFilter}
prefixCls={`${prefixCls}-filter`}
dropdownPrefixCls={dropdownPrefixCls || 'ant-dropdown'}
getPopupContainer={this.getPopupContainer}
getPopupContainer={this.generatePopupContainerFunc()}
key="filter-dropdown"
/>
);
}
if (column.sorter) {
const sortDirections = column.sortDirections || this.sortDirections;
const isAscend = isSortColumn && sortOrder === 'ascend';
const isDescend = isSortColumn && sortOrder === 'descend';
const ascend = sortDirections.indexOf('ascend') !== -1 && (
<Icon
class={`${prefixCls}-column-sorter-up ${isAscend ? 'on' : 'off'}`}
type="caret-up"
theme="filled"
/>
);
const descend = sortDirections.indexOf('descend') !== -1 && (
<Icon
class={`${prefixCls}-column-sorter-down ${isDescend ? 'on' : 'off'}`}
type="caret-down"
theme="filled"
/>
);
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 title={locale.sortTitle} class={`${prefixCls}-column-sorter`} key="sorter">
{ascend}
{descend}
</div>
);
customHeaderCell = col => {
@ -821,7 +859,7 @@ export default {
return colProps;
};
}
const sortTitleString = sortButton && typeof sortTitle === 'string' ? sortTitle : undefined;
// const sortTitleString = sortButton && typeof sortTitle === 'string' ? sortTitle : undefined;
return {
...column,
className: classNames(column.className, {
@ -833,7 +871,6 @@ export default {
title: [
<div
key="title"
title={sortTitleString}
class={sortButton ? `${prefixCls}-column-sorters` : undefined}
>
{this.renderColumnTitle(column.title)}
@ -857,33 +894,33 @@ export default {
return title;
},
getColumnTitle(title, parentNode) {
if (!title) {
return;
}
if (isValidElement(title)) {
const props = title.componentOptions;
let children = null;
if (props && props.children) {
// for component
children = filterEmpty(props.children);
} else if (title.children) {
// for dom
children = filterEmpty(title.children);
}
if (children && children.length === 1) {
children = children[0];
const attrs = getAllProps(title);
if (!children.tag && children.text) {
// for textNode
children = children.text;
}
return this.getColumnTitle(children, attrs);
}
} else {
return parentNode.title || title;
}
},
// getColumnTitle(title, parentNode) {
// if (!title) {
// return;
// }
// if (isValidElement(title)) {
// const props = title.componentOptions;
// let children = null;
// if (props && props.children) {
// // for component
// children = filterEmpty(props.children);
// } else if (title.children) {
// // for dom
// children = filterEmpty(title.children);
// }
// if (children && children.length === 1) {
// children = children[0];
// const attrs = getAllProps(title);
// if (!children.tag && children.text) {
// // for textNode
// children = children.text;
// }
// return this.getColumnTitle(children, attrs);
// }
// } else {
// return parentNode.title || title;
// }
// },
handleShowSizeChange(current, pageSize) {
const pagination = this.sPagination;
@ -903,7 +940,7 @@ export default {
);
},
renderPagination(paginationPosition) {
renderPagination(prefixCls, paginationPosition) {
//
if (!this.hasPagination()) {
return null;
@ -920,7 +957,7 @@ export default {
const { class: cls, style, onChange, onShowSizeChange, ...restProps } = pagination; // eslint-disable-line
const paginationProps = mergeProps({
key: `pagination-${paginationPosition}`,
class: classNames(cls, `${this.prefixCls}-pagination`),
class: classNames(cls, `${prefixCls}-pagination`),
props: {
...restProps,
total,
@ -1063,12 +1100,17 @@ export default {
};
},
renderTable(contextLocale, loading) {
renderTable(prefixCls, renderEmpty, dropdownPrefixCls, contextLocale, loading) {
const locale = { ...contextLocale, ...this.locale };
const { prefixCls, showHeader, ...restProps } = getOptionProps(this);
const { showHeader, ...restProps } = getOptionProps(this);
const data = this.getCurrentPageData();
const expandIconAsCell = this.expandedRowRender && this.expandIconAsCell !== false;
const mergedLocale = { ...contextLocale, ...locale };
if (!locale || !locale.emptyText) {
mergedLocale.emptyText = renderEmpty(h, 'Table');
}
const classString = classNames({
[`${prefixCls}-${this.size}`]: true,
[`${prefixCls}-bordered`]: this.bordered,
@ -1076,8 +1118,8 @@ export default {
[`${prefixCls}-without-column-header`]: !showHeader,
});
let columns = this.renderRowSelection(locale);
columns = this.renderColumnsDropdown(columns, locale);
let columns = this.renderRowSelection(prefixCls, mergedLocale);
columns = this.renderColumnsDropdown(prefixCls, dropdownPrefixCls, columns, mergedLocale);
columns = columns.map((column, i) => {
const newColumn = { ...column };
newColumn.key = this.getColumnKey(newColumn, i);
@ -1091,7 +1133,7 @@ export default {
key: 'table',
props: {
...restProps,
customRow: this.onRow,
customRow: (record, index) => this.onRow(prefixCls, record, index),
components: this.customComponents,
prefixCls,
data,
@ -1099,7 +1141,7 @@ export default {
showHeader,
expandIconColumnIndex,
expandIconAsCell,
emptyText: !(loading.props && loading.props.spinning) && locale.emptyText,
emptyText: !(loading.props && loading.props.spinning) && mergedLocale.emptyText,
},
on: this.$listeners,
class: classString,
@ -1109,7 +1151,10 @@ export default {
},
render() {
const { prefixCls } = this;
const {
prefixCls: customizePrefixCls,
dropdownPrefixCls: customizeDropdownPrefixCls,
} = this;
const data = this.getCurrentPageData();
let loading = this.loading;
@ -1124,12 +1169,20 @@ export default {
props: { ...loading },
};
}
const getPrefixCls = this.configProvider.getPrefixCls || ConfigConsumerProps.getPrefixCls;
const renderEmpty = (
this.configProvider.renderEmpty &&
this.configProvider.renderEmpty()
) || ConfigConsumerProps.renderEmpty;
const prefixCls = getPrefixCls('table', customizePrefixCls);
const dropdownPrefixCls = getPrefixCls('dropdown', customizeDropdownPrefixCls);
const table = (
<LocaleReceiver
componentName="Table"
defaultLocale={defaultLocale.Table}
children={locale => this.renderTable(locale, loading)}
children={locale => this.renderTable(prefixCls, renderEmpty, dropdownPrefixCls, locale, loading)}
/>
);
@ -1149,9 +1202,9 @@ export default {
return (
<div class={classNames(`${prefixCls}-wrapper`)}>
<Spin {...spinProps}>
{this.renderPagination('top')}
{this.renderPagination(prefixCls, 'top')}
{table}
{this.renderPagination('bottom')}
{this.renderPagination(prefixCls, 'bottom')}
</Spin>
</div>
);

@ -14,7 +14,13 @@ Simple table with actions.
<a slot="name" slot-scope="text" href="javascript:;">{{text}}</a>
<span slot="customTitle"><a-icon type="smile-o" /> Name</span>
<span slot="tags" slot-scope="tags">
<a-tag v-for="tag in tags" color="blue" :key="tag">{{tag}}</a-tag>
<a-tag
v-for="tag in tags"
:color="tag==='loser' ? 'volcano' : (tag.length > 5 ? 'geekblue' : 'green')"
:key="tag"
>
{{tag.toUpperCase()}}
</a-tag>
</span>
<span slot="action" slot-scope="text, record">
<a href="javascript:;">Invite 一 {{record.name}}</a>

@ -11,7 +11,7 @@ Implement a customized column search example via `filterDropdown`.
```html
<template>
<a-table :dataSource="data" :columns="columns">
<div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" class='custom-filter-dropdown'>
<div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" style="padding: 8px">
<a-input
v-ant-ref="c => searchInput = c"
:placeholder="`Search ${column.dataIndex}`"
@ -143,12 +143,6 @@ export default {
}
</script>
<style scoped>
.custom-filter-dropdown {
padding: 8px;
border-radius: 4px;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
}
.highlight {
background-color: rgb(255, 192, 105);

@ -1,14 +1,14 @@
<cn>
#### 固定头和列
适合同时展示有大量数据和数据列。
> 若列头与内容不对齐或出现列重复,请指定列的宽度 `width`
> 若列头与内容不对齐或出现列重复,请指定**固定**的宽度 `width`
> 建议指定 `scroll.x` 为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过 `scroll.x`
</cn>
<us>
#### Fixed Columns and Header
A Solution for displaying large amounts of data with long columns.
> Specify the width of columns if header and cell do not align properly.
> Specify the width of columns if header and cell do not align properly. (Leave one column at least without width to fit fluid layout)
> A fixed value which is greater than table width for `scroll.x` is recommended. The sum of unfixed columns should not greater than `scroll.x`.
</us>

@ -1,14 +1,14 @@
<cn>
#### 固定列
对于列数很多的数据,可以固定前后的列,横向滚动查看其它数据,需要和 `scroll.x` 配合使用。
> 若列头与内容不对齐或出现列重复,请指定列的宽度 `width`
> 若列头与内容不对齐或出现列重复,请指定**固定**的宽度 `width`
> 建议指定 `scroll.x` 为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过 `scroll.x`
</cn>
<us>
#### Fixed Columns
To fix some columns and scroll inside other columns, and you must set `scroll.x` meanwhile.
> Specify the width of columns if header and cell do not align properly.
> Specify the width of columns if header and cell do not align properly.(Leave one column at least without width to fit fluid layout)
> A fixed value which is greater than table width for `scroll.x` is recommended. The sum of unfixed columns should not greater than `scroll.x`.
</us>

@ -1,13 +1,13 @@
<cn>
#### 固定表头
方便一页内展示大量数据。
需要指定 column 的 `width` 属性,否则列头和内容可能不对齐。
需要指定 column 的 `width` 属性,否则列头和内容可能不对齐。(建议留一列不设宽度以适应弹性布局)
</cn>
<us>
#### Fixed Header
Display large amounts of data in scrollable view.
> Specify the width of each column if header and cell do not align properly.
> Specify width of columns if header and cell do not align properly.(Leave one column at least without width to fit fluid layout)
</us>
```html

@ -2,6 +2,7 @@
#### 筛选和排序
对某一列数据进行筛选,使用列的 `filters` 属性来指定需要筛选菜单的列,`onFilter` 用于筛选当前数据,`filterMultiple` 用于指定多选和单选。
对某一列数据进行排序,通过指定列的 `sorter` 函数即可启动排序按钮。`sorter: function(rowA, rowB) { ... }` rowA、rowB 为比较的两个行数据。
`sortDirections: ['ascend' | 'descend']`改变每列可用的排序方式切换排序时按数组内容依次切换设置在table props上时对所有列生效。
使用 `defaultSortOrder` 属性,设置列的默认排序顺序。
</cn>
@ -9,6 +10,7 @@
#### Filter and sorter
Use `filters` to generate filter menu in columns, `onFilter` to determine filtered result, and `filterMultiple` to indicate whether it's multiple or single selection.
Use `sorter` to make a column sortable. `sorter` can be a function of the type `function(a, b) { ... }` for sorting data locally.
`sortDirections: ['ascend' | 'descend']` defines available sort methods for each columns, effective for all columns when set on table props.
Uses `defaultSortOrder` to make a column sorted by default.
If a `sortOrder` or `defaultSortOrder` is specified with the value `ascend` or `descend`, you can access this value from within the function passed to the `sorter` as explained above. Such a function can take the form: `function(a, b, sortOrder) { ... }`.
</us>
@ -42,6 +44,7 @@ const columns = [{
// here is that finding the name started with `value`
onFilter: (value, record) => record.name.indexOf(value) === 0,
sorter: (a, b) => a.name.length - b.name.length,
sortDirections: ['descend'],
}, {
title: 'Age',
dataIndex: 'age',
@ -59,6 +62,7 @@ const columns = [{
filterMultiple: false,
onFilter: (value, record) => record.address.indexOf(value) === 0,
sorter: (a, b) => a.address.length - b.address.length,
sortDirections: ['descend', 'ascend'],
}];
const data = [{

@ -62,27 +62,8 @@ export default {
return (
<div>
<md cn={md.cn} us={md.us}/>
<Ajax />
<Basic />
<Bordered />
<ColspanRowspan />
<CustomFilterPanel />
<EditCell />
<EditRow />
<ExpandChildren />
<Expand />
<FixedColumnsHeader />
<FixedColumns />
<FixedHeader />
<GroupingColumns />
<Head />
<NestedTable />
<ResetFilter />
<RowSelectionAndOperation />
<RowSelectionCustom />
<RowSelection />
<Size />
<Template />
<api>
<template slot='cn'>
<CN/>

@ -261,7 +261,7 @@ export default {
}
const menus = filterDropdown ? (
<FilterDropdownMenuWrapper>{filterDropdown}</FilterDropdownMenuWrapper>
<FilterDropdownMenuWrapper class={`${prefixCls}-dropdown`}>{filterDropdown}</FilterDropdownMenuWrapper>
) : (
<FilterDropdownMenuWrapper class={`${prefixCls}-dropdown`}>
<Menu

@ -50,7 +50,7 @@ const columns = [{
| indentSize | Indent size in pixels of tree data | number | 15 |
| loading | Loading status of table | boolean\|[object](/components/spin) | `false` |
| locale | i18n text including filter, sort, empty text, etc | object | filterConfirm: 'Ok' <br> filterReset: 'Reset' <br> emptyText: 'No Data' |
| pagination | Pagination [config](#pagination) or [`Pagination`] (/components/pagination/), hide it by setting it to `false` | object | |
| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object | |
| rowClassName | Row's className | Function(record, index):string | - |
| rowKey | Row's unique key, could be a string or function that returns a string | string\|Function(record):string | `key` |
| rowSelection | Row selection [config](#rowSelection) | object | null |
@ -81,9 +81,11 @@ Follow [Vue jsx](https://github.com/vuejs/babel-plugin-transform-vue-jsx) syntax
xxx...
},
on: {
click: () => {}, // click row
mouseenter: () => {}, // mouse enter row
xxxx...
click: (event) => {}, // click row
doubleclick: (event) => {}, // double click row
contextmenu: (event) => {} // right button click row
mouseenter: (event) => {} // mouse enter row
mouseleave: (event) => {} // mouse leave row
},
};
)}

@ -50,13 +50,13 @@ const columns = [{
| indentSize | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | 15 |
| loading | 页面是否加载中 | boolean\|[object](/components/spin-cn) | false |
| locale | 默认文案设置,目前包括排序、过滤、空数据文案 | object | filterConfirm: '确定' <br> filterReset: '重置' <br> emptyText: '暂无数据' |
| pagination | 分页器,参考[配置项](#pagination)或 [pagination](/components/pagination-cn/),设为 false 时不展示和进行分页 | object | |
| pagination | 分页器,参考[配置项](#pagination)或 [pagination](/components/pagination-cn/)文档,设为 false 时不展示和进行分页 | object | |
| rowClassName | 表格行的类名 | Function(record, index):string | - |
| rowKey | 表格行 key 的取值,可以是字符串或一个函数 | string\|Function(record):string | 'key' |
| rowSelection | 列表项是否可选择,[配置项](#rowSelection) | object | null |
| scroll | 设置横向或纵向滚动,也可用于指定滚动区域的宽和高,建议为 `x` 设置一个数字,如果要设置为 `true`,需要配合样式 `.ant-table td { white-space: nowrap; }` | { x: number \| true, y: number } | - |
| showHeader | 是否显示表头 | boolean | true |
| size | 正常或迷你类型,`default` or `small` | string | default |
| size | 表格大小 | default \| middle \| small | default |
| title | 表格标题 | Function(currentPageData)\|slot-scope | |
| customHeaderRow | 设置头部行属性 | Function(column, index) | - |
| customRow | 设置行属性 | Function(record, index) | - |
@ -82,9 +82,11 @@ const columns = [{
xxx... //属性
},
on: { // 事件
click: () => {}, // 点击行
mouseenter: () => {}, // 鼠标移入行
xxxx...
click: (event) => {}, // 点击行
doubleclick: (event) => {},
contextmenu: (event) => {},
mouseenter: (event) => {}, // 鼠标移入行
mouseleave: (event) => {}
},
};

@ -35,7 +35,8 @@ export const ColumnProps = {
fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['left', 'right'])]),
filterIcon: PropTypes.any,
filteredValue: PropTypes.array,
sortOrder: PropTypes.oneOf(['ascend', 'descend']),
sortOrder: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['ascend', 'descend'])]),
sortDirections: PropTypes.array,
// children?: ColumnProps<T>[];
// onCellClick?: (record: T, event: any) => void;
// onCell?: (record: T) => any;
@ -126,6 +127,7 @@ export const TableProps = {
scroll: PropTypes.object,
childrenColumnName: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
bodyStyle: PropTypes.any,
sortDirections: PropTypes.array,
// className?: PropTypes.string,
// style?: React.CSSProperties;
// children?: React.ReactNode;

Loading…
Cancel
Save