antv3 x6 基本语法-流程图(二)

实现效果

    * 在左侧拖拽到右侧绘制流程图

    * 相关基本操作 )

 

image

 

 

页面布局创建: 左右两部分  vue部分

给每个拖拽区域绑定 

@mousedown="handleDragEnd 
<template>
  <div class="conatainer">
    <div class="left">
      <!-- 图形部分--start -->

      <div class="custom-node">
        <div
          class="node-dom dnd-circle dnd-start"
          @mousedown="handleDragEnd($event, { name: '开始', type: 'start' })"
        ></div>
        <div>开始</div>
      </div>
      <div class="custom-node">
        <div
          class="node-dom dnd-rect"
          @mousedown="handleDragEnd($event, { name: 'rect', type: 'node1' })"
        ></div>
        <div>节点1</div>
      </div>
      <div class="custom-node">
        <div
          class="node-dom dnd-polygon"
          @mousedown="handleDragEnd($event, { name: 'start', type: 'node2' })"
        ></div>
        <div>节点2</div>
      </div>
      <div class="custom-node">
        <div
          class="node-dom dnd-circle"
          @mousedown="handleDragEnd($event, { name: 'start', type: 'end' })"
        ></div>
        <div>结束</div>
      </div>
      <!-- 图形部分--end -->

      <div
        v-for="item in moduleList"
        :key="item.id"
        @mousedown="handleDragEnd($event, item)"
        style="text-align: center"
      >
        <img :src="item.image" alt="" width="50px" />
        <p>{{ item.name }}</p>
      </div>
    </div>
    <div class="right" ref="right">
      <div style="width: 100%; height: 100%">
        <div id="container"></div>
      </div>
    </div>
  </div>
</template>
View Code

 

js 部分

插件使用都在antv 直接导入,v3版本做了整合,不用每个单独引入

import {
  Clipboard,
  Graph,
  History,
  Keyboard,
  Selection,
  Shape,
  Snapline,
  Stencil,
  Transform,
  Dnd,
} from "@antv/x6";

 

桩子节点,上下左右 ,如果某个节点只有上下,可以在节点创建时进行覆盖

