mirror of https://github.com/abpframework/abp.git
35 changed files with 401 additions and 214 deletions
@ -1,20 +0,0 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
import { Options } from './models/options'; |
|||
import { ACCOUNT_OPTIONS, optionsFactory } from './tokens/options.token'; |
|||
|
|||
@NgModule({}) |
|||
export class RootAccountModule { |
|||
static forRoot(options = {} as Options): ModuleWithProviders { |
|||
return { |
|||
ngModule: RootAccountModule, |
|||
providers: [ |
|||
{ provide: ACCOUNT_OPTIONS, useValue: options }, |
|||
{ |
|||
provide: 'ACCOUNT_OPTIONS', |
|||
useFactory: optionsFactory, |
|||
deps: [ACCOUNT_OPTIONS], |
|||
}, |
|||
], |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,174 @@ |
|||
import { |
|||
Directive, |
|||
EmbeddedViewRef, |
|||
Input, |
|||
IterableChangeRecord, |
|||
IterableChanges, |
|||
IterableDiffer, |
|||
IterableDiffers, |
|||
OnChanges, |
|||
TemplateRef, |
|||
TrackByFunction, |
|||
ViewContainerRef, |
|||
} from '@angular/core'; |
|||
import compare from 'just-compare'; |
|||
|
|||
export type CompareFn<T = any> = (value: T, comparison: T) => boolean; |
|||
|
|||
class AbpForContext { |
|||
constructor(public $implicit: any, public index: number, public count: number, public list: any[]) {} |
|||
} |
|||
|
|||
class RecordView { |
|||
constructor(public record: IterableChangeRecord<any>, public view: EmbeddedViewRef<AbpForContext>) {} |
|||
} |
|||
|
|||
@Directive({ |
|||
selector: '[abpFor]', |
|||
}) |
|||
export class ForDirective implements OnChanges { |
|||
@Input('abpForOf') |
|||
items: any[]; |
|||
|
|||
@Input('abpForOrderBy') |
|||
orderBy: string; |
|||
|
|||
@Input('abpForOrderDir') |
|||
orderDir: 'ASC' | 'DESC'; |
|||
|
|||
@Input('abpForFilterBy') |
|||
filterBy: string; |
|||
|
|||
@Input('abpForFilterVal') |
|||
filterVal: any; |
|||
|
|||
@Input('abpForTrackBy') |
|||
trackBy; |
|||
|
|||
@Input('abpForCompareBy') |
|||
compareBy: CompareFn; |
|||
|
|||
@Input('abpForEmptyRef') |
|||
emptyRef: TemplateRef<any>; |
|||
|
|||
private differ: IterableDiffer<any>; |
|||
|
|||
private isShowEmptyRef: boolean; |
|||
|
|||
get compareFn(): CompareFn { |
|||
return this.compareBy || compare; |
|||
} |
|||
|
|||
get trackByFn(): TrackByFunction<any> { |
|||
return this.trackBy || ((index: number, item: any) => (item as any).id || index); |
|||
} |
|||
|
|||
constructor( |
|||
private tempRef: TemplateRef<AbpForContext>, |
|||
private vcRef: ViewContainerRef, |
|||
private differs: IterableDiffers, |
|||
) {} |
|||
|
|||
private iterateOverAppliedOperations(changes: IterableChanges<any>) { |
|||
const rw: RecordView[] = []; |
|||
|
|||
changes.forEachOperation((record: IterableChangeRecord<any>, previousIndex: number, currentIndex: number) => { |
|||
if (record.previousIndex == null) { |
|||
const view = this.vcRef.createEmbeddedView( |
|||
this.tempRef, |
|||
new AbpForContext(null, -1, -1, this.items), |
|||
currentIndex, |
|||
); |
|||
|
|||
rw.push(new RecordView(record, view)); |
|||
} else if (currentIndex == null) { |
|||
this.vcRef.remove(previousIndex); |
|||
} else { |
|||
const view = this.vcRef.get(previousIndex); |
|||
this.vcRef.move(view, currentIndex); |
|||
|
|||
rw.push(new RecordView(record, view as EmbeddedViewRef<AbpForContext>)); |
|||
} |
|||
}); |
|||
|
|||
for (let i = 0, l = rw.length; i < l; i++) { |
|||
rw[i].view.context.$implicit = rw[i].record.item; |
|||
} |
|||
} |
|||
|
|||
private iterateOverAttachedViews(changes: IterableChanges<any>) { |
|||
for (let i = 0, l = this.vcRef.length; i < l; i++) { |
|||
const viewRef = this.vcRef.get(i) as EmbeddedViewRef<AbpForContext>; |
|||
viewRef.context.index = i; |
|||
viewRef.context.count = l; |
|||
viewRef.context.list = this.items; |
|||
} |
|||
|
|||
changes.forEachIdentityChange((record: IterableChangeRecord<any>) => { |
|||
const viewRef = this.vcRef.get(record.currentIndex) as EmbeddedViewRef<AbpForContext>; |
|||
viewRef.context.$implicit = record.item; |
|||
}); |
|||
} |
|||
|
|||
private projectItems(items: any[]): void { |
|||
if (!items.length && this.emptyRef) { |
|||
this.vcRef.createEmbeddedView(this.emptyRef).rootNodes; |
|||
this.isShowEmptyRef = true; |
|||
|
|||
return; |
|||
} |
|||
|
|||
if (this.emptyRef && this.isShowEmptyRef) { |
|||
this.vcRef.clear(); |
|||
this.isShowEmptyRef = false; |
|||
} |
|||
|
|||
if (!this.differ && items) { |
|||
this.differ = this.differs.find(items).create(this.trackByFn); |
|||
} |
|||
|
|||
if (this.differ) { |
|||
const changes = this.differ.diff(items); |
|||
|
|||
if (changes) { |
|||
this.iterateOverAppliedOperations(changes); |
|||
this.iterateOverAttachedViews(changes); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private sortItems(items: any[]) { |
|||
if (this.orderBy) { |
|||
items.sort((a, b) => (a[this.orderBy] > b[this.orderBy] ? 1 : a[this.orderBy] < b[this.orderBy] ? -1 : 0)); |
|||
} else { |
|||
items.sort(); |
|||
} |
|||
} |
|||
|
|||
ngOnChanges() { |
|||
let items = [...this.items] as any[]; |
|||
if (!Array.isArray(items)) return; |
|||
|
|||
const compareFn = this.compareFn; |
|||
|
|||
if (typeof this.filterBy !== 'undefined') { |
|||
items = items.filter(item => compareFn(item[this.filterBy], this.filterVal)); |
|||
} |
|||
|
|||
switch (this.orderDir) { |
|||
case 'ASC': |
|||
this.sortItems(items); |
|||
this.projectItems(items); |
|||
break; |
|||
|
|||
case 'DESC': |
|||
this.sortItems(items); |
|||
items.reverse(); |
|||
this.projectItems(items); |
|||
break; |
|||
|
|||
default: |
|||
this.projectItems(items); |
|||
} |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
export * from './autofocus.directive'; |
|||
export * from './ellipsis.directive'; |
|||
export * from './for.directive'; |
|||
export * from './form-submit.directive'; |
|||
export * from './permission.directive'; |
|||
export * from './visibility.directive'; |
|||
|
|||
@ -1,11 +0,0 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
|
|||
@NgModule({}) |
|||
export class RootFeatureManagementModule { |
|||
static forRoot(): ModuleWithProviders { |
|||
return { |
|||
ngModule: RootFeatureManagementModule, |
|||
providers: [], |
|||
}; |
|||
} |
|||
} |
|||
@ -1,3 +1,2 @@ |
|||
export * from './lib/root-feature-management.module'; |
|||
export * from './lib/feature-management.module'; |
|||
export * from './lib/components'; |
|||
|
|||
@ -1,11 +0,0 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
|
|||
@NgModule({}) |
|||
export class RootIdentityModule { |
|||
static forRoot(): ModuleWithProviders { |
|||
return { |
|||
ngModule: RootIdentityModule, |
|||
providers: [], |
|||
}; |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
|
|||
@NgModule({}) |
|||
export class RootPermissionManagementModule { |
|||
static forRoot(): ModuleWithProviders { |
|||
return { |
|||
ngModule: RootPermissionManagementModule, |
|||
providers: [], |
|||
}; |
|||
} |
|||
} |
|||
@ -1 +0,0 @@ |
|||
export * from './setting/setting.component'; |
|||
@ -1,17 +0,0 @@ |
|||
import { SettingTab } from '@abp/ng.theme.shared'; |
|||
import { Injectable, OnInit } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import snq from 'snq'; |
|||
|
|||
@Injectable() |
|||
export class InitialService { |
|||
public settings: SettingTab[]; |
|||
|
|||
constructor(private router: Router) { |
|||
this.settings = this.router.config |
|||
.map(route => snq(() => route.data.routes.settings)) |
|||
.filter(settings => settings && settings.length) |
|||
.reduce((acc, val) => [...acc, ...val], []) |
|||
.sort((a, b) => a.order - b.order); |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
<div class="row entry-row"> |
|||
<div class="col-auto"> |
|||
<h1 class="content-header-title">{{ 'AbpSettingManagement::Settings' | abpLocalization }}</h1> |
|||
</div> |
|||
<!-- <div id="breadcrumb" class="col-md-auto pl-md-0"> |
|||
<abp-breadcrumb></abp-breadcrumb> |
|||
</div> --> |
|||
<div class="col"> |
|||
<div class="text-lg-right pt-2" id="AbpContentToolbar"></div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="SettingManagementWrapper"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div *ngIf="!settingManagementService.settings.length" class="text-center"> |
|||
<i class="fa fa-spinner fa-spin"></i> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-3"> |
|||
<ul class="nav flex-column nav-pills" id="nav-tab" role="tablist"> |
|||
<li |
|||
*abpFor=" |
|||
let setting of settingManagementService.settings; |
|||
trackBy: trackByFn; |
|||
orderBy: 'order'; |
|||
orderDir: 'ASC' |
|||
" |
|||
(click)="settingManagementService.setSelected(setting)" |
|||
class="nav-item" |
|||
[abpPermission]="setting.requiredPolicy" |
|||
> |
|||
<a |
|||
class="nav-link" |
|||
[id]="setting.name + '-tab'" |
|||
role="tab" |
|||
[class.active]="setting.name === settingManagementService.selected.name" |
|||
>{{ setting.name | abpLocalization }}</a |
|||
> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="col-9"> |
|||
<div *ngIf="settingManagementService.settings.length" class="tab-content"> |
|||
<div |
|||
class="tab-pane fade show active" |
|||
[id]="settingManagementService.selected.name + '-tab'" |
|||
role="tabpanel" |
|||
> |
|||
<h2>{{ settingManagementService.selected.name | abpLocalization }}</h2> |
|||
<hr class="my-4" /> |
|||
<router-outlet></router-outlet> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,37 @@ |
|||
import { eLayoutType } from '@abp/ng.core'; |
|||
import { SettingTab } from '@abp/ng.theme.shared'; |
|||
import { Component, TrackByFunction } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import { timer } from 'rxjs'; |
|||
import { SettingManagementService } from '../services/setting-management.service'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-setting-layout', |
|||
templateUrl: './setting-layout.component.html', |
|||
}) |
|||
export class SettingLayoutComponent { |
|||
// required for dynamic component
|
|||
static type = eLayoutType.setting; |
|||
|
|||
trackByFn: TrackByFunction<SettingTab> = (_, item) => item.name; |
|||
|
|||
constructor(public settingManagementService: SettingManagementService, private router: Router) { |
|||
if ( |
|||
settingManagementService.selected && |
|||
this.router.url !== settingManagementService.selected.url && |
|||
settingManagementService.settings.length |
|||
) { |
|||
settingManagementService.setSelected(settingManagementService.settings[0]); |
|||
} |
|||
} |
|||
|
|||
ngOnDestroy() {} |
|||
|
|||
ngAfterViewInit() { |
|||
timer(250).subscribe(() => { |
|||
if (!this.settingManagementService.settings.length) { |
|||
this.settingManagementService.setSettings(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
<div class="row entry-row"> |
|||
<div class="col-auto"> |
|||
<h1 class="content-header-title">{{ 'AbpSettingManagement::Settings' | abpLocalization }}</h1> |
|||
</div> |
|||
<div id="breadcrumb" class="col-md-auto pl-md-0"> |
|||
<abp-breadcrumb></abp-breadcrumb> |
|||
</div> |
|||
<div class="col"> |
|||
<div class="text-lg-right pt-2" id="AbpContentToolbar"></div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="SettingManagementWrapper"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-3"> |
|||
<ul class="nav flex-column nav-pills" id="T515ccf3324254f41a4a9a6b34b0dae56" role="tablist"> |
|||
<li |
|||
*ngFor="let setting of settings" |
|||
(click)="selected = setting" |
|||
class="nav-item" |
|||
[abpPermission]="setting.requiredPolicy" |
|||
> |
|||
<a |
|||
class="nav-link" |
|||
[id]="setting.name + '-tab'" |
|||
role="tab" |
|||
[class.active]="setting.name === selected.name" |
|||
>{{ setting.name | abpLocalization }}</a |
|||
> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="col-9"> |
|||
<div class="tab-content"> |
|||
<div class="tab-pane fade show active" [id]="selected.name + '-tab'" role="tabpanel"> |
|||
<h2>{{ selected.name | abpLocalization }}</h2> |
|||
<hr class="my-4" /> |
|||
|
|||
<ng-container |
|||
*ngComponentOutlet="selected.component" |
|||
[abpPermission]="selected.requiredPolicy" |
|||
></ng-container> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -1,20 +0,0 @@ |
|||
import { Component, OnInit } from '@angular/core'; |
|||
import { SettingTab, fade } from '@abp/ng.theme.shared'; |
|||
import { InitialService } from '../services/initial.service'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-setting', |
|||
templateUrl: './setting.component.html', |
|||
}) |
|||
export class SettingComponent implements OnInit { |
|||
settings: SettingTab[]; |
|||
|
|||
selected = {} as SettingTab; |
|||
|
|||
constructor(private initialService: InitialService) {} |
|||
|
|||
ngOnInit() { |
|||
this.settings = this.initialService.settings; |
|||
this.selected = this.settings[0]; |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
|
|||
@NgModule({}) |
|||
export class RootSettingManagementModule { |
|||
static forRoot(): ModuleWithProviders { |
|||
return { |
|||
ngModule: RootSettingManagementModule, |
|||
providers: [], |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
import { SettingTab } from '@abp/ng.theme.shared'; |
|||
import { Injectable } from '@angular/core'; |
|||
import { Router, RouteConfigLoadEnd, NavigationEnd } from '@angular/router'; |
|||
import { Navigate } from '@ngxs/router-plugin'; |
|||
import { Store } from '@ngxs/store'; |
|||
import { filter } from 'rxjs/operators'; |
|||
import { takeUntilDestroy } from '@abp/ng.core'; |
|||
import { Subscription, timer } from 'rxjs'; |
|||
|
|||
@Injectable({ providedIn: 'root' }) |
|||
export class SettingManagementService { |
|||
settings: SettingTab[] = []; |
|||
|
|||
selected = {} as SettingTab; |
|||
|
|||
constructor(private router: Router, private store: Store) { |
|||
let timeout: Subscription; |
|||
this.router.events |
|||
.pipe( |
|||
filter(event => event instanceof RouteConfigLoadEnd), |
|||
takeUntilDestroy(this), |
|||
) |
|||
.subscribe(event => { |
|||
if (timeout) timeout.unsubscribe(); |
|||
timeout = timer(150).subscribe(() => { |
|||
this.setSettings(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
ngOnDestroy() {} |
|||
|
|||
setSettings() { |
|||
setTimeout(() => { |
|||
const route = this.router.config.find(r => r.path === 'setting-management'); |
|||
this.settings = route.data.settings.sort((a, b) => a.order - b.order); |
|||
this.checkSelected(); |
|||
}, 0); |
|||
} |
|||
|
|||
checkSelected() { |
|||
this.selected = this.settings.find(setting => setting.url === this.router.url) || ({} as SettingTab); |
|||
|
|||
if (!this.selected.name && this.settings.length) { |
|||
this.setSelected(this.settings[0]); |
|||
} |
|||
} |
|||
|
|||
setSelected(selected: SettingTab) { |
|||
this.selected = selected; |
|||
this.store.dispatch(new Navigate([selected.url])); |
|||
} |
|||
} |
|||
@ -1,15 +1,14 @@ |
|||
import { CoreModule, noop } from '@abp/ng.core'; |
|||
import { NgModule, ModuleWithProviders, APP_INITIALIZER, Self } from '@angular/core'; |
|||
import { SettingComponent } from './components/setting/setting.component'; |
|||
import { SettingManagementRoutingModule } from './setting-management-routing.module'; |
|||
import { CoreModule } from '@abp/ng.core'; |
|||
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
|||
import { InitialService } from './components/services/initial.service'; |
|||
import { NgModule } from '@angular/core'; |
|||
import { SettingLayoutComponent } from './components/setting-layout.component'; |
|||
import { SettingManagementRoutingModule } from './setting-management-routing.module'; |
|||
|
|||
export const SETTING_LAYOUT = SettingLayoutComponent; |
|||
|
|||
@NgModule({ |
|||
declarations: [SettingComponent], |
|||
declarations: [SETTING_LAYOUT], |
|||
imports: [SettingManagementRoutingModule, CoreModule, ThemeSharedModule], |
|||
providers: [InitialService], |
|||
entryComponents: [SETTING_LAYOUT], |
|||
}) |
|||
export class SettingManagementModule { |
|||
constructor(@Self() initialService: InitialService) {} |
|||
} |
|||
export class SettingManagementModule {} |
|||
|
|||
@ -1,4 +1,3 @@ |
|||
export * from './lib/root-setting-management.module'; |
|||
export * from './lib/setting-management.module'; |
|||
export * from './lib/components'; |
|||
export * from './lib/constants'; |
|||
export * from './lib/components/setting-layout.component'; |
|||
|
|||
@ -1,11 +0,0 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
|
|||
@NgModule({}) |
|||
export class RootTenantManagementModule { |
|||
static forRoot(): ModuleWithProviders { |
|||
return { |
|||
ngModule: RootTenantManagementModule, |
|||
providers: [], |
|||
}; |
|||
} |
|||
} |
|||
@ -1,8 +1,6 @@ |
|||
import { Type } from '@angular/core'; |
|||
|
|||
export interface SettingTab { |
|||
name: string; |
|||
order: number; |
|||
component: Type<any>; |
|||
requiredPolicy?: string; |
|||
url?: string; |
|||
} |
|||
|
|||
Loading…
Reference in new issue