Files
kitchen/preview/food_compat_query.html
Developer 3a056ca676 feat: 新增菜品对比和食物相生相克功能
- 新增菜品对比功能,支持1v1左右和1vN上下布局切换
- 新增食物相生相克查询工具,包含API服务和详情页面
- 优化平台工具类,移除冗余的鸿蒙系统检测逻辑
- 更新版本号至1.5.1,修改更新日志和版本说明
- 修复多个页面列表分隔符构建器参数警告
- 新增雷达图组件,用于展示多维度对比数据
- 新增可编辑数值组件,支持双击编辑和范围验证
- 优化导出按钮,增加对比功能入口
- 新增搜索引擎枚举工具类
- 更新应用路由配置,添加对比和相生相克相关页面
2026-05-01 09:13:27 +08:00

955 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>食物相生相克 - 查询</title>
<style>
:root {
--primary: #007AFF;
--primary-light: rgba(0,122,255,0.12);
--secondary: #FF9500;
--background: #F2F2F7;
--card: #FFFFFF;
--text1: #1C1C1E;
--text2: #8E8E93;
--text3: #C7C7CC;
--green: #34C759;
--red: #FF3B30;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 20px;
--radius-xl: 28px;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-7: 32px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
--shadow-md: 0 4px 12px rgba(0,0,0,0.08);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.1);
--font-xs: 11px;
--font-sm: 12px;
--font-md: 14px;
--font-lg: 16px;
--font-xl: 18px;
--font-xxl: 22px;
--glass-blur: 20px;
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif;
background: var(--background);
color: var(--text1);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
}
.app-container {
max-width: 480px;
margin: 0 auto;
min-height: 100vh;
background: var(--background);
position: relative;
overflow-x: hidden;
}
.header {
position: sticky;
top: 0;
z-index: 100;
background: rgba(242,242,247,0.72);
backdrop-filter: blur(var(--glass-blur));
-webkit-backdrop-filter: blur(var(--glass-blur));
border-bottom: 0.5px solid rgba(0,0,0,0.06);
padding: var(--space-3) var(--space-4);
}
.header-nav {
display: flex;
align-items: center;
gap: var(--space-3);
}
.back-btn {
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(0,0,0,0.04);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
font-size: 18px;
color: var(--primary);
transition: all 0.2s;
}
.back-btn:hover { background: rgba(0,0,0,0.08); }
.header-title {
font-size: var(--font-lg);
font-weight: 600;
flex: 1;
}
.header-action {
font-size: var(--font-sm);
color: var(--primary);
cursor: pointer;
font-weight: 500;
}
.content {
padding: var(--space-4);
padding-bottom: 100px;
}
.hero-card {
background: var(--card);
border-radius: var(--radius-xl);
padding: var(--space-6);
box-shadow: var(--shadow-md);
text-align: center;
margin-bottom: var(--space-4);
position: relative;
overflow: hidden;
}
.hero-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 120px;
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}
.hero-card.xiangsheng::before {
background: linear-gradient(180deg, rgba(52,199,89,0.12), transparent);
}
.hero-card.xiangke::before {
background: linear-gradient(180deg, rgba(255,59,48,0.12), transparent);
}
.hero-emoji {
width: 88px;
height: 88px;
border-radius: var(--radius-xl);
display: flex;
align-items: center;
justify-content: center;
font-size: 44px;
margin: 0 auto var(--space-4);
position: relative;
}
.hero-emoji.xiangsheng {
background: linear-gradient(135deg, rgba(52,199,89,0.2), rgba(48,209,88,0.1));
box-shadow: 0 8px 24px rgba(52,199,89,0.2);
}
.hero-emoji.xiangke {
background: linear-gradient(135deg, rgba(255,59,48,0.2), rgba(255,107,107,0.1));
box-shadow: 0 8px 24px rgba(255,59,48,0.2);
}
.hero-name {
font-size: 28px;
font-weight: 700;
letter-spacing: -0.5px;
margin-bottom: var(--space-2);
position: relative;
}
.hero-tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border-radius: 14px;
font-size: var(--font-md);
font-weight: 600;
position: relative;
}
.hero-tag.xiangsheng { background: rgba(52,199,89,0.12); color: #34C759; }
.hero-tag.xiangke { background: rgba(255,59,48,0.12); color: #FF3B30; }
.hero-views {
margin-top: var(--space-3);
font-size: var(--font-sm);
color: var(--text3);
position: relative;
}
.info-section {
background: var(--card);
border-radius: var(--radius-lg);
padding: var(--space-5);
box-shadow: var(--shadow-sm);
margin-bottom: var(--space-4);
}
.info-section-title {
font-size: var(--font-md);
font-weight: 600;
color: var(--text2);
margin-bottom: var(--space-3);
display: flex;
align-items: center;
gap: var(--space-2);
}
.info-desc {
font-size: var(--font-md);
line-height: 1.8;
color: var(--text1);
padding: var(--space-4);
background: var(--background);
border-radius: var(--radius-md);
}
.related-list {
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.related-item {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-3) var(--space-4);
background: var(--background);
border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.2s;
}
.related-item:hover { background: rgba(0,0,0,0.04); transform: translateX(4px); }
.related-emoji {
width: 40px;
height: 40px;
border-radius: var(--radius-sm);
background: var(--primary-light);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.related-info { flex: 1; min-width: 0; }
.related-name {
font-size: var(--font-md);
font-weight: 500;
}
.related-desc {
font-size: var(--font-xs);
color: var(--text2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.related-arrow {
color: var(--text3);
font-size: 16px;
flex-shrink: 0;
}
.nav-bar {
display: flex;
gap: var(--space-3);
margin-top: var(--space-4);
}
.nav-btn {
flex: 1;
padding: var(--space-4);
border-radius: var(--radius-lg);
border: none;
background: var(--card);
box-shadow: var(--shadow-sm);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
font-size: var(--font-md);
font-weight: 500;
color: var(--primary);
transition: all 0.2s;
}
.nav-btn:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.nav-btn:disabled { opacity: 0.4; cursor: default; transform: none; box-shadow: var(--shadow-sm); }
.action-bar {
display: flex;
gap: var(--space-3);
margin-top: var(--space-4);
}
.action-btn {
flex: 1;
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-lg);
border: none;
font-size: var(--font-md);
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
transition: all 0.2s;
}
.action-btn.copy {
background: var(--background);
color: var(--text1);
border: 1.5px solid rgba(0,0,0,0.06);
}
.action-btn.copy:hover { background: rgba(0,0,0,0.06); }
.action-btn.search {
background: linear-gradient(135deg, #007AFF, #5856D6);
color: white;
box-shadow: 0 4px 12px rgba(0,122,255,0.3);
}
.action-btn.search:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(0,122,255,0.4); }
.action-btn.copied {
background: rgba(52,199,89,0.12);
color: #34C759;
border-color: rgba(52,199,89,0.2);
}
.engine-menu {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.4);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
z-index: 300;
display: none;
align-items: flex-end;
justify-content: center;
}
.engine-menu.show { display: flex; }
.engine-sheet {
width: 100%;
max-width: 480px;
background: var(--card);
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
padding: var(--space-2) 0 var(--space-7);
animation: slideUp 0.3s cubic-bezier(0.32,0.72,0,1);
}
@keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
.engine-handle { width: 36px; height: 5px; background: var(--text3); border-radius: 3px; margin: var(--space-2) auto var(--space-3); }
.engine-title { font-size: var(--font-lg); font-weight: 600; text-align: center; margin-bottom: var(--space-4); padding: 0 var(--space-4); }
.engine-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); padding: 0 var(--space-4); }
.engine-card { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-4); background: var(--background); border-radius: var(--radius-md); cursor: pointer; transition: all 0.2s; border: 1.5px solid transparent; }
.engine-card:hover { border-color: var(--primary); transform: scale(1.02); }
.engine-card-icon { width: 40px; height: 40px; border-radius: var(--radius-sm); background: var(--primary-light); display: flex; align-items: center; justify-content: center; font-size: 20px; }
.engine-card-name { font-size: var(--font-md); font-weight: 600; }
.engine-card-url { font-size: var(--font-xs); color: var(--text3); }
.search-section {
background: var(--card);
border-radius: var(--radius-lg);
padding: var(--space-5);
box-shadow: var(--shadow-sm);
margin-bottom: var(--space-4);
}
.search-row {
display: flex;
align-items: center;
gap: var(--space-3);
}
.search-input-wrap {
flex: 1;
display: flex;
align-items: center;
height: 44px;
background: rgba(0,0,0,0.04);
border-radius: var(--radius-md);
padding: 0 var(--space-3);
gap: var(--space-2);
}
.search-input-wrap input {
flex: 1;
border: none;
background: none;
font-size: var(--font-md);
color: var(--text1);
outline: none;
}
.search-input-wrap input::placeholder { color: var(--text3); }
.search-btn {
height: 44px;
padding: 0 var(--space-5);
border-radius: var(--radius-md);
border: none;
background: linear-gradient(135deg, #007AFF, #5856D6);
color: white;
font-size: var(--font-md);
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 4px 12px rgba(0,122,255,0.3);
}
.search-btn:hover { transform: scale(1.03); }
.search-results {
margin-top: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.search-result-item {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-3);
background: var(--background);
border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.2s;
}
.search-result-item:hover { background: rgba(0,0,0,0.04); }
.search-result-emoji {
width: 40px;
height: 40px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.search-result-emoji.xiangsheng { background: rgba(52,199,89,0.12); }
.search-result-emoji.xiangke { background: rgba(255,59,48,0.12); }
.search-result-info { flex: 1; min-width: 0; }
.search-result-name { font-size: var(--font-md); font-weight: 500; }
.search-result-desc { font-size: var(--font-xs); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.search-result-arrow { color: var(--text3); font-size: 14px; }
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: var(--space-7);
gap: var(--space-3);
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid var(--text3);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: var(--font-sm); color: var(--text2); }
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: var(--space-7);
gap: var(--space-3);
}
.empty-icon {
width: 80px;
height: 80px;
border-radius: var(--radius-xl);
background: var(--primary-light);
display: flex;
align-items: center;
justify-content: center;
font-size: 36px;
}
.empty-title { font-size: var(--font-lg); font-weight: 600; }
.empty-desc { font-size: var(--font-sm); color: var(--text2); text-align: center; line-height: 1.5; }
.param-hint {
background: var(--card);
border-radius: var(--radius-lg);
padding: var(--space-5);
box-shadow: var(--shadow-sm);
margin-bottom: var(--space-4);
}
.param-hint-title {
font-size: var(--font-md);
font-weight: 600;
margin-bottom: var(--space-3);
display: flex;
align-items: center;
gap: var(--space-2);
}
.param-hint-code {
background: var(--background);
border-radius: var(--radius-md);
padding: var(--space-3);
font-family: 'SF Mono', 'Menlo', monospace;
font-size: var(--font-sm);
line-height: 1.8;
color: var(--text2);
overflow-x: auto;
}
.param-hint-code .param { color: var(--primary); font-weight: 600; }
.param-hint-code .value { color: var(--green); }
@media (prefers-color-scheme: dark) {
:root {
--background: #000000;
--card: #1C1C1E;
--text1: #FFFFFF;
--text2: #EBEBF5;
--text3: #8E8E93;
--primary-light: rgba(10,132,255,0.18);
--shadow-sm: 0 1px 3px rgba(0,0,0,0.2);
--shadow-md: 0 4px 12px rgba(0,0,0,0.3);
}
.header { background: rgba(0,0,0,0.72); border-bottom-color: rgba(255,255,255,0.06); }
.search-input-wrap { background: rgba(255,255,255,0.06); }
.info-desc, .related-item:hover, .search-result-item:hover { background: #2C2C2E; }
.param-hint-code { background: #2C2C2E; }
.engine-sheet { background: #1C1C1E; }
.engine-card { background: #2C2C2E; }
.action-btn.copy { background: #2C2C2E; border-color: rgba(255,255,255,0.06); color: var(--text1); }
.action-btn.copy:hover { background: #3A3A3C; }
}
</style>
</head>
<body>
<div class="app-container" id="app">
<div class="header">
<div class="header-nav">
<button class="back-btn" onclick="goBack()"></button>
<div class="header-title">食物相生相克查询</div>
<div class="header-action" onclick="goHome()">首页</div>
</div>
</div>
<div class="content" id="content">
<div class="loading-container">
<div class="loading-spinner"></div>
<div class="loading-text">加载中...</div>
</div>
</div>
</div>
<script>
const API_BASE = 'https://tools.wktyl.com/api/shiwu';
const foodEmojis = {
'苹果': '🍎', '香蕉': '🍌', '葡萄': '🍇', '草莓': '🍓', '西瓜': '🍉',
'橙子': '🍊', '柠檬': '🍋', '桃子': '🍑', '梨': '🍐', '芒果': '🥭',
'菠萝': '🍍', '樱桃': '🍒', '牛奶': '🥛', '鸡蛋': '🥚', '蜂蜜': '🍯',
'豆腐': '🧈', '萝卜': '🥕', '菠菜': '🥬', '番茄': '🍅', '土豆': '🥔',
'黄瓜': '🥒', '茄子': '🍆', '辣椒': '🌶️', '大蒜': '🧄', '洋葱': '🧅',
'蘑菇': '🍄', '芹菜': '🥦', '白菜': '🥬', '花生': '🥜', '核桃': '🌰',
'绿茶': '🍵', '咖啡': '☕', '啤酒': '🍺', '白酒': '🍶', '红酒': '🍷',
'虾': '🦐', '蟹': '🦀', '螃蟹': '🦀', '鱼': '🐟', '鸡肉': '🍗',
'牛肉': '🥩', '猪肉': '🥓', '羊肉': '🍖', '鸭肉': '🦆', '豆浆': '🥛',
'酸奶': '🥛', '奶酪': '🧀', '巧克力': '🍫', '醋': '🫗', '生姜': '🫚',
'红薯': '🍠', '山药': '🥔', '南瓜': '🎃', '木耳': '🍄', '银耳': '🍄',
'海带': '🌊', '芝麻': '🫘', '栗子': '🌰', '桂圆': '🔴', '山楂': '🔴',
'木瓜': '🟡', '榴莲': '🟡', '椰子': '🥥', '猕猴桃': '🥝', '火龙果': '🐉',
};
function getEmoji(name) {
for (const [key, val] of Object.entries(foodEmojis)) {
if (name.includes(key)) return val;
}
return '🍽️';
}
function getCategoryClass(name) { return name === '相生' ? 'xiangsheng' : 'xiangke'; }
function getCategoryIcon(name) { return name === '相生' ? '✅' : '⚠️'; }
async function fetchAPI(endpoint, params = {}) {
const url = new URL(`${API_BASE}/${endpoint}`);
Object.entries(params).forEach(([k, v]) => {
if (v !== null && v !== undefined && v !== '') url.searchParams.set(k, v);
});
try {
const resp = await fetch(url.toString());
const data = await resp.json();
if (data.code === 1) return data.data;
return null;
} catch (e) {
console.error('API Error:', e);
return null;
}
}
function goBack() {
if (document.referrer && document.referrer.includes(location.host)) {
history.back();
} else {
location.href = 'food_compatibility.html';
}
}
function goHome() {
location.href = 'food_compatibility.html';
}
function renderDetail(data) {
const catClass = getCategoryClass(data.category_name);
const content = document.getElementById('content');
content.innerHTML = `
<div class="hero-card ${catClass}">
<div class="hero-emoji ${catClass}">${getEmoji(data.sw)}</div>
<div class="hero-name">${data.sw}</div>
<div class="hero-tag ${catClass}">${getCategoryIcon(data.category_name)} ${data.category_name}</div>
<div class="hero-views">👁 ${data.views || 0} 次浏览</div>
</div>
<div class="info-section">
<div class="info-section-title">📝 搭配说明</div>
<div class="info-desc">${data.yh || '暂无说明'}</div>
</div>
${data.related && data.related.length > 0 ? `
<div class="info-section">
<div class="info-section-title">🔗 相关推荐</div>
<div class="related-list">
${data.related.map(r => `
<div class="related-item" onclick="loadById('${r.hash_id}')">
<div class="related-emoji">${getEmoji(r.sw)}</div>
<div class="related-info">
<div class="related-name">${r.sw}</div>
<div class="related-desc">${r.yh || ''}</div>
</div>
<div class="related-arrow"></div>
</div>
`).join('')}
</div>
</div>` : ''}
<div class="nav-bar">
<button class="nav-btn" ${!data.prev ? 'disabled' : ''} onclick="loadById('${data.prev ? data.prev.hash_id : ''}')">
${data.prev ? data.prev.sw : '无'}
</button>
<button class="nav-btn" ${!data.next ? 'disabled' : ''} onclick="loadById('${data.next ? data.next.hash_id : ''}')">
${data.next ? data.next.sw : '无'}
</button>
</div>
<div class="action-bar">
<button class="action-btn copy" id="copyBtn" onclick="copyFoodInfo('${data.sw}', \`${(data.yh || '').replace(/`/g, '\\`')}\`)">
📋 复制信息
</button>
<button class="action-btn search" onclick="showEngineMenu('${data.sw}')">
🔍 去搜索
</button>
</div>
<div class="search-section">
<div class="info-section-title">🔍 查询其他食物</div>
<div class="search-row">
<div class="search-input-wrap">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="${getComputedStyle(document.documentElement).getPropertyValue('--text3').trim()}" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
<input type="text" id="searchInput" placeholder="输入食物名称..." />
</div>
<button class="search-btn" onclick="doSearch()">搜索</button>
</div>
<div class="search-results" id="searchResults"></div>
</div>
`;
document.getElementById('searchInput').addEventListener('keydown', function(e) {
if (e.key === 'Enter') doSearch();
});
}
function renderSearchMode(keyword) {
const content = document.getElementById('content');
content.innerHTML = `
<div class="search-section">
<div class="info-section-title">🔍 搜索 "${keyword}"</div>
<div class="search-row">
<div class="search-input-wrap">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="${getComputedStyle(document.documentElement).getPropertyValue('--text3').trim()}" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
<input type="text" id="searchInput" placeholder="输入食物名称..." value="${keyword}" />
</div>
<button class="search-btn" onclick="doSearch()">搜索</button>
</div>
<div class="search-results" id="searchResults">
<div class="loading-container">
<div class="loading-spinner"></div>
<div class="loading-text">搜索中...</div>
</div>
</div>
</div>
<div class="param-hint">
<div class="param-hint-title">💡 接口说明</div>
<div class="param-hint-code">
其他页面可通过 URL 参数传递食物名称:<br>
<span class="param">?keyword=</span><span class="value">苹果</span><br>
<span class="param">?keyword=</span><span class="value">牛奶+蜂蜜</span><br><br>
或通过食物 ID 直接查看详情:<br>
<span class="param">?id=</span><span class="value">16o7v5</span>
</div>
</div>
`;
document.getElementById('searchInput').addEventListener('keydown', function(e) {
if (e.key === 'Enter') doSearch();
});
performSearch(keyword);
}
function renderEmpty() {
const content = document.getElementById('content');
content.innerHTML = `
<div class="empty-state">
<div class="empty-icon">🥗</div>
<div class="empty-title">食物相生相克查询</div>
<div class="empty-desc">请通过 URL 参数传递食物名称<br>或使用下方搜索框查询</div>
</div>
<div class="search-section">
<div class="info-section-title">🔍 搜索食物</div>
<div class="search-row">
<div class="search-input-wrap">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="${getComputedStyle(document.documentElement).getPropertyValue('--text3').trim()}" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
<input type="text" id="searchInput" placeholder="输入食物名称..." />
</div>
<button class="search-btn" onclick="doSearch()">搜索</button>
</div>
<div class="search-results" id="searchResults"></div>
</div>
<div class="param-hint">
<div class="param-hint-title">💡 接口说明</div>
<div class="param-hint-code">
其他页面可通过 URL 参数传递食物名称:<br>
<span class="param">?keyword=</span><span class="value">苹果</span><br>
<span class="param">?keyword=</span><span class="value">牛奶+蜂蜜</span><br><br>
或通过食物 ID 直接查看详情:<br>
<span class="param">?id=</span><span class="value">16o7v5</span><br><br>
Flutter 页面调用示例:<br>
Get.toNamed(<span class="value">'/tools/food-compat'</span>, parameters: {'keyword': '苹果'});
</div>
</div>
`;
document.getElementById('searchInput').addEventListener('keydown', function(e) {
if (e.key === 'Enter') doSearch();
});
}
async function loadById(id) {
const content = document.getElementById('content');
content.innerHTML = `
<div class="loading-container">
<div class="loading-spinner"></div>
<div class="loading-text">加载详情...</div>
</div>`;
const data = await fetchAPI('detail', { id });
if (!data) {
content.innerHTML = `
<div class="empty-state">
<div class="empty-icon">😔</div>
<div class="empty-title">加载失败</div>
<div class="empty-desc">食物不存在或网络错误</div>
</div>`;
return;
}
history.pushState(null, '', `?id=${data.hash_id}`);
renderDetail(data);
}
async function performSearch(keyword) {
const resultsEl = document.getElementById('searchResults');
if (!resultsEl) return;
resultsEl.innerHTML = `
<div class="loading-container">
<div class="loading-spinner"></div>
<div class="loading-text">搜索中...</div>
</div>`;
const data = await fetchAPI('search', { keyword, limit: 20 });
if (!data || !data.list || data.list.length === 0) {
resultsEl.innerHTML = `
<div class="empty-state" style="padding: var(--space-5)">
<div class="empty-icon" style="width:56px;height:56px;font-size:24px">🔍</div>
<div class="empty-title" style="font-size:var(--font-md)">未找到"${keyword}"</div>
<div class="empty-desc">试试其他关键词</div>
</div>`;
return;
}
resultsEl.innerHTML = '';
data.list.forEach(item => {
const catClass = getCategoryClass(item.category_name);
const el = document.createElement('div');
el.className = 'search-result-item';
el.onclick = () => loadById(item.hash_id);
el.innerHTML = `
<div class="search-result-emoji ${catClass}">${getEmoji(item.sw)}</div>
<div class="search-result-info">
<div class="search-result-name">${item.sw}</div>
<div class="search-result-desc">${getCategoryIcon(item.category_name)} ${item.category_name} · ${item.yh || ''}</div>
</div>
<div class="search-result-arrow"></div>`;
resultsEl.appendChild(el);
});
}
function doSearch() {
const input = document.getElementById('searchInput');
if (!input || !input.value.trim()) return;
const keyword = input.value.trim();
history.pushState(null, '', `?keyword=${encodeURIComponent(keyword)}`);
performSearch(keyword);
}
async function init() {
const urlParams = new URLSearchParams(window.location.search);
const keyword = urlParams.get('keyword');
const id = urlParams.get('id');
if (id) {
await loadById(id);
} else if (keyword) {
renderSearchMode(keyword);
} else {
renderEmpty();
}
}
window.addEventListener('popstate', init);
init();
let _searchKeyword = '';
function copyFoodInfo(name, desc) {
const text = `${name}】食物相生相克\n${desc}`;
navigator.clipboard.writeText(text).then(() => {
const btn = document.getElementById('copyBtn');
if (btn) {
btn.classList.add('copied');
btn.innerHTML = '✅ 已复制';
setTimeout(() => { btn.classList.remove('copied'); btn.innerHTML = '📋 复制信息'; }, 2000);
}
}).catch(() => {
const ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
const btn = document.getElementById('copyBtn');
if (btn) {
btn.classList.add('copied');
btn.innerHTML = '✅ 已复制';
setTimeout(() => { btn.classList.remove('copied'); btn.innerHTML = '📋 复制信息'; }, 2000);
}
});
}
function showEngineMenu(keyword) {
_searchKeyword = keyword;
document.getElementById('engineMenu').classList.add('show');
}
function closeEngineOutside(e) {
if (e.target === document.getElementById('engineMenu')) {
document.getElementById('engineMenu').classList.remove('show');
}
}
function launchEngine(engine) {
const keyword = _searchKeyword;
const encoded = encodeURIComponent(keyword + ' 相生相克');
const urls = {
baidu: `https://www.baidu.com/s?wd=${encoded}`,
bing: `https://www.bing.com/search?q=${encoded}`,
google: `https://www.google.com/search?q=${encoded}`,
sogou: `https://www.sogou.com/web?query=${encoded}`,
};
window.open(urls[engine] || urls.baidu, '_blank');
document.getElementById('engineMenu').classList.remove('show');
}
</script>
<!-- Engine Menu -->
<div class="engine-menu" id="engineMenu" onclick="closeEngineOutside(event)">
<div class="engine-sheet">
<div class="engine-handle"></div>
<div class="engine-title">🔍 选择搜索引擎</div>
<div class="engine-grid">
<div class="engine-card" onclick="launchEngine('baidu')">
<div class="engine-card-icon">🔍</div>
<div><div class="engine-card-name">百度</div><div class="engine-card-url">baidu.com</div></div>
</div>
<div class="engine-card" onclick="launchEngine('bing')">
<div class="engine-card-icon">🟦</div>
<div><div class="engine-card-name">Bing</div><div class="engine-card-url">bing.com</div></div>
</div>
<div class="engine-card" onclick="launchEngine('google')">
<div class="engine-card-icon">🌐</div>
<div><div class="engine-card-name">谷歌</div><div class="engine-card-url">google.com</div></div>
</div>
<div class="engine-card" onclick="launchEngine('sogou')">
<div class="engine-card-icon">🐕</div>
<div><div class="engine-card-name">搜狗</div><div class="engine-card-url">sogou.com</div></div>
</div>
</div>
</div>
</div>
</body>
</html>