Drawer 抽屉
有些时候, Dialog
组件并不满足我们的需求, 比如你的表单很长, 亦或是你需要临时展示一些文档, Drawer
拥有和 Dialog
几乎相同的 API, 在 UI 上带来不一样的体验.
TIP
在 Vue 3 之后的版本 v-model 可以用于任何一个组件,visible.sync
已被移除,请使用 v-model="visibilityBinding"
来控制抽屉组件的显示和隐藏状态。
基础用法
呼出一个临时的侧边栏,支持上下左右四个方向。
你必须像 Dialog
一样为 Drawer
设置 model-value
属性来控制 Drawer
的显示与隐藏状态,该属性接受一个 boolean
类型。 Drawer
包含三部分: title
& body
& footer
, 其中 title
是一个具名 slot, 你还可以通过 title
属性来设置标题, 默认情况下它是一个空字符串, 其中 body
部分是 Drawer
组件的主区域, 它包含了用户定义的主要内容. footer和title用法一致, 用来显示页脚信息. 当 Drawer
打开时,默认设置是从右至左打开 30% 浏览器宽度。 你可以通过传入对应的 direction
和 size
属性来修改这一默认行为。 下面一个示例将展示如何使用 before-close
API,更多详细用法请参考页面底部的 API 部分。
<template>
<el-radio-group v-model="direction">
<el-radio label="ltr">left to right</el-radio>
<el-radio label="rtl">right to left</el-radio>
<el-radio label="ttb">top to bottom</el-radio>
<el-radio label="btt">bottom to top</el-radio>
</el-radio-group>
<el-button type="primary" style="margin-left: 16px" @click="drawer = true">
open
</el-button>
<el-button type="primary" style="margin-left: 16px" @click="drawer2 = true">
with footer
</el-button>
<el-drawer
v-model="drawer"
title="I am the title"
:direction="direction"
:before-close="handleClose"
>
<span>Hi, there!</span>
</el-drawer>
<el-drawer v-model="drawer2" :direction="direction">
<template #title>
<h4>set title by slot</h4>
</template>
<template #default>
<div>
<el-radio v-model="radio1" label="Option 1" size="large"
>Option 1</el-radio
>
<el-radio v-model="radio1" label="Option 2" size="large"
>Option 2</el-radio
>
</div>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancelClick">cancel</el-button>
<el-button type="primary" @click="confirmClick">confirm</el-button>
</div>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'
const drawer = ref(false)
const drawer2 = ref(false)
const direction = ref('rtl')
const radio1 = ref('Option 1')
const handleClose = (done: () => void) => {
ElMessageBox.confirm('Are you sure you want to close this?')
.then(() => {
done()
})
.catch(() => {
// catch error
})
}
function cancelClick() {
drawer2.value = false
}
function confirmClick() {
ElMessageBox.confirm(`Are you confirm to chose ${radio1.value} ?`)
.then(() => {
drawer2.value = false
})
.catch(() => {
// catch error
})
}
</script>
不添加 Title
当你不需要标题的时候,你可以将它移除。
通过设置 with-header
属性为 false 来控制是否显示标题。 如果你的应用需要具备可访问性,请务必设置好 title
。
<template>
<el-button type="primary" style="margin-left: 16px" @click="drawer = true">
open
</el-button>
<el-drawer v-model="drawer" title="I am the title" :with-header="false">
<span>Hi there!</span>
</el-drawer>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const drawer = ref(false)
</script>
自定义内容
像 Dialog
组件一样,Drawer
也可以用来显示多种不同的交互。
<template>
<el-button text @click="table = true"
>Open Drawer with nested table</el-button
>
<el-button text @click="dialog = true"
>Open Drawer with nested form</el-button
>
<el-drawer
v-model="table"
title="I have a nested table inside!"
direction="rtl"
size="50%"
>
<el-table :data="gridData">
<el-table-column property="date" label="Date" width="150" />
<el-table-column property="name" label="Name" width="200" />
<el-table-column property="address" label="Address" />
</el-table>
</el-drawer>
<el-drawer
ref="drawerRef"
v-model="dialog"
title="I have a nested form inside!"
:before-close="handleClose"
direction="ltr"
custom-class="demo-drawer"
>
<div class="demo-drawer__content">
<el-form :model="form">
<el-form-item label="Name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="Area" :label-width="formLabelWidth">
<el-select
v-model="form.region"
placeholder="Please select activity area"
>
<el-option label="Area1" value="shanghai" />
<el-option label="Area2" value="beijing" />
</el-select>
</el-form-item>
</el-form>
<div class="demo-drawer__footer">
<el-button @click="cancelForm">Cancel</el-button>
<el-button type="primary" :loading="loading" @click="onClick">{{
loading ? 'Submitting ...' : 'Submit'
}}</el-button>
</div>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { ElDrawer, ElMessageBox } from 'element-plus'
const formLabelWidth = '80px'
let timer
const table = ref(false)
const dialog = ref(false)
const loading = ref(false)
const form = reactive({
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: '',
})
const gridData = [
{
date: '2016-05-02',
name: 'Peter Parker',
address: 'Queens, New York City',
},
{
date: '2016-05-04',
name: 'Peter Parker',
address: 'Queens, New York City',
},
{
date: '2016-05-01',
name: 'Peter Parker',
address: 'Queens, New York City',
},
{
date: '2016-05-03',
name: 'Peter Parker',
address: 'Queens, New York City',
},
]
const drawerRef = ref<InstanceType<typeof ElDrawer>>()
const onClick = () => {
drawerRef.value!.close()
}
const handleClose = (done) => {
if (loading.value) {
return
}
ElMessageBox.confirm('Do you want to submit?')
.then(() => {
loading.value = true
timer = setTimeout(() => {
done()
// 动画关闭需要一定的时间
setTimeout(() => {
loading.value = false
}, 400)
}, 2000)
})
.catch(() => {
// catch error
})
}
const cancelForm = () => {
loading.value = false
dialog.value = false
clearTimeout(timer)
}
</script>
自定义头部
header
可用于自定义显示标题的区域。 为了保持可用性,除了使用此插槽外,使用 title
属性,或使用 titleId
插槽属性来指定哪些元素应该读取为抽屉标题。
<template>
<el-button @click="visible = true">
Open Drawer with customized header
</el-button>
<el-drawer v-model="visible" :show-close="false">
<template #header="{ close, titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">This is a custom header!</h4>
<el-button type="danger" @click="close">
<el-icon class="el-icon--left"><CircleCloseFilled /></el-icon>
Close
</el-button>
</template>
This is drawer content.
</el-drawer>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ElButton, ElDrawer } from 'element-plus'
import { CircleCloseFilled } from '@element-plus/icons-vue'
const visible = ref(false)
</script>
嵌套抽屉
Drawer
组件也拥有多层嵌套的方法
需要设置 model-value
属性,它的类型是 boolean
,当为 true 时显示 Drawer。
<template>
<el-button type="primary" style="margin-left: 16px" @click="drawer = true">
open
</el-button>
<el-drawer v-model="drawer" title="I'm outer Drawer" size="50%">
<div>
<el-button @click="innerDrawer = true">Click me!</el-button>
<el-drawer
v-model="innerDrawer"
title="I'm inner Drawer"
:append-to-body="true"
:before-close="handleClose"
>
<p>_(:зゝ∠)_</p>
</el-drawer>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'
const drawer = ref(false)
const innerDrawer = ref(false)
const handleClose = (done: () => void) => {
ElMessageBox.confirm('You still have unsaved data, proceed?')
.then(() => {
done()
})
.catch(() => {
// catch error
})
}
</script>
TIP
Drawer 的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot 不会被渲染到 DOM 上。 因此,如果需要执行 DOM 操作,或通过 ref
获取相应组件,请在 open
事件回调中进行。
TIP
Drawer 还提供一个 destroy-on-close
的 API,用来控制是否在 Drawer 隐藏之后把 Drawer 的默认插槽内的内容销毁。 当你需要你的 挂载的
生命周期每次打开时你可以使用此 API。
Drawer 属性
属性 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
model-value / v-model | 是否显示 Drawer | boolean | — | false |
append-to-body | Drawer 自身是否插入至 body 元素上。嵌套的 Drawer 必须指定该属性并赋值为 true | boolean | — | false |
lock-scroll | 是否在 Drawer 出现时将 body 滚动锁定 | boolean | — | true |
before-close | 关闭前的回调,会暂停 Drawer 的关闭 | function(done),done 用于关闭 Drawer | — | — |
close-on-click-modal | 是否可以通过点击 modal 关闭 Drawer | boolean | — | true |
close-on-press-escape | 是否可以通过按下 ESC 关闭 Drawer | boolean | — | true |
open-delay | Drawer 打开的延时时间,单位毫秒 | number | — | 0 |
close-delay | Drawer 关闭的延时时间,单位毫秒 | number | — | 0 |
custom-class | Drawer 的自定义类名 | string | — | — |
destroy-on-close | 控制是否在关闭 Drawer 之后将子元素全部销毁 | boolean | - | false |
modal | 是否需要遮罩层 | boolean | — | true |
direction | Drawer 打开的方向 | Direction | rtl / ltr / ttb / btt | rtl |
show-close | 是否显示关闭按钮 | boolean | — | true |
size | Drawer 窗体的大小, 当使用 number 类型时, 以像素为单位, 当使用 string 类型时, 请传入 ‘x%’, 否则便会以 number 类型解释 | number / string | - | ‘30%’ |
title | Drawer 的标题,也可通过具名 slot (见下表)传入 | string | — | — |
with-header | 控制是否显示 header 栏, 默认为 true, 当此项为 false 时, title attribute 和 title slot 均不生效 | boolean | - | true |
modal-class | 遮罩层的自定义类名 | string | - | - |
z-index | 设置 z-index | number | - | - |
Drawer 插槽
插槽名 | 说明 |
---|---|
— | Drawer 的内容 |
header | Drawer 标题的内容;会替换标题部分,但不会移除关闭按钮。 |
title(已废弃) | 与 header 作用相同 请使用 header |
footer | Drawer 页脚部分 |
Drawer 方法
名称 | 说明 |
---|---|
handleClose | 用于关闭 Drawer, 该方法会调用传入的 before-close 方法 |
Drawer 事件
事件名称 | 说明 | 参数 |
---|---|---|
open | Drawer 打开的回调 | — |
opened | Drawer 打开动画结束时的回调 | — |
close | Drawer 关闭的回调 | — |
closed | Drawer 关闭动画结束时的回调 | — |