效果展示
具体实现
-
创建指令并绑定到vue实例上,给元素添加右键事件
export const ContextmenuDirective: Directive = { mounted: (el: HTMLElement, binding) => { el[CTX_CONTEXTMENU_HANDLER] = (event: MouseEvent) => contextmenuListener(el, event, binding); el.addEventListener('contextmenu', el[CTX_CONTEXTMENU_HANDLER]); }, unmounted: (el: HTMLElement, binding) => { if (el && el[CTX_CONTEXTMENU_HANDLER]) { el.removeEventListener('contextmenu', el[CTX_CONTEXTMENU_HANDLER]); delete el[CTX_CONTEXTMENU_HANDLER]; } }, };
-
实现右键事件,把右键菜单添加到body上
const contextmenuListener = (el: HTMLElement, event: MouseEvent, binding: DirectiveBinding) => { event.stopPropagation(); event.preventDefault(); const menus = binding.value(el); if (!menus) return; let container: HTMLDivElement | null = null; // 移除右键菜单并取消相关的事件监听 const removeContextmenu = () => { if (container) { document.body.removeChild(container); container = null; } el.classList.remove('contextmenu-active'); document.body.removeEventListener('scroll', removeContextmenu); window.removeEventListener('resize', removeContextmenu); }; // 创建自定义菜单 const options = { axis: { x: event.x, y: event.y }, el, menus, removeContextmenu, }; container = document.createElement('div'); const vm = createVNode(ContextmenuComponent, options, null); render(vm, container); document.body.appendChild(container); // 为目标节点添加菜单激活状态的className el.classList.add('contextmenu-active'); // 页面变化时移除菜单 document.body.addEventListener('scroll', removeContextmenu); window.addEventListener('resize', removeContextmenu); };
-
右键内容组件实现
<template> <div class="mask" @contextmenu.prevent="removeContextmenu()" @mousedown="removeContextmenu()" ></div> <div class="contextmenu" :style="{ left: style.left + 'px', top: style.top + 'px', }" @contextmenu.prevent > <MenuContent :menus="menus" :handleClickMenuItem="handleClickMenuItem" /> </div> </template>
添加mask蒙版的作用是,当鼠标在右键菜单外点击时,当前右键组件删除。
// 计算内容组件出现的位置,并设置相应样式 const style = computed(() => { const MENU_WIDTH = 170; const MENU_HEIGHT = 30; const DIVIDER_HEIGHT = 11; const PADDING = 5; const { x, y } = props.axis; const menuCount = props.menus.filter((menu) => !(menu.divider || menu.hide)).length; const dividerCount = props.menus.filter((menu) => menu.divider).length; const menuWidth = MENU_WIDTH; const menuHeight = menuCount * MENU_HEIGHT + dividerCount * DIVIDER_HEIGHT + PADDING * 2; const screenWidth = document.body.clientWidth; const screenHeight = document.body.clientHeight; return { left: screenWidth <= x + menuWidth ? x - menuWidth : x, top: screenHeight <= y + menuHeight ? y - menuHeight : y, }; }); // 处理点击事件 const handleClickMenuItem = (item: ContextmenuItem) => { if (item.disable) return; if (item.children && !item.handler) return; if (item.handler) item.handler(props.el); props.removeContextmenu(); };
使用
<a-button v-contextmenu="contextmenu">右键菜单</a-button>
const contextmenu = () => {
return [
{
text: '粘贴',
subText: 'Ctrl + V',
handler: () => console.log('粘贴'),
},
{
text: '全选',
subText: 'Ctrl + A',
handler: () => console.log('全选'),
},
];
};
完整代码 如果觉得文章对你有帮助,欢迎一键三连