diff --git a/miniprogram/note.md b/miniprogram/note.md new file mode 100644 index 0000000..4ae21ff --- /dev/null +++ b/miniprogram/note.md @@ -0,0 +1 @@ +当前版本:2.1.8 \ No newline at end of file diff --git a/miniprogram/pages/daily/daily.ts b/miniprogram/pages/daily/daily.ts index db4071a..896ccfb 100644 --- a/miniprogram/pages/daily/daily.ts +++ b/miniprogram/pages/daily/daily.ts @@ -1,189 +1,178 @@ -// 定义日历日期类型 +// pages/checkin/checkin.ts + interface CalendarDay { - type: 'current' | 'empty'; // current:当月日期, empty:空占位(上月/下月) - day: number; // 日期数字 - date: string; // 完整日期(YYYY-MM-DD) - isChecked: boolean; // 是否已签到 - isToday: boolean; // 是否是今天 -} - -Page({ - data: { - currentYear: new Date().getFullYear(), // 当前年 - currentMonth: new Date().getMonth(), // 当前月(0-11) - calendarDays: [] as CalendarDay[], // 日历日期列表 - checkinHistory: [] as string[], // 签到历史(存储YYYY-MM-DD格式日期) - continuousCheckinDays: 0, // 连续签到天数 - totalCheckinDays: 0, // 本月总签到天数 - isCheckedToday: false, // 今日是否已签到 - isAnimating: false // 是否正在执行签到动画 - }, - - onLoad() { - // 从本地缓存加载签到历史 - this.loadCheckinHistory(); - // 生成当月日历数据 - this.generateCalendarDays(); - // 计算签到统计数据 - this.calculateCheckinStats(); - }, - - /** - * 从本地缓存加载签到历史 - */ - loadCheckinHistory() { - const history = wx.getStorageSync('checkinHistory') || []; - this.setData({ checkinHistory: history }); - - // 检查今日是否已签到 - const today = this.formatDate(new Date()); - const isCheckedToday = history.includes(today); - this.setData({ isCheckedToday }); - }, - - /** - * 生成当月日历数据 - */ - generateCalendarDays() { - const { currentYear, currentMonth, checkinHistory } = this.data; - const calendarDays: CalendarDay[] = []; - - // 获取当月第一天是星期几(0=周日,1=周一...6=周六) - const firstDayOfMonth = new Date(currentYear, currentMonth, 1).getDay(); - // 获取当月总天数 - const totalDaysOfMonth = new Date(currentYear, currentMonth + 1, 0).getDate(); - // 获取今日日期(YYYY-MM-DD) - const today = this.formatDate(new Date()); - - // 添加上月残留日期(空占位) - for (let i = 0; i < firstDayOfMonth; i++) { - calendarDays.push({ - type: 'empty', - day: 0, - date: '', - isChecked: false, - isToday: false - }); - } - - // 添加当月日期 - for (let day = 1; day <= totalDaysOfMonth; day++) { - const date = new Date(currentYear, currentMonth, day); - const dateStr = this.formatDate(date); - const isChecked = checkinHistory.includes(dateStr); - const isToday = dateStr === today; - - calendarDays.push({ - type: 'current', - day, - date: dateStr, - isChecked, - isToday - }); - } - - // 添加下月占位日期(确保日历满6行,共42个格子) - const totalCells = 42; // 6行×7列=42个格子 - const remainingCells = totalCells - calendarDays.length; - for (let i = 0; i < remainingCells; i++) { - calendarDays.push({ - type: 'empty', - day: 0, - date: '', - isChecked: false, - isToday: false - }); - } - - this.setData({ calendarDays }); - }, - - /** - * 计算签到统计数据 - */ - calculateCheckinStats() { - const { checkinHistory, currentYear, currentMonth } = this.data; - const today = new Date(); - - // 计算本月总签到天数 - const monthStart = new Date(currentYear, currentMonth, 1); - const monthEnd = new Date(currentYear, currentMonth + 1, 0); - const monthStartStr = this.formatDate(monthStart); - const monthEndStr = this.formatDate(monthEnd); - - const monthlyCheckins = checkinHistory.filter(date => { - return date >= monthStartStr && date <= monthEndStr; - }); - - // 计算连续签到天数 - let continuousDays = 0; - const sortedHistory = [...checkinHistory].sort(); - - // 从今天开始往前查连续签到 - const checkDate = new Date(today); - while (true) { - const dateStr = this.formatDate(checkDate); - if (sortedHistory.includes(dateStr)) { - continuousDays++; - // 往前推一天 - checkDate.setDate(checkDate.getDate() - 1); - } else { - break; - } - } - - this.setData({ - totalCheckinDays: monthlyCheckins.length, - continuousCheckinDays: continuousDays - }); - }, - - /** - * 处理签到逻辑 - */ - handleCheckin() { - if (this.data.isCheckedToday || this.data.isAnimating) return; - - // 开始动画 - this.setData({ isAnimating: true }); - - // 记录当前日期 - const today = this.formatDate(new Date()); - const { checkinHistory } = this.data; - - // 添加到签到历史 - const newHistory = [...checkinHistory, today]; - wx.setStorageSync('checkinHistory', newHistory); - - // 更新数据 - setTimeout(() => { - this.setData({ - checkinHistory: newHistory, - isCheckedToday: true, - isAnimating: false - }); - - // 更新日历和统计 - this.generateCalendarDays(); - this.calculateCheckinStats(); - - // 显示成功提示 - wx.showToast({ - title: '签到成功!', - icon: 'success', - duration: 1500 - }); - }, 1000); // 动画持续1秒 - }, - - /** - * 格式化日期为YYYY-MM-DD - */ - formatDate(date: Date): string { - const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, '0'); - const day = String(date.getDate()).padStart(2, '0'); - return `${year}-${month}-${day}`; + day: number | string; // 日期数字或空字符串 + fullDate?: string; // 完整日期字符串,如 'YYYY-MM-DD' + isPrevMonth?: boolean; // 是否上月日期 + isNextMonth?: boolean; // 是否下月日期 + isToday?: boolean; // 是否今天 + isCheckedIn?: boolean; // 是否已签到 } -}); - \ No newline at end of file + + Page({ + data: { + weekdays: ['日', '一', '二', '三', '四', '五', '六'], + currentYear: 0, + currentMonth: 0, // 1-12月 + calendarDays: [] as CalendarDay[], // 日历日期数据 + + consecutiveCheckInDays: 0, // 连续签到天数 + monthCheckInDays: 0, // 本月签到天数 + todayCheckedIn: false, // 今日是否已签到 + + // 假设这些是用户已签到的日期(仅日期数字),在真实项目中从后端获取 + checkedInDates: [] as number[], // [1, 5, 18, 29] 表示当月哪些天已签到 + + // 用于保存当前显示的月份的Date对象,方便计算 + displayMonthDate: new Date(), + }, + + onLoad() { + this.initCalendar(); + this.fetchCheckInStats(); // 模拟从后端获取签到数据 + }, + + /** + * 初始化日历,生成当前月份的日期 + */ + initCalendar() { + const today = new Date(); + this.data.displayMonthDate = new Date(today.getFullYear(), today.getMonth(), 1); // 设置为当前月的第一天 + + this.renderCalendar(this.data.displayMonthDate); + }, + + /** + * 渲染指定月份的日历 + * @param date 当前月份的任意一天,例如 new Date(2025, 8, 1) 表示 2025年9月 + */ + renderCalendar(date: Date) { + const year = date.getFullYear(); + const month = date.getMonth(); // 0-11 + + this.setData({ + currentYear: year, + currentMonth: month + 1, // 显示给用户是1-12月 + }); + + const calendarDays: CalendarDay[] = []; + const firstDayOfMonth = new Date(year, month, 1); + const lastDayOfMonth = new Date(year, month + 1, 0); // 当月最后一天 + + // 补充上个月的日期(如果需要) + const startDayOfWeek = firstDayOfMonth.getDay(); // 0-6,0是周日 + for (let i = 0; i < startDayOfWeek; i++) { + calendarDays.push({ day: '', isPrevMonth: true }); // 用空字符串占位或显示上月日期 + } + + // 填充当前月份的日期 + for (let i = 1; i <= lastDayOfMonth.getDate(); i++) { + const currentDay = new Date(year, month, i); + const isToday = this.isSameDay(currentDay, new Date()); + const isCheckedIn = this.data.checkedInDates.includes(i); // 判断是否已签到 + + calendarDays.push({ + day: i, + fullDate: this.formatDate(currentDay), + isToday: isToday, + isCheckedIn: isCheckedIn, + }); + } + + // 补充下个月的日期(如果需要,凑够6行或7的倍数) + const totalDays = calendarDays.length; + const remainingDays = 42 - totalDays; // 假设日历固定6行7列,共42个格子 + for (let i = 0; i < remainingDays; i++) { + calendarDays.push({ day: '', isNextMonth: true }); // 用空字符串占位 + } + + this.setData({ + calendarDays: calendarDays, + }); + }, + + /** + * 模拟从后端获取签到统计数据和已签到日期 + */ + fetchCheckInStats() { + // 真实项目中这里会发起 wx.request 到您的后端接口 + // 假设后端返回的数据格式如下: + const mockApiResponse = { + consecutiveCheckInDays: 29, // 连续签到天数 + monthCheckInDays: 15, // 本月签到天数 + todayCheckedIn: false, // 假设今天未签到 + checkedInDaysInMonth: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 29], // 当月已签到的日期数字 + }; + + this.setData({ + consecutiveCheckInDays: mockApiResponse.consecutiveCheckInDays, + monthCheckInDays: mockApiResponse.monthCheckInDays, + todayCheckedIn: mockApiResponse.todayCheckedIn, + checkedInDates: mockApiResponse.checkedInDaysInMonth, + }); + + // 重新渲染日历以应用新的签到状态 + this.renderCalendar(this.data.displayMonthDate); + }, + + /** + * 处理签到按钮点击 + */ + handleCheckIn() { + if (this.data.todayCheckedIn) { + wx.showToast({ title: '今日已签到', icon: 'none' }); + return; + } + + // 模拟后端签到逻辑 + wx.showLoading({ title: '签到中...' }); + setTimeout(() => { + // 假设后端返回签到成功 + const today = new Date().getDate(); + const newCheckedInDates = [...this.data.checkedInDates, today]; + + this.setData({ + todayCheckedIn: true, + consecutiveCheckInDays: this.data.consecutiveCheckInDays + 1, // 假设连续+1 + monthCheckInDays: this.data.monthCheckInDays + 1, // 假设当月+1 + checkedInDates: newCheckedInDates, + }); + + // 重新渲染日历,标记今天为已签到 + this.renderCalendar(this.data.displayMonthDate); + + wx.hideLoading(); + wx.showToast({ title: '签到成功!', icon: 'success' }); + }, 1000); // 模拟网络请求 + }, + + /** + * 处理日历中的日期点击(可选,如果需要点击日期查看历史签到等) + */ + handleDayClick(event: WechatMiniprogram.TouchEvent) { + const fullDate = event.currentTarget.dataset.date; + if (fullDate) { + console.log('点击了日期:', fullDate); + // 可在此处实现点击日期查看历史签到详情等逻辑 + } + }, + + /** + * 辅助函数:判断两个日期是否是同一天 (忽略时间) + */ + isSameDay(date1: Date, date2: Date): boolean { + return date1.getFullYear() === date2.getFullYear() && + date1.getMonth() === date2.getMonth() && + date1.getDate() === date2.getDate(); + }, + + /** + * 辅助函数:格式化日期为 YYYY-MM-DD + */ + formatDate(date: Date): string { + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const day = date.getDate().toString().padStart(2, '0'); + return `${year}-${month}-${day}`; + }, + }); \ No newline at end of file diff --git a/miniprogram/pages/daily/daily.wxml b/miniprogram/pages/daily/daily.wxml index c33aaf6..336e63a 100644 --- a/miniprogram/pages/daily/daily.wxml +++ b/miniprogram/pages/daily/daily.wxml @@ -1,107 +1,53 @@ - - - - {{currentYear}}年{{currentMonth + 1}}月 - + + + {{ currentYear }}年{{ currentMonth }}月 + - {{continuousCheckinDays}} - 连续签到 + {{ consecutiveCheckInDays }} + 连续签到 + - {{totalCheckinDays}} - 本月签到 + {{ monthCheckInDays }} + 本月签到 + + + + 签到 + + + 已签到 + + + 今日已完成签到,明天继续哦~ + 点击签到,记录美好生活~ - - - - - {{isCheckedToday ? '今日已完成签到,明天继续哦~' : '点击按钮完成今日签到,领取奖励'}} + + + 签到日历 + + + + + {{ item }} + - - - - 签到日历 - - - - - - - - - - - - - + + - - {{item.type !== 'empty' ? item.day : ''}} + + {{ item.day }} - - - - - - 签到奖励 - - - 连续签到3天 - - 获得10积分 - - - 已达成 - - - - 连续签到7天 - - 获得30积分 + 优惠券 - - - 已达成 - - - - 连续签到15天 - - 获得50积分 + 专属权益 - - - 已达成 - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/miniprogram/pages/daily/daily.wxss b/miniprogram/pages/daily/daily.wxss index 166ec96..4432ee6 100644 --- a/miniprogram/pages/daily/daily.wxss +++ b/miniprogram/pages/daily/daily.wxss @@ -1,271 +1,198 @@ -/* 全局样式 */ +/* pages/checkin/checkin.wxss */ page { - background-color: #f7f8fa; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - min-height: 100vh; -} - -/* 容器样式 */ -.checkin-container { - padding: 20rpx; - box-sizing: border-box; -} - -/* 顶部信息区 */ -.header { - background-color: #51bdb6; - border-radius: 20rpx; - padding: 30rpx; - color: #fff; - margin-bottom: 30rpx; - box-shadow: 0 8rpx 16rpx rgba(81, 189, 182, 0.2); -} - -.month-info { - font-size: 32rpx; - font-weight: 500; - margin-bottom: 20rpx; - text-align: center; -} - -.checkin-stats { - display: flex; - justify-content: space-around; - align-items: center; -} - -.stat-item { - display: flex; - flex-direction: column; - align-items: center; -} - -.stat-value { - font-size: 48rpx; - font-weight: bold; - line-height: 1; - margin-bottom: 10rpx; -} - -.stat-label { - font-size: 24rpx; - opacity: 0.9; -} - -/* 签到按钮区 */ -.checkin-button-area { - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 40rpx; -} - -.checkin-btn { - width: 240rpx; - height: 240rpx; - border-radius: 50%; - background-color: #51bdb6; - color: #fff; - font-size: 34rpx; - font-weight: 500; - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 20rpx; - box-shadow: 0 10rpx 20rpx rgba(81, 189, 182, 0.3); - transition: all 0.3s ease; - border: none; - padding: 0; -} - -.checkin-btn.checked { - background-color: #e6f7f6; - color: #51bdb6; - box-shadow: 0 5rpx 15rpx rgba(81, 189, 182, 0.2); -} - -.checkin-btn.animating { - animation: pulse 1s ease-in-out; -} - -@keyframes pulse { - 0% { transform: scale(1); } - 50% { transform: scale(1.1); } - 100% { transform: scale(1); } -} - -.btn-content { - display: flex; - flex-direction: column; - align-items: center; -} - -/* 签到按钮内部的对勾图标样式控制 */ -.checkin-btn .check-icon { - width: 40rpx; - height: auto; - margin-top: 10rpx; - display: none; /* 默认隐藏 */ -} - -.checkin-btn.checked .check-icon { - display: block; /* 仅在已签到状态下显示 */ -} - -.checkin-tip { - font-size: 26rpx; - color: #666; - text-align: center; - line-height: 1.5; -} - -/* 日历签到区 - 核心修复区域 */ -.calendar-section { - background-color: #fff; - border-radius: 20rpx; - padding: 30rpx; - margin-bottom: 30rpx; - box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); -} - -.section-title { - font-size: 30rpx; - font-weight: 500; - color: #333; - margin-bottom: 25rpx; - padding-left: 10rpx; - border-left: 6rpx solid #51bdb6; -} - -.weekdays { - display: flex; - margin-bottom: 15rpx; -} - -.weekday { - flex: 1; - text-align: center; - font-size: 26rpx; - color: #999; - padding: 10rpx 0; -} - -.calendar-grid { - display: flex; - flex-wrap: wrap; - width: 100%; -} - -/* 日期格子容器 */ -.calendar-day { - width: 14.2857%; /* 7天平均分配宽度 */ - aspect-ratio: 1; /* 宽高比1:1 */ - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 8rpx; - box-sizing: border-box; -} - -/* 日期数字和圆圈 - 重点修复 */ -.day-number { - width: 60rpx; - height: 60rpx; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - font-size: 28rpx; - font-weight: 500; - position: relative; - line-height: 1; - color: #333; /* 默认颜色 */ -} - -/* 已签到状态 - 关键修复,确保数字可见 */ -.calendar-day.checked .day-number { - background-color: #51bdb6; /* 绿色背景 */ - color: #ffffff; /* **纯白色文字,解决覆盖问题** */ - font-weight: 600; - box-shadow: 0 2rpx 8rpx rgba(81, 189, 182, 0.4); -} - -/* 今日未签到状态 */ -.calendar-day.today .day-number { - border: 2rpx solid #51bdb6; - color: #51bdb6; - font-weight: 600; -} - -/* 空日期(上月/下月) */ -.calendar-day.empty .day-number { - color: #eee; - background: transparent; -} - -/* 签到对勾 - 调整位置 */ -.check-mark { - position: absolute; - /* 将对勾放在圆圈的右下角作为修饰 */ - right: 15rpx; - bottom: 10rpx; - width: 24rpx !important; - height: 24rpx !important; - opacity: 0.9; - z-index: 10; -} - -/* 奖励区 */ -.rewards-section { - background-color: #fff; - border-radius: 20rpx; - padding: 30rpx; - box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); -} - -.rewards-list { - display: flex; - flex-direction: column; - gap: 20rpx; -} - -.reward-item { - display: flex; - align-items: center; - padding: 25rpx; - border-radius: 12rpx; - background-color: #f7f8fa; - transition: all 0.3s ease; -} - -.reward-item.achieved { - background-color: #e6f7f6; - border-left: 6rpx solid #51bdb6; -} - -.reward-days { - font-size: 28rpx; - color: #333; - width: 160rpx; -} - -.reward-separator { - font-size: 24rpx; - color: #ccc; - margin: 0 10rpx; -} - -.reward-content { - font-size: 28rpx; - color: #666; - flex: 1; -} - -.reward-status { - display: flex; - align-items: center; - font-size: 24rpx; - color: #51bdb6; - gap: 8rpx; -} \ No newline at end of file + background-color: #f4f6f8; + font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Arial, sans-serif; + /* ⭐️ 移除这里的 padding */ + } + + /* ⭐️ 将 padding 添加到这里 */ + .container { + padding: 32rpx; + display: flex; + flex-direction: column; + gap: 32rpx; /* 间距 */ + } + + /* 顶部统计卡片 */ + .summary-card { + background: linear-gradient(135deg, #74cdb9, #52b19e); /* 渐变绿 */ + border-radius: 20rpx; + padding: 40rpx 32rpx; + box-shadow: 0 8rpx 24rpx rgba(82, 177, 158, 0.3); + color: #fff; + display: flex; + flex-direction: column; + align-items: center; + } + + .month-display { + font-size: 30rpx; + margin-bottom: 30rpx; + font-weight: 500; + } + + .stats-row { + display: flex; + justify-content: space-around; + width: 100%; + margin-bottom: 40rpx; + align-items: center; + } + + .stat-item { + display: flex; + flex-direction: column; + align-items: center; + width: 45%; /* 均分空间 */ + } + + .stat-number { + font-size: 68rpx; + font-weight: bold; + line-height: 1; + margin-bottom: 10rpx; + } + + .stat-label { + font-size: 26rpx; + opacity: 0.8; + } + + .divider-v { + width: 2rpx; + height: 80rpx; + background-color: rgba(255, 255, 255, 0.4); + align-self: center; + } + + .today-checkin-box { + margin-top: 30rpx; + margin-bottom: 20rpx; + } + + .checkin-button, .checked-in-status { + width: 180rpx; + height: 180rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 36rpx; + font-weight: 600; + transition: all 0.2s ease-in-out; + } + + .checkin-button { + background-color: rgba(255, 255, 255, 0.2); + border: 4rpx solid rgba(255, 255, 255, 0.6); + color: #fff; + } + + .checkin-button:active { + background-color: rgba(255, 255, 255, 0.3); + transform: scale(0.98); + } + + .checked-in-status { + background-color: rgba(255, 255, 255, 0.3); /* 已签到的淡背景 */ + border: 4rpx solid rgba(255, 255, 255, 0.4); + color: #fff; + } + + .checkin-tip { + font-size: 26rpx; + opacity: 0.9; + text-align: center; + margin-top: 10rpx; + } + + /* 签到日历卡片 */ + .calendar-card { + background-color: #ffffff; + border-radius: 20rpx; + padding: 32rpx; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08); + } + + .calendar-header { + padding-bottom: 24rpx; + border-bottom: 1rpx solid #eee; + margin-bottom: 24rpx; + } + + .calendar-title { + font-size: 32rpx; + font-weight: 600; + color: #333333; + } + + .calendar-weekdays { + display: grid; + grid-template-columns: repeat(7, 1fr); + text-align: center; + margin-bottom: 20rpx; + } + + .weekday-item { + font-size: 26rpx; + color: #999999; + padding: 10rpx 0; + } + + .calendar-days { + display: grid; + grid-template-columns: repeat(7, 1fr); + text-align: center; + } + + .day-item { + height: 80rpx; + display: flex; + align-items: center; + justify-content: center; + position: relative; + font-size: 30rpx; + color: #333333; + line-height: 1; /* 确保文字不撑开 */ + } + + /* 其他月份日期 */ + .day-item.other-month { + color: #cccccc; + } + + /* 今日高亮 */ + .day-item.today .day-content { + background-color: #ffeecc; /* 浅黄色背景 */ + border-radius: 50%; + width: 60rpx; + height: 60rpx; + display: flex; + align-items: center; + justify-content: center; + } + + /* 已签到日期样式 */ + .day-content.checked-in { + background-color: #52b19e; /* 绿色背景 */ + border-radius: 50%; + width: 60rpx; + height: 60rpx; + display: flex; + align-items: center; + justify-content: center; + color: #fff; /* 确保数字颜色为白色,在绿色背景上清晰可见 */ + font-weight: 600; + } + + /* 确保数字的样式 */ + .day-number { + z-index: 2; /* 确保数字在背景之上 */ + position: relative; /* 配合 z-index */ + } + + /* 卡片整体点击态 */ + .card-hover { + transform: scale(0.99); + box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1); + } \ No newline at end of file