學校行事曆管理系統 - 支援班級事務、學年事務、課務提醒的完整解決方案 // 載入預設事件 function loadDefaultEvents() { if (confirm('這將會載入所有預設的學校事件,是否繼續?')) { events = [...defaultEvents]; saveEvents(); renderCalendar(); alert('預設事件已載入完成!包含:\n• 單車環台相關活動\n• G10主題課程\n• 學年主任會議\n• 健康檢查流程\n• G12模擬考與學測\n• 各項學校活動'); } }
// 清除所有資料
function clearAllData() {
if (confirm('確定要清除所有資料嗎?這個動作無法復原!')) {
localStorage.clear();
events = [];
todos = {
classAffairs: [],
academicYear: [],
g12: [],
g10: [],
g7: []
};
uploadedFiles = [];
renderCalendar();
renderAllTodos();
renderUploadedFiles();
alert('所有資料已清除!');
}
<!DOCTYPE html>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.header h1 {
text-align: center;
color: #4a5568;
margin-bottom: 20px;
font-size: 2em;
}
.nav-tabs {
display: flex;
justify-content: center;
gap: 10px;
flex-wrap: wrap;
}
.tab-btn {
padding: 12px 24px;
background: linear-gradient(145deg, #f7fafc, #edf2f7);
border: none;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.tab-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.tab-btn.active {
background: linear-gradient(145deg, #4299e1, #3182ce);
color: white;
}
.content {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.calendar-container {
display: grid;
gap: 20px;
}
.calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 10px;
}
.month-nav {
display: flex;
align-items: center;
gap: 15px;
}
.nav-btn {
background: linear-gradient(145deg, #4299e1, #3182ce);
color: white;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.nav-btn:hover {
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(66, 153, 225, 0.3);
}
.current-month {
font-size: 1.5em;
font-weight: bold;
color: #2d3748;
min-width: 200px;
text-align: center;
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
background: #e2e8f0;
border-radius: 10px;
overflow: hidden;
}
.day-header {
background: linear-gradient(145deg, #4a5568, #2d3748);
color: white;
padding: 15px 5px;
text-align: center;
font-weight: bold;
font-size: 0.9em;
}
.day-cell {
background: white;
min-height: 120px;
padding: 8px 4px;
position: relative;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid transparent;
}
.day-cell:hover {
background: #f7fafc;
border-color: #4299e1;
}
.day-cell.other-month {
background: #f8f9fa;
color: #a0aec0;
}
.day-cell.today {
background: linear-gradient(145deg, #bee3f8, #90cdf4);
border-color: #3182ce;
}
.day-number {
font-weight: bold;
margin-bottom: 5px;
text-align: center;
}
.event-item {
background: linear-gradient(145deg, #48bb78, #38a169);
color: white;
padding: 2px 5px;
border-radius: 4px;
font-size: 0.7em;
margin-bottom: 2px;
cursor: pointer;
transition: all 0.2s ease;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.event-item:hover {
transform: scale(1.05);
z-index: 10;
position: relative;
}
.event-item.class-affairs {
background: linear-gradient(145deg, #4299e1, #3182ce);
}
.event-item.academic-year {
background: linear-gradient(145deg, #ed8936, #dd6b20);
}
.event-item.course-g12 {
background: linear-gradient(145deg, #e53e3e, #c53030);
}
.event-item.course-g10 {
background: linear-gradient(145deg, #9f7aea, #805ad5);
}
.event-item.course-g7 {
background: linear-gradient(145deg, #38b2ac, #319795);
}
.admin-panel {
display: none;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.admin-panel.active {
display: grid;
}
.form-section {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
border: 1px solid #e2e8f0;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #4a5568;
}
.form-control {
width: 100%;
padding: 10px;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: #4299e1;
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1);
}
.btn {
background: linear-gradient(145deg, #4299e1, #3182ce);
color: white;
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
margin: 5px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(66, 153, 225, 0.3);
}
.btn-success {
background: linear-gradient(145deg, #48bb78, #38a169);
}
.btn-danger {
background: linear-gradient(145deg, #e53e3e, #c53030);
}
.file-upload {
border: 2px dashed #cbd5e0;
border-radius: 10px;
padding: 30px;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
}
.file-upload:hover {
border-color: #4299e1;
background: #f7fafc;
}
.file-upload.dragover {
border-color: #48bb78;
background: #f0fff4;
}
.todo-list {
max-height: 300px;
overflow-y: auto;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 10px;
}
.todo-item {
display: flex;
align-items: center;
padding: 8px;
margin-bottom: 5px;
background: white;
border-radius: 6px;
border: 1px solid #e2e8f0;
transition: all 0.2s ease;
}
.todo-item:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.todo-item.completed {
opacity: 0.6;
text-decoration: line-through;
}
.todo-checkbox {
margin-right: 10px;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.modal.active {
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: white;
border-radius: 15px;
padding: 30px;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
position: relative;
animation: modalSlideIn 0.3s ease;
}
@keyframes modalSlideIn {
from {
transform: translateY(-50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.close-btn {
position: absolute;
top: 15px;
right: 20px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #a0aec0;
}
.close-btn:hover {
color: #e53e3e;
}
.legend {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 20px;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
font-size: 0.9em;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 4px;
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
.calendar-header {
flex-direction: column;
text-align: center;
}
.current-month {
font-size: 1.2em;
}
.day-cell {
min-height: 80px;
padding: 4px 2px;
}
.admin-panel {
grid-template-columns: 1fr;
}
.nav-tabs {
justify-content: flex-start;
overflow-x: auto;
padding-bottom: 10px;
}
.tab-btn {
white-space: nowrap;
flex-shrink: 0;
}
}
</style>
<div class="content">
<!-- 日曆視圖 -->
<div id="calendar-tab" class="tab-content">
<div class="legend">
<div class="legend-item">
<div class="legend-color" style="background: linear-gradient(145deg, #4299e1, #3182ce);"></div>
<span>班級事務</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: linear-gradient(145deg, #ed8936, #dd6b20);"></div>
<span>學年事務</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: linear-gradient(145deg, #e53e3e, #c53030);"></div>
<span>G12課務</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: linear-gradient(145deg, #9f7aea, #805ad5);"></div>
<span>G10課務</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: linear-gradient(145deg, #38b2ac, #319795);"></div>
<span>G7課務</span>
</div>
</div>
<div class="calendar-header">
<div class="month-nav">
<button class="nav-btn" onclick="previousMonth()">‹</button>
<div class="current-month" id="currentMonth"></div>
<button class="nav-btn" onclick="nextMonth()">›</button>
</div>
<button class="btn" onclick="showModal('eventModal')">➕ 新增事件</button>
</div>
<div class="calendar-grid" id="calendarGrid"></div>
</div>
<!-- 班級事務 -->
<div id="class-affairs-tab" class="tab-content" style="display: none;">
<h2>📚 班級事務管理</h2>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px;">
<div class="form-section">
<h3>待辦事項</h3>
<div class="todo-list" id="classAffairsTodo"></div>
<div style="margin-top: 15px;">
<input type="text" id="newClassTodo" class="form-control" placeholder="新增待辦事項...">
<button class="btn" onclick="addTodo('classAffairs')">新增</button>
</div>
</div>
<div class="form-section">
<h3>已完成事項</h3>
<div class="todo-list" id="classAffairsCompleted"></div>
</div>
</div>
</div>
<!-- 學年事務 -->
<div id="academic-year-tab" class="tab-content" style="display: none;">
<h2>🎓 學年事務提醒</h2>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px;">
<div class="form-section">
<h3>待辦事項</h3>
<div class="todo-list" id="academicYearTodo"></div>
<div style="margin-top: 15px;">
<input type="text" id="newAcademicTodo" class="form-control" placeholder="新增學年事務...">
<button class="btn" onclick="addTodo('academicYear')">新增</button>
</div>
</div>
<div class="form-section">
<h3>已完成事項</h3>
<div class="todo-list" id="academicYearCompleted"></div>
</div>
</div>
</div>
<!-- 課務管理 -->
<div id="courses-tab" class="tab-content" style="display: none;">
<h2>📖 課務管理</h2>
<div class="nav-tabs" style="margin: 20px 0;">
<button class="tab-btn active" onclick="showCourseTab('g12')">G12</button>
<button class="tab-btn" onclick="showCourseTab('g10')">G10</button>
<button class="tab-btn" onclick="showCourseTab('g7')">G7</button>
</div>
<div id="g12-course" class="course-content">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-section">
<h3>G12 待辦事項</h3>
<div class="todo-list" id="g12Todo"></div>
<div style="margin-top: 15px;">
<input type="text" id="newG12Todo" class="form-control" placeholder="新增G12課務...">
<button class="btn" onclick="addTodo('g12')">新增</button>
</div>
</div>
<div class="form-section">
<h3>G12 已完成事項</h3>
<div class="todo-list" id="g12Completed"></div>
</div>
</div>
</div>
<div id="g10-course" class="course-content" style="display: none;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-section">
<h3>G10 待辦事項</h3>
<div class="todo-list" id="g10Todo"></div>
<div style="margin-top: 15px;">
<input type="text" id="newG10Todo" class="form-control" placeholder="新增G10課務...">
<button class="btn" onclick="addTodo('g10')">新增</button>
</div>
</div>
<div class="form-section">
<h3>G10 已完成事項</h3>
<div class="todo-list" id="g10Completed"></div>
</div>
</div>
</div>
<div id="g7-course" class="course-content" style="display: none;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-section">
<h3>G7 待辦事項</h3>
<div class="todo-list" id="g7Todo"></div>
<div style="margin-top: 15px;">
<input type="text" id="newG7Todo" class="form-control" placeholder="新增G7課務...">
<button class="btn" onclick="addTodo('g7')">新增</button>
</div>
</div>
<div class="form-section">
<h3>G7 已完成事項</h3>
<div class="todo-list" id="g7Completed"></div>
</div>
</div>
</div>
</div>
<!-- 後台管理 -->
<div id="admin-tab" class="tab-content" style="display: none;">
<h2>⚙️ 後台管理</h2>
<div class="admin-panel active">
<div class="form-section">
<h3>檔案上傳</h3>
<div class="file-upload" id="fileUpload">
<p>📁 將檔案拖放到此處或點擊選擇檔案</p>
<p style="font-size: 0.9em; color: #666; margin-top: 10px;">
支援格式:Word (.docx), Excel (.xlsx), PDF, 圖片檔, 文字檔
</p>
<input type="file" id="fileInput" style="display: none;" multiple
accept=".docx,.doc,.xlsx,.xls,.pdf,.txt,.jpg,.jpeg,.png,.gif">
</div>
<div id="uploadedFiles" style="margin-top: 15px;"></div>
</div>
<div class="form-section">
<h3>快速新增事件</h3>
<div class="form-group">
<label>事件標題</label>
<input type="text" id="eventTitle" class="form-control" placeholder="輸入事件標題">
</div>
<div class="form-group">
<label>事件類型</label>
<select id="eventType" class="form-control">
<option value="class-affairs">班級事務</option>
<option value="academic-year">學年事務</option>
<option value="course-g12">G12課務</option>
<option value="course-g10">G10課務</option>
<option value="course-g7">G7課務</option>
</select>
</div>
<div class="form-group">
<label>日期</label>
<input type="date" id="eventDate" class="form-control">
</div>
<div class="form-group">
<label>描述</label>
<textarea id="eventDescription" class="form-control" rows="3" placeholder="事件描述..."></textarea>
</div>
<button class="btn" onclick="addEvent()">新增事件</button>
<button class="btn btn-danger" onclick="clearAllData()">清除所有資料</button>
</div>
</div>
</div>
</div>
</div>
<!-- 事件詳情模態框 -->
<div id="eventModal" class="modal">
<div class="modal-content">
<button class="close-btn" onclick="hideModal('eventModal')">×</button>
<h2>新增事件</h2>
<div class="form-group">
<label>事件標題</label>
<input type="text" id="modalEventTitle" class="form-control">
</div>
<div class="form-group">
<label>事件類型</label>
<select id="modalEventType" class="form-control">
<option value="class-affairs">班級事務</option>
<option value="academic-year">學年事務</option>
<option value="course-g12">G12課務</option>
<option value="course-g10">G10課務</option>
<option value="course-g7">G7課務</option>
</select>
</div>
<div class="form-group">
<label>日期</label>
<input type="date" id="modalEventDate" class="form-control">
</div>
<div class="form-group">
<label>描述</label>
<textarea id="modalEventDescription" class="form-control" rows="3"></textarea>
</div>
<div style="text-align: center; margin-top: 20px;">
<button class="btn" onclick="saveEvent()">儲存事件</button>
<button class="btn" style="background: #718096;" onclick="hideModal('eventModal')">取消</button>
</div>
</div>
</div>
<script>
// 全域變數
let currentDate = new Date();
let events = JSON.parse(localStorage.getItem('calendarEvents') || '[]');
let todos = JSON.parse(localStorage.getItem('calendarTodos') || '{}');
let uploadedFiles = JSON.parse(localStorage.getItem('uploadedFiles') || '[]');
// 初始化待辦事項結構
if (!todos.classAffairs) todos.classAffairs = [];
if (!todos.academicYear) todos.academicYear = [];
if (!todos.g12) todos.g12 = [];
if (!todos.g10) todos.g10 = [];
if (!todos.g7) todos.g7 = [];
// 月份名稱
const monthNames = [
'一月', '二月', '三月', '四月', '五月', '六月',
'七月', '八月', '九月', '十月', '十一月', '十二月'
];
const dayNames = ['週日', '週一', '週二', '週三', '週四', '週五', '週六'];
// 初始化應用
function init() {
renderCalendar();
renderAllTodos();
setupFileUpload();
renderUploadedFiles();
}
// 切換分頁
function showTab(tabName) {
// 隱藏所有分頁內容
document.querySelectorAll('.tab-content').forEach(tab => {
tab.style.display = 'none';
});
// 移除所有按鈕的active類別
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
});
// 顯示選中的分頁
document.getElementById(tabName + '-tab').style.display = 'block';
// 添加active類別到對應按鈕
event.target.classList.add('active');
}
// 切換課務分頁
function showCourseTab(course) {
document.querySelectorAll('.course-content').forEach(content => {
content.style.display = 'none';
});
document.querySelectorAll('#courses-tab .tab-btn').forEach(btn => {
btn.classList.remove('active');
});
document.getElementById(course + '-course').style.display = 'block';
event.target.classList.add('active');
}
// 渲染日曆
function renderCalendar() {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
document.getElementById('currentMonth').textContent = `${year}年 ${monthNames[month]}`;
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const startDate = new Date(firstDay);
startDate.setDate(startDate.getDate() - firstDay.getDay());
const calendarGrid = document.getElementById('calendarGrid');
calendarGrid.innerHTML = '';
// 添加星期標題
dayNames.forEach(day => {
const dayHeader = document.createElement('div');
dayHeader.className = 'day-header';
dayHeader.textContent = day;
calendarGrid.appendChild(dayHeader);
});
// 生成日曆格子
for (let i = 0; i < 42; i++) {
const cellDate = new Date(startDate);
cellDate.setDate(startDate.getDate() + i);
const dayCell = document.createElement('div');
dayCell.className = 'day-cell';
if (cellDate.getMonth() !== month) {
dayCell.classList.add('other-month');
}
if (isToday(cellDate)) {
dayCell.classList.add('today');
}
const dayNumber = document.createElement('div');
dayNumber.className = 'day-number';
dayNumber.textContent = cellDate.getDate();
dayCell.appendChild(dayNumber);
// 添加事件
const dayEvents = getEventsForDate(cellDate);
dayEvents.forEach(event => {
const eventElement = document.createElement('div');
eventElement.className = `event-item ${event.type}`;
eventElement.textContent = event.title;
eventElement.title = event.description || event.title;
eventElement.onclick = (e) => {
e.stopPropagation();
showEventDetails(event);
};
dayCell.appendChild(eventElement);
});
dayCell.onclick = () => selectDate(cellDate);
calendarGrid.appendChild(dayCell);
}
}
// 檢查是否為今天
function isToday(date) {
const today = new Date();
return date.toDateString() === today.toDateString();
}
// 獲取指定日期的事件
function getEventsForDate(date) {
return events.filter(event => {
const eventDate = new Date(event.date);
return eventDate.toDateString() === date.toDateString();
});
}
// 選擇日期
function selectDate(date) {
const dateString = date.toISOString().split('T')[0];
document.getElementById('modalEventDate').value = dateString;
showModal('eventModal');
}
// 上一個月
function previousMonth() {
currentDate.setMonth(currentDate.getMonth() - 1);
renderCalendar();
}
// 下一個月
function nextMonth() {
currentDate.setMonth(currentDate.getMonth() + 1);
renderCalendar();
}
// 顯示模態框
function showModal(modalId) {
document.getElementById(modalId).classList.add('active');
}
// 隱藏模態框
function hideModal(modalId) {
document.getElementById(modalId).classList.remove('active');
}
// 新增事件
function addEvent() {
const title = document.getElementById('eventTitle').value;
const type = document.getElementById('eventType').value;
const date = document.getElementById('eventDate').value;
const description = document.getElementById('eventDescription').value;
if (!title || !date) {
alert('請填寫事件標題和日期!');
return;
}
const event = {
id: Date.now().toString(),
title,
type,
date,
description
};
events.push(event);
saveEvents();
renderCalendar();
// 清空表單
document.getElementById('eventTitle').value = '';
document.getElementById('eventDate').value = '';
document.getElementById('eventDescription').value = '';
alert('事件新增成功!');
}
// 從模態框儲存事件
function saveEvent() {
const title = document.getElementById('modalEventTitle').value;
const type = document.getElementById('modalEventType').value;
const date = document.getElementById('modalEventDate').value;
const description = document.getElementById('modalEventDescription').value;
if (!title || !date) {
alert('請填寫事件標題和日期!');
return;
}
const event = {
id: Date.now().toString(),
title,
type,
date,
description
};
events.push(event);
saveEvents();
renderCalendar();
hideModal('eventModal');
// 清空表單
document.getElementById('modalEventTitle').value = '';
document.getElementById('modalEventType').value = '';
document.getElementById('modalEventDate').value = '';
document.getElementById('modalEventDescription').value = '';
alert('事件新增成功!');
}
// 顯示事件詳情
function showEventDetails(event) {
const details = `📅 事件詳情
━━━━━━━━━━━━━━━ 📌 標題:${event.title} 🏷️ 類型:${getTypeDisplayName(event.type)} 📅 日期:${event.date}${event.endDate ? ' 至 ' + event.endDate : ''} ⏰ 時間:${event.time || '全天'} 📝 描述:${event.description || '無描述'}`;
alert(details);
}
// 獲取類型顯示名稱
function getTypeDisplayName(type) {
const typeMap = {
'class-affairs': '班級事務',
'academic-year': '學年事務',
'course-g12': 'G12課務',
'course-g10': 'G10課務',
'course-g7': 'G7課務'
};
return typeMap[type] || type;
}
// 儲存事件
function saveEvents() {
localStorage.setItem('calendarEvents', JSON.stringify(events));
}
// 新增待辦事項
function addTodo(category) {
const inputId = `new${category.charAt(0).toUpperCase() + category.slice(1).replace(/([A-Z])/g, '$1')}Todo`;
const input = document.getElementById(inputId);
const text = input.value.trim();
if (!text) {
alert('請輸入待辦事項!');
return;
}
if (!todos[category]) {
todos[category] = [];
}
const todo = {
id: Date.now().toString(),
text,
completed: false,
createdAt: new Date().toISOString()
};
todos[category].push(todo);
saveTodos();
renderTodos(category);
input.value = '';
}
// 切換待辦事項狀態
function toggleTodo(category, todoId) {
const todo = todos[category].find(t => t.id === todoId);
if (todo) {
todo.completed = !todo.completed;
saveTodos();
renderTodos(category);
}
}
// 刪除待辦事項
function deleteTodo(category, todoId) {
if (confirm('確定要刪除這個待辦事項嗎?')) {
todos[category] = todos[category].filter(t => t.id !== todoId);
saveTodos();
renderTodos(category);
}
}
// 渲染待辦事項
function renderTodos(category) {
const todoContainer = document.getElementById(category + 'Todo');
const completedContainer = document.getElementById(category + 'Completed');
if (!todoContainer || !completedContainer) return;
const categoryTodos = todos[category] || [];
// 渲染待辦事項
const pendingTodos = categoryTodos.filter(todo => !todo.completed);
todoContainer.innerHTML = pendingTodos.map(todo => `
<div class="todo-item">
<input type="checkbox" class="todo-checkbox"
onchange="toggleTodo('${category}', '${todo.id}')">
<span>${todo.text}</span>
<button class="btn btn-danger" style="margin-left: auto; padding: 5px 10px; font-size: 12px;"
onclick="deleteTodo('${category}', '${todo.id}')">刪除</button>
</div>
`).join('');
// 渲染已完成事項
const completedTodos = categoryTodos.filter(todo => todo.completed);
completedContainer.innerHTML = completedTodos.map(todo => `
<div class="todo-item completed">
<input type="checkbox" class="todo-checkbox" checked
onchange="toggleTodo('${category}', '${todo.id}')">
<span>${todo.text}</span>
<button class="btn btn-danger" style="margin-left: auto; padding: 5px 10px; font-size: 12px;"
onclick="deleteTodo('${category}', '${todo.id}')">刪除</button>
</div>
`).join('');
}
// 渲染所有待辦事項
function renderAllTodos() {
renderTodos('classAffairs');
renderTodos('academicYear');
renderTodos('g12');
renderTodos('g10');
renderTodos('g7');
}
// 儲存待辦事項
function saveTodos() {
localStorage.setItem('calendarTodos', JSON.stringify(todos));
}
// 設置檔案上傳
function setupFileUpload() {
const fileUpload = document.getElementById('fileUpload');
const fileInput = document.getElementById('fileInput');
fileUpload.addEventListener('click', () => {
fileInput.click();
});
fileUpload.addEventListener('dragover', (e) => {
e.preventDefault();
fileUpload.classList.add('dragover');
});
fileUpload.addEventListener('dragleave', () => {
fileUpload.classList.remove('dragover');
});
fileUpload.addEventListener('drop', (e) => {
e.preventDefault();
fileUpload.classList.remove('dragover');
handleFiles(e.dataTransfer.files);
});
fileInput.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
}
// 處理檔案上傳
function handleFiles(files) {
Array.from(files).forEach(file => {
const fileInfo = {
id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
name: file.name,
type: file.type,
size: file.size,
uploadedAt: new Date().toISOString()
};
uploadedFiles.push(fileInfo);
// 這裡可以加入實際的檔案上傳邏輯
// 由於瀏覽器環境限制,我們只儲存檔案資訊
console.log('檔案上傳:', fileInfo);
});
saveUploadedFiles();
renderUploadedFiles();
alert(`成功上傳 ${files.length} 個檔案!`);
}
// 渲染已上傳檔案
function renderUploadedFiles() {
const container = document.getElementById('uploadedFiles');
if (uploadedFiles.length === 0) {
container.innerHTML = '<p style="color: #666;">尚未上傳任何檔案</p>';
return;
}
container.innerHTML = `
<h4>已上傳檔案 (${uploadedFiles.length})</h4>
${uploadedFiles.map(file => `
<div class="todo-item" style="justify-content: space-between;">
<div>
<strong>${file.name}</strong>
<br>
<small style="color: #666;">
${formatFileSize(file.size)} • ${new Date(file.uploadedAt).toLocaleString()}
</small>
</div>
<button class="btn btn-danger" style="padding: 5px 10px; font-size: 12px;"
onclick="deleteUploadedFile('${file.id}')">刪除</button>
</div>
`).join('')}
`;
}
// 格式化檔案大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 刪除已上傳檔案
function deleteUploadedFile(fileId) {
if (confirm('確定要刪除這個檔案嗎?')) {
uploadedFiles = uploadedFiles.filter(file => file.id !== fileId);
saveUploadedFiles();
renderUploadedFiles();
}
}
// 儲存已上傳檔案資訊
function saveUploadedFiles() {
localStorage.setItem('uploadedFiles', JSON.stringify(uploadedFiles));
}
// 清除所有資料
function clearAllData() {
if (confirm('確定要清除所有資料嗎?這個動作無法復原!')) {
localStorage.clear();
events = [];
todos = {
classAffairs: [],
academicYear: [],
g12: [],
g10: [],
g7: []
};
uploadedFiles = [];
renderCalendar();
renderAllTodos();
renderUploadedFiles();
alert('所有資料已清除!');
}
}
// 頁面載入時初始化
window.addEventListener('load', init);
// 點擊模態框背景關閉
window.addEventListener('click', (e) => {
if (e.target.classList.contains('modal')) {
e.target.classList.remove('active');
}
});
</script>