优化更新

This commit is contained in:
gyq
2025-11-11 11:04:26 +08:00
parent 2432c53a73
commit 636fa4e033
32 changed files with 2280 additions and 704 deletions

View File

@@ -69,7 +69,7 @@
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-i18n": "^11.1.0", "vue-i18n": "^11.1.0",
"vue-router": "^4.5.0", "vue-router": "^4.5.0",
"ysk-utils": "^1.0.35" "ysk-utils": "^1.0.50"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.7.1", "@commitlint/cli": "^19.7.1",

View File

@@ -54,6 +54,21 @@ const API = {
data data
}); });
}, },
// 批量台桌配置
shopTabBatch(data: any) {
return request({
url: `${baseURL}/batch`,
method: "PUT",
data
});
},
// 获取清台配置信息
tableCurrentState() {
return request({
url: `${baseURL}/currentState`,
method: "GET"
});
},
} }
export default API; export default API;
/** /**

View File

@@ -0,0 +1,30 @@
import request from "@/utils/request";
import { Market_BaseUrl } from "@/api/config";
const API = {
// 获取当前可用限时折扣
getLimitTimeDiscount(params) {
return request({
url: `${Market_BaseUrl}/admin/limitTimeDiscount`,
method: "get",
params
});
},
// 满减活动配置
getDiscountActivity(params) {
return request({
url: `${Market_BaseUrl}/admin/discountActivity`,
method: "get",
params
});
},
// 根据用户id获取新客立减金额返回null代表不可用
getConsumeDiscount(params) {
return request({
url: `${Market_BaseUrl}/admin/consumeDiscount/getDiscountByUserId`,
method: "get",
params
});
},
}
export default API;

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.54 18.8H13.74C13.4 18.84 13.06 18.7 12.8 18.46C12.56 18.22 12.44 17.88 12.46 17.52V13.7C12.42 13.36 12.56 13.02 12.8 12.76C13.04 12.52 13.38 12.4 13.74 12.42H15V11.8C15 11.26 14.9 10.52 14.36 10.52H5.46003C4.92003 10.52 4.82003 11.26 4.82003 11.8V12.44H6.10003C6.44003 12.4 6.78003 12.54 7.04003 12.78C7.28003 13.02 7.40003 13.36 7.38003 13.72V17.54C7.42003 17.88 7.28003 18.22 7.04003 18.48C6.80003 18.72 6.46003 18.84 6.10003 18.82H2.30003C1.96003 18.86 1.62003 18.72 1.36003 18.48C1.12003 18.24 1.00003 17.9 1.02003 17.54V13.7C0.980025 13.36 1.12003 13.02 1.36003 12.76C1.60003 12.52 1.94003 12.4 2.30003 12.42H3.58003V10.52C3.58003 9.46 4.42003 9.24 5.48003 9.24H9.30003V7.34H10.58V9.24H14.4C15.48 9.24 16.3 9.44 16.3 10.52V12.42H17.58C17.92 12.38 18.26 12.52 18.52 12.76C18.76 13 18.88 13.34 18.86 13.7V17.52C18.9 17.86 18.76 18.2 18.52 18.46C18.24 18.7 17.9 18.82 17.54 18.8ZM11.82 7.36H8.02003C7.32003 7.36 6.74003 6.8 6.74003 6.08V2.28C6.74003 1.58 7.30003 1 8.02003 1H11.84C12.54 1 13.12 1.56 13.12 2.28V6.1C13.1 6.78 12.52 7.36 11.82 7.36ZM11.82 2.9C11.82 2.54 11.54 2.26 11.18 2.26H8.64003C8.28003 2.26 8.00003 2.54 8.00003 2.9V5.44C8.00003 5.8 8.28003 6.08 8.64003 6.08H11.18C11.54 6.08 11.82 5.8 11.82 5.44V2.9Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,12 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_334_1283)">
<path d="M10.3939 0.870157C10.147 0.740821 9.85303 0.776095 9.64139 0.964219C9.62963 0.964219 8.58319 1.82254 7.44268 2.70437C5.44386 4.23288 4.85597 4.49156 4.73839 4.53859H1.90476C1.893 4.53859 1.88125 4.53859 1.85773 4.53859H0.587889C0.270429 4.53859 0 4.80902 0 5.14999V14.8502C0 15.1794 0.258671 15.4616 0.587889 15.4616H5.2087C5.32628 15.5086 5.87889 15.7673 7.64256 17.2723C8.67725 18.1541 9.60611 19.0007 9.60611 19.0124C9.73545 19.13 9.91182 19.2006 10.0764 19.2006C10.1705 19.2006 10.2763 19.177 10.3704 19.1418C10.629 19.0242 10.7937 18.7538 10.7937 18.4716V1.54035C10.7937 1.24641 10.6408 0.999493 10.3939 0.870157ZM16.6138 9.9942C16.6138 8.11296 15.6379 6.46686 14.1681 5.58503C14.0976 5.54976 13.9447 5.47921 13.8389 5.47921C13.5215 5.47921 13.251 5.74964 13.251 6.09062C13.251 6.31401 13.3686 6.5139 13.545 6.61972C14.6737 7.28991 15.438 8.54799 15.438 9.9942C15.438 11.4639 14.6502 12.7455 13.4862 13.404C13.3216 13.5098 13.204 13.7097 13.204 13.9331C13.204 14.274 13.4627 14.5445 13.7919 14.5445C13.933 14.5445 14.0858 14.4622 14.0858 14.4622C15.5908 13.5803 16.6138 11.9107 16.6138 9.9942ZM15.8613 2.49273C15.7554 2.42219 15.6496 2.3634 15.5085 2.3634C15.1911 2.3634 14.9206 2.63383 14.9206 2.9748C14.9206 3.20996 15.0617 3.40984 15.2499 3.52742C17.3898 4.83253 18.836 7.24288 18.836 10.0177C18.836 11.4522 18.448 12.7808 17.7778 13.9213C17.6367 14.1565 17.672 14.4504 17.8601 14.6385C17.8601 14.6385 17.8601 14.6385 17.8718 14.6503C18.1423 14.909 18.5891 14.8619 18.7772 14.5327C19.5532 13.2158 20 11.6756 20 10.0177C20 6.80784 18.3304 4.00949 15.8613 2.49273ZM17.4721 15.4851C17.2604 15.2147 16.8489 15.1911 16.6138 15.4381C16.2022 15.8496 15.7554 16.2023 15.2616 16.4963C15.0735 16.6021 14.9089 16.802 14.9089 17.0489C14.9089 17.3899 15.1675 17.6603 15.4968 17.6603C15.6261 17.6603 15.7319 17.6132 15.8377 17.5427C16.4139 17.19 16.9547 16.7667 17.425 16.2846C17.6484 16.073 17.672 15.732 17.4721 15.4851C17.4838 15.4968 17.4721 15.4968 17.4721 15.4851Z" fill="#999999"/>
<path d="M4.83245 5.64366C4.17401 5.76124 2.92768 6.12573 2.10464 7.19569C1.96355 7.37206 1.99882 7.63073 2.17519 7.77182C2.17519 7.77182 2.17519 7.77182 2.18694 7.77182C2.36331 7.91292 2.61022 7.87764 2.75132 7.71303C3.20987 7.14866 3.76249 6.6078 4.93827 6.45495C5.13815 6.43143 5.27924 6.26682 5.27924 6.0787V6.01991C5.27924 5.78476 5.0676 5.60839 4.83245 5.64366Z" fill="#999999"/>
<path d="M2.01059 8.3011C1.77543 8.19528 1.505 8.31285 1.43446 8.54801C1.38743 8.73613 1.3404 8.92426 1.31688 9.1359C1.28161 9.37106 1.45797 9.5827 1.69313 9.5827H1.76368C1.96356 9.5827 2.12817 9.42984 2.13993 9.24172C2.15168 9.08887 2.1752 8.98305 2.22223 8.77141C2.28102 8.58328 2.18696 8.39516 2.01059 8.3011Z" fill="#999999"/>
</g>
<defs>
<clipPath id="clip0_334_1283">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -63,6 +63,16 @@ export const constantRoutes: RouteRecordRaw[] = [
keepAlive: true, keepAlive: true,
}, },
}, },
{
path: "work",
name: "workStatistics",
component: () => import("@/views/data/work.vue"),
meta: {
title: "交班记录",
affix: false,
keepAlive: true,
},
},
{ {
path: "401", path: "401",
component: () => import("@/views/error/401.vue"), component: () => import("@/views/error/401.vue"),

View File

@@ -3,6 +3,7 @@ import WebSocketManager, { type ApifoxModel, msgType } from "@/utils/websocket";
import orderApi from "@/api/order/order"; import orderApi from "@/api/order/order";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import productApi from "@/api/product/index"; import productApi from "@/api/product/index";
import limitTimeDiscountApi from '@/api/market/limitTimeDiscount.js'
import * as UTILS from "@/utils/coupon-utils.js"; import * as UTILS from "@/utils/coupon-utils.js";
import { BigNumber } from "bignumber.js"; import { BigNumber } from "bignumber.js";
import _ from "lodash"; import _ from "lodash";
@@ -15,6 +16,11 @@ import {
OrderExtraConfig, MerchantReductionConfig, MerchantReductionType, OrderExtraConfig, MerchantReductionConfig, MerchantReductionType,
GoodsType, FullReductionActivity GoodsType, FullReductionActivity
} from "@/utils/goods"; } from "@/utils/goods";
// import yskUtils from 'ysk-utils';
// const OrderPriceCalculator = yskUtils.OrderPriceCalculator
import { useUserStore } from "@/store/modules/user";
import { canUseLimitTimeDiscount, returnPrice } from '@/utils/order-utils'
const shopUser = useUserStoreHook(); const shopUser = useUserStoreHook();
export interface CartsState { export interface CartsState {
@@ -72,7 +78,9 @@ export const useCartsStore = defineStore("carts", () => {
vipPriceShare: !!item.activityInfo.vipPriceShare vipPriceShare: !!item.activityInfo.vipPriceShare
} }
: undefined, : undefined,
skuData skuData,
is_time_discount: item.isTimeDiscount,
isTimeDiscount: item.isTimeDiscount
}; };
}; };
@@ -85,10 +93,16 @@ export const useCartsStore = defineStore("carts", () => {
const oldOrderGoods = Object.values(oldOrder.value.detailMap || {}) const oldOrderGoods = Object.values(oldOrder.value.detailMap || {})
.flat() .flat()
.map(convertToBaseCartItem); .map(convertToBaseCartItem);
// console.log('list=====================================', list.value);
// console.log('giftList=====================================', giftList.value);
// console.log('oldOrder.value.detailMap=====================================', oldOrder.value.detailMap);
console.log('getAllGoodsList.[]===================', [...currentGoods, ...giftGoods, ...oldOrderGoods])
return [...currentGoods, ...giftGoods, ...oldOrderGoods]; return [...currentGoods, ...giftGoods, ...oldOrderGoods];
}; };
const limitDiscountRes = ref(null)
// ------------------------------ 2. Store 内部原有响应式变量 ------------------------------ // ------------------------------ 2. Store 内部原有响应式变量 ------------------------------
// 选择用户 // 选择用户
const vipUser = ref<{ id?: string | number, isVip?: boolean }>({}); const vipUser = ref<{ id?: string | number, isVip?: boolean }>({});
@@ -119,16 +133,78 @@ export const useCartsStore = defineStore("carts", () => {
originAmount: 0 originAmount: 0
}); });
/**
* 异步查询链接/任务是否完成,完成后设置 isLinkFinshed.value = true
* @param {number} [interval=1000] - 查询间隔时间(毫秒),默认 1 秒
* @returns {Promise<void>} - 完成后 resolve可通过 await 等待后续操作
*/
async function checkLinkFinished(interval = 100) {
return new Promise((resolve) => {
let elapsedTime = 0; // 已消耗时间
const checkTimer = setInterval(async () => {
// 2. 实际查询逻辑(这里替换为你的真实查询逻辑,比如调用接口)
try {
if (isLinkFinshed.value == true) {
clearInterval(checkTimer);
resolve();
} else {
console.log('链接未完成,继续查询...');
elapsedTime += interval;
}
} catch (error) {
console.error('查询出错,继续重试:', error);
elapsedTime += interval;
}
}, interval);
});
}
// 代客下单页面商品缓存 // 代客下单页面商品缓存
const goods = useStorage<any[]>("Instead_goods", []); const goods = useStorage<any[]>("Instead_goods", []);
async function getGoods(query: any) { async function getGoods(query: any) {
// 获取当前店铺可用的限时折扣
limitDiscountRes.value = await limitTimeDiscountApi.getLimitTimeDiscount({
shopId: localStorage.getItem("shopId")
})
// 获取满减活动
fullReductionActivities.value = await limitTimeDiscountApi.getDiscountActivity({
shopId: localStorage.getItem("shopId")
})
if (limitDiscountRes.value !== null) {
checkLinkFinished().then(() => {
console.log('给ws发送限时折扣信息');
WebSocketManager.sendMessage({
type: "shopping",
operate_type: "time_discount_save",
table_code: table_code.value,
shop_id: localStorage.getItem("shopId"),
data: limitDiscountRes.value,
});
})
}
console.log('代客下单页面商品缓存.获取当前店铺可用的限时折扣', limitDiscountRes.value);
const res = await productApi.getPage({ const res = await productApi.getPage({
page: 1, page: 1,
size: 999, size: 999,
status: "on_sale", status: "on_sale",
...query, ...query,
}); });
goods.value = res.records;
const shopInfo = JSON.parse(localStorage.getItem('userInfo'))
goods.value = res.records.map(item => {
return {
...item,
isLimitDiscount: limitDiscountRes.value !== null && canUseLimitTimeDiscount(item, limitDiscountRes.value, shopInfo, shopUser.userInfo),
limitDiscountPrice: limitDiscountRes.value !== null && returnPrice({ goods: { ...item, memberPrice: item.lowMemberPrice, salePrice: item.lowPrice }, shopInfo: shopInfo, limitTimeDiscountRes: limitDiscountRes.value, shopUserInfo: shopUser.userInfo, idKey: 'id' })
}
});
console.log('代客下单页面商品缓存.goods.value', goods.value);
setGoodsMap(goods.value); setGoodsMap(goods.value);
} }
@@ -215,35 +291,15 @@ export const useCartsStore = defineStore("carts", () => {
pointsPerYuan: 100, pointsPerYuan: 100,
maxDeductionAmount: Infinity maxDeductionAmount: Infinity
}) })
const fullReductionActivities = ref([])
//使用积分数量 //使用积分数量
const userPoints = ref(0); const userPoints = ref(0);
const testFullReductionActivity: FullReductionActivity = {
"id": 231, // 新客立减金额
"shopId": 26, const newUserDiscount = ref(0)
"status": 2, // 2=进行中
"sort": 10,
"createTime": "2025-10-14 13:56:07",
"updateTime": "2025-10-14 14:41:02", // 最新修改
"validStartTime": "2025-10-14",
"validEndTime": "2025-12-14",
"useType": "dine,pickup,deliv,express", // 支持所有就餐类型
"useDays": "周一,周二,周三,周四,周五,周六,周日", // 全周期
"useTimeType": "all", // 全时段
"useStartTime": '',
"useEndTime": '',
"couponShare": 0, // 与优惠券不同享
"discountShare": 0, // 与限时折扣不同享
"vipPriceShare": 0, // 与会员价不同享
"pointsShare": 0, // 与积分不同享
"thresholds": [ // 多门槛此处1个
{
"activityId": 231,
"fullAmount": 1, // 满1元
"discountAmount": 10 // 减10元
}
],
"isDel": false,
};
// 订单额外配置(现在依赖响应式的 merchantReduction // 订单额外配置(现在依赖响应式的 merchantReduction
const orderExtraConfig = computed<OrderExtraConfig>(() => ({ const orderExtraConfig = computed<OrderExtraConfig>(() => ({
// 引用扩展后的商家减免配置 // 引用扩展后的商家减免配置
@@ -255,9 +311,11 @@ export const useCartsStore = defineStore("carts", () => {
userPoints: userPoints.value, userPoints: userPoints.value,
isMember: useVipPrice.value, isMember: useVipPrice.value,
memberDiscountRate: shopUser.userInfo.memberDiscountRate || 1, memberDiscountRate: shopUser.userInfo.memberDiscountRate || 1,
fullReductionActivities: [testFullReductionActivity], fullReductionActivities: fullReductionActivities.value,
currentDinnerType: dinnerType.value currentDinnerType: dinnerType.value,
limitTimeDiscount: limitDiscountRes.value,
shopUserInfo: shopUser.userInfo,
newUserDiscount: newUserDiscount.value
})); }));
// 营销活动列表 // 营销活动列表
@@ -305,7 +363,7 @@ export const useCartsStore = defineStore("carts", () => {
// 订单费用汇总(调用内部的 getAllGoodsList // 订单费用汇总(调用内部的 getAllGoodsList
const orderCostSummary = computed(() => { const orderCostSummary = computed(() => {
allGoods.value = getAllGoodsList(); allGoods.value = getAllGoodsList();
console.log(' allGoods.value', allGoods.value); console.log(' allGoods.value+++++++++++++++++++++++++', allGoods.value);
console.log(' orderExtraConfig.value', orderExtraConfig.value); console.log(' orderExtraConfig.value', orderExtraConfig.value);
const costSummary = OrderPriceCalculator.calculateOrderCostSummary( const costSummary = OrderPriceCalculator.calculateOrderCostSummary(
allGoods.value, allGoods.value,
@@ -316,7 +374,7 @@ export const useCartsStore = defineStore("carts", () => {
cartOrder.value, cartOrder.value,
new Date() new Date()
); );
console.log('costSummary', costSummary); console.log('costSummary----------------------------', costSummary);
return costSummary; return costSummary;
}); });
@@ -464,10 +522,19 @@ export const useCartsStore = defineStore("carts", () => {
} }
function add(data: any) { function add(data: any) {
goods.value.map(item => {
if (item.id == data.product_id) {
data.is_time_discount = item.isLimitDiscount ? 1 : 0
}
})
const msg = { ...basic_msg, ...data }; const msg = { ...basic_msg, ...data };
const hasCart = list.value.find((cartItem) => { const hasCart = list.value.find((cartItem) => {
return cartItem.product_id == msg.product_id && cartItem.sku_id == msg.sku_id; return cartItem.product_id == msg.product_id && cartItem.sku_id == msg.sku_id;
}); });
console.log('carts.add===', data);
if (hasCart) { if (hasCart) {
update({ ...hasCart, ...msg, number: hasCart.number * 1 + msg.number * 1 }); update({ ...hasCart, ...msg, number: hasCart.number * 1 + msg.number * 1 });
} else { } else {
@@ -566,6 +633,7 @@ export const useCartsStore = defineStore("carts", () => {
if (res) setOldOrder(res); if (res) setOldOrder(res);
} }
// 购物车信息补全
function getProductDetails(v: { product_id: string, sku_id: string }) { function getProductDetails(v: { product_id: string, sku_id: string }) {
const goods = goodsMap[v.product_id]; const goods = goodsMap[v.product_id];
console.log('getProductDetails', goods) console.log('getProductDetails', goods)
@@ -579,6 +647,7 @@ export const useCartsStore = defineStore("carts", () => {
if (skuData) { if (skuData) {
return { return {
limitDiscountPrice: goods.limitDiscountPrice,
salePrice: skuData.salePrice || 0, salePrice: skuData.salePrice || 0,
memberPrice: skuData.memberPrice || 0, memberPrice: skuData.memberPrice || 0,
coverImg: goods.coverImg, coverImg: goods.coverImg,
@@ -613,10 +682,13 @@ export const useCartsStore = defineStore("carts", () => {
product_name: v.productName, product_name: v.productName,
sku_name: v.skuName, sku_name: v.skuName,
sku_id: v.skuId, sku_id: v.skuId,
product_type: v.productType product_type: v.productType,
is_time_discount: v.isTimeDiscount
}; };
}); });
} }
console.log('returnDetailMap.newData================', newData);
return newData; return newData;
} }
@@ -645,8 +717,10 @@ export const useCartsStore = defineStore("carts", () => {
function concocatSocket(initParams = $initParams) { function concocatSocket(initParams = $initParams) {
WebSocketManager.subscribeToTopic(initParams, (msg) => { WebSocketManager.subscribeToTopic(initParams, (msg) => {
console.log("收到消息:", msg); console.log("收到消息:", msg);
if (msg.hasOwnProperty('status') && msg.status !== 1) { if (!msg.status) {
return ElMessage.error(msg.message || '操作失败'); if (msg.hasOwnProperty('status') && msg.status !== 1) {
return ElMessage.error(msg.message || '操作失败');
}
} }
if (msg?.data) { if (msg?.data) {
if (Array.isArray(msg.data) && msg.data.length && msg.data[0].table_code) { if (Array.isArray(msg.data) && msg.data.length && msg.data[0].table_code) {
@@ -834,11 +908,14 @@ export const useCartsStore = defineStore("carts", () => {
clearMerchantReduction, clearMerchantReduction,
seatFeeConfig, seatFeeConfig,
pointDeductionRule, pointDeductionRule,
// 新客立减金额
newUserDiscount,
//使用积分数量 //使用积分数量
userPoints, userPoints,
coupons, coupons,
setCoupons, setCoupons,
payParamsInit payParamsInit,
limitDiscountRes
}; };
}); });

View File

@@ -5,19 +5,45 @@ import _ from "lodash";
* 返回商品单价 * 返回商品单价
* @param goods 商品 * @param goods 商品
* @param user 用户信息 * @param user 用户信息
* @param {Object} shopInfo * @param {Object} shopInfo 店铺信息
* @param {boolean} limitTimeDiscount - 限时折扣
*/ */
export function returnGoodsPrice(goods, user, shopInfo) { export function returnGoodsPrice(goods, user, shopInfo, limitTimeDiscount) {
if (!goods) { if (!goods) {
return 0; return 0;
} }
//是否可以使用会员价
const canUseVipPrice =
user && user.isVip && user.isMemberPrice && goods.memberPrice * 1 > 0;
// 商家改价
if (goods.discount_sale_amount * 1 > 0) { if (goods.discount_sale_amount * 1 > 0) {
return goods.discount_sale_amount;
}
if (shopInfo && !shopInfo.isMemberPrice) {
return goods.salePrice; return goods.salePrice;
} }
if (user.isVip && goods.memberPrice * 1 <= goods.salePrice * 1 && goods.memberPrice * 1 > 0) { // 限时折扣
if (limitTimeDiscount && limitTimeDiscount.id) {
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.memberPrice;
} }
return goods.salePrice; return goods.salePrice;
@@ -63,7 +89,9 @@ export function returnCoupType(coupon) {
* @param selCoupon 已选择的优惠券列表 * @param selCoupon 已选择的优惠券列表
* @param user 用户信息 * @param user 用户信息
*/ */
export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) { export function returnCanDikouGoodsArr(args) {
const { canDikouGoodsArr, selCoupon, user, shopInfo, limitTimeDiscount } =
args;
const types = [2, 4, 6]; const types = [2, 4, 6];
// 收集已抵扣商品并关联对应的优惠券类型 // 收集已抵扣商品并关联对应的优惠券类型
const goodsCouponGoods = selCoupon const goodsCouponGoods = selCoupon
@@ -92,11 +120,60 @@ export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
} }
return v; return v;
}) })
.filter((v) => v.num > 0); // 过滤掉数量<=0的商品 .filter((v) => {
const canUseNum = v.num - (v.returnNum || 0);
if (canUseNum <= 0 || v.is_temporary == 1 || v.is_gift == 1) {
return false;
}
return true;
}); // 过滤掉数量<=0的商品,赠菜,临时菜
return arr; return arr;
} }
/**
* 返回商品是否享用了会员价/会员折扣
* @param {*} goods
*/
function returnGoodsIsUseVipPrice(shopInfo, user, goods) {
if (goods.is_time_discount) {
return false;
}
if (shopInfo.isMemberPrice != 1 || user.isVip != 1) {
return false;
}
if (shopInfo.isMemberPrice == 1 && user.isVip == 1) {
if (goods.memberPrice <= 0) {
return false;
}
return true;
}
}
/**
* 返回可以计算抵扣金额的商品列表
*/
function returnCanCalcGoodsList(canCalcGoodsArr, coupon, shopInfo, user) {
return canCalcGoodsArr.filter((goods) => {
console.log("goods");
console.log(goods);
if (
!coupon.discountShare &&
(goods.is_time_discount || goods.isTimeDiscount)
) {
return false;
}
if (
!coupon.vipPriceShare &&
returnGoodsIsUseVipPrice(shopInfo, user, goods)
) {
return false;
}
return true;
});
}
/** /**
* 判断优惠券是否可使用,并返回不可用原因 * 判断优惠券是否可使用,并返回不可用原因
* *
@@ -111,10 +188,20 @@ export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
* @param {Object} args.user - 用户信息对象 * @param {Object} args.user - 用户信息对象
* @param {Object} args.selCoupon - 已经选择的优惠券信息对象 * @param {Object} args.selCoupon - 已经选择的优惠券信息对象
* @param {Object} args.shopInfo * @param {Object} args.shopInfo
* @param {boolean} args.limitTimeDiscount - 限时折扣
* @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因 * @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因
*/ */
export function returnCouponCanUse(args) { export function returnCouponCanUse(args) {
let { canDikouGoodsArr, coupon, goodsOrderPrice, user, selCoupon, shopInfo } = args; let {
canDikouGoodsArr,
coupon,
goodsOrderPrice,
user,
selCoupon,
shopInfo,
isMemberPrice,
limitTimeDiscount,
} = args;
// 优惠券未启用 // 优惠券未启用
if (!coupon.use) { if (!coupon.use) {
return { return {
@@ -122,10 +209,27 @@ export function returnCouponCanUse(args) {
reason: coupon.noUseRestrictions || "不在可用时间段内", reason: coupon.noUseRestrictions || "不在可用时间段内",
}; };
} }
if (
limitTimeDiscount &&
limitTimeDiscount.id &&
limitTimeDiscount.foodType == 1 &&
!coupon.discountShare
) {
return {
canUse: false,
reason: coupon.noUseRestrictions || "不可与限时折扣同享",
};
}
// 计算门槛金额 // 计算门槛金额
let fullAmount = goodsOrderPrice; let fullAmount = goodsOrderPrice;
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user, shopInfo); canDikouGoodsArr = returnCanDikouGoodsArr({
canDikouGoodsArr,
selCoupon,
user,
shopInfo,
limitTimeDiscount,
});
//优惠券指定门槛商品列表 //优惠券指定门槛商品列表
let canCalcGoodsArr = [...canDikouGoodsArr]; let canCalcGoodsArr = [...canDikouGoodsArr];
//部分商品参与门槛计算 //部分商品参与门槛计算
@@ -133,10 +237,20 @@ export function returnCouponCanUse(args) {
canCalcGoodsArr = canDikouGoodsArr.filter((v) => { canCalcGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.thresholdFoods.find((food) => food.id == v.productId); return coupon.thresholdFoods.find((food) => food.id == v.productId);
}); });
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return pre + returnGoodsPrice(cur, user, shopInfo) * cur.num;
}, 0);
} }
canCalcGoodsArr = returnCanCalcGoodsList(
canCalcGoodsArr,
coupon,
shopInfo,
user
);
console.log("canCalcGoodsArr");
console.log(canCalcGoodsArr);
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return (
pre + returnGoodsPrice(cur, user, shopInfo, limitTimeDiscount) * cur.num
);
}, 0);
// 是否全部商品可用 // 是否全部商品可用
const isDikouAll = coupon.useFoods.length === 0; const isDikouAll = coupon.useFoods.length === 0;
@@ -147,12 +261,12 @@ export function returnCouponCanUse(args) {
return coupon.useFoods.find((food) => food.id == v.productId); return coupon.useFoods.find((food) => food.id == v.productId);
}); });
} }
if (user.isVip && !coupon.vipPriceShare) { // if (user.isVip && !coupon.vipPriceShare) {
return { // return {
canUse: false, // canUse: false,
reason: "非会员可用", // reason: "非会员可用",
}; // };
} // }
if (selCoupon.length > 0 && !selCoupon[0].otherCouponShare) { if (selCoupon.length > 0 && !selCoupon[0].otherCouponShare) {
return { return {
canUse: false, canUse: false,
@@ -183,13 +297,6 @@ export function returnCouponCanUse(args) {
} }
// 商品兑换券,第二件半价和买一送一判断是否有可用商品 // 商品兑换券,第二件半价和买一送一判断是否有可用商品
if ([2, 4, 5].includes(coupon.type)) { if ([2, 4, 5].includes(coupon.type)) {
if (coupon.type == 2 && fullAmount < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
// 没有符合条件的商品 // 没有符合条件的商品
if (isDikouAll && canDikouGoodsArr.length === 0) { if (isDikouAll && canDikouGoodsArr.length === 0) {
return { return {
@@ -203,6 +310,20 @@ export function returnCouponCanUse(args) {
reason: "没有符合条件的商品", reason: "没有符合条件的商品",
}; };
} }
if (coupon.type == 2) {
if (canCalcGoodsArr.length <= 0) {
return {
canUse: false,
reason: "没有符合计算门槛条件的商品",
};
}
if (fullAmount < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
} }
//商品兑换券是否达到门槛金额 //商品兑换券是否达到门槛金额
if (coupon.type == 2 && goodsOrderPrice < coupon.fullAmount) { if (coupon.type == 2 && goodsOrderPrice < coupon.fullAmount) {
@@ -258,11 +379,19 @@ export function returnCouponCanUse(args) {
* @param discountNum 抵扣数量 * @param discountNum 抵扣数量
* @param user 用户信息 * @param user 用户信息
* @param {Object} shopInfo 店铺信息 * @param {Object} shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/ */
export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, shopInfo) { export function calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
) {
let hasCountNum = 0; let hasCountNum = 0;
let discountPrice = 0; let discountPrice = 0;
let hasDiscountGoodsArr = []; let hasDiscountGoodsArr = [];
for (let i = 0; i < discountGoodsArr.length; i++) { for (let i = 0; i < discountGoodsArr.length; i++) {
if (hasCountNum >= discountNum) { if (hasCountNum >= discountNum) {
break; break;
@@ -270,7 +399,14 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, s
const goods = discountGoodsArr[i]; const goods = discountGoodsArr[i];
const shengyuNum = discountNum - hasCountNum; const shengyuNum = discountNum - hasCountNum;
const num = Math.min(goods.num, shengyuNum); const num = Math.min(goods.num, shengyuNum);
discountPrice += returnGoodsPrice(goods, user, shopInfo) * num; const realPrice = returnGoodsPrice(
goods,
user,
shopInfo,
limitTimeDiscount
);
discountPrice += realPrice * num;
hasCountNum += num; hasCountNum += num;
hasDiscountGoodsArr.push({ hasDiscountGoodsArr.push({
@@ -293,22 +429,62 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, s
* @param goodsOrderPrice 商品订单金额 * @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表 * @param selCoupon 已选择的优惠券列表
* @param shopInfo 店铺信息 * @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/ */
export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoupon, shopInfo) { export function returnCouponDiscount(
arr = returnCanDikouGoods(arr, user, shopInfo); arr,
const canDikouGoodsArr = returnCanDikouGoodsArr(arr, selCoupon, user); coupon,
user,
goodsOrderPrice,
selCoupon,
shopInfo,
limitTimeDiscount
) {
arr = returnCanDikouGoods(arr, user, shopInfo,limitTimeDiscount);
const canDikouGoodsArr = returnCanDikouGoodsArr({
canDikouGoodsArr: arr,
selCoupon,
user,
shopInfo,
limitTimeDiscount,
});
if (coupon.type == 2) { if (coupon.type == 2) {
return returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shopInfo); return returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
} }
if (coupon.type == 6) { if (coupon.type == 6) {
const result = returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopInfo); const result = returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
return result; return result;
} }
if (coupon.type == 4) { if (coupon.type == 4) {
return returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo); return returnSecoendDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
} }
if (coupon.type == 3) { if (coupon.type == 3) {
return returnCouponZhekouDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon); return returnCouponZhekouDiscount(
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon,
limitTimeDiscount
);
} }
} }
@@ -319,6 +495,7 @@ export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoup
* @param user 用户信息 * @param user 用户信息
* @param goodsOrderPrice 商品订单金额 * @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表 * @param selCoupon 已选择的优惠券列表
* @param limitTimeDiscount 限时折扣
* *
*/ */
export function returnCouponZhekouDiscount( export function returnCouponZhekouDiscount(
@@ -326,7 +503,8 @@ export function returnCouponZhekouDiscount(
coupon, coupon,
user, user,
goodsOrderPrice, goodsOrderPrice,
selCoupon selCoupon,
limitTimeDiscount
) { ) {
const { discountRate, maxDiscountAmount } = coupon; const { discountRate, maxDiscountAmount } = coupon;
@@ -334,14 +512,20 @@ export function returnCouponZhekouDiscount(
const goodsCouponDiscount = selCoupon const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2) .filter((v) => v.type == 2)
.reduce((prve, cur) => { .reduce((prve, cur) => {
return new BigNumber(prve).plus(new BigNumber(cur.discount.discountPrice)); return new BigNumber(prve).plus(
new BigNumber(cur.discount.discountPrice)
);
}, new BigNumber(0)); }, new BigNumber(0));
// 将商品订单价格转换为BigNumber并减去优惠券折扣 // 将商品订单价格转换为BigNumber并减去优惠券折扣
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(goodsCouponDiscount); const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(
goodsCouponDiscount
);
// 计算优惠比例:(100 - 折扣率) / 100 // 计算优惠比例:(100 - 折扣率) / 100
const discountAmountRatio = new BigNumber(100).minus(discountRate).dividedBy(100); const discountAmountRatio = new BigNumber(100)
.minus(discountRate)
.dividedBy(100);
// 计算折扣金额:调整后的商品订单金额 × 优惠比例 // 计算折扣金额:调整后的商品订单金额 × 优惠比例
let discountPrice = adjustedGoodsOrderPrice let discountPrice = adjustedGoodsOrderPrice
@@ -351,7 +535,8 @@ export function returnCouponZhekouDiscount(
// 应用最大折扣金额限制 // 应用最大折扣金额限制
if (maxDiscountAmount !== 0) { if (maxDiscountAmount !== 0) {
discountPrice = discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice; discountPrice =
discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
} }
return { return {
@@ -366,11 +551,20 @@ export function returnCouponZhekouDiscount(
* @param coupon 优惠券 * @param coupon 优惠券
* @param user 用户信息 * @param user 用户信息
* @param shopInfo 店铺信息 * @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/ */
export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shopInfo) { export function returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, discountNum, useRule } = coupon; const { useFoods, discountNum, useRule } = coupon;
//抵扣商品数组 //抵扣商品数组
let discountGoodsArr = []; let discountGoodsArr = [];
//抵扣全部商品 //抵扣全部商品
if (useFoods.length === 0) { if (useFoods.length === 0) {
if (useRule == "price_asc") { if (useRule == "price_asc") {
@@ -389,7 +583,15 @@ export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shop
discountGoodsArr = discountSelGoodsArr.slice(0, discountNum); discountGoodsArr = discountSelGoodsArr.slice(0, discountNum);
} }
} }
const result = calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, shopInfo);
const result = calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
);
return result; return result;
} }
@@ -399,8 +601,15 @@ export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shop
* @param coupon 优惠券 * @param coupon 优惠券
* @param user 用户信息 * @param user 用户信息
* @param shopInfo 店铺信息 * @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/ */
function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopInfo) { function returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, useRule } = coupon; const { useFoods, useRule } = coupon;
//抵扣商品 //抵扣商品
let discountGoods = undefined; let discountGoods = undefined;
@@ -415,7 +624,9 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopI
} }
} else { } else {
//符合抵扣条件的商品 //符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId)); const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") { if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1]; discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else { } else {
@@ -424,9 +635,13 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopI
} }
let discountPrice = 0; let discountPrice = 0;
let hasDiscountGoodsArr = []; let hasDiscountGoodsArr = [];
console.log("returnCouponBuyOneGiveOneDiscount:discountGoods", discountGoods);
if (discountGoods) { if (discountGoods) {
discountPrice = returnGoodsPrice(discountGoods, user, shopInfo); discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods]; hasDiscountGoodsArr = [discountGoods];
} }
return { return {
@@ -441,8 +656,15 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopI
* @param coupon 优惠券 * @param coupon 优惠券
* @param user 用户信息 * @param user 用户信息
* @param shopInfo 店铺信息 * @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/ */
function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) { function returnSecoendDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, useRule } = coupon; const { useFoods, useRule } = coupon;
//抵扣商品 //抵扣商品
let discountGoods = undefined; let discountGoods = undefined;
@@ -457,7 +679,9 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
} }
} else { } else {
//符合抵扣条件的商品 //符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId)); const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") { if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1]; discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else { } else {
@@ -467,12 +691,20 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
let discountPrice = 0; let discountPrice = 0;
let hasDiscountGoodsArr = []; let hasDiscountGoodsArr = [];
if (discountGoods) { if (discountGoods) {
discountPrice = returnGoodsPrice(discountGoods, user, shopInfo); discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods]; hasDiscountGoodsArr = [discountGoods];
} }
//返回半价价格 //返回半价价格
return { return {
discountPrice: discountPrice <= 0 ? 0 : new BigNumber(discountPrice).dividedBy(2).toNumber(), discountPrice:
discountPrice <= 0
? 0
: new BigNumber(discountPrice).dividedBy(2).toNumber(),
hasDiscountGoodsArr, hasDiscountGoodsArr,
}; };
} }
@@ -482,8 +714,9 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
* @param arr 商品列表 * @param arr 商品列表
* @param user 用户信息 * @param user 用户信息
* @param shopInfo 店铺信息 * @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/ */
export function returnCanDikouGoods(arr, user, shopInfo) { export function returnCanDikouGoods(arr, user, shopInfo, limitTimeDiscount) {
const result = arr const result = arr
.filter((v) => { .filter((v) => {
return v.is_temporary != 1 && v.is_gift != 1; return v.is_temporary != 1 && v.is_gift != 1;
@@ -492,7 +725,10 @@ export function returnCanDikouGoods(arr, user, shopInfo) {
return v.num > 0; return v.num > 0;
}) })
.sort((a, b) => { .sort((a, b) => {
return returnGoodsPrice(b, user, shopInfo) - returnGoodsPrice(a, user, shopInfo); return (
returnGoodsPrice(b, user, shopInfo, limitTimeDiscount) -
returnGoodsPrice(a, user, shopInfo, limitTimeDiscount)
);
}); });
return result; return result;
} }

734
src/utils/goods-utils.js Normal file
View File

@@ -0,0 +1,734 @@
import { BigNumber } from "bignumber.js";
import _ from "lodash";
/**
* 返回商品单价
* @param goods 商品
* @param user 用户信息
* @param {Object} shopInfo 店铺信息
* @param {boolean} limitTimeDiscount - 限时折扣
*/
export function returnGoodsPrice(goods, user, shopInfo, limitTimeDiscount) {
if (!goods) {
return 0;
}
//是否可以使用会员价
const canUseVipPrice =
user && user.isVip && user.isMemberPrice && goods.memberPrice * 1 > 0;
// 商家改价
if (goods.discount_sale_amount * 1 > 0) {
return goods.salePrice;
}
// 限时折扣
if (limitTimeDiscount && limitTimeDiscount.id) {
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) {
let map = {};
arr.forEach((v) => {
const key = v.productId + "_" + v.skuId;
if (!map[key]) {
map[key] = [];
}
map[key].push(v);
});
return map;
}
/**
* 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
* @param coupon
*/
export function returnCoupType(coupon) {
const couponTypes = {
1: "满减券",
2: "商品券",
3: "折扣券",
4: "第二件半价券",
5: "消费送券",
6: "买一送一券",
7: "固定价格券",
8: "免配送费券",
};
return couponTypes[coupon.type] || "未知类型";
}
/**
* 返回商品券抵扣后的商品列表
* @param canDikouGoodsArr 可抵扣商品列表
* @param selCoupon 已选择的优惠券列表
* @param user 用户信息
*/
export function returnCanDikouGoodsArr(args) {
const { canDikouGoodsArr, selCoupon, user, shopInfo, limitTimeDiscount } =
args;
const types = [2, 4, 6];
// 收集已抵扣商品并关联对应的优惠券类型
const goodsCouponGoods = selCoupon
.filter((v) => types.includes(v.type))
.reduce((prev, cur) => {
// 给每个抵扣商品添加所属优惠券类型
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)) {
// 类型4第二件半价或6买一送一数量减2
v.num -= 2;
} else {
// 其他类型如类型2商品券按原逻辑扣减对应数量
v.num -= findCart.num;
}
}
return v;
})
.filter((v) => {
const canUseNum = v.num - (v.returnNum || 0);
if (canUseNum <= 0 || v.is_temporary == 1 || v.is_gift == 1) {
return false;
}
return true;
}); // 过滤掉数量<=0的商品,赠菜,临时菜
return arr;
}
/**
* 返回商品是否享用了会员价/会员折扣
* @param {*} goods
*/
function returnGoodsIsUseVipPrice(shopInfo, user, goods) {
if (goods.is_time_discount) {
return false;
}
if (shopInfo.isMemberPrice != 1 || user.isVip != 1) {
return false;
}
if (shopInfo.isMemberPrice == 1 && user.isVip == 1) {
if (goods.memberPrice <= 0) {
return false;
}
return true;
}
}
/**
* 返回可以计算抵扣金额的商品列表
*/
function returnCanCalcGoodsList(canCalcGoodsArr, coupon, shopInfo, user) {
return canCalcGoodsArr.filter((goods) => {
console.log("goods");
console.log(goods);
if (
!coupon.discountShare &&
(goods.is_time_discount || goods.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) {
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({
canDikouGoodsArr,
selCoupon,
user,
shopInfo,
limitTimeDiscount,
});
//优惠券指定门槛商品列表
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
);
console.log("canCalcGoodsArr");
console.log(canCalcGoodsArr);
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return (
pre + returnGoodsPrice(cur, user, shopInfo, limitTimeDiscount) * cur.num
);
}, 0);
// 是否全部商品可用
const isDikouAll = coupon.useFoods.length === 0;
// 订单可用商品列表
let canUseGoodsArr = [];
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) {
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) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
}
//商品兑换券是否达到门槛金额
if (coupon.type == 2 && goodsOrderPrice < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
// 买一送一券特殊验证
if (coupon.type === 6) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => v.num >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => v.num >= 2);
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
// 第二件半价券特殊验证
if (coupon.type === 4) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => v.num >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => v.num >= 2);
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
// 所有条件都满足
return {
canUse: true,
reason: "",
};
}
/**
* 计算抵扣商品金额
* @param discountGoodsArr 可抵扣商品列表
* @param discountNum 抵扣数量
* @param user 用户信息
* @param {Object} shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
) {
let hasCountNum = 0;
let discountPrice = 0;
let hasDiscountGoodsArr = [];
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, shengyuNum);
const realPrice = returnGoodsPrice(
goods,
user,
shopInfo,
limitTimeDiscount
);
discountPrice += realPrice * num;
hasCountNum += num;
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,
coupon,
user,
goodsOrderPrice,
selCoupon,
shopInfo,
limitTimeDiscount
) {
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,
coupon,
user,
goodsOrderPrice,
selCoupon,
limitTimeDiscount
) {
const { discountRate, maxDiscountAmount } = coupon;
// 计算商品优惠券折扣总和使用BigNumber避免精度问题
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return new BigNumber(prve).plus(
new BigNumber(cur.discount.discountPrice)
);
}, new BigNumber(0));
// 将商品订单价格转换为BigNumber并减去优惠券折扣
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(
goodsCouponDiscount
);
// 计算优惠比例:(100 - 折扣率) / 100
const discountAmountRatio = new BigNumber(100)
.minus(discountRate)
.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 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, discountNum, useRule } = coupon;
//抵扣商品数组
let discountGoodsArr = [];
//抵扣全部商品
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 店铺信息
* @param limitTimeDiscount 限时折扣
*/
function returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
//符合买一送一条件的商品
const canUseGoods = canDikouGoodsArr.filter((v) => v.num >= 2);
//抵扣全部商品
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 = [];
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 店铺信息
* @param limitTimeDiscount 限时折扣
*/
function returnSecoendDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
//符合条件的商品
const canUseGoods = canDikouGoodsArr.filter((v) => v.num >= 2);
//抵扣全部商品
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 = [];
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, user, shopInfo, limitTimeDiscount) {
const result = arr
.filter((v) => {
return v.is_temporary != 1 && v.is_gift != 1;
})
.filter((v) => {
return v.num > 0;
})
.sort((a, b) => {
return (
returnGoodsPrice(b, user, shopInfo, limitTimeDiscount) -
returnGoodsPrice(a, user, shopInfo, limitTimeDiscount)
);
});
return result;
}

View File

@@ -234,6 +234,46 @@ export interface FreeDineConfig {
useShopType?: string; //all 全部 part部分 useShopType?: string; //all 全部 part部分
shopIdList?: number[]; //可用门店id 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;
}
//用户信息
interface ShopUserInfo {
isVip: number | null; //是否会员
discount: number | null; //用户折扣
isMemberPrice: number | null; //会员折扣与会员价是否同时使用
}
/** 订单额外费用配置 */ /** 订单额外费用配置 */
export interface OrderExtraConfig { export interface OrderExtraConfig {
// merchantReduction: number; // 商家减免金额默认0 // merchantReduction: number; // 商家减免金额默认0
@@ -251,6 +291,8 @@ export interface OrderExtraConfig {
currentDinnerType: "dine-in" | "take-out" | "take-away" | "post"; // 当前就餐类型匹配useType currentDinnerType: "dine-in" | "take-out" | "take-away" | "post"; // 当前就餐类型匹配useType
isFreeDine?: boolean; //是否霸王餐 isFreeDine?: boolean; //是否霸王餐
freeDineConfig?: FreeDineConfig; freeDineConfig?: FreeDineConfig;
limitTimeDiscount?: TimeLimitDiscountConfig; //限时折扣
shopUserInfo: ShopUserInfo; // 用户信息
} }
/** 订单费用汇总(修改:补充商家减免类型和明细) */ /** 订单费用汇总(修改:补充商家减免类型和明细) */
@@ -283,10 +325,12 @@ export interface OrderCostSummary {
config: OrderExtraConfig; // 订单额外费用配置 config: OrderExtraConfig; // 订单额外费用配置
//满减活动 //满减活动
fullReduction: { fullReduction: {
usedFullReductionActivityFullAmount: number; // 计算出的满减活动的门槛金额
usedActivity?: FullReductionActivity; // 实际使用的满减活动 usedActivity?: FullReductionActivity; // 实际使用的满减活动
usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优) usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优)
actualAmount: number; // 满减实际减免金额(元) actualAmount: number; // 满减实际减免金额(元)
}; };
vipDiscountAmount: number; //会员折扣减免金额
// 订单原支付金额 // 订单原支付金额
orderOriginFinalPayAmount: number; //订单原金额(包含打包费+餐位费) orderOriginFinalPayAmount: number; //订单原金额(包含打包费+餐位费)
} }
@@ -399,6 +443,195 @@ function isDinnerTypeMatch(
//满减活动的就餐类型和当前券类型字段值不一样暂时返回true //满减活动的就餐类型和当前券类型字段值不一样暂时返回true
return true; return true;
} }
//判断商品是否可以使用限时折扣
export function returnCanUseLimitTimeDiscount(
goods: BaseCartItem,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean,
idKey = "product_id"
) {
if (!limitTimeDiscount || !limitTimeDiscount.id) {
return false;
}
const canUseFoods = (limitTimeDiscount.foods || "").split(",");
const goodsCanUse =
limitTimeDiscount.foodType == 1 || canUseFoods.includes("" + goods[idKey as keyof BaseCartItem]);
if (!goodsCanUse) {
return false;
}
if (limitTimeDiscount.discountPriority == "limit-time") {
return true;
}
if (limitTimeDiscount.discountPriority == "vip-price") {
if (!useVipPrice) {
return true;
}
if (useVipPrice && goods.hasOwnProperty("memberPrice")) {
if (goods.memberPrice && goods.memberPrice * 1 <= 0) {
return true;
}
}
}
return false;
}
function returnMemberPrice(useVipPrice: boolean, goods: BaseCartItem) {
if (useVipPrice) {
return goods.memberPrice || goods.salePrice;
} else {
return goods.salePrice;
}
}
/**
* 返回商品限时折扣价格
*/
function returnLimitPrice(
goods: BaseCartItem,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean
) {
if (!limitTimeDiscount) {
return 0;
}
const discountRate = new BigNumber(limitTimeDiscount.discountRate).dividedBy(
100
);
const canuseLimit = returnCanUseLimitTimeDiscount(
goods,
limitTimeDiscount,
useVipPrice
);
if (canuseLimit) {
//可以使用限时折扣
if (limitTimeDiscount.discountPriority == "limit-time") {
//限时价优先
const result = BigNumber(goods.salePrice)
.times(discountRate)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
return result;
}
if (limitTimeDiscount.discountPriority == "vip-price") {
//会员价优先
if (useVipPrice && goods.memberPrice && goods.memberPrice * 1 > 0) {
//使用会员价
return returnMemberPrice(useVipPrice, goods);
} else {
//不使用会员价
const result = BigNumber(goods.salePrice)
.times(discountRate)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
return result;
}
}
} else {
//不可以使用限时折扣
//会员价优先
if (useVipPrice) {
//使用会员价
return returnMemberPrice(useVipPrice, goods);
} else {
return goods.salePrice;
}
}
}
/**
* 计算商品计算门槛时的金额
*/
export function returnCalcPrice(
goods: BaseCartItem,
fullReductionActivitie: FullReductionActivity | undefined,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean,
idKey = "product_id"
) {
if (goods.discountSaleAmount && goods.discountSaleAmount * 1 > 0) {
return goods.salePrice;
}
//限时折扣和满减活动都有
if (fullReductionActivitie && limitTimeDiscount) {
if (
fullReductionActivitie.discountShare == 1 &&
fullReductionActivitie.vipPriceShare == 1
) {
//与限时折扣同享,与会员价不同享
return returnLimitPrice(goods, limitTimeDiscount, useVipPrice);
}
if (
fullReductionActivitie.discountShare != 1 &&
fullReductionActivitie.vipPriceShare == 1
) {
//与限时折扣不同享,与会员价同享
return returnMemberPrice(useVipPrice, goods);
}
if (fullReductionActivitie.vipPriceShare != 1) {
//与会员价不同享
return goods.salePrice;
}
return goods.salePrice;
}
//只有满减活动
if (fullReductionActivitie) {
if (fullReductionActivitie.vipPriceShare == 1) {
return returnMemberPrice(useVipPrice, goods);
} else {
return goods.salePrice;
}
}
//只有限时折扣
if (limitTimeDiscount) {
return returnLimitPrice(goods, limitTimeDiscount, useVipPrice);
}
if (useVipPrice) {
return returnMemberPrice(useVipPrice, goods);
}
return goods.salePrice;
}
/**
* 计算满减活动门槛
*/
export function calcFullReductionActivityFullAmount(
goodsList: BaseCartItem[],
fullReductionActivitie: FullReductionActivity | undefined,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean,
seatFee: number,
packFee: number
): number {
if (!fullReductionActivitie) {
return 0;
}
let amount = 0;
for (let goods of goodsList) {
const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
if (goods.is_temporary || goods.is_gift || availableNum <= 0) {
//临时菜,赠菜,数量<=0的商品不计算
continue;
}
const calcPrice = returnCalcPrice(
goods,
fullReductionActivitie,
limitTimeDiscount,
useVipPrice,
"product_id"
);
if (calcPrice !== undefined) {
amount += calcPrice * availableNum;
}
}
return amount + seatFee + packFee;
console.log("amount", amount);
}
/** /**
* 筛选最优满减活动(对齐后端逻辑:状态→时间→周期→时段→就餐类型→排序→修改时间) * 筛选最优满减活动(对齐后端逻辑:状态→时间→周期→时段→就餐类型→排序→修改时间)
* @param activities 后端返回的满减活动列表 * @param activities 后端返回的满减活动列表
@@ -424,11 +657,16 @@ export function filterOptimalFullReductionActivity(
activity.thresholds?.length // 至少有一个满减阈值 activity.thresholds?.length // 至少有一个满减阈值
); );
}); });
console.log("baseEligible", baseEligible);
if (!baseEligible.length) return undefined; if (!baseEligible.length) return undefined;
// 第二步:时间筛选(有效期内+周期内+时段内) // 第二步:时间筛选(有效期内+周期内+时段内)
const timeEligible = baseEligible.filter((activity) => { const timeEligible = baseEligible.filter((activity) => {
// 1. 校验有效期validStartTime ~ validEndTime // 1. 校验有效期validStartTime ~ validEndTime
if (activity.useTimeType == "all") {
return true;
}
if (!activity.validStartTime || !activity.validEndTime) return false; if (!activity.validStartTime || !activity.validEndTime) return false;
const startDate = new Date(activity.validStartTime); const startDate = new Date(activity.validStartTime);
const endDate = new Date(activity.validEndTime); const endDate = new Date(activity.validEndTime);
@@ -520,9 +758,7 @@ export function calcMemberPrice(
if (!isMember) return truncateToTwoDecimals(goods.salePrice); if (!isMember) return truncateToTwoDecimals(goods.salePrice);
// 优先级SKU会员价 > 商品会员价 > 商品原价(无会员价时用会员折扣) // 优先级SKU会员价 > 商品会员价 > 商品原价(无会员价时用会员折扣)
const basePrice = const basePrice = goods.memberPrice || goods.salePrice;
goods.skuData?.memberPrice ?? goods.memberPrice ?? goods.salePrice;
// 仅当无SKU会员价、无商品会员价时才应用会员折扣率 // 仅当无SKU会员价、无商品会员价时才应用会员折扣率
if (memberDiscountRate && !goods.skuData?.memberPrice && !goods.memberPrice) { if (memberDiscountRate && !goods.skuData?.memberPrice && !goods.memberPrice) {
return truncateToTwoDecimals( return truncateToTwoDecimals(
@@ -565,8 +801,8 @@ export function filterThresholdGoods(
return applicableProductIds.length === 0 return applicableProductIds.length === 0
? baseEligibleGoods ? baseEligibleGoods
: baseEligibleGoods.filter((goods) => : baseEligibleGoods.filter((goods) =>
applicableProductIds.includes(String(goods.product_id)) applicableProductIds.includes(String(goods.product_id))
); // 核心修正用商品ID匹配 ); // 核心修正用商品ID匹配
} }
/** /**
@@ -665,11 +901,13 @@ export function calcCouponThresholdAmount(
*/ */
export function calcSingleGoodsRealPrice( export function calcSingleGoodsRealPrice(
goods: BaseCartItem, goods: BaseCartItem,
config: Pick<OrderExtraConfig, "isMember" | "memberDiscountRate"> & { config: Pick<
activity?: ActivityConfig; // 商品参与的营销活动如限时折扣按商品ID匹配 OrderExtraConfig,
} "isMember" | "memberDiscountRate" | "limitTimeDiscount"
>
): number { ): number {
const { isMember, memberDiscountRate, activity } = config; const { isMember, memberDiscountRate, limitTimeDiscount: activity } = config;
console.log("activity", activity);
//如果是增菜价格为0 //如果是增菜价格为0
if (goods.is_gift) { if (goods.is_gift) {
@@ -687,12 +925,38 @@ export function calcSingleGoodsRealPrice(
); );
// 3. 优先级3营销活动折扣如限时折扣需按商品ID匹配活动 // 3. 优先级3营销活动折扣如限时折扣需按商品ID匹配活动
const isActivityApplicable = activity let isActivityApplicable = false;
? (activity.applicableProductIds || []).includes(String(goods.product_id)) // 核心修正用商品ID匹配活动 if (activity) {
: false; if (activity.foodType == 1) {
isActivityApplicable = true;
} else {
const canUseGoods = activity.foods?.split(",") || [];
if (canUseGoods.find((v) => v == String(goods.product_id))) {
isActivityApplicable = true;
}
}
}
if (!activity || !isActivityApplicable) { if (!activity || !isActivityApplicable) {
return memberPrice.toNumber(); return memberPrice.toNumber();
} }
console.log("isMember", isMember);
//限时折扣优先或者会员价优先但是不是会员或者未开启会员价格时限时折扣优先
if (
activity.discountPriority == "limit-time" ||
(activity.discountPriority == "vip-price" && !isMember) ||
(activity.discountPriority == "vip-price" && isMember && !goods.memberPrice)
) {
//限时折扣优先
return truncateToTwoDecimals(
new BigNumber(goods.salePrice)
.times(activity.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber()
);
}
if (activity.discountPriority == "vip-price" && isMember) {
return memberPrice.toNumber();
}
// 处理活动与会员的同享/不同享逻辑 // 处理活动与会员的同享/不同享逻辑
if (activity.vipPriceShare) { if (activity.vipPriceShare) {
@@ -751,26 +1015,18 @@ export function calcGoodsOriginalAmount(goodsList: BaseCartItem[]): number {
*/ */
export function calcGoodsRealAmount( export function calcGoodsRealAmount(
goodsList: BaseCartItem[], goodsList: BaseCartItem[],
config: Pick<OrderExtraConfig, "isMember" | "memberDiscountRate">, config: Pick<
activities: ActivityConfig[] = [] OrderExtraConfig,
"isMember" | "memberDiscountRate" | "limitTimeDiscount"
>
): number { ): number {
let total = new BigNumber(0); let total = new BigNumber(0);
for (const goods of goodsList) { for (const goods of goodsList) {
const availableNum = Math.max(0, goods.number - (goods.returnNum || 0)); const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
if (availableNum <= 0) continue; if (availableNum <= 0) continue;
console.log("goods", goods);
// 匹配商品参与的营销活动按商品ID匹配优先商品自身配置 const realPrice = new BigNumber(calcSingleGoodsRealPrice(goods, config));
const activity =
goods.activityInfo ??
activities.find(
(act) =>
(act.applicableProductIds || []).includes(String(goods.product_id)) // 核心修正用商品ID匹配活动
);
const realPrice = new BigNumber(
calcSingleGoodsRealPrice(goods, { ...config, activity })
);
total = total.plus(realPrice.multipliedBy(availableNum)); total = total.plus(realPrice.multipliedBy(availableNum));
} }
@@ -818,27 +1074,18 @@ export function selectOptimalThreshold(
const validThresholds = thresholds.filter((threshold) => { const validThresholds = thresholds.filter((threshold) => {
const fullAmount = new BigNumber(threshold.fullAmount || 0); const fullAmount = new BigNumber(threshold.fullAmount || 0);
const discountAmount = new BigNumber(threshold.discountAmount || 0); const discountAmount = new BigNumber(threshold.discountAmount || 0);
console.log("fullAmount", fullAmount);
console.log("discountAmount", discountAmount);
return ( return (
fullAmount.isLessThanOrEqualTo(thresholdBase) && fullAmount.isLessThanOrEqualTo(thresholdBase) &&
discountAmount.isGreaterThan(0) discountAmount.isGreaterThan(0)
); );
}); });
console.log("validThresholds", validThresholds);
if (!validThresholds.length) return undefined; if (!validThresholds.length) return undefined;
// 第三步选择最优阈值优先级1.满金额最小 → 2.减免金额最大)
// const sortValidThresholds = validThresholds.sort((a, b) => {
// const aFull = new BigNumber(a.fullAmount || 0);
// const bFull = new BigNumber(b.fullAmount || 0);
// const aDiscount = new BigNumber(a.discountAmount || 0);
// const bDiscount = new BigNumber(b.discountAmount || 0);
// // 先比满金额越小越优先满1减10 比 满100减20 更优)
// if (!aFull.isEqualTo(bFull)) {
// return aFull.comparedTo(bFull) || 0; // Ensure a number is always returned
// }
// // 再比减免金额:越大越优先
// return bDiscount.comparedTo(aDiscount) || 0; // Ensure a number is always returned
// })
// 找到抵扣金额最大的门槛项 // 找到抵扣金额最大的门槛项
const maxDiscountThreshold = validThresholds.reduce( const maxDiscountThreshold = validThresholds.reduce(
(maxItem, currentItem) => { (maxItem, currentItem) => {
@@ -981,13 +1228,12 @@ export function calcTotalPackFee(
const packNumber = goods.packNumber ? goods.packNumber * 1 : 0; const packNumber = goods.packNumber ? goods.packNumber * 1 : 0;
let availableNum = Math.max(0, goods.number - (goods.returnNum || 0)); let availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
if (availableNum === 0) continue; if (availableNum === 0) continue;
// 计算单个商品打包数量外卖全打包堂食按配置称重商品≤1 // 计算单个商品打包数量外卖全打包堂食按配置称重商品≤1
let packNum = Math.min(availableNum, packNumber); let packNum = Math.min(availableNum, packNumber);
if (dinnerType === "take-out") { if (dinnerType === "take-out") {
packNum = availableNum packNum = availableNum;
} }
if (goods.product_type === GoodsType.WEIGHT) { if (goods.product_type === GoodsType.WEIGHT) {
packNum = Math.min(packNum, 1); packNum = Math.min(packNum, 1);
@@ -1043,8 +1289,8 @@ export function calcPointDeduction(
) )
? maxDeductByPoints ? maxDeductByPoints
: new BigNumber(rule.maxDeductionAmount || Infinity).isLessThan(maxLimitBn) : new BigNumber(rule.maxDeductionAmount || Infinity).isLessThan(maxLimitBn)
? maxDeductByPoints ? maxDeductByPoints
: maxLimitBn; : maxLimitBn;
// 实际使用积分 = 抵扣金额 * 积分兑换比例 // 实际使用积分 = 抵扣金额 * 积分兑换比例
const usedPoints = maxDeductAmount.multipliedBy(pointsPerYuanBn); const usedPoints = maxDeductAmount.multipliedBy(pointsPerYuanBn);
@@ -1057,6 +1303,24 @@ export function calcPointDeduction(
}; };
} }
function calcVipDiscountAmount(
goodsRealAmount: number,
shopUserInfo: ShopUserInfo
): number {
if (!shopUserInfo.isVip || shopUserInfo.discount === 0) return 0;
if (shopUserInfo.isVip == 1 && shopUserInfo.isMemberPrice != 1) {
return 0;
}
console.log("goodsRealAmount", goodsRealAmount);
console.log("discount", (100 - (shopUserInfo.discount || 0)) / 100);
return truncateToTwoDecimals(
new BigNumber(goodsRealAmount)
.times((100 - (shopUserInfo.discount || 0)) / 100)
.decimalPlaces(2, BigNumber.ROUND_DOWN)
.toNumber()
);
}
// ============================ 6. 订单总费用汇总与实付金额计算(核心入口,补充细分字段) ============================ // ============================ 6. 订单总费用汇总与实付金额计算(核心入口,补充细分字段) ============================
/** /**
* 计算订单所有费用子项并汇总(核心入口函数) * 计算订单所有费用子项并汇总(核心入口函数)
@@ -1079,7 +1343,14 @@ export function calculateOrderCostSummary(
currentTime: Date = new Date() currentTime: Date = new Date()
): OrderCostSummary { ): OrderCostSummary {
//是否使用霸王餐,霸王餐配置 //是否使用霸王餐,霸王餐配置
const { isFreeDine, freeDineConfig } = config; const {
isFreeDine,
freeDineConfig,
limitTimeDiscount,
fullReductionActivities,
shopUserInfo,
} = config;
console.log("shopUserInfo", shopUserInfo);
// ------------------------------ 1. 基础费用计算 ------------------------------ // ------------------------------ 1. 基础费用计算 ------------------------------
const goodsOriginalAmount = calcGoodsOriginalAmount(goodsList); // 商品原价总和 const goodsOriginalAmount = calcGoodsOriginalAmount(goodsList); // 商品原价总和
@@ -1089,19 +1360,35 @@ export function calculateOrderCostSummary(
{ {
isMember: config.isMember, isMember: config.isMember,
memberDiscountRate: config.memberDiscountRate, memberDiscountRate: config.memberDiscountRate,
}, limitTimeDiscount: config.limitTimeDiscount,
activities }
); );
const goodsDiscountAmount = calcGoodsDiscountAmount( const goodsDiscountAmount = calcGoodsDiscountAmount(
goodsOriginalAmount, goodsOriginalAmount,
goodsRealAmount goodsRealAmount
); // 商品折扣金额 ); // 商品折扣金额
const newUserDiscount = config.newUserDiscount || 0; // 新客立减 const newUserDiscount = config.newUserDiscount || 0; // 新客立减
// 其他费用计算(原有逻辑不变) ------------------------------
const packFee = calcTotalPackFee(goodsList, dinnerType); // 打包费
let seatFee = calcSeatFee(config.seatFeeConfig); // 餐位费
seatFee = dinnerType === "dine-in" ? seatFee : 0; // 外卖不收餐位费
const additionalFee = Math.max(0, config.additionalFee); // 附加费
// ------------------------------ 2. 满减活动计算(核心步骤) ------------------------------ // ------------------------------ 2. 满减活动计算(核心步骤) ------------------------------
let usedFullReductionActivity: FullReductionActivity | undefined; let usedFullReductionActivity: FullReductionActivity | undefined;
let usedFullReductionThreshold: FullReductionThreshold | undefined; let usedFullReductionThreshold: FullReductionThreshold | undefined;
let fullReductionAmount = 0; let fullReductionAmount = 0;
let usedFullReductionActivityFullAmount = calcFullReductionActivityFullAmount(
goodsList,
usedFullReductionActivity,
config.limitTimeDiscount,
config.isMember,
seatFee,
packFee
);
// 2.1 筛选最优满减活动(后端活动列表、当前店铺、就餐类型、时间) // 2.1 筛选最优满减活动(后端活动列表、当前店铺、就餐类型、时间)
usedFullReductionActivity = filterOptimalFullReductionActivity( usedFullReductionActivity = filterOptimalFullReductionActivity(
config.fullReductionActivities, config.fullReductionActivities,
@@ -1110,14 +1397,15 @@ export function calculateOrderCostSummary(
currentTime currentTime
); );
// 其他费用计算(原有逻辑不变) ------------------------------
const packFee = calcTotalPackFee(goodsList, dinnerType); // 打包费
let seatFee = calcSeatFee(config.seatFeeConfig); // 餐位费
seatFee = dinnerType === "dine-in" ? seatFee : 0; // 外卖不收餐位费
const additionalFee = Math.max(0, config.additionalFee); // 附加费
// 2.2 计算满减基数(先扣新客立减) // 2.2 计算满减基数(先扣新客立减)
let baseAfterNewUserDiscount = new BigNumber(goodsRealAmount) let baseAfterNewUserDiscount = new BigNumber(
limitTimeDiscount &&
limitTimeDiscount.id &&
usedFullReductionActivity &&
!usedFullReductionActivity.discountShare
? goodsRealAmount
: goodsRealAmount
)
.minus(newUserDiscount) .minus(newUserDiscount)
.plus(packFee) .plus(packFee)
.plus(seatFee) .plus(seatFee)
@@ -1128,9 +1416,19 @@ export function calculateOrderCostSummary(
// 2.3 选择最优满减阈值(多门槛场景) // 2.3 选择最优满减阈值(多门槛场景)
if (usedFullReductionActivity) { if (usedFullReductionActivity) {
//计算当前满减活动的门槛金额
usedFullReductionActivityFullAmount = calcFullReductionActivityFullAmount(
goodsList,
usedFullReductionActivity,
config.limitTimeDiscount,
config.isMember,
seatFee,
packFee
);
usedFullReductionThreshold = selectOptimalThreshold( usedFullReductionThreshold = selectOptimalThreshold(
usedFullReductionActivity.thresholds, usedFullReductionActivity.thresholds,
baseAfterNewUserDiscount, usedFullReductionActivityFullAmount,
goodsOriginalAmount, goodsOriginalAmount,
goodsRealAmount, goodsRealAmount,
usedFullReductionActivity.discountShare || 0 // 与限时折扣同享规则 usedFullReductionActivity.discountShare || 0 // 与限时折扣同享规则
@@ -1143,6 +1441,7 @@ export function calculateOrderCostSummary(
usedFullReductionThreshold usedFullReductionThreshold
); );
} }
// ------------------------------ 3. 优惠券抵扣(适配满减同享规则) ------------------------------ // ------------------------------ 3. 优惠券抵扣(适配满减同享规则) ------------------------------
let couponDeductionAmount = 0; let couponDeductionAmount = 0;
let productCouponDeduction = 0; let productCouponDeduction = 0;
@@ -1164,6 +1463,7 @@ export function calculateOrderCostSummary(
currentTime, currentTime,
} }
); );
console.log("couponResult", couponResult);
couponDeductionAmount = couponResult.deductionAmount; couponDeductionAmount = couponResult.deductionAmount;
productCouponDeduction = couponResult.productCouponDeduction; productCouponDeduction = couponResult.productCouponDeduction;
fullCouponDeduction = couponResult.fullCouponDeduction; fullCouponDeduction = couponResult.fullCouponDeduction;
@@ -1171,7 +1471,10 @@ export function calculateOrderCostSummary(
excludedProductIds = couponResult.excludedProductIds; excludedProductIds = couponResult.excludedProductIds;
// 若满减与优惠券同享couponShare=1才计算优惠券否则优惠券抵扣为0 // 若满减与优惠券同享couponShare=1才计算优惠券否则优惠券抵扣为0
if (usedFullReductionActivity && !usedFullReductionActivity.couponShare) { if (
usedFullReductionThreshold &&
(!usedFullReductionActivity || !usedFullReductionActivity.couponShare)
) {
couponDeductionAmount = 0; couponDeductionAmount = 0;
productCouponDeduction = 0; productCouponDeduction = 0;
fullCouponDeduction = 0; fullCouponDeduction = 0;
@@ -1195,26 +1498,26 @@ export function calculateOrderCostSummary(
maxPointDeductionLimit = maxPointDeductionLimit =
maxPointDeductionLimit > 0 ? maxPointDeductionLimit : 0; maxPointDeductionLimit > 0 ? maxPointDeductionLimit : 0;
const pointResult = calcPointDeduction( const pointResult = calcPointDeduction(
config.userPoints, config.userPoints,
config.pointDeductionRule, config.pointDeductionRule,
maxPointDeductionLimit maxPointDeductionLimit
); );
console.log("积分抵扣结果:", pointResult);
pointDeductionAmount = pointResult.deductionAmount; pointDeductionAmount = pointResult.deductionAmount;
usedPoints = pointResult.usedPoints; usedPoints = pointResult.usedPoints;
// 若满减与积分不同享pointsShare=1积分抵扣为0 // 若满减与积分不同享pointsShare=1积分抵扣为0
if (usedFullReductionActivity && !usedFullReductionActivity.pointsShare) { if (
console.log("满减与积分不同享:积分抵扣为0"); usedFullReductionThreshold &&
(!usedFullReductionActivity || !usedFullReductionActivity.pointsShare)
) {
pointDeductionAmount = 0; pointDeductionAmount = 0;
usedPoints = 0; usedPoints = 0;
} }
//使用霸王餐 //使用霸王餐
if (isFreeDine && freeDineConfig && freeDineConfig.enable) { if (isFreeDine && freeDineConfig && freeDineConfig.enable) {
console.log("使用霸王餐"); fullReductionAmount = 0;
//不与优惠券同享 //不与优惠券同享
if (!freeDineConfig.withCoupon) { if (!freeDineConfig.withCoupon) {
couponDeductionAmount = 0; couponDeductionAmount = 0;
@@ -1242,13 +1545,13 @@ export function calculateOrderCostSummary(
.plus(packFee) .plus(packFee)
.isGreaterThan(0) .isGreaterThan(0)
? new BigNumber(goodsRealAmount) ? new BigNumber(goodsRealAmount)
.minus(newUserDiscount) .minus(newUserDiscount)
.minus(fullReductionAmount) .minus(fullReductionAmount)
.minus(couponDeductionAmount) .minus(couponDeductionAmount)
.minus(pointDeductionAmount) .minus(pointDeductionAmount)
.plus(seatFee) .plus(seatFee)
.plus(packFee) .plus(packFee)
.toNumber() .toNumber()
: 0; : 0;
switch (merchantReductionConfig.type) { switch (merchantReductionConfig.type) {
@@ -1271,9 +1574,22 @@ export function calculateOrderCostSummary(
truncateToTwoDecimals(merchantReductionActualAmount) truncateToTwoDecimals(merchantReductionActualAmount)
); );
// 会员折扣减免
const vipDiscountAmount = calcVipDiscountAmount(
new BigNumber(goodsRealAmount)
.minus(couponDeductionAmount)
.plus(packFee)
.plus(seatFee)
.minus(newUserDiscount)
.minus(fullReductionAmount)
.toNumber(),
shopUserInfo
);
console.log("vipDiscountAmount", vipDiscountAmount);
// ------------------------------ 6. 最终实付金额计算 ------------------------------ // ------------------------------ 6. 最终实付金额计算 ------------------------------
const finalPayAmountBn = new BigNumber(goodsRealAmount) const finalPayAmountBn = new BigNumber(goodsRealAmount)
.minus(newUserDiscount) .minus(newUserDiscount)
.minus(vipDiscountAmount)
.minus(fullReductionAmount) .minus(fullReductionAmount)
.minus(couponDeductionAmount) .minus(couponDeductionAmount)
.minus(pointDeductionAmount) .minus(pointDeductionAmount)
@@ -1301,6 +1617,7 @@ export function calculateOrderCostSummary(
.plus(couponDeductionAmount) .plus(couponDeductionAmount)
.plus(pointDeductionAmount) .plus(pointDeductionAmount)
.plus(merchantReductionActualAmount) .plus(merchantReductionActualAmount)
.plus(vipDiscountAmount)
.toNumber() .toNumber()
); );
//积分可抵扣最大金额 最终支付金额+积分抵扣-商家减免 //积分可抵扣最大金额 最终支付金额+积分抵扣-商家减免
@@ -1331,10 +1648,12 @@ export function calculateOrderCostSummary(
scoreMaxMoney, scoreMaxMoney,
// 满减活动明细(后端字段) // 满减活动明细(后端字段)
fullReduction: { fullReduction: {
usedFullReductionActivityFullAmount: usedFullReductionActivityFullAmount,
usedActivity: usedFullReductionActivity, usedActivity: usedFullReductionActivity,
usedThreshold: usedFullReductionThreshold, usedThreshold: usedFullReductionThreshold,
actualAmount: truncateToTwoDecimals(fullReductionAmount), actualAmount: truncateToTwoDecimals(fullReductionAmount),
}, },
vipDiscountAmount: vipDiscountAmount, //会员折扣减免金额
merchantReduction: { merchantReduction: {
type: merchantReductionConfig.type, type: merchantReductionConfig.type,
originalConfig: merchantReductionConfig, originalConfig: merchantReductionConfig,

183
src/utils/order-utils.js Normal file
View File

@@ -0,0 +1,183 @@
import BigNumber from "bignumber.js";
//判断商品是否可以使用限时折扣
export function canUseLimitTimeDiscount(
goods,
limitTimeDiscountRes,
shopInfo,
shopUserInfo,
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 (shopUserInfo.isVip != 1 || shopUserInfo.isMemberPrice != 1) {
return true;
}
if (
shopUserInfo.isVip == 1 &&
shopUserInfo.isMemberPrice == 1 &&
goods.memberPrice * 1 <= 0
) {
return true;
}
}
return false;
}
/**
* 返回商品显示价格
* @params {*} args 参数对象
* @params {*} args.goods 商品对象
* @params {*} args.shopInfo 店铺信息
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.shopUserInfo 店铺用户信息
* @returns
*/
export function returnPrice(args) {
let {
goods,
shopInfo,
limitTimeDiscountRes,
shopUserInfo,
idKey = "product_id",
} = args;
console.log('返回商品显示价格.args===', args);
limitTimeDiscountRes = limitTimeDiscountRes || { foods: '', foodType: 2 }
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
// console.log('返回商品显示价格.goods===', goods);
const includesGoods =
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes("" + goods[idKey]);
console.log('includesGoods===', includesGoods);
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;
}
}
}
/**
* 返回限时折扣价格
* @params {*} args 参数对象
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.price 商品价格
* @param {*} args.goods 商品对象
* @returns
*/
export function returnLimitPrice(args) {
console.log('返回限时折扣价格===', args);
const { limitTimeDiscountRes, price, goods } = args;
const discountRate = new BigNumber(
limitTimeDiscountRes.discountRate
).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) {
const { shopInfo, shopUserInfo } = args;
if (shopUserInfo.isMemberPrice == 1 && shopUserInfo.isVip == 1) {
return true;
} else {
return false;
}
}
/**
* 返回会员价格
* @param {*} goods
* @returns
*/
export function returnMemberPrice(goods) {
return goods.memberPrice || goods.salePrice;
}

View File

@@ -387,10 +387,10 @@ export default {
conName: v.conName, conName: v.conName,
purchasePrice: v.price, purchasePrice: v.price,
unitName: v.conUnit, unitName: v.conUnit,
inOutNumber, inOutNumber: v.number,
subTotal: this.xiaoji(v), subTotal: this.xiaoji(v),
imgUrls: v.imgUrls, imgUrls: v.imgUrls,
number: v.number, number: inOutNumber,
}; };
}); });
if (this.type == "in") { if (this.type == "in") {

View File

@@ -37,7 +37,11 @@
</el-table-column> </el-table-column>
<el-table-column label="会员" prop="memberLevelName"> <el-table-column label="会员" prop="memberLevelName">
<template #default="scope"> <template #default="scope">
{{ scope.row.memberLevelName.length > 0 ? scope.row.memberLevelName : '否' }} <span v-if="scope.row.isVip == 1">
<template v-if="scope.row.memberLevelName.length > 0"> {{ scope.row.memberLevelName }}</template>
<template v-else></template>
</span>
<span v-else></span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="分销员" prop="distributionShops"> <el-table-column label="分销员" prop="distributionShops">

View File

@@ -217,6 +217,7 @@ async function getTableData() {
if (tabIndex.value == 0) { if (tabIndex.value == 0) {
res = await distributionUserInviteUser({ res = await distributionUserInviteUser({
id: rowInfo.value.id, id: rowInfo.value.id,
shopId: rowInfo.value.shopId,
page: tableData.page, page: tableData.page,
size: tableData.size, size: tableData.size,
shopUserId: querForm.value.userId, shopUserId: querForm.value.userId,
@@ -225,6 +226,7 @@ async function getTableData() {
} else { } else {
res = await distributionFlowGet({ res = await distributionFlowGet({
id: rowInfo.value.id, id: rowInfo.value.id,
shopId: rowInfo.value.shopId,
parentId: rowInfo.value.id, parentId: rowInfo.value.id,
shopUserId: querForm.value.userId, shopUserId: querForm.value.userId,
distributionLevelId: querForm.value.distributionLevelId, distributionLevelId: querForm.value.distributionLevelId,

View File

@@ -96,7 +96,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="关联订单号" prop="orderNo" min-width="200"></el-table-column> <el-table-column label="关联订单号" prop="orderNo" min-width="200"></el-table-column>
<el-table-column label="收益(元)" prop="rewardAmount" min-width="120"> <el-table-column label="收益(元)" prop="rewardAmount" min-width="120">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.status == 'refund'">-</span> <span v-if="scope.row.status == 'refund'">-</span>
{{ multiplyAndFormat(scope.row.rewardAmount || 0) }} {{ multiplyAndFormat(scope.row.rewardAmount || 0) }}

View File

@@ -27,6 +27,7 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="分销员等级" prop="distributionLevelName" width="100"></el-table-column>
<el-table-column label="总收益(元)" prop="totalIncome" width="100"> <el-table-column label="总收益(元)" prop="totalIncome" width="100">
<template #default="scope"> <template #default="scope">
{{ multiplyAndFormat(scope.row.totalIncome || 0) }} {{ multiplyAndFormat(scope.row.totalIncome || 0) }}

View File

@@ -69,6 +69,7 @@ async function submitHandle() {
confirmLoading.value = true confirmLoading.value = true
let data = {} let data = {}
data.id = rowInfo.value.id data.id = rowInfo.value.id
data.shopId = rowInfo.value.shopId
if (type.value == 1) { if (type.value == 1) {
data.isAssignLevel = 1 data.isAssignLevel = 1
data.distributionLevelId = form.value.distributionLevelId data.distributionLevelId = form.value.distributionLevelId

View File

@@ -48,7 +48,15 @@
</el-table-column> </el-table-column>
<el-table-column label="变动金额(元)" prop="changeAmount"></el-table-column> <el-table-column label="变动金额(元)" prop="changeAmount"></el-table-column>
<el-table-column label="变动后金额(元)" prop="amount"></el-table-column> <el-table-column label="变动后金额(元)" prop="amount"></el-table-column>
<el-table-column label="变动原因" prop="remark"></el-table-column> <el-table-column label="变动原因" prop="remark">
<template #default="scope">
<div class="column" v-if="scope.row.type == 'manual_recharge' || scope.row.type == 'manual_sub'">
<span>{{ scope.row.remark }}</span>
<span class="info">操作员{{ scope.row.opAccount }}</span>
</div>
<span v-else>{{ scope.row.remark }}</span>
</template>
</el-table-column>
<el-table-column label="关联订单" prop="orderNo"></el-table-column> <el-table-column label="关联订单" prop="orderNo"></el-table-column>
<el-table-column label="创建时间" prop="createTime"></el-table-column> <el-table-column label="创建时间" prop="createTime"></el-table-column>
</el-table> </el-table>
@@ -77,7 +85,7 @@ const info = ref({
const statusList = ref([ const statusList = ref([
{ {
value: 'manual_recharge', value: 'manual_recharge',
label: '充值' label: '手动充值'
}, },
{ {
value: 'self_recharge', value: 'self_recharge',
@@ -194,4 +202,14 @@ defineExpose({
} }
} }
} }
.column {
display: flex;
flex-direction: column;
.info {
font-size: 14px;
color: #999;
}
}
</style> </style>

View File

@@ -63,7 +63,7 @@
<div class="title_row"> <div class="title_row">
分销员等级 分销员等级
</div> </div>
<el-form-item label="条件升级" style="margin-top: 14px;" prop="upgradeType"> <el-form-item label="升级条件" style="margin-top: 14px;" prop="upgradeType">
<el-radio-group v-model="form.upgradeType" @change="upgradeTypeChange"> <el-radio-group v-model="form.upgradeType" @change="upgradeTypeChange">
<el-radio label="不自动升级" value="not_upgrade"></el-radio> <el-radio label="不自动升级" value="not_upgrade"></el-radio>
<el-radio label="邀请有效人数" value="invite"></el-radio> <el-radio label="邀请有效人数" value="invite"></el-radio>
@@ -163,7 +163,7 @@
<el-form-item style="margin-top: 24px;"> <el-form-item style="margin-top: 24px;">
<el-button type="primary" @click="addLevelHandle">添加等级</el-button> <el-button type="primary" @click="addLevelHandle">添加等级</el-button>
</el-form-item> </el-form-item>
<div class="title_row mt14">未开通页面营销</div> <div class="title_row mt14">未开通页面</div>
<el-form-item :label-width="0" style="margin-top: 14px;"> <el-form-item :label-width="0" style="margin-top: 14px;">
<WangEditor v-model="form.notActivatedPage" /> <WangEditor v-model="form.notActivatedPage" />
</el-form-item> </el-form-item>

View File

@@ -3,6 +3,9 @@
<div class="gyq_content"> <div class="gyq_content">
<HeaderCard name="分销" intro="用户成为业务员,可促进消费" icon="xffx" showSwitch v-model:isOpen="form.isEnable"> <HeaderCard name="分销" intro="用户成为业务员,可促进消费" icon="xffx" showSwitch v-model:isOpen="form.isEnable">
</HeaderCard> </HeaderCard>
<div class="tips">
<el-alert title="请记得前往《分销明细》充值余额,余额不足时用户收益将无法正常入账" type="primary" show-icon :closable="false" />
</div>
<div class="row mt14"> <div class="row mt14">
<tabHeader v-model="tabActiveIndex" :list="tabList" /> <tabHeader v-model="tabActiveIndex" :list="tabList" />
</div> </div>
@@ -111,4 +114,8 @@ onMounted(() => {
margin-top: 14px; margin-top: 14px;
} }
} }
.tips {
margin-top: 14px;
}
</style> </style>

View File

@@ -8,10 +8,10 @@
<div class="preview"> <div class="preview">
<div class="info"> <div class="info">
<div class="top"> <div class="top">
<div class="title">{{ form.title || '请输入模块标题' }}</div> <div class="title">{{ form.title || defaultTitle }}</div>
<div class="content">{{ form.content || '请输入模块内容' }}</div> <div class="content">{{ form.content || '请输入模块内容' }}</div>
</div> </div>
<div class="btm">{{ form.note || '请输入模块提示语' }}</div> <div class="btm">{{ form.note || defaultNote }}</div>
</div> </div>
<div class="img_wrap"> <div class="img_wrap">
<el-image :src="form.qrCode" style="width: 100%;height: 100%;"></el-image> <el-image :src="form.qrCode" style="width: 100%;height: 100%;"></el-image>
@@ -32,7 +32,7 @@
<single-image-upload style="width: 120px; height: 120px" v-model="form.qrCode"></single-image-upload> <single-image-upload style="width: 120px; height: 120px" v-model="form.qrCode"></single-image-upload>
</el-form-item> </el-form-item>
<el-form-item label="模块标题" prop="title"> <el-form-item label="模块标题" prop="title">
<el-input placeholder="请输入模块标题" :maxlength="20" v-model.trim="form.title"></el-input> <el-input :placeholder="defaultTitle" :maxlength="20" v-model.trim="form.title"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="模块内容"> <el-form-item label="模块内容">
<div class="textarea"> <div class="textarea">
@@ -42,7 +42,7 @@
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="模块提示语"> <el-form-item label="模块提示语">
<el-input placeholder="请输模块提示语" :maxlength="20" v-model.trim="form.note"></el-input> <el-input :placeholder="defaultNote" :maxlength="20" v-model.trim="form.note"></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :loading="loading" @click="submitHandle">保存</el-button> <el-button type="primary" :loading="loading" @click="submitHandle">保存</el-button>
@@ -59,13 +59,14 @@ import { ref, reactive, onMounted } from 'vue'
import { ElNotification } from 'element-plus' import { ElNotification } from 'element-plus'
import { drainageConfigGet, drainageConfigPost } from '@/api/coupon/index' import { drainageConfigGet, drainageConfigPost } from '@/api/coupon/index'
const defaultTitle = ref('扫码进群,优惠多多')
const defaultNote = ref('长按识别,微信内扫一扫加好友') const defaultNote = ref('长按识别,微信内扫一扫加好友')
const form = ref({ const form = ref({
id: '', id: '',
useType: ['dine-in'], useType: ['dine-in'],
qrCode: '', qrCode: '',
title: '', title: defaultTitle.value,
content: '', content: '',
note: '长按识别,微信内扫一扫加好友', note: '长按识别,微信内扫一扫加好友',
isEnable: '', isEnable: '',
@@ -129,6 +130,14 @@ function submitHandle() {
let data = { ...form.value } let data = { ...form.value }
if (data.title == '') {
data.title = defaultTitle.value
}
if (data.note == '') {
data.note = defaultNote.value
}
await drainageConfigPost(data); await drainageConfigPost(data);
ElNotification({ ElNotification({
title: '注意', title: '注意',
@@ -151,6 +160,13 @@ async function drainageConfigGetAjax() {
res.useType = [] res.useType = []
} }
form.value = res form.value = res
if (form.value.title == '') {
form.value.title = defaultTitle.value
}
if (form.value.note == '') {
form.value.note = defaultNote.value
}
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@@ -137,7 +137,7 @@ const statusList = ref([
}, },
{ {
value: 2, value: 2,
label: '发送成', label: '发送成',
type: 'success' type: 'success'
}, },
{ {

View File

@@ -28,8 +28,8 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="变动原因" prop="reason"></el-table-column> <el-table-column label="变动原因" prop="reason"></el-table-column>
<el-table-column label="变动前余额" prop="balance"></el-table-column>
<el-table-column label="变动金额" prop="expense"></el-table-column> <el-table-column label="变动金额" prop="expense"></el-table-column>
<el-table-column label="变动后余额" prop="balance"></el-table-column>
<el-table-column label="创建时间" prop="createTime"></el-table-column> <el-table-column label="创建时间" prop="createTime"></el-table-column>
</el-table> </el-table>
</div> </div>

View File

@@ -70,7 +70,7 @@
<el-radio-button :label="1">可选套餐</el-radio-button> <el-radio-button :label="1">可选套餐</el-radio-button>
</el-radio-group> </el-radio-group>
</div> </div>
<div v-if="ruleForm.groupType == '0'"> <div v-if="ruleForm.groupType == 0">
<el-table border :data="item.goods" v-for="(item, index) in ruleForm.proGroupVo" :key="index"> <el-table border :data="item.goods" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-table-column label="名称" prop="proName"></el-table-column> <el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"></el-table-column> <el-table-column label="规格" prop="skuName"></el-table-column>
@@ -95,7 +95,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<div v-if="ruleForm.groupType == '1'"> <div v-if="ruleForm.groupType == 1">
<div class="group_wrap" v-for="(item, index) in ruleForm.proGroupVo" :key="index"> <div class="group_wrap" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-form inline :model="item"> <el-form inline :model="item">
<el-form-item label="规格组名"> <el-form-item label="规格组名">
@@ -382,7 +382,7 @@ const ruleForm = reactive<RuleForm>({
// 规格id // 规格id
specId: "", specId: "",
// 套餐类型 // 套餐类型
groupType: "0", groupType: 0,
// 套餐入参 // 套餐入参
proGroupVo: [ proGroupVo: [
{ {
@@ -569,7 +569,7 @@ function selectShopRes(res: Array<any>) {
return item; return item;
}); });
if (ruleForm.groupType == "0") { if (ruleForm.groupType == 0) {
let obj = { let obj = {
title: "", title: "",
count: newres.length, count: newres.length,
@@ -726,7 +726,7 @@ function cartesian(arr) {
// 套餐类型切换 // 套餐类型切换
function typeChange() { function typeChange() {
// ruleForm.typeEnum = 'normal' // ruleForm.typeEnum = 'normal'
if (ruleForm.groupType == "0") { if (ruleForm.groupType == 0) {
ruleForm.proGroupVo = []; ruleForm.proGroupVo = [];
ruleForm.proGroupVo[0] = { ruleForm.proGroupVo[0] = {
title: "", title: "",
@@ -783,7 +783,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
// 标题和几选几是否填写 // 标题和几选几是否填写
if (ruleForm.type == "package") { if (ruleForm.type == "package") {
if (ruleForm.groupType == "1") { if (ruleForm.groupType == 1) {
let selectTitle = false; let selectTitle = false;
ruleForm.proGroupVo.forEach((item: any) => { ruleForm.proGroupVo.forEach((item: any) => {
if (item.number == "" || item.title == "") { if (item.number == "" || item.title == "") {

View File

@@ -3,25 +3,14 @@
<div> <div>
<el-form ref="form" :model="form" :rules="rules" label-width="160px" label-position="left"> <el-form ref="form" :model="form" :rules="rules" label-width="160px" label-position="left">
<el-form-item label="门店名称" prop="shopName"> <el-form-item label="门店名称" prop="shopName">
<el-input <el-input v-model.trim="form.shopName" placeholder="请输入门店名称" style="width: 500px"></el-input>
v-model.trim="form.shopName"
placeholder="请输入门店名称"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="连锁店扩展店名"> <el-form-item label="连锁店扩展店名">
<el-input <el-input v-model.trim="form.chainName" placeholder="请输入连锁店扩展店名" style="width: 500px"></el-input>
v-model.trim="form.chainName"
placeholder="请输入连锁店扩展店名"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="门店logo"> <el-form-item label="门店logo">
<div class="img_box"> <div class="img_box">
<single-image-upload <single-image-upload style="width: 80px; height: 80px" v-model="form.logo"></single-image-upload>
style="width: 80px; height: 80px"
v-model="form.logo"
></single-image-upload>
</div> </div>
</el-form-item> </el-form-item>
<!-- <el-form-item label="门店照片"> <!-- <el-form-item label="门店照片">
@@ -38,37 +27,21 @@
<el-form-item label="门店收款码"> <el-form-item label="门店收款码">
<div class="img_box"> <div class="img_box">
<canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas> <canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas>
<el-button <el-button size="small" plain v-if="form.paymentQrcode" @click="downloadCanvas(form.paymentQrcode)">
size="small"
plain
v-if="form.paymentQrcode"
@click="downloadCanvas(form.paymentQrcode)"
>
下载 下载
</el-button> </el-button>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="微信二维码"> <el-form-item label="微信二维码">
<div class="img_box"> <div class="img_box">
<single-image-upload <single-image-upload style="width: 80px; height: 80px" v-model="form.shopQrcode"></single-image-upload>
style="width: 80px; height: 80px"
v-model="form.shopQrcode"
></single-image-upload>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="店铺小程序码"> <el-form-item label="店铺小程序码">
<div class="img_box"> <div class="img_box">
<el-image <el-image :src="form.smallQrcode || img_download_error" fit="contain"
:src="form.smallQrcode || img_download_error" style="width: 80px; height: 80px"></el-image>
fit="contain" <el-button size="small" plain v-if="form.shopQrcode" @click="downloadImgHandle(form.smallQrcode)">
style="width: 80px; height: 80px"
></el-image>
<el-button
size="small"
plain
v-if="form.shopQrcode"
@click="downloadImgHandle(form.smallQrcode)"
>
下载 下载
</el-button> </el-button>
</div> </div>
@@ -92,11 +65,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> --> </el-form-item> -->
<el-form-item label="联系电话" prop="phone"> <el-form-item label="联系电话" prop="phone">
<el-input <el-input v-model.trim="form.phone" placeholder="请输入联系电话" style="width: 500px"></el-input>
v-model.trim="form.phone"
placeholder="请输入联系电话"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<!-- <el-form-item label="外卖起送金额"> <!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right" <el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
@@ -122,83 +91,43 @@
<div style="color: #999">准确的定位便于用户导航到店铺</div> <div style="color: #999">准确的定位便于用户导航到店铺</div>
</el-form-item> </el-form-item>
<el-form-item label="门店详细地址"> <el-form-item label="门店详细地址">
<el-input <el-input type="textarea" v-model.trim="form.address" placeholder="请输入门店详细地址" style="width: 500px"></el-input>
type="textarea"
v-model.trim="form.address"
placeholder="请输入门店详细地址"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="营业时间"> <el-form-item label="营业时间">
<div class="u-flex gap-2" style="width: 50%"> <div class="u-flex gap-2" style="width: 50%">
<el-select v-model="form.businessStartDay" placeholder="周几开始"> <el-select v-model="form.businessStartDay" placeholder="周几开始">
<el-option <el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
</el-select> </el-select>
<el-select v-model="form.businessEndDay" placeholder="周几结束"> <el-select v-model="form.businessEndDay" placeholder="周几结束">
<el-option <el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
</el-select> </el-select>
<el-time-picker <el-time-picker placeholder="起始时间" v-model="startTime" :picker-options="{
placeholder="起始时间" selectableRange: '00:00:00 - 23:59:59',
v-model="startTime" format: 'HH:mm',
:picker-options="{ }" format="HH:mm" value-format="HH:mm"></el-time-picker>
selectableRange: '00:00:00 - 23:59:59', <el-time-picker placeholder="结束时间" v-model="endTime" :picker-options="{
format: 'HH:mm', selectableRange: '00:00:00 - 23:59:59',
}" }" format="HH:mm" value-format="HH:mm"></el-time-picker>
format="HH:mm"
value-format="HH:mm"
></el-time-picker>
<el-time-picker
placeholder="结束时间"
v-model="endTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59',
}"
format="HH:mm"
value-format="HH:mm"
></el-time-picker>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="桌位费/位/元"> <el-form-item label="桌位费/位/元">
<el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" /> <el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" />
<!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> --> <!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> -->
<el-switch <el-switch class="u-m-l-10" v-model.trim="form.isTableFee" :active-value="1" :inactive-value="0"
class="u-m-l-10" active-text="免餐位费"></el-switch>
v-model.trim="form.isTableFee"
:active-value="1"
:inactive-value="0"
active-text="免餐位费"
></el-switch>
</el-form-item> </el-form-item>
<!-- <el-form-item label="是否开启8折活动"> <!-- <el-form-item label="是否开启8折活动">
<el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch> <el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch>
<div style="color: #999;">是否允许用户在小程序端支付订单</div> <div style="color: #999;">是否允许用户在小程序端支付订单</div>
</el-form-item> --> </el-form-item> -->
<el-form-item label="是否启用商品会员价"> <!-- <el-form-item label="是否启用商品会员价">
<el-switch <el-switch v-model.trim="form.isMemberPrice" :active-value="1" ::inactive-value="0"></el-switch>
v-model.trim="form.isMemberPrice" <div style="color: #999;">是否允许用户在小程序端支付订单</div>
:active-value="1"
::inactive-value="0"
></el-switch>
<!-- <div style="color: #999;">是否允许用户在小程序端支付订单</div> -->
</el-form-item> </el-form-item>
<el-form-item label="是否开启会员余额支付"> <el-form-item label="是否开启会员余额支付">
<el-switch <el-switch v-model.trim="form.isAccountPay" :active-value="1" :inactive-value="0"></el-switch>
v-model.trim="form.isAccountPay" <div style="color: #999;">是否允许用户在小程序端支付订单</div>
:active-value="1" </el-form-item> -->
:inactive-value="0"
></el-switch>
<!-- <div style="color: #999;">是否允许用户在小程序端支付订单</div> -->
</el-form-item>
<!-- <el-form-item label="结算类型"> <!-- <el-form-item label="结算类型">
<el-radio-group v-model="form.settleType"> <el-radio-group v-model="form.settleType">
<el-radio :label="0">今日</el-radio> <el-radio :label="0">今日</el-radio>
@@ -219,20 +148,11 @@
</el-time-picker> </el-time-picker>
</el-form-item> --> </el-form-item> -->
<el-form-item label="店铺简介"> <el-form-item label="店铺简介">
<el-input <el-input type="textarea" v-model.trim="form.detail" placeholder="请输入店铺简介" style="width: 500px"></el-input>
type="textarea"
v-model.trim="form.detail"
placeholder="请输入店铺简介"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="台桌预订短信"> <el-form-item label="台桌预订短信">
<el-input <el-input type="textarea" v-model.trim="form.bookingSms" placeholder="请输入台桌预订短信"
type="textarea" style="width: 500px"></el-input>
v-model.trim="form.bookingSms"
placeholder="请输入台桌预订短信"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
@@ -249,24 +169,11 @@
</el-form> </el-form>
</div> </div>
<ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress> <ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress>
<el-dialog <el-dialog v-model="showUpload" :close-on-click-modal="false" append-to-body width="500px"
v-model="showUpload" @close="showUpload = false">
:close-on-click-modal="false" <el-upload :before-remove="handleBeforeRemove" :on-success="handleSuccess" :on-error="handleError"
append-to-body :file-list="fileList" :headers="headers" :action="qiNiuUploadApi" :limit="1" list-type="picture"
width="500px" class="upload-demo">
@close="showUpload = false"
>
<el-upload
:before-remove="handleBeforeRemove"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:headers="headers"
:action="qiNiuUploadApi"
:limit="1"
list-type="picture"
class="upload-demo"
>
<el-button size="small" type="primary">点击上传</el-button> <el-button size="small" type="primary">点击上传</el-button>
<template #tip> <template #tip>
<div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div> <div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
@@ -465,7 +372,6 @@ export default {
...this.form, ...this.form,
eatModel: this.form.eatModel.join(","), eatModel: this.form.eatModel.join(","),
}); });
this.formLoading = false;
ElMessage.success({ ElMessage.success({
title: "成功", title: "成功",
message: "提交成功", message: "提交成功",
@@ -473,7 +379,8 @@ export default {
setTimeout(() => { setTimeout(() => {
// location.reload(); // location.reload();
}, 1000); }, 1000);
} catch (error) {} } catch (error) { }
this.formLoading = false;
} }
}); });
}, },
@@ -487,7 +394,7 @@ export default {
handleBeforeRemove(file, fileList) { handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) { for (let i = 0; i < this.files.length; i++) {
if (this.files[i].uid === file.uid) { if (this.files[i].uid === file.uid) {
crudQiNiu.del([this.files[i].id]).then((res) => {}); crudQiNiu.del([this.files[i].id]).then((res) => { });
return true; return true;
} }
} }

View File

@@ -30,10 +30,7 @@
<div class="isSeatFee img u-line-1 u-flex u-col-center u-row-center" v-if="isSeatFee"> <div class="isSeatFee img u-line-1 u-flex u-col-center u-row-center" v-if="isSeatFee">
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
</div> </div>
<div <div class="isSeatFee img u-line-1 u-flex u-col-center u-row-center" v-else-if="item.is_temporary">
class="isSeatFee img u-line-1 u-flex u-col-center u-row-center"
v-else-if="item.is_temporary"
>
<span>临时菜</span> <span>临时菜</span>
</div> </div>
<img v-else :src="item.coverImg" class="" alt="" /> <img v-else :src="item.coverImg" class="" alt="" />
@@ -62,7 +59,9 @@
<div class="note" v-if="item.remark">备注:{{ item.remark }}</div> <div class="note" v-if="item.remark">备注:{{ item.remark }}</div>
<div class="note flex" v-else> <div class="note flex" v-else>
<span>备注:</span> <span>备注:</span>
<el-icon v-if="!isOld" @click="editNote" color="#999"><EditPen /></el-icon> <el-icon v-if="!isOld" @click="editNote" color="#999">
<EditPen />
</el-icon>
</div> </div>
</div> </div>
<div class="note" v-if="placeNum != 0 && item.note">备注:{{ item.remark || "" }}</div> <div class="note" v-if="placeNum != 0 && item.note">备注:{{ item.remark || "" }}</div>
@@ -75,17 +74,13 @@
<div class="" v-else>X{{ item.number }}</div> <div class="" v-else>X{{ item.number }}</div>
<div class="absolute" v-if="canChangeNumber"> <div class="absolute" v-if="canChangeNumber">
<div class="order-input-number"> <div class="order-input-number">
<el-icon color="#d8d8d8" size="22" @click="changeOrderNumber(-1)"><Remove /></el-icon> <el-icon color="#d8d8d8" size="22" @click="changeOrderNumber(-1)">
<Remove />
</el-icon>
<div style="width: 60px; margin: 0 4px" class="number-box"> <div style="width: 60px; margin: 0 4px" class="number-box">
<el-input <el-input :min="0" type="number" @input="cartGoodsNumberInput" @change="cartGoodsNumberChange"
:min="0" v-model="number" placeholder="0"></el-input>
type="number"
@input="cartGoodsNumberInput"
@change="cartGoodsNumberChange"
v-model="number"
placeholder="0"
></el-input>
</div> </div>
<el-icon color="#22bf64" size="22" @click="changeOrderNumber(1)"> <el-icon color="#22bf64" size="22" @click="changeOrderNumber(1)">
<CirclePlusFilled /> <CirclePlusFilled />
@@ -93,7 +88,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="color-333 total-price"> <div class="color-333 total-price">
<template v-if="item.is_gift || item.status == 'return'"> <template v-if="item.is_gift || item.status == 'return'">
<div>0</div> <div>0</div>
@@ -103,28 +97,32 @@
</div> </div>
</template> </template>
<!-- 单品改价 --> <!-- 单品改价 -->
<template <template v-else-if="
v-else-if=" item.discount_sale_amount * 1 > 0 &&
item.discount_sale_amount * 1 > 0 && discount_before_price != allPrice &&
discount_before_price != allPrice && !item.is_temporary
!item.is_temporary ">
"
>
<div>¥{{ to2(allPrice) }}</div> <div>¥{{ to2(allPrice) }}</div>
<div class="free-price"> <div class="free-price">
<span>¥{{ to2(discount_before_price) }}</span> <span>¥{{ to2(discount_before_price) }}</span>
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div> <div v-if="item.isLimitDiscount || item.is_time_discount">
<div>
¥{{ to2(item.limitDiscountPrice) }}
</div>
<div class="free-price">
<span>¥{{ to2(item.salePrice) }}</span>
</div>
</div>
<div v-else>
<div v-if="useVipPrice && vipAllPrice * 1 != allPrice * 1"> <div v-if="useVipPrice && vipAllPrice * 1 != allPrice * 1">
¥{{ to2(vipAllPrice) }} ¥{{ to2(vipAllPrice) }}
</div> </div>
<div <div :class="{
:class="{ 'free-price': useVipPrice && vipAllPrice != allPrice,
'free-price': useVipPrice && vipAllPrice != allPrice, }">
}"
>
<span>¥{{ to2(allPrice) }}</span> <span>¥{{ to2(allPrice) }}</span>
</div> </div>
</div> </div>
@@ -349,7 +347,7 @@ onMounted(() => {
flex-direction: column; flex-direction: column;
gap: 2px; gap: 2px;
> span { >span {
display: block; display: block;
width: 18px; width: 18px;
height: 18px; height: 18px;
@@ -367,6 +365,7 @@ onMounted(() => {
.pack { .pack {
background: #35ac6a; background: #35ac6a;
.number { .number {
background: #f56c6c; background: #f56c6c;
color: #fff; color: #fff;
@@ -379,8 +378,10 @@ onMounted(() => {
border-radius: 50%; border-radius: 50%;
} }
} }
.tui { .tui {
position: relative; position: relative;
.number { .number {
background: #f56c6c; background: #f56c6c;
color: #fff; color: #fff;
@@ -393,6 +394,7 @@ onMounted(() => {
border-radius: 50%; border-radius: 50%;
} }
} }
.da { .da {
background: #35ac6a; background: #35ac6a;
} }

View File

@@ -1,58 +1,34 @@
<template> <template>
<div class="goods-item"> <div class="goods-item">
<el-image <el-image v-if="item.coverImg" class="goods-image"
v-if="item.coverImg" :src="item.coverImg + '?x-oss-process=image/resize,m_lfit,w_100,h_100'" fit="cover"></el-image>
class="goods-image"
:src="item.coverImg + '?x-oss-process=image/resize,m_lfit,w_100,h_100'"
fit="cover"
></el-image>
<div class="info" @click="itemClick"> <div class="info" @click="itemClick">
<div class="name u-flex u-flex-wrap"> <div class="name u-flex u-flex-wrap">
<span class="weight" v-if="item.type == 'weight'">称重</span> <span class="weight" v-if="item.type == 'weight'">称重</span>
<span class="u-line-3">{{ item.name }}</span> <span class="u-line-3">{{ item.name }}</span>
</div> </div>
<div class="">{{ item.lowPrice }}</div> <div class="" v-if="item.isLimitDiscount">
<span class="o_price">{{ item.lowPrice }}</span>
<span>{{ item.limitDiscountPrice }}</span>
</div>
<div v-else>{{ item.lowPrice }}</div>
</div> </div>
<div <div class="status" v-if="
class="status" item.isSoldStock ||
v-if=" !item.isSale ||
item.isSoldStock || !item.isSaleTime ||
!item.isSale || (item.isStock && item.stockNumber * 1 <= 0)
!item.isSaleTime || ">
(item.isStock && item.stockNumber * 1 <= 0) <svg-icon @click="ElMessage.error('该商品已下架')" v-if="!item.isSale" iconClass="yi-xiajia" color="#fff"
" size="60"></svg-icon>
> <svg-icon @click="
<svg-icon ElMessage.error('该商品不在可售时间内') ||
@click="ElMessage.error('该商品已下架')" isProductAvailable(item.days, item.startTime, item.endTime)
v-if="!item.isSale" " v-else-if="!item.isSaleTime" iconClass="no-sale" color="#fff" size="60"></svg-icon>
iconClass="yi-xiajia" <svg-icon @click="ElMessage.error('该商品已售罄')" v-else-if="item.isSoldStock" iconClass="shouqing" color="#fff"
color="#fff" size="60"></svg-icon>
size="60" <svg-icon @click="ElMessage.error('库存不足')" v-else-if="item.isStock && item.stockNumber * 1 <= 0"
></svg-icon> iconClass="stock_null" color="#fff" size="60"></svg-icon>
<svg-icon
@click="
ElMessage.error('该商品不在可售时间内') ||
isProductAvailable(item.days, item.startTime, item.endTime)
"
v-else-if="!item.isSaleTime"
iconClass="no-sale"
color="#fff"
size="60"
></svg-icon>
<svg-icon
@click="ElMessage.error('该商品已售罄')"
v-else-if="item.isSoldStock"
iconClass="shouqing"
color="#fff"
size="60"
></svg-icon>
<svg-icon
@click="ElMessage.error('库存不足')"
v-else-if="item.isStock && item.stockNumber * 1 <= 0"
iconClass="stock_null"
color="#fff"
size="60"
></svg-icon>
</div> </div>
</div> </div>
</template> </template>
@@ -105,10 +81,12 @@ function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
.goods-image { .goods-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.info { .info {
position: absolute; position: absolute;
box-sizing: border-box; box-sizing: border-box;
@@ -122,6 +100,7 @@ function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) {
background-color: rgba(46, 46, 46, 0.38); background-color: rgba(46, 46, 46, 0.38);
z-index: 1; z-index: 1;
} }
.status { .status {
position: absolute; position: absolute;
box-sizing: border-box; box-sizing: border-box;
@@ -134,9 +113,11 @@ function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) {
background-color: rgba($color: #000000, $alpha: 0.5); background-color: rgba($color: #000000, $alpha: 0.5);
} }
} }
:deep(.svg-icon) { :deep(.svg-icon) {
margin-right: 0; margin-right: 0;
} }
.weight { .weight {
height: 15px; height: 15px;
background: linear-gradient(124deg, rgb(115, 201, 105) 6%, rgb(39, 146, 27) 93%); background: linear-gradient(124deg, rgb(115, 201, 105) 6%, rgb(39, 146, 27) 93%);
@@ -147,4 +128,10 @@ function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) {
padding: 0px 2px; padding: 0px 2px;
margin-right: 2px; margin-right: 2px;
} }
.o_price {
font-size: 10px;
text-decoration: line-through;
opacity: .8;
}
</style> </style>

View File

@@ -42,22 +42,11 @@
<span class="u-font-14">部分抵扣</span> <span class="u-font-14">部分抵扣</span>
</el-radio> --> </el-radio> -->
</el-radio-group> </el-radio-group>
<el-input-number <el-input-number class="u-m-l-10" v-if="score.sel != -1" v-model="usePointsNumber" step-strictly
class="u-m-l-10" :step="pointsRes.equivalentPoints" placeholder="请输入积分抵扣数量" :min="pointsRes.minDeductionPoints"
v-if="score.sel != -1" :max="pointsRes.maxUsablePoints" @change="pointsToMoney"></el-input-number>
v-model="usePointsNumber"
step-strictly
:step="pointsRes.equivalentPoints"
placeholder="请输入积分抵扣数量"
:min="pointsRes.minDeductionPoints"
:max="pointsRes.maxUsablePoints"
@change="pointsToMoney"
></el-input-number>
</div> </div>
<p <p class="u-font-14 color-666 u-m-t-10" v-if="pointsRes.unusableReason && !pointsRes.usable">
class="u-font-14 color-666 u-m-t-10"
v-if="pointsRes.unusableReason && !pointsRes.usable"
>
<span class="color-red">*</span> <span class="color-red">*</span>
<span>{{ pointsRes.unusableReason }}</span> <span>{{ pointsRes.unusableReason }}</span>
</p> </p>
@@ -70,7 +59,9 @@
<span class="u-font-14 font-bold u-m-r-20">团购代金券</span> <span class="u-font-14 font-bold u-m-r-20">团购代金券</span>
<div class="u-flex my-select"> <div class="u-flex my-select">
<span class="u-m-r-10">代金券名称</span> <span class="u-m-r-10">代金券名称</span>
<el-icon><ArrowDown /></el-icon> <el-icon>
<ArrowDown />
</el-icon>
</div> </div>
<svg-icon iconClass="scan" size="30" class="u-m-l-10 cur-pointer"></svg-icon> <svg-icon iconClass="scan" size="30" class="u-m-l-10 cur-pointer"></svg-icon>
</div> </div>
@@ -78,7 +69,9 @@
<span class="u-font-14 font-bold u-m-r-20">优惠券</span> <span class="u-font-14 font-bold u-m-r-20">优惠券</span>
<div class="u-flex my-select" @click="openCoupon"> <div class="u-flex my-select" @click="openCoupon">
<span class="u-m-r-10">选择优惠券</span> <span class="u-m-r-10">选择优惠券</span>
<el-icon><ArrowDown /></el-icon> <el-icon>
<ArrowDown />
</el-icon>
</div> </div>
</div> </div>
<div class="u-m-t-20" v-if="quansSelArr.length > 0"> <div class="u-m-t-20" v-if="quansSelArr.length > 0">
@@ -121,16 +114,13 @@
<div class="u-m-t-30"> <div class="u-m-t-30">
<p class="u-font-16 font-bold u-m-r-20 font-bold u-flex">选择支付方式</p> <p class="u-font-16 font-bold u-m-r-20 font-bold u-flex">选择支付方式</p>
<div class="u-m-t-20"> <div class="u-m-t-20">
<el-button <div>
v-for="(item, index) in payTypes.list" <el-button v-for="(item, index) in payTypes.list" :key="index" size="large"
:key="index" :type="index == payTypes.sel ? 'primary' : ''" :disabled="canUsePayType(item)"
size="large" @click="changePayType(index)">
:type="index == payTypes.sel ? 'primary' : ''" {{ item.payName }}
:disabled="canUsePayType(item)" </el-button>
@click="changePayType(index)" </div>
>
{{ item.payName }}
</el-button>
</div> </div>
<div class="u-m-t-20"> <div class="u-m-t-20">
<el-button type="primary" size="large" @click="nowPayClick()">立即支付</el-button> <el-button type="primary" size="large" @click="nowPayClick()">立即支付</el-button>
@@ -206,26 +196,17 @@
</div> </div>
</div> </div>
<!-- 扫码 --> <!-- 扫码 -->
<scanPay <scanPay ref="refScanPay" :order="orderInfo" @confirm="refScanPayConfirm" @paysuccess="paysuccess"></scanPay>
ref="refScanPay"
:order="orderInfo"
@confirm="refScanPayConfirm"
@paysuccess="paysuccess"
></scanPay>
<!-- 打折 --> <!-- 打折 -->
<discount ref="refDiscount" @confirm="discountConfirm"></discount> <discount ref="refDiscount" @confirm="discountConfirm"></discount>
<!-- 优惠券 --> <!-- 优惠券 -->
<popup-coupon ref="refCoupon" :user="user" @confirm="refCouponConfirm"></popup-coupon> <popup-coupon ref="refCoupon" :user="user" @confirm="refCouponConfirm"></popup-coupon>
<!-- 挂账 --> <!-- 挂账 -->
<chooseGuaZahng <chooseGuaZahng ref="refGuaZhang" :payMoney="currentpayMoney" @confirm="refGuaZhangConfirm"></chooseGuaZahng>
ref="refGuaZhang"
:payMoney="currentpayMoney"
@confirm="refGuaZhangConfirm"
></chooseGuaZahng>
</div> </div>
</template> </template>
<script setup> <script setup>
import * as UTILS from "@/utils/coupon-utils.js"; import * as UTILS from "@/utils/coupon-utils.js";
import * as quanUtil from "../quan_util.js"; import * as quanUtil from "../quan_util.js";
@@ -237,7 +218,7 @@ const shopUser = useUserStore();
const carts = useCartsStore(); const carts = useCartsStore();
import popupCoupon from "./popup-coupon.vue"; import popupCoupon from "./popup-coupon.vue";
import PointsApi from "@/api/account/points"; import PointsApi from "@/api/account/points";
import limitTimeDiscountApi from '@/api/market/limitTimeDiscount.js'
import payTypeApi from "@/api/account/payType"; import payTypeApi from "@/api/account/payType";
import payApi from "@/api/order/pay"; import payApi from "@/api/order/pay";
import scanPay from "./scan-pay.vue"; import scanPay from "./scan-pay.vue";
@@ -357,7 +338,7 @@ function discountShow(e) {
const props = defineProps({ const props = defineProps({
table: { table: {
type: Object, type: Object,
default: () => {}, default: () => { },
}, },
user: { user: {
type: Object, type: Object,
@@ -371,7 +352,7 @@ const props = defineProps({
}, },
orderInfo: { orderInfo: {
type: Object, type: Object,
default: () => {}, default: () => { },
}, },
}); });
@@ -488,6 +469,26 @@ function changePayType(i) {
refScanPayOpen(payType); refScanPayOpen(payType);
} }
// 根据用户id获取新客立减金额返回null代表不可用
const newCustomerDiscount = ref(null)
async function getConsumeDiscountAjax() {
try {
if (props.user.id) {
newCustomerDiscount.value = await limitTimeDiscountApi.getConsumeDiscount({
shopId: localStorage.getItem("shopId"),
shopUserId: props.user.id,
orderId: props.orderInfo.id
})
console.log('根据用户id获取新客立减金额返回null代表不可用', newCustomerDiscount.value);
if (newCustomerDiscount.value !== null) {
carts.newUserDiscount = newCustomerDiscount.value.amount
}
}
} catch (error) {
console.log(error);
}
}
function returnPayParams() { function returnPayParams() {
console.log("carts.orderCostSummary", carts.orderCostSummary); console.log("carts.orderCostSummary", carts.orderCostSummary);
return { return {
@@ -511,6 +512,9 @@ function returnPayParams() {
couponList: carts.coupons.map((v) => v.id), couponList: carts.coupons.map((v) => v.id),
userId: props.user.userId || "", userId: props.user.userId || "",
allPack: carts.dinnerType == "take-out" ? 1 : 0, allPack: carts.dinnerType == "take-out" ? 1 : 0,
limitRate: carts.limitDiscountRes,
newCustomerDiscountId: newCustomerDiscount.value !== null ? newCustomerDiscount.value.id : '', // 新客立减Id
newCustomerDiscountAmount: newCustomerDiscount.value !== null ? newCustomerDiscount.value.amount : 0, // 新客立减金额
}, },
}; };
} }
@@ -550,7 +554,7 @@ function nowPayClick(payType) {
.then(() => { .then(() => {
payOrder("cash"); payOrder("cash");
}) })
.catch(() => {}); .catch(() => { });
return; return;
} }
if (payType == "member-account") { if (payType == "member-account") {
@@ -680,9 +684,25 @@ watch(
} }
} }
); );
// 获取当前店铺可用的限时折扣
async function getlimitTimeDiscount() {
try {
const res = await limitTimeDiscountApi.getLimitTimeDiscount({
shopId: localStorage.getItem("shopId")
})
console.log('获取当前店铺可用的限时折扣===', res);
} catch (error) {
console.log(error);
}
}
onMounted(() => { onMounted(() => {
carts.payParamsInit(); carts.payParamsInit();
getPaytype(); getPaytype();
getlimitTimeDiscount()
getConsumeDiscountAjax()
}); });
defineExpose({ defineExpose({
nowPayClick, nowPayClick,
@@ -696,6 +716,7 @@ defineExpose({
line-height: 1; line-height: 1;
cursor: pointer; cursor: pointer;
} }
.vip { .vip {
padding: 2px 5px; padding: 2px 5px;
background: #f7793d; background: #f7793d;
@@ -704,27 +725,33 @@ defineExpose({
margin-left: 10px; margin-left: 10px;
font-size: 10px; font-size: 10px;
} }
.order-box { .order-box {
display: flex; display: flex;
padding: 20px 20px; padding: 20px 20px;
background-color: #f7f7fa; background-color: #f7f7fa;
min-height: 100%; min-height: 100%;
.left, .left,
.right { .right {
flex: 1; flex: 1;
} }
.left { .left {
padding-right: 20px; padding-right: 20px;
} }
.right { .right {
border-left: 1px solid #ebebeb; border-left: 1px solid #ebebeb;
padding-left: 20px; padding-left: 20px;
.order-info { .order-info {
font-size: 14px; font-size: 14px;
.title {
} .title {}
.value {
} .value {}
.price { .price {
color: #fa5555; color: #fa5555;
font-size: 20px; font-size: 20px;
@@ -732,6 +759,7 @@ defineExpose({
} }
} }
} }
.my-select { .my-select {
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
border-radius: 4px; border-radius: 4px;

View File

@@ -1,9 +1,5 @@
<template> <template>
<div <div class="box" v-loading="!carts.isLinkFinshed" element-loading-text="购物车连接初始化中,请稍等……">
class="box"
v-loading="!carts.isLinkFinshed"
element-loading-text="购物车连接初始化中请稍等"
>
<div class="content"> <div class="content">
<div class="top"> <div class="top">
<div class="left u-flex u-col-center"> <div class="left u-flex u-col-center">
@@ -13,12 +9,7 @@
<el-button type="primary" v-if="!user.id">选择用户</el-button> <el-button type="primary" v-if="!user.id">选择用户</el-button>
<div v-else class="flex cur-pointer"> <div v-else class="flex cur-pointer">
<img <img v-if="user.headImg && user.headImg != 'null'" class="headimg" :src="user.headImg" alt="" />
v-if="user.headImg && user.headImg != 'null'"
class="headimg"
:src="user.headImg"
alt=""
/>
<div v-else class="headimg flex flex-x-y-center"> <div v-else class="headimg flex flex-x-y-center">
<i class="el-icon-user"></i> <i class="el-icon-user"></i>
</div> </div>
@@ -34,19 +25,11 @@
</div> </div>
</div> </div>
<el-popover placement="right" width="333" trigger="click" ref="refTable"> <el-popover placement="right" width="333" trigger="click" ref="refTable">
<el-input <el-input placeholder="请输入内容" prefix-icon="search" v-model="tableSearchText"
placeholder="请输入内容" @input="tablesearchInput"></el-input>
prefix-icon="search"
v-model="tableSearchText"
@input="tablesearchInput"
></el-input>
<div style="max-height: 398px; overflow-y: scroll" class="u-m-t-12"> <div style="max-height: 398px; overflow-y: scroll" class="u-m-t-12">
<div <div class="u-flex u-row-between u-p-t-8 table-item u-p-b-8 u-p-r-30" v-for="(item, index) in tableList"
class="u-flex u-row-between u-p-t-8 table-item u-p-b-8 u-p-r-30" :key="index" @click="tableClick(item, index)">
v-for="(item, index) in tableList"
:key="index"
@click="tableClick(item, index)"
>
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<span :style="{ color: returnTableColor(item.status) }"> <span :style="{ color: returnTableColor(item.status) }">
{{ returnTableLabel(item.status) }} {{ returnTableLabel(item.status) }}
@@ -64,14 +47,11 @@
</div> </div>
</div> </div>
<div class="right"> <div class="right">
<el-input <el-input placeholder="请输入商品名称" v-model="goods.query.name" clearable @change="getGoods">
placeholder="请输入商品名称"
v-model="goods.query.name"
clearable
@change="getGoods"
>
<template #suffix> <template #suffix>
<el-icon class="el-input__icon"><search /></el-icon> <el-icon class="el-input__icon">
<search />
</el-icon>
</template> </template>
</el-input> </el-input>
</div> </div>
@@ -81,83 +61,51 @@
<div class="diners"> <div class="diners">
<!-- 就餐类型 --> <!-- 就餐类型 -->
<el-button-group v-model="diners.sel" style="width: 100%; display: flex"> <el-button-group v-model="diners.sel" style="width: 100%; display: flex">
<el-button <el-button :class="{ active: index == diners.sel }" v-for="(item, index) in diners.list"
:class="{ active: index == diners.sel }" :disabled="dinerDisabled(item, index)" @click="changeDinersSel(index)" :key="index">
v-for="(item, index) in diners.list"
:disabled="dinerDisabled(item, index)"
@click="changeDinersSel(index)"
:key="index"
>
{{ item.label }} {{ item.label }}
</el-button> </el-button>
</el-button-group> </el-button-group>
</div> </div>
<div class="u-flex u-font-14 clear u-m-t-10 perpoles"> <div class="u-flex u-font-14 clear u-m-t-10 perpoles">
<div <div @click="showDinerNumber" class="u-flex u-p-r-14 u-m-r-14"
@click="showDinerNumber" style="border-right: 1px solid #ebebeb; line-height: 1">
class="u-flex u-p-r-14 u-m-r-14"
style="border-right: 1px solid #ebebeb; line-height: 1"
>
<span>就餐人数{{ perpole || "-" }} </span> <span>就餐人数{{ perpole || "-" }} </span>
<el-icon><ArrowRight /></el-icon> <el-icon>
<ArrowRight />
</el-icon>
</div> </div>
<el-button link type="primary" :disabled="carts.isEmpty" @click="clearCarts"> <el-button link type="primary" :disabled="carts.isEmpty" @click="clearCarts">
清空 清空
</el-button> </el-button>
</div> </div>
<!-- 购物车 --> <!-- 购物车 -->
<cartsList <cartsList @editNote="showNote(true)" @createOrder="createOrder" @hideOrder="hideOrder"
@editNote="showNote(true)" @clearOldOrder="clearOldOrder" :showOrder="showOrder" :goodsList="carts.goods" :dinerType="diners.sel"
@createOrder="createOrder" :perpole="perpole" :remark="remark" :table="table" ref="refCart"></cartsList>
@hideOrder="hideOrder"
@clearOldOrder="clearOldOrder"
:showOrder="showOrder"
:goodsList="carts.goods"
:dinerType="diners.sel"
:perpole="perpole"
:remark="remark"
:table="table"
ref="refCart"
></cartsList>
</div> </div>
<div class="center"> <div class="center">
<!-- 购物车控制操作按钮 --> <!-- 购物车控制操作按钮 -->
<Controls <Controls @noteClick="showNote" @packClick="showPack" @changePriceClick="showChangePrice"
@noteClick="showNote" @return="refReturnCartShow" @rottable="rottableShow" @changeCartNumberShow="refChangeNumberShow" />
@packClick="showPack"
@changePriceClick="showChangePrice"
@return="refReturnCartShow"
@rottable="rottableShow"
@changeCartNumberShow="refChangeNumberShow"
/>
</div> </div>
<div class="right"> <div class="right">
<template v-if="!showOrder"> <template v-if="!showOrder">
<div class="flex categoty u-col-center"> <div class="flex categoty u-col-center">
<div <div class="show_more_btn" :class="{ showAll: category.showAll }" @click="toggleShowAll">
class="show_more_btn"
:class="{ showAll: category.showAll }"
@click="toggleShowAll"
>
<div class="flex"> <div class="flex">
<div class="flex showmore"> <div class="flex showmore">
<el-icon color="#fff"><ArrowDown /></el-icon> <el-icon color="#fff">
<ArrowDown />
</el-icon>
</div> </div>
<span>{{ category.showAll ? "收起" : "展开" }}</span> <span>{{ category.showAll ? "收起" : "展开" }}</span>
</div> </div>
</div> </div>
<div class="flex categorys" :class="{ 'flex-wrap': category.showAll }"> <div class="flex categorys" :class="{ 'flex-wrap': category.showAll }">
<div <div v-for="(item, index) in category.list" :key="index" @click="changeCategoryId(item)">
v-for="(item, index) in category.list" <el-tag size="large" :type="goods.query.categoryId === item.id ? 'primary' : 'info'" effect="dark">
:key="index"
@click="changeCategoryId(item)"
>
<el-tag
size="large"
:type="goods.query.categoryId === item.id ? 'primary' : 'info'"
effect="dark"
>
{{ item.name }} {{ item.name }}
</el-tag> </el-tag>
</div> </div>
@@ -166,29 +114,19 @@
<div v-if="carts.goods.length <= 0" class="no-goods">未找到相关商品</div> <div v-if="carts.goods.length <= 0" class="no-goods">未找到相关商品</div>
<div class="goods-list"> <div class="goods-list">
<div class="lingshicai" @click="showaddLingShiCai"> <div class="lingshicai" @click="showaddLingShiCai">
<el-icon size="26"><Plus /></el-icon> <el-icon size="26">
<Plus />
</el-icon>
<div class="u-m-t-10">临时菜</div> <div class="u-m-t-10">临时菜</div>
</div> </div>
<GoodsItem <GoodsItem :item="item" @itemClick="goodsClick(item)" v-for="item in carts.goods" :key="item.id">
:item="item" </GoodsItem>
@itemClick="goodsClick(item)"
v-for="item in carts.goods"
:key="item.id"
></GoodsItem>
</div> </div>
</template> </template>
<!-- 订单信息展示 --> <!-- 订单信息展示 -->
<Order <Order ref="refOrder" :orderInfo="carts.oldOrder" @chooseUser="showChooseUser" @paysuccess="refresh"
ref="refOrder" :table="table" :perpole="perpole" v-else :user="user"></Order>
:orderInfo="carts.oldOrder"
@chooseUser="showChooseUser"
@paysuccess="refresh"
:table="table"
:perpole="perpole"
v-else
:user="user"
></Order>
</div> </div>
</div> </div>
</div> </div>
@@ -203,11 +141,7 @@
<!-- 临时菜 --> <!-- 临时菜 -->
<addLingShiCai ref="refAddLingShiCai" @confirm="addLingShiCaiConfirm"></addLingShiCai> <addLingShiCai ref="refAddLingShiCai" @confirm="addLingShiCaiConfirm"></addLingShiCai>
<!-- 改价 --> <!-- 改价 -->
<changePrice <changePrice ref="refChangePrice" :useVipPrice="carts.useVipPrice" @confirm="changePriceConfirm"></changePrice>
ref="refChangePrice"
:useVipPrice="carts.useVipPrice"
@confirm="changePriceConfirm"
></changePrice>
<!-- 称重商品 --> <!-- 称重商品 -->
<change-weight ref="refChangeWeight" @confirm="changeWeightConfirm"></change-weight> <change-weight ref="refChangeWeight" @confirm="changeWeightConfirm"></change-weight>
<!-- 可选套餐 --> <!-- 可选套餐 -->
@@ -421,6 +355,7 @@ async function createOrder(key) {
placeNum: carts.oldOrder.placeNum * 1 + 1, placeNum: carts.oldOrder.placeNum * 1 + 1,
waitCall: false, waitCall: false,
vipPrice: user.value.id && user.value.isVip, vipPrice: user.value.id && user.value.isVip,
limitRate: carts.limitDiscountRes
}); });
clearTimeout(loadingTimer); clearTimeout(loadingTimer);
loading.close(); loading.close();
@@ -553,7 +488,7 @@ async function getTableDetail(params) {
const res = await tableApi.get(params); const res = await tableApi.get(params);
return res; return res;
} }
function tablesearchInput() {} function tablesearchInput() { }
//返回桌台状态颜色 //返回桌台状态颜色
function returnTableColor(key) { function returnTableColor(key) {
const item = $status[key]; const item = $status[key];
@@ -773,11 +708,15 @@ function clearCarts() {
}); });
} }
function addCarts(item, isWeight = false) { function addCarts(item, isWeight = false) {
console.log('index.addCarts===', item);
if (isWeight) { if (isWeight) {
carts.add({ pack_number: diners.sel ? 1 : 0, ...item }); carts.add({ pack_number: diners.sel ? 1 : 0, ...item, is_time_discount: item.isLimitDiscount ? 1 : 0 });
return; return;
} }
carts.add({ pack_number: diners.sel ? item.number : 0, ...item }); carts.add({ pack_number: diners.sel ? item.number : 0, ...item, is_time_discount: item.isLimitDiscount ? 1 : 0 });
} }
watch( watch(
@@ -804,11 +743,11 @@ onMounted(async () => {
// 获取历史订单数据 // 获取历史订单数据
const res = id const res = id
? await orderApi.getHistoryList({ ? await orderApi.getHistoryList({
orderId: id, orderId: id,
}) })
: await orderApi.getHistoryList({ : await orderApi.getHistoryList({
tableCode, tableCode,
}); });
const noPayStatus = { const noPayStatus = {
cancelled: "订单已取消", cancelled: "订单已取消",
done: "订单已关闭", done: "订单已关闭",
@@ -893,20 +832,24 @@ watch(
<style lang="scss" scoped> <style lang="scss" scoped>
$pl: 30px; $pl: 30px;
.box { .box {
padding: 20px 20px 10px 20px; padding: 20px 20px 10px 20px;
height: 100%; height: 100%;
.content { .content {
background-color: #fff; background-color: #fff;
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
padding: 20px 20px 20px 0; padding: 20px 20px 20px 0;
.top { .top {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding-bottom: 20px; padding-bottom: 20px;
border-bottom: 1px solid #ebebeb; border-bottom: 1px solid #ebebeb;
margin-left: $pl; margin-left: $pl;
.left { .left {
flex: 1; flex: 1;
@@ -917,39 +860,48 @@ $pl: 30px;
white-space: nowrap; white-space: nowrap;
} }
} }
.right { .right {
flex: 1; flex: 1;
} }
} }
.diancan { .diancan {
padding-top: 10px; padding-top: 10px;
display: flex; display: flex;
height: 100%; height: 100%;
max-height: calc(100vh - 256px); max-height: calc(100vh - 256px);
.left { .left {
width: 350px; width: 350px;
padding-right: 14px; padding-right: 14px;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.diners { .diners {
padding-left: $pl; padding-left: $pl;
} }
.perpoles { .perpoles {
padding-left: $pl; padding-left: $pl;
} }
} }
.right { .right {
flex: 1; flex: 1;
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 4px; width: 4px;
} }
} }
.center { .center {
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 4px; width: 4px;
} }
@@ -964,18 +916,22 @@ $pl: 30px;
background: rgba(24, 144, 255, 0.1) !important; background: rgba(24, 144, 255, 0.1) !important;
z-index: 10; z-index: 10;
} }
:deep(.diners .el-button-group) { :deep(.diners .el-button-group) {
width: 100%; width: 100%;
display: flex; display: flex;
} }
:deep(.diners .el-button-group .el-button) { :deep(.diners .el-button-group .el-button) {
flex: 1; flex: 1;
} }
:deep(.categorys .el-tag--plain.el-tag--info) { :deep(.categorys .el-tag--plain.el-tag--info) {
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
color: #000; color: #000;
font-weight: 600; font-weight: 600;
} }
:deep(.categorys .el-tag) { :deep(.categorys .el-tag) {
min-width: 80px; min-width: 80px;
height: 38px; height: 38px;
@@ -991,6 +947,7 @@ $pl: 30px;
margin: 0 10px 10px 0; margin: 0 10px 10px 0;
cursor: pointer; cursor: pointer;
} }
.categoty { .categoty {
position: sticky; position: sticky;
top: 0; top: 0;
@@ -1036,20 +993,24 @@ $pl: 30px;
} }
} }
} }
.goods-list { .goods-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 15px; gap: 15px;
flex: 1; flex: 1;
} }
:deep(.carts) { :deep(.carts) {
padding-left: $pl; padding-left: $pl;
} }
:deep(.left) { :deep(.left) {
.bottom { .bottom {
padding-left: $pl; padding-left: $pl;
} }
} }
.lingshicai { .lingshicai {
width: 100px; width: 100px;
height: 100px; height: 100px;
@@ -1064,11 +1025,13 @@ $pl: 30px;
border: 1px solid #dddfe6; border: 1px solid #dddfe6;
color: #999; color: #999;
} }
:deep(.el-tag--dark.el-tag--info) { :deep(.el-tag--dark.el-tag--info) {
background-color: #fff; background-color: #fff;
color: #000; color: #000;
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
} }
.table-item { .table-item {
cursor: pointer; cursor: pointer;
transition: 0.2s ease-in-out; transition: 0.2s ease-in-out;
@@ -1077,12 +1040,15 @@ $pl: 30px;
background: #f4f9ff; background: #f4f9ff;
} }
} }
.no-goods { .no-goods {
color: #999; color: #999;
} }
.choose-user { .choose-user {
margin-right: 10px; margin-right: 10px;
} }
.headimg { .headimg {
width: 34px; width: 34px;
height: 34px; height: 34px;
@@ -1090,6 +1056,7 @@ $pl: 30px;
border-radius: 2px; border-radius: 2px;
margin-right: 6px; margin-right: 6px;
} }
.vip { .vip {
padding: 2px 5px; padding: 2px 5px;
background: #f7793d; background: #f7793d;

View File

@@ -1,35 +1,14 @@
<template> <template>
<el-dialog <el-dialog :title="form.id ? '编辑台桌' : '添加台桌'" v-model="dialogVisible" @open="tbShopAreaGet" @close="reset">
:title="form.id ? '编辑台桌' : '添加台桌'" <el-form ref="refForm" :model="form" :rules="rules" label-width="120px" label-position="left">
v-model="dialogVisible"
@open="tbShopAreaGet"
@close="reset"
>
<el-form
ref="refForm"
:model="form"
:rules="rules"
label-width="120px"
label-position="left"
>
<el-form-item label="选择区域" prop="areaId"> <el-form-item label="选择区域" prop="areaId">
<el-select v-model="form.areaId" placeholder="请选择区域"> <el-select v-model="form.areaId" placeholder="请选择区域">
<el-option <el-option :label="item.name" :value="item.id" v-for="item in areaList" :key="item.id"></el-option>
:label="item.name"
:value="item.id"
v-for="item in areaList"
:key="item.id"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="台桌状态" prop="status" v-if="form.id"> <el-form-item label="台桌状态" prop="status" v-if="form.id">
<el-select v-model="form.status" placeholder="请选择台桌状态"> <el-select v-model="form.status" placeholder="请选择台桌状态">
<el-option <el-option :label="item.label" :value="item.value" v-for="item in statusList" :key="item.value"></el-option>
:label="item.label"
:value="item.value"
v-for="item in statusList"
:key="item.value"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="台桌标识" prop="sign" v-if="!form.id"> <el-form-item label="台桌标识" prop="sign" v-if="!form.id">
@@ -41,29 +20,18 @@
<div class="u-flex u-m-l-30"> <div class="u-flex u-m-l-30">
<div class="u-flex"> <div class="u-flex">
<div class="u-m-r-4">起始</div> <div class="u-m-r-4">起始</div>
<el-input <el-input v-model="form.start" style="width: 57px" placeholder="请输入起始值"></el-input>
v-model="form.start"
style="width: 57px"
placeholder="请输入起始值"
></el-input>
</div> </div>
<div <div style="
style="
background-color: #d9d9d9; background-color: #d9d9d9;
height: 1px; height: 1px;
width: 32px; width: 32px;
border-radius: 1px; border-radius: 1px;
" " class="u-m-l-16 u-m-r-16"></div>
class="u-m-l-16 u-m-r-16"
></div>
<div class="u-flex"> <div class="u-flex">
<span class="u-m-r-4">结束</span> <span class="u-m-r-4">结束</span>
<el-input <el-input v-model="form.end" style="width: 57px" placeholder="请输入结束值"></el-input>
v-model="form.end"
style="width: 57px"
placeholder="请输入结束值"
></el-input>
</div> </div>
</div> </div>
</div> </div>
@@ -72,31 +40,19 @@
<el-input v-model="form.name" placeholder="请输入台桌名称"></el-input> <el-input v-model="form.name" placeholder="请输入台桌名称"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="客座数"> <el-form-item label="客座数">
<el-input-number <el-input-number v-model="form.maxCapacity" :min="0" controls-position="right"></el-input-number>
v-model="form.maxCapacity"
:min="0"
controls-position="right"
></el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="清台管理"> <!-- <el-form-item label="清台管理">
<el-radio-group v-model="form.autoClear"> <el-radio-group v-model="form.autoClear">
<el-radio-button :value="0">手动清台</el-radio-button> <el-radio-button :value="0">手动清台</el-radio-button>
<el-radio-button :value="1">自动清台</el-radio-button> <el-radio-button :value="1">自动清台</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item> -->
<el-form-item v-if="form.id" label="网络预定开关"> <el-form-item v-if="form.id" label="网络预定开关">
<el-switch <el-switch v-model="form.isPredate" :active-value="1" :inactive-value="2"></el-switch>
v-model="form.isPredate"
:active-value="1"
:inactive-value="2"
></el-switch>
</el-form-item> </el-form-item>
<el-form-item v-if="form.id" label="网络预定台桌支付金额"> <el-form-item v-if="form.id" label="网络预定台桌支付金额">
<el-input-number <el-input-number v-model="form.predateAmount" :min="0" controls-position="right"></el-input-number>
v-model="form.predateAmount"
:min="0"
controls-position="right"
></el-input-number>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>

View File

@@ -0,0 +1,113 @@
<!-- 统一清台设置 -->
<template>
<el-dialog title="清台设置" width="600px" v-model="visible">
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="120">
<el-form-item label="清台方式">
<el-radio-group v-model="form.autoClear">
<el-radio label="手动清台" :value="0"></el-radio>
<el-radio label="自动清台" :value="1"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="自动清台时间" prop="autoClearTime" v-if="form.autoClear === 1">
<div class="column">
<div>
<el-input v-model="form.autoClearTime" style="width: 220px;" placeholder="请输入"
@input="e => form.autoClearTime = filterNumberInput(e, 0)">
<template #append>分钟后自动清台</template>
</el-input>
</div>
<div class="tips">输入0时默认支付后既自动清台</div>
</div>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="submitHandle" :loading="confirmLoading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
import tableApi from "@/api/account/table";
import { filterNumberInput } from '@/utils'
const visible = ref(false)
const formRef = ref(null)
const form = ref({
autoClear: 1, // 自动清台 0手动 1自动
autoClearTime: 0, // 自动清台时间 单位分钟 默认10
})
const rules = ref({
autoClearTime: [
{
validator: (rule, value, callback) => {
if (form.value.autoClearTime === '') {
callback(new Error('请输入自动清台时间'))
} else {
callback()
}
},
trigger: 'blur'
}
]
})
// 提交配置
const confirmLoading = ref(false)
function submitHandle() {
formRef.value.validate(async vaild => {
try {
if (vaild) {
confirmLoading.value = true
await tableApi.shopTabBatch(form.value)
visible.value = false
ElNotification({
title: '注意',
message: '设置成功',
type: 'success'
})
}
} catch (error) {
console.log(error);
}
setTimeout(() => {
confirmLoading.value = false
}, 500);
})
}
// 获取清台配置信息
async function tableCurrentStateAjax() {
try {
const res = await tableApi.tableCurrentState()
form.value.autoClear = res.autoClear
form.value.autoClearTime = res.autoClearTime || 0
} catch (error) {
console.log(error);
}
}
function show() {
visible.value = true
tableCurrentStateAjax()
}
defineExpose({
show
})
</script>
<style scoped lang="scss">
.column {
display: flex;
flex-direction: column;
.tips {
color: #999;
}
}
</style>

View File

@@ -2,12 +2,7 @@
<div class="app-container"> <div class="app-container">
<el-tabs v-model="tableArea.tabsSel" type="card" @tab-click="tabClick"> <el-tabs v-model="tableArea.tabsSel" type="card" @tab-click="tabClick">
<el-tab-pane label="全部" name="" /> <el-tab-pane label="全部" name="" />
<el-tab-pane <el-tab-pane v-for="item in tableArea.tabs" :key="item.id" :label="item.name" :name="`${item.id}`">
v-for="item in tableArea.tabs"
:key="item.id"
:label="item.name"
:name="`${item.id}`"
>
<template #label> <template #label>
<div class=""> <div class="">
<span> <span>
@@ -31,6 +26,7 @@
<div class=""> <div class="">
<el-button icon="plus" @click="addEaraShow()">添加区域</el-button> <el-button icon="plus" @click="addEaraShow()">添加区域</el-button>
<el-button type="primary" icon="plus" @click="addTableShow()">添加台桌</el-button> <el-button type="primary" icon="plus" @click="addTableShow()">添加台桌</el-button>
<el-button type="danger" icon="Setting" @click="clearTabDialogRef.show()">清台设置</el-button>
<el-button type="primary" icon="download" @click="showDownloadTableCode"> <el-button type="primary" icon="download" @click="showDownloadTableCode">
下载桌台码 下载桌台码
</el-button> </el-button>
@@ -41,12 +37,9 @@
<div class="u-flex u-p-b-15 u-font-14 u-m-t-16"> <div class="u-flex u-p-b-15 u-font-14 u-m-t-16">
<div v-for="(item, key) in status" :key="key" class="state u-m-r-24"> <div v-for="(item, key) in status" :key="key" class="state u-m-r-24">
<span <span class="dot" :style="{
class="dot" backgroundColor: status[key] ? status[key].type : '',
:style="{ }" />
backgroundColor: status[key] ? status[key].type : '',
}"
/>
{{ item.label }} {{ item.label }}
</div> </div>
</div> </div>
@@ -54,14 +47,9 @@
<!-- 列表 --> <!-- 列表 -->
<div class="head-container"> <div class="head-container">
<div v-loading="loading" class="table_list"> <div v-loading="loading" class="table_list">
<div <div v-for="item in tableList" :key="item.id" class="item" :style="{
v-for="item in tableList" 'background-color': status[item.status] ? status[item.status].type : '',
:key="item.id" }">
class="item"
:style="{
'background-color': status[item.status] ? status[item.status].type : '',
}"
>
<div class="new-top flex u-row-between"> <div class="new-top flex u-row-between">
<div class="u-flex u-flex-1 u-p-r-10"> <div class="u-flex u-flex-1 u-p-r-10">
<span class="name u-line-1" style="max-width: 50px"> <span class="name u-line-1" style="max-width: 50px">
@@ -80,10 +68,7 @@
<el-dropdown-item command="edit"> <el-dropdown-item command="edit">
<span>编辑</span> <span>编辑</span>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item <el-dropdown-item command="bindTableCode" v-if="envName !== 'production'">
command="bindTableCode"
v-if="envName !== 'production'"
>
<span>绑定桌码</span> <span>绑定桌码</span>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item command="del"> <el-dropdown-item command="del">
@@ -102,20 +87,14 @@
item.bookingInfo.bookingPerson item.bookingInfo.bookingPerson
}}{{ item.bookingInfo.gender == 1 ? "先生" : "女士" }} }}{{ item.bookingInfo.gender == 1 ? "先生" : "女士" }}
</span> </span>
<div <div class="state" style="
class="state"
style="
font-size: 12px; font-size: 12px;
color: #666; color: #666;
display: flex; display: flex;
align-items: center; align-items: center;
" ">
> <img style="width: 16px; height: 16px; filter: contrast(0.5)" src="@/assets/images/perpole.png"
<img alt="" />
style="width: 16px; height: 16px; filter: contrast(0.5)"
src="@/assets/images/perpole.png"
alt=""
/>
{{ item.bookingInfo.bookingTime.substring(11, 19) }} {{ item.bookingInfo.bookingTime.substring(11, 19) }}
</div> </div>
@@ -128,71 +107,47 @@
</div> </div>
<div v-else class="u-font-18 font-600 total-price"> <div v-else class="u-font-18 font-600 total-price">
<span <span v-if="item.status == 'using'" class="cur-pointer" @click="diancanShow(item, 'isAddGoods')">
v-if="item.status == 'using'"
class="cur-pointer"
@click="diancanShow(item, 'isAddGoods')"
>
¥{{ item.totalAmount || 0 }}{{ item.productNum }} ¥{{ item.totalAmount || 0 }}{{ item.productNum }}
</span> </span>
<!-- <span v-else class="color-fff">|</span> --> <!-- <span v-else class="color-fff">|</span> -->
</div> </div>
<div class="row btn-group" style="margin-top: 10px"> <div class="row btn-group" style="margin-top: 10px">
<template v-if="item.status == 'subscribe'"> <template v-if="item.status == 'subscribe'">
<el-button <el-button type="success" :disabled="!item.tableId || item.status === 'closed'"
type="success" @click="markStatus(item, -1)">
:disabled="!item.tableId || item.status === 'closed'"
@click="markStatus(item, -1)"
>
取消预约 取消预约
</el-button> </el-button>
</template> </template>
<template <template v-if="
v-if=" item.status == 'subscribe' &&
item.status == 'subscribe' && item.bookingInfo &&
item.bookingInfo && item.bookingInfo.status == 20
item.bookingInfo.status == 20 ">
" <el-button type="success" :disabled="!item.tableId || item.status === 'closed'"
> @click="markStatus(item, 10)">
<el-button
type="success"
:disabled="!item.tableId || item.status === 'closed'"
@click="markStatus(item, 10)"
>
到店 到店
</el-button> </el-button>
</template> </template>
<template <template v-if="
v-if=" item.status == 'idle' ||
item.status == 'idle' || (item.status == 'subscribe' &&
(item.status == 'subscribe' && item.bookingInfo &&
item.bookingInfo && item.bookingInfo.status == 10)
item.bookingInfo.status == 10) ">
" <el-button type="primary" :disabled="!item.tableCode || item.status === 'closed'"
> @click="diancanShow(item)">
<el-button
type="primary"
:disabled="!item.tableCode || item.status === 'closed'"
@click="diancanShow(item)"
>
点餐 点餐
</el-button> </el-button>
</template> </template>
<template <template v-else-if="item.status != 'idle' && item.status != 'subscribe'">
v-else-if="item.status != 'idle' && item.status != 'subscribe'"
>
<template v-if="item.status == 'using'"> <template v-if="item.status == 'using'">
<el-button <el-button :disabled="!item.tableId || item.status === 'closed'"
:disabled="!item.tableId || item.status === 'closed'" @click="diancanShow(item, 'isAddGoods')">
@click="diancanShow(item, 'isAddGoods')"
>
加菜 加菜
</el-button> </el-button>
<el-button <el-button type="danger" :disabled="!item.tableId || item.status === 'closed'"
type="danger" @click="diancanShow(item, 'isPayOrder')">
:disabled="!item.tableId || item.status === 'closed'"
@click="diancanShow(item, 'isPayOrder')"
>
结账 结账
</el-button> </el-button>
</template> </template>
@@ -200,13 +155,10 @@
<el-button type="info" disabled>已关台</el-button> <el-button type="info" disabled>已关台</el-button>
</template> </template>
<template v-else-if="item.status == 'cleaning'"> <template v-else-if="item.status == 'cleaning'">
<el-button <el-button type="info" :style="{
type="info" backgroundColor: status[item.status].type,
:style="{ borderColor: status[item.status].type,
backgroundColor: status[item.status].type, }">
borderColor: status[item.status].type,
}"
>
清理中 清理中
</el-button> </el-button>
</template> </template>
@@ -216,26 +168,17 @@
</template> </template>
</div> </div>
</div> </div>
<div <div class="u-flex u-col-bottom bottom u-row-between color-666"
class="u-flex u-col-bottom bottom u-row-between color-666" :class="{ 'opacity-0': item.status == 'closed' }">
:class="{ 'opacity-0': item.status == 'closed' }"
>
<div class="u-flex u-col-center"> <div class="u-flex u-col-center">
<img <img style="width: 16px; height: 16px; filter: contrast(0.5)" src="@/assets/images/perpole.png"
style="width: 16px; height: 16px; filter: contrast(0.5)" alt="" />
src="@/assets/images/perpole.png"
alt=""
/>
<span class="u-m-t-4 u-font-12 u-m-l-2"> <span class="u-m-t-4 u-font-12 u-m-l-2">
{{ item.useNum || 0 }}/{{ item.maxCapacity }} {{ item.useNum || 0 }}/{{ item.maxCapacity }}
</span> </span>
</div> </div>
<div v-if="item.status == 'using'" class="u-flex"> <div v-if="item.status == 'using'" class="u-flex">
<img <img style="width: 16px; height: 16px; filter: contrast(0.5)" src="@/assets/images/shalou.png" alt="" />
style="width: 16px; height: 16px; filter: contrast(0.5)"
src="@/assets/images/shalou.png"
alt=""
/>
<span class="u-m-t-4 u-font-12"> <span class="u-m-t-4 u-font-12">
{{ formatTime(item.durationTime) }} {{ formatTime(item.durationTime) }}
</span> </span>
@@ -256,6 +199,8 @@
<downloadTableCode ref="refDownloadTableCode" /> <downloadTableCode ref="refDownloadTableCode" />
<!-- 绑定桌码 --> <!-- 绑定桌码 -->
<bindCode ref="refBindCode" @refresh="tableInit" /> <bindCode ref="refBindCode" @refresh="tableInit" />
<!-- 清台设置 -->
<clearTabDialog ref="clearTabDialogRef" />
</div> </div>
</template> </template>
@@ -270,6 +215,9 @@ import addEara from "./components/addEara.vue";
import addTable from "./components/addTable.vue"; import addTable from "./components/addTable.vue";
import bindCode from "./components/bind-table-code.vue"; import bindCode from "./components/bind-table-code.vue";
import downloadTableCode from "./components/downloadTableCode.vue"; import downloadTableCode from "./components/downloadTableCode.vue";
import clearTabDialog from "./components/clearTabDialog.vue";
const clearTabDialogRef = ref(null)
const envName = import.meta.env.VITE_APP_NAME; const envName = import.meta.env.VITE_APP_NAME;
@@ -298,7 +246,7 @@ function formatTime(milliseconds) {
return `${days ? days + "天" : ""} ${hours ? hours + "时" : ""} ${minutes + "分"}`; return `${days ? days + "天" : ""} ${hours ? hours + "时" : ""} ${minutes + "分"}`;
} }
function downloadTableCpde() {} function downloadTableCpde() { }
function downloadShopCpde() { function downloadShopCpde() {
try { try {
const link = document.createElement("a"); const link = document.createElement("a");
@@ -341,7 +289,7 @@ function tableComman(command, item) {
ElMessage.success("删除成功"); ElMessage.success("删除成功");
tableInit(); tableInit();
}) })
.catch(() => {}); .catch(() => { });
return; return;
} }
} }
@@ -417,7 +365,7 @@ function init() {
areainit(); areainit();
tableInit(); tableInit();
} }
onMounted(() => {}); onMounted(() => { });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>