diff --git a/src/components/navigation/ArrivalPanel.vue b/src/components/navigation/ArrivalPanel.vue index 803592f..799dcac 100644 --- a/src/components/navigation/ArrivalPanel.vue +++ b/src/components/navigation/ArrivalPanel.vue @@ -124,7 +124,10 @@ import { THIRD_PARTY_MAP_PROVIDERS, type ThirdPartyMapProviderOption } from '@/services/ThirdPartyMapSearchService' -import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment' +import { + isNativeWechatMiniProgram, + isWechatMiniProgramRuntime +} from '@/utils/hostEnvironment' const props = withDefaults(defineProps<{ visible?: boolean @@ -148,7 +151,7 @@ const emit = defineEmits<{ const typeOptions = computed(() => ARRIVAL_TARGET_TYPES) const mapProviders = computed(() => THIRD_PARTY_MAP_PROVIDERS) -const shouldUseMiniProgramLocation = computed(() => isEmbeddedInWechatMiniProgram()) +const shouldUseMiniProgramLocation = computed(() => isWechatMiniProgramRuntime()) const primaryActionText = computed(() => ( shouldUseMiniProgramLocation.value ? '打开地图导航' : '第三方导航' )) @@ -239,7 +242,7 @@ const startLayoutObserver = () => { }) } -const handleNavigateTap = () => { +const handleNavigateTap = async () => { if (!props.selectedTarget) { uni.showToast({ title: '请先选择点位', @@ -248,13 +251,16 @@ const handleNavigateTap = () => { return } - if (shouldUseMiniProgramLocation.value) { - void openWechatMiniProgramLocation({ - latitude: props.selectedTarget.latitude, - longitude: props.selectedTarget.longitude, - name: props.selectedTarget.title, - address: props.selectedTarget.subtitle - }) + const target = { + latitude: props.selectedTarget.latitude, + longitude: props.selectedTarget.longitude, + name: props.selectedTarget.title, + address: props.selectedTarget.subtitle + } + const isNativeMiniProgram = isNativeWechatMiniProgram() + const openedInMiniProgram = await openWechatMiniProgramLocation(target) + + if (openedInMiniProgram || isNativeMiniProgram || isWechatMiniProgramRuntime()) { return } diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index 47b2722..3fbdc27 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -117,7 +117,6 @@ class="guide-quick-actions" > ( && !isSimulatingRoute.value && (!showGuideHomeDock.value || !homeSearchExpanded.value) )) -const showArrivalQuickAction = false const activeArrivalTargets = computed(() => ARRIVAL_TARGETS_BY_TYPE[activeArrivalType.value]) diff --git a/src/pages/search/index.vue b/src/pages/search/index.vue index cf07f39..01e4e92 100644 --- a/src/pages/search/index.vue +++ b/src/pages/search/index.vue @@ -105,13 +105,17 @@ onUnmounted(() => { right: 0; top: 0; height: 210px; - background: - linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0 64px, rgba(0, 0, 0, 0.18) 65px, rgba(0, 0, 0, 0.5) 100%), - url('/static/guide/app-launch-loading.webp') center top / cover no-repeat; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0 64px, rgba(224, 225, 0, 0.2) 65px, rgba(21, 23, 19, 0.36) 100%); pointer-events: none; } /* #ifdef H5 */ +.poi-search-page::before { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0 64px, rgba(0, 0, 0, 0.18) 65px, rgba(0, 0, 0, 0.5) 100%), + url('/static/guide/app-launch-loading.webp') center top / cover no-repeat; +} + .poi-search-page { height: var(--poi-search-page-height, 100dvh); min-height: var(--poi-search-page-height, 100dvh); diff --git a/src/services/ThirdPartyMapSearchService.ts b/src/services/ThirdPartyMapSearchService.ts index 3e4eaeb..f1b3c24 100644 --- a/src/services/ThirdPartyMapSearchService.ts +++ b/src/services/ThirdPartyMapSearchService.ts @@ -1,4 +1,8 @@ -import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment' +import { openExternalNavigation } from '@/services/MapNavigationService' +import { + isEmbeddedInWechatMiniProgram, + isNativeWechatMiniProgram +} from '@/utils/hostEnvironment' export type ThirdPartyMapProvider = 'amap' | 'baidu' | 'tencent' @@ -15,6 +19,7 @@ export interface WechatMiniProgramLocationTarget { } interface WechatMiniProgramBridge { + getEnv?: (callback: (result: { miniprogram?: boolean }) => void) => void navigateTo?: (options: { url: string success?: () => void @@ -78,14 +83,16 @@ export const openThirdPartyMapSearch = ( export const buildWechatMiniProgramOpenLocationUrl = ( target: WechatMiniProgramLocationTarget ) => { - const params = new URLSearchParams({ - latitude: String(target.latitude), - longitude: String(target.longitude), - name: target.name, - address: target.address || '' - }) + const params = [ + ['latitude', String(target.latitude)], + ['longitude', String(target.longitude)], + ['name', target.name], + ['address', target.address || ''] + ] + .map(([key, value]) => `${key}=${encode(value)}`) + .join('&') - return `/pages/open-location/index?${params.toString()}` + return `/pages/open-location/index?${params}` } const getWechatMiniProgramBridge = (): WechatMiniProgramBridge | null => { @@ -123,31 +130,84 @@ const loadWechatJsSdk = () => { return wechatJsSdkLoadPromise } -export const openWechatMiniProgramLocation = async ( - target: WechatMiniProgramLocationTarget -) => { - if (!isEmbeddedInWechatMiniProgram()) return false +const isWechatBrowser = () => ( + typeof window !== 'undefined' + && /MicroMessenger/i.test(window.navigator?.userAgent || '') +) - await loadWechatJsSdk() - const miniProgram = getWechatMiniProgramBridge() - if (!miniProgram?.navigateTo) { - uni.showToast({ - title: '请在小程序内打开地图', - icon: 'none' - }) - return false +const resolveMiniProgramWebViewEnvironment = ( + miniProgram: WechatMiniProgramBridge +) => { + if (isEmbeddedInWechatMiniProgram()) return Promise.resolve(true) + const getEnv = miniProgram.getEnv + if (!getEnv) return Promise.resolve(false) + + return new Promise((resolve) => { + let settled = false + const finish = (isMiniProgram: boolean) => { + if (settled) return + settled = true + clearTimeout(timeout) + resolve(isMiniProgram) + } + const timeout = setTimeout(() => finish(false), 1500) + + try { + getEnv((result) => finish(Boolean(result.miniprogram))) + } catch (error) { + console.error('识别小程序 web-view 环境失败:', error) + finish(false) + } + }) +} + +const navigateToMiniProgramLocationPage = ( + miniProgram: WechatMiniProgramBridge, + target: WechatMiniProgramLocationTarget +) => new Promise((resolve) => { + if (!miniProgram.navigateTo) { + resolve(false) + return } miniProgram.navigateTo({ url: buildWechatMiniProgramOpenLocationUrl(target), + success: () => resolve(true), fail: (error) => { console.error('跳转小程序地图页失败:', error) uni.showToast({ title: '无法打开地图导航', icon: 'none' }) + resolve(false) } }) +}) - return true +export const openWechatMiniProgramLocation = async ( + target: WechatMiniProgramLocationTarget +) => { + if (isNativeWechatMiniProgram()) { + return openExternalNavigation(target) + } + + const synchronouslyEmbedded = isEmbeddedInWechatMiniProgram() + if (!synchronouslyEmbedded && !isWechatBrowser()) return false + + await loadWechatJsSdk() + const miniProgram = getWechatMiniProgramBridge() + if (!miniProgram?.navigateTo) { + if (synchronouslyEmbedded) { + uni.showToast({ + title: '请在小程序内打开地图', + icon: 'none' + }) + } + return false + } + + const isMiniProgramWebView = await resolveMiniProgramWebViewEnvironment(miniProgram) + if (!isMiniProgramWebView) return false + + return navigateToMiniProgramLocationPage(miniProgram, target) } diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 6b4e4f0..b6f43b0 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -1,3 +1,4 @@ +/* #ifdef H5 */ @font-face { font-family: '鸿蒙黑体'; src: url('/static/Fonts/HarmonyOS_SansSC_Thin.ttf') format('truetype'); @@ -53,6 +54,7 @@ font-style: normal; font-display: swap; } +/* #endif */ /* CSS 变量定义 */ :root { diff --git a/src/utils/hostEnvironment.ts b/src/utils/hostEnvironment.ts index d1baf47..02808a7 100644 --- a/src/utils/hostEnvironment.ts +++ b/src/utils/hostEnvironment.ts @@ -15,6 +15,16 @@ const getCurrentUrlSearchParams = () => { return params } +export const isNativeWechatMiniProgram = () => { + let isNativeRuntime = false + + // #ifdef MP-WEIXIN + isNativeRuntime = true + // #endif + + return isNativeRuntime +} + export const isEmbeddedInWechatMiniProgram = () => { if (typeof window === 'undefined') return false @@ -32,3 +42,7 @@ export const isEmbeddedInWechatMiniProgram = () => { return /miniProgram/i.test(window.navigator?.userAgent || '') } + +export const isWechatMiniProgramRuntime = () => ( + isNativeWechatMiniProgram() || isEmbeddedInWechatMiniProgram() +)