微信小程序开发--日历/日视图

背景

最近项目中需要使用到日历组件,并且需要在日视图上显示当天的日报数据,最终实现的效果图如下所示:

看起来还是有些丑的,不过在此还是把实现的过程记录一下。

1、需求拆分

从上图来看,我们的界面主要分为三部分,日历周视图、时间侧边栏、右侧日程内容,下面就对这三部分内容分别进行实现。

2、顶部日历周视图

顶部日历周视图采用了自定义组件去实现,以便于后面需要用到它的地方可以进行复用。

2.1 日历周试图的实现

1)在微信开发者工具中创建一个工程,并且在工程中与 pages文件夹同级下,新建一个 component文件夹。

2)在component文件夹下新建  calendarweek文件夹,并且在微信开发者工具右键 点击  calendarweek文件夹, 选择 “新建Component” 输入  "calendarweek"文件名,如下图所示结构:

2.2 calendarweek.js 的实现

// component/calendarweek.js
var utils = require('../../utils/util')
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    setdate: {
      type: String,
      value: ''
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    dateList: [], // 日历数据数组
    swiperCurrent: 0, // 日历轮播正处在哪个索引位置
    dateCurrent: new Date(), // 正选择的当前日期
    dateCurrentStr: '', // 正选择日期的 id
    dateMonth: '1月', // 正显示的月份
    dateListArray: ['日', '一', '二', '三', '四', '五', '六'],
  },

  ready: function () {

    //获取高度
    const query = this.createSelectorQuery()
    query.select('#calendarweekheight').boundingClientRect()
    query.selectViewport().scrollOffset()
    query.exec((res)=>{
      // console.log(res[0].height)
      let a = res[0].height
      this.triggerEvent('getHeader',a)
    })

    var today = utils.formatTime2(new Date());
    this.setData({
      today,
    });
    //2021-03-19
    if(this.properties.setdate != ''){
      let year = this.properties.setdate.substring(0,4);
      let month = this.properties.setdate.substring(5,7);
      let day = this.properties.setdate.substring(8);
      var d = new Date(year,month - 1,day);
      this.initDate(-5, 2, d); // 日历组件程序  -4左表示过去4周  右1表示过去一周 
    } else {
      var d = new Date();
      this.initDate(-5, 2, d); // 日历组件程序  -4左表示过去4周  右1表示过去一周 
    }   
  },
  /**
   * 组件的方法列表
   */
  methods: {
    tiaotime(e) {
      let data = e.detail.value.split("-")
      var d = new Date(Number(data[0]), Number(data[1]) - 1, Number(data[2]));
      this.setData({
        dateList: []
      })
      this.initDate(-5, 2, d); // 日历组件程序  -4左表示过去4周  右1表示过去一周
    },

    // 日历组件部分
    // ----------------------------
    initDate(left, right, d) {
      var month = utils.addZero(d.getMonth() + 1),
        year = utils.addZero(d.getFullYear()),
        day = utils.addZero(d.getDate());
      for (var i = left; i <= right; i++) {
        this.updateDate(utils.DateAddDay(d, i * 7)); //多少天之后的
      }
      this.setData({
        swiperCurrent: 5,
        dateCurrent: d,
        dateCurrentStr: d.getFullYear() + '-' + month + '-' + day,
        dateMonth: month + '月',
        dateYear: year + '年',
        dateCurrentStr: year + "-" + month + "-" + day,
      });
    },
    // 获取这周从周日到周六的日期
    calculateDate(_date) {
      var first = utils.FirstDayInThisWeek(_date);
      var d = {
        'month': first.getMonth() + 1,
        'days': [],
      };
      for (var i = 0; i < 7; i++) {
        var dd = utils.DateAddDay(first, i);
        var day = utils.addZero(dd.getDate()),
          year = utils.addZero(dd.getFullYear()),
          month = utils.addZero(dd.getMonth() + 1);

        d.days.push({
          'day': day,
          'id': dd.getFullYear() + '-' + month + '-' + day,
          'ids': dd.getFullYear() + ',' + month + ',' + day,
        });
      }
      return d;
    },
    // 更新日期数组数据
    updateDate(_date) {
      var week = this.calculateDate(_date);
      this.setData({
        dateList: this.data.dateList.concat(week),
      });
    },
    // 日历组件轮播切换
    dateSwiperChange(e) {
      const lastIndex = this.data.swiperCurrent,
        currentIndex = e.detail.current,
        dateList = this.data.dateList
      let flag = false,
        key = 'lastMonth' //判断是左划还是右 
      if (lastIndex > currentIndex) {
        lastIndex === 7 && currentIndex === 0 ?
          flag = true :
          null
      } else {
        lastIndex === 0 && currentIndex === 7 ?
          null :
          flag = true
      }
      if (flag) {
        key = 'nextMonth'
      }
      if (key == 'lastMonth') {
        let nowindex = currentIndex - 3;
        let uptime = currentIndex - 4;
        let a = 0;
        if (nowindex < 0) {
          nowindex = nowindex + 8;
          a = 0
        }
        if (uptime < 0) {
          uptime = uptime + 8
        }

        let seltime = dateList[nowindex].days[a].ids.split(',')
        var week = this.calculateDate(utils.formatTime(utils.DateAddDay(new Date(Number(seltime[0]), Number(seltime[1]) - 1, Number(seltime[2])), -1)));
        dateList[uptime] = week
        this.setData({
          dateList: dateList
        })
      }
      if (key == 'nextMonth') {
        let indexne = 0
        let aa = 0
        if (currentIndex == 7) { //要更新的下标
          indexne = 0
          aa = 1
        } else {
          indexne = currentIndex + 1
          aa = currentIndex + 2
        }
        if (aa == 8) {
          aa = 0
        }
        //aa 要更新的数组下标 datanex要往后查询一周的日期
        let datanex = dateList[indexne].days[6].ids.split(',')
        //获取下一周的
        var week = this.calculateDate(utils.formatTime(utils.DateAddDay(new Date(Number(datanex[0]), Number(datanex[1]) - 1, Number(datanex[2])), 1)));
        dateList[aa] = week
        this.setData({
          dateList: dateList
        })
      }
      var dDateFormat = this.data.dateList[currentIndex].days[3].ids.split(',');
      this.setData({
        swiperCurrent: currentIndex,
        dateMonth: dDateFormat[1] + '月',
        dateYear: dDateFormat[0] + "年"
      })
    },
    // 获得日期字符串
    getDateStr: function (arg) {
      if (utils.type(arg) == 'array') {
        return arr[0] + '-' + arr[1] + '-' + arr[2] + ' 00:00:00';
      } else if (utils.type(arg) == 'date') {
        return arg.getFullYear() + '-' + (arg.getMonth() + 1) + '-' + arg.getDate() + ' 00:00:00';
      } else if (utils.type(arg) == 'object') {
        return arg.year + '-' + arg.month + '-' + arg.day + ' 00:00:00';
      }
    },

    // 点击日历某日
    chooseDate(e) {
      var str = e.currentTarget.id;
      console.log("当前选择的日期:",str);
      this.setData({
        dateCurrentStr: str
      });
      this.triggerEvent('myevent', {
        data: str
      })
    },
  }
})

