212 lines
6.7 KiB
JavaScript
212 lines
6.7 KiB
JavaScript
// 日历状态管理
|
||
class CalendarApp {
|
||
constructor() {
|
||
this.currentDate = new Date();
|
||
this.markedDates = this.loadData();
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
this.renderCalendar();
|
||
this.bindEvents();
|
||
}
|
||
|
||
// 获取月份的第一天是星期几(0=周日, 1=周一...)
|
||
getFirstDayOfMonth(date) {
|
||
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
|
||
return firstDay.getDay();
|
||
}
|
||
|
||
// 获取月份的天数
|
||
getDaysInMonth(date) {
|
||
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
||
}
|
||
|
||
// 格式化日期为 YYYY-MM-DD
|
||
formatDate(date) {
|
||
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}`;
|
||
}
|
||
|
||
// 检查是否为今天
|
||
isToday(date) {
|
||
const today = new Date();
|
||
return date.getDate() === today.getDate() &&
|
||
date.getMonth() === today.getMonth() &&
|
||
date.getFullYear() === today.getFullYear();
|
||
}
|
||
|
||
// 获取日期标记状态
|
||
getDateStatus(date) {
|
||
const dateStr = this.formatDate(date);
|
||
return this.markedDates[dateStr] || 'none';
|
||
}
|
||
|
||
// 切换日期标记状态
|
||
toggleDateStatus(date) {
|
||
const dateStr = this.formatDate(date);
|
||
const currentStatus = this.markedDates[dateStr] || 'none';
|
||
|
||
// 状态循环: none -> completed -> partial -> none
|
||
let newStatus;
|
||
switch (currentStatus) {
|
||
case 'none':
|
||
newStatus = 'completed';
|
||
break;
|
||
case 'completed':
|
||
newStatus = 'partial';
|
||
break;
|
||
case 'partial':
|
||
newStatus = 'none';
|
||
break;
|
||
default:
|
||
newStatus = 'none';
|
||
}
|
||
|
||
if (newStatus === 'none') {
|
||
delete this.markedDates[dateStr];
|
||
} else {
|
||
this.markedDates[dateStr] = newStatus;
|
||
}
|
||
|
||
this.saveData();
|
||
this.renderCalendar();
|
||
}
|
||
|
||
// 渲染日历
|
||
renderCalendar() {
|
||
const calendar = document.getElementById('calendar');
|
||
calendar.innerHTML = '';
|
||
|
||
// 星期标题
|
||
const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
|
||
weekdays.forEach(day => {
|
||
const weekdayEl = document.createElement('div');
|
||
weekdayEl.className = 'weekday';
|
||
weekdayEl.textContent = day;
|
||
calendar.appendChild(weekdayEl);
|
||
});
|
||
|
||
// 更新月份显示
|
||
const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月',
|
||
'七月', '八月', '九月', '十月', '十一月', '十二月'];
|
||
const monthDisplay = document.getElementById('currentMonth');
|
||
monthDisplay.textContent = `${this.currentDate.getFullYear()}年 ${monthNames[this.currentDate.getMonth()]}`;
|
||
|
||
// 获取月份信息
|
||
const firstDay = this.getFirstDayOfMonth(this.currentDate);
|
||
const daysInMonth = this.getDaysInMonth(this.currentDate);
|
||
const today = new Date();
|
||
|
||
// 添加上个月的日期(填充空白)
|
||
const prevMonth = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() - 1, 0);
|
||
const daysInPrevMonth = prevMonth.getDate();
|
||
|
||
for (let i = firstDay - 1; i >= 0; i--) {
|
||
const day = daysInPrevMonth - i;
|
||
const date = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() - 1, day);
|
||
this.createDayElement(calendar, date, day, true);
|
||
}
|
||
|
||
// 添加当前月的日期
|
||
for (let day = 1; day <= daysInMonth; day++) {
|
||
const date = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth(), day);
|
||
this.createDayElement(calendar, date, day, false);
|
||
}
|
||
|
||
// 添加下个月的日期(填充到完整周)
|
||
const totalCells = calendar.children.length - 7; // 减去星期标题
|
||
const remainingCells = 42 - totalCells; // 6行 x 7列 = 42
|
||
for (let day = 1; day <= remainingCells; day++) {
|
||
const date = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() + 1, day);
|
||
this.createDayElement(calendar, date, day, true);
|
||
}
|
||
}
|
||
|
||
// 创建日期元素
|
||
createDayElement(container, date, dayNumber, isOtherMonth) {
|
||
const dayEl = document.createElement('div');
|
||
dayEl.className = 'day';
|
||
|
||
if (isOtherMonth) {
|
||
dayEl.classList.add('other-month');
|
||
}
|
||
|
||
if (this.isToday(date)) {
|
||
dayEl.classList.add('today');
|
||
}
|
||
|
||
const status = this.getDateStatus(date);
|
||
if (status !== 'none') {
|
||
dayEl.classList.add(status);
|
||
}
|
||
|
||
const dayNumberEl = document.createElement('div');
|
||
dayNumberEl.className = 'day-number';
|
||
dayNumberEl.textContent = dayNumber;
|
||
dayEl.appendChild(dayNumberEl);
|
||
|
||
if (status !== 'none') {
|
||
const markEl = document.createElement('div');
|
||
markEl.className = 'day-mark';
|
||
markEl.textContent = status === 'completed' ? '✓' : '○';
|
||
dayEl.appendChild(markEl);
|
||
}
|
||
|
||
// 添加点击事件
|
||
dayEl.addEventListener('click', () => {
|
||
if (!isOtherMonth) {
|
||
this.toggleDateStatus(date);
|
||
}
|
||
});
|
||
|
||
container.appendChild(dayEl);
|
||
}
|
||
|
||
// 绑定事件
|
||
bindEvents() {
|
||
document.getElementById('prevMonth').addEventListener('click', () => {
|
||
this.currentDate.setMonth(this.currentDate.getMonth() - 1);
|
||
this.renderCalendar();
|
||
});
|
||
|
||
document.getElementById('nextMonth').addEventListener('click', () => {
|
||
this.currentDate.setMonth(this.currentDate.getMonth() + 1);
|
||
this.renderCalendar();
|
||
});
|
||
|
||
document.getElementById('todayBtn').addEventListener('click', () => {
|
||
this.currentDate = new Date();
|
||
this.renderCalendar();
|
||
});
|
||
}
|
||
|
||
// 保存数据到 localStorage
|
||
saveData() {
|
||
try {
|
||
localStorage.setItem('calendarMarkedDates', JSON.stringify(this.markedDates));
|
||
} catch (e) {
|
||
console.error('保存数据失败:', e);
|
||
}
|
||
}
|
||
|
||
// 从 localStorage 加载数据
|
||
loadData() {
|
||
try {
|
||
const data = localStorage.getItem('calendarMarkedDates');
|
||
return data ? JSON.parse(data) : {};
|
||
} catch (e) {
|
||
console.error('加载数据失败:', e);
|
||
return {};
|
||
}
|
||
}
|
||
}
|
||
|
||
// 初始化应用
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
new CalendarApp();
|
||
});
|
||
|