From acd20e3f2cdd95bbee07f57bb818af1e4b628b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=84=AD=E6=B2=9B=E8=BB=92?= Date: Wed, 24 Sep 2025 16:59:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=A6=E7=8F=BE=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E4=BE=8B=E5=8F=A5=E5=9C=96=E7=89=87=E9=A1=AF=E7=A4=BA=E5=92=8C?= =?UTF-8?q?AI=E7=94=9F=E6=88=90=E6=8C=89=E9=88=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改例句圖片邏輯,只顯示已確認存在的圖片 - 為沒有例句圖片的詞彙顯示「新增例句圖」按鈕 - 添加AI生成例句圖片的預留接口 - 提供直觀的視覺提示,區分已有圖片和待生成圖片 - 為下階段AI生成流程做準備 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/app/flashcards/page.tsx | 103 ++++++++++++++++++------------- 1 file changed, 61 insertions(+), 42 deletions(-) diff --git a/frontend/app/flashcards/page.tsx b/frontend/app/flashcards/page.tsx index f147454..2f4fcf7 100644 --- a/frontend/app/flashcards/page.tsx +++ b/frontend/app/flashcards/page.tsx @@ -24,35 +24,29 @@ function FlashcardsContent() { const [searchState, searchActions] = useFlashcardSearch(activeTab) // 例句圖片邏輯 - const getExampleImage = (word: string): string => { - const availableImages = [ - '/images/examples/bring_up.png', - '/images/examples/instinct.png', - '/images/examples/warrant.png' - ] - + const getExampleImage = (word: string): string | null => { const imageMap: {[key: string]: string} = { 'brought': '/images/examples/bring_up.png', 'instincts': '/images/examples/instinct.png', 'warrants': '/images/examples/warrant.png', - 'hello': '/images/examples/bring_up.png', - 'beautiful': '/images/examples/instinct.png', - 'understand': '/images/examples/warrant.png', - 'elaborate': '/images/examples/bring_up.png', - 'sophisticated': '/images/examples/instinct.png', - 'ubiquitous': '/images/examples/warrant.png' + 'evidence': '/images/examples/bring_up.png', + 'recovering': '/images/examples/instinct.png' } - // 根據詞彙返回對應圖片,如果沒有則根據字母分配 - const mappedImage = imageMap[word?.toLowerCase()] - if (mappedImage) return mappedImage + // 只返回已確認存在的圖片,沒有則返回 null + return imageMap[word?.toLowerCase()] || null + } - // 根據首字母分配圖片 - const firstChar = (word || 'a')[0].toLowerCase() - const charCode = firstChar.charCodeAt(0) - 97 // a=0, b=1, c=2... - const imageIndex = charCode % availableImages.length + // 檢查詞彙是否有例句圖片 + const hasExampleImage = (word: string): boolean => { + return getExampleImage(word) !== null + } - return availableImages[imageIndex] + // 處理AI生成例句圖片 (預留接口) + const handleGenerateExampleImage = (card: Flashcard) => { + console.log('準備為詞彙生成例句圖片:', card.word) + // TODO: 下階段實現AI生成流程 + // router.push(`/generate-image?word=${encodeURIComponent(card.word)}`) } // 初始化數據載入 @@ -258,6 +252,8 @@ function FlashcardsContent() { getCEFRColor={getCEFRColor} highlightSearchTerm={highlightSearchTerm} getExampleImage={getExampleImage} + hasExampleImage={hasExampleImage} + onGenerateExampleImage={handleGenerateExampleImage} router={router} /> @@ -469,7 +465,9 @@ interface SearchResultsProps { onToggleFavorite: (card: Flashcard) => void getCEFRColor: (level: string) => string highlightSearchTerm: (text: string, term: string) => React.ReactNode - getExampleImage: (word: string) => string + getExampleImage: (word: string) => string | null + hasExampleImage: (word: string) => boolean + onGenerateExampleImage: (card: Flashcard) => void router: any } @@ -482,6 +480,8 @@ function SearchResults({ getCEFRColor, highlightSearchTerm, getExampleImage, + hasExampleImage, + onGenerateExampleImage, router }: SearchResultsProps) { if (searchState.flashcards.length === 0) { @@ -521,6 +521,8 @@ function SearchResults({ getCEFRColor={getCEFRColor} highlightSearchTerm={highlightSearchTerm} getExampleImage={getExampleImage} + hasExampleImage={hasExampleImage} + onGenerateExampleImage={() => onGenerateExampleImage(card)} router={router} /> ))} @@ -537,11 +539,13 @@ interface FlashcardItemProps { onToggleFavorite: () => void getCEFRColor: (level: string) => string highlightSearchTerm: (text: string, term: string) => React.ReactNode - getExampleImage: (word: string) => string + getExampleImage: (word: string) => string | null + hasExampleImage: (word: string) => boolean + onGenerateExampleImage: () => void router: any } -function FlashcardItem({ card, searchTerm, onEdit, onDelete, onToggleFavorite, getCEFRColor, highlightSearchTerm, getExampleImage, router }: FlashcardItemProps) { +function FlashcardItem({ card, searchTerm, onEdit, onDelete, onToggleFavorite, getCEFRColor, highlightSearchTerm, getExampleImage, hasExampleImage, onGenerateExampleImage, router }: FlashcardItemProps) { return (
@@ -554,25 +558,40 @@ function FlashcardItem({ card, searchTerm, onEdit, onDelete, onToggleFavorite, g
- {/* 例句圖片 */} + {/* 例句圖片區域 */}
- {`${card.word} { - const target = e.target as HTMLImageElement - target.style.display = 'none' - target.parentElement!.innerHTML = ` -
- - - - 例句圖 -
- ` - }} - /> + {hasExampleImage(card.word) ? ( + // 有例句圖片時顯示圖片 + {`${card.word} { + const target = e.target as HTMLImageElement + target.style.display = 'none' + target.parentElement!.innerHTML = ` +
+ + + + 圖片載入失敗 +
+ ` + }} + /> + ) : ( + // 沒有例句圖片時顯示新增按鈕 + + )}
{/* 詞卡信息 */}