微信小程序 自定义日历组件小demo



最近做了一个小项目,但是需要使用日历,通过点击日历上的日期来获取数据,第一反应就是去问度娘,发现案例倒是很多,但是都打不到想要实现的效果,只能自己动手了。

下面先展示一下小样的初步形态:


然后就是码一下基本逻辑了,ui框架我是用的是weui,布局方式我使用了flex布局:

wxml :

<view class="page">
  <view class="page__bd page__bd_spacing">
    <!-- 显示日期以及点击 -->
    <view class="weui-flex">
      <view>
        <view class="placeholder">←</view>
      </view>
      <view class="weui-flex__item">
       <picker mode="date" value="{{date}}" start="2015-09-01" end="2017-09-01" bindchange="bindDateChange">
        <view class="placeholder chooseDay">{{canlender.thisDay}}</view>
        </picker>
      </view>
      <view>
        <view class="placeholder">→</view>
      </view>
    </view>
    <!-- flex的周期显示 -->
    <view class="weui-flex flex-week">
      <view class="weui-flex__item">
        <view class="placeholder">日</view>
      </view>
      <view class="weui-flex__item">
        <view class="placeholder">一</view>
      </view>
      <view class="weui-flex__item">
        <view class="placeholder">二</view>
      </view>
      <view class="weui-flex__item">
        <view class="placeholder">三</view>
      </view>
      <view class="weui-flex__item">
        <view class="placeholder">四</view>
      </view>
      <view class="weui-flex__item">
        <view class="placeholder">五</view>
      </view>
      <view class="weui-flex__item">
        <view class="placeholder">六</view>
      </view>
    </view>
    <!-- flex的日期显示 -->
    <block wx:for="{{canlender.weeks}}" wx:key="this" wx:for-item="week" wx:for-index="i">
      <view class="weui-flex flex-day">
        <view class="weui-flex__item" wx:for="{{week}}" wx:key="this" wx:for-item="day">
          <view class="placeholder  {{day.click ? 'day_late' : ''}} {{day.noDay ? 'day_no' : ''}}" data-index = "{{i}},{{index}},{{day.noDay}}" data-date = "{{day.year}}/{{day.month}}/{{day.date}}" bindtap='getThisData'>{{day.date}}</view>
        </view>
      </view>
    </block>
  </view>
</view>

然后是wxss :

