Skip to content

自定义样式和渲染

自定义样式

如果你觉得默认菜单样式不好看,想修改掉它,还可以覆盖默认css样式(比起覆盖默认样式,但是还是推荐你使用自定义主题,更简单而且支持动态切换),所有的css样式定义都在源代码 /ContextMenu.scss 中。你可以将所有样式复制出来,按需修改,存放在你的文件中。然后在导入的地方覆盖默认样式:

js
import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css'
import '你的样式scss文件路径.scss'

css变量

菜单的样式抽离了一些css变量供您使用,你可以很方便的修改颜色,无须每个状态都特殊指定。

scss
//Backgroud
--mx-menu-backgroud: #fff; //菜单背景颜色
--mx-menu-hover-backgroud: #f1f1f1; //菜单条目鼠标悬浮时的背景颜色
--mx-menu-active-backgroud: #dfdfdf; //菜单条目鼠标按下时的背景颜色
--mx-menu-open-backgroud: #f1f1f1; //菜单条目子菜单打开时的背景颜色
--mx-menu-open-hover-background: #f1f1f1; //菜单条目子菜单打开时悬浮的背景颜色
--mx-menu-divider: #f0f0f0; //菜单分割线条目颜色

//Text
--mx-menu-text: #2e2e2e; //菜单文字颜色
--mx-menu-hover-text: #2e2e2e; //菜单条目鼠标悬浮时的文字颜色
--mx-menu-active-text: #2e2e2e; //菜单条目鼠标按下时的文字颜色
--mx-menu-open-text: #2e2e2e; //菜单条目子菜单打开时的文字颜色
--mx-menu-open-hover-text: #2e2e2e; //菜单条目子菜单打开时悬浮的文字颜色
--mx-menu-disabled-text: #c9c8c8; //菜单条目禁用的文字颜色

//Shadow
--mx-menu-shadow-color: rgba(0, 0, 0, 0.1); //菜单背景阴影颜色
--mx-menu-backgroud-radius: 10px; //菜单背景圆角大小

//Shortcut badge
--mx-menu-shortcut-backgroud: #ebebeb; //菜单快捷键标记的背景颜色,下方是它的四个状态
--mx-menu-shortcut-backgroud-hover:#ebebeb;
--mx-menu-shortcut-backgroud-active:#ebebeb;
--mx-menu-shortcut-backgroud-open:#ebebeb;
--mx-menu-shortcut-backgroud-disabled:#fdfdfd;
--mx-menu-shortcut-text: #424242; //菜单快捷键标记的文字颜色,下方是它的四个状态
--mx-menu-shortcut-text-hover: #424242;
--mx-menu-shortcut-text-active: #424242;
--mx-menu-shortcut-text-open: #424242;
--mx-menu-shortcut-text-disabled: #a5a5a5;

//Focus border color
--mx-menu-focus-color: #0085f1; //菜单项键盘激活时的外框颜色

//Icon placeholder width
--mx-menu-placeholder-width: 24px; //图标占位区域的宽度

自定义字体

你可以通过 css 自定义设置字体:

scss
//设置所有菜单的字体
.mx-context-menu {
  font-family: 'Times New Roman', Times, serif;
}

//设置指定菜单的字体
.my-menu {
  font-family: 'Times New Roman', Times, serif;
}

//自定义类
//this.$contextmenu({
//  customClass: 'my-menu',
//  ...
//});

自定义渲染

菜单提供了一些插槽,允许你自定义渲染某些部分。

完整案例可在这里查看

函数模式

js
this.$contextmenu({
  customClass
  items: [
    { 
      label: "Item with custom icon render",
      //Icon 属性支持传入 VNode 自定义渲染内容
      icon: h('img', {
        src: 'https://imengyu.top/assets/images/test/icon.png',
        style: {
          width: '20px',
          height: '20px',
        }
      }),
      divided: true, 
    },
    { 
      //Label 属性支持传入 VNode 自定义渲染内容
      label: h('div', {
        style: {
          fontSize: '20px',
          color: '#f98',
        }
      }, "Item with custom render"),
    },
  ],
  zIndex: 3,
  minWidth: 230,
  x: e.x,
  y: e.y
})

组件模式

组件模式支持更多的自定义插槽。

注:函数模式也同样支持这些插槽,只需要在 showContextMenu 函数传入第二个参数,提供插槽渲染函数,案例可参考 examples\views\BasicCustomize.vue 112行。

插槽名描述参数
itemRender当前菜单全局条目渲染插槽MenuItemRenderData
itemIconRender当前菜单全局图标渲染插槽MenuItemRenderData
itemLabelRender当前菜单全局文字渲染插槽MenuItemRenderData
itemRightArrowRender当前菜单全局右侧箭头渲染插槽MenuItemRenderData
separatorRender当前菜单分隔符渲染插槽-

下方是一个完全自定义菜单的案例,你可以参考此案例二次封装自己的菜单组件使用。

