代客下单问题修复,积分上传问题修复

This commit is contained in:
2025-12-12 17:56:26 +08:00
parent 9a0164eff6
commit 6f1864771d
29 changed files with 12071 additions and 7571 deletions

View File

@@ -10,4 +10,27 @@ export const pointsShopList = (data) => {
})
}
export const pointsConfig = (data) => {
return request({
url: prveUrl + '/user/point/pointsConfig',
method: 'get',
data: data
})
}
export const userPoints = (data) => {
return request({
url: prveUrl + '/user/point/userPoints',
method: 'get',
data: data
})
}
export const userRecord = (data) => {
return request({
url: prveUrl + '/user/point/userRecord',
method: 'get',
data: data
})
}

View File

@@ -29,7 +29,7 @@
>
<image
@click.stop="()=>{}"
@click.stop="onBack"
class="max-img"
:src="item"
:lazy-load="true"

0
lib/carts.ts Normal file
View File

852
lib/coupon.ts Normal file
View File

@@ -0,0 +1,852 @@
import { BigNumber } from "bignumber.js";
import _ from "lodash";
import {
ShopInfo,
couponCalcParams,
BaseCartItem,
TimeLimitDiscountConfig,
CanDikouGoodsArrArgs,
Coupon,
ShopUserInfo,
GoodsType,
BackendCoupon,
ExchangeCalculationResult,
PointDeductionRule,
OrderCostSummary,
} from "./types";
import { getCompatibleFieldValue } from "./utils";
/**
* 返回商品单价
* @param goods 商品
* @param user 用户信息
* @param {Object} shopInfo
*/
export function returnGoodsPrice(
goods: BaseCartItem,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined
) {
if (!goods) {
return 0;
}
//是否可以使用会员价
const canUseVipPrice =
user &&
user.isVip &&
user.isMemberPrice &&
goods.memberPrice * 1 > 0 &&
shopInfo &&
shopInfo.isMemberPrice;
// 商家改价
if (goods.discount_sale_amount && goods.discount_sale_amount * 1 > 0) {
return goods.salePrice;
}
// 限时折扣
if (limitTimeDiscount && limitTimeDiscount.id) {
//优先使用
// 兼容 isTimeDiscount/is_time_discount这里顺便处理该字段的命名兼容
const isTimeDiscount = getCompatibleFieldValue(
goods,
"isTimeDiscount",
"is_time_discount"
);
if (isTimeDiscount) {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
const canUseFoods = limitTimeDiscount.foods.split(",");
const canUseLimit =
limitTimeDiscount.foodType == 1 ||
canUseFoods.includes(`${goods.productId}`);
if (canUseLimit && limitTimeDiscount.discountPriority == "limit-time") {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
if (canUseLimit && limitTimeDiscount.discountPriority == "vip-price") {
if (canUseVipPrice) {
return goods.memberPrice;
} else {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
}
}
if (canUseVipPrice) {
return goods.memberPrice;
}
return goods.salePrice;
}
/**
* 返回商品分组
* @param arr 商品列表
*/
export function returnGoodsGroupMap(arr: BaseCartItem[]) {
let map: { [key: string]: BaseCartItem[] } = {};
arr.forEach((v) => {
const key = v.productId + "_" + v.skuId;
if (!map[key]) {
map[key] = [];
}
map[key].push(v);
});
return map;
}
interface CouponTypes {
1: "满减券";
2: "商品券";
3: "折扣券";
4: "第二件半价券";
5: "消费送券";
6: "买一送一券";
7: "固定价格券";
8: "免配送费券";
}
/**
* 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
* @param coupon
*/
export function returnCoupType(coupon: Coupon) {
const couponTypes: CouponTypes = {
1: "满减券",
2: "商品券",
3: "折扣券",
4: "第二件半价券",
5: "消费送券",
6: "买一送一券",
7: "固定价格券",
8: "免配送费券",
};
return couponTypes[coupon.type as keyof CouponTypes] || "未知类型";
}
/**
* 返回商品券抵扣后的商品列表
* @param canDikouGoodsArr 可抵扣商品列表
* @param selCoupon 已选择的优惠券列表
* @param user 用户信息
*/
export function returnCanDikouGoodsArr(args: CanDikouGoodsArrArgs) {
const { canDikouGoodsArr, selCoupon, user, shopInfo, limitTimeDiscount } =
args;
const types = [2, 4, 6];
// 收集已抵扣商品并关联对应的优惠券类型
const goodsCouponGoods = selCoupon
.filter((v) => types.includes(v.type))
.reduce((prev: BaseCartItem[], cur) => {
// 给每个抵扣商品添加所属优惠券类型
if (cur && cur.discount) {
const goodsWithType = cur.discount.hasDiscountGoodsArr.map((goods) => ({
...goods,
couponType: cur.type, // 记录该商品是被哪种类型的优惠券抵扣的
}));
prev.push(...goodsWithType);
}
return prev;
}, []);
const arr = _.cloneDeep(canDikouGoodsArr)
.map((v) => {
const findCart = goodsCouponGoods.find((carts) => carts.id == v.id);
if (findCart) {
// 根据优惠券类型判断扣减数量
if ([4, 6].includes(findCart.couponType ?? 0)) {
// 类型4第二件半价或6买一送一数量减2
if (v.num) {
v.num -= 2;
}
} else {
// 其他类型如类型2商品券按原逻辑扣减对应数量
if (v.num) {
v.num -= findCart.num ?? 0;
}
}
}
return v;
})
.filter((v) => {
const canUseNum = (v.num ?? 0) - (v.returnNum || 0);
// 兼容 is_temporary/isTemporary 和 is_gift/isGift
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
if (canUseNum <= 0 || isTemporary || isGift) {
return false;
}
return true;
}); // 过滤掉数量<=0的商品,赠菜,临时菜
return arr;
}
/**
* 返回商品是否享用了会员价/会员折扣
* @param {*} goods
*/
function returnGoodsIsUseVipPrice(
shopInfo: ShopInfo,
user: ShopUserInfo,
goods: BaseCartItem
) {
// 兼容 isTimeDiscount/is_time_discount
const isTimeDiscount = getCompatibleFieldValue(
goods,
"isTimeDiscount",
"is_time_discount"
);
if (isTimeDiscount) {
return false;
}
if (shopInfo.isMemberPrice != 1 || user.isVip != 1) {
return false;
}
if (shopInfo.isMemberPrice == 1 && user.isVip == 1) {
if (goods.memberPrice <= 0) {
return false;
} else {
return true;
}
}
return false;
}
/**
* 返回可以计算抵扣金额的商品列表
*/
function returnCanCalcGoodsList(
canCalcGoodsArr: BaseCartItem[],
coupon: Coupon,
shopInfo: ShopInfo,
user: ShopUserInfo
) {
return canCalcGoodsArr.filter((goods) => {
// 兼容 isTimeDiscount/is_time_discount
const isTimeDiscount = getCompatibleFieldValue(
goods,
"isTimeDiscount",
"is_time_discount"
);
if (!coupon.discountShare && isTimeDiscount) {
return false;
}
if (
!coupon.vipPriceShare &&
returnGoodsIsUseVipPrice(shopInfo, user, goods)
) {
return false;
}
return true;
});
}
/**
* 判断优惠券是否可使用,并返回不可用原因
*
* @param {Object} args - 函数参数集合
* @param {Array} args.canDikouGoodsArr - 可参与抵扣的商品列表
* @param {Object} args.coupon - 优惠券信息对象
* @param {boolean} args.coupon.use - 优惠券是否启用
* @param {Array} args.coupon.useFoods - 优惠券适用的商品ID列表
* @param {number} args.coupon.fullAmount - 优惠券使用门槛金额
* @param {number} args.coupon.type - 优惠券类型
* @param {number} args.goodsOrderPrice - 订单中所有商品的总金额
* @param {Object} args.user - 用户信息对象
* @param {Object} args.selCoupon - 已经选择的优惠券信息对象
* @param {Object} args.shopInfo
* @param {boolean} args.limitTimeDiscount - 限时折扣
* @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因
*/
export function returnCouponCanUse(args: couponCalcParams) {
let {
canDikouGoodsArr,
coupon,
goodsOrderPrice,
user,
selCoupon,
shopInfo,
isMemberPrice,
limitTimeDiscount,
} = args;
// 优惠券未启用
if (!coupon.use) {
return {
canUse: false,
reason: coupon.noUseRestrictions || "不在可用时间段内",
};
}
if (
limitTimeDiscount &&
limitTimeDiscount.id &&
limitTimeDiscount.foodType == 1 &&
!coupon.discountShare
) {
return {
canUse: false,
reason: coupon.noUseRestrictions || "不可与限时折扣同享",
};
}
// 计算门槛金额
let fullAmount = goodsOrderPrice;
canDikouGoodsArr = returnCanDikouGoodsArr(args);
//优惠券指定门槛商品列表
let canCalcGoodsArr = [...canDikouGoodsArr];
//部分商品参与门槛计算
if (coupon.thresholdFoods.length) {
canCalcGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.thresholdFoods.find((food) => food.id == v.productId);
});
}
canCalcGoodsArr = returnCanCalcGoodsList(
canCalcGoodsArr,
coupon,
shopInfo,
user
);
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return (
pre +
returnGoodsPrice(cur, user, shopInfo, limitTimeDiscount) * (cur.num || 0)
);
}, 0);
// 是否全部商品可用
const isDikouAll = coupon.useFoods.length === 0;
// 订单可用商品列表
let canUseGoodsArr: BaseCartItem[] = [];
if (!isDikouAll) {
canUseGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.useFoods.find((food) => food.id == v.productId);
});
}
// if (user.isVip && !coupon.vipPriceShare) {
// return {
// canUse: false,
// reason: "非会员可用",
// };
// }
if (selCoupon.length > 0 && !selCoupon[0].otherCouponShare) {
return {
canUse: false,
reason: "当前选中的券不可与其他券同享",
};
}
if (selCoupon.length > 0 && !coupon.otherCouponShare) {
return {
canUse: false,
reason: "当前选中的券不可与其他券同享",
};
}
// 满减券和折扣券计算门槛金额是否满足
if ([1, 3].includes(coupon.type)) {
if (canCalcGoodsArr.length <= 0) {
return {
canUse: false,
reason: "没有可参与计算门槛的商品",
};
}
// 不满足门槛金额
if (fullAmount < (coupon.fullAmount || 0)) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
// 商品兑换券,第二件半价和买一送一判断是否有可用商品
if ([2, 4, 5].includes(coupon.type)) {
// 没有符合条件的商品
if (isDikouAll && canDikouGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
};
}
if (!isDikouAll && canUseGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
};
}
if (coupon.type == 2) {
if (canCalcGoodsArr.length <= 0) {
return {
canUse: false,
reason: "没有符合计算门槛条件的商品",
};
}
if (fullAmount < (coupon.fullAmount || 0)) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
}
//商品兑换券是否达到门槛金额
if (coupon.type == 2 && goodsOrderPrice < (coupon.fullAmount || 0)) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
// 买一送一券特殊验证
if (coupon.type === 6) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => (v.num || 0) >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => (v.num || 0) >= 2);
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
// 第二件半价券特殊验证
if (coupon.type === 4) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => (v.num || 0) >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => (v.num || 0) >= 2);
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
// 所有条件都满足
return {
canUse: true,
reason: "",
};
}
/**
* 计算抵扣商品金额
* @param discountGoodsArr 可抵扣商品列表
* @param discountNum 抵扣数量
* @param user 用户信息
* @param {Object} shopInfo 店铺信息
*/
export function calcDiscountGoodsArrPrice(
discountGoodsArr: BaseCartItem[],
discountNum: number,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
let hasCountNum = 0;
let discountPrice = 0;
let hasDiscountGoodsArr:BaseCartItem[] = [];
for (let i = 0; i < discountGoodsArr.length; i++) {
if (hasCountNum >= discountNum) {
break;
}
const goods = discountGoodsArr[i];
const shengyuNum = discountNum - hasCountNum;
const num = Math.min(goods.num || 0, shengyuNum);
const realPrice = returnGoodsPrice(
goods,
user,
shopInfo,
limitTimeDiscount
);
discountPrice += realPrice * num;
hasCountNum += num;
if(goods){
hasDiscountGoodsArr.push({
...goods,
num,
});
}
}
return {
discountPrice,
hasDiscountGoodsArr,
};
}
/**
* 计算优惠券抵扣金额
* @param arr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponDiscount(
arr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
goodsOrderPrice: number,
selCoupon: Coupon[],
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
arr = returnCanDikouGoods(arr, user, shopInfo, limitTimeDiscount);
const canDikouGoodsArr = returnCanDikouGoodsArr({
canDikouGoodsArr: arr,
selCoupon,
user,
shopInfo,
limitTimeDiscount,
});
if (coupon.type == 2) {
return returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
}
if (coupon.type == 6) {
const result = returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
if (coupon.type == 4) {
return returnSecoendDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
}
if (coupon.type == 3) {
return returnCouponZhekouDiscount(
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon,
limitTimeDiscount
);
}
}
/**
* 折扣券抵扣金额
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponZhekouDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
goodsOrderPrice: number,
selCoupon: Coupon[],
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
let { discountRate, maxDiscountAmount } = coupon;
maxDiscountAmount = maxDiscountAmount || 0;
// 计算商品优惠券折扣总和使用BigNumber避免精度问题
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return new BigNumber(prve).plus(
new BigNumber(cur?.discount?.discountPrice || 0)
);
}, new BigNumber(0));
// 将商品订单价格转换为BigNumber并减去优惠券折扣
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(
goodsCouponDiscount
);
// 计算优惠比例:(100 - 折扣率) / 100
const discountAmountRatio = new BigNumber(100)
.minus(discountRate || 0)
.dividedBy(100);
// 计算折扣金额:调整后的商品订单金额 × 优惠比例
let discountPrice = adjustedGoodsOrderPrice
.times(discountAmountRatio)
.decimalPlaces(2, BigNumber.ROUND_FLOOR)
.toNumber();
// 应用最大折扣金额限制
if (maxDiscountAmount !== 0) {
discountPrice =
discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
}
return {
discountPrice, // 折扣抵扣金额(即优惠的金额)
hasDiscountGoodsArr: [],
};
}
/**
* 商品券抵扣金额
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
export function returnCouponProductDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
let { useFoods, discountNum, useRule } = coupon;
discountNum = discountNum || 0;
//抵扣商品数组
let discountGoodsArr:BaseCartItem[] = [];
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoodsArr = canDikouGoodsArr.slice(discountNum * -1).reverse();
} else {
discountGoodsArr = canDikouGoodsArr.slice(0, discountNum);
}
} else {
//抵扣选中商品
const discountSelGoodsArr = canDikouGoodsArr.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoodsArr = discountSelGoodsArr.slice(discountNum * -1).reverse();
} else {
discountGoodsArr = discountSelGoodsArr.slice(0, discountNum);
}
}
const result = calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
/**
* 返回买一送一券抵扣详情
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
function returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods:BaseCartItem | undefined = undefined;
//符合买一送一条件的商品(数量>=2 + 非临时/非赠品)
const canUseGoods = canDikouGoodsArr.filter((v) => {
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
return (v.num || 0) >= 2 && !isTemporary && !isGift;
});
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods[0];
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1[0];
}
}
let discountPrice = 0;
let hasDiscountGoodsArr: BaseCartItem[] = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods];
}
return {
discountPrice: discountPrice <= 0 ? 0 : discountPrice,
hasDiscountGoodsArr,
};
}
/**
* 返回第二件半价券抵扣详情
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
function returnSecoendDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods:BaseCartItem | undefined = undefined;
//符合条件的商品(数量>=2 + 非临时/非赠品)
const canUseGoods = canDikouGoodsArr.filter((v) => {
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
return (v.num || 0) >= 2 && !isTemporary && !isGift;
});
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods[0];
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1[0];
}
}
let discountPrice = 0;
let hasDiscountGoodsArr: BaseCartItem[] = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods];
}
//返回半价价格
return {
discountPrice:
discountPrice <= 0
? 0
: new BigNumber(discountPrice).dividedBy(2).toNumber(),
hasDiscountGoodsArr,
};
}
/**
* 返回可以抵扣优惠券的商品列表,过滤掉赠品、临时商品,价格从高到低排序
* @param arr 商品列表
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCanDikouGoods(
arr: BaseCartItem[],
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
const result = arr
.filter((v) => {
// 兼容 is_temporary/isTemporary 和 is_gift/isGift
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
return !isTemporary && !isGift;
})
.filter((v) => {
return (v.num || 0) > 0;
})
.sort((a, b) => {
return (
returnGoodsPrice(b, user, shopInfo, limitTimeDiscount) -
returnGoodsPrice(a, user, shopInfo, limitTimeDiscount)
);
});
return result;
}
export const utils = {
returnGoodsPrice,
returnGoodsGroupMap,
returnCoupType,
returnCanDikouGoods,
returnCanDikouGoodsArr,
returnCouponCanUse,
calcDiscountGoodsArrPrice,
returnCouponDiscount,
returnCouponProductDiscount,
returnCouponZhekouDiscount,
};
export default utils;

1275
lib/goods-1.0.47-back.ts Normal file

File diff suppressed because it is too large Load Diff

1388
lib/goods.ts Normal file

File diff suppressed because it is too large Load Diff

11
lib/index.ts Normal file
View File

@@ -0,0 +1,11 @@
export * from "./types";
import OrderPriceCalculator from "./goods";
import couponUtils from "./coupon";
import limitUtils from "./limit";
export { OrderPriceCalculator, couponUtils, limitUtils };
export default {
OrderPriceCalculator,
couponUtils,
limitUtils,
};

216
lib/limit.ts Normal file
View File

@@ -0,0 +1,216 @@
import BigNumber from "bignumber.js";
import _ from "lodash";
import {
BaseCartItem,
ShopUserInfo,
ShopInfo,
TimeLimitDiscountConfig,
CanReturnMemberPriceArgs,
returnPriceArgs,
} from "./types";
/**
* 判断商品是否可以使用限时折扣
* @param goods 商品对象
* @param limitTimeDiscountRes 限时折扣配置
* @param shopInfo 店铺信息
* @param shopUserInfo 店铺用户信息
* @param idKey 商品ID键名默认"id"
* @returns
*/
export function canUseLimitTimeDiscount(
goods: BaseCartItem,
limitTimeDiscountRes: TimeLimitDiscountConfig | null | undefined,
shopInfo: ShopInfo,
shopUserInfo: ShopUserInfo,
idKey = "id" as keyof BaseCartItem
) {
shopInfo = shopInfo || {};
shopUserInfo = shopUserInfo || {};
if(shopInfo.isMemberPrice){
shopUserInfo.isMemberPrice=1
}
if (!limitTimeDiscountRes || !limitTimeDiscountRes.id) {
return false;
}
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
const goodsCanUse =
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes(`${goods[idKey]}`);
if (!goodsCanUse) {
return false;
}
if (limitTimeDiscountRes.discountPriority == "limit-time") {
return true;
}
if (limitTimeDiscountRes.discountPriority == "vip-price") {
if (
shopUserInfo.isVip == 1 &&
shopUserInfo.isMemberPrice == 1 &&
goods.memberPrice * 1 > 0
) {
return false;
}
return true;
}
return false;
}
/**
* 返回商品显示价格
* @params {*} args 参数对象
* @params {*} args.goods 商品对象
* @params {*} args.shopInfo 店铺信息
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.shopUserInfo 店铺用户信息
* @returns
*/
export function returnPrice(args: returnPriceArgs) {
let {
goods,
shopInfo,
limitTimeDiscountRes,
shopUserInfo,
idKey = "product_id",
} = args;
limitTimeDiscountRes = limitTimeDiscountRes || {
foods: "",
foodType: 2,
discountPriority: "",
discountRate: 0,
id: 0,
shopId: 0,
useType: "",
};
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
const includesGoods =
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes("" + goods[idKey]);
shopInfo = shopInfo || {};
shopUserInfo = shopUserInfo || {};
if (
shopUserInfo.isMemberPrice == 1 &&
shopUserInfo.isVip == 1 &&
shopInfo.isMemberPrice == 1
) {
const memberPrice = goods.memberPrice || goods.salePrice;
//是会员而且启用会员价
if (limitTimeDiscountRes) {
//使用限时折扣
//限时折扣优先
if (limitTimeDiscountRes.discountPriority == "limit-time") {
if (includesGoods) {
return returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
});
} else {
return memberPrice;
}
}
if (
limitTimeDiscountRes.discountPriority == "vip-price" &&
includesGoods
) {
if (goods.memberPrice * 1 > 0) {
//会员优先
return memberPrice;
} else {
const price = returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
goods: goods,
});
return price;
}
} else {
return memberPrice;
}
} else {
//是会员没有限时折扣
return memberPrice;
}
} else {
//不是会员或者没有启用会员价
if (limitTimeDiscountRes && limitTimeDiscountRes.id && includesGoods) {
const price = returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
goods: goods,
});
return price;
} else {
return goods.salePrice;
}
}
}
interface returnLimitPriceArgs {
limitTimeDiscountRes: TimeLimitDiscountConfig | null | undefined;
price: number;
goods?: BaseCartItem;
}
/**
* 返回限时折扣价格
* @params {*} args 参数对象
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.price 商品价格
* @param {*} args.goods 商品对象
* @returns
*/
export function returnLimitPrice(args: returnLimitPriceArgs) {
const { limitTimeDiscountRes, price, goods } = args;
const discountRate = new BigNumber(
limitTimeDiscountRes ? limitTimeDiscountRes.discountRate : 100
).dividedBy(100);
const result = BigNumber(price)
.times(discountRate)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
return result;
}
/**
* 判断是否返回会员价
* @param {*} args 参数对象
* @param {*} args.shopInfo 店铺信息
* @param {*} args.shopUserInfo 店铺用户信息
* @returns
*/
export function canReturnMemberPrice(args: CanReturnMemberPriceArgs) {
const { shopInfo, shopUserInfo } = args;
if (shopUserInfo.isMemberPrice == 1 && shopUserInfo.isVip == 1) {
return true;
} else {
return false;
}
}
/**
* 返回会员价格
* @param {*} goods
* @returns
*/
export function returnMemberPrice(goods: BaseCartItem) {
return goods.memberPrice || goods.salePrice;
}
export const utils = {
returnPrice,
canUseLimitTimeDiscount,
returnLimitPrice,
canReturnMemberPrice,
returnMemberPrice,
};
export default utils;

