selectMapImagess.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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, onMounted } from "vue";
  30. import { QuiskExpose } from "@/helper/mount";
  31. import { debounce } from "@/util";
  32. import html2canvas from "html2canvas"
  33. import L from 'leaflet'
  34. import 'leaflet/dist/leaflet.css'
  35. export type MapImage = { blob: Blob | null; search: MapInfo | null };
  36. type MapInfo = { lat: number; lng: number; zoom: number; text: string };
  37. let map = null;
  38. const keyword = ref("");
  39. const showSearch = ref(true);
  40. const info = ref<MapInfo>();
  41. const searchInfo = ref<MapInfo>();
  42. watchEffect(() => {
  43. if (keyword.value) {
  44. showSearch.value = true;
  45. }
  46. });
  47. const mapEl = ref<HTMLDivElement>();
  48. const resultEl = ref<HTMLDivElement>();
  49. const searchAMap = ref<any>();
  50. watchEffect(async (onCleanup) => {
  51. if (!mapEl.value || !resultEl.value) {
  52. return;
  53. }
  54. const AMap = await AMapLoader.load({
  55. plugins: ["AMap.PlaceSearch", "AMap.Event"],
  56. key: "e661b00bdf2c44cccf71ef6070ef41b8",
  57. version: "2.0",
  58. });
  59. map = new AMap.Map(mapEl.value, {
  60. WebGLParams: {
  61. preserveDrawingBuffer: true,
  62. },
  63. resizeEnable: true,
  64. });
  65. const placeSearch = new AMap.PlaceSearch({
  66. pageSize: 5,
  67. showCover: false,
  68. pageIndex: 1,
  69. map: map,
  70. panel: resultEl.value,
  71. autoFitView: true,
  72. });
  73. const setSearch = (data) => {
  74. console.log('setSearch', data);
  75. let city = data.cityname == data.adname? data.cityname : (data.cityname + data.adname);
  76. searchInfo.value = {
  77. text: data.pname + city + data.address,
  78. lat: data.location.lat,
  79. lng: data.location.lng,
  80. zoom: 0,
  81. };
  82. };
  83. placeSearch.on("listElementClick", (e) => {
  84. setSearch(e.data);
  85. showSearch.value = false;
  86. });
  87. let clickMarker;
  88. map.on("click", function (e) {
  89. // 获取点击位置的经纬度坐标
  90. var latitude = e.lnglat.lat;
  91. var longitude = e.lnglat.lng;
  92. searchInfo.value = {
  93. text: "",
  94. lat: latitude,
  95. lng: longitude,
  96. zoom: 0,
  97. };
  98. clickMarker && map.remove(clickMarker);
  99. clickMarker = null;
  100. // 在地图上添加标记
  101. clickMarker = new AMap.Marker({
  102. position: [longitude, latitude],
  103. title: "点击位置",
  104. });
  105. map.add(clickMarker);
  106. });
  107. placeSearch.on("complete", function (result) {
  108. setTimeout(() => {
  109. const markers = map.getAllOverlays("marker");
  110. for (const marker of markers) {
  111. marker.on("click", () => {
  112. clickMarker && map.remove(clickMarker);
  113. clickMarker = null;
  114. setSearch(marker._data);
  115. });
  116. }
  117. }, 500);
  118. });
  119. const getMapInfo = (): MapInfo => {
  120. var zoom = map.getZoom(); //获取当前地图级别
  121. var center = map.getCenter();
  122. return {
  123. text: "",
  124. zoom,
  125. lat: center.lat,
  126. lng: center.lng,
  127. };
  128. };
  129. //绑定地图移动与缩放事件
  130. map.on("moveend", () => {
  131. info.value = getMapInfo();
  132. });
  133. map.on("zoomend", () => {
  134. info.value = getMapInfo();
  135. });
  136. searchAMap.value = placeSearch;
  137. onCleanup(() => {
  138. searchAMap.value = null;
  139. map.destroy();
  140. });
  141. });
  142. var dataURLtoBlob = function (dataurl){
  143. var arr = dataurl.split(','),
  144. mime = arr[0].match(/:(.*?);/)[1],
  145. bstr = atob(arr[1]),
  146. n = bstr.length,
  147. u8arr = new Uint8Array(n);
  148. while (n--) {
  149. u8arr[n] = bstr.charCodeAt(n);
  150. }
  151. return new Blob([u8arr], { type: mime })
  152. }
  153. const search = debounce((keyword: string) => {
  154. searchAMap.value.search(keyword);
  155. }, 1000);
  156. const getblc = () => {
  157. console.log('map', map);
  158. if (!map) {
  159. ElMessage.error('地图未初始化')
  160. return
  161. }
  162. try {
  163. // 获取当前地图的可视区域边界
  164. const bounds = map.getBounds()
  165. const southwest = bounds.getSouthWest() // 西南角
  166. const northeast = bounds.getNorthEast() // 东北角
  167. const northwest = new (window as any).AMap.LngLat(southwest.lng, northeast.lat) // 西北角
  168. const southeast = new (window as any).AMap.LngLat(northeast.lng, southwest.lat) // 东南角
  169. // 使用高德地图的几何工具计算距离(单位:米)
  170. const AMap = (window as any).AMap
  171. // 计算宽度(东西方向距离)
  172. const width = AMap.GeometryUtil.distance(southwest, southeast)
  173. // 计算高度(南北方向距离)
  174. const height = AMap.GeometryUtil.distance(southwest, northwest)
  175. // 计算面积(平方米)
  176. const area = width * height
  177. // 获取当前缩放级别
  178. const zoom = map.getZoom()
  179. // 获取地图中心点
  180. const center = map.getCenter()
  181. const viewportInfo = {
  182. width: width, // 宽度(米)
  183. height: height, // 高度(米)
  184. area: Math.round(area), // 面积(平方米)
  185. zoom: zoom, // 缩放级别
  186. center: {
  187. lat: center.lat,
  188. lng: center.lng
  189. },
  190. bounds: {
  191. southwest: { lat: southwest.lat, lng: southwest.lng },
  192. northeast: { lat: northeast.lat, lng: northeast.lng }
  193. }
  194. }
  195. console.log('当前可视区域信息:', viewportInfo)
  196. // ElMessage.success(`
  197. // 可视区域信息:
  198. // 宽度:${viewportInfo.width.toLocaleString()} 米
  199. // 高度:${viewportInfo.height.toLocaleString()} 米
  200. // 面积:${viewportInfo.area.toLocaleString()} 平方米
  201. // 缩放级别:${viewportInfo.zoom.toFixed(2)}
  202. // `)
  203. return {
  204. width: viewportInfo.width,
  205. height: viewportInfo.height,
  206. }
  207. } catch (error) {
  208. console.error('计算可视区域失败:', error)
  209. ElMessage.error('计算可视区域失败')
  210. }
  211. }
  212. watchEffect(() => {
  213. searchAMap.value && search(keyword.value);
  214. });
  215. defineExpose<QuiskExpose>({
  216. submit() {
  217. return new Promise<MapImage>((resolve) => {
  218. const info = getblc();
  219. let firstElement = document.querySelector('.leaflet-control-container');
  220. firstElement.style.visibility = 'hidden';
  221. console.log('searchInfo',info, searchInfo.value, mapEl.value);
  222. if (mapEl.value) {
  223. // const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;
  224. // console.log(canvas, 'canvas');
  225. // canvas && canvas.toBlob((blob) => resolve({ blob, search: searchInfo.value! }))// || resolve({ search: searchInfo.value! });
  226. // if(!canvas){
  227. //div内容生成图片
  228. html2canvas(mapEl.value, {
  229. useCORS: true, // 添加这个选项以解决跨域问题
  230. }).then((canvas) => {
  231. let imgUrl = canvas.toDataURL("image/png");
  232. let blob = dataURLtoBlob(imgUrl)
  233. resolve({ blob, search: searchInfo.value!, ...info })// || resolve({ search: searchInfo.value!, ...info });
  234. });
  235. // }
  236. } else {
  237. resolve({ blob: null, search: null });
  238. }
  239. });
  240. },
  241. });
  242. </script>
  243. <style lang="scss" scoped>
  244. .search-layout {
  245. display: inline-block;
  246. position: relative;
  247. margin-bottom: 15px;
  248. z-index: 2;
  249. }
  250. .rrr {
  251. position: absolute;
  252. left: 0;
  253. right: 0;
  254. z-index: 1;
  255. }
  256. .search-sh,
  257. .search-result {
  258. overflow: hidden;
  259. &.show {
  260. max-height: 450px;
  261. overflow-y: auto;
  262. }
  263. }
  264. .def-map-info {
  265. margin-top: 10px;
  266. p {
  267. font-size: 14px;
  268. color: rgba(0, 0, 0, 0.85);
  269. display: inline;
  270. &:not(:last-child)::after {
  271. content: ",";
  272. margin-right: 6px;
  273. }
  274. }
  275. span::after {
  276. content: ":";
  277. }
  278. }
  279. .def-select-map-layout {
  280. --scale: 1.5;
  281. width: 100%;
  282. padding-top: calc((390 / 540) * 100%);
  283. position: relative;
  284. z-index: 1;
  285. }
  286. .def-select-map {
  287. position: absolute;
  288. left: 0;
  289. top: 0;
  290. width: 100%;
  291. height: 100%;
  292. }
  293. </style>