uniapp下拉选择组件

2024-05-12 04:04
文章标签 uniapp 组件 选择 下拉

本文主要是介绍uniapp下拉选择组件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

uniapp下拉选择组件

  • 背景
  • 实现思路
  • 代码实现
  • 配置项
  • 使用
  • 尾巴

背景

最近遇到一个这样的需求,在输入框中输入关键字,通过接口查询到结果之后,以下拉框列表形式展现供用户选择。查询了下uni-app官网和项目中使用的uv-ui库,没找到符合条件的组件。唯一一个有点类似的就是uni官方下拉框组件,但是不支持input组件,所以我们自己来实现一个。

我们先上一张图镇楼,提供了多种模式和使用场景的情况
在这里插入图片描述

实现思路

那么实现这样一个组件要有哪些注意点了?我大概罗列了一下:
1、下拉框默认是不显示的,要知道是在哪个位置显示
2、初始显示的组件不能定死,可以是button,可以是view,或者是input,要灵活
3、提供丰富的自定义配置
要解决第一和第二个问题,我们的自定义组件需要使用插槽,插槽用来放我们的初始显示组件,来灵活适配各种组件。然后在插槽下面来通过绝对定位来显示下拉框组件,默认隐藏,然后暴露出显示函数给外部。

代码实现

接下来就是代码实现环节了,有了思路直接按思路撸代码就行。
在这里插入图片描述
知道你们不喜欢啰嗦BB,哈哈,直接上代码
(down-select.vue)组件代码:

