From 4f04c1b2e480f7ed30cb8e39de25d99b37023eec Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期三, 16 九月 2026 15:29:41 +0800
Subject: [PATCH] update

---
 frontend/src/components/AmapMeetingPointMap.vue |   82 +++++++++++++++++++++++++++++-----------
 1 files changed, 59 insertions(+), 23 deletions(-)

diff --git a/frontend/src/components/AmapMeetingPointMap.vue b/frontend/src/components/AmapMeetingPointMap.vue
index e06d656..6e64489 100644
--- a/frontend/src/components/AmapMeetingPointMap.vue
+++ b/frontend/src/components/AmapMeetingPointMap.vue
@@ -9,6 +9,9 @@
   activeIndex: { type: Number, default: null },
   cityName: { type: String, default: '' },
   showSearch: { type: Boolean, default: true },
+  directPlacement: { type: Boolean, default: false },
+  address: { type: String, default: '' },
+  searchLabel: { type: String, default: '' },
   compact: { type: Boolean, default: false },
 })
 const emit = defineEmits(['place', 'address-resolved'])
@@ -29,9 +32,16 @@
 let hasFittedPoints = false
 let suggestTimer
 let suggestionRequestId = 0
+let placementRequestId = 0
 let skipNextSuggestion = false
+let preferResolvedAddress = false
+let lastSyncedAddress = ''
 
-const isMarking = computed(() => Number.isInteger(props.activeIndex))
+const placementIndex = computed(() => {
+  if (Number.isInteger(props.activeIndex)) return props.activeIndex
+  return props.directPlacement && props.points.length ? 0 : null
+})
+const isMarking = computed(() => Number.isInteger(placementIndex.value))
 const hasPosition = (point) => (
   point?.longitude != null
   && point?.latitude != null
@@ -51,7 +61,7 @@
     if (!hasPosition(point)) return []
     const content = document.createElement('button')
     content.type = 'button'
-    content.className = `amap-meeting-marker${props.activeIndex === index ? ' is-active' : ''}`
+    content.className = `amap-meeting-marker${placementIndex.value === index ? ' is-active' : ''}`
     const markerLabel = document.createElement('span')
     markerLabel.textContent = String(index + 1)
     content.appendChild(markerLabel)
@@ -61,7 +71,7 @@
       anchor: 'bottom-center',
       content,
       title: content.title,
-      zIndex: props.activeIndex === index ? 120 : 100,
+      zIndex: placementIndex.value === index ? 120 : 100,
     })
     marker.on('click', () => map.setZoomAndCenter(17, marker.getPosition()))
     return [marker]
@@ -76,7 +86,7 @@
 }
 
 const focusCurrentPoint = () => {
-  const point = props.points[props.activeIndex]
+  const point = props.points[placementIndex.value]
   if (map && hasPosition(point)) {
     map.setZoomAndCenter(Math.max(map.getZoom(), 17), [Number(point.longitude), Number(point.latitude)])
   }
@@ -110,20 +120,29 @@
   }
 }
 
-const handleMapClick = async (event) => {
+const placeAtCoordinates = async (coordinates, fallbackAddress = '') => {
   if (!isMarking.value) {
     ElMessage.info('请先选择需要标注的汇合点')
     return
   }
+  const index = placementIndex.value
+  const requestId = ++placementRequestId
+  const address = await resolveAddress(coordinates)
+  if (requestId !== placementRequestId) return
+  preferResolvedAddress = true
+  emit('place', {
+    index,
+    ...coordinates,
+    address: address || fallbackAddress,
+  })
+}
+
+const handleMapClick = (event) => {
   const coordinates = {
     longitude: Number(event.lnglat.getLng().toFixed(6)),
     latitude: Number(event.lnglat.getLat().toFixed(6)),
   }
-  emit('place', {
-    index: props.activeIndex,
-    ...coordinates,
-    address: await resolveAddress(coordinates),
-  })
+  placeAtCoordinates(coordinates)
 }
 
 const getLocationCoordinates = (location) => {
@@ -204,21 +223,30 @@
     return
   }
   map.setZoomAndCenter(17, [coordinates.longitude, coordinates.latitude])
-  if (!isMarking.value) {
-    ElMessage.info('请先点击“标注汇合点”选择需要设置的汇合点')
-    return
-  }
-  emit('place', {
-    index: props.activeIndex,
-    ...coordinates,
-    address: await resolveAddress(coordinates),
-  })
+  await placeAtCoordinates(
+    coordinates,
+    [poi.district, poi.address, poi.name].filter(Boolean).join(' · '),
+  )
 }
 
 const clearSearch = () => {
   suggestionRequestId += 1
   window.clearTimeout(suggestTimer)
   searchResults.value = []
+}
+
+const syncKeywordAddress = (address) => {
+  const value = typeof address === 'string' ? address.trim() : ''
+  if (!value) {
+    lastSyncedAddress = ''
+    return
+  }
+  if (preferResolvedAddress || !keyword.value.trim() || keyword.value === lastSyncedAddress) {
+    skipNextSuggestion = true
+    keyword.value = value
+    preferResolvedAddress = false
+  }
+  lastSyncedAddress = value
 }
 
 onMounted(async () => {
@@ -257,11 +285,13 @@
   updateSearchCity()
   moveToCity()
 })
