vue3使用@antv/x6-节点工具-右键菜单

3,632 阅读1分钟

官方文档是React方式实现的;但我们需要用vue3的方式去实现;所以需要做以下改动:

核心知识点 是vue中的renderh函数

# Vue3 app从创建到挂载

比起createApp更好用

核心代码

  • ContextMenuTool.tsx
import type { EdgeView } from '@antv/x6'
import { ToolsView } from '@antv/x6'
import { Dropdown } from 'ant-design-vue'
import { h, render } from 'vue'
import type { JSX } from 'vue/jsx-runtime'

class ContextMenuTool extends ToolsView.ToolItem<
  EdgeView,
  ContextMenuToolOptions
> {
  private knob!: HTMLDivElement
  private timer!: number

  render() {
    if (!this.knob) {
      this.knob = ToolsView.createElement('div', false) as HTMLDivElement
      this.knob.style.position = 'absolute'
      this.container.appendChild(this.knob)
    }
    return this
  }

  private toggleContextMenu(visible: boolean) {
    render(h('span'), this.knob)

    document.removeEventListener('mousedown', this.onMouseDown)

    if (visible) {
      render(h(
        <Dropdown
          open={true}
          trigger={['contextmenu']}
          overlay={this.options.menu}
        >
          <span></span>
        </Dropdown>,
      ), this.knob)
      document.addEventListener('mousedown', this.onMouseDown)
    }
  }

  private updatePosition(e?: MouseEvent) {
    const style = this.knob.style
    if (e) {
      const pos = this.graph.clientToGraph(e.clientX, e.clientY)
      style.left = `${pos.x}px`
      style.top = `${pos.y}px`
    }
    else {
      style.left = '-1000px'
      style.top = '-1000px'
    }
  }

  private onMouseDown = () => {
    this.timer = window.setTimeout(() => {
      this.updatePosition()
      this.toggleContextMenu(false)
    }, 200)
  }

  private onContextMenu({ e }: { e: MouseEvent }) {
    if (this.timer) {
      clearTimeout(this.timer)
      this.timer = 0
    }
    this.updatePosition(e)
    this.toggleContextMenu(true)
  }

  delegateEvents() {
    this.cellView.on('cell:contextmenu', this.onContextMenu, this)
    return super.delegateEvents()
  }

  protected onRemove() {
    this.cellView.off('cell:contextmenu', this.onContextMenu, this)
  }
}

ContextMenuTool.config({
  tagName: 'div',
  isSVGElement: false,
})

export interface ContextMenuToolOptions extends ToolsView.ToolItem.Options {
  menu: JSX.Element
}

export {
  ContextMenuTool,
}


vue使用:

<template>
  <div id="container" style="min-width: 400px; min-height: 600px;" />
</template>

<script lang='tsx' setup>
import { ContextMenuTool } from '@/components/antv-x6/tools/ContextMenuTool'
import { Color, Graph } from '@antv/x6';
import { register } from '@antv/x6-vue-shape';

// 注册节点工具
Graph.registerEdgeTool('contextmenu', ContextMenuTool, true)
// 注册边工具
Graph.registerNodeTool('contextmenu', ContextMenuTool, true)

const menu = (
        <Menu>
          <Menu.Item>1st menu item</Menu.Item>
          <Menu.Item>2nd menu item</Menu.Item>
          <Menu.Item>
            <a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
              3rd menu item
            </a>
          </Menu.Item>
          <Menu.Item danger>a danger item</Menu.Item>
        </Menu>
      )

const MyComponent = ({ node }) => {
    const color = node.prop("color");
    return (
        <div
            style={{
                color: "#fff",
                width: "100%",
                height: "100%",
                textAlign: "center",
                lineHeight: "50px",
                borderRadius: 4,
                background: color
            }}
        >
            {color}
        </div>
    );
};

// 注册vue节点
register({
    shape: "custom-node",
    width: 120,
    height: 50,
    effect: ["color"],
    component: MyComponent
});

// 因为使用了 `unplugin-auto-import/vite` 插件,没显示导入
onMounted(() => {
// 同上
  nextTick(() => {
    const graph = new Graph({
      container: document.getElementById('container') as HTMLElement,
      grid: true,
    })

    const source = graph.addNode({
      shape: 'custom-node',
      x: 180,
      y: 60,
      tools: [
        {
          name: 'contextmenu',
          args: {
            menu,
          },
        },
      ],
    })

    const target = graph.addNode({
      x: 320,
      y: 250,
      width: 100,
      height: 40,
      attrs: {
        body: {
          stroke: '#5F95FF',
          fill: '#EFF4FF',
          strokeWidth: 1,
        },
      },
      tools: [
        {
          name: 'contextmenu',
          args: {
            menu,
          },
        },
      ],
    })

    graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#A2B1C3',
          strokeWidth: 2,
        },
      },
      tools: [
        {
          name: 'contextmenu',
          args: {
            menu,
          },
        },
      ],
    })

    const update = () => {
      source.prop("color", Color.randomHex());
      target.prop("color", Color.randomHex());
      setTimeout(update, 1000);
    };

    update();

  })
})
</script>

以上就是用vue3在@antv/x6中实现右键菜单的代码,希望能帮到你