/* pages/cancelar/cancelar.wxss */
@import "../../lib/weui.wxss"; /* 这里需要引入weui.wxss样式规则 */
.placeholder{
    margin: 5px;
    padding: 0 10px;
    text-align: center;
    background-color: #EBEBEB;
    height: 2.3em;
    line-height: 2.3em;
    color: #cfcfcf;
}
.flex-week {
  background-color: #fff;
  border-top : 2rpx solid#f00;
  border-bottom : 2rpx solid#f00;
}
.flex-week .weui-flex__item view{
  background-color: #fff;
  color: #f00;
}
.flex-day .weui-flex__item .placeholder{
  margin: 0rpx;
  color: #fff;
  background-color: #f00;
  border : 1rpx solid#f00;
}
.day_late {   
/* 可以点击的日期样式 */
background-color: #fff !important; border : 1rpx solid#EBEBEB !important; color: #cfcfcf !important;}.day_no { /* 无法操作的日期样式 */ background-color: #EBEBEB !important; border : 1rpx solid#fff !important; color: #cfcfcf !important;}.chooseDay { color: #000;}

然后就是展示js了,逻辑就不说了,直接贴代码:

Page({
  data:{
    canlender:{
    	'month': new Date().getMonth()+1,
    	'date': new Date().getDate(),
    	"day": new Date().getDay(),
    	'year': new Date().getFullYear(),
      "weeks":[],
      //  这是第几个索引
      'thisIndex' : 0,
      'thisDay' : '1980/01/01',
    }
  },
  onLoad:function(options){
    var _date = new Date()
    var year = _date.getFullYear()  //年
    var month = _date.getMonth() + 1  //月
    var date = _date.getDate()    //日
    this.showThisDay(_date,year,month,date);
  },
  /**
   *  刷新页面值
   */
  showThisDay : function(date,_year,_month,_date1){
    // 页面初始化 options为页面跳转所带来的参数
    var canlender = [], _date = new Date(date);
    var year = _year, month = _month , date = _date1;
    console.info(year + "-" + month + "-" + date)
    var day = _date.getDay();
    var firstDay = new Date(year, month - 1, 1).getDay();
    var lastMonthDays = [];
    // 上个月需要显示的天数
    for (var i = firstDay - 1; i >= 0; i--) {
      console.warn(new Date(year, month, -i).getDate())
      lastMonthDays.push({
        'year': year,
        'date': new Date(year, month, -i).getDate() + '',
        'month': month - 1,
        'click': false,
        'noDay': true,
      })
    }
    var currentMonthDys = [];
    //  这个月显示的天数进行判断  如果已经过去则没法点击
    console.log('this date' + date);
    for (var i = 1; i <= new Date(year, month, 0).getDate(); i++) {
      if (i > date) {
        currentMonthDys.push({
          'year': year,
          'date': i + "",
          'month': month,
          'click': true,
          'noDay': false,
        })
      } else if (i == date) {
        //  这里会打印的从1开始的
        var fristDayLength = firstDay + i - 1;
        this.setData({
          'canlender.thisIndex': 0 + ',' + fristDayLength,
          'canlender.thisDay': year+'/'+month+'/'+i
        })
        currentMonthDys.push({
          'year': year,
          'date': i + "",
          'month': month,
          'click': false,
          'noDay': false,
        })
      } else {
        currentMonthDys.push({
          'year': year,
          'date': i + "",
          'month': month,
          'click': false,
          'noDay': true,
        })
      }
    }
    var nextMonthDays = []
    var endDay = new Date(year, month, 0).getDay();
    for (var i = 1; i < 7 - endDay; i++) {
      nextMonthDays.push({
        'year': year,
        'date': i + '',
        'month': parseInt(month) + 1,
        'click': false,
        'noDay': true,
      })
    }

    canlender = canlender.concat(lastMonthDays, currentMonthDys, nextMonthDays)
    var weeks = []

    for (var i = 0; i < canlender.length; i++) {
      if (i % 7 == 0) {
        weeks[parseInt(i / 7)] = new Array(7);
      }
      weeks[parseInt(i / 7)][i % 7] = canlender[i]
    }

    console.info(weeks)
    this.setData({
      "canlender.weeks": weeks
    })
  },
  /**
   *  获取到当前天数
   */
  getThisData : function(dom){
    var xy = dom.currentTarget.dataset.index, thisDay = dom.currentTarget.dataset.date;
    var xyArr = xy.split(',');
    var x = parseInt(xyArr[0]) , y = parseInt(xyArr[1]);
    if (xyArr[2] == true || xyArr[2] == 'true') return;
    //  index为从0开始的索引
    var thisDayDomClick = "canlender.weeks[" + x + "][" + [y]+"].click";
    var oldXy = this.data.canlender.thisIndex;
    var oldXyArr = oldXy.split(',');
    var oldx = parseInt(oldXyArr[0]), oldy = parseInt(oldXyArr[1]);
    var oldDayDom = "canlender.weeks[" + oldx + "][" + oldy +"].click";
    this.setData({
      [oldDayDom]: true,
      [thisDayDomClick] : false,
      'canlender.thisIndex' : x+','+y,
      'canlender.thisDay': thisDay,
    })
  },
  /**
   *  bindDateChange 
   */
  bindDateChange : function(e){
    var date = e.detail.value;
    var dateArr = date.split('-');
    var month = dateArr[1].substring(0, 1) == 0 || dateArr[1].substring(0, 1) == '0' ? dateArr[1].replace(/^[0-9]/, '') : dateArr[1];
    var day = dateArr[2].substring(0, 1) == 0 || dateArr[2].substring(0, 1) == '0' ? dateArr[2].replace(/^[0-9]/, '') : dateArr[2];
    var dateObj = new Date(dateArr[0]+'-'+month+'-'+day);
    this.showThisDay(dateObj,dateArr[0],month,day);
  },
  onReady:function(){
    // 页面渲染完成
  },
  onShow:function(){
    // 页面显示
  },
  onHide:function(){
    // 页面隐藏
  },
  onUnload:function(){
    // 页面关闭
  },
  tap: function(e){
      console.info(e)
  }
})

到这里,就可以实现以下功能了(录屏的时候可能有阳光照射导致效果不是很好):

到这里,基本的日历框架逻辑就差不多了,然后把上面的代码写成组件然后集成到项目中就可以了:

组件的js :

// compent/cancelar.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    title: {  // 是否显示
      type: Boolean,
      value: true,
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    'month': new Date().getMonth() + 1,
    'date': new Date().getDate(),
    "day": new Date().getDay(),
    'year': new Date().getFullYear(),
    "weeks": [],
    //  这是第几个索引
    'thisIndex': 0,
    'thisDay': '1980/01/01',
  },

  /**
   * 组件的方法列表
   */
  methods: {
    //  内部方法
    /**
      *  刷新页面值
      */
    showThisDay: function (date, _year, _month, _date1) {
      // 页面初始化 options为页面跳转所带来的参数
      var canlender = [], _date = new Date(date);
      var year = _year, month = _month, date = _date1;
      console.info(year + "-" + month + "-" + date)
      var day = _date.getDay();
      var firstDay = new Date(year, month - 1, 1).getDay();
      var lastMonthDays = [];
      // 上个月需要显示的天数
      for (var i = firstDay - 1; i >= 0; i--) {
        console.warn(new Date(year, month, -i).getDate())
        lastMonthDays.push({
          'year': year,
          'date': new Date(year, month, -i).getDate() + '',
          'month': month - 1,
          'click': false,
          'noDay': true,
        })
      }
      var currentMonthDys = [];
      //  这个月显示的天数进行判断  如果已经过去则没法点击
      console.log('this date' + date);
      for (var i = 1; i <= new Date(year, month, 0).getDate(); i++) {
        if (i > date) {
          currentMonthDys.push({
            'year': year,
            'date': i + "",
            'month': month,
            'click': true,
            'noDay': false,
          })
        } else if (i == date) {
          //  这里会打印的从1开始的
          var fristDayLength = firstDay + i - 1;
          this.setData({
            'canlender.thisIndex': 0 + ',' + fristDayLength,
            'canlender.thisDay': year + '/' + month + '/' + i
          })
          currentMonthDys.push({
            'year': year,
            'date': i + "",
            'month': month,
            'click': false,
            'noDay': false,
          })
        } else {
          currentMonthDys.push({
            'year': year,
            'date': i + "",
            'month': month,
            'click': false,
            'noDay': true,
          })
        }
      }
      var nextMonthDays = []
      var endDay = new Date(year, month, 0).getDay();
      for (var i = 1; i < 7 - endDay; i++) {
        nextMonthDays.push({
          'year': year,
          'date': i + '',
          'month': parseInt(month) + 1,
          'click': false,
          'noDay': true,
        })
      }

      canlender = canlender.concat(lastMonthDays, currentMonthDys, nextMonthDays)
      var weeks = []

      for (var i = 0; i < canlender.length; i++) {
        if (i % 7 == 0) {
          weeks[parseInt(i / 7)] = new Array(7);
        }
        weeks[parseInt(i / 7)][i % 7] = canlender[i]
      }

      console.info(weeks)
      this.setData({
        "canlender.weeks": weeks
      })
    },
    /**
     *  获取到当前天数
     */
    getThisData: function (dom) {
      var xy = dom.currentTarget.dataset.index, thisDay = dom.currentTarget.dataset.date;
      var xyArr = xy.split(',');
      console.log(xyArr[2]);
      if (xyArr[2] == true) return;
      var x = parseInt(xyArr[0]), y = parseInt(xyArr[1]);
      //  index为从0开始的索引
      var thisDayDomClick = "canlender.weeks[" + x + "][" + [y] + "].click";
      var oldXy = this.data.canlender.thisIndex;
      var oldXyArr = oldXy.split(',');
      var oldx = parseInt(oldXyArr[0]), oldy = parseInt(oldXyArr[1]);
      var oldDayDom = "canlender.weeks[" + oldx + "][" + oldy + "].click";
      this.setData({
        [oldDayDom]: true,
        [thisDayDomClick]: false,
        'canlender.thisIndex': x + ',' + y,
        'canlender.thisDay': thisDay,
      })
    },
    /**
     *   picker 日历触发事件
     */
    bindDateChange: function (e) {
      var date = e.detail.value;
      var dateArr = date.split('-');
      var month = dateArr[1].substring(0, 1) == 0 || dateArr[1].substring(0, 1) == '0' ? dateArr[1].replace(/^[0-9]/, '') : dateArr[1];
      var day = dateArr[2].substring(0, 1) == 0 || dateArr[2].substring(0, 1) == '0' ? dateArr[2].replace(/^[0-9]/, '') : dateArr[2];
      var dateObj = new Date(dateArr[0] + '-' + month + '-' + day);
      this.showThisDay(dateObj, dateArr[0], month, day);
      //  用来显示前台的
      var oldXy = this.data.canlender.thisIndex;
      var oldXyArr = oldXy.split(',');
      var oldx = parseInt(oldXyArr[0]), oldy = parseInt(oldXyArr[1]);
      var oldDayDom = "canlender.weeks[" + oldx + "][" + oldy + "].click";
      var thisDay = dateArr[0]+'/'+month+'/'+day;
      this.setData({
        'canlender.thisDay': thisDay,
      });
      // warn 这有个bug 就是无法控制原先的日期显示
      //  发送点击组件的时间
      this.triggerEvent("chooseEvent");
    },
    //  私有方法
    _clickEvent: function (dom) {
      var xy = dom.currentTarget.dataset.index, thisDay = dom.currentTarget.dataset.date;
      var xyArr = xy.split(',');
      if (xyArr[2] == true || xyArr[2] == 'true') return;
      var x = parseInt(xyArr[0]), y = parseInt(xyArr[1]);
      //  index为从0开始的索引
      var thisDayDomClick = "canlender.weeks[" + x + "][" + [y] + "].click";
      var oldXy = this.data.canlender.thisIndex;
      var oldXyArr = oldXy.split(',');
      var oldx = parseInt(oldXyArr[0]), oldy = parseInt(oldXyArr[1]);
      var oldDayDom = "canlender.weeks[" + oldx + "][" + oldy + "].click";
      this.setData({
        [oldDayDom]: true,
        [thisDayDomClick]: false,
        'canlender.thisIndex': x + ',' + y,
        'canlender.thisDay': thisDay,
      });
      //  发送点击组件的时间
      this.triggerEvent("chooseEvent");
    }
  }
})

