浏览代码

feat: 定制需求制作

bill 1 年之前
父节点
当前提交
88ad12c13c
共有 15 个文件被更改,包括 2902 次插入2494 次删除
  1. 1 1
      .env
  2. 1 1
      .env.development
  3. 1 1
      .env.production
  4. 1 1
      .env.uat
  5. 89 19
      src/lib/board/4dmap.d.ts
  6. 2599 2350
      src/lib/board/4dmap.js
  7. 21 5
      src/lib/board/4dmap.umd.cjs
  8. 6 2
      src/store/scene.ts
  9. 24 1
      src/util/index.ts
  10. 2 2
      src/view/login.vue
  11. 19 5
      src/view/map/coord.vue
  12. 16 25
      src/view/map/install.ts
  13. 19 47
      src/view/map/layout.vue
  14. 79 15
      src/view/pano/pano.vue
  15. 24 19
      src/view/scene-select.vue

+ 1 - 1
.env

@@ -1,4 +1,4 @@
 VITE_QJ_URL=https://test.4dkankan.com/panorama
-VITE_LASER_URL=https://uat-laser.4dkankan.com/uat
+VITE_LASER_URL=https://uat-laser.4dkankan.com/laser-4pc
 VITE_API=https://uat-sp.4dkankan.com/
 

+ 1 - 1
.env.development

@@ -1,3 +1,3 @@
 VITE_QJ_URL=https://test.4dkankan.com/panorama
-VITE_LASER_URL=https://uat-laser.4dkankan.com/uat
+VITE_LASER_URL=https://uat-laser.4dkankan.com/laser-4pc
 VITE_API=https://uat-sp.4dkankan.com/

+ 1 - 1
.env.production

@@ -1,3 +1,3 @@
 VITE_QJ_URL=https://test.4dkankan.com/panorama
-VITE_LASER_URL=https://uat-laser.4dkankan.com/uat
+VITE_LASER_URL=https://laser.4dkankan.com/laser-4pc
 VITE_API=https://uat-sp.4dkankan.com/

+ 1 - 1
.env.uat

@@ -1,3 +1,3 @@
 VITE_QJ_URL=https://test.4dkankan.com/panorama
-VITE_LASER_URL=https://uat-laser.4dkankan.com/uat
+VITE_LASER_URL=https://uat-laser.4dkankan.com/laser-4pc
 VITE_API=https://uat-sp.4dkankan.com/

文件差异内容过多而无法显示
+ 89 - 19
src/lib/board/4dmap.d.ts


文件差异内容过多而无法显示
+ 2599 - 2350
src/lib/board/4dmap.js


文件差异内容过多而无法显示
+ 21 - 5
src/lib/board/4dmap.umd.cjs


+ 6 - 2
src/store/scene.ts

