조직도 타이틀, 검색 기능 구현.

This commit is contained in:
leejinho 2019-11-12 09:47:56 +09:00
parent d28b6cc171
commit 8fec0e15f2
3 changed files with 264 additions and 107 deletions

View File

@ -1,37 +1,50 @@
<div
class="oraganization-tab"
*ngIf="departmentInfoList$ | async"
>
<div class="oraganization-tab-tree" >
<div>
<div class="current-head">
<h3>조직도</h3>
</div>
<ucap-organization-tenant-search
[companyList]="companyList$ | async"
[companyCode]="companyCode"
(keyDownEnter)="onKeyDownEnterOrganizationTenantSearch($event)"
(cancel)="onClickCancel($event)"
>
</ucap-organization-tenant-search>
</div>
<div class="oraganization-tab" *ngIf="departmentInfoList$ | async">
<div class="oraganization-tab-tree">
<ucap-organization-tree
[oraganizationList]="departmentInfoList$ | async"
[loginRes]="loginRes"
(selected)="onSelectedOrganization($event)"
></ucap-organization-tree>
</div>
<div class="select-list ">
<dl class="select-dept text-accent-color">
<dt>
<div class="select-list">
<dl class="select-dept text-accent-color">
<dt>
<ng-container *ngIf="!isShowSearch">
{{ getSelectedDepartmentName() }}
</dt>
<dd>
<mat-checkbox
#checkbox
[checked]="getCheckedAllUser()"
(change)="onCheckAllUser(checkbox.checked)"
(click)="$event.stopPropagation()"
</ng-container>
<ng-container *ngIf="isShowSearch">
검색결과<span class="text-accent-color"
>({{ searchUserInfos.length }}명)</span
>
</mat-checkbox>
</dd>
</dl>
</ng-container>
</dt>
<dd>
<mat-checkbox
#checkbox
[checked]="getCheckedAllUser()"
(change)="onCheckAllUser(checkbox.checked)"
(click)="$event.stopPropagation()"
>
</mat-checkbox>
</dd>
</dl>
<div *ngIf="selectedDepartmentProcessing">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
<div class="search-list">
<cdk-virtual-scroll-viewport
itemSize="20"
style="height: 100%;"
>
<div *ngIf="!isShowSearch" class="search-list">
<cdk-virtual-scroll-viewport itemSize="20" style="height: 100%;">
<ucap-profile-user-list-item
*cdkVirtualFor="let userInfo of selectedDepartmentUserInfoList"
[userInfo]="userInfo"
@ -55,11 +68,25 @@
>
</ucap-profile-user-list-item> -->
</div>
<div *ngIf="isShowSearch" class="search-result">
<cdk-virtual-scroll-viewport itemSize="20" style="height: 100%;">
<ucap-profile-user-list-item
*cdkVirtualFor="let userInfo of searchUserInfos"
[userInfo]="userInfo"
[checkable]="true"
[sessionVerinfo]="sessionVerinfo"
[selectedUserList]="selectedUserList"
[isChecked]="getCheckedUser(userInfo)"
(checkUser)="onCheckUser($event)"
>
</ucap-profile-user-list-item>
</cdk-virtual-scroll-viewport>
</div>
</div>
<div *ngIf="!isUserSelect" class="btn-box">
<!--선택된 후에는 disabled="true" 빼주세요. -->
<button
mat-stroked-button
[disabled]="selectedUserList.length > 0 ? 'false' : 'true'"
class="mat-primary"
(click)="onClickShowSelectedUserList()"
>
@ -69,18 +96,29 @@
</button>
<ul>
<li>
<button mat-flat-button (click)="onClickAddGroup()" class="mat-primary">
<button
mat-flat-button
[disabled]="selectedUserList.length > 0 ? 'false' : 'true'"
(click)="onClickAddGroup()"
class="mat-primary"
>
그룹에 추가
</button>
</li>
<li>
<button mat-flat-button (click)="onClickChatOpen()" class="mat-primary">
<button
mat-flat-button
[disabled]="selectedUserList.length > 0 ? 'false' : 'true'"
(click)="onClickChatOpen()"
class="mat-primary"
>
대화
</button>
</li>
<li>
<button
mat-flat-button
[disabled]="selectedUserList.length > 0 ? 'false' : 'true'"
(click)="onClickConference()"
class="mat-primary"
>