2.3 calendarweek.wxml 的实现

<!--component/calendarweek.wxml-->
<view id="calendarweekheight" class="date-choose shrink border-bottom10">
  <view class="weekday">
    <block wx:for-item="weekday" wx:for="{{dateListArray}}" wx:key="{{index}}">
      <text class="week">{{weekday}}</text>
    </block>
  </view>
  <swiper class="date-choose-swiper" circular="true" indicator-dots="{{false}}" current="{{swiperCurrent}}"
    bindchange="dateSwiperChange">
    <block wx:for="{{dateList}}" wx:for-item="date" wx:key="date.id">
      <swiper-item class="swiper-item"> 
        <view class="dateday">
          <block wx:for="{{date.days}}" wx:for-item="day" wx:key="{{day.id}}">
            <view class="day" id="{{day.id}}" bindtap="chooseDate">
              <text class="{{dateCurrentStr==day.id?'active':'nomal'}}{{today==day.id?' reds':''}}">{{day.day}}</text>
            </view>
          </block>
        </view>
      </swiper-item>
    </block>
  </swiper>
</view>

2.4 、calendarweek.wxss的实现

/* component/calendarweek.wxss */
.date-choose {
  background: #fff;
  overflow: hidden;
  height: auto;
}

.data-month {
  width: 100%;
  align-items: center;
  padding: .5rem .35rem;
  text-align: left;
  color: #333;
  font-size: 24rpx;
}

.date-choose-swiper {
  flex-grow: 1;
  height: 120rpx;
}

.swiper-item {
  display: flex;
  flex-direction: column;
}

.weekday,
.dateday {
  display: flex;
  justify-content: space-between;
  align-items: center;
  text-align: center;
  flex-wrap: wrap;
  flex-grow: 1;
}

.week,
.day {
  width: 14.286%;
  flex-basis: 14.286%;
}

.week {
  font-size: 20rpx;
  font-family: PingFang SC;
  /* font-weight: bold; */
  color: #000000;
}

.day text {
  position: relative;
  color: #333333;
}

.day .active:before {
  /* 圈圈 */
  content: "";
  position: absolute;
  width: 55rpx;
  height: 55rpx;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
  border: 1px solid #286AFE;
  border-radius: 100%;
  background-color: #286AFE;
  /* opacity: 0.8; */
  z-index: -1;
}

.day text.active {
  color: #ffffff;
  font-size: 24rpx; 
  font-family: PingFang SC;
  /* font-weight: bold; */
}

.nomal{
  font-size: 24rpx; 
  font-family: PingFang SC;
  /* font-weight: bold; */
}

.day text.reds {
  color: #ff0000;
  font-size: 24rpx; 
}

/*开始*/

.headerone {
  width: 100%;
  height: auto;
  font-size: 24rpx; 
}

.headerone .ra {
  margin-right: 20rpx; 
}

.headerone .radio-group {
  margin: 20rpx 0 20rpx 30rpx;
}

.headertwo {
  width: 100%;
  height: auto;
  font-size: 24rpx;
  margin-top: 10rpx;
  margin-bottom: 26rpx;
}

.headertwo .le image {
  width: 70rpx;
  height: 70rpx;
  border-radius: 10px;
  margin-left: 30rpx;
  margin-right: 20rpx
}

.headertwo .ri {
  flex: 1;
  margin-right: 30rpx;
  border-radius: 6px;
  box-shadow: 0px 1px 6px 0px rgba(198, 198, 198, 0.5);
}

.headertwo .ri .one {
  width: 100%;
  padding-top: 24rpx;
  padding-bottom: 24rpx
}

.headertwo .ri .one view .jiao {
  margin: 0 16rpx;
  border: 15rpx solid; 
  border-color: #ffffff #ffffff #b3b3b3 #ffffff;
}

.xi {
  background: red;
  color: #ffffff;
  padding: 3px 10px;
  border-radius: 6px 0px 0 6px; 
}

.headertwo .ri .one view view.jiaos {
  margin: 0 16rpx;
  border: 15rpx solid;
  margin-top: 14rpx;
  border-color: #b3b3b3 #ffffff #ffffff #ffffff;
}

.headertwo .ri .two {
  width: 100%;
  overflow: hidden;
  transition: all 0.5s
}

.headertwo .ri .two .body {
  width: 100%;
  padding-top: 24rpx;
  padding-bottom: 24rpx;
}

2.5 、util.js 的实现

// 时间格式转换 yyyy-mm-dd
function formatTime2(date) {
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var day = date.getDate() 
  var hour = date.getHours()
  var minute = date.getMinutes()
  var second = date.getSeconds()  
  return [year, month, day].map(formatNumber).join('-')
}

// 时间格式转换 yyyy/mm/dd
function formatTime(date) {
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var day = date.getDate() 
  var hour = date.getHours()
  var minute = date.getMinutes()
  var second = date.getSeconds()  
  return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}

// 两位数自动补零
function formatNumber(n) {
  n = n.toString()
  return n[1] ? n : '0' + n
}

// 计算变化多少天后的日期
function DateAddDay(d, days) {
  var d = new Date(d); 
  return new Date(d.setDate(d.getDate() + days));
}

// 获得本周周日的日期
function FirstDayInThisWeek(d) { 
  var d = new Date(d);  
  return DateAddDay(d, 0 - d.getDay());
}

module.exports = {
  formatTime,
  formatTime2,
  DateAddDay,
  addZero: formatNumber,
  FirstDayInThisWeek
}

至此,canlendar 组件的实现已经完成,这个时候就可以进行使用了。

3、canlendar 组件的使用

在pages 文件夹下新建 如下图所示的文件:

然后在calendarday.json 文件中添加如下代码:

{
  "usingComponents": {
    "calendarweek":"../../component/calendarweek/calendarweek"
  }
}

其中 "../../component/calendarweek/calendarweek" 就是我们自定义组件的路径;

"calendarweek" 是我们给组定义组件取得别名,当然你可以可以替换为其它名字,但在使用得时候,你这个地方定义的是什么名字,使用的时候就要用什么名字。

具体在 calendarday.wxml 文件的实现中使用到了我们自定义的组件。

下面看下calendarday 其它文件的具体实现代码。

3.1、calendarday.js 的实现

// pages/calendarday/calendarday.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
// 页面总高度将会放在这里
windowHeight: 0,
calendarWeekHeight: 0,
// scroll-view的高度
scrollViewHeight: 0,
title:'',
    hour_titles: Array.from({ length: 24 }).map(function (value, index) {
      var hour = (index + 1) % 24;
      return ((hour < 10) ? "0" : "") + hour + ":00";
    }),
    day_hour_items: Array.from({length:24}).map((value,index)=>index+1),
    todo_item_sizes: Array.from({ length: 24 }).map(function(){
      return {
        width:1,
        height :1
      }
    }),
    bar_item_sizes: Array.from({ length: 24 }).map(function () {
      return {
        width: 1,
        height: 1
      }
    }),

