Files
xk-of-view/src/components/ui/MapContainer.vue
2026-07-18 05:57:07 +08:00

83 lines
3.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="map-shell">
<div v-if="canLoadMap" ref="mapRef" class="map-canvas" :aria-label="title"></div>
<div v-if="!canLoadMap || hasError" class="map-fallback">
<div>
<span>公司位置</span>
<h3>{{ title }}</h3>
<p>{{ address }}</p>
<small>经纬度{{ lng }}, {{ lat }}</small>
</div>
<a :href="mapLink" target="_blank" rel="noreferrer">打开高德地图</a>
</div>
<div v-if="loading && canLoadMap" class="map-loading">地图加载中...</div>
</div>
</template>
<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
const props = defineProps({
lng: { type: Number, required: true },
lat: { type: Number, required: true },
zoom: { type: Number, default: 16 },
title: { type: String, default: '公司位置' },
address: { type: String, default: '' },
amapKey: { type: String, default: import.meta.env.VITE_AMAP_KEY || '' },
securityJsCode: { type: String, default: import.meta.env.VITE_AMAP_SECURITY_CODE || '' }
})
const mapRef = ref(null)
const mapInstance = ref(null)
const loading = ref(false)
const hasError = ref(false)
const canLoadMap = computed(() => Boolean(props.amapKey && props.securityJsCode))
const mapLink = computed(() => `https://uri.amap.com/marker?position=${props.lng},${props.lat}&name=${encodeURIComponent(props.title)}&src=xiaokang&coordinate=gaode&callnative=1`)
const initMap = async () => {
if (!canLoadMap.value) return
loading.value = true
hasError.value = false
try {
window._AMapSecurityConfig = { securityJsCode: props.securityJsCode }
const AMap = await AMapLoader.load({ key: props.amapKey, version: '2.0', plugins: ['AMap.InfoWindow'] })
await nextTick()
if (!mapRef.value) return
mapInstance.value = new AMap.Map(mapRef.value, { zoom: props.zoom, center: [props.lng, props.lat], viewMode: '3D' })
const marker = new AMap.Marker({ position: [props.lng, props.lat], title: props.title })
mapInstance.value.add(marker)
const infoWindow = new AMap.InfoWindow({
content: `<div style="padding:8px 10px"><strong>${props.title}</strong><p style="margin:6px 0 0;font-size:12px;line-height:1.6">${props.address}</p></div>`,
offset: new AMap.Pixel(0, -30)
})
infoWindow.open(mapInstance.value, [props.lng, props.lat])
marker.on('click', () => infoWindow.open(mapInstance.value, [props.lng, props.lat]))
} catch (error) {
console.error('地图加载失败', error)
hasError.value = true
} finally {
loading.value = false
}
}
onMounted(initMap)
onBeforeUnmount(() => {
mapInstance.value?.destroy?.()
mapInstance.value = null
})
</script>
<style scoped>
.map-shell { position: relative; min-height: 380px; overflow: hidden; border: 1px solid var(--line); border-radius: 14px; background: var(--panel-bg); }
.map-canvas { width: 100%; min-height: 380px; }
.map-loading { position: absolute; inset: 0; display: grid; place-items: center; background: color-mix(in srgb, var(--panel-bg) 70%, transparent); color: var(--text-main); }
.map-fallback { min-height: 380px; display: flex; align-items: end; justify-content: space-between; gap: 24px; padding: 28px; background: linear-gradient(135deg, color-mix(in srgb, var(--panel-bg) 88%, #fff 12%), var(--page-muted)); }
.map-fallback span { color: var(--gold); font-size: 13px; font-weight: 800; }
.map-fallback h3 { margin-top: 10px; color: var(--ink); font-size: clamp(28px, 4vw, 48px); font-weight: 900; }
.map-fallback p { margin-top: 10px; color: var(--text-main); line-height: 1.8; }
.map-fallback small { display: block; margin-top: 10px; color: var(--text-muted); }
.map-fallback a { flex: 0 0 auto; border: 1px solid var(--line); border-radius: 999px; padding: 12px 18px; color: var(--ink); font-weight: 800; }
@media (max-width: 720px) { .map-fallback { align-items: stretch; flex-direction: column; } .map-fallback a { text-align: center; } }
</style>