View File

@ -11,98 +11,134 @@
display: -webkit-box;
-webkit-line-clamp: $row;
-webkit-box-orient: vertical;
word-wrap: break-word;
word-wrap: break-word;
}
}
.current-head {
display: flex;
justify-content: center;
padding: 0 10px;
height: 70px;
background-color: #eeeeee;
background: #f15f79;
background: -webkit-linear-gradient(to right, #352a37, #f15f79);
background: linear-gradient(to right, #352a37, #ef4c73);
color: #ffffff;
h3 {
display: inline-flex;
padding-left: 10px;
align-items: center;
width: 100%;
}
.btn-box {
height: 100%;
margin-left: auto;
display: inline-flex;
align-items: center;
svg {
stroke: #333333;
}
}
}
.search-result {
height: calc(100% - 40px);
overflow: auto;
.result-num {
padding: 10px;
display: flex;
height: 40px;
}
}
.oraganization-tab {
height: 100%;
height: calc(100% - 130px);
flex-direction: inherit;
display: flex;
.oraganization-tab-tree {
height:40%;
height: 40%;
overflow-y: auto;
}
}
//팝업에 있는 조직도
.mat-card-content{
.mat-tab-body-content{
.oraganization-box{
.mat-card-content {
.mat-tab-body-content {
.oraganization-box {
.oraganization-tab {
height: 80%;
flex-direction: row;
}
}
}
}
.select-list{
height:60%;
border-top:1px solid #dddddd;
.select-dept{
padding:0 20px;
height:40px;
line-height:40px;
display:flex;
background-color: #f9f9f9;
dt{
}
dd{
margin-left:auto;
}
}
.search-list{
height: calc(100% - 40px);
overflow: auto;
.list-item{
height:70px;
}
}
}
.btn-box{
padding:10px;
button{
width:100%;
@include ellipsis(1);
}
ul{
display:flex;
flex-flow: row;
align-content: space-between;
margin-top:4px;
li{
display:inline-flex;
align-items: center;
flex-grow: 1;
width:33%;
margin-right: 4px;
&:last-child{
margin-right: 0;
}
button{
text-align:center;
width:100%;
height:100%;
padding: 0 6px;
}
}
}
}
}
.select-list {
height: 60%;
border-top: 1px solid #dddddd;
.select-dept {
padding: 0 20px;
height: 40px;
line-height: 40px;
display: flex;
background-color: #f9f9f9;
dt {
}
dd {
margin-left: auto;
}
}
.search-list {
height: calc(100% - 40px);
overflow: auto;
.list-item {
height: 70px;
}
}
}
.btn-box {
padding: 10px;
button {
width: 100%;
@include ellipsis(1);
span {
vertical-align: baseline;
}
}
ul {
display: flex;
flex-flow: row;
align-content: space-between;
margin-top: 4px;
li {
display: inline-flex;
align-items: center;
flex-grow: 1;
width: 33%;
margin-right: 4px;
&:last-child {
margin-right: 0;
}
button {
text-align: center;
width: 100%;
height: 100%;
padding: 0 6px;
}
}
}
}
.dialog-org {
.oraganization-tab {
width: 100%;
height:380px;
height: 380px;
border-bottom: 1px solid #dddddd;
position: relative;
.oraganization-tab-tree {
display: inline-flex;
width: 50%;
height:100%;
height: 100%;
border-right: 1px solid #dddddd;
overflow: auto;
}
@ -110,11 +146,11 @@
display: inline-flex;
flex-direction: column;
width: 50%;
height:100%;
height: 100%;
overflow: auto;
.search-list {
overflow: auto;
}
}
}
}
}

View File

@ -4,10 +4,10 @@ import {
OnDestroy,
Output,
EventEmitter,
Input
Input,
} from '@angular/core';
import { ucapAnimations, DialogService } from '@ucap-webmessenger/ui';
import { Observable, Subscription } from 'rxjs';
import { Observable, Subscription, of } from 'rxjs';
import {
DeptInfo,
QueryProtocolService,
@ -15,7 +15,10 @@ import {
UserInfoSS,
DeptUserResponse,
UserInfoF,
UserInfoDN
UserInfoDN,
SSVC_TYPE_QUERY_DEPT_USER_DATA,
SSVC_TYPE_QUERY_DEPT_USER_RES,
DeptUserData,
} from '@ucap-webmessenger/protocol-query';
import { UserInfo, GroupDetailData } from '@ucap-webmessenger/protocol-sync';
import { Store, select } from '@ngrx/store';
@ -25,23 +28,25 @@ import * as AppStore from '@app/store';
import * as QueryStore from '@app/store/messenger/query';
import * as SyncStore from '@app/store/messenger/sync';
import * as ChatStore from '@app/store/messenger/chat';
import * as StatusStore from '@app/store/messenger/status';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
import { take, map, tap, delay } from 'rxjs/operators';
import { take, map, tap, delay, catchError } from 'rxjs/operators';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { KEY_VER_INFO } from '@app/types/ver-info.type';
import {
SelectGroupDialogComponent,
SelectGroupDialogData,
SelectGroupDialogResult
SelectGroupDialogResult,
} from '../../dialogs/group/select-group.dialog.component';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { Company } from '@ucap-webmessenger/api-external';
@Component({
selector: 'app-layout-chat-left-sidenav-organization',
templateUrl: './organization.component.html',
styleUrls: ['./organization.component.scss'],
animations: ucapAnimations
animations: ucapAnimations,
})
export class OrganizationComponent implements OnInit, OnDestroy {
@Input()
@ -62,6 +67,9 @@ export class OrganizationComponent implements OnInit, OnDestroy {
userInfos: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[];
}>();
companyList$: Observable<Company[]>;
companyCode: string;
departmentInfoList$: Observable<DeptInfo[]>;
selectedDepartmentUserInfoList$: Observable<UserInfoSS[]>;
selectedDepartmentUserInfoList: UserInfoSS[] = [];
@ -75,9 +83,13 @@ export class OrganizationComponent implements OnInit, OnDestroy {
loginRes: LoginResponse;
sessionVerinfo: VersionInfo2Response;
isShowSearch = false;
searchUserInfos: UserInfoSS[] = [];
constructor(
private store: Store<any>,
private sessionStorageService: SessionStorageService,
private queryProtocolService: QueryProtocolService,
private dialogService: DialogService,
private logger: NGXLogger
) {
@ -88,6 +100,11 @@ export class OrganizationComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.companyCode = this.loginInfo.companyCode;
this.companyList$ = this.store.pipe(
select(AppStore.SettingSelector.CompanySelector.companyList)
);
this.departmentInfoList$ = this.store.pipe(
select(AppStore.MessengerSelector.QuerySelector.departmentInfoList)
);
@ -133,7 +150,7 @@ export class OrganizationComponent implements OnInit, OnDestroy {
search: '',
searchRange: DeptSearchType.All,
senderCompanyCode: this.loginInfo.companyCode,
senderEmployeeType: loginRes.userInfo.employeeType
senderEmployeeType: loginRes.userInfo.employeeType,
})
);
return loginRes;
@ -155,7 +172,67 @@ export class OrganizationComponent implements OnInit, OnDestroy {
}
}
/** 유저검색 */
onKeyDownEnterOrganizationTenantSearch(params: {
companyCode: string;
searchWord: string;
}) {
if (params.searchWord.trim().length > 1) {
this.isShowSearch = true;
this.selectedDepartmentProcessing = true;
const searchUserInfos: UserInfoSS[] = [];
this.queryProtocolService
.deptUser({
divCd: 'GRP',
companyCode: params.companyCode,
searchRange: DeptSearchType.All,
search: params.searchWord,
senderCompanyCode: params.companyCode,
senderEmployeeType: this.loginRes.userInfo.employeeType,
})
.pipe(
map(res => {
switch (res.SSVC_TYPE) {
case SSVC_TYPE_QUERY_DEPT_USER_DATA:
searchUserInfos.push(...(res as DeptUserData).userInfos);
break;
case SSVC_TYPE_QUERY_DEPT_USER_RES:
{
// 검색 결과 처리.
this.searchUserInfos = searchUserInfos;
this.selectedDepartmentProcessing = false;
// 검색 결과에 따른 프레즌스 조회.
const userSeqList: number[] = [];
this.searchUserInfos.map(user => userSeqList.push(user.seq));
if (userSeqList.length > 0) {
this.store.dispatch(
StatusStore.bulkInfo({
divCd: 'orgSrch',
userSeqs: userSeqList,
})
);
}
}
break;
}
}),
catchError(error => {
this.selectedDepartmentProcessing = false;
return of(this.logger.error(error));
})
)
.subscribe();
}
}
/** 검색 취소 */
onClickCancel() {
this.isShowSearch = false;
this.searchUserInfos = [];
}
onSelectedOrganization(deptInfo: DeptInfo) {
this.onClickCancel();
this.store
.pipe(
take(1),
@ -169,7 +246,7 @@ export class OrganizationComponent implements OnInit, OnDestroy {
search: '',
searchRange: DeptSearchType.All,
senderCompanyCode: this.loginInfo.companyCode,
senderEmployeeType: loginRes.userInfo.employeeType
senderEmployeeType: loginRes.userInfo.employeeType,
})
);
}),
@ -182,7 +259,7 @@ export class OrganizationComponent implements OnInit, OnDestroy {
getSelectedDepartmentName() {
if (!!this.selectedDepartmentProcessing) {
return '부서조회중..';
return '조회중..';
}
if (!!this.selectedDepartmentName) {
@ -194,10 +271,14 @@ export class OrganizationComponent implements OnInit, OnDestroy {
/** 전체 체크여부 */
getCheckedAllUser() {
const compareList: UserInfoSS[] = this.isShowSearch
? this.searchUserInfos
: this.selectedDepartmentUserInfoList;
if (
!this.selectedDepartmentUserInfoList ||
this.selectedDepartmentUserInfoList.length === 0 ||
this.selectedDepartmentUserInfoList.filter(
!compareList ||
compareList.length === 0 ||
compareList.filter(
item =>
!(
this.selectedUserList.filter(user => user.seq === item.seq).length >
@ -225,7 +306,9 @@ export class OrganizationComponent implements OnInit, OnDestroy {
onCheckAllUser(value: boolean) {
this.checkAllUser.emit({
isChecked: value,
userInfos: this.selectedDepartmentUserInfoList
userInfos: this.isShowSearch
? this.searchUserInfos
: this.selectedDepartmentUserInfoList,
});
}
@ -251,8 +334,8 @@ export class OrganizationComponent implements OnInit, OnDestroy {
>(SelectGroupDialogComponent, {
width: '600px',
data: {
title: 'Group Select'
}
title: 'Group Select',
},
});
if (!!result && !!result.choice && result.choice) {
@ -269,7 +352,7 @@ export class OrganizationComponent implements OnInit, OnDestroy {
this.store.dispatch(
SyncStore.updateGroupMember({
oldGroup,
trgtUserSeq
trgtUserSeq,
})
);