Vue3.0實現原生高度可自定義選單元件vue3-menus

xfy520發表於2021-08-30

Vue3.0 自定義右鍵選單

Vue3.0 原生實現完全自定義右鍵選單元件, 零依賴,可根據可視區域自動調節顯示位置,可支援插槽完全重寫每一項選單

展示

專案地址

線上演示

快速安裝

npm 安裝

npm install vue3-menus

yarn add vue3-menus

CDN

<script src="https://unpkg.com/vue3-menus/dist/vue3-menus.umd.min.js">

使用

CDN引入則不需要 app.use(Vue3Menus)

樣例中使用的是@ant-design/icons-vue圖示與@element-plus/icons圖示、圖示可以使用html程式碼傳入、也可以通過插槽自定義圖示、也可以完全重寫每一項選單

// 全域性註冊元件、指令、方法
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');
<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 v-model:open="isOpen" :event="eventVal" :menus="menus.menus" hasIcon>
      <template #icon="{item: {activeIndex}}">{{activeIndex}}</template>
      <template #label="{ item: { item } }">插槽:{{ item.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',
          icon: {
            node: SyncOutlined,
            option: {
              spin: true
            }
          },
          click: () => location.reload(),
          divided: true
        },
        {
          label: "另存為(A)...",
          tip: 'Ctrl+S'
        },
        {
          label: "列印(P)...",
          tip: 'Ctrl+P',
          icon: {
            node: Printer,
            option: {
              color: 'red'
            }
          },
          click: () => window.print(),
        },
        {
          label: "投射(C)...",
          divided: true
        },
        {
          label: '傳送到你的裝置',
          icon: WindowsOutlined,
          children: [
            {
              label: 'iPhone',
            },
            {
              label: 'iPad'
            },
            {
              label: 'Windows 11'
            }
          ]
        },
        {
          label: "為此頁面建立二維碼",
          divided: true,
          icon: {
            node: QrcodeOutlined,
            option: {
              style: {
                color: 'aqua'
              }
            }
          }
        },
        {
          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>
.div {
  display: inline-block;
  background-color: aqua;
  margin: 0 20px;
  line-height: 200px;
  padding: 0 20px;
  height: 200px;
}

指令方式使用

<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>

方法方式使用

<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>

元件方式使用

<template>
  <div class="div" @click.stop @contextmenu="rightClick">元件方式開啟選單</div>
  <vue3-menus v-model:open="isOpen" :event="eventVal" :menus="menus" hasIcon>
    <template #icon="{item: {activeIndex}}">{{activeIndex}}</template>
    <template #label="{ item: { item } }">插槽:{{ item.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>

Vite下使用

使用方式1

import { createApp } from 'vue';
import App from './App.vue';
import Vue3Menus from 'https://esm.sh/vue3-menus@1.0.3'; // 也可以將1.0.3換成其他版本號
const app = createApp(App);
app.mount('#app');

使用方式2

在vite配置檔案vite.config中進行別名替換

import { createApp } from 'vue';
import App from './App.vue';
import Vue3Menus from 'vue3-menus';
const app = createApp(App);
app.mount('#app');
export default {
  resolve: {
    alias: {
      // 其他配置
      'vue3-menus': 'https://esm.sh/vue3-menus@1.0.3'// 也可以將1.0.3換成其他版本號
    }
  }
}

引數說明

單個選單項引數MenusItemOptions

屬性 描述 型別 是否必填 預設值
label 選單項名稱 string true
style 每一項選單的自定義樣式 object false {}
icon string: 傳入圖示html程式碼、object: 傳入元件或者{node: 元件, option: 元件配置引數} string | object false undefined
disabled 是否禁用選單項 boolean false undefined
divided 是否顯示分割線 boolean false undefined
tip 沒項選單後面的小提示 string false ''
click 選單項點選事件,返回nullfalse不關閉選單 Function() false undefined
children 子選單列表資訊 MenusItemOptions[] false undefined

公共引數MenuOptions

屬性 描述 型別 是否必填 預設值
menus 選單列表資訊 MenusItemOptions[] true []
menusStyle 選單容器的樣式 object false {}
menusItemClass 選單每一項的class string false null
event 滑鼠事件資訊(指令使用時可以不傳) Event position必填一項 {}
position 手動傳入選單顯示位置(指令使用時可以不傳) {x: number, y: number} event必填一項 {}
minWidth 選單容器最小寬度 number | string false none
maxWidth 選單容器最打寬度 number | string false none
zIndex 選單層級 number | string false 3

元件Vue3Menus引數

屬性 描述 型別 是否必填 預設值 插槽傳入值
open 控制選單元件顯示: v-model:open boolean true false false
default 預設插槽 Slot false - activeIndex: 當前選中項, item: 當前選單屬性值
icon 圖示插槽 Slot false - activeIndex: 當前選中項, item: 當前選單屬性值
label 選單標題插槽 Slot false - activeIndex: 當前選中項, item: 當前選單屬性值
suffix 選單字尾插槽 Slot false - activeIndex: 當前選中項, item: 當前選單屬性值

指令使用配置

指令使用方式 描述 引數型別 引數是否必填 預設值
v-menus 繫結元素右擊開啟選單 MenuOptions true -
v-menus:all 繫結元素左右擊均可開啟選單 MenuOptions true -
v-menus:left 繫結元素左擊開啟 MenuOptions true -
v-menus:right 繫結元素右擊開啟 MenuOptions true -
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章