From 349894d107280878a9185df561d3bdec57237396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=A5=95?= Date: Fri, 28 Apr 2017 15:53:39 +0800 Subject: [PATCH] Table: add summary row, fixed #1307, fixed #4451 (#4484) --- examples/docs/en-US/table.md | 201 +++++++++++++++++++++ examples/docs/zh-CN/table.md | 203 ++++++++++++++++++++++ packages/table/src/table-body.js | 2 + packages/table/src/table-column.js | 6 + packages/table/src/table-footer.js | 142 +++++++++++++++ packages/table/src/table-layout.js | 3 +- packages/table/src/table.vue | 42 ++++- packages/theme-default/src/common/var.css | 1 + packages/theme-default/src/table.css | 43 ++++- src/locale/lang/bg.js | 3 +- src/locale/lang/ca.js | 3 +- src/locale/lang/cz.js | 3 +- src/locale/lang/da.js | 3 +- src/locale/lang/de.js | 3 +- src/locale/lang/el.js | 3 +- src/locale/lang/en.js | 3 +- src/locale/lang/es.js | 3 +- src/locale/lang/fa.js | 3 +- src/locale/lang/fi.js | 3 +- src/locale/lang/fr.js | 3 +- src/locale/lang/id.js | 3 +- src/locale/lang/it.js | 3 +- src/locale/lang/ja.js | 3 +- src/locale/lang/ko.js | 3 +- src/locale/lang/nb-NO.js | 3 +- src/locale/lang/nl.js | 3 +- src/locale/lang/pl.js | 3 +- src/locale/lang/pt-br.js | 3 +- src/locale/lang/pt.js | 3 +- src/locale/lang/ru-RU.js | 3 +- src/locale/lang/sk.js | 3 +- src/locale/lang/sv-SE.js | 3 +- src/locale/lang/th.js | 3 +- src/locale/lang/tk.js | 3 +- src/locale/lang/tr-TR.js | 3 +- src/locale/lang/ua.js | 3 +- src/locale/lang/vi.js | 3 +- src/locale/lang/zh-CN.js | 3 +- src/locale/lang/zh-TW.js | 3 +- 39 files changed, 693 insertions(+), 40 deletions(-) create mode 100644 packages/table/src/table-footer.js diff --git a/examples/docs/en-US/table.md b/examples/docs/en-US/table.md index 588f573a4..0377170a0 100644 --- a/examples/docs/en-US/table.md +++ b/examples/docs/en-US/table.md @@ -154,12 +154,68 @@ address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }], + tableData6: [{ + id: '12987122', + name: 'Tom', + amount1: '234', + amount2: '3.2', + amount3: 10 + }, { + id: '12987123', + name: 'Tom', + amount1: '165', + amount2: '4.43', + amount3: 12 + }, { + id: '12987124', + name: 'Tom', + amount1: '324', + amount2: '1.9', + amount3: 9 + }, { + id: '12987125', + name: 'Tom', + amount1: '621', + amount2: '2.2', + amount3: 17 + }, { + id: '12987126', + name: 'Tom', + amount1: '539', + amount2: '4.1', + amount3: 15 + }], currentRow: null, multipleSelection: [] }; }, methods: { + getSummaries(param) { + const { columns, data } = param; + const sums = []; + columns.forEach((column, index) => { + if (index === 0) { + sums[index] = 'Total Cost'; + return; + } + const values = data.map(item => Number(item[column.property])); + if (!values.every(value => isNaN(value))) { + sums[index] = '$ ' + values.reduce((prev, curr) => { + const value = Number(curr); + if (!isNaN(value)) { + return prev + curr; + } else { + return prev; + } + }, 0); + } else { + sums[index] = 'N/A'; + } + }); + + return sums; + }, setCurrent(row) { this.$refs.singleTable.setCurrentRow(row); }, @@ -1470,6 +1526,143 @@ When the row content is too long and you do not want to display the horizontal s ``` ::: +### Summary row + +For table of numbers, you can add an extra row at the table footer displaying each column's sum. +:::demo You can add the summary row by setting `show-summary` to `true`. By default, for the summary row, the first column does not sum anything up but always displays 'Sum' (you can configure the displayed text using `sum-text`), while other columns sum every number in that column up and display them. You can of course define your own sum behaviour. To do so, pass a method to `summary-method`, which returns an array, and each element of the returned array will be displayed in the columns of the summary row. The second table of this example is a detailed demo. +```html + + + +``` +::: + ### Table Attributes | Attribute | Description | Type | Accepted Values | Default | |---------- |-------------- |---------- |-------------------------------- |-------- | @@ -1490,6 +1683,9 @@ When the row content is too long and you do not want to display the horizontal s | expand-row-keys | set expanded rows by this prop, prop's value is the keys of expand rows, you should set row-key before using this prop | Array | — | | | default-sort | set the default sort column and order. property `prop` is used to set default sort column, property `order` is used to set default sort order | Object | `order`: ascending, descending | if `prop` is set, and `order` is not set, then `order` is default to ascending | | tooltip-effect | tooltip `effect` property | String | dark/light | | dark | +| show-summary | whether to display a summary row | Boolean | — | false | +| sum-text | displayed text for the first column of summary row | String | — | Sum | +| summary-method | custom summary method | Function({ columns, data }) | — | — | ### Table Events | Event Name | Description | Parameters | @@ -1518,6 +1714,11 @@ When the row content is too long and you do not want to display the horizontal s | toggleRowSelection | used in multiple selection Table, toggle if a certain row is selected. With the second parameter, you can directly set if this row is selected | row, selected | | setCurrentRow | used in single selection Table, set a certain row selected. If called without any parameter, it will clear selection. | row | +### Table Slot +| Name | Description | +|------|--------| +| append | Contents to be inserted after the last row. It is still nested inside the `` tag. You may need this slot if you want to implement infinite scroll for the table. This slot will be displayed above the summary row if there is one. | + ### Table-column Attributes | Attribute | Description | Type | Accepted Values | Default | |---------- |-------------- |---------- |-------------------------------- |-------- | diff --git a/examples/docs/zh-CN/table.md b/examples/docs/zh-CN/table.md index df9eac215..319b8899b 100644 --- a/examples/docs/zh-CN/table.md +++ b/examples/docs/zh-CN/table.md @@ -194,12 +194,69 @@ shop: '王小虎夫妻店', shopId: '10333' }], + tableData6: [{ + id: '12987122', + name: '王小虎', + amount1: '234', + amount2: '3.2', + amount3: 10 + }, { + id: '12987123', + name: '王小虎', + amount1: '165', + amount2: '4.43', + amount3: 12 + }, { + id: '12987124', + name: '王小虎', + amount1: '324', + amount2: '1.9', + amount3: 9 + }, { + id: '12987125', + name: '王小虎', + amount1: '621', + amount2: '2.2', + amount3: 17 + }, { + id: '12987126', + name: '王小虎', + amount1: '539', + amount2: '4.1', + amount3: 15 + }], currentRow: null, multipleSelection: [] }; }, methods: { + getSummaries(param) { + const { columns, data } = param; + const sums = []; + columns.forEach((column, index) => { + if (index === 0) { + sums[index] = '总价'; + return; + } + const values = data.map(item => Number(item[column.property])); + if (!values.every(value => isNaN(value))) { + sums[index] = values.reduce((prev, curr) => { + const value = Number(curr); + if (!isNaN(value)) { + return prev + curr; + } else { + return prev; + } + }, 0); + sums[index] += ' 元'; + } else { + sums[index] = 'N/A'; + } + }); + + return sums; + }, setCurrent(row) { this.$refs.singleTable.setCurrentRow(row); }, @@ -1555,6 +1612,144 @@ ``` ::: +### 表尾合计行 + +若表格展示的是各类数字,可以在表尾显示各列的合计。 +:::demo 将`show-summary`设置为`true`就会在表格尾部展示合计行。默认情况下,对于合计行,第一列不进行数据求合操作,而是显示「合计」二字(可通过`sum-text`配置),其余列会将本列所有数值进行求合操作,并显示出来。当然,你也可以定义自己的合计逻辑。使用`summary-method`并传入一个方法,返回一个数组,这个数组中的各项就会显示在合计行的各列中,具体可以参考本例中的第二个表格。 +```html + + + +``` +::: + ### Table Attributes | 参数 | 说明 | 类型 | 可选值 | 默认值 | |---------- |-------------- |---------- |-------------------------------- |-------- | @@ -1575,6 +1770,9 @@ | expand-row-keys | 可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。| Array | — | | | default-sort | 默认的排序列的prop和顺序。它的`prop`属性指定默认的排序的列,`order`指定默认排序的顺序| Object | `order`: ascending, descending | 如果只指定了`prop`, 没有指定`order`, 则默认顺序是ascending | | tooltip-effect | tooltip `effect` 属性 | String | dark/light | | dark | +| show-summary | 是否在表尾显示合计行 | Boolean | — | false | +| sum-text | 合计行第一列的文本 | String | — | 合计 | +| summary-method | 自定义的合计计算方法 | Function({ columns, data }) | — | — | ### Table Events | 事件名 | 说明 | 参数 | @@ -1603,6 +1801,11 @@ | toggleRowSelection | 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中) | row, selected | | setCurrentRow | 用于单选表格,设定某一行为选中行,如果调用时不加参数,则会取消目前高亮行的选中状态。 | row | +### Table Slot +| name | 说明 | +|------|--------| +| append | 插入至表格最后一行之后的内容,仍然位于 `` 标签内。如果需要对表格的内容进行无限滚动操作,可能需要用到这个 slot。若表格有合计行,该 slot 会位于合计行之上。 | + ### Table-column Attributes | 参数 | 说明 | 类型 | 可选值 | 默认值 | |---------- |-------------- |---------- |-------------------------------- |-------- | diff --git a/packages/table/src/table-body.js b/packages/table/src/table-body.js index d47817a32..7ffcb665d 100644 --- a/packages/table/src/table-body.js +++ b/packages/table/src/table-body.js @@ -77,6 +77,8 @@ export default { ) : '' ] + ).concat( + this._self.$parent.$slots.append ).concat( ) diff --git a/packages/table/src/table-column.js b/packages/table/src/table-column.js index d1ec059ee..8bdb09459 100644 --- a/packages/table/src/table-column.js +++ b/packages/table/src/table-column.js @@ -364,6 +364,12 @@ export default { this.columnConfig.fixed = newVal; this.owner.store.scheduleLayout(); } + }, + + sortable(newVal) { + if (this.columnConfig) { + this.columnConfig.sortable = newVal; + } } }, diff --git a/packages/table/src/table-footer.js b/packages/table/src/table-footer.js new file mode 100644 index 000000000..b76ab17a8 --- /dev/null +++ b/packages/table/src/table-footer.js @@ -0,0 +1,142 @@ +export default { + name: 'ElTableFooter', + + render(h) { + const sums = []; + this.columns.forEach((column, index) => { + if (index === 0) { + sums[index] = this.sumText; + return; + } + const values = this.store.states.data.map(item => Number(item[column.property])); + const precisions = []; + let notNumber = true; + values.forEach(value => { + if (!isNaN(value)) { + notNumber = false; + let decimal = ('' + value).split('.')[1]; + precisions.push(decimal ? decimal.length : 0); + } + }); + const precision = Math.max.apply(null, precisions); + if (!notNumber) { + sums[index] = values.reduce((prev, curr) => { + const value = Number(curr); + if (!isNaN(value)) { + return parseFloat((prev + curr).toFixed(precision)); + } else { + return prev; + } + }, 0); + } else { + sums[index] = ''; + } + }); + + return ( + + + { + this._l(this.columns, column => + ) + } + { + !this.fixed && this.layout.gutterWidth + ? + : '' + } + + + + { + this._l(this.columns, (column, cellIndex) => + + ) + } + { + !this.fixed && this.layout.gutterWidth + ? + : '' + } + + + + ); + }, + + props: { + fixed: String, + store: { + required: true + }, + layout: { + required: true + }, + summaryMethod: Function, + sumText: String, + border: Boolean, + defaultSort: { + type: Object, + default() { + return { + prop: '', + order: '' + }; + } + } + }, + + computed: { + isAllSelected() { + return this.store.states.isAllSelected; + }, + + columnsCount() { + return this.store.states.columns.length; + }, + + leftFixedCount() { + return this.store.states.fixedColumns.length; + }, + + rightFixedCount() { + return this.store.states.rightFixedColumns.length; + }, + + columns() { + return this.store.states.columns; + } + }, + + methods: { + isCellHidden(index, columns) { + if (this.fixed === true || this.fixed === 'left') { + return index >= this.leftFixedCount; + } else if (this.fixed === 'right') { + let before = 0; + for (let i = 0; i < index; i++) { + before += columns[i].colSpan; + } + return before < this.columnsCount - this.rightFixedCount; + } else { + return (index < this.leftFixedCount) || (index >= this.columnsCount - this.rightFixedCount); + } + } + } +}; diff --git a/packages/table/src/table-layout.js b/packages/table/src/table-layout.js index b53e32780..a35faebe2 100644 --- a/packages/table/src/table-layout.js +++ b/packages/table/src/table-layout.js @@ -83,7 +83,8 @@ class TableLayout { this.fixedBodyHeight = this.scrollX ? height - this.gutterWidth : height; } else { const headerHeight = this.headerHeight = headerWrapper.offsetHeight; - const bodyHeight = height - headerHeight; + const ratio = this.table.showSummary && this.table.data && this.table.data.length > 0 ? 2 : 1; + const bodyHeight = height - ratio * headerHeight + (this.table.showSummary ? 1 : 0); if (this.height !== null && (!isNaN(this.height) || typeof this.height === 'string')) { this.bodyHeight = bodyHeight; } diff --git a/packages/table/src/table.vue b/packages/table/src/table.vue index 2e07f9551..1996ee495 100644 --- a/packages/table/src/table.vue +++ b/packages/table/src/table.vue @@ -36,6 +36,17 @@ {{ emptyText || t('el.table.emptyText') }} +
+
+