vue实现每日签到日历
实现每日签到日历的基本思路
使用Vue实现每日签到日历需要结合日期处理和组件化设计。核心功能包括生成当月日历数据、标记签到状态以及交互逻辑。
日历数据生成
通过JavaScript的Date对象处理日期,生成当月所有天数数组,并标记是否为当前月份:
methods: {
generateCalendar() {
const date = new Date()
const year = date.getFullYear()
const month = date.getMonth()
const firstDay = new Date(year, month, 1)
const lastDay = new Date(year, month + 1, 0)
const days = []
// 上个月末尾几天
for (let i = 0; i < firstDay.getDay(); i++) {
days.push({ day: null, currentMonth: false })
}
// 当月所有天数
for (let i = 1; i <= lastDay.getDate(); i++) {
days.push({
day: i,
currentMonth: true,
signed: this.isSigned(year, month, i)
})
}
return days
}
}
签到状态管理
使用Vuex或组件状态管理签到数据,通常需要与后端API交互:
methods: {
async handleSign(day) {
if (this.isSigned(day)) return
try {
await api.signIn() // 调用签到API
this.$set(day, 'signed', true)
} catch (error) {
console.error('签到失败', error)
}
},
isSigned(year, month, day) {
// 检查本地存储或状态中是否已签到
return this.signedDates.includes(`${year}-${month+1}-${day}`)
}
}
日历组件模板
使用网格布局展示日历,包含星期标题和日期单元格:
<template>
<div class="calendar">
<div class="weekdays">
<div v-for="day in ['日','一','二','三','四','五','六']" :key="day">
{{ day }}
</div>
</div>
<div class="days">
<div
v-for="(day, index) in days"
:key="index"
:class="{
'current-month': day.currentMonth,
'signed': day.signed,
'today': isToday(day)
}"
@click="handleSign(day)"
>
{{ day.day }}
<span v-if="day.signed" class="sign-mark">✓</span>
</div>
</div>
</div>
</template>
样式设计
基础日历样式确保布局整齐,突出显示可交互元素:
.calendar {
width: 100%;
max-width: 400px;
margin: 0 auto;
}
.weekdays, .days {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
}
.days div {
height: 40px;
line-height: 40px;
border: 1px solid #eee;
cursor: pointer;
}
.current-month {
background: white;
}
.signed {
background-color: #e8f5e9;
color: #2e7d32;
}
.today {
border: 2px solid #2196f3 !important;
}
.sign-mark {
color: #4caf50;
font-weight: bold;
}
完整组件示例
将上述代码整合为可复用的Vue组件:
export default {
data() {
return {
days: [],
signedDates: [] // 应从后端获取或本地存储加载
}
},
created() {
this.days = this.generateCalendar()
},
methods: {
// 包含前面提到的所有方法
isToday(day) {
if (!day.currentMonth) return false
const today = new Date()
return (
today.getDate() === day.day &&
today.getMonth() === this.currentMonth
)
}
}
}
进阶功能实现
对于更复杂的需求,可以考虑以下扩展:
连续签到计数功能需要后端支持记录连续签到天数,前端展示进度:
computed: {
continuousDays() {
// 计算连续签到天数逻辑
return this.signedDates.filter(/* 连续条件 */).length
}
}
签到奖励系统可在连续签到特定天数时触发:
watch: {
continuousDays(newVal) {
if (newVal === 3) this.showReward(1)
if (newVal === 7) this.showReward(2)
}
}
响应式设计确保在不同设备上正常显示:
@media (max-width: 600px) {
.days div {
height: 30px;
line-height: 30px;
font-size: 12px;
}
}






