修改样式

This commit is contained in:
2026-06-05 15:03:32 +08:00
parent 7d891c9f7b
commit f2cde43bf4
58 changed files with 2762 additions and 939 deletions
+236 -99
View File
@@ -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;
}