Skip to content

抽屉 Drawer

赞助

屏幕边缘滑出的浮层面板

何时使用

抽屉从父窗体边缘滑入,覆盖住部分父窗体内容。用户在抽屉内操作时不必离开当前任务,操作完成后,可以平滑地回到原任务。

  • 当需要一个附加的面板来控制父窗体内容,这个面板在需要时呼出。比如,控制界面展示样式,往界面中添加内容
  • 当需要在当前任务流中插入临时任务,创建或预览附加内容。比如展示协议条款,创建子对象

基本用法

基础抽屉,点击触发按钮抽屉从右滑出,点击遮罩区关闭


Open
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
const open = ref<boolean>(false)
function afterOpenChange(val: boolean) {
  console.log('open', val)
}
function onClose() {
  open.value = false
}
</script>
<template>
  <Button type="primary" @click="open = true">Open</Button>
  <Drawer
    v-model:open="open"
    class="custom-class"
    root-class-name="root-class-name"
    :root-style="{ color: 'blue' }"
    :content-wrapper-style="{ color: 'red' }"
    title="Basic Drawer"
    placement="right"
    @after-open-change="afterOpenChange"
    @close="onClose"
  >
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </Drawer>
</template>

自定义位置

自定义位置,点击触发按钮抽屉从相应的位置滑出,点击遮罩区关闭


top
right
bottom
left
Open
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
import type { RadioOption } from 'vue-amazing-ui'
const open = ref<boolean>(false)
const options = ref<RadioOption[]>([
  { label: 'top', value: 'top' },
  { label: 'right', value: 'right' },
  { label: 'bottom', value: 'bottom' },
  { label: 'left', value: 'left' }
])
const placement = ref('right')
function onClose() {
  open.value = false
}
</script>
<template>
  <Radio v-model:value="placement" :options="options" style="margin-right: 8px" />
  <Button type="primary" @click="open = true">Open</Button>
  <Drawer v-model:open="open" title="Basic Drawer" :closable="false" :placement="placement" @close="onClose">
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </Drawer>
</template>

额外操作

操作按钮建议放在抽屉的右上角,可以使用 extra 属性来实现


top
right
bottom
left
Open
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
import type { RadioOption } from 'vue-amazing-ui'
const open = ref<boolean>(false)
const options = ref<RadioOption[]>([
  { label: 'top', value: 'top' },
  { label: 'right', value: 'right' },
  { label: 'bottom', value: 'bottom' },
  { label: 'left', value: 'left' }
])
const placement = ref('right')
function onClose() {
  open.value = false
}
</script>
<template>
  <Radio v-model:value="placement" :options="options" style="margin-right: 8px" />
  <Button type="primary" @click="open = true">Open</Button>
  <Drawer v-model:open="open" :width="500" title="Basic Drawer" :placement="placement" @close="onClose">
    <template #extra>
      <Button style="margin-right: 8px" @click="onClose">Cancel</Button>
      <Button type="primary" @click="onClose">Submit</Button>
    </template>
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </Drawer>
</template>

渲染在当前 DOM

渲染在当前 DOM 里。自定义容器,查看 to


Render in this
Open
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
const open = ref(false)
function onClose() {
  open.value = false
}
</script>
<template>
  <div
    :style="{
      height: '200px',
      overflow: 'hidden',
      position: 'relative',
      border: '1px solid #ebedf0',
      borderRadius: '2px',
      padding: '48px',
      textAlign: 'center',
      background: '#fafafa'
    }"
  >
    Render in this
    <div style="margin-top: 16px">
      <Button type="primary" @click="open = true">Open</Button>
    </div>
    <Drawer
      v-model:open="open"
      title="Basic Drawer"
      placement="right"
      :closable="false"
      :to="false"
      :style="{ position: 'absolute' }"
      @close="onClose"
    >
      <p>Some contents...</p>
    </Drawer>
  </div>
</template>

抽屉表单

在抽屉中使用表单


