index.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <template>
  2. <div class="location-picker" v-if="visabled">
  3. <!-- 地图容器 -->
  4. <div id="map-container"></div>
  5. <div class="confirmLocation">
  6. <!-- 搜索框 -->
  7. <input
  8. v-model="searchText"
  9. @change="handleSearch"
  10. ref="searchInput"
  11. placeholder="搜索地点..."
  12. />
  13. <!-- 搜索结果列表 -->
  14. <ul v-if="searchResults.length" class="search-results">
  15. <li
  16. v-for="(result, index) in searchResults"
  17. :key="index"
  18. @click="selectSearchResult(result)"
  19. >
  20. {{ result.name }} - {{ result.address }}
  21. </li>
  22. </ul>
  23. <!-- 定位结果展示 -->
  24. <div v-if="selectedLocation" class="result">
  25. <p>已选择:{{ selectedLocation.address }}</p>
  26. <p>经纬度:{{ selectedLocation.lng }}, {{ selectedLocation.lat }}</p>
  27. </div>
  28. <!-- 操作按钮 -->
  29. <van-button
  30. size="small"
  31. @click="confirmLocation"
  32. text="确认选择"
  33. ></van-button>
  34. </div>
  35. </div>
  36. </template>
  37. <script>
  38. import AMapLoader from "@amap/amap-jsapi-loader";
  39. export default {
  40. data() {
  41. return {
  42. map: null,
  43. visabled: false,
  44. marker: null,
  45. AMapRef: null,
  46. searchText: "",
  47. selectedLocation: null,
  48. searchResults: [], // 搜索结果列表
  49. };
  50. },
  51. methods: {
  52. async initMap() {
  53. try {
  54. this.visabled = true;
  55. this.selectedLocation = null;
  56. this.searchText = "";
  57. // 加载高德地图JS API
  58. AMapLoader.load({
  59. key: "487c977753dd211e731f1d13d9322f09",
  60. version: "2.0",
  61. securityJsCode: "2403bc79e3493aa796277092566cda59",
  62. plugins: ["AMap.Geocoder", "AMap.PlaceSearch", "AMap.AutoComplete"],
  63. }).then((AMap) => {
  64. this.AMapRef = AMap;
  65. // 初始化地图
  66. this.map = new AMap.Map("map-container", {
  67. zoom: 15,
  68. });
  69. // 添加点击选点事件
  70. this.map.on("click", (e) => {
  71. this.updateMarkerPosition(e.lnglat);
  72. this.getAddress(e.lnglat.lng, e.lnglat.lat);
  73. });
  74. // 初始化标记点
  75. this.marker = new AMap.Marker({
  76. position: this.map.getCenter(),
  77. map: this.map,
  78. });
  79. });
  80. } catch (error) {
  81. console.error("地图初始化失败:", error);
  82. }
  83. },
  84. // 更新标记位置
  85. updateMarkerPosition(lnglat) {
  86. this.marker.setPosition(lnglat);
  87. this.map.setCenter(lnglat);
  88. },
  89. getAddress(long, lat) {
  90. fetch(
  91. `https://restapi.amap.com/v3/geocode/regeo?location=${long},${lat}&key=74d5aa7c4270effe9a18d9cfa6149abf`
  92. )
  93. .then((res) => res.json())
  94. .then((data) => {
  95. let address = data.regeocode.addressComponent;
  96. this.selectedLocation = {
  97. address: data.regeocode.formatted_address,
  98. lng: long,
  99. lat: lat,
  100. };
  101. })
  102. .catch((error) => {
  103. console.log(error);
  104. });
  105. },
  106. // 搜索地点
  107. handleSearch() {
  108. fetch(
  109. `https://restapi.amap.com/v3/place/text?keywords=${this.searchText}&key=74d5aa7c4270effe9a18d9cfa6149abf`
  110. )
  111. .then((res) => res.json())
  112. .then((data) => {
  113. this.searchResults = data.pois.map((poi) => ({
  114. name: poi.name,
  115. address: poi.address,
  116. location: poi.location,
  117. }));
  118. this.$refs.searchInput.blur(); // 收起键盘
  119. })
  120. .catch((error) => {
  121. console.log(error);
  122. });
  123. },
  124. // 选择搜索结果
  125. selectSearchResult(result) {
  126. this.updateMarkerPosition([
  127. result.location.split(",")[0],
  128. result.location.split(",")[1],
  129. ]);
  130. this.selectedLocation = {
  131. lng: result.location.split(",")[0],
  132. lat: result.location.split(",")[1],
  133. address: result.name + result.address,
  134. };
  135. this.searchResults = []; // 清空搜索结果
  136. },
  137. // 确认选择
  138. confirmLocation() {
  139. if (this.selectedLocation) {
  140. this.$emit("selected", this.selectedLocation);
  141. this.visabled = false;
  142. } else {
  143. alert("请先在地图上选择位置");
  144. }
  145. },
  146. },
  147. };
  148. </script>
  149. <style scoped>
  150. #map-container {
  151. width: 100%;
  152. height: 400px;
  153. border: 1px solid #ddd;
  154. border-radius: 8px;
  155. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  156. margin-bottom: 10px;
  157. }
  158. .confirmLocation {
  159. position: absolute;
  160. top: 20px;
  161. right: 20px;
  162. width: 240px;
  163. /* width: calc(100% - 40px); */
  164. background: #fff;
  165. padding: 15px;
  166. border-radius: 8px;
  167. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  168. }
  169. .location-picker {
  170. position: fixed;
  171. z-index: 999;
  172. width: 100%;
  173. top: 0;
  174. display: flex;
  175. flex-direction: column;
  176. gap: 10px;
  177. background: rgba(255, 255, 255, 0.95);
  178. padding: 10px;
  179. }
  180. input {
  181. padding: 10px;
  182. border: 1px solid #ccc;
  183. border-radius: 4px;
  184. font-size: 14px;
  185. width: 100%;
  186. box-sizing: border-box;
  187. }
  188. .search-results {
  189. list-style: none;
  190. padding: 0;
  191. margin: 10px 0 0;
  192. border: 1px solid #ccc;
  193. border-radius: 4px;
  194. max-height: 200px;
  195. overflow-y: auto;
  196. background: #fff;
  197. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  198. }
  199. .search-results li {
  200. padding: 10px;
  201. cursor: pointer;
  202. border-bottom: 1px solid #eee;
  203. font-size: 14px;
  204. }
  205. .search-results li:last-child {
  206. border-bottom: none;
  207. }
  208. .search-results li:hover {
  209. background: #f7f7f7;
  210. }
  211. .result {
  212. font-size: 14px;
  213. color: #333;
  214. margin-top: 10px;
  215. }
  216. .result p {
  217. margin: 5px 0;
  218. }
  219. .van-button {
  220. width: 100%;
  221. background-color: #007bff;
  222. color: #fff;
  223. border-radius: 4px;
  224. font-size: 16px;
  225. padding: 10px;
  226. text-align: center;
  227. cursor: pointer;
  228. }
  229. .van-button:hover {
  230. background-color: #0056b3;
  231. }
  232. </style>