本文实例为大家分享了小程序实现日历打卡功能的具体代码,供大家参考,具体内容如下
一、效果图展示
老惯例,先上效果图
二、实现思路
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&¤tmonth==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】
component({ /** * 组件的属性列表 */ 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({ premonthdatelen }) 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="getobj" 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" } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。