Skip to content

菜单 Menu

要吃东西。

基本用法

通过 Menu、MenuItem、MenuGroup、Submenu 组织菜单结构。MenuItem 接收 hrefroute 参数的时候,渲染围为 <a> 标签或者 Vue Router 的 RouterLink 组件。

WARNING

hrefroute 属性将直接渲染到 <a> 标签中,如果传递类似 javascript:alert(1) 这样的值或恶意 URL,可能会导致 XSS 或开放重定向漏洞。

<template>
	<px-menu :default-active="'Home'">
		<px-menu-item index="Home" href="/pixelium-design/">
			Home
			<template #icon>
				<IconHome></IconHome>
			</template>
		</px-menu-item>
		<px-submenu index="Components" label="Components">
			<template #icon>
				<IconBars></IconBars>
			</template>
			<px-menu-item index="Button">
				Button
				<template #icon>
					<IconArrowAltCircleUp></IconArrowAltCircleUp>
				</template>
			</px-menu-item>
			<px-menu-item index="Input">
				Input
				<template #icon>
					<IconPencil></IconPencil>
				</template>
			</px-menu-item>
		</px-submenu>
		<px-menu-group index="More" label="More">
			<px-menu-item index="About">
				About
				<template #icon>
					<IconLink></IconLink>
				</template>
			</px-menu-item>
			<px-menu-item
				index="Github"
				href="https://github.com/shika-works/pixelium-design"
				target="_blank"
			>
				Github
				<template #icon>
					<IconGithub></IconGithub>
				</template>
			</px-menu-item>
		</px-menu-group>
	</px-menu>
</template>

<script setup lang="ts">
import {
	IconHome,
	IconArrowAltCircleUp,
	IconPencil,
	IconGithub,
	IconLink,
	IconBars
} from '@pixelium/web-vue/icon-hn/es'
</script>

横向菜单

设置 direction"horizontal" 时,展示横向菜单,ellipsistrue 时溢出的菜单项会自动收纳到 ... 子菜单中。

<template>
	<px-menu direction="horizontal" :ellipsis="true">
		<px-menu-item :key="i" :index="i" v-for="i in 15">Item {{ i }}</px-menu-item>
	</px-menu>
</template>

折叠侧栏

侧边栏场景可使用 collapsed 属性以缩略展示菜单。

<template>
	<div>
		<px-switch v-model="collapsed" inactive-tip="Expanded" active-tip="Collapsed"></px-switch>
		<px-menu :collapsed="collapsed" :default-active="1" style="margin-top: 8px">
			<px-menu-item index="Overview">
				Overview
				<template #icon>
					<IconGrid></IconGrid>
				</template>
			</px-menu-item>
			<px-submenu index="Components" label="Components">
				<template #icon>
					<IconBars></IconBars>
				</template>
				<px-menu-item index="Input">
					Input
					<template #icon>
						<IconPencil></IconPencil>
					</template>
				</px-menu-item>
				<px-menu-item index="Search">
					Search
					<template #icon>
						<IconSearch></IconSearch>
					</template>
				</px-menu-item>
			</px-submenu>
			<px-menu-group index="Help" label="Help">
				<px-menu-item index="Document">
					Document
					<template #icon>
						<IconFolder></IconFolder>
					</template>
				</px-menu-item>
				<px-menu-item index="AI">
					AI
					<template #icon>
						<IconRobot></IconRobot>
					</template>
				</px-menu-item>
			</px-menu-group>
		</px-menu>
	</div>
</template>
<script setup lang="ts">
import {
	IconGrid,
	IconPencil,
	IconSearch,
	IconRobot,
	IconFolder,
	IconBars
} from '@pixelium/web-vue/icon-hn'
import { ref } from 'vue'

const collapsed = ref(true)
</script>

分组与子菜单

结合 MenuGroup 与 Submenu 构建多层菜单结构。

<template>
	<px-menu>
		<px-menu-group index="Undead" label="Undead">
			<px-submenu index="Hero" label="Hero">
				<px-menu-item index="Death Knight">Death Knight</px-menu-item>
				<px-menu-item index="Lich">Lich</px-menu-item>
			</px-submenu>
			<px-submenu index="Unit" label="Unit">
				<px-submenu index="Crypt" label="Crypt">
					<px-menu-item index="Ghoul">Ghoul</px-menu-item>
					<px-menu-item index="Crypt Fiend">Crypt Fiend</px-menu-item>
				</px-submenu>
				<px-submenu index="Boneyard" label="Boneyard">
					<px-menu-item index="Frost Wyrm">Frost Wyrm</px-menu-item>
				</px-submenu>
			</px-submenu>
		</px-menu-group>
	</px-menu>