New account
Show Code
vue
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { SelectOption } from 'vue-amazing-ui'
import { PlusOutlined } from '@ant-design/icons-vue'
const open = ref<boolean>(false)
const form = reactive({
  name: '',
  url: '',
  owner: undefined as string | undefined,
  type: undefined as string | undefined,
  approver: undefined as string | undefined,
  dateTime: '',
  description: ''
})
const ownerOptions = ref<SelectOption[]>([
  { label: 'Xiaoxiao Fu', value: 'xiao' },
  { label: 'Maomao Zhou', value: 'mao' }
])
const typeOptions = ref<SelectOption[]>([
  { label: 'Private', value: 'private' },
  { label: 'Public', value: 'public' }
])
const approverOptions = ref<SelectOption[]>([
  { label: 'Jack Ma', value: 'jack' },
  { label: 'Tom Liu', value: 'tom' }
])
function onClose() {
  open.value = false
}
</script>
<template>
  <Button type="primary" @click="open = true">
    <template #icon><PlusOutlined /></template>
    New account
  </Button>
  <Drawer
    v-model:open="open"
    title="Create a new account"
    :width="720"
    :body-style="{ paddingBottom: '80px' }"
    :footer-style="{ textAlign: 'right' }"
    @close="onClose"
  >
    <Row :gutter="16">
      <Col :span="12">
        <div class="form-item">
          <span class="form-label">Name</span>
          <Input v-model:value="form.name" placeholder="Please enter user name" />
        </div>
      </Col>
      <Col :span="12">
        <div class="form-item">
          <span class="form-label">Url</span>
          <Input v-model:value="form.url" addon-before="http://" addon-after=".com" placeholder="please enter url" />
        </div>
      </Col>
    </Row>
    <Row :gutter="16">
      <Col :span="12">
        <div class="form-item">
          <span class="form-label">Owner</span>
          <Select
            v-model="form.owner"
            :options="ownerOptions"
            placeholder="Please select an owner"
            style="width: 100%"
          />
        </div>
      </Col>
      <Col :span="12">
        <div class="form-item">
          <span class="form-label">Type</span>
          <Select
            v-model="form.type"
            :options="typeOptions"
            placeholder="Please choose the type"
            style="width: 100%"
          />
        </div>
      </Col>
    </Row>
    <Row :gutter="16">
      <Col :span="12">
        <div class="form-item">
          <span class="form-label">Approver</span>
          <Select
            v-model="form.approver"
            :options="approverOptions"
            placeholder="Please choose the approver"
            style="width: 100%"
          />
        </div>
      </Col>
      <Col :span="12">
        <div class="form-item">
          <span class="form-label">DateTime</span>
          <DatePicker v-model="form.dateTime" placeholder="Please choose the dateTime" style="width: 100%" />
        </div>
      </Col>
    </Row>
    <Row :gutter="16">
      <Col :span="24">
        <div class="form-item">
          <span class="form-label">Description</span>
          <Textarea v-model:value="form.description" :rows="4" placeholder="please enter url description" />
        </div>
      </Col>
    </Row>
    <template #extra>
      <Space>
        <Button @click="onClose">Cancel</Button>
        <Button type="primary" @click="onClose">Submit</Button>
      </Space>
    </template>
  </Drawer>
</template>

多层抽屉

在抽屉内打开新的抽屉,用以解决多分支任务的复杂状况


Open
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
const open = ref<boolean>(false)
const childrenDrawer = ref<boolean>(false)
function onClose() {
  open.value = false
}
</script>
<template>
  <Button type="primary" @click="open = true">Open</Button>
  <Drawer
    v-model:open="open"
    title="Multi-level drawer"
    width="520"
    :closable="false"
    :footer-style="{ textAlign: 'right' }"
    @close="onClose"
  >
    <Button type="primary" @click="childrenDrawer = true">Two-level drawer</Button>
    <Drawer v-model:open="childrenDrawer" title="Two-level Drawer" width="320" :closable="false">
      <p>This is two-level drawer</p>
    </Drawer>
    <template #footer>
      <Button style="margin-right: 8px" @click="onClose">Cancel</Button>
      <Button type="primary" @click="onClose">Submit</Button>
    </template>
  </Drawer>
</template>

信息预览抽屉

