1. 조직도 검색시 200건 이상 조회 가능하도록 수정,

2. 조직도 테이블 virture scroll 적용.
This commit is contained in:
leejinho 2020-04-03 10:33:02 +09:00
parent e3b26549cc
commit 7eb7d85da9
6 changed files with 195 additions and 75 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "ucap-webmessenger", "name": "ucap-webmessenger",
"version": "1.0.16", "version": "1.0.0",
"author": { "author": {
"name": "LG CNS", "name": "LG CNS",
"email": "lgucap@lgcns.com" "email": "lgucap@lgcns.com"
@ -137,6 +137,7 @@
"moment": "^2.24.0", "moment": "^2.24.0",
"moment-timezone": "^0.5.27", "moment-timezone": "^0.5.27",
"ng-packagr": "^5.7.1", "ng-packagr": "^5.7.1",
"ng-table-virtual-scroll": "^1.3.1",
"ngrx-store-freeze": "^0.2.4", "ngrx-store-freeze": "^0.2.4",
"ngx-logger": "^4.0.8", "ngx-logger": "^4.0.8",
"ngx-perfect-scrollbar": "^8.0.0", "ngx-perfect-scrollbar": "^8.0.0",

View File

@ -95,6 +95,8 @@ export const searchDeptUser = createAction(
props<{ props<{
companyCode: string; companyCode: string;
search: string; search: string;
pageCurrent?: number;
userList?: UserInfoSS[];
}>() }>()
); );
@ -119,6 +121,8 @@ export const integrateSearchDeptUser = createAction(
props<{ props<{
companyCode: string; companyCode: string;
search: string; search: string;
pageCurrent?: number;
userList?: UserInfoSS[];
}>() }>()
); );

View File

