master
lion 3 days ago
parent a9787af6cc
commit cb333fda21

@ -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 {

@ -98,6 +98,13 @@ export interface TiandituMap {
removeEventListener?(type: string, handler: (e?: TiandituMapClickEvent) => void): void
lngLatToLayerPoint?(lnglat: TiandituLngLat): TiandituPoint
getPanes?(): { overlayPane: HTMLElement }
checkResize?(): void
destroy?(): void
}
/** 弹窗内地图在容器尺寸变化后需重算布局,避免瓦片错位到页面顶部 */
export function invalidateMapSize(map: TiandituMap) {
map.checkResize?.()
}
interface SchoolMapOverlayInstance {

@ -22,6 +22,7 @@ const filterRegion = ref('')
const dialog = ref(false)
const mapPickVisible = ref(false)
const mapPickReady = ref(false)
const editing = ref<UniversityRow | null>(null)
const pickDraft = ref({ longitude: '', latitude: '' })
const form = ref({
@ -90,9 +91,18 @@ function openMapPick() {
longitude: form.value.longitude,
latitude: form.value.latitude,
}
mapPickReady.value = false
mapPickVisible.value = true
}
function onMapPickOpened() {
mapPickReady.value = true
}
function onMapPickClosed() {
mapPickReady.value = false
}
function confirmMapPick() {
if (!pickDraft.value.longitude || !pickDraft.value.latitude) {
ElMessage.warning('请先在地图上选点')
@ -232,15 +242,20 @@ usePageLoad(load)
<el-dialog
v-model="mapPickVisible"
class="map-pick-dialog"
title="地图选点"
width="720px"
destroy-on-close
append-to-body
align-center
@opened="onMapPickOpened"
@closed="onMapPickClosed"
>
<p v-if="pickDraft.longitude && pickDraft.latitude" class="pick-coord-preview">
当前选点{{ pickDraft.longitude }}{{ pickDraft.latitude }}
</p>
<TiandituPickMap
v-if="mapPickReady"
v-model:longitude="pickDraft.longitude"
v-model:latitude="pickDraft.latitude"
:default-keyword="form.name"
@ -276,3 +291,10 @@ usePageLoad(load)
font-size: 13px;
}
</style>
<style>
/* 弹窗动画 transform 会导致天地图瓦片错位,打开后取消 transform */
.map-pick-dialog.el-dialog {
transform: none !important;
}
</style>

Loading…
Cancel
Save