需要快速预览对象概要时使用,点击遮罩区关闭


Lily

Progresser XTech

Lily

Progresser XTech
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
const open = ref<boolean>(false)
// 列表数据为两条同名 Lily
const profileList = ['Lily', 'Lily']
function onClose() {
  open.value = false
}
</script>
<template>
  <List bordered>
    <ListItem v-for="(name, index) in profileList" :key="index" :title="name" description="Progresser XTech">
      <template #avatar>
        <Avatar src="https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.1.2/1.jpg" />
      </template>
      <template #actions>
        <a href="#" @click.prevent="open = true">View Profile</a>
      </template>
    </ListItem>
  </List>
  <Drawer v-model:open="open" :width="640" placement="right" :closable="false" @close="onClose">
    <p class="profile-title">User Profile</p>
    <p class="profile-label">Personal</p>
    <Descriptions :column="2">
      <DescriptionsItem label="Full Name">Lily</DescriptionsItem>
      <DescriptionsItem label="Account">themusecatcher</DescriptionsItem>
      <DescriptionsItem label="City">HangZhou</DescriptionsItem>
      <DescriptionsItem label="Country">China🇨🇳</DescriptionsItem>
      <DescriptionsItem label="Birthday">February 2,1900</DescriptionsItem>
      <DescriptionsItem label="Website">-</DescriptionsItem>
      <DescriptionsItem label="Message">Make things as simple as possible but no simpler.</DescriptionsItem>
    </Descriptions>
    <Divider />
    <p class="profile-label">Company</p>
    <Descriptions :column="2">
      <DescriptionsItem label="Position">Programmer</DescriptionsItem>
      <DescriptionsItem label="Responsibilities">Coding</DescriptionsItem>
      <DescriptionsItem label="Department">XTech</DescriptionsItem>
      <DescriptionsItem label="Supervisor">
        <a>Lin</a>
      </DescriptionsItem>
      <DescriptionsItem label="Skills">
        C / C + +, data structures, software engineering, operating systems, computer networks, databases,
        compiler theory, computer architecture, Microcomputer Principle and Interface Technology, Computer English,
        Java, ASP, etc.
      </DescriptionsItem>
    </Descriptions>
    <Divider />
    <p class="profile-label">Contacts</p>
    <Descriptions :column="2">
      <DescriptionsItem label="Email">themusecatcher@163.com</DescriptionsItem>
      <DescriptionsItem label="Phone Number">+86 181 0000 0000</DescriptionsItem>
      <DescriptionsItem label="Github">
        <a href="https://github.com/themusecatcher/vue-amazing-ui">
          github.com/themusecatcher/vue-amazing-ui
        </a>
      </DescriptionsItem>
    </Descriptions>
  </Drawer>
</template>

预设宽度

抽屉的默认宽度为 378px,另外还提供一个大号抽屉 736px,可以用 size 属性来设置


Open Default Size (378px)
Open Large Size (736px)
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
import type { DrawerProps } from 'vue-amazing-ui'
const open = ref<boolean>(false)
const size = ref<DrawerProps['size']>('default')
function showDrawer(val: DrawerProps['size']) {
  size.value = val
  open.value = true
}
function onClose() {
  open.value = false
}
</script>
<template>
  <Button type="primary" style="margin-right: 8px" @click="showDrawer('default')">Open Default Size (378px)</Button>
  <Button type="primary" @click="showDrawer('large')">Open Large Size (736px)</Button>
  <Drawer v-model:open="open" title="Basic Drawer" :size="size" @close="onClose">
    <template #extra>
      <Button style="margin-right: 8px" @click="onClose">Cancel</Button>
      <Button type="primary" @click="onClose">Submit</Button>
    </template>
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </Drawer>
</template>

抽屉页脚