</template>

强调菜单

设置 Menu dark 属性为 true 开启高对比度的深色模式。

<template>
	<px-menu :default-active="'Home'" dark>
		<px-menu-item index="Home">
			Home
			<template #icon>
				<IconHome></IconHome>
			</template>
		</px-menu-item>
		<px-submenu index="Components" label="Components">
			<template #icon>
				<IconBars></IconBars>
			</template>
			<px-menu-item index="Button">
				Button
				<template #icon>
					<IconArrowAltCircleUp></IconArrowAltCircleUp>
				</template>
			</px-menu-item>
			<px-menu-item index="Input">
				Input
				<template #icon>
					<IconPencil></IconPencil>
				</template>
			</px-menu-item>
		</px-submenu>
		<px-menu-group index="More" label="More">
			<px-menu-item index="About">
				About
				<template #icon>
					<IconLink></IconLink>
				</template>
			</px-menu-item>
			<px-menu-item index="Github">
				Github
				<template #icon>
					<IconGithub></IconGithub>
				</template>
			</px-menu-item>
		</px-menu-group>
	</px-menu>
</template>

<script setup lang="ts">
import {
	IconHome,
	IconArrowAltCircleUp,
	IconPencil,
	IconGithub,
	IconLink,
	IconBars
} from '@pixelium/web-vue/icon-hn/es'
</script>

选项属性

Menu 的 options 属性用于直接传入选项,可以用于简单菜单的快速创建,选项的字符串值和 index 字段作为菜单子组件的唯一标识,推荐确保它们的唯一性。

<template>
	<px-space direction="vertical">
		<px-menu :options="optionsHorizontal" direction="horizontal"> </px-menu>
		<px-menu :options="optionsVertical"> </px-menu>
	</px-space>
</template>

<script setup lang="ts">
import {
	IconHome,
	IconArrowAltCircleUp,
	IconPencil,
	IconGithub,
	IconLink,
	IconBars
} from '@pixelium/web-vue/icon-hn/es'
import { h, ref } from 'vue'

const optionsVertical = ref([
	{
		label: 'Home',
		index: 'Home',
		href: '/pixelium-design/',
		icon: h(IconHome)
	},
	{
		label: 'Components',
		index: 'Components',
		icon: h(IconBars),
		type: 'submenu',
		children: [
			{
				label: 'Button',
				index: 'Button',
				icon: h(IconArrowAltCircleUp)
			},
			{
				label: 'Input',
				index: 'Input',
				icon: h(IconPencil)
			}
		]
	},
	{
		label: 'More',
		index: 'More',
		type: 'group',
		children: [
			{
				label: 'About',
				index: 'About',
				icon: h(IconLink)
			},
			{
				label: 'Github',
				index: 'Github',
				href: 'https://github.com/shika-works/pixelium-design',
				target: '_blank',
				icon: h(IconGithub)
			},
			'Contact us'
		]
	}
])
const optionsHorizontal = ref([
	{
		label: 'Home',
		index: 'Home',
		href: '/pixelium-design/',
		icon: h(IconHome)
	},
	{
		label: 'Components',
		index: 'Components',
		icon: h(IconBars),
		type: 'submenu',
		children: [
			{
				label: 'Form',
				type: 'submenu',
				children: [
					{
						label: 'Button',
						index: 'Button',
						icon: h(IconArrowAltCircleUp)
					},
					{
						label: 'Input',
						index: 'Input',
						icon: h(IconPencil)
					}
				]
			}
		]
	},
	{
		label: 'More',
		index: 'More',
		type: 'submenu',
		children: [
			{
				label: 'Link',
				index: 'Link',
				type: 'group',
				children: [
					{
						label: 'About',
						index: 'About',
						icon: h(IconLink)
					},
					{
						label: 'Github',
						index: 'Github',
						href: 'https://github.com/shika-works/pixelium-design',
						target: '_blank',
						icon: h(IconGithub)
					},
					'Contact us'
				]
			}
		]
	}
])
</script>

受控模式

通过传入 activeexpanded 控制选中菜单和展开菜单,支持 v-model。不传或为 undefined 则为非受控模式,此时可以通过 defaultActivedefaultExpanded 设置默认值。

Active: 1 | Expanded: [ "2" ]
<template>
	<div>
		<px-menu v-model:active="active" v-model:expanded="expanded">
			<px-menu-item index="1">Home</px-menu-item>
			<px-submenu index="2" label="Features">
				<px-menu-item index="2-1">One</px-menu-item>
				<px-menu-item index="2-2">Two</px-menu-item>
			</px-submenu>
		</px-menu>

		<div style="margin-top: 12px">Active: {{ active }} | Expanded: {{ expanded }}</div>
	</div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const active = ref('1')
