Skip to content

EpMenu

基础用法

默认纵向:

展开

横向:

插槽:

View Source
vue
<script setup lang="ts">
import type { RouteRecordRaw } from 'vue-router'

import { ref } from 'vue'
import { useRouter } from 'vitepress'

const menuList = ref<RouteRecordRaw[]>([
  {
    name: 'components',
    path: '/components',
    redirect: '',
    meta: { title: 'Components', icon: 'ep:menu' },
    children: [
      {
        name: 'components-start',
        path: '/components/index',
        redirect: '',
        meta: { title: 'Start', icon: 'ep:menu' },
      },
      {
        name: 'components-ep-menu',
        path: '/components/ep-menu/index',
        redirect: '',
        meta: { title: 'EpMenu', icon: 'ep:menu' },
      },
      {
        name: 'components-ep-tree',
        path: '/components/ep-tree/index',
        redirect: '',
        meta: { title: 'EpTree', icon: 'ep:menu' },
      },
      {
        name: 'components-icon-select',
        path: '/components/icon-select/index',
        redirect: '',
        meta: { title: 'IconSelect', icon: 'ep:menu' },
      },
    ],
  },
  {
    name: 'composables',
    path: '/composables',
    redirect: '',
    meta: { title: 'Composables', icon: 'ep:menu' },
    children: [
      {
        name: 'composables-start',
        path: '/composables/index',
        redirect: '',
        meta: { title: 'Start', icon: 'ep:menu' },
      },
      {
        name: 'composables-use-crud',
        path: '/composables/use-crud/',
        redirect: '',
        meta: { title: 'useCrud', icon: 'ep:menu' },
      },
      {
        name: 'composables-use-dict',
        path: '/composables/use-dict/',
        redirect: '',
        meta: { title: 'useDict', icon: 'ep:menu' },
      },
    ],
  },
])

const router = useRouter()
function onMenuItemClick(route: RouteRecordRaw) {
  console.log('item-click', route)
  if (!route.children?.length)
    router.go(route.path)
}
function onMenuItemContextmenu(route: RouteRecordRaw) {
  console.log('item-contextmenu', route)
}

const activeMenuIndex = ref('/components/ep-menu/index')

const collapse = ref(false)
</script>

<template>
  <h3>默认纵向:</h3>
  <div style="width:300px">
    <el-switch v-model="collapse" active-text="折叠" inactive-text="展开" />
    <EpMenu v-model="activeMenuIndex" :routes="menuList" :collapse="collapse" @item-click="onMenuItemClick" @item-contextmenu="onMenuItemContextmenu" />
  </div>
  <h3>横向:</h3>
  <EpMenu
    v-model="activeMenuIndex" :routes="menuList" mode="horizontal" @item-click="onMenuItemClick"
    @item-contextmenu="onMenuItemContextmenu"
  />
  <h3>插槽:</h3>
  <EpMenu v-model="activeMenuIndex" :routes="menuList" :collapse="collapse">
    <template #default="{ route }">
      <span @click="onMenuItemClick(route)">
        自定义插槽 - {{ route.meta?.title }}
      </span>
    </template>
  </EpMenu>
  <EpMenu v-model="activeMenuIndex" :routes="menuList" mode="horizontal">
    <template #default="{ route }">
      <div style="width:100%" @click="onMenuItemClick(route)">
        自定义插槽 - {{ route.meta?.title }}
      </div>
    </template>
  </EpMenu>
</template>

属性

名称说明类型可选值默认值
(...menuProps)el-menu所有属性MenuProps--
(...subMenuProps)el-sub-menu所有属性SubMenuProps--
v-model绑定的菜单项string--
routes路由RouteRecordRaw[]--
indexKey菜单索引string'name'|'path''path'

事件

名称说明回调参数
item-clickmenu-item 和 sub-menu 点击的回调(route:RouteRecordRaw)
item-contextmenumenu-item 和 sub-menu 右击的回调(route:RouteRecordRaw)
select菜单激活回调Parameters<MenuEmits['select']
opensub-menu 展开的回调Parameters<MenuEmits['open']
closesub-menu 收起的回调Parameters<MenuEmits['close']

方法

名称说明参数
open展开指定的 sub-menu(index:string)
close收起指定的 sub-menu(index:string)

插槽

名称说明参数
-自定义菜单项内容{route: RouteRecordRaw}

TIP

使用插槽需要自行处理点击事件

路由定义

ts
declare module 'vue-router' {
  interface RouteMeta {
    /** 菜单标题 */
    title?: string
    /** 菜单图标 */
    icon?: string
    /** 禁用菜单 */
    disabled?: boolean
  }
}

类型定义

ts
import type { ExtractPropTypes, PropType } from 'vue'
import type { RouteRecordRaw } from 'vue-router'
import type { MenuItemSlots } from './menu-item'

import { menuEmits as elMenuEmits, menuProps as elMenuProps } from 'element-plus'
import { omit } from 'lodash-unified'

import { menuItemProps } from './menu-item'

export const menuProps = {
  ...elMenuProps,
  ...omit(menuItemProps, 'route'),
  routes: { type: Array as PropType<RouteRecordRaw[]>, default: () => [] },
  modelValue: { type: String },
}

export const menuEmits = {
  ...elMenuEmits,
  itemClick: (_route: RouteRecordRaw) => true,
  itemContextmenu: (_route: RouteRecordRaw) => true,
}

export type MenuProps = ExtractPropTypes<typeof menuProps>
export type MenuEmits = typeof menuEmits
export type MenuSlots = MenuItemSlots
ts
import type { ExtractPropTypes, PropType } from 'vue'
import type { RouteRecordRaw } from 'vue-router'

import { menuProps, subMenuProps } from 'element-plus'
import { omit } from 'lodash-unified'

export const menuItemProps = {
  ...omit(subMenuProps, 'index'),
  /** 是否折叠 */
  collapse: menuProps.collapse,
  /** 菜单项 */
  route: { type: Object as PropType<RouteRecordRaw>, default: () => ({}) },
  /** 菜单索引 */
  indexKey: { type: String as PropType<'name' | 'path'>, default: 'path' },
}

export const menuItemEmits = {
  click: (_route: RouteRecordRaw) => true,
  contextmenu: (_route: RouteRecordRaw) => true,
}

export type MenuItemProps = ExtractPropTypes<typeof menuItemProps>
export type MenuItemEmits = typeof menuItemEmits
export interface MenuItemSlots {
  default(props: { route: RouteRecordRaw }): any
}