import React, { useState, useRef, useEffect } from 'react'; import { XMarkIcon, InboxArrowDownIcon, CalendarIcon, ClockIcon, ChartBarIcon, CameraIcon, PhotoIcon } from '@heroicons/react/24/outline'; import axios from 'axios'; import Chart from 'chart.js/auto'; export default function FoodTracker() { // 狀態管理 const [activeTab, setActiveTab] = useState('analyzer'); const [selectedImage, setSelectedImage] = useState(null); const [isAnalyzing, setIsAnalyzing] = useState(false); const [analysisResults, setAnalysisResults] = useState(null); const [isDragging, setIsDragging] = useState(false); const [isLoading, setIsLoading] = useState(false); // 個人資料狀態 const [userProfile, setUserProfile] = useState({ name: '', age: '', gender: '', weight: '', height: '', activityLevel: 'moderate', goal: 'maintain', dailyCalories: 2000, proteinGoal: 150, fiberGoal: 25 }); // 食物記錄狀態 const [foodDiary, setFoodDiary] = useState([]); const [nutritionSummary, setNutritionSummary] = useState(null); const [recentMeals, setRecentMeals] = useState([]); // 相機相關 const videoRef = useRef(null); const canvasRef = useRef(null); const chartRef = useRef(null); const chartInstance = useRef(null); // 初始化 useEffect(() => { loadStoredData(); if (activeTab === 'tracking') { updateTrackingPage(); } }, [activeTab]); useEffect(() => { if (activeTab === 'tracking' && chartRef.current) { renderWeeklyChart(); } }, [activeTab, foodDiary]); // 載入儲存的資料 const loadStoredData = () => { try { const storedProfile = localStorage.getItem('userProfile'); const storedDiary = localStorage.getItem('foodDiary'); if (storedProfile) { setUserProfile(JSON.parse(storedProfile)); } if (storedDiary) { setFoodDiary(JSON.parse(storedDiary)); } } catch (error) { console.error('Error loading stored data:', error); } }; // 儲存資料 const saveData = () => { localStorage.setItem('userProfile', JSON.stringify(userProfile)); localStorage.setItem('foodDiary', JSON.stringify(foodDiary)); }; useEffect(() => { saveData(); }, [userProfile, foodDiary]); // 相機功能 const startCamera = async () => { try { if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); if (videoRef.current) { videoRef.current.srcObject = stream; } } } catch (error) { console.error('Error accessing camera:', error); alert('無法存取相機,請檢查權限設定'); } }; const capturePhoto = () => { if (videoRef.current && canvasRef.current) { const context = canvasRef.current.getContext('2d'); canvasRef.current.width = videoRef.current.videoWidth; canvasRef.current.height = videoRef.current.videoHeight; context.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height); const imageData = canvasRef.current.toDataURL('image/png'); setSelectedImage(imageData); analyzeImage(imageData); } }; // 檔案上傳處理 const handleDrop = async (event) => { event.preventDefault(); setIsDragging(false); if (event.dataTransfer.files && event.dataTransfer.files.length > 0) { await handleImageUpload({ target: { files: event.dataTransfer.files } }); } }; const handleDragOver = (event) => { event.preventDefault(); setIsDragging(true); }; const handleDragLeave = (event) => { event.preventDefault(); setIsDragging(false); }; const handleImageUpload = async (event) => { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { setSelectedImage(reader.result); }; reader.readAsDataURL(file); analyzeImage(reader.result); } }; // 圖片分析 const analyzeImage = async (imageData) => { setIsAnalyzing(true); try { // 將 base64 圖片數據轉換為 Blob const base64Response = await fetch(imageData); const blob = await base64Response.blob(); // 創建 FormData 並添加圖片 const formData = new FormData(); formData.append('file', blob, 'food-image.jpg'); // 發送到後端 API const response = await fetch('http://localhost:8000/api/ai/analyze-food-image/', { method: 'POST', body: formData, }); if (!response.ok) { throw new Error('分析請求失敗'); } const data = await response.json(); // 模擬詳細的分析結果 const mockAnalysis = { foodName: data.food_name, description: `這是 ${data.food_name},富含營養價值`, healthIndex: 75, glycemicIndex: 50, benefits: ['富含維生素', '低熱量', '高纖維'], nutrition: { calories: 150, protein: 8, carbs: 20, fat: 5, fiber: 3, sugar: 2 }, vitamins: { '維生素C': 25, '維生素A': 15 }, minerals: { '鈣': 50, '鐵': 2 } }; setAnalysisResults(mockAnalysis); } catch (error) { console.error('Error analyzing image:', error); alert('圖片分析失敗,請稍後再試'); } finally { setIsAnalyzing(false); } }; // 個人資料處理 const handleProfileChange = (field, value) => { setUserProfile(prev => ({ ...prev, [field]: value })); }; const saveProfile = () => { if (!userProfile.name || !userProfile.age || !userProfile.gender || !userProfile.height || !userProfile.weight) { alert('請填寫所有必填欄位!'); return; } // 計算基礎代謝率和每日熱量需求 const bmr = calculateBMR(userProfile); const dailyCalories = calculateDailyCalories(userProfile); const updatedProfile = { ...userProfile, bmr, dailyCalories, bmi: (userProfile.weight / Math.pow(userProfile.height / 100, 2)).toFixed(1) }; setUserProfile(updatedProfile); alert('個人資料已儲存成功!✅'); setActiveTab('analyzer'); }; const calculateBMR = (profile) => { let bmr; if (profile.gender === 'male') { bmr = 88.362 + (13.397 * profile.weight) + (4.799 * profile.height) - (5.677 * profile.age); } else { bmr = 447.593 + (9.247 * profile.weight) + (3.098 * profile.height) - (4.330 * profile.age); } return Math.round(bmr); }; const calculateDailyCalories = (profile) => { const activityMultipliers = { sedentary: 1.2, light: 1.375, moderate: 1.55, active: 1.725, extra: 1.9 }; let calories = profile.bmr * activityMultipliers[profile.activityLevel]; if (profile.goal === 'lose') { calories -= 300; } else if (profile.goal === 'gain') { calories += 300; } return Math.round(calories); }; // 食物記錄功能 const addToFoodDiary = () => { if (!analysisResults) { alert('沒有可加入的分析結果。'); return; } const meal = { ...analysisResults, id: Date.now(), timestamp: new Date().toISOString() }; setFoodDiary(prev => [...prev, meal]); alert(`${meal.foodName} 已加入記錄!`); setAnalysisResults(null); setSelectedImage(null); }; // 追蹤頁面更新 const updateTrackingPage = () => { if (!userProfile.name) return; const todayTotals = foodDiary.reduce((totals, meal) => { totals.calories += meal.nutrition.calories || 0; totals.protein += meal.nutrition.protein || 0; totals.fiber += meal.nutrition.fiber || 0; return totals; }, { calories: 0, protein: 0, fiber: 0 }); setNutritionSummary(todayTotals); }; // 圖表渲染 const renderWeeklyChart = () => { if (chartInstance.current) { chartInstance.current.destroy(); } const ctx = chartRef.current.getContext('2d'); // 模擬一週的數據 const days = ['週一', '週二', '週三', '週四', '週五', '週六', '週日']; const caloriesData = Array(7).fill(0).map(() => Math.floor(Math.random() * 1000 + 1000)); // 今天的數據 const today = new Date().getDay() || 7; caloriesData[today - 1] = foodDiary.reduce((total, meal) => total + (meal.nutrition.calories || 0), 0) || Math.floor(Math.random() * 1000 + 1000); chartInstance.current = new Chart(ctx, { type: 'line', data: { labels: days, datasets: [ { label: '熱量 (卡)', data: caloriesData, borderColor: 'rgb(255, 99, 132)', backgroundColor: 'rgba(255, 99, 132, 0.2)', tension: 0.3 } ] }, options: { responsive: true, plugins: { legend: { position: 'top', } }, scales: { y: { beginAtZero: true } } } }); }; // 通知功能 const showNotification = (message, type = 'info') => { const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 12px 20px; margin-bottom: 10px; border-radius: 4px; color: white; opacity: 0; transition: opacity 0.3s ease-in-out; box-shadow: 0 2px 10px rgba(0,0,0,0.1); z-index: 1000; background-color: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#F44336' : type === 'warning' ? '#FF9800' : '#2196F3'}; `; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '1'; }, 10); setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => { notification.remove(); }, 300); }, 3000); }; return (
{/* 標題 */}

🍎 AI食物營養分析器

智能分析您的飲食營養

{/* 標籤導航 */}
{/* 內容區域 */}
{/* 食物分析頁面 */} {activeTab === 'analyzer' && (
{!userProfile.name && (

請先到「個人資料」頁面完成設定,以獲得個人化營養建議和追蹤功能 👆

)}
{/* 相機區域 */}
{/* 控制按鈕 */}
{/* 載入中 */} {isAnalyzing && (

AI正在分析食物...

)} {/* 分析結果 */} {analysisResults && !isAnalyzing && (

{analysisResults.foodName}

{analysisResults.description}

{/* 健康指數 */}
健康指數 {analysisResults.healthIndex}/100
升糖指數 {analysisResults.glycemicIndex}/100
{/* 營養益處 */}

營養益處

{analysisResults.benefits.map((benefit, index) => ( {benefit} ))}
{/* 營養資訊 */}

營養資訊 (每100g)

{Object.entries(analysisResults.nutrition).map(([key, value]) => (
{key === 'calories' ? '熱量' : key === 'protein' ? '蛋白質' : key === 'carbs' ? '碳水化合物' : key === 'fat' ? '脂肪' : key === 'fiber' ? '纖維' : '糖分'}
{value} {key === 'calories' ? '卡' : 'g'}
))}
{/* 維生素和礦物質 */} {(analysisResults.vitamins || analysisResults.minerals) && (
{analysisResults.vitamins && (

維生素

{Object.entries(analysisResults.vitamins).map(([key, value]) => (
{key}
{value} mg
))}
)} {analysisResults.minerals && (

礦物質

{Object.entries(analysisResults.minerals).map(([key, value]) => (
{key}
{value} mg
))}
)}
)} {/* 個人化建議 */} {userProfile.name && (
💡 個人化建議
這份食物約佔您每日熱量建議的 {Math.round((analysisResults.nutrition.calories / userProfile.dailyCalories) * 100)}%。
)}
)}
)} {/* 個人資料頁面 */} {activeTab === 'profile' && (

個人資料設定

handleProfileChange('name', e.target.value)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="請輸入您的姓名" />
handleProfileChange('age', e.target.value)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="歲" />
handleProfileChange('height', e.target.value)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="公分" />
handleProfileChange('weight', e.target.value)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="公斤" />
)} {/* 營養追蹤頁面 */} {activeTab === 'tracking' && (
{!userProfile.name ? (
📊

請先完成個人資料設定

開始追蹤您的營養攝取 👆

) : (
{/* 歡迎訊息 */}

👋 你好, {userProfile.name}!

這是您今天的營養總覽。

{/* 統計卡片 */}
{nutritionSummary?.calories || 0}
今日熱量
{nutritionSummary?.protein || 0}g
蛋白質
{nutritionSummary?.fiber || 0}g
纖維
{userProfile.dailyCalories}
目標熱量
{/* 目標進度 */}

目標進度

{[ { label: '熱量', current: nutritionSummary?.calories || 0, goal: userProfile.dailyCalories, color: 'from-red-400 to-red-600' }, { label: '蛋白質', current: nutritionSummary?.protein || 0, goal: userProfile.proteinGoal, color: 'from-blue-400 to-blue-600' }, { label: '纖維', current: nutritionSummary?.fiber || 0, goal: userProfile.fiberGoal, color: 'from-green-400 to-green-600' } ].map((item, index) => { const progress = item.goal > 0 ? Math.min(100, (item.current / item.goal) * 100) : 0; return (
{item.label} {item.current} / {item.goal} {item.label === '熱量' ? '卡' : 'g'}
); })}
{/* 本週趨勢圖 */}

本週營養攝取趨勢

{/* 今日飲食記錄 */}

今日飲食記錄

{foodDiary.length} 項記錄
{foodDiary.length === 0 ? (
🍽️

今天尚未記錄任何食物

) : (
{foodDiary.map((meal) => (

{meal.foodName}

{new Date(meal.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
{Math.round(meal.nutrition.calories)} 卡
))}
)}
)}
)}
); }