- 新增菜品对比功能,支持1v1左右和1vN上下布局切换 - 新增食物相生相克查询工具,包含API服务和详情页面 - 优化平台工具类,移除冗余的鸿蒙系统检测逻辑 - 更新版本号至1.5.1,修改更新日志和版本说明 - 修复多个页面列表分隔符构建器参数警告 - 新增雷达图组件,用于展示多维度对比数据 - 新增可编辑数值组件,支持双击编辑和范围验证 - 优化导出按钮,增加对比功能入口 - 新增搜索引擎枚举工具类 - 更新应用路由配置,添加对比和相生相克相关页面
955 lines
28 KiB
HTML
955 lines
28 KiB
HTML
<!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>
|