439
src/components/navigation/ArrivalPanel.vue
Normal file
439
src/components/navigation/ArrivalPanel.vue
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
<template>
|
||||||
|
<view
|
||||||
|
v-if="visible"
|
||||||
|
class="arrival-scrim"
|
||||||
|
@tap="emit('close')"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
v-if="visible"
|
||||||
|
class="arrival-panel"
|
||||||
|
@tap.stop
|
||||||
|
>
|
||||||
|
<view class="arrival-handle"></view>
|
||||||
|
|
||||||
|
<view class="arrival-header">
|
||||||
|
<view class="arrival-title-group">
|
||||||
|
<text class="arrival-kicker">来馆</text>
|
||||||
|
<text class="arrival-title">选择第三方地图检索目标</text>
|
||||||
|
</view>
|
||||||
|
<view class="arrival-close" @tap="emit('close')">
|
||||||
|
<text class="arrival-close-text">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="arrival-notes">
|
||||||
|
<text class="arrival-note">公交站、停车场信息以第三方地图实时结果为准</text>
|
||||||
|
<text class="arrival-note">暂未接入停车余位</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="arrival-section">
|
||||||
|
<text class="arrival-section-title">导航到场馆</text>
|
||||||
|
<view
|
||||||
|
v-for="target in venueTargets"
|
||||||
|
:key="target.id"
|
||||||
|
class="arrival-target-row"
|
||||||
|
@tap="handleTargetSelect(target)"
|
||||||
|
>
|
||||||
|
<view class="arrival-target-dot"></view>
|
||||||
|
<view class="arrival-target-copy">
|
||||||
|
<text class="arrival-target-title">{{ target.title }}</text>
|
||||||
|
<text class="arrival-target-keyword">{{ target.keyword }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="arrival-target-action">选择地图</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="arrival-section">
|
||||||
|
<text class="arrival-section-title">推荐公共交通</text>
|
||||||
|
<view
|
||||||
|
v-for="target in transitTargets"
|
||||||
|
:key="target.id"
|
||||||
|
class="arrival-target-row"
|
||||||
|
@tap="handleTargetSelect(target)"
|
||||||
|
>
|
||||||
|
<view class="arrival-target-dot"></view>
|
||||||
|
<view class="arrival-target-copy">
|
||||||
|
<text class="arrival-target-title">{{ target.title }}</text>
|
||||||
|
<text class="arrival-target-keyword">{{ target.keyword }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="arrival-target-action">选择地图</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="arrival-section">
|
||||||
|
<text class="arrival-section-title">实时查询</text>
|
||||||
|
<view
|
||||||
|
v-for="target in realtimeTargets"
|
||||||
|
:key="target.id"
|
||||||
|
class="arrival-target-row"
|
||||||
|
@tap="handleTargetSelect(target)"
|
||||||
|
>
|
||||||
|
<view class="arrival-target-dot"></view>
|
||||||
|
<view class="arrival-target-copy">
|
||||||
|
<text class="arrival-target-title">{{ target.title }}</text>
|
||||||
|
<text class="arrival-target-keyword">{{ target.keyword }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="arrival-target-action">选择地图</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
v-if="selectedTarget"
|
||||||
|
class="provider-scrim"
|
||||||
|
@tap="closeProviderSheet"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
v-if="selectedTarget"
|
||||||
|
class="provider-sheet"
|
||||||
|
@tap.stop
|
||||||
|
>
|
||||||
|
<view class="provider-header">
|
||||||
|
<view class="provider-title-group">
|
||||||
|
<text class="provider-kicker">选择地图</text>
|
||||||
|
<text class="provider-title">{{ selectedTarget.title }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="provider-close" @tap="closeProviderSheet">
|
||||||
|
<text class="provider-close-text">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-for="provider in mapProviders"
|
||||||
|
:key="provider.provider"
|
||||||
|
class="provider-row"
|
||||||
|
@tap="handleProviderSelect(provider)"
|
||||||
|
>
|
||||||
|
<view class="provider-mark">
|
||||||
|
<text class="provider-mark-text">{{ provider.label.slice(0, 1) }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="provider-label">{{ provider.label }}</text>
|
||||||
|
<text class="provider-action">打开</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import {
|
||||||
|
ARRIVAL_REALTIME_TARGETS,
|
||||||
|
ARRIVAL_TRANSIT_TARGETS,
|
||||||
|
ARRIVAL_VENUE_TARGETS,
|
||||||
|
type ArrivalSearchTarget
|
||||||
|
} from '@/data/arrivalSearchTargets'
|
||||||
|
import {
|
||||||
|
openThirdPartyMapSearch,
|
||||||
|
THIRD_PARTY_MAP_PROVIDERS,
|
||||||
|
type ThirdPartyMapProviderOption
|
||||||
|
} from '@/services/ThirdPartyMapSearchService'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
visible?: boolean
|
||||||
|
}>(), {
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
close: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const venueTargets = computed(() => ARRIVAL_VENUE_TARGETS)
|
||||||
|
const transitTargets = computed(() => ARRIVAL_TRANSIT_TARGETS)
|
||||||
|
const realtimeTargets = computed(() => ARRIVAL_REALTIME_TARGETS)
|
||||||
|
const mapProviders = computed(() => THIRD_PARTY_MAP_PROVIDERS)
|
||||||
|
const selectedTarget = ref<ArrivalSearchTarget | null>(null)
|
||||||
|
|
||||||
|
const handleTargetSelect = (target: ArrivalSearchTarget) => {
|
||||||
|
selectedTarget.value = target
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeProviderSheet = () => {
|
||||||
|
selectedTarget.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleProviderSelect = (provider: ThirdPartyMapProviderOption) => {
|
||||||
|
const target = selectedTarget.value
|
||||||
|
if (!target) return
|
||||||
|
|
||||||
|
openThirdPartyMapSearch(provider.provider, {
|
||||||
|
keyword: target.keyword,
|
||||||
|
region: target.region
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(visible) => {
|
||||||
|
if (!visible) {
|
||||||
|
selectedTarget.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.arrival-scrim {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2090;
|
||||||
|
background: rgba(0, 0, 0, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-panel {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
bottom: calc(env(safe-area-inset-bottom) + 16px);
|
||||||
|
z-index: 2100;
|
||||||
|
width: min(430px, calc(100vw - 24px));
|
||||||
|
max-height: min(72vh, 620px);
|
||||||
|
padding: 10px 14px 16px;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(31, 35, 41, 0.08);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 10px 34px rgba(31, 35, 41, 0.18);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-handle {
|
||||||
|
width: 38px;
|
||||||
|
height: 4px;
|
||||||
|
margin: 0 auto 12px;
|
||||||
|
background: #d7d9cf;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-title-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-kicker {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #696962;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-title {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 25px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #262421;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-close {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f3f3f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-close-text {
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 26px;
|
||||||
|
color: #424754;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-notes {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
background: #f5f5ed;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-note {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 17px;
|
||||||
|
color: #424754;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-section {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-section-title {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #262421;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-target-row {
|
||||||
|
min-height: 58px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-top: 1px solid #eceee7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-section-title + .arrival-target-row {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-target-dot {
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #e0e100;
|
||||||
|
border: 2px solid #262421;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-target-copy {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-target-title {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 21px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #262421;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-target-keyword {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 17px;
|
||||||
|
color: #696962;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival-target-action {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #262421;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-scrim {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2190;
|
||||||
|
background: rgba(0, 0, 0, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-sheet {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
bottom: calc(env(safe-area-inset-bottom) + 12px);
|
||||||
|
z-index: 2200;
|
||||||
|
width: min(430px, calc(100vw - 24px));
|
||||||
|
padding: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 12px 36px rgba(31, 35, 41, 0.22);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-title-group {
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-kicker {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #696962;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-title {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #262421;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-close {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #f3f3f3;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-close-text {
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 26px;
|
||||||
|
color: #424754;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-row {
|
||||||
|
min-height: 52px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 9px 0;
|
||||||
|
border-top: 1px solid #eceee7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-mark {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #262421;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-mark-text {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #e0e100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-label {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 21px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #262421;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-action {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #696962;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
89
src/data/arrivalSearchTargets.ts
Normal file
89
src/data/arrivalSearchTargets.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
export type ArrivalTargetGroup = 'venue' | 'transit' | 'realtime'
|
||||||
|
export type ArrivalRealtimeKind = 'bus' | 'parking'
|
||||||
|
|
||||||
|
export interface ArrivalSearchTarget {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
keyword: string
|
||||||
|
region: string
|
||||||
|
group: ArrivalTargetGroup
|
||||||
|
realtimeKind?: ArrivalRealtimeKind
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ARRIVAL_SEARCH_REGION = '深圳市'
|
||||||
|
|
||||||
|
export const ARRIVAL_SEARCH_TARGETS: ArrivalSearchTarget[] = [
|
||||||
|
{
|
||||||
|
id: 'shenzhen-natural-history-museum',
|
||||||
|
title: '深圳自然博物馆',
|
||||||
|
keyword: '深圳自然博物馆',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'venue'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'shabo-station-exit-d',
|
||||||
|
title: '沙壆站 D口',
|
||||||
|
keyword: '沙壆站D口',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'transit'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'natural-museum-west-station',
|
||||||
|
title: '自然博物馆西站',
|
||||||
|
keyword: '自然博物馆西站',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'transit'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'museum-bus-stop',
|
||||||
|
title: '深圳自然博物馆 公交站',
|
||||||
|
keyword: '深圳自然博物馆 公交站',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'realtime',
|
||||||
|
realtimeKind: 'bus'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'shabo-metro-bus-stop',
|
||||||
|
title: '沙壆地铁站 公交站',
|
||||||
|
keyword: '沙壆地铁站 公交站',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'realtime',
|
||||||
|
realtimeKind: 'bus'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'natural-museum-west-bus-stop',
|
||||||
|
title: '自然博物馆西 公交站',
|
||||||
|
keyword: '自然博物馆西 公交站',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'realtime',
|
||||||
|
realtimeKind: 'bus'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'museum-parking',
|
||||||
|
title: '深圳自然博物馆 停车场',
|
||||||
|
keyword: '深圳自然博物馆 停车场',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'realtime',
|
||||||
|
realtimeKind: 'parking'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pingshan-cultural-cluster-parking',
|
||||||
|
title: '坪山文化聚落 停车场',
|
||||||
|
keyword: '坪山文化聚落 停车场',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'realtime',
|
||||||
|
realtimeKind: 'parking'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yanzi-lake-area-parking',
|
||||||
|
title: '燕子湖片区 停车场',
|
||||||
|
keyword: '燕子湖片区 停车场',
|
||||||
|
region: ARRIVAL_SEARCH_REGION,
|
||||||
|
group: 'realtime',
|
||||||
|
realtimeKind: 'parking'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const ARRIVAL_VENUE_TARGETS = ARRIVAL_SEARCH_TARGETS.filter((target) => target.group === 'venue')
|
||||||
|
export const ARRIVAL_TRANSIT_TARGETS = ARRIVAL_SEARCH_TARGETS.filter((target) => target.group === 'transit')
|
||||||
|
export const ARRIVAL_REALTIME_TARGETS = ARRIVAL_SEARCH_TARGETS.filter((target) => target.group === 'realtime')
|
||||||
@@ -36,14 +36,13 @@
|
|||||||
show-floor-header
|
show-floor-header
|
||||||
:show-layer-mode-toggle="false"
|
:show-layer-mode-toggle="false"
|
||||||
:show-floor="is3DMode && !showRoutePlanner"
|
:show-floor="is3DMode && !showRoutePlanner"
|
||||||
show-zoom-controls
|
:show-zoom-controls="is3DMode"
|
||||||
zoom-controls-top="calc(100vh - 292px)"
|
zoom-controls-top="calc(100vh - 292px)"
|
||||||
:route-preview="activeRoutePreview"
|
:route-preview="activeRoutePreview"
|
||||||
:show-route="Boolean(activeRoutePreview)"
|
:show-route="Boolean(activeRoutePreview)"
|
||||||
:disable-auto-exit="disableIndoorAutoExit"
|
:disable-auto-exit="disableIndoorAutoExit"
|
||||||
:auto-switch-threshold-low="0.58"
|
:auto-switch-threshold-low="0.58"
|
||||||
:auto-switch-threshold-high="1.18"
|
:auto-switch-threshold-high="1.18"
|
||||||
:outdoor-nav-polylines="outdoorNavPolylines"
|
|
||||||
@mode-change="handleModeChange"
|
@mode-change="handleModeChange"
|
||||||
@floor-request="handleFloorRequest"
|
@floor-request="handleFloorRequest"
|
||||||
@floor-change="handleFloorChange"
|
@floor-change="handleFloorChange"
|
||||||
@@ -56,7 +55,6 @@
|
|||||||
@initial-model-ready="handleInitialModelReady"
|
@initial-model-ready="handleInitialModelReady"
|
||||||
@initial-model-failed="handleInitialModelFailed"
|
@initial-model-failed="handleInitialModelFailed"
|
||||||
@tool-click="handleIndoorToolClick"
|
@tool-click="handleIndoorToolClick"
|
||||||
@map-tap="handleMapTapForOutdoorNav"
|
|
||||||
>
|
>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<view v-if="guideOutdoorState === 'entrance' && !is3DMode" class="entrance-tip">
|
<view v-if="guideOutdoorState === 'entrance' && !is3DMode" class="entrance-tip">
|
||||||
@@ -228,25 +226,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</GuideMapShell>
|
</GuideMapShell>
|
||||||
|
|
||||||
<OutdoorNavigationPanel
|
<ArrivalPanel
|
||||||
v-if="currentTab === 'guide' && isOutdoorGuidePanelEnabled"
|
v-if="currentTab === 'guide'"
|
||||||
:visible="showOutdoorNavPanel"
|
:visible="showArrivalPanel"
|
||||||
:loading="outdoorNavLoading"
|
@close="closeArrivalPanel"
|
||||||
:route-error="outdoorNavError"
|
|
||||||
:route-distance="outdoorNavRoute?.distance ?? 0"
|
|
||||||
:route-duration="outdoorNavRoute?.duration ?? 0"
|
|
||||||
:selecting-manual-start="outdoorNavSelectingManualStart"
|
|
||||||
:start-point="outdoorNavStartPoint ? { ...outdoorNavStartPoint, name: '已选位置' } : null"
|
|
||||||
:start-mode="outdoorNavManualMode || outdoorNavStartPoint ? 'manual' : 'gps'"
|
|
||||||
@close="handleOutdoorNavClose"
|
|
||||||
@gps-start="handleOutdoorNavGpsStart"
|
|
||||||
@manual-start-request="handleOutdoorNavManualStartRequest"
|
|
||||||
@manual-start-cancel="handleOutdoorNavManualStartCancel"
|
|
||||||
@manual-start-confirm="handleOutdoorNavManualStartConfirm"
|
|
||||||
@travel-mode-change="handleOutdoorNavTravelModeChange"
|
|
||||||
@start-navigation="handleOutdoorNavStartNavigation"
|
|
||||||
@clear-route="handleOutdoorNavClearRoute"
|
|
||||||
@back="handleOutdoorNavBack"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<view v-else-if="currentTab === 'explain'" class="explain-page">
|
<view v-else-if="currentTab === 'explain'" class="explain-page">
|
||||||
@@ -308,7 +291,7 @@ import { onLoad } from '@dcloudio/uni-app'
|
|||||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||||
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
||||||
import RoutePlannerPanel from '@/components/navigation/RoutePlannerPanel.vue'
|
import RoutePlannerPanel from '@/components/navigation/RoutePlannerPanel.vue'
|
||||||
import OutdoorNavigationPanel from '@/components/navigation/OutdoorNavigationPanel.vue'
|
import ArrivalPanel from '@/components/navigation/ArrivalPanel.vue'
|
||||||
import PoiSearchPanel from '@/components/search/PoiSearchPanel.vue'
|
import PoiSearchPanel from '@/components/search/PoiSearchPanel.vue'
|
||||||
import type {
|
import type {
|
||||||
RoutePointOption
|
RoutePointOption
|
||||||
@@ -346,17 +329,6 @@ import type {
|
|||||||
MuseumFloor,
|
MuseumFloor,
|
||||||
MuseumHall
|
MuseumHall
|
||||||
} from '@/domain/museum'
|
} from '@/domain/museum'
|
||||||
import { MUSEUM_LOCATION, type TravelMode } from '@/config/museum'
|
|
||||||
import {
|
|
||||||
planWalkingRoute,
|
|
||||||
planDrivingRoute,
|
|
||||||
planTransitRoute,
|
|
||||||
type Route,
|
|
||||||
type RoutePoint
|
|
||||||
} from '@/services/tencent/TencentMapService'
|
|
||||||
import {
|
|
||||||
navigateToMuseum
|
|
||||||
} from '@/services/MapNavigationService'
|
|
||||||
|
|
||||||
type GuideIndoorView = 'overview' | 'floor' | 'multi'
|
type GuideIndoorView = 'overview' | 'floor' | 'multi'
|
||||||
type LayerDisplayMode = 'single' | 'multi'
|
type LayerDisplayMode = 'single' | 'multi'
|
||||||
@@ -566,17 +538,7 @@ const handleLaunchContinue = () => {
|
|||||||
hideLaunchOverlay()
|
hideLaunchOverlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 馆外导航相关状态
|
const showArrivalPanel = ref(false)
|
||||||
const isOutdoorGuidePanelEnabled = false
|
|
||||||
const showOutdoorNavPanel = ref(false)
|
|
||||||
const outdoorNavLoading = ref(false)
|
|
||||||
const outdoorNavError = ref('')
|
|
||||||
const outdoorNavRoute = ref<Route | null>(null)
|
|
||||||
const outdoorNavPolylines = ref<Array<{ points: Array<{ latitude: number; longitude: number }>; color: string; width: number }>>([])
|
|
||||||
const outdoorNavStartPoint = ref<{ latitude: number; longitude: number } | null>(null)
|
|
||||||
const outdoorNavTravelMode = ref<TravelMode>('walking')
|
|
||||||
const outdoorNavSelectingManualStart = ref(false)
|
|
||||||
const outdoorNavManualMode = ref(false)
|
|
||||||
|
|
||||||
const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
|
const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
|
||||||
const indoorModelSource = guideUseCase.getModelSource()
|
const indoorModelSource = guideUseCase.getModelSource()
|
||||||
@@ -634,8 +596,9 @@ const disableIndoorAutoExit = computed(() => (
|
|||||||
|
|
||||||
const showGuideHomeDock = computed(() => (
|
const showGuideHomeDock = computed(() => (
|
||||||
currentTab.value === 'guide'
|
currentTab.value === 'guide'
|
||||||
|
&& is3DMode.value
|
||||||
&& !showRoutePlanner.value
|
&& !showRoutePlanner.value
|
||||||
&& !showOutdoorNavPanel.value
|
&& !showArrivalPanel.value
|
||||||
&& !selectedGuidePoi.value
|
&& !selectedGuidePoi.value
|
||||||
&& !isSimulatingRoute.value
|
&& !isSimulatingRoute.value
|
||||||
&& !(guideOutdoorState.value === 'entrance' && !is3DMode.value)
|
&& !(guideOutdoorState.value === 'entrance' && !is3DMode.value)
|
||||||
@@ -644,7 +607,7 @@ const showGuideHomeDock = computed(() => (
|
|||||||
const showGuideFloatingActions = computed(() => (
|
const showGuideFloatingActions = computed(() => (
|
||||||
currentTab.value === 'guide'
|
currentTab.value === 'guide'
|
||||||
&& !showRoutePlanner.value
|
&& !showRoutePlanner.value
|
||||||
&& !showOutdoorNavPanel.value
|
&& !showArrivalPanel.value
|
||||||
&& !selectedGuidePoi.value
|
&& !selectedGuidePoi.value
|
||||||
&& !isSimulatingRoute.value
|
&& !isSimulatingRoute.value
|
||||||
&& (!showGuideHomeDock.value || !homeSearchExpanded.value)
|
&& (!showGuideHomeDock.value || !homeSearchExpanded.value)
|
||||||
@@ -652,6 +615,7 @@ const showGuideFloatingActions = computed(() => (
|
|||||||
|
|
||||||
const guideQuickActiveAction = computed<'indoor' | 'arrival' | 'explain'>(() => {
|
const guideQuickActiveAction = computed<'indoor' | 'arrival' | 'explain'>(() => {
|
||||||
if (currentTab.value === 'explain') return 'explain'
|
if (currentTab.value === 'explain') return 'explain'
|
||||||
|
if (showArrivalPanel.value) return 'arrival'
|
||||||
return is3DMode.value ? 'indoor' : 'arrival'
|
return is3DMode.value ? 'indoor' : 'arrival'
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -777,8 +741,7 @@ const handleTabChange = (tabId: GuideTopTab) => {
|
|||||||
showRoutePlanner.value = false
|
showRoutePlanner.value = false
|
||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
isPoiCardCollapsed.value = false
|
isPoiCardCollapsed.value = false
|
||||||
showOutdoorNavPanel.value = false
|
closeArrivalPanel()
|
||||||
clearOutdoorNavState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncTopTabFromHash = () => {
|
const syncTopTabFromHash = () => {
|
||||||
@@ -793,6 +756,7 @@ const syncTopTabFromHash = () => {
|
|||||||
showRoutePlanner.value = false
|
showRoutePlanner.value = false
|
||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
isPoiCardCollapsed.value = false
|
isPoiCardCollapsed.value = false
|
||||||
|
closeArrivalPanel()
|
||||||
}
|
}
|
||||||
syncTopTabToUrl(tab)
|
syncTopTabToUrl(tab)
|
||||||
return
|
return
|
||||||
@@ -1239,7 +1203,7 @@ const handleRouteSimulate = () => {
|
|||||||
// 进入 3D 馆内模式
|
// 进入 3D 馆内模式
|
||||||
const handleEnter3DMode = () => {
|
const handleEnter3DMode = () => {
|
||||||
console.log('进入 3D 馆内模式')
|
console.log('进入 3D 馆内模式')
|
||||||
closeOutdoorNavPanel()
|
closeArrivalPanel()
|
||||||
indoorView.value = 'overview'
|
indoorView.value = 'overview'
|
||||||
is3DMode.value = true
|
is3DMode.value = true
|
||||||
guideOutdoorState.value = 'home'
|
guideOutdoorState.value = 'home'
|
||||||
@@ -1253,6 +1217,7 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
|||||||
is3DMode.value = mode === '3d'
|
is3DMode.value = mode === '3d'
|
||||||
|
|
||||||
if (mode === '2d') {
|
if (mode === '2d') {
|
||||||
|
closeHomeSearchDock()
|
||||||
indoorView.value = 'overview'
|
indoorView.value = 'overview'
|
||||||
guideOutdoorState.value = 'home'
|
guideOutdoorState.value = 'home'
|
||||||
selectedGuidePoi.value = null
|
selectedGuidePoi.value = null
|
||||||
@@ -1261,7 +1226,7 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
|||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
clearRoutePreviewState()
|
clearRoutePreviewState()
|
||||||
} else {
|
} else {
|
||||||
closeOutdoorNavPanel()
|
closeArrivalPanel()
|
||||||
indoorView.value = 'overview'
|
indoorView.value = 'overview'
|
||||||
showIndoorHint()
|
showIndoorHint()
|
||||||
if (!routePointOptions.value.length) {
|
if (!routePointOptions.value.length) {
|
||||||
@@ -1295,7 +1260,7 @@ const handleSwitchEntrance = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMoreRouteGuide = () => {
|
const handleMoreRouteGuide = () => {
|
||||||
closeOutdoorNavPanel()
|
closeArrivalPanel()
|
||||||
showRoutePlanner.value = false
|
showRoutePlanner.value = false
|
||||||
is3DMode.value = true
|
is3DMode.value = true
|
||||||
indoorView.value = 'floor'
|
indoorView.value = 'floor'
|
||||||
@@ -1306,44 +1271,24 @@ const handleMoreRouteGuide = () => {
|
|||||||
showIndoorHint(`当前楼层:${getGuideFloorLabel(renderedFloorId.value || activeGuideFloor.value) || '馆内单层'}`, 3200)
|
showIndoorHint(`当前楼层:${getGuideFloorLabel(renderedFloorId.value || activeGuideFloor.value) || '馆内单层'}`, 3200)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 馆外导航处理函数
|
const closeArrivalPanel = () => {
|
||||||
const openOutdoorNavPanel = () => {
|
showArrivalPanel.value = false
|
||||||
if (!isOutdoorGuidePanelEnabled) {
|
|
||||||
showOutdoorNavPanel.value = false
|
|
||||||
showRoutePlanner.value = false
|
|
||||||
selectedGuidePoi.value = null
|
|
||||||
isPoiCardCollapsed.value = false
|
|
||||||
is3DMode.value = false
|
|
||||||
clearOutdoorNavState()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
showOutdoorNavPanel.value = true
|
|
||||||
showRoutePlanner.value = false
|
|
||||||
selectedGuidePoi.value = null
|
|
||||||
isPoiCardCollapsed.value = false
|
|
||||||
// 切换到馆外 2D 地图
|
|
||||||
is3DMode.value = false
|
|
||||||
clearOutdoorNavState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearOutdoorNavState = () => {
|
const closeHomeSearchDock = () => {
|
||||||
outdoorNavRoute.value = null
|
homeSearchPanelRef.value?.collapseHomePanel?.()
|
||||||
outdoorNavPolylines.value = []
|
homeSearchExpanded.value = false
|
||||||
outdoorNavStartPoint.value = null
|
|
||||||
outdoorNavError.value = ''
|
|
||||||
outdoorNavLoading.value = false
|
|
||||||
outdoorNavSelectingManualStart.value = false
|
|
||||||
outdoorNavManualMode.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeOutdoorNavPanel = () => {
|
|
||||||
showOutdoorNavPanel.value = false
|
|
||||||
clearOutdoorNavState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMoreOutdoorNav = () => {
|
const handleMoreOutdoorNav = () => {
|
||||||
openOutdoorNavPanel()
|
showArrivalPanel.value = true
|
||||||
|
showRoutePlanner.value = false
|
||||||
|
is3DMode.value = false
|
||||||
|
guideOutdoorState.value = 'home'
|
||||||
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
|
isSimulatingRoute.value = false
|
||||||
|
closeHomeSearchDock()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleExplainQuickTap = () => {
|
const handleExplainQuickTap = () => {
|
||||||
@@ -1355,8 +1300,7 @@ const handleHomeSearchExpandedChange = (expanded: boolean) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleHomeSearchOutsideTap = () => {
|
const handleHomeSearchOutsideTap = () => {
|
||||||
homeSearchPanelRef.value?.collapseHomePanel?.()
|
closeHomeSearchDock()
|
||||||
homeSearchExpanded.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleHomeDockActivate = () => {
|
const handleHomeDockActivate = () => {
|
||||||
@@ -1366,155 +1310,6 @@ const handleHomeDockActivate = () => {
|
|||||||
homeSearchExpanded.value = true
|
homeSearchExpanded.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOutdoorNavClose = () => {
|
|
||||||
closeOutdoorNavPanel()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavBack = () => {
|
|
||||||
closeOutdoorNavPanel()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavGpsStart = () => {
|
|
||||||
outdoorNavLoading.value = true
|
|
||||||
outdoorNavError.value = ''
|
|
||||||
|
|
||||||
uni.getLocation({
|
|
||||||
type: 'gcj02',
|
|
||||||
success: (res) => {
|
|
||||||
console.log('GPS 定位成功:', res)
|
|
||||||
outdoorNavStartPoint.value = {
|
|
||||||
latitude: res.latitude,
|
|
||||||
longitude: res.longitude
|
|
||||||
}
|
|
||||||
void planOutdoorRoute({
|
|
||||||
latitude: res.latitude,
|
|
||||||
longitude: res.longitude
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('GPS 定位失败:', err)
|
|
||||||
const errMsg = err.errMsg || ''
|
|
||||||
if (errMsg.includes('auth deny') || errMsg.includes('auth reject')) {
|
|
||||||
outdoorNavError.value = '定位权限被拒绝,请在设置中开启'
|
|
||||||
} else if (errMsg.includes('timeout')) {
|
|
||||||
outdoorNavError.value = '定位超时,请重试'
|
|
||||||
} else if (errMsg.includes('unavailable')) {
|
|
||||||
outdoorNavError.value = '位置服务不可用,请检查设备设置'
|
|
||||||
} else {
|
|
||||||
outdoorNavError.value = '定位失败,请检查位置权限'
|
|
||||||
}
|
|
||||||
outdoorNavLoading.value = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavManualStartRequest = () => {
|
|
||||||
outdoorNavManualMode.value = true
|
|
||||||
outdoorNavSelectingManualStart.value = true
|
|
||||||
uni.showToast({
|
|
||||||
title: '输入地址搜索,或点击地图选择起点',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavManualStartCancel = () => {
|
|
||||||
outdoorNavManualMode.value = false
|
|
||||||
outdoorNavSelectingManualStart.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavManualStartConfirm = (location: { latitude: number; longitude: number }) => {
|
|
||||||
outdoorNavStartPoint.value = location
|
|
||||||
outdoorNavManualMode.value = false
|
|
||||||
outdoorNavSelectingManualStart.value = false
|
|
||||||
void planOutdoorRoute(location)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavTravelModeChange = (mode: TravelMode) => {
|
|
||||||
outdoorNavTravelMode.value = mode
|
|
||||||
if (outdoorNavStartPoint.value) {
|
|
||||||
outdoorNavLoading.value = true
|
|
||||||
void planOutdoorRoute(outdoorNavStartPoint.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const planOutdoorRoute = async (from: { latitude: number; longitude: number }) => {
|
|
||||||
outdoorNavLoading.value = true
|
|
||||||
outdoorNavError.value = ''
|
|
||||||
|
|
||||||
const to: RoutePoint = {
|
|
||||||
latitude: MUSEUM_LOCATION.latitude,
|
|
||||||
longitude: MUSEUM_LOCATION.longitude
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let result
|
|
||||||
switch (outdoorNavTravelMode.value) {
|
|
||||||
case 'walking':
|
|
||||||
result = await planWalkingRoute(from, to)
|
|
||||||
break
|
|
||||||
case 'driving':
|
|
||||||
result = await planDrivingRoute(from, to)
|
|
||||||
break
|
|
||||||
case 'transit':
|
|
||||||
result = await planTransitRoute(from, to)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
result = await planWalkingRoute(from, to)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.status === 0 && result.routes && result.routes.length > 0) {
|
|
||||||
const route = result.routes[0]
|
|
||||||
outdoorNavRoute.value = route
|
|
||||||
outdoorNavPolylines.value = [{
|
|
||||||
points: route.polyline,
|
|
||||||
color: '#E0E100',
|
|
||||||
width: 6
|
|
||||||
}]
|
|
||||||
console.log('路线规划成功:', {
|
|
||||||
distance: route.distance,
|
|
||||||
duration: route.duration,
|
|
||||||
steps: route.steps.length
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
outdoorNavError.value = result.message || '路线规划失败'
|
|
||||||
outdoorNavRoute.value = null
|
|
||||||
outdoorNavPolylines.value = []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('路线规划异常:', error)
|
|
||||||
outdoorNavError.value = '路线规划失败,请重试'
|
|
||||||
outdoorNavRoute.value = null
|
|
||||||
outdoorNavPolylines.value = []
|
|
||||||
} finally {
|
|
||||||
outdoorNavLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavStartNavigation = async () => {
|
|
||||||
// 直接调起导航
|
|
||||||
await navigateToMuseum()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOutdoorNavClearRoute = () => {
|
|
||||||
outdoorNavRoute.value = null
|
|
||||||
outdoorNavPolylines.value = []
|
|
||||||
outdoorNavStartPoint.value = null
|
|
||||||
outdoorNavError.value = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理地图点击(用于手动选择起点)
|
|
||||||
const handleMapTapForOutdoorNav = (location: { latitude?: number; longitude?: number }) => {
|
|
||||||
if (!outdoorNavSelectingManualStart.value) return
|
|
||||||
|
|
||||||
const latitude = location.latitude
|
|
||||||
const longitude = location.longitude
|
|
||||||
if (latitude !== undefined && longitude !== undefined) {
|
|
||||||
outdoorNavStartPoint.value = { latitude, longitude }
|
|
||||||
outdoorNavSelectingManualStart.value = false
|
|
||||||
void planOutdoorRoute({ latitude, longitude })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const guideShellMode = computed(() => (is3DMode.value ? '3d' : '2d'))
|
const guideShellMode = computed(() => (is3DMode.value ? '3d' : '2d'))
|
||||||
const guideMapType = computed(() => (is3DMode.value ? 'indoor' : 'outdoor'))
|
const guideMapType = computed(() => (is3DMode.value ? 'indoor' : 'outdoor'))
|
||||||
const guideOutdoorVariant = computed(() => (
|
const guideOutdoorVariant = computed(() => (
|
||||||
|
|||||||
57
src/services/ThirdPartyMapSearchService.ts
Normal file
57
src/services/ThirdPartyMapSearchService.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
export type ThirdPartyMapProvider = 'amap' | 'baidu' | 'tencent'
|
||||||
|
|
||||||
|
export interface ThirdPartyMapSearchTarget {
|
||||||
|
keyword: string
|
||||||
|
region: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ThirdPartyMapProviderOption {
|
||||||
|
provider: ThirdPartyMapProvider
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const THIRD_PARTY_MAP_PROVIDERS: ThirdPartyMapProviderOption[] = [
|
||||||
|
{ provider: 'amap', label: '高德地图' },
|
||||||
|
{ provider: 'baidu', label: '百度地图' },
|
||||||
|
{ provider: 'tencent', label: '腾讯地图' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const encode = (value: string) => encodeURIComponent(value)
|
||||||
|
|
||||||
|
export const buildThirdPartyMapSearchUri = (
|
||||||
|
provider: ThirdPartyMapProvider,
|
||||||
|
target: ThirdPartyMapSearchTarget
|
||||||
|
) => {
|
||||||
|
const keyword = target.keyword.trim()
|
||||||
|
const region = target.region.trim()
|
||||||
|
|
||||||
|
switch (provider) {
|
||||||
|
case 'amap':
|
||||||
|
return `https://uri.amap.com/search?keyword=${encode(keyword)}&city=${encode(region)}&src=museum-guide&callnative=1`
|
||||||
|
case 'baidu':
|
||||||
|
return `https://api.map.baidu.com/place/search?query=${encode(keyword)}®ion=${encode(region)}&output=html&src=museum-guide`
|
||||||
|
case 'tencent':
|
||||||
|
return `https://apis.map.qq.com/uri/v1/search?keyword=${encode(keyword)}®ion=${encode(region)}&referer=museum-guide`
|
||||||
|
default: {
|
||||||
|
const exhaustive: never = provider
|
||||||
|
return exhaustive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const openThirdPartyMapSearch = (
|
||||||
|
provider: ThirdPartyMapProvider,
|
||||||
|
target: ThirdPartyMapSearchTarget
|
||||||
|
) => {
|
||||||
|
const uri = buildThirdPartyMapSearchUri(provider, target)
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined' && window.location) {
|
||||||
|
window.location.href = uri
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '请在 H5 浏览器中打开地图',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user