Table: support sort-by

This commit is contained in:
wangfengming 2017-10-30 10:26:25 +08:00 committed by 杨奕
parent 6619daf1ab
commit 7074a247f4
7 changed files with 109 additions and 23 deletions

View File

@ -1263,7 +1263,7 @@ You can also select multiple rows.
Sort the data to find or compare data quickly.
:::demo Set attribute `sortable` in a certain column to sort the data based on this column. It accepts `Boolean` with a default value `false`. Set table attribute `default-sort` to determine default sort column and order. To apply your own sorting rules, use `sort-method`. If you need remote sorting from backend, set `sortable` to `custom`, and listen to the `sort-change` event on Table. In the event handler, you have access to the sorting column and sorting order so that you can fetch sorted table data from API. In this example we use another attribute named `formatter` to format the value of certain columns. It accepts a function which has two parameters: `row` and `column`. You can handle it according to your own needs.
:::demo Set attribute `sortable` in a certain column to sort the data based on this column. It accepts `Boolean` with a default value `false`. Set table attribute `default-sort` to determine default sort column and order. To apply your own sorting rules, use `sort-method` or `sort-by`. If you need remote sorting from backend, set `sortable` to `custom`, and listen to the `sort-change` event on Table. In the event handler, you have access to the sorting column and sorting order so that you can fetch sorted table data from API. In this example we use another attribute named `formatter` to format the value of certain columns. It accepts a function which has two parameters: `row` and `column`. You can handle it according to your own needs.
```html
<template>
<el-table
@ -2010,6 +2010,7 @@ You can customize row index in `type=index` columns.
| render-header | render function for table header of this column | Function(h, { column, $index }) | — | — |
| sortable | whether column can be sorted. Remote sorting can be done by setting this attribute to 'custom' and listening to the `sort-change` event of Table | boolean, string | true, false, custom | false |
| sort-method | sorting method, works when `sortable` is `true`. Should return a number, just like Array.sort | Function(a, b) | — | — |
| sort-by | sorting by method or property, works when `sortable` is `true` and `sort-method` is `undefined`. if `sort-by` is Array, will sort by the first, if the first is equal, will sort by the second, and so on.| Function(row, index)/String/Array | — | — |
| resizable | whether column width can be resized, works when `border` of `el-table` is `true` | boolean | — | false |
| formatter | function that formats cell content | Function(row, column, cellValue) | — | — |
| show-overflow-tooltip | whether to hide extra content and show them in a tooltip when hovering on the cell | boolean | — | false |

View File

@ -1303,7 +1303,7 @@
对表格进行排序,可快速查找或对比数据。
:::demo 在列中设置`sortable`属性即可实现以该列为基准的排序,接受一个`Boolean`,默认为`false`。可以通过 Table 的`default-sort`属性设置默认的排序列和排序顺序。可以使用`sort-method`使用自定义的排序规则。如果需要后端排序,需将`sortable`设置为`custom`,同时在 Table 上监听`sort-change`事件,在事件回调中可以获取当前排序的字段名和排序顺序,从而向接口请求排序后的表格数据。在本例中,我们还使用了`formatter`属性,它用于格式化指定列的值,接受一个`Function`,会传入两个参数:`row`和`column`,可以根据自己的需求进行处理。
:::demo 在列中设置`sortable`属性即可实现以该列为基准的排序,接受一个`Boolean`,默认为`false`。可以通过 Table 的`default-sort`属性设置默认的排序列和排序顺序。可以使用`sort-method`或者`sort-by`使用自定义的排序规则。如果需要后端排序,需将`sortable`设置为`custom`,同时在 Table 上监听`sort-change`事件,在事件回调中可以获取当前排序的字段名和排序顺序,从而向接口请求排序后的表格数据。在本例中,我们还使用了`formatter`属性,它用于格式化指定列的值,接受一个`Function`,会传入两个参数:`row`和`column`,可以根据自己的需求进行处理。
```html
<template>
<el-table
@ -2073,6 +2073,7 @@
| render-header | 列标题 Label 区域渲染使用的 Function | Function(h, { column, $index }) | — | — |
| sortable | 对应列是否可以排序,如果设置为 'custom',则代表用户希望远程排序,需要监听 Table 的 sort-change 事件 | boolean, string | true, false, 'custom' | false |
| sort-method | 对数据进行排序的时候使用的方法,仅当 sortable 设置为 true 的时候有效,需返回一个数字,和 Array.sort 表现一致 | Function(a, b) | — | — |
| sort-by | 对数据进行排序的时候按照 sort-by 排序,仅当 sortable 设置为 true 且没有设置 sort-method 的时候有效。如果 sort-by 为数组,则先按照第 0 个排序,如果第 0 个相等,再按照第 1 个排序,以此类推。 | Function(row, index)/String/Array | — | — |
| resizable | 对应列是否可以通过拖动改变宽度(需要在 el-table 上设置 border 属性为真) | boolean | — | true |
| formatter | 用来格式化内容 | Function(row, column, cellValue) | — | — |
| show-overflow-tooltip | 当内容过长被隐藏时显示 tooltip | Boolean | — | false |

