钉钉小程序有四个场景,一直在用的是企业内部应用开发. 钉钉小程序几乎和微信小程序一样,阔以说是搬了微信小程序过来,钉钉一些原生组件样式和功能上并不能满足我们的需求,比如钉钉小程序的picker原生组件: <picker mode="{{item.pickerData?'selector':'date'}}"..... mode为date时就是日期选择器,ios和安卓展现形式并不一致 ios展现形式: 安卓展现形式: 可以看出来ios和安卓的日期选择器展示形式并不一样,并且这个原生picker组件的时分不能去除,当然选择一个日期后我们使用了substring去掉了时分的展示,但是不能去掉弹窗里面时分的显示,可以看出来展现形式是比较丑的,应需求我们只能自定义一个年月日的picker日期选择器。

先给大家看下自定义组件最后呈现的样式(ios和安卓展现样式一致): 并且日的滚动可以无限循环,意思是向上滚动到1之后紧接着就有31、30、29,向下滚动到31后紧接着就有1、2、3,年月日picker日期选择器几乎符合设计的要求!

下面开始讲解这个年月日picker自定义组件:

ai-multi-picker.axml

< view class = "picker-button-group" > < text catchTap = "cancelChoose" > 取消 </ text > < text catchTap = "confirmChoose" > 确认 </ text > </ view > <!-- 年月日 --> < picker-view value = "{{initialValue}}" onChange = "changeTime" a:if = "{{type == 'days'}}" > < picker-view-column > < view a:for = "{{years}}" > {{item}} </ view > </ picker-view-column > < picker-view-column > < view a:for = "{{months}}" > {{item}} </ view > </ picker-view-column > < picker-view-column > < view a:for = "{{days}}" > {{item}} </ view > </ picker-view-column > </ picker-view > <!-- 年月 --> < picker-view value = "{{initialValue}}" onChange = "changeTime" a:else > < picker-view-column > < view a:for = "{{years}}" > {{item}} </ view > </ picker-view-column > < picker-view-column > < view a:for = "{{months}}" > {{item}} </ view > </ picker-view-column > </ picker-view > </ view > 复制代码

ai-multi-picker.js

