This commit is contained in:
richard-loafle 2020-02-11 16:35:41 +09:00
commit 7094ebddfd
10 changed files with 463 additions and 16 deletions

View File

@ -69,7 +69,7 @@
perfectScrollbar perfectScrollbar
fxFlexFill fxFlexFill
> >
<ucap-profile-user-list-item <ucap-daesang-profile-user-list-item
*ngFor="let userInfo of vsDeptUser.viewPortItems" *ngFor="let userInfo of vsDeptUser.viewPortItems"
[userInfo]="userInfo" [userInfo]="userInfo"
[sessionVerinfo]="sessionVerinfo" [sessionVerinfo]="sessionVerinfo"
@ -81,11 +81,8 @@
(openProfile)="onClickOpenProfile($event)" (openProfile)="onClickOpenProfile($event)"
(click)="onToggleUser(userInfo)" (click)="onToggleUser(userInfo)"
(contextmenu)="onContextMenuOrgUser($event, userInfo)" (contextmenu)="onContextMenuOrgUser($event, userInfo)"
[matTooltip]="getTooltip(userInfo)"
matTooltipShowDelay="500"
matTooltipPosition="after"
> >
</ucap-profile-user-list-item> </ucap-daesang-profile-user-list-item>
</virtual-scroller> </virtual-scroller>
</div> </div>
<div *ngIf="isShowSearch" class="search-result"> <div *ngIf="isShowSearch" class="search-result">
@ -95,7 +92,7 @@
perfectScrollbar perfectScrollbar
fxFlexFill fxFlexFill
> >
<ucap-profile-user-list-item <ucap-daesang-profile-user-list-item
*ngFor="let userInfo of vsDeptSearchUser.viewPortItems" *ngFor="let userInfo of vsDeptSearchUser.viewPortItems"
[userInfo]="userInfo" [userInfo]="userInfo"
[checkable]="userInfo.seq !== loginRes.userSeq" [checkable]="userInfo.seq !== loginRes.userSeq"
@ -106,11 +103,8 @@
(openProfile)="onClickOpenProfile($event)" (openProfile)="onClickOpenProfile($event)"
(click)="onToggleUser(userInfo)" (click)="onToggleUser(userInfo)"
(contextmenu)="onContextMenuOrgUser($event, userInfo)" (contextmenu)="onContextMenuOrgUser($event, userInfo)"
[matTooltip]="getTooltip(userInfo)"
matTooltipShowDelay="500"
matTooltipPosition="after"
> >
</ucap-profile-user-list-item> </ucap-daesang-profile-user-list-item>
</virtual-scroller> </virtual-scroller>
</div> </div>
</div> </div>

View File

