Skip to content

Drawer 抽屉

屏幕边缘滑出的浮层面板,用于展示详细信息或进行复杂操作。带有霓虹发光效果的赛博朋克主题抽屉组件。

基础用法

最简单的用法,通过 v-model 控制抽屉的显示和隐藏。

vue
<script setup>
import { ref } from 'vue'

const visible = ref(false)
</script>

<template>
  <ExButton type="primary" @click="visible = true">打开抽屉</ExButton>
  <ExDrawer v-model="visible" title="基础抽屉">
    <p>这是抽屉的内容</p>
    <p>可以放置任何内容</p>
  </ExDrawer>
</template>

不同方向

通过 placement 属性可以设置抽屉从不同方向滑出。

vue
<script setup>
import { ref } from 'vue'

const visibleLeft = ref(false)
const visibleRight = ref(false)
const visibleTop = ref(false)
const visibleBottom = ref(false)
</script>

<template>
  <ExButton @click="visibleLeft = true">从左侧打开</ExButton>
  <ExButton @click="visibleRight = true">从右侧打开</ExButton>
  <ExButton @click="visibleTop = true">从顶部打开</ExButton>
  <ExButton @click="visibleBottom = true">从底部打开</ExButton>

  <ExDrawer v-model="visibleLeft" title="左侧抽屉" placement="left">
    <p>从左侧滑出的抽屉</p>
  </ExDrawer>

  <ExDrawer v-model="visibleRight" title="右侧抽屉" placement="right">
    <p>从右侧滑出的抽屉</p>
  </ExDrawer>

  <ExDrawer v-model="visibleTop" title="顶部抽屉" placement="top">
    <p>从顶部滑出的抽屉</p>
  </ExDrawer>

  <ExDrawer v-model="visibleBottom" title="底部抽屉" placement="bottom">
    <p>从底部滑出的抽屉</p>
  </ExDrawer>
</template>

不同尺寸

通过 size 属性可以设置抽屉的尺寸,支持预设尺寸和自定义尺寸。

vue
<script setup>
import { ref } from 'vue'

const visibleSmall = ref(false)
const visibleMedium = ref(false)
const visibleLarge = ref(false)
const visibleCustom = ref(false)
</script>

<template>
  <ExButton @click="visibleSmall = true">小尺寸</ExButton>
  <ExButton @click="visibleMedium = true">中等尺寸</ExButton>
  <ExButton @click="visibleLarge = true">大尺寸</ExButton>
  <ExButton @click="visibleCustom = true">自定义尺寸</ExButton>

  <ExDrawer v-model="visibleSmall" title="小尺寸抽屉" size="small">
    <p>宽度 300px</p>
  </ExDrawer>

  <ExDrawer v-model="visibleMedium" title="中等尺寸抽屉" size="medium">
    <p>宽度 500px(默认)</p>
  </ExDrawer>

  <ExDrawer v-model="visibleLarge" title="大尺寸抽屉" size="large">
    <p>宽度 800px</p>
  </ExDrawer>

  <ExDrawer v-model="visibleCustom" title="自定义尺寸抽屉" :size="600">
    <p>自定义宽度 600px</p>
  </ExDrawer>
</template>

带底部操作栏

通过 show-footer 属性可以显示底部操作栏。

vue
<script setup>
import { ref } from 'vue'

const visible = ref(false)

const handleOk = () => {
  console.log('确认')
  visible.value = false
}

const handleCancel = () => {
  console.log('取消')
}
</script>

<template>
  <ExButton type="primary" @click="visible = true">打开抽屉</ExButton>
  <ExDrawer 
    v-model="visible" 
    title="带底部操作栏的抽屉"
    show-footer
    @ok="handleOk"
    @cancel="handleCancel"
  >
    <p>这是抽屉的内容</p>
  </ExDrawer>
</template>

无遮罩层

通过 mask 属性可以控制是否显示遮罩层。

vue
<script setup>
import { ref } from 'vue'

const visible = ref(false)
</script>

<template>
  <ExButton @click="visible = true">打开无遮罩抽屉</ExButton>
  <ExDrawer v-model="visible" title="无遮罩层抽屉" :mask="false">
    <p>没有遮罩层的抽屉</p>
  </ExDrawer>
</template>

禁止点击遮罩关闭

通过 mask-closable 属性可以控制点击遮罩层是否关闭抽屉。

vue
<script setup>
import { ref } from 'vue'

const visible = ref(false)
</script>

<template>
  <ExButton @click="visible = true">打开抽屉</ExButton>
  <ExDrawer v-model="visible" title="禁止点击遮罩关闭" :mask-closable="false">
    <p>点击遮罩层不会关闭抽屉</p>
  </ExDrawer>
</template>

销毁子元素

通过 destroy-on-close 属性可以在关闭时销毁子元素。

vue
<script setup>
import { ref } from 'vue'

const visible = ref(false)
</script>