@ -10,8 +10,7 @@ import {
map, map,
tap, tap,
switchMap, switchMap,
withLatestFrom, withLatestFrom
take
} from 'rxjs/operators'; } from 'rxjs/operators';
import { import {
@ -28,7 +27,6 @@ import {
selectedDeptSuccess, selectedDeptSuccess,
searchDeptUser, searchDeptUser,
searchDeptUserSuccess, searchDeptUserSuccess,
clearSearchDeptUser,
integrateSearchDeptUser, integrateSearchDeptUser,
integrateSearchDeptUserSuccess, integrateSearchDeptUserSuccess,
integrateSearchDeptUserFailure, integrateSearchDeptUserFailure,
@ -45,13 +43,12 @@ import {
SSVC_TYPE_QUERY_DEPT_USER_DATA, SSVC_TYPE_QUERY_DEPT_USER_DATA,
DeptUserData, DeptUserData,
SSVC_TYPE_QUERY_DEPT_USER_RES, SSVC_TYPE_QUERY_DEPT_USER_RES,
DeptUserResponse, DeptSearchType,
DeptSearchType DeptUserRequest
} from '@ucap-webmessenger/protocol-query'; } from '@ucap-webmessenger/protocol-query';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { KEY_LOGIN_RES_INFO } from '@app/types';
import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { OrganizationService } from '@ucap-webmessenger/ui-organization'; import { OrganizationService } from '@ucap-webmessenger/ui-organization';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
@ -171,38 +168,106 @@ export class Effects {
) )
), ),
switchMap(([req, loginResInfo]) => { switchMap(([req, loginResInfo]) => {
return this.organizationService const pageListCount = 180;
.getDeptUser({ const pageCurrent = !!req.pageCurrent ? req.pageCurrent : 1;
divCd: 'INT_SRCH', const request = {
companyCode: req.companyCode, divCd: 'INT_SRCH',
search: req.search, companyCode: req.companyCode,
searchRange: DeptSearchType.All, search: req.search,
senderCompanyCode: loginResInfo.companyCode, searchRange: DeptSearchType.All,
senderEmployeeType: loginResInfo.userInfo.employeeType senderCompanyCode: loginResInfo.companyCode,
}) senderEmployeeType: loginResInfo.userInfo.employeeType,
.pipe( pageListCount,
map(datas => { pageCurrent
const userInfos: UserInfoSS[] = datas.userInfos; } as DeptUserRequest;
this.store.dispatch(
integrateSearchDeptUserSuccess({
userInfos
})
);
// 검색 결과에 따른 프레즌스 조회. let mergedUserList: UserInfoSS[] = !!req.userList ? req.userList : [];
const userSeqList: number[] = [];
userInfos.map(user => userSeqList.push(user.seq)); const userInfos: UserInfoSS[] = [];
if (userSeqList.length > 0) { return this.queryProtocolService.deptUser(request).pipe(
this.store.dispatch( map(res => {
StatusStore.bulkInfo({ switch (res.SSVC_TYPE) {
divCd: 'inttrSrch', case SSVC_TYPE_QUERY_DEPT_USER_DATA:
userSeqs: userSeqList userInfos.push(...(res as DeptUserData).userInfos);
}) break;
); case SSVC_TYPE_QUERY_DEPT_USER_RES:
} if (!!userInfos && userInfos.length > 0) {
}), mergedUserList = [...mergedUserList, ...userInfos];
catchError(error => of(integrateSearchDeptUserFailure({ error }))) }
);
// 검색 결과에 따른 프레즌스 조회.
const userSeqList: number[] = [];
userInfos.map(user => userSeqList.push(user.seq));
if (userSeqList.length > 0) {
this.store.dispatch(
StatusStore.bulkInfo({
divCd: 'inttrSrch',
userSeqs: userSeqList
})
);
}
// 재귀 할지 판단.
if (!!userInfos && userInfos.length >= pageListCount) {
// 추가 조회.
this.store.dispatch(
integrateSearchDeptUser({
...req,
pageCurrent: pageCurrent + 1,
userList: mergedUserList
})
);
} else {
mergedUserList.sort((a, b) =>
a.order < b.order
? -1
: a.order > b.order
? 1
: a.name < b.name
? -1
: a.name > b.name
? 1
: 0
);
// 최종 조회건.
this.store.dispatch(
integrateSearchDeptUserSuccess({
userInfos: mergedUserList
})
);
}
break;
}
}),
catchError(error => of(integrateSearchDeptUserFailure({ error })))
);
// return this.organizationService
// .getDeptUser(request)
// .pipe(
// map(datas => {
// const userInfos: UserInfoSS[] = datas.userInfos;
// this.store.dispatch(
// integrateSearchDeptUserSuccess({
// userInfos
// })
// );
// // 검색 결과에 따른 프레즌스 조회.
// const userSeqList: number[] = [];
// userInfos.map(user => userSeqList.push(user.seq));
// if (userSeqList.length > 0) {
// this.store.dispatch(
// StatusStore.bulkInfo({
// divCd: 'inttrSrch',
// userSeqs: userSeqList
// })
// );
// }
// }),
// catchError(error => of(integrateSearchDeptUserFailure({ error })))
// );
}) })
); );
}, },
@ -222,38 +287,80 @@ export class Effects {
) )
), ),
switchMap(([req, loginResInfo]) => { switchMap(([req, loginResInfo]) => {
return this.organizationService const pageListCount = 180;
.getDeptUser({ const pageCurrent = !!req.pageCurrent ? req.pageCurrent : 1;
divCd: 'ORG_SRCH', const request = {
companyCode: req.companyCode, divCd: 'ORG_SRCH',
search: req.search, companyCode: req.companyCode,
searchRange: DeptSearchType.All, search: req.search,
senderCompanyCode: loginResInfo.companyCode, searchRange: DeptSearchType.All,
senderEmployeeType: loginResInfo.userInfo.employeeType senderCompanyCode: loginResInfo.companyCode,
}) senderEmployeeType: loginResInfo.userInfo.employeeType,
.pipe( pageListCount,
map(datas => { pageCurrent
const userInfos: UserInfoSS[] = datas.userInfos; } as DeptUserRequest;
this.store.dispatch(
searchDeptUserSuccess({
userInfos
})
);
// 검색 결과에 따른 프레즌스 조회. let mergedUserList: UserInfoSS[] = !!req.userList ? req.userList : [];
const userSeqList: number[] = [];
userInfos.map(user => userSeqList.push(user.seq)); const userInfos: UserInfoSS[] = [];
if (userSeqList.length > 0) { return this.queryProtocolService.deptUser(request).pipe(
this.store.dispatch( map(res => {
StatusStore.bulkInfo({ switch (res.SSVC_TYPE) {
divCd: 'orgtrSrch', case SSVC_TYPE_QUERY_DEPT_USER_DATA:
userSeqs: userSeqList userInfos.push(...(res as DeptUserData).userInfos);
}) break;
); case SSVC_TYPE_QUERY_DEPT_USER_RES:
} if (!!userInfos && userInfos.length > 0) {
}), mergedUserList = [...mergedUserList, ...userInfos];
catchError(error => of(deptUserFailure({ error }))) }
);
// 검색 결과에 따른 프레즌스 조회.
const userSeqList: number[] = [];
userInfos.map(user => userSeqList.push(user.seq));
if (userSeqList.length > 0) {
this.store.dispatch(
StatusStore.bulkInfo({
divCd: 'orgtrSrch',
userSeqs: userSeqList
})
);
}
// 재귀 할지 판단.
if (!!userInfos && userInfos.length >= pageListCount) {
// 추가 조회.
this.store.dispatch(
searchDeptUser({
...req,
pageCurrent: pageCurrent + 1,
userList: mergedUserList
})
);
} else {
mergedUserList.sort((a, b) =>
a.order < b.order
? -1
: a.order > b.order
? 1
: a.name < b.name
? -1
: a.name > b.name
? 1
: 0
);
// 최종 조회건.
this.store.dispatch(
searchDeptUserSuccess({
userInfos: mergedUserList
})
);
}
break;
}
}),
catchError(error => of(deptUserFailure({ error })))
);
}) })
); );
}, },

