diff --git a/background_light.jpg b/background_light.jpg new file mode 100644 index 0000000..aa7fe4c Binary files /dev/null and b/background_light.jpg differ diff --git a/exam/Scripts/examInfo.js b/exam/Scripts/examInfo.js new file mode 100644 index 0000000..55f3952 --- /dev/null +++ b/exam/Scripts/examInfo.js @@ -0,0 +1,154 @@ +document.addEventListener("DOMContentLoaded", () => { + const examNameElem = document.getElementById("examName"); + const messageElem = document.getElementById("message"); + const currentTimeElem = document.getElementById("current-time"); + const currentSubjectElem = document.getElementById("current-subject"); + const examTimingElem = document.getElementById("exam-timing"); + const remainingTimeElem = document.getElementById("remaining-time"); + const statusElem = document.getElementById("status"); + const examTableBodyElem = document.getElementById("exam-table-body"); + const roomElem = document.getElementById("room"); + let offsetTime = getCookie("offsetTime") || 0; + + function fetchData() { + return fetch('exam_config.json', { cache: "no-store" }) + .then(response => response.json()) + .then(data => { + displayExamInfo(data); + updateCurrentTime(); + updateExamInfo(data); + setInterval(() => updateCurrentTime(), 1000); + setInterval(() => updateExamInfo(data), 1000); + }) + .catch(error => errorSystem.show('获取考试数据失败: ' + error.message)); + } + + function displayExamInfo(data) { + try { + const examNameText = data.examName; + const roomText = roomElem.textContent; + examNameElem.innerHTML = `${examNameText} ${roomText}`; + messageElem.textContent = data.message; + } catch (e) { + errorSystem.show('显示考试信息失败: ' + e.message); + } + } + + function updateCurrentTime() { + try { + const now = new Date(new Date().getTime() + offsetTime * 1000); + currentTimeElem.textContent = now.toLocaleTimeString('zh-CN', { hour12: false }); + } catch (e) { + errorSystem.show('更新时间失败: ' + e.message); + } + } + + function updateExamInfo(data) { + try { + const now = new Date(new Date().getTime() + offsetTime * 1000); + let currentExam = null; + let nextExam = null; + let lastExam = null; + + data.examInfos.forEach(exam => { + const start = new Date(exam.start); + const end = new Date(exam.end); + if (now >= start && now <= end) { + currentExam = exam; + } + if (!currentExam && !nextExam && now < start) { + nextExam = exam; + } + if (now > end && (!lastExam || end > new Date(lastExam.end))) { + lastExam = exam; + } + }); + + if (currentExam) { + currentSubjectElem.textContent = `当前科目: ${currentExam.name}`; + examTimingElem.textContent = `起止时间: ${formatTimeWithoutSeconds(new Date(currentExam.start).toLocaleTimeString('zh-CN', { hour12: false }))} - ${formatTimeWithoutSeconds(new Date(currentExam.end).toLocaleTimeString('zh-CN', { hour12: false }))}`; + const remainingTime = (new Date(currentExam.end).getTime() - now.getTime() + 1000) / 1000; + const remainingHours = Math.floor(remainingTime / 3600); + const remainingMinutes = Math.floor((remainingTime % 3600) / 60); + const remainingSeconds = Math.floor(remainingTime % 60); + const remainingTimeText = `${remainingHours}时 ${remainingMinutes}分 ${remainingSeconds}秒`; + + if (remainingHours === 0 && remainingMinutes <= 14) { + remainingTimeElem.textContent = `倒计时: ${remainingTimeText}`; + remainingTimeElem.style.color = "red"; + remainingTimeElem.style.fontWeight = "bold"; + } else { + remainingTimeElem.textContent = `剩余时间: ${remainingTimeText}`; + remainingTimeElem.style.color = "#93b4f7"; + remainingTimeElem.style.fontWeight = "normal"; + } + + statusElem.textContent = "状态: 进行中"; + statusElem.style.color = "#5ba838"; + } else if (lastExam && now < new Date(lastExam.end).getTime() + 60000) { + const timeSinceEnd = (now.getTime() - new Date(lastExam.end).getTime()) / 1000; + currentSubjectElem.textContent = `上场科目: ${lastExam.name}`; + examTimingElem.textContent = ""; + remainingTimeElem.textContent = ``; + statusElem.textContent = "状态: 已结束"; + statusElem.style.color = "red"; + } else if (nextExam) { + const timeUntilStart = ((new Date(nextExam.start).getTime() - now.getTime()) / 1000) + 1; + const remainingHours = Math.floor(timeUntilStart / 3600); + const remainingMinutes = Math.floor((timeUntilStart % 3600) / 60); + const remainingSeconds = Math.floor(timeUntilStart % 60); + const remainingTimeText = `${remainingHours}时 ${remainingMinutes}分 ${remainingSeconds}秒`; + + if (timeUntilStart <= 15 * 60) { + currentSubjectElem.textContent = `即将开始: ${nextExam.name}`; + remainingTimeElem.textContent = `倒计时: ${remainingTimeText}`; + remainingTimeElem.style.color = "orange"; + remainingTimeElem.style.fontWeight = "bold"; + statusElem.textContent = "状态: 即将开始"; + statusElem.style.color = "#DBA014"; + } else { + currentSubjectElem.textContent = `下一场科目: ${nextExam.name}`; + remainingTimeElem.textContent = ""; + statusElem.textContent = "状态: 未开始"; + remainingTimeElem.style.fontWeight = "normal"; + statusElem.style.color = "#EAEE5B"; + } + + examTimingElem.textContent = `起止时间: ${formatTimeWithoutSeconds(new Date(nextExam.start).toLocaleTimeString('zh-CN', { hour12: false }))} - ${formatTimeWithoutSeconds(new Date(nextExam.end).toLocaleTimeString('zh-CN', { hour12: false }))}`; + } else { + currentSubjectElem.textContent = "考试均已结束"; + examTimingElem.textContent = ""; + remainingTimeElem.textContent = ""; + statusElem.textContent = "状态: 空闲"; + statusElem.style.color = "#3946AF"; + } + + examTableBodyElem.innerHTML = ""; + data.examInfos.forEach(exam => { + const start = new Date(exam.start); + const end = new Date(exam.end); + let status = ""; + if (now < start) { + status = "即将开始"; + } else if (now > end) { + status = "已结束"; + } else { + status = "进行中"; + } + + const row = document.createElement("tr"); + row.className = `exam-status-${status}`; + row.innerHTML = ` + ${exam.name} + ${formatTimeWithoutSeconds(new Date(exam.start).toLocaleTimeString('zh-CN', { hour12: false }))} + ${formatTimeWithoutSeconds(new Date(exam.end).toLocaleTimeString('zh-CN', { hour12: false }))} + `; + examTableBodyElem.appendChild(row); + }); + } catch (e) { + errorSystem.show('更新考试信息失败: ' + e.message); + } + } + + fetchData(); +}); diff --git a/exam/Scripts/main.js b/exam/Scripts/main.js new file mode 100644 index 0000000..09d3ec6 --- /dev/null +++ b/exam/Scripts/main.js @@ -0,0 +1,17 @@ +document.addEventListener("DOMContentLoaded", () => { + const fullscreenBtn = document.getElementById("fullscreen-btn"); + + fullscreenBtn.addEventListener("click", () => { + try { + if (!document.fullscreenElement) { + document.documentElement.requestFullscreen(); + } else { + if (document.exitFullscreen) { + document.exitFullscreen(); + } + } + } catch (e) { + errorSystem.show('全屏切换失败: ' + e.message); + } + }); +}); diff --git a/exam/Scripts/settings.js b/exam/Scripts/settings.js new file mode 100644 index 0000000..0f57755 --- /dev/null +++ b/exam/Scripts/settings.js @@ -0,0 +1,87 @@ +document.addEventListener("DOMContentLoaded", () => { + const settingsBtn = document.getElementById("settings-btn"); + const settingsModal = document.getElementById("settings-modal"); + const closeSettingsBtn = document.getElementById("close-settings-btn"); + const saveSettingsBtn = document.getElementById("save-settings-btn"); + const offsetTimeInput = document.getElementById("offset-time"); + const roomInput = document.getElementById("room-input"); + const roomElem = document.getElementById("room"); + const zoomInput = document.getElementById("zoom-input"); + const themeToggle = document.getElementById("theme-toggle"); + const themeLink = document.getElementById("theme-link"); + + let offsetTime = getCookie("offsetTime") || 0; + let room = getCookie("room") || ""; + let zoomLevel = getCookie("zoomLevel") || 1; + let theme = getCookie("theme") || "dark"; + + offsetTime = parseInt(offsetTime); + roomElem.textContent = room; + + if (theme === "light") { + themeLink.href = "Styles/light.css"; + themeToggle.checked = true; + } else { + themeLink.href = "Styles/dark.css"; + themeToggle.checked = false; + } + + settingsBtn.addEventListener("click", () => { + try { + offsetTimeInput.value = offsetTime; + roomInput.value = room; + zoomInput.value = zoomLevel; + settingsModal.style.display = "block"; + } catch (e) { + errorSystem.show('打开设置失败: ' + e.message); + } + }); + + closeSettingsBtn.addEventListener("click", () => { + try { + settingsModal.classList.add("fade-out"); + setTimeout(() => { + settingsModal.style.display = "none"; + settingsModal.classList.remove("fade-out"); + }, 300); + } catch (e) { + errorSystem.show('关闭设置失败: ' + e.message); + } + }); + + saveSettingsBtn.addEventListener("click", () => { + try { + offsetTime = parseInt(offsetTimeInput.value); + room = roomInput.value; + zoomLevel = parseFloat(zoomInput.value); + theme = themeToggle.checked ? "light" : "dark"; + setCookie("offsetTime", offsetTime, 365); + setCookie("room", room, 365); + setCookie("zoomLevel", zoomLevel, 365); + setCookie("theme", theme, 365); + roomElem.textContent = room; + document.body.style.zoom = zoomLevel; + themeLink.href = theme === "light" ? "Styles/light.css" : "Styles/dark.css"; + settingsModal.classList.add("fade-out"); + setTimeout(() => { + settingsModal.style.display = "none"; + settingsModal.classList.remove("fade-out"); + }, 300); + // 立即生效时间偏移 + location.reload(); + } catch (e) { + errorSystem.show('保存设置失败: ' + e.message); + } + }); + + themeToggle.addEventListener("change", () => { + const theme = themeToggle.checked ? "light" : "dark"; + themeLink.href = theme === "light" ? "Styles/light.css" : "Styles/dark.css"; + }); + + try { + document.body.style.zoom = zoomLevel; + } catch (e) { + errorSystem.show('初始化缩放失败: ' + e.message); + } +}); diff --git a/exam/Scripts/utils.js b/exam/Scripts/utils.js new file mode 100644 index 0000000..68e8cb9 --- /dev/null +++ b/exam/Scripts/utils.js @@ -0,0 +1,39 @@ +function setCookie(name, value, days) { + const d = new Date(); + d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)); + const expires = "expires=" + d.toUTCString(); + document.cookie = name + "=" + value + ";" + expires + ";path=/"; +} + +function getCookie(name) { + const nameEQ = name + "="; + const ca = document.cookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) === ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); + } + return null; +} + +function formatTimeWithoutSeconds(time) { + return time.slice(0, -3); +} + +const errorSystem = { + show: function(message) { + try { + const container = document.querySelector('.error-container'); + const content = document.getElementById('errorMessage'); + content.textContent = message; + container.style.display = 'flex'; + setTimeout(this.hide, 5000); + } catch(e) { + console.error('错误提示系统异常:', e); + } + }, + hide: function() { + const container = document.querySelector('.error-container'); + if (container) container.style.display = 'none'; + } +}; diff --git a/exam/styles.css b/exam/Styles/dark.css similarity index 73% rename from exam/styles.css rename to exam/Styles/dark.css index d235218..c8ed665 100644 --- a/exam/styles.css +++ b/exam/Styles/dark.css @@ -2,7 +2,7 @@ body { font-family: 'HarmonyOS Sans SC Regular', 'Roboto', Arial, sans-serif; margin: 0; padding: 0; - background: url('../background.jpg') no-repeat center center fixed; + background: url('../../background.jpg') no-repeat center center fixed; /* 更新路径 */ background-size: cover; animation: fadeIn 1s; color: #e0e0e0; @@ -207,6 +207,15 @@ td:last-child { to { opacity: 1; transform: translateY(0); } } +@keyframes fadeOut { + from { opacity: 1; transform: translateY(0); } + to { opacity: 0; transform: translateY(-20px); } +} + +.fade-out { + animation: fadeOut 0.3s ease; +} + #settings-modal-content h3 { margin: 0 0 20px; color: #e0e0e0; @@ -222,8 +231,16 @@ td:last-child { color: #b0b0b0; } +#settings-modal-content label[for="offset-time"], +#settings-modal-content label[for="room-input"], +#settings-modal-content label[for="zoom-input"] { + justify-content: space-between; +} + #settings-modal-content input[type="number"], #settings-modal-content input[type="text"] { + flex-grow: 1; + margin-left: 10px; font-size: 1.5rem; padding: 10px; margin-top: 10px; @@ -281,3 +298,95 @@ td:last-child { #close-settings-btn:hover { background-color: #c9302c; } + +.error-container { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: #ff6b6b; + color: white; + padding: 16px; + display: none; + z-index: 10001; + animation: slideUp 0.3s ease; +} + +@keyframes slideUp { + from { transform: translateY(100%); } + to { transform: translateY(0); } +} + +.error-content { + max-width: 800px; + margin: 0 auto; + font-size: 15px; + display: flex; + align-items: center; + gap: 12px; +} + +.error-content:before { + content: '!'; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + background: white; + color: #ff6b6b; + border-radius: 50%; + font-weight: bold; +} + +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: .4s; + border-radius: 34px; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:checked + .slider:before { + transform: translateX(26px); +} + +.theme-toggle-container { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} diff --git a/exam/Styles/light.css b/exam/Styles/light.css new file mode 100644 index 0000000..aed35e0 --- /dev/null +++ b/exam/Styles/light.css @@ -0,0 +1,394 @@ +body { + font-family: 'HarmonyOS Sans SC Regular', 'Roboto', Arial, sans-serif; + margin: 0; + padding: 0; + background: url('../../background_light.jpg') no-repeat center center fixed; + background-size: cover; + animation: fadeIn 1s; + color: #333; + overflow: auto; +} + +body::-webkit-scrollbar { + display: none; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +#fullscreen-btn, #settings-btn { + position: absolute; + top: 20px; + padding: 10px 20px; + font-size: 1rem; + cursor: pointer; + background-color: #f0f0f0; + color: #333; + border: 1px solid #ccc; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + transition: background-color 0.3s ease, transform 0.3s ease; + z-index: 1001; +} + +#fullscreen-btn { + right: 20px; +} + +#settings-btn { + right: 120px; +} + +#settings-btn:hover, #fullscreen-btn:hover { + background-color: #ccc; + transform: scale(1.05); +} + +.container { + padding: 20px; + max-width: 1400px; + margin: auto; + background-color: rgba(255, 255, 255, 0); +} + +h1 { + font-size: 3.5rem; + font-weight: bold; + text-align: left; + margin-bottom: 10px; + color: #333; + display: flex; + align-items: center; + justify-content: space-between; +} + +#room { + font-size: 3.5rem; + font-weight: bold; + color: #333; + position: relative; + right: 0; + margin-left: 20px; +} + +#message { + font-size: 1.5rem; + color: #16a3d1; + margin-bottom: 20px; +} + +.content { + display: flex; + justify-content: space-between; + gap: 3px; +} + +.left-column, .right-column { + width: 48%; + display: flex; + flex-direction: column; + gap: 3px; +} + +.clock-section, .info-section, .right-column { + background-color: rgba(255, 255, 255, 0.2); + backdrop-filter: blur(10px); + padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + border-radius: 8px; +} + +#current-time { + font-size: 8rem; + text-align: center; + color: #333; + margin: 0; + font-weight: bold; +} + +#current-subject, #exam-timing, #remaining-time, #status { + font-size: 3rem; + margin: 10px 0; + text-align: left; + color: #333; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + border: 1px solid #ccc; + background-color: rgba(255, 255, 255, 0.5); +} + +th, td { + border: 1px solid #ccc; + padding: 2px; + font-size: 2rem; + text-align: center; +} + +th { + background-color: #f0f0f0; + color: #333; + font-weight: bold; + border-bottom: 2px solid #ccc; +} + +.exam-status-进行中 td { + color: #5ba838 !important; +} + +.exam-status-即将开始 td { + color: #fe9901 !important; +} + +.exam-status-已结束 td { + color: #ec0434 !important; +} + +.exam-status-空闲 td { + color: blue !important; +} + +tr:hover { + background-color: #f0f0f0; +} + +table { + border-radius: 8px; + overflow: hidden; +} + +td { + border-bottom: 1px solid #ccc; +} + +td:first-child { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +} + +td:last-child { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} + +#settings-modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); + padding-top: 60px; +} + +#settings-modal-content { + background: rgba(255, 255, 255, 0.95); + padding: 25px; + margin: 25px auto; + border-radius: 12px; + border: 1px solid #ccc; + backdrop-filter: blur(8px); + max-width: 600px; + animation: fadeIn 0.5s ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-20px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes fadeOut { + from { opacity: 1; transform: translateY(0); } + to { opacity: 0; transform: translateY(-20px); } +} + +.fade-out { + animation: fadeOut 0.3s ease; +} + +#settings-modal-content h3 { + margin: 0 0 20px; + color: #333; + font-size: 20px; +} + +#settings-modal-content label { + display: flex; + align-items: center; + gap: 10px; + margin: 12px 0; + font-size: 16px; + color: #666; +} + +#settings-modal-content label[for="offset-time"], +#settings-modal-content label[for="room-input"], +#settings-modal-content label[for="zoom-input"] { + justify-content: space-between; +} + +#settings-modal-content input[type="number"], +#settings-modal-content input[type="text"] { + flex-grow: 1; + margin-left: 10px; +} + +#settings-modal-content input[type="number"], +#settings-modal-content input[type="text"] { + font-size: 1.5rem; + padding: 10px; + margin-top: 10px; + margin-bottom: 20px; + width: 100%; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 5px; + background-color: #fff; + color: #333; +} + +#settings-modal-content input:focus { + outline: none; + border-color: #007acc; + box-shadow: 0 0 0 1px #007acc; +} + +.button-group { + display: flex; + justify-content: flex-end; + gap: 10px; +} + +#settings-modal-content button { + background: linear-gradient(135deg, #3498db, #2980b9); + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + cursor: pointer; + font-size: 15px; + font-weight: 500; + transition: all 0.2s ease; + box-shadow: 0 4px 12px rgba(52,152,219,0.25); +} + +#settings-modal-content button:hover { + transform: translateY(-1px); + box-shadow: 0 6px 16px rgba(52,152,219,0.35); +} + +#close-settings-btn { + padding: 10px 20px; + font-size: 2rem; + cursor: pointer; + background-color: #d9534f; + color: white; + border: none; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + transition: background-color 0.3s ease; +} + +#close-settings-btn:hover { + background-color: #c9302c; +} + +.error-container { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: #ff6b6b; + color: white; + padding: 16px; + display: none; + z-index: 10001; + animation: slideUp 0.3s ease; +} + +@keyframes slideUp { + from { transform: translateY(100%); } + to { transform: translateY(0); } +} + +.error-content { + max-width: 800px; + margin: 0 auto; + font-size: 15px; + display: flex; + align-items: center; + gap: 12px; +} + +.error-content:before { + content: '!'; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + background: white; + color: #ff6b6b; + border-radius: 50%; + font-weight: bold; +} + +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: .4s; + border-radius: 34px; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:checked + .slider:before { + transform: translateX(26px); +} + +.theme-toggle-container { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} diff --git a/exam/exam_config.json b/exam/exam_config.json index ef7b8bd..65a45c3 100644 --- a/exam/exam_config.json +++ b/exam/exam_config.json @@ -1,47 +1,17 @@ { - "examName": "开学检测", - "message": "沉着考试,冷静应对", - "room": " ", + "examName": "测试考试", + "message": "请保持安静,认真答题", + "room": "101", "examInfos": [ { - "name": "语文", - "start": "2025-02-14T07:20:00", - "end": "2025-02-14T09:50:00" + "name": "测试科目1", + "start": "2025-03-03T22:00:00", + "end": "2025-03-03T23:30:00" }, { - "name": "化学", - "start": "2025-02-14T10:20:00", - "end": "2025-02-14T11:50:00" - }, - { - "name": "政治/生物", - "start": "2025-02-14T14:10:00", - "end": "2025-02-14T15:40:00" - }, - { - "name": "地理", - "start": "2025-02-14T16:10:00", - "end": "2025-02-14T17:40:00" - }, - { - "name": "英语", - "start": "2025-02-15T07:50:00", - "end": "2025-02-15T09:50:00" - }, - { - "name": "物理", - "start": "2025-02-15T10:20:00", - "end": "2025-02-15T11:50:00" - }, - { - "name": "数学", - "start": "2025-02-15T14:10:00", - "end": "2025-02-15T16:10:00" - }, - { - "name": "历史", - "start": "2025-02-15T16:30:00", - "end": "2025-02-15T18:00:00" + "name": "测试科目2", + "start": "2025-03-03T23:45:00", + "end": "2025-03-04T01:15:00" } ] } diff --git a/exam/index.html b/exam/index.html index 9b5e941..55b0564 100644 --- a/exam/index.html +++ b/exam/index.html @@ -4,10 +4,13 @@ Exam Schedule - + +
+
+
@@ -54,12 +57,22 @@

系统设置

+
+ + +
- + + + + diff --git a/exam/script.js b/exam/script.js deleted file mode 100644 index 5af39d1..0000000 --- a/exam/script.js +++ /dev/null @@ -1,223 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const examNameElem = document.getElementById("examName"); - const messageElem = document.getElementById("message"); - const currentTimeElem = document.getElementById("current-time"); - const currentSubjectElem = document.getElementById("current-subject"); - const examTimingElem = document.getElementById("exam-timing"); - const remainingTimeElem = document.getElementById("remaining-time"); - const statusElem = document.getElementById("status"); - const examTableBodyElem = document.getElementById("exam-table-body"); - const fullscreenBtn = document.getElementById("fullscreen-btn"); - const settingsBtn = document.getElementById("settings-btn"); - const settingsModal = document.getElementById("settings-modal"); - const closeSettingsBtn = document.getElementById("close-settings-btn"); - const saveSettingsBtn = document.getElementById("save-settings-btn"); - const offsetTimeInput = document.getElementById("offset-time"); - const roomInput = document.getElementById("room-input"); - const roomElem = document.getElementById("room"); - const zoomInput = document.getElementById("zoom-input"); - - let offsetTime = getCookie("offsetTime") || 0; - let room = getCookie("room") || ""; - let zoomLevel = getCookie("zoomLevel") || 1; - - offsetTime = parseInt(offsetTime); - roomElem.textContent = room; - - function fetchData() { - return fetch('exam_config.json', { cache: "no-store" }) // 不保留缓存 - .then(response => response.json()) - .then(data => { - displayExamInfo(data); - updateCurrentTime(); - updateExamInfo(data); - setInterval(() => updateCurrentTime(), 1000); // Update current time every second - setInterval(() => updateExamInfo(data), 1000); // Update exam info every second - }) - .catch(error => console.error('Error fetching exam data:', error)); - } - - function displayExamInfo(data) { - // Display exam name - const examNameText = data.examName; - const roomText = roomElem.textContent; - examNameElem.innerHTML = `${examNameText} ${roomText}`; - // Display message - messageElem.textContent = data.message; - } - - function updateCurrentTime() { - const now = new Date(new Date().getTime() + offsetTime * 1000); - currentTimeElem.textContent = now.toLocaleTimeString('zh-CN', { hour12: false }); - } - - function formatTimeWithoutSeconds(time) { - // Convert time to string and remove seconds if present - return time.slice(0, -3); - } - - function updateExamInfo(data) { - const now = new Date(new Date().getTime() + offsetTime * 1000); - let currentExam = null; - let nextExam = null; - let lastExam = null; - - data.examInfos.forEach(exam => { - const start = new Date(exam.start); - const end = new Date(exam.end); - if (now >= start && now <= end) { - currentExam = exam; - } - if (!currentExam && !nextExam && now < start) { - nextExam = exam; - } - if (now > end && (!lastExam || end > new Date(lastExam.end))) { - lastExam = exam; - } - }); - - if (currentExam) { - currentSubjectElem.textContent = `当前科目: ${currentExam.name}`; - examTimingElem.textContent = `起止时间: ${formatTimeWithoutSeconds(new Date(currentExam.start).toLocaleTimeString('zh-CN', { hour12: false }))} - ${formatTimeWithoutSeconds(new Date(currentExam.end).toLocaleTimeString('zh-CN', { hour12: false }))}`; - const remainingTime = (new Date(currentExam.end).getTime() - now.getTime() + 1000) / 1000; - const remainingHours = Math.floor(remainingTime / 3600); - const remainingMinutes = Math.floor((remainingTime % 3600) / 60); - const remainingSeconds = Math.floor(remainingTime % 60); - const remainingTimeText = `${remainingHours}时 ${remainingMinutes}分 ${remainingSeconds}秒`; - - if (remainingHours === 0 && remainingMinutes <= 14) { - remainingTimeElem.textContent = `倒计时: ${remainingTimeText}`; - remainingTimeElem.style.color = "red"; - remainingTimeElem.style.fontWeight = "bold"; - } else { - remainingTimeElem.textContent = `剩余时间: ${remainingTimeText}`; - remainingTimeElem.style.color = "#93b4f7"; - remainingTimeElem.style.fontWeight = "normal"; - } - - statusElem.textContent = "状态: 进行中"; - statusElem.style.color = "#5ba838"; - } else if (lastExam && now < new Date(lastExam.end).getTime() + 60000) { - const timeSinceEnd = (now.getTime() - new Date(lastExam.end).getTime()) / 1000; - currentSubjectElem.textContent = `上场科目: ${lastExam.name}`; - examTimingElem.textContent = ""; - remainingTimeElem.textContent = ``; - statusElem.textContent = "状态: 已结束"; - statusElem.style.color = "red"; - } else if (nextExam) { - const timeUntilStart = ((new Date(nextExam.start).getTime() - now.getTime()) / 1000) + 1; - const remainingHours = Math.floor(timeUntilStart / 3600); - const remainingMinutes = Math.floor((timeUntilStart % 3600) / 60); - const remainingSeconds = Math.floor(timeUntilStart % 60); - const remainingTimeText = `${remainingHours}时 ${remainingMinutes}分 ${remainingSeconds}秒`; - - if (timeUntilStart <= 15 * 60) { - currentSubjectElem.textContent = `即将开始: ${nextExam.name}`; - remainingTimeElem.textContent = `倒计时: ${remainingTimeText}`; - remainingTimeElem.style.color = "orange"; - remainingTimeElem.style.fontWeight = "bold"; - statusElem.textContent = "状态: 即将开始"; - statusElem.style.color = "#DBA014"; - } else { - currentSubjectElem.textContent = `下一场科目: ${nextExam.name}`; - remainingTimeElem.textContent = ""; - statusElem.textContent = "状态: 未开始"; - remainingTimeElem.style.fontWeight = "normal"; - statusElem.style.color = "#EAEE5B"; - } - - examTimingElem.textContent = `起止时间: ${formatTimeWithoutSeconds(new Date(nextExam.start).toLocaleTimeString('zh-CN', { hour12: false }))} - ${formatTimeWithoutSeconds(new Date(nextExam.end).toLocaleTimeString('zh-CN', { hour12: false }))}`; - } else { - currentSubjectElem.textContent = "考试均已结束"; - examTimingElem.textContent = ""; - remainingTimeElem.textContent = ""; - statusElem.textContent = "状态: 空闲"; - statusElem.style.color = "#3946AF"; - } - - // Update next exams table - examTableBodyElem.innerHTML = ""; - data.examInfos.forEach(exam => { - const start = new Date(exam.start); - const end = new Date(exam.end); - let status = ""; - if (now < start) { - status = "即将开始"; - } else if (now > end) { - status = "已结束"; - } else { - status = "进行中"; - } - - const row = document.createElement("tr"); - row.className = `exam-status-${status}`; - row.innerHTML = ` - ${exam.name} - ${formatTimeWithoutSeconds(new Date(exam.start).toLocaleTimeString('zh-CN', { hour12: false }))} - ${formatTimeWithoutSeconds(new Date(exam.end).toLocaleTimeString('zh-CN', { hour12: false }))} - `; - examTableBodyElem.appendChild(row); - }); - } - - // Fullscreen functionality - fullscreenBtn.addEventListener("click", () => { - if (!document.fullscreenElement) { - document.documentElement.requestFullscreen(); - } else { - if (document.exitFullscreen) { - document.exitFullscreen(); - } - } - }); - - // Open settings modal - settingsBtn.addEventListener("click", () => { - offsetTimeInput.value = offsetTime; - roomInput.value = room; - zoomInput.value = zoomLevel; - settingsModal.style.display = "block"; - }); - - // Close settings modal - closeSettingsBtn.addEventListener("click", () => { - settingsModal.style.display = "none"; - }); - - // Save settings - saveSettingsBtn.addEventListener("click", () => { - offsetTime = parseInt(offsetTimeInput.value); - room = roomInput.value; - zoomLevel = parseFloat(zoomInput.value); - setCookie("offsetTime", offsetTime, 365); - setCookie("room", room, 365); - setCookie("zoomLevel", zoomLevel, 365); - roomElem.textContent = room; - document.body.style.zoom = zoomLevel; - settingsModal.style.display = "none"; - }); - - document.body.style.zoom = zoomLevel; - - // Utility function to set a cookie - function setCookie(name, value, days) { - const d = new Date(); - d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)); - const expires = "expires=" + d.toUTCString(); - document.cookie = name + "=" + value + ";" + expires + ";path=/"; - } - - // Utility function to get a cookie - function getCookie(name) { - const nameEQ = name + "="; - const ca = document.cookie.split(';'); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) === ' ') c = c.substring(1, c.length); - if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); - } - return null; - } - - fetchData(); -}); diff --git a/favicon.ico b/favicon.ico index 0cf6fc4..b4d95b8 100644 Binary files a/favicon.ico and b/favicon.ico differ diff --git a/time/index.html b/time/index.html index 0959d77..dfb0dc6 100644 --- a/time/index.html +++ b/time/index.html @@ -50,8 +50,6 @@ text-align: center; background: rgba(255, 255, 255, 0); padding: 30px; - border-radius: 16px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); animation: fadeIn 2s ease-in-out; }