小程序开发技巧:如何实现带上午和下午的时间选择器?

1,484 阅读5分钟

本文正在参加「金石计划」

前言

在本篇文章中,我们将会学习如何使用 picker 组件实现一个带有上午和下午的时间选择器,并且探讨其中涉及到的一些关键技术点。无论你是小程序初学者还是有一定经验的开发者,本文都将为你提供有益的参考和指导。

背景

在我们日常生活中,经常需要进行预约或打卡等操作,而这些操作通常需要选择具体的上下午时间。因此,在小程序中实现一个带有上午和下午的时间选择器,对于提高用户体验和操作效率都具有重要意义。

最近我就遇见这么一个需求,请假的时候需要在表单中填写请的上午还是下午,但是翻阅了市面上存在的时间选择器都没有含上下午的时间选择器,所以没办法只有自行来实现这么一个时间选择器功能。下面就是我实现的效果,如下图所示:

pic2.png

开发

下面我就来详细讲解如何使用小程序提供的 picker 组件以及如何处理时间数据和界面展示等方面的问题,让你也能更好地理解和掌握实现带有上午和下午的时间选择器的方法。

页面布局

<view class="contanier">
  <view class="item" bindtap="openPickerModal">
    <view class="label">开始时间:</view>
    <view class="value">{{selectTime == '' ? '选择时间' : selectTime}}</view>
  </view>
  
  <!-- Picker -->
  <view class="modal" wx:if="{{isShowPicker}}">
    <view class="modal-body">
      <view class="top">
        <view class="noBtn" bindtap="onCancel">取消</view>
        <view class="okBtn" bindtap="onOk">确定</view>
      </view>
      <picker-view class="content" value="{{value}}" bindchange="onChangePicker">
        <picker-view-column>
          <view class="column-item" wx:for="{{years}}" wx:key="years">{{item}}年</view>
        </picker-view-column>
        <picker-view-column>
          <view class="column-item" wx:for="{{months}}" wx:key="months">{{item}}月</view>
        </picker-view-column>
        <picker-view-column>
          <view class="column-item" wx:for="{{days}}" wx:key="days">{{item}}日</view>
        </picker-view-column>
        <picker-view-column>
          <view class="column-item">上午</view>
          <view class="column-item">下午</view>
        </picker-view-column>
      </picker-view>
    </view>
  </view>
</view>

首先界面布局其实就是一个触发选择时间的盒子和该时间选择器的容器。然后就是使用小程序提供的picker-view组件,并在其内部添加4picker-view-column,分别用来填装选择器的年、月、日和上下午的数据。

样式文件

.contanier .item {
  display: flex;
  align-items: center;
  padding: 20rpx;
}

.contanier .item .value {
  flex: 1;
  padding: 10rpx 20rpx;
  border: 1px solid #ddd;
  border-radius: 12rpx;
}

.modal {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 999;
  height: 100%;
  width: 100%;
  background-color: rgba(0,0,0,.4);
}

.modal .modal-body {
  background-color: #fff;
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  z-index: 9999;
}
.modal .modal-body .top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24rpx 30rpx;
  font-size: 32rpx;
  color: #333;
  border-bottom: 1px solid #ddd;
}
.modal .modal-body .top .noBtn {
  color: #999;
}
.modal .modal-body .top .okBtn {
  color: #0084ff;
}

.modal .modal-body .content {
  width: 100%; 
  height: 400rpx;
}

.modal .modal-body .content .column-item {
  display: flex;
  justify-content: center;
  align-items: center;
}

样式文件也很简单,就是使用flex布局和position定位来处理界面样式。其中时间选择器的外部盒子使用了fixed进行固定,让它的层级为999并设置其为蒙层;其次就是时间选择器的内容区域,让其处于蒙层的上方,并设置它的层级为9999

逻辑代码

