virtual scroll of organization is implemented
This commit is contained in:
parent
241fd65d31
commit
50c8c2a02d
|
@ -33,10 +33,10 @@
|
|||
.mat-tab-labels {
|
||||
flex-flow: column;
|
||||
height: 280px;
|
||||
padding-top:10px;
|
||||
padding-top: 10px;
|
||||
.mat-tab-label {
|
||||
height: 80px;
|
||||
padding:0 16px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
.mat-ink-bar {
|
||||
|
@ -63,7 +63,7 @@
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: scale(0.9);
|
||||
transition: transform .3s cubic-bezier(.4,0,0,1);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0, 1);
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
|
@ -82,7 +82,7 @@
|
|||
.mat-tab-label-content {
|
||||
.icon-item {
|
||||
background: #ef4c73;
|
||||
transform: scale(1.0);
|
||||
transform: scale(1);
|
||||
/*svg {
|
||||
stroke: #ef4c73;
|
||||
fill: #ef4c73;
|
||||
|
|
|
@ -71,7 +71,7 @@ export class GroupComponent implements OnInit, OnDestroy {
|
|||
UserInfo | UserInfoSS | UserInfoF | UserInfoDN
|
||||
>();
|
||||
|
||||
@ViewChild('groupExpansionPanel', { static: true })
|
||||
@ViewChild('groupExpansionPanel', { static: false })
|
||||
groupExpansionPanel: GroupExpansionPanelComponent;
|
||||
|
||||
@ViewChild('profileContextMenuTrigger', { static: true })
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
>
|
||||
</ucap-organization-tenant-search>
|
||||
</div>
|
||||
<div class="oraganization-tab" *ngIf="departmentInfoList$ | async">
|
||||
<div class="oraganization-tab">
|
||||
<div class="oraganization-tab-tree">
|
||||
<ucap-organization-tree
|
||||
[oraganizationList]="departmentInfoList$ | async"
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
ElementRef,
|
||||
AfterViewInit,
|
||||
Output,
|
||||
EventEmitter,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ucapAnimations,
|
||||
|
@ -20,7 +20,7 @@ import {
|
|||
AlertDialogData,
|
||||
AlertDialogResult,
|
||||
FileUploadQueueComponent,
|
||||
StringUtil,
|
||||
StringUtil
|
||||
} from '@ucap-webmessenger/ui';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
@ -33,7 +33,7 @@ import {
|
|||
isRecallable,
|
||||
InfoResponse,
|
||||
EventJson,
|
||||
FileEventJson,
|
||||
FileEventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
import * as AppStore from '@app/store';
|
||||
|
@ -47,36 +47,36 @@ import {
|
|||
EnvironmentsInfo,
|
||||
KEY_ENVIRONMENTS_INFO,
|
||||
UserSelectDialogType,
|
||||
RightDrawer,
|
||||
RightDrawer
|
||||
} from '@app/types';
|
||||
import { RoomInfo, UserInfo, RoomType } from '@ucap-webmessenger/protocol-room';
|
||||
import { tap, take, map, catchError } from 'rxjs/operators';
|
||||
import {
|
||||
FileInfo,
|
||||
FormComponent as UCapUiChatFormComponent,
|
||||
FormComponent as UCapUiChatFormComponent
|
||||
} from '@ucap-webmessenger/ui-chat';
|
||||
import { KEY_VER_INFO } from '@app/types/ver-info.type';
|
||||
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
||||
import {
|
||||
MatMenuTrigger,
|
||||
MatSnackBarRef,
|
||||
SimpleSnackBar,
|
||||
SimpleSnackBar
|
||||
} from '@angular/material';
|
||||
import {
|
||||
CommonApiService,
|
||||
FileUploadItem,
|
||||
FileTalkSaveRequest,
|
||||
FileTalkSaveResponse,
|
||||
FileTalkSaveResponse
|
||||
} from '@ucap-webmessenger/api-common';
|
||||
import {
|
||||
CreateChatDialogComponent,
|
||||
CreateChatDialogData,
|
||||
CreateChatDialogResult,
|
||||
CreateChatDialogResult
|
||||
} from '../dialogs/chat/create-chat.dialog.component';
|
||||
import {
|
||||
FileViewerDialogComponent,
|
||||
FileViewerDialogData,
|
||||
FileViewerDialogResult,
|
||||
FileViewerDialogResult
|
||||
} from '@app/layouts/common/dialogs/file-viewer.dialog.component';
|
||||
import { CONST, FileUtil } from '@ucap-webmessenger/core';
|
||||
import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar';
|
||||
|
@ -84,12 +84,12 @@ import { StatusCode } from '@ucap-webmessenger/api';
|
|||
import {
|
||||
EditChatRoomDialogComponent,
|
||||
EditChatRoomDialogResult,
|
||||
EditChatRoomDialogData,
|
||||
EditChatRoomDialogData
|
||||
} from '../dialogs/chat/edit-chat-room.dialog.component';
|
||||
import {
|
||||
SelectGroupDialogComponent,
|
||||
SelectGroupDialogResult,
|
||||
SelectGroupDialogData,
|
||||
SelectGroupDialogData
|
||||
} from '../dialogs/group/select-group.dialog.component';
|
||||
import { GroupDetailData } from '@ucap-webmessenger/protocol-sync';
|
||||
|
||||
|
@ -97,7 +97,7 @@ import { GroupDetailData } from '@ucap-webmessenger/protocol-sync';
|
|||
selector: 'app-layout-messenger-messages',
|
||||
templateUrl: './messages.component.html',
|
||||
styleUrls: ['./messages.component.scss'],
|
||||
animations: ucapAnimations,
|
||||
animations: ucapAnimations
|
||||
})
|
||||
export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
@Output()
|
||||
|
@ -379,7 +379,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
// duration: 3000,
|
||||
verticalPosition: 'bottom',
|
||||
horizontalPosition: 'center',
|
||||
panelClass: ['chat-snackbar-class'],
|
||||
panelClass: ['chat-snackbar-class']
|
||||
});
|
||||
this.snackBarPreviewEvent.onAction().subscribe(() => {
|
||||
this.setEventMoreInit();
|
||||
|
@ -420,7 +420,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
EventStore.info({
|
||||
roomSeq: this.roomInfo.roomSeq,
|
||||
baseSeq: seq,
|
||||
requestCount: CONST.EVENT_INFO_READ_COUNT,
|
||||
requestCount: CONST.EVENT_INFO_READ_COUNT
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -438,8 +438,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
width: '360px',
|
||||
data: {
|
||||
title: 'Alert',
|
||||
message: `대화내용을 입력해주세요.`,
|
||||
},
|
||||
message: `대화내용을 입력해주세요.`
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -453,8 +453,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
roomSeq: this.roomInfo.roomSeq,
|
||||
eventType: EventType.MassText,
|
||||
// sentMessage: message.replace(/\n/gi, '\r\n')
|
||||
sentMessage: message,
|
||||
},
|
||||
sentMessage: message
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
@ -464,8 +464,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
req: {
|
||||
roomSeq: this.roomInfo.roomSeq,
|
||||
eventType: EventType.Character,
|
||||
sentMessage: message,
|
||||
},
|
||||
sentMessage: message
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -479,7 +479,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
onMassDetail(value: number) {
|
||||
this.store.dispatch(
|
||||
ChatStore.selectedMassDetail({
|
||||
massEventSeq: value,
|
||||
massEventSeq: value
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -491,7 +491,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
FileViewerDialogResult
|
||||
>(FileViewerDialogComponent, {
|
||||
position: {
|
||||
top: '30px',
|
||||
top: '30px'
|
||||
},
|
||||
maxWidth: '100vw',
|
||||
maxHeight: '100vh',
|
||||
|
@ -504,8 +504,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
downloadUrl: this.sessionVerInfo.downloadUrl,
|
||||
deviceType: this.environmentsInfo.deviceType,
|
||||
token: this.loginRes.tokenString,
|
||||
userSeq: this.loginRes.userSeq,
|
||||
},
|
||||
userSeq: this.loginRes.userSeq
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -531,7 +531,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
|
||||
const info = {
|
||||
senderSeq: this.loginRes.userSeq,
|
||||
roomSeq: this.roomInfo.roomSeq,
|
||||
roomSeq: this.roomInfo.roomSeq
|
||||
};
|
||||
|
||||
const allObservables: Observable<FileTalkSaveResponse>[] = [];
|
||||
|
@ -552,7 +552,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
'rv',
|
||||
'ts',
|
||||
'webm',
|
||||
'wmv',
|
||||
'wmv'
|
||||
].indexOf(FileUtil.getExtension(fileUploadItem.file.name))
|
||||
) {
|
||||
thumbnail = await FileUtil.thumbnail(fileUploadItem.file);
|
||||
|
@ -567,7 +567,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
file: fileUploadItem.file,
|
||||
fileName: fileUploadItem.file.name,
|
||||
thumb: thumbnail,
|
||||
fileUploadItem,
|
||||
fileUploadItem
|
||||
};
|
||||
|
||||
allObservables.push(
|
||||
|
@ -599,8 +599,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
req: {
|
||||
roomSeq: info.roomSeq,
|
||||
eventType: EventType.File,
|
||||
sentMessage: res.returnJson,
|
||||
},
|
||||
sentMessage: res.returnJson
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -626,7 +626,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.messageContextMenuTrigger.menu.focusFirstItem('mouse');
|
||||
this.messageContextMenuTrigger.menuData = {
|
||||
message: params.message,
|
||||
loginRes: this.loginRes,
|
||||
loginRes: this.loginRes
|
||||
};
|
||||
this.messageContextMenuTrigger.openMenu();
|
||||
}
|
||||
|
@ -646,7 +646,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.snackBarService.open('클립보드에 복사되었습니다.', '', {
|
||||
duration: 3000,
|
||||
verticalPosition: 'top',
|
||||
horizontalPosition: 'center',
|
||||
horizontalPosition: 'center'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -658,7 +658,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
userSeq: this.loginRes.userSeq,
|
||||
deviceType: this.environmentsInfo.deviceType,
|
||||
token: this.loginRes.tokenString,
|
||||
eventMassSeq: message.seq,
|
||||
eventMassSeq: message.seq
|
||||
})
|
||||
.pipe(take(1))
|
||||
.subscribe(res => {
|
||||
|
@ -669,7 +669,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
{
|
||||
duration: 3000,
|
||||
verticalPosition: 'top',
|
||||
horizontalPosition: 'center',
|
||||
horizontalPosition: 'center'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -693,8 +693,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
data: {
|
||||
type: UserSelectDialogType.MessageForward,
|
||||
title: 'MessageForward',
|
||||
ignoreRoom: [this.roomInfo],
|
||||
},
|
||||
ignoreRoom: [this.roomInfo]
|
||||
}
|
||||
});
|
||||
|
||||
if (!!result && !!result.choice && result.choice) {
|
||||
|
@ -718,10 +718,10 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
req: {
|
||||
roomSeq: '-999',
|
||||
eventType: message.type,
|
||||
sentMessage: message.sentMessage,
|
||||
sentMessage: message.sentMessage
|
||||
},
|
||||
trgtUserSeqs: userSeqs,
|
||||
trgtRoomSeq: roomSeq,
|
||||
trgtRoomSeq: roomSeq
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -737,9 +737,9 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
req: {
|
||||
roomSeq: '-999',
|
||||
eventType: message.type,
|
||||
sentMessage: message.sentMessage,
|
||||
sentMessage: message.sentMessage
|
||||
},
|
||||
trgtUserSeqs: [this.loginRes.talkWithMeBotSeq],
|
||||
trgtUserSeqs: [this.loginRes.talkWithMeBotSeq]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -755,15 +755,15 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
width: '220px',
|
||||
data: {
|
||||
title: 'Delete',
|
||||
html: `선택한 메시지를 삭제하시겠습니까?<br/>삭제된 메시지는 내 대화방에서만 적용되며 상대방의 대화방에서는 삭제되지 않습니다.`,
|
||||
},
|
||||
html: `선택한 메시지를 삭제하시겠습니까?<br/>삭제된 메시지는 내 대화방에서만 적용되며 상대방의 대화방에서는 삭제되지 않습니다.`
|
||||
}
|
||||
});
|
||||
|
||||
if (!!result && !!result.choice && result.choice) {
|
||||
this.store.dispatch(
|
||||
EventStore.del({
|
||||
roomSeq: this.roomInfo.roomSeq,
|
||||
eventSeq: message.seq,
|
||||
eventSeq: message.seq
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -779,8 +779,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
width: '220px',
|
||||
data: {
|
||||
title: 'ReCall',
|
||||
html: `해당 대화를 회수하시겠습니까?<br/>상대방 대화창에서도 회수됩니다.`,
|
||||
},
|
||||
html: `해당 대화를 회수하시겠습니까?<br/>상대방 대화창에서도 회수됩니다.`
|
||||
}
|
||||
});
|
||||
|
||||
if (!!result && !!result.choice && result.choice) {
|
||||
|
@ -788,7 +788,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
EventStore.cancel({
|
||||
roomSeq: this.roomInfo.roomSeq,
|
||||
eventSeq: message.seq,
|
||||
deviceType: this.environmentsInfo.deviceType,
|
||||
deviceType: this.environmentsInfo.deviceType
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -805,7 +805,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
{
|
||||
this.store.dispatch(
|
||||
ChatStore.selectedRightDrawer({
|
||||
req: RightDrawer.AlbumBox,
|
||||
req: RightDrawer.AlbumBox
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -814,7 +814,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
{
|
||||
this.store.dispatch(
|
||||
ChatStore.selectedRightDrawer({
|
||||
req: RightDrawer.FileBox,
|
||||
req: RightDrawer.FileBox
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -823,7 +823,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
{
|
||||
this.store.dispatch(
|
||||
ChatStore.selectedRightDrawer({
|
||||
req: RightDrawer.RoomUser,
|
||||
req: RightDrawer.RoomUser
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -841,8 +841,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
title: 'Edit Chat Member',
|
||||
curRoomUser: this.userInfoList.filter(
|
||||
user => user.seq !== this.loginRes.userSeq
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
if (!!result && !!result.choice && result.choice) {
|
||||
|
@ -864,8 +864,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
RoomStore.inviteOrOpen({
|
||||
req: {
|
||||
divCd: 'Invite',
|
||||
userSeqs,
|
||||
},
|
||||
userSeqs
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -881,8 +881,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
>(SelectGroupDialogComponent, {
|
||||
width: '600px',
|
||||
data: {
|
||||
title: 'Group Select',
|
||||
},
|
||||
title: 'Group Select'
|
||||
}
|
||||
});
|
||||
|
||||
if (!!result && !!result.choice && result.choice) {
|
||||
|
@ -899,7 +899,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.store.dispatch(
|
||||
SyncStore.updateGroupMember({
|
||||
oldGroup,
|
||||
trgtUserSeq,
|
||||
trgtUserSeq
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -916,8 +916,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
width: '600px',
|
||||
data: {
|
||||
title: 'Edit Chat Room',
|
||||
roomInfo: this.roomInfo,
|
||||
},
|
||||
roomInfo: this.roomInfo
|
||||
}
|
||||
});
|
||||
|
||||
if (!!result && !!result.choice && result.choice) {
|
||||
|
@ -934,8 +934,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
roomName,
|
||||
receiveAlarm: roomInfo.receiveAlarm,
|
||||
syncAll:
|
||||
roomNameChangeTarget.toUpperCase() === 'ALL' ? true : false,
|
||||
},
|
||||
roomNameChangeTarget.toUpperCase() === 'ALL' ? true : false
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -947,7 +947,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.store.dispatch(
|
||||
RoomStore.updateTimeRoomInterval({
|
||||
roomSeq: roomInfo.roomSeq,
|
||||
timerInterval: timeRoomInterval,
|
||||
timerInterval: timeRoomInterval
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="chat-messages">
|
||||
<div class="chat-messages" #scrollMe>
|
||||
<!-- <div class="message-row" *ngIf="eventRemain">
|
||||
<button mat-button (click)="onClickMore($event)">이전 대화 보기</button>
|
||||
</div> -->
|
||||
|
|
|
@ -1,17 +1,27 @@
|
|||
<mat-tree
|
||||
<cdk-virtual-scroll-viewport #cvsvOrganization itemSize="48" fxFlexFill>
|
||||
<ng-container
|
||||
*cdkVirtualFor="let node of dataSource.expandedData$"
|
||||
></ng-container>
|
||||
<mat-tree
|
||||
#orgranizationTree
|
||||
[dataSource]="dataSource"
|
||||
[treeControl]="treeControl"
|
||||
class="organization-tree"
|
||||
>
|
||||
<mat-nested-tree-node *matTreeNodeDef="let node">
|
||||
>
|
||||
<!-- This is the tree node template for leaf nodes -->
|
||||
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
|
||||
<li>
|
||||
<div class="mat-tree-node" (click)="onClickNode(node)">
|
||||
{{ node.title }}
|
||||
{{ node.name }}
|
||||
</div>
|
||||
</li>
|
||||
</mat-nested-tree-node>
|
||||
<mat-nested-tree-node *matTreeNodeDef="let node; when: hasChildren" class="tree-node-frame">
|
||||
</mat-tree-node>
|
||||
<!-- This is the tree node template for expandable nodes -->
|
||||
<mat-tree-node
|
||||
*matTreeNodeDef="let node; when: hasChild"
|
||||
matTreeNodePadding
|
||||
class="tree-node-frame"
|
||||
>
|
||||
<li>
|
||||
<div class="mat-tree-node" (click)="onClickNode(node)" class="path">
|
||||
<span class="horizontal-line"></span>
|
||||
|
@ -26,10 +36,12 @@
|
|||
{{ treeControl.isExpanded(node) ? 'remove' : 'add' }}
|
||||
</mat-icon>
|
||||
</button>
|
||||
<span class="dept-name">{{ node.title }}</span>
|
||||
<span class="dept-name">{{ node.name }}</span>
|
||||
</div>
|
||||
<ul
|
||||
[class.organization-tree-node-invisible]="!treeControl.isExpanded(node)"
|
||||
[class.organization-tree-node-invisible]="
|
||||
!treeControl.isExpanded(node)
|
||||
"
|
||||
>
|
||||
<div *ngIf="treeControl.isExpanded(node)" class="boxnone">
|
||||
<div class="vertical-line"></div>
|
||||
|
@ -37,5 +49,6 @@
|
|||
</div>
|
||||
</ul>
|
||||
</li>
|
||||
</mat-nested-tree-node>
|
||||
</mat-tree>
|
||||
</mat-tree-node>
|
||||
</mat-tree>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@charset 'utf-8';
|
||||
.organization-tree {
|
||||
padding:10px;
|
||||
padding: 10px;
|
||||
ul,
|
||||
li {
|
||||
margin-top: 0;
|
||||
|
@ -13,83 +13,83 @@
|
|||
}
|
||||
}
|
||||
|
||||
.tree-node-frame{
|
||||
li{
|
||||
.path{
|
||||
.horizontal-line{
|
||||
display:none;
|
||||
.tree-node-frame {
|
||||
li {
|
||||
.path {
|
||||
.horizontal-line {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mat-tree-node {
|
||||
min-height: 30px;
|
||||
font-size: 13px;
|
||||
padding-left:20px;
|
||||
margin-top:4px;
|
||||
padding-left: 20px;
|
||||
margin-top: 4px;
|
||||
&:hover {
|
||||
background-color: #f4f4f4;
|
||||
border:1px solid #cccccc;
|
||||
border-radius:4px;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(32, 33, 36, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul .tree-node-frame li .path > .horizontal-line{
|
||||
display:inline-block;
|
||||
ul .tree-node-frame li .path > .horizontal-line {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.boxnone{
|
||||
position:relative;
|
||||
.vertical-line{
|
||||
background: rgba(189,189,189,.4);
|
||||
.boxnone {
|
||||
position: relative;
|
||||
.vertical-line {
|
||||
background: rgba(189, 189, 189, 0.4);
|
||||
bottom: 6px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 2px;
|
||||
}
|
||||
.mat-nested-tree-node:last-child{
|
||||
padding-bottom:10px;
|
||||
.mat-tree-node:last-child {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.path {
|
||||
padding: 6px 4px;
|
||||
+ ul{
|
||||
li:last-chlid{
|
||||
border-bottom:1px solid #dddddd;
|
||||
+ ul {
|
||||
li:last-chlid {
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
}
|
||||
.horizontal-line{
|
||||
width:10px;
|
||||
height:1px;
|
||||
.horizontal-line {
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
background-color: #dddddd;
|
||||
display:inline-block;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left:-10px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
.mat-icon-button{
|
||||
.mat-icon-button {
|
||||
padding: 0;
|
||||
min-width: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
flex-shrink: 0;
|
||||
line-height: 20px;
|
||||
.mat-icon-rtl-mirror{
|
||||
.mat-icon-rtl-mirror {
|
||||
border: 1px solid #dddddd;
|
||||
padding: 2px;
|
||||
font-size: 14px;
|
||||
min-width: 14px;
|
||||
min-height: 14px;
|
||||
line-height: 14px;
|
||||
width:20px;
|
||||
height:20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
box-shadow: 0 2px 1px rgba(48, 48, 48, 0.2);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.dept-name{
|
||||
padding-left:10px;
|
||||
.dept-name {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,39 +8,26 @@ import {
|
|||
EventEmitter,
|
||||
AfterViewInit
|
||||
} from '@angular/core';
|
||||
import { MatTreeNestedDataSource, MatTree } from '@angular/material';
|
||||
import { NestedTreeControl } from '@angular/cdk/tree';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
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';
|
||||
|
||||
export class OraganizationNode {
|
||||
private childNodeBehaviorSubject: BehaviorSubject<OraganizationNode[]>;
|
||||
private childNodeList: OraganizationNode[];
|
||||
interface OrganizationNode {
|
||||
deptInfo: DeptInfo;
|
||||
name: string;
|
||||
children?: OrganizationNode[];
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this.deptInfo.name;
|
||||
}
|
||||
|
||||
get children(): BehaviorSubject<OraganizationNode[]> {
|
||||
if (!this.childNodeBehaviorSubject) {
|
||||
this.childNodeBehaviorSubject = new BehaviorSubject(
|
||||
undefined === this.childNodeList ? [] : this.childNodeList
|
||||
);
|
||||
}
|
||||
return this.childNodeBehaviorSubject;
|
||||
}
|
||||
|
||||
constructor(public deptInfo: DeptInfo) {}
|
||||
|
||||
addChild(childNode: OraganizationNode) {
|
||||
if (!this.childNodeList) {
|
||||
this.childNodeList = [];
|
||||
}
|
||||
this.childNodeList.push(childNode);
|
||||
}
|
||||
/** Flat node with expandable and level information */
|
||||
interface FlatNode {
|
||||
expandable: boolean;
|
||||
name: string;
|
||||
level: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -55,87 +42,96 @@ export class TreeComponent implements OnInit, AfterViewInit {
|
|||
@Input()
|
||||
loginRes: LoginResponse;
|
||||
@Input()
|
||||
set oraganizationList(deptInfo: DeptInfo[]) {
|
||||
const nodeMap = new Map<number, OraganizationNode>();
|
||||
const rootNodeList: OraganizationNode[] = [];
|
||||
const remainChildNodeList: OraganizationNode[] = [];
|
||||
let myNode: OraganizationNode;
|
||||
|
||||
deptInfo.forEach(value => {
|
||||
const node = new OraganizationNode(value);
|
||||
if (nodeMap.has(value.seq)) {
|
||||
this.logger.warn('duplicate seq', value.seq);
|
||||
set oraganizationList(deptInfoList: DeptInfo[]) {
|
||||
if (!deptInfoList || 0 === deptInfoList.length) {
|
||||
return;
|
||||
}
|
||||
nodeMap.set(value.seq, node);
|
||||
const nodeMap = new Map<number, OrganizationNode>();
|
||||
const rootNodeList: OrganizationNode[] = [];
|
||||
const remainChildNodeList: OrganizationNode[] = [];
|
||||
let myNode: OrganizationNode;
|
||||
|
||||
if (value.seq === this.loginRes.departmentCode) {
|
||||
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 === value.parentSeq) {
|
||||
if (0 === deptInfo.parentSeq) {
|
||||
rootNodeList.push(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeMap.has(value.parentSeq)) {
|
||||
nodeMap.get(value.parentSeq).addChild(node);
|
||||
if (nodeMap.has(deptInfo.parentSeq)) {
|
||||
nodeMap.get(deptInfo.parentSeq).children.push(node);
|
||||
} else {
|
||||
remainChildNodeList.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
remainChildNodeList.forEach(value => {
|
||||
if (nodeMap.has(value.deptInfo.parentSeq)) {
|
||||
nodeMap.get(value.deptInfo.parentSeq).addChild(value);
|
||||
remainChildNodeList.forEach(node => {
|
||||
if (nodeMap.has(node.deptInfo.parentSeq)) {
|
||||
nodeMap.get(node.deptInfo.parentSeq).children.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
this.dataSource.data = rootNodeList;
|
||||
// console.log('myNode', myNode);
|
||||
// console.log('this.dataSource.data', this.dataSource.data[0]);
|
||||
// this.treeControl.expandDescendants(this.dataSource.data[2]);
|
||||
// console.log('this.dataSource', this.dataSource);
|
||||
// console.log('this.dataSource.data', this.dataSource.data);
|
||||
// console.log('this.treeControl', this.treeControl);
|
||||
// console.log('this.treeControl.dataNodes', this.treeControl.dataNodes);
|
||||
|
||||
// const myNode = this.treeControl.dataNodes.filter(
|
||||
// node => node.deptInfo.seq === this.loginRes.departmentCode
|
||||
// );
|
||||
// if (!!myNode && myNode.length > 0) {
|
||||
// this.treeControl.expand(myNode[0]);
|
||||
// }
|
||||
}
|
||||
|
||||
@ViewChild('orgranizationTree', { static: true })
|
||||
orgranizationTree: MatTree<OraganizationNode>;
|
||||
@ViewChild('cvsvOrganization', { static: false })
|
||||
cvsvOrganization: CdkVirtualScrollViewport;
|
||||
|
||||
levels = new Map<OraganizationNode, number>();
|
||||
treeControl: NestedTreeControl<OraganizationNode>;
|
||||
@ViewChild('orgranizationTree', { static: false })
|
||||
orgranizationTree: MatTree<OrganizationNode>;
|
||||
|
||||
dataSource: MatTreeNestedDataSource<OraganizationNode>;
|
||||
treeControl: FlatTreeControl<FlatNode>;
|
||||
treeFlattener: MatTreeFlattener<OrganizationNode, FlatNode>;
|
||||
dataSource: VirtualScrollTreeFlatDataSource<OrganizationNode, FlatNode>;
|
||||
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private logger: NGXLogger
|
||||
) {
|
||||
this.treeControl = new NestedTreeControl<OraganizationNode>(
|
||||
this.getChildren
|
||||
this.treeControl = new FlatTreeControl<FlatNode>(
|
||||
node => node.level,
|
||||
node => node.expandable
|
||||
);
|
||||
this.dataSource = new MatTreeNestedDataSource();
|
||||
this.treeFlattener = new MatTreeFlattener<OrganizationNode, FlatNode>(
|
||||
(node: OrganizationNode, level: number) => {
|
||||
return {
|
||||
expandable: !!node.children && node.children.length > 0,
|
||||
name: node.name,
|
||||
level
|
||||
};
|
||||
},
|
||||
node => node.level,
|
||||
node => node.expandable,
|
||||
node => node.children
|
||||
);
|
||||
this.dataSource = new VirtualScrollTreeFlatDataSource<
|
||||
OrganizationNode,
|
||||
FlatNode
|
||||
>(this.treeControl, this.treeFlattener);
|
||||
}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
ngAfterViewInit(): void {
|
||||
this.dataSource.cdkVirtualScrollViewport = this.cvsvOrganization;
|
||||
}
|
||||
|
||||
getChildren = (node: OraganizationNode) => node.children;
|
||||
hasChild = (_: number, node: FlatNode) => node.expandable;
|
||||
|
||||
hasChildren = (index: number, node: OraganizationNode) =>
|
||||
0 < node.children.value.length;
|
||||
|
||||
onClickNode(node: OraganizationNode) {
|
||||
onClickNode(node: OrganizationNode) {
|
||||
this.selected.emit(node.deptInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import { NgModule, ModuleWithProviders } from '@angular/core';
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
@ -21,6 +23,7 @@ const SERVICES = [];
|
|||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
|
||||
FlexLayoutModule,
|
||||
ScrollingModule,
|
||||
|
||||
MatButtonModule,
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
|
||||
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
merge,
|
||||
Observable,
|
||||
Subject,
|
||||
Subscription
|
||||
} from 'rxjs';
|
||||
import { map, share } from 'rxjs/operators';
|
||||
import { MatTreeFlattener } from '@angular/material';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
|
||||
export class VirtualScrollTreeFlatDataSource<T, F> extends DataSource<F> {
|
||||
private flattenedDataSubject = new BehaviorSubject<F[]>([]);
|
||||
|
||||
private expandedDataSubject = new BehaviorSubject<F[]>([]);
|
||||
expandedData$: Observable<F[]>;
|
||||
|
||||
private connectSubject: Subject<F[]>;
|
||||
private dataChangeSubscription: Subscription;
|
||||
private rangeChangeSubscription: Subscription;
|
||||
|
||||
private rangeSubject: BehaviorSubject<{
|
||||
start: number;
|
||||
end: number;
|
||||
}>;
|
||||
|
||||
// tslint:disable-next-line: variable-name
|
||||
private _cdkVirtualScrollViewport: CdkVirtualScrollViewport;
|
||||
set cdkVirtualScrollViewport(
|
||||
cdkVirtualScrollViewport: CdkVirtualScrollViewport
|
||||
) {
|
||||
if (!cdkVirtualScrollViewport) {
|
||||
return;
|
||||
}
|
||||
this._cdkVirtualScrollViewport = cdkVirtualScrollViewport;
|
||||
this.rangeSubject = new BehaviorSubject<{ start: number; end: number }>({
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
|
||||
this.rangeChangeSubscription = cdkVirtualScrollViewport.renderedRangeStream.subscribe(
|
||||
range => {
|
||||
this.rangeSubject.next({
|
||||
start: range.start,
|
||||
end: range.end
|
||||
});
|
||||
if (!!this.connectSubject) {
|
||||
this.connectSubject.next(
|
||||
this.expandedDataSubject.value.slice(
|
||||
this.rangeSubject.value.start,
|
||||
this.rangeSubject.value.end
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: variable-name
|
||||
private _data: BehaviorSubject<T[]>;
|
||||
get data() {
|
||||
return this._data.value;
|
||||
}
|
||||
set data(value: T[]) {
|
||||
this._data.next(value);
|
||||
this.flattenedDataSubject.next(this.treeFlattener.flattenNodes(this.data));
|
||||
this.treeControl.dataNodes = this.flattenedDataSubject.value;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private treeControl: FlatTreeControl<F>,
|
||||
private treeFlattener: MatTreeFlattener<T, F>,
|
||||
initialData: T[] = []
|
||||
) {
|
||||
super();
|
||||
this._data = new BehaviorSubject<T[]>(initialData);
|
||||
this.expandedData$ = this.expandedDataSubject.asObservable().pipe(share());
|
||||
}
|
||||
|
||||
connect(collectionViewer: CollectionViewer): Observable<F[]> {
|
||||
this.connectSubject = new Subject<F[]>();
|
||||
|
||||
this.dataChangeSubscription = merge(
|
||||
collectionViewer.viewChange,
|
||||
this.treeControl.expansionModel.changed,
|
||||
this.flattenedDataSubject
|
||||
)
|
||||
.pipe(
|
||||
map(() => {
|
||||
this.expandedDataSubject.next(
|
||||
this.treeFlattener.expandFlattenedNodes(
|
||||
this.flattenedDataSubject.value,
|
||||
this.treeControl
|
||||
)
|
||||
);
|
||||
|
||||
return !this.rangeSubject
|
||||
? this.expandedDataSubject.value
|
||||
: this.expandedDataSubject.value.slice(
|
||||
this.rangeSubject.value.start,
|
||||
this.rangeSubject.value.end
|
||||
);
|
||||
})
|
||||
)
|
||||
.subscribe(datas => {
|
||||
this.connectSubject.next(datas);
|
||||
if (!!this._cdkVirtualScrollViewport) {
|
||||
this._cdkVirtualScrollViewport.checkViewportSize();
|
||||
}
|
||||
});
|
||||
|
||||
return this.connectSubject.asObservable();
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
if (!!this.dataChangeSubscription) {
|
||||
this.dataChangeSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (!!this.rangeChangeSubscription) {
|
||||
this.rangeChangeSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@ export * from './lib/components/file-upload-queue.component';
|
|||
export * from './lib/components/file-viewer.component';
|
||||
export * from './lib/components/float-action-button.component';
|
||||
|
||||
export * from './lib/data-source/virtual-scroll-tree-flat.data-source';
|
||||
|
||||
export * from './lib/dialogs/alert.dialog.component';
|
||||
export * from './lib/dialogs/confirm.dialog.component';
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user