|
@@ -120,6 +120,9 @@
|
|
|
:coupon-list="couponList"
|
|
:coupon-list="couponList"
|
|
|
:selected-coupon-id="selectedCouponId"
|
|
:selected-coupon-id="selectedCouponId"
|
|
|
:view-only="false"
|
|
:view-only="false"
|
|
|
|
|
+ :list-loading="checkoutCouponLoading"
|
|
|
|
|
+ :list-has-more="checkoutCouponHasMore"
|
|
|
|
|
+ @load-more="handleCheckoutCouponLoadMore"
|
|
|
@select="handleCouponSelect"
|
|
@select="handleCouponSelect"
|
|
|
@close="couponModalOpen = false"
|
|
@close="couponModalOpen = false"
|
|
|
/>
|
|
/>
|
|
@@ -134,7 +137,7 @@ import { go } from '@/utils/utils.js';
|
|
|
import { getFileUrl } from '@/utils/file.js';
|
|
import { getFileUrl } from '@/utils/file.js';
|
|
|
import { useUserStore } from '@/store/user.js';
|
|
import { useUserStore } from '@/store/user.js';
|
|
|
import * as diningApi from '@/api/dining.js';
|
|
import * as diningApi from '@/api/dining.js';
|
|
|
-import { normalizeUserCouponListItem } from '@/utils/couponNormalize.js';
|
|
|
|
|
|
|
+import { normalizeUserCouponListItem, parseCouponListPage, mergeCouponListById } from '@/utils/couponNormalize.js';
|
|
|
import SelectCouponModal from '@/pages/orderFood/components/SelectCouponModal.vue';
|
|
import SelectCouponModal from '@/pages/orderFood/components/SelectCouponModal.vue';
|
|
|
|
|
|
|
|
const orderId = ref('');
|
|
const orderId = ref('');
|
|
@@ -172,6 +175,11 @@ const couponModalOpen = ref(false);
|
|
|
const couponList = ref([]);
|
|
const couponList = ref([]);
|
|
|
const selectedCouponId = ref(null);
|
|
const selectedCouponId = ref(null);
|
|
|
|
|
|
|
|
|
|
+const CHECKOUT_COUPON_PAGE_SIZE = 10;
|
|
|
|
|
+const checkoutCouponLoading = ref(false);
|
|
|
|
|
+const checkoutCouponHasMore = ref(true);
|
|
|
|
|
+const checkoutCouponLastPage = ref(0);
|
|
|
|
|
+
|
|
|
// 菜品清单展示(排除特殊占位 id=-1)
|
|
// 菜品清单展示(排除特殊占位 id=-1)
|
|
|
const displayFoodList = computed(() =>
|
|
const displayFoodList = computed(() =>
|
|
|
(foodList.value ?? []).filter((it) => Number(it?.id ?? it?.cuisineId) !== -1)
|
|
(foodList.value ?? []).filter((it) => Number(it?.id ?? it?.cuisineId) !== -1)
|
|
@@ -236,68 +244,145 @@ const onCouponRowClick = () => {
|
|
|
openCouponModal();
|
|
openCouponModal();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 打开优惠券弹窗并拉取用户可用券
|
|
|
|
|
-const openCouponModal = async () => {
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 拉取结算页可用优惠券(分页,每页 CHECKOUT_COUPON_PAGE_SIZE)
|
|
|
|
|
+ * @param {{ reset?: boolean }} options
|
|
|
|
|
+ */
|
|
|
|
|
+async function fetchCheckoutCouponList(options = { reset: true }) {
|
|
|
|
|
+ const { reset = true } = options;
|
|
|
const storeId = orderInfo.value.storeId || uni.getStorageSync('currentStoreId') || '';
|
|
const storeId = orderInfo.value.storeId || uni.getStorageSync('currentStoreId') || '';
|
|
|
- couponModalOpen.value = false;
|
|
|
|
|
if (!storeId) {
|
|
if (!storeId) {
|
|
|
uni.showToast({ title: '暂无门店信息', icon: 'none' });
|
|
uni.showToast({ title: '暂无门店信息', icon: 'none' });
|
|
|
- return;
|
|
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ if (reset) {
|
|
|
|
|
+ if (checkoutCouponLoading.value) return false;
|
|
|
|
|
+ checkoutCouponLoading.value = true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (
|
|
|
|
|
+ checkoutCouponLoading.value ||
|
|
|
|
|
+ !checkoutCouponHasMore.value
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ checkoutCouponLoading.value = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const page = reset ? 1 : checkoutCouponLastPage.value + 1;
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
const res = await diningApi.GetUserCouponList({
|
|
const res = await diningApi.GetUserCouponList({
|
|
|
storeId,
|
|
storeId,
|
|
|
tabType: 0,
|
|
tabType: 0,
|
|
|
- page: 1,
|
|
|
|
|
- size: 20
|
|
|
|
|
|
|
+ page,
|
|
|
|
|
+ size: CHECKOUT_COUPON_PAGE_SIZE
|
|
|
});
|
|
});
|
|
|
- const list = Array.isArray(res) ? res : (res?.data ?? res?.records ?? res?.list ?? []);
|
|
|
|
|
- const normalized = (Array.isArray(list) ? list : [])
|
|
|
|
|
|
|
+ const { list: rawList, total } = parseCouponListPage(res);
|
|
|
|
|
+ const arr = Array.isArray(rawList) ? rawList : [];
|
|
|
|
|
+ const normalized = arr
|
|
|
.map((item) => normalizeUserCouponListItem(item, 0))
|
|
.map((item) => normalizeUserCouponListItem(item, 0))
|
|
|
.filter(Boolean);
|
|
.filter(Boolean);
|
|
|
- couponList.value = normalized;
|
|
|
|
|
- couponModalOpen.value = true;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (reset) {
|
|
|
|
|
+ couponList.value = normalized;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ couponList.value = mergeCouponListById(couponList.value, normalized);
|
|
|
|
|
+ }
|
|
|
|
|
+ checkoutCouponLastPage.value = page;
|
|
|
|
|
+
|
|
|
|
|
+ const mergedLen = couponList.value.length;
|
|
|
|
|
+ if (total > 0) {
|
|
|
|
|
+ checkoutCouponHasMore.value = mergedLen < total;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ checkoutCouponHasMore.value = normalized.length >= CHECKOUT_COUPON_PAGE_SIZE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
console.error('获取优惠券失败:', err);
|
|
console.error('获取优惠券失败:', err);
|
|
|
- uni.showToast({ title: '获取优惠券失败', icon: 'none' });
|
|
|
|
|
|
|
+ if (reset) {
|
|
|
|
|
+ uni.showToast({ title: '获取优惠券失败', icon: 'none' });
|
|
|
|
|
+ couponList.value = [];
|
|
|
|
|
+ checkoutCouponHasMore.value = false;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: '加载更多失败', icon: 'none' });
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ checkoutCouponLoading.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleCheckoutCouponLoadMore() {
|
|
|
|
|
+ if (!checkoutCouponLoading.value && checkoutCouponHasMore.value) {
|
|
|
|
|
+ fetchCheckoutCouponList({ reset: false });
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 打开优惠券弹窗并拉取用户可用券
|
|
|
|
|
+const openCouponModal = async () => {
|
|
|
|
|
+ couponModalOpen.value = false;
|
|
|
|
|
+ const ok = await fetchCheckoutCouponList({ reset: true });
|
|
|
|
|
+ if (ok) {
|
|
|
|
|
+ couponModalOpen.value = true;
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 选择优惠券后更新订单优惠与应付金额
|
|
// 选择优惠券后更新订单优惠与应付金额
|
|
|
const handleCouponSelect = ({ coupon, selectedId }) => {
|
|
const handleCouponSelect = ({ coupon, selectedId }) => {
|
|
|
const hasSelected = selectedId != null && selectedId !== '';
|
|
const hasSelected = selectedId != null && selectedId !== '';
|
|
|
- selectedCouponId.value = hasSelected ? String(selectedId) : null;
|
|
|
|
|
- orderInfo.value.couponId = hasSelected ? (coupon?.couponId ?? coupon?.id ?? selectedCouponId.value) : null;
|
|
|
|
|
|
|
+
|
|
|
if (!hasSelected) {
|
|
if (!hasSelected) {
|
|
|
|
|
+ selectedCouponId.value = null;
|
|
|
|
|
+ orderInfo.value.couponId = null;
|
|
|
orderInfo.value.discountAmount = 0;
|
|
orderInfo.value.discountAmount = 0;
|
|
|
orderInfo.value.couponName = '';
|
|
orderInfo.value.couponName = '';
|
|
|
orderInfo.value.couponType = null;
|
|
orderInfo.value.couponType = null;
|
|
|
orderInfo.value.discountRate = null;
|
|
orderInfo.value.discountRate = null;
|
|
|
orderInfo.value.nominalValue = null;
|
|
orderInfo.value.nominalValue = null;
|
|
|
- } else if (coupon) {
|
|
|
|
|
- const couponType = Number(coupon.couponType) || 0;
|
|
|
|
|
- orderInfo.value.couponName = coupon.name ?? '';
|
|
|
|
|
- orderInfo.value.couponType = couponType || null;
|
|
|
|
|
- orderInfo.value.discountRate = coupon.discountRate != null ? coupon.discountRate : null;
|
|
|
|
|
- const nvRaw = coupon.nominalValue;
|
|
|
|
|
- if (nvRaw != null && nvRaw !== '') {
|
|
|
|
|
- const nv = Number(nvRaw);
|
|
|
|
|
- orderInfo.value.nominalValue = Number.isFinite(nv) ? nv : null;
|
|
|
|
|
- } else if (couponType === 1) {
|
|
|
|
|
- orderInfo.value.nominalValue = Number(coupon.amount) || null;
|
|
|
|
|
- } else {
|
|
|
|
|
- orderInfo.value.nominalValue = null;
|
|
|
|
|
- }
|
|
|
|
|
- const food = foodSubtotalForDisplay.value;
|
|
|
|
|
- const fee = Number(orderInfo.value.serviceFee) || 0;
|
|
|
|
|
- const baseForDiscount = food + fee;
|
|
|
|
|
- if (couponType === 2 && coupon.discountRate != null && baseForDiscount > 0) {
|
|
|
|
|
- const rate = Number(coupon.discountRate) || 0;
|
|
|
|
|
- orderInfo.value.discountAmount = Math.round(baseForDiscount * (1 - rate / 10) * 100) / 100;
|
|
|
|
|
- } else {
|
|
|
|
|
- orderInfo.value.discountAmount =
|
|
|
|
|
- Number(coupon.nominalValue) || Number(coupon.amount) || 0;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ updateCheckoutPayAmount();
|
|
|
|
|
+ couponModalOpen.value = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!coupon) {
|
|
|
|
|
+ couponModalOpen.value = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const couponType = Number(coupon.couponType) || 0;
|
|
|
|
|
+ const foodSubtotal = Number(foodSubtotalForDisplay.value) || 0;
|
|
|
|
|
+ const threshold = Number(coupon.minAmount) || 0;
|
|
|
|
|
+ // 满减券(couponType=1)且有门槛:菜品总价须 ≥ 门槛,否则不可选
|
|
|
|
|
+ if (couponType === 1 && threshold > 0 && foodSubtotal < threshold) {
|
|
|
|
|
+ uni.showToast({ title: '未到满减此券不可用', icon: 'none' });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ selectedCouponId.value = String(selectedId);
|
|
|
|
|
+ orderInfo.value.couponId = coupon?.couponId ?? coupon?.id ?? String(selectedId) ?? null;
|
|
|
|
|
+ orderInfo.value.couponName = coupon.name ?? '';
|
|
|
|
|
+ orderInfo.value.couponType = couponType || null;
|
|
|
|
|
+ orderInfo.value.discountRate = coupon.discountRate != null ? coupon.discountRate : null;
|
|
|
|
|
+ const nvRaw = coupon.nominalValue;
|
|
|
|
|
+ if (nvRaw != null && nvRaw !== '') {
|
|
|
|
|
+ const nv = Number(nvRaw);
|
|
|
|
|
+ orderInfo.value.nominalValue = Number.isFinite(nv) ? nv : null;
|
|
|
|
|
+ } else if (couponType === 1) {
|
|
|
|
|
+ orderInfo.value.nominalValue = Number(coupon.amount) || null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ orderInfo.value.nominalValue = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ const food = foodSubtotal;
|
|
|
|
|
+ const fee = Number(orderInfo.value.serviceFee) || 0;
|
|
|
|
|
+ const baseForDiscount = food + fee;
|
|
|
|
|
+ if (couponType === 2 && coupon.discountRate != null && baseForDiscount > 0) {
|
|
|
|
|
+ const rate = Number(coupon.discountRate) || 0;
|
|
|
|
|
+ orderInfo.value.discountAmount = Math.round(baseForDiscount * (1 - rate / 10) * 100) / 100;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ orderInfo.value.discountAmount =
|
|
|
|
|
+ Number(coupon.nominalValue) || Number(coupon.amount) || 0;
|
|
|
}
|
|
}
|
|
|
updateCheckoutPayAmount();
|
|
updateCheckoutPayAmount();
|
|
|
couponModalOpen.value = false;
|
|
couponModalOpen.value = false;
|