用antV X6实现下拉菜单网拓图


概要

在vue2项目中使用antV X6,实现下拉菜单网拓关系图

整体架构流程

1.首先,使用npm install @antv/x6 --save安装最新版本的antV X6插件,下载好后去package.json文件中查看相应版本信息

2.紧接着,引入Graph库并初始化画布,创建画布容器,以及完成你需要的流程图,我是直接根据需求封装了组件

代码部分:(需要可以自取,根据需求进行改写)

<template>

  <div style="height: 100vh; background-color: #f7f8f9">

    <div id="container" style="width: 100%; height: 100%"></div>

  </div>

</template>

<script>

// eslint-disable-next-line no-unused-vars

import { Graph, ObjectExt, Cell } from "@antv/x6";

export default {

  mounted() {

    this.init();

  },

  methods: {

    init() {

      // #region 注册基础图形

      Graph.registerNode(

        "class",

        {

          inherit: "rect",

          markup: [

            {

              tagName: "rect",

              selector: "body",

            },

            // {

            //   tagName: "rect",

            //   selector: "name-rect",

            // },

            {

              tagName: "rect",

              selector: "dropdown-rect",

            },

            {

              tagName: "rect",

              selector: "attrs-rect",

            },

            // {

            //   tagName: "rect",

            //   selector: "methods-rect",

            // },

            {

              tagName: "foreignObject", // 添加foreignObject元素

              selector: "dropdown-container",

            },

            // {

            //   tagName: "text",

            //   selector: "name-text",

            // },

            {

              tagName: "text",

              selector: "attrs-text",

              attrs: {

                dy: "10em",

              },

            },

            {

              tagName: "text",

              selector: "methods-text",

            },

            {

              tagName: "image",

              selector: "search",

            },

          ],

          attrs: {

            rect: {

              width: 260,

            },

            body: {

              stroke: "#eff4ff",

              //  refWidth: '100%',

              //  refHeight: '100%',

              rx: 5,

              ry: 5,

            },

            // "name-rect": {

            //   fill: "#5f95ff",

            //   stroke: "#fff",

            //   strokeWidth: 0.5,

            // },

            "dropdown-rect": {

              fill: "#eff4ff",

              stroke: "#fff",

              strokeWidth: 0.5,

            },

            "dropdown-container": {

              // size:{height:30},

              // 设置foreignObject的位置,使其位于节点上方

              // y: 0, // 根据需要调整这个值以确保下拉框位于节点上方

              // refY: 0, // 相对于节点底部定位

              // refX: 0,

              // width: 250,

              // height:36,

              //         html: `

              //   <div style="width:100%;background: #5f95ff; padding:0 10px;">

              //     <select  style="width:100%;background: #5f95ff;border:0;color: #fff;" disabled>

              //       <option value="option1"></option>

              //     </select>

              //   </div>

              // `,

            },

            "attrs-rect": {

              fill: "#FFF",

              stroke: "#fff",

              strokeWidth: 0.5,

              rx: 4, // 设置圆角的半径

              ry: 4, // 设置圆角的半径

            },

            // "methods-rect": {

            //   fill: "#eff4ff",

            //   stroke: "#fff",

            //   strokeWidth: 0.5,

            // },

            // "name-text": {

            //   ref: "name-rect",

            //   refY: 0.5,

            //   refX: 0.5,

            //   textAnchor: "middle",

            //   fontWeight: "bold",

            //   fill: "#fff",

            //   fontSize: 14,

            // },

            "attrs-text": {

              ref: "attrs-rect",

              refY: 70,

              refX: 20,

              textAnchor: "left",

              fill: "black",

              fontSize: 14,

              fontWeight: "bold",

            },

            // "content1-text": {

            //   ref: "attrs-rect",

            //   refY: 70,

            //   refX: 20,

            //   textAnchor: "left",

            //   // fill: "black",

            //   fontSize: 14,

            //   fontWeight: "bold",

            // },

            // "content2-text": {

            //   ref: "attrs-rect",

            //   refY: 70,

            //   refX: 20,

            //   textAnchor: "left",

            //   // fill: "black",

            //   fontSize: 14,

            //   fontWeight: "bold",

            // },

            // "content3-text": {

            //   ref: "attrs-rect",

            //   refY: 70,

            //   refX: 20,

            //   textAnchor: "left",

            //   // fill: "black",

            //   fontSize: 14,

            //   fontWeight: "bold",

            // },

            // "methods-text": {

            //   ref: "methods-rect",

            //   refY: 0.5,

            //   refX: 5,

            //   textAnchor: "left",

            //   fill: "black",

            //   fontSize: 10,

            // },

            search: {

              width: 30,

              height: 30,

              refX: "86%",

              refY: "66%",

              "xlink:href": "../../../../static/search.png",

            },

          },

          ports: {

            groups: {

              left: {

                position: "left",

                attrs: {

                  circle: {

                    magnet: true,

                    stroke: "#8f8f8f",

                    r: 5,

                  },

                },

              },

              bottom: {

                position: "bottom",

                attrs: {

                  circle: {

                    magnet: true,

                    stroke: "#8f8f8f",

                    r: 5,

                  },

                },

              },

              right: {

                position: "right",

                attrs: {

                  circle: {

                    magnet: true,

                    stroke: "#8f8f8f",

                    r: 5,

                  },

                },

              },

              top: {

                position: "top",

                attrs: {

                  circle: {

                    magnet: true,

                    stroke: "#8f8f8f",

                    r: 5,

                  },

                },

              },

            },

          },

          propHooks(meta) {

            const {

              name,

              attributes,

              content1,

              content2,

              content3,

              ...others

            } = meta;

            if (!(name && attributes)) {

              return meta;

            }

            const rects = [

              // { type: "name", text: name },

              { type: "attrs", text: attributes },

              // { type: "content1", text: content1 },

              // { type: "content2", text: content2 },

              // { type: "content3", text: content3 },

            ];

            let offsetY = 0;

            rects.forEach((rect) => {

              const height = rect.text.length * 40 + 16;

              // ObjectExt.setByPath(

              //   others,

              //   `attrs/${rect.type}-text/text`,

              //   rect.text

              //     .map((t) => {

              //       return t;

              //     })

              //     .join("\n")

              // );

              ObjectExt.setByPath(

                others,

                `attrs/${rect.type}-text/text`,

                rect.text

                  .map((t) => {

                    return t;

                  })

                  .join("\n")

              );

              ObjectExt.setByPath(

                others,

                `attrs/${rect.type}-rect/height`,

                height

              );

              ObjectExt.setByPath(

                others,

                `attrs/${rect.type}-rect/transform`,

                "translate(0," + offsetY + ")"

              );

              offsetY += height;

            });

            others.size = { width: 260, height: offsetY };

            return others;

          },

        },

        true

      );

      // 聚合

      Graph.registerEdge(

        "aggregation",

        {

          inherit: "edge",

          attrs: {

            line: {

              stroke: "#000", // 设置线条颜色为黑色

              strokeWidth: 1,

              // sourceMarker: {

              //   name: 'path',

              //   d: 'M 30 10 L 20 16 L 10 10 L 20 4 z',

              //   fill: 'white',

              //   offsetX: -10,

              // },

              targetMarker: {},

              sourceMarker: {},

              //  targetMarker: {

              //   name: 'circle',

              //   r: 4, // 圆的半径

              //   fill: 'none', // 设置为none以创建空心效果

              //   stroke: '#000', // 圆的边框颜色

              //   strokeWidth: 1, // 圆的边框宽度

              // },

              // targetMarker: {

              //   name: 'circle',

              //   r: 4, // 圆的半径

              //   fill: 'none', // 设置为none以创建空心效果

              //   stroke: '#000', // 圆的边框颜色

              //   strokeWidth: 1, // 圆的边框宽度

              // },

            },

          },

        },

        true

      );

      const graph = new Graph({

        container: document.getElementById("container"),

      });

      fetch("../../../static/data.json")

        .then((response) => response.json())

        .then((data) => {

          const cells = [];

          const edgeShapes = [

            "extends",

            "composition",

            "implement",

            "aggregation",

            "association",

          ];

          const nodes = [];

          data.forEach((item) => {

            if (edgeShapes.includes(item.shape)) {

              item.source.cell = nodes.find(

                (node) => node.cell === item.source.cell

              ).node;

              item.target.cell = nodes.find(

                (node) => node.cell === item.target.cell

              ).node;

              // 添加链接线

              item = {

                ...item,

                router: {

                  // 设置连接线为直角

                  name: "manhattan",

                  args: {},

                },

              };

              cells.push(graph.createEdge(item));

            } else {

              // 添加图形

              item = {

                ...item,

              };

              const node = graph.createNode(item);

              //        node.setAttrs({

              //           'dropdown-container' :{

              //         // 设置foreignObject的位置,使其位于节点上方

              //         y: 20, // 根据需要调整这个值以确保下拉框位于节点上方

              //         refX: 0, // 水平居中

              //         refY: 2, // 相对于节点底部定位

              //         width: 160,

              //         html: `

              //   <div style="width:100%;background: #5f95ff; padding:10px">

              //     <select  style="width:100%;background: #5f95ff;" disabled>

              //       <option value="option1"></option>

              //     </select>

              //   </div>

              // `,

              //         }

              //        })

              //         node.setAttrs(

              //           {

              //             "dropdown-container": {

              //               y: 0,

              //         // refX: 0.5,

              //               html: `

              //   <div class='title'>

              //       <div>${item.name[0]}</div><div class="arrow-down"></div>

              //   </div>

              // `,

              //             },

              //             //   <select   style="width:100%;" >

              //             //   <option value="${item.name[0]}" selected></option>

              //             // </select>

              //           },

              //           { deep: false }

              //         );

              node.attr(

                "dropdown-container/html",

                `

     <div class='title'>

         <div>${item.name[0]}</div><div class="arrow-down"></div>

          </div>

       `

              );

              nodes.push({ cell: "node" + item.id, node });

              console.log("item", item);

              cells.push(node);

            }

          });

          graph.resetCells(cells);

          graph.zoomToFit({ padding: 10, maxScale: 1 });

          this.$nextTick(() => {});

        });

    },

  },

};

