/**
* @file 导航服务
* @description 支持路由守卫的导航服务
* @author JunBin.Yang
*/
import { Logger } from '@core/util';
import { Unknown } from "@core/common";
import { RouteGuard, RouteContext, GuardResult } from './RouteGuard';
import { GuardManager } from './GuardManager';
const TAG: string = 'NavigationService';
/**
* Provider / Consumer 共享的键名
*/
export const NAV_PATH_STACK_KEY: string = "navStack";
/**
* 导航选项
*/
export interface NavigateOptions {
/**
* 是否跳过守卫检查
*/
skipGuards?: boolean;
/**
* 是否需要动画
*/
animated?: boolean;
}
/**
* 导航服务
* 在原 NavigationService 基础上增加路由守卫支持
*/
export class NavigationService {
/**
* 导航栈
*/
private stack: NavPathStack = new NavPathStack();
/**
* 守卫管理器
*/
private guardManager: GuardManager;
/**
* 当前路由名称
*/
private currentRoute: string = '';
/**
* 是否启用日志
*/
private enableLog: boolean = true;
/**
* 构造函数
* @param navStack 导航栈实例
* @param enableLog 是否启用日志
*/
constructor(navStack: NavPathStack, enableLog: boolean = true) {
this.stack = navStack;
this.enableLog = enableLog;
this.guardManager = new GuardManager(enableLog);
}
/**
* 获取导航栈
* @returns 导航栈实例
*/
getPathStack(): NavPathStack | null {
return this.stack;
}
/**
* 注册路由守卫
* @param guard 守卫实例
*/
registerGuard(guard: RouteGuard): void {
this.guardManager.register(guard);
}
/**
* 注销路由守卫
* @param guard 守卫实例
* @returns 是否注销成功
*/
unregisterGuard(guard: RouteGuard): boolean {
return this.guardManager.unregister(guard);
}
/**
* 通过名称注销守卫
* @param name 守卫名称
* @returns 是否注销成功
*/
unregisterGuardByName(name: string): boolean {
return this.guardManager.unregisterByName(name);
}
/**
* 路由跳转(入栈)
* @param name 路由名称
* @param params 跳转参数
* @param options 导航选项
* @returns 是否导航成功
*/
async navigateTo(name: string, params?: Unknown, options?: NavigateOptions): Promise<boolean> {
if (!this.stack) {
this.logError('Navigation stack is not initialized');
return false;
}
// 构建路由上下文
const context: RouteContext = {
targetRoute: name,
params,
fromRoute: this.currentRoute
};
// 执行守卫检查(除非跳过)
if (!options?.skipGuards) {
const guardResult: GuardResult = await this.guardManager.runGuards(context);
if (!guardResult.canActivate) {
// 如果有重定向路由,执行重定向
if (guardResult.redirectTo) {
this.log(`Redirecting to: ${guardResult.redirectTo}`);
return this.navigateTo(guardResult.redirectTo, guardResult.redirectParams, { skipGuards: true });
}
return false;
}
}
// 执行导航
try {
if (params !== undefined) {
this.stack.pushPath({
name: name,
param: params
});
} else {
this.stack.pushPath({
name: name
});
}
this.currentRoute = name;
this.log(`Navigated to: ${name}`);
return true;
} catch (err) {
this.logError(`Navigation failed: ${JSON.stringify(err)}`);
return false;
}
}
/**
* 返回上一页
* @param animated 是否需要动画
*/
navigateBack(animated?: boolean): void {
if (!this.stack) {
return;
}
try {
this.stack.pop(undefined, animated);
this.log('Navigated back');
} catch (err) {
this.logError(`Navigate back failed: ${JSON.stringify(err)}`);
}
}
/**
* 携带结果返回上一页
* @param result 返回结果
* @param animated 是否需要动画
*/
navigateBackWithResult(result: Object, animated?: boolean): void {
if (!this.stack) {
return;
}
try {
this.stack.pop(result, animated);
this.log('Navigated back with result');
} catch (err) {
this.logError(`Navigate back with result failed: ${JSON.stringify(err)}`);
}
}
/**
* 返回到指定路由
* @param name 目标路由名称
* @param inclusive 是否移除目标自身
*/
popTo(name: string, inclusive: boolean = false): void {
if (!this.stack) {
return;
}
try {
this.stack.popToName(name, inclusive);
this.log(`Popped to: ${name}`);
} catch (err) {
this.logError(`Pop to failed: ${JSON.stringify(err)}`);
}
}
/**
* 跳转并监听返回结果
* @param name 路由名称
* @param params 跳转参数
* @param onPop 返回结果回调
* @param options 导航选项
* @returns 是否导航成功
*/
async navigateToWithResult(
name: string,
params?: Unknown,
onPop?: (info: PopInfo) => void,
options?: NavigateOptions
): Promise<boolean> {
if (!this.stack) {
return false;
}
// 构建路由上下文
const context: RouteContext = {
targetRoute: name,
params,
fromRoute: this.currentRoute
};
// 执行守卫检查
if (!options?.skipGuards) {
const guardResult = await this.guardManager.runGuards(context);
if (!guardResult.canActivate) {
if (guardResult.redirectTo) {
return this.navigateToWithResult(guardResult.redirectTo, guardResult.redirectParams, onPop, { skipGuards: true });
}
return false;
}
}
try {
const safeParam: Object | undefined = params as Object | undefined;
if (onPop) {
await this.stack.pushDestinationByName(name, safeParam, onPop);
} else {
await this.stack.pushDestinationByName(name, safeParam);
}
this.currentRoute = name;
return true;
} catch (err) {
this.logError(`Navigate to with result failed: ${JSON.stringify(err)}`);
return false;
}
}
/**
* 跳转并以 Promise 形式获取返回结果
* @param name 路由名称
* @param params 跳转参数
* @param options 导航选项
* @returns 返回结果
*/
async navigateToForResult<T>(name: string, params?: object, options?: NavigateOptions): Promise<T | undefined> {
return new Promise<T | undefined>((resolve) => {
this.navigateToWithResult(name, params, (info: PopInfo) => {
resolve(info?.result as T);
}, options);
});
}
/**
* 获取指定路由的参数
* @param name 路由名称
* @returns 参数对象
*/
getRouteParams<T>(name: string): T | undefined {
if (!this.stack) {
return undefined;
}
try {
return this.stack.getParamByName(name)[0] as T;
} catch (err) {
this.logError(`Get route params failed: ${JSON.stringify(err)}`);
return undefined;
}
}
/**
* 获取导航栈实例
* @returns 导航栈
*/
getStack(): NavPathStack {
return this.stack;
}
/**
* 获取守卫管理器
* @returns 守卫管理器
*/
getGuardManager(): GuardManager {
return this.guardManager;
}
/**
* 获取当前路由
* @returns 当前路由名称
*/
getCurrentRoute(): string {
return this.currentRoute;
}
/**
* 设置是否启用日志
* @param enable 是否启用
*/
setLogEnabled(enable: boolean): void {
this.enableLog = enable;
this.guardManager.setLogEnabled(enable);
}
/**
* 记录日志
*/
private log(message: string): void {
if (this.enableLog) {
Logger.info(message, TAG);
}
}
/**
* 记录错误日志
*/
private logError(message: string): void {
Logger.error(message, TAG);
}
}