// calendar.js const storage = require('../../utils/storage') const dateUtils = require('../../utils/date') const { TYPE_NAMES, TYPE_ICONS, IMPORTANCE_COLORS } = require('../../utils/constants') 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) { if (a.solarYear === year && a.solarMonth === month) { const key = String(a.solarDay) 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 .filter(a => a.solarYear === currentYear && a.solarMonth === currentMonth) .map(a => { const person = persons.find(p => p.id === a.personId) const date = new Date(currentYear, currentMonth - 1, a.solarDay) const daysUntil = dateUtils.getDaysUntil(date) return { ...a, personName: person ? person.name : '未知', typeIcon: this.getTypeIcon(a.type), typeName: a.customTypeName || this.getTypeName(a.type), dateText: dateUtils.formatDate(date, 'MM月DD日'), daysUntil } }) .sort((a, b) => a.solarDay - b.solarDay) 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}` }) } } })