[web-app] ng-alain模版工程初始化
This commit is contained in:
5
web-app/src/app/core/README.md
Normal file
5
web-app/src/app/core/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
### CoreModule
|
||||
|
||||
**应** 仅只留 `providers` 属性。
|
||||
|
||||
**作用:** 一些通用服务,例如:用户消息、HTTP数据访问。
|
||||
15
web-app/src/app/core/core.module.ts
Normal file
15
web-app/src/app/core/core.module.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { NgModule, Optional, SkipSelf } from '@angular/core';
|
||||
import { throwIfAlreadyLoaded } from './module-import-guard';
|
||||
|
||||
import { I18NService } from './i18n/i18n.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
I18NService
|
||||
]
|
||||
})
|
||||
export class CoreModule {
|
||||
constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
|
||||
throwIfAlreadyLoaded(parentModule, 'CoreModule');
|
||||
}
|
||||
}
|
||||
84
web-app/src/app/core/i18n/i18n.service.spec.ts
Normal file
84
web-app/src/app/core/i18n/i18n.service.spec.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { TestBed, TestBedStatic } from '@angular/core/testing';
|
||||
import { DelonLocaleService, SettingsService } from '@delon/theme';
|
||||
import { NzSafeAny } from 'ng-zorro-antd/core/types';
|
||||
import { NzI18nService } from 'ng-zorro-antd/i18n';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { I18NService } from './i18n.service';
|
||||
|
||||
describe('Service: I18n', () => {
|
||||
let injector: TestBedStatic;
|
||||
let srv: I18NService;
|
||||
const MockSettingsService: NzSafeAny = {
|
||||
layout: {
|
||||
lang: null
|
||||
}
|
||||
};
|
||||
const MockNzI18nService = {
|
||||
setLocale: () => {},
|
||||
setDateLocale: () => {}
|
||||
};
|
||||
const MockDelonLocaleService = {
|
||||
setLocale: () => {}
|
||||
};
|
||||
const MockTranslateService = {
|
||||
getBrowserLang: jasmine.createSpy('getBrowserLang'),
|
||||
addLangs: () => {},
|
||||
setLocale: () => {},
|
||||
getDefaultLang: () => '',
|
||||
use: (lang: string) => of(lang),
|
||||
instant: jasmine.createSpy('instant')
|
||||
};
|
||||
|
||||
function genModule(): void {
|
||||
injector = TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [
|
||||
I18NService,
|
||||
{ provide: SettingsService, useValue: MockSettingsService },
|
||||
{ provide: NzI18nService, useValue: MockNzI18nService },
|
||||
{ provide: DelonLocaleService, useValue: MockDelonLocaleService }
|
||||
]
|
||||
});
|
||||
srv = TestBed.inject(I18NService);
|
||||
}
|
||||
|
||||
it('should working', () => {
|
||||
spyOnProperty(navigator, 'languages').and.returnValue(['zh-CN']);
|
||||
genModule();
|
||||
expect(srv).toBeTruthy();
|
||||
expect(srv.defaultLang).toBe('zh-CN');
|
||||
srv.fanyi('a');
|
||||
srv.fanyi('a', {});
|
||||
});
|
||||
|
||||
it('should be used layout as default language', () => {
|
||||
MockSettingsService.layout.lang = 'en-US';
|
||||
const navSpy = spyOnProperty(navigator, 'languages');
|
||||
genModule();
|
||||
expect(navSpy).not.toHaveBeenCalled();
|
||||
expect(srv.defaultLang).toBe('en-US');
|
||||
MockSettingsService.layout.lang = null;
|
||||
});
|
||||
|
||||
it('should be used browser as default language', () => {
|
||||
spyOnProperty(navigator, 'languages').and.returnValue(['zh-TW']);
|
||||
genModule();
|
||||
expect(srv.defaultLang).toBe('zh-TW');
|
||||
});
|
||||
|
||||
it('should be use default language when the browser language is not in the list', () => {
|
||||
spyOnProperty(navigator, 'languages').and.returnValue(['es-419']);
|
||||
genModule();
|
||||
expect(srv.defaultLang).toBe('zh-CN');
|
||||
});
|
||||
|
||||
it('should be trigger notify when changed language', () => {
|
||||
genModule();
|
||||
srv.use('en-US', {});
|
||||
srv.change.subscribe(lang => {
|
||||
expect(lang).toBe('en-US');
|
||||
});
|
||||
});
|
||||
});
|
||||
116
web-app/src/app/core/i18n/i18n.service.ts
Normal file
116
web-app/src/app/core/i18n/i18n.service.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
// 请参考:https://ng-alain.com/docs/i18n
|
||||
import { Platform } from '@angular/cdk/platform';
|
||||
import { registerLocaleData } from '@angular/common';
|
||||
import ngEn from '@angular/common/locales/en';
|
||||
import ngZh from '@angular/common/locales/zh';
|
||||
import ngZhTw from '@angular/common/locales/zh-Hant';
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
DelonLocaleService,
|
||||
en_US as delonEnUS,
|
||||
SettingsService,
|
||||
zh_CN as delonZhCn,
|
||||
zh_TW as delonZhTw,
|
||||
_HttpClient,
|
||||
AlainI18nBaseService
|
||||
} from '@delon/theme';
|
||||
import { AlainConfigService } from '@delon/util/config';
|
||||
import { enUS as dfEn, zhCN as dfZhCn, zhTW as dfZhTw } from 'date-fns/locale';
|
||||
import { NzSafeAny } from 'ng-zorro-antd/core/types';
|
||||
import { en_US as zorroEnUS, NzI18nService, zh_CN as zorroZhCN, zh_TW as zorroZhTW } from 'ng-zorro-antd/i18n';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
interface LangConfigData {
|
||||
abbr: string;
|
||||
text: string;
|
||||
ng: NzSafeAny;
|
||||
zorro: NzSafeAny;
|
||||
date: NzSafeAny;
|
||||
delon: NzSafeAny;
|
||||
}
|
||||
|
||||
const DEFAULT = 'zh-CN';
|
||||
const LANGS: { [key: string]: LangConfigData } = {
|
||||
'zh-CN': {
|
||||
text: '简体中文',
|
||||
ng: ngZh,
|
||||
zorro: zorroZhCN,
|
||||
date: dfZhCn,
|
||||
delon: delonZhCn,
|
||||
abbr: '🇨🇳'
|
||||
},
|
||||
'zh-TW': {
|
||||
text: '繁体中文',
|
||||
ng: ngZhTw,
|
||||
zorro: zorroZhTW,
|
||||
date: dfZhTw,
|
||||
delon: delonZhTw,
|
||||
abbr: '🇭🇰'
|
||||
},
|
||||
'en-US': {
|
||||
text: 'English',
|
||||
ng: ngEn,
|
||||
zorro: zorroEnUS,
|
||||
date: dfEn,
|
||||
delon: delonEnUS,
|
||||
abbr: '🇬🇧'
|
||||
}
|
||||
};
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class I18NService extends AlainI18nBaseService {
|
||||
protected _defaultLang = DEFAULT;
|
||||
private _langs = Object.keys(LANGS).map(code => {
|
||||
const item = LANGS[code];
|
||||
return { code, text: item.text, abbr: item.abbr };
|
||||
});
|
||||
|
||||
constructor(
|
||||
private http: _HttpClient,
|
||||
private settings: SettingsService,
|
||||
private nzI18nService: NzI18nService,
|
||||
private delonLocaleService: DelonLocaleService,
|
||||
private platform: Platform,
|
||||
cogSrv: AlainConfigService
|
||||
) {
|
||||
super(cogSrv);
|
||||
|
||||
const defaultLang = this.getDefaultLang();
|
||||
this._defaultLang = this._langs.findIndex(w => w.code === defaultLang) === -1 ? DEFAULT : defaultLang;
|
||||
}
|
||||
|
||||
private getDefaultLang(): string {
|
||||
if (!this.platform.isBrowser) {
|
||||
return DEFAULT;
|
||||
}
|
||||
if (this.settings.layout.lang) {
|
||||
return this.settings.layout.lang;
|
||||
}
|
||||
let res = (navigator.languages ? navigator.languages[0] : null) || navigator.language;
|
||||
const arr = res.split('-');
|
||||
return arr.length <= 1 ? res : `${arr[0]}-${arr[1].toUpperCase()}`;
|
||||
}
|
||||
|
||||
loadLangData(lang: string): Observable<NzSafeAny> {
|
||||
return this.http.get(`assets/tmp/i18n/${lang}.json`);
|
||||
}
|
||||
|
||||
use(lang: string, data: Record<string, unknown>): void {
|
||||
if (this._currentLang === lang) return;
|
||||
|
||||
this._data = this.flatData(data, []);
|
||||
|
||||
const item = LANGS[lang];
|
||||
registerLocaleData(item.ng);
|
||||
this.nzI18nService.setLocale(item.zorro);
|
||||
this.nzI18nService.setDateLocale(item.date);
|
||||
this.delonLocaleService.setLocale(item.delon);
|
||||
this._currentLang = lang;
|
||||
|
||||
this._change$.next(lang);
|
||||
}
|
||||
|
||||
getLangs(): Array<{ code: string; text: string; abbr: string }> {
|
||||
return this._langs;
|
||||
}
|
||||
}
|
||||
4
web-app/src/app/core/index.ts
Normal file
4
web-app/src/app/core/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './i18n/i18n.service';
|
||||
export * from './module-import-guard';
|
||||
export * from './net/default.interceptor';
|
||||
export * from './startup/startup.service';
|
||||
6
web-app/src/app/core/module-import-guard.ts
Normal file
6
web-app/src/app/core/module-import-guard.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// https://angular.io/guide/styleguide#style-04-12
|
||||
export function throwIfAlreadyLoaded(parentModule: any, moduleName: string): void {
|
||||
if (parentModule) {
|
||||
throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`);
|
||||
}
|
||||
}
|
||||
261
web-app/src/app/core/net/default.interceptor.ts
Normal file
261
web-app/src/app/core/net/default.interceptor.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
import {
|
||||
HttpErrorResponse,
|
||||
HttpEvent,
|
||||
HttpHandler,
|
||||
HttpHeaders,
|
||||
HttpInterceptor,
|
||||
HttpRequest,
|
||||
HttpResponseBase
|
||||
} from '@angular/common/http';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
|
||||
import { ALAIN_I18N_TOKEN, _HttpClient } from '@delon/theme';
|
||||
import { environment } from '@env/environment';
|
||||
import { NzNotificationService } from 'ng-zorro-antd/notification';
|
||||
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
|
||||
import { catchError, filter, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||
|
||||
const CODEMESSAGE: { [key: number]: string } = {
|
||||
200: '服务器成功返回请求的数据。',
|
||||
201: '新建或修改数据成功。',
|
||||
202: '一个请求已经进入后台排队(异步任务)。',
|
||||
204: '删除数据成功。',
|
||||
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
|
||||
401: '用户没有权限(令牌、用户名、密码错误)。',
|
||||
403: '用户得到授权,但是访问是被禁止的。',
|
||||
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
|
||||
406: '请求的格式不可得。',
|
||||
410: '请求的资源被永久删除,且不会再得到的。',
|
||||
422: '当创建一个对象时,发生一个验证错误。',
|
||||
500: '服务器发生错误,请检查服务器。',
|
||||
502: '网关错误。',
|
||||
503: '服务不可用,服务器暂时过载或维护。',
|
||||
504: '网关超时。'
|
||||
};
|
||||
|
||||
/**
|
||||
* 默认HTTP拦截器,其注册细节见 `app.module.ts`
|
||||
*/
|
||||
@Injectable()
|
||||
export class DefaultInterceptor implements HttpInterceptor {
|
||||
private refreshTokenEnabled = environment.api.refreshTokenEnabled;
|
||||
private refreshTokenType: 're-request' | 'auth-refresh' = environment.api.refreshTokenType;
|
||||
private refreshToking = false;
|
||||
private refreshToken$: BehaviorSubject<any> = new BehaviorSubject<any>(null);
|
||||
|
||||
constructor(private injector: Injector) {
|
||||
if (this.refreshTokenType === 'auth-refresh') {
|
||||
this.buildAuthRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private get notification(): NzNotificationService {
|
||||
return this.injector.get(NzNotificationService);
|
||||
}
|
||||
|
||||
private get tokenSrv(): ITokenService {
|
||||
return this.injector.get(DA_SERVICE_TOKEN);
|
||||
}
|
||||
|
||||
private get http(): _HttpClient {
|
||||
return this.injector.get(_HttpClient);
|
||||
}
|
||||
|
||||
private goTo(url: string): void {
|
||||
setTimeout(() => this.injector.get(Router).navigateByUrl(url));
|
||||
}
|
||||
|
||||
private checkStatus(ev: HttpResponseBase): void {
|
||||
if ((ev.status >= 200 && ev.status < 300) || ev.status === 401) {
|
||||
return;
|
||||
}
|
||||
|
||||
const errortext = CODEMESSAGE[ev.status] || ev.statusText;
|
||||
this.notification.error(`请求错误 ${ev.status}: ${ev.url}`, errortext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新 Token 请求
|
||||
*/
|
||||
private refreshTokenRequest(): Observable<any> {
|
||||
const model = this.tokenSrv.get();
|
||||
return this.http.post(`/api/auth/refresh`, null, null, { headers: { refresh_token: model?.refresh_token || '' } });
|
||||
}
|
||||
|
||||
// #region 刷新Token方式一:使用 401 重新刷新 Token
|
||||
|
||||
private tryRefreshToken(ev: HttpResponseBase, req: HttpRequest<any>, next: HttpHandler): Observable<any> {
|
||||
// 1、若请求为刷新Token请求,表示来自刷新Token可以直接跳转登录页
|
||||
if ([`/api/auth/refresh`].some(url => req.url.includes(url))) {
|
||||
this.toLogin();
|
||||
return throwError(ev);
|
||||
}
|
||||
// 2、如果 `refreshToking` 为 `true` 表示已经在请求刷新 Token 中,后续所有请求转入等待状态,直至结果返回后再重新发起请求
|
||||
if (this.refreshToking) {
|
||||
return this.refreshToken$.pipe(
|
||||
filter(v => !!v),
|
||||
take(1),
|
||||
switchMap(() => next.handle(this.reAttachToken(req)))
|
||||
);
|
||||
}
|
||||
// 3、尝试调用刷新 Token
|
||||
this.refreshToking = true;
|
||||
this.refreshToken$.next(null);
|
||||
|
||||
return this.refreshTokenRequest().pipe(
|
||||
switchMap(res => {
|
||||
// 通知后续请求继续执行
|
||||
this.refreshToking = false;
|
||||
this.refreshToken$.next(res);
|
||||
// 重新保存新 token
|
||||
this.tokenSrv.set(res);
|
||||
// 重新发起请求
|
||||
return next.handle(this.reAttachToken(req));
|
||||
}),
|
||||
catchError(err => {
|
||||
this.refreshToking = false;
|
||||
this.toLogin();
|
||||
return throwError(err);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新附加新 Token 信息
|
||||
*
|
||||
* > 由于已经发起的请求,不会再走一遍 `@delon/auth` 因此需要结合业务情况重新附加新的 Token
|
||||
*/
|
||||
private reAttachToken(req: HttpRequest<any>): HttpRequest<any> {
|
||||
// 以下示例是以 NG-ALAIN 默认使用 `SimpleInterceptor`
|
||||
const token = this.tokenSrv.get()?.token;
|
||||
return req.clone({
|
||||
setHeaders: {
|
||||
token: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region 刷新Token方式二:使用 `@delon/auth` 的 `refresh` 接口
|
||||
|
||||
private buildAuthRefresh(): void {
|
||||
if (!this.refreshTokenEnabled) {
|
||||
return;
|
||||
}
|
||||
this.tokenSrv.refresh
|
||||
.pipe(
|
||||
filter(() => !this.refreshToking),
|
||||
switchMap(res => {
|
||||
console.log(res);
|
||||
this.refreshToking = true;
|
||||
return this.refreshTokenRequest();
|
||||
})
|
||||
)
|
||||
.subscribe(
|
||||
res => {
|
||||
// TODO: Mock expired value
|
||||
res.expired = +new Date() + 1000 * 60 * 5;
|
||||
this.refreshToking = false;
|
||||
this.tokenSrv.set(res);
|
||||
},
|
||||
() => this.toLogin()
|
||||
);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
private toLogin(): void {
|
||||
this.notification.error(`未登录或登录已过期,请重新登录。`, ``);
|
||||
this.goTo(this.tokenSrv.login_url!);
|
||||
}
|
||||
|
||||
private handleData(ev: HttpResponseBase, req: HttpRequest<any>, next: HttpHandler): Observable<any> {
|
||||
this.checkStatus(ev);
|
||||
// 业务处理:一些通用操作
|
||||
switch (ev.status) {
|
||||
case 200:
|
||||
// 业务层级错误处理,以下是假定restful有一套统一输出格式(指不管成功与否都有相应的数据格式)情况下进行处理
|
||||
// 例如响应内容:
|
||||
// 错误内容:{ status: 1, msg: '非法参数' }
|
||||
// 正确内容:{ status: 0, response: { } }
|
||||
// 则以下代码片断可直接适用
|
||||
// if (ev instanceof HttpResponse) {
|
||||
// const body = ev.body;
|
||||
// if (body && body.status !== 0) {
|
||||
// this.injector.get(NzMessageService).error(body.msg);
|
||||
// // 注意:这里如果继续抛出错误会被行254的 catchError 二次拦截,导致外部实现的 Pipe、subscribe 操作被中断,例如:this.http.get('/').subscribe() 不会触发
|
||||
// // 如果你希望外部实现,需要手动移除行254
|
||||
// return throwError({});
|
||||
// } else {
|
||||
// // 忽略 Blob 文件体
|
||||
// if (ev.body instanceof Blob) {
|
||||
// return of(ev);
|
||||
// }
|
||||
// // 重新修改 `body` 内容为 `response` 内容,对于绝大多数场景已经无须再关心业务状态码
|
||||
// return of(new HttpResponse(Object.assign(ev, { body: body.response })));
|
||||
// // 或者依然保持完整的格式
|
||||
// return of(ev);
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
case 401:
|
||||
if (this.refreshTokenEnabled && this.refreshTokenType === 're-request') {
|
||||
return this.tryRefreshToken(ev, req, next);
|
||||
}
|
||||
this.toLogin();
|
||||
break;
|
||||
case 403:
|
||||
case 404:
|
||||
case 500:
|
||||
// this.goTo(`/exception/${ev.status}?url=${req.urlWithParams}`);
|
||||
break;
|
||||
default:
|
||||
if (ev instanceof HttpErrorResponse) {
|
||||
console.warn(
|
||||
'未可知错误,大部分是由于后端不支持跨域CORS或无效配置引起,请参考 https://ng-alain.com/docs/server 解决跨域问题',
|
||||
ev
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ev instanceof HttpErrorResponse) {
|
||||
return throwError(ev);
|
||||
} else {
|
||||
return of(ev);
|
||||
}
|
||||
}
|
||||
|
||||
private getAdditionalHeaders(headers?: HttpHeaders): { [name: string]: string } {
|
||||
const res: { [name: string]: string } = {};
|
||||
const lang = this.injector.get(ALAIN_I18N_TOKEN).currentLang;
|
||||
if (!headers?.has('Accept-Language') && lang) {
|
||||
res['Accept-Language'] = lang;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// 统一加上服务端前缀
|
||||
let url = req.url;
|
||||
if (!url.startsWith('https://') && !url.startsWith('http://')) {
|
||||
const { baseUrl } = environment.api;
|
||||
url = baseUrl + (baseUrl.endsWith('/') && url.startsWith('/') ? url.substring(1) : url);
|
||||
}
|
||||
|
||||
const newReq = req.clone({ url, setHeaders: this.getAdditionalHeaders(req.headers) });
|
||||
return next.handle(newReq).pipe(
|
||||
mergeMap(ev => {
|
||||
// 允许统一对请求错误处理
|
||||
if (ev instanceof HttpResponseBase) {
|
||||
return this.handleData(ev, newReq, next);
|
||||
}
|
||||
// 若一切都正常,则后续操作
|
||||
return of(ev);
|
||||
}),
|
||||
catchError((err: HttpErrorResponse) => this.handleData(err, newReq, next))
|
||||
);
|
||||
}
|
||||
}
|
||||
127
web-app/src/app/core/startup/startup.service.ts
Normal file
127
web-app/src/app/core/startup/startup.service.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
|
||||
import { ALAIN_I18N_TOKEN, MenuService, SettingsService, TitleService } from '@delon/theme';
|
||||
import { ACLService } from '@delon/acl';
|
||||
import { I18NService } from '../i18n/i18n.service';
|
||||
import { Observable, zip, of } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import type { NzSafeAny } from 'ng-zorro-antd/core/types';
|
||||
import { NzIconService } from 'ng-zorro-antd/icon';
|
||||
|
||||
import { ICONS } from '../../../style-icons';
|
||||
import { ICONS_AUTO } from '../../../style-icons-auto';
|
||||
|
||||
/**
|
||||
* Used for application startup
|
||||
* Generally used to get the basic data of the application, like: Menu Data, User Data, etc.
|
||||
*/
|
||||
@Injectable()
|
||||
export class StartupService {
|
||||
constructor(
|
||||
iconSrv: NzIconService,
|
||||
private menuService: MenuService,
|
||||
@Inject(ALAIN_I18N_TOKEN) private i18n: I18NService,
|
||||
private settingService: SettingsService,
|
||||
private aclService: ACLService,
|
||||
private titleService: TitleService,
|
||||
@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
|
||||
private httpClient: HttpClient,
|
||||
private router: Router
|
||||
) {
|
||||
iconSrv.addIcon(...ICONS_AUTO, ...ICONS);
|
||||
}
|
||||
|
||||
|
||||
private viaHttp(): Observable<void> {
|
||||
const defaultLang = this.i18n.defaultLang;
|
||||
return zip(this.i18n.loadLangData(defaultLang), this.httpClient.get('assets/tmp/app-data.json')).pipe(
|
||||
catchError((res: NzSafeAny) => {
|
||||
console.warn(`StartupService.load: Network request failed`, res);
|
||||
setTimeout(() => this.router.navigateByUrl(`/exception/500`));
|
||||
return [];
|
||||
}),
|
||||
map(([langData, appData]: [Record<string, string>, NzSafeAny]) => {
|
||||
// setting language data
|
||||
this.i18n.use(defaultLang, langData);
|
||||
|
||||
// Application data
|
||||
// Application information: including site name, description, year
|
||||
this.settingService.setApp(appData.app);
|
||||
// User information: including name, avatar, email address
|
||||
this.settingService.setUser(appData.user);
|
||||
// ACL: Set the permissions to full, https://ng-alain.com/acl/getting-started
|
||||
this.aclService.setFull(true);
|
||||
// Menu data, https://ng-alain.com/theme/menu
|
||||
this.menuService.add(appData.menu);
|
||||
// Can be set page suffix title, https://ng-alain.com/theme/title
|
||||
this.titleService.suffix = appData.app.name;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private viaMockI18n(): Observable<void> {
|
||||
const defaultLang = this.i18n.defaultLang;
|
||||
return this.i18n.loadLangData(defaultLang).pipe(
|
||||
map((langData: NzSafeAny) => {
|
||||
this.i18n.use(defaultLang, langData);
|
||||
|
||||
this.viaMock();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private viaMock(): Observable<void> {
|
||||
// const tokenData = this.tokenService.get();
|
||||
// if (!tokenData.token) {
|
||||
// this.router.navigateByUrl(this.tokenService.login_url!);
|
||||
// return;
|
||||
// }
|
||||
// mock
|
||||
const app: any = {
|
||||
name: `HertzBeat`,
|
||||
description: `面向开发者,易用友好的高性能监控云服务`
|
||||
};
|
||||
const user: any = {
|
||||
name: 'Admin',
|
||||
avatar: './assets/tmp/img/avatar.svg',
|
||||
email: 'tomsun28@outlook.com',
|
||||
token: '123456789'
|
||||
};
|
||||
// Application information: including site name, description, year
|
||||
this.settingService.setApp(app);
|
||||
// User information: including name, avatar, email address
|
||||
this.settingService.setUser(user);
|
||||
// ACL: Set the permissions to full, https://ng-alain.com/acl/getting-started
|
||||
this.aclService.setFull(true);
|
||||
// Menu data, https://ng-alain.com/theme/menu
|
||||
this.menuService.add([
|
||||
{
|
||||
text: 'Main',
|
||||
group: true,
|
||||
children: [
|
||||
{
|
||||
text: 'Dashboard',
|
||||
link: '/dashboard',
|
||||
icon: { type: 'icon', value: 'appstore' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
// Can be set page suffix title, https://ng-alain.com/theme/title
|
||||
this.titleService.suffix = app.name;
|
||||
|
||||
return of();
|
||||
}
|
||||
|
||||
load(): Observable<void> {
|
||||
// http
|
||||
// return this.viaHttp();
|
||||
// mock: Don’t use it in a production environment. ViaMock is just to simulate some data to make the scaffolding work normally
|
||||
// mock:请勿在生产环境中这么使用,viaMock 单纯只是为了模拟一些数据使脚手架一开始能正常运行
|
||||
return this.viaMockI18n();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user