<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>静音育林 - 教室自律系统</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--main-green: #2ecc71;
--dark-green: #27ae60;
--bg-color: #f5fbf7;
--warning-red: #e74c3c;
--dark-red: #c0392b;
--panel-bg: #ffffff;
--text-color: #333333;
--border-color: #e0e0e0;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: '微软雅黑', sans-serif;
background: var(--bg-color);
color: var(--text-color);
min-height: 100vh;
display: flex;
flex-direction: column;
padding: 20px;
}
.header {
text-align: center;
padding: 20px;
background: var(--panel-bg);
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header h1 {
font-size: 1.8rem;
margin-bottom: 10px;
color: var(--dark-green);
}
.header p {
font-size: 1rem;
color: #666;
}
.main-container {
display: flex;
gap: 20px;
flex: 1;
}
/* 左侧功能区 */
.control-panel {
background: var(--panel-bg);
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
width: 200px;
display: flex;
flex-direction: column;
gap: 10px;
}
.btn {
padding: 12px 15px;
background: var(--main-green);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
transition: 0.3s;
display: flex;
align-items: center;
gap: 8px;
justify-content: center;
}
.btn:hover {
background: var(--dark-green);
}
.btn.secondary {
background: #3498db;
}
.btn.secondary:hover {
background: #2980b9;
}
.btn.warning {
background: var(--warning-red);
}
.btn.warning:hover {
background: var(--dark-red);
}
/* 中间状态区 */
.status-panel {
background: var(--panel-bg);
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
flex: 0.7;
display: flex;
flex-direction: column;
}
.status-grid {
display: grid;
grid-template-columns: 1fr;
gap: 15px;
margin-bottom: 20px;
}
.status-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.status-label {
font-weight: bold;
display: flex;
align-items: center;
gap: 8px;
}
.status-value {
font-size: 1.2rem;
font-weight: bold;
color: var(--dark-green);
}
.status-value.warning {
color: var(--warning-red);
}
.progress-container {
margin: 10px 0;
}
.progress-bar {
height: 20px;
background: #eee;
border-radius: 10px;
overflow: hidden;
}
.progress {
height: 100%;
background: var(--main-green);
width: 0;
transition: width 0.5s;
}
.progress.warning {
background: var(--warning-red);
}
/* 小树苗生长区 */
.saplings-area {
background: #f9f9f9;
padding: 15px;
border-radius: 10px;
min-height:100px;
display: flex;
flex-wrap: wrap;
gap: 10px;
align-content: flex-start;
flex: 1;
}
/* 右侧森林区 */
.forest-panel {
background: var(--panel-bg);
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden; /* 新增:防止内容溢出 */
}
.panel-title {
margin-bottom: 15px;
text-align: center;
color: var(--dark-green);
font-size: 1.3rem;
}
.forest {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: 15px;
align-content: flex-start;
justify-content:flex-start; /* 关键修改:从左对齐(原为center) */
padding: 10px;
overflow-y: auto;
}
.tree {
display: inline-block;
transition: transform 0.3s;
}
/* 小树苗单独样式 */
.sapling {
font-size: 30px; /* 小树苗大小 */
}
/* 大树单独样式 */
.mature-tree {
font-size: 50px; /* 大树大小 */
}
/* 弹窗样式 */
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 100;
justify-content: center;
align-items: center;
}
.fa-trophy {
color: gold; /* 金色奖杯 */
text-shadow: 0 0 2px rgba(0,0,0,0.3); /* 轻微阴影 */
}
.modal-content {
background: var(--panel-bg);
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
}
/* 响应式设计 */
@media (max-width: 992px) {
.main-container {
flex-direction: column;
}
.control-panel {
width: 100%;
}
}
/* ========= 新增屏蔽模式样式 ========= */
.shield-overlay {
display: none;
position: fixed;
top: 120px; /* 与页面边距一致 */
left: 240px;
right: 20px;
bottom: 20px;
background: #f8f8f8;
z-index: 999;
justify-content: center;
align-items: center;
border-radius: 20px; /* 圆角匹配整体风格 */
box-shadow: 0 0 20px rgba(0,0,0,0.2);
margin: 0;
}
* 响应式调整 */
@media (max-width: 992px) {
.shield-overlay {
left: 20px; /* 移动端恢复全屏 */
top: 180px; /* 留出顶部空间 */
}
}
.shield-timer {
font-size: 3rem;
background: var(--main-green);
color: white;
width: 200px;
height: 200px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
}
</style>
</head>
<body>
<div class="header">
<h1>🌱嘘!小树正在安静生长🌱</h1>
<p>保持安静环境,培育属于班级的静谧森林</p>
</div>
<div class="main-container">
<!-- 左侧功能按钮区 -->
<div class="control-panel">
<button class="btn" id="settingsBtn">
<i class="fas fa-cog"></i> 参数设置
</button>
<button class="btn" id="startBtn">
<i class="fas fa-play"></i> 开始生长
</button>
<button class="btn warning" id="pauseBtn" disabled>
<i class="fas fa-pause"></i> 暂停培育
</button>
<button class="btn secondary" id="restartBtn">
<i class="fas fa-redo"></i> 重新开始
</button>
<button class="btn" id="fullscreenBtn">
<i class="fas fa-expand"></i> 全 屏
</button>
<button class="btn secondary" id="historyBtn">
<i class="fas fa-trophy"></i> 历史成就
</button>
<button class="btn secondary" id="shieldBtn">
<i class="fas fa-eye-slash"></i> 屏蔽模式
</button>
</div>
<!-- 中间状态和小树苗区 -->
<div class="status-panel">
<div class="status-grid">
<div class="status-item">
<span class="status-label">
<i class="fas fa-volume-up" ></i> 当前分贝
</span>
<span class="status-value" id="currentDB">-- dB</span>
</div>
<div class="status-item">
<span class="status-label">
<i class="far fa-clock"></i> 安静时间
</span>
<span class="status-value" id="quietTime">0秒</span>
</div>
<div class="status-item">
<span class="status-label">
<i class="fas fa-seedling" style="color: var(--main-green)"></i> 树苗数量
</span>
<span class="status-value" id="saplingCount">0</span>
</div>
</div>
<div class="progress-container">
<div class="progress-bar">
<div class="progress" id="progress"></div>
</div>
</div>
<div class="status-item" style="margin: 10px 0;">
<span class="status-label">
<i class="fas fa-info-circle"></i> 生长进度
</span>
<span class="status-value" id="statusText">准备就绪</span>
</div>
<h3 class="panel-title">小树苗生长区</h3>
<div class="saplings-area" id="saplingsArea"></div>
</div>
<!-- 右侧森林展示区 -->
<div class="forest-panel">
<h3 class="panel-title">班级静谧森林</h3>
<div class="forest" id="forest"></div>
</div>
</div>
<!-- 设置面板 -->
<div class="modal-overlay" id="settingsModal">
<div class="modal-content">
<h3>参数设置</h3>
<div class="settings-item" style="margin: 15px 0;">
<label for="volumeThreshold">音量阈值 (dB) <span style="color:#666">(0-100)</span></label>
<input type="range" id="volumeThreshold" min="0" max="100" value="20" style="width:100%">
<div style="text-align:center" id="volumeThresholdValue">20</div>
</div>
<div class="settings-item" style="margin: 15px 0;">
<label for="timeThreshold">计时时间 (秒) <span style="color:#666">(10-300)</span></label>
<input type="range" id="timeThreshold" min="10" max="300" value="30" style="width:100%">
<div style="text-align:center" id="timeThresholdValue">30</div>
</div>
<div class="settings-item" style="margin: 15px 0;">
<label>
<input type="checkbox" id="resetProgress" checked> 重置进度(清零所有树苗和大树)
</label>
</div>
<div class="settings-item" style="margin: 15px 0;">
<label for="punishVolumeThreshold">惩罚音量 (dB)</label>
<input type="range" id="punishVolumeThreshold" min="50" max="100" value="60" style="width:100%">
<div style="text-align:center" id="punishVolumeThresholdValue">60</div>
</div>
<div class="settings-item" style="margin: 15px 0;">
<label for="punishTimeThreshold">持续时间 (秒)</label>
<input type="range" id="punishTimeThreshold" min="10" max="120" value="20" style="width:100%">
<div style="text-align:center" id="punishTimeThresholdValue">10</div>
</div>
<div style="margin-top: 20px; display: flex;justify-content: flex-end; gap: 30px;">
<button class="btn secondary" id="cancelSettings">
<i class="fas fa-times"></i> 取消
</button>
<button class="btn" id="saveSettings">
<i class="fas fa-save"></i> 保存
</button>
</div>
</div>
</div>
<!-- 历史记录面板 -->
<div class="modal-overlay" id="historyModal">
<div class="modal-content">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<h3>培育历史记录</h3>
<button id="closeHistory" style="background:none; border:none; font-size:1.5rem; cursor:pointer;">
<i class="fas fa-times"></i>
</button>
</div>
<div id="historyList">
<!-- 历史记录将在这里动态生成 -->
</div>
</div>
</div>
<div class="shield-overlay" id="shieldOverlay">
<div class="shield-timer" id="shieldTimer">00:00:00</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 可选的树种emoji
const TREE_TYPES = ['🌲', '🌳', '🌴', '🌻', '🌾', '🌵', '🎄', '🎋'];
// 系统状态变量
let isMonitoring = false;
let audioContext = null;
let analyser = null;
let microphone = null;
let quietStartTime = null;
let noisyStartTime = null;
let saplingCount = 0;
let treeCount = 0;
let detectionInterval = null;
let requiredQuietTime = 30;
let punishVolumeThreshold = 60;
let punishTimeThreshold = 10;
let treeTypes = [];
let historyRecords = JSON.parse(localStorage.getItem('forestHistory')) || [];
let currentSessionStart = null;
let totalQuietTime = 0;
let lastQuietCheckTime = null;
// ========= 新增屏蔽模式变量 =========
let isShieldMode = false;
let shieldStartTime = null;
let shieldInterval = null;
const shieldBtn = document.getElementById('shieldBtn');
const shieldOverlay = document.getElementById('shieldOverlay');
const shieldTimer = document.getElementById('shieldTimer');
// DOM元素
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const restartBtn = document.getElementById('restartBtn');
const fullscreenBtn = document.getElementById('fullscreenBtn');
const settingsBtn = document.getElementById('settingsBtn');
const historyBtn = document.getElementById('historyBtn');
const currentDB = document.getElementById('currentDB');
const quietTime = document.getElementById('quietTime');
const saplingDisplay = document.getElementById('saplingCount');
const statusText = document.getElementById('statusText');
const progress = document.getElementById('progress');
const forest = document.getElementById('forest');
const saplingsArea = document.getElementById('saplingsArea');
// 弹窗相关
const settingsModal = document.getElementById('settingsModal');
const historyModal = document.getElementById('historyModal');
const closeHistory = document.getElementById('closeHistory');
const historyList = document.getElementById('historyList');
// 设置相关元素
const volumeThreshold = document.getElementById('volumeThreshold');
const volumeThresholdValue = document.getElementById('volumeThresholdValue');
const timeThreshold = document.getElementById('timeThreshold');
const timeThresholdValue = document.getElementById('timeThresholdValue');
const punishVolumeThresholdInput = document.getElementById('punishVolumeThreshold');
const punishVolumeThresholdValue = document.getElementById('punishVolumeThresholdValue');
const punishTimeThresholdInput = document.getElementById('punishTimeThreshold');
const punishTimeThresholdValue = document.getElementById('punishTimeThresholdValue');
const resetProgress = document.getElementById('resetProgress');
const saveSettings = document.getElementById('saveSettings');
const cancelSettings = document.getElementById('cancelSettings');
// 初始化
loadSettingsFromLocal(); // 加载保存的设置
renderHistory();
updateButtonStates();
// 获取随机树种
function getRandomTree() {
return TREE_TYPES[Math.floor(Math.random() * TREE_TYPES.length)];
}
// 更新按钮状态
function updateButtonStates() {
startBtn.disabled = isMonitoring;
pauseBtn.disabled = !isMonitoring;
}
// 开始培育
startBtn.addEventListener('click', startMonitoring);
// 暂停培育
pauseBtn.addEventListener('click', stopMonitoring);
// ========= 新增按钮监听 =========
shieldBtn.addEventListener('click', toggleShieldMode);
// 重新开始
restartBtn.addEventListener('click', function() {
if (confirm('确定要重新开始吗?这将重置所有进度。')) {
resetSystem();
}
});
// 全屏模式
fullscreenBtn.addEventListener('click', toggleFullscreen);
// 设置按钮
settingsBtn.addEventListener('click', function() {
settingsModal.style.display = 'flex';
});
// 历史记录按钮
historyBtn.addEventListener('click', function() {
renderHistory();
historyModal.style.display = 'flex';
});
// 关闭历史记录
closeHistory.addEventListener('click', function() {
historyModal.style.display = 'none';
});
// 音量阈值调整
volumeThreshold.addEventListener('input', function(e) {
volumeThresholdValue.textContent = e.target.value;
});
// 计时时间调整
timeThreshold.addEventListener('input', function(e) {
timeThresholdValue.textContent = e.target.value;
});
// 惩罚音量阈值调整
punishVolumeThresholdInput.addEventListener('input', function(e) {
punishVolumeThresholdValue.textContent = e.target.value;
});
// 惩罚计时时间调整
punishTimeThresholdInput.addEventListener('input', function(e) {
punishTimeThresholdValue.textContent = e.target.value;
});
// 保存设置
saveSettings.addEventListener('click', saveSettingsHandler);
// 取消设置
cancelSettings.addEventListener('click', function() {
settingsModal.style.display = 'none';
});
// 开始监测
async function startMonitoring() {
if (!isMonitoring) {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
audioContext = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioContext.createAnalyser();
microphone = audioContext.createMediaStreamSource(stream);
microphone.connect(analyser);
analyser.fftSize = 2048;
isMonitoring = true;
currentSessionStart = new Date();
totalQuietTime = 0;
lastQuietCheckTime = null;
updateButtonStates();
startDetection();
} catch (error) {
alert('需要允许麦克风权限才能使用本功能');
console.error('麦克风访问错误:', error);
stopMonitoring();
}
}
}
// 停止监测
function stopMonitoring() {
if (isMonitoring) {
clearInterval(detectionInterval);
if (microphone) {
microphone.disconnect();
}
if (audioContext) {
audioContext.close();
audioContext = null;
}
isMonitoring = false;
updateButtonStates();
if (totalQuietTime > 0) {
addHistoryRecord();
}
}
}
// 开始检测
function startDetection() {
detectionInterval = setInterval(function() {
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
let sum = 0;
for (let i = 0; i < bufferLength; i++) {
sum += dataArray[i];
}
const average = sum / bufferLength;
const db = 20 * Math.log10(average / 255 * 1000);
const displayDB = Math.max(Math.round(db), 0);
currentDB.textContent = `${displayDB} dB`;
const rewardThreshold = parseInt(volumeThreshold.value);
const punishThreshold = parseInt(punishVolumeThresholdInput.value);
if (displayDB >= punishThreshold) {
currentDB.classList.add('warning');
progress.classList.add('warning');
statusText.classList.add('warning');
statusText.textContent = '环境嘈杂,请保持安静';
if (!noisyStartTime) {
noisyStartTime = Date.now();
progress.style.width = '0%';
}
const noisyElapsed = Math.floor((Date.now() - noisyStartTime) / 1000);
const progressWidth = Math.min((noisyElapsed / punishTimeThreshold) * 100, 100);
progress.style.width = `${progressWidth}%`;
if (noisyElapsed >= punishTimeThreshold) {
removeSapling();
noisyStartTime = Date.now();
progress.style.width = '0%';
}
quietStartTime = null;
lastQuietCheckTime = null;
}
else if (displayDB < rewardThreshold) {
currentDB.classList.remove('warning');
progress.classList.remove('warning');
statusText.classList.remove('warning');
statusText.textContent = '环境达标,正在培育';
const now = Date.now();
if (!quietStartTime) {
quietStartTime = now;
progress.style.width = '0%';
}
if (lastQuietCheckTime) {
totalQuietTime += (now - lastQuietCheckTime) / 1000;
}
lastQuietCheckTime = now;
const quietElapsed = Math.floor((now - quietStartTime) / 1000);
quietTime.textContent = `${Math.floor(totalQuietTime)}秒`;
const progressWidth = Math.min((quietElapsed / requiredQuietTime) * 100, 100);
progress.style.width = `${progressWidth}%`;
if (quietElapsed >= requiredQuietTime) {
addSapling();
quietStartTime = now;
progress.style.width = '0%';
}
noisyStartTime = null;
}
else {
currentDB.classList.remove('warning');
progress.classList.remove('warning');
statusText.classList.remove('warning');
statusText.textContent = '环境一般,请保持安静';
quietStartTime = null;
noisyStartTime = null;
lastQuietCheckTime = null;
progress.style.width = '0%';
}
}, 500);
}
// 添加小树苗
function addSapling() {
saplingCount++;
saplingDisplay.textContent = `${saplingCount}/10`;
const saplingIcon = document.createElement('span');
saplingIcon.textContent = '🌱';
saplingIcon.className = 'tree sapling';
saplingsArea.appendChild(saplingIcon);
if (saplingCount >= 10) {
saplingsArea.innerHTML = '';
const newTreeType = getRandomTree();
treeTypes.push(newTreeType);
treeCount++;
saplingCount = 0;
saplingDisplay.textContent = '0/10';
renderForest();
}
}
// 移除小树苗
function removeSapling() {
if (saplingCount > 0) {
saplingCount--;
saplingDisplay.textContent = `${saplingCount}/10`;
const saplings = saplingsArea.querySelectorAll('.tree');
if (saplings.length > 0) {
saplingsArea.removeChild(saplings[saplings.length-1]);
}
} else if (treeCount > 0) {
treeCount--;
treeTypes.pop();
saplingCount = 9;
saplingDisplay.textContent = '9/10';
renderForest();
for (let i = 0; i < 9; i++) {
const saplingIcon = document.createElement('span');
saplingIcon.textContent = '🌱';
saplingIcon.className = 'tree';
saplingsArea.appendChild(saplingIcon);
}
}
}
// 渲染森林
function renderForest() {
forest.innerHTML = '';
// 渲染所有大树
treeTypes.forEach(treeType => {
const treeIcon = document.createElement('span');
treeIcon.textContent = treeType;
treeIcon.className = 'tree mature-tree'; // 添加大树类
forest.appendChild(treeIcon);
});
// 渲染当前的小树苗
for (let i = 0; i < saplingCount; i++) {
const saplingIcon = document.createElement('span');
saplingIcon.textContent = '🌱';
saplingIcon.className = 'tree sapling'; // 添加小树苗类
forest.appendChild(saplingIcon);
}
}
// 重置系统
function resetSystem() {
stopMonitoring();
saplingCount = 0;
treeCount = 0;
treeTypes = [];
saplingDisplay.textContent = saplingCount;
quietTime.textContent = '0秒';
currentDB.textContent = '-- dB';
statusText.textContent = '准备就绪';
progress.style.width = '0%';
forest.innerHTML = '';
saplingsArea.innerHTML = '';
currentSessionStart = null;
totalQuietTime = 0;
lastQuietCheckTime = null;
}
// 保存设置
function saveSettingsHandler() {
requiredQuietTime = parseInt(timeThreshold.value);
punishVolumeThreshold = parseInt(punishVolumeThresholdInput.value);
punishTimeThreshold = parseInt(punishTimeThresholdInput.value);
if (resetProgress.checked) {
resetSystem();
}
settingsModal.style.display = 'none';
}
// ========= 新增屏蔽模式函数 =========
function toggleShieldMode() {
isShieldMode = !isShieldMode;
if (isShieldMode) {
// 进入屏蔽模式
shieldOverlay.style.display = 'flex';
shieldBtn.innerHTML = '<i class="fas fa-eye"></i> 显示森林';
// 立即显示当前时间并开始计时
updateShieldTimer();
clearInterval(shieldInterval);
shieldInterval = setInterval(updateShieldTimer, 1000);
} else {
// 退出屏蔽模式
shieldOverlay.style.display = 'none';
shieldBtn.innerHTML = '<i class="fas fa-eye-slash"></i> 屏蔽模式';
clearInterval(shieldInterval);
}
}
function updateShieldTimer() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
shieldTimer.textContent = `${hours}:${minutes}:${seconds}`;
}
// 切换全屏
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(err => {
alert(`全屏错误: ${err.message}`);
});
document.body.classList.add('fullscreen');
fullscreenBtn.innerHTML = '<i class="fas fa-compress"></i> 退出全屏';
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
document.body.classList.remove('fullscreen');
fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i> 全屏';
}
}
}
// 保存设置到本地存储
function saveSettingsToLocal() {
const settings = {
volumeThreshold: volumeThreshold.value,
timeThreshold: timeThreshold.value,
punishVolumeThreshold: punishVolumeThresholdInput.value,
punishTimeThreshold: punishTimeThresholdInput.value
};
localStorage.setItem('forestSettings', JSON.stringify(settings));
}
// 从本地存储加载设置
function loadSettingsFromLocal() {
const savedSettings = localStorage.getItem('forestSettings');
if (savedSettings) {
const settings = JSON.parse(savedSettings);
// 更新滑块和显示值
volumeThreshold.value = settings.volumeThreshold;
volumeThresholdValue.textContent = settings.volumeThreshold;
timeThreshold.value = settings.timeThreshold;
timeThresholdValue.textContent = settings.timeThreshold;
punishVolumeThresholdInput.value = settings.punishVolumeThreshold;
punishVolumeThresholdValue.textContent = settings.punishVolumeThreshold;
punishTimeThresholdInput.value = settings.punishTimeThreshold;
punishTimeThresholdValue.textContent = settings.punishTimeThreshold;
// 更新全局变量
requiredQuietTime = parseInt(settings.timeThreshold);
punishVolumeThreshold = parseInt(settings.punishVolumeThreshold);
punishTimeThreshold = parseInt(settings.punishTimeThreshold);
}
}
// 修改保存设置的函数
function saveSettingsHandler() {
requiredQuietTime = parseInt(timeThreshold.value);
punishVolumeThreshold = parseInt(punishVolumeThresholdInput.value);
punishTimeThreshold = parseInt(punishTimeThresholdInput.value);
saveSettingsToLocal(); // 保存到本地存储
if (resetProgress.checked) {
resetSystem();
}
settingsModal.style.display = 'none';
}
// 添加历史记录
function addHistoryRecord() {
const record = {
date: currentSessionStart,
quietTime: Math.floor(totalQuietTime),
saplingCount: saplingCount,
treeCount: treeCount
};
historyRecords.unshift(record);
localStorage.setItem('forestHistory', JSON.stringify(historyRecords));
}
// 删除历史记录
function deleteHistoryRecord(index) {
historyRecords.splice(index, 1);
localStorage.setItem('forestHistory', JSON.stringify(historyRecords));
renderHistory();
}
// 渲染历史记录
function renderHistory() {
historyList.innerHTML = '';
if (historyRecords.length === 0) {
historyList.innerHTML = '<p style="text-align: center; color: #666;">暂无历史记录</p>';
return;
}
historyRecords.forEach((record, index) => {
const item = document.createElement('div');
item.style.display = 'flex';
item.style.justifyContent = 'space-between';
item.style.alignItems = 'center';
item.style.padding = '10px';
item.style.marginBottom = '10px';
item.style.background = '#f9f9f9';
item.style.borderRadius = '8px';
const dateStr = new Date(record.date).toLocaleString();
saplingDisplay.textContent = '0/10';
item.innerHTML = `
<div style="display:flex; gap:15px; flex-wrap:wrap;">
<span><i class="far fa-calendar-alt"></i> ${dateStr}</span>
<span><i class="far fa-clock"></i> ${record.quietTime}秒</span>
<span><i class="fas fa-seedling"></i> ${record.saplingCount}</span>
<span><i class="fas fa-tree"></i> ${record.treeCount}</span>
</div>
<div>
<button onclick="deleteHistoryRecord(${index})" style="background:var(--warning-red); color:white; border:none; border-radius:5px; padding:5px 10px; cursor:pointer;">
<i class="fas fa-trash"></i>
</button>
</div>
`;
historyList.appendChild(item);
});
}
// 使deleteHistoryRecord在全局可用
window.deleteHistoryRecord = deleteHistoryRecord;
// 监听全屏变化
document.addEventListener('fullscreenchange', function() {
if (!document.fullscreenElement) {
document.body.classList.remove('fullscreen');
fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i> 全屏';
}
});
// 点击模态框外部关闭
window.addEventListener('click', function(event) {
if (event.target === settingsModal) {
settingsModal.style.display = 'none';
}
if (event.target === historyModal) {
historyModal.style.display = 'none';
}
});
});
</script>
</body>
</html>
index.html
style.css
index.js
index.html