<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Out-前缀学习系统</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@keyframes celebrate {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
@keyframes confetti {
0% { opacity: 1; transform: translateY(0) rotate(0deg); }
100% { opacity: 0; transform: translateY(-100px) rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.celebrate { animation: celebrate 0.6s ease-in-out; }
.shake { animation: shake 0.5s ease-in-out; }
.confetti { animation: confetti 2s ease-out forwards; }
.fade-in { animation: fadeIn 0.5s ease-out; }
.word-card {
transition: all 0.3s ease;
cursor: pointer;
}
.word-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
}
.category-zone {
min-height: 150px;
border: 2px dashed #e5e7eb;
transition: all 0.3s ease;
}
.category-zone.drag-over {
border-color: #3b82f6;
background-color: #eff6ff;
}
.knowledge-card {
transition: transform 0.3s ease;
}
.knowledge-card:hover {
transform: translateY(-5px);
}
.login-container {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-50 to-indigo-50 min-h-screen">
<!-- 登录界面 -->
<div id="login-section" class="login-container min-h-screen flex items-center justify-center">
<div class="bg-white rounded-2xl shadow-2xl p-8 max-w-md w-full mx-4 fade-in">
<div class="text-center mb-8">
<div class="text-4xl mb-4">📚</div>
<h1 class="text-3xl font-bold text-gray-800 mb-2">Out-前缀学习系统</h1>
<p class="text-gray-600">请输入您的学号后6位数字登录</p>
</div>
<form id="login-form" class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">学号后6位</label>
<input
type="text"
id="student-id"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
placeholder="请输入6位数字"
maxlength="6"
pattern="[0-9]{6}"
required
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">姓名(可选)</label>
<input
type="text"
id="student-name"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
placeholder="请输入您的姓名"
>
</div>
<button
type="submit"
class="w-full bg-gradient-to-r from-blue-500 to-purple-500 text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-all duration-300 transform hover:scale-105"
>
🚀 开始学习
</button>
</form>
<div class="mt-6 text-center text-sm text-gray-500">
<p>💡 提示:管理员请使用学号 000000</p>
</div>
</div>
</div>
<!-- 主界面容器 -->
<div id="main-container" class="hidden">
<div class="container mx-auto p-6">
<!-- 顶部用户信息栏 -->
<div class="bg-white rounded-lg shadow-sm p-4 mb-6 flex justify-between items-center">
<div class="flex items-center space-x-4">
<div class="text-2xl">👋</div>
<div>
<div class="font-semibold text-gray-800">欢迎,<span id="current-user-name">学生</span></div>
<div class="text-sm text-gray-600">学号:<span id="current-user-id"></span></div>
</div>
</div>
<div class="flex items-center space-x-4">
<div id="user-stats" class="text-sm text-gray-600"></div>
<button id="logout-btn" class="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600 transition-colors">
退出登录
</button>
</div>
</div>
<!-- 标题区 -->
<div class="text-center mb-8">
<h1 class="text-4xl font-bold text-gray-800 mb-4">Out-前缀完整学习系统</h1>
<p class="text-lg text-gray-600">先学习知识点,再进行分类练习</p>
</div>
<!-- 导航按钮 -->
<div class="flex justify-center mb-8">
<button id="knowledge-btn" class="bg-blue-500 text-white px-6 py-3 rounded-l-lg font-semibold transition-all duration-300 hover:bg-blue-600">
📚 知识讲解
</button>
<button id="game-btn" class="bg-gray-400 text-white px-6 py-3 font-semibold transition-all duration-300 hover:bg-green-600">
🎮 分类游戏
</button>
<button id="admin-btn" class="bg-gray-400 text-white px-6 py-3 rounded-r-lg font-semibold transition-all duration-300 hover:bg-purple-600 hidden">
📊 管理界面
</button>
</div>
<!-- 知识讲解区域 -->
<div id="knowledge-section" class="space-y-8">
<!-- 知识点概述 -->
<div class="bg-white rounded-xl shadow-lg p-8">
<h2 class="text-3xl font-bold text-center text-gray-800 mb-6">Out-前缀的两大核心含义</h2>
<div class="text-center text-gray-600 text-lg mb-8">
out-作为英语前缀,主要表达两种核心概念,理解这两点是掌握out-词汇的关键!
</div>
<!-- 两大类别 -->
<div class="grid md:grid-cols-2 gap-8">
<!-- 第一类:向外/外部 -->
<div class="knowledge-card bg-gradient-to-br from-green-50 to-emerald-50 rounded-xl p-6 border-2 border-green-200">
<div class="text-center mb-4">
<div class="text-4xl mb-2">🌟</div>
<h3 class="text-2xl font-bold text-green-700">向外/外部</h3>
<p class="text-green-600 text-sm">Outward/External Direction</p>
</div>
<div class="space-y-4">
<div class="bg-white rounded-lg p-4 shadow-sm">
<h4 class="font-semibold text-gray-800 mb-2">🎯 核心概念</h4>
<p class="text-gray-600 text-sm">表示空间上的"向外、在外面、从内到外"的方向或位置概念</p>
</div>
<div class="bg-white rounded-lg p-4 shadow-sm">
<h4 class="font-semibold text-gray-800 mb-2">📝 典型例词</h4>
<div class="space-y-2 text-sm">
<div><span class="font-semibold text-green-700">outbreak</span> = break向外 → 爆发</div>
<div><span class="font-semibold text-green-700">outlet</span> = let向外 → 出口</div>
<div><span class="font-semibold text-green-700">output</span> = put向外 → 输出</div>
</div>
</div>
</div>
</div>
<!-- 第二类:超越/胜过 -->
<div class="knowledge-card bg-gradient-to-br from-purple-50 to-violet-50 rounded-xl p-6 border-2 border-purple-200">
<div class="text-center mb-4">
<div class="text-4xl mb-2">🏆</div>
<h3 class="text-2xl font-bold text-purple-700">超越/胜过</h3>
<p class="text-purple-600 text-sm">Surpass/Exceed</p>
</div>
<div class="space-y-4">
<div class="bg-white rounded-lg p-4 shadow-sm">
<h4 class="font-semibold text-gray-800 mb-2">🎯 核心概念</h4>
<p class="text-gray-600 text-sm">表示比较上的"超过、胜过、优于",强调在某方面比别人更强</p>
</div>
<div class="bg-white rounded-lg p-4 shadow-sm">
<h4 class="font-semibold text-gray-800 mb-2">📝 典型例词</h4>
<div class="space-y-2 text-sm">
<div><span class="font-semibold text-purple-700">outrun</span> = run得更好 → 跑得更快</div>
<div><span class="font-semibold text-purple-700">outwit</span> = wit更胜一筹 → 智取</div>
<div><span class="font-semibold text-purple-700">outweigh</span> = weigh更重 → 更重要</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 详细举例说明 -->
<div class="bg-white rounded-xl shadow-lg p-8">
<h3 class="text-2xl font-bold text-center text-gray-800 mb-6">详细举例分析</h3>
<div class="grid md:grid-cols-2 gap-8">
<!-- 向外/外部类例子 -->
<div>
<h4 class="text-xl font-bold text-green-700 mb-4 flex items-center">
<span class="mr-2">🌟</span> 向外/外部类详解
</h4>
<div class="space-y-4">
<div class="bg-green-50 rounded-lg p-4 border-l-4 border-green-400">
<div class="font-semibold text-green-800">outbreak /ˈaʊtbreɪk/</div>
<div class="text-sm text-gray-600 mt-1">
<strong>构成:</strong>out- (向外) + break (破裂)<br>
<strong>含义:</strong>从内部向外突然爆发<br>
<strong>例句:</strong>The outbreak of war shocked everyone.
</div>
</div>
<div class="bg-green-50 rounded-lg p-4 border-l-4 border-green-400">
<div class="font-semibold text-green-800">outlet /ˈaʊtlet/</div>
<div class="text-sm text-gray-600 mt-1">
<strong>构成:</strong>out- (向外) + let (让,允许)<br>
<strong>含义:</strong>让东西向外流出的地方<br>
<strong>例句:</strong>This store is a famous outlet for designer clothes.
</div>
</div>
<div class="bg-green-50 rounded-lg p-4 border-l-4 border-green-400">
<div class="font-semibold text-green-800">outcry /ˈaʊtkraɪ/</div>
<div class="text-sm text-gray-600 mt-1">
<strong>构成:</strong>out- (向外) + cry (呼喊)<br>
<strong>含义:</strong>向外大声呼喊、抗议<br>
<strong>例句:</strong>There was a public outcry against the decision.
</div>
</div>
</div>
</div>
<!-- 超越/胜过类例子 -->
<div>
<h4 class="text-xl font-bold text-purple-700 mb-4 flex items-center">
<span class="mr-2">🏆</span> 超越/胜过类详解
</h4>
<div class="space-y-4">
<div class="bg-purple-50 rounded-lg p-4 border-l-4 border-purple-400">
<div class="font-semibold text-purple-800">outrun /ˌaʊtˈrʌn/</div>
<div class="text-sm text-gray-600 mt-1">
<strong>构成:</strong>out- (胜过) + run (跑)<br>
<strong>含义:</strong>跑得比...更快<br>
<strong>例句:</strong>The athlete easily outran all his competitors.
</div>
</div>
<div class="bg-purple-50 rounded-lg p-4 border-l-4 border-purple-400">
<div class="font-semibold text-purple-800">outwit /ˌaʊtˈwɪt/</div>
<div class="text-sm text-gray-600 mt-1">
<strong>构成:</strong>out- (胜过) + wit (智慧)<br>
<strong>含义:</strong>在智慧上胜过对手<br>
<strong>例句:</strong>The detective outwitted the criminal with clever planning.
</div>
</div>
<div class="bg-purple-50 rounded-lg p-4 border-l-4 border-purple-400">
<div class="font-semibold text-purple-800">outweigh /ˌaʊtˈweɪ/</div>
<div class="text-sm text-gray-600 mt-1">
<strong>构成:</strong>out- (超过) + weigh (称重)<br>
<strong>含义:</strong>在重量或重要性上超过<br>
<strong>例句:</strong>The benefits outweigh the risks in this case.
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 开始游戏按钮 -->
<div class="text-center">
<button id="start-game-btn" class="bg-gradient-to-r from-green-500 to-purple-500 text-white px-12 py-4 rounded-full text-xl font-bold shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105">
🎯 现在开始分类练习!
</button>
</div>
</div>
<!-- 游戏区域 -->
<div id="game-section" class="hidden space-y-8">
<!-- 游戏介绍 -->
<div class="bg-white rounded-xl shadow-lg p-6 text-center">
<h2 class="text-2xl font-bold text-gray-800 mb-4">🎮 分类练习游戏</h2>
<p class="text-gray-600 mb-4">根据刚才学到的知识,将下面的20个单词拖拽到正确的分类中!</p>
<div class="flex justify-center space-x-8 text-sm">
<span class="text-blue-600 font-semibold">得分: <span id="score">0</span></span>
<span class="text-purple-600 font-semibold">进度: <span id="progress">0/20</span></span>
</div>
</div>
<!-- 分类区域 -->
<div class="grid md:grid-cols-2 gap-8 mb-8">
<!-- 向外/外部 -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="text-center mb-4">
<div class="text-3xl mb-2">🌟</div>
<h3 class="text-xl font-bold text-green-600">向外/外部</h3>
<p class="text-sm text-gray-600">空间方向概念:向外、在外面、从内到外</p>
</div>
<div id="category-outer" class="category-zone rounded-lg p-4 flex flex-wrap gap-2 justify-center"></div>
</div>
<!-- 超越/胜过 -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="text-center mb-4">
<div class="text-3xl mb-2">🏆</div>
<h3 class="text-xl font-bold text-purple-600">超越/胜过</h3>
<p class="text-sm text-gray-600">比较概念:超过、胜过、优于他人</p>
</div>
<div id="category-surpass" class="category-zone rounded-lg p-4 flex flex-wrap gap-2 justify-center"></div>
</div>
</div>
<!-- 单词卡片区 -->
<div class="bg-white rounded-xl shadow-lg p-6">
<h3 class="text-xl font-bold text-gray-700 mb-4 text-center">待分类单词</h3>
<div id="word-pool" class="flex flex-wrap gap-3 justify-center min-h-[100px]"></div>
</div>
<!-- 控制按钮 -->
<div class="text-center space-x-4">
<button id="reset-btn" class="bg-blue-500 text-white px-6 py-3 rounded-full font-semibold hover:bg-blue-600 transition-colors">
🔄 重新开始
</button>
<button id="back-to-knowledge" class="bg-gray-500 text-white px-6 py-3 rounded-full font-semibold hover:bg-gray-600 transition-colors">
📚 回到知识讲解
</button>
</div>
<!-- 完成提示 -->
<div id="completion-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-2xl p-8 text-center max-w-md mx-4">
<div class="text-6xl mb-4">🎉</div>
<h2 class="text-2xl font-bold text-gray-800 mb-2">恭喜完成!</h2>
<p class="text-gray-600 mb-4">你已经成功掌握了out-前缀的核心含义!</p>
<p class="text-lg font-semibold text-blue-600 mb-4">最终得分: <span id="final-score"></span></p>
<div class="space-x-4">
<button id="play-again" class="bg-green-500 text-white px-6 py-2 rounded-lg hover:bg-green-600 transition-colors">
再玩一次
</button>
<button id="review-knowledge" class="bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600 transition-colors">
复习知识点
</button>
</div>
</div>
</div>
</div>
<!-- 管理界面 -->
<div id="admin-section" class="hidden space-y-8">
<!-- 管理员标题 -->
<div class="bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-xl shadow-lg p-8 text-center">
<h2 class="text-3xl font-bold mb-2">📊 管理员界面</h2>
<p class="text-lg opacity-90">学生游戏记录统计与管理</p>
</div>
<!-- 统计卡片 -->
<div class="grid md:grid-cols-4 gap-6">
<div class="bg-white rounded-xl shadow-lg p-6 text-center">
<div class="text-3xl mb-2">👥</div>
<div class="text-2xl font-bold text-gray-800" id="total-students">0</div>
<div class="text-sm text-gray-600">总学生数</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 text-center">
<div class="text-3xl mb-2">🎮</div>
<div class="text-2xl font-bold text-gray-800" id="total-games">0</div>
<div class="text-sm text-gray-600">总游戏次数</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 text-center">
<div class="text-3xl mb-2">⭐</div>
<div class="text-2xl font-bold text-gray-800" id="avg-score">0</div>
<div class="text-sm text-gray-600">平均分</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 text-center">
<div class="text-3xl mb-2">🏆</div>
<div class="text-2xl font-bold text-gray-800" id="highest-score">0</div>
<div class="text-sm text-gray-600">最高分</div>
</div>
</div>
<!-- 学生记录表格 -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex justify-between items-center mb-6">
<h3 class="text-xl font-bold text-gray-800">学生游戏记录</h3>
<div class="flex space-x-4">
<button id="export-data" class="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 transition-colors">
📊 导出数据
</button>
<button id="clear-data" class="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600 transition-colors">
🗑️ 清空数据
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left font-semibold text-gray-700">学号</th>
<th class="px-4 py-3 text-left font-semibold text-gray-700">姓名</th>
<th class="px-4 py-3 text-center font-semibold text-gray-700">游戏次数</th>
<th class="px-4 py-3 text-center font-semibold text-gray-700">最高分</th>
<th class="px-4 py-3 text-center font-semibold text-gray-700">平均分</th>
<th class="px-4 py-3 text-center font-semibold text-gray-700">总时长(分钟)</th>
<th class="px-4 py-3 text-center font-semibold text-gray-700">最后游戏</th>
<th class="px-4 py-3 text-center font-semibold text-gray-700">操作</th>
</tr>
</thead>
<tbody id="student-records" class="divide-y divide-gray-200">
<!-- 数据将通过JavaScript动态填充 -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- 粒子特效容器 -->
<div id="particles" class="fixed inset-0 pointer-events-none z-40"></div>
<script>
// 重新分类的词汇数据
const words = [
// 向外/外部类 (8个)
{word: "outbreak", meaning: "爆发", category: "outer"},
{word: "outlet", meaning: "出口", category: "outer"},
{word: "outcry", meaning: "呐喊", category: "outer"},
{word: "outlay", meaning: "支出", category: "outer"},
{word: "outset", meaning: "开端", category: "outer"},
{word: "outlook", meaning: "展望", category: "outer"},
{word: "output", meaning: "输出", category: "outer"},
{word: "outlaw", meaning: "罪犯", category: "outer"},
// 超越/胜过类 (12个)
{word: "outdistance", meaning: "远超", category: "surpass"},
{word: "outweigh", meaning: "更重要", category: "surpass"},
{word: "outlast", meaning: "持续更久", category: "surpass"},
{word: "outgrow", meaning: "长得过大", category: "surpass"},
{word: "outpace", meaning: "超越速度", category: "surpass"},
{word: "outnumber", meaning: "数量胜出", category: "surpass"},
{word: "outwit", meaning: "智取", category: "surpass"},
{word: "outrun", meaning: "跑得更快", category: "surpass"},
{word: "outdo", meaning: "做得更好", category: "surpass"},
{word: "outbid", meaning: "出价更高", category: "surpass"},
{word: "outmaneuver", meaning: "策略胜出", category: "surpass"},
{word: "outperform", meaning: "表现更好", category: "surpass"},
{word: "outplay", meaning: "技术更好", category: "surpass"},
{word: "outsmart", meaning: "更聪明", category: "surpass"}
];
// 全局变量
let currentUser = null;
let score = 0;
let completed = 0;
let currentWords = [...words];
let gameStartTime = null;
// 数据管理类
class DataManager {
static getStudentData() {
const data = localStorage.getItem('outPrefixStudents');
return data ? JSON.parse(data) : {};
}
static saveStudentData(studentData) {
localStorage.setItem('outPrefixStudents', JSON.stringify(studentData));
}
static updateStudentRecord(studentId, gameData) {
const allData = this.getStudentData();
if (!allData[studentId]) {
allData[studentId] = {
id: studentId,
name: gameData.name || '未知学生',
games: [],
totalGames: 0,
highestScore: 0,
totalScore: 0,
totalTime: 0,
lastPlayed: new Date().toISOString()
};
}
const student = allData[studentId];
student.games.push({
score: gameData.score,
timeSpent: gameData.timeSpent,
completedAt: new Date().toISOString()
});
student.totalGames++;
student.totalScore += gameData.score;
student.highestScore = Math.max(student.highestScore, gameData.score);
student.totalTime += gameData.timeSpent;
student.lastPlayed = new Date().toISOString();
if (gameData.name) {
student.name = gameData.name;
}
this.saveStudentData(allData);
}
static getStudentStats(studentId) {
const allData = this.getStudentData();
const student = allData[studentId];
if (!student) return null;
return {
totalGames: student.totalGames,
highestScore: student.highestScore,
averageScore: Math.round(student.totalScore / student.totalGames),
totalTime: Math.round(student.totalTime / 60), // 转换为分钟
lastPlayed: student.lastPlayed
};
}
}
// 登录处理
document.getElementById('login-form').addEventListener('submit', function(e) {
e.preventDefault();
const studentId = document.getElementById('student-id').value;
const studentName = document.getElementById('student-name').value;
if (studentId.length !== 6 || !/^\d{6}$/.test(studentId)) {
alert('请输入正确的6位数字学号!');
return;
}
currentUser = {
id: studentId,
name: studentName || '学生',
isAdmin: studentId === '000000'
};
showMainInterface();
});
// 显示主界面
function showMainInterface() {
document.getElementById('login-section').classList.add('hidden');
document.getElementById('main-container').classList.remove('hidden');
// 更新用户信息
document.getElementById('current-user-name').textContent = currentUser.name;
document.getElementById('current-user-id').textContent = currentUser.id;
// 显示用户统计
updateUserStats();
// 如果是管理员,显示管理按钮
if (currentUser.isAdmin) {
document.getElementById('admin-btn').classList.remove('hidden');
}
showKnowledge();
}
// 更新用户统计
function updateUserStats() {
if (currentUser.isAdmin) {
document.getElementById('user-stats').innerHTML = '管理员账户';
} else {
const stats = DataManager.getStudentStats(currentUser.id);
if (stats) {
document.getElementById('user-stats').innerHTML =
`游戏次数: ${stats.totalGames} | 最高分: ${stats.highestScore}`;
} else {
document.getElementById('user-stats').innerHTML = '首次使用';
}
}
}
// 页面切换函数
function showKnowledge() {
hideAllSections();
document.getElementById('knowledge-section').classList.remove('hidden');
updateNavButtons('knowledge');
}
function showGame() {
hideAllSections();
document.getElementById('game-section').classList.remove('hidden');
updateNavButtons('game');
initGame();
}
function showAdmin() {
if (!currentUser.isAdmin) {
alert('无权限访问管理界面!');
return;
}
hideAllSections();
document.getElementById('admin-section').classList.remove('hidden');
updateNavButtons('admin');
loadAdminData();
}
function hideAllSections() {
document.getElementById('knowledge-section').classList.add('hidden');
document.getElementById('game-section').classList.add('hidden');
document.getElementById('admin-section').classList.add('hidden');
}
function updateNavButtons(active) {
const buttons = {
'knowledge': document.getElementById('knowledge-btn'),
'game': document.getElementById('game-btn'),
'admin': document.getElementById('admin-btn')
};
Object.entries(buttons).forEach(([key, btn]) => {
if (key === active) {
btn.classList.remove('bg-gray-400');
if (key === 'knowledge') btn.classList.add('bg-blue-500');
else if (key === 'game') btn.classList.add('bg-green-500');
else if (key === 'admin') btn.classList.add('bg-purple-500');
} else {
btn.classList.add('bg-gray-400');
btn.classList.remove('bg-blue-500', 'bg-green-500', 'bg-purple-500');
}
});
}
// 管理界面数据加载
function loadAdminData() {
const allData = DataManager.getStudentData();
const students = Object.values(allData);
// 更新统计卡片
document.getElementById('total-students').textContent = students.length;
const totalGames = students.reduce((sum, s) => sum + s.totalGames, 0);
document.getElementById('total-games').textContent = totalGames;
const totalScore = students.reduce((sum, s) => sum + s.totalScore, 0);
const avgScore = totalGames > 0 ? Math.round(totalScore / totalGames) : 0;
document.getElementById('avg-score').textContent = avgScore;
const highestScore = Math.max(...students.map(s => s.highestScore), 0);
document.getElementById('highest-score').textContent = highestScore;
// 更新学生记录表格
const tbody = document.getElementById('student-records');
tbody.innerHTML = '';
students.sort((a, b) => new Date(b.lastPlayed) - new Date(a.lastPlayed))
.forEach(student => {
const averageScore = student.totalGames > 0 ? Math.round(student.totalScore / student.totalGames) : 0;
const totalTimeMinutes = Math.round(student.totalTime / 60);
const lastPlayed = new Date(student.lastPlayed).toLocaleString('zh-CN');
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
row.innerHTML = `
<td class="px-4 py-3 font-medium">${student.id}</td>
<td class="px-4 py-3">${student.name}</td>
<td class="px-4 py-3 text-center">${student.totalGames}</td>
<td class="px-4 py-3 text-center font-semibold text-blue-600">${student.highestScore}</td>
<td class="px-4 py-3 text-center">${averageScore}</td>
<td class="px-4 py-3 text-center">${totalTimeMinutes}</td>
<td class="px-4 py-3 text-center text-sm text-gray-600">${lastPlayed}</td>
<td class="px-4 py-3 text-center">
<button onclick="deleteStudent('${student.id}')" class="text-red-600 hover:text-red-800 text-sm">
删除
</button>
</td>
`;
tbody.appendChild(row);
});
}
// 删除学生记录
function deleteStudent(studentId) {
if (confirm(`确定要删除学号 ${studentId} 的所有记录吗?`)) {
const allData = DataManager.getStudentData();
delete allData[studentId];
DataManager.saveStudentData(allData);
loadAdminData();
}
}
// 导出数据
function exportData() {
const allData = DataManager.getStudentData();
const csvContent = "data:text/csv;charset=utf-8,"
+ "学号,姓名,游戏次数,最高分,平均分,总时长(分钟),最后游戏时间\n"
+ Object.values(allData).map(s => {
const avgScore = s.totalGames > 0 ? Math.round(s.totalScore / s.totalGames) : 0;
const totalTime = Math.round(s.totalTime / 60);
return `${s.id},${s.name},${s.totalGames},${s.highestScore},${avgScore},${totalTime},${new Date(s.lastPlayed).toLocaleString('zh-CN')}`;
}).join('\n');
const link = document.createElement('a');
link.setAttribute('href', encodeURI(csvContent));
link.setAttribute('download', `out-prefix-game-records-${new Date().toISOString().slice(0,10)}.csv`);
link.click();
}
// 清空数据
function clearAllData() {
if (confirm('确定要清空所有学生记录吗?此操作不可恢复!')) {
localStorage.removeItem('outPrefixStudents');
loadAdminData();
alert('所有数据已清空!');
}
}
// 游戏相关函数
function initGame() {
score = 0;
completed = 0;
currentWords = [...words];
gameStartTime = new Date();
updateScore();
renderWords();
clearCategories();
}
// 渲染单词卡片
function renderWords() {
const pool = document.getElementById('word-pool');
pool.innerHTML = '';
currentWords.forEach((wordObj, index) => {
const card = document.createElement('div');
card.className = 'word-card bg-gray-100 px-4 py-3 rounded-lg border-2 border-transparent';
card.draggable = true;
card.dataset.word = wordObj.word;
card.dataset.category = wordObj.category;
card.innerHTML = `
<div class="font-semibold text-gray-800">${wordObj.word}</div>
<div class="text-sm text-gray-600">${wordObj.meaning}</div>
`;
// 拖拽事件
card.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', JSON.stringify(wordObj));
});
pool.appendChild(card);
});
}
// 清空分类区域
function clearCategories() {
['category-outer', 'category-surpass'].forEach(id => {
document.getElementById(id).innerHTML = '';
});
}
// 设置拖拽目标区域
function setupDropZones() {
const categories = {
'category-outer': 'outer',
'category-surpass': 'surpass'
};
Object.entries(categories).forEach(([id, categoryType]) => {
const zone = document.getElementById(id);
zone.addEventListener('dragover', (e) => {
e.preventDefault();
zone.classList.add('drag-over');
});
zone.addEventListener('dragleave', () => {
zone.classList.remove('drag-over');
});
zone.addEventListener('drop', (e) => {
e.preventDefault();
zone.classList.remove('drag-over');
const wordData = JSON.parse(e.dataTransfer.getData('text/plain'));
handleDrop(wordData, categoryType, zone);
});
});
}
// 处理拖拽放置
function handleDrop(wordData, targetCategory, zone) {
const wordElement = document.querySelector(`[data-word="${wordData.word}"]`);
if (wordData.category === targetCategory) {
// 正确分类
showSuccess(zone, wordElement);
moveWordToCategory(wordData, zone);
score += 10;
completed++;
if (completed === words.length) {
setTimeout(showCompletion, 1000);
}
} else {
// 错误分类
showError(wordElement);
score = Math.max(0, score - 3);
}
updateScore();
}
// 移动单词到分类区域
function moveWordToCategory(wordData, zone) {
const wordCard = document.createElement('div');
wordCard.className = 'bg-green-100 border-2 border-green-300 px-3 py-2 rounded-lg text-sm';
wordCard.innerHTML = `
<div class="font-semibold text-green-800">${wordData.word}</div>
<div class="text-xs text-green-600">${wordData.meaning}</div>
`;
zone.appendChild(wordCard);
// 从单词池中移除
const originalCard = document.querySelector(`[data-word="${wordData.word}"]`);
originalCard.remove();
// 从数组中移除
currentWords = currentWords.filter(w => w.word !== wordData.word);
}
// 显示成功特效
function showSuccess(zone, wordElement) {
zone.classList.add('celebrate');
setTimeout(() => zone.classList.remove('celebrate'), 600);
createConfetti(zone);
}
// 显示错误提醒
function showError(wordElement) {
wordElement.classList.add('shake');
wordElement.style.borderColor = '#ef4444';
wordElement.style.backgroundColor = '#fee2e2';
setTimeout(() => {
wordElement.classList.remove('shake');
wordElement.style.borderColor = 'transparent';
wordElement.style.backgroundColor = '#f3f4f6';
}, 500);
}
// 创建庆祝粒子特效
function createConfetti(element) {
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7'];
const rect = element.getBoundingClientRect();
for(let i = 0; i < 12; i++) {
const confetti = document.createElement('div');
confetti.className = 'confetti absolute w-2 h-2 rounded-full';
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
confetti.style.left = (rect.left + rect.width/2) + 'px';
confetti.style.top = (rect.top + rect.height/2) + 'px';
confetti.style.transform = `translate(${(Math.random() - 0.5) * 100}px, 0)`;
document.getElementById('particles').appendChild(confetti);
setTimeout(() => confetti.remove(), 2000);
}
}
// 更新分数显示
function updateScore() {
document.getElementById('score').textContent = score;
document.getElementById('progress').textContent = `${completed}/${words.length}`;
}
// 显示完成模态框
function showCompletion() {
const gameEndTime = new Date();
const timeSpent = Math.round((gameEndTime - gameStartTime) / 1000); // 秒数
// 保存游戏记录(非管理员)
if (!currentUser.isAdmin) {
DataManager.updateStudentRecord(currentUser.id, {
name: currentUser.name,
score: score,
timeSpent: timeSpent
});
updateUserStats();
}
document.getElementById('final-score').textContent = score;
document.getElementById('completion-modal').classList.remove('hidden');
document.getElementById('completion-modal').classList.add('flex');
}
// 事件监听器
document.getElementById('knowledge-btn').addEventListener('click', showKnowledge);
document.getElementById('game-btn').addEventListener('click', showGame);
document.getElementById('admin-btn').addEventListener('click', showAdmin);
document.getElementById('start-game-btn').addEventListener('click', showGame);
document.getElementById('back-to-knowledge').addEventListener('click', showKnowledge);
document.getElementById('reset-btn').addEventListener('click', initGame);
document.getElementById('logout-btn').addEventListener('click', () => {
if (confirm('确定要退出登录吗?')) {
location.reload();
}
});
document.getElementById('play-again').addEventListener('click', () => {
document.getElementById('completion-modal').classList.add('hidden');
document.getElementById('completion-modal').classList.remove('flex');
initGame();
});
document.getElementById('review-knowledge').addEventListener('click', () => {
document.getElementById('completion-modal').classList.add('hidden');
document.getElementById('completion-modal').classList.remove('flex');
showKnowledge();
});
document.getElementById('export-data').addEventListener('click', exportData);
document.getElementById('clear-data').addEventListener('click', clearAllData);
// 初始化
setupDropZones();
</script>
<script>window.parent.postMessage({ action: "ready" }, "*");
window.console = new Proxy(console, {
get(target, prop) {
if (['log', 'warn', 'error'].includes(prop)) {
return new Proxy(target[prop], {
apply(fn, thisArg, args) {
fn.apply(thisArg, args);
window.parent.postMessage({ action: 'console',
type: prop,
args: args.map((arg) => {
try {
return JSON.stringify(arg).replace(/^["']|["']$/g, '');
} catch (e) {
return arg;
}
})
}, '*');
}
});
}
return target[prop];
}
});
</script></body>
</html>
index.html
index.html