在components目录下创建一个vue文件
<template>
<view class="calendar">
<view class="header" style="display: flex; justify-content: space-between; align-items: center; margin-top: 50rpx; background-color: #f8f8f8;">
<view class="month">
{{currentDate}}
</view>
<view class="year">
{{year}}
</view>
<!-- <view class="current-date">{{ currentDate }}</view> -->
<view class="btn btn-prev" @click="onSwitchPrevMonth()">
<uni-icons style="margin-left: 300rpx;" type="left" size="24"></uni-icons>
</view>
<view class="btn btn-next" @click="onSwitchNextMonth()">
<uni-icons type="right" size="24"></uni-icons>
</view>
</view>
<view class="body">
<view class="weeks">
<view class="week-item" v-for="week in weeks" :key="week"
>{{ week }}
</view>
</view>
<view class="day-list">
<view
:class="{
'day-item': true,
today: day.isToday,
checked: day.checked,
'current-month-day': day.type === 'current'
}"
v-for="(day, index) in dayList"
:key="index"
>
<text class="text">{{ day.checked ? '√' : day.day }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
checks: {
type: Array,
default() {
return []
}
}
},
data() {
return {
year: null,
month: null,
day: null,
dayList: [],
weeks: ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
}
},
computed: {
currentDate() {
const year = this.year
const month = this.month < 10 ? '0' + this.month : this.month
return month
}
},
watch: {
checks() {
this.initDate()
}
},
created() {
const { year, month, day } = this.getCurrentDate()
this.initDate(year, month, day)
},
methods: {
initDate(year = this.year, month = this.month, day = this.day) {
const dayList = this.getDayList(year, month)
this.year = year
this.month = month
this.day = day
this.dayList = dayList
},
//点击日期签到
handleClick(day) {
const payload = {
year: this.year,
month: this.month,
...day
}
switch (day.type) {
case 'prev':
this.onSwitchPrevMonth(day)
break
case 'next':
this.onSwitchNextMonth(day)
break
default:
this.$emit('current-month-day', payload)
}
},
onSwitchPrevMonth(day) {
const { prevYear, prevMonth } = this.getPrevMonthInfo(
this.year,
this.month
)
const payload = {
year: prevYear,
month: prevMonth,
oldYear: this.year,
oldMonth: this.month,
...day
}
this.initDate(prevYear, prevMonth)
this.$emit('prev-month-day', payload)
},
onSwitchNextMonth(day) {
console.log(111)
const { nextYear, nextMonth } = this.getNextMonthInfo(
this.year,
this.month
)
const payload = {
year: nextYear,
month: nextMonth,
oldYear: this.year,
oldMonth: this.month,
...day
}
this.initDate(nextYear, nextMonth)
this.$emit('next-month-day', payload)
},
getPrevMonthInfo(year, month) {
let prevYear = year
let prevMonth = month - 1
if (prevMonth < 1) {
prevMonth = 12
prevYear -= 1
}
return {
prevYear,
prevMonth
}
},
getNextMonthInfo(year, month) {
let nextYear = year
let nextMonth = month + 1
if (nextMonth > 12) {
nextMonth = 1
nextYear += 1
}
return {
nextYear,
nextMonth
}
},
getDayList(year, month) {
const prevMonthDays = this.getPrevMonthDays(year, month)
const currentMonthDays = this.getCurrentMonthDays(year, month)
const nextMonthDays = this.getNextMonthDays(year, month)
return [...prevMonthDays, ...currentMonthDays, ...nextMonthDays]
},
getCurrentDate() {
const date = new Date()
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return {
year,
month,
day
}
},
getMonthLastDay(year, month) {
const { nextYear, nextMonth } = this.getNextMonthInfo(year, month)
const date = new Date(`${nextYear}/${nextMonth}/1`)
const firstDayTimeStamp = date.getTime()
const oneDayTimeStamp = 24 * 60 * 60 * 1000
const prevMonthLastDay = new Date(
firstDayTimeStamp - oneDayTimeStamp
).getDate()
return prevMonthLastDay
},
getPrevMonthDays(year, month) {
const { prevYear, prevMonth } = this.getPrevMonthInfo(year, month)
const prevMonthLastDay = this.getMonthLastDay(prevYear, prevMonth)
const date = new Date(`${year}/${month}/1`)
const week = date.getDay()
const days = []
for (let i = 0; i < week; i++) {
days.unshift({
type: 'prev',
checked: false,
day: prevMonthLastDay - i
})
}
return days
},
getCurrentMonthDays(year, month) {
const currentMonthLastDay = this.getMonthLastDay(year, month)
const checks = this.checks
const days = []
const {
year: currentYear,
month: currentMonth,
day: currentDay
} = this.getCurrentDate()
for (let i = 1; i <= currentMonthLastDay; i++) {
let checked = false
let isToday =
currentYear === year && currentMonth === month && currentDay === i
checks.forEach(date => {
const dateArr = date.split('-')
if (year == dateArr[0] && month == dateArr[1] && i == dateArr[2]) {
checked = true
}
})
days.push({
type: 'current',
checked,
isToday,
day: i
})
}
return days
},
getNextMonthDays(year, month) {
const currentMonthLastDay = this.getMonthLastDay(year, month)
const date = new Date(`${year}/${month}/${currentMonthLastDay}`)
const week = date.getDay()
const days = []
let day = 0
for (let i = week + 1; i <= 6; i++) {
day++
days.push({
type: 'next',
checked: false,
day
})
}
return days
}
}
}
</script>
<style lang="scss" scoped>
.calendar {
width: 702rpx;
margin: 0 auto;
background-color: #fff;
color: $uni-text-color;
.header {
padding: 0 24upx;
display: flex;
justify-content: space-between;
// border-bottom: 2upx solid #eee;
.current-date {
text-align: center;
font-size: 24upx;
padding: 32upx 0;
color: #030303;
}
.btn-group {
display: flex;
align-items: center;
.btn {
line-height: 1;
background: #fff;
border: 1px solid #dcdfe6;
color: #606266;
text-align: center;
box-sizing: border-box;
font-weight: 500;
padding: 12upx 20upx;
font-size: 24upx;
&.btn-prev {
border-radius: 2px 0 0 2px;
border-right: 0;
}
&.btn-next {
border-radius: 0 2px 2px 0;
}
}
}
}
.body {
.weeks {
display: flex;
font-size: 26upx;
padding: 32upx 0;
.week-item {
flex: 1;
text-align: center;
}
}
.day-list {
display: flex;
flex-wrap: wrap;
.day-item {
display: flex;
justify-content: center;
align-items: center;
width: 14.285%;
text-align: center;
padding: 20upx 0;
font-size: 36upx;
color: #c8c8c8;
&.current-month-day {
color: #171717;
}
&.checked,
&.today {
// padding: 26upx 0;
.text {
display: flex;
align-items: center;
justify-content: center;
width: 80upx;
height: 80upx;
// border-radius: 100%;
box-sizing: border-box;
background-color: #9263FE;
color: #fff;
font-size: 28upx;
padding: 28upx 0;
}
}
}
}
}
}
.year{
width: 108rpx;
height: 70rpx;
font-size: 48rpx;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #BFBFBF;
}
.month{
width: 54rpx;
height: 70rpx;
font-size: 48rpx;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #1A1A1A;
}
</style>
2.引入
<template>
<view class="box">
<view class="custom-title-box">
<view class="status-bar-height-box" :style="{height: statusBarHeight + 'px'}"></view>
<view class="title-box" :style="{height: titleHeight + 'px'}">
<uni-icons type="back" class="left" size="30" color="#fff" @click="back()"></uni-icons>
签到
</view>
</view>
<view class="empty-box" :style="{height: statusBarHeight + titleHeight + 'px'}" style="width: 100%;"></view>
<ss-calendar class="s" :checks="checks" @current-month-day="onSignIn" @prev-month-day="onPrev" @next-month-day="onNext" />
<button @click="signIn" class="click">签到</button>
</view>
</template>
<script>
import ssCalendar from '@/components/ss-calendar/ss-calendar.vue'
export default {
components: {
ssCalendar
},
data() {
return {
statusBarHeight: 0, // 状态栏高度
titleHeight: 0, // 标题栏高度
checks: ["2023-08-01"],
}
},
onLoad() {
this.checks = uni.getStorageSync("qd")
},
onReady() {
uni.getSystemInfo({
success: res => {
this.statusBarHeight = res.statusBarHeight
}
})
const btnInfo = uni.getMenuButtonBoundingClientRect();
this.titleHeight = (btnInfo.top - this.statusBarHeight) * 2 + btnInfo.height
},
methods: {
back() {
uni.navigateBack()
},
onSignIn(payload) {
const {
checked,
year,
month,
day
} = payload;
const today = new Date();
const todayYear = today.getFullYear();
const todayMonth = today.getMonth() + 1; // 月份从0开始计算,需要加1
const todayDay = today.getDate();
if (!checked && year === todayYear && month === todayMonth && day === todayDay) {
this.checks.push(`${year}-${month}-${day}`);
}
console.log('onSignIn', payload);
},
signIn() {
const currentDate = new Date().toISOString().split('T')[0]; // 获取当前日期的字符串表示,例如:Sat Nov 20 2021
if (!this.checks.includes(currentDate)) {
this.checks.push(currentDate);
// uni.setStorageSync("qd",this.checks)
uni.showToast({
title: "签到成功"
})
} else {
uni.showToast({
title: "今天已签过",
icon: "none"
})
console.log('今天已经签过到了!');
}
uni.setStorageSync("qd", this.checks)
},
onPrev(payload) {
console.log('onPrev', payload)
},
onNext(payload) {
console.log('onNext', payload)
}
},
}
</script>
<style lang="scss">
.custom-title-box {
width: 750rpx;
position: fixed;
top: 0;
left: 0;
z-index: 999;
background-color: #9263FE;
.title-box {
// background-color: red;
display: flex;
justify-content: center;
align-items: center;
font-size: 36rpx;
color: #FFFFFF;
>image {
width: 346rpx;
height: 96rpx;
}
.left {
width: 60rpx;
height: 60rpx;
position: absolute;
left: 24rpx;
bottom: 10rpx;
}
}
}
.box {
width: 750rpx;
height: 1624rpx;
background-color: #F8F8F8;
position: relative;
}
.s{
width: 702rpx;
margin: 0 auto;
}
.click{
width: 382rpx;
height: 110rpx;
background: #9263FE;
border-radius: 60rpx 60rpx 60rpx 60rpx;
opacity: 1;
font-size: 44rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 400;
color: #FFFFFF;
text-align: center;
line-height: 110rpx;
margin-top: 52rpx;
}
</style>