// index.js (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define([], factory); } else if (typeof module === 'object' && module.exports) { // CommonJS module.exports = factory(); } else { // 全局变量 root.UtmTracker = factory(); } }(this, function () { 'use strict'; // 工具函数 function getUrlParam(name) { var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); var r = window.location.search.substr(1).match(reg); if (r != null) return decodeURIComponent(r[2]); return null; } // 获取浏览器信息 function getBrowserInfo() { var ua = navigator.userAgent; var browser = 'Unknown'; var isMobile = /Mobile|Android|iPhone|iPad|iPod|HarmonyOS|HMS/i.test(ua); var osType = 'Unknown'; var osVersion = 'Unknown'; // 系统类型和版本号判断 if (/iPhone|iPad|iPod/i.test(ua)) { osType = 'iOS'; var iosVersionMatch = ua.match(/OS (\d+)[_.](\d+)?([_.](\d+))?/i); if (iosVersionMatch) { osVersion = iosVersionMatch[1]; if (iosVersionMatch[2]) osVersion += '.' + iosVersionMatch[2]; if (iosVersionMatch[4]) osVersion += '.' + iosVersionMatch[4]; } } else if (/Android/i.test(ua)) { osType = 'Android'; var androidVersionMatch = ua.match(/Android ([\d.]+)/i); if (androidVersionMatch) { osVersion = androidVersionMatch[1]; } } else if (/HarmonyOS|HMS/i.test(ua)) { osType = 'HarmonyOS'; var harmonyVersionMatch = ua.match(/HarmonyOS[\s/]?([\d.]+)/i) || ua.match(/HMSCore[\s/]?([\d.]+)/i); if (harmonyVersionMatch) { osVersion = harmonyVersionMatch[1]; } } // 浏览器类型判断 if (/Edg/i.test(ua)) { browser = 'Edge'; } else if (/HuaweiBrowser/i.test(ua) || /HMS/i.test(ua)) { browser = 'HuaweiBrowser'; } else if (/Chrome/i.test(ua)) { browser = 'Chrome'; } else if (/Firefox/i.test(ua)) { browser = 'Firefox'; } else if (/Safari/i.test(ua) && !/Chrome/i.test(ua) && !/Edg/i.test(ua)) { browser = 'Safari'; } else if (/MSIE|Trident/i.test(ua)) { browser = 'IE'; } // 特殊浏览器环境检测 if (/MicroMessenger/i.test(ua)) { browser = 'WeChat'; } else if (/QQBrowser/i.test(ua)) { browser = 'QQBrowser'; } else if (/UCBrowser/i.test(ua)) { browser = 'UCBrowser'; } else if (/Telegram/i.test(ua)) { browser = 'Telegram'; } return { isMobile: isMobile, browser: browser, userAgent: ua, osType: osType, osVersion: osVersion }; } //获取上一级url function getPreviousUrl() { let referrer = document.referrer; if (referrer) { return referrer; } // else if (window.history.length > 1) { // // 如果有历史记录 // window.history.back(); // return window.location.href; // } else { // // 如果没有历史记录 // return null; // } } // 主类 function UtmTracker(config) { this.config = Object.assign({ reportUrl: '', // 上报地址 autoSend: true, // 是否自动上报 method: 'POST', // 请求方式 headers: { 'Content-Type': 'application/json' }, // 请求头 extra: {}, // 额外参数 }, config || {}); if (this.config.autoSend && this.config.reportUrl) { this.send(); } } // 获取UTM参数 UtmTracker.prototype.getParams = function() { const browser = getBrowserInfo() const params = { utm_source: getUrlParam('utm_source') || '', utm_medium: getUrlParam('utm_medium') || '', utm_campaign: getUrlParam('utm_campaign') || '', utm_term: getUrlParam('utm_term') || '', utm_content: getUrlParam('utm_content') || '', referrer: getPreviousUrl() || '', timestamp: new Date().toISOString(), url: window.location.href, isMobile: browser.isMobile, browser: browser.browser, userAgent: browser.ua, osType: browser.osType, osVersion: browser.osVersion }; // 合并额外参数 let result = params; if (this.config && this.config.extra && typeof this.config.extra === 'object') { result = Object.assign({}, params, this.config.extra); } if (typeof this.config.onParams === 'function') { this.config.onParams(result); } return result; }; // 发送请求 UtmTracker.prototype.send = function (data) { const cfg = this.config || {}; const payload = data || this.getParams(); console.log(payload); if (!cfg.reportUrl) return; fetch(cfg.reportUrl, { method: 'POST', headers: cfg.headers || { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }) .then(res => { // 假设res为Response对象,需要解析json if (typeof res.json === 'function') { res.json().then(data => { if (data.downloadUrl) { // 倒计时弹窗 let countdown = 3; const countdownMask = document.createElement('div'); countdownMask.style.position = 'fixed'; countdownMask.style.top = 0; countdownMask.style.left = 0; countdownMask.style.width = '100vw'; countdownMask.style.height = '100vh'; countdownMask.style.background = 'rgba(0,0,0,0.5)'; countdownMask.style.zIndex = 9999; countdownMask.style.display = 'flex'; countdownMask.style.alignItems = 'center'; countdownMask.style.justifyContent = 'center'; const countdownModal = document.createElement('div'); countdownModal.style.background = '#fff'; countdownModal.style.borderRadius = '8px'; countdownModal.style.padding = '32px 48px'; countdownModal.style.fontSize = '20px'; countdownModal.style.boxShadow = '0 2px 16px rgba(0,0,0,0.2)'; countdownModal.style.display = 'flex'; countdownModal.style.flexDirection = 'column'; countdownModal.style.alignItems = 'center'; const text = document.createElement('div'); text.innerText = `即将跳转,${countdown}秒...`; countdownModal.appendChild(text); countdownMask.appendChild(countdownModal); document.body.appendChild(countdownMask); const timer = setInterval(() => { countdown--; text.innerText = `即将跳转,${countdown}秒...`; if (countdown <= 0) { clearInterval(timer); document.body.removeChild(countdownMask); // 跳转 console.log(data.downloadUrl); window.location.href = data.downloadUrl return } }, 1000); return // 创建遮罩 const mask = document.createElement('div'); mask.style.position = 'fixed'; mask.style.top = 0; mask.style.left = 0; mask.style.width = '100vw'; mask.style.height = '100vh'; mask.style.background = 'rgba(0,0,0,0.5)'; mask.style.zIndex = 9999; mask.style.display = 'flex'; mask.style.alignItems = 'center'; mask.style.justifyContent = 'center'; // 弹窗容器 const modal = document.createElement('div'); modal.style.background = '#fff'; modal.style.borderRadius = '8px'; modal.style.padding = '24px'; modal.style.maxWidth = '90vw'; modal.style.maxHeight = '90vh'; modal.style.boxShadow = '0 2px 16px rgba(0,0,0,0.2)'; modal.style.position = 'relative'; modal.style.display = 'flex'; modal.style.flexDirection = 'column'; modal.style.alignItems = 'center'; // 关闭按钮 const closeBtn = document.createElement('button'); closeBtn.innerText = '关闭'; closeBtn.style.position = 'absolute'; closeBtn.style.top = '8px'; closeBtn.style.right = '8px'; closeBtn.style.background = '#f44336'; closeBtn.style.color = '#fff'; closeBtn.style.border = 'none'; closeBtn.style.borderRadius = '4px'; closeBtn.style.padding = '4px 12px'; closeBtn.style.cursor = 'pointer'; closeBtn.onclick = function () { document.body.removeChild(mask); }; modal.appendChild(closeBtn); // 内容区 let contentEl; if (data.type === 'video') { contentEl = document.createElement('video'); contentEl.src = data.downloadUrl; contentEl.controls = true; contentEl.autoplay = true; contentEl.style.maxWidth = '80vw'; contentEl.style.maxHeight = '70vh'; } else if (data.type === 'audio') { contentEl = document.createElement('audio'); contentEl.src = data.downloadUrl; contentEl.controls = true; contentEl.autoplay = true; contentEl.style.width = '100%'; } else if (data.type === 'image') { contentEl = document.createElement('img'); contentEl.src = data.downloadUrl; contentEl.style.maxWidth = '80vw'; contentEl.style.maxHeight = '70vh'; } else { contentEl = document.createElement('a'); contentEl.href = data.downloadUrl; contentEl.innerText = '下载文件'; contentEl.target = '_blank'; } modal.appendChild(contentEl); mask.appendChild(modal); document.body.appendChild(mask); } }); } }) .catch(err => { console.log(err); }); }; // 静态方法:快速获取(无需实例化) UtmTracker.get = function (config) { const tracker = new UtmTracker(config); const params = tracker.getParams(); if (config && config.autoSend && config.reportUrl) { tracker.send(params); } return params; }; return UtmTracker; }));