top
right
bottom
left
Open
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
import type { RadioOption } from 'vue-amazing-ui'
const open = ref<boolean>(false)
const options = ref<RadioOption[]>([
  { label: 'top', value: 'top' },
  { label: 'right', value: 'right' },
  { label: 'bottom', value: 'bottom' },
  { label: 'left', value: 'left' }
])
const placement = ref('right')
function onClose() {
  open.value = false
}
</script>
<template>
  <Radio v-model:value="placement" :options="options" style="margin-right: 8px" />
  <Button type="primary" @click="open = true">Open</Button>
  <Drawer
    v-model:open="open"
    title="Basic Drawer"
    :placement="placement"
    :footer-style="{ textAlign: 'right' }"
    @close="onClose"
  >
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
    <template #footer>
      <Button style="margin-right: 8px" @click="onClose">Cancel</Button>
      <Button type="primary" @click="onClose">Submit</Button>
    </template>
  </Drawer>
</template>

自定义 header & body 样式

通过 header-stylebody-style 自定义抽屉头部与内容区域样式,并设置背景色便于观察


Open
Show Code
vue
<script lang="ts" setup>
import { ref } from 'vue'
const open = ref<boolean>(false)
</script>
<template>
  <Button type="primary" @click="open = true">Open</Button>
  <Drawer
    v-model:open="open"
    :closable="false"
    title="Basic Drawer"
    :header-style="{ textAlign: 'center' }"
    :body-style="{ textAlign: 'center' }"
  >
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </Drawer>
</template>

APIs

Drawer

参数说明类型默认值
width抽屉宽度,在 placementrightleft 时使用,单位 px;未设置时由 size 推导(default378pxlarge736pxstring | numberundefined
height抽屉高度,在 placementtopbottom 时使用,单位 px;未设置时由 size 推导(default378pxlarge736pxstring | numberundefined
size预设抽屉宽度(或高度),default378pxlarge736px'default' | 'large''default'
title标题stringundefined
closable是否显示左上角的关闭按钮booleantrue
closeIcon自定义关闭图标,插槽形态请用 #closeIconVNode | (() => VNode)undefined
placement抽屉的方向'top' | 'right' | 'bottom' | 'left''right'
headerClass设置 Drawer 头部的类名stringundefined
headerStyle设置 Drawer 头部的样式CSSProperties{}
bodyClass设置 Drawer 内容部分的类名stringundefined
bodyStyle设置 Drawer 内容部分的样式CSSProperties{}
footer抽屉的页脚stringundefined
footerClass设置 Drawer 页脚的类名stringundefined
footerStyle设置 Drawer 页脚的样式CSSProperties{}
extra抽屉右上角的操作区域stringundefined
scrollbarPropsScrollbar 组件属性配置,参考 Scrollbar Props,用于设置内容滚动条的样式ScrollbarProps{}
destroyOnClose关闭时是否销毁 Drawer 里的子元素booleanfalse
forceRender预渲染 Drawer 内元素booleanfalse
contentWrapperStyle设置 Drawer 包裹内容部分的样式CSSProperties{}
rootClassName最外层容器的类名stringundefined
rootStyle最外层容器的样式CSSProperties{}
toDrawer 挂载的节点,可选:元素标签名(如 'body')、元素本身或 false(渲染在当前 DOMstring | HTMLElement | false'body'
zIndex设置 Drawerz-indexnumber1000
open
v-model
抽屉是否可见booleanfalse
autofocus抽屉展开后是否将焦点切换至其 DOM 节点booleantrue
keyboard是否支持键盘 esc 关闭booleantrue
mask是否展示遮罩booleantrue
maskClosable点击蒙层是否允许关闭booleantrue
maskStyle遮罩样式CSSProperties{}
blockScroll是否在打开时禁用 body 滚动booleantrue
push用于设置多层 Drawer 的推动行为boolean | { distance: string | number }{ distance: 180 }

组件上的 class / style 透传到最外层容器 .drawer-wrap(等价于 rootClassName / rootStyle)。

Events

名称说明类型
close点击遮罩层或左上角叉或取消按钮的回调(e: Event) => void
afterOpenChange切换抽屉时动画结束后的回调(open: boolean) => void

Slots

名称说明类型
title自定义标题v-slot:title
extra自定义抽屉右上角的操作区域v-slot:extra
default自定义抽屉内容v-slot:default
footer自定义抽屉的页脚v-slot:footer
closeIcon自定义关闭图标v-slot:closeIcon

Released under the MIT License.