ports: {
        groups: {
          top: {
            position: "top" /* 顶部端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
          bottom: {
            position: "bottom" /* 底部端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
          left: {
            position: "left" /* 左侧端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
          right: {
            position: "right" /* 右侧端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
        },
        items: [
          // 端口实例(启用四个方向的端口)
          { group: "top", id: "top" },
          { group: "bottom", id: "bottom" },
          { group: "left", id: "left" },
          { group: "right", id: "right" },
        ],
      },

 

 

创建画布-基本配置

 this.graph = new Graph({
        container: document.getElementById("container"),
        // 设置画布大小,默认为容器大小
        // width: rightRef.offsetWidth,
        // height: rightRef.offsetHeight,
        grid: 10,

        mousewheel: {
          enabled: true,
          zoomAtMousePosition: true,
          modifiers: "ctrl",
          minScale: 0.5,
          maxScale: 3,
        },
        connecting: {
          router: "manhattan",
          connector: {
            name: "rounded",
            args: {
              radius: 8,
            },
          },
          anchor: "center",
          connectionPoint: "anchor",
          allowBlank: false,
          snap: {
            radius: 20,
          },
          createEdge() {
            return new Shape.Edge({
              attrs: {
                line: {
                  stroke: "#A2B1C3",
                  strokeWidth: 2,
                  targetMarker: {
                    name: "block",
                    width: 12,
                    height: 8,
                  },
                },
              },
              zIndex: 0,
            });
          },
          validateConnection({ targetMagnet }) {
            return !!targetMagnet;
          },
          highlighting: {
            magnetAdsorbed: {
              name: "stroke",
              args: {
                attrs: {
                  fill: "#5F95FF",
                  stroke: "#5F95FF",
                },
              },
            },
          },
        },

        autoResize: true,
        panning: true,
        // 设置画布背景颜色
        background: { color: "#F2F7FA" },
        grid: {
          visible: true,
          type: "doubleMesh", // 或者不用
          args: [
            {
              color: "#eee", // 主网格线颜色
              thickness: 1, // 主网格线宽度
            },
            {
              color: "#ddd", // 次网格线颜色
              thickness: 1, // 次网格线宽度
              factor: 4, // 主次网格线间隔
            },
          ],
        },
      });

 

使用dnd 插件 

  this.dnd = new Dnd({
        target: this.graph,
        scaled: false,
        // preview: (graph, node) => {
        //   node.attr({
        //     "body/stroke": "#40a9ff",
        //     "body/strokeWidth": 2,
        //     "body/fill": "#f0f8ff",
        //   });
        //   return node;
        // },
      });

 

注册节点
  Graph.registerNode(
        "custom-circle-start",
        {
          inherit: "circle",
          ports: { ...this.ports },
        },
        true,
      );

 

创建节点

// 创建圆圈节点
      const startNode = this.graph.createNode({
        shape: "custom-circle-start",
        id: IdValue,
        width: 38,
        height: 38,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: "#000000",
            fill: "#ffffff",
            rx: 10,
            ry: 10,
          },
        },
      });

 

画布渲染显示

this.dnd.start(node_type, e);

 

 

辅助操作

this.graph.bindKey(["ctrl+2", "meta+2"], () => {
        const zoom = this.graph.zoom();
        if (zoom > 0.5) {
          this.graph.zoom(-0.1);
        }
      });

      this.graph.on("cell:mouseenter", ({ cell }) => {
        const container = document.getElementById("container");
        const ports = container.querySelectorAll(".x6-port-body");
        showPorts(ports, true);
        if (cell.isNode()) {
          cell.addTools([
            {
              name: "button-remove",
              args: {
                x: 0,
                y: 0,
                offset: { x: 10, y: 10 },
              },
            },
          ]);
        } else {
          cell.addTools([
            {
              name: "button-remove",
              args: { distance: -40 },
            },
          ]);
        }
      });

      this.graph.on("cell:mouseleave", ({ cell }) => {
        if (cell.hasTool("button-remove")) {
          cell.removeTool("button-remove");
        }
        const container = document.getElementById("container");
        const ports = container.querySelectorAll(".x6-port-body");
        showPorts(ports, false);
      });

 

 

完整代码

<template>
  <div class="conatainer">
    <div class="left">
      <!-- 图形部分--start -->

      <div class="custom-node">
        <div
          class="node-dom dnd-circle dnd-start"
          @mousedown="handleDragEnd($event, { name: '开始', type: 'start' })"
        ></div>
        <div>开始</div>
      </div>
      <div class="custom-node">
        <div
          class="node-dom dnd-rect"
          @mousedown="handleDragEnd($event, { name: 'rect', type: 'node1' })"
        ></div>
        <div>节点1</div>
      </div>
      <div class="custom-node">
        <div
          class="node-dom dnd-polygon"
          @mousedown="handleDragEnd($event, { name: 'start', type: 'node2' })"
        ></div>
        <div>节点2</div>
      </div>
      <div class="custom-node">
        <div
          class="node-dom dnd-circle"
          @mousedown="handleDragEnd($event, { name: 'start', type: 'end' })"
        ></div>
        <div>结束</div>
      </div>
      <!-- 图形部分--end -->

      <div
        v-for="item in moduleList"
        :key="item.id"
        @mousedown="handleDragEnd($event, item)"
        style="text-align: center"
      >
        <img :src="item.image" alt="" width="50px" />
        <p>{{ item.name }}</p>
      </div>
    </div>
    <div class="right" ref="right">
      <div style="width: 100%; height: 100%">
        <div id="container"></div>
      </div>
    </div>
  </div>
</template>

<script>
import {
  Clipboard,
  Graph,
  History,
  Keyboard,
  Selection,
  Shape,
  Snapline,
  Stencil,
  Transform,
  Dnd,
} from "@antv/x6";
export default {
  name: "AntvX6",
  data() {
    return {
      graph: null,
      dnd: null,
      moduleList: [
        {
          id: 1,
          name: "节点1",
          image:
            "https://ts1.tc.mm.bing.net/th/id/OIP-C.mH9YLFEL5YdVxJM82mjVJQHaEo?w=285&h=211&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2",
        },
        {
          id: 8,
          name: "节点2",
          image:
            "https://ts1.tc.mm.bing.net/th/id/OIP-C.Mq9zt66lU2fko_h2OWHIlAHaE8?w=255&h=211&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2",
        },
        {
          id: 2,
          name: "节点3",
          image:
            "https://ts1.tc.mm.bing.net/th/id/OIP-C.g9UbVfyVZX-SfD09JcYr5QHaEK?w=283&h=211&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2",
        },
        {
          id: 3,
          name: "节点4",
          image:
            "https://ts1.tc.mm.bing.net/th/id/OIP-C.IJZgTNx1vp9EML_1wV5p2gHaEo?w=255&h=211&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2",
        },
      ],
      ports: {
        groups: {
          top: {
            position: "top" /* 顶部端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
          bottom: {
            position: "bottom" /* 底部端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
          left: {
            position: "left" /* 左侧端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
          right: {
            position: "right" /* 右侧端口 */,
            attrs: {
              circle: {
                r: 4,
                magnet: true,
                stroke: "#5F95FF",
                strokeWidth: 1,
                fill: "#fff",
              },
            },
          },
        },
        items: [
          // 端口实例(启用四个方向的端口)
          { group: "top", id: "top" },
          { group: "bottom", id: "bottom" },
          { group: "left", id: "left" },
          { group: "right", id: "right" },
        ],
      },
    };
  },
  mounted() {
    this.initGraph();
  },
  methods: {
    initGraph() {
      const rightRef = this.$refs.right;
      this.graph = new Graph({
        container: document.getElementById("container"),
        // 设置画布大小,默认为容器大小
        // width: rightRef.offsetWidth,
        // height: rightRef.offsetHeight,
        grid: 10,

        mousewheel: {
          enabled: true,
          zoomAtMousePosition: true,
          modifiers: "ctrl",
          minScale: 0.5,
          maxScale: 3,
        },
        connecting: {
          router: "manhattan",
          connector: {
            name: "rounded",
            args: {
              radius: 8,
            },
          },
          anchor: "center",
          connectionPoint: "anchor",
          allowBlank: false,
          snap: {
            radius: 20,
          },
          createEdge() {
            return new Shape.Edge({
              attrs: {
                line: {
                  stroke: "#A2B1C3",
                  strokeWidth: 2,
                  targetMarker: {
                    name: "block",
                    width: 12,
                    height: 8,
                  },
                },
              },
              zIndex: 0,
            });
          },
          validateConnection({ targetMagnet }) {
            return !!targetMagnet;
          },
          highlighting: {
            magnetAdsorbed: {
              name: "stroke",
              args: {
                attrs: {
                  fill: "#5F95FF",
                  stroke: "#5F95FF",
                },
              },
            },
          },
        },

        autoResize: true,
        panning: true,
        // 设置画布背景颜色
        background: { color: "#F2F7FA" },
        grid: {
          visible: true,
          type: "doubleMesh", // 或者不用
          args: [
            {
              color: "#eee", // 主网格线颜色
              thickness: 1, // 主网格线宽度
            },
            {
              color: "#ddd", // 次网格线颜色
              thickness: 1, // 次网格线宽度
              factor: 4, // 主次网格线间隔
            },
          ],
        },
      });

      // #region 使用插件
      this.graph
        .use(
          new Transform({
            resizing: true,
            rotating: true,
          }),
        )
        .use(
          new Selection({
            rubberband: true,
            showNodeSelectionBox: true,
          }),
        )
        .use(new Snapline())
        .use(new Keyboard())
        .use(new Clipboard())
        .use(new History());
      // #endregion

      this.dnd = new Dnd({
        target: this.graph,
        scaled: false,
        // preview: (graph, node) => {
        //   node.attr({
        //     "body/stroke": "#40a9ff",
        //     "body/strokeWidth": 2,
        //     "body/fill": "#f0f8ff",
        //   });
        //   return node;
        // },
      });

      // **************** 注册节点 -start ****************
      Graph.registerNode(
        "custom-node",
        {
          inherit: "rect", // 继承于 rect 节点
          width: 100,
          height: 40,
          markup: [
            {
              tagName: "rect", // 标签名称
              selector: "body", // 选择器
            },
            {
              tagName: "image",
              selector: "img",
            },
            {
              tagName: "text",
              selector: "label",
            },
          ],
          attrs: {
            body: {
              stroke: "#8f8f8f",
              strokeWidth: 1,
              fill: "#fff",
              rx: 6,
              ry: 6,
            },
            img: {
              "xlink:href":
                "https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png",
              width: 16,
              height: 16,
              x: 12,
              y: 12,
            },
          },
        },
        true,
      );

      Graph.registerNode(
        "custom-circle-start",
        {
          inherit: "circle",
          ports: { ...this.ports },
        },
        true,
      );

      Graph.registerNode(
        "custom-polygon",
        {
          inherit: "polygon",
          points: "0,10 10,0 20,10 10,20",
          ports: {
            ...this.ports,
          },
        },
        true,
      );

      Graph.registerNode(
        "custom-rect",
        {
          inherit: "rect",
          // attrs 可自定义
          ports: { ...this.ports },
        },
        true,
      );
      // **************** 注册节点 -end ****************
      this.graph.centerContent(); // 居中显示

      // 控制连接桩显示/隐藏
      const showPorts = (ports, show) => {
        for (let i = 0, len = ports.length; i < len; i += 1) {
          ports[i].style.visibility = show ? "visible" : "hidden";
        }
      };

      this.graph.bindKey(["meta+c", "ctrl+c"], () => {
        const cells = this.graph.getSelectedCells();
        if (cells.length) {
          this.graph.copy(cells);
        }
        return false;
      });

      this.graph.bindKey(["meta+x", "ctrl+x"], () => {
        const cells = this.graph.getSelectedCells();
        if (cells.length) {
          this.graph.cut(cells);
        }
        return false;
      });

      this.graph.bindKey(["meta+v", "ctrl+v"], () => {
        if (!this.graph.isClipboardEmpty()) {
          const cells = this.graph.paste({ offset: 32 });
          this.graph.cleanSelection();
          this.graph.select(cells);
        }
        return false;
      });

      this.graph.bindKey(["meta+z", "ctrl+z"], () => {
        if (this.graph.canUndo()) {
          this.graph.undo();
        }
        return false;
      });

      this.graph.bindKey(["meta+shift+z", "ctrl+shift+z"], () => {
        if (this.graph.canRedo()) {
          this.graph.redo();
        }
        return false;
      });

      this.graph.bindKey(["meta+a", "ctrl+a"], () => {
        const nodes = this.graph.getNodes();
        if (nodes) {
          this.graph.select(nodes);
        }
      });

      this.graph.bindKey("backspace", () => {
        const cells = this.graph.getSelectedCells();
        if (cells.length) {
          this.graph.removeCells(cells);
        }
      });

      this.graph.bindKey(["ctrl+1", "meta+1"], () => {
        const zoom = this.graph.zoom();
        if (zoom < 1.5) {
          this.graph.zoom(0.1);
        }
      });

      this.graph.bindKey(["ctrl+2", "meta+2"], () => {
        const zoom = this.graph.zoom();
        if (zoom > 0.5) {
          this.graph.zoom(-0.1);
        }
      });

      this.graph.on("cell:mouseenter", ({ cell }) => {
        const container = document.getElementById("container");
        const ports = container.querySelectorAll(".x6-port-body");
        showPorts(ports, true);
        if (cell.isNode()) {
          cell.addTools([
            {
              name: "button-remove",
              args: {
                x: 0,
                y: 0,
                offset: { x: 10, y: 10 },
              },
            },
          ]);
        } else {
          cell.addTools([
            {
              name: "button-remove",
              args: { distance: -40 },
            },
          ]);
        }
      });

      this.graph.on("cell:mouseleave", ({ cell }) => {
        if (cell.hasTool("button-remove")) {
          cell.removeTool("button-remove");
        }
        const container = document.getElementById("container");
        const ports = container.querySelectorAll(".x6-port-body");
        showPorts(ports, false);
      });
      this.graph.on("node:click", ({ x, y, node, cell }) => {
        this.currentCell = cell;
        if (cell.isNode() && !cell.attrs.typeName) {
          // 这可以写一些点击节点时和右侧表单交互的效果
        }
        // if (cell.hasTool("button")) {
        //   cell.removeTool("button");
        // } else {

        //   if (
        //     cell.isNode() &&
        //     !cell.attrs.typeName &&
        //     cell.shape !== "custom-circle-start"
        //   ) {
        //     const container = document.getElementById("container");
        //     const ports = container.querySelectorAll(".x6-port-body");
        //     showPorts(ports, false);
        //   }
        // }
      });

      this.graph.on("blank:click", () => {
        // this.currentCell && this.currentCell.removeTools();
        if (this.currentCell) {
          this.currentCell.removeTool("button");
          this.currentCell.removeTool("button-move");
        }
        // this.isClose = true;
        // this.isGloable = true;
      });
    },
    handleDragEnd(e, item) {
      // console.log(e);
      this.addHandleNode(e, item);
    },
    addHandleNode(e, item) {
      const IdValue = new Date().getTime();

      // 创建圆圈节点
      const startNode = this.graph.createNode({
        shape: "custom-circle-start",
        id: IdValue,
        width: 38,
        height: 38,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: "#000000",
            fill: "#ffffff",
            rx: 10,
            ry: 10,
          },
        },
      });

      const polygonNode = this.graph.createNode({
        shape: "custom-polygon",
        width: 80,
        height: 60,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: "#000000",
            fill: "#ffffff",
            rx: 10,
            ry: 10,
          },
          label: {
            fontSize: 13,
            fontWeight: "bold",
          },
        },
      });
      const rectNode = this.graph.createNode({
        shape: "custom-rect",
        width: 80,
        height: 60,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: "#000000",
            fill: "#ffffff",
            rx: 10,
            ry: 10,
          },
          label: {
            fontSize: 13,
            fontWeight: "bold",
          },
        },
      });
      const endNode = this.graph.createNode({
        shape: "custom-circle-start",
        width: 38,
        height: 38,
        key: "end",
        attrs: {
          body: {
            strokeWidth: 4,
            stroke: "#000000",
            fill: "#ffffff",
            rx: 10,
            ry: 10,
          },
          label: {
            fontSize: 13,
            fontWeight: "bold",
          },
        },
      });

      let node_type = null;
      if (item.type === "start") {
        node_type = startNode;
      } else if (item.type === "node1") {
        node_type = rectNode;
      } else if (item.type === "node2") {
        node_type = polygonNode;
      } else if (item.type === "end") {
        node_type = endNode;
      }

      this.dnd.start(node_type, e);
    },
  },
};
</script>

