小程序实现日历打卡功能

2022-08-28 16:41:54

本文实例为大家分享了小程序实现日历打卡功能的具体代码,供大家参考,具体内容如下一、效果图展示老惯例,先上效果图二、实现思路1、日历展示例如下图中:2021月7月打卡日历页面,共35个日期数据,上月残余...

本文实例为大家分享了小程序实现日历打卡功能的具体代码,供大家参考,具体内容如下

一、效果图展示

老惯例,先上效果图

小程序实现日历打卡功能

二、实现思路

1、日历展示

例如下图中:

2021月7月打卡日历页面,共35个日期数据,上月残余4天+本月31天;

2021月6月打卡日历页面,共35个日期数据,上月残余2天+本月30天+下月残余3天;

2021月5月打卡日历页面,共42个日期数据,上月残余6天+本月31天+下月残余5天。

【结论】打卡日历页面存在展示35个或42个日期数据的情况,35个或42个日期数据=当前显示月所有日期数据+上月残余尾部日期+下月残余头部日期。

小程序实现日历打卡功能

计算出每个月的日期天数,获取本月1号是周几,上月残余天数=本月1号的星期X的X数(比如,2021年7月1日是星期四,则上月残余4天),假设 a=35-本月天数-上月残余天数。如果a>=0,则下月残余天数=a;如果a<0,则下月残余天数=7+a (比如2021年5月,35-37=-2;7+(-2)=5)

2、打卡功能

打卡实现的功能:可打卡的日期为今日日期或今日日期之前未打卡过的日期。

如图:今日日期为绿色圆形背景,当前点击日期为橙色圆形背景;可打卡时,打卡按钮背景为蓝色,不可打卡时,打卡背景为灰色;√ 代表已打卡。

小程序实现日历打卡功能

通过数据库查询当前已打卡的数据,已打卡的数据需要设置打卡按钮禁用标志。打卡按钮禁用的情况(1)页面初始化时,未点击任何日期(2)当前点击的日期在今天之后(3)当前日期在今天之前但已打卡。

点击打卡,记录打卡日期并保存至数据库。

三、代码

1、数据库数据

小程序实现日历打卡功能

2、日历组件

【calendar.wXML】

代码里使用了WXS页内脚本,渲染“已打卡”的标志(√)

<view class="calendar">
 <view class='tit'>
  <view class='pre' bindtap='gotoPreMonth'>{{'《'}}</view>
  <view class='current'>{{currentYear}}年{{currentMonth}}月</view>
  <view class='next' bindtap='gotoNextMonth'>{{'》'}}</view>
 </view>
 <view class='w100P showData'>
  <view class="week" style='color: #999'>日</view>
  <view class="week">一</view>
  <view class="week">二</view>
  <view class="week">三</view>
  <view class="week">四</view>
  <view class="week">五</view>
  <view class="week" style='color: #999'>六</view>
 </view>
 <view class='content'>
  <view wx:for="{{allArr}}" wx:key="index" class='itemData' data-current="{{item.month == 'current' ? '1' : '0'}}"
   data-day='{{item.date}}' bindtap='clickDate'>
   <view class="{{item.month == 'current' ? '' : 'gray'}}"
    style="height:44px;width:44px;line-height:30px;{{nowYear==currentYear&&currentMonth==nowMonth&&item.date==nowDate?'color:#fff;background:#33D4C5;border-radius:100px':''}};{{item.month == 'current'&&selectedYear==currentYear&&selectedMonth==currentMonth&&item.date==selectedDate?'color:#fff;background:orange;border-radius:100px':''}} ">
    {{item.date}}
    <view>
     <icon wx:if="{{item.month == 'current'&&dataProcess.filterDate(currentPunchCardDate,item.date)}}" class="icon" color="#F44336" type="success_no_circle" size="15"></icon>
    </view>
   </view>
  </view>
 </view>
 <view class="btn-wrapper" bindtap="gotoToday">
  <button class="btn">回今天</button>
 </view>
 <!-- wxs页面内脚本,在渲染层做数据处理 -->
 <wxs module="dataProcess">
 function filterDate(currentPunchCardDate,date){
  if(currentPunchCardDate.indexOf(date)!==-1){
   return true
 }
 }
 module.exports={
  filterDate:filterDate
 }
 </wxs>
</view>

【calendar.wxss】

.calendar {
 width: 100%;
 background: #fff;
}

.pre,
.next {
 color: #33D4C5;
 text-align: center;
 line-height: 20px;
}

.calendar .tit {
 display: Flex;
 justify-content: center;
 align-items: center;
 padding: 40rpx 0;

}

.current {
 font-size: 32rpx;
 color: #2A2A2A;
}

.calendar .tit .current {
 margin: 0 60rpx;
}

