微信小程序-自定义日期组件实现

前言

    今日份跟大佬聊到说前端基础架构平台的东西、涉及到基础组件库、公共工具类等内容, 然后期望能自我驱动带头去做, 陷入深深滴沉思, 该考虑如何做?
思绪一片混乱, 那就写篇文章冷静冷静,故有了此篇, 仅供参考。

原生picker组件

微信小程序原生有提供一套日期组件, 大概如下:
image.png

跟UI预期不一致的点有如下几个:
A. 期望弹窗居中显示、而不是从底部弹出;
B. 期望小于10的展示为1月,1日这种, 而不是01月, 01日;
C. UI样式跟微信原生差别有点大;
D. 不需要头部的取消&确定按钮、预期底部整个确定按钮即可;
想着让产品接受原生日期组件的, But拗不过产品的思维, 只能开干、自己撸一个自定义日期组件, 造轮子=.= 

自定义picker组件

既然原生的不能用, 那么我们看看小程序是否有提供这种类似的滚动器, 查看官方文档发现: 
image.png
image.png
那就开干, 为尽可能保持代码的最小颗粒度(这里不考虑弹窗外壳的封装、纯日期组件).
话不多说、这里直接贴上代码、预留的坑位都会在代码内有备注, 请参考:

// 组件wxml
<!-- 预留坑位: 按道理该日期组件应该是做在弹窗上的、这里为了简化代码故直接写在了页面上; 
     后期使用者烦请自己做个弹窗套上、用showModal属性控制其显示隐藏-->
<view class="picker" wx:if="{{showModal}}">
   <picker-view indicator-class="picker-indicator" value="{{pickerIndexList}}" bindchange="bindChangeDate">
      <picker-view-column>
        <view wx:for="{{yearList}}" wx:key="index" class="{{pickerIndexList[0]==index?'txt-active':''}}">{{item}}年</view>
      </picker-view-column>
      <picker-view-column>
        <view wx:for="{{monthList}}" wx:key="index" class="{{pickerIndexList[1]==index?'txt-active':''}}">{{item}}月</view>
      </picker-view-column>
      <picker-view-column>
        <view wx:for="{{dayList}}" wx:key="index" class="{{pickerIndexList[2]==index?'txt-active':''}}">{{item}}日</view>
      </picker-view-column>
    </picker-view>
    <!-- 预留坑位: 日期组件可能仅允许数据回选、不允许修改。
    思路: 通过自定义蒙层盖在日期控件上从而达到禁止控件滚动的效果.
     -->
    <view wx:if="{{!canEdit}}" class="disabled-picker"></view>
</view>

// 组件wxss
.picker{
    position: relative;
    height: 300rpx;
    width: 600rpx;
    margin: 0 auto;
    border: 1rpx solid red;
}
.picker picker-view {
    width: 100%;
    height: 100%;
}
.picker-indicator {
    height: 60rpx;
    line-height: 60rpx;
}
.picker picker-view-column view {
    font-size: 40rpx;
    line-height: 60rpx;
    text-align: center;
}
.txt-active {
    color: #2c2c2c;
}
/* 预留坑位: 为便于区分真的有遮罩层盖住、特意加了个背景色、实际使用过程可改成透明色 */
    .disabled-picker{
    width: 600rpx;
    position: absolute;
    top: 0;
    left: 0;
    height: 300rpx;
    z-index: 999;
    background: rgb(255,222,173,0.7);
}


