buriedPiont.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // 通用 JSBridge
  2. window.JSBridge = {
  3. getOS: function () {
  4. const ua = navigator.userAgent || navigator.vendor || window.opera;
  5. if (/android/i.test(ua)) return 'Android';
  6. if (/iPad|iPhone|iPod/.test(ua) && !window.MSStream) return 'iOS';
  7. return 'Web';
  8. },
  9. getUserId: function (callback) {
  10. // Android WebView
  11. if (window.Android && typeof window.Android.getUserId === 'function') {
  12. callback(window.Android.getUserId());
  13. return;
  14. }
  15. // iOS WebView
  16. if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.getUserId) {
  17. window.webkit.messageHandlers.getUserId.postMessage(null);
  18. window.onUserIdReceived = callback; // iOS原生需回调window.onUserIdReceived(userId)
  19. return;
  20. }
  21. // Web环境(用 FingerprintJS 生成指纹)
  22. if (window.FingerprintJS) {
  23. FingerprintJS.load().then(fp => {
  24. fp.get().then(result => {
  25. callback(result.visitorId);
  26. });
  27. });
  28. } else {
  29. callback('unsupported');
  30. }
  31. }
  32. };
  33. let userId = '';
  34. JSBridge.getUserId(function (id) {
  35. userId = id;
  36. });
  37. class Tracker {
  38. constructor(options = {}) {
  39. this.baseUrl = options.baseUrl || '';
  40. this.timer = null;
  41. this.startTime = 0;
  42. this.heartbeatInterval = options.heartbeatInterval || 30000; // 默认30秒发送一次心跳
  43. }
  44. init() {
  45. this.startTime = Date.now();
  46. this.bindEvents();
  47. this.startHeartbeat();
  48. }
  49. bindEvents() {
  50. window.addEventListener('beforeunload', this.handleBeforeUnload);
  51. window.addEventListener('click', this.handleClickEvent);
  52. }
  53. startHeartbeat() {
  54. this.timer = setInterval(() => {
  55. console.log(userId);
  56. this.sendData({
  57. eventType: 'heartbeat',
  58. duration: Math.round((Date.now() - this.startTime) / 1000),
  59. }, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
  60. }, this.heartbeatInterval);
  61. }
  62. handleBeforeUnload = (e) => {
  63. const data = {
  64. eventType: 'page_close',
  65. duration: Math.round((Date.now() - this.startTime) / 1000),
  66. };
  67. const payload = JSON.stringify(data);
  68. if (navigator.sendBeacon) {
  69. navigator.sendBeacon(this.baseUrl, payload);
  70. } else {
  71. this.sendData(payload, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
  72. }
  73. };
  74. handleClickEvent = (event) => {
  75. const target = event.target;
  76. if (target.matches('[data-track]')) {
  77. const trackInfo = {
  78. eventType: 'button_click',
  79. elementId: target.id || '无ID',
  80. elementClass: target.className || '无class',
  81. text: target.innerText || target.textContent || '',
  82. timestamp: new Date().toISOString(),
  83. };
  84. this.sendData(trackInfo, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
  85. }
  86. };
  87. sendData(data, headers = {}) {
  88. console.log('上报埋点数据:', data);
  89. fetch(`${this.baseUrl}`, { method: 'POST', headers: { 'Content-Type': 'application/json' ,...headers}, body: JSON.stringify(data) });
  90. }
  91. destroy() {
  92. window.removeEventListener('beforeunload', this.handleBeforeUnload);
  93. window.removeEventListener('click', this.handleClickEvent);
  94. if (this.timer) {
  95. clearInterval(this.timer);
  96. }
  97. this.sendData({
  98. eventType: 'tracker_destroyed',
  99. duration: Math.round((Date.now() - this.startTime) / 1000),
  100. }, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
  101. }
  102. }
  103. // 使用示例:
  104. // const tracker = new Tracker({ baseUrl: 'https://your-tracking-api.com' });
  105. // tracker.init();
  106. // 暴露给全局或者作为模块导出
  107. window.Tracker = Tracker;