RAG面试链路工程取舍
2026/7/7 3:10:06
文件结构
文件夹
word.html
data.js
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>考研英语单词背诵</title> <script src="data.js"></script> <style> /* ===== 全局重置 & 基础 ===== */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', 'PingFang SC', Roboto, 'Helvetica Neue', sans-serif; background: #eef5ff; min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 24px; } .app-container { width: 1400px; max-width: 100%; background: #ffffff; border-radius: 32px; box-shadow: 0 20px 60px rgba(44, 107, 255, 0.15); overflow: hidden; display: flex; flex-direction: column; } /* ===== 顶部栏 ===== */ .header { background: linear-gradient(135deg, #2b6cff 0%, #1a4fc3 100%); padding: 20px 36px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; } .header-left { display: flex; align-items: center; gap: 16px; } .header-left h1 { color: #fff; font-size: 26px; font-weight: 700; letter-spacing: 0.5px; } .header-left .badge { background: rgba(255, 255, 255, 0.2); color: #fff; padding: 4px 14px; border-radius: 20px; font-size: 14px; font-weight: 500; backdrop-filter: blur(4px); } .header-right { display: flex; align-items: center; gap: 24px; color: rgba(255, 255, 255, 0.9); font-size: 15px; } .header-right .progress-text { background: rgba(255, 255, 255, 0.15); padding: 6px 18px; border-radius: 20px; font-weight: 500; } .header-right .mastery-count { background: rgba(255, 255, 255, 0.15); padding: 6px 18px; border-radius: 20px; font-weight: 500; } /* ===== 主体 ===== */ .main-area { display: flex; flex: 1; min-height: 600px; } /* ===== 学习区 ===== */ .learning-area { flex: 1; padding: 40px 44px 32px; display: flex; flex-direction: column; background: #fafcff; border-right: 1px solid #e8eff9; min-width: 0; } .word-display { text-align: center; padding: 8px 0 20px; } .word-display .part-of-speech { font-size: 22px; font-weight: 600; color: #2b6cff; background: #e8f0ff; padding: 4px 18px; border-radius: 30px; display: inline-block; margin-bottom: 8px; } .word-display .meaning { font-size: 38px; font-weight: 700; color: #1a2a4a; display: block; margin-top: 4px; letter-spacing: 1px; } /* 字母输入区 */ .input-area { display: flex; flex-direction: column; align-items: center; margin: 16px 0 8px; padding: 20px 0; position: relative; } .letter-container { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; padding: 12px 20px; background: #f5f9ff; border-radius: 20px; border: 2px dashed #c8daf5; min-height: 80px; align-items: center; cursor: text; transition: border-color 0.25s, background 0.25s; width: 100%; max-width: 700px; } .letter-container:focus-within { border-color: #2b6cff; background: #f0f6ff; box-shadow: 0 0 0 4px rgba(43, 108, 255, 0.08); } .letter-container .letter-slot { display: inline-flex; justify-content: center; align-items: center; width: 44px; height: 56px; font-size: 30px; font-weight: 700; color: #1a2a4a; background: #ffffff; border-radius: 10px; border: 2px solid #d4e4ff; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.02); transition: all 0.15s; font-family: 'Courier New', monospace; text-transform: lowercase; } .letter-container .letter-slot.filled { border-color: #2b6cff; background: #eaf2ff; color: #1a3a8a; } .letter-container .letter-slot.correct-slot { border-color: #22c55e; background: #e6f9ec; color: #16a34a; } .letter-container .letter-slot.wrong-slot { border-color: #ef4444; background: #fee9e9; color: #dc2626; animation: shake 0.3s ease; } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } } #hiddenInput { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; } .input-hint { margin-top: 12px; font-size: 14px; color: #8899bb; letter-spacing: 0.3px; user-select: none; } .input-hint kbd { background: #eef3fc; padding: 2px 10px; border-radius: 6px; font-size: 13px; border: 1px solid #d4e0f0; color: #2b4a7a; font-weight: 600; } /* 例句区域 */ .example-area { margin: 12px 0 16px; padding: 16px 24px; background: #f5f9ff; border-radius: 16px; border-left: 5px solid #2b6cff; min-height: 70px; display: flex; flex-direction: column; justify-content: center; transition: all 0.3s; opacity: 0; transform: translateY(8px); max-width: 700px; align-self: center; width: 100%; } .example-area.visible { opacity: 1; transform: translateY(0); } .example-area .example-en { font-size: 18px; color: #1a2a4a; font-weight: 500; line-height: 1.6; } .example-area .example-zh { font-size: 16px; color: #5a6f8a; margin-top: 4px; line-height: 1.5; } .example-area .example-label { font-size: 13px; color: #2b6cff; font-weight: 600; margin-bottom: 4px; letter-spacing: 0.5px; } /* 控制按钮 */ .controls { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 20px; padding-top: 16px; border-top: 1px solid #e8eff9; } .controls button { padding: 10px 24px; border: none; border-radius: 30px; font-size: 15px; font-weight: 600; cursor: pointer; transition: all 0.2s; display: inline-flex; align-items: center; gap: 6px; background: #f0f6ff; color: #1a3a6b; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02); } .controls button:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(43, 108, 255, 0.15); } .controls button:active { transform: scale(0.96); } .controls .btn-primary { background: #2b6cff; color: #fff; } .controls .btn-primary:hover { background: #1a5ae0; box-shadow: 0 8px 24px rgba(43, 108, 255, 0.30); } .controls .btn-outline { background: transparent; border: 2px solid #c8daf5; color: #2b4a7a; } .controls .btn-outline:hover { border-color: #2b6cff; background: #f0f6ff; } .controls .btn-warning { background: #f59e0b; color: #fff; } .controls .btn-warning:hover { background: #d97706; box-shadow: 0 8px 24px rgba(245, 158, 11, 0.30); } .controls .btn-success { background: #22c55e; color: #fff; } .controls .btn-success:hover { background: #16a34a; box-shadow: 0 8px 24px rgba(34, 197, 94, 0.30); } /* ===== 单词列表 ===== */ .word-list-area { width: 320px; min-width: 260px; background: #fafcff; display: flex; flex-direction: column; padding: 20px 16px 16px; flex-shrink: 0; } .word-list-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 12px; border-bottom: 2px solid #e8eff9; margin-bottom: 12px; flex-shrink: 0; } .word-list-header .list-title { font-size: 17px; font-weight: 700; color: #1a2a4a; display: flex; align-items: center; gap: 8px; } .word-list-header .list-title span { background: #2b6cff; color: #fff; font-size: 12px; padding: 1px 10px; border-radius: 20px; font-weight: 600; } .word-list-header .page-info { font-size: 14px; color: #6a7f9a; font-weight: 500; } .word-list-body { flex: 1; overflow-y: auto; padding-right: 4px; min-height: 0; } .word-list-body::-webkit-scrollbar { width: 4px; } .word-list-body::-webkit-scrollbar-track { background: #eef3fc; border-radius: 10px; } .word-list-body::-webkit-scrollbar-thumb { background: #c8daf5; border-radius: 10px; } .word-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 14px; margin-bottom: 4px; border-radius: 12px; font-size: 15px; font-weight: 500; color: #1a2a4a; transition: all 0.15s; cursor: default; border: 1px solid transparent; } .word-item .word-text { font-weight: 600; font-size: 16px; color: #1a2a4a; } .word-item .word-status { font-size: 14px; opacity: 0.7; } .word-item.current { background: #e8f0ff; border-color: #2b6cff; box-shadow: 0 2px 8px rgba(43, 108, 255, 0.08); } .word-item.current .word-text { color: #1a4fc3; } .word-item.mastered { background: #e6f9ec; border-color: #86d9a4; } .word-item.mastered .word-text { color: #16a34a; } .word-item.mastered .word-status { color: #22c55e; } .word-list-footer { display: flex; justify-content: space-between; gap: 8px; padding-top: 12px; border-top: 2px solid #e8eff9; margin-top: 12px; flex-shrink: 0; } .word-list-footer button { flex: 1; padding: 8px 0; border: none; border-radius: 30px; font-size: 14px; font-weight: 600; cursor: pointer; background: #f0f6ff; color: #1a3a6b; transition: all 0.2s; } .word-list-footer button:hover { background: #e0ebff; transform: translateY(-1px); } .word-list-footer button:active { transform: scale(0.96); } .word-list-footer button:disabled { opacity: 0.4; cursor: not-allowed; transform: none; } /* ===== 完成模态框 ===== */ .completion-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(8px); display: none; justify-content: center; align-items: center; z-index: 9999; animation: fadeInOverlay 0.5s ease; } .completion-overlay.active { display: flex; } @keyframes fadeInOverlay { from { opacity: 0; } to { opacity: 1; } } .completion-card { background: linear-gradient(135deg, #ffffff 0%, #f0f7ff 100%); border-radius: 48px; padding: 56px 64px; max-width: 550px; width: 90%; text-align: center; box-shadow: 0 40px 80px rgba(43, 108, 255, 0.3); position: relative; animation: popIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275); border: 2px solid rgba(43, 108, 255, 0.2); } @keyframes popIn { 0% { transform: scale(0.5); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } .completion-card .emoji-big { font-size: 72px; display: block; margin-bottom: 8px; } .completion-card h2 { font-size: 34px; color: #1a2a4a; margin: 16px 0 8px; font-weight: 800; } .completion-card p { font-size: 18px; color: #4a5f7a; margin: 8px 0 24px; line-height: 1.6; } .completion-card .btn-celebrate { background: linear-gradient(135deg, #2b6cff, #1a4fc3); color: #fff; border: none; padding: 14px 44px; border-radius: 60px; font-size: 18px; font-weight: 700; cursor: pointer; transition: all 0.3s; box-shadow: 0 10px 30px rgba(43, 108, 255, 0.3); } .completion-card .btn-celebrate:hover { transform: translateY(-3px) scale(1.02); box-shadow: 0 16px 40px rgba(43, 108, 255, 0.4); } /* ===== 五彩纸屑 ===== */ .confetti-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10000; overflow: hidden; } .confetti-piece { position: absolute; width: 12px; height: 12px; opacity: 0.9; animation: confettiFall linear forwards; } @keyframes confettiFall { 0% { transform: translateY(-20vh) rotate(0deg) scale(1); opacity: 1; } 100% { transform: translateY(110vh) rotate(720deg) scale(0.5); opacity: 0; } } /* ===== 响应式 ===== */ @media (max-width: 1024px) { .main-area { flex-direction: column; } .word-list-area { width: 100%; min-width: 0; border-top: 1px solid #e8eff9; max-height: 280px; } .learning-area { border-right: none; padding: 28px 24px 20px; } .letter-container .letter-slot { width: 36px; height: 48px; font-size: 24px; } .word-display .meaning { font-size: 30px; } } @media (max-width: 600px) { .header { padding: 14px 18px; } .header-left h1 { font-size: 20px; } .header-right .progress-text, .header-right .mastery-count { font-size: 12px; padding: 4px 12px; } .learning-area { padding: 20px 16px 16px; } .word-display .meaning { font-size: 26px; } .word-display .part-of-speech { font-size: 18px; } .letter-container { gap: 6px; padding: 10px 12px; } .letter-container .letter-slot { width: 30px; height: 40px; font-size: 20px; } .controls button { padding: 8px 16px; font-size: 13px; } .example-area .example-en { font-size: 16px; } .word-list-area { max-height: 200px; padding: 12px 12px 12px; } .word-item { font-size: 13px; padding: 6px 10px; } .completion-card { padding: 40px 28px; } .completion-card h2 { font-size: 28px; } } .fade-in { animation: fadeIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .celebration { animation: pop 0.4s ease; } @keyframes pop { 0% { transform: scale(0.9); opacity: 0.5; } 50% { transform: scale(1.05); } 100% { transform: scale(1); opacity: 1; } } </style> </head> <body> <div class="app-container"> <header class="header"> <div class="header-left"> <h1>📖 考研英语</h1> <span class="badge">单词背诵</span> </div> <div class="header-right"> <span class="progress-text" id="progressText">第 1 / 22 个</span> <span class="mastery-count" id="masteryCount">✅ 已掌握 0</span> </div> </header> <div class="main-area"> <!-- 学习区 --> <section class="learning-area"> <div class="word-display"> <span class="part-of-speech" id="partOfSpeech">v.</span> <span class="meaning" id="meaning">放弃,遗弃</span> </div> <div class="input-area"> <div class="letter-container" id="letterContainer" tabindex="0"> <!-- 由 JS 动态生成 --> </div> <input type="text" id="hiddenInput" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /> <div class="input-hint"> 输入单词 · <kbd>Enter</kbd> 确认 · <kbd>⌫</kbd> 删除 </div> </div> <div class="example-area" id="exampleArea"> <div class="example-label">📌 例句</div> <div class="example-en" id="exampleEn">He had to abandon his car in the snow.</div> <div class="example-zh" id="exampleZh">他不得不把车丢弃在雪地里。</div> </div> <div class="controls"> <button class="btn-outline" id="prevBtn">◀ 上一个</button> <button class="btn-outline" id="nextBtn">下一个 ▶</button> <button class="btn-success" id="showAnswerBtn">👁️ 显示答案</button> <button class="btn-warning" id="resetBtn">🔄 重新开始</button> <button class="btn-primary" id="shuffleBtn">🎲 打乱顺序</button> </div> </section> <!-- 单词列表 --> <aside class="word-list-area"> <div class="word-list-header"> <div class="list-title"> 📋 单词一览 <span id="totalCount">22</span> </div> <span class="page-info" id="pageInfo">1 / 3</span> </div> <div class="word-list-body" id="wordListBody"> <!-- 由 JS 动态生成 --> </div> <div class="word-list-footer"> <button id="prevPageBtn">◀ 上一页</button> <button id="nextPageBtn">下一页 ▶</button> </div> </aside> </div> </div> <!-- ===== 完成模态框 ===== --> <div class="completion-overlay" id="completionOverlay"> <div class="completion-card"> <span class="emoji-big">🎉</span> <h2>太棒了!全部完成!</h2> <p>你已掌握所有 <strong id="totalWordsDisplay">22</strong> 个考研单词,<br />继续加油,胜利就在前方!</p> <button class="btn-celebrate" id="celebrationRestartBtn">🚀 重新挑战</button> </div> </div> <div class="confetti-container" id="confettiContainer"></div> <script> // ================================================================ // 主逻辑 // ================================================================ const allWords = window.wordData || []; const state = { words: [], currentIndex: 0, currentInput: [], isCorrect: false, mastered: new Set(), pageSize: 10, // ✅ 已改为每页10个 currentPage: 0, totalPages: 1, isWaitingForNext: false, isShuffled: false, completionShown: false, showingAnswer: false, }; // ---------- DOM ---------- const $ = (id) => document.getElementById(id); const letterContainer = $('letterContainer'); const hiddenInput = $('hiddenInput'); const partOfSpeech = $('partOfSpeech'); const meaning = $('meaning'); const exampleArea = $('exampleArea'); const exampleEn = $('exampleEn'); const exampleZh = $('exampleZh'); const progressText = $('progressText'); const masteryCount = $('masteryCount'); const wordListBody = $('wordListBody'); const pageInfo = $('pageInfo'); const totalCount = $('totalCount'); const prevBtn = $('prevBtn'); const nextBtn = $('nextBtn'); const resetBtn = $('resetBtn'); const shuffleBtn = $('shuffleBtn'); const showAnswerBtn = $('showAnswerBtn'); const prevPageBtn = $('prevPageBtn'); const nextPageBtn = $('nextPageBtn'); const completionOverlay = $('completionOverlay'); const totalWordsDisplay = $('totalWordsDisplay'); const celebrationRestartBtn = $('celebrationRestartBtn'); const confettiContainer = $('confettiContainer'); // ---------- 初始化 ---------- function init() { if (!allWords.length) { letterContainer.innerHTML = `<div class="empty-state">⚠️ 请确保 data.js 已加载,且包含有效的单词数据。</div>`; return; } state.words = [...allWords]; state.totalPages = Math.ceil(state.words.length / state.pageSize); totalCount.textContent = state.words.length; totalWordsDisplay.textContent = state.words.length; state.currentIndex = 0; state.mastered = new Set(); state.isShuffled = false; state.isWaitingForNext = false; state.currentPage = 0; state.completionShown = false; state.showingAnswer = false; completionOverlay.classList.remove('active'); confettiContainer.innerHTML = ''; renderAll(); focusInput(); } // ---------- 渲染 ---------- function renderAll() { renderWord(); renderLetterSlots(); renderWordList(); renderPageInfo(); renderProgress(); renderMastery(); } function renderWord() { const word = getCurrentWord(); if (!word) return; partOfSpeech.textContent = word.partOfSpeech || ''; meaning.textContent = word.meaning || ''; if (state.isCorrect || state.showingAnswer) { exampleEn.textContent = word.example || ''; exampleZh.textContent = word.exampleTranslation || ''; exampleArea.classList.add('visible'); } else { exampleArea.classList.remove('visible'); } } function renderLetterSlots() { const word = getCurrentWord(); if (!word) { letterContainer.innerHTML = ''; return; } const wordLen = word.word.length; const input = state.currentInput; const isCorrect = state.isCorrect; const showing = state.showingAnswer; let html = ''; for (let i = 0; i < wordLen; i++) { let char = ''; let cls = 'letter-slot'; if (showing) { char = word.word[i]; cls += ' correct-slot filled'; } else if (isCorrect) { char = word.word[i]; cls += ' correct-slot filled'; } else { char = input[i] || ''; if (char) cls += ' filled'; } html += `<span class="${cls}">// data.js window.wordData = [ // ----- 新增 12 个考研词汇 ----- { "word": "autonomous", "partOfSpeech": "adj.", "meaning": "自治的,自主的", "example": "The region is an autonomous republic.", "exampleTranslation": "该地区是一个自治共和国。" }, { "word": "colony", "partOfSpeech": "n.", "meaning": "殖民地,群体", "example": "The former colony gained independence in 1960.", "exampleTranslation": "这个前殖民地于1960年获得独立。" }, { "word": "diplomacy", "partOfSpeech": "n.", "meaning": "外交,策略", "example": "Diplomacy is essential to avoid conflict.", "exampleTranslation": "外交是避免冲突的关键。" }, { "word": "independent", "partOfSpeech": "adj.", "meaning": "独立的,自主的", "example": "She is financially independent.", "exampleTranslation": "她在经济上独立。" }, { "word": "self-reliant", "partOfSpeech": "adj.", "meaning": "自立的,自力更生的", "example": "The community is self-reliant in food production.", "exampleTranslation": "这个社区在粮食生产上自给自足。" }, { "word": "self-sufficient", "partOfSpeech": "adj.", "meaning": "自给自足的", "example": "The country aims to be self-sufficient in energy.", "exampleTranslation": "该国力求能源自给自足。" }, { "word": "sovereign", "partOfSpeech": "adj./n.", "meaning": "至高无上的,主权的;君主", "example": "The nation is a sovereign state.", "exampleTranslation": "该国是一个主权国家。" }, { "word": "reign", "partOfSpeech": "v./n.", "meaning": "统治,支配;统治时期", "example": "The king reigned for over forty years.", "exampleTranslation": "这位国王统治了四十多年。" }, { "word": "conservative", "partOfSpeech": "adj.", "meaning": "保守的,守旧的", "example": "He holds conservative views on social issues.", "exampleTranslation": "他在社会问题上持保守观点。" }, { "word": "democracy", "partOfSpeech": "n.", "meaning": "民主,民主制度", "example": "Democracy allows citizens to vote.", "exampleTranslation": "民主制度允许公民投票。" }, { "word": "liberal", "partOfSpeech": "adj.", "meaning": "自由的,开明的;自由主义的", "example": "She has a liberal attitude toward education.", "exampleTranslation": "她对教育持开明态度。" }, { "word": "republic", "partOfSpeech": "n.", "meaning": "共和国,共和政体", "example": "France is a republic.", "exampleTranslation": "法国是一个共和国。" } ];