# AI生成網頁前端實際技術規格 ## 📋 **文件資訊** - **文件名稱**: AI生成網頁前端實際技術規格 - **版本**: v1.0 (基於實際實現) - **建立日期**: 2025-09-22 - **最後更新**: 2025-09-22 - **對應代碼**: /app/generate/page.tsx + /components/ClickableTextV2.tsx --- ## 🏗️ **實際技術架構** ### **A1. 技術棧組成** #### **A1.1 實際使用的技術** ```json { "framework": "Next.js 15.5.3", "language": "TypeScript", "styling": "Tailwind CSS", "stateManagement": "React Hooks (useState, useMemo, useCallback)", "api": "Fetch API (目前使用假資料)", "routing": "Next.js App Router", "authentication": "ProtectedRoute組件" } ``` #### **A1.2 實際組件結構** ``` GeneratePage (路由保護) └── GenerateContent (主邏輯組件) ├── 輸入模式 (!showAnalysisView) │ ├── 頁面標題 │ ├── 文本輸入區域 (textarea + 字符計數) │ ├── 分析按鈕 (載入狀態處理) │ └── 個人化程度指示器 └── 分析結果模式 (showAnalysisView) ├── 語法修正面板 (條件顯示) ├── 詞彙統計卡片區 (4張卡片) ├── ClickableTextV2 (例句展示) ├── 翻譯區域 (灰色背景) ├── 慣用語展示區域 ├── 慣用語彈窗 (Portal渲染) └── 返回按鈕 ``` --- ## 💾 **實際數據架構** ### **D1. 類型定義實現** #### **D1.1 語法修正類型** ```typescript interface GrammarCorrection { hasErrors: boolean; originalText: string; correctedText: string; corrections: Array<{ error: string; correction: string; type: string; explanation: string; }>; } ``` #### **D1.2 慣用語彈窗類型** ```typescript interface PhrasePopup { phrase: string; analysis: any; position: { x: number; y: number; }; } ``` #### **D1.3 實際WordAnalysis結構** ```typescript // 實際測試數據中的結構 interface MockWordAnalysis { word: string; translation: string; definition: string; partOfSpeech: string; pronunciation: string; difficultyLevel: string; isPhrase: boolean; synonyms: string[]; example: string; exampleTranslation: string; } ``` --- ## ⚡ **實際性能優化實現** ### **P1. React Hooks優化** #### **P1.1 記憶化函數** ```typescript // 統計計算優化 const vocabularyStats = useMemo(() => { if (!sentenceAnalysis) return null const userLevel = localStorage.getItem('userEnglishLevel') || 'A2' // 計算邏輯... return { simpleCount, moderateCount, difficultCount, phraseCount } }, [sentenceAnalysis]) // 事件處理優化 const handleAnalyzeSentence = useCallback(async () => { setIsAnalyzing(true) try { await new Promise(resolve => setTimeout(resolve, 1000)) // 模擬延遲 // 設置假資料... setShowAnalysisView(true) } finally { setIsAnalyzing(false) } }, []) const handleSaveWord = useCallback(async (word: string, analysis: any) => { try { const cardData = { word: word, translation: analysis.translation || '', definition: analysis.definition || '', pronunciation: analysis.pronunciation || `/${word}/`, partOfSpeech: analysis.partOfSpeech || 'unknown', example: `Example sentence with ${word}.` } const response = await flashcardsService.createFlashcard(cardData) if (response.success) { alert(`✅ 已將「${word}」保存到詞卡!`) } } catch (error) { console.error('Save word error:', error) throw error } }, []) ``` #### **P1.2 ClickableTextV2性能優化** ```typescript // 工具函數記憶化 const getCEFRColor = useCallback((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' // ...其他等級 default: return 'bg-gray-100 text-gray-700 border-gray-200' } }, []) const findWordAnalysis = useCallback((word: string) => { const cleanWord = word.toLowerCase().replace(/[.,!?;:]/g, '') return analysis?.[cleanWord] || analysis?.[word] || analysis?.[word.toLowerCase()] || null }, [analysis]) // 文字分割優化 const words = useMemo(() => text.split(/(\s+|[.,!?;:])/g), [text]) ``` --- ## 🎨 **實際樣式系統** ### **S1. 實現的設計Token** #### **S1.1 實際使用的顏色** ```css /* 詞彙分類顏色 - 實際實現 */ .simple-word { background: #f9fafb; /* bg-gray-50 */ border: #d1d5db; /* border-gray-300 */ color: #6b7280; /* text-gray-600 */ border-style: dashed; opacity: 0.8; } .moderate-word { background: #f0fdf4; /* bg-green-50 */ border: #bbf7d0; /* border-green-200 */ color: #15803d; /* text-green-700 */ font-weight: 500; /* font-medium */ } .difficult-word { background: #fff7ed; /* bg-orange-50 */ border: #fed7aa; /* border-orange-200 */ color: #c2410c; /* text-orange-700 */ font-weight: 500; /* font-medium */ } .phrase-word { background: #eff6ff; /* bg-blue-50 */ border: #bfdbfe; /* border-blue-200 */ color: #1d4ed8; /* text-blue-700 */ font-weight: 500; /* font-medium */ } ``` #### **S1.2 基礎樣式類別** ```css /* 實際基礎樣式 */ .word-base { cursor: pointer; transition: all 0.2s ease; border-radius: 0.25rem; /* rounded */ position: relative; margin: 0 0.125rem; /* mx-0.5 */ padding: 0.125rem 0.25rem; /* px-1 py-0.5 */ } /* 文字容器 */ .text-container { font-size: 1.25rem; /* text-lg */ line-height: 2.5; /* 自訂行高 */ } ``` --- ## 🔌 **實際API整合** ### **API1. 當前實現狀態** #### **API1.1 假資料模式** ```typescript // 當前使用的測試數據生成 const handleAnalyzeSentence = async () => { console.log('🚀 handleAnalyzeSentence 被調用 (假資料模式)') setIsAnalyzing(true) try { // 模擬API延遲 await new Promise(resolve => setTimeout(resolve, 1000)) const testSentence = "She just join the team, so let's cut her some slack until she get used to the workflow." // 完整的假資料設置... setSentenceAnalysis(mockAnalysis) setSentenceMeaning("她剛加入團隊,所以讓我們對她寬容一點,直到她習慣工作流程。") setGrammarCorrection({ hasErrors: true, originalText: testSentence, correctedText: correctedSentence, corrections: [/* 修正詳情 */] }) setShowAnalysisView(true) } finally { setIsAnalyzing(false) } } ``` #### **API1.2 真實API準備** **預留的API結構**: ```typescript // 註解中的真實API調用代碼 const response = await fetch('http://localhost:5000/api/ai/analyze-sentence', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ inputText: textInput, userLevel: userLevel, analysisMode: 'full' }) }) ``` --- ## 🎯 **實際算法實現** ### **A1. 詞彙分類算法** #### **A1.1 CEFR等級比較實現** ```typescript const CEFR_LEVELS = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2'] as const const getLevelIndex = (level: string): number => { return CEFR_LEVELS.indexOf(level as typeof CEFR_LEVELS[number]) } // ClickableTextV2中的實際分類邏輯 const getWordClass = (word: string) => { const wordAnalysis = findWordAnalysis(word) const baseClass = "cursor-pointer transition-all duration-200 rounded relative mx-0.5 px-1 py-0.5" if (wordAnalysis) { const isPhrase = getWordProperty(wordAnalysis, 'isPhrase') const difficultyLevel = getWordProperty(wordAnalysis, 'difficultyLevel') || 'A1' const userLevel = typeof window !== 'undefined' ? localStorage.getItem('userEnglishLevel') || 'A2' : 'A2' if (isPhrase) { return "" // 慣用語:純黑字 } const userIndex = getLevelIndex(userLevel) const wordIndex = getLevelIndex(difficultyLevel) if (userIndex > wordIndex) { return `${baseClass} bg-gray-50 border border-dashed border-gray-300 hover:bg-gray-100 hover:border-gray-400 text-gray-600 opacity-80` } else if (userIndex === wordIndex) { return `${baseClass} bg-green-50 border border-green-200 hover:bg-green-100 hover:shadow-lg transform hover:-translate-y-0.5 text-green-700 font-medium` } else { return `${baseClass} bg-orange-50 border border-orange-200 hover:bg-orange-100 hover:shadow-lg transform hover:-translate-y-0.5 text-orange-700 font-medium` } } else { return "" // 無資料:純黑字 } } ``` #### **A1.2 統計計算算法實現** ```typescript // 實際的統計計算邏輯 Object.entries(sentenceAnalysis).forEach(([, wordData]: [string, any]) => { const isPhrase = wordData?.isPhrase || wordData?.IsPhrase const difficultyLevel = wordData?.difficultyLevel || 'A1' if (isPhrase) { phraseCount++ } else { const userIndex = getLevelIndex(userLevel) const wordIndex = getLevelIndex(difficultyLevel) if (userIndex > wordIndex) { simpleCount++ } else if (userIndex === wordIndex) { moderateCount++ } else { difficultCount++ } } }) ``` --- ## 🎪 **實際互動系統實現** ### **I1. Portal彈窗系統** #### **I1.1 React Portal實現** ```typescript // ClickableTextV2中的實際Portal實現 const VocabPopup = () => { if (!selectedWord || !analysis?.[selectedWord] || !mounted) return null return createPortal( <> {/* 背景遮罩 */}
{/* 彈窗內容 */}