feat: 单词乐园 — PEP 7上 英语学习网站 (看词选图/语法/打卡/SRS复习)
This commit is contained in:
+929
@@ -0,0 +1,929 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>📚 单词乐园 — PEP 7上</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@500;600;700;900&family=Noto+Sans+SC:wght@400;700;900&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { margin:0; padding:0; box-sizing:border-box; }
|
||||
body {
|
||||
font-family:'Noto Sans SC','Inter',sans-serif;
|
||||
background:#0a0a14; min-height:100vh; color:#c8c8d0;
|
||||
overflow-x:hidden;
|
||||
}
|
||||
.app { max-width:820px; margin:0 auto; padding:16px; position:relative; min-height:100vh; }
|
||||
|
||||
/* HEADER */
|
||||
.hd {
|
||||
background:rgba(10,10,26,0.85); border:1px solid rgba(99,102,241,0.12);
|
||||
padding:10px 16px; display:flex; align-items:center; gap:10px; margin-bottom:14px;
|
||||
backdrop-filter:blur(8px); border-radius:6px;
|
||||
}
|
||||
.hd .lg { font-weight:900; font-size:14px; letter-spacing:1px; }
|
||||
.hd .lg .g { color:#6366f1; }
|
||||
.hd .lg .y { color:#f59e0b; }
|
||||
.hd-s { flex:1; }
|
||||
.hd-st { text-align:center; padding:0 8px; border-right:1px solid rgba(255,255,255,0.03); }
|
||||
.hd-st:last-child { border-right:none; }
|
||||
.hd-st .lb { font-size:8px; color:rgba(255,255,255,0.35); letter-spacing:2px; font-weight:700; }
|
||||
.hd-st .vl { font-size:16px; font-weight:900; font-family:'Inter',monospace; }
|
||||
.hd-st .vl.g { color:#6366f1; }
|
||||
.hd-st .vl.y { color:#f59e0b; }
|
||||
|
||||
/* PAGES */
|
||||
.pg { display:none; }
|
||||
.pg.act { display:block; animation:fadeIn 0.3s ease; }
|
||||
@keyframes fadeIn { from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:translateY(0)} }
|
||||
@keyframes pop { 0%{opacity:0;transform:scale(0.85)} 50%{transform:scale(1.05)} 100%{opacity:1;transform:scale(1)} }
|
||||
@keyframes flipIn { 0%{opacity:0;transform:rotateY(90deg)} 100%{opacity:1;transform:rotateY(0)} }
|
||||
@keyframes bIn { 0%{opacity:0;transform:scale(0.3)} 50%{transform:scale(1.1)} 70%{transform:scale(0.9)} 100%{opacity:1;transform:scale(1)} }
|
||||
@keyframes slideUp { from{opacity:0;transform:translateY(20px)} to{opacity:1;transform:translateY(0)} }
|
||||
@keyframes pulse { 0%,100%{transform:scale(1)} 50%{transform:scale(1.05)} }
|
||||
|
||||
/* BTN */
|
||||
.btn {
|
||||
display:inline-flex; align-items:center; gap:6px; padding:10px 20px;
|
||||
border:none; border-radius:6px; font-size:13px; font-weight:700;
|
||||
font-family:inherit; cursor:pointer; transition:all 0.25s;
|
||||
}
|
||||
.btn-p { background:#6366f1; color:#fff; }
|
||||
.btn-p:hover { background:#4f46e5; transform:translateY(-1px); }
|
||||
.btn-o { background:#f59e0b; color:#0a0a14; }
|
||||
.btn-o:hover { background:#d97706; transform:translateY(-1px); }
|
||||
.btn-gh { background:rgba(255,255,255,0.04); color:rgba(255,255,255,0.6); border:1px solid rgba(255,255,255,0.06); }
|
||||
.btn-gh:hover { background:rgba(255,255,255,0.06); color:rgba(255,255,255,0.8); }
|
||||
.btn-s { background:rgba(99,102,241,0.1); color:#6366f1; border:1px solid rgba(99,102,241,0.15); }
|
||||
.btn-s:hover { background:rgba(99,102,241,0.15); }
|
||||
.btn-g { background:rgba(16,185,129,0.1); color:#10b981; border:1px solid rgba(16,185,129,0.15); }
|
||||
.btn-g:hover { background:rgba(16,185,129,0.15); }
|
||||
|
||||
/* HOME */
|
||||
.h-card {
|
||||
background:rgba(10,10,26,0.5); border:1px solid rgba(255,255,255,0.03);
|
||||
border-radius:8px; padding:18px; margin-bottom:12px;
|
||||
}
|
||||
.h-card .ht { font-size:13px; font-weight:700; color:rgba(255,255,255,0.5); letter-spacing:1px; margin-bottom:6px; }
|
||||
.streak-bar {
|
||||
display:flex; gap:6px; margin:8px 0;
|
||||
}
|
||||
.streak-bar .d {
|
||||
width:32px; height:40px; border-radius:4px; display:flex;
|
||||
align-items:center; justify-content:center; font-size:11px; font-weight:700;
|
||||
background:rgba(255,255,255,0.03); color:rgba(255,255,255,0.15);
|
||||
}
|
||||
.streak-bar .d.on { background:rgba(245,158,11,0.12); color:#f59e0b; border:1px solid rgba(245,158,11,0.15); }
|
||||
.streak-bar .d.td { background:rgba(99,102,241,0.12); color:#6366f1; border:1px solid rgba(99,102,241,0.15); animation:pulse 1.5s infinite; }
|
||||
|
||||
.stat-row { display:flex; gap:10px; margin:10px 0; }
|
||||
.stat-box {
|
||||
flex:1; background:rgba(255,255,255,0.02); border-radius:6px; padding:12px; text-align:center;
|
||||
border:1px solid rgba(255,255,255,0.03);
|
||||
}
|
||||
.stat-box .n { font-size:24px; font-weight:900; font-family:'Inter',monospace; }
|
||||
.stat-box .l { font-size:10px; color:rgba(255,255,255,0.3); margin-top:2px; }
|
||||
.stat-box.g { border-left:2px solid #6366f1; }
|
||||
.stat-box.y { border-left:2px solid #f59e0b; }
|
||||
|
||||
/* UNITS */
|
||||
.unit-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
|
||||
.u-card {
|
||||
background:rgba(10,10,26,0.5); border:1px solid rgba(255,255,255,0.03);
|
||||
border-radius:6px; padding:14px; cursor:pointer; transition:all 0.25s; text-align:center;
|
||||
}
|
||||
.u-card:hover { border-color:rgba(99,102,241,0.15); transform:translateY(-2px); }
|
||||
.u-card .un { font-size:22px; margin-bottom:2px; }
|
||||
.u-card .ut { font-size:12px; font-weight:700; color:#fff; }
|
||||
.u-card .ud { font-size:9px; color:rgba(255,255,255,0.25); margin-top:2px; }
|
||||
.u-card .ub { font-size:8px; color:rgba(255,255,255,0.15); margin-top:4px; }
|
||||
.u-card .ub .m { color:#6366f1; }
|
||||
.u-card.done { border-color:rgba(16,185,129,0.1); }
|
||||
.u-card.done .un { opacity:0.5; }
|
||||
|
||||
/* WORD LIST */
|
||||
.wl-h {
|
||||
display:flex; align-items:center; gap:8px; margin-bottom:12px;
|
||||
padding-bottom:10px; border-bottom:1px solid rgba(255,255,255,0.03);
|
||||
}
|
||||
.wl-h h2 { font-size:16px; color:#fff; flex:1; }
|
||||
.wl-h .c { font-size:10px; color:rgba(255,255,255,0.2); font-family:'Inter',monospace; }
|
||||
.wi {
|
||||
display:flex; align-items:center; gap:10px; padding:10px 12px;
|
||||
border-bottom:1px solid rgba(255,255,255,0.02); cursor:pointer;
|
||||
transition:all 0.2s; border-radius:4px;
|
||||
}
|
||||
.wi:hover { background:rgba(255,255,255,0.02); }
|
||||
.wi .we { font-size:28px; flex-shrink:0; width:40px; text-align:center; }
|
||||
.wi .we.s { font-size:18px; }
|
||||
.wi .we.xl { font-size:32px; }
|
||||
.wi .ww { flex:1; }
|
||||
.wi .ww .en { font-size:15px; font-weight:700; color:#fff; }
|
||||
.wi .ww .zh { font-size:12px; color:rgba(255,255,255,0.4); }
|
||||
.wi .ww .ex { font-size:10px; color:rgba(255,255,255,0.15); margin-top:2px; line-height:1.4; }
|
||||
.wi .st { flex-shrink:0; }
|
||||
.wi .st .lvl { display:inline-block; width:18px; height:18px; line-height:18px; text-align:center; border-radius:50%; font-size:9px; font-weight:700; }
|
||||
.wi .st .lvl.n { background:rgba(99,102,241,0.1); color:#6366f1; }
|
||||
.wi .st .lvl.l { background:rgba(16,185,129,0.1); color:#10b981; }
|
||||
.wi .st .lvl.m { background:rgba(245,158,11,0.1); color:#f59e0b; }
|
||||
.wi .st .snd { cursor:pointer; font-size:16px; margin-left:4px; opacity:0.3; transition:opacity 0.2s; }
|
||||
.wi .st .snd:hover { opacity:0.8; }
|
||||
|
||||
/* QUIZ */
|
||||
.qz-h { text-align:center; margin-bottom:14px; }
|
||||
.qz-h .qp { font-size:11px; color:rgba(255,255,255,0.2); font-family:'Inter',monospace; letter-spacing:1px; }
|
||||
.qz-h .qt { font-size:13px; color:rgba(255,255,255,0.4); margin-top:2px; }
|
||||
|
||||
.qz-card {
|
||||
background:rgba(10,10,26,0.5); border:1px solid rgba(255,255,255,0.03);
|
||||
border-radius:12px; padding:28px 20px; text-align:center; margin-bottom:14px;
|
||||
}
|
||||
.qz-card .q-em { font-size:72px; margin-bottom:10px; animation:bIn 0.4s ease; }
|
||||
.qz-card .q-en { font-size:32px; font-weight:900; color:#fff; letter-spacing:-0.5px; animation:bIn 0.4s ease; }
|
||||
.qz-card .q-zh { font-size:18px; color:rgba(255,255,255,0.4); margin-top:4px; }
|
||||
.qz-card .q-snd { font-size:24px; cursor:pointer; display:inline-block; margin-top:8px; padding:8px; border-radius:50%; background:rgba(99,102,241,0.06); transition:background 0.2s; }
|
||||
.qz-card .q-snd:hover { background:rgba(99,102,241,0.12); }
|
||||
|
||||
.qz-ops { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
|
||||
.qz-op {
|
||||
background:rgba(10,10,26,0.5); border:1px solid rgba(255,255,255,0.04);
|
||||
border-radius:8px; padding:14px; cursor:pointer; text-align:center;
|
||||
transition:all 0.25s; display:flex; flex-direction:column; align-items:center; gap:6px;
|
||||
}
|
||||
.qz-op:hover { border-color:rgba(99,102,241,0.2); background:rgba(99,102,241,0.03); }
|
||||
.qz-op .oe { font-size:36px; }
|
||||
.qz-op .ot { font-size:13px; color:#fff; font-weight:600; }
|
||||
.qz-op.cor { border-color:#10b981; background:rgba(16,185,129,0.06); animation:bIn 0.3s ease; }
|
||||
.qz-op.wrg { border-color:#ef4444; background:rgba(239,68,68,0.06); animation:shake 0.4s ease; }
|
||||
.qz-op.disabled { pointer-events:none; opacity:0.5; }
|
||||
@keyframes shake { 0%,100%{transform:translateX(0)} 20%{transform:translateX(-8px)} 40%{transform:translateX(8px)} 60%{transform:translateX(-5px)} 80%{transform:translateX(5px)} }
|
||||
|
||||
.qz-fb {
|
||||
text-align:center; padding:14px; margin-top:10px; border-radius:6px; display:none;
|
||||
}
|
||||
.qz-fb.show { display:block; animation:slideUp 0.3s ease; }
|
||||
.qz-fb.pass { background:rgba(16,185,129,0.04); border:1px solid rgba(16,185,129,0.08); }
|
||||
.qz-fb.fail { background:rgba(239,68,68,0.04); border:1px solid rgba(239,68,68,0.08); }
|
||||
|
||||
/* GRAMMAR */
|
||||
.gr-card {
|
||||
background:rgba(10,10,26,0.5); border:1px solid rgba(255,255,255,0.03);
|
||||
border-radius:8px; padding:16px; margin-bottom:10px;
|
||||
}
|
||||
.gr-card .gt { font-size:15px; font-weight:700; color:#fff; margin-bottom:6px; }
|
||||
.gr-card .gt .gi { font-size:18px; margin-right:6px; }
|
||||
.gr-card .gd { font-size:12px; color:rgba(255,255,255,0.5); line-height:1.7; }
|
||||
.gr-card .ge { display:inline-block; background:rgba(99,102,241,0.06); border:1px solid rgba(99,102,241,0.08); border-radius:3px; padding:2px 8px; font-size:12px; color:#818cf8; font-weight:600; margin:2px; }
|
||||
.gr-card .gr { margin-top:6px; font-size:11px; color:rgba(255,255,255,0.25); border-top:1px solid rgba(255,255,255,0.02); padding-top:6px; }
|
||||
|
||||
/* CHECK-IN DIALOG */
|
||||
.modal {
|
||||
display:none; position:fixed; inset:0; background:rgba(5,5,10,0.85);
|
||||
backdrop-filter:blur(10px); z-index:100; align-items:center; justify-content:center;
|
||||
}
|
||||
.modal.show { display:flex; }
|
||||
.modal-in {
|
||||
background:#0a0a1a; border:1px solid rgba(99,102,241,0.1); border-radius:12px;
|
||||
padding:32px 24px; text-align:center; max-width:340px; width:90%;
|
||||
animation:bIn 0.5s ease;
|
||||
}
|
||||
.modal-in .mi { font-size:64px; margin-bottom:8px; }
|
||||
.modal-in h2 { font-size:20px; color:#fff; margin-bottom:4px; }
|
||||
.modal-in p { font-size:12px; color:rgba(255,255,255,0.35); margin-bottom:14px; }
|
||||
|
||||
/* PROGRESS BAR */
|
||||
.pb { height:4px; background:rgba(255,255,255,0.03); border-radius:2px; margin:6px 0; overflow:hidden; }
|
||||
.pb-f { height:100%; border-radius:2px; background:linear-gradient(90deg,#6366f1,#818cf8); transition:width 0.5s ease; }
|
||||
|
||||
/* RESPONSIVE */
|
||||
@media (max-width:600px) {
|
||||
.unit-grid { grid-template-columns:1fr; }
|
||||
.qz-ops { grid-template-columns:1fr; }
|
||||
.stat-row { flex-direction:column; }
|
||||
.app { padding:10px; }
|
||||
}
|
||||
|
||||
/* CHECK-IN STAR */
|
||||
.ci-star {
|
||||
display:inline-flex; align-items:center; gap:4px;
|
||||
background:rgba(245,158,11,0.06); border:1px solid rgba(245,158,11,0.1);
|
||||
padding:8px 16px; border-radius:6px; cursor:pointer; transition:all 0.25s;
|
||||
}
|
||||
.ci-star:hover { background:rgba(245,158,11,0.1); }
|
||||
.ci-star .ci-e { font-size:20px; }
|
||||
.ci-star .ci-t { font-size:12px; font-weight:700; color:#f59e0b; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="hd">
|
||||
<div class="lg"><span class="g">📚</span> 单词<span class="y">乐园</span></div>
|
||||
<div class="hd-s"></div>
|
||||
<div class="hd-st"><div class="lb">🔥 连续</div><div class="vl y" id="hStreak">0天</div></div>
|
||||
<div class="hd-st"><div class="lb">📖 已学</div><div class="vl g" id="hLearned">0</div></div>
|
||||
</div>
|
||||
|
||||
<!-- HOME PAGE -->
|
||||
<div class="pg act" id="pHome">
|
||||
<div class="h-card">
|
||||
<div class="ht">📅 本周打卡</div>
|
||||
<div class="streak-bar" id="weekBar"></div>
|
||||
<div id="checkInArea"></div>
|
||||
</div>
|
||||
|
||||
<div class="h-card">
|
||||
<div class="ht">📊 学习统计</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-box g"><div class="n" id="sLearned">0</div><div class="l">已学单词</div></div>
|
||||
<div class="stat-box y"><div class="n" id="sMastered">0</div><div class="l">已掌握</div></div>
|
||||
<div class="stat-box" style="border-left:2px solid rgba(255,255,255,0.06);"><div class="n" id="sDue">0</div><div class="l">待复习</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-card">
|
||||
<div class="ht">📚 单元列表 · PEP 7上</div>
|
||||
<div class="unit-grid" id="unitGrid"></div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||
<button class="btn btn-gh" id="btnReview" style="flex:1;">🔄 复习</button>
|
||||
<button class="btn btn-gh" id="btnGrammar" style="flex:1;">📝 语法</button>
|
||||
<button class="btn btn-gh" id="btnClear" style="flex:1;">🗑️ 清除进度</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- UNIT PAGE -->
|
||||
<div class="pg" id="pUnit">
|
||||
<button class="btn btn-gh" id="bkHome" style="margin-bottom:10px;">← 返回</button>
|
||||
<div class="wl-h" id="unitHeader"></div>
|
||||
<div id="wordList"></div>
|
||||
<button class="btn btn-p" id="startQuiz" style="width:100%;margin-top:8px;">🎯 开始测这单元</button>
|
||||
</div>
|
||||
|
||||
<!-- QUIZ PAGE -->
|
||||
<div class="pg" id="pQuiz">
|
||||
<div class="qz-h"><div class="qp" id="qProg"></div><div class="qt" id="qMode"></div></div>
|
||||
<div class="qz-card" id="qCard"></div>
|
||||
<div class="qz-ops" id="qOpts"></div>
|
||||
<div class="qz-fb" id="qFb"></div>
|
||||
</div>
|
||||
|
||||
<!-- GRAMMAR PAGE -->
|
||||
<div class="pg" id="pGrammar">
|
||||
<button class="btn btn-gh" id="bkHomeG" style="margin-bottom:10px;">← 返回</button>
|
||||
<h2 style="font-size:18px;color:#fff;margin-bottom:10px;">📝 语法知识点 · PEP 7上</h2>
|
||||
<div id="grammarList"></div>
|
||||
</div>
|
||||
|
||||
<!-- REVIEW PAGE -->
|
||||
<div class="pg" id="pReview">
|
||||
<button class="btn btn-gh" id="bkHomeR" style="margin-bottom:10px;">← 返回</button>
|
||||
<h2 style="font-size:18px;color:#fff;margin-bottom:10px;">🔄 复习</h2>
|
||||
<div id="reviewContent"></div>
|
||||
</div>
|
||||
|
||||
<!-- CHECK-IN MODAL -->
|
||||
<div class="modal" id="ciModal">
|
||||
<div class="modal-in">
|
||||
<div class="mi">🌟</div>
|
||||
<h2>打卡成功!</h2>
|
||||
<p id="ciMsg">今天又学了新单词!</p>
|
||||
<button class="btn btn-o" id="ciBtn">继续学习</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ===== DATA: PEP Go for it! 7上 =====
|
||||
const UNITS = [
|
||||
{ id:'S1', em:'☀️', title:'Good Morning!', sub:'Starter 1 · 字母 & 问候', words:[
|
||||
{e:'good',c:'好的',em:'👍',s:'Good morning, class! 早上好,同学们!'},
|
||||
{e:'morning',c:'早上',em:'🌅',s:'I have breakfast in the morning. 我早上吃早餐。'},
|
||||
{e:'afternoon',c:'下午',em:'☀️',s:'Good afternoon, Miss Li! 下午好,李老师!'},
|
||||
{e:'evening',c:'晚上',em:'🌆',s:'Good evening, Mom! 晚上好,妈妈!'},
|
||||
{e:'fine',c:'好的;健康的',em:'💪',s:'I am fine, thank you. 我很好,谢谢。'},
|
||||
{e:'OK',c:'好;行',em:'👌',s:'OK, let\'s go! 好的,我们走吧!'},
|
||||
{e:'hello',c:'你好',em:'👋',s:'Hello, my friend! 你好,我的朋友!'},
|
||||
{e:'hi',c:'嗨',em:'✋',s:'Hi, how are you? 嗨,你好吗?'},
|
||||
{e:'thanks',c:'谢谢',em:'🙏',s:'Thanks for your help. 谢谢你的帮助。'},
|
||||
{e:'how',c:'怎样;怎么',em:'❓',s:'How are you today? 你今天怎么样?'},
|
||||
{e:'am',c:'是',em:'✅',s:'I am a student. 我是一个学生。'},
|
||||
{e:'are',c:'是',em:'✅',s:'You are my friend. 你是我的朋友。'},
|
||||
]},
|
||||
{ id:'S2', em:'✏️', title:'What\'s This?', sub:'Starter 2 · 物品问答', words:[
|
||||
{e:'what',c:'什么',em:'❓',s:'What is this? 这是什么?'},
|
||||
{e:'is',c:'是',em:'✅',s:'This is a book. 这是一本书。'},
|
||||
{e:'this',c:'这个',em:'👆',s:'This is my pen. 这是我的钢笔。'},
|
||||
{e:'that',c:'那个',em:'👉',s:'That is a ruler. 那是一把尺子。'},
|
||||
{e:'it',c:'它',em:'🎯',s:'It is a map. 它是一张地图。'},
|
||||
{e:'a',c:'一个',em:'1️⃣',s:'I have a pen. 我有一支钢笔。'},
|
||||
{e:'an',c:'一个(元音前)',em:'1️⃣',s:'It is an orange. 它是一个橙子。'},
|
||||
{e:'map',c:'地图',em:'🗺️',s:'This is a map of China. 这是一张中国地图。'},
|
||||
{e:'key',c:'钥匙',em:'🔑',s:'Where is my key? 我的钥匙在哪里?'},
|
||||
{e:'jacket',c:'夹克衫',em:'🧥',s:'My jacket is blue. 我的夹克衫是蓝色的。'},
|
||||
{e:'cup',c:'杯子',em:'🥤',s:'This cup is red. 这个杯子是红色的。'},
|
||||
{e:'ruler',c:'尺子',em:'📏',s:'I need a ruler. 我需要一把尺子。'},
|
||||
{e:'pen',c:'钢笔',em:'🖊️',s:'This pen is black. 这支钢笔是黑色的。'},
|
||||
{e:'orange',c:'橙子',em:'🍊',s:'The orange is sweet. 这个橙子很甜。'},
|
||||
{e:'spell',c:'拼写',em:'🔤',s:'How do you spell it? 你怎么拼写它?'},
|
||||
{e:'please',c:'请',em:'🙏',s:'Sit down, please. 请坐下。'},
|
||||
]},
|
||||
{ id:'S3', em:'🌈', title:'What Color?', sub:'Starter 3 · 颜色', words:[
|
||||
{e:'color',c:'颜色',em:'🎨',s:'What color is it? 它是什么颜色?'},
|
||||
{e:'red',c:'红色',em:'🔴',s:'The apple is red. 这个苹果是红色的。'},
|
||||
{e:'yellow',c:'黄色',em:'🟡',s:'The sun is yellow. 太阳是黄色的。'},
|
||||
{e:'green',c:'绿色',em:'🟢',s:'The tree is green. 树是绿色的。'},
|
||||
{e:'blue',c:'蓝色',em:'🔵',s:'The sky is blue. 天空是蓝色的。'},
|
||||
{e:'black',c:'黑色',em:'⚫',s:'My cat is black. 我的猫是黑色的。'},
|
||||
{e:'white',c:'白色',em:'⚪',s:'Snow is white. 雪是白色的。'},
|
||||
{e:'purple',c:'紫色',em:'🟣',s:'I like purple flowers. 我喜欢紫色的花。'},
|
||||
{e:'brown',c:'棕色',em:'🟤',s:'The dog is brown. 这只狗是棕色的。'},
|
||||
]},
|
||||
{ id:'U1', em:'👋', title:'My Name\'s Gina', sub:'Unit 1 · 自我介绍 · be动词', words:[
|
||||
{e:'name',c:'名字',em:'🏷️',s:'My name is Tom. 我的名字是汤姆。'},
|
||||
{e:'my',c:'我的',em:'👤',s:'This is my book. 这是我的书。'},
|
||||
{e:'your',c:'你的',em:'👉',s:'What is your name? 你叫什么名字?'},
|
||||
{e:'his',c:'他的',em:'👦',s:'His name is Mike. 他的名字是迈克。'},
|
||||
{e:'her',c:'她的',em:'👧',s:'Her bag is red. 她的包是红色的。'},
|
||||
{e:'he',c:'他',em:'👦',s:'He is my friend. 他是我的朋友。'},
|
||||
{e:'she',c:'她',em:'👧',s:'She is a teacher. 她是一位老师。'},
|
||||
{e:'yes',c:'是的',em:'👍',s:'Yes, I can. 是的,我能。'},
|
||||
{e:'no',c:'不;不是',em:'👎',s:'No, it isn\'t. 不,它不是。'},
|
||||
{e:'first',c:'第一的;首先',em:'🥇',s:'This is my first book. 这是我的第一本书。'},
|
||||
{e:'last',c:'最后的',em:'🏁',s:'My last name is Smith. 我姓史密斯。'},
|
||||
{e:'friend',c:'朋友',em:'🤝',s:'She is my good friend. 她是我的好朋友。'},
|
||||
{e:'zero',c:'零',em:'0️⃣',s:'My number has a zero. 我的号码里有一个零。'},
|
||||
{e:'one',c:'一',em:'1️⃣',s:'I have one apple. 我有一个苹果。'},
|
||||
{e:'two',c:'二',em:'2️⃣',s:'Two cats are playing. 两只猫在玩。'},
|
||||
{e:'three',c:'三',em:'3️⃣',s:'Three birds are singing. 三只鸟在唱歌。'},
|
||||
{e:'four',c:'四',em:'4️⃣',s:'I have four pens. 我有四支钢笔。'},
|
||||
{e:'five',c:'五',em:'5️⃣',s:'Five stars for you! 给你五颗星!'},
|
||||
{e:'six',c:'六',em:'6️⃣',s:'Six books on the desk. 六本书在桌子上。'},
|
||||
{e:'seven',c:'七',em:'7️⃣',s:'Seven days in a week. 一周有七天。'},
|
||||
{e:'eight',c:'八',em:'8️⃣',s:'I am eight years old. 我八岁了。'},
|
||||
{e:'nine',c:'九',em:'9️⃣',s:'Nine + one = ten. 九加一等于十。'},
|
||||
{e:'telephone',c:'电话',em:'📞',s:'My telephone number is 123. 我的电话号码是123。'},
|
||||
{e:'number',c:'号码;数字',em:'🔢',s:'What is your number? 你的号码是多少?'},
|
||||
]},
|
||||
{ id:'U2', em:'👨👩👧👦', title:'This Is My Sister', sub:'Unit 2 · 家庭成员 · 指示代词', words:[
|
||||
{e:'sister',c:'姐妹',em:'👧',s:'My sister is kind. 我的姐姐很善良。'},
|
||||
{e:'brother',c:'兄弟',em:'👦',s:'My brother likes sports. 我的哥哥喜欢运动。'},
|
||||
{e:'mother',c:'母亲',em:'👩',s:'My mother cooks dinner. 我妈妈做晚饭。'},
|
||||
{e:'father',c:'父亲',em:'👨',s:'My father is tall. 我爸爸很高。'},
|
||||
{e:'parent',c:'父亲或母亲',em:'👪',s:'My parents are at home. 我的父母在家。'},
|
||||
{e:'grandmother',c:'祖母',em:'👵',s:'My grandmother tells stories. 我奶奶讲故事。'},
|
||||
{e:'grandfather',c:'祖父',em:'👴',s:'My grandfather reads newspaper. 我爷爷看报纸。'},
|
||||
{e:'family',c:'家庭',em:'👪',s:'My family has four people. 我家有四口人。'},
|
||||
{e:'those',c:'那些',em:'👉👈',s:'Those are my friends. 那些是我的朋友。'},
|
||||
{e:'these',c:'这些',em:'👈👈',s:'These are my books. 这些是我的书。'},
|
||||
{e:'who',c:'谁',em:'❓',s:'Who is she? 她是谁?'},
|
||||
{e:'aunt',c:'阿姨;姑姑',em:'👩',s:'My aunt is a doctor. 我的阿姨是医生。'},
|
||||
{e:'uncle',c:'叔叔;舅舅',em:'👨',s:'My uncle drives a car. 我的叔叔开车。'},
|
||||
{e:'son',c:'儿子',em:'👦',s:'He is her son. 他是她的儿子。'},
|
||||
{e:'daughter',c:'女儿',em:'👧',s:'She is my daughter. 她是我的女儿。'},
|
||||
{e:'picture',c:'图片;照片',em:'🖼️',s:'This is a picture of my family. 这是我家的照片。'},
|
||||
]},
|
||||
{ id:'U3', em:'🎒', title:'Is This Your Pencil?', sub:'Unit 3 · 学习用品 · 物主代词', words:[
|
||||
{e:'pencil',c:'铅笔',em:'✏️',s:'Is this your pencil? 这是你的铅笔吗?'},
|
||||
{e:'book',c:'书',em:'📖',s:'I read a book every day. 我每天读一本书。'},
|
||||
{e:'eraser',c:'橡皮',em:'🧹',s:'I need an eraser. 我需要一块橡皮。'},
|
||||
{e:'schoolbag',c:'书包',em:'🎒',s:'My schoolbag is heavy. 我的书包很重。'},
|
||||
{e:'dictionary',c:'字典',em:'📕',s:'Look it up in the dictionary. 在字典里查一下。'},
|
||||
{e:'teacher',c:'老师',em:'👩🏫',s:'My teacher is nice. 我的老师很好。'},
|
||||
{e:'student',c:'学生',em:'🧑🎓',s:'He is a good student. 他是个好学生。'},
|
||||
{e:'classroom',c:'教室',em:'🏫',s:'Our classroom is big. 我们的教室很大。'},
|
||||
{e:'library',c:'图书馆',em:'📚',s:'I like the school library. 我喜欢学校图书馆。'},
|
||||
{e:'watch',c:'手表',em:'⌚',s:'My watch is new. 我的手表是新的。'},
|
||||
{e:'ring',c:'戒指',em:'💍',s:'This is a gold ring. 这是一个金戒指。'},
|
||||
{e:'bag',c:'包',em:'👜',s:'I have a new bag. 我有一个新包。'},
|
||||
{e:'baseball',c:'棒球',em:'⚾',s:'Let\'s play baseball. 我们打棒球吧。'},
|
||||
{e:'help',c:'帮助',em:'🤲',s:'Can you help me? 你能帮我吗?'},
|
||||
{e:'welcome',c:'欢迎',em:'👏',s:'Welcome to our school! 欢迎来到我们学校!'},
|
||||
]},
|
||||
{ id:'U4', em:'🛏️', title:'Where\'s My Schoolbag?', sub:'Unit 4 · 方位介词', words:[
|
||||
{e:'where',c:'在哪里',em:'📍',s:'Where is my pen? 我的钢笔在哪里?'},
|
||||
{e:'schoolbag',c:'书包',em:'🎒',s:'Where is my schoolbag? 我的书包在哪里?'},
|
||||
{e:'desk',c:'书桌',em:'🪑',s:'My book is on the desk. 我的书在书桌上。'},
|
||||
{e:'table',c:'桌子',em:'🪑',s:'The cat is under the table. 猫在桌子下面。'},
|
||||
{e:'chair',c:'椅子',em:'💺',s:'Sit on the chair, please. 请坐在椅子上。'},
|
||||
{e:'bookcase',c:'书柜',em:'📚',s:'The books are in the bookcase. 书在书柜里。'},
|
||||
{e:'bed',c:'床',em:'🛏️',s:'I sleep on the bed. 我在床上睡觉。'},
|
||||
{e:'sofa',c:'沙发',em:'🛋️',s:'The cat is on the sofa. 猫在沙发上。'},
|
||||
{e:'dresser',c:'梳妆台',em:'🪞',s:'The comb is on the dresser. 梳子在梳妆台上。'},
|
||||
{e:'drawer',c:'抽屉',em:'🗄️',s:'It\'s in the drawer. 它在抽屉里。'},
|
||||
{e:'plant',c:'植物',em:'🌱',s:'The plant is near the window. 植物在窗户旁边。'},
|
||||
{e:'under',c:'在…下面',em:'⬇️',s:'The ball is under the chair. 球在椅子下面。'},
|
||||
{e:'on',c:'在…上面',em:'⬆️',s:'The book is on the table. 书在桌子上面。'},
|
||||
{e:'in',c:'在…里面',em:'📥',s:'The pen is in the bag. 钢笔在包里。'},
|
||||
{e:'behind',c:'在…后面',em:'⬅️',s:'The dog is behind the door. 狗在门后面。'},
|
||||
{e:'next to',c:'在…旁边',em:'➡️',s:'The desk is next to the bed. 书桌在床旁边。'},
|
||||
]},
|
||||
{ id:'U5', em:'⚽', title:'Do You Have a Soccer Ball?', sub:'Unit 5 · 体育 · 一般现在时(有)', words:[
|
||||
{e:'have',c:'有',em:'🤲',s:'I have a soccer ball. 我有一个足球。'},
|
||||
{e:'has',c:'有(三单)',em:'🤲',s:'He has a basketball. 他有一个篮球。'},
|
||||
{e:'soccer',c:'英式足球',em:'⚽',s:'Let\'s play soccer! 我们踢足球吧!'},
|
||||
{e:'basketball',c:'篮球',em:'🏀',s:'I like basketball. 我喜欢篮球。'},
|
||||
{e:'volleyball',c:'排球',em:'🏐',s:'She plays volleyball well. 她排球打得很好。'},
|
||||
{e:'tennis',c:'网球',em:'🎾',s:'Tennis is fun. 网球很有趣。'},
|
||||
{e:'baseball',c:'棒球',em:'⚾',s:'Baseball is popular in the US. 棒球在美国很流行。'},
|
||||
{e:'ball',c:'球',em:'⚪',s:'The ball is red. 这个球是红色的。'},
|
||||
{e:'sport',c:'运动',em:'🏃',s:'My favorite sport is swimming. 我最喜欢的运动是游泳。'},
|
||||
{e:'play',c:'玩;打',em:'🎮',s:'I play soccer after school. 我放学后踢足球。'},
|
||||
{e:'watch',c:'观看',em:'👀',s:'We watch TV at home. 我们在家看电视。'},
|
||||
{e:'game',c:'比赛;游戏',em:'🎯',s:'The game is exciting. 比赛很激动人心。'},
|
||||
{e:'fun',c:'有趣的;乐趣',em:'😄',s:'Playing soccer is fun. 踢足球很有趣。'},
|
||||
{e:'boring',c:'无聊的',em:'😴',s:'This movie is boring. 这部电影很无聊。'},
|
||||
{e:'difficult',c:'困难的',em:'😰',s:'Math is difficult for me. 数学对我来说很难。'},
|
||||
{e:'easy',c:'容易的',em:'😊',s:'This test is easy. 这个测试很简单。'},
|
||||
{e:'relaxing',c:'放松的',em:'😌',s:'Music is relaxing. 音乐让人放松。'},
|
||||
]},
|
||||
{ id:'U6', em:'🍎', title:'Do You Like Bananas?', sub:'Unit 6 · 食物 · 可数/不可数名词', words:[
|
||||
{e:'like',c:'喜欢',em:'❤️',s:'I like apples. 我喜欢苹果。'},
|
||||
{e:'banana',c:'香蕉',em:'🍌',s:'Bananas are yellow. 香蕉是黄色的。'},
|
||||
{e:'apple',c:'苹果',em:'🍎',s:'An apple a day keeps the doctor away. 一天一苹果,医生远离我。'},
|
||||
{e:'orange',c:'橙子',em:'🍊',s:'This orange is sweet. 这个橙子很甜。'},
|
||||
{e:'pear',c:'梨',em:'🍐',s:'The pear is juicy. 这个梨很多汁。'},
|
||||
{e:'strawberry',c:'草莓',em:'🍓',s:'I like strawberries. 我喜欢草莓。'},
|
||||
{e:'hamburger',c:'汉堡包',em:'🍔',s:'Let\'s eat hamburgers. 我们吃汉堡吧。'},
|
||||
{e:'rice',c:'米饭',em:'🍚',s:'I eat rice for lunch. 我午饭吃米饭。'},
|
||||
{e:'bread',c:'面包',em:'🍞',s:'Bread is good for breakfast. 面包适合当早餐。'},
|
||||
{e:'milk',c:'牛奶',em:'🥛',s:'I drink milk every day. 我每天喝牛奶。'},
|
||||
{e:'water',c:'水',em:'💧',s:'Drink more water. 多喝水。'},
|
||||
{e:'egg',c:'鸡蛋',em:'🥚',s:'I have an egg for breakfast. 我早餐吃一个鸡蛋。'},
|
||||
{e:'chicken',c:'鸡肉',em:'🍗',s:'Chicken is delicious. 鸡肉很好吃。'},
|
||||
{e:'salad',c:'沙拉',em:'🥗',s:'I like fruit salad. 我喜欢水果沙拉。'},
|
||||
{e:'ice cream',c:'冰淇淋',em:'🍦',s:'Ice cream is cold and sweet. 冰淇淋又冷又甜。'},
|
||||
{e:'healthy',c:'健康的',em:'💪',s:'Eating vegetables is healthy. 吃蔬菜是健康的。'},
|
||||
{e:'food',c:'食物',em:'🍽️',s:'Chinese food is delicious. 中国食物很好吃。'},
|
||||
{e:'fruit',c:'水果',em:'🍎',s:'Fruit is good for us. 水果对我们有好处。'},
|
||||
{e:'vegetable',c:'蔬菜',em:'🥦',s:'Eat more vegetables. 多吃蔬菜。'},
|
||||
{e:'breakfast',c:'早餐',em:'🌅',s:'I have breakfast at 7. 我七点吃早餐。'},
|
||||
{e:'lunch',c:'午餐',em:'☀️',s:'We have lunch at school. 我们在学校吃午餐。'},
|
||||
{e:'dinner',c:'晚餐',em:'🌙',s:'Family dinner is warm. 家庭晚餐很温馨。'},
|
||||
]},
|
||||
{ id:'U7', em:'👕', title:'How Much Are These Socks?', sub:'Unit 7 · 购物 · 价格', words:[
|
||||
{e:'much',c:'多;大量',em:'📊',s:'How much is it? 它多少钱?'},
|
||||
{e:'how much',c:'多少钱',em:'💰',s:'How much are these shoes? 这些鞋多少钱?'},
|
||||
{e:'dollar',c:'美元',em:'💵',s:'It is five dollars. 它五美元。'},
|
||||
{e:'big',c:'大的',em:'🐘',s:'The store is big. 这个商店很大。'},
|
||||
{e:'small',c:'小的',em:'🐭',s:'I need a small size. 我需要小号。'},
|
||||
{e:'long',c:'长的',em:'📏',s:'This ruler is long. 这把尺子很长。'},
|
||||
{e:'short',c:'短的;矮的',em:'📐',s:'The pencil is short. 这铅笔短了。'},
|
||||
{e:'tall',c:'高的',em:'🗼',s:'My father is tall. 我爸爸很高。'},
|
||||
{e:'socks',c:'袜子',em:'🧦',s:'These socks are warm. 这些袜子很暖和。'},
|
||||
{e:'shoes',c:'鞋子',em:'👟',s:'My shoes are new. 我的鞋子是新的。'},
|
||||
{e:'shirt',c:'衬衫',em:'👔',s:'Your shirt is nice. 你的衬衫很漂亮。'},
|
||||
{e:'T-shirt',c:'T恤',em:'👕',s:'I like your T-shirt. 我喜欢你的T恤。'},
|
||||
{e:'sweater',c:'毛衣',em:'🧶',s:'This sweater is soft. 这件毛衣很柔软。'},
|
||||
{e:'jacket',c:'夹克',em:'🧥',s:'Put on your jacket. 穿上你的夹克。'},
|
||||
{e:'skirt',c:'裙子',em:'👗',s:'She wears a red skirt. 她穿一条红裙子。'},
|
||||
{e:'hat',c:'帽子',em:'🎩',s:'This hat is cool. 这顶帽子很酷。'},
|
||||
{e:'price',c:'价格',em:'🏷️',s:'The price is good. 价格不错。'},
|
||||
{e:'buy',c:'买',em:'🛒',s:'I want to buy a book. 我想买一本书。'},
|
||||
{e:'sell',c:'卖',em:'🏪',s:'The store sells shoes. 这家店卖鞋。'},
|
||||
{e:'cheap',c:'便宜的',em:'💲',s:'This pen is cheap. 这支钢笔很便宜。'},
|
||||
{e:'expensive',c:'昂贵的',em:'💎',s:'The watch is expensive. 这块手表很贵。'},
|
||||
]},
|
||||
{ id:'U8', em:'🎂', title:'When Is Your Birthday?', sub:'Unit 8 · 日期 · 序数词', words:[
|
||||
{e:'when',c:'什么时候',em:'📅',s:'When is your birthday? 你的生日是什么时候?'},
|
||||
{e:'birthday',c:'生日',em:'🎂',s:'Happy birthday to you! 祝你生日快乐!'},
|
||||
{e:'month',c:'月份',em:'📅',s:'There are 12 months in a year. 一年有12个月。'},
|
||||
{e:'January',c:'一月',em:'🎊',s:'New Year is in January. 新年在一月。'},
|
||||
{e:'February',c:'二月',em:'💕',s:'February is cold. 二月很冷。'},
|
||||
{e:'March',c:'三月',em:'🌸',s:'Spring starts in March. 春天从三月开始。'},
|
||||
{e:'April',c:'四月',em:'🌧️',s:'April showers bring May flowers. 四月雨带来五月花。'},
|
||||
{e:'May',c:'五月',em:'🌺',s:'May is a beautiful month. 五月是美丽的月份。'},
|
||||
{e:'June',c:'六月',em:'☀️',s:'School ends in June. 学校六月放假。'},
|
||||
{e:'July',c:'七月',em:'🏖️',s:'July is very hot. 七月很热。'},
|
||||
{e:'August',c:'八月',em:'🌞',s:'We go swimming in August. 我们八月去游泳。'},
|
||||
{e:'September',c:'九月',em:'🍂',s:'School starts in September. 学校九月开学。'},
|
||||
{e:'October',c:'十月',em:'🎃',s:'Halloween is in October. 万圣节在十月。'},
|
||||
{e:'November',c:'十一月',em:'🍁',s:'November is cool. 十一月很凉爽。'},
|
||||
{e:'December',c:'十二月',em:'🎄',s:'Christmas is in December. 圣诞节在十二月。'},
|
||||
{e:'first',c:'第一',em:'🥇',s:'I am the first in the race. 我在比赛中得第一。'},
|
||||
{e:'second',c:'第二',em:'🥈',s:'February is the second month. 二月是第二个月。'},
|
||||
{e:'third',c:'第三',em:'🥉',s:'He got third place. 他得了第三名。'},
|
||||
{e:'party',c:'聚会',em:'🎉',s:'Let\'s have a birthday party! 我们开生日派对吧!'},
|
||||
{e:'happy',c:'快乐的',em:'😊',s:'I am happy today. 我今天很开心。'},
|
||||
{e:'old',c:'老的;…岁的',em:'🎂',s:'I am twelve years old. 我十二岁了。'},
|
||||
{e:'age',c:'年龄',em:'🎂',s:'What is your age? 你多大了?'},
|
||||
]},
|
||||
{ id:'U9', em:'📚', title:'My Favorite Subject', sub:'Unit 9 · 科目 · 特殊疑问句', words:[
|
||||
{e:'favorite',c:'最喜欢的',em:'⭐',s:'My favorite color is blue. 我最喜欢的颜色是蓝色。'},
|
||||
{e:'subject',c:'科目',em:'📚',s:'My favorite subject is English. 我最喜欢的科目是英语。'},
|
||||
{e:'math',c:'数学',em:'🔢',s:'Math is interesting. 数学很有趣。'},
|
||||
{e:'English',c:'英语',em:'🇬🇧',s:'I speak English every day. 我每天说英语。'},
|
||||
{e:'Chinese',c:'语文;中文',em:'🇨🇳',s:'Chinese is my first language. 中文是我的母语。'},
|
||||
{e:'science',c:'科学',em:'🔬',s:'Science is fun. 科学很有趣。'},
|
||||
{e:'history',c:'历史',em:'📜',s:'I like history class. 我喜欢历史课。'},
|
||||
{e:'music',c:'音乐',em:'🎵',s:'Music makes me happy. 音乐让我快乐。'},
|
||||
{e:'art',c:'美术',em:'🎨',s:'I draw pictures in art class. 我在美术课上画画。'},
|
||||
{e:'P.E.',c:'体育',em:'🏃',s:'P.E. is my favorite. 体育是我最喜欢的。'},
|
||||
{e:'geography',c:'地理',em:'🌍',s:'Geography is about the world. 地理是关于世界的。'},
|
||||
{e:'biology',c:'生物',em:'🧬',s:'Biology studies living things. 生物学研究生物。'},
|
||||
{e:'interesting',c:'有趣的',em:'🤔',s:'This book is interesting. 这本书很有趣。'},
|
||||
{e:'useful',c:'有用的',em:'🛠️',s:'English is useful. 英语很有用。'},
|
||||
{e:'exciting',c:'令人兴奋的',em:'🎉',s:'The game is exciting. 比赛很激动人心。'},
|
||||
{e:'because',c:'因为',em:'🤷',s:'I like it because it\'s fun. 我喜欢它因为它有趣。'},
|
||||
{e:'why',c:'为什么',em:'❓',s:'Why do you like English? 你为什么喜欢英语?'},
|
||||
{e:'teacher',c:'老师',em:'👩🏫',s:'My teacher is very kind. 我的老师很和蔼。'},
|
||||
{e:'lesson',c:'课',em:'📖',s:'We have six lessons a day. 我们一天上六节课。'},
|
||||
{e:'finish',c:'完成',em:'✅',s:'I finish my homework. 我完成了作业。'},
|
||||
]},
|
||||
];
|
||||
|
||||
// ===== GRAMMAR DATA =====
|
||||
const GRAMMAR = [
|
||||
{icon:'✅', title:'be动词 (am/is/are)', detail:'be动词是英语中最基本的系动词,意思为"是"。',
|
||||
examples:['I am a student. (我是学生。)','You are my friend. (你是我的朋友。)','He/She/It is nice. (他/她/它很好。)','We/You/They are happy. (我们/你们/他们很开心。)'],
|
||||
note:'口诀:I用am,you用are,is连着he/she/it;复数都用are。'},
|
||||
{icon:'❓', title:'一般疑问句 (Yes/No问句)', detail:'把be动词提到主语前,句尾加问号。',
|
||||
examples:['Are you a student? (你是学生吗?) → Yes, I am. / No, I\'m not.','Is this your pen? (这是你的钢笔吗?) → Yes, it is. / No, it isn\'t.'],
|
||||
note:'回答用Yes/No + 主语 + be动词的肯定/否定形式。'},
|
||||
{icon:'🔤', title:'名词复数', detail:'可数名词变复数有规则变化和不规则变化。',
|
||||
examples:['一般+s: book→books, pen→pens','以s/x/sh/ch结尾+es: bus→buses, box→boxes','以辅音+y变y为i+es: family→families','不规则: child→children, man→men, woman→women'],
|
||||
note:'不可数名词(water, rice, bread等)没有复数形式。'},
|
||||
{icon:'👤', title:'物主代词 (my/your/his/her)', detail:'物主代词表示"…的",用于名词前。',
|
||||
examples:['This is my book. (这是我的书。)','Is that your pen? (那是你的钢笔吗?)','Her name is Mary. (她的名字是玛丽。)','Our school is big. (我们的学校很大。)'],
|
||||
note:'形容词性物主代词:my, your, his, her, its, our, your, their。后面必须跟名词。'},
|
||||
{icon:'📍', title:'方位介词 (in/on/under/between)', detail:'表示位置关系的词,常和the连用。',
|
||||
examples:['The book is on the desk. (书在书桌上。)','The cat is under the table. (猫在桌子下面。)','The pen is in the bag. (钢笔在包里。)','The ball is behind the door. (球在门后面。)'],
|
||||
note:'口诀:on在上面,in在里面,under在下面,behind在后面,next to在旁边。'},
|
||||
{icon:'🔄', title:'一般现在时 (do/does)', detail:'表示经常发生的动作或存在的状态。主语是三单时动词加s/es。',
|
||||
examples:['I like apples. (我喜欢苹果。)','He likes bananas. (他喜欢香蕉。)','She goes to school by bus. (她坐公交上学。)','They play soccer on Sunday. (他们周日踢足球。)'],
|
||||
note:'三单动词规则:一般+s (like→likes);s/x/sh/ch+es (watch→watches);辅音+y→ies (study→studies)。'},
|
||||
{icon:'❌', title:'否定句 (don\'t/doesn\'t)', detail:'一般现在时的否定:在动词前加don\'t/doesn\'t,动词恢复原形。',
|
||||
examples:['I don\'t like milk. (我不喜欢牛奶。)','He doesn\'t like sports. (他不喜欢运动。)','She doesn\'t go to school on Sunday. (她周日不上学。)'],
|
||||
note:'doesn\'t后动词必须用原形!不能说"he doesn\'t likes"。'},
|
||||
{icon:'🎯', title:'特殊疑问句 (What/Where/Who/When/Why)', detail:'用疑问词开头的问句,回答不能只用Yes/No。',
|
||||
examples:['What is your name? (你叫什么?) → My name is Tom.','Where is my bag? (我的包在哪?) → It\'s on the desk.','Who is she? (她是谁?) → She is my sister.','When is your birthday? (你的生日在哪天?) → It\'s in May.','Why do you like English? (你为什么喜欢英语?) → Because it\'s fun.'],
|
||||
note:'特殊疑问词:What(什么), Where(哪里), Who(谁), When(何时), Why(为什么), How(怎样), How much(多少钱)。'},
|
||||
{icon:'🧮', title:'序数词 (first/second/third)', detail:'表示顺序的数词,前面通常加the。',
|
||||
examples:['first (1st) 第一','second (2nd) 第二','third (3rd) 第三','fourth (4th) 第四','fifth (5th) 第五'],
|
||||
note:'基数词变序数词口诀:一二三特殊记,th要从四加起,ve要用f替,y变ie再加th。'},
|
||||
{icon:'🤔', title:'情态动词 can', detail:'can表示能力"能、会",后面接动词原形。',
|
||||
examples:['I can swim. (我会游泳。)','She can speak English. (她会说英语。)','Can you play soccer? (你会踢足球吗?) → Yes, I can. / No, I can\'t.'],
|
||||
note:'can后面必跟动词原形!否定形式can\'t = cannot。'},
|
||||
];
|
||||
|
||||
// ===== STATE =====
|
||||
const DAYS = ['日','一','二','三','四','五','六'];
|
||||
let S = {};
|
||||
function ld() { try { const d = localStorage.getItem('wordLe'); if (d) { S = JSON.parse(d); } } catch(e) {} }
|
||||
function sv() { try { localStorage.setItem('wordLe', JSON.stringify(S)); } catch(e) {} }
|
||||
function initState() {
|
||||
const def = {
|
||||
learned: {}, // { "U1_0": { level:0..3, lastReview:0, nextReview:0, correct:0, wrong:0 } }
|
||||
daily: {}, // { "2026-06-29": { learned:5, quiz:10, correct:8 } }
|
||||
streak: 0,
|
||||
lastCheckIn: 0,
|
||||
totalQuiz: 0,
|
||||
totalCorrect: 0,
|
||||
};
|
||||
if (!S.learned) Object.assign(S, def);
|
||||
}
|
||||
|
||||
// ===== HELPERS =====
|
||||
function today() { return new Date().toISOString().slice(0,10); }
|
||||
function wIdx(d) { return d.getDay(); }
|
||||
function weekDates() {
|
||||
const d = new Date(); const arr = [];
|
||||
for (let i = 0; i < 7; i++) { const c = new Date(d); c.setDate(d.getDate() - d.getDay() + i); arr.push(c); }
|
||||
return arr;
|
||||
}
|
||||
function shuffle(a) { for (let i = a.length-1; i > 0; i--) { const j = Math.floor(Math.random()*(i+1)); [a[i],a[j]] = [a[j],a[i]]; } return a; }
|
||||
function uid(ui, wi) { return ui+'_'+wi; }
|
||||
function allWords() { const r=[]; UNITS.forEach((u,ui)=>u.words.forEach((w,wi)=>r.push({...w,ui,wi}))); return r; }
|
||||
function speak(text, cb) {
|
||||
if (!window.speechSynthesis) return;
|
||||
window.speechSynthesis.cancel();
|
||||
const u = new SpeechSynthesisUtterance(text);
|
||||
u.lang = 'en-US'; u.rate = 0.9;
|
||||
if (cb) u.onend = cb;
|
||||
window.speechSynthesis.speak(u);
|
||||
}
|
||||
|
||||
// ===== RENDER =====
|
||||
function page(id) {
|
||||
document.querySelectorAll('.pg').forEach(p=>p.classList.remove('act'));
|
||||
document.getElementById(id).classList.add('act');
|
||||
}
|
||||
|
||||
function renderHome() {
|
||||
// Week bar
|
||||
const wb = document.getElementById('weekBar');
|
||||
const dates = weekDates();
|
||||
wb.innerHTML = dates.map(d => {
|
||||
const key = d.toISOString().slice(0,10);
|
||||
const isToday = key === today();
|
||||
const hasData = S.daily[key];
|
||||
return `<div class="d ${isToday?'td':''} ${hasData?'on':''}">${DAYS[d.getDay()]}</div>`;
|
||||
}).join('');
|
||||
|
||||
// Check-in area
|
||||
const ci = document.getElementById('checkInArea');
|
||||
const last = S.lastCheckIn;
|
||||
const td = parseInt(new Date()/86400000);
|
||||
const canCheck = last !== td;
|
||||
ci.innerHTML = canCheck
|
||||
? `<button class="ci-star" id="doCheckIn"><span class="ci-e">⭐</span><span class="ci-t">今日打卡</span></button>`
|
||||
: `<div class="ci-star" style="cursor:default;opacity:0.7;"><span class="ci-e">✅</span><span class="ci-t">今日已打卡</span></div>`;
|
||||
|
||||
// Stats
|
||||
const all = allWords();
|
||||
const learned = Object.keys(S.learned).length;
|
||||
const mastered = Object.values(S.learned).filter(v => v.level >= 2).length;
|
||||
const now = Date.now();
|
||||
const due = Object.values(S.learned).filter(v => v.level < 3 && v.nextReview > 0 && v.nextReview <= now).length;
|
||||
document.getElementById('sLearned').textContent = learned;
|
||||
document.getElementById('sMastered').textContent = mastered;
|
||||
document.getElementById('sDue').textContent = due;
|
||||
|
||||
// Unit grid
|
||||
const grid = document.getElementById('unitGrid');
|
||||
grid.innerHTML = UNITS.map((u, i) => {
|
||||
const cnt = u.words.length;
|
||||
const done = u.words.filter((w, wi) => S.learned[uid(i,wi)] && S.learned[uid(i,wi)].level >= 1).length;
|
||||
return `<div class="u-card ${done===cnt?'done':''}" data-idx="${i}">
|
||||
<div class="un">${u.em}</div>
|
||||
<div class="ut">${u.title}</div>
|
||||
<div class="ud">${u.sub}</div>
|
||||
<div class="ub">${done}/${cnt} <span class="m">✓</span></div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
// HUD
|
||||
document.getElementById('hStreak').textContent = S.streak + '天';
|
||||
document.getElementById('hLearned').textContent = learned;
|
||||
}
|
||||
|
||||
function renderUnit(ui) {
|
||||
const u = UNITS[ui];
|
||||
document.getElementById('unitHeader').innerHTML = `<h2>${u.em} ${u.title}</h2><div class="c">${u.sub} · ${u.words.length}词</div>`;
|
||||
document.getElementById('startQuiz').dataset.ui = ui;
|
||||
const wl = document.getElementById('wordList');
|
||||
wl.innerHTML = u.words.map((w, wi) => {
|
||||
const st = S.learned[uid(ui,wi)];
|
||||
const lv = st ? st.level : 0;
|
||||
const lvCls = ['n','l','m','g'][Math.min(lv,3)];
|
||||
const lvLabel = ['新','认','熟','掌'][Math.min(lv,3)];
|
||||
return `<div class="wi" data-idx="${wi}">
|
||||
<div class="we ${w.em.length>2?'s':''}">${w.em}</div>
|
||||
<div class="ww">
|
||||
<div class="en">${w.e}</div>
|
||||
<div class="zh">${w.c}</div>
|
||||
<div class="ex">${w.s}</div>
|
||||
</div>
|
||||
<div class="st">
|
||||
<span class="lvl ${lvCls}">${lvLabel}</span>
|
||||
<span class="snd" data-word="${w.e}">🔊</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// ===== CHECK-IN =====
|
||||
function doCheckIn() {
|
||||
const td = parseInt(new Date()/86400000);
|
||||
S.lastCheckIn = td;
|
||||
S.streak = (S.streak || 0) + 1;
|
||||
if (!S.daily[today()]) S.daily[today()] = { learned:0, quiz:0, correct:0 };
|
||||
S.daily[today()].learned = (S.daily[today()].learned || 0) + 1;
|
||||
sv(); renderHome();
|
||||
// Show modal
|
||||
const days = S.streak;
|
||||
document.getElementById('ciMsg').textContent = days >= 7 ? `🔥 连续 ${days} 天!太棒了!` : `已连续打卡 ${days} 天,继续加油!`;
|
||||
document.getElementById('ciModal').classList.add('show');
|
||||
}
|
||||
|
||||
// ===== EVENT DELEGATION =====
|
||||
document.addEventListener('click', function(e) {
|
||||
const t = e.target;
|
||||
|
||||
// Unit card
|
||||
const uc = t.closest('.u-card');
|
||||
if (uc) { S.curUnit = parseInt(uc.dataset.idx); page('pUnit'); renderUnit(S.curUnit); return; }
|
||||
|
||||
// Back
|
||||
if (t.closest('#bkHome') || t.closest('#bkHomeG') || t.closest('#bkHomeR')) { page('pHome'); renderHome(); return; }
|
||||
|
||||
// Speak
|
||||
const snd = t.closest('.snd');
|
||||
if (snd) { const w = snd.dataset.word; if (w) speak(w); return; }
|
||||
|
||||
// Start quiz
|
||||
if (t.closest('#startQuiz')) { startQuiz(parseInt(t.closest('#startQuiz').dataset.ui)); return; }
|
||||
|
||||
// Quiz option
|
||||
const qo = t.closest('.qz-op');
|
||||
if (qo && !qo.classList.contains('disabled')) { pickQuiz(parseInt(qo.dataset.idx)); return; }
|
||||
|
||||
// Check-in
|
||||
if (t.closest('#doCheckIn')) { doCheckIn(); return; }
|
||||
if (t.closest('#ciBtn')) { document.getElementById('ciModal').classList.remove('show'); return; }
|
||||
|
||||
// Grammar
|
||||
if (t.closest('#btnGrammar')) { page('pGrammar'); renderGrammar(); return; }
|
||||
|
||||
// Review
|
||||
if (t.closest('#btnReview')) { page('pReview'); renderReview(); return; }
|
||||
|
||||
// Word in list → quiz single
|
||||
const wi = t.closest('.wi');
|
||||
if (wi && !snd) {
|
||||
const idx = parseInt(wi.dataset.idx);
|
||||
if (S.curUnit !== undefined) { startQuiz(S.curUnit, idx); }
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear
|
||||
if (t.closest('#btnClear')) {
|
||||
if (confirm('清除所有学习进度?')) { localStorage.removeItem('wordLe'); S={}; initState(); sv(); renderHome(); }
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// ===== QUIZ ENGINE =====
|
||||
let Q = { pool:[], idx:0, correct:0, total:0, mode:'', ui:-1, done:false };
|
||||
|
||||
function startQuiz(ui, startIdx) {
|
||||
const u = UNITS[ui];
|
||||
const pool = u.words.map((w, wi) => ({...w, ui, wi}));
|
||||
Q = { pool: shuffle(pool), idx:0, correct:0, total:pool.length, mode:'en2em', ui, done:false };
|
||||
if (startIdx !== undefined) {
|
||||
const si = Q.pool.findIndex(p => p.wi === startIdx);
|
||||
if (si >= 0) Q.idx = si;
|
||||
}
|
||||
page('pQuiz');
|
||||
renderQuiz();
|
||||
}
|
||||
|
||||
function renderQuiz() {
|
||||
if (Q.done || Q.idx >= Q.pool.length) { finishQuiz(); return; }
|
||||
const q = Q.pool[Q.idx];
|
||||
const prog = `${Q.idx+1}/${Q.total}`;
|
||||
document.getElementById('qProg').textContent = prog + ' · ✅ '+Q.correct;
|
||||
document.getElementById('qMode').textContent = UNITS[Q.ui].em + ' ' + UNITS[Q.ui].title;
|
||||
|
||||
// Generate options: 3 random wrong + 1 correct
|
||||
const u = UNITS[Q.ui];
|
||||
let opts = [q];
|
||||
const pool = u.words.filter(w => w.e !== q.e);
|
||||
shuffle(pool);
|
||||
for (let i = 0; i < 3 && i < pool.length; i++) opts.push(pool[i]);
|
||||
shuffle(opts);
|
||||
|
||||
// Card
|
||||
const card = document.getElementById('qCard');
|
||||
card.innerHTML = `<div class="q-zh">选择对应的图片</div><div class="q-en" style="font-size:40px;margin-top:8px;">${q.e}</div>`;
|
||||
card.innerHTML += `<div class="q-snd" data-word="${q.e}">🔊</div>`;
|
||||
|
||||
// Options (emojis only)
|
||||
const odiv = document.getElementById('qOpts');
|
||||
odiv.innerHTML = opts.map((o, i) =>
|
||||
`<div class="qz-op" data-idx="${i}" data-correct="${o.e===q.e?'1':'0'}">
|
||||
<div class="oe">${o.em}</div>
|
||||
<div class="ot">${o.c}</div>
|
||||
</div>`
|
||||
).join('');
|
||||
|
||||
document.getElementById('qFb').className = 'qz-fb';
|
||||
document.getElementById('qFb').innerHTML = '';
|
||||
|
||||
// Auto-speak
|
||||
setTimeout(() => speak(q.e), 300);
|
||||
}
|
||||
|
||||
function pickQuiz(idx) {
|
||||
const opts = document.querySelectorAll('.qz-op');
|
||||
const selected = opts[idx];
|
||||
const isCorrect = selected.dataset.correct === '1';
|
||||
const q = Q.pool[Q.idx];
|
||||
const id = uid(q.ui, q.wi);
|
||||
|
||||
// Disable all
|
||||
opts.forEach(o => o.classList.add('disabled'));
|
||||
|
||||
if (isCorrect) {
|
||||
selected.classList.add('cor');
|
||||
Q.correct++;
|
||||
// Update SRS
|
||||
if (!S.learned[id]) S.learned[id] = { level:0, lastReview:0, nextReview:0, correct:0, wrong:0 };
|
||||
const st = S.learned[id];
|
||||
st.correct = (st.correct || 0) + 1;
|
||||
st.lastReview = Date.now();
|
||||
if (st.correct >= 3) st.level = Math.min(3, st.level + 1);
|
||||
else if (st.correct >= 1) st.level = Math.max(1, st.level);
|
||||
// Next review: 1h → 6h → 24h → 7d
|
||||
const intervals = [0, 3600000, 21600000, 86400000, 604800000];
|
||||
st.nextReview = Date.now() + intervals[Math.min(st.level + 1, intervals.length - 1)];
|
||||
// Daily
|
||||
if (!S.daily[today()]) S.daily[today()] = { learned:0, quiz:0, correct:0 };
|
||||
S.daily[today()].quiz = (S.daily[today()].quiz || 0) + 1;
|
||||
S.daily[today()].correct = (S.daily[today()].correct || 0) + 1;
|
||||
S.totalQuiz++; S.totalCorrect++;
|
||||
sv();
|
||||
|
||||
// Show correct mark on card
|
||||
const fb = document.getElementById('qFb');
|
||||
fb.className = 'qz-fb show pass';
|
||||
fb.innerHTML = `✅ <strong>${q.e}</strong> = ${q.c} ${q.em}<br><span style="font-size:11px;color:rgba(255,255,255,0.3);">${q.s}</span>`;
|
||||
speak(q.e);
|
||||
|
||||
setTimeout(() => { Q.idx++; renderQuiz(); }, 1200);
|
||||
} else {
|
||||
selected.classList.add('wrg');
|
||||
// Highlight correct
|
||||
opts.forEach(o => { if (o.dataset.correct === '1') o.classList.add('cor'); });
|
||||
// Update SRS
|
||||
if (!S.learned[id]) S.learned[id] = { level:0, lastReview:0, nextReview:0, correct:0, wrong:0 };
|
||||
const st = S.learned[id];
|
||||
st.wrong = (st.wrong || 0) + 1;
|
||||
st.level = 0;
|
||||
st.nextReview = Date.now() + 60000; // Retry in 1min
|
||||
if (!S.daily[today()]) S.daily[today()] = { learned:0, quiz:0, correct:0 };
|
||||
S.daily[today()].quiz = (S.daily[today()].quiz || 0) + 1;
|
||||
S.totalQuiz++;
|
||||
sv();
|
||||
|
||||
const fb = document.getElementById('qFb');
|
||||
fb.className = 'qz-fb show fail';
|
||||
fb.innerHTML = `❌ 正确答案:<strong>${q.e}</strong> ${q.em} = ${q.c}<br><span style="font-size:11px;color:rgba(255,255,255,0.3);">${q.s}</span>`;
|
||||
speak(q.e);
|
||||
|
||||
setTimeout(() => { Q.idx++; renderQuiz(); }, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
function finishQuiz() {
|
||||
const pct = Q.total ? Math.round(Q.correct / Q.total * 100) : 0;
|
||||
const msg = pct >= 90 ? '🌟 完美!全部掌握!' : pct >= 70 ? '👍 不错,继续加油!' : '💪 多练几次就会了!';
|
||||
const fb = document.getElementById('qFb');
|
||||
fb.className = 'qz-fb show ' + (pct >= 70 ? 'pass' : 'fail');
|
||||
fb.innerHTML = `<div style="font-size:28px;font-weight:900;color:#fff;">${Q.correct}/${Q.total}</div>
|
||||
<div style="font-size:13px;color:rgba(255,255,255,0.4);margin:4px 0;">正确率 ${pct}%</div>
|
||||
<div style="font-size:12px;margin-bottom:10px;">${msg}</div>
|
||||
<button class="btn btn-p" onclick="page('pHome');renderHome();">🏠 返回</button>
|
||||
<button class="btn btn-s" onclick="startQuiz(${Q.ui})" style="margin-left:6px;">🔄 再来一次</button>`;
|
||||
document.getElementById('qCard').innerHTML = '';
|
||||
document.getElementById('qOpts').innerHTML = '';
|
||||
document.getElementById('qProg').textContent = '';
|
||||
document.getElementById('qMode').textContent = '';
|
||||
Q.done = true;
|
||||
}
|
||||
|
||||
// ===== GRAMMAR =====
|
||||
function renderGrammar() {
|
||||
const gl = document.getElementById('grammarList');
|
||||
gl.innerHTML = GRAMMAR.map(g => `<div class="gr-card">
|
||||
<div class="gt"><span class="gi">${g.icon}</span>${g.title}</div>
|
||||
<div class="gd">${g.detail}</div>
|
||||
<div style="margin:6px 0;">${g.examples.map(e => `<span class="ge">${e}</span>`).join(' ')}</div>
|
||||
<div class="gr">💡 ${g.note}</div>
|
||||
</div>`).join('');
|
||||
}
|
||||
|
||||
// ===== REVIEW (SRS-based) =====
|
||||
function renderReview() {
|
||||
const now = Date.now();
|
||||
const due = allWords().filter(w => {
|
||||
const st = S.learned[uid(w.ui, w.wi)];
|
||||
return st && st.level < 3 && st.nextReview > 0 && st.nextReview <= now;
|
||||
});
|
||||
const rc = document.getElementById('reviewContent');
|
||||
if (!due.length) {
|
||||
rc.innerHTML = '<div style="text-align:center;padding:40px 20px;color:rgba(255,255,255,0.15);">🎉 没有待复习的单词!<br>学点新的吧!</div>';
|
||||
return;
|
||||
}
|
||||
rc.innerHTML = `<div style="text-align:center;margin-bottom:10px;color:rgba(255,255,255,0.3);font-size:12px;">📋 有 <span style="color:#f59e0b;">${due.length}</span> 个单词待复习</div>`;
|
||||
due.slice(0, 20).forEach(w => {
|
||||
const st = S.learned[uid(w.ui, w.wi)];
|
||||
const lv = ['新','生','熟']['' + Math.min(st.level, 2)];
|
||||
rc.innerHTML += `<div class="wi" style="cursor:default;">
|
||||
<div class="we ${w.em.length>2?'s':''}">${w.em}</div>
|
||||
<div class="ww">
|
||||
<div class="en">${w.e}</div>
|
||||
<div class="zh">${w.c}</div>
|
||||
</div>
|
||||
<div class="st">
|
||||
<span class="snd" data-word="${w.e}" style="font-size:16px;">🔊</span>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
rc.innerHTML += `<button class="btn btn-p" style="width:100%;margin-top:10px;" onclick="startQuiz(S.curUnit !== undefined ? S.curUnit : 0)">🎯 开始复习这些词</button>`;
|
||||
}
|
||||
|
||||
// ===== INIT =====
|
||||
ld(); initState(); sv(); renderHome();
|
||||
|
||||
// Listen for speech button clicks (for q-snd)
|
||||
document.addEventListener('click', function(e) {
|
||||
const qs = e.target.closest('.q-snd');
|
||||
if (qs) {
|
||||
const txt = qs.dataset.word;
|
||||
if (txt) { speak(txt); return; }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user