vue
//MyContextMenu.vue
<template>
  <!--this is Full Customized context-menu-->
  <context-menu
    :show="show"
    :options="{ ...options, x, y }"
    @update:show="(v: boolean) => $emit('update:show', v)"
  >
    <!--itemRender slot can customize the rendering of the entire menu item-->
    <template #itemRender="{ disabled, label, icon, showRightArrow, onClick, onMouseEnter }">
      <div 
        :class="'my-menu-item'+(disabled?' disabled':'')"
        @click="onClick"
        @mouseenter="onMouseEnter"
      >
        <img v-if="icon" :src="icon" />
        <div v-else class="icon-place-holder"></div>
        <span>{{label}}</span>
        <span v-if="showRightArrow" class="right-arraw">>></span>
      </div>
    </template>

    <slot />
  </context-menu>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import type { MenuOptions } from '@imengyu/vue3-context-menu';

export default defineComponent({
  data() {
    return {
      options: {
        // The slot rendering submenu container is temporarily unavailable, 
        // but the custom class can be used to customize the style of the container
        customClass: "my-menu-box", 
        zIndex: 3,
        minWidth: 230,
      } as MenuOptions,
    }
  },
  props: {
    show: {
      type: Boolean,
      required: true,
    },
    x: {
      type: Number,
      required: true,
    },
    y: {
      type: Number,
      required: true,
    },
  },
});
</script>

<style>
.my-menu-box {
  border-radius: 0!important;
  box-shadow: none!important;
  border: 1px solid #585858!important;
  background-color: #fff!important;
  padding: 0!important;
}
.my-menu-item {
  padding: 2px 10px;
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
  user-select: none;
}
.my-menu-item:hover {
  background-color: rgb(223, 223, 223);
}
.my-menu-item .icon-place-holder {
  width: 20px;
  height: 20px;
}
.my-menu-item img {
  width: 20px;
  height: 20px;
}
.my-menu-item span {
  font-size: 15px;
  color: #000;
  white-space: nowrap;
}
.my-menu-item .right-arraw {
  font-size: 20px;
  color: #f05;
}
.my-menu-sperator {
  border-bottom: 1px dashed #f00;
}
</style>

封装之后的使用方法:

vue
//TestMyMenu.vue
<template>
  <!--使用方法与之前的一样-->
  <MyContextMenu
    v-model:show="show"
    :x="x"
    :y="y"
  >
    <context-menu-item label="Simple item" @click="alertContextMenuItemClicked('Item1')" />
    <context-menu-item label="Item with a icon" icon="https://imengyu.top/assets/images/test/icon.png" @click="alertContextMenuItemClicked('Item2')" />
    <context-menu-group label="Menu with child">
      <context-menu-item label="Item1" @click="alertContextMenuItemClicked('Item1')" />
      <context-menu-item label="Item1" @click="alertContextMenuItemClicked('Item1')" />
    </context-menu-group>
    <div class="my-menu-sperator"></div>
    <context-menu-group label="Menu with child child child">
      <context-menu-item label="Item1" @click="alertContextMenuItemClicked('Item1')" />
      <context-menu-item label="Item2" @click="alertContextMenuItemClicked('Item2')" />
      <context-menu-group label="Child with v-for 50">
        <context-menu-item v-for="index of 50" :key="index" :label="'Item3-'+index" @click="alertContextMenuItemClicked('Item3' + index)" />
      </context-menu-group>
      <div class="my-menu-sperator"></div>
      <context-menu-group label="Childs">
        <context-menu-item label="Item1-1" @click="alertContextMenuItemClicked('Item1-1')" />
        <context-menu-item label="Item1-2" @click="alertContextMenuItemClicked('Item1-2')" />
        <div class="my-menu-sperator"></div>
        <context-menu-group label="Childs">
          <context-menu-item label="Item2-1" @click="alertContextMenuItemClicked('Item2-1')" />
          <context-menu-item label="Item2-2" @click="alertContextMenuItemClicked('Item2-2')" />
        </context-menu-group>
      </context-menu-group>
    </context-menu-group>
  </MyContextMenu>
</template>


<script lang="ts">
import { defineComponent } from 'vue'
import MyContextMenu from './MyContextMenu.vue';//导入上面的组件

export default defineComponent({
  data() {
    return {
      show: false,
      x: 500,
      y: 200
    }
  },
  component: {
    //注册组件
    MyContextMenu
  },
  methods: {
    onContextMenu(e : MouseEvent) {
      e.preventDefault();
      //Set the mouse position
      this.x = e.x;
      this.y = e.y;
      //Show menu
      this.show = true;
    },
    alertContextMenuItemClicked(name: string) {
      alert(name);
    },
  }
});
</script>

特殊样式

mx-context-no-clickable : 如果你在子元素中添加这个样式,则点击子元素不会触发菜单的点击事件。

Released under the MIT License.