原生微信小程序中封装一个模拟select 下拉框组件

1.首先在components 里面设置组件名称:van-select(随便取名字);

2.新建文件写代码:

wxml:

html 复制代码
<view class="w100 select_all_view">
  <!-- 标题,可以没有 -->
  <view class="mr-10 pt-10 size-28" style="width: {{titleWidth}};" wx:if="{{title}}">{{title}}</view>
  <view class="select_view relative" style="width: {{title ? 'calc(100% - ' + titleWidth + ' - 10rpx)' : '100%'}};max-width: {{title ? 'calc(100% - ' + titleWidth + ' - 10rpx)' : '100%'}};">
    <view class="inputPlaceholder h100 w100 radius-10 relative flex_l pd-10 {{ disabled ? 'gray-3' : 'black' }}" bindtap="{{disabled || readonly ? '' : 'changeShow'}}" style="background: {{disabled ?'#f5f7fa' : bgColor}};border: 2rpx solid #ddd;">
      <block wx:if="{{disabled || readonly}}">
        <view class="flex-1" wx:if="{{selectLabel}}">{{selectLabel}}</view>
        <view class="flex-1 gray-3 line-1" wx:else>{{placeholder}}</view>
        <van-icon class="gray-3" name="arrow-down" />
      </block>
      <block wx:else>
        <block wx:if="{{selectLabel}}">
          <view class="flex-1">{{selectLabel}}</view>
          <van-icon class="gray-3" name="clear" wx:if='{{!show}}' catchtap="clearInput" />
          <van-icon class="gray-3" name="arrow-up" wx:else />
        </block>
        <block wx:else>
          <view class="flex-1 gray-3 line-1">{{placeholder}}</view>
          <van-icon class="gray-3" name="arrow-down" class="transfer {{show ? 'is-reverse' : 'no-reverse' }}" />
        </block>
      </block>
    </view>
    <!-- 下拉展开后的可选择内容 -->
    <block wx:if='{{show}}'>
      <view class="{{toTop ? 'triangleBox-top' : 'triangleBox'}}">
        <view class="{{toTop ? 'triangle-top' : 'triangle'}}"></view>
      </view>
      <view class="content radius-10 pd-20 size-28" style="{{toTop ? 'top: -' + (options.length > 4 ? 150 : (options.length * 30 + 40)) + 'rpx; margin-top: -6rpx;' : 'margin-top: 10rpx;'}}">
        <view class="pd-10 center gray-3" wx:if="{{options.length < 1}}">暂无数据</view>
        <view class="line-1 w100 pd-10 contentItem {{item[valueName] == selectValue ? 'bold':''}}" wx:for="{{options}}" wx:key="index" bindtap="handleChange" data-item="{{item}}" style="color: {{ item[valueName] == selectValue ? textColor : '#000'}}; background: {{item[valueName] == selectValue ? itemBgColor:''}};">
          {{item[labelName]}}
        </view>
      </view>
    </block>
  </view>
</view>

wxss:

css 复制代码
.select_all_view {
  display: flex;
  justify-content: start;
  align-items: start;
  z-index: 999;
  margin-bottom: 20rpx;
}

.select_view {
  /* min-width: 200rpx; */
  min-height: 64rpx;
}

.inputPlaceholder {
  font-size: 28rpx;
}
.flex_l{
  display: flex;
}
.flex-1{
  flex: 1;
}
.pd-10{
  padding:20rpx;
}
.relative{
  position: relative;
}
.radius-10{
  border-radius: 10rpx;
}
.icon {
  position: absolute;
  right: 12rpx;
  top: 20rpx;
}

.contentItem {
  height: 30rpx;
  line-height: 30rpx;
  font-size: 24rpx;
}

.content {
  width: calc(100% - 4px);
  margin-left: 2px;
  position: absolute;
  z-index: 999;
  max-height: 150rpx;
  background: #FFFFFF;
  /* border: 1px solid #ccc; */
  box-shadow: 0 0 4px #ccc;
  opacity: 1;
  /* margin-top: 10rpx; */
  overflow-x: hidden;
  overflow-y: scroll;
}

.triangleBox {
  position: absolute;
  z-index: 1000;
  left: 30rpx;
}

.triangle {
  position: relative;
  border-left: 12rpx solid transparent;
  border-right: 12rpx solid transparent;
  border-bottom: 10rpx solid #ccc;
}

.triangle::after {
  content: '';
  position: absolute;
  top: 3rpx;
  left: -12rpx;
  border-left: 12rpx solid transparent;
  border-right: 12rpx solid transparent;
  border-bottom: 10rpx solid #fff;
}

.triangleBox-top {
  position: absolute;
  z-index: 1000;
  left: 30rpx;
  /* display: none; */
}

.triangle-top {
  position: relative;
  border-left: 12rpx solid transparent;
  border-right: 12rpx solid transparent;
  border-top: 10rpx solid #ccc;
}

.triangle-top::after {
  content: '';
  position: absolute;
  bottom: 3rpx;
  left: -12rpx;
  border-left: 12rpx solid transparent;
  border-right: 12rpx solid transparent;
  border-top: 10rpx solid #fff;
}

.is-reverse {
  transform: rotate(180deg);
}

.transfer {
  transition: transform .3s;
}

.no-reverse {
  transition: rotate(0deg);
}

js:

