/** * 全局错误处理工具 * 用于统一处理应用中的各种错误 */ import { ElMessage, ElNotification } from 'element-plus' import router from '@/router' // 错误类型枚举 export const ErrorTypes = { NETWORK: 'network', API: 'api', VALIDATION: 'validation', PERMISSION: 'permission', BUSINESS: 'business', UNKNOWN: 'unknown' } // 错误级别枚举 export const ErrorLevels = { INFO: 'info', WARNING: 'warning', ERROR: 'error', CRITICAL: 'critical' } // 错误处理配置 const errorHandlingConfig = { // 是否显示错误通知 showNotification: true, // 是否记录错误日志 logError: true, // 是否上报错误 reportError: true, // 错误上报URL reportUrl: '/api/errors/report', // 最大重试次数 maxRetries: 3, // 重试延迟(毫秒) retryDelay: 1000 } /** * 错误处理器类 */ class ErrorHandler { constructor() { this.errorQueue = [] this.retryCount = new Map() this.initGlobalErrorHandlers() } /** * 初始化全局错误处理器 */ initGlobalErrorHandlers() { // 监听未捕获的Promise错误 window.addEventListener('unhandledrejection', (event) => { this.handleError(event.reason, { type: ErrorTypes.UNKNOWN, level: ErrorLevels.ERROR, context: 'unhandledrejection', promise: event.promise }) }) // 监听全局JavaScript错误 window.addEventListener('error', (event) => { this.handleError(event.error || new Error(event.message), { type: ErrorTypes.UNKNOWN, level: ErrorLevels.ERROR, context: 'javascript', filename: event.filename, lineno: event.lineno, colno: event.colno }) }) } /** * 处理错误 * @param {Error} error - 错误对象 * @param {Object} options - 错误处理选项 */ handleError(error, options = {}) { const { type = ErrorTypes.UNKNOWN, level = ErrorLevels.ERROR, context = '', showMessage = true, customMessage = '', retryCallback = null, ...otherOptions } = options // 构建错误信息 const errorInfo = { message: error?.message || '未知错误', stack: error?.stack || '', type, level, context, url: window.location.href, userAgent: navigator.userAgent, timestamp: new Date().toISOString(), ...otherOptions } // 记录错误 if (errorHandlingConfig.logError) { console.error(`[ErrorHandler] ${level.toUpperCase()}:`, error, errorInfo) } // 上报错误 if (errorHandlingConfig.reportError) { this.reportError(errorInfo) } // 显示错误消息 if (showMessage) { this.showErrorMessage(errorInfo, customMessage) } // 执行重试回调 if (retryCallback && typeof retryCallback === 'function') { this.executeRetry(retryCallback, errorInfo) } // 将错误添加到队列 this.errorQueue.push(errorInfo) // 限制错误队列大小 if (this.errorQueue.length > 100) { this.errorQueue.shift() } return errorInfo } /** * 处理网络错误 * @param {Error} error - 错误对象 * @param {Object} options - 错误处理选项 */ handleNetworkError(error, options = {}) { return this.handleError(error, { type: ErrorTypes.NETWORK, level: ErrorLevels.WARNING, context: 'network', customMessage: '网络连接失败,请检查网络设置', ...options }) } /** * 处理API错误 * @param {Object} response - API响应对象 * @param {Object} options - 错误处理选项 */ handleApiError(response, options = {}) { const { status, data } = response let message = '服务器错误' let level = ErrorLevels.ERROR // 根据状态码设置错误消息和级别 switch (status) { case 400: message = data?.message || '请求参数错误' level = ErrorLevels.WARNING break case 401: message = '未授权,请重新登录' level = ErrorLevels.WARNING // 跳转到登录页 router.push('/login') break case 403: message = '没有权限访问该资源' level = ErrorLevels.WARNING break case 404: message = '请求的资源不存在' level = ErrorLevels.WARNING break case 500: message = '服务器内部错误' level = ErrorLevels.ERROR break case 502: case 503: case 504: message = '服务暂时不可用,请稍后重试' level = ErrorLevels.ERROR break default: message = data?.message || `服务器错误 (${status})` level = ErrorLevels.ERROR } return this.handleError(new Error(message), { type: ErrorTypes.API, level, context: 'api', status, responseData: data, ...options }) } /** * 处理验证错误 * @param {Object} errors - 验证错误对象 * @param {Object} options - 错误处理选项 */ handleValidationErrors(errors, options = {}) { let message = '输入验证失败' // 如果是数组,取第一个错误 if (Array.isArray(errors) && errors.length > 0) { message = errors[0].message || message } // 如果是对象,取第一个错误 else if (typeof errors === 'object' && errors !== null) { const firstKey = Object.keys(errors)[0] if (firstKey && errors[firstKey]) { message = Array.isArray(errors[firstKey]) ? errors[firstKey][0] : errors[firstKey].message || message } } return this.handleError(new Error(message), { type: ErrorTypes.VALIDATION, level: ErrorLevels.WARNING, context: 'validation', validationErrors: errors, ...options }) } /** * 显示错误消息 * @param {Object} errorInfo - 错误信息 * @param {string} customMessage - 自定义消息 */ showErrorMessage(errorInfo, customMessage = '') { const message = customMessage || errorInfo.message const { level } = errorInfo if (!errorHandlingConfig.showNotification) { return } // 根据错误级别选择不同的显示方式 switch (level) { case ErrorLevels.INFO: ElMessage.info(message) break case ErrorLevels.WARNING: ElMessage.warning(message) break case ErrorLevels.ERROR: ElMessage.error(message) break case ErrorLevels.CRITICAL: ElNotification({ title: '严重错误', message, type: 'error', duration: 0, // 不自动关闭 showClose: true }) break default: ElMessage.error(message) } } /** * 上报错误 * @param {Object} errorInfo - 错误信息 */ reportError(errorInfo) { try { // 使用navigator.sendBeacon进行非阻塞上报 if (navigator.sendBeacon) { const data = new Blob([JSON.stringify(errorInfo)], { type: 'application/json' }) navigator.sendBeacon(errorHandlingConfig.reportUrl, data) } else { // 降级使用fetch fetch(errorHandlingConfig.reportUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(errorInfo), keepalive: true // 尝试保持连接 }).catch(err => { console.error('[ErrorHandler] 上报错误失败:', err) }) } } catch (err) { console.error('[ErrorHandler] 上报错误异常:', err) } } /** * 执行重试 * @param {Function} callback - 重试回调 * @param {Object} errorInfo - 错误信息 */ executeRetry(callback, errorInfo) { const errorKey = `${errorInfo.type}_${errorInfo.context}` const currentRetryCount = this.retryCount.get(errorKey) || 0 if (currentRetryCount < errorHandlingConfig.maxRetries) { this.retryCount.set(errorKey, currentRetryCount + 1) ElMessage.info(`正在重试 (${currentRetryCount + 1}/${errorHandlingConfig.maxRetries})...`) setTimeout(() => { try { callback() } catch (err) { this.handleError(err, { type: errorInfo.type, level: errorInfo.level, context: `${errorInfo.context}_retry`, showMessage: false }) } }, errorHandlingConfig.retryDelay) } else { ElMessage.error('已达到最大重试次数,请稍后再试') this.retryCount.delete(errorKey) } } /** * 获取错误队列 */ getErrorQueue() { return [...this.errorQueue] } /** * 清空错误队列 */ clearErrorQueue() { this.errorQueue = [] } /** * 配置错误处理 * @param {Object} config - 配置选项 */ configure(config) { Object.assign(errorHandlingConfig, config) } } // 创建全局错误处理器实例 const globalErrorHandler = new ErrorHandler() export default globalErrorHandler // 导出便捷方法 export const handleError = (error, options) => globalErrorHandler.handleError(error, options) export const handleNetworkError = (error, options) => globalErrorHandler.handleNetworkError(error, options) export const handleApiError = (response, options) => globalErrorHandler.handleApiError(response, options) export const handleValidationErrors = (errors, options) => globalErrorHandler.handleValidationErrors(errors, options) export const configureErrorHandler = (config) => globalErrorHandler.configure(config)