微信小程序日期+时间选择器

最近在做一些小程序项目,应项目需求开始学习wxml、wxss和js语法,其中有个地方需要用到选择器。在iOS中使用UIPickerView控件可以完成。同样在官方文档中也可以找到picker组件,微信小程序组件-picker

这种内置定义好的选择器都是从底部弹起。目前支持五种选择器,通过设置mode来区分。分别是普通选择器,多列选择器,时间选择器,日期选择器,省市区选择器,默认是普通选择器。

先贴上需求效果:

这里我使用多列选择器,普通选择器、日期选择器和时间选择器没法实现。

首先定义在wxml中定义picker组件:

<picker class='time-picker' mode="multiSelector" bindchange="bindStartMultiPickerChange" bindtap='pickerTap' bindcolumnchange="bindMultiPickerColumnChange" value="{{multiIndex}}" range="{{multiArray}}">{{startDate}}</picker>

这里定义的range为一个二维数组,现在数据是写定的:

// .js
multiArray: [['今天', '明天', '3-2', '3-3', '3-4', '3-5'], [0, 1, 2, 3, 4, 5, 6], [0, 10, 20]],

很明显这里的数据并不符合要求,按照需求,这里应该是展示当前的日期格式为:月-日 时 分
其中月-日列为:今天,明天,然后往后延28天。
时这里为0~23点 24个选项
分钟这里按照0~10,10~20,20~30,30~40,40~50来分段显示!

修改js代码:

pickerTap:function() {
    var date = new Date();

    var monthDay = ['今天','明天'];
    var hours = [];
    var minute = [];

    // 月-日
    for (var i = 2; i <= 28; i++) {
      var date1 = new Date(date);
      date1.setDate(date.getDate() + i);
      var md = (date1.getMonth() + 1) + "-" + date1.getDate();
      monthDay.push(md);
    }

    // 时
    for (var i = 0; i < 24; i++) {
      hours.push(i);
    }

    // 分
    for (var i = 0; i < 60; i += 10) {
      minute.push(i);
    }

    var data = {
      multiArray: this.data.multiArray,
      multiIndex: this.data.multiIndex
   };
    data.multiArray[0] = monthDay;
    data.multiArray[1] = hours;
    data.multiArray[2] = minute;
    this.setData(data);
},

在pickerTap函数中定义三个数组分别储存月-日、时、分。

然后在当前日期上往后延2-28天,并分别push到monthDay数组中。时和分比较好添加。添加完毕最后赋值给multiArray。在编译的话月-日、时、分总算完整了。

但是发现选择今天的时候,时、分是可以在0~23、0~50之间选择的。如果现在是上午9点,我们在选择今天的时间候就只能选择9点以后的了。所以这里需要改进一下。如果选择今天,那么时、分中只能是当前时间往后的选项。

这里还有一点需要注意,如果今天时间是9:55。那么选项中首条应该怎么展示。首先时这里应该是10点往后延,而分就是0~50了。所以在获取到当前分还需要做判断才能给hours和minute赋值,具体看一下代码:

var currentHours = date.getHours();
    var currentMinute = date.getMinutes();

    // 月-日
    for (var i = 2; i <= 28; i++) {
      var date1 = new Date(date);
      date1.setDate(date.getDate() + i);
      var md = (date1.getMonth() + 1) + "-" + date1.getDate();
      monthDay.push(md);
    }

    var minuteIndex;
    if (currentMinute > 0 && currentMinute <= 10) {
      minuteIndex = 10;
    } else if (currentMinute > 10 && currentMinute <= 20) {
      minuteIndex = 20;
    } else if (currentMinute > 20 && currentMinute <= 30) {
      minuteIndex = 30;
    } else if (currentMinute > 30 && currentMinute <= 40) {
      minuteIndex = 40;
    } else if (currentMinute > 40 && currentMinute <= 50) {
      minuteIndex = 50;
    } else {
      minuteIndex = 60;
    }

    if (minuteIndex == 60) {
      // 时
      for (var i = currentHours+1; i < 24; i++) {
        hours.push(i);
      }

      // 分
      for (var i = 0; i < 60; i += 10) {
        minute.push(i);
      }

    } else {
      // 时
      for (var i = currentHours; i < 24; i++) {
        hours.push(i);
      }

      // 分
      for (var i = minuteIndex; i < 60; i += 10) {
        minute.push(i);
      }
 }

