|
|
|
|
@ -1,8 +1,9 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
|
|
|
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import {
|
|
|
|
|
getTiandituKey,
|
|
|
|
|
invalidateMapSize,
|
|
|
|
|
loadTianditu,
|
|
|
|
|
parseTiandituPoiLonlat,
|
|
|
|
|
SUZHOU_MAP_CENTER,
|
|
|
|
|
@ -33,12 +34,46 @@ let map: TiandituMap | null = null
|
|
|
|
|
let marker: TiandituMarker | null = null
|
|
|
|
|
let localSearch: TiandituLocalSearch | null = null
|
|
|
|
|
let tiandituApi: NonNullable<typeof window.T> | null = null
|
|
|
|
|
let resizeTimers: ReturnType<typeof setTimeout>[] = []
|
|
|
|
|
|
|
|
|
|
function parseCoord(v: string) {
|
|
|
|
|
const n = Number(v)
|
|
|
|
|
return Number.isFinite(n) ? n : null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clearResizeTimers() {
|
|
|
|
|
for (const timer of resizeTimers) clearTimeout(timer)
|
|
|
|
|
resizeTimers = []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function scheduleMapResize() {
|
|
|
|
|
clearResizeTimers()
|
|
|
|
|
const delays = [0, 80, 240, 480]
|
|
|
|
|
for (const delay of delays) {
|
|
|
|
|
resizeTimers.push(
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (map) invalidateMapSize(map)
|
|
|
|
|
}, delay),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function destroyMapInstance() {
|
|
|
|
|
clearResizeTimers()
|
|
|
|
|
if (map && marker) {
|
|
|
|
|
try {
|
|
|
|
|
map.removeOverLay(marker)
|
|
|
|
|
} catch {
|
|
|
|
|
/* ignore */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
marker = null
|
|
|
|
|
map?.destroy?.()
|
|
|
|
|
map = null
|
|
|
|
|
localSearch = null
|
|
|
|
|
if (containerRef.value) containerRef.value.innerHTML = ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function applyPick(lng: number, lat: number, zoom = 16) {
|
|
|
|
|
longitude.value = lng.toFixed(6)
|
|
|
|
|
latitude.value = lat.toFixed(6)
|
|
|
|
|
@ -104,6 +139,7 @@ function selectPoi(poi: TiandituSearchPoi) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function initMap() {
|
|
|
|
|
await nextTick()
|
|
|
|
|
if (!containerRef.value) return
|
|
|
|
|
|
|
|
|
|
mapError.value = ''
|
|
|
|
|
@ -114,14 +150,10 @@ async function initMap() {
|
|
|
|
|
|
|
|
|
|
mapLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
destroyMapInstance()
|
|
|
|
|
|
|
|
|
|
const T = await loadTianditu()
|
|
|
|
|
tiandituApi = T
|
|
|
|
|
if (map) {
|
|
|
|
|
if (marker) map.removeOverLay(marker)
|
|
|
|
|
marker = null
|
|
|
|
|
map = null
|
|
|
|
|
}
|
|
|
|
|
localSearch = null
|
|
|
|
|
|
|
|
|
|
const container = containerRef.value
|
|
|
|
|
container.innerHTML = ''
|
|
|
|
|
@ -148,6 +180,8 @@ async function initMap() {
|
|
|
|
|
searchResults.value = []
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
scheduleMapResize()
|
|
|
|
|
|
|
|
|
|
if (props.defaultKeyword.trim()) {
|
|
|
|
|
searchKeyword.value = props.defaultKeyword.trim()
|
|
|
|
|
runSearch()
|
|
|
|
|
@ -156,6 +190,7 @@ async function initMap() {
|
|
|
|
|
mapError.value = e instanceof Error ? e.message : '地图加载失败'
|
|
|
|
|
} finally {
|
|
|
|
|
mapLoading.value = false
|
|
|
|
|
scheduleMapResize()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -168,14 +203,15 @@ watch([longitude, latitude], async () => {
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
void initMap()
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
void initMap()
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
if (map && marker) map.removeOverLay(marker)
|
|
|
|
|
marker = null
|
|
|
|
|
map = null
|
|
|
|
|
localSearch = null
|
|
|
|
|
destroyMapInstance()
|
|
|
|
|
tiandituApi = null
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
@ -202,20 +238,22 @@ onBeforeUnmount(() => {
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<div v-if="mapError" class="pick-map-error">{{ mapError }}</div>
|
|
|
|
|
<div
|
|
|
|
|
v-else
|
|
|
|
|
ref="containerRef"
|
|
|
|
|
v-loading="mapLoading"
|
|
|
|
|
class="pick-map"
|
|
|
|
|
:style="{ height: `${props.height}px` }"
|
|
|
|
|
/>
|
|
|
|
|
<div v-else class="pick-map-shell" v-loading="mapLoading">
|
|
|
|
|
<div
|
|
|
|
|
ref="containerRef"
|
|
|
|
|
class="pick-map"
|
|
|
|
|
:style="{ height: `${props.height}px` }"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<p v-if="!mapError" class="pick-hint">可搜索地点或点击地图选点,确定后将填入经纬度</p>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.pick-map-wrap {
|
|
|
|
|
position: relative;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pick-search-bar {
|
|
|
|
|
@ -270,12 +308,27 @@ onBeforeUnmount(() => {
|
|
|
|
|
color: var(--el-text-color-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pick-map {
|
|
|
|
|
.pick-map-shell {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
border: 1px solid var(--el-border-color-light);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
background: #e8eef5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pick-map {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
cursor: crosshair;
|
|
|
|
|
isolation: isolate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pick-map :deep(.tdt-map) {
|
|
|
|
|
width: 100% !important;
|
|
|
|
|
height: 100% !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pick-map-error {
|
|
|
|
|
|