@@ -1,5 +1,113 @@
const baseUrl = import . meta . env . VITE _BASE _URL
const assetsUrl = import . meta . env . VITE _ASSETSURL
// 环境变量处理
const getEnvVar = ( key , defaultValue = '' ) => {
if ( typeof import !== 'undefined' && import . meta && import . meta . env ) {
return import . meta . env [ key ] || defaultValue
}
if ( typeof process !== 'undefined' && process . env ) {
return process . env [ key ] || defaultValue
}
return defaultValue
}
const baseUrl = getEnvVar ( 'VITE_BASE_URL' , 'https://api.example.com' )
const assetsUrl = getEnvVar ( 'VITE_ASSETSURL' , '/assets/' )
// 错误监控实例
let errorMonitor = null
/**
* 初始化错误监控实例
*/
function initErrorMonitorInstance ( ) {
if ( errorMonitor ) return errorMonitor
try {
// 在 Node.js 环境下不尝试导入 uniapp-error-monitor
if ( typeof window === 'undefined' && typeof uni === 'undefined' ) {
throw new Error ( '非浏览器/UniApp 环境,跳过 uniapp-error-monitor 导入' )
}
// 尝试导入 uniapp-error-monitor
const ErrorMonitorModule = require ( 'uniapp-error-monitor' )
if ( ErrorMonitorModule . default ) {
errorMonitor = new ErrorMonitorModule . default ( )
} else if ( ErrorMonitorModule . ErrorMonitor ) {
errorMonitor = new ErrorMonitorModule . ErrorMonitor ( )
} else {
throw new Error ( '未找到可用的 ErrorMonitor 类' )
}
console . log ( '[Tool] 使用 uniapp-error-monitor' )
} catch ( error ) {
console . warn ( '[Tool] uniapp-error-monitor 导入失败,将使用本地错误监控实现:' , error . message )
// 本地错误监控实现
errorMonitor = {
errorStats : {
total : 0 ,
global : 0 ,
promise : 0 ,
console : 0 ,
miniProgram : 0 ,
api : 0 ,
network : 0 ,
manual : 0 ,
lastErrorTime : null ,
} ,
init ( config = { } ) {
console . log ( '[Tool] 使用本地错误监控实现' )
// 这里可以添加本地错误监控逻辑
} ,
reportError ( type = 'manual' , error , context = { } , forceSend = false ) {
this . errorStats . total ++
this . errorStats [ type ] = ( this . errorStats [ type ] || 0 ) + 1
this . errorStats . lastErrorTime = Date . now ( )
console . log ( ` [Tool] 错误上报: ${ type } ` , error )
} ,
wrapPromise ( promise ) {
return promise . catch ( error => {
this . reportError ( 'promise' , error )
throw error
} )
} ,
getErrorStats ( ) {
return { ... this . errorStats }
} ,
resetErrorStats ( ) {
this . errorStats = {
total : 0 ,
global : 0 ,
promise : 0 ,
console : 0 ,
miniProgram : 0 ,
api : 0 ,
network : 0 ,
manual : 0 ,
lastErrorTime : null ,
}
} ,
getEnvironmentInfo ( ) {
return {
isProduction : getEnvVar ( 'MODE' ) === 'production' ,
mode : getEnvVar ( 'MODE' , 'development' ) ,
platform : 'Unknown' ,
errorMonitorEnabled : true ,
timestamp : Date . now ( ) ,
}
}
}
}
return errorMonitor
}
// 初始化错误监控实例
const monitorInstance = initErrorMonitorInstance ( )
/**
* 工具类 - 提供常用的工具方法
@@ -12,32 +120,15 @@ class Tool {
NONE : 0 ,
SUCCESS : 1 ,
LOADING : 2 ,
ERROR : 3 ,
WARNING : 4 ,
}
// 字体加载状态缓存
this . loadedFonts = new Set ( )
// 初始化错误统计
this . errorStats = {
total : 0 ,
global : 0 ,
promise : 0 ,
console : 0 ,
miniProgram : 0 ,
lastErrorTime : null ,
}
// Promise包装方法
this . wrapPromise = null
// 项目信息
this . projectInfo = {
name : 'template' ,
version : '1.0.0' ,
}
// 尝试从 manifest.json 加载项目信息
this . _loadProjectInfo ( )
// 错误监控配置
this . config = null
}
/**
@@ -57,6 +148,8 @@ class Tool {
[ this . ICON _TYPES . NONE ] : 'none' ,
[ this . ICON _TYPES . SUCCESS ] : 'success' ,
[ this . ICON _TYPES . LOADING ] : 'loading' ,
[ this . ICON _TYPES . ERROR ] : 'error' ,
[ this . ICON _TYPES . WARNING ] : 'none' , // uni-app toast不支持warning图标
}
uni . showToast ( {
@@ -360,16 +453,58 @@ class Tool {
} )
} )
}
/**
* 处理支付流程的完整方法
* @param {Object} paymentData 支付参数
* @param {Function} successCallback 成功回调
* @param {Function} errorCallback 错误回调
* @returns {Promise<Object>} 支付结果
*/
async handlePayment ( paymentData , successCallback = null , errorCallback = null ) {
try {
// 调用微信支付
const res = await this . requestPayment ( paymentData )
this . alert ( '支付完成,正在验证结果...' , this . ICON _TYPES . LOADING )
// 如果有成功回调,调用它
if ( successCallback && typeof successCallback === 'function' ) {
await successCallback ( res )
}
return res
} catch ( error ) {
console . log ( '支付失败:' , error )
// 错误处理
if ( error . errMsg && error . errMsg . includes ( 'cancel' ) ) {
await this . alert ( '支付已取消' , this . ICON _TYPES . WARNING )
} else {
await this . alert ( '支付失败,请重试' , this . ICON _TYPES . ERROR )
}
// 如果有错误回调,调用它
if ( errorCallback && typeof errorCallback === 'function' ) {
await errorCallback ( error )
}
throw error
}
}
upload ( filePath ) {
return new Promise ( ( resolve , reject ) => {
uni . uploadFile ( {
url : ` ${ baseUrl } file /upload` ,
url : ` ${ baseUrl } /j1732/api/front /upload/image ` ,
fileType : 'image' ,
header : {
Authorization: `Bearer ${ this . storage ( 'token' ) } ` ,
' Authori- zation' : ` ${ this . storage ( 'token' ) } ` ,
} ,
filePath ,
name : 'file ' ,
name : 'multipart ' ,
formData : {
model : 'user' , // 用户模块
pid : 7 , // 前台用户分类ID
} ,
success : ( { data } ) => {
resolve ( JSON . parse ( data ) )
} ,
@@ -380,32 +515,6 @@ class Tool {
} )
}
/**
* 检测是否为生产环境
* @private
* @returns {boolean} 是否为生产环境
*/
_isProduction ( ) {
// 检查uniapp运行模式
try {
const systemInfo = uni . getSystemInfoSync ? . ( )
if ( systemInfo ? . mode && systemInfo . mode !== 'default' ) {
// 体验版、开发版、预览版
return false
}
} catch ( error ) {
// 忽略错误,继续检测
}
// 检查环境变量MODE
if ( import . meta . env . MODE === 'development' ) {
return false
}
// 默认:开发环境和体验版不启用,生产环境启用
return true
}
/**
* 初始化全局错误监控
* @param {Object} options 配置选项
@@ -429,146 +538,32 @@ class Tool {
... options ,
}
// 环境检查:只在生产环境下启用错误监控
if ( ! config . forceEnable && ! this . _isProduction ( ) ) {
console . info ( '当前为非生产环境,错误监控已禁用' )
return
}
// 检查webhook配置
if ( ! config . webhookUrl ) {
console . warn ( '错误监控初始化失败: 未配置webhook地址' )
return
}
this . config = config
// 全局错误捕获( uniapp环境适配)
if ( config . enableGlobalError ) {
// Web环境
if ( typeof window !== 'undefined' ) {
window . onerror = ( message , source , lineno , colno , error ) => {
this . _handleGlobalError ( {
type : 'global' ,
message ,
source ,
lineno ,
colno ,
error ,
timestamp : Date . now ( ) ,
} )
}
// 使用 uniapp-error-monitor 或本地实现初始化错误监控
monitorInstance . init ( config )
// 处理未捕获的Promise错误
window . addEventListener ( 'unhandledrejection' , event => {
this . _handlePromiseError ( {
type : 'promise' ,
reason : event . reason ,
promise : event . promise ,
timestamp : Date . now ( ) ,
} )
} )
}
// uniapp环境 - 提供Promise包装工具
if ( typeof uni !== 'undefined' && config . enablePromiseError ) {
// 提供一个包装Promise的方法, 让开发者可以手动包装重要的Promise
this . wrapPromise = promise => {
const self = this
return promise . catch ( error => {
self . _handlePromiseError ( {
type : 'promise' ,
reason : error ,
timestamp : Date . now ( ) ,
} )
throw error
} )
}
}
}
// console.error捕获( 可选)
if ( config . enableConsoleError ) {
const originalError = console . error
console . error = ( ... args ) => {
originalError . apply ( console , args )
this . _handleConsoleError ( {
type : 'console' ,
args : args . map ( arg => this . _serializeError ( arg ) ) ,
timestamp : Date . now ( ) ,
} )
}
}
// 微信小程序错误捕获
if ( typeof uni !== 'undefined' ) {
// 监听小程序错误事件
uni . onError &&
uni . onError ( error => {
this . _handleMiniProgramError ( {
type : 'miniProgram' ,
error ,
timestamp : Date . now ( ) ,
} )
} )
// 监听小程序页面错误
uni . onPageNotFound &&
uni . onPageNotFound ( result => {
this . _handleMiniProgramError ( {
type : 'pageNotFound' ,
path : result . path ,
query : result . query ,
timestamp : Date . now ( ) ,
} )
} )
// 监听小程序网络请求错误
const originalRequest = uni . request
uni . request = options => {
return originalRequest ( {
... options ,
fail : err => {
options . fail && options . fail ( err )
this . _handleNetworkError ( {
type : 'network' ,
url : options . url ,
method : options . method ,
error : err ,
timestamp : Date . now ( ) ,
} )
} ,
} )
}
}
console . log ( '错误监控已初始化' )
console . log ( '错误监控已初始化(基于 uniapp-error-monitor) ' )
}
/**
* 手动上报错误
* @param {string} type 错误类型 ('manual', 'api', 'network', 'global', 'promise', 'console', 'miniProgram')
* @param {Error|Object} error 错误对象或错误信息
* @param {Object} [context] 错误上下文信息
* @param {boolean} [forceSend=false] 强制发送(忽略环境检查)
*/
reportError ( error , context = { } , forceSend = false ) {
const errorInfo = {
type : 'manual' ,
error : error instanceof Error ? error . message : error ,
stack : error instanceof Error ? error . stack : null ,
context ,
timestamp : Date . now ( ) ,
url : this . _getCurrentUrl ( ) ,
userAgent : this . _getUserAgent ( ) ,
page : getCurrentPageName ( ) ,
reportError ( type = 'manual' , error, context = { } , forceSend = false ) {
monitorInstance . reportError ( type , error , context , forceSend )
}
if ( forceSend ) {
// 强制发送
this . _sendErrorToWebhook ( errorInfo , 0 , true )
} else {
this . _sendErrorToWebhook ( errorInfo )
}
/**
* 包装Promise, 自动捕获Promise错误
* @param {Promise} promise 要包装的Promise
* @returns {Promise} 包装后的Promise
*/
wrapPromise ( promise ) {
return monitorInstance . wrapPromise ( promise )
}
/**
@@ -576,21 +571,14 @@ class Tool {
* @returns {Object} 错误统计信息
*/
getErrorStats ( ) {
return { ... this . e rrorStats }
return monitorInstance . getE rrorStats( )
}
/**
* 重置错误统计
*/
resetErrorStats ( ) {
this . e rrorStats = {
total : 0 ,
global : 0 ,
promise : 0 ,
console : 0 ,
miniProgram : 0 ,
lastErrorTime : null ,
}
monitorInstance . resetE rrorStats( )
}
/**
@@ -598,359 +586,32 @@ class Tool {
* @returns {Object} 环境信息
*/
getEnvironmentInfo ( ) {
return {
isProduction : this . _isProduction ( ) ,
mode : import . meta . env . MODE ,
platform : this . _getUserAgent ( ) ,
errorMonitorEnabled : ! ! this . config ,
timestamp : Date . now ( ) ,
}
return monitorInstance . getEnvironmentInfo ( )
}
/**
* 处理全局错误
* @private
* 确认对话框
* @param {string} content 确认内容
* @param {string} [title='提示'] 标题
* @returns {Promise<boolean>} 用户选择结果, true为确认, false为取消
*/
_handleGlobalError ( errorInfo ) {
this . errorStats . total ++
this . errorStats . global ++
this . errorStats . lastErrorTime = errorInfo . timestamp
this . _sendErrorToWebhook ( {
... errorInfo ,
message : errorInfo . message || 'Unknown global error' ,
source : errorInfo . source || '' ,
lineno : errorInfo . lineno || 0 ,
colno : errorInfo . colno || 0 ,
url : this . _getCurrentUrl ( ) ,
userAgent : this . _getUserAgent ( ) ,
page : getCurrentPageName ( ) ,
} )
}
/**
* 处理Promise错误
* @private
*/
_handlePromiseError ( errorInfo ) {
this . errorStats . total ++
this . errorStats . promise ++
this . errorStats . lastErrorTime = errorInfo . timestamp
this . _sendErrorToWebhook ( {
... errorInfo ,
reason : this . _serializeError ( errorInfo . reason ) ,
url : this . _getCurrentUrl ( ) ,
userAgent : this . _getUserAgent ( ) ,
page : getCurrentPageName ( ) ,
} )
}
/**
* 处理console错误
* @private
*/
_handleConsoleError ( errorInfo ) {
this . errorStats . total ++
this . errorStats . console ++
this . errorStats . lastErrorTime = errorInfo . timestamp
this . _sendErrorToWebhook ( {
... errorInfo ,
url : this . _getCurrentUrl ( ) ,
userAgent : this . _getUserAgent ( ) ,
page : getCurrentPageName ( ) ,
} )
}
/**
* 处理小程序错误
* @private
*/
_handleMiniProgramError ( errorInfo ) {
this . errorStats . total ++
this . errorStats . miniProgram ++
this . errorStats . lastErrorTime = errorInfo . timestamp
this . _sendErrorToWebhook ( {
... errorInfo ,
url : this . _getCurrentUrl ( ) ,
userAgent : this . _getUserAgent ( ) ,
page : getCurrentPageName ( ) ,
} )
}
/**
* 处理网络错误
* @private
*/
_handleNetworkError ( errorInfo ) {
this . errorStats . total ++
this . errorStats . miniProgram ++
this . errorStats . lastErrorTime = errorInfo . timestamp
this . _sendErrorToWebhook ( {
... errorInfo ,
url : this . _getCurrentUrl ( ) ,
userAgent : this . _getUserAgent ( ) ,
page : getCurrentPageName ( ) ,
} )
}
/**
* 获取当前URL
* @private
*/
_getCurrentUrl ( ) {
if ( typeof window !== 'undefined' ) {
return window . location ? . href || ''
}
if ( typeof uni !== 'undefined' ) {
try {
const pages = getCurrentPages ( )
if ( pages && pages . length > 0 ) {
const currentPage = pages [ pages . length - 1 ]
return currentPage . route || ''
}
} catch ( error ) {
// 忽略错误
}
}
return ''
}
/**
* 获取用户代理信息
* @private
*/
_getUserAgent ( ) {
if ( typeof navigator !== 'undefined' ) {
return navigator . userAgent || ''
}
if ( typeof uni !== 'undefined' ) {
try {
const systemInfo = uni . getSystemInfoSync ( )
return ` ${ systemInfo . platform } ${ systemInfo . system } ${ systemInfo . model } `
} catch ( error ) {
return 'Unknown Device'
}
}
return 'Unknown Device'
}
/**
* 序列化错误对象
* @private
*/
_serializeError ( error ) {
if ( error instanceof Error ) {
return {
name : error . name ,
message : error . message ,
stack : error . stack ,
}
}
if ( typeof error === 'object' && error !== null ) {
try {
return JSON . stringify ( error , null , 2 )
} catch ( e ) {
return String ( error )
}
}
return String ( error )
}
/**
* 发送错误到webhook
* @private
*/
async _sendErrorToWebhook ( errorInfo , retryCount = 0 , forceSend = false ) {
// 环境检查:只在生产环境下发送错误信息
if ( ! forceSend && ! this . _isProduction ( ) && ! this . config ? . forceEnable ) {
console . info ( '非生产环境, 错误信息不上报到webhook:' , errorInfo . type )
return
}
const webhookUrl = import . meta . env . VITE _WEBHOOK
if ( ! webhookUrl ) {
console . error ( '未配置webhook地址, 无法发送错误信息' )
return
}
try {
// 格式化错误信息
const message = this . _formatErrorMessage ( errorInfo )
// 使用uni.request发送POST请求( 适配uniapp环境)
await new Promise ( ( resolve , reject ) => {
uni . request ( {
url : webhookUrl ,
method : 'POST' ,
header : {
'Content-Type' : 'application/json' ,
confirm ( content , title = '提示' ) {
return new Promise ( resolve => {
uni . showModal ( {
title ,
content ,
confirmText : '确认' ,
cancelText : '取消' ,
success : res => {
resolve ( res . confirm )
} ,
data : {
msgtype : 'text' ,
text : {
content : message ,
mentioned _list : [ ] ,
fail : ( ) => {
resolve ( false )
} ,
} ,
success : resolve ,
fail : reject ,
} )
} )
console . log ( '错误信息已发送到webhook' )
} catch ( error ) {
console . error ( '发送错误到webhook失败:' , error )
// 重试机制
if ( retryCount < ( this . config ? . maxRetries || 3 ) ) {
setTimeout ( ( ) => {
this . _sendErrorToWebhook ( errorInfo , retryCount + 1 )
} , ( this . config ? . retryDelay || 1000 ) * ( retryCount + 1 ) )
}
}
}
/**
* 加载项目信息
* @private
*/
_loadProjectInfo ( ) {
try {
if ( uni . getSystemInfoSync ( ) ) {
const AppInfo = uni . getSystemInfoSync ( )
this . projectInfo . name = AppInfo . appName
this . projectInfo . version = AppInfo . appVersion
}
} catch ( error ) {
// 如果加载失败,使用默认信息
console . warn ( '无法加载项目信息,使用默认值' )
}
}
/**
* 格式化错误消息
* @private
*/
_formatErrorMessage ( errorInfo ) {
const timestamp = new Date ( errorInfo . timestamp ) . toLocaleString ( 'zh-CN' )
let message = ` 🚨 JavaScript错误报告 \n `
message += ` 📦 项目: ${ this . projectInfo . name } \n `
message += ` 🏷️ 版本: ${ this . projectInfo . version } \n `
message += ` ⏰ 时间: ${ timestamp } \n `
message += ` 📱 页面: ${ errorInfo . page || '未知页面' } \n `
message += ` 🌐 链接: ${ errorInfo . url || '未知链接' } \n \n `
switch ( errorInfo . type ) {
case 'global' :
message += ` 🔍 错误类型: 全局错误 \n `
message += ` 📝 错误信息: ${ errorInfo . message } \n `
if ( errorInfo . source ) {
message += ` 📂 文件: ${ errorInfo . source } \n `
}
if ( errorInfo . lineno ) {
message += ` 📍 行号: ${ errorInfo . lineno } : ${ errorInfo . colno } \n `
}
break
case 'promise' :
message += ` 🔍 错误类型: Promise错误 \n `
message += ` 📝 错误信息: ${ this . _serializeError ( errorInfo . reason ) } \n `
break
case 'console' :
message += ` 🔍 错误类型: Console错误 \n `
message += ` 📝 错误信息: ${ errorInfo . args . join ( ' ' ) } \n `
break
case 'miniProgram' :
message += ` 🔍 错误类型: 小程序错误 \n `
message += ` 📝 错误信息: ${ errorInfo . error || 'Unknown' } \n `
if ( errorInfo . path ) {
message += ` 📱 页面路径: ${ errorInfo . path } \n `
}
if ( errorInfo . query ) {
message += ` 🔗 查询参数: ${ errorInfo . query } \n `
}
break
case 'network' :
message += ` 🔍 错误类型: 网络错误 \n `
message += ` 📝 请求地址: ${ errorInfo . url || 'Unknown' } \n `
message += ` 📝 请求方法: ${ errorInfo . method || 'Unknown' } \n `
message += ` 📝 错误信息: ${ this . _serializeError ( errorInfo . error ) } \n `
break
default :
message += ` 🔍 错误类型: ${ errorInfo . type } \n `
message += ` 📝 错误信息: ${ this . _serializeError ( errorInfo . error ) } \n `
}
message += ` \n 📊 统计信息: \n `
message += ` 总计错误: ${ this . errorStats . total } \n `
message += ` 全局错误: ${ this . errorStats . global } \n `
message += ` Promise错误: ${ this . errorStats . promise } \n `
message += ` Console错误: ${ this . errorStats . console } \n `
message += ` 小程序错误: ${ this . errorStats . miniProgram } \n `
// 添加设备信息
if ( errorInfo . userAgent ) {
message += ` \n 📱 设备信息: \n ${ errorInfo . userAgent } \n `
}
return message
}
}
/**
* 获取当前页面名称
* @returns {string} 页面名称
*/
function getCurrentPageName ( ) {
try {
// 尝试从getCurrentPages获取
const pages = getCurrentPages ( )
if ( pages && pages . length > 0 ) {
const currentPage = pages [ pages . length - 1 ]
return currentPage . route || currentPage . $page ? . fullPath || '未知页面'
}
} catch ( error ) {
// 忽略错误,返回默认值
}
// 微信小程序环境
if ( typeof uni !== 'undefined' ) {
try {
const currentPages = getCurrentPages ? . ( )
if ( currentPages && currentPages . length > 0 ) {
return currentPages [ currentPages . length - 1 ] ? . route || '未知页面'
}
} catch ( error ) {
return '未知页面'
}
}
// Web环境
try {
if ( typeof window !== 'undefined' && window . location ) {
return window . location . pathname || '未知页面'
}
} catch ( error ) {
return '未知页面'
}
return '未知页面'
}
// 创建单例并导出
export default new Tool ( )