clean: 移除展示頁面和舊的規格文件
- 刪除 /vocab-designs 展示頁面(已完成使命) - 清理過時的中文規格文件 - Portal重構完成後不再需要樣式對比頁面 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
421edd0589
commit
5583b763bc
|
|
@ -1,743 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Navigation } from '@/components/Navigation'
|
||||
import { ProtectedRoute } from '@/components/ProtectedRoute'
|
||||
|
||||
export default function VocabDesignsPage() {
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<VocabDesignsContent />
|
||||
</ProtectedRoute>
|
||||
)
|
||||
}
|
||||
|
||||
function VocabDesignsContent() {
|
||||
const [selectedDesign, setSelectedDesign] = useState('modern')
|
||||
const [showPopup, setShowPopup] = useState(false)
|
||||
|
||||
// 假詞彙數據
|
||||
const mockWord = {
|
||||
word: 'elaborate',
|
||||
pronunciation: '/ɪˈlæbərət/',
|
||||
partOfSpeech: 'verb',
|
||||
translation: '詳細說明',
|
||||
definition: 'To explain something in more detail; to develop or present a theory, policy, or system in further detail',
|
||||
synonyms: ['explain', 'detail', 'expand', 'clarify'],
|
||||
difficultyLevel: 'B2',
|
||||
isHighValue: true,
|
||||
example: 'Could you elaborate on your proposal?',
|
||||
exampleTranslation: '你能詳細說明一下你的提案嗎?'
|
||||
}
|
||||
|
||||
const designs = [
|
||||
{ id: 'modern', name: '現代玻璃', description: '毛玻璃效果,現代感設計' },
|
||||
{ id: 'classic', name: '經典卡片', description: '傳統卡片風格,清晰分區' },
|
||||
{ id: 'minimal', name: '極簡風格', description: '簡潔乾淨,突出重點' },
|
||||
{ id: 'magazine', name: '雜誌排版', description: '類似雜誌的排版風格' },
|
||||
{ id: 'mobile', name: '移動應用', description: 'iOS/Android應用風格' },
|
||||
{ id: 'learning', name: '學習卡片', description: '與學習功能一致的風格' },
|
||||
{ id: 'flashcard', name: '詞卡風格', description: '參考詞卡詳細頁面的設計' }
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navigation />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{/* 頁面標題 */}
|
||||
<div className="text-center mb-8">
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-4">詞彙明細版型展示</h1>
|
||||
<p className="text-xl text-gray-600 mb-6">6種不同風格的詞彙彈窗設計</p>
|
||||
|
||||
{/* 測試詞彙 */}
|
||||
<div className="inline-block bg-white rounded-lg shadow-sm p-4 border border-gray-200">
|
||||
<span className="text-gray-600 text-sm">測試詞彙:</span>
|
||||
<button
|
||||
onClick={() => setShowPopup(true)}
|
||||
className="ml-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
||||
>
|
||||
點擊查看 "elaborate"
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 版型選擇器 */}
|
||||
<div className="flex justify-center mb-8">
|
||||
<div className="bg-white rounded-xl shadow-sm p-2 inline-flex flex-wrap gap-2">
|
||||
{designs.map((design) => (
|
||||
<button
|
||||
key={design.id}
|
||||
onClick={() => setSelectedDesign(design.id)}
|
||||
className={`px-4 py-2 rounded-lg transition-all ${
|
||||
selectedDesign === design.id
|
||||
? 'bg-primary text-white shadow-md'
|
||||
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
{design.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 版型預覽 */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* 左側:版型說明 */}
|
||||
<div className="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
{designs.find(d => d.id === selectedDesign)?.name}
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
{designs.find(d => d.id === selectedDesign)?.description}
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">設計特色</h3>
|
||||
<ul className="text-sm text-gray-700 space-y-1">
|
||||
{getDesignFeatures(selectedDesign).map((feature, idx) => (
|
||||
<li key={idx} className="flex items-center gap-2">
|
||||
<span className="text-blue-500">•</span>
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">適用場景</h3>
|
||||
<p className="text-sm text-gray-700">
|
||||
{getDesignScenario(selectedDesign)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右側:版型預覽 */}
|
||||
<div className="bg-gradient-to-br from-gray-100 to-gray-200 rounded-xl p-8 flex items-center justify-center min-h-[600px] relative">
|
||||
<div className="text-center">
|
||||
<p className="text-gray-600 mb-4">點擊下方按鈕預覽版型效果</p>
|
||||
<button
|
||||
onClick={() => setShowPopup(true)}
|
||||
className="px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors font-medium shadow-lg"
|
||||
>
|
||||
預覽 "{mockWord.word}" 詞彙詳情
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 模擬背景文字 */}
|
||||
<div className="absolute inset-4 opacity-10 text-gray-500 text-lg leading-relaxed pointer-events-none">
|
||||
This is a sample sentence where you can click on any word to see the elaborate definition and detailed explanation.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 詞彙彈窗 - 根據選擇的設計風格渲染 */}
|
||||
{showPopup && (
|
||||
<>
|
||||
{/* 背景遮罩 */}
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-50 z-40"
|
||||
onClick={() => setShowPopup(false)}
|
||||
/>
|
||||
|
||||
{/* 彈窗內容 */}
|
||||
<div className="fixed inset-0 flex items-center justify-center z-50 p-4">
|
||||
{renderVocabPopup(selectedDesign, mockWord, () => setShowPopup(false))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 渲染不同風格的詞彙彈窗
|
||||
function renderVocabPopup(design: string, word: any, onClose: () => void) {
|
||||
const handleSave = () => {
|
||||
alert(`✅ 已將「${word.word}」保存到詞卡!`)
|
||||
onClose()
|
||||
}
|
||||
|
||||
switch (design) {
|
||||
case 'modern':
|
||||
return <ModernGlassDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
case 'classic':
|
||||
return <ClassicCardDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
case 'minimal':
|
||||
return <MinimalDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
case 'magazine':
|
||||
return <MagazineDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
case 'mobile':
|
||||
return <MobileAppDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
case 'learning':
|
||||
return <LearningCardDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
case 'flashcard':
|
||||
return <FlashcardDetailDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
default:
|
||||
return <ModernGlassDesign word={word} onClose={onClose} onSave={handleSave} />
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 現代玻璃風格
|
||||
function ModernGlassDesign({ word, onClose, onSave }: any) {
|
||||
return (
|
||||
<div
|
||||
className="bg-white rounded-2xl shadow-2xl border-0 w-80 backdrop-blur-sm"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.98)',
|
||||
backdropFilter: 'blur(12px)',
|
||||
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.8)'
|
||||
}}
|
||||
>
|
||||
<div className="relative p-5 pb-0">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-3 right-3 w-6 h-6 rounded-full bg-gray-100 hover:bg-gray-200 transition-colors flex items-center justify-center text-gray-500"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
<div className="pr-8">
|
||||
<div className="flex items-baseline gap-3 mb-1">
|
||||
<h3 className="text-2xl font-bold text-gray-900">{word.word}</h3>
|
||||
{word.isHighValue && <span className="text-yellow-500 text-lg">⭐</span>}
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-gray-600">
|
||||
<span className="text-sm font-medium">{word.pronunciation}</span>
|
||||
<span className="text-xs px-2 py-1 rounded-full font-medium bg-blue-100 text-blue-700">
|
||||
{word.difficultyLevel}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-5 py-4 space-y-4">
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">翻譯</div>
|
||||
<div className="text-lg font-semibold text-gray-900">{word.translation}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">定義</div>
|
||||
<div className="text-sm text-gray-700 leading-relaxed">{word.definition}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">同義詞</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{word.synonyms.map((synonym: string, idx: number) => (
|
||||
<span key={idx} className="bg-blue-50 text-blue-700 px-2 py-1 rounded text-xs font-medium border border-blue-200">
|
||||
{synonym}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-5 pt-2 border-t border-gray-100">
|
||||
<button
|
||||
onClick={onSave}
|
||||
className="w-full bg-gradient-to-r from-blue-600 to-blue-700 text-white py-3 px-4 rounded-xl font-medium hover:from-blue-700 hover:to-blue-800 transition-all duration-200 transform hover:scale-105 active:scale-95 flex items-center justify-center gap-2 shadow-lg"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
||||
</svg>
|
||||
<span className="text-sm font-medium">加入詞卡</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 2. 經典卡片風格
|
||||
function ClassicCardDesign({ word, onClose, onSave }: any) {
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-lg border border-gray-200 w-96 max-w-md">
|
||||
<div className="bg-gradient-to-r from-blue-600 to-blue-700 text-white p-4 rounded-t-lg">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">{word.word}</h3>
|
||||
<p className="text-blue-100 text-sm">{word.pronunciation}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-blue-100 hover:text-white w-8 h-8 rounded-full hover:bg-blue-800 transition-colors flex items-center justify-center"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-sm font-medium">
|
||||
{word.partOfSpeech}
|
||||
</span>
|
||||
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||
word.difficultyLevel === 'A1' || word.difficultyLevel === 'A2' ? 'bg-green-100 text-green-700' :
|
||||
word.difficultyLevel === 'B1' || word.difficultyLevel === 'B2' ? 'bg-yellow-100 text-yellow-700' :
|
||||
'bg-red-100 text-red-700'
|
||||
}`}>
|
||||
{word.difficultyLevel}
|
||||
</span>
|
||||
{word.isHighValue && (
|
||||
<span className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded-full text-xs font-medium">
|
||||
⭐ 高價值
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 rounded-lg p-3 border-l-4 border-blue-400">
|
||||
<h4 className="font-semibold text-blue-900 mb-1">中文翻譯</h4>
|
||||
<p className="text-blue-800 font-medium">{word.translation}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-3">
|
||||
<h4 className="font-semibold text-gray-900 mb-2">英文定義</h4>
|
||||
<p className="text-gray-700 text-sm leading-relaxed">{word.definition}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 mb-2">同義詞</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{word.synonyms.map((synonym: string, idx: number) => (
|
||||
<span key={idx} className="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-sm">
|
||||
{synonym}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onSave}
|
||||
className="w-full bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<span>💾</span>
|
||||
<span>保存到詞卡</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 3. 極簡風格
|
||||
function MinimalDesign({ word, onClose, onSave }: any) {
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-lg w-72 border border-gray-100">
|
||||
<div className="p-4 border-b border-gray-100">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-xl font-bold text-gray-900">{word.word}</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 w-5 h-5 flex items-center justify-center"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-1">{word.pronunciation}</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-3">
|
||||
<div>
|
||||
<span className="text-lg font-medium text-gray-900">{word.translation}</span>
|
||||
<span className="ml-2 text-xs text-gray-500">({word.partOfSpeech})</span>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600 leading-relaxed">{word.definition}</p>
|
||||
|
||||
<div className="flex flex-wrap gap-1 pt-2">
|
||||
{word.synonyms.slice(0, 3).map((synonym: string, idx: number) => (
|
||||
<span key={idx} className="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded">
|
||||
{synonym}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onSave}
|
||||
className="w-full mt-4 bg-gray-900 text-white py-2 rounded text-sm font-medium hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
加入學習
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 4. 雜誌排版風格
|
||||
function MagazineDesign({ word, onClose, onSave }: any) {
|
||||
return (
|
||||
<div className="bg-white shadow-xl w-96 max-w-md" style={{ fontFamily: 'Georgia, serif' }}>
|
||||
<div className="p-6 border-b-2 border-gray-900">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className="text-3xl font-bold text-gray-900 mb-1" style={{ fontFamily: 'Georgia, serif' }}>
|
||||
{word.word}
|
||||
</h3>
|
||||
<div className="flex items-center gap-4 text-sm text-gray-600">
|
||||
<span className="italic">{word.pronunciation}</span>
|
||||
<span className="font-semibold">{word.partOfSpeech}</span>
|
||||
<span className="bg-gray-900 text-white px-2 py-1 text-xs">{word.difficultyLevel}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 text-xl"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<div className="mb-4">
|
||||
<h4 className="text-xs font-bold text-gray-500 uppercase tracking-widest mb-2 border-b border-gray-200 pb-1">
|
||||
Translation
|
||||
</h4>
|
||||
<p className="text-xl font-semibold text-gray-900" style={{ fontFamily: 'Georgia, serif' }}>
|
||||
{word.translation}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<h4 className="text-xs font-bold text-gray-500 uppercase tracking-widest mb-2">Definition</h4>
|
||||
<p className="text-sm text-gray-700 leading-relaxed" style={{ textAlign: 'justify' }}>
|
||||
{word.definition}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<h4 className="text-xs font-bold text-gray-500 uppercase tracking-widest mb-2">Synonyms</h4>
|
||||
<p className="text-sm text-gray-700 italic">
|
||||
{word.synonyms.join(', ')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onSave}
|
||||
className="w-full bg-gray-900 text-white py-3 font-semibold uppercase tracking-wider text-sm hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
Add to Collection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 5. 移動應用風格
|
||||
function MobileAppDesign({ word, onClose, onSave }: any) {
|
||||
return (
|
||||
<div className="bg-white rounded-3xl shadow-xl w-80 overflow-hidden">
|
||||
<div className="bg-gray-50 px-4 py-3 border-b border-gray-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold text-sm">
|
||||
{word.word[0].toUpperCase()}
|
||||
</div>
|
||||
<span className="font-medium text-gray-900">詞彙詳情</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-blue-500 font-medium text-sm"
|
||||
>
|
||||
完成
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4">
|
||||
<div className="text-center mb-4">
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-1">{word.word}</h3>
|
||||
<p className="text-gray-500 text-sm">{word.pronunciation}</p>
|
||||
<div className="flex items-center justify-center gap-2 mt-2">
|
||||
<span className="bg-gray-100 text-gray-700 px-2 py-1 rounded-full text-xs">
|
||||
{word.partOfSpeech}
|
||||
</span>
|
||||
<span className={`px-2 py-1 rounded-full text-xs ${
|
||||
word.difficultyLevel === 'A1' || word.difficultyLevel === 'A2' ? 'bg-green-100 text-green-700' :
|
||||
word.difficultyLevel === 'B1' || word.difficultyLevel === 'B2' ? 'bg-yellow-100 text-yellow-700' :
|
||||
'bg-red-100 text-red-700'
|
||||
}`}>
|
||||
{word.difficultyLevel}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3 py-2">
|
||||
<div className="w-8 text-center">🌏</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-gray-900">{word.translation}</div>
|
||||
<div className="text-xs text-gray-500">中文翻譯</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3 py-2 border-t border-gray-100">
|
||||
<div className="w-8 text-center">📝</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-sm text-gray-700 leading-relaxed">{word.definition}</div>
|
||||
<div className="text-xs text-gray-500 mt-1">英文定義</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3 py-2 border-t border-gray-100">
|
||||
<div className="w-8 text-center">🔗</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{word.synonyms.slice(0, 3).map((synonym: string, idx: number) => (
|
||||
<span key={idx} className="bg-gray-100 text-gray-700 px-2 py-1 rounded-full text-xs">
|
||||
{synonym}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mt-1">同義詞</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onSave}
|
||||
className="w-full mt-6 bg-blue-500 text-white py-3 rounded-xl font-medium text-base hover:bg-blue-600 transition-colors active:bg-blue-700"
|
||||
>
|
||||
加入詞卡
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 6. 學習卡片風格
|
||||
function LearningCardDesign({ word, onClose, onSave }: any) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl shadow-lg w-96 max-w-md overflow-hidden">
|
||||
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 p-5 border-b border-blue-200">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold">
|
||||
{word.word[0].toUpperCase()}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-gray-900">{word.word}</h3>
|
||||
<p className="text-sm text-gray-600">{word.partOfSpeech}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-sm font-medium text-gray-700">{word.pronunciation}</span>
|
||||
<button className="text-blue-600 hover:text-blue-700 p-1 rounded-full hover:bg-blue-50 transition-colors">
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-5 space-y-4">
|
||||
<div className="bg-green-50 rounded-lg p-4">
|
||||
<h4 className="font-semibold text-green-900 mb-2 text-left">翻譯</h4>
|
||||
<p className="text-green-800 font-medium text-left">{word.translation}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<h4 className="font-semibold text-gray-900 mb-2 text-left">定義</h4>
|
||||
<p className="text-gray-700 text-sm text-left leading-relaxed">{word.definition}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 rounded-lg p-4">
|
||||
<h4 className="font-semibold text-blue-900 mb-2 text-left">同義詞</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{word.synonyms.map((synonym: string, idx: number) => (
|
||||
<span key={idx} className="bg-white text-blue-700 px-3 py-1 rounded-full text-sm border border-blue-200">
|
||||
{synonym}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onSave}
|
||||
className="w-full bg-primary text-white py-3 rounded-lg font-medium hover:bg-primary-hover transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<span>💾</span>
|
||||
<span>保存學習</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 7. 詞卡風格 - 參考詞卡詳細頁面
|
||||
function FlashcardDetailDesign({ word, onClose, onSave }: any) {
|
||||
// 獲取CEFR等級顏色
|
||||
const getCEFRColor = (level: string) => {
|
||||
switch (level) {
|
||||
case 'A1': return 'bg-green-100 text-green-700 border-green-200'
|
||||
case 'A2': return 'bg-blue-100 text-blue-700 border-blue-200'
|
||||
case 'B1': return 'bg-yellow-100 text-yellow-700 border-yellow-200'
|
||||
case 'B2': return 'bg-orange-100 text-orange-700 border-orange-200'
|
||||
case 'C1': return 'bg-red-100 text-red-700 border-red-200'
|
||||
case 'C2': return 'bg-purple-100 text-purple-700 border-purple-200'
|
||||
default: return 'bg-gray-100 text-gray-700 border-gray-200'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-xl shadow-lg w-96 max-w-md overflow-hidden">
|
||||
{/* 標題區 - 漸層背景 */}
|
||||
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 p-5 border-b border-blue-200">
|
||||
{/* 關閉按鈕 - 獨立一行 */}
|
||||
<div className="flex justify-end mb-3">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 w-6 h-6 rounded-full bg-white bg-opacity-80 hover:bg-opacity-100 transition-all flex items-center justify-center"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 詞彙標題 */}
|
||||
<div className="mb-3">
|
||||
<h3 className="text-2xl font-bold text-gray-900">{word.word}</h3>
|
||||
</div>
|
||||
|
||||
{/* 詞性、發音、播放按鈕、CEFR */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-sm bg-gray-100 text-gray-700 px-3 py-1 rounded-full">
|
||||
{word.partOfSpeech}
|
||||
</span>
|
||||
<span className="text-base text-gray-600">{word.pronunciation}</span>
|
||||
<button className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center text-white hover:bg-blue-700 transition-colors">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* CEFR標籤 - 在播放按鈕那一行的最右邊 */}
|
||||
<span className={`px-3 py-1 rounded-full text-sm font-medium border ${getCEFRColor(word.difficultyLevel)}`}>
|
||||
{word.difficultyLevel}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 內容區 - 彩色區塊設計 */}
|
||||
<div className="p-4 space-y-4">
|
||||
{/* 翻譯區塊 - 綠色 */}
|
||||
<div className="bg-green-50 rounded-lg p-3 border border-green-200">
|
||||
<h4 className="font-semibold text-green-900 mb-2 text-left text-sm">中文翻譯</h4>
|
||||
<p className="text-green-800 font-medium text-left">{word.translation}</p>
|
||||
</div>
|
||||
|
||||
{/* 定義區塊 - 灰色 */}
|
||||
<div className="bg-gray-50 rounded-lg p-3 border border-gray-200">
|
||||
<h4 className="font-semibold text-gray-900 mb-2 text-left text-sm">英文定義</h4>
|
||||
<p className="text-gray-700 text-left text-sm leading-relaxed">{word.definition}</p>
|
||||
</div>
|
||||
|
||||
{/* 同義詞區塊 - 紫色 */}
|
||||
<div className="bg-purple-50 rounded-lg p-3 border border-purple-200">
|
||||
<h4 className="font-semibold text-purple-900 mb-2 text-left text-sm">同義詞</h4>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{word.synonyms.slice(0, 4).map((synonym: string, idx: number) => (
|
||||
<span key={idx} className="bg-white text-purple-700 px-2 py-1 rounded-full text-xs border border-purple-200 font-medium">
|
||||
{synonym}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 保存按鈕 - 底部平均延展 */}
|
||||
<div className="p-4 pt-2">
|
||||
<button
|
||||
onClick={onSave}
|
||||
className="w-full bg-primary text-white py-3 rounded-lg font-medium hover:bg-primary-hover transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
||||
</svg>
|
||||
<span className="font-medium">保存到詞卡</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 輔助函數
|
||||
function getDesignFeatures(design: string): string[] {
|
||||
const features = {
|
||||
modern: [
|
||||
'毛玻璃背景效果',
|
||||
'大尺寸陰影和圓角',
|
||||
'漸層按鈕設計',
|
||||
'微互動動畫',
|
||||
'現代配色方案'
|
||||
],
|
||||
classic: [
|
||||
'藍色漸層標題欄',
|
||||
'清晰的區塊分隔',
|
||||
'傳統卡片佈局',
|
||||
'顏色編碼標籤',
|
||||
'穩重的設計風格'
|
||||
],
|
||||
minimal: [
|
||||
'極簡配色方案',
|
||||
'去除多餘裝飾',
|
||||
'重點信息突出',
|
||||
'輕量化設計',
|
||||
'快速瀏覽體驗'
|
||||
],
|
||||
magazine: [
|
||||
'雜誌式字體',
|
||||
'專業排版設計',
|
||||
'大標題小說明',
|
||||
'黑白主色調',
|
||||
'閱讀導向佈局'
|
||||
],
|
||||
mobile: [
|
||||
'iOS/Android風格',
|
||||
'圓角和圖標設計',
|
||||
'列表式信息展示',
|
||||
'觸控友好設計',
|
||||
'移動端優化'
|
||||
],
|
||||
learning: [
|
||||
'學習功能一致性',
|
||||
'彩色區塊設計',
|
||||
'教育導向佈局',
|
||||
'清晰的信息分類',
|
||||
'學習體驗優化'
|
||||
],
|
||||
flashcard: [
|
||||
'詞卡詳細頁面風格',
|
||||
'右上角CEFR標籤',
|
||||
'漸層標題背景',
|
||||
'彩色內容區塊',
|
||||
'響應式設計優化'
|
||||
]
|
||||
}
|
||||
return features[design as keyof typeof features] || []
|
||||
}
|
||||
|
||||
function getDesignScenario(design: string): string {
|
||||
const scenarios = {
|
||||
modern: '適合追求現代感和科技感的用戶,特別是年輕用戶群體。設計前衛,視覺效果佳。',
|
||||
classic: '適合喜歡傳統界面的用戶,信息展示清晰,功能分區明確,適合學術或商務場景。',
|
||||
minimal: '適合追求效率的用戶,減少視覺干擾,快速獲取核心信息,適合頻繁使用的場景。',
|
||||
magazine: '適合喜歡閱讀體驗的用戶,類似字典或雜誌的專業排版,適合深度學習。',
|
||||
mobile: '適合手機用戶,觸控友好,符合移動端應用的使用習慣,適合隨時隨地學習。',
|
||||
learning: '與現有學習功能保持一致,用戶體驗連貫,適合在學習流程中使用。',
|
||||
flashcard: '完全參考詞卡詳細頁面設計,提供最一致的用戶體驗,適合需要詳細信息展示的場景。'
|
||||
}
|
||||
return scenarios[design as keyof typeof scenarios] || ''
|
||||
}
|
||||
509
個人化詞彙庫功能規格.md
509
個人化詞彙庫功能規格.md
|
|
@ -1,509 +0,0 @@
|
|||
# 個人化詞彙庫功能規格
|
||||
|
||||
## 🎯 功能概述
|
||||
|
||||
個人化詞彙庫是一個用戶專屬的詞彙管理系統,允許用戶收集、組織和追蹤自己的學習詞彙,並根據學習表現提供個人化的學習建議。
|
||||
|
||||
## 📋 核心功能需求
|
||||
|
||||
### 1. 詞彙收集功能
|
||||
|
||||
#### 1.1 手動添加詞彙
|
||||
- **功能描述**:用戶可以手動輸入新詞彙到個人詞彙庫
|
||||
- **輸入欄位**:
|
||||
- 英文詞彙(必填)
|
||||
- 詞性(可選,下拉選單)
|
||||
- 發音(可選,自動生成或手動輸入)
|
||||
- 定義(可選,自動生成或手動輸入)
|
||||
- 中文翻譯(可選,自動生成或手動輸入)
|
||||
- 個人筆記(可選,用戶自訂)
|
||||
- **自動補全**:系統自動查詢並填入詞彙資訊
|
||||
- **重複檢查**:避免添加重複詞彙
|
||||
|
||||
#### 1.2 學習中收集
|
||||
- **學習頁面收藏**:在任何測驗模式中點擊「收藏」按鈕
|
||||
- **困難詞彙標記**:答錯的詞彙自動標記為需要加強
|
||||
- **快速收集**:一鍵添加當前學習的詞彙到個人庫
|
||||
|
||||
#### 1.3 批量導入
|
||||
- **文字檔導入**:支援 .txt 格式的詞彙列表
|
||||
- **CSV 導入**:支援結構化的詞彙資料
|
||||
- **從學習記錄導入**:將過往答錯的詞彙批量加入
|
||||
|
||||
### 2. 詞彙組織功能
|
||||
|
||||
#### 2.1 分類管理
|
||||
- **預設分類**:
|
||||
- 新學詞彙(New)
|
||||
- 學習中(Learning)
|
||||
- 熟悉(Familiar)
|
||||
- 精通(Mastered)
|
||||
- 困難詞彙(Difficult)
|
||||
- **自訂分類**:用戶可創建自己的分類標籤
|
||||
- **多重分類**:單一詞彙可屬於多個分類
|
||||
|
||||
#### 2.2 標籤系統
|
||||
- **難度標籤**:A1, A2, B1, B2, C1, C2
|
||||
- **主題標籤**:商業、旅遊、學術、日常等
|
||||
- **來源標籤**:書籍、電影、新聞、會話等
|
||||
- **自訂標籤**:用戶可創建個人標籤
|
||||
|
||||
#### 2.3 優先級管理
|
||||
- **高優先級**:急需掌握的詞彙
|
||||
- **中優先級**:重要但不緊急的詞彙
|
||||
- **低優先級**:選擇性學習的詞彙
|
||||
|
||||
### 3. 學習追蹤功能
|
||||
|
||||
#### 3.1 熟悉度評分
|
||||
- **評分機制**:0-100 分的熟悉度評分
|
||||
- **多維度評估**:
|
||||
- 認識度(Recognition):看到詞彙能理解
|
||||
- 回想度(Recall):能主動想起詞彙
|
||||
- 應用度(Application):能在語境中正確使用
|
||||
- **動態調整**:根據測驗表現自動調整評分
|
||||
|
||||
#### 3.2 學習歷史
|
||||
- **學習次數**:詞彙被學習的總次數
|
||||
- **正確率**:各種測驗模式的正確率統計
|
||||
- **最後學習時間**:記錄最近一次學習時間
|
||||
- **學習軌跡**:詳細的學習歷程記錄
|
||||
|
||||
#### 3.3 遺忘曲線追蹤
|
||||
- **複習提醒**:基於遺忘曲線的智能提醒
|
||||
- **複習間隔**:動態調整複習時間間隔
|
||||
- **記憶強度**:評估詞彙在記憶中的鞏固程度
|
||||
|
||||
### 4. 個人化學習功能
|
||||
|
||||
#### 4.1 智能推薦
|
||||
- **弱點分析**:識別用戶的學習弱點
|
||||
- **相似詞彙**:推薦語義相關的詞彙
|
||||
- **同根詞擴展**:推薦同詞根的相關詞彙
|
||||
- **搭配詞推薦**:推薦常見的詞彙搭配
|
||||
|
||||
#### 4.2 個人化測驗
|
||||
- **客製化題組**:根據個人詞彙庫生成測驗
|
||||
- **弱點加強**:針對困難詞彙的專門訓練
|
||||
- **複習模式**:基於遺忘曲線的複習測驗
|
||||
- **混合練習**:結合不同來源詞彙的綜合測驗
|
||||
|
||||
#### 4.3 學習計劃
|
||||
- **每日目標**:設定每日學習詞彙數量
|
||||
- **週期計劃**:制定短期和長期學習目標
|
||||
- **進度追蹤**:視覺化顯示學習進度
|
||||
- **成就系統**:學習里程碑和獎勵機制
|
||||
|
||||
## 🗃️ 資料結構設計
|
||||
|
||||
### 個人詞彙資料模型
|
||||
```typescript
|
||||
interface PersonalVocabulary {
|
||||
id: string;
|
||||
userId: string;
|
||||
word: string;
|
||||
partOfSpeech?: string;
|
||||
pronunciation?: string;
|
||||
definition?: string;
|
||||
translation?: string;
|
||||
personalNotes?: string;
|
||||
|
||||
// 分類和標籤
|
||||
categories: string[];
|
||||
tags: string[];
|
||||
priority: 'high' | 'medium' | 'low';
|
||||
|
||||
// 學習追蹤
|
||||
familiarityScore: number; // 0-100
|
||||
recognitionScore: number; // 0-100
|
||||
recallScore: number; // 0-100
|
||||
applicationScore: number; // 0-100
|
||||
|
||||
// 學習統計
|
||||
totalPractices: number;
|
||||
correctAnswers: number;
|
||||
incorrectAnswers: number;
|
||||
lastPracticed: Date;
|
||||
nextReview: Date;
|
||||
|
||||
// 測驗模式統計
|
||||
flipMemoryStats: TestModeStats;
|
||||
vocabChoiceStats: TestModeStats;
|
||||
sentenceFillStats: TestModeStats;
|
||||
// ... 其他測驗模式
|
||||
|
||||
// 元資料
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
source?: string; // 詞彙來源
|
||||
}
|
||||
|
||||
interface TestModeStats {
|
||||
attempts: number;
|
||||
correct: number;
|
||||
averageTime: number; // 平均回答時間(秒)
|
||||
lastAttempt: Date;
|
||||
}
|
||||
```
|
||||
|
||||
### 學習會話記錄
|
||||
```typescript
|
||||
interface LearningSession {
|
||||
id: string;
|
||||
userId: string;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
mode: string;
|
||||
vocabulariesPracticed: string[]; // 詞彙 IDs
|
||||
totalQuestions: number;
|
||||
correctAnswers: number;
|
||||
timeSpent: number; // 秒
|
||||
performance: SessionPerformance;
|
||||
}
|
||||
|
||||
interface SessionPerformance {
|
||||
accuracy: number; // 正確率
|
||||
speed: number; // 平均回答速度
|
||||
improvement: number; // 相對上次的進步
|
||||
weakWords: string[]; // 表現較差的詞彙
|
||||
strongWords: string[]; // 表現較好的詞彙
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 技術實現方案
|
||||
|
||||
### 前端實現
|
||||
|
||||
#### 1. 狀態管理
|
||||
```typescript
|
||||
// 使用 Context API 或 Zustand
|
||||
interface PersonalVocabStore {
|
||||
vocabularies: PersonalVocabulary[];
|
||||
currentSession: LearningSession | null;
|
||||
filters: VocabFilters;
|
||||
|
||||
// Actions
|
||||
addVocabulary: (vocab: Partial<PersonalVocabulary>) => void;
|
||||
updateVocabulary: (id: string, updates: Partial<PersonalVocabulary>) => void;
|
||||
deleteVocabulary: (id: string) => void;
|
||||
updateFamiliarity: (id: string, testResult: TestResult) => void;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 本地儲存策略
|
||||
- **IndexedDB**:大量詞彙資料的本地儲存
|
||||
- **localStorage**:用戶偏好和設定
|
||||
- **同步機制**:與伺服器的雙向同步
|
||||
|
||||
#### 3. UI 組件結構
|
||||
```
|
||||
/components/PersonalVocab/
|
||||
├── VocabLibrary.tsx # 詞彙庫主頁面
|
||||
├── VocabCard.tsx # 單一詞彙卡片
|
||||
├── VocabForm.tsx # 新增/編輯詞彙表單
|
||||
├── VocabFilters.tsx # 篩選和搜尋
|
||||
├── VocabStats.tsx # 學習統計
|
||||
├── CategoryManager.tsx # 分類管理
|
||||
├── TagManager.tsx # 標籤管理
|
||||
└── ReviewScheduler.tsx # 複習排程
|
||||
```
|
||||
|
||||
### 後端實現
|
||||
|
||||
#### 1. API 端點設計
|
||||
```
|
||||
GET /api/personal-vocab # 獲取用戶詞彙庫
|
||||
POST /api/personal-vocab # 新增詞彙
|
||||
PUT /api/personal-vocab/:id # 更新詞彙
|
||||
DELETE /api/personal-vocab/:id # 刪除詞彙
|
||||
POST /api/personal-vocab/batch # 批量操作
|
||||
|
||||
GET /api/personal-vocab/stats # 獲取學習統計
|
||||
POST /api/personal-vocab/practice # 記錄練習結果
|
||||
GET /api/personal-vocab/review # 獲取需要複習的詞彙
|
||||
|
||||
GET /api/learning-sessions # 獲取學習會話記錄
|
||||
POST /api/learning-sessions # 記錄學習會話
|
||||
```
|
||||
|
||||
#### 2. 資料庫設計
|
||||
```sql
|
||||
-- 個人詞彙表
|
||||
CREATE TABLE personal_vocabularies (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES users(id),
|
||||
word VARCHAR(100) NOT NULL,
|
||||
part_of_speech VARCHAR(20),
|
||||
pronunciation VARCHAR(200),
|
||||
definition TEXT,
|
||||
translation TEXT,
|
||||
personal_notes TEXT,
|
||||
familiarity_score INTEGER DEFAULT 0,
|
||||
recognition_score INTEGER DEFAULT 0,
|
||||
recall_score INTEGER DEFAULT 0,
|
||||
application_score INTEGER DEFAULT 0,
|
||||
total_practices INTEGER DEFAULT 0,
|
||||
correct_answers INTEGER DEFAULT 0,
|
||||
incorrect_answers INTEGER DEFAULT 0,
|
||||
last_practiced TIMESTAMP,
|
||||
next_review TIMESTAMP,
|
||||
priority VARCHAR(10) DEFAULT 'medium',
|
||||
source VARCHAR(100),
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 詞彙分類表
|
||||
CREATE TABLE vocab_categories (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES users(id),
|
||||
name VARCHAR(50) NOT NULL,
|
||||
color VARCHAR(7), -- HEX color
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 詞彙-分類關聯表
|
||||
CREATE TABLE vocab_category_relations (
|
||||
vocab_id UUID REFERENCES personal_vocabularies(id),
|
||||
category_id UUID REFERENCES vocab_categories(id),
|
||||
PRIMARY KEY (vocab_id, category_id)
|
||||
);
|
||||
|
||||
-- 學習會話表
|
||||
CREATE TABLE learning_sessions (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES users(id),
|
||||
mode VARCHAR(50) NOT NULL,
|
||||
start_time TIMESTAMP NOT NULL,
|
||||
end_time TIMESTAMP,
|
||||
total_questions INTEGER DEFAULT 0,
|
||||
correct_answers INTEGER DEFAULT 0,
|
||||
time_spent INTEGER DEFAULT 0, -- 秒
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 詞彙練習記錄表
|
||||
CREATE TABLE vocab_practice_records (
|
||||
id UUID PRIMARY KEY,
|
||||
session_id UUID REFERENCES learning_sessions(id),
|
||||
vocab_id UUID REFERENCES personal_vocabularies(id),
|
||||
test_mode VARCHAR(50) NOT NULL,
|
||||
is_correct BOOLEAN NOT NULL,
|
||||
response_time INTEGER, -- 毫秒
|
||||
user_answer TEXT,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
## 🎨 使用者介面設計
|
||||
|
||||
### 主要頁面結構
|
||||
|
||||
#### 1. 詞彙庫總覽頁面 (`/personal-vocab`)
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 🏠 個人詞彙庫 (1,247 個詞彙) │
|
||||
├─────────────────────────────────────┤
|
||||
│ [搜尋框] [篩選] [排序] [新增詞彙] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📊 學習統計 │
|
||||
│ • 今日學習:12 個詞彙 │
|
||||
│ • 本週進度:85% 完成 │
|
||||
│ • 平均正確率:78% │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📚 詞彙分類 │
|
||||
│ [新學詞彙 124] [學習中 89] [熟悉 856] │
|
||||
│ [困難詞彙 45] [我的收藏 67] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📝 詞彙列表 │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ brought (動詞) ⭐⭐⭐⭐☆ │ │
|
||||
│ │ 發音: /brɔːt/ | 熟悉度: 80% │ │
|
||||
│ │ 定義: Past tense of bring... │ │
|
||||
│ │ [編輯] [練習] [刪除] │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
│ (更多詞彙卡片...) │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 2. 詞彙詳情頁面 (`/personal-vocab/:id`)
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ← 返回詞彙庫 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📝 brought │
|
||||
│ 動詞 | 難度: B1 | 優先級: 高 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🔊 發音: /brɔːt/ [播放] │
|
||||
│ 📖 定義: Past tense of bring... │
|
||||
│ 🈲 翻譯: 提出、帶來 │
|
||||
│ 📝 個人筆記: [編輯區域] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📊 學習統計 │
|
||||
│ • 熟悉度: ████████░░ 80% │
|
||||
│ • 總練習: 25 次 │
|
||||
│ • 正確率: 85% │
|
||||
│ • 上次練習: 2 小時前 │
|
||||
│ • 下次複習: 明天 14:00 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🎯 各模式表現 │
|
||||
│ • 翻卡記憶: 90% (15/15) │
|
||||
│ • 詞彙選擇: 75% (12/16) │
|
||||
│ • 例句填空: 80% (8/10) │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🏷️ 分類標籤 │
|
||||
│ [學習中] [商業英語] [重要詞彙] │
|
||||
│ [+ 添加標籤] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🎮 快速練習 │
|
||||
│ [翻卡記憶] [詞彙選擇] [例句填空] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 3. 新增詞彙頁面 (`/personal-vocab/add`)
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ➕ 新增詞彙到個人庫 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📝 英文詞彙: [輸入框] *必填 │
|
||||
│ 🔍 [智能查詢] - 自動填入詞彙資訊 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📖 詞彙資訊 │
|
||||
│ • 詞性: [下拉選單] │
|
||||
│ • 發音: [輸入框] [生成] │
|
||||
│ • 定義: [文字區域] [自動生成] │
|
||||
│ • 翻譯: [輸入框] [自動翻譯] │
|
||||
├─────────────────────────────────────┤
|
||||
│ 🏷️ 分類設定 │
|
||||
│ • 分類: [多選下拉] [新增分類] │
|
||||
│ • 標籤: [標籤選擇器] [新增標籤] │
|
||||
│ • 優先級: ⚫ 高 ⚪ 中 ⚪ 低 │
|
||||
├─────────────────────────────────────┤
|
||||
│ 📝 個人筆記 │
|
||||
│ [多行文字輸入區域] │
|
||||
├─────────────────────────────────────┤
|
||||
│ [取消] [儲存詞彙] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🔄 學習流程整合
|
||||
|
||||
### 1. 學習中的詞彙收集
|
||||
- **收藏按鈕**:每個測驗頁面都有收藏功能
|
||||
- **自動收集**:答錯的詞彙自動標記為需要加強
|
||||
- **學習後提醒**:學習會話結束後推薦收藏的詞彙
|
||||
|
||||
### 2. 個人化測驗生成
|
||||
- **我的詞彙測驗**:從個人庫選取詞彙生成測驗
|
||||
- **弱點強化**:針對低熟悉度詞彙的專門練習
|
||||
- **混合模式**:結合系統詞彙和個人詞彙的測驗
|
||||
|
||||
### 3. 複習提醒系統
|
||||
- **智能排程**:基於遺忘曲線安排複習時間
|
||||
- **推送通知**:瀏覽器通知提醒複習時間
|
||||
- **複習優化**:根據表現調整複習頻率
|
||||
|
||||
## 📱 響應式設計考量
|
||||
|
||||
### 桌面版 (>= 1024px)
|
||||
- **三欄布局**:側邊欄(分類)+ 詞彙列表 + 詳情面板
|
||||
- **拖拉操作**:支援拖拉詞彙到不同分類
|
||||
- **快速鍵**:鍵盤快速鍵支援
|
||||
|
||||
### 平板版 (768px - 1023px)
|
||||
- **兩欄布局**:詞彙列表 + 詳情面板
|
||||
- **觸控優化**:適合觸控操作的按鈕尺寸
|
||||
|
||||
### 手機版 (< 768px)
|
||||
- **單欄布局**:全螢幕顯示當前頁面
|
||||
- **底部導航**:快速切換功能
|
||||
- **手勢支援**:滑動操作和長按功能
|
||||
|
||||
## 🚀 實施階段規劃
|
||||
|
||||
### 階段 1:基礎詞彙管理 (第 1-2 週)
|
||||
- [ ] 資料庫設計和建立
|
||||
- [ ] 基本 CRUD API 開發
|
||||
- [ ] 詞彙列表頁面
|
||||
- [ ] 新增/編輯詞彙功能
|
||||
- [ ] 基本搜尋和篩選
|
||||
|
||||
### 階段 2:學習追蹤系統 (第 3-4 週)
|
||||
- [ ] 熟悉度評分系統
|
||||
- [ ] 學習歷史記錄
|
||||
- [ ] 測驗結果整合
|
||||
- [ ] 學習統計儀表板
|
||||
|
||||
### 階段 3:智能化功能 (第 5-6 週)
|
||||
- [ ] 遺忘曲線算法
|
||||
- [ ] 複習提醒系統
|
||||
- [ ] 個人化推薦
|
||||
- [ ] 弱點分析
|
||||
|
||||
### 階段 4:高級功能 (第 7-8 週)
|
||||
- [ ] 批量導入/導出
|
||||
- [ ] 學習計劃制定
|
||||
- [ ] 成就系統
|
||||
- [ ] 社交分享功能
|
||||
|
||||
## 📊 成功指標
|
||||
|
||||
### 用戶行為指標
|
||||
- **詞彙庫使用率**:> 80% 用戶建立個人詞彙庫
|
||||
- **收藏率**:> 60% 學習中的詞彙被收藏
|
||||
- **複習完成率**:> 70% 的複習提醒被完成
|
||||
- **熟悉度提升**:平均熟悉度每週提升 5%
|
||||
|
||||
### 學習效果指標
|
||||
- **記憶保持率**:複習詞彙的正確率 > 85%
|
||||
- **學習效率**:個人詞彙的學習時間縮短 30%
|
||||
- **長期記憶**:30 天後的詞彙記憶率 > 70%
|
||||
|
||||
### 系統性能指標
|
||||
- **回應時間**:詞彙庫載入時間 < 2 秒
|
||||
- **同步效率**:資料同步成功率 > 99%
|
||||
- **儲存效率**:本地儲存空間使用 < 50MB
|
||||
|
||||
## 🔐 隱私和安全考量
|
||||
|
||||
### 資料隱私
|
||||
- **用戶授權**:明確的隱私政策和使用條款
|
||||
- **資料加密**:敏感資料的端到端加密
|
||||
- **匿名化**:學習統計資料的匿名化處理
|
||||
|
||||
### 資料安全
|
||||
- **備份機制**:定期備份用戶資料
|
||||
- **版本控制**:資料變更的版本記錄
|
||||
- **災難恢復**:資料遺失的恢復機制
|
||||
|
||||
## 🔮 未來擴展功能
|
||||
|
||||
### 社交學習功能
|
||||
- **詞彙分享**:分享個人詞彙庫給其他用戶
|
||||
- **學習小組**:創建詞彙學習小組
|
||||
- **競賽模式**:與朋友的詞彙學習競賽
|
||||
|
||||
### AI 智能功能
|
||||
- **智能生成**:AI 生成個人化例句
|
||||
- **發音評估**:AI 評估發音準確度
|
||||
- **學習建議**:AI 提供個人化學習建議
|
||||
|
||||
### 多媒體功能
|
||||
- **語音筆記**:錄音形式的個人筆記
|
||||
- **圖片聯想**:為詞彙添加個人化圖片
|
||||
- **影片連結**:連結相關的學習影片
|
||||
|
||||
---
|
||||
|
||||
## 📝 附註
|
||||
|
||||
本規格文件為個人化詞彙庫功能的完整設計,包含前後端實現細節和用戶體驗考量。實際開發時可根據優先級和資源情況分階段實施。
|
||||
|
||||
**建議優先實施階段 1 和階段 2**,建立穩固的基礎功能,再逐步添加智能化和高級功能。
|
||||
|
||||
---
|
||||
|
||||
*最後更新:2025-09-20*
|
||||
*版本:v1.0*
|
||||
2273
詞卡保存功能技術規格.md
2273
詞卡保存功能技術規格.md
File diff suppressed because it is too large
Load Diff
616
詞卡管理系統簡化規格.md
616
詞卡管理系統簡化規格.md
|
|
@ -1,616 +0,0 @@
|
|||
# 詞卡管理系統簡化規格
|
||||
|
||||
## 設計理念
|
||||
|
||||
DramaLing詞卡管理採用**極簡設計理念**,專注於詞彙本身的學習價值,去除複雜的分類系統,讓用戶能夠專注於詞彙學習而非管理工作。
|
||||
|
||||
---
|
||||
|
||||
## 1. 簡化設計原則
|
||||
|
||||
### 1.1 去除卡組分類
|
||||
- **原因**: 避免用戶花費過多時間在分類管理上
|
||||
- **好處**: 降低認知負荷,專注學習本質
|
||||
- **替代**: 使用搜尋和收藏功能進行詞卡組織
|
||||
|
||||
### 1.2 核心功能保留
|
||||
- ✅ **詞卡展示**: 清晰的詞卡列表展示
|
||||
- ✅ **搜尋功能**: 強大的搜尋和篩選功能
|
||||
- ✅ **收藏系統**: 重要詞卡的標記和管理
|
||||
- ✅ **學習統計**: 詞卡的學習進度和統計
|
||||
|
||||
---
|
||||
|
||||
## 2. 簡化後的系統架構
|
||||
|
||||
### 2.1 詞卡管理界面
|
||||
|
||||
#### 主要頁面結構
|
||||
```
|
||||
詞卡管理
|
||||
├── 所有詞卡 (主要tab)
|
||||
│ ├── 進階搜尋功能
|
||||
│ ├── 多維度篩選
|
||||
│ └── 詞卡列表展示
|
||||
└── 收藏詞卡 (特殊tab)
|
||||
├── 僅顯示收藏詞卡
|
||||
└── 相同的操作功能
|
||||
```
|
||||
|
||||
#### 簡化的Tab設計
|
||||
```typescript
|
||||
// 移除的Tab
|
||||
❌ 我的卡組
|
||||
❌ 未分類詞卡
|
||||
|
||||
// 保留的Tab
|
||||
✅ 所有詞卡
|
||||
✅ 收藏詞卡
|
||||
```
|
||||
|
||||
### 2.2 詞卡展示優化
|
||||
|
||||
#### 展示信息調整
|
||||
```
|
||||
原來: 卡組: {cardSet.name}
|
||||
修改: 創建: {createdAt}
|
||||
|
||||
原來: 卡組信息顯示
|
||||
修改: 學習統計信息
|
||||
```
|
||||
|
||||
#### 統計信息重構
|
||||
```typescript
|
||||
// 詞卡列表顯示
|
||||
┌─────────────────────────────┐ [A1]
|
||||
│ │ hello noun
|
||||
│ 例句圖片 │ 你好
|
||||
│ │ /həˈloʊ/ ▶️
|
||||
└─────────────────────────────┘ 創建: 2025-09-17 | 掌握度: 95% | 複習: 15 次
|
||||
[⭐收藏] [編輯] [刪除] [詳細] →
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 功能重新設計
|
||||
|
||||
### 3.1 搜尋與篩選增強
|
||||
|
||||
#### 替代卡組的組織方式
|
||||
```typescript
|
||||
// 透過搜尋找到相關詞卡
|
||||
1. 關鍵字搜尋: "商務" → 找到所有商務相關詞卡
|
||||
2. CEFR篩選: "C1" → 找到所有高級詞卡
|
||||
3. 詞性篩選: "verb" → 找到所有動詞
|
||||
4. 掌握度篩選: "需加強" → 找到需要練習的詞卡
|
||||
|
||||
// 快速篩選按鈕
|
||||
[需加強詞卡] [收藏詞卡] [高級詞彙] [清除全部]
|
||||
```
|
||||
|
||||
#### 進階搜尋功能
|
||||
- **多欄位搜尋**: 詞彙、翻譯、定義同時搜尋
|
||||
- **智能篩選**: CEFR等級、詞性、掌握程度組合篩選
|
||||
- **收藏篩選**: 快速找到重要詞卡
|
||||
- **搜尋高亮**: 關鍵字黃色標記
|
||||
|
||||
### 3.2 收藏系統強化
|
||||
|
||||
#### 收藏作為主要組織方式
|
||||
```typescript
|
||||
// 收藏的多重意義
|
||||
⭐ 重要詞彙: 學習價值高的詞卡
|
||||
⭐ 困難詞彙: 需要重點練習的詞卡
|
||||
⭐ 常用詞彙: 經常使用的實用詞卡
|
||||
⭐ 個人標記: 用戶自定義的重要性標記
|
||||
```
|
||||
|
||||
#### 收藏功能設計
|
||||
- **一鍵收藏**: 詞卡列表和詳細頁面都可收藏
|
||||
- **收藏統計**: 顯示收藏詞卡的數量
|
||||
- **收藏過濾**: 專門的收藏詞卡tab頁面
|
||||
- **狀態清晰**: 收藏按鈕的視覺狀態明確
|
||||
|
||||
---
|
||||
|
||||
## 4. 用戶體驗優化
|
||||
|
||||
### 4.1 簡化的學習流程
|
||||
|
||||
#### 核心學習路徑
|
||||
```
|
||||
1. 生成/保存詞卡 → 2. 搜尋/瀏覽詞卡 → 3. 收藏重要詞卡 → 4. 開始學習
|
||||
```
|
||||
|
||||
#### 去除的複雜流程
|
||||
```
|
||||
❌ 創建卡組 → 分配詞卡 → 管理卡組 → 卡組學習
|
||||
✅ 直接學習 → 搜尋篩選 → 收藏管理 → 重點練習
|
||||
```
|
||||
|
||||
### 4.2 認知負荷降低
|
||||
|
||||
#### 決策簡化
|
||||
- **原來**: 這個詞卡應該放在哪個卡組?
|
||||
- **現在**: 這個詞卡重要嗎?需要收藏嗎?
|
||||
|
||||
#### 操作簡化
|
||||
- **原來**: 創建卡組 → 命名 → 選顏色 → 分配詞卡
|
||||
- **現在**: 點擊收藏 → 完成標記
|
||||
|
||||
### 4.3 專注學習本質
|
||||
|
||||
#### 學習導向設計
|
||||
- **詞彙為主**: 界面以詞彙內容為核心
|
||||
- **學習統計**: 突出學習進度和掌握程度
|
||||
- **收藏引導**: 鼓勵用戶標記重要詞彙
|
||||
- **搜尋優先**: 通過搜尋快速找到相關詞卡
|
||||
|
||||
---
|
||||
|
||||
## 5. 技術實現簡化
|
||||
|
||||
### 5.1 前端架構簡化
|
||||
|
||||
#### 移除的組件和邏輯
|
||||
```typescript
|
||||
❌ CardSetSelector // 卡組選擇器
|
||||
❌ CardSetGrid // 卡組網格展示
|
||||
❌ CreateCardSetForm // 創建卡組表單
|
||||
❌ CardSetManagement // 卡組管理邏輯
|
||||
|
||||
✅ FlashcardList // 詞卡列表(保留)
|
||||
✅ FlashcardDetail // 詞卡詳情(保留)
|
||||
✅ SearchAndFilter // 搜尋篩選(增強)
|
||||
✅ FavoriteManagement // 收藏管理(保留)
|
||||
```
|
||||
|
||||
#### 狀態管理簡化
|
||||
```typescript
|
||||
// 移除的狀態
|
||||
❌ selectedCardSet
|
||||
❌ cardSets
|
||||
❌ showCreateCardSetForm
|
||||
|
||||
// 保留的狀態
|
||||
✅ flashcards
|
||||
✅ searchTerm
|
||||
✅ searchFilters
|
||||
✅ favorites
|
||||
```
|
||||
|
||||
### 5.2 API使用簡化
|
||||
|
||||
#### 需要的API端點
|
||||
```typescript
|
||||
✅ GET /api/flashcards // 獲取詞卡列表
|
||||
✅ POST /api/flashcards // 創建詞卡
|
||||
✅ PUT /api/flashcards/{id} // 更新詞卡
|
||||
✅ DELETE /api/flashcards/{id} // 刪除詞卡
|
||||
✅ POST /api/flashcards/{id}/favorite // 切換收藏
|
||||
|
||||
❌ GET /api/cardsets // 不再需要
|
||||
❌ POST /api/cardsets // 不再需要
|
||||
❌ PUT /api/cardsets/{id} // 不再需要
|
||||
❌ DELETE /api/cardsets/{id} // 不再需要
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 資料結構調整
|
||||
|
||||
### 6.1 詞卡實體簡化
|
||||
|
||||
#### Flashcard實體調整
|
||||
```csharp
|
||||
public class Flashcard
|
||||
{
|
||||
// 保留的核心欄位
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public string Word { get; set; } // 詞彙
|
||||
public string Translation { get; set; } // 翻譯
|
||||
public string Definition { get; set; } // 定義
|
||||
public string? PartOfSpeech { get; set; } // 詞性
|
||||
public string? Pronunciation { get; set; } // 發音
|
||||
public string? Example { get; set; } // 例句
|
||||
public string? ExampleTranslation { get; set; } // 例句翻譯
|
||||
|
||||
// SM-2學習算法欄位(保留)
|
||||
public float EasinessFactor { get; set; }
|
||||
public int Repetitions { get; set; }
|
||||
public int IntervalDays { get; set; }
|
||||
public DateTime NextReviewDate { get; set; }
|
||||
|
||||
// 學習統計(保留)
|
||||
public int MasteryLevel { get; set; }
|
||||
public int TimesReviewed { get; set; }
|
||||
public int TimesCorrect { get; set; }
|
||||
public DateTime? LastReviewedAt { get; set; }
|
||||
|
||||
// 用戶標記(保留並增強)
|
||||
public bool IsFavorite { get; set; }
|
||||
public bool IsArchived { get; set; }
|
||||
public string? DifficultyLevel { get; set; } // CEFR等級
|
||||
|
||||
// 時間戳(保留)
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
// 移除的欄位
|
||||
❌ public Guid CardSetId { get; set; } // 不再需要卡組關聯
|
||||
❌ public virtual CardSet CardSet { get; set; } // 不再需要導航屬性
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 資料庫調整建議
|
||||
|
||||
#### 可選的架構調整
|
||||
```sql
|
||||
-- 如果要完全移除卡組功能,可以執行:
|
||||
-- 1. 移除外鍵約束
|
||||
ALTER TABLE flashcards DROP CONSTRAINT FK_flashcards_card_sets;
|
||||
|
||||
-- 2. 移除卡組ID欄位(可選)
|
||||
ALTER TABLE flashcards DROP COLUMN card_set_id;
|
||||
|
||||
-- 3. 刪除卡組相關表格(可選)
|
||||
DROP TABLE card_sets;
|
||||
DROP TABLE flashcard_tags; -- 如果有標籤關聯
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 用戶界面重新設計
|
||||
|
||||
### 7.1 主頁面布局
|
||||
|
||||
#### 簡化的詞卡管理頁面
|
||||
```
|
||||
詞卡管理
|
||||
├── 頁面標題: "我的詞卡"
|
||||
├── 操作按鈕: [新增詞卡] [AI生成詞卡]
|
||||
├── Tab導航: [所有詞卡] [收藏詞卡]
|
||||
├── 搜尋區域: 進階搜尋和篩選功能
|
||||
└── 詞卡列表: 統一的詞卡展示
|
||||
```
|
||||
|
||||
#### 詞卡展示信息
|
||||
```
|
||||
┌─────────────────────────────┐ [CEFR]
|
||||
│ │ 詞彙 [詞性]
|
||||
│ 例句圖片 │ 翻譯
|
||||
│ │ /音標/ ▶️
|
||||
└─────────────────────────────┘ 創建時間 | 掌握度 | 複習次數
|
||||
[收藏] [編輯] [刪除] [詳細] →
|
||||
```
|
||||
|
||||
### 7.2 詞卡詳細頁面
|
||||
|
||||
#### 移除卡組相關信息
|
||||
```typescript
|
||||
// 詞卡資訊區塊
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-600">詞性:</span>
|
||||
<span className="ml-2 font-medium">{flashcard.partOfSpeech}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">創建時間:</span>
|
||||
<span className="ml-2 font-medium">{createdAt}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">下次複習:</span>
|
||||
<span className="ml-2 font-medium">{nextReviewDate}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">複習次數:</span>
|
||||
<span className="ml-2 font-medium">{timesReviewed} 次</span>
|
||||
</div>
|
||||
<!-- 移除:所屬卡組信息 -->
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 替代組織方案
|
||||
|
||||
### 8.1 基於搜尋的組織
|
||||
|
||||
#### 虛擬分類
|
||||
用戶可以通過搜尋實現類似卡組的效果:
|
||||
- **商務詞彙**: 搜尋"business, meeting, presentation"
|
||||
- **日常對話**: 搜尋"hello, thanks, please"
|
||||
- **高級詞彙**: 篩選"C1, C2"等級
|
||||
- **需要練習**: 篩選"掌握度 < 60%"
|
||||
|
||||
#### 搜尋記憶功能
|
||||
```typescript
|
||||
// 可以考慮添加搜尋歷史
|
||||
interface SearchHistory {
|
||||
query: string;
|
||||
filters: SearchFilters;
|
||||
timestamp: Date;
|
||||
resultCount: number;
|
||||
}
|
||||
|
||||
// 常用搜尋快捷方式
|
||||
const commonSearches = [
|
||||
{ name: "需要練習", filters: { masteryLevel: 'low' } },
|
||||
{ name: "收藏詞卡", filters: { onlyFavorites: true } },
|
||||
{ name: "高級詞彙", filters: { cefrLevel: 'C1' } },
|
||||
{ name: "動詞類", filters: { partOfSpeech: 'verb' } }
|
||||
];
|
||||
```
|
||||
|
||||
### 8.2 基於收藏的組織
|
||||
|
||||
#### 收藏的多重意義
|
||||
- **⭐ 重點學習**: 困難或重要的詞卡
|
||||
- **⭐ 常用詞彙**: 日常會用到的詞卡
|
||||
- **⭐ 考試重點**: 考試必備的詞卡
|
||||
- **⭐ 個人偏好**: 用戶特別喜歡的詞卡
|
||||
|
||||
#### 收藏增強功能(未來可選)
|
||||
```typescript
|
||||
// 可以考慮的收藏擴展
|
||||
interface FavoriteEnhancement {
|
||||
favoriteReason?: string; // 收藏原因
|
||||
favoriteTags?: string[]; // 收藏標籤
|
||||
favoriteNote?: string; // 個人筆記
|
||||
favoriteDate: Date; // 收藏時間
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 學習體驗優化
|
||||
|
||||
### 9.1 專注學習本質
|
||||
|
||||
#### 簡化的學習流程
|
||||
```
|
||||
1. 詞彙發現 (AI生成/句子分析)
|
||||
↓
|
||||
2. 詞卡保存 (一鍵保存)
|
||||
↓
|
||||
3. 詞卡瀏覽 (搜尋/篩選)
|
||||
↓
|
||||
4. 重點標記 (收藏重要詞卡)
|
||||
↓
|
||||
5. 開始學習 (進入學習模式)
|
||||
```
|
||||
|
||||
#### 避免的複雜流程
|
||||
```
|
||||
❌ 詞彙發現 → 選擇卡組 → 分配詞卡 → 管理卡組 → 卡組學習
|
||||
✅ 詞彙發現 → 保存詞卡 → 標記重要 → 直接學習
|
||||
```
|
||||
|
||||
### 9.2 學習動機維持
|
||||
|
||||
#### 成就感來源
|
||||
- **詞卡總數**: 累積學習的詞彙數量
|
||||
- **掌握程度**: 每個詞卡的學習進展
|
||||
- **收藏數量**: 重要詞卡的積累
|
||||
- **學習統計**: 複習次數、正確率等
|
||||
|
||||
#### 進度可視化
|
||||
```typescript
|
||||
// 整體學習統計
|
||||
interface OverallProgress {
|
||||
totalWords: number; // 總詞卡數
|
||||
masteredWords: number; // 已掌握詞卡數
|
||||
favoriteWords: number; // 收藏詞卡數
|
||||
averageMastery: number; // 平均掌握度
|
||||
dailyProgress: number; // 每日學習進度
|
||||
streak: number; // 連續學習天數
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 技術債務清理
|
||||
|
||||
### 10.1 後端清理(可選)
|
||||
|
||||
#### 可以移除的服務
|
||||
```csharp
|
||||
❌ CardSetsController // 卡組管理API
|
||||
❌ ICardSetService // 卡組業務邏輯
|
||||
❌ CardSetRepository // 卡組資料存取
|
||||
❌ CardSet Entity // 卡組實體(可選)
|
||||
```
|
||||
|
||||
#### 簡化的FlashcardsController
|
||||
```csharp
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class FlashcardsController : ControllerBase
|
||||
{
|
||||
// 簡化的詞卡CRUD
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> GetFlashcards(
|
||||
[FromQuery] string? search,
|
||||
[FromQuery] bool favoritesOnly = false,
|
||||
[FromQuery] int limit = 50,
|
||||
[FromQuery] int offset = 0)
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> CreateFlashcard([FromBody] CreateFlashcardRequest request)
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult> UpdateFlashcard(Guid id, [FromBody] UpdateFlashcardRequest request)
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ActionResult> DeleteFlashcard(Guid id)
|
||||
|
||||
[HttpPost("{id}/favorite")]
|
||||
public async Task<ActionResult> ToggleFavorite(Guid id)
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 前端清理
|
||||
|
||||
#### 移除的組件
|
||||
```typescript
|
||||
❌ CardSetSelector.tsx // 卡組選擇器
|
||||
❌ CardSetGrid.tsx // 卡組網格展示
|
||||
❌ CreateCardSetModal.tsx // 創建卡組彈窗
|
||||
❌ CardSetManagement.tsx // 卡組管理邏輯
|
||||
```
|
||||
|
||||
#### 簡化的狀態管理
|
||||
```typescript
|
||||
interface FlashcardPageState {
|
||||
// 保留
|
||||
flashcards: Flashcard[];
|
||||
searchTerm: string;
|
||||
searchFilters: SearchFilters;
|
||||
activeTab: 'all-cards' | 'favorites';
|
||||
|
||||
// 移除
|
||||
❌ cardSets: CardSet[];
|
||||
❌ selectedCardSet: string | null;
|
||||
❌ showCreateCardSetForm: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 用戶遷移策略
|
||||
|
||||
### 11.1 現有用戶處理
|
||||
|
||||
#### 卡組資料處理
|
||||
```sql
|
||||
-- 如果現有用戶已有卡組資料
|
||||
-- 可以選擇以下策略之一:
|
||||
|
||||
-- 策略1: 保留但隱藏卡組功能
|
||||
-- 在前端隱藏卡組相關界面,後端保留資料結構
|
||||
|
||||
-- 策略2: 將卡組信息轉為詞卡標籤
|
||||
UPDATE flashcards
|
||||
SET notes = CONCAT('來源: ', (SELECT name FROM card_sets WHERE id = flashcards.card_set_id))
|
||||
WHERE card_set_id IS NOT NULL;
|
||||
|
||||
-- 策略3: 完全移除卡組,保留詞卡
|
||||
-- 所有詞卡保留,移除卡組關聯
|
||||
```
|
||||
|
||||
### 11.2 功能遷移指南
|
||||
|
||||
#### 給用戶的建議
|
||||
```markdown
|
||||
# 卡組功能移除通知
|
||||
|
||||
為了簡化學習體驗,我們移除了卡組分類功能。
|
||||
您可以使用以下方式管理詞卡:
|
||||
|
||||
✅ **搜尋功能**: 輸入關鍵字快速找到相關詞卡
|
||||
✅ **進階篩選**: 使用CEFR等級、詞性等篩選條件
|
||||
✅ **收藏系統**: 標記重要詞卡,建立個人重點學習列表
|
||||
✅ **快速篩選**: 一鍵找到需要練習的詞卡
|
||||
|
||||
這些功能比卡組分類更靈活,讓您專注於詞彙學習本身。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 優勢分析
|
||||
|
||||
### 12.1 簡化帶來的好處
|
||||
|
||||
#### 用戶體驗提升
|
||||
- **學習專注**: 減少分類管理的時間投入
|
||||
- **操作簡化**: 更少的決策點,更直觀的操作
|
||||
- **認知減負**: 降低界面複雜度,專注學習內容
|
||||
|
||||
#### 開發維護優勢
|
||||
- **代碼簡潔**: 移除複雜的卡組管理邏輯
|
||||
- **測試簡化**: 減少測試場景和邊界條件
|
||||
- **性能提升**: 減少資料庫查詢和前端狀態管理
|
||||
|
||||
#### 功能聚焦
|
||||
- **核心功能**: 突出詞彙學習的核心價值
|
||||
- **搜尋優化**: 強化搜尋功能作為主要組織方式
|
||||
- **收藏增強**: 收藏功能成為主要的個人化標記
|
||||
|
||||
### 12.2 潛在挑戰與解決
|
||||
|
||||
#### 可能的用戶疑慮
|
||||
- **擔心**: "沒有分類會不會很亂?"
|
||||
- **解答**: 搜尋功能比分類更靈活,可以找到任何相關詞卡
|
||||
|
||||
- **擔心**: "如何管理大量詞卡?"
|
||||
- **解答**: 收藏+搜尋+篩選的組合比卡組更強大
|
||||
|
||||
- **擔心**: "如何進行主題學習?"
|
||||
- **解答**: 通過搜尋關鍵字實現主題學習,更靈活
|
||||
|
||||
---
|
||||
|
||||
## 13. 實施建議
|
||||
|
||||
### 13.1 分階段實施
|
||||
|
||||
#### 第一階段:界面簡化
|
||||
- ✅ 移除卡組相關Tab和界面元素
|
||||
- ✅ 簡化詞卡展示信息
|
||||
- ✅ 強化搜尋和收藏功能
|
||||
|
||||
#### 第二階段:邏輯清理
|
||||
- 🔄 移除前端卡組相關狀態和邏輯
|
||||
- 🔄 簡化API調用
|
||||
- 🔄 更新假資料結構
|
||||
|
||||
#### 第三階段:後端清理(可選)
|
||||
- ⚠️ 評估是否移除後端卡組功能
|
||||
- ⚠️ 資料庫架構調整
|
||||
- ⚠️ API端點清理
|
||||
|
||||
### 13.2 回滾方案
|
||||
|
||||
#### 如果需要恢復卡組功能
|
||||
- **前端**: Git回滾到卡組功能存在的版本
|
||||
- **後端**: 保持現有API不變
|
||||
- **資料**: 卡組資料結構保留,隨時可恢復
|
||||
|
||||
---
|
||||
|
||||
## 14. 總結
|
||||
|
||||
### 14.1 簡化價值
|
||||
詞卡管理系統的簡化體現了**"少即是多"**的設計哲學:
|
||||
|
||||
- **專注本質**: 將注意力集中在詞彙學習本身
|
||||
- **降低門檻**: 新用戶更容易上手使用
|
||||
- **提升效率**: 減少管理時間,增加學習時間
|
||||
- **靈活組織**: 搜尋比固定分類更靈活
|
||||
|
||||
### 14.2 功能對照
|
||||
|
||||
| 需求 | 卡組方案 | 簡化方案 |
|
||||
|------|----------|----------|
|
||||
| 詞彙分類 | 創建不同主題卡組 | 使用關鍵字搜尋 |
|
||||
| 重點標記 | 創建"重點"卡組 | 使用收藏功能 |
|
||||
| 進度追蹤 | 卡組級別進度 | 整體學習統計 |
|
||||
| 主題學習 | 選擇特定卡組 | 搜尋相關詞彙 |
|
||||
| 內容組織 | 卡組顏色分類 | CEFR+收藏+搜尋 |
|
||||
|
||||
### 14.3 設計哲學
|
||||
|
||||
**"完美不是無法再加,而是無法再減"** - 簡化的詞卡管理系統移除了非必要的複雜性,讓用戶能夠:
|
||||
|
||||
- 🎯 **專注學習**: 將時間用在學習詞彙而非管理分類
|
||||
- 🔍 **靈活查找**: 搜尋比預設分類更精確靈活
|
||||
- ⭐ **個人標記**: 收藏功能滿足個人化需求
|
||||
- 📊 **進度清晰**: 整體統計比分散統計更有意義
|
||||
|
||||
---
|
||||
|
||||
**文件版本**: 1.0
|
||||
**建立日期**: 2025-09-20
|
||||
**設計理念**: 極簡主義,專注學習本質
|
||||
**適用範圍**: DramaLing詞卡管理系統簡化版
|
||||
1027
詞彙生成待完成功能規格.md
1027
詞彙生成待完成功能規格.md
File diff suppressed because it is too large
Load Diff
711
詞彙生成與儲存規格.md
711
詞彙生成與儲存規格.md
|
|
@ -1,711 +0,0 @@
|
|||
# 詞彙生成與儲存系統規格
|
||||
|
||||
## 系統概述
|
||||
|
||||
DramaLing詞彙學習系統是一個基於AI的英語詞彙學習平台,主要功能包括詞彙生成、智能分析、儲存管理和學習追蹤。系統採用前後端分離架構,前端使用Next.js 14,後端使用.NET 8 Web API,資料庫使用SQLite + Entity Framework Core。
|
||||
|
||||
## 1. 詞彙生成功能規格
|
||||
|
||||
### 1.1 句子分析功能
|
||||
|
||||
#### 核心特性
|
||||
- **AI分析引擎**: 使用Google Gemini API進行句子深度分析
|
||||
- **多層次分析**: 包含詞彙分析、語法檢查、整句翻譯
|
||||
- **個人化適配**: 根據用戶英語程度(A1-C2)調整分析深度
|
||||
- **智能快取**: 使用快取機制避免重複AI調用
|
||||
- **使用量追蹤**: 追蹤用戶查詢使用量並實施限制
|
||||
|
||||
#### API端點
|
||||
- **端點**: `POST /api/ai/analyze-sentence`
|
||||
- **功能**: 句子綜合分析
|
||||
- **輸入參數** (AnalyzeSentenceRequest):
|
||||
```json
|
||||
{
|
||||
"inputText": "string", // 用戶輸入的英文句子 (最多1000字元)
|
||||
"userLevel": "string", // 用戶英語程度 (A1-C2, 預設A2)
|
||||
"forceRefresh": boolean, // 是否強制重新分析
|
||||
"analysisMode": "string" // 分析模式 ("full")
|
||||
}
|
||||
```
|
||||
- **回應格式**:
|
||||
```json
|
||||
{
|
||||
"success": boolean,
|
||||
"data": {
|
||||
"wordAnalysis": {
|
||||
"[word]": {
|
||||
"word": "string",
|
||||
"translation": "string",
|
||||
"definition": "string",
|
||||
"partOfSpeech": "string",
|
||||
"pronunciation": "string",
|
||||
"isHighValue": boolean,
|
||||
"difficultyLevel": "string" // CEFR等級
|
||||
}
|
||||
},
|
||||
"sentenceMeaning": {
|
||||
"translation": "string",
|
||||
"explanation": "string"
|
||||
},
|
||||
"grammarCorrection": {
|
||||
"hasErrors": boolean,
|
||||
"originalText": "string",
|
||||
"correctedText": "string",
|
||||
"corrections": [
|
||||
{
|
||||
"errorType": "string",
|
||||
"original": "string",
|
||||
"corrected": "string",
|
||||
"reason": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"finalAnalysisText": "string",
|
||||
"highValueWords": ["string"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 快取機制 (SentenceAnalysisCache)
|
||||
- **表名**: `SentenceAnalysisCache`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class SentenceAnalysisCache
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string InputTextHash { get; set; } // SHA-256雜湊
|
||||
public string InputText { get; set; } // 原始輸入文本
|
||||
public string? CorrectedText { get; set; } // 修正後文本
|
||||
public bool HasGrammarErrors { get; set; } // 是否有語法錯誤
|
||||
public string? GrammarCorrections { get; set; } // JSON格式語法修正
|
||||
public string AnalysisResult { get; set; } // JSON格式分析結果
|
||||
public string? HighValueWords { get; set; } // JSON格式高價值詞彙
|
||||
public string? PhrasesDetected { get; set; } // JSON格式檢測片語
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public int AccessCount { get; set; } // 存取次數
|
||||
public DateTime? LastAccessedAt { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 實現位置
|
||||
- **前端**: `frontend/app/generate/page.tsx:29-100`
|
||||
- **後端控制器**: `backend/DramaLing.Api/Controllers/AIController.cs`
|
||||
- **服務層**: `backend/DramaLing.Api/Services/GeminiService.cs`
|
||||
- **快取服務**: `backend/DramaLing.Api/Services/AnalysisCacheService.cs`
|
||||
|
||||
### 1.2 詞卡生成功能
|
||||
|
||||
#### 核心特性
|
||||
- **智能萃取**: 支援詞彙萃取(vocabulary)和智能萃取(smart)兩種模式
|
||||
- **批量生成**: 一次可生成1-20張詞卡
|
||||
- **多元內容**: 包含單字、翻譯、定義、例句、同義詞、難度等級
|
||||
- **測試模式**: 支援無認證的測試端點
|
||||
|
||||
#### API端點
|
||||
- **端點**: `POST /api/ai/test/generate` (測試用,無需認證)
|
||||
- **功能**: 生成詞卡
|
||||
- **輸入參數** (GenerateCardsRequest):
|
||||
```json
|
||||
{
|
||||
"inputText": "string", // 原始文本 (最多5000字元)
|
||||
"extractionType": "string", // "vocabulary" | "smart"
|
||||
"cardCount": number // 1-20
|
||||
}
|
||||
```
|
||||
- **回應格式**:
|
||||
```json
|
||||
{
|
||||
"success": boolean,
|
||||
"data": {
|
||||
"taskId": "guid",
|
||||
"status": "completed",
|
||||
"generatedCards": [
|
||||
{
|
||||
"word": "string",
|
||||
"translation": "string",
|
||||
"definition": "string",
|
||||
"partOfSpeech": "string",
|
||||
"pronunciation": "string",
|
||||
"example": "string",
|
||||
"exampleTranslation": "string",
|
||||
"synonyms": ["string"],
|
||||
"difficultyLevel": "string",
|
||||
"score": number // AI生成評分
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": "string"
|
||||
}
|
||||
```
|
||||
|
||||
#### 實現位置
|
||||
- **前端**: `frontend/app/generate/page.tsx:114-151`
|
||||
- **後端**: `backend/DramaLing.Api/Controllers/AIController.cs:42-100`
|
||||
|
||||
### 1.3 互動式詞彙查詢
|
||||
|
||||
#### 核心特性
|
||||
- **點擊查詢**: 用戶可點擊句子中任意單字查看詳細資訊
|
||||
- **即時分析**: 動態調用AI API獲取單字分析
|
||||
- **高價值標示**: 自動標示高價值單字和片語
|
||||
- **使用量計費**: 區分高價值(免費)和低價值(計費)詞彙
|
||||
|
||||
#### API端點
|
||||
- **端點**: `POST /api/ai/query-word`
|
||||
- **輸入參數** (QueryWordRequest):
|
||||
```json
|
||||
{
|
||||
"word": "string", // 要查詢的單字
|
||||
"sentence": "string", // 上下文句子
|
||||
"analysisId": "guid?" // 分析ID (可選)
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用量統計 (WordQueryUsageStats)
|
||||
- **表名**: `WordQueryUsageStats`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class WordQueryUsageStats
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public DateOnly Date { get; set; } // 日期
|
||||
public int SentenceAnalysisCount { get; set; } // 句子分析次數
|
||||
public int HighValueWordClicks { get; set; } // 高價值詞彙點擊(免費)
|
||||
public int LowValueWordClicks { get; set; } // 低價值詞彙點擊(收費)
|
||||
public int TotalApiCalls { get; set; } // 總API調用次數
|
||||
public int UniqueWordsQueried { get; set; } // 查詢的獨特詞彙數
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 實現位置
|
||||
- **組件**: `frontend/components/ClickableTextV2.tsx`
|
||||
- **前端邏輯**: `frontend/app/generate/page.tsx:402-421`
|
||||
- **使用量服務**: `backend/DramaLing.Api/Services/UsageTrackingService.cs`
|
||||
|
||||
## 2. 詞彙儲存系統規格
|
||||
|
||||
### 2.1 資料庫架構
|
||||
|
||||
#### 2.1.1 用戶管理 (User)
|
||||
- **表名**: `user_profiles`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class User
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Username { get; set; } // 用戶名 (唯一)
|
||||
public string Email { get; set; } // 信箱 (唯一)
|
||||
public string PasswordHash { get; set; } // 密碼雜湊
|
||||
public string? DisplayName { get; set; } // 顯示名稱
|
||||
public string? AvatarUrl { get; set; } // 頭像URL
|
||||
public string SubscriptionType { get; set; } = "free"; // 訂閱類型
|
||||
public Dictionary<string, object> Preferences { get; set; } // JSON偏好設定
|
||||
|
||||
// 個人化學習相關
|
||||
public string EnglishLevel { get; set; } = "A2"; // 英語程度(A1-C2)
|
||||
public DateTime LevelUpdatedAt { get; set; } // 程度更新時間
|
||||
public bool IsLevelVerified { get; set; } = false; // 是否通過測試驗證
|
||||
public string? LevelNotes { get; set; } // 程度設定備註
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.2 詞卡實體 (Flashcard)
|
||||
- **表名**: `flashcards`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class Flashcard
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; } // 所屬用戶
|
||||
public Guid CardSetId { get; set; } // 所屬卡組
|
||||
|
||||
// 詞卡內容
|
||||
[Required, MaxLength(255)]
|
||||
public string Word { get; set; } // 單字
|
||||
[Required]
|
||||
public string Translation { get; set; } // 翻譯
|
||||
[Required]
|
||||
public string Definition { get; set; } // 定義
|
||||
[MaxLength(50)]
|
||||
public string? PartOfSpeech { get; set; } // 詞性
|
||||
[MaxLength(255)]
|
||||
public string? Pronunciation { get; set; } // 發音
|
||||
public string? Example { get; set; } // 例句
|
||||
public string? ExampleTranslation { get; set; } // 例句翻譯
|
||||
|
||||
// SM-2 間隔重複算法參數
|
||||
public float EasinessFactor { get; set; } = 2.5f; // 難易度係數
|
||||
public int Repetitions { get; set; } = 0; // 重複次數
|
||||
public int IntervalDays { get; set; } = 1; // 間隔天數
|
||||
public DateTime NextReviewDate { get; set; } // 下次複習日期
|
||||
|
||||
// 學習統計
|
||||
[Range(0, 100)]
|
||||
public int MasteryLevel { get; set; } = 0; // 掌握程度(0-100)
|
||||
public int TimesReviewed { get; set; } = 0; // 複習次數
|
||||
public int TimesCorrect { get; set; } = 0; // 正確次數
|
||||
public DateTime? LastReviewedAt { get; set; } // 最後複習時間
|
||||
|
||||
// 狀態管理
|
||||
public bool IsFavorite { get; set; } = false; // 是否收藏
|
||||
public bool IsArchived { get; set; } = false; // 是否封存
|
||||
[MaxLength(10)]
|
||||
public string? DifficultyLevel { get; set; } // 難度等級(A1-C2)
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.3 卡組實體 (CardSet)
|
||||
- **表名**: `card_sets`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class CardSet
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; } // 所屬用戶
|
||||
|
||||
[Required, MaxLength(255)]
|
||||
public string Name { get; set; } // 卡組名稱
|
||||
public string? Description { get; set; } // 描述
|
||||
[MaxLength(50)]
|
||||
public string Color { get; set; } = "bg-blue-500"; // 顏色標籤
|
||||
public int CardCount { get; set; } = 0; // 詞卡數量
|
||||
public bool IsDefault { get; set; } = false; // 是否為預設卡組
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.4 標籤系統 (Tag & FlashcardTag)
|
||||
- **表名**: `tags`, `flashcard_tags`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class Tag
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
[Required, MaxLength(100)]
|
||||
public string Name { get; set; } // 標籤名稱
|
||||
[Required, MaxLength(50)]
|
||||
public string Color { get; set; } // 標籤顏色
|
||||
public int UsageCount { get; set; } = 0; // 使用次數
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class FlashcardTag // 多對多關聯表
|
||||
{
|
||||
public Guid FlashcardId { get; set; }
|
||||
public Guid TagId { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.5 學習追蹤 (StudySession & StudyRecord)
|
||||
- **表名**: `study_sessions`, `study_records`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class StudySession
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
[Required, MaxLength(50)]
|
||||
public string SessionType { get; set; } // 學習模式
|
||||
public DateTime StartedAt { get; set; } // 開始時間
|
||||
public DateTime? EndedAt { get; set; } // 結束時間
|
||||
public int TotalCards { get; set; } = 0; // 總詞卡數
|
||||
public int CorrectCount { get; set; } = 0; // 正確數量
|
||||
public int DurationSeconds { get; set; } = 0; // 持續時間(秒)
|
||||
public int AverageResponseTimeMs { get; set; } = 0; // 平均回應時間(毫秒)
|
||||
}
|
||||
|
||||
public class StudyRecord
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public Guid FlashcardId { get; set; }
|
||||
public Guid SessionId { get; set; }
|
||||
[Required, MaxLength(50)]
|
||||
public string StudyMode { get; set; } // 學習模式
|
||||
public int QualityRating { get; set; } // 品質評分(0-5)
|
||||
public int? ResponseTimeMs { get; set; } // 回應時間(毫秒)
|
||||
public string? UserAnswer { get; set; } // 用戶答案
|
||||
public bool IsCorrect { get; set; } // 是否正確
|
||||
public DateTime StudiedAt { get; set; } // 學習時間
|
||||
|
||||
// SM-2算法歷史記錄
|
||||
public float PreviousEasinessFactor { get; set; }
|
||||
public int PreviousRepetitions { get; set; }
|
||||
public int PreviousIntervalDays { get; set; }
|
||||
public float NewEasinessFactor { get; set; }
|
||||
public int NewRepetitions { get; set; }
|
||||
public int NewIntervalDays { get; set; }
|
||||
public DateTime NextReviewDate { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.6 錯誤回報 (ErrorReport)
|
||||
- **表名**: `error_reports`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class ErrorReport
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public Guid FlashcardId { get; set; }
|
||||
[Required, MaxLength(100)]
|
||||
public string ReportType { get; set; } // 錯誤類型
|
||||
public string? Description { get; set; } // 描述
|
||||
[MaxLength(50)]
|
||||
public string? StudyMode { get; set; } // 學習模式
|
||||
[Required, MaxLength(50)]
|
||||
public string Status { get; set; } = "pending"; // 狀態
|
||||
public string? AdminNotes { get; set; } // 管理員備註
|
||||
public Guid? ResolvedBy { get; set; } // 解決者ID
|
||||
public DateTime? ResolvedAt { get; set; } // 解決時間
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.7 每日統計 (DailyStats)
|
||||
- **表名**: `daily_stats`
|
||||
- **主要欄位**:
|
||||
```csharp
|
||||
public class DailyStats
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public DateOnly Date { get; set; } // 日期
|
||||
public int WordsStudied { get; set; } = 0; // 學習單字數
|
||||
public int WordsCorrect { get; set; } = 0; // 正確單字數
|
||||
public int StudyTimeSeconds { get; set; } = 0; // 學習時間(秒)
|
||||
public int SessionCount { get; set; } = 0; // 學習場次
|
||||
public int CardsGenerated { get; set; } = 0; // 生成詞卡數
|
||||
public int AiApiCalls { get; set; } = 0; // AI API調用次數
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 儲存服務API
|
||||
|
||||
#### 2.2.1 詞卡管理API
|
||||
- **取得詞卡列表**: `GET /api/flashcards`
|
||||
- **查詢參數**:
|
||||
- `setId` (Guid?): 指定卡組ID
|
||||
- `search` (string?): 搜尋關鍵字(詞彙/翻譯)
|
||||
- `favoritesOnly` (bool): 僅顯示收藏
|
||||
- `limit` (int): 限制數量(預設50,最多100)
|
||||
- `offset` (int): 偏移量(分頁用)
|
||||
- **回應格式**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"flashcards": [...],
|
||||
"total": number,
|
||||
"hasMore": boolean
|
||||
}
|
||||
}
|
||||
```
|
||||
- **實現位置**: `FlashcardsController.cs:58-135`
|
||||
|
||||
- **建立詞卡**: `POST /api/flashcards`
|
||||
- **請求體** (CreateFlashcardRequest):
|
||||
```json
|
||||
{
|
||||
"cardSetId": "guid?", // 可選,未指定則使用預設卡組
|
||||
"word": "string", // 必填
|
||||
"translation": "string", // 必填
|
||||
"definition": "string", // 必填
|
||||
"partOfSpeech": "string?",
|
||||
"pronunciation": "string?",
|
||||
"example": "string?",
|
||||
"exampleTranslation": "string?"
|
||||
}
|
||||
```
|
||||
- **自動功能**: 如無指定卡組,自動分配到預設卡組或創建新的預設卡組
|
||||
- **實現位置**: `FlashcardsController.cs:137-200`
|
||||
|
||||
- **更新詞卡**: `PUT /api/flashcards/{id}`
|
||||
- **請求體** (UpdateFlashcardRequest): 支援部分欄位更新
|
||||
- **實現位置**: `FlashcardsController.cs:233-287`
|
||||
|
||||
- **刪除詞卡**: `DELETE /api/flashcards/{id}`
|
||||
- **實現位置**: `FlashcardsController.cs:289-324`
|
||||
|
||||
- **切換收藏狀態**: `POST /api/flashcards/{id}/favorite`
|
||||
- **功能**: 切換詞卡的收藏狀態
|
||||
|
||||
#### 2.2.2 卡組管理API
|
||||
- **取得卡組列表**: `GET /api/cardsets`
|
||||
- **回應**: 包含卡組基本資訊和詞卡數量統計
|
||||
|
||||
- **建立卡組**: `POST /api/cardsets`
|
||||
- **請求體** (CreateCardSetRequest):
|
||||
```json
|
||||
{
|
||||
"name": "string", // 必填
|
||||
"description": "string?", // 可選
|
||||
"isPublic": boolean // 可選,預設false
|
||||
}
|
||||
```
|
||||
|
||||
- **更新卡組**: `PUT /api/cardsets/{id}`
|
||||
- **請求體** (UpdateCardSetRequest): 支援部分欄位更新
|
||||
|
||||
- **刪除卡組**: `DELETE /api/cardsets/{id}`
|
||||
- **限制**: 無法刪除預設卡組
|
||||
|
||||
- **確保預設卡組**: `POST /api/cardsets/ensure-default`
|
||||
- **功能**: 確保用戶有預設卡組,如無則自動創建
|
||||
|
||||
#### 2.2.3 前端服務層
|
||||
- **檔案位置**: `frontend/lib/services/flashcards.ts`
|
||||
- **核心介面**:
|
||||
```typescript
|
||||
export interface Flashcard {
|
||||
id: string;
|
||||
word: string;
|
||||
translation: string;
|
||||
definition: string;
|
||||
partOfSpeech: string;
|
||||
pronunciation: string;
|
||||
example: string;
|
||||
exampleTranslation?: string;
|
||||
masteryLevel: number; // 0-100掌握程度
|
||||
timesReviewed: number; // 複習次數
|
||||
isFavorite: boolean; // 是否收藏
|
||||
nextReviewDate: string; // 下次複習日期
|
||||
createdAt: string;
|
||||
cardSet: {
|
||||
name: string;
|
||||
color: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CardSet {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
color: string;
|
||||
cardCount: number; // 詞卡數量
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
isDefault: boolean; // 是否為預設卡組
|
||||
progress: number; // 學習進度
|
||||
lastStudied: string; // 最後學習時間
|
||||
tags: string[]; // 標籤
|
||||
}
|
||||
|
||||
class FlashcardsService {
|
||||
// 詞卡CRUD操作
|
||||
async getFlashcards(cardSetId?: string): Promise<ApiResponse<{flashcards: Flashcard[]; total: number; hasMore: boolean}>>
|
||||
async createFlashcard(data: CreateFlashcardRequest): Promise<ApiResponse<Flashcard>>
|
||||
async updateFlashcard(id: string, data: Partial<CreateFlashcardRequest>): Promise<ApiResponse<Flashcard>>
|
||||
async deleteFlashcard(id: string): Promise<ApiResponse<void>>
|
||||
async toggleFavorite(id: string): Promise<ApiResponse<Flashcard>>
|
||||
|
||||
// 卡組CRUD操作
|
||||
async getCardSets(): Promise<ApiResponse<{sets: CardSet[]}>>
|
||||
async createCardSet(data: CreateCardSetRequest): Promise<ApiResponse<CardSet>>
|
||||
async deleteCardSet(id: string): Promise<ApiResponse<void>>
|
||||
async ensureDefaultCardSet(): Promise<ApiResponse<CardSet>>
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.4 資料庫關聯與索引
|
||||
- **外鍵關聯**:
|
||||
- `flashcards.user_id` → `user_profiles.id` (CASCADE)
|
||||
- `flashcards.card_set_id` → `card_sets.id` (CASCADE)
|
||||
- `card_sets.user_id` → `user_profiles.id` (CASCADE)
|
||||
- `flashcard_tags.flashcard_id` → `flashcards.id` (CASCADE)
|
||||
- `flashcard_tags.tag_id` → `tags.id` (CASCADE)
|
||||
|
||||
- **重要索引**:
|
||||
- `user_profiles`: email(UNIQUE), username(UNIQUE)
|
||||
- `flashcards`: user_id, card_set_id
|
||||
- `card_sets`: user_id
|
||||
- `tags`: user_id
|
||||
- `daily_stats`: (user_id, date)(UNIQUE)
|
||||
- `SentenceAnalysisCache`: input_text_hash(UNIQUE), expires_at
|
||||
|
||||
## 3. 系統整合流程
|
||||
|
||||
### 3.1 完整學習流程
|
||||
1. **用戶認證** → JWT Token驗證與用戶程度讀取
|
||||
2. **句子輸入** → 用戶在生成頁面輸入英文句子(最多300字元)
|
||||
3. **快取檢查** → 檢查是否已有分析結果快取
|
||||
4. **AI分析** → 調用Gemini API進行句子深度分析
|
||||
5. **結果快取** → 將分析結果儲存到`SentenceAnalysisCache`
|
||||
6. **互動探索** → 用戶點擊單字查看詳細分析(使用量追蹤)
|
||||
7. **詞卡生成** → 基於分析結果生成個人化詞卡
|
||||
8. **儲存管理** → 詞卡儲存到預設或指定卡組
|
||||
9. **學習追蹤** → 使用SM-2算法追蹤學習進度
|
||||
|
||||
### 3.2 資料流向圖
|
||||
```
|
||||
用戶請求 → JWT認證 → 程度檢查 → 輸入驗證 → 快取檢查 → AI API調用
|
||||
↓
|
||||
結果快取 → 使用量記錄 → 前端顯示 → 用戶互動 → 詞卡生成 → 資料庫儲存
|
||||
↓
|
||||
學習記錄 → SM-2更新 → 統計更新 → 進度追蹤
|
||||
```
|
||||
|
||||
### 3.3 關鍵業務邏輯
|
||||
|
||||
#### 3.3.1 預設卡組管理
|
||||
- 用戶首次創建詞卡時,系統自動創建名為「未分類」的預設卡組
|
||||
- 預設卡組無法刪除,確保用戶始終有存放詞卡的地方
|
||||
- 實現位置: `FlashcardsController.cs:33-56`
|
||||
|
||||
#### 3.3.2 SM-2間隔重複算法
|
||||
- 基於用戶答題品質(0-5分)調整複習間隔
|
||||
- 記錄詳細的學習歷史用於算法優化
|
||||
- 實現位置: `backend/DramaLing.Api/Services/SM2Algorithm.cs`
|
||||
|
||||
#### 3.3.3 使用量限制機制
|
||||
- 免費用戶3小時內最多5次句子分析
|
||||
- 高價值詞彙點擊免費,低價值詞彙計費
|
||||
- 實現位置: `UsageTrackingService.cs`
|
||||
|
||||
## 4. 技術架構
|
||||
|
||||
### 4.1 前端技術棧
|
||||
- **框架**: Next.js 14 (App Router)
|
||||
- **語言**: TypeScript
|
||||
- **樣式**: Tailwind CSS
|
||||
- **狀態管理**: React Hooks + Context API
|
||||
- **HTTP客戶端**: Fetch API
|
||||
- **認證**: JWT + localStorage
|
||||
- **路由保護**: ProtectedRoute組件
|
||||
|
||||
### 4.2 後端技術棧
|
||||
- **框架**: .NET 8 Web API
|
||||
- **ORM**: Entity Framework Core 8.0
|
||||
- **資料庫**: SQLite (開發)
|
||||
- **認證**: JWT Bearer Token
|
||||
- **AI服務**: Google Gemini API
|
||||
- **快取**: 內建EF快取 + 自定義快取服務
|
||||
- **日誌**: Microsoft.Extensions.Logging
|
||||
|
||||
### 4.3 資料庫設計特點
|
||||
- **Snake_case命名**: 所有資料表和欄位使用snake_case
|
||||
- **GUID主鍵**: 所有實體使用Guid作為主鍵
|
||||
- **軟刪除**: 支援IsArchived標記而非硬刪除
|
||||
- **審計欄位**: CreatedAt、UpdatedAt自動管理
|
||||
- **JSON欄位**: 使用JSON存儲複雜結構(如Preferences)
|
||||
|
||||
### 4.4 API設計原則
|
||||
- **RESTful風格**: 遵循REST慣例
|
||||
- **統一回應格式**:
|
||||
```json
|
||||
{
|
||||
"success": boolean,
|
||||
"data": object,
|
||||
"error": string,
|
||||
"message": string,
|
||||
"timestamp": datetime
|
||||
}
|
||||
```
|
||||
- **認證保護**: 所有業務API需JWT認證
|
||||
- **錯誤處理**: 統一錯誤處理中間件
|
||||
- **請求驗證**: DataAnnotations + 自定義驗證
|
||||
- **分頁支援**: 統一的limit/offset分頁機制
|
||||
|
||||
## 5. 效能與優化
|
||||
|
||||
### 5.1 快取策略
|
||||
- **句子分析快取**:
|
||||
- 使用SHA-256雜湊避重
|
||||
- 設定過期時間(ExpiresAt)
|
||||
- 記錄存取次數和最後存取時間
|
||||
|
||||
- **詞彙查詢快取**:
|
||||
- 減少重複AI API調用
|
||||
- 提升查詞響應速度
|
||||
|
||||
- **清理機制**:
|
||||
- 定期清理過期快取
|
||||
- 實現位置: `CacheCleanupService.cs`
|
||||
|
||||
### 5.2 效能監控
|
||||
- **日常統計**: DailyStats記錄用戶活動指標
|
||||
- **使用量追蹤**: WordQueryUsageStats追蹤API使用
|
||||
- **錯誤報告**: ErrorReport系統收集問題回饋
|
||||
|
||||
### 5.3 限制與配額
|
||||
- **免費用戶限制**:
|
||||
- 句子分析: 3小時內最多5次
|
||||
- 手動輸入: 最多300字元
|
||||
- 詞卡生成: 一次最多20張
|
||||
|
||||
- **付費用戶**: 無限制使用
|
||||
- **API速率限制**: 防止濫用攻擊
|
||||
|
||||
## 6. 安全性考量
|
||||
|
||||
### 6.1 認證與授權
|
||||
- **JWT Token**: 包含用戶ID和過期時間
|
||||
- **用戶隔離**: 所有資料按UserId嚴格隔離
|
||||
- **端點保護**: [Authorize]屬性保護敏感API
|
||||
- **測試端點**: 部分功能提供[AllowAnonymous]測試
|
||||
|
||||
### 6.2 資料安全
|
||||
- **密碼安全**: BCrypt雜湊儲存
|
||||
- **輸入驗證**: 前後端雙重驗證
|
||||
- **SQL注入防護**: EF Core參數化查詢
|
||||
- **XSS防護**: 自動HTML編碼
|
||||
|
||||
### 6.3 API安全
|
||||
- **CORS設定**: 限制來源域名
|
||||
- **請求大小限制**: 防止大檔案攻擊
|
||||
- **錯誤資訊隱藏**: 生產環境隱藏敏感錯誤
|
||||
|
||||
## 7. 監控與維護
|
||||
|
||||
### 7.1 系統監控
|
||||
- **健康檢查**: API健康狀態監控
|
||||
- **效能指標**: 回應時間、吞吐量追蹤
|
||||
- **錯誤追蹤**: 異常日誌和錯誤報告
|
||||
- **資源監控**: 資料庫和儲存空間監控
|
||||
|
||||
### 7.2 維護策略
|
||||
- **資料備份**: 定期資料庫備份機制
|
||||
- **日誌清理**: 定期清理舊日誌和快取
|
||||
- **版本控制**: Git版本管理和部署追蹤
|
||||
- **文件更新**: 與程式碼同步更新文件
|
||||
|
||||
## 8. 未來擴展規劃
|
||||
|
||||
### 8.1 短期功能擴展
|
||||
- **語音功能**: 整合Azure Speech Service
|
||||
- **個人化推薦**: 基於學習歷史的智能推薦
|
||||
- **社群功能**: 卡組分享和協作學習
|
||||
- **多語言支援**: 擴展到其他語言學習
|
||||
|
||||
### 8.2 技術架構升級
|
||||
- **資料庫升級**: 從SQLite遷移到PostgreSQL
|
||||
- **快取優化**: 引入Redis分散式快取
|
||||
- **微服務化**: 拆分AI服務和業務服務
|
||||
- **容器化部署**: Docker + Kubernetes部署
|
||||
|
||||
### 8.3 擴展性考量
|
||||
- **水平擴展**: 支援多實例負載平衡
|
||||
- **資料分割**: 大規模用戶資料分割策略
|
||||
- **CDN整合**: 靜態資源和多媒體加速
|
||||
- **國際化**: 多地區部署和資料同步
|
||||
|
||||
---
|
||||
|
||||
**文件版本**: 2.0
|
||||
**最後更新**: 2025-09-20
|
||||
**維護者**: DramaLing開發團隊
|
||||
**更新說明**: 基於實際程式碼架構重新整理,修正與系統實現的差距
|
||||
Loading…
Reference in New Issue