Browse Source

feat: 设置泳池旋转

liaojiaxing 6 months ago
parent
commit
d46ea7f579

+ 1 - 0
apps/designer/.umirc.ts

@@ -1,6 +1,7 @@
 import { defineConfig } from "umi";
 
 export default defineConfig({
+  base: '/',
   favicons: [
     '/favicon.ico'
   ],

+ 10 - 3
apps/designer/src/components/CustomInput.tsx

@@ -15,7 +15,7 @@ export default function CustomInput(props: {
   onChange?: (value: string) => void;
 }) {
   const { value, styles, node, placeholder, txtStyle } = props;
-  const [isEditing, setIsEditing] = useSafeState(true);
+  const [isEditing, setIsEditing] = useSafeState(!node.data?.lock);
   
   const style = useMemo(() => {
     const top = styles.textVAlign === 'top' ? 0 : styles.textVAlign === 'middle' ? '50%' : undefined;
@@ -36,6 +36,13 @@ export default function CustomInput(props: {
     props.onChange?.(val);
   }
 
+  const handleSetEditing = (edit: boolean) => {
+    if(node.data?.lock) {
+      return
+    }
+    setIsEditing(edit);
+  }
+
   // useEffect(() => {
   //   // 处理字体加载
   //   // @ts-ignore
@@ -56,7 +63,7 @@ export default function CustomInput(props: {
           className="absolute"
           style={style}
           onChange={(e) => handleChange(e.target.value)}
-          onBlur={() => setIsEditing(false)}
+          onBlur={() => handleSetEditing(false)}
           autoSize
           autoFocus
         />
@@ -64,7 +71,7 @@ export default function CustomInput(props: {
         <div
           className="absolute w-full"
           style={style}
-          onDoubleClick={() => setIsEditing(true)}
+          onDoubleClick={() => handleSetEditing(true)}
         >
           {value}
         </div>

+ 319 - 0
apps/designer/src/components/lane/Pool.tsx

@@ -0,0 +1,319 @@
+import { Node } from "@antv/x6";
+import CustomInput from "../CustomInput";
+import { LaneItem, StageItem } from "@/types";
+
+export default function Pool({ 
+  node,
+  fillContent,
+  strokeWidth,
+  strokeColor,
+ }: { 
+  node: Node,
+  fillContent: string,
+  strokeWidth: number,
+  strokeColor: string,
+}) {
+  const {
+    poolName,
+    text,
+    lane,
+    stage,
+    direction,
+    textDirection,
+    headerHeight,
+    stageWidth,
+    laneHeadHeight,
+  } = node.getData();
+
+  const size = node.getSize();
+  
+  const handleStartMove = () => {
+    node.setData({
+      ignoreDrag: false,
+    });
+  };
+
+  const handleEndMove = () => {
+    node.setData({
+      ignoreDrag: true,
+    });
+  };
+
+  return direction === "vertical" ? (
+    // 垂直泳池
+    <>
+      <div
+        className="w-full relative cursor-move"
+        style={{
+          height: headerHeight,
+          background: fillContent,
+          borderBottom: `solid ${strokeWidth}px ${strokeColor}`,
+        }}
+        onMouseEnter={handleStartMove}
+        onMouseLeave={handleEndMove}
+      >
+        <CustomInput
+          value={poolName}
+          styles={text}
+          node={node}
+          onChange={(val) => node.setData({ poolName: val })}
+        />
+      </div>
+      <div
+        className="relative content"
+        style={{ height: `calc(100% - ${headerHeight}px)` }}
+      >
+        {/* 阶段 */}
+        {stage.length ? (
+          <div className="stage h-full w-full absolute left-0 top-0">
+            {stage.map((stageItem: StageItem, index: number) => {
+              return (
+                <div
+                  key={index}
+                  style={{
+                    width: "100%",
+                    height: stageItem.height - strokeWidth,
+                    borderBottom:
+                      index < stage.length - 1
+                        ? `solid ${strokeWidth}px ${strokeColor}`
+                        : "node",
+                  }}
+                >
+                  <div
+                    className="relative stage-item cursor-move"
+                    style={{
+                      width: stageWidth,
+                      background: fillContent,
+                      height: stageItem.height - 2 * strokeWidth,
+                      borderRight: `solid ${strokeWidth}px ${strokeColor}`,
+                    }}
+                    onMouseEnter={handleStartMove}
+                    onMouseLeave={handleEndMove}
+                  >
+                    <CustomInput
+                      value={stageItem.name}
+                      styles={text}
+                      node={node}
+                      txtStyle={{
+                        transform: `rotate(-90deg) translateX(-${stageItem.height}px)`,
+                        transformOrigin: "0 0",
+                        height: stageWidth,
+                        width: stageItem.height,
+                      }}
+                      onChange={(val) => {
+                        node.setData({
+                          stage: stage.map((item: StageItem, i: number) => {
+                            if (index === i) {
+                              return {
+                                ...item,
+                                name: val,
+                              };
+                            }
+                          }),
+                        });
+                      }}
+                    />
+                  </div>
+                </div>
+              );
+            })}
+          </div>
+        ) : (
+          ""
+        )}
+        {/* 泳道 */}
+        <div
+          className="lane flex h-full absolute top-0"
+          style={{ left: stage.length ? stageWidth : 0 }}
+        >
+          {lane.map((item: any, index: number) => (
+            <div
+              key={index}
+              style={{
+                width: item.width,
+                borderRight:
+                  index === lane.length - 1
+                    ? "none"
+                    : `solid ${strokeWidth}px ${strokeColor}`,
+              }}
+            >
+              <div
+                className="flex-1 w-full relative cursor-move"
+                style={{
+                  height: laneHeadHeight,
+                  background: fillContent,
+                  borderBottom: `solid ${strokeWidth}px ${strokeColor}`,
+                }}
+                onMouseEnter={handleStartMove}
+                onMouseLeave={handleEndMove}
+              >
+                <CustomInput
+                  value={item.name}
+                  styles={text}
+                  node={node}
+                  onChange={(val) => {
+                    node.setData({
+                      lane: lane.map((item: LaneItem, i: number) => {
+                        if (index === i) {
+                          return {
+                            ...item,
+                            name: val,
+                          };
+                        }
+                        return item;
+                      }),
+                    });
+                  }}
+                />
+              </div>
+            </div>
+          ))}
+        </div>
+      </div>
+    </>
+  ) : (
+    // 水平泳池
+    <>
+      <div
+        className="h-full relative cursor-move inline-block"
+        style={{
+          width: headerHeight,
+          background: fillContent,
+          borderRight: `solid ${strokeWidth}px ${strokeColor}`,
+        }}
+        onMouseEnter={handleStartMove}
+        onMouseLeave={handleEndMove}
+      >
+        <CustomInput
+          value={poolName}
+          styles={text}
+          node={node}
+          onChange={(val) => node.setData({ poolName: val })}
+          txtStyle={{
+            transform: `rotate(-90deg) translateX(-${size.height}px)`,
+            transformOrigin: "0 0",
+            width: size.height,
+            height: headerHeight,
+          }}
+        />
+      </div>
+      <div
+        className="relative content h-full inline-block"
+        style={{ width: `calc(100% - ${headerHeight}px)` }}
+      >
+        {/* 阶段 */}
+        {stage.length ? (
+          <div className="stage h-full w-full absolute top-0 left-0 flex">
+            {stage.map((stageItem: StageItem, index: number) => {
+              return (
+                <div
+                  key={index}
+                  style={{
+                    width: stageItem.height - strokeWidth,
+                    height: "100%",
+                    borderRight:
+                      index < stage.length - 1
+                        ? `solid ${strokeWidth}px ${strokeColor}`
+                        : "node",
+                  }}
+                >
+                  <div
+                    className="relative stage-item cursor-move"
+                    style={{
+                      width: stageItem.height - 2 * strokeWidth,
+                      background: fillContent,
+                      height: stageWidth,
+                      borderBottom: `solid ${strokeWidth}px ${strokeColor}`,
+                    }}
+                    onMouseEnter={handleStartMove}
+                    onMouseLeave={handleEndMove}
+                  >
+                    <CustomInput
+                      value={stageItem.name}
+                      styles={text}
+                      node={node}
+                      onChange={(val) => {
+                        node.setData({
+                          stage: stage.map((item: StageItem, i: number) => {
+                            if (index === i) {
+                              return {
+                                ...item,
+                                name: val,
+                              };
+                            }
+                          }),
+                        });
+                      }}
+                    />
+                  </div>
+                </div>
+              );
+            })}
+          </div>
+        ) : (
+          ""
+        )}
+        {/* 泳道 */}
+        <div
+          className="lane absolute left-0"
+          style={{
+            top: stage.length ? stageWidth : 0,
+            width: `100%`,
+            height: `calc(100% - ${stage.length ? stageWidth : 0}px)`,
+          }}
+        >
+          {lane.map((item: LaneItem, index: number) => (
+            <div
+              key={index}
+              style={{
+                width: "100%",
+                height: item.width,
+                borderBottom:
+                  index === lane.length - 1
+                    ? "none"
+                    : `solid ${strokeWidth}px ${strokeColor}`,
+              }}
+            >
+              <div
+                className="flex-1 w-full relative cursor-move"
+                style={{
+                  width: laneHeadHeight,
+                  height: "100%",
+                  background: fillContent,
+                  borderRight: `solid ${strokeWidth}px ${strokeColor}`,
+                }}
+                onMouseEnter={handleStartMove}
+                onMouseLeave={handleEndMove}
+              >
+                <CustomInput
+                  value={item.name}
+                  styles={text}
+                  node={node}
+                  txtStyle={{
+                    transform: `rotate(-90deg) translateX(-${item.width}px)`,
+                    transformOrigin: "0 0",
+                    width: item.width - strokeWidth,
+                    height: laneHeadHeight,
+                  }}
+                  onChange={(val) => {
+                    node.setData({
+                      lane: lane.map((item: LaneItem, i: number) => {
+                        if (index === i) {
+                          return {
+                            ...item,
+                            name: val,
+                          };
+                        }
+                        return item;
+                      }),
+                    });
+                  }}
+                />
+              </div>
+            </div>
+          ))}
+        </div>
+      </div>
+    </>
+  );
+}

+ 254 - 0
apps/designer/src/components/lane/hooks/useLane.ts

@@ -0,0 +1,254 @@
+import { Graph, Node } from "@antv/x6";
+import { useEffect, useRef } from "react";
+import { LaneItem, StageItem } from "@/types";
+export const useLane = (node: Node, graph: Graph) => {
+  const { lane, stage, stageWidth, stroke, headerHeight, direction } =
+    node.getData<{
+      lane: LaneItem[];
+      stage: StageItem[];
+      headerHeight: number;
+      stageWidth: number;
+      stroke: { width: number };
+      direction: "vertical" | "horizontal";
+    }>();
+  const strokeWidth = stroke.width;
+  const lister = useRef<any>();
+
+  // 监听泳变化
+  useEffect(() => {
+    let width = node.getSize().width;
+    let height = node.getSize().height;
+
+    if (lane.length) {
+      if (direction === "vertical") {
+        width = lane.reduce(
+          (a: number, b: LaneItem) => a + b.width + strokeWidth,
+          0
+        );
+      } else {
+        height = lane.reduce(
+          (a: number, b: LaneItem) => a + b.width + strokeWidth,
+          0
+        );
+      }
+    }
+    if (stage.length) {
+      if (direction === "vertical") {
+        width += stageWidth;
+      } else {
+        height += stageWidth;
+      }
+    }
+
+    node.setSize({ width, height });
+  }, [lane.length]);
+
+  // 监听宽高变化
+  if (!lister.current) {
+    lister.current = node.on("change:size", (args) => {
+      const { lane, stage, direction } = node.data;
+      // 更新泳道宽度
+      if (lane.length) {
+        const originWidth = lane.reduce(
+          (a: number, b: LaneItem) => a + b.width,
+          0
+        );
+        const offset =
+          (direction === "vertical"
+            ? args?.current?.width || 0
+            : args?.current?.height || 0) -
+          originWidth -
+          (stage.length ? stageWidth : 0);
+        console.log(offset )
+        if (offset) {
+          node.setData({
+            lane: lane.map((item: LaneItem) => {
+              return {
+                ...item,
+                width: item.width + offset / lane.length - strokeWidth,
+              };
+            }),
+          });
+        }
+      }
+      // 更新阶段高度
+      if (stage.length) {
+        const originHeight = stage.reduce(
+          (a: number, b: StageItem) => a + b.height,
+          0
+        );
+        const offset =
+          (direction === "vertical"
+            ? args?.current?.height || 0
+            : args?.current?.width || 0) -
+          originHeight -
+          headerHeight;
+
+        if (offset) {
+          node.setData({
+            stage: stage.map((item: StageItem) => {
+              return {
+                ...item,
+                height: item.height + offset / stage.length - strokeWidth,
+              };
+            }),
+          });
+        }
+      }
+    });
+  }
+
+  // 插入阶段
+  const handleInsertStage = (x: number, y: number) => {
+    const stage = node.data.stage || [];
+    const { width, height } = node.getSize();
+    // 新阶段位置
+    const h1 =
+      direction === "vertical"
+        ? y - node.getPosition().y - headerHeight
+        : x - node.getPosition().x - headerHeight;
+    // 无阶段 从中切开
+    if (!stage.length) {
+      const h2 = direction === "vertical"
+      ? height - h1 - headerHeight
+      : width - h1 - headerHeight;
+      node.setData({
+        stage: [
+          {
+            name: "阶段",
+            height: h1,
+          },
+          {
+            name: "阶段",
+            height: h2,
+          },
+        ],
+      });
+      node.setSize({
+        width: width + (direction === "vertical" ? stageWidth : 0),
+        height: height + (direction === "horizontal" ? stageWidth : 0),
+      });
+    } else {
+      // 有阶段 则找出在哪一段内 再分成两段
+      let temp = 0; // 记录长度
+      for (let i = 0; i < stage.length; i++) {
+        const item = stage[i];
+        temp += item.height;
+        if (temp > h1) {
+          // 插入
+          const h3 = temp - h1;
+          const h4 = item.height - h3;
+          const newStages = [
+            {
+              name: "阶段",
+              height: h4,
+            },
+            {
+              ...item,
+              height: h3,
+            },
+          ];
+          stage.splice(i, 1, ...newStages);
+          node.setData(
+            {
+              stage: [...stage],
+            },
+            {
+              deep: false,
+            }
+          );
+          break;
+        }
+      }
+    }
+  };
+
+  // 插入泳道
+  const handleInsertLane = (x: number, y: number) => {
+    const lane = node.data.lane || [];
+    const stage = node.data.stage || [];
+    if (!lane.length) {
+      node.setData({
+        lane: [
+          {
+            name: "",
+            width: direction === "vertical" 
+            ? node.getSize().width - (stage.length ? stageWidth : 0)
+            : node.getSize().height - (stage.length ? stageWidth : 0),
+          },
+        ],
+      });
+    } else {
+      // 判断在哪一个泳道内
+      let temp = 0;
+      const w1 = direction === "vertical" 
+        ? x - node.getPosition().x
+        : y - node.getPosition().y;
+      for (let i = 0; i < lane.length; i++) {
+        const item = lane[i];
+        temp += item.width;
+        if (temp > w1) {
+          // 插入
+          lane.splice(i, 0, {
+            name: "",
+            width: lane[lane.length - 1].width,
+          });
+          node.setData({
+            lane: [...lane],
+          });
+          break;
+        }
+      }
+    }
+  };
+
+  const listerEmbedded = useRef<any>();
+  if (!listerEmbedded.current) {
+    listerEmbedded.current = graph.on("node:embedded", (args) => {
+      const { isSeparator, isLane, separatorDiration, laneDirection } =
+        args.node.data || {};
+      const { direction } = node.data;
+      // 分隔符
+      if (isSeparator && separatorDiration !== direction) {
+        const bbox = node.getBBox();
+        if (bbox.isIntersectWithRect(args.node.getBBox())) {
+          handleInsertStage(args.x, args.y);
+        }
+      }
+      // 泳道
+      if (isLane && laneDirection === direction) {
+        const bbox = node.getBBox();
+        if (bbox.isIntersectWithRect(args.node.getBBox())) {
+          handleInsertLane(args.x, args.y);
+        }
+      }
+    });
+  }
+
+  const handleChangeLane = (val: number | null) => {
+    if (!val) {
+      node.setData({ lane: [] }, { deep: false });
+      return;
+    }
+    const currentLanes = node.data.lane || [];
+    const currentLength = currentLanes.length;
+    const { direction } = node.data;
+
+    if (currentLength < val) {
+      let width = direction === "vertical" ? node.getSize().width : node.getSize().height;
+      if (currentLength > 0) {
+        width = currentLanes[currentLength - 1].width;
+      }
+      const newLanes = new Array(val - currentLength).fill({ width, name: "" });
+      node.setData({ lane: [...(node.data.lane || []), ...newLanes] });
+    } else {
+      node.updateData({
+        lane: currentLanes.slice(0, val),
+      });
+    }
+  };
+
+  return {
+    handleChangeLane,
+  };
+};

+ 12 - 368
apps/designer/src/components/lane/horizontalPool.tsx

@@ -2,30 +2,20 @@ import { CompoundedComponent } from "@/types";
 import { register } from "@antv/x6-react-shape";
 import { Graph, Node } from "@antv/x6";
 import { defaultData } from "../data";
-import CustomInput from "../CustomInput";
 import { useSizeHook, useShapeProps } from "@/hooks";
-import { useEffect, useRef, useState } from "react";
-import Setting from "./setting";
-import { LaneItem, StageItem } from "@/types";
+import { useState } from "react";
+import Setting from "./Setting";
+import Pool from "./Pool";
+import { useLane } from "./hooks/useLane";
 
 const component = ({ node, graph }: { node: Node; graph: Graph }) => {
   const {
-    poolName,
-    text,
     fill,
     stroke,
     opacity,
-    lane,
-    stage,
-    direction,
-    textDirection,
-    headerHeight,
-    stageWidth,
-    laneHeadHeight,
   } = node.getData();
   const [showSetting, setShowSetting] = useState(false);
   const { size, ref } = useSizeHook();
-  const lister = useRef<any>();
   const { fillContent, strokeColor, strokeWidth } = useShapeProps(
     fill,
     size,
@@ -41,371 +31,25 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
     if (node.id === args.node.id) setShowSetting(false);
   });
 
-  useEffect(() => {
-    let width = node.getSize().width;
-    let height = node.getSize().height;
+  const { handleChangeLane } = useLane(node, graph);
 
-    if (lane.length) {
-      height = lane.reduce(
-        (a: number, b: LaneItem) => a + b.width + strokeWidth,
-        0
-      );
-    }
-    if (stage.length) {
-      height += stageWidth;
-    }
-
-    node.setSize({ width, height });
-  }, [lane.length]);
-
-  // 监听宽高变化
-  if (!lister.current) {
-    lister.current = node.on("change:size", (args) => {
-      const lane = node.data.lane;
-      const stage = node.data.stage;
-      // 更新泳道宽度
-      if (lane.length) {
-        const originWidth = lane.reduce(
-          (a: number, b: LaneItem) => a + b.width,
-          0
-        );
-        const offsetY =
-          (args?.current?.height || 0) -
-          originWidth -
-          (stage.length ? stageWidth : 0);
-
-        if (offsetY) {
-          node.setData({
-            lane: lane.map((item: LaneItem) => {
-              return {
-                ...item,
-                width: item.width + offsetY / lane.length - strokeWidth,
-              };
-            }),
-          });
-        }
-      }
-      // 更新阶段高度
-      if (stage.length) {
-        const originHeight = stage.reduce(
-          (a: number, b: StageItem) => a + b.height,
-          0
-        );
-        const offsetX =
-          (args?.current?.width || 0) -
-          originHeight -  headerHeight;
-
-        if (offsetX) {
-          node.setData({
-            stage: stage.map((item: StageItem) => {
-              return {
-                ...item,
-                height: item.height + offsetX / stage.length - strokeWidth,
-              };
-            }),
-          });
-        }
-      }
-    });
-  }
-
-  const handleStartMove = () => {
-    node.setData({
-      ignoreDrag: false,
-    });
-  };
-
-  const handleEndMove = () => {
-    node.setData({
-      ignoreDrag: true,
-    });
-  };
-
-  // 插入阶段
-  const handleInsertStage = (x: number, y: number) => {
-    const stage = node.data.stage || [];
-    const { width, height } = node.getSize();
-    // 新阶段位置
-    const w1 = x - node.getPosition().x - headerHeight;
-    // 无阶段 从中切开
-    if (!stage.length) {
-      const w2 = width - w1 - headerHeight;
-      node.setData({
-        stage: [
-          {
-            name: "阶段",
-            height: w1,
-          },
-          {
-            name: "阶段",
-            height: w2,
-          },
-        ],
-      });
-      node.setSize({
-        width,
-        height: height + stageWidth
-      })
-    } else {
-      // 有阶段 则找出在哪一段内 再分成两段
-      let temp = 0; // 记录长度
-      for (let i = 0; i < stage.length; i++) {
-        const item = stage[i];
-        temp += item.height;
-        if (temp > w1) {
-          // 插入
-          const w3 = temp - w1;
-          const w4 = item.height - w3;
-          const newStages = [
-            {
-              name: "阶段",
-              height: w4,
-            },
-            {
-              ...item,
-              height: w3,
-            },
-          ];
-          stage.splice(i, 1, ...newStages);
-          node.setData(
-            {
-              stage: [...stage],
-            },
-            {
-              deep: false,
-            }
-          );
-          break;
-        }
-      }
-    }
-  };
-
-  // 插入泳道
-  const handleInsertLane = (x: number, _y: number) => {
-    console.log('插入泳道');
-    const lane = node.data.lane || [];
-    const stage = node.data.stage || [];
-    if(!lane.length) {
-      node.setData({
-        lane: [{
-          name: "",
-          width: node.getSize().height - (stage.length ? stageWidth : 0) 
-        }]
-      })
-    } else {
-      // 判断在哪一个泳道内
-      let temp = 0;
-      const w1 = x - node.getPosition().y;
-      for (let i = 0; i < lane.length; i++) {
-        const item = lane[i];
-        temp += item.width;
-        if (temp > w1) {
-          // 插入
-          lane.splice(i, 0, {
-            name: "",
-            width: lane[lane.length - 1].width
-          })
-          node.setData({
-            lane: [...lane]
-          });
-          break;
-        }
-      }
-    }
-  }
-
-  const listerEmbedded = useRef<any>();
-  if (!listerEmbedded.current) {
-    listerEmbedded.current = graph.on("node:embedded", (args) => {
-      const {isSeparator, isLane, separatorDiration, laneDirection } = args.node.data || {};
-      // 分隔符
-      if (isSeparator && separatorDiration !== direction) {
-        const bbox = node.getBBox();
-        if (bbox.isIntersectWithRect(args.node.getBBox())) {
-          handleInsertStage(args.x, args.y);
-        }
-      }
-      // 泳道
-      if (isLane && laneDirection === direction) {
-        const bbox = node.getBBox();
-        if (bbox.isIntersectWithRect(args.node.getBBox())) {
-          handleInsertLane(args.x, args.y);
-        }
-      }
-    });
-  }
-
-  const handleChangeLane = (val: number | null) => {
-    if (!val) {
-      node.setData({ lane: [] }, { deep: false });
-      return;
-    }
-    const currentLanes = node.data.lane || [];
-    const currentLength = currentLanes.length;
-
-    if (currentLength < val) {
-      let width = node.getSize().width;
-      if (currentLength > 0) {
-        width = currentLanes[currentLength - 1].width;
-      }
-      const newLanes = new Array(val - currentLength).fill({ width, name: "" });
-      node.setData({ lane: [...(node.data.lane || []), ...newLanes] });
-    } else {
-      node.updateData({
-        lane: currentLanes.slice(0, val),
-      });
-    }
-  };
   return (
     <>
       {showSetting && <Setting node={node} onChangeLane={handleChangeLane} />}
       <div
-        className="relative text-0 w-full h-full flex"
+        className="relative text-0 w-full h-full"
         style={{
           opacity: opacity / 100,
           border: `solid ${strokeWidth}px ${strokeColor}`,
         }}
         ref={ref}
       >
-        <div
-          className="h-full relative cursor-move"
-          style={{
-            width: headerHeight,
-            background: fillContent,
-            borderRight: `solid ${strokeWidth}px ${strokeColor}`,
-          }}
-          onMouseEnter={handleStartMove}
-          onMouseLeave={handleEndMove}
-        >
-          <CustomInput
-            value={poolName}
-            styles={text}
-            node={node}
-            onChange={(val) => node.setData({ poolName: val })}
-            txtStyle={{
-              transform: `rotate(-90deg) translateX(-${size.height}px)`,
-              transformOrigin: '0 0',
-              width: size.height,
-              height: headerHeight,
-            }}
-          />
-        </div>
-        <div
-          className="relative content h-full"
-          style={{ width: `calc(100% - ${headerHeight}px)` }}
-        >
-          {/* 阶段 */}
-          {stage.length ? (
-            <div
-              className="stage h-full w-full absolute top-0 left-0 flex"
-            >
-              {stage.map((stageItem: StageItem, index: number) => {
-                return (
-                  <div key={index} 
-                  style={{
-                    width: stageItem.height - strokeWidth,
-                    height: '100%',
-                    borderRight:
-                      index < stage.length - 1
-                        ? `solid ${strokeWidth}px ${strokeColor}`
-                        : "node",
-                  }}
-                  >
-                    <div
-                      className="relative stage-item cursor-move"
-                      style={{
-                        width: stageItem.height - 2 * strokeWidth,
-                        background: fillContent,
-                        height: stageWidth,
-                        borderBottom: `solid ${strokeWidth}px ${strokeColor}`,
-                      }}
-                      onMouseEnter={handleStartMove}
-                      onMouseLeave={handleEndMove}
-                    >
-                      <CustomInput
-                        value={stageItem.name}
-                        styles={text}
-                        node={node}
-                        onChange={(val) => {
-                          node.setData({
-                            stage: stage.map((item: StageItem, i: number) => {
-                              if (index === i) {
-                                return {
-                                  ...item,
-                                  name: val,
-                                };
-                              }
-                            }),
-                          });
-                        }}
-                      />
-                    </div>
-                  </div>
-                );
-              })}
-            </div>
-          ) : (
-            ""
-          )}
-          {/* 泳道 */}
-          <div className="lane absolute left-0" style={{ 
-              top: stage.length ? stageWidth : 0, 
-              width: `100%`,
-              height: `calc(100% - ${stage.length ? stageWidth : 0}px)`
-            }}>
-            {lane.map((item: LaneItem, index: number) => (
-              <div
-                key={index}
-                style={{
-                  width: '100%',
-                  height: item.width,
-                  borderBottom:
-                    index === lane.length - 1
-                      ? "none"
-                      : `solid ${strokeWidth}px ${strokeColor}`,
-                }}
-              >
-                <div
-                  className="flex-1 w-full relative cursor-move"
-                  style={{
-                    width: laneHeadHeight,
-                    height: '100%',
-                    background: fillContent,
-                    borderRight: `solid ${strokeWidth}px ${strokeColor}`,
-                  }}
-                  onMouseEnter={handleStartMove}
-                  onMouseLeave={handleEndMove}
-                >
-                  <CustomInput
-                    value={item.name}
-                    styles={text}
-                    node={node}
-                    txtStyle={{
-                      transform: `rotate(-90deg) translateX(-${item.width}px)`,
-                      transformOrigin: '0 0',
-                      width: item.width - strokeWidth,
-                      height: laneHeadHeight,
-                    }}
-                    onChange={(val) => {
-                      node.setData({
-                        lane: lane.map((item: LaneItem, i: number) => {
-                          if (index === i) {
-                            return {
-                              ...item,
-                              name: val,
-                            };
-                          }
-                          return item;
-                        }),
-                      });
-                    }}
-                  />
-                </div>
-              </div>
-            ))}
-          </div>
-        </div>
+        <Pool
+          node={node}
+          fillContent={fillContent}
+          strokeWidth={strokeWidth}
+          strokeColor={strokeColor}
+        />
       </div>
     </>
   );

apps/designer/src/components/lane/image/vertialSeparator.png → apps/designer/src/components/lane/image/verticalSeparator.png


+ 2 - 2
apps/designer/src/components/lane/index.ts

@@ -1,7 +1,7 @@
 import verticalPool from "./verticalPool"
 import verticalLane from "./verticalLane"
 import horizontalSeparator from "./horizontalSeparator"
-import vertialSeparator from "./verticalSeparator"
+import verticalSeparator from "./verticalSeparator"
 import horizontalPool from "./horizontalPool"
 import horizontalLane from "./horizontalLane"
 
@@ -9,7 +9,7 @@ export const lane = [
   verticalPool,
   verticalLane,
   horizontalSeparator,
-  vertialSeparator,
+  verticalSeparator,
   horizontalPool,
   horizontalLane
 ]

+ 12 - 7
apps/designer/src/components/lane/setting.tsx

@@ -1,6 +1,5 @@
 import { Node } from "@antv/x6";
 import { Button, InputNumber, Divider, ConfigProvider } from "antd";
-import React from "react";
 
 export default function setting({
   node,
@@ -15,6 +14,16 @@ export default function setting({
     textDirection: string;
   }>();
 
+  const handleSetDirtion = (val: "vertical" | "horizontal") => {
+    if(val === direction) return;
+    node.setData({ direction: val });
+    const { width, height } = node.getSize();
+    node.setSize({
+      width: height,
+      height: width
+    })
+  }
+
   return (
     <ConfigProvider prefixCls="shalu">
       <div
@@ -35,17 +44,13 @@ export default function setting({
           size="small"
           className={`rotate--90 mr-8px ${direction === "horizontal" ? "active" : ""}`}
           icon={<i className="iconfont icon-yongdao rotate-90" />}
-          onClick={() => {
-            node.setData({ direction: "horizontal" });
-          }}
+          onClick={() => handleSetDirtion("horizontal")}
         />
         <Button
           size="small"
           className={`${direction === "vertical" ? "active" : ""}`}
           icon={<i className="iconfont icon-yongdao" />}
-          onClick={() => {
-            node.setData({ direction: "vertical" });
-          }}
+          onClick={() => handleSetDirtion("vertical")}
         />
         <Divider type="vertical" />
         <Button

+ 17 - 356
apps/designer/src/components/lane/verticalPool.tsx

@@ -2,35 +2,29 @@ import { CompoundedComponent } from "@/types";
 import { register } from "@antv/x6-react-shape";
 import { Graph, Node } from "@antv/x6";
 import { defaultData } from "../data";
-import CustomInput from "../CustomInput";
 import { useSizeHook, useShapeProps } from "@/hooks";
-import { useEffect, useRef, useState } from "react";
-import Setting from "./setting";
-import { LaneItem, StageItem } from "@/types";
+import { useState } from "react";
+import Setting from "./Setting";
+import Pool from "./Pool";
+import { useLane } from "./hooks/useLane";
 
 const component = ({ node, graph }: { node: Node; graph: Graph }) => {
   const {
-    poolName,
-    text,
     fill,
     stroke,
     opacity,
-    lane,
-    stage,
-    direction,
-    textDirection,
-    headerHeight,
-    stageWidth,
   } = node.getData();
+
   const [showSetting, setShowSetting] = useState(false);
   const { size, ref } = useSizeHook();
-  const lister = useRef<any>();
   const { fillContent, strokeColor, strokeWidth } = useShapeProps(
     fill,
     size,
     stroke
   );
 
+  const { handleChangeLane } = useLane(node, graph);
+
   graph.on("node:selected", (args) => {
     setShowSetting(
       graph.getSelectedCells().length === 1 && node.id === args.node.id
@@ -40,224 +34,9 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
     if (node.id === args.node.id) setShowSetting(false);
   });
 
-  useEffect(() => {
-    let width = node.getSize().width;
-    let height = node.getSize().height;
-
-    if (lane.length) {
-      width = lane.reduce(
-        (a: number, b: LaneItem) => a + b.width + strokeWidth,
-        0
-      );
-    }
-    if (stage.length) {
-      width += stageWidth;
-    }
-
-    node.setSize({ width, height });
-  }, [lane.length]);
-
-  // 监听宽高变化
-  if (!lister.current) {
-    lister.current = node.on("change:size", (args) => {
-      const lane = node.data.lane;
-      const stage = node.data.stage;
-      // 更新泳道宽度
-      if (lane.length) {
-        const originWidth = lane.reduce(
-          (a: number, b: LaneItem) => a + b.width,
-          0
-        );
-        const offsetX =
-          (args?.current?.width || 0) -
-          originWidth -
-          (stage.length ? stageWidth : 0);
-
-        if (offsetX) {
-          node.setData({
-            lane: lane.map((item: LaneItem) => {
-              return {
-                ...item,
-                width: item.width + offsetX / lane.length - strokeWidth,
-              };
-            }),
-          });
-        }
-      }
-      // 更新阶段高度
-      if (stage.length) {
-        const originHeight = stage.reduce(
-          (a: number, b: StageItem) => a + b.height,
-          0
-        );
-        const offsetY =
-          (args?.current?.height || 0) -
-          originHeight -  headerHeight;
-
-        if (offsetY) {
-          node.setData({
-            stage: stage.map((item: StageItem) => {
-              return {
-                ...item,
-                height: item.height + offsetY / stage.length - strokeWidth,
-              };
-            }),
-          });
-        }
-      }
-    });
-  }
-
-  const handleStartMove = () => {
-    node.setData({
-      ignoreDrag: false,
-    });
-  };
-
-  const handleEndMove = () => {
-    node.setData({
-      ignoreDrag: true,
-    });
-  };
-
-  // 插入阶段
-  const handleInsertStage = (x: number, y: number) => {
-    const stage = node.data.stage || [];
-    const { width, height } = node.getSize();
-    // 新阶段位置
-    const h1 = y - node.getPosition().y - headerHeight;
-    // 无阶段 从中切开
-    if (!stage.length) {
-      const h2 = height - h1 - headerHeight;
-      node.setData({
-        stage: [
-          {
-            name: "阶段",
-            height: h1,
-          },
-          {
-            name: "阶段",
-            height: h2,
-          },
-        ],
-      });
-      node.setSize({
-        width: width + stageWidth,
-        height
-      })
-    } else {
-      // 有阶段 则找出在哪一段内 再分成两段
-      let temp = 0; // 记录长度
-      for (let i = 0; i < stage.length; i++) {
-        const item = stage[i];
-        temp += item.height;
-        if (temp > h1) {
-          // 插入
-          const h3 = temp - h1;
-          const h4 = item.height - h3;
-          const newStages = [
-            {
-              name: "阶段",
-              height: h4,
-            },
-            {
-              ...item,
-              height: h3,
-            },
-          ];
-          stage.splice(i, 1, ...newStages);
-          node.setData(
-            {
-              stage: [...stage],
-            },
-            {
-              deep: false,
-            }
-          );
-          break;
-        }
-      }
-    }
-  };
-
-  // 插入泳道
-  const handleInsertLane = (x: number, _y: number) => {
-    console.log('插入泳道');
-    const lane = node.data.lane || [];
-    const stage = node.data.stage || [];
-    if(!lane.length) {
-      node.setData({
-        lane: [{
-          name: "",
-          width: node.getSize().width - (stage.length ? stageWidth : 0) 
-        }]
-      })
-    } else {
-      // 判断在哪一个泳道内
-      let temp = 0;
-      const w1 = x - node.getPosition().x;
-      for (let i = 0; i < lane.length; i++) {
-        const item = lane[i];
-        temp += item.width;
-        if (temp > w1) {
-          // 插入
-          lane.splice(i, 0, {
-            name: "",
-            width: lane[lane.length - 1].width
-          })
-          node.setData({
-            lane: [...lane]
-          });
-          break;
-        }
-      }
-    }
-  }
-
-  const listerEmbedded = useRef<any>();
-  if (!listerEmbedded.current) {
-    listerEmbedded.current = graph.on("node:embedded", (args) => {
-      const {isSeparator, isLane, separatorDiration, laneDirection } = args.node.data || {};
-      // 分隔符
-      if (isSeparator && separatorDiration !== direction) {
-        const bbox = node.getBBox();
-        if (bbox.isIntersectWithRect(args.node.getBBox())) {
-          handleInsertStage(args.x, args.y);
-        }
-      }
-      // 泳道
-      if (isLane && laneDirection === direction) {
-        const bbox = node.getBBox();
-        if (bbox.isIntersectWithRect(args.node.getBBox())) {
-          handleInsertLane(args.x, args.y);
-        }
-      }
-    });
-  }
-
-  const handleChangeLane = (val: number | null) => {
-    if (!val) {
-      node.setData({ lane: [] }, { deep: false });
-      return;
-    }
-    const currentLanes = node.data.lane || [];
-    const currentLength = currentLanes.length;
-
-    if (currentLength < val) {
-      let width = node.getSize().width;
-      if (currentLength > 0) {
-        width = currentLanes[currentLength - 1].width;
-      }
-      const newLanes = new Array(val - currentLength).fill({ width, name: "" });
-      node.setData({ lane: [...(node.data.lane || []), ...newLanes] });
-    } else {
-      node.updateData({
-        lane: currentLanes.slice(0, val),
-      });
-    }
-  };
   return (
     <>
+      {showSetting && <Setting node={node} onChangeLane={handleChangeLane} />}
       <div
         className="relative text-0 w-full h-full"
         style={{
@@ -266,132 +45,12 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
         }}
         ref={ref}
       >
-        {showSetting && <Setting node={node} onChangeLane={handleChangeLane} />}
-        <div
-          className="w-full relative cursor-move"
-          style={{
-            height: headerHeight,
-            background: fillContent,
-            borderBottom: `solid ${strokeWidth}px ${strokeColor}`,
-          }}
-          onMouseEnter={handleStartMove}
-          onMouseLeave={handleEndMove}
-        >
-          <CustomInput
-            value={poolName}
-            styles={text}
-            node={node}
-            onChange={(val) => node.setData({ poolName: val })}
-          />
-        </div>
-        <div
-          className="relative content"
-          style={{ height: `calc(100% - ${headerHeight}px)` }}
-        >
-          {/* 阶段 */}
-          {stage.length ? (
-            <div
-              className="stage h-full w-full absolute left-0 top-0"
-            >
-              {stage.map((stageItem: StageItem, index: number) => {
-                return (
-                  <div key={index} 
-                  style={{
-                    width: '100%',
-                    height: stageItem.height - strokeWidth,
-                    borderBottom:
-                      index < stage.length - 1
-                        ? `solid ${strokeWidth}px ${strokeColor}`
-                        : "node",
-                  }}
-                  >
-                    <div
-                      className="relative stage-item cursor-move"
-                      style={{
-                        width: stageWidth,
-                        background: fillContent,
-                        height: stageItem.height - 2 * strokeWidth,
-                        borderRight: `solid ${strokeWidth}px ${strokeColor}`,
-                      }}
-                      onMouseEnter={handleStartMove}
-                      onMouseLeave={handleEndMove}
-                    >
-                      <CustomInput
-                        value={stageItem.name}
-                        styles={text}
-                        node={node}
-                        txtStyle={{
-                          transform: `rotate(-90deg) translateX(-${stageItem.height}px)`,
-                          transformOrigin: "0 0",
-                          height: stageWidth,
-                          width: stageItem.height,
-                        }}
-                        onChange={(val) => {
-                          node.setData({
-                            stage: stage.map((item: StageItem, i: number) => {
-                              if (index === i) {
-                                return {
-                                  ...item,
-                                  name: val,
-                                };
-                              }
-                            }),
-                          });
-                        }}
-                      />
-                    </div>
-                  </div>
-                );
-              })}
-            </div>
-          ) : (
-            ""
-          )}
-          {/* 泳道 */}
-          <div className="lane flex h-full absolute top-0" style={{ left: stage.length ? stageWidth : 0 }}>
-            {lane.map((item: any, index: number) => (
-              <div
-                key={index}
-                style={{
-                  width: item.width,
-                  borderRight:
-                    index === lane.length - 1
-                      ? "none"
-                      : `solid ${strokeWidth}px ${strokeColor}`,
-                }}
-              >
-                <div
-                  className="flex-1 w-full h-40px relative cursor-move"
-                  style={{
-                    background: fillContent,
-                    borderBottom: `solid ${strokeWidth}px ${strokeColor}`,
-                  }}
-                  onMouseEnter={handleStartMove}
-                  onMouseLeave={handleEndMove}
-                >
-                  <CustomInput
-                    value={item.name}
-                    styles={text}
-                    node={node}
-                    onChange={(val) => {
-                      node.setData({
-                        lane: lane.map((item: LaneItem, i: number) => {
-                          if (index === i) {
-                            return {
-                              ...item,
-                              name: val,
-                            };
-                          }
-                          return item;
-                        }),
-                      });
-                    }}
-                  />
-                </div>
-              </div>
-            ))}
-          </div>
-        </div>
+        <Pool
+          node={node}
+          fillContent={fillContent}
+          strokeWidth={strokeWidth}
+          strokeColor={strokeColor}
+        />
       </div>
     </>
   );
@@ -418,8 +77,10 @@ const verticalPool: CompoundedComponent = {
       lane: [],
       // 阶段
       stage: [],
-      // 泳名称高度
+      // 泳名称高度
       headerHeight: 40,
+      // 泳道名称高度
+      laneHeadHeight: 40,
       // 阶段宽度
       stageWidth: 20,
       // 泳池方向

+ 6 - 6
apps/designer/src/components/lane/verticalSeparator.tsx

@@ -17,24 +17,24 @@ const component = ({ node, graph }: { node: Node, graph: Graph }) => {
 };
 
 register({
-  shape: "custom-react-vertialSeparator",
+  shape: "custom-react-verticalSeparator",
   width: 2,
   height: 270,
   effect: ["data"],
   component: component,
 });
 
-const vertialSeparator: CompoundedComponent = {
+const verticalSeparator: CompoundedComponent = {
   name: "分隔符(需拖动到水平泳池上)",
-  icon: require("./image/vertialSeparator.png"),
+  icon: require("./image/verticalSeparator.png"),
   node: {
-    shape: "custom-react-vertialSeparator",
+    shape: "custom-react-verticalSeparator",
     data: {
       label: "",
       // 分割符
       isSeparator: true,
       // 分割方向
-      separatorDiration: "vertial",
+      separatorDiration: "vertical",
       // 忽略拖动
       ignoreDrag: true,
       // 不用创建
@@ -44,4 +44,4 @@ const vertialSeparator: CompoundedComponent = {
   },
 };
 
-export default vertialSeparator;
+export default verticalSeparator;

+ 1 - 1
apps/designer/src/utils/hander.tsx

@@ -781,7 +781,7 @@ export const handleLock = (graph: Graph) => {
   const cells = graph.getSelectedCells();
   cells?.forEach((cell) => {
     cell.setData({
-      lock: false,
+      lock: true,
     });
   });
 };

+ 3 - 29
packages/x6-plugin-selection/package.json

@@ -1,33 +1,7 @@
 {
   "name": "@repo/x6-plugin-selection",
-  "version": "2.2.2",
+  "version": "0.0.0",
   "private": true,
-  "module": "src/index.ts",
-  "files": [
-    "src"
-  ],
-  "keywords": [
-    "plugin",
-    "selection",
-    "x6",
-    "antv"
-  ],
-  "author": {
-    "name": "bubkoo",
-    "email": "bubkoo.wy@gmail.com"
-  },
-  "license": "MIT",
-  "homepage": "https://x6.antv.antgroup.com/tutorial/plugins/selection",
-  "bugs": {
-    "url": "https://github.com/antvis/x6/issues"
-  },
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/antvis/x6.git",
-    "directory": "packages/x6-plugin-selection"
-  },
-  "publishConfig": {
-    "access": "public",
-    "registry": "https://registry.npmjs.org"
-  }
+  "module": "./src/index.ts",
+  "types": "./src/index.ts"
 }

+ 13 - 4
packages/x6-plugin-selection/src/index.ts

@@ -47,7 +47,7 @@ export class Selection
       ...Selection.defaultOptions,
       ...options,
     }
-
+   
     // CssLoader.ensure(this.name, content)
   }
 
@@ -306,7 +306,9 @@ export class Selection
 
   protected startListening() {
     this.graph.on('blank:mousedown', this.onBlankMouseDown, this)
+    this.graph.on('node:mousedown', this.onBlankMouseDown, this)
     this.graph.on('blank:click', this.onBlankClick, this)
+    this.graph.on('node:click', this.onBlankClick, this)
     this.graph.on('cell:mousemove', this.onCellMouseMove, this)
     this.graph.on('cell:mouseup', this.onCellMouseUp, this)
     this.selectionImpl.on('box:mousedown', this.onBoxMouseDown, this)
@@ -314,13 +316,18 @@ export class Selection
 
   protected stopListening() {
     this.graph.off('blank:mousedown', this.onBlankMouseDown, this)
+    this.graph.off('node:mousedown', this.onBlankMouseDown, this)
     this.graph.off('blank:click', this.onBlankClick, this)
+    this.graph.off('node:click', this.onBlankClick, this)
     this.graph.off('cell:mousemove', this.onCellMouseMove, this)
     this.graph.off('cell:mouseup', this.onCellMouseUp, this)
     this.selectionImpl.off('box:mousedown', this.onBoxMouseDown, this)
   }
 
-  protected onBlankMouseDown({ e }: EventArgs['blank:mousedown']) {
+  protected onBlankMouseDown({ e, node }: EventArgs['node:mousedown']) {
+    if (node && !node?.data?.isPage) {
+      return
+    }
     if (!this.allowBlankMouseDown(e)) {
       return
     }
@@ -344,8 +351,10 @@ export class Selection
     )
   }
 
-  protected onBlankClick() {
-    this.clean()
+  protected onBlankClick(args: EventArgs['node:click']) {
+    if(!args?.cell || args?.cell?.data?.isPage) {
+      this.clean();
+    }
   }
 
   protected allowRubberband(e: Dom.MouseDownEvent, strict?: boolean) {

+ 1 - 1
packages/x6-plugin-selection/src/selection.ts

@@ -42,7 +42,7 @@ export class SelectionImpl extends View<SelectionImpl.EventArgs> {
   constructor(options: SelectionImpl.Options) {
     super()
     this.options = options
-
+    
     if (this.options.model) {
       this.options.collection = this.options.model.collection
     }

+ 5 - 5
pnpm-lock.yaml

@@ -267,11 +267,7 @@ importers:
         specifier: ^5.3.3
         version: 5.5.4
 
-  packages/x6-plugin-selection:
-    devDependencies:
-      '@antv/x6':
-        specifier: ^2.x
-        version: 2.18.1
+  packages/x6-plugin-selection: {}
 
 packages:
 
@@ -661,9 +657,11 @@ packages:
     dependencies:
       lodash-es: 4.17.21
       utility-types: 3.11.0
+    dev: false
 
   /@antv/x6-geometry@2.0.5:
     resolution: {integrity: sha512-MId6riEQkxphBpVeTcL4ZNXL4lScyvDEPLyIafvWMcWNTGK0jgkK7N20XSzqt8ltJb0mGUso5s56mrk8ysHu2A==}
+    dev: false
 
   /@antv/x6-plugin-clipboard@2.1.6(@antv/x6@2.18.1):
     resolution: {integrity: sha512-roZPLnZx6PK8MBvee0QMo90fz/TXeF0WNe4EGin2NBq5M1I5XTWrYvA6N2XVIiWAAI67gjQeEE8TpkL7f8QdqA==}
@@ -756,6 +754,7 @@ packages:
       '@antv/x6-common': 2.0.17
       '@antv/x6-geometry': 2.0.5
       utility-types: 3.11.0
+    dev: false
 
   /@babel/code-frame@7.24.7:
     resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
@@ -14412,6 +14411,7 @@ packages:
   /utility-types@3.11.0:
     resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==}
     engines: {node: '>= 4'}
+    dev: false
 
   /utils-merge@1.0.1:
     resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}