selectMapleaftImages.vue 8.0 KB

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