bill 2 months ago
parent
commit
6b94fde600

+ 5 - 5
.env.jmdev

@@ -2,10 +2,10 @@ VITE_PRIMARY='#109BE0'
 VITE_TITLE='绘图'
 VITE_ENTRY='/example/fuse/enter.ts'
 VITE_ENTRY_EXAMPLE='./main.ts'
-VITE_MOCK_ENV=jmtest
+VITE_MOCK_ENV=jmg
 
 
-VITE_STATIC='http://192.168.0.25'
+VITE_STATIC='https://survey.4dkankan.com/'
 VITE_OSS='/oss/'
 VITE_OSS_ROOT="/rootOss/"
 VITE_MESH_OSS='/meshOSS/'
@@ -14,6 +14,6 @@ VITE_CLOUD_API='/cloudAPI/'
 VITE_FUSE_API='/fuseAPI/'
 
 VITE_MESH_VIEW='./static/kankan.html?m={m}&lang=zh&env=dev&token={token}'
-VITE_CLOUD_VIEW='http://192.168.0.25/swss/index.html?m={m}&lang=zh&token={token}'
-VITE_FUSE_VIEW='http://192.168.0.25/code/'
-VITE_LOGIN_VIEW='http://192.168.0.25/admin/index.html#/login?redirect={redirect}'
+VITE_CLOUD_VIEW='https://survey.4dkankan.com//swss/index.html?m={m}&lang=zh&token={token}'
+VITE_FUSE_VIEW='https://survey.4dkankan.com//code/'
+VITE_LOGIN_VIEW='https://survey.4dkankan.com//admin/index.html#/login?redirect={redirect}'

+ 17 - 0
.env.jmg

@@ -0,0 +1,17 @@
+VITE_PRIMARY='#109BE0'
+VITE_TITLE='绘图'
+VITE_ENTRY='/example/fuse/enter.ts'
+VITE_ENTRY_EXAMPLE='./main.ts'
+
+
+VITE_OSS_ROOT="https://survey.4dkankan.com/"
+VITE_OSS='https://survey.4dkankan.com/oss/'
+VITE_MESH_OSS='https://survey.4dkankan.com/oss/'
+VITE_MESH_API='https://survey.4dkankan.com/'
+VITE_CLOUD_API='https://survey.4dkankan.com/'
+VITE_FUSE_API='https://survey.4dkankan.com/'
+
+VITE_MESH_VIEW='./static/kankan.html?m={m}&lang=zh&env=dev'
+VITE_CLOUD_VIEW='https://survey.4dkankan.com/swss/index.html?m={m}&lang=zh'
+VITE_FUSE_VIEW='https://survey.4dkankan.com/code/'
+VITE_LOGIN_VIEW='https://survey.4dkankan.com/admin/#/statistics/scene?redirect={redirect}'

+ 2 - 3
src/core/components/group/group.vue

@@ -21,13 +21,12 @@ import { setShapeTransform } from "@/utils/shape.ts";
 import { DrawStoreBusArgs, useStore } from "../../store/index.ts";
 import { Transform } from "konva/lib/Util";
 import { useHistory } from "@/core/hook/use-history.ts";
-import { computed, nextTick, onUnmounted, ref, shallowRef, watchEffect } from "vue";
+import { computed, nextTick, onUnmounted, ref, shallowRef } from "vue";
 import { useOperMode } from "@/core/hook/use-status.ts";
 import { EntityShape } from "@/deconstruction.js";
-import { getBaseItem } from "../util.ts";
 import { useForciblyShowItemIds } from "@/core/hook/use-global-vars.ts";
 import { themeColor } from "@/constant";
-import { debounce, frameEebounce } from "@/utils/shared.ts";
+import { debounce } from "@/utils/shared.ts";
 
 const props = defineProps<{ data: GroupData }>();
 const emit = defineEmits<{

+ 20 - 0
src/core/hook/use-component.ts

@@ -258,6 +258,26 @@ export const useComponentStatus = <S extends EntityShape, T extends DrawItem>(
   };
 };
 
