2 Commits

Author SHA1 Message Date
pcwl_yancheng f214a61735 修复bug 2026-06-22 14:24:08 +08:00
pcwl_yancheng 3fb5db48f2 修改 2026-06-18 21:04:03 +08:00
4 changed files with 214 additions and 229 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
"name" : "CHEFLINK delivery", "name" : "CHEFLINK delivery",
"appid" : "__UNI__06509BE", "appid" : "__UNI__06509BE",
"description" : "", "description" : "",
"versionName" : "3.2.9", "versionName" : "3.3.2",
"versionCode" : 329, "versionCode" : 332,
"transformPx" : false, "transformPx" : false,
/* 5+App */ /* 5+App */
"app-plus" : { "app-plus" : {
+55 -4
View File
@@ -1419,6 +1419,51 @@ const isUserMemberCheckout = computed(() => {
return false; return false;
}); });
function getCheckoutCartItemDiscountPrice(item: MerchantCartVo) {
return item.discountPrice ?? item.merchantDishVo?.discountPrice;
}
function getCheckoutCartItemOriginalPrice(item: MerchantCartVo) {
return item.originalPrice ?? item.merchantDishVo?.originalPrice;
}
function getCheckoutCartItemMemberPrice(item: MerchantCartVo) {
return item.memberPrice ?? item.merchantDishVo?.memberPrice;
}
function normalizeCheckoutPriceString(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 checkoutPriceToCentString(val: unknown) {
const normalized = normalizeCheckoutPriceString(val);
const [intPart, decPart = '00'] = normalized.split('.');
return `${intPart}${decPart}`.replace(/^0+(?=\d)/, '') || '0';
}
/** 与购物车列表一致:会员展示会员价,非会员展示折扣价 */
function getCheckoutLineDisplayPrice(item: MerchantCartVo) {
if (isUserMemberCheckout.value) {
return getCheckoutCartItemMemberPrice(item);
}
return getCheckoutCartItemDiscountPrice(item);
}
/** 与购物车列表一致:划线原价与当前展示价不一致时显示 */
function showCheckoutLineOriginalPrice(item: MerchantCartVo) {
const originalCent = checkoutPriceToCentString(getCheckoutCartItemOriginalPrice(item));
const currentCent =
isUserMemberCheckout.value && checkoutPriceToCentString(getCheckoutCartItemMemberPrice(item)) !== '0'
? checkoutPriceToCentString(getCheckoutCartItemMemberPrice(item))
: checkoutPriceToCentString(getCheckoutCartItemDiscountPrice(item));
return originalCent !== '0' && originalCent !== currentCent;
}
const cartTotalPieceCount = computed(() => { const cartTotalPieceCount = computed(() => {
if (orderType.value === 'batch') { if (orderType.value === 'batch') {
let n = 0; let n = 0;
@@ -1773,7 +1818,13 @@ function handleClose(merchantId?: string) {
</view> </view>
</view> </view>
</view> </view>
<view class="text-30rpx text-#333 font-500 shrink-0 ml-16rpx">${{ item.merchantDishVo?.discountPrice }}</view> <view class="checkout-order-line-price shrink-0 ml-16rpx text-right">
<view class="text-30rpx text-#333 font-500">${{ getCheckoutLineDisplayPrice(item) }}</view>
<view
v-if="showCheckoutLineOriginalPrice(item)"
class="text-24rpx text-#7D7D7D line-through mt-4rpx"
>${{ getCheckoutCartItemOriginalPrice(item) }}</view>
</view>
</view> </view>
</wd-collapse-item> </wd-collapse-item>
</wd-collapse> </wd-collapse>
@@ -1863,11 +1914,11 @@ function handleClose(merchantId?: string) {
<view class="flex items-center justify-between mt-12rpx"> <view class="flex items-center justify-between mt-12rpx">
<view class="flex items-center"> <view class="flex items-center">
<text class="text-[#333333] text-28rpx font-500" <text class="text-[#333333] text-28rpx font-500"
>${{ item.merchantDishVo?.discountPrice }}</text> >${{ getCheckoutLineDisplayPrice(item) }}</text>
<text <text
v-if="item.merchantDishVo?.originalPrice && item.merchantDishVo?.originalPrice !== item.merchantDishVo?.discountPrice" v-if="showCheckoutLineOriginalPrice(item)"
class="text-[#7D7D7D] text-22rpx line-through ml-8rpx" class="text-[#7D7D7D] text-22rpx line-through ml-8rpx"
>${{ item.merchantDishVo?.originalPrice }}</text> >${{ getCheckoutCartItemOriginalPrice(item) }}</text>
</view> </view>
<view class="text-[#7D7D7D] text-22rpx"> <view class="text-[#7D7D7D] text-22rpx">
x{{ item.count }} x{{ item.count }}
@@ -44,12 +44,12 @@ const { t, locale } = useI18n();
/** 首页运营图:按语言切换(中文 / 英文) */ /** 首页运营图:按语言切换(中文 / 英文) */
const HOME_PROMO_BANNERS = { const HOME_PROMO_BANNERS = {
memberUpgrade: { memberUpgrade: {
zh: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/05/d4a0d40503ac4206a0387af1ab869de6.png', zh: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/edb822ef721642b39e52f19b2e5df949.png',
en: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/05/c01f1664626d417a9a7ca6165a20f06f.png', en: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/6a8a43575f70425eb6b8071633708531.png',
}, },
deliveryTime: { deliveryTime: {
zh: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/03/c5673a8874594755bdde7ed7fcbd1982.jpg', zh: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/8a0fcfab0a6e4ff29fdecdc41e01ebdb.png',
en: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/03/1da02f1e0af34cea91a4f643247176be.png', en: 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/99c6b38e7f1e4337baee0ec5e42c76ba.png',
}, },
} as const } as const
@@ -355,30 +355,21 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
</script> </script>
<template> <template>
<view <view class="home-page-root" :style="[
class="home-page-root"
:style="[
{ {
height: configStore.windowHeight + 'px', height: configStore.windowHeight + 'px',
}, },
]" ]">
> <z-paging @onRefresh="onRefresh" ref="paging" v-model="dataList" :auto="false" @query="queryList"
<z-paging @onRefresh="onRefresh" ref="paging" v-model="dataList" :auto="false" @query="queryList" @scroll="onPageScroll" :refresher-enabled="true" :auto-show-back-to-top="false"> @scroll="onPageScroll" :refresher-enabled="true" :auto-show-back-to-top="false">
<template #top> <template #top>
<status-bar /> <status-bar />
<home-top-header <home-top-header :app-name="Config.appName" :location-text="userStore.userLocation.location"
:app-name="Config.appName" :cart-badge-total="cartBadgeTotal" :is-login="userStore.isLogin"
:location-text="userStore.userLocation.location" @click-location="navigateTo('/pages-user/pages/search-address/index')" @click-cart="goCart" />
:cart-badge-total="cartBadgeTotal"
:is-login="userStore.isLogin"
@click-location="navigateTo('/pages-user/pages/search-address/index')"
@click-cart="goCart"
/>
</template> </template>
<view <view class="animate-in fade-in animate-ease-out animate-duration-300"
class="animate-in fade-in animate-ease-out animate-duration-300" v-show="loading && featuredList.length === 0">
v-show="loading && featuredList.length === 0"
>
<home-skeleton /> <home-skeleton />
</view> </view>
<view class="px-24rpx pt-12rpx pb-8rpx"> <view class="px-24rpx pt-12rpx pb-8rpx">
@@ -390,56 +381,29 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
</view> </view>
</view> </view>
<!-- 轮播图/app/marketActivity/list --> <!-- 轮播图/app/marketActivity/list -->
<swiper <swiper v-if="swiperList.length > 0" class="card-swiper" :circular="swiperList.length > 1"
v-if="swiperList.length > 0" :autoplay="swiperList.length > 1">
class="card-swiper" <swiper-item v-for="(item, sIdx) in swiperList" :key="item.id ?? sIdx" @click="handleClickSwiper(item)">
:circular="swiperList.length > 1" <image :src="item.activityImage" class="swiper-item-content w-full h-100%" mode="scaleToFill"></image>
:autoplay="swiperList.length > 1"
>
<swiper-item
v-for="(item, sIdx) in swiperList"
:key="item.id ?? sIdx"
@click="handleClickSwiper(item)"
>
<image
:src="item.activityImage"
class="swiper-item-content w-full h-100%"
mode="scaleToFill"
></image>
</swiper-item> </swiper-item>
</swiper> </swiper>
<!-- 快捷入口固定五项跳转精选菜品专题页 --> <!-- 快捷入口固定五项跳转精选菜品专题页 -->
<tabs-type <tabs-type :current-id="currentCategory" class="mt-28rpx mb-24rpx home-tabs-quick"
:current-id="currentCategory" @change-type="tabsTypeChange" />
class="mt-28rpx mb-24rpx home-tabs-quick"
@change-type="tabsTypeChange"
/>
<view <view class="home-promo-block px-24rpx">
class="home-member-banner" <image :src="memberUpgradeBannerSrc" class="home-promo-banner-img" mode="widthFix" />
:style="{ backgroundImage: `url(${memberUpgradeBannerSrc})` }" <view class="home-promo-actions">
> <view class="home-promo-btn" @click="navigateTo('/pages-user/pages/member/index')">
<view class="home-member-banner__actions"> <text class="home-promo-btn-text">{{ t('pages.home.open-member') }}</text>
<view
class="home-member-banner__btn"
@click="navigateTo('/pages-user/pages/member/index')"
>
<text class="home-member-banner__btn-text">{{ t('pages.home.open-member') }}</text>
</view> </view>
<view <view class="home-promo-btn" @click="navigateTo('/pages-user/pages/recharge/index')">
class="home-member-banner__btn" <text class="home-promo-btn-text">{{ t('pages.home.recharge-now') }}</text>
@click="navigateTo('/pages-user/pages/recharge/index')"
>
<text class="home-member-banner__btn-text">{{ t('pages.home.recharge-now') }}</text>
</view> </view>
</view> </view>
<image :src="deliveryTimeBannerSrc" class="home-promo-banner-img" mode="widthFix" />
</view> </view>
<image
:src="deliveryTimeBannerSrc"
class="w-100% h-[200rpx] rounded-24rpx mt-4rpx"
mode="widthFix"
/>
<!-- 精选商家和附近商家 --> <!-- 精选商家和附近商家 -->
<view class="animate-in fade-in animate-ease-in animate-duration-300" v-if="isShowMerchant"> <view class="animate-in fade-in animate-ease-in animate-duration-300" v-if="isShowMerchant">
<!-- Featured on ChefLink 精选商家浅底 + 横向卡片对齐设计稿 --> <!-- Featured on ChefLink 精选商家浅底 + 横向卡片对齐设计稿 -->
@@ -461,55 +425,26 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
<!-- List 精选菜品瀑布流浅底 + 白卡片 + 阴影结构对齐设计稿 --> <!-- List 精选菜品瀑布流浅底 + 白卡片 + 阴影结构对齐设计稿 -->
<view class="featured-dishes-section mt-36rpx px-24rpx pb-40rpx"> <view class="featured-dishes-section mt-36rpx px-24rpx pb-40rpx">
<view class="mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a " <view class="mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a ">{{ t('pages.home.featured-dishes') }}</view>
>{{ t('pages.home.featured-dishes') }}</view>
<view class="waterfall-row flex gap-16rpx items-start"> <view class="waterfall-row flex gap-16rpx items-start">
<view <view v-for="(col, colIndex) in featuredDishColumns" :key="colIndex"
v-for="(col, colIndex) in featuredDishColumns" class="waterfall-col flex-1 min-w-0 flex flex-col gap-16rpx">
:key="colIndex" <view v-for="item in col" :key="item.id || String(item.merchantId) + '-' + item.dishName"
class="waterfall-col flex-1 min-w-0 flex flex-col gap-16rpx" @click="navigateToDishes(item)" class="featured-dish-card w-full">
>
<view
v-for="item in col"
:key="item.id || String(item.merchantId) + '-' + item.dishName"
@click="navigateToDishes(item)"
class="featured-dish-card w-full"
>
<view class="featured-dish-image"> <view class="featured-dish-image">
<view v-if="item.isNew == 1" class="dish-new-ribbon"> <view v-if="item.isNew == 1" class="dish-new-ribbon">
<text class="dish-new-ribbon__text">NEW</text> <text class="dish-new-ribbon__text">NEW</text>
</view> </view>
<image <image :src="item?.dishImage?.split(',')[0]" mode="aspectFill" class="featured-dish-img" />
:src="item?.dishImage?.split(',')[0]" <view v-if="isSoldOutStock(item?.stock)" class="featured-dish-sold-dim" />
mode="aspectFill" <image v-if="isSoldOutStock(item?.stock)" src="/static/app/images/SoldOut.png" mode="aspectFill"
class="featured-dish-img" class="featured-dish-sold-overlay" />
/> <view @click.stop="handleDishCollectionClick(item)"
<view class="featured-dish-collect w-56rpx h-56rpx absolute z-4 top-12rpx right-12rpx center">
v-if="isSoldOutStock(item?.stock)" <image v-if="!item.isCollect" src="@img-store/1334.png" mode="aspectFill"
class="featured-dish-sold-dim" class="w-44rpx h-44rpx featured-dish-collect-icon" />
/> <image v-else src="@img-store/1337.png" mode="aspectFill"
<image class="w-44rpx h-44rpx featured-dish-collect-icon" />
v-if="isSoldOutStock(item?.stock)"
src="/static/app/images/SoldOut.png"
mode="aspectFill"
class="featured-dish-sold-overlay"
/>
<view
@click.stop="handleDishCollectionClick(item)"
class="featured-dish-collect w-56rpx h-56rpx absolute z-4 top-12rpx right-12rpx center"
>
<image
v-if="!item.isCollect"
src="@img-store/1334.png"
mode="aspectFill"
class="w-44rpx h-44rpx featured-dish-collect-icon"
/>
<image
v-else
src="@img-store/1337.png"
mode="aspectFill"
class="w-44rpx h-44rpx featured-dish-collect-icon"
/>
</view> </view>
</view> </view>
<view class="featured-dish-body"> <view class="featured-dish-body">
@@ -521,30 +456,24 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
class="featured-dish-original" class="featured-dish-original"
>US${{ item?.originalPrice }}</text> --> >US${{ item?.originalPrice }}</text> -->
</view> </view>
<text class="featured-dish-sales shrink-0">{{ t('pages-store.store.sales') }}: {{ formatSalesCount(item?.salesCount) }}</text> <text class="featured-dish-sales shrink-0">{{ t('pages-store.store.sales') }}: {{
formatSalesCount(item?.salesCount) }}</text>
</view> </view>
<view class="featured-dish-title line-clamp-2 mb-16rpx"> <view class="featured-dish-title line-clamp-2 mb-16rpx">
{{ item?.dishName }} {{ item?.dishName }}
</view> </view>
<view class="flex items-center justify-between gap-12rpx"> <view class="flex items-center justify-between gap-12rpx">
<view <view v-if="Number(item?.memberPrice) > 0" class="featured-dish-member shrink min-w-0">
v-if="Number(item?.memberPrice) > 0" <text class="featured-dish-member-inner">{{ t('pages-store.store.members') }}: US${{
class="featured-dish-member shrink min-w-0" item?.memberPrice
> }}</text>
<text class="featured-dish-member-inner">{{ t('pages-store.store.members') }}: US${{ item?.memberPrice }}</text>
</view> </view>
<view v-else class="flex-1 min-w-0"></view> <view v-else class="flex-1 min-w-0"></view>
<view class="featured-dish-add shrink-0"> <view class="featured-dish-add shrink-0">
<image <image src="/static/app/images/add_cart.png" class="featured-dish-add__icon" />
src="/static/app/images/add_cart.png"
class="featured-dish-add__icon"
/>
</view> </view>
</view> </view>
<view <view v-if="getDishPromoLabel(item as Record<string, unknown>)" class="featured-dish-promo mt-16rpx">
v-if="getDishPromoLabel(item as Record<string, unknown>)"
class="featured-dish-promo mt-16rpx"
>
<text class="featured-dish-promo-text">{{ getDishPromoLabel(item as Record<string, unknown>) }}</text> <text class="featured-dish-promo-text">{{ getDishPromoLabel(item as Record<string, unknown>) }}</text>
</view> </view>
</view> </view>
@@ -558,7 +487,8 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
</template> </template>
</z-paging> </z-paging>
<!-- 回到顶部购物车已并入顶栏不再使用底部浮条 --> <!-- 回到顶部购物车已并入顶栏不再使用底部浮条 -->
<view v-if="showBackToTop" @click="scrollToTop" class="home-back-top fixed left-30rpx w-88rpx h-88rpx bg-#14181B rounded-50% center shadow-lg"> <view v-if="showBackToTop" @click="scrollToTop"
class="home-back-top fixed left-30rpx w-88rpx h-88rpx bg-#14181B rounded-50% center shadow-lg">
<image src="@img/chef/119.png" class="w-40rpx h-40rpx shrink-0 rotate-180"></image> <image src="@img/chef/119.png" class="w-40rpx h-40rpx shrink-0 rotate-180"></image>
</view> </view>
</view> </view>
@@ -578,28 +508,29 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
padding-right: 8rpx; padding-right: 8rpx;
} }
.home-member-banner { .home-promo-block {
position: relative; display: flex;
width: 100%; flex-direction: column;
height: 550rpx; gap: 24rpx;
background-size: 100% 100%; margin-top: 8rpx;
background-position: center; margin-bottom: 8rpx;
background-repeat: no-repeat;
} }
.home-member-banner__actions { .home-promo-banner-img {
position: absolute; display: block;
left: 0; width: 100%;
right: 0; border-radius: 24rpx;
bottom: 5rpx; }
padding: 0 40rpx;
.home-promo-actions {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 24rpx; gap: 24rpx;
padding: 0 8rpx;
} }
.home-member-banner__btn { .home-promo-btn {
flex: 1; flex: 1;
height: 72rpx; height: 72rpx;
padding: 0 24rpx; padding: 0 24rpx;
@@ -610,7 +541,7 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
justify-content: center; justify-content: center;
} }
.home-member-banner__btn-text { .home-promo-btn-text {
font-size: 28rpx; font-size: 28rpx;
font-weight: 600; font-weight: 600;
color: #ffffff; color: #ffffff;
@@ -629,6 +560,7 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
.card-swiper { .card-swiper {
height: 420rpx; height: 420rpx;
} }
.swiper-item-content { .swiper-item-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -127,8 +127,10 @@ const isUserMember = computed(()=> {
function handleTabClick(item: Tab) { function handleTabClick(item: Tab) {
switch (item.code) { switch (item.code) {
case "inviteFriends": { case "inviteFriends": {
// R.both(checkNeedLogin, R.pipe(() => emits("inviteUser"), R.T))(item.isLogin) R.both(
emits("inviteUser"); checkNeedLogin,
R.pipe(() => emits("inviteUser"), R.T)
)(item.isLogin);
break; break;
} }
case "support": { case "support": {