<style lang="scss" scoped>
.conatainer {
  display: flex;
  height: 100vh;
  // padding: 10px;
  .left {
    min-width: 150px;
    border: 1px solid red;
    margin: 5px;
  }
  .right {
    flex: 1;
    margin-left: 10px;
    border: 1px solid blue;
    margin: 5px;
  }
}

.dnd-rect {
  width: 50px;
  height: 30px;
  line-height: 40px;
  text-align: center;
  border: 2px solid #000000;
  border-radius: 6px;
  cursor: move;
  font-size: 12px;
  margin-top: 30px;
}

.dnd-polygon {
  width: 35px;
  height: 35px;
  border: 2px solid #000000;
  transform: rotate(45deg);
  cursor: move;
  font-size: 12px;
  margin-top: 30px;
  margin-bottom: 10px;
}

.dnd-circle {
  width: 35px;
  height: 35px;
  line-height: 45px;
  text-align: center;
  border: 5px solid #000000;
  border-radius: 100%;
  cursor: move;
  font-size: 12px;
  margin-top: 30px;
}

.dnd-start {
  border: 2px solid #000000;
}
.custom-node {
  text-align: center;
}

.node-dom {
  display: inline-block;
}
</style>
View Code

 

posted on 2026-02-25 14:48  Mc525  阅读(11)  评论(0)    收藏  举报

导航