Files
medical-mall/pages/mall/consumer/index - 副本.uvue

1239 lines
26 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 消费者端首页 - 淘宝风格布局 -->
<template>
<view class="consumer-home">
<!-- 自定义顶部导航栏(简化版) -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-content">
<view class="location" @click="showCityPicker">
<text class="location-icon">📍</text>
<text class="location-text">{{ currentCity }}</text>
<text class="location-arrow">▼</text>
</view>
<view class="nav-icons">
<view class="icon-item" @click="navigateToMessages">
<text class="icon">💬</text>
</view>
<view class="icon-item" @click="navigateToCart">
<text class="icon">🛒</text>
</view>
</view>
</view>
</view>
<!-- 主内容区 -->
<scroll-view
scroll-y
class="main-content"
:style="{ height: scrollViewHeight + 'px' }"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="loadMore"
:show-scrollbar="false"
>
<!-- 顶部分类导航 - 横向排列 -->
<view class="top-category-section">
<scroll-view scroll-x class="top-category-scroll" :show-scrollbar="false">
<view class="top-category-list">
<view
v-for="(category, index) in categoryList"
:key="index"
:class="['top-category-item', { active: activeTopCategory === category.id }]"
@click="switchTopCategory(category.id)"
>
<view class="category-icon-wrapper">
<text class="category-icon">{{ category.icon }}</text>
</view>
<text class="category-name">{{ category.name }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 搜索栏 - 在分类下面 -->
<view class="search-section" @click="navigateToSearch">
<view class="search-container">
<view class="search-box">
<text class="search-icon">🔍</text>
<text class="search-placeholder">搜索商品/品牌/店铺</text>
</view>
</view>
</view>
<!-- 轮播图 -->
<view class="banner-section">
<swiper
class="banner-swiper"
:autoplay="true"
:interval="4000"
:duration="500"
:circular="true"
:indicator-dots="true"
indicator-color="rgba(255,255,255,0.6)"
indicator-active-color="#ff5000"
>
<swiper-item v-for="(banner, index) in bannerList" :key="banner.id">
<image
class="banner-image"
:src="banner.image_url"
mode="aspectFill"
/>
</swiper-item>
</swiper>
</view>
<!-- 快捷入口 -->
<view class="quick-actions">
<view class="action-item" @click="navigateToFlashSale">
<view class="action-icon flash">⚡</view>
<text class="action-text">限时秒杀</text>
</view>
<view class="action-item" @click="navigateToCoupons">
<view class="action-icon coupon">🎫</view>
<text class="action-text">领券中心</text>
</view>
<view class="action-item" @click="navigateToDailyDeals">
<view class="action-icon daily">🔥</view>
<text class="action-text">每日特价</text>
</view>
<view class="action-item" @click="navigateToBrand">
<view class="action-icon brand">🏷️</view>
<text class="action-text">品牌特卖</text>
</view>
<view class="action-item" @click="navigateToGroupBuy">
<view class="action-icon group">👥</view>
<text class="action-text">拼团购</text>
</view>
</view>
<!-- 限时秒杀 -->
<view class="flash-sale-section">
<view class="section-header">
<view class="title-row">
<view class="title-with-icon">
<text class="flash-icon">⚡</text>
<text class="section-title">限时秒杀</text>
</view>
<view class="countdown">
<text class="countdown-label">距结束</text>
<view class="time-box">
<text class="time">{{ countdown.hours }}</text>
<text class="colon">:</text>
<text class="time">{{ countdown.minutes }}</text>
<text class="colon">:</text>
<text class="time">{{ countdown.seconds }}</text>
</view>
</view>
</view>
<view class="more-link" @click="navigateToFlashSale">
<text>更多</text>
<text class="arrow"></text>
</view>
</view>
<scroll-view scroll-x class="flash-sale-scroll" :show-scrollbar="false">
<view class="flash-sale-list">
<view
v-for="(product, index) in flashSaleProducts"
:key="index"
class="flash-product"
@click="navigateToProduct(product)"
>
<view class="product-image-wrapper">
<image
class="product-image"
:src="product.image"
mode="aspectFill"
/>
<view v-if="product.tag" class="product-tag">{{ product.tag }}</view>
</view>
<view class="price-info">
<text class="flash-price">¥{{ product.price }}</text>
<text class="original-price">¥{{ product.original_price }}</text>
<view class="progress-bar">
<view class="progress-inner" :style="{ width: product.soldPercent + '%' }"></view>
</view>
<text class="sold-text">已抢{{ product.soldPercent }}%</text>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 为你推荐 - 瀑布流布局 -->
<view class="recommend-section">
<view class="section-header">
<view class="title-with-icon">
<text class="recommend-icon">❤️</text>
<text class="section-title">为你推荐</text>
</view>
</view>
<!-- 瀑布流容器 -->
<view class="waterfall-container">
<!-- 左列 -->
<view class="waterfall-column left-column">
<view
v-for="(product, index) in leftColumnProducts"
:key="product.id"
class="product-card"
@click="navigateToProduct(product)"
>
<view class="product-image-wrapper">
<image
class="product-image"
:src="product.image"
mode="widthFix"
lazy-load="true"
/>
<view v-if="product.tag" class="product-tag">{{ product.tag }}</view>
</view>
<view class="product-info">
<text class="product-title">{{ product.name }}</text>
<view class="price-section">
<view class="current-price">
<text class="price-symbol">¥</text>
<text class="price-value">{{ product.price }}</text>
</view>
<text v-if="product.original_price > product.price" class="original-price">
¥{{ product.original_price }}
</text>
</view>
<view class="product-meta">
<text class="sales-count">{{ product.sales }}人已买</text>
<view class="shop-info" v-if="product.shop">
<text class="shop-name">{{ product.shop }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 右列 -->
<view class="waterfall-column right-column">
<view
v-for="(product, index) in rightColumnProducts"
:key="product.id"
class="product-card"
@click="navigateToProduct(product)"
>
<view class="product-image-wrapper">
<image
class="product-image"
:src="product.image"
mode="widthFix"
lazy-load="true"
/>
<view v-if="product.tag" class="product-tag">{{ product.tag }}</view>
</view>
<view class="product-info">
<text class="product-title">{{ product.name }}</text>
<view class="price-section">
<view class="current-price">
<text class="price-symbol">¥</text>
<text class="price-value">{{ product.price }}</text>
</view>
<text v-if="product.original_price > product.price" class="original-price">
¥{{ product.original_price }}
</text>
</view>
<view class="product-meta">
<text class="sales-count">{{ product.sales }}人已买</text>
<view class="shop-info" v-if="product.shop">
<text class="shop-name">{{ product.shop }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<view v-if="!hasMore && productList.length > 0" class="no-more">
<text>--- 我是有底线的 ---</text>
</view>
</view>
<!-- 底部安全区域 -->
<view class="safe-area"></view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
// 响应式数据
const bannerList = ref<any[]>([])
const categoryList = ref<any[]>([])
const productList = ref<any[]>([])
const flashSaleProducts = ref<any[]>([])
const currentCity = ref<string>('北京市')
const refreshing = ref<boolean>(false)
const loading = ref<boolean>(false)
const hasMore = ref<boolean>(true)
const page = ref<number>(1)
const activeTopCategory = ref<string>('1')
const statusBarHeight = ref<number>(0)
const scrollViewHeight = ref<number>(0)
// 倒计时数据
const countdown = reactive({
hours: '02',
minutes: '30',
seconds: '15'
})
// 计算瀑布流列数据
const leftColumnProducts = computed(() => {
return productList.value.filter((_, index) => index % 2 === 0)
})
const rightColumnProducts = computed(() => {
return productList.value.filter((_, index) => index % 2 === 1)
})
// Mock 数据
const mockData = {
banners: [
{
id: '1',
title: '双11狂欢购',
image_url: 'https://picsum.photos/750/350?random=1'
},
{
id: '2',
title: '品牌品质日',
image_url: 'https://picsum.photos/750/350?random=2'
},
{
id: '3',
title: '新人专享礼',
image_url: 'https://picsum.photos/750/350?random=3'
}
],
categories: [
{ id: '1', name: '推荐', icon: '⭐' },
{ id: '2', name: '女装', icon: '👗' },
{ id: '3', name: '男装', icon: '👔' },
{ id: '4', name: '鞋靴', icon: '👠' },
{ id: '5', name: '箱包', icon: '👜' },
{ id: '6', name: '美妆', icon: '💄' },
{ id: '7', name: '数码', icon: '📱' },
{ id: '8', name: '家电', icon: '📺' },
{ id: '9', name: '家居', icon: '🏠' },
{ id: '10', name: '母婴', icon: '👶' },
{ id: '11', name: '食品', icon: '🍎' },
{ id: '12', name: '运动', icon: '⚽' },
{ id: '13', name: '汽车', icon: '🚗' },
{ id: '14', name: '百货', icon: '🛒' }
],
flashSaleProducts: [
{
id: '1001',
name: '无线蓝牙耳机',
price: 299,
original_price: 599,
image: 'https://picsum.photos/200/200?random=1',
sales: 150,
soldPercent: 75,
tag: '秒杀'
},
{
id: '1002',
name: '运动T恤',
price: 79,
original_price: 159,
image: 'https://picsum.photos/200/200?random=2',
sales: 200,
soldPercent: 80,
tag: '爆款'
},
{
id: '1003',
name: '智能手环',
price: 199,
original_price: 299,
image: 'https://picsum.photos/200/200?random=3',
sales: 180,
soldPercent: 60,
tag: '新品'
},
{
id: '1004',
name: '电动牙刷',
price: 89,
original_price: 159,
image: 'https://picsum.photos/200/200?random=4',
sales: 250,
soldPercent: 90,
tag: '热卖'
},
{
id: '1005',
name: '保温杯',
price: 49,
original_price: 99,
image: 'https://picsum.photos/200/200?random=5',
sales: 300,
soldPercent: 85,
tag: '秒杀'
}
],
products: [
{
id: '2001',
name: '智能手机 5G全网通 大内存',
price: 3999,
original_price: 4999,
image: 'https://picsum.photos/300/400?random=6',
sales: 500,
shop: '官方旗舰店',
tag: '官方'
},
{
id: '2002',
name: '笔记本电脑 轻薄本 学生办公',
price: 5999,
original_price: 6999,
image: 'https://picsum.photos/300/300?random=7',
sales: 300,
shop: '品牌专卖店',
tag: '折扣'
},
{
id: '2003',
name: '运动鞋 透气舒适跑步鞋',
price: 299,
original_price: 499,
image: 'https://picsum.photos/300/500?random=8',
sales: 800,
shop: '运动专营店'
},
{
id: '2004',
name: '电饭煲 智能预约多功能',
price: 399,
original_price: 599,
image: 'https://picsum.photos/300/350?random=9',
sales: 450,
shop: '家电旗舰店',
tag: '热卖'
},
{
id: '2005',
name: '护肤品套装 保湿补水',
price: 299,
original_price: 399,
image: 'https://picsum.photos/300/450?random=10',
sales: 600,
shop: '美妆官方店'
},
{
id: '2006',
name: '儿童玩具积木益智',
price: 199,
original_price: 299,
image: 'https://picsum.photos/300/320?random=11',
sales: 350,
shop: '母婴专营店',
tag: '新品'
},
{
id: '2007',
name: '智能手表 心率监测',
price: 899,
original_price: 1299,
image: 'https://picsum.photos/300/380?random=12',
sales: 280,
shop: '数码旗舰店'
},
{
id: '2008',
name: '羽绒服 冬季保暖',
price: 499,
original_price: 899,
image: 'https://picsum.photos/300/420?random=13',
sales: 420,
shop: '服装旗舰店',
tag: '爆款'
}
]
}
// 生命周期
onMounted(() => {
// 获取状态栏高度
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight || 0
// 计算滚动区域高度
const windowHeight = systemInfo.windowHeight
const tabBarHeight = 50 // 假设tabBar高度为50px
scrollViewHeight.value = windowHeight - statusBarHeight.value - tabBarHeight
// 加载数据
loadMockData()
setupCountdown()
})
onUnmounted(() => {
// 清理定时器
if (countdownTimer.value !== null) {
clearInterval(countdownTimer.value)
}
})
// 加载Mock数据
const loadMockData = () => {
bannerList.value = mockData.banners
categoryList.value = mockData.categories
flashSaleProducts.value = mockData.flashSaleProducts
productList.value = mockData.products
}
// 倒计时定时器
const countdownTimer = ref<any>(null)
const setupCountdown = () => {
countdownTimer.value = setInterval(() => {
let hours = parseInt(countdown.hours)
let minutes = parseInt(countdown.minutes)
let seconds = parseInt(countdown.seconds)
seconds--
if (seconds < 0) {
seconds = 59
minutes--
if (minutes < 0) {
minutes = 59
hours--
if (hours < 0) {
// 重置倒计时
hours = 2
minutes = 30
seconds = 15
}
}
}
countdown.hours = hours.toString().padStart(2, '0')
countdown.minutes = minutes.toString().padStart(2, '0')
countdown.seconds = seconds.toString().padStart(2, '0')
}, 1000)
}
// 事件处理函数
const showCityPicker = () => {
uni.showActionSheet({
itemList: ['北京市', '上海市', '广州市', '深圳市', '其他城市'],
success: (res) => {
const cities = ['北京市', '上海市', '广州市', '深圳市', '其他城市']
currentCity.value = cities[res.tapIndex]
}
})
}
const switchTopCategory = (categoryId: string) => {
activeTopCategory.value = categoryId
// 这里可以添加根据分类筛选商品的逻辑
}
// 下拉刷新
const onRefresh = () => {
refreshing.value = true
setTimeout(() => {
loadMockData()
refreshing.value = false
uni.showToast({
title: '刷新成功',
icon: 'success'
})
}, 1000)
}
// 上拉加载更多
const loadMore = () => {
if (!loading.value && hasMore.value) {
loading.value = true
setTimeout(() => {
// 模拟加载更多数据
const newProducts = [...mockData.products].map((item, index) => ({
...item,
id: `300${index}`,
price: item.price + Math.floor(Math.random() * 100)
}))
productList.value = [...productList.value, ...newProducts]
loading.value = false
hasMore.value = productList.value.length < 20 // 假设最多显示20个商品
}, 1500)
}
}
// 导航函数
const navigateToSearch = () => uni.navigateTo({ url: '/pages/mall/consumer/search' })
const navigateToCategory = (category?: any) => {
if (category) {
uni.navigateTo({ url: `/pages/mall/consumer/category?id=${category.id}&name=${category.name}` })
} else {
uni.switchTab({ url: '/pages/mall/consumer/category' })
}
}
const navigateToProduct = (product: any) => {
uni.navigateTo({ url: `/pages/mall/consumer/product-detail?id=${product.id}` })
}
const navigateToCoupons = () => uni.navigateTo({ url: '/pages/mall/consumer/coupons' })
const navigateToMessages = () => uni.navigateTo({ url: '/pages/mall/consumer/messages' })
const navigateToFlashSale = () => uni.navigateTo({ url: '/pages/mall/consumer/flash-sale' })
const navigateToBrand = () => uni.navigateTo({ url: '/pages/mall/consumer/brand' })
const navigateToCart = () => uni.switchTab({ url: '/pages/mall/consumer/cart' })
const navigateToDailyDeals = () => uni.navigateTo({ url: '/pages/mall/consumer/daily-deals' })
const navigateToGroupBuy = () => uni.navigateTo({ url: '/pages/mall/consumer/group-buy' })
</script>
<style>
/* 重置默认样式 */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.consumer-home {
width: 100%;
min-height: 100vh;
background: #f5f5f5;
display: flex;
flex-direction: column;
}
/* 自定义导航栏 - 简化版 */
.custom-navbar {
background: #ff5000;
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
}
.nav-content {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
color: white;
}
.location {
display: flex;
align-items: center;
flex-shrink: 0;
padding: 5px 10px;
background: rgba(255, 255, 255, 0.2);
border-radius: 15px;
}
.location-icon {
font-size: 14px;
margin-right: 4px;
}
.location-text {
font-size: 14px;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 60px;
}
.location-arrow {
font-size: 10px;
margin-left: 4px;
color: rgba(255, 255, 255, 0.8);
}
.nav-icons {
display: flex;
align-items: center;
gap: 15px;
}
.icon-item {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
}
.icon {
font-size: 20px;
}
/* 主内容区 */
.main-content {
background: #f5f5f5;
margin-top: 44px;
width: 100%;
}
/* 顶部分类导航 - 横向滚动 */
.top-category-section {
background: white;
width: 100%;
padding: 10px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.top-category-scroll {
width: 100%;
white-space: nowrap;
height: 70px;
}
.top-category-list {
display: inline-flex;
padding: 0 10px;
}
.top-category-item {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 70px;
height: 60px;
flex-shrink: 0;
padding: 0 5px;
}
.top-category-item.active .category-icon-wrapper {
background: #ff5000;
}
.top-category-item.active .category-icon {
color: white;
}
.top-category-item.active .category-name {
color: #ff5000;
font-weight: bold;
}
.category-icon-wrapper {
width: 40px;
height: 40px;
border-radius: 20px;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 4px;
transition: all 0.2s;
}
.category-icon {
font-size: 20px;
color: #666;
}
.category-name {
font-size: 11px;
color: #333;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
/* 搜索栏 - 在分类下面 */
.search-section {
background: white;
padding: 10px 15px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.search-container {
width: 100%;
}
.search-box {
background: #f5f5f5;
border-radius: 20px;
padding: 10px 15px;
display: flex;
align-items: center;
width: 100%;
height: 40px;
}
.search-icon {
color: #999;
margin-right: 8px;
font-size: 16px;
}
.search-placeholder {
color: #999;
font-size: 14px;
flex: 1;
}
/* 轮播图区域 */
.banner-section {
background: white;
width: 100%;
overflow: hidden;
}
.banner-swiper {
height: 180px;
width: 100%;
}
.banner-image {
width: 100%;
height: 100%;
display: block;
}
/* 快捷入口 - 横向排列 */
.quick-actions {
background: white;
padding: 15px 0;
margin: 10px;
border-radius: 10px;
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
width: calc(100% - 20px);
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 20%;
flex-shrink: 0;
}
.action-icon {
width: 40px;
height: 40px;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
margin-bottom: 6px;
}
.action-icon.flash {
background: linear-gradient(135deg, #ff3b30, #ff9500);
color: white;
}
.action-icon.coupon {
background: linear-gradient(135deg, #34c759, #30d158);
color: white;
}
.action-icon.daily {
background: linear-gradient(135deg, #ff9500, #ffcc00);
color: white;
}
.action-icon.brand {
background: linear-gradient(135deg, #af52de, #ff2d55);
color: white;
}
.action-icon.group {
background: linear-gradient(135deg, #007aff, #5856d6);
color: white;
}
.action-text {
font-size: 12px;
color: #333;
font-weight: 500;
text-align: center;
width: 100%;
}
/* 限时秒杀 */
.flash-sale-section {
background: linear-gradient(135deg, #fff0e8, #ffece6);
margin: 10px;
border-radius: 10px;
padding: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border: 1px solid #ffddd1;
width: calc(100% - 20px);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.title-row {
display: flex;
align-items: center;
gap: 10px;
}
.title-with-icon {
display: flex;
align-items: center;
gap: 6px;
}
.flash-icon, .recommend-icon {
color: #ff5000;
font-size: 18px;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #ff5000;
}
.countdown {
display: flex;
align-items: center;
gap: 6px;
background: #ff5000;
padding: 4px 10px;
border-radius: 4px;
}
.countdown-label {
font-size: 12px;
color: white;
}
.time-box {
display: flex;
align-items: center;
gap: 2px;
}
.time {
background: white;
color: #ff5000;
padding: 2px 6px;
border-radius: 3px;
font-size: 12px;
min-width: 20px;
text-align: center;
font-weight: bold;
}
.colon {
color: white;
font-size: 12px;
font-weight: bold;
}
.flash-sale-scroll {
width: 100%;
white-space: nowrap;
margin-top: 15px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.flash-sale-list {
display: inline-flex;
gap: 12px;
padding-bottom: 10px;
padding-right: 10px;
}
.flash-product {
width: 120px;
flex-shrink: 0;
background: white;
border-radius: 8px;
overflow: hidden;
border: 1px solid #ffddd1;
box-shadow: 0 2px 4px rgba(255, 80, 0, 0.1);
}
.product-image-wrapper {
position: relative;
width: 100%;
height: 120px;
}
.product-image {
width: 100%;
height: 100%;
display: block;
}
.product-tag {
position: absolute;
top: 6px;
left: 6px;
background: #ff5000;
color: white;
font-size: 10px;
padding: 2px 8px;
border-radius: 10px;
}
.price-info {
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
}
.flash-price {
color: #ff5000;
font-size: 16px;
font-weight: bold;
text-align: center;
width: 100%;
}
.original-price {
color: #999;
font-size: 12px;
text-decoration: line-through;
margin: 4px 0;
text-align: center;
width: 100%;
}
.progress-bar {
width: 100%;
height: 6px;
background: #ffece6;
border-radius: 3px;
margin: 8px 0;
overflow: hidden;
}
.progress-inner {
height: 100%;
background: linear-gradient(90deg, #ff9500, #ff5000);
border-radius: 3px;
}
.sold-text {
font-size: 10px;
color: #ff5000;
text-align: center;
width: 100%;
}
/* 为你推荐 - 瀑布流布局 */
.recommend-section {
background: white;
margin: 10px;
border-radius: 10px;
padding: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
width: calc(100% - 20px);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
/* 瀑布流布局 */
.waterfall-container {
display: flex;
justify-content: space-between;
gap: 10px;
width: 100%;
}
.waterfall-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.left-column {
margin-right: 5px;
}
.right-column {
margin-left: 5px;
}
.product-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
width: 100%;
}
.product-image-wrapper {
position: relative;
width: 100%;
background: #f8f8f8;
}
.product-image {
width: 100%;
display: block;
}
.product-info {
padding: 12px;
}
.product-title {
font-size: 14px;
color: #333;
line-height: 1.4;
height: 40px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
margin-bottom: 10px;
font-weight: 500;
}
.price-section {
display: flex;
align-items: baseline;
margin-bottom: 8px;
}
.current-price {
color: #ff5000;
}
.price-symbol {
font-size: 12px;
}
.price-value {
font-size: 18px;
font-weight: bold;
}
.original-price {
color: #999;
font-size: 12px;
text-decoration: line-through;
margin-left: 8px;
}
.product-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
}
.sales-count {
color: #999;
}
.shop-info {
display: flex;
align-items: center;
}
.shop-name {
color: #666;
}
/* 加载状态 */
.loading-container {
padding: 30px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
}
.loading-spinner {
width: 30px;
height: 30px;
border: 3px solid #f0f5ff;
border-top-color: #ff5000;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
color: #999;
font-size: 14px;
}
.no-more {
text-align: center;
color: #999;
font-size: 12px;
padding: 30px 0;
border-top: 1px solid #f0f0f0;
margin-top: 10px;
width: 100%;
}
/* 安全区域 */
.safe-area {
height: 20px;
width: 100%;
}
/* 媒体查询适配 */
@media (max-width: 375px) {
.top-category-item {
width: 65px;
}
.action-item {
width: 20%;
}
}
@media (min-width: 376px) and (max-width: 414px) {
.top-category-item {
width: 70px;
}
.action-item {
width: 20%;
}
}
</style>