From 469b4074d7d8132faf17cf4bd32932a268bafe8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E5=AE=87=E5=AE=81?= Date: Fri, 13 Nov 2026 14:20:14 +0700 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E4=B8=8B=E8=BD=BD=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/landing-hg2/Mobile.vue | 4 +- src/utils/useAppDownload.ts | 68 ++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/components/landing-hg2/Mobile.vue b/src/components/landing-hg2/Mobile.vue index d2ae57f..774213c 100644 --- a/src/components/landing-hg2/Mobile.vue +++ b/src/components/landing-hg2/Mobile.vue @@ -214,7 +214,7 @@ const menus = [ { key: 'about', label: '关于我们' }, ] -const { isIos, hasIosStore, download, init } = useAppDownload() +const { isIos, hasIosStore, download, init, autoDownloadChannelApk } = useAppDownload() const scroller = ref(null) const header = ref(null) @@ -361,6 +361,8 @@ function doCopy() { onMounted(() => { init() + // 渠道包:安卓端进页面 3 秒后自动下载 APK(无 channelApkAndDownUrl 时不触发) + autoDownloadChannelApk() centerNav() nextTick(() => { centerNav() diff --git a/src/utils/useAppDownload.ts b/src/utils/useAppDownload.ts index 81a3e4f..49a67b9 100644 --- a/src/utils/useAppDownload.ts +++ b/src/utils/useAppDownload.ts @@ -5,6 +5,7 @@ * - 点下载:bottonclick 返回 data.link(地址数组),跳 link[index];同一请求后端记点击 * - iOS 轻量版:点击直接调 h5/config 下载描述文件(不看开关);社媒内置浏览器装不了描述文件,只弹「复制到 Safari」引导 * - iOS 商店版:iosStore(packageType 2) 走 bottonclick 拿 link + * - 渠道包自动下载:bottonlist 返回 channelApkAndDownUrl 时,安卓端进页面 3 秒后自动拉起该 APK * - 下载前把 { ...query, tid } 写入剪贴板供 App 首启归因 * * 组件接入: @@ -30,6 +31,8 @@ export interface ButtonList { iosLink?: string androidButton?: number hasChannelApk?: boolean + // 渠道包地址:有值说明该渠道有专属 APK,安卓端进页面自动下载 + channelApkAndDownUrl?: string [key: string]: unknown } @@ -42,6 +45,10 @@ const ip = ref('') const ready = ref(false) let initPromise: Promise | null = null +// 渠道包自动下载:延迟时长(从进页面算起)+ 单页生命周期内只触发一次 +const AUTO_DOWNLOAD_DELAY = 3000 +let autoDownloadFired = false + const isIos = /iphone|ipad|ipod/i.test(navigator.userAgent) const isWeiXin = /MicroMessenger/i.test(navigator.userAgent) @@ -151,15 +158,59 @@ export function useAppDownload() { return true } + function buildParam(terminal: Terminal): donwloadParam { + return { + appId: APP_ID, + ip: ip.value, + channel: filterChannel(route.query), + domain: window.location.host || '', + // bili 默认值,安卓请求同样带 iosH5 + type: 'iosH5', + terminal, + } + } + + // 下载前写剪贴板,供 App 首次启动读取做安装归因(整包 URL 参数 + tid) + function writeAttribution() { + const trace_id = renewSessionTraceId() + try { + copyToClipboard(JSON.stringify({ ...route.query, tid: trace_id })) + } catch (err) { + console.log('写入归因剪贴板失败' + err) + } + } + + /** + * 渠道包自动下载:bottonlist 返回 channelApkAndDownUrl 有值时, + * 安卓端进页面 delay 毫秒后自动拉起该 APK(整个页面生命周期只触发一次)。 + * iOS 装不了 APK、社媒内置浏览器会被拦截跳出,这两种情况都跳过,交给用户手动点。 + */ + function autoDownloadChannelApk(delay = AUTO_DOWNLOAD_DELAY) { + if (autoDownloadFired || isIos || isSocialInAppBrowser()) return + autoDownloadFired = true + // 计时与拉接口并行:3 秒从进页面算起,接口慢就等接口回来再下 + const waiting = new Promise((resolve) => setTimeout(resolve, delay)) + Promise.all([init(), waiting]) + .then(() => { + const url = String(buttonList.value.channelApkAndDownUrl || '').trim() + if (!url) return + writeAttribution() + // 自动下载同样记一次点击,统计口径与手动点下载一致 + bottonclick(buildParam('android')).catch((err) => + console.log('自动下载埋点失败' + JSON.stringify(err)), + ) + jumpToLink(url) + }) + .catch((err) => console.log('渠道包自动下载失败' + err)) + } + /** * 点击下载,返回是否拿到并开始下载(false 时页面可提示稍后重试) * @param index iOS:0 = 轻量版,1 = 商店包;Android:下载地址下标 * @param terminal 默认按 UA;PC 端弹窗里的 iOS / Android 按钮显式传入 */ async function download(index = 0, terminal: Terminal = isIos ? 'ios' : 'android') { - const trace_id = renewSessionTraceId() - // 写剪贴板供 App 首次启动读取做安装归因:整包 URL 参数 + tid(黄果 App 按 tid 读) - copyToClipboard(JSON.stringify({ ...route.query, tid: trace_id })) + writeAttribution() if (terminal === 'ios' && index !== 1) { try { @@ -171,15 +222,7 @@ export function useAppDownload() { } await init() - const param: donwloadParam = { - appId: APP_ID, - ip: ip.value, - channel: filterChannel(route.query), - domain: window.location.host || '', - // bili 默认值,安卓请求同样带 iosH5 - type: 'iosH5', - terminal, - } + const param = buildParam(terminal) try { return terminal === 'ios' ? await downloadIosStore(param) @@ -205,5 +248,6 @@ export function useAppDownload() { iosInstallUrl, init, download, + autoDownloadChannelApk, } }