Skip to content
API 风格:

Dropdown 下拉選單 ​

向下彈出的命令選單。觸發器可以是任意元素,選單項透過 command 標識,點選後以 command 事件拋出。按 WAI-ARIA menu 模式實作:方向鍵循環、Home/End 首尾、Enter/Space 觸發、Esc 關閉並歸還焦點。

基礎用法 ​

click 觸發 + command 事件

最近指令:—

vue
<template>
  <LhDropdown @command="onCommand">
    <LhButton>{{ selectedLabel || '更多操作' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="new">新建</LhDropdownItem>
      <LhDropdownItem command="edit">编辑</LhDropdownItem>
      <LhDropdownItem command="delete">删除</LhDropdownItem>
    </template>
  </LhDropdown>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const lastCommand = ref('')
// 觸發器文字由使用者維護:command 事件只拋出命令值,標籤怎麼顯示交給外部決定
const labelMap: Record<string, string> = { new: '新建', edit: '编辑', delete: '删除' }
const selectedLabel = ref('')
function onCommand(command: string | number | undefined) {
  lastCommand.value = String(command)
  selectedLabel.value = labelMap[String(command)] ?? String(command)
}
</script>
vue
<script>
export default {
  data() {
    return {
      lastCommand: '',
      // 觸發器文字由使用者維護:command 事件只拋出命令值,標籤怎麼顯示交給外部決定
      labelMap: { new: '新建', edit: '编辑', delete: '删除' },
      selectedLabel: '',
    }
  },
  methods: {
    onCommand(command) {
      this.lastCommand = String(command)
      this.selectedLabel = this.labelMap[String(command)] ?? String(command)
    },
  },
}
</script>

<template>
  <LhDropdown @command="onCommand">
    <LhButton>{{ selectedLabel || '更多操作' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="new">新建</LhDropdownItem>
      <LhDropdownItem command="edit">编辑</LhDropdownItem>
      <LhDropdownItem command="delete">删除</LhDropdownItem>
    </template>
  </LhDropdown>
</template>

觸發方式 ​

預設為 click,也可改為懸停或右鍵展開。

hover / contextmenu
vue
<script setup lang="ts">
import { ref } from 'vue'
const hoverLabel = ref('')
function onHoverCommand(command: string | number | undefined) {
  hoverLabel.value = String(command)
}
const contextLabel = ref('')
function onContextCommand(command: string | number | undefined) {
  contextLabel.value = String(command)
}
</script>

<template>
  <!-- 懸停觸發 -->
  <LhDropdown trigger="hover" @command="onHoverCommand">
    <LhButton>{{ hoverLabel || '悬停触发' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="hover-1">选项一</LhDropdownItem>
      <LhDropdownItem command="hover-2">选项二</LhDropdownItem>
    </template>
  </LhDropdown>
  <!-- 右鍵觸發 -->
  <LhDropdown trigger="contextmenu" @command="onContextCommand">
    <LhButton>{{ contextLabel || '右键触发' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="ctx-1">刷新</LhDropdownItem>
      <LhDropdownItem command="ctx-2">复制</LhDropdownItem>
    </template>
  </LhDropdown>
</template>
vue
<script>
export default {
  data() {
    return { hoverLabel: '', contextLabel: '' }
  },
  methods: {
    onHoverCommand(command) {
      this.hoverLabel = String(command)
    },
    onContextCommand(command) {
      this.contextLabel = String(command)
    },
  },
}
</script>

<template>
  <!-- 懸停觸發 -->
  <LhDropdown trigger="hover" @command="onHoverCommand">
    <LhButton>{{ hoverLabel || '悬停触发' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="hover-1">选项一</LhDropdownItem>
      <LhDropdownItem command="hover-2">选项二</LhDropdownItem>
    </template>
  </LhDropdown>
  <!-- 右鍵觸發 -->
  <LhDropdown trigger="contextmenu" @command="onContextCommand">
    <LhButton>{{ contextLabel || '右键触发' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="ctx-1">刷新</LhDropdownItem>
      <LhDropdownItem command="ctx-2">复制</LhDropdownItem>
    </template>
  </LhDropdown>
</template>

contextmenu 觸發會阻止瀏覽器預設右鍵選單。

停用與分隔線 ​

disabled / divided
vue
<script setup lang="ts">
import { ref } from 'vue'
const actionLabel = ref('')
function onActionCommand(command: string | number | undefined) {
  actionLabel.value = String(command)
}
</script>

<template>
  <!-- 帶停用項與分隔線的操作選單 -->
  <LhDropdown @command="onActionCommand">
    <LhButton>{{ actionLabel || '操作' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="export">导出</LhDropdownItem>
      <LhDropdownItem command="import">导入</LhDropdownItem>
      <LhDropdownItem divided command="audit">审计日志</LhDropdownItem>
      <LhDropdownItem disabled command="purge">清空数据</LhDropdownItem>
    </template>
  </LhDropdown>
</template>
vue
<script>
export default {
  data() {
    return { actionLabel: '' }
  },
  methods: {
    onActionCommand(command) {
      this.actionLabel = String(command)
    },
  },
}
</script>

<template>
  <!-- 帶停用項與分隔線的操作選單 -->
  <LhDropdown @command="onActionCommand">
    <LhButton>{{ actionLabel || '操作' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="export">导出</LhDropdownItem>
      <LhDropdownItem command="import">导入</LhDropdownItem>
      <LhDropdownItem divided command="audit">审计日志</LhDropdownItem>
      <LhDropdownItem disabled command="purge">清空数据</LhDropdownItem>
    </template>
  </LhDropdown>
</template>

停用項仍可被聚焦(aria-disabled="true"),螢幕閱讀器能讀出該項;但方向鍵會跳過它,點選與 Enter/Space 均不觸發 command。

位置與最大高度 ​

placement 與 maxHeight
vue
<script setup lang="ts">
import { ref } from 'vue'
const itemLabel = ref('')
function onItemCommand(command: string | number | undefined) {
  itemLabel.value = `第 ${command} 项`
}
</script>

<template>
  <!-- 底部右對齊並限制最大高度 -->
  <LhDropdown placement="bottom-end" :max-height="160" @command="onItemCommand">
    <LhButton>{{ itemLabel || '底部右对齐' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem v-for="i in 12" :key="i" :command="i">第 {{ i }} 项</LhDropdownItem>
    </template>
  </LhDropdown>
</template>
vue
<script>
export default {
  data() {
    return { itemLabel: '' }
  },
  methods: {
    onItemCommand(command) {
      this.itemLabel = `第 ${command} 项`
    },
  },
}
</script>

<template>
  <!-- 底部右對齊並限制最大高度 -->
  <LhDropdown placement="bottom-end" :max-height="160" @command="onItemCommand">
    <LhButton>{{ itemLabel || '底部右对齐' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem v-for="i in 12" :key="i" :command="i">第 {{ i }} 项</LhDropdownItem>
    </template>
  </LhDropdown>
</template>

受控顯隱與點選行為 ​

v-model:visible 手動控制顯隱;hide-on-click 控制選中後是否自動收起;popper-class 給彈出層加自訂類別;teleport-target 指定掛載容器。

受控顯隱
vue
<script setup lang="ts">
import { ref } from 'vue'
const menuVisible = ref(false)
</script>

<template>
  <!-- 受控顯隱:選中後不收起 -->
  <LhDropdown
    v-model:visible="menuVisible"
    :hide-on-click="false"
    popper-class="demo-dropdown-popper"
  >
    <LhButton>{{ menuVisible ? '收起' : '展开' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="copy">复制(选中后不收起)</LhDropdownItem>
      <LhDropdownItem command="paste">粘贴</LhDropdownItem>
    </template>
  </LhDropdown>
</template>
vue
<script>
export default {
  data() {
    return { menuVisible: false }
  },
}
</script>

<template>
  <!-- 受控顯隱:選中後不收起 -->
  <LhDropdown
    v-model:visible="menuVisible"
    :hide-on-click="false"
    popper-class="demo-dropdown-popper"
  >
    <LhButton>{{ menuVisible ? '收起' : '展开' }} ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="copy">复制(选中后不收起)</LhDropdownItem>
      <LhDropdownItem command="paste">粘贴</LhDropdownItem>
    </template>
  </LhDropdown>
</template>
掛載到指定容器
vue
<script setup lang="ts">
</script>

<template>
  <!-- 掛載到指定容器 -->
  <LhDropdown teleport-target="#dropdown-zone">
    <LhButton>更多 ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="a">选项</LhDropdownItem>
    </template>
  </LhDropdown>
</template>
vue
<script>
export default {}
</script>

<template>
  <!-- 掛載到指定容器 -->
  <LhDropdown teleport-target="#dropdown-zone">
    <LhButton>更多 ▾</LhButton>
    <template #dropdown>
      <LhDropdownItem command="a">选项</LhDropdownItem>
    </template>
  </LhDropdown>
</template>

Props ​

屬性說明類型預設值
trigger觸發方式"hover" | "click" | "contextmenu"'click'
placement展示位置(12 種,同 Tooltip)"top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end"'bottom-start'
visible顯隱狀態(v-model:visible)boolean—
disabled是否停用觸發器boolean—
hideOnClick點選選單項後是否自動關閉booleantrue
teleportTargetTeleport 目標(選擇器或元素)string | HTMLElement'body'
popperClass彈出層自訂類別名string—
maxHeight選單最大高度(數字為 px)string | number—
themeVars元件級主題變數{ [x: string]: string; }—
bgPattern元件級背景花紋,覆寫 ConfigProvider 設定string—
sxsx 快速版面配置屬性(作用於觸發器根元素)LhSxProps—

Events ​

事件說明參數
command點選選單項時觸發(command: string | number | undefined)
click選單項(LhDropdownItem)被點選時觸發(command: string | number | undefined)
visible-change顯隱變化(value: boolean)
update:visible顯隱變化(v-model)(value: boolean)

Slots ​

插槽說明
default觸發器內容
dropdown選單內容(LhDropdownItem 列表)

Methods ​

方法說明
show程式化展開
hide程式化收起
toggle程式化切換

Props ​

屬性說明類型預設值
command命令標識,點選時隨 command 事件拋出string | number—
disabled是否停用booleanfalse
divided是否在上方顯示分隔線booleanfalse

無障礙 ​

  • 觸發器帶 aria-haspopup="menu" 與 aria-expanded;選單為 role="menu",項為 role="menuitem"
  • 鍵盤遵循 APG menu 模式:↑/↓ 循環(跳過停用項)、Home/End 跳首尾、Enter/Space 觸發 command 並關閉、Esc 關閉並歸還焦點
  • 停用項用 aria-disabled 而非移出焦點樹,方向鍵與 Enter 鍵均會跳過它
  • 選單展開時主軸溢出會自動翻轉方向,交叉軸溢出會夾回視區

類型定義 ​

本元件對外匯出的 TypeScript 類型結構如下(自動提取自 packages/components/dropdown/src/types.ts 與 @lh-ui/share/core/* 原始碼,點選各條目展開檢視完整定義;屬性說明見上方 Props 表):

LhDropdownProps
ts
/**
 * Dropdown 元件 Props
 */
export interface LhDropdownProps {
  /** 觸發方式 */
  trigger?: LhDropdownTrigger
  /** 展示位置 */
  placement?: LhDropdownPlacement
  /** 手動控制的顯示狀態(v-model:visible) */
  visible?: boolean
  /** 是否停用 */
  disabled?: boolean
  /** 點選選單項後是否自動關閉 */
  hideOnClick?: boolean
  /** Teleport 目標(選擇器或元素,預設 body) */
  teleportTarget?: string | HTMLElement
  /** 彈出層自訂類別名 */
  popperClass?: string
  /** 選單最大高度 */
  maxHeight?: string | number
  /** 元件級主題變數,覆寫 ConfigProvider 設定 */
  themeVars?: Record<string, string>
  /** 元件級背景花紋,覆寫 ConfigProvider 設定 */
  bgPattern?: string
  /** sx 快速版面配置屬性(作用於觸發器根元素) */
  sx?: LhSxProps
}
LhDropdownItemProps
ts
/**
 * DropdownItem 元件 Props
 */
export interface LhDropdownItemProps {
  /** 命令標識,點選時隨 command 事件拋出 */
  command?: string | number
  /** 是否停用(保留可聚焦性以進入無障礙樹) */
  disabled?: boolean
  /** 是否顯示分隔線 */
  divided?: boolean
}
LhDropdownTrigger (來自 @lh-ui/share/core/dropdown (logic.ts))
ts
/** 觸發方式 */
export type LhDropdownTrigger = 'hover' | 'click' | 'contextmenu'