.showData {
 display: flex;
 justify-content: center;
 align-items: center;
 box-sizing: border-box;
 padding-left: 25rpx;
 padding-right: 25rpx;
}

.showData .week {
 width: 14%;
 height: 70rpx;
 line-height: 70rpx;
 text-align: center;
 flex-shrink: 0;
 font-size: 30rpx;
 color: #2A2A2A;
}

.calendar .content {
 display: flex;
 flex-wrap: wrap;
 box-sizing: border-box;
 padding-left: 25rpx;
 padding-right: 25rpx;
}

.calendar .content .itemData {
 width: 14.2%;
 height: 90rpx;
 line-height: 90rpx;
 flex-shrink: 0;
 font-size: 30rpx;
 color: #2A2A2A;
 text-align: center;
 display: flex;
 align-items: center;
 justify-content: center;
}

.calendar .content .icon {
 position: relative;
 top: -25rpx;
}

.calendar .content .gray {
 color: #999;
}

.currentSelected {
 color: #fff;
 background: #1CA2FC;
 border-radius: 100px;
}

.calendar .btn-wrapper {
 text-align: right;
 background-color: #fff;
 width: 100%;
 padding-bottom: 10rpx;
}

.calendar .btn-wrapper .btn {
 border: 1px solid #33D4C5;
 padding: 5rpx;
 width: 95rpx;
 font-size: 21rpx;
 color: #33D4C5;
 border-radius: 20rpx;
 margin-bottom: 15rpx;
 position: relative;
 left: calc(50% - 100rpx);
}

【calendar.js】

