조회 부서원 리스트 표현 1차.
This commit is contained in:
parent
3064213ed5
commit
d52ace1f03
|
@ -1,12 +1,33 @@
|
|||
<div class="container" fxFlex fxLayout="column">
|
||||
<mat-toolbar class="organization-toolbar">
|
||||
<div fxFlex fxLayout="row" class="organization-header">
|
||||
<div fxLayout="row" fxLayoutAlign="start center" class="profile-img">
|
||||
icon
|
||||
</div>
|
||||
<div
|
||||
fxLayout="row"
|
||||
fxLayoutAlign="start center"
|
||||
class="profile-img"
|
||||
></div>
|
||||
<div class="organization-info">
|
||||
<h3 class="organization-name">
|
||||
title
|
||||
<ng-container *ngIf="!(isSearch$ | async)">
|
||||
<ng-container
|
||||
*ngIf="
|
||||
!!(selectedDepartmentProcessing$ | async);
|
||||
else useSelectedDepartmentName
|
||||
"
|
||||
>
|
||||
{{ 'common.messages.searching' | translate }} ...
|
||||
</ng-container>
|
||||
<ng-template #useSelectedDepartmentName>
|
||||
{{ selectedDepartment$ | async | ucapTranslate: 'name' }}
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!!(isSearch$ | async)">
|
||||
{{ 'common.searchResult' | translate
|
||||
}}<span class="text-accent-color"
|
||||
>({{ (searchDepartmentUserInfoList$ | async).length }}
|
||||
{{ 'common.units.persons' | translate }})</span
|
||||
>
|
||||
</ng-container>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="organization-option">
|
||||
|
@ -15,10 +36,165 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
*ngIf="selectedDepartmentProcessing$ | async"
|
||||
style="position: absolute; width: 100%;"
|
||||
>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
<div fxFlex="1 1 auto" class="organization-content">
|
||||
contents
|
||||
<div fxFlex="0 0 auto" class="table-box">
|
||||
<perfect-scrollbar class="scrollbar">
|
||||
<table mat-table [dataSource]="departmentUserInfoList$ | async">
|
||||
<ng-container matColumnDef="profile">
|
||||
<th mat-header-cell *matHeaderCellDef class="profile">
|
||||
{{ 'search.fieldProfile' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<div class="table-item">
|
||||
<div class="profile">
|
||||
<span
|
||||
class="presence"
|
||||
[ngClass]="getPresence(element, PresenceType.PC)"
|
||||
[matTooltip]="getPresenceMsg(element)"
|
||||
matTooltipPosition="after"
|
||||
></span>
|
||||
<span class="thumbnail-mask">
|
||||
<img
|
||||
class="thumbnail"
|
||||
ucapImage
|
||||
[base]="profileImageRoot"
|
||||
[path]="element.profileImageFile"
|
||||
[default]="'assets/images/img_nophoto_50.png'"
|
||||
(click)="onClickOpenProfile($event, element.seq)"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
*ngIf="
|
||||
getPresence(element, PresenceType.MOBILE) === 'mobileOn'
|
||||
"
|
||||
class="text-accent-color marker-mobile-state"
|
||||
>
|
||||
<mat-icon>phone_android</mat-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
class="work-status"
|
||||
[ngClass]="getWorkstatusInfo(element, 'style')"
|
||||
>
|
||||
{{ getWorkstatusInfo(element, 'text') }}
|
||||
</span>
|
||||
<span class="name">
|
||||
{{ element.name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="grade">
|
||||
<th mat-header-cell *matHeaderCellDef class="grade">
|
||||
{{ 'search.fieldGrade' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="grade">
|
||||
<div class="grade">
|
||||
{{ element.grade }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="deptName">
|
||||
<th mat-header-cell *matHeaderCellDef class="deptName">
|
||||
{{ 'search.fieldDeptartment' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<div class="deptName">
|
||||
{{ element.deptName }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="companyName">
|
||||
<th mat-header-cell *matHeaderCellDef class="companyName">
|
||||
{{ 'search.fieldCompany' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<div class="companyName">
|
||||
{{ element.companyName }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="lineNumber">
|
||||
<th mat-header-cell *matHeaderCellDef class="lineNumber">
|
||||
{{ 'search.fieldOfficePhoneNumber' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="lineNumber">
|
||||
<div class="lineNumber">
|
||||
{{ element.lineNumber }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="hpNumber">
|
||||
<th mat-header-cell *matHeaderCellDef class="hpNumber">
|
||||
{{ 'search.fieldHandphone' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="hpNumber">
|
||||
<div class="hpNumber">
|
||||
{{ element.hpNumber }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="email">
|
||||
<th mat-header-cell *matHeaderCellDef class="email">
|
||||
{{ 'search.fieldEmail' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="email">
|
||||
<div class="email">
|
||||
{{ element.email }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="responsibilities">
|
||||
<th mat-header-cell *matHeaderCellDef class="responsibilities">
|
||||
{{ 'search.fieldResponsibilities' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="responsibilities">
|
||||
<div class="responsibilities">
|
||||
{{ element.responsibilities }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="workplace">
|
||||
<th mat-header-cell *matHeaderCellDef class="workplace">
|
||||
{{ 'search.fieldWorkPlace' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element" class="workplace">
|
||||
<div class="workplace">
|
||||
{{ element.workplace }}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr
|
||||
mat-header-row
|
||||
*matHeaderRowDef="displayedColumns; sticky: true"
|
||||
></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
</perfect-scrollbar>
|
||||
<div
|
||||
class="no-search-result"
|
||||
fxFlexFill
|
||||
*ngIf="
|
||||
!(departmentUserInfoList$ | async) ||
|
||||
0 === (departmentUserInfoList$ | async).length
|
||||
"
|
||||
>
|
||||
{{ 'common.noSearchResult' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxFlex="0 0 auto" fxLayout="column"></div>
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
padding: 0;
|
||||
.organization-header {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -64,6 +65,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.progress {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.organization-content {
|
||||
|
@ -71,4 +76,137 @@
|
|||
background: transparent;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
@mixin disable-selection {
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
height: 550px;
|
||||
}
|
||||
|
||||
.table-box {
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
th {
|
||||
@include disable-selection;
|
||||
text-align: center;
|
||||
&.profile {
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
td.mat-cell {
|
||||
padding: 6px;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
div {
|
||||
@include ellipsis(1);
|
||||
|
||||
&.table-item {
|
||||
display: flex;
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
font-size: 1em;
|
||||
div {
|
||||
display: inline-flex;
|
||||
height: 100%;
|
||||
align-self: center;
|
||||
.name {
|
||||
@include ellipsis(1);
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
.status {
|
||||
font-size: 0.84em;
|
||||
}
|
||||
|
||||
&.profile {
|
||||
width: 70px;
|
||||
text-overflow: unset;
|
||||
flex: 0 0 auto;
|
||||
.presence {
|
||||
transform: translateY(6px);
|
||||
}
|
||||
.thumbnail {
|
||||
cursor: pointer;
|
||||
&-mask {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 0;
|
||||
position: relative;
|
||||
img {
|
||||
width: 40px;
|
||||
height: auto;
|
||||
background-color: #efefef;
|
||||
}
|
||||
}
|
||||
}
|
||||
.marker-mobile-state {
|
||||
position: absolute;
|
||||
background-color: #ffffff;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
bottom: 4px;
|
||||
left: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
.mat-icon {
|
||||
font-size: 0.9em;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
min-width: 18px;
|
||||
min-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.work-status {
|
||||
display: inline-block;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
color: #ffffff;
|
||||
height: 100%;
|
||||
min-width: 32px;
|
||||
margin-right: 4px;
|
||||
border-radius: 24px;
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-search-result {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,229 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
ChangeDetectorRef,
|
||||
OnDestroy,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
import { SelectedDept, UserInfoSS } from '@ucap-webmessenger/protocol-query';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DialogService } from '@ucap-webmessenger/ui';
|
||||
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
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 { Observable, Subscription } from 'rxjs';
|
||||
import { PresenceType, StatusCode } from '@ucap-webmessenger/core';
|
||||
import {
|
||||
StatusBulkInfo,
|
||||
WorkStatusType
|
||||
} from '@ucap-webmessenger/protocol-status';
|
||||
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
||||
import { KEY_VER_INFO } from '@app/types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout-messenger-organization',
|
||||
templateUrl: './organization.component.html',
|
||||
styleUrls: ['./organization.component.scss']
|
||||
})
|
||||
export class OrganizationComponent implements OnInit {
|
||||
constructor() {}
|
||||
export class OrganizationComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
openProfile = new EventEmitter<number>();
|
||||
|
||||
ngOnInit() {}
|
||||
sessionVerinfo: VersionInfo2Response;
|
||||
|
||||
isSearch$: Observable<boolean>;
|
||||
selectedDepartmentProcessing$: Observable<boolean>;
|
||||
selectedDepartment$: Observable<SelectedDept>;
|
||||
|
||||
departmentUserInfoList$: Observable<UserInfoSS[]>;
|
||||
searchDepartmentUserInfoList$: Observable<UserInfoSS[]>;
|
||||
|
||||
profileImageRoot: string;
|
||||
PresenceType = PresenceType;
|
||||
presenceSubscription: Subscription;
|
||||
presence: StatusBulkInfo[] = [];
|
||||
displayedColumns: string[] = [
|
||||
'profile',
|
||||
'deptName',
|
||||
'companyName',
|
||||
'grade',
|
||||
'lineNumber',
|
||||
'hpNumber',
|
||||
'email',
|
||||
'responsibilities',
|
||||
'workplace'
|
||||
];
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private sessionStorageService: SessionStorageService,
|
||||
private dialogService: DialogService,
|
||||
private translateService: TranslateService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private logger: NGXLogger
|
||||
) {
|
||||
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
|
||||
KEY_VER_INFO
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.isSearch$ = this.store.pipe(
|
||||
select(AppStore.MessengerSelector.QuerySelector.isSearch)
|
||||
);
|
||||
|
||||
this.selectedDepartmentProcessing$ = this.store.pipe(
|
||||
select(
|
||||
AppStore.MessengerSelector.QuerySelector.selectedDepartmentProcessing
|
||||
)
|
||||
);
|
||||
|
||||
this.selectedDepartment$ = this.store.pipe(
|
||||
select(AppStore.MessengerSelector.QuerySelector.selectedDepartment)
|
||||
);
|
||||
|
||||
this.departmentUserInfoList$ = this.store.pipe(
|
||||
select(AppStore.MessengerSelector.QuerySelector.departmentUserInfoList)
|
||||
);
|
||||
|
||||
this.searchDepartmentUserInfoList$ = this.store.pipe(
|
||||
select(
|
||||
AppStore.MessengerSelector.QuerySelector.searchDepartmentUserInfoList
|
||||
)
|
||||
);
|
||||
|
||||
this.presenceSubscription = this.store
|
||||
.pipe(
|
||||
select(
|
||||
AppStore.MessengerSelector.StatusSelector.selectAllStatusBulkInfo
|
||||
)
|
||||
)
|
||||
.subscribe(presence => {
|
||||
this.presence = presence;
|
||||
});
|
||||
|
||||
this.profileImageRoot = this.sessionVerinfo.profileRoot;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.presenceSubscription) {
|
||||
this.presenceSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************
|
||||
* ABOUT TABLE
|
||||
*******************************/
|
||||
|
||||
getPresence(userInfo: UserInfoSS, type: PresenceType): string {
|
||||
const presences = this.presence.filter(p => p.userSeq === userInfo.seq);
|
||||
|
||||
let status: string;
|
||||
let rtnClass = '';
|
||||
if (!!presences && presences.length > 0) {
|
||||
const presence = presences[0];
|
||||
switch (type) {
|
||||
case PresenceType.PC:
|
||||
status = !!presence ? presence.pcStatus : undefined;
|
||||
break;
|
||||
case PresenceType.MOBILE:
|
||||
status = !!presence ? presence.mobileStatus : undefined;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!!status) {
|
||||
switch (status) {
|
||||
case StatusCode.OnLine:
|
||||
rtnClass = type + 'On';
|
||||
break;
|
||||
case StatusCode.Away:
|
||||
rtnClass = type + 'Out';
|
||||
break;
|
||||
case StatusCode.Busy:
|
||||
rtnClass = type + 'Other';
|
||||
break;
|
||||
default:
|
||||
rtnClass = type + 'Off';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rtnClass = type + 'Off';
|
||||
}
|
||||
|
||||
return rtnClass;
|
||||
}
|
||||
getPresenceMsg(userInfo: UserInfoSS): string {
|
||||
const presences = this.presence.filter(p => p.userSeq === userInfo.seq);
|
||||
|
||||
if (!!presences && presences.length > 0) {
|
||||
const presence = presences[0];
|
||||
if (
|
||||
!!presence &&
|
||||
!!presence.statusMessage &&
|
||||
presence.statusMessage !== '.'
|
||||
) {
|
||||
return presence.statusMessage;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
getWorkstatusInfo(userInfo: UserInfoSS, type: string): string {
|
||||
let workstatus = userInfo.workstatus;
|
||||
|
||||
const presences = this.presence.filter(p => p.userSeq === userInfo.seq);
|
||||
|
||||
if (!!presences && presences.length > 0) {
|
||||
const presence = presences[0];
|
||||
if (
|
||||
!!presence &&
|
||||
!!presence.workstatus &&
|
||||
presence.workstatus.trim().length > 0
|
||||
) {
|
||||
workstatus = presence.workstatus;
|
||||
}
|
||||
}
|
||||
|
||||
let text = '';
|
||||
// morning-off: 오전 afternoon-off: 오후 day-off: 휴가 long-time: 장기 leave-of-absence: 휴직
|
||||
let style = '';
|
||||
switch (workstatus) {
|
||||
case WorkStatusType.VacationAM:
|
||||
style = 'morning-off';
|
||||
text = '오전';
|
||||
break;
|
||||
case WorkStatusType.VacationPM:
|
||||
style = 'afternoon-off';
|
||||
text = '오후';
|
||||
break;
|
||||
case WorkStatusType.VacationAll:
|
||||
style = 'day-off';
|
||||
text = '휴가';
|
||||
break;
|
||||
case WorkStatusType.LeaveOfAbsence:
|
||||
style = 'leave-of-absence';
|
||||
text = '휴직';
|
||||
break;
|
||||
case WorkStatusType.LongtermRefresh:
|
||||
style = 'long-time';
|
||||
text = '장기';
|
||||
break;
|
||||
}
|
||||
|
||||
return type === 'text' ? text : style;
|
||||
}
|
||||
|
||||
onClickOpenProfile(event: MouseEvent, userSeq: number) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.openProfile.emit(userSeq);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user