管理后台的主题设置面板通常使用 Element Plus 的 el-drawer 抽屉组件承载,其默认宽度为百分比(如 30%)。这在桌面端表现良好,但当屏幕宽度较小时,内部控件(如 Slider、RadioGroup)会变得难以操作。本节从 PC 端最小宽度限制和移动端全屏适配两个角度,介绍 Drawer 组件的响应式样式优化方案。
问题分析
主题设置抽屉在不同屏幕宽度下面临两个核心问题:
- PC 端:当用户将浏览器窗口拖拽到较窄(如 1100px 以下)时,30% 的宽度只有约 330px,内部组件空间不足
- 移动端:默认的 30% 宽度在大屏手机上也十分局促,旁边的遮罩层占据大量无用空间
方案一:PC 端设置最小宽度
Element Plus 的 Drawer 组件提供了 custom-class 属性(虽然文档标注为 deprecated,但目前仍可使用),也可以通过给 Drawer 外层容器添加 class 来设置最小宽度。
使用 custom-class
<!-- components/ThemeSettings.vue -->
<template>
<el-drawer
v-model="visible"
title="主题设置"
direction="rtl"
custom-class="theme-drawer"
:size="'30%'"
>
<!-- 主题设置内容 -->
</el-drawer>
</template>
<style scoped>
:deep(.theme-drawer) {
min-width: 330px;
}
</style>
vue
当 custom-class 设置后,对应的 class 会加到 Drawer 弹出的外层容器上,通过 CSS 的 min-width 即可限制最小宽度。拖拽浏览器窗口到 1100px 以下时,Drawer 宽度将保持在 330px 不再缩小。
使用 class 属性(推荐)
如果 custom-class 后续版本不可用,可以直接在 el-drawer 上使用 class 属性:
<el-drawer
v-model="visible"
title="主题设置"
direction="rtl"
class="theme-drawer"
:size="'30%'"
>
<!-- 主题设置内容 -->
</el-drawer>
vue
class 属性会直接应用到 Drawer 的外层容器(el-drawer 元素)上,可以通过常规 CSS 设置样式:
.theme-drawer {
min-width: 330px;
}
css
关于深度运算符
需要注意的是,在 scoped 样式中使用 :deep() 穿透到 el-drawer 内部的 class(如 .el-drawer__body)时,由于 Drawer 默认挂载在 <body> 下而非组件内部,scoped 样式可能无法生效。Element Plus 提供了 append-to-body 属性控制这一行为,默认 Drawer 不追加到 body。如果使用了 append-to-body,则需要使用全局样式或 custom-class / class 属性来设置样式。
方案二:移动端全屏展示
当屏幕宽度小于一定阈值(如 640px)时,让 Drawer 内容撑满整个屏幕宽度,移除旁边的空白遮罩区域。使用 UnoCSS 的响应式 class 即可实现:
<el-drawer
v-model="visible"
title="主题设置"
direction="rtl"
class="theme-drawer lt-sm:w-full"
:size="'30%'"
>
<!-- 主题设置内容 -->
</el-drawer>
vue
lt-sm:w-full 表示当屏幕宽度小于 sm 断点(640px)时,宽度设为 100%。UnoCSS 的 lt- 前缀对应 max-width 媒体查询:
| UnoCSS class | 等价媒体查询 | 含义 |
|---|---|---|
lt-sm:w-full | @media (max-width: 639px) | 小于 640px 时宽度 100% |
lt-md:w-full | @media (max-width: 767px) | 小于 768px 时宽度 100% |
lt-lg:w-full | @media (max-width: 1023px) | 小于 1024px 时宽度 100% |
完整示例
将 PC 端最小宽度和移动端全屏方案结合,得到完整的 Drawer 响应式配置:
<!-- components/ThemeSettings.vue -->
<template>
<el-drawer
v-model="visible"
title="主题设置"
direction="rtl"
class="theme-drawer lt-sm:w-full"
:size="'30%'"
>
<div class="p-4">
<!-- 导航模式 -->
<div class="mb-6">
<h3 class="text-sm font-medium mb-2">导航模式</h3>
<el-radio-group v-model="settings.mode">
<el-radio value="vertical">左侧</el-radio>
<el-radio value="horizontal">顶部</el-radio>
<el-radio value="top">混合</el-radio>
</el-radio-group>
</div>
<!-- 主题色 -->
<div class="mb-6">
<h3 class="text-sm font-medium mb-2">主题色</h3>
<el-color-picker v-model="settings.themeColor" />
</div>
<!-- 显示设置 -->
<div class="mb-6">
<h3 class="text-sm font-medium mb-2">显示设置</h3>
<div class="flex items-center justify-between mb-2">
<span>显示标签栏</span>
<el-switch v-model="settings.showTabs" />
</div>
<div class="flex items-center justify-between">
<span>显示面包屑</span>
<el-switch v-model="settings.showBreadcrumb" />
</div>
</div>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const visible = ref(false)
const settings = ref({
mode: 'vertical',
themeColor: '#409eff',
showTabs: true,
showBreadcrumb: true,
})
</script>
<style scoped>
.theme-drawer {
min-width: 330px;
}
</style>
vue
总结
Drawer 抽屉组件的响应式优化思路可以归纳为两个方向:PC 端通过 min-width 确保内容在窄屏下仍然可用,移动端通过 UnoCSS 的 lt- 响应式前缀让内容全屏展示。这种方式无需 JavaScript 介入,纯 CSS 即可实现流畅的响应式过渡效果。
↑