1
0

selectMapImage.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <template>
  2. <div class="search-layout">
  3. <el-input v-model="keyword" placeholder="输入名称搜索" style="width: 350px" clearable @change="onSearch">
  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">
  10. <div class="search-list" v-for="item,index in keywordList" @click="hanleItem(item.name)">
  11. {{ item.name }}
  12. </div>
  13. </div>
  14. <!-- <div class="search-sh" v-show="keyword">
  15. <el-button style="width: 100%" @click="showSearch = !showSearch">
  16. {{ showSearch ? "收起" : "展开" }}搜索结果
  17. </el-button>
  18. </div> -->
  19. </div>
  20. </div>
  21. <div class="def-select-map-layout">
  22. <div class="def-select-map" id="mapEl" ref="mapEl"></div>
  23. </div>
  24. <div class="def-map-info" v-if="searchInfo">
  25. <p><span>纬度</span>{{ searchInfo.lat }}</p>
  26. <p><span>经度</span>{{ searchInfo.lng }}</p>
  27. <!-- <p><span>缩放级别</span>{{ info.zoom }}</p> -->
  28. </div>
  29. </template>
  30. <script setup lang="ts">
  31. import AMapLoader from "@amap/amap-jsapi-loader";
  32. import { Search } from "@element-plus/icons-vue";
  33. import { wgs84_to_gcj02 } from "./map"
  34. import { getTipsList, getTipsNames, getCaseInfo } from "@/store/case";
  35. import { ref, watchEffect, onMounted, computed } from "vue";
  36. import { QuiskExpose } from "@/helper/mount";
  37. import { debounce } from "@/util";
  38. import html2canvas from "html2canvas"
  39. import L from 'leaflet'
  40. import 'leaflet/dist/leaflet.css'
  41. import { router } from "@/router";
  42. export type MapImage = { blob: Blob | null; search: MapInfo | null };
  43. type MapInfo = { lat: number; lng: number; zoom: number; text: string; address: string; id: string };
  44. // const layer = L.tileLayer('http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}')
  45. const layer = L.tileLayer('http://a.map.jms.gd/tile/osm/{z}/{x}/{y}.png')
  46. // const layer = L.tileLayer('http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=6&x={x}&y={y}&z={z}&token=YOUR_API_KEY')
  47. let map: any = {}
  48. let clickMarker;
  49. const keyword = ref("");
  50. const showSearch = ref(true);
  51. const info = ref<MapInfo>();
  52. const caseInfoData = ref<any>(null);
  53. const searchInfo = ref<MapInfo>();
  54. const mapEl = ref<HTMLDivElement>();
  55. const keywordList = ref([]);
  56. watchEffect(() => {
  57. if (keyword.value) {
  58. showSearch.value = true;
  59. }
  60. });
  61. const caseId = computed(() => {
  62. const caseId = router.currentRoute.value.params.caseId;
  63. if (caseId) {
  64. return Number(caseId);
  65. }
  66. });
  67. onMounted(async () => {
  68. caseInfoData.value = await getCaseInfo(caseId.value);
  69. let center = [22.61, 113.05];
  70. if(caseInfoData.value.latAndLong){
  71. center = caseInfoData.value.latAndLong.split(",")
  72. }
  73. console.log("caseInfoData", caseInfoData.value.latAndLong, center);
  74. // 'map'为HTML节点id
  75. map = L.map(mapEl.value, {
  76. center: center,//中心坐标
  77. zoom: 14,//缩放级别
  78. zoomControl: true, //缩放组件
  79. attributionControl: false, //去掉右下角logol
  80. layers: [layer],//图层
  81. // center: [51.505, -0.09],
  82. // zoom: 13
  83. })
  84. map.on("click", function (e) {
  85. // 获取点击位置的经纬度坐标
  86. var latitude = e.latlng.lat;
  87. var longitude = e.latlng.lng;
  88. console.log('click', e, [longitude, latitude]);
  89. searchInfo.value = {
  90. text: "",
  91. lat: latitude,
  92. lng: longitude,
  93. zoom: 0,
  94. };
  95. clickMarker && clickMarker.remove();
  96. clickMarker = null;
  97. // 在地图上添加标记
  98. clickMarker = L.marker([latitude, longitude],{
  99. position: [latitude, longitude],
  100. title: "点击位置",
  101. });
  102. clickMarker.addTo(map);
  103. map.panTo([latitude, longitude])
  104. // map.add(clickMarker);
  105. });
  106. })
  107. const resultEl = ref<HTMLDivElement>();
  108. const searchAMap = ref<any>();
  109. watchEffect(async (onCleanup) => {
  110. if (!mapEl.value || !resultEl.value) {
  111. return;
  112. }
  113. // const AMap = await AMapLoader.load({
  114. // plugins: ["AMap.PlaceSearch", "AMap.Event"],
  115. // key: "e661b00bdf2c44cccf71ef6070ef41b8",
  116. // version: "2.0",
  117. // });
  118. // const map = new AMap.Map(mapEl.value, {
  119. // WebGLParams: {
  120. // preserveDrawingBuffer: true,
  121. // },
  122. // resizeEnable: true,
  123. // });
  124. // const placeSearch = new AMap.PlaceSearch({
  125. // pageSize: 5,
  126. // showCover: false,
  127. // pageIndex: 1,
  128. // map: map,
  129. // panel: resultEl.value,
  130. // autoFitView: true,
  131. // });
  132. // const setSearch = (data) => {
  133. // console.log('setSearch', data);
  134. // let city = data.cityname == data.adname? data.cityname : (data.cityname + data.adname);
  135. // searchInfo.value = {
  136. // text: data.pname + city + data.address,
  137. // lat: data.location.lat,
  138. // lng: data.location.lng,
  139. // zoom: 0,
  140. // };
  141. // };
  142. // placeSearch.on("listElementClick", (e) => {
  143. // setSearch(e.data);
  144. // showSearch.value = false;
  145. // });
  146. // placeSearch.on("complete", function (result) {
  147. // setTimeout(() => {
  148. // const markers = map.getAllOverlays("marker");
  149. // for (const marker of markers) {
  150. // marker.on("click", () => {
  151. // clickMarker && map.remove(clickMarker);
  152. // clickMarker = null;
  153. // setSearch(marker._data);
  154. // });
  155. // }
  156. // }, 500);
  157. // });
  158. //绑定地图移动与缩放事件
  159. map.on("moveend", () => {
  160. info.value = getMapInfo();
  161. });
  162. map.on("zoomend", () => {
  163. info.value = getMapInfo();
  164. });
  165. searchAMap.value = placeSearch;
  166. onCleanup(() => {
  167. searchAMap.value = null;
  168. map.destroy();
  169. });
  170. });
  171. const getMapInfo = (): MapInfo => {
  172. var zoom = map.getZoom(); //获取当前地图级别
  173. var center = map.getCenter();
  174. return {
  175. text: "",
  176. zoom,
  177. lat: center.lat,
  178. lng: center.lng,
  179. };
  180. };
  181. const onSearch = (val) => {
  182. getTipsList(val).then((res) => {
  183. console.log('getTipsList', res);
  184. keywordList.value = res.data
  185. });
  186. console.log('onSearch', val, 'keyword.value', keyword.value);
  187. };
  188. const hanleItem = (name) => {
  189. // keyword.value = item.name;
  190. getTipsNames(name).then((ress) => {
  191. let res = ress.data;
  192. // longlat = wgs84_to_gcj02(Number(res.lng),Number(res.lat))
  193. keyword.value = '';
  194. searchInfo.value = {
  195. ...res,
  196. // lng: longlat[0],
  197. // lat: longlat[1],
  198. text: res.name,
  199. }
  200. clickMarker && clickMarker.remove();
  201. // let icon = L.icon({
  202. // iconUrl: require('./icon.svg'),
  203. // iconSize: [25, 30],
  204. // iconAnchor: [12, 30]
  205. // });
  206. clickMarker = null;
  207. // 在地图上添加标记
  208. clickMarker = L.marker([res.lat, res.lng],{
  209. position: [res.lat, res.lng],
  210. title: "点击位置",
  211. // icon,
  212. });
  213. clickMarker.addTo(map);
  214. map.panTo([res.lat, res.lng])
  215. })
  216. // onSearch(item.name);
  217. }
  218. var dataURLtoBlob = function (dataurl){
  219. var arr = dataurl.split(','),
  220. mime = arr[0].match(/:(.*?);/)[1],
  221. bstr = atob(arr[1]),
  222. n = bstr.length,
  223. u8arr = new Uint8Array(n);
  224. while (n--) {
  225. u8arr[n] = bstr.charCodeAt(n);
  226. }
  227. return new Blob([u8arr], { type: mime })
  228. }
  229. const search = debounce((keyword: string) => {
  230. searchAMap.value.search(keyword);
  231. }, 1000);
  232. watchEffect(() => {
  233. searchAMap.value && search(keyword.value);
  234. });
  235. defineExpose<QuiskExpose>({
  236. submit() {
  237. return new Promise<MapImage>((resolve) => {
  238. console.log('searchInfo', searchInfo.value, mapEl.value);
  239. if (mapEl.value) {
  240. const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;
  241. console.log(canvas, 'canvas');
  242. canvas && canvas.toBlob((blob) => resolve({ blob, search: searchInfo.value! }))// || resolve({ search: searchInfo.value! });
  243. if(!canvas){
  244. //div内容生成图片
  245. html2canvas(mapEl.value, {
  246. useCORS: true, // 添加这个选项以解决跨域问题
  247. }).then((canvas) => {
  248. let imgUrl = canvas.toDataURL("image/png");
  249. let blob = dataURLtoBlob(imgUrl)
  250. resolve({ blob, search: searchInfo.value! });
  251. });
  252. }
  253. } else {
  254. resolve({ blob: null, search: null });
  255. }
  256. });
  257. },
  258. });
  259. </script>
  260. <style lang="scss" scoped>
  261. .search-layout {
  262. display: inline-block;
  263. position: relative;
  264. margin-bottom: 15px;
  265. z-index: 2;
  266. }
  267. .rrr {
  268. position: absolute;
  269. left: 0;
  270. right: 0;
  271. z-index: 1;
  272. }
  273. .search-sh,
  274. .search-result {
  275. overflow: hidden;
  276. &.show {
  277. max-height: 450px;
  278. overflow-y: auto;
  279. }
  280. .search-list{
  281. background: #fff;
  282. padding-left: 10px;
  283. line-height: 36px;
  284. }
  285. }
  286. .def-map-info {
  287. margin-top: 10px;
  288. p {
  289. font-size: 14px;
  290. color: rgba(0, 0, 0, 0.85);
  291. display: inline;
  292. &:not(:last-child)::after {
  293. content: ",";
  294. margin-right: 6px;
  295. }
  296. }
  297. span::after {
  298. content: ":";
  299. }
  300. }
  301. .def-select-map-layout {
  302. --scale: 1.5;
  303. width: 100%;
  304. padding-top: calc((390 / 540) * 100%);
  305. position: relative;
  306. z-index: 1;
  307. }
  308. .def-select-map {
  309. position: absolute;
  310. left: 0;
  311. top: 0;
  312. width: 100%;
  313. height: 100%;
  314. }
  315. </style>