首先获取当前时分,并给分值分段。如果minuteIndex = 60,也就是当前分为50~60之间。这种情况下就是当前时+1,分为0~50。否则就是当前时~23点,当前分在那个分段~50这样显示。

虽然这样改进之后第一次点开picker展示没问题。但是在开始选择时就不行了。当时选择17,这里的分展示肯定有问题。所以这里的数据我们还需要根据每列的选择来变动。

具体需求:
当选择今天。时为当前时,那么分就展示当前分段~50选项。
当选择今天。时不为当前时,那么分就展示 0~50选项。
当第一列不为今天,那么时就为 0~23选项,分就为0~50的选项。
按照需求来代码实现:

bindMultiPickerColumnChange:function(e) {

    date = new Date();

    var that = this;

    var monthDay = ['今天', '明天'];
    var hours = [];
    var minute = [];

    currentHours = date.getHours();
    currentMinute = date.getMinutes();

    var data = {
      multiArray: this.data.multiArray,
      multiIndex: this.data.multiIndex
    };
    // 把选择的对应值赋值给 multiIndex
    data.multiIndex[e.detail.column] = e.detail.value;

    // 然后再判断当前改变的是哪一列,如果是第1列改变
    if (e.detail.column === 0) {
      // 如果第一列滚动到第一行
      if (e.detail.value === 0) {

        that.loadData(hours, minute);
        
      } else {
        that.loadHoursMinute(hours, minute);
      }

      data.multiIndex[1] = 0;
      data.multiIndex[2] = 0;

      // 如果是第2列改变
    } else if (e.detail.column === 1) {

      // 如果第一列为今天
      if (data.multiIndex[0] === 0) {
        if (e.detail.value === 0) {
          that.loadData(hours, minute);
        } else {
          that.loadMinute(hours, minute);
        }
        // 第一列不为今天
      } else {
        that.loadHoursMinute(hours, minute);
      }
      data.multiIndex[2] = 0;

      // 如果是第3列改变
    } else {
      // 如果第一列为'今天'
      if (data.multiIndex[0] === 0) {

        // 如果第一列为 '今天'并且第二列为当前时间
        if(data.multiIndex[1] === 0) {
          that.loadData(hours, minute);
        } else {
          that.loadMinute(hours, minute);
        }
      } else {
        that.loadHoursMinute(hours, minute);
      }
    }
    data.multiArray[1] = hours;
    data.multiArray[2] = minute;
    this.setData(data);
  },

  loadData: function (hours, minute) {
    var minuteIndex;
    if (currentMinute > 0 && currentMinute <= 10) {
      minuteIndex = 10;
    } else if (currentMinute > 10 && currentMinute <= 20) {
      minuteIndex = 20;
    } else if (currentMinute > 20 && currentMinute <= 30) {
      minuteIndex = 30;
    } else if (currentMinute > 30 && currentMinute <= 40) {
      minuteIndex = 40;
    } else if (currentMinute > 40 && currentMinute <= 50) {
      minuteIndex = 50;
    } else {
      minuteIndex = 60;
    }

    if (minuteIndex == 60) {
      // 时
      for (var i = currentHours + 1; i < 24; i++) {
        hours.push(i);
      }
      // 分
      for (var i = 0; i < 60; i += 10) {
        minute.push(i);
      }
    } else {
      // 时
      for (var i = currentHours; i < 24; i++) {
        hours.push(i);
      }
      // 分
      for (var i = minuteIndex; i < 60; i += 10) {
        minute.push(i);
      }
    }
  },

  loadHoursMinute: function (hours, minute){
    // 时
    for (var i = 0; i < 24; i++) {
      hours.push(i);
    }
    // 分
    for (var i = 0; i < 60; i += 10) {
      minute.push(i);
    }
  },

  loadMinute: function (hours, minute) {
    var minuteIndex;
    if (currentMinute > 0 && currentMinute <= 10) {
      minuteIndex = 10;
    } else if (currentMinute > 10 && currentMinute <= 20) {
      minuteIndex = 20;
    } else if (currentMinute > 20 && currentMinute <= 30) {
      minuteIndex = 30;
    } else if (currentMinute > 30 && currentMinute <= 40) {
      minuteIndex = 40;
    } else if (currentMinute > 40 && currentMinute <= 50) {
      minuteIndex = 50;
    } else {
      minuteIndex = 60;
    }

    if (minuteIndex == 60) {
      // 时
      for (var i = currentHours + 1; i < 24; i++) {
        hours.push(i);
      }
    } else {
      // 时
      for (var i = currentHours; i < 24; i++) {
        hours.push(i);
      }
    }
    // 分
    for (var i = 0; i < 60; i += 10) {
      minute.push(i);
    }
  },