const expanded = ref(['2'])
</script>

API

属性类型可选默认值描述版本
direction'horizontal' | 'vertical''vertical'菜单的方向0.1.0
darkbooleanfalse菜单是否为深色模式。0.1.0
activenumber | string | symbol | null激活的菜单项,支持 v-model 受控模式。0.1.0
defaultActivenumber | string | symbol | null激活的菜单项的默认值,非受控模式。0.1.0
expanded(number | string | symbol)[] | null展开的子菜单,支持 v-model 受控模式。0.1.0
defaultExpanded(number | string | symbol)[] | null展开的子菜单默认值,非受控模式。0.1.0
collapsedbooleanfalse垂直菜单是否折叠。0.1.0
submenuMode'inline' | 'popover''inline'子菜单展示方式,若子菜单 mode 未设置将采用该值。0.1.0
submenuTrigger'hover' | 'click''hover'子菜单浮窗的触发方式,若子菜单 trigger 未设置将采用该值。0.1.0
indentnumber16每级菜单的缩进。0.1.0
ellipsisbooleantrue横向菜单超出部分是否收纳到 ... 子菜单中。0.1.0
options(string | MenuOption | MenuGroupOption | SubmenuOption)[]用于创建菜单子组件的选项,当未传入 default 插槽时生效。0.1.0
事件参数描述版本
update:activevalue: number | string | symbol更新 active 的回调。0.1.0
update:expendvalue: (number | string | symbol)[]0.1.0
selectindex: number | string | symbol, event: MouseEvent选中菜单的回调。0.1.0
expandChangevalue: (number | string | symbol)[], event: MouseEvent展开的子菜单变化的回调。0.1.0
expandindex: number | string | symbol, event: MouseEvent展开一个子菜单时触发的回调。0.1.0
foldindex: number | string | symbol, event: MouseEvent折叠一个子菜单时触发的回调。0.1.0
插槽参数描述版本
default构成菜单的子组件们。0.1.0
属性类型可选默认值描述版本
labelstring文本标签。0.1.0
indexnumber | string | symbol唯一标识,MenuItem 和 Submenu 组件的 index 属性不可重复。0.1.0
disabledbooleanfalse是否禁用。0.1.0
routestring | objectVue Router 的 RouterLink 的 to 参数,传入后,将以 RouterLink 作为 <a> 渲染文本标签。如果使用该属性,请确保在全局的 Vue App 中注册 Vue Router。0.1.0
hrefstring<a> 标签的 href 属性,传入 href 参数时,将以 <a> 标签渲染文本标签。0.1.0
targetstring<a> 标签的 target 属性。0.1.0
插槽参数描述版本
default文本标签。0.1.0
icon图标。0.1.0
属性类型可选默认值描述版本
labelstring文本标签。0.1.0
indexnumber | string | symbol唯一标识,MenuItem 和 Submenu 组件的 index 属性不可重复。0.1.0
disabledbooleanfalse是否禁用。0.1.0
modeboolean子菜单展示方式,inline 为内联的下拉子菜单,popover 为弹出框展示的浮窗子菜单。值为 inline 只在垂直菜单中和非浮窗子菜单中生效。0.1.0
triggerboolean子菜单的浮窗触发方式。0.1.0
popoverPropsOmit<PopoverProps, 'visible' | 'defaultVisible' | 'content'> & EmitEvent<PopoverEvents>内部 Popover 的属性。0.1.0
插槽参数描述版本
default构成菜单的子组件们。0.1.0
label文本标签。0.1.0
icon图标。0.1.0
属性类型可选默认值描述版本
labelstring文本标签。0.1.0
插槽参数描述版本
default构成菜单的子组件们。0.1.0
label文本标签。0.1.0
ts
export interface MenuOption extends NavigationOption {
	disabled?: boolean
	href?: string
	route?: string | object
	target?: string
	icon?: () => ValidVNodeContent
}

export interface MenuGroupOption extends NavigationOption {
	children: (string | MenuOption | MenuGroupOption | SubmenuOption)[]
	type: 'group'
}

export interface SubmenuOption extends NavigationOption {
	children: (string | MenuOption | MenuGroupOption | SubmenuOption)[]
	disabled?: boolean
	type: 'submenu'
	icon?: () => ValidVNodeContent
}

export interface NavigationOption {
	index: string | number | symbol
	label?: string
}
export type ValidVNodeContent = (...args: any[]) => VNode | JSX.Element

EmitEvent

ts
export type EmitEvent<T extends Record<string, any>> = {
	[K in keyof T as `on${Capitalize<K & string>}`]?: (...args: T[K]) => void
}