forks

Creation chat

ClarkZhang
帮我做一个背单词的应用。 对单词分三类: 我懂的/我不懂的/我完全掌握 根据艾宾浩斯遗忘曲线,定时提醒我复习单词。 可以随机输入100个单词,作为单词库
03-12 08:25

登录 后可以评论

Assistant
(HTML code has been generated and updated; see above for the current version.)
▼ Code
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>艾宾浩斯背单词</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script>tailwind.config = { darkMode: 'class' }</script>
    <style>
        .flip-card-inner {
            transition: transform 0.6s;
            transform-style: preserve-3d;
        }
        .flip-card.flipped .flip-card-inner {
            transform: rotateY(180deg);
        }
        .flip-card-front, .flip-card-back {
            backface-visibility: hidden;
        }
        .flip-card-back {
            transform: rotateY(180deg);
        }
    </style>
</head>
<body class="min-h-screen bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100">
    <!-- 主题切换按钮 -->
    <button id="theme-toggle-btn" class="fixed top-4 right-4 z-10 p-2 rounded-md hover:bg-gray-200 dark:hover:bg-grayly-700 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors flex items-center justify-center" aria-label="Toggle theme" onclick="toggleTheme()">
        <svg id="icon-moon" class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998z"/></svg>
        <svg id="icon-sun" class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true" style="display:none"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"></path></svg>
    </button>
    <script>
    function toggleTheme(){ var el=document.documentElement; var isDark=!el.classList.contains('dark'); el.classList.toggle('dark',isDark); localStorage.setItem('theme',isDark?'dark':'light'); applyTheme(isDark); }
    function applyTheme(isDark){ document.getElementById('icon-moon').style.display=isDark?'none':'block'; document.getElementById('icon-sun').style.display=isDark?'block':'none'; }
    document.addEventListener('DOMContentLoaded',function(){ var saved=localStorage.getItem('theme'); var isDark=saved==='dark'||(!saved&&window.matchMedia('(prefers-color-scheme: dark)').matches); document.documentElement.classList.toggle('dark',isDark); applyTheme(isDark); });
    </script>

    <!-- 自定义弹窗 -->
    <div id="custom-modal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 hidden" aria-hidden="true">
        <div class="rounded-xl bg-white dark:bg-slate-800 p-6 shadow-xl max-w-sm w-full border border-slate-200 dark:border-slate-700">
            <p id="custom-modal-msg" class="text-slate-700 dark:text-slate-200 mb-4"></p>
            <button type="button" onclick="hideModal()" class="w-full rounded-lg bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600">确定</button>
        </div>
    </div>
    <script>
    function showModal(msg){ var el=document.getElementById('custom-modal'); document.getElementById('custom-modal-msg').textContent=msg||''; el.classList.remove('hidden'); }
    function hideModal(){ document.getElementById('custom-modal').classList.add('hidden'); }
    </script>

    <header class="bg-white dark:bg-slate-800 shadow-sm py-4 px-6">
        <div class="container mx-auto flex justify-between items-center">
            <h1 class="text-2xl font-bold text-slate-800 dark:text-slate-100">艾宾浩斯背单词</h1>
            <div class="flex items-center space-x-4">
                <button id="stats-btn" class="px-3 py-1 bg-slate-100 dark:bg-slate-700 rounded-lg text-sm font-medium hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors">
                    统计
                </button>
                <button id="reset-btn" class="px-3 py-1 bg-amber-100 dark:bg-amber-900/50 text-amber-700 dark:text-amber-300 rounded-lg text-sm font-medium hover:bg-amber-200 dark:hover:bg-amber-800 transition-colors">
                    重置
                </button>
            </div>
        </div>
    </header>

    <main class="container mx-auto px-4 py-8">
        <!-- 单词输入区域 -->
        <section id="input-section" class="mb-12">
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-md p-6 max-w-3xl mx-auto">
                <h2 class="text-xl font-semibold mb-4 text-slate-800 dark:text-slate-100">导入单词库</h2>
                <p class="text-slate-600 dark:text-slate-300 mb-4">请输入100个单词(每行一个单词)</p>
                
                <form id="word-input-form">
                    <textarea 
                        id="word-input" 
                        class="w-full h-40 p-3 border border-slate-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-700 text-slate-900 dark:text-slate-100 mb-4"
                        placeholder="例如:&#10;apple&#10;banana&#10;computer&#10;..."></textarea>
                    
                    <div class="flex justify-between items-center">
                        <div class="text-sm text-slate-500 dark:text-slate-400">
                            已输入 <span id="word-count">0</span> 个单词
                        </div>
                        <button 
                            type="submit" 
                            class="px-6 py-2 bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white rounded-lg font-medium transition-colors"
                        >
                            开始学习
                        </button>
                    </div>
                </form>
            </div>
        </section>

        <!-- 学习区域 -->
        <section id="learning-section" class="hidden mb-12">
            <div class="max-w-2xl mx-auto">
                <div class="bg-white dark:bg-slate-800 rounded-xl shadow-md p-6 mb-6">
                    <div class="flex justify-between items-center mb-4">
                        <div class="text-sm font-medium text-slate-500 dark:text-slate-400">
                            <span id="current-index">1</span> / <span id="total-words">0</span>
                        </div>
                        <div class="text-sm font-medium text-slate-500 dark:text-slate-400">
                            待复习: <span id="review-count" class="text-amber-600 dark:text-amber-400">0</span>
                        </div>
                    </div>
                    
                    <!-- 单词卡片 -->
                    <div id="word-card" class="flip-card cursor-pointer mb-6 min-h-[200px] flex items-center justify-center">
                        <div class="flip-card-inner w-full h-full">
                            <div class="flip-card-front absolute inset-0 flex items-center justify-center">
                                <div class="text-center p-4">
                                    <div class="text-3xl font-bold mb-2 text-slate-800 dark:text-slate-100" id="current-word">单词</div>
                                    <div class="text-slate-500 dark:text-slate-400 text-sm">点击翻转查看释义</div>
                                </div>
                            </div>
                            <div class="flip-card-back absolute inset-0 flex items-center justify-center">
                                <div class="text-center p-4">
                                    <div class="text-xl mb-4 text-slate-700 dark:text-slate-200" id="current-meaning">单词释义</div>
                                    <div class="text-slate-500 dark:text-slate-400 text-sm">点击翻转查看单词</div>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- 操作按钮 -->
                    <div class="grid grid-cols-3 gap-3">
                        <button 
                            id="known-btn" 
                            class="py-3 px-4 bg-green-100 hover:bg-green-200 dark:bg-green-900/30 dark:hover:bg-green-800/50 text-green-700 dark:text-green-300 rounded-lg font-medium transition-colors flex flex-col items-center"
                        >
                            <span class="text-lg">✓</span>
                            <span class="text-sm">认识</span>
                        </button>
                        
                        <button 
                            id="unknown-btn" 
                            class="py-3 px-4 bg-red-100 hover:bg-red-200 dark:bg-red-900/30 dark:hover:bg-red-800/50 text-red-700 dark:text-red-300 rounded-lg font-medium transition-colors flex flex-col items-center"
                        >
                            <span class="text-lg">✗</span>
                            <span class="text-sm">不认识</span>
                        </button>
                        
                        <button 
                            id="mastered-btn" 
                            class="py-3 px-4 bg-blue-100 hover:bg-blue-200 dark:bg-blue-900/30 dark:hover:bg-blue-800/50 text-blue-700 dark:text-blue-300 rounded-lg font-medium transition-colors flex flex-col items-center"
                        >
                            <span class="text-lg">★</span>
                            <span class="text-sm">掌握</span>
                        </button>
                    </div>
                </div>
                
                <!-- 进度条 -->
                <div class="bg-white dark:bg-slate-800 rounded-xl shadow-md p-6">
                    <h3 class="font-medium mb-3 text-slate-800 dark:text-slate-100">学习进度</h3>
                    <div class="space-y-3">
                        <div>
                            <div class="flex justify-between text-sm mb-1">
                                <span class="text-slate-600 dark:text-slate-300">认识</span>
                                <span class="font-medium text-green-600 dark:text-green-400" id="known-count">0</span>
                            </div>
                            <div class="h-2 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden">
                                <div id="known-progress" class="h-full bg-green-500 rounded-full" style="width: 0%"></div>
                            </div>
                        </div>
                        
                        <div>
                            <div class="flex justify-between text-sm mb-1">
                                <span class="text-slate-600 dark:text-slate-300">不认识</span>
                                <span class="font-medium text-red-600 dark:text-red-400" id="unknown-count">0</span>
                            </div>
                            <div class="h-2 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden">
                                <div id="unknown-progress" class="h-full bg-red-500 rounded-full" style="width: 0%"></div>
                            </div>
                        </div>
                        
                        <div>
                            <div class="flex justify-between text-sm mb-1">
                                <span class="text-slate-600 dark:text-slate-300">掌握</span>
                                <span class="font-medium text-blue-600 dark:text-blue-400" id="mastered-count">0</span>
                            </div>
                            <div class="h-2 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden">
                                <div id="mastered-progress" class="h-full bg-blue-500 rounded-full" style="width: 0%"></div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- 统计区域 -->
        <section id="stats-section" class="hidden">
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-md p-6 max-w-3xl mx-auto">
                <h2 class="text-xl font-semibold mb-6 text-slate-800 dark:text-slate-100">学习统计</h2>
                
                <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
                    <div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-5 text-center">
                        <div class="text-3xl font-bold text-blue-600 dark:text-blue-400 mb-2" id="total-words-stats">0</div>
                        <div class="text-slate-600 dark:text-slate-300">总单词数</div>
                    </div>
                    
                    <div class="bg-green-50 dark:bg-green-900/20 rounded-lg p-5 text-center">
                        <div class="text-3xl font-bold text-green-600 dark:text-green-400 mb-2" id="mastered-words-stats">0</div>
                        <div class="text-slate-600 dark:text-slate-300">已掌握</div>
                    </div>
                    
                    <div class="bg-amber-50 dark:bg-amber-900/20 rounded-lg p-5 text-center">
                        <div class="text-3xl font-bold text-amber-600 dark:text-amber-400 mb-2" id="review-words-stats">0</div>
                        <div class="text-slate-600 dark:text-slate-300">待复习</div>
                    </div>
                </div>
                
                <div class="mb-6">
                    <h3 class="font-medium mb-3 text-slate-800 dark:text-slate-100">复习计划</h3>
                    <div class="space-y-3">
                        <div class="flex items-center justify-between p-3 bg-slate-50 dark:bg-slate-700/50 rounded-lg">
                            <div>
                                <div class="font-medium text-slate-800 dark:text-slate-100">5分钟后</div>
                                <div class="text-sm text-slate-500 dark:text-slate-400" id="review-5min">0 个单词</div>
                            </div>
                            <div class="text-sm bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 px-2 py-1 rounded">
                                即将复习
                            </div>
                        </div>
                        
                        <div class="flex items-center justify-between p-3 bg-slate-50 dark:bg-slate-700/50 rounded-lg">
                            <div>
                                <div class="font-medium text-slate-800 dark:text-slate-100">30分钟后</div>
                                <div class="text-sm text-slate-500 dark:text-slate-400" id="review-30min">0 个单词</div>
                            </div>
                            <div class="text-sm bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 px-2 py-1 rounded">
                                即将复习
                            </div>
                        </div>
                        
                        <div class="flex items-center justify-between p-3 bg-slate-50 dark:bg-slate-700/50 rounded-lg">
                            <div>
                                <div class="font-medium text-slate-800 dark:text-slate-100">12小时后</div>
                                <div class="text-sm text-slate-500 dark:text-slate-400" id="review-12h">0 个单词</div>
                            </div>
                            <div class="text-sm bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 px-2 py-1 rounded">
                                即将复习
                            </div>
                        </div>
                        
                        <div class="flex items-center justify-between p-3 bg-slate-50 dark:bg-slate-700/50 rounded-lg">
                            <div>
                                <div class="font-medium text-slate-800 dark:text-slate-100">1天后</div>
                                <div class="text-sm text-slate-500 dark:text-slate-400" id="review-1d">0 个单词</div>
                            </div>
                            <div class="text-sm bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300 px-2 py-1 rounded">
                                即将复习
                            </div>
                        </div>
                    </div>
                </div>
                
                <button 
                    id="back-to-learning" 
                    class="w-full py-3 bg-slate-100 hover:bg-slate-200 dark:bg-slate-700 dark:hover:bg-slate-600 rounded-lg font-medium transition-colors"
                >
                    返回学习
                </button>
            </div>
        </section>
    </main>

    <script>
        // 数据结构
        let words = [];
        let currentIndex = 0;
        let reviewSchedule = {};

        // DOM 元素
        const inputSection = document.getElementById('input-section');
        const learningSection = document.getElementById('learning-section');
        const statsSection = document.getElementById('stats-section');
        const wordInput = document.getElementById('word-input');
        const wordCount = document.getElementById('word-count');
        const wordCard = document.getElementById('word-card');
        const currentWord = document.getElementById('current-word');
        const currentMeaning = document.getElementById('current-meaning');
        const currentIndexEl = document.getElementById('current-index');
        const totalWordsEl = document.getElementById('total-words');
        const reviewCountEl = document.getElementById('review-count');

        // 进度统计元素
        const knownCountEl = document.getElementById('known-count');
        const unknownCountEl = document.getElementById('unknown-count');
        const masteredCountEl = document.getElementById('mastered-count');
        const knownProgressEl = document.getElementById('known-progress');
        const unknownProgressEl = document.getElementById('unknown-progress');
        const masteredProgressEl = document.getElementById('mastered-progress');

        // 统计页面元素
        const totalWordsStatsEl = document.getElementById('total-words-stats');
        const masteredWordsStatsEl = document.getElementById('mastered-words-stats');
        const reviewWordsStatsEl = document.getElementById('review-words-stats');
        const review5minEl = document.getElementById('review-5min');
        const review30minEl = document.getElementById('review-30min');
        const review12hEl = document.getElementById('review-12h');
        const review1dEl = document.getElementById('review-1d');

        // 按钮
        const statsBtn = document.getElementById('stats-btn');
        const resetBtn = document.getElementById('reset-btn');
        const backToLearningBtn = document.getElementById('back-to-learning');

        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            loadWords();
            updateWordCount();
            setupEventListeners();
            
            // 如果已有单词数据,直接进入学习界面
            if (words.length > 0) {
                showLearningSection();
                updateLearningUI();
            }
        });

        // 事件监听器
        function setupEventListeners() {
            // 单词输入表单
            document.getElementById('word-input-form').addEventListener('submit', function(e) {
                e.preventDefault();
                processWords();
            });

            // 单词输入区域字符统计
            wordInput.addEventListener('input', updateWordCount);

            // 单词卡片翻转
            wordCard.addEventListener('click', function() {
                this.classList.toggle('flipped');
            });

            // 分类按钮
            document.getElementById('known-btn').addEventListener('click', function() {
                classifyWord('known');
            });

            document.getElementById('unknown-btn').addEventListener('click', function() {
                classifyWord('unknown');
            });

            document.getElementById('mastered-btn').addEventListener('click', function() {
                classifyWord('mastered');
            });

            // 统计按钮
            statsBtn.addEventListener('click', showStatsSection);

            // 重置按钮
            resetBtn.addEventListener('click', function() {
                if (confirm('确定要重置所有学习数据吗?')) {
                    localStorage.removeItem('ebbinghaus_words');
                    words = [];
                    reviewSchedule = {};
                    showInputSection();
                }
            });

            // 返回学习按钮
            backToLearningBtn.addEventListener('click', showLearningSection);
        }

        // 更新单词计数
        function updateWordCount() {
            const text = wordInput.value.trim();
            const count = text ? text.split('\n').filter(word => word.trim() !== '').length : 0;
            wordCount.textContent = count;
        }

        // 处理输入的单词
        function processWords() {
            const text = wordInput.value.trim();
            if (!text) {
                showModal('请输入单词');
                return;
            }

            const wordArray = text.split('\n')
                .map(word => word.trim())
                .filter(word => word !== '')
                .slice(0, 100); // 限制最多100个单词

            if (wordArray.length === 0) {
                showModal('请输入有效的单词');
                return;
            }

            // 初始化单词数据
            words = wordArray.map((word, index) => ({
                id: index,
                word: word,
                meaning: `【${word}】的释义`, // 实际应用中可以从API获取
                status: 'new', // new, known, unknown, mastered
                nextReview: null,
                reviewCount: 0
            }));

            saveWords();
            showLearningSection();
            updateLearningUI();
        }

        // 显示输入区域
        function showInputSection() {
            inputSection.classList.remove('hidden');
            learningSection.classList.add('hidden');
            statsSection.classList.add('hidden');
        }

        // 显示学习区域
        function showLearningSection() {
            inputSection.classList.add('hidden');
            learningSection.classList.remove('hidden');
            statsSection.classList.add('hidden');
            
            // 重置卡片状态
            wordCard.classList.remove('flipped');
        }

        // 显示统计区域
        function showStatsSection() {
            inputSection.classList.add('hidden');
            learningSection.classList.add('hidden');
            statsSection.classList.remove('hidden');
            updateStatsUI();
        }

        // 更新学习界面
        function updateLearningUI() {
            if (words.length === 0) return;

            // 确保索引在有效范围内
            if (currentIndex >= words.length) {
                currentIndex = 0;
            }

            const word = words[currentIndex];
            currentWord.textContent = word.word;
            currentMeaning.textContent = word.meaning;
            currentIndexEl.textContent = currentIndex + 1;
            totalWordsEl.textContent = words.length;

            // 更新复习计数
            const reviewCount = words.filter(w => w.status === 'unknown' || w.status === 'known').length;
            reviewCountEl.textContent = reviewCount;

            // 更新进度统计
            updateProgressStats();
        }

        // 更新进度统计
        function updateProgressStats() {
            const knownCount = words.filter(w => w.status === 'known').length;
            const unknownCount = words.filter(w => w.status === 'unknown').length;
            const masteredCount = words.filter(w => w.status === 'mastered').length;
            const totalCount = words.length;

            knownCountEl.textContent = knownCount;
            unknownCountEl.textContent = unknownCount;
            masteredCountEl.textContent = masteredCount;

            knownProgressEl.style.width = totalCount ? `${(knownCount / totalCount) * 100}%` : '0%';
            unknownProgressEl.style.width = totalCount ? `${(unknownCount / totalCount) * 100}%` : '0%';
            masteredProgressEl.style.width = totalCount ? `${(masteredCount / totalCount) * 100}%` : '0%';
        }

        // 更新统计页面
        function updateStatsUI() {
            const totalCount = words.length;
            const masteredCount = words.filter(w => w.status === 'mastered').length;
            const reviewCount = words.filter(w => w.status === 'unknown' || w.status === 'known').length;

            totalWordsStatsEl.textContent = totalCount;
            masteredWordsStatsEl.textContent = masteredCount;
            reviewWordsStatsEl.textContent = reviewCount;

            // 计算各时间段待复习单词数
            const now = Date.now();
            let count5min = 0;
            let count30min = 0;
            let count12h = 0;
            let count1d = 0;

            words.forEach(word => {
                if (word.nextReview) {
                    const nextReview = new Date(word.nextReview).getTime();
                    const diffMinutes = (nextReview - now) / (1000 * 60);
                    
                    if (diffMinutes <= 5) count5min++;
                    if (diffMinutes <= 30) count30min++;
                    if (diffMinutes <= 720) count12h++; // 12小时
                    if (diffMinutes <= 1440) count1d++; // 24小时
                }
            });

            review5minEl.textContent = `${count5min} 个单词`;
            review30minEl.textContent = `${count30min} 个单词`;
            review12hEl.textContent = `${count12h} 个单词`;
            review1dEl.textContent = `${count1d} 个单词`;
        }

        // 分类单词
        function classifyWord(status) {
            if (words.length === 0) return;

            const word = words[currentIndex];
            word.status = status;
            word.reviewCount++;

            // 根据艾宾浩斯遗忘曲线设置下次复习时间
            const intervals = [5, 30, 720, 1440]; // 5分钟, 30分钟, 12小时, 24小时 (分钟)
            const reviewIndex = Math.min(word.reviewCount, intervals.length - 1);
            const nextReviewTime = new Date(Date.now() + intervals[reviewIndex] * 60000);
            word.nextReview = nextReviewTime.toISOString();

            saveWords();
            moveToNextWord();
        }

        // 移动到下一个单词
        function moveToNextWord() {
            currentIndex++;
            
            // 如果到达末尾,回到开头
            if (currentIndex >= words.length) {
                currentIndex = 0;
            }

            // 重置卡片状态
            wordCard.classList.remove('flipped');
            updateLearningUI();
        }

        // 保存单词数据到本地存储
        function saveWords() {
            try {
                localStorage.setItem('ebbinghaus_words', JSON.stringify(words));
            } catch (e) {
                console.error('保存数据失败:', e);
            }
        }

        // 从本地存储加载单词数据
        function loadWords() {
            try {
                const savedWords = localStorage.getItem('ebbinghaus_words');
                if (savedWords) {
                    words = JSON.parse(savedWords);
                }
            } catch (e) {
                console.error('加载数据失败:', e);
                words = [];
            }
        }

        // 检查是否需要复习
        function checkReviewSchedule() {
            const now = Date.now();
            let needsReview = false;

            words.forEach(word => {
                if (word.nextReview && new Date(word.nextReview).getTime() <= now) {
                    needsReview = true;
                }
            });

            if (needsReview) {
                // 在实际应用中,这里可以触发通知
                console.log('有单词需要复习了!');
            }
        }

        // 每分钟检查一次复习计划
        setInterval(checkReviewSchedule, 60000);
    </script>
</body>
</html>
03-12 08:28

登录 后可以评论

Comments

登录 后可以评论