“mitao”
2026-04-27 c4ea08aedbcb4f43467f7ff84d6751305af7e39b
fix:分享页自动打开APP修复
1个文件已修改
107 ■■■■■ 已修改文件
tcm-training-admin-backend/src/main/resources/static/app-download/index.html 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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 继续注册'
    }