Files
timeline/www/app.js
2025-11-11 11:07:25 +08:00

212 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 日历状态管理
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();
});