Component({
  mixins: [],
  data: {
    initMinDate: new Date(2010,1,0).getTime(),
    initMaxDate: new Date().getTime(),
    initialValue: [0, 0, 0],
    years: [],
    months: [],
    days: [],
    inintDaysArr: []
  props: {
    type:'days', //展示类型 默认展示年月日 如只要月,则传type为months
    format: "yyyy/MM/dd",//返回的格式要求 1.yyyy-MM-dd 2.yyyy-MM-dd hh:mm:ss 3.yyyy/MM/dd 4.yyyy年MM月dd日  5.yyyy年MM月  5.yyyy-MM
    value: new Date().getTime(),
    minDate: new Date(2010,1,0).getTime(),
    maxDate: new Date().getTime(),
    initialArr: [],
    onPickerChange: () => { },
    onHiddenMask: () => { },
  didMount() {
    this.updateByValue();
  didUpdate(prevProps, prevData) {
    if (prevProps.value != this.props.value) {
      this.updateByValue();
  didUnmount() { },
  methods: {
    updateByValue() {
      let value = new Date(this.props.value);
      let years = this.getYears();
      let months = this.getMonths(value.getFullYear());
      let days = this.getDays(value.getFullYear(), value.getMonth() + 1);
      //let inintDaysArr = this.data.inintDaysArr.length? this.data.inintDaysArr:this.getDays(value.getFullYear(), value.getMonth() + 1);
      let initialValue = [0, 0, 0];
      if(this.props.initialArr.length){
        initialValue = this.props.initialArr;
      }else{
        //最开始没有initialArr值时,则计算出来
        initialValue[0] = years.indexOf(value.getFullYear());
        initialValue[1] = months.indexOf(value.getMonth() + 1);
        initialValue[2] = days.indexOf(value.getDate());
      (days.length > 6) && days.unshift(...this.data.inintDaysArr);
      (days.length > 6) && days.push(...this.data.inintDaysArr);
      this.setData({
        years: years,
        months: months,
        days: days,
      }, () => {
        this.setData({
          initialValue: initialValue,
    getYears() {
      let minDate = this.props.minDate?new Date(this.props.minDate): new Date(this.data.initMinDate);
      let maxDate = this.props.maxDate?new Date(this.props.maxDate): new Date(this.data.initMaxDate);
      let years = Array(maxDate.getFullYear() - minDate.getFullYear() + 1).fill(minDate.getFullYear()).map((x, y) => x + y);
      return years;
    getMonths(year) {
      //let maxDate = new Date(this.props.maxDate);
      let minDate = this.props.minDate?new Date(this.props.minDate): new Date(this.data.initMinDate);
      let maxDate = this.props.maxDate?new Date(this.props.maxDate): new Date(this.data.initMaxDate);
      let maxMonth = (year == maxDate.getFullYear() ? (maxDate.getMonth() + 1) : 12);
      let months = Array(maxMonth).fill(1).map((x, y) => x + y);
      let minDateIndex = -1;
      //跟最小值的年对比
      if(year == minDate.getFullYear()){
        minDateIndex = months.indexOf(minDate.getMonth() + 1);
        months.splice(0, minDateIndex);
      return months
    getDays(year, month) {
      //month = this.getMonths(year)[month];
      let minDate = this.props.minDate?new Date(this.props.minDate): new Date(this.data.initMinDate);
      let maxDate = this.props.maxDate?new Date(this.props.maxDate): new Date(this.data.initMaxDate);
      let maxDay = (year == maxDate.getFullYear() && month == (maxDate.getMonth() + 1)) ? maxDate.getDate() : new Date(year, month, 0).getDate();
      let days = Array(maxDay).fill(1).map((x, y) => x + y);
      let minDateIndex = -1;
      //跟最小值的年对比
      if(year == minDate.getFullYear() && month == minDate.getMonth() + 1){
        minDateIndex = days.indexOf(minDate.getDate());
        days.splice(0, minDateIndex);
      this.setData({
        inintDaysArr: this.deepCopy(days)
      return days
    changeTime(e) {
      let _valArr = e.detail.value;
      let months = this.getMonths(this.data.years[_valArr[0]]);
      if (_valArr[1] >= months.length) {
        _valArr[1] = months.length - 1;
      let days = [];
      //let inintDaysArr = this.getDays(this.data.years[_valArr[0]], this.data.months[_valArr[1]]);
      if(_valArr[0] != this.data.initialValue[0] || (_valArr[1] != this.data.initialValue[1])){
        days = this.getDays(this.data.years[_valArr[0]], months[_valArr[1]]);
      days = days.length ? days: this.data.days;
      if (_valArr[2] >= days.length) {
        _valArr[2] = days.length - 1;
      //年和月change时才改变days,且展示的个数是7个
      if(days.length > 6){
        let isAddDays = (_valArr[0] != this.data.initialValue[0]) || (_valArr[1] != this.data.initialValue[1]);
        //如果年或者月变化时,日添加重复数组数据,固定3个
        if(isAddDays){
          days.unshift(...this.data.inintDaysArr);
          days.push(...this.data.inintDaysArr);
          //以下代表日位于数组第一个范围内时比如10月1,因为在前面添加了一个数组数据,所以日所在位置也添加一个数组长度,视觉上前面还有值可选
          if(_valArr[2] < (days.length / 3)){
            _valArr[2] = _valArr[2] + days.length / 3;
        //如果日变化,就判断是第一组还是第三组
        if(_valArr[2] != this.data.initialValue[2]){
          let initDays = days.length?days: this.data.days;
          let daysLen = initDays.length;
          let inintDaysArrLen = this.data.inintDaysArr.length;
          //代表第一组,则删除第三组,添加到最前面,日所在位置对应添加一个数组长度
          if(_valArr[2] < (daysLen / 3)){
            initDays.splice(daysLen - daysLen / 3,daysLen / 3);
            initDays.unshift(...this.data.inintDaysArr);
            days = initDays;
            _valArr[2] = _valArr[2] + inintDaysArrLen;
          //代表第三组,则删除第一组,添加到最后面,日所在位置对应减去一个数组长度
          if(_valArr[2] > (daysLen / 3 * 2)){
            initDays.splice(0, daysLen / 3);
            initDays.push(...this.data.inintDaysArr);
            days = initDays;
            _valArr[2] = _valArr[2] - inintDaysArrLen;
      this.setData({
        months: months,
        days: days.length?days: this.data.days,
        'initialValue': _valArr
    cancelChoose() {
      this.props.onHiddenMask();
    confirmChoose(e) {
      let initialValue = this.data.initialValue;
      let _val = new Date(this.data.years[initialValue[0]], this.data.months[initialValue[1]] - 1, this.data.days[initialValue[2]]);
      let crtTime = this.dateFtt(this.props.format,_val);
      //第一个参数为固定格式的时间,第二个选中的index数组,第三个时间戳
      this.props.onPickerChange(crtTime, initialValue, _val.getTime());
      this.props.onHiddenMask();
    dateFtt(fmt,date) { //author: meizz   
      var o = {   
        "M+" : date.getMonth()+1,                 //月份   
        "d+" : date.getDate(),                    //日   
        "h+" : date.getHours(),                   //小时   
        "m+" : date.getMinutes(),                 //分   
        "s+" : date.getSeconds(),                 //秒   
        "q+" : Math.floor((date.getMonth()+3)/3), //季度   
        "S"  : date.getMilliseconds()             //毫秒   
      if(/(y+)/.test(fmt))   
        fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length));   
      for(var k in o)   
        if(new RegExp("("+ k +")").test(fmt))   
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));   
      return fmt;   
    typeCheck(data) {
      let typeObj = {
        "[object String]": 'string',
        "[object Number]": 'number',
        "[object Boolean]": 'boolean',
        "[object Object]": 'object',
        "[object Null]": 'null',
        "[object Undefined]": 'undefined',
        "[object Symbol]": 'symbol',
        // 上面的是7种基本类型。下面的是自带原生类型情况。
        "[object Function]": 'function',   // object
        "[object Date]": 'date',           // object  -- new Date() 出来的值。
        "[object Array]": 'array',         // object
        "[object RegExp]": 'regexp',       // object  正则的简写也同样是object,有test,match这个方法。      
      // 得到一个数据最原始的类型,直接去调用Object.prototype.toString()的方法。
      let strKey = Object.prototype.toString.call(data);  // 直接调用call方法,call为传单个参数的值,apply传入list;
      return typeObj[strKey];
    deepCopy(data) {
      // 1.先进行数据的类型判断
      let typeKey = this.typeCheck(data);
      // 2.只有两种情况。 1种是这个数据是值类型,另一种是object.
      let res_data = "", list = [], obj = {};
      if (typeof data === 'object') { // 会有一个null的问题。  
        if (typeKey === 'object') {
          // 对像
          for (let o in data) {
            obj[o] = this.deepCopy(data[o])
          return obj;
        } else if (typeKey === 'array') {
          // list;
          data.forEach(item => {
            list.push(this.deepCopy(item));
          return list;
        } else {
          // 其它对象类型。eg: function, date, reg 是对象也是以值类型存在。
          return data
      } else {
        // 基础值类型--在这一个基础值类型中只会执行一次。
        return data;
复制代码

typeCheck和deepCopy方法可以写在共用方法中去,这里我直接写在了js中,方便你们复制

怎么使用年月日picker自定义组件?

index.axml

<ai-multi-picker a:if="{{!chooseData.datatimePopup && timeRange.timebegin.multiPickerType}}"
                   type="{{timeRange.timebegin.multiPickerType}}" 
                   format="{{timeRange.timebegin.format}}"
                   value="{{pickerValue}}" 
                   initialValue="{{initialValue}}" 
                   minDate="{{minDate}}"
                   maxDate="{{maxDate}}"
                   onHiddenMask="onHiddenMask" 
                   onPickerChange="pickerChange">
  </ai-multi-picker>
  <!-- 年月日选择 -->
复制代码

index.json

"titleBarColor" : "#2639A0" , "usingComponents" : { "ai-multi-picker" : "/components/ai-multi-picker/ai-multi-picker" 复制代码

index.js

import store from '/store'
import create from '/scripts/westore/create'
import util from '/scripts/util.js'
import config from '/scripts/config.js'
const app = getApp();
create(store, {
  data: {
  onReady() {
  onLoad(query) {
  onShow() {
    dd.hideLoading();
  pickerChange(value, initialValue, timeStamp) {
    let rangePosition = this.data.timeRangeData.rangePosition;
    let timeType = rangePosition == 'startTime' ? 'timebegin' : 'timeend';
    this.store.data.timeRange[timeType].value = value;
    this.update();
    this.setData({
      weekValue: value,
      pickerValue: timeStamp,//时间戳
      initialValue: initialValue,
      [rangePosition]: value,
      timeType: timeType
  //range组件点击弹出picker
  onRangeChangeTime(timeRangeData) {
    let rangePosition = timeRangeData.rangePosition;
    let minOrMaxPicker = rangePosition == 'startTime' ? 'maxPicker' : 'minPicker';
    let minOrmaxDate = rangePosition == 'endTime' ? 'minPicker' : 'maxPicker';
    this.setData({
      pickerValue: new Date(timeRangeData[rangePosition]).getTime(),//时间戳
      weekValue: timeRangeData[rangePosition],
      timeRangeData: timeRangeData,
      maxPicker: minOrMaxPicker == 'maxPicker' ? timeRangeData[minOrMaxPicker] : '',
      minPicker: minOrMaxPicker == 'minPicker' ? timeRangeData[minOrMaxPicker] : '',
      minDate: rangePosition == 'endTime' ? new Date(timeRangeData[minOrmaxDate]).getTime() : '',
      maxDate: rangePosition == 'startTime' ? new Date(timeRangeData[minOrmaxDate]).getTime() : '',
      [`popupShow.bottom`]: true
复制代码

时间区间点击弹出自定义picker组件,年月日picker组件在时间区间选择的时候具有开始时间不能晚于结束时间,结束时间不能早于开始时间的功能

着重说下日无限循环滚动的问题 请看ai-multi-picker.js中代码,大概思路是根据年月获取days数组,因为每个月的日数组长度不同,2月只有28天,2019年12月有31天,所以picker的onChange触发时,就去计算days的数组,实时变化,当然滚动日的时候不需要变化;整体思路是在日数组前后插入相同的日数组,当滚动到在日第一数组时,立马把日的最后一个数组删除,并移到最前面,当滚动到日第三数组时,立马把日的最前面一个数组删除,并移动最后面,当前滚动的下标加上或者减去一个日数组长度,造成视觉上的错觉即可; 后续有什么走不通的问题请评论区留言,谢谢

分类:
前端
  •