Ver código fonte

feat: 添加上报接口

jcq 3 semanas atrás
pai
commit
a97fd37d1b
2 arquivos alterados com 224 adições e 6 exclusões
  1. 45 0
      README.md
  2. 179 6
      src/index.js

+ 45 - 0
README.md

@@ -62,7 +62,32 @@ fetch('https://your-api.com/track', {
    ```
    这样 HTML 里就可以直接用 `window.UtmTracker.get()`。
 
+## 使用示例
+
+### 自动上报
+```js
+new UtmTracker({
+  reportUrl: 'https://your-server.com/collect',
+  autoSend: true,
+  method: 'POST', // 或 'GET'
+  extra: { custom: 'value' }
+});
+```
+
+### 手动上报
+```js
+const tracker = new UtmTracker({ reportUrl: 'https://your-server.com/collect' });
+const params = tracker.getParams();
+tracker.send(params);
+```
 
+### 静态用法
+```js
+UtmTracker.get({
+  reportUrl: 'https://your-server.com/collect',
+  autoSend: true
+});
+```
 
 ### 返回参数说明
 
@@ -131,6 +156,26 @@ fetch('https://your-api.com/track', {
 }
 ```
 
+## 跳转倒计时弹窗使用示例
+
+当后端返回 { downloadUrl, type } 时,自动弹出3秒倒计时弹窗,倒计时结束后自动跳转到 downloadUrl。
+
+```js
+new UtmTracker({
+  reportUrl: 'https://your-server.com/collect',
+  autoSend: true,
+  // 只需后端返回 { downloadUrl: '...', type: 'video'|'audio'|'image' },无需额外配置
+});
+```
+
+// 后端返回示例:
+// {
+//   downloadUrl: 'https://your-server.com/file.mp4',
+//   type: 'video'
+// }
+
+// 前端会自动弹出“即将跳转,3秒...”的弹窗,3秒后自动跳转到 downloadUrl。
+
 ---
 
 如需更多帮助或定制返回内容,请联系作者。

+ 179 - 6
src/index.js

@@ -104,26 +104,199 @@
         // }
     }
     // 主类
-    function UtmTracker() {}
+    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() {
-        return {
+        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() || '',
-            browser: getBrowserInfo(),
             timestamp: new Date().toISOString(),
-            url: window.location.href
+            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() {
-        return new UtmTracker().getParams();
+    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;