// 背诵排序功能脚本 let extractedItems = []; let sortedItems = []; // 原始排序后的所有项目 let availableItems = []; // 当前可用于转盘的项目(排除已掌握的) let masteredItems = []; // 已掌握的项目列表 let currentSpinIndex = 0; let currentSelectedItem = null; // 当前转盘选中的项目 let isSpinning = false; // 颜色配置 - 转盘使用不同颜色 const colors = [ '#667eea', '#764ba2', '#f093fb', '#f5576c', '#4facfe', '#00f2fe', '#43e97b', '#38f9d7', '#fa709a', '#fee140', '#30cfd0', '#330867' ]; // DOM元素 const textInput = document.getElementById('textInput'); const extractBtn = document.getElementById('extractBtn'); const extractedSection = document.getElementById('extractedSection'); const itemsList = document.getElementById('itemsList'); const itemCount = document.getElementById('itemCount'); const sortBtn = document.getElementById('sortBtn'); const wheelSection = document.getElementById('wheelSection'); const wheel = document.getElementById('wheel'); const spinBtn = document.getElementById('spinBtn'); const currentItem = document.getElementById('currentItem'); const resultSection = document.getElementById('resultSection'); const sortedList = document.getElementById('sortedList'); const resetBtn = document.getElementById('resetBtn'); const exportTxtBtn = document.getElementById('exportTxtBtn'); const exportJsonBtn = document.getElementById('exportJsonBtn'); const exportCsvBtn = document.getElementById('exportCsvBtn'); const masteredBtn = document.getElementById('masteredBtn'); const forgotBtn = document.getElementById('forgotBtn'); const masteryButtons = document.getElementById('masteryButtons'); const remainingNum = document.getElementById('remainingNum'); // 本地存储键 const STORAGE_KEY_EXTRACTED = 'recitation_extracted_items'; const STORAGE_KEY_SORTED = 'recitation_sorted_items'; const STORAGE_KEY_ORIGINAL_TEXT = 'recitation_original_text'; const STORAGE_KEY_MASTERED = 'recitation_mastered_items'; // 页面加载时恢复数据 window.addEventListener('DOMContentLoaded', () => { restoreFromStorage(); }); // 保存到本地存储 function saveToStorage() { try { if (extractedItems.length > 0) { localStorage.setItem(STORAGE_KEY_EXTRACTED, JSON.stringify(extractedItems)); } if (sortedItems.length > 0) { localStorage.setItem(STORAGE_KEY_SORTED, JSON.stringify(sortedItems)); } if (textInput.value.trim()) { localStorage.setItem(STORAGE_KEY_ORIGINAL_TEXT, textInput.value); } if (masteredItems.length > 0) { localStorage.setItem(STORAGE_KEY_MASTERED, JSON.stringify(masteredItems)); } } catch (e) { console.error('保存到本地存储失败:', e); } } // 从本地存储恢复 function restoreFromStorage() { try { const savedExtracted = localStorage.getItem(STORAGE_KEY_EXTRACTED); const savedSorted = localStorage.getItem(STORAGE_KEY_SORTED); const savedText = localStorage.getItem(STORAGE_KEY_ORIGINAL_TEXT); const savedMastered = localStorage.getItem(STORAGE_KEY_MASTERED); if (savedText) { textInput.value = savedText; } if (savedExtracted) { extractedItems = JSON.parse(savedExtracted); displayExtractedItems(extractedItems); extractedSection.style.display = 'block'; textInput.disabled = true; } if (savedMastered) { masteredItems = JSON.parse(savedMastered); } if (savedSorted) { sortedItems = JSON.parse(savedSorted); updateAvailableItems(); // 更新可用项目列表 displaySortedItems(sortedItems); createWheel(availableItems); wheelSection.style.display = 'block'; resultSection.style.display = 'block'; updateMasteryInfo(); } } catch (e) { console.error('从本地存储恢复失败:', e); } } // 清除本地存储 function clearStorage() { localStorage.removeItem(STORAGE_KEY_EXTRACTED); localStorage.removeItem(STORAGE_KEY_SORTED); localStorage.removeItem(STORAGE_KEY_ORIGINAL_TEXT); localStorage.removeItem(STORAGE_KEY_MASTERED); } // 更新可用项目列表(排除已掌握的) function updateAvailableItems() { availableItems = sortedItems.filter(item => !masteredItems.includes(item)); updateMasteryInfo(); } // 更新掌握情况信息 function updateMasteryInfo() { remainingNum.textContent = availableItems.length; } // 提取知识点 extractBtn.addEventListener('click', async () => { const text = textInput.value.trim(); if (!text) { alert('请输入需要识别的文本'); return; } extractBtn.disabled = true; extractBtn.textContent = '识别中...'; try { const response = await fetch('/api/extract', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }) }); const data = await response.json(); if (data.success) { extractedItems = data.items; displayExtractedItems(extractedItems); extractedSection.style.display = 'block'; textInput.disabled = true; saveToStorage(); // 保存到本地存储 } else { alert(data.message || '提取失败'); } } catch (error) { console.error('提取失败:', error); alert('提取失败,请检查网络连接'); } finally { extractBtn.disabled = false; extractBtn.textContent = '识别知识点'; } }); // 显示提取到的项目 function displayExtractedItems(items) { itemCount.textContent = items.length; itemsList.innerHTML = ''; items.forEach((item, index) => { const tag = document.createElement('span'); tag.className = 'item-tag'; tag.textContent = `${index + 1}. ${item}`; itemsList.appendChild(tag); }); } // 开始排序 sortBtn.addEventListener('click', async () => { if (extractedItems.length === 0) { alert('请先提取知识点'); return; } sortBtn.disabled = true; sortBtn.textContent = '排序中...'; try { const response = await fetch('/api/sort', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items: extractedItems }) }); const data = await response.json(); if (data.success) { sortedItems = data.items; masteredItems = []; // 重新排序时重置已掌握列表 updateAvailableItems(); // 更新可用项目 displaySortedItems(sortedItems); createWheel(availableItems); wheelSection.style.display = 'block'; resultSection.style.display = 'block'; currentSpinIndex = 0; currentSelectedItem = null; masteryButtons.style.display = 'none'; saveToStorage(); // 保存到本地存储 } else { alert(data.message || '排序失败'); } } catch (error) { console.error('排序失败:', error); alert('排序失败,请检查网络连接'); } finally { sortBtn.disabled = false; sortBtn.textContent = '开始随机排序'; } }); // 创建转盘 - 使用SVG实现真实转盘效果 function createWheel(items) { wheel.innerHTML = ''; if (items.length === 0) { currentItem.textContent = '所有知识点已掌握!'; spinBtn.disabled = true; return; } spinBtn.disabled = false; const anglePerItem = 360 / items.length; const radius = 190; // 转盘半径,考虑边框 const centerX = 200; const centerY = 200; // 创建SVG转盘 const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', '400'); svg.setAttribute('height', '400'); svg.setAttribute('viewBox', '0 0 400 400'); svg.style.position = 'absolute'; svg.style.top = '0'; svg.style.left = '0'; svg.style.width = '100%'; svg.style.height = '100%'; svg.style.transition = 'transform 3s cubic-bezier(0.17, 0.67, 0.12, 0.99)'; items.forEach((item, index) => { const startAngle = (index * anglePerItem - 90) * Math.PI / 180; const endAngle = ((index + 1) * anglePerItem - 90) * Math.PI / 180; const x1 = centerX + radius * Math.cos(startAngle); const y1 = centerY + radius * Math.sin(startAngle); const x2 = centerX + radius * Math.cos(endAngle); const y2 = centerY + radius * Math.sin(endAngle); const largeArcFlag = anglePerItem > 180 ? 1 : 0; const pathData = [ `M ${centerX} ${centerY}`, `L ${x1} ${y1}`, `A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2}`, 'Z' ].join(' '); const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('d', pathData); const colorIndex = index % colors.length; path.setAttribute('fill', colors[colorIndex]); path.setAttribute('stroke', '#fff'); path.setAttribute('stroke-width', '2'); svg.appendChild(path); // 添加文本 const midAngle = (startAngle + endAngle) / 2; const textRadius = radius * 0.7; const textX = centerX + textRadius * Math.cos(midAngle); const textY = centerY + textRadius * Math.sin(midAngle); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', textX); text.setAttribute('y', textY); text.setAttribute('text-anchor', 'middle'); text.setAttribute('fill', 'white'); text.setAttribute('font-size', items.length > 8 ? '12' : '14'); text.setAttribute('font-weight', 'bold'); text.setAttribute('transform', `rotate(${(midAngle * 180 / Math.PI + 90)}, ${textX}, ${textY})`); const displayText = item.length > 12 ? item.substring(0, 12) + '...' : item; text.textContent = displayText; svg.appendChild(text); }); wheel.appendChild(svg); } // 转动转盘 spinBtn.addEventListener('click', () => { if (isSpinning || availableItems.length === 0) return; isSpinning = true; spinBtn.disabled = true; currentItem.textContent = '转盘中...'; masteryButtons.style.display = 'none'; // 隐藏按钮,等待转盘停止 currentSelectedItem = null; // 随机选择一个项目,并增加多圈旋转效果 const randomIndex = Math.floor(Math.random() * availableItems.length); const spins = 3; // 转3圈 const anglePerItem = 360 / availableItems.length; // 计算目标角度:转多圈 + 指向选中项 const targetAngle = spins * 360 + (360 - (randomIndex * anglePerItem) - anglePerItem / 2); // 获取当前角度 const svg = wheel.querySelector('svg'); if (!svg) return; const currentAngle = getCurrentRotation(svg); // 计算总旋转角度(考虑当前角度) const totalRotation = currentAngle + targetAngle; svg.style.transform = `rotate(${totalRotation}deg)`; // 转盘停止后显示结果 setTimeout(() => { currentSelectedItem = availableItems[randomIndex]; currentItem.textContent = `${randomIndex + 1}. ${currentSelectedItem}`; currentSpinIndex = randomIndex; isSpinning = false; spinBtn.disabled = false; masteryButtons.style.display = 'flex'; // 显示掌握按钮 }, 3000); }); // 获取当前旋转角度 function getCurrentRotation(element) { if (!element) return 0; const style = window.getComputedStyle(element); const transform = style.transform; if (transform === 'none') return 0; const matrix = new DOMMatrix(transform); const angle = Math.atan2(matrix.b, matrix.a) * (180 / Math.PI); return angle; } // 背会了按钮 masteredBtn.addEventListener('click', () => { if (!currentSelectedItem) return; // 添加到已掌握列表 if (!masteredItems.includes(currentSelectedItem)) { masteredItems.push(currentSelectedItem); } // 更新可用项目列表 updateAvailableItems(); // 重新创建转盘(因为项目数量可能改变) createWheel(availableItems); // 隐藏按钮和当前项目显示 masteryButtons.style.display = 'none'; currentItem.textContent = ''; currentSelectedItem = null; // 重置转盘角度 const svg = wheel.querySelector('svg'); if (svg) { svg.style.transform = 'rotate(0deg)'; } // 保存状态 saveToStorage(); // 更新排序结果列表显示(标记已掌握的) displaySortedItems(sortedItems); }); // 忘记了按钮 forgotBtn.addEventListener('click', () => { if (!currentSelectedItem) return; // 忘记了的项目保留在列表中,不做任何操作 // 隐藏按钮,可以继续转动转盘 masteryButtons.style.display = 'none'; currentItem.textContent = ''; currentSelectedItem = null; }); // 显示排序结果 function displaySortedItems(items) { sortedList.innerHTML = ''; items.forEach((item, index) => { const itemDiv = document.createElement('div'); itemDiv.className = 'sorted-item'; // 如果已掌握,添加特殊样式 if (masteredItems.includes(item)) { itemDiv.classList.add('sorted-item-mastered'); } const numberSpan = document.createElement('span'); numberSpan.className = 'sorted-item-number'; numberSpan.textContent = index + 1; const textSpan = document.createElement('span'); textSpan.className = 'sorted-item-text'; textSpan.textContent = item; if (masteredItems.includes(item)) { const masteredIcon = document.createElement('span'); masteredIcon.className = 'mastered-icon'; masteredIcon.textContent = '✓'; textSpan.appendChild(masteredIcon); } itemDiv.appendChild(numberSpan); itemDiv.appendChild(textSpan); sortedList.appendChild(itemDiv); }); } // 导出功能 exportTxtBtn.addEventListener('click', () => exportData('txt')); exportJsonBtn.addEventListener('click', () => exportData('json')); exportCsvBtn.addEventListener('click', () => exportData('csv')); function exportData(format) { // 导出时只导出当前可用的项目(未掌握的) const itemsToExport = availableItems.length > 0 ? availableItems : sortedItems; if (itemsToExport.length === 0) { alert('没有可导出的数据'); return; } fetch('/api/export/sorted', { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8' }, body: JSON.stringify({ items: itemsToExport, format: format }) }) .then(response => { if (!response.ok) { throw new Error('导出失败'); } return response.blob(); }) .then(blob => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, '').replace('T', '_'); a.download = `背诵排序结果_${timestamp}.${format}`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); }) .catch(error => { console.error('导出失败:', error); alert('导出失败,请稍后重试'); }); } // 重置 resetBtn.addEventListener('click', () => { extractedItems = []; sortedItems = []; availableItems = []; masteredItems = []; currentSpinIndex = 0; currentSelectedItem = null; textInput.value = ''; textInput.disabled = false; extractedSection.style.display = 'none'; wheelSection.style.display = 'none'; resultSection.style.display = 'none'; wheel.innerHTML = ''; const svg = wheel.querySelector('svg'); if (svg) { svg.style.transform = 'rotate(0deg)'; } currentItem.textContent = ''; masteryButtons.style.display = 'none'; isSpinning = false; spinBtn.disabled = false; updateMasteryInfo(); clearStorage(); // 清除本地存储 });