Explorar o código

Merge branch 'master' into merge-selection

bill hai 2 meses
pai
achega
97c0fe3e80

+ 63 - 4
public/static/kankan.html

@@ -18,12 +18,46 @@
       width: 100%;
       height: 100%;
     }
+
+    #floors {
+      pointer-events: all;
+      position: fixed;
+      left: 20px;
+      z-index: 10;
+      top: 50%;
+      transform: translateY(-50%);
+      background: rgba(0, 0, 0, .6);
+      border-radius: 6px 6px 6px 6px;
+      overflow: hidden;
+      transition: all .3s;
+      padding: 5px 13px;
+      min-width: 30px;
+    }
+
+    #floors li {
+      height: 36px;
+      cursor: pointer;
+      color: hsla(0, 0%, 100%, .7);
+      font-size: 14px;
+      line-height: 40px;
+      max-width: 83vw;
+      text-align: center;
+      position: relative;
+      color: #fff;
+      text-overflow: ellipsis;
+      overflow: hidden;
+      white-space: nowrap;
+    }
+    .active {
+      color: #00c8af !important;
+    }
   </style>
 </head>
 
 <body>
   <div id="scene" class="scene"></div>
   <div class="map"></div>
+  <ul id="floors" style="display: none;"></ul>
   <script>
     const params = new URLSearchParams(location.search)
     let host = params.get('host') || ''
@@ -56,20 +90,45 @@
         server: host,
         resource: host + '/oss/'
       })
