Dropdown 下拉菜单
用于承载一组收起的操作项或选择项,适合页面操作过多、需要按触发器就近展开菜单的场景。
何时使用
- 页面或列表行操作较多,需要把低频操作收纳起来。
- 触发元素附近展示命令菜单,比 Dialog 或 Drawer 更轻量。
- 菜单内容较短,用户选择后通常立即关闭浮层。
基础用法
当前选择:未选择
vue
<template>
<x-dropdown @select="handleSelect">
<x-button>更多操作</x-button>
<template #content>
<x-dropdown-option value="edit">编辑</x-dropdown-option>
<x-dropdown-option value="copy">复制</x-dropdown-option>
<x-dropdown-option value="delete" disabled>删除</x-dropdown-option>
</template>
</x-dropdown>
<p>当前选择:{{ selected }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const selected = ref('未选择');
const handleSelect = (value: unknown) => {
selected.value = String(value ?? '未选择');
};
</script>弹出位置
当前选择:未选择
vue
<template>
<x-space wrap>
<x-dropdown position="top">
<x-button>Top</x-button>
<template #content>
<x-dropdown-option value="edit">编辑</x-dropdown-option>
<x-dropdown-option value="copy">复制</x-dropdown-option>
</template>
</x-dropdown>
<x-dropdown position="br">
<x-button>BR</x-button>
<template #content>
<x-dropdown-option value="edit">编辑</x-dropdown-option>
<x-dropdown-option value="copy">复制</x-dropdown-option>
</template>
</x-dropdown>
</x-space>
</template>触发方式
最近选择:未触发
vue
<template>
<x-space wrap>
<x-dropdown trigger="click">
<x-button>点击触发</x-button>
<template #content>
<x-dropdown-option value="click">点击菜单</x-dropdown-option>
</template>
</x-dropdown>
<x-dropdown trigger="hover">
<x-button>悬浮触发</x-button>
<template #content>
<x-dropdown-option value="hover">悬浮菜单</x-dropdown-option>
</template>
</x-dropdown>
</x-space>
</template>受控显示
显示状态:隐藏;当前选择:未选择
vue
<template>
<x-space>
<x-dropdown v-model:popup-visible="visible" @select="handleSelect">
<x-button type="primary">受控菜单</x-button>
<template #content>
<x-dropdown-option value="save">保存</x-dropdown-option>
<x-dropdown-option value="publish">发布</x-dropdown-option>
</template>
</x-dropdown>
<x-button @click="visible = true">外部打开</x-button>
<x-button @click="visible = false">外部关闭</x-button>
</x-space>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const visible = ref(false);
const handleSelect = (value: unknown) => {
console.log(value);
};
</script>分组
当前选择:未选择
vue
<template>
<x-dropdown @select="handleSelect">
<x-button>项目操作</x-button>
<template #content>
<x-dropdown-group title="常用">
<x-dropdown-option value="rename">重命名</x-dropdown-option>
<x-dropdown-option value="duplicate">复制项目</x-dropdown-option>
</x-dropdown-group>
<x-dropdown-group title="危险">
<x-dropdown-option value="archive">归档</x-dropdown-option>
<x-dropdown-option value="delete" disabled>删除</x-dropdown-option>
</x-dropdown-group>
</template>
</x-dropdown>
</template>
<script setup lang="ts">
const handleSelect = (value: unknown) => {
console.log(value);
};
</script>子菜单
当前选择:未选择
vue
<template>
<x-dropdown @select="handleSelect">
<x-button>导出</x-button>
<template #content>
<x-dropdown-option value="quick-export">快速导出</x-dropdown-option>
<x-dropdown-submenu trigger="hover">
更多格式
<template #content>
<x-dropdown-option value="csv">CSV</x-dropdown-option>
<x-dropdown-option value="xlsx">Excel</x-dropdown-option>
<x-dropdown-option value="pdf">PDF</x-dropdown-option>
</template>
</x-dropdown-submenu>
</template>
</x-dropdown>
</template>
<script setup lang="ts">
const handleSelect = (value: unknown) => {
console.log(value);
};
</script>右键菜单
在此区域右键打开菜单
当前选择:未选择vue
<template>
<x-dropdown trigger="contextMenu" align-point @select="handleSelect">
<div class="context-target">在此区域右键打开菜单</div>
<template #content>
<x-dropdown-option value="refresh">刷新</x-dropdown-option>
<x-dropdown-option value="copy-link">复制链接</x-dropdown-option>
<x-dropdown-option value="open-new">新窗口打开</x-dropdown-option>
</template>
</x-dropdown>
</template>
<script setup lang="ts">
const handleSelect = (value: unknown) => {
console.log(value);
};
</script>
<style scoped>
.context-target {
padding: 24px;
border: 1px dashed #94a3b8;
}
</style>图标、后缀和页脚
vue
<template>
<x-dropdown @select="handleSelect">
<x-button>账户菜单</x-button>
<template #content>
<x-dropdown-option value="profile">
<template #icon><span>P</span></template>
个人资料
<template #suffix>⌘P</template>
</x-dropdown-option>
<x-dropdown-option value="settings">
<template #icon><span>S</span></template>
设置
<template #suffix>⌘,</template>
</x-dropdown-option>
</template>
<template #footer>
<div>菜单页脚</div>
</template>
</x-dropdown>
</template>
<script setup lang="ts">
const handleSelect = (value: unknown) => {
console.log(value);
};
</script>选择后保持打开
当前选择:未选择;显示状态:隐藏
vue
<template>
<x-dropdown v-model:popup-visible="visible" :hide-on-select="false">
<x-button>批量操作</x-button>
<template #content>
<x-dropdown-option value="select-all">全选</x-dropdown-option>
<x-dropdown-option value="invert">反选</x-dropdown-option>
<x-dropdown-option value="clear">清空选择</x-dropdown-option>
</template>
<template #footer>
<x-button size="mini" @click="visible = false">完成</x-button>
</template>
</x-dropdown>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const visible = ref(false);
</script>空态、加载和滚动
滚动菜单可触发 reach-bottom;当前选择:未选择
vue
<template>
<x-dropdown is-empty>
<x-button>空菜单</x-button>
<template #empty>暂无可用操作</template>
</x-dropdown>
<x-dropdown
:popup-max-height="144"
:bottom-offset="8"
@reach-bottom="reached = true"
>
<x-button>滚动菜单</x-button>
<template #content>
<x-dropdown-option v-for="item in items" :key="item.value" :value="item.value">
{{ item.label }}
</x-dropdown-option>
</template>
</x-dropdown>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const reached = ref(false);
const items = Array.from({ length: 18 }, (_, index) => ({
label: `操作 ${index + 1}`,
value: `action-${index + 1}`,
}));
</script>按钮式下拉
vue
<template>
<x-space wrap>
<x-dropdown-button type="primary" @click="handleClick" @select="handleSelect">
新建
<template #content>
<x-dropdown-option value="new-file">新建文件</x-dropdown-option>
<x-dropdown-option value="new-folder">新建文件夹</x-dropdown-option>
<x-dropdown-option value="import">导入</x-dropdown-option>
</template>
</x-dropdown-button>
<x-dropdown-button @click="handleClick" @select="handleSelect">
Publish
<template #content>
<x-dropdown-option value="publish-now">立即发布</x-dropdown-option>
<x-dropdown-option value="schedule">定时发布</x-dropdown-option>
</template>
</x-dropdown-button>
<x-dropdown-button disabled>
Disabled
<template #content>
<x-dropdown-option value="disabled">不可用</x-dropdown-option>
</template>
</x-dropdown-button>
</x-space>
</template>
<script setup lang="ts">
const handleClick = () => {
console.log('main button');
};
const handleSelect = (value: unknown) => {
console.log(value);
};
</script>按需导入
ts
import { Dropdown, DropdownOption, DropdownGroup, DropdownSubmenu, DropdownButton } from 'x-next';Dropdown Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
popupVisible | 弹出框是否可见 | boolean | undefined |
defaultPopupVisible | 默认是否可见,非受控模式 | boolean | false |
trigger | 触发方式 | 'hover' | 'click' | 'focus' | 'contextMenu' | array | 'click' |
position | 弹出位置 | 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'bottom' |
popupContainer | 弹出层挂载容器 | string | HTMLElement | - |
popupMaxHeight | 弹出层最大高度 | boolean | number | true |
loading | 是否为加载中状态 | boolean | false |
isEmpty | 是否为空状态 | boolean | false |
bottomOffset | 触发到达底部的偏移量 | number | 0 |
alignPoint | 弹出层是否跟随鼠标位置,常用于右键菜单 | boolean | false |
hideOnSelect | 选择后是否隐藏弹出层 | boolean | true |
Dropdown Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
update:popupVisible | 弹出层显隐变化时触发 | visible: boolean |
popup-visible-change | 弹出层显隐变化时触发 | visible: boolean |
select | 选中菜单项时触发 | value, event |
scroll | 下拉菜单滚动时触发 | event |
reach-bottom | 下拉菜单滚动到底部时触发 | event |
Dropdown Slots
| 插槽名 | 说明 |
|---|---|
default | 触发元素 |
content | 下拉菜单内容 |
empty | 空状态内容 |
footer | 下拉菜单底部 |
DropdownOption Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
value | 选项值,不传时使用文本内容 | string | number | object | - |
disabled | 是否禁用 | boolean | false |
DropdownOption Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
click | 点击或键盘激活选项时触发 | event |
DropdownOption Slots
| 插槽名 | 说明 |
|---|---|
default | 选项内容 |
icon | 选项图标 |
suffix | 选项后缀 |
DropdownGroup Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
title | 分组标题 | string | - |
DropdownGroup Slots
| 插槽名 | 说明 |
|---|---|
title | 分组标题 |
default | 分组内选项 |
DropdownSubmenu Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
disabled | 是否禁用 | boolean | false |
trigger | 触发方式 | 'hover' | 'click' | array | 'click' |
position | 弹出位置 | 'rt' | 'lt' | 'rt' |
popupVisible | 弹出层是否可见 | boolean | undefined |
defaultPopupVisible | 默认是否可见 | boolean | false |
optionProps | 传给内部 DropdownOption 的属性 | object | - |
DropdownSubmenu Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
update:popupVisible | 子菜单显隐变化时触发 | visible: boolean |
popup-visible-change | 子菜单显隐变化时触发 | visible: boolean |
DropdownSubmenu Slots
| 插槽名 | 说明 |
|---|---|
default | 子菜单触发项内容 |
icon | 子菜单图标 |
suffix | 子菜单后缀 |
content | 子菜单内容 |
footer | 子菜单底部 |
DropdownButton Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
popupVisible | 弹出框是否可见 | boolean | undefined |
defaultPopupVisible | 默认是否可见,非受控模式 | boolean | false |
trigger | 触发方式 | 'hover' | 'click' | 'focus' | 'contextMenu' | array | 'click' |
position | 弹出位置 | 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'br' |
popupContainer | 弹出层挂载容器 | string | HTMLElement | - |
disabled | 是否禁用 | boolean | false |
type | 按钮类型 | ButtonTypes | 'default' |
size | 按钮尺寸 | ButtonSize | 'medium' |
buttonProps | 左侧主按钮属性 | ButtonProps | - |
hideOnSelect | 选择后是否隐藏弹出层 | boolean | true |
DropdownButton Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
update:popupVisible | 弹出层显隐变化时触发 | visible: boolean |
popup-visible-change | 弹出层显隐变化时触发 | visible: boolean |
click | 点击左侧主按钮时触发 | event |
select | 选中菜单项时触发 | value, event |
DropdownButton Slots
| 插槽名 | 说明 |
|---|---|
default | 左侧主按钮内容 |
icon | 右侧下拉按钮图标,参数为 popupVisible |
content | 下拉菜单内容 |
交互说明
- 菜单项支持鼠标点击,也支持聚焦后按 Enter 或 Space 触发。
- 复杂菜单项建议显式传入
value,避免默认文本值受到图标、后缀或隐藏文本影响。 - 多级菜单当前主要适合鼠标和悬浮交互,完整方向键焦点管理仍在待补齐范围内。