@@ -2,7 +2,7 @@
import { ref , computed } from 'vue'
import { useI18n } from 'vue-i18n'
import useEventEmit from "@/hooks/useEventEmit" ;
import { CollectionType , EventEnum } from "@/constant/enums" ;
import { CollectionType , EventEnum } from "@/constant/enums" ;
import { useConfigStore , useUserStore } from "@/store" ;
import Config from '@/config/index'
import { debounce } from 'throttle-debounce'
@@ -26,7 +26,7 @@ import {
appRecipeCategoryListGet
} from "@/service" ;
import usePage from "@/hooks/usePage" ;
import { getFeaturedDishList } from "@/pages-store/service" ;
import { getFeaturedDishList } from "@/pages-store/service" ;
import {
buildQuickTopicUrl ,
isQuickTopicSlug ,
@@ -44,12 +44,12 @@ const { t, locale } = useI18n();
/** 首页运营图:按语言切换(中文 / 英文) */
const HOME _PROMO _BANNERS = {
memberUpgrade : {
zh : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/05/d4a0d40503ac4206a0387af1ab869de6 .png' ,
en : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/05/c01f1664626d417a9a7ca6165a20f06f .png' ,
zh : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/edb822ef721642b39e52f19b2e5df949 .png' ,
en : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/6a8a43575f70425eb6b8071633708531 .png' ,
} ,
deliveryTime : {
zh : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/03/c5673a8874594755bdde7ed7fcbd1982.jp g' ,
en : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/03/1da02f1e0af34cea91a4f6432471 76be .png' ,
zh : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/8a0fcfab0a6e4ff29fdecdc41e01ebdb.pn g' ,
en : 'https://www.howhowfresh.com/minio/ruoyi/2026/06/18/99c6b38e7f1e4337baee0ec5e42c 76ba .png' ,
} ,
} as const
@@ -84,7 +84,7 @@ function getDishPromoLabel(item: Record<string, unknown>): string {
function navigateTo ( url : string ) {
if ( userStore . checkLogin ( ) ) {
if ( userStore . checkLogin ( ) ) {
uni . navigateTo ( {
url ,
} ) ;
@@ -95,7 +95,7 @@ const swiperList = ref<any[]>([])
async function initData ( ) {
// 只在首次加载时显示骨架屏,避免切换时的白屏
if ( featuredList . value . length === 0 ) {
if ( featuredList . value . length === 0 ) {
loading . value = true
}
getAppMarketActivityList ( )
@@ -168,9 +168,9 @@ function getAppFeaturedList() {
lat : userStore . userLocation . latitude ,
lng : userStore . userLocation . longitude ,
}
} ) . then ( res => {
} ) . then ( res => {
featuredList . value = res . data || [ ]
} ) . finally ( ( ) => {
} ) . finally ( ( ) => {
loading . value = false
} )
}
@@ -183,7 +183,7 @@ function getAppNearbyListPost() {
lat : userStore . userLocation . latitude ,
lng : userStore . userLocation . longitude ,
}
} ) . then ( res => {
} ) . then ( res => {
nearbyList . value = res . data || [ ]
} )
}
@@ -200,7 +200,7 @@ function toggleDiscount(value: number) {
discount . value = value ;
// paging.value.refresh()
}
const { paging , dataList , queryList } = usePage ( getList )
const { paging , dataList , queryList } = usePage ( getList )
function getList ( pageNum : number , pageSize : number ) {
return new Promise ( resolve => {
getFeaturedDishList ( {
@@ -208,7 +208,7 @@ function getList(pageNum: number, pageSize: number) {
pageSize ,
} ) . then ( res => {
console . log ( '查询精选菜品列表' , res )
resolve ( { rows : res . rows } )
resolve ( { rows : res . rows } )
} )
} )
}
@@ -263,8 +263,8 @@ function tabsTypeChange(id: string | number) {
}
// 是否展示精选商家和附近商家 true 显示 false隐藏
const isShowMerchant = computed ( ( ) => {
if ( ! selfPickup . value && ! discount . value && ! props . scoreRange && ! props . price && ! currentCategory . value ) {
const isShowMerchant = computed ( ( ) => {
if ( ! selfPickup . value && ! discount . value && ! props . scoreRange && ! props . price && ! currentCategory . value ) {
return true // 没有筛选条件时显示
} else {
return true // 有筛选条件时隐藏
@@ -321,9 +321,9 @@ function handleClickSwiper(item: any) {
case 5 : // 充值活动
navigateTo ( '/pages-user/pages/recharge/activity-detail?id=' + item . id )
break
// case 4:
// navigateTo('/pages/ai/chat/index')
// break
// case 4:
// navigateTo('/pages/ai/chat/index')
// break
}
}
@@ -334,19 +334,19 @@ function navigateToDishes(item: any) {
}
// 收藏菜品
function handleDishCollectionClick ( item : any ) {
debouncedEmit ( item . isCollect , item . id , CollectionType . DISH , ( ) => {
debouncedEmit ( item . isCollect , item . id , CollectionType . DISH , ( ) => {
item . isCollect = ! item . isCollect
} )
}
// 防抖处理函数
const debouncedEmit = debounce ( 1300 , ( isCollected : boolean , id : string , type : CollectionType , callback : ( ) => void ) => {
const debouncedEmit = debounce ( 1300 , ( isCollected : boolean , id : string , type : CollectionType , callback : ( ) => void ) => {
// 收藏接口
appCollectCollectPost ( {
body : {
targetId : id ,
targetType : type
}
} ) . then ( res => {
} ) . then ( res => {
callback ( )
} )
} , {
@@ -355,210 +355,140 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
< / script >
< template >
< view
class = "home-page-root"
: style = "[
{
height: configStore.windowHeight + 'px',
},
]"
>
< z-paging @onRefresh ="onRefresh" ref = "paging" v-model = "dataList" :auto="false" @query="queryList" @scroll="onPageScroll" :refresher-enabled="true" :auto-show-back-to-top="false" >
< template # top >
< status-bar / >
< home-top-header
:app-name = "Config.appName"
:location-text = "userStore.userLocation.location "
:cart-badge-total = "cartBadgeTotal"
:is-login = "userStore.isLogin"
@ click -location = " navigateTo ( ' / pages -user / pages / search -address / index ' ) "
@ click -cart = " goCart "
/ >
< / template >
< view
class = "animate-in fade-in animate-ease-out animate-duration-300"
v-show = "loading && featuredList.length === 0"
>
< home-skeleton / >
< view class = "home-page-root" : style = "[
{
height: configStore.windowHeight + 'px',
},
]" >
< z-paging @onRefresh ="onRefresh" ref = "paging" v-model = "dataList" :auto="false" @query="queryList"
@scroll ="onPageScroll" :refresher-enabled = "true" :auto-show-back-to-top = "false" >
< template # top >
< status-bar / >
< home-top-header :app-name = "Config.appName" :location-text = "userStore.userLocation.location"
:cart-badge-total = "cartBadgeTotal" :is-login = "userStore.isLogin"
@ click -location = " navigateTo ( ' / pages -user / pages / search -address / index ' ) " @ click -cart = " goCart " / >
< / template >
< view class = "animate-in fade-in animate-ease-out animate-duration-300 "
v-show = "loading && featuredList.length === 0" >
< home -skeleton / >
< / view >
< view class = "px-24rpx pt-12rpx pb-8rpx" >
< search / >
< msg-box / >
<!-- 分类标签 -- >
< view class = "mt-28rpx" v-if = "appMerchantLabelList.length > 0" >
< class -bullet :categories = "appMerchantLabelList" @itemClick ="handleItemClick" / >
< / view >
< view class = "px-24rpx pt-12rpx pb-8rpx" >
< search / >
< msg-box / >
<!-- 分类标签 -- >
< view class = "mt-28rpx" v-if = "appMerchantLabelList.length > 0 ">
< class -bullet :categories = "appMerchantLabelList" @itemClick ="handleItemClick" / >
< / view >
<!-- 轮播图 : / app / marketActivity / list -- >
< swiper v-if = "swiperList.length > 0" class="card-swiper" :circular="swiperList.length > 1"
: 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 >
<!-- 快捷入口 ( 固定五项 , 跳转精选菜品专题页 ) -- >
< tabs-type :current-id = "currentCategory" class = "mt-28rpx mb-24rpx home-tabs-quick"
@ change -type = " tabsTypeChange " / >
< view class = "home-promo-block px-24rpx" >
< image :src = "memberUpgradeBannerSrc" class = "home-promo-banner-img" mode = "widthFix" / >
< view class = "home-promo-actions" >
< view class = "home-promo-btn" @click ="navigateTo('/pages-user/pages/member/index')" >
< text class = "home-promo-btn-text" > { { t ( 'pages.home.open-member' ) } } < / text >
< / view >
< view class = "home-promo-btn" @click ="navigateTo('/pages-user/pages/recharge/index')" >
< text class = "home-promo-btn-text" > { { t ( 'pages.home.recharge-now' ) } } < / text >
< / view >
< / view >
<!-- 轮播图 : / app / marketActivity / list -- >
< swiper
v-if = "swiperList.length > 0"
class = "card-swiper"
: circular = "swiperList.length > 1"
: 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 >
<!-- 快捷入口 ( 固定五项 , 跳转精选菜品专题页 ) -- >
< tabs-type
:current-id = "currentCategory"
class = "mt-28rpx mb-24rpx home-tabs-quick"
@ change -type = " tabsTypeChange "
/ >
< view
class = "home-member-banner"
: style = "{ backgroundImage: `url(${memberUpgradeBannerSrc})` }"
>
< view class = "home-member-banner__actions" >
< 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
class = "home-member-banner__btn"
@click ="navigateTo('/pages-user/pages/recharge/index')"
>
< text class = "home-member-banner__btn-text" > { { t ( 'pages.home.recharge-now' ) } } < / text >
< / view >
< / 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" >
< ! - - Featured on ChefLink 精选商家 ( 浅底 + 横向卡片 , 对齐设计稿 ) - - >
< view v-if = "featuredList.length > 0" class="featured-merchants-section mt-36rpx px-24rpx pt-8rpx pb-16rpx" >
< view class = "mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a" >
{ { t ( 'pages.home.featured-on' ) } }
< / view >
< featured-on :list = "featuredList" / >
< / view >
<!-- Nearby Merchants 附近商家 -- >
< view v-if = "nearbyList.length > 0" class="nearby-merchants-block mt-36rpx px-24rpx pb-16rpx" >
< view class = "mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a " >
{ { t ( 'pages.home.nearby-merchants' ) } }
< / view >
< nearby-merchants :list = "nearbyList" / >
< image :src = "deliveryTimeBannerSrc" class = "home-promo-banner-img" mode = "widthFix" / >
< / view >
<!-- 精选商家和附近商家 -- >
< view class = "animate-in fade-in animate-ease-in animate-duration-300" v-if = "isShowMerchant" >
< ! - - Featured on ChefLink 精选商家 ( 浅底 + 横向卡片 , 对齐设计稿 ) - - >
< view v-if = "featuredList.length > 0" class="featured-merchants-section mt-36rpx px-24rpx pt-8rpx pb-16rpx" >
< view class = "mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a" >
{ { t ( 'pages.home.featured-on' ) } }
< / view >
< featured-on :list = "featuredList" / >
< / view >
<!-- List 精选菜品瀑布流 ( 浅底 + 白卡片 + 阴影 , 结构对齐设计稿 ) -- >
< view class = "f eatured-dishes-section mt-36rpx px-24rpx pb-40 rpx" >
< view class = "mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a "
> { { t ( 'pages.home.f eatured-dishe s' ) } } < / view >
< view class = "waterfall-row flex gap-16rpx items-start" >
< view
v-for = "(col, colIndex) in featuredDishColumns"
:key = "colIndex"
class = "waterfall-col flex-1 min-w-0 flex flex-col gap-16rpx"
>
< 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 v-if = "item.isNew == 1" class="dish-new-ribbon " >
< text class = "dish-new-ribbon__text" > NEW < / text >
< / view >
< image
:src = "item?.dishImage?.split(',')[0]"
mode = "aspectFill"
class = "featured-dish-img"
/ >
< view
v-if = "isSoldOutStock(item?.stock)"
class = "featured-dish-sold-dim"
/ >
< image
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 >
<!-- Nearby Merchants 附近商家 -- >
< view v-if = "n earbyList.length > 0" class="nearby-merchants-block mt-36rpx px-24rpx pb-16 rpx" >
< view class = "mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a " >
{ { t ( 'pages.home.n earby-merchant s' ) } }
< / view >
< nearby-merchants :list = "nearbyList" / >
< / view >
< / view >
<!-- List 精选菜品瀑布流 ( 浅底 + 白卡片 + 阴影 , 结构对齐设计稿 ) -- >
< view class = "featured-dishes-section mt-36rpx px-24rpx pb-40rpx" >
< view class = "mb-24rpx px-6rpx text-28rpx lh-36rpx text-#1a1a1a " > { { t ( 'pages.home.featured-dishes' ) } } < / view >
< view class = "waterfall-row flex gap-16rpx items-start" >
< view v-for = "(col, colIndex) in featuredDishColumns" :key="colIndex "
class = "waterfall-col flex-1 min-w-0 flex flex-col gap-16rpx" >
< 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 v-if = "item.isNew == 1" class="dish-new-ribbon" >
< text class = "dish-new-ribbon__text" > NEW < / text >
< / view >
< view class = "featured-dish-body" >
< view class = "featured-dish-meta flex items-start justify-between gap-12rpx mb-14rpx" >
< view class = "min-w-0 flex-1" >
< text class = "featured-dish-price" > US$ { { item ? . originalPrice } } < / text >
<!-- < text
< image :src = "item?.dishImage?.split(',')[0]" mode = "aspectFill" class = "featured-dish-img" / >
< view v-if = "isSoldOutStock(item?.stock)" class="featured-dish-sold-dim" / >
< image 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 class = "featured-dish-body" >
< view class = "featured-dish-meta flex items-start justify-between gap-12rpx mb-14rpx" >
< view class = "min-w-0 flex-1" >
< text class = "featured-dish-price" > US$ { { item ? . originalPrice } } < / text >
<!-- < text
v-if = "Number(item?.originalPrice) > Number(item?.discountPrice)"
class = "featured-dish-original"
> US$ { { item ? . originalPrice } } < / text > -- >
< / view >
< text class = "featured-dish-sales shrink-0" > { { t ( 'pages-store.store.sales' ) } } : { { formatSalesCount ( item ? . salesCount ) } } < / text >
< / view >
< view class = "featured-dish-title line-clamp-2 mb-16rpx" >
{ { item ? . dishName } }
< text class = "featured-dish-sales shrink-0" > { { t ( 'pages-store.store.sales' ) } } : { {
formatSalesCount ( item ? . salesCount ) } } < / text >
< / view >
< view class = "featured-dish-title line-clamp-2 mb-16rpx" >
{ { item ? . dishName } }
< / view >
< view class = "flex items-center justify-between gap-12rpx" >
< view v-if = "Number(item?.memberPrice) > 0" class="featured-dish-member shrink min-w-0" >
< text class = "featured-dish-member-inner" > { { t ( 'pages-store.store.members' ) } } : US$ { {
item ? . memberPrice
} } < / text >
< / view >
< view class = "flex items-center justify-between gap-12rpx" >
< view
v-if = "Number(item?.memberPrice) > 0"
class = "featured-dish-member shrink min-w-0"
>
< text class = "featured-dish-member-inner" > { { t ( 'pages-store.store.members' ) } } : US$ { { item ? . memberPrice } } < / text >
< / view >
< view v-else class = "flex-1 min-w-0" > < / view >
< view class = "featured-dish-add shrink-0" >
< image
src = "/static/app/images/add_cart.png"
class = "featured-dish-add__icon"
/ >
< / view >
< / view >
< view
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 >
< view v-else class = "flex-1 min-w-0" > < / view >
< view class = "featured-dish-add shrink-0" >
< image src = "/static/app/images/add_cart.png" class = "featured-dish-add__icon" / >
< / view >
< / view >
< view 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 >
< / view >
< / view >
< / view >
< / view >
< / view >
< template # bottom >
< view class = "h-50px" > < / view >
< view :style = "[configStore.iosSafeBottomPlaceholder] "> < / view >
< / template >
< / z-paging >
< / view >
< template # bottom >
< view class = "h-50px "> < / view >
< view :style = "[configStore.iosSafeBottomPlaceholder]" > < / view >
< / template >
< / 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 >
< / view >
< / view >
@@ -578,28 +508,29 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
padding - right : 8 rpx ;
}
. home - member - banner {
position : relative ;
width : 100 % ;
height : 550 rpx ;
background - size : 100 % 100 % ;
background - position : center ;
background - repeat : no - repeat ;
. home - promo - block {
display : flex ;
flex - direction : column ;
gap : 24 rpx ;
margin - top : 8 rpx ;
margin - bottom : 8 rpx ;
}
. home - member - banner _ _actions {
position : absolute ;
left : 0 ;
right : 0 ;
bottom : 5 rpx ;
padding : 0 40 rpx ;
. home - promo - banner - img {
display : block ;
width : 100 % ;
border - radius : 24 rpx ;
}
. home - promo - actions {
display : flex ;
align - items : center ;
justify - content : space - between ;
gap : 24 rpx ;
padding : 0 8 rpx ;
}
. home - member - banner _ _btn {
. home - promo - btn {
flex : 1 ;
height : 72 rpx ;
padding : 0 24 rpx ;
@@ -610,7 +541,7 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
justify - content : center ;
}
. home - member - banner _ _btn - text {
. home - promo - btn - text {
font - size : 28 rpx ;
font - weight : 600 ;
color : # ffffff ;
@@ -629,6 +560,7 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
. card - swiper {
height : 420 rpx ;
}
. swiper - item - content {
width : 100 % ;
height : 100 % ;