From 11e19d5e1c7858e04ca7a3ae64234a77fd73933a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=84=AD=E6=B2=9B=E8=BB=92?= Date: Sun, 21 Sep 2025 21:31:08 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=84=AA=E5=8C=96=E8=A9=9E=E5=BD=99?= =?UTF-8?q?=E6=A8=99=E8=A8=98=E6=A8=A3=E5=BC=8F=E8=88=87=E7=89=87=E8=AA=9E?= =?UTF-8?q?=E9=BB=9E=E6=93=8A=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 還原例句中詞彙樣式為簡潔設計 (rounded, px-1 py-0.5) - 實現片語標籤點擊顯示詳細彈窗功能 - 修正假資料結構,區分cut動詞和cut someone some slack片語 - 調整片語標籤樣式與例句詞彙保持一致 - 修復Console錯誤和語法問題 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/app/generate/page.tsx | 152 +++++++++++++++++++++--- frontend/components/ClickableTextV2.tsx | 4 +- 2 files changed, 138 insertions(+), 18 deletions(-) diff --git a/frontend/app/generate/page.tsx b/frontend/app/generate/page.tsx index c08cc4b..ce88051 100644 --- a/frontend/app/generate/page.tsx +++ b/frontend/app/generate/page.tsx @@ -18,6 +18,11 @@ function GenerateContent() { const [finalText, setFinalText] = useState('') const [usageCount, setUsageCount] = useState(0) const [isPremium] = useState(true) + const [phrasePopup, setPhrasePopup] = useState<{ + phrase: string + analysis: any + position: { x: number, y: number } + } | null>(null) // 處理句子分析 - 使用假資料測試 @@ -120,16 +125,16 @@ function GenerateContent() { exampleTranslation: "我們去公園吧。" }, "cut": { - word: "cut someone some slack", - translation: "對某人寬容一點", - definition: "to be more lenient or forgiving with someone", - partOfSpeech: "idiom", - pronunciation: "/kʌt ˈsʌmwʌn sʌm slæk/", - difficultyLevel: "B2", - isPhrase: true, - synonyms: ["be lenient", "be forgiving", "give leeway"], - example: "Cut him some slack, he's new here.", - exampleTranslation: "對他寬容一點,他是新來的。" + word: "cut", + translation: "切;削減", + definition: "to use a knife or other sharp tool to divide something", + partOfSpeech: "verb", + pronunciation: "/kʌt/", + difficultyLevel: "A2", + isPhrase: false, + synonyms: ["slice", "chop", "reduce"], + example: "Please cut the apple.", + exampleTranslation: "請切蘋果。" }, "her": { word: "her", @@ -250,7 +255,19 @@ function GenerateContent() { synonyms: ["becomes", "obtains"], example: "It gets cold at night.", exampleTranslation: "晚上會變冷。" - } + }, + "cut someone some slack": { + word: "cut someone some slack", + translation: "對某人寬容一點", + definition: "to be more lenient or forgiving with someone", + partOfSpeech: "idiom", + pronunciation: "/kʌt ˈsʌmwʌn sʌm slæk/", + difficultyLevel: "B2", + isPhrase: true, + synonyms: ["be lenient", "be forgiving", "give leeway"], + example: "Cut him some slack, he's new here.", + exampleTranslation: "對他寬容一點,他是新來的。" + }, } // 設定結果 - 包含語法錯誤情境 @@ -621,7 +638,7 @@ function GenerateContent() { {/* 句子主體展示 */}
-
+
( { - console.log('Clicked phrase:', phrase) + className="cursor-pointer transition-all duration-200 rounded-lg relative mx-0.5 px-1 py-0.5 inline-flex items-center gap-1 bg-blue-50 border border-blue-200 hover:bg-blue-100 hover:shadow-lg transform hover:-translate-y-0.5 text-blue-700 font-medium" + onClick={(e) => { + // 找到片語的完整分析資料 + const phraseAnalysis = sentenceAnalysis?.["cut someone some slack"] + + if (phraseAnalysis) { + // 設定片語彈窗狀態 + setPhrasePopup({ + phrase: phrase.phrase, + analysis: phraseAnalysis, + position: { + x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, + y: e.currentTarget.getBoundingClientRect().bottom + 10 + } + }) + } }} title={`${phrase.phrase}: ${phrase.meaning}`} > - "{phrase.phrase}" + {phrase.phrase} ))}
@@ -712,6 +742,96 @@ function GenerateContent() {
)} + + {/* 片語彈窗 */} + {phrasePopup && ( + <> +
setPhrasePopup(null)} + /> +
+
+
+ +
+ +
+

{phrasePopup.analysis.word}

+
+ +
+
+ + {phrasePopup.analysis.partOfSpeech} + + {phrasePopup.analysis.pronunciation} +
+ + + {phrasePopup.analysis.difficultyLevel} + +
+
+ +
+
+

中文翻譯

+

{phrasePopup.analysis.translation}

+
+ +
+

英文定義

+

{phrasePopup.analysis.definition}

+
+ + {phrasePopup.analysis.example && ( +
+

例句

+
+

+ "{phrasePopup.analysis.example}" +

+

+ {phrasePopup.analysis.exampleTranslation} +

+
+
+ )} +
+ +
+ +
+
+ + )}
) diff --git a/frontend/components/ClickableTextV2.tsx b/frontend/components/ClickableTextV2.tsx index abe1e4a..f69d17d 100644 --- a/frontend/components/ClickableTextV2.tsx +++ b/frontend/components/ClickableTextV2.tsx @@ -104,7 +104,7 @@ export function ClickableTextV2({ const getWordClass = (word: string) => { const wordAnalysis = findWordAnalysis(word) - const baseClass = "cursor-pointer transition-all duration-200 rounded-lg relative mx-0.5 my-1 px-2 py-1 inline-flex items-center gap-1" + 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') @@ -275,7 +275,7 @@ export function ClickableTextV2({ return (
-
+
{words.map((word, index) => { if (word.trim() === '' || /^[.,!?;:\s]+$/.test(word)) { return {word}