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% 浏览器宽度。 你可以通过传入对应的 directionsize 属性来修改这一默认行为。 下面一个示例将展示如何使用 before-close API,更多详细用法请参考页面底部的 API 部分。

Drawer 抽屉 - 图1

  1. <template>
  2. <el-radio-group v-model="direction">
  3. <el-radio label="ltr">left to right</el-radio>
  4. <el-radio label="rtl">right to left</el-radio>
  5. <el-radio label="ttb">top to bottom</el-radio>
  6. <el-radio label="btt">bottom to top</el-radio>
  7. </el-radio-group>
  8. <el-button type="primary" style="margin-left: 16px" @click="drawer = true">
  9. open
  10. </el-button>
  11. <el-button type="primary" style="margin-left: 16px" @click="drawer2 = true">
  12. with footer
  13. </el-button>
  14. <el-drawer
  15. v-model="drawer"
  16. title="I am the title"
  17. :direction="direction"
  18. :before-close="handleClose"
  19. >
  20. <span>Hi, there!</span>
  21. </el-drawer>
  22. <el-drawer v-model="drawer2" :direction="direction">
  23. <template #title>
  24. <h4>set title by slot</h4>
  25. </template>
  26. <template #default>
  27. <div>
  28. <el-radio v-model="radio1" label="Option 1" size="large"
  29. >Option 1</el-radio
  30. >
  31. <el-radio v-model="radio1" label="Option 2" size="large"
  32. >Option 2</el-radio
  33. >
  34. </div>
  35. </template>
  36. <template #footer>
  37. <div style="flex: auto">
  38. <el-button @click="cancelClick">cancel</el-button>
  39. <el-button type="primary" @click="confirmClick">confirm</el-button>
  40. </div>
  41. </template>
  42. </el-drawer>
  43. </template>
  44. <script lang="ts" setup>
  45. import { ref } from 'vue'
  46. import { ElMessageBox } from 'element-plus'
  47. const drawer = ref(false)
  48. const drawer2 = ref(false)
  49. const direction = ref('rtl')
  50. const radio1 = ref('Option 1')
  51. const handleClose = (done: () => void) => {
  52. ElMessageBox.confirm('Are you sure you want to close this?')
  53. .then(() => {
  54. done()
  55. })
  56. .catch(() => {
  57. // catch error
  58. })
  59. }
  60. function cancelClick() {
  61. drawer2.value = false
  62. }
  63. function confirmClick() {
  64. ElMessageBox.confirm(`Are you confirm to chose ${radio1.value} ?`)
  65. .then(() => {
  66. drawer2.value = false
  67. })
  68. .catch(() => {
  69. // catch error
  70. })
  71. }
  72. </script>

不添加 Title

当你不需要标题的时候,你可以将它移除。

通过设置 with-header 属性为 false 来控制是否显示标题。 如果你的应用需要具备可访问性,请务必设置好 title

Drawer 抽屉 - 图2

  1. <template>
  2. <el-button type="primary" style="margin-left: 16px" @click="drawer = true">
  3. open
  4. </el-button>
  5. <el-drawer v-model="drawer" title="I am the title" :with-header="false">
  6. <span>Hi there!</span>
  7. </el-drawer>
  8. </template>
  9. <script lang="ts" setup>
  10. import { ref } from 'vue'
  11. const drawer = ref(false)
  12. </script>

自定义内容

Dialog 组件一样,Drawer 也可以用来显示多种不同的交互。

