Files
medical-mall/pages/mall/consumer/index紧凑.uvue
2026-01-23 16:47:05 +08:00

1793 lines
35 KiB
Plaintext

<!-- pages/mall/consumer/index.uvue -->
<template>
<view class="medicine-home">
<!-- 顶部导航栏 -->
<view class="medicine-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-content">
<view class="nav-left">
<view class="logo-area">
<text class="logo-icon">💊</text>
<text class="logo-text">康乐医药商城</text>
</view>
<view class="slogan">
<text class="slogan-text">健康生活,品质保障</text>
</view>
</view>
<view class="nav-right">
<view class="user-actions">
<view class="action-item" @click="navigateToSearch">
<text class="action-icon search-icon">🔍</text>
</view>
<view class="action-item" @click="navigateToOrders">
<text class="action-icon order-icon">📋</text>
<text v-if="pendingOrders > 0" class="badge">{{ pendingOrders }}</text>
</view>
<view class="action-item" @click="navigateToCart">
<text class="action-icon cart-icon">🛒</text>
<text v-if="cartCount > 0" class="badge cart-badge">{{ cartCount }}</text>
</view>
<view class="user-info" @click="showUserMenu">
<image
class="user-avatar"
:src="userInfo.avatar || '/static/default-avatar.png'"
mode="aspectFill"
/>
</view>
</view>
</view>
</view>
</view>
<!-- 主内容区 -->
<scroll-view
scroll-y
class="main-content"
:style="{ height: scrollViewHeight + 'px' }"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="loadMore"
>
<!-- 健康提示区域 -->
<view class="health-tip-section">
<view class="tip-content">
<text class="tip-icon">💡</text>
<text class="tip-text">今日健康小贴士:按时服药,保持良好生活习惯</text>
</view>
</view>
<!-- 药品分类区域 - 改为网格布局 -->
<view class="medicine-category-section">
<view class="section-header">
<text class="section-title">药品分类</text>
<text class="section-subtitle">快速找到所需药品</text>
</view>
<!-- PC端和手机端使用不同的布局 -->
<view class="category-grid" v-if="isMobile">
<view
v-for="(category, index) in medicineCategories"
:key="category.id"
:class="['category-item', { active: activeCategory === category.id }]"
@click="switchCategory(category.id, index)"
>
<view class="category-icon-wrapper" :style="{ backgroundColor: category.color }">
<text class="category-icon">{{ category.icon }}</text>
</view>
<text class="category-name">{{ category.name }}</text>
</view>
</view>
<scroll-view
v-else
scroll-x
class="category-scroll"
:show-scrollbar="false"
:scroll-left="scrollLeft"
@scroll="onScroll"
>
<view class="category-container">
<view
v-for="(category, index) in medicineCategories"
:key="category.id"
:class="['category-item', { active: activeCategory === category.id }]"
@click="switchCategory(category.id, index)"
>
<view class="category-icon-wrapper" :style="{ backgroundColor: category.color }">
<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="health-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="#4CAF50"
>
<swiper-item v-for="(banner, index) in healthBanners" :key="banner.id">
<image
class="banner-image"
:src="banner.image_url"
mode="aspectFill"
/>
<view class="banner-overlay">
<text class="banner-title">{{ banner.title }}</text>
<text class="banner-desc">{{ banner.desc }}</text>
</view>
</swiper-item>
</swiper>
</view>
<!-- 快捷服务 -->
<view class="quick-service-section">
<view class="service-grid">
<view class="service-item" @click="navigateToConsultation">
<view class="service-icon doctor">👨‍⚕️</view>
<text class="service-text">在线问诊</text>
</view>
<view class="service-item" @click="navigateToPrescription">
<view class="service-icon prescription">📝</view>
<text class="service-text">处方购药</text>
</view>
<view class="service-item" @click="navigateToOTC">
<view class="service-icon otc">💊</view>
<text class="service-text">非处方药</text>
</view>
<view class="service-item" @click="navigateToHealthTools">
<view class="service-icon tool">🩺</view>
<text class="service-text">健康工具</text>
</view>
</view>
</view>
<!-- 特价药品 -->
<view class="special-offer-section">
<view class="section-header">
<view class="header-left">
<text class="section-icon">🎯</text>
<text class="section-title">特价药品</text>
</view>
<view class="countdown-timer">
<text class="countdown-text">特惠倒计时</text>
<view class="time-display">
<text class="time-unit">{{ countdown.hours }}</text>
<text class="time-separator">:</text>
<text class="time-unit">{{ countdown.minutes }}</text>
<text class="time-separator">:</text>
<text class="time-unit">{{ countdown.seconds }}</text>
</view>
</view>
</view>
<view class="medicine-grid">
<view
v-for="(medicine, index) in specialOfferMedicines"
:key="medicine.id"
class="medicine-card"
@click="navigateToMedicineDetail(medicine)"
>
<view class="medicine-image-container">
<image
class="medicine-image"
:src="medicine.image"
mode="aspectFill"
/>
<view class="medicine-tag" v-if="medicine.tag">{{ medicine.tag }}</view>
</view>
<view class="medicine-info">
<text class="medicine-name">{{ medicine.name }}</text>
<text class="medicine-spec">{{ medicine.specification }}</text>
<view class="price-section">
<text class="current-price">¥{{ medicine.price }}</text>
<text class="original-price" v-if="medicine.originalPrice > medicine.price">
¥{{ medicine.originalPrice }}
</text>
</view>
<view class="medicine-meta">
<text class="manufacturer">{{ medicine.manufacturer }}</text>
<view class="sales-info">
<text class="sales-text">已售{{ medicine.sales }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 家庭常备药 -->
<view class="family-medicine-section">
<view class="section-header">
<text class="section-icon">🏠</text>
<text class="section-title">家庭常备药</text>
<text class="section-subtitle">为家人健康护航</text>
</view>
<view class="family-medicine-grid">
<view
v-for="(medicine, index) in familyMedicines"
:key="medicine.id"
class="family-medicine-card"
@click="navigateToMedicineDetail(medicine)"
>
<view class="medicine-main">
<view class="medicine-icon-wrapper">
<text class="medicine-icon">{{ medicine.icon }}</text>
</view>
<view class="medicine-details">
<text class="medicine-name">{{ medicine.name }}</text>
<text class="medicine-use">{{ medicine.use }}</text>
</view>
</view>
<view class="buy-button" @click.stop="addToCart(medicine)">
<text class="buy-text">加入清单</text>
</view>
</view>
</view>
</view>
<!-- 药品推荐 -->
<view class="recommend-section">
<view class="section-header">
<view class="header-left">
<text class="section-icon">💊</text>
<text class="section-title">药品推荐</text>
</view>
<view class="filter-options">
<text
v-for="filter in filterOptions"
:key="filter.id"
:class="['filter-option', { active: activeFilter === filter.id }]"
@click="switchFilter(filter.id)"
>
{{ filter.name }}
</text>
</view>
</view>
<!-- 药品列表 -->
<view class="medicine-list">
<view
v-for="medicine in recommendedMedicines"
:key="medicine.id"
class="medicine-item"
@click="navigateToMedicineDetail(medicine)"
>
<image
class="medicine-image"
:src="medicine.image"
mode="aspectFill"
/>
<view class="medicine-details">
<view class="medicine-header">
<text class="medicine-name">{{ medicine.name }}</text>
<text class="medicine-tag" v-if="medicine.tag">{{ medicine.tag }}</text>
</view>
<text class="medicine-specification">{{ medicine.specification }}</text>
<view class="manufacturer-info">
<text class="manufacturer">{{ medicine.manufacturer }}</text>
</view>
<view class="price-action">
<view class="price-info">
<text class="current-price">¥{{ medicine.price }}</text>
<text class="original-price" v-if="medicine.originalPrice > medicine.price">
¥{{ medicine.originalPrice }}
</text>
</view>
<view class="action-buttons">
<view class="cart-button" @click.stop="addToCart(medicine)">
<text class="cart-icon">🛒</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 && recommendedMedicines.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 statusBarHeight = ref<number>(0)
const scrollViewHeight = ref<number>(0)
const refreshing = ref<boolean>(false)
const loading = ref<boolean>(false)
const hasMore = ref<boolean>(true)
const cartCount = ref<number>(2)
const pendingOrders = ref<number>(1)
const activeCategory = ref<string>('all')
const activeFilter = ref<string>('recommend')
const scrollLeft = ref<number>(0)
const isMobile = ref<boolean>(false) // 是否是手机端
// 用户信息
const userInfo = reactive({
name: '张先生',
avatar: 'https://picsum.photos/100/100?random=user'
})
// 倒计时数据
const countdown = reactive({
hours: '02',
minutes: '30',
seconds: '15'
})
// 药品分类
const medicineCategories = [
{ id: 'all', name: '全部', icon: '📦', color: '#4CAF50' },
{ id: 'cold', name: '感冒发烧', icon: '🤧', color: '#2196F3' },
{ id: 'stomach', name: '肠胃用药', icon: '🤢', color: '#FF9800' },
{ id: 'pain', name: '止痛消炎', icon: '💊', color: '#F44336' },
{ id: 'skin', name: '皮肤用药', icon: '🤕', color: '#9C27B0' },
{ id: 'vitamin', name: '维生素', icon: '🍊', color: '#FFC107' },
{ id: 'chronic', name: '慢性病', icon: '🫀', color: '#795548' },
{ id: 'child', name: '儿童用药', icon: '👶', color: '#00BCD4' },
{ id: 'external', name: '外用药品', icon: '🧴', color: '#8BC34A' },
{ id: 'device', name: '医疗器械', icon: '🩺', color: '#607D8B' }
]
// 筛选选项
const filterOptions = [
{ id: 'recommend', name: '推荐' },
{ id: 'sales', name: '销量' },
{ id: 'price', name: '价格' },
{ id: 'new', name: '新品' }
]
// 健康知识轮播
const healthBanners = [
{
id: '1',
title: '秋季流感预防',
desc: '提前做好防护准备',
image_url: 'https://picsum.photos/750/350?random=flu'
},
{
id: '2',
title: '家庭用药安全',
desc: '正确储存和使用药品',
image_url: 'https://picsum.photos/750/350?random=medicine'
},
{
id: '3',
title: '慢性病管理',
desc: '科学用药,健康生活',
image_url: 'https://picsum.photos/750/350?random=chronic'
}
]
// 特价药品
const specialOfferMedicines = [
{
id: 'med1',
name: '布洛芬缓释胶囊',
specification: '0.3g*24粒',
price: 18.5,
originalPrice: 25.8,
image: 'https://picsum.photos/200/200?random=ibuprofen',
manufacturer: '修正药业',
sales: 1560,
tag: '特价'
},
{
id: 'med2',
name: '板蓝根颗粒',
specification: '10g*20袋',
price: 22.8,
originalPrice: 29.9,
image: 'https://picsum.photos/200/200?random=banlangen',
manufacturer: '白云山',
sales: 2340,
tag: '热销'
},
{
id: 'med3',
name: '维生素C片',
specification: '100mg*100片',
price: 15.9,
originalPrice: 19.9,
image: 'https://picsum.photos/200/200?random=vitaminc',
manufacturer: '养生堂',
sales: 1890,
tag: '新品'
},
{
id: 'med4',
name: '胃康灵胶囊',
specification: '0.4g*24粒',
price: 32.8,
originalPrice: 38.5,
image: 'https://picsum.photos/200/200?random=stomach',
manufacturer: '三九医药',
sales: 890,
tag: '特价'
}
]
// 家庭常备药
const familyMedicines = [
{
id: 'fam1',
name: '创可贴',
use: '伤口护理',
icon: '🩹',
iconColor: '#FF5722'
},
{
id: 'fam2',
name: '体温计',
use: '健康监测',
icon: '🌡️',
iconColor: '#2196F3'
},
{
id: 'fam3',
name: '碘伏棉签',
use: '消毒杀菌',
icon: '🧴',
iconColor: '#4CAF50'
},
{
id: 'fam4',
name: '口罩',
use: '日常防护',
icon: '😷',
iconColor: '#607D8B'
},
{
id: 'fam5',
name: '退热贴',
use: '物理降温',
icon: '🧊',
iconColor: '#00BCD4'
},
{
id: 'fam6',
name: '消毒酒精',
use: '环境消毒',
icon: '🧪',
iconColor: '#FFC107'
}
]
// 推荐药品
const recommendedMedicines = [
{
id: 'rec1',
name: '阿莫西林胶囊',
specification: '0.25g*24粒',
price: 28.5,
originalPrice: 35.0,
image: 'https://picsum.photos/300/300?random=amoxicillin',
manufacturer: '哈药集团',
tag: '处方药'
},
{
id: 'rec2',
name: '连花清瘟胶囊',
specification: '0.35g*36粒',
price: 42.8,
originalPrice: 48.0,
image: 'https://picsum.photos/300/300?random=lianhua',
manufacturer: '以岭药业',
tag: '中成药'
},
{
id: 'rec3',
name: '盐酸氨溴索口服液',
specification: '100ml',
price: 35.9,
originalPrice: 42.0,
image: 'https://picsum.photos/300/300?random=cough',
manufacturer: '扬子江药业'
},
{
id: 'rec4',
name: '氯雷他定片',
specification: '10mg*6片',
price: 18.6,
originalPrice: 22.0,
image: 'https://picsum.photos/300/300?random=allergy',
manufacturer: '拜耳医药',
tag: '抗过敏'
},
{
id: 'rec5',
name: '复方丹参滴丸',
specification: '27mg*150丸',
price: 56.8,
originalPrice: 65.0,
image: 'https://picsum.photos/300/300?random=heart',
manufacturer: '天士力',
tag: '心脑血管'
},
{
id: 'rec6',
name: '达克宁乳膏',
specification: '15g',
price: 24.5,
originalPrice: 28.0,
image: 'https://picsum.photos/300/300?random=skin',
manufacturer: '西安杨森',
tag: '皮肤用药'
}
]
// 生命周期
onMounted(() => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight || 0
const windowHeight = systemInfo.windowHeight
const navHeight = 60 // 导航栏高度
scrollViewHeight.value = windowHeight - statusBarHeight.value - navHeight
// 判断是否是手机端
const screenWidth = systemInfo.screenWidth || systemInfo.windowWidth
isMobile.value = screenWidth < 768
startCountdown()
})
onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer)
}
})
// 倒计时定时器
let countdownTimer: any = null
const startCountdown = () => {
countdownTimer = 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 switchCategory = (categoryId: string, index: number) => {
activeCategory.value = categoryId
const itemWidth = 80
scrollLeft.value = index * itemWidth
}
const switchFilter = (filterId: string) => {
activeFilter.value = filterId
}
const onScroll = (e: any) => {
scrollLeft.value = e.detail.scrollLeft
}
// 下拉刷新
const onRefresh = () => {
refreshing.value = true
setTimeout(() => {
refreshing.value = false
uni.showToast({
title: '刷新成功',
icon: 'success'
})
}, 1000)
}
// 加载更多
const loadMore = () => {
if (loading.value || !hasMore.value) return
loading.value = true
setTimeout(() => {
const newMedicines = [...recommendedMedicines].map((item, index) => ({
...item,
id: `new${index}`,
price: Math.floor(item.price * 0.9 + Math.random() * 10)
}))
// 实际项目中应该合并数据
loading.value = false
hasMore.value = recommendedMedicines.length < 20
}, 1500)
}
// 添加到购物车
const addToCart = (medicine: any) => {
uni.showToast({
title: '已添加到购物车',
icon: 'success'
})
cartCount.value++
}
// 显示用户菜单
const showUserMenu = () => {
uni.showActionSheet({
itemList: ['个人中心', '我的处方', '用药提醒', '设置'],
success: (res) => {
const pages = [
'/pages/user/profile',
'/pages/user/prescriptions',
'/pages/user/reminders',
'/pages/user/settings'
]
uni.navigateTo({ url: pages[res.tapIndex] })
}
})
}
// 导航函数
const navigateToSearch = () => uni.navigateTo({ url: '/pages/medicine/search' })
const navigateToCart = () => uni.navigateTo({ url: '/pages/medicine/cart' })
const navigateToOrders = () => uni.navigateTo({ url: '/pages/medicine/orders' })
const navigateToMedicineDetail = (medicine: any) => {
uni.navigateTo({
url: `/pages/medicine/detail?id=${medicine.id}`
})
}
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' })
</script>
<style>
/* 基础样式重置 */
.medicine-home * {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.medicine-home {
width: 100%;
min-height: 100vh;
background: #f8f9fa;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
}
/* 导航栏样式 */
.medicine-navbar {
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
box-shadow: 0 2px 10px rgba(76, 175, 80, 0.2);
}
.nav-content {
height: 60px;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
.nav-left {
display: flex;
align-items: center;
gap: 15px;
}
.logo-area {
display: flex;
align-items: center;
gap: 8px;
}
.logo-icon {
font-size: 24px;
color: white;
}
.logo-text {
font-size: 20px;
font-weight: bold;
color: white;
letter-spacing: 0.5px;
}
.slogan {
padding: 4px 10px;
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
}
.slogan-text {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
}
/* 用户操作区域 */
.user-actions {
display: flex;
align-items: center;
gap: 20px;
}
.action-item {
position: relative;
cursor: pointer;
padding: 8px;
border-radius: 8px;
transition: all 0.2s ease;
}
.action-item:hover {
background: rgba(255, 255, 255, 0.1);
}
.action-icon {
font-size: 20px;
color: white;
}
.badge {
position: absolute;
top: -5px;
right: -5px;
background: #FF5722;
color: white;
font-size: 10px;
min-width: 16px;
height: 16px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 4px;
border: 2px solid #4CAF50;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.3);
}
/* 主内容区 */
.main-content {
margin-top: 60px;
padding: 16px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
width: 100%;
}
/* 健康提示 */
.health-tip-section {
background: linear-gradient(135deg, #E3F2FD 0%, #BBDEFB 100%);
border-radius: 12px;
padding: 12px 20px;
margin-bottom: 16px;
border-left: 4px solid #2196F3;
}
.tip-content {
display: flex;
align-items: center;
gap: 10px;
}
.tip-icon {
font-size: 18px;
color: #2196F3;
}
.tip-text {
font-size: 14px;
color: #1976D2;
font-weight: 500;
}
/* 药品分类区域 */
.medicine-category-section {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.section-header {
margin-bottom: 16px;
}
.section-title {
font-size: 18px;
font-weight: bold;
color: #333;
margin-right: 10px;
}
.section-subtitle {
font-size: 14px;
color: #666;
}
/* PC端分类滚动区域 */
.category-scroll {
width: 100%;
white-space: nowrap;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.category-container {
display: flex;
flex-direction: row;
padding: 5px 0;
}
/* 手机端分类网格 */
.category-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
/* 分类项通用样式 */
.category-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
}
.category-item.active .category-name {
color: #4CAF50;
font-weight: bold;
}
.category-item.active .category-icon-wrapper {
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
}
.category-icon-wrapper {
width: 56px;
height: 56px;
border-radius: 28px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
transition: all 0.3s ease;
}
.category-icon {
font-size: 24px;
color: white;
}
.category-name {
font-size: 13px;
color: #666;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
/* 搜索栏 */
.search-section {
margin-bottom: 16px;
}
.search-container {
width: 100%;
}
.search-box {
background: white;
border-radius: 25px;
padding: 14px 20px;
display: flex;
align-items: center;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
border: 1px solid #e0e0e0;
}
.search-icon {
color: #4CAF50;
margin-right: 12px;
font-size: 18px;
}
.search-placeholder {
color: #999;
font-size: 15px;
flex: 1;
}
/* 健康知识轮播 */
.health-banner-section {
background: white;
border-radius: 12px;
overflow: hidden;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.banner-swiper {
height: 180px;
position: relative;
}
.banner-image {
width: 100%;
height: 100%;
display: block;
}
.banner-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
padding: 20px;
color: white;
}
.banner-title {
font-size: 18px;
font-weight: bold;
display: block;
margin-bottom: 4px;
}
.banner-desc {
font-size: 14px;
opacity: 0.9;
display: block;
}
/* 快捷服务 */
.quick-service-section {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.service-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.service-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
}
.service-item:hover {
transform: translateY(-2px);
}
.service-icon {
width: 50px;
height: 50px;
border-radius: 25px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 8px;
}
.service-icon.doctor {
background: #E3F2FD;
color: #2196F3;
}
.service-icon.prescription {
background: #F3E5F5;
color: #9C27B0;
}
.service-icon.otc {
background: #E8F5E9;
color: #4CAF50;
}
.service-icon.tool {
background: #FFF3E0;
color: #FF9800;
}
.service-text {
font-size: 13px;
color: #333;
font-weight: 500;
text-align: center;
}
/* 特价药品 */
.special-offer-section {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.header-left {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
}
.section-icon {
font-size: 20px;
color: #4CAF50;
}
.countdown-timer {
display: flex;
align-items: center;
gap: 8px;
background: #FF5722;
color: white;
padding: 6px 12px;
border-radius: 20px;
font-size: 13px;
}
.countdown-text {
font-weight: 500;
}
.time-display {
display: flex;
align-items: center;
gap: 2px;
}
.time-unit {
background: white;
color: #FF5722;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
min-width: 20px;
text-align: center;
}
.time-separator {
color: white;
font-weight: bold;
}
/* 药品网格 */
.medicine-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.medicine-card {
background: #f8f9fa;
border-radius: 10px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid #e0e0e0;
}
.medicine-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.medicine-image-container {
position: relative;
height: 140px;
background: white;
}
.medicine-image {
width: 100%;
height: 100%;
object-fit: contain;
padding: 10px;
}
.medicine-tag {
position: absolute;
top: 8px;
left: 8px;
background: #FF5722;
color: white;
font-size: 11px;
padding: 3px 8px;
border-radius: 10px;
font-weight: bold;
}
.medicine-info {
padding: 12px;
}
.medicine-name {
font-size: 14px;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 4px;
line-height: 1.3;
}
.medicine-spec {
font-size: 12px;
color: #666;
display: block;
margin-bottom: 8px;
}
.price-section {
display: flex;
align-items: baseline;
gap: 6px;
margin-bottom: 8px;
}
.current-price {
font-size: 18px;
font-weight: bold;
color: #FF5722;
}
.original-price {
font-size: 12px;
color: #999;
text-decoration: line-through;
}
.medicine-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
}
.manufacturer {
color: #666;
}
.sales-text {
color: #999;
}
/* 家庭常备药 */
.family-medicine-section {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.family-medicine-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.family-medicine-card {
background: #f8f9fa;
border-radius: 10px;
padding: 16px;
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #e0e0e0;
}
.medicine-main {
display: flex;
align-items: center;
gap: 12px;
}
.medicine-icon-wrapper {
width: 40px;
height: 40px;
border-radius: 20px;
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
display: flex;
align-items: center;
justify-content: center;
}
.medicine-icon {
font-size: 20px;
}
.medicine-details {
display: flex;
flex-direction: column;
}
.medicine-name {
font-size: 14px;
font-weight: 600;
color: #333;
margin-bottom: 2px;
}
.medicine-use {
font-size: 12px;
color: #666;
}
.buy-button {
background: #4CAF50;
color: white;
padding: 6px 12px;
border-radius: 15px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.buy-button:hover {
background: #388E3C;
}
.buy-text {
font-weight: 500;
}
/* 药品推荐 */
.recommend-section {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.filter-options {
display: flex;
gap: 16px;
align-items: center;
flex-wrap: wrap;
margin-top: 12px;
}
.filter-option {
font-size: 13px;
color: #666;
padding: 4px 12px;
border-radius: 15px;
border: 1px solid #e0e0e0;
cursor: pointer;
transition: all 0.2s ease;
}
.filter-option.active {
background: #4CAF50;
color: white;
border-color: #4CAF50;
}
.filter-option:hover {
background: #f5f5f5;
}
.filter-option.active:hover {
background: #388E3C;
}
/* 药品列表 */
.medicine-list {
margin-top: 20px;
}
.medicine-item {
display: flex;
gap: 16px;
padding: 16px;
background: #f8f9fa;
border-radius: 10px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid #e0e0e0;
}
.medicine-item:hover {
background: #f0f7f0;
transform: translateY(-2px);
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.1);
}
.medicine-image {
width: 80px;
height: 80px;
border-radius: 8px;
object-fit: contain;
background: white;
padding: 8px;
}
.medicine-details {
flex: 1;
display: flex;
flex-direction: column;
}
.medicine-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.medicine-name {
font-size: 15px;
font-weight: 600;
color: #333;
flex: 1;
}
.medicine-tag {
font-size: 11px;
color: #FF5722;
background: #FFEBEE;
padding: 2px 8px;
border-radius: 10px;
}
.medicine-specification {
font-size: 13px;
color: #666;
margin-bottom: 8px;
}
.manufacturer-info {
margin-bottom: 12px;
}
.manufacturer {
font-size: 12px;
color: #888;
}
.price-action {
display: flex;
justify-content: space-between;
align-items: center;
}
.price-info {
display: flex;
align-items: baseline;
gap: 8px;
}
.current-price {
font-size: 18px;
font-weight: bold;
color: #FF5722;
}
.original-price {
font-size: 13px;
color: #999;
text-decoration: line-through;
}
.cart-button {
width: 36px;
height: 36px;
border-radius: 18px;
background: #4CAF50;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.cart-button:hover {
background: #388E3C;
transform: scale(1.1);
}
.cart-icon {
font-size: 16px;
color: white;
}
/* 加载状态 */
.loading-container {
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: 50%;
animation: spin 1s linear infinite;
margin-bottom: 12px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 14px;
color: #666;
}
.no-more {
text-align: center;
color: #999;
font-size: 13px;
padding: 30px 0;
border-top: 1px solid #f0f0f0;
margin-top: 10px;
}
/* 安全区域 */
.safe-area {
height: 20px;
width: 100%;
}
/* ===== 响应式适配 ===== */
/* 超小屏手机 (小于360px) */
@media screen and (max-width: 360px) {
.category-grid {
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.category-icon-wrapper {
width: 48px;
height: 48px;
border-radius: 24px;
}
.category-icon {
font-size: 20px;
}
.category-name {
font-size: 12px;
}
.service-grid {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.medicine-grid {
grid-template-columns: 1fr;
gap: 12px;
}
.family-medicine-grid {
grid-template-columns: 1fr;
gap: 12px;
}
.banner-swiper {
height: 140px;
}
.main-content {
padding: 12px;
}
}
/* 小屏手机 (361px-414px) */
@media screen and (min-width: 361px) and (max-width: 414px) {
.category-grid {
grid-template-columns: repeat(5, 1fr);
gap: 12px;
}
.category-icon-wrapper {
width: 50px;
height: 50px;
border-radius: 25px;
}
.service-grid {
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.medicine-grid {
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.family-medicine-grid {
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.banner-swiper {
height: 150px;
}
.main-content {
padding: 12px;
}
}
/* 中屏手机 (415px-480px) */
@media screen and (min-width: 415px) and (max-width: 480px) {
.category-grid {
grid-template-columns: repeat(5, 1fr);
gap: 14px;
}
.category-icon-wrapper {
width: 52px;
height: 52px;
border-radius: 26px;
}
.service-grid {
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.medicine-grid {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.family-medicine-grid {
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.banner-swiper {
height: 160px;
}
}
/* 大屏手机 (481px-768px) */
@media screen and (min-width: 481px) and (max-width: 768px) {
.nav-content {
padding: 0 16px;
}
.logo-text {
font-size: 18px;
}
.slogan {
display: none;
}
.category-grid {
grid-template-columns: repeat(5, 1fr);
gap: 16px;
}
.category-icon-wrapper {
width: 54px;
height: 54px;
border-radius: 27px;
}
.service-grid {
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.medicine-grid {
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.family-medicine-grid {
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.banner-swiper {
height: 180px;
}
.main-content {
padding: 14px;
}
}
/* 平板设备 (769px-1024px) */
@media screen and (min-width: 769px) and (max-width: 1024px) {
.category-scroll {
display: block;
}
.category-grid {
display: none;
}
.medicine-grid {
grid-template-columns: repeat(3, 1fr);
}
.service-grid {
grid-template-columns: repeat(4, 1fr);
}
.family-medicine-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* PC端 (1025px以上) */
@media screen and (min-width: 1025px) {
.category-scroll {
display: block;
}
.category-grid {
display: none;
}
.medicine-grid {
grid-template-columns: repeat(4, 1fr);
}
.family-medicine-grid {
grid-template-columns: repeat(3, 1fr);
}
.service-grid {
grid-template-columns: repeat(4, 1fr);
}
}
/* 暗黑模式适配 */
@media (prefers-color-scheme: dark) {
.medicine-home {
background: #121212;
}
.medicine-category-section,
.search-box,
.health-banner-section,
.quick-service-section,
.special-offer-section,
.family-medicine-section,
.recommend-section {
background: #1e1e1e;
border-color: #333;
}
.section-title,
.category-name,
.service-text,
.medicine-name,
.medicine-spec,
.current-price {
color: #fff;
}
.section-subtitle,
.search-placeholder,
.medicine-specification,
.manufacturer {
color: #aaa;
}
.medicine-card,
.family-medicine-card,
.medicine-item {
background: #2d2d2d;
border-color: #444;
}
.price-section .original-price {
color: #888;
}
.filter-option {
background: #333;
border-color: #444;
color: #ccc;
}
.filter-option.active {
background: #4CAF50;
color: white;
}
}
</style>