Skip to content

About

學校行事曆管理系統 - 支援班級事務、學年事務、課務提醒的完整解決方案

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

school-calendar-system

學校行事曆管理系統 - 支援班級事務、學年事務、課務提醒的完整解決方案 // 載入預設事件 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>
<title>學校行事曆管理系統</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; }
    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>

About

學校行事曆管理系統 - 支援班級事務、學年事務、課務提醒的完整解決方案

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors