微信小程序自定义yPicker组件分析及省市区三级联动实现



本文为做者行舟客投稿,前端

原文地址:https://blog.csdn.net/qq_43624878/article/details/109284502?utm_source=appgit

完整项目地址:https://github.com/1314mxc/yunUIgithub

 点击阅读原文便可打开github,欢迎点赞!小程序




背景

自从上一篇文章:微信小程序自定义日历组件及flex布局最后一行对齐问题分析 出来之后,有人私聊我说能不能从头分析一下我开源的自定义组件?一直没时间。这不,最近项目中有个需求是 省市区三级联动 ,我就顺便从组件库中的第一个 「扩展日期-时间(点此直接至GitHub)」picker组件开始说一下这两个功能的实现。微信小程序


简单说一下“自定义日期-时间组件”

它的背景是项目的初版当时发现微信小程序内置的日期组件:picker只能精确到某一天(年月日),可是咱们不少时候须要年月日时分甚至是年月日时分秒(如结束时间/发布时间)。image.png数组

笔者仔细翻阅了官方文档和许多博主文章发现提出了各类各样的解决方案(但很遗憾没发现有博主详细公开代码),可是对于这样一个其实并不须要“联动”、列数也不固定的功能,用多列picker模拟多列选择器 便可。微信

<picker mode="multiSelector" bindchange="bindMultiPickerChange" bindcolumnchange="bindMultiPickerColumnChange" value="{{multiIndex}}" range="{{multiArray}}">
 <input value='{{time}}' readonly="" disabled="true" placeholder='{{defaulttext}}' />
</picker>

其中 readonly="" disabled="true" 的做用是使“input聚焦时软键盘不弹出”(两个属性做用同样,都写是由于Android和iOS的兼容性问题)。app

用input代替view是由于input的placeholder能够方便实现“无选中时默认提示”的效果。ide

主要实现策略函数

如上所示,监听了两个事件,分别是:日期选择窗口弹出时以及点击“肯定”按钮时触发函数change、多列选择器每一列滑动时触发事件columnchange。

  1. change中很简单:只须要把选中的数据暴露给页面中(或者经过 triggerEvent 返回给调用页面)便可;
  2. columnchange中要作的就是当前选中的每一列的值填充到data中对应数组的某一项。好比:e.detail.column==1 时表示当前滑动的是第二列(月份),此时就要判断每一月有几天:
if (e.detail.column == 1) {
     let num = parseInt(this.data.multiArray[e.detail.column][e.detail.value]);
     let temp = [];
     if (num == 1 || num == 3 || num == 5 || num == 7 || num == 8 || num == 10 || num == 12) { //判断31天的月份
       for (let i = 1; i <= 31; i++) {
         if (i < 10) {
           i = "0" + i;
         }
         temp.push("" + i);
       }
       this.setData({
         ['multiArray[2]']: temp   //天数更新(根据月份)
       });
    }
}

注意: 多列picker组件监听两个参数:multiArray和multiIndex,他们都是数组!multiArray主要用来表示监听几列,其元素都是一个个数组,如:[years, months, days, hours, minutes]multiIndex是当前每一列(点开时的)初始值!如:[10, meng_date.getMonth(), meng_date.getDate()-1, meng_date.getHours(), meng_date.getMinutes()]通常来讲,multiIndex中的值也被用来当作取multiArray中元素时的第二个索引!


说说省市区三级联动实现

先将城市列表文件发出来:(永久免费下载)

连接 提取码
https://pan.baidu.com/s/1z4ZfOWnAG2zVaGfxXxpF9Q j3m3

使用时按以下引入便可:(是一个citysearch.js文件)

import placeArrays from 'citysearch文件路径';
const placeArray=placeArrays.placeArray

image.pngcitys

正式开始

不知你们有没有使用过,或听太小程序的 picker-view 组件,其定位就是:嵌入页面的滚动选择器。它有三个参数:

参数 类型 说明
value Number Array 数组中的数字依次表示 picker-view 内的 picker-view-colume 选择的第几项(下标从 0 开始),数字大于 picker-view-column 可选项长度时,选择最后一项。
indicator-style String 设置选择器中间选中框的样式
bindchange EventHandle 当滚动选择,value 改变时触发 change 事件,event.detail = {value: value} value为数组,表示 picker-view 内的 picker-view-column 当前选择的是第几项(下标从 0 开始)

须要注意的是:其中只可放置<picker-view-column/>组件,其余节点不会显示,其孩子节点的高度会自动设置成与picker-view的选中框的高度一致。

有了这个组件,咱们是否是能想到:在一个弹出view中设置三个picker-view组件,每一个组件中放一个picker-view-column组件用于展现当前列?

value中也能够只放一个number(一般能够放数组元素下标),picker-view会自动将其转为 [下标值]

就像这样:

<view style="width:100%;position:fixed;bottom:0;left:0;z-index:10000;height:500rpx;background-color:white">
  <!-- 仿原生picker的“肯定”和“取消”按钮 -->
  <view style="display:flex;width:100%;height:100%">
    <view
      style="position: absolute;top:0;width:100%;height:100rpx;z-index:1000000;display:flex;justify-content:space-between;align-items:center;">

      <view style="width:calc(100% / 3);text-align:center;color:rgba(0,0,0,.6);font-size:39rpx" bindtap="displayer">取消
      </view>
      <view style="width:calc(100% / 3);text-align:center;color:rgb(63,142,255);font-size:39rpx" bindtap="confirm">肯定
      </view>
    </view>
    
    <picker-view indicator-style="height: 200rpx;"
      style="width: 100%;height: 300rpx;text-align: center;margin-top:150rpx" value="{{pIndex}}"
      bindchange="changeProvince">

      <picker-view-column>
        <view wx:for="{{placeArray}}" wx:key="name" style="line-height: 77rpx">{{item.name}}</view>
      </picker-view-column>
    </picker-view>
    <picker-view indicator-style="height: 200rpx;"
      style="width: 100%;height: 300rpx;text-align: center;margin-top:150rpx" value="{{cIndex}}"
      bindchange="changeCity">

      <picker-view-column>
        <view wx:for="{{placeArray[pIndex].city}}" wx:key="name" style="line-height: 77rpx">{{item.name}}</view>
      </picker-view-column>
    </picker-view>
    <picker-view indicator-style="height: 200rpx;"
      style="width: 100%;height: 300rpx;text-align: center;margin-top:150rpx" value="{{aIndex}}"
      bindchange="changeArea">

      <picker-view-column>
        <view wx:for="{{placeArray[pIndex].city[cIndex].area}}" wx:key="*this" style="line-height: 77rpx">{{item}}
        </view>
      </picker-view-column>
    </picker-view>
    
  </view>
</view>

能够看到,每个picker-view-column中作的惟一一件事就是:遍历固定的某一列(某一个数组)并渲染出来。image.png

// js-data
data:{
 placeArray: placeArray,
    province"",//placeArray[0].name - 省
    pIndex0,
    city"",//placeArray[0].city[0].name - 市
    cIndex0,
    area"",//placeArray[0].city[0].area[0] - 区
    aIndex0,
}

而后如上wxml中为每一列(picker-view)都绑定了一个change函数——滑动时触发:

changeProvince: function(e){
  const val = e.detail.value
  this.setData({
    pIndex: val,
    cIndex0,
    aIndex0,
    province: placeArray[val].name,
    city: placeArray[val].city[0].name,
    area: placeArray[val].city[0].area[0]
  })
},
changeCityfunction(e){
  const val = e.detail.value
  this.setData({
    cIndex: val,
    aIndex0,
    city: placeArray[this.data.pIndex].city[val].name,
    area: placeArray[this.data.pIndex].city[val].area[0]
  })
},
changeAreafunction(e){
  const val = e.detail.value
  this.setData({
    aIndex: val,
    area: placeArray[this.data.pIndex].city[this.data.cIndex].area[val]
  })
},

他们的做用就是把当前选择列的选中元素(出如今indicator-style视野中的元素)暴露到页面上,并将下标定位到这里 —— 以便在页面无刷新下的下一次点开时从这里开始找!而后最重要的一点就是:在滑动中止时,将另外两列的数据从新定位到第一个!
——固然,你也能够选择在一个picker-view中放置多个picker-view-column组件,这样的话就和上面多列picker同样,须要多个数组联动来传递数据了!image.png



最后

  • 欢迎加我微信(winty230),拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,作个专业的技术人...

image.png

相关文章
相关标签/搜索