/** * 前端埋点工具 * 记录用户关键操作和错误信息 */ import { request } from './request' interface TrackEvent { event: string page?: string data?: Record } /** 上报队列(批量上报,减少请求) */ let eventQueue: TrackEvent[] = [] let flushTimer: number | null = null const FLUSH_INTERVAL = 500 // 500ms 攒批 const MAX_QUEUE_SIZE = 10 // 10 条立即发送 /** 获取当前页面路径 */ function getCurrentPage(): string { const pages = getCurrentPages() if (pages.length > 0) { return `/${pages[pages.length - 1].route}` } return '/unknown' } /** 批量上报事件 */ async function flushEvents() { if (eventQueue.length === 0) return const events = [...eventQueue] eventQueue = [] try { await request({ url: '/track', method: 'POST', data: { events } }) } catch { // 上报失败不处理,避免影响用户体验 } } /** 添加事件到队列 */ function addToQueue(event: TrackEvent) { eventQueue.push(event) // 队列满时立即上报 if (eventQueue.length >= MAX_QUEUE_SIZE) { flushEvents() return } // 设置定时上报 if (!flushTimer) { flushTimer = setTimeout(() => { flushTimer = null flushEvents() }, FLUSH_INTERVAL) as unknown as number } } /** * 追踪页面访问 */ export function trackPageView(pagePath?: string) { addToQueue({ event: 'page_view', page: pagePath || getCurrentPage() }) } /** * 追踪用户操作 * @param action 操作名称,如 'click_save', 'submit_form' * @param data 附加数据 */ export function trackAction(action: string, data?: Record) { addToQueue({ event: 'action', page: getCurrentPage(), data: { action, ...data } }) } /** * 追踪错误 */ export function trackError(error: Error | string, extra?: Record) { const errorInfo = typeof error === 'string' ? { message: error } : { message: error.message, stack: error.stack?.slice(0, 500) } addToQueue({ event: 'error', page: getCurrentPage(), data: { ...errorInfo, ...extra } }) } /** * 追踪 API 请求失败 */ export function trackApiError(url: string, status: number, message: string) { addToQueue({ event: 'api_error', page: getCurrentPage(), data: { url, status, message } }) } /** * 设置全局错误监听 */ export function setupErrorTracking() { // Vue 错误处理 uni.onError((err: string) => { trackError(err, { type: 'global' }) }) // 未处理的 Promise rejection // #ifdef H5 if (typeof window !== 'undefined') { window.addEventListener('unhandledrejection', (event) => { trackError(String(event.reason), { type: 'unhandledrejection' }) }) } // #endif } export default { trackPageView, trackAction, trackError, trackApiError, setupErrorTracking }