View File

@ -77,6 +77,7 @@ Vue.component('el-table-column', ElTableColumn)
| render-header | 列标题 Label 区域渲染使用的 Function | Function(h, { column, $index }) | — | — |
| sortable | 对应列是否可以排序,如果设置为 'custom',则代表用户希望远程排序,需要监听 Table 的 sort-change 事件 | boolean, string | true, false, 'custom' | false |
| sort-method | 对数据进行排序的时候使用的方法,仅当 sortable 设置为 true 的时候有效 | Function(a, b) | — | — |
| sort-by | 对数据进行排序的时候按照 sort-by 排序,仅当 sortable 设置为 true 且没有设置 sort-method 的时候有效。如果 sort-by 为数组,则先按照第 0 个排序,如果第 0 个相等,再按照第 1 个排序,以此类推。 | Function(row, index)/String/Array | — | — |
| resizable | 对应列是否可以通过拖动改变宽度(需要在 el-table 上设置 border 属性为真) | boolean | — | true |
| formatter | 用来格式化内容 | Function(row, column) | — | — |
| show-overflow-tooltip | 当内容过长被隐藏时显示 tooltip | Boolean | — | false |

View File

@ -134,6 +134,7 @@ export default {
default: false
},
sortMethod: Function,
sortBy: [String, Function, Array],
resizable: {
type: Boolean,
default: true
@ -234,6 +235,7 @@ export default {
headerAlign: this.headerAlign ? 'is-' + this.headerAlign : (this.align ? 'is-' + this.align : null),
sortable: this.sortable === '' ? true : this.sortable,
sortMethod: this.sortMethod,
sortBy: this.sortBy,
resizable: this.resizable,
showOverflowTooltip: this.showOverflowTooltip || this.showTooltipWhenOverflow,
formatter: this.formatter,

View File

@ -8,7 +8,7 @@ const sortData = (data, states) => {
if (!sortingColumn || typeof sortingColumn.sortable === 'string') {
return data;
}
return orderBy(data, states.sortProp, states.sortOrder, sortingColumn.sortMethod);
return orderBy(data, states.sortProp, states.sortOrder, sortingColumn.sortMethod, sortingColumn.sortBy);
};
const getKeysMap = function(array, rowKey) {

View File

@ -17,28 +17,62 @@ const isObject = function(obj) {
return obj !== null && typeof obj === 'object';
};
export const orderBy = function(array, sortKey, reverse, sortMethod) {
if (typeof reverse === 'string') {
reverse = reverse === 'descending' ? -1 : 1;
}
if (!sortKey && !sortMethod) {
export const orderBy = function(array, sortKey, reverse, sortMethod, sortBy) {
if (!sortKey && !sortMethod && (!sortBy || Array.isArray && !sortBy.length)) {
return array;
}
const order = (reverse && reverse < 0) ? -1 : 1;
// sort on a copy to avoid mutating original array
return array.slice().sort(sortMethod ? function(a, b) {
const result = sortMethod(a, b);
return result === 0 ? 0 : result > 0 ? order : -order;
} : function(a, b) {
if (sortKey !== '$key') {
if (isObject(a) && '$value' in a) a = a.$value;
if (isObject(b) && '$value' in b) b = b.$value;
if (typeof reverse === 'string') {
reverse = reverse === 'descending' ? -1 : 1;
} else {
reverse = (reverse && reverse < 0) ? -1 : 1;
}
const getKey = sortMethod ? null : function(value, index) {
if (sortBy) {
if (!Array.isArray) {
sortBy = [sortBy];
}
return sortBy.map(function(by) {
if (typeof by === 'string') {
return getValueByPath(value, by);
} else {
return by(value, index, array);
}
});
}
a = isObject(a) ? getValueByPath(a, sortKey) : a;
b = isObject(b) ? getValueByPath(b, sortKey) : b;
return a === b ? 0 : a > b ? order : -order;
});
if (sortKey !== '$key') {
if (isObject(value) && '$value' in value) return [value.$value];
} else {
return [isObject(value) ? getValueByPath(value, sortKey) : value];
}
};
const compare = function(a, b) {
if (sortMethod) {
return sortMethod(a.value, b.value);
}
for (let i = 0, len = a.key.length; i < len; i++) {
if (a.key[i] < b.key[i]) {
return -1;
}
if (a.key[i] > b.key[i]) {
return 1;
}
}
return 0;
};
return array.map(function(value, index) {
return {
value: value,
index: index,
key: getKey ? getKey(value, index) : null
};
}).sort(function(a, b) {
let order = compare(a, b);
if (!order) {
// make stable https://en.wikipedia.org/wiki/Sorting_algorithm#Stability
order = a.index - b.index;
}
return order * reverse;
}).map(item => item.value);
};
export const getColumnById = function(table, columnId) {

View File

@ -995,7 +995,14 @@ describe('Table', () => {
'sortable :sort-method="sortMethod"', '', '', '', {
methods: {
sortMethod(a, b) {
return a.runtime < b.runtime;
// sort method should return number
if (a.runtime < b.runtime) {
return 1;
}
if (a.runtime > b.runtime) {
return -1;
}
return 0;
}
}
});
@ -1013,6 +1020,46 @@ describe('Table', () => {
}, DELAY);
});
it('sortable by method', done => {
const vm = createTable(
'sortable :sort-by="sortBy"', '', '', '', {
methods: {
sortBy(a) {
return -a.runtime;
}
}
});
setTimeout(_ => {
const elm = vm.$el.querySelector('.caret-wrapper');
elm.click();
setTimeout(_ => {
const lastCells = vm.$el.querySelectorAll('.el-table__body-wrapper tbody tr td:last-child');
expect(toArray(lastCells).map(node => node.textContent)).to.eql(['100', '95', '92', '92', '80']);
destroyVM(vm);
done();
}, DELAY);
}, DELAY);
});
it('sortable by property', done => {
const vm = createTable(
'sortable sort-by="runtime"', '', '', '', {});
setTimeout(_ => {
const elm = vm.$el.querySelector('.caret-wrapper');
elm.click();
setTimeout(_ => {
const lastCells = vm.$el.querySelectorAll('.el-table__body-wrapper tbody tr td:last-child');
expect(toArray(lastCells).map(node => node.textContent)).to.eql(['80', '92', '92', '95', '100']);
destroyVM(vm);
done();
}, DELAY);
}, DELAY);
});
it('sort-change', done => {
let result;
const vm = createTable('sortable="custom"', '', '', '', {