Pārlūkot izejas kodu

fix: 添加用户指纹

jcq 2 nedēļas atpakaļ
vecāks
revīzija
775f512aa7
8 mainītis faili ar 1053 papildinājumiem un 34 dzēšanām
  1. 72 0
      README.md
  2. 20 0
      demo-utm-vue3/package.json
  3. 792 0
      demo-utm-vue3/pnpm-lock.yaml
  4. 55 0
      demo-utm-vue3/src/App.vue
  5. 29 10
      package-lock.json
  6. 2 1
      package.json
  7. 5 5
      pnpm-lock.yaml
  8. 78 18
      src/index.js

+ 72 - 0
README.md

@@ -180,3 +180,75 @@ new UtmTracker({
 
 如需更多帮助或定制返回内容,请联系作者。
 
+## 配置项说明(config 参数)
+
+| 配置项      | 类型     | 说明                                                         | 是否必填 | 默认值                      |
+|-------------|----------|--------------------------------------------------------------|----------|-----------------------------|
+| reportUrl   | string   | 上报数据的接口地址                                           | 是       | ''                          |
+| autoSend    | boolean  | 是否在实例化时自动上报一次                                   | 否       | true                       |
+| method      | string   | 请求方式,支持 'POST' 或 'GET'                               | 否       | 'POST'                     |
+| headers     | object   | 请求头设置                                                   | 否       | { 'Content-Type': 'application/json' } |
+| extra       | object   | 额外自定义参数,会合并进最终上报数据                         | 否       | {}                          |
+| onParams    | function | 获取参数后回调,参数为 params                                 | 否       | null                        |
+| onSend      | function | 发送成功回调,参数为 (response, params)                      | 否       | null                        |
+| onError     | function | 发送失败回调,参数为 (error, params)                         | 否       | null                        |
+
+### 示例
+
+```js
+import UtmTracker from 'utm-params-extractor-test';
+
+const tracker = new UtmTracker({
+  reportUrl: 'https://your-server.com/collect',
+  autoSend: false,
+  method: 'POST',
+  headers: { 'Content-Type': 'application/json' },
+  extra: { foo: 'bar' },
+  onParams: params => console.log(params),
+  onSend: (res, params) => console.log('sent', res, params),
+  onError: (err, params) => console.error('error', err, params)
+});
+```
+
+## 浏览器指纹(fingerprintId)说明
+
+- `fingerprintId` 字段会自动添加到所有 UTM 参数和上报数据中。
+- 该字段用于唯一标识当前浏览器环境,便于后端做用户去重、分析等。
+- 指纹生成后会存储在本地 localStorage(key: fingerprint_id),后续请求会优先复用,确保唯一性和高效性。
+- **本包已自动集成 [FingerprintJS](https://github.com/fingerprintjs/fingerprintjs),无需单独安装或引入,无论 npm 还是浏览器全局都能直接用指纹功能。**
+
+### 示例
+
+```js
+const tracker = new UtmTracker({ ... });
+const params = await tracker.getParamsAsync();
+console.log(params.fingerprintId); // 浏览器唯一指纹
+```
+
+## FingerprintJS 依赖说明
+
+本库自动适配两种场景:
+
+### 1. npm/打包工具用户
+- 请在你的项目中安装依赖:
+  ```bash
+  npm install @fingerprintjs/fingerprintjs
+  ```
+- 本库会自动 require 该依赖,无需手动引入。
+- 推荐在 package.json 中声明 peerDependencies:
+  ```json
+  "peerDependencies": {
+    "@fingerprintjs/fingerprintjs": ">=4.0.0"
+  }
+  ```
+
+### 2. 浏览器全局用户
+- 请在你的 HTML 中**先**引入 FingerprintJS,再引入本库:
+  ```html
+  <script src="https://openfpcdn.io/fingerprintjs/v4"></script>
+  <script src="dist/your-utm-bundle.js"></script>
+  ```
+- 本库会自动检测 window.FingerprintJS。
+
+如未正确引入依赖,指纹功能将无法使用,fingerprintId 字段会为空字符串。
+

+ 20 - 0
demo-utm-vue3/package.json

@@ -0,0 +1,20 @@
+{
+  "name": "demo-utm-vue3",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite --host",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@fingerprintjs/fingerprintjs": "^4.6.2",
+    "utm-params-extractor-test": "^1.0.11",
+    "vue": "^3.5.17"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^6.0.0",
+    "vite": "^7.0.0"
+  }
+}

+ 792 - 0
demo-utm-vue3/pnpm-lock.yaml

@@ -0,0 +1,792 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@fingerprintjs/fingerprintjs':
+        specifier: ^4.6.2
+        version: 4.6.2
+      vue:
+        specifier: ^3.5.17
+        version: 3.5.17
+    devDependencies:
+      '@vitejs/plugin-vue':
+        specifier: ^6.0.0
+        version: 6.0.0([email protected])([email protected])
+      vite:
+        specifier: ^7.0.0
+        version: 7.0.5
+
+packages:
+
+  '@babel/[email protected]':
+    resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/[email protected]':
+    resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/[email protected]':
+    resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/[email protected]':
+    resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [netbsd]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/[email protected]':
+    resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
+  '@fingerprintjs/[email protected]':
+    resolution: {integrity: sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw==}
+
+  '@jridgewell/[email protected]':
+    resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
+
+  '@rolldown/[email protected]':
+    resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==}
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==}
+    cpu: [arm]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==}
+    cpu: [arm]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==}
+    cpu: [arm64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==}
+    cpu: [arm64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==}
+    cpu: [loong64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==}
+    cpu: [s390x]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==}
+    cpu: [x64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==}
+    cpu: [x64]
+    os: [linux]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/[email protected]':
+    resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==}
+    cpu: [x64]
+    os: [win32]
+
+  '@types/[email protected]':
+    resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+  '@vitejs/[email protected]':
+    resolution: {integrity: sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+      vue: ^3.2.25
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==}
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==}
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==}
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==}
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==}
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==}
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==}
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==}
+    peerDependencies:
+      vue: 3.5.17
+
+  '@vue/[email protected]':
+    resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==}
+
+  [email protected]:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  [email protected]:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  [email protected]:
+    resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  [email protected]:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  [email protected]:
+    resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
+    peerDependencies:
+      picomatch: ^3 || ^4
+    peerDependenciesMeta:
+      picomatch:
+        optional: true
+
+  [email protected]:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
+  [email protected]:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+  [email protected]:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  [email protected]:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  [email protected]:
+    resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+    engines: {node: '>=12'}
+
+  [email protected]:
+    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  [email protected]:
+    resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  [email protected]:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  [email protected]:
+    resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
+    engines: {node: '>=12.0.0'}
+
+  [email protected]:
+    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+  [email protected]:
+    resolution: {integrity: sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^20.19.0 || >=22.12.0
+      jiti: '>=1.21.0'
+      less: ^4.0.0
+      lightningcss: ^1.21.0
+      sass: ^1.70.0
+      sass-embedded: ^1.70.0
+      stylus: '>=0.54.8'
+      sugarss: ^5.0.0
+      terser: ^5.16.0
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      jiti:
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+  [email protected]:
+    resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+snapshots:
+
+  '@babel/[email protected]': {}
+
+  '@babel/[email protected]': {}
+
+  '@babel/[email protected]':
+    dependencies:
+      '@babel/types': 7.28.1
+
+  '@babel/[email protected]':
+    dependencies:
+      '@babel/helper-string-parser': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@esbuild/[email protected]':
+    optional: true
+
+  '@fingerprintjs/[email protected]':
+    dependencies:
+      tslib: 2.8.1
+
+  '@jridgewell/[email protected]': {}
+
+  '@rolldown/[email protected]': {}
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@rollup/[email protected]':
+    optional: true
+
+  '@types/[email protected]': {}
+
+  '@vitejs/[email protected]([email protected])([email protected])':
+    dependencies:
+      '@rolldown/pluginutils': 1.0.0-beta.19
+      vite: 7.0.5
+      vue: 3.5.17
+
+  '@vue/[email protected]':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/shared': 3.5.17
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.1
+
+  '@vue/[email protected]':
+    dependencies:
+      '@vue/compiler-core': 3.5.17
+      '@vue/shared': 3.5.17
+
+  '@vue/[email protected]':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/compiler-core': 3.5.17
+      '@vue/compiler-dom': 3.5.17
+      '@vue/compiler-ssr': 3.5.17
+      '@vue/shared': 3.5.17
+      estree-walker: 2.0.2
+      magic-string: 0.30.17
+      postcss: 8.5.6
+      source-map-js: 1.2.1
+
+  '@vue/[email protected]':
+    dependencies:
+      '@vue/compiler-dom': 3.5.17
+      '@vue/shared': 3.5.17
+
+  '@vue/[email protected]':
+    dependencies:
+      '@vue/shared': 3.5.17
+
+  '@vue/[email protected]':
+    dependencies:
+      '@vue/reactivity': 3.5.17
+      '@vue/shared': 3.5.17
+
+  '@vue/[email protected]':
+    dependencies:
+      '@vue/reactivity': 3.5.17
+      '@vue/runtime-core': 3.5.17
+      '@vue/shared': 3.5.17
+      csstype: 3.1.3
+
+  '@vue/[email protected]([email protected])':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.17
+      '@vue/shared': 3.5.17
+      vue: 3.5.17
+
+  '@vue/[email protected]': {}
+
+  [email protected]: {}
+
+  [email protected]: {}
+
+  [email protected]:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.25.6
+      '@esbuild/android-arm': 0.25.6
+      '@esbuild/android-arm64': 0.25.6
+      '@esbuild/android-x64': 0.25.6
+      '@esbuild/darwin-arm64': 0.25.6
+      '@esbuild/darwin-x64': 0.25.6
+      '@esbuild/freebsd-arm64': 0.25.6
+      '@esbuild/freebsd-x64': 0.25.6
+      '@esbuild/linux-arm': 0.25.6
+      '@esbuild/linux-arm64': 0.25.6
+      '@esbuild/linux-ia32': 0.25.6
+      '@esbuild/linux-loong64': 0.25.6
+      '@esbuild/linux-mips64el': 0.25.6
+      '@esbuild/linux-ppc64': 0.25.6
+      '@esbuild/linux-riscv64': 0.25.6
+      '@esbuild/linux-s390x': 0.25.6
+      '@esbuild/linux-x64': 0.25.6
+      '@esbuild/netbsd-arm64': 0.25.6
+      '@esbuild/netbsd-x64': 0.25.6
+      '@esbuild/openbsd-arm64': 0.25.6
+      '@esbuild/openbsd-x64': 0.25.6
+      '@esbuild/openharmony-arm64': 0.25.6
+      '@esbuild/sunos-x64': 0.25.6
+      '@esbuild/win32-arm64': 0.25.6
+      '@esbuild/win32-ia32': 0.25.6
+      '@esbuild/win32-x64': 0.25.6
+
+  [email protected]: {}
+
+  [email protected]([email protected]):
+    optionalDependencies:
+      picomatch: 4.0.3
+
+  [email protected]:
+    optional: true
+
+  [email protected]:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.4
+
+  [email protected]: {}
+
+  [email protected]: {}
+
+  [email protected]: {}
+
+  [email protected]:
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  [email protected]:
+    dependencies:
+      '@types/estree': 1.0.8
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.45.1
+      '@rollup/rollup-android-arm64': 4.45.1
+      '@rollup/rollup-darwin-arm64': 4.45.1
+      '@rollup/rollup-darwin-x64': 4.45.1
+      '@rollup/rollup-freebsd-arm64': 4.45.1
+      '@rollup/rollup-freebsd-x64': 4.45.1
+      '@rollup/rollup-linux-arm-gnueabihf': 4.45.1
+      '@rollup/rollup-linux-arm-musleabihf': 4.45.1
+      '@rollup/rollup-linux-arm64-gnu': 4.45.1
+      '@rollup/rollup-linux-arm64-musl': 4.45.1
+      '@rollup/rollup-linux-loongarch64-gnu': 4.45.1
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1
+      '@rollup/rollup-linux-riscv64-gnu': 4.45.1
+      '@rollup/rollup-linux-riscv64-musl': 4.45.1
+      '@rollup/rollup-linux-s390x-gnu': 4.45.1
+      '@rollup/rollup-linux-x64-gnu': 4.45.1
+      '@rollup/rollup-linux-x64-musl': 4.45.1
+      '@rollup/rollup-win32-arm64-msvc': 4.45.1
+      '@rollup/rollup-win32-ia32-msvc': 4.45.1
+      '@rollup/rollup-win32-x64-msvc': 4.45.1
+      fsevents: 2.3.3
+
+  [email protected]: {}
+
+  [email protected]:
+    dependencies:
+      fdir: 6.4.6([email protected])
+      picomatch: 4.0.3
+
+  [email protected]: {}
+
+  [email protected]:
+    dependencies:
+      esbuild: 0.25.6
+      fdir: 6.4.6([email protected])
+      picomatch: 4.0.3
+      postcss: 8.5.6
+      rollup: 4.45.1
+      tinyglobby: 0.2.14
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  [email protected]:
+    dependencies:
+      '@vue/compiler-dom': 3.5.17
+      '@vue/compiler-sfc': 3.5.17
+      '@vue/runtime-dom': 3.5.17
+      '@vue/server-renderer': 3.5.17([email protected])
+      '@vue/shared': 3.5.17

+ 55 - 0
demo-utm-vue3/src/App.vue

@@ -0,0 +1,55 @@
+
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+// import UtmTracker from 'utm-params-extractor-test'
+import '../../src/index'
+const utm = ref(null)
+let tracker = null
+
+
+onMounted(async () => {
+  tracker = new UtmTracker({
+    reportUrl: 'http://1e86b0fd.r32.cpolar.top/admin/marketing/config/report',
+    autoSend: false
+  });
+  utm.value = await tracker.getParamsAsync()
+})
+
+const utmJson = computed(() => utm.value ? JSON.stringify(utm.value, null, 2) : '正在获取...')
+
+const sendUtm = () => {
+  if (tracker) {
+    tracker.send()
+  } else {
+    console.warn('UtmTracker 尚未初始化')
+  }
+}
+</script>
+
+<template>
+  <div style="padding: 24px;">
+    <h1>UTM Params Extractor Demo (Vue3)</h1>
+    <p>本页面演示如何在 Vue3 项目中使用 <code>utm-params-extractor-test</code> 包。</p>
+    <button @click="sendUtm">发送</button>
+    <h2>获取到的参数:</h2>
+    <pre style="background: #f6f8fa; padding: 16px; border-radius: 8px; text-align: left;">
+      {{ utmJson }}
+    </pre>
+  </div>
+</template>
+
+<style scoped>
+.logo {
+  height: 6em;
+  padding: 1.5em;
+  will-change: filter;
+  transition: filter 300ms;
+}
+.logo:hover {
+  filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.vue:hover {
+  filter: drop-shadow(0 0 2em #42b883aa);
+}
+</style>

+ 29 - 10
package-lock.json

@@ -1,15 +1,16 @@
 {
-  "name": "utm-params-extractor",
-  "version": "1.0.0",
+  "name": "utm-params-extractor-test",
+  "version": "1.0.12",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "utm-params-extractor",
-      "version": "1.0.0",
+      "name": "utm-params-extractor-test",
+      "version": "1.0.12",
       "license": "MIT",
       "dependencies": {
-        "utm-params-extractor": "^1.0.0"
+        "@fingerprintjs/fingerprintjs": "^4.6.2",
+        "utm-params-extractor-test": "^1.0.0"
       },
       "devDependencies": {
         "@babel/core": "^7.27.7",
@@ -1530,6 +1531,15 @@
         "node": ">=10.0.0"
       }
     },
+    "node_modules/@fingerprintjs/fingerprintjs": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.6.2.tgz",
+      "integrity": "sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw==",
+      "license": "BUSL-1.1",
+      "dependencies": {
+        "tslib": "^2.4.1"
+      }
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.3.11",
       "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.11.tgz",
@@ -3433,6 +3443,12 @@
         }
       }
     },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
     "node_modules/undici-types": {
       "version": "7.8.0",
       "resolved": "https://mirrors.cloud.tencent.com/npm/undici-types/-/undici-types-7.8.0.tgz",
@@ -3515,11 +3531,14 @@
         "browserslist": ">= 4.21.0"
       }
     },