calendarList:[],
    curDate:'',
    testData:[{"key":"测试1","top":1,"height":1},
    {"key":"测试sssss","top":1,"height":1},
    {"key":"测试sssss","top":1,"height":1},
    {"key":"测试sssss","top":1,"height":1},
    {"key":"测试3","top":2,"height":2},
    {"key":"测试3","top":2,"height":3},
    {"key":"测试5","top":10,"height":4},
    {"key":"测试6","top":1,"height":4},
    {"key":"测试7","top":30,"height":5},
    {"key":"测试8","top":5,"height":10},
    {"key":"测试8","top":60,"height":10}],
},

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
  },

  //获取选择的日历数据
  onMyEvent: function(e){
    console.log('选择的日期:',e.detail);
  },

  //获取组件高度
  getHeader: function(e){
    console.log('获取日历组件的高度:',e.detail);
    let calendarWeekHeight = e.detail;
    wx.getSystemInfo({
      success:  res => {
        this.data.windowHeight = res.windowHeight;
      }
  });
  
     let scrollViewHeight = this.data.windowHeight - calendarWeekHeight;
     console.log("scrollViewHeight = ",scrollViewHeight);
     // 算出来之后存到data对象里面
     this.setData({
         scrollViewHeight: scrollViewHeight
     });
  },
})

3.1、calendarday.wxml 的实现

<!--pages/calendarday/calendarday.wxml-->
<view class="page">

  <calendarweek id="calendarweekmenu" bindmyevent="onMyEvent" bindgetHeader="getHeader"></calendarweek>

  <scroll-view scroll-y="true" style="height: {{scrollViewHeight}}px" >
    <view class='day-content'>
    <view class='day-hour-bar'>
      <view class='day-hour-bar-item' wx:for="{{hour_titles}}" wx:key="{{index}}">
        <view id="hour-bar-{{index}}" class='item' bindtap='handleTap' style='height:calc(50rpx + {{bar_item_sizes[index].height}} * 50rpx);line-height:calc(50rpx + {{bar_item_sizes[index].height}} * 50rpx)'>
          <block  wx:if="{{item === '00:00'}}">
            <text>  </text>
          </block>
          <block wx:else>
            <text>{{item}}</text>
          </block>
        </view>
        <view  wx:if="{{index != 0}}" class="day-hour-context-item-div"></view>
      </view>
    </view>

    <view class="day-content-item"  >
      <view class="day-content-item-sub" wx:for="{{testData}}">
        <view class="day-content-show" style="margin-top:calc({{item.top}}*10rpx); height:calc({{item.height == 0 ? 1 : item.height}} * 60rpx)" >
          <text class="day-content-show-tex">{{item.key}}</text>
        </view>
      </view>
    </view>

  </view>
</scroll-view>
</view>

3.3、calendarday.wxss 的实现

/* pages/calendarday/calendarday.wxss */
.page {
  overflow: scroll;
  width: 100vw;
  height: 100vh;
}

.day-hour-bar {
  position: absolute;
  z-index: 2;
  width: 100%;
}

.day-content {
  position: relative;
  z-index: 1;
  width: 100%;
}

.day-hour-bar-item {
  height: 60rpx;
  display: flex;
  flex-direction: row;
  background-color: #F6F8FA;
  /* background-color: #B8F1F1;
  border: 3rpx solid #FCBDAD; */
  /* line-height: 100rpx; */
  text-align: center;
}

.item {
  display: inline-block;
  width: 70rpx;
  margin-left: 30rpx;
  margin-top: 10rpx;
  font-size: 20rpx;
  font-family: PingFang SC;
  color: #999999;
}

.day-hour-context-item-div{
  background-color: #DBDCDC;
  width: 100%;
  margin-left: 30rpx;
  height: 2rpx;
  margin-right: 20rpx;
}

.day-content-item{
  position: relative;
  z-index: 3;
  display: flex;
  margin-left: 110rpx;
  width: 100%;
}

.day-content-item-sub{
  width: fit-content;
}

.day-content-show{
  border: 2rpx solid #DBDCDC;
  border-left: 10rpx solid #00B853;
  border-radius: 5px;
  background-color: #ffffff;
  margin-left: 10rpx;
  overflow:hidden;
  text-overflow:ellipsis;
  width: auto;
}

至此,我们的日历/日视图的代码实现已经完成。

如果上面的内容对你有所帮助,不要忘记点个赞哟。

4、运行动态效果图

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值