小程序---小程序实现日历打卡功能
打卡日历页面存在展示35个或42个日期数据的情况,35个或42个日期数据=当前显示月所有日期数据+上月残余尾部日期+下月残余头部日期。通过数据库查询当前已打卡的数据,已打卡的数据需要设置打卡按钮禁用标志。打卡按钮禁用的情况(1)页面初始化时,未点击任何日期(2)当前点击的日期在今天之后(3)当前日期在今天之前但已打卡。点击打卡,记录打卡日期并保存至数据库。
一、效果图展示
老惯例,先上效果图
二、实现思路
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"
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)