@@ -36,11 +36,13 @@ import type {
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
guideTopTabUrl,
|
||||
isGuideTopTab,
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment'
|
||||
import { explainGuideStopListUrl } from '@/utils/explainNavigation'
|
||||
import { returnToWechatMiniProgram } from '@/services/WechatMiniProgramBridgeService'
|
||||
|
||||
const explainHallItems = ref<ExplainHallSelectItem[]>([])
|
||||
const explainLoading = ref(false)
|
||||
@@ -71,8 +73,6 @@ const buildExplainHallItems = (
|
||||
|
||||
const shouldUseHostNavigation = computed(() => isEmbeddedInWechatMiniProgram())
|
||||
|
||||
const GUIDE_HOME_URL = '/pages/index/index?tab=guide'
|
||||
|
||||
type ExplainListHistoryState = {
|
||||
museumGuidePage?: 'explain-list'
|
||||
[key: string]: unknown
|
||||
@@ -93,9 +93,7 @@ const getPageStack = () => (
|
||||
typeof getCurrentPages === 'function' ? getCurrentPages() as PageStackEntry[] : []
|
||||
)
|
||||
|
||||
const guideHomeUrl = () => (
|
||||
shouldUseHostNavigation.value ? guideTopTabUrl('guide') : GUIDE_HOME_URL
|
||||
)
|
||||
const guideHomeUrl = (tab: GuideTopTab = 'guide') => guideTopTabUrl(tab)
|
||||
|
||||
const isExplainListHistoryState = (state: unknown): state is ExplainListHistoryState => (
|
||||
Boolean(
|
||||
@@ -120,6 +118,25 @@ const reLaunchGuideHome = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const reLaunchPreviousGuideHome = (tab: GuideTopTab) => {
|
||||
uni.reLaunch({
|
||||
url: guideHomeUrl(tab),
|
||||
fail: showGuideHomeReturnError
|
||||
})
|
||||
}
|
||||
|
||||
const returnToHostMiniProgram = async () => {
|
||||
isReturningToGuideHome = true
|
||||
const result = await returnToWechatMiniProgram()
|
||||
if (result.status === 'returned') return
|
||||
|
||||
isReturningToGuideHome = false
|
||||
uni.showToast({
|
||||
title: '返回小程序失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const consumeExplainListHistory = () => {
|
||||
if (
|
||||
typeof window === 'undefined'
|
||||
@@ -144,25 +161,27 @@ const returnToGuideHome = () => {
|
||||
const previousTab = Array.isArray(previousPage?.options?.tab)
|
||||
? previousPage.options.tab[0]
|
||||
: previousPage?.options?.tab
|
||||
const canNavigateBackToGuideHome = (
|
||||
!shouldUseHostNavigation.value
|
||||
&& previousRoute === 'pages/index/index'
|
||||
&& (!previousTab || previousTab === 'guide')
|
||||
)
|
||||
const canNavigateBackToGuideHome = previousRoute === 'pages/index/index'
|
||||
const previousGuideHomeTab = isGuideTopTab(previousTab) ? previousTab : 'guide'
|
||||
|
||||
if (canNavigateBackToGuideHome) {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
fail: reLaunchGuideHome
|
||||
fail: () => reLaunchPreviousGuideHome(previousGuideHomeTab)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (shouldUseHostNavigation.value) {
|
||||
void returnToHostMiniProgram()
|
||||
return
|
||||
}
|
||||
|
||||
reLaunchGuideHome()
|
||||
}
|
||||
|
||||
const pushExplainListHistory = () => {
|
||||
if (typeof window === 'undefined' || getPageStack().length > 1) return
|
||||
if (typeof window === 'undefined' || shouldUseHostNavigation.value || getPageStack().length > 1) return
|
||||
if (explainListHistoryPushed) return
|
||||
|
||||
if (isExplainListHistoryState(window.history.state)) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
interface MiniProgramBridge {
|
||||
getEnv?: (callback: (result: { miniprogram?: boolean }) => void) => void
|
||||
navigateTo?: (options: { url: string; success?: () => void; fail?: (error: { errMsg?: string }) => void }) => void
|
||||
navigateBack?: (options: { delta?: number; success?: () => void; fail?: (error: { errMsg?: string }) => void }) => void
|
||||
}
|
||||
|
||||
export type WechatMiniProgramBridgeResult =
|
||||
@@ -15,6 +16,11 @@ export type WechatMiniProgramBridgeResult =
|
||||
| { status: 'not-embedded' }
|
||||
| { status: 'failed'; reason: 'invalid-target' | 'bridge-unavailable' | 'not-in-mini-program' | 'navigation-failed' | 'page-not-found' | 'timeout' }
|
||||
|
||||
export type WechatMiniProgramReturnResult =
|
||||
| { status: 'returned' }
|
||||
| { status: 'not-embedded' }
|
||||
| { status: 'failed'; reason: 'bridge-unavailable' | 'not-in-mini-program' | 'navigation-failed' | 'timeout' }
|
||||
|
||||
const SDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.3.2.js'
|
||||
const BRIDGE_WAIT_TIMEOUT = 2500
|
||||
const BRIDGE_POLL_INTERVAL = 50
|
||||
@@ -22,6 +28,7 @@ const GET_ENV_TIMEOUT = 1500
|
||||
const NAVIGATION_TIMEOUT = 3000
|
||||
|
||||
let pendingNavigation: Promise<WechatMiniProgramBridgeResult> | null = null
|
||||
let pendingReturn: Promise<WechatMiniProgramReturnResult> | null = null
|
||||
let sdkLoadPromise: Promise<void> | null = null
|
||||
|
||||
const getBridge = (): MiniProgramBridge | null => {
|
||||
@@ -108,6 +115,31 @@ const navigateToHostLocationPage = (bridge: MiniProgramBridge, target: WechatOpe
|
||||
}
|
||||
})
|
||||
|
||||
const navigateBackToHostPage = (bridge: MiniProgramBridge) => new Promise<WechatMiniProgramReturnResult>((resolve) => {
|
||||
if (!bridge.navigateBack) return resolve({ status: 'failed', reason: 'bridge-unavailable' })
|
||||
let settled = false
|
||||
const finish = (result: WechatMiniProgramReturnResult) => {
|
||||
if (settled) return
|
||||
settled = true
|
||||
clearTimeout(timeout)
|
||||
resolve(result)
|
||||
}
|
||||
const timeout = setTimeout(() => finish({ status: 'failed', reason: 'timeout' }), NAVIGATION_TIMEOUT)
|
||||
try {
|
||||
bridge.navigateBack({
|
||||
delta: 1,
|
||||
success: () => finish({ status: 'returned' }),
|
||||
fail: (error) => {
|
||||
console.error('返回微信小程序失败:', error)
|
||||
finish({ status: 'failed', reason: 'navigation-failed' })
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('调用微信小程序返回桥接失败:', error)
|
||||
finish({ status: 'failed', reason: 'navigation-failed' })
|
||||
}
|
||||
})
|
||||
|
||||
const openOnce = async (target: WechatOpenLocationTarget): Promise<WechatMiniProgramBridgeResult> => {
|
||||
const validation = validateWechatOpenLocationTarget(target)
|
||||
if (!validation.ok) return { status: 'failed', reason: 'invalid-target' }
|
||||
@@ -118,8 +150,22 @@ const openOnce = async (target: WechatOpenLocationTarget): Promise<WechatMiniPro
|
||||
return navigateToHostLocationPage(bridge, validation.value)
|
||||
}
|
||||
|
||||
const returnOnce = async (): Promise<WechatMiniProgramReturnResult> => {
|
||||
if (!isEmbeddedInWechatMiniProgram()) return { status: 'not-embedded' }
|
||||
const bridge = await waitForBridge()
|
||||
if (!bridge) return { status: 'failed', reason: 'bridge-unavailable' }
|
||||
if (!await isMiniProgramWebView(bridge)) return { status: 'failed', reason: 'not-in-mini-program' }
|
||||
return navigateBackToHostPage(bridge)
|
||||
}
|
||||
|
||||
export const openWechatMiniProgramLocation = (target: WechatOpenLocationTarget) => {
|
||||
if (pendingNavigation) return pendingNavigation
|
||||
pendingNavigation = openOnce(target).finally(() => { pendingNavigation = null })
|
||||
return pendingNavigation
|
||||
}
|
||||
|
||||
export const returnToWechatMiniProgram = () => {
|
||||
if (pendingReturn) return pendingReturn
|
||||
pendingReturn = returnOnce().finally(() => { pendingReturn = null })
|
||||
return pendingReturn
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user