</script>

<style lang="">

.arrow-down {

  width: 0;

  height: 0;

  border-left: 5px solid transparent;

  border-right: 5px solid transparent;

  border-top: 5px solid white;

  margin-left: 10px;

}

.title {

  width: 260px;

  height: 36px;

  background: #165dff;

  padding: 0 20px;

  box-sizing: border-box;

  display: flex;

  justify-content: space-between;

  align-items: center;

  color: #fff;

  font-weight: 800;

  border-top-left-radius: 4px;

  border-top-right-radius: 4px;

  /* margin-top: -36px; */

  /* position:relative;

    top:-36px */

}

.x6-graph-svg {

  position: absolute;

  top: -204px;

  right: 0;

  bottom: 0;

  left: 0;

}

</style>

3.其中用到了放大镜图片,我将其放进了 静态资源static文件夹内

4.结果展示

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Vue3中使用AntV X6实现右键菜单功能,你可以按照以下步骤操作: 1. 安装依赖:在Vue项目中安装AntV X6ant-design-vue库。 ``` npm install @antv/x6 ant-design-vue --save ``` 2. 创建右键菜单组件:创建一个Vue组件,作为X6图形编辑器右键菜单的容器。 ```vue <template> <a-dropdown :trigger="['contextmenu']" @visible-change="onVisibleChange"> <a-menu slot="overlay" :style="{ width: '120px' }"> <a-menu-item @click="deleteNode">删除节点</a-menu-item> </a-menu> <div class="x6-contextmenu" ref="container"></div> </a-dropdown> </template> <script> import { defineComponent } from 'vue'; import { Dropdown, Menu, message } from 'ant-design-vue'; export default defineComponent({ name: 'X6ContextMenu', components: { Dropdown, Menu, MenuItem: Menu.Item, }, emits: ['deleteNode'], mounted() { this.menu = this.$refs.container; this.menu.addEventListener('contextmenu', (e) => { e.preventDefault(); e.stopPropagation(); }); }, methods: { onVisibleChange(visible) { if (visible) { this.$emit('contextmenu', this.menu); } }, deleteNode() { this.$emit('deleteNode'); }, }, }); </script> ``` 3. 在X6图形编辑器中添加右键菜单:在X6图形编辑器中添加右键菜单功能。 ```vue <template> <div class="x6-editor"> <x6-contextmenu @contextmenu="onContextMenu" @deleteNode="deleteNode"></x6-contextmenu> <div class="x6-graph" ref="container"></div> </div> </template> <script> import { defineComponent } from 'vue'; import { Graph, Node } from '@antv/x6'; import X6ContextMenu from './X6ContextMenu.vue'; export default defineComponent({ name: 'X6Editor', components: { X6ContextMenu, }, data() { return { graph: null, }; }, mounted() { this.graph = new Graph({ container: this.$refs.container, grid: true, // 其他配置 }); // 添加节点 const node = this.graph.addNode({ // 节点配置 }); // 右键菜单事件 this.graph.on('contextmenu', ({ x, y }) => { this.$refs.contextmenu.show(x, y); }); }, methods: { onContextMenu(menu) { // 清空菜单 menu.innerHTML = ''; // 添加菜单项 const deleteMenuItem = document.createElement('a-menu-item'); deleteMenuItem.innerHTML = '删除节点'; deleteMenuItem.addEventListener('click', () => { this.deleteNode(); }); menu.appendChild(deleteMenuItem); }, deleteNode() { // 删除节点 this.graph.removeNode(node); }, }, }); </script> ``` 这样就可以在Vue3中使用AntV X6实现右键菜单功能了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值