/
Kirill2015
/
ret
Обзор
Документация
Войти
/
Kirill2015
/
ret
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
set.html
775 строк
25 KB
Kirill2015
upload files
04 ноя 2025, 20:34
04 ноя 2025, 20:34
c3b1744
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Гуманитарик — 4Б</title> <meta name="theme-color" content="#1e88e5"> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <style> * { box-sizing: border-box; } body { font-family: 'Segoe UI', sans-serif; background: #f5f7fa; margin: 0; padding: 10px; color: #333; line-height: 1.5; font-size: 14px; } @media (min-width: 768px) { body { font-size: 16px; padding: 20px; } } .container { max-width: 1000px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 6px 20px rgba(0,0,0,0.1); overflow: hidden; } header { background: #1e88e5; color: white; padding: 15px; text-align: center; position: relative; } .logout { position: absolute; right: 10px; top: 10px; color: #ffeb3b; cursor: pointer; font-weight: bold; font-size: 0.9em; } .welcome { font-size: 1.1em; margin: 5px 0 0; } .tab-menu { display: flex; flex-wrap: wrap; background: #e3f2fd; border-bottom: 1px solid #ddd; } .tab { padding: 12px 15px; cursor: pointer; text-align: center; flex: 1; font-weight: 600; color: #555; font-size: 0.9em; } @media (max-width: 600px) { .tab { padding: 10px; font-size: 0.85em; } } .tab.active { background: #1e88e5; color: white; } .tab-content { display: none; padding: 15px; } .tab-content.active { display: block; } table { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 0.9em; } th, td { border: 1px solid #ddd; padding: 8px; text-align: center; } th { background: #e3f2fd; } .card { background: #f1f8ff; padding: 12px; border-radius: 8px; margin: 8px 0; box-shadow: 0 2px 6px rgba(0,0,0,0.05); } input, select, button, textarea { padding: 10px; margin: 5px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; width: 100%; } @media (min-width: 768px) { input, select, button, textarea { width: auto; display: inline-block; } } button { background: #1e88e5; color: white; border: none; cursor: pointer; } button:hover { background: #1976d2; } .badge { display: inline-block; background: #ff9800; color: white; padding: 4px 8px; border-radius: 10px; font-size: 0.8em; margin: 4px 2px; } .editable { width: 100%; background: #fffde7; border: 1px dashed #ffa000; border-radius: 4px; padding: 6px; font-family: inherit; resize: vertical; } #loginPage { text-align: center; margin: 40px 10px; } #loginPage input { padding: 10px; width: 100%; max-width: 300px; margin: 8px; font-size: 16px; } #loginPage button { padding: 12px 24px; background: #1e88e5; font-size: 1.1em; } .notification { position: fixed; top: 20px; right: 20px; background: #4caf50; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); z-index: 1000; opacity: 0; transition: opacity 0.3s; } .notification.show { opacity: 1; } footer { text-align: center; padding: 15px; color: #777; font-size: 0.9em; background: #f1f1f1; } .file-link { color: #1e88e5; text-decoration: none; } .file-link:hover { text-decoration: underline; } @media (max-width: 600px) { .welcome { font-size: 1em; } h1, h2 { font-size: 1.2em; } } </style> </head> <body> <!-- Уведомление --> <div id="notification" class="notification"></div> <!-- Страница входа --> <div id="loginPage"> <h1>🌌 Гуманитарик</h1> <p>Цифровая экосистема 4Б класса</p> <input type="text" id="login" placeholder="Логин" /><br> <input type="password" id="password" placeholder="Пароль" /><br> <button onclick="login()">Войти</button> </div> <!-- Основной интерфейс --> <div id="app" style="display: none;"> <div class="container"> <header> <h1>🎓 Гуманитарик</h1> <div class="welcome"> Привет, <span id="currentName">Пользователь</span> (<span id="currentRole">—</span>) </div> <span class="logout" onclick="logout()">Выйти</span> </header> <div class="tab-menu"> <div class="tab active" onclick="openTab('home')">Главная</div> <div class="tab" onclick="openTab('diary')">Дневник</div> <div class="tab" onclick="openTab('chat')">Чат</div> <div class="tab" onclick="openTab('progress')">Прогресс</div> <div class="tab" onclick="openTab('admin')" id="adminTab" style="display: none;">Учитель</div> </div> <!-- Главная --> <div id="home" class="tab-content active"> <h2>📅 Расписание</h2> <div id="scheduleContainer"></div> <h2>📚 Домашнее задание</h2> <div class="card"> <input type="text" id="hwSubject" placeholder="Предмет" /> <input type="text" id="hwTask" placeholder="Задание" /> <input type="file" id="hwFile" /> <button onclick="addHomework()">➕ Добавить задание</button> </div> <div id="hwList"></div> </div> <!-- Дневник --> <div id="diary" class="tab-content"> <h2>📘 Оценки</h2> <div id="gradesSection" class="card"> <select id="gradeStudent"></select> <select id="gradeSubject"> <option>Математика</option> <option>Русский язык</option> <option>Литература</option> <option>Окружающий мир</option> </select> <input type="number" id="gradeValue" placeholder="Оценка" min="2" max="5" /> <button onclick="addGrade()" id="gradeButton">Поставить оценку</button> <button id="refreshStudents" style="display: none;" onclick="renderStudentSelect()">🔄 Обновить</button> </div> <div id="gradesList"></div> </div> <!-- Чат --> <div id="chat" class="tab-content"> <h2>💬 Чат класса</h2> <textarea id="chatInput" rows="2" placeholder="Сообщение..."></textarea> <button onclick="sendMessage()">Отправить</button> <div id="chatMessages"></div> </div> <!-- Прогресс --> <div id="progress" class="tab-content"> <h2>📊 Ваш прогресс</h2> <canvas id="progressChart" height="100"></canvas> <div class="card"> <h3>🏆 Достижения</h3> <div id="badges"></div> </div> </div> <!-- Панель учителя --> <div id="admin" class="tab-content"> <h2>👨🏫 Управление классом</h2> <div class="card"> <h3>➕ Добавить ученика</h3> <input type="text" id="newStudentName" placeholder="Имя ученика" /> <input type="text" id="newParentName" placeholder="Имя родителя" /> <input type="text" id="newStudentLogin" placeholder="Логин ученика" /> <input type="password" id="newStudentPass" placeholder="Пароль ученика" /> <input type="text" id="newParentLogin" placeholder="Логин родителя" /> <input type="password" id="newParentPass" placeholder="Пароль родителя" /> <button onclick="addStudent()">Добавить</button> </div> <div class="card"> <h3>📚 Ученики</h3> <div id="studentList"></div> </div> <div class="card"> <h3>📊 Средний балл по классу</h3> <canvas id="classChart" height="100"></canvas> </div> </div> <footer>© 2025 | Гуманитарик — экосистема Лицея гуманитарных наук</footer> </div> </div> <script> let currentUserName = ""; let currentUserRole = ""; let currentUserLogin = ""; let studentChild = ""; const dataKey = "gumanitarik_v8"; let data = { users: [], homework: [], grades: [], chat: [], schedule: { Пн: "Математика<br>Литература", Вт: "Русский<br>Физкультура", Ср: "Математика<br>ИЗО", Чт: "Окр. мир<br>Музыка", Пт: "Русский<br>Технология" }, notifications: [] }; // === Инициализация === function init() { try { const saved = localStorage.getItem(dataKey); if (saved) { data = JSON.parse(saved); } else { // Создаём учителя по умолчанию data.users.push({ name: "Учитель", login: "teacher", password: "12345", role: "teacher" }); saveData(); } } catch (e) { console.error("Ошибка инициализации:", e); data = { users: [{ name: "Учитель", login: "teacher", password: "12345", role: "teacher" }], homework: [], grades: [], chat: [], schedule: {}, notifications: [] }; saveData(); } } function saveData() { localStorage.setItem(dataKey, JSON.stringify(data)); } // === Уведомления === function showNotification(text) { const notif = document.getElementById("notification"); notif.textContent = text; notif.classList.add("show"); setTimeout(() => { notif.classList.remove("show"); }, 3000); } // === Вход === function login() { const login = document.getElementById("login").value.trim(); const pass = document.getElementById("password").value.trim(); if (!login || !pass) return alert("Введите логин и пароль"); const user = data.users.find(u => u.login === login && u.password === pass); if (!user) { showNotification("❌ Неверный логин или пароль"); return; } currentUserName = user.name; currentUserRole = user.role; currentUserLogin = user.login; if (user.role === "parent" && user.child) { studentChild = user.child; } document.getElementById("loginPage").style.display = "none"; document.getElementById("app").style.display = "block"; document.getElementById("currentName").textContent = user.name; document.getElementById("currentRole").textContent = getRoleName(user.role); document.getElementById("adminTab").style.display = (user.role === "teacher") ? "block" : "none"; document.getElementById("gradeButton").textContent = (user.role === "teacher") ? "Поставить" : "Просмотр"; loadAll(); showNotification(`✅ Добро пожаловать, ${user.name}!`); } function getRoleName(role) { return { student: "Ученик", parent: "Родитель", teacher: "Учитель" }[role] || role; } function logout() { document.getElementById("app").style.display = "none"; document.getElementById("loginPage").style.display = "block"; document.getElementById("login").value = ""; document.getElementById("password").value = ""; showNotification("Вы вышли из системы"); } // === Вкладки === function openTab(tabId) { document.querySelectorAll(".tab-content").forEach(t => t.classList.remove("active")); document.querySelectorAll(".tab").forEach(t => t.classList.remove("active")); document.getElementById(tabId).classList.add("active"); const tab = [...document.querySelectorAll(".tab")].find(t => t.textContent.toLowerCase().includes(tabId)); if (tab) tab.classList.add("active"); if (tabId === "progress") renderChart(); if (tabId === "admin") renderAdmin(); if (tabId === "home") renderSchedule(); if (tabId === "diary") setupGradesForm(); } // === Расписание === function renderSchedule() { const container = document.getElementById("scheduleContainer"); container.innerHTML = ""; const days = ["Пн", "Вт", "Ср", "Чт", "Пт"]; const table = document.createElement("table"); const header = document.createElement("tr"); days.forEach(d => { const th = document.createElement("th"); th.textContent = d; header.appendChild(th); }); table.appendChild(header); const row = document.createElement("tr"); days.forEach(day => { const td = document.createElement("td"); if (currentUserRole === "teacher") { const ta = document.createElement("textarea"); ta.className = "editable"; ta.rows = 2; ta.value = (data.schedule[day] || "").replace(/<br>/g, "\n"); ta.onblur = () => { data.schedule[day] = ta.value.trim().replace(/\n/g, "<br>"); saveData(); showNotification("📅 Расписание обновлено"); }; td.appendChild(ta); } else { td.innerHTML = data.schedule[day] || "—"; } row.appendChild(td); }); table.appendChild(row); container.appendChild(table); } // === Дневник === function setupGradesForm() { const select = document.getElementById("gradeStudent"); const refreshBtn = document.getElementById("refreshStudents"); if (currentUserRole === "teacher") { select.style.display = "inline-block"; refreshBtn.style.display = "inline-block"; renderStudentSelect(); } else { select.style.display = "none"; refreshBtn.style.display = "none"; } } function renderStudentSelect() { const select = document.getElementById("gradeStudent"); select.innerHTML = ""; const students = data.users.filter(u => u.role === "student"); if (students.length === 0) { const opt = document.createElement("option"); opt.value = ""; opt.textContent = "— Нет учеников —"; opt.disabled = true; select.appendChild(opt); } else { const defaultOpt = document.createElement("option"); defaultOpt.value = ""; defaultOpt.textContent = "— Выберите —"; select.appendChild(defaultOpt); students.forEach(u => { const opt = document.createElement("option"); opt.value = u.name; opt.textContent = u.name; select.appendChild(opt); }); } } function addGrade() { if (currentUserRole !== "teacher") return alert("Только учитель может ставить оценки!"); const student = document.getElementById("gradeStudent").value; const subject = document.getElementById("gradeSubject").value; const grade = parseInt(document.getElementById("gradeValue").value); if (!student) return alert("Выберите ученика!"); if (isNaN(grade) || grade < 2 || grade > 5) return alert("Оценка от 2 до 5!"); data.grades.push({ student, subject, grade, teacher: currentUserName, time: getTime() }); saveData(); loadGrades(); document.getElementById("gradeValue").value = ""; showNotification(`✅ Оценка ${grade} поставлена ${student}`); } function loadGrades() { const el = document.getElementById("gradesList"); el.innerHTML = "<table><tr><th>Предмет</th><th>Оценка</th><th>Учитель</th><th>Дата</th></tr>"; const students = getVisibleStudents(); const filtered = data.grades.filter(g => students.includes(g.student)); if (filtered.length === 0) { el.innerHTML += "<tr><td colspan='4'>Нет оценок</td></tr>"; } else { filtered.forEach(g => { el.innerHTML += `<tr> <td>${g.subject}</td> <td><b>${g.grade}</b></td> <td>${g.teacher}</td> <td>${g.time}</td> </tr>`; }); } el.innerHTML += "</table>"; } function getVisibleStudents() { if (currentUserRole === "student") return [currentUserName]; if (currentUserRole === "parent") return [studentChild]; if (currentUserRole === "teacher") return data.users.filter(u => u.role === "student").map(u => u.name); return []; } // === Добавление ученика === function addStudent() { const name = document.getElementById("newStudentName").value.trim(); const parentName = document.getElementById("newParentName").value.trim(); const loginS = document.getElementById("newStudentLogin").value.trim(); const passS = document.getElementById("newStudentPass").value.trim(); const loginP = document.getElementById("newParentLogin").value.trim(); const passP = document.getElementById("newParentPass").value.trim(); if (!name || !loginS || !passS) return alert("Заполните данные ученика!"); if (data.users.find(u => u.login === loginS || u.name === name)) { return alert("Ученик с таким именем или логином уже есть!"); } data.users.push({ name, login: loginS, password: passS, role: "student" }); if (parentName && loginP && passP) { data.users.push({ name: parentName, login: loginP, password: passP, role: "parent", child: name }); } saveData(); renderAdmin(); clearAddStudentForm(); showNotification(`✅ ${name} добавлен в класс`); } function clearAddStudentForm() { document.getElementById("newStudentName").value = ""; document.getElementById("newParentName").value = ""; document.getElementById("newStudentLogin").value = ""; document.getElementById("newStudentPass").value = ""; document.getElementById("newParentLogin").value = ""; document.getElementById("newParentPass").value = ""; } function renderAdmin() { const list = document.getElementById("studentList"); list.innerHTML = ""; const students = data.users.filter(u => u.role === "student"); if (students.length === 0) { list.innerHTML = "<p>Нет учеников.</p>"; } else { students.forEach(s => { const div = document.createElement("div"); div.className = "card"; div.innerHTML = `<b>${s.name}</b> <button onclick="removeStudent('${s.login}')" style="background:#e53935; float:right;">×</button>`; list.appendChild(div); }); } const ctx = document.getElementById("classChart").getContext("2d"); const subjects = ["Математика", "Русский язык", "Литература", "Окружающий мир"]; const avg = subjects.map(subj => { const grades = data.grades.filter(g => g.subject === subj).map(g => parseInt(g.grade)); return grades.length ? (grades.reduce((a,b) => a + b, 0) / grades.length).toFixed(1) : 0; }); if (window.classChart) window.classChart.destroy(); window.classChart = new Chart(ctx, { type: 'line', data: { labels: subjects, datasets: [{ label: 'Средний балл', data: avg, borderColor: '#1e88e5', fill: false }] } }); } function removeStudent(login) { if (!confirm("Удалить ученика и все данные?")) return; data.users = data.users.filter(u => u.login !== login && u.child !== login); data.grades = data.grades.filter(g => g.student !== login); saveData(); renderAdmin(); showNotification("❌ Пользователь удалён"); } // === Домашка === function addHomework() { const subject = document.getElementById("hwSubject").value.trim(); const task = document.getElementById("hwTask").value.trim(); const fileInput = document.getElementById("hwFile"); const file = fileInput.files[0]; if (!subject || !task) return alert("Введите предмет и задание!"); let fileData = null; let fileName = ""; if (file) { const reader = new FileReader(); reader.onload = function(e) { fileData = e.target.result; fileName = file.name; data.homework.push({ subject, task, file: fileData, fileName, author: currentUserName, time: getTime() }); saveData(); loadHomework(); clearHomeworkForm(); showNotification("📘 Новое домашнее задание!"); }; reader.readAsDataURL(file); } else { data.homework.push({ subject, task, file: null, fileName: "", author: currentUserName, time: getTime() }); saveData(); loadHomework(); clearHomeworkForm(); showNotification("📘 Домашка добавлена"); } } function clearHomeworkForm() { document.getElementById("hwSubject").value = ""; document.getElementById("hwTask").value = ""; document.getElementById("hwFile").value = ""; } function loadHomework() { const el = document.getElementById("hwList"); el.innerHTML = ""; if (data.homework.length === 0) { el.innerHTML = "<p>Нет домашних заданий.</p>"; return; } data.homework.slice().reverse().forEach(h => { const div = document.createElement("div"); div.className = "card"; let fileHtml = ""; if (h.file) { const sizeKB = Math.round(h.file.length * 0.75 / 1024); fileHtml = `<br>📎 <a href="${h.file}" download="${h.fileName}" class="file-link">${h.fileName}</a> (${sizeKB} КБ)`; } div.innerHTML = `<b>${h.subject}</b>: ${h.task} — ${h.author} <small>(${h.time})</small>${fileHtml}`; el.appendChild(div); }); } // === Чат === function sendMessage() { const text = document.getElementById("chatInput").value.trim(); if (!text) return; data.chat.push({ author: currentUserName, text, role: currentUserRole, time: getTime() }); if (data.chat.length > 100) data.chat.shift(); saveData(); loadChat(); document.getElementById("chatInput").value = ""; showNotification("💬 Новое сообщение в чате"); } function loadChat() { const el = document.getElementById("chatMessages"); el.innerHTML = ""; data.chat.forEach(m => { const div = document.createElement("div"); div.className = "card"; div.innerHTML = `<b>${m.author}</b> (${m.role}): ${m.text} <small>${m.time}</small>`; el.appendChild(div); }); el.scrollTop = el.scrollHeight; } // === Прогресс === function renderChart() { const ctx = document.getElementById("progressChart").getContext("2d"); const subjects = ["Математика", "Русский язык", "Литература", "Окружающий мир"]; const students = getVisibleStudents(); const avg = subjects.map(subj => { const grades = data.grades .filter(g => students.includes(g.student) && g.subject === subj) .map(g => parseInt(g.grade)); return grades.length ? (grades.reduce((a,b) => a + b, 0) / grades.length).toFixed(1) : 0; }); if (window.progressChart) window.progressChart.destroy(); window.progressChart = new Chart(ctx, { type: 'bar', data: { labels: subjects, datasets: [{ label: 'Средний балл', data: avg, backgroundColor: '#1e88e5' }] }, options: { responsive: true, scales: { y: { min: 0, max: 5 } } } }); const badgesEl = document.getElementById("badges"); badgesEl.innerHTML = ""; const total = data.grades.filter(g => students.includes(g.student)).length; if (total > 3) badgesEl.innerHTML += '<span class="badge">Читатель</span>'; if (avg[0] >= 4) badgesEl.innerHTML += '<span class="badge">Математик</span>'; } function getTime() { return new Date().toLocaleTimeString("ru", { hour: "2-digit", minute: "2-digit" }); } function loadAll() { renderSchedule(); loadHomework(); loadGrades(); loadChat(); setupGradesForm(); } window.onload = () => { init(); }; </script> </body> </html>