Doubao_Creation/index.html

1651 lines
70 KiB
HTML
Raw Normal View History

2025-11-22 15:38:21 +08:00
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>今天吃什么 - 随机菜单抽取器</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- Tailwind 配置 -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#FF6B6B',
secondary: '#4ECDC4',
accent: '#FFE66D',
light: '#F7FFF7',
dark: '#1A535C'
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
animation: {
'spin-slow': 'spin 3s linear infinite',
'bounce-slow': 'bounce 2s infinite',
}
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.text-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
}
.card-hover {
@apply transition-all duration-300 hover:shadow-lg hover:-translate-y-1;
}
.btn-primary {
@apply bg-primary text-white font-bold py-3 px-6 rounded-lg shadow-md hover:bg-opacity-90 transition-all duration-300 transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-opacity-50;
}
.btn-secondary {
@apply bg-secondary text-white font-bold py-2 px-4 rounded-lg shadow-md hover:bg-opacity-90 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-secondary focus:ring-opacity-50;
}
.input-field {
@apply w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent;
}
.badge {
@apply inline-block px-2 py-1 text-xs font-semibold rounded-full;
}
.badge-primary {
@apply bg-primary bg-opacity-20 text-primary;
}
.badge-secondary {
@apply bg-secondary bg-opacity-20 text-secondary;
}
.badge-accent {
@apply bg-accent bg-opacity-20 text-dark;
}
}
/* 自定义动画 */
@keyframes spin-wheel {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spin-wheel-animation {
animation: spin-wheel 3s cubic-bezier(0.5, 0, 0.5, 1) forwards;
}
/* 转盘样式 */
.wheel-container {
position: relative;
width: 300px;
height: 300px;
margin: 0 auto;
}
.wheel {
width: 100%;
height: 100%;
border-radius: 50%;
position: relative;
overflow: hidden;
transition: transform 3s cubic-bezier(0.5, 0, 0.5, 1);
}
.wheel-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60px;
height: 60px;
background-color: white;
border-radius: 50%;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
.wheel-pointer {
position: absolute;
top: -10px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
border-bottom: 25px solid #FF6B6B;
z-index: 10;
}
/* 菜品卡片样式 */
.meal-card {
@apply bg-white rounded-xl shadow-md overflow-hidden card-hover;
}
/* 加载动画 */
.loading {
@apply inline-block animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-primary;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- 导航栏 -->
<nav class="bg-white shadow-md">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<div class="flex items-center space-x-2">
<img src="https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/rc/pc/super_tool/7bededf640e748eb98cc85f945aa5f41~tplv-a9rns2rl98-image.image?rcl=20251122135701E64BCA52CB770CFFFE61&amp;rk3s=8e244e95&amp;rrcfp=f06b921b&amp;x-expires=1766383039&amp;x-signature=jbkwsTYX7ACM3iPbPDTWLdmr7Dw%3D" alt="Logo" class="w-10 h-10">
<h1 class="text-2xl font-bold text-dark">今天吃什么</h1>
</div>
<div>
<button id="helpBtn" class="btn-secondary flex items-center">
<i class="fa fa-question-circle mr-2"></i> 使用帮助
</button>
</div>
</div>
</nav>
<!-- 主内容区 -->
<main class="container mx-auto px-4 py-8">
<!-- 加载状态提示 -->
<div id="loadingIndicator" class="mb-6 flex justify-center items-center hidden">
<div class="loading mr-2"></div>
<span>正在加载菜单数据...</span>
</div>
<!-- JSON文件加载状态提示 -->
<div id="jsonLoadStatus" class="mb-6 hidden">
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative" role="alert">
<strong class="font-bold">成功!</strong>
<span class="block sm:inline" id="jsonLoadMessage">已从menu.json加载菜品数据</span>
<span class="absolute top-0 bottom-0 right-0 px-4 py-3">
<button onclick="document.getElementById('jsonLoadStatus').classList.add('hidden')" class="text-green-700 hover:text-green-900"><i class="fa fa-times"></i></button>
</span>
</div>
</div>
<!-- 菜品管理区域 -->
<section class="mb-10">
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-4 gap-4">
<h2 class="text-xl font-bold text-dark flex items-center">
<i class="fa fa-cutlery text-primary mr-2"></i> 菜品管理
</h2>
<div class="flex flex-wrap gap-2">
<button id="toggleAddFormBtn" class="btn-primary flex items-center">
<i class="fa fa-plus mr-2"></i> 添加菜品
</button>
<button id="batchAddBtn" class="btn-secondary flex items-center">
<i class="fa fa-list-ol mr-2"></i> 批量添加
</button>
<label for="jsonFileInput" class="btn-secondary flex items-center cursor-pointer">
<i class="fa fa-upload mr-2"></i> 导入JSON
</label>
<input type="file" id="jsonFileInput" accept=".json" class="hidden">
<button id="clearBtn" class="bg-gray-200 text-gray-700 font-bold py-2 px-4 rounded-lg shadow-sm hover:bg-gray-300 transition-all duration-300 focus:outline-none">
<i class="fa fa-trash mr-2"></i> 清空菜单
</button>
</div>
</div>
<!-- 添加菜品表单 -->
<div id="addMealForm" class="mb-6 p-4 bg-gray-50 rounded-lg hidden">
<h3 class="font-bold mb-3 text-dark">添加菜品</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">菜品名称 <span class="text-red-500">*</span></label>
<input type="text" id="mealName" class="input-field" placeholder="请输入菜品名称">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">分类 <span class="text-red-500">*</span></label>
<select id="mealCategory" class="input-field">
<option value="主食">主食</option>
<option value="荤菜">荤菜</option>
<option value="素菜">素菜</option>
<option value="汤品">汤品</option>
<option value="其他">其他</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">备注</label>
<input type="text" id="mealNote" class="input-field" placeholder="可选">
</div>
</div>
<div class="mt-4 flex justify-end space-x-2">
<button id="cancelAddBtn" class="bg-gray-200 text-gray-700 font-bold py-2 px-4 rounded-lg shadow-sm hover:bg-gray-300 transition-all duration-300 focus:outline-none">
取消
</button>
<button id="confirmAddBtn" class="btn-secondary">
确认添加
</button>
</div>
</div>
<!-- 批量添加菜品表单 -->
<div id="batchAddForm" class="mb-6 p-4 bg-gray-50 rounded-lg hidden">
<h3 class="font-bold mb-3 text-dark">批量添加菜品</h3>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">菜品列表 <span class="text-red-500">*</span></label>
<textarea id="batchMealNames" class="input-field" rows="6" placeholder="请输入菜品名称,每行一个"></textarea>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">分类 <span class="text-red-500">*</span></label>
<select id="batchMealCategory" class="input-field">
<option value="主食">主食</option>
<option value="荤菜">荤菜</option>
<option value="素菜">素菜</option>
<option value="汤品">汤品</option>
<option value="其他">其他</option>
</select>
</div>
<div class="mt-4 flex justify-end space-x-2">
<button id="cancelBatchAddBtn" class="bg-gray-200 text-gray-700 font-bold py-2 px-4 rounded-lg shadow-sm hover:bg-gray-300 transition-all duration-300 focus:outline-none">
取消
</button>
<button id="confirmBatchAddBtn" class="btn-secondary">
确认添加
</button>
</div>
</div>
</div>
</section>
<!-- 菜单列表区域 -->
<section class="mb-10">
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-dark flex items-center">
<i class="fa fa-list text-primary mr-2"></i> 菜单列表
</h2>
<div class="flex items-center space-x-2">
<div class="relative">
<input type="text" id="searchInput" placeholder="搜索菜品..." class="input-field pr-10">
<i class="fa fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
<select id="categoryFilter" class="input-field">
<option value="all">全部分类</option>
</select>
</div>
</div>
<div id="menuList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 max-h-96 overflow-y-auto p-2">
<!-- 菜单列表将通过 JavaScript 动态生成 -->
<div class="col-span-full text-center py-10 text-gray-500" id="emptyMenuMsg">
<i class="fa fa-info-circle text-3xl mb-3"></i>
<p>暂无菜单数据,请点击上方"添加菜品"按钮添加</p>
</div>
</div>
<div class="mt-4 flex justify-end items-center">
<div class="text-sm text-gray-500">
<span id="totalMeals">0</span> 个菜品
</div>
</div>
</div>
</section>
<!-- 随机抽取区域 -->
<section class="mb-10">
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-bold mb-4 text-dark flex items-center">
<i class="fa fa-random text-primary mr-2"></i> 随机抽取
</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- 左侧:转盘 -->
<div class="flex flex-col items-center justify-center">
<div class="wheel-container">
<div class="wheel-pointer"></div>
<div id="wheel" class="wheel">
<!-- 转盘内容将通过 JavaScript 动态生成 -->
<img src="https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/rc/pc/super_tool/32352db51cfe4b978936ee83db986dc1~tplv-a9rns2rl98-image.image?rcl=20251122135701E64BCA52CB770CFFFE61&amp;rk3s=8e244e95&amp;rrcfp=f06b921b&amp;x-expires=1766383052&amp;x-signature=eeKdGn2fh3DIiXJ%2B%2FWpgP9UFJPE%3D" alt="转盘" class="w-full h-full object-cover opacity-50" id="wheelPlaceholder">
</div>
<div class="wheel-center">
<i class="fa fa-cutlery text-2xl text-primary"></i>
</div>
</div>
<div class="mt-6 w-full max-w-xs">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">抽取数量</label>
<div class="flex items-center">
<button id="decreaseCount" class="bg-gray-200 text-gray-700 w-10 h-10 rounded-l-lg flex items-center justify-center hover:bg-gray-300 transition-all duration-300">
<i class="fa fa-minus"></i>
</button>
<input type="number" id="drawCount" min="1" value="1" class="input-field text-center border-l-0 border-r-0 rounded-none">
<button id="increaseCount" class="bg-gray-200 text-gray-700 w-10 h-10 rounded-r-lg flex items-center justify-center hover:bg-gray-300 transition-all duration-300">
<i class="fa fa-plus"></i>
</button>
</div>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">抽取方式</label>
<div class="flex space-x-2">
<button id="normalDrawBtn" class="btn-primary flex-1">
<i class="fa fa-random mr-2"></i> 随机抽取
</button>
<button id="smartDrawBtn" class="btn-secondary flex-1">
<i class="fa fa-lightbulb-o mr-2"></i> 智能推荐
</button>
</div>
</div>
</div>
</div>
<!-- 右侧:结果展示 -->
<div>
<div class="bg-gray-50 rounded-lg p-6 min-h-[300px] flex flex-col">
<h3 class="font-bold mb-4 text-dark">抽取结果</h3>
<div id="resultContainer" class="flex-1">
<!-- 未抽取时的提示 -->
<div id="noResultMsg" class="flex flex-col items-center justify-center h-full text-gray-500">
<i class="fa fa-hand-pointer-o text-4xl mb-3"></i>
<p>点击上方按钮开始抽取</p>
</div>
<!-- 抽取结果列表 -->
<div id="resultList" class="hidden">
<!-- 结果将通过 JavaScript 动态生成 -->
</div>
</div>
<div class="mt-4 flex justify-between">
<button id="saveResultBtn" class="btn-secondary flex items-center hidden">
<i class="fa fa-save mr-2"></i> 保存结果
</button>
<button id="shareResultBtn" class="btn-secondary flex items-center hidden">
<i class="fa fa-share-alt mr-2"></i> 分享结果
</button>
</div>
</div>
<!-- 历史记录 -->
<div class="mt-6">
<h3 class="font-bold mb-3 text-dark flex items-center">
<i class="fa fa-history text-primary mr-2"></i> 历史记录
</h3>
<div id="historyList" class="bg-gray-50 rounded-lg p-4 max-h-40 overflow-y-auto">
<!-- 历史记录将通过 JavaScript 动态生成 -->
<div class="text-center py-3 text-gray-500 text-sm">
暂无历史记录
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- 页脚 -->
<footer class="bg-dark text-white py-6">
<div class="container mx-auto px-4 text-center">
<p>© 2025 今天吃什么 - 随机菜单抽取器</p>
<p class="text-sm text-gray-400 mt-2">解决您的选择困难症</p>
</div>
</footer>
<!-- 帮助模态框 -->
<div id="helpModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[80vh] overflow-y-auto">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-dark">使用帮助</h2>
<button id="closeHelpBtn" class="text-gray-500 hover:text-gray-700">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="space-y-4">
<div>
<h3 class="font-bold text-primary mb-2">1. 添加菜品</h3>
<p>您可以通过以下三种方式添加菜品:</p>
<ul class="list-disc pl-5 mt-2">
<li><strong>单个添加</strong>:点击"添加菜品"按钮,填写菜品名称、分类和备注信息</li>
<li><strong>批量添加</strong>:点击"批量添加"按钮,在文本框中每行输入一个菜品名称</li>
<li><strong>导入JSON</strong>:点击"导入JSON"按钮选择包含菜品数据的JSON文件</li>
<li><strong>自动加载</strong>页面会自动加载同目录下的menu.json文件</li>
</ul>
<p class="mt-2">JSON文件支持两种格式</p>
<ul class="list-disc pl-5 mt-2">
<li>简单格式:<code>["菜品1", "菜品2", "菜品3"]</code></li>
<li>对象格式:<code>[{"name": "菜品1", "category": "分类", "note": "备注"}, ...]</code></li>
</ul>
<p class="mt-2">对象格式支持的字段包括name/title/dish/food名称、category/type/kind分类、note/remark/description备注</p>
</div>
<div>
<h3 class="font-bold text-primary mb-2">2. 菜单管理</h3>
<p>导入菜单后,您可以:</p>
<ul class="list-disc pl-5 mt-2">
<li>使用搜索框搜索菜品</li>
<li>使用分类筛选器筛选菜品</li>
<li>点击"添加菜品"按钮手动添加菜品</li>
<li>点击菜品卡片上的编辑或删除按钮修改或删除菜品</li>
</ul>
</div>
<div>
<h3 class="font-bold text-primary mb-2">3. 随机抽取</h3>
<p>设置抽取数量后,点击以下按钮之一进行抽取:</p>
<ul class="list-disc pl-5 mt-2">
<li><strong>随机抽取</strong>:完全随机地从菜单中抽取指定数量的菜品</li>
<li><strong>智能推荐</strong>:基于历史抽取记录,优先推荐较少出现的菜品</li>
</ul>
<p class="mt-2">抽取结果将显示在右侧的结果区域,您可以保存或分享结果。</p>
</div>
<div>
<h3 class="font-bold text-primary mb-2">4. 数据存储</h3>
<p>本应用使用浏览器的本地存储功能保存您的菜单数据和历史记录,数据仅存储在您的设备上,不会上传到服务器。</p>
<p class="mt-2">清除浏览器缓存或使用隐私模式可能会导致数据丢失,请定期备份您的 Excel 文件。</p>
</div>
</div>
</div>
</div>
</div>
<!-- 编辑菜品模态框 -->
<div id="editModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-dark">编辑菜品</h2>
<button id="closeEditBtn" class="text-gray-500 hover:text-gray-700">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">菜品名称</label>
<input type="text" id="editMealName" class="input-field">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">分类</label>
<select id="editMealCategory" class="input-field">
<option value="主食">主食</option>
<option value="荤菜">荤菜</option>
<option value="素菜">素菜</option>
<option value="汤品">汤品</option>
<option value="其他">其他</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">备注</label>
<input type="text" id="editMealNote" class="input-field">
</div>
<div class="flex justify-end space-x-2 mt-6">
<button id="cancelEditBtn" class="bg-gray-200 text-gray-700 font-bold py-2 px-4 rounded-lg shadow-sm hover:bg-gray-300 transition-all duration-300 focus:outline-none">
取消
</button>
<button id="confirmEditBtn" class="btn-secondary">
确认修改
</button>
</div>
</div>
</div>
</div>
</div>
<!-- 分享结果模态框 -->
<div id="shareModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-dark">分享结果</h2>
<button id="closeShareBtn" class="text-gray-500 hover:text-gray-700">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">分享文本</label>
<textarea id="shareText" class="input-field" rows="4" readonly=""></textarea>
</div>
<div class="flex justify-between space-x-2 mt-6">
<button id="copyShareBtn" class="btn-secondary flex-1">
<i class="fa fa-copy mr-2"></i> 复制文本
</button>
<button id="shareWechatBtn" class="btn-secondary flex-1">
<i class="fa fa-wechat mr-2"></i> 分享到微信
</button>
</div>
</div>
</div>
</div>
</div>
<script>
// 全局变量
let mealData = [];
let filteredMeals = [];
let currentEditIndex = -1;
let drawHistory = [];
let categories = ['主食', '荤菜', '素菜', '汤品', '其他'];
// DOM 元素
const clearBtn = document.getElementById('clearBtn');
const menuList = document.getElementById('menuList');
const emptyMenuMsg = document.getElementById('emptyMenuMsg');
const totalMeals = document.getElementById('totalMeals');
const searchInput = document.getElementById('searchInput');
const categoryFilter = document.getElementById('categoryFilter');
const toggleAddFormBtn = document.getElementById('toggleAddFormBtn');
const addMealForm = document.getElementById('addMealForm');
const cancelAddBtn = document.getElementById('cancelAddBtn');
const confirmAddBtn = document.getElementById('confirmAddBtn');
const mealName = document.getElementById('mealName');
const mealCategory = document.getElementById('mealCategory');
const mealNote = document.getElementById('mealNote');
const decreaseCount = document.getElementById('decreaseCount');
const increaseCount = document.getElementById('increaseCount');
const drawCount = document.getElementById('drawCount');
const normalDrawBtn = document.getElementById('normalDrawBtn');
const smartDrawBtn = document.getElementById('smartDrawBtn');
const resultContainer = document.getElementById('resultContainer');
const noResultMsg = document.getElementById('noResultMsg');
const resultList = document.getElementById('resultList');
const saveResultBtn = document.getElementById('saveResultBtn');
const shareResultBtn = document.getElementById('shareResultBtn');
const historyList = document.getElementById('historyList');
const helpBtn = document.getElementById('helpBtn');
const helpModal = document.getElementById('helpModal');
const closeHelpBtn = document.getElementById('closeHelpBtn');
const editModal = document.getElementById('editModal');
const closeEditBtn = document.getElementById('closeEditBtn');
const cancelEditBtn = document.getElementById('cancelEditBtn');
const confirmEditBtn = document.getElementById('confirmEditBtn');
const editMealName = document.getElementById('editMealName');
const editMealCategory = document.getElementById('editMealCategory');
const editMealNote = document.getElementById('editMealNote');
const shareModal = document.getElementById('shareModal');
const closeShareBtn = document.getElementById('closeShareBtn');
const copyShareBtn = document.getElementById('copyShareBtn');
const shareWechatBtn = document.getElementById('shareWechatBtn');
const shareText = document.getElementById('shareText');
const wheel = document.getElementById('wheel');
const wheelPlaceholder = document.getElementById('wheelPlaceholder');
const loadingIndicator = document.getElementById('loadingIndicator');
const jsonLoadStatus = document.getElementById('jsonLoadStatus');
const jsonLoadMessage = document.getElementById('jsonLoadMessage');
// 初始化
function init() {
console.log('初始化应用');
// 显示加载状态
loadingIndicator.classList.remove('hidden');
// 加载本地存储的数据
loadFromLocalStorage();
// 优先读取本地JSON文件
loadMealsFromJsonFile()
.then(() => {
// 隐藏加载状态
loadingIndicator.classList.add('hidden');
// 更新菜单列表
updateMenuList();
// 更新分类筛选器
updateCategoryFilter();
// 更新历史记录
updateHistoryList();
// 更新转盘
updateWheel();
})
.catch(error => {
console.log('JSON文件加载失败:', error);
// 隐藏加载状态
loadingIndicator.classList.add('hidden');
// 即使JSON加载失败也要初始化界面
updateMenuList();
updateCategoryFilter();
updateHistoryList();
updateWheel();
});
// 事件监听
clearBtn.addEventListener('click', clearMenu);
searchInput.addEventListener('input', handleSearch);
categoryFilter.addEventListener('change', handleCategoryFilter);
toggleAddFormBtn.addEventListener('click', toggleAddForm);
cancelAddBtn.addEventListener('click', toggleAddForm);
confirmAddBtn.addEventListener('click', addMeal);
// 批量添加相关事件
const batchAddBtn = document.getElementById('batchAddBtn');
const cancelBatchAddBtn = document.getElementById('cancelBatchAddBtn');
const confirmBatchAddBtn = document.getElementById('confirmBatchAddBtn');
batchAddBtn.addEventListener('click', toggleBatchAddForm);
cancelBatchAddBtn.addEventListener('click', toggleBatchAddForm);
confirmBatchAddBtn.addEventListener('click', batchAddMeals);
// JSON导入相关事件
const jsonFileInput = document.getElementById('jsonFileInput');
jsonFileInput.addEventListener('change', handleJsonFileImport);
// 抽取相关事件
decreaseCount.addEventListener('click', decreaseDrawCount);
increaseCount.addEventListener('click', increaseDrawCount);
normalDrawBtn.addEventListener('click', () => drawMeals(false));
smartDrawBtn.addEventListener('click', () => drawMeals(true));
// 结果相关事件
saveResultBtn.addEventListener('click', saveResult);
shareResultBtn.addEventListener('click', showShareModal);
// 模态框相关事件
helpBtn.addEventListener('click', showHelpModal);
closeHelpBtn.addEventListener('click', hideHelpModal);
closeEditBtn.addEventListener('click', hideEditModal);
cancelEditBtn.addEventListener('click', hideEditModal);
confirmEditBtn.addEventListener('click', updateMeal);
closeShareBtn.addEventListener('click', hideShareModal);
copyShareBtn.addEventListener('click', copyShareText);
shareWechatBtn.addEventListener('click', shareToWechat);
}
// 批量添加菜品
function batchAddMeals() {
console.log('执行批量添加菜品');
const batchMealNames = document.getElementById('batchMealNames');
const batchMealCategory = document.getElementById('batchMealCategory');
const namesText = batchMealNames.value.trim();
const category = batchMealCategory.value;
console.log('菜品名称文本:', namesText);
console.log('分类:', category);
// 验证输入
if (!namesText) {
console.log('错误: 菜品名称文本为空');
alert('请输入菜品名称');
batchMealNames.focus();
return;
}
// 按行分割
const names = namesText.split('\n').filter(name => name.trim());
console.log('解析后的菜品名称:', names);
if (names.length === 0) {
console.log('错误: 未找到有效的菜品名称');
alert('未找到有效的菜品名称');
batchMealNames.focus();
return;
}
// 检查重复
const duplicates = [];
names.forEach(name => {
if (mealData.some(meal => meal.name === name.trim())) {
duplicates.push(name.trim());
}
});
console.log('重复的菜品:', duplicates);
if (duplicates.length > 0) {
const confirmMsg = `以下菜品已存在,是否仍然添加其他菜品?\n${duplicates.join('\n')}`;
if (!confirm(confirmMsg)) {
console.log('用户取消添加');
return;
}
}
// 添加菜品
let addedCount = 0;
names.forEach(name => {
const trimmedName = name.trim();
if (trimmedName && !mealData.some(meal => meal.name === trimmedName)) {
const newMeal = {
name: trimmedName,
category: category,
note: '',
count: 0
};
console.log('添加新菜品:', newMeal);
mealData.push(newMeal);
addedCount++;
}
});
console.log('成功添加的菜品数量:', addedCount);
// 添加新分类
if (!categories.includes(category)) {
console.log('添加新分类:', category);
categories.push(category);
updateCategoryFilter();
}
// 更新界面
console.log('更新菜单列表');
updateMenuList();
updateWheel();
saveToLocalStorage();
// 显示成功消息
console.log('批量添加完成');
alert(`成功添加 ${addedCount} 个菜品`);
// 隐藏表单
toggleBatchAddForm();
}
// 切换批量添加菜品表单
function toggleBatchAddForm() {
const batchAddForm = document.getElementById('batchAddForm');
const addMealForm = document.getElementById('addMealForm');
// 隐藏单个添加表单
addMealForm.classList.add('hidden');
// 切换批量添加表单
batchAddForm.classList.toggle('hidden');
// 如果显示表单,清空输入
if (!batchAddForm.classList.contains('hidden')) {
document.getElementById('batchMealNames').value = '';
document.getElementById('batchMealNames').focus();
}
}
// 清空菜单
function clearMenu() {
if (mealData.length === 0) return;
if (confirm('确定要清空所有菜单数据吗?此操作不可撤销。')) {
mealData = [];
filteredMeals = [];
updateMenuList();
updateCategoryFilter();
updateWheel();
saveToLocalStorage();
}
}
// 更新菜单列表
function updateMenuList() {
// 如果没有数据,显示空消息
if (mealData.length === 0) {
emptyMenuMsg.classList.remove('hidden');
menuList.innerHTML = '';
menuList.appendChild(emptyMenuMsg);
totalMeals.textContent = '0';
return;
}
// 隐藏空消息
emptyMenuMsg.classList.add('hidden');
// 更新总数
totalMeals.textContent = mealData.length;
// 过滤数据
filteredMeals = mealData.filter(meal => {
const matchesSearch = meal.name.toLowerCase().includes(searchInput.value.toLowerCase()) ||
meal.note.toLowerCase().includes(searchInput.value.toLowerCase());
const matchesCategory = categoryFilter.value === 'all' || meal.category === categoryFilter.value;
return matchesSearch && matchesCategory;
});
// 清空列表
menuList.innerHTML = '';
// 添加菜品卡片
filteredMeals.forEach((meal, index) => {
const card = document.createElement('div');
card.className = 'meal-card';
// 获取分类对应的颜色
const categoryColor = getCategoryColor(meal.category);
card.innerHTML = `
<div class="p-4">
<div class="flex justify-between items-start">
<h3 class="font-bold text-lg">${meal.name}</h3>
<span class="badge ${categoryColor}">${meal.category}</span>
</div>
${meal.note ? `<p class="text-gray-600 text-sm mt-1">${meal.note}</p>` : ''}
<div class="flex justify-end mt-3 space-x-2">
<button class="text-gray-500 hover:text-primary" onclick="editMeal(${index})">
<i class="fa fa-pencil"></i>
</button>
<button class="text-gray-500 hover:text-red-500" onclick="deleteMeal(${index})">
<i class="fa fa-trash"></i>
</button>
</div>
</div>
`;
menuList.appendChild(card);
});
}
// 获取分类对应的颜色
function getCategoryColor(category) {
switch (category) {
case '主食': return 'badge-primary';
case '荤菜': return 'badge-secondary';
case '素菜': return 'badge-accent';
case '汤品': return 'badge-primary';
default: return 'badge-secondary';
}
}
// 更新分类筛选器
function updateCategoryFilter() {
// 清空现有选项
categoryFilter.innerHTML = '<option value="all">全部分类</option>';
// 添加分类选项
categories.forEach(category => {
const option = document.createElement('option');
option.value = category;
option.textContent = category;
categoryFilter.appendChild(option);
});
}
// 处理搜索
function handleSearch() {
updateMenuList();
}
// 处理分类筛选
function handleCategoryFilter() {
updateMenuList();
}
// 切换添加菜品表单
function toggleAddForm() {
console.log('切换添加菜品表单');
// 隐藏批量添加表单
const batchAddForm = document.getElementById('batchAddForm');
batchAddForm.classList.add('hidden');
// 切换单个添加表单
addMealForm.classList.toggle('hidden');
// 如果显示表单,清空输入并聚焦
if (!addMealForm.classList.contains('hidden')) {
console.log('显示添加菜品表单');
mealName.value = '';
mealNote.value = '';
mealName.focus();
} else {
console.log('隐藏添加菜品表单');
}
}
// 添加菜品
function addMeal() {
console.log('执行添加菜品');
const name = mealName.value.trim();
const category = mealCategory.value;
const note = mealNote.value.trim();
console.log('菜品名称:', name);
console.log('分类:', category);
console.log('备注:', note);
// 验证输入
if (!name) {
console.log('错误: 菜品名称为空');
alert('请输入菜品名称');
mealName.focus();
return;
}
// 检查重复
if (mealData.some(meal => meal.name === name)) {
console.log('错误: 菜品名称已存在');
alert('菜品名称已存在');
mealName.focus();
return;
}
// 添加菜品
const newMeal = {
name,
category,
note,
count: 0
};
console.log('添加新菜品:', newMeal);
mealData.push(newMeal);
// 添加新分类
if (!categories.includes(category)) {
console.log('添加新分类:', category);
categories.push(category);
updateCategoryFilter();
}
// 更新界面
console.log('更新菜单列表');
updateMenuList();
updateWheel();
saveToLocalStorage();
// 显示成功消息
console.log('菜品添加成功');
alert(`菜品"${name}"添加成功!`);
// 隐藏表单
toggleAddForm();
}
// 编辑菜品
function editMeal(index) {
currentEditIndex = index;
const meal = filteredMeals[index];
// 填充表单
editMealName.value = meal.name;
editMealCategory.value = meal.category;
editMealNote.value = meal.note;
// 显示模态框
editModal.classList.remove('hidden');
}
// 隐藏编辑模态框
function hideEditModal() {
editModal.classList.add('hidden');
currentEditIndex = -1;
}
// 更新菜品
function updateMeal() {
if (currentEditIndex === -1) return;
const name = editMealName.value.trim();
const category = editMealCategory.value;
const note = editMealNote.value.trim();
// 验证输入
if (!name) {
alert('请输入菜品名称');
editMealName.focus();
return;
}
// 检查重复(排除当前菜品)
const originalIndex = mealData.findIndex(meal => meal === filteredMeals[currentEditIndex]);
if (mealData.some((meal, index) => meal.name === name && index !== originalIndex)) {
alert('菜品名称已存在');
editMealName.focus();
return;
}
// 更新菜品
const meal = mealData[originalIndex];
meal.name = name;
// 如果分类改变,更新分类列表
if (meal.category !== category) {
meal.category = category;
if (!categories.includes(category)) {
categories.push(category);
updateCategoryFilter();
}
}
meal.note = note;
// 更新界面
updateMenuList();
updateWheel();
saveToLocalStorage();
// 隐藏模态框
hideEditModal();
}
// 删除菜品
function deleteMeal(index) {
const meal = filteredMeals[index];
if (confirm(`确定要删除菜品"${meal.name}"吗?`)) {
// 找到原始索引
const originalIndex = mealData.findIndex(m => m === meal);
// 删除菜品
mealData.splice(originalIndex, 1);
// 更新界面
updateMenuList();
updateCategoryFilter();
updateWheel();
saveToLocalStorage();
}
}
// 减少抽取数量
function decreaseDrawCount() {
const count = parseInt(drawCount.value);
if (count > 1) {
drawCount.value = count - 1;
}
}
// 增加抽取数量
function increaseDrawCount() {
const count = parseInt(drawCount.value);
if (count < mealData.length) {
drawCount.value = count + 1;
}
}
// 抽取菜品
function drawMeals(smart = false) {
// 检查是否有菜品
if (mealData.length === 0) {
alert('请先导入菜单或添加菜品');
return;
}
const count = parseInt(drawCount.value);
// 检查抽取数量
if (count > mealData.length) {
alert(`菜单中只有 ${mealData.length} 个菜品,无法抽取 ${count} 个`);
drawCount.value = mealData.length;
return;
}
// 显示转盘动画
showWheelAnimation();
// 延迟显示结果,模拟转盘旋转
setTimeout(() => {
// 抽取菜品
let selectedMeals;
if (smart) {
// 智能推荐:优先选择抽取次数少的菜品
selectedMeals = smartDraw(count);
} else {
// 随机抽取
selectedMeals = randomDraw(count);
}
// 更新抽取次数
selectedMeals.forEach(meal => {
meal.count++;
});
// 显示结果
showResult(selectedMeals);
// 保存历史记录
saveDrawHistory(selectedMeals);
// 保存到本地存储
saveToLocalStorage();
}, 3000);
}
// 随机抽取
function randomDraw(count) {
const result = [];
const availableMeals = [...mealData];
for (let i = 0; i < count; i++) {
if (availableMeals.length === 0) break;
const randomIndex = Math.floor(Math.random() * availableMeals.length);
result.push(availableMeals[randomIndex]);
availableMeals.splice(randomIndex, 1);
}
return result;
}
// 智能推荐
function smartDraw(count) {
// 按抽取次数排序
const sortedMeals = [...mealData].sort((a, b) => a.count - b.count);
// 选择抽取次数最少的菜品
return sortedMeals.slice(0, count);
}
// 显示转盘动画
function showWheelAnimation() {
// 如果没有菜品,显示占位图
if (mealData.length === 0) {
wheelPlaceholder.classList.remove('hidden');
return;
}
// 隐藏占位图
wheelPlaceholder.classList.add('hidden');
// 创建转盘分区
createWheelSegments();
// 添加动画类
wheel.classList.add('spin-wheel-animation');
// 动画结束后移除类
setTimeout(() => {
wheel.classList.remove('spin-wheel-animation');
}, 3000);
}
// 创建转盘分区
function createWheelSegments() {
// 清空转盘
wheel.innerHTML = '';
// 如果没有菜品,返回
if (mealData.length === 0) return;
// 计算每个分区的角度
const angle = 360 / mealData.length;
// 创建分区
mealData.forEach((meal, index) => {
const segment = document.createElement('div');
segment.className = 'absolute';
segment.style.width = '50%';
segment.style.height = '50%';
segment.style.transformOrigin = '100% 100%';
segment.style.transform = `rotate(${index * angle}deg)`;
segment.style.backgroundColor = getSegmentColor(index);
segment.style.display = 'flex';
segment.style.alignItems = 'center';
segment.style.justifyContent = 'center';
segment.style.padding = '10px';
segment.style.boxSizing = 'border-box';
// 创建菜品名称
const name = document.createElement('div');
name.className = 'text-white font-bold text-center';
name.style.transform = 'rotate(45deg)';
name.style.width = '100px';
name.style.overflow = 'hidden';
name.style.textOverflow = 'ellipsis';
name.style.whiteSpace = 'nowrap';
name.textContent = meal.name;
segment.appendChild(name);
wheel.appendChild(segment);
});
}
// 获取分区颜色
function getSegmentColor(index) {
const colors = [
'#FF6B6B', '#4ECDC4', '#FFE66D', '#1A535C', '#FF9F1C',
'#7B287D', '#00B4D8', '#0077B6', '#2EC4B6', '#E76F51'
];
return colors[index % colors.length];
}
// 显示结果
function showResult(selectedMeals) {
// 隐藏无结果提示
noResultMsg.classList.add('hidden');
// 显示结果列表
resultList.classList.remove('hidden');
// 清空结果列表
resultList.innerHTML = '';
// 添加结果卡片
selectedMeals.forEach((meal, index) => {
const card = document.createElement('div');
card.className = 'bg-white rounded-lg shadow-sm p-4 mb-3 border-l-4 border-primary';
const categoryColor = getCategoryColor(meal.category);
card.innerHTML = `
<div class="flex justify-between items-start">
<div>
<div class="flex items-center">
<span class="text-lg font-bold mr-2">${index + 1}. ${meal.name}</span>
<span class="badge ${categoryColor}">${meal.category}</span>
</div>
${meal.note ? `<p class="text-gray-600 text-sm mt-1">${meal.note}</p>` : ''}
</div>
<span class="text-gray-500 text-sm">已抽取 ${meal.count} 次</span>
</div>
`;
resultList.appendChild(card);
});
// 显示保存和分享按钮
saveResultBtn.classList.remove('hidden');
shareResultBtn.classList.remove('hidden');
}
// 保存结果
function saveResult() {
// 创建日期字符串
const date = new Date();
const dateStr = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
// 获取当前结果
const resultItems = resultList.querySelectorAll('.bg-white');
let resultText = '菜品名称,分类,备注\n';
resultItems.forEach(item => {
const name = item.querySelector('.font-bold').textContent.split('. ')[1];
const category = item.querySelector('.badge').textContent;
const note = item.querySelector('.text-gray-600')?.textContent || '';
// 转义CSV特殊字符
const escapeCSV = (str) => `"${str.replace(/"/g, '""')}"`;
resultText += `${escapeCSV(name)},${escapeCSV(category)},${escapeCSV(note)}\n`;
});
// 创建Blob对象
const blob = new Blob([resultText], { type: 'text/csv;charset=utf-8;' });
// 创建下载链接
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `抽取结果_${dateStr}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// 显示分享模态框
function showShareModal() {
// 获取当前结果
const resultItems = resultList.querySelectorAll('.bg-white');
let shareContent = '今天吃什么 - 抽取结果\n\n';
resultItems.forEach((item, index) => {
const name = item.querySelector('.font-bold').textContent;
const category = item.querySelector('.badge').textContent;
shareContent += `${name} [${category}]\n`;
});
// 添加日期
const date = new Date();
shareContent += `\n抽取时间: ${date.toLocaleString('zh-CN')}`;
// 设置分享文本
shareText.value = shareContent;
// 显示模态框
shareModal.classList.remove('hidden');
}
// 隐藏分享模态框
function hideShareModal() {
shareModal.classList.add('hidden');
}
// 复制分享文本
function copyShareText() {
shareText.select();
document.execCommand('copy');
// 显示提示
alert('文本已复制到剪贴板');
}
// 分享到微信
function shareToWechat() {
alert('请手动复制文本,然后粘贴到微信中分享');
}
// 保存抽取历史
function saveDrawHistory(selectedMeals) {
// 创建历史记录
const history = {
date: new Date(),
meals: selectedMeals.map(meal => ({
name: meal.name,
category: meal.category
}))
};
// 添加到历史记录
drawHistory.unshift(history);
// 限制历史记录数量
if (drawHistory.length > 10) {
drawHistory.pop();
}
// 更新历史记录列表
updateHistoryList();
}
// 更新历史记录列表
function updateHistoryList() {
// 如果没有历史记录,显示提示
if (drawHistory.length === 0) {
historyList.innerHTML = `
<div class="text-center py-3 text-gray-500 text-sm">
暂无历史记录
</div>
`;
return;
}
// 清空历史记录列表
historyList.innerHTML = '';
// 添加历史记录
drawHistory.forEach((history, index) => {
const item = document.createElement('div');
item.className = 'bg-white rounded-lg shadow-sm p-3 mb-2';
// 格式化日期
const dateStr = history.date.toLocaleString('zh-CN');
// 创建菜品列表
const mealList = history.meals.map(meal => meal.name).join('、');
item.innerHTML = `
<div class="flex justify-between items-start">
<div>
<p class="font-medium">${mealList}</p>
<p class="text-xs text-gray-500">${dateStr}</p>
</div>
<button class="text-primary hover:text-primary-dark" onclick="loadHistory(${index})">
<i class="fa fa-refresh"></i>
</button>
</div>
`;
historyList.appendChild(item);
});
}
// 加载历史记录
function loadHistory(index) {
const history = drawHistory[index];
// 查找菜品
const selectedMeals = history.meals.map(meal => {
return mealData.find(m => m.name === meal.name) || meal;
});
// 显示结果
showResult(selectedMeals);
}
// 显示帮助模态框
function showHelpModal() {
helpModal.classList.remove('hidden');
}
// 隐藏帮助模态框
function hideHelpModal() {
helpModal.classList.add('hidden');
}
// 保存到本地存储
function saveToLocalStorage() {
localStorage.setItem('mealData', JSON.stringify(mealData));
localStorage.setItem('categories', JSON.stringify(categories));
localStorage.setItem('drawHistory', JSON.stringify(drawHistory));
}
// 从本地存储加载
function loadFromLocalStorage() {
const savedMealData = localStorage.getItem('mealData');
const savedCategories = localStorage.getItem('categories');
const savedDrawHistory = localStorage.getItem('drawHistory');
if (savedMealData) {
mealData = JSON.parse(savedMealData);
}
if (savedCategories) {
categories = JSON.parse(savedCategories);
}
if (savedDrawHistory) {
drawHistory = JSON.parse(savedDrawHistory);
// 转换日期字符串为 Date 对象
drawHistory.forEach(history => {
history.date = new Date(history.date);
});
}
}
// 从本地JSON文件加载菜品数据返回Promise
async function loadMealsFromJsonFile() {
console.log('尝试从本地JSON文件加载菜品数据');
try {
// 首先尝试加载menu.json
const response = await fetch('menu.json', {
method: 'GET',
cache: 'no-cache'
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const jsonData = await response.json();
console.log('成功读取menu.json文件');
// 处理JSON数据
const addedCount = processJsonData(jsonData);
// 显示成功提示
if (addedCount > 0) {
jsonLoadMessage.textContent = `已从menu.json加载 ${addedCount} 个菜品`;
jsonLoadStatus.classList.remove('hidden');
}
return true;
} catch (error) {
console.log('无法加载menu.json文件:', error.message);
// 尝试加载其他可能的JSON文件名
const alternativeFiles = ['meals.json', 'dishes.json', 'food.json'];
for (const filename of alternativeFiles) {
try {
console.log(`尝试加载${filename}`);
const response = await fetch(filename, {
method: 'GET',
cache: 'no-cache'
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const jsonData = await response.json();
console.log(`成功读取${filename}文件`);
// 处理JSON数据
const addedCount = processJsonData(jsonData);
// 显示成功提示
if (addedCount > 0) {
jsonLoadMessage.textContent = `已从${filename}加载 ${addedCount} 个菜品`;
jsonLoadStatus.classList.remove('hidden');
}
return true;
} catch (altError) {
console.log(`加载${filename}失败:`, altError.message);
continue;
}
}
// 如果所有文件都加载失败,抛出错误
throw new Error('未找到可用的JSON菜单文件');
}
}
// 处理JSON数据返回添加的菜品数量
function processJsonData(jsonData) {
console.log('处理JSON数据');
// 验证JSON格式
if (!Array.isArray(jsonData)) {
console.log('错误: JSON格式不正确应为数组');
alert('JSON文件格式错误应为数组格式');
return 0;
}
// 处理导入的菜品数据
let addedCount = 0;
let duplicateCount = 0;
jsonData.forEach(item => {
// 支持不同的JSON格式
let name, category, note;
if (typeof item === 'string') {
// 简单格式: ["菜品1", "菜品2", ...]
name = item.trim();
category = '其他'; // 默认分类
note = '';
} else if (typeof item === 'object' && item !== null) {
// 对象格式: [{name: "菜品1", category: "分类", note: "备注"}, ...]
name = item.name || item.title || item.dish || item.food || '';
name = name ? name.trim() : '';
category = item.category || item.type || item.kind || '其他';
note = item.note || item.remark || item.description || '';
}
// 验证菜品名称
if (!name) {
console.log('跳过无效菜品:', item);
return;
}
// 检查重复
if (mealData.some(meal => meal.name === name)) {
console.log('跳过重复菜品:', name);
duplicateCount++;
return;
}
// 添加菜品
mealData.push({
name,
category,
note,
count: 0
});
// 添加新分类
if (!categories.includes(category)) {
categories.push(category);
}
addedCount++;
});
console.log(`处理完成: 添加${addedCount}个菜品, 跳过${duplicateCount}个重复菜品`);
// 如果有新菜品添加,保存到本地存储
if (addedCount > 0) {
saveToLocalStorage();
}
return addedCount;
}
// 处理JSON文件导入
function handleJsonFileImport(event) {
console.log('处理JSON文件导入');
const file = event.target.files[0];
if (!file) {
console.log('未选择文件');
return;
}
if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
console.log('错误: 不是JSON文件');
alert('请选择JSON格式的文件');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
try {
console.log('读取文件完成');
const jsonData = JSON.parse(e.target.result);
const addedCount = processJsonData(jsonData);
// 更新界面
updateCategoryFilter();
updateMenuList();
updateWheel();
// 显示成功消息
alert(`JSON文件导入成功共添加 ${addedCount} 个新菜品`);
} catch (error) {
console.error('解析JSON文件出错:', error);
alert('解析JSON文件出错请检查文件格式');
}
};
reader.onerror = function() {
console.error('读取文件出错');
alert('读取文件出错,请重试');
};
reader.readAsText(file, 'UTF-8');
// 重置文件输入,允许重新选择相同文件
event.target.value = '';
}
// 更新转盘(空实现,确保函数存在)
function updateWheel() {
// 如果有菜品,创建转盘分区
if (mealData.length > 0) {
wheelPlaceholder.classList.add('hidden');
createWheelSegments();
} else {
wheelPlaceholder.classList.remove('hidden');
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>