761 lines
23 KiB
Vue
761 lines
23 KiB
Vue
<script setup lang="ts">
|
||
import useEventEmit from "@/hooks/useEventEmit";
|
||
import {CollectionType, EventEnum} from "@/constant/enums";
|
||
import { useConfigStore, useUserStore } from "@/store";
|
||
import Config from '@/config/index'
|
||
import { debounce } from 'throttle-debounce'
|
||
import MsgBox from "@/pages/home/components/tabbar-home/components/msg-box.vue";
|
||
import Search from "@/pages/home/components/tabbar-home/components/search.vue";
|
||
import ClassBullet from "./components/class-bullet.vue";
|
||
import TabsType from "./components/tabs-type.vue";
|
||
import FeaturedOn from "./components/featured-on/index.vue";
|
||
import FiltrateTool from "@/components/filtrate-tool/index.vue";
|
||
import NearbyMerchants from "./components/nearby-merchants/index.vue";
|
||
import FoodBox from "./components/food-box/index.vue";
|
||
import HomeSkeleton from "./components/home-skeleton.vue";
|
||
import {
|
||
appMarketActivityListPost,
|
||
appMerchantCartListMerchantPost,
|
||
appMerchantCategoryListGet,
|
||
appMerchantFeaturedListPost,
|
||
appMerchantLabelListGet,
|
||
appMerchantNearbyListPost, appMerchantRecommendListPost,
|
||
appCollectCollectPost,
|
||
appRecipeCategoryListGet
|
||
} from "@/service";
|
||
import usePage from "@/hooks/usePage";
|
||
import {getFeaturedDishList} from "@/pages-store/service";
|
||
import { formatSalesCount } from "@/utils/utils";
|
||
const configStore = useConfigStore();
|
||
const userStore = useUserStore();
|
||
const props = defineProps<{
|
||
scoreRange?: string;
|
||
price?: Array<string> | string;
|
||
}>();
|
||
const emit = defineEmits(["toggleScore", "togglePrice", "toggleNotOpen"]);
|
||
const { t } = useI18n();
|
||
|
||
const loading = ref(false)
|
||
|
||
function isSoldOutStock(stockLike: unknown) {
|
||
const n = Number(stockLike)
|
||
return !Number.isNaN(n) && n <= 0
|
||
}
|
||
|
||
/** 底部营销条文案(接口若返回 marketingLabel / hotSaleTag 等则展示) */
|
||
function getDishPromoLabel(item: Record<string, unknown>): string {
|
||
const raw = item.marketingLabel ?? item.hotSaleTag ?? item.rankTag ?? item.promotionLabel
|
||
return typeof raw === 'string' && raw.trim() ? raw.trim() : ''
|
||
}
|
||
|
||
function getFeaturedDishDisplayPrice(item: Record<string, any>) {
|
||
const firstSpecPrice = item?.merchantSideDishVoList?.[0]?.merchantSideDishItemVoList?.[0]?.actualSalePrice
|
||
if (firstSpecPrice != null && String(firstSpecPrice) !== '') return firstSpecPrice
|
||
if (item?.actualSalePrice != null && String(item.actualSalePrice) !== '') return item.actualSalePrice
|
||
return item?.discountPrice ?? 0
|
||
}
|
||
|
||
function navigateTo(url: string) {
|
||
if(userStore.checkLogin()) {
|
||
uni.navigateTo({
|
||
url,
|
||
});
|
||
}
|
||
}
|
||
|
||
const swiperList = ref([]);
|
||
const currentSwiper = ref(0);
|
||
|
||
async function initData() {
|
||
// 只在首次加载时显示骨架屏,避免切换时的白屏
|
||
if(featuredList.value.length === 0) {
|
||
loading.value = true
|
||
}
|
||
appMarketActivityList()
|
||
getAppMerchantLabelList()
|
||
getAppMerchantCategoryList()
|
||
getAppFeaturedList()
|
||
getAppNearbyListPost()
|
||
// 获取当前用户购物车信息
|
||
userStore.getUserCartAllData()
|
||
}
|
||
|
||
// 筛选事件触发
|
||
function toggleScore() {
|
||
emit("toggleScore");
|
||
}
|
||
function togglePrice() {
|
||
emit("togglePrice");
|
||
}
|
||
function toggleNotOpen() {
|
||
emit("toggleNotOpen");
|
||
}
|
||
|
||
async function getIndexList() {
|
||
// 切换时立即刷新列表,无需等待nextTick
|
||
if (paging.value) {
|
||
paging.value.refresh()
|
||
}
|
||
}
|
||
|
||
defineExpose({
|
||
initData,
|
||
init: getIndexList,
|
||
});
|
||
|
||
// 获取轮播图列表
|
||
function appMarketActivityList() {
|
||
appMarketActivityListPost({}).then(res=> {
|
||
console.log('互动列表', res)
|
||
swiperList.value = res.data
|
||
})
|
||
}
|
||
|
||
// 滚动信息获取 APP-商家标签分类控制器(用户端首页上面左右滚动的)
|
||
const appMerchantLabelList = ref<any>([])
|
||
function getAppMerchantLabelList() {
|
||
appRecipeCategoryListGet({}).then(res => {
|
||
console.log('滚动信息获取 APP-商家标签分类控制器(用户端首页上面左右滚动的)', res)
|
||
appMerchantLabelList.value = res.data || []
|
||
})
|
||
// appMerchantLabelListGet({}).then(res => {
|
||
// console.log('滚动信息获取 APP-商家标签分类控制器(用户端首页上面左右滚动的)', res)
|
||
// appMerchantLabelList.value = res.data || []
|
||
// })
|
||
}
|
||
// 查询所有商家分类数据
|
||
const appMerchantCategoryList = ref([])
|
||
const currentCategory = ref('')
|
||
function getAppMerchantCategoryList() {
|
||
appMerchantCategoryListGet({}).then((res: any) => {
|
||
console.log('查询所有商家分类数据', res)
|
||
appMerchantCategoryList.value = res.data || []
|
||
})
|
||
}
|
||
|
||
// 查询精选商家列表(首页)
|
||
const featuredList = ref([])
|
||
function getAppFeaturedList() {
|
||
appMerchantFeaturedListPost({
|
||
body: {
|
||
lat: userStore.userLocation.latitude,
|
||
lng: userStore.userLocation.longitude,
|
||
}
|
||
}).then(res=> {
|
||
featuredList.value = res.data || []
|
||
}).finally(()=> {
|
||
loading.value = false
|
||
})
|
||
}
|
||
|
||
// 查询附近商家列表(首页) /app/merchant/nearbyList
|
||
const nearbyList = ref([])
|
||
function getAppNearbyListPost() {
|
||
appMerchantNearbyListPost({
|
||
body: {
|
||
lat: userStore.userLocation.latitude,
|
||
lng: userStore.userLocation.longitude,
|
||
}
|
||
}).then(res=> {
|
||
nearbyList.value = res.data || []
|
||
})
|
||
}
|
||
|
||
// 是否自提
|
||
const selfPickup = ref<number | null>(null)
|
||
function togglePickup(value: number) {
|
||
selfPickup.value = value;
|
||
// paging.value.refresh()
|
||
}
|
||
// 是否有折扣
|
||
const discount = ref<number | null>(null)
|
||
function toggleDiscount(value: number) {
|
||
discount.value = value;
|
||
// paging.value.refresh()
|
||
}
|
||
const {paging, dataList, queryList} = usePage(getList)
|
||
function getList(pageNum: number, pageSize: number) {
|
||
return new Promise(resolve => {
|
||
getFeaturedDishList({
|
||
pageNum,
|
||
pageSize,
|
||
}).then(res => {
|
||
console.log('查询精选菜品列表', res)
|
||
resolve({rows: res.rows})
|
||
})
|
||
})
|
||
}
|
||
|
||
// 回到顶部
|
||
const showBackToTop = ref(false)
|
||
function scrollToTop() {
|
||
if (paging.value) {
|
||
paging.value.scrollToTop()
|
||
}
|
||
}
|
||
|
||
// 监听页面滚动
|
||
function onPageScroll(e: any) {
|
||
// 滚动距离大于60时显示,小于等于60时隐藏
|
||
showBackToTop.value = e.detail.scrollTop > 120
|
||
}
|
||
|
||
// 点击头部分类
|
||
const merchantLabelId = ref('')
|
||
function handleItemClick(e) {
|
||
console.log(e, '点击头部分类')
|
||
merchantLabelId.value = e.id
|
||
// paging.value.refresh()
|
||
}
|
||
function tabsTypeChange(id: string) {
|
||
currentCategory.value = id
|
||
navigateTo('/pages-store/pages/home-store/index?merchantCategoryIds=' + id)
|
||
// console.log('分类切换', id)
|
||
// paging.value.refresh()
|
||
}
|
||
|
||
// 是否展示精选商家和附近商家 true 显示 false隐藏
|
||
const isShowMerchant = computed(()=> {
|
||
if(!selfPickup.value && !discount.value && !props.scoreRange && !props.price && !currentCategory.value) {
|
||
return true // 没有筛选条件时显示
|
||
} else {
|
||
return true // 有筛选条件时隐藏
|
||
}
|
||
})
|
||
|
||
// 精选菜品瀑布流:按序均分到两列(0,2,4… / 1,3,5…),分页追加后仍交错排列
|
||
const featuredDishColumns = computed(() => {
|
||
const list = dataList.value
|
||
return [
|
||
list.filter((_, i) => i % 2 === 0),
|
||
list.filter((_, i) => i % 2 === 1),
|
||
]
|
||
})
|
||
|
||
/** 顶栏购物车角标(多商家购物车汇总件数) */
|
||
const cartBadgeTotal = computed(() => {
|
||
const list = userStore.userCartAllData
|
||
if (!Array.isArray(list) || list.length === 0) return 0
|
||
let n = 0
|
||
for (const m of list) {
|
||
n += (m as { merchantCartVoList?: unknown[] })?.merchantCartVoList?.length || 0
|
||
}
|
||
return n
|
||
})
|
||
|
||
function goCart() {
|
||
navigateTo('/pages-user/pages/cart/index')
|
||
}
|
||
|
||
// 手动触发下拉刷新了
|
||
function onRefresh() {
|
||
console.log('手动触发下拉刷新了')
|
||
merchantLabelId.value = ''
|
||
currentCategory.value = ''
|
||
paging.value.refresh()
|
||
}
|
||
|
||
function handleClickSwiper(item: any) {
|
||
console.log(item, '点击轮播图')
|
||
switch (Number(item.activityType)) {
|
||
case 1: // 商家列表
|
||
navigateTo('/pages-store/pages/list/index')
|
||
break
|
||
case 2: // 活动菜品列表
|
||
navigateTo('/pages-store/pages/dishes/index?id=' + item.id)
|
||
break
|
||
case 3: // 会员
|
||
navigateTo('/pages-user/pages/member/index')
|
||
break
|
||
}
|
||
}
|
||
|
||
function navigateToDishes(item: any) {
|
||
uni.navigateTo({
|
||
url: '/pages-store/pages/store/dishes?id=' + item.id + '&storeId=' + item.merchantId,
|
||
})
|
||
}
|
||
// 收藏菜品
|
||
function handleDishCollectionClick(item: any) {
|
||
debouncedEmit(item.isCollect, item.id, CollectionType.DISH, ()=> {
|
||
item.isCollect = !item.isCollect
|
||
})
|
||
}
|
||
// 防抖处理函数
|
||
const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: CollectionType, callback: ()=> void) => {
|
||
// 收藏接口
|
||
appCollectCollectPost({
|
||
body: {
|
||
targetId: id,
|
||
targetType: type
|
||
}
|
||
}).then(res=> {
|
||
callback()
|
||
})
|
||
}, {
|
||
atBegin: true, // 立即触发
|
||
})
|
||
</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 />
|
||
<!-- 设计稿:品牌行 + 右侧地址胶囊、消息/客服、购物车 -->
|
||
<view class="home-top-header px-24rpx pt-12rpx pb-8rpx">
|
||
<view class="flex items-center justify-between gap-12rpx">
|
||
<view class="flex items-center gap-14rpx min-w-0 flex-1">
|
||
<image src="@img/logo.png" class="w-52rpx h-52rpx shrink-0" style="border-radius: 50%;"></image>
|
||
<text class="text-32rpx lh-36rpx text-#1a1a1a font-bold tracking-tight shrink-0">{{ Config.appName }}</text>
|
||
</view>
|
||
<view class="flex items-center gap-10rpx shrink-0">
|
||
<view class="home-loc-pill" @click="navigateTo('/pages-user/pages/search-address/index')">
|
||
<!-- <image src="@img/chef/101.png" class="home-loc-pill__pin w-22rpx h-22rpx shrink-0"></image> -->
|
||
<text class="home-loc-pill__text line-clamp-1">{{
|
||
userStore.userLocation.location || t('pages.home.default-location')
|
||
}}</text>
|
||
<image src="@img/chef/119.png" class="home-loc-pill__arrow w-20rpx h-20rpx shrink-0 op-50 mt-2rpx"></image>
|
||
</view>
|
||
<view class="home-cart-btn" @click="goCart">
|
||
<view class="i-carbon:shopping-cart text-36rpx text-#14181b"></view>
|
||
<view v-if="userStore.isLogin && cartBadgeTotal > 0" class="home-cart-badge">{{ cartBadgeTotal > 99 ? '99+' : cartBadgeTotal }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="home-delivery-actions-row mt-14rpx flex items-center justify-between gap-16rpx">
|
||
<view @click="navigateTo('/pages/address/index')" class="home-delivery-row flex items-center min-w-0 flex-1 text-26rpx lh-32rpx text-#00A76D">
|
||
<text v-if="userStore.appointmentTimeShow">{{ t('pages.address.appTime') }}: {{ userStore.appointmentTimeShow }}</text>
|
||
<text v-else>{{ t('pages.address.reservation') }}</text>
|
||
<image src="@img/chef/119.png" class="w-22rpx h-22rpx ml-8rpx shrink-0"></image>
|
||
</view>
|
||
<msg-box compact class="shrink-0" @toggleNotOpen="toggleNotOpen" />
|
||
</view>
|
||
</view>
|
||
</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 />
|
||
<!-- 分类标签(双行横滑) -->
|
||
<view class="mt-28rpx" v-if="appMerchantLabelList.length > 0">
|
||
<class-bullet :categories="appMerchantLabelList" @itemClick="handleItemClick" />
|
||
</view>
|
||
</view>
|
||
<swiper
|
||
class="home-promo-swiper card-swiper"
|
||
:circular="true"
|
||
:autoplay="true"
|
||
previous-margin="48rpx"
|
||
next-margin="48rpx"
|
||
>
|
||
<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% rounded-32rpx bg-common"
|
||
></image>
|
||
</swiper-item>
|
||
</swiper>
|
||
|
||
<!-- 快捷入口(圆形分类) -->
|
||
<tabs-type @changeType="tabsTypeChange" v-if="appMerchantCategoryList.length > 0" :list="appMerchantCategoryList" :currentId="currentCategory" class="mt-28rpx home-tabs-quick" />
|
||
|
||
<!-- 筛选工具 -->
|
||
<!-- <filtrate-tool class="mt-32rpx" @togglePickup="togglePickup" @toggleDiscount="toggleDiscount" @toggleScore="toggleScore" @togglePrice="togglePrice" />-->
|
||
|
||
<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-32rpx lh-44rpx 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-32rpx lh-44rpx text-#1a1a1a ">
|
||
{{ t('pages.home.nearby-merchants') }}
|
||
</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-32rpx lh-44rpx 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>
|
||
<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${{ getFeaturedDishDisplayPrice(item) }}</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 }}
|
||
</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 center shrink-0">
|
||
<image
|
||
src="@img/chef/1285.png"
|
||
class="w-28rpx h-28rpx"
|
||
/>
|
||
</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>
|
||
</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">
|
||
<image src="@img/chef/119.png" class="w-40rpx h-40rpx shrink-0 rotate-180"></image>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.home-page-root {
|
||
background: #f2f2f2;
|
||
}
|
||
|
||
.home-top-header {
|
||
background: #f2f2f2;
|
||
}
|
||
|
||
.home-loc-pill {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
max-width: 200rpx;
|
||
padding: 12rpx 18rpx;
|
||
background: #fff;
|
||
border-radius: 999rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.home-loc-pill__text {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 22rpx;
|
||
line-height: 28rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
}
|
||
|
||
.home-cart-btn {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
flex-shrink: 0;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.07);
|
||
}
|
||
|
||
.home-cart-badge {
|
||
position: absolute;
|
||
top: 4rpx;
|
||
right: 4rpx;
|
||
min-width: 28rpx;
|
||
height: 28rpx;
|
||
padding: 0 6rpx;
|
||
font-size: 18rpx;
|
||
line-height: 28rpx;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
text-align: center;
|
||
background: #e23636;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.home-delivery-row {
|
||
padding-left: 2rpx;
|
||
}
|
||
|
||
.home-promo-swiper {
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.home-tabs-quick {
|
||
padding-left: 8rpx;
|
||
padding-right: 8rpx;
|
||
}
|
||
|
||
.nearby-merchants-block {
|
||
background: #f2f2f2;
|
||
}
|
||
|
||
/* 为底栏 Tab 预留空间,避免被遮挡 */
|
||
.home-back-top {
|
||
bottom: calc(100rpx + env(safe-area-inset-bottom, 0px));
|
||
}
|
||
|
||
.card-swiper {
|
||
height: 400rpx;
|
||
}
|
||
.swiper-item-content {
|
||
width: 100%;
|
||
height: 100%;
|
||
transform: scale(0.94);
|
||
border-radius: 32rpx;
|
||
box-shadow: 0 8rpx 28rpx rgba(0, 0, 0, 0.08);
|
||
transition: transform 0.3s ease;
|
||
}
|
||
.swiper-item-active .swiper-item-content {
|
||
transform: scale(1);
|
||
}
|
||
|
||
/* 精选商家 / 精选菜品 区块(与页面背景统一) */
|
||
.featured-merchants-section,
|
||
.featured-dishes-section {
|
||
background: #f2f2f2;
|
||
}
|
||
|
||
.featured-dish-collect-icon {
|
||
filter: drop-shadow(0 2rpx 6rpx rgba(0, 0, 0, 0.18));
|
||
}
|
||
|
||
/* 瀑布流商品图:固定 1:1 区域,便于「已售完」等角标统一 */
|
||
.featured-dish-image {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 0;
|
||
padding-bottom: 100%;
|
||
background: #f0f0f0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.featured-dish-img {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
z-index: 1;
|
||
}
|
||
|
||
.featured-dish-sold-dim {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 2;
|
||
background: rgba(20, 24, 27, 0.42);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.featured-dish-sold-overlay {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 3;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.featured-dish-card {
|
||
background: #fff;
|
||
border-radius: 24rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.featured-dish-body {
|
||
padding: 20rpx 20rpx 22rpx;
|
||
}
|
||
|
||
.featured-dish-price {
|
||
color: #e02e24;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.featured-dish-original {
|
||
margin-left: 10rpx;
|
||
color: #b3b3b3;
|
||
font-size: 22rpx;
|
||
font-weight: 400;
|
||
text-decoration: line-through;
|
||
vertical-align: baseline;
|
||
}
|
||
|
||
.featured-dish-sales {
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
line-height: 1.35;
|
||
max-width: 48%;
|
||
text-align: right;
|
||
}
|
||
|
||
.featured-dish-title {
|
||
color: #1a1a1a;
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
/* 会员价:暖色胶囊,替代长条形切图 */
|
||
.featured-dish-member {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
max-width: calc(100% - 88rpx);
|
||
}
|
||
|
||
.featured-dish-member-inner {
|
||
display: inline-block;
|
||
padding: 6rpx 18rpx;
|
||
border-radius: 999rpx;
|
||
background: linear-gradient(180deg, #fff5eb 0%, #ffe8d6 100%);
|
||
color: #c45c1a;
|
||
font-size: 24rpx;
|
||
font-weight: 500;
|
||
line-height: 1.35;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.featured-dish-add {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
border-radius: 50%;
|
||
background: #14181b;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.12);
|
||
}
|
||
|
||
/* 可选营销条(淡红底 + 文案) */
|
||
.featured-dish-promo {
|
||
padding: 12rpx 16rpx;
|
||
border-radius: 12rpx;
|
||
background: #fff0f0;
|
||
}
|
||
|
||
.featured-dish-promo-text {
|
||
color: #e02e24;
|
||
font-size: 22rpx;
|
||
font-weight: 500;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.dish-new-ribbon {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 184rpx;
|
||
height: 184rpx;
|
||
overflow: hidden;
|
||
z-index: 5;
|
||
pointer-events: none;
|
||
|
||
&__text {
|
||
position: absolute;
|
||
top: 26rpx;
|
||
left: -66rpx;
|
||
width: 240rpx;
|
||
text-align: center;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
letter-spacing: 2rpx;
|
||
line-height: 44rpx;
|
||
background: #E23636;
|
||
transform: rotate(-45deg);
|
||
}
|
||
}
|
||
</style>
|