diff --git a/common/api/market/birthdayGift.js b/common/api/market/birthdayGift.js
new file mode 100644
index 0000000..96b3a8e
--- /dev/null
+++ b/common/api/market/birthdayGift.js
@@ -0,0 +1,11 @@
+// 引入 request 文件
+import request from "@/common/api/request.js";
+import { prveUrl } from "./config.js";
+
+export const config = (data) => {
+ return request({
+ url: prveUrl + "/user/birthdayGift",
+ method: "get",
+ data: data,
+ });
+};
diff --git a/common/api/market/discountActivity.js b/common/api/market/discountActivity.js
new file mode 100644
index 0000000..443b867
--- /dev/null
+++ b/common/api/market/discountActivity.js
@@ -0,0 +1,11 @@
+// 引入 request 文件
+import request from "@/common/api/request.js";
+import { prveUrl } from "./config.js";
+
+export const config = (data) => {
+ return request({
+ url: prveUrl + "/user/discountActivity",
+ method: "get",
+ data: data,
+ });
+};
diff --git a/common/api/market/drainageConfig.js b/common/api/market/drainageConfig.js
new file mode 100644
index 0000000..c25d08a
--- /dev/null
+++ b/common/api/market/drainageConfig.js
@@ -0,0 +1,11 @@
+// 引入 request 文件
+import request from "@/common/api/request.js";
+import { prveUrl } from "./config.js";
+
+export const config = (data) => {
+ return request({
+ url: prveUrl + "/user/drainageConfig",
+ method: "get",
+ data: data,
+ });
+};
diff --git a/components/birthday-modal.vue b/components/birthday-modal.vue
new file mode 100644
index 0000000..bd0ff83
--- /dev/null
+++ b/components/birthday-modal.vue
@@ -0,0 +1,545 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ returnTitle(item.couponInfo)
+ }}
+
+ {{ returnSmallTitle(item.couponInfo) }}
+
+
+
+ {{
+ returnTitle(item.couponInfo)
+ }}
+
+
+
+
+
+ {{ item.couponInfo.title }}
+ x{{ item.num }}
+
+
+
+
+
+ 有效期至:{{ formatDate(item.couponInfo.validStartTime) }}-{{
+ formatDate(item.couponInfo.validEndTime)
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ num }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/coupon-modal.vue b/components/coupon-modal.vue
index 987b94e..b39b165 100644
--- a/components/coupon-modal.vue
+++ b/components/coupon-modal.vue
@@ -1,6 +1,6 @@
-
+
@@ -141,6 +141,8 @@ async function getCouponPopupAjax() {
bgUrl.value = bgUrlList.value[1];
currentNum.value = 1;
}
+ }else{
+ close()
}
} catch (error) {
console.log(error);
@@ -167,6 +169,12 @@ async function getHandle() {
}
uni.hideLoading();
}
+const emit=defineEmits(['close'])
+
+function close() {
+ show.value = false;
+ emit('close');
+}
onMounted(() => {
getCouponPopupAjax();
diff --git a/components/drainage.vue b/components/drainage.vue
new file mode 100644
index 0000000..2dbc572
--- /dev/null
+++ b/components/drainage.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+ {{
+ drainageConfig.title
+ }}
+ {{
+ drainageConfig.content
+ }}
+
+
+ {{
+ drainageConfig.note
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/modal-list.vue b/components/modal-list.vue
new file mode 100644
index 0000000..71898c5
--- /dev/null
+++ b/components/modal-list.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/official-account.vue b/components/official-account.vue
new file mode 100644
index 0000000..854d264
--- /dev/null
+++ b/components/official-account.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+ 长按识别关注,更多优惠不能错过
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/order-finish-modal.vue b/components/order-finish-modal.vue
new file mode 100644
index 0000000..e6ecce7
--- /dev/null
+++ b/components/order-finish-modal.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/paymentMethod copy.vue b/components/paymentMethod copy.vue
new file mode 100644
index 0000000..84e0447
--- /dev/null
+++ b/components/paymentMethod copy.vue
@@ -0,0 +1,260 @@
+
+
+
+
+ 支付方式
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ 当前余额¥{{orderVIP?(orderVIP.amount||0):0}}
+ 去充值
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/paymentMethod.vue b/components/paymentMethod.vue
index 84e0447..00af685 100644
--- a/components/paymentMethod.vue
+++ b/components/paymentMethod.vue
@@ -1,260 +1,276 @@
-
-
-
- 支付方式
-
-
-
-
-
-
-
- {{ item.name }}
-
-
- 当前余额¥{{orderVIP?(orderVIP.amount||0):0}}
- 去充值
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ 支付方式
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ 当前余额¥{{ orderVIP ? orderVIP.amount || 0 : 0 }}
+ 去充值
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/0-conf.js b/framework/0-conf.js
index 6367448..0031c6f 100644
--- a/framework/0-conf.js
+++ b/framework/0-conf.js
@@ -1,5 +1,5 @@
// const debug = process.env.NODE_ENV == 'development' ? true : false;
-const debug = false
+const debug = true
// #ifdef H5
const proxyApi = "/api"
// #endif
diff --git a/pages/order/components/orderInfo.vue b/pages/order/components/orderInfo.vue
index 59684f6..ab064d4 100644
--- a/pages/order/components/orderInfo.vue
+++ b/pages/order/components/orderInfo.vue
@@ -1,103 +1,142 @@
-
-
-
-
-
-
-
-
-
- {{item.title}}
-
-
-
-
-
- 桌台
-
- {{ listinfo.tableName || '' }}
-
+
+
+
+
+
+
+ {{
+ item.title
+ }}
+
+
+
+
+
+ 桌台
+
+
+ {{ listinfo.tableName || "" }}
+
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
- {{item.productName}}
- {{item.skuName}}
-
-
-
- {{item.price}}
-
- ¥{{useVipPrice?(item.memberPrice|| item.price):item.price}}
-
-
- x{{item.num}}
-
-
+
+
+
+
+
+
+
+
+ {{ item.productName }}
+ {{
+ item.skuName
+ }}
+
+
+
+ {{ item.price }}
+
+ ¥{{
+ useVipPrice ? item.memberPrice || item.price : item.price
+ }}
+
+
+ x{{ item.num }}
+
+
+
+
-
+
+ 总计¥
+
+ {{ cartStore.orderCostSummary.goodsRealAmount }}
+
+
-
-
-
-
- 总计¥
- {{cartStore.orderCostSummary.goodsRealAmount}}
-
-
-
-
- 打包费
-
-
- ¥
- {{cartStore.orderCostSummary.packFee }}
-
-
+
+ 打包费
+
+
+ ¥
+ {{ cartStore.orderCostSummary.packFee }}
+
+
+
+
+ 餐位费
+
+ X{{ cartStore.seatFeeConfig.personCount }}
+ ¥
+ {{ cartStore.orderCostSummary.seatFee }}
+
+
-
-
- 餐位费
-
- X{{cartStore.seatFeeConfig.personCount}}
- ¥
- {{cartStore.orderCostSummary.seatFee}}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.name }}
-
- 「可用积分{{pointsRes.accountPoints}},最大可抵扣{{maxPointDiscount}}元」
+
+
+
+
+
+
+ 满减活动
+
-
-
+ -¥{{ discountActivity.discountAmount }}
+
+
+
+
+ 新客立减
+
+ -¥{{ newUserDiscount }}
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ 「可用积分{{ pointsRes.accountPoints }},最大可抵扣{{
+ maxPointDiscount
+ }}元」
+
+
+
-
-
-
- -¥{{cartStore.orderCostSummary.couponDeductionAmount}}
-
-
- 有可用优惠券
- 暂无可用优惠券
-
+
+
+
+ -¥{{
+ cartStore.orderCostSummary.couponDeductionAmount
+ }}
+
+
+ {{ disabledCouponReason }}
+ 有可用优惠券
+ 暂无可用优惠券
+
+
+
+
+
+
+
+ -¥{{ newUserDiscount }}
+
+
+
+ {{ disabledPointsUseReason }}
+
+
+
+ -¥{{ cartStore.orderCostSummary.pointDeductionAmount }}
+
+
-
-
-
-
-
-
- -¥{{newUserDiscount}}
-
-
-
-
-
-
-
-
- -¥{{cartStore.orderCostSummary.pointDeductionAmount}}
-
-
-
-
-
-
- 使用抵扣
-
-
-
-
-
-
-
-
-
-
-
-
- 优惠折扣
-
-
- {{item.name}}
-
- {{item.amount}}元
-
-
-
-
-
-
-
-
-
-
- 订单编号:
- {{listinfo.orderNo}}(点击复制)
-
-
- 下单时间:
- {{$u.timeFormat(listinfo.updateTime, 'yyyy-mm-dd hh:MM:ss')}}
-
-
- 下单台桌:
- {{listinfo.tableName}}
-
-
- 下单门店:
- {{orderVIP.shopName}}
-
-
- 用餐模式:
- 堂食
- 外带
- 外卖
-
-
- 备注:
- {{ listinfo.remark || ""}}
-
-
- 就餐人数:
- {{ listinfo.seatNum || ""}}
-
-
- 退款方式:
- {{ listinfo.refundType == 'cash'?'手动退款':'原路退回'}}
-
-
-
-
-
-
+
+
+
+ 使用抵扣
+
+
+
+
+
+
+
+
+ 优惠折扣
+
+
+ {{ item.name }}
+
+ {{ item.amount }}元
+
+
+
+
+
+
+
+
+ 订单编号:
+ {{ listinfo.orderNo }}(点击复制)
+
+
+ 下单时间:
+ {{
+ $u.timeFormat(listinfo.updateTime, "yyyy-mm-dd hh:MM:ss")
+ }}
+
+
+ 下单台桌:
+ {{ listinfo.tableName }}
+
+
+ 下单门店:
+ {{ orderVIP.shopName }}
+
+
+ 用餐模式:
+ 堂食
+ 外带
+ 外卖
+
+
+ 备注:
+ {{ listinfo.remark || "" }}
+
+
+ 就餐人数:
+ {{ listinfo.seatNum || "" }}
+
+
+ 退款方式:
+ {{
+ listinfo.refundType == "cash" ? "手动退款" : "原路退回"
+ }}
+
+
+
\ No newline at end of file
diff --git a/pages/order/components/rechargeFree.vue b/pages/order/components/rechargeFree.vue
index 9c18794..aea2dbe 100644
--- a/pages/order/components/rechargeFree.vue
+++ b/pages/order/components/rechargeFree.vue
@@ -1,92 +1,107 @@
-
-
-
-
- 优惠
-
- 充值消费{{freeDineConfig.rechargeTimes}}倍(订单满¥{{freeDineConfig.rechargeThreshold}}元可用),本单立享免单!
-
-
-
-
+
+
+
+ 优惠
+
+ 充值消费{{ freeDineConfig.rechargeTimes }}倍(订单满¥{{
+ freeDineConfig.rechargeThreshold
+ }}元可用),本单立享免单!
+
+ {}">
+
+
+
+
+
-
-
+
+
\ No newline at end of file
diff --git a/pages/order/confirm-order.vue b/pages/order/confirm-order.vue
index e893384..a2b2bad 100644
--- a/pages/order/confirm-order.vue
+++ b/pages/order/confirm-order.vue
@@ -1,1614 +1,1679 @@
-
-
-
-
+
+
+
+
+
+ 待支付
+ 已完成
+ 退单
+ 部分退单
+ 取消订单
+
+ 下单时间:{{ listinfo.createTime }}
+
+ 付款时间:{{ listinfo.paidTime }}
+
+
-
- 待支付
- 已完成
- 退单
- 部分退单
- 取消订单
-
- 下单时间:{{ listinfo.createTime }}
-
- 付款时间:{{listinfo.paidTime}}
-
-
+
+
+ 扫码加好友,优惠多多
+ 优惠活动多
+ 充值有好礼
+ 会员享低价
+ 长按保存,微信内扫一扫加好友
+
+
+
+
+
+
+
+
-
-
- 扫码加好友,优惠多多
- 优惠活动多
- 充值有好礼
- 会员享低价
- 长按保存,微信内扫一扫加好友
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
- 订单备注:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 实付金额:
-
- ¥{{rechargeItem.amount}}
-
-
- ¥{{cartStore.orderCostSummary.finalPayAmount}}
-
-
-
-
-
-
-
-
-
- 加菜
-
-
-
-
- 取消订单
-
-
-
-
-
-
-
- 仅下单
-
-
-
-
-
- 加菜
-
-
-
-
-
- {{paymentmethod.paymentBtnText}}
-
-
+
+
+
+
+
+ 订单备注:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 实付金额:
+
+ ¥{{ rechargeItem.amount }}
+
+
+ ¥{{ cartStore.orderCostSummary.finalPayAmount }}
+
+
+
+
+
+
+
+
+ 加菜
+
+
+
+
+ 取消订单
+
+
+
+
+
+
+
+ 仅下单
+
+
+
+
+
+ 加菜
+
+
+
+
+
+ {{ paymentmethod.paymentBtnText }}
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/product/index.vue b/pages/product/index.vue
index 5a78e68..412b7c2 100644
--- a/pages/product/index.vue
+++ b/pages/product/index.vue
@@ -1,519 +1,861 @@
-
-
-
-
-
-
-
-
- {{ shopInfo.shopName }}
-
-
-
-
-
-
-
- 距离您{{ distance }}
-
-
- 营业时间:{{ (shopInfo.businessStartDay || '--') + ' 至 ' + (shopInfo.businessEndDay || '--') }} {{ endTimeref.startTime }} -
- {{ endTimeref.endTime < '05' ? ` 凌晨${endTimeref.endTime}` : endTimeref.endTime }}
-
-
- 查看
-
-
-
-
- 本店招牌菜
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.name }}
-
- 本店回头客第{{ index + 1 }}名
-
- 招牌
-
- 「{{ item.suitNum }}{{ item.unitName }}起点」
-
-
+
+
+
+
+
+
+
+
+ {{ shopInfo.shopName }}
+
+
+
+
+
+
+
+ 距离您{{ distance }}
+
+
+ 营业时间:{{
+ (shopInfo.businessStartDay || "--") +
+ " 至 " +
+ (shopInfo.businessEndDay || "--")
+ }}
+ {{ endTimeref.startTime }} -
+ {{
+ endTimeref.endTime < "05"
+ ? ` 凌晨${endTimeref.endTime}`
+ : endTimeref.endTime
+ }}
+
+
+ 查看
+
+
+
+
+ 本店招牌菜
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+ 本店回头客第{{ index + 1 }}名
+
+ 招牌
+
+ 「{{ item.suitNum }}{{ item.unitName }}起点」
+
+
-
-
-
-
- ¥
-
-
- {{ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1 ? item.memberPrice || item.salePrice : item.salePrice }}
-
-
- /{{ item.unitName }}
-
-
-
-
- 未开售
- 已售罄
-
-
-
+
+
+
+ ¥
+
+
+ {{
+ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1
+ ? item.memberPrice || item.salePrice
+ : item.salePrice
+ }}
+
+
+ /{{ item.unitName }}
+
+
+
+
+ 未开售
+ 已售罄
+
+
+
-
-
-
- ¥
-
-
- {{ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1 ? item.memberPrice || item.salePrice : item.salePrice }}
-
- /{{ item.unitName }}
-
+
+ {{
+ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1
+ ? item.memberPrice || item.salePrice
+ : item.salePrice
+ }}
+
+ /{{ item.unitName }}
+
-
-
-
-
-
-
- 选择套餐
- 选规格
- {{ ifcartNumber(item) < 99 ? ifcartNumber(item) : '99+' }}
-
-
-
-
- singleclick(item, '-'), 500)">
-
- {{ ifcartNumber(item) }}
-
-
- singleclick(item, '+'), 500)">
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ 选择套餐
+ 选规格
+ {{
+ ifcartNumber(item) < 99 ? ifcartNumber(item) : "99+"
+ }}
+
+
+
+
+ singleclick(item, '-'), 500)
+ "
+ >
+
+ {{ ifcartNumber(item) }}
+
+
+ singleclick(item, '+'), 500)
+ "
+ >
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
- {{ item.name }}
-
-
-
-
-
-
-
- {{ item.name }}
-
-
-
-
-
-
-
-
-
-
- TOP{{ index1 + 1 }}
-
- {{ item1.name }}
-
- 热销
- {{ item1.shortTitle ? item1.shortTitle : '' }}
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+ TOP{{ index1 + 1 }}
+
+ {{ item1.name }}
+
+ 热销
+ {{
+ item1.shortTitle ? item1.shortTitle : ""
+ }}
-
- 「{{ item1.suitNum }}{{ item1.unitName }}起点」
-
-
-
- ¥
-
- {{ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1 ? item1.memberPrice || item1.salePrice : item1.salePrice }}
-
- /{{ item1.unitName }}
-
-
-
- 未开售
- 已售罄
-
-
-
-
-
- ¥
-
- {{ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1 ? item1.memberPrice || item1.salePrice : item1.salePrice }}
-
- /{{ item1.unitName }}
-
-
-
-
- 选择套餐
- 选规格
- {{ ifcartNumber(item1) < 99 ? ifcartNumber(item1) : '99+' }}
-
-
-
-
-
- singleclick(item1, '-'), 500)">
-
+
+
+
+ 选择套餐
+ 选规格
+ {{
+ ifcartNumber(item1) < 99 ? ifcartNumber(item1) : "99+"
+ }}
+
+
+
+
+
+ singleclick(item1, '-'), 500)
+ "
+ >
+
- {{ ifcartNumber(item1) }}
-
-
- singleclick(item1, '+'), 500)">
-
-
-
-
-
-
-
-
-
-
-
+ {{ ifcartNumber(item1) }}
+
+
+ singleclick(item1, '+'), 500)
+ "
+ >
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
- {{ cartLists_count < 99 ? cartLists_count : '99+' }}
-
+
+
+
+
+
+
+ {{
+ cartLists_count < 99 ? cartLists_count : "99+"
+ }}
+
- ¥
- {{ totalPrices }}
-
-
- 结算
-
-
-
+ ¥
+ {{ totalPrices }}
+
+
+ 结算
+
+
+
-
-
-
-
-
-
-
-
- {{ specifications.item.name }}
-
- {{ specifications.item.shortTitle ? specifications.item.shortTitle : '' }}
-
-
-
-
- {{ specType }}
-
-
-
- {{ option }}
-
- 售罄
-
-
- 已下架
-
-
-
-
-
-
-
-
-
- {{ setmenu.title }} {{ setmenu.count }} 选{{ setmenu.number }}
-
-
- {{ option.proName }}
- /{{ option.unitName }}
-
- 售罄
-
-
-
-
-
-
-
-
-
-
- ¥
-
- {{
- shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1
- ? specifications.item.result.memberPrice || specifications.item.result.salePrice
- : specifications.item.result.salePrice
- }}
-
- /{{ specifications.item.unitName }}
- 「{{ specifications.item.result.suitNum }}{{ specifications.item.result.unitName }}起点」
-
-
- ¥
-
- {{
- shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1
- ? specifications.item.memberPrice || specifications.item.salePrice
- : specifications.item.salePrice
- }}
-
- /{{ specifications.item.unitName }}
- 「{{ specifications.item.suitNum }}{{ specifications.item.unitName }}起点」
-
+
+
+
+
+
+
+
+
+ {{ specifications.item.name }}
+
+ {{
+ specifications.item.shortTitle
+ ? specifications.item.shortTitle
+ : ""
+ }}
+
+
+
+
+ {{ specType }}
+
+
+
+ {{ option }}
+
+ 售罄
+
+
+ 已下架
+
+
+
+
+
+
+
+
+
+ {{ setmenu.title }} {{ setmenu.count }} 选{{
+ setmenu.number
+ }}
+
+
+ {{ option.proName }}
+ /{{ option.unitName }}
+
+ 售罄
+
+
+
+
+
+
+
+
+
+
+ ¥
+
+ {{
+ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1
+ ? specifications.item.result.memberPrice ||
+ specifications.item.result.salePrice
+ : specifications.item.result.salePrice
+ }}
+
+ /{{ specifications.item.unitName }}
+ 「{{ specifications.item.result.suitNum
+ }}{{ specifications.item.result.unitName }}起点」
+
+
+ ¥
+
+ {{
+ shopInfo.isVip == 1 && shopInfo.isMemberPrice == 1
+ ? specifications.item.memberPrice ||
+ specifications.item.salePrice
+ : specifications.item.salePrice
+ }}
+
+ /{{ specifications.item.unitName }}
+ 「{{ specifications.item.suitNum
+ }}{{ specifications.item.unitName }}起点」
+
-
-
-
-
-
- {{ shopCartNumber }}
-
-
-
-
-
-
-
- {{ selectedSpecsStr }}
-
-
- {{ skuBtnText }}
-
-
- {{ skuBtnText }}
-
-
-
-
-
-
- 已下单菜品
-
-
-
-
+
+
+
+
+
+ {{ shopCartNumber }}
+
+
+
+
+
+
+
+ {{ selectedSpecsStr }}
+
+
+ {{ skuBtnText }}
+
+
+ {{ skuBtnText }}
+
+
+
+
+
+
+ 已下单菜品
+
+
+
+
diff --git a/pages/test/test.vue b/pages/test/test.vue
index 6e89ea4..9d7dafe 100644
--- a/pages/test/test.vue
+++ b/pages/test/test.vue
@@ -1,65 +1,13 @@
-
-
- 初始化
-
-
- 去下单
-
-
-
-
-
-
-
-
-
-
- 取消
- 确定
-
-
-
-
-
+
+
+
diff --git a/stores/carts.js b/stores/carts.js
index ce77dd9..f692086 100644
--- a/stores/carts.js
+++ b/stores/carts.js
@@ -1,616 +1,657 @@
-import {
- defineStore
-} from 'pinia';
-import yskUtils from 'ysk-utils'
-const {
- OrderPriceCalculator,
- BaseCartItem,
- BackendCoupon,
- ActivityConfig,
- OrderExtraConfig,
- MerchantReductionConfig,
- MerchantReductionType,
- GoodsType
-} = yskUtils
+import { defineStore } from "pinia";
+// import yskUtils from 'ysk-utils'
+// const {
+// OrderPriceCalculator,
+// BaseCartItem,
+// BackendCoupon,
+// ActivityConfig,
+// OrderExtraConfig,
+// MerchantReductionConfig,
+// MerchantReductionType,
+// GoodsType
+// } = yskUtils
+import { OrderPriceCalculator } from "@/utils/goods";
+
+import { ref, computed, reactive, watchEffect, watch } from "vue";
import {
- ref,
- computed,
- reactive,
- watchEffect,
- watch
-} from 'vue';
-import {
- productminiApphotsquery,
- APIgroupquery,
+ productminiApphotsquery,
+ APIgroupquery,
} from "@/common/api/product/product.js";
-export const useCartsStore = defineStore('cart',
- () => {
+export const useCartsStore = defineStore("cart", () => {
+ // 店铺信息
+ const shopInfo = ref(
+ uni.cache.get("shopInfo") || {
+ isMemberPrice: 0,
+ isTableFee: 1,
+ }
+ );
- // 店铺信息
- const shopInfo = ref(uni.cache.get('shopInfo') || {
- isMemberPrice: 0,
- isTableFee: 1
- })
-
- const consumeDiscount=ref({})
+ const consumeDiscount = ref({});
- // 适配工具库 BaseCartItem 接口的商品数据转换函数
- const convertToBaseCartItem = (item) => {
- const skuData = item.skuData ? {
- id: item.skuData.id || item.sku_id,
- salePrice: item.skuData.salePrice || 0,
- memberPrice: item.skuData.memberPrice || 0
- } :
- undefined;
+ // 适配工具库 BaseCartItem 接口的商品数据转换函数
+ const convertToBaseCartItem = (item) => {
+ const skuData = item.skuData
+ ? {
+ id: item.skuData.id || item.sku_id,
+ salePrice: item.skuData.salePrice || 0,
+ memberPrice: item.skuData.memberPrice || 0,
+ }
+ : undefined;
- const goods = getProductDetails({
- ...item,
- product_id: item.product_id || item.productId,
- sku_id:item.skuId||item.sku_id
- })
- return {
- ...item,
- id: item.id,
- sku_id:item.skuId||item.sku_id,
- product_id: item.product_id || item.productId,
- salePrice: item.salePrice || item.price,
- number: item.number || item.num || 0,
- product_type: item.productType,
- is_temporary: !!(item.is_temporary || item.isTemporary),
- is_gift: !!(item.is_gift || item.isGift),
- returnNum: item.returnNum || 0,
- memberPrice: item.memberPrice || 0,
- discountSaleAmount: item.discount_sale_amount || item.discountSaleAmount || 0,
- packFee: item.packFee || (goods?goods.packFee:0) || 0,
- packNumber: item.pack_number || item.packNumber || 0,
- activityInfo: item.activityInfo ? {
- type: item.activityInfo.type,
- discountRate: OrderPriceCalculator.formatDiscountRate(item.activityInfo.discountRate),
- vipPriceShare: !!item.activityInfo.vipPriceShare
- } : undefined,
- skuData
- };
- };
+ const goods = getProductDetails({
+ ...item,
+ product_id: item.product_id || item.productId,
+ sku_id: item.skuId || item.sku_id,
+ });
+ return {
+ ...item,
+ id: item.id,
+ sku_id: item.skuId || item.sku_id,
+ product_id: item.product_id || item.productId,
+ salePrice: item.salePrice || item.price,
+ number: item.number || item.num || 0,
+ product_type: item.productType,
+ is_temporary: !!(item.is_temporary || item.isTemporary),
+ is_gift: !!(item.is_gift || item.isGift),
+ returnNum: item.returnNum || 0,
+ memberPrice: item.memberPrice || 0,
+ discountSaleAmount:
+ item.discount_sale_amount || item.discountSaleAmount || 0,
+ packFee: item.packFee || (goods ? goods.packFee : 0) || 0,
+ packNumber: item.pack_number || item.packNumber || 0,
+ activityInfo: item.activityInfo
+ ? {
+ type: item.activityInfo.type,
+ discountRate: OrderPriceCalculator.formatDiscountRate(
+ item.activityInfo.discountRate
+ ),
+ vipPriceShare: !!item.activityInfo.vipPriceShare,
+ }
+ : undefined,
+ skuData,
+ };
+ };
- // 合并所有商品列表
- const allGoods = ref([])
+ // 合并所有商品列表
+ const allGoods = ref([]);
- function getAllGoodsList() {
- const currentGoods = (carts.value).map(convertToBaseCartItem);
- const giftGoods = [].map(convertToBaseCartItem);
- // 扁平化历史订单商品
- const oldOrderGoods = Object.values(oldOrder.value.detailMap || {})
- .flat()
- .map(convertToBaseCartItem);
- return [...currentGoods, ...giftGoods, ...oldOrderGoods];
- }
+ function getAllGoodsList() {
+ const currentGoods = carts.value.map(convertToBaseCartItem);
+ const giftGoods = [].map(convertToBaseCartItem);
+ // 扁平化历史订单商品
+ const oldOrderGoods = Object.values(oldOrder.value.detailMap || {})
+ .flat()
+ .map(convertToBaseCartItem);
+ return [...currentGoods, ...giftGoods, ...oldOrderGoods];
+ }
+ // 就餐类型 'dine-in' | 'take-out'
- // 就餐类型 'dine-in' | 'take-out'
+ let dinnerType = ref("dine-in");
- let dinnerType = ref('dine-in');
+ function setDinnerType(str) {
+ dinnerType.value = str;
+ }
+ //餐位费配置
- function setDinnerType(str) {
- dinnerType.value = str
- }
- //餐位费配置
+ const dinersNum = uni.cache.get("dinersNum") || 0;
+ const seatFeeConfig = ref({
+ pricePerPerson: shopInfo.value.tableFee || 0,
+ personCount: dinersNum || 0,
+ isEnabled: !shopInfo.value.isTableFee,
+ });
+ watch(
+ () => shopInfo.value.isTableFee,
+ (newval) => {
+ seatFeeConfig.value.isEnabled = !shopInfo.value.isTableFee;
+ seatFeeConfig.value.pricePerPerson = shopInfo.value.tableFee || 0;
+ },
+ {
+ deep: true,
+ }
+ );
- const dinersNum = uni.cache.get('dinersNum') || 0
- const seatFeeConfig = ref({
- pricePerPerson: shopInfo.value.tableFee || 0,
- personCount: dinersNum||0,
- isEnabled: !shopInfo.value.isTableFee
- })
- watch(() => shopInfo.value.isTableFee, (newval) => {
- seatFeeConfig.value.isEnabled = !shopInfo.value.isTableFee
- seatFeeConfig.value.pricePerPerson = shopInfo.value.tableFee||0
- },{
- deep:true
- })
+ function setSeatFeeConfig(key, val) {
+ seatFeeConfig.value[key] = val;
+ }
+ watch(
+ () => seatFeeConfig.value,
+ (newval) => {
+ console.log("seatFeeConfig", seatFeeConfig.value);
+ },
+ {
+ deep: true,
+ }
+ );
- function setSeatFeeConfig(key, val) {
- seatFeeConfig.value[key] = val;
- }
- watch(() => seatFeeConfig.value, (newval) => {
- console.log('seatFeeConfig', seatFeeConfig.value);
- }, {
- deep: true
- })
+ //积分规则
+ const pointDeductionRule = ref({
+ pointsPerYuan: 100,
+ maxDeductionAmount: Infinity,
+ });
- //积分规则
- const pointDeductionRule = ref({
- pointsPerYuan: 100,
- maxDeductionAmount: Infinity
- })
+ function setPointDeductionRule(pointsPerYuan, maxDeductionAmount) {
+ pointDeductionRule.value.pointsPerYuan = pointsPerYuan;
+ pointDeductionRule.value.maxDeductionAmount = maxDeductionAmount;
+ }
- function setPointDeductionRule(pointsPerYuan, maxDeductionAmount) {
- pointDeductionRule.value.pointsPerYuan = pointsPerYuan
- pointDeductionRule.value.maxDeductionAmount = maxDeductionAmount
- }
+ // 初始配置:默认无减免(固定金额 0 元)
+ const merchantReductionConfig = ref({
+ type: "fixed_amount",
+ fixedAmount: 0,
+ });
+ //使用积分数量
+ const userPoints = ref(0);
- // 初始配置:默认无减免(固定金额 0 元)
- const merchantReductionConfig = ref({
- type: 'fixed_amount',
- fixedAmount: 0
- });
- //使用积分数量
- const userPoints = ref(0);
+ function setUserPoints(newval) {
+ userPoints.value = newval;
+ }
- function setUserPoints(newval) {
- userPoints.value = newval
- }
+ //新客立减
+ const newUserDiscount = ref(0);
+ //满减活动
+ const fullReductionActivities = ref([]);
+ //是否使用霸王餐
+ const isFreeDine = ref(false);
+ // 商家霸王餐配置
+ const freeDineConfig = ref(null);
+ // 订单额外配置
+ const orderExtraConfig = computed(() => ({
+ // 引用扩展后的商家减免配置
+ merchantReduction: merchantReductionConfig.value,
+ additionalFee: 0,
+ pointDeductionRule: pointDeductionRule.value,
+ seatFeeConfig: seatFeeConfig.value,
+ currentStoreId: "",
+ userPoints: userPoints.value,
+ isMember: useVipPrice.value,
+ memberDiscountRate: 1,
+ newUserDiscount: newUserDiscount.value,
+ fullReductionActivities: fullReductionActivities.value,
+ currentDinnerType: dinnerType.value,
+ isFreeDine: isFreeDine.value,
+ freeDineConfig: freeDineConfig.value,
+ }));
+ // 营销活动列表
+ const activityList = computed(() => {
+ return [];
+ });
+ // 优惠券列表
+ const backendCoupons = ref([]);
- //新客立减
- const newUserDiscount = ref(0);
- // 订单额外配置
- const orderExtraConfig = computed(() => ({
- // 引用扩展后的商家减免配置
- merchantReduction: merchantReductionConfig.value,
- additionalFee: 0,
- pointDeductionRule: pointDeductionRule.value,
- seatFeeConfig: seatFeeConfig.value,
- currentStoreId: '',
- userPoints: userPoints.value,
- isMember: useVipPrice.value,
- memberDiscountRate: 1,
- newUserDiscount: newUserDiscount.value
- }));
+ function setCoupons(cps) {
+ console.log("setCoupons", cps);
+ backendCoupons.value = cps;
+ }
+ // 商品加入购物车顺序
+ const cartOrder = ref({});
+ // 订单费用汇总
+ const orderCostSummary = computed(() => {
+ allGoods.value = getAllGoodsList();
+ console.log("orderExtraConfig.value", orderExtraConfig.value);
+ const costSummary = OrderPriceCalculator.calculateOrderCostSummary(
+ allGoods.value,
+ dinnerType.value,
+ backendCoupons.value,
+ activityList.value,
+ orderExtraConfig.value,
+ cartOrder.value,
+ new Date()
+ );
+ console.log(" 订单费用汇总", costSummary);
+ return costSummary;
+ });
- // 营销活动列表
- const activityList = computed(() => {
- return [];
- });
- // 优惠券列表
- const backendCoupons = ref([])
+ const goodsIsloading = ref(true);
- function setCoupons(cps) {
- console.log('setCoupons', cps);
- backendCoupons.value = cps;
- }
- // 商品加入购物车顺序
- const cartOrder = ref({});
- // 订单费用汇总
- const orderCostSummary = computed(() => {
- allGoods.value = getAllGoodsList()
- console.log('allGoods.value',allGoods.value);
- const costSummary = OrderPriceCalculator.calculateOrderCostSummary(
- allGoods.value,
- dinnerType.value,
- backendCoupons.value,
- activityList.value,
- orderExtraConfig.value,
- cartOrder.value,
- new Date()
- );
- return costSummary;
- });
+ //商品数据Map
+ const goodsMap = reactive({});
+ //获取商品数据
+ async function goodsInit() {
+ goodsIsloading.value = true;
+ //获取招牌菜商品
+ const hotres = await productminiApphotsquery();
+ for (let product of hotres) {
+ setGoodsMap(product.id, product);
+ }
+ //获取分组商品
+ const groupres = await APIgroupquery();
+ for (let group of groupres) {
+ for (let product of group.productList) {
+ setGoodsMap(product.id, product);
+ }
+ }
+ allGoods.value = getAllGoodsList();
+ console.log("allGoods.value ", allGoods.value);
+ goodsIsloading.value = false;
+ }
- const goodsIsloading = ref(true);
+ function setGoodsMap(product_id, data) {
+ goodsMap[product_id] = data;
+ }
- //商品数据Map
- const goodsMap = reactive({})
- //获取商品数据
- async function goodsInit() {
- goodsIsloading.value = true;
- //获取招牌菜商品
- const hotres = await productminiApphotsquery();
- for (let product of hotres) {
- setGoodsMap(product.id, product)
- }
- //获取分组商品
- const groupres = await APIgroupquery()
- for (let group of groupres) {
- for (let product of group.productList) {
- setGoodsMap(product.id, product)
- }
- }
- allGoods.value = getAllGoodsList()
- console.log('allGoods.value ',allGoods.value );
- goodsIsloading.value = false
- }
+ //websocket回执
+ const websocketsendMessage = (data) => {
+ uni.$u.debounce(sendMessage(data), 500);
+ };
- function setGoodsMap(product_id, data) {
- goodsMap[product_id] = data;
- }
+ const isLoading = ref(true);
- //websocket回执
- const websocketsendMessage = (data) => {
- uni.$u.debounce(sendMessage(data), 500)
- }
+ function getProductDetails(v) {
+ const goods = goodsMap[v.product_id];
+ if (!goods) {
+ return undefined;
+ }
+ let skuData = undefined;
+ skuData = goods?.skuList.find((sku) => sku.id == v.sku_id);
- const isLoading = ref(true);
+ if (skuData) {
+ return {
+ ...v,
+ productId: v.product_id,
+ salePrice: skuData ? skuData.salePrice : 0,
+ price: skuData ? skuData.salePrice : 0,
+ memberPrice: skuData ? skuData.memberPrice : 0,
+ coverImg: goods.coverImg,
+ productImg: goods.coverImg,
+ name: goods.name,
+ productName: goods.name,
+ specInfo: skuData.specInfo,
+ packFee: goods.packFee || 0,
+ type: goods.type,
+ skuData,
+ skuName: skuData.name,
+ num: v.number * 1,
+ };
+ } else {
+ return undefined;
+ }
+ }
+ // 用于记录已经处理过的消息的 msg_id
+ const processedMessageIds = new Set();
- function getProductDetails(v) {
- const goods = goodsMap[v.product_id]
- if (!goods) {
- return undefined
- }
- let skuData = undefined;
- skuData = goods?.skuList.find((sku) => sku.id == v.sku_id);
-
- if (skuData) {
- return {
- ...v,
- productId: v.product_id,
- salePrice: skuData ? skuData.salePrice : 0,
- price: skuData ? skuData.salePrice : 0,
- memberPrice: skuData ? skuData.memberPrice : 0,
- coverImg: goods.coverImg,
- productImg: goods.coverImg,
- name: goods.name,
- productName: goods.name,
- specInfo: skuData.specInfo,
- packFee: goods.packFee || 0,
- type: goods.type,
- skuData,
- skuName: skuData.name,
- num: v.number * 1
- }
- } else {
- return undefined
- }
- }
- // 用于记录已经处理过的消息的 msg_id
- const processedMessageIds = new Set();
+ //购物车商品信息补全初始化
+ function cartsGoodsInfoInit(arr) {
+ carts.value = arr
+ .map((v) => {
+ return getProductDetails(v);
+ })
+ .filter((v) => v);
+ }
- //购物车商品信息补全初始化
- function cartsGoodsInfoInit(arr) {
- carts.value = arr.map(v => {
- return getProductDetails(v)
- }).filter(v => v)
- }
+ //收到socket消息时对购物车进行处理
+ async function onMessage(Message) {
+ if (Message) {
+ // 心跳返回 过滤
+ if (
+ Message.type == "ping_interval" ||
+ Message.msg_id == "ping_interval"
+ ) {
+ isLoading.value = false;
+ return false;
+ }
+ // 检查消息是否已经处理过
+ if (processedMessageIds.has(Message.msg_id)) {
+ return;
+ }
+ processedMessageIds.add(Message.msg_id);
+ const msgData = Message.data;
+ // 初始化
+ if (Message.operate_type == "init") {
+ console.log("carts init", msgData);
+ cartsGoodsInfoInit(msgData);
+ uni.hideLoading();
+ isLoading.value = false;
+ }
+ // 清空购物车
+ if (Message.operate_type == "cleanup") {
+ carts.value = [];
+ }
- //收到socket消息时对购物车进行处理
- async function onMessage(Message) {
- if (Message) {
- // 心跳返回 过滤
- if (Message.type == "ping_interval" || Message.msg_id == "ping_interval") {
- isLoading.value = false;
- return false
- }
- // 检查消息是否已经处理过
- if (processedMessageIds.has(Message.msg_id)) {
- return;
- }
- processedMessageIds.add(Message.msg_id);
- const msgData = Message.data
- // 初始化
- if (Message.operate_type == "init") {
- console.log('carts init', msgData);
- cartsGoodsInfoInit(msgData)
- uni.hideLoading();
- isLoading.value = false;
- }
+ // 删除除购物车
+ if (Message.operate_type == "del" && Message.status == 1) {
+ // 优化:使用可选链操作符避免报错
+ carts.value = carts.value.filter((item) => item.id !== msgData?.id);
+ }
- // 清空购物车
- if (Message.operate_type == 'cleanup') {
- carts.value = []
- }
+ // 添加或者减少购物后返回
+ if (Message.operate_type == "add" || Message.operate_type == "edit") {
+ const index = carts.value.findIndex((v) => v.id == msgData.id);
+ if (index !== -1) {
+ carts.value[index] = getProductDetails(msgData);
+ } else {
+ carts.value.push(getProductDetails(msgData));
+ }
+ }
- // 删除除购物车
- if (Message.operate_type == 'del' && Message.status == 1) {
- // 优化:使用可选链操作符避免报错
- carts.value = carts.value.filter(item => item.id !== msgData?.id);
- }
+ // 历史订单
+ if (Message.operate_type == "clearOrder") {
+ }
- // 添加或者减少购物后返回
- if (Message.operate_type == 'add' || Message.operate_type == 'edit') {
- const index = carts.value.findIndex((v => v.id == msgData.id))
- if (index !== -1) {
- carts.value[index] = getProductDetails(msgData);
- } else {
- carts.value.push(getProductDetails(msgData));
- }
- }
+ // 购物车数据更新从新请求
+ if (
+ Message.type == "product" &&
+ Message.data_type == "product_update" &&
+ Message.operate_type == "product_update"
+ ) {
+ await goodsInit();
+ await cartsGoodsInfoInit();
+ }
- // 历史订单
- if (Message.operate_type == 'clearOrder') {}
+ // 提示
+ if (Message.status == 0 && Message.type != "no_suit_num") {
+ uni.showToast({
+ title: Message.msg,
+ icon: "none",
+ });
+ }
- // 购物车数据更新从新请求
- if (Message.type == 'product' && Message.data_type == 'product_update' && Message
- .operate_type == 'product_update') {
- await goodsInit()
- await cartsGoodsInfoInit()
- }
+ if (Message.type == "no_suit_num") {
+ uni.showModal({
+ title: "提示",
+ showCancel: false,
+ content: "此商品库存不足起售数量!",
+ success: async (data) => {},
+ });
+ }
+ }
+ }
- // 提示
- if (Message.status == 0 && Message.type != 'no_suit_num') {
- uni.showToast({
- title: Message.msg,
- icon: "none"
- })
- }
+ //购物车数据
+ const carts = ref([]);
- if (Message.type == 'no_suit_num') {
- uni.showModal({
- title: '提示',
- showCancel: false,
- content: '此商品库存不足起售数量!',
- success: async (data) => {
+ //历史订单数据
+ const oldOrder = ref({
+ detailMap: {},
+ originAmount: 0,
+ });
- }
- });
- }
- }
- }
+ function setOldOrder(data) {
+ oldOrder.value = data;
+ allGoods.value = getAllGoodsList();
+ }
+ // 会员信息
+ const orderVIP = ref(
+ uni.cache.get("orderVIP") || {
+ isVip: false,
+ }
+ );
- //购物车数据
- const carts = ref([])
+ function updateData(key, newval) {
+ if (key === "orderVIP") {
+ uni.cache.set("orderVIP", newval);
+ return (orderVIP.value = newval);
+ }
+ if (key === "shopInfo") {
+ uni.cache.set("shopInfo", newval);
+ return (shopInfo.value = newval);
+ }
+ }
- //历史订单数据
- const oldOrder = ref({
- detailMap: {},
- originAmount: 0
- })
+ //是否使用会员价
+ const useVipPrice = computed(() => {
+ const isUse =
+ orderVIP.value.isVip && shopInfo.value.isMemberPrice ? true : false;
+ return isUse;
+ });
- function setOldOrder(data) {
- oldOrder.value = data
- allGoods.value=getAllGoodsList()
- }
+ function currentCalcMpneyNumber(item) {
+ const n = item.number - (item.returnNum || 0);
+ return n <= 0 ? 0 : n;
+ }
+ //历史订单商品价格总和
+ const oldOrderMoney = computed(() => {
+ let total = 0;
+ for (let i in oldOrder.value.detailMap) {
+ total += oldOrder.value.detailMap[i].reduce((prve, cur) => {
+ if (cur.isGift) {
+ return prve + 0;
+ }
+ const discount_sale_amount = cur.discount_sale_amount * 1 || 0;
+ const memberPrice = cur.skuData
+ ? cur.skuData.memberPrice || cur.skuData.salePrice
+ : 0;
+ const price = discount_sale_amount || cur.salePrice || 0;
+ const number = currentCalcMpneyNumber(cur);
+ return (
+ prve +
+ (number <= 0 ? 0 : number) *
+ (discount_sale_amount || (useVipPrice.value ? memberPrice : price))
+ );
+ }, 0);
+ }
+ return total;
+ });
+ //当前购物车总价格
+ const totalPrice = computed(() => {
+ const money = carts.value.reduce((prve, cur) => {
+ const memberPrice = cur.memberPrice || cur.salePrice;
+ const price = useVipPrice.value ? memberPrice : cur.salePrice;
+ const curMoney = price * currentCalcMpneyNumber(cur);
+ return prve + curMoney;
+ }, 0);
+ return money;
+ });
+ // 霸王餐购物车原价,不享受任何优惠
+ const totalOriginPrice = computed(() => {
+ const money = carts.value.reduce((prve, cur) => {
+ const curMoney = cur.salePrice * currentCalcMpneyNumber(cur);
+ return prve + curMoney;
+ }, 0);
+ return money;
+ });
- // 会员信息
- const orderVIP = ref(uni.cache.get('orderVIP') || {
- isVip: false
- })
+ //返回打包数量(称重商品打包数量最大为1)
+ function returnCartPackNumber(cur) {
+ const maxReturnNum = cur.number - (cur.returnNum || 0);
+ let pack_number = cur.number;
+ pack_number =
+ cur.product_type == "weight" && pack_number > 1 ? 1 : pack_number;
+ pack_number = Math.min(maxReturnNum, pack_number);
+ pack_number = pack_number <= 0 ? 0 : pack_number;
+ return pack_number * 1;
+ }
- function updateData(key, newval) {
- if (key === 'orderVIP') {
- uni.cache.set('orderVIP', newval)
- return orderVIP.value = newval
- }
- if (key === 'shopInfo') {
- uni.cache.set('shopInfo', newval)
- return shopInfo.value = newval
- }
- }
+ //当前购物车打包费
+ const totalPackFee = computed(() => {
+ const money = carts.value.reduce((prve, cur) => {
+ const curMoney = (cur.packFee || 0) * currentCalcMpneyNumber(cur);
+ return prve + curMoney;
+ }, 0);
+ return money;
+ });
+ //打包费
+ const packFee = computed(() => {
+ const nowPackFee = carts.value.reduce((acc, cur) => {
+ return acc + (cur.packFee || 0) * returnCartPackNumber(cur);
+ }, 0);
+ let oldPackfee = 0;
+ for (let i in oldOrder.value.detailMap) {
+ oldPackfee += oldOrder.value.detailMap[i].reduce((prve, cur) => {
+ return prve + (cur.packFee || 0) * returnCartPackNumber(cur);
+ }, 0);
+ }
+ return nowPackFee + oldPackfee;
+ });
+ //购物车是否为空
+ const isEmpty = computed(() => {
+ return !carts.value || carts.value.length <= 0;
+ });
+ // 计算向上取整
+ const roundUpToTwoDecimals = (num, i) => {
+ // 先将数字乘以 100 并转换为字符串保留足够的小数位
+ let temp = (num * 100).toFixed(10);
+ // 向上取整
+ let rounded = null;
+ if (i == "upward") {
+ rounded = Math.ceil(parseFloat(temp));
+ } else {
+ rounded = Math.floor(parseFloat(temp));
+ }
+ // 再除以 100 得到保留两位小数的结果
+ return rounded / 100;
+ };
- //是否使用会员价
- const useVipPrice = computed(() => {
- const isUse = (orderVIP.value.isVip && shopInfo.value.isMemberPrice) ? true : false
- return isUse
- })
+ // 精确计算函数
+ const preciseCalculation = (num1, num2) => {
+ // 将数字转换为整数,乘以 100 以保留两位小数
+ const int1 = BigInt(Math.round(num1 * 100));
+ const int2 = BigInt(Math.round(num2 * 100));
+ // 执行乘法运算
+ const result = int1 * int2;
+ // 再除以 10000 以还原为原来的小数
+ return Number(result) / 10000;
+ };
- function currentCalcMpneyNumber(item) {
- const n = item.number - (item.returnNum || 0)
- return n <= 0 ? 0 : n
- }
- //历史订单商品价格总和
- const oldOrderMoney = computed(() => {
- let total = 0
- for (let i in oldOrder.value.detailMap) {
- total += oldOrder.value.detailMap[i].reduce((prve, cur) => {
- if (cur.isGift) {
- return prve + 0
- }
- const discount_sale_amount = cur.discount_sale_amount * 1 || 0
- const memberPrice = cur.skuData ? (cur.skuData.memberPrice || cur.skuData
- .salePrice) : 0
- const price = (discount_sale_amount || cur.salePrice || 0)
- const number = currentCalcMpneyNumber(cur)
- return prve + (number <= 0 ? 0 : number) * (discount_sale_amount || (useVipPrice
- .value ? memberPrice : price))
- }, 0)
- }
- return total
- })
+ // 计算购物车商品总价格
+ const getTotalTotalPrices = (matchedProducts, isBwc = true) => {
+ if (!matchedProducts || !Array.isArray(matchedProducts)) {
+ return 0;
+ }
+ // 购物车总数价格
+ let cart = matchedProducts.reduce((total, item) => {
+ if (isBwc === true) {
+ return (
+ total + parseFloat(item.price) * parseFloat(item.num - item.returnNum)
+ );
+ }
+ // 是否启用会员价 0否1是
+ if (useVipPrice.value) {
+ // memberPrice会员价
+ return (
+ total +
+ parseFloat(item.memberPrice || item.price) *
+ parseFloat(item.num - item.returnNum)
+ );
+ } else {
+ // salePrice销售价
+ return (
+ total + parseFloat(item.price) * parseFloat(item.num - item.returnNum)
+ );
+ }
+ }, 0);
- //当前购物车总价格
- const totalPrice = computed(() => {
- const money = carts.value.reduce((prve, cur) => {
- const memberPrice = cur.memberPrice || cur.salePrice
- const price = useVipPrice.value ? memberPrice : cur.salePrice;
- const curMoney = price * currentCalcMpneyNumber(cur)
- return prve + curMoney
- }, 0)
- return money
- })
- // 霸王餐购物车原价,不享受任何优惠
- const totalOriginPrice = computed(() => {
- const money = carts.value.reduce((prve, cur) => {
- const curMoney = cur.salePrice * currentCalcMpneyNumber(cur)
- return prve + curMoney
- }, 0)
- return money
- })
+ cart = cart.toFixed(2);
+ return parseFloat(cart);
+ };
- //返回打包数量(称重商品打包数量最大为1)
- function returnCartPackNumber(cur) {
- const maxReturnNum = cur.number - (cur.returnNum || 0);
- let pack_number = cur.number;
- pack_number = (cur.product_type == 'weight' && pack_number > 1) ? 1 : pack_number;
- pack_number = Math.min(maxReturnNum, pack_number);
- pack_number = pack_number <= 0 ? 0 : pack_number
- return pack_number * 1
- }
+ // 计算商品卷所选择的总价格
+ const getTotalProductroll = (matchedProducts) =>
+ computed(() => {
+ if (!matchedProducts || !Array.isArray(matchedProducts)) {
+ return 0;
+ }
+ // 购物车总数价格
+ let cart = matchedProducts.reduce((total, item) => {
+ // 是否启用会员价 0否1是
+ if (useVipPrice.value) {
+ // memberPrice会员价
+ return total + parseFloat(item.memberPrice || item.price);
+ } else {
+ // salePrice销售价
+ return total + parseFloat(item.price);
+ }
+ }, 0);
+ // 向上取整并保留两位小数
+ let result = roundUpToTwoDecimals(cart, "upward");
+ return result;
+ });
+ // 桌位置
+ const getTotalSeatcharge = (seatNum) =>
+ computed(() => {
+ // 是否免除桌位费 0 否 1 是
+ let cart = 0;
- //当前购物车打包费
- const totalPackFee = computed(() => {
- const money = carts.value.reduce((prve, cur) => {
- const curMoney = (cur.packFee || 0) * currentCalcMpneyNumber(cur)
- return prve + curMoney
- }, 0)
- return money
- })
+ if (uni.cache.get("ordershopUserInfo").isTableFee == 0 && seatNum) {
+ cart =
+ parseFloat(seatNum) *
+ parseFloat(uni.cache.get("ordershopUserInfo").tableFee);
+ }
+ // 向下取整并保留两位小数
+ let result = roundUpToTwoDecimals(cart, "downward");
+ return result;
+ });
- //打包费
- const packFee = computed(() => {
- const nowPackFee = carts.value.reduce((acc, cur) => {
- return acc + (cur.packFee || 0) * returnCartPackNumber(cur)
- }, 0)
- let oldPackfee = 0;
- for (let i in oldOrder.value.detailMap) {
- oldPackfee += oldOrder.value.detailMap[i].reduce((prve, cur) => {
- return prve + (cur.packFee || 0) * returnCartPackNumber(cur)
- }, 0)
- }
- return nowPackFee + oldPackfee
- })
+ // 计算购物车总打包费用(向下取整并保留两位小数)
+ const getTotalPackFee = (cartList) =>
+ computed(() => {
+ const total = cartList.reduce((sum, item) => {
+ return (
+ sum +
+ parseFloat(item.packAmount) *
+ (parseFloat(item.packNumber) ||
+ parseFloat(item.num) - parseFloat(item.returnNum))
+ );
+ }, 0);
+ // 向下取整并保留两位小数
+ let result = roundUpToTwoDecimals(total, "downward");
+ return result;
+ });
- //购物车是否为空
- const isEmpty = computed(() => {
- return !carts.value || carts.value.length <= 0
- })
- // 计算向上取整
- const roundUpToTwoDecimals = (num, i) => {
- // 先将数字乘以 100 并转换为字符串保留足够的小数位
- let temp = (num * 100).toFixed(10);
- // 向上取整
- let rounded = null
- if (i == 'upward') {
- rounded = Math.ceil(parseFloat(temp));
- } else {
- rounded = Math.floor(parseFloat(temp));
- }
- // 再除以 100 得到保留两位小数的结果
- return rounded / 100;
- };
+ function clearOrderConfig() {
+ console.log("clearOrderConfig");
+ backendCoupons.value = [];
+ allGoods.value = [];
+ carts.value = [];
+ seatFeeConfig.value.personCount = uni.cache.get("dinersNum") || 0;
+ dinnerType.value = "dine-in";
+ userPoints.value = 0;
+ fullReductionActivities.value = [];
+ isFreeDine.value = false;
+ }
- // 精确计算函数
- const preciseCalculation = (num1, num2) => {
- // 将数字转换为整数,乘以 100 以保留两位小数
- const int1 = BigInt(Math.round(num1 * 100));
- const int2 = BigInt(Math.round(num2 * 100));
- // 执行乘法运算
- const result = int1 * int2;
- // 再除以 10000 以还原为原来的小数
- return Number(result) / 10000;
- };
-
- // 计算购物车商品总价格
- const getTotalTotalPrices = (matchedProducts, isBwc = true) => {
- if (!matchedProducts || !Array.isArray(matchedProducts)) {
- return 0;
- }
- // 购物车总数价格
- console.log('isBwc');
- console.log(isBwc);
- let cart = matchedProducts.reduce((total, item) => {
- if (isBwc === true) {
- return total + (parseFloat(item.price) * parseFloat(item.num - item.returnNum));
- }
- // 是否启用会员价 0否1是
- if (useVipPrice.value) {
- // memberPrice会员价
- return total + (parseFloat(item.memberPrice || item.price) * parseFloat(item
- .num - item
- .returnNum));
- } else {
- // salePrice销售价
- return total + (parseFloat(item.price) * parseFloat(item.num - item.returnNum));
- }
- }, 0);
-
- cart = cart.toFixed(2)
- console.log(parseFloat(cart))
- return parseFloat(cart);
- };
-
- // 计算商品卷所选择的总价格
- const getTotalProductroll = (matchedProducts) => computed(() => {
- if (!matchedProducts || !Array.isArray(matchedProducts)) {
- return 0;
- }
- // 购物车总数价格
- let cart = matchedProducts.reduce((total, item) => {
- // 是否启用会员价 0否1是
- if (useVipPrice.value) {
- // memberPrice会员价
- return total + parseFloat(item.memberPrice || item.price)
- } else {
- // salePrice销售价
- return total + parseFloat(item.price)
- }
- }, 0);
- // 向上取整并保留两位小数
- let result = roundUpToTwoDecimals(cart, 'upward')
- return result;
- });
-
- // 桌位置
- const getTotalSeatcharge = (seatNum) => computed(() => {
- // 是否免除桌位费 0 否 1 是
- let cart = 0
-
- if (uni.cache.get('ordershopUserInfo').isTableFee == 0 && seatNum) {
- cart = parseFloat(seatNum) * parseFloat(uni.cache.get('ordershopUserInfo').tableFee)
- }
- // 向下取整并保留两位小数
- let result = roundUpToTwoDecimals(cart, 'downward')
- return result;
- });
-
- // 计算购物车总打包费用(向下取整并保留两位小数)
- const getTotalPackFee = (cartList) => computed(() => {
- const total = cartList.reduce((sum, item) => {
- return sum + (parseFloat(item.packAmount) * (parseFloat(item.packNumber) || (
- parseFloat(item.num) - parseFloat(item.returnNum))));
- }, 0);
- // 向下取整并保留两位小数
- let result = roundUpToTwoDecimals(total, 'downward')
- return result;
- });
-
-
- function clearOrderConfig(){
- console.log('clearOrderConfig');
- backendCoupons.value=[];
- allGoods.value=[];
- carts.value=[]
- seatFeeConfig.value.personCount=uni.cache.get('dinersNum') || 0;
- dinnerType.value='dine-in';
- userPoints.value=0
- }
-
-
- return {
- getTotalPackFee,
- getTotalSeatcharge,
- getTotalTotalPrices,
- getTotalProductroll,
- carts,
- isEmpty,
- setGoodsMap,
- goodsMap,
- goodsIsloading,
- goodsInit,
- onMessage,
- totalPrice,
- totalPackFee,
- updateData,
- useVipPrice,
- totalOriginPrice,
- orderCostSummary,
- setCoupons,
- userPoints,
- setUserPoints,
- setPointDeductionRule,
- setOldOrder,
- //优惠券列表
- backendCoupons,
- allGoods,
- //就餐类型
- dinnerType,
- setDinnerType,
- setSeatFeeConfig,
- seatFeeConfig,
- shopInfo,
- //新客立减数据
- consumeDiscount,
- //新客立减金额
- newUserDiscount,
- getAllGoodsList,
- //清空配置
- clearOrderConfig
-
- };
- }
-);
\ No newline at end of file
+ return {
+ getTotalPackFee,
+ getTotalSeatcharge,
+ getTotalTotalPrices,
+ getTotalProductroll,
+ carts,
+ isEmpty,
+ setGoodsMap,
+ goodsMap,
+ goodsIsloading,
+ goodsInit,
+ onMessage,
+ totalPrice,
+ totalPackFee,
+ updateData,
+ useVipPrice,
+ totalOriginPrice,
+ orderCostSummary,
+ setCoupons,
+ userPoints,
+ setUserPoints,
+ setPointDeductionRule,
+ setOldOrder,
+ //优惠券列表
+ backendCoupons,
+ allGoods,
+ //就餐类型
+ dinnerType,
+ setDinnerType,
+ setSeatFeeConfig,
+ seatFeeConfig,
+ shopInfo,
+ //新客立减数据
+ consumeDiscount,
+ //新客立减金额
+ newUserDiscount,
+ getAllGoodsList,
+ //清空配置
+ clearOrderConfig,
+ //满减活动
+ fullReductionActivities,
+ isFreeDine,//是否使用霸王餐
+ // 商家霸王餐配置
+ freeDineConfig,
+ };
+});
diff --git a/stores/pay.js b/stores/pay.js
index c346615..c33a6f8 100644
--- a/stores/pay.js
+++ b/stores/pay.js
@@ -23,10 +23,7 @@ export const Memberpay = defineStore('memberpay', {
return new Promise(async (resolve, reject) => {
try {
let res = await APIpayltPayVip({
- shopId: data.shopId,
- shopUserId: data.shopUserId,
- amount: data.amount,
- activateId: data.activateId,
+ ...data,
// #ifdef MP-WEIXIN
payType: 'wechatPay',
openId: uni.cache.get('userInfo').wechatOpenId,
@@ -35,11 +32,6 @@ export const Memberpay = defineStore('memberpay', {
payType: 'aliPay',
openId: uni.cache.get('userInfo').wechatOpenId,
// #endif
- returnUrl: data.returnUrl ? data.returnUrl : '',
- buyerRemark: data.buyerRemark ? data.buyerRemark : '',
- orderId: data.orderId,
- userAllPack: data.userAllPack,
- seatNum:data.seatNum
})
if (res) {
uni.showLoading({
@@ -67,20 +59,20 @@ export const Memberpay = defineStore('memberpay', {
title: "支付成功"
})
console.log('支付成功')
- setTimeout(() => {
- uni.navigateBack()
- }, 1000)
- resolve(res)
+ // setTimeout(() => {
+ // uni.navigateBack()
+ // }, 1000)
+ resolve(true)
// #endif
// #ifdef MP-ALIPAY
if (res.resultCode == '9000') {
uni.showToast({
title: "支付成功"
})
- setTimeout(() => {
- uni.navigateBack()
- }, 1000)
- resolve(res)
+ // setTimeout(() => {
+ // uni.navigateBack()
+ // }, 1000)
+ resolve(true)
} else {
uni.showToast({
title: "支付失败"
@@ -163,14 +155,14 @@ export const Memberpay = defineStore('memberpay', {
title: "支付成功"
})
console.log('支付成功')
- resolve(res)
+ resolve(true)
// #endif
// #ifdef MP-ALIPAY
if (res.resultCode == '9000') {
uni.showToast({
title: "支付成功"
})
- resolve(res)
+ resolve(true)
} else {
uni.showToast({
title: "支付失败"
diff --git a/stores/user.js b/stores/user.js
index d099c48..eec458a 100644
--- a/stores/user.js
+++ b/stores/user.js
@@ -104,6 +104,7 @@ export const productStore = defineStore('product', {
},
// 扫码请求
scanCodeactions(q) {
+ console.log('扫码内容', q)
return new Promise(async (resolve, reject) => {
if (q) {
console.log(q)
@@ -121,7 +122,7 @@ export const productStore = defineStore('product', {
// 储存卓玛
uni.cache.set('tableCode', tableCode)
if (tableCode) {
- console.log(uni.cache.get('tableCode'));
+ console.log('台桌码', uni.cache.get('tableCode'));
let data = await this.actionsproductqueryShop(tableCode)
console.log('data', data)
@@ -194,6 +195,7 @@ export const productStore = defineStore('product', {
// /通过桌码获取当前店铺信息
actionsproductqueryShop(tableCode) {
+ console.log('台桌码',tableCode);
return new Promise(async (resolve, reject) => {
// try {
try {
@@ -213,7 +215,7 @@ export const productStore = defineStore('product', {
resolve(res)
} else {
- console.log(res)
+ console.error('通过桌码获取当前店铺信息失败' ,res)
reject()
}
diff --git a/utils/goods-utils.js b/utils/goods-utils.js
index f231c1f..c431129 100644
--- a/utils/goods-utils.js
+++ b/utils/goods-utils.js
@@ -295,6 +295,7 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, s
* @param shopInfo 店铺信息
*/
export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoupon, shopInfo) {
+ arr = returnCanDikouGoods(arr, user, shopInfo);
const canDikouGoodsArr = returnCanDikouGoodsArr(arr, selCoupon, user);
if (coupon.type == 2) {
return returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shopInfo);
@@ -423,7 +424,6 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopI
}
let discountPrice = 0;
let hasDiscountGoodsArr = [];
- console.log("returnCouponBuyOneGiveOneDiscount:discountGoods", discountGoods);
if (discountGoods) {
discountPrice = returnGoodsPrice(discountGoods, user, shopInfo);
hasDiscountGoodsArr = [discountGoods];
diff --git a/utils/goods.ts b/utils/goods.ts
new file mode 100644
index 0000000..086e579
--- /dev/null
+++ b/utils/goods.ts
@@ -0,0 +1,1374 @@
+import { BigNumber } from "bignumber.js";
+
+// 配置BigNumber精度
+BigNumber.set({
+ DECIMAL_PLACES: 2,
+ ROUNDING_MODE: BigNumber.ROUND_DOWN, // 向下取整,符合业务需求
+});
+
+/**
+ * 购物车订单价格计算公共库
+ * 功能:覆盖订单全链路费用计算(商品价格、优惠券、积分、餐位费等),支持策略扩展
+ * 小数处理:使用bignumber.js确保精度,统一舍去小数点后两位(如 10.129 → 10.12,15.998 → 15.99)
+ * 扩展设计:优惠券/营销活动采用策略模式,新增类型仅需扩展策略,无需修改核心逻辑
+ * 关键规则:
+ * - 所有优惠券均支持指定门槛商品(后端foods字段:空字符串=全部商品,ID字符串=指定商品ID)
+ * - 与限时折扣/会员价同享规则:开启则门槛计算含对应折扣,关闭则用原价/非会员价
+ * 字段说明:
+ * - BaseCartItem.id:购物车项ID(唯一标识购物车中的条目)
+ * - BaseCartItem.product_id:商品ID(唯一标识商品,用于优惠券/活动匹配)
+ * - BaseCartItem.skuData.id:SKU ID(唯一标识商品规格)
+ */
+
+// ============================ 1. 基础类型定义(核心修正:明确ID含义) ============================
+/** 商品类型枚举 */
+export enum GoodsType {
+ NORMAL = "normal", // 普通商品
+ WEIGHT = "weight", // 称重商品
+ GIFT = "gift", // 赠菜(继承普通商品逻辑,标记用)
+ EMPTY = "", // 空字符串类型(后端未返回时默认归类为普通商品)
+ PACKAGE = "package", // 打包商品(如套餐/预打包商品,按普通商品逻辑处理,可扩展特殊规则)
+}
+
+/** 优惠券计算结果类型(新增细分字段) */
+interface CouponResult {
+ deductionAmount: number; // 抵扣金额
+ excludedProductIds: string[]; // 不适用商品ID列表(注意:是商品ID,非购物车ID)
+ usedCoupon: Coupon | undefined; // 实际使用的优惠券
+ productCouponDeduction: number; // 新增:商品优惠券抵扣(兑换券等)
+ fullCouponDeduction: number; // 新增:满减优惠券抵扣
+}
+
+/** 兑换券计算结果类型(新增细分字段) */
+interface ExchangeCalculationResult {
+ deductionAmount: number;
+ excludedProductIds: string[]; // 不适用商品ID列表(商品ID)
+ productCouponDeduction: number; // 新增:兑换券属于商品券,同步记录
+}
+
+/** 优惠券类型枚举 */
+export enum CouponType {
+ FULL_REDUCTION = "full_reduction", // 满减券
+ DISCOUNT = "discount", // 折扣券
+ SECOND_HALF = "second_half", // 第二件半价券
+ BUY_ONE_GET_ONE = "buy_one_get_one", // 买一送一券
+ EXCHANGE = "exchange", // 商品兑换券
+}
+
+/** 后端返回的优惠券原始字段类型 */
+export interface BackendCoupon {
+ id?: number; // 自增主键(int64)
+ shopId?: number; // 店铺ID(int64)
+ syncId?: number; // 同步Id(int64)
+ type?: number; // 优惠券类型:1-满减券,2-商品兑换券,3-折扣券,4-第二件半价券,5-消费送券,6-买一送一券,7-固定价格券,8-免配送费券
+ name?: string; // 券名称
+ useShopType?: string; // 可用门店类型:only-仅本店;all-所有门店,custom-指定门店
+ useShops?: string; // 可用门店(逗号分隔字符串,如"1,2,3")
+ useType?: string; // 可使用类型:dine堂食/pickup自取/deliv配送/express快递
+ validType?: string; // 有效期类型:fixed(固定时间),custom(自定义时间)
+ validDays?: number; // 有效期(天)
+ validStartTime?: string; // 有效期开始时间(如"2024-01-01 00:00:00")
+ validEndTime?: string; // 有效期结束时间
+ daysToTakeEffect?: number; // 隔天生效
+ useDays?: string; // 可用周期(如"周一,周二")
+ useTimeType?: string; // 可用时间段类型:all-全时段,custom-指定时段
+ useStartTime?: string; // 可用开始时间(每日)
+ useEndTime?: string; // 可用结束时间(每日)
+ getType?: string; // 发放设置:不可自行领取/no,可领取/yes
+ getMode?: string; // 用户领取方式
+ giveNum?: number; // 总发放数量,-10086为不限量
+ getUserType?: string; // 可领取用户:全部/all,新用户一次/new,仅会员/vip
+ getLimit?: number; // 每人领取限量,-10086为不限量
+ useLimit?: number; // 每人每日使用限量,-10086为不限量
+ discountShare?: number; // 与限时折扣同享:0-否,1-是
+ vipPriceShare?: number; // 与会员价同享:0-否,1-是
+ ruleDetails?: string; // 附加规则说明
+ status?: number; // 状态:0-禁用,1-启用
+ useNum?: number; // 已使用数量
+ leftNum?: number; // 剩余数量
+ foods?: string; // 指定门槛商品(逗号分隔字符串,如"101,102",此处为商品ID)
+ fullAmount?: number; // 使用门槛:满多少金额(元)
+ discountAmount?: number; // 使用门槛:减多少金额(元)
+ discountRate?: number; // 折扣%(如90=9折)
+ maxDiscountAmount?: number; // 可抵扣最大金额(元)
+ useRule?: string; // 使用规则:price_asc-价格低到高,price_desc-高到低
+ discountNum?: number; // 抵扣数量
+ otherCouponShare?: number; // 与其它优惠共享:0-否,1-是
+ createTime?: string; // 创建时间
+ updateTime?: string; // 更新时间
+}
+
+/** 营销活动类型枚举 */
+export enum ActivityType {
+ TIME_LIMIT_DISCOUNT = "time_limit_discount", // 限时折扣
+}
+
+/** 基础购物车商品项(核心修正:新增product_id,明确各ID含义) */
+export interface BaseCartItem {
+ id: string | number; // 购物车ID(唯一标识购物车中的条目,如购物车项主键)
+ product_id: string | number; // 商品ID(唯一标识商品,用于优惠券/活动匹配,必选)
+ salePrice: number; // 商品原价(元)
+ number: number; // 商品数量
+ product_type: GoodsType; // 商品类型
+ is_temporary?: boolean; // 是否临时菜(默认false)
+ is_gift?: boolean; // 是否赠菜(默认false)
+ returnNum?: number; // 退货数量(历史订单用,默认0)
+ memberPrice?: number; // 商品会员价(元,优先级:商品会员价 > 会员折扣)
+ discountSaleAmount?: number; // 商家改价后单价(元,优先级最高)
+ packFee?: number; // 单份打包费(元,默认0)
+ packNumber?: number; // 堂食打包数量(默认0)
+ activityInfo?: {
+ // 商品参与的营销活动(如限时折扣)
+ type: ActivityType;
+ discountRate: number; // 折扣率(如0.8=8折)
+ vipPriceShare: boolean; // 是否与会员优惠同享(默认false)
+ };
+ skuData?: {
+ // SKU扩展数据(可选)
+ id: string | number; // SKU ID(唯一标识商品规格,如颜色/尺寸)
+ memberPrice?: number; // SKU会员价
+ salePrice?: number; // SKU原价
+ };
+}
+
+/** 基础优惠券接口(所有券类型继承,包含统一门槛商品字段) */
+export interface BaseCoupon {
+ id: string | number; // 优惠券ID
+ type: CouponType; // 工具库字符串枚举(由后端couponType转换)
+ name: string; // 对应后端title
+ available: boolean; // 基于BackendCoupon字段计算的可用性
+ useShopType?: string; // only-仅本店;all-所有门店,custom-指定门店
+ useShops: string[]; // 可用门店ID列表
+ discountShare: boolean; // 与限时折扣同享:0-否,1-是(后端字段转换为布尔值)
+ vipPriceShare: boolean; // 与会员价同享:0-否,1-是(后端字段转换为布尔值)
+ useType?: string[]; // 可使用类型:dine堂食/pickup自取/deliv配送/express快递
+ isValid: boolean; // 是否在有效期内
+ discountAmount?: number; // 减免金额 (满减券有)
+ fullAmount?: number; // 使用门槛:满多少金额
+ maxDiscountAmount?: number; // 可抵扣最大金额 元
+ applicableProductIds: string[]; // 门槛商品ID列表(空数组=全部商品,非空=指定商品ID)
+}
+
+/** 满减券(适配后端字段) */
+export interface FullReductionCoupon extends BaseCoupon {
+ type: CouponType.FULL_REDUCTION;
+ fullAmount: number; // 对应后端fullAmount(满减门槛)
+ discountAmount: number; // 对应后端discountAmount(减免金额)
+ maxDiscountAmount?: number; // 对应后端maxDiscountAmount(最大减免)
+}
+
+/** 折扣券(适配后端字段) */
+export interface DiscountCoupon extends BaseCoupon {
+ type: CouponType.DISCOUNT;
+ discountRate: number; // 后端discountRate(%)转小数(如90→0.9)
+ maxDiscountAmount: number; // 对应后端maxDiscountAmount(最大减免)
+}
+
+/** 第二件半价券(适配后端字段) */
+export interface SecondHalfPriceCoupon extends BaseCoupon {
+ type: CouponType.SECOND_HALF;
+ maxUseCountPerOrder?: number; // 对应后端useLimit(-10086=不限)
+}
+
+/** 买一送一券(适配后端字段) */
+export interface BuyOneGetOneCoupon extends BaseCoupon {
+ type: CouponType.BUY_ONE_GET_ONE;
+ maxUseCountPerOrder?: number; // 对应后端useLimit(-10086=不限)
+}
+
+/** 商品兑换券(适配后端字段) */
+export interface ExchangeCoupon extends BaseCoupon {
+ type: CouponType.EXCHANGE;
+ deductCount: number; // 对应后端discountNum(抵扣数量)
+ sortRule: "low_price_first" | "high_price_first"; // 后端useRule转换
+}
+
+/** 所有优惠券类型联合 */
+export type Coupon =
+ | FullReductionCoupon
+ | DiscountCoupon
+ | SecondHalfPriceCoupon
+ | BuyOneGetOneCoupon
+ | ExchangeCoupon;
+
+/** 营销活动配置(如限时折扣,applicableProductIds为商品ID列表) */
+export interface ActivityConfig {
+ type: ActivityType;
+ applicableProductIds?: string[]; // 适用商品ID列表(与BaseCartItem.product_id匹配)
+ discountRate: number; // 折扣率(如0.8=8折)
+ vipPriceShare: boolean; // 是否与会员优惠同享
+}
+
+/** 积分抵扣规则 */
+export interface PointDeductionRule {
+ pointsPerYuan: number; // X积分=1元(如100=100积分抵1元)
+ maxDeductionAmount?: number; // 最大抵扣金额(元,默认不限)
+}
+
+/** 餐位费配置 */
+export interface SeatFeeConfig {
+ pricePerPerson: number; // 每人餐位费(元)
+ personCount: number; // 用餐人数(默认1)
+ isEnabled: boolean; // 是否启用餐位费(默认false)
+}
+/** 商家减免类型枚举 */
+export enum MerchantReductionType {
+ FIXED_AMOUNT = "fixed_amount", // 固定金额减免(如直接减 10 元)
+ DISCOUNT_RATE = "discount_rate", // 比例折扣减免(如打 9 折,即减免 10%)
+}
+
+/** 商家减免配置(新增,替代原单一金额字段) */
+export interface MerchantReductionConfig {
+ type: MerchantReductionType; // 减免类型(二选一)
+ fixedAmount?: number; // 固定减免金额(元,仅 FIXED_AMOUNT 生效,≥0)
+ discountRate?: number; // 折扣率(%,仅 DISCOUNT_RATE 生效,0-100,如 90 代表 9 折)
+}
+/**商家霸王餐配置 */
+export interface FreeDineConfig {
+ enable: boolean; //是否开启
+ rechargeThreshold: number; //订单满多少元可以使用
+ rechargeTimes: number; //充值多少倍免单
+ withCoupon: boolean; //与优惠券同享
+ withPoints: boolean; //与积分同享
+ useType?: string[]; //使用类型 dine-in店内 takeout 自取 post快递,takeaway外卖
+ useShopType?: string; //all 全部 part部分
+ shopIdList?: number[]; //可用门店id
+}
+/** 订单额外费用配置 */
+export interface OrderExtraConfig {
+ // merchantReduction: number; // 商家减免金额(元,默认0)
+ // 替换原单一金额字段,支持两种减免形式
+ merchantReduction: MerchantReductionConfig;
+ additionalFee: number; // 附加费(元,如余额充值、券包,默认0)
+ pointDeductionRule: PointDeductionRule; // 积分抵扣规则
+ seatFeeConfig: SeatFeeConfig; // 餐位费配置
+ currentStoreId: string; // 当前门店ID(用于验证优惠券适用门店)
+ userPoints: number; // 用户当前积分(用于积分抵扣)
+ isMember: boolean; // 用户是否会员(用于会员优惠)
+ memberDiscountRate?: number; // 会员折扣率(如0.95=95折,无会员价时用)
+ newUserDiscount?: number; // 新用户减免金额(元,默认0)
+ fullReductionActivities: FullReductionActivity[]; // 当前店铺的满减活动列表(后端返回结构)
+ currentDinnerType: "dine-in" | "take-out" | "take-away" | "post"; // 当前就餐类型(匹配useType)
+ isFreeDine?: boolean; //是否霸王餐
+ freeDineConfig?: FreeDineConfig;
+}
+
+/** 订单费用汇总(修改:补充商家减免类型和明细) */
+export interface OrderCostSummary {
+ // 商品总件数
+ goodsTotal: number;
+ totalDiscountAmount: number;
+ goodsRealAmount: number; // 商品真实原价总和
+ goodsOriginalAmount: number; // 商品原价总和
+ goodsDiscountAmount: number; // 商品折扣金额
+ couponDeductionAmount: number; // 优惠券总抵扣
+ productCouponDeduction: number; // 商品优惠券抵扣
+ fullCouponDeduction: number; // 满减优惠券抵扣
+ pointDeductionAmount: number; // 积分抵扣金额
+ seatFee: number; // 餐位费
+ packFee: number; // 打包费
+ scoreMaxMoney: number; // 积分最大可抵扣金额
+ // 新增:商家减免明细
+ merchantReduction: {
+ type: MerchantReductionType; // 实际使用的减免类型
+ originalConfig: MerchantReductionConfig; // 原始配置(便于前端展示)
+ actualAmount: number; // 实际减免金额(计算后的值,≥0)
+ };
+ additionalFee: number; // 附加费
+ finalPayAmount: number; // 最终实付金额
+ couponUsed?: Coupon; // 实际使用的优惠券
+ pointUsed: number; // 实际使用的积分
+ newUserDiscount: number; // 新用户减免金额(元,默认0)
+ dinnerType?: "dine-in" | "take-out"; // 就餐类型(堂食/自取/配送/快递)
+ config: OrderExtraConfig; // 订单额外费用配置
+ //满减活动
+ fullReduction: {
+ usedActivity?: FullReductionActivity; // 实际使用的满减活动
+ usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优)
+ actualAmount: number; // 满减实际减免金额(元)
+ };
+ // 订单原支付金额
+ orderOriginFinalPayAmount: number; //订单原金额(包含打包费+餐位费)
+}
+
+/** 满减活动阈值(单条满减规则:满X减Y)- 对应 MkDiscountThresholdInsertGroupDefaultGroup */
+export interface FullReductionThreshold {
+ activityId?: number; // 关联满减活动ID
+ fullAmount?: number; // 满多少金额(元,必填)
+ discountAmount?: number; // 减多少金额(元,必填)
+}
+
+/** 满减活动主表 - 对应 Request 接口(后端真实字段) */
+export interface FullReductionActivity {
+ id?: number; // 自增主键(后端字段:id)
+ shopId?: number; // 店铺ID(后端字段:shopId)
+ status?: number; // 活动状态:1=未开始,2=进行中,3=已结束(后端字段:status)
+ sort?: number; // 排序值(越大优先级越高,后端字段:sort)
+ createTime?: string; // 创建时间(后端字段:createTime,格式如"2025-10-14 13:56:07")
+ updateTime?: string; // 最新修改时间(后端字段:updateTime,用于优先级排序)
+ validStartTime?: string; // 有效期开始时间(后端字段:validStartTime,格式如"2025-10-14")
+ validEndTime?: string; // 有效期结束时间(后端字段:validEndTime,格式如"2025-12-14")
+ useType?: string; // 可使用类型(后端字段:useType,如"dine,pickup,deliv,express")
+ useDays?: string; // 可用周期(后端字段:useDays,如"周一,周二,周三,周四,周五,周六,周日")
+ useTimeType?: string; // 可用时间段类型(后端字段:useTimeType,all=全时段,custom=指定时段)
+ useStartTime?: string; // 每日可用开始时间(后端字段:useStartTime,如"09:00:00",仅custom时有效)
+ useEndTime?: string; // 每日可用结束时间(后端字段:useEndTime,如"22:00:00",仅custom时有效)
+ couponShare?: number; // 与优惠券同享:0=否,1=是(后端字段:couponShare)
+ discountShare?: number; // 与限时折扣同享:0=否,1=是(后端字段:discountShare)
+ vipPriceShare?: number; // 与会员价同享:0=否,1=是(后端字段:vipPriceShare)
+ pointsShare?: number; // 与积分抵扣同享:0=否,1=是(后端字段:pointsShare)
+ thresholds?: FullReductionThreshold[]; // 满减阈值列表(多门槛,后端字段:thresholds)
+ isDel?: boolean; // 是否删除:0=否,1=是(后端字段:isDel,默认false)
+}
+
+// 辅助枚举:星期映射(用于useDays校验)
+const WEEKDAY_MAP = {
+ 周一: 1,
+ 周二: 2,
+ 周三: 3,
+ 周四: 4,
+ 周五: 5,
+ 周六: 6,
+ 周日: 0, // JS中getDay()返回0=周日
+};
+
+/**
+ * 辅助:校验当前时间是否在活动的「每日可用时段」内
+ * @param activity 满减活动
+ * @param currentTime 当前时间
+ * @returns 是否在时段内
+ */
+function isInDailyTimeRange(
+ activity: FullReductionActivity,
+ currentTime: Date
+): boolean {
+ // 全时段无需校验
+ if (activity.useTimeType === "all") return true;
+ // 无时段配置则不通过
+ if (!activity.useStartTime || !activity.useEndTime) return false;
+
+ const [startHour, startMinute] = activity.useStartTime.split(":").map(Number);
+ const [endHour, endMinute] = activity.useEndTime.split(":").map(Number);
+ const currentHour = currentTime.getHours();
+ const currentMinute = currentTime.getMinutes();
+
+ // 转换为分钟数比较
+ const startTotalMin = startHour * 60 + startMinute;
+ const endTotalMin = endHour * 60 + endMinute;
+ const currentTotalMin = currentHour * 60 + currentMinute;
+
+ // 处理跨天场景(如23:00-02:00)
+ if (startTotalMin <= endTotalMin) {
+ return currentTotalMin >= startTotalMin && currentTotalMin <= endTotalMin;
+ } else {
+ return currentTotalMin >= startTotalMin || currentTotalMin <= endTotalMin;
+ }
+}
+
+/**
+ * 辅助:校验当前时间是否在活动的「可用周期」内(如周一至周日)
+ * @param activity 满减活动
+ * @param currentTime 当前时间
+ * @returns 是否在周期内
+ */
+function isInWeeklyCycle(
+ activity: FullReductionActivity,
+ currentTime: Date
+): boolean {
+ // 无周期配置则不通过
+ if (!activity.useDays) return false;
+ const currentWeekday = currentTime.getDay(); // 0=周日,1=周一...6=周六
+ const allowedWeekdays = activity.useDays
+ .split(",")
+ .map((day) => WEEKDAY_MAP[day as keyof typeof WEEKDAY_MAP]);
+ return allowedWeekdays.includes(currentWeekday);
+}
+
+/**
+ * 辅助:校验当前就餐类型是否在活动的「可用类型」内(如堂食/自取)
+ * @param activity 满减活动
+ * @param currentDinnerType 当前就餐类型
+ * @returns 是否匹配
+ */
+function isDinnerTypeMatch(
+ activity: FullReductionActivity,
+ currentDinnerType: string
+): boolean {
+ if (!activity.useType) return false;
+ const allowedTypes = activity.useType.split(",");
+ //满减活动的就餐类型和当前券类型字段值不一样,暂时返回true
+ return true;
+}
+/**
+ * 筛选最优满减活动(对齐后端逻辑:状态→时间→周期→时段→就餐类型→排序→修改时间)
+ * @param activities 后端返回的满减活动列表
+ * @param currentShopId 当前店铺ID
+ * @param currentDinnerType 当前就餐类型(dine/pickup等)
+ * @param currentTime 当前时间(默认当前时间)
+ * @returns 最优满减活动(无符合条件则返回undefined)
+ */
+export function filterOptimalFullReductionActivity(
+ activities: FullReductionActivity[],
+ currentShopId: number,
+ currentDinnerType: string,
+ currentTime: Date = new Date()
+): FullReductionActivity | undefined {
+ if (!activities || !activities.length) return undefined;
+ console.log("原始满减活动列表:", activities);
+ // 第一步:基础筛选(未删除+当前店铺+活动进行中+就餐类型匹配)
+ const baseEligible = activities.filter((activity) => {
+ return (
+ activity.isDel !== true && // 未删除
+ // activity.shopId === currentShopId && // 当前店铺
+ // activity.status === 2 && // 状态=2(进行中)
+ isDinnerTypeMatch(activity, currentDinnerType) && // 就餐类型匹配
+ activity.thresholds?.length // 至少有一个满减阈值
+ );
+ });
+ console.log("基础筛选结果:", baseEligible);
+ if (!baseEligible.length) return undefined;
+
+ // 第二步:时间筛选(有效期内+周期内+时段内)
+ const timeEligible = baseEligible.filter((activity) => {
+ // 1. 校验有效期(validStartTime ~ validEndTime)
+ if (!activity.validStartTime || !activity.validEndTime) return false;
+ const startDate = new Date(activity.validStartTime);
+ const endDate = new Date(activity.validEndTime);
+ // 处理有效期结束日期的23:59:59
+ endDate.setHours(23, 59, 59, 999);
+ if (currentTime < startDate || currentTime > endDate) return false;
+
+ // 2. 校验可用周期(如周一至周日)
+ if (!isInWeeklyCycle(activity, currentTime)) return false;
+
+ // 3. 校验每日可用时段(如09:00-22:00)
+ if (!isInDailyTimeRange(activity, currentTime)) return false;
+
+ return true;
+ });
+
+ if (!timeEligible.length) return undefined;
+
+ // 第三步:按优先级排序(需求规则)
+ return timeEligible.sort((a, b) => {
+ // 1. 先比排序值:排序值大的优先
+ if ((a.sort || 0) !== (b.sort || 0)) {
+ return (b.sort || 0) - (a.sort || 0);
+ }
+ // 2. 再比修改时间:最新修改的优先(时间戳降序)
+ const aUpdateTime = a.updateTime ? new Date(a.updateTime).getTime() : 0;
+ const bUpdateTime = b.updateTime ? new Date(b.updateTime).getTime() : 0;
+ return bUpdateTime - aUpdateTime;
+ })[0]; // 取最优活动
+}
+/**
+ * 折扣率格式化:后端discountRate(%)→ 工具库小数(如90→0.9)
+ */
+export function formatDiscountRate(backendDiscountRate?: number): number {
+ if (!backendDiscountRate || backendDiscountRate <= 0) return 1; // 默认无折扣(1=100%)
+ // 后端若为百分比(如90=9折),除以100;若已为小数(如0.9)直接返回
+ return backendDiscountRate >= 1
+ ? backendDiscountRate / 100
+ : backendDiscountRate;
+}
+
+/**
+ * 统一小数处理:舍去小数点后两位以后的数字(解决浮点数精度偏差)
+ * 如 10.129 → 10.12,1.01 → 1.01,1.0100000001 → 1.01
+ * @param num 待处理数字
+ * @returns 处理后保留两位小数的数字
+ */
+export function truncateToTwoDecimals(num: number | string): number {
+ return new BigNumber(num).decimalPlaces(2, BigNumber.ROUND_DOWN).toNumber();
+}
+
+/**
+ * 判断商品是否为临时菜(临时菜不参与优惠券门槛和折扣计算)
+ * @param goods 商品项
+ * @returns 是否临时菜
+ */
+export function isTemporaryGoods(goods: BaseCartItem): boolean {
+ return !!goods.is_temporary;
+}
+
+/**
+ * 判断商品是否为赠菜(赠菜不计入优惠券活动,但计打包费)
+ * @param goods 商品项
+ * @returns 是否赠菜
+ */
+export function isGiftGoods(goods: BaseCartItem): boolean {
+ return !!goods.is_gift;
+}
+/**
+ * 判断可用类型是否可用
+ */
+export function useTypeCanUse(useType: string[]) {
+ const arr = ["all", "dine-in", "take-out", "take-away", "post"];
+ return useType.some((item) => arr.includes(item));
+}
+
+/**
+ * 计算单个商品的会员价(优先级:SKU会员价 > 商品会员价 > 会员折扣率)
+ * @param goods 商品项
+ * @param isMember 是否会员
+ * @param memberDiscountRate 会员折扣率(如0.95=95折)
+ * @returns 会员价(元)
+ */
+export function calcMemberPrice(
+ goods: BaseCartItem,
+ isMember: boolean,
+ memberDiscountRate?: number
+): number {
+ if (!isMember) return truncateToTwoDecimals(goods.salePrice);
+
+ // 优先级:SKU会员价 > 商品会员价 > 商品原价(无会员价时用会员折扣)
+ const basePrice =
+ goods.skuData?.memberPrice ?? goods.memberPrice ?? goods.salePrice;
+
+ // 仅当无SKU会员价、无商品会员价时,才应用会员折扣率
+ if (memberDiscountRate && !goods.skuData?.memberPrice && !goods.memberPrice) {
+ return truncateToTwoDecimals(
+ new BigNumber(basePrice).multipliedBy(memberDiscountRate).toNumber()
+ );
+ }
+
+ return truncateToTwoDecimals(basePrice);
+}
+
+/**
+ * 过滤可参与优惠券计算的商品(排除临时菜、赠菜、已用兑换券的商品)
+ * @param goodsList 商品列表
+ * @param excludedProductIds 需排除的商品ID列表(商品ID,非购物车ID)
+ * @returns 可参与优惠券计算的商品列表
+ */
+export function filterCouponEligibleGoods(
+ goodsList: BaseCartItem[],
+ excludedProductIds: string[] = []
+): BaseCartItem[] {
+ return goodsList.filter(
+ (goods) =>
+ !isTemporaryGoods(goods) &&
+ !isGiftGoods(goods) &&
+ !excludedProductIds.includes(String(goods.product_id)) // 核心修正:用商品ID排除
+ );
+}
+
+/**
+ * 统一筛选门槛商品的工具函数(所有券类型复用,按商品ID匹配)
+ * @param baseEligibleGoods 基础合格商品(已排除临时菜/赠菜/已抵扣商品)
+ * @param applicableProductIds 优惠券指定的门槛商品ID数组
+ * @returns 最终参与优惠券计算的商品列表
+ */
+export function filterThresholdGoods(
+ baseEligibleGoods: BaseCartItem[],
+ applicableProductIds: string[]
+): BaseCartItem[] {
+ // 空数组=全部基础合格商品;非空=仅商品ID匹配的商品(转字符串兼容类型)
+ return applicableProductIds.length === 0
+ ? baseEligibleGoods
+ : baseEligibleGoods.filter((goods) =>
+ applicableProductIds.includes(String(goods.product_id))
+ ); // 核心修正:用商品ID匹配
+}
+
+/**
+ * 商品排序(用于商品兑换券:按价格/数量/加入顺序排序,按商品ID分组去重)
+ * @param goodsList 商品列表
+ * @param sortRule 排序规则(low_price_first/high_price_first)
+ * @param cartOrder 商品加入购物车的顺序(key=购物车ID,value=加入时间戳)
+ * @returns 排序后的商品列表
+ */
+export function sortGoodsForCoupon(
+ goodsList: BaseCartItem[],
+ sortRule: "low_price_first" | "high_price_first",
+ cartOrder: Record = {}
+): BaseCartItem[] {
+ return [...goodsList].sort((a, b) => {
+ // 1. 按商品单价排序(优先级最高)
+ const priceA = a.skuData?.salePrice ?? a.salePrice;
+ const priceB = b.skuData?.salePrice ?? b.salePrice;
+ if (priceA !== priceB) {
+ return sortRule === "low_price_first" ? priceA - priceB : priceB - priceA;
+ }
+
+ // 2. 同价格按商品数量排序(降序,多的优先)
+ if (a.number !== b.number) {
+ return b.number - a.number;
+ }
+
+ // 3. 同价格同数量按加入购物车顺序(早的优先,用购物车ID匹配)
+ const orderA = cartOrder[String(a.id)] ?? Infinity;
+ const orderB = cartOrder[String(b.id)] ?? Infinity;
+ return orderA - orderB;
+ });
+}
+
+/**
+ * 计算优惠券门槛金额(根据同享规则,按商品ID匹配限时折扣)
+ * @param eligibleGoods 可参与优惠券的商品列表(已过滤临时菜/赠菜)
+ * @param coupon 优惠券(含discountShare/vipPriceShare配置)
+ * @param config 订单配置(会员信息)
+ * @param activities 全局营销活动(限时折扣,applicableProductIds为商品ID)
+ * @returns 满足优惠券门槛的金额基数
+ */
+export function calcCouponThresholdAmount(
+ eligibleGoods: BaseCartItem[],
+ coupon: BaseCoupon,
+ config: Pick,
+ activities: ActivityConfig[] = []
+): number {
+ let total = new BigNumber(0);
+
+ for (const goods of eligibleGoods) {
+ const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
+ if (availableNum <= 0) continue;
+
+ // 1. 基础金额:默认用商品原价(SKU原价优先)
+ const basePrice = new BigNumber(
+ goods.skuData?.salePrice ?? goods.salePrice
+ );
+ let itemAmount = basePrice.multipliedBy(availableNum);
+
+ // 2. 处理「与会员价/会员折扣同享」规则:若开启,用会员价计算
+ if (coupon.vipPriceShare) {
+ const memberPrice = new BigNumber(
+ calcMemberPrice(goods, config.isMember, config.memberDiscountRate)
+ );
+ itemAmount = memberPrice.multipliedBy(availableNum);
+ }
+
+ // 3. 处理「与限时折扣同享」规则:若开启,叠加限时折扣(按商品ID匹配活动)
+ if (coupon.discountShare) {
+ const activity =
+ goods.activityInfo ??
+ activities.find(
+ (act) =>
+ act.type === ActivityType.TIME_LIMIT_DISCOUNT &&
+ (act.applicableProductIds || []).includes(String(goods.product_id)) // 核心修正:用商品ID匹配活动
+ );
+
+ if (activity) {
+ itemAmount = itemAmount.multipliedBy(activity.discountRate); // 叠加限时折扣
+ }
+ }
+
+ total = total.plus(itemAmount);
+ }
+
+ return truncateToTwoDecimals(total.toNumber());
+}
+
+// ============================ 3. 商品核心价格计算(核心修正:按商品ID匹配营销活动) ============================
+/**
+ * 计算单个商品的实际单价(整合商家改价、会员优惠、营销活动折扣,按商品ID匹配活动)
+ * @param goods 商品项
+ * @param config 订单额外配置(含会员、活动信息)
+ * @returns 单个商品实际单价(元)
+ */
+export function calcSingleGoodsRealPrice(
+ goods: BaseCartItem,
+ config: Pick & {
+ activity?: ActivityConfig; // 商品参与的营销活动(如限时折扣,按商品ID匹配)
+ }
+): number {
+ const { isMember, memberDiscountRate, activity } = config;
+
+ //如果是增菜价格为0
+ if (goods.is_gift) {
+ return 0;
+ }
+
+ // 1. 优先级1:商家改价(改价后单价>0才生效)
+ if (goods.discountSaleAmount && goods.discountSaleAmount > 0) {
+ return truncateToTwoDecimals(goods.discountSaleAmount);
+ }
+
+ // 2. 优先级2:会员价(含会员折扣率,SKU会员价优先)
+ const memberPrice = new BigNumber(
+ calcMemberPrice(goods, isMember, memberDiscountRate)
+ );
+
+ // 3. 优先级3:营销活动折扣(如限时折扣,需按商品ID匹配活动)
+ const isActivityApplicable = activity
+ ? (activity.applicableProductIds || []).includes(String(goods.product_id)) // 核心修正:用商品ID匹配活动
+ : false;
+ if (!activity || !isActivityApplicable) {
+ return memberPrice.toNumber();
+ }
+
+ // 处理活动与会员的同享/不同享逻辑
+ if (activity.vipPriceShare) {
+ // 同享:会员价基础上叠加工活动折扣
+ return truncateToTwoDecimals(
+ memberPrice.multipliedBy(activity.discountRate).toNumber()
+ );
+ } else {
+ // 不同享:取会员价和活动价的最小值(活动价用SKU原价计算)
+ const basePriceForActivity = new BigNumber(
+ goods.skuData?.salePrice ?? goods.salePrice
+ );
+ const activityPrice = basePriceForActivity.multipliedBy(
+ activity.discountRate
+ );
+
+ return truncateToTwoDecimals(
+ memberPrice.isLessThanOrEqualTo(activityPrice)
+ ? memberPrice.toNumber()
+ : activityPrice.toNumber()
+ );
+ }
+}
+
+/**
+ * 计算商品原价总和(所有商品:原价*数量,含临时菜、赠菜,用SKU原价优先)
+ * @param goodsList 商品列表
+ * @returns 商品原价总和(元)
+ */
+export function calcGoodsOriginalAmount(goodsList: BaseCartItem[]): number {
+ let total = new BigNumber(0);
+
+ for (const goods of goodsList) {
+ const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
+ let basePrice = new BigNumber(0);
+ if (goods.is_temporary) {
+ basePrice = new BigNumber(goods?.discountSaleAmount ?? 0);
+ } else if (goods.is_gift) {
+ basePrice = new BigNumber(0);
+ } else {
+ basePrice = new BigNumber(goods.skuData?.salePrice ?? goods.salePrice); // SKU原价优先
+ }
+
+ total = total.plus(basePrice.multipliedBy(availableNum));
+ }
+
+ return truncateToTwoDecimals(total.toNumber());
+}
+
+/**
+ * 计算商品实际总价(所有商品:实际单价*数量,含临时菜、赠菜,按商品ID匹配活动)
+ * @param goodsList 商品列表
+ * @param config 订单额外配置(含会员、活动信息)
+ * @param activities 全局营销活动列表(如限时折扣,applicableProductIds为商品ID)
+ * @returns 商品实际总价(元)
+ */
+export function calcGoodsRealAmount(
+ goodsList: BaseCartItem[],
+ config: Pick,
+ activities: ActivityConfig[] = []
+): number {
+ let total = new BigNumber(0);
+
+ for (const goods of goodsList) {
+ const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
+ if (availableNum <= 0) continue;
+
+ // 匹配商品参与的营销活动(按商品ID匹配,优先商品自身配置)
+ const activity =
+ goods.activityInfo ??
+ activities.find(
+ (act) =>
+ (act.applicableProductIds || []).includes(String(goods.product_id)) // 核心修正:用商品ID匹配活动
+ );
+
+ const realPrice = new BigNumber(
+ calcSingleGoodsRealPrice(goods, { ...config, activity })
+ );
+ total = total.plus(realPrice.multipliedBy(availableNum));
+ }
+
+ return truncateToTwoDecimals(total.toNumber());
+}
+
+/**
+ * 计算商品折扣总金额(商品原价总和 - 商品实际总价)
+ * @param goodsOriginalAmount 商品原价总和
+ * @param goodsRealAmount 商品实际总价
+ * @returns 商品折扣总金额(元,≥0)
+ */
+export function calcGoodsDiscountAmount(
+ goodsOriginalAmount: number,
+ goodsRealAmount: number
+): number {
+ const original = new BigNumber(goodsOriginalAmount);
+ const real = new BigNumber(goodsRealAmount);
+ const discount = original.minus(real);
+
+ return truncateToTwoDecimals(Math.max(0, discount.toNumber()));
+}
+/**
+ * 从满减活动的多门槛中,选择最优阈值(满金额最小且减免金额最大)
+ * @param thresholds 满减阈值列表(多门槛)
+ * @param baseAmount 满减计算基数(新客立减后的金额)
+ * @param goodsOriginalAmount 商品原价总和(discountShare=0时用)
+ * @param goodsRealAmount 商品折扣后总和(discountShare=1时用)
+ * @param discountShare 与限时折扣同享:0=否,1=是
+ * @returns 最优阈值(无符合条件则返回undefined)
+ */
+export function selectOptimalThreshold(
+ thresholds: FullReductionThreshold[] = [],
+ baseAmount: number,
+ goodsOriginalAmount: number,
+ goodsRealAmount: number,
+ discountShare: number = 0
+): FullReductionThreshold | undefined {
+ if (!thresholds.length) return undefined;
+
+ // 第一步:确定满减门槛基数(根据discountShare规则)
+ const thresholdBase =
+ discountShare === 1
+ ? new BigNumber(goodsRealAmount) // 与限时折扣同享→用折扣后金额
+ : new BigNumber(goodsOriginalAmount); // 不同享→用原价
+
+ // 第二步:筛选「满金额≤基数」且「减免金额>0」的有效阈值
+ const validThresholds = thresholds.filter((threshold) => {
+ const fullAmount = new BigNumber(threshold.fullAmount || 0);
+ const discountAmount = new BigNumber(threshold.discountAmount || 0);
+ return (
+ fullAmount.isLessThanOrEqualTo(thresholdBase) &&
+ discountAmount.isGreaterThan(0)
+ );
+ });
+
+ if (!validThresholds.length) return undefined;
+
+ // 第三步:选择最优阈值(优先级:1.满金额最小 → 2.减免金额最大)
+ return validThresholds.sort((a, b) => {
+ const aFull = new BigNumber(a.fullAmount || 0);
+ const bFull = new BigNumber(b.fullAmount || 0);
+ const aDiscount = new BigNumber(a.discountAmount || 0);
+ const bDiscount = new BigNumber(b.discountAmount || 0);
+
+ // 先比满金额:越小越优先(满1减10 比 满100减20 更优)
+ if (!aFull.isEqualTo(bFull)) {
+ return aFull.comparedTo(bFull) || 0; // Ensure a number is always returned
+ }
+ // 再比减免金额:越大越优先
+ return bDiscount.comparedTo(aDiscount) || 0; // Ensure a number is always returned
+ })[0];
+}
+
+/**
+ * 计算满减实际减免金额(适配多门槛、同享规则)
+ * @param optimalActivity 最优满减活动
+ * @param optimalThreshold 最优满减阈值
+ * @param baseAmount 计算基数(新客立减后的金额)
+ * @returns 实际减免金额(元,未达门槛则0)
+ */
+export function calcFullReductionAmount(
+ baseAmount: number,
+ optimalActivity?: FullReductionActivity,
+ optimalThreshold?: FullReductionThreshold
+): number {
+ if (!optimalActivity || !optimalThreshold) return 0;
+
+ const baseAmountBn = new BigNumber(baseAmount);
+ const discountAmountBn = new BigNumber(optimalThreshold.discountAmount || 0);
+
+ // 1. 基数必须为正(避免减免后为负)
+ if (baseAmountBn.isLessThanOrEqualTo(0)) return 0;
+
+ // 2. 减免金额不能超过基数(避免减成负数)
+ const maxReducible = baseAmountBn;
+ const actualReduction = discountAmountBn.isLessThanOrEqualTo(maxReducible)
+ ? discountAmountBn
+ : maxReducible;
+
+ return truncateToTwoDecimals(actualReduction.toNumber());
+}
+
+// ------------------------------ 策略辅助函数 ------------------------------
+/**
+ * 根据优惠券useShops列表判断门店是否匹配(适配BaseCoupon的useShops字段)
+ */
+function isStoreMatchByList(
+ useShops: string[],
+ currentStoreId: string
+): boolean {
+ // 适用门店为空数组 → 无限制(所有门店适用)
+ if (useShops.length === 0) return true;
+ // 匹配当前门店ID(字符串比较,避免类型问题)
+ return useShops.includes(currentStoreId);
+}
+
+/**
+ * 计算优惠券抵扣金额(处理互斥逻辑,选择最优优惠券,按商品ID排除,新增细分统计)
+ * @param backendCoupons 后端优惠券列表
+ * @param goodsList 商品列表
+ * @param config 订单配置(含就餐类型)
+ * @returns 最优优惠券的抵扣结果(含商品券/满减券细分)
+ */
+export function calcCouponDeduction(
+ backendCoupons: BackendCoupon[],
+ goodsList: BaseCartItem[],
+ config: Pick<
+ OrderExtraConfig,
+ "currentStoreId" | "isMember" | "memberDiscountRate"
+ > & {
+ activities: ActivityConfig[];
+ cartOrder: Record;
+ dinnerType: "dine-in" | "take-out";
+ currentTime?: Date;
+ }
+): {
+ deductionAmount: number;
+ productCouponDeduction: number; // 新增:商品优惠券抵扣(兑换券/折扣券/买一送一等)
+ fullCouponDeduction: number; // 新增:满减优惠券抵扣
+ usedCoupon?: Coupon;
+ excludedProductIds: string[]; // 排除的商品ID列表(商品ID)
+} {
+ const goodsCoupon = backendCoupons.filter((v) => v.type == 2);
+ const discountCoupon = backendCoupons.filter((v) => v.type != 2);
+
+ // 3. 计算非兑换券最优抵扣(传递已抵扣商品ID,避免重复,统计细分字段)
+ let nonExchangeResult: CouponResult = {
+ deductionAmount: discountCoupon.reduce((prve, cur): number => {
+ return prve + (cur.discountAmount || 0);
+ }, 0),
+ excludedProductIds: [],
+ usedCoupon: undefined,
+ productCouponDeduction: 0,
+ fullCouponDeduction: 0,
+ };
+
+ // 4. 计算兑换券抵扣(排除非兑换券已抵扣的商品ID,统计商品券细分)
+ let exchangeResult: ExchangeCalculationResult = {
+ deductionAmount: goodsCoupon.reduce((prve, cur): number => {
+ return prve + (cur.discountAmount || 0);
+ }, 0),
+ excludedProductIds: [],
+ productCouponDeduction: 0,
+ };
+
+ // 5. 汇总结果:兑换券与非兑换券不可同时使用,取抵扣金额大的
+ const exchangeBn = new BigNumber(exchangeResult.deductionAmount);
+ const nonExchangeBn = new BigNumber(nonExchangeResult.deductionAmount);
+ const isExchangeBetter = exchangeBn.isGreaterThan(nonExchangeBn);
+
+ return {
+ deductionAmount: exchangeBn.plus(nonExchangeBn).toNumber(),
+ productCouponDeduction: exchangeResult.deductionAmount,
+ fullCouponDeduction: nonExchangeResult.deductionAmount, // 兑换券与满减券互斥,满减券抵扣置0
+ usedCoupon: isExchangeBetter ? undefined : nonExchangeResult.usedCoupon,
+ excludedProductIds: isExchangeBetter
+ ? exchangeResult.excludedProductIds
+ : nonExchangeResult.excludedProductIds,
+ };
+}
+
+// ============================ 5. 其他费用计算(无ID依赖,逻辑不变) ============================
+/**
+ * 计算总打包费(赠菜也计算,称重商品打包数量≤1)
+ * @param goodsList 商品列表
+ * @param dinnerType 就餐类型(堂食dine-in/外卖take-out)
+ * @returns 总打包费(元)
+ */
+export function calcTotalPackFee(
+ goodsList: BaseCartItem[],
+ dinnerType: "dine-in" | "take-out"
+): number {
+ // if (dinnerType !== "take-out") return 0;
+ let total = new BigNumber(0);
+
+ for (const goods of goodsList) {
+ const packNumber = goods.packNumber ? goods.packNumber * 1 : 0;
+ let availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
+ availableNum = Math.min(availableNum, packNumber);
+
+ if (availableNum === 0) continue;
+
+ // 计算单个商品打包数量(外卖全打包,堂食按配置,称重商品≤1)
+ let packNum = availableNum;
+ if (goods.product_type === GoodsType.WEIGHT) {
+ packNum = Math.min(packNum, 1);
+ }
+
+ total = total.plus(new BigNumber(goods.packFee || 0).multipliedBy(packNum));
+ }
+
+ return truncateToTwoDecimals(total.toNumber());
+}
+
+/**
+ * 计算餐位费(按人数,不参与营销活动)
+ * @param config 餐位费配置
+ * @returns 餐位费(元,未启用则0)
+ */
+export function calcSeatFee(config: SeatFeeConfig): number {
+ if (!config.isEnabled || config.personCount == 0) return 0;
+ const personCount = Math.max(1, config.personCount); // 至少1人
+ return truncateToTwoDecimals(
+ new BigNumber(config.pricePerPerson).multipliedBy(personCount).toNumber()
+ );
+}
+
+/**
+ * 计算积分抵扣金额(按X积分=1元,不超过最大抵扣和用户积分)
+ * @param userPoints 用户当前积分
+ * @param rule 积分抵扣规则
+ * @param maxDeductionLimit 最大抵扣上限(通常为订单金额,避免超扣)
+ * @returns 积分抵扣金额 + 实际使用积分
+ */
+export function calcPointDeduction(
+ userPoints: number,
+ rule: PointDeductionRule,
+ maxDeductionLimit: number
+): {
+ deductionAmount: number;
+ usedPoints: number;
+} {
+ if (rule.pointsPerYuan <= 0 || userPoints <= 0) {
+ return { deductionAmount: 0, usedPoints: 0 };
+ }
+
+ const userPointsBn = new BigNumber(userPoints);
+ const pointsPerYuanBn = new BigNumber(rule.pointsPerYuan);
+ const maxLimitBn = new BigNumber(maxDeductionLimit);
+
+ // 最大可抵扣金额(积分可抵金额 vs 规则最大 vs 订单上限)
+ const maxDeductByPoints = userPointsBn.dividedBy(pointsPerYuanBn);
+ const maxDeductAmount = maxDeductByPoints.isLessThan(
+ rule.maxDeductionAmount ?? Infinity
+ )
+ ? maxDeductByPoints
+ : new BigNumber(rule.maxDeductionAmount || Infinity).isLessThan(maxLimitBn)
+ ? maxDeductByPoints
+ : maxLimitBn;
+
+ // 实际使用积分 = 抵扣金额 * 积分兑换比例
+ const usedPoints = maxDeductAmount.multipliedBy(pointsPerYuanBn);
+
+ return {
+ deductionAmount: truncateToTwoDecimals(maxDeductAmount.toNumber()),
+ usedPoints: truncateToTwoDecimals(
+ Math.min(usedPoints.toNumber(), userPoints)
+ ), // 避免积分超扣
+ };
+}
+
+// ============================ 6. 订单总费用汇总与实付金额计算(核心入口,补充细分字段) ============================
+/**
+ * 计算订单所有费用子项并汇总(核心入口函数)
+ * @param goodsList 购物车商品列表
+ * @param dinnerType 就餐类型
+ * @param backendCoupons 后端优惠券列表
+ * @param activities 全局营销活动列表
+ * @param config 订单额外配置(会员、积分、餐位费等)
+ * @param cartOrder 商品加入购物车顺序(key=购物车ID,value=时间戳)
+ * @param currentTime 当前时间(用于优惠券有效期判断)
+ * @returns 订单费用汇总(含所有子项和实付金额,新增商品券/满减券细分)
+ */
+export function calculateOrderCostSummary(
+ goodsList: BaseCartItem[],
+ dinnerType: "dine-in" | "take-out", // 前端就餐类型
+ backendCoupons: BackendCoupon[] = [],
+ activities: ActivityConfig[] = [],
+ config: OrderExtraConfig, // 含后端满减活动、currentDinnerType
+ cartOrder: Record = {},
+ currentTime: Date = new Date()
+): OrderCostSummary {
+ //是否使用霸王餐,霸王餐配置
+ const { isFreeDine, freeDineConfig } = config;
+
+ // ------------------------------ 1. 基础费用计算 ------------------------------
+ const goodsOriginalAmount = calcGoodsOriginalAmount(goodsList); // 商品原价总和
+ const goodsRealAmount = calcGoodsRealAmount(
+ // 商品折扣后总和
+ goodsList,
+ {
+ isMember: config.isMember,
+ memberDiscountRate: config.memberDiscountRate,
+ },
+ activities
+ );
+ const goodsDiscountAmount = calcGoodsDiscountAmount(
+ goodsOriginalAmount,
+ goodsRealAmount
+ ); // 商品折扣金额
+ const newUserDiscount = config.newUserDiscount || 0; // 新客立减
+
+ // ------------------------------ 2. 满减活动计算(核心步骤) ------------------------------
+ let usedFullReductionActivity: FullReductionActivity | undefined;
+ let usedFullReductionThreshold: FullReductionThreshold | undefined;
+ let fullReductionAmount = 0;
+ // 2.1 筛选最优满减活动(后端活动列表、当前店铺、就餐类型、时间)
+ usedFullReductionActivity = filterOptimalFullReductionActivity(
+ config.fullReductionActivities,
+ Number(config.currentStoreId), // 转换为数字(后端shopId是number)
+ config.currentDinnerType, // 后端useType匹配的就餐类型(如"dine")
+ currentTime
+ );
+
+ // 2.2 计算满减基数(先扣新客立减)
+ const baseAfterNewUserDiscount = new BigNumber(goodsRealAmount)
+ .minus(newUserDiscount)
+ .isGreaterThan(0)
+ ? new BigNumber(goodsRealAmount).minus(newUserDiscount).toNumber()
+ : 0;
+
+ // 2.3 选择最优满减阈值(多门槛场景)
+ if (usedFullReductionActivity) {
+ usedFullReductionThreshold = selectOptimalThreshold(
+ usedFullReductionActivity.thresholds,
+ baseAfterNewUserDiscount,
+ goodsOriginalAmount,
+ goodsRealAmount,
+ usedFullReductionActivity.discountShare || 0 // 与限时折扣同享规则
+ );
+
+ // 2.4 计算满减实际减免金额
+ fullReductionAmount = calcFullReductionAmount(
+ baseAfterNewUserDiscount,
+ usedFullReductionActivity,
+ usedFullReductionThreshold
+ );
+ }
+
+ // ------------------------------ 3. 优惠券抵扣(适配满减同享规则) ------------------------------
+ let couponDeductionAmount = 0;
+ let productCouponDeduction = 0;
+ let fullCouponDeduction = 0;
+ let usedCoupon: Coupon | undefined;
+ let excludedProductIds: string[] = [];
+
+ const couponResult = calcCouponDeduction(
+ // 原有优惠券计算函数
+ backendCoupons,
+ goodsList,
+ {
+ currentStoreId: config.currentStoreId,
+ isMember: config.isMember,
+ memberDiscountRate: config.memberDiscountRate,
+ activities,
+ cartOrder,
+ dinnerType,
+ currentTime,
+ }
+ );
+ console.log("优惠券计算结果", couponResult);
+ couponDeductionAmount = couponResult.deductionAmount;
+ productCouponDeduction = couponResult.productCouponDeduction;
+ fullCouponDeduction = couponResult.fullCouponDeduction;
+ usedCoupon = couponResult.usedCoupon;
+ excludedProductIds = couponResult.excludedProductIds;
+
+ // 若满减与优惠券同享(couponShare=1),才计算优惠券;否则优惠券抵扣为0
+ if (usedFullReductionActivity && !usedFullReductionActivity.couponShare) {
+ couponDeductionAmount=0;
+ productCouponDeduction=0;
+ fullCouponDeduction=0;
+ usedCoupon=undefined;
+ excludedProductIds=[];
+ }
+
+ // ------------------------------ 4. 积分抵扣(适配满减同享规则) ------------------------------
+ let pointDeductionAmount = 0;
+ let usedPoints = 0;
+
+ // 计算积分抵扣基数(商品折扣后 - 新客立减 - 满减 - 优惠券)
+ const maxPointDeductionLimit = new BigNumber(goodsRealAmount)
+ .minus(newUserDiscount)
+ .minus(fullReductionAmount)
+ .minus(couponDeductionAmount)
+ .isGreaterThan(0)
+ ? new BigNumber(goodsRealAmount)
+ .minus(newUserDiscount)
+ .minus(fullReductionAmount)
+ .minus(couponDeductionAmount)
+ .toNumber()
+ : 0;
+
+ // 若满减与积分同享(pointsShare=1),才计算积分;否则积分抵扣为0
+ if (
+ usedFullReductionActivity?.pointsShare === 1 &&
+ maxPointDeductionLimit > 0
+ ) {
+ const pointResult = calcPointDeduction(
+ config.userPoints,
+ config.pointDeductionRule,
+ maxPointDeductionLimit
+ );
+ pointDeductionAmount = pointResult.deductionAmount;
+ usedPoints = pointResult.usedPoints;
+ }
+
+ //使用霸王餐
+ if (isFreeDine && freeDineConfig && freeDineConfig.enable) {
+ //不与优惠券同享
+ if (!freeDineConfig.withCoupon) {
+ couponDeductionAmount = 0;
+ productCouponDeduction = 0;
+ fullCouponDeduction = 0;
+ usedCoupon = undefined;
+ excludedProductIds = [];
+ }
+ //不与积分同享
+ if (!freeDineConfig.withPoints) {
+ pointDeductionAmount = 0;
+ usedPoints = 0;
+ }
+ }
+
+ // ------------------------------ 5. 其他费用计算(原有逻辑不变) ------------------------------
+ const packFee = calcTotalPackFee(goodsList, dinnerType); // 打包费
+ let seatFee = calcSeatFee(config.seatFeeConfig); // 餐位费
+ seatFee = dinnerType === "dine-in" ? seatFee : 0; // 外卖不收餐位费
+ const additionalFee = Math.max(0, config.additionalFee); // 附加费
+
+ // 商家减免计算(原有逻辑不变)
+ const merchantReductionConfig = config.merchantReduction;
+ let merchantReductionActualAmount = 0;
+ const maxMerchantReductionLimit = new BigNumber(goodsRealAmount)
+ .minus(newUserDiscount)
+ .minus(fullReductionAmount)
+ .minus(couponDeductionAmount)
+ .minus(pointDeductionAmount)
+ .plus(seatFee)
+ .plus(packFee)
+ .isGreaterThan(0)
+ ? new BigNumber(goodsRealAmount)
+ .minus(newUserDiscount)
+ .minus(fullReductionAmount)
+ .minus(couponDeductionAmount)
+ .minus(pointDeductionAmount)
+ .plus(seatFee)
+ .plus(packFee)
+ .toNumber()
+ : 0;
+
+ switch (merchantReductionConfig.type) {
+ case MerchantReductionType.FIXED_AMOUNT:
+ merchantReductionActualAmount = Math.min(
+ merchantReductionConfig.fixedAmount || 0,
+ maxMerchantReductionLimit
+ );
+ break;
+ case MerchantReductionType.DISCOUNT_RATE:
+ const validRate =
+ Math.max(0, Math.min(100, merchantReductionConfig.discountRate || 0)) /
+ 100;
+ merchantReductionActualAmount =
+ maxMerchantReductionLimit * (1 - validRate);
+ break;
+ }
+ merchantReductionActualAmount = Math.max(
+ 0,
+ truncateToTwoDecimals(merchantReductionActualAmount)
+ );
+
+ // ------------------------------ 6. 最终实付金额计算 ------------------------------
+ const finalPayAmountBn = new BigNumber(goodsRealAmount)
+ .minus(newUserDiscount)
+ .minus(fullReductionAmount)
+ .minus(couponDeductionAmount)
+ .minus(pointDeductionAmount)
+ .minus(merchantReductionActualAmount)
+ .plus(seatFee)
+ .plus(packFee)
+ .plus(additionalFee);
+ let finalPayAmount = Math.max(
+ 0,
+ truncateToTwoDecimals(finalPayAmountBn.toNumber())
+ );
+ // ------------------------------ 使用霸王餐计算 ------------------------------
+let orderOriginFinalPayAmount=finalPayAmount;
+ if (isFreeDine && freeDineConfig && freeDineConfig.enable) {
+ finalPayAmount = BigNumber(finalPayAmount)
+ .times(freeDineConfig.rechargeTimes)
+ .toNumber();
+ }
+
+ // ------------------------------ 7. 总优惠金额计算 ------------------------------
+ const totalDiscountAmount = truncateToTwoDecimals(
+ new BigNumber(goodsDiscountAmount)
+ .plus(newUserDiscount)
+ .plus(fullReductionAmount)
+ .plus(couponDeductionAmount)
+ .plus(pointDeductionAmount)
+ .plus(merchantReductionActualAmount)
+ .toNumber()
+ );
+ //积分可抵扣最大金额 最终支付金额+积分抵扣-商家减免
+ const scoreMaxMoney = new BigNumber(finalPayAmount)
+ .plus(pointDeductionAmount)
+ .minus(merchantReductionActualAmount)
+ .toNumber();
+
+ // ------------------------------ 8. 返回完整结果 ------------------------------
+ return {
+ goodsTotal: goodsList.reduce(
+ (sum, g) => sum + Math.max(0, g.number - (g.returnNum || 0)),
+ 0
+ ),
+ goodsRealAmount,
+ goodsOriginalAmount,
+ goodsDiscountAmount,
+ couponDeductionAmount,
+ productCouponDeduction,
+ fullCouponDeduction,
+ pointDeductionAmount,
+ seatFee,
+ packFee,
+ totalDiscountAmount,
+ //最终支付原金额
+ orderOriginFinalPayAmount,
+ //积分最大可抵扣金额
+ scoreMaxMoney,
+ // 满减活动明细(后端字段)
+ fullReduction: {
+ usedActivity: usedFullReductionActivity,
+ usedThreshold: usedFullReductionThreshold,
+ actualAmount: truncateToTwoDecimals(fullReductionAmount),
+ },
+ merchantReduction: {
+ type: merchantReductionConfig.type,
+ originalConfig: merchantReductionConfig,
+ actualAmount: merchantReductionActualAmount,
+ },
+ additionalFee,
+ finalPayAmount,
+ couponUsed: usedCoupon,
+ pointUsed: usedPoints,
+ newUserDiscount,
+ dinnerType,
+ config,
+ };
+}
+
+export function isWeightGoods(goods: BaseCartItem): boolean {
+ return goods.product_type === GoodsType.WEIGHT;
+}
+
+// ============================ 7. 对外暴露工具库 ============================
+export const OrderPriceCalculator = {
+ // 基础工具
+ truncateToTwoDecimals,
+ isTemporaryGoods,
+ isGiftGoods,
+ formatDiscountRate,
+ filterThresholdGoods,
+ isWeightGoods,
+ // 商品价格计算
+ calcSingleGoodsRealPrice,
+ calcGoodsOriginalAmount,
+ calcGoodsRealAmount,
+ calcGoodsDiscountAmount,
+ //满减活动工具
+ filterOptimalFullReductionActivity,
+ // 优惠券计算
+ calcCouponDeduction,
+ // 其他费用计算
+ calcTotalPackFee,
+ calcSeatFee,
+ calcPointDeduction,
+ // 核心入口
+ calculateOrderCostSummary,
+ // 枚举导出
+ Enums: {
+ GoodsType,
+ CouponType,
+ ActivityType,
+ WEEKDAY_MAP,
+ },
+};
+
+export default OrderPriceCalculator;
diff --git a/utils/pay.js b/utils/pay.js
index 43b4c8c..9bee02a 100644
--- a/utils/pay.js
+++ b/utils/pay.js
@@ -1,57 +1,57 @@
export const pay = (res) => {
- return new Promise((resolve, reject) => {
- uni.showLoading({
- title: '支付中...',
- mask: true
- })
- uni.requestPayment({
- // #ifdef MP-WEIXIN
- provider: 'wxpay', //支付类型-固定值
- partnerid: res.appId, // 微信支付商户号
- timeStamp: res.timeStamp, // 时间戳(单位:秒)
- nonceStr: res.nonceStr, // 随机字符串
- package: res.package, // 固定值
- signType: res.signType, //固定值
- paySign: res.paySign, //签名
- // #endif
- // #ifdef MP-ALIPAY
- provider: 'alipay', //支付类型-固定值
- orderInfo: res.tradeNo, // 微信支付商户号
- // #endif
- success: (res) => {
- console.log('pay');
- console.log(res);
- uni.hideLoading()
- // #ifdef MP-WEIXIN
- uni.showToast({
- title: "支付成功",
- icon:'none'
- })
- console.log('支付成功')
- resolve(res)
- // #endif
- // #ifdef MP-ALIPAY
- if (res.resultCode == '9000') {
- uni.showToast({
- title: "支付成功",
- icon:'none'
- })
- resolve(res)
- } else {
- uni.showToast({
- title: "支付失败",
- icon:'none'
- })
- }
- // #endif
- },
- fail: (res) => {
- setTimeout(() => {
- uni.hideLoading()
- }, 1000)
- reject(false)
- },
-
- });
- })
-}
\ No newline at end of file
+ return new Promise((resolve, reject) => {
+ uni.showLoading({
+ title: "支付中...",
+ mask: true,
+ });
+ uni.requestPayment({
+ // #ifdef MP-WEIXIN
+ provider: "wxpay", //支付类型-固定值
+ partnerid: res.appId, // 微信支付商户号
+ timeStamp: res.timeStamp, // 时间戳(单位:秒)
+ nonceStr: res.nonceStr, // 随机字符串
+ package: res.package, // 固定值
+ signType: res.signType, //固定值
+ paySign: res.paySign, //签名
+ // #endif
+ // #ifdef MP-ALIPAY
+ provider: "alipay", //支付类型-固定值
+ orderInfo: res.tradeNo, // 微信支付商户号
+ // #endif
+ success: (res) => {
+ console.log("pay");
+ console.log(res);
+ uni.hideLoading();
+ // #ifdef MP-WEIXIN
+ uni.showToast({
+ title: "支付成功",
+ icon: "none",
+ });
+ console.log("支付成功");
+ resolve(true);
+ // #endif
+ // #ifdef MP-ALIPAY
+ if (res.resultCode == "9000") {
+ uni.showToast({
+ title: "支付成功",
+ icon: "none",
+ });
+ resolve(true);
+ } else {
+ uni.showToast({
+ title: "支付失败",
+ icon: "none",
+ });
+ reject(false);
+ }
+ // #endif
+ },
+ fail: (res) => {
+ setTimeout(() => {
+ uni.hideLoading();
+ }, 1000);
+ reject(false);
+ },
+ });
+ });
+};