View File

@ -1,11 +1,13 @@
<perfect-scrollbar <cdk-virtual-scroll-viewport
class="scrollbar" perfectScrollbar
*ngIf="!!sortedData && 0 < sortedData.length" tvsItemSize="53"
headerHeight="56"
style="height: 100%;"
> >
<table <table
mat-table mat-table
matSort matSort
[dataSource]="sortedData" [dataSource]="dataSource"
(matSortChange)="sortData($event)" (matSortChange)="sortData($event)"
> >
<ng-container matColumnDef="profileImage"> <ng-container matColumnDef="profileImage">
@ -151,7 +153,7 @@
</div> </div>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="checkable" stickyEnd> <ng-container matColumnDef="checkable">
<th mat-header-cell *matHeaderCellDef> <th mat-header-cell *matHeaderCellDef>
<mat-checkbox <mat-checkbox
#checkbox #checkbox
@ -175,7 +177,7 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table> </table>
</perfect-scrollbar> </cdk-virtual-scroll-viewport>
<div <div
class="no-search-result" class="no-search-result"
fxFlexFill fxFlexFill

View File

@ -18,6 +18,7 @@ import {
import { Sort } from '@angular/material/sort'; import { Sort } from '@angular/material/sort';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { TableVirtualScrollDataSource } from 'ng-table-virtual-scroll';
@Component({ @Component({
selector: 'ucap-organization-detail-table', selector: 'ucap-organization-detail-table',
@ -29,6 +30,7 @@ export class DetailTableComponent implements OnInit, OnDestroy {
set userInfoListIn(userInfo: UserInfoSS[]) { set userInfoListIn(userInfo: UserInfoSS[]) {
this.departmentUserInfoList = userInfo; this.departmentUserInfoList = userInfo;
this.sortedData = userInfo; this.sortedData = userInfo;
this.dataSource.data = !!userInfo ? userInfo : [];
} }
@Input() @Input()
loginRes: LoginResponse; loginRes: LoginResponse;
@ -60,6 +62,7 @@ export class DetailTableComponent implements OnInit, OnDestroy {
departmentUserInfoList: UserInfoSS[]; departmentUserInfoList: UserInfoSS[];
sortedData: UserInfoSS[] = []; sortedData: UserInfoSS[] = [];
dataSource = new TableVirtualScrollDataSource<UserInfoSS>([]);
PresenceType = PresenceType; PresenceType = PresenceType;
displayedColumns: string[] = [ displayedColumns: string[] = [
@ -227,6 +230,7 @@ export class DetailTableComponent implements OnInit, OnDestroy {
return 0; return 0;
} }
}); });
this.dataSource.data = this.sortedData;
} }
compare(a: number | string, b: number | string, isAsc: boolean) { compare(a: number | string, b: number | string, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1); return (a < b ? -1 : 1) * (isAsc ? 1 : -1);

View File

@ -16,6 +16,7 @@ import { MatTreeModule } from '@angular/material/tree';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { VirtualScrollerModule } from 'ngx-virtual-scroller'; import { VirtualScrollerModule } from 'ngx-virtual-scroller';
import { TableVirtualScrollModule } from 'ng-table-virtual-scroll';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { UCapUiModule } from '@ucap-webmessenger/ui'; import { UCapUiModule } from '@ucap-webmessenger/ui';
@ -56,6 +57,7 @@ const DIRECTIVES = [];
PerfectScrollbarModule, PerfectScrollbarModule,
VirtualScrollerModule, VirtualScrollerModule,
TableVirtualScrollModule,
UCapUiModule UCapUiModule
], ],