CoDsgVymponent({
 /**
 * 组件的属性列表
 */

 properties: {
  currentPunchCardDate: {
   type: Array,
   value: []
  },
  currentYear: { // 当前页面显示的年
   type: Number,
   value: new Date().getFullYear()
  },
  currentMonth: { // 当前页面显示的年月
   type: Number,
   value: new Date().getMonth() + 1
  },
  nowYear: { // 当前年
   type: Number,
   value: new Date().getFullYear()
  },
  nowMonth: { // 当前月
   type: Number,
   value: new Date().getMonth() + 1
  },
  nowDate: { // 当前日
   type: Number,
   value: new Date().getDate()
  },

 },

 /**
 * 组件的初始数据
 */
 data: {
  currentMonthDateLen: 0, // 当月天数
  preMonthDateLen: 0, // 当月中,上月多余天数
  allArr: [], // 35个或42个日期数据=当前显示月所有日期数据+上月残余尾部日期+下月残余头部日期
  nowDate: null,
  selectedDate: null, //当前选择日期
  selectedMonth: null, //当前选择月
  selectedYear: null, //当前选择年
 },
 // 用observers监听properties的属性值
 observers: {
  'currentPunchCardDate': function (val) {
   console.log(val)

  }
 },
 // 在组件实例刚刚被创建时执行
 created() {},
 // 在组件实例进入页面节点树时执行
 ready() {
  this.getAllArr()
 },
 /**
 * 组件的方法列表
 */
 methods: {
  // 获取某年某月天数:下个月1日-本月1日
  getDateLen(year, month) {
   let actualMonth = month - 1;
   let timeDistance = new Date(year, month) - new Date(year, actualMonth);
   return timeDistance / (1000 * 60 * 60 * 24);
  },
  // 获取某月1号是周几
  getFirstDateWeek(year, month) {
   // 0-6,0代表周天
   return new Date(year, month - 1, 1).getDay()
  },
  // 上月
  preMonth(year, month) {
   if (month == 1) {
    return {
     year: --year,
     month: 12
    }
   } else {
    return {
     year: year,
     month: --month
    }
   }
  },
  // 下月
  nextMonth(year, month) {
   if (month == 12) {
    return {
     year: ++year,
     month: 1
    }
   } else {
    return {
     year: year,
     month: ++month
    }
   }
  },
  // 获取当月数据,返回数组
  getCurrentArr() {
   let currentMonthDateLen = this.getDateLen(this.data.currentYear, this.data.currentMonth) // 获取当月天数
   let currentMonthDateArr = [] // 定义空数组
   if (currentMonthDateLen > 0) {
    for (let i = 1; i <= currentMonthDateLen; i++) {
     currentMonthDateArr.push({
      month: 'current', // 只是为了增加标识,区分上下月
      date: i
     })
    }
   }
   this.setData({
    currentMonthDateLen
   })
   return currentMonthDateArr
  },
  // 获取当月中,上月多余的日期数据,返回数组
  getPreArr() {
   let preMonthDateLen = this.getFirstDateWeek(this.data.currentYear, this.data.currentMonth) // 当月1号是周几 == 上月残余天数)
   console.log("preMonthDateLen=", preMonthDateLen);
   let preMonthDateArr = [] // 定义空数组
   if (preMonthDateLen > 0) {
    let {
     year,
     month
    } = this.preMonth(this.data.currentYear, this.data.currentMonth) // 获取上月 年、月
    let date = this.getDateLen(year, month) // 获取上月天数
    for (let i = 0; i < preMonthDateLen; i++) {
     preMonthDateArr.unshift({ // 尾部追加
      month: 'pre', // 只是为了增加标识,区分当、下月
      date: date
     })
     date--
    }
   }
   this.setData({
    prepythonMonthDateLen
   })
   return preMonthDateArr
  },
  // 获取当月中,下月多余的日期数据,返回数组
  getNextArr() {
   let nextMonthDateLen = 35 - this.data.preMonthDateLen - this.data.currentMonthDateLen // 下月多余天数
   console.log(" nextMonthDateLen=", nextMonthDateLen);
   let nextMonthDateArr = [] // 定义空数组
   if (nextMonthDateLen > 0) {
    for (let i = 1; i <= nextMonthDateLen; i++) {
     nextMonthDateArr.push({
      month: 'next', // 只是为了增加标识,区分当、上月
      date: i
     })
    }
   } else if (nextMonthDateLen < 0) {
    for (let i = 1; i <= (7 + nextMonthDateLen); i++) {
     nextMonthDateArr.push({
      month: 'next', // 只是为了增加标识,区分当、上月
      date: i
     })
    }

   }
   return nextMonthDateArr
  },
  // 整合当月所有日期数据=上月残余+本月+下月多余
  getAllArr() {
   let preArr = this.getPreArr()
   let currentArr = this.getCurrentArr()
   let nextArr = this.getNextArr()
   let allArr = [...preArr, ...currentArr, ...nextArr]
   this.setData({
    allArr
   })
   let sendObj = {
    currentYear: this.data.currentYear,
    currentMonth: this.data.currentMonth,
    currentDate: this.data.selectedDate,
    allArr: this.data.allArr,
   }
   // 向父组件发送数据
   this.triggerEvent('sendObj', sendObj)

  },
  // 点击 上月
  gotoPreMonth() {
   let {
    year,
    month
   } = this.preMonth(this.data.currentYear, this.data.currentMonth)
   this.setData({
    currentYear: year,
    currentMonth: month,
   })
   this.getAllArr()
  },
  // 点击 下月
  gotoNextMonth() {
   let {
    year,
    month
   } = this.nextMonth(this.data.currentYear, this.data.currentMonth)
   this.setData({
    currentYear: year,
    currentMonth: month,
   })
   this.getAllArr()
  },
  // 点击日期
  clickDate(e) {
   var date = e.currentTarget.dataset.day;
   var current = e.currentTarget.dataset.current;
   if (current == 0) {
    if (date > 6) {
     // 点击上月日期--去上个月
     var {
      year,
      month
     } = this.preMonth(this.data.currentYear, this.data.currentMonth)
     this.gotoPreMonth()
    } else {
     // 点击下月
     var {
      year,
      month
     } = this.nextMonth(this.data.currentYear, this.data.currentMonth)
     this.gotoNextMonth()
    }
   } else {
    var year = this.data.currentYear;
    var month = this.data.currentMonth;
   }
   this.setData({
    selectedYear: year,
    selectedMonth: month,
    selectedDate: date,
   })
   console.log("当前选择日期", year, "-", month, "-", date);
   console.log(this.data.selectedDate);
   wx.nextTick(() => {
    this.getAllArr()
   })

  },
  // 回今天
  gotoToday() {
   this.setData({
    currentYear: this.data.nowYear,
    currentMonth: this.data.nowMonth,
   })
   this.getAllArr()
  }
 }
})

3、打卡及统计

【calendarCard.wxml】

<view class="page-wrapper">
 <top-title toptitle="打卡日历" backImgFlag="true"></top-title>
 <calendar bind:sendObj="getjavascriptObj" currentPunchCardDate="{{punchCardDateArr}}"></calendar>
 <view class="btn-wrapper">
  <button class="btn" type="primary" disabled="{{ disabledFlag}}" bindtap="punchCard">打 卡</button>
 </view>
 <view class="record-wrapper">
  <view class="title">
   <image class="img" src="{{icon}}"></image> {{name}}打卡统计
  </view>
  <view class="record">
   <view class="record-item">
    <view class="top"><text class="num">{{monthDays}}</text> 天</view>
    <view class="bottom">本月坚持天数</view>
   </view>
   <view class="record-item">
    <view class="top"><text class="num"> {{totalDays}}</text> 天</view>
    <view class="bottom">总共坚持天数</view>
   </view>
  </view>
 </view>
</view>

【calendarCard.wxss】

