From c4ea08aedbcb4f43467f7ff84d6751305af7e39b Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期一, 27 四月 2026 21:32:03 +0800
Subject: [PATCH] fix:分享页自动打开APP修复
---
tcm-training-admin-backend/src/main/resources/static/app-download/index.html | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 102 insertions(+), 5 deletions(-)
diff --git a/tcm-training-admin-backend/src/main/resources/static/app-download/index.html b/tcm-training-admin-backend/src/main/resources/static/app-download/index.html
index 0d73a94..63dab49 100644
--- a/tcm-training-admin-backend/src/main/resources/static/app-download/index.html
+++ b/tcm-training-admin-backend/src/main/resources/static/app-download/index.html
@@ -267,6 +267,8 @@
bindTicketLoading: false,
openAppUrl: '',
autoOpenTimer: 0,
+ resumeWatchTimer: 0,
+ openConfirmTimer: 0,
openingApp: false,
pendingResumeOpen: false
}
@@ -274,6 +276,9 @@
const PENDING_OPEN_STORAGE_KEY = 'appInvitePendingOpen'
const AUTO_DOWNLOAD_DELAY_MS = 1600
const RESUME_AUTO_OPEN_DELAY_MS = 500
+ const OPEN_CONFIRM_DELAY_MS = 1200
+ const RESUME_WATCH_INTERVAL_MS = 1200
+ const RESUME_WATCH_DURATION_MS = 15000
/**
* 页面初始化。
@@ -378,11 +383,13 @@
if (!openAppUrl) {
return
}
+ const pendingOpen = readPendingOpen()
+ const shouldOnlyOpenApp = !fromResume && pendingOpen && pendingOpen.inviteCode === state.inviteCode && pendingOpen.resumeAttempted
persistPendingOpen()
tryOpenApp(openAppUrl)
- if (fromResume) {
- clearPendingOpen()
+ if (fromResume || shouldOnlyOpenApp) {
showStatus('正在尝试打开 APP,如未打开请点击按钮重试')
+ scheduleOpenFallbackConfirm()
return
}
scheduleDownloadFallback()
@@ -459,6 +466,10 @@
document.getElementById('flowTip').textContent = '安装完成后返回本页,页面会自动打开 APP 继续注册。'
document.getElementById('primaryActionBtn').textContent = '安装完成后自动打开中...'
showStatus('未检测到 APP,正在为您下载对应安装包')
+ persistPendingOpen({
+ downloadStartedAt: Date.now(),
+ resumeAttempted: false
+ })
window.location.href = downloadUrl
}, AUTO_DOWNLOAD_DELAY_MS)
}
@@ -495,13 +506,14 @@
/**
* 记录安装后返回页面需要继续打开 APP。
*/
- function persistPendingOpen() {
+ function persistPendingOpen(extraData) {
try {
- window.localStorage.setItem(PENDING_OPEN_STORAGE_KEY, JSON.stringify({
+ const currentValue = readPendingOpen() || {}
+ window.localStorage.setItem(PENDING_OPEN_STORAGE_KEY, JSON.stringify(Object.assign({}, currentValue, {
inviteCode: state.inviteCode,
openAppUrl: state.openAppUrl,
createdAt: Date.now()
- }))
+ }, extraData || {})))
} catch (error) {
}
}
@@ -514,12 +526,19 @@
if (!pendingOpen || pendingOpen.inviteCode !== state.inviteCode || state.pendingResumeOpen) {
return
}
+ if (pendingOpen.resumeAttempted) {
+ renderInstalledOpenFallback()
+ return
+ }
state.pendingResumeOpen = true
if (pendingOpen.openAppUrl) {
state.openAppUrl = pendingOpen.openAppUrl
}
window.setTimeout(function () {
if (!document.hidden) {
+ persistPendingOpen({
+ resumeAttempted: true
+ })
void handlePrimaryAction(true)
}
state.pendingResumeOpen = false
@@ -532,12 +551,84 @@
function bindResumeAutoOpenListeners() {
window.addEventListener('pageshow', function () {
scheduleResumeAutoOpen()
+ startResumeWatch()
+ })
+ window.addEventListener('focus', function () {
+ scheduleResumeAutoOpen()
+ startResumeWatch()
})
document.addEventListener('visibilitychange', function () {
if (!document.hidden && !state.openingApp) {
scheduleResumeAutoOpen()
+ startResumeWatch()
}
})
+ }
+
+ /**
+ * 在安卓安装器返回浏览器后短时间轮询补偿事件丢失。
+ */
+ function startResumeWatch() {
+ const pendingOpen = readPendingOpen()
+ if (!pendingOpen || pendingOpen.inviteCode !== state.inviteCode || state.resumeWatchTimer) {
+ return
+ }
+ const watchStartedAt = Date.now()
+ state.resumeWatchTimer = window.setInterval(function () {
+ if (Date.now() - watchStartedAt > RESUME_WATCH_DURATION_MS) {
+ clearResumeWatch()
+ return
+ }
+ if (!document.hidden && !state.openingApp) {
+ scheduleResumeAutoOpen()
+ }
+ }, RESUME_WATCH_INTERVAL_MS)
+ }
+
+ /**
+ * 清理安卓返回检测轮询。
+ */
+ function clearResumeWatch() {
+ if (state.resumeWatchTimer) {
+ window.clearInterval(state.resumeWatchTimer)
+ state.resumeWatchTimer = 0
+ }
+ }
+
+ /**
+ * 自动拉起失败后保留用户点击兜底入口。
+ */
+ function scheduleOpenFallbackConfirm() {
+ clearOpenConfirmTimer()
+ state.openConfirmTimer = window.setTimeout(function () {
+ if (document.hidden) {
+ clearPendingOpen()
+ clearResumeWatch()
+ return
+ }
+ document.getElementById('flowTip').textContent = '若未自动打开,请点击下方按钮继续注册。'
+ document.getElementById('primaryActionBtn').textContent = '已安装,点此打开 APP'
+ showStatus('若未自动打开 APP,请点击按钮继续注册')
+ }, OPEN_CONFIRM_DELAY_MS)
+ }
+
+ /**
+ * 展示已安装后的手动打开兜底按钮态。
+ */
+ function renderInstalledOpenFallback() {
+ document.getElementById('flowTip').textContent = '安装完成后,请点击下方按钮打开 APP 继续注册。'
+ document.getElementById('primaryActionBtn').textContent = '已安装,点此打开 APP'
+ showStatus('若未自动打开 APP,请点击按钮继续注册')
+ }
+
+ /**
+ * 清理自动打开确认计时器。
+ */
+ function clearOpenConfirmTimer() {
+ if (state.openConfirmTimer) {
+ window.clearTimeout(state.openConfirmTimer)
+ state.openConfirmTimer = 0
+ }
}
/**
@@ -568,6 +659,7 @@
window.localStorage.removeItem(PENDING_OPEN_STORAGE_KEY)
} catch (error) {
}
+ clearResumeWatch()
}
/**
@@ -586,6 +678,11 @@
function updateButtonState() {
const primaryActionBtn = document.getElementById('primaryActionBtn')
primaryActionBtn.disabled = state.bindTicketLoading || (!state.androidDownloadUrl && !state.iosDownloadUrl)
+ const pendingOpen = readPendingOpen()
+ if (pendingOpen && pendingOpen.inviteCode === state.inviteCode && pendingOpen.resumeAttempted) {
+ primaryActionBtn.textContent = '已安装,点此打开 APP'
+ return
+ }
primaryActionBtn.textContent = state.bindTicketLoading ? '正在准备注册信息...' : '打开 APP 继续注册'
}
--
Gitblit v1.7.1