背景
对结果进行展示时,需要对数据相同的列进行合并,具体效果如下:
环境
- @Angular/cli@11.0.7
- ng-devui@11.0.0
操作
-
先对数据源进行处理
原始数据源数据格式如下:
export const students = [ { school: 'hx', grade: 'one', name: 'xm', course: 'math', fraction: '80', average: '80' }, { school: 'hx', grade: 'one', name: 'xm', course: 'chinese', fraction: '81', average: '80' }, { school: 'hx', grade: 'one', name: 'xm', course: 'english', fraction: '82', average: '80' }, { school: 'hx', grade: 'one', name: 'zy', course: 'math', fraction: '80', average: '80' }, { school: 'hx', grade: 'one', name: 'zy', course: 'chinese', fraction: '81', average: '80' }, { school: 'hx', grade: 'one', name: 'zy', course: 'english', fraction: '82', average: '80' }, { school: 'hx', grade: 'two', name: 'xh', course: 'math', fraction: '80', average: '80' }, { school: 'hx', grade: 'two', name: 'xh', course: 'chinese', fraction: '81', average: '80' }, { school: 'hx', grade: 'two', name: 'xh', course: 'english', fraction: '82', average: '80' }, ];
需将数据源中相同的列进行合并,同时,对
cource
列进行行转列,转换后数据格式如下:export const ss = [ { type: 'fraction', child: [ { school: 'hx', child: [ { grade: 'one', child: [ { name: 'xm', type: 'fraction', school: 'hx', grade: 'one', math: '80', chinese: '81', english: '82' }, { name: 'zy', type: 'fraction', school: 'hx', grade: 'one', math: '80', chinese: '81', english: '82' }, ], len: 2, }, { grade: 'two', child: [{ name: 'xh', type: 'fraction', school: 'hx', grade: 'two', math: '80', chinese: '81', english: '82' }], len: 1, }, ], len: 3, }, ], len: 3, ## len表示总共有多少行数据,供rowspan使用 }, { type: 'average', child: [ { school: 'hx', child: [ { grade: 'one', child: [ { name: 'xm', type: 'average', school: 'hx', grade: 'one', math: '80', chinese: '80', english: '80' }, { name: 'zy', type: 'average', school: 'hx', grade: 'one', math: '80', chinese: '80', english: '80' }, ], len: 2, }, { grade: 'two', child: [{ name: 'xh', type: 'average', school: 'hx', grade: 'two', math: '80', chinese: '80', english: '80' }], len: 1, }, ], len: 3, }, ], len: 3, }, ];
具体实现转换的代码如下:
export class ConditionDatatableComponent implements OnInit { rowSpan: number[] = []; stuSource = []; stuColumns = ['type', 'school', 'grade', 'name', 'math', 'chinese', 'english']; constructor() {} ngOnInit(): void { this.stuSource = this.setSource(); console.log(this.stuSource); } /** * 设置rowspan */ setRowSpans(index: number, value: number): number[] { this.rowSpan[index] = value; return this.rowSpan; } /** * 数据源处理 */ setSource() { let dest = []; students.forEach((item, index) => { //处理fraction this.setDataSource(item, dest, this.stuColumns, ['type', 'school', 'grade', 'name', 'course'], 0, 'fraction'); //处理average this.setDataSource(item, dest, this.stuColumns, ['type', 'school', 'grade', 'name', 'course'], 0, 'average'); }); return dest; } /** * 对数据源进行处理 */ setDataSource(item: any, dest: any[], columns: string[], groups: string[], index: number, graphType: string) { if (!item) return; if (!groups || groups.length == 0 || groups.length < index) return; if (!graphType) return; //当前key值 let target = groups[index]; //当前的value值 let value = this.isGraphType(target) ? graphType : item[target]; let isCreate = false; let len = 0; let dest_child = dest.filter((a) => a[target] == value)[0]; if (!dest_child) { isCreate = true; dest_child = {}; dest_child[target] = value; if (!this.isEnd(groups, index)) { dest_child['child'] = []; } else { for (let i = 0; i < index; i++) { let col = columns[i]; let v = this.isGraphType(col) ? graphType : item[col]; dest_child[col] = v; } } dest.push(dest_child); } if (!this.isEnd(groups, index)) { len = this.setDataSource(item, dest_child.child ?? [], columns, groups, index + 1, graphType); dest_child['len'] = (dest_child['len'] ?? 0) + len; } else { //当前key值 target = groups[index + 1]; //当前的value值 value = item[target]; dest_child[value] = item[graphType]; if (isCreate) { len = 1; } } return len; } }
-
页面展示
页面展示部分主要使用的是
ng-devui
的组件(可使用其他组件,逻辑是一样的),再辅以ng-template
、ng-container
以及ngTemplateOutlet
来实现该功能,具体代码如下:<d-data-table [dataSource]="stuSource"> <thead dTableHead> <tr dTableRow> <th dHeadCell *ngFor="let name of stuColumns">{{ name }}</th> </tr> </thead> <tbody dTableBody> <ng-template let-column="column" let-colIndex="colIndex" let-rowItem="rowItem" let-rowIndex="rowIndex"> <ng-container *ngTemplateOutlet="TableTd; context: { item: rowItem, colIndex: 0, startColIndex: 0, rowSpans: rowSpan }"></ng-container> </ng-template> </tbody> </d-data-table> <ng-template #TableTd let-item="item" let-colIndex="colIndex" let-startColIndex="startColIndex" let-rowSpans="rowSpans"> <ng-container *ngIf="item['child']"> <ng-container *ngFor="let item_child of item['child']; let i = index"> <ng-container *ngTemplateOutlet=" TableTd; context: { item: item_child, colIndex: colIndex + 1, startColIndex: i === 0 ? startColIndex : colIndex + 1, rowSpans: setRowSpans(colIndex, item['len']) } " ></ng-container> </ng-container> </ng-container> <ng-container *ngIf="!item['child']"> <tr dTableRow> <ng-container *ngFor="let col of stuColumns; let i = index"> <td dTableCell *ngIf="i >= startColIndex" [ngClass]="{ 'border-left': i === startColIndex && startColIndex > 0 }" [attr.rowSpan]="rowSpan && rowSpan[i] && rowSpan[i] > 0 ? rowSpan[i] : 1" > {{ item[col] }} </td> </ng-container> </tr> </ng-container> </ng-template>
上述代码中:
[ngClass]="{ 'border-left': i === startColIndex && startColIndex > 0 }"
格式为:
[ngClass]="{'border-left':condition}"
,第一个参数为样式的名称,第二个参数为boolean值,当第二个参数,即condition
为true
时,才会添加第一个参数的样式,即border-left
。 -
样式
样式采用的是
scss
格式,代码如下:@import '~ng-devui/styles-var/devui-var.scss'; tr { td:first-child { border-left: 1px solid $devui-dividing-line; } td { border-right: 1px solid $devui-dividing-line; } } thead { tr { th:first-child { border-left: 1px solid $devui-dividing-line; } th { border-top: 1px solid $devui-dividing-line; border-right: 1px solid $devui-dividing-line; } } } .border-left { border-left: 1px $devui-dividing-line !important; }