This commit is contained in:
2026-04-14 15:02:00 +08:00
parent ec9282a64f
commit d2077f5844
23 changed files with 1551 additions and 428 deletions
@@ -25,9 +25,9 @@ defineExpose({
@close="handleClose"
>
<view class="px-30rpx text-#333 text-center pt-48rpx pb-60rpx">
<view class="text-40rpx lh-40rpx font-500">Delete shopping cart?</view>
<view class="text-40rpx lh-40rpx font-500">{{ t("pages-user.cart.removeCartTitle") }}</view>
<view class="text-28rpx lh-28rpx mt-36rpx">
Are you sure you want to delete the shopping cart?
{{ t("pages-user.cart.removeCartDesc") }}
</view>
<view class="mt-70rpx">
<wd-button @click="handleDelete" custom-class="!h-108rpx !bg-14181B !text-36rpx !lh-108rpx !text-#fff !rounded-16rpx" block>
@@ -4,6 +4,20 @@ const emit = defineEmits(['confirm','close'])
const show = ref(false);
const goodsName = ref('');
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 removeProductDescText = computed(() =>
fillI18nParams(t("pages-user.cart.removeProductDesc"), { name: goodsName.value })
);
function onOpen(title: string) {
if (title) {
goodsName.value = title;
@@ -31,9 +45,9 @@ defineExpose({
@close="handleClose"
>
<view class="px-30rpx text-#333 text-center pt-48rpx pb-60rpx">
<view class="text-40rpx lh-40rpx font-500">Remove the product?</view>
<view class="text-40rpx lh-40rpx font-500">{{ t("pages-user.cart.removeProductTitle") }}</view>
<view class="text-28rpx lh-28rpx mt-36rpx">
Are you sure you want to remove {{ goodsName }} from your shopping cart?
{{ removeProductDescText }}
</view>
<view class="mt-70rpx">
<wd-button @click="confirmRemove" custom-class="!h-108rpx !bg-14181B !text-36rpx !lh-108rpx !text-#fff !rounded-16rpx" block>
+87 -35
View File
@@ -38,6 +38,9 @@ interface CartItem {
merchantDishVo: any;
sideDishList: any[];
checked: boolean;
discountPrice: number;
originalPrice: number;
memberPrice: number;
}
interface CartMerchant {
@@ -66,6 +69,59 @@ const selectedCartItems = computed(() => {
});
const selectedCount = computed(() => selectedCartItems.value.length);
const appDisplayName = computed(() => {
const name = String((Config as any)?.appName ?? "").trim();
return name || "CHEFLINK";
});
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;
}
function normalizePriceString(val: unknown) {
const s = String(val ?? "").trim();
if (!/^\d+(\.\d+)?$/.test(s)) return "0";
const [intRaw, decRaw = ""] = s.split(".");
const intPart = intRaw.replace(/^0+(?=\d)/, "") || "0";
const decPart = decRaw.slice(0, 2).padEnd(2, "0");
return `${intPart}.${decPart}`;
}
function priceToCentString(val: unknown) {
const normalized = normalizePriceString(val);
const [intPart, decPart = "00"] = normalized.split(".");
return `${intPart}${decPart}`.replace(/^0+(?=\d)/, "") || "0";
}
function formatPriceForView(val: unknown) {
const normalized = normalizePriceString(val);
return normalized.replace(/\.00$/, "").replace(/(\.\d)0$/, "$1");
}
function getCartLineDisplayPrice(item: CartItem) {
// const memberCent = priceToCentString(item?.memberPrice);
// console.log('memberCent', memberCent);
console.log('item?.memberPrice', item?.memberPrice);
console.log(userStore.userInfo.userMembershipVo, 'userStore.isMember');
if (userStore.userInfo.userMembershipVo!==null) {
return item?.memberPrice;
}
return item?.discountPrice;
}
function showCartLineOriginalPrice(item: CartItem) {
const originalCent = priceToCentString(item?.originalPrice);
const currentCent = userStore.userInfo.userMembershipVo!==null && priceToCentString(item?.memberPrice) !== "0"
? priceToCentString(item?.memberPrice)
: priceToCentString(item?.discountPrice);
return originalCent !== "0" && originalCent !== currentCent;
}
const priceData = ref<
Record<string, any> & {
@@ -100,6 +156,17 @@ const selectedGoodsSubtotal = computed(() => {
return total.toFixed(2);
});
const deliveryUnifiedText = computed(() =>
fillI18nParams(t("pages-user.cart.deliveryUnified"), { name: appDisplayName.value })
);
const itemsTotalWithPriceText = computed(() =>
fillI18nParams(t("pages-user.cart.itemsTotalWithPrice"), {
count: priceData.value.totalQuantity ?? selectedItemQty.value,
amount: selectedGoodsSubtotal.value,
})
);
/** 与地址页、结算页一致:展示用户已保存预约日,无则展示今天(选时间会跳转 reservation-time 并写回接口) */
const deliveryScheduleLabel = computed(() => {
const ap = userStore.appointmentTime as any;
@@ -210,7 +277,7 @@ function confirmRemove() {
getCartList();
uni.showToast({
icon: "none",
title: "删除成功",
title: t("toast.deleteSuccess"),
});
});
}
@@ -324,23 +391,18 @@ function scheduleCartUpdate(item: CartItem, targetValue: number) {
pendingUpdateTimers.set(key, timer);
}
function normalizeInputNumberValue(payload: any): number {
if (typeof payload === "number") return payload;
if (payload && typeof payload === "object") {
if (typeof payload.detail?.value === "number")
return payload.detail.value;
if (typeof payload.value === "number") return payload.value;
}
const numeric = Number(payload);
return Number.isNaN(numeric) ? 0 : numeric;
/** wd-input-number change 为 { value },兼容 detail.value */
function normalizeInputNumberValue(payload: unknown): number {
const raw =
typeof payload === "number"
? payload
: (payload as any)?.value ?? (payload as any)?.detail?.value ?? payload;
const n = Number(raw);
return Number.isFinite(n) ? n : 0;
}
const delItemData = ref<CartItem | null>(null);
function onItemQtyChange(item: CartItem) {
return (payload: unknown) => handleQuantityChange(payload, item);
}
function handleQuantityChange(payload: any, item: CartItem) {
if (!item) return;
const nextValue = normalizeInputNumberValue(payload);
@@ -593,9 +655,7 @@ async function getCartList() {
t("pages-user.cart.localDelivery")
}}</text>
<text class="cart-delivery-sub">{{
t("pages-user.cart.deliveryUnified", {
name: Config.appName,
})
deliveryUnifiedText
}}</text>
</view>
</view>
@@ -607,11 +667,7 @@ async function getCartList() {
</view>
<view class="cart-price-summary">
<text class="cart-summary-line1">{{
t("pages-user.cart.itemsTotalWithPrice", {
count:
priceData.totalQuantity ?? selectedItemQty,
amount: selectedGoodsSubtotal,
})
itemsTotalWithPriceText
}}</text>
<view
v-if="
@@ -731,26 +787,22 @@ async function getCartList() {
<text class="mr-6rpx">{{
dish?.merchantSideDishItemVo?.name || ""
}}</text>
<text
<!-- <text
v-if="dish?.merchantSideDishItemVo?.price"
class="text-[#00A76D] mr-10rpx"
>+${{ dish?.merchantSideDishItemVo?.price }}</text
>
>${{ dish?.merchantSideDishItemVo?.price }}</text
> -->
</view>
</view>
<view class="cart-line-price-row">
<view class="flex items-baseline flex-wrap">
<text class="cart-line-price"
>${{ item.merchantDishVo?.discountPrice }}</text
>${{ getCartLineDisplayPrice(item) }}</text
>
<text
v-if="
item.merchantDishVo?.originalPrice &&
item.merchantDishVo?.originalPrice !==
item.merchantDishVo?.discountPrice
"
v-if="showCartLineOriginalPrice(item)"
class="cart-line-price-old"
>${{ item.merchantDishVo?.originalPrice }}</text
>${{ item?.originalPrice }}</text
>
</view>
<wd-input-number
@@ -761,7 +813,7 @@ async function getCartList() {
:input-width="72"
button-size="52"
custom-class="cart-qty-input"
@change="onItemQtyChange(item)"
@change="(e) => handleQuantityChange(e, item)"
/>
</view>
</view>
@@ -809,12 +861,12 @@ async function getCartList() {
<template v-else>
<view class="flex flex-col items-center justify-center py-100rpx">
<image src="@img/chef/100.png" class="w-318rpx h-318rpx" />
<text class="text-32rpx text-[#7D7D7D]">Your cart is empty</text>
<text class="text-32rpx text-[#7D7D7D]">{{ t("pages-user.cart.emptyTitle") }}</text>
<view
class="mt-60rpx w-400rpx h-88rpx rounded-16rpx bg-[#14181B] flex items-center justify-center"
@click="goToHome"
>
<text class="text-30rpx text-white">Explore Restaurants</text>
<text class="text-30rpx text-white">{{ t("pages-user.cart.emptyAction") }}</text>
</view>
</view>
</template>
+25 -19
View File
@@ -115,20 +115,14 @@ function scheduleCartUpdate(item: MerchantCartVo, targetValue: number) {
pendingUpdateTimers.set(key, timer)
}
function normalizeInputNumberValue(payload: any): number {
if (typeof payload === 'number') {
return payload
}
if (payload && typeof payload === 'object') {
if (typeof payload.detail?.value === 'number') {
return payload.detail.value
}
if (typeof payload.value === 'number') {
return payload.value
}
}
const numeric = Number(payload)
return Number.isNaN(numeric) ? 0 : numeric
/** wd-input-number change 为 { value },兼容 detail.value */
function normalizeInputNumberValue(payload: unknown): number {
const raw =
typeof payload === "number"
? payload
: (payload as any)?.value ?? (payload as any)?.detail?.value ?? payload;
const n = Number(raw);
return Number.isFinite(n) ? n : 0;
}
const delItemData = ref<MerchantCartVo | null>(null)
@@ -247,6 +241,18 @@ function appMerchantCartCalculateSavings() {
})
}
function getCartItemDiscountPrice(item: MerchantCartVo) {
return item.discountPrice ?? item.merchantDishVo?.discountPrice
}
function getCartItemOriginalPrice(item: MerchantCartVo) {
return item.originalPrice ?? item.merchantDishVo?.originalPrice
}
function getCartItemMemberPrice(item: MerchantCartVo) {
return item.memberPrice ?? item.merchantDishVo?.memberPrice
}
function navigateTo(url: string) {
uni.navigateTo({ url })
}
@@ -301,21 +307,21 @@ function navigateTo(url: string) {
<view class="price-row flex items-center mt-18rpx">
<text class="current-price text-[#333333] text-30rpx font-normal"
>${{ item.merchantDishVo.discountPrice }}</text
>${{ getCartItemDiscountPrice(item) }}</text
>
<text
v-if="item.merchantDishVo.originalPrice"
v-if="getCartItemOriginalPrice(item)"
class="original-price text-[#7D7D7D] text-24rpx font-normal line-through ml-10rpx"
>${{ item.merchantDishVo.originalPrice }}</text
>${{ getCartItemOriginalPrice(item) }}</text
>
<!-- 会员价标签 -->
<view
v-if="Number(item.merchantDishVo.memberPrice) > 0"
v-if="Number(getCartItemMemberPrice(item)) > 0"
class="member-price-tag ml-10rpx center pl-16rpx pr-6rpx pb-2rpx"
>
<text class="text-[#FBE3C3] text-20rpx"
>{{ t('pages-store.store.members') }}: ${{ item.merchantDishVo.memberPrice }}</text
>{{ t('pages-store.store.members') }}: ${{ getCartItemMemberPrice(item) }}</text
>
</view>
</view>