/
fedotovskij.v
/
Diplom_another
Обзор
Документация
Войти
/
fedotovskij.v
/
Diplom_another
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
script.js
867 строк
27 KB
root
some commit
22 май 2026, 16:25
22 май 2026, 16:25
d16c143
Код
Авторство
О чём код?
// Глобальные переменные let currentClass = ''; let currentWeekOffset = 0; let isAdminMode = false; let editingLesson = null; function createEmptySchedule() { return { monday: [], tuesday: [], wednesday: [], thursday: [], friday: [], saturday: [] }; } // API базовый URL const API_BASE = '/api'; function getAdminToken() { return localStorage.getItem('adminToken'); } function getAuthHeaders() { const token = getAdminToken(); if (!token) { return {}; } return { Authorization: `Bearer ${token}` }; } // Данные о расписании (будут загружены из API) let scheduleData = {}; function formatDateLocal(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}`; } function getCurrentWeekStartDate() { const today = new Date(); today.setHours(0, 0, 0, 0); const currentDay = today.getDay(); const mondayOffset = currentDay === 0 ? -6 : 1 - currentDay; today.setDate(today.getDate() + mondayOffset + currentWeekOffset * 7); return formatDateLocal(today); } function getScheduleKey(className = currentClass) { return `${className}_${getCurrentWeekStartDate()}`; } // Инициализация приложения document.addEventListener('DOMContentLoaded', function() { setupEventListeners(); restoreTeacherMode(); loadClasses(); }); // Загрузка классов с сервера async function loadClasses() { try { const response = await fetch(`${API_BASE}/classes`); if (!response.ok) throw new Error('Ошибка загрузки классов'); const classes = await response.json(); initializeClassButtons(classes.map(c => c.name)); } catch (error) { console.error('Ошибка:', error); // Если сервер недоступен, используем localStorage loadScheduleFromStorage(); initializeClassButtons(Object.keys(scheduleData)); } } // Загрузка расписания класса с сервера async function loadClassSchedule(className) { try { const weekStart = getCurrentWeekStartDate(); const response = await fetch( `${API_BASE}/schedule/${encodeURIComponent(className)}?weekStart=${encodeURIComponent(weekStart)}` ); if (!response.ok) { throw new Error('Ошибка загрузки расписания'); } const schedule = await response.json(); scheduleData[getScheduleKey(className)] = schedule || createEmptySchedule(); return scheduleData[getScheduleKey(className)]; } catch (error) { console.error('Ошибка:', error); return scheduleData[getScheduleKey(className)] || createEmptySchedule(); } } // Создание кнопок классов function initializeClassButtons(classNames) { const classButtons = document.getElementById('classButtons'); classButtons.innerHTML = ''; // Очищаем перед заполнением classNames.forEach(className => { const button = document.createElement('button'); button.className = 'class-btn'; button.textContent = className; button.onclick = () => selectClass(className); classButtons.appendChild(button); }); } // Загрузка расписания из localStorage (резервный вариант) function loadScheduleFromStorage() { const savedSchedule = localStorage.getItem('schoolSchedule'); if (savedSchedule) { try { const parsed = JSON.parse(savedSchedule); Object.assign(scheduleData, parsed); } catch (e) { console.error('Ошибка загрузки расписания:', e); } } } // Сохранение расписания в localStorage (резервный вариант) function saveScheduleToStorage() { localStorage.setItem('schoolSchedule', JSON.stringify(scheduleData)); } async function deleteCurrentClass() { if (!isAdminMode) { alert('Удалять класс может только преподаватель'); return; } if (!currentClass) { alert('Класс не выбран'); return; } const confirmed = confirm( `Вы уверены, что хотите удалить класс "${currentClass}"?\n\n` + 'Будет удалено всё расписание этого класса.' ); if (!confirmed) { return; } try { const response = await fetch(`${API_BASE}/classes/${encodeURIComponent(currentClass)}`, { method: 'DELETE', headers: getAuthHeaders() }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Ошибка удаления класса'); } delete scheduleData[getScheduleKey()]; alert(data.message || `Класс "${currentClass}" удалён`); currentClass = ''; currentWeekOffset = 0; document.querySelector('.class-selector').style.display = 'block'; document.getElementById('scheduleContainer').style.display = 'none'; applyTeacherMode(false); await loadClasses(); } catch (error) { console.error('Ошибка удаления класса:', error); alert(error.message || 'Ошибка удаления класса. Попробуйте ещё раз.'); } } // Установка обработчиков событий function setupEventListeners() { const bindClick = (id, handler) => { const element = document.getElementById(id); if (!element) { console.warn(`Элемент #${id} не найден`); return; } element.addEventListener('click', handler); }; const bindSubmit = (id, handler) => { const element = document.getElementById(id); if (!element) { console.warn(`Форма #${id} не найдена`); return; } element.addEventListener('submit', handler); }; bindClick('prevWeek', () => changeWeek(-1)); bindClick('nextWeek', () => changeWeek(1)); bindClick('backToClasses', backToClassSelection); bindClick('adminLogin', adminLogin); bindClick('adminLogout', adminLogout); bindClick('addLesson', openAddLessonModal); bindClick('saveSchedule', saveSchedule); bindClick('deleteClass', deleteCurrentClass); // Кнопка создания класса bindClick('createClassBtn', openCreateClassModal); // Модальное окно редактирования урока const closeBtn = document.querySelector('.close'); if (closeBtn) { closeBtn.addEventListener('click', closeModal); } bindClick('cancelEdit', closeModal); bindSubmit('lessonForm', saveLesson); bindClick('deleteLesson', deleteLesson); // Модальное окно создания класса const closeCreateClassBtn = document.querySelector('.close-create-class'); if (closeCreateClassBtn) { closeCreateClassBtn.addEventListener('click', closeCreateClassModal); } bindClick('cancelCreateClass', closeCreateClassModal); bindClick('submitCreateClass', createClass); bindSubmit('createClassForm', createClass); // Закрытие модальных окон при клике вне них window.addEventListener('click', function(event) { const modal = document.getElementById('editModal'); const createClassModal = document.getElementById('createClassModal'); if (modal && event.target === modal) { closeModal(); } if (createClassModal && event.target === createClassModal) { closeCreateClassModal(); } }); } function restoreTeacherMode() { isAdminMode = localStorage.getItem('teacherMode') === 'true' && !!getAdminToken(); applyTeacherMode(false); } function applyTeacherMode(needRender = true) { const adminLoginBtn = document.getElementById('adminLogin'); const adminLogoutBtn = document.getElementById('adminLogout'); const adminActions = document.getElementById('adminActions'); const createClassContainer = document.getElementById('createClassContainer'); const deleteClassContainer = document.getElementById('deleteClassContainer'); if (adminLoginBtn) { adminLoginBtn.style.display = isAdminMode ? 'none' : 'inline-block'; } if (adminLogoutBtn) { adminLogoutBtn.style.display = isAdminMode ? 'inline-block' : 'none'; } if (adminActions) { adminActions.style.display = isAdminMode ? 'flex' : 'none'; } if (createClassContainer) { createClassContainer.style.display = isAdminMode ? 'block' : 'none'; } if (deleteClassContainer) { deleteClassContainer.style.display = isAdminMode && currentClass ? 'flex' : 'none'; } if (needRender && currentClass) { renderSchedule(); } } // Вход в режим администратора async function adminLogin() { const password = prompt('Введите пароль преподавателя:'); if (password === null) { return; } try { const response = await fetch(`${API_BASE}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Ошибка входа'); } localStorage.setItem('adminToken', data.token); localStorage.setItem('teacherMode', 'true'); isAdminMode = true; applyTeacherMode(true); } catch (error) { alert(error.message || 'Ошибка входа'); } } // Выход из режима администратора async function adminLogout() { try { await fetch(`${API_BASE}/auth/logout`, { method: 'POST', headers: getAuthHeaders() }); } catch (error) { console.error('Ошибка выхода:', error); } localStorage.removeItem('adminToken'); localStorage.removeItem('teacherMode'); isAdminMode = false; applyTeacherMode(true); } // Открытие модального окна для добавления урока function openAddLessonModal(day = null, lessonIndex = null) { editingLesson = null; document.getElementById('modalTitle').textContent = 'Добавление урока'; document.getElementById('lessonForm').reset(); if (day) { document.getElementById('lessonDay').value = day; } if (lessonIndex !== null && lessonIndex !== undefined) { document.getElementById('lessonIndex').value = lessonIndex + 1; } document.getElementById('deleteLesson').style.display = 'none'; document.getElementById('editModal').style.display = 'block'; } // Открытие модального окна для редактирования урока function openEditLessonModal(day, index) { const scheduleKey = getScheduleKey(); const classSchedule = scheduleData[scheduleKey] || createEmptySchedule(); const lesson = classSchedule[day] ? classSchedule[day][index] : null; if (!lesson) { return; } editingLesson = { day, index }; document.getElementById('modalTitle').textContent = 'Редактирование урока'; document.getElementById('lessonTime').value = lesson.time; document.getElementById('lessonSubject').value = lesson.subject; document.getElementById('lessonTeacher').value = lesson.teacher; document.getElementById('lessonDay').value = day; document.getElementById('lessonIndex').value = index + 1; document.getElementById('deleteLesson').style.display = 'inline-block'; document.getElementById('editModal').style.display = 'block'; } // Закрытие модального окна function closeModal() { document.getElementById('editModal').style.display = 'none'; editingLesson = null; } // Открытие модального окна для создания класса function openCreateClassModal() { console.log('Нажата кнопка создания класса'); const form = document.getElementById('createClassForm'); const modal = document.getElementById('createClassModal'); if (!modal) { console.error('Не найден элемент #createClassModal'); return; } if (form) { form.reset(); } modal.style.display = 'block'; } // Закрытие модального окна создания класса function closeCreateClassModal() { document.getElementById('createClassModal').style.display = 'none'; } // Создание класса async function createClass(e) { if (e) { e.preventDefault(); } const input = document.getElementById('newClassName'); const submitBtn = document.getElementById('submitCreateClass'); const className = input ? input.value.trim() : ''; if (!className) { alert('Введите название класса'); return; } try { if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Создание...'; } console.log('Создание класса:', className); const response = await fetch(`${API_BASE}/classes`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify({ name: className }) }); const responseText = await response.text(); let responseData = {}; try { responseData = responseText ? JSON.parse(responseText) : {}; } catch (parseError) { console.error('Сервер вернул не JSON:', responseText); throw new Error('Сервер вернул некорректный ответ'); } if (!response.ok) { throw new Error(responseData.error || 'Ошибка создания класса'); } closeCreateClassModal(); await loadClasses(); alert(`Класс "${className}" успешно создан!`); } catch (error) { console.error('Ошибка создания класса:', error); alert(error.message || 'Ошибка создания класса. Попробуйте еще раз.'); } finally { if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Создать'; } } } // Сохранение урока // Сохранение урока async function saveLesson(e) { e.preventDefault(); const time = document.getElementById('lessonTime').value; const subject = document.getElementById('lessonSubject').value; const teacher = document.getElementById('lessonTeacher').value; const day = document.getElementById('lessonDay').value; const lessonIndexInput = parseInt(document.getElementById('lessonIndex').value, 10); if (Number.isNaN(lessonIndexInput) || lessonIndexInput < 1) { alert('Номер урока должен быть не меньше 1'); return; } // В интерфейсе пользователь видит 1, 2, 3... // В массиве расписания используем индексы 0, 1, 2... const index = lessonIndexInput - 1; const lessonData = { time, subject, teacher }; try { const weekStart = getCurrentWeekStartDate(); const response = await fetch(`${API_BASE}/schedule/${encodeURIComponent(currentClass)}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify({ weekStart, day, index, ...lessonData }) }); if (!response.ok) { throw new Error('Ошибка сохранения урока'); } const scheduleKey = getScheduleKey(); if (!scheduleData[scheduleKey]) { scheduleData[scheduleKey] = createEmptySchedule(); } if (!Array.isArray(scheduleData[scheduleKey][day])) { scheduleData[scheduleKey][day] = []; } while (scheduleData[scheduleKey][day].length <= index) { scheduleData[scheduleKey][day].push(null); } scheduleData[scheduleKey][day][index] = lessonData; closeModal(); renderSchedule(); } catch (error) { console.error('Ошибка:', error); alert('Ошибка сохранения урока. Попробуйте еще раз.'); } } // Удаление урока async function deleteLesson() { if (!editingLesson || !confirm('Вы уверены, что хотите удалить этот урок?')) { return; } const { day, index } = editingLesson; try { // Удаление с сервера const weekStart = getCurrentWeekStartDate(); const response = await fetch( `${API_BASE}/schedule/${encodeURIComponent(currentClass)}/${day}/${index}?weekStart=${encodeURIComponent(weekStart)}`, { method: 'DELETE', headers: getAuthHeaders() } ); if (!response.ok) throw new Error('Ошибка удаления урока'); // Обновление локальных данных const scheduleKey = getScheduleKey(); if ( scheduleData[scheduleKey] && scheduleData[scheduleKey][day] && scheduleData[scheduleKey][day][index] ) { scheduleData[scheduleKey][day][index] = null; const daySchedule = scheduleData[scheduleKey][day]; while (daySchedule.length > 0 && daySchedule[daySchedule.length - 1] === null) { daySchedule.pop(); } } closeModal(); renderSchedule(); } catch (error) { console.error('Ошибка:', error); alert('Ошибка удаления урока. Попробуйте еще раз.'); } } // Сохранение всего расписания async function saveSchedule() { try { // Здесь можно добавить сохранение всего расписания на сервер // Сейчас используем localStorage как резервный вариант saveScheduleToStorage(); alert('Расписание успешно сохранено!'); } catch (error) { console.error('Ошибка:', error); alert('Ошибка сохранения расписания. Попробуйте еще раз.'); } } // Выбор класса async function selectClass(className) { currentClass = className; document.querySelectorAll('.class-btn').forEach(btn => { btn.classList.remove('active'); if (btn.textContent === className) { btn.classList.add('active'); } }); await loadClassSchedule(className); showSchedule(); } // Показ расписания function showSchedule() { document.querySelector('.class-selector').style.display = 'none'; document.getElementById('scheduleContainer').style.display = 'block'; document.getElementById('classTitle').textContent = `Расписание класса ${currentClass}`; updateWeekDisplay(); renderSchedule(); applyTeacherMode(false); } // Обновление отображения недели function updateScheduleDateHeaders() { const dayNames = [ 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота' ]; const headerCells = document.querySelectorAll('#scheduleTable thead th'); if (!headerCells || headerCells.length < 7) { return; } const today = new Date(); const currentDay = today.getDay(); // 0 — воскресенье, 1 — понедельник const mondayOffset = currentDay === 0 ? -6 : 1 - currentDay; const monday = new Date(today); monday.setDate(today.getDate() + mondayOffset + currentWeekOffset * 7); const formatter = new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long' }); dayNames.forEach((dayName, index) => { const date = new Date(monday); date.setDate(monday.getDate() + index); headerCells[index + 1].innerHTML = ` <div>${dayName}</div> <div class="day-date">${formatter.format(date)}</div> `; }); } function updateWeekDisplay() { const weekText = currentWeekOffset === 0 ? 'Текущая неделя' : currentWeekOffset > 0 ? `${currentWeekOffset} неделя вперед` : `${Math.abs(currentWeekOffset)} неделя назад`; document.getElementById('currentWeek').textContent = weekText; updateScheduleDateHeaders(); } // Изменение недели async function changeWeek(direction) { currentWeekOffset += direction; updateWeekDisplay(); if (currentClass) { await loadClassSchedule(currentClass); } renderSchedule(); applyTeacherMode(false); } // Отрисовка расписания function renderSchedule(classSchedule = null) { const scheduleBody = document.getElementById('scheduleBody'); if (!scheduleBody) return; scheduleBody.innerHTML = ''; const days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; const dayNames = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота']; const scheduleKey = getScheduleKey(); classSchedule = classSchedule || scheduleData[scheduleKey] || createEmptySchedule(); days.forEach(day => { if (!Array.isArray(classSchedule[day])) { classSchedule[day] = []; } }); // Находим максимальное количество уроков в день let maxLessons = 0; days.forEach(day => { if (classSchedule[day].length > maxLessons) { maxLessons = classSchedule[day].length; } }); // Если расписания нет if (maxLessons === 0) { const row = document.createElement('tr'); const timeCell = document.createElement('td'); timeCell.className = 'time-column'; timeCell.textContent = '—'; row.appendChild(timeCell); days.forEach((day) => { const cell = document.createElement('td'); const emptyDiv = document.createElement('div'); emptyDiv.className = 'lesson break'; if (isAdminMode && currentClass) { emptyDiv.style.cursor = 'pointer'; emptyDiv.style.backgroundColor = '#e6fffa'; emptyDiv.innerHTML = '+ Добавить урок'; emptyDiv.onclick = () => { openAddLessonModal(day, 0); }; emptyDiv.title = 'Нажмите для добавления урока'; } else { emptyDiv.innerHTML = 'Выходной'; } cell.appendChild(emptyDiv); row.appendChild(cell); }); scheduleBody.appendChild(row); return; } // Создаем строки расписания for (let i = 0; i < maxLessons; i++) { const row = document.createElement('tr'); // Добавляем время const timeCell = document.createElement('td'); timeCell.className = 'time-column'; let lessonTime = ''; for (const day of days) { if (classSchedule[day][i]) { lessonTime = classSchedule[day][i].time; break; } } timeCell.innerHTML = ` <div class="lesson-number">${i + 1} урок</div> <div class="lesson-time">${lessonTime || '—'}</div> `; row.appendChild(timeCell); // Добавляем уроки для каждого дня days.forEach((day) => { const cell = document.createElement('td'); const lesson = classSchedule[day][i]; if (lesson) { const lessonDiv = document.createElement('div'); lessonDiv.className = 'lesson subject'; if (isAdminMode) { lessonDiv.style.cursor = 'pointer'; lessonDiv.onclick = () => openEditLessonModal(day, i); lessonDiv.title = 'Нажмите для редактирования'; } lessonDiv.innerHTML = ` <div><strong>${lesson.subject}</strong></div> <div style="font-size: 0.8rem; color: #718096;">${lesson.teacher}</div> `; cell.appendChild(lessonDiv); } else { const emptyDiv = document.createElement('div'); emptyDiv.className = 'lesson break'; if (isAdminMode) { emptyDiv.style.cursor = 'pointer'; emptyDiv.style.backgroundColor = '#e6fffa'; emptyDiv.innerHTML = '+ Добавить урок'; emptyDiv.onclick = () => { openAddLessonModal(day, i); }; emptyDiv.title = 'Нажмите для добавления урока'; } else { emptyDiv.innerHTML = '—'; } cell.appendChild(emptyDiv); } row.appendChild(cell); }); scheduleBody.appendChild(row); } // Если нет уроков в какой-то день, добавляем пустые строки if (maxLessons === 0) { const row = document.createElement('tr'); const timeCell = document.createElement('td'); timeCell.className = 'time-column'; timeCell.textContent = '—'; row.appendChild(timeCell); for (let i = 0; i < 6; i++) { const cell = document.createElement('td'); cell.innerHTML = '<div class="lesson break">Выходной</div>'; row.appendChild(cell); } scheduleBody.appendChild(row); } } // Функция для возврата к выбору класса function backToClassSelection() { document.querySelector('.class-selector').style.display = 'block'; document.getElementById('scheduleContainer').style.display = 'none'; document.querySelectorAll('.class-btn').forEach(btn => { btn.classList.remove('active'); }); currentClass = ''; currentWeekOffset = 0; applyTeacherMode(false); }