Menu 导航菜单
为页面和功能提供导航的菜单列表。
何时使用
导航菜单是一个网站的灵魂,用户依赖导航在各个页面中进行跳转。一般分为顶部导航和侧边导航,顶部导航提供全局性的类目和功能,侧边导航提供多级结构来收纳和排列网站架构。
更多布局和导航的使用可以参考:通用布局。
代码演示
···
Navigation One
···
Navigation Two
···
Navigation Three - Submenu
···
- ···
水平的顶部导航菜单。
<template>
<a-menu v-model:selectedKeys="current" mode="horizontal">
<a-menu-item key="mail">
<mail-outlined />
Navigation One
</a-menu-item>
<a-menu-item key="app" disabled>
<appstore-outlined />
Navigation Two
</a-menu-item>
<a-sub-menu>
<template #title>
<span class="submenu-title-wrapper">
<setting-outlined />
Navigation Three - Submenu
</span>
</template>
<a-menu-item-group title="Item 1">
<a-menu-item key="setting:1">Option 1</a-menu-item>
<a-menu-item key="setting:2">Option 2</a-menu-item>
</a-menu-item-group>
<a-menu-item-group title="Item 2">
<a-menu-item key="setting:3">Option 3</a-menu-item>
<a-menu-item key="setting:4">Option 4</a-menu-item>
</a-menu-item-group>
</a-sub-menu>
<a-menu-item key="alipay">
<a href="https://antdv.com" target="_blank" rel="noopener noreferrer">
Navigation Four - Link
</a>
</a-menu-item>
</a-menu>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const current = ref<string[]>(['mail']);
return {
current,
};
},
components: {
MailOutlined,
AppstoreOutlined,
SettingOutlined,
},
});
</script>
Navigation One
Item 1
- Option 1
- Option 2
Item 2
- Option 3
- Option 4
Navigation Two
Navigation Three
垂直菜单,子菜单内嵌在菜单区域。
<template>
<a-menu
id="dddddd"
style="width: 256px"
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
mode="inline"
@click="handleClick"
>
<a-sub-menu key="sub1" @titleClick="titleClick">
<template #title>
<span>
<MailOutlined />
<span>Navigation One</span>
</span>
</template>
<a-menu-item-group key="g1">
<template #title>
<QqOutlined />
<span>Item 1</span>
</template>
<a-menu-item key="1">Option 1</a-menu-item>
<a-menu-item key="2">Option 2</a-menu-item>
</a-menu-item-group>
<a-menu-item-group key="g2" title="Item 2">
<a-menu-item key="3">Option 3</a-menu-item>
<a-menu-item key="4">Option 4</a-menu-item>
</a-menu-item-group>
</a-sub-menu>
<a-sub-menu key="sub2" @titleClick="titleClick">
<template #title>
<span>
<AppstoreOutlined />
<span>Navigation Two</span>
</span>
</template>
<a-menu-item key="5">Option 5</a-menu-item>
<a-menu-item key="6">Option 6</a-menu-item>
<a-sub-menu key="sub3" title="Submenu">
<a-menu-item key="7">Option 7</a-menu-item>
<a-menu-item key="8">Option 8</a-menu-item>
</a-sub-menu>
</a-sub-menu>
<a-sub-menu key="sub4">
<template #title>
<span>
<SettingOutlined />
<span>Navigation Three</span>
</span>
</template>
<a-menu-item key="9">Option 9</a-menu-item>
<a-menu-item key="10">Option 10</a-menu-item>
<a-menu-item key="11">Option 11</a-menu-item>
<a-menu-item key="12">Option 12</a-menu-item>
</a-sub-menu>
</a-menu>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from 'vue';
import { MailOutlined, QqOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const selectedKeys = ref<string[]>(['1']);
const openKeys = ref<string[]>(['sub1']);
const handleClick = (e: Event) => {
console.log('click', e);
};
const titleClick = (e: Event) => {
console.log('titleClick', e);
};
watch(
() => openKeys,
val => {
console.log('openKeys', val);
},
);
return {
selectedKeys,
openKeys,
handleClick,
titleClick,
};
},
components: {
MailOutlined,
QqOutlined,
AppstoreOutlined,
SettingOutlined,
},
});
</script>
内嵌菜单可以被缩起/展开。
你可以在 Layout 里查看侧边布局结合的完整示例。
<template>
<div style="width: 256px">
<a-button type="primary" @click="toggleCollapsed" style="margin-bottom: 16px">
<MenuUnfoldOutlined v-if="collapsed" />
<MenuFoldOutlined v-else />
</a-button>
<a-menu
mode="inline"
theme="dark"
:inline-collapsed="collapsed"
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
>
<a-menu-item key="1">
<PieChartOutlined />
<span>Option 1</span>
</a-menu-item>
<a-menu-item key="2">
<DesktopOutlined />
<span>Option 2</span>
</a-menu-item>
<a-menu-item key="3">
<InboxOutlined />
<span>Option 3</span>
</a-menu-item>
<a-sub-menu key="sub1">
<template #title>
<span>
<MailOutlined />
<span>Navigation One</span>
</span>
</template>
<a-menu-item key="5">Option 5</a-menu-item>
<a-menu-item key="6">Option 6</a-menu-item>
<a-menu-item key="7">Option 7</a-menu-item>
<a-menu-item key="8">Option 8</a-menu-item>
</a-sub-menu>
<a-sub-menu key="sub2">
<template #title>
<span>
<AppstoreOutlined />
<span>Navigation Two</span>
</span>
</template>
<a-menu-item key="9">Option 9</a-menu-item>
<a-menu-item key="10">Option 10</a-menu-item>
<a-sub-menu key="sub3" title="Submenu">
<a-menu-item key="11">Option 11</a-menu-item>
<a-menu-item key="12">Option 12</a-menu-item>
</a-sub-menu>
</a-sub-menu>
</a-menu>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, watch } from 'vue';
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
MailOutlined,
DesktopOutlined,
InboxOutlined,
AppstoreOutlined,
} from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const state = reactive({
collapsed: false,
selectedKeys: ['1'],
openKeys: ['sub1'],
preOpenKeys: ['sub1'],
});
watch(
() => state.openKeys,
(val, oldVal) => {
state.preOpenKeys = oldVal;
},
);
const toggleCollapsed = () => {
state.collapsed = !state.collapsed;
state.openKeys = state.collapsed ? [] : state.preOpenKeys;
};
return {
...toRefs(state),
toggleCollapsed,
};
},
components: {
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
MailOutlined,
DesktopOutlined,
InboxOutlined,
AppstoreOutlined,
},
});
</script>
点击菜单,收起其他展开的所有菜单,保持菜单聚焦简洁。
<template>
<div>
<a-menu
style="width: 256px"
mode="inline"
:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
@openChange="onOpenChange"
>
<a-sub-menu key="sub1">
<template #title>
<span>
<MailOutlined />
<span>Navigation One</span>
</span>
</template>
<a-menu-item key="1">Option 1</a-menu-item>
<a-menu-item key="2">Option 2</a-menu-item>
<a-menu-item key="3">Option 3</a-menu-item>
<a-menu-item key="4">Option 4</a-menu-item>
</a-sub-menu>
<a-sub-menu key="sub2">
<template #title>
<span>
<AppstoreOutlined />
<span>Navigation Two</span>
</span>
</template>
<a-menu-item key="5">Option 5</a-menu-item>
<a-menu-item key="6">Option 6</a-menu-item>
<a-sub-menu key="sub3" title="Submenu">
<a-menu-item key="7">Option 7</a-menu-item>
<a-menu-item key="8">Option 8</a-menu-item>
</a-sub-menu>
</a-sub-menu>
<a-sub-menu key="sub4">
<template #title>
<span>
<SettingOutlined />
<span>Navigation Three</span>
</span>
</template>
<a-menu-item key="9">Option 9</a-menu-item>
<a-menu-item key="10">Option 10</a-menu-item>
<a-menu-item key="11">Option 11</a-menu-item>
<a-menu-item key="12">Option 12</a-menu-item>
</a-sub-menu>
</a-menu>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const state = reactive({
rootSubmenuKeys: ['sub1', 'sub2', 'sub4'],
openKeys: ['sub1'],
selectedKeys: [],
});
const onOpenChange = (openKeys: string[]) => {
const latestOpenKey = openKeys.find(key => state.openKeys.indexOf(key) === -1);
if (state.rootSubmenuKeys.indexOf(latestOpenKey!) === -1) {
state.openKeys = openKeys;
} else {
state.openKeys = latestOpenKey ? [latestOpenKey] : [];
}
};
return {
...toRefs(state),
onOpenChange,
};
},
components: {
MailOutlined,
AppstoreOutlined,
SettingOutlined,
},
});
</script>
展示动态切换模式。
<template>
<div>
<a-switch @change="changeMode" />
Change Mode
<span class="ant-divider" style="margin: 0 1em" />
<a-switch @change="changeTheme" />
Change Theme
<br />
<br />
<a-menu
style="width: 256px"
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
:mode="mode"
:theme="theme"
>
<a-menu-item key="1">
<MailOutlined />
Navigation One
</a-menu-item>
<a-menu-item key="2">
<CalendarOutlined />
Navigation Two
</a-menu-item>
<a-sub-menu key="sub1">
<template #title>
<span>
<AppstoreOutlined />
<span>Navigation Three</span>
</span>
</template>
<a-menu-item key="3">Option 3</a-menu-item>
<a-menu-item key="4">Option 4</a-menu-item>
<a-sub-menu key="sub1-2" title="Submenu">
<a-menu-item key="5">Option 5</a-menu-item>
<a-menu-item key="6">Option 6</a-menu-item>
</a-sub-menu>
</a-sub-menu>
<a-sub-menu key="sub2">
<template #title>
<span>
<SettingOutlined />
<span>Navigation Four</span>
</span>
</template>
<a-menu-item key="7">Option 7</a-menu-item>
<a-menu-item key="8">Option 8</a-menu-item>
<a-menu-item key="9">Option 9</a-menu-item>
<a-menu-item key="10">Option 10</a-menu-item>
</a-sub-menu>
</a-menu>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import {
MailOutlined,
CalendarOutlined,
AppstoreOutlined,
SettingOutlined,
} from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const state = reactive({
mode: 'inline',
theme: 'light',
selectedKeys: ['1'],
openKeys: ['sub1'],
});
const changeMode = (checked: boolean) => {
state.mode = checked ? 'vertical' : 'inline';
};
const changeTheme = (checked: boolean) => {
state.theme = checked ? 'dark' : 'light';
};
return {
...toRefs(state),
changeMode,
changeTheme,
};
},
components: {
MailOutlined,
CalendarOutlined,
AppstoreOutlined,
SettingOutlined,
},
});
</script>
内建了两套主题 light
和 dark
,默认 light
。
<template>
<div>
<a-switch
:checked="theme === 'dark'"
checked-children="Dark"
un-checked-children="Light"
@change="changeTheme"
/>
<br />
<br />
<a-menu
style="width: 256px"
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
mode="inline"
:theme="theme"
>
<a-menu-item key="1">
<MailOutlined />
Navigation One
</a-menu-item>
<a-menu-item key="2">
<CalendarOutlined />
Navigation Two
</a-menu-item>
<a-sub-menu key="sub1">
<template #title>
<span>
<AppstoreOutlined />
<span>Navigation Three</span>
</span>
</template>
<a-menu-item key="3">Option 3</a-menu-item>
<a-menu-item key="4">Option 4</a-menu-item>
<a-sub-menu key="sub1-2" title="Submenu">
<a-menu-item key="5">Option 5</a-menu-item>
<a-menu-item key="6">Option 6</a-menu-item>
</a-sub-menu>
</a-sub-menu>
<a-sub-menu key="sub2">
<template #title>
<span>
<SettingOutlined />
<span>Navigation Four</span>
</span>
</template>
<a-menu-item key="7">Option 7</a-menu-item>
<a-menu-item key="8">Option 8</a-menu-item>
<a-menu-item key="9">Option 9</a-menu-item>
<a-menu-item key="10">Option 10</a-menu-item>
</a-sub-menu>
</a-menu>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import {
MailOutlined,
CalendarOutlined,
AppstoreOutlined,
SettingOutlined,
} from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const state = reactive({
theme: 'dark',
selectedKeys: ['1'],
openKeys: ['sub1'],
});
const changeTheme = (checked: boolean) => {
state.theme = checked ? 'dark' : 'light';
};
return {
...toRefs(state),
changeTheme,
};
},
components: {
MailOutlined,
CalendarOutlined,
AppstoreOutlined,
SettingOutlined,
},
});
</script>
- Navigation One
- Navigation Two
Navigation Three
Navigation Four
子菜单是弹出的形式。
<template>
<a-menu
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
style="width: 256px"
mode="vertical"
@click="handleClick"
>
<a-menu-item key="1">
<MailOutlined />
Navigation One
</a-menu-item>
<a-menu-item key="2">
<CalendarOutlined />
Navigation Two
</a-menu-item>
<a-sub-menu key="sub1">
<template #title>
<span>
<AppstoreOutlined />
<span>Navigation Three</span>
</span>
</template>
<a-menu-item key="3">Option 3</a-menu-item>
<a-menu-item key="4">Option 4</a-menu-item>
<a-sub-menu key="sub1-2" title="Submenu">
<a-menu-item key="5">Option 5</a-menu-item>
<a-menu-item key="6">Option 6</a-menu-item>
</a-sub-menu>
</a-sub-menu>
<a-sub-menu key="sub2">
<template #title>
<span>
<SettingOutlined />
<span>Navigation Four</span>
</span>
</template>
<a-menu-item key="7">Option 7</a-menu-item>
<a-menu-item key="8">Option 8</a-menu-item>
<a-menu-item key="9">Option 9</a-menu-item>
<a-menu-item key="10">Option 10</a-menu-item>
</a-sub-menu>
</a-menu>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, VNodeChild } from 'vue';
import {
MailOutlined,
CalendarOutlined,
AppstoreOutlined,
SettingOutlined,
} from '@ant-design/icons-vue';
interface MenuInfo {
key: string;
keyPath: string[];
item: VNodeChild;
domEvent: MouseEvent;
}
export default defineComponent({
setup() {
const state = reactive({
selectedKeys: [],
openKeys: [],
});
const handleClick = (e: MenuInfo) => {
console.log('click ', e);
};
return {
...toRefs(state),
handleClick,
};
},
components: {
MailOutlined,
CalendarOutlined,
AppstoreOutlined,
SettingOutlined,
},
});
</script>
使用单文件方式递归生成菜单。
<template>
<div style="width: 256px">
<a-button type="primary" @click="toggleCollapsed" style="margin-bottom: 16px">
<MenuUnfoldOutlined v-if="collapsed" />
<MenuFoldOutlined v-else />
</a-button>
<a-menu
:default-selected-keys="['1']"
:default-open-keys="['2']"
mode="inline"
theme="dark"
:inline-collapsed="collapsed"
>
<template v-for="item in list" :key="item.key">
<template v-if="!item.children">
<a-menu-item :key="item.key">
<PieChartOutlined />
<span>{{ item.title }}</span>
</a-menu-item>
</template>
<template v-else>
<sub-menu :menu-info="item" :key="item.key" />
</template>
</template>
</a-menu>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
MailOutlined,
} from '@ant-design/icons-vue';
// you can rewrite it to a single file component, if not, you should config vue alias to vue/dist/vue.esm-bundler.js
const SubMenu = {
name: 'SubMenu',
props: {
menuInfo: {
type: Object,
default: () => ({}),
},
},
template: `
<a-sub-menu :key="menuInfo.key" v-bind="$attrs">
<template #title>
<span>
<MailOutlined /><span>{{ menuInfo.title }}</span>
</span>
</template>
<template v-for="item in menuInfo.children" :key="item.key">
<template v-if="!item.children">
<a-menu-item :key="item.key">
<PieChartOutlined />
<span>{{ item.title }}</span>
</a-menu-item>
</template>
<template v-else>
<sub-menu :menu-info="item" :key="item.key" />
</template>
</template>
</a-sub-menu>
`,
components: {
PieChartOutlined,
MailOutlined,
},
};
const list = [
{
key: '1',
title: 'Option 1',
},
{
key: '2',
title: 'Navigation 2',
children: [
{
key: '2.1',
title: 'Navigation 3',
children: [{ key: '2.1.1', title: 'Option 2.1.1' }],
},
],
},
];
export default defineComponent({
setup() {
const collapsed = ref<boolean>(false);
const toggleCollapsed = () => {
collapsed.value = !collapsed.value;
};
return {
list,
collapsed,
toggleCollapsed,
};
},
components: {
'sub-menu': SubMenu,
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
},
});
</script>
API
<template>
<a-menu>
<a-menu-item>菜单项</a-menu-item>
<a-sub-menu title="子菜单">
<a-menu-item>子菜单项</a-menu-item>
</a-sub-menu>
</a-menu>
</template>
Menu
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
forceSubMenuRender | 在子菜单展示之前就渲染进 DOM | boolean | false |
inlineCollapsed | inline 时菜单是否收起状态 | boolean | - |
inlineIndent | inline 模式的菜单缩进宽度 | number | 24 |
mode | 菜单类型,现在支持垂直、水平、和内嵌模式三种 | string: vertical vertical-right horizontal inline | vertical |
multiple | 是否允许多选 | boolean | false |
openKeys(v-model) | 当前展开的 SubMenu 菜单项 key 数组 | string[] | |
selectable | 是否允许选中 | boolean | true |
selectedKeys(v-model) | 当前选中的菜单项 key 数组 | string[] | |
subMenuCloseDelay | 用户鼠标离开子菜单后关闭延时,单位:秒 | number | 0.1 |
subMenuOpenDelay | 用户鼠标进入子菜单后开启延时,单位:秒 | number | 0 |
theme | 主题颜色 | string: light dark | light |
overflowedIndicator | 自定义 Menu 折叠时的图标 | DOM | <span>···</span> |
Menu 事件
事件名称 | 说明 | 回调参数 |
---|---|---|
click | 点击 MenuItem 调用此函数 | function({ item, key, keyPath }) |
deselect | 取消选中时调用,仅在 multiple 生效 | function({ item, key, selectedKeys }) |
openChange | SubMenu 展开/关闭的回调 | function(openKeys: string[]) |
select | 被选中时调用 | function({ item, key, selectedKeys }) |
Menu.Item
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
disabled | 是否禁用 | boolean | false |
key | item 的唯一标志 | string | |
title | 设置收缩时展示的悬浮标题 | string |
Menu.SubMenu
参数 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
popupClassName | 子菜单样式 | string | 1.5.0 | |
disabled | 是否禁用 | boolean | false | |
key | 唯一标志 | string | ||
title | 子菜单项值 | string|slot | ||
expandIcon | 自定义 Menu 展开收起图标 | slot | 箭头图标 |
Menu.SubMenu 的子元素必须是 MenuItem
或者 SubMenu
.
SubMenu 事件
事件名称 | 说明 | 回调参数 |
---|---|---|
titleClick | 点击子菜单标题 | ({ key, domEvent }) |
Menu.ItemGroup
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
title | 分组标题 | string||function|slot |
Menu.ItemGroup 的子元素必须是 MenuItem
.
Menu.Divider
菜单项分割线,只用在弹出菜单内。