// components/calendar/index.ts interface DayInfo { date: string; // YYYY-MM-DD year: number; month: number; day: number; isCurrentMonth: boolean; hasJournal: boolean; isToday: boolean; } interface MonthInfo { year: number; month: number; days: DayInfo[]; } interface CalendarData { currentYear: number; months: MonthInfo[]; scrollIntoView: string; // 滚动到的元素 id } Component({ properties: { // 日记数据,key 为日期 YYYY-MM-DD,value 为日记 id 数组 journalMap: { type: Object, value: {} } }, data: { currentYear: 0, months: [], scrollIntoView: '' }, lifetimes: { attached() { this.initCalendar(); } }, observers: { 'journalMap.**'(journalMap: Record) { // 日记数据更新时重新生成所有月份的日期 if (this.data.months.length > 0) { this.updateAllMonthsDays(journalMap); } } }, methods: { /** 初始化日历 */ initCalendar() { const now = new Date(); const currentYear = now.getFullYear(); const currentMonth = now.getMonth() + 1; this.setData({ currentYear, months: this.generateYearMonths(currentYear) }); // 滚动到当前月份 this.scrollToMonth(currentMonth); }, /** 生成指定年份的 12 个月 */ generateYearMonths(year: number): MonthInfo[] { const months: MonthInfo[] = []; const today = new Date(); const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; for (let month = 1; month <= 12; month++) { months.push({ year, month, days: this.generateDaysForMonth(year, month, this.data.journalMap || {}, todayStr) }); } return months; }, /** 更新所有月份的日期数据 */ updateAllMonthsDays(journalMap: Record) { const { months } = this.data; const today = new Date(); const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; const updatedMonths = months.map(monthInfo => ({ ...monthInfo, days: this.generateDaysForMonth(monthInfo.year, monthInfo.month, journalMap, todayStr) })); this.setData({ months: updatedMonths }); }, /** 生成指定月份的日期数据 */ generateDaysForMonth(year: number, month: number, journalMap: Record, todayStr: string): DayInfo[] { const days: DayInfo[] = []; // 当前月第一天 const firstDay = new Date(year, month - 1, 1); const firstDayWeek = firstDay.getDay(); // 当前月最后一天 const lastDay = new Date(year, month, 0); const lastDate = lastDay.getDate(); // 上个月需要显示的日期 const prevMonthLastDay = new Date(year, month - 1, 0); const prevMonthLastDate = prevMonthLastDay.getDate(); for (let i = firstDayWeek - 1; i >= 0; i--) { const day = prevMonthLastDate - i; const prevMonth = month === 1 ? 12 : month - 1; const prevYear = month === 1 ? year - 1 : year; const dateKey = `${prevYear}-${String(prevMonth).padStart(2, '0')}-${String(day).padStart(2, '0')}`; days.push({ date: dateKey, year: prevYear, month: prevMonth, day, isCurrentMonth: false, hasJournal: !!journalMap[dateKey], isToday: dateKey === todayStr }); } // 当前月的日期 for (let day = 1; day <= lastDate; day++) { const dateKey = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`; days.push({ date: dateKey, year, month, day, isCurrentMonth: true, hasJournal: !!journalMap[dateKey], isToday: dateKey === todayStr }); } // 下个月需要显示的日期(补齐到 35 个格子,6 行) const remainingDays = 35 - days.length; for (let day = 1; day <= remainingDays; day++) { const nextMonth = month === 12 ? 1 : month + 1; const nextYear = month === 12 ? year + 1 : year; const dateKey = `${nextYear}-${String(nextMonth).padStart(2, '0')}-${String(day).padStart(2, '0')}`; days.push({ date: dateKey, year: nextYear, month: nextMonth, day, isCurrentMonth: false, hasJournal: !!journalMap[dateKey], isToday: dateKey === todayStr }); } return days; }, /** 切换年份 */ changeYear(delta: number) { const newYear = this.data.currentYear + delta; this.setData({ currentYear: newYear, months: this.generateYearMonths(newYear) }); // 滚动到第一个月 this.scrollToMonth(1); }, /** 上一年 */ prevYear() { this.changeYear(-1); }, /** 下一年 */ nextYear() { this.changeYear(1); }, /** 滚动到指定月份 */ scrollToMonth(month: number) { // 先清空,然后设置,触发滚动 this.setData({ scrollIntoView: '' }); wx.nextTick(() => { this.setData({ scrollIntoView: `month-${month}` }); }); }, /** 点击日期 */ onDayTap(e: WechatMiniprogram.BaseEvent) { const { date, year, month, day } = e.currentTarget.dataset; // 触发自定义事件,传递选中的日期信息 this.triggerEvent('dateselect', { date, year, month, day }); } } });