Drawer 抽屉 - 图3

  1. <template>
  2. <el-button text @click="table = true"
  3. >Open Drawer with nested table</el-button
  4. >
  5. <el-button text @click="dialog = true"
  6. >Open Drawer with nested form</el-button
  7. >
  8. <el-drawer
  9. v-model="table"
  10. title="I have a nested table inside!"
  11. direction="rtl"
  12. size="50%"
  13. >
  14. <el-table :data="gridData">
  15. <el-table-column property="date" label="Date" width="150" />
  16. <el-table-column property="name" label="Name" width="200" />
  17. <el-table-column property="address" label="Address" />
  18. </el-table>
  19. </el-drawer>
  20. <el-drawer
  21. ref="drawerRef"
  22. v-model="dialog"
  23. title="I have a nested form inside!"
  24. :before-close="handleClose"
  25. direction="ltr"
  26. custom-class="demo-drawer"
  27. >
  28. <div class="demo-drawer__content">
  29. <el-form :model="form">
  30. <el-form-item label="Name" :label-width="formLabelWidth">
  31. <el-input v-model="form.name" autocomplete="off" />
  32. </el-form-item>
  33. <el-form-item label="Area" :label-width="formLabelWidth">
  34. <el-select
  35. v-model="form.region"
  36. placeholder="Please select activity area"
  37. >
  38. <el-option label="Area1" value="shanghai" />
  39. <el-option label="Area2" value="beijing" />
  40. </el-select>
  41. </el-form-item>
  42. </el-form>
  43. <div class="demo-drawer__footer">
  44. <el-button @click="cancelForm">Cancel</el-button>
  45. <el-button type="primary" :loading="loading" @click="onClick">{{
  46. loading ? 'Submitting ...' : 'Submit'
  47. }}</el-button>
  48. </div>
  49. </div>
  50. </el-drawer>
  51. </template>
  52. <script lang="ts" setup>
  53. import { reactive, ref } from 'vue'
  54. import { ElDrawer, ElMessageBox } from 'element-plus'
  55. const formLabelWidth = '80px'
  56. let timer
  57. const table = ref(false)
  58. const dialog = ref(false)
  59. const loading = ref(false)
  60. const form = reactive({
  61. name: '',
  62. region: '',
  63. date1: '',
  64. date2: '',
  65. delivery: false,
  66. type: [],
  67. resource: '',
  68. desc: '',
  69. })
  70. const gridData = [
  71. {
  72. date: '2016-05-02',
  73. name: 'Peter Parker',
  74. address: 'Queens, New York City',
  75. },
  76. {
  77. date: '2016-05-04',
  78. name: 'Peter Parker',
  79. address: 'Queens, New York City',
  80. },
  81. {
  82. date: '2016-05-01',
  83. name: 'Peter Parker',
  84. address: 'Queens, New York City',
  85. },
  86. {
  87. date: '2016-05-03',
  88. name: 'Peter Parker',
  89. address: 'Queens, New York City',
  90. },
  91. ]
  92. const drawerRef = ref<InstanceType<typeof ElDrawer>>()
  93. const onClick = () => {
  94. drawerRef.value!.close()
  95. }
  96. const handleClose = (done) => {
  97. if (loading.value) {
  98. return
  99. }
  100. ElMessageBox.confirm('Do you want to submit?')
  101. .then(() => {
  102. loading.value = true
  103. timer = setTimeout(() => {
  104. done()
  105. // 动画关闭需要一定的时间
  106. setTimeout(() => {
  107. loading.value = false
  108. }, 400)
  109. }, 2000)
  110. })
  111. .catch(() => {
  112. // catch error
  113. })
  114. }
  115. const cancelForm = () => {
  116. loading.value = false
  117. dialog.value = false
  118. clearTimeout(timer)
  119. }
  120. </script>

自定义头部

header 可用于自定义显示标题的区域。 为了保持可用性,除了使用此插槽外,使用 title 属性,或使用 titleId 插槽属性来指定哪些元素应该读取为抽屉标题。

Drawer 抽屉 - 图4

  1. <template>
  2. <el-button @click="visible = true">
  3. Open Drawer with customized header
  4. </el-button>
  5. <el-drawer v-model="visible" :show-close="false">
  6. <template #header="{ close, titleId, titleClass }">
  7. <h4 :id="titleId" :class="titleClass">This is a custom header!</h4>
  8. <el-button type="danger" @click="close">
  9. <el-icon class="el-icon--left"><CircleCloseFilled /></el-icon>
  10. Close
  11. </el-button>
  12. </template>
  13. This is drawer content.
  14. </el-drawer>
  15. </template>
  16. <script lang="ts" setup>
  17. import { ref } from 'vue'
  18. import { ElButton, ElDrawer } from 'element-plus'
  19. import { CircleCloseFilled } from '@element-plus/icons-vue'
  20. const visible = ref(false)
  21. </script>

嵌套抽屉

Drawer 组件也拥有多层嵌套的方法

需要设置 model-value 属性,它的类型boolean,当为 true 时显示 Drawer。

