|
|
@@ -1,7 +1,7 @@
|
|
|
<template>
|
|
|
<!-- 点餐页 -->
|
|
|
<view class="content">
|
|
|
- <view class="top-info">桌号:A08 就餐人数:{{ currentDiners }}人</view>
|
|
|
+ <view class="top-info">桌号:{{ tableId || '—' }} 就餐人数:{{ currentDiners }}人</view>
|
|
|
<!-- 搜索 -->
|
|
|
<input type="text" placeholder="请输入菜品名称" class="search-input" />
|
|
|
|
|
|
@@ -11,7 +11,7 @@
|
|
|
<scroll-view class="category-list" scroll-y>
|
|
|
<view v-for="(category, index) in categories" :key="index" class="category-item"
|
|
|
:class="{ active: currentCategoryIndex === index }" @click="selectCategory(index)">
|
|
|
- {{ category.name }}
|
|
|
+ {{ category.categoryName }}
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
|
|
|
@@ -50,8 +50,10 @@ import CouponModal from "./components/CouponModal.vue";
|
|
|
import CartModal from "./components/CartModal.vue";
|
|
|
import SelectCouponModal from "./components/SelectCouponModal.vue";
|
|
|
import { go } from "@/utils/utils.js";
|
|
|
+import { DiningOrderFood, GetStoreCategories, GetStoreCuisines } from "@/api/dining.js";
|
|
|
|
|
|
-const currentDiners = ref(uni.getStorageSync('currentDiners'));
|
|
|
+const tableId = ref(''); // 桌号,来自上一页 url 参数 tableid
|
|
|
+const currentDiners = ref(uni.getStorageSync('currentDiners') || '');
|
|
|
const currentCategoryIndex = ref(0);
|
|
|
const couponModalOpen = ref(false);
|
|
|
const cartModalOpen = ref(false);
|
|
|
@@ -59,86 +61,30 @@ const selectCouponModalOpen = ref(false);
|
|
|
const discountAmount = ref(12); // 优惠金额,示例数据
|
|
|
const selectedCouponId = ref(null); // 选中的优惠券ID
|
|
|
|
|
|
-// 分类列表
|
|
|
-const categories = ref([
|
|
|
- { name: '推荐菜品', id: 'recommend' },
|
|
|
- { name: '招牌川菜', id: 'signature' },
|
|
|
- { name: '凉菜', id: 'cold' },
|
|
|
- { name: '热菜', id: 'hot' },
|
|
|
- { name: '汤品', id: 'soup' },
|
|
|
- { name: '主食', id: 'staple' },
|
|
|
- { name: '饮品', id: 'drink' }
|
|
|
-]);
|
|
|
+// 分类列表(由接口 /store/info/categories 返回后赋值)
|
|
|
+const categories = ref([]);
|
|
|
|
|
|
-// 菜品列表数据(示例数据)
|
|
|
-const foodList = ref([
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- name: '炭烤牛排',
|
|
|
- price: 26,
|
|
|
- desc: '正宗川味, 麻辣鲜香, 豆腐滑嫩',
|
|
|
- image: '/static/demo.png',
|
|
|
- tags: [
|
|
|
- { text: '招牌', type: 'signature' },
|
|
|
- { text: '中辣', type: 'spicy' }
|
|
|
- ],
|
|
|
- monthlySales: 160,
|
|
|
- quantity: 0,
|
|
|
- categoryId: 'recommend'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2,
|
|
|
- name: '炭烤牛排',
|
|
|
- price: 26,
|
|
|
- desc: '正宗川味, 麻辣鲜香, 豆腐滑嫩',
|
|
|
- image: '/static/demo.png',
|
|
|
- tags: [
|
|
|
- { text: '招牌', type: 'signature' },
|
|
|
- { text: '中辣', type: 'spicy' }
|
|
|
- ],
|
|
|
- monthlySales: 160,
|
|
|
- quantity: 99,
|
|
|
- categoryId: 'recommend'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- name: '炭烤牛排',
|
|
|
- price: 26,
|
|
|
- desc: '正宗川味, 麻辣鲜香, 豆腐滑嫩',
|
|
|
- image: '/static/demo.png',
|
|
|
- tags: [
|
|
|
- { text: '招牌', type: 'signature' },
|
|
|
- { text: '中辣', type: 'spicy' }
|
|
|
- ],
|
|
|
- monthlySales: 160,
|
|
|
- quantity: 23,
|
|
|
- categoryId: 'cold'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 4,
|
|
|
- name: '炭烤牛排',
|
|
|
- price: 26,
|
|
|
- desc: '正宗川味, 麻辣鲜香, 豆腐滑嫩',
|
|
|
- image: '/static/demo.png',
|
|
|
- tags: [
|
|
|
- { text: '招牌', type: 'signature' },
|
|
|
- { text: '中辣', type: 'spicy' }
|
|
|
- ],
|
|
|
- monthlySales: 160,
|
|
|
- quantity: 0,
|
|
|
- categoryId: 'cold'
|
|
|
- }
|
|
|
-]);
|
|
|
+// 菜品列表(由接口 /store/info/cuisines 按分类拉取并合并,每项含 quantity、categoryId)
|
|
|
+const foodList = ref([]);
|
|
|
|
|
|
-// 当前分类的菜品列表
|
|
|
+// 当前分类的菜品列表(按当前选中的分类 id 过滤)
|
|
|
const currentFoodList = computed(() => {
|
|
|
- const categoryId = categories.value[currentCategoryIndex.value].id;
|
|
|
- return foodList.value.filter(food => food.categoryId === categoryId);
|
|
|
+ const cat = categories.value[currentCategoryIndex.value];
|
|
|
+ if (!cat) return [];
|
|
|
+ const categoryId = cat.id ?? cat.categoryId;
|
|
|
+ return foodList.value.filter((food) => String(food?.categoryId ?? '') === String(categoryId ?? ''));
|
|
|
});
|
|
|
|
|
|
-// 购物车列表(只包含数量大于0的菜品)
|
|
|
+// 购物车列表:只包含数量>0 的菜品,并按菜品 id 去重(同一菜品只算一条,避免金额叠加)
|
|
|
const cartList = computed(() => {
|
|
|
- return foodList.value.filter(food => food.quantity > 0);
|
|
|
+ const withQty = foodList.value.filter((food) => (food.quantity || 0) > 0);
|
|
|
+ const seen = new Set();
|
|
|
+ return withQty.filter((f) => {
|
|
|
+ const id = String(f?.id ?? '');
|
|
|
+ if (seen.has(id)) return false;
|
|
|
+ seen.add(id);
|
|
|
+ return true;
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
// 优惠券列表(示例数据)
|
|
|
@@ -182,23 +128,65 @@ const availableCoupons = computed(() => {
|
|
|
return couponList.value.filter(coupon => coupon.isReceived);
|
|
|
});
|
|
|
|
|
|
-// 选择分类
|
|
|
-const selectCategory = (index) => {
|
|
|
+// 分类 id 统一转字符串,避免 number/string 比较导致误判
|
|
|
+const sameCategory = (a, b) => String(a ?? '') === String(b ?? '');
|
|
|
+
|
|
|
+// 根据分类 id 拉取菜品并合并到 foodList,切换分类时保留其他分类已选菜品及本分类已选数量
|
|
|
+const fetchCuisinesByCategoryId = async (categoryId) => {
|
|
|
+ if (!categoryId) return;
|
|
|
+ try {
|
|
|
+ const res = await GetStoreCuisines({ categoryId });
|
|
|
+ const list = res?.list ?? res?.data ?? (Array.isArray(res) ? res : []);
|
|
|
+ const normalized = (Array.isArray(list) ? list : []).map(item => ({
|
|
|
+ ...item,
|
|
|
+ quantity: item.quantity ?? 0,
|
|
|
+ categoryId: item.categoryId ?? categoryId
|
|
|
+ }));
|
|
|
+ // 其他分类的菜品原样保留(含已选数量)
|
|
|
+ const rest = foodList.value.filter(f => !sameCategory(f.categoryId, categoryId));
|
|
|
+ // 本分类:按菜品 id 从整份列表里取已选数量,id 一致则自动带上数量
|
|
|
+ const merged = normalized.map((newItem) => {
|
|
|
+ const existing = foodList.value.find(
|
|
|
+ (e) => String(e?.id ?? '') === String(newItem?.id ?? '')
|
|
|
+ );
|
|
|
+ const quantity = existing ? existing.quantity : (newItem.quantity ?? 0);
|
|
|
+ return { ...newItem, quantity };
|
|
|
+ });
|
|
|
+ foodList.value = [...rest, ...merged];
|
|
|
+ } catch (err) {
|
|
|
+ console.error('获取菜品失败:', err);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 选择分类:切换高亮并拉取该分类菜品
|
|
|
+const selectCategory = async (index) => {
|
|
|
currentCategoryIndex.value = index;
|
|
|
+ const cat = categories.value[index];
|
|
|
+ if (!cat) return;
|
|
|
+ const categoryId = cat.id ?? cat.categoryId;
|
|
|
+ await fetchCuisinesByCategoryId(categoryId);
|
|
|
+};
|
|
|
+
|
|
|
+// 更新菜品数量:菜品 id 一致则全部同步为同一数量,触发响应式使底部金额重新计算
|
|
|
+const updateFoodQuantity = (food, delta) => {
|
|
|
+ if (!food) return;
|
|
|
+ const id = food.id;
|
|
|
+ const nextQty = Math.max(0, (food.quantity || 0) + delta);
|
|
|
+ const sameId = (item) =>
|
|
|
+ String(item?.id ?? '') === String(id ?? '');
|
|
|
+ foodList.value = foodList.value.map((item) =>
|
|
|
+ sameId(item) ? { ...item, quantity: nextQty } : item
|
|
|
+ );
|
|
|
};
|
|
|
|
|
|
// 增加数量
|
|
|
const handleIncrease = (food) => {
|
|
|
- if (food) {
|
|
|
- food.quantity = (food.quantity || 0) + 1;
|
|
|
- }
|
|
|
+ updateFoodQuantity(food, 1);
|
|
|
};
|
|
|
|
|
|
// 减少数量
|
|
|
const handleDecrease = (food) => {
|
|
|
- if (food && food.quantity > 0) {
|
|
|
- food.quantity -= 1;
|
|
|
- }
|
|
|
+ updateFoodQuantity(food, -1);
|
|
|
};
|
|
|
|
|
|
// 优惠券点击(领取优惠券弹窗)
|
|
|
@@ -306,10 +294,54 @@ const handleOrderClick = (data) => {
|
|
|
go('/pages/placeOrder/index');
|
|
|
};
|
|
|
|
|
|
-onLoad((e) => {
|
|
|
- uni.setNavigationBarTitle({
|
|
|
- title: '店铺名称'
|
|
|
- });
|
|
|
+onLoad(async (options) => {
|
|
|
+ // 获取上一页(选择就餐人数页)传来的桌号和就餐人数
|
|
|
+ const tableid = options.tableid || '';
|
|
|
+ const diners = options.diners || '';
|
|
|
+ tableId.value = tableid;
|
|
|
+ currentDiners.value = diners;
|
|
|
+ console.log('点餐页接收参数 - 桌号(tableid):', tableid, '就餐人数(diners):', diners);
|
|
|
+
|
|
|
+ // 调用点餐页接口,入参 dinerCount、tableId
|
|
|
+ if (tableid || diners) {
|
|
|
+ try {
|
|
|
+ const res = await DiningOrderFood({
|
|
|
+ tableId: tableid,
|
|
|
+ dinerCount: diners
|
|
|
+ });
|
|
|
+ console.log('点餐页接口返回:', res);
|
|
|
+
|
|
|
+ // 成功后调接口获取菜品种类(storeId 取自点餐页接口返回,若无则需从别处获取)
|
|
|
+ const storeId = res?.storeId ?? res?.data?.storeId ?? '';
|
|
|
+ if (storeId) {
|
|
|
+ try {
|
|
|
+ const categoriesRes = await GetStoreCategories({ storeId });
|
|
|
+ const list = categoriesRes?.list ?? categoriesRes?.data ?? categoriesRes;
|
|
|
+ if (Array.isArray(list) && list.length) {
|
|
|
+ categories.value = list;
|
|
|
+ // 默认用第一项的分类 id 拉取菜品
|
|
|
+ const firstCat = list[0];
|
|
|
+ const firstCategoryId = firstCat.id ?? firstCat.categoryId;
|
|
|
+ if (firstCategoryId) {
|
|
|
+ const cuisinesRes = await GetStoreCuisines({ categoryId: firstCategoryId });
|
|
|
+ const cuisinesList = cuisinesRes?.list ?? cuisinesRes?.data ?? (Array.isArray(cuisinesRes) ? cuisinesRes : []);
|
|
|
+ foodList.value = (Array.isArray(cuisinesList) ? cuisinesList : []).map(item => ({
|
|
|
+ ...item,
|
|
|
+ quantity: item.quantity ?? 0,
|
|
|
+ categoryId: item.categoryId ?? firstCategoryId
|
|
|
+ }));
|
|
|
+ console.log('默认分类菜品:', cuisinesRes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log('菜品种类:', categoriesRes);
|
|
|
+ } catch (err) {
|
|
|
+ console.error('获取菜品种类失败:', err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('点餐页接口失败:', e);
|
|
|
+ }
|
|
|
+ }
|
|
|
});
|
|
|
</script>
|
|
|
|