<template>
  <ExButton @click="visible = true">打开抽屉</ExButton>
  <ExDrawer v-model="visible" title="销毁子元素" destroy-on-close>
    <p>关闭时会销毁这些内容</p>
    <p>{{ new Date().toLocaleTimeString() }}</p>
  </ExDrawer>
</template>

主题示例

抽屉应用。

vue
<script setup>
import { ref } from 'vue'

const visible = ref(false)
</script>

<template>
  <ExButton type="primary" @click="visible = true">角色设置</ExButton>

  <ExDrawer 
    v-model="visible" 
    title="🎮 角色设置"
    size="large"
    show-footer
  >
    <!-- 角色设置内容 -->
  </ExDrawer>
</template>

API

Drawer Props

参数说明类型默认值
v-model是否显示抽屉booleanfalse
title抽屉标题string
placement抽屉位置'left' | 'right' | 'top' | 'bottom''right'
size抽屉尺寸'small' | 'medium' | 'large' | string | number'medium'
closable是否显示关闭按钮booleantrue
mask是否显示遮罩层booleantrue
mask-closable点击遮罩层是否关闭booleantrue
destroy-on-close是否在关闭时销毁子元素booleanfalse
show-footer是否显示底部操作栏booleanfalse
ok-text确认按钮文字string'确定'
cancel-text取消按钮文字string'取消'
ok-type确认按钮类型'primary' | 'success' | 'warning' | 'danger' | 'info''primary'
ok-loading确认按钮是否加载中booleanfalse
ok-disabled是否禁用确认按钮booleanfalse
cancel-disabled是否禁用取消按钮booleanfalse
custom-class自定义类名string
custom-style自定义样式string | Record<string, string | number>
z-index层级number1000
lock-scroll是否锁定滚动booleantrue
before-open打开前的回调() => boolean | Promise<boolean>
before-close关闭前的回调() => boolean | Promise<boolean>
aria-label无障碍标签string

Drawer Events

事件名说明类型
update:model-value显示状态变化时触发(配合 v-model(value: boolean) => void
open打开时触发() => void
opened打开动画结束后触发() => void
close关闭时触发() => void
closed关闭动画结束后触发() => void
ok点击确认按钮时触发() => void
cancel点击取消按钮时触发() => void

Drawer Slots

插槽名说明
default抽屉内容
title自定义标题
header自定义头部
footer自定义底部

Drawer Methods

方法名说明类型
open打开抽屉() => void
close关闭抽屉() => void
getElement获取抽屉DOM元素() => HTMLDivElement | null

无障碍支持

  • 使用 role="dialog"aria-modal="true" 标记抽屉
  • 支持 ESC 键关闭
  • 完整的 ARIA 属性支持
  • 支持屏幕阅读器
  • 自动锁定和恢复页面滚动

受控 / 非受控

Drawer 通过 v-modelmodelValue)受控:

vue
<template>
  <ExButton @click="open = true">打开抽屉</ExButton>
  <ExDrawer v-model="open" title="详情" placement="right">内容</ExDrawer>
</template>

<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>
  • 拦截关闭before-close 返回 false 阻止关闭,且不会造成 modelValue 与内部状态脱钩(关闭请求统一经守卫处理)。
  • 拦截打开before-open 返回 false 阻止打开并回写 modelValue,适合打开前做权限/数据校验。

常见问题

Q:打开后键盘 Tab 会跑到背景内容吗? 不会。抽屉打开后自动聚焦首个可聚焦元素,Tab/Shift+Tab 锁定在抽屉内,关闭后焦点归还触发元素。

Q:before-close 返回 false 后抽屉再也关不掉? 不会(这是早期版本的缺陷,现已修复)。守卫仅在「请求关闭」时执行,modelValue 始终是唯一事实来源。

Q:移动端抽屉怎么做? 窄屏建议 placement="bottom" 并用百分比尺寸,如 size="60%",更贴合移动端交互习惯。

Q:如何做无遮罩的常驻侧栏? 设置 :mask="false";此时 aria-modal 也会相应变为非模态。

最佳实践

  • 详情查看 / 侧边表单优先用 Drawer,主区域上下文不被完全遮挡。
  • 尺寸用预设(small/medium/large)或百分比/像素自定义,避免在小屏溢出。
  • 底部操作栏show-footer + ok-loading 表达提交加载态。
  • 始终提供 titlearia-label 以满足无障碍。

主题定制

Drawer 不定义独立的组件级变量,而是消费 ExUI 全局设计令牌。定制方式:

  • 单个实例:用 custom-class / custom-style 定制。
  • 全局:覆盖所消费的全局令牌:
css
:root {
  --ex-color-bg-elevated: #11151c;    /* 抽屉背景 */
  --ex-color-border-primary: #1f2a37; /* 边框 */
  --ex-shadow-2xl: 0 20px 60px rgba(0, 0, 0, 0.6); /* 抽屉投影 */
}