第一次用Element Plus的el-select组件时,我就被它的开箱即用特性惊艳到了。但随着项目深入,标准样式在中后台系统里总显得格格不入。比如我们有个物流管理系统,需要在地图界面上展示城市选择器,默认的蓝色边框和白色下拉框在深色地图背景上简直像块补丁。
最基础的样式覆盖方法是通过CSS直接修改。比如要改变选项悬停颜色,可以这样写:
css复制.el-select-dropdown__item {
background-color: #f0f7ff;
color: #1a1a1a;
}
.el-select-dropdown__item.hover {
background-color: #d0e3ff;
}
但实际项目中我发现这种写法有个坑——样式污染。因为Element Plus的类名都是全局的,可能会影响其他页面的下拉框。后来我改用作用域scoped样式配合深度选择器:
vue复制<style scoped>
:deep(.el-select-dropdown__item) {
font-family: 'HarmonyOS Sans', sans-serif;
padding: 8px 12px;
}
</style>
对于需要整体换肤的场景,我更推荐使用CSS变量。Element Plus官方支持通过:root定义一套设计变量:
css复制:root {
--el-select-dropdown-bg: #ffffff;
--el-select-item-hover-bg: #f5f7fa;
--el-select-border-color: #dcdfe6;
}
最近做政务系统项目时,客户要求所有表单控件都要符合政务系统UI规范。我们最终封装了一个SelectWrapper组件,内部预置了整套政务风格样式,开发时只需要这样用:
vue复制<SelectWrapper v-model="city" :options="cityOptions" />
处理长文本选项是el-select最头疼的问题之一。记得去年做电商后台时,商品SKU经常是"iPhone14 Pro Max 256G 深空灰 国行版 2023年新款"这种长度,默认下拉框直接显示成"...", 采购同事天天抱怨要反复点开确认。
最简单的解决方案是启用fit-input-width属性:
vue复制<el-select
v-model="sku"
:fit-input-width="true"
>
<el-option
v-for="item in skuList"
:key="item.id"
:label="item.name"
:value="item.id"
:title="item.name"
/>
</el-select>
但实测发现这个方案在复杂布局里不太稳定。后来我改用popper-class配合自定义宽度:
vue复制<template>
<el-select
v-model="longTextValue"
popper-class="custom-long-select"
>
<!-- options -->
</el-select>
</template>
<style>
.custom-long-select {
width: auto;
min-width: 100%;
max-width: 400px;
}
.custom-long-select .el-select-dropdown__item {
white-space: normal;
padding: 8px 12px;
line-height: 1.5;
}
</style>
对于超长内容,我总结出三种实用方案:
第三种方案实现起来最有意思,需要自定义选项模板:
vue复制<el-select v-model="value">
<el-option
v-for="item in longOptions"
:key="item.value"
:value="item.value"
>
<template #default>
<div class="truncate-text">
{{ truncate(item.label, 30) }}
<el-button
v-if="item.label.length > 30"
link
@click.stop="showFullText(item)"
>
展开
</el-button>
</div>
</template>
</el-option>
</el-select>
当选项内容不仅是长,还包含结构化信息时,就需要更智能的展示方案。比如我们做医疗系统时,药品名称可能是"注射用头孢曲松钠(罗氏芬) 1g/支 进口药品 需冷链保存"。
这时候简单的悬浮提示就不够用了。我的解决方案是分三级信息展示:
vue复制<el-select v-model="drug">
<el-option
v-for="item in drugList"
:key="item.code"
:value="item.code"
>
<div class="drug-option">
<div class="main-text">
{{ item.name }} {{ item.spec }}
</div>
<div class="tags">
<el-tag v-if="item.imported" size="small">进口</el-tag>
<el-tag v-if="item.coldChain" type="warning" size="small">冷链</el-tag>
</div>
<el-popover
placement="right"
trigger="click"
:content="item.fullDesc"
>
<template #reference>
<el-button link size="small">详情</el-button>
</template>
</el-popover>
</div>
</el-option>
</el-select>
对于金融行业常见的代码+长名称组合(如"000001.SH 上证综合指数"),我推荐使用分组展示:
vue复制<el-select v-model="stock">
<el-option-group
v-for="group in stockGroups"
:key="group.label"
:label="group.label"
>
<el-option
v-for="item in group.options"
:key="item.code"
:value="item.code"
:label="`${item.code} ${item.name}`"
>
<span class="code">{{ item.code }}</span>
<span class="name">{{ item.name }}</span>
</el-option>
</el-option-group>
</el-select>
经过多个项目迭代,我总结出一套Select高级封装方案。以地区选择器为例,我们需要处理:
vue复制<script setup>
const AreaSelect = defineComponent({
props: {
modelValue: String,
showHot: { type: Boolean, default: true },
showRecent: { type: Boolean, default: true }
},
setup(props, { emit }) {
const searchQuery = ref('')
const recentUsed = ref(JSON.parse(localStorage.getItem('recentAreas') || '[]'))
const filteredOptions = computed(() => {
// 实现拼音搜索+热门置顶+最近使用逻辑
})
const handleSelect = (value) => {
updateRecentUsed(value)
emit('update:modelValue', value)
}
return { searchQuery, filteredOptions, handleSelect }
}
})
</script>
<template>
<el-select
:model-value="modelValue"
filterable
@update:model-value="handleSelect"
>
<template v-if="showRecent && recentUsed.length">
<el-option-group label="最近使用">
<!-- 最近使用选项 -->
</el-option-group>
</template>
<template v-if="showHot">
<el-option-group label="热门城市">
<!-- 热门城市选项 -->
</el-option-group>
</template>
<el-option-group label="全部地区">
<!-- 常规选项 -->
</el-option-group>
</el-select>
</template>
对于需要支持多选的场景,我增加了标签化展示和数量限制:
vue复制<template>
<el-select
v-model="selectedUsers"
multiple
:multiple-limit="5"
collapse-tags
collapse-tags-tooltip
>
<el-option
v-for="user in userList"
:key="user.id"
:label="user.name"
:value="user.id"
/>
</el-select>
</template>
性能优化方面,当选项超过500条时,建议加入虚拟滚动:
vue复制<el-select
v-model="value"
:teleported="false"
popper-class="virtual-select"
>
<el-virtual-scroll
:item-size="32"
:items="largeList"
>
<template #default="{ item }">
<el-option
:key="item.value"
:label="item.label"
:value="item.value"
/>
</template>
</el-virtual-scroll>
</el-select>