import Taro from '@tarojs/taro'; // 定义本地的RequestError类,避免循环依赖 export class RequestError extends Error { public type: string; public code?: number; public data?: any; constructor(message: string, type: string, code?: number, data?: any) { super(message); this.name = 'RequestError'; this.type = type; this.code = code; this.data = data; } } // 错误类型枚举 export enum ErrorType { NETWORK_ERROR = 'NETWORK_ERROR', TIMEOUT_ERROR = 'TIMEOUT_ERROR', BUSINESS_ERROR = 'BUSINESS_ERROR', AUTH_ERROR = 'AUTH_ERROR', UNKNOWN_ERROR = 'UNKNOWN_ERROR' } // 错误级别枚举 export enum ErrorLevel { INFO = 'info', WARNING = 'warning', ERROR = 'error', FATAL = 'fatal' } // 错误信息接口 export interface ErrorInfo { message: string; level: ErrorLevel; type: string; stack?: string; extra?: any; timestamp: number; userId?: string; page?: string; } /** * 全局错误处理器 */ class GlobalErrorHandler { private static instance: GlobalErrorHandler; private errorQueue: ErrorInfo[] = []; private maxQueueSize = 50; private constructor() { this.setupGlobalErrorHandlers(); } public static getInstance(): GlobalErrorHandler { if (!GlobalErrorHandler.instance) { GlobalErrorHandler.instance = new GlobalErrorHandler(); } return GlobalErrorHandler.instance; } /** * 设置全局错误处理器 */ private setupGlobalErrorHandlers() { // 捕获未处理的Promise rejection if (typeof window !== 'undefined') { window.addEventListener('unhandledrejection', (event) => { this.handleError(event.reason, ErrorLevel.ERROR, 'UnhandledPromiseRejection'); event.preventDefault(); }); } } /** * 处理错误 */ public handleError( error: any, level: ErrorLevel = ErrorLevel.ERROR, type: string = 'Unknown', extra?: any ) { const errorInfo = this.createErrorInfo(error, level, type, extra); // 添加到错误队列 this.addToQueue(errorInfo); // 根据错误级别决定处理方式 switch (level) { case ErrorLevel.FATAL: this.handleFatalError(errorInfo); break; case ErrorLevel.ERROR: this.handleNormalError(errorInfo); break; case ErrorLevel.WARNING: this.handleWarning(errorInfo); break; case ErrorLevel.INFO: this.handleInfo(errorInfo); break; } // 上报错误 this.reportError(errorInfo); } /** * 创建错误信息对象 */ private createErrorInfo( error: any, level: ErrorLevel, type: string, extra?: any ): ErrorInfo { let message = '未知错误'; let stack: string | undefined; if (error instanceof Error) { message = error.message; stack = error.stack; } else if (error instanceof RequestError) { message = error.message; type = error.type; extra = { ...extra, code: error.code, data: error.data }; } else if (typeof error === 'string') { message = error; } else if (error && typeof error === 'object') { message = error.message || error.errMsg || JSON.stringify(error); } return { message, level, type, stack, extra, timestamp: Date.now(), userId: Taro.getStorageSync('UserId') || undefined, page: this.getCurrentPage() }; } /** * 获取当前页面路径 */ private getCurrentPage(): string { try { const pages = Taro.getCurrentPages(); const currentPage = pages[pages.length - 1]; return currentPage?.route || 'unknown'; } catch { return 'unknown'; } } /** * 添加到错误队列 */ private addToQueue(errorInfo: ErrorInfo) { this.errorQueue.push(errorInfo); // 保持队列大小 if (this.errorQueue.length > this.maxQueueSize) { this.errorQueue.shift(); } } /** * 处理致命错误 */ private handleFatalError(errorInfo: ErrorInfo) { console.error('Fatal Error:', errorInfo); Taro.showModal({ title: '严重错误', content: '应用遇到严重错误,需要重启', showCancel: false, confirmText: '重启应用', success: () => { Taro.reLaunch({ url: '/pages/index/index' }); } }); } /** * 处理普通错误 */ private handleNormalError(errorInfo: ErrorInfo) { console.error('Error:', errorInfo); // 根据错误类型显示不同的提示 let title = '操作失败'; if (errorInfo.type === ErrorType.NETWORK_ERROR) { title = '网络连接失败'; } else if (errorInfo.type === ErrorType.TIMEOUT_ERROR) { title = '请求超时'; } else if (errorInfo.type === ErrorType.AUTH_ERROR) { title = '认证失败'; } Taro.showToast({ title: errorInfo.message || title, icon: 'error', duration: 2000 }); } /** * 处理警告 */ private handleWarning(errorInfo: ErrorInfo) { console.warn('Warning:', errorInfo); // 警告通常不需要用户交互,只记录日志 } /** * 处理信息 */ private handleInfo(errorInfo: ErrorInfo) { console.info('Info:', errorInfo); } /** * 上报错误到服务器 */ private reportError(errorInfo: ErrorInfo) { try { // 这里可以实现错误上报逻辑 // 例如发送到后端日志系统、第三方监控服务等 // 示例:发送到后端 // request.post('/api/error/report', errorInfo).catch(() => { // // 上报失败也不要影响用户体验 // }); // 开发环境下打印详细信息 if (process.env.NODE_ENV === 'development') { console.group('🚨 Error Report'); console.log('Message:', errorInfo.message); console.log('Level:', errorInfo.level); console.log('Type:', errorInfo.type); console.log('Page:', errorInfo.page); console.log('UserId:', errorInfo.userId); console.log('Timestamp:', new Date(errorInfo.timestamp).toLocaleString()); if (errorInfo.stack) { console.log('Stack:', errorInfo.stack); } if (errorInfo.extra) { console.log('Extra:', errorInfo.extra); } console.groupEnd(); } } catch (reportError) { console.error('Failed to report error:', reportError); } } /** * 获取错误队列 */ public getErrorQueue(): ErrorInfo[] { return [...this.errorQueue]; } /** * 清空错误队列 */ public clearErrorQueue() { this.errorQueue = []; } } // 导出单例实例 export const errorHandler = GlobalErrorHandler.getInstance(); // 便捷方法 export const handleError = (error: any, level?: ErrorLevel, type?: string, extra?: any) => { errorHandler.handleError(error, level, type, extra); }; export const handleFatalError = (error: any, extra?: any) => { errorHandler.handleError(error, ErrorLevel.FATAL, 'FatalError', extra); }; export const handleWarning = (error: any, extra?: any) => { errorHandler.handleError(error, ErrorLevel.WARNING, 'Warning', extra); }; export const handleInfo = (message: string, extra?: any) => { errorHandler.handleError(message, ErrorLevel.INFO, 'Info', extra); }; export default errorHandler;