@@ -160,8 +160,12 @@ export const SceneStatusDesc: { [key in SceneStatus]: string } = {
 
 export const boardData = ref<PolygonsAttrib & { id: string }>();
 export const refreshBoardData = async () => {
-  const data = (await getDrawingDetailFetch(String(relicsId.value)))
-    .data as PolygonsAttrib;
+  const res = await getDrawingDetailFetch(String(relicsId.value));
+  const data = (res?.data || {
+    points: [],
+    polygons: [],
+    lines: [],
+  }) as PolygonsAttrib;
 
   boardData.value = {
     ...data,

+ 24 - 1
src/util/index.ts

@@ -254,4 +254,27 @@ export const dateFormat = (date: Date, fmt: string) => {
   return fmt;
 };
 
-export * from './tree'
+let canvas: HTMLCanvasElement;
+let ctx: CanvasRenderingContext2D;
+export const getTextBound = (
+  text: string,
+  font: string,
+  padding: number[] = [0, 0],
+  margin: number[] = [0, 0]
+) => {
+  if (!canvas) {
+    canvas = document.createElement("canvas");
+    ctx = canvas.getContext("2d")!;
+  }
+
+  ctx.font = font;
+  const textMetrics = ctx.measureText(text);
+  const width = textMetrics.width + (padding[1] + margin[1]) * 2;
+  const fontHeight =
+    textMetrics.fontBoundingBoxAscent + textMetrics.fontBoundingBoxDescent;
+  const height = fontHeight + (padding[0] + margin[0]) * 2;
+
+  return { width, height };
+};
+
+export * from "./tree";

+ 2 - 2
src/view/login.vue

@@ -122,8 +122,8 @@ const verify = ref<any>();
 const isPassing2 = ref(false);
 // 表单
 const form = reactive({
-  phone: import.meta.env.DEV ? "99999999999" : "",
-  psw: import.meta.env.DEV ? "4Dage168" : "",
+  phone: import.meta.env.DEV ? "13800000001" : "",
+  psw: import.meta.env.DEV ? "88888888Sw" : "",
 });
 const verification = reactive({ phone: "", psw: "" });
 

+ 19 - 5
src/view/map/coord.vue

@@ -41,9 +41,12 @@
                   <el-icon>
                     <Grid />
                   </el-icon>
-                  {{ data.raw.sceneName }}
-
-                  <span class="tree-scene-name">{{ node.label }}</span>
+                  <span>
+                    <p>{{ data.raw.sceneName }}</p>
+                  </span>
+                  <span class="tree-scene-name">
+                    <p>{{ node.label }}</p>
+                  </span>
                 </span>
               </el-tooltip>
               <el-tooltip
@@ -253,6 +256,7 @@ const addHandler = async () => {
     scenes: scenes.value,
     selfScenes: scenes.value.filter((scene) => scene.creationMethod === 2),
     submit: async (nScene) => {
+
       const requests: Promise<any>[] = [];
       const delScenes = sceneCodes
         .filter((sceneCode) => !nScene.some((scene) => scene.sceneCode === sceneCode))
@@ -346,9 +350,12 @@ onBeforeUnmount(() => {
     overflow: hidden;
     display: inline-flex;
     align-items: center;
-    text-overflow: ellipsis; //文本溢出显示省略号
-    white-space: nowrap; //文本不会换行
     line-height: 26px;
+    margin-right: 10px;
+
+    p {
+      margin: 0;
+    }
   }
 
   .title-box {
@@ -401,5 +408,12 @@ onBeforeUnmount(() => {
   font-size: 10px;
   margin: 0;
   color: #999;
+  p {
+    margin: 0;
+    max-width: 90%;
+    text-overflow: ellipsis; //文本溢出显示省略号
+    overflow: hidden;
+    white-space: nowrap; //文本不会换行
+  }
 }
 </style>

+ 16 - 25
src/view/map/install.ts

@@ -1,6 +1,5 @@
 import { TileType, createMap, Manage } from "./openlayer";
 import { computed, ref, watch, watchEffect } from "vue";
-import ScaleLine from "ol/control/ScaleLine";
 import { createBoard } from "drawing-board";
 import { Scene, ScenePoint, boardData, relicsId, scenes } from "@/store/scene";
 import { router } from "@/router";
@@ -12,31 +11,9 @@ export const tileOptions: TileType[] = ["影像底图", "矢量底图"];
 export const tileType = ref<TileType>(tileOptions[0]);
 export const defaultCenter = [116.412611, 39.908866];
 
-const addScale = (mapManage: Manage) => {
-  const scaleLine = new ScaleLine({
-    className: "scale-view",
-    maxWidth: 150,
-    minWidth: 100,
-    units: "metric",
-  });
-  // 加载比例尺
-  mapManage.map.addControl(scaleLine);
-
-  watch(
-    tileType,
-    (type) => {
-      const el = (scaleLine as any).element as HTMLDivElement;
-      el.classList.add(type === "影像底图" ? "light" : "dark");
-      el.classList.remove(type === "影像底图" ? "dark" : "light");
-    },
-    { flush: "post", immediate: true }
-  );
-};
-
 export const mapManage = createMap();
 mapManage.setCenter(defaultCenter);
 watchEffect(() => mapManage.setTileType(tileType.value));
-addScale(mapManage);
 
 const noValidPoint = (pos: ScenePoint) => !pos.pos || pos.pos.length === 0;
 const validScene = (scene: Scene) => !scene.scenePos.every(noValidPoint);
@@ -99,8 +76,22 @@ export const boardDataChange = (dataChange?: () => void) => {
   });
 };
 
+watch(
+  tileType,
+  (type) => {
+    if (type === "影像底图") {
+      board.scale.setColor("#fff");
+    } else {
+      board.scale.setColor("#000");
+    }
+  },
+  { flush: "post", immediate: true }
+);
+
 // -----------status----------
 
-export const queryMode = computed(() =>
-  router.currentRoute.value.name.toString().includes("query")
+export const queryMode = computed(
+  () =>
+    router.currentRoute.value.name &&
+    router.currentRoute.value.name.toString().includes("query")
 );

+ 19 - 47
src/view/map/layout.vue

@@ -26,11 +26,13 @@
       <div class="map-container" :ref="setMapContainer">
         <div class="board" :ref="setBoardContainer"></div>
         <div class="map-top-out-pano">
-          <el-button @click="capture" v-if="loaded"> 屏幕截图 </el-button>
+          <template v-if="!isCoordPage">
+            <el-button @click="capture" v-if="loaded"> 提取位置图 </el-button>
+            <el-button @click="showPoints = !showPoints">
+              <el-checkbox :modelValue="showPoints" label="点位" size="large" />
+            </el-button>
+          </template>
 
-          <el-button @click="showPoints = !showPoints">
-            <el-checkbox :modelValue="showPoints" label="点位" size="large" />
-          </el-button>
           <div class="tile-select">
             <el-select
               v-model="tileType"
@@ -88,7 +90,7 @@ import {
   defaultCenter,
   tileType,
 } from "./install";
-import { nextTick, ref, watch } from "vue";
+import { computed, nextTick, ref, watch } from "vue";
 import { initRelics, relics } from "@/store/relics";
 import { queryMode } from "./install";
 import { PoPoint } from "drawing-board";
@@ -130,21 +132,25 @@ watch(
   { immediate: true }
 );
 
+const isCoordPage = computed(() => {
+  const name = router.currentRoute.value.name;
+  return name && [COORD_NAME, QUERY_COORD_NAME].includes(name.toString());
+});
 const showPoints = ref(true);
+
 watch(
-  () => [router.currentRoute.value.name, boardData.value, showPoints.value] as const,
-  ([name, _, showPoints]) => {
+  () => [isCoordPage.value, boardData.value, showPoints.value] as const,
+  ([isCoordPage, _, showPoints]) => {
     if (!board.polygon) return;
-    const rtksOnly = name && ![COORD_NAME, QUERY_COORD_NAME].includes(name.toString());
     board.polygon.children.forEach((entity) => {
       if (entity instanceof PoPoint) {
-        if (entity.attrib.rtk) {
-          entity.visible(showPoints);
+        if (isCoordPage) {
+          entity.visible(entity.attrib.rtk);
         } else {
-          entity.visible(showPoints && rtksOnly);
+          entity.visible(showPoints);
         }
       } else {
-        entity.visible(rtksOnly);
+        entity.visible(!isCoordPage);
       }
     });
   },
@@ -154,7 +160,7 @@ watch(
 const captureing = ref(false);
 const capture = async () => {
   captureing.value = true;
-  await nextTick();
+  await new Promise((resolve) => setTimeout(resolve, 300));
   try {
     const dataURL = await board.toDataURL(2);
     await saveAs(dataURL, "map.png");
@@ -303,37 +309,3 @@ const capture = async () => {
   }
 }
 </style>
-
-<style lang="scss">
-.scale-view {
-  --color: #fff;
-  position: absolute;
-  left: 20px;
-  bottom: 20px;
-  height: 8px;
-  color: var(--color);
-  text-align: center;
-  border: 1px solid var(--color);
-  border-top: none;
-  z-index: 1;
-  font-size: 14px;
-  display: flex;
-  align-items: end;
-
-  &.light {
-    --color: #fff;
-
-    > div {
-      text-shadow: 0 0 2px #000;
-    }
-  }
-
-  &.dark {
-    --color: #000;
-
-    > div {
-      text-shadow: 0 0 2px #fff;
-    }
-  }
-}
-</style>

+ 79 - 15
src/view/pano/pano.vue

@@ -52,7 +52,7 @@ import {
   ScenePoint,
   scenePoints,
 } from "@/store/scene";
-import { copyText, toDegrees } from "@/util";
+import { copyText, toDegrees, getTextBound } from "@/util";
 import { ElMessage } from "element-plus";
 import saveAs from "@/util/file-serve";
 import { DeviceType } from "@/store/device";
@@ -87,26 +87,90 @@ const panoUrls = computed(() => {
 const update = ref(false);
 const loading = ref(false);
 
-const copyGis = async () => {
+const getGis = () => {
   const pos = point.value!.pos as number[];
-  await copyText(
-    `经度:${toDegrees(pos[0])}, 纬度: ${toDegrees(pos[1])}, 高程: ${pos[2]}`
-  );
+  return `经度: ${toDegrees(pos[0])}\n纬度: ${toDegrees(pos[1])}\n高程: ${round(
+    pos[2],
+    4
+  )}`;
+};
+
+const copyGis = async () => {
+  await copyText(getGis());
   ElMessage.success("经纬度高程复制成功");
 };
 
-const photo = () => {
+const canvas = document.createElement("canvas");
+// 水印添加函数
+const addWatermark = (imgURL: string, ration: number) => {
+  const ctx = canvas.getContext("2d");
+  const image = new Image();
+  image.src = imgURL;
+
+  return new Promise<string>((resolve, reject) => {
+    image.onload = () => {
+      canvas.width = image.width;
+      canvas.height = image.height;
+      ctx.drawImage(image, 0, 0, image.width, image.height);
+
+      const font = `${ration * 20}px Arial`;
+      const pos = point.value!.pos as number[];
+      const lines = `经度: ${toDegrees(pos[0])}\n纬度: ${toDegrees(pos[1])}`.split("\n");
+      const lineTopPadding = 5 * ration;
+      const lineBounds = lines.map((line) =>
+        getTextBound(line, font, [lineTopPadding, 0])
+      );
+      const bound = lineBounds.reduce(
+        (t, { width, height }) => {
+          t.width = Math.max(t.width, width);
+          t.height += height;
+          return t;
+        },
+        { width: 0, height: 0 }
+      );
+      const padding = 20 * ration;
+      const margin = 80 * ration;
+
+      const position = [
+        image.width - margin - bound.width,
+        image.height - margin - bound.height,
+      ];
+
+      ctx.rect(
+        position[0] - padding,
+        position[1] - padding,
+        bound.width + 2 * padding,
+        bound.height + 2 * padding
+      );
+      ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
+      ctx.fill();
+
+      ctx.font = font;
+      ctx.textBaseline = "top";
+      ctx.fillStyle = "#fff";
+      let itemTop = 0;
+      lines.forEach((line, ndx) => {
+        ctx.fillText(line, position[0], position[1] + itemTop + lineTopPadding);
+        itemTop += lineBounds[ndx].height;
+      });
+      resolve(canvas.toDataURL("image/jpg", 1));
+    };
+    image.onerror = reject;
+  });
+};
+
+const photo = async () => {
   loading.value = true;
-  setSize(3, 1920, 1080);
-  panoDomRef.value!.toBlob(async (blob) => {
-    if (blob) {
-      await saveAs(blob, `${relics.value?.name}.jpg`);
-      ElMessage.success("图片导出成功");
-    }
+  await new Promise((resolve) => setTimeout(resolve, 300));
+  const ration = 3;
+  setSize(ration, 1920, 1080);
+  let dataURL = panoDomRef.value.toDataURL("image/jpg", 1);
+  dataURL = await addWatermark(dataURL, ration);
 
-    setSize(devicePixelRatio);
-    loading.value = false;
-  }, "image/jpg");
+  await saveAs(dataURL, `${relics.value?.name}.jpg`);
+  ElMessage.success("图片导出成功");
+  setSize(devicePixelRatio);
+  loading.value = false;
 };
 
 let pano: ReturnType<typeof init>;

+ 24 - 19
src/view/scene-select.vue

@@ -13,7 +13,7 @@ import { computed, ref, watch } from "vue";
 import SceneTable from "./scene.vue";
 import { ElMessage, ElTable } from "element-plus";
 import { QuiskExpose } from "@/helper/mount";
-import { Scene, SceneStatus } from "@/store/scene";
+import { Scene, SceneStatus, scenes as rScenes } from "@/store/scene";
 
 type SimpleScene = Pick<Scene, "sceneId" | "sceneCode">;
 const props = defineProps<{
@@ -29,6 +29,12 @@ const scenes = computed(() =>
     simpleScenes.value.some(({ sceneCode }) => scene.sceneCode === sceneCode)
   )
 );
+const sceneAlls: Scene[] = [...rScenes.value];
+const selectSelects = computed(() => {
+  return simpleScenes.value.map((sScene) =>
+    sceneAlls.find((s) => s.sceneCode === sScene.sceneCode)
+  );
+});
 
 const loaded = ref(false);
 const tableProps = {
@@ -40,24 +46,24 @@ const tableProps = {
       ({ sceneCode }) => !originSceneCodes.includes(sceneCode)
       // || !val.some((scene) => scene.sceneCode === sceneCode)
     );
-    console.log(val, [...simpleScenes.value], originSceneCodes);
 
     let tip = false;
-    simpleScenes.value.push(
-      ...val
-        .filter((scene) => {
-          if (scene.calcStatus !== SceneStatus.SUCCESS) {
-            console.log("fff", scene);
-            tableProps.tableRef.value!.toggleRowSelection(scene, false);
-            tip || ElMessage.error({ message: "计算中场景无法添加", repeatNum: 1 });
-            tip = true;
-            return false;
-          } else {
-            return true;
-          }
-        })
-        .map((scene) => ({ sceneCode: scene.sceneCode, sceneId: scene.sceneId }))
-    );
+    val.forEach((scene) => {
+      if (
+        selectSelects.value.length &&
+        selectSelects.value[0].cameraType !== scene.cameraType
+      ) {
+        tableProps.tableRef.value!.toggleRowSelection(scene, false);
+        tip || ElMessage.error({ message: "请添加相同类型的场景", repeatNum: 1 });
+        tip = true;
+      } else if (scene.calcStatus !== SceneStatus.SUCCESS) {
+        tableProps.tableRef.value!.toggleRowSelection(scene, false);
+        tip || ElMessage.error({ message: "计算中场景无法添加", repeatNum: 1 });
+        tip = true;
+      } else {
+        simpleScenes.value.push({ sceneCode: scene.sceneCode, sceneId: scene.sceneId });
+      }
+    });
 
     if (props.selfScenes) {
       const foreChecks = props.selfScenes.filter(
@@ -77,12 +83,12 @@ const tableProps = {
       }
     }
 
-    console.log([...simpleScenes.value]);
     tip = false;
   },
   tableDataChange(val: Scene[]) {
     loaded.value = false;
     originScenes.value = val;
+    sceneAlls.push(...val);
     setTimeout(checkedTable);
   },
   tableRef: ref<InstanceType<typeof ElTable>>(),
@@ -92,7 +98,6 @@ let time: NodeJS.Timeout;
 const checkedTable = () => {
   if (tableProps.tableRef.value) {
     tableProps.tableRef.value!.clearSelection();
-    console.log("1");
     scenes.value.forEach((item) => {
       tableProps.tableRef.value!.toggleRowSelection(item, true);
     });