代客下单修改

This commit is contained in:
YeMingfei666 2025-11-18 18:39:52 +08:00
parent e40b4fe8cc
commit 08e76b8897
39 changed files with 12932 additions and 4425 deletions

View File

@ -160,6 +160,8 @@ App.vue本身不是页面这里不能编写视图元素也就是没有<tem
</script> </script>
<style lang="scss"> <style lang="scss">
@import "uview-plus/index.scss";
/** 每个页面公共css */ /** 每个页面公共css */
@import '@/commons/style/global.scss'; @import '@/commons/style/global.scss';
@import '@/commons/style/common.scss'; @import '@/commons/style/common.scss';
@ -167,5 +169,4 @@ App.vue本身不是页面这里不能编写视图元素也就是没有<tem
/** uni 组件样式覆盖 */ /** uni 组件样式覆盖 */
@import '@/commons/style/uni-overwrite.scss'; @import '@/commons/style/uni-overwrite.scss';
@import "uview-plus/index.scss";
</style> </style>

View File

@ -1,14 +1,5 @@
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' +
'2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ=='
// 加密
export function encrypt(txt) { export function encrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(publicKey) // 设置公钥 return txt
return encryptor.encrypt(txt) // 对需要加密的数据进行加密
} }

View File

@ -0,0 +1,17 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
/**
* 新客立减
* @returns
*/
export function getDiscountByUserId(params) {
return request({
url: marketUrl + `/admin/consumeDiscount/getDiscountByUserId`,
method: "get",
params: {
...params,
},
});
}

View File

@ -38,3 +38,18 @@ export function del(id) {
}) })
} }
/**
* 满减活动
* @returns
*/
export function discountActivity(params) {
return request({
url: urlType+`/admin/discountActivity`,
method: 'get',
params: {
...params
}
})
}

View File

@ -0,0 +1,17 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
/**
* 限时折扣
* @returns
*/
export function limitTimeDiscount(params) {
return request({
url: urlType + `/admin/limitTimeDiscount`,
method: "get",
params: {
...params,
},
});
}

View File

