// API 配置 const API_BASE_URL = 'http://localhost:8086/api'; // 支付二维码图片配置(方便更换) // 请将图片复制到 oederFoodVue/images/ 文件夹,然后使用相对路径 const PAYMENT_QRCODE_IMAGE = 'images/payment-qrcode.jpg'; // 应用状态 const state = { cart: {}, // { dishId: count } 雪花ID(字符串/数字)唯一键,彻底解决选品关联问题 currentCategory: null, selectedDish: null, categories: [], dishes: [] }; // 工具函数:价格格式化(非数字兜底为0,避免NaN) function formatPrice(price) { const num = parseFloat(price); return isNaN(num) ? 0 : num; } // API 调用函数 - 获取分类 async function fetchCategories() { try { const response = await fetch(`${API_BASE_URL}/categories`); const result = await response.json(); if (result.code === 200) { state.categories = result.data || []; if (state.categories.length > 0 && !state.currentCategory) { state.currentCategory = parseInt(state.categories[0].id); } } else { console.error('获取分类失败:', result.message); alert('获取分类失败: ' + result.message); } } catch (error) { console.error('获取分类出错:', error); alert('获取分类出错,请检查后端服务是否启动'); } } // API 调用函数 - 获取菜品 async function fetchDishes() { try { const response = await fetch(`${API_BASE_URL}/dishes`); const result = await response.json(); if (result.code === 200) { state.dishes = result.data || []; } else { console.error('获取菜品失败:', result.message); alert('获取菜品失败: ' + result.message); } } catch (error) { console.error('获取菜品出错:', error); alert('获取菜品出错,请检查后端服务是否启动'); } } // 初始化 async function init() { // 加载提示 document.getElementById('dishContainer').innerHTML = '
加载中...
'; // 并行请求数据,提升速度 await Promise.all([fetchCategories(), fetchDishes()]); // 渲染页面 renderCategories(); renderDishes(); bindEvents(); } // 渲染分类列表 function renderCategories() { const categoryList = document.getElementById('categoryList'); if (state.categories.length === 0) { categoryList.innerHTML = '
暂无分类
'; return; } categoryList.innerHTML = state.categories.map(cat => { const catId = cat.id; // 后端返回的字符串ID const currentId = state.currentCategory; // 直接用字符串ID,不转数字 return `
${cat.categoryName}
`}).join(''); } // 渲染菜品列表 - 全程字符串ID,无任何parseInt,彻底解决精度丢失/ID不匹配 function renderDishes() { const dishContainer = document.getElementById('dishContainer'); if (state.dishes.length === 0) { dishContainer.innerHTML = '
暂无菜品
'; return; } // 过滤有菜品的分类:字符串ID严格相等对比,无任何转换 const categoriesWithDishes = state.categories.filter(cat => state.dishes.some(dish => dish.categoryId === cat.id) ); if (categoriesWithDishes.length === 0) { dishContainer.innerHTML = '
暂无菜品
'; return; } // 按分类渲染菜品,全程字符串ID,data-id唯一绑定 dishContainer.innerHTML = categoriesWithDishes.map(category => { const categoryId = category.id; // 纯字符串分类ID,不做任何转换 // 过滤当前分类的菜品:字符串categoryId严格相等对比 const dishes = state.dishes.filter(dish => dish.categoryId === categoryId); return `

${category.categoryName}

${dishes.map(dish => { const imgUrl = dish.coverImg || 'https://picsum.photos/100/100?text=菜品图片'; const description = dish.description || '暂无描述'; const tasteFeature = dish.tasteFeature || '美味'; const adaptScene = dish.adaptScene || '推荐'; const sales = dish.sales || 0; const dishId = dish.id; // 保留雪花ID原始字符串值,不做任何转换 const inCart = state.cart[dishId]; // 仅匹配当前dishId的购物车状态 return `
${dish.dishName}
${dish.dishName}
${description}
${tasteFeature} ${adaptScene}
`}).join('')}
`; }).join(''); } // 更新购物车 - 核心:按dishId唯一计算,不混淆 function updateCart() { // 计算总数量(仅累加当前dishId的数量) const totalCount = Object.values(state.cart).reduce((sum, count) => sum + (Number(count) || 0), 0); // 计算总价(按dishId匹配菜品,精准计算) const totalPrice = Object.entries(state.cart).reduce((sum, [dishId, count]) => { const dish = state.dishes.find(d => d.id === dishId); // 精准匹配dishId return sum + (dish ? formatPrice(dish.price) * (Number(count) || 0) : 0); }, 0); // 更新购物车角标 const cartCountEl = document.getElementById('cartCount'); const cartIconEl = document.querySelector('.cart-icon'); cartCountEl.textContent = totalCount; totalCount > 0 ? (cartCountEl.classList.add('show'), cartIconEl.classList.add('has-items')) : (cartCountEl.classList.remove('show'), cartIconEl.classList.remove('has-items')); // 更新总价和结算按钮 document.getElementById('totalPrice').textContent = `¥${totalPrice.toFixed(2)}`; const checkoutBtn = document.getElementById('checkoutBtn'); // 取消起送门槛,只要有商品就可以结算 if (totalCount > 0) { checkoutBtn.disabled = false; checkoutBtn.textContent = `去结算(总计¥${totalPrice.toFixed(2)})`; } else { checkoutBtn.disabled = true; checkoutBtn.textContent = `去结算`; } // 局部更新,不重渲染整个列表(性能优化+避免选品关联) renderCartDetail(); updateDishCartStatus(); } // 局部更新菜品购物车状态 - 核心:按dishId精准更新单个菜品,不影响其他 function updateDishCartStatus() { document.querySelectorAll('.dish-item').forEach(dishItem => { const dishId = dishItem.dataset.id; // 单独获取每个菜品的dishId const inCart = state.cart[dishId]; // 仅判断当前dishId的购物车状态 const controlsDiv = dishItem.querySelector('.dish-controls'); // 单独更新当前菜品的选中样式 inCart ? dishItem.classList.add('in-cart') : dishItem.classList.remove('in-cart'); // 单独更新当前菜品的加减按钮 if (inCart) { controlsDiv.innerHTML = ` ${inCart} `; } else { controlsDiv.innerHTML = ``; } }); } // 渲染购物车详情 - 按dishId精准展示 function renderCartDetail() { const cartDetailList = document.getElementById('cartDetailList'); const cartItems = Object.entries(state.cart).map(([dishId, count]) => { const dish = state.dishes.find(d => d.id === dishId); return dish ? { ...dish, count } : null; }).filter(Boolean); if (cartItems.length === 0) { cartDetailList.innerHTML = '
购物车是空的,快来添加菜品吧~
'; return; } cartDetailList.innerHTML = cartItems.map(item => `
${item.dishName} ¥${formatPrice(item.price).toFixed(2)}
${item.count}
`).join(''); } // 更新菜品数量 - 核心:dishId全程唯一,操作仅影响当前菜品 function updateDishCount(dishId, action) { console.log('=== updateDishCount ==='); console.log('dishId:', dishId, 'type:', typeof dishId); console.log('action:', action); console.log('购物车 before:', JSON.stringify(state.cart)); if (action === 'plus') { state.cart[dishId] = (state.cart[dishId] || 0) + 1; // 首次加购提示 if (Object.keys(state.cart).length === 1 && state.cart[dishId] === 1) showCartTip(); } else if (action === 'minus') { state.cart[dishId] > 1 ? state.cart[dishId]-- : delete state.cart[dishId]; } console.log('购物车 after:', JSON.stringify(state.cart)); console.log('购物车键:', Object.keys(state.cart)); console.log('购物车值:', Object.values(state.cart)); updateCart(); // 仅更新当前菜品相关状态 } // 购物车加购提示 function showCartTip() { const tip = document.getElementById('cartTip'); if (tip) { tip.classList.add('show'); setTimeout(() => tip.classList.remove('show'), 3000); } } // 显示菜品详情 - 按dishId精准匹配 function showDishDetail(dishId) { const dish = state.dishes.find(d => d.id === dishId); if (!dish) return; state.selectedDish = dish; const imgUrl = dish.coverImg || 'https://picsum.photos/400/400?text=菜品详情'; document.getElementById('dishDetailImg').src = imgUrl; document.getElementById('dishDetailImg').onerror = () => this.src = 'https://picsum.photos/400/400?text=菜品详情'; document.getElementById('dishDetailName').textContent = dish.dishName; document.getElementById('dishDetailDesc').textContent = dish.description || '暂无描述'; document.getElementById('dishDetailPrice').textContent = formatPrice(dish.price).toFixed(2); document.getElementById('dishDetailTags').innerHTML = ` ${dish.tasteFeature || '美味'} ${dish.adaptScene || '推荐'} 月售${dish.sales || 0} `; document.getElementById('dishDetailMask').classList.add('show'); document.getElementById('dishDetail').classList.add('show'); } // 隐藏菜品详情 function hideDishDetail() { document.getElementById('dishDetailMask').classList.remove('show'); document.getElementById('dishDetail').classList.remove('show'); } // 切换购物车详情 function toggleCartDetail() { console.log('=== toggleCartDetail ==='); console.log('state.cart:', state.cart); console.log('购物车键:', Object.keys(state.cart)); console.log('购物车值:', Object.values(state.cart)); const totalCount = Object.values(state.cart).reduce((sum, count) => sum + (Number(count) || 0), 0); console.log('总数量:', totalCount); if (totalCount === 0) { alert('购物车为空,无法打开~'); return; } const mask = document.getElementById('cartMask'); const detail = document.getElementById('cartDetail'); mask.classList.contains('show') ? (mask.classList.remove('show'), detail.classList.remove('show')) : (mask.classList.add('show'), detail.classList.add('show')); } // 清空购物车 function clearCart() { if (confirm('确定要清空购物车吗?清空后将恢复为空~')) { state.cart = {}; updateCart(); toggleCartDetail(); } } // 结算功能 - 先显示二维码,支付成功后创建订单 async function checkout() { const totalPrice = Object.entries(state.cart).reduce((sum, [dishId, count]) => { const dish = state.dishes.find(d => d.id === dishId); return sum + (dish ? formatPrice(dish.price) * (Number(count) || 0) : 0); }, 0); // 取消起送门槛检查,只要有商品就可以结算 if (totalPrice <= 0) { alert('购物车是空的,请先添加商品~'); return; } // 准备订单数据(暂存,支付成功后再提交) const orderData = { items: Object.entries(state.cart).map(([dishId, quantity]) => ({ dishId: dishId, quantity: quantity })), totalAmount: totalPrice, deliveryFee: 0, finalAmount: totalPrice, remark: '' }; // 直接显示支付二维码 showPaymentQRCode(orderData); } // 显示支付二维码 function showPaymentQRCode(orderData) { // 生成临时支付单号用于验证 const paymentId = 'PAY' + Date.now(); // 显示订单信息 document.getElementById('payOrderNumber').textContent = paymentId; document.getElementById('payAmount').textContent = `¥${orderData.finalAmount.toFixed(2)}`; // 设置二维码图片 const qrcodeImage = document.getElementById('qrcodeImage'); qrcodeImage.src = PAYMENT_QRCODE_IMAGE; qrcodeImage.onerror = function() { this.src = 'https://via.placeholder.com/200x200/4caf50/ffffff?text=请添加支付二维码'; }; // 显示支付弹窗 document.getElementById('paymentMask').classList.add('show'); document.getElementById('paymentModal').classList.add('show'); // 开始轮询支付状态(传入支付单号和订单数据) startPaymentPolling(paymentId, orderData); } // 轮询支付状态 let paymentPollingTimer = null; let pollingCount = 0; const MAX_POLLING_COUNT = 60; // 最多轮询60次(2分钟) function startPaymentPolling(paymentId, orderData) { if (paymentPollingTimer) clearInterval(paymentPollingTimer); pollingCount = 0; // 每2秒轮询一次支付状态 paymentPollingTimer = setInterval(async () => { pollingCount++; // 超时处理 if (pollingCount > MAX_POLLING_COUNT) { stopPaymentPolling(); showPaymentFailure('支付超时,请重新下单'); return; } try { // 调用后端验证支付状态 const response = await fetch(`${API_BASE_URL}/payment/verify?paymentId=${paymentId}`); const result = await response.json(); if (result.code === 200 && result.data.paid) { // 支付成功,停止轮询 stopPaymentPolling(); // 创建订单 await createOrderAfterPayment(orderData); } else if (result.code === 200 && result.data.failed) { // 支付失败 stopPaymentPolling(); showPaymentFailure('支付失败,请重试'); } // 如果是待支付状态,继续轮询 } catch (error) { console.error('验证支付状态失败:', error); } }, 2000); } // 支付成功后创建订单 async function createOrderAfterPayment(orderData) { try { const response = await fetch(`${API_BASE_URL}/orders`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) }); const result = await response.json(); if (result.code === 200) { showPaymentSuccess(); } else { showPaymentFailure('订单创建失败: ' + result.message); } } catch (error) { console.error('创建订单失败:', error); showPaymentFailure('订单创建失败,请联系客服'); } } function stopPaymentPolling() { if (paymentPollingTimer) { clearInterval(paymentPollingTimer); paymentPollingTimer = null; } pollingCount = 0; } // 显示支付失败 function showPaymentFailure(message) { document.getElementById('paymentStatus').innerHTML = `

${message}

`; setTimeout(() => { closePaymentModal(); }, 3000); } function showPaymentSuccess() { document.getElementById('paymentStatus').innerHTML = `

支付成功!

`; setTimeout(() => { closePaymentModal(); state.cart = {}; updateCart(); const mask = document.getElementById('cartMask'); const detail = document.getElementById('cartDetail'); mask.classList.remove('show'); detail.classList.remove('show'); alert('支付成功!我们将尽快为您配送~'); }, 2000); } function closePaymentModal() { stopPaymentPolling(); document.getElementById('paymentMask').classList.remove('show'); document.getElementById('paymentModal').classList.remove('show'); document.getElementById('paymentStatus').innerHTML = `

等待支付中...

`; } // 事件绑定 - 核心:事件委托精准获取当前dishId,不混淆 function bindEvents() { // 分类切换 document.getElementById('categoryList').addEventListener('click', (e) => { const item = e.target.closest('.category-item'); if (item) { const categoryId = item.dataset.id; state.currentCategory = categoryId; console.log('=== 分类切换 ==='); console.log('目标分类 ID:', categoryId); renderCategories(); // 【补充】分类切换时建议重新渲染菜品(确保菜品区域和分类同步) renderDishes(); // 使用 requestAnimationFrame 确保 DOM 完全更新(双重帧足够等待渲染) requestAnimationFrame(() => { requestAnimationFrame(() => { // 错误1修复:拼接正确的分类区域ID → category-xxx const section = document.getElementById(`category-${categoryId}`); // 错误2修复:修正滚动容器类名 → .dish-container(和渲染一致) const container = document.querySelector('.dish-container'); console.log('分类区域元素:', section); console.log('滚动容器:', container); if (section && container) { section.scrollIntoView({ behavior: 'smooth', block: 'start' // 贴顶显示,体验更好 }); console.log('滚动成功:已定位到分类 →', `category-${categoryId}`); } else { // 分情况提示错误,方便排查 if (!section) { console.warn('滚动失败:未找到分类区域元素,可能该分类无菜品', `ID: category-${categoryId}`); } if (!container) { console.error('滚动失败:未找到菜品滚动容器,请检查类名是否为 .dish-container'); } } }); }); } }); // 以下是你原有的其他事件逻辑(菜品操作、购物车、详情等),保留不变即可 // 菜品操作 - 精准获取当前点击按钮的dishId document.getElementById('dishContainer').addEventListener('click', (e) => { const btn = e.target.closest('.control-btn'); if (btn) { e.stopPropagation(); const dishId = btn.dataset.id; const action = btn.dataset.action; updateDishCount(dishId, action); return; } // 菜品详情 const dishItem = e.target.closest('.dish-item'); dishItem && showDishDetail(dishItem.dataset.id); }); // 购物车图标 document.getElementById('cartIcon').addEventListener('click', toggleCartDetail); // 购物车遮罩 document.getElementById('cartMask').addEventListener('click', toggleCartDetail); // 购物车内部操作 document.getElementById('cartDetailList').addEventListener('click', (e) => { const btn = e.target.closest('.control-btn'); btn && updateDishCount(btn.dataset.id, btn.dataset.action); }); // 清空购物车 document.getElementById('clearCart').addEventListener('click', (e) => { e.stopPropagation(); clearCart(); }); // 结算按钮 document.getElementById('checkoutBtn').addEventListener('click', (e) => { e.stopPropagation(); checkout(); }); // 菜品详情操作 document.getElementById('addToCartBtn').addEventListener('click', () => { state.selectedDish && (updateDishCount(state.selectedDish.id, 'plus'), hideDishDetail()); }); document.getElementById('closeDetail').addEventListener('click', hideDishDetail); document.getElementById('dishDetailMask').addEventListener('click', hideDishDetail); document.getElementById('dishDetail').addEventListener('click', (e) => e.stopPropagation()); // 支付弹窗事件 document.getElementById('closePayment').addEventListener('click', closePaymentModal); document.getElementById('paymentMask').addEventListener('click', closePaymentModal); document.getElementById('paymentModal').addEventListener('click', (e) => e.stopPropagation()); } // 页面加载完成初始化 document.addEventListener('DOMContentLoaded', init);