简要说明一下。picker每列数据变动事都会调bindMultiPickerColumnChange函数。首先每次拿到最新时间,并更新全局当前时、分:

date = new Date();
currentHours = date.getHours();
currentMinute = date.getMinutes();

拿到page-data里面的multiArray:元数据数组和muliIndex:picker改变数据数组:

multiArray: this.data.multiArray,
multiIndex: this.data.multiIndex

picker改动的列索引和对应的值在e.detail中,所以每次改变,就把值赋值给multiIndex:

data.multiIndex[e.detail.column] = e.detail.value;

现在开始判断了,如果第一列或第二列或第三列发生改变:

if (e.detail.column === 0) {
    // 如果第一列滚动到第一行
   if (e.detail.value === 0) {
      var minuteIndex;
    if (currentMinute > 0 && currentMinute <= 10) {
      minuteIndex = 10;
    } else if (currentMinute > 10 && currentMinute <= 20) {
      minuteIndex = 20;
    } else if (currentMinute > 20 && currentMinute <= 30) {
      minuteIndex = 30;
    } else if (currentMinute > 30 && currentMinute <= 40) {
      minuteIndex = 40;
    } else if (currentMinute > 40 && currentMinute <= 50) {
      minuteIndex = 50;
    } else {
      minuteIndex = 60;
    }

    if (minuteIndex == 60) {
      // 时
      for (var i = currentHours + 1; i < 24; i++) {
        hours.push(i);
      }
      // 分
      for (var i = 0; i < 60; i += 10) {
        minute.push(i);
      }
    } else {
      // 时
      for (var i = currentHours; i < 24; i++) {
        hours.push(i);
      }
      // 分
      for (var i = minuteIndex; i < 60; i += 10) {
        minute.push(i);
      }
    }
 } else {
      // 时
    for (var i = 0; i < 24; i++) {
      hours.push(i);
    }
    // 分
    for (var i = 0; i < 60; i += 10) {
      minute.push(i);
    }
 }
    data.multiIndex[1] = 0;
    data.multiIndex[2] = 0;
}

如果是第一列发生改变,并且是滚动到0,也就是滚动到 '今天' 的时候,第二列和第三列对应的应该变为当前时和分来显示。这里的minuteIndex定义为局部变量,是把当前分划分到哪个时间段的。如果在0~10之内,那么分钟显示为10、20、30、40、50。如果在40~50之间的话,分钟应该显示为50。如果分钟在50~60之间的话,这个时候小时应该+1,并且分钟为0、10、20、30、40、50。

如果当前为18:54,那么显示应该如下:

上面是对第一列为 '今天的操作',如果第一列不为今天,那么第二列和第三列就应该显示全部数据。也就是下面这段代码:

 // 时
for (var i = 0; i < 24; i++) {
    hours.push(i);
}
    // 分
for (var i = 0; i < 60; i += 10) {
    minute.push(i);
}

给hours和minute赋值之后,最后再赋值给multiArray就可以显示出来了。
最后一一步就是在第一列改变的时候,第二列和第三列相应都滚动到0号位。

data.multiIndex[1] = 0;
data.multiIndex[2] = 0;

接下来是第二列改变的判断:

