实现来馆第三方地图跳转
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
lyf
2026-07-09 00:07:47 +08:00
parent 86d036ef18
commit 64dfb480dc
4 changed files with 616 additions and 236 deletions

View 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>