selectMapImage.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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="search-result" v-show="keyword" ref="resultEl"></div>
  9. </div>
  10. <div class="def-select-map" ref="mapEl"></div>
  11. <div class="def-map-info" v-if="info">
  12. <p><span>经度</span>{{ info.lat }}</p>
  13. <p><span>维度</span>{{ info.lng }}</p>
  14. <p><span>缩放级别</span>{{ info.zoom }}</p>
  15. </div>
  16. </template>
  17. <script setup lang="ts">
  18. import AMapLoader from "@amap/amap-jsapi-loader";
  19. import { Search } from "@element-plus/icons-vue";
  20. import { ref, watchEffect } from "vue";
  21. import { QuiskExpose } from "@/helper/mount";
  22. import { debounce } from "@/util";
  23. export type MapImage = { blob: Blob | null };
  24. type MapInfo = { lat: number; lng: number; zoom: number };
  25. const keyword = ref("");
  26. const info = ref<MapInfo>();
  27. const mapEl = ref<HTMLDivElement>();
  28. const resultEl = ref<HTMLDivElement>();
  29. const searchAMap = ref<any>();
  30. watchEffect(async (onCleanup) => {
  31. if (!mapEl.value || !resultEl.value) {
  32. return;
  33. }
  34. const AMap = await AMapLoader.load({
  35. plugins: ["AMap.PlaceSearch"],
  36. key: "e661b00bdf2c44cccf71ef6070ef41b8",
  37. version: "2.0",
  38. });
  39. const map = new AMap.Map(mapEl.value, {
  40. WebGLParams: {
  41. preserveDrawingBuffer: true,
  42. },
  43. resizeEnable: true,
  44. });
  45. const placeSearch = new AMap.PlaceSearch({
  46. pageSize: 5,
  47. pageIndex: 1,
  48. map: map,
  49. panel: resultEl.value,
  50. autoFitView: true,
  51. });
  52. const getMapInfo = (): MapInfo => {
  53. var zoom = map.getZoom(); //获取当前地图级别
  54. var center = map.getCenter();
  55. return {
  56. zoom,
  57. lat: center.lat,
  58. lng: center.lng,
  59. };
  60. };
  61. //绑定地图移动与缩放事件
  62. map.on("moveend", () => (info.value = getMapInfo()));
  63. map.on("zoomend", () => (info.value = getMapInfo()));
  64. searchAMap.value = placeSearch;
  65. onCleanup(() => {
  66. searchAMap.value = null;
  67. map.destroy();
  68. });
  69. });
  70. const search = debounce((keyword: string) => {
  71. searchAMap.value.search(keyword);
  72. }, 1000);
  73. watchEffect(() => {
  74. searchAMap.value && search(keyword.value);
  75. });
  76. defineExpose<QuiskExpose>({
  77. submit() {
  78. return new Promise<MapImage>((resolve) => {
  79. if (mapEl.value) {
  80. const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;
  81. canvas.toBlob((blob) => resolve({ blob }));
  82. } else {
  83. resolve({ blob: null });
  84. }
  85. });
  86. },
  87. });
  88. </script>
  89. <style lang="scss" scoped>
  90. .search-layout {
  91. display: inline-block;
  92. position: relative;
  93. margin-bottom: 15px;
  94. z-index: 2;
  95. }
  96. .search-result {
  97. position: absolute;
  98. left: 0;
  99. right: 0;
  100. z-index: 1;
  101. overflow: hidden;
  102. &.show {
  103. max-height: 450px;
  104. overflow-y: auto;
  105. }
  106. }
  107. .def-map-info {
  108. margin-top: 10px;
  109. p {
  110. font-size: 14px;
  111. color: rgba(0, 0, 0, 0.85);
  112. display: inline;
  113. &:not(:last-child)::after {
  114. content: ",";
  115. margin-right: 6px;
  116. }
  117. }
  118. span::after {
  119. content: ":";
  120. }
  121. }
  122. .def-select-map {
  123. width: 540px;
  124. height: 390px;
  125. z-index: 1;
  126. }
  127. </style>