@ -101,8 +101,8 @@ $tablet-s-width: 768px;
border: 1px dotted #cccccc; border: 1px dotted #cccccc;
box-sizing: border-box; box-sizing: border-box;
img { img {
width: 100%; max-width: 100%;
height: 100%; max-height: 100%;
} }
} }
dd { dd {

View File

@ -57,6 +57,7 @@ import { AppCommonLayoutModule } from '@app/layouts/common/common.layout.module'
import { COMPONENTS } from './components'; import { COMPONENTS } from './components';
import { DIALOGS } from './dialogs'; import { DIALOGS } from './dialogs';
import { UCapDaesangModule } from '@ucap-webmessenger/daesang';
@NgModule({ @NgModule({
imports: [ imports: [
@ -108,6 +109,8 @@ import { DIALOGS } from './dialogs';
UCapUiOrganizationModule, UCapUiOrganizationModule,
UCapUiSettingsModule, UCapUiSettingsModule,
UCapDaesangModule,
AppCommonLayoutModule AppCommonLayoutModule
], ],
exports: [...COMPONENTS, ...DIALOGS], exports: [...COMPONENTS, ...DIALOGS],

View File

@ -0,0 +1,70 @@
<!--체크박스 보여줄때는 <div class="list-item checkbox" matRipple> 클래스에 checkbox만 추가-->
<div class="list-item checkbox" *ngIf="userInfo" matRipple>
<!--pcOn , pcOut pcOff , pcOther-->
<span
class="presence"
*ngIf="showPresence"
[ngClass]="getPresence(PresenceType.PC)"
[matTooltip]="getPresenceMsg()"
matTooltipPosition="after"
>
</span>
<dl class="item-default">
<dt>
<div class="thumbnail-mask">
<img
class="thumbnail"
ucapImage
[base]="profileImageRoot"
[path]="userInfo.profileImageFile"
[default]="'assets/images/img_nophoto_50.png'"
(click)="onClickOpenProfile($event, userInfo.seq)"
/>
</div>
<span
*ngIf="getPresence(PresenceType.MOBILE) === 'mobileOn'"
class="text-accent-color marker-mobile-state"
>
<mat-icon>phone_android</mat-icon>
</span>
</dt>
<dd class="info">
<div class="user-info">
<div class="user">
<span
*ngIf="getWorkstatusInfo('text').length > 0"
class="work-status"
[ngClass]="getWorkstatusInfo('style')"
>{{ getWorkstatusInfo('text') }}</span
>
<span class="detail">
<b class="name">{{ userInfo | ucapTranslate: 'name' }}</b>
<span class="grade">{{ userInfo | ucapTranslate: 'grade' }}</span>
</span>
</div>
<span class="dept">
<span *ngIf="!!userInfo.companyName">
{{ userInfo.companyName }} /
</span>
{{ userInfo | ucapTranslate: 'deptName' }}
</span>
</div>
<!-- 상태메시지 제외
<div
class="msg-status"
*ngIf="!checkable && userInfo.intro.trim() && !compactable"
>
{{ userInfo.intro }}
</div>
--></dd>
</dl>
<mat-checkbox
*ngIf="checkable"
#checkbox
[checked]="isChecked"
[disabled]="checkDisabled"
(change)="onChangeCheck(checkbox.checked, userInfo)"
(click)="$event.stopPropagation()"
>
</mat-checkbox>
</div>

View File

@ -0,0 +1,145 @@
@charset 'utf-8';
$font-dark: #212121;
$font-mid: #666666;
$font-light: #848d95;
$font-white: #ffffff;
$line-basic: 1px solid #dddddd;
$bg-list-item-msg: #f0f4f6;
$bg-list-hover: #efefef;
$listH-row2: 60px;
$presence-size: 8px;
$thumbnail-msize: 40px;
@mixin ellipsis($row) {
overflow: hidden;
text-overflow: ellipsis;
@if $row == 1 {
display: block;
white-space: nowrap;
word-wrap: normal;
} @else if $row >= 2 {
display: -webkit-box;
-webkit-line-clamp: $row;
-webkit-box-orient: vertical;
word-wrap: break-word;
}
}
%list-item {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: $listH-row2;
border-bottom: $line-basic;
padding: 20px;
}
%msg-status {
display: inline-block;
height: 40px;
background-color: $bg-list-item-msg;
font-size: 11px;
padding: 2px 6px;
vertical-align: middle;
align-items: center;
@include ellipsis(2);
border-radius: 4px;
}
.item-default {
display: flex;
width: 100%;
dt {
flex: none;
position: relative;
.thumbnail {
width: $thumbnail-msize;
height: auto;
}
}
.info {
position: relative;
display: inline-block;
margin: 0;
padding: 0;
width: calc(100% - 50px);
transform: translateY(2px);
.user-info {
.user {
display: flex;
flex-flow: row;
margin-bottom: 2px;
.work-status {
display: inline-flex;
justify-content: center;
justify-items: center;
color: #ffffff;
height: 100%;
min-width: 32px;
margin-right: 4px;
border-radius: 24px;
flex: 0 0 auto;
}
.detail {
color: $font-dark;
width: calc(100% - 40px);
@include ellipsis(1);
}
}
.dept {
color: $font-mid;
@include ellipsis(1);
}
.msg-status {
display: inline-block;
float: right;
@extend %msg-status;
}
}
}
}
//가변에 따른 list-item width값 변경
.list-item {
.presence[class*='pc'] {
+ .item-default {
width: calc(100% - 20px);
}
}
&.checkbox {
.presence[class*='pc'] {
+ .item-default {
width: calc(100% - 50px);
}
}
.item-default {
width: calc(100% - 30px);
.info {
.detail {
width: 100%;
}
.msg-status {
display: none;
}
}
}
}
}
.oraganization-tab {
.list-item {
.presence {
display: none;
}
.item-default {
width: calc(100% - 30px);
}
}
}
.org {
.list-item {
height: 70px;
}
}

View File

@ -0,0 +1,28 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { UserListItemComponent } from './user-list-item.component';
describe('UserListItemComponent', () => {
let component: UserListItemComponent;
let fixture: ComponentFixture<UserListItemComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ UserListItemComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UserListItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,165 @@
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ViewEncapsulation
} from '@angular/core';
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
import {
UserInfoSS,
UserInfoF,
UserInfoDN
} from '@ucap-webmessenger/protocol-query';
import {
StatusBulkInfo,
StatusInfo,
WorkStatusType
} from '@ucap-webmessenger/protocol-status';
import { NGXLogger } from 'ngx-logger';
import { StatusCode, PresenceType } from '@ucap-webmessenger/core';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
@Component({
selector: 'ucap-daesang-profile-user-list-item',
templateUrl: './user-list-item.component.html',
styleUrls: ['./user-list-item.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class UserListItemComponent implements OnInit {
@Input()
userInfo: UserInfoSS;
@Input()
profileImageRoot?: string;
@Input()
presence: StatusBulkInfo | StatusInfo;
@Input()
showPresence = true;
@Input()
checkable = false;
@Input()
checkDisabled = false;
@Input()
isChecked = false;
@Input()
compactable = false;
@Input()
sessionVerinfo: VersionInfo2Response;
@Input()
/** 선택된 사용자의 리스트 */
selectedUserList?: UserInfoSS[] = [];
@Output()
checkUser = new EventEmitter<{
isChecked: boolean;
userInfo: UserInfoSS;
}>();
@Output()
openProfile = new EventEmitter<number>();
PresenceType = PresenceType;
constructor(private logger: NGXLogger) {}
ngOnInit() {
this.profileImageRoot =
this.profileImageRoot || this.sessionVerinfo.profileRoot;
}
getPresence(type: PresenceType): string {
let status: string;
let rtnClass = '';
switch (type) {
case PresenceType.PC:
status = !!this.presence ? this.presence.pcStatus : undefined;
break;
case PresenceType.MOBILE:
status = !!this.presence ? this.presence.mobileStatus : undefined;
break;
}
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;
}
return rtnClass;
}
getPresenceMsg(): string {
if (
!!this.presence &&
!!this.presence.statusMessage &&
this.presence.statusMessage !== '.'
) {
return this.presence.statusMessage;
} else {
return '';
}
}
/** 리스트가 checkable 할 경우 checkbox 의 change 이벤트를 상위 컴포넌트로 전달한다. */
onChangeCheck(value: boolean, userInfo: UserInfoSS) {
this.checkUser.emit({
isChecked: value,
userInfo
});
}
onClickOpenProfile(event: MouseEvent, userSeq: number) {
event.preventDefault();
event.stopPropagation();
this.openProfile.emit(userSeq);
}
getWorkstatusInfo(type: string): string {
let workstatus = this.userInfo.workstatus;
if (
!!this.presence &&
!!this.presence.workstatus &&
this.presence.workstatus.trim().length > 0
) {
workstatus = this.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;
}
}

View File

@ -1,8 +1,27 @@
import { NgModule, ModuleWithProviders } from '@angular/core'; import { NgModule, ModuleWithProviders } from '@angular/core';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatCardModule } from '@angular/material/card';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatRippleModule, MatCheckboxModule } from '@angular/material';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { UCapUiModule } from '@ucap-webmessenger/ui';
import { TranslateModule } from '@ngx-translate/core';
import { DaesangCipherService } from './services/daesang-cipher.service'; import { DaesangCipherService } from './services/daesang-cipher.service';
import { DaesangProtocolService } from './services/daesang-protocol.service'; import { DaesangProtocolService } from './services/daesang-protocol.service';
import { DaesangApiService } from './services/daesang-api.service'; import { DaesangApiService } from './services/daesang-api.service';
import { UserListItemComponent } from './components/user-list-item.component';
const COMPONENTS = [UserListItemComponent];
const SERVICES = [ const SERVICES = [
DaesangCipherService, DaesangCipherService,
@ -11,9 +30,26 @@ const SERVICES = [
]; ];
@NgModule({ @NgModule({
declarations: [], imports: [
imports: [], CommonModule,
exports: [] ReactiveFormsModule,
FlexLayoutModule,
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatRippleModule,
MatCheckboxModule,
MatCardModule,
MatTooltipModule,
MatProgressSpinnerModule,
TranslateModule,
UCapUiModule
],
exports: [...COMPONENTS],
declarations: [...COMPONENTS]
}) })
export class UCapDaesangModule { export class UCapDaesangModule {
public static forRoot(): ModuleWithProviders<UCapDaesangModule> { public static forRoot(): ModuleWithProviders<UCapDaesangModule> {

View File

@ -3,7 +3,8 @@
position: relative; position: relative;
img { img {
height: 140px; max-height: 140px;
min-height: 20px;
width: auto; width: auto;
} }

View File

@ -175,6 +175,10 @@ export class MessagesComponent implements OnInit, OnDestroy {
this.loginRes = loginRes; this.loginRes = loginRes;
}); });
this.roomInfoSubscription = this.roomInfo$.subscribe(roomInfo => { this.roomInfoSubscription = this.roomInfo$.subscribe(roomInfo => {
if (!this.roomInfo || this.roomInfo.roomSeq !== roomInfo.roomSeq) {
this.baseEventSeq = 0;
}
this.roomInfo = roomInfo; this.roomInfo = roomInfo;
this.showMore = false; this.showMore = false;
@ -186,6 +190,7 @@ export class MessagesComponent implements OnInit, OnDestroy {
// clear :: readToHearEvent object // clear :: readToHearEvent object
this.readToHereEvent = undefined; this.readToHereEvent = undefined;
this.existReadToHereEvent = true; this.existReadToHereEvent = true;
this.firstCheckReadHere = true;
/** [E] initializing by changed room */ /** [E] initializing by changed room */
if (!this.roomInfo || this.roomInfo.roomSeq !== roomInfo.roomSeq) { if (!this.roomInfo || this.roomInfo.roomSeq !== roomInfo.roomSeq) {