mirror of https://github.com/abpframework/abp.git
committed by
GitHub
10 changed files with 437 additions and 358 deletions
@ -1,4 +1,7 @@ |
|||||
export * from './account-layout/account-layout.component'; |
export * from './account-layout/account-layout.component'; |
||||
export * from './application-layout/application-layout.component'; |
export * from './application-layout/application-layout.component'; |
||||
export * from './empty-layout/empty-layout.component'; |
export * from './empty-layout/empty-layout.component'; |
||||
|
export * from './logo/logo.component'; |
||||
|
export * from './nav-items/nav-items.component'; |
||||
|
export * from './routes/routes.component'; |
||||
export * from './validation-error/validation-error.component'; |
export * from './validation-error/validation-error.component'; |
||||
|
|||||
@ -0,0 +1,29 @@ |
|||||
|
import { Config, ConfigState } from '@abp/ng.core'; |
||||
|
import { Component } from '@angular/core'; |
||||
|
import { Store } from '@ngxs/store'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-logo', |
||||
|
template: ` |
||||
|
<a class="navbar-brand" routerLink="/"> |
||||
|
<img |
||||
|
*ngIf="appInfo.logoUrl; else appName" |
||||
|
[src]="appInfo.logoUrl" |
||||
|
[alt]="appInfo.name" |
||||
|
width="100%" |
||||
|
height="auto" |
||||
|
/> |
||||
|
</a> |
||||
|
|
||||
|
<ng-template #appName> |
||||
|
{{ appInfo.name }} |
||||
|
</ng-template> |
||||
|
`,
|
||||
|
}) |
||||
|
export class LogoComponent { |
||||
|
get appInfo(): Config.Application { |
||||
|
return this.store.selectSnapshot(ConfigState.getApplicationInfo); |
||||
|
} |
||||
|
|
||||
|
constructor(private store: Store) {} |
||||
|
} |
||||
@ -0,0 +1,81 @@ |
|||||
|
<ul class="navbar-nav"> |
||||
|
<ng-container |
||||
|
*ngFor="let element of rightPartElements; trackBy: trackByFn" |
||||
|
[ngTemplateOutlet]="element" |
||||
|
[ngTemplateOutletContext]="{ smallScreen: smallScreen }" |
||||
|
></ng-container> |
||||
|
</ul> |
||||
|
|
||||
|
<ng-template #language let-smallScreen="smallScreen"> |
||||
|
<li *ngIf="(dropdownLanguages$ | async)?.length > 0" class="nav-item"> |
||||
|
<div class="dropdown" ngbDropdown #languageDropdown="ngbDropdown" display="static"> |
||||
|
<a |
||||
|
ngbDropdownToggle |
||||
|
class="nav-link" |
||||
|
href="javascript:void(0)" |
||||
|
role="button" |
||||
|
id="dropdownMenuLink" |
||||
|
data-toggle="dropdown" |
||||
|
aria-haspopup="true" |
||||
|
aria-expanded="false" |
||||
|
> |
||||
|
{{ defaultLanguage$ | async }} |
||||
|
</a> |
||||
|
<div |
||||
|
class="dropdown-menu dropdown-menu-right border-0 shadow-sm" |
||||
|
aria-labelledby="dropdownMenuLink" |
||||
|
[class.d-block]="smallScreen && languageDropdown.isOpen()" |
||||
|
> |
||||
|
<a |
||||
|
*ngFor="let lang of dropdownLanguages$ | async" |
||||
|
href="javascript:void(0)" |
||||
|
class="dropdown-item" |
||||
|
(click)="onChangeLang(lang.cultureName)" |
||||
|
>{{ lang?.displayName }}</a |
||||
|
> |
||||
|
</div> |
||||
|
</div> |
||||
|
</li> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #currentUser let-smallScreen="smallScreen"> |
||||
|
<li class="nav-item"> |
||||
|
<ng-template #loginBtn> |
||||
|
<a role="button" class="nav-link" routerLink="/account/login">{{ |
||||
|
'AbpAccount::Login' | abpLocalization |
||||
|
}}</a> |
||||
|
</ng-template> |
||||
|
<div |
||||
|
*ngIf="(currentUser$ | async)?.isAuthenticated; else loginBtn" |
||||
|
ngbDropdown |
||||
|
class="dropdown" |
||||
|
#currentUserDropdown="ngbDropdown" |
||||
|
display="static" |
||||
|
> |
||||
|
<a |
||||
|
ngbDropdownToggle |
||||
|
class="nav-link" |
||||
|
href="javascript:void(0)" |
||||
|
role="button" |
||||
|
id="dropdownMenuLink" |
||||
|
data-toggle="dropdown" |
||||
|
aria-haspopup="true" |
||||
|
aria-expanded="false" |
||||
|
> |
||||
|
{{ (currentUser$ | async)?.userName }} |
||||
|
</a> |
||||
|
<div |
||||
|
class="dropdown-menu dropdown-menu-right border-0 shadow-sm" |
||||
|
aria-labelledby="dropdownMenuLink" |
||||
|
[class.d-block]="smallScreen && currentUserDropdown.isOpen()" |
||||
|
> |
||||
|
<a class="dropdown-item" routerLink="/account/manage-profile" |
||||
|
><i class="fa fa-cog mr-1"></i>{{ 'AbpAccount::ManageYourProfile' | abpLocalization }}</a |
||||
|
> |
||||
|
<a class="dropdown-item" href="javascript:void(0)" (click)="logout()" |
||||
|
><i class="fa fa-power-off mr-1"></i>{{ 'AbpUi::Logout' | abpLocalization }}</a |
||||
|
> |
||||
|
</div> |
||||
|
</div> |
||||
|
</li> |
||||
|
</ng-template> |
||||
@ -0,0 +1,125 @@ |
|||||
|
import { |
||||
|
Component, |
||||
|
AfterViewInit, |
||||
|
TrackByFunction, |
||||
|
TemplateRef, |
||||
|
ViewChild, |
||||
|
OnDestroy, |
||||
|
Input, |
||||
|
} from '@angular/core'; |
||||
|
import { |
||||
|
ABP, |
||||
|
takeUntilDestroy, |
||||
|
SetLanguage, |
||||
|
AuthService, |
||||
|
ConfigState, |
||||
|
ApplicationConfiguration, |
||||
|
SessionState, |
||||
|
} from '@abp/ng.core'; |
||||
|
import { LayoutState } from '../../states/layout.state'; |
||||
|
import { Store, Select } from '@ngxs/store'; |
||||
|
import { eNavigationElementNames } from '../../enums/navigation-element-names'; |
||||
|
import { AddNavigationElement } from '../../actions/layout.actions'; |
||||
|
import { map, filter } from 'rxjs/operators'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { Layout } from '../../models/layout'; |
||||
|
import { Navigate, RouterState } from '@ngxs/router-plugin'; |
||||
|
import snq from 'snq'; |
||||
|
import compare from 'just-compare'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-nav-items', |
||||
|
templateUrl: 'nav-items.component.html', |
||||
|
}) |
||||
|
export class NavItemsComponent implements AfterViewInit, OnDestroy { |
||||
|
@Select(LayoutState.getNavigationElements) |
||||
|
navElements$: Observable<Layout.NavigationElement[]>; |
||||
|
|
||||
|
@Select(ConfigState.getOne('currentUser')) |
||||
|
currentUser$: Observable<ApplicationConfiguration.CurrentUser>; |
||||
|
|
||||
|
@Select(ConfigState.getDeep('localization.languages')) |
||||
|
languages$: Observable<ApplicationConfiguration.Language[]>; |
||||
|
|
||||
|
@ViewChild('currentUser', { static: false, read: TemplateRef }) |
||||
|
currentUserRef: TemplateRef<any>; |
||||
|
|
||||
|
@ViewChild('language', { static: false, read: TemplateRef }) |
||||
|
languageRef: TemplateRef<any>; |
||||
|
|
||||
|
@Input() |
||||
|
smallScreen: boolean; |
||||
|
|
||||
|
rightPartElements: TemplateRef<any>[] = []; |
||||
|
|
||||
|
trackByFn: TrackByFunction<ABP.FullRoute> = (_, element) => element; |
||||
|
|
||||
|
get defaultLanguage$(): Observable<string> { |
||||
|
return this.languages$.pipe( |
||||
|
map( |
||||
|
languages => |
||||
|
snq( |
||||
|
() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, |
||||
|
), |
||||
|
'', |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
get dropdownLanguages$(): Observable<ApplicationConfiguration.Language[]> { |
||||
|
return this.languages$.pipe( |
||||
|
map( |
||||
|
languages => |
||||
|
snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), |
||||
|
[], |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
get selectedLangCulture(): string { |
||||
|
return this.store.selectSnapshot(SessionState.getLanguage); |
||||
|
} |
||||
|
|
||||
|
constructor(private store: Store, private authService: AuthService) {} |
||||
|
|
||||
|
ngAfterViewInit() { |
||||
|
const navigations = this.store |
||||
|
.selectSnapshot(LayoutState.getNavigationElements) |
||||
|
.map(({ name }) => name); |
||||
|
|
||||
|
if (navigations.indexOf(eNavigationElementNames.Language) < 0) { |
||||
|
this.store.dispatch( |
||||
|
new AddNavigationElement([ |
||||
|
{ element: this.languageRef, order: 4, name: eNavigationElementNames.Language }, |
||||
|
{ element: this.currentUserRef, order: 5, name: eNavigationElementNames.User }, |
||||
|
]), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
this.navElements$ |
||||
|
.pipe( |
||||
|
map(elements => elements.map(({ element }) => element)), |
||||
|
filter(elements => !compare(elements, this.rightPartElements)), |
||||
|
takeUntilDestroy(this), |
||||
|
) |
||||
|
.subscribe(elements => { |
||||
|
setTimeout(() => (this.rightPartElements = elements), 0); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy() {} |
||||
|
|
||||
|
onChangeLang(cultureName: string) { |
||||
|
this.store.dispatch(new SetLanguage(cultureName)); |
||||
|
} |
||||
|
|
||||
|
logout() { |
||||
|
this.authService.logout().subscribe(() => { |
||||
|
this.store.dispatch( |
||||
|
new Navigate(['/'], null, { |
||||
|
state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, |
||||
|
}), |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
<ul class="navbar-nav"> |
||||
|
<ng-container |
||||
|
*ngFor="let route of visibleRoutes$ | async; trackBy: trackByFn" |
||||
|
[ngTemplateOutlet]="route?.children?.length ? dropdownLink : defaultLink" |
||||
|
[ngTemplateOutletContext]="{ $implicit: route }" |
||||
|
> |
||||
|
</ng-container> |
||||
|
|
||||
|
<ng-template #defaultLink let-route> |
||||
|
<li class="nav-item" *abpPermission="route.requiredPolicy"> |
||||
|
<a class="nav-link" [routerLink]="[route.url]" |
||||
|
><i *ngIf="route.iconClass" [ngClass]="route.iconClass"></i> |
||||
|
{{ route.name | abpLocalization }}</a |
||||
|
> |
||||
|
</li> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #dropdownLink let-route> |
||||
|
<li |
||||
|
#navbarRootDropdown |
||||
|
*abpPermission="route.requiredPolicy" |
||||
|
[abpVisibility]="routeContainer" |
||||
|
class="nav-item dropdown" |
||||
|
display="static" |
||||
|
(click)=" |
||||
|
navbarRootDropdown.expand |
||||
|
? (navbarRootDropdown.expand = false) |
||||
|
: (navbarRootDropdown.expand = true) |
||||
|
" |
||||
|
> |
||||
|
<a |
||||
|
class="nav-link dropdown-toggle" |
||||
|
data-toggle="dropdown" |
||||
|
aria-haspopup="true" |
||||
|
aria-expanded="false" |
||||
|
href="javascript:void(0)" |
||||
|
> |
||||
|
<i *ngIf="route.iconClass" [ngClass]="route.iconClass"></i> |
||||
|
{{ route.name | abpLocalization }} |
||||
|
</a> |
||||
|
<div |
||||
|
#routeContainer |
||||
|
class="dropdown-menu border-0 shadow-sm" |
||||
|
(click)="$event.preventDefault(); $event.stopPropagation()" |
||||
|
[class.d-block]="smallScreen && navbarRootDropdown.expand" |
||||
|
> |
||||
|
<ng-template |
||||
|
#forTemplate |
||||
|
ngFor |
||||
|
[ngForOf]="route.children" |
||||
|
[ngForTrackBy]="trackByFn" |
||||
|
[ngForTemplate]="childWrapper" |
||||
|
></ng-template> |
||||
|
</div> |
||||
|
</li> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #childWrapper let-child> |
||||
|
<ng-template |
||||
|
[ngTemplateOutlet]="child?.children?.length ? dropdownChild : defaultChild" |
||||
|
[ngTemplateOutletContext]="{ $implicit: child }" |
||||
|
></ng-template> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #defaultChild let-child> |
||||
|
<div class="dropdown-submenu" *abpPermission="child.requiredPolicy"> |
||||
|
<a class="dropdown-item" [routerLink]="[child.url]"> |
||||
|
<i *ngIf="child.iconClass" [ngClass]="child.iconClass"></i> |
||||
|
{{ child.name | abpLocalization }}</a |
||||
|
> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #dropdownChild let-child> |
||||
|
<div |
||||
|
[abpVisibility]="childrenContainer" |
||||
|
class="dropdown-submenu" |
||||
|
ngbDropdown |
||||
|
#dropdownSubmenu="ngbDropdown" |
||||
|
[display]="isDropdownChildDynamic ? 'dynamic' : 'static'" |
||||
|
placement="right-top" |
||||
|
[autoClose]="true" |
||||
|
*abpPermission="child.requiredPolicy" |
||||
|
(openChange)="openChange($event, childrenContainer)" |
||||
|
> |
||||
|
<div ngbDropdownToggle [class.dropdown-toggle]="false"> |
||||
|
<a |
||||
|
abpEllipsis="210px" |
||||
|
[abpEllipsisEnabled]="isDropdownChildDynamic" |
||||
|
role="button" |
||||
|
class="btn d-block text-left dropdown-toggle" |
||||
|
> |
||||
|
<i *ngIf="child.iconClass" [ngClass]="child.iconClass"></i> |
||||
|
{{ child.name | abpLocalization }} |
||||
|
</a> |
||||
|
</div> |
||||
|
<div |
||||
|
#childrenContainer |
||||
|
class="dropdown-menu border-0 shadow-sm" |
||||
|
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()" |
||||
|
> |
||||
|
<ng-template |
||||
|
ngFor |
||||
|
[ngForOf]="child.children" |
||||
|
[ngForTrackBy]="trackByFn" |
||||
|
[ngForTemplate]="childWrapper" |
||||
|
></ng-template> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</ul> |
||||
@ -0,0 +1,51 @@ |
|||||
|
import { Component, OnInit, TrackByFunction, Input, Renderer2 } from '@angular/core'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { ABP, ConfigState } from '@abp/ng.core'; |
||||
|
import { map } from 'rxjs/operators'; |
||||
|
import { Select } from '@ngxs/store'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-routes', |
||||
|
templateUrl: 'routes.component.html', |
||||
|
}) |
||||
|
export class RoutesComponent { |
||||
|
@Select(ConfigState.getOne('routes')) |
||||
|
routes$: Observable<ABP.FullRoute[]>; |
||||
|
|
||||
|
@Input() |
||||
|
smallScreen: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
isDropdownChildDynamic: boolean; |
||||
|
|
||||
|
get visibleRoutes$(): Observable<ABP.FullRoute[]> { |
||||
|
return this.routes$.pipe(map(routes => getVisibleRoutes(routes))); |
||||
|
} |
||||
|
|
||||
|
trackByFn: TrackByFunction<ABP.FullRoute> = (_, item) => item.name; |
||||
|
|
||||
|
constructor(private renderer: Renderer2) {} |
||||
|
|
||||
|
openChange(event: boolean, childrenContainer: HTMLDivElement) { |
||||
|
if (!event) { |
||||
|
Object.keys(childrenContainer.style) |
||||
|
.filter(key => Number.isInteger(+key)) |
||||
|
.forEach(key => { |
||||
|
this.renderer.removeStyle(childrenContainer, childrenContainer.style[key]); |
||||
|
}); |
||||
|
this.renderer.removeStyle(childrenContainer, 'left'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function getVisibleRoutes(routes: ABP.FullRoute[]) { |
||||
|
return routes.reduce((acc, val) => { |
||||
|
if (val.invisible) return acc; |
||||
|
|
||||
|
if (val.children && val.children.length) { |
||||
|
val.children = getVisibleRoutes(val.children); |
||||
|
} |
||||
|
|
||||
|
return [...acc, val]; |
||||
|
}, []); |
||||
|
} |
||||
Loading…
Reference in new issue