فهرست منبع

feat: 制作墙图例功能

bill 1 ماه پیش
والد
کامیت
82e07af289

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

@@ -77,7 +77,9 @@ const { shape, tData, data } = useComponentStatus<Rect, GroupData>({
       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")));
+      getGroupShapes!().forEach((shape) => {
+        nextTick(() => shape.fire("bound-change"));
+      });
       if (autoUpdate.value) {
         transformResolve && transformResolve();
         transformResolve = null;

+ 1 - 1
src/core/components/group/temp-group.vue

@@ -29,7 +29,7 @@ const getGroupShapes = () => {
   const shapes: EntityShape[] = [];
   for (const id of data.value.ids) {
     const $shape = $stage!.findOne(`#${id}`)!;
-    if (!$shape) return;
+    if (!$shape || $shape.attrs.disableGroupOper) continue;
     shapes.push($shape as EntityShape);
   }
   return shapes;

+ 2 - 1
src/core/components/index.ts

@@ -60,6 +60,7 @@ type CompAttach<key extends ShapeType> = {
     data: DrawItem<key>,
     childrenId?: string
   ) => void;
+  checkItemData?: (item: DrawItem<key>) => boolean
   useDraw?: () => void;
   getSnapInfos?: (items: DrawItem<key>) => ComponentSnapInfo[];
   GroupComponent: (props: { data: DrawItem[] }) => any;
@@ -134,7 +135,7 @@ export type InteractiveFix<T extends ShapeType> = (args: {
   viewTransform: Transform;
   store: DrawStore;
   history: DrawHistory;
-}) => DrawItem<T>;
+}) => DrawItem<T> | undefined;
 
 export type MatResponseProps<T extends ShapeType> = {
   data: DrawItem<T>;

+ 56 - 49
src/core/components/line-icon/icon.vue

@@ -34,7 +34,7 @@ import { setShapeTransform } from "@/utils/shape.ts";
 import { useStore } from "@/core/store/index.ts";
 import { usePointerPos } from "@/core/hook/use-global-vars.ts";
 import { useViewerInvertTransform } from "@/core/hook/use-viewer.ts";
-import { computed, watch } from "vue";
+import { computed, nextTick, watch } from "vue";
 import { useHistory } from "@/core/hook/use-history.ts";
 import { eqPoint, line2IncludedAngle, lineInner, lineLen, Pos } from "@/utils/math.ts";
 import { copy } from "@/utils/shared.ts";
@@ -55,6 +55,7 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus({
   props,
   getMouseStyle,
   transformType: "custom",
+  selfData: true,
   customTransform(callback, shape, data) {
     let prevInvMat: Transform;
     return useCustomTransformer(shape, data, {
@@ -115,65 +116,71 @@ watch(
   (line, oldLine) => {
     history.preventTrack(() => {
       if (!line) {
-        emit("delShape");
+        return emit("delShape");
       }
       if (!oldLine) return;
 
       const eq0 = eqPoint(oldLine[0], line[0]);
       const eq1 = eqPoint(oldLine[1], line[1]);
-      if (eq0 === eq1) return;
+      if (eq0 !== eq1) {
+        // 联动
+        const startNdx = eq0 ? 0 : 1;
+        const endNdx = eq0 ? 1 : 0;
+        const rotate = line2IncludedAngle(
+          [oldLine[startNdx], oldLine[endNdx]],
+          [line[startNdx], line[endNdx]]
+        );
+        const mat = new Transform()
+          .translate(line[startNdx].x, line[startNdx].y)
+          .rotate(rotate)
+          .translate(-line[startNdx].x, -line[startNdx].y);
+        const endPoints = getLineIconEndpoints(oldLine, data.value).map((p) =>
+          mat.point(p)
+        );
 
-      // 联动
-      const startNdx = eq0 ? 0 : 1;
-      const endNdx = eq0 ? 1 : 0;
-      const rotate = line2IncludedAngle(
-        [oldLine[startNdx], oldLine[endNdx]],
-        [line[startNdx], line[endNdx]]
-      );
-      const mat = new Transform()
-        .translate(line[startNdx].x, line[startNdx].y)
-        .rotate(rotate)
-        .translate(-line[startNdx].x, -line[startNdx].y);
-      const endPoints = getLineIconEndpoints(oldLine, data.value).map((p) =>
-        mat.point(p)
-      );
-
-      if (lineInner(line, endPoints[0]) && lineInner(line, endPoints[1])) {
-        emit("updateShape", {
-          ...data.value,
-          startLen: lineLen(line[0], endPoints[0]),
-          endLen: lineLen(line[0], endPoints[1]),
-        });
-      } else {
-        emit("delShape");
+        if (lineInner(line, endPoints[0]) && lineInner(line, endPoints[1])) {
+          emit("updateShape", {
+            ...data.value,
+            startLen: lineLen(line[0], endPoints[0]),
+            endLen: lineLen(line[0], endPoints[1]),
+          });
+        } else {
+          emit("delShape");
+          return;
+        }
       }
+      nextTick(() => {
+        shape.value?.getNode().fire("bound-change");
+      });
     });
   },
   { immediate: true }
 );
 
-operateMenus.splice(
-  operateMenus.length - 1,
-  0,
-  {
-    label: "内外翻转",
-    handler: () => {
-      emit("updateShape", {
-        ...data.value,
-        openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
-      });
-    },
-  },
-  {
-    label: "左右翻转",
-    handler: () => {
-      emit("updateShape", {
-        ...data.value,
-        openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
-        startLen: data.value.endLen,
-        endLen: data.value.startLen,
-      });
+if (props.data.type === "align-bottom") {
+  operateMenus.splice(
+    operateMenus.length - 1,
+    0,
+    {
+      label: "内外翻转",
+      handler: () => {
+        emit("updateShape", {
+          ...data.value,
+          openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
+        });
+      },
     },
-  }
-);
+    {
+      label: "左右翻转",
+      handler: () => {
+        emit("updateShape", {
+          ...data.value,
+          openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
+          startLen: data.value.endLen,
+          endLen: data.value.startLen,
+        });
+      },
+    }
+  );
+}
 </script>

+ 70 - 39
src/core/components/line-icon/index.ts

@@ -15,7 +15,7 @@ import {
   Size,
   vector2IncludedAngle,
 } from "@/utils/math.ts";
-import { MathUtils, Vector2 } from "three";
+import { Vector2 } from "three";
 import { LineData } from "../line/index.ts";
 import { DrawStore } from "@/core/store/index.ts";
 
@@ -28,9 +28,13 @@ export type LineIconData = Omit<IconData, "mat" | "width"> & {
   endLen: number;
   lineId: string;
   openSide: "LEFT" | "RIGHT";
+  type: "full" | "align-bottom";
 };
 
-export const getSnapLine = (store: DrawStore, data: LineIconData) => {
+export const getSnapLine = (
+  store: DrawStore,
+  data: Pick<LineIconData, "lineId">
+) => {
   const lineData = store.getTypeItems("line")[0];
   if (!lineData) return null;
 
@@ -62,11 +66,14 @@ export const isRangInner = (line: Pos[], data: LineIconData) => {
     data.startLen <= len &&
     data.endLen <= len
   );
-}
+};
 
 export const getLineIconMat = (
   snapLine: Pos[],
-  data: Pick<LineIconData, "height" | "startLen" | "endLen" | "openSide">
+  data: Pick<
+    LineIconData,
+    "height" | "startLen" | "endLen" | "openSide" | "type"
+  >
 ) => {
   const line = getLineIconEndpoints(snapLine, data);
   const lineRotate = vector2IncludedAngle(lineVector(line), { x: 1, y: 0 });
@@ -75,12 +82,14 @@ export const getLineIconMat = (
   const movev = new Vector2(Math.cos(moveRotate), -Math.sin(moveRotate));
   const shapeRotate = vector2IncludedAngle({ x: 0, y: -1 }, movev);
   const center = lineCenter(line);
-  const offset = movev.clone().multiplyScalar(data.height / 2);
 
-  const mat = new Transform()
-    .translate(offset.x, offset.y)
-    .translate(center.x, center.y)
-    .rotate(shapeRotate);
+  const mat = new Transform();
+  if (data.type === "align-bottom") {
+    const offset = movev.clone().multiplyScalar(data.height / 2);
+    mat.translate(offset.x, offset.y);
+  }
+
+  mat.translate(center.x, center.y).rotate(shapeRotate);
 
   const afterStart = mat.point({
     x: -Math.abs(data.endLen - data.startLen) / 2,
@@ -98,8 +107,9 @@ export const matResponse = ({
   mat,
   store,
   operType,
+  increment
 }: MatResponseProps<"lineIcon">) => {
-  if (!store) return data;
+  if (!store || increment) return data;
   const lineData = store.getTypeItems("line")[0];
   if (!lineData) return data;
 
@@ -115,35 +125,40 @@ export const matResponse = ({
     });
     const attach = getLineIconAttach(position);
     attach && Object.assign(data, attach);
-    return data;
-  }
-
-  const line = getSnapLine(store, data)!;
-  const oldMat = getLineIconMat(line, data);
-  const incMat = mat.copy().multiply(oldMat.invert());
-  const points = getLineIconEndpoints(line, data);
-  const oldWidth = Math.abs(data.endLen - data.startLen )
-  let startLen = data.startLen
-  let endLen = data.endLen
-  if (operType === 'middle-left') {
-    const startPoint = incMat.point(points[0])
-    startLen = lineLen(line[0], startPoint)
-    if (!eqPoint(lineVector([line[0], startPoint]), lineVector(line))) {
-      startLen *= -1
+  } else {
+    const line = getSnapLine(store, data)!;
+    const oldMat = getLineIconMat(line, data);
+    const incMat = mat.copy().multiply(oldMat.invert());
+    const points = getLineIconEndpoints(line, data);
+    const oldWidth = Math.abs(data.endLen - data.startLen);
+    let startLen = data.startLen;
+    let endLen = data.endLen;
+    if (operType === "middle-left") {
+      const startPoint = incMat.point(points[0]);
+      startLen = lineLen(line[0], startPoint);
+      if (!eqPoint(lineVector([line[0], startPoint]), lineVector(line))) {
+        startLen *= -1;
+      }
+    } else if (operType === "middle-right") {
+      const endPoint = incMat.point(points[1]);
+      endLen = lineLen(line[0], endPoint);
+      if (!eqPoint(lineVector([line[0], endPoint]), lineVector(line))) {
+        endLen *= -1;
+      }
     }
-  } else if (operType === 'middle-right') {
-    const endPoint = incMat.point(points[1])
-    endLen = lineLen(line[0], endPoint)
-    if (!eqPoint(lineVector([line[0], endPoint]), lineVector(line))) {
-      endLen *= -1
+
+    if (isRangInner(line, { ...data, startLen, endLen })) {
+      const width = Math.abs(endLen - startLen);
+      data.startLen = startLen;
+      data.endLen = endLen;
+      if (data.type === "align-bottom") {
+        data.height = (data.height / oldWidth) * width;
+      }
     }
   }
-
-  if (isRangInner(line, {...data, startLen, endLen})) {
-    const width = Math.abs(endLen - startLen )
-    data.height = data.height / oldWidth * width
-    data.startLen = startLen
-    data.endLen = endLen
+  if (data.type === "full") {
+    const wall = lineData.lines.find((line) => line.id === data.lineId);
+    wall && (data.height = wall.strokeWidth);
   }
   return data;
 };
@@ -180,9 +195,12 @@ export const genGetLineIconAttach = (lineData: LineData, size: Size) => {
       return null;
     }
 
-    const attrib = shapeLines[ndx]
+    const attrib = shapeLines[ndx];
     const shapev = lineVector([position, attrib.pjPoint]);
-    const angle = vector2IncludedAngle(lineVector([attrib.start, attrib.end]), shapev);
+    const angle = vector2IncludedAngle(
+      lineVector([attrib.start, attrib.end]),
+      shapev
+    );
 
     return {
       openSide: angle < 0 ? "RIGHT" : "LEFT",
@@ -207,7 +225,11 @@ export const interactiveToData: InteractiveTo<"lineIcon"> = ({
       ...args,
       viewTransform,
       info,
-      data: { ...getBaseItem(), ...preset } as unknown as LineIconData,
+      data: {
+        ...getBaseItem(),
+        type: "align-bottom",
+        ...preset,
+      } as unknown as LineIconData,
     });
   }
 };
@@ -220,15 +242,24 @@ export const interactiveFixData: InteractiveFix<"lineIcon"> = ({
   const lineData = store.getTypeItems("line")[0];
   if (!lineData) throw "没有线段数据,无法添加icon";
   const width = (data as any).width || data.height;
+
   const getLineIconAttach = genGetLineIconAttach(lineData, {
     width,
     height: data.height,
   });
   const attach = getLineIconAttach(info.cur!);
   attach && Object.assign(data, attach);
+
+  if (data.type === "full") {
+    const wall = lineData.lines.find((line) => line.id === data.lineId);
+    wall && (data.height = wall.strokeWidth);
+  }
+
   return data;
 };
 
+export const checkItemData = (item: LineIconData) => !!item.lineId
+
 export const getPredefine = (key: keyof LineIconData) => {
   if (key === "fill" || key === "stroke") {
     return { canun: true };

+ 8 - 1
src/core/components/line-icon/temp-icon.vue

@@ -1,6 +1,7 @@
 <template>
   <TempIcon
     :data="data"
+    :disableGroupOper="true"
     :add-mode="addMode"
     v-if="data && props.data.openSide"
     :ref="(e: any) => shape = e?.shape"
@@ -25,6 +26,11 @@ defineExpose({
 const props = defineProps<{ data: LineIconData; addMode?: boolean }>();
 const store = useStore();
 const line = computed(() => getSnapLine(store, props.data));
+const weight = computed(
+  () =>
+    store.getTypeItems("line")[0].lines.find((line) => line.id === props.data.lineId)
+      ?.strokeWidth
+);
 
 const mat = computed(() => {
   if (!line.value) return;
@@ -35,13 +41,14 @@ const rangInner = computed(() => line.value && isRangInner(line.value, props.dat
 
 const data = computed(() => {
   if (mat.value) {
-    return {
+    const iconData = {
       ...props.data,
       mat: mat.value,
       width: Math.abs(props.data.endLen - props.data.startLen),
       coverFill: rangInner.value ? undefined : "red",
       coverOpcatiy: rangInner.value ? 0 : 0.3,
     };
+    return iconData;
   }
 });
 </script>

+ 132 - 3
src/core/components/line/attach-server.ts

@@ -1,8 +1,13 @@
-import { SnapResultInfo, useSnapConfig } from "@/core/hook/use-snap";
-import { defaultStyle, LineData } from ".";
+import {
+  SnapResultInfo,
+  useCustomSnapInfos,
+  useSnapConfig,
+} from "@/core/hook/use-snap";
+import { defaultStyle, getSnapInfos, LineData } from ".";
 import {
   eqPoint,
   getVectorLine,
+  lineCenter,
   lineIntersection,
   lineLen,
   linePointLen,
@@ -16,6 +21,11 @@ import {
 import { copy, onlyId, rangMod } from "@/utils/shared";
 import { MathUtils, Vector2 } from "three";
 import { getBaseItem } from "../util";
+import { ComponentSnapInfo } from "..";
+import { useStore } from "@/core/store";
+import { computed, Ref } from "vue";
+import { installGlobalVar } from "@/core/hook/use-global-vars";
+import { mergeDescribes, PropertyDescribes } from "@/core/html-mount/propertys";
 
 export type NLineDataCtx = {
   del: {
@@ -187,7 +197,7 @@ export const genMoveLineHandler = (
   const angleRange = [MathUtils.degToRad(10), MathUtils.degToRad(170)];
 
   const getRefInfo = (moveDire: Vector2, ndx: number) => {
-    const joinLines = getJoinLine(data,  line, pointIds[ndx]);
+    const joinLines = getJoinLine(data, line, pointIds[ndx]);
     const joinLineDires: Vector2[] = [];
     const linePoints = [points[ndx], points[Number(!ndx)]];
     const lineDire = lineVector(linePoints);
@@ -442,3 +452,122 @@ export const genMoveLineHandler = (
     end,
   };
 };
+
+export const useLineDataSnapInfos = () => {
+  const infos = useCustomSnapInfos();
+  const store = useStore();
+  const lineData = computed(() => store.getTypeItems("line")[0]);
+  let snapInfos: ComponentSnapInfo[];
+
+  const updateSnapInfos = (pointIds: string[]) => {
+    clear()
+    snapInfos = getSnapInfos({
+      ...lineData.value,
+      lines: lineData.value.lines.filter(
+        (item) => !(pointIds.includes(item.a) || pointIds.includes(item.b))
+      ),
+      points: lineData.value.points.filter(
+        (item) => !pointIds.includes(item.id)
+      ),
+    });
+    snapInfos.forEach((item) => {
+      infos.add(item);
+    });
+  };
+
+  const clear = () => {
+    snapInfos && snapInfos.forEach((item) => infos.remove(item));
+  };
+
+  return {
+    update: updateSnapInfos,
+    clear,
+  };
+};
+
+export const updateLineLength = (
+  lineData: LineData,
+  line: LineData["lines"][0],
+  length: number,
+  flex?: "a" | "b" | "both",
+  vector?: Pos
+) => {
+  const points = [
+    lineData.points.find((p) => p.id === line.a)!,
+    lineData.points.find((p) => p.id === line.b)!,
+  ];
+  vector = vector || lineVector(points);
+
+  if (!flex) {
+    const aCount = lineData.lines.filter(
+      (line) => line.a === points[0].id || line.b === points[0].id
+    ).length;
+    const bCount = lineData.lines.filter(
+      (line) => line.a === points[1].id || line.b === points[1].id
+    ).length;
+    if (aCount === bCount || (aCount > 1 && bCount > 1)) {
+      flex = "both";
+    } else {
+      flex = aCount > 1 ? "b" : "a";
+    }
+  }
+
+  let moveVector = new Vector2(vector.x, vector.y);
+  if (flex === "both") {
+    const center = lineCenter(points);
+    const l1 = getVectorLine(
+      moveVector.clone().multiplyScalar(-1),
+      center,
+      length / 2
+    );
+    const l2 = getVectorLine(moveVector, center, length / 2);
+    return [l1[1], l2[1]];
+  } else {
+    const fNdx = flex === "a" ? 1 : 0;
+    const mNdx = flex === "a" ? 0 : 1;
+    const line = getVectorLine(
+      mNdx === 1 ? moveVector : moveVector.multiplyScalar(-1),
+      points[fNdx],
+      length
+    );
+    const nPoints: Pos[] = [];
+    nPoints[fNdx] = points[fNdx];
+    nPoints[mNdx] = line[1];
+    return nPoints;
+  }
+};
+
+export const useLineDescribes = (line: Ref<LineData["lines"][0]>) => {
+  const d: any = mergeDescribes(line, {}, ["stroke", "strokeWidth"]);
+  d.strokeWidth.props = {
+    ...d.strokeWidth.props,
+    proportion: true,
+  };
+  d.strokeWidth.label = "粗细";
+  d.stroke.label = "颜色";
+
+  const store = useStore();
+  const lineData = computed(() => store.getTypeItems("line")[0]);
+  const points = computed(() => [
+    lineData.value.points.find((p) => p.id === line.value.a)!,
+    lineData.value.points.find((p) => p.id === line.value.b)!,
+  ]);
+
+  let setLineVector: Vector2;
+
+  d.length = {
+    type: "inputNum",
+    label: "线段长度",
+    "layout-type": "row",
+    get value() {
+      return lineLen(points.value[0], points.value[1]);
+    },
+    set value(val) {
+      if (!d.isChange) {
+        setLineVector = lineVector(points.value);
+      }
+      updateLineLength(lineData.value, line.value, val, undefined, setLineVector)
+    },
+  };
+  return d as PropertyDescribes
+};

+ 76 - 5
src/core/components/line/attach-view.ts

@@ -15,14 +15,15 @@ import {
 import { LineData } from ".";
 import { getJoinLine } from "./attach-server";
 import { MathUtils } from "three";
-import { diffArrayChange, flatPositions, rangMod } from "@/utils/shared";
+import { diffArrayChange, rangMod } from "@/utils/shared";
 import { globalWatch, installGlobalVar } from "@/core/hook/use-global-vars";
 import { useStore } from "@/core/store";
-import { IconData } from "../icon";
-import { computed, reactive, Ref, watch, watchEffect } from "vue";
+import { computed, reactive, watchEffect } from "vue";
 import { Transform } from "konva/lib/Util";
-import { useTestPoints } from "@/core/hook/use-debugger";
 import { sortFn } from "@/core/store/store";
+import { getLineIconEndpoints, getSnapLine } from "../line-icon";
+import { useTestPoints } from "@/core/hook/use-debugger";
+import { useDrawIngData } from "@/core/hook/use-draw";
 
 const minAngle = MathUtils.degToRad(0.1);
 const palAngle = MathUtils.degToRad(20);
@@ -157,7 +158,7 @@ export const useGetExtendPolygon = installGlobalVar(() => {
 });
 
 // 计算与icon相差的多边形
-export const useGetDiffPolygons = installGlobalVar(() => {
+export const useGetDiffIconPolygons = installGlobalVar(() => {
   const store = useStore();
   const iconPolygons = reactive({}) as { [key in string]: Pos[] };
   const icons = computed(() => store.getTypeItems("icon"));
@@ -209,3 +210,73 @@ export const useGetDiffPolygons = installGlobalVar(() => {
     onDestroy: stopWatch,
   };
 });
+
+// 计算与icon相差的多边形
+export const useGetDiffLineIconPolygons = (line: LineData["lines"][0]) => {
+  const store = useStore();
+  const drawStore = useDrawIngData()
+  const linePoints = computed(() => getSnapLine(store, { lineId: line.id }));
+  const linevv = computed(() => verticalVector(lineVector(linePoints.value!)));
+  const icons = computed(() => {
+    if (drawStore.lineIcon) {
+      return drawStore.lineIcon.concat(store.getTypeItems("lineIcon"))
+    } else {
+      return store.getTypeItems("lineIcon")
+    }
+  });
+  const lineIcons = computed(() =>
+    icons.value.filter((icon) => icon.lineId === line.id)
+  );
+  const interPolygons = computed(() => {
+    const lineSteps = lineIcons.value
+      .map((icon) =>
+        icon.endLen > icon.startLen
+          ? [icon.startLen, icon.endLen]
+          : [icon.endLen, icon.startLen]
+      )
+      .sort((line1, line2) => line1[0] - line2[0]);
+    if (!lineSteps.length) return [];
+
+    const interSteps: number[][] = [];
+    let i = 0;
+    do {
+      const startStep = lineSteps[i][0];
+      let endStep = lineSteps[i][1];
+      for (i++; i < lineSteps.length; i++) {
+        if (lineSteps[i][0] <= endStep) {
+          if (lineSteps[i][1] > endStep) {
+            endStep = lineSteps[i][1];
+          }
+        } else {
+          break;
+        }
+      }
+      interSteps.push([startStep, endStep]);
+    } while (i < lineSteps.length);
+
+    const interLines = interSteps.map((steps) =>
+      getLineIconEndpoints(linePoints.value!, {
+        startLen: steps[0],
+        endLen: steps[1],
+      })
+    );
+
+    return interLines.map(interLine => {
+      const topOffset = linevv.value.clone().multiplyScalar(line.strokeWidth)
+      const botOffset = topOffset.clone().multiplyScalar(-1)
+      return [
+        topOffset.clone().add(interLine[0]),
+        topOffset.clone().add(interLine[1]),
+        botOffset.clone().add(interLine[1]),
+        botOffset.clone().add(interLine[0]),
+      ]
+    })
+  });
+
+  return (polygon: Pos[]) => {
+    if (!interPolygons.value.length) {
+      return [polygon];
+    }
+    return getDiffPolygons(polygon, interPolygons.value)
+  };
+};

+ 25 - 144
src/core/components/line/single-line.vue

@@ -1,16 +1,15 @@
 <template>
-  <!-- 使用 更复杂的线段拖拽 -->
   <!-- @dragstart="emit('dragLineStart', props.line)"
     @update:line="(ps) => emit('dragLine', props.line, ps)"
     @dragend="emit('dragLineEnd', props.line)" -->
   <EditLine
     :ref="(d: any) => shape = d?.shape"
-    :data="{ ...line, ...style, lineJoin: 'miter' }"
+    :data="data"
     :opacity="0"
     :points="points"
     :closed="false"
     :id="line.id"
-    :disablePoint="!showEditPoint || isDrawIng"
+    :disablePoint="true"
     :ndx="0"
     @dragstart="dragstartHandler(points.map((item) => item.id))"
     @update:line="
@@ -40,32 +39,12 @@
     v-for="polygon in diffPolygons"
     :config="{
       points: flatPositions(polygon),
-      fill: line.stroke,
+      fill: isDrawIng ? themeColor : style.stroke,
       closed: true,
       listening: false,
     }"
     v-if="diffPolygons"
   />
-  <template v-if="showEditPoint">
-    <EditPoint
-      v-for="(point, ndx) in points"
-      :key="point.id"
-      :size="line.strokeWidth"
-      :points="points"
-      :opacity="1"
-      :drawIng="ndx === 0 && isDrawIng"
-      :ndx="ndx"
-      :closed="false"
-      :id="line.id"
-      :disable="addMode"
-      :color="isDrawIng ? themeColor : style.stroke"
-      @dragstart="dragstartHandler([point.id])"
-      @update:position="(p) => emit('updatePoint', { ...point, ...p })"
-      @dragend="dragendHandler"
-      @delete="delPoint(point)"
-    />
-  </template>
-
   <PropertyUpdate
     :describes="describes"
     :data="line"
@@ -73,10 +52,9 @@
     :name="shapeName"
     @change="
       () => {
-        isStartChange || emit('updateBefore', []);
+        describes.length.isChange || emit('updateBefore', []);
         emit('updateLine', { ...line });
         emit('update');
-        isStartChange = false;
       }
     "
     @delete="delHandler"
@@ -86,30 +64,22 @@
 
 <script lang="ts" setup>
 import { computed, ref, watchEffect } from "vue";
-import { getMouseStyle, getSnapInfos, LineData, shapeName } from "./index.ts";
+import { getMouseStyle, LineData, shapeName } from "./index.ts";
 import { flatPositions, onlyId } from "@/utils/shared.ts";
 import EditLine from "../share/edit-line.vue";
-import { getVectorLine, lineCenter, lineLen, lineVector, Pos } from "@/utils/math.ts";
+import { Pos } from "@/utils/math.ts";
 import { Line } from "konva/lib/shapes/Line";
 import { DC } from "@/deconstruction.js";
-import { useMode } from "@/core/hook/use-status.ts";
-import { Mode } from "@/constant/mode.ts";
 import SizeLine from "../share/size-line.vue";
 import { useConfig } from "@/core/hook/use-config.ts";
-import { ComponentSnapInfo } from "../index.ts";
-import { useCustomSnapInfos } from "@/core/hook/use-snap.ts";
-import { mergeDescribes } from "@/core/html-mount/propertys/index.ts";
 import { PropertyUpdate, Operate } from "../../html-mount/propertys/index.ts";
 import {
   useAnimationMouseStyle,
   useMouseShapeStatus,
 } from "@/core/hook/use-mouse-status.ts";
 import { themeColor } from "@/constant";
-import { Vector2 } from "three";
-import { useGetDiffPolygons, useGetExtendPolygon } from "./attach-view.ts";
-import EditPoint from "../share/edit-point.vue";
-
-const mode = useMode();
+import { useGetDiffLineIconPolygons, useGetExtendPolygon } from "./attach-view.ts";
+import { useLineDataSnapInfos, useLineDescribes } from "./attach-server.ts";
 
 const props = defineProps<{
   line: LineData["lines"][number];
@@ -119,31 +89,9 @@ const props = defineProps<{
   dragPointIds?: string[];
 }>();
 
-const getExtendPolygon = useGetExtendPolygon();
-const showEditPoint = computed(() => {
-  return (
-    (!mode.include(Mode.readonly) && !mode.include(Mode.draw) && props.canEdit) ||
-    isDrawIng.value
-  );
-});
-const polygon = computed(() =>
-  getExtendPolygon(props.data, props.line, !showEditPoint.value)
-);
-const dragIng = computed(
-  () =>
-    props.dragPointIds?.length &&
-    (props.dragPointIds.includes(props.line.a) ||
-      props.dragPointIds.includes(props.line.b))
-);
-const getDiffPolygons = useGetDiffPolygons();
-const diffPolygons = computed(() =>
-  !dragIng.value ? getDiffPolygons(polygon.value) : [polygon.value]
-);
-
 const emit = defineEmits<{
   (e: "updatePoint", value: LineData["points"][number]): void;
   (e: "addPoint", value: LineData["points"][number]): void;
-  (e: "delPoint", value: LineData["points"][number]): void;
   (e: "delLine"): void;
   (e: "updateLine", value: LineData["lines"][number]): void;
   (e: "updateBefore", value: string[]): void;
@@ -154,125 +102,58 @@ const emit = defineEmits<{
   (e: "dragLineEnd", value: LineData["lines"][number]): void;
 }>();
 
+const getExtendPolygon = useGetExtendPolygon();
+const polygon = computed(() => getExtendPolygon(props.data, props.line, true));
+const getDiffPolygons = useGetDiffLineIconPolygons(props.line);
+const diffPolygons = computed(() => getDiffPolygons(polygon.value));
+
 const shape = ref<DC<Line>>();
 const points = computed(() => [
   props.data.points.find((p) => p.id === props.line.a)!,
   props.data.points.find((p) => p.id === props.line.b)!,
 ]);
 const lineData = computed(() => props.line);
-const describes = mergeDescribes(lineData, {}, ["stroke", "strokeWidth"]);
-const d = describes as any;
-d.strokeWidth.props = {
-  ...d.strokeWidth.props,
-  proportion: true,
-};
-d.strokeWidth.label = "粗细";
-d.stroke.label = "颜色";
-
-let isStartChange = false;
-let setLineVector: Vector2;
-describes.length = {
-  type: "inputNum",
-  label: "线段长度",
-  "layout-type": "row",
-  get value() {
-    return lineLen(points.value[0], points.value[1]);
-  },
-  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(
-      (line) => line.a === points.value[0].id || line.b === points.value[0].id
-    ).length;
-    const bCount = props.data.lines.filter(
-      (line) => line.a === points.value[1].id || line.b === points.value[1].id
-    ).length;
-
-    if (aCount === bCount || (aCount > 1 && bCount > 1)) {
-      // 两端伸展
-      const center = lineCenter(points.value);
-      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 lineVec =
-        aCount > 1 ? setLineVector : setLineVector.clone().multiplyScalar(-1);
-      const line = getVectorLine(lineVec, start, val);
-      emit("updatePoint", { ...points.value[changeNdx], ...line[1] });
-    }
-  },
-  props: { proportion: true },
-};
+const describes = useLineDescribes(lineData);
 
 const delHandler = () => {
   emit("updateBefore", [props.line.a, props.line.b]);
   emit("delLine");
   emit("update");
 };
-const menus = [
-  {
-    label: "删除",
-    handler: delHandler,
-  },
-];
+const menus = [{ label: "删除", handler: delHandler }];
 
 const status = useMouseShapeStatus(shape);
-const [mstyle] = useAnimationMouseStyle({
+const [style] = useAnimationMouseStyle({
   shape,
   getMouseStyle,
   data: lineData as any,
 });
+
 const isDrawIng = computed(
   () =>
     props.addMode && props.data.lines.indexOf(props.line) === props.data.lines.length - 1
 );
 
-const style = computed(() =>
-  isDrawIng.value ? { ...mstyle.value, stroke: themeColor } : mstyle.value
-);
-
 const addPoint = (pos: Pos) => {
   emit("updateBefore", []);
   emit("addPoint", { ...points.value[0], ...pos, id: onlyId() });
   emit("update");
 };
 
+const lDataSnap = useLineDataSnapInfos();
 const config = useConfig();
-const delPoint = (point: LineData["points"][number]) => {
-  emit("updateBefore", []);
-  emit("delPoint", point);
-  emit("update");
-};
-
-const infos = useCustomSnapInfos();
-let snapInfos: ComponentSnapInfo[];
 const dragstartHandler = (eIds: string[]) => {
   emit("updateBefore", eIds);
-
-  snapInfos = getSnapInfos({
-    ...props.data,
-    lines: props.data.lines.filter(
-      (item) => !(eIds.includes(item.a) || eIds.includes(item.b))
-    ),
-    points: props.data.points.filter((item) => !eIds.includes(item.id)),
-  });
-  snapInfos.forEach((item) => {
-    infos.add(item);
-  });
+  lDataSnap.update(eIds);
 };
 const dragendHandler = () => {
   emit("update");
-  snapInfos.forEach((item) => infos.remove(item));
+  lDataSnap.clear();
 };
 
-// const padstart = computed(() => {
-//   props.line.
-// })
+defineExpose({
+  get shape() {
+    return shape.value;
+  },
+});
 </script>

+ 105 - 0
src/core/components/line/single-point.vue

@@ -0,0 +1,105 @@
+<template>
+  <template v-for="(point, ndx) in points" :key="point.id">
+    <EditPoint
+      v-if="showEditPoint"
+      :ref="(r: any) => shapes[ndx] = r?.shape"
+      :size="line.strokeWidth"
+      :points="points"
+      :opacity="1"
+      :drawIng="ndx === 1 && isDrawIng"
+      :ndx="ndx"
+      :closed="false"
+      :id="line.id"
+      :disable="addMode"
+      :color="isDrawIng ? themeColor : style.stroke"
+      @dragstart="dragstartHandler([point.id])"
+      @update:position="(p) => emit('updatePoint', { ...point, ...p })"
+      @dragend="dragendHandler"
+      @delete="delPoint(point)"
+    />
+  </template>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from "vue";
+import { getMouseStyle, LineData } from "./index.ts";
+import { useAnimationMouseStyle, useShapeIsHover } from "@/core/hook/use-mouse-status.ts";
+import { themeColor } from "@/constant";
+import EditPoint from "../share/edit-point.vue";
+import { DC } from "@/deconstruction.js";
+import { Line } from "konva/lib/shapes/Line";
+import { useLineDataSnapInfos } from "./attach-server.ts";
+import { Circle } from "konva/lib/shapes/Circle";
+
+const props = defineProps<{
+  lineShape?: DC<Line>;
+  line: LineData["lines"][number];
+  addMode?: boolean;
+  data: LineData;
+  dragPointIds?: string[];
+}>();
+const shapes = ref<DC<Circle>[]>([]);
+
+const emit = defineEmits<{
+  (e: "updatePoint", value: LineData["points"][number]): void;
+  (e: "delPoint", value: LineData["points"][number]): void;
+  (e: "updateBefore", value: string[]): void;
+  (e: "update"): void;
+}>();
+
+const points = computed(() => [
+  props.data.points.find((p) => p.id === props.line.a)!,
+  props.data.points.find((p) => p.id === props.line.b)!,
+]);
+const lineData = computed(() => props.line);
+const lineShape = computed(() => props.lineShape);
+
+const [style] = useAnimationMouseStyle({
+  shape: lineShape,
+  getMouseStyle,
+  data: lineData as any,
+});
+const isLineHover = useShapeIsHover(lineShape)[0];
+const isPointHovera = useShapeIsHover(computed(() => shapes.value[0]))[0];
+const isPointHoverb = useShapeIsHover(computed(() => shapes.value[1]))[0];
+const isDrawIng = computed(
+  () =>
+    props.addMode && props.data.lines.indexOf(props.line) === props.data.lines.length - 1
+);
+const showEditPoint = computed(
+  () =>
+    (props.dragPointIds &&
+      props.dragPointIds.includes(props.line.a) &&
+      props.dragPointIds.includes(props.line.b)) ||
+    isLineHover.value ||
+    isPointHovera.value ||
+    isPointHoverb.value ||
+    dragIng.value ||
+    isDrawIng.value
+);
+
+const delPoint = (point: LineData["points"][number]) => {
+  emit("updateBefore", []);
+  emit("delPoint", point);
+  emit("update");
+};
+
+const lDataSnap = useLineDataSnapInfos();
+const dragIng = ref(false);
+const dragstartHandler = (eIds: string[]) => {
+  emit("updateBefore", eIds);
+  lDataSnap.update(eIds);
+  dragIng.value = true;
+};
+const dragendHandler = () => {
+  dragIng.value = false;
+  emit("update");
+  lDataSnap.clear();
+};
+
+defineExpose({
+  get shape() {
+    return shapes.value;
+  },
+});
+</script>

+ 21 - 10
src/core/components/line/temp-line.vue

@@ -2,7 +2,8 @@
   <v-group :id="data.id" ref="shape">
     <v-group>
       <singleLine
-        v-for="item in data.lines"
+        v-for="(item, ndx) in data.lines"
+        :ref="(r: any) => lineShapes[ndx] = r?.shape"
         :key="item.id"
         :line="item"
         :data="data"
@@ -10,7 +11,6 @@
         :can-edit="!initData && !operMode.mulSelection"
         :dragPointIds="dragPointIds"
         @add-point="(p) => addPointHandler(p, item)"
-        @del-point="delPointHandler"
         @update-point="updatePointHandler"
         @update-before="updateBeforeHandler"
         @update="updateHandler"
@@ -20,6 +20,21 @@
         @dragLineEnd="dragendLineHandler"
       />
     </v-group>
+    <v-group>
+      <singlePoint
+        v-for="(item, ndx) in data.lines"
+        :line-shape="lineShapes[ndx]"
+        :dragPointIds="dragPointIds"
+        :key="item.id"
+        :line="item"
+        :data="data"
+        :add-mode="addMode"
+        @del-point="delPointHandler"
+        @update-point="updatePointHandler"
+        @update-before="updateBeforeHandler"
+        @update="updateHandler"
+      />
+    </v-group>
   </v-group>
 </template>
 
@@ -27,6 +42,7 @@
 import { onlyId } from "@/utils/shared.ts";
 import { delPoint, LineData } from "./index.ts";
 import singleLine from "./single-line.vue";
+import singlePoint from "./single-point.vue";
 import { useInitData } from "./use-draw.ts";
 import {
   genMoveLineHandler,
@@ -34,7 +50,7 @@ import {
   NLineDataCtx,
   normalLineData,
 } from "./attach-server.ts";
-import { computed, ref, watch } from "vue";
+import { computed, ref } from "vue";
 import { useZIndex } from "@/core/hook/use-layer.ts";
 import { DC } from "@/deconstruction.js";
 import { Group } from "konva/lib/Group";
@@ -42,6 +58,7 @@ import { useOperMode } from "@/core/hook/use-status.ts";
 import { useMouseShapeStatus } from "@/core/hook/use-mouse-status.ts";
 import { Pos } from "@/utils/math.ts";
 import { useSnapConfig, useSnapResultInfo } from "@/core/hook/use-snap.ts";
+import { Line } from "konva/lib/shapes/Line";
 
 const props = defineProps<{
   data: LineData;
@@ -49,19 +66,13 @@ const props = defineProps<{
   canEdit?: boolean;
 }>();
 
-watch(
-  () => props.data,
-  (ndata, odata) => {
-    console.log("data change", ndata, odata, ndata === odata);
-  }
-);
-
 const operMode = useOperMode();
 const initData = useInitData();
 const emit = defineEmits<{
   (e: "updateShape"): void;
 }>();
 
+const lineShapes = ref<DC<Line>[]>([]);
 const data = computed(() => {
   if (!initData.value || props.addMode) return props.data;
   return initData.value;

+ 1 - 1
src/core/components/line/use-draw.ts

@@ -243,7 +243,7 @@ export const useDraw = () => {
         viewTransform: viewTransform.value,
         history,
         store,
-      });
+      })!;
       isTempDraw = true;
     };
 

+ 4 - 2
src/core/components/share/edit-line.vue

@@ -6,6 +6,7 @@
       strokeWidth: data.strokeWidth,
       opacity: opacity || 0,
       stroke: data.stroke,
+      zIndex: zIndex,
       points: flatPositions(points),
       hitStrokeWidth: data.strokeWidth,
     }"
@@ -43,7 +44,7 @@ import { useViewer } from "@/core/hook/use-viewer";
 import { useMode } from "@/core/hook/use-status";
 import { Mode } from "@/constant/mode";
 
-type LData = Required<Pick<SLineData, "strokeWidth" | "stroke">>;
+type LData = Pick<SLineData, "strokeWidth" | "stroke">;
 const props = defineProps<{
   data: LData;
   points: Pos[];
@@ -52,6 +53,7 @@ const props = defineProps<{
   closed?: boolean;
   disablePoint?: boolean;
   opacity?: number;
+  zIndex?: number;
 }>();
 const emit = defineEmits<{
   (e: "update:line", data: Pos[]): void;
@@ -162,7 +164,7 @@ useShapeClick(point, () => {
 const center = computed(() => lineCenter(points.value));
 const pointStyle = computed(() => {
   const color = getMouseColors(props.data.stroke || themeColor);
-  const size = props.data.strokeWidth + 6 || 5;
+  const size = (props.data.strokeWidth || 1) + 6 || 5;
   return {
     radius: size / 2,
     fill: "#fff",

+ 16 - 8
src/core/helper/debugger.vue

@@ -1,12 +1,20 @@
 <template>
-  <v-circle
-    v-for="(p, ndx) in points"
-    :config="{
-      ...p,
-      radius: testPoints[ndx].size || 8,
-      fill: testPoints[ndx].color || 'red',
-    }"
-  />
+  <v-group v-for="(p, ndx) in points" :x="p.x" :y="p.y">
+    <v-circle
+      :config="{
+        radius: testPoints[ndx].size || 8,
+        fill: testPoints[ndx].color || 'red',
+      }"
+    />
+    <v-text
+      v-if="testPoints[ndx].text"
+      :config="{
+        text: testPoints[ndx].text,
+        y: testPoints[ndx].size || 8,
+        fill: testPoints[ndx].color || 'red',
+      }"
+    />
+  </v-group>
 </template>
 
 <script lang="ts" setup>

+ 5 - 3
src/core/hook/use-component.ts

@@ -7,6 +7,7 @@ import {
   nextTick,
   onUnmounted,
   reactive,
+  ref,
   Ref,
   shallowReactive,
   shallowRef,
@@ -171,6 +172,7 @@ export type UseComponentStatusProps<
   alignment?: (data: T, mat: Transform) => void;
   getMouseStyle: any;
   defaultStyle: any;
+  selfData?: boolean
   propertys: PropertyKeys;
   debug?: boolean;
   noJoinZindex?: boolean;
@@ -197,7 +199,7 @@ export const useComponentStatus = <S extends EntityShape, T extends DrawItem>(
     },
     { flush: "sync" }
   );
-  const data = useAutomaticData(() => args.props.data);
+  const data = useAutomaticData(() => args.props.data, data => args.selfData ? data : copy(data));
   const [style, pause, resume] = useAnimationMouseStyle({
     data: data,
     shape,
@@ -373,10 +375,10 @@ export const useOnComponentBoundChange = () => {
       const repShape = (shape.repShape as T) || shape;
       const syncBd = () => update(repShape);
       repShape.on("transform", syncBd);
-      shape.on("bound-change", () => syncBd);
+      shape.on("bound-change", syncBd);
       return () => {
         repShape.off("transform", syncBd);
-        shape.off("bound-change", () => syncBd);
+        shape.off("bound-change", syncBd);
       };
     };
     const cleanups: (() => void)[] = [];

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

@@ -4,7 +4,7 @@ import { Pos } from "@/utils/math";
 import { DrawItem } from "../components";
 
 export const useTestPoints = installGlobalVar(() => {
-  return ref<(Pos & {color?: string, size?: number})[]>([]);
+  return ref<(Pos & {color?: string, size?: number, text?: string})[]>([]);
 });
 
 

+ 28 - 14
src/core/hook/use-draw.ts

@@ -1,5 +1,5 @@
 import { computed, h, nextTick, reactive, ref, watch, watchEffect } from "vue";
-import { installGlobalVar, useCursor, useStage } from "./use-global-vars";
+import { globalWatch, installGlobalVar, useCursor, useRunHook, useStage } from "./use-global-vars";
 import { useCan, useMode, useOperMode } from "./use-status";
 import {
   Area,
@@ -16,6 +16,7 @@ import {
   components,
   ComponentSnapInfo,
   ComponentValue,
+  DrawData,
   DrawItem,
   ShapeType,
   SnapPoint,
@@ -139,8 +140,11 @@ export const useInteractiveDrawShapeAPI = installGlobalVar(() => {
     },
     drawing: computed(() => mode.include(Mode.draw)),
     drawType: computed(() => {
-      return interactiveProps.value?.type && components[interactiveProps.value?.type].addMode
-    })
+      return (
+        interactiveProps.value?.type &&
+        components[interactiveProps.value?.type].addMode
+      );
+    }),
   };
 });
 
@@ -171,7 +175,10 @@ export const useDrawRunning = (shapeType?: ShapeType) => {
   return isRunning;
 };
 
-export const usePointBeforeHandler = (enableTransform = false, enableSnap = false) => {
+export const usePointBeforeHandler = (
+  enableTransform = false,
+  enableSnap = false
+) => {
   const operMode = useOperMode();
   const conversionPosition = useConversionPosition(enableTransform);
   const snap = enableSnap && useSnap();
@@ -242,7 +249,6 @@ const useInteractiveDrawTemp = <T extends ShapeType>({
   const clear = () => {
     beforeHandler.clear();
     beforeHandler.clearRef();
-    
   };
 
   const ia = useIA({
@@ -290,10 +296,12 @@ const useInteractiveDrawTemp = <T extends ShapeType>({
 
     const storeAddItem = (cItem: any) => {
       const items = store.getTypeItems(type);
-      if (items.some((item) => item.id === cItem.id)) {
-        store.setItem(type, { id: cItem.id, value: cItem });
-      } else {
-        store.addItem(type, cItem);
+      if (!obj.checkItemData || obj.checkItemData(cItem)) {
+        if (items.some((item) => item.id === cItem.id)) {
+          store.setItem(type, { id: cItem.id, value: cItem });
+        } else {
+          store.addItem(type, cItem);
+        }
       }
     };
 
@@ -576,10 +584,12 @@ export const useInteractiveDrawPen = <T extends ShapeType>(type: T) => {
 
     const storeAddItem = (cItem: any) => {
       const items = store.getTypeItems(type);
-      if (items.some((item) => item.id === cItem.id)) {
-        store.setItem(type, { id: cItem.id, value: cItem });
-      } else {
-        store.addItem(type, cItem);
+      if (!obj.checkItemData || obj.checkItemData(cItem)) {
+        if (items.some((item) => item.id === cItem.id)) {
+          store.setItem(type, { id: cItem.id, value: cItem });
+        } else {
+          store.addItem(type, cItem);
+        }
       }
     };
 
@@ -648,7 +658,6 @@ export const useInteractiveDrawPen = <T extends ShapeType>(type: T) => {
   return items;
 };
 
-
 export const useInteractiveAdd = <T extends ShapeType>(type: T) => {
   const obj = components[type];
   if (obj.addMode === "dots") {
@@ -659,3 +668,8 @@ export const useInteractiveAdd = <T extends ShapeType>(type: T) => {
     return useInteractiveDrawDots(type);
   }
 };
+
+export const useDrawIngData = installGlobalVar(() => {
+  const drawStore: DrawData = reactive({});
+  return drawStore;
+});

+ 14 - 0
src/core/hook/use-dxf.ts

@@ -33,6 +33,7 @@ import {
 import { nextTick } from "vue";
 import { SLineData } from "../components/sequent-line";
 import { IRect } from "konva/lib/types";
+import { LineIconData } from "../components/line-icon";
 
 export const useGetDXF = () => {
   const store = useStore();
@@ -375,6 +376,19 @@ export const useGetDXF = () => {
             }
           });
           break;
+        case 'lineIcon':
+          const lineIconItem = _item as LineIconData;
+          const linePathGroup = $stage
+            .findOne<Group>(`#${lineIconItem.id}`)!
+            .findOne<Group>(".rep-position")!;
+
+          await writeImage(linePathGroup, () => {
+            lineIconItem.strokeScaleEnabled = true
+            return () => {
+              lineIconItem.strokeScaleEnabled = false
+            }
+          });
+          break;
       }
     }
 

+ 1 - 1
src/core/hook/use-global-vars.ts

@@ -237,7 +237,7 @@ export const usePointerIntersections = installGlobalVar(() => {
       return;
     }
     shapes.value = stage.value.getNode().getAllIntersections(pos.value) || [];
-  }, 300);
+  }, 100);
 
   const stopWatch = watch(pos, updateShapes);
 

+ 2 - 0
src/core/html-mount/propertys/index.ts

@@ -47,8 +47,10 @@ export type PropertyDescribes = Record<
     label: string;
     default?: PropertyValue<PropertyType>;
     'layout-type'?: string;
+    isChange: boolean,
     props?: Partial<PropertyProps<PropertyType>>;
     value?: PropertyValue<PropertyType>;
+    onChange?: () => void,
     sort?: number
   }
 >;

+ 11 - 2
src/core/html-mount/propertys/mount.vue

@@ -32,8 +32,17 @@
                   ? describes[key].value
                   : props.data && props.data[key]
               "
-              @update:value="(val: any) => updateValue(key, val)"
-              @change="changeHandler"
+              @update:value="(val: any) => {
+                updateValue(key, val)
+                describes[key].isChange = true
+              }"
+              @change="
+                () => {
+                  changeHandler();
+                  describes[key].onChange && describes[key].onChange();
+                  describes[key].isChange = false;
+                }
+              "
               :is="propertyComponents[val.type]"
               :key="key"
             />

+ 6 - 1
src/core/renderer/draw-group.vue

@@ -8,13 +8,18 @@
 </template>
 
 <script setup lang="ts">
+import { onUnmounted } from "vue";
 import { ShapeType, components } from "../components";
-import { useInteractiveAdd } from "../hook/use-draw.ts";
+import { useDrawIngData, useInteractiveAdd } from "../hook/use-draw.ts";
 
 const props = defineProps<{ type: ShapeType }>();
 const type = props.type;
+const drawStore = useDrawIngData();
 const tempItems = components[type].useDraw
   ? components[type].useDraw()
   : useInteractiveAdd(props.type);
 const ShapeComponent = components[type].TempComponent || components[type].Component;
+drawStore[type] = (tempItems || []) as any;
+
+onUnmounted(() => delete drawStore[type]);
 </script>

+ 40 - 16
src/example/constant.ts

@@ -2,16 +2,16 @@ export type IconItem = {
   wall?: boolean;
   icon: string;
   name: string;
-  color?: string
-  parse?: { fill?: string; stroke?: string };
+  color?: string;
+  parse?: { fill?: string; stroke?: string; type?: string };
 };
 type IconGroup = {
-  name: string,
+  name: string;
   children: {
-    name: string,
-    children: IconItem[]
-  }[]
-}
+    name: string;
+    children: IconItem[];
+  }[];
+};
 
 export const iconGroups: IconGroup[] = [
   {
@@ -20,20 +20,44 @@ export const iconGroups: IconGroup[] = [
       {
         name: "门",
         children: [
-          { wall: true, icon: "men_l", name: "左开门" },
-          
-          { wall: true, icon: "men", name: "右开门" },
-          { wall: true, icon: "shuangkaimen", name: "双开门" },
-          { wall: true, icon: "yimen", name: "移门" },
-          { wall: true, icon: "yakou", name: "哑口" },
+          {
+            wall: true,
+            icon: "men_l",
+            name: "左开门",
+            parse: { type: "align-bottom" },
+          },
+          {
+            wall: true,
+            icon: "men",
+            name: "右开门",
+            parse: { type: "align-bottom" },
+          },
+          {
+            wall: true,
+            icon: "shuangkaimen",
+            name: "双开门",
+            parse: { type: "align-bottom" },
+          },
+          { wall: true, icon: "yimen", name: "移门", parse: { type: "full" } },
+          { wall: true, icon: "yakou", name: "哑口", parse: { type: "full" } },
         ],
       },
       {
         name: "窗",
         children: [
-          { wall: true, icon: "chuang", name: "窗" },
-          { wall: true, icon: "piaochuang", name: "飘窗" },
-          { wall: true, icon: "luodichuang", name: "落地窗" },
+          { wall: true, icon: "chuang", name: "窗", parse: { type: "full" } },
+          {
+            wall: true,
+            icon: "piaochuang",
+            name: "飘窗",
+            parse: { type: "align-bottom" },
+          },
+          {
+            wall: true,
+            icon: "luodichuang",
+            name: "落地窗",
+            parse: { type: "full" },
+          },
         ],
       },
       {