uniapp 下拉选择组件

1.前言

  • 官方的picker组件不能禁用某个下拉项,所以就有了这个下拉组件
  • 组件只适配了宽屏模式,效果参照element-ui的select组件
  • demo地址:lianlizhou / ep-select

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
                //修改v-model的值
                this.$emit('input',item[this.value_key])
                //将事件通知父组件
                this.$emit('change',item[this.value_key])
                
                //重置输入框
				if(!this.keepInput){
					this.input = ''
				}
                
            },
            //展开选项
            openOptions(){
                //禁用则不做任何操作
                if(this.disabled) return
                
                //模式判断 select or picker
                //console.log(this.mode)
                
                this.show_option  = true
            },
            //点击蒙版
            clickMask(){
                //关闭下拉选项
                this.show_option = false
                //重置输入框
                if(!this.keepInput){
                	this.input = ''
                }
            }
        },
        computed:{
            //输入框要展示的文本 动态计算placeholder
            showLabel(){
                //根据v-model传入的值,查找对应的下拉项
                var target = this.options.find(item=>{
                    return item[this.value_key] === this.value
                })
                //如果目标项存在 则返回其 label
                if(target){
                    return target[this.label_key]
                }else{
                    return this.placeholder
                }
            },
            //动态计算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;
            /* padding-left:10px; */
            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>
posted @   ---空白---  阅读(5843)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
历史上的今天:
2021-02-18 PHP 分支与循环
2021-02-18 PHP 字符串
点击右上角即可分享
微信分享提示