官方文档是React方式实现的;但我们需要用vue3的方式去实现;所以需要做以下改动:
核心知识点 是vue中的render和h函数
比起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中实现右键菜单的代码,希望能帮到你