Page({
  data: {
    years: [], // 年份数据集合
    months: [], // 月份数据集合
    days: [], // 天数数据集合
    bigMonth: ['01', '03', '05', '07', '08', '10', '12'], // 大月份数据集合
    year: '',
    month: '',
    day: '',
    value: [],
    isDaytime: true, // true:上午;false:下午
    isShowPicker: false, // 选择时间弹窗显示隐藏标识

    selectTime: '' // 默认时间
  },

  // 选择时间
  openPickerModal () {
    this.initPicker()
    this.setData({
      isShowPicker: true
    })
  },

  // 初始化
  initPicker () {
    const date = this.data.selectTime ? new Date(this.data.selectTime.substring(0, 10)) : new Date() // 默认选中日期

    let years = []
    let months = []
    let days = []

    const getYear = date.getFullYear()
    const getMonth = date.getMonth()
    const getDate = date.getDate()

    // 年(取前后20年)
    for (let i = getYear - 20; i <= getYear + 20; i++) {
      years.push(i);
    }

    // 月
    for (let i = 1; i <= 12; i++) {
      months.push(i < 10 ? `0${i}` : i)
    }

    // 日
    for (let i = 1; i <= 31; i++) {
      days.push(i < 10 ? `0${i}` : i)
    }

    this.setData({
      years: years,
      months: months,
      days: days,
      year: getYear,
      month: (getMonth + 1) < 10 ? `0${(getMonth + 1)}` : (getMonth + 1),
      day: getDate < 10 ? `0${getDate}` : getDate,
      value: [20, getMonth, getDate - 1]
    })
  },

  // 设置天数
  setDays (day) {
    let temp = [];
    for (let i = 1; i <= day; i++) {
      temp.push(i < 10 ? `0${i}` : i)
    }
    this.setData({
      days: temp,
    })
  },

  // Picker改变事件
  onChangePicker (e) {
    const val = e.detail.value
    const yearStr = this.data.years[val[0]]
    const monthStr = this.data.months[val[1]]
    const datStr = this.data.days[val[2]]
    // 闰年
    if (monthStr == 2) {
      if (yearStr % 4 === 0 && yearStr % 100 !== 0) {
        this.setDays(29);
      } else {
        this.setDays(28);
      }
    } else {
      // 大月
      if (this.data.bigMonth.includes(monthStr)) {
        this.setDays(31)
      } else {
        this.setDays(30)
      }
    }
    this.setData({
      year: yearStr,
      month: monthStr,
      day: datStr,
      isDaytime: !val[3]
    })
  },

  // 取消
  onCancel () {
    this.setData({
      isShowPicker: false
    })
  },

  // 确定
  onOk () {
    const dateTimeBody = `${this.data.year}-${this.data.month}-${this.data.day}`
    const todays = this.data.isDaytime === true ? '上午' : '下午'

    // TODO: do something...

    this.setData({
      isShowPicker: false,
      selectTime: `${dateTimeBody} ${todays}`
    })
  }
})

逻辑代码也没有太复杂,更多的是处理时间的逻辑。在逻辑代码中,打开时间选择器时需要去初始化它,这个时候我们可以为其传入一个默认时间,如果有就使用默认时间进行初始化,没有就使用当前时间进行初始化,这样为以后将其封装为公用组件时提供了可能。

后面的代码逻辑就是判断时间是否是闰年,根据闰年设置2月的实际天数;然后就是判断选择的月份是否是大月,如果是大月那该月份的天数则有31天否则为30天;最后将选择的时间保存并赋值给selectTime即可在页面上进行展示。

至此,该上述代码已经可以让其在小程序里面展示为一个时间选择器弹窗了。

实现效果

下面就来看看上面的代码最终的实现效果,如下:

test1.gif

其他

其实这个需求是在开发钉钉小程序遇见的,但是为了让小伙伴们能清晰理解,就将其搬移到微信小程序中来进行演示,毕竟微信小程序的开发者还是要比钉钉的多。至于怎么让其也能在其他小程序中使用呢?那就需要根据其他小程序的语法来稍微变动上述代码即可,比如钉钉就可以按照下图改变即可使用:

pic1.png

还有在逻辑代码中,在选择完时间后需要保存数据时,我留了一块TODO,这里也可以根据实际需求来进行时间的比较,比如:开始时间不能大于结束时间之类的判断。如下:

image.png

但是这里要注意的是,小程序中获取时间的时间戳来进行比较时要转换一下时间格式,不然在苹果手机上会出现不兼容问题,最好将时间格式转为:2023/04/06 12:00:00,这样就不会在ios系统中出错了(还可以使用moment.jsday.js来处理时间)。

最好,实现带有上午和下午的时间选择器在小程序开发中是一个常见而且非常有用的功能。通过本文,我们可以了解到如何使用小程序提供的 picker 组件来实现时间选择器,并且详细讲解了如何处理时间数据、界面展示方面的问题。

希望本文对你在小程序开发中实现带有上午和下午的时间选择器有所帮助。同时,也欢迎大家分享自己的经验和想法,共同促进小程序开发技术的发展。

后语

小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走哦^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。