Skip to content

Commit ad60ba3

Browse files
author
Kenneth Cheng
committed
add grouping feature
1 parent 4b21df1 commit ad60ba3

File tree

5 files changed

+78
-9
lines changed

5 files changed

+78
-9
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,43 @@ to transparent color and remove the border-top. It also affects the next column
852852
853853
![Hide Duplication](https://i.imgur.com/HCFyEEp.png "Hide Duplication")
854854
855+
All field set to hide duplication will be set as readonly by default.
856+
857+
### Grouping
858+
859+
Grouping is a little bit tricky. Sometimes we want to show the first row of a set of record and hide the rest of the row
860+
based on the cell value. For example, we want to group the gender by M and F and the first record are the header that holds the statistics
861+
of each group. If this is the case, you could use grouping attribute. For example:
862+
863+
```html
864+
<vue-excel-editor ref="editor" v-if="!hide" v-model="jsondata" filter-row v-model:selected-count="count">
865+
<vue-excel-column type="string" field="region" width="80px" label="Group1" grouping="collapse" />
866+
<vue-excel-column type="string" field="area" width="80px" label="Group2" grouping="collapse" />
867+
<vue-excel-column type="map" field="gender" width="80px" label="Gender" :options="{M: 'Male', F: 'Female'}" :change="genderChange" />
868+
<vue-excel-column type="number" field="age" width="60px" label="Age" :validate="validateAge" summary="avg" />
869+
<vue-excel-column type="badge" field="user" width="75px" label="User" :bgcolor="badgeColor" />
870+
<vue-excel-column type="string" field="name" width="150px" label="Name" :link="linkClick" :isLink="isLink" />
871+
<vue-excel-column type="string" field="phone" width="160px" label="Contact" :to-text="toTextPasswd"/>
872+
<vue-excel-column type="password" field="pwd" width="90px" label="Password" />
873+
<vue-excel-column type="date" field="birth" width="115px" label="Date Of Birth" />
874+
<vue-excel-column type="action" field="action" width="75px" label="#" :options="['Edit', 'Remove']" :change="doAction" placeholder="Action" />
875+
</vue-excel-editor>
876+
```
877+
878+
```js
879+
jsondata = [
880+
{region: 'US', area: 'AZ', user: "hc", name: "Harry Cole", phone: "1-415-2345678", gender: "M", age: 25, birth: "1997-07-01"},
881+
{region: 'US', area: 'AZ', user: "sm", name: "Simon Minolta", phone: "1-123-7675682", gender: "M", age: 25, birth: "1999-11-12"},
882+
{region: 'US', area: 'CL', user: "ra", name: "Rachael Amy", phone: "1-456-9981212", gender: "F", age: 19, birth: "2000-06-11"},
883+
{region: 'US', area: 'CL', user: "ag", name: "Mary George", phone: "1-556-1245684", gender: "F", age: 19, birth: "2002-08-01"},
884+
{region: 'CN', area: 'GZ', user: "kl", name: "Kenny Linus", phone: "1-891-2345685", gender: "F", age: 29, birth: "1990-09-01"}
885+
])
886+
```
887+
888+
All field set to be grouping will be set as readonly and hide duplication by default.
889+
890+
![Grouping](https://i.imgur.com/g5XnsbF.png "Grouping")
891+
855892
### Localization
856893
857894
The developer may override the default values through localized-label prop.

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vue3-excel-editor",
33
"email": "apple.6502@gmail.com",
44
"description": "Vue3 plugin for displaying and editing the array-of-object in Excel style",
5-
"version": "1.0.43",
5+
"version": "1.0.44",
66
"main": "src/main.js",
77
"dependencies": {
88
"@vuepic/vue-datepicker": "^3.3.0",

src/VueExcelColumn.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default {
2626
cellClick: {type: Function, default: null},
2727
autoFillWidth: {type: Boolean, default: false},
2828
hideDuplicate: {type: Boolean, default: false},
29+
grouping: {type: String, default: null},
2930
3031
allowKeys: {type: [Array, Function], default () {return null}},
3132
mandatory: {type: String, default: ''},
@@ -294,7 +295,8 @@ export default {
294295
listByClick: this.listByClick || this._listByClick,
295296
color: this.color || this._color,
296297
bgcolor: this.bgcolor || this._bgcolor,
297-
hideDuplicate: this.hideDuplicate
298+
hideDuplicate: this.hideDuplicate || this.grouping,
299+
grouping: this.grouping
298300
})
299301
}
300302
}

src/VueExcelEditor.vue

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,17 @@
117117
error: errmsg[`id-${record.$id}-${item.name}`],
118118
link: item.link && item.isLink && item.isLink(record),
119119
select: item.options,
120+
grouping: item.grouping,
120121
datepick: item.type == 'date',
121122
stickyColumn: item.sticky,
122-
hideDuplicate: item.hideDuplicate && rowPos > 0 && isSameSinceLeft(p, record, pagingTable[rowPos-1])
123+
hideDuplicate: item.hideDuplicate && rowPos > 0 && isSameSinceLeft(p, record, pagingTable[rowPos-1]),
123124
}"
124125
:key="p"
125126
:style="Object.assign(cellStyle(record, item), renderColumnCellStyle(item, record))"
126127
@mouseover="cellMouseOver"
127128
@mousemove="cellMouseMove">
128-
<template v-if="item.format=='html'"><span v-html="item.toText(record[item.name], record, item)" /></template>
129-
<template v-else>{{ item.toText(record[item.name], record, item) }}</template>
129+
<template v-if="item.format=='html'"><span v-html="item.toText(record[item.name], record, item, p)" /></template>
130+
<template v-else>{{ item.toText(record[item.name], record, item, p) }}</template>
130131
</td>
131132
<td v-if="vScroller.buttonHeight < vScroller.height" class="last-col"></td>
132133
</tr>
@@ -478,7 +479,9 @@ export default defineComponent({
478479
summaryRow: false,
479480
summary: {},
480481
showFilteredOnly: true,
481-
showSelectedOnly: false
482+
showSelectedOnly: false,
483+
484+
ungroup: {}
482485
}
483486
return dataset
484487
},
@@ -836,6 +839,7 @@ export default defineComponent({
836839
}
837840
})
838841
this.filteredValue = this.modelValue.filter(record => this.recordFilter(record))
842+
this.filteredValue = this.filteredValue.filter((record, i) => this.filterGrouping(record, i, this.modelValue))
839843
if (filterColumnList.length === 0)
840844
this.table = this.filteredValue
841845
else {
@@ -908,6 +912,22 @@ export default defineComponent({
908912
}
909913
this.calSummary()
910914
},
915+
filterGrouping (rec, i, table) {
916+
if (i === 0) return true
917+
const prec = table[i-1]
918+
let result = true
919+
this.fields.forEach(field => {
920+
const name = field.name
921+
if (field.grouping && rec[name] === prec[name]) {
922+
if (field.grouping === 'collapse' && this.ungroup[field.name + rec[name]] !== true)
923+
result = false
924+
else
925+
if (field.grouping === 'expand' && this.ungroup[field.name + rec[name]])
926+
result = false
927+
}
928+
})
929+
return result
930+
},
911931
calStickyLeft () {
912932
let left = 0, n = 0
913933
this.leftMost = -1
@@ -2280,7 +2300,11 @@ export default defineComponent({
22802300
this.currentField.cellClick(this.currentCell.textContent, this.currentRecord, rowPos, colPos, this.currentField, this)
22812301
if (this.currentField && this.currentField.link /* && e.altKey */ && this.currentCell.textContent)
22822302
return setTimeout(() => this.currentField.link(this.currentCell.textContent, this.currentRecord, rowPos, colPos, this.currentField, this))
2283-
2303+
if (this.currentField.grouping) {
2304+
this.ungroup[this.currentField.name + this.currentCell.textContent] = !this.ungroup[this.currentField.name + this.currentCell.textContent]
2305+
this.refresh()
2306+
return
2307+
}
22842308
setTimeout(() => this.inputBox.focus())
22852309
this.focused = true
22862310
this.moveInputSquare(rowPos, colPos)
@@ -2328,7 +2352,7 @@ export default defineComponent({
23282352
const row = e.target.parentNode
23292353
const colPos = Array.from(row.children).indexOf(e.target) - 1
23302354
const currentField = this.fields[colPos]
2331-
if (currentField?.type === 'action')
2355+
if (currentField?.type === 'action' || currentField?.grouping)
23322356
cursor = 'pointer'
23332357
e.target.style.cursor = cursor
23342358
},
@@ -3063,6 +3087,12 @@ input:focus, input:active:focus, input.active:focus {
30633087
.systable td.first-col.focus {
30643088
border-right: 1px solid rgb(61, 85, 61) !important;
30653089
}
3090+
.systable tbody td.grouping {
3091+
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAlmVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSATEAAgAAABEAAABah2kABAAAAAEAAABsAAAAAAAAAGAAAAABAAAAYAAAAAF3d3cuaW5rc2NhcGUub3JnAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADaADAAQAAAABAAAADQAAAAC2lhxrAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADBGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj44MjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj44MjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj45NjwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+OTY8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+d3d3Lmlua3NjYXBlLm9yZzwveG1wOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4K/79DmAAAARFJREFUKBWFks9KQkEUh70h9EdwY6abFhcUxK1P4EqsoKezB+gNjDDa1FO40YVoG7V1URDo940zUkT0g++ec+ecM85vrllhpyLhK+Z94gVU4RVG8AAq9B2khJjDGG7hDNYx+u66dTd2MMiFTxjsXn89b1j5APv2cicLKoNjaMfou7JuX5AePIpKDRXyBZy6iNK6HvueT9PPoI7gBBpwGOOG+AYe7wkuHdL0EtQ5PEIJyjCEd+jBBFZQc8ifrIN6gQ404Q6uYQoOqhrYX/jL05ya3tR3T9oJ8lYGMbdBXy3wFtOAdfv2ysn++07W7Qu76Msv7cI9VMHbDKaJXdDHFcygmPFQadBcj57bW13DCH7897YprztHz5xxRwAAAABJRU5ErkJggg==');
3092+
background-repeat: no-repeat;
3093+
background-size: 8px 8px;
3094+
background-position: right 5px top 8px;
3095+
}
30663096
.systable tbody td.select:not(.readonly) {
30673097
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAASUExURQAAANra2tfX19ra2tnZ2dnZ2c8lDs8AAAAFdFJOUwAwQL/PKlwehgAAAAlwSFlzAAAXEQAAFxEByibzPwAAAEdJREFUKFNdyskBACAIA8F49d+yiBEh+9rHYC5poPGiDmUDUGZI2EHCHBV2UWFEiT2UWKBgHwVLiCwjsoKcVeRMkDFFxoiADdH4AyvGhvOPAAAAAElFTkSuQmCC');
30683098
background-repeat: no-repeat;

0 commit comments

Comments
 (0)