-    "node_modules/utm-params-extractor": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/utm-params-extractor/-/utm-params-extractor-1.0.0.tgz",
-      "integrity": "sha512-hHDp0uOuwX0iL6cgIh1XQTpDBkidZtxhqgpwBTyMr1EJCYCCWSgbg/lG97wKLFyWs1qkzMVenww/1QxTdpcHBQ==",
-      "license": "MIT"
+    "node_modules/utm-params-extractor-test": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/utm-params-extractor-test/-/utm-params-extractor-test-1.0.12.tgz",
+      "integrity": "sha512-LedkLpHrGvxSSvS0xd4ValR9k31rLCie9Ywo0P0B9cHyf2dtT0prfkublM7L5c5J5eqAdnuNNWqq7y/dDvfRww==",
+      "license": "MIT",
+      "dependencies": {
+        "utm-params-extractor-test": "^1.0.0"
+      }
     },
     "node_modules/watchpack": {
       "version": "2.4.4",

+ 2 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "utm-params-extractor-test",
-  "version": "1.0.10",
+  "version": "1.0.12",
   "description": "Extract UTM parameters from URL and browser information",
   "main": "dist/main.js",
   "scripts": {
@@ -28,6 +28,7 @@
   "author": "Jiachen",
   "license": "MIT",
   "dependencies": {
+    "@fingerprintjs/fingerprintjs": "^4.6.2",
     "utm-params-extractor-test": "^1.0.0"
   },
   "types": "index.d.ts"

+ 5 - 5
pnpm-lock.yaml

@@ -8,9 +8,9 @@ importers:
 
   .:
     dependencies:
-      utm-params-extractor:
+      utm-params-extractor-test:
         specifier: ^1.0.0
-        version: 1.0.2
+        version: 1.0.12
     devDependencies:
       '@babel/core':
         specifier: ^7.27.7
@@ -1180,8 +1180,8 @@ packages:
     peerDependencies:
       browserslist: '>= 4.21.0'
 
-  [email protected]:
-    resolution: {integrity: sha512-lIix4mFjQItTn3uTOXpBH2B2R8sM5Xa8lytaO9cbOFHWTt7DIAsh6A2MnUugfQv2plIzZu2uK5j3cKoHkMHGCg==}
+  utm-params-extractor-test@1.0.12:
+    resolution: {integrity: sha512-LedkLpHrGvxSSvS0xd4ValR9k31rLCie9Ywo0P0B9cHyf2dtT0prfkublM7L5c5J5eqAdnuNNWqq7y/dDvfRww==}
 
   [email protected]:
     resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==}
@@ -2495,7 +2495,7 @@ snapshots:
       escalade: 3.2.0
       picocolors: 1.1.1
 
-  [email protected]: {}
+  utm-params-extractor-test@1.0.12: {}
 
   [email protected]:
     dependencies:

+ 78 - 18
src/index.js

@@ -1,4 +1,6 @@
 // index.js
+import FingerprintJS from '@fingerprintjs/fingerprintjs';
+
 (function (factory) {
     if (typeof define === 'function' && define.amd) {
         // AMD
@@ -122,9 +124,17 @@
         }
     }
 
-    // 获取UTM参数
+    // 集成 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') || '',
@@ -132,8 +142,10 @@
             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,
@@ -151,12 +163,47 @@
         return result;
     };
 
-    // 发送请求
-    UtmTracker.prototype.send = function (data) {
+    // 获取指纹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 || {};
-        const payload = data || this.getParams();
-        console.log(cfg);
-        
+        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',
@@ -167,10 +214,8 @@
                 // 假设res为Response对象,需要解析json
                 if (typeof res.json === 'function') {
                     res.json().then(data => {
-                        console.log(data);
-
-                        if (data.data.downloadUrl) {
-                            console.log(111);
+                        if (data.code == 1) return
+                        if (data.data && data.data.downloadUrl) {
 
                             if (data.data.type === 'link') {
                                 // 倒计时弹窗
@@ -303,20 +348,35 @@
     };
 
     // 静态方法:快速获取(无需实例化)
-    UtmTracker.get = function (config) {
+    UtmTracker.get = async function (config) {
         const tracker = new UtmTracker(config);
-        const params = tracker.getParams();
-        if (config && config.autoSend && config.reportUrl) {
-            tracker.send(params);
-        }
-        return params;
+
+        return await tracker.getParamsAsync();
     };
 
     // 新增静态send方法
-    UtmTracker.send = function (data, config) {
+    UtmTracker.send = async function (data, config) {
         const tracker = new UtmTracker(config);
-        return tracker.send(data);
+        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;
 }));