Skip to content

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';
参数说明类型默认值
popupVisible弹出框是否可见booleanundefined
defaultPopupVisible默认是否可见,非受控模式booleanfalse
trigger触发方式'hover' | 'click' | 'focus' | 'contextMenu' | array'click'
position弹出位置'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br''bottom'
popupContainer弹出层挂载容器string | HTMLElement-
popupMaxHeight弹出层最大高度boolean | numbertrue
loading是否为加载中状态booleanfalse
isEmpty是否为空状态booleanfalse
bottomOffset触发到达底部的偏移量number0
alignPoint弹出层是否跟随鼠标位置,常用于右键菜单booleanfalse
hideOnSelect选择后是否隐藏弹出层booleantrue
事件名说明回调参数
update:popupVisible弹出层显隐变化时触发visible: boolean
popup-visible-change弹出层显隐变化时触发visible: boolean
select选中菜单项时触发value, event
scroll下拉菜单滚动时触发event
reach-bottom下拉菜单滚动到底部时触发event
插槽名说明
default触发元素
content下拉菜单内容
empty空状态内容
footer下拉菜单底部
参数说明类型默认值
value选项值,不传时使用文本内容string | number | object-
disabled是否禁用booleanfalse
事件名说明回调参数
click点击或键盘激活选项时触发event
插槽名说明
default选项内容
icon选项图标
suffix选项后缀
参数说明类型默认值
title分组标题string-
插槽名说明
title分组标题
default分组内选项
参数说明类型默认值
disabled是否禁用booleanfalse
trigger触发方式'hover' | 'click' | array'click'
position弹出位置'rt' | 'lt''rt'
popupVisible弹出层是否可见booleanundefined
defaultPopupVisible默认是否可见booleanfalse
optionProps传给内部 DropdownOption 的属性object-
事件名说明回调参数
update:popupVisible子菜单显隐变化时触发visible: boolean
popup-visible-change子菜单显隐变化时触发visible: boolean
插槽名说明
default子菜单触发项内容
icon子菜单图标
suffix子菜单后缀
content子菜单内容
footer子菜单底部
参数说明类型默认值
popupVisible弹出框是否可见booleanundefined
defaultPopupVisible默认是否可见,非受控模式booleanfalse
trigger触发方式'hover' | 'click' | 'focus' | 'contextMenu' | array'click'
position弹出位置'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br''br'
popupContainer弹出层挂载容器string | HTMLElement-
disabled是否禁用booleanfalse
type按钮类型ButtonTypes'default'
size按钮尺寸ButtonSize'medium'
buttonProps左侧主按钮属性ButtonProps-
hideOnSelect选择后是否隐藏弹出层booleantrue
事件名说明回调参数
update:popupVisible弹出层显隐变化时触发visible: boolean
popup-visible-change弹出层显隐变化时触发visible: boolean
click点击左侧主按钮时触发event
select选中菜单项时触发value, event
插槽名说明
default左侧主按钮内容
icon右侧下拉按钮图标,参数为 popupVisible
content下拉菜单内容

交互说明

  • 菜单项支持鼠标点击,也支持聚焦后按 Enter 或 Space 触发。
  • 复杂菜单项建议显式传入 value,避免默认文本值受到图标、后缀或隐藏文本影响。
  • 多级菜单当前主要适合鼠标和悬浮交互,完整方向键焦点管理仍在待补齐范围内。