0
lib/socket.ts Normal file
View File

430
lib/types.ts Normal file
View File

@@ -0,0 +1,430 @@
/** 商品类型枚举 */
export enum GoodsType {
NORMAL = "normal", // 普通商品
WEIGHT = "weight", // 称重商品
GIFT = "gift", // 赠菜(继承普通商品逻辑,标记用)
EMPTY = "", // 空字符串类型(后端未返回时默认归类为普通商品)
PACKAGE = "package", // 打包商品(如套餐/预打包商品,按普通商品逻辑处理,可扩展特殊规则)
}
/** 优惠券计算结果类型(新增细分字段) */
export interface CouponResult {
deductionAmount: number; // 抵扣金额
excludedProductIds: string[]; // 不适用商品ID列表注意是商品ID非购物车ID
usedCoupon: Coupon | undefined; // 实际使用的优惠券
productCouponDeduction: number; // 新增:商品优惠券抵扣(兑换券等)
fullCouponDeduction: number; // 新增:满减优惠券抵扣
}
/** 兑换券计算结果类型(新增细分字段) */
export interface ExchangeCalculationResult {
deductionAmount: number;
excludedProductIds: string[]; // 不适用商品ID列表商品ID
productCouponDeduction: number; // 新增:兑换券属于商品券,同步记录
}
export interface CouponTypes {
1: "满减券";
2: "商品券";
3: "折扣券";
4: "第二件半价券";
5: "消费送券";
6: "买一送一券";
7: "固定价格券";
8: "免配送费券";
}
/** 优惠券类型枚举 */
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; // 店铺IDint64
syncId?: number; // 同步Idint64
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唯一标识商品用于优惠券/活动匹配,必选)
productId?: string | number; // 商品ID
salePrice: number; // 商品原价(元)
number: number; // 商品数量
num?: number; // 商品数量
isTimeDiscount?: boolean; // 是否限时折扣商品默认false
is_time_discount?: boolean; // 是否限时折扣商品默认false
product_type: GoodsType; // 商品类型
is_temporary?: boolean; // 是否临时菜默认false
isTemporary?: boolean; // 是否临时菜默认false
is_gift?: boolean; // 是否赠菜默认false
isGift?: boolean; // 是否赠菜默认false
returnNum?: number; // 退货数量历史订单用默认0
memberPrice: number; // 商品会员价(元,优先级:商品会员价 > 会员折扣)
discountSaleAmount?: number; // 商家改价后单价(元,优先级最高)
discount_sale_amount?: 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原价
};
skuId?: string | number; // SKU ID唯一标识商品规格如颜色/尺寸)
couponType?: number; // 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
}
export interface CouponFoods {
id: string;
name: string;
images: string;
}
/** 基础优惠券接口(所有券类型继承,包含统一门槛商品字段) */
export interface BaseCoupon {
otherCouponShare?: number; // 与其它优惠共享0-否1-是
id: string | number; // 优惠券ID
type: number; // 工具库字符串枚举由后端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; // 可抵扣最大金额 元
use: boolean;
discountNum?: number; // 抵扣数量
useRule?: string; // 使用规则price_asc-价格低到高price_desc-高到低
discountRate?: number; // 折扣%如90=9折
noUseRestrictions?: boolean; // 是不可用原因
thresholdFoods: CouponFoods[]; // 门槛商品ID列表空数组=全部商品,非空=指定商品ID
useFoods: CouponFoods[]; // 可用商品ID列表空数组=全部商品,非空=指定商品ID
}
export interface couponDiscount {
discountPrice: number;
hasDiscountGoodsArr: BaseCartItem[];
}
/** 满减券(适配后端字段) */
export interface FullReductionCoupon extends BaseCoupon {
fullAmount: number; // 对应后端fullAmount满减门槛
discountAmount: number; // 对应后端discountAmount减免金额
maxDiscountAmount?: number; // 对应后端maxDiscountAmount最大减免
discount?: couponDiscount;
}
/** 折扣券(适配后端字段) */
export interface DiscountCoupon extends BaseCoupon {
discountRate: number; // 后端discountRate%转小数如90→0.9
maxDiscountAmount: number; // 对应后端maxDiscountAmount最大减免
discount?: couponDiscount;
}
/** 第二件半价券(适配后端字段) */
export interface SecondHalfPriceCoupon extends BaseCoupon {
maxUseCountPerOrder?: number; // 对应后端useLimit-10086=不限)
discount?: couponDiscount;
}
/** 买一送一券(适配后端字段) */
export interface BuyOneGetOneCoupon extends BaseCoupon {
maxUseCountPerOrder?: number; // 对应后端useLimit-10086=不限)
discount?: couponDiscount;
}
/** 商品兑换券(适配后端字段) */
export interface ExchangeCoupon extends BaseCoupon {
deductCount: number; // 对应后端discountNum抵扣数量
sortRule: "low_price_first" | "high_price_first"; // 后端useRule转换
discount?: couponDiscount;
}
/** 所有优惠券类型联合 */
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 TimeLimitDiscountConfig {
/**
* 折扣优先级 limit-time/vip-price
*/
discountPriority: string;
/**
* 折扣% 范围1-99
*/
discountRate: number;
/**
* 参与商品
*/
foods: string;
/**
* 参与商品 1全部 2部分
*/
foodType: number;
/**
* 自增主键
*/
id: number;
/**
* 店铺ID
*/
shopId: number;
/**
* 可使用类型:堂食 dine-in 外带 take-out 外卖 take-away 配送 post
*/
useType: string;
[property: string]: any;
}
//用户信息
export interface ShopUserInfo {
isVip: number | null; //是否会员
discount: number | null; //用户折扣
isMemberPrice: number | null; //会员折扣与会员价是否同时使用
id?: 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;
limitTimeDiscount?: TimeLimitDiscountConfig; //限时折扣
shopUserInfo: ShopUserInfo; // 用户信息
}
/** 订单费用汇总(修改:补充商家减免类型和明细) */
export interface OrderCostSummary {
goodsList: BaseCartItem[];
// 商品总件数
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: {
usedFullReductionActivityFullAmount: number; // 计算出的满减活动的门槛金额
usedActivity?: FullReductionActivity; // 实际使用的满减活动
usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优)
actualAmount: number; // 满减实际减免金额(元)
};
vipDiscountAmount: 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; // 可用时间段类型后端字段useTimeTypeall=全时段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校验
export const WEEKDAY_MAP = {
周一: 1,
周二: 2,
周三: 3,
周四: 4,
周五: 5,
周六: 6,
周日: 0, // JS中getDay()返回0=周日
};
export interface ShopInfo {
isMemberPrice: number; // 是否开启会员价 1是开启
[property: string]: any;
}
export interface couponCalcParams {
canDikouGoodsArr: BaseCartItem[];
coupon: Coupon;
user: ShopUserInfo;
shopInfo: ShopInfo;
selCoupon: Coupon[];
goodsOrderPrice: number; //商品订单总价
isMemberPrice: number; // 是否开启会员价 1是开启
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined;
}
export interface CanDikouGoodsArrArgs {
canDikouGoodsArr: BaseCartItem[];
selCoupon: Coupon[];
user: ShopUserInfo;
shopInfo: ShopInfo;
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined;
}
export interface returnPriceArgs {
goods: BaseCartItem;
selCoupon: Coupon[];
user: ShopUserInfo;
shopInfo: ShopInfo;
shopUserInfo: ShopUserInfo;
limitTimeDiscountRes?: TimeLimitDiscountConfig | null | undefined;
idKey?: keyof BaseCartItem;
}
export interface CanReturnMemberPriceArgs {
shopInfo?: ShopInfo;
shopUserInfo: ShopUserInfo;
}

