1993 lines
66 KiB
HTML
1993 lines
66 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>单词默写系统</title>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
}
|
||
|
||
body {
|
||
background: linear-gradient(135deg, #0a2a1a, #0c3a4a, #0d4b66);
|
||
min-height: 100vh;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20px;
|
||
overflow-x: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
/* 鼠标跟随小圈 - 更慢更滑溜 */
|
||
.cursor-follower {
|
||
position: fixed;
|
||
width: 20px;
|
||
height: 20px;
|
||
border: 2px solid rgba(100, 255, 200, 0.7);
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 9999;
|
||
transition: width 0.5s, height 0.5s, border 0.5s, transform 0.1s;
|
||
mix-blend-mode: difference;
|
||
}
|
||
|
||
.cursor-follower.hover {
|
||
width: 40px;
|
||
height: 40px;
|
||
border: 2px solid rgba(100, 255, 200, 0.9);
|
||
background: rgba(100, 255, 200, 0.1);
|
||
}
|
||
|
||
.container {
|
||
width: 100%;
|
||
max-width: 1000px;
|
||
text-align: center;
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
.header {
|
||
margin-bottom: 40px;
|
||
animation: fadeInDown 1s ease-out;
|
||
position: relative;
|
||
}
|
||
|
||
.logo {
|
||
font-size: 4.5rem;
|
||
margin-bottom: 20px;
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
text-shadow: 0 0 20px rgba(100, 255, 200, 0.3);
|
||
animation: pulse 3s infinite;
|
||
}
|
||
|
||
.title {
|
||
font-size: 2.8rem;
|
||
font-weight: 700;
|
||
margin-bottom: 15px;
|
||
color: #fff;
|
||
text-shadow: 0 0 15px rgba(100, 255, 200, 0.5);
|
||
position: relative;
|
||
z-index: 20;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 1.2rem;
|
||
color: #c0ffea;
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
line-height: 1.6;
|
||
position: relative;
|
||
z-index: 20;
|
||
}
|
||
|
||
/* 模式选择按钮 */
|
||
.mode-selector {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 30px;
|
||
margin: 40px 0;
|
||
animation: fadeInUp 1s ease-out 0.3s both;
|
||
}
|
||
|
||
.mode-btn {
|
||
padding: 20px 40px;
|
||
border-radius: 25px;
|
||
font-size: 1.2rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.4s ease;
|
||
position: relative;
|
||
overflow: hidden;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||
border: none;
|
||
color: #0a2a1a;
|
||
min-width: 180px;
|
||
}
|
||
|
||
.teacher-btn {
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
|
||
}
|
||
|
||
.student-btn {
|
||
background: linear-gradient(45deg, #5de6ff, #00d2ff, #7cffcb);
|
||
}
|
||
|
||
.mode-btn:hover {
|
||
transform: translateY(-5px) scale(1.05);
|
||
box-shadow: 0 15px 35px rgba(100, 255, 200, 0.4);
|
||
}
|
||
|
||
.mode-btn:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
.mode-btn::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
||
transition: 0.5s;
|
||
}
|
||
|
||
.mode-btn:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
/* 主要内容区域 */
|
||
.main-content {
|
||
background: rgba(10, 42, 26, 0.7);
|
||
border-radius: 30px;
|
||
padding: 40px;
|
||
margin: 30px 0;
|
||
backdrop-filter: blur(15px);
|
||
border: 1px solid rgba(100, 255, 200, 0.3);
|
||
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4);
|
||
animation: fadeInUp 1s ease-out 0.6s both;
|
||
position: relative;
|
||
overflow: hidden; /* 防止内容溢出 */
|
||
height: auto; /* 或者设置一个固定高度 */
|
||
}
|
||
|
||
/* 欢迎界面 */
|
||
.welcome-screen {
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
.welcome-icon {
|
||
font-size: 5rem;
|
||
margin-bottom: 30px;
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
}
|
||
|
||
.welcome-title {
|
||
font-size: 2.2rem;
|
||
color: #fff;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.welcome-text {
|
||
color: #c0ffea;
|
||
font-size: 1.1rem;
|
||
line-height: 1.8;
|
||
max-width: 600px;
|
||
margin: 0 auto 30px;
|
||
}
|
||
|
||
/* 老师端界面 */
|
||
.teacher-screen {
|
||
display: none;
|
||
}
|
||
|
||
/* 标签页 */
|
||
.tabs {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 15px;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.tab-btn {
|
||
padding: 12px 25px;
|
||
border-radius: 20px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border: 1px solid rgba(100, 255, 200, 0.3);
|
||
color: #c0ffea;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.tab-btn.active {
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
|
||
color: #0a2a1a;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.tab-btn:hover:not(.active) {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
/* 搜索框 */
|
||
.search-container {
|
||
display: flex;
|
||
gap: 15px;
|
||
margin: 25px 0;
|
||
max-width: 600px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
padding: 15px 20px;
|
||
border-radius: 15px;
|
||
border: 1px solid rgba(100, 255, 200, 0.3);
|
||
background: rgba(0, 0, 0, 0.2);
|
||
color: #fff;
|
||
font-size: 1rem;
|
||
outline: none;
|
||
}
|
||
|
||
.search-input::placeholder {
|
||
color: #a0ffe0;
|
||
}
|
||
|
||
.search-input:focus {
|
||
border-color: #7cffcb;
|
||
box-shadow: 0 0 15px rgba(124, 255, 203, 0.3);
|
||
}
|
||
|
||
/* 按钮组 */
|
||
.button-group {
|
||
display: flex;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
gap: 15px;
|
||
margin: 25px 0;
|
||
}
|
||
|
||
.action-btn {
|
||
padding: 12px 25px;
|
||
border-radius: 15px;
|
||
border: none;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
color: #c0ffea;
|
||
border: 1px solid rgba(100, 255, 200, 0.3);
|
||
}
|
||
|
||
.btn-success {
|
||
background: linear-gradient(45deg, #34C759, #7cffcb);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.btn-warning {
|
||
background: linear-gradient(45deg, #FFCC00, #ffd93d);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.btn-danger {
|
||
background: linear-gradient(45deg, #FF3B30, #ff6b6b);
|
||
color: white;
|
||
}
|
||
|
||
/* --- 禁用按钮样式 --- */
|
||
.action-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
/* 移除或减弱 hover 和 active 效果 */
|
||
}
|
||
.action-btn:disabled:hover {
|
||
transform: none;
|
||
box-shadow: none;
|
||
}
|
||
.action-btn:disabled:active {
|
||
transform: none;
|
||
}
|
||
.action-btn:disabled::before {
|
||
/* 移除或减弱 hover 效果动画 */
|
||
display: none;
|
||
}
|
||
/* --- 结束禁用按钮样式 --- */
|
||
|
||
|
||
.action-btn:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 8px 20px rgba(100, 255, 200, 0.3);
|
||
}
|
||
|
||
.action-btn:active {
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.action-btn::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
||
transition: 0.5s;
|
||
}
|
||
|
||
.action-btn:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
/* 统计信息 */
|
||
.stats-container {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
margin: 30px 0;
|
||
flex-wrap: wrap;
|
||
gap: 20px;
|
||
}
|
||
|
||
.stat-card {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
min-width: 120px;
|
||
border: 1px solid rgba(100, 255, 200, 0.2);
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
color: #7cffcb;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.stat-label {
|
||
color: #c0ffea;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* 单词网格 */
|
||
.word-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||
gap: 15px;
|
||
margin: 30px 0;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
padding: 10px;
|
||
}
|
||
|
||
.word-grid::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.word-grid::-webkit-scrollbar-track {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.word-grid::-webkit-scrollbar-thumb {
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.word-item {
|
||
padding: 15px 10px;
|
||
border-radius: 10px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
color: #c0ffea;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
border: 1px solid transparent;
|
||
font-weight: 500;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.word-item:hover {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.word-item.selected {
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
|
||
color: #0a2a1a;
|
||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.word-text {
|
||
font-weight: bold;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.word-translation {
|
||
font-size: 0.8em;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
/* 添加单词提示 */
|
||
.add-word-suggestion {
|
||
background: linear-gradient(135deg, rgba(255, 204, 0, 0.2), rgba(255, 107, 107, 0.2));
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
border: 1px solid rgba(255, 204, 0, 0.3);
|
||
display: none;
|
||
}
|
||
|
||
.add-word-suggestion p {
|
||
color: #ffd93d;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
/* 添加单词输入框 */
|
||
.add-word-inputs {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.add-word-input {
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
border: 1px solid rgba(100, 255, 200, 0.3);
|
||
background: rgba(0, 0, 0, 0.2);
|
||
color: #fff;
|
||
}
|
||
|
||
/* 学生端界面 */
|
||
.student-screen {
|
||
display: none;
|
||
}
|
||
|
||
/* 文件上传区域 */
|
||
.file-upload-area {
|
||
border: 2px dashed rgba(100, 255, 200, 0.3);
|
||
border-radius: 20px;
|
||
padding: 50px 30px;
|
||
margin: 30px 0;
|
||
text-align: center;
|
||
transition: all 0.3s ease;
|
||
background: rgba(0, 0, 0, 0.2);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.file-upload-area:hover {
|
||
border-color: #7cffcb;
|
||
background: rgba(124, 255, 203, 0.1);
|
||
}
|
||
|
||
.upload-icon {
|
||
font-size: 4rem;
|
||
color: #7cffcb;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.upload-text {
|
||
color: #c0ffea;
|
||
font-size: 1.2rem;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.upload-subtext {
|
||
color: #a0ffe0;
|
||
font-size: 0.9rem;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
/* 导入信息卡片 */
|
||
.import-info {
|
||
background: linear-gradient(135deg, rgba(52, 199, 89, 0.2), rgba(124, 255, 203, 0.2));
|
||
border-radius: 20px;
|
||
padding: 30px;
|
||
margin: 30px 0;
|
||
border: 1px solid rgba(52, 199, 89, 0.3);
|
||
display: none;
|
||
}
|
||
|
||
.import-info h3 {
|
||
color: #7cffcb;
|
||
margin-bottom: 20px;
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.import-details {
|
||
color: #c0ffea;
|
||
margin-bottom: 20px;
|
||
text-align: left;
|
||
}
|
||
|
||
.import-details p {
|
||
margin: 10px 0;
|
||
}
|
||
|
||
/* 默写区域 */
|
||
.dictation-area {
|
||
display: none;
|
||
padding: 30px 0;
|
||
}
|
||
|
||
.word-display {
|
||
font-size: 3rem;
|
||
font-weight: 300;
|
||
color: #fff;
|
||
margin: 40px 0;
|
||
text-shadow: 0 0 30px rgba(100, 255, 200, 0.5);
|
||
min-height: 150px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.word-blank {
|
||
display: inline-block;
|
||
min-width: 300px;
|
||
text-align: center;
|
||
margin: 10px 0;
|
||
font-family: 'Courier New', monospace;
|
||
letter-spacing: 5px;
|
||
font-size: 2.5rem;
|
||
}
|
||
|
||
.word-translation-student {
|
||
color: #a0ffe0;
|
||
font-size: 1.5rem;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.answer-input {
|
||
padding: 15px 20px;
|
||
border-radius: 15px;
|
||
border: 2px solid rgba(100, 255, 200, 0.3);
|
||
background: rgba(0, 0, 0, 0.2);
|
||
color: #fff;
|
||
font-size: 1.5rem;
|
||
text-align: center;
|
||
outline: none;
|
||
width: 300px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.answer-input:focus {
|
||
border-color: #7cffcb;
|
||
box-shadow: 0 0 20px rgba(124, 255, 203, 0.5);
|
||
}
|
||
|
||
.input-indicator {
|
||
color: #7cffcb;
|
||
font-size: 1.2rem;
|
||
margin-bottom: 10px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.progress-info {
|
||
color: #c0ffea;
|
||
font-size: 1.2rem;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.progress-bar {
|
||
width: 100%;
|
||
height: 10px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 5px;
|
||
overflow: hidden;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
|
||
border-radius: 5px;
|
||
width: 0%;
|
||
transition: width 0.5s ease;
|
||
}
|
||
|
||
.dictation-controls {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 20px;
|
||
margin: 40px 0;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.control-btn {
|
||
padding: 15px 30px;
|
||
border-radius: 15px;
|
||
border: none;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
min-width: 150px;
|
||
}
|
||
|
||
.prev-btn {
|
||
background: linear-gradient(45deg, #5de6ff, #00d2ff);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.next-btn {
|
||
background: linear-gradient(45deg, #7cffcb, #00d2ff);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.end-btn {
|
||
background: linear-gradient(45deg, #FF3B30, #ff6b6b);
|
||
color: white;
|
||
}
|
||
|
||
.submit-btn {
|
||
background: linear-gradient(45deg, #34C759, #7cffcb);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
/* 成绩报告区域 */
|
||
.result-area {
|
||
display: none;
|
||
padding: 30px 0;
|
||
}
|
||
|
||
.result-header {
|
||
color: #fff;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.score-circle {
|
||
width: 200px;
|
||
height: 200px;
|
||
border-radius: 50%;
|
||
background: conic-gradient(#7cffcb 0% 70%, #5de6ff 70% 100%);
|
||
margin: 0 auto 30px;
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.score-inner {
|
||
width: 160px;
|
||
height: 160px;
|
||
border-radius: 50%;
|
||
background: rgba(10, 42, 26, 0.9);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.score-number {
|
||
font-size: 3rem;
|
||
font-weight: 700;
|
||
color: #7cffcb;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.score-text {
|
||
color: #c0ffea;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.result-stats {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
margin: 30px 0;
|
||
flex-wrap: wrap;
|
||
gap: 20px;
|
||
}
|
||
|
||
.result-stat-card {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
min-width: 150px;
|
||
border: 1px solid rgba(100, 255, 200, 0.2);
|
||
}
|
||
|
||
.result-stat-number {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
color: #7cffcb;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.result-stat-label {
|
||
color: #c0ffea;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.grade-badge {
|
||
padding: 10px 20px;
|
||
border-radius: 20px;
|
||
font-weight: 600;
|
||
margin: 20px 0;
|
||
display: inline-block;
|
||
}
|
||
|
||
.grade-a {
|
||
background: linear-gradient(45deg, #34C759, #7cffcb);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.grade-b {
|
||
background: linear-gradient(45deg, #FFCC00, #ffd93d);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.grade-c {
|
||
background: linear-gradient(45deg, #FF9500, #ffcc80);
|
||
color: #0a2a1a;
|
||
}
|
||
|
||
.grade-d {
|
||
background: linear-gradient(45deg, #FF3B30, #ff6b6b);
|
||
color: white;
|
||
}
|
||
|
||
/* 错误详情列表 */
|
||
.error-details {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 15px;
|
||
padding: 25px;
|
||
margin: 30px 0;
|
||
border: 1px solid rgba(255, 107, 107, 0.3);
|
||
}
|
||
|
||
.error-details h3 {
|
||
color: #ff6b6b;
|
||
margin-bottom: 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.error-list {
|
||
list-style: none;
|
||
text-align: left;
|
||
}
|
||
|
||
.error-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 15px;
|
||
margin: 10px 0;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
border-radius: 10px;
|
||
border-left: 4px solid #ff6b6b;
|
||
}
|
||
|
||
.error-number {
|
||
font-weight: bold;
|
||
color: #ff6b6b;
|
||
margin-right: 15px;
|
||
min-width: 30px;
|
||
}
|
||
|
||
.error-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.error-word {
|
||
font-weight: bold;
|
||
color: #fff;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.error-answer {
|
||
color: #ffd93d;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.error-correct {
|
||
color: #7cffcb;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.error-arrow {
|
||
margin: 0 10px;
|
||
color: #ffd93d;
|
||
}
|
||
|
||
/* Particles background */
|
||
.particles {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: -1;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.particle {
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
background: rgba(100, 255, 200, 0.2);
|
||
animation: float 15s infinite linear;
|
||
}
|
||
|
||
@keyframes float {
|
||
0% {
|
||
transform: translateY(0) translateX(0) rotate(0deg);
|
||
opacity: 0;
|
||
}
|
||
10% {
|
||
opacity: 1;
|
||
}
|
||
90% {
|
||
opacity: 0.5;
|
||
}
|
||
100% {
|
||
transform: translateY(-100vh) translateX(100px) rotate(360deg);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
/* 旋转立方体 */
|
||
.cube-container {
|
||
position: absolute;
|
||
top: 20%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 150px;
|
||
height: 150px;
|
||
perspective: 1000px;
|
||
z-index: 5;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.cube {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
transform-style: preserve-3d;
|
||
animation: rotate 20s infinite linear;
|
||
}
|
||
|
||
.cube-face {
|
||
position: absolute;
|
||
width: 150px;
|
||
height: 150px;
|
||
background: rgba(100, 255, 200, 0.1);
|
||
border: 2px solid rgba(100, 255, 200, 0.3);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-size: 2rem;
|
||
font-weight: bold;
|
||
color: rgba(100, 255, 200, 0.7);
|
||
backdrop-filter: blur(2px);
|
||
}
|
||
|
||
.cube-face-front { transform: rotateY(0deg) translateZ(75px); }
|
||
.cube-face-back { transform: rotateY(180deg) translateZ(75px); }
|
||
.cube-face-right { transform: rotateY(90deg) translateZ(75px); }
|
||
.cube-face-left { transform: rotateY(-90deg) translateZ(75px); }
|
||
.cube-face-top { transform: rotateX(90deg) translateZ(75px); }
|
||
.cube-face-bottom { transform: rotateX(-90deg) translateZ(75px); }
|
||
|
||
@keyframes rotate {
|
||
0% { transform: rotateX(0) rotateY(0) rotateZ(0); }
|
||
100% { transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg); }
|
||
}
|
||
|
||
/* Animations */
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@keyframes fadeInDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
@keyframes fadeInUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% { text-shadow: 0 0 10px rgba(100, 255, 200, 0.3); }
|
||
50% { text-shadow: 0 0 30px rgba(100, 255, 200, 0.6); }
|
||
100% { text-shadow: 0 0 10px rgba(100, 255, 200, 0.3); }
|
||
}
|
||
|
||
/* --- 添加滚动动画相关的 CSS --- */
|
||
|
||
/* 为需要动画的容器添加基类 */
|
||
.animate-container {
|
||
position: relative;
|
||
overflow: hidden; /* 防止内容溢出 */
|
||
}
|
||
|
||
/* 定义动画类 */
|
||
.slide-transition {
|
||
transition: transform 0.5s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.5s ease-in-out;
|
||
}
|
||
|
||
/* 水平滑入(从右到左) */
|
||
.slide-enter-from-right {
|
||
transform: translateX(100%);
|
||
opacity: 0;
|
||
}
|
||
|
||
.slide-enter-to-right, .slide-leave-from-right {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
|
||
.slide-leave-to-right {
|
||
transform: translateX(-100%);
|
||
opacity: 0;
|
||
}
|
||
|
||
/* 水平滑出(从左到右) */
|
||
.slide-enter-from-left {
|
||
transform: translateX(-100%);
|
||
opacity: 0;
|
||
}
|
||
|
||
.slide-enter-to-left, .slide-leave-from-left {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
|
||
.slide-leave-to-left {
|
||
transform: translateX(100%);
|
||
opacity: 0;
|
||
}
|
||
|
||
/* 垂直滑入(从下到上) - 用于标签页切换 */
|
||
.slide-v-enter-from {
|
||
transform: translateY(20px);
|
||
opacity: 0;
|
||
}
|
||
|
||
.slide-v-enter-to, .slide-v-leave-from {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
|
||
.slide-v-leave-to {
|
||
transform: translateY(-20px);
|
||
opacity: 0;
|
||
}
|
||
|
||
/* --- 修改现有样式以支持动画 --- */
|
||
|
||
/* 老师端和学生端屏幕需要绝对定位来实现滚动效果 */
|
||
/* 但为了兼容性,我们使用 opacity 和 transform 来模拟 */
|
||
.teacher-screen, .student-screen {
|
||
/* 移除 display: none; */
|
||
/* display: none; */
|
||
opacity: 0;
|
||
transform: translateX(100%);
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
padding: 40px; /* 与 .main-content padding 一致 */
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.teacher-screen.active, .student-screen.active {
|
||
position: relative; /* 激活时恢复相对定位 */
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
|
||
/* 标签内容区域动画 */
|
||
.tab-content {
|
||
/* display: none; */ /* 移除 display: none */
|
||
opacity: 0;
|
||
transform: translateY(20px);
|
||
}
|
||
|
||
.tab-content.active {
|
||
/* display: block; */ /* 移除 display: block */
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
/* --- 结束添加滚动动画相关的 CSS --- */
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.title {
|
||
font-size: 2.2rem;
|
||
}
|
||
|
||
.logo {
|
||
font-size: 3.5rem;
|
||
}
|
||
|
||
.mode-selector {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 15px;
|
||
}
|
||
|
||
.mode-btn {
|
||
width: 100%;
|
||
max-width: 300px;
|
||
}
|
||
|
||
.main-content {
|
||
padding: 25px 20px;
|
||
}
|
||
|
||
.word-grid {
|
||
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||
gap: 10px;
|
||
}
|
||
|
||
.word-item {
|
||
padding: 12px 8px;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.word-display {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.word-blank {
|
||
font-size: 1.8rem;
|
||
min-width: 200px;
|
||
}
|
||
|
||
.answer-input {
|
||
width: 250px;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.dictation-controls {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.control-btn {
|
||
width: 100%;
|
||
max-width: 250px;
|
||
}
|
||
|
||
.cube-container {
|
||
width: 100px;
|
||
height: 100px;
|
||
top: 25%;
|
||
}
|
||
|
||
.cube-face {
|
||
width: 100px;
|
||
height: 100px;
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.cube-face-front { transform: rotateY(0deg) translateZ(50px); }
|
||
.cube-face-back { transform: rotateY(180deg) translateZ(50px); }
|
||
.cube-face-right { transform: rotateY(90deg) translateZ(50px); }
|
||
.cube-face-left { transform: rotateY(-90deg) translateZ(50px); }
|
||
.cube-face-top { transform: rotateX(90deg) translateZ(50px); }
|
||
.cube-face-bottom { transform: rotateX(-90deg) translateZ(50px); }
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.title {
|
||
font-size: 1.8rem;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.main-content {
|
||
padding: 20px 15px;
|
||
}
|
||
|
||
.word-grid {
|
||
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
|
||
}
|
||
|
||
.word-display {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.word-blank {
|
||
font-size: 1.5rem;
|
||
min-width: 150px;
|
||
letter-spacing: 3px;
|
||
}
|
||
|
||
.answer-input {
|
||
width: 200px;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.stats-container {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.stat-card {
|
||
width: 100%;
|
||
max-width: 200px;
|
||
}
|
||
|
||
.score-circle {
|
||
width: 150px;
|
||
height: 150px;
|
||
}
|
||
|
||
.score-inner {
|
||
width: 120px;
|
||
height: 120px;
|
||
}
|
||
|
||
.score-number {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.error-item {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
text-align: left;
|
||
}
|
||
|
||
.error-number {
|
||
margin-bottom: 10px;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 鼠标跟随小圈 -->
|
||
<div class="cursor-follower"></div>
|
||
|
||
<!-- 背景粒子 -->
|
||
<div class="particles" id="particles"></div>
|
||
|
||
<!-- 旋转立方体 -->
|
||
<div class="cube-container">
|
||
<div class="cube">
|
||
<div class="cube-face cube-face-front">单</div>
|
||
<div class="cube-face cube-face-back">词</div>
|
||
<div class="cube-face cube-face-right">默</div>
|
||
<div class="cube-face cube-face-left">写</div>
|
||
<div class="cube-face cube-face-top">系</div>
|
||
<div class="cube-face cube-face-bottom">统</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="container">
|
||
<div class="header">
|
||
<div class="logo">
|
||
<i class="fas fa-book"></i>
|
||
</div>
|
||
<h1 class="title">单词默写系统</h1>
|
||
<p class="subtitle">简单易用的单词默写工具</p>
|
||
</div>
|
||
|
||
<!-- 模式选择 -->
|
||
<div class="mode-selector">
|
||
<button class="mode-btn teacher-btn" onclick="switchMode('teacher')">
|
||
<i class="fas fa-chalkboard-teacher"></i> 教师端
|
||
</button>
|
||
<button class="mode-btn student-btn" onclick="switchMode('student')">
|
||
<i class="fas fa-user-graduate"></i> 学生端
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 主要内容区域 -->
|
||
<div class="main-content">
|
||
<!-- 欢迎界面 -->
|
||
<div id="welcomeScreen" class="welcome-screen">
|
||
<div class="welcome-icon">
|
||
<i class="fas fa-book-open"></i>
|
||
</div>
|
||
<h2 class="welcome-title">欢迎使用单词默写系统</h2>
|
||
<p class="welcome-text">
|
||
这是一个便捷的单词默写工具,老师可以选择单词并导出配置文件,
|
||
学生可以导入配置文件进行单词默写练习。
|
||
</p>
|
||
<p class="welcome-text">
|
||
请先选择使用模式:教师端或学生端
|
||
</p>
|
||
</div>
|
||
|
||
<!-- 老师端界面 -->
|
||
<div id="teacherScreen" class="teacher-screen animate-container">
|
||
<!-- 标签页 -->
|
||
<div class="tabs">
|
||
<button class="tab-btn active" onclick="switchTab('selection')">单词选择</button>
|
||
</div>
|
||
|
||
<!-- 单词选择标签页 -->
|
||
<div id="selectionTab">
|
||
<h3 style="color: #fff; margin: 20px 0;">选择单词</h3>
|
||
|
||
<div class="search-container">
|
||
<input type="text" class="search-input" id="searchInput" placeholder="搜索单词..." oninput="filterWords()">
|
||
<button class="action-btn btn-secondary" onclick="clearSearch()">
|
||
<i class="fas fa-times"></i> 清除
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 添加单词提示 -->
|
||
<div id="addWordSuggestion" class="add-word-suggestion">
|
||
<p><i class="fas fa-lightbulb"></i> 没有找到您要的单词?可以添加到词库中!</p>
|
||
<div class="add-word-inputs">
|
||
<input type="text" class="add-word-input" id="newWordInput" placeholder="英文单词">
|
||
<input type="text" class="add-word-input" id="newTranslationInput" placeholder="中文翻译">
|
||
<button class="action-btn btn-warning" onclick="addNewWordWithTranslation()">
|
||
<i class="fas fa-plus"></i> 添加单词
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<button class="action-btn btn-primary" onclick="selectAllWords()">
|
||
<i class="fas fa-check-square"></i> 全选
|
||
</button>
|
||
<button class="action-btn btn-secondary" onclick="deselectAllWords()">
|
||
<i class="fas fa-square"></i> 取消全选
|
||
</button>
|
||
<button class="action-btn btn-warning" onclick="randomSelectWords()">
|
||
<i class="fas fa-dice"></i> 随机60个
|
||
</button>
|
||
<button class="action-btn btn-success" onclick="exportConfig()">
|
||
<i class="fas fa-download"></i> 导出配置
|
||
</button>
|
||
</div>
|
||
|
||
<div class="stats-container">
|
||
<div class="stat-card">
|
||
<div class="stat-number" id="totalWords">0</div>
|
||
<div class="stat-label">总单词</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-number" id="selectedCount">0</div>
|
||
<div class="stat-label">已选择</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-number" id="percentage">0%</div>
|
||
<div class="stat-label">完成度</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="word-grid" id="wordGrid">
|
||
<!-- 单词项将通过JavaScript动态生成 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 学生端界面 -->
|
||
<div id="studentScreen" class="student-screen animate-container">
|
||
<h3 style="color: #fff; margin: 20px 0;">导入配置文件</h3>
|
||
|
||
<div class="file-upload-area" id="fileUploadArea" onclick="document.getElementById('configFile').click()">
|
||
<div class="upload-icon">
|
||
<i class="fas fa-cloud-upload-alt"></i>
|
||
</div>
|
||
<h3 class="upload-text">点击或拖拽文件到此处上传</h3>
|
||
<p class="upload-subtext">支持 .json 格式的配置文件</p>
|
||
<button class="action-btn btn-primary">
|
||
<i class="fas fa-folder-open"></i> 选择文件
|
||
</button>
|
||
<input type="file" id="configFile" accept=".json" style="display: none;" onchange="handleFileSelect(event)">
|
||
</div>
|
||
|
||
<div id="importInfo" class="import-info">
|
||
<h3><i class="fas fa-check-circle"></i> 配置导入成功</h3>
|
||
<div class="import-details">
|
||
<p><strong>单词数量:</strong><span id="importedWordCount">0</span> 个</p>
|
||
<p><strong>创建时间:</strong><span id="importedTime">-</span></p>
|
||
</div>
|
||
<button class="action-btn btn-success" onclick="startDictation()">
|
||
<i class="fas fa-play"></i> 开始默写
|
||
</button>
|
||
</div>
|
||
|
||
<div id="dictationArea" class="dictation-area">
|
||
<h3 style="color: #fff; margin-bottom: 30px;">单词默写</h3>
|
||
<div class="word-display" id="wordDisplay">
|
||
<div class="input-indicator">请输入单词:</div>
|
||
<div class="word-blank" id="wordBlank">_ _ _ _ _</div>
|
||
<div class="word-first-letter" id="wordFirstLetter" style="color:#ffd93d;font-size:2rem;margin-top:10px;"></div>
|
||
<div class="word-translation-student" id="wordTranslationStudent"></div> <!-- 新增翻译显示 -->
|
||
</div>
|
||
<input type="text" class="answer-input" id="answerInput" placeholder="请输入单词..." onkeypress="handleKeyPress(event)">
|
||
<div class="progress-info" id="progressInfo">进度: 0/0</div>
|
||
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="progressFill"></div>
|
||
</div>
|
||
|
||
<div class="dictation-controls">
|
||
<button class="control-btn prev-btn" onclick="showPrevWord()">
|
||
<i class="fas fa-arrow-left"></i> 上一个
|
||
</button>
|
||
<button class="control-btn submit-btn" onclick="submitAnswer()">
|
||
<i class="fas fa-check"></i> 提交答案
|
||
</button>
|
||
<button class="control-btn next-btn" onclick="showNextWord()">
|
||
下一个 <i class="fas fa-arrow-right"></i>
|
||
</button>
|
||
<button class="control-btn end-btn" onclick="endDictation()">
|
||
<i class="fas fa-stop"></i> 结束默写
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="resultArea" class="result-area">
|
||
<h2 class="result-header">默写成绩报告</h2>
|
||
<div class="score-circle">
|
||
<div class="score-inner">
|
||
<div class="score-number" id="finalScore">0</div>
|
||
<div class="score-text">正确率</div>
|
||
</div>
|
||
</div>
|
||
<div class="grade-badge" id="gradeBadge">等级: A</div>
|
||
|
||
<div class="result-stats">
|
||
<div class="result-stat-card">
|
||
<div class="result-stat-number" id="correctCount">0</div>
|
||
<div class="result-stat-label">正确</div>
|
||
</div>
|
||
<div class="result-stat-card">
|
||
<div class="result-stat-number" id="wrongCount">0</div>
|
||
<div class="result-stat-label">错误</div>
|
||
</div>
|
||
<div class="result-stat-card">
|
||
<div class="result-stat-number" id="totalCount">0</div>
|
||
<div class="result-stat-label">总计</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="errorDetails" class="error-details">
|
||
<h3><i class="fas fa-times-circle"></i> 错误详情</h3>
|
||
<ul class="error-list" id="errorList">
|
||
<!-- 错误列表将通过JavaScript动态生成 -->
|
||
</ul>
|
||
</div>
|
||
|
||
<button class="action-btn btn-primary" onclick="restartDictation()">
|
||
<i class="fas fa-redo"></i> 重新开始
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 单词库 - 扩展版 (带中文翻译) - 初始为空
|
||
|
||
let toeflWords = [
|
||
];
|
||
|
||
|
||
// 全局变量
|
||
let selectedWords = []; // 存储选中的单词对象 { word, translation }
|
||
let importedWords = []; // 存储从配置文件导入的单词对象 { word, translation }
|
||
let currentWordIndex = 0;
|
||
let dictationStarted = false;
|
||
let userAnswers = []; // 存储用户答案 (字符串)
|
||
let correctAnswers = []; // 存储正确答案 (字符串) - 修复:存储英文单词字符串
|
||
|
||
// 页面加载时初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
renderWordGrid();
|
||
updateStats();
|
||
createParticles();
|
||
initCursorFollower();
|
||
});
|
||
|
||
// 创建背景粒子
|
||
function createParticles() {
|
||
const particlesContainer = document.getElementById('particles');
|
||
const particleCount = 30;
|
||
|
||
for (let i = 0; i < particleCount; i++) {
|
||
const particle = document.createElement('div');
|
||
particle.classList.add('particle');
|
||
|
||
// 随机大小
|
||
const size = Math.random() * 20 + 5;
|
||
particle.style.width = `${size}px`;
|
||
particle.style.height = `${size}px`;
|
||
|
||
// 随机位置
|
||
particle.style.left = `${Math.random() * 100}%`;
|
||
particle.style.top = `${Math.random() * 100}%`;
|
||
|
||
// 随机动画延迟和时长
|
||
const delay = Math.random() * 15;
|
||
const duration = 15 + Math.random() * 10;
|
||
particle.style.animationDelay = `${delay}s`;
|
||
particle.style.animationDuration = `${duration}s`;
|
||
|
||
// 随机颜色
|
||
const colors = ['rgba(100, 255, 200, 0.2)', 'rgba(0, 210, 255, 0.2)', 'rgba(93, 230, 255, 0.2)'];
|
||
particle.style.background = colors[Math.floor(Math.random() * colors.length)];
|
||
|
||
particlesContainer.appendChild(particle);
|
||
}
|
||
}
|
||
|
||
// 鼠标跟随效果
|
||
function initCursorFollower() {
|
||
const cursor = document.querySelector('.cursor-follower');
|
||
const buttons = document.querySelectorAll('button, .feature-card');
|
||
|
||
let mouseX = 0;
|
||
let mouseY = 0;
|
||
let cursorX = 0;
|
||
let cursorY = 0;
|
||
const speed = 0.1;
|
||
|
||
document.addEventListener('mousemove', e => {
|
||
mouseX = e.clientX;
|
||
mouseY = e.clientY;
|
||
});
|
||
|
||
function animateCursor() {
|
||
const distX = mouseX - cursorX;
|
||
const distY = mouseY - cursorY;
|
||
|
||
cursorX += distX * speed;
|
||
cursorY += distY * speed;
|
||
|
||
cursor.style.left = cursorX + 'px';
|
||
cursor.style.top = cursorY + 'px';
|
||
|
||
requestAnimationFrame(animateCursor);
|
||
}
|
||
|
||
animateCursor();
|
||
|
||
buttons.forEach(button => {
|
||
button.addEventListener('mouseenter', () => {
|
||
cursor.classList.add('hover');
|
||
});
|
||
|
||
button.addEventListener('mouseleave', () => {
|
||
cursor.classList.remove('hover');
|
||
});
|
||
});
|
||
}
|
||
|
||
// 模式切换 (带滑动动画)
|
||
function switchMode(mode) {
|
||
const welcomeScreen = document.getElementById('welcomeScreen');
|
||
const teacherScreen = document.getElementById('teacherScreen');
|
||
const studentScreen = document.getElementById('studentScreen');
|
||
|
||
// 添加动画类
|
||
teacherScreen.classList.add('slide-transition');
|
||
studentScreen.classList.add('slide-transition');
|
||
|
||
// 隐藏所有屏幕
|
||
welcomeScreen.style.display = 'none';
|
||
teacherScreen.classList.remove('active');
|
||
studentScreen.classList.remove('active');
|
||
|
||
// 根据目标模式显示屏幕
|
||
if (mode === 'teacher') {
|
||
// 从右侧滑入
|
||
teacherScreen.classList.add('slide-enter-from-right');
|
||
teacherScreen.style.display = 'block'; // 确保元素可见
|
||
// 强制重绘以触发动画
|
||
teacherScreen.offsetHeight;
|
||
teacherScreen.classList.remove('slide-enter-from-right');
|
||
teacherScreen.classList.add('active');
|
||
} else if (mode === 'student') {
|
||
// 从右侧滑入
|
||
studentScreen.classList.add('slide-enter-from-right');
|
||
studentScreen.style.display = 'block'; // 确保元素可见
|
||
// 强制重绘以触发动画
|
||
studentScreen.offsetHeight;
|
||
studentScreen.classList.remove('slide-enter-from-right');
|
||
studentScreen.classList.add('active');
|
||
} else {
|
||
// 默认显示欢迎界面 (如果需要从其他模式返回)
|
||
welcomeScreen.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
|
||
// 标签页切换(老师端只有一个标签页)
|
||
function switchTab(tabId) {
|
||
// 移除所有标签的active状态
|
||
document.querySelectorAll('.tab-btn').forEach(tab => {
|
||
tab.classList.remove('active');
|
||
});
|
||
|
||
// 设置当前标签为active
|
||
event.target.classList.add('active');
|
||
|
||
// 切换显示内容
|
||
if (tabId === 'selection') {
|
||
document.getElementById('selectionTab').style.display = 'block';
|
||
}
|
||
}
|
||
|
||
// 渲染单词网格
|
||
function renderWordGrid(filteredWords = toeflWords) {
|
||
const wordGrid = document.getElementById('wordGrid');
|
||
wordGrid.innerHTML = '';
|
||
|
||
filteredWords.forEach(wordObj => {
|
||
const wordItem = document.createElement('div');
|
||
wordItem.className = `word-item ${selectedWords.some(w => w.word === wordObj.word) ? 'selected' : ''}`;
|
||
// 修改:显示单词和翻译
|
||
wordItem.innerHTML = `
|
||
<div class="word-text">${wordObj.word}</div>
|
||
<div class="word-translation">${wordObj.translation}</div>
|
||
`;
|
||
wordItem.onclick = () => toggleWordSelection(wordObj); // 传递整个对象
|
||
wordGrid.appendChild(wordItem);
|
||
});
|
||
}
|
||
|
||
// 切换单词选择状态
|
||
function toggleWordSelection(wordObj) {
|
||
const index = selectedWords.findIndex(w => w.word === wordObj.word);
|
||
if (index > -1) {
|
||
selectedWords.splice(index, 1);
|
||
} else {
|
||
selectedWords.push(wordObj);
|
||
}
|
||
renderWordGrid();
|
||
updateStats();
|
||
}
|
||
|
||
// 更新统计信息
|
||
function updateStats() {
|
||
const total = toeflWords.length;
|
||
const selected = selectedWords.length;
|
||
const percentage = total > 0 ? Math.round((selected / total) * 100) : 0;
|
||
|
||
document.getElementById('selectedCount').textContent = selected;
|
||
document.getElementById('totalWords').textContent = total;
|
||
document.getElementById('percentage').textContent = percentage + '%';
|
||
}
|
||
|
||
// 全选单词
|
||
function selectAllWords() {
|
||
// 确保 selectedWords 包含 toeflWords 中的所有单词对象
|
||
selectedWords = [...toeflWords];
|
||
renderWordGrid(); // 重新渲染网格以反映选择状态
|
||
updateStats(); // 更新统计信息
|
||
}
|
||
|
||
// 取消全选
|
||
function deselectAllWords() {
|
||
selectedWords = []; // 清空已选单词数组
|
||
renderWordGrid(); // 重新渲染网格
|
||
updateStats(); // 更新统计信息
|
||
}
|
||
|
||
// 随机选择60个单词
|
||
function randomSelectWords() {
|
||
selectedWords = [];
|
||
// 创建一个 toeflWords 的副本并随机打乱
|
||
const shuffled = [...toeflWords].sort(() => 0.5 - Math.random());
|
||
// 选取前60个(或如果总数不足60个,则全部选取)
|
||
selectedWords = shuffled.slice(0, Math.min(60, shuffled.length));
|
||
renderWordGrid(); // 重新渲染网格
|
||
updateStats(); // 更新统计信息
|
||
}
|
||
|
||
// 搜索单词
|
||
function filterWords() {
|
||
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
||
if (searchTerm === '') {
|
||
renderWordGrid();
|
||
document.getElementById('addWordSuggestion').style.display = 'none';
|
||
} else {
|
||
// 修改:在单词和翻译中搜索
|
||
const filtered = toeflWords.filter(wordObj =>
|
||
wordObj.word.toLowerCase().includes(searchTerm) ||
|
||
wordObj.translation.toLowerCase().includes(searchTerm)
|
||
);
|
||
renderWordGrid(filtered);
|
||
|
||
// 如果没有找到匹配的单词,显示添加单词提示
|
||
if (filtered.length === 0) {
|
||
document.getElementById('addWordSuggestion').style.display = 'block';
|
||
// 不再直接设置 newWordText,而是预填充输入框
|
||
document.getElementById('newWordInput').value = searchTerm;
|
||
document.getElementById('newTranslationInput').value = '';
|
||
} else {
|
||
document.getElementById('addWordSuggestion').style.display = 'none';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加新单词 (带翻译)
|
||
function addNewWordWithTranslation() {
|
||
const newWord = document.getElementById('newWordInput').value.trim().toLowerCase();
|
||
const newTranslation = document.getElementById('newTranslationInput').value.trim();
|
||
|
||
if (newWord && newTranslation) {
|
||
// 检查是否已存在
|
||
if (!toeflWords.some(w => w.word === newWord)) {
|
||
const newWordObj = { word: newWord, translation: newTranslation };
|
||
toeflWords.push(newWordObj);
|
||
toeflWords.sort((a, b) => a.word.localeCompare(b.word)); // 按字母排序
|
||
renderWordGrid();
|
||
updateStats();
|
||
document.getElementById('addWordSuggestion').style.display = 'none';
|
||
document.getElementById('searchInput').value = '';
|
||
document.getElementById('newWordInput').value = '';
|
||
document.getElementById('newTranslationInput').value = '';
|
||
|
||
// 显示添加成功提示
|
||
alert(`单词 "${newWord}" (${newTranslation}) 已成功添加到词库中!`);
|
||
} else {
|
||
alert(`单词 "${newWord}" 已存在于词库中!`);
|
||
}
|
||
} else {
|
||
alert('请输入英文单词和中文翻译!');
|
||
}
|
||
}
|
||
|
||
// 清除搜索
|
||
function clearSearch() {
|
||
document.getElementById('searchInput').value = '';
|
||
document.getElementById('addWordSuggestion').style.display = 'none';
|
||
renderWordGrid();
|
||
}
|
||
|
||
// 导出配置文件 - 修复下载问题
|
||
function exportConfig() {
|
||
if (selectedWords.length === 0) {
|
||
alert('请先选择单词!');
|
||
return;
|
||
}
|
||
|
||
const config = {
|
||
words: selectedWords, // 导出包含 word 和 translation 的对象数组
|
||
createdTime: new Date().toISOString(),
|
||
version: '1.1' // 更新版本号
|
||
};
|
||
|
||
// 创建Blob对象
|
||
const blob = new Blob([JSON.stringify(config, null, 2)], {type: 'application/json'});
|
||
|
||
// 创建下载链接
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = 'toefl_words_config_with_translation.json';
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
|
||
// 清理
|
||
setTimeout(() => {
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url);
|
||
}, 100);
|
||
|
||
// 添加成功动画
|
||
const exportBtn = event.target.closest('.action-btn');
|
||
const originalText = exportBtn.innerHTML;
|
||
exportBtn.innerHTML = '<i class="fas fa-check"></i> 已导出';
|
||
exportBtn.classList.add('btn-success');
|
||
|
||
setTimeout(() => {
|
||
exportBtn.innerHTML = originalText;
|
||
exportBtn.classList.remove('btn-success');
|
||
}, 2000);
|
||
}
|
||
|
||
// 处理文件选择
|
||
function handleFileSelect(event) {
|
||
const file = event.target.files[0];
|
||
if (file) {
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
try {
|
||
const config = JSON.parse(e.target.result);
|
||
// 兼容旧版本 (仅包含单词字符串的数组)
|
||
if (Array.isArray(config.words) && config.words.length > 0 && typeof config.words[0] === 'string') {
|
||
// 转换旧格式到新格式 (翻译留空)
|
||
importedWords = config.words.map(word => ({ word, translation: '' }));
|
||
} else if (Array.isArray(config.words) && config.words.length > 0 && typeof config.words[0] === 'object' && config.words[0].hasOwnProperty('word')) {
|
||
// 新格式
|
||
importedWords = config.words || [];
|
||
} else {
|
||
throw new Error('Invalid word list format');
|
||
}
|
||
|
||
const createdTime = new Date(config.createdTime);
|
||
|
||
document.getElementById('importedWordCount').textContent = importedWords.length;
|
||
document.getElementById('importedTime').textContent = createdTime.toLocaleString();
|
||
|
||
document.getElementById('importInfo').style.display = 'block';
|
||
|
||
} catch (error) {
|
||
console.error(error); // 在控制台打印错误详情
|
||
alert('配置文件格式错误或版本不兼容!');
|
||
}
|
||
};
|
||
reader.readAsText(file);
|
||
}
|
||
}
|
||
|
||
// 开始默写
|
||
function startDictation() {
|
||
if (importedWords.length === 0) {
|
||
alert('请先导入配置文件!');
|
||
return;
|
||
}
|
||
|
||
document.getElementById('importInfo').style.display = 'none';
|
||
document.getElementById('dictationArea').style.display = 'block';
|
||
|
||
// 修复:初始化状态
|
||
userAnswers = new Array(importedWords.length).fill('');
|
||
// 修复:correctAnswers 应该是英文单词字符串数组
|
||
correctAnswers = importedWords.map(w => w.word);
|
||
currentWordIndex = 0;
|
||
dictationStarted = true;
|
||
showCurrentWord();
|
||
|
||
// 聚焦到输入框
|
||
document.getElementById('answerInput').focus();
|
||
}
|
||
|
||
// 显示当前单词(横杠形式)和翻译,并显示首字母
|
||
function showCurrentWord() {
|
||
if (currentWordIndex >= 0 && currentWordIndex < importedWords.length) {
|
||
const currentWordObj = importedWords[currentWordIndex];
|
||
const currentWord = currentWordObj.word;
|
||
const wordTranslation = currentWordObj.translation;
|
||
const wordLength = currentWord.length;
|
||
|
||
// 横线
|
||
const blanks = '_ '.repeat(wordLength).trim();
|
||
document.getElementById('wordBlank').textContent = blanks;
|
||
|
||
// 首字母提示
|
||
document.getElementById('wordFirstLetter').textContent = `首字母提示:${currentWord[0].toUpperCase()}`;
|
||
|
||
// 翻译
|
||
document.getElementById('wordTranslationStudent').textContent = wordTranslation ? `(${wordTranslation})` : '';
|
||
|
||
// 进度
|
||
document.getElementById('progressInfo').textContent =
|
||
`进度: ${currentWordIndex + 1}/${importedWords.length}`;
|
||
|
||
const progress = importedWords.length > 0 ?
|
||
Math.round(((currentWordIndex + 1) / importedWords.length) * 100) : 0;
|
||
document.getElementById('progressFill').style.width = progress + '%';
|
||
|
||
// 回填答案
|
||
document.getElementById('answerInput').value = userAnswers[currentWordIndex] || '';
|
||
document.getElementById('answerInput').focus();
|
||
}
|
||
}
|
||
|
||
// 处理键盘按键
|
||
function handleKeyPress(event) {
|
||
if (event.key === 'Enter') {
|
||
submitAnswer();
|
||
}
|
||
}
|
||
|
||
// 提交答案
|
||
function submitAnswer() {
|
||
const answerInput = document.getElementById('answerInput');
|
||
const userAnswer = answerInput.value.trim().toLowerCase();
|
||
|
||
if (userAnswer) {
|
||
// 保存用户答案 (字符串)
|
||
userAnswers[currentWordIndex] = userAnswer;
|
||
|
||
// 如果是最后一个单词,自动结束
|
||
if (currentWordIndex === importedWords.length - 1) {
|
||
endDictation();
|
||
} else {
|
||
// 自动跳到下一个单词
|
||
showNextWord();
|
||
}
|
||
} else {
|
||
alert('请输入答案!');
|
||
}
|
||
}
|
||
|
||
// 下一个单词
|
||
function showNextWord() {
|
||
if (currentWordIndex < importedWords.length - 1) {
|
||
currentWordIndex++;
|
||
showCurrentWord();
|
||
}
|
||
}
|
||
|
||
// 上一个单词
|
||
function showPrevWord() {
|
||
if (currentWordIndex > 0) {
|
||
currentWordIndex--;
|
||
showCurrentWord();
|
||
}
|
||
}
|
||
|
||
// 结束默写并显示成绩
|
||
function endDictation() {
|
||
dictationStarted = false;
|
||
|
||
// 计算成绩
|
||
let correctCount = 0;
|
||
const errorDetails = [];
|
||
|
||
for (let i = 0; i < importedWords.length; i++) {
|
||
// 修复:比较用户答案和正确答案 (都是字符串)
|
||
if (userAnswers[i] === correctAnswers[i]) {
|
||
correctCount++;
|
||
} else {
|
||
// 记录错误详情 (包含翻译)
|
||
errorDetails.push({
|
||
number: i + 1,
|
||
word: correctAnswers[i], // 正确答案是英文单词字符串
|
||
translation: importedWords[i].translation, // 获取翻译
|
||
userAnswer: userAnswers[i] || '(未作答)'
|
||
});
|
||
}
|
||
}
|
||
|
||
const totalCount = importedWords.length;
|
||
const accuracy = totalCount > 0 ? Math.round((correctCount / totalCount) * 100) : 0;
|
||
|
||
// 显示成绩报告
|
||
document.getElementById('dictationArea').style.display = 'none';
|
||
document.getElementById('resultArea').style.display = 'block';
|
||
|
||
// 更新成绩显示
|
||
document.getElementById('finalScore').textContent = accuracy + '%';
|
||
document.getElementById('correctCount').textContent = correctCount;
|
||
document.getElementById('wrongCount').textContent = totalCount - correctCount;
|
||
document.getElementById('totalCount').textContent = totalCount;
|
||
|
||
// 根据正确率显示等级
|
||
let gradeClass = '';
|
||
let gradeText = '';
|
||
if (accuracy >= 90) {
|
||
gradeClass = 'grade-a';
|
||
gradeText = 'A (优秀)';
|
||
} else if (accuracy >= 80) {
|
||
gradeClass = 'grade-b';
|
||
gradeText = 'B (良好)';
|
||
} else if (accuracy >= 70) {
|
||
gradeClass = 'grade-c';
|
||
gradeText = 'C (及格)';
|
||
} else {
|
||
gradeClass = 'grade-d';
|
||
gradeText = 'D (需要努力)';
|
||
}
|
||
|
||
const gradeBadge = document.getElementById('gradeBadge');
|
||
gradeBadge.textContent = '等级: ' + gradeText;
|
||
gradeBadge.className = 'grade-badge ' + gradeClass;
|
||
|
||
// 更新圆形进度条
|
||
const scoreCircle = document.querySelector('.score-circle');
|
||
const gradientDegree = (accuracy / 100) * 360;
|
||
scoreCircle.style.background = `conic-gradient(#7cffcb 0deg ${gradientDegree}deg, #5de6ff ${gradientDegree}deg 360deg)`;
|
||
|
||
// 显示错误详情
|
||
const errorList = document.getElementById('errorList');
|
||
errorList.innerHTML = '';
|
||
|
||
if (errorDetails.length > 0) {
|
||
errorDetails.forEach(error => {
|
||
const li = document.createElement('li');
|
||
li.className = 'error-item';
|
||
// 修改:显示翻译
|
||
li.innerHTML = `
|
||
<div class="error-number">${error.number}.</div>
|
||
<div class="error-content">
|
||
<div class="error-word">${error.word} ${error.translation ? `(${error.translation})` : ''}</div>
|
||
<div class="error-answer">您的答案: ${error.userAnswer}</div>
|
||
<div class="error-correct">
|
||
<i class="fas fa-times" style="color: #ff6b6b;"></i>
|
||
<span class="error-arrow">→</span>
|
||
<i class="fas fa-check" style="color: #7cffcb;"></i>
|
||
<span style="margin-left: 10px;">正确答案: ${error.word}</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
errorList.appendChild(li);
|
||
});
|
||
} else {
|
||
const li = document.createElement('li');
|
||
li.innerHTML = '<div style="color: #7cffcb; text-align: center; padding: 20px;"><i class="fas fa-check-circle"></i> 恭喜!全部正确!</div>';
|
||
errorList.appendChild(li);
|
||
}
|
||
}
|
||
|
||
// 重新开始默写
|
||
function restartDictation() {
|
||
document.getElementById('resultArea').style.display = 'none';
|
||
document.getElementById('dictationArea').style.display = 'block';
|
||
|
||
// 重置状态
|
||
userAnswers = new Array(importedWords.length).fill('');
|
||
currentWordIndex = 0;
|
||
dictationStarted = true;
|
||
showCurrentWord();
|
||
}
|
||
|
||
// 拖拽上传功能
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const uploadArea = document.getElementById('fileUploadArea');
|
||
|
||
uploadArea.addEventListener('dragover', function(e) {
|
||
e.preventDefault();
|
||
uploadArea.style.borderColor = '#7cffcb';
|
||
uploadArea.style.backgroundColor = 'rgba(124, 255, 203, 0.1)';
|
||
});
|
||
|
||
uploadArea.addEventListener('dragleave', function(e) {
|
||
e.preventDefault();
|
||
uploadArea.style.borderColor = '';
|
||
uploadArea.style.backgroundColor = '';
|
||
});
|
||
|
||
uploadArea.addEventListener('drop', function(e) {
|
||
e.preventDefault();
|
||
uploadArea.style.borderColor = '';
|
||
uploadArea.style.backgroundColor = '';
|
||
|
||
if (e.dataTransfer.files.length) {
|
||
document.getElementById('configFile').files = e.dataTransfer.files;
|
||
const event = new Event('change');
|
||
document.getElementById('configFile').dispatchEvent(event);
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |