|
@@ -78,7 +78,7 @@
|
|
|
<text class="coupon-arrow">›</text>
|
|
<text class="coupon-arrow">›</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
- <view v-if="(orderInfo.discountAmount ?? 0) > 0" class="info-item info-item--coupon">
|
|
|
|
|
|
|
+ <view v-if="showCheckoutDiscountRow" class="info-item info-item--coupon">
|
|
|
<view class="info-item-label">优惠金额</view>
|
|
<view class="info-item-label">优惠金额</view>
|
|
|
<view class="info-item-value coupon-value">
|
|
<view class="info-item-value coupon-value">
|
|
|
<text class="coupon-amount">-¥{{ formatPrice(orderInfo.discountAmount) }}</text>
|
|
<text class="coupon-amount">-¥{{ formatPrice(orderInfo.discountAmount) }}</text>
|
|
@@ -181,7 +181,17 @@ const dinerCount = computed(() => parseDinerCount(orderInfo.value.diners));
|
|
|
const foodSubtotalForDisplay = computed(() => {
|
|
const foodSubtotalForDisplay = computed(() => {
|
|
|
const list = displayFoodList.value;
|
|
const list = displayFoodList.value;
|
|
|
if (list.length > 0) {
|
|
if (list.length > 0) {
|
|
|
- const sum = list.reduce((s, it) => s + (Number(it.lineSubtotal) || 0), 0);
|
|
|
|
|
|
|
+ let sum = 0;
|
|
|
|
|
+ for (const it of list) {
|
|
|
|
|
+ const ls = Number(it.lineSubtotal);
|
|
|
|
|
+ if (Number.isFinite(ls) && ls > 0) {
|
|
|
|
|
+ sum += ls;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ const q = Number(it.quantity) || 1;
|
|
|
|
|
+ const u = Number(it.price) || 0;
|
|
|
|
|
+ sum += u * q;
|
|
|
|
|
+ }
|
|
|
return Math.max(0, Math.round(sum * 100) / 100);
|
|
return Math.max(0, Math.round(sum * 100) / 100);
|
|
|
}
|
|
}
|
|
|
const dt = orderInfo.value.dishTotal;
|
|
const dt = orderInfo.value.dishTotal;
|
|
@@ -194,6 +204,18 @@ const foodSubtotalForDisplay = computed(() => {
|
|
|
return Math.max(0, total - fee);
|
|
return Math.max(0, total - fee);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+/** 是否展示「优惠金额」行:有优惠额,或已选折扣券且 discountRate 有效 */
|
|
|
|
|
+const showCheckoutDiscountRow = computed(() => {
|
|
|
|
|
+ const o = orderInfo.value;
|
|
|
|
|
+ if ((Number(o.discountAmount) || 0) > 0) return true;
|
|
|
|
|
+ return (
|
|
|
|
|
+ Number(o.couponType) === 2 &&
|
|
|
|
|
+ o.couponId != null &&
|
|
|
|
|
+ String(o.couponId).trim() !== '' &&
|
|
|
|
|
+ Number(o.discountRate) > 0
|
|
|
|
|
+ );
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
/** 是否已在结算页选定优惠券(含订单带入) */
|
|
/** 是否已在结算页选定优惠券(含订单带入) */
|
|
|
const hasCheckoutCouponChosen = computed(() => {
|
|
const hasCheckoutCouponChosen = computed(() => {
|
|
|
const o = orderInfo.value;
|
|
const o = orderInfo.value;
|
|
@@ -253,7 +275,18 @@ async function fetchCheckoutCouponList(options = { reset: true }) {
|
|
|
const { list: rawList } = parseCouponListPage(res);
|
|
const { list: rawList } = parseCouponListPage(res);
|
|
|
const arr = Array.isArray(rawList) ? rawList : [];
|
|
const arr = Array.isArray(rawList) ? rawList : [];
|
|
|
const normalized = arr
|
|
const normalized = arr
|
|
|
- .map((item) => normalizeUserCouponListItem(item, 0))
|
|
|
|
|
|
|
+ .map((item) => {
|
|
|
|
|
+ const n = normalizeUserCouponListItem(item, 0);
|
|
|
|
|
+ if (!n) return null;
|
|
|
|
|
+ const apiRate = item?.discountRate ?? item?.discount_rate;
|
|
|
|
|
+ if (Number(n.couponType) === 2 && apiRate != null && apiRate !== '') {
|
|
|
|
|
+ const num = Number(apiRate);
|
|
|
|
|
+ if (Number.isFinite(num)) {
|
|
|
|
|
+ return { ...n, _sourceDiscountRate: num };
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return n;
|
|
|
|
|
+ })
|
|
|
.filter(Boolean);
|
|
.filter(Boolean);
|
|
|
|
|
|
|
|
couponList.value = normalized;
|
|
couponList.value = normalized;
|
|
@@ -280,6 +313,30 @@ function handleCheckoutCouponLoadMore() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 将接口/列表里的「折」统一为应付公式用的刻度:payRatio = rate/10,优惠 = 基数×(1−rate/10)。
|
|
|
|
|
+ * 如 88(八八折整数)、8.8、0.88 均归一为 8.8。
|
|
|
|
|
+ */
|
|
|
|
|
+function normalizeZheRateForCheckoutFormula(r) {
|
|
|
|
|
+ if (!Number.isFinite(r) || r <= 0) return 0;
|
|
|
|
|
+ if (r > 10 && r <= 100) return Math.round((r / 10) * 100) / 100;
|
|
|
|
|
+ if (r > 0 && r < 1) return Math.round(r * 10 * 100) / 100;
|
|
|
|
|
+ return Math.round(r * 100) / 100;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 折扣券:仅用接口/券上的 discountRate(及 _sourceDiscountRate 保留的原始值)计算折标,不用 amount 代替 */
|
|
|
|
|
+function resolveCheckoutDiscountCouponRate(coupon) {
|
|
|
|
|
+ if (!coupon || Number(coupon.couponType) !== 2) return 0;
|
|
|
|
|
+ const rawVal =
|
|
|
|
|
+ coupon._sourceDiscountRate ??
|
|
|
|
|
+ coupon.discountRate ??
|
|
|
|
|
+ coupon.discount_rate;
|
|
|
|
|
+ if (rawVal == null || rawVal === '') return 0;
|
|
|
|
|
+ const dr = Number(rawVal);
|
|
|
|
|
+ if (!Number.isFinite(dr) || dr <= 0) return 0;
|
|
|
|
|
+ return normalizeZheRateForCheckoutFormula(dr);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 选择优惠券后更新订单优惠与应付金额
|
|
// 选择优惠券后更新订单优惠与应付金额
|
|
|
const handleCouponSelect = ({ coupon, selectedId }) => {
|
|
const handleCouponSelect = ({ coupon, selectedId }) => {
|
|
|
const hasSelected = selectedId != null && selectedId !== '';
|
|
const hasSelected = selectedId != null && selectedId !== '';
|
|
@@ -315,7 +372,6 @@ const handleCouponSelect = ({ coupon, selectedId }) => {
|
|
|
orderInfo.value.couponId = coupon?.couponId ?? coupon?.id ?? String(selectedId) ?? null;
|
|
orderInfo.value.couponId = coupon?.couponId ?? coupon?.id ?? String(selectedId) ?? null;
|
|
|
orderInfo.value.couponName = String(coupon.name ?? coupon.title ?? coupon.couponName ?? '').trim();
|
|
orderInfo.value.couponName = String(coupon.name ?? coupon.title ?? coupon.couponName ?? '').trim();
|
|
|
orderInfo.value.couponType = couponType || null;
|
|
orderInfo.value.couponType = couponType || null;
|
|
|
- orderInfo.value.discountRate = coupon.discountRate != null ? coupon.discountRate : null;
|
|
|
|
|
const nvRaw = coupon.nominalValue;
|
|
const nvRaw = coupon.nominalValue;
|
|
|
if (nvRaw != null && nvRaw !== '') {
|
|
if (nvRaw != null && nvRaw !== '') {
|
|
|
const nv = Number(nvRaw);
|
|
const nv = Number(nvRaw);
|
|
@@ -328,10 +384,18 @@ const handleCouponSelect = ({ coupon, selectedId }) => {
|
|
|
const food = foodSubtotal;
|
|
const food = foodSubtotal;
|
|
|
const fee = Number(orderInfo.value.serviceFee) || 0;
|
|
const fee = Number(orderInfo.value.serviceFee) || 0;
|
|
|
const baseForDiscount = food + fee;
|
|
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;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (couponType === 2) {
|
|
|
|
|
+ const rate = resolveCheckoutDiscountCouponRate(coupon);
|
|
|
|
|
+ orderInfo.value.discountRate = rate > 0 ? rate : null;
|
|
|
|
|
+ if (rate > 0 && baseForDiscount > 0) {
|
|
|
|
|
+ const rawDiscount = Math.round(baseForDiscount * (1 - rate / 10) * 100) / 100;
|
|
|
|
|
+ orderInfo.value.discountAmount = Math.max(0, Math.min(rawDiscount, baseForDiscount));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ orderInfo.value.discountAmount = 0;
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ orderInfo.value.discountRate = null;
|
|
|
orderInfo.value.discountAmount =
|
|
orderInfo.value.discountAmount =
|
|
|
Number(coupon.nominalValue) || Number(coupon.amount) || 0;
|
|
Number(coupon.nominalValue) || Number(coupon.amount) || 0;
|
|
|
}
|
|
}
|
|
@@ -360,7 +424,8 @@ function recalcDiscountAfterServiceFeeChange() {
|
|
|
const base = food + fee;
|
|
const base = food + fee;
|
|
|
const rate = Number(orderInfo.value.discountRate) || 0;
|
|
const rate = Number(orderInfo.value.discountRate) || 0;
|
|
|
if (base <= 0) return;
|
|
if (base <= 0) return;
|
|
|
- orderInfo.value.discountAmount = Math.round(base * (1 - rate / 10) * 100) / 100;
|
|
|
|
|
|
|
+ const rawDiscount = Math.round(base * (1 - rate / 10) * 100) / 100;
|
|
|
|
|
+ orderInfo.value.discountAmount = Math.max(0, Math.min(rawDiscount, base));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -485,6 +550,11 @@ function normalizeOrderItem(item) {
|
|
|
const unit = Number(item?.unitPrice ?? item?.price ?? 0) || 0;
|
|
const unit = Number(item?.unitPrice ?? item?.price ?? 0) || 0;
|
|
|
lineSubtotal = unit * qty;
|
|
lineSubtotal = unit * qty;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (lineSubtotal <= 0) {
|
|
|
|
|
+ const unit = Number(item?.unitPrice ?? item?.price ?? item?.salePrice ?? 0) || 0;
|
|
|
|
|
+ const fallback = unit * qty;
|
|
|
|
|
+ if (fallback > 0) lineSubtotal = fallback;
|
|
|
|
|
+ }
|
|
|
const unitForDisplay =
|
|
const unitForDisplay =
|
|
|
Number(item?.unitPrice ?? item?.price ?? 0) ||
|
|
Number(item?.unitPrice ?? item?.price ?? 0) ||
|
|
|
(qty > 0 ? lineSubtotal / qty : 0);
|
|
(qty > 0 ? lineSubtotal / qty : 0);
|