1.前言
2.封装思路
- 此组件使用了字体图标,素材来源于iconfont
- 组件分为3个部分:输入框容器,下拉容器,蒙版容器
- 输入框容器:展示已选中的选项,点击则展示下拉容器和蒙版容器
- 下拉容器:使用绝对定位,展示备选下拉项,以及相应的点击事件
- 蒙版容器:使用fixed定位,全屏覆盖,点击隐藏下拉容器和自身
3.基本用法
<template>
<view class="content">
<view style="display: flex;justify-content: center;padding:10px;">
<ep-select :disabled="false" v-model="select" :options="options" @change="change"></ep-select>
</view>
</view>
</template>
<script>
export default {
data() {
return {
select: '',
options:[
{value:"1",label:"上海"},{value:"2",label:"深圳"},{value:"3",label:"广州",disabled:true}
]
}
},
onLoad() {
},
methods: {
change(e){
console.log('select = ',this.select)
}
}
}
</script>
- 效果

4.参数说明
参数 |
类型 |
默认值 |
说明 |
value |
String,Number |
'' |
当前选中项的支持v-model |
options |
Array |
[] |
下拉选项列表,disabled属性代表当前项禁用 |
value_key |
String |
value |
指定 Object 中 key 的值作为选择器选中内容,以此同步给value属性 |
label_key |
String |
label |
指定 Object 中 key 的值作为选择器展示内容 |
参数 |
类型 |
默认值 |
说明 |
change |
EventHandle |
- |
下拉选择切换事件 |
5.完整代码
<template>
<view class="ep-select-box">
<view class="ep-mask-box" v-show="show_option" @click="clickMask"></view>
<view class="ep-input-box" :class="{'disabled':disabled}" @click="openOptions">
<view v-if="!filterable" class="label-item" :class="{'no-match': value === ''}">{{showLabel}}</view>
<view v-else class="input-item">
<uni-easyinput type="text" v-model="input" :placeholder="showLabel" :placeholderStyle="placeholderStyle" :clearable="false" :inputBorder="false"></uni-easyinput>
</view>
<text v-if="!show_option" class="iconfont icon-xiala"></text>
<text v-else class="iconfont icon-xialashang"></text>
</view>
<view v-show="show_option" class="ep-select-content-wrap">
<scroll-view class="ep-select-content" :scroll-y="true">
<view v-for="item in filterOptions" :key="item[value_key]"
:class="{'disabled':item.disabled,'active':value===item[value_key]}"
class="option-item"
@click="itemClick(item)"
>{{item[label_key]}}</view>
<view v-if='filterOptions.length==0' class="empty-text">暂无数据</view>
</scroll-view>
<text class="triangle"></text>
</view>
</view>
</template>
<script>
export default{
data(){
return {
show_option: false,
input:"",
}
},
props:{
value:{
type:[String,Number],
default:""
},
options:{
type: Array,
default: function(){
return []
}
},
value_key:{
type:String,
default:"value"
},
label_key:{
type:String,
default:"label"
},
disabled:{
type:Boolean,
default: false
},
filterable:{
type:Boolean,
default:false
},
placeholder: {
type:String,
default:"请选择"
},
"keep-input":{
type:Boolean,
default:false
},
},
model:{
prop:'value',
event:"input"
},
methods:{
itemClick(item){
if(item.disabled) return
this.show_option = false
this.$emit('input',item[this.value_key])
this.$emit('change',item[this.value_key])
if(!this.keepInput){
this.input = ''
}
},
openOptions(){
if(this.disabled) return
this.show_option = true
},
clickMask(){
this.show_option = false
if(!this.keepInput){
this.input = ''
}
}
},
computed:{
showLabel(){
var target = this.options.find(item=>{
return item[this.value_key] === this.value
})
if(target){
return target[this.label_key]
}else{
return this.placeholder
}
},
placeholderStyle(){
if(!this.value){
return 'color:#999;'
}else if(this.show_option){
return 'color:#999;'
}else{
return 'color:#333;'
}
},
filterOptions(){
return this.options.filter(item=>{
return item[this.label_key].includes(this.input)
})
},
}
}
</script>
<style scoped lang="scss">
@import './iconfont.css';
.ep-select-box{
box-sizing: border-box;
position: relative;
width:100%;
color: #333;
background-color: #fff;
font-size: 26rpx;
.ep-mask-box{
position: fixed;
z-index: 999;
top: 0;
right: 0;
left: 0;
bottom: 0;
background:none;
}
.ep-input-box{
height: 36px;
border: 1px solid rgb(229, 229, 229);
border-radius: 8rpx;
position: relative;
cursor: pointer;
display: flex;
align-items: center;
.label-item{
display: flex;align-items: center;height: 36px;padding-left:16rpx;
}
.label-item.no-match{
color: rgb(153, 153, 153);
}
.input-item{
display: flex;align-items: center;min-height: 36px;
}
.iconfont{
position: absolute;
top: 50%;
right: 10rpx;
font-size: 40rpx;
transform: translateY(-50%);
color:#B8B8B8;
}
}
.disabled{
cursor:not-allowed;
background-color: #f5f7fa;
.label-item{
color: #999;
}
}
.ep-select-content-wrap{
width: 100%;
min-width: 250rpx;
position: absolute;
top: 45px;
left: 0;
z-index: 9999;
padding-top:6px;
.ep-select-content{
background-color: #fff;
padding: 6rpx 0;
font-size: 26rpx;
box-shadow: 0 0 40rpx 10rpx rgba(0,0,0,0.3);
border-radius: 10rpx;
max-height: 181px;
.option-item{
padding: 16rpx 36rpx;
cursor: pointer;
&:hover{
background-color: #f5f7fa;
}
}
.active{
color:#007AFF;
}
.disabled{
color:#c0c4cc;
&:hover{
background-color: #f5f7fa;
}
}
}
.triangle{
width: 0;
height: 0;
border-top: 12rpx solid rgba(0,0,0,0);
border-right: 12rpx solid rgba(0,0,0,0);
border-bottom: 12rpx solid #fff;
border-left: 12rpx solid rgba(0,0,0,0);
position: absolute;
top: -12rpx;
left: 50%;
transform: translateX(-50%);
box-sizing: content-box;
}
}
}
.empty-text{
color:#999;
text-align: center;
padding: 6rpx 0;
font-size: 26rpx;
}
.uni-easyinput__placeholder-class{
font-size: 26rpx !important;
font-weight: 400 !important;
height: 100%;
display: flex;
align-items: center;
}
</style>
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
2021-02-18 PHP 分支与循环
2021-02-18 PHP 字符串