239 lines
6.2 KiB
TypeScript
239 lines
6.2 KiB
TypeScript
import {
|
|
Component,
|
|
OnInit,
|
|
Input,
|
|
ChangeDetectorRef,
|
|
ViewChild,
|
|
Output,
|
|
EventEmitter,
|
|
AfterViewInit,
|
|
OnDestroy
|
|
} from '@angular/core';
|
|
import { MatTreeFlattener, MatTree } from '@angular/material';
|
|
import { NGXLogger } from 'ngx-logger';
|
|
|
|
import { DeptInfo } from '@ucap-webmessenger/protocol-query';
|
|
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
|
import { FlatTreeControl } from '@angular/cdk/tree';
|
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
|
import { VirtualScrollTreeFlatDataSource } from '@ucap-webmessenger/ui';
|
|
import { ucapAnimations } from '@ucap-webmessenger/ui';
|
|
import { trigger, transition, style, animate } from '@angular/animations';
|
|
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
|
import { Subscription } from 'rxjs';
|
|
|
|
interface OrganizationNode {
|
|
deptInfo: DeptInfo;
|
|
name: string;
|
|
children?: OrganizationNode[];
|
|
}
|
|
|
|
/** Flat node with expandable and level information */
|
|
interface FlatNode {
|
|
expandable: boolean;
|
|
name: string;
|
|
level: number;
|
|
deptInfo: DeptInfo;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'ucap-organization-tree',
|
|
templateUrl: './tree.component.html',
|
|
styleUrls: ['./tree.component.scss'],
|
|
animations: [
|
|
ucapAnimations,
|
|
trigger('romvoeAdd', [
|
|
transition('remove <=> add', [
|
|
style({
|
|
transform: `rotate(45deg)`,
|
|
opacity: 0
|
|
}),
|
|
animate('.2s 0s ease-out')
|
|
])
|
|
])
|
|
]
|
|
})
|
|
export class TreeComponent implements OnInit, OnDestroy, AfterViewInit {
|
|
@Output()
|
|
selected = new EventEmitter<DeptInfo>();
|
|
|
|
@Input()
|
|
loginRes: LoginResponse;
|
|
|
|
@Input()
|
|
set oraganizationList(deptInfoList: DeptInfo[]) {
|
|
if (!deptInfoList || 0 === deptInfoList.length) {
|
|
return;
|
|
}
|
|
const nodeMap = new Map<number, OrganizationNode>();
|
|
const rootNodeList: OrganizationNode[] = [];
|
|
const remainChildNodeList: OrganizationNode[] = [];
|
|
let myNode: OrganizationNode;
|
|
|
|
deptInfoList.forEach(deptInfo => {
|
|
const node: OrganizationNode = {
|
|
deptInfo,
|
|
name: deptInfo.name,
|
|
children: []
|
|
};
|
|
if (nodeMap.has(deptInfo.seq)) {
|
|
this.logger.warn('duplicate seq', deptInfo.seq);
|
|
return;
|
|
}
|
|
nodeMap.set(deptInfo.seq, node);
|
|
|
|
if (deptInfo.seq === this.loginRes.departmentCode) {
|
|
myNode = node;
|
|
}
|
|
|
|
if (0 === deptInfo.parentSeq) {
|
|
rootNodeList.push(node);
|
|
return;
|
|
}
|
|
|
|
if (nodeMap.has(deptInfo.parentSeq)) {
|
|
nodeMap.get(deptInfo.parentSeq).children.push(node);
|
|
} else {
|
|
remainChildNodeList.push(node);
|
|
}
|
|
});
|
|
|
|
remainChildNodeList.forEach(node => {
|
|
if (nodeMap.has(node.deptInfo.parentSeq)) {
|
|
nodeMap.get(node.deptInfo.parentSeq).children.push(node);
|
|
}
|
|
});
|
|
this.dataSource.data = rootNodeList;
|
|
}
|
|
|
|
@ViewChild('cvsvOrganization', { static: false })
|
|
cvsvOrganization: CdkVirtualScrollViewport;
|
|
|
|
@ViewChild('orgranizationTree', { static: false })
|
|
orgranizationTree: MatTree<OrganizationNode>;
|
|
|
|
@ViewChild(PerfectScrollbarDirective, { static: false })
|
|
psDirectiveRef?: PerfectScrollbarDirective;
|
|
|
|
treeControl: FlatTreeControl<FlatNode>;
|
|
treeFlattener: MatTreeFlattener<OrganizationNode, FlatNode>;
|
|
dataSource: VirtualScrollTreeFlatDataSource<OrganizationNode, FlatNode>;
|
|
|
|
treeControlExpansionChangeSubscription: Subscription;
|
|
|
|
constructor(
|
|
private changeDetectorRef: ChangeDetectorRef,
|
|
private logger: NGXLogger
|
|
) {
|
|
this.treeControl = new FlatTreeControl<FlatNode>(
|
|
node => node.level,
|
|
node => node.expandable
|
|
);
|
|
this.treeFlattener = new MatTreeFlattener<OrganizationNode, FlatNode>(
|
|
(node: OrganizationNode, level: number) => {
|
|
return {
|
|
expandable: !!node.children && node.children.length > 0,
|
|
name: node.name,
|
|
level,
|
|
deptInfo: node.deptInfo
|
|
};
|
|
},
|
|
node => node.level,
|
|
node => node.expandable,
|
|
node => node.children
|
|
);
|
|
this.dataSource = new VirtualScrollTreeFlatDataSource<
|
|
OrganizationNode,
|
|
FlatNode
|
|
>(this.treeControl, this.treeFlattener);
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.treeControlExpansionChangeSubscription = this.treeControl.expansionModel.changed.subscribe(
|
|
() => {
|
|
this.psDirectiveRef.update();
|
|
}
|
|
);
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.logger.debug('-----------------------TreeComponent ngOnDestroy');
|
|
if (!!this.treeControlExpansionChangeSubscription) {
|
|
this.treeControlExpansionChangeSubscription.unsubscribe();
|
|
}
|
|
}
|
|
|
|
ngAfterViewInit(): void {
|
|
this.dataSource.cdkVirtualScrollViewport = this.cvsvOrganization;
|
|
}
|
|
|
|
hasChild = (_: number, node: FlatNode) => node.expandable;
|
|
|
|
isLastNode(node: FlatNode, depth: number): boolean {
|
|
const i = this.findNodeIndex(node, depth);
|
|
|
|
if (-1 === i) {
|
|
return false;
|
|
}
|
|
|
|
if (i === this.dataSource.expandedDataSubject.value.length - 1) {
|
|
return true;
|
|
}
|
|
|
|
const n = this.dataSource.expandedDataSubject.value[i];
|
|
|
|
for (
|
|
let idx = i + 1;
|
|
idx < this.dataSource.expandedDataSubject.value.length;
|
|
idx++
|
|
) {
|
|
const element = this.dataSource.expandedDataSubject.value[idx];
|
|
if (n.level === element.level) {
|
|
return false;
|
|
}
|
|
if (n.level > element.level) {
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
appendDivArray(level: number): number[] {
|
|
if (0 === level) {
|
|
return [];
|
|
}
|
|
|
|
return Array.from({ length: level }, (_, i: number) => i);
|
|
}
|
|
|
|
onClickNode(node: OrganizationNode) {
|
|
this.selected.emit(node.deptInfo);
|
|
}
|
|
|
|
private findNodeIndex(node: FlatNode, depth: number): number {
|
|
if (!node) {
|
|
return -1;
|
|
}
|
|
const i = this.dataSource.expandedDataSubject.value.findIndex(n => {
|
|
return node.deptInfo.seq === n.deptInfo.seq;
|
|
});
|
|
|
|
if (0 === depth) {
|
|
return i;
|
|
}
|
|
return this.findNodeIndex(this.findParentNode(i), depth - 1);
|
|
}
|
|
|
|
private findParentNode(index: number): FlatNode {
|
|
const node = this.dataSource.expandedDataSubject.value[index];
|
|
|
|
for (let idx = index - 1; idx >= 0; idx--) {
|
|
const n = this.dataSource.expandedDataSubject.value[idx];
|
|
if (n.level === node.level - 1) {
|
|
return n;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|