|
|
@@ -3,7 +3,7 @@
|
|
|
<view class="page-wrap">
|
|
|
<view class="top-fixed">
|
|
|
<NavBar :title="navTitle" only-home />
|
|
|
- <view class="top-info">桌号:{{ displayTableNumber }} 就餐人数:{{ currentDiners }}人</view>
|
|
|
+ <view class="top-info">桌号:{{ displayTableNumber }}</view>
|
|
|
<input type="text" placeholder="请输入菜品名称" class="search-input" />
|
|
|
</view>
|
|
|
<view class="content">
|
|
|
@@ -58,7 +58,7 @@ 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, GetStoreDetail, getOrderSseConfig, GetOrderCart, PostOrderCartAdd, PostOrderCartUpdate, PostOrderCartClear, PutOrderCartUpdateTableware, GetUserOwnedCouponList } from "@/api/dining.js";
|
|
|
+import { DiningOrderFood, GetStoreCategories, GetStoreCuisines, GetStoreDetail, getOrderSseConfig, GetOrderCart, PostOrderCartAdd, PostOrderCartUpdate, PostOrderCartClear, PutOrderCartUpdateTableware, GetUserCouponList } from "@/api/dining.js";
|
|
|
import { createSSEConnection } from "@/utils/sse.js";
|
|
|
|
|
|
const storeName = ref(''); // 店铺名称,用于导航栏标题
|
|
|
@@ -82,6 +82,7 @@ const couponModalOpen = ref(false);
|
|
|
const cartModalOpen = ref(false);
|
|
|
const selectCouponModalOpen = ref(false);
|
|
|
const selectCouponViewOnly = ref(false); // true=左下角仅查看,false=购物车内可选
|
|
|
+const fromNumberOfDiners = ref(false); // 是否从选座页(就餐人数页)进入,仅此时才调用 update-tableware 接口
|
|
|
const discountAmount = ref(12); // 优惠金额,示例数据
|
|
|
const selectedCouponId = ref(null); // 选中的优惠券ID
|
|
|
|
|
|
@@ -115,13 +116,21 @@ const cartList = computed(() => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-// 用于展示的购物车列表:优先使用接口返回的 items(含 cuisineName/cuisineImage/unitPrice/subtotalAmount/remark),只展示数量>0
|
|
|
+// 用于展示的购物车列表:数量>0 的项展示;餐具(id=-1)始终展示(含数量为 0);若接口未返回餐具则补默认项
|
|
|
const displayCartList = computed(() => {
|
|
|
const apiItems = cartData.value?.items;
|
|
|
+ let base = [];
|
|
|
if (Array.isArray(apiItems) && apiItems.length > 0) {
|
|
|
- return apiItems.filter((it) => (Number(it?.quantity) || 0) > 0);
|
|
|
+ base = apiItems.filter((it) => (Number(it?.quantity) || 0) > 0 || Number(it?.cuisineId ?? it?.id) === -1);
|
|
|
+ } else {
|
|
|
+ base = cartList.value;
|
|
|
+ }
|
|
|
+ const hasTableware = base.some((it) => Number(it?.cuisineId ?? it?.id) === -1);
|
|
|
+ if (!hasTableware) {
|
|
|
+ const fee = Number(cartData.value?.tablewareFee) || 0;
|
|
|
+ return [...base, { cuisineId: -1, cuisineName: '餐具费', quantity: 0, unitPrice: fee || 0, subtotalAmount: 0 }];
|
|
|
}
|
|
|
- return cartList.value;
|
|
|
+ return base;
|
|
|
});
|
|
|
|
|
|
// 行小计:接口项用 subtotalAmount,否则 数量×单价(与 BottomActionBar/CartModal 一致)
|
|
|
@@ -155,15 +164,14 @@ const couponList = ref([]);
|
|
|
// 门店可用优惠券列表(选择优惠券弹窗用,与 couponList 同步自同一接口)
|
|
|
const storeUsableCouponList = ref([]);
|
|
|
|
|
|
-// 规范化接口优惠券项为弹窗所需格式(对接 userOwnedByStore 返回的 data 数组:id/couponId/userCouponId、name、couponType、discountRate、minimumSpendingAmount、expirationTime 等)
|
|
|
+// 规范化接口优惠券项为弹窗所需格式(对接 getUserCouponList 返回的 data:id/couponId/userCouponId、name、couponType、discountRate、minimumSpendingAmount、expirationTime 等)
|
|
|
// couponType 1=满减券显示 nominalValue 为金额,2=折扣券显示 discountRate 为折扣力度
|
|
|
function normalizeCouponItem(item) {
|
|
|
if (!item || typeof item !== 'object') return null;
|
|
|
const raw = item;
|
|
|
const couponType = Number(raw.couponType) || 0;
|
|
|
const nominalValue = Number(raw.nominalValue ?? raw.amount ?? 0) || 0;
|
|
|
- // userOwnedByStore 接口返回的 discountRate 需除以 10(如 55 表示 5.5折)
|
|
|
- const discountRate = (Number(raw.discountRate) || 0) / 10;
|
|
|
+ const discountRate = Number(raw.discountRate) || 0;
|
|
|
const minAmount = Number(raw.minimumSpendingAmount ?? raw.minAmount ?? raw.min_amount ?? 0) || 0;
|
|
|
const isReceived = raw.canReceived === false;
|
|
|
let amountDisplay = nominalValue + '元';
|
|
|
@@ -325,7 +333,7 @@ const syncCartDataFromFoodList = () => {
|
|
|
cartData.value = { ...cartData.value, items: nextItems, totalAmount, totalQuantity };
|
|
|
};
|
|
|
|
|
|
-// 判断是否为餐具(cuisineId 或 id 为 -1),餐具不可修改数量
|
|
|
+// 判断是否为餐具(cuisineId 或 id 为 -1),餐具可修改数量(含减至 0)
|
|
|
const isTableware = (item) => {
|
|
|
if (!item) return false;
|
|
|
const id = item.id ?? item.cuisineId;
|
|
|
@@ -336,10 +344,6 @@ const isTableware = (item) => {
|
|
|
// Update 接口返回 400 时不改页面数量和金额(会回滚本地状态)
|
|
|
const updateFoodQuantity = (food, delta) => {
|
|
|
if (!food) return;
|
|
|
- if (isTableware(food)) {
|
|
|
- uni.showToast({ title: '不允许更改餐具数量', icon: 'none' });
|
|
|
- return;
|
|
|
- }
|
|
|
const id = food.id ?? food.cuisineId;
|
|
|
const prevQty = food.quantity || 0;
|
|
|
const nextQty = Math.max(0, prevQty + delta);
|
|
|
@@ -383,7 +387,18 @@ const updateFoodQuantity = (food, delta) => {
|
|
|
|
|
|
if (tableId.value && delta !== 0) {
|
|
|
const needAdd = delta > 0 && (idx < 0 || nextQty === 1);
|
|
|
- if (needAdd) {
|
|
|
+ const isTablewareItem = Number(id) === -1;
|
|
|
+ if (isTablewareItem && fromNumberOfDiners.value) {
|
|
|
+ PutOrderCartUpdateTableware({
|
|
|
+ quantity: nextQty,
|
|
|
+ tableId: tableId.value
|
|
|
+ }).catch((err) => {
|
|
|
+ console.error('更新餐具数量失败:', err);
|
|
|
+ applyQuantity(prevQty);
|
|
|
+ });
|
|
|
+ } else if (isTablewareItem) {
|
|
|
+ // 非选座页进入:餐具仅本地更新,不调 update-tableware
|
|
|
+ } else if (needAdd) {
|
|
|
PostOrderCartAdd({
|
|
|
cuisineId: id,
|
|
|
quantity: nextQty,
|
|
|
@@ -417,11 +432,11 @@ const handleDecrease = (item) => {
|
|
|
if (food && (food.quantity || 0) > 0) updateFoodQuantity(food, -1);
|
|
|
};
|
|
|
|
|
|
-// 拉取用户已领/可用优惠券(GET /dining/coupon/userOwnedByStore),列表在 SelectCouponModal 中展示,空时弹窗内显示「暂无可用优惠券」
|
|
|
+// 拉取用户优惠券列表(GET /dining/coupon/getUserCouponList),列表在 SelectCouponModal 中展示,空时弹窗内显示「暂无可用优惠券」
|
|
|
const fetchUserOwnedCoupons = async () => {
|
|
|
const storeId = uni.getStorageSync('currentStoreId') || '';
|
|
|
try {
|
|
|
- const res = await GetUserOwnedCouponList({ storeId });
|
|
|
+ const res = await GetUserCouponList({ storeId });
|
|
|
// 接口返回 { code, data: [...], msg, success },请求层可能只返回 data,故 res 可能为数组
|
|
|
const list = Array.isArray(res) ? res : (res?.data ?? res?.records ?? res?.list ?? []);
|
|
|
const normalized = (Array.isArray(list) ? list : []).map((item) => normalizeCouponItem(item)).filter(Boolean);
|
|
|
@@ -519,21 +534,43 @@ const handleCartClose = () => {
|
|
|
cartModalOpen.value = false;
|
|
|
};
|
|
|
|
|
|
-// 清空购物车:调用 /store/order/cart/clear,入参 tableId,成功后清空本地并关闭弹窗
|
|
|
+// 清空购物车:调用 /store/order/cart/clear,成功后用接口返回的数据更新购物车
|
|
|
const handleCartClear = () => {
|
|
|
- const doClear = () => {
|
|
|
- pendingCartData = null;
|
|
|
- foodList.value = foodList.value.map((f) => ({ ...f, quantity: 0 }));
|
|
|
- cartData.value = { items: [], totalAmount: 0, totalQuantity: 0 };
|
|
|
+ const items = cartData.value?.items ?? [];
|
|
|
+ const dishItems = items.filter((it) => Number(it?.cuisineId ?? it?.id) !== -1);
|
|
|
+
|
|
|
+ const applyClearResult = (res) => {
|
|
|
+ const list = parseCartListFromResponse(res) ?? [];
|
|
|
+ pendingCartData = list;
|
|
|
+ const totalAmount = Number(res?.totalAmount) || 0;
|
|
|
+ const totalQuantity = Number(res?.totalQuantity) || 0;
|
|
|
+ const tablewareFee = Number(res?.tablewareFee ?? res?.tablewareAmount ?? 0) || list.reduce((s, it) => {
|
|
|
+ if (Number(it?.cuisineId ?? it?.id) !== -1) return s;
|
|
|
+ const line = it?.subtotalAmount != null ? Number(it.subtotalAmount) : (Number(it?.quantity) || 0) * (Number(it?.unitPrice ?? it?.price) || 0);
|
|
|
+ return s + line;
|
|
|
+ }, 0);
|
|
|
+ cartData.value = { items: list, totalAmount, totalQuantity, tablewareFee };
|
|
|
+ mergeCartIntoFoodList();
|
|
|
cartModalOpen.value = false;
|
|
|
uni.showToast({ title: '已清空购物车', icon: 'success' });
|
|
|
};
|
|
|
+
|
|
|
if (!tableId.value) {
|
|
|
- doClear();
|
|
|
+ const tablewareItems = items.filter((it) => Number(it?.cuisineId ?? it?.id) === -1);
|
|
|
+ const utensilFee = tablewareItems.reduce((sum, it) => {
|
|
|
+ const line = it?.subtotalAmount != null ? Number(it.subtotalAmount) : (Number(it?.quantity) || 0) * (Number(it?.unitPrice ?? it?.price) || 0);
|
|
|
+ return sum + line;
|
|
|
+ }, 0);
|
|
|
+ const utensilQty = tablewareItems.reduce((s, i) => s + (Number(i?.quantity) || 0), 0);
|
|
|
+ applyClearResult({ items: tablewareItems, totalAmount: utensilFee, totalQuantity: utensilQty, tablewareFee: utensilFee });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (dishItems.length === 0) {
|
|
|
+ applyClearResult(cartData.value);
|
|
|
return;
|
|
|
}
|
|
|
PostOrderCartClear(tableId.value)
|
|
|
- .then(doClear)
|
|
|
+ .then(applyClearResult)
|
|
|
.catch((err) => {
|
|
|
console.error('清空购物车失败:', err);
|
|
|
uni.showToast({ title: '清空失败,请重试', icon: 'none' });
|
|
|
@@ -553,6 +590,17 @@ const getTablewareFeeFromCart = () => {
|
|
|
|
|
|
// 下单点击:先带购物车数据跳转确认订单页,创建订单在确认页点击「确认下单」时再调
|
|
|
const handleOrderClick = () => {
|
|
|
+ // 仅餐具无菜品时不允许下单
|
|
|
+ const items = displayCartList.value ?? [];
|
|
|
+ const hasDish = items.some((it) => Number(it?.cuisineId ?? it?.id) !== -1);
|
|
|
+ if (!hasDish && items.length > 0) {
|
|
|
+ uni.showToast({ title: '请至少选择一道菜品', icon: 'none' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (items.length === 0) {
|
|
|
+ uni.showToast({ title: '请先选择菜品', icon: 'none' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
const fromApi = Number(cartData.value?.tablewareFee) || 0;
|
|
|
const fromItems = getTablewareFeeFromCart();
|
|
|
const utensilFee = fromApi > 0 ? fromApi : fromItems;
|
|
|
@@ -582,12 +630,13 @@ onLoad(async (options) => {
|
|
|
const diners = options.diners || '';
|
|
|
tableId.value = tableid;
|
|
|
currentDiners.value = diners;
|
|
|
+ fromNumberOfDiners.value = diners !== '' && diners != null; // 仅从选座页进入时带 diners 参数
|
|
|
if (tableid) uni.setStorageSync('currentTableId', tableid);
|
|
|
if (diners) uni.setStorageSync('currentDiners', diners);
|
|
|
console.log('点餐页接收参数 - 桌号(tableid):', tableid, '就餐人数(diners):', diners);
|
|
|
|
|
|
- // 更新餐具数量:quantity 传用餐人数,tableId 传桌号ID
|
|
|
- if (tableid && diners !== '' && diners != null) {
|
|
|
+ // 更新餐具数量:仅从选座页进入时调用 update-tableware 接口
|
|
|
+ if (fromNumberOfDiners.value && tableid) {
|
|
|
PutOrderCartUpdateTableware({
|
|
|
quantity: Number(diners) || 1,
|
|
|
tableId: parseInt(tableid, 10) || 0
|