Files
opencloud/src/components/cloud/MiniLocationMap.vue
T
2026-07-28 21:31:38 +08:00

124 lines
3.3 KiB
Vue

<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { loadAMap } from '@/lib/amap'
const props = defineProps<{
latitude: number | null
longitude: number | null
locationName?: string | null
}>()
const mapEl = ref<HTMLDivElement | null>(null)
const loading = ref(false)
const errorMsg = ref('')
let amapLib: typeof AMap | null = null
let mapInstance: AMap.Map | null = null
let marker: AMap.Marker | null = null
const hasCoordinates = computed(() => {
return Number.isFinite(props.latitude) && Number.isFinite(props.longitude)
})
const center = computed<[number, number] | null>(() => {
if (!hasCoordinates.value) return null
return [props.longitude as number, props.latitude as number]
})
function destroyMap() {
marker?.setMap(null)
marker = null
mapInstance?.destroy()
mapInstance = null
}
async function initializeMap() {
if (!center.value) {
destroyMap()
return
}
loading.value = true
errorMsg.value = ''
await nextTick()
destroyMap()
if (!mapEl.value) {
loading.value = false
return
}
try {
amapLib = await loadAMap()
if ('getConfig' in amapLib) {
amapLib.getConfig().appname = 'amap-jsapi-skill'
}
mapInstance = new amapLib.Map(mapEl.value, {
viewMode: '2D',
zoom: 11,
center: center.value,
mapStyle: 'amap://styles/normal',
features: ['bg', 'road', 'building', 'point'],
resizeEnable: true,
dragEnable: true,
zoomEnable: true,
keyboardEnable: false,
doubleClickZoom: true,
scrollWheel: true,
} as AMap.MapOptions)
mapInstance.addControl(new amapLib.ToolBar({
position: 'RT',
offset: [8, 8],
} as Record<string, unknown>))
marker = new amapLib.Marker({
position: center.value,
title: props.locationName || '拍摄位置',
content: '<div class="h-4 w-4 rounded-full border-2 border-white bg-teal-500 shadow-[0_0_0_4px_rgba(52,116,93,0.24)]"></div>',
offset: new amapLib.Pixel(-8, -8),
} as AMap.MarkerOptions)
marker.setMap(mapInstance)
} catch (error) {
errorMsg.value = error instanceof Error ? error.message : '地图加载失败'
} finally {
loading.value = false
}
}
watch(center, () => {
initializeMap()
}, { immediate: true })
onBeforeUnmount(() => {
destroyMap()
})
</script>
<template>
<div v-if="hasCoordinates" class="mt-5 overflow-hidden border border-slate-200 bg-white">
<div class="flex items-center justify-between border-b border-slate-200 px-4 py-3">
<div>
<p class="oc-section-label">位置地图</p>
<p class="mt-1 text-sm font-medium text-slate-900">{{ locationName || '拍摄位置' }}</p>
</div>
<p class="text-right text-[11px] leading-5 text-slate-500">
{{ latitude?.toFixed(4) }}<br />
{{ longitude?.toFixed(4) }}
</p>
</div>
<div class="relative h-40 bg-sky-50">
<div ref="mapEl" class="h-full w-full"></div>
<div v-if="loading" class="absolute inset-0 flex items-center justify-center bg-white/70 text-sm text-slate-500">
地图加载中...
</div>
<div v-else-if="errorMsg" class="absolute inset-0 flex items-center justify-center bg-white/85 px-4 text-center text-sm text-slate-500">
{{ errorMsg }}
</div>
</div>
</div>
</template>