33
lib/utils.ts Normal file
View File

@@ -0,0 +1,33 @@
/**
* 通用字段兼容工具函数:处理驼峰/下划线命名的字段取值
* @param obj 目标对象(如商品信息 BaseCartItem
* @param camelCaseKey 驼峰命名字段(如 'isTemporary'
* @param snakeCaseKey 下划线命名字段(如 'is_temporary'
* @param defaultValue 默认值(默认 false适配布尔类型字段
* @returns 字段值(优先取存在的字段,无则返回默认值)
*/
export function getCompatibleFieldValue(
obj: Record<string, any>,
camelCaseKey: string,
snakeCaseKey: string,
defaultValue: boolean = false
): boolean {
// 优先判断驼峰字段(如果存在且不是 undefined/null
if (
obj.hasOwnProperty(camelCaseKey) &&
obj[camelCaseKey] !== undefined &&
obj[camelCaseKey] !== null
) {
return Boolean(obj[camelCaseKey]);
}
// 再判断下划线字段
if (
obj.hasOwnProperty(snakeCaseKey) &&
obj[snakeCaseKey] !== undefined &&
obj[snakeCaseKey] !== null
) {
return Boolean(obj[snakeCaseKey]);
}
// 都不存在时返回默认值(布尔类型字段默认 false
return defaultValue;
}

View File

@@ -69,6 +69,8 @@
</view>
</view>
</view>
</view>
</template>
@@ -552,4 +554,9 @@ const showOldPrice = computed(() => {});
text-decoration: line-through;
text-align: right;
}
.total{
padding-top: 32rpx;
border-top: 1px solid #EDEDED;
text-align: right;
}
</style>

View File

