diff --git a/project.md b/project.md
index 3c4803e..cead6b7 100644
--- a/project.md
+++ b/project.md
@@ -42,7 +42,7 @@ DMP编码: JHA-214 # 无则留空
备注:
- `package.json` 中 `name` 为 `landpage`,仓库/目录名为 `91dj-landpage-client`。
- - 业务 `appId` 为 **204**(见 `src/views/land/index.vue`),埋点 `appId` 为 **JHA-204**(见 `public/index.html` 与 `src/utils/eventTracker.js`)。
+ - 下载接口 `appId` 为 **5**(见 `src/api/btn.js`),已无埋点。
- 接口路径前缀为 **`/plm`**,开发环境由 `vue.config.js` 代理;选线候选域名见各环境 `.env.*` 中的 `VUE_APP_BASE_HOSTS`(JSON 数组),勿将含密钥、内网地址的未脱敏内容对外发布。
---
@@ -60,14 +60,13 @@ DMP编码: JHA-214 # 无则留空
| 特定 PC URL | `tips.vue` |
| 接口失败选线 | `selectLinePopup.vue` |
-子组件通过 props 接收 `os`、`configs`、`switchData` 等,通过 `$emit` 向上传递 `getApkInfo`、`recordClick` 等事件。
+子组件通过 props 接收 `os`、`switchData` 等,通过 `$emit` 向上传递 `getApkInfo` 事件。
**启动流程(`land/index.vue`):**
1. 解析 URL 查询参数(`dc`、`ch`、`pc` 等)并写入 sessionStorage。
-2. 调用 `getTemplate`(`/plm/okn/ijb/nuS0EDXSczKgf3Xh`)拉取按钮/下载配置(`switchData`、`configs`、各平台下载链接)。
-3. 上报落地页展示埋点 `landingPageView`(`eventTracker.js` → `window.tracker` / LandingSDK)。
-4. 用户点击下载时:`recordClick` 上报点击并调用 `getApkInfo`;`handleGetApkInfo` 根据终端类型跳转 iOS/Android 下载链接。
+2. 获取外网 IP 后调用 `bottonlist`(`https://blsp01.com/api/stat/version/bottonlist`)拉取按钮开关(`switchData`,不含下载地址)。
+3. 用户点击下载时 `handleGetApkInfo`:iOS 轻量版调 `h5/config` 下载描述文件;iOS 商店版(`iosStore`)与 Android 调 `bottonclick` 拿 `data.link` 跳转,后端同时记录点击。
5. 接口失败时弹出 **选线弹窗**,用户选择后切换 `axios.defaults.baseURL` 并重试。
**技术栈要点:**
@@ -76,7 +75,6 @@ DMP编码: JHA-214 # 无则留空
- 样式:**Sass/SCSS**(`node-sass` + `sass-loader`),`element-variables.scss` 定制 Element 主题。
- 适配:**lib-flexible** + **postcss-plugin-px2rem**(`rootValue: 37.5`,设计稿基准 375px);带 **`pcm`** 选择器前缀的样式不转 rem(PC 端)。
- 网络:**axios** 封装于 `src/utils/request.js`,默认 `baseURL: '/'`,业务码 `200` 视为成功。
-- 埋点:`public/index.html` 异步加载 **`/sdk/landing-sdk-v1.1.1.js`**(LandingSDK),`src/utils/eventTracker.js` 封装 `landing_page_view` / `landing_page_click` 事件及设备信息解析。
- 其他:**qrcode** 生成 PC 扫码图、**vue-clipboard2** 复制渠道参数、**moment**、**swiper**、**vue-analytics**(UA-165940626-2)、**register-service-worker**(PWA 配置于 `vue.config.js`)。
**Node 版本:** 仓库 `README.md` 建议使用 **12.18.1**。
@@ -104,7 +102,6 @@ DMP编码: JHA-214 # 无则留空
91pron-landpage-client/
├── public/
│ ├── index.html # 入口 HTML,含 LandingSDK 与 SEO meta
-│ └── sdk/landing-sdk-v1.1.1.js
├── src/
│ ├── main.js # 入口:Router、Vuex、Vant、Element、Analytics 等
│ ├── App.vue
@@ -115,8 +112,8 @@ DMP编码: JHA-214 # 无则留空
│ ├── views/land/
│ │ ├── index.vue # 落地页主入口:设备判断、选线、下载逻辑
│ │ └── components/ # mobile1 / mobile2 / pc / pc2 / tips / selectLinePopup
-│ ├── api/ # getApkInfo、getTemplate、getIp、getCustomerServiceUrl
-│ ├── utils/ # request、eventTracker、userAgent 等
+│ ├── api/ # btn(下载接口)、getIp
+│ ├── utils/ # request、traceId、userAgent 等
│ ├── plugins/ # flexible、element、scale_no
│ └── assets/ # 样式、各模板图片(mobile1/mobile2/pc/pc2)
├── vue.config.js
@@ -128,8 +125,8 @@ DMP编码: JHA-214 # 无则留空
**接口与选线:**
-- 业务接口统一走 **`/plm/okn/ijb/...`** 路径(见 `src/api/`),由网关或 devServer 反代。
-- **`VUE_APP_BASE_HOSTS`**:各环境 `.env.*` 中为 **JSON 数组字符串**,供选线弹窗展示候选 API 域名;用户选择后写入 `axios.defaults.baseURL` 并重试 `getTemplate`。
+- 下载接口直连 **`https://blsp01.com/api/stat/version`**(见 `src/api/btn.js`,接口已开 CORS)。
+- **`VUE_APP_BASE_HOSTS`**:各环境 `.env.*` 中为 **JSON 数组字符串**,供选线弹窗展示候选 API 域名;用户选择后写入 `axios.defaults.baseURL` 并重试 `bottonlist`。
- 生产部署需由 **Nginx / 网关** 将 **`/plm`** 转发至实际业务后端;History 模式需配置 **单页回退**(`try_files` → `index.html`)。
---
@@ -140,7 +137,6 @@ DMP编码: JHA-214 # 无则留空
- **业务 API**:`/plm` 路径下的配置与下载上报接口;静态部署时需保证 **`/plm` 反代** 与 HTTPS 证书策略一致。
- **选线域名**:`VUE_APP_BASE_HOSTS` 中的候选域名需可达,否则用户只能通过选线弹窗手动切换。
-- **LandingSDK**:`public/sdk/landing-sdk-v1.1.1.js`,用于埋点上报(`window.tracker` / `LandingSDK.init`)。
- **静态资源托管**:`npm run build` 产物在 **`dist/`**,由 CDN 或 Web 服务器提供。
- **第三方**:Google Analytics(`UA-165940626-2`)、可选外网 IP 查询(`api.ipify.org`,`getIp` 中,当前主流程未启用)。
@@ -173,8 +169,7 @@ DMP编码: JHA-214 # 无则留空
# 4. 风险说明
- **强依赖网关反代**:生产若 `/plm` 代理错误,**配置拉取与下载上报将不可用**;History 模式服务器配置错误会导致 **深链 404**。
-- **选线失败**:`getTemplate` 请求失败时弹出选线弹窗;若所有候选域名不可用,用户可能无法获取下载配置。
-- **埋点依赖 SDK**:`LandingSDK` 未加载完成前,`window.tracker` 使用队列缓冲;SDK 加载失败会导致埋点丢失。
+- **选线失败**:`bottonlist` 请求失败时弹出选线弹窗;若所有候选域名不可用,用户可能无法获取下载配置。
- **渠道参数**:下载与剪贴板逻辑依赖 URL 参数(`dc`、`ch`、`pc`、`tid` 等),参数缺失或格式不符可能影响跳转与统计。
- **配置敏感信息**:`.env`、选线域名、部署脚本中的主机信息 **勿提交到公开仓库**;供应链上锁定 `package-lock.json` 并定期审计依赖。
@@ -191,6 +186,5 @@ DMP编码: JHA-214 # 无则留空
- 落地页主逻辑集中在 **`src/views/land/index.vue`**;新增模板时参考 `mobile1` / `mobile2` / `pc` 组件模式,并在 `index.vue` 中注册与切换。
- 子组件规范见 **`.cursor/rules/vue-component.mdc`**;样式与 px2rem 见 **`.cursor/rules/scss-styling.mdc`**。
- 接口封装在 **`src/api/`**;HTTP 请求统一走 **`src/utils/request.js`**。
-- 埋点上报使用 **`src/utils/eventTracker.js`** 的 `landingPageView` / `landingPageClick`,勿绕过 SDK 直接上报。
- Figma 转落地页工作流见 **`.cursor/skills/figma-to-landpage/SKILL.md`**。
- **请勿将含密钥、生产内网地址的 `.env` 或未脱敏部署脚本提交至对外可见仓库**;YAML 头部元数据若与真实组织不一致,请在内部系统中核对后修改。
diff --git a/public/favicon.ico b/public/favicon.ico
index 28b642b..424f800 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/index.html b/public/index.html
index 888a869..2261800 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,86 +5,27 @@
-
-
+
-
91短剧 APP下载 | 国产大神成人短剧平台 百万资源免费
-
-
+ 黄果漫剧 APP下载 | 百万大神短剧原创 海量资源免费
+
+
-
-
+
+
-
-
-
+
-
-
+
+
-
-
-
+
diff --git a/public/sdk/.gitkeep b/public/sdk/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/public/sdk/landing-sdk-v1.1.1.js b/public/sdk/landing-sdk-v1.1.1.js
deleted file mode 100644
index 8d9b07b..0000000
--- a/public/sdk/landing-sdk-v1.1.1.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).LandingSDK=t()}(this,function(){"use strict";if("function"!=typeof Object.keys||"function"!=typeof Date.now||"function"!=typeof Array.isArray){var e=function(){},t=function(){};t.landingPageView=e,t.landingPageClick=e;var n=function(){};if(n.init=e,n.track=t,"undefined"!=typeof globalThis)try{globalThis.LandingSDK=n}catch(e){}else"undefined"!=typeof window&&(window.LandingSDK=n);return n}function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}var i,o="_s_token_",a="_d_token_",u="_d_sig_",c="_ping_flag_",d="data-app-id",l="app-id",f="event_id",v=null,p=null,s=null,g=null,y=function(e){var t=e.toString(16);return 1===t.length?"0"+t:t},_=function(){for(var e="",t=0;t<16;t++)e+=y(Math.floor(256*Math.random()));return e},h=function(){if(null!==g)return g;try{return window.localStorage.setItem(c,"1"),window.localStorage.removeItem(c),g=!0,!0}catch(e){return g=!1,!1}},w=function(){if("undefined"==typeof navigator)return"PC";var e=navigator.userAgent||"";return/Android/i.test(e)?"Android":/iPhone|iPad|iPod/i.test(e)?"iOS":"PC"},m=function(e){if(!e)return"";for(var t=e+"device_id_secret_key_v1",n=0,r=0;r0&&void 0!==arguments[0]?arguments[0]:"")||""),n=0;n>>0).toString(16).padStart(8,"0")}(y));return function(t){if(t&&h())try{window.localStorage.setItem(e,t)}catch(e){}}(_),_}catch(e){return""}},S=function(){var e,t,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=function(){if("undefined"==typeof window)return"";if(h())try{var e=window.localStorage.getItem(a),t=window.localStorage.getItem(u);if(e&&!t){var n=_(),r=m(n);return window.localStorage.setItem(a,n),window.localStorage.setItem(u,r),n}if(e&&t){if(m(e)===t)return e;var i=_(),o=m(i);return window.localStorage.setItem(a,i),window.localStorage.setItem(u,o),i}var c=_(),d=m(c);return window.localStorage.setItem(a,c),window.localStorage.setItem(u,d),c}catch(e){}return s||(s=_()),s}(),i=function(){var e,t,n,r=(t=(e=new Date).getMonth()+1,n=e.getDate(),e.getFullYear()+"-"+(t<10?"0"+t:t)+"-"+(n<10?"0"+n:n));if("undefined"!=typeof window&&h())try{var i=window.localStorage.getItem(o);if(i)try{var a=JSON.parse(i);if(a.date===r&&a.sid)return a.sid}catch(e){}var u=_();return window.localStorage.setItem(o,JSON.stringify({sid:u,date:r})),u}catch(e){}return v&&p===r||(v=_(),p=r),v}(),c="",d="";return{app_id:n.appId||b(),uid:0,channel:n.channel||"",sid:i,client_ts:Math.floor(Date.now()/1e3),device:w(),device_id:n.deviceId||r,user_agent:"undefined"==typeof navigator?"":navigator.userAgent||"",device_brand:c,device_model:d,fp_version:"1.0.0",device_fingerprint:k(),sdk_version:"1.1.1",app_version:(e=n.appVersion,t=String(null==e?"":e).trim(),t?/^\d+\.\d+\.\d+$/.test(t)?t:("undefined"!=typeof console&&console.warn,"1.0.0"):"1.0.0")}},A=function(){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],t=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"")||(window.location.origin?window.location.origin:window.location.protocol+"//"+window.location.hostname+(window.location.port?":"+window.location.port:""));if(!n)return"";var r="/"===n.charAt(n.length-1)?n.slice(0,-1):n,i=e?"/dcserv":"",o=t?"/api/eventTracking/batchReport.json":"/api/eventTracking/report.json";return"".concat(r).concat(i).concat(o)},j=function(){for(var e={},t=0;t>6,128|63&i);else if(i>=55296&&i<=56319&&r+1=56320&&o<=57343){var a=o-56320+(i-55296<<10)+65536;n.push(240|a>>18,128|a>>12&63,128|a>>6&63,128|63&a),r+=1}else n.push(224|i>>12,128|i>>6&63,128|63&i)}else n.push(224|i>>12,128|i>>6&63,128|63&i)}var u=8*n.length;for(n.push(128);n.length%64!=56;)n.push(0);for(var c=0;c<8;c+=1)n.push(u>>>8*c&255);for(var d=function(e,t){return e<>>32-t},l=function(e,t){return((e>>>0)+(t>>>0)&4294967295)>>>0},f=function(e,t,n,r){return l(l(e,t),l(n,r))},v=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21],p=new Array(64),s=0;s<64;s+=1)p[s]=Math.floor(4294967296*Math.abs(Math.sin(s+1)))>>>0;for(var g=1732584193,y=4023233417,_=2562383102,h=271733878,w=0;w>>0}for(var S=g,A=y,j=_,I=h,O=0;O<64;O+=1){var P=void 0,T=void 0;O<16?(P=A&j|~A&I,T=O):O<32?(P=I&A|~I&j,T=(5*O+1)%16):O<48?(P=A^j^I,T=(3*O+5)%16):(P=j^(A|~I),T=7*O%16);var q=I;I=j,j=A,A=l(A,d(f(S,P,p[O],m[T]),v[O])),S=q}g=l(g,S),y=l(y,A),_=l(_,j),h=l(h,I)}var x=function(e){for(var t="",n=0;n<4;n+=1)t+=(256+(e>>>8*n&255)).toString(16).slice(1);return t};return""+x(g)+x(y)+x(_)+x(h)},O=function(e){return null===e?"__t:null__":"string"==typeof e?"__t:str__:"+e:"number"==typeof e&&isFinite(e)?"__t:num__:"+String(e):"boolean"==typeof e?"__t:bool__:"+(e?"1":"0"):Array.isArray(e)?"["+e.map(O).join(",")+"]":"object"===r(e)?"{"+Object.keys(e).sort().map(function(t){return t+":"+O(e[t])}).join(",")+"}":"__t:any__:"+String(e)},P=function(e){var t=(e=e||{}).endpoint?e.endpoint:A(e.domain,!0,e.batch)||"",n=function(e){return e=e||{},{sendPayload:function(t,n){if(n){var i,o;if(e.batch?(i=JSON.stringify(t),o="application/json"):(i=function(e){for(var t=Object.keys(e||{}),n=[],i=0;i100?E(e,t,r,"must be int in [0,100]"):{keep:!0,value:o}}if(function(e){return"page_no"===e||"click_position"===e||/_position$/.test(e)}(t)){var a=M(n);return!D(a)||a<1?E(e,t,r,"must be int >= 1"):{keep:!0,value:a}}if("amount"===t||"coin_quantity"===t||"search_result_count"===t||"click_page_x"===t||"click_page_y"===t||"screen_width"===t||"screen_height"===t||"video_duration"===t||"play_duration"===t||"page_load_time"===t){var u=M(n);return!D(u)||u<=0?E(e,t,r,"must be int > 0"):{keep:!0,value:u}}if(x[t]){var c=C(n);if(!c)return E(e,t,r,"must not be empty");var d=c.split(",").map(function(e){return e.trim()}).filter(Boolean).join(",");return d?{keep:!0,value:d}:E(e,t,r,"must not be empty")}if(function(e){return/_key$/.test(e)}(t)){var l=C(n);return!l||l.length>64||!/^[A-Za-z0-9_]+$/.test(l)?E(e,t,r,"must match key rule"):{keep:!0,value:l}}if(function(e){return/_name$/.test(e)}(t)){var f=C(n);return f?{keep:!0,value:f.slice(0,128)}:E(e,t,r,"must not be empty")}if(function(e){return/(^id$|_id$)/.test(e)}(t)){var v=C(n);return v?{keep:!0,value:v.slice(0,128)}:E(e,t,r,"must not be empty")}if(function(e){return"comment_content"===e||"keyword"===e||/_title$/.test(e)||"novel_title"===e||"video_title"===e}(t)){var p=C(n),s="comment_content"===t||"keyword"===t?500:256;return p?{keep:!0,value:p.slice(0,s)}:E(e,t,r,"must not be empty")}if(Object.prototype.hasOwnProperty.call(q,t)){var g=C(n);return q[t][g]?{keep:!0,value:g}:E(e,t,r,"must be in enum whitelist")}return"string"==typeof n?{keep:!0,value:n.trim()}:{keep:!0,value:n}},N=function(e){return null===e?"null":Array.isArray(e)?"array":r(e)},R=function(e,t,n){t=t||{};var i=(n=n||{}).allowExtra,o=void 0!==i&&i,a=t&&"object"===r(t)&&!Array.isArray(t)?t:null;if(!a)throw new TypeError("[sdk][event] "+e+" payload must be an object");for(var u=T[e]||{},c=Object.keys(a),d={},l=0;l
+ request({
+ method: "post",
+ url: `${STAT_API}/bottonlist`,
+ data: { ...data, appId: APP_ID }
+ });
+
+// 点击下载:返回 data.link(下载地址数组),同时后端记录点击
+export const bottonclick = data =>
+ request({
+ method: "post",
+ url: `${STAT_API}/bottonclick`,
+ data: { ...data, appId: APP_ID }
+ });
+
+// iOS H5 描述文件:返回 .mobileconfig 原文(非 JSON)
+export const downloadH5 = async data => {
+ const res = await fetch(`${STAT_API}/h5/config`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ ...data, appId: APP_ID })
+ });
+ if (!res.ok) throw new Error("获取描述文件失败 " + res.status);
+ return res.text();
+};
diff --git a/src/api/getApkInfo.js b/src/api/getApkInfo.js
deleted file mode 100644
index b1941ad..0000000
--- a/src/api/getApkInfo.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * @Author: 王枫叶
- * @Date: 2020-07-20 20:48:00
- * @LastEditors: 王枫叶
- * @LastEditTime: 2020-10-05 17:23:15
- */
-import request from "@/utils/request";
-
-export default data =>
- request({
- method: "post",
- url: "/plm/okn/ijb/vl0gpuBNWklJwcv1",
- data
- });
diff --git a/src/api/getCustomerServiceUrl.js b/src/api/getCustomerServiceUrl.js
deleted file mode 100644
index b130a40..0000000
--- a/src/api/getCustomerServiceUrl.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import request from "@/utils/request";
-
-export default data =>
- request({
- method: "post",
- url: "/plm/okn/ijb/fsdfrererdfdf",
- data,
- });
diff --git a/src/api/getTemplate.js b/src/api/getTemplate.js
deleted file mode 100644
index 974e87a..0000000
--- a/src/api/getTemplate.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * @Author: 王枫叶
- * @Date: 2020-08-10 22:55:30
- * @LastEditors: 王枫叶
- * @LastEditTime: 2020-10-28 22:55:14
- */
-import request from "@/utils/request";
-
-export default data =>
- request({
- method: "post",
- url: "/plm/okn/ijb/nuS0EDXSczKgf3Xh",
- data
- });
diff --git a/src/assets/images/common/hgmj_logo.png b/src/assets/images/common/hgmj_logo.png
new file mode 100644
index 0000000..090c04b
Binary files /dev/null and b/src/assets/images/common/hgmj_logo.png differ
diff --git a/src/assets/images/mobile1/bottomBg.webp b/src/assets/images/mobile1/bottomBg.webp
index 927467e..aba6eb0 100644
Binary files a/src/assets/images/mobile1/bottomBg.webp and b/src/assets/images/mobile1/bottomBg.webp differ
diff --git a/src/assets/images/mobile1/iosTip.webp b/src/assets/images/mobile1/iosTip.webp
index f8e789d..ae13dcc 100644
Binary files a/src/assets/images/mobile1/iosTip.webp and b/src/assets/images/mobile1/iosTip.webp differ
diff --git a/src/assets/images/pc/bg.png b/src/assets/images/pc/bg.png
index c85875b..5825a39 100644
Binary files a/src/assets/images/pc/bg.png and b/src/assets/images/pc/bg.png differ
diff --git a/src/utils/eventTracker.js b/src/utils/eventTracker.js
deleted file mode 100644
index b79f12b..0000000
--- a/src/utils/eventTracker.js
+++ /dev/null
@@ -1,190 +0,0 @@
-const APPID = "JHA-214";
-const APP_VERSION = "1.0.0";
-
-export function getTraceId() {
- let str = sessionStorage.getItem(`${APPID}_traceId`);
- if (str) return str;
- str =
- Math.random()
- .toString()
- .substr(2, 36) + Date.now().toString(36);
- sessionStorage.setItem(`${APPID}_traceId`, str);
- return str;
-}
-
-function getAndroidBrand(model = "") {
- const m = model.toLowerCase();
- if (!m) return "";
- if (/^(sm-|gt-|sch-|sgh-|shv-)/.test(m)) return "Samsung";
- if (/^(mix|mi |mi-|mi\d|redmi|m\d{1,2}[a-z]|poco|2\d{3}[a-z0-9]{4,})/.test(m)) return "Xiaomi";
- if (/^(pixel)/.test(m)) return "Google";
- if (/^(huawei|huawe|ana-|bla-|ele-|lya-|noh-|tas-|vog-|aln-|bra-|clt-|dub-|eml-|frl-|hma-|ine-|jef-|jkm-|jny-|lio-|mar-|mrd-|nam-|oce-|pct-|pot-|sea-|snk-|stk-|tet-|wlz-|yal-)/.test(m)) return "HUAWEI";
- if (/^(honor|ali-|bkl-|yal-|nth-|peg-|any-|are-|bkk-|cdy-|cph-|dnn-|fne-|hry-|jsn-|lld-|mov-|rne-|rea-|tfy-|tnr-|crr-|ksa-)/.test(m)) return "HONOR";
- if (/^(vivo|v\d|pd\d)/.test(m)) return "vivo";
- if (/^(oppo|cph|peh|pht|pgc|pgd|pgf|a\d{3})/.test(m)) return "OPPO";
- if (/^(oneplus|hd19|kb20|le21|gm19|cph2\d|de2\d|ne2\d|in2\d|kb2\d|le2\d)/.test(m)) return "OnePlus";
- if (/^(realme|rmx)/.test(m)) return "realme";
- if (/^(moto|motorola|xt\d)/.test(m)) return "Motorola";
- if (/^(iqoo|i2\d|v\d{4}[a-z])/.test(m)) return "iQOO";
- if (/^(nokia|ta-\d+)/.test(m)) return "Nokia";
- if (/^(sony|xq-|g8\d|xperia)/.test(m)) return "Sony";
- if (/^(asus|zs\d|ai\d|rog)/.test(m)) return "ASUS";
- if (/^(meizu|m\d+ note)/.test(m)) return "Meizu";
- if (/^(nubia|nx\d)/.test(m)) return "nubia";
- if (/^(zte|zte-)/.test(m)) return "ZTE";
- if (/^(lenovo|tb-|l3\d)/.test(m)) return "Lenovo";
- return "";
-}
-
-function extractMobileModel(ua = "") {
- const sectionMatch = ua.match(
- /\(([^)]*(Android|HarmonyOS|OpenHarmony)[^)]*)\)/i
- );
- if (!sectionMatch || !sectionMatch[1]) return "";
- const candidates = sectionMatch[1]
- .split(";")
- .map(item => item.trim())
- .map(item => item.replace(/\s+Build\/.*$/i, "").trim())
- .filter(
- item =>
- item &&
- !/^linux$/i.test(item) &&
- !/^u$/i.test(item) &&
- !/^wv$/i.test(item) &&
- !/^android[\d.\s]*$/i.test(item) &&
- !/^[a-z]{2}([-_][a-z]{2})?$/i.test(item)
- );
- return candidates.length ? candidates[candidates.length - 1] : "";
-}
-
-function getBrandFromUA(model = "", ua = "") {
- const brand = getAndroidBrand(model);
- if (brand) return brand;
- if (/honor/i.test(ua)) return "HONOR";
- if (/(huawei|huaweibrowser)/i.test(ua)) return "HUAWEI";
- if (/\bxiaomi\b|miuibrowser/i.test(ua)) return "Xiaomi";
- if (/\boppo\b|oppobrowser|heytap/i.test(ua)) return "OPPO";
- if (/\bvivo\b|vivobrowser/i.test(ua)) return "vivo";
- if (/\bsamsungbrowser\b/i.test(ua)) return "Samsung";
- if (/\boneplus\b/i.test(ua)) return "OnePlus";
- if (/\brealme\b/i.test(ua)) return "realme";
- if (/\bmeizu\b/i.test(ua)) return "Meizu";
- if (/\blenovo\b/i.test(ua)) return "Lenovo";
- return "";
-}
-
-function parseDeviceInfo(ua = "") {
- if (!ua) return { device_brand: "", device_model: "" };
- if (/iPhone/i.test(ua)) return { device_brand: "Apple", device_model: "iPhone" };
- if (/iPad/i.test(ua)) return { device_brand: "Apple", device_model: "iPad" };
- if (/iPod/i.test(ua)) return { device_brand: "Apple", device_model: "iPod" };
- if (/Android/i.test(ua) || /(harmonyos|openharmony|hongmeng|hmos)/i.test(ua)) {
- const model = extractMobileModel(ua);
- return { device_brand: getBrandFromUA(model, ua), device_model: model };
- }
- if (/Macintosh|Mac OS X/i.test(ua)) return { device_brand: "Apple", device_model: "Mac" };
- if (/Windows/i.test(ua)) return { device_brand: "Microsoft", device_model: "Windows PC" };
- if (/Linux/i.test(ua)) return { device_brand: "Linux", device_model: "Linux PC" };
- return { device_brand: "", device_model: "" };
-}
-
-export function getSystemType(ua = navigator.userAgent || "") {
- if (
- /Android/i.test(ua) ||
- /(harmonyos|openharmony|hongmeng|hmos)/i.test(ua)
- ) {
- return "android";
- }
- if (/iPhone|iPad|iPod/i.test(ua)) {
- return "ios";
- }
- return "pc";
-}
-
-export function getSystemVersion(ua = navigator.userAgent || "") {
- const iosMatch = ua.match(/OS (\d+(?:[_\d]+)?)/i);
- if (iosMatch && iosMatch[1]) {
- return iosMatch[1].replace(/_/g, ".");
- }
- const androidMatch = ua.match(/Android\s+([\d.]+)/i);
- if (androidMatch && androidMatch[1]) {
- return androidMatch[1];
- }
- return "";
-}
-
-export function getDeviceType(ua = navigator.userAgent || "") {
- if (/iPhone/i.test(ua)) return "iPhone";
- if (/iPad/i.test(ua)) return "iPad";
- if (/iPod/i.test(ua)) return "iPod";
- if (
- /Android/i.test(ua) ||
- /(harmonyos|openharmony|hongmeng|hmos)/i.test(ua)
- ) {
- return extractMobileModel(ua);
- }
- const { device_model } = parseDeviceInfo(ua);
- return device_model;
-}
-
-let _cachedDeviceInfo = null;
-function getDeviceInfo() {
- if (!_cachedDeviceInfo) {
- _cachedDeviceInfo = parseDeviceInfo(navigator.userAgent || "");
- }
- return _cachedDeviceInfo;
-}
-
-/**
- * 上报落地页展示
- * vm: 组件 this,必须传(读取 vm.version)
- * opts: { landing_page_id }
- */
-export function landingPageView(vm, opts = {}) {
- const { device_brand, device_model } = getDeviceInfo();
- const data = {
- event: "landing_page_view",
- landing_page_id: opts.landing_page_id || `${APPID}_${vm.version}`,
- device_brand,
- device_model,
- app_version: APP_VERSION
- };
- return window.tracker(data);
-}
-
-/**
- * 上报落地页点击
- * vm: 组件 this
- * opts: { landing_page_id, tab_key, tab_name, click_coordinates_x, click_coordinates_y,
- * click_x_percent, click_y_percent, screen_width, screen_height, trace_id }
- */
-export function landingPageClick(vm, opts = {}) {
- const { device_brand, device_model } = getDeviceInfo();
- const screenW = window.screen ? window.screen.width : opts.screen_width || 0;
- const screenH = window.screen
- ? window.screen.height
- : opts.screen_height || 0;
- const data = {
- event: "landing_page_click",
- landing_page_id: opts.landing_page_id || `${APPID}_${vm.version}`,
- tab_key: opts.tab_key || "",
- tab_name: opts.tab_name || "",
- click_coordinates_x: opts.click_coordinates_x || 0,
- click_coordinates_y: opts.click_coordinates_y || 0,
- click_x_percent:
- typeof opts.click_x_percent === "number"
- ? opts.click_x_percent
- : Math.round(((opts.click_coordinates_x || 0) / (screenW || 1)) * 100),
- click_y_percent:
- typeof opts.click_y_percent === "number"
- ? opts.click_y_percent
- : Math.round(((opts.click_coordinates_y || 0) / (screenH || 1)) * 100),
- screen_width: screenW,
- screen_height: screenH,
- trace_id: opts.trace_id || getTraceId(),
- device_brand,
- device_model,
- app_version: APP_VERSION
- };
- return window.tracker(data);
-}
diff --git a/src/utils/traceId.js b/src/utils/traceId.js
new file mode 100644
index 0000000..758371f
--- /dev/null
+++ b/src/utils/traceId.js
@@ -0,0 +1,13 @@
+// 会话 trace id:剪贴板里的 tid 供 App 首启做安装归因
+const KEY = "hgmj_traceId";
+
+export function getTraceId() {
+ let str = sessionStorage.getItem(KEY);
+ if (str) return str;
+ str =
+ Math.random()
+ .toString()
+ .substr(2, 36) + Date.now().toString(36);
+ sessionStorage.setItem(KEY, str);
+ return str;
+}
diff --git a/src/views/land/components/mobile1.vue b/src/views/land/components/mobile1.vue
index a93c177..c8dbb0d 100644
--- a/src/views/land/components/mobile1.vue
+++ b/src/views/land/components/mobile1.vue
@@ -1,35 +1,13 @@
-