taro(vue)-日期时间选择器picker(无默认)

2,555 阅读29分钟

简介:

Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发 微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ 小程序 / H5 等应用。现如今市面上端的形态多种多样,Web、React Native、微信小程序等各种端大行其道,当业务要求同时在不同的端都要求有所表现的时候,针对不同的端去编写多套代码的成本显然非常高,这时候只编写一套代码就能够适配到多端的能力就显得极为需要。

 前景:

我司某项目某页面需要一个日期时间的五列选择器,我在taro的组件库之picker中找了又找,只发现了mode=date的日期选择器和mode=time的时间选择器,如下:

               

 由于Taro是基于微信小程序仿照的,截图来源如微信小程序的官方demo

需求:

组件库picker功能很完善和强大,日期和时间的都有,然后项目中要求的选择器如下

 问题:

what???需要既可以选日期又可以选时间的picker选择器,但目前picker组件只能要么选日期要么选时间,想省事儿的我当时的第一想法就是能不能让设计把时间选择器拆开为两个,这样就直接可以套用原有的组件了。但是我仔细看了下设计,同页面有活动时间和截止时间两个地方需要选择,如果拆开的话,可能样式不是那么美观。再加上技术的角度不能遇到点小困难就换设计(虽然大多数都换不了),下定决心的我决心自己写一个

 实现:

仔细看了下组件库的picker,mode=date,mode=time对我是没啥用了,mode=multiSelector自定义的多列选择器,仔细看了下小程序的picker的demo,只需要控制range的数据为[[year],[month],[day],[hour],[minute]],picker组件即可展示出五列。组件库中还有个rangeKey属性,由于页面中展示的选择器一般都是xxx年xx月,但是提交给服务端的一般都是数字,所以该组件我采用的是Object Array结构,[{name:‘1997年’,id:1997}],展示的时候用:rangeKey="'name'",即可展示name的字段,提交数据的时候用id字段。还有就是我项目初始时间是当前时间往后推30分钟 

 具体过程:

有了以上的思路,接下来就好办了。是需要实现[year],[month],[day],[hour],[minute]的数据即可。

**[year]:**只需要按照需求给定一个起始时间和结束时间,for循环的出来数据即可。我项目的需求是当前时间往后年推50年。

**[month]:**月份的情况和年份差不多,基本上都是固定的,只需要看年份=当前年的话,月份可能不足12个月。

**[day]:**天数的情况算是稍微麻烦点的,因为每个月有多少天都是需要计算的,尤其是二月份还分平年和瑞年,一个28一个29天,如果按照这个规律计算的话,比较麻烦。我这里借助日期对象Date,注意根据Date对象的两个规则如下:

我们可以给Date传参为需要计算的天数的月份+1,day传0,根据date的自动调整原则,实际得到的就是该天数的最后一天。如下:

 var getDaysInOneMonth = function (year, month) {
  let _month = parseInt(month, 10);
  let d = new Date(year, _month, 0); //_month假如为2的话,实际得到的就是1月份的最后一天
  return d.getDate();
} 

**[hour]:**同month,只需要注意年月日刚好为当前时间的时候,初始值的设置 

**[minute]:**同month,只需要注意年月日时刚好为当前时间的时候,初始值的设置 

代码展示:

**第一步、**dateTimePicker.js 

首选需要一个用来获取[[year],[month],[day],[hour],[minute]]的数据的方法,由于年份是不需要每次都重新计算,有避免全局污染,这里我采用了闭包函数,整体代码如下:

var getDaysInOneMonth = function (year, month) {
  let _month = parseInt(month, 10);
  let d = new Date(year, _month, 0);
  return d.getDate();
}
var dateDate = function (date) {
  let year = date.getFullYear();
  let month = date.getMonth()+1;
  let day = date.getDate();
  let hours = date.getHours();
  let minutes = date.getMinutes();
  // console.log({
  //   year, month, day, hours, minutes
  // })
  return {
    year, month, day, hours, minutes
  }
}
var dateTimePicker = function (inityear) {
  // 获取date time  年份,月份,天数,小时,分钟推后30分
  const years = [];
  for (let i = inityear; i <= inityear + 50; i++) {
    years.push({
      name: i + '年',
      id: i
    });
  }
  return function (_year, _month, _day, _hour, _minute) {
    const months = [];
    const days = [];
    const hours = [];
    const minutes = [];
    _year = parseInt(_year);
    _month = parseInt(_month);
     _day = parseInt(_day);
    _hour = parseInt(_hour);
     _minute = parseInt(_minute);
    //获取月份
    for (let i = _month; i <= 12; i++) {
      if (i < 10) {
        i = "0" + i;
      }
      months.push({
        name: i + '月',
        id: i
      });
    }
    //获取日期
    for (let i = _day; i <= getDaysInOneMonth(_year, _month); i++) {
      if (i < 10) {
        i = "0" + i;
      }
      days.push({
        name: i + '日',
        id: i
      });
    }
    //获取小时
    for (let i = _hour; i < 24; i++) {
      if (i < 10) {
        i = "0" + i;
      }
      hours.push({
        name: i + '时',
        id: i
      });
    }
    //获取分钟
    for (let i = _minute; i < 60; i++) {
      if (i < 10) {
        i = "0" + i;
      }
      minutes.push({
        name: i + '分',
        id: i
      });
    }
    return [years, months, days, hours, minutes];
  }
}
export {
  dateTimePicker,
  getDaysInOneMonth,
  dateDate
}