// 组件js
Component({
  properties: {},
  data: {
    yearList: [],
    monthList: [],
    dayList: [],
    pickerIndexList: [0, 0, 0]
  },
  methods: {
    // dateString格式: 'YYYY-MM-DD'
    initPicker (dateString) {
      let nowDate = new Date()
      // 预留个坑位: 若需要指定某一日期则从外面传入、否则默认当天
      if(dateString){
        // 预留个坑位: 判定传入的数据类型是否符合要求、若不符合该报错的报错
        nowDate = new Date(dateString)
      }

      // 预留个坑位: 因为下面的日期指定在1900.01.01-2100.12.31、故这里最好校验下传入日期是否在区间内.
      let nowYear = nowDate.getFullYear()
      let nowMonth = nowDate.getMonth() + 1
      let yearList = this.getYearList(nowYear)
      let monthList = this.getMonthList()
      let dayList = this.getDayList(nowYear, nowMonth)

      // 获取多列选择器的选中值下标
      let pickerIndexList = []
      pickerIndexList[0] = yearList.findIndex(o => o === nowDate.getFullYear())
      pickerIndexList[1] = monthList.findIndex(o => o === nowDate.getMonth()+1)
      pickerIndexList[2] = dayList.findIndex(o => o === nowDate.getDate())
      this.setData({
        yearList,
        monthList,
        dayList,
        pickerIndexList,
        showModal: true
      })
    },
    // 获取年份
    getYearList (nowYear) {
      let yearList = []
      if(nowYear < 1900 || nowYear > 2100){
        return false
      }
      for (let i = 1900; i <= 2100; i++) {
        yearList.push(i)
      }
      return yearList
    },
    // 获取月份
    getMonthList () {
      let monthList = []
      for (let i = 1; i <= 12; i++) {
        monthList.push(i)
      }
      return monthList
    },
    // 获取日期 -> 根据年份&月份
    getDayList (year, month) {
      let dayList = []
      month = parseInt(month, 10) 
      // 特别注意: 这里要根据年份&&月份去计算当月有多少天[切记切记]
      let temp = new Date(year, month, 0)
      let days = temp.getDate()
      for (let i = 1; i <= days; i++) {
        dayList.push(i)
      }
      return dayList
    },
    // 日期选择改变事件
    bindChangeDate (e) {
      let pickerColumnList = e.detail.value
      const { yearList=[], monthList=[] } = this.data
      const nowYear = yearList[pickerColumnList[0]]
      const nowMonth = monthList[pickerColumnList[1]]
      this.setData({
        dayList: this.getDayList(nowYear, nowMonth),
        pickerIndexList: pickerColumnList
      })
    },
    show (birthday) {
      // 预留坑位: 这里也许会有一定的逻辑判定是否允许编辑日期控件, 故预留canEdit属性去控制
      this.setData({
        canEdit: true
      })
      this.initPicker(birthday)
    },
    // 预留坑位、点击确定按钮获取到选中的日期
    surePicker () {
      const { pickerIndexList, yearList, monthList, dayList } = this.data
      // 预留坑位: 月份&日期补0
      let txtDate = `${yearList[pickerIndexList[0]]}-${monthList[pickerIndexList[1]]}-${dayList[pickerIndexList[2]]}`
      console.log(txtDate)
    },
  }
})
复制代码

接下来我们看看使用方是怎么使用的?

// 页面wxml
<!-- 预留坑位: 这里仅展示触发事件、开发者替换成实际业务即可-->
  <view bind:tap="openPicker" style="margin:20rpx; text-align:center;">打开日期控件</view>

// 页面json: 记得在使用页面的json处引入该组件、配置组件路径

// 页面js
  methods: {
    openPicker (){
      // 获取组件实例、这里可选择是否传入日期
      this.date_picker = this.selectComponent && this.selectComponent('#date_picker')
      this.date_picker && this.date_picker.show()
    },
  }
复制代码

一切准备就绪、我们看看效果图!
这是日期可编辑时、你是可滚动选择器的:
image.png
我们看看日期不可编辑时、仅可查看的效果图:
image.png
样式是稍微有点丑、到时开发者按照实际UI去做微调即可、这不难的=.=.
这里预留了几个扩展点:
1.支持外部传入日期、默认选中预设值;
2.支持在弹窗内显示日期控件、需要使用者自行开发弹窗;
3.支持日期控件仅可查看、不可编辑;
4.支持日期控件的关闭、一般是弹窗上有个关闭按钮或者是点击弹窗的蒙层可关闭、使用者自行开发;
Tips: 具体的代码改动点都有在上面的code中有备注、欢迎对号改代码, 若有任何不懂的欢迎留言或者私信、很愿意帮您解答。

写在最后

若有错误之处, 恳请留言, 定会及时更正!
若觉着对您有帮助的话恳请点个赞或着收藏吧!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享