Bläddra i källkod

feat(mall): 完善商城售后退款金额计算

- 秒杀与团购商品按活动价退款,普通商品按整单优惠和红包比例计算
- 展示活动及优惠信息,并在花店端增加退款审核入口
shizhongqi 1 dag sedan
förälder
incheckning
759e1f8db3
2 ändrade filer med 168 tillägg och 18 borttagningar
  1. 2 1
      hdApp/src/admin/home/apply.vue
  2. 166 17
      mallApp/src/pages/order/refund.vue

+ 2 - 1
hdApp/src/admin/home/apply.vue

@@ -98,7 +98,8 @@ export default {
             { name: '配送方式', icon: 'delivery-outline', url: '/admin/home/psMethod', pf: 1 },
             { name: '配置范围', icon: 'address', url: '/admin/shop/sk', pf: 1 },
             { name: '分销规则', icon: 'settings-filled', url: '/admin/home/distributionRule', pf: 1 },
-            { name: '分销报表', icon: 'stats-pie-filled', url: '/admin/home/distributionReport', pf: 1 }
+            { name: '分销报表', icon: 'stats-pie-filled', url: '/admin/home/distributionReport', pf: 1 },
+            { name: '退款审核', icon: 'order-filled', url: '/admin/order/mallRefundList', pf: 1 }
           ]
         },
         {

+ 166 - 17
mallApp/src/pages/order/refund.vue

@@ -2,6 +2,7 @@
   商城订单申请售后页
   供订单详情「申请售后」进入;顾客勾选商品/金额后提交待审核申请,商家在 hdApp 审核
   撤销后仍可重新申请:把已取消记录改回待审核
+  退货并退款金额:普通商品按勾选原价分摊会员折/满减;秒杀、团购按活动单价退,不再叠整单优惠
 -->
 <template>
 	<view class="refund-page">
@@ -92,6 +93,7 @@
 								<text class="product-name-compact">{{ res.name }}</text>
 								<view class="product-meta">
 									<text class="stock-info">{{ parseFloat(res.num) }}份 × ¥{{ parseFloat(res.unitPrice) }}</text>
+									<text class="activity-info" v-if="goodsActivityLabel(res)">{{ goodsActivityLabel(res) }}</text>
 									<text class="available-info">可退 {{ getRemainNum(res) }}份</text>
 								</view>
 							</view>
@@ -117,6 +119,7 @@
 								<text class="product-name-compact">{{ res.name }}</text>
 								<view class="product-meta">
 									<text class="stock-info">{{ parseFloat(res.num) }}{{ res.unitName || '份' }} × ¥{{ parseFloat(res.unitPrice) }}</text>
+									<text class="activity-info" v-if="goodsActivityLabel(res)">{{ goodsActivityLabel(res) }}</text>
 									<text class="available-info">可退 {{ getRemainNum(res) }}{{ res.unitName || '份' }}</text>
 								</view>
 							</view>
@@ -160,6 +163,14 @@
 						<text class="amount-label">可退金额</text>
 						<text class="amount-value available">¥{{ couldRefundPrice }}</text>
 					</view>
+					<view class="amount-row" v-if="hasDiscountAmount">
+						<text class="amount-label">优惠金额</text>
+						<text class="amount-value">¥{{ parseFloat(orderInfo.discountAmount) || 0 }}</text>
+					</view>
+					<view class="amount-row" v-if="hasMeetCut">
+						<text class="amount-label">订单满减</text>
+						<text class="amount-value">¥{{ parseFloat(orderInfo.orderReachDiscountPrice) || 0 }}</text>
+					</view>
 					<view class="amount-row" v-if="hasHbAmount">
 						<text class="amount-label">红包减免</text>
 						<text class="amount-value">¥{{ parseFloat(orderInfo.hbAmount) || 0 }}</text>
@@ -168,7 +179,7 @@
 						<text class="amount-label required">退款金额</text>
 						<text class="amount-value refund-money">¥{{ refundMoney }}</text>
 					</view>
-					<view class="amount-hint" v-if="refundType == 1">按勾选数量自动计算,不可修改</view>
+					<view class="amount-hint" v-if="refundType == 1">秒杀/团购按活动价,其余已计入优惠与满减,不可修改</view>
 					<view class="amount-hint" v-else>仅退款默认按可退金额申请,全额退清后未过期红包将自动退回</view>
 				</view>
 			</view>
@@ -243,27 +254,38 @@ export default {
 		hasHbAmount() {
 			return this.orderInfo.hbAmount && Number(this.orderInfo.hbAmount) > 0
 		},
+		/** 是否展示优惠金额(会员折等整单优惠) */
+		hasDiscountAmount() {
+			return Number(this.orderInfo.discountAmount) > 0
+		},
+		/** 是否展示订单满减 */
+		hasMeetCut() {
+			return Number(this.orderInfo.orderReachDiscountPrice) > 0
+		},
+		/** 整单拼团:下单已跳过会员折/满减,商品按团购价退 */
+		isGroupBuyOrder() {
+			return Number(this.orderInfo.groupBuyId) > 0
+		},
 		/**
-		 * 退款金额:退货并退款按勾选数量×单价;仅退款用可退金额
+		 * 退款金额:仅退款用可退金额;退货并退款时秒杀/团购按活动价,其余分摊整单优惠
 		 */
 		refundMoney() {
 			if (this.refundType == 2) {
 				return this.couldRefundPrice
 			}
-			let price = 0
-			;(this.goodsInfoList || []).forEach(item => {
-				const count = Number(item.refundCount) || 0
-				if (count > 0) {
-					price += count * Number(item.unitPrice || 0)
-				}
-			})
-			;(this.itemInfoList || []).forEach(item => {
-				const count = Number(item.refundCount) || 0
-				if (count > 0) {
-					price += count * Number(item.unitPrice || 0)
-				}
-			})
-			return parseFloat(price.toFixed(2))
+			const parts = this.sumSelectedPriceParts()
+			const activityMoney = Number(parts.activity) || 0
+			const normalMoney = this.applyOrderDiscounts(Number(parts.normal) || 0)
+			const money = activityMoney + normalMoney
+			if (!(money > 0)) {
+				return 0
+			}
+			const could = Number(this.couldRefundPrice) || 0
+			const capped = money > could ? could : money
+			if (!(capped > 0)) {
+				return 0
+			}
+			return parseFloat(capped.toFixed(2))
 		}
 	},
 	methods: {
@@ -275,6 +297,124 @@ export default {
 		getRemainNum(res) {
 			return Number(res.num) - Number(res.refundNum || 0)
 		},
+		/**
+		 * 商品活动标签:秒杀行 / 整单拼团。给模板展示,避免复杂表达式
+		 * @param {object} res 订单商品或花材行
+		 * @returns {string} 秒杀|团购|空
+		 */
+		goodsActivityLabel(res) {
+			if (this.isGroupBuyOrder) {
+				return '团购'
+			}
+			if (res && Number(res.seckillGoodsId) > 0) {
+				return '秒杀'
+			}
+			return ''
+		},
+		/**
+		 * 是否按活动价退、不摊会员折/满减:拼团整单,或花束行 seckillGoodsId>0
+		 * @param {object} item 订单行
+		 */
+		isActivityLine(item) {
+			if (this.isGroupBuyOrder) {
+				return true
+			}
+			return Number(item && item.seckillGoodsId) > 0
+		},
+		/**
+		 * 勾选退货拆成活动价合计与普通原价合计
+		 * 秒杀/团购按行上的活动单价;普通商品再去分摊整单优惠
+		 */
+		sumSelectedPriceParts() {
+			let activity = 0
+			let normal = 0
+			const addList = list => {
+				;(list || []).forEach(item => {
+					const count = Number(item.refundCount) || 0
+					if (!(count > 0)) {
+						return
+					}
+					const line = count * Number(item.unitPrice || 0)
+					if (this.isActivityLine(item)) {
+						activity += line
+					} else {
+						normal += line
+					}
+				})
+			}
+			addList(this.goodsInfoList)
+			addList(this.itemInfoList)
+			return {
+				activity: parseFloat(activity.toFixed(2)),
+				normal: parseFloat(normal.toFixed(2))
+			}
+		},
+		/**
+		 * 整单里秒杀/团购商品原价合计,用来从优惠分摊基数里剔除
+		 * 下单时会员折/满减本来就不打在这些活动价上
+		 */
+		sumOrderActivityOriginal() {
+			if (this.isGroupBuyOrder) {
+				return Number(this.orderInfo.goodsPrice) || 0
+			}
+			let price = 0
+			;(this.goodsInfoList || []).forEach(item => {
+				if (Number(item.seckillGoodsId) > 0) {
+					price += Number(item.num || 0) * Number(item.unitPrice || 0)
+				}
+			})
+			return parseFloat(price.toFixed(2))
+		},
+		/**
+		 * 整单优惠分摊基数:会员折/满减按「商品+运费包装-红包」后的应付计算,
+		 * prePrice 已扣红包,这里加回红包得到扣优惠前的应付,与下单口径一致
+		 */
+		getDiscountAllocBase() {
+			const info = this.orderInfo || {}
+			const hb = Number(info.hbAmount) || 0
+			const pre = Number(info.prePrice) || 0
+			if (pre > 0) {
+				return parseFloat((pre + hb).toFixed(2))
+			}
+			const goods = Number(info.goodsPrice) || 0
+			const send = Number(info.sendCost) || 0
+			const pack = Number(info.packingFee) || 0
+			const labour = Number(info.labourCost) || 0
+			const service = Number(info.serviceFee) || 0
+			const base = goods + send + pack + labour + service
+			return parseFloat(base.toFixed(2))
+		},
+		/**
+		 * 只对普通商品勾选原价分摊会员折、满减、红包
+		 * 秒杀已按活动价成交,红包/会员折打在非秒杀应付上,故从分摊基数剔除活动商品原价
+		 * @param {number} original 普通商品勾选原价合计
+		 * @returns {number} 扣除整单优惠后的应退金额
+		 */
+		applyOrderDiscounts(original) {
+			if (!(original > 0)) {
+				return 0
+			}
+			const info = this.orderInfo || {}
+			const discountAmount = Number(info.discountAmount) || 0
+			const meetCut = Number(info.orderReachDiscountPrice) || 0
+			const hbAmount = Number(info.hbAmount) || 0
+			const discountTotal = discountAmount + meetCut + hbAmount
+			const allocBase = this.getDiscountAllocBase()
+			const activityOriginal = this.sumOrderActivityOriginal()
+			let normalBase = allocBase - activityOriginal
+			if (!(normalBase > 0)) {
+				normalBase = 0
+			}
+			if (!(discountTotal > 0) || !(normalBase > 0)) {
+				return parseFloat(original.toFixed(2))
+			}
+			const deduct = original * discountTotal / normalBase
+			const money = original - deduct
+			if (!(money > 0)) {
+				return 0
+			}
+			return parseFloat(money.toFixed(2))
+		},
 		/**
 		 * 成品退货数量输入
 		 * 不用 v-model 绑 v-for 项,避免 key 表达式写入 data-event-opts
@@ -371,7 +511,8 @@ export default {
 						unitType: ele.unitType || 0,
 						unitName: ele.unitName || '份',
 						unitPrice: ele.unitPrice,
-						property: 0
+						property: 0,
+						seckillGoodsId: Number(ele.seckillGoodsId) || 0
 					})
 				}
 			})
@@ -586,6 +727,14 @@ export default {
 			border-radius: 4upx;
 			margin-right: 12upx;
 		}
+		.activity-info {
+			font-size: 24upx;
+			color: $mainColor;
+			background: rgba(255, 40, 66, 0.08);
+			padding: 4upx 8upx;
+			border-radius: 4upx;
+			margin-right: 12upx;
+		}
 		.available-info {
 			font-size: 24upx;
 			color: $mainColor;