-      console.log(kankan.store)
-      kankan.store.on('floorcad', floor => {
-        console.log('floorcad', floor)
+
+      const setCurrentFloor = (id) => {
+        const $old = document.querySelector(`#floors > .active`)
+        if ($old) {
+          $old.classList.remove('active')
+        }
+        const $item = document.querySelector(`#floors > li[attr-id='${id}']`)
+        kankan.Scene.gotoFloor(Number(id))
+        $item && $item.classList.add('active')
+      }
+
+      const renderFloors = (floors) => {
+        const $floors = document.querySelector('#floors')
+        if (floors.length <= 1) return;
+        $floors.style.display = 'block'
+        $floors.innerHTML = floors.map(item => `<li attr-id="${item.id}">${item.name}</li>`).join('')
+        $floors.addEventListener('click', ev => {
+          const dom = ev.target
+          const id = dom.getAttribute('attr-id')
+          if (!id) return;
+          setCurrentFloor(id)
+        })
+      }
+
+
+      kankan.store.on('flooruser', floor => {
+        renderFloors(floor.floors)
       })
       kankan.Scene.on('loaded', () => {
         const player = kankan.core.get('Player')
         player.viewLinkManager.addEventListener('loaded', () => {
           player.viewLinkManager.hideAllViews()
         })
+        setCurrentFloor(kankan.Scene.floorId)
       })
       kankan.Scene.on('ready', async () => {
         let metadata = await kankan.store.get('metadata')
         metadata.surveillances = 0
-        
+
       })
       kankan.render()
 

+ 26 - 11
src/core/components/group/group.vue

@@ -68,7 +68,21 @@ const { shape, tData, data } = useComponentStatus<Rect, GroupData>({
   transformType: "custom",
   customTransform(callback, shape, data) {
     let prevMat: Transform;
-    let transformResolve: () => void;
+    let unUpdate = false;
+    let transformResolve: (() => void) | null = null;
+
+    const updateChildren = debounce((data, mat) => {
+      unUpdate = false;
+      setShapeTransform(shape.value!.getNode(), mat);
+      matResponse({ data, mat, store }, prevMat);
+      prevMat = mat;
+      getGroupShapes!().forEach((shape) => nextTick(() => shape.fire("bound-change")));
+      if (autoUpdate.value) {
+        transformResolve && transformResolve();
+        transformResolve = null;
+      }
+      return true;
+    }, 6);
     useCustomTransformer(shape, data, {
       openSnap: false,
       getRepShape($shape) {
@@ -94,15 +108,13 @@ const { shape, tData, data } = useComponentStatus<Rect, GroupData>({
           },
         };
       },
-      handler: debounce((data, mat) => {
-        setShapeTransform(shape.value!.getNode(), mat);
-        const ctxs = data.ids.map(getShapeBelong).filter((item: any) => !!item);
-        matResponse({ data, mat, store }, prevMat, ctxs);
-        prevMat = mat;
-        getGroupShapes!().forEach((shape) => nextTick(() => shape.fire("bound-change")));
-        return true;
-      }, 6),
-      start() {
+      handler: (data, mat) => {
+        unUpdate = true;
+        updateChildren(data, mat);
+      },
+      async start() {
+        transformResolve && transformResolve();
+        await nextTick();
         autoUpdate.value = false;
         history.onceTrack(
           () =>
@@ -113,7 +125,10 @@ const { shape, tData, data } = useComponentStatus<Rect, GroupData>({
       },
       callback() {
         autoUpdate.value = true;
-        transformResolve();
+        if (!unUpdate) {
+          transformResolve && transformResolve();
+          transformResolve = null;
+        }
         callback();
       },
     });

+ 8 - 4
src/core/components/line/single-line.vue

@@ -92,6 +92,7 @@ import {
   useMouseShapeStatus,
 } from "@/core/hook/use-mouse-status.ts";
 import { themeColor } from "@/constant";
+import { Vector2 } from "three";
 
 const mode = useMode();
 
@@ -129,6 +130,7 @@ d.strokeWidth.label = "粗细";
 d.stroke.label = "颜色";
 
 let isStartChange = false;
+let setLineVector: Vector2;
 describes.length = {
   type: "inputNum",
   label: "线段长度",
@@ -139,6 +141,7 @@ describes.length = {
   set value(val) {
     if (!isStartChange) {
       emit("updateBefore", [props.line.a, props.line.b]);
+      setLineVector = lineVector(points.value);
     }
     isStartChange = true;
     const aCount = props.data.lines.filter(
@@ -151,16 +154,17 @@ describes.length = {
     if (aCount === bCount || (aCount > 1 && bCount > 1)) {
       // 两端伸展
       const center = lineCenter(points.value);
-      const l1 = getVectorLine(lineVector([center, points.value[0]]), center, val / 2);
-      const l2 = getVectorLine(lineVector([center, points.value[1]]), center, val / 2);
+      const l1 = getVectorLine(setLineVector.clone().multiplyScalar(-1), center, val / 2);
+      const l2 = getVectorLine(setLineVector, center, val / 2);
       emit("updatePoint", { ...points.value[0], ...l1[1] });
       emit("updatePoint", { ...points.value[1], ...l2[1] });
     } else {
       // 单端伸展
       const changeNdx = aCount > 1 ? 1 : 0;
       const start = points.value[aCount > 1 ? 0 : 1];
-      const end = points.value[changeNdx];
-      const line = getVectorLine(lineVector([start, end]), start, val);
+      const lineVec =
+        aCount > 1 ? setLineVector : setLineVector.clone().multiplyScalar(-1);
+      const line = getVectorLine(lineVec, start, val);
       emit("updatePoint", { ...points.value[changeNdx], ...line[1] });
     }
   },

+ 2 - 5
src/core/hook/use-expose.ts

@@ -36,7 +36,6 @@ import { components } from "../components/index.ts";
 import { useProportion } from "./use-proportion.ts";
 import { useGetDXF } from "./use-dxf.ts";
 import { getIconStyle } from "../components/icon/index.ts";
-import { useGetShapeBelong } from "./use-component.ts";
 
 // 自动粘贴服务
 export const useAutoPaste = () => {
@@ -117,12 +116,10 @@ export const useShortcutKey = () => {
   const getChildren = useGetFormalChildren();
   const operMode = useOperMode();
   const eSelection = useExcludeSelection()
-  const getShapeBelong = useGetShapeBelong()
   useListener(
     "keydown",
     (ev) => {
       if (ev.target !== document.body) return;
-
       if (ev.key === "z" && ev.ctrlKey) {
         ev.preventDefault();
         history.hasUndo.value && history.undo();
@@ -160,12 +157,12 @@ export const useShortcutKey = () => {
             status.actives = [];
           }
         }
-      } else if (operMode.value.mulSelection && ev.key === "A") {
+      } else if (operMode.value.mulSelection && ev.key === "a") {
         ev.preventDefault();
         if (status.selects.length) {
           status.selects = [];
         } else {
-          
+          console.log('多选')
           status.selects = getChildren().filter(shape => !eSelection.value.includes(shape.id()));
         }
       }

+ 1 - 0
src/core/hook/use-history.ts

@@ -145,6 +145,7 @@ export class DrawHistory {
     if (this.onceFlag) {
       this.onceHistory = data;
     } else if (data !== this.current?.data) {
+      console.log('push history')
       this.bus.emit("push", data);
       this.history.push({ attachs: JSON.stringify(this.pushAttachs), data });
       this.pushAttachs = {};

+ 1 - 1
src/core/hook/use-status.ts

@@ -87,7 +87,7 @@ export const useOperMode = installGlobalVar(() => {
 
   return computed(() => ({
     // 多选模式
-    mulSelection: keys.has('Shift') && !keys.has(' ') && !keys.has('Alt'),
+    mulSelection: keys.has('Ctrl') && !keys.has(' ') && !keys.has('Alt'),
     // mulSelection: false,
     // 自由移动视图
     freeView: keys.has(' '),

+ 2 - 1
src/example/components/container/container.vue

@@ -80,6 +80,7 @@ defineExpose({
   display: flex;
   flex-direction: column;
   align-items: stretch;
+  min-width: 980px;
   height: 100vh;
   background: #f0f2f5;
   --top: 0px;
@@ -87,7 +88,7 @@ defineExpose({
   overflow: hidden;
 
   .container {
-    height: calc(100vh - var(--headerSize) - var(--top));
+    height: calc(100% - var(--headerSize) - var(--top));
     display: flex;
     align-items: stretch;
   }

+ 41 - 19
src/example/fuse/views/overview/header.vue

@@ -13,7 +13,7 @@
 
 <script lang="ts" setup>
 import Header from "../../../components/header/index.vue";
-import { ElButton } from "element-plus";
+import { ElButton, ElMessage } from "element-plus";
 import { useDraw } from "../../../components/container/use-draw.ts";
 import { selectScene } from "../../../dialog/vr/index.ts";
 import { Scene } from "../../../platform/platform-resource.ts";
@@ -25,7 +25,7 @@ import {
   tableCoverHeight,
   overviewData,
 } from "../../store.ts";
-import { nextTick, onUnmounted } from "vue";
+import { nextTick, onUnmounted, reactive } from "vue";
 import { DataGroupId } from "@/constant/index.ts";
 import { Group } from "konva/lib/Group";
 import { Mode } from "@/constant/mode.ts";
@@ -35,6 +35,7 @@ import { router } from "../../router.ts";
 import { overviewId, params, tabulationId } from "@/example/env.ts";
 import { listener } from "@/utils/event.ts";
 import { repeatedlyOnly } from "@/utils/shared.ts";
+import saveAs from "@/utils/file-serve.ts";
 
 const props = defineProps<{ title: string }>();
 const draw = useDraw();
@@ -66,8 +67,24 @@ const actions = [
       ...baseActions.expose,
       children: baseActions.expose.children.map((item) => ({
         ...item,
-        handler() {
-          return item.handler(props.title);
+        async handler() {
+          const format = item.text.toLowerCase();
+          if (format !== "dxf") {
+            const blob = await draw.enterTemp(async () => {
+              const back = draw.config.back;
+              const [_, recover] = await setViewToTableCover();
+              if (format === "jpg") draw.config.back = back;
+              await nextTick();
+              const blob = await getImage(draw, `image/${format}`);
+              recover();
+              await nextTick();
+              return blob;
+            });
+            await saveAs(blob, `${props.title}.${format}`);
+            ElMessage.success("导出成功");
+          } else {
+            await item.handler(props.title);
+          }
         },
       })),
     },
@@ -86,17 +103,16 @@ const setViewToTableCover = async () => {
 
   const pop = draw.mode.push(Mode.readonly);
   const rect = getRect();
-  const rectScale = (rect.width || 1080) / (rect.height || 850);
+  let width = rect.width;
+  let height = rect.height;
+  const rectScale = width / height;
   const tableCoverScale = tableCoverWidth / tableCoverHeight;
   const padding = 70;
 
-  let width: number, height: number;
   if (rectScale > tableCoverScale) {
-    width = 1080;
-    height = width / rectScale;
+    height = width / tableCoverScale;
   } else {
-    height = 850;
-    width = rectScale * height;
+    width = tableCoverScale * height;
   }
   if (width < padding * 2) {
     width += padding * 2;
@@ -115,7 +131,7 @@ const setViewToTableCover = async () => {
   draw.config.labelLineConfig.fontSize = 10;
 
   await nextTick();
-  draw.config.labelLineConfig.showOffset = padding;
+  draw.config.labelLineConfig.showOffset = padding - 5;
   draw.initViewport(padding);
   await nextTick();
 
@@ -139,31 +155,37 @@ const setViewToTableCover = async () => {
 
 const saveHandler = repeatedlyOnly(async () => {
   const storeData = draw.getData();
-  const [blob, scale, rect] = await draw.enterTemp(async () => {
+  const [tabBlob, listBlob, scale, rect] = await draw.enterTemp(async () => {
+    const back = draw.config.back;
     const [rect, recover] = await setViewToTableCover();
     await nextTick();
     const mat = draw.viewer.transform.invert();
     const scale =
       lineLen(mat.point({ x: 1, y: 0 }), mat.point({ x: 0, y: 0 })) *
       draw.store.config.proportion.scale;
-    const blob = await getImage(draw, "image/png");
+    const tabBlob = await getImage(draw, "image/png");
+    draw.config.back = back;
+    await nextTick();
+    const listBlob = await getImage(draw, "image/jpg");
     recover();
     await nextTick();
-    return [blob, scale, rect] as const;
+    return [tabBlob, listBlob, scale, rect] as const;
   });
 
-  const url = await window.platform.uploadResourse(
-    new File([blob], `tabulation-cover.png`)
-  );
+  const [tabUrl, listUrl] = await Promise.all([
+    window.platform.uploadResourse(new File([tabBlob], `tabulation-cover.png`)),
+    window.platform.uploadResourse(new File([listBlob], `list-cover.png`)),
+  ]);
+
   overviewId.value = await window.platform.saveOverviewData(overviewId.value, {
     ...overviewData.value,
-    listCover: url,
+    listCover: listUrl,
     store: storeData,
     viewport: draw!.viewer.transform.m,
   });
 
   const cover = {
-    url,
+    url: tabUrl,
     width: rect.width,
     height: rect.height,
     proportion: { ...draw.store.config.proportion, scale },

+ 1 - 1
src/example/fuse/views/tabulation/index.vue

@@ -174,7 +174,7 @@ watch(cover, (cover, _, onCleanup) => {
           return coverScale.value;
         },
         set value(val) {
-          coverScale.value = val;
+          coverScale.value = Math.max(val || 0, 1);
         },
         props: { min: 1 },
       },