selectMapImage.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <template>
  2. <div class="search-layout">
  3. <el-input v-model="keyword" placeholder="输入名称搜索" style="width: 350px" clearable>
  4. <template #append>
  5. <el-button :icon="Search" />
  6. </template>
  7. </el-input>
  8. <div class="rrr">
  9. <div class="search-result" v-show="keyword && showSearch" ref="resultEl"></div>
  10. <div class="search-sh" v-show="keyword">
  11. <el-button style="width: 100%" @click="showSearch = !showSearch">
  12. {{ showSearch ? "收起" : "展开" }}搜索结果
  13. </el-button>
  14. </div>
  15. </div>
  16. </div>
  17. <div class="def-select-map-layout">
  18. <div class="def-select-map" ref="mapEl"></div>
  19. </div>
  20. <div class="def-map-info" v-if="info">
  21. <p><span>纬度</span>{{ info.lat }}</p>
  22. <p><span>经度</span>{{ info.lng }}</p>
  23. <p><span>缩放级别</span>{{ info.zoom }}</p>
  24. </div>
  25. </template>
  26. <script setup lang="ts">
  27. import AMapLoader from "@amap/amap-jsapi-loader";
  28. import { Search } from "@element-plus/icons-vue";
  29. import { ref, watchEffect } from "vue";
  30. import { QuiskExpose } from "@/helper/mount";
  31. import { debounce } from "@/util";
  32. export type MapImage = { blob: Blob | null; search: MapInfo | null };
  33. type MapInfo = { lat: number; lng: number; zoom: number; text: string };
  34. const keyword = ref("");
  35. const showSearch = ref(true);
  36. const info = ref<MapInfo>();
  37. const searchInfo = ref<MapInfo>();
  38. watchEffect(() => {
  39. if (keyword.value) {
  40. showSearch.value = true;
  41. }
  42. });
  43. const mapEl = ref<HTMLDivElement>();
  44. const resultEl = ref<HTMLDivElement>();
  45. const searchAMap = ref<any>();
  46. watchEffect(async (onCleanup) => {
  47. if (!mapEl.value || !resultEl.value) {
  48. return;
  49. }
  50. AMapLoader.reset()
  51. const AMap = await AMapLoader.load({
  52. plugins: ["AMap.PlaceSearch", "AMap.Event"],
  53. key: "15ae4f26585b9f111fa6c3f2a0eb1c03",
  54. version: "2.0",
  55. });
  56. const map = new AMap.Map(mapEl.value, {
  57. WebGLParams: {
  58. preserveDrawingBuffer: true,
  59. },
  60. resizeEnable: true,
  61. });
  62. const placeSearch = new AMap.PlaceSearch({
  63. pageSize: 5,
  64. showCover: false,
  65. pageIndex: 1,
  66. map: map,
  67. panel: resultEl.value,
  68. autoFitView: true,
  69. });
  70. const setSearch = (data) => {
  71. searchInfo.value = {
  72. text: data.pname + data.cityname + data.adname + data.address,
  73. lat: data.location.lat,
  74. lng: data.location.lng,
  75. zoom: 0,
  76. };
  77. };
  78. placeSearch.on("listElementClick", (e) => {
  79. setSearch(e.data);
  80. showSearch.value = false;
  81. });
  82. let clickMarker;
  83. map.on("click", function (e) {
  84. // 获取点击位置的经纬度坐标
  85. var latitude = e.lnglat.lat;
  86. var longitude = e.lnglat.lng;
  87. searchInfo.value = {
  88. text: "",
  89. lat: latitude,
  90. lng: longitude,
  91. zoom: 0,
  92. };
  93. clickMarker && map.remove(clickMarker);
  94. clickMarker = null;
  95. // 在地图上添加标记
  96. clickMarker = new AMap.Marker({
  97. position: [longitude, latitude],
  98. title: "点击位置",
  99. });
  100. map.add(clickMarker);
  101. });
  102. placeSearch.on("complete", function (result) {
  103. setTimeout(() => {
  104. const markers = map.getAllOverlays("marker");
  105. for (const marker of markers) {
  106. marker.on("click", () => {
  107. clickMarker && map.remove(clickMarker);
  108. clickMarker = null;
  109. setSearch(marker._data);
  110. });
  111. }
  112. }, 500);
  113. });
  114. const getMapInfo = (): MapInfo => {
  115. var zoom = map.getZoom(); //获取当前地图级别
  116. var center = map.getCenter();
  117. return {
  118. text: "",
  119. zoom,
  120. lat: center.lat,
  121. lng: center.lng,
  122. };
  123. };
  124. //绑定地图移动与缩放事件
  125. map.on("moveend", () => {
  126. info.value = getMapInfo();
  127. });
  128. map.on("zoomend", () => {
  129. info.value = getMapInfo();
  130. });
  131. searchAMap.value = placeSearch;
  132. onCleanup(() => {
  133. searchAMap.value = null;
  134. map.destroy();
  135. });
  136. });
  137. const search = debounce((keyword: string) => {
  138. searchAMap.value.search(keyword);
  139. }, 1000);
  140. watchEffect(() => {
  141. searchAMap.value && search(keyword.value);
  142. });
  143. defineExpose<QuiskExpose>({
  144. submit() {
  145. return new Promise<MapImage>((resolve) => {
  146. if (mapEl.value) {
  147. const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;
  148. canvas.toBlob((blob) => resolve({ blob, search: searchInfo.value! }));
  149. } else {
  150. resolve({ blob: null, search: null });
  151. }
  152. });
  153. },
  154. });
  155. </script>
  156. <style lang="scss" scoped>
  157. .search-layout {
  158. display: inline-block;
  159. position: relative;
  160. margin-bottom: 15px;
  161. z-index: 2;
  162. }
  163. .rrr {
  164. position: absolute;
  165. left: 0;
  166. right: 0;
  167. z-index: 1;
  168. }
  169. .search-sh,
  170. .search-result {
  171. overflow: hidden;
  172. &.show {
  173. max-height: 450px;
  174. overflow-y: auto;
  175. }
  176. }
  177. .def-map-info {
  178. margin-top: 10px;
  179. p {
  180. font-size: 14px;
  181. color: rgba(0, 0, 0, 0.85);
  182. display: inline;
  183. &:not(:last-child)::after {
  184. content: ",";
  185. margin-right: 6px;
  186. }
  187. }
  188. span::after {
  189. content: ":";
  190. }
  191. }
  192. .def-select-map-layout {
  193. --scale: 1.5;
  194. width: 100%;
  195. padding-top: calc((390 / 540) * 100%);
  196. position: relative;
  197. z-index: 1;
  198. }
  199. .def-select-map {
  200. position: absolute;
  201. left: 0;
  202. top: 0;
  203. width: 100%;
  204. height: 100%;
  205. }
  206. </style>