cgb2025 / index.html
googleman's picture
Add 2 files
66da0fc verified
Raw
History Blame Contribute Delete
26 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FoodAI - 智能食材识别 | Smart Ingredient Recognition</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.camera-circle {
box-shadow: 0 0 0 12px rgba(255, 255, 255, 0.2);
}
.ingredient-tag {
transition: all 0.3s ease;
}
.ingredient-tag:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.recipe-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
.slide-up {
animation: slideUp 0.5s ease-out forwards;
}
@keyframes slideUp {
from { transform: translateY(100px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.lang-btn.active {
background-color: rgba(255,255,255,0.3);
font-weight: bold;
}
</style>
</head>
<body class="bg-gray-50 font-sans">
<div class="max-w-md mx-auto bg-white min-h-screen shadow-lg overflow-hidden">
<!-- Header -->
<header class="bg-gradient-to-r from-green-500 to-emerald-600 text-white p-6">
<div class="flex justify-between items-center">
<div>
<h1 class="text-2xl font-bold">FoodAI</h1>
<p class="text-sm opacity-90" id="appSubtitle">智能烹饪助手 | Smart cooking companion</p>
</div>
<div class="flex space-x-4">
<div class="flex space-x-1 bg-white bg-opacity-20 rounded-full p-1">
<button class="lang-btn px-2 py-1 rounded-full text-xs active" data-lang="zh">中文</button>
<button class="lang-btn px-2 py-1 rounded-full text-xs" data-lang="en">EN</button>
</div>
<button class="p-2 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 transition" id="settingsBtn">
<i class="fas fa-cog"></i>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="p-6">
<!-- Camera Section -->
<div id="cameraSection" class="mb-8 slide-up">
<div class="text-center mb-6">
<h2 class="text-xl font-semibold text-gray-800 mb-2" id="cameraTitle">厨房里有什么? | What's in your kitchen?</h2>
<p class="text-gray-600" id="cameraSubtitle">拍摄您的食材照片,我们将推荐美味食谱 | Snap a photo of your ingredients and we'll suggest delicious recipes</p>
</div>
<div class="relative">
<div class="bg-gray-100 rounded-2xl overflow-hidden h-64 flex items-center justify-center">
<!-- Placeholder for camera preview -->
<div id="cameraPlaceholder" class="text-center">
<div class="mx-auto w-20 h-20 bg-white rounded-full flex items-center justify-center camera-circle mb-4">
<i class="fas fa-camera text-emerald-500 text-2xl"></i>
</div>
<p class="text-gray-500" id="cameraPrompt">点击拍摄食材 | Tap to capture ingredients</p>
</div>
<!-- Actual camera view would go here -->
<div id="cameraView" class="hidden w-full h-full"></div>
</div>
<div class="flex justify-center mt-6 space-x-4">
<button id="captureBtn" class="bg-emerald-500 hover:bg-emerald-600 text-white rounded-full w-16 h-16 flex items-center justify-center shadow-lg">
<i class="fas fa-camera text-2xl"></i>
</button>
<button id="uploadBtn" class="bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 rounded-full w-16 h-16 flex items-center justify-center shadow-lg">
<i class="fas fa-images text-2xl"></i>
</button>
</div>
</div>
</div>
<!-- Detected Ingredients (hidden initially) -->
<div id="ingredientsSection" class="hidden mb-8">
<div class="flex justify-between items-center mb-4">
<h3 class="font-medium text-gray-800" id="ingredientsTitle">检测到的食材 | Detected Ingredients</h3>
<button id="rescanBtn" class="text-sm text-emerald-600 hover:text-emerald-700" data-zh="重新扫描" data-en="Rescan">重新扫描 | Rescan</button>
</div>
<div id="ingredientsList" class="flex flex-wrap gap-2">
<!-- Ingredients will be added here dynamically -->
</div>
<div class="mt-4">
<button id="addManuallyBtn" class="text-sm text-gray-600 hover:text-gray-800 flex items-center" data-zh="手动添加" data-en="Add manually">
<i class="fas fa-plus-circle mr-1"></i> <span>手动添加 | Add manually</span>
</button>
</div>
</div>
<!-- Preferences Section -->
<div id="preferencesSection" class="hidden mb-8">
<h3 class="font-medium text-gray-800 mb-3" id="preferencesTitle">您的偏好 | Your Preferences</h3>
<div class="space-y-4">
<div>
<label class="block text-sm text-gray-600 mb-1" id="dietLabel">饮食限制 | Dietary Restrictions</label>
<div class="flex flex-wrap gap-2">
<button class="preference-btn" data-pref="vegetarian" data-zh="素食" data-en="Vegetarian">素食 | Vegetarian</button>
<button class="preference-btn" data-pref="vegan" data-zh="纯素" data-en="Vegan">纯素 | Vegan</button>
<button class="preference-btn" data-pref="gluten-free" data-zh="无麸质" data-en="Gluten-Free">无麸质 | Gluten-Free</button>
<button class="preference-btn" data-pref="dairy-free" data-zh="无乳制品" data-en="Dairy-Free">无乳制品 | Dairy-Free</button>
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1" id="cuisineLabel">菜系偏好 | Cuisine Preference</label>
<div class="flex flex-wrap gap-2">
<button class="preference-btn" data-pref="italian" data-zh="意大利" data-en="Italian">意大利 | Italian</button>
<button class="preference-btn" data-pref="asian" data-zh="亚洲" data-en="Asian">亚洲 | Asian</button>
<button class="preference-btn" data-pref="mexican" data-zh="墨西哥" data-en="Mexican">墨西哥 | Mexican</button>
<button class="preference-btn" data-pref="mediterranean" data-zh="地中海" data-en="Mediterranean">地中海 | Mediterranean</button>
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1" id="timeLabel">烹饪时间 | Cooking Time</label>
<div class="flex flex-wrap gap-2">
<button class="preference-btn" data-pref="quick" data-zh="快速 (30分钟内)" data-en="Quick (under 30min)">快速 (30分钟内) | Quick (under 30min)</button>
<button class="preference-btn" data-pref="medium" data-zh="中等 (30-60分钟)" data-en="Medium (30-60min)">中等 (30-60分钟) | Medium (30-60min)</button>
<button class="preference-btn" data-pref="slow" data-zh="慢煮 (60分钟以上)" data-en="Slow (60min+)">慢煮 (60分钟以上) | Slow (60min+)</button>
</div>
</div>
</div>
<div class="mt-6">
<button id="findRecipesBtn" class="w-full bg-emerald-500 hover:bg-emerald-600 text-white py-3 px-4 rounded-lg font-medium transition" data-zh="查找食谱" data-en="Find Recipes">
查找食谱 | Find Recipes
</button>
</div>
</div>
<!-- Recipe Recommendations (hidden initially) -->
<div id="recipesSection" class="hidden">
<div class="flex justify-between items-center mb-4">
<h3 class="font-medium text-gray-800" id="recipesTitle">推荐食谱 | Recommended Recipes</h3>
<button id="newSearchBtn" class="text-sm text-emerald-600 hover:text-emerald-700" data-zh="新的搜索" data-en="New Search">新的搜索 | New Search</button>
</div>
<div id="recipesList" class="space-y-4">
<!-- Recipes will be added here dynamically -->
</div>
</div>
<!-- Loading State (hidden initially) -->
<div id="loadingSection" class="hidden flex flex-col items-center justify-center py-12">
<div class="animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-emerald-500 mb-4"></div>
<p class="text-gray-600" id="loadingText">正在分析您的食材... | Analyzing your ingredients...</p>
<p class="text-sm text-gray-500 mt-1" id="loadingSubtext">正在为您寻找完美食谱 | Finding the perfect recipes for you</p>
</div>
</main>
<!-- Navigation -->
<nav class="bg-white border-t border-gray-200 p-4">
<div class="flex justify-around">
<button class="text-emerald-600 p-2 rounded-full">
<i class="fas fa-home text-xl"></i>
</button>
<button class="text-gray-500 hover:text-emerald-600 p-2 rounded-full">
<i class="fas fa-search text-xl"></i>
</button>
<button class="bg-emerald-500 text-white p-4 rounded-full -mt-6 shadow-lg">
<i class="fas fa-camera text-xl"></i>
</button>
<button class="text-gray-500 hover:text-emerald-600 p-2 rounded-full">
<i class="fas fa-heart text-xl"></i>
</button>
<button class="text-gray-500 hover:text-emerald-600 p-2 rounded-full">
<i class="fas fa-user text-xl"></i>
</button>
</div>
</nav>
</div>
<script>
// Sample data
const sampleIngredients = [
{ name: "Tomato", name_zh: "番茄", confidence: 92 },
{ name: "Onion", name_zh: "洋葱", confidence: 88 },
{ name: "Garlic", name_zh: "大蒜", confidence: 85 },
{ name: "Chicken", name_zh: "鸡肉", confidence: 95 },
{ name: "Bell Pepper", name_zh: "甜椒", confidence: 78 }
];
const sampleRecipes = [
{
name: "Chicken Stir Fry",
name_zh: "鸡肉炒菜",
ingredients: ["Chicken", "Bell Pepper", "Onion", "Garlic"],
ingredients_zh: ["鸡肉", "甜椒", "洋葱", "大蒜"],
time: "25 min",
difficulty: "Easy",
difficulty_zh: "简单",
rating: 4.5,
image: "https://images.unsplash.com/photo-1626804471355-3a98a8c0e212?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Tomato Basil Pasta",
name_zh: "番茄罗勒意面",
ingredients: ["Tomato", "Garlic", "Onion"],
ingredients_zh: ["番茄", "大蒜", "洋葱"],
time: "20 min",
difficulty: "Easy",
difficulty_zh: "简单",
rating: 4.2,
image: "https://images.unsplash.com/photo-1611270629569-8b357cb88da9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
},
{
name: "Chicken Fajitas",
name_zh: "鸡肉法吉塔",
ingredients: ["Chicken", "Bell Pepper", "Onion"],
ingredients_zh: ["鸡肉", "甜椒", "洋葱"],
time: "35 min",
difficulty: "Medium",
difficulty_zh: "中等",
rating: 4.7,
image: "https://images.unsplash.com/photo-1574897578259-a05fea79a1cd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80"
}
];
// DOM elements
const cameraSection = document.getElementById('cameraSection');
const ingredientsSection = document.getElementById('ingredientsSection');
const preferencesSection = document.getElementById('preferencesSection');
const recipesSection = document.getElementById('recipesSection');
const loadingSection = document.getElementById('loadingSection');
const ingredientsList = document.getElementById('ingredientsList');
const recipesList = document.getElementById('recipesList');
const captureBtn = document.getElementById('captureBtn');
const uploadBtn = document.getElementById('uploadBtn');
const rescanBtn = document.getElementById('rescanBtn');
const addManuallyBtn = document.getElementById('addManuallyBtn');
const findRecipesBtn = document.getElementById('findRecipesBtn');
const newSearchBtn = document.getElementById('newSearchBtn');
const preferenceBtns = document.querySelectorAll('.preference-btn');
const langBtns = document.querySelectorAll('.lang-btn');
// State
let selectedPreferences = new Set();
let currentLang = 'zh'; // Default to Chinese
// Event Listeners
captureBtn.addEventListener('click', simulateCapture);
uploadBtn.addEventListener('click', simulateUpload);
rescanBtn.addEventListener('click', resetToCamera);
addManuallyBtn.addEventListener('click', addManualIngredient);
findRecipesBtn.addEventListener('click', findRecipes);
newSearchBtn.addEventListener('click', resetToCamera);
// Language switch
langBtns.forEach(btn => {
btn.addEventListener('click', function() {
currentLang = this.getAttribute('data-lang');
updateLanguage();
// Update active state
langBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
});
});
preferenceBtns.forEach(btn => {
btn.addEventListener('click', function() {
const pref = this.getAttribute('data-pref');
if (selectedPreferences.has(pref)) {
selectedPreferences.delete(pref);
this.classList.remove('bg-emerald-500', 'text-white');
this.classList.add('bg-gray-100', 'text-gray-700');
} else {
selectedPreferences.add(pref);
this.classList.add('bg-emerald-500', 'text-white');
this.classList.remove('bg-gray-100', 'text-gray-700');
}
});
});
// Functions
function updateLanguage() {
// Update all elements with data attributes
document.querySelectorAll('[data-zh], [data-en]').forEach(el => {
if (el.tagName === 'BUTTON' || el.tagName === 'LABEL' || el.tagName === 'P' || el.tagName === 'H1' || el.tagName === 'H2' || el.tagName === 'H3' || el.tagName === 'SPAN') {
el.textContent = el.getAttribute(`data-${currentLang}`);
}
});
// Update titles and subtitles
document.getElementById('appSubtitle').textContent = currentLang === 'zh' ? '智能烹饪助手' : 'Smart cooking companion';
document.getElementById('cameraTitle').textContent = currentLang === 'zh' ? '厨房里有什么?' : 'What\'s in your kitchen?';
document.getElementById('cameraSubtitle').textContent = currentLang === 'zh' ? '拍摄您的食材照片,我们将推荐美味食谱' : 'Snap a photo of your ingredients and we\'ll suggest delicious recipes';
document.getElementById('cameraPrompt').textContent = currentLang === 'zh' ? '点击拍摄食材' : 'Tap to capture ingredients';
document.getElementById('ingredientsTitle').textContent = currentLang === 'zh' ? '检测到的食材' : 'Detected Ingredients';
document.getElementById('preferencesTitle').textContent = currentLang === 'zh' ? '您的偏好' : 'Your Preferences';
document.getElementById('dietLabel').textContent = currentLang === 'zh' ? '饮食限制' : 'Dietary Restrictions';
document.getElementById('cuisineLabel').textContent = currentLang === 'zh' ? '菜系偏好' : 'Cuisine Preference';
document.getElementById('timeLabel').textContent = currentLang === 'zh' ? '烹饪时间' : 'Cooking Time';
document.getElementById('recipesTitle').textContent = currentLang === 'zh' ? '推荐食谱' : 'Recommended Recipes';
document.getElementById('loadingText').textContent = currentLang === 'zh' ? '正在分析您的食材...' : 'Analyzing your ingredients...';
document.getElementById('loadingSubtext').textContent = currentLang === 'zh' ? '正在为您寻找完美食谱' : 'Finding the perfect recipes for you';
// Update displayed ingredients and recipes if they're visible
if (!ingredientsSection.classList.contains('hidden')) {
displayIngredients();
}
if (!recipesSection.classList.contains('hidden')) {
displayRecipes();
}
}
function simulateCapture() {
cameraSection.classList.add('hidden');
loadingSection.classList.remove('hidden');
// Simulate AI processing time
setTimeout(() => {
loadingSection.classList.add('hidden');
ingredientsSection.classList.remove('hidden');
preferencesSection.classList.remove('hidden');
displayIngredients();
}, 2000);
}
function simulateUpload() {
// In a real app, this would open the file picker
simulateCapture();
}
function displayIngredients() {
ingredientsList.innerHTML = '';
sampleIngredients.forEach(ingredient => {
const ingredientEl = document.createElement('div');
ingredientEl.className = 'ingredient-tag bg-emerald-100 text-emerald-800 px-3 py-1 rounded-full text-sm flex items-center';
const displayName = currentLang === 'zh' ? ingredient.name_zh : ingredient.name;
ingredientEl.innerHTML = `
${displayName}
<span class="ml-1 text-xs opacity-70">${ingredient.confidence}%</span>
<button class="ml-2 text-emerald-600 hover:text-emerald-800">
<i class="fas fa-times"></i>
</button>
`;
ingredientsList.appendChild(ingredientEl);
// Add click event to remove button
ingredientEl.querySelector('button').addEventListener('click', function() {
ingredientEl.remove();
});
});
}
function addManualIngredient() {
const promptText = currentLang === 'zh' ? '输入食材名称:' : 'Enter ingredient name:';
const ingredientName = prompt(promptText);
if (ingredientName) {
const ingredientEl = document.createElement('div');
ingredientEl.className = 'ingredient-tag bg-blue-100 text-blue-800 px-3 py-1 rounded-full text-sm flex items-center';
ingredientEl.innerHTML = `
${ingredientName}
<button class="ml-2 text-blue-600 hover:text-blue-800">
<i class="fas fa-times"></i>
</button>
`;
ingredientsList.appendChild(ingredientEl);
// Add click event to remove button
ingredientEl.querySelector('button').addEventListener('click', function() {
ingredientEl.remove();
});
}
}
function findRecipes() {
ingredientsSection.classList.add('hidden');
preferencesSection.classList.add('hidden');
loadingSection.classList.remove('hidden');
// Simulate recipe search time
setTimeout(() => {
loadingSection.classList.add('hidden');
recipesSection.classList.remove('hidden');
displayRecipes();
}, 1500);
}
function displayRecipes() {
recipesList.innerHTML = '';
sampleRecipes.forEach(recipe => {
const recipeEl = document.createElement('div');
recipeEl.className = 'recipe-card bg-white rounded-xl overflow-hidden shadow-md transition duration-300';
const displayName = currentLang === 'zh' ? recipe.name_zh : recipe.name;
const displayIngredients = currentLang === 'zh' ? recipe.ingredients_zh.join(', ') : recipe.ingredients.join(', ');
const displayDifficulty = currentLang === 'zh' ? recipe.difficulty_zh : recipe.difficulty;
recipeEl.innerHTML = `
<div class="relative h-40">
<img src="${recipe.image}" alt="${displayName}" class="w-full h-full object-cover">
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4">
<h4 class="text-white font-semibold text-lg">${displayName}</h4>
</div>
<div class="absolute top-2 right-2 bg-white/90 text-emerald-600 px-2 py-1 rounded-full text-xs font-medium">
${recipe.time}
</div>
</div>
<div class="p-4">
<div class="flex justify-between items-center mb-2">
<span class="text-sm text-gray-600">${displayIngredients}</span>
<div class="flex items-center">
<i class="fas fa-star text-yellow-400 mr-1"></i>
<span class="text-sm font-medium">${recipe.rating}</span>
</div>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-gray-500">${currentLang === 'zh' ? '难度: ' : 'Difficulty: '}${displayDifficulty}</span>
<button class="text-emerald-600 hover:text-emerald-700 text-sm font-medium">
${currentLang === 'zh' ? '查看食谱' : 'View Recipe'} <i class="fas fa-chevron-right ml-1"></i>
</button>
</div>
</div>
`;
recipesList.appendChild(recipeEl);
});
}
function resetToCamera() {
ingredientsSection.classList.add('hidden');
preferencesSection.classList.add('hidden');
recipesSection.classList.add('hidden');
loadingSection.classList.add('hidden');
cameraSection.classList.remove('hidden');
selectedPreferences.clear();
// Reset preference buttons
preferenceBtns.forEach(btn => {
btn.classList.remove('bg-emerald-500', 'text-white');
btn.classList.add('bg-gray-100', 'text-gray-700');
});
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=googleman/cgb2025" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>