@@ -8,22 +8,13 @@
<!-- 先付款 -->
<view class="tabBox" v-if="listinfo.status == 'unpaid' || !listinfo.id">
<view class="tab">
<view
v-for="(item, index) in tebtypeList"
:key="index"
:class="
<view v-for="(item, index) in tebtypeList" :key="index" :class="
is_type == index
? 'tab_item' + (is_type + 1) + ' tab_item active '
: 'tab_item'
"
@click="tabClick(item, index)"
>
" @click="tabClick(item, index)">
<view></view>
<image
class="icon"
:src="is_type == index ? item.url_active : item.url"
mode="aspectFill"
/>
<image class="icon" :src="is_type == index ? item.url_active : item.url" mode="aspectFill" />
<text class="title" :class="{ active: is_type == index }">{{
item.title
}}</text>
@@ -31,11 +22,9 @@
</view>
<view class="table" v-if="is_type == 0">
<view class="table_left">
<image
class="icon"
<image class="icon"
src="https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/table.png"
mode="aspectFill"
/>
mode="aspectFill" />
<text class="title">桌台</text>
</view>
<view class="value" v-if="listinfo.tableName">
@@ -51,69 +40,25 @@
:nowCarts="nowCarts"
:limitDiscount="cartStore.limitTimeDiscount"
></orderItemVue> -->
<orderItemVue
:useVipPrice="useVipPrice"
v-if="cartStore.allGoods.length"
:nowCarts="cartStore.allGoods"
:limitDiscount="cartStore.limitTimeDiscount"
></orderItemVue>
<template v-if="false">
<view
class="card_item"
v-for="(value, key) in listinfo.detailMap"
:key="key"
>
<view class="shop-info">
<view class="item" v-for="item in value" :key="item.id">
<view class="cover">
<up-image
width="76"
height="76"
radius="6"
:src="item.productImg"
v-if="item.productId != -999"
></up-image>
<up-image
width="76"
height="76"
radius="6"
:src="'https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/table.png'"
mode="heightFix"
v-else
></up-image>
</view>
<view class="info">
<text class="productName">{{ item.productName }}</text>
<text class="productSkuName" v-if="item.skuName">{{
item.skuName
}}</text>
</view>
<view class="price">
<view class="priceAmount">
<text v-if="isBwc">{{ item.price }}</text>
<text v-else>
{{
useVipPrice
? item.memberPrice || item.price
: item.price
}}
<orderItemVue :useVipPrice="useVipPrice" v-if="cartStore.allGoods.length" :nowCarts="cartStore.allGoods"
:limitDiscount="cartStore.limitTimeDiscount"></orderItemVue>
<view class="total-wrap u-m-t-30" v-if="listinfo.status == 'unpaid' || !listinfo.id">
<text>总计</text>
<text class="u-font-32"></text>
<text class="price">
{{ cartStore.orderCostSummary.goodsRealAmount }}
</text>
</view>
<view class="num">x{{ item.num }}</view>
</view>
</view>
</view>
</view>
</template>
<view
class="total-wrap u-m-t-30"
v-if="listinfo.status == 'unpaid' || !listinfo.id"
>
<view>总计</view>
<view class="price">
<view class="total-wrap u-m-t-30" v-else>
<text>总计</text>
<text class="u-font-32"></text>
<text class="price">
{{ cartStore.orderCostSummary.goodsRealAmount }}
</view>
</text>
</view>
<!-- <view class="total-wrap" v-else>
@@ -150,89 +95,54 @@
</view> -->
<!-- 支付之前 -->
<block
v-if="
<block v-if="
listinfo.status == 'unpaid' ||
listinfo.status == 'paying' ||
!listinfo.id
"
>
">
<view class="u-m-t-40 u-p-l-20 u-p-r-20">
<view
class="youhui-item"
v-if="cartStore.orderCostSummary.fullReduction.actualAmount"
>
<view class="youhui-item" v-if="cartStore.orderCostSummary.fullReduction.actualAmount">
<view class="u-flex align-center">
<image
src="/static/icon/coup.png"
bgColor="#fff"
style="width: 50rpx; height: 50rpx"
></image>
<image src="/static/icon/coup.png" bgColor="#fff" style="width: 50rpx; height: 50rpx">
</image>
<text class="color-333 font-14 u-m-l-16 no-wrap">满减活动</text>
</view>
<view class="red font-12 u-flex align-center"
>-¥{{
<view class="red font-12 u-flex align-center">-¥{{
cartStore.orderCostSummary.fullReduction.actualAmount
}}</view
>
}}</view>
</view>
<view
class="youhui-item"
v-if="cartStore.orderCostSummary.vipDiscountAmount"
>
<view class="youhui-item" v-if="cartStore.orderCostSummary.vipDiscountAmount">
<view class="u-flex align-center">
<image
src="/static/icon/newUserDiscount.png"
bgColor="#fff"
style="width: 50rpx; height: 50rpx"
></image>
<image src="/static/icon/newUserDiscount.png" bgColor="#fff"
style="width: 50rpx; height: 50rpx"></image>
<text class="color-333 font-14 u-m-l-16 no-wrap">会员折扣</text>
</view>
<view class="red font-12 u-flex align-center"
>-¥{{ cartStore.orderCostSummary.vipDiscountAmount }}</view
>
<view class="red font-12 u-flex align-center">
-¥{{ cartStore.orderCostSummary.vipDiscountAmount }}</view>
</view>
<view class="youhui-item" v-if="newUserDiscount">
<view class="u-flex align-center">
<image
src="/static/icon/newUserDiscount.png"
bgColor="#fff"
style="width: 50rpx; height: 50rpx"
></image>
<image src="/static/icon/newUserDiscount.png" bgColor="#fff"
style="width: 50rpx; height: 50rpx"></image>
<text class="color-333 font-14 u-m-l-16 no-wrap">新客立减</text>
</view>
<view class="red font-12 u-flex align-center"
>-¥{{ newUserDiscount }}</view
>
<view class="red font-12 u-flex align-center">-¥{{ newUserDiscount }}</view>
</view>
<view
class="favorable"
v-for="(item, indexe) in favorablelist"
:key="indexe"
:class="{ column: item.type == 'product' }"
@click="goUrl(item)"
>
<view class="favorable" v-for="(item, indexe) in favorablelist" :key="indexe"
:class="{ column: item.type == 'product' }" @click="goUrl(item)">
<view class="favorable_left">
<view class="u-flex" style="width: 44rpx">
<u-image
bgColor="#fff"
:width="item.width || '50rpx'"
:height="item.height || '50rpx'"
v-if="item.icon"
:src="item.icon"
mode=""
></u-image>
<u-image bgColor="#fff" :width="item.width || '50rpx'"
:height="item.height || '50rpx'" v-if="item.icon" :src="item.icon"
mode=""></u-image>
</view>
<text class="name u-m-l-16 no-wrap"> {{ item.name }} </text>
<template v-if="item.type == 'points' && !pointObj.disabled">
<text
class="u-p-l-6"
style="color: #666; max-width: 400rpx; font-size: 10px"
>
「可用积分{{ pointsRes.accountPoints }},最大可抵扣{{
<text class="u-p-l-6" style="color: #666; max-width: 400rpx; font-size: 10px">
「可用积分{{ cartStore.shopUserInfo.pointsUser?cartStore.shopUserInfo.pointsUser.pointBalance:0}},最大可抵扣{{
maxPointDiscount
}}元」
</text>
@@ -241,125 +151,87 @@
<!-- 优惠卷 -->
<view class="favorable_right" v-if="item.type == 'coupon'">
<template
v-if="cartStore.orderCostSummary.couponDeductionAmount > 0"
>
<text class="favorable_right_text red"
>-¥{{
<template v-if="cartStore.orderCostSummary.couponDeductionAmount > 0">
<text class="favorable_right_text red">-¥{{
cartStore.orderCostSummary.couponDeductionAmount
}}</text
>
}}</text>
</template>
<template v-else>
<text
class="favorable_right_text red"
v-if="couponObj.disabled"
>{{ couponObj.disabledReason }}</text
>
<text
class="favorable_right_text red"
v-else-if="hasCouponCanUse"
>有可用优惠券</text
>
<text class="favorable_right_text" v-else
>暂无可用优惠券</text
>
<text class="favorable_right_text red"
v-if="couponObj.disabled">{{ couponObj.disabledReason }}</text>
<text class="favorable_right_text red" v-else-if="hasCouponCanUse">有可用优惠券</text>
<text class="favorable_right_text" v-else>暂无可用优惠券</text>
</template>
<template v-if="!couponObj.disabled">
<up-icon
name="arrow-right"
color="#575B66"
size="16"
v-if="
<up-icon name="arrow-right" color="#575B66" size="16" v-if="
listinfo.status == 'unpaid' ||
listinfo.status == 'paying' ||
!listinfo.id
"
></up-icon>
"></up-icon>
</template>
</view>
<!-- 新客立减 -->
<view class="favorable_right" v-if="item.type == 'newUser'">
<text
class="favorable_right_text red"
v-if="newUserDiscount > 0"
>-¥{{ newUserDiscount }}</text
>
<text class="favorable_right_text red"
v-if="newUserDiscount > 0">-¥{{ newUserDiscount }}</text>
</view>
<!-- 积分 -->
<view
class="favorable_right u-flex u-col-center"
v-if="item.type == 'points'"
>
<text
v-if="pointObj.disabled"
class="u-p-l-6 red"
style="max-width: 440rpx; font-size: 24rpx"
>{{ pointObj.disabledReason }}</text
>
<template
v-else-if="
<view class="favorable_right u-flex u-col-center" v-if="item.type == 'points'">
<text v-if="pointObj.disabled" class="u-p-l-6 red"
style="max-width: 440rpx; font-size: 24rpx">{{ pointObj.disabledReason }}</text>
<template v-else-if="
usePoints && cartStore.orderCostSummary.pointDeductionAmount
"
>
<up-icon
name="checkmark-circle-fill"
size="24rpx"
color="#FE7E00"
></up-icon>
">
<up-icon name="checkmark-circle-fill" size="24rpx" color="#FE7E00"></up-icon>
<text class="red">
-¥{{ cartStore.orderCostSummary.pointDeductionAmount }}
</text>
</template>
<template v-else>
<view
v-if="maxPointDiscount > 0 && !pointObj.disabled"
class="u-flex u-col-center"
>
<view v-if="maxPointDiscount > 0 && !pointObj.disabled" class="u-flex u-col-center">
<view class="round"></view>
<text class="color-333 font-12 no-wrap u-m-l-4"
>使用抵扣</text
>
<text class="color-333 font-12 no-wrap u-m-l-4">使用抵扣</text>
</view>
</template>
</view>
</view>
</view>
</block>
<block v-else>
<view
class="cell-item column"
v-if="listinfo.discountInfo && listinfo.discountInfo != '{}'"
>
<view class="label">优惠折扣</view>
<view class="val column">
<view
class="productCoupon"
v-for="(item, index) in listinfo.discountInfo"
:key="index"
>
<view class="name">{{ item.name }}</view>
<!-- <view class="num">{{item.amount}}</view> -->
<view class="amount" style="margin-left: 10rpx"
>{{ item.amount }}元</view
>
</view>
</view>
<view class="disocunt " v-if="showDiscount">
<view class="row" v-if="listinfo.productCouponDiscountAmount">
<text class="t">商品券</text>
<text class=" price">-{{listinfo.productCouponDiscountAmount}}</text>
</view>
</block>
<view class="row" v-if="listinfo.otherCouponDiscountAmount">
<text class="t">优惠券</text>
<text class="price">-{{listinfo.otherCouponDiscountAmount}}</text>
</view>
<view class="row" v-if="listinfo.pointsDiscountAmount">
<text class="t">积分抵扣</text>
<text class="price">-{{listinfo.pointsDiscountAmount}}</text>
</view>
<view class="orderInfo">
<view class="row" v-if="listinfo.discountActAmount">
<text class="t">满减活动</text>
<text class="price">-{{listinfo.discountActAmount}}</text>
</view>
<view class="row" v-if="listinfo.newCustomerDiscountAmount">
<text class="t">新客立减</text>
<text class="price">-{{listinfo.newCustomerDiscountAmount}}</text>
</view>
</view>
<view class="orderInfo" v-if="listinfo.id&&listinfo.status=='done'">
<view class="row" @click="copyHandle(listinfo.orderNo)">
<text class="t">订单编号:</text>
<text
class="info"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
>{{ listinfo.orderNo }}(点击复制)</text
>
<text class="info"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">{{ listinfo.orderNo }}(点击复制)</text>
</view>
<view class="row">
<text class="t">下单时间:</text>
@@ -381,7 +253,7 @@
<text class="info" v-if="listinfo.dineMod == 'take-out'">外带</text>
<text class="info" v-if="listinfo.dineMod == 'take-away'">外卖</text>
</view>
<view class="row" v-if="listinfo.id && listinfo.satus != 'unpaid'">
<view class="row">
<text class="t">备注:</text>
<text class="info u-line-5">{{ listinfo.remark || "" }}</text>
</view>
@@ -400,10 +272,14 @@
</template>
<script setup>
import { useCartsStore } from "@/stores/carts.js";
import {
useCartsStore
} from "@/stores/carts.js";
import * as discountActivityApi from "@/common/api/market/discountActivity";
const cartStore = useCartsStore();
import { consumeDiscount } from "@/common/api/market/index.js";
import {
consumeDiscount
} from "@/common/api/market/index.js";
import orderItemVue from "./order-item.vue";
import {
ref,
@@ -419,7 +295,13 @@ import {
provide,
nextTick,
} from "vue";
import { returnHasCouponCanUse } from "@/utils/coupon.js";
import {
returnHasCouponCanUse
} from "@/utils/coupon.js";
import {
BigNumber
} from "bignumber.js";
let hasCouponCanUse = ref(false);
const usePoints = ref(false);
@@ -442,15 +324,13 @@ import pointsIcon from "@/static/icon/jifen.png";
const emits = defineEmits(["customevent", "istype", "learcoupons"]);
// teb 切换送餐和打包
const tebtypeList = reactive([
{
const tebtypeList = reactive([{
title: "送餐到桌",
show: false,
type: "table",
val: "dine-in",
url: "https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/tab1.png",
url_active:
"https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/tab1_active.png",
url_active: "https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/tab1_active.png",
},
{
title: "打包外带",
@@ -458,8 +338,7 @@ const tebtypeList = reactive([
type: "takeself",
val: "take-out",
url: "https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/tab2.png",
url_active:
"https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/tab2_active.png",
url_active: "https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/tab2_active.png",
},
]);
@@ -537,6 +416,47 @@ const props = defineProps({
},
});
/**
* 判断一个对象里的某些属性是否存在且值不等于0满足其中一个就返回true否则false
* @param {Object} obj - 要检测的目标对象若为非对象类型直接返回false
* @param {Array<string>} keys - 要检测的属性名数组(若为非数组/空数组直接返回false
* @returns {boolean} 满足条件返回true否则返回false
*/
function isObjHasPropertyAndNotNull(obj, keys) {
console.log(obj)
// 1. 边界校验obj必须是有效对象keys必须是非空数组
if (
!obj || // 排除null/undefined
typeof obj !== 'object' || // 排除字符串/数字/布尔等非对象类型
!Array.isArray(keys) || // 确保keys是数组
keys.length === 0 // 空数组直接返回false
) {
return false;
}
// 2. 遍历所有要检测的属性
for (const key of keys) {
console.log(obj[key])
// 检查属性是否是对象自身的(排除原型链上的属性) + 属性值不等于0
if (obj.hasOwnProperty(key) && obj[key] !== 0) {
return true; // 只要有一个满足立即返回true
}
}
// 3. 所有属性都不满足条件
return false;
}
const showDiscount = computed(() => {
const keys = ['productCouponDiscountAmount', 'otherCouponDiscountAmount',
'pointsDiscountAmount', 'discountActAmount', 'newCustomerDiscountAmount'
]
if (isObjHasPropertyAndNotNull(props.listinfo, keys)) {
return true
}
return false
})
const is_type = ref(0);
//满减活动
@@ -575,6 +495,14 @@ const couponObj = computed(() => {
return obj;
});
//积分可抵扣最大金额
const maxMoney = computed(() => {
return (
cartStore.orderCostSummary.orderOriginFinalPayAmount +
cartStore.orderCostSummary.pointDeductionAmount
);
});
const pointObj = computed(() => {
const obj = {
disabled: false,
@@ -596,10 +524,11 @@ const pointObj = computed(() => {
obj.disabledReason = "当前满减活动不可与积分同享";
}
}
//积分抵扣不足
if (pointsRes && !pointsRes.usable) {
obj.disabledReason = pointsRes.unusableReason || "积分抵扣不足或不可用";
if (maxMoney.value * 1 < cartStore.pointDeductionRule.minPaymentAmount || !cartStore.pointDeductionRule
.enableRewards) {
obj.disabled = true;
obj.disabledReason = "积分抵扣不足或不可用";
}
if (obj.disabled) {
cartStore.setUserPoints(0);
@@ -668,47 +597,45 @@ const calcUsablePointsData = reactive({
integral: "",
});
const pointsRes = reactive({
accountPoints: 0,
});
const maxPointDiscount = ref(0);
//积分可抵扣最大金额
const maxMoney = computed(() => {
return (
cartStore.orderCostSummary.orderOriginFinalPayAmount +
cartStore.orderCostSummary.pointDeductionAmount
);
});
async function getMaxPointsDiscount() {
if (!props.orderVIP.id) {
return;
}
let res = await APImemberPointscalcUsablePoints({
shopUserId: props.orderVIP.id,
orderAmount: maxMoney.value,
});
if (res) {
cartStore.setPointDeductionRule(
res.equivalentPoints || 0,
res.maxDeductionAmount || 0
);
Object.assign(pointsRes, res);
maxPointDiscount.value = res.maxDeductionAmount || 0;
console.log("积分可抵扣最大金额", maxPointDiscount.value);
if (usePoints.value && res.usable) {
console.log("积分抵扣金额", res.maxDeductionAmount);
cartStore.setUserPoints(res.maxUsablePoints || 0);
let userPoints = cartStore.shopUserInfo.pointsUser ? cartStore.shopUserInfo.pointsUser.pointBalance : 0;
//1积分等于多少钱
const onePointsMoney = cartStore.pointDeductionRule.pointsPerYuan ? ((1 || 0) / cartStore.pointDeductionRule
.pointsPerYuan) : 0
const calcMaxDiscount = Math.floor(maxMoney.value * (cartStore.pointDeductionRule.maxDeductionRatio / 100))
const userMaxDiscount = BigNumber(userPoints).times(onePointsMoney).decimalPlaces(2, BigNumber.ROUND_DOWN)
.toNumber()
maxPointDiscount.value = Math.min(calcMaxDiscount, userMaxDiscount)
console.log('积分最大可抵扣金额', maxPointDiscount.value)
cartStore.pointDeductionRule.maxDeductionAmount = maxPointDiscount.value || 0
if (usePoints.value && cartStore.pointDeductionRule.enableRewards) {
let num = (maxPointDiscount.value || 0) * cartStore.pointDeductionRule.pointsPerYuan
if (num > userPoints) {
num = userPoints
}
if (!res.usable) {
console.log('积分使用数量', num)
cartStore.setUserPoints(num);
}
if (!cartStore.pointDeductionRule.enableRewards) {
cartStore.setUserPoints(0);
}
}
}
watch(
() => maxMoney.value,
(newval) => {
getMaxPointsDiscount();
}
);
@@ -723,26 +650,11 @@ watch(
}
);
// * 获取积分相关信息
const getCalcUsablePoints = async (data) => {
Object.assign(calcUsablePointsData, data);
try {
cartStore.setPointDeductionRule(
data.equivalentPoints,
data.maxDeductionAmount
);
calcUsablePointsData.minIntegral = data.minDeductionPoints;
calcUsablePointsData.maxIntegral = data.maxUsablePoints;
calcUsablePointsData.instructionText = `请输入 ${data.minDeductionPoints} - ${data.maxUsablePoints} 之间的积分`;
} catch (error) {
//TODO handle the exception
}
};
// 积分取消清空
const IntegralInputclose = async () => {
calcUsablePointsData.integral = null;
props.listinfo.pointsDiscountAmount = 0;
calcUsablePointsData.showModal = false;
// cartStore.setUserPoints(0);
};
@@ -762,7 +674,6 @@ const copyHandle = (e) => {
// 将方法暴露给父组件
defineExpose({
getCalcUsablePoints,
bwcclear,
IntegralInputclose,
});
@@ -863,10 +774,7 @@ function calcDiscountActivity() {
const usedFullReductionActivityFullAmount =
cartStore.orderCostSummary.fullReduction
.usedFullReductionActivityFullAmount;
console.log(
"usedFullReductionActivityFullAmount",
usedFullReductionActivityFullAmount
);
if (res && res.thresholds && res.thresholds.length > 0) {
const canUseThresholds = res.thresholds
@@ -876,7 +784,6 @@ function calcDiscountActivity() {
.sort((a, b) => {
return b.discountAmount - a.discountAmount;
});
console.log("canUseThresholds", canUseThresholds);
if (canUseThresholds.length) {
discountActivity.value = canUseThresholds[0];
@@ -1478,11 +1385,11 @@ onMounted(async () => {
margin: 0 30rpx;
justify-content: flex-end;
align-items: flex-end;
padding: 0 0 30rpx 34rpx;
padding: 32rpx 0 0 34rpx;
font-weight: bold;
font-size: 28rpx;
color: #333333;
border-bottom: 2rpx dashed #e5e5e5;
border-top: 2rpx dashed #e5e5e5;
.price {
font-weight: bold;
@@ -1579,6 +1486,7 @@ onMounted(async () => {
.red {
color: #ff3232;
}
.youhui-item {
height: 60rpx;
width: 100%;
@@ -1586,9 +1494,11 @@ onMounted(async () => {
justify-content: space-between;
align-content: center;
}
.align-center {
align-items: center;
}
.limitDiscount {
background-color: #cc5617;
padding: 2rpx 10rpx;
@@ -1604,4 +1514,36 @@ onMounted(async () => {
z-index: 9;
color: #fff;
}
.disocunt {
background: #FFFFFF;
border-radius: 18rpx 18rpx 18rpx 18rpx;
overflow: hidden;
margin-top: 32rpx;
.price {
color: #FF1C1C;
font-size: 28rpx;
line-height: 42rpx;
}
.row {
padding: 16rpx 24rpx;
display: flex;
justify-content: space-between;
.t {
font-weight: bold;
font-size: 28rpx;
color: #333333;
flex-shrink: 0;
}
.info {
font-weight: 400;
font-size: 28rpx;
color: #666666;
}
}
}
</style>

View File

@@ -1,9 +1,6 @@
<template>
<view>
<view class="card_box">
<view class="card_head_box">
<view class="card_head_item" v-for="(item,index) in 8" :key="index"></view>
</view>
<!-- 先付款 -->
<view class="tabBox" v-if="listinfo.status == 'unpaid'">
@@ -44,6 +41,21 @@
</view>
</view> -->
</view>
<view class="tabBox" v-else-if="listinfo.tableName">
<view class="table" >
<view class="table_left">
<image class="icon"
src="https://czg-qr-order.oss-cn-beijing.aliyuncs.com/confirmOrder/table.png"
mode="aspectFill" />
<text class="title">桌台</text>
</view>
<view class="value" v-if="listinfo.tableName"> {{ listinfo.tableName || '' }} </view>
</view>
</view>
<view class="card">
<!-- 订单头部 -->
<view class="card_item" v-for="(value, key) in listinfo.detailMap" :key="key">
@@ -162,31 +174,57 @@
</view>
</view>
</block>
<block v-else>
<!-- <block v-else>
<view class="cell-item column" v-if="listinfo.discountInfo">
<view class="label">优惠折扣</view>
<view class="val column">
<view class="productCoupon" v-for="(item,index) in listinfo.discountInfo" :key="index">
<view class="name">{{item.name}}</view>
<!-- <view class="num">{{item.amount}}</view> -->
<view class="amount" style="margin-left: 10rpx;">{{item.amount}}</view>
</view>
</view>
</view>
</block>
</block> -->
<view class="total-wrap" v-if="listinfo.status == 'unpaid'">
<view>总计</view>
<view class="price"> {{listinfo.totalCost}} </view>
<text>总计</text>
<text class="u-font-32"></text>
<text class="price"> {{listinfo.totalCost}} </text>
</view>
<view class="total-wrap" v-else>
<view>实付</view>
<view class="price"> {{listinfo.payAmount}} </view>
<view>实付</view>
<text class="u-font-32"></text>
<text class="price"> {{listinfo.payAmount}} </text>
</view>
</view>
</view>
<view class="disocunt " v-if="showDiscount">
<view class="row" v-if="listinfo.productCouponDiscountAmount">
<text class="t">商品券</text>
<text class="info price">-{{listinfo.productCouponDiscountAmount}}</text>
</view>
<view class="row" v-if="listinfo.otherCouponDiscountAmount">
<text class="t">优惠券</text>
<text class="price">-{{listinfo.otherCouponDiscountAmount}}</text>
</view>
<view class="row" v-if="listinfo.pointsDiscountAmount">
<text class="t">积分抵扣</text>
<text class="price">-{{listinfo.pointsDiscountAmount}}</text>
</view>
<view class="row" v-if="listinfo.discountActAmount">
<text class="t">满减活动</text>
<text class="price">-{{listinfo.discountActAmount}}</text>
</view>
<view class="row" v-if="listinfo.newCustomerDiscountAmount">
<text class="t">新客立减</text>
<text class="price">-{{listinfo.newCustomerDiscountAmount}}</text>
</view>
</view>
<view class="orderInfo">
<view class="row" @click="copyHandle(listinfo.orderNo)">
<text class="t">订单编号</text>
@@ -295,6 +333,7 @@
])
const props = defineProps({
freeCheck: {
type: Boolean
@@ -330,6 +369,47 @@
})
/**
* 判断一个对象里的某些属性是否存在且值不等于0满足其中一个就返回true否则false
* @param {Object} obj - 要检测的目标对象若为非对象类型直接返回false
* @param {Array<string>} keys - 要检测的属性名数组(若为非数组/空数组直接返回false
* @returns {boolean} 满足条件返回true否则返回false
*/
function isObjHasPropertyAndNotNull(obj, keys) {
console.log(obj)
// 1. 边界校验obj必须是有效对象keys必须是非空数组
if (
!obj || // 排除null/undefined
typeof obj !== 'object' || // 排除字符串/数字/布尔等非对象类型
!Array.isArray(keys) || // 确保keys是数组
keys.length === 0 // 空数组直接返回false
) {
return false;
}
// 2. 遍历所有要检测的属性
for (const key of keys) {
console.log(obj[key])
// 检查属性是否是对象自身的(排除原型链上的属性) + 属性值不等于0
if (obj.hasOwnProperty(key) && obj[key] !== 0) {
return true; // 只要有一个满足立即返回true
}
}
// 3. 所有属性都不满足条件
return false;
}
const showDiscount = computed(() => {
const keys = ['productCouponDiscountAmount', 'otherCouponDiscountAmount',
'pointsDiscountAmount', 'discountActAmount', 'newCustomerDiscountAmount'
]
if (isObjHasPropertyAndNotNull(props.listinfo, keys)) {
return true
}
return false
})
const is_type = ref(0)
// 监听送餐/打包切换
const tabClick = (item, index) => {
@@ -477,11 +557,9 @@
<style lang="scss" scoped>
.card_box {
background-color: #fff;
// box-shadow: 0rpx 8rpx 12rpx 2rpx rgba(87,86,86,0.35);
position: relative;
width: 100%;
height: 100%;
// box-shadow: 0rpx 4rpx 12rpx 2rpx rgba(87,86,86,0.35);
border-radius: 18rpx;
padding-bottom: 32rpx;
@@ -1050,7 +1128,31 @@
}
}
.disocunt{
background: #FFFFFF;
border-radius: 18rpx 18rpx 18rpx 18rpx;overflow: hidden;
margin-top: 32rpx;
.row{
padding: 16rpx 24rpx;
display: flex;
justify-content: space-between;
.t {
font-weight: bold;
font-size: 28rpx;
color: #333333;
flex-shrink: 0;
}
.info {
font-weight: 400;
font-size: 28rpx;
color: #666666;
}
}
.price {
color: #FF1C1C;
}
}
.orderInfo {
background: #FFFFFF;
border-radius: 18rpx 18rpx 18rpx 18rpx;
@@ -1129,4 +1231,6 @@
}
}
}
</style>

View File

@@ -5,9 +5,7 @@
<view class="left">
<view class="icon">优惠</view>
<view class="text">
充值消费{{ freeDineConfig.rechargeTimes }}订单满{{
freeDineConfig.rechargeThreshold
}}元可用本单立享免单</view
充值消费{{ freeDineConfig.rechargeTimes }}本单立享免单</view
>
</view>
<view @click.stop="()=>{}">
@@ -92,12 +90,11 @@ const changeFree = (e) => {
font-weight: 500;
font-size: 20rpx;
color: #ffffff;
margin-right: 12rpx;
margin-right: 18rpx;
}
.text {
width: 80%;
font-weight: 500;
font-weight: 700;
font-size: 28rpx;
color: #333333;
}

View File

@@ -3,41 +3,22 @@
<up-navbar :title="navTitle" @leftClick="onback" placeholder></up-navbar>
<view class="headStatus">
<view class="status">
<up-icon
v-if="
<up-icon v-if="
listinfo.status == 'done' ||
listinfo.status == 'unpaid' ||
!listinfo.id
"
name="checkmark-circle-fill"
color="#03C061"
size="23"
></up-icon>
<view
class="statusName"
v-if="listinfo.status == 'unpaid' || !listinfo.id"
>待支付</view
>
" name="checkmark-circle-fill" color="#03C061" size="23"></up-icon>
<view class="statusName" v-if="listinfo.status == 'unpaid' || !listinfo.id">待支付</view>
<view class="statusName" v-if="listinfo.status == 'done'">已完成</view>
<view class="statusName" v-if="listinfo.status == 'refund'">退单</view>
<view class="statusName" v-if="listinfo.status == 'part-refund'"
>部分退单</view
>
<view class="statusName" v-if="listinfo.status == 'cancelled'"
>取消订单</view
>
<view class="statusName" v-if="listinfo.status == 'part-refund'">部分退单</view>
<view class="statusName" v-if="listinfo.status == 'cancelled'">取消订单</view>
</view>
<view class="time" v-if="listinfo.createTime"
>下单时间:{{ listinfo.createTime }}</view
>
<view
class="time"
v-if="
<view class="time" v-if="listinfo.createTime">下单时间:{{ listinfo.createTime }}</view>
<view class="time" v-if="
listinfo.paidTime &&
(listinfo.status != 'cancelled' || listinfo.status != 'unpaid')
"
>付款时间:{{ listinfo.paidTime }}</view
>
">付款时间:{{ listinfo.paidTime }}</view>
</view>
<view class="order-msg-list" v-if="listinfo.status=='done'">
@@ -59,88 +40,50 @@
<u-image width="152" height="152" radius="16" :src="shopQrcode"></u-image>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<image
style="height: 150rpx; width: 150rpx"
:src="shopQrcode"
@longpress="saveImage(shopQrcode)"
mode="aspectFill"
></image>
<image style="height: 150rpx; width: 150rpx" :src="shopQrcode" @longpress="saveImage(shopQrcode)"
mode="aspectFill"></image>
<!-- #endif -->
</view>
<!-- {{cartStore.orderCostSummary}} -->
<!-- 详情 -->
<orderInfo
:useVipPrice="cartStore.useVipPrice"
:packfee="packfee"
:shifu="shifu"
:nowCarts="cartStore.carts"
ref="orderInfoAfterRef"
:freeCheck="freeCheck"
:listinfo="listinfo"
:orderVIP="orderVIP"
:ordershopUserInfo="ordershopUserInfo"
@istype="istype"
@clickPointsamount="clickPointsamount"
:isBwc="isBwc"
></orderInfo>
<orderInfo :useVipPrice="cartStore.useVipPrice" :packfee="packfee" :shifu="shifu" :nowCarts="cartStore.carts"
ref="orderInfoAfterRef" :freeCheck="freeCheck" :listinfo="listinfo" :orderVIP="orderVIP"
:ordershopUserInfo="ordershopUserInfo" @istype="istype" :isBwc="isBwc"></orderInfo>
<!-- 订单备注 -->
<view class="orderInfo">
<view class="orderInfo" v-if="listinfo.status!='done'">
<view class="">
<view class="t">订单备注:</view>
<view class="u-m-t-18">
<up-textarea
border="none"
placeholder="请填写口味、偏好等要求"
type="textarea"
v-model="orderRemarker"
:clearable="true"
/>
<view class="u-m-t-18 textarea">
<up-textarea border="none" placeholder="请填写口味、偏好等要求" type="textarea" v-model="orderRemarker"
:clearable="true" />
</view>
</view>
</view>
<!-- 霸王餐 -->
<rechargeFree
:freeDineConfig="orderVIP.freeDineConfig"
:payAmount="listinfo.totalPrices"
v-if="showFreeDine && (listinfo.status == 'unpaid' || !listinfo.id)"
@changeFree="changeFree"
></rechargeFree>
<rechargeFree :freeDineConfig="orderVIP.freeDineConfig" :payAmount="listinfo.totalPrices"
v-if="showFreeDine && (listinfo.status == 'unpaid' || !listinfo.id)" @changeFree="changeFree">
</rechargeFree>
<!-- 支付方式 -->
<paymentMethodes
ref="paymentMethodref"
:orderVIP="orderVIP"
@groupChange="groupChange"
:disablePayType="disablePayType"
:changeFreeenable="isBwc"
v-model="paymentmethod"
v-if="listinfo.status == 'unpaid' || !listinfo.id"
>
<paymentMethodes ref="paymentMethodref" :orderVIP="orderVIP" @groupChange="groupChange"
:disablePayType="disablePayType" :changeFreeenable="isBwc" v-model="paymentmethod"
v-if="listinfo.status == 'unpaid' || !listinfo.id">
<template #bottom>
<!-- 充值享优惠 -->
<view
v-if="
<view v-if="
!showFreeDine &&
(listinfo.status == 'unpaid' || !listinfo.id) &&
cartStore.orderCostSummary.finalPayAmount > 0
"
>
<ChargeVue
@updateChargeSel="(e) => updateChargeSel(e)"
@updateRechargeId="updateRechargeId"
@updateIsShow="updateIsShow"
v-if="listinfo.status == 'unpaid' || !listinfo.id"
></ChargeVue>
">
<ChargeVue @updateChargeSel="(e) => updateChargeSel(e)" @updateRechargeId="updateRechargeId"
@updateIsShow="updateIsShow" v-if="listinfo.status == 'unpaid' || !listinfo.id"></ChargeVue>
</view>
</template>
</paymentMethodes>
<view class="fixedview">
<view
class="flex-between"
v-if="listinfo.status == 'unpaid' || !listinfo.id"
>
<view class="flex-between" v-if="listinfo.status == 'unpaid' || !listinfo.id">
<view class="fixedview_one flex-start">
<view class="fixedview_oneone">实付金额</view>
<view class="fixedview_onetow" v-if="rechargeItem.id">
@@ -155,60 +98,33 @@
<view class="flex-colum-end u-flex u-flex-y-center">
<view class="u-m-r-30 u-flex u-flex-y-center">
<template v-if="ordershopUserInfo.registerType == 'before'">
<up-button
shape="circle"
v-if="!listinfo.id"
@click="toJiacai"
plain
:custom-style="customStyle"
>
<up-button shape="circle" v-if="!listinfo.id" @click="toJiacai" plain
:custom-style="customStyle">
<view class="u-flex u-flex-y-center">
<image
style="width: 40rpx; height: 40rpx"
src="@/static/icon/add-goods.png"
mode=""
></image>
<image style="width: 40rpx; height: 40rpx" src="@/static/icon/add-goods.png"
mode=""></image>
<text class="u-font-32 color-main u-m-l-16">加菜</text>
</view>
</up-button>
<up-button
shape="circle"
v-if="listinfo.id && listinfo.status == 'unpaid'"
plain
@tap="cancelOrder()"
:custom-style="customStyle"
>
<up-button shape="circle" v-if="listinfo.id && listinfo.status == 'unpaid'" plain
@tap="cancelOrder()" :custom-style="customStyle">
<view class="u-flex u-flex-y-center">
<text class="u-font-32 color-main u-m-l-16">取消订单</text>
</view>
</up-button>
</template>
<template v-else>
<up-button
v-if="!cartStore.isEmpty"
shape="circle"
@click="onlyCreateOrder"
plain
:custom-style="customStyle"
>
<up-button v-if="!cartStore.isEmpty" shape="circle" @click="onlyCreateOrder" plain
:custom-style="customStyle">
<view class="u-flex u-flex-y-center">
<!-- <image style="width: 40rpx;height: 40rpx;" src="@/static/icon/add-goods.png" mode=""></image> -->
<text class="u-font-32 color-main">仅下单</text>
</view>
</up-button>
<up-button
v-else
shape="circle"
@click="toJiacai"
plain
:custom-style="customStyle"
>
<up-button v-else shape="circle" @click="toJiacai" plain :custom-style="customStyle">
<view class="u-flex u-flex-y-center">
<image
style="width: 40rpx; height: 40rpx"
src="@/static/icon/add-goods.png"
mode=""
></image>
<image style="width: 40rpx; height: 40rpx" src="@/static/icon/add-goods.png"
mode=""></image>
<text class="u-font-32 color-main u-m-l-16">加菜</text>
</view>
</up-button>
@@ -224,23 +140,28 @@
</view>
</view>
<view style="width: 100%; height: 200rpx; color: #fff; color: #fff"></view>
<payPassword
ref="payPasswordref"
:isShow="ispws"
@inputComplete="(e) => accountPayevent(e)"
@close="pwdClose"
/>
<payPassword ref="payPasswordref" :isShow="ispws" @inputComplete="(e) => accountPayevent(e)"
@close="pwdClose" />
<!-- 私域引流配置 -->
<OrderFinshModal v-model="showDrainage"></OrderFinshModal>
</view>
</template>
<script setup>
import { back } from "@/utils/uniapp.js";
import { onLoad, onUnload } from "@dcloudio/uni-app";
import {
back
} from "@/utils/uniapp.js";
import {
onLoad,
onUnload
} from "@dcloudio/uni-app";
import ChargeVue from "./components/charge.vue";
import { pay } from "@/utils/pay.js";
import {
pay
} from "@/utils/pay.js";
import OrderFinshModal from "@/components/order-finish-modal.vue";
import _ from "lodash";
import * as pointsApi from '@/common/api/market/points.js'
import yskUtils from '@/lib/index'
import {
ref,
reactive,
@@ -255,7 +176,9 @@ import {
} from "vue";
provide("shopUserInfo", ref(uni.cache.get("shopUserInfo")));
provide("shopInfo", ref(uni.cache.get("shopInfo")));
import { getTableInfo } from "@/common/api/shop/index.js";
import {
getTableInfo
} from "@/common/api/shop/index.js";
import {
APIgetOrderById,
APIputuserorder,
@@ -263,13 +186,20 @@ import {
rechargePayOrder,
APIcancelOrder,
} from "@/common/api/order/index.js";
import { APIusershopInfodetail, APIshopUserInfo } from "@/common/api/member.js";
import {
APIusershopInfodetail,
APIshopUserInfo
} from "@/common/api/member.js";
import {
APImemberPointsmyPoints,
APImemberPointscalcUsablePoints,
} from "@/common/api/shop/index.js";
import { useCartsStore } from "@/stores/carts.js";
import { useWebSocket } from "@/stores/carts-websocket.js";
import {
useCartsStore
} from "@/stores/carts.js";
import {
useWebSocket
} from "@/stores/carts-websocket.js";
import * as chatApi from "@/http/php/chat";
import MsgListItem from '@/components/msg-list-item/msg-list-item.vue'
@@ -287,6 +217,7 @@ onBackPress(() => {
console.log("返回拦截");
closeSocket();
});
function onback() {
closeSocket();
console.log("返回");
@@ -305,6 +236,7 @@ const cartStore = useCartsStore();
//充值相关start
//充值和余额支付是否可用
const isCanUseCharge = ref(true);
function updateIsShow(e) {
isCanUseCharge.value = e;
}
@@ -330,16 +262,13 @@ async function onMessage(Message) {
youhuiReset();
}
if (Message.operate_type == "cleanup") {
console.log("onMessage:cleanup", Message);
console.log("onMessage:options", options);
console.log("onMessage:listinfo", listinfo);
youhuiReset();
if (listinfo.id || options.tableCode) {
let res = listinfo.id
? await APIgetOrderById({
let res = listinfo.id ?
await APIgetOrderById({
orderId: listinfo.id,
})
: await APIhistoryOrder({
}) :
await APIhistoryOrder({
tableCode: options.tableCode || "",
});
@@ -377,8 +306,15 @@ async function onMessage(Message) {
) {
youhuiReset();
}
if (Message.operate_type == 'bulk_edit') {
}
return Message;
}
function toDiancai() {
uni.navigateBack()
}
let cartsSocket = null;
cartStore.goodsInit();
const socketInitPar = {
@@ -398,9 +334,7 @@ const noPayStatus = {
};
function youhuiReset() {
listinfo.pointsDiscountAmount = 0;
listinfo.pointsNum = 0;
listinfo.pointsDiscountAmount = 0;
listinfo.Productroll = 0;
try {
orderInfoAfterRef.value?.IntegralInputclose();
@@ -408,11 +342,15 @@ function youhuiReset() {
}
// 结账管理
import { Memberpay } from "@/stores/pay.js";
import {
Memberpay
} from "@/stores/pay.js";
const storeMemberpay = Memberpay();
import { productStore } from "@/stores/user.js";
import {
productStore
} from "@/stores/user.js";
const storeuser = productStore();
import payPassword from "@/components/payPassword.vue";
@@ -420,9 +358,16 @@ import orderInfo from "./components/orderInfo.vue";
// import orderInfoBefore from '../components/orderInfoBefore.vue'
import rechargeFree from "./components/rechargeFree.vue";
import paymentMethodes from "@/components/paymentMethod.vue"; //支付方式
import { onShow, onBackPress } from "@dcloudio/uni-app";
import { onHide } from "@dcloudio/uni-app";
import { getMsgByOrderIdReq } from "../../common/api/account/message";
import {
onShow,
onBackPress
} from "@dcloudio/uni-app";
import {
onHide
} from "@dcloudio/uni-app";
import {
getMsgByOrderIdReq
} from "../../common/api/account/message";
// 输入支付密码
const ispws = ref(false);
let userInfo = uni.cache.get("userInfo");
@@ -469,7 +414,6 @@ let backtimer = null;
let historyTotalPrices = 0;
// * 获取订单详情接口
const orderorderInfo = async (isNpwGetOrderDetail = false) => {
console.log("listinfo.id", listinfo.id);
if (!listinfo.id && !options.tableCode) {
return;
@@ -480,11 +424,11 @@ const orderorderInfo = async (isNpwGetOrderDetail = false) => {
}
}
let res = listinfo.id
? await APIgetOrderById({
let res = listinfo.id ?
await APIgetOrderById({
orderId: listinfo.id,
})
: await APIhistoryOrder({
}) :
await APIhistoryOrder({
tableCode: options.tableCode || "",
});
orderRemarker.value = res.remark;
@@ -565,8 +509,6 @@ function getOrderInfoAfterCalcInit(res) {
}
listinfo.discountInfo = tempArray;
}
// 回填先质控
listinfo.pointsDiscountAmount = 0;
// console.log(listinfo)
} else {
listinfo.totalPrices = cartStore.totalPrice * 1;
@@ -695,16 +637,13 @@ const createOrder = async () => {
orderId: listinfo.id || "",
tableCode: options.tableCode || "",
userId: uni.cache.get("userInfo").id || "", //
limitRate:
cartStore.limitTimeDiscount && cartStore.limitTimeDiscount.id
? {
limitRate: cartStore.limitTimeDiscount && cartStore.limitTimeDiscount.id ? {
id: cartStore.limitTimeDiscount.id,
discountRate: cartStore.limitTimeDiscount.discountRate,
discountPriority: cartStore.limitTimeDiscount.discountPriority,
foodType: cartStore.limitTimeDiscount.foodType,
foods: cartStore.limitTimeDiscount.foods,
}
: null,
} : null,
});
// 清空购物车
if (res) {
@@ -768,37 +707,30 @@ function returnPayParams() {
originAmount: cartStore.orderCostSummary.goodsRealAmount, //订单原金额(不包含打包费+餐位费)
discountRatio: 1, //折扣比例(计算时 向上取整保留 两位小数) 写死1
discountAmount: 0, //手动优惠金额 写死0
productCouponDiscountAmount:
cartStore.orderCostSummary.productCouponDeduction, //商品优惠券抵扣金额
productCouponDiscountAmount: cartStore.orderCostSummary.productCouponDeduction, //商品优惠券抵扣金额
otherCouponDiscountAmount: cartStore.orderCostSummary.fullCouponDeduction, //其他优惠券抵扣金额
couponList: cartStore.backendCoupons.map((v) => v.id), //用户使用的卡券
orderAmount: isBwc.value
? cartStore.orderCostSummary.orderOriginFinalPayAmount
: cartStore.orderCostSummary.finalPayAmount, // 最终订单金额
orderAmount: isBwc.value ?
cartStore.orderCostSummary.orderOriginFinalPayAmount : cartStore.orderCostSummary
.finalPayAmount, // 最终订单金额
roundAmount: 0, //抹零金额 减免多少钱
pointsDiscountAmount: cartStore.orderCostSummary.pointDeductionAmount, //积分抵扣金额(tb_points_basic_setting表)
pointsNum: cartStore.orderCostSummary.pointUsed, //(扣除各类折扣 enable_deduction后使用)
newCustomerDiscountAmount: cartStore.orderCostSummary.newUserDiscount, //新客立减
newCustomerDiscountId:
cartStore.orderCostSummary.newUserDiscount > 0
? cartStore.consumeDiscount.id
: "",
newCustomerDiscountId: cartStore.orderCostSummary.newUserDiscount > 0 ?
cartStore.consumeDiscount.id : "",
remark: orderRemarker.value, //用户备注
discountActAmount: cartStore.orderCostSummary.fullReduction.actualAmount, //满减抵扣金额
discountActId: cartStore.orderCostSummary.fullReduction.usedActivity
? cartStore.orderCostSummary.fullReduction.usedActivity.id
: null,
discountActId: cartStore.orderCostSummary.fullReduction.usedActivity ?
cartStore.orderCostSummary.fullReduction.usedActivity.id : null,
userId: uni.cache.get("userInfo").id || "", //
limitRate:
cartStore.limitTimeDiscount && cartStore.limitTimeDiscount.id
? {
limitRate: cartStore.limitTimeDiscount && cartStore.limitTimeDiscount.id ? {
id: cartStore.limitTimeDiscount.id,
discountRate: cartStore.limitTimeDiscount.discountRate,
discountPriority: cartStore.limitTimeDiscount.discountPriority,
foodType: cartStore.limitTimeDiscount.foodType,
foods: cartStore.limitTimeDiscount.foods,
}
: null,
} : null,
vipDiscountAmount: cartStore.orderCostSummary.vipDiscountAmount, //会员折扣减免金额
};
return {
@@ -813,9 +745,8 @@ function returnPayParams() {
shopId: orderVIP.value.shopId,
pwd: "",
userId: uni.cache.get("userInfo").id || "", //
discountActld: cartStore.orderCostSummary.fullReduction.usedActivity
? cartStore.orderCostSummary.fullReduction.usedActivity.id
: "", //满减活动id
discountActld: cartStore.orderCostSummary.fullReduction.usedActivity ?
cartStore.orderCostSummary.fullReduction.usedActivity.id : "", //满减活动id
};
}
@@ -868,6 +799,9 @@ const istoricalorders = async () => {
combinedArray = [...combinedArray, ...subArray];
}
}
console.log('combinedArray', combinedArray)
console.log('listinfo.combinedArray', listinfo.combinedArray)
// 判断支付之前是是否还有新加订单
if (listinfo.combinedArray.length === combinedArray.length) {
goToPay(payParams);
@@ -1008,15 +942,9 @@ const goToPay = async (payParams) => {
}
}
pay_unlock();
console.log("orderorderInfo");
orderorderInfo();
};
//
const clickPointsamount = (Pointsamount) => {
listinfo.pointsDiscountAmount = Pointsamount.pointsDiscountAmount;
listinfo.pointsNum = Pointsamount.pointsNum;
};
const payPasswordref = ref(null);
// 余额支付
@@ -1045,7 +973,6 @@ const accountPayevent = async (pwd) => {
//TODO handle the exception
pay_unlock();
}
orderorderInfo();
};
onBeforeUnmount(() => {
@@ -1128,6 +1055,7 @@ const navTitle = computed(() => {
//支付成功后的处理
async function paySucessCallback() {
console.log("paySucessCallback");
orderorderInfo(true);
cartsSocket.closeSocket();
showDrainage.value = true;
// const groupinfo=await chatApi.groupShopinfo({shop_id:cartStore.shopInfo.id})
@@ -1156,6 +1084,7 @@ function setOrder(res) {
}
orderRemarker.value = res.remark;
Object.assign(listinfo, res);
getOrderInfoAfterCalcInit(res);
}
async function init(opt) {
await storeuser.actionsproductqueryProduct();
@@ -1183,12 +1112,18 @@ async function init(opt) {
const shopInfoRes = await APIusershopInfodetail({
shopId: options.shopId,
});
//获取积分配置
pointsApi.pointsConfig().then(res => {
cartStore.setPointDeductionRule(res);
})
if (!isPayBefor() && options.tableCode) {
const res = await APIhistoryOrder({
tableCode: options.tableCode || "",
});
if (res && typeof res == "object") {
setOrder(res);
} else {
}
}
if (shopInfoRes) {
@@ -1254,8 +1189,7 @@ watch(
) {
showFreeDine.value = true;
}
},
{
}, {
immediate: true,
}
);
@@ -1305,15 +1239,74 @@ const disablePayType = computed(() => {
return Array.from(arr);
});
</script>
<style lang="scss">
page {
background-color: #f7f7f7;
watch(() => cartStore.limitTimeDiscount, (newval, oldval) => {
})
/**
* 更新购物车和历史订单数据
*/
function updateCartAndHistory() {
let newData = {
history: [],
cart: [],
};
// 扁平化历史订单商品
const oldOrderGoods = listinfo.combinedArray
for (let cart of oldOrderGoods) {
const canUseLimitTimeDiscount = yskUtils.limitUtils.canUseLimitTimeDiscount(
cart,
cartStore.limitTimeDiscount,
cartStore.shopInfo,
cartStore.shopUserInfo,
"productId"
) ?
1 :
0;
if (canUseLimitTimeDiscount != cart.isTimeDiscount) {
newData.history.push({
id: cart.id,
is_time_discount: canUseLimitTimeDiscount,
});
}
}
for (let cart of carts.value) {
const canUseLimitTimeDiscount = yskUtils.limitUtils.canUseLimitTimeDiscount(
cart,
cartStore.limitTimeDiscount,
cartStore.shopInfo,
cartStore.shopUserInfo,
"productId"
) ?
1 :
0;
if (canUseLimitTimeDiscount != cart.isTimeDiscount) {
newData.cart.push({
id: cart.id,
is_time_discount: canUseLimitTimeDiscount,
});
}
}
if (newData.history.length <= 0 && newData.cart.length <= 0) {
return;
}
cartsSocket.sendMessage({
...socketInitPar,
operate_type: "bulk_edit",
data: newData,
});
}
</script>
<style lang="scss" scoped>
.container {
padding: 32rpx 28rpx 32rpx 28rpx;
background-color: #f7f7f7;
}
.headStatus {
@@ -1509,8 +1502,7 @@ page {
padding: 32rpx 0 !important;
display: flex;
.cover {
}
.cover {}
.info {
flex: 1;
@@ -1792,4 +1784,14 @@ page {
.order-msg-list {
margin-bottom: 30rpx;
}
.textarea {
background-color: #F5F5F5;
border-radius: 10rpx;
overflow: hidden;
}
:deep(.u-textarea) {
background-color: #F5F5F5 !important;
}
</style>

View File

@@ -16,51 +16,32 @@
</view>
</view> -->
<view class="status-wrap">
<view
class="item"
:class="{ active: querForm.statusActiveIndex == 0 }"
@click="tabChange(0)"
>
<view class="item" :class="{ active: querForm.statusActiveIndex == 0 }" @click="tabChange(0)">
<text class="t">商品兑换券 {{ returnSelNumber(0) }}</text>
</view>
<view
class="item"
:class="{ active: querForm.statusActiveIndex == 1 }"
@click="tabChange(1)"
>
<view class="item" :class="{ active: querForm.statusActiveIndex == 1 }" @click="tabChange(1)">
<text class="t">折扣优惠券{{ returnSelNumber(1) }}</text>
</view>
<view
class="icon-wrap"
:style="{
<view class="icon-wrap" :style="{
width: `${100 / statusList.length}%`,
left: `${(100 / statusList.length) * querForm.statusActiveIndex}%`,
}"
>
<image
class="active-icon"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAYAAAB4MH11AAACKUlEQVR4AaRTS2sTURT+MpkxUzNtRpuYpBsrqbgQH6BYRHGllloQN+5cuNeV6MKfoj9BwYUgIsXHRqE+SluliFLbBqmENJNMMpMm87qecw1NglJse+HMPeee833nce8oIgpFq7wsGt/fCb9eFrtdfmNd1L+9Jc4Vog6E4jcqqM0/R+3zNOwv09jtshdfSx7mDJwKqAMfIvAAEaFVWkLgWjvOEbVdtNa+Epf4wxmFUNTkfqiDGUkqogDu8icKiKS9nQ9jnR8fqM5AwuJJE/G9JhRF05E8dAqIKeDlrsyiWVxg9f+Fum+uzsFZmtnEGIVxKHsGIFn1bAF6ZlQ6I28D9uIrmYSrkodbfETgw6GubZo9Yzk0kR4Fc7IuE8QUFebJKSTSB/kM4UYd1scnKL18QInm4Vk/6W6qiNoOwpZDuoX2elFWXHrzELW5Z+RzJVbPHca+09doIKq0ZQLW+C44iWbm2ZQSNMqozj5F5f1jWCSVGd4fwerstYUXCOplGcsfxponJqHS7Nlm2UyAWAza0AFkzt1A6uhFqEYaoHsR9BLCpg2vukZVr5IU4dV+yU7oRikkTrHDMI9PIHPhJrhQ9Kxugs6hkkhi8Mh5DI9flyCep6ImOt7uxnF6dgypYxMy1hg7S0/yH3FdSL+mpbIw6CVwVSNX7yM/eQfZS7eQu3wb+St3MTJ1D2nq1iicgZbK9YN7rL866PH1qfGBIWj0v/Do4rrR59vK+A0AAP//GfTndQAAAAZJREFUAwCu+SjIaSGpLwAAAABJRU5ErkJggg=="
></image>
}">
<image class="active-icon"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAYAAAB4MH11AAACKUlEQVR4AaRTS2sTURT+MpkxUzNtRpuYpBsrqbgQH6BYRHGllloQN+5cuNeV6MKfoj9BwYUgIsXHRqE+SluliFLbBqmENJNMMpMm87qecw1NglJse+HMPeee833nce8oIgpFq7wsGt/fCb9eFrtdfmNd1L+9Jc4Vog6E4jcqqM0/R+3zNOwv09jtshdfSx7mDJwKqAMfIvAAEaFVWkLgWjvOEbVdtNa+Epf4wxmFUNTkfqiDGUkqogDu8icKiKS9nQ9jnR8fqM5AwuJJE/G9JhRF05E8dAqIKeDlrsyiWVxg9f+Fum+uzsFZmtnEGIVxKHsGIFn1bAF6ZlQ6I28D9uIrmYSrkodbfETgw6GubZo9Yzk0kR4Fc7IuE8QUFebJKSTSB/kM4UYd1scnKL18QInm4Vk/6W6qiNoOwpZDuoX2elFWXHrzELW5Z+RzJVbPHca+09doIKq0ZQLW+C44iWbm2ZQSNMqozj5F5f1jWCSVGd4fwerstYUXCOplGcsfxponJqHS7Nlm2UyAWAza0AFkzt1A6uhFqEYaoHsR9BLCpg2vukZVr5IU4dV+yU7oRikkTrHDMI9PIHPhJrhQ9Kxugs6hkkhi8Mh5DI9flyCep6ImOt7uxnF6dgypYxMy1hg7S0/yH3FdSL+mpbIw6CVwVSNX7yM/eQfZS7eQu3wb+St3MTJ1D2nq1iicgZbK9YN7rL866PH1qfGBIWj0v/Do4rrR59vK+A0AAP//GfTndQAAAAZJREFUAwCu+SjIaSGpLwAAAABJRU5ErkJggg==">
</image>
</view>
</view>
</view>
<view class="list-wrap">
<view class="tips">
<text class="t"
>使用商品兑换券的商品不再计入同享优惠券门槛和折扣计算</text
>
<text class="t">使用商品兑换券的商品不再计入同享优惠券门槛和折扣计算</text>
</view>
<view class="title-wrap">
<text class="t">可用红包</text>
<text class="n">{{ list.canUseCoupons.length }}</text>
</view>
<view
class="item"
v-for="item in list.canUseCoupons"
:key="item.id"
@click="changeSelCoupon(item)"
>
<view class="item" v-for="item in list.canUseCoupons" :key="item.id" @click="changeSelCoupon(item)">
<view class="top">
<view class="icon">
<couponIcon :item="item" />
@@ -70,29 +51,21 @@
<text class="t">{{ item.name }}</text>
</view>
<view class="view time">
<text class="t"
>{{ dayjs(item.effectStartTime).format("YYYY.M.D") }} -
{{ dayjs(item.effectEndTime).format("YYYY.M.D") }}</text
>
<text class="t">{{ dayjs(item.effectStartTime).format("YYYY.M.D") }} -
{{ dayjs(item.effectEndTime).format("YYYY.M.D") }}</text>
</view>
</view>
<view class="btn">
<view class="active" v-if="isActive(item)">
<up-icon
name="checkmark-circle-fill"
size="24"
color="#FF3232"
></up-icon>
<up-icon name="checkmark-circle-fill" size="24" color="#FF3232"></up-icon>
</view>
<view class="round" v-else></view>
</view>
</view>
<view class="btm">
<view class="left"
>1可适用门店{{ item.useShops }} 2可适用商品{{
<view class="left">1可适用门店{{ item.useShops }} 2可适用商品{{
item.foods
}}3可使用类型{{ convertValuesToLabels(item.useType) }}</view
>
}}3可使用类型{{ convertValuesToLabels(item.useType) }}</view>
<view class="right" @click.stop="showDetailHandle(item)">
<text class="t">查看详情</text>
</view>
@@ -102,11 +75,7 @@
<text class="t">不可用红包</text>
<text class="n">{{ list.noCanUseCoupons.length }}</text>
</view>
<view
class="item disabled"
v-for="item in list.noCanUseCoupons"
:key="item.id"
>
<view class="item disabled" v-for="item in list.noCanUseCoupons" :key="item.id">
<view class="top">
<view class="icon">
<couponIcon :item="item" />
@@ -116,10 +85,8 @@
<text class="t">{{ item.name }}</text>
</view>
<view class="view time">
<text class="t"
>{{ dayjs(item.effectStartTime).format("YYYY.M.D") }} -
{{ dayjs(item.effectEndTime).format("YYYY.M.D") }}</text
>
<text class="t">{{ dayjs(item.effectStartTime).format("YYYY.M.D") }} -
{{ dayjs(item.effectEndTime).format("YYYY.M.D") }}</text>
</view>
</view>
<view class="btn">
@@ -144,17 +111,8 @@
<view class="title">
<text class="t">店铺列表</text>
</view>
<scroll-view
class="popup-list"
direction="vertical"
@scrollend="scrollBottom"
>
<view
class="item"
v-for="item in shopList"
:key="item.shopId"
@click="selectShopHandle(item)"
>
<scroll-view class="popup-list" direction="vertical" @scrollend="scrollBottom">
<view class="item" v-for="item in shopList" :key="item.shopId" @click="selectShopHandle(item)">
<text class="t">{{ item.shopName }}</text>
<text class="intro">地址{{ item.shopAddress }}</text>
</view>
@@ -162,24 +120,16 @@
</scroll-view>
</view>
</u-popup>
<u-popup
:show="showDetail"
round="20"
closeable
@close="showDetail = false"
>
<u-popup :show="showDetail" round="20" closeable @close="showDetail = false">
<view class="shoplist-popup">
<view class="title">
<text class="t">详情说明</text>
</view>
<scroll-view class="popup-list" direction="vertical">
<view class="ul">
<view
class="li"
v-for="(item, index) in selectListItemDetails"
:key="index"
>{{ index + 1 }}{{ item }}</view
>
<view class="li" v-for="(item, index) in selectListItemDetails" :key="index">
{{ index + 1 }}{{ item }}
</view>
</view>
</scroll-view>
</view>
@@ -189,7 +139,13 @@
<script setup>
import dayjs from "dayjs";
import { ref, reactive, onMounted, computed, watch } from "vue";
import {
ref,
reactive,
onMounted,
computed,
watch
} from "vue";
import {
onLoad,
onReady,
@@ -203,12 +159,16 @@ import {
APIfindCoupon,
getCouponShops,
} from "@/common/api/member.js";
import { findCoupon } from "@/common/api/market/coupon.js";
import {
findCoupon
} from "@/common/api/market/coupon.js";
import couponIcon from "@/components/coupon-icon/index";
// import * as UTILS from '@/utils/goods-utils.js';
import yskUtils from "ysk-utils";
const UTILS = yskUtils.couponUtils;
import { useCartsStore } from "@/stores/carts.js";
import {
useCartsStore
} from "@/stores/carts.js";
const cartStore = useCartsStore();
//返回不可用原因
@@ -231,8 +191,7 @@ const querForm = ref({
statusActiveIndex: 0,
});
const statusList = ref([
{
const statusList = ref([{
value: 0,
label: "商品兑换券",
bg: "#333333",
@@ -265,22 +224,22 @@ function showDetailHandle(item) {
const ruleList = [];
// 规则1限时折扣同享始终显示
const discountRule = item.discountShare
? "与限时折扣同享"
: "不与限时折扣同享";
const discountRule = item.discountShare ?
"与限时折扣同享" :
"不与限时折扣同享";
ruleList.push(discountRule);
// 规则2会员价/会员折扣同享(始终显示)
const vipRule = item.vipPriceShare
? "与会员价/会员折扣同享"
: "不与会员价/会员折扣同享";
const vipRule = item.vipPriceShare ?
"与会员价/会员折扣同享" :
"不与会员价/会员折扣同享";
ruleList.push(vipRule);
// 规则3其他优惠券同享仅item.type=2时显示
if (item.type === 2) {
const otherCouponRule = item.otherCouponShare
? "与其他优惠券同享"
: "不与其他优惠券同享";
const otherCouponRule = item.otherCouponShare ?
"与其他优惠券同享" :
"不与其他优惠券同享";
ruleList.push(otherCouponRule);
}
const shareRuleText = `${ruleList.join("、")}`;
@@ -389,6 +348,7 @@ watch(
formatCoupon();
}
);
function isActive(item) {
if (querForm.value.statusActiveIndex) {
return couponSel.value.id == item.id;
@@ -398,6 +358,7 @@ function isActive(item) {
}
const couponList = ref([]);
function formatCoupon() {
let canUseGoodsCoupon = [];
let canUseDiscountCoupon = [];
@@ -420,9 +381,9 @@ function formatCoupon() {
for (let i = 0; i < couponList.value.length; i++) {
const coupon = couponList.value[i];
const selCoupon =
querForm.value.statusActiveIndex != 1
? quansSelArr.value.filter((v) => v.type != 2)
: quansSelArr.value.filter((v) => v.type == 2);
querForm.value.statusActiveIndex != 1 ?
quansSelArr.value.filter((v) => v.type != 2) :
quansSelArr.value.filter((v) => v.type == 2);
const canuseResult = UTILS.returnCouponCanUse({
canDikouGoodsArr,
coupon,
@@ -432,7 +393,10 @@ function formatCoupon() {
shopInfo,
limitTimeDiscount: cartStore.limitTimeDiscount,
});
const { canUse, reason } = canuseResult;
const {
canUse,
reason
} = canuseResult;
if (coupon.type == 2) {
if (canUse || goodsCouponSel.value.id == coupon.id) {
canUseGoodsCoupon.push(coupon);
@@ -460,7 +424,7 @@ function formatCoupon() {
v,
user,
goodsOrderPrice,
quansSelArr.value,
quansSelArr.value.filter((v) => v.type != 2),
shopInfo,
cartStore.limitTimeDiscount
);
@@ -477,7 +441,7 @@ function formatCoupon() {
v,
user,
goodsOrderPrice,
quansSelArr.value,
quansSelArr.value.filter((v) => v.type == 2),
shopInfo,
cartStore.limitTimeDiscount
);
@@ -534,8 +498,7 @@ function scrollBottom() {
*/
function convertValuesToLabels(valueStr, options, separator = "、") {
try {
options = [
{
options = [{
value: "dine",
label: "堂食",
},
@@ -661,6 +624,16 @@ watch(
};
});
otherCoupon = otherCoupon.map((v) => {
const canuseResult = UTILS.returnCouponCanUse({
canDikouGoodsArr,
coupon:v,
goodsOrderPrice,
user,
selCoupon: goodsCoupon,
shopInfo,
limitTimeDiscount: cartStore.limitTimeDiscount,
});
const discount = UTILS.returnCouponDiscount(
canDikouGoodsArr,
v,
@@ -672,13 +645,40 @@ watch(
);
return {
...v,
canuseResult,
discount,
discountAmount: discount ? discount.discountPrice : v.discountAmount,
};
});
uni.$emit("selCoupon", [...goodsCoupon, ...otherCoupon]);
},
{
let noCanUseCoupon = null
for (let item of otherCoupon) {
console.log('item',item)
if (!item.canuseResult.canUse) {
noCanUseCoupon = item
couponSel.value={id:''}
console.log('couponSel.value',couponSel.value)
return uni.showModal({
title: '提示',
showCancel:false,
content: '优惠券' + item.name + '已不满足使用门槛,已剔除',
success() {
}
})
break
}
}
if(noCanUseCoupon){
return
}
const newArr = [...goodsCoupon, ...otherCoupon]
uni.$emit("selCoupon", newArr);
}, {
deep: true,
}
);

View File

@@ -217,7 +217,6 @@
listinfo.discountInfo = tempArray;
}
// 回填先质控
listinfo.pointsDiscountAmount = 0
// console.log(listinfo)
console.log("orderorderInfo list info: ", listinfo);
@@ -686,7 +685,6 @@
position: relative;
width: 100%;
height: 100%;
box-shadow: 0rpx 4rpx 12rpx 2rpx rgba(87, 86, 86, 0.35);
border-radius: 18rpx;
.card_head_box {

View File

@@ -73,16 +73,23 @@
<view class="shop-amount">
<text
class="orderAmount">{{item.status == 'unpaid'?item.originAmount:item.orderAmount}}</text>
<text class="totalNumber">{{item.goods.length}}</text>
<text class="totalNumber">{{totalGoodsNum(item.goods) }}</text>
</view>
</view>
</view>
<!-- <view class="footer-wrap">
<view class="take_food_number u-m-b-16" v-if="item.takeCode">
<text class="u-font-24 color-333"> 取餐号</text>
<text class="u-font-32 font-700 color-333">{{item.takeCode}}</text>
</view>
<view class="footer-wrap">
<view class="btn" @click.stop="$u.debounce(isRemoveOrder(item,index),1000)"
v-if="item.status != 'unpaid' && item.status != 'paying'"> 删除订单 </view>
v-if="item.status == 'done' || item.status == 'cancelled'"> 删除订单 </view>
<view class="btn s" @click.stop="$u.debounce(showpopupclick(item),1000)"
v-if="item.status == 'unpaid' || item.status == 'paying'"> 去付款 </view>
</view> -->
<view class="btn s" @click.stop="agignOrder(item)" v-if="item.status == 'done'"> 再来一单 </view>
</view>
</view>
</view>
<view class="flex-colum" v-if="orderForm.list.length <= 0">
@@ -116,14 +123,60 @@
} from '@dcloudio/uni-app'
import Nav from '@/components/CustomNavbar.vue'; //导航栏
import {
APIuserorder
APIuserorder,
APIputuserorder
} from '@/common/api/order/index.js'
// pinia管理
import {
useNavbarStore
} from '@/stores/navbarStore';
import {APIshopUserInfo} from '@/common/api/member.js'
const store = useNavbarStore();
async function agignOrder(item){
await APIshopUserInfo({
shopId:item.shopId
}).then(shopUserInfo=>{
if(shopUserInfo){
uni.cache.set("dinersNum", 1);
uni.cache.set("tableCode", shopUserInfo.id);
uni.cache.set("shopId", item.shopId);
uni.navigateTo({
url: "/pages/product/index?type=beforehand&order_id="+item.id+'&one_more_order=1',
});
}
})
}
function totalGoodsNum(arr){
if(!arr){
return 0
}
return arr.reduce((prve,cur)=>{
return prve+cur.num
},0)
}
function isRemoveOrder(item) {
uni.showModal({
title: '提示',
content: '订单一旦删除不可恢复,确认删除该订单吗?',
showCancel: true,
success(res) {
if (res.confirm) {
APIputuserorder(item.id).then(res => {
uni.showToast({
title: '删除成功',
icon: 'none'
})
setTimeout(() => {
init_fn()
}, 1000)
})
}
}
})
}
// 导航栏
const tabs = [{
name: '全部',
@@ -340,7 +393,7 @@
}
.header-wrap {
padding: 16rpx 18rpx;
padding: 32rpx 24rpx;
display: flex;
justify-content: space-between;
align-items: center;
@@ -378,7 +431,7 @@
}
.content {
padding: 0 18rpx 18rpx 18rpx;
padding: 0 24rpx 16rpx 30rpx;
.shop-info {
display: flex;
@@ -450,11 +503,9 @@
padding: 0 18rpx 32rpx 18rpx;
.btn {
width: 128rpx;
height: 48rpx;
line-height: 43rpx;
text-align: center;
background: #FFFFFF;
padding: 6rpx 14rpx;
border-radius: 10rpx 10rpx 10rpx 10rpx;
border: 2rpx solid #EDEDED;
font-weight: 400;
@@ -527,4 +578,10 @@
.ml-20 {
margin-left: 20rpx;
}
.take_food_number{
padding: 20rpx;
background-color: #F8F8F8;
margin-left: 36rpx;
margin-right: 24rpx;
}
</style>

View File

@@ -0,0 +1,43 @@
// 套餐比较两个对象是否相等
export function isObjectEqual(obj1, obj2) {
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
return obj1 === obj2;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
for (const key of keys1) {
if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) {
if (!isArrayEqual(obj1[key], obj2[key])) {
return false;
}
} else if (!isObjectEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
// 比较两个数组是否相等(忽略顺序)
export function isArrayEqual(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}
const usedIndices = new Array(arr2.length).fill(false);
for (const item1 of arr1) {
let found = false;
for (let i = 0; i < arr2.length; i++) {
if (!usedIndices[i] && isObjectEqual(item1, arr2[i])) {
usedIndices[i] = true;
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@
</view>
<view class="bildRight">
<text>我的积分</text>
<view>{{shopUserInfo.accountPoints||0}}</view>
<view>{{shopUserInfo.pointsUser?shopUserInfo.pointsUser.pointBalance:0}}</view>
</view>
</view>
<view class="bottom">
@@ -109,7 +109,8 @@
} from 'vue'
import {
onReachBottom,onLoad
onReachBottom,
onLoad
} from '@dcloudio/uni-app'
import {
@@ -208,20 +209,21 @@
})
if (res) {
Object.assign(shopUserInfo, res)
formData.id =res.pointsUser?res.pointsUser.id:''
formData.shopId =res.pointsUser?res.pointsUser.shopId:''
}
}
const options = reactive({})
onLoad((opt)=>{
onLoad(async (opt) => {
Object.assign(options, opt)
console.log('options', options);
formData.shopId = options.shopId
formData.active = options.type
formData.id = options.id || ''
console.log(formData.info)
await getShopUserInfo()
getlist()
getShopUserInfo()
})
</script>
<style scoped lang="less">
@@ -232,6 +234,7 @@
justify-content: space-around;
align-items: center;
position: relative;
.bg {
position: absolute;
left: 0;
@@ -239,11 +242,13 @@
top: 0;
bottom: 0;
z-index: -1;
.image {
width: 100%;
height: 100%;
}
}
.bildLeft,
.bildRight {
font-weight: 400;
@@ -261,12 +266,14 @@
}
}
}
.bottom {
background-color: rgba(255, 255, 255, .3);
padding: 40rpx 28rpx 0 28rpx;
transform: translateY(-140rpx);
border-radius: 74rpx 74rpx 0 0;
}
.navTop {
display: flex;
justify-content: space-around;

View File

@@ -33,7 +33,7 @@
<view class="u-flex">
<view class="color-666 no-wrap">领取方式</view>
<view class="u-m-l-16" v-if="item.goodsCategory=='优惠券'">系统发放</view>
<view class="u-m-l-16" v-else>店内自</view>
<view class="u-m-l-16" v-else>需前往店铺自行兑换领</view>
</view>
<view class="u-flex u-m-t-16 u-col-baseline" v-if="item.goodsCategory=='优惠券'">
<view class="color-666 no-wrap">特别说明</view>
@@ -53,9 +53,12 @@
</view>
</view>
<view style="height: 100px"></view>
<view style="height: 140px"></view>
<view class="fixed-bottom u-flex u-row-center">
<view class="btn" @click="exchangeClick" :class="[isCanExchange?'':'gray']">
<view v-if="isCanExchange" class="btn" @click="exchangeClick" >
{{returnBtmText}}
</view>
<view class="btn gray" v-else >
{{returnBtmText}}
</view>
</view>
@@ -77,7 +80,7 @@
<view class="popup-content-top">
<text class="color-666">领取方式</text>
<text class="u-m-l-16" v-if="item.goodsCategory=='优惠券'">系统发放</text>
<text class="u-m-l-16" v-else>店内自</text>
<text class="u-m-l-16" v-else>需前往店铺自行兑换领</text>
</view>
<view class="goods-info">
<view class="u-flex">
@@ -414,7 +417,7 @@
background-color: #fff;
left: 0;
right: 0;
padding-bottom: calc(env(safe-area-inset-bottom) + 2rpx);
padding-bottom: 40px;
padding-top: 32rpx;
background-color: #fff;
bottom: 0;

View File

@@ -3,7 +3,7 @@
<view v-for="(item, index) in list" :key="index" @click="toDetail(item)">
<template v-if="layout === 'list'">
<view class="item">
<view class="img coupon" v-if="item.goodsCategory=='优惠券'&&item.couponInfo">
<view class="img coupon" v-if="item.goodsCategory=='优惠券'&&item.couponInfo" style="width: 142rpx;">
<couponIcon :item="item.couponInfo" typeKey="couponType" />
</view>
<image class="img" v-else lazy-load :src="item.goodsImageUrl" mode="aspectFill"></image>

View File

@@ -7,7 +7,7 @@
</view>
<view class="u-m-t-22 u-flex u-col-center">
<up-image width="132rpx" height="132rpx" :src="item.goodsImageUrl" v-if="item.goodsCategory!='优惠券'"></up-image>
<view class="img" v-else>
<view class="img" style="width: 138rpx;" v-else>
<couponIcon :item="item.couponInfo" typeKey="couponType" />
</view>
<view class="u-p-l-36">

View File

@@ -10,13 +10,17 @@
<view class="u-p-l-28 u-flex-1">
<view class="font-bold">{{item.pointsGoodsName}}</view>
<view class="u-flex u-row-between u-m-t-8">
<text class="color1 font-bold">{{item.spendPoints}}积分</text>
<view class="u-flex color1 font-bold">
<text >{{item.spendPoints}}积分</text>
<text v-if="item.extraPaymentAmount">+ {{item.extraPaymentAmount}}</text>
</view>
<text class="status " :class="[returnStatusClass(item)]">{{item.status}}</text>
</view>
<view class="u-m-t-8 font-bold color1"> X{{item.number}} </view>
</view>
</view>
<template v-if="item.goodsCategory=='其它商品'&&item.status!='已退款'">
<template v-if="item.goodsCategory=='其它商品'&&item.status!='已退款'&&item.status!='已完成'">
<view class="u-m-t-16">
<view class="u-flex u-row-center">
<up-qrcode cid="ex1" :size="104" :val="qrcode"></up-qrcode>

View File

@@ -13,7 +13,7 @@
<up-loadmore :status="isEnd?'nomore':'loadmore'"></up-loadmore>
</view>
<modal v-model="modalData.show" title="立即兑换确认" :showBottom="false">
<modal v-model="modalData.show" title="查看券码" :showBottom="false">
<view class="u-p-28">
<view class="u-flex u-row-center">
<up-qrcode cid="ex1" :size="104" :val="qrcode"></up-qrcode>

View File

@@ -93,6 +93,9 @@ export const useWebSocket = defineStore('socketTask', () => {
function setOnMessage(onMessageBallBack){
onMessage=onMessageBallBack
}
function chnageInitMessage(data){
initMessage=data
}
// 连接 WebSocket
const connect = async (connectMsg, onMessageBallBack) => {
if (!isNetworkConnected.value) {
@@ -363,6 +366,6 @@ export const useWebSocket = defineStore('socketTask', () => {
initNetworkListener,
connect,
allowReconnect,
socketTask,setOnMessage
socketTask,setOnMessage,chnageInitMessage
};
})

View File

@@ -1,4 +1,6 @@
import { defineStore } from "pinia";
import {
defineStore
} from "pinia";
// import yskUtils from 'ysk-utils'
// const {
// OrderPriceCalculator,
@@ -10,11 +12,19 @@ import { defineStore } from "pinia";
// MerchantReductionType,
// GoodsType
// } = yskUtils
import yskUtils from '@/lib/index'
// import yskUtils from 'ysk-utils'
const {
OrderPriceCalculator
} = yskUtils
import yskUtils from 'ysk-utils'
const {OrderPriceCalculator}=yskUtils
import { ref, computed, reactive, watchEffect, watch } from "vue";
import {
ref,
computed,
reactive,
watchEffect,
watch
} from "vue";
import {
productminiApphotsquery,
APIgroupquery,
@@ -35,13 +45,12 @@ export const useCartsStore = defineStore("cart", () => {
// 适配工具库 BaseCartItem 接口的商品数据转换函数
const convertToBaseCartItem = (item) => {
const skuData = item.skuData
? {
const skuData = item.skuData ? {
id: item.skuData.id || item.sku_id,
salePrice: item.skuData.salePrice || 0,
memberPrice: item.skuData.memberPrice || 0,
}
: undefined;
} :
undefined;
const goods = getProductDetails({
...item,
@@ -49,7 +58,8 @@ export const useCartsStore = defineStore("cart", () => {
sku_id: item.skuId || item.sku_id,
});
console.log('convertToBaseCartItem', item)
const discountSaleAmount=item.discount_sale_amount?(item.discount_sale_amount*1) : (item.discountSaleAmount?item.discountSaleAmount*1:0)
const discountSaleAmount = item.discount_sale_amount ? (item.discount_sale_amount * 1) : (item
.discountSaleAmount ? item.discountSaleAmount * 1 : 0)
return {
...item,
id: item.id,
@@ -66,15 +76,13 @@ export const useCartsStore = defineStore("cart", () => {
discountSaleAmount: discountSaleAmount,
packFee: item.packFee || (goods ? goods.packFee : 0) || 0,
packNumber: item.pack_number || item.packNumber || 0,
activityInfo: item.activityInfo
? {
activityInfo: item.activityInfo ? {
type: item.activityInfo.type,
discountRate: OrderPriceCalculator.formatDiscountRate(
item.activityInfo.discountRate
),
vipPriceShare: !!item.activityInfo.vipPriceShare,
}
: undefined,
} : undefined,
skuData,
};
};
@@ -117,8 +125,7 @@ export const useCartsStore = defineStore("cart", () => {
(newval) => {
seatFeeConfig.value.isEnabled = !shopInfo.value.isTableFee;
seatFeeConfig.value.pricePerPerson = shopInfo.value.tableFee || 0;
},
{
}, {
deep: true,
}
);
@@ -130,21 +137,36 @@ export const useCartsStore = defineStore("cart", () => {
() => seatFeeConfig.value,
(newval) => {
console.log("seatFeeConfig", seatFeeConfig.value);
},
{
}, {
deep: true,
}
);
//积分规则
const pointDeductionRule = ref({
enableRewards: 0, //是否开启
pointsPerYuan: 0,
maxDeductionAmount: Infinity,
maxDeductionRatio: 0 ,//积分抵扣比例
minPaymentAmount:0,//门槛
});
function setPointDeductionRule(pointsPerYuan, maxDeductionAmount) {
pointDeductionRule.value.pointsPerYuan = pointsPerYuan;
pointDeductionRule.value.maxDeductionAmount = maxDeductionAmount;
function setPointDeductionRule(args) {
const {
equivalentPoints,
maxDeductionAmount,enableRewards,minPaymentAmount,
maxDeductionRatio
} = args
pointDeductionRule.value.pointsPerYuan =equivalentPoints||0;
pointDeductionRule.value.maxDeductionAmount = maxDeductionAmount||0;
pointDeductionRule.value.maxDeductionRatio = maxDeductionRatio||0;
pointDeductionRule.value.enableRewards = enableRewards||0;
pointDeductionRule.value.minPaymentAmount = minPaymentAmount||0;
}
// 初始配置:默认无减免(固定金额 0 元)
@@ -351,8 +373,7 @@ export const useCartsStore = defineStore("cart", () => {
}
// 历史订单
if (Message.operate_type == "clearOrder") {
}
if (Message.operate_type == "clearOrder") {}
// 购物车数据更新从新请求
if (
@@ -431,9 +452,10 @@ export const useCartsStore = defineStore("cart", () => {
return false;
}
const isUse =
shopUserInfo.value.isVip && shopUserInfo.value.isMemberPrice && shopInfo.value.isMemberPrice==1
? true
: false;
shopUserInfo.value.isVip && shopUserInfo.value.isMemberPrice && shopInfo.value
.isMemberPrice == 1 ?
true :
false;
return isUse;
});
@@ -648,7 +670,7 @@ export const useCartsStore = defineStore("cart", () => {
orderCostSummary,
setCoupons,
userPoints,
setUserPoints,
setUserPoints,pointDeductionRule,
setPointDeductionRule,
setOldOrder,
//返回商品信息