map2.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <template>
  2. <div class="map-wrapper">
  3. <div class="search-controls">
  4. <div class="search-input-group">
  5. <div>
  6. <input
  7. v-model="searchQuery"
  8. @keyup.enter="executeSearch"
  9. placeholder="输入地点或地址"
  10. class="search-input"
  11. />
  12. <button @click="executeSearch" class="search-button" style="margin-left: 5px;">搜索</button>
  13. </div>
  14. <div style="margin-left: 10px;">
  15. <input v-model="longitude" class="search-input">
  16. <input v-model="latitude" class="search-input">
  17. <button @click="confirmAddress" class="search-button" style="margin-left: 5px;">确认</button>
  18. </div>
  19. </div>
  20. <div v-if="searchResults.length" class="search-results">
  21. <div
  22. v-for="(result, index) in searchResults"
  23. :key="index"
  24. class="result-item"
  25. @click="selectSearchResult(result)"
  26. >
  27. <h3>{{ result.title }}</h3>
  28. <p>{{ result.address }}</p>
  29. </div>
  30. </div>
  31. </div>
  32. <div ref="mapElement" class="bmap-container"></div>
  33. <div v-if="selectedPoint" class="coordinates-display">
  34. 经度: {{ selectedPoint.lng.toFixed(6) }},
  35. 纬度: {{ selectedPoint.lat.toFixed(6) }}
  36. <p v-if="selectedTitle">地名: {{ selectedTitle }}</p>
  37. <p v-if="selectedAddress">地址: {{ selectedAddress }}</p>
  38. </div>
  39. </div>
  40. </template>
  41. <script setup>
  42. import { ref, onMounted, onBeforeUnmount } from 'vue'
  43. const mapElement = ref(null)
  44. const mapInstance = ref(null)
  45. const searchQuery = ref('')
  46. const searchResults = ref([])
  47. const selectedPoint = ref(null)
  48. const selectedAddress = ref('')
  49. const selectedTitle = ref('')
  50. const longitude = ref('')
  51. const latitude = ref('')
  52. let geocoder = null
  53. // 状态常量
  54. const BMAP_STATUS_SUCCESS = ref(0)
  55. const initializeMap = () => {
  56. const BMap = window.BMap
  57. BMAP_STATUS_SUCCESS.value = window.BMAP_STATUS_SUCCESS
  58. mapInstance.value = new BMap.Map(mapElement.value)
  59. const initialPoint = new BMap.Point(117.026817,31.968348)
  60. mapInstance.value.centerAndZoom(initialPoint, 12)
  61. mapInstance.value.enableScrollWheelZoom(true)
  62. geocoder = new BMap.Geocoder()
  63. mapInstance.value.addEventListener('click', handleMapClick)
  64. }
  65. const handleMapClick = async (e) => {
  66. // clearMarkers()
  67. // addMarker(e.point)
  68. selectedPoint.value = e.point
  69. await getAddressFromPoint(e.point)
  70. }
  71. // 修正后的搜索方法
  72. const executeSearch = () => {
  73. if (!searchQuery.value.trim()) return
  74. const BMap = window.BMap
  75. const localSearch = new BMap.LocalSearch(mapInstance.value, {
  76. onSearchComplete: (results) => {
  77. if (localSearch.getStatus() === BMAP_STATUS_SUCCESS.value) {
  78. // 新版POI获取方式
  79. const pois = [];
  80. const total = results.getCurrentNumPois();
  81. for (let i = 0; i < total; i++) {
  82. const poi = results.getPoi(i);
  83. if (poi) {
  84. pois.push({
  85. title: poi.title,
  86. address: poi.address || '无地址信息',
  87. point: poi.point
  88. });
  89. }
  90. }
  91. searchResults.value = pois;
  92. }
  93. },
  94. pageCapacity: 10
  95. })
  96. localSearch.search(searchQuery.value)
  97. }
  98. const emit = defineEmits(['getPos'])
  99. const confirmAddress = () => {
  100. emit('getPos', {lon:longitude.value,lat:latitude.value});
  101. searchQuery.value = ''
  102. searchResults.value = []
  103. selectedAddress.value = ''
  104. selectedTitle.value = ''
  105. selectedPoint.value = null
  106. longitude.value = ''
  107. latitude.value = ''
  108. clearMarkers()
  109. mapInstance.value.clearOverlays()
  110. }
  111. const selectSearchResult = (result) => {
  112. clearMarkers()
  113. mapInstance.value.panTo(result.point)
  114. addMarker(result.point)
  115. selectedPoint.value = result.point
  116. getAddressFromPoint(result.point)
  117. searchResults.value = []
  118. }
  119. const addMarker = (point) => {
  120. const marker = new BMap.Marker(point,{
  121. enableDragging: false, // 关闭标记拖拽
  122. })
  123. mapInstance.value.addOverlay(marker)
  124. convertPoint(point.lng, point.lat)
  125. // marker.addEventListener('click', () => {
  126. // showInfoWindow(marker, point)
  127. // })
  128. mapInstance.value.addEventListener('click', (e) => {
  129. if (e.overlay) return // 当点击覆盖物时不执行地图操作
  130. // 其他地图点击逻辑...
  131. })
  132. }
  133. const convertPoint = (lng, lat) => {
  134. const BMap = window.BMap
  135. if (mapInstance.value.getMapType() === BMAP_NORMAL_MAP) {
  136. return new BMap.Point(lng, lat)
  137. }
  138. // 处理百度坐标偏移
  139. const convertor = new BMap.Convertor()
  140. convertor.translate([new BMap.Point(lng, lat)], 1, 5, (res) => {
  141. if (res.status === 0) {
  142. return res.points[0]
  143. }
  144. })
  145. }
  146. const showInfoWindow = (marker, point) => {
  147. const infoWindow = new BMap.InfoWindow(`
  148. <div class="info-window">
  149. <h4>位置详情</h4>
  150. <p>经度: ${point.lng.toFixed(6)}</p>
  151. <p>纬度: ${point.lat.toFixed(6)}</p>
  152. ${selectedAddress.value ? `<p>地址: ${selectedAddress.value}</p>` : ''}
  153. </div>
  154. `)
  155. marker.openInfoWindow(infoWindow)
  156. }
  157. const clearMarkers = () => {
  158. mapInstance.value.clearOverlays()
  159. }
  160. const getAddressFromPoint = (point) => {
  161. return new Promise((resolve) => {
  162. geocoder.getLocation(point, (result) => {
  163. if (result) {
  164. selectedAddress.value = result.address
  165. selectedTitle.value = result?.content?.poi_region[0]?.name
  166. longitude.value = result?.point?.lng?.toFixed(6)
  167. latitude.value = result?.point?.lat?.toFixed(6)
  168. resolve(result.address)
  169. }
  170. })
  171. })
  172. }
  173. onMounted(() => {
  174. const script = document.createElement('script')
  175. script.src = `https://api.map.baidu.com/api?v=3.0&ak=tHCKI9rDMnTgxHPYsk95hGExEbYqkcIn&callback=initMap`
  176. document.head.appendChild(script)
  177. window.initMap = () => {
  178. initializeMap()
  179. }
  180. })
  181. onBeforeUnmount(() => {
  182. if (mapInstance.value) {
  183. mapInstance.value.removeEventListener('click', handleMapClick)
  184. mapInstance.value.destroy()
  185. }
  186. })
  187. </script>
  188. <style>
  189. .map-wrapper {
  190. position: relative;
  191. height: 500px;
  192. width: 100%;
  193. }
  194. .bmap-container {
  195. height: 100%;
  196. width: 100%;
  197. }
  198. .search-controls {
  199. position: absolute;
  200. top: 10px;
  201. left: 10px;
  202. z-index: 1000;
  203. background: rgba(255, 255, 255, 0.9);
  204. padding: 15px;
  205. border-radius: 8px;
  206. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  207. width: calc(100% - 20px);
  208. }
  209. .search-input-group {
  210. display: flex;
  211. gap: 8px;
  212. margin-bottom: 10px;
  213. }
  214. .search-input {
  215. flex: 1;
  216. padding: 8px;
  217. border: 1px solid #ddd;
  218. border-radius: 4px;
  219. }
  220. .search-button {
  221. padding: 8px 15px;
  222. background: #3385ff;
  223. color: white;
  224. border: none;
  225. border-radius: 4px;
  226. cursor: pointer;
  227. }
  228. .search-results {
  229. max-height: 300px;
  230. overflow-y: auto;
  231. }
  232. .result-item {
  233. padding: 10px;
  234. border-bottom: 1px solid #eee;
  235. cursor: pointer;
  236. transition: background 0.2s;
  237. }
  238. .result-item:hover {
  239. background: #f5f5f5;
  240. }
  241. .coordinates-display {
  242. position: absolute;
  243. bottom: 20px;
  244. left: 20px;
  245. background: rgba(255, 255, 255, 0.9);
  246. padding: 15px;
  247. border-radius: 8px;
  248. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  249. }
  250. .info-window {
  251. padding: 10px;
  252. min-width: 200px;
  253. }
  254. </style>