.page-wrapper {
 background-color: #fff;
 height: 100vh;
}
.page-wrapper .btn-wrapper .btn {
 width: 95vw;
 border-radius: 40rpx;
 height: 80rpx;
 font-size: 30rpx;
 background-color: #27d6f5;
 padding: 20rpx;
}
.page-wrapper .btn-wrapper .btn[disabled] {
 background-color: #e7e5e5;
}
.page-wrapper .record-wrapper {
 padding: 20rpx;
}
.page-wrapper .record-wrapper .title {
 color: #444;
 font-weight: bold;
}
.page-wrapper .record-wrapper .title .img {
 width: 60rpx;
 height: 60rpx;
 position: relative;
 top: 18rpx;
}
.page-wrapper .record-wrapper .record {
 display: flex;
 justify-content: space-around;
 margin-top: 20rpx;
}
.page-wrapper .record-wrapper .record .record-item {
 text-align: center;
 font-size: 24rpx;
 color: #a3a3a3;
}
.page-wrapper .record-wrapper .record .record-item .top {
 height: 80rpx;
 line-height: 80rpx;
 border-bottom: 1px solid #ececec;
 color: #333;
}
.page-wrapper .record-wrapper .record .record-item .top .num {
 font-size: 44rpx;
 font-weight: bold;
 color: #F44336;
}

【calendarCard.js】

// miniprogram/pages/punchCard/calendarCard/calendarCard.js
Page({

 /**
 * 页面的初始数据
 */
 data: {
  id: null,
  name: "",
  icon: "",
  disabledFlag: true,
  totalDays:0,
  monthDays:0,
  habitInfo: {},
  currentDate: null,
  currentMonth: null,
  currentYear: null,
  nowYear: new Date().getFullYear(),
  nowMonth: new Date().getMonth(),
  nowDate:new Date().getDate(),
  punchCardDateArr: [] //用于存放当月打卡日期-日

 },
 /**
 * 生命周期函数--监听页面加载
 */
 onLoad: function (options) {
  console.log(options);
  this.setData({
   id: options.id,
   name: options.name,
   icon: options.icon
  })
  var nowYear = new Date().getFullYear()
  var nowMonth = new Date().getMonth()
  wx.nextTick(() => {
   this.getHabitInfo(nowYear, nowMonth)
  })
 },
 // 获取子组件的数据
 getObj(e) {
  console.log("获取子组件的数据", e);
  this.setData({
   currentDate: e.detail.currentDate,
   currentMonth: e.detail.currentMonth,
   currentYear: e.detail.currentYear,
  })
  this.getHabitInfo(e.detail.currentYear, e.detail.currentMonth - 1)
 },
 // 获取当月的打卡数据
 getHabitInfo(year, month) {
  // 注意month范围 0-11,0代表1月
  const db = wx.cloud.database()
  db.collection('habitList').where({
   _id:this.data.id,
  }).get().then(res => {
   // console.log("从数据库获取数据[res]===", res);
   var dateTimeArr = res.data[0].dateTime
   var dateArr = []
   dateTimeArr.forEach((item) => {
    if (item.getFullYear() == year && item.getMonth() == month) {
     dateArr.push(item.getDate())
    }
   })
   console.log(year, month,this.data.currentDate);
   if (!this.data.currentDate ||(year==this.data.nowYear && month>this.data.nowMonth)||(year==this.data.nowYear && month==this.data.nowMonth &&this.data.currentDate>this.data.nowDate) ) {
    // 打卡按钮禁用的情况(1)页面初始化时,未点击任何日期(2)当前点击的日期在今天之后
    var flag = true
   } else {
    // 打卡按钮禁用的情况 (3)当前日期已打卡
    var flag = dateArr.indexOf(this.data.currentDate) == -1 ? false : true
   }

   this.setData({
    habitInfo: res.data[0],
    punchCardDateArr: dateArr,
    disabledFlag: flag,
    totalDays:dateTimeArr.length,
    monthDays:dateArr.length
   })

  }).catch(err => {
   console.log(err);
  })
 },
 // 点击打卡按钮-打卡
 punchCard() {
  console.log(this.data.currentYear, this.data.currentMonth - 1, this.data.currentDate);
  var currentTime = new Date(this.data.currentYear, this.data.currentMonth - 1, this.data.currentDate)
  const db = wx.cloud.database()
  db.collection('habitList').doc(this.data.id).update({
   data: {
   dateTime:db.command.push(currentTime)
   },
   success: res => {
    wx.showToast({
     title: '打卡成功',
    })
    this.getHabitInfo(this.data.currentYear, this.data.currentMonth - 1)
   },
   fail: err => {
    wx.showToast({
     icon: 'none',
     title: '新增记录失败'
    })
    console.error('[数据库] [新增记录] 失败:', err)
   }
  })
 }
})

【calendarCard.json】

{
 "usingComponents": {
  "top-title":"../../../components/topTitle/topTitle",
  "calendar":"../components/calendar/calendar"
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。