bixi
Version:
企业级中后台前端解决方案
160 lines (144 loc) • 5.08 kB
text/typescript
import { Injectable, Injector, OnDestroy, Optional } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { IBreadcrumb } from './layout.type';
export const LAYOUT = 'layout';
interface IOptions {
breadFn?: (items: IBreadcrumb[]) => IBreadcrumb[];
}
const uniqueElementsBy = (breads: IBreadcrumb[], fn: (bread1: IBreadcrumb, bread2: IBreadcrumb) => boolean) =>
breads.reduce((result: IBreadcrumb[], v: IBreadcrumb) => {
if (!result.some((x: IBreadcrumb) => fn(v, x))) { // 判断是否重复
result.push(v);
}
return result;
}, []);
export class BixiLayoutService implements OnDestroy {
openChange = new BehaviorSubject<boolean>(true);
breadcrumb$ = new BehaviorSubject<IBreadcrumb[]>([]);
menuVisible$ = new BehaviorSubject<boolean>(true);
headerVisible$ = new BehaviorSubject<boolean>(true);
// 这里记录路由切换时,组件加载时,传入的参数
options$ = new BehaviorSubject<IOptions>({});
routerChange = new Subscription();
constructor(
private injector: Injector,
private router: Router,
private title: Title,
private i18n: TranslateService
) {
this.routerChange = this.router.events
.pipe(filter((e) => e instanceof NavigationEnd))
.subscribe(() => {
this.updateBreads();
});
}
private updateBreads() {
let route = this.injector.get(ActivatedRoute);
let titles: IBreadcrumb[] = [];
while (route.firstChild) {
route = route.firstChild;
const data = (route.snapshot && route.snapshot.data) || {};
if (data.breadcrumbs) {
if (typeof data.breadcrumbs === 'function') {
const breadcrumbs = data.breadcrumbs(route);
titles = [...titles, ...breadcrumbs];
} else if (Array.isArray(data.breadcrumbs)) {
titles = [...titles, ...data.breadcrumbs];
} else {
console.warn('route breadcrumbs 只支持 Array 和 function, 请查看路由配置');
}
titles = titles.map((bread: IBreadcrumb) => {
if (bread.titleI18n) {
return {
name: this.i18n.instant(bread.titleI18n),
link: bread.link || bread.path
};
}
return bread;
});
} else if (data.title || data.titleI18n) {
if (this.i18n) {
titles.push({
name: data.title ? data.title : this.i18n.instant(data.titleI18n),
link: data.path
});
} else {
titles.push({
name: data.title,
link: data.path
});
}
} else {
// nothing
}
}
this.breadcrumb$.next(uniqueElementsBy(this.formatBreakcrumb(titles), (bread1: IBreadcrumb, bread2: IBreadcrumb) => {
return bread1.name === bread2.name && bread1.path === bread2.path && bread1.link === bread2.link;
}));
this.options$.next({});
}
toggleMenu(open?: boolean) {
if (open === undefined) {
this.openChange.next(!this.openChange.value);
} else {
this.openChange.next(!!open);
}
}
toggleHeader(show?: boolean) {
if (show === undefined) {
this.headerVisible$.next(!this.headerVisible$.value);
} else {
this.headerVisible$.next(!!show);
}
}
private formatBreakcrumb(breads: IBreadcrumb[]) {
const options = this.options$.value;
if (typeof options.breadFn === 'function') {
return options.breadFn(breads);
}
return breads;
}
setBreadcrumbs(breadFn: (items: IBreadcrumb[]) => IBreadcrumb[]) {
// 由于 router 变化钩子函数在组件的 constructor 之后执行,这里为了保证 setBreadcrumbs 的时效性,使用定时器
setTimeout(() => {
this.options$.next({
...this.options$.value,
breadFn
});
this.updateBreads();
});
}
private getByRoute(): string {
let next = this.injector.get(ActivatedRoute);
while (next.firstChild) next = next.firstChild;
const data = (next.snapshot && next.snapshot.data) || {};
if (typeof data.breadcrumbs === 'function') {
const breadcrumbs = data.breadcrumbs(next);
data.title = breadcrumbs && breadcrumbs.length ? breadcrumbs[breadcrumbs.length - 1].name : '';
}
if (data.titleI18n && this.i18n && !data.title) {
data.title = this.i18n.instant(data.titleI18n);
}
return data.title;
}
setPageTitle(titleFn?: (breads: IBreadcrumb[]) => string) {
if (typeof titleFn === 'function') {
this.title.setTitle(titleFn(this.breadcrumb$.value));
return;
}
const title = this.getByRoute();
if (title) {
this.title.setTitle(`${this.getByRoute()}`);
} else {
this.title.setTitle('Bixi');
}
}
ngOnDestroy(): void {
this.routerChange.unsubscribe();
}
}