386 lines
14 KiB
Markdown
386 lines
14 KiB
Markdown
|
|
# vue3-menus
|
|||
|
|
|
|||
|
|
Vue3.0 自定义右键菜单,支持 Vite2.0,[官网](https://doc.wssio.com/opensource/vue3-menus/)
|
|||
|
|
|
|||
|
|
Vue3.0 原生实现完全自定义右键菜单组件, 零依赖,可根据可视区域自动调节显示位置,可支持插槽完全重写每一项菜单
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
## 在线演示
|
|||
|
|
|
|||
|
|
- [完整菜单功能演示](https://codepen.io/xfy520/pen/yLXNqzy)
|
|||
|
|
- [复制粘贴演示](https://codepen.io/xfy520/pen/xxrGJdg)
|
|||
|
|
|
|||
|
|
## 快速安装
|
|||
|
|
|
|||
|
|
### npm 安装
|
|||
|
|
|
|||
|
|
```shell
|
|||
|
|
npm install vue3-menus
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
或
|
|||
|
|
|
|||
|
|
```shell
|
|||
|
|
yarn add vue3-menus
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### CDN
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<script src="https://unpkg.com/vue3-menus/dist/vue3-menus.umd.min.js">
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 使用(Vite 情况下同样使用)
|
|||
|
|
|
|||
|
|
CDN引入则不需要 `app.use(Vue3Menus)`
|
|||
|
|
> 样例中使用的是`@ant-design/icons-vue`图标与`@element-plus/icons`图标、图标可以使用`html`代码传入、也可以通过插槽`自定义图标`、也可以`完全重写每一项菜单`
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// 全局注册组件、指令、方法
|
|||
|
|
import { createApp } from 'vue';
|
|||
|
|
import Menus from 'vue3-menus';
|
|||
|
|
import App from './App.vue';
|
|||
|
|
const app = createApp(App);
|
|||
|
|
app.use(Menus);
|
|||
|
|
app.mount('#app');
|
|||
|
|
// 单个注册某个,以下三种方式均可在单个文件内使用
|
|||
|
|
import { createApp } from 'vue';
|
|||
|
|
import { directive, menusEvent, Vue3Menus } from 'vue3-menus';
|
|||
|
|
import App from './App.vue';
|
|||
|
|
const app = createApp(App);
|
|||
|
|
app.component('vue3-menus', Vue3Menus); // 只注册组件
|
|||
|
|
app.directive('menus', directive); // 只注册指令
|
|||
|
|
app.config.globalProperties.$menusEvent = menusEvent; // 只绑定方法
|
|||
|
|
app.mount('#app');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<template>
|
|||
|
|
<div style="height: 98vh; width: 100%;" v-menus:left="menus">
|
|||
|
|
<div class="div" v-menus:left="menus">指令方式打开菜单</div>
|
|||
|
|
<div class="div" @click.stop @contextmenu="($event) => $menusEvent($event, menus)">事件方式打开菜单</div>
|
|||
|
|
<div class="div" @click.stop @contextmenu="rightClick">组件方式打开菜单</div>
|
|||
|
|
<vue3-menus :open="isOpen" :event="eventVal" :menus="menus.menus">
|
|||
|
|
<template #icon="{menu, activeIndex, index}">{{activeIndex}}</template>
|
|||
|
|
<template #label="{ menu, activeIndex, index }">插槽:{{ menu.label }}</template>
|
|||
|
|
</vue3-menus>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
<script>
|
|||
|
|
import { defineComponent, nextTick, ref, shallowRef } from "vue";
|
|||
|
|
import { SyncOutlined, WindowsOutlined, QrcodeOutlined } from '@ant-design/icons-vue';
|
|||
|
|
import { Printer } from '@element-plus/icons'
|
|||
|
|
|
|||
|
|
export default defineComponent({
|
|||
|
|
name: "App",
|
|||
|
|
setup() {
|
|||
|
|
const isOpen = ref(false);
|
|||
|
|
const eventVal = ref({});
|
|||
|
|
function rightClick(event) {
|
|||
|
|
isOpen.value = false;
|
|||
|
|
nextTick(() => {
|
|||
|
|
eventVal.value = event;
|
|||
|
|
isOpen.value = true;
|
|||
|
|
})
|
|||
|
|
event.preventDefault();
|
|||
|
|
}
|
|||
|
|
const menus = shallowRef({
|
|||
|
|
menus: [
|
|||
|
|
{
|
|||
|
|
label: "返回(B)",
|
|||
|
|
tip: 'Alt+向左箭头',
|
|||
|
|
click: () => {
|
|||
|
|
window.history.back(-1);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "点击不关闭菜单",
|
|||
|
|
tip: '不关闭菜单',
|
|||
|
|
click: () => {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "前进(F)",
|
|||
|
|
tip: 'Alt+向右箭头',
|
|||
|
|
disabled: true
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "重新加载(R)",
|
|||
|
|
tip: 'Ctrl+R',
|
|||
|
|
click: () => location.reload(),
|
|||
|
|
divided: true
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "另存为(A)...",
|
|||
|
|
tip: 'Ctrl+S'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "打印(P)...",
|
|||
|
|
tip: 'Ctrl+P',
|
|||
|
|
click: () => window.print(),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "投射(C)...",
|
|||
|
|
divided: true
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: '发送到你的设备',
|
|||
|
|
children: [
|
|||
|
|
{
|
|||
|
|
label: 'iPhone',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: 'iPad'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: 'Windows 11'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "为此页面创建二维码",
|
|||
|
|
divided: true,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "使用网页翻译(F)",
|
|||
|
|
divided: true,
|
|||
|
|
children: [
|
|||
|
|
{ label: "翻译成繁体中文" },
|
|||
|
|
{ label: "翻译成繁体中文" },
|
|||
|
|
{
|
|||
|
|
label: "百度翻译", children: [
|
|||
|
|
{ label: "翻译成繁体中文" },
|
|||
|
|
{ label: "翻译成繁体中文" },]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "搜狗翻译", children: [
|
|||
|
|
{ label: "翻译成繁体中文" },
|
|||
|
|
{ label: "翻译成繁体中文" },
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "有道翻译", children: [
|
|||
|
|
{ label: "翻译成繁体中文" },
|
|||
|
|
{ label: "翻译成繁体中文" },
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "截取网页(R)"
|
|||
|
|
},
|
|||
|
|
{ label: "查看网页源代码(U)", tip: 'Ctrl+U' },
|
|||
|
|
{ label: "检查(N)", tip: 'Ctrl+Shift+I' }
|
|||
|
|
]
|
|||
|
|
})
|
|||
|
|
return { menus, isOpen, rightClick, eventVal }
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```css
|
|||
|
|
.div {
|
|||
|
|
display: inline-block;
|
|||
|
|
background-color: aqua;
|
|||
|
|
margin: 0 20px;
|
|||
|
|
line-height: 200px;
|
|||
|
|
padding: 0 20px;
|
|||
|
|
height: 200px;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 指令方式使用
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<template>
|
|||
|
|
<div v-menus:left="menus">指令方式打开菜单</div>
|
|||
|
|
</template>
|
|||
|
|
<script>
|
|||
|
|
import { defineComponent, shallowRef } from "vue";
|
|||
|
|
import { directive } from 'vue3-menus';
|
|||
|
|
|
|||
|
|
export default defineComponent({
|
|||
|
|
name: "App",
|
|||
|
|
directives: {
|
|||
|
|
menus: directive
|
|||
|
|
},
|
|||
|
|
setup() {
|
|||
|
|
const menus = shallowRef({
|
|||
|
|
menus: [
|
|||
|
|
{
|
|||
|
|
label: "返回(B)",
|
|||
|
|
tip: 'Alt+向左箭头',
|
|||
|
|
click: () => {
|
|||
|
|
window.history.back(-1);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "点击不关闭菜单",
|
|||
|
|
tip: '不关闭菜单',
|
|||
|
|
click: () => {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
})
|
|||
|
|
return { menus }
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方法方式使用
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<template>
|
|||
|
|
<div class="div" @click.stop @contextmenu="rightClick">事件方式打开菜单</div>
|
|||
|
|
</template>
|
|||
|
|
<script>
|
|||
|
|
import { defineComponent, shallowRef } from "vue";
|
|||
|
|
import { menusEvent } from 'vue3-menus';
|
|||
|
|
|
|||
|
|
export default defineComponent({
|
|||
|
|
name: "App",
|
|||
|
|
setup() {
|
|||
|
|
const menus = shallowRef({
|
|||
|
|
menus: [
|
|||
|
|
{
|
|||
|
|
label: "返回(B)",
|
|||
|
|
tip: 'Alt+向左箭头',
|
|||
|
|
click: () => {
|
|||
|
|
window.history.back(-1);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "点击不关闭菜单",
|
|||
|
|
tip: '不关闭菜单',
|
|||
|
|
click: () => {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
function rightClick(event) {
|
|||
|
|
menusEvent(event, menus.value);
|
|||
|
|
event.preventDefault();
|
|||
|
|
}
|
|||
|
|
return { rightClick }
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 组件方式使用
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<template>
|
|||
|
|
<div class="div" @click.stop @contextmenu="rightClick">组件方式打开菜单</div>
|
|||
|
|
<vue3-menus v-model:open="isOpen" :event="eventVal" :menus="menus" hasIcon>
|
|||
|
|
<template #icon="{menu, activeIndex, index}">{{activeIndex}}</template>
|
|||
|
|
<template #label="{ menu, activeIndex, index}">插槽:{{ menu.label }}</template>
|
|||
|
|
</vue3-menus>
|
|||
|
|
</template>
|
|||
|
|
<script>
|
|||
|
|
import { defineComponent, nextTick, ref, shallowRef } from "vue";
|
|||
|
|
import { Vue3Menus } from 'vue3-menus';
|
|||
|
|
|
|||
|
|
export default defineComponent({
|
|||
|
|
name: "App",
|
|||
|
|
components: {
|
|||
|
|
Vue3Menus
|
|||
|
|
},
|
|||
|
|
setup() {
|
|||
|
|
const isOpen = ref(false);
|
|||
|
|
const eventVal = ref({});
|
|||
|
|
function rightClick(event) {
|
|||
|
|
isOpen.value = false;
|
|||
|
|
nextTick(() => {
|
|||
|
|
eventVal.value = event;
|
|||
|
|
isOpen.value = true;
|
|||
|
|
})
|
|||
|
|
event.preventDefault();
|
|||
|
|
}
|
|||
|
|
const menus = shallowRef([
|
|||
|
|
{
|
|||
|
|
label: "返回(B)",
|
|||
|
|
tip: 'Alt+向左箭头',
|
|||
|
|
click: () => {
|
|||
|
|
window.history.back(-1);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: "点击不关闭菜单",
|
|||
|
|
tip: '不关闭菜单',
|
|||
|
|
click: () => {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]);
|
|||
|
|
return { menus, isOpen, rightClick, eventVal }
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 参数说明
|
|||
|
|
|
|||
|
|
### 单个菜单项参数`MenusItemOptions`
|
|||
|
|
|
|||
|
|
| 属性 | 描述 | 类型 | 是否必填 | 默认值 |
|
|||
|
|
| :------: | :----------------------------------------------------------: | :--------------------: | :------: | :---------: |
|
|||
|
|
| label | 菜单项名称 | `string` | `true` | — |
|
|||
|
|
| style | 每一项菜单的自定义样式 | `object` | `false` | `{}` |
|
|||
|
|
| icon | 图标参数,内部支持html字符串图标,传入组件时需要实现icon插槽 | `string` \| `其他类型` | `false` | `undefined` |
|
|||
|
|
| disabled | 是否禁用菜单项 | `boolean` | `false` | `undefined` |
|
|||
|
|
| divided | 是否显示分割线 | `boolean` | `false` | `undefined` |
|
|||
|
|
| tip | 没项菜单后面的小提示 | `string` | `false` | `''` |
|
|||
|
|
| hidden | 是否隐藏该项 | `boolean` | `false` | `undefined` |
|
|||
|
|
| children | 子菜单列表信息 | `MenusItemOptions[]` | `false` | `undefined` |
|
|||
|
|
| enter | 菜单项移入事件,返回`null`或`false`不展开子菜单 | `Function()` | `false` | `undefined` |
|
|||
|
|
| click | 菜单项点击事件,返回`null`或`false`不关闭菜单 | `Function()` | `false` | `undefined` |
|
|||
|
|
|
|||
|
|
### 指令与方法使用参数
|
|||
|
|
|
|||
|
|
| 属性 | 描述 | 类型 | 是否必填 | 默认值 |
|
|||
|
|
| :-------: | :---------------------------------------------: | :-------------------: | :------: | :---------: |
|
|||
|
|
| menus | 菜单列表信息 | `MenusItemOptions[]` | `true` | [] |
|
|||
|
|
| menusClass | 菜单外层 `div` 的 `class` 名 | `string` | `false` | `null` |
|
|||
|
|
| itemClass | 菜单每一项的`class`名 | `string` | `false` | `null` |
|
|||
|
|
| minWidth | 菜单容器最小宽度 | `number` \| `string` | `false` | `none` |
|
|||
|
|
| maxWidth | 菜单容器最打宽度 | `number` \| `string` | `false` | `none` |
|
|||
|
|
| zIndex | 菜单层级 | `number` \| `string` | `false` | `3` |
|
|||
|
|
| direction | 菜单打开方向 | `left` \| `right` | `false` | `right` |
|
|||
|
|
|
|||
|
|
### 组件使用参数
|
|||
|
|
|
|||
|
|
| 属性 | 描述 | 类型 | 是否必填 | 默认值 | 插槽传入值 |
|
|||
|
|
| :-------: | :---------------------------------------------: | :-------------------: | :------------------: | :---------: | :-----------------------------------------------: |
|
|||
|
|
| menus | 菜单列表信息 | `MenusItemOptions[]` | `true` | [] | |
|
|||
|
|
| event | 鼠标事件信息(指令使用时不传) | `Event` | 与`position`必填一项 | {} | |
|
|||
|
|
| menusClass | 菜单外层 `div` 的 `class` 名 | `string` | `false` | `null` | |
|
|||
|
|
| itemClass | 菜单每一项的`class`名 | `string` | `false` | `null` | |
|
|||
|
|
| minWidth | 菜单容器最小宽度 | `number` \| `string` | `false` | `none` | |
|
|||
|
|
| maxWidth | 菜单容器最打宽度 | `number` \| `string` | `false` | `none` | |
|
|||
|
|
| zIndex | 菜单层级 | `number` \| `string` | `false` | `3` | |
|
|||
|
|
| direction | 菜单打开方向 | `left` \| `right` | `false` | `right` | |
|
|||
|
|
| open | 控制菜单组件显示 | `boolean` | `true` | `false` | |
|
|||
|
|
| args | 附加参数 | `unknown` | `false` | `undefined` | |
|
|||
|
|
| default | 默认插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中索引, `menu`: 当前菜单项 `MenusItemOptions`, `index`: 当前菜单索引 |
|
|||
|
|
| icon | 图标插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中索引, `menu`: 当前菜单项 `MenusItemOptions`, `index`: 当前菜单索引 |
|
|||
|
|
| label | 菜单标题插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中索引, `menu`: 当前菜单项 `MenusItemOptions`, `index`: 当前菜单索引 |
|
|||
|
|
| suffix | 菜单后缀插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中索引, `menu`: 当前菜单项 `MenusItemOptions`, `index`: 当前菜单索引 |
|
|||
|
|
|
|||
|
|
### 指令使用配置
|
|||
|
|
|
|||
|
|
> 配置参数与方法使用相同
|
|||
|
|
|
|||
|
|
| 指令使用方式 | 描述 | 参数类型 | 参数是否必填 | 默认值 |
|
|||
|
|
| :-----------: | :------------------------: | :-----------: | :----------: | :----: |
|
|||
|
|
| v-menus | 绑定元素右击打开菜单 | `MenuOptions` | `true` | - |
|
|||
|
|
| v-menus:all | 绑定元素左右击均可打开菜单 | `MenuOptions` | `true` | - |
|
|||
|
|
| v-menus:left | 绑定元素左击打开 | `MenuOptions` | `true` | - |
|
|||
|
|
| v-menus:right | 绑定元素右击打开 | `MenuOptions` | `true` | - |
|