Sfoglia il codice sorgente

Merge branch 'master' into Local_2.0.0

tangning 5 mesi fa
parent
commit
fbb2c5d240

+ 2 - 0
src/store/caseFile.ts

@@ -76,6 +76,7 @@ export type SaveCaseFileImageInfo = Pick<CaseFile, "caseId" | "filesTitle"> & {
   imgType: BoardType;
   content: BoardData;
   file: File;
+  ognFileUrl?: string;
 };
 
 export const saveCaseFileImageInfo = async (props: SaveCaseFileImageInfo) =>
@@ -229,5 +230,6 @@ export type BoardShapeData =
 
 export interface BoardData {
   id: number;
+  ognFilesUrl?: string | null;
   shapes: BoardShapeData[];
 }

+ 6 - 1
src/util/index.ts

@@ -217,10 +217,15 @@ export const urlToBlob = async (url: string) => {
   try {
     const response = await fetch(url, { mode: 'no-cors' }); // 获取文件内容
     const blob = await response.blob(); // 转成 blob
+    console.log('urlToBlob', blob);
+    if(blob.type == 'text/html'){
+      throw "暂未获取到平面图,请前往三维场景制作并保存";
+      return
+    }
     return blob;
   } catch (error) {
     // 请求出错,说明图片不存在
-    alert("暂未获取到平面图,请前往三维场景制作");
+    alert("暂未获取到平面图,请前往三维场景制作并保存");
   }
 }
 export const base64ToBlob = (base64Data: string) => {

+ 2 - 1
src/view/case/draw/board/index.js

@@ -19,7 +19,7 @@ export const create = async (store, canvas) => {
     baseMap: null,
   };
 
-  console.log(store)
+  console.log('ssssss', store)
   setTimeout(() => {
     console.log(!!store.floors[0].bgImage)
     refs.bus.emit('exixtsBgImage', !!store.floors[0].bgImage)
@@ -112,6 +112,7 @@ export const create = async (store, canvas) => {
       return {
         id: store.id,
         shapes,
+        ognFilesUrl: store.ognFilesUrl,
         ...layer.uiControl.exportJSON(),
       };
     },

+ 10 - 0
src/view/case/draw/board/useBoard.ts

@@ -71,9 +71,14 @@ const getStore = async (caseId: number, fileId: number, type: BoardType) => {
     if (fileInfo) {
       data = {
         ...fileInfo.content,
+        ognFilesUrl: fileInfo.ognFilesUrl,
         id: fileInfo.filesId,
       };
+<<<<<<< HEAD
       console.log("fileInfo", data, fileInfo.content);
+=======
+      console.log('fileInfo', fileInfo, data);
+>>>>>>> master
     } else {
       router.replace({ name: RouteName.caseFile, params: { caseId } });
       throw "该图不存在!";
@@ -118,8 +123,13 @@ export const useBoard = (props: Ref<BoardProps | null>) => {
       props.value.fileId,
       props.value.type
     );
+    console.log('store', store);
     const boardRaw = (board.value = await boardFactory(store, props.value.dom));
+<<<<<<< HEAD
     console.log("watchEffect", props, boardRaw);
+=======
+    console.log('boardRaw', boardRaw);
+>>>>>>> master
     state.value.exixtsBgImage = !!(store as any).floors[0].bgImage
     onCleanup(() => {
       boardRaw && boardRaw.destroy();

+ 11 - 4
src/view/case/draw/index.vue

@@ -65,6 +65,7 @@ const list = ref({
 });
 const fmtId = ref(0);
 const pmtId = ref(0);
+const ognFilesUrl = ref('')
 const dom = ref<HTMLCanvasElement>();
 const props = computed(() => {
   const route = router.currentRoute.value;
@@ -115,7 +116,10 @@ const trackImage = async () => {
     props.value!.type === BoardType.scene
       ? await selectFuseImage(props.value!)
       : await selectMapImage({});
-
+  console.log('户型图', data);
+  if (data?.ognFilesUrl) {
+    ognFilesUrl.value = data.ognFilesUrl;
+  }
   if (data?.blob) {
     setBackImage(data.blob);
     if ("taggings" in data) {
@@ -134,18 +138,20 @@ console.log('useBoard', board, state)
 // 获取通用数据
 const getStore = async () => {
   const store = await board.value!.getStore();
+  console.log('getStore', store, board.value, state.value);
   const titleShape = store.shapes.find(
     (shape: any) => shape.type === title
   ) as TitleShapeData;
-  return { store, titleShape };
+  return { store, titleShape, ognFilesUrl: store.ognFilesUrl };
 };
 
 //裁剪
 const handleCropping = async (data) => {
   const appStore = await getStore();
-  console.log('titleShape', appStore, board.value, state.value);
+  const args = props.value!;
+  let imgUrl = args.inAdd ? ognFilesUrl.value : appStore.store.ognFilesUrl;
   const {width, height } = appStore.store.floors?.[0].bgImage;
-  const blob = await fetch(state.value.selectShape.data.url).then(res => res.blob());
+  const blob = await fetch(imgUrl || state.value.selectShape.data.url).then(res => res.blob());
   const cropBlob = await imageCropper({
       img: blob,
       fixed: [width, height]
@@ -170,6 +176,7 @@ const saveHandler = async () => {
     file: new File([blob], `${filesTitle}.jpg`),
     filesTitle: filesTitle,
     content: store && JSON.stringify(store),
+    ognFileUrl: ognFilesUrl.value || store.ognFilesUrl,
   };
   args.inAdd || (body.filesId = props.value!.fileId);
   const { data } = await uploadNewFile(body);

+ 13 - 9
src/view/case/draw/selectFuseImage.vue

@@ -57,11 +57,12 @@ import {
 } from "@/view/case/help";
 import { computed, nextTick, onMounted, ref, watchEffect } from "vue";
 import { QuiskExpose } from "@/helper/mount";
+import { imageCropper } from "@/view/system/quisk";
 
 export type FuseImage = { blob: Blob | null; taggings: CaseTagging[] };
 const props = defineProps<{ caseId: number }>();
-console.log('fuseUrl', props);
-const fuseUrl =  computed(() => getQuery(props.caseId, true, true));
+const ognFilesUrl = ref<string | null>(null);
+const fuseUrl = computed(() => getQuery(props.caseId, true, true));
 
 const taggings = ref<CaseTagging[]>([]);
 const transferSource = computed(() =>
@@ -94,29 +95,32 @@ const refreshBlob = async () => {
   const width = Math.ceil(540 * scale);
   const height = Math.ceil(390 * scale);
   const fuseImage = await getFuseImage(iframeRef.value, width, height);
-
+  if(fuseImage?.ognFilesUrl) {
+    ognFilesUrl.value = fuseImage?.ognFilesUrl;
+  }
   if (fuseImage?.blob) {
     console.log('fuseImage', fuseImage?.blob);
-
+    // const cropBlob = await imageCropper({
+    //   img: fuseImage?.blob,
+    //   fixed: [width, height]
+    //  })
     imageBlob.value =
       fuseImage.type !== FuseImageType.FUSE
-        ? fuseImage.blob
+        ? fuseImage?.blob
         : await fuseImageJoinHot(
             iframeRef.value,
-            fuseImage.blob,
+            fuseImage?.blob,
             selectTaggings.value,
             width,
             height,
             scale
           );
-    console.log('fuseImage2', imageBlob.value);
-
   }
 };
 
 defineExpose<QuiskExpose>({
   submit() {
-    return { blob: imageBlob.value, taggings: selectTaggings.value };
+    return { blob: imageBlob.value, taggings: selectTaggings.value, ognFilesUrl: ognFilesUrl.value };
   },
 });
 

+ 3 - 3
src/view/case/draw/selectMapImage.vue

@@ -43,8 +43,8 @@ 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://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')
 
 
@@ -79,7 +79,7 @@ onMounted(async () => {
   // 'map'为HTML节点id
   map = L.map(mapEl.value, {
     center: center,//中心坐标
-    zoom: 10,//缩放级别
+    zoom: 14,//缩放级别
     zoomControl: true, //缩放组件
     attributionControl: false, //去掉右下角logol
     layers: [layer],//图层

+ 336 - 0
src/view/case/draw/selectMapImagess.vue

@@ -0,0 +1,336 @@
+<template>
+  <div class="search-layout">
+    <el-input
+      v-model="keyword"
+      placeholder="输入名称搜索"
+      style="width: 350px"
+      clearable
+    >
+      <template #append>
+        <el-button :icon="Search" />
+      </template>
+    </el-input>
+    <div class="rrr">
+      <div
+        class="search-result"
+        v-show="keyword && showSearch"
+        ref="resultEl"
+      ></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">
+    <GoogleMap v-if="mapType=='google'"
+    :api-key="ggMapKey"
+    class="def-select-map"
+    :center="center"
+    :zoom="15"
+    >
+        <Marker :options="{ position: center }" />
+    </GoogleMap>
+    <div v-else class="def-select-map" ref="mapEl"></div>
+  </div>
+
+  <div class="def-map-info" v-if="info">
+    <p><span>纬度</span>{{ info.lat }}</p>
+    <p><span>经度</span>{{ info.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 { ref, watchEffect, onMounted, computed } from "vue";
+import { GoogleMap, Marker } from 'vue3-google-map'
+import { getTipsList, getTipsNames, getCaseInfo } from "@/store/case";
+import { QuiskExpose } from "@/helper/mount";
+import { debounce } from "@/util";
+import html2canvas from "html2canvas";
+import { router } from "@/router";
+import L from "leaflet";
+import "leaflet/dist/leaflet.css";
+export type MapImage = { blob: Blob | null; search: MapInfo | null };
+type MapInfo = { lat: number; lng: number; zoom: number; text: string };
+
+const keyword = ref("");
+const ggMapKey = ref("AIzaSyBGUvCR1bppO9pfuS0MUWzuftiZ127y4Os");
+const showSearch = ref(true);
+const info = ref<MapInfo>();
+const searchInfo = ref<MapInfo>();
+const caseInfoData = ref<any>(null);
+const mapType = ref("gaode");
+const center = ref({
+  lat: 113.0,
+  lng: 22.61,
+});
+const caseId = computed(() => {
+  const caseId = router.currentRoute.value.params.caseId;
+  if (caseId) {
+    return Number(caseId);
+  }
+});
+
+watchEffect(() => {
+  if (keyword.value) {
+    showSearch.value = true;
+  }
+});
+
+const mapEl = ref<HTMLDivElement>();
+const resultEl = ref<HTMLDivElement>();
+const searchAMap = ref<any>();
+
+const renderMapGd = async () => {
+  const AMap = await AMapLoader.load({
+    plugins: ["AMap.PlaceSearch", "AMap.Event"],
+    key: "e661b00bdf2c44cccf71ef6070ef41b8",
+    version: "2.0",
+  });
+  const map = new AMap.Map(mapEl.value, {
+    WebGLParams: {
+      preserveDrawingBuffer: true,
+    },
+    resizeEnable: true,
+    center: [center.value.lat, center.value.lng], //中心坐标
+    zoom: 10, //缩放级别
+  });
+  const placeSearch = new AMap.PlaceSearch({
+    pageSize: 5,
+    showCover: false,
+    pageIndex: 1,
+    map: map,
+    panel: resultEl.value,
+    autoFitView: true,
+  });
+  const setSearch = (data) => {
+    console.log("setSearch", data);
+    let city =
+      data.cityname == data.adname
+        ? data.cityname
+        : data.cityname + data.adname;
+    searchInfo.value = {
+      text: data.pname + city + data.address,
+      lat: data.location.lat,
+      lng: data.location.lng,
+      zoom: 0,
+    };
+  };
+
+  placeSearch.on("listElementClick", (e) => {
+    setSearch(e.data);
+    showSearch.value = false;
+  });
+  let clickMarker;
+  //绑定地图移动与缩放事件
+  map.on("moveend", () => {
+    info.value = getMapInfo();
+  });
+  map.on("zoomend", () => {
+    info.value = getMapInfo();
+  });
+  map.on("click", async function (e) {
+    // 获取点击位置的经纬度坐标
+    const KEY = "e661b00bdf2c44cccf71ef6070ef41b8";
+    var latitude = e.lnglat.lat;
+    var longitude = e.lnglat.lng;
+    let location = [longitude, latitude];
+    const res = await regeocoder(location);
+    console.log(e, res, "function");
+    searchInfo.value = {
+      text: "",
+      lat: latitude,
+      lng: longitude,
+      zoom: 0,
+    };
+    clickMarker && map.remove(clickMarker);
+    clickMarker = null;
+    // 在地图上添加标记
+    clickMarker = new AMap.Marker({
+      position: [longitude, latitude],
+      title: "点击位置",
+    });
+
+    map.add(clickMarker);
+  });
+  placeSearch.on("complete", function (result) {
+    setTimeout(() => {
+      const markers = map.getAllOverlays("marker");
+      for (const marker of markers) {
+        marker.on("click", () => {
+          clickMarker && map.remove(clickMarker);
+          clickMarker = null;
+          setSearch(marker._data);
+        });
+      }
+    }, 500);
+  });
+  async function regeocoder(lnglatXY) {
+    return AMap.plugin("AMap.Geocoder", function () {
+      var geocoder = new AMap.Geocoder({
+        radius: 1000,
+        extensions: "all",
+      });
+      return geocoder.getAddress(lnglatXY, function (status, result) {
+        if (status === "complete" && result.info === "OK") {
+          console.log(
+            result.regeocode.formattedAddress,
+            "result.regeocode.formattedAddress"
+          );
+          searchInfo.value.text = result.regeocode.formattedAddress;
+          return result.regeocode.formattedAddress;
+        }
+      });
+      // if(!marker){
+      //     marker = new AMap.Marker();
+      //     map.add(marker);
+      // }
+      // marker.setPosition(lnglatXY);
+    });
+  }
+  const getMapInfo = (): MapInfo => {
+    var zoom = map.getZoom(); //获取当前地图级别
+    var centers = map.getCenter();
+    return {
+      text: "",
+      zoom,
+      lat: centers.lat,
+      lng: centers.lng,
+    };
+  };
+  searchAMap.value = placeSearch;
+};
+const renderGgMapGd = async () => {
+  // const map = new google.maps.Map(mapEl.value, {
+  //   zoom: 10,
+  //   center: center.value,
+  //   mapTypeId: "roadmap",
+  // });
+};
+onMounted(async () => {
+  caseInfoData.value = await getCaseInfo(caseId.value);
+  if (caseInfoData.value?.latAndLong) {
+    let centerObj = caseInfoData.value.latAndLong.split(",").reverse()
+    center.value.lat = parseFloat(centerObj[0]);
+    center.value.lng = parseFloat(centerObj[1]);
+  }
+  renderMapGd()
+  renderGgMapGd()
+});
+watchEffect(async (onCleanup) => {
+  if (!mapEl.value || !resultEl.value) {
+    return;
+  }
+
+  // onCleanup(() => {
+  //   searchAMap.value = null;
+  //   map.destroy();
+  // });
+});
+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);
+watchEffect(() => {
+  searchAMap.value && search(keyword.value);
+});
+
+defineExpose<QuiskExpose>({
+  submit() {
+    return new Promise<MapImage>((resolve) => {
+      console.log("searchInfo", searchInfo.value, mapEl.value);
+      if (mapEl.value) {
+        const canvas = mapEl.value.querySelector("canvas") as HTMLCanvasElement;
+        console.log(canvas, "canvas");
+        canvas &&
+          canvas.toBlob((blob) => resolve({ blob, search: searchInfo.value! })); // || 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! });
+          });
+        }
+      } 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;
+  }
+}
+
+.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>

+ 5 - 1
src/view/case/help.ts

@@ -189,6 +189,10 @@ export const getFuseImage = async (
   const typeMap = await getIframeSceneType(iframe);
   let blob: Blob | null = null;
   let newUrl: string | null = null;
+<<<<<<< HEAD
+=======
+
+>>>>>>> master
   switch (typeMap?.type) {
     case FuseImageType.FUSE:
       console.error(width, height);
@@ -241,7 +245,7 @@ export const getFuseImage = async (
       });
       break;
   }
-  return { type: typeMap?.type, blob };
+  return { type: typeMap?.type, blob, ognFilesUrl: newUrl };
 };
 
 // 处理fuse融合一起的图,将热点加入图片内