fix: 修正圖片載入失敗 + 清理未使用的 CSS 檔案

## 修正圖片載入問題
-  移除 flashcardUtils.ts 中的硬編碼端口
-  使用統一的 API_CONFIG.BASE_URL 配置
-  圖片 URL 現在自動跟隨後端配置
-  添加圖片載入失敗的錯誤處理

## 代碼清理
- 🗑️ 移除未使用的 review-simple/globals.css
- 🗑️ 移除對應的 CSS import
-  所有組件使用 Tailwind CSS,保持一致性

## 技術改進
- 🔧 消除硬編碼,提升維護性
- ⚙️ 統一配置管理,環境變數驅動
- 🛡️ 更好的錯誤處理和用戶體驗

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
鄭沛軒 2025-10-05 05:14:25 +08:00
parent fde7d1209b
commit 2a2c47da48
4 changed files with 24 additions and 111 deletions

View File

@ -1,107 +0,0 @@
/* 極簡MVP專用CSS - 復用現有的精美設計 */
/* 高級3D翻卡動畫 (來自原FlipMemoryTest設計) */
.flip-card-container {
perspective: 1000px;
}
.flip-card {
position: relative;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.flip-card.flipped {
transform: rotateY(180deg);
}
.flip-card-front,
.flip-card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 0.75rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.flip-card-back {
transform: rotateY(180deg);
}
.flip-card:hover .flip-card-front,
.flip-card:hover .flip-card-back {
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
/* 信心度按鈕 (復用ConfidenceButtons設計) */
.confidence-button {
transition: all 0.2s ease-in-out;
border: 2px solid;
border-radius: 0.5rem;
padding: 0.75rem;
font-weight: 500;
}
.confidence-button:hover:not(:disabled) {
transform: scale(1.02);
}
.confidence-button.selected {
transform: scale(1.05);
ring: 2px;
ring-color: rgba(59, 130, 246, 0.75);
ring-offset: 2px;
}
.confidence-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 進度條平滑動畫 */
.progress-bar {
transition: width 0.3s ease-in-out;
}
/* 響應式設計 (復用原有邏輯) */
@media (max-width: 480px) {
.flip-card-container {
min-height: 300px;
}
}
@media (min-width: 481px) and (max-width: 768px) {
.flip-card-container {
min-height: 350px;
}
}
@media (min-width: 769px) {
.flip-card-container {
min-height: 400px;
}
}
/* 內容區塊樣式 (復用原有設計) */
.content-block {
background-color: #f9fafb;
border-radius: 0.5rem;
padding: 1rem;
margin-bottom: 0.75rem;
}
.content-block h3 {
font-weight: 600;
color: #111827;
margin-bottom: 0.5rem;
text-align: left;
}
.content-block p {
color: #374151;
text-align: left;
}

View File

@ -1,7 +1,6 @@
'use client' 'use client'
import { Navigation } from '@/components/shared/Navigation' import { Navigation } from '@/components/shared/Navigation'
import './globals.css'
import { FlipMemory } from '@/components/review/quiz/FlipMemory' import { FlipMemory } from '@/components/review/quiz/FlipMemory'
import { VocabChoiceQuiz } from '@/components/review/quiz/VocabChoiceQuiz' import { VocabChoiceQuiz } from '@/components/review/quiz/VocabChoiceQuiz'
import { QuizProgress } from '@/components/review/ui/QuizProgress' import { QuizProgress } from '@/components/review/ui/QuizProgress'

View File

@ -72,6 +72,25 @@ export const FlashcardContentBlocks: React.FC<FlashcardContentBlocksProps> = ({
alt={`${flashcard.word} example`} alt={`${flashcard.word} example`}
className="w-full aspect-square object-cover rounded-lg border border-blue-300" className="w-full aspect-square object-cover rounded-lg border border-blue-300"
style={{ imageRendering: 'auto' }} style={{ imageRendering: 'auto' }}
onError={(e) => {
console.error('圖片載入失敗:', getFlashcardImageUrl(flashcard))
// 隱藏破損的圖片,顯示無圖片狀態
e.currentTarget.style.display = 'none'
const parent = e.currentTarget.parentElement?.parentElement
if (parent) {
parent.innerHTML = `
<div class="w-full max-w-md mx-auto h-48 bg-gray-100 rounded-lg border-2 border-dashed border-gray-300 flex items-center justify-center">
<div class="text-center text-gray-500">
<svg class="w-12 h-12 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<p class="text-sm"></p>
<p class="text-xs text-red-500 mt-1"></p>
</div>
</div>
`
}
}}
/> />
</div> </div>
) : ( ) : (

View File

@ -3,6 +3,8 @@
* *
*/ */
import { API_CONFIG } from '@/lib/config/api'
// 詞性簡寫轉換 // 詞性簡寫轉換
export const getPartOfSpeechDisplay = (partOfSpeech: string): string => { export const getPartOfSpeechDisplay = (partOfSpeech: string): string => {
const shortMap: {[key: string]: string} = { const shortMap: {[key: string]: string} = {
@ -77,7 +79,7 @@ export const getFlashcardImageUrl = (flashcard: any): string | null => {
if (flashcard.primaryImageUrl) { if (flashcard.primaryImageUrl) {
// 如果是相對路徑,加上後端基礎 URL // 如果是相對路徑,加上後端基礎 URL
if (flashcard.primaryImageUrl.startsWith('/')) { if (flashcard.primaryImageUrl.startsWith('/')) {
return `http://localhost:5008${flashcard.primaryImageUrl}` return `${API_CONFIG.BASE_URL}${flashcard.primaryImageUrl}`
} }
return flashcard.primaryImageUrl return flashcard.primaryImageUrl
} }
@ -87,10 +89,10 @@ export const getFlashcardImageUrl = (flashcard: any): string | null => {
const primaryImage = flashcard.exampleImages.find((img: any) => img.isPrimary) const primaryImage = flashcard.exampleImages.find((img: any) => img.isPrimary)
if (primaryImage) { if (primaryImage) {
const imageUrl = primaryImage.imageUrl const imageUrl = primaryImage.imageUrl
return imageUrl?.startsWith('/') ? `http://localhost:5008${imageUrl}` : imageUrl return imageUrl?.startsWith('/') ? `${API_CONFIG.BASE_URL}${imageUrl}` : imageUrl
} }
const firstImageUrl = flashcard.exampleImages[0].imageUrl const firstImageUrl = flashcard.exampleImages[0].imageUrl
return firstImageUrl?.startsWith('/') ? `http://localhost:5008${firstImageUrl}` : firstImageUrl return firstImageUrl?.startsWith('/') ? `${API_CONFIG.BASE_URL}${firstImageUrl}` : firstImageUrl
} }
return null return null