// calendar.js const storage = require('../../utils/storage') const dateUtils = require('../../utils/date') const lunar = require('../../utils/lunar') const { TYPE_NAMES, TYPE_ICONS, IMPORTANCE_COLORS } = require('../../utils/constants') // 算出某条纪念日在指定年份的公历 (month, day) // 公历纪念日:每年同月同日,直接用录入时的 solarMonth/solarDay // 农历纪念日:把农历月日转回当年公历,因为农历对应的公历日期每年都不同 function getAnniversaryDateInYear(a, year) { if (a.isLunar && a.lunarMonth && a.lunarDay) { const date = lunar.lunarToSolar(year, a.lunarMonth, a.lunarDay, !!a.isLeapMonth) return { month: date.getMonth() + 1, day: date.getDate() } } return { month: a.solarMonth, day: a.solarDay } } Page({ data: { currentYear: new Date().getFullYear(), currentMonth: new Date().getMonth() + 1, calendarDays: [], monthEvents: [] }, onLoad() { const today = new Date() this.setData({ currentYear: today.getFullYear(), currentMonth: today.getMonth() + 1 }) this.renderCalendar() this.loadMonthEvents() }, /** * 构建当月事件索引 { "day": [anniversary, ...] } */ buildEventIndex(anniversaries, year, month) { const index = {} for (const a of anniversaries) { const { month: m, day: d } = getAnniversaryDateInYear(a, year) if (m === month) { const key = String(d) if (!index[key]) index[key] = [] index[key].push(a) } } return index }, /** * 渲染日历 */ renderCalendar() { const { currentYear, currentMonth } = this.data const anniversaries = storage.getAnniversaries() // 预建事件索引,避免 O(n×m) 过滤 const eventIndex = this.buildEventIndex(anniversaries, currentYear, currentMonth) const firstDay = new Date(currentYear, currentMonth - 1, 1) const startWeekday = firstDay.getDay() const daysInMonth = new Date(currentYear, currentMonth, 0).getDate() const prevMonthDays = new Date(currentYear, currentMonth - 1, 0).getDate() const calendarDays = [] // 填充上个月的日期 for (let i = startWeekday - 1; i >= 0; i--) { const day = prevMonthDays - i calendarDays.push({ day, isToday: false, isOtherMonth: true, events: [], hasMore: false }) } // 填充本月的日期 for (let day = 1; day <= daysInMonth; day++) { const date = new Date(currentYear, currentMonth - 1, day) const isToday = dateUtils.isToday(date) const allEvents = eventIndex[String(day)] || [] const events = allEvents.slice(0, 3).map(a => ({ id: a.id, color: this.getEventColor(a) })) calendarDays.push({ day, isToday, isOtherMonth: false, events, hasMore: allEvents.length > 3, moreCount: allEvents.length - 3 }) } // 填充下个月的日期(补全6行) const remainingDays = 42 - calendarDays.length for (let day = 1; day <= remainingDays; day++) { calendarDays.push({ day, isToday: false, isOtherMonth: true, events: [], hasMore: false }) } this.setData({ calendarDays }) }, /** * 获取事件颜色 */ getEventColor(anniversary) { return IMPORTANCE_COLORS[anniversary.importance] || '#07c160' }, /** * 加载本月事件 */ loadMonthEvents() { const { currentYear, currentMonth } = this.data const persons = storage.getPersons() const anniversaries = storage.getAnniversaries() const monthEvents = anniversaries .map(a => { const { month: m, day: d } = getAnniversaryDateInYear(a, currentYear) return { ...a, _displayMonth: m, _displayDay: d } }) .filter(x => x._displayMonth === currentMonth) .map(x => { const person = persons.find(p => p.id === x.personId) const date = new Date(currentYear, x._displayMonth - 1, x._displayDay) const ld = lunar.solarToLunar(date) return { ...x, personName: person ? person.name : '未知', typeIcon: this.getTypeIcon(x.type), typeName: x.customTypeName || this.getTypeName(x.type), dateText: dateUtils.formatDate(date, 'MM月DD日'), lunarMD: ld.lunarText, daysUntil: dateUtils.getDaysUntil(date) } }) .sort((a, b) => a._displayDay - b._displayDay) this.setData({ monthEvents }) }, /** * 获取类型图标 */ getTypeIcon(type) { return TYPE_ICONS[type] || '📅' }, /** * 获取类型名称 */ getTypeName(type) { return TYPE_NAMES[type] || '其他' }, /** * 上一个月 */ onPrevMonth() { let { currentYear, currentMonth } = this.data if (currentMonth === 1) { currentYear-- currentMonth = 12 } else { currentMonth-- } this.setData({ currentYear, currentMonth }) this.renderCalendar() this.loadMonthEvents() }, /** * 下一个月 */ onNextMonth() { let { currentYear, currentMonth } = this.data if (currentMonth === 12) { currentYear++ currentMonth = 1 } else { currentMonth++ } this.setData({ currentYear, currentMonth }) this.renderCalendar() this.loadMonthEvents() }, /** * 回到今天 */ onGoToday() { const today = new Date() this.setData({ currentYear: today.getFullYear(), currentMonth: today.getMonth() + 1 }) this.renderCalendar() this.loadMonthEvents() }, /** * 点击事件 */ onEventTap(e) { const id = e.currentTarget.dataset.id const anniversary = storage.getAnniversaries().find(a => a.id === id) if (anniversary) { wx.navigateTo({ url: `/pages/person-detail/person-detail?id=${anniversary.personId}` }) } } })