1610 lines
54 KiB
Vue
1610 lines
54 KiB
Vue
<script setup lang="ts">
|
||
import { useConfigStore, useUserStore } from "@/store";
|
||
import { dayjs } from "@/plugin/index";
|
||
import Config from '@/config/index'
|
||
import CheckoutSkeleton from "./components/checkout-skeleton.vue";
|
||
import ChangePhone from "./components/change-phone.vue";
|
||
import PriceDetail from "./components/price-detail.vue";
|
||
import VisitMethod from "@/components/visit-method/index.vue";
|
||
import {ref} from "vue";
|
||
import {
|
||
appMerchantCartListByMerchantIdPost,
|
||
appMerchantCartCalculateSavingsPost,
|
||
appMerchantDetailMerchantIdGet,
|
||
appUserAddressListPost,
|
||
type MerchantCartVo,
|
||
type MerchantVo,
|
||
appMerchantOrderCalculatePriceCartPost,
|
||
appUserCardSelectDefaultPost, appMerchantOrderCreateOrderCartPost, appMerchantOrderPayOrderPost,
|
||
appMerchantOrderCreateOrderCartBatchPost,
|
||
appMerchantOrderCalculatePriceCartBatchPost,
|
||
appMerchantCartListMerchantPost,
|
||
appMerchantOrderPayOrderBatchPost
|
||
} from "@/service";
|
||
import useEventEmit from "@/hooks/useEventEmit";
|
||
import {EventEnum} from "@/constant/enums";
|
||
import { getDistanceInMiles} from "@/utils/utils";
|
||
const { t } = useI18n();
|
||
const configStore = useConfigStore();
|
||
const userStore = useUserStore();
|
||
|
||
// 送达偏好
|
||
const visitMethodRef = ref();
|
||
const visitMethod = ref({
|
||
value: 2,
|
||
label: t("components.visit.putItAtTheDoor"),
|
||
});
|
||
function chooseVisitMethod() {
|
||
visitMethodRef.value?.onOpen(visitMethod.value.value);
|
||
}
|
||
function handleVisitMethodConfirm(data: { value: number; label: string }) {
|
||
visitMethod.value = data;
|
||
}
|
||
|
||
// 联系方式(这里默认值取用户信息的手机号区号)
|
||
const contact = ref({
|
||
phone: userStore.userInfo.phone,
|
||
areaCode: "+1",
|
||
});
|
||
const changePhoneRef = ref<InstanceType<typeof ChangePhone>>();
|
||
function openChangePhone() {
|
||
if (changePhoneRef.value) {
|
||
changePhoneRef.value.onOpen(contact.value);
|
||
}
|
||
}
|
||
function confirmPhone(data: { phone: string; areaCode: string }) {
|
||
contact.value = data;
|
||
formData.value.phone = data.phone;
|
||
}
|
||
|
||
// 配送时间 1 即刻配送 2 预约配送
|
||
const deliveryTimeType = ref(1);
|
||
// 配送时间配置
|
||
const deliveryMinutes = ref(0); // 预计送达时间(分钟)
|
||
// 自取时间配置
|
||
const selfPickupMinutes = ref(0); // 自取时间(分钟)
|
||
const deliveryWindowMinutes = ref(30); // 配送时间窗口(分钟)
|
||
// 预约配送日期配送
|
||
const userSelectedDeliveryTimeDate = ref(null);
|
||
// 预约自取时间
|
||
const userSelectedSelfPickupTimeDate = ref(null);
|
||
// 显示用的配送时间(即刻配送=当前时间加预计时间)(预约配送=预约时间+预计时间分钟)
|
||
const showDeliveryTime = computed(() => {
|
||
// 配送
|
||
if (deliveryTimeType.value === 1) {
|
||
// 即刻配送:当前时间 + 预计送达时间
|
||
let time = 0
|
||
if(deliveryMethod.value === 0) {
|
||
time = deliveryMinutes.value
|
||
} else {
|
||
time = selfPickupMinutes.value
|
||
}
|
||
const deliveryStart = dayjs().add(Number(time), "minute");
|
||
const deliveryEnd = deliveryStart.add(
|
||
deliveryWindowMinutes.value,
|
||
"minute"
|
||
);
|
||
|
||
return `${deliveryStart.format("HH:mm")}-${deliveryEnd.format("HH:mm")}`;
|
||
}
|
||
if (deliveryTimeType.value === 2) {
|
||
return deliveryMethod.value === 0 ? userSelectedDeliveryTimeDate : '123';
|
||
}
|
||
return "";
|
||
});
|
||
// 切换配送时间点击事件
|
||
function toggleDeliveryTimeType(type: number) {
|
||
if (type === 2) {
|
||
if (userSelectedDeliveryTimeDate.value) {
|
||
deliveryTimeType.value = type;
|
||
}
|
||
// 跳转到时间选择页面
|
||
console.log(storeDetail.value.merch);
|
||
|
||
uni.navigateTo({
|
||
url: "/pages/address/reservation-time?storeBusinessHours=" + storeDetail.value.businessHours,
|
||
});
|
||
} else {
|
||
deliveryTimeType.value = type;
|
||
}
|
||
}
|
||
|
||
const diyTime = ref({})
|
||
useEventEmit(EventEnum.CHOOSE_APPOINTMENT_TIME, (data) => {
|
||
console.log('CHOOSE_APPOINTMENT_TIME', data)
|
||
if(data) {
|
||
diyTime.value = data;
|
||
deliveryTimeType.value = 2
|
||
|
||
// 配送还是自取
|
||
if(deliveryMethod.value === 0) {
|
||
userSelectedDeliveryTimeDate.value = dayjs(data.date).format('MM-DD') + ' ' + data.timeSlot;
|
||
} else {
|
||
userSelectedSelfPickupTimeDate.value = dayjs(data.date).format('MM-DD') + ' ' + data.timeSlot;
|
||
}
|
||
}
|
||
})
|
||
|
||
// 设置配送时间(分钟)
|
||
const setDeliveryMinutes = (minutes: number) => {
|
||
deliveryMinutes.value = minutes;
|
||
};
|
||
|
||
// 设置配送时间窗口(分钟)
|
||
const setDeliveryWindowMinutes = (minutes: number) => {
|
||
deliveryWindowMinutes.value = minutes;
|
||
};
|
||
|
||
const deliveryMethod = ref(0);
|
||
// 配送方式
|
||
const showDeliverySwitch = ref(false)
|
||
const deliveryMethodOptions = [
|
||
t("pages-store.store.delivery"),
|
||
t("pages-store.store.pickup"),
|
||
];
|
||
function handleClickSegmented(index: number) {
|
||
console.log("切换配送方式:", index);
|
||
if(+storeDetail.value.deliveryService !== 1 && index === 0) {
|
||
uni.showToast({
|
||
title: t('pages-store.store.toast.deliveryService'),
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
if(+storeDetail.value.selfPickup !== 1 && index === 1) {
|
||
uni.showToast({
|
||
title: t('pages-store.store.toast.selfPickup'),
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
if(index !== deliveryMethod.value) {
|
||
deliveryMethod.value = index
|
||
// 重置配送类型
|
||
deliveryTimeType.value = 1
|
||
appMerchantOrderCalculatePriceCart()
|
||
}
|
||
}
|
||
|
||
// 折叠面板
|
||
const collapseValue = ref([""]);
|
||
|
||
// 当前选中的小费
|
||
const selectedTipIndex = ref(5);
|
||
const diyTipValue = ref('')
|
||
const tipOptions = ref([
|
||
{
|
||
label: "5%",
|
||
value: 5,
|
||
},
|
||
{
|
||
label: "10%",
|
||
value: 10,
|
||
},
|
||
{
|
||
label: "15%",
|
||
value: 15,
|
||
},
|
||
]);
|
||
function selectedTipChange(item: any) {
|
||
if(item.value === selectedTipIndex.value) return
|
||
diyTipValue.value = ''
|
||
selectedTipIndex.value = item.value
|
||
appMerchantOrderCalculatePriceCart()
|
||
}
|
||
function handleConfirmTip() {
|
||
if(diyTipValue.value) {
|
||
appMerchantOrderCalculatePriceCart()
|
||
}
|
||
}
|
||
|
||
// 是否选择了配送周卡
|
||
const isWeeklyDelivery = ref(false);
|
||
// 切换配送周卡
|
||
const toggleWeeklyDelivery = (value: boolean) => {
|
||
if(value === isWeeklyDelivery.value) return
|
||
isWeeklyDelivery.value = value;
|
||
appMerchantOrderCalculatePriceCart()
|
||
};
|
||
|
||
// 价格明细
|
||
const priceDetailRef = ref<InstanceType<typeof PriceDetail>>();
|
||
// 打开价格明细
|
||
const openPriceDetail = () => {
|
||
priceDetailRef.value?.onOpen(priceData.value, cartSavingsData.value?.savings || 0);
|
||
};
|
||
|
||
function navigateTo(url: string) {
|
||
uni.navigateTo({ url })
|
||
}
|
||
|
||
// 支付参数
|
||
const payMethodOptions = ref({
|
||
orderId: '',
|
||
cardId: '',
|
||
payMethod: 1, // 支付方式 1信用卡 2余额
|
||
payPassword: '',
|
||
})
|
||
useEventEmit(EventEnum.CHOOSE_PAYMENT_METHOD, (data) => {
|
||
if(data) {
|
||
if(data.payMethod === 1) {
|
||
payMethodOptions.value.cardId = data.cardId
|
||
payMethodOptions.value.cardNumber = data.cardNumber
|
||
payMethodOptions.value.payMethod = 1
|
||
} else {
|
||
payMethodOptions.value.payMethod = 2
|
||
}
|
||
}
|
||
})
|
||
|
||
|
||
const storeId = ref('')
|
||
const orderType = ref('') // 订单类型:batch-批量下单,normal-普通下单
|
||
const batchCartIds = ref([]) // 批量下单的购物车ID列表
|
||
const formData = ref<CreateOrderCartBo>({
|
||
orderRemark: '',
|
||
phone: '',
|
||
receiveMethod: 0,
|
||
startScheduledTime: 0,
|
||
endScheduledTime: 0,
|
||
tipDiscount: 0,
|
||
weeklyDeliveryFee: 0,
|
||
})
|
||
// 是否需要餐具
|
||
const needTableware = ref(false)
|
||
onLoad((options: any)=> {
|
||
loading.value = true
|
||
|
||
// 判断是批量下单还是普通下单
|
||
if(options.type == 'batch' && options.cartIds) {
|
||
// 批量下单模式
|
||
orderType.value = 'batch'
|
||
batchCartIds.value = options.cartIds.split(',')
|
||
console.log("下单类型",options.type);
|
||
|
||
// 默认取用户信息中的手机号作为收货手机号
|
||
formData.value.phone = userStore.userInfo.phone || ''
|
||
contact.value.areaCode = userStore.userInfo.areaCode || ''
|
||
// 获取用户地址列表
|
||
getAddressList()
|
||
// 查询批量购物车详情
|
||
getBatchCartInfo()
|
||
// 查询用户默认信用卡
|
||
appUserCardSelectDefault()
|
||
} else if(options.storeId) {
|
||
// 普通下单模式
|
||
orderType.value = 'normal'
|
||
storeId.value = options.storeId as string
|
||
formData.value.orderRemark = options.orderRemark as string
|
||
|
||
needTableware.value = options.needTableware === 'true'
|
||
|
||
// 默认取用户信息中的手机号作为收货手机号
|
||
formData.value.phone = userStore.userInfo.phone || ''
|
||
contact.value.areaCode = userStore.userInfo.areaCode || ''
|
||
// 获取用户地址列表
|
||
getAddressList()
|
||
// 查询当前店铺购物车详情
|
||
getCartInfo()
|
||
// 获取商家详情信息
|
||
getStoreDetail()
|
||
// 查询用户默认信用卡
|
||
appUserCardSelectDefault()
|
||
}
|
||
})
|
||
|
||
onShow(()=> {
|
||
// 刷新购物车列表
|
||
getAddressList()
|
||
})
|
||
|
||
// 页面加载状态
|
||
const loading = ref(true);
|
||
onMounted(() => {
|
||
userStore.getUserInfo()
|
||
const currentHour = dayjs().hour();
|
||
if (currentHour >= 11 && currentHour <= 14) {
|
||
setDeliveryMinutes(60);
|
||
} else if (currentHour >= 17 && currentHour <= 20) {
|
||
// 晚餐高峰期,配送时间延长
|
||
setDeliveryMinutes(50);
|
||
} else {
|
||
// 非高峰期,正常配送时间
|
||
setDeliveryMinutes(40);
|
||
}
|
||
});
|
||
|
||
const cartDataList = ref<MerchantCartVo[]>([])
|
||
function getCartInfo() {
|
||
appMerchantCartListByMerchantIdPost({
|
||
params: {
|
||
merchantId: storeId.value,
|
||
}
|
||
}).then((res: any)=> {
|
||
console.log('购物车列表', res)
|
||
cartDataList.value = res.data
|
||
|
||
// 购物车有菜品,查询菜品会员折扣价
|
||
if(cartDataList.value.length > 0) {
|
||
appMerchantCartCalculateSavings()
|
||
// 查询购物车下单价格
|
||
appMerchantOrderCalculatePriceCart()
|
||
}
|
||
}).finally(()=> {
|
||
loading.value = false
|
||
})
|
||
}
|
||
|
||
// 批量下单:查询购物车详情
|
||
async function getBatchCartInfo() {
|
||
try {
|
||
// 根据购物车ID列表,按店铺分组查询
|
||
const cartIds = batchCartIds.value
|
||
console.log('批量购物车ID列表', cartIds)
|
||
|
||
// 1. 先获取所有店铺列表
|
||
const merchantRes = await appMerchantCartListMerchantPost({});
|
||
console.log('批量模式-购物车店铺列表', merchantRes);
|
||
|
||
if (!merchantRes.data || merchantRes.data.length === 0) {
|
||
cartDataList.value = [];
|
||
return;
|
||
}
|
||
|
||
// 2. 对每个店铺查询完整的商品信息
|
||
const merchantPromises = merchantRes.data.map(async (merchant: any) => {
|
||
try {
|
||
const cartRes = await appMerchantCartListByMerchantIdPost({
|
||
params: {
|
||
merchantId: merchant.id
|
||
}
|
||
});
|
||
console.log(`批量模式-店铺 ${merchant.merchantName} 的商品列表`, cartRes);
|
||
|
||
// 只保留选中的商品(根据 cartIds 过滤)
|
||
const filteredItems = (cartRes.data || []).filter((item: any) =>
|
||
cartIds.includes(String(item.id))
|
||
);
|
||
|
||
// 如果该店铺有选中的商品,才返回
|
||
if (filteredItems.length > 0) {
|
||
return {
|
||
...merchant,
|
||
merchantCartVoList: filteredItems
|
||
};
|
||
}
|
||
return null;
|
||
} catch (error) {
|
||
console.error(`获取店铺 ${merchant.id} 商品失败`, error);
|
||
return null;
|
||
}
|
||
});
|
||
|
||
// 3. 等待所有店铺的商品信息加载完成,并过滤掉 null
|
||
const results = await Promise.all(merchantPromises);
|
||
cartDataList.value = results.filter(item => item !== null);
|
||
console.log('批量模式-最终购物车数据', cartDataList.value);
|
||
|
||
// 查询菜品会员折扣价
|
||
if(cartIds.length > 0) {
|
||
appMerchantCartCalculateSavings()
|
||
// 注意:价格计算会在地址加载完成后自动调用
|
||
// 这里不需要立即调用 appMerchantOrderCalculatePriceCart()
|
||
}
|
||
} catch (error) {
|
||
console.error('获取批量购物车详情失败', error)
|
||
cartDataList.value = [];
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 查询菜品会员折扣价
|
||
const cartSavingsData = ref({})
|
||
function appMerchantCartCalculateSavings() {
|
||
const cartIds = orderType.value == 'batch' ? batchCartIds.value : cartDataList.value.map(item => item.id)
|
||
appMerchantCartCalculateSavingsPost({
|
||
body: cartIds
|
||
}).then(res=> {
|
||
console.log('菜品会员折扣价', res)
|
||
cartSavingsData.value = res.data
|
||
})
|
||
}
|
||
|
||
// 获取商家详情信息
|
||
// 商家是否支持配送
|
||
const storeIsDeliveryService = computed(()=> {
|
||
if(!storeDetail.value) return false
|
||
return +storeDetail.value.deliveryService === 1
|
||
})
|
||
// 商家是否支持自提
|
||
const storeIsSelfPickup = computed(()=> {
|
||
if(!storeDetail.value) return false
|
||
return +storeDetail.value.selfPickup === 1
|
||
})
|
||
const storeDetail = ref<MerchantVo>({})
|
||
// 用户距离商家的距离信息
|
||
const storeDistance = ref(null)
|
||
function getStoreDetail() {
|
||
appMerchantDetailMerchantIdGet({
|
||
params: {
|
||
merchantId: storeId.value,
|
||
}
|
||
}).then((res: any) => {
|
||
console.log('商家详情', res)
|
||
storeDetail.value = res.data as MerchantVo
|
||
|
||
// 配送时间(是否支持配送)
|
||
if(+storeDetail.value.deliveryService === 1) {
|
||
// 配送时间
|
||
deliveryMinutes.value = res.data.deliveryTime || 0
|
||
}
|
||
if(+storeDetail.value.selfPickup === 1) {
|
||
// 自提时间
|
||
selfPickupMinutes.value = res.data.pickupTime
|
||
}
|
||
// 商户的经纬度存在,并且用户的经纬度也存在
|
||
if(res.data.latitude && res.data.longitude && userStore.userLocation.latitude && userStore.userLocation.longitude) {
|
||
let distance = getDistanceInMiles(res.data.latitude, res.data.longitude, userStore.userLocation.latitude, userStore.userLocation.longitude)
|
||
console.log('距离商家距离', distance)
|
||
storeDistance.value = distance
|
||
}
|
||
|
||
// 判断配送和自取的开通状态
|
||
const hasDelivery = +storeDetail.value.deliveryService === 1
|
||
const hasPickup = +storeDetail.value.selfPickup === 1
|
||
|
||
if (!hasDelivery && !hasPickup) {
|
||
// 两个都没开通,不显示切换组件
|
||
showDeliverySwitch.value = false
|
||
} else if (!hasDelivery && hasPickup) {
|
||
// 只开通自取,默认选中自取
|
||
deliveryMethod.value = 1
|
||
showDeliverySwitch.value = true
|
||
} else if (hasDelivery && !hasPickup) {
|
||
// 只开通配送,默认选中配送
|
||
deliveryMethod.value = 0
|
||
showDeliverySwitch.value = true
|
||
} else {
|
||
// 两个都开通,默认选中配送
|
||
deliveryMethod.value = 0
|
||
showDeliverySwitch.value = true
|
||
}
|
||
}).catch((error) => {
|
||
console.error('获取商家详情失败:', error);
|
||
})
|
||
}
|
||
|
||
const priceData = ref({})
|
||
function appMerchantOrderCalculatePriceCart() {
|
||
// 优先使用新选择的地址id
|
||
const targetAddressId = selectedAddressId.value || currentAddressId.value
|
||
// if(!targetAddressId) return
|
||
|
||
const cartIds = orderType.value == 'batch' ? batchCartIds.value : cartDataList.value.map(item => item.id)
|
||
|
||
// 批量模式使用批量计算价格接口
|
||
if(orderType.value == 'batch') {
|
||
appMerchantOrderCalculatePriceCartBatchPost({
|
||
body: {
|
||
addressId: targetAddressId,
|
||
cartIds: cartIds,
|
||
receiveMethod: deliveryMethod.value === 0 ? 1 : 2,
|
||
couponId: couponInfo.value ? couponInfo.value.id : '',
|
||
tipDiscount: deliveryMethod.value === 1 ? 0 : (diyTipValue.value ? Number(diyTipValue.value) / 100 : (selectedTipIndex.value || 0) / 100), // 小费比例,自取订单不需要小费
|
||
phone: formData.value.phone,
|
||
areaCode: contact.value.areaCode,
|
||
}
|
||
}).then(res=> {
|
||
console.log('批量购物车下单价格', res)
|
||
priceData.value = res.data
|
||
})
|
||
} else {
|
||
// 普通模式使用单店铺计算价格接口
|
||
appMerchantOrderCalculatePriceCartPost({
|
||
body: {
|
||
addressId: targetAddressId,
|
||
cartIds: cartIds,
|
||
receiveMethod: deliveryMethod.value === 0 ? 1 : 2,
|
||
couponId: couponInfo.value ? couponInfo.value.id : '',
|
||
tipDiscount: deliveryMethod.value === 1 ? 0 : (diyTipValue.value ? Number(diyTipValue.value) / 100 : (selectedTipIndex.value || 0) / 100), // 小费比例,自取订单不需要小费
|
||
// weeklyDeliveryFee: isWeeklyDelivery.value ? 1 : 2, // 是否支付周配送费(1-是 2-否)
|
||
phone: formData.value.phone,
|
||
areaCode: contact.value.areaCode,
|
||
}
|
||
}).then(res=> {
|
||
console.log('购物车下单价格', res)
|
||
priceData.value = res.data
|
||
})
|
||
}
|
||
}
|
||
|
||
// 获取用户地址列表
|
||
const addressesList = ref([])
|
||
// 当前选中的地址id
|
||
const currentAddressId = ref('')
|
||
// 新选择的地址id(优先使用)
|
||
const selectedAddressId = ref('')
|
||
// 当前选中的地址信息
|
||
const addressInfo = computed(()=> {
|
||
if(addressesList.value.length === 0) return {}
|
||
// 优先使用新选择的地址id,如果没有则使用默认的地址id
|
||
const targetId = selectedAddressId.value || currentAddressId.value
|
||
return addressesList.value.find(item => String(item.id) === String(targetId)) || {};
|
||
});
|
||
|
||
function getAddressList() {
|
||
appUserAddressListPost({
|
||
params: {
|
||
pageNum: 1,
|
||
pageSize: 100,
|
||
}
|
||
}).then(res => {
|
||
console.log('获取用户地址列表', res)
|
||
addressesList.value = res.rows
|
||
if(addressesList.value.length > 0) {
|
||
currentAddressId.value = addressesList.value[0].id
|
||
|
||
let data = addressesList.value[0]
|
||
|
||
// 回显用户地址中的送达偏好
|
||
// 回显送达偏好
|
||
if(+data.deliveryRemark === 1) {
|
||
visitMethod.value.label = t('components.visit.leaveItToMePersonally')
|
||
visitMethod.value.value = 1
|
||
}
|
||
if(+data.deliveryRemark === 2) {
|
||
visitMethod.value.label = t('components.visit.putItAtTheDoor')
|
||
visitMethod.value.value = 2
|
||
}
|
||
|
||
// 如果是批量模式,地址加载完成后重新计算价格
|
||
if(orderType.value === 'batch') {
|
||
appMerchantOrderCalculatePriceCart()
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
function chooseAddress() {
|
||
// 优先使用新选择的地址id
|
||
const targetAddressId = selectedAddressId.value || currentAddressId.value
|
||
uni.navigateTo({
|
||
url: '/pages-store/pages/order/choose-address?id=' + targetAddressId,
|
||
events: {
|
||
// 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
|
||
acceptDataFromOpenedPage: function(data) {
|
||
console.log('获取被打开页面传送到当前页面的数据', data)
|
||
// 将新选择的地址id存储到selectedAddressId中
|
||
selectedAddressId.value = data.data
|
||
|
||
// 回显送达偏好
|
||
if(+data.deliveryType === 1) {
|
||
visitMethod.value.label = t('components.visit.leaveItToMePersonally')
|
||
}
|
||
if(+data.deliveryType === 2) {
|
||
visitMethod.value.label = t('components.visit.putItAtTheDoor')
|
||
}
|
||
|
||
// 重新计算价格
|
||
appMerchantOrderCalculatePriceCart()
|
||
},
|
||
},
|
||
})
|
||
}
|
||
|
||
function appUserCardSelectDefault() {
|
||
appUserCardSelectDefaultPost({}).then(res=> {
|
||
console.log('查询用户默认信用卡', res)
|
||
payMethodOptions.value.cardId = res.data?.cardId || ''
|
||
payMethodOptions.value.cardNumber = res.data?.cardNumber || ''
|
||
})
|
||
}
|
||
|
||
// 是否显示周配送费选项
|
||
const isShowWeeklyDelivery = computed(()=> {
|
||
let time = userStore?.userInfo?.weeklyDeliveryExpire;
|
||
// 如果值为null,返回true
|
||
if (time === null) return true;
|
||
// 如果值不存在(如undefined),也返回true(根据你的需求可调整)
|
||
if (!time) return true;
|
||
// 检测是否到期:已过期返回true,未过期返回false
|
||
return dayjs().isAfter(dayjs(Number(time)));
|
||
})
|
||
|
||
const passwordInputRef = ref(null)
|
||
const resOrderId = ref('')
|
||
const resOrderIds = ref([]) // 批量下单返回的订单ID列表
|
||
function handleGoSettle() {
|
||
// 优先使用新选择的地址id
|
||
const targetAddressId = selectedAddressId.value || currentAddressId.value
|
||
if(!targetAddressId) {
|
||
uni.showToast({
|
||
title: t('pages-store.checkout.pleaseSelectAddress'),
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
if(payMethodOptions.value.payMethod === 1 && !payMethodOptions.value.cardId) {
|
||
uni.showToast({
|
||
title: t('pages-store.checkout.pleaseSelectCreditCard'),
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 批量下单
|
||
if(orderType.value == 'batch') {
|
||
if(resOrderIds.value.length === 0) {
|
||
let data = {
|
||
addressId: targetAddressId,
|
||
phone: formData.value.phone,
|
||
areaCode: contact.value.areaCode,
|
||
cartIds: batchCartIds.value,
|
||
couponId: couponInfo.value ? couponInfo.value.id : '',
|
||
deliveryMethod: visitMethod.value.label, // 派送方式(如放门口或者交到顾客手中)
|
||
deliveryType: deliveryTimeType.value, // 1-立即交付 2-预约交付
|
||
orderRemark: formData.value.orderRemark,
|
||
receiveMethod: deliveryMethod.value === 0 ? 1 : 2, // 收货方式(1-派送 2-自取)
|
||
startScheduledTime: '', //
|
||
endScheduledTime: '', //
|
||
tipDiscount: deliveryMethod.value === 1 ? 0 : (diyTipValue.value ? Number(diyTipValue.value) / 100 : (selectedTipIndex.value || 0) / 100), // 小费比例,自取订单不需要小费
|
||
needTableware: needTableware.value ? 1 : 2, // 餐具 1是 2否
|
||
}
|
||
|
||
// 如果是预约派送
|
||
if(deliveryTimeType.value === 1) {
|
||
const result = getTimeStamps(showDeliveryTime.value);
|
||
data.startScheduledTime = result.startTimestamp
|
||
data.endScheduledTime = result.endTimestamp
|
||
} else {
|
||
data.startScheduledTime = diyTime.value.startTime
|
||
data.endScheduledTime = diyTime.value.endTime
|
||
}
|
||
console.log('批量下单参数', data)
|
||
appMerchantOrderCreateOrderCartBatchPost({
|
||
body: data
|
||
}).then(res=> {
|
||
console.log('批量下单成功', res)
|
||
resOrderIds.value = res.data.orderIds || []
|
||
// 如果是余额支付,弹出支付密码弹窗
|
||
if(payMethodOptions.value.payMethod === 2) {
|
||
passwordInputRef.value?.showPasswordInput()
|
||
} else {
|
||
appMerchantOrderPayOrderBatch()
|
||
}
|
||
})
|
||
} else {
|
||
// 如果是余额支付,弹出支付密码弹窗
|
||
if(payMethodOptions.value.payMethod === 2) {
|
||
passwordInputRef.value?.showPasswordInput()
|
||
} else {
|
||
appMerchantOrderPayOrderBatch()
|
||
}
|
||
}
|
||
} else {
|
||
// 普通下单
|
||
if(!resOrderId.value) {
|
||
let data = {
|
||
addressId: targetAddressId,
|
||
phone: formData.value.phone,
|
||
areaCode: contact.value.areaCode,
|
||
cartIds: cartDataList.value.map(item => item.id),
|
||
couponId: couponInfo.value ? couponInfo.value.id : '',
|
||
deliveryMethod: visitMethod.value.label, // 派送方式(如放门口或者交到顾客手中)
|
||
deliveryType: deliveryTimeType.value, // 1-立即交付 2-预约交付
|
||
orderRemark: formData.value.orderRemark,
|
||
receiveMethod: deliveryMethod.value === 0 ? 1 : 2, // 收货方式(1-派送 2-自取)
|
||
startScheduledTime: '', //
|
||
endScheduledTime: '', //
|
||
tipDiscount: deliveryMethod.value === 1 ? 0 : (diyTipValue.value ? Number(diyTipValue.value) / 100 : (selectedTipIndex.value || 0) / 100), // 小费比例,自取订单不需要小费
|
||
// weeklyDeliveryFee: isWeeklyDelivery.value ? 1 : 2, // 是否支付周配送费(1-是 2-否)
|
||
needTableware: needTableware.value ? 1 : 2, // 餐具 1是 2否
|
||
}
|
||
|
||
// 如果是预约派送
|
||
if(deliveryTimeType.value === 1) {
|
||
const result = getTimeStamps(showDeliveryTime.value);
|
||
data.startScheduledTime = result.startTimestamp
|
||
data.endScheduledTime = result.endTimestamp
|
||
} else {
|
||
data.startScheduledTime = diyTime.value.startTime
|
||
data.endScheduledTime = diyTime.value.endTime
|
||
}
|
||
console.log('下单参数', data)
|
||
appMerchantOrderCreateOrderCartPost({
|
||
body: data
|
||
}).then(res=> {
|
||
console.log('下单成功', res)
|
||
resOrderId.value = res.data || ''
|
||
// 如果是余额支付,弹出支付密码弹窗
|
||
if(payMethodOptions.value.payMethod === 2) {
|
||
passwordInputRef.value?.showPasswordInput()
|
||
} else {
|
||
appMerchantOrderPayOrder()
|
||
}
|
||
})
|
||
} else {
|
||
// 如果是余额支付,弹出支付密码弹窗
|
||
if(payMethodOptions.value.payMethod === 2) {
|
||
passwordInputRef.value?.showPasswordInput()
|
||
} else {
|
||
appMerchantOrderPayOrder()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function payPawSuccess(password: string) {
|
||
payMethodOptions.value.payPassword = password
|
||
if(orderType.value === 'batch') {
|
||
appMerchantOrderPayOrderBatch()
|
||
} else {
|
||
appMerchantOrderPayOrder()
|
||
}
|
||
}
|
||
|
||
function appMerchantOrderPayOrder() {
|
||
appMerchantOrderPayOrderPost({
|
||
body: {
|
||
...payMethodOptions.value,
|
||
orderId: resOrderId.value
|
||
}
|
||
}).then(res=> {
|
||
console.log('支付结果', res)
|
||
uni.showToast({
|
||
title: t('pages-store.checkout.paymentSuccess'),
|
||
icon: 'none'
|
||
})
|
||
|
||
setTimeout(()=> {
|
||
uni.navigateBack({
|
||
delta: 2,
|
||
})
|
||
}, 500)
|
||
})
|
||
}
|
||
|
||
// 批量订单支付
|
||
function appMerchantOrderPayOrderBatch() {
|
||
// 使用批量支付接口
|
||
appMerchantOrderPayOrderBatchPost({
|
||
body: {
|
||
orderIds: resOrderIds.value,
|
||
cardId: payMethodOptions.value.cardId,
|
||
payMethod: payMethodOptions.value.payMethod,
|
||
payPassword: payMethodOptions.value.payPassword
|
||
}
|
||
}).then(res=> {
|
||
console.log('批量支付结果', res)
|
||
uni.showToast({
|
||
title: t('pages-store.checkout.paymentSuccess'),
|
||
icon: 'none'
|
||
})
|
||
|
||
setTimeout(()=> {
|
||
uni.navigateBack({
|
||
delta: 2,
|
||
})
|
||
}, 500)
|
||
}).catch(err => {
|
||
console.error('批量支付失败', err)
|
||
uni.showToast({
|
||
title: '支付失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
}
|
||
|
||
function getTimeStamps(timeStr: string) {
|
||
// 验证输入是否为空
|
||
if (!timeStr || typeof timeStr !== 'string') {
|
||
throw new Error('输入必须是有效的时间字符串,格式如 "HH:mm-HH:mm"');
|
||
}
|
||
|
||
// 验证格式是否正确
|
||
const timeFormatRegex = /^([01]\d|2[0-3]):([0-5]\d)-([01]\d|2[0-3]):([0-5]\d)$/;
|
||
if (!timeFormatRegex.test(timeStr)) {
|
||
throw new Error('时间格式不正确,请使用 "HH:mm-HH:mm" 格式,例如 "17:57-18:27"');
|
||
}
|
||
|
||
// 分割时间字符串
|
||
const [startTimeStr, endTimeStr] = timeStr.split('-');
|
||
|
||
// 获取当前日期
|
||
const today = dayjs().format('YYYY-MM-DD');
|
||
|
||
// 解析开始和结束时间
|
||
const startTime = dayjs(`${today} ${startTimeStr}`);
|
||
const endTime = dayjs(`${today} ${endTimeStr}`);
|
||
|
||
// 验证时间是否有效
|
||
if (!startTime.isValid()) {
|
||
throw new Error(`开始时间 ${startTimeStr} 无效`);
|
||
}
|
||
|
||
if (!endTime.isValid()) {
|
||
throw new Error(`结束时间 ${endTimeStr} 无效`);
|
||
}
|
||
|
||
// 验证结束时间是否晚于开始时间
|
||
if (endTime.isBefore(startTime)) {
|
||
throw new Error('结束时间不能早于开始时间');
|
||
}
|
||
|
||
// 返回时间戳(毫秒)
|
||
return {
|
||
startTimestamp: startTime.valueOf(),
|
||
endTimestamp: endTime.valueOf()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* CreateOrderCartBo
|
||
*/
|
||
export interface CreateOrderCartBo {
|
||
/**
|
||
* 配送地址id
|
||
*/
|
||
addressId?: number;
|
||
/**
|
||
* 区号
|
||
*/
|
||
areaCode?: string;
|
||
/**
|
||
* 购物车id列表
|
||
*/
|
||
cartIds?: number[];
|
||
/**
|
||
* 优惠券id
|
||
*/
|
||
couponId?: number;
|
||
/**
|
||
* 派送方式(如放门口或者交到顾客手中)
|
||
*/
|
||
deliveryMethod?: string;
|
||
/**
|
||
* 交付时间类型(1-立即交付 2-预约交付)
|
||
*/
|
||
deliveryType?: number;
|
||
/**
|
||
* 订单备注
|
||
*/
|
||
orderRemark?: string;
|
||
/**
|
||
* 手机号
|
||
*/
|
||
phone: string;
|
||
/**
|
||
* 收货方式(1-派送 2-自取)
|
||
*/
|
||
receiveMethod: number;
|
||
/**
|
||
* 预约时间- 开始
|
||
*/
|
||
startScheduledTime?: number;
|
||
/**
|
||
* 预约时间- 结束
|
||
*/
|
||
endScheduledTime?: number;
|
||
/**
|
||
* 小费比例
|
||
*/
|
||
tipDiscount?: number;
|
||
/**
|
||
* 是否支付周配送费(1-是 2-否)
|
||
*/
|
||
weeklyDeliveryFee?: number;
|
||
[property: string]: any;
|
||
}
|
||
|
||
const couponInfo = ref(null)
|
||
function navigateToCoupon() {
|
||
uni.navigateTo({
|
||
url: '/pages-user/pages/coupon/list?id=' + storeId.value,
|
||
events: {
|
||
// 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
|
||
selectedCoupon: function(data) {
|
||
console.log(data)
|
||
if(data) {
|
||
couponInfo.value = data
|
||
// 重新计算价格
|
||
appMerchantOrderCalculatePriceCart()
|
||
}
|
||
},
|
||
},
|
||
})
|
||
}
|
||
function handleClose() {
|
||
couponInfo.value = null
|
||
// 重新计算价格
|
||
appMerchantOrderCalculatePriceCart()
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<navbar />
|
||
<view
|
||
class="animate-in fade-in animate-ease-out animate-duration-300"
|
||
v-show="loading"
|
||
>
|
||
<!-- 骨架屏 -->
|
||
<CheckoutSkeleton
|
||
/></view>
|
||
<view
|
||
class="animate-in fade-in animate-ease-in animate-duration-300"
|
||
v-if="!loading"
|
||
>
|
||
<!-- 页面标题 -->
|
||
<view class="px-30rpx pt-20rpx">
|
||
<view class="text-46rpx font-bold text-#333 mb-52rpx">{{ t("pages-store.checkout.title") }}</view>
|
||
|
||
<!-- 配送方式选择 -->
|
||
<!-- <l-segmented
|
||
@click="handleClickSegmented"
|
||
:value="deliveryMethod"
|
||
:options="deliveryMethodOptions"
|
||
shape="round"
|
||
bg-color="#F2F2F2"
|
||
active-color="#333"
|
||
/> -->
|
||
</view>
|
||
|
||
<!-- 地址信息 -->
|
||
<view class="mt-4rpx">
|
||
<view
|
||
class="animate-in fade-in animate-ease-out animate-duration-300"
|
||
v-show="deliveryMethod === 0"
|
||
>
|
||
<!-- 收货地址 -->
|
||
<view @click="chooseAddress" class="flex-center-sb border-bottom py-36rpx px-30rpx">
|
||
<!-- 地址图标 -->
|
||
<view class="flex items-center">
|
||
<image
|
||
src="@img/chef/145.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx shrink-0 relative z-1"
|
||
/>
|
||
<!-- 地址信息 -->
|
||
<view v-if="addressesList.length > 0 && addressInfo" class="ml-28rpx">
|
||
<view
|
||
class="text-32rpx lh-32rpx font-500 text-#333 block mb-8rpx"
|
||
>
|
||
<text class="line-clamp-1">{{ addressInfo.formattedAddress }}</text>
|
||
</view>
|
||
<view class="text-28rpx lh-28rpx text-#6D6D6D">{{
|
||
addressInfo.displayName
|
||
}}</view>
|
||
</view>
|
||
<view v-else class="ml-28rpx text-32rpx lh-32rpx font-500 text-#333">{{ t('pages-store.checkout.addAddress') }}</view>
|
||
</view>
|
||
<image
|
||
src="@img/chef/142.png"
|
||
mode="aspectFill"
|
||
class="w-32rpx h-32rpx shrink-0"
|
||
/>
|
||
</view>
|
||
<!-- 配送偏好 -->
|
||
<view
|
||
@click="chooseVisitMethod"
|
||
class="flex-center-sb border-bottom py-36rpx px-30rpx"
|
||
>
|
||
<view class="flex items-center">
|
||
<image
|
||
src="@img/chef/1333.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx shrink-0 relative z-1"
|
||
/>
|
||
<view class="ml-28rpx text-32rpx lh-32rpx font-500 text-#333">
|
||
{{ visitMethod.label }}
|
||
</view>
|
||
</view>
|
||
<image
|
||
src="@img/chef/142.png"
|
||
mode="aspectFill"
|
||
class="w-32rpx h-32rpx shrink-0"
|
||
/>
|
||
</view>
|
||
<!-- 联系电话 -->
|
||
<view
|
||
@click="openChangePhone"
|
||
class="flex-center-sb border-bottom py-36rpx px-30rpx"
|
||
>
|
||
<view class="flex items-center">
|
||
<image
|
||
src="@img/chef/1334.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx shrink-0 relative z-1"
|
||
/>
|
||
<view class="ml-28rpx text-32rpx lh-32rpx font-500 text-#333">
|
||
{{ contact.areaCode }} {{ formData.phone }}
|
||
</view>
|
||
</view>
|
||
<image
|
||
src="@img/chef/142.png"
|
||
mode="aspectFill"
|
||
class="w-32rpx h-32rpx shrink-0"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view
|
||
class="animate-in fade-in animate-ease-in animate-duration-300"
|
||
v-show="deliveryMethod === 1"
|
||
>
|
||
<!-- 门店地址 -->
|
||
<view class="flex items-center border-bottom py-36rpx px-30rpx">
|
||
<image
|
||
src="@img/chef/1337.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx shrink-0 relative z-1"
|
||
/>
|
||
<view class="ml-28rpx">
|
||
<text class="text-32rpx lh-32rpx font-500 text-#333 block mb-8rpx"
|
||
>{{ storeDetail?.merchantName }}</text
|
||
>
|
||
<text class="text-28rpx lh-28rpx text-#6D6D6D line-clamp-1"
|
||
>{{ storeDetail?.merchantAddress }}</text
|
||
>
|
||
</view>
|
||
</view>
|
||
<!-- 门店距离 -->
|
||
<view class="flex items-center border-bottom py-36rpx px-30rpx">
|
||
<image
|
||
src="@img/chef/145.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx shrink-0 relative z-1"
|
||
/>
|
||
<view class="ml-28rpx">
|
||
<text
|
||
class="text-32rpx lh-32rpx font-500 text-#333 block mb-8rpx"
|
||
>{{ t("pages-store.checkout.distance") }}</text
|
||
>
|
||
<text class="text-28rpx lh-28rpx text-#6D6D6D">
|
||
<template v-if="storeDistance">
|
||
{{ storeDistance }} {{ t('common.mile') }}
|
||
</template>
|
||
<template v-else>{{ t('pages-store.checkout.enableLocationForDistance') }}</template>
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 个人配送选项 -->
|
||
<view class="flex-center-sb py-36rpx px-30rpx">
|
||
<view class="flex items-center">
|
||
<image
|
||
src="@img/chef/146.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx shrink-0 relative z-1"
|
||
/>
|
||
<view class="ml-28rpx text-32rpx lh-32rpx font-500 text-#333">
|
||
<template v-if="deliveryMethod === 0">
|
||
{{ t("pages-store.checkout.deliveryTime") }}
|
||
</template>
|
||
<template v-if="deliveryMethod === 1">
|
||
{{ t('pages-store.checkout.pickupTime') }}
|
||
</template>
|
||
</view>
|
||
</view>
|
||
<!-- 时间 -->
|
||
<view class="text-32rpx lh-32rpx font-500 text-#333">
|
||
<!--配送-->
|
||
<template v-if="deliveryMethod === 0">
|
||
<!--即刻配送-->
|
||
<template v-if="deliveryTimeType === 1">
|
||
{{ showDeliveryTime }}
|
||
</template>
|
||
<template v-else>
|
||
{{ userSelectedDeliveryTimeDate }}
|
||
</template>
|
||
</template>
|
||
<!--自取-->
|
||
<template v-if="deliveryMethod === 1">
|
||
<!--即刻配送-->
|
||
<template v-if="deliveryTimeType === 1">
|
||
{{ showDeliveryTime }}
|
||
</template>
|
||
<template v-else>
|
||
{{ userSelectedSelfPickupTimeDate }}
|
||
</template>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
<!--配送-->
|
||
<template v-if="storeIsDeliveryService && deliveryMethod === 0">
|
||
<view class="flex-center-sb pb-36rpx px-30rpx gap-22rpx">
|
||
<view
|
||
@click="toggleDeliveryTimeType(1)"
|
||
:class="[deliveryTimeType === 1 ? 'border-#333' : 'border-#D8D8D8']"
|
||
class="w-full h-152rpx text-28rpx lh-28rpx border-solid border-1px rounded-20rpx center"
|
||
>
|
||
<view class="text-center">
|
||
<view class="text-#333 mb-12rpx">
|
||
{{ t("pages-store.checkout.immediateDelivery") }}
|
||
</view>
|
||
<view class="text-#7D7D7D">{{ storeDetail?.deliveryTime }}{{ t('common.minutes') }}</view>
|
||
</view>
|
||
</view>
|
||
<view
|
||
@click="toggleDeliveryTimeType(2)"
|
||
:class="[deliveryTimeType === 2 ? 'border-#333' : 'border-#D8D8D8']"
|
||
class="w-full h-152rpx text-28rpx lh-28rpx border-solid border-1px rounded-20rpx center"
|
||
>
|
||
<view class="text-center">
|
||
<view class="text-#333 mb-12rpx">
|
||
{{ t("pages-store.checkout.appointmentDelivery") }}
|
||
</view>
|
||
<view class="text-#7D7D7D">
|
||
<template v-if="!userSelectedDeliveryTimeDate">
|
||
{{ t("pages-store.checkout.chooseTime") }}
|
||
</template>
|
||
<template v-else>
|
||
{{ userSelectedDeliveryTimeDate }}
|
||
</template>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<!--自取-->
|
||
<template v-if="storeIsSelfPickup && deliveryMethod === 1">
|
||
<view class="flex-center-sb pb-36rpx px-30rpx gap-22rpx">
|
||
<view
|
||
@click="toggleDeliveryTimeType(1)"
|
||
:class="[deliveryTimeType === 1 ? 'border-#333' : 'border-#D8D8D8']"
|
||
class="w-full h-152rpx text-28rpx lh-28rpx border-solid border-1px rounded-20rpx center"
|
||
>
|
||
<view class="text-center">
|
||
<view class="text-#333 mb-12rpx">
|
||
{{ t('pages-store.checkout.immediatePickup') }}
|
||
</view>
|
||
<view class="text-#7D7D7D">{{ storeDetail?.pickupTime }}{{ t('common.minutes') }}</view>
|
||
</view>
|
||
</view>
|
||
<view
|
||
@click="toggleDeliveryTimeType(2)"
|
||
:class="[deliveryTimeType === 2 ? 'border-#333' : 'border-#D8D8D8']"
|
||
class="w-full h-152rpx text-28rpx lh-28rpx border-solid border-1px rounded-20rpx center"
|
||
>
|
||
<view class="text-center">
|
||
<view class="text-#333 mb-12rpx">
|
||
{{ t('pages-store.checkout.appointmentPickup') }}
|
||
</view>
|
||
<view class="text-#7D7D7D">
|
||
<template v-if="!userSelectedSelfPickupTimeDate">
|
||
{{ t("pages-store.checkout.chooseTime") }}
|
||
</template>
|
||
<template v-else>
|
||
{{ userSelectedSelfPickupTimeDate }}
|
||
</template>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
|
||
<!-- 分隔线 -->
|
||
<view class="w-full h-10rpx bg-#F6F6F6"></view>
|
||
|
||
<!-- 订单信息摘要 -->
|
||
<view class="pt-36rpx pb-36rpx" v-if="orderType === 'normal' && cartDataList.length > 0">
|
||
<text
|
||
class="px-30rpx text-32rpx lh-32rpx font-500 text-#333 block mb-10rpx"
|
||
>{{ t("pages-store.checkout.orderInfoSummary") }}</text
|
||
>
|
||
|
||
<wd-collapse v-model="collapseValue">
|
||
<wd-collapse-item name="item3">
|
||
<template #title="{ expanded, disabled, isFirst }">
|
||
<view class="flex-center-sb">
|
||
<view class="flex items-center">
|
||
<image
|
||
class="w-80rpx h-80rpx rounded-50% mr-24rpx"
|
||
:src="storeDetail?.logo"
|
||
mode="aspectFill"
|
||
/>
|
||
<view>
|
||
<view class="text-30rpx lh-30rpx text-#333 font-500"
|
||
>{{ storeDetail?.merchantName }}</view
|
||
>
|
||
<view
|
||
class="text-28rpx lh-28rpx text-#7D7D7D font-400 mt-16rpx"
|
||
>{{ cartDataList.length }} {{ t('pages-user.cart.items') }}</view
|
||
>
|
||
</view>
|
||
</view>
|
||
<image
|
||
:class="[expanded ? 'rotate--90' : 'rotate-90']"
|
||
src="@img/chef/142.png"
|
||
mode="aspectFill"
|
||
class="w-32rpx h-32rpx shrink-0 transition-all duration-300"
|
||
/>
|
||
</view>
|
||
</template>
|
||
<template v-for="(item , index) in cartDataList">
|
||
<view
|
||
class="w-full border-bottom h-136rpx flex-center-sb pl-46rpx pr-30rpx"
|
||
>
|
||
<view class="flex items-center">
|
||
<view
|
||
class="w-48rpx h-48rpx rounded-8rpx bg-#F2F2F2 center mr-40rpx"
|
||
>{{ index + 1 }}</view
|
||
>
|
||
<view>
|
||
<view class="text-32rpx lh-32rpx text-#333 font-500"
|
||
>{{ item.merchantDishVo?.dishName }}</view
|
||
>
|
||
<view v-if="item.sideDishList?.length > 0"
|
||
class="text-28rpx lh-28rpx text-#7D7D7D font-400 mt-12rpx"
|
||
>
|
||
<template v-for="dish in item.sideDishList">
|
||
<text class="mr-6rpx">{{ dish.merchantSideDishItemVo?.name }}</text>
|
||
<text class="text-#00A76D mx-6rpx">+${{ dish.merchantSideDishItemVo?.price }}</text>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="text-32rpx lh-32rpx text-#333 font-500">${{ item.merchantDishVo?.discountPrice }}</view>
|
||
</view>
|
||
</template>
|
||
</wd-collapse-item>
|
||
</wd-collapse>
|
||
</view>
|
||
|
||
<!-- 批量模式:多店铺列表 -->
|
||
<view class="pt-36rpx pb-36rpx" v-if="orderType === 'batch' && cartDataList.length > 0">
|
||
<text
|
||
class="px-30rpx text-32rpx lh-32rpx font-500 text-#333 block mb-20rpx"
|
||
>{{ t("pages-store.checkout.orderInfoSummary") }}</text
|
||
>
|
||
<view class="px-30rpx">
|
||
<view
|
||
v-for="(merchant, merchantIndex) in cartDataList"
|
||
:key="merchant.id"
|
||
class="mb-20rpx rounded-16rpx border-1rpx border-solid border-[#E8E8E8] overflow-hidden"
|
||
>
|
||
<!-- 店铺头部 -->
|
||
<view class="p-20rpx flex items-center bg-#F6F6F6">
|
||
<image
|
||
class="w-60rpx h-60rpx rounded-50% mr-20rpx"
|
||
:src="merchant.logo"
|
||
mode="aspectFill"
|
||
/>
|
||
<view>
|
||
<view class="text-28rpx lh-28rpx text-#333 font-500"
|
||
>{{ merchant.merchantName }}</view
|
||
>
|
||
<view
|
||
class="text-24rpx lh-24rpx text-#7D7D7D font-400 mt-8rpx"
|
||
>{{ merchant.merchantCartVoList.length }} {{ t('pages-user.cart.items') }}</view
|
||
>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 商品列表 -->
|
||
<view class="px-20rpx">
|
||
<view
|
||
v-for="(item, itemIndex) in merchant.merchantCartVoList"
|
||
:key="item.id"
|
||
class="py-20rpx flex items-start"
|
||
:class="itemIndex < merchant.merchantCartVoList.length - 1 ? 'border-bottom border-[#F2F2F2]' : ''"
|
||
>
|
||
<!-- 商品图片 -->
|
||
<image
|
||
:src="item.merchantDishVo?.dishImage?.split(',')[0]"
|
||
mode="aspectFill"
|
||
class="w-100rpx h-100rpx shrink-0 rounded-12rpx"
|
||
></image>
|
||
|
||
<!-- 商品信息 -->
|
||
<view class="ml-16rpx flex-1">
|
||
<view class="text-[#333333] text-26rpx lh-36rpx font-500 line-clamp-2">{{
|
||
item.merchantDishVo?.dishName
|
||
}}</view>
|
||
|
||
<!-- 配菜信息 -->
|
||
<view class="text-[#7D7D7D] text-22rpx lh-26rpx mt-8rpx" v-if="item.sideDishList?.length > 0">
|
||
<template v-for="dish in item.sideDishList">
|
||
<text class="mr-6rpx">
|
||
{{ dish?.merchantSideDishItemVo?.name || '' }}
|
||
</text>
|
||
<text v-if="dish?.merchantSideDishItemVo?.price" class="text-#00A76D mr-10rpx">+${{ dish?.merchantSideDishItemVo?.price }}</text>
|
||
</template>
|
||
</view>
|
||
|
||
<!-- 价格和数量 -->
|
||
<view class="flex items-center justify-between mt-12rpx">
|
||
<view class="flex items-center">
|
||
<text class="text-[#333333] text-28rpx font-500"
|
||
>${{ item.merchantDishVo?.discountPrice }}</text>
|
||
<text
|
||
v-if="item.merchantDishVo?.originalPrice && item.merchantDishVo?.originalPrice !== item.merchantDishVo?.discountPrice"
|
||
class="text-[#7D7D7D] text-22rpx line-through ml-8rpx"
|
||
>${{ item.merchantDishVo?.originalPrice }}</text>
|
||
</view>
|
||
<view class="text-[#7D7D7D] text-22rpx">
|
||
x{{ item.count }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 批量模式且没有数据时的提示 -->
|
||
<view class="pt-36rpx pb-36rpx" v-if="orderType === 'batch' && cartDataList.length === 0">
|
||
<text
|
||
class="px-30rpx text-32rpx lh-32rpx font-500 text-#333 block mb-10rpx"
|
||
>{{ t("pages-store.checkout.orderInfoSummary") }}</text
|
||
>
|
||
<view class="px-30rpx py-40rpx text-center">
|
||
<text class="text-28rpx text-#7D7D7D">已选择 {{ batchCartIds.length }} 件商品</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="pt-36rpx pb-36rpx" >
|
||
<!-- 优惠券 -->
|
||
<view @click="navigateToCoupon" class="flex-center-sb border-bottom py-36rpx px-30rpx gap-20rpx">
|
||
<view class="flex-center-sb flex-1">
|
||
<view class="flex items-center">
|
||
<image
|
||
src="@img/chef/105.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx relative z-1"
|
||
/>
|
||
<view class="ml-28rpx text-32rpx lh-32rpx font-500 text-#333">
|
||
{{ couponInfo? couponInfo.snapshotNameZh : t('pages-store.checkout.addCoupon') }}
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="couponInfo" @click.stop="handleClose" class="i-carbon:close-filled"></view>
|
||
</view>
|
||
<image
|
||
src="@img/chef/142.png"
|
||
mode="aspectFill"
|
||
class="w-32rpx h-32rpx shrink-0"
|
||
/>
|
||
</view>
|
||
|
||
<template v-if="deliveryMethod === 0">
|
||
<!-- 小费 -->
|
||
<view class="border-bottom py-36rpx px-30rpx">
|
||
<view class="flex items-center mb-28rpx">
|
||
<image
|
||
src="@img/chef/1335.png"
|
||
mode="aspectFill"
|
||
class="w-44rpx h-44rpx relative z-1"
|
||
/>
|
||
<view class="ml-28rpx text-32rpx lh-32rpx font-500 text-#333">
|
||
{{ t('pages-store.checkout.chooseTips') }}
|
||
</view>
|
||
</view>
|
||
|
||
<view class="flex-center-sb gap-22rpx">
|
||
<template v-for="(item, index) in tipOptions">
|
||
<view
|
||
@click="selectedTipChange(item)"
|
||
:class="[
|
||
selectedTipIndex === item.value
|
||
? 'border-#333'
|
||
: 'border-#D8D8D8',
|
||
]"
|
||
class="w-full h-72rpx text-28rpx lh-28rpx text-#333 rounded-20rpx border-solid border-1px center"
|
||
>
|
||
{{ item.label }}
|
||
</view>
|
||
</template>
|
||
<view
|
||
@click="selectedTipChange({ value: 0 })"
|
||
:class="[
|
||
selectedTipIndex === 0
|
||
? 'border-#333'
|
||
: 'border-#D8D8D8',
|
||
]"
|
||
class="w-full h-72rpx text-28rpx lh-28rpx text-#333 rounded-20rpx border-solid border-1px center"
|
||
>
|
||
<view class="px-10rpx center">
|
||
<wd-input
|
||
no-border
|
||
use-prefix-slot
|
||
@blur="handleConfirmTip"
|
||
custom-class="!center !text-30rpx !bg-transparent flex-1 !text-center"
|
||
placeholderStyle="font-size: 28rpx;color: #333; text-align: center;"
|
||
:placeholder="t('pages-store.checkout.other')"
|
||
v-model="diyTipValue"
|
||
@confirm="handleConfirmTip"
|
||
>
|
||
<template #suffix>
|
||
<text v-if="diyTipValue.length > 0">%</text>
|
||
</template>
|
||
</wd-input>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 配送费周卡 -->
|
||
<!-- <view v-if="isShowWeeklyDelivery" class="border-bottom px-30rpx py-36rpx">-->
|
||
<!-- <view class="flex-center-sb">-->
|
||
<!-- <text class="text-32rpx lh-32rpx font-500 text-#333"-->
|
||
<!-- >{{ t('pages-store.checkout.tips') }}?</text-->
|
||
<!-- >-->
|
||
<!-- <view-->
|
||
<!-- class="flex items-center gap-50rpx text-32rpx lh-32rpx font-500 text-#333"-->
|
||
<!-- >-->
|
||
<!-- <view @click="toggleWeeklyDelivery(true)" class="flex items-center">-->
|
||
<!-- <!– 单选按钮是 –>-->
|
||
<!-- <image-->
|
||
<!-- :src="-->
|
||
<!-- isWeeklyDelivery-->
|
||
<!-- ? '/static/images/chef/133.png'-->
|
||
<!-- : '/static/images/chef/134.png'-->
|
||
<!-- "-->
|
||
<!-- class="w-36rpx h-36rpx shrink-0 mr-10rpx"-->
|
||
<!-- mode="aspectFit"-->
|
||
<!-- />-->
|
||
<!-- {{ t('pages-store.checkout.yes') }}-->
|
||
<!-- </view>-->
|
||
<!-- <view-->
|
||
<!-- @click="toggleWeeklyDelivery(false)"-->
|
||
<!-- class="flex items-center"-->
|
||
<!-- >-->
|
||
<!-- <!– 单选按钮否 –>-->
|
||
<!-- <image-->
|
||
<!-- :src="-->
|
||
<!-- !isWeeklyDelivery-->
|
||
<!-- ? '/static/images/chef/133.png'-->
|
||
<!-- : '/static/images/chef/134.png'-->
|
||
<!-- "-->
|
||
<!-- class="w-36rpx h-36rpx shrink-0 mr-10rpx"-->
|
||
<!-- mode="aspectFit"-->
|
||
<!-- />-->
|
||
<!-- {{ t('pages-store.checkout.no') }}-->
|
||
<!-- </view>-->
|
||
<!-- </view>-->
|
||
<!-- </view>-->
|
||
<!-- <view class="text-28rpx lh-28rpx text-#7D7D7D mt-24rpx">-->
|
||
<!-- {{ t('pages-store.checkout.tipsDesc') }}-->
|
||
<!-- </view>-->
|
||
<!-- </view>-->
|
||
</template>
|
||
|
||
<!-- 费用明细 -->
|
||
<view class="border-bottom px-30rpx py-32rpx">
|
||
<view class="flex-center-sb mb-40rpx text-32rpx lh-32rpx text-#333">
|
||
<text>{{ t('pages-store.order.subtotal') }}</text>
|
||
<text v-if="orderType === 'batch'">${{ priceData?.totalActualAmount || '0.00' }}</text>
|
||
<text v-else>${{ priceData?.actualAmount || '0.00' }}</text>
|
||
</view>
|
||
<view class="flex-center-sb mb-40rpx text-32rpx lh-32rpx text-#333">
|
||
<view @click="openPriceDetail" class="flex items-center">
|
||
<text>{{ t('pages-store.order.taxesAndOtherFees') }}</text>
|
||
<image
|
||
src="@img/chef/1336.png"
|
||
class="w-28rpx h-28rpx shrink-0 ml-10rpx mt-4rpx"
|
||
mode="aspectFit"
|
||
/>
|
||
</view>
|
||
<view v-if="orderType === 'batch'">
|
||
<text>${{ (Number(priceData?.totalTax || 0) + Number(priceData?.totalTip || 0) + Number(priceData?.totalDeliveryFee || 0)).toFixed(2) }}</text>
|
||
</view>
|
||
<view v-else>
|
||
<text>${{ (Number(priceData?.tax || 0) + Number(priceData?.tip || 0) + Number(priceData?.deliveryFee || 0)).toFixed(2) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="flex-center-sb text-32rpx lh-32rpx text-#333 font-500">
|
||
<text>{{ t('pages-store.order.total') }}</text>
|
||
<text v-if="orderType === 'batch'">${{ priceData?.totalPaidAmount || '0.00' }}</text>
|
||
<text v-else>${{ priceData?.paidAmount || '0.00' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付方式 -->
|
||
<view
|
||
@click="navigateTo('/pages-user/pages/choose-paymethod/index')"
|
||
class="flex-center-sb border-bottom text-32rpx lh-32rpx font-500 text-#333 py-36rpx px-30rpx"
|
||
>
|
||
<view class="flex items-center">
|
||
<image
|
||
src="@img/chef/138.png"
|
||
class="w-44rpx h-44rpx mr-28rpx shrink-0"
|
||
mode="aspectFit"
|
||
/>
|
||
<text class="">
|
||
<template v-if="payMethodOptions.payMethod === 1">{{ t('pages-user.choosePaymethod.creditCard') }}</template>
|
||
<template v-else>{{ t('pages-user.choosePaymethod.wallet') }}</template>
|
||
</text>
|
||
</view>
|
||
<view class="flex items-center">
|
||
<view class="mr-12px">
|
||
<template v-if="payMethodOptions.payMethod === 1">
|
||
<!--判断用户是否有信用卡-->
|
||
<text v-if="!payMethodOptions.cardId">{{ t("pages-user.member.creditCard") }}</text>
|
||
<text v-else>{{ payMethodOptions.cardNumber }}</text>
|
||
</template>
|
||
<template v-else-if="payMethodOptions.payMethod === 2">
|
||
<text>{{ t('pages-user.choosePaymethod.wallet') }}</text>
|
||
</template>
|
||
</view>
|
||
<image
|
||
src="@img/chef/142.png"
|
||
class="w-32rpx h-32rpx shrink-0"
|
||
mode="aspectFit"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="h-204rpx"></view>
|
||
|
||
<!-- 底部支付按钮 -->
|
||
<view class="fixed z-9 bottom-0 left-0 right-0 bg-white">
|
||
<view @click="navigateTo('/pages-user/pages/member/index')" v-if="cartSavingsData && cartSavingsData.savings > 0" class="h-76rpx bg-#CE7138 pl-56rpx flex items-center">
|
||
<image
|
||
src="@img/chef/1289.png"
|
||
class="mr-10rpx w-32rpx h-32rpx shrink-0"
|
||
></image>
|
||
<text class="text-[#fff] text-24rpx lh-24rpx">
|
||
{{ t('pages-store.store.use') }} {{ Config.appName }} {{ t('pages-store.store.tips3') }} ${{ cartSavingsData?.savings }} {{ t('pages-store.store.discount') }}
|
||
</text>
|
||
</view>
|
||
<view class="px-30rpx py-18rpx">
|
||
<wd-button
|
||
@click="handleGoSettle"
|
||
custom-class="!h-92rpx !text-30rpx !text-#fff !lh-92rpx !rounded-16rpx"
|
||
block
|
||
>{{ t('common.goSettle') }}
|
||
</wd-button>
|
||
<!-- <wd-button
|
||
v-if="showDeliverySwitch"
|
||
@click="handleGoSettle"
|
||
custom-class="!h-92rpx !text-30rpx !text-#fff !lh-92rpx !rounded-16rpx"
|
||
block
|
||
>{{ t('common.goSettle') }}
|
||
</wd-button> -->
|
||
<!-- <wd-button
|
||
v-else
|
||
custom-class="!h-92rpx !text-30rpx !text-#fff !lh-92rpx !rounded-16rpx"
|
||
block
|
||
>{{ t('pages-store.checkout.notActivated') }}
|
||
</wd-button> -->
|
||
</view>
|
||
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
|
||
</view>
|
||
|
||
<!-- 修改配送偏好 -->
|
||
<visit-method ref="visitMethodRef" @confirm="handleVisitMethodConfirm" />
|
||
<!-- 修改手机号 -->
|
||
<change-phone ref="changePhoneRef" @confirm="confirmPhone" />
|
||
<!-- 价格明细 -->
|
||
<price-detail ref="priceDetailRef" />
|
||
<password-container @success="payPawSuccess" ref="passwordInputRef" />
|
||
</view>
|
||
</template>
|
||
|
||
<style>
|
||
page {
|
||
background-color: #fff;
|
||
}
|
||
</style>
|
||
<style scoped lang="scss">
|
||
:deep(.wd-collapse-item) {
|
||
&::after {
|
||
display: none;
|
||
background-color: transparent !important;
|
||
}
|
||
}
|
||
:deep(.wd-collapse-item__header.is-expanded) {
|
||
&::after {
|
||
background-color: transparent !important;
|
||
}
|
||
}
|
||
:deep(.wd-collapse-item__body) {
|
||
padding: 0 !important;
|
||
}
|
||
</style>
|