Drawer 抽屉 - 图5

  1. <template>
  2. <el-button type="primary" style="margin-left: 16px" @click="drawer = true">
  3. open
  4. </el-button>
  5. <el-drawer v-model="drawer" title="I'm outer Drawer" size="50%">
  6. <div>
  7. <el-button @click="innerDrawer = true">Click me!</el-button>
  8. <el-drawer
  9. v-model="innerDrawer"
  10. title="I'm inner Drawer"
  11. :append-to-body="true"
  12. :before-close="handleClose"
  13. >
  14. <p>_(:зゝ∠)_</p>
  15. </el-drawer>
  16. </div>
  17. </el-drawer>
  18. </template>
  19. <script lang="ts" setup>
  20. import { ref } from 'vue'
  21. import { ElMessageBox } from 'element-plus'
  22. const drawer = ref(false)
  23. const innerDrawer = ref(false)
  24. const handleClose = (done: () => void) => {
  25. ElMessageBox.confirm('You still have unsaved data, proceed?')
  26. .then(() => {
  27. done()
  28. })
  29. .catch(() => {
  30. // catch error
  31. })
  32. }
  33. </script>

TIP

Drawer 的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot 不会被渲染到 DOM 上。 因此,如果需要执行 DOM 操作,或通过 ref 获取相应组件,请在 open 事件回调中进行。

TIP

Drawer 还提供一个 destroy-on-close 的 API,用来控制是否在 Drawer 隐藏之后把 Drawer 的默认插槽内的内容销毁。 当你需要你的 挂载的 生命周期每次打开时你可以使用此 API。

Drawer 属性

属性说明类型可选值默认值
model-value / v-model是否显示 Drawerbooleanfalse
append-to-bodyDrawer 自身是否插入至 body 元素上。嵌套的 Drawer 必须指定该属性并赋值为 truebooleanfalse
lock-scroll是否在 Drawer 出现时将 body 滚动锁定booleantrue
before-close关闭前的回调,会暂停 Drawer 的关闭function(done),done 用于关闭 Drawer
close-on-click-modal是否可以通过点击 modal 关闭 Drawerbooleantrue
close-on-press-escape是否可以通过按下 ESC 关闭 Drawerbooleantrue
open-delayDrawer 打开的延时时间,单位毫秒number0
close-delayDrawer 关闭的延时时间,单位毫秒number0
custom-classDrawer 的自定义类名string
destroy-on-close控制是否在关闭 Drawer 之后将子元素全部销毁boolean-false
modal是否需要遮罩层booleantrue
directionDrawer 打开的方向Directionrtl / ltr / ttb / bttrtl
show-close是否显示关闭按钮booleantrue
sizeDrawer 窗体的大小, 当使用 number 类型时, 以像素为单位, 当使用 string 类型时, 请传入 ‘x%’, 否则便会以 number 类型解释number / string-‘30%’
titleDrawer 的标题,也可通过具名 slot (见下表)传入string
with-header控制是否显示 header 栏, 默认为 true, 当此项为 false 时, title attribute 和 title slot 均不生效boolean-true
modal-class遮罩层的自定义类名string--
z-index设置 z-indexnumber--

Drawer 插槽

插槽名说明
Drawer 的内容
headerDrawer 标题的内容;会替换标题部分,但不会移除关闭按钮。
title(已废弃)与 header 作用相同 请使用 header
footerDrawer 页脚部分

Drawer 方法

名称说明
handleClose用于关闭 Drawer, 该方法会调用传入的 before-close 方法

Drawer 事件

事件名称说明参数
openDrawer 打开的回调
openedDrawer 打开动画结束时的回调
closeDrawer 关闭的回调
closedDrawer 关闭动画结束时的回调

源代码

组件 Drawer 抽屉 - 图6 文档 Drawer 抽屉 - 图7

贡献者

Drawer 抽屉 - 图8 JeremyWuuuuu

Drawer 抽屉 - 图9 三咲智子

Drawer 抽屉 - 图10 云游君

Drawer 抽屉 - 图11 opengraphica

Drawer 抽屉 - 图12 Alan Wang

Drawer 抽屉 - 图13 C.Y.Kun

Drawer 抽屉 - 图14 bqy_fe

Drawer 抽屉 - 图15 류한경

Drawer 抽屉 - 图16 Delyan Haralanov

Drawer 抽屉 - 图17 btea

Drawer 抽屉 - 图18 Aex

Drawer 抽屉 - 图19 msidolphin

Drawer 抽屉 - 图20 on the field of hope

Drawer 抽屉 - 图21 zazzaz

Drawer 抽屉 - 图22 Hades-li

Drawer 抽屉 - 图23 renovate[bot]