else if (e.detail.column === 1) {
    // 如果第一列为今天
  if (data.multiIndex[0] === 0) {
  if (e.detail.value === 0) {
      var minuteIndex;
    if (currentMinute > 0 && currentMinute <= 10) {
      minuteIndex = 10;
    } else if (currentMinute > 10 && currentMinute <= 20) {
      minuteIndex = 20;
    } else if (currentMinute > 20 && currentMinute <= 30) {
      minuteIndex = 30;
    } else if (currentMinute > 30 && currentMinute <= 40) {
      minuteIndex = 40;
    } else if (currentMinute > 40 && currentMinute <= 50) {
      minuteIndex = 50;
    } else {
      minuteIndex = 60;
    }

    if (minuteIndex == 60) {
      // 时
      for (var i = currentHours + 1; i < 24; i++) {
        hours.push(i);
      }
      // 分
      for (var i = 0; i < 60; i += 10) {
        minute.push(i);
      }
    } else {
      // 时
      for (var i = currentHours; i < 24; i++) {
        hours.push(i);
      }
      // 分
      for (var i = minuteIndex; i < 60; i += 10) {
        minute.push(i);
      }
    }
  } else {
      var minuteIndex;
    if (currentMinute > 0 && currentMinute <= 10) {
      minuteIndex = 10;
    } else if (currentMinute > 10 && currentMinute <= 20) {
      minuteIndex = 20;
    } else if (currentMinute > 20 && currentMinute <= 30) {
      minuteIndex = 30;
    } else if (currentMinute > 30 && currentMinute <= 40) {
      minuteIndex = 40;
    } else if (currentMinute > 40 && currentMinute <= 50) {
      minuteIndex = 50;
    } else {
      minuteIndex = 60;
    }

    if (minuteIndex == 60) {
      // 时
      for (var i = currentHours + 1; i < 24; i++) {
        hours.push(i);
      }
    } else {
      // 时
      for (var i = currentHours; i < 24; i++) {
        hours.push(i);
      }
    }
    // 分
    for (var i = 0; i < 60; i += 10) {
      minute.push(i);
    }
    }
      // 第一列不为今天
   } else {
     // 时
    for (var i = 0; i < 24; i++) {
      hours.push(i);
    }
    // 分
    for (var i = 0; i < 60; i += 10) {
      minute.push(i);
    }
   }
    data.multiIndex[2] = 0;
}

如果第二列发生改变,要根据第一列的位置来改变hours和minute的值。所以如果第一列为0,也就是'今天',并且第二列也为0,那么第二列和第三列应该根据当前时间进行显示。如果第二列不为0。那么第二列根据当前时来显示,但是第三列应该是0~50全部显示。如果第一列不为'今天'。这个时候第二列和第三列都应该全部显示,也就是0~23、0~50。最后需要滚动第三列到0号位:

data.multiIndex[2] = 0;

同样如果第三列发生改变,也要根据第一列和第二列的位置来显示。如果第一列为0,第二列为0,那么这里的hours和minute应该根据当前时间来显示。如果第二列不为0,那么minute应该是0~50显示全部选项。最后如果第一列也不为0,那么hours和minute都应该全部显示。

最后把hours和minute赋值到data:

data.multiArray[1] = hours;
data.multiArray[2] = minute;
this.setData(data);

写到这里,最后的效果就如开头展示的那样。

第一次写小程序文章,如果有错误或者讲得不到位的地位欢迎留言讨论,谢谢。

最后补充一下,如果currentHours或currentHours+1 大于等于24的话要进行判断处理。

本文源码:GitHub WXProgram

推荐阅读更多精彩内容

  • 在一些电商类的微信小程序中,地址选择这个功能一般是必备的,一般的收货信息都需要有一个能选择省市县的控件,当然也有些...
    第九程序官方阅读 2,829评论 0 6
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 6,996评论 6 13
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 119,170评论 16 133
  • 马的意志 一匹健壮的骏马,向着远方艰难跋涉,它怀着远大的梦想,用自己的足走遍千山万水,游尽大山内外,不负从小立下的...
    南溪向南北歌流海阅读 154评论 0 0
  • 扩展NSMutableDictionary的一个类别,上面注释说的很清楚,除非value为nil的时候,调用方法 ...
    小笨憨阅读 111评论 0 0