selectMapImagess.vue 8.4 KB

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