到这里组件的功能基本就完成了,然后就是如何在页面中调用以及如何将数据传到当前页面中去了,组件与页面的数据交互我使用的是微信小程序的selectComponent方法,通过获取到组件对象直接获取和调用组建的方法,可以做一个小尝试:

在json中声明一下组件的名称:

{
"usingComponents" : {
"cancelar" : "../../compent/cancelar"
}
}

在页面中引用一个组件:

< view class = "container {{isShow ? 'show' : 'hide'}}">
< cancelar id = 'dialog'
bind:chooseEvent = "chooseEvent"
>
</ cancelar >
</ view >

在js中获取到这个组件:

this .dialog = this .selectComponent( "#dialog" );

然后打印组件中data对象里的数据就可以直接这么打印:

var getThisDay = this.dialog.data.canlender;

然后在想使用这个组件的页面直接引用该组件就可以了,但是组件在页面的样式我是在页面中定义的,因为项目中该组件的位置是不唯一的,我只把小样中的样式贴一下:

/* container模板样式 */ 
.container {
  position: absolute;
  top: 120px;
  left: 0;
  background-color: #fff;
  width: 100%;
  padding: 0px;
  height: 330px;
}
到这里,如果全部集成进去的话样式基本就搞定了,之后就是按照你们的需要来制定了。


由于项目急,没来得及开源,想要源码可以加1192366134qq。

猜你喜欢

转载自blog.csdn.net/ikceo1211/article/details/80206272