1
0

selectMapImage.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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. const AMap = await AMapLoader.load({
  51. plugins: ["AMap.PlaceSearch", "AMap.Event"],
  52. key: "e661b00bdf2c44cccf71ef6070ef41b8",
  53. version: "2.0",
  54. });
  55. const map = new AMap.Map(mapEl.value, {
  56. WebGLParams: {
  57. preserveDrawingBuffer: true,
  58. },
  59. resizeEnable: true,
  60. });
  61. const placeSearch = new AMap.PlaceSearch({
  62. pageSize: 5,
  63. showCover: false,
  64. pageIndex: 1,
  65. map: map,
  66. panel: resultEl.value,
  67. autoFitView: true,
  68. });
  69. const setSearch = (data) => {
  70. searchInfo.value = {
  71. text: data.pname + data.cityname + data.adname + data.address,
  72. lat: data.location.lat,
  73. lng: data.location.lng,
  74. zoom: 0,
  75. };
  76. };
  77. placeSearch.on("listElementClick", (e) => {
  78. setSearch(e.data);
  79. showSearch.value = false;
  80. });
  81. placeSearch.on("complete", function (result) {
  82. setTimeout(() => {
  83. const markers = map.getAllOverlays("marker");
  84. for (const marker of markers) {
  85. marker.on("click", () => {
  86. setSearch(marker._data);
  87. });
  88. }
  89. }, 500);
  90. });
  91. const getMapInfo = (): MapInfo => {
  92. var zoom = map.getZoom(); //获取当前地图级别
  93. var center = map.getCenter();
  94. return {
  95. text: "",
  96. zoom,
  97. lat: center.lat,
  98. lng: center.lng,
  99. };
  100. };
  101. //绑定地图移动与缩放事件
  102. map.on("moveend", () => {
  103. info.value = getMapInfo();
  104. });
  105. map.on("zoomend", () => {
  106. info.value = getMapInfo();
  107. });
  108. searchAMap.value = placeSearch;
  109. onCleanup(() => {
  110. searchAMap.value = null;
  111. map.destroy();
  112. });
  113. });
  114. const search = debounce((keyword: string) => {
  115. searchAMap.value.search(keyword);
  116. }, 1000);
  117. watchEffect(() => {
  118. searchAMap.value && search(keyword.value);
  119. });
  120. defineExpose<QuiskExpose>({
  121. submit() {
  122. return new Promise<MapImage>((resolve) => {
  123. if (mapEl.value) {
  124. const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;
  125. canvas.toBlob((blob) => resolve({ blob, search: searchInfo.value! }));
  126. } else {
  127. resolve({ blob: null, search: null });
  128. }
  129. });
  130. },
  131. });
  132. </script>
  133. <style lang="scss" scoped>
  134. .search-layout {
  135. display: inline-block;
  136. position: relative;
  137. margin-bottom: 15px;
  138. z-index: 2;
  139. }
  140. .rrr {
  141. position: absolute;
  142. left: 0;
  143. right: 0;
  144. z-index: 1;
  145. }
  146. .search-sh,
  147. .search-result {
  148. overflow: hidden;
  149. &.show {
  150. max-height: 450px;
  151. overflow-y: auto;
  152. }
  153. }
  154. .def-map-info {
  155. margin-top: 10px;
  156. p {
  157. font-size: 14px;
  158. color: rgba(0, 0, 0, 0.85);
  159. display: inline;
  160. &:not(:last-child)::after {
  161. content: ",";
  162. margin-right: 6px;
  163. }
  164. }
  165. span::after {
  166. content: ":";
  167. }
  168. }
  169. .def-select-map-layout {
  170. --scale: 1.5;
  171. width: 100%;
  172. padding-top: calc((390 / 540) * 100%);
  173. position: relative;
  174. z-index: 1;
  175. }
  176. .def-select-map {
  177. position: absolute;
  178. left: 0;
  179. top: 0;
  180. width: 100%;
  181. height: 100%;
  182. }
  183. </style>