**第二步、**five-picker.vue组件

需要注意的就是选择列的时候需要改变每列数据的值,例如选择了年,则需要改变月,天,时,分的数据。

<template>
  <picker
    mode="multiSelector"
    :rangeKey="'name'"
    @change="bindMultiPickerChange"
    @columnChange="bindMultiPickerColumnChange"
    :value="timeIndex"
    :range="activityArray"
  >
    <slot></slot>
  </picker>
</template>
<script>
import { dateTimePicker, dateDate } from "../dateTimePicker.js";
let datePicker,date;
export default {
  props:{
    startTime:{
      type:String,
      default:new Date()
    }
  },
  data() {
    return {
      timeIndex: [0, 0, 0, 0, 0],
      activityArray: [],
      year: 0,
      month: 1,
      day: 1,
      hour: 0,
      minute: 0,
    };
  },
  created() {
    date = new Date(this.startTime);
    datePicker = dateTimePicker(date.getFullYear());
    this.setDateData();
  },
  methods: {
    setDateData() {
      console.log(654656);
      let _data = dateDate(date);
      this.activityArray = datePicker(
        _data.year,
        _data.month,
        _data.day,
        _data.hours,
        _data.minutes
      );
    },
    bindMultiPickerChange(e) {
      console.log("picker发送选择改变,携带值为", e.detail.value);
      let activityArray = JSON.parse(JSON.stringify(this.activityArray)),
        { value } = e.detail,
        _result = [],
        timeIndex = this.timeIndex;
      for (let i = 0; i < timeIndex.length; i++) {
        _result[i] = activityArray[i][value[i]].id;
      }
      this.$emit("result", _result);
    },
    bindMultiPickerColumnChange(e) {
      console.log("修改的列为", e.detail.column, ",值为", e.detail.value);
      let _data = JSON.parse(JSON.stringify(this.activityArray)),
        timeIndex = JSON.parse(JSON.stringify(this.timeIndex)),
        { column, value } = e.detail,
        _value = _data[column][value].id;
      switch (e.detail.column) {
        case 0:
          this.year = _value;
          value === 0
            ? this.setDateData()
            : (this.activityArray = datePicker(_value, 1, 1, 0, 0));
          timeIndex = [value, 0, 0, 0, 0];
          this.timeIndex = timeIndex;
          break;
        case 1:
          this.month = _value;
          this.timeIndex = JSON.parse(
            JSON.stringify([timeIndex[0], value, 0, 0, 0])
          );
          _data[2] = datePicker(this.year, this.month, 1, 0, 0)[2];
          _data[3] = datePicker(this.year, this.month, 1, 0, 0)[3];
          _data[4] = datePicker(this.year, this.month, 1, 0, 0)[4];
          this.activityArray = _data;
          break;
        case 2:
          this.day = _value;
          this.timeIndex = JSON.parse(
            JSON.stringify([timeIndex[0], timeIndex[1], value, 0, 0])
          );
          _data[3] = datePicker(this.year, this.month, this.day, 0, 0)[3];
          _data[4] = datePicker(this.year, this.month, this.day, 0, 0)[4];
          this.activityArray = _data;
          break;
        case 3:
          this.hour = _value;
          timeIndex[3] = value;
          timeIndex[4] = 0;
          this.timeIndex = timeIndex;
          _data[4] = datePicker(
            this.year,
            this.month,
            this.day,
            this.hour,
            0
          )[4];
          this.activityArray = _data;
          break;
        case 4:
          timeIndex[4] = value;
          this.timeIndex = timeIndex;
          break;
      }
    },
  },
};
</script>

 这里面要注意的就是获取选择器数据和索引的时候一定要加深拷贝, let _data = JSON.parse(JSON.stringify(this.activityArray)), timeIndex = JSON.parse(JSON.stringify(this.timeIndex)), 不然下面this.activityArray赋值为新数组的时候,视图一直不更新,这个问题坑了我蛮久 

 组件引用: 

import FivePicker from "../components/five-picker";
<FivePicker @result="onResult2">
  <CCell
    title="报名截止时间"
    placeholder="请选择时间"
    :value="deadline"
  />
</FivePicker>

onResult2(arr) { arr为你选择完时候得到的数组
  this.deadline =
    arr[0] + "." + arr[1] + "." + arr[2] + " " + arr[3] + ":" + arr[4];
},

友情提示:

该组件也是我写项目时临时开发的,并没有真正的适应所有的情况下的日期时间选择器,在初始值那块可能好需要你自己灵活的改动下 

仓库地址:

github.com/xml19781579…