修改样式
This commit is contained in:
@@ -25,21 +25,18 @@ import {
|
||||
} from "@/service";
|
||||
import useEventEmit from "@/hooks/useEventEmit";
|
||||
import {EventEnum} from "@/constant/enums";
|
||||
import { getDistanceInMiles} from "@/utils/utils";
|
||||
import { getDistanceInMiles, parseMerchantCartPayload } from "@/utils/utils";
|
||||
import {
|
||||
buildReservationTimeUrl,
|
||||
buildDayAppointmentSlot,
|
||||
findNearestDeliveryScheduleDate,
|
||||
loadCheckoutMerchantAppointments,
|
||||
type MerchantAppointmentSlot,
|
||||
} from "@/utils/deliverySchedule";
|
||||
const { t } = useI18n();
|
||||
const configStore = useConfigStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
function fillI18nParams(template: string, params: Record<string, string | number>) {
|
||||
let text = template;
|
||||
Object.keys(params).forEach((key) => {
|
||||
const value = String(params[key] ?? "");
|
||||
text = text.replace(new RegExp(`\\{${key}\\}`, "g"), value);
|
||||
text = text.replace(new RegExp(`\\$\\{${key}\\}`, "g"), value);
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
const appDisplayName = computed(() => {
|
||||
const name = String((Config as any)?.appName ?? "").trim();
|
||||
return name || "CHEFLINK";
|
||||
@@ -111,43 +108,134 @@ const showDeliveryTime = computed(() => {
|
||||
}
|
||||
return "";
|
||||
});
|
||||
// 切换配送时间点击事件(目前仅支持预约配送)
|
||||
function toggleDeliveryTimeType(type: number) {
|
||||
// 统一设置为预约配送
|
||||
deliveryTimeType.value = 2;
|
||||
const merchantAppointmentMap = ref<Record<string, MerchantAppointmentSlot>>({});
|
||||
const merchantAppointmentDisplay = ref<Record<string, string>>({});
|
||||
const selectingMerchantId = ref<string | null>(null);
|
||||
|
||||
// 跳转到时间选择页面
|
||||
const businessHours = (storeDetail.value as any)?.businessHours;
|
||||
const url = businessHours
|
||||
? `/pages/address/reservation-time?storeBusinessHours=${encodeURIComponent(
|
||||
businessHours
|
||||
)}`
|
||||
: "/pages/address/reservation-time";
|
||||
function ensureDefaultMerchantAppointment(merchant: {
|
||||
id?: string | number;
|
||||
deliveryScheduleTimes?: string;
|
||||
}) {
|
||||
const key = String(merchant.id ?? "");
|
||||
if (!key || merchantAppointmentMap.value[key]?.startTime) return;
|
||||
|
||||
const nearest = findNearestDeliveryScheduleDate(
|
||||
merchant.deliveryScheduleTimes
|
||||
);
|
||||
if (!nearest) return;
|
||||
|
||||
const slot = buildDayAppointmentSlot(nearest);
|
||||
merchantAppointmentMap.value[key] = slot;
|
||||
syncAppointmentDisplay(key, { date: nearest, timeSlot: "" });
|
||||
}
|
||||
|
||||
function syncAppointmentDisplay(merchantId: string, data: any) {
|
||||
const label =
|
||||
dayjs(data.date).format("MM-DD") +
|
||||
(data.timeSlot ? ` ${data.timeSlot}` : "");
|
||||
merchantAppointmentDisplay.value[merchantId] = label;
|
||||
if (deliveryMethod.value === 0) {
|
||||
userSelectedDeliveryTimeDate.value = label;
|
||||
} else {
|
||||
userSelectedSelfPickupTimeDate.value = label;
|
||||
}
|
||||
}
|
||||
|
||||
async function openReservationForMerchant(merchant: {
|
||||
id?: string | number;
|
||||
businessHours?: string;
|
||||
deliveryScheduleTimes?: string;
|
||||
}) {
|
||||
if (!merchant?.id) return;
|
||||
selectingMerchantId.value = String(merchant.id);
|
||||
let schedule = merchant.deliveryScheduleTimes;
|
||||
let hours = merchant.businessHours;
|
||||
if (!schedule || !hours) {
|
||||
try {
|
||||
const res: any = await appMerchantDetailMerchantIdGet({
|
||||
params: { merchantId: merchant.id as any },
|
||||
});
|
||||
schedule = res.data?.deliveryScheduleTimes ?? schedule;
|
||||
hours = res.data?.businessHours ?? hours;
|
||||
const mid = String(merchant.id);
|
||||
const found = cartDataList.value.find(
|
||||
(m: any) => String(m.id) === mid
|
||||
) as any;
|
||||
if (found) {
|
||||
found.deliveryScheduleTimes = schedule;
|
||||
found.businessHours = hours;
|
||||
}
|
||||
if (String(storeId.value) === mid) {
|
||||
storeDetail.value = {
|
||||
...storeDetail.value,
|
||||
deliveryScheduleTimes: schedule,
|
||||
businessHours: hours,
|
||||
} as MerchantVo;
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
uni.navigateTo({
|
||||
url,
|
||||
url: buildReservationTimeUrl({
|
||||
merchantId: merchant.id,
|
||||
deliveryScheduleTimes: schedule,
|
||||
businessHours: hours,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const diyTime = ref({})
|
||||
useEventEmit(EventEnum.CHOOSE_APPOINTMENT_TIME, (data) => {
|
||||
console.log('CHOOSE_APPOINTMENT_TIME', data)
|
||||
if(data) {
|
||||
diyTime.value = data;
|
||||
deliveryTimeType.value = 2
|
||||
// 切换配送时间点击事件(目前仅支持预约配送)
|
||||
function toggleDeliveryTimeType(
|
||||
_type: number,
|
||||
merchant?: { id?: string | number; businessHours?: string; deliveryScheduleTimes?: string }
|
||||
) {
|
||||
deliveryTimeType.value = 2;
|
||||
if (orderType.value === "batch" && merchant) {
|
||||
void openReservationForMerchant(merchant);
|
||||
return;
|
||||
}
|
||||
void openReservationForMerchant({
|
||||
id: storeId.value,
|
||||
businessHours: (storeDetail.value as any)?.businessHours,
|
||||
deliveryScheduleTimes: storeDetail.value?.deliveryScheduleTimes,
|
||||
});
|
||||
}
|
||||
|
||||
// 配送还是自取
|
||||
if(deliveryMethod.value === 0) {
|
||||
userSelectedDeliveryTimeDate.value =
|
||||
dayjs(data.date).format('MM-DD') +
|
||||
(data.timeSlot ? ' ' + data.timeSlot : '');
|
||||
} else {
|
||||
userSelectedSelfPickupTimeDate.value =
|
||||
dayjs(data.date).format('MM-DD') +
|
||||
(data.timeSlot ? ' ' + data.timeSlot : '');
|
||||
function getMerchantPillText(merchantId: string | number) {
|
||||
const key = String(merchantId);
|
||||
if (merchantAppointmentDisplay.value[key]) {
|
||||
return `${merchantAppointmentDisplay.value[key]} >`;
|
||||
}
|
||||
const ap = merchantAppointmentMap.value[key];
|
||||
if (ap?.startTime) {
|
||||
const d = dayjs(Number(ap.startTime));
|
||||
if (d.isValid()) {
|
||||
return `${d.format("dddd")}, ${d.format("MM/DD")} >`;
|
||||
}
|
||||
}
|
||||
})
|
||||
return t("pages-store.checkout.chooseTime");
|
||||
}
|
||||
|
||||
const diyTime = ref<MerchantAppointmentSlot | Record<string, never>>({});
|
||||
useEventEmit(EventEnum.CHOOSE_APPOINTMENT_TIME, (data: any) => {
|
||||
if (!data?.startTime || !data?.endTime) return;
|
||||
const merchantId = String(
|
||||
data.merchantId || selectingMerchantId.value || storeId.value || ""
|
||||
);
|
||||
if (!merchantId) return;
|
||||
|
||||
merchantAppointmentMap.value[merchantId] = {
|
||||
date: data.date,
|
||||
timeSlot: data.timeSlot,
|
||||
startTime: data.startTime,
|
||||
endTime: data.endTime,
|
||||
};
|
||||
diyTime.value = merchantAppointmentMap.value[merchantId];
|
||||
deliveryTimeType.value = 2;
|
||||
syncAppointmentDisplay(merchantId, data);
|
||||
selectingMerchantId.value = null;
|
||||
});
|
||||
|
||||
// 设置配送时间(分钟)
|
||||
const setDeliveryMinutes = (minutes: number) => {
|
||||
@@ -408,6 +496,19 @@ onLoad(async (options: any)=> {
|
||||
// 严格按顺序执行
|
||||
await safeAwait('getAddressList', getAddressList)
|
||||
await safeAwait('getBatchCartInfo', getBatchCartInfo)
|
||||
merchantAppointmentMap.value = loadCheckoutMerchantAppointments()
|
||||
cartDataList.value.forEach((m: any) => {
|
||||
const key = String(m.id);
|
||||
const ap = merchantAppointmentMap.value[key];
|
||||
if (ap?.startTime) {
|
||||
syncAppointmentDisplay(key, {
|
||||
date: ap.date ? new Date(ap.date as any) : new Date(Number(ap.startTime)),
|
||||
timeSlot: ap.timeSlot,
|
||||
});
|
||||
} else {
|
||||
ensureDefaultMerchantAppointment(m);
|
||||
}
|
||||
});
|
||||
await safeAwait('appUserCardSelectDefault', appUserCardSelectDefault)
|
||||
} else if(options.storeId) {
|
||||
// 普通下单模式
|
||||
@@ -469,7 +570,7 @@ async function getCartInfo() {
|
||||
})
|
||||
|
||||
console.log('购物车列表', res)
|
||||
cartDataList.value = res.data
|
||||
cartDataList.value = parseMerchantCartPayload(res?.data).items as MerchantCartVo[]
|
||||
|
||||
// 购物车有菜品,查询菜品会员折扣价 + 计算价格(严格串行)
|
||||
if(cartDataList.value.length > 0) {
|
||||
@@ -503,9 +604,9 @@ async function getBatchCartInfo() {
|
||||
}
|
||||
});
|
||||
console.log(`批量模式-店铺 ${merchant.merchantName} 的商品列表`, cartRes);
|
||||
|
||||
// 只保留选中的商品(根据 cartIds 过滤)
|
||||
const filteredItems = (cartRes.data || []).filter((item: any) =>
|
||||
|
||||
const payload = parseMerchantCartPayload(cartRes?.data);
|
||||
const filteredItems = payload.items.filter((item: any) =>
|
||||
cartIds.includes(String(item.id))
|
||||
);
|
||||
|
||||
@@ -513,6 +614,8 @@ async function getBatchCartInfo() {
|
||||
if (filteredItems.length > 0) {
|
||||
return {
|
||||
...merchant,
|
||||
deliveryScheduleTimes:
|
||||
payload.deliveryScheduleTimes ?? merchant.deliveryScheduleTimes,
|
||||
merchantCartVoList: filteredItems
|
||||
};
|
||||
}
|
||||
@@ -532,6 +635,7 @@ async function getBatchCartInfo() {
|
||||
if (merchantTipIndexMap.value[id] === undefined) {
|
||||
merchantTipIndexMap.value[id] = 2;
|
||||
}
|
||||
ensureDefaultMerchantAppointment(m);
|
||||
});
|
||||
|
||||
// 批量模式:查询菜品会员折扣价 + 计算价格(严格串行)
|
||||
@@ -616,6 +720,11 @@ async function getStoreDetail() {
|
||||
deliveryMethod.value = 0
|
||||
showDeliverySwitch.value = true
|
||||
}
|
||||
|
||||
ensureDefaultMerchantAppointment({
|
||||
id: storeId.value,
|
||||
deliveryScheduleTimes: storeDetail.value?.deliveryScheduleTimes,
|
||||
});
|
||||
}
|
||||
|
||||
const priceData = ref({})
|
||||
@@ -659,13 +768,25 @@ async function appMerchantOrderCalculatePriceCart() {
|
||||
}
|
||||
})
|
||||
|
||||
const startMap: Record<string, number> = {}
|
||||
const endMap: Record<string, number> = {}
|
||||
cartDataList.value.forEach((m: any) => {
|
||||
const ap = merchantAppointmentMap.value[String(m.id)]
|
||||
if (ap?.startTime && ap?.endTime) {
|
||||
startMap[String(m.id)] = ap.startTime
|
||||
endMap[String(m.id)] = ap.endTime
|
||||
}
|
||||
})
|
||||
|
||||
const res: any = await appMerchantOrderCalculatePriceCartBatchPost({
|
||||
body: {
|
||||
addressId: targetAddressId,
|
||||
cartIds: cartIds,
|
||||
receiveMethod: deliveryMethod.value === 0 ? 1 : 2,
|
||||
merchantCouponMap: couponMap,
|
||||
merchantTipMap: tipMap, // 小费金额(美元)
|
||||
merchantTipMap: tipMap,
|
||||
merchantStartScheduledTimeMap: startMap,
|
||||
merchantEndScheduledTimeMap: endMap,
|
||||
phone: formData.value.phone,
|
||||
areaCode: contact.value.areaCode,
|
||||
}
|
||||
@@ -792,13 +913,30 @@ function handleGoSettle() {
|
||||
return
|
||||
}
|
||||
|
||||
// 仅支持预约配送:必须先选择预约时间
|
||||
if (deliveryTimeType.value === 2 && (!diyTime.value.startTime || !diyTime.value.endTime)) {
|
||||
uni.showToast({
|
||||
title: t('pages-store.checkout.chooseTime'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
if (deliveryTimeType.value === 2) {
|
||||
if (orderType.value === "batch") {
|
||||
for (const m of cartDataList.value as any[]) {
|
||||
const ap = merchantAppointmentMap.value[String(m.id)];
|
||||
if (!ap?.startTime || !ap?.endTime) {
|
||||
uni.showToast({
|
||||
title: t("pages-store.checkout.chooseTimeForStore", {
|
||||
name: m.merchantName || "",
|
||||
}),
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const ap = merchantAppointmentMap.value[String(storeId.value)];
|
||||
if (!ap?.startTime || !ap?.endTime) {
|
||||
uni.showToast({
|
||||
title: t("pages-store.checkout.chooseTime"),
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 批量下单
|
||||
@@ -851,14 +989,22 @@ function handleGoSettle() {
|
||||
needTableware: needTableware.value ? 1 : 2, // 餐具 1是 2否
|
||||
}
|
||||
|
||||
// 如果是预约派送
|
||||
if(deliveryTimeType.value === 1) {
|
||||
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
|
||||
const startMap: Record<string, number> = {}
|
||||
const endMap: Record<string, number> = {}
|
||||
cartDataList.value.forEach((m: any) => {
|
||||
const ap = merchantAppointmentMap.value[String(m.id)]
|
||||
if (ap?.startTime && ap?.endTime) {
|
||||
startMap[String(m.id)] = ap.startTime
|
||||
endMap[String(m.id)] = ap.endTime
|
||||
}
|
||||
})
|
||||
data.merchantStartScheduledTimeMap = startMap
|
||||
data.merchantEndScheduledTimeMap = endMap
|
||||
}
|
||||
console.log('批量下单参数', data)
|
||||
appMerchantOrderCreateOrderCartBatchPost({
|
||||
@@ -924,8 +1070,9 @@ function handleGoSettle() {
|
||||
data.startScheduledTime = result.startTimestamp
|
||||
data.endScheduledTime = result.endTimestamp
|
||||
} else {
|
||||
data.startScheduledTime = diyTime.value.startTime
|
||||
data.endScheduledTime = diyTime.value.endTime
|
||||
const ap = merchantAppointmentMap.value[String(storeId.value)]
|
||||
data.startScheduledTime = ap?.startTime ?? (diyTime.value as any).startTime
|
||||
data.endScheduledTime = ap?.endTime ?? (diyTime.value as any).endTime
|
||||
}
|
||||
console.log('下单参数', data)
|
||||
appMerchantOrderCreateOrderCartPost({
|
||||
@@ -1201,22 +1348,10 @@ const serviceFeeAmount = computed(() => {
|
||||
});
|
||||
|
||||
const deliveryPillText = computed(() => {
|
||||
const raw =
|
||||
deliveryMethod.value === 0
|
||||
? userSelectedDeliveryTimeDate.value
|
||||
: userSelectedSelfPickupTimeDate.value;
|
||||
if (!raw) return t('pages-store.checkout.chooseTime');
|
||||
const str = String(raw);
|
||||
const m = str.match(/^(\d{2})-(\d{2})/);
|
||||
if (m) {
|
||||
const y = dayjs().year();
|
||||
const d = dayjs(`${y}-${m[1]}-${m[2]}`);
|
||||
if (d.isValid()) {
|
||||
const w = d.format('dddd');
|
||||
return `${w}, ${m[1]}/${m[2]} >`;
|
||||
}
|
||||
if (orderType.value === "batch") {
|
||||
return t("pages-store.checkout.chooseTime");
|
||||
}
|
||||
return `${str} >`;
|
||||
return getMerchantPillText(storeId.value);
|
||||
});
|
||||
|
||||
function safeToNumber(val: unknown, fallback = 0) {
|
||||
@@ -1282,24 +1417,24 @@ const scheduledServiceEndLabel = computed(() => {
|
||||
});
|
||||
|
||||
const localDeliverySubtitleText = computed(() =>
|
||||
fillI18nParams(t('pages-store.checkout.localDeliverySubtitle'), { name: appDisplayName.value })
|
||||
t('pages-store.checkout.localDeliverySubtitle', { name: appDisplayName.value })
|
||||
);
|
||||
|
||||
const itemsGoodsTotalWithPriceText = computed(() =>
|
||||
fillI18nParams(t('pages-store.checkout.itemsGoodsTotalWithPrice'), {
|
||||
t('pages-store.checkout.itemsGoodsTotalWithPrice', {
|
||||
count: cartTotalPieceCount.value,
|
||||
amount: displayGoodsAmountStr.value,
|
||||
})
|
||||
);
|
||||
|
||||
const scheduledDeliveryWindowText = computed(() =>
|
||||
fillI18nParams(t('pages-store.checkout.scheduledDeliveryWindow'), {
|
||||
t('pages-store.checkout.scheduledDeliveryWindow', {
|
||||
time: scheduledServiceEndLabel.value,
|
||||
})
|
||||
);
|
||||
|
||||
const deliverBeforeText = computed(() =>
|
||||
fillI18nParams(t('pages-store.checkout.deliverBefore'), {
|
||||
t('pages-store.checkout.deliverBefore', {
|
||||
time: deliveryMethod.value === 0
|
||||
? String(userSelectedDeliveryTimeDate.value ?? '')
|
||||
: String(userSelectedSelfPickupTimeDate.value ?? ''),
|
||||
@@ -1307,17 +1442,17 @@ const deliverBeforeText = computed(() =>
|
||||
);
|
||||
|
||||
const memberThanksText = computed(() =>
|
||||
fillI18nParams(t('pages-store.checkout.memberThanks'), { name: appDisplayName.value })
|
||||
t('pages-store.checkout.memberThanks', { name: appDisplayName.value })
|
||||
);
|
||||
|
||||
const subtotalWithPiecesText = computed(() =>
|
||||
fillI18nParams(t('pages-store.checkout.subtotalWithPieces'), {
|
||||
t('pages-store.checkout.subtotalWithPieces', {
|
||||
count: cartTotalPieceCount.value,
|
||||
})
|
||||
);
|
||||
|
||||
const subtotalOneLineText = computed(() =>
|
||||
fillI18nParams(t('pages-store.checkout.subtotalOneLine'), {
|
||||
t('pages-store.checkout.subtotalOneLine', {
|
||||
amount: displayPaidStr.value,
|
||||
})
|
||||
);
|
||||
@@ -1588,25 +1723,6 @@ function handleClose(merchantId?: string) {
|
||||
|
||||
<!-- 批量模式:多店铺列表 -->
|
||||
<view class="checkout-block" v-if="orderType === 'batch' && cartDataList.length > 0">
|
||||
<view class="checkout-section-label">{{ t('pages-store.checkout.confirmOrder') }}</view>
|
||||
<view v-if="showAppointmentEntry" class="checkout-card checkout-gutter checkout-batch-pill-card">
|
||||
<view class="checkout-confirm-band checkout-confirm-band--compact">
|
||||
<view @click.stop="toggleDeliveryTimeType(2)" class="checkout-date-pill">
|
||||
{{ deliveryPillText }}
|
||||
</view>
|
||||
<view class="checkout-confirm-band-right">
|
||||
<text class="checkout-confirm-band-line1">
|
||||
{{ cartTotalPieceCount }} {{ t('pages-user.cart.items') }} · ${{ displayGoodsAmountStr }}
|
||||
</text>
|
||||
<view v-if="deliveryMethod === 0" class="checkout-confirm-band-line2">
|
||||
<text>{{ t('pages-store.checkout.shippingFee') }}</text>
|
||||
<text v-if="showListDeliveryStrike" class="checkout-price-strike"> ${{ listDeliveryFeeStr }}</text>
|
||||
<text v-if="showListDeliveryStrike"> </text>
|
||||
<text class="checkout-batch-shipping-now">${{ displayDeliveryFeeStr }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="checkout-gutter">
|
||||
<view
|
||||
v-for="merchant in cartDataList"
|
||||
@@ -1620,7 +1736,7 @@ function handleClose(merchantId?: string) {
|
||||
:src="merchant.logo"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view>
|
||||
<view class="flex-1 min-w-0">
|
||||
<view class="text-28rpx lh-28rpx text-#333 font-500"
|
||||
>{{ merchant.merchantName }}</view
|
||||
>
|
||||
@@ -1630,6 +1746,17 @@ function handleClose(merchantId?: string) {
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="showAppointmentEntry"
|
||||
class="checkout-merchant-schedule-row"
|
||||
>
|
||||
<view
|
||||
class="checkout-date-pill"
|
||||
@click.stop="toggleDeliveryTimeType(2, merchant)"
|
||||
>
|
||||
{{ getMerchantPillText(merchant.id) }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<view class="checkout-merchant-body">
|
||||
@@ -2758,6 +2885,16 @@ $checkout-gutter: 32rpx;
|
||||
border-bottom: 1rpx solid $checkout-border;
|
||||
}
|
||||
|
||||
.checkout-merchant-schedule-row {
|
||||
padding: 20rpx 24rpx;
|
||||
border-bottom: 1rpx solid $checkout-border;
|
||||
}
|
||||
|
||||
.checkout-confirm-band-right--full {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.checkout-merchant-body {
|
||||
padding: 8rpx 24rpx 16rpx;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user