|
@@ -0,0 +1,361 @@
|
|
|
+<template>
|
|
|
+ <div class="search-layout">
|
|
|
+ <el-input
|
|
|
+ v-model="keyword"
|
|
|
+ placeholder="输入名称搜索"
|
|
|
+ style="width: 350px"
|
|
|
+ clearable
|
|
|
+ @change="onSearch"
|
|
|
+ >
|
|
|
+ <!-- <template #append>
|
|
|
+ <el-button :icon="Search" />
|
|
|
+ </template> -->
|
|
|
+ </el-input>
|
|
|
+ <div class="rrr">
|
|
|
+ <div class="search-result" v-show="keyword">
|
|
|
+ <div
|
|
|
+ class="search-list"
|
|
|
+ v-for="(item, index) in keywordList"
|
|
|
+ @click="hanleItem(item.name)"
|
|
|
+ >
|
|
|
+ {{ item.name }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- <div class="search-sh" v-show="keyword">
|
|
|
+ <el-button style="width: 100%" @click="showSearch = !showSearch">
|
|
|
+ {{ showSearch ? "收起" : "展开" }}搜索结果
|
|
|
+ </el-button>
|
|
|
+ </div> -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="def-select-map-layout">
|
|
|
+ <div class="def-select-map" id="mapEl" ref="mapEl"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="def-map-info" v-if="searchInfo">
|
|
|
+ <p><span>纬度</span>{{ searchInfo.lat }}</p>
|
|
|
+ <p><span>经度</span>{{ searchInfo.lng }}</p>
|
|
|
+ <!-- <p><span>缩放级别</span>{{ info.zoom }}</p> -->
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import AMapLoader from "@amap/amap-jsapi-loader";
|
|
|
+import { Search } from "@element-plus/icons-vue";
|
|
|
+import { wgs84_to_gcj02 } from "./map";
|
|
|
+import { getTipsList, getTipsNames, getCaseInfo } from "@/store/case";
|
|
|
+import { ref, watchEffect, onMounted, computed } from "vue";
|
|
|
+import { QuiskExpose } from "@/helper/mount";
|
|
|
+import { debounce } from "@/util";
|
|
|
+import html2canvas from "html2canvas";
|
|
|
+import L from "leaflet";
|
|
|
+import "leaflet.chinatmsproviders";
|
|
|
+import "leaflet/dist/leaflet.css";
|
|
|
+import { router } from "@/router";
|
|
|
+export type MapImage = { blob: Blob | null; search: MapInfo | null };
|
|
|
+type MapInfo = {
|
|
|
+ lat: number;
|
|
|
+ lng: number;
|
|
|
+ zoom: number;
|
|
|
+ text: string;
|
|
|
+ address: string;
|
|
|
+ id: string;
|
|
|
+};
|
|
|
+// 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}')
|
|
|
+// const layer = L.tileLayer("http://a.map.jms.gd/tile/osm/{z}/{x}/{y}.png");
|
|
|
+// 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')
|
|
|
+var normalMap = L.tileLayer.chinaProvider("Google.Normal.Map", {
|
|
|
+ maxZoom: 18,
|
|
|
+ minZoom: 5,
|
|
|
+});
|
|
|
+var Gaode = L.tileLayer.chinaProvider("GaoDe.Normal.Map", {
|
|
|
+ maxZoom: 18,
|
|
|
+ minZoom: 5,
|
|
|
+});
|
|
|
+var baseLayers = {
|
|
|
+ '谷歌地图': normalMap,
|
|
|
+ '高德地图': Gaode,
|
|
|
+};
|
|
|
+
|
|
|
+let map: any = {};
|
|
|
+let clickMarker;
|
|
|
+const keyword = ref("");
|
|
|
+const showSearch = ref(true);
|
|
|
+const info = ref<MapInfo>();
|
|
|
+const caseInfoData = ref<any>(null);
|
|
|
+const searchInfo = ref<MapInfo>();
|
|
|
+const mapEl = ref<HTMLDivElement>();
|
|
|
+const keywordList = ref([]);
|
|
|
+watchEffect(() => {
|
|
|
+ if (keyword.value) {
|
|
|
+ showSearch.value = true;
|
|
|
+ }
|
|
|
+});
|
|
|
+const caseId = computed(() => {
|
|
|
+ const caseId = router.currentRoute.value.params.caseId;
|
|
|
+ if (caseId) {
|
|
|
+ return Number(caseId);
|
|
|
+ }
|
|
|
+});
|
|
|
+onMounted(async () => {
|
|
|
+ caseInfoData.value = {};
|
|
|
+ let center = [22.61, 113.05];
|
|
|
+ if (caseInfoData.value?.latAndLong) {
|
|
|
+ center = caseInfoData.value.latAndLong.split(",");
|
|
|
+ }
|
|
|
+ console.log("caseInfoData", caseInfoData.value.latAndLong, center);
|
|
|
+
|
|
|
+ // 'map'为HTML节点id
|
|
|
+ map = L.map(mapEl.value, {
|
|
|
+ center: center, //中心坐标
|
|
|
+ zoom: 14, //缩放级别
|
|
|
+ zoomControl: true, //缩放组件
|
|
|
+ attributionControl: false, //去掉右下角logol
|
|
|
+ layers: [Gaode], //图层
|
|
|
+ // center: [51.505, -0.09],
|
|
|
+ // zoom: 13
|
|
|
+ });
|
|
|
+ L.control.layers(baseLayers, null).addTo(map);
|
|
|
+ map.on("click", function (e) {
|
|
|
+ // 获取点击位置的经纬度坐标
|
|
|
+ var latitude = e.latlng.lat;
|
|
|
+ var longitude = e.latlng.lng;
|
|
|
+ console.log("click", e, [longitude, latitude]);
|
|
|
+
|
|
|
+ searchInfo.value = {
|
|
|
+ text: "",
|
|
|
+ lat: latitude,
|
|
|
+ lng: longitude,
|
|
|
+ zoom: 0,
|
|
|
+ };
|
|
|
+ clickMarker && clickMarker.remove();
|
|
|
+ clickMarker = null;
|
|
|
+ // 在地图上添加标记
|
|
|
+ clickMarker = L.marker([latitude, longitude], {
|
|
|
+ position: [latitude, longitude],
|
|
|
+ title: "点击位置",
|
|
|
+ });
|
|
|
+ clickMarker.addTo(map);
|
|
|
+ map.panTo([latitude, longitude]);
|
|
|
+ // map.add(clickMarker);
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+const resultEl = ref<HTMLDivElement>();
|
|
|
+const searchAMap = ref<any>();
|
|
|
+
|
|
|
+watchEffect(async (onCleanup) => {
|
|
|
+ if (!mapEl.value || !resultEl.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //绑定地图移动与缩放事件
|
|
|
+ map.on("moveend", () => {
|
|
|
+ info.value = getMapInfo();
|
|
|
+ });
|
|
|
+ map.on("zoomend", () => {
|
|
|
+ info.value = getMapInfo();
|
|
|
+ });
|
|
|
+ searchAMap.value = placeSearch;
|
|
|
+
|
|
|
+ onCleanup(() => {
|
|
|
+ searchAMap.value = null;
|
|
|
+ map.destroy();
|
|
|
+ });
|
|
|
+});
|
|
|
+const getMapInfo = (): MapInfo => {
|
|
|
+ var zoom = map.getZoom(); //获取当前地图级别
|
|
|
+ var center = map.getCenter();
|
|
|
+ return {
|
|
|
+ text: "",
|
|
|
+ zoom,
|
|
|
+ lat: center.lat,
|
|
|
+ lng: center.lng,
|
|
|
+ };
|
|
|
+};
|
|
|
+const onSearch = (val) => {
|
|
|
+ // getGaoDeGaoDeList(val).then((res) => {
|
|
|
+ // console.log("getTipsList", res);
|
|
|
+ // keywordList.value = res.data;
|
|
|
+ // });
|
|
|
+ console.log("onSearch", val, "keyword.value", keyword.value);
|
|
|
+};
|
|
|
+const hanleItem = (name) => {
|
|
|
+ // keyword.value = item.name;
|
|
|
+ getTipsNames(name).then((ress) => {
|
|
|
+ let res = ress.data;
|
|
|
+ // longlat = wgs84_to_gcj02(Number(res.lng),Number(res.lat))
|
|
|
+ keyword.value = "";
|
|
|
+ searchInfo.value = {
|
|
|
+ ...res,
|
|
|
+ // lng: longlat[0],
|
|
|
+ // lat: longlat[1],
|
|
|
+ text: res.name,
|
|
|
+ };
|
|
|
+ clickMarker && clickMarker.remove();
|
|
|
+ // let icon = L.icon({
|
|
|
+ // iconUrl: require('./icon.svg'),
|
|
|
+ // iconSize: [25, 30],
|
|
|
+ // iconAnchor: [12, 30]
|
|
|
+ // });
|
|
|
+ clickMarker = null;
|
|
|
+
|
|
|
+ // 在地图上添加标记
|
|
|
+ clickMarker = L.marker([res.lat, res.lng], {
|
|
|
+ position: [res.lat, res.lng],
|
|
|
+ title: "点击位置",
|
|
|
+ // icon,
|
|
|
+ });
|
|
|
+ clickMarker.addTo(map);
|
|
|
+ map.panTo([res.lat, res.lng]);
|
|
|
+ });
|
|
|
+ // onSearch(item.name);
|
|
|
+};
|
|
|
+var dataURLtoBlob = function (dataurl) {
|
|
|
+ var arr = dataurl.split(","),
|
|
|
+ mime = arr[0].match(/:(.*?);/)[1],
|
|
|
+ bstr = atob(arr[1]),
|
|
|
+ n = bstr.length,
|
|
|
+ u8arr = new Uint8Array(n);
|
|
|
+ while (n--) {
|
|
|
+ u8arr[n] = bstr.charCodeAt(n);
|
|
|
+ }
|
|
|
+ return new Blob([u8arr], { type: mime });
|
|
|
+};
|
|
|
+const search = debounce((keyword: string) => {
|
|
|
+ searchAMap.value.search(keyword);
|
|
|
+}, 1000);
|
|
|
+const getblc = function() {
|
|
|
+ const size = map.getSize();
|
|
|
+ // 获取地图容器相对于视口的位置
|
|
|
+const container = map.getContainer();
|
|
|
+const position = container.getBoundingClientRect();
|
|
|
+
|
|
|
+const box = {
|
|
|
+ width: size.x,
|
|
|
+ height: size.y,
|
|
|
+ left: position.left,
|
|
|
+ top: position.top,
|
|
|
+ right: position.left + size.x,
|
|
|
+ bottom: position.top + size.y
|
|
|
+};
|
|
|
+console.log('getblc',box);
|
|
|
+const bounds = map.getBounds();
|
|
|
+// 获取东北角和西北角(计算高度)
|
|
|
+const northEast = bounds.getNorthEast();
|
|
|
+const northWest = L.latLng(northEast.lat, bounds.getSouthWest().lng);
|
|
|
+
|
|
|
+// 获取西南角和东南角(计算宽度)
|
|
|
+const southWest = bounds.getSouthWest();
|
|
|
+const southEast = L.latLng(southWest.lat, northEast.lng);
|
|
|
+
|
|
|
+// 计算宽度(东西方向距离)
|
|
|
+const widthInMeters = northEast.distanceTo(northWest);
|
|
|
+
|
|
|
+// 计算高度(南北方向距离)
|
|
|
+const heightInMeters = northEast.distanceTo(southEast);
|
|
|
+
|
|
|
+const geoBox = {
|
|
|
+ width: widthInMeters, // 宽度(米)
|
|
|
+ height: heightInMeters, // 高度(米)
|
|
|
+ center: bounds.getCenter(),
|
|
|
+ zoom: map.getZoom()
|
|
|
+};
|
|
|
+
|
|
|
+console.log('getblc',geoBox);
|
|
|
+return geoBox
|
|
|
+}
|
|
|
+watchEffect(() => {
|
|
|
+ searchAMap.value && search(keyword.value);
|
|
|
+});
|
|
|
+
|
|
|
+defineExpose<QuiskExpose>({
|
|
|
+ submit() {
|
|
|
+ return new Promise<MapImage>((resolve) => {
|
|
|
+ console.log("searchInfo", searchInfo.value, mapEl.value);
|
|
|
+ const info = getblc();
|
|
|
+ if (mapEl.value) {
|
|
|
+ const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;
|
|
|
+ console.log(canvas, "canvas");
|
|
|
+ canvas &&
|
|
|
+ canvas.toBlob((blob) => resolve({ blob, search: searchInfo.value!, ...info })); // || resolve({ search: searchInfo.value! });
|
|
|
+ if (!canvas) {
|
|
|
+ //div内容生成图片
|
|
|
+ html2canvas(mapEl.value, {
|
|
|
+ useCORS: true, // 添加这个选项以解决跨域问题
|
|
|
+ }).then((canvas) => {
|
|
|
+ let imgUrl = canvas.toDataURL("image/png");
|
|
|
+ let blob = dataURLtoBlob(imgUrl);
|
|
|
+ resolve({ blob, search: searchInfo.value!, ...info });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ resolve({ blob: null, search: null });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.search-layout {
|
|
|
+ display: inline-block;
|
|
|
+ position: relative;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ z-index: 2;
|
|
|
+}
|
|
|
+
|
|
|
+.rrr {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ z-index: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.search-sh,
|
|
|
+.search-result {
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ &.show {
|
|
|
+ max-height: 450px;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+ .search-list {
|
|
|
+ background: #fff;
|
|
|
+ padding-left: 10px;
|
|
|
+ line-height: 36px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.def-map-info {
|
|
|
+ margin-top: 10px;
|
|
|
+ p {
|
|
|
+ font-size: 14px;
|
|
|
+ color: rgba(0, 0, 0, 0.85);
|
|
|
+ display: inline;
|
|
|
+ &:not(:last-child)::after {
|
|
|
+ content: ",";
|
|
|
+ margin-right: 6px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ span::after {
|
|
|
+ content: ":";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.def-select-map-layout {
|
|
|
+ --scale: 1.5;
|
|
|
+ width: 100%;
|
|
|
+ padding-top: calc((390 / 540) * 100%);
|
|
|
+ position: relative;
|
|
|
+ z-index: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.def-select-map {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+</style>
|