Files
medical-mall/pages/mall/consumer/index copy.uvue

2667 lines
59 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.
<!-- pages/mall/consumer/index.uvue -->
<template>
<view class="medic-home">
<!-- 智能顶部导航栏 - 添加滚动隐藏效果 -->
<view
class="smart-navbar"
:style="{
paddingTop: statusBarHeight + 'px',
transform: showNavbar ? 'translateY(0)' : 'translateY(-100%)'
}"
>
<view class="search-container">
<view class="search-box" @click="navigateToSearch" :style="{ height: '30px' }">
<!-- 模拟输入框 -->
<text class="search-placeholder">请输入药品名称、症状或品牌</text>
<!-- 扫码图标 -->
<view class="nav-icon-btn" @click.stop="onScan">
<text class="nav-icon">🔳</text>
</view>
<!-- 相机图标 -->
<view class="nav-camera-btn" @click.stop="onCamera">
<text class="nav-camera-icon">📷</text>
</view>
<!-- 搜索按钮 -->
<view class="nav-inner-search-btn" :style="{ height: '22px' }">
<text class="nav-inner-search-text">搜索</text>
</view>
</view>
</view>
</view>
<!-- 导航栏占位符 - 移除,改为使用 margin-top -->
<!-- <view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view> -->
<!-- 主内容区 -->
<scroll-view
direction="vertical"
class="main-scroll"
refresher-enabled
:refresher-triggered="refreshing"
:lower-threshold="50"
@refresherrefresh="onRefresh"
@scrolltolower="loadMore"
@scroll="handleScroll"
>
<!-- 健康资讯轮播 (Moved Up) -->
<!-- 健康资讯轮播 (Hidden) -->
<!--
<view class="health-news">
...
</view>
-->
<!-- 智能健康卡片 (Hidden) -->
<!-- <view class="smart-health-card" :style="{ marginTop: (statusBarHeight + 44 + 10) + 'px' }">
<view class="health-content">
<view class="health-header">
<text class="health-title">智能健康助手</text>
<text class="health-subtitle">根据您的健康数据推荐</text>
</view>
<view class="health-tips">
<text class="tip-item">💡 按时用药提醒</text>
<text class="tip-item">📋 健康记录跟踪</text>
<text class="tip-item">🩺 在线问诊咨询</text>
</view>
</view>
</view> -->
<!-- 智能分类网格 - 完全响应式 -->
<view class="smart-categories" :style="{ marginTop: (statusBarHeight + 44 + 10) + 'px' }">
<view class="section-header">
<view class="category-tabs-pills">
<view :class="['tab-pill', { active: categoryTab == 'category' }]" @click="categoryTab = 'category'">
<text class="tab-text">智能分类</text>
</view>
<view :class="['tab-pill', { active: categoryTab == 'brand' }]" @click="categoryTab = 'brand'">
<text class="tab-text">品牌甄选</text>
</view>
</view>
<text class="section-desc">快速定位</text>
</view>
<view class="category-grid" v-if="categoryTab === 'category'">
<!-- 一级分类 -->
<view
v-for="category in parentCategories"
:key="category.id"
class="category-card"
@click="onParentCategoryClick(category)"
:style="{ '--card-color': category.color }"
>
<view class="card-icon">
<text class="card-icon-text">{{ category.icon }}</text>
</view>
<text class="card-name">{{ category.name }}</text>
</view>
</view>
<!-- 二级分类 -->
<view v-if="categoryTab === 'category' && showSubCategories && subCategories.length > 0" class="sub-category-grid">
<view class="sub-category-header">
<text class="sub-category-title">{{ selectedParentCategory?.name }}分类</text>
<text class="sub-category-close" @click="showSubCategories = false">✕</text>
</view>
<view class="sub-category-wrapper">
<view
v-for="subCat in subCategories"
:key="subCat.id"
class="sub-category-card"
@click="onSubCategoryClick(subCat)"
>
<view class="card-icon">
<text class="card-icon-text">{{ subCat.icon }}</text>
</view>
<text class="card-name">{{ subCat.name }}</text>
</view>
</view>
</view>
<!-- 品牌列表 -->
<view class="category-grid" v-if="categoryTab === 'brand'">
<view
v-for="brand in brands"
:key="brand.id"
class="category-card"
@click="switchBrand(brand)"
style="--card-color: #5785e5"
>
<view class="card-icon" v-if="brand.logo_url == null || brand.logo_url == ''">
<text class="card-icon-text">{{ getBrandIcon(brand.name) }}</text>
</view>
<image v-else :src="brand.logo_url" mode="aspectFit" class="brand-logo" style="width: 40px; height: 40px; border-radius: 20px;" />
<text class="card-name">{{ brand.name }}</text>
</view>
</view>
</view>
<!-- 健康资讯轮播 (Original Position - Removed) -->
<!-- 智能服务入口 (Hidden) -->
<!-- <view class="smart-services">
<view class="services-grid">
<view class="service-card" @click="navigateToConsultation">
<view class="service-icon" style="background: #2196F3;">
<text class="service-icon-text">👨‍⚕️</text>
</view>
<text class="service-name">在线问诊</text>
<text class="service-desc">三甲医生在线</text>
</view>
<view class="service-card" @click="navigateToPrescription">
<view class="service-icon" style="background: #4CAF50;">
<text class="service-icon-text">📋</text>
</view>
<text class="service-name">电子处方</text>
<text class="service-desc">医生开方购药</text>
</view>
<view class="service-card" @click="navigateToOTC">
<view class="service-icon" style="background: #FF9800;">
<text class="service-icon-text">💊</text>
</view>
<text class="service-name">非处方药</text>
<text class="service-desc">安全自主选购</text>
</view>
<view class="service-card" @click="navigateToHealthTools">
<view class="service-icon" style="background: #9C27B0;">
<text class="service-icon-text">🩺</text>
</view>
<text class="service-name">健康工具</text>
<text class="service-desc">健康管理助手</text>
</view>
</view>
</view> -->
<!-- 热销药品专区 -->
<view class="hot-products">
<view class="section-header">
<view class="title-section">
<text class="section-icon">🔥</text>
<text class="section-title">热销商品</text>
</view>
<view class="sort-tabs">
<text
v-for="tab in sortTabs"
:key="tab.id"
:class="['sort-tab', { active: activeSort === tab.id }]"
@click="switchSort(tab.id)"
>
{{ tab.name }}
</text>
</view>
</view>
<view class="products-grid">
<view
v-for="product in hotProducts"
:key="product.id"
class="product-card"
@click="navigateToProduct(product)"
>
<image
class="product-image"
:src="product.main_image_url"
mode="aspectFill"
/>
<text class="product-name" :lines="2">{{ product.name }}</text>
<view class="product-bottom">
<text class="product-price">¥{{ product.price }}</text>
<view class="product-add-btn" @click.stop="addToCart(product)">
<text class="add-icon">+</text>
</view>
</view>
</view>
</view>
<!-- 加载状态提示 -->
<view class="load-more-status" v-if="loading || showLoadMore">
<text class="loading-text">正在加载更多商品...</text>
</view>
</view>
<!-- 家庭常备药 (Hidden) -->
<!-- <view class="family-medicine">
<view class="section-header">
<view class="title-section">
<text class="section-icon">🏠</text>
<text class="section-title">家庭常备药</text>
</view>
<text class="section-subtitle">守护全家健康</text>
</view>
<view class="family-grid">
<view
v-for="item in familyItems"
:key="item.id"
class="family-item"
@click="navigateToCategory(item)"
>
<view class="family-icon" :style="{ backgroundColor: item.color }">
<text class="family-icon-text">{{ item.icon }}</text>
</view>
<text class="family-name">{{ item.name }}</text>
<text class="family-desc">{{ item.desc }}</text>
</view>
</view>
</view> -->
<!-- 智能推荐模块已隐藏 -->
<!-- 健康提醒 (Hidden) -->
<!-- <view class="health-reminder">
<view class="reminder-content">
<text class="reminder-icon">⏰</text>
<view class="reminder-text">
<text class="reminder-title">健康提醒</text>
<text class="reminder-desc">您有1个待用药提醒点击查看详情</text>
</view>
<view class="reminder-action" @click="navigateToReminders">
<text class="action-text">查看</text>
</view>
</view>
</view> -->
<!-- 底部安全区域 -->
<view class="safe-area"></view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
import { ref, reactive, onMounted } from 'vue'
import { onShow, onLoad } from '@dcloudio/uni-app'
import supabaseService from '@/utils/supabaseService.uts'
import type { Product, Category, Brand } from '@/utils/supabaseService.uts'
import { getCurrentUser } from '@/utils/store.uts'
// 响应式数据
const statusBarHeight = ref(0)
const scrollHeight = ref(0)
const refreshing = ref(false)
const loading = ref(false)
const isFirstShow = ref(true)
const hasMore = ref(true)
const activeSort = ref('recommend') // 默认展示智能推荐
const activeFilter = ref('recommend')
const currentPage = ref(1)
// 数据源
const hotProducts = ref<Product[]>([])
const recommendedProducts = ref<Product[]>([])
const hotKeywords = ref<string[]>([])
// 屏幕尺寸检测
const isMobile = ref(false)
const showLoadMore = ref(false)
// 导航栏显示控制
const showNavbar = ref(true)
const lastScrollTop = ref(0)
const scrollThreshold = 30 // 降低滚动阈值,使其更灵敏
const scrollingUp = ref(false)
// 分类数据 - 从Supabase获取
const categoryTab = ref<string>('category')
const categories = ref<Category[]>([])
const brands = ref<Brand[]>([])
// 一级分类和二级分类
const parentCategories = ref<Category[]>([])
const subCategories = ref<Category[]>([])
const selectedParentCategory = ref<Category | null>(null)
const showSubCategories = ref(false)
type SortTab = {
id: string
name: string
}
// 排序标签
const sortTabs: SortTab[] = [
{ id: 'recommend', name: '智能推荐' },
{ id: 'sales', name: '销量' },
{ id: 'price', name: '价格' },
{ id: 'new', name: '新品' },
{ id: 'discount', name: '特价' }
]
// 健康资讯
const healthNews = [
{
id: 'news1',
title: '秋季流感预防指南,科学防护健康过冬',
tag: '健康科普',
image: 'https://picsum.photos/800/400?random=health1'
},
{
id: 'news2',
title: '家庭常备药清单,为家人健康保驾护航',
tag: '家庭用药',
image: 'https://picsum.photos/800/400?random=health2'
},
{
id: 'news3',
title: '慢性病科学管理,提高生活质量',
tag: '健康管理',
image: 'https://picsum.photos/800/400?random=health3'
}
]
// 获取一级分类数据
const loadCategories = async (): Promise<void> => {
try {
const categoriesData = await supabaseService.getParentCategories()
parentCategories.value = categoriesData
// 兼容其他使用 categories 的地方
categories.value = categoriesData
console.log('一级分类数据:', JSON.stringify(parentCategories.value))
} catch (error) {
console.error('加载分类数据失败:', error)
parentCategories.value = []
categories.value = []
}
}
// 获取二级分类数据
const loadSubCategories = async (parentId: string): Promise<void> => {
try {
console.log('[loadSubCategories] 开始加载二级分类, parentId:', parentId)
const subData = await supabaseService.getSubCategories(parentId)
console.log('[loadSubCategories] 获取到二级分类数量:', subData.length)
console.log('[loadSubCategories] 二级分类数据:', JSON.stringify(subData))
subCategories.value = subData
} catch (error) {
console.error('加载子分类数据失败:', error)
subCategories.value = []
}
}
// 点击一级分类
const onParentCategoryClick = async (category: Category): Promise<void> => {
console.log('[onParentCategoryClick] 点击一级分类:', category.name, 'id:', category.id)
// 检查是否有一级分类本身需要跳转的情况(例如:没有子分类,或者本身就是个链接)
// 这里暂时假设没有子分类时跳转
// 注意:需要先检查是否有子分类,这可能需要一次异步请求或预加载
// 这里简化逻辑:点击一级分类也尝试跳转,或者展开
// 如果已经选中,则切换显示/隐藏二级分类
if (selectedParentCategory.value != null && selectedParentCategory.value.id === category.id) {
if (subCategories.value.length === 0) {
// 没有子分类,直接跳转
console.log('没有二级分类,直接跳转')
uni.setStorageSync('selectedCategory', category.id)
uni.switchTab({
url: '/pages/mall/consumer/category'
})
return
}
console.log('[onParentCategoryClick] 切换显示状态')
showSubCategories.value = !showSubCategories.value
return
}
// 选中新的分类
selectedParentCategory.value = category
showSubCategories.value = true
// 加载二级分类
await loadSubCategories(category.id)
// 加载完成后再次检查
if (subCategories.value.length === 0) {
console.log('加载后发现没有二级分类,直接跳转')
uni.setStorageSync('selectedCategory', category.id)
uni.switchTab({
url: '/pages/mall/consumer/category'
})
}
}
}
// 点击二级分类
const onSubCategoryClick = (category: Category): void => {
// 跳转到分类页面
console.log('准备跳转分类:', category.id, category.name)
uni.setStorageSync('selectedCategory', category.id)
uni.switchTab({
url: '/pages/mall/consumer/category',
success: () => {
console.log('SwitchTab 成功')
},
fail: (err) => {
console.error('SwitchTab 失败:', err)
}
})
}
// 获取品牌数据
const loadBrands = async (): Promise<void> => {
try {
const brandsData = await supabaseService.getBrands()
brands.value = brandsData
} catch (e) {
console.error('加载品牌失败:', e)
brands.value = []
}
}
// 根据品牌名称获取图标
const getBrandIcon = (name: string): string => {
if (name == null || name === '') {
return '🏢'
}
// 常见品牌图标映射(使用数组方式避免 Object.keys 问题)
const iconKeys = ['感冒', '发烧', '咳嗽', '消炎', '维生素', '钙片', '胃药', '止痛', '过敏', '皮肤', '眼药水', '口腔', '血压', '血糖', '血脂', '保健', '养生', '减肥', '美容', '母婴', '儿童', '老人', '男性', '女性', '维生素C', '维生素D', '蛋白粉', '鱼油', '蜂胶', '阿胶', '红枣', '枸杞', '菊花', '金银花', '口罩', '消毒液', '体温计', '创可贴', '棉签']
const iconValues = ['💊', '🌡️', '😷', '🔬', '💊', '🦴', '🫁', '💉', '🌸', '🧴', '👁️', '🦷', '❤️', '🩸', '💓', '🧬', '🍵', '⚖️', '💅', '👶', '🧒', '👴', '♂️', '♀️', '🍊', '☀️', '🥛', '🐟', '🐝', '🍯', '🫘', '🌿', '🌼', '🌸', '😷', '🧴', '🌡️', '🩹', '🧺']
// 尝试精确匹配
for (let i = 0; i < iconKeys.length; i++) {
if (name === iconKeys[i]) {
return iconValues[i]
}
}
// 尝试模糊匹配
for (let i = 0; i < iconKeys.length; i++) {
if (name.indexOf(iconKeys[i]) !== -1) {
return iconValues[i]
}
}
// 默认返回品牌图标
return '🏢'
}
// 默认加载商品数量
const defaultLoadLimit: number = 6
// 前置声明内部加载函数
const doLoadHotProducts = async (targetLimit: number, resolve: (value: void) => void, reject: (reason?: any) => void): Promise<void> => {
try {
let products: Product[] = []
const limit = targetLimit
console.log('加载热销商品,当前排序方式:', activeSort.value, 'limit:', limit)
switch (activeSort.value) {
case 'sales':
console.log('调用 getHotProducts')
products = await supabaseService.getHotProducts(limit)
break
case 'price':
console.log('调用 getProductsByPrice')
// 按价格升序(从低到高)
products = await supabaseService.getProductsByPrice(limit, true)
break
case 'new':
console.log('调用 getProductsByNewest')
// 按创建时间,最新的在前
products = await supabaseService.getProductsByNewest(limit)
break
case 'recommend':
console.log('调用 getSmartRecommendations')
// 智能推荐(基于用户搜索历史、浏览历史、热销商品)
products = await supabaseService.getSmartRecommendations(limit)
break
case 'discount':
console.log('调用 getDiscountProducts')
// 特价商品badge为'特价'
products = await supabaseService.getDiscountProducts(limit)
break
default:
console.log('调用默认 getHotProducts')
products = await supabaseService.getHotProducts(limit)
}
console.log('加载到的商品数量:', products.length)
if (products.length > 0) {
console.log('Sample Product Merchant IDs:')
for (let i = 0; i < Math.min(products.length, 3); i++) {
const p = products[i]
console.log(` - Product: ${p.name}, MerchantID: ${p.merchant_id}`)
}
}
hotProducts.value = products
} catch (error) {
console.error('加载热销商品失败:', error)
hotProducts.value = []
}
}
// 获取热销商品(根据当前排序方式)
function loadHotProducts(targetLimit: number): Promise<void> {
return new Promise<void>((resolve, reject) => {
doLoadHotProducts(targetLimit, resolve, reject)
})
}
// 前置声明推荐商品加载函数
const doLoadRecommendedProducts = async (limit: number, resolve: (value: void) => void, reject: (reason?: any) => void): Promise<void> => {
recommendedProducts.value = await supabaseService.getRecommendedProducts(limit)
resolve()
}
// 获取推荐商品
function loadRecommendedProducts(limit: number): Promise<void> {
return new Promise<void>((resolve, reject) => {
doLoadRecommendedProducts(limit, resolve, reject)
})
}
// 加载热搜词
const loadHotKeywords = async (): Promise<void> => {
try {
const keywords = await supabaseService.getHotKeywords(10)
hotKeywords.value = keywords
console.log('加载热搜词:', keywords.length, '个')
} catch (error) {
console.error('加载热搜词失败:', error)
hotKeywords.value = []
}
}
// 点击热搜词进行搜索
const searchByKeyword = (keyword: string): void => {
uni.navigateTo({
url: `/pages/mall/consumer/search?keyword=${encodeURIComponent(keyword)}`
})
}
// 初始化数据
const initData = async () => {
// 首先确保用户资料已加载
try {
await getCurrentUser()
console.log('主页初始化:用户资料加载完成')
} catch (error) {
console.error('加载用户资料失败:', error)
}
await loadCategories()
await loadBrands()
await loadHotKeywords()
await loadHotProducts(defaultLoadLimit)
await loadRecommendedProducts(defaultLoadLimit)
}
// 家庭常备药
const familyItems = [
{
id: 'family1',
name: '创可贴',
desc: '伤口护理',
icon: '🩹',
color: '#FF5722',
categoryId: 'external'
},
{
id: 'family2',
name: '体温计',
desc: '健康监测',
icon: '🌡️',
color: '#2196F3',
categoryId: 'device'
},
{
id: 'family3',
name: '消毒酒精',
desc: '环境消毒',
icon: '🧪',
color: '#4CAF50',
categoryId: 'external'
},
{
id: 'family4',
name: '口罩',
desc: '日常防护',
icon: '😷',
color: '#607D8B',
categoryId: 'device'
},
{
id: 'family5',
name: '退热贴',
desc: '物理降温',
icon: '🧊',
color: '#00BCD4',
categoryId: 'cold'
},
{
id: 'family6',
name: '棉签纱布',
desc: '伤口处理',
icon: '🩹',
color: '#FF9800',
categoryId: 'external'
}
]
// 初始化页面
const initPage = () => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight
// 计算滚动区域高度 - 不再需要手动计算,使用 Flex 布局自动撑开
// scrollHeight.value = windowHeight - 50
// 检测屏幕尺寸
const screenWidth = systemInfo.screenWidth
isMobile.value = screenWidth < 768 // 小于768px为小屏幕
}
// 生命周期
onMounted(() => {
initPage()
initData()
})
// 页面显示时重置状态
onShow(() => {
console.log('=== index页面onShow被调用 ===')
console.log('主页重新显示,重置页面状态')
// 重置导航栏显示状态
showNavbar.value = true
lastScrollTop.value = 0
// 重置滚动位置到顶部
// 注意这里不能直接操作scroll-view的滚动位置
// 但可以重置一些页面状态
// 注意这里不再清除selectedCategory
// 让分类页面在成功读取后自行清除
// 这样可以确保分类页面能正确读取到传递的数据
// 每次页面显示时尝试更新用户资料
if (!isFirstShow.value) {
getCurrentUser().then(profile => {
if (profile != null) {
console.log('主页onShow用户资料更新成功')
} else {
console.log('主页onShow用户资料为空可能未登录')
}
}).catch(error => {
console.error('主页onShow加载用户资料失败:', error)
})
} else {
isFirstShow.value = false
console.log('主页首次显示跳过onShow中的用户资料检查交由initData处理')
}
console.log('=== index页面onShow执行完成 ===')
})
// 处理滚动事件
const handleScroll = (event: any) => {
const eventObj = event as UTSJSONObject
const detail = eventObj.get('detail') as UTSJSONObject
const scrollTop = detail.getNumber('scrollTop') ?? 0
const currentTime = Date.now()
// 判断滚动方向
if (scrollTop > lastScrollTop.value) {
// 向下滚动
scrollingUp.value = false
// 向下滚动超过阈值时隐藏导航栏
if (scrollTop > scrollThreshold && showNavbar.value) {
showNavbar.value = false
}
} else if (scrollTop < lastScrollTop.value) {
// 向上滚动
scrollingUp.value = true
// 向上滚动时显示导航栏
if (!showNavbar.value) {
showNavbar.value = true
}
}
// 滚动到顶部时强制显示导航栏
if (scrollTop <= 10) {
showNavbar.value = true
}
lastScrollTop.value = scrollTop
// 调试信息(开发时可启用)
// console.log(`Scroll: ${scrollTop}, ShowNavbar: ${showNavbar.value}, ScrollingUp: ${scrollingUp.value}`)
}
// 重置导航栏显示状态(例如点击回到顶部时)
const resetNavbar = () => {
showNavbar.value = true
lastScrollTop.value = 0
}
// 切换分类 - 跳转到分类页面并传递分类ID
const switchCategory = (category: any) => {
console.log('=== switchCategory函数开始执行 ===')
// 将 category 转换为 UTSJSONObject 以访问属性
const catObj = (category instanceof UTSJSONObject) ? (category as UTSJSONObject) : (JSON.parse(JSON.stringify(category)) as UTSJSONObject)
const categoryId = catObj.getString('id') ?? ''
const categoryName = catObj.getString('name') ?? ''
console.log('分类ID:', categoryId, '分类名称:', categoryName)
// 使用Storage传递参数确保switchTab后能被读取
uni.setStorageSync('selectedCategory', categoryId)
// 生成唯一的时间戳和随机参数,确保每次跳转都是新的页面
const timestamp = Date.now()
const randomParam = Math.random().toString(36).substring(2, 8)
// 构建带参数的URL直接通过URL传递分类信息
const url = `/pages/mall/consumer/category?categoryId=${categoryId}&name=${encodeURIComponent(categoryName)}&timestamp=${timestamp}&random=${randomParam}`
uni.switchTab({
url: '/pages/mall/consumer/category',
success: () => {
// 通过 Storage 传递参数已在上面设置
console.log('跳转分类页面成功categoryId:', categoryId)
}
})
}
const switchBrand = (brand: Brand) => {
// 假设跳转到搜索结果页或者分类页带 filter
uni.navigateTo({
url: `/pages/mall/consumer/search?keyword=${encodeURIComponent(brand.name)}&type=brand&brandId=${brand.id}`
})
}
// 切换排序
const switchSort = (sortId: string) => {
activeSort.value = sortId
hasMore.value = true // 重置加载更多状态
// 重新加载热销商品,排序由 Supabase 服务处理
loadHotProducts(defaultLoadLimit)
}
// 切换筛选器
const switchFilter = (filterId: string) => {
activeFilter.value = filterId
// 重新加载推荐商品,筛选由 Supabase 服务处理
loadRecommendedProducts(defaultLoadLimit)
}
// 查看新闻详情
const viewNewsDetail = (news: any) => {
uni.navigateTo({
url: `/pages/news/detail?id=${news.id}`
})
}
// 下拉刷新
const onRefresh = async () => {
refreshing.value = true
try {
// 重新加载数据
await initData()
} catch (e) {
console.error('刷新数据失败:', e)
} finally {
// 延迟关闭刷新动画,确保用户能看到刷新过程
setTimeout(() => {
refreshing.value = false
// 延迟显示提示,避免与动画冲突
setTimeout(() => {
uni.showToast({
title: '刷新成功',
icon: 'success'
})
}, 200)
}, 800)
}
}
// 加载更多
const loadMore = async () => {
console.log('=== 触发触底事件 ===')
if (loading.value) {
console.log('正在加载中,跳过')
return
}
showLoadMore.value = true
loading.value = true
try {
// 获取当前热销商品的数量
const currentCount = hotProducts.value.length
const nextLimit = currentCount + 6
console.log('开始加载更多,当前数量:', currentCount, '目标数量:', nextLimit)
// 加载更多热销商品
await loadHotProducts(nextLimit)
// 检查是否还有更多数据
if (hotProducts.value.length === currentCount) {
hasMore.value = false
uni.showToast({
title: '没有更多了',
icon: 'none'
})
} else {
// 还有数据,或者是刚加载了一批
/* uni.showToast({
title: '加载完成',
icon: 'success'
}) */
}
} catch (error) {
console.error('加载更多失败:', error)
} finally {
loading.value = false
// 稍微延迟隐藏加载条,让用户看到
setTimeout(() => {
showLoadMore.value = false
}, 500)
}
}
// 添加到购物车
const addToCart = async (product: any) => {
uni.showLoading({ title: '检查商品...' })
try {
// 将 product 转换为 UTSJSONObject 以访问属性
const prodObj = (product instanceof UTSJSONObject) ? (product as UTSJSONObject) : (JSON.parse(JSON.stringify(product)) as UTSJSONObject)
const productId = prodObj.getString('id') ?? ''
const merchantId = prodObj.getString('merchant_id') ?? ''
// 检查商品是否有SKU
const skus = await supabaseService.getProductSkus(productId)
uni.hideLoading()
if (skus.length > 0) {
// 有规格,提示并跳转到商品详情页选择规格
uni.showToast({
title: '请选择规格',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/mall/consumer/product-detail?id=' + productId
})
}, 500)
} else {
// 无规格,直接加入购物车
uni.showLoading({ title: '添加中...' })
const success = await supabaseService.addToCart(productId, 1, '', merchantId)
uni.hideLoading()
if (success) {
uni.showToast({
title: '已添加到购物车',
icon: 'success'
})
} else {
uni.showToast({
title: '添加失败,请先登录',
icon: 'none'
})
}
}
} catch (e) {
console.error('添加到购物车异常', e)
uni.hideLoading()
uni.showToast({
title: '操作异常',
icon: 'none'
})
}
}
// 扫码功能
const onScan = (): void => {
uni.scanCode({
success: (res) => {
console.log('扫码成功:', res)
uni.showToast({
title: '扫码成功: ' + res.result,
icon: 'none'
})
},
fail: (err) => {
console.error('扫码失败:', err)
}
})
}
// 相机功能
const onCamera = (): void => {
uni.chooseImage({
count: 1,
sourceType: ['camera'],
success: (res) => {
console.log('相机拍摄成功:', res.tempFilePaths[0])
uni.showToast({
title: '已拍摄,正在识别...',
icon: 'loading'
})
setTimeout(() => {
uni.showToast({
title: '识别成功',
icon: 'success'
})
}, 1000)
},
fail: (err) => {
console.error('相机调用失败:', err)
}
})
}
// 导航函数
const navigateToSearch = (): void => { uni.navigateTo({ url: '/pages/mall/consumer/search' }) }
const navigateToNews = (): void => { uni.navigateTo({ url: '/pages/news/list' }) }
const navigateToProduct = (product: any) => {
// 将 product 转换为 UTSJSONObject 以访问属性
const prodObj = (product instanceof UTSJSONObject) ? (product as UTSJSONObject) : (JSON.parse(JSON.stringify(product)) as UTSJSONObject)
// 使用productId如果存在作为跳转的商品ID否则使用id
const productId = prodObj.getString('productId') ?? prodObj.getString('id') ?? ''
const name = prodObj.getString('name') ?? ''
// 使用 main_image_url
const image = prodObj.getString('main_image_url') ?? prodObj.getString('image') ?? '/static/product1.jpg'
const price = (prodObj.getNumber('base_price') ?? prodObj.getNumber('price') ?? 0).toString()
const marketPrice = prodObj.getNumber('market_price') ?? prodObj.getNumber('original_price') ?? (parseFloat(price) * 1.2)
const originalPrice = marketPrice.toString()
// 手动构建URL避免双重编码问题
uni.navigateTo({
url: `/pages/mall/consumer/product-detail?id=${productId}&price=${price}&originalPrice=${originalPrice}&name=${encodeURIComponent(name)}&image=${encodeURIComponent(image)}`
})
}
const navigateToCategory = (item: any) => {
uni.navigateTo({
url: `/pages/mall/consumer/search?keyword=${encodeURIComponent(item.name)}&type=family`
})
}
const navigateToConsultation = () => uni.navigateTo({ url: '/pages/medicine/consultation' })
const navigateToPrescription = () => uni.navigateTo({ url: '/pages/medicine/prescription' })
const navigateToOTC = () => uni.navigateTo({ url: '/pages/medicine/otc' })
const navigateToHealthTools = () => uni.navigateTo({ url: '/pages/medicine/tools' })
const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders' })
</script>
<style>
/* 全局重置 removed - uniapp-x does not support * selector */
/* .medic-home * {
box-sizing: border-box;
margin: 0;
padding: 0;
} */
.medic-home {
width: 100%;
height: 100%;
overflow: hidden;
background: #f8fafc;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
line-height: 1.5;
display: flex;
flex-direction: column;
}
.main-scroll {
flex: 1;
height: 1px; /* 让 flex 生效并允许滚动 */
width: 100%;
}
/* 智能导航栏 - 重新设计布局 */
.smart-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
z-index: 1000;
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15); /* 调整为与分类页一致 */
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
/* will-change: transform, opacity; removed for uniapp-x support */
/* pointer-events: auto; */
/* backface-visibility: hidden; */
/* -webkit-backface-visibility: hidden; */
}
/* 导航栏搜索框容器内边距调整 */
.search-container {
height: 44px;
padding: 0 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
/* 搜索框 hover 效果 */
.search-box:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 导航栏搜索框容器内边距调整 */
.search-box {
flex: 1;
max-width: 600px;
background: #f0f0f0;
border-radius: 20px;
padding: 0 4px 0 12px;
display: flex;
flex-direction: row; /* UVUE 显式设置 row */
align-items: center;
/* cursor: pointer; removed for uniapp-x support */
transition: all 0.3s ease;
width: 100%;
height: 32px; /* 减小高度与顶部高度44px适配略小于顶部高度 */
}
.search-placeholder {
font-size: 14px;
color: #999;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.nav-icon-btn {
padding: 4px 8px 4px 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-right: 1px solid #ddd;
margin-right: 8px;
}
.nav-icon {
font-size: 18px;
}
/* 搜索按钮高度微调 */
.nav-inner-search-btn {
padding: 0 12px; /* 减小内边距 */
background-color: #87CEEB; /* 天空蓝 */
border-radius: 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 24px; /* 随搜索框高度减小而减小 */
}
.nav-inner-search-text {
font-size: 12px; /* 字体稍微变小 */
color: #ffffff;
font-weight: normal;
}
/* 导航栏占位符 */
.navbar-placeholder {
width: 100%;
flex-shrink: 0;
}
.nav-camera-btn {
padding: 4px 8px 4px 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-right-width: 1px;
border-right-style: solid;
border-right-color: #ddd;
border-right: 1px solid #ddd; /* 修复UVUE样式 */
margin-right: 8px;
}
.nav-camera-icon {
font-size: 20px;
}
/* 主内容区域 */
.main-scroll {
flex: 1;
padding: 0 16px 16px;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
width: 100%;
}
/* 智能健康卡片 */
.smart-health-card {
background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
/* margin-top 由 style 动态控制 */
color: white;
}
.health-content {
display: flex;
flex-direction: column;
/* gap: 12px; removed for uniapp-x support */
}
.health-header {
display: flex;
flex-direction: column;
/* gap: 4px; removed for uniapp-x support */
margin-bottom: 12px; /* acts as gap for health-content */
}
.health-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 4px; /* acts as gap for health-header */
}
.health-subtitle {
font-size: 14px;
opacity: 0.9;
}
.health-tips {
display: flex;
flex-wrap: wrap;
/* gap: 12px; removed for uniapp-x support */
margin-top: 8px;
}
.tip-item {
font-size: 13px;
padding: 6px 12px;
background: rgba(255, 255, 255, 0.2);
border-radius: 20px;
/* backdrop-filter: blur(10px); removed for uniapp-x support */
margin-right: 12px;
margin-bottom: 12px; /* acts as gap for health-tips */
}
/* 智能分类网格 */
.smart-categories {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20px;
}
.category-tabs-pills {
display: flex;
flex-direction: row;
background-color: #f0f2f5;
padding: 3px;
border-radius: 20px;
align-items: center;
}
.tab-pill {
padding: 6px 18px;
border-radius: 17px;
transition: all 0.3s;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.tab-pill.active {
background-color: #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.tab-text {
font-size: 14px;
color: #888;
font-weight: normal;
}
.tab-pill.active .tab-text {
color: #4CAF50;
font-weight: bold;
}
.section-title {
font-size: 18px;
font-weight: bold;
color: #666;
transition: color 0.3s;
}
.section-title.active {
color: #4CAF50;
font-size: 20px;
}
.section-desc {
font-size: 14px;
color: #666;
}
/* 分类网格布局 */
.category-grid {
display: flex;
flex-direction: row; /* Ensure items are in row */
flex-wrap: wrap;
/* gap: 16px; removed for uniapp-x support */
margin: 0 -1.5%;
}
.category-card {
width: 18%; /* 一行5个 */
margin: 0 1% 12px 1%;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
background: #f8f9fa;
border-radius: 10px;
/* cursor: pointer; removed for uniapp-x support */
transition: all 0.3s ease;
border: 1px solid transparent;
position: relative;
}
.category-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border-color: var(--card-color, #4CAF50);
}
/* 二级分类样式 */
.sub-category-grid {
background: #f8f9fa;
border-radius: 12px;
padding: 16px;
margin-top: 16px;
}
.sub-category-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #e0e0e0;
}
.sub-category-title {
font-size: 14px;
font-weight: bold;
color: #333;
}
.sub-category-close {
font-size: 16px;
color: #999;
padding: 4px 8px;
}
.sub-category-wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
}
.sub-category-card {
width: 23%;
background: white;
border-radius: 8px;
padding: 10px 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px solid #eee;
margin-right: 2%;
margin-bottom: 10px;
}
.sub-category-card .card-icon {
width: 36px;
height: 36px;
border-radius: 18px;
margin-bottom: 6px;
display: flex;
align-items: center;
justify-content: center;
}
.sub-category-card .card-icon-text {
font-size: 18px;
}
.sub-category-card .card-name {
font-size: 11px;
color: #333;
text-align: center;
lines: 1;
text-overflow: ellipsis;
}
.card-icon {
width: 44px;
height: 44px;
border-radius: 22px;
background: var(--card-color, #4CAF50);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}
.card-icon-text {
font-size: 20px;
color: white;
}
.card-name {
font-size: 12px;
font-weight: normal;
color: #333;
margin-bottom: 4px;
text-align: center;
width: 100%;
overflow-wrap: break-word;
}
.card-desc {
font-size: 12px;
color: #666;
text-align: center;
}
/* 二级分类样式 */
.sub-category-grid {
background: #f8f9fa;
border-radius: 12px;
padding: 16px;
margin-top: 16px;
}
.sub-category-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #e0e0e0;
}
.sub-category-title {
font-size: 14px;
font-weight: bold;
color: #333;
}
.sub-category-close {
font-size: 16px;
color: #999;
padding: 4px 8px;
}
.sub-category-wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
}
.sub-category-card {
width: 23%;
background: white;
border-radius: 8px;
padding: 10px 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px solid #eee;
margin-right: 2%;
margin-bottom: 10px;
}
.sub-category-card .card-icon {
width: 36px;
height: 36px;
border-radius: 18px;
margin-bottom: 6px;
display: flex;
align-items: center;
justify-content: center;
}
.sub-category-card .card-icon-text {
font-size: 18px;
}
.sub-category-card .card-name {
font-size: 11px;
color: #333;
text-align: center;
lines: 1;
text-overflow: ellipsis;
}
/* 健康资讯 */
.health-news {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.news-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.news-title {
font-size: 18px;
font-weight: bold;
color: #333;
}
.news-more {
font-size: 14px;
color: #4CAF50;
/* cursor: pointer; removed for uvue support */
}
.news-swiper {
height: 200px;
border-radius: 12px;
overflow: hidden;
}
.news-content {
position: relative;
height: 100%;
border-radius: 12px;
overflow: hidden;
}
.news-image {
width: 100%;
height: 100%;
display: flex;
}
.news-caption {
font-size: 16px;
font-weight: bold;
line-height: 1.4;
display: flex;
}
/* 智能服务 */
.smart-services {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.services-grid {
display: flex;
flex-direction: row; /* Ensure items are in row */
flex-wrap: wrap;
/* gap: 20px; removed for uniapp-x support */
margin: 0 -1.5%;
}
.service-card {
width: 47%; /* 50 - 3 */
margin: 0 1.5% 20px 1.5%;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: #f8f9fa;
border-radius: 12px;
/* cursor: pointer; removed for uvue support */
transition: all 0.3s ease;
}
.service-card:hover {
transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.service-icon {
width: 60px;
height: 60px;
border-radius: 30px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.service-icon-text {
font-size: 28px;
color: white;
}
.service-name {
font-size: 15px;
font-weight: bold;
color: #333;
margin-bottom: 4px;
}
.service-desc {
font-size: 12px;
color: #666;
}
/* 热搜词区域 */
.hot-keywords-section {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
}
.keywords-list {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 15px;
}
.keyword-item {
display: flex;
align-items: center;
padding: 8px 16px;
background: #f5f5f5;
border-radius: 20px;
cursor: pointer;
transition: all 0.2s ease;
}
.keyword-item:hover {
background: #fff0f0;
}
.keyword-rank {
width: 20px;
height: 20px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
color: #999;
background: #eee;
margin-right: 8px;
}
.keyword-rank.top-three {
background: #ff4757;
color: white;
}
.keyword-text {
font-size: 14px;
color: #333;
}
/* 热销药品 */
.hot-products {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.section-header {
display: flex;
flex-direction: column; /* 标题和筛选器垂直排列 */
align-items: flex-start;
margin-bottom: 20px;
width: 100%;
}
.title-section {
display: flex;
align-items: center;
/* gap: 8px; removed */
width: 100%;
}
.section-icon {
font-size: 20px;
color: #4CAF50;
margin-right: 8px; /* Replacement for gap */
}
.sort-tabs {
display: flex;
flex-direction: row; /* UVUE 显式设置 row */
/* gap: 8px; removed */
align-items: center;
flex-wrap: wrap; /* 允许换行,实现自适应 */
justify-content: flex-start;
width: 100%;
margin-top: 12px;
}
.sort-tab {
font-size: 13px;
color: #666;
padding: 8px 12px; /* 增加左右内边距 */
border-radius: 20px;
border: 1px solid #e0e0e0;
/* cursor: pointer; removed for uvue support */
transition: all 0.2s ease;
white-space: nowrap;
flex: 1; /* 均分宽度 */
min-width: 70px; /* 设置最小宽度防止过窄 */
text-align: center;
display: flex;
justify-content: center;
align-items: center;
margin-right: 8px; /* Replacement for gap */
}
.sort-tab.active {
background: #4CAF50;
color: white;
border-color: #4CAF50;
}
.sort-tab:hover {
background: #f5f5f5;
}
.sort-tab.active:hover {
background: #388E3C;
}
/* 产品网格 */
.products-grid {
display: flex; /* 替换 block 为 flex */
flex-direction: row; /* 确保横向排列 */
flex-wrap: wrap; /* 确保网格布局 */
/* gap: 10px; removed for uniapp-x support */
justify-content: space-between; /* use space-between instead of gap */
margin-top: 20px;
min-height: 500px; /* 确保有足够高度触发滚动 */
padding-bottom: 20px;
}
.product-card {
display: flex;
flex-direction: column;
background: #fff;
border-radius: 8px;
overflow: hidden;
width: 48%;
margin-bottom: 12px;
}
.product-image {
width: 100%;
height: 170px;
border-radius: 8px;
margin-bottom: 8px;
background: #f5f5f5;
}
.product-name {
font-size: 13px;
color: #333;
margin-bottom: 5px;
line-height: 1.4;
height: 36px;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 8px;
}
.product-bottom {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 8px 8px;
}
.product-price {
font-size: 15px;
color: #ff5000;
font-weight: bold;
}
.product-add-btn {
width: 24px;
height: 24px;
background-color: #ff5000;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.add-icon {
color: #fff;
font-size: 16px;
font-weight: bold;
}
.cart-btn {
display: flex;
align-items: center;
justify-content: center;
/* gap: 6px; removed */
background: #4CAF50;
color: white;
padding: 8px 12px;
border-radius: 8px;
font-size: 13px;
font-weight: bold;
/* cursor: pointer; removed for uvue support */
transition: all 0.2s ease;
}
.cart-btn:hover {
background: #388E3C;
}
.cart-icon {
font-size: 14px;
margin-right: 6px; /* Replacement for gap */
}
.cart-text {
font-size: 13px;
}
/* 家庭常备药 */
.family-medicine {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.section-subtitle {
font-size: 14px;
color: #666;
margin-left: 12px;
}
.family-grid {
display: flex;
flex-direction: row; /* Ensure items are in row */
flex-wrap: wrap;
/* gap: 16px; removed for uniapp-x support */
margin: 0 -1.5%;
margin-top: 20px;
}
.family-item {
width: 47%; /* 50 - 3 */
margin: 0 1.5% 16px 1.5%;
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
background: #f8f9fa;
border-radius: 12px;
/* cursor: pointer; removed for uvue support */
transition: all 0.3s ease;
}
.family-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.family-icon {
width: 48px;
height: 48px;
border-radius: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.family-icon-text {
font-size: 20px;
color: white;
}
.family-name {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 4px;
}
.family-desc {
font-size: 12px;
color: #666;
}
/* 智能推荐 */
.smart-recommend {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.recommend-filters {
display: flex;
flex-direction: row; /* UVUE 显式设置 row */
/* gap: 8px; removed for uniapp-x support */
align-items: center;
flex-wrap: wrap; /* 允许换行,实现自适应 */
justify-content: flex-start;
width: 100%;
margin-top: 12px;
}
.filter-item {
font-size: 13px;
color: #666;
padding: 8px 12px; /* 增加左右内边距 */
border-radius: 20px;
border: 1px solid #e0e0e0;
/* cursor: pointer; removed for uvue support */
transition: all 0.2s ease;
white-space: nowrap;
flex: 1; /* 均分宽度 */
min-width: 80px; /* 设置最小宽度防止过窄 */
text-align: center;
display: flex;
justify-content: center;
align-items: center;
margin-right: 8px;
margin-bottom: 8px;
}
.filter-item.active {
background: #4CAF50;
color: white;
border-color: #4CAF50;
}
.filter-item:hover {
background: #f5f5f5;
}
.filter-item.active:hover {
background: #388E3C;
}
.recommend-grid {
display: flex;
flex-direction: row; /* Ensure items are in row */
flex-wrap: wrap;
/* gap: 20px; removed for uniapp-x support */
margin: 0 -1.5%;
margin-top: 20px;
}
.recommend-product {
width: 97%; /* 1 col */
margin: 0 1.5% 20px 1.5%;
background: #f8f9fa;
border-radius: 12px;
overflow: hidden;
/* cursor: pointer; removed for uvue support */
transition: all 0.3s ease;
border: 1px solid #e0e0e0;
}
.recommend-product:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.product-image-container {
position: relative;
height: 180px;
}
.product-image-container .product-image {
width: 100%;
height: 100%;
background: white;
}
.product-tags {
position: absolute;
top: 12px;
left: 12px;
display: flex;
flex-direction: row;
/* gap: 8px; removed */
}
.product-tag, .featured-tag {
padding: 4px 10px;
border-radius: 10px;
font-size: 11px;
font-weight: bold;
color: white;
margin-right: 8px;
}
.product-tag {
background: rgba(76, 175, 80, 0.9);
}
.featured-tag {
background: rgba(255, 87, 34, 0.9);
}
.product-details {
padding: 16px;
}
.product-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 4px;
line-height: 1.4;
display: flex;
}
.product-specification {
font-size: 13px;
color: #666;
margin-bottom: 12px;
display: flex;
}
.product-rating {
display: flex;
flex-direction: row;
align-items: center;
/* gap: 8px; removed */
margin-bottom: 12px;
font-size: 13px;
}
.rating-stars {
display: flex;
flex-direction: row;
align-items: center;
/* gap: 4px; removed */
margin-right: 8px;
}
.star-icon {
font-size: 14px;
color: #FFC107;
margin-right: 2px;
}
.rating-value {
font-weight: bold;
color: #333;
}
.reviews-count {
color: #666;
}
.product-actions {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
.add-to-cart {
width: 36px;
height: 36px;
border-radius: 18px;
background: #4CAF50;
display: flex;
align-items: center;
justify-content: center;
/* cursor: pointer; removed for uvue support */
transition: all 0.2s ease;
}
.add-to-cart:hover {
background: #388E3C;
transform: scale(1.1);
}
.cart-icon {
font-size: 16px;
color: white;
}
/* 健康提醒 */
.health-reminder {
background: linear-gradient(135deg, #FF9800 0%, #F57C00 100%);
border-radius: 16px;
padding: 16px 20px;
margin-bottom: 20px;
color: white;
}
.reminder-content {
display: flex;
flex-direction: row;
align-items: center;
/* gap: 12px; removed */
}
.reminder-icon {
font-size: 24px;
margin-right: 12px;
}
.reminder-text {
flex: 1;
display: flex;
flex-direction: column;
/* gap: 4px; removed */
}
.reminder-title {
font-size: 15px;
font-weight: bold;
margin-bottom: 4px;
}
.reminder-desc {
font-size: 13px;
opacity: 0.9;
}
.reminder-action {
padding: 6px 16px;
background: rgba(255, 255, 255, 0.2);
border-radius: 20px;
/* cursor: pointer; removed for uvue support */
transition: all 0.2s ease;
}
.reminder-action:hover {
background: rgba(255, 255, 255, 0.3);
}
.action-text {
font-size: 13px;
font-weight: bold;
}
/* 加载状态 */
.loading-state {
padding: 40px 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f0f0f0;
border-top-color: #4CAF50;
border-radius: 16px;
margin-bottom: 12px;
}
.loading-text {
font-size: 14px;
color: #666;
}
.no-more {
padding: 30px 0;
text-align: center;
border-top: 1px solid #f0f0f0;
margin-top: 10px;
}
.no-more-text {
font-size: 13px;
color: #999;
}
/* 安全区域 */
.safe-area {
height: 20px;
width: 100%;
}
/* ===== 响应式设计 ===== */
/* 小屏手机 (小于414px) */
@media screen and (max-width: 414px) {
.search-container {
padding: 0 12px;
height: 44px;
}
.search-box {
padding: 0 4px 0 12px;
margin: 0;
}
.main-scroll {
padding: 0 12px 12px;
}
.category-grid {
/* grid-template-columns: repeat(5, 1fr); removed for uniapp-x support */
/* gap: 8px; removed */
margin: 0 -1%;
padding: 0 4px;
}
.category-grid .category-card {
width: 18%; /* 5 cols : 20 - 2 */
margin: 0 1% 8px 1%;
padding: 8px 0;
background: transparent; /* 移除卡片背景 */
box-shadow: none; /* 移除阴影 */
border: none; /* 移除边框 */
}
.category-card:hover {
transform: none; /* 移动端移除悬停效果 */
box-shadow: none;
}
.card-icon {
width: 44px; /* 减小图标尺寸 */
height: 44px;
border-radius: 22px;
margin-bottom: 6px;
}
.card-icon-text {
font-size: 20px;
}
.card-name {
font-size: 11px; /* 减小文字大小 */
font-weight: normal;
color: #333;
}
.card-desc {
display: none; /* 手机端隐藏描述文字,保持界面整洁 */
}
.services-grid .service-card {
width: 23%; /* 4 cols */
margin: 0 1% 8px 1%;
}
.service-card {
padding: 10px 4px;
background: #f8f9fa; /* 保持淡色背景 */
}
.service-icon {
width: 40px;
height: 40px;
border-radius: 20px;
margin-bottom: 8px;
}
.service-icon-text {
font-size: 20px;
}
.service-name {
font-size: 11px;
}
.service-desc {
display: none; /* 隐藏描述 */
}
.load-more-status {
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.loading-text {
color: #888;
font-size: 14px;
}
.products-grid {
/* column-count: 2; removed for flex */
/* column-gap: 8px; removed */
}
.recommend-grid .recommend-product {
width: 48%;
margin: 0 1% 8px 1%;
}
.product-info,
.product-details {
padding: 6px; /* 极小内边距 */
}
.product-name,
.product-title {
font-size: 13px; /* 调整字体大小 */
height: 36px; /* 限制高度,防止参差不齐 */
overflow: hidden;
text-overflow: ellipsis;
/* display: webkit-box removed for compatibility */
display: flex;
white-space: nowrap;
}
.product-image,
.product-image-container {
height: 140px; /* 稍微减小图片高度适配双列 */
}
/* 手机端商品卡片极简模式(热销 & 推荐) */
.hot-products .product-spec,
.hot-products .manufacturer,
.hot-products .original-price,
.hot-products .cart-text,
.hot-products .sales-info,
.hot-products .product-action, /* 隐藏热销区加购按钮 */
.smart-recommend .product-specification,
.smart-recommend .product-rating,
.smart-recommend .original-price,
.smart-recommend .product-actions /* 隐藏推荐区加购按钮 */
{
display: none;
}
.hot-products .product-info,
.smart-recommend .product-details {
padding: 6px; /* 极小内边距 */
}
.hot-products .product-image,
.hot-products .product-image-container,
.smart-recommend .product-image,
.smart-recommend .product-image-container {
height: 110px; /* 进一步减小图片高度 */
}
.hot-products .product-name,
.smart-recommend .product-title {
margin-bottom: 2px;
font-size: 12px;
line-height: 1.3;
height: 32px; /* 限制2行高度 */
}
.hot-products .price-section,
.smart-recommend .price-section {
margin-bottom: 0;
margin-top: 4px;
}
.hot-products .price-symbol,
.smart-recommend .price-symbol {
font-size: 10px;
color: #FF5722;
}
.hot-products .price-value,
.smart-recommend .price-value {
font-size: 14px; /* 字体变小 */
font-weight: bold;
}
.family-grid .family-item {
width: 23%; /* 4 cols */
margin: 0 1% 8px 1%;
padding: 8px 4px;
background: #f8f9fa;
}
.family-icon {
width: 36px;
height: 36px;
border-radius: 18px;
margin-bottom: 6px;
}
.family-icon-text {
font-size: 18px;
}
.family-name {
font-size: 11px;
}
.family-desc {
display: none;
}
.news-swiper {
height: 160px;
}
.sort-tabs,
.recommend-filters {
/* gap: 8px; removed */
justify-content: flex-start; /* 保持左对齐 */
/* overflow-x: auto; REMOVED for uniapp-x support - use generic view wrapping instead */
/* flex-wrap: nowrap; REMOVED to allow wrapping on mobile since overflow-x not supported on view */
padding-bottom: 4px; /* 滚动条空间 */
}
.sort-tab,
.filter-item {
padding: 5px 12px;
font-size: 12px;
flex-shrink: 0; /* 防止被压缩 */
min-width: 0; /* CHANGED from auto to 0 */
flex: 0 0 auto; /* 取消均分 */
margin-right: 8px;
margin-bottom: 8px; /* Added spacing for wrapped items */
}
.section-header {
flex-direction: column;
align-items: stretch;
/* gap: 12px; removed */
}
.title-section {
justify-content: center;
margin-bottom: 12px;
}
.sort-tabs,
.recommend-filters {
width: 100%;
justify-content: center;
}
}
/* 中屏手机/小平板 (415px-768px) */
@media screen and (min-width: 415px) and (max-width: 768px) {
.search-container {
padding: 0 16px;
height: 44px;
}
.main-scroll {
/* 移除 margin-top */
}
.category-grid .category-card {
width: 30.33%;
}
.services-grid .service-card {
width: 47%;
}
.products-grid {
/* column-count: 2; removed */
}
.recommend-grid .recommend-product {
width: 47%;
}
.family-grid .family-item {
width: 30.33%;
}
.sort-tabs,
.recommend-filters {
/* gap: 10px; removed */
justify-content: flex-start;
}
.sort-tab,
.filter-item {
padding: 6px 14px;
font-size: 12px;
flex-grow: 0;
margin-right: 10px;
}
.section-header {
flex-direction: column;
align-items: stretch;
/* gap: 12px; removed */
}
.title-section {
justify-content: center;
margin-bottom: 12px;
}
.sort-tabs,
.recommend-filters {
width: 100%;
justify-content: center;
}
}
/* 平板设备 (769px-1024px) */
@media screen and (min-width: 769px) and (max-width: 1024px) {
.search-container {
padding: 0 24px;
height: 44px;
}
.nav-search-tools .nav-tool-item {
display: none;
}
.main-scroll {
padding: 0 24px 20px;
}
.category-grid .category-card {
width: 22%;
}
.services-grid .service-card {
width: 22%;
}
.product-card {
/* width: calc((100% - 20px) / 3); */
width: 32%; /* Fallback for calc */
/* margin-right: 1.33%; */
}
.recommend-grid .recommend-product {
width: 47%;
}
.family-grid .family-item {
width: 30.33%;
}
.news-swiper {
height: 240px;
}
}
/* 桌面端 (1025px以上) */
@media screen and (min-width: 1025px) {
.search-container {
padding: 0 32px;
height: 44px;
}
.main-scroll {
padding: 0 32px 24px;
}
.category-grid .category-card {
width: 13.66%;
}
.services-grid .service-card {
width: 22%;
}
.product-card {
/* width: calc((100% - 30px) / 4); */
width: 23%;
/* margin-right: 2%; */
}
.recommend-grid .recommend-product {
width: 30.33%;
}
.family-grid .family-item {
width: 30.33%;
}
.news-swiper {
height: 260px;
}
}
/* 大桌面端 (1400px以上) */
@media screen and (min-width: 1400px) {
.category-grid {
margin-right: -24px;
}
.category-grid .category-card {
width: 17%;
margin: 0 1.5% 24px 1.5%;
}
.product-card {
/* width: calc((100% - 30px) / 4); */
width: 23%;
/* margin-right: 2%; */
}
.recommend-grid .recommend-product {
width: 22%;
}
}
/* 暗黑模式适配 */
@media (prefers-color-scheme: dark) {
.medic-home {
background: #121212;
}
.smart-categories,
.health-news,
.smart-services,
.hot-products,
.family-medicine,
.smart-recommend {
background: #1e1e1e;
border-color: #333;
}
.nav-search-box {
background: rgba(30, 30, 30, 0.95);
border-color: #444;
}
.category-card,
.service-card,
.product-card,
.family-item,
.recommend-product {
background: #2d2d2d;
border-color: #444;
}
.section-title,
.card-name,
.service-name,
.product-name,
.family-name,
.product-title,
.section-desc,
.card-desc,
.service-desc,
.product-spec,
.product-specification,
.family-desc {
color: #aaa;
}
.sort-tab,
.filter-item {
background: #333;
border-color: #444;
color: #ccc;
}
.sort-tab.active,
.filter-item.active {
background: #4CAF50;
color: white;
}
.sort-tab:hover,
.filter-item:hover {
background: #444;
}
.sort-tab.active:hover,
.filter-item.active:hover {
background: #388E3C;
}
.nav-tool-item {
background: rgba(76, 175, 80, 0.2);
color: #4CAF50;
}
.manufacturer,
.sales-count,
.reviews-count,
.original-price {
color: #888;
}
}
</style>