+watch(() => props.address, syncKeywordAddress, { immediate: true })
 watch(keyword, loadSuggestions)
 
 onBeforeUnmount(() => {
   window.clearTimeout(suggestTimer)
   suggestionRequestId += 1
+  placementRequestId += 1
   clearMarkers()
   map?.destroy()
   map = null
@@ -270,11 +300,12 @@
 
 <template>
   <div class="amap-picker" :class="{ compact }">
-    <div v-if="showSearch" class="map-search">
+    <div v-if="showSearch" class="map-search" :class="{ 'has-label': searchLabel }">
+      <span v-if="searchLabel" class="map-search-label">{{ searchLabel }}</span>
       <el-input
         v-model="keyword"
         clearable
-        placeholder="搜索景区、地址或地标"
+        placeholder="搜索景区、地址或地标,选择后自动定位"
         @keyup.enter="searchLocation"
         @clear="clearSearch"
       />
@@ -289,7 +320,7 @@
     <div v-loading="loading" class="map-shell" :class="{ marking: isMarking }">
       <div ref="mapContainer" class="map-container" />
       <div v-if="loadError" class="map-error">{{ loadError }}</div>
-      <span v-else-if="isMarking" class="marking-tip">点击地图设置“{{ points[activeIndex]?.pointName }}”</span>
+      <span v-else-if="isMarking" class="marking-tip">点击地图设置“{{ points[placementIndex]?.pointName }}”</span>
     </div>
   </div>
 </template>
@@ -297,8 +328,13 @@
 <style scoped>
 .amap-picker { width: min(100%, 900px); }
 .amap-picker.compact { width: 100%; }
-.map-search { position: relative; z-index: 10; display: grid; grid-template-columns: minmax(280px, 440px) auto; gap: 10px; width: fit-content; margin-bottom: 12px; }
+.map-search { position: relative; z-index: 10; display: grid; grid-template-columns: minmax(280px, 440px) auto; gap: 10px; align-items: center; width: fit-content; margin-bottom: 12px; }
+.compact .map-search { width: 100%; grid-template-columns: minmax(0, 1fr) 72px; }
+.map-search.has-label { width: 100%; grid-template-columns: 92px minmax(0, 1fr) 72px; }
+.map-search-label { color: #475467; font-size: 14px; font-weight: 500; white-space: nowrap; }
 .search-results { position: absolute; top: 40px; left: 0; width: 440px; max-height: 280px; overflow-y: auto; background: white; border: 1px solid #d0d5dd; box-shadow: 0 8px 24px rgba(16, 24, 40, .14); }
+.compact .search-results { width: calc(100% - 82px); }
+.map-search.has-label .search-results { left: 102px; width: calc(100% - 184px); }
 .search-results button { display: flex; flex-direction: column; gap: 3px; width: 100%; padding: 10px 12px; color: #344054; text-align: left; background: white; border: 0; border-bottom: 1px solid #eaecf0; cursor: pointer; }
 .search-results button:hover { background: #f2f4f7; }
 .search-results strong { font-size: 13px; }

--
Gitblit v1.7.1