// 通用 JSBridge window.JSBridge = { getOS: function () { const ua = navigator.userAgent || navigator.vendor || window.opera; if (/android/i.test(ua)) return 'Android'; if (/iPad|iPhone|iPod/.test(ua) && !window.MSStream) return 'iOS'; return 'Web'; }, getUserId: function (callback) { // Android WebView if (window.Android && typeof window.Android.getUserId === 'function') { callback(window.Android.getUserId()); return; } // iOS WebView if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.getUserId) { window.webkit.messageHandlers.getUserId.postMessage(null); window.onUserIdReceived = callback; // iOS原生需回调window.onUserIdReceived(userId) return; } // Web环境(用 FingerprintJS 生成指纹) if (window.FingerprintJS) { FingerprintJS.load().then(fp => { fp.get().then(result => { callback(result.visitorId); }); }); } else { callback('unsupported'); } } }; let userId = ''; JSBridge.getUserId(function (id) { userId = id; }); class Tracker { constructor(options = {}) { this.baseUrl = options.baseUrl || ''; this.timer = null; this.startTime = 0; this.heartbeatInterval = options.heartbeatInterval || 30000; // 默认30秒发送一次心跳 } init() { this.startTime = Date.now(); this.bindEvents(); this.startHeartbeat(); } bindEvents() { window.addEventListener('beforeunload', this.handleBeforeUnload); window.addEventListener('click', this.handleClickEvent); } startHeartbeat() { this.timer = setInterval(() => { console.log(userId); this.sendData({ eventType: 'heartbeat', duration: Math.round((Date.now() - this.startTime) / 1000), }, { heartbeatSeconds: this.heartbeatInterval, userId: userId }); }, this.heartbeatInterval); } handleBeforeUnload = (e) => { const data = { eventType: 'page_close', duration: Math.round((Date.now() - this.startTime) / 1000), }; const payload = JSON.stringify(data); if (navigator.sendBeacon) { navigator.sendBeacon(this.baseUrl, payload); } else { this.sendData(payload, { heartbeatSeconds: this.heartbeatInterval, userId: userId }); } }; handleClickEvent = (event) => { const target = event.target; if (target.matches('[data-track]')) { const trackInfo = { eventType: 'button_click', elementId: target.id || '无ID', elementClass: target.className || '无class', text: target.innerText || target.textContent || '', timestamp: new Date().toISOString(), }; this.sendData(trackInfo, { heartbeatSeconds: this.heartbeatInterval, userId: userId }); } }; sendData(data, headers = {}) { console.log('上报埋点数据:', data); fetch(`${this.baseUrl}`, { method: 'POST', headers: { 'Content-Type': 'application/json' ,...headers}, body: JSON.stringify(data) }); } destroy() { window.removeEventListener('beforeunload', this.handleBeforeUnload); window.removeEventListener('click', this.handleClickEvent); if (this.timer) { clearInterval(this.timer); } this.sendData({ eventType: 'tracker_destroyed', duration: Math.round((Date.now() - this.startTime) / 1000), }, { heartbeatSeconds: this.heartbeatInterval, userId: userId }); } } // 使用示例: // const tracker = new Tracker({ baseUrl: 'https://your-tracking-api.com' }); // tracker.init(); // 暴露给全局或者作为模块导出 window.Tracker = Tracker;