123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- // index.js
- import FingerprintJS from '@fingerprintjs/fingerprintjs';
- (function (factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD
- define([], factory);
- } else if (typeof module === 'object' && module.exports) {
- // CommonJS
- module.exports = factory();
- } else {
- // 浏览器全局变量
- var globalObj = typeof globalThis !== 'undefined' ? globalThis
- : typeof self !== 'undefined' ? self
- : typeof window !== 'undefined' ? window
- : typeof global !== 'undefined' ? global
- : this;
- globalObj.UtmTracker = factory();
- }
- }(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();
- }
- }
- // 集成 FingerprintJS,兼容npm和浏览器全局
- console.log('FingerprintJSLib:', FingerprintJS);
- // 获取UTM参数(同步)
- UtmTracker.prototype.getParams = function () {
- const browser = getBrowserInfo()
- let fingerprint = '';
- try {
- fingerprint = localStorage.getItem('fingerprint_id') || '';
- } catch (e) { }
- const params = {
- utmSource: getUrlParam('utm_source') || '',
- utmMedium: getUrlParam('utm_medium') || '',
- utmCampaign: getUrlParam('utm_campaign') || '',
- utmTerm: getUrlParam('utm_term') || '',
- utmContent: getUrlParam('utm_content') || '',
- referrer: getPreviousUrl() || '',
- fingerprint: fingerprint,
- timestamp: new Date().toISOString(),
- url: window.location.href,
- domain: window.location.hostname,
- isMobile: browser.isMobile,
- browser: browser.browser,
- userAgent: browser.userAgent,
- 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;
- };
- // 获取指纹ID(本地优先,无则生成并存储)
- async function getfingerprint() {
- let fingerprint = '';
- try {
- fingerprint = localStorage.getItem('fingerprint_id') || '';
- } catch (e) { }
- if (fingerprint) {
- return fingerprint;
- } else if (FingerprintJS && typeof FingerprintJS.load === 'function') {
- try {
- const fp = await FingerprintJS.load();
- const result = await fp.get();
- fingerprint = result.visitorId;
- try {
- localStorage.setItem('fingerprint_id', fingerprint);
- } catch (e) { }
- return fingerprint;
- } catch (e) {
- return '';
- }
- } else {
- return '';
- }
- }
- // 获取UTM参数(异步,带指纹,本地唯一)
- UtmTracker.prototype.getParamsAsync = async function () {
- const params = this.getParams();
- params.fingerprint = await getfingerprint();
- return params;
- };
- // 发送请求(自动带指纹,异步,指纹本地唯一)
- UtmTracker.prototype.send = async function (data) {
- const cfg = this.config || {};
- let payload = data;
- if (!payload) {
- payload = await this.getParamsAsync();
- } else if (!payload.fingerprint) {
- payload.fingerprint = await getfingerprint();
- }
- 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.code == 1) return
- if (data.data && data.data.downloadUrl) {
- if (data.data.type === 'link') {
- // 倒计时弹窗
- 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.data.downloadUrl);
- window.location.href = data.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.data.type === 'video') {
- contentEl = document.createElement('video');
- contentEl.src = data.data.downloadUrl;
- contentEl.controls = true;
- contentEl.autoplay = true;
- contentEl.style.maxWidth = '80vw';
- contentEl.style.maxHeight = '70vh';
- } else if (data.data.type === 'audio') {
- contentEl = document.createElement('audio');
- contentEl.src = data.data.downloadUrl;
- contentEl.controls = true;
- contentEl.autoplay = true;
- contentEl.style.width = '100%';
- } else if (data.data.type === 'image') {
- contentEl = document.createElement('img');
- contentEl.src = data.data.downloadUrl;
- contentEl.style.maxWidth = '80vw';
- contentEl.style.maxHeight = '70vh';
- } else {
- contentEl = document.createElement('a');
- contentEl.href = data.data.downloadUrl;
- contentEl.innerText = '下载文件';
- contentEl.target = '_blank';
- }
- modal.appendChild(contentEl);
- mask.appendChild(modal);
- document.body.appendChild(mask);
- }
- });
- }
- })
- .catch(err => {
- console.log(err);
- });
- };
- // 静态方法:快速获取(无需实例化)
- UtmTracker.get = async function (config) {
- const tracker = new UtmTracker(config);
- return await tracker.getParamsAsync();
- };
- // 新增静态send方法
- UtmTracker.send = async function (data, config) {
- const tracker = new UtmTracker(config);
- return await tracker.send(data);
- };
- // 首次进入自动生成指纹ID(如本地无)
- (async function ensurefingerprint() {
- let fingerprint = '';
- try {
- fingerprint = localStorage.getItem('fingerprint_id') || '';
- } catch (e) { }
- if (!fingerprint && FingerprintJS && typeof FingerprintJS.load === 'function') {
- try {
- const fp = await FingerprintJS.load();
- const result = await fp.get();
- fingerprint = result.visitorId;
- try {
- localStorage.setItem('fingerprint_id', fingerprint);
- } catch (e) { }
- } catch (e) { }
- }
- })();
- return UtmTracker;
- }));
|