mirror of https://github.com/abpframework/abp
Merge branch 'dev' of https://github.com/volosoft/abp into dev
commit
f32ebc84cb
@ -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