+export const useGetShapeBelong = () => {
+  const store = useStore();
+  return (shape: EntityShape) => {
+    let curId = shape.id();
+    let id: string;
+    let item: DrawItem | undefined;
+    let type: ShapeType | undefined;
+    do {
+      id = shape.id();
+      item = store.getItemById(id);
+      type = store.getType(id);
+      if (item && type) {
+        break;
+      }
+    } while ((shape = shape.parent as any));
+
+    return item ? [item, curId === id, type, id] as const : null
+  }
+}
+
 export const useGetComponentData = <D extends DrawItem>() => {
   const store = useStore();
 

+ 4 - 0
src/core/hook/use-expose.ts

@@ -37,6 +37,7 @@ import { useProportion } from "./use-proportion.ts";
 import { ShapeType } from "@/index.ts";
 import { useGetDXF } from "./use-dxf.ts";
 import { getIconStyle } from "../components/icon/index.ts";
+import { useGetShapeBelong } from "./use-component.ts";
 
 // 自动粘贴服务
 export const useAutoPaste = () => {
@@ -110,6 +111,7 @@ export const useShortcutKey = () => {
   const status = useMouseShapesStatus();
   const getChildren = useGetFormalChildren();
   const operMode = useOperMode();
+  const getShapeBelong = useGetShapeBelong()
   useListener(
     "keydown",
     (ev) => {
@@ -133,6 +135,8 @@ export const useShortcutKey = () => {
         const shapes = isSelect ? status.selects : status.actives;
         const delItems = shapes
           .map((shape) => {
+            // getShapeBelong(shape)
+
             let curId = shape.id();
             if (!curId) return;
             let id: string;

+ 320 - 0
src/core/hook/use-selection-n.ts

@@ -0,0 +1,320 @@
+import { Rect } from "konva/lib/shapes/Rect";
+import {
+  globalWatch,
+  installGlobalVar,
+  useForciblyShowItemIds,
+  useMountParts,
+  useStage,
+} from "./use-global-vars";
+import {
+  useGetFormalChildren,
+  useFormalLayer,
+  useHelperLayer,
+} from "./use-layer";
+import { themeColor } from "@/constant";
+import { dragListener } from "@/utils/event";
+import { Layer } from "konva/lib/Layer";
+import { useOperMode } from "./use-status";
+import {
+  computed,
+  markRaw,
+  nextTick,
+  reactive,
+  Ref,
+  ref,
+  toRaw,
+  watch,
+  watchEffect,
+} from "vue";
+import { EntityShape } from "@/deconstruction";
+import { Util } from "konva/lib/Util";
+import {
+  useViewerInvertTransform,
+  useViewerInvertTransformConfig,
+} from "./use-viewer";
+import { debounce, diffArrayChange, mergeFuns, onlyId } from "@/utils/shared";
+import { IRect } from "konva/lib/types";
+import { useMouseShapesStatus } from "./use-mouse-status";
+import Icon from "../components/icon/temp-icon.vue";
+import { Group } from "konva/lib/Group";
+import { Component as GroupComp, GroupData } from "../components/group/";
+import { useStore } from "../store";
+import { useOnComponentBoundChange } from "./use-component";
+import { useHistory } from "./use-history";
+import { isRectContained } from "@/utils/math";
+import { useTransformer } from "./use-transformer";
+import { IconData } from "../components/icon";
+import { usePause } from "./use-pause";
+import mitt, { Emitter } from "mitt";
+
+// 多选不包含分组, 只包含选中者
+export const useSelection = installGlobalVar(() => {
+  const layer = useHelperLayer();
+  const getChildren = useGetFormalChildren();
+  const box = new Rect({
+    stroke: themeColor,
+    strokeWidth: 1,
+    fill: "#fff",
+    listening: false,
+    opacity: 0.5,
+  });
+  const stage = useStage();
+  const operMode = useOperMode();
+  const selections = ref<EntityShape[]>();
+  const transformer = useTransformer();
+
+  let shapeBoxs: IRect[] = [];
+  let shapes: EntityShape[] = [];
+
+  const updateSelections = () => {
+    const boxRect = box.getClientRect();
+    selections.value = [];
+
+    for (let i = 0; i < shapeBoxs.length; i++) {
+      if (
+        Util.haveIntersection(boxRect, shapeBoxs[i]) &&
+        !isRectContained(shapeBoxs[i], boxRect) &&
+        shapes[i] !== toRaw(transformer)
+      ) {
+        if (!selections.value.includes(shapes[i])) {
+          selections.value.push(shapes[i]);
+        }
+      }
+    }
+  };
+
+  const init = (dom: HTMLDivElement, layer: Layer) => {
+    const stopListener = dragListener(dom, {
+      down(pos) {
+        layer.add(box);
+        box.x(pos.x);
+        box.y(pos.y);
+        box.width(0);
+        box.height(0);
+      },
+      move({ end }) {
+        box.width(end.x - box.x());
+        box.height(end.y - box.y());
+        updateSelections();
+      },
+      up() {
+        selections.value = undefined;
+        box.remove();
+      },
+    });
+    return () => {
+      stopListener();
+      box.remove();
+    };
+  };
+
+  const updateInitData = () => {
+    shapes = getChildren();
+    shapeBoxs = shapes.map((shape) => shape.getClientRect());
+  };
+
+  const stopWatch = globalWatch(
+    () => operMode.value.mulSelection,
+    (mulSelection, _, onCleanup) => {
+      if (!mulSelection) return;
+      const dom = stage.value?.getNode().container()!;
+      updateInitData();
+      onCleanup(init(dom, layer.value!));
+    }
+  );
+
+  return {
+    onDestroy: stopWatch,
+    var: { selections, box },
+  };
+});
+
+type ShapeIconArgs = Partial<
+  Pick<IconData, "width" | "height" | "url" | "fill" | "stroke">
+>;
+export const useShapesIcon = (
+  shapes: Ref<EntityShape[] | undefined>,
+  args: ShapeIconArgs = {}
+) => {
+  const mParts = useMountParts();
+  const { on } = useOnComponentBoundChange();
+  const iconProps = {
+    width: 12,
+    height: 12,
+    url: "./icons/state_s.svg",
+    fill: themeColor,
+    stroke: "#fff",
+    ...args,
+    listening: false,
+  };
+  const invConfig = useViewerInvertTransformConfig();
+  const invMat = useViewerInvertTransform();
+  const getShapeMat = (shape: EntityShape) => {
+    const rect = shape.getClientRect();
+    const center = invMat.value.point({
+      x: rect.x + rect.width / 2,
+      y: rect.y + rect.height / 2,
+    });
+    return [1, 0, 0, 1, center.x, center.y];
+  };
+  const unMountMap = new WeakMap<EntityShape, () => void>();
+
+  const pause = usePause();
+  const stop = watch([shapes, () => pause.isPause], ([shapes], [oldShapes]) => {
+    if (pause.isPause) {
+      shapes = [];
+    }
+
+    const { added, deleted } = diffArrayChange(shapes || [], oldShapes || []);
+    for (const addShape of added) {
+      const mat = ref(getShapeMat(addShape));
+      const data = reactive({ ...iconProps, mat: mat });
+      const unHooks = [
+        on(addShape, () => (mat.value = getShapeMat(addShape))),
+        watch(
+          invConfig,
+          () => {
+            data.width = invConfig.value.scaleX * iconProps.width;
+            data.height = invConfig.value.scaleY * iconProps.height;
+          },
+          { immediate: true }
+        ),
+        mParts.add({
+          comp: markRaw(Icon),
+          props: { data },
+        }),
+      ];
+      unMountMap.set(addShape, mergeFuns(unHooks));
+    }
+    for (const delShape of deleted) {
+      const fn = unMountMap.get(delShape);
+      fn && fn();
+    }
+  });
+  return [stop, pause];
+};
+
+export const useStoreSelectionManage = installGlobalVar(() => {
+  const store = useStore();
+  const bus: Emitter<Record<"del" | "update", EntityShape>> = mitt();
+  const { on } = useOnComponentBoundChange();
+
+  const canSelect = (shape: EntityShape) => {
+    const id = shape.id();
+    return id && store.items.some((item) => item.id === id);
+  };
+  const listener = (shape: EntityShape) => {
+    return watch(
+      () => canSelect(shape),
+      (exixts, _, onCleanup) => {
+        if (!exixts) {
+          bus.emit("del", shape);
+        } else {
+          onCleanup(on(shape, () => bus.emit("update", shape)));
+        }
+      },
+      { immediate: true }
+    );
+  };
+
+  return { canSelect, listener };
+});
+
+export const useSelectionRevise = () => {
+  const storeManage = useStoreSelectionManage()
+  const mParts = useMountParts();
+  const status = useMouseShapesStatus();
+  const store = useStore();
+  const { selections: rectSelects } = useSelection();
+
+  let initSelections: EntityShape[] = [];
+  watch(
+    () => rectSelects.value && [...rectSelects.value],
+    (rectSelects, oldRectSelects) => {
+      if (!oldRectSelects) {
+        initSelections = [...status.selects];
+      } else if (!rectSelects) {
+        initSelections = [];
+      } else {
+        status.selects = initSelections.concat(rectSelects);
+        filterSelect()
+      }
+    }
+  );
+  useShapesIcon(computed(() => status.selects.concat(rectSelects.value || [])));
+
+  const filterSelect = debounce(() => {
+    const mouseSelects = status.selects.filter((shape) => storeManage.canSelect(shape));
+    status.selects = mouseSelects;
+  }, 16);
+  store.bus.on("delItemAfter", filterSelect);
+  store.bus.on("clearAfter", filterSelect);
+  store.bus.on("dataChangeAfter", filterSelect);
+  store.bus.on("setCurrentLayerAfter", filterSelect);
+
+  const ids = computed(() => [
+    ...new Set(status.selects.map((item) => item.id())),
+  ]);
+  const groupConfig = {
+    id: onlyId(),
+    createTime: Date.now(),
+    lock: false,
+    opacity: 1,
+    ref: false,
+    listening: false,
+    stroke: themeColor,
+  };
+  const operMode = useOperMode();
+  const layer = useFormalLayer();
+  watch(
+    () => [!!ids.value.length, operMode.value.mulSelection],
+    (_a, _b) => {
+      const groupShape = layer.value?.findOne<Group>(`#${groupConfig.id}`);
+      if (!groupShape) return;
+      if (ids.value.length && !operMode.value.mulSelection) {
+        status.actives = [groupShape];
+      } else if (status.actives.includes(groupShape)) {
+        status.actives = [];
+      }
+    }
+  );
+
+  const stage = useStage();
+  const history = useHistory();
+  const showItemId = useForciblyShowItemIds();
+  watchEffect((onCleanup) => {
+    if (!ids.value.length) return;
+    const props = {
+      data: { ...groupConfig, ids: ids.value },
+      key: groupConfig.id,
+      onUpdateShape(data: GroupData) {
+        // status.selects;
+        // data.ids;
+      },
+      onDelShape() {
+        status.selects = [];
+      },
+      onAddShape(data: GroupData) {
+        history.onceTrack(() => {
+          const ids = data.ids;
+          const groups = store.typeItems.group;
+          const exists = groups?.some((group) => {
+            if (group.ids.length !== ids.length) return false;
+            const diff = diffArrayChange(group.ids, ids);
+            return diff.added.length === 0 && diff.deleted.length == 0;
+          });
+          if (exists) return;
+
+          store.addItem("group", { ...data, ids });
+          showItemId.cycle(data.id, async () => {
+            await nextTick();
+            const $stage = stage.value!.getNode();
+            const addShape = $stage.findOne("#" + data.id) as EntityShape;
+            status.selects = [addShape];
+          });
+        });
+      },
+    };
+    onCleanup(mParts.add({ comp: markRaw(GroupComp), props }));
+  });
+};

+ 13 - 12
src/core/hook/use-selection.ts

@@ -27,7 +27,7 @@ import {
 } from "vue";
 import { EntityShape } from "@/deconstruction";
 import { Util } from "konva/lib/Util";
-import { useViewerInvertTransform } from "./use-viewer";
+import { useViewerInvertTransform, useViewerInvertTransformConfig } from "./use-viewer";
 import { debounce, diffArrayChange, mergeFuns, onlyId } from "@/utils/shared";
 import { IRect } from "konva/lib/types";
 import { useMouseShapesStatus } from "./use-mouse-status";
@@ -108,7 +108,7 @@ export const useSelection = installGlobalVar(() => {
   const layer = useHelperLayer();
   const getChildren = useGetFormalChildren();
   const box = new Rect({
-    stroke: themeColor,
+    stroke: themeColor, 
     strokeWidth: 1,
     fill: "#fff",
     listening: false,
@@ -181,7 +181,7 @@ export const useSelection = installGlobalVar(() => {
 
   return {
     onDestroy: stopWatch,
-    var: selections,
+    var: {selections, box},
   };
 });
 
@@ -189,14 +189,14 @@ export const useSelectionShowIcons = installGlobalVar(() => {
   const mParts = useMountParts();
   const { on } = useOnComponentBoundChange();
   const iconProps = {
-    width: 20,
-    height: 20,
+    width: 12,
+    height: 12,
     url: "./icons/state_s.svg",
     fill: themeColor,
     stroke: "#fff",
     listening: false,
   };
-
+  const invConfig = useViewerInvertTransformConfig()
   const status = useMouseShapesStatus();
 
   const store = useStore();
@@ -217,13 +217,16 @@ export const useSelectionShowIcons = installGlobalVar(() => {
     const { added, deleted } = diffArrayChange(shapes, oldShapes);
     for (const addShape of added) {
       const mat = ref(getShapeMat(addShape));
+      const data = reactive({ ...iconProps, mat: mat })
       const unHooks = [
         on(addShape, () => (mat.value = getShapeMat(addShape))),
+        watch(invConfig, () => {
+          data.width = invConfig.value.scaleX * iconProps.width
+          data.height = invConfig.value.scaleY * iconProps.height
+        }, {immediate: true}),
         mParts.add({
           comp: markRaw(Icon),
-          props: {
-            data: reactive({ ...iconProps, mat: mat }),
-          },
+          props: { data },
         }),
       ];
       unMountMap.set(addShape, mergeFuns(unHooks));
@@ -320,8 +323,6 @@ export const useSelectionRevise = () => {
   const mParts = useMountParts();
   const status = useMouseShapesStatus();
   const store = useStore();
-  // const { addShapes, delShapes, watchSelection } =
-  //   useWatchSelectionGroup();
 
   const { addShapes, delShapes, watchSelection } = useWatchSelection();
 
@@ -340,7 +341,7 @@ export const useSelectionRevise = () => {
   store.bus.on("dataChangeAfter", filterSelect);
   store.bus.on("setCurrentLayerAfter", filterSelect);
 
-  const rectSelects = useSelection();
+  const { selections: rectSelects } = useSelection();
   let initSelections: EntityShape[] = [];
   let stopWatchSelection = watchSelection();
   watch(

+ 4 - 0
src/example/components/slide/slide.vue

@@ -39,6 +39,10 @@ const activeMenu = computed(() =>
 const selectHandler = async (val: string) => {
   if (active.value) {
     props.draw.quitDrawShape();
+    if (active.value === val) {
+      active.value = undefined;
+      return;
+    }
   }
   await nextTick();
   active.value = val;

+ 6 - 1
src/example/fuse/enter.ts

@@ -95,7 +95,12 @@ const login = (isBack = true) => {
     });
 
     if (!isBack) {
-      const url = new URL(link);
+      let url: URL
+      try {
+        url = new URL(link);
+      } catch {
+        url = new URL(link, location.origin)
+      }
       url.searchParams.delete("redirect");
       const query = urlGetQuery(url.toString());
       delete query["redirect"];

+ 3 - 2
src/example/fuse/main.ts

@@ -1,8 +1,9 @@
-import { createApp } from 'vue'
+
 import './global.scss'
 import 'element-plus/theme-chalk/src/index.scss'
-import App from './App.vue'
 import 'virtual:svg-icons-register'
+import { createApp } from 'vue'
+import App from './App.vue'
 import Icon from '@/components/icon/index.vue'
 import {router} from "./router";
 

+ 2 - 1
vite.config.ts

@@ -19,6 +19,7 @@ export default ({ mode }: any) => {
       rewrite: (path: any) => path.replace(prev, ""),
     });
 
+    console.log(env, env.VITE_MOCK_ENV, mockEnv)
     if (env.VITE_MOCK_PROXY) {
       proxy[env.VITE_MOCK_PROXY] = getProxy(
         env.VITE_MOCK_PROXY,
@@ -33,7 +34,7 @@ export default ({ mode }: any) => {
     }
   }
 
-  console.log(proxy)
+  console.log('===>', proxy)
 
   return defineConfig({
     base: './',