Add image detail map and quick upload
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
<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(20,184,166,0.22)]"></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="text-xs uppercase tracking-[0.18em] text-slate-500">位置地图</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>
|
||||
Reference in New Issue
Block a user