diff --git a/.env.development b/.env.development index bf19431..34d306e 100644 --- a/.env.development +++ b/.env.development @@ -7,14 +7,14 @@ VITE_APP_BASE_API=/dev-api # 接口地址 # VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 测试 -VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式 -# VITE_APP_API_URL=http://192.168.1.42/ # 本地 +# VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式 + VITE_APP_API_URL=http://192.168.1.42/ # 本地 # WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws # VITE_APP_WS_ENDPOINT=wss://sockets.sxczgkj.com/wss -VITE_APP_WS_ENDPOINT=wss://czgeatws.sxczgkj.com/wss # 正式 -# VITE_APP_WS_ENDPOINT=ws://192.168.1.42:2348 # 本地 +# VITE_APP_WS_ENDPOINT=wss://czgeatws.sxczgkj.com/wss # 正式 +VITE_APP_WS_ENDPOINT=ws://192.168.1.42:2348 # 本地 # 启用 Mock 服务 diff --git a/package.json b/package.json index 88422bc..25a9273 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "vue-clipboard3": "^2.0.0", "vue-i18n": "^11.1.0", "vue-router": "^4.5.0", - "ysk-utils": "^1.0.50" + "ysk-utils": "^1.0.59" }, "devDependencies": { "@commitlint/cli": "^19.7.1", diff --git a/src/store/modules/carts.ts b/src/store/modules/carts.ts index 5eb1dc2..7ece518 100644 --- a/src/store/modules/carts.ts +++ b/src/store/modules/carts.ts @@ -21,6 +21,9 @@ import { import { useUserStore } from "@/store/modules/user"; import { canUseLimitTimeDiscount, returnPrice } from '@/utils/order-utils' +import yskUtils from 'ysk-utils' +// const limitUtils = yskUtils.limitUtils +import limitUtils from './limit' const shopUser = useUserStoreHook(); export interface CartsState { @@ -37,7 +40,6 @@ interface PointDeductionRule { maxDeductionAmount: number; } - export const useCartsStore = defineStore("carts", () => { // ------------------------------ 1. 移到内部的工具函数(核心修复) ------------------------------ // 适配工具库 BaseCartItem 接口的商品数据转换函数(原外部函数,现在内部) @@ -61,13 +63,12 @@ export const useCartsStore = defineStore("carts", () => { return { id: item.id, product_id: item.product_id, - salePrice: item.salePrice || 0, number: item.number || 0, product_type: productType, is_temporary: !!item.is_temporary, is_gift: !!item.is_gift, returnNum: item.returnNum || 0, - memberPrice: item.memberPrice || 0, + memberPrice: item.is_time_discount == 1 ? item.limitDiscountPrice : (item.memberPrice || 0), discountSaleAmount: item.discount_sale_amount || 0, packFee: item.packFee || 0, packNumber: item.pack_number || 0, @@ -79,9 +80,9 @@ export const useCartsStore = defineStore("carts", () => { } : undefined, skuData, - is_time_discount: item.isTimeDiscount, - isTimeDiscount: item.isTimeDiscount, - salePrice: item.limitDiscountPrice + is_time_discount: item.is_time_discount, + isTimeDiscount: item.is_time_discount, + salePrice: item.limitDiscountPrice || 0 }; }; @@ -97,18 +98,21 @@ export const useCartsStore = defineStore("carts", () => { // console.log('list=====================================', list.value); // console.log('giftList=====================================', giftList.value); // console.log('oldOrder.value.detailMap=====================================', oldOrder.value.detailMap); - - console.log('getAllGoodsList.[]===================', [...currentGoods, ...giftGoods, ...oldOrderGoods]) + // console.log('getAllGoodsList.[]===================', [...currentGoods, ...giftGoods, ...oldOrderGoods]) return [...currentGoods, ...giftGoods, ...oldOrderGoods]; }; - const limitDiscountRes = ref(null) + const limitDiscountRes = ref | null>(null); // ------------------------------ 2. Store 内部原有响应式变量 ------------------------------ // 选择用户 const vipUser = ref<{ id?: string | number, isVip?: boolean }>({}); function changeUser(user: any) { - vipUser.value = user; + vipUser.value = { + ...user, + isMemberPrice: shopUser.userInfo.isMemberPrice + }; + getGoods({}) payParamsInit() } @@ -169,9 +173,10 @@ export const useCartsStore = defineStore("carts", () => { }) // 获取满减活动 - fullReductionActivities.value = await limitTimeDiscountApi.getDiscountActivity({ + const fullReductionRes = await limitTimeDiscountApi.getDiscountActivity({ shopId: localStorage.getItem("shopId") - }) + }); + fullReductionActivities.value = fullReductionRes?.data || []; if (limitDiscountRes.value !== null) { checkLinkFinished().then(() => { @@ -195,13 +200,41 @@ export const useCartsStore = defineStore("carts", () => { ...query, }); - const shopInfo = JSON.parse(localStorage.getItem('userInfo')) - goods.value = res.records.map(item => { + const shopInfoStr = localStorage.getItem('userInfo') || '{}'; + const shopInfo = JSON.parse(shopInfoStr); + + interface ProductItem { + id: string | number; + lowMemberPrice?: number; + lowPrice?: number; + [key: string]: any; + } + + interface LimitDiscountResult { + discountPriority?: 'limit-time' | 'vip-price' | string; + [key: string]: any; + } + + interface GoodsWithDiscount extends ProductItem { + isLimitDiscount: boolean; + limitDiscountPrice: number; + } + + const shopInfoTyped = shopInfo as Record; + const ldRes = limitDiscountRes.value as LimitDiscountResult | null; + + goods.value = (res.records as ProductItem[]).map((item: ProductItem): GoodsWithDiscount => { return { ...item, - isLimitDiscount: limitDiscountRes.value !== null && canUseLimitTimeDiscount(item, limitDiscountRes.value, shopInfo, vipUser.value), - limitDiscountPrice: limitDiscountRes.value !== null && returnPrice({ goods: { ...item, memberPrice: item.lowMemberPrice, salePrice: item.lowPrice }, shopInfo: shopInfo, limitTimeDiscountRes: limitDiscountRes.value, shopUserInfo: vipUser.value, idKey: 'id' }) - } + isLimitDiscount: ldRes !== null && limitUtils.canUseLimitTimeDiscount(item, ldRes, shopInfoTyped, vipUser.value, 'id'), + limitDiscountPrice: limitUtils.returnPrice({ + goods: { ...item, memberPrice: item.lowMemberPrice, salePrice: item.lowPrice }, + shopInfo: shopInfoTyped, + limitTimeDiscountRes: ldRes, + shopUserInfo: vipUser.value, + idKey: 'id' + }) + } as GoodsWithDiscount; }); console.log('代客下单页面商品缓存.goods.value', goods.value); @@ -292,8 +325,7 @@ export const useCartsStore = defineStore("carts", () => { pointsPerYuan: 100, maxDeductionAmount: Infinity }) - - const fullReductionActivities = ref([]) + const fullReductionActivities = ref([]) //使用积分数量 const userPoints = ref(0); @@ -364,8 +396,8 @@ export const useCartsStore = defineStore("carts", () => { // 订单费用汇总(调用内部的 getAllGoodsList) const orderCostSummary = computed(() => { allGoods.value = getAllGoodsList(); - console.log(' allGoods.value+++++++++++++++++++++++++', allGoods.value); - console.log(' orderExtraConfig.value', orderExtraConfig.value); + console.log('allGoods.value+++++++++++++++++++++++++', allGoods.value); + console.log('orderExtraConfig.value', orderExtraConfig.value); const costSummary = OrderPriceCalculator.calculateOrderCostSummary( allGoods.value, dinnerType.value, @@ -614,8 +646,6 @@ export const useCartsStore = defineStore("carts", () => { giftList.value = []; cartOrder.value = {}; userPoints.value = 0; - - } interface GroupSnap { diff --git a/src/store/modules/limit.ts b/src/store/modules/limit.ts new file mode 100644 index 0000000..0e5d9c4 --- /dev/null +++ b/src/store/modules/limit.ts @@ -0,0 +1,345 @@ +import BigNumber from "bignumber.js"; + +import _ from "lodash"; + +export interface Goods { + productId: string | number; // 商品ID(唯一标识商品,用于优惠券/活动匹配,必选) + skuId: string | number; // 商品规格ID(唯一标识商品规格,如颜色/尺寸) + id: string | number; // 购物车ID(唯一标识购物车中的条目,如购物车项主键) + product_id: string | number; // 商品ID(唯一标识商品,用于优惠券/活动匹配,必选) + salePrice: number; // 商品原价(元) + number: number; // 商品数量 + product_type: string; // 商品类型 + is_temporary?: number; // 是否临时菜(默认false) + is_gift?: number; // 是否赠菜(默认false) + returnNum?: number; // 退货数量(历史订单用,默认0) + memberPrice: number; // 商品会员价(元,优先级:商品会员价 > 会员折扣) + discountSaleAmount?: number; // 商家改价后单价(元,优先级最高) + packFee?: number; // 单份打包费(元,默认0) + packNumber?: number; // 堂食打包数量(默认0) + skuData?: { + // SKU扩展数据(可选) + id: string | number; // SKU ID(唯一标识商品规格,如颜色/尺寸) + memberPrice?: number; // SKU会员价 + salePrice?: number; // SKU原价 + }; + discount_sale_amount: number; // 商家改价后单价(元,优先级最高) + [property: string]: any; +} + +export interface User { + isVip: number; // 是否会员 1是会员 + [property: string]: any; +} + +export interface ShopInfo { + isMemberPrice: number; // 是否开启会员价 1是开启 + [property: string]: any; +} + +export interface ThresholdFood { + id: string | number; // 商品ID + [property: string]: any; +} +export interface UseFood { + id: string | number; + [property: string]: any; +} +export interface Coupon { + id: string | number; + use: boolean; + type: number; + thresholdFoods: ThresholdFood[]; + useFoods: UseFood[]; + noUseRestrictions?: string; + discountShare: number; // 是否与折扣优惠同享 1是同享 + vipPriceShare: number; // 是否与会员优惠同享 1是同享 + otherCouponShare: number; // 是否与其他优惠券同享 1是同享 + fullAmount: number; // 使用门槛金额 + discountRate: number; // 折扣率(满减券:折扣金额/门槛金额,折扣券:折扣率) + maxDiscountAmount: number; // 最大折扣金额(满减券:折扣金额,折扣券:折扣金额) + discountNum: number; // 抵扣商品数量(商品券:抵扣商品数量,折扣券:0) + useRule: string; // 使用规则(price_asc:按商品单价升序,price_desc:按商品单价降序) +} + +export interface couponDiscount { + discountPrice: number; + hasDiscountGoodsArr: Goods[]; +} +export interface selCoupon extends Coupon { + discount?: couponDiscount; +} + +export interface couponCalcParams { + canDikouGoodsArr: Goods[]; + coupon: Coupon; + user: User; + shopInfo: ShopInfo; + selCoupon: selCoupon[]; + goodsOrderPrice: number; //商品订单总价 + isMemberPrice: number; // 是否开启会员价 1是开启 + limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined; +} + +//限时折扣配置 +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 CanDikouGoodsArrArgs { + canDikouGoodsArr: Goods[]; + selCoupon: selCoupon[]; + user: User; + shopInfo: ShopInfo; + limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined; +} + +/** + * 判断商品是否可以使用限时折扣 + * @param goods 商品对象 + * @param limitTimeDiscountRes 限时折扣配置 + * @param shopInfo 店铺信息 + * @param shopUserInfo 店铺用户信息 + * @param idKey 商品ID键名,默认"id" + * @returns + */ +export function canUseLimitTimeDiscount( + goods: Goods, + limitTimeDiscountRes: TimeLimitDiscountConfig | null | undefined, + shopInfo: ShopInfo, + shopUserInfo: User, + idKey = "id" +) { + shopInfo = shopInfo || {}; + shopUserInfo = shopUserInfo || {}; + 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 (goods.id == 727) { + console.log('goods', goods); + console.log('shopUserInfo', shopUserInfo); + } + + if (shopUserInfo.isVip != 1 || shopUserInfo.isMemberPrice != 1) { + return true; + } + + if ( + shopUserInfo.isVip == 1 && + shopUserInfo.isMemberPrice == 1 && + goods.memberPrice * 1 <= 0 + ) { + return true; + } + } + + return false; +} +export interface CanDikouGoodsArrArgs { + goods: Goods; + shopInfo: ShopInfo; + limitTimeDiscountRes: TimeLimitDiscountConfig | null | undefined; + shopUserInfo: User; + idKey?: string; +} +/** + * 返回商品显示价格 + * @params {*} args 参数对象 + * @params {*} args.goods 商品对象 + * @params {*} args.shopInfo 店铺信息 + * @params {*} args.limitTimeDiscountRes 限时折扣信息 + * @params {*} args.shopUserInfo 店铺用户信息 + * @returns + */ +export function returnPrice(args: CanDikouGoodsArrArgs) { + 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 { + // console.log('不是会员或者没有启用会员价',goods,limitTimeDiscountRes); + + //不是会员或者没有启用会员价 + 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?: Goods; +} +/** + * 返回限时折扣价格 + * @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 + */ +interface CanReturnMemberPriceArgs { + shopInfo?: ShopInfo; + shopUserInfo: User; +} +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: Goods) { + return goods.memberPrice || goods.salePrice; +} + +export const utils = { + returnPrice, + canUseLimitTimeDiscount, + returnLimitPrice, + canReturnMemberPrice, + returnMemberPrice, +}; + +export default utils; diff --git a/src/views/tool/Instead/components/goods-item.vue b/src/views/tool/Instead/components/goods-item.vue index 5ed0daf..6de1058 100644 --- a/src/views/tool/Instead/components/goods-item.vue +++ b/src/views/tool/Instead/components/goods-item.vue @@ -130,7 +130,7 @@ function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) { position: absolute; box-sizing: border-box; padding: 8px; - z-index: 99; + z-index: 2; inset: 0; display: flex; justify-content: center; diff --git a/src/views/tool/Instead/index.vue b/src/views/tool/Instead/index.vue index 0efe509..b64dc65 100644 --- a/src/views/tool/Instead/index.vue +++ b/src/views/tool/Instead/index.vue @@ -123,7 +123,6 @@ - @@ -148,7 +147,6 @@ -