Appearance
BizContextMenu 右键菜单
右键菜单标准件:v-model 显隐 + x/y 定位(取 contextmenu 事件坐标),菜单项支持图标 / 禁用 / 危险态 / 分隔线 / 快捷键提示 / 一级子菜单(子菜单与主菜单均边缘自动翻转),↑↓ 方向键导航、Enter 执行、Esc 关闭,点击外部或页面滚动自动关闭。也提供 v-context-menu 指令一行绑定。看板卡片、文件树、表格行、甘特任务等右键操作位通用。
基础用法
在此区域内点击鼠标右键
目标区域监听
contextmenu,坐标透传组件;点击菜单项后 select 事件回传并自动关闭。<script setup lang="ts">
/** 基础用法:目标区域监听 contextmenu,坐标透传组件;点击项 / Esc / 点击外部自动关闭 */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { BizContextMenu } from 'vue3-ep-biz-lib'
import type { BizContextMenuItem } from 'vue3-ep-biz-lib'
const visible = ref(false)
const x = ref(0)
const y = ref(0)
const items: BizContextMenuItem[] = [
{ key: 'refresh', label: '刷新', onClick: () => ElMessage.info('刷新') },
{ key: 'open', label: '新窗口打开', onClick: () => ElMessage.info('新窗口打开') },
{ key: 'copy', label: '复制链接', divided: true, onClick: () => ElMessage.success('已复制') },
{ key: 'report', label: '举报', danger: true, onClick: () => ElMessage.warning('举报') },
]
const onContextMenu = (event: MouseEvent): void => {
event.preventDefault()
x.value = event.clientX
y.value = event.clientY
visible.value = true
}
const onSelect = (item: BizContextMenuItem): void => {
ElMessage.success(`选中:${item.label}`)
}
</script>
<template>
<div
style="height: 160px; display: flex; align-items: center; justify-content: center; border: 1px dashed var(--el-border-color); border-radius: 6px; color: var(--el-text-color-secondary); user-select: none"
@contextmenu="onContextMenu"
>
在此区域内点击鼠标右键
</div>
<BizContextMenu v-model="visible" :items="items" :x="x" :y="y" @select="onSelect" />
</template>
显示代码
项能力与键盘操作
右键打开菜单后试试键盘:↑↓ 选择、Enter 执行、Esc 关闭
icon 图标、shortcut 快捷键提示(纯展示)、disabled 禁用、danger + divided 危险项分隔。菜单打开后 ↑↓ 选择、Enter 执行、Esc 关闭。<script setup lang="ts">
/** 项能力:图标 / 禁用 / 危险态 / 分隔线 / 快捷键提示;方向键 + Enter 键盘可操作 */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { DocumentCopy, Fold, RefreshRight } from '@element-plus/icons-vue'
import { BizContextMenu } from 'vue3-ep-biz-lib'
import type { BizContextMenuItem } from 'vue3-ep-biz-lib'
const visible = ref(false)
const x = ref(0)
const y = ref(0)
const items: BizContextMenuItem[] = [
{ key: 'refresh', label: '刷新', icon: RefreshRight, shortcut: 'F5', onClick: () => ElMessage.info('刷新') },
{ key: 'copy', label: '复制', icon: DocumentCopy, shortcut: 'Ctrl+C', onClick: () => ElMessage.success('已复制') },
{ key: 'fold', label: '折叠全部', icon: Fold, disabled: true },
{ key: 'delete', label: '删除', danger: true, divided: true, onClick: () => ElMessage.warning('删除') },
]
const onContextMenu = (event: MouseEvent): void => {
event.preventDefault()
x.value = event.clientX
y.value = event.clientY
visible.value = true
}
</script>
<template>
<div
style="height: 160px; display: flex; flex-direction: column; gap: 4px; align-items: center; justify-content: center; border: 1px dashed var(--el-border-color); border-radius: 6px; color: var(--el-text-color-secondary); user-select: none"
@contextmenu="onContextMenu"
>
<span>右键打开菜单后试试键盘:↑↓ 选择、Enter 执行、Esc 关闭</span>
</div>
<BizContextMenu v-model="visible" :items="items" :x="x" :y="y" />
</template>
显示代码
一级子菜单
右键打开(子菜单靠近右缘时自动翻转)
children 挂二级项;hover 或 → 键展开,← 收起;靠近视口右缘时子菜单自动翻转到左侧。<script setup lang="ts">
/** 一级子菜单:children 挂二级项,hover / ArrowRight 展开,边缘自动翻转到左侧 */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { BizContextMenu } from 'vue3-ep-biz-lib'
import type { BizContextMenuItem } from 'vue3-ep-biz-lib'
const visible = ref(false)
const x = ref(0)
const y = ref(0)
const items: BizContextMenuItem[] = [
{
key: 'new',
label: '新建',
children: [
{ key: 'new-file', label: '文件', onClick: () => ElMessage.info('新建文件') },
{ key: 'new-folder', label: '文件夹', onClick: () => ElMessage.info('新建文件夹') },
],
},
{
key: 'sort',
label: '排序',
children: [
{ key: 'sort-name', label: '按名称', onClick: () => ElMessage.info('按名称') },
{ key: 'sort-time', label: '按修改时间', onClick: () => ElMessage.info('按修改时间') },
{ key: 'sort-size', label: '按大小', onClick: () => ElMessage.info('按大小') },
],
},
{ key: 'remove', label: '删除', danger: true, divided: true, onClick: () => ElMessage.warning('删除') },
]
const onContextMenu = (event: MouseEvent): void => {
event.preventDefault()
x.value = event.clientX
y.value = event.clientY
visible.value = true
}
</script>
<template>
<div
style="height: 160px; display: flex; align-items: center; justify-content: center; border: 1px dashed var(--el-border-color); border-radius: 6px; color: var(--el-text-color-secondary); user-select: none"
@contextmenu="onContextMenu"
>
右键打开(子菜单靠近右缘时自动翻转)
</div>
<BizContextMenu v-model="visible" :items="items" :x="x" :y="y" />
</template>
显示代码
指令用法
静态项:收藏夹文件夹(右键)
函数项:季度报表.xlsx(右键)
v-context-menu="items" 一行绑定;传函数时弹出瞬间求值,菜单项可随宿主数据动态生成。<script setup lang="ts">
/** 指令用法:v-context-menu 一行绑定;函数形式弹出时求值(项可动态) */
import { ElMessage } from 'element-plus'
import { vContextMenu } from 'vue3-ep-biz-lib'
import type { BizContextMenuItem } from 'vue3-ep-biz-lib'
const items: BizContextMenuItem[] = [
{ key: 'top', label: '置顶', onClick: () => ElMessage.success('已置顶') },
{ key: 'rename', label: '重命名', onClick: () => ElMessage.info('重命名') },
{ key: 'remove', label: '删除', danger: true, divided: true, onClick: () => ElMessage.warning('删除') },
]
/** 函数形式:右键谁就给谁对应的菜单 */
const menuOf = (name: string): BizContextMenuItem[] => [
{ key: 'open', label: `打开 ${name}`, onClick: () => ElMessage.info(`打开 ${name}`) },
{ key: 'copy', label: '复制', onClick: () => ElMessage.success('已复制') },
]
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 8px">
<div
v-context-menu="items"
style="padding: 12px 16px; border: 1px solid var(--el-border-color-lighter); border-radius: 6px; cursor: context-menu"
>
静态项:收藏夹文件夹(右键)
</div>
<div
v-context-menu="() => menuOf('季度报表.xlsx')"
style="padding: 12px 16px; border: 1px solid var(--el-border-color-lighter); border-radius: 6px; cursor: context-menu"
>
函数项:季度报表.xlsx(右键)
</div>
</div>
</template>
显示代码
编程式开关
open(x, y) 与右键事件共用一套菜单,下拉按钮 / 更多操作位可直接复用
open(x, y) / close() 编程式控制——下拉按钮等非右键入口复用同一菜单实例。<script setup lang="ts">
/** 编程式开关:open(x, y) / close()——按钮等非右键入口复用同一菜单实例 */
import { ref } from 'vue'
import { ElButton, ElMessage } from 'element-plus'
import { BizContextMenu } from 'vue3-ep-biz-lib'
import type { BizContextMenuItem } from 'vue3-ep-biz-lib'
const menuRef = ref<{ open: (x: number, y: number) => void; close: () => void }>()
const items: BizContextMenuItem[] = [
{ key: 'today', label: '今日订单', onClick: () => ElMessage.info('今日订单') },
{ key: 'week', label: '近 7 天', onClick: () => ElMessage.info('近 7 天') },
{ key: 'export', label: '导出当前视图', divided: true, onClick: () => ElMessage.success('导出') },
]
/** 在触发按钮下方弹出 */
const openAtButton = (event: MouseEvent): void => {
menuRef.value?.open(event.clientX, event.clientY + 8)
}
</script>
<template>
<div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap">
<ElButton type="primary" @click="openAtButton">按钮弹出菜单</ElButton>
<ElButton @click="menuRef?.close()">关闭菜单</ElButton>
<span style="font-size: 12px; color: var(--el-text-color-secondary)">
open(x, y) 与右键事件共用一套菜单,下拉按钮 / 更多操作位可直接复用
</span>
</div>
<BizContextMenu ref="menuRef" :items="items" />
</template>
显示代码
表格行右键
共 3 条
- 1
页
BizTable 的 row-contextmenu 事件坐标透传,文件夹 / 文件两类行给不同菜单。<script setup lang="ts">
/** 表格行右键:row-contextmenu 坐标透传,菜单项随行数据动态生成 */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { BizContextMenu, BizTable } from 'vue3-ep-biz-lib'
import type { BizContextMenuItem, BizTableColumn } from 'vue3-ep-biz-lib'
type FileRow = { id: number; name: string; type: 'folder' | 'file'; size: string }
const data = ref<FileRow[]>([
{ id: 1, name: '设计稿', type: 'folder', size: '—' },
{ id: 2, name: '合同扫描件.pdf', type: 'file', size: '2.4 MB' },
{ id: 3, name: '季度报表.xlsx', type: 'file', size: '86 KB' },
])
const columns: BizTableColumn<FileRow>[] = [
{ prop: 'name', label: '名称', minWidth: 140 },
{ prop: 'type', label: '类型', width: 90, enums: [{ value: 'folder', label: '文件夹', tag: 'info' }, { value: 'file', label: '文件', tag: 'success' }] },
{ prop: 'size', label: '大小', width: 100 },
]
const visible = ref(false)
const x = ref(0)
const y = ref(0)
const items = ref<BizContextMenuItem[]>([])
const onRowContextMenu = (row: FileRow, _column: unknown, event: Event): void => {
const mouse = event as MouseEvent
mouse.preventDefault()
items.value =
row.type === 'folder'
? [
{ key: 'open', label: '打开', onClick: () => ElMessage.info(`打开 ${row.name}`) },
{ key: 'empty', label: '清空文件夹', danger: true, divided: true, onClick: () => ElMessage.warning('清空') },
]
: [
{ key: 'preview', label: '预览', onClick: () => ElMessage.info(`预览 ${row.name}`) },
{ key: 'download', label: '下载', onClick: () => ElMessage.success('开始下载') },
{ key: 'delete', label: '删除', danger: true, divided: true, onClick: () => ElMessage.warning('删除') },
]
x.value = mouse.clientX
y.value = mouse.clientY
visible.value = true
}
</script>
<template>
<div style="height: 240px">
<BizTable :columns="columns" :data="data" row-key="id" border @row-contextmenu="onRowContextMenu" />
</div>
<BizContextMenu v-model="visible" :items="items" :x="x" :y="y" />
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| items | BizContextMenuItem[] | —(必填) | 菜单项配置 |
| v-model | boolean | false | 显隐 |
| x | number | 0 | 定位横坐标(clientX) |
| y | number | 0 | 定位纵坐标(clientY) |
| close-on-scroll | boolean | true | 页面滚动时自动关闭 |
BizContextMenuItem
| 属性 | 类型 | 说明 |
|---|---|---|
| key | string | 项标识 |
| label | string | 文案 |
| icon | Component | 图标 |
| disabled | boolean | 禁用 |
| danger | boolean | 危险操作(红色) |
| divided | boolean | 顶部加分隔线 |
| shortcut | string | 快捷键提示(纯展示) |
| children | BizContextMenuItem[] | 一级子菜单 |
| onClick | () => void | 点击回调(与 select 事件同时派发) |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| select | (item, parent?) | 点击菜单项(parent 为子菜单项的父项),点击后自动关闭 |
| open | — | 菜单打开 |
| close | — | 菜单关闭 |
Expose
| 属性/方法 | 说明 |
|---|---|
| open(x, y) | 编程式打开(坐标优先于 props x/y) |
| close() | 编程式关闭 |
指令
| 指令 | 值 | 说明 |
|---|---|---|
| v-context-menu | BizContextMenuItem[] | (() => BizContextMenuItem[]) | 宿主元素右键弹出菜单;函数形式弹出时求值 |