fix: 优惠券修改

This commit is contained in:
YeMingfei666 2025-09-28 13:26:39 +08:00
parent a82f116918
commit 0df2d7198f
6 changed files with 376 additions and 277 deletions

View File

@ -8,13 +8,13 @@ VITE_APP_BASE_API=/dev-api
# VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 测试 # VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 测试
# VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式 # VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式
VITE_APP_API_URL=http://192.168.0.71/ # 本地 VITE_APP_API_URL=http://192.168.1.42/ # 本地
# WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws # WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws
# VITE_APP_WS_ENDPOINT=wss://sockets.sxczgkj.com/wss # VITE_APP_WS_ENDPOINT=wss://sockets.sxczgkj.com/wss
# VITE_APP_WS_ENDPOINT=wss://czgeatws.sxczgkj.com/wss # 正式 # VITE_APP_WS_ENDPOINT=wss://czgeatws.sxczgkj.com/wss # 正式
VITE_APP_WS_ENDPOINT=ws://192.168.0.71:2348 # 本地 VITE_APP_WS_ENDPOINT=ws://192.168.1.42:2348 # 本地
# 启用 Mock 服务 # 启用 Mock 服务

View File

@ -1,4 +1,5 @@
import { BigNumber } from "bignumber.js"; import { BigNumber } from "bignumber.js";
import _ from "lodash";
/** /**
* 返回可以抵扣优惠券的商品列表,过滤掉赠品临时商品,价格从高到低排序 * 返回可以抵扣优惠券的商品列表,过滤掉赠品临时商品,价格从高到低排序
@ -10,6 +11,9 @@ export function returnCanDikouGoods(arr, user) {
.filter((v) => { .filter((v) => {
return v.is_temporary != 1 && v.is_gift != 1; return v.is_temporary != 1 && v.is_gift != 1;
}) })
.filter((v) => {
return v.num > 0;
})
.sort((a, b) => { .sort((a, b) => {
return returnGoodsPrice(b, user) - returnGoodsPrice(a, user); return returnGoodsPrice(b, user) - returnGoodsPrice(a, user);
}); });
@ -65,17 +69,53 @@ export function returnCoupType(coupon) {
} }
/** /**
* 判券是否可用 * 返回商品券抵扣后的商品列表
* @param canDikouGoodsArr 可抵扣商品列表 * @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券 * @param selCoupon 已选择的优惠券列表
* @param goodsOrderPrice 商品订单金额
* @param user 用户信息 * @param user 用户信息
*/ */
export function returnCouponCanUse(canDikouGoodsArr, coupon, goodsOrderPrice, user) { export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
const goodsCouponGoods = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
prve.push(...cur.discount.hasDiscountGoodsArr);
return prve;
}, []);
const arr = _.cloneDeep(canDikouGoodsArr)
.map((v) => {
const findCart = goodsCouponGoods.find((carts) => carts.id == v.id);
if (findCart) {
v.num -= findCart.num;
}
return v;
})
.filter((v) => v.num > 0);
return arr;
}
/**
* 判断优惠券是否可使用
*
* @param {Object} args - 函数参数集合
* @param {Array} args.canDikouGoodsArr - 可参与抵扣的商品列表每个元素包含商品信息productIdnum等
* @param {Object} args.coupon - 优惠券信息对象
* @param {boolean} args.coupon.use - 优惠券是否启用true为启用false为禁用
* @param {Array} args.coupon.useFoods - 优惠券适用的商品ID列表空数组表示适用全部商品
* @param {number} args.coupon.fullAmount - 优惠券使用门槛金额
* @param {number} args.coupon.type - 优惠券类型1:满减券, 2:商品券, 3:折扣券, 4:第二件半价券, 6:买一送一券
* @param {number} args.goodsOrderPrice - 订单中所有商品的总金额未筛选时的初始金额
* @param {Object} args.user - 用户信息对象用于计算商品价格如会员价等
* @param {Object} args.user - 用户信息对象用于计算商品价格如会员价等
* @param {Object} args.selCoupon - 已经选择的优惠券信息对象
* @returns {boolean} - 优惠券是否可用true可用false不可用
*/
export function returnCouponCanUse(args) {
let { canDikouGoodsArr, coupon, goodsOrderPrice, user, selCoupon } = args;
if (!coupon.use) { if (!coupon.use) {
return false; return false;
} }
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user);
// 计算门槛金额 // 计算门槛金额
let fullAmount = goodsOrderPrice; let fullAmount = goodsOrderPrice;
//是否抵扣全部商品 //是否抵扣全部商品
@ -143,7 +183,7 @@ export function returnCouponCanUse(canDikouGoodsArr, coupon, goodsOrderPrice, us
} }
if (coupon.type == 3) { if (coupon.type == 3) {
//折扣券 //折扣券
return false; return true;
} }
} }
@ -176,17 +216,65 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user) {
* @param canDikouGoodsArr 可抵扣商品列表 * @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券 * @param coupon 优惠券
* @param user 用户信息 * @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
*/ */
export function returnCouponDiscount(canDikouGoodsArr, coupon, user) { export function returnCouponDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon) {
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user);
if (coupon.type == 2) { if (coupon.type == 2) {
return returnCouponProductDiscount(canDikouGoodsArr, coupon, user); return returnCouponProductDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
} }
if (coupon.type == 6) { if (coupon.type == 6) {
return returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user); return returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
} }
if (coupon.type == 4) { if (coupon.type == 4) {
return returnSecoendDiscount(canDikouGoodsArr, coupon, user); return returnSecoendDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
} }
if (coupon.type == 3) {
return returnCouponZhekouDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon);
}
}
/**
* 折扣券抵扣金额
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
*
*/
export function returnCouponZhekouDiscount(
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon
) {
const { discountRate, maxDiscountAmount } = coupon;
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return prve + cur.discount.discountPrice;
}, 0);
goodsOrderPrice -= goodsCouponDiscount;
// 使用bignumber处理高精度计算
// 1. 计算折扣率百分比转小数discountRate / 100
const discountRatio = new BigNumber(discountRate).dividedBy(100);
// 2. 计算优惠比例1 - 折扣率例如8折的优惠比例是 1 - 0.8 = 0.2
const discountAmountRatio = new BigNumber(1).minus(discountRatio);
// 3. 计算折扣金额:商品订单金额 × 优惠比例
let discountPrice = new BigNumber(goodsOrderPrice).times(discountAmountRatio).toNumber();
if (maxDiscountAmount != 0) {
discountPrice = discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
}
return {
discountPrice, // 折扣抵扣金额(即优惠的金额)
hasDiscountGoodsArr: [],
};
} }
/** /**

View File

@ -269,7 +269,7 @@ export function convertBackendCouponToToolCoupon(
currentTime: Date = new Date() currentTime: Date = new Date()
): Coupon | null { ): Coupon | null {
// 1. 基础校验必选字段缺失直接返回null // 1. 基础校验必选字段缺失直接返回null
if (!backendCoupon.id || backendCoupon.couponType === undefined || !backendCoupon.title) { if (!backendCoupon.id || backendCoupon.type === undefined || !backendCoupon.title) {
console.warn('优惠券必选字段缺失', backendCoupon); console.warn('优惠券必选字段缺失', backendCoupon);
return null; return null;
} }

View File

@ -19,33 +19,20 @@
label-position="right" label-position="right"
> >
<el-form-item label="充值面额" required> <el-form-item label="充值面额" required>
<el-button type="primary" @click="AddDialogRef.open()"> <el-button type="primary" @click="AddDialogRef.open()">添加面额</el-button>
添加面额
</el-button>
</el-form-item> </el-form-item>
<el-form-item prop="rechargeDetailList"> <el-form-item prop="rechargeDetailList">
<el-table :data="form.rechargeDetailList" border stripe> <el-table :data="form.rechargeDetailList" border stripe>
<el-table-column label="ID" prop="id"></el-table-column> <el-table-column label="ID" prop="id"></el-table-column>
<el-table-column <el-table-column label="充值金额(元)" prop="amount"></el-table-column>
label="充值金额(元)" <el-table-column label="赠送金额" prop="rewardAmount"></el-table-column>
prop="amount" <el-table-column label="赠送积分" prop="rewardPoints"></el-table-column>
></el-table-column>
<el-table-column
label="赠送金额"
prop="rewardAmount"
></el-table-column>
<el-table-column
label="赠送积分"
prop="rewardPoints"
></el-table-column>
<el-table-column label="赠送优惠券" prop="couponInfoList"> <el-table-column label="赠送优惠券" prop="couponInfoList">
<template #default="scope"> <template #default="scope">
<div class="column"> <div class="column">
<div v-for="item in scope.row.couponInfoList"> <div v-for="item in scope.row.couponInfoList">
<el-tag type="primary" disable-transitions> <el-tag type="primary" disable-transitions>
{{ couponListFilter(item.id) }}x{{ {{ couponListFilter(item.id) }}x{{ item.num }}
item.num
}}
</el-tag> </el-tag>
</div> </div>
</div> </div>
@ -63,9 +50,7 @@
<el-button <el-button
type="danger" type="danger"
link link
@click=" @click="form.rechargeDetailList.splice(scope.$index, 1)"
form.rechargeDetailList.splice(scope.$index, 1)
"
> >
删除 删除
</el-button> </el-button>
@ -73,11 +58,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-form-item> </el-form-item>
<el-form-item <el-form-item label="选择门店" prop="useType" v-if="shopInfo.isHeadShop">
label="选择门店"
prop="useType"
v-if="shopInfo.isHeadShop"
>
<el-radio-group v-model="form.useType"> <el-radio-group v-model="form.useType">
<el-radio label="全部门店" value="all"></el-radio> <el-radio label="全部门店" value="all"></el-radio>
<el-radio label="指定门店可用" value="part"></el-radio> <el-radio label="指定门店可用" value="part"></el-radio>
@ -102,11 +83,7 @@
<el-form-item label="自定义金额"> <el-form-item label="自定义金额">
<div class="column"> <div class="column">
<div class="center"> <div class="center">
<el-switch <el-switch v-model="form.isCustom" :active-value="1" :inactive-value="0" />
v-model="form.isCustom"
:active-value="1"
:inactive-value="0"
/>
<span class="tips">自定义金额不参与赠送优惠</span> <span class="tips">自定义金额不参与赠送优惠</span>
</div> </div>
</div> </div>
@ -114,11 +91,7 @@
<el-form-item label="充值并下单"> <el-form-item label="充值并下单">
<div class="column"> <div class="column">
<div class="center"> <div class="center">
<el-switch <el-switch v-model="form.isOrder" :active-value="1" :inactive-value="0" />
v-model="form.isOrder"
:active-value="1"
:inactive-value="0"
/>
<span class="tips">开启后订单支付页面显示充值选项</span> <span class="tips">开启后订单支付页面显示充值选项</span>
</div> </div>
</div> </div>
@ -134,16 +107,12 @@
placeholder="填写内容" placeholder="填写内容"
></el-input> ></el-input>
</div> </div>
<div class="item textarea-num"> <div class="item textarea-num">{{ form.remark.length }}/250字内单文本</div>
{{ form.remark.length }}/250字内单文本
</div>
</div> </div>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="footer"> <div class="footer">
<el-button type="primary" size="large" @click="submitHandle"> <el-button type="primary" size="large" @click="submitHandle">保存</el-button>
保存
</el-button>
<el-button size="large" @click="back">取消</el-button> <el-button size="large" @click="back">取消</el-button>
</div> </div>
</el-tab-pane> </el-tab-pane>
@ -274,7 +243,7 @@ async function shopRechargeGetAjax() {
const res = await shopRechargeGet(); const res = await shopRechargeGet();
res.rechargeDetailList.map((item) => { res.rechargeDetailList.map((item) => {
item.couponInfoList.map((val) => { item.couponInfoList.map((val) => {
val.id = val.coupon.id; val.id = val.coupon ? val.coupon.id : null;
}); });
}); });
form.value = res; form.value = res;

View File

@ -77,7 +77,7 @@ const state = reactive({
show: false, show: false,
query: { query: {
name: "", name: "",
isVip: 1, // isVip: 1,
}, },
tableData: { tableData: {
data: [], data: [],

View File

@ -1,5 +1,5 @@
<template> <template>
<el-dialog width="700px" :title="title" v-model="show" top="20px" @close="reset"> <el-dialog width="900px" :title="title" v-model="show" top="20px" @close="reset">
<div class="u-p-15"> <div class="u-p-15">
<div class=""> <div class="">
<el-tabs v-model="activeName" @tab-click="tabClick"> <el-tabs v-model="activeName" @tab-click="tabClick">
@ -20,11 +20,16 @@
{{ UTILS.returnCoupType(scope.row) }} {{ UTILS.returnCoupType(scope.row) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="discountAmount" label="抵扣"> <el-table-column prop="discountAmount" label="抵扣" align="center">
<template v-slot="scope"> <template v-slot="scope">
<span class="color-red">{{ scope.row.discountAmount }}</span> <span class="color-red">{{ scope.row.discountAmount }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="discountAmount" label="最大抵扣金额" align="center">
<template v-slot="scope">
<span class="color-red">{{ scope.row.maxDiscountAmount }}</span>
</template>
</el-table-column>
<el-table-column prop="discountAmount" label="限制" width="180"> <el-table-column prop="discountAmount" label="限制" width="180">
<template v-slot="scope"> <template v-slot="scope">
<div class="u-flex"> <div class="u-flex">
@ -255,10 +260,22 @@ async function getcoup() {
quans.value.coupon = res quans.value.coupon = res
.filter((v) => v.type != 2) .filter((v) => v.type != 2)
.filter((coupon) => { .filter((coupon) => {
return UTILS.returnCouponCanUse(canDikouGoodsArr, coupon, orderPrice.value, props.user); return UTILS.returnCouponCanUse({
canDikouGoodsArr,
coupon,
orderPrice: orderPrice.value,
user: props.user,
selCoupon: quansSelArr.value,
});
}) })
.map((v) => { .map((v) => {
const discount = UTILS.returnCouponDiscount(canDikouGoodsArr, v, props.user); const discount = UTILS.returnCouponDiscount(
canDikouGoodsArr,
v,
props.user,
orderPrice.value,
quansSelArr.value
);
return { return {
...v, ...v,
discount, discount,
@ -268,11 +285,23 @@ async function getcoup() {
quans.value.productCoupon = res quans.value.productCoupon = res
.filter((v) => v.type == 2) .filter((v) => v.type == 2)
.filter((coupon) => { .filter((coupon) => {
return UTILS.returnCouponCanUse(canDikouGoodsArr, coupon, orderPrice.value, props.user); return UTILS.returnCouponCanUse({
canDikouGoodsArr,
coupon,
orderPrice: orderPrice.value,
user: props.user,
selCoupon: [],
});
}) })
.map((v) => { .map((v) => {
const findGoods = goodsArr.find((goods) => goods.productId == v.proId); const findGoods = goodsArr.find((goods) => goods.productId == v.proId);
const discount = UTILS.returnCouponDiscount(canDikouGoodsArr, v, props.user); const discount = UTILS.returnCouponDiscount(
canDikouGoodsArr,
v,
props.user,
orderPrice.value,
[]
);
return { return {
...v, ...v,
productImg: findGoods ? findGoods.productImg : "", productImg: findGoods ? findGoods.productImg : "",
@ -320,6 +349,19 @@ defineExpose({
close, close,
open, open,
}); });
watch(
() => activeName.value,
() => {
getcoup();
}
);
watch(
() => quansSelArr.value,
() => {
getcoup();
}
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>