javascript 复制代码
Component({
  options: {
    addGlobalClass: true,
  },
  properties: {
    /* --------- 样式参数 --------- */
    titleWidth: { // 标题长度
      type: String,
      value: "60px"
    },
    bgColor: { // 输入框背景颜色
      type: String,
      value: "#fff"
    },
    itemBgColor: { // 选中的选项背景颜色
      type: String,
      value: "#F5F8FE"
    },
    textColor: { // 选中的字体颜色
      type: String,
      value: "#FF5733"
    },
    /* --------- 数据参数 --------- */
    title: { // 下拉框标题
      type: String,
      value: ""
    },
    number: { // 下拉框标题
      type: String,
      value: ""
    },
    options: { // 选项数组
      type: Array,
      value: [],
    },
    labelName: { // 选项数组-绑定的label名称
      type: String,
      value: "dictLabel",
    },
    valueName: { // 选项数组-绑定的value名称
      type: String,
      value: "dictValue"
    },
    modelValue: { // 绑定的value
      type: String,
      value: "",
      observer: function () {
        //如果有默认值,需要匹配出name,所以这里使用obersver,当父组件中值改变时触发
        this.handleData();
      }
    },
    placeholder: { // 输入框为空时占位符
      type: String,
      value: "请选择"
    },
    disabled: { // 是否禁用
      type: Boolean,
      value: false
    },
    readonly: { // 是否只读
      type: Boolean,
      value: false
    }
  },
  /**
   * 页面的初始数据
   */
  data: {
    show: false, //选项框及图标展示
    selectValue: "", //选中的value
    selectLabel: "", //选中的label
    toTop: false, // 下拉框是否展示在输入框上方
  },
  attached() {
    this.handleData()
  },
  methods: {
    // 清空输入框
    clearInput() {
      this.setData({
        selectValue: "", //选中的value
        selectLabel: "", //选中的label
        show: false,
      })
    },
    // 下拉框收起和展开
    changeShow(e) {
      let that = this
      const query = wx.createSelectorQuery();
      // 选择当前点击的 view 元素
      query.select('.inputPlaceholder'+this.data.number).boundingClientRect();
      query.exec(function (res) { // res[0].bottom 是元素距离可视区域顶部的距离加上元素自身的高度; res[1].scrollTop 是页面的滚动距离
        var show = !that.data.show
        if (res[0]) {
          /* that.triggerEvent("handleShow", show); // [暂未发现]处理滚动选项区域时背景页面滚动问题 */
          let toBottom = wx.getSystemInfoSync().windowHeight - res[0].bottom;
          console.log('距离设备底部的距离:', toBottom);
          that.setData({
            toTop: toBottom < 150 ? true : false,
            show: show
          })
        } else {
          that.setData({ show: show })
        }
      });
    },
    // 选择数据后回显
    handleChange(e) {
      let { item } = e.currentTarget.dataset
      let { labelName, valueName } = this.data
      this.setData({
        selectValue: item[valueName],
        selectLabel: item[labelName],
        show: false
      })
      let obj = {}
      obj[valueName] = item[valueName]
      obj[labelName] = item[labelName]
      this.triggerEvent("handleChange", obj);// 传参
    },
    // 匹配值并回显
    handleData() {
      let { modelValue, options, valueName, labelName } = this.properties;
      if (modelValue) {
        let item = options.find(r => r[valueName] == modelValue)
        this.setData({
          selectLabel: item ? item[labelName] : modelValue,
          selectValue: modelValue,
        });
      }
    }
  }
})

json:

javascript 复制代码
{
  "component": true,
  "usingComponents": {}
}

以上就是组件的全部代码,当然你也可以自己再按照需求改造,接下来就是组件的应用:

组件应用:

1.在所需要用组件的页面中引入组件,在所需页面的json中:

javascript 复制代码
{
  "navigationBarTitleText": "",
  "usingComponents": {
      "wan-select": "../components/van-select/index"
  }
}

2.在wxml里面使用:

html 复制代码
<view class="cont-box">
   <wan-select 
class="inputPlaceholder1" 
options="{{options1}}" 
number="1" 
labelName="text" 
valueName="id" 
modelValue="{{selectedId1}}" 
placeholder="请选择" 
bindhandleChange="handleChange" 
title="" 
readonly="{{false}}" 
disabled="{{options1.length == 0}}"></wan-select>
</view>

说明:

class="inputPlaceholder1" (自己写的类,可以修改样式)

options="{{options1}}"(下拉框的选项数据)

labelName="text"(选项数组-绑定的label名称)

valueName="id"(选项数组-绑定的value名称)

number="1"(自定义的值,多个select 有关联的情况下,添加,根据自己需求,一个的话没必要)

modelValue="{{selectedId1}}"(为了回显下拉框数据中选中的项)

bindhandleChange="handleChange"(操作事件)

title="" (下拉框左侧添加名称)

readonly="{{false}}"(是否只读)

disabled=""(是否禁用)

如图所示:

相关推荐
.生产的驴29 分钟前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
汤姆yu6 小时前
基于微信小程序的乡村旅游系统
微信小程序·旅游·乡村旅游
计算机徐师兄6 小时前
基于TP5框架的家具购物小程序的设计与实现【附源码、文档】
小程序·php·家具购物小程序·家具购物微信小程序·家具购物
曲辒净7 小时前
微信小程序实现二维码海报保存分享功能
微信小程序·小程序
朽木成才8 小时前
小程序快速实现大模型聊天机器人
小程序·机器人
peachSoda78 小时前
随手记:小程序使用uni.createVideoContext视频无法触发播放
小程序
何极光8 小时前
uniapp小程序样式穿透
前端·小程序·uni-app
小墨&晓末9 小时前
【PythonGui实战】自动摇号小程序
python·算法·小程序·系统安全
oil欧哟1 天前
🤔认真投入一个月做的小程序,能做成什么样子?有人用吗?
前端·vue.js·微信小程序
汤姆yu1 天前
基于微信小程序的消防隐患在线举报系统
微信小程序·小程序·消防隐患