<template><view class='layout-column'><view id="parent" style="width:fit-content;"><slot></slot></view><view:style="'width:'+slotW+';max-height: '+getListContentHei+'rpx;z-index: 9999;position: absolute;margin-top:'+slotH+';'+(isShow ? '' : 'display:none;')":class="(dataList.length > 0 ? 'data-box-shadow ' : 'data-box ') + animate"><block v-if="dataList.length > 0"><view class="data-box-scroll":style="'height:'+dataList.length*(itemHeight-1)+'rpx;max-height: '+max*(itemHeight-1)+'rpx;'"><text v-for="(item,index) in dataList" :key="item[identifier]":class="'layout-row less-center list-item '+(item.enable === false ? '' : 'active')":style="'color:'+(item.enable === false ? '#dedede' : (checkedIndex.indexOf(index) >= 0 ? itemSelectColor : itemColor))+';font-size:'+itemFontsize+'rpx;'"@click="handleListItem(index, item)">{{item[showKey]}}</text></view><view class="layout-row opera-btns less-center" v-if="mode == 'multiple'"><view class="opera-cancel layout-row center" @click='handelCancel'>取消</view><view class="opera-sure layout-row center" @click='handelSure'>确定</view></view></block><view v-else :style="'width:'+slotW+';'" class='status-text'>暂无数据</view></view><view class="mask" v-show="isShow" @click="handelCancel"></view></view>
</template><script>export default {name: "down-select",props: {//要显示的字段showKey: {type: String,default: '',},mode: {type: String,default: 'single', //multiple// default: 'multiple'},dataList: {type: Array,default: []},//选中的列表,用作显示列表是展示已选中项checkedDataList: {type: Array,default: []},//最多展示几项后开始滑动max: {type: Number,default: 4},//数据项每个item高度rpxitemHeight: {type: Number,default: 80},//唯一标识符字段,用来比对选中项和维持v-for列表中的key,不填此项无选中效果identifier: {type: String,default: ''},itemSelectColor: {type: String,default: '#00aaff'},itemColor: {type: String,default: 'black'},itemFontsize: {type: Number,default: 30}},computed: {getListContentHei() {let len = this.dataList.lengthlet signleH = len < this.max ? this.itemHeight * len : this.itemHeight * this.maxif (this.mode == 'single') {return len <= 0 ? this.itemHeight : signleH} else {return len <= 0 ? this.itemHeight : (signleH + this.itemHeight)}}},watch: {dataList: {handler: function(newVal, oldVal) {if (this.checkedDataList.length >= 0 && this.identifier) {this.checkedIndex = []this.checkedDataList.forEach(ele => {let index = newVal.findIndex(ele1 => ele[this.identifier] === ele1[this.identifier])if (index >= 0) {this.checkedIndex.push(index)}})}},immediate: true, // 组件创建时立即触发deep: true // 对象内部属性变化时也触发},checkedDataList: {handler: function(newVal, oldVal) {if (newVal.length >= 0 && this.identifier) {this.checkedIndex = []newVal.forEach(ele => {let index = this.dataList.findIndex(ele1 => ele[this.identifier] === ele1[this.identifier])if (index >= 0) {this.checkedIndex.push(index)}})}},immediate: true, // 组件创建时立即触发deep: true // 对象内部属性变化时也触发}},mounted() {this.$nextTick(() => {uni.createSelectorQuery().in(this).select('#parent').boundingClientRect(res => {if (res.width) {this.slotW = `${res.width}px`this.slotH = `${res.height+5}px`}}).exec()})},data() {return {slotW: '0px',slotH: '0px',isShow: false,checkedIndex: [],animate: '',//传进来选中项,后又改成未选中并确认,多选模式生效checkedDels: []};},methods: {open() {if (this.checkedDataList.length >= 0 && this.identifier) {this.checkedIndex = []this.checkedDataList.forEach(ele => {let index = this.dataList.findIndex(ele1 => ele[this.identifier] === ele1[this.identifier])if (index >= 0) {this.checkedIndex.push(index)}})}this.isShow = truethis.animate = 'show-animate'},close() {this.animate = 'hide-animate'this.checkedIndex = []this.checkedDels = []this.isShow = false},handleListItem(index, obj) {if(obj.enable === false){return}if (this.mode === 'single') {this.checkedIndex = []this.checkedIndex.push(index)this.handelSure()} else {let sindex = this.checkedIndex.indexOf(index)if (sindex >= 0) {if (this.identifier) {//判断未选中的项在传进来的已选项中是否存在let contain = this.checkedDataList.filter(ele => ele[this.identifier] === this.dataList[index][this.identifier])if (contain.length > 0) {//传进来的已选项中是否存在选择为未选中的内容let contain1 = this.checkedDels.filter(ele => ele[this.identifier] === contain[0][this.identifier])if (contain1.length <= 0) {this.checkedDels.push(contain[0])}}}this.checkedIndex.splice(sindex, 1);} else {if (this.identifier) {let contain2 = this.checkedDels.filter(ele => ele[this.identifier] === this.dataList[index][this.identifier])if (contain2.length > 0) {let tempIndex = this.checkedDels.findIndex(ele => ele[this.identifier] === this.dataList[index][this.identifier])if (tempIndex >= 0) {this.checkedDels.splice(tempIndex, 1)}}}this.checkedIndex.push(index)}}},handelCancel() {this.close()this.$emit('cancelDimss', '')},handelSure() {let results = []if (this.checkedIndex.length <= 0) {uni.showToast({title: '请选择至少一项',icon: 'none'});return}this.checkedIndex.forEach(ele => {if (this.dataList[ele]) {results.push(this.dataList[ele])}})//将本次选中结果清除this.checkedIndex = []this.$emit('resultBack', results, this.checkedDels)this.close()}}}
</script><style scoped>.active {}.active:active {opacity: 0.6;}.layout-row {display: flex;flex-direction: row;}.layout-column {display: flex;flex-direction: column;}/* 整体方向居中 */.center {align-items: center;justify-content: center;}/* 主轴方向居中 */.main-center {justify-content: center;}/* 侧轴方向居中 */.less-center {align-items: center;}.data-box-scroll {width: 100%;overflow-y: scroll;}.data-box-scroll::-webkit-scrollbar {display: none}.data-box {background: white;border-radius: 8rpx;box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);z-index: 9999;}.data-box-shadow {background: white;border-radius: 8rpx;box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);z-index: 9999;}.list-item {width: 100%;height: 80rpx;margin-left: 20rpx;margin-right: 20rpx;border-bottom: 1rpx solid #D8DFEC;text-align: right;}.opera-btns {width: 100%;height: 80rpx;justify-content: flex-end;}.opera-cancel {width: 100rpx;height: 50rpx;background-color: white;box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);border-radius: 5rpx;font-size: 26rpx;}.opera-sure {width: 100rpx;height: 50rpx;background-color: #58a2e4;box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);border-radius: 5rpx;font-size: 26rpx;color: white;margin-right: 30rpx;margin-left: 30rpx;}.status-text {text-align: center;font-size: 28rpx;font-weight: 600;color: #c2c2c2;padding-top: 20rpx;padding-bottom: 20rpx;}.mask {position: fixed;background: transparent;top: 0;left: 0;width: 100%;height: 100%;z-index: 1;}.show-animate {animation-name: open;animation-duration: 1s;animation-iteration-count: 1;}@keyframes open {0% {height: 0rpx;}100% {height: 100%;}}.hide-animate {animation-name: close;animation-duration: 1s;animation-iteration-count: 1;animation-fill-mode: forwards;}@keyframes close {0% {height: 100%;}100% {height: 0rpx;}}
</style>

(test.vue)测试页面代码:

<template><view class="content"><downSelect ref='selectRef' showKey="name" mode='single' :dataList="list" @resultBack="result"><view class="select-btn" @click="handleClick">单选默认配置</view></downSelect><view>{{select.name}}</view><downSelect ref='selectRef1' showKey="name" mode='single' itemColor='red' :dataList="list1" @resultBack="result1"><view class="select-btn1" @click="handleClick1">单选字体设置,是否可点击</view></downSelect><view>{{select1.name}}</view><downSelect ref='selectRef2' showKey="name" mode='multiple' identifier="id" :dataList="list2":max="5" :itemFontsize='20' :checkedDataList="select2" @resultBack="result2"><view class="select-btn2" @click="handleClick2">多选字体大小设置,超出设置项后滑动设置</view></downSelect><view v-for="item in select2">{{item.name}}</view><downSelect ref='selectRef3' showKey="name" mode='single'><view class="select-btn2" @click="handleClick3">空数据</view></downSelect><downSelect ref='selectRef4' showKey="name" mode='single' :dataList="list4" @resultBack="result4"><input class="select-btn1" placeholder="请输入" @input="handleInput"/></downSelect><view>{{select4.name}}</view></view></template><script>import downSelect from "@/components/down-select.vue"export default {components: {downSelect},data() {return {select: {},select1: {},select2: [],select4: {},list: [{name: '选项一'}, {name: '选项二'}],list1: [{name: '选项一',enable: false}, {name: '选项二'}, {name: '选项三'}, {name: '选项四'}, {name: '选项五'}],list2: [{id: '0',name: '选项一'}, {id: '1',name: '选项二'}, {id: '2',name: '选项三'}, {id: '3',name: '选项四'}, {id: '4',name: '选项五'}, {id: '5',name: '选项六'}],list4: []}},onLoad() {},methods: {handleInput(e){this.$refs.selectRef4.open()//这里模拟接口访问获取数据setTimeout(()=> {this.list4 = [{name: '选项一'}, {name: '选项二'}]},2000)},result(result) {this.select = result[0]},result1(result){this.select1 = result[0]},result2(result, dels = []){// this.select2 = resultif(this.select2.length <= 0){this.select2.push(...result)}else {result.forEach(ele => {let contain = this.select2.filter(ele1 => ele.id === ele1.id)if(contain.length <= 0){this.select2.push(ele)}})}if(dels.length > 0){dels.forEach(ele => {let index = this.select2.findIndex(ele1 => ele1.id === ele.id)if(index >= 0){this.select2.splice(index, 1)}})}},result4(result){this.select4 = result[0]},handleClick(){this.$refs.selectRef.open()},handleClick1() {this.$refs.selectRef1.open()},handleClick2 () {this.$refs.selectRef2.open()},handleClick3 () {this.$refs.selectRef3.open()}},}
</script><style>.content {width: 690rpx;margin: 25rpx;}.select-btn {width: 690rpx;height: 60rpx;display: flex;flex-direction: row;align-items: center;justify-content: center;border: 1rpx solid #e5e5e5;border-radius: 15rpx;}.select-btn1 {width: 490rpx;height: 60rpx;display: flex;margin-top: 100rpx;flex-direction: row;align-items: center;justify-content: center;border: 1rpx solid #e5e5e5;border-radius: 15rpx;}.select-btn2 {width: 690rpx;height: 60rpx;display: flex;margin-top: 100rpx;flex-direction: row;align-items: center;justify-content: center;border: 1rpx solid #e5e5e5;border-radius: 15rpx;}
</style>

以上代码都可以直接拷贝使用,无其他关联项,vue2、vue3、小程序、h5、APP都可用。

配置项

1、showKey:传进来的数组单个对象在下拉框中要展示的值,比如传的dataList为[{name: ‘韩梅梅’}],那showKey传name就行。
2、mode:下拉框是单选还是多选模式,见镇楼效果图。
3、dataList:传入的数据列表,列表为对象,多选时对象需要指定唯一标识。
4、checkedDataList:多选选中的列表数据。
5、max:多条数据时最多显示几项后可以滑动。
6、identifier:唯一标识符,用作列表key和多选模式显示已选数据项时的标识。
7、itemSelectColor:多选模式选中后的字体颜色。
8、itemColor:字体颜色。
9、itemFontsize:字体大小。

以上配置项在代码层面没做严格限制,不同模式混用可能会有bug,如果发现可以留言。配置项不满足你需求,可以自行增删。

使用

页面引入down-select.vue,然后参考第二节代码实现中的demo。

尾巴

今天的文章就到这里了,希望能给大家帮助,如果喜欢我的文章,欢迎给我点赞,评论,关注,谢谢大家!

这篇关于uniapp下拉选择组件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/981546

相关文章

Vue中组件之间传值的六种方式(完整版)

《Vue中组件之间传值的六种方式(完整版)》组件是vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,针对不同的使用场景,如何选择行之有效的通信方式... 目录前言方法一、props/$emit1.父组件向子组件传值2.子组件向父组件传值(通过事件形式)方

Spring组件初始化扩展点BeanPostProcessor的作用详解

《Spring组件初始化扩展点BeanPostProcessor的作用详解》本文通过实战案例和常见应用场景详细介绍了BeanPostProcessor的使用,并强调了其在Spring扩展中的重要性,感... 目录一、概述二、BeanPostProcessor的作用三、核心方法解析1、postProcessB

kotlin中的行为组件及高级用法

《kotlin中的行为组件及高级用法》Jetpack中的四大行为组件:WorkManager、DataBinding、Coroutines和Lifecycle,分别解决了后台任务调度、数据驱动UI、异... 目录WorkManager工作原理最佳实践Data Binding工作原理进阶技巧Coroutine

基于Python实现多语言朗读与单词选择测验

《基于Python实现多语言朗读与单词选择测验》在数字化教育日益普及的今天,开发一款能够支持多语言朗读和单词选择测验的程序,对于语言学习者来说无疑是一个巨大的福音,下面我们就来用Python实现一个这... 目录一、项目概述二、环境准备三、实现朗读功能四、实现单词选择测验五、创建图形用户界面六、运行程序七、

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

Vue ElementUI中Upload组件批量上传的实现代码

《VueElementUI中Upload组件批量上传的实现代码》ElementUI中Upload组件批量上传通过获取upload组件的DOM、文件、上传地址和数据,封装uploadFiles方法,使... ElementUI中Upload组件如何批量上传首先就是upload组件 <el-upl

前端知识点之Javascript选择输入框confirm用法

《前端知识点之Javascript选择输入框confirm用法》:本文主要介绍JavaScript中的confirm方法的基本用法、功能特点、注意事项及常见用途,文中通过代码介绍的非常详细,对大家... 目录1. 基本用法2. 功能特点①阻塞行为:confirm 对话框会阻塞脚本的执行,直到用户作出选择。②

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element