app.js 7.83 KB
/*
 * @Date: 2025-06-28 10:33:00
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2026-01-17 00:22:34
 * @FilePath: /xyxBooking-weapp/src/app.js
 * @Description: 应用入口文件
 */
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import './utils/polyfill'
import './app.less'
import { saveCurrentPagePath, hasAuth, silentAuth, navigateToAuth } from '@/utils/authRedirect'
import Taro from '@tarojs/taro'
import { refresh_offline_booking_cache, has_offline_booking_cache } from '@/composables/useOfflineBookingCache'
import { is_usable_network, get_network_type } from '@/utils/network'
import { enable_offline_booking_cache_polling } from '@/composables/useOfflineBookingCachePolling'
import { weak_network_text, get_weak_network_modal_use_cache_options } from '@/utils/uiText'

// 记录是否已展示过网络异常提示弹窗
let has_shown_network_modal = false
// 记录上一次网络是否可用,用于识别“从可用变为不可用”的场景
let last_network_usable = null

const App = createApp({
    // 对应 onLaunch
    async onLaunch(options) {
        const path = options?.path || ''
        const query = options?.query || {}

        const query_string = Object.keys(query)
            .map((key) => `${key}=${encodeURIComponent(query[key])}`)
            .join('&')
        const full_path = query_string ? `${path}?${query_string}` : path

        // 保存当前页面路径,用于授权后跳转回原页面
        if (full_path) {
            saveCurrentPagePath(full_path)
        }

        /**
         * 预加载离线预约记录数据(列表+详情)
         * - 仅在有授权且网络可用时调用
         * - 成功后将数据存储到本地缓存(key: OFFLINE_BOOKING_DATA)
         */
        const preloadBookingData = async () => {
            try {
                await refresh_offline_booking_cache()
            } catch (e) {
                console.error('Preload booking cache failed', e)
            }
        }

        /**
         * 判断是否应该跳过网络异常提示弹窗
         * - 仅在当前页面为离线预约列表/详情/核销码页时返回 true
         */

        const should_skip_network_prompt = () => {
            const pages = Taro.getCurrentPages ? Taro.getCurrentPages() : []
            const current_page = pages && pages.length ? pages[pages.length - 1] : null
            const current_route = String(current_page?.route || '')
            if (!current_route) return false
            if (current_route.includes('pages/offlineBookingList/index')) return true
            if (current_route.includes('pages/offlineBookingDetail/index')) return true
            if (current_route.includes('pages/offlineBookingCode/index')) return true
            return false
        }

        /**
         * 处理不良网络情况
         * - 仅在当前页面未跳过提示弹窗时调用
         * - 若有离线预约缓存,则展示弹窗询问是否使用缓存数据
         * - 否则展示简单提示弹窗
         */

        const handle_bad_network = async (network_type) => {
            if (has_shown_network_modal) return false
            if (should_skip_network_prompt()) return false

            const is_none_network = network_type === 'none'
            const is_weak_network = !is_usable_network(network_type)
            if (!is_weak_network) return false

            has_shown_network_modal = true

            if (has_offline_booking_cache()) {
                try {
                    const modal_res = await Taro.showModal(get_weak_network_modal_use_cache_options())
                    if (modal_res?.confirm) {
                        await Taro.reLaunch({ url: '/pages/offlineBookingList/index' })
                        return true
                    }
                } catch (e) {
                    return is_none_network
                }
            } else {
                try {
                    await Taro.showToast({ title: weak_network_text.toast_title, icon: 'none', duration: 2000 })
                } catch (e) {
                    return is_none_network
                }
            }

            return is_none_network
        }

        /**
         * 监听网络状态变化
         * - 当网络连接且有授权时,预加载离线预约记录数据
         */
        Taro.onNetworkStatusChange((res) => {
            const is_connected = res?.isConnected !== false
            const network_type = res?.networkType || 'none'
            const network_usable = is_connected && is_usable_network(network_type)

            if (network_usable) {
                has_shown_network_modal = false
                last_network_usable = true
                if (hasAuth()) preloadBookingData()
                return
            }

            const should_prompt = last_network_usable === true || last_network_usable === null
            last_network_usable = false
            if (should_prompt) {
                handle_bad_network(network_type)
            }
            return
        })

        /**
         * 处理在启动时出现的不良网络情况
         * - 当网络连接不良且有离线预约记录缓存时,提示用户是否使用缓存进入离线模式
         * - 当网络连接不良且无缓存时,提示用户网络连接不畅
         * @returns {Promise<boolean>} 如果用户选择进入离线模式,则返回 true;否则返回 false
         */
        const handle_bad_network_on_launch = async () => {
            /**
             * 避免重复提示用户
             * - 仅在首次启动时检查网络情况
             * - 如果用户已展示过提示弹窗,则直接返回 false
             */
            if (has_shown_network_modal) return false

            const network_type = await get_network_type()
            last_network_usable = is_usable_network(network_type)
            return handle_bad_network(network_type)
        }

        /**
         * 尝试在网络可用时预加载离线预约记录数据
         * - 仅在有授权时调用
         * @returns {Promise<void>}
         */
        const try_preload_when_online = () => {
            if (!hasAuth()) return
            Taro.getNetworkType({
                success: (res) => {
                    if (is_usable_network(res.networkType)) {
                        preloadBookingData()
                    }
                }
            })
        }

        /**
         * @description: 授权成功后的共用启动逻辑
         * - 尝试在网络可用时预加载离线预约数据
         * - 启动离线预约缓存轮询(会自行处理网络可用性与引用计数)
         */
        const bootstrap_after_auth = () => {
            try_preload_when_online()
            enable_offline_booking_cache_polling({ interval_ms: 2 * 1000 * 60 })
        }

        // 处理在启动时出现的不良网络情况
        const should_stop = await handle_bad_network_on_launch()
        // 如果用户选择进入离线模式,则直接返回
        if (should_stop) return

        /**
         * 尝试在有授权时预加载离线预约记录数据
         * - 若无授权,则尝试静默授权
         * - 授权成功后调用 bootstrap_after_auth 启动共用逻辑
         * - 授权失败则跳转至授权页面
         */
        if (hasAuth()) {
            bootstrap_after_auth()
            return
        }

        if (path === 'pages/auth/index') return

        try {
            // 尝试静默授权
            await silentAuth()
            // 授权成功后调用 bootstrap_after_auth 启动共用逻辑
            bootstrap_after_auth()
        } catch (error) {
            console.error('静默授权失败:', error)
            // 授权失败则跳转至授权页面
            navigateToAuth(full_path || undefined)
        }

        return
    },
    onShow() {
    },
});

App.use(createPinia())

export default App