@ -8,7 +8,7 @@ const request = http.request
export function getOrderPayUrl(data, urlType = 'order') { export function getOrderPayUrl(data, urlType = 'order') {
return request({ return request({
url: `${urlType}/pay/shopPayApi/orderPayUrl`, url: `${urlType}/pay/shopPayApi/orderPayUrl`,
method: "GET", method: "POST",
data: { data: {
...data ...data
} }

View File

@ -21,7 +21,7 @@ export function getShopTable(data, urlType = 'account') {
*/ */
export function getShopTableDetail(data, urlType = 'account') { export function getShopTableDetail(data, urlType = 'account') {
return request({ return request({
url: `${urlType}/admin/shopTable/detail`, url: `/account/admin/shopTable/detail`,
method: "GET", method: "GET",
data: { data: {
...data ...data

View File

@ -15,6 +15,7 @@ import infoBox from "@/commons/utils/infoBox.js"
import go from '@/commons/utils/go.js'; import go from '@/commons/utils/go.js';
import { reject } from 'lodash'; import { reject } from 'lodash';
// 设置node环境 // 设置node环境
// envConfig.changeEnv(storageManage.env('production'))
envConfig.changeEnv(storageManage.env('development')) envConfig.changeEnv(storageManage.env('development'))
// 测试服 // 测试服

415
http/yskApi/Instead.js Normal file
View File

@ -0,0 +1,415 @@
// 代课下单
import http from '@/http/yskApi/http.js'
import { accountUrl, marketUrl } from './prveUrl.js'
const request = http.request
//就餐形式,默认堂食后付费
const useType = 'dine-in-after'
function getUseType() {
const type = uni.getStorageSync("useType")
return type ? type : useType
}
// import {
// webscoketUtill
// } from '../websock.js'
// let wxObj = null
// wx初始化购物车
// export function getWXCart(params) {
// let wxUrl = 'ws://192.168.1.31:2348/?' + objectToString(params)
// wxObj = new webscoketUtill(wxUrl,3000,9,(e)=>{
// console.log('收到消息');
// console.log(e);
// })
// return uni.getStorageSync('wxList')
// }
// 新增\删除\修改到购物车
// export function addWXCart(params) {
// wxObj.ws.send(params)
// }
function objectToString(obj) {
let result = '';
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result += `${key}=${obj[key]}&`;
}
}
// 去掉最后一个多余的 &
return result.slice(0, -1);
}
/**
* 获取当前台桌订单信息
* @returns
*/
export function getCart(params) {
return request({
url: `/api/place/cart`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...params
}
});
}
/**
* 已上架商品列表
* @returns
*/
export function getGoodsLists(params, showLoading = true) {
return request({
url: `/product/admin/product/list`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...params
},
showLoading
});
}
/**
* 点单
* @returns
*/
export function addCart(data) {
return request({
url: `/api/place/addCart`,
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 清空购物车/支付订单
* @returns
*/
export function $clearCart(data) {
return request({
url: `/api/place/clearCart`,
method: "delete",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 删除购物车某个商品
* @returns
*/
export function $removeCart(data) {
return request({
url: `/api/place/removeCart`,
method: "delete",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 更新规格
* @returns
*/
export function $updateCart(data) {
return request({
url: `/api/place/updateCart`,
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
/**
* 批量打包
* @returns
*/
export function $allPack(data) {
return request({
url: `/api/place/pack`,
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
/**
* 获取取餐号
* @returns
*/
export function $getMasterId(data) {
return request({
url: `/api/place/masterId`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 支付方式获取
* @returns
*/
export function $getPayType(data) {
return request({
url: `/account/admin/payType`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
/**
* 创建订单
* @returns
*/
export function $createOrder(data) {
return request({
url: `/api/place/order`,
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 挂起订单
* @returns
*/
export function $cacheOrder(data) {
return request({
url: `/api/place/pending`,
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 获取已挂起订单
* @returns
*/
export function $getCacheOrder(data) {
return request({
url: `/api/place/pending/cart`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
// 会员点单/取消会员点单
export function $setUser(data) {
return request({
url: `/api/place/updateVip`,
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 删除订单
export function $delOrder(data) {
return request({
url: `/api/place/order`,
method: "delete",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 支付订单
export function $payOrder(data) {
return request({
url: '/api/place/pay',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//退单
export function $returnCart(data) {
return request({
url: '/api/place/returnCart',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 选择台桌
export function $choseTable(data) {
return request({
url: '/api/place/choseTable',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 用餐人数
export function $choseCount(data) {
return request({
url: '/api/place/choseCount',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
// 批量生成台桌
export function $fastCreateTable(data) {
return request({
url: '/api/tbShopTable/generate',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//打印当前台桌订单
export function $printOrder(data) {
return request({
url: '/api/place/printOrder',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//打印当前台桌菜品
export function $printDishes(data) {
return request({
url: '/api/place/printDishes',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 就餐模式切换
export function $changeUseType(data) {
return request({
url: '/api/place/choseModel',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 退款
export function $returnOrder(data) {
return request({
url: '/api/place/returnOrder',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//获取用户可用优惠券
export function $findCoupon(data) {
return request({
url: marketUrl+'/admin/coupon/findCoupon',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//会员积分列表
export function $returnMemberPointsList(data) {
return request({
url: '/api/points/member-points/page',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 会员积分账户信息
export function $returnMemberPoints(memberId) {
return request({
url: '/api/points/member-points/' + memberId,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//002-获取订单可用积分及抵扣金额(支付页面使用)
export function $calcUsablePoints(data) {
return request({
url: '/api/points/member-points/calc-usable-points',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 003-根据积分计算可抵扣金额
export function $calcDeDuctionPoints(data) {
return request({
url: '/api/points/member-points/calc-deduction-amount',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}

235
http/yskApi/http.js Normal file
View File

@ -0,0 +1,235 @@
/**
* HTTP的封装 基于uni.request
* 包括 通用响应结果的处理 业务的增删改查函数
*
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/12/16 18:35
*/
// 设置env配置文件
import envConfig from '@/env/config.js'
// 导入全局属性
import appConfig from '@/config/appConfig.js'
import storageManage from '@/commons/utils/storageManage.js'
import infoBox from "@/commons/utils/infoBox.js"
import go from '@/commons/utils/go.js';
import { reject } from 'lodash';
// 设置node环境
// envConfig.changeEnv(storageManage.env('production'))
envConfig.changeEnv(storageManage.env('development'))
// 测试服
// #ifdef H5
let baseUrl = '/api/'
// #endif
// #ifndef H5
// let baseUrl = 'https://tapi.cashier.sxczgkj.cn/'
//预发布
// let baseUrl = 'https://pre-cashieradmin.sxczgkj.cn'
//正式
// let baseUrl = 'https://cashier.sxczgkj.com/'
let baseUrl = appConfig.env.JEEPAY_BASE_URL
// #endif
const loadingShowTime = 200
function getHeader(){
const headerObject={}
headerObject["token"] = storageManage.token()
headerObject["shopId"] = uni.getStorageSync("shopInfo").id
headerObject["platformType"] = 'APP'
return headerObject
}
// 通用处理逻辑
function commonsProcess(showLoading, httpReqCallback) {
// 判断是否请求完成(用作 是否loading
// 包括: 'ing', 'ingLoading', 'finish'
let reqState = 'ing'
// 是否已经提示的错误信息
let isShowErrorToast = false
// 请求完成, 需要处理的动作
let reqFinishFunc = () => {
if (reqState == 'ingLoading') { // 关闭loading弹层
infoBox.hideLoading()
}
reqState = 'finish' // 请求完毕
}
// 明确显示loading
if (showLoading) {
// xx ms内响应完成不提示loading
setTimeout(() => {
if (reqState == 'ing') {
reqState = 'ingLoading'
infoBox.showLoading()
}
}, loadingShowTime)
}
return httpReqCallback().then((httpData) => {
reqFinishFunc(); // 请求完毕的动作
// 从http响应数据中解构响应数据 [ 响应码、 bodyData ]
let {
statusCode,
data
} = httpData
// 避免混淆重新命名
let bodyData = data
if (statusCode == 500) {
isShowErrorToast = true
return Promise.reject(bodyData) // 跳转到catch函数
}
if (statusCode == 501) {
// storageManage.token(null, true)
// 提示信息
isShowErrorToast = true
// infoBox.showErrorToast('请登录').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
return Promise.reject(bodyData) // 跳转到catch函数
}
// http响应码不正确
if (statusCode != 200 && statusCode != 204 && statusCode != 201) {
isShowErrorToast = true
bodyData.msg=bodyData.msg=='Bad credentials'?'用户名或密码错误':bodyData.msg
infoBox.showToast(bodyData.msg || '服务器异常')
return Promise.reject(bodyData) // 跳转到catch函数
}
// // 业务响应异常
if (bodyData.hasOwnProperty('code') && bodyData.code != 200) {
isShowErrorToast = true
infoBox.showToast(bodyData.msg)
// if (bodyData.code == 5005) { // 密码已过期, 直接跳转到更改密码页面
// uni.reLaunch({
// url: '/pageUser/setting/updatePwd'
// })
// }
// if(bodyData.code == 500){ // 密码已过期, 直接跳转到更改密码页面
// uni.redirectTo({url: '/pages/login/index'})
// }
return Promise.reject(bodyData) // 跳转到catch函数
}
// 构造请求成功的响应数据
return Promise.resolve(bodyData.data)
}).catch(res => {
console.log(res)
if(res.code==501){
storageManage.token(null, true)
infoBox.showToast('登录过期,请重新登录').then(() => {
uni.redirectTo({url: '/pages/login/index'})
reject()
})
}
// if(res.status==400){
// storageManage.token(null, true)
// infoBox.showErrorToast('').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
// }
if(res.code==500){
infoBox.showToast(res.msg||'服务器异常').then(() => {})
}
// if(res&&res.msg){
// infoBox.showErrorToast(res.msg)
// }
reqFinishFunc(); // 请求完毕的动作
// 如果没有提示错误, 那么此处提示 异常。
if (!isShowErrorToast) {
infoBox.showToast(`请求网络异常`)
}
return Promise.reject(res)
}).finally(() => { // finally 是 then结束后再执行, 此处不适用。 需要在请求完成后立马调用: reqFinishFunc()
});
}
// 默认 显示loading(控制 xxs 内 不提示loading )
function req(uri, data, method = "GET", showLoading = true, extParams = {}) {
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign({
url: baseUrl + uri,
data: data,
method: method,
header: getHeader()
}, extParams)
)
})
}
// 默认 显示loading(控制 xxs 内 不提示loading )
function request(args) {
const {
url,
data,
params,
method = "GET",
showLoading = true,
extParams = {}
} = args
let headerObject = {}
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign({
url: baseUrl + url,
data: params||data,
method: method,
header: getHeader()
}, extParams)
)
})
}
// 上传
function upload(uri, data, file, showLoading = true, extParams = {}) {
// 放置token
let headerObject = {}
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.uploadFile(
Object.assign({
url: baseUrl + uri,
formData: data,
name: "file",
filePath: file.path||file.url,
header: getHeader()
}, extParams)
).then((httpData) => {
// uni.upload 返回bodyData 的是 string类型。 需要解析。
httpData.data = JSON.parse(httpData.data)
return Promise.resolve(httpData)
}).catch(err=>{
uni.hideLoading()
infoBox.showErrorToast(`上传失败`)
})
})
}
export default {
req: req,
request,
upload: upload
}

View File

@ -0,0 +1,19 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {marketUrl} from './prveUrl.js'
/**
* 限时折扣
* @returns
*/
export function limitTimeDiscount(params) {
return request({
url: marketUrl+`/admin/limitTimeDiscount`,
method: 'get',
params: {
shopId: uni.getStorageSync('shopInfo').id,
...params
}
})
}

View File

@ -0,0 +1,20 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {marketUrl} from '../prveUrl.js'
/**
* 新客立减
* @returns
*/
export function getDiscountByUserId(params) {
return request({
url: marketUrl+`/admin/consumeDiscount/getDiscountByUserId`,
method: 'get',
params: {
shopId: uni.getStorageSync('shopInfo').id,
...params
}
})
}

View File

@ -0,0 +1,21 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {marketUrl} from '../prveUrl.js'
/**
* 满减活动
* @returns
*/
export function discountActivity(params) {
console.log(uni.getStorageSync('shopInfo').id)
return request({
url: marketUrl+`/admin/discountActivity`,
method: 'get',
params: {
shopId: uni.getStorageSync('shopInfo').id,
...params
}
})
}

118
http/yskApi/order.js Normal file
View File

@ -0,0 +1,118 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {orderUrl} from './prveUrl.js'
/**
* 查询订单
* @param {*} data
* @returns
*/
export function tbOrderInfoData(data) {
return request({
url: "/order/admin/order",
method: "get",
data: {
shopId: uni.getStorageSync('shopId'),
...data
}
});
}
/**
* 导出数据
* @param {*} data
* @returns
*/
export function tbOrderInfoDownload(data) {
return request({
url: "/api/tbOrderInfo/download",
method: "post",
data: {
shopId: uni.getStorageSync('shopId'),
...data
},
responseType: "blob"
});
}
export function createOrder(data, urlType = 'order') {
return request({
url: `/${urlType}/admin/order/createOrder`,
method: "POST",
data: {
...data
}
})
}
/**
* 通过Id查询订单
* @param {*} id
* @returns
*/
export function tbOrderInfoDetail(id) {
return request({
url: orderUrl+ `/admin/order/historyOrder?orderId=${id}`,
method: "get"
});
}
/**
* 通过Id查询订单
* @param {*} createdAt
* @returns
*/
export function payCount(createdAt) {
console.log(createdAt);
return request({
url: `/api/tbOrderInfo/payCount`,
method: "post",
data: {
shopId: uni.getStorageSync('shopId'),
createdAt: createdAt
}
});
}
/**
* 订单列表
* @param {*} createdAt
* @returns
*/
export function tbGroupOrderInfo(params) {
return request({
url: `/api/tbGroupOrderInfo`,
method: "post",
data: {
shopId: uni.getStorageSync('shopId'),
...params
}
});
}
/**
* 退单
* @param {*} data
* @returns
*/
export function returnGpOrder(data) {
return request({
url: `/api/tbGroupOrderInfo/returnGpOrder`,
method: "post",
data
});
}
/**
* 店铺订单支付获取链接
*/
export function $getOrderPayUrl(data) {
return request({
url: `/api/shopPayApi/getOrderPayUrl`,
method: "get",
data: {
shopId: uni.getStorageSync('shopId'),
...data
}
});
}

3
http/yskApi/prveUrl.js Normal file
View File

@ -0,0 +1,3 @@
export const marketUrl = '/market'
export const accountUrl = '/account'
export const orderUrl = '/order'

59
http/yskApi/shop-user.js Normal file
View File

@ -0,0 +1,59 @@
// 用户管理
import http from './http.js'
import {accountUrl} from './prveUrl.js'
const request=http.request
// 获取店铺会员二维码
export function getwxacode(data) {
return request({
url: `/shop/storage/getwxacode`,
method: "post",
data
});
}
/**
* 商家用户列表
* @returns
*/
export function queryAllShopUser(params) {
return request({
url: accountUrl+`/admin/shopUser`,
method: "get",
params: {
shopId: uni.getStorageSync('shopId'),
...params
}
});
}
/**
* 查询商家用户概述信息
* @returns
*/
export function queryAllShopInfo(params) {
return request({
url: `/api/tbShopUser/summary`,
method: "get",
params: {
shopId: uni.getStorageSync('shopId'),
isVip:1,
...params
}
});
}
/**
* 获取店铺用户详情
* @returns
*/
export function shopUserDetail(params) {
return request({
url: accountUrl+`/admin/shopUser/detail`,
method: "get",
params: {
shopId: uni.getStorageSync('shopId'),
...params
}
});
}

0
lib/carts.ts Normal file
View File

852
lib/coupon.ts Normal file
View File

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

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

File diff suppressed because it is too large Load Diff

1382
lib/goods.ts Normal file

File diff suppressed because it is too large Load Diff

11
lib/index.ts Normal file
View File

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

216
lib/limit.ts Normal file
View File

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

0
lib/socket.ts Normal file
View File

430
lib/types.ts Normal file
View File

@ -0,0 +1,430 @@
/** 商品类型枚举 */
export enum GoodsType {
NORMAL = "normal", // 普通商品
WEIGHT = "weight", // 称重商品
GIFT = "gift", // 赠菜(继承普通商品逻辑,标记用)
EMPTY = "", // 空字符串类型(后端未返回时默认归类为普通商品)
PACKAGE = "package", // 打包商品(如套餐/预打包商品,按普通商品逻辑处理,可扩展特殊规则)
}
/** 优惠券计算结果类型(新增细分字段) */
export interface CouponResult {
deductionAmount: number; // 抵扣金额
excludedProductIds: string[]; // 不适用商品ID列表注意是商品ID非购物车ID
usedCoupon: Coupon | undefined; // 实际使用的优惠券
productCouponDeduction: number; // 新增:商品优惠券抵扣(兑换券等)
fullCouponDeduction: number; // 新增:满减优惠券抵扣
}
/** 兑换券计算结果类型(新增细分字段) */
export interface ExchangeCalculationResult {
deductionAmount: number;
excludedProductIds: string[]; // 不适用商品ID列表商品ID
productCouponDeduction: number; // 新增:兑换券属于商品券,同步记录
}
export interface CouponTypes {
1: "满减券";
2: "商品券";
3: "折扣券";
4: "第二件半价券";
5: "消费送券";
6: "买一送一券";
7: "固定价格券";
8: "免配送费券";
}
/** 优惠券类型枚举 */
export enum CouponType {
FULL_REDUCTION = "full_reduction", // 满减券
DISCOUNT = "discount", // 折扣券
SECOND_HALF = "second_half", // 第二件半价券
BUY_ONE_GET_ONE = "buy_one_get_one", // 买一送一券
EXCHANGE = "exchange", // 商品兑换券
}
/** 后端返回的优惠券原始字段类型 */
export interface BackendCoupon {
id?: number; // 自增主键int64
shopId?: number; // 店铺IDint64
syncId?: number; // 同步Idint64
type?: number; // 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
name?: string; // 券名称
useShopType?: string; // 可用门店类型only-仅本店all-所有门店custom-指定门店
useShops?: string; // 可用门店(逗号分隔字符串,如"1,2,3"
useType?: string; // 可使用类型dine堂食/pickup自取/deliv配送/express快递
validType?: string; // 有效期类型fixed固定时间custom自定义时间
validDays?: number; // 有效期(天)
validStartTime?: string; // 有效期开始时间(如"2024-01-01 00:00:00"
validEndTime?: string; // 有效期结束时间
daysToTakeEffect?: number; // 隔天生效
useDays?: string; // 可用周期(如"周一,周二"
useTimeType?: string; // 可用时间段类型all-全时段custom-指定时段
useStartTime?: string; // 可用开始时间(每日)
useEndTime?: string; // 可用结束时间(每日)
getType?: string; // 发放设置:不可自行领取/no可领取/yes
getMode?: string; // 用户领取方式
giveNum?: number; // 总发放数量,-10086为不限量
getUserType?: string; // 可领取用户:全部/all新用户一次/new仅会员/vip
getLimit?: number; // 每人领取限量,-10086为不限量
useLimit?: number; // 每人每日使用限量,-10086为不限量
discountShare?: number; // 与限时折扣同享0-否1-是
vipPriceShare?: number; // 与会员价同享0-否1-是
ruleDetails?: string; // 附加规则说明
status?: number; // 状态0-禁用1-启用
useNum?: number; // 已使用数量
leftNum?: number; // 剩余数量
foods?: string; // 指定门槛商品(逗号分隔字符串,如"101,102"此处为商品ID
fullAmount?: number; // 使用门槛:满多少金额(元)
discountAmount?: number; // 使用门槛:减多少金额(元)
discountRate?: number; // 折扣%如90=9折
maxDiscountAmount?: number; // 可抵扣最大金额(元)
useRule?: string; // 使用规则price_asc-价格低到高price_desc-高到低
discountNum?: number; // 抵扣数量
otherCouponShare?: number; // 与其它优惠共享0-否1-是
createTime?: string; // 创建时间
updateTime?: string; // 更新时间
}
/** 营销活动类型枚举 */
export enum ActivityType {
TIME_LIMIT_DISCOUNT = "time_limit_discount", // 限时折扣
}
/** 基础购物车商品项核心修正新增product_id明确各ID含义 */
export interface BaseCartItem {
id: string | number; // 购物车ID唯一标识购物车中的条目如购物车项主键
product_id: string | number; // 商品ID唯一标识商品用于优惠券/活动匹配,必选)
productId?: string | number; // 商品ID
salePrice: number; // 商品原价(元)
number: number; // 商品数量
num?: number; // 商品数量
isTimeDiscount?: boolean; // 是否限时折扣商品默认false
is_time_discount?: boolean; // 是否限时折扣商品默认false
product_type: GoodsType; // 商品类型
is_temporary?: boolean; // 是否临时菜默认false
isTemporary?: boolean; // 是否临时菜默认false
is_gift?: boolean; // 是否赠菜默认false
isGift?: boolean; // 是否赠菜默认false
returnNum?: number; // 退货数量历史订单用默认0
memberPrice: number; // 商品会员价(元,优先级:商品会员价 > 会员折扣)
discountSaleAmount?: number; // 商家改价后单价(元,优先级最高)
discount_sale_amount?: number; // 商家改价后单价(元,优先级最高)
packFee?: number; // 单份打包费默认0
packNumber?: number; // 堂食打包数量默认0
activityInfo?: {
// 商品参与的营销活动(如限时折扣)
type: ActivityType;
discountRate: number; // 折扣率如0.8=8折
vipPriceShare: boolean; // 是否与会员优惠同享默认false
};
skuData?: {
// SKU扩展数据可选
id: string | number; // SKU ID唯一标识商品规格如颜色/尺寸)
memberPrice: number; // SKU会员价
salePrice?: number; // SKU原价
};
skuId?: string | number; // SKU ID唯一标识商品规格如颜色/尺寸)
couponType?: number; // 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
}
export interface CouponFoods {
id: string;
name: string;
images: string;
}
/** 基础优惠券接口(所有券类型继承,包含统一门槛商品字段) */
export interface BaseCoupon {
otherCouponShare?: number; // 与其它优惠共享0-否1-是
id: string | number; // 优惠券ID
type: number; // 工具库字符串枚举由后端couponType转换
name: string; // 对应后端title
available: boolean; // 基于BackendCoupon字段计算的可用性
useShopType?: string; // only-仅本店all-所有门店custom-指定门店
useShops: string[]; // 可用门店ID列表
discountShare: boolean; // 与限时折扣同享0-否1-是(后端字段转换为布尔值)
vipPriceShare: boolean; // 与会员价同享0-否1-是(后端字段转换为布尔值)
useType?: string[]; // 可使用类型dine堂食/pickup自取/deliv配送/express快递
isValid: boolean; // 是否在有效期内
discountAmount?: number; // 减免金额 (满减券有)
fullAmount?: number; // 使用门槛:满多少金额
maxDiscountAmount?: number; // 可抵扣最大金额 元
use: boolean;
discountNum?: number; // 抵扣数量
useRule?: string; // 使用规则price_asc-价格低到高price_desc-高到低
discountRate?: number; // 折扣%如90=9折
noUseRestrictions?: boolean; // 是不可用原因
thresholdFoods: CouponFoods[]; // 门槛商品ID列表空数组=全部商品,非空=指定商品ID
useFoods: CouponFoods[]; // 可用商品ID列表空数组=全部商品,非空=指定商品ID
}
export interface couponDiscount {
discountPrice: number;
hasDiscountGoodsArr: BaseCartItem[];
}
/** 满减券(适配后端字段) */
export interface FullReductionCoupon extends BaseCoupon {
fullAmount: number; // 对应后端fullAmount满减门槛
discountAmount: number; // 对应后端discountAmount减免金额
maxDiscountAmount?: number; // 对应后端maxDiscountAmount最大减免
discount?: couponDiscount;
}
/** 折扣券(适配后端字段) */
export interface DiscountCoupon extends BaseCoupon {
discountRate: number; // 后端discountRate%转小数如90→0.9
maxDiscountAmount: number; // 对应后端maxDiscountAmount最大减免
discount?: couponDiscount;
}
/** 第二件半价券(适配后端字段) */
export interface SecondHalfPriceCoupon extends BaseCoupon {
maxUseCountPerOrder?: number; // 对应后端useLimit-10086=不限)
discount?: couponDiscount;
}
/** 买一送一券(适配后端字段) */
export interface BuyOneGetOneCoupon extends BaseCoupon {
maxUseCountPerOrder?: number; // 对应后端useLimit-10086=不限)
discount?: couponDiscount;
}
/** 商品兑换券(适配后端字段) */
export interface ExchangeCoupon extends BaseCoupon {
deductCount: number; // 对应后端discountNum抵扣数量
sortRule: "low_price_first" | "high_price_first"; // 后端useRule转换
discount?: couponDiscount;
}
/** 所有优惠券类型联合 */
export type Coupon =
| FullReductionCoupon
| DiscountCoupon
| SecondHalfPriceCoupon
| BuyOneGetOneCoupon
| ExchangeCoupon;
/** 营销活动配置如限时折扣applicableProductIds为商品ID列表 */
export interface ActivityConfig {
type: ActivityType;
applicableProductIds?: string[]; // 适用商品ID列表与BaseCartItem.product_id匹配
discountRate: number; // 折扣率如0.8=8折
vipPriceShare: boolean; // 是否与会员优惠同享
}
/** 积分抵扣规则 */
export interface PointDeductionRule {
pointsPerYuan: number; // X积分=1元如100=100积分抵1元
maxDeductionAmount?: number; // 最大抵扣金额(元,默认不限)
}
/** 餐位费配置 */
export interface SeatFeeConfig {
pricePerPerson: number; // 每人餐位费(元)
personCount: number; // 用餐人数默认1
isEnabled: boolean; // 是否启用餐位费默认false
}
/** 商家减免类型枚举 */
export enum MerchantReductionType {
FIXED_AMOUNT = "fixed_amount", // 固定金额减免(如直接减 10 元)
DISCOUNT_RATE = "discount_rate", // 比例折扣减免(如打 9 折,即减免 10%
}
/** 商家减免配置(新增,替代原单一金额字段) */
export interface MerchantReductionConfig {
type: MerchantReductionType; // 减免类型(二选一)
fixedAmount?: number; // 固定减免金额(元,仅 FIXED_AMOUNT 生效≥0
discountRate?: number; // 折扣率(%,仅 DISCOUNT_RATE 生效0-100如 90 代表 9 折)
}
/**商家霸王餐配置 */
export interface FreeDineConfig {
enable: boolean; //是否开启
rechargeThreshold: number; //订单满多少元可以使用
rechargeTimes: number; //充值多少倍免单
withCoupon: boolean; //与优惠券同享
withPoints: boolean; //与积分同享
useType?: string[]; //使用类型 dine-in店内 takeout 自取 post快递takeaway外卖
useShopType?: string; //all 全部 part部分
shopIdList?: number[]; //可用门店id
}
//限时折扣配置
export interface TimeLimitDiscountConfig {
/**
* limit-time/vip-price
*/
discountPriority: string;
/**
* % 1-99
*/
discountRate: number;
/**
*
*/
foods: string;
/**
* 1 2
*/
foodType: number;
/**
*
*/
id: number;
/**
* ID
*/
shopId: number;
/**
* 使 dine-in take-out take-away post
*/
useType: string;
[property: string]: any;
}
//用户信息
export interface ShopUserInfo {
isVip: number | null; //是否会员
discount: number | null; //用户折扣
isMemberPrice: number | null; //会员折扣与会员价是否同时使用
id?: number; //用户ID
}
/** 订单额外费用配置 */
export interface OrderExtraConfig {
// merchantReduction: number; // 商家减免金额默认0
// 替换原单一金额字段,支持两种减免形式
merchantReduction: MerchantReductionConfig;
additionalFee: number; // 附加费如余额充值、券包默认0
pointDeductionRule: PointDeductionRule; // 积分抵扣规则
seatFeeConfig: SeatFeeConfig; // 餐位费配置
currentStoreId: string; // 当前门店ID用于验证优惠券适用门店
userPoints: number; // 用户当前积分(用于积分抵扣)
isMember: boolean; // 用户是否会员(用于会员优惠)
memberDiscountRate?: number; // 会员折扣率如0.95=95折无会员价时用
newUserDiscount?: number; // 新用户减免金额默认0
fullReductionActivities: FullReductionActivity[]; // 当前店铺的满减活动列表(后端返回结构)
currentDinnerType: "dine-in" | "take-out" | "take-away" | "post"; // 当前就餐类型匹配useType
isFreeDine?: boolean; //是否霸王餐
freeDineConfig?: FreeDineConfig;
limitTimeDiscount?: TimeLimitDiscountConfig; //限时折扣
shopUserInfo: ShopUserInfo; // 用户信息
}
/** 订单费用汇总(修改:补充商家减免类型和明细) */
export interface OrderCostSummary {
goodsList: BaseCartItem[];
// 商品总件数
goodsTotal: number;
totalDiscountAmount: number;
goodsRealAmount: number; // 商品真实原价总和
goodsOriginalAmount: number; // 商品原价总和
goodsDiscountAmount: number; // 商品折扣金额
couponDeductionAmount: number; // 优惠券总抵扣
productCouponDeduction: number; // 商品优惠券抵扣
fullCouponDeduction: number; // 满减优惠券抵扣
pointDeductionAmount: number; // 积分抵扣金额
seatFee: number; // 餐位费
packFee: number; // 打包费
scoreMaxMoney: number; // 积分最大可抵扣金额
// 新增:商家减免明细
merchantReduction: {
type: MerchantReductionType; // 实际使用的减免类型
originalConfig: MerchantReductionConfig; // 原始配置(便于前端展示)
actualAmount: number; // 实际减免金额计算后的值≥0
};
additionalFee: number; // 附加费
finalPayAmount: number; // 最终实付金额
couponUsed?: Coupon; // 实际使用的优惠券
pointUsed: number; // 实际使用的积分
newUserDiscount: number; // 新用户减免金额默认0
dinnerType?: "dine-in" | "take-out"; // 就餐类型(堂食/自取/配送/快递)
config: OrderExtraConfig; // 订单额外费用配置
//满减活动
fullReduction: {
usedFullReductionActivityFullAmount: number; // 计算出的满减活动的门槛金额
usedActivity?: FullReductionActivity; // 实际使用的满减活动
usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优)
actualAmount: number; // 满减实际减免金额(元)
};
vipDiscountAmount: number; //会员折扣减免金额
// 订单原支付金额
orderOriginFinalPayAmount: number; //订单原金额(包含打包费+餐位费)
}
/** 满减活动阈值单条满减规则满X减Y- 对应 MkDiscountThresholdInsertGroupDefaultGroup */
export interface FullReductionThreshold {
activityId?: number; // 关联满减活动ID
fullAmount?: number; // 满多少金额(元,必填)
discountAmount?: number; // 减多少金额(元,必填)
}
/** 满减活动主表 - 对应 Request 接口(后端真实字段) */
export interface FullReductionActivity {
id?: number; // 自增主键后端字段id
shopId?: number; // 店铺ID后端字段shopId
status?: number; // 活动状态1=未开始2=进行中3=已结束后端字段status
sort?: number; // 排序值越大优先级越高后端字段sort
createTime?: string; // 创建时间后端字段createTime格式如"2025-10-14 13:56:07"
updateTime?: string; // 最新修改时间后端字段updateTime用于优先级排序
validStartTime?: string; // 有效期开始时间后端字段validStartTime格式如"2025-10-14"
validEndTime?: string; // 有效期结束时间后端字段validEndTime格式如"2025-12-14"
useType?: string; // 可使用类型后端字段useType如"dine,pickup,deliv,express"
useDays?: string; // 可用周期后端字段useDays如"周一,周二,周三,周四,周五,周六,周日"
useTimeType?: string; // 可用时间段类型后端字段useTimeTypeall=全时段custom=指定时段)
useStartTime?: string; // 每日可用开始时间后端字段useStartTime如"09:00:00"仅custom时有效
useEndTime?: string; // 每日可用结束时间后端字段useEndTime如"22:00:00"仅custom时有效
couponShare?: number; // 与优惠券同享0=否1=是后端字段couponShare
discountShare?: number; // 与限时折扣同享0=否1=是后端字段discountShare
vipPriceShare?: number; // 与会员价同享0=否1=是后端字段vipPriceShare
pointsShare?: number; // 与积分抵扣同享0=否1=是后端字段pointsShare
thresholds?: FullReductionThreshold[]; // 满减阈值列表多门槛后端字段thresholds
isDel?: boolean; // 是否删除0=否1=是后端字段isDel默认false
}
// 辅助枚举星期映射用于useDays校验
export const WEEKDAY_MAP = {
周一: 1,
周二: 2,
周三: 3,
周四: 4,
周五: 5,
周六: 6,
周日: 0, // JS中getDay()返回0=周日
};
export interface ShopInfo {
isMemberPrice: number; // 是否开启会员价 1是开启
[property: string]: any;
}
export interface couponCalcParams {
canDikouGoodsArr: BaseCartItem[];
coupon: Coupon;
user: ShopUserInfo;
shopInfo: ShopInfo;
selCoupon: Coupon[];
goodsOrderPrice: number; //商品订单总价
isMemberPrice: number; // 是否开启会员价 1是开启
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined;
}
export interface CanDikouGoodsArrArgs {
canDikouGoodsArr: BaseCartItem[];
selCoupon: Coupon[];
user: ShopUserInfo;
shopInfo: ShopInfo;
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined;
}
export interface returnPriceArgs {
goods: BaseCartItem;
selCoupon: Coupon[];
user: ShopUserInfo;
shopInfo: ShopInfo;
shopUserInfo: ShopUserInfo;
limitTimeDiscountRes?: TimeLimitDiscountConfig | null | undefined;
idKey?: keyof BaseCartItem;
}
export interface CanReturnMemberPriceArgs {
shopInfo?: ShopInfo;
shopUserInfo: ShopUserInfo;
}

33
lib/utils.ts Normal file
View File

@ -0,0 +1,33 @@
/**
* /线
* @param obj BaseCartItem
* @param camelCaseKey 'isTemporary'
* @param snakeCaseKey 线 'is_temporary'
* @param defaultValue false
* @returns
*/
export function getCompatibleFieldValue(
obj: Record<string, any>,
camelCaseKey: string,
snakeCaseKey: string,
defaultValue: boolean = false
): boolean {
// 优先判断驼峰字段(如果存在且不是 undefined/null
if (
obj.hasOwnProperty(camelCaseKey) &&
obj[camelCaseKey] !== undefined &&
obj[camelCaseKey] !== null
) {
return Boolean(obj[camelCaseKey]);
}
// 再判断下划线字段
if (
obj.hasOwnProperty(snakeCaseKey) &&
obj[snakeCaseKey] !== undefined &&
obj[snakeCaseKey] !== null
) {
return Boolean(obj[snakeCaseKey]);
}
// 都不存在时返回默认值(布尔类型字段默认 false
return defaultValue;
}

View File

@ -1,5 +1,6 @@
{ {
"dependencies": { "dependencies": {
"bignumber.js": "^9.3.1",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"gm-crypto": "^0.1.8", "gm-crypto": "^0.1.8",
@ -9,7 +10,8 @@
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"to-arraybuffer": "^1.0.1", "to-arraybuffer": "^1.0.1",
"uview-plus": "^3.3.32" "uview-plus": "^3.3.32",
"ysk-utils": "^1.0.78"
}, },
"devDependencies": { "devDependencies": {
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^12.0.2",

View File

@ -4,7 +4,7 @@
<view class="goods"> <view class="goods">
<view class="u-m-t-24 u-flex u-col-top u-relative"> <view class="u-m-t-24 u-flex u-col-top u-relative">
<view class="img"> <view class="img">
<up--image :width="63" :height="63" :radius="3" :src="data.coverImg"></up--image> <up-image :width="63" :height="63" :radius="3" :src="data.coverImg"></up-image>
</view> </view>
<view class="w-full info u-p-l-30"> <view class="w-full info u-p-l-30">
<view class="color-333 u-flex u-row-between"> <view class="color-333 u-flex u-row-between">

View File

@ -26,7 +26,7 @@
</label> </label>
</view> </view>
<view class="img"> <view class="img">
<up--image :width="63" :height="63" :radius="3" :src="data.coverImg"></up--image> <up-image :width="63" :height="63" :radius="3" :src="data.coverImg"></up-image>
</view> </view>
<view class="w-full info"> <view class="w-full info">
<view class="info-p-l color-333 u-flex u-row-between"> <view class="info-p-l color-333 u-flex u-row-between">

View File

@ -79,7 +79,6 @@
name: '', name: '',
totalElements: 0, totalElements: 0,
size: 10, size: 10,
isVip: 1
}) })
const list = reactive([]) const list = reactive([])
let hasAjax = ref(false) let hasAjax = ref(false)

View File

@ -0,0 +1,610 @@
<template>
<view
class="bg-fff border-r-24 u-m-t-32"
v-if="orderInfo && orderInfo.detailMap"
>
<view
class="u-m-b-20"
v-for="(goods, orderIndex) in orderInfo.detailMap"
:key="orderIndex"
>
<view class="u-p-t-24"> {{ orderIndex }}次下单 </view>
<view class="u-m-t-24 list">
<view class="item u-m-b-32" v-for="(item, index) in goods" :key="index">
<view class="u-flex u-col-top">
<view class="u-flex u-relative">
<view class="limit-discount" v-if="item.isTimeDiscount"
>限时折扣</view
>
<image
v-if="item.isTemporary == 0"
class="img"
:src="item.coverImg || item.productImg"
mode=""
></image>
<view
v-else
style="
background-color: #3f9eff;
width: 152rpx;
height: 152rpx;
line-height: 152rpx;
text-align: center;
color: #fff;
"
>
临时菜
</view>
</view>
<view class="u-p-l-32 u-flex-1">
<view class="u-flex u-row-between u-col-top">
<view class="">
<view class="u-flex">
<view class="tui" v-if="isTui(item)">
{{
item.status == "part_refund" ? "部分已退" : "全部已退"
}}
</view>
<view
:class="{
'line-th':
item.status == 'return' ||
item.status == 'refund' ||
item.status == 'refunding',
}"
>
{{ item.name || item.productName }}
</view>
</view>
<view
class="u-flex u-m-t-8"
style="flex-direction: column; align-items: flex-start"
>
<view class="u-flex u-m-b-8">
<view class="u-m-r-20 u-flex" v-if="item.isGift">
<uni-tag
text="赠送"
custom-style="background-color: #FFF0DF; border-color: #FFF0DF; color: #FF9F2E;"
>
</uni-tag>
</view>
<view class="u-m-r-20 u-flex" v-if="item.userCouponId">
<uni-tag
:text="productCouponDikou(item)"
custom-style="background-color: #FFF0DF; border-color: #FFF0DF; color: #FF9F2E;"
>
</uni-tag>
</view>
<view class="u-m-r-20 u-flex" v-if="item.packNumber > 0">
<uni-tag
custom-style="background-color: #E6F0FF; border-color: #E6F0FF; color: #318AFE;"
size="small"
text="打包"
inverted
type="success"
/>
</view>
</view>
<view class="u-flex u-m-t-8">
<view
class="u-m-r-20 u-font-24 u-flex"
v-if="item.refundNum > 0"
>
<view class="color-666">退款金额:</view>
<view class="color-999 u-m-l-6">{{
item.refundNum * item.unitPrice
}}</view>
</view>
<view
class="u-m-r-20 u-font-24 u-flex"
v-if="item.returnNum"
>
<view class="color-666">退菜数量:</view>
<view class="color-999 u-m-l-6">{{
item.returnNum
}}</view>
</view>
</view>
</view>
<view class="color-999 u-font-24 u-m-t-8">{{
item.skuName || ""
}}</view>
<view class="u-m-t-12 color-666 u-font-24" v-if="item.remark">
备注{{ item.remark }}
</view>
</view>
<view class="u-text-right u-m-t-28">
<view class="u-relative">
<template v-if="item.isGift">
<text class="line-th color-999"
>{{ toFixed(item.price * item.num, item) }}</text
>
<view class="u-absolute" style="right: 0; bottom: 100%">
<text class="font-bold">0</text>
</view>
</template>
<template v-else>
<template
v-if="
item.discountSaleAmount &&
item.discountSaleAmount * 1 > 0
"
>
<text class="line-th color-999"
>{{ toFixed(item.price * item.num, item) }}</text
>
<view class="u-absolute" style="right: 0; bottom: 100%">
<text class="font-bold"
>{{
toFixed(item.discountSaleAmount * item.num, item)
}}</text
>
</view>
</template>
<template v-else-if="item.isTimeDiscount">
<text class="line-th color-999"
>{{ toFixed(item.price * item.num, item) }}</text
>
<view
class="u-absolute xianshi"
style="right: 0; bottom: 100%"
>
<text class="font-bold"
>{{ returnLimitTotalMoney(item) }}</text
>
</view>
</template>
<template
v-else-if="
isVip &&
item.price &&
item.price * 1 != item.memberPrice * 1
"
>
<text class="line-th color-999"
>{{ toFixed(item.price * item.num, item) }}</text
>
<view class="u-absolute" style="right: 0; bottom: 100%">
<text class="font-bold"
>{{
toFixed(item.memberPrice * item.num, item)
}}</text
>
</view>
</template>
<template v-else>
<view class="font-bold">
<text></text>
<text class="">{{
toFixed(item.price * item.num, item)
}}</text>
</view>
</template>
</template>
</view>
<view class="u-m-t-22 color-999 u-font-24"
>X{{ item.num || item.num }}</view
>
</view>
</view>
</view>
</view>
<!-- <template v-if="canTuicai(orderInfo, item)"> -->
<template v-if="false">
<view
class="u-flex u-row-right gap-20 u-m-t-24"
v-if="item.returnNum * item.unitPrice < item.num * item.unitPrice"
>
<my-button
:width="128"
:height="48"
plain
shape="circle"
@tap="tuicai(item, index)"
><text class="no-wrap">退菜</text></my-button
>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed, reactive,inject } from "vue";
import BigNumber from "bignumber.js";
import { hasPermission } from "@/commons/utils/hasPermission.js";
import {
isTui,
isTuiCai,
isGift,
canTuiKuan,
canTuicai,
mathFloorPrice,
} from "@/commons/utils/goodsUtil.js";
const emits = defineEmits(["tuicai", "tuikuan", "printOrder"]);
const yskUtils = inject("yskUtils");
const shopInfo = inject("shopInfo");
const pageData = inject("pageData");
const pop = reactive({
youhui: false,
});
const props = defineProps({
orderInfo: {
type: Object,
default: () => {},
},
data: {
type: Array,
default: () => [],
},
seatFee: {
type: Object,
default: () => {},
},
user: {
type: Object,
default: () => {
return {
id: "",
isVip: false,
};
},
},
});
/**
* 计算菜品数量
*/
const goodsNumber = computed(() => {
let result = 0;
result = props.data.reduce((a, b) => {
const bTotal = b.info.length;
return a + bTotal;
}, 0);
return result.toFixed(0);
});
/**
* 桌位费
*/
const seatFeePrice = computed(() => {
const n =
props.orderInfo.seatNum > 0
? props.orderInfo.seatNum * uni.getStorageSync("shopInfo").tableFee
: 0;
return n.toFixed(2);
});
function toFixed(price, item) {
if (item) {
if (item.productType == "weight" || item.type == "weight") {
return (Math.floor(price * 100) / 100).toFixed(2);
} else {
return parseFloat(price).toFixed(2);
}
} else {
return parseFloat(price).toFixed(2);
}
}
/**
* 判断是否是会员
*/
const isVip = computed(() => {
return (
uni.getStorageSync("shopInfo").isMemberPrice &&
props.user &&
props.user.id &&
props.user.isVip
);
});
function returnLimitTotalMoney(data) {
const price = yskUtils.limitUtils.returnPrice({
goods: data,
shopInfo: pageData.shopInfo,
limitTimeDiscountRes: pageData.limitTimeDiscount,
shopUserInfo: pageData.user,
idKey: "product_id",
});
return BigNumber(price).times(data.number).toNumber();
}
const vipDiscountPrice = computed(() => {
if (!isVip.value) {
return 0;
}
const goodsPrice = props.data.reduce((prve, cur) => {
const curTotal = cur.info
.filter(
(v) =>
v.discountSaleAmount <= 0 &&
v.isGift != 1 &&
v.status !== "return" &&
v.price != v.unitPrice &&
v.memberPrice != v.price
)
.reduce((a, b) => {
return a + b.num * (b.price - b.memberPrice);
}, 0);
return prve + curTotal;
}, 0);
return goodsPrice.toFixed(2);
});
/**
* 单品打折优惠
*/
const discountSaleAmount = computed(() => {
const goodsPrice = props.data.reduce((prve, cur) => {
const curTotal = cur.info
.filter(
(v) =>
v.discountSaleAmount > 0 && v.isGift != 1 && v.status !== "return"
)
.reduce((a, b) => {
return a + b.num * (b.price - b.discountSaleAmount);
}, 0);
return prve + curTotal;
}, 0);
return goodsPrice.toFixed(2);
});
/**
* 打折优惠
*/
const discountAmount = computed(() => {
return props.orderInfo.discountAmount || 0;
});
/**
* 总优惠金额
*/
const discountsPrice = computed(() => {
//
let fullCouponDiscountAmount =
props.orderInfo.status == "done"
? props.orderInfo.fullCouponDiscountAmount
: 0;
//
let productCouponDiscountAmount =
props.orderInfo.status == "done"
? props.orderInfo.productCouponDiscountAmount
: 0;
//
let pointsDiscountAmount =
props.orderInfo.status == "done" ? props.orderInfo.pointsDiscountAmount : 0;
return (
parseFloat(vipDiscountPrice.value) +
parseFloat(discountSaleAmount.value) +
discountAmount.value +
fullCouponDiscountAmount +
productCouponDiscountAmount +
pointsDiscountAmount
).toFixed(2);
});
//
const productCoupPrice = computed(() => {
if (props.orderInfo.status == "done") {
return props.orderInfo.productCouponDiscountAmount;
}
const goodsPrice = props.data.reduce((a, b) => {
const curTotal = b.info
.filter((v) => !v.isGift)
.reduce((prve, cur) => {
let memberPrice = cur.memberPrice ? cur.memberPrice : cur.price;
let tPrice = isVip.value ? memberPrice : cur.price;
tPrice =
cur.memberPrice != cur.unitPrice && cur.price != cur.unitPrice
? cur.unitPrice
: tPrice;
let Total = Math.floor(tPrice * cur.num * 100) / 100;
return (
prve + Total - cur.returnNum * tPrice - cur.refundNum * cur.unitPrice
);
}, 0);
return a + curTotal;
}, 0);
// console.log("==",goodsPrice)
return goodsPrice.toFixed(2);
});
const allPpackFee = computed(() => {
//退
const goodsPrice = props.data.reduce((prve, cur) => {
const curTotal = cur.info
.filter((v) => v.packNumber > 0)
.reduce((a, b) => {
return a + parseFloat(b.packAmount * b.packNumber).toFixed(2) * 1;
}, 0);
return prve + curTotal;
}, 0);
return goodsPrice.toFixed(2);
});
const packFee = computed(() => {
//退
const goodsPrice = props.data.reduce((prve, cur) => {
const curTotal = cur.info
.filter((v) => v.status !== "return" && v.returnNum + v.refundNum < v.num)
.reduce((a, b) => {
return (
a +
parseFloat(
(
b.packAmount *
(b.num - (b.returnNum + b.refundNum) > b.packNumber
? b.packNumber
: b.num - (b.returnNum + b.refundNum))
).toFixed(2)
)
);
}, 0);
return prve + curTotal;
}, 0);
return goodsPrice.toFixed(2);
});
const allPrice = computed(() => {
let seatAmount = props.orderInfo.seatAmount || 0;
const total = productCoupPrice.value * 1 + seatAmount * 1 + packFee.value * 1;
return (total <= 0 ? 0 : total).toFixed(2);
});
/**
* 已优惠金额
*/
const youhuiAllPrice = computed(() => {
const n = vipDiscountPrice.value * 1;
return (n < 0 ? 0 : n).toFixed(2);
});
function youhuiDetailShow() {
pop.youhui = true;
}
function productCouponDikou(item) {
return "商品券抵扣¥" + returnProductCoupPrice(item);
}
function youhuiDetailHide() {
pop.youhui = false;
}
/**
* 转桌/并桌
*/
function rotatingTables() {
let arr = [];
props.data.forEach((ele) => {
ele.info.forEach((res) => {
// coverImg
res.coverImg = res.productImg;
// name
res.name = res.productName;
// price
res.price = res.price;
// number
res.number = res.num;
res.masterId = props.orderInfo.masterId;
res.useType = props.orderInfo.useType;
res.tableId = props.orderInfo.tableId;
arr.push(res);
});
});
uni.navigateTo({
url:
"/pagesCreateOrder/confirm-order/rotatingTables?item=" +
JSON.stringify(arr) +
"&tableId=" +
props.orderInfo.tableId,
});
}
function returnProductCoupPrice(item) {
if (!item.isMember) {
return item.price * item.num;
}
const price = item.memberPrice ? item.memberPrice : item.price;
return price * item.num;
}
function returnCanTuiMoney(item) {
// if (props.orderInfo.status == 'unpaid') {
// return returnTotalMoney(item)
// } else {
if (
props.orderInfo.pointsDiscountAmount > 0 ||
props.orderInfo.fullCouponDiscountAmount > 0
) {
return item.canReturnAmount;
} else if (item.price != item.unitPrice) {
return item.price * item.num;
} else {
return (Math.floor(item.num * item.unitPrice * 100) / 100).toFixed(2);
}
// }
}
function tuicai(item, index) {
emits("tuicai", item, index);
}
</script>
<style lang="scss" scoped>
.img {
width: 152rpx;
height: 152rpx;
border-radius: 6px;
}
.border-top {
border-color: #f6f6f6;
}
.border-r-24 {
border-radius: 24rpx;
}
.border-bottom {
// border-color: rgb(240, 240, 240);
border-color: #f6f6f6;
}
.line-th {
text-decoration: line-through;
}
.tag {
padding: 4rpx 8rpx 2rpx 10rpx;
border-radius: 8rpx;
font-size: 24rpx;
&.no-pay {
background-color: rgb(170, 170, 170);
color: #fff;
}
&.refund {
background-color: #fce7e7;
padding: 8rpx 20rpx 6rpx 22rpx;
color: #eb4f4f;
}
}
.tui {
background-color: rgb(239, 239, 239);
border-radius: 4rpx;
margin-right: 6rpx;
color: #666;
padding: 0 4rpx;
font-size: 20rpx;
}
.limit-discount {
background-color: #cc5617;
padding: 2rpx 10rpx;
white-space: nowrap;
text-align: center;
position: absolute;
top: 0;
left: 0;
font-weight: 400;
font-size: 24rpx;
color: #ffffff;
border-radius: 20rpx 0rpx 20rpx 0rpx;
z-index: 9;
color: #fff;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,285 +1,402 @@
<template> <template>
<view> <view>
<view class="mask" @tap="hideGoods" v-if="switchGoods"></view> <view class="mask" @tap="hideGoods" v-if="switchGoods"></view>
<view class="car border-top u-flex u-row-between u-col-bottom u-relative"> <view class="car border-top u-flex u-row-between u-col-bottom u-relative">
<view class="u-absolute goods bg-fff"> <view class="u-absolute goods bg-fff">
<view class="u-p-t-32 color-666 border-bottom bg-fff u-absolute total u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between"> <view
<view>已添加{{ goodsNumber.toFixed(0) }}件商品</view> class="u-p-t-32 color-666 border-bottom bg-fff u-absolute total u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between"
<view class="color-666"> >
<uni-icons color="#666" type="trash"></uni-icons> <view>已添加{{ goodsNumber.toFixed(0) }}件商品</view>
<text class="u-m-l-10" @tap="setModalShow('clear', true, 'cart', '是否清空全部已添加的商品')">清空购物车</text> <view class="color-666">
</view> <uni-icons color="#666" type="trash"></uni-icons>
</view> <text
<scroll-view scroll-y="true" class="tranistion" :style="{ height: switchGoods ? '50vh' : 0 }"> class="u-m-l-10"
<!-- 占位 --> @tap="
<view class="u-p-t-32 color-666 border-bottom u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between" style="opacity: 0"> setModalShow('clear', true, 'cart', '是否清空全部已添加的商品')
<view>已添加{{ goodsNumber.toFixed(0) }}件商品</view> "
<view class="color-666"> >清空购物车</text
<uni-icons color="#666" type="trash"></uni-icons> >
<text class="u-m-l-10">清空</text> </view>
</view> </view>
</view> <scroll-view
<!-- 占位 --> scroll-y="true"
<view class="color-333 item border-top u-flex u-row-center u-row-between" v-for="(item, index) in data" :key="index"> class="tranistion"
<view> :style="{ height: switchGoods ? '50vh' : 0 }"
<view class="up-line-1">{{ item.name }}</view> >
<view class="u-m-t-10 u-font-24 color-666 up-line-1">{{ item.specInfo || '' }}</view> <!-- 占位 -->
</view> <view
<view class="u-flex" style="flex-shrink: 0"> class="u-p-t-32 color-666 border-bottom u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between"
<view class="font-bold red u-m-r-32">{{ formatPrice(item.lowPrice * item.number) }}</view> style="opacity: 0"
<view class="u-flex" @tap="updateNumber(false, index, item)"> >
<image src="/pagesCreateOrder/static/images/icon-reduce-black.svg" class="icon" mode="" /> <view>已添加{{ goodsNumber.toFixed(0) }}件商品</view>
</view> <view class="color-666">
<view class="u-m-l-30 u-m-r-30 color-333"> <uni-icons color="#666" type="trash"></uni-icons>
{{ item.number.toFixed(2) }} <text class="u-m-l-10">清空</text>
</view> </view>
<view class="u-flex" @tap="updateNumber(true, index, item)"> </view>
<image <!-- 占位 -->
src="/pagesCreateOrder/static/images/icon-add-black.svg" <view
class="icon" class="color-333 item border-top u-flex u-row-center u-row-between"
:class="{ grayscale: item.type == 'package' && item.groupType == 1 }" v-for="(item, index) in data"
mode="" :key="index"
/> >
</view> <view>
</view> <view class="up-line-1">{{ item.name }}</view>
</view> <view class="u-m-t-10 u-font-24 color-666 up-line-1">{{
<view style="margin: 50rpx auto 110rpx auto" v-if="!data.length"> item.specInfo || ""
<my-empty text="暂未有添加商品"></my-empty> }}</view>
</view> </view>
<!-- 历史订单 --> <view class="u-flex" style="flex-shrink: 0">
<view v-if="historyOrder.length > 0" class="u-p-t-32 u-p-b-32 u-p-r-28 u-p-l-28 u-m-t-40 bg-fff u-flex u-row-between"> <view class="font-bold red" v-if="item.is_time_discount"
<view class="color-333" style="font-weight: bold">历史订单</view> >{{ returnLimitTotalPrice(item) }}
<view class="color-666"> </view>
<uni-icons color="#666" type="trash"></uni-icons> <view
<text class="u-m-l-10" @tap="setModalShow('clear', true, 'allHistoryOrder', '清空历史订单')">清空历史订单</text> class="font-bold red u-m-r-32"
</view> :class="[item.is_time_discount ? 'old-price' : '']"
</view> >{{ formatPrice(item.lowPrice * item.number) }}</view
<view v-for="(item, index) in historyOrder" :key="index"> >
<view v-if="historyOrder.length > 0" class="u-p-t-32 border-top bg-fff u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between"> <view class="u-flex" @tap="updateNumber(false, index, item)">
<view class="color-333" style="font-size: 30rpx">{{ item.placeNum }}次下单</view> <image
<view class="color-666"> src="/pagesCreateOrder/static/images/icon-reduce-black.svg"
<uni-icons color="#666" type="trash"></uni-icons> class="icon"
<text class="u-m-l-10" @tap="setModalShow('clear', true, item.placeNum, '清空第' + item.placeNum + '次下单历史订单')">清空</text> mode=""
</view> />
</view> </view>
<view class="color-333 item border-top u-flex u-row-center u-row-between" v-for="(v, i) in item.info" :key="i"> <view class="u-m-l-30 u-m-r-30 color-333">
<view style="display: flex; align-items: center"> {{ item.number.toFixed(2) }}
<view class="up-line-1" style="margin-right: 10rpx">{{ v.productName }}</view> </view>
<uni-tag <view class="u-flex" @tap="updateNumber(true, index, item)">
v-if="v.returnNum > 0" <image
:text="'退菜X' + v.returnNum" src="/pagesCreateOrder/static/images/icon-add-black.svg"
custom-style="background-color: #EB4F4F; border-color: #EB4F4F; color: #fff;" class="icon"
></uni-tag> :class="[
</view> item.type == 'package' && item.groupType == 1
<view class="u-flex" style="flex-shrink: 0"> ? grayscale
<view class="font-bold red u-m-r-32">{{ formatPrice(v.price * (v.num - v.returnNum)) }}</view> : '',
<view class="u-m-l-30 u-m-r-30 color-333">X{{ v.num.toFixed(2) }}</view> ]"
</view> mode=""
</view> />
</view> </view>
</scroll-view> </view>
</view> </view>
<view class="icon-car-box" @tap="toggleGoods"> <view style="margin: 50rpx auto 110rpx auto" v-if="!data.length">
<image src="/pagesCreateOrder/static/images/icon-car.svg" class="icon-car" /> <my-empty text="暂未有添加商品"></my-empty>
<view class="dot" v-if="goodsNumber > 0">{{ goodsNumber }}</view> </view>
</view> <!-- 历史订单 -->
<view class="price font-bold u-flex"> <view
<view></view> v-if="historyOrder.length > 0"
<view>{{ allPrice }}</view> class="u-p-t-32 u-p-b-32 u-p-r-28 u-p-l-28 u-m-t-40 bg-fff u-flex u-row-between"
</view> >
<my-button shape="circle" height="80" width="220" @tap="toConfimOrder"> <view class="color-333" style="font-weight: bold">历史订单</view>
<text class="u-font-32 font-bold">{{ table.type == 'add' ? '确认加菜' : '去下单' }}</text> <view class="color-666">
</my-button> <uni-icons color="#666" type="trash"></uni-icons>
</view> <text
<up-modal class="u-m-l-10"
title="提示" @tap="
:content="modal.title" setModalShow('clear', true, 'allHistoryOrder', '清空历史订单')
:show="modal.clear" "
showCancelButton >清空历史订单</text
closeOnClickOverlay >
@confirm="confirmModelConfirm" </view>
@cancel="setModalShow('clear', false)" </view>
@close="setModalShow('clear', false)" <view v-for="(item, index) in historyOrder" :key="index">
width="300px" <view
></up-modal> v-if="historyOrder.length > 0"
</view> class="u-p-t-32 border-top bg-fff u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between"
>
<view class="color-333" style="font-size: 30rpx"
>{{ item.placeNum }}次下单</view
>
<view class="color-666">
<uni-icons color="#666" type="trash"></uni-icons>
<text
class="u-m-l-10"
@tap="
setModalShow(
'clear',
true,
item.placeNum,
'清空第' + item.placeNum + '次下单历史订单'
)
"
>清空</text
>
</view>
</view>
<view
class="color-333 item border-top u-flex u-row-center u-row-between"
v-for="(v, i) in item.info"
:key="i"
>
<view style="display: flex; align-items: center">
<view class="up-line-1" style="margin-right: 10rpx">{{
v.productName
}}</view>
<uni-tag
v-if="v.returnNum > 0"
:text="'退菜X' + v.returnNum"
custom-style="background-color: #EB4F4F; border-color: #EB4F4F; color: #fff;"
></uni-tag>
</view>
<view class="u-flex" style="flex-shrink: 0">
<view class="font-bold red u-m-r-32"
>{{ formatPrice(v.price * (v.num - v.returnNum)) }}</view
>
<view class="u-m-l-30 u-m-r-30 color-333"
>X{{ v.num.toFixed(2) }}</view
>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="icon-car-box" @tap="toggleGoods">
<image
src="/pagesCreateOrder/static/images/icon-car.svg"
class="icon-car"
/>
<view class="dot" v-if="goodsNumber > 0">{{ goodsNumber }}</view>
</view>
<view class="price font-bold u-flex">
<view></view>
<view>{{ allPrice }}</view>
</view>
<my-button shape="circle" height="80" width="220" @tap="toConfimOrder">
<text class="u-font-32 font-bold">{{
table.type == "add" ? "确认加菜" : "去下单"
}}</text>
</my-button>
</view>
<up-modal
title="提示"
:content="modal.title"
:show="modal.clear"
showCancelButton
closeOnClickOverlay
@confirm="confirmModelConfirm"
@cancel="setModalShow('clear', false)"
@close="setModalShow('clear', false)"
width="300px"
></up-modal>
</view>
</template> </template>
<script setup> <script setup>
import { computed, reactive, ref, watch } from 'vue'; import { computed, reactive, ref, watch, inject } from "vue";
import myButton from '@/components/my-components/my-button.vue'; import myButton from "@/components/my-components/my-button.vue";
import go from '@/commons/utils/go.js'; import go from "@/commons/utils/go.js";
import infoBox from '@/commons/utils/infoBox.js'; import BigNumber from "bignumber.js";
import { formatPrice } from '@/commons/utils/format.js';
import { $trturnPayAfter } from '../util.js'
import infoBox from "@/commons/utils/infoBox.js";
import { formatPrice } from "@/commons/utils/format.js";
import { $trturnPayAfter } from "../util.js";
const yskUtils = inject("yskUtils");
const shopInfo = uni.getStorageSync("shopInfo");
const props = defineProps({ const props = defineProps({
data: { data: {
type: Array, type: Array,
default: () => { default: () => {
return []; return [];
} },
}, },
historyOrder: { historyOrder: {
type: Array, type: Array,
default: () => { default: () => {
return []; return [];
} },
}, },
isCreateOrderToDetail: { isCreateOrderToDetail: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
orderInfo: { orderInfo: {
type: Object, type: Object,
default: () => { default: () => {
return { return {
id: '' id: "",
}; };
} },
}, },
table: { table: {
type: Object, type: Object,
default: () => { default: () => {
return { return {
tableId: '' tableId: "",
}; };
} },
} },
limitTimeDiscount: {
type: Object,
default: () => {
return {};
},
},
}); });
function returnLimitPrice(data) {
const price = yskUtils.limitUtils.returnPrice({
goods: data,
shopInfo: shopInfo,
limitTimeDiscountRes: props.limitTimeDiscount,
shopUserInfo: null,
idKey: "product_id",
});
return price;
}
function returnLimitTotalPrice(data) {
console.log('returnLimitTotalPrice',data)
const price = yskUtils.limitUtils.returnPrice({
goods: data,
shopInfo: shopInfo,
limitTimeDiscountRes: props.limitTimeDiscount,
shopUserInfo: null,
idKey: "product_id",
});
return BigNumber(price).times(data.number).toNumber();
}
let allHistoryOrder = ref([]); let allHistoryOrder = ref([]);
const allPrice = computed(() => { const allPrice = computed(() => {
let cartPrice = props.data.reduce((prve, cur) => { let cartPrice = props.data.reduce((prve, cur) => {
let price = Math.floor(cur.lowPrice * cur.number * 100) / 100; let price =
return prve + price; (cur.is_time_discount ? returnLimitPrice(cur) : cur.lowPrice) *
}, 0); cur.number;
let historyOrderPrice = allHistoryOrder.value.reduce((prve, cur) => { return BigNumber(prve).plus(price);
let price = Math.floor(cur.price * (cur.num - cur.returnNum) * 100) / 100; }, 0);
return prve + price; let historyOrderPrice = allHistoryOrder.value.reduce((prve, cur) => {
}, 0); let price =
(cur.isTimeDiscount
return (cartPrice + historyOrderPrice).toFixed(2); ? returnLimitPrice({ ...cur, salePrice: cur.price })
: cur.price) *
(cur.num - cur.returnNum);
return BigNumber(prve).plus(price);
}, 0);
return BigNumber(cartPrice)
.plus(historyOrderPrice)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}); });
const goodsNumber = computed(() => { const goodsNumber = computed(() => {
let result = 0; let result = 0;
let cart = props.data.reduce((prve, cur) => { let cart = props.data.reduce((prve, cur) => {
return prve + cur.number; return prve + cur.number;
}, 0); }, 0);
// let historyOrderNum = allHistoryOrder.value.reduce((prve, cur) => { // let historyOrderNum = allHistoryOrder.value.reduce((prve, cur) => {
// return prve + cur.num; // return prve + cur.num;
// }, 0); // }, 0);
result = cart; result = cart;
result = result > 0 ? result.toFixed(2) : 0; result = result > 0 ? result.toFixed(2) : 0;
return result >= 99 ? 99 : parseFloat(result); return result >= 99 ? 99 : parseFloat(result);
}); });
watch( watch(
() => props.historyOrder, () => props.historyOrder,
(newval) => { (newval) => {
allHistoryOrder.value = []; allHistoryOrder.value = [];
newval.forEach((item) => { newval.forEach((item) => {
allHistoryOrder.value = [...allHistoryOrder.value, ...item.info]; allHistoryOrder.value = [...allHistoryOrder.value, ...item.info];
}); });
} }
); );
const modal = reactive({ const modal = reactive({
key: '', key: "",
title: '', title: "",
type: '', type: "",
clear: false clear: false,
}); });
function confirmModelConfirm() { function confirmModelConfirm() {
if (modal.key == 'clear') { if (modal.key == "clear") {
clear(); clear();
} }
} }
function setModalShow(key = 'show', show = true, type = '', title = '') { function setModalShow(key = "show", show = true, type = "", title = "") {
// if (key == 'clear' && show && props.data.length <= 0) { // if (key == 'clear' && show && props.data.length <= 0) {
// return infoBox.showToast('') // return infoBox.showToast('')
// } // }
if (title) { if (title) {
modal.title = title; modal.title = title;
} }
if (type) { if (type) {
modal.type = type; modal.type = type;
} }
modal.key = key; modal.key = key;
modal[key] = show; modal[key] = show;
console.log(modal); console.log(modal);
} }
const edmits = defineEmits(['clear', 'updateNumber']); const edmits = defineEmits(["clear", "updateNumber"]);
// mask // mask
let switchGoods = ref(false); let switchGoods = ref(false);
function hideGoods() { function hideGoods() {
switchGoods.value = false; switchGoods.value = false;
} }
function showGoods() { function showGoods() {
switchGoods.value = true; switchGoods.value = true;
} }
function toggleGoods() { function toggleGoods() {
switchGoods.value = !switchGoods.value; switchGoods.value = !switchGoods.value;
} }
function toConfimOrder() { function toConfimOrder() {
if (props.data.length <= 0 && allHistoryOrder.value.length <= 0) { if (props.data.length <= 0 && allHistoryOrder.value.length <= 0) {
return infoBox.showToast('还没有选择商品'); return infoBox.showToast("还没有选择商品");
} }
const { name, status, type } = props.table; const { name, status, type } = props.table;
if (props.data.length <= 0 && allHistoryOrder.value.length > 0) { if (props.data.length <= 0 && allHistoryOrder.value.length > 0) {
go.to('PAGES_ORDER_PAY', { go.to("PAGES_ORDER_PAY", {
orderId: props.orderInfo.id orderId: props.orderInfo.id,
}); });
return; return;
} }
if (props.table.id == '' && props.table.tableCode == '') { if (props.table.id == "" && props.table.tableCode == "") {
go.to('PAGES_CONFIRM_ORDER', { go.to("PAGES_CONFIRM_ORDER", {
isCreateOrderToDetail: props.isCreateOrderToDetail ? 1 : 0 isCreateOrderToDetail: props.isCreateOrderToDetail ? 1 : 0,
}); });
return; return;
} }
let shopInfo = uni.getStorageSync('shopInfo'); let shopInfo = uni.getStorageSync("shopInfo");
console.log($trturnPayAfter(shopInfo)); console.log($trturnPayAfter(shopInfo));
go.to('PAGES_CONFIRM_ORDER', { go.to("PAGES_CONFIRM_ORDER", {
type: type, type: type,
tableId: props.table.id, tableId: props.table.id,
tableCode: props.table.tableCode, tableCode: props.table.tableCode,
name: name, name: name,
status: status, status: status,
isCreateOrderToDetail: props.isCreateOrderToDetail ? 1 : 0 isCreateOrderToDetail: props.isCreateOrderToDetail ? 1 : 0,
}); });
} }
function updateNumber(isAdd, index, goods) { function updateNumber(isAdd, index, goods) {
const step = isAdd ? 1 : -1; const step = isAdd ? 1 : -1;
const newval = goods.number + step; const newval = goods.number + step;
if (goods.type == 'package' && goods.groupType == 1 && isAdd) { if (goods.type == "package" && goods.groupType == 1 && isAdd) {
return; return;
} }
const par = { const par = {
num: newval, num: newval,
index: index, index: index,
goods: goods goods: goods,
}; };
edmits('updateNumber', par); edmits("updateNumber", par);
} }
function clear() { function clear() {
if (modal.type == 'cart') { if (modal.type == "cart") {
hideGoods(); hideGoods();
} }
setModalShow('clear', false); setModalShow("clear", false);
edmits('clear', modal.type); edmits("clear", modal.type);
} }
</script> </script>
@ -288,104 +405,108 @@ $car-size: 96rpx;
$car-top: -16rpx; $car-top: -16rpx;
@mixin fixedAll { @mixin fixedAll {
position: fixed; position: fixed;
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
} }
.total { .total {
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
z-index: 1; z-index: 1;
} }
.icon { .icon {
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
} }
.grayscale { .grayscale {
filter: opacity(60%); filter: opacity(60%);
} }
.mask { .mask {
@include fixedAll; @include fixedAll;
background: rgba(51, 51, 51, 0.5); background: rgba(51, 51, 51, 0.5);
} }
.goods { .goods {
position: absolute; position: absolute;
bottom: 100%; bottom: 100%;
left: 0; left: 0;
right: 0; right: 0;
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
overflow: hidden; overflow: hidden;
.item { .item {
padding: 32rpx 28rpx; padding: 32rpx 28rpx;
} }
} }
.border-bottom { .border-bottom {
border-bottom: 1px solid #e5e5e5; border-bottom: 1px solid #e5e5e5;
} }
.border-top { .border-top {
border-top: 1px solid #e5e5e5; border-top: 1px solid #e5e5e5;
} }
.red { .red {
color: #eb4f4f; color: #eb4f4f;
} }
.car { .car {
padding: 0 28rpx; padding: 0 28rpx;
position: relative; position: relative;
background-color: #fff; background-color: #fff;
padding-top: 10rpx; padding-top: 10rpx;
padding-bottom: calc(40rpx + env(safe-area-inset-bottom)); padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
/* #ifdef H5 */ /* #ifdef H5 */
padding-bottom: 68rpx; padding-bottom: 68rpx;
/* #endif */ /* #endif */
.icon-car-box { .icon-car-box {
position: absolute; position: absolute;
left: 28rpx; left: 28rpx;
top: $car-top; top: $car-top;
display: flex; display: flex;
width: $car-size; width: $car-size;
height: $car-size; height: $car-size;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
z-index: 2; z-index: 2;
.dot { .dot {
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
width: 28rpx; width: 28rpx;
height: 28rpx; height: 28rpx;
background: #eb4f4f; background: #eb4f4f;
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 20rpx; font-size: 20rpx;
font-weight: bold; font-weight: bold;
color: #ffffff; color: #ffffff;
} }
} }
.price { .price {
color: #eb4f4f; color: #eb4f4f;
margin-left: calc(38rpx + $car-size); margin-left: calc(38rpx + $car-size);
transform: translateY(calc($car-top / 2)); transform: translateY(calc($car-top / 2));
} }
.icon-car { .icon-car {
width: $car-size; width: $car-size;
height: $car-size; height: $car-size;
} }
}
.old-price {
color: #999;
text-decoration: line-through;
} }
</style> </style>

View File

@ -1,233 +1,299 @@
<template> <template>
<my-model ref="model" borderRadius="12" :title="title"> <my-model ref="model" borderRadius="12" :title="title">
<template #desc> <template #desc>
<scroll-view scroll-y="true" style="height: 50vh;" class="u-p-30 guigeModel"> <scroll-view
<view class="u-m-b-40" v-for="(item,index) in skus" :key="index"> scroll-y="true"
<view class="u-text-left"> style="height: 50vh"
<view class="color-333 up-line-1" >{{item.name}}</view> class="u-p-30 guigeModel"
</view> >
<view class="u-flex u-m-t-20 u-flex-wrap"> <view class="u-m-b-40" v-for="(item, index) in skus" :key="index">
<view class="item" @tap="chooseSkd(index,skd)" <view class="u-text-left">
:class="{active:item.sel===skd.name,disabled:skd.disabled}" <view class="color-333 up-line-1">{{ item.name }}</view>
v-for="(skd,skdIndex) in item.values" :key="skdIndex"> </view>
{{skd.name}} <view class="u-flex u-m-t-20 u-flex-wrap">
</view> <view
</view> class="item"
</view> @tap="chooseSkd(index, skd)"
</scroll-view> :class="{ active: item.sel === skd.name, disabled: skd.disabled }"
</template> v-for="(skd, skdIndex) in item.values"
<template #btn> :key="skdIndex"
<view class="u-p-30 border-top "> >
<view class="u-flex u-p-b-30 u-row-between"> {{ skd.name }}
<view class="price"> </view>
<template v-if="goods&&goods.isGrounding"> </view>
<text></text> </view>
<text>{{to2(goods.salePrice*number) }}</text> </scroll-view>
</template> </template>
</view> <template #btn>
<view class="u-flex"> <view class="u-p-30 border-top">
<view class="u-flex" @tap="reduce"> <view class="u-flex u-p-b-30 u-row-between">
<image src="/pagesCreateOrder/static/images/icon-reduce-black.svg" class="icon" mode=""> <view class="price">
</image> <template v-if="goods && goods.isGrounding">
</view> <text v-if="is_time_discount"
<view class="u-m-l-30 u-m-r-30 color-333"> >{{ returnLimitPrice() }}</text
{{number}} >
</view> <text :class="{ oldPrice: is_time_discount }"
<view class="u-flex" @tap="add"> >{{ goods.salePrice }}</text
<image src="/pagesCreateOrder/static/images/icon-add-black.svg" class="icon" mode=""> >
</image> </template>
</view> </view>
</view> <view class="u-flex">
</view> <view class="u-flex" @tap="reduce">
<view class="u-m-t-10"> <image
<my-button @tap="close" type="cancel" v-if="isDisabled"> src="/pagesCreateOrder/static/images/icon-reduce-black.svg"
<view class="color-999">已下架/售罄</view> class="icon"
</my-button> mode=""
<my-button @tap="confirm" v-else>添加</my-button> >
</view> </image>
</view> </view>
</template> <view class="u-m-l-30 u-m-r-30 color-333">
</my-model> {{ number }}
</view>
<view class="u-flex" @tap="add">
<image
src="/pagesCreateOrder/static/images/icon-add-black.svg"
class="icon"
mode=""
>
</image>
</view>
</view>
</view>
<view class="u-m-t-10">
<my-button @tap="close" type="cancel" v-if="isDisabled">
<view class="color-999">已下架/售罄</view>
</my-button>
<my-button @tap="confirm" v-else>添加</my-button>
</view>
</view>
</template>
</my-model>
</template> </template>
<script setup> <script setup>
import { computed, reactive, ref, watch } from 'vue'; import { computed, reactive, ref, watch,inject } from "vue";
import util from '../util.js'; import util from "../util.js";
import infobox from '@/commons/utils/infoBox.js' import infobox from "@/commons/utils/infoBox.js";
import myModel from '@/components/my-components/my-model.vue' import myModel from "@/components/my-components/my-model.vue";
import myButton from '@/components/my-components/my-button.vue' import myButton from "@/components/my-components/my-button.vue";
const props = defineProps({ import BigNumber from "bignumber.js";
goodsData: { const yskUtils = inject("yskUtils");
type: Object, const shopInfo = uni.getStorageSync("shopInfo");
default: () => {} const shopUserInfo = uni.getStorageSync("shopUserInfo");
},
title: {
type: String,
default: ''
},
skuMap: {
type: Object,
default: () => {
return {}
}
},
skus: {
type: Array,
default: () => {
return []
}
},
})
const emits = defineEmits(['confirm', 'updateSku'])
const model = ref(null)
let number = ref(1)
function to2(number) {
return Number(number).toFixed(2)
}
const selSku = computed(() => { const props = defineProps({
return props.skus.reduce((prve, cur) => { goodsData: {
prve.push(cur.sel) type: Object,
return prve default: () => {},
}, []).join() },
}) title: {
type: String,
default: "",
},
skuMap: {
type: Object,
default: () => {
return {};
},
},
skus: {
type: Array,
default: () => {
return [];
},
},
limitTimeDiscount: {
type: Object,
default: () => {
return {};
},
},
});
const emits = defineEmits(["confirm", "updateSku"]);
const model = ref(null);
let number = ref(1);
function to2(number) {
return Number(number).toFixed(2);
}
const goods = computed(() => { const selSku = computed(() => {
return props.skuMap[selSku.value] return props.skus
}) .reduce((prve, cur) => {
watch(() => goods.value, (newval) => { prve.push(cur.sel);
number.value = newval.suitNum || 1 return prve;
}) }, [])
.join();
});
const isCanBuy = computed(() => { const goods = computed(() => {
if (!goods.value) { return props.skuMap[selSku.value];
return false });
} watch(
return util.isCanBuy( () => goods.value,
goods.value, (newval) => {
props.goodsData number.value = newval.suitNum || 1;
) }
);
}) const isCanBuy = computed(() => {
if (!goods.value) {
return false;
}
return util.isCanBuy(goods.value, props.goodsData);
});
/** /**
* 全部规格是否都无法使用 * 全部规格是否都无法使用
*/ */
const isAllDisabled = computed(() => { const isAllDisabled = computed(() => {
return props.skus.reduce((prve, cur) => { return props.skus.reduce((prve, cur) => {
return prve && cur.values.filter(v => v.disabled).length === cur.values.length return (
}, true) prve && cur.values.filter((v) => v.disabled).length === cur.values.length
}) );
}, true);
});
/** /**
* 规格选择 * 规格选择
* @param {Object} skusIndex * @param {Object} skusIndex
* @param {Object} skd * @param {Object} skd
*/ */
function chooseSkd(skusIndex, skd) { function chooseSkd(skusIndex, skd) {
const { name, disabled } = skd const { name, disabled } = skd;
if (disabled) { if (disabled) {
return return;
} }
if (props.skus[skusIndex].sel != name) { if (props.skus[skusIndex].sel != name) {
emits('updateSku', skusIndex, name) emits("updateSku", skusIndex, name);
} }
} }
/** /**
* 禁止操作 * 禁止操作
*/ */
const isDisabled = computed(() => { const isDisabled = computed(() => {
return isAllDisabled.value || !isCanBuy.value return isAllDisabled.value || !isCanBuy.value;
}) });
/** /**
* 数量减少 * 数量减少
*/ */
function reduce() { function reduce() {
if (isDisabled.value) { if (isDisabled.value) {
return return;
} }
const suitNum = goods.value.suitNum || 1 const suitNum = goods.value.suitNum || 1;
const newval = number.value - 1 const newval = number.value - 1;
if (newval < suitNum) { if (newval < suitNum) {
return infobox.showToast(suitNum + '个起售') return infobox.showToast(suitNum + "个起售");
} }
number.value = newval <= 1 ? 1 : newval number.value = newval <= 1 ? 1 : newval;
} }
/** /**
* 数量增加 * 数量增加
*/ */
function add() { function add() {
if (isDisabled.value) { if (isDisabled.value) {
return return;
} }
number.value = number.value + 1 number.value = number.value + 1;
} }
/** /**
* 都规格选择确认 * 都规格选择确认
*/ */
function confirm() { function confirm() {
close() close();
if (isDisabled.value) { if (isDisabled.value) {
return return;
} }
emits('confirm', goods.value, number.value) emits("confirm", goods.value, number.value,is_time_discount.value?1:0);
} }
function open() { function open() {
model.value.open() model.value.open();
} }
function close() { function close() {
model.value.close() model.value.close();
} }
defineExpose({
open, const is_time_discount = computed(() => {
close if (!props.limitTimeDiscount || !props.limitTimeDiscount.id) {
}) return false;
}
const isCanuse = yskUtils.limitUtils.canUseLimitTimeDiscount(
goods.value,
props.limitTimeDiscount,
shopInfo,
shopUserInfo,
"productId"
);
console.log('isCanuse');
console.log( goods.value);
return isCanuse;
});
function returnLimitPrice() {
const price = yskUtils.limitUtils.returnPrice({
goods: goods.value,
shopInfo: shopInfo,
limitTimeDiscountRes: props.limitTimeDiscount,
shopUserInfo: shopUserInfo,
idKey: "productId",
});
return BigNumber(price).times(number.value).toNumber();
}
defineExpose({
open,
close,
});
</script> </script>
<style lang="scss"> <style lang="scss">
.border-top {} .border-top {
}
.icon { .icon {
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
} }
.guigeModel { .guigeModel {
.item { .item {
color: #666; color: #666;
font-size: 24rpx; font-size: 24rpx;
padding: 4rpx 28rpx; padding: 4rpx 28rpx;
border: 1px solid #E5E5E5; border: 1px solid #e5e5e5;
border-radius: 8rpx; border-radius: 8rpx;
margin-right: 20rpx; margin-right: 20rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
transition: all .2s ease-in-out; transition: all 0.2s ease-in-out;
&.active { &.active {
border-color: $my-main-color; border-color: $my-main-color;
color: $my-main-color; color: $my-main-color;
} }
&.disabled { &.disabled {
color: #ccc; color: #ccc;
border-color: #eee; border-color: #eee;
} }
} }
} }
.price { .price {
color: #EB4F4F; color: #eb4f4f;
} }
.oldPrice {
.border-top { color: #999;
border-top: 1px solid #E5E5E5; text-decoration: line-through;
} }
.border-top {
border-top: 1px solid #e5e5e5;
}
</style> </style>

View File

@ -1,232 +1,345 @@
<template> <template>
<view class="u-relative u-flex item"> <view class="u-relative u-flex item">
<up-image
:src="data.coverImg"
mode="aspectFill"
:width="img.width"
:height="img.height"
></up-image>
<view class="info u-flex u-row-between u-col-top u-flex-col">
<view class="limit-discount" v-if="is_time_discount">限时折扣</view>
<up-image :src="data.coverImg" mode="aspectFill" :width="img.width" :height="img.height"></up-image> <view>
<view class="info u-flex u-row-between u-col-top u-flex-col" > <view>
<view > <text class="up-line-1">{{ data.name }}</text>
<view > </view>
<text class="up-line-1">{{data.name}}</text> <view>
</view> <text class="u-font-32 font-bold u-m-t-16" v-if="is_time_discount"> {{ limitPrice }} </text>
<view class="u-font-32 font-bold u-m-t-16"> <text
{{data.lowPrice}} class="u-font-32 font-bold u-m-t-16"
</view> :class="[is_time_discount ? 'line-through' : '']"
<template v-if="data.type=='weight'"> >
<view class="btnweigh">称重</view> {{ data.lowPrice }}
</template> </text>
</view> </view>
<view class="u-flex" v-if="!isSellout">
<template v-if="data.type == 'sku'||data.groupType==1">
<button class="btn" hover-class="btn-hover-class" @tap="emitEvent('chooseGuige')">选规格</button>
</template>
<template v-else>
<view class="u-flex icon-btn">
<view class="u-flex" @tap.stop="emitEvent(data.type=='weight'?'tapweigh':'add')">
<image src="/pagesCreateOrder/static/images/icon-add.svg" class="icon" mode=""></image>
</view>
<template v-if="data.chooseNumber">
<view class="u-font-32">
{{(data.chooseNumber).toFixed(2)}}
</view>
<view class="u-flex" @tap.stop="emitEvent('reduce')">
<image src="/pagesCreateOrder/static/images/icon-reduce.svg" class="icon" mode="">
</image>
</view>
</template>
</view>
</template>
<template v-if="data.type == 'weight'">
</view> <view class="btnweigh">称重</view>
<template v-if="isSellout"> </template>
<view class="isSellout" v-if="data.isSale == 0" > </view>
<image class="isSellout_icon" src="/pagesCreateOrder/static/images/no-toDown.svg" mode=""></image> <view class="u-flex" v-if="!isSellout">
</view> <template v-if="data.type == 'sku' || data.groupType == 1">
<view class="isSellout" v-else-if="!isProductAvailable(data.days,data.startTime,data.endTime)" > <button
<image class="isSellout_icon" src="/pagesCreateOrder/static/images/no-saleTime.svg" mode=""></image> class="btn"
</view> hover-class="btn-hover-class"
<view class="isSellout" v-else-if="data.isSoldStock == 1" > @tap="emitEvent('chooseGuige')"
<image class="isSellout_icon" src="/pagesCreateOrder/static/images/no-sold.svg" mode=""></image> >
</view> 选规格
<view class="isSellout" v-else-if="data.isStock == 1 && data.stockNumber <= 0" > </button>
<image class="isSellout_icon" src="/pagesCreateOrder/static/images/no-stock.svg" mode=""></image> </template>
</view> <template v-else>
</template> <view class="u-flex icon-btn">
</view> <view
</view> class="u-flex"
@tap.stop="emitEvent(data.type == 'weight' ? 'tapweigh' : 'add')"
>
<image
src="/pagesCreateOrder/static/images/icon-add.svg"
class="icon"
mode=""
></image>
</view>
<template v-if="data.chooseNumber">
<view class="u-font-32">
{{ data.chooseNumber.toFixed(2) }}
</view>
<view class="u-flex" @tap.stop="emitEvent('reduce')">
<image
src="/pagesCreateOrder/static/images/icon-reduce.svg"
class="icon"
mode=""
>
</image>
</view>
</template>
</view>
</template>
</view>
<template v-if="isSellout">
<view class="isSellout" v-if="data.isSale == 0">
<image
class="isSellout_icon"
src="/pagesCreateOrder/static/images/no-toDown.svg"
mode=""
></image>
</view>
<view
class="isSellout"
v-else-if="
!isProductAvailable(data.days, data.startTime, data.endTime)
"
>
<image
class="isSellout_icon"
src="/pagesCreateOrder/static/images/no-saleTime.svg"
mode=""
></image>
</view>
<view class="isSellout" v-else-if="data.isSoldStock == 1">
<image
class="isSellout_icon"
src="/pagesCreateOrder/static/images/no-sold.svg"
mode=""
></image>
</view>
<view
class="isSellout"
v-else-if="data.isStock == 1 && data.stockNumber <= 0"
>
<image
class="isSellout_icon"
src="/pagesCreateOrder/static/images/no-stock.svg"
mode=""
></image>
</view>
</template>
</view>
</view>
</template> </template>
<script setup> <script setup>
import { computed, toRef } from 'vue'; import { computed, toRef, inject } from "vue";
import util from '../util.js'; import util from "../util.js";
import dayjs from "dayjs"; import dayjs from "dayjs";
import isBetween from "dayjs/plugin/isBetween.js"; import isBetween from "dayjs/plugin/isBetween.js";
dayjs.extend(isBetween) const yskUtils = inject("yskUtils");
const shopInfo = inject("shopInfo");
const props = defineProps({ dayjs.extend(isBetween);
img: {
type: Object,
default: {
width: '250rpx',
height: '272rpx'
}
},
index: {
type: [Number, String],
},
isSeatFee: {
//
type: Boolean,
default: false
},
data: {
type: Object,
default: () => {
return {
chooseNumber: 0
}
}
}
})
/** const props = defineProps({
* 判断是否是菜品 img: {
*/ type: Object,
function isGoods() { default: {
return props.data.hasOwnProperty('id') width: "250rpx",
} height: "272rpx",
},
},
index: {
type: [Number, String],
},
isSeatFee: {
//
type: Boolean,
default: false,
},
data: {
type: Object,
default: () => {
return {
chooseNumber: 0,
};
},
},
limitTimeDiscount: {
type: Object,
default: () => {
return {};
},
},
});
/** //
* 判断商品是否售尽 const is_time_discount = computed(() => {
*/ if (!props.limitTimeDiscount || !props.limitTimeDiscount.id) {
const isSellout = computed(() => { return false;
const item = props.data }
if (!isGoods()) {
return false
}
return (
(item.isStock == 1 && item.stockNumber <= 0) || item.isSoldStock == 1 || item.isSale == 0 || !isProductAvailable(item.days,item.startTime,item.endTime)
);
})
//
function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) {
//
const sellDays = sellDaysStr.split(',');
    const now = dayjs();
    const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const currentDay = days[now.day()];
    const currentTime = now.format('HH:mm:ss');
// console.log(':', currentDay);
// console.log(':', sellDays);
    //
    if (!sellDays.includes(currentDay)) {
        // console.log('');
        return false;
    }
    const startTime = dayjs(`${now.format('YYYY-MM-DD')} ${startTimeStr}`);
    let endTime = dayjs(`${now.format('YYYY-MM-DD')} ${endTimeStr}`);
//
    if (endTime.isBefore(startTime)) {
        endTime = endTime.add(1, 'day');
    }
// console.log(':', now.format('YYYY-MM-DD HH:mm:ss'));
    // console.log(':', startTime.format('YYYY-MM-DD HH:mm:ss'));
    // console.log(':', endTime.format('YYYY-MM-DD HH:mm:ss'));
    const isInRange = now.isBetween(startTime, endTime, null, '[)'); const isCanuse = yskUtils.limitUtils.canUseLimitTimeDiscount(
    // console.log(':', isInRange); props.data,
props.limitTimeDiscount,
shopInfo,
null,
"id"
);
return isCanuse;
});
    return isInRange; const limitPrice = computed(() => {
if (!is_time_discount.value) {
return 0;
}
console.log("props.data", props.data);
const price = yskUtils.limitUtils.returnPrice({
goods: props.data,
shopInfo: shopInfo,
limitTimeDiscountRes: props.limitTimeDiscount,
shopUserInfo: null,
idKey: "id",
});
return price;
});
} /**
* 判断是否是菜品
*/
function isGoods() {
return props.data.hasOwnProperty("id");
}
const emits = defineEmits(['add', 'reduce', 'chooseGuige','tapweigh']) /**
* 判断商品是否售尽
*/
const isSellout = computed(() => {
const item = props.data;
if (!isGoods()) {
return false;
}
return (
(item.isStock == 1 && item.stockNumber <= 0) ||
item.isSoldStock == 1 ||
item.isSale == 0 ||
!isProductAvailable(item.days, item.startTime, item.endTime)
);
});
//
function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) {
//
const sellDays = sellDaysStr.split(",");
const now = dayjs();
const days = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
const currentDay = days[now.day()];
const currentTime = now.format("HH:mm:ss"); //
function emitEvent(emitName) { // console.log(':', currentDay);
if (isGoods()) { // console.log(':', sellDays);
emits(emitName, props.index) if (!sellDays.includes(currentDay)) {
} // console.log('');
} return false;
}
const startTime = dayjs(`${now.format("YYYY-MM-DD")} ${startTimeStr}`);
let endTime = dayjs(`${now.format("YYYY-MM-DD")} ${endTimeStr}`);
//
if (endTime.isBefore(startTime)) {
endTime = endTime.add(1, "day");
} // console.log('开始时间:', startTime.format('YYYY-MM-DD HH:mm:ss')); // console.log('结束时间:', endTime.format('YYYY-MM-DD HH:mm:ss'));
// console.log(':', now.format('YYYY-MM-DD HH:mm:ss'));
const isInRange = now.isBetween(startTime, endTime, null, "[)"); // console.log(':', isInRange);
return isInRange;
}
const emits = defineEmits(["add", "reduce", "chooseGuige", "tapweigh"]);
function emitEvent(emitName) {
if (isGoods()) {
emits(emitName, props.index);
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.icon { .icon {
width: 48rpx; width: 48rpx;
height: 48rpx; height: 48rpx;
} }
.icon-btn { .icon-btn {
gap: 14rpx; gap: 14rpx;
} }
.btn { .btn {
background: #EB4F4F; background: #eb4f4f;
border-radius: 100rpx; border-radius: 100rpx;
font-size: 28rpx; font-size: 28rpx;
height: 56rpx; height: 56rpx;
line-height: 56rpx; line-height: 56rpx;
color: #fff; color: #fff;
} }
.btnweigh { .btnweigh {
margin: 5rpx 0; margin: 5rpx 0;
width: 100rpx; width: 100rpx;
background: linear-gradient(124deg, #73c969 6%, #27921b 93%); background: linear-gradient(124deg, #73c969 6%, #27921b 93%);
border-radius: 10rpx; border-radius: 10rpx;
font-size: 24rpx; font-size: 24rpx;
padding: 6rpx 0; padding: 6rpx 0;
text-align: center; text-align: center;
} }
.btn-hover-class { .btn-hover-class {
opacity: .6; opacity: 0.6;
} }
image { image {
will-change: transform will-change: transform;
} }
.item { .item {
// width: 250rpx; // width: 250rpx;
// height: 272rpx; // height: 272rpx;
background: #F9B798; background: #f9b798;
border-radius: 8rpx 8rpx 8rpx 8rpx; border-radius: 8rpx 8rpx 8rpx 8rpx;
overflow: hidden; overflow: hidden;
.img { .img {
width: 250rpx; width: 250rpx;
height: 272rpx; height: 272rpx;
} }
.info { .info {
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
color: #fff; color: #fff;
padding: 32rpx 24rpx 24rpx 24rpx; padding: 52rpx 24rpx 24rpx 24rpx;
top: 0; top: 0;
background: rgba(37, 22, 15, 0.5); background: rgba(37, 22, 15, 0.5);
border-radius: 8rpx 8rpx 8rpx 8rpx; border-radius: 8rpx 8rpx 8rpx 8rpx;
overflow: hidden; overflow: hidden;
} }
} }
.isSellout{ .isSellout {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
background-color: rgba(0, 0, 0, 0.6); background-color: rgba(0, 0, 0, 0.6);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
.isSellout_icon{ .isSellout_icon {
height: 60%; height: 60%;
width: 60%; width: 60%;
} }
} }
.limit-discount {
background-color: #cc5617;
padding: 10rpx 20rpx;
white-space: nowrap;
text-align: center;
position: absolute;
top: 0;
left: 0;
font-weight: 400;
font-size: 24rpx;
color: #ffffff;
border-radius: 0 0rpx 20rpx 0rpx;
color: #fff;
}
.line-through{
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
text-decoration: line-through;
}
</style> </style>

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,8 @@ export function getNowCart(carItem,goodsList,user) {
// console.log("carItem===",carItem) // console.log("carItem===",carItem)
// console.log("goodsList===",goodsList) // console.log("goodsList===",goodsList)
// const nowCart = records.find(v => v.placeNum == 0) // const nowCart = records.find(v => v.placeNum == 0)
console.log("carItem===",carItem)
const arr = [] const arr = []
if( carItem.is_temporary != 1 ){ if( carItem.is_temporary != 1 ){
carItem.isGrounding = false; carItem.isGrounding = false;
@ -13,8 +15,9 @@ export function getNowCart(carItem,goodsList,user) {
if(carItem.sku_id == item.id){ if(carItem.sku_id == item.id){
carItem.lowPrice = item.salePrice carItem.lowPrice = item.salePrice
carItem.lowMemberPrice = item.memberPrice carItem.lowMemberPrice = item.memberPrice
carItem.memberPrice = item.memberPrice
carItem.specInfo = item.specInfo carItem.specInfo = item.specInfo
carItem.suitNum = item.suitNum carItem.salePrice = item.salePrice
if( uni.getStorageSync('shopInfo').isMemberPrice && user && user.id && user.isVip ){ if( uni.getStorageSync('shopInfo').isMemberPrice && user && user.id && user.isVip ){
carItem.salePrice = item.memberPrice carItem.salePrice = item.memberPrice
@ -38,8 +41,10 @@ export function getNowCart(carItem,goodsList,user) {
carItem.number = parseFloat(carItem.number) carItem.number = parseFloat(carItem.number)
carItem.name = carItem.product_name carItem.name = carItem.product_name
carItem.lowPrice = carItem.discount_sale_amount carItem.lowPrice = carItem.discount_sale_amount
carItem.discount_sale_amount = 0 carItem.discount_sale_amount = carItem.discount_sale_amount?carItem.discount_sale_amount*1:0
carItem.discountSaleAmount = carItem.discount_sale_amount
// carItem.discount_sale_amount = 0
} }
return carItem return carItem
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1313
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff