1419 lines
39 KiB
Plaintext
1419 lines
39 KiB
Plaintext
<template>
|
||
<view class="category-page">
|
||
<!-- 顶部搜索栏 -->
|
||
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<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>
|
||
|
||
<!-- 分类内容区 -->
|
||
<view
|
||
class="category-content"
|
||
:style="{
|
||
marginTop: (statusBarHeight + headerHeight + 10) + 'px',
|
||
height: `calc(100vh - ${statusBarHeight + headerHeight + 10}px)`
|
||
}"
|
||
>
|
||
<!-- 左侧一级分类 -->
|
||
<scroll-view scroll-y class="primary-category">
|
||
<view
|
||
v-for="item in primaryCategories"
|
||
:key="item.id"
|
||
:class="['primary-item', { active: activePrimary === item.id }]"
|
||
@click="selectPrimaryCategory(item.id)"
|
||
:style="{ backgroundColor: activePrimary === item.id ? item.color : 'transparent' }"
|
||
>
|
||
<text class="primary-icon">{{ item.icon }}</text>
|
||
<text class="primary-name">{{ item.name }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 右侧商品列表 -->
|
||
<scroll-view scroll-y class="product-content">
|
||
<!-- 分类标题 -->
|
||
<view class="category-header">
|
||
<text class="category-title">{{ currentCategoryName }}</text>
|
||
<text class="category-desc">{{ currentCategoryDesc }}</text>
|
||
</view>
|
||
|
||
<!-- 商品网格 -->
|
||
<view v-if="productList.length > 0" class="product-grid">
|
||
<view
|
||
v-for="product in productList"
|
||
:key="product.id"
|
||
class="product-card"
|
||
@click="navigateToProduct(product)"
|
||
>
|
||
<view class="product-badge" v-if="product.badge">{{ product.badge }}</view>
|
||
<image
|
||
class="product-image"
|
||
:src="product.image"
|
||
mode="aspectFill"
|
||
/>
|
||
<view class="product-info">
|
||
<text class="product-name">{{ product.name }}</text>
|
||
<text class="product-spec">{{ product.specification }}</text>
|
||
|
||
<view class="price-section">
|
||
<view class="current-price">
|
||
<text class="price-symbol">¥</text>
|
||
<text class="price-value">{{ product.price }}</text>
|
||
</view>
|
||
<text class="original-price" v-if="product.originalPrice > product.price">
|
||
¥{{ product.originalPrice }}
|
||
</text>
|
||
</view>
|
||
|
||
<view class="product-meta">
|
||
<text class="manufacturer">{{ product.manufacturer }}</text>
|
||
<view class="sales-info">
|
||
<text class="sales-count">已售{{ product.sales }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view v-else class="empty-state">
|
||
<text class="empty-icon">💊</text>
|
||
<text class="empty-text">暂无相关药品</text>
|
||
<text class="empty-desc">该分类下暂无商品,敬请期待</text>
|
||
</view>
|
||
|
||
<!-- 加载更多提示 -->
|
||
<view v-if="hasMore" class="load-more">
|
||
<text class="load-text">上拉加载更多</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref, onMounted } from 'vue'
|
||
|
||
// 响应式数据
|
||
const statusBarHeight = ref(0)
|
||
const headerHeight = ref(44) // 默认头部高度
|
||
const primaryCategories = ref<any[]>([])
|
||
const productList = ref<any[]>([])
|
||
const activePrimary = ref<string>('cold')
|
||
const cartCount = ref(3)
|
||
const hasMore = ref(true)
|
||
|
||
// 获取当前分类信息
|
||
const currentCategoryName = ref('感冒发烧')
|
||
const currentCategoryDesc = ref('解热镇痛')
|
||
|
||
// 页面参数
|
||
const pageParams = ref<any>({})
|
||
|
||
// 医药分类数据(与主页一致)
|
||
const medicineCategories = [
|
||
{ id: 'cold', name: '感冒发烧', icon: '🤧', desc: '解热镇痛', color: '#2196F3' },
|
||
{ id: 'stomach', name: '肠胃用药', icon: '🤢', desc: '消化系统', color: '#4CAF50' },
|
||
{ id: 'pain', name: '止痛消炎', icon: '💊', desc: '镇痛消炎', color: '#F44336' },
|
||
{ id: 'skin', name: '皮肤用药', icon: '🤕', desc: '皮肤护理', color: '#9C27B0' },
|
||
{ id: 'vitamin', name: '维生素', icon: '🍊', desc: '营养补充', color: '#FF9800' },
|
||
{ id: 'chronic', name: '慢性病', icon: '🫀', desc: '长期管理', color: '#795548' },
|
||
{ id: 'child', name: '儿童用药', icon: '👶', desc: '儿童专用', color: '#00BCD4' },
|
||
{ id: 'external', name: '外用药品', icon: '🧴', desc: '外用制剂', color: '#8BC34A' },
|
||
{ id: 'device', name: '医疗器械', icon: '🩺', desc: '医疗设备', color: '#607D8B' },
|
||
{ id: 'health', name: '健康食品', icon: '🥗', desc: '保健食品', color: '#FFC107' }
|
||
]
|
||
|
||
// Mock 商品数据 - 使用本地图片避免网络请求
|
||
const mockProducts = {
|
||
cold: [
|
||
{
|
||
id: 'cold1',
|
||
shopId: 'shop_001',
|
||
shopName: '修正药业官方旗舰店',
|
||
name: '布洛芬缓释胶囊',
|
||
specification: '0.3g*24粒',
|
||
price: 18.5,
|
||
originalPrice: 25.8,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '修正药业',
|
||
sales: 2560,
|
||
badge: '热销'
|
||
},
|
||
{
|
||
id: 'cold2',
|
||
shopId: 'shop_002',
|
||
shopName: '白云山大药房',
|
||
name: '板蓝根颗粒',
|
||
specification: '10g*20袋',
|
||
price: 22.8,
|
||
originalPrice: 29.9,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '白云山',
|
||
sales: 1890,
|
||
badge: '推荐'
|
||
},
|
||
{
|
||
id: 'cold3',
|
||
shopId: 'shop_003',
|
||
shopName: '以岭药业自营店',
|
||
name: '连花清瘟胶囊',
|
||
specification: '0.35g*36粒',
|
||
price: 42.8,
|
||
originalPrice: 48.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '以岭药业',
|
||
sales: 3200,
|
||
badge: '爆款'
|
||
},
|
||
{
|
||
id: 'cold4',
|
||
shopId: 'shop_004',
|
||
shopName: '强生制药旗舰店',
|
||
name: '对乙酰氨基酚片',
|
||
specification: '0.5g*12片',
|
||
price: 8.9,
|
||
originalPrice: 12.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '强生制药',
|
||
sales: 1420,
|
||
badge: '特价'
|
||
},
|
||
{
|
||
id: 'cold5',
|
||
shopId: 'shop_005',
|
||
shopName: '同仁堂大药房',
|
||
name: '感冒清热颗粒',
|
||
specification: '3g*10袋',
|
||
price: 16.5,
|
||
originalPrice: 19.9,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '同仁堂',
|
||
sales: 980,
|
||
badge: '新品'
|
||
},
|
||
{
|
||
id: 'cold6',
|
||
shopId: 'shop_006',
|
||
shopName: '三九医药旗舰店',
|
||
name: '复方氨酚烷胺片',
|
||
specification: '10片/盒',
|
||
price: 12.8,
|
||
originalPrice: 15.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '三九医药',
|
||
sales: 1650,
|
||
badge: '家庭装'
|
||
}
|
||
],
|
||
|
||
stomach: [
|
||
{
|
||
id: 'stomach1',
|
||
shopId: 'shop_006',
|
||
shopName: '三九医药旗舰店',
|
||
name: '胃康灵胶囊',
|
||
specification: '0.4g*24粒',
|
||
price: 32.8,
|
||
originalPrice: 38.5,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '三九医药',
|
||
sales: 890,
|
||
badge: '热销'
|
||
},
|
||
{
|
||
id: 'stomach2',
|
||
shopId: 'shop_007',
|
||
shopName: '阿斯利康医药',
|
||
name: '奥美拉唑肠溶胶囊',
|
||
specification: '20mg*14粒',
|
||
price: 28.5,
|
||
originalPrice: 35.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '阿斯利康',
|
||
sales: 1250,
|
||
badge: '处方药'
|
||
},
|
||
{
|
||
id: 'stomach3',
|
||
shopId: 'shop_008',
|
||
shopName: '江中制药旗舰店',
|
||
name: '健胃消食片',
|
||
specification: '0.8g*32片',
|
||
price: 15.9,
|
||
originalPrice: 19.9,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '江中制药',
|
||
sales: 2100,
|
||
badge: '推荐'
|
||
},
|
||
{
|
||
id: 'stomach4',
|
||
shopId: 'shop_009',
|
||
shopName: '益普生大药房',
|
||
name: '蒙脱石散',
|
||
specification: '3g*10袋',
|
||
price: 18.6,
|
||
originalPrice: 22.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '益普生',
|
||
sales: 1780,
|
||
badge: '止泻'
|
||
},
|
||
{
|
||
id: 'stomach5',
|
||
shopId: 'shop_010',
|
||
shopName: '西安杨森旗舰店',
|
||
name: '多潘立酮片',
|
||
specification: '10mg*30片',
|
||
price: 22.8,
|
||
originalPrice: 26.5,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '西安杨森',
|
||
sales: 950,
|
||
badge: '促消化'
|
||
},
|
||
{
|
||
id: 'stomach6',
|
||
shopId: 'shop_011',
|
||
shopName: '拜耳医药自营店',
|
||
name: '铝碳酸镁咀嚼片',
|
||
specification: '0.5g*20片',
|
||
price: 25.9,
|
||
originalPrice: 29.9,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '拜耳',
|
||
sales: 1320,
|
||
badge: '护胃'
|
||
}
|
||
],
|
||
|
||
pain: [
|
||
{
|
||
id: 'pain1',
|
||
shopId: 'shop_012',
|
||
shopName: '华北制药旗舰店',
|
||
name: '阿莫西林胶囊',
|
||
specification: '0.25g*24粒',
|
||
price: 28.5,
|
||
originalPrice: 35.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '华北制药',
|
||
sales: 1560,
|
||
badge: '处方药'
|
||
},
|
||
{
|
||
id: 'pain2',
|
||
shopId: 'shop_013',
|
||
shopName: '诺华制药旗舰店',
|
||
name: '双氯芬酸钠缓释片',
|
||
specification: '75mg*10片',
|
||
price: 19.8,
|
||
originalPrice: 24.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '诺华制药',
|
||
sales: 1280,
|
||
badge: '止痛'
|
||
},
|
||
{
|
||
id: 'pain3',
|
||
shopId: 'shop_014',
|
||
shopName: '云南白药旗舰店',
|
||
name: '云南白药胶囊',
|
||
specification: '0.25g*32粒',
|
||
price: 35.9,
|
||
originalPrice: 42.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '云南白药',
|
||
sales: 2350,
|
||
badge: '经典'
|
||
},
|
||
{
|
||
id: 'pain4',
|
||
shopId: 'shop_015',
|
||
shopName: '辉瑞医药旗舰店',
|
||
name: '塞来昔布胶囊',
|
||
specification: '0.2g*10粒',
|
||
price: 48.6,
|
||
originalPrice: 55.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '辉瑞',
|
||
sales: 890,
|
||
badge: '抗炎'
|
||
},
|
||
{
|
||
id: 'pain5',
|
||
shopId: 'shop_016',
|
||
shopName: '中美史克大药房',
|
||
name: '布洛芬片',
|
||
specification: '0.1g*24片',
|
||
price: 12.5,
|
||
originalPrice: 15.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '中美史克',
|
||
sales: 1680,
|
||
badge: '经济装'
|
||
},
|
||
{
|
||
id: 'pain6',
|
||
shopId: 'shop_002',
|
||
shopName: '白云山大药房',
|
||
name: '头孢克肟胶囊',
|
||
specification: '0.1g*6粒',
|
||
price: 32.8,
|
||
originalPrice: 38.0,
|
||
image: '/static/images/default-product.png',
|
||
manufacturer: '广州白云山',
|
||
sales: 1120,
|
||
badge: '抗生素'
|
||
}
|
||
]
|
||
}
|
||
|
||
// 补充其他分类的默认数据(简化版)
|
||
const generateDefaultProducts = (categoryId: string) => {
|
||
const baseProducts = [
|
||
{ name: '通用药品1', price: 25.8, manufacturer: '知名药企', sales: 1200 },
|
||
{ name: '通用药品2', price: 18.5, manufacturer: '知名药企', sales: 950 },
|
||
{ name: '通用药品3', price: 32.0, manufacturer: '知名药企', sales: 1450 },
|
||
{ name: '通用药品4', price: 22.8, manufacturer: '知名药企', sales: 880 },
|
||
{ name: '通用药品5', price: 28.9, manufacturer: '知名药企', sales: 1100 },
|
||
{ name: '通用药品6', price: 19.9, manufacturer: '知名药企', sales: 920 }
|
||
]
|
||
|
||
return baseProducts.map((product, index) => ({
|
||
id: `${categoryId}${index + 1}`,
|
||
shopId: `shop_default_${categoryId}_${index}`, // 确保不同分类店铺ID不同
|
||
shopName: '平台自营大药房',
|
||
...product,
|
||
specification: '规格待定',
|
||
originalPrice: product.price * 1.2,
|
||
image: '/static/images/default-product.png',
|
||
badge: index === 0 ? '热销' : index === 1 ? '推荐' : ''
|
||
}))
|
||
}
|
||
|
||
// 生命周期
|
||
onMounted(() => {
|
||
initPage()
|
||
|
||
console.log('=== category页面onMounted被调用 ===')
|
||
|
||
// 在onMounted中只初始化页面,不处理分类参数
|
||
// 分类参数的处理交给onLoad函数,因为onLoad在页面加载时执行
|
||
console.log('onMounted中不处理分类参数,等待onLoad处理')
|
||
|
||
// 注意:这里不再默认选择分类,让onLoad函数处理分类选择
|
||
// 如果onLoad没有设置分类,则保持默认状态
|
||
})
|
||
|
||
// 页面加载时处理参数 - 这是处理分类切换的主要入口
|
||
onLoad((options: any) => {
|
||
console.log('=== category页面onLoad被调用 ===')
|
||
console.log('页面加载时间:', Date.now())
|
||
console.log('传入的options参数:', options)
|
||
console.log('当前活动分类:', activePrimary.value)
|
||
|
||
let categoryId = ''
|
||
let categoryName = ''
|
||
|
||
// 首先检查传入的options参数
|
||
if (options && options.categoryId) {
|
||
categoryId = options.categoryId
|
||
categoryName = options.name || ''
|
||
console.log('✅ onLoad中找到分类参数:', categoryId, categoryName)
|
||
}
|
||
|
||
// 如果options中没有,尝试从getCurrentPages()获取
|
||
if (!categoryId) {
|
||
const pages = getCurrentPages()
|
||
if (pages.length > 0) {
|
||
const currentPage = pages[pages.length - 1]
|
||
const pageOptions = currentPage.options || {}
|
||
console.log('从getCurrentPages()获取参数:', pageOptions)
|
||
|
||
if (pageOptions.categoryId) {
|
||
categoryId = pageOptions.categoryId
|
||
categoryName = pageOptions.name || ''
|
||
console.log('✅ 从getCurrentPages()找到分类参数:', categoryId, categoryName)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果有找到分类ID,则选中对应的分类
|
||
if (categoryId) {
|
||
console.log('✅ 准备选中分类:', categoryId)
|
||
console.log('分类名称:', categoryName || '未指定')
|
||
|
||
// 检查是否需要更新分类
|
||
if (activePrimary.value !== categoryId) {
|
||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
||
console.log('准备调用selectPrimaryCategory函数...')
|
||
selectPrimaryCategory(categoryId)
|
||
} else {
|
||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
||
setTimeout(() => {
|
||
selectPrimaryCategory(categoryId)
|
||
}, 100)
|
||
}
|
||
} else {
|
||
console.log('⚠️ onLoad中未找到分类参数,使用默认分类')
|
||
// 默认选中第一个分类
|
||
const defaultCategory = 'cold'
|
||
console.log('默认分类:', defaultCategory)
|
||
|
||
// 无论如何都重新加载一次默认分类的数据
|
||
setTimeout(() => {
|
||
selectPrimaryCategory(defaultCategory)
|
||
}, 100)
|
||
}
|
||
|
||
console.log('=== category页面onLoad执行完成 ===')
|
||
})
|
||
|
||
// 页面显示时也检查参数,确保从其他页面返回时能正确显示
|
||
onShow(() => {
|
||
console.log('=== category页面onShow被调用 ===')
|
||
console.log('页面显示时间:', Date.now())
|
||
console.log('当前活动分类:', activePrimary.value)
|
||
|
||
// 在onShow中,我们也需要检查是否有新的参数
|
||
// 因为当从主页再次点击分类跳转过来时,可能不会触发onLoad
|
||
// 而是触发onShow
|
||
|
||
// 获取当前页面实例和参数
|
||
const pages = getCurrentPages()
|
||
if (pages.length > 0) {
|
||
const currentPage = pages[pages.length - 1]
|
||
const pageOptions = currentPage.options || {}
|
||
console.log('onShow中获取参数:', pageOptions)
|
||
|
||
// 检查是否有分类参数
|
||
if (pageOptions.categoryId) {
|
||
const categoryId = pageOptions.categoryId
|
||
const categoryName = pageOptions.name || ''
|
||
|
||
console.log('✅ onShow中找到分类参数:', categoryId, categoryName)
|
||
console.log('URL中的时间戳参数:', pageOptions.timestamp)
|
||
console.log('URL中的随机参数:', pageOptions.random)
|
||
|
||
// 检查是否需要更新分类
|
||
if (activePrimary.value !== categoryId) {
|
||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
||
console.log('准备调用selectPrimaryCategory函数...')
|
||
selectPrimaryCategory(categoryId)
|
||
} else {
|
||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
||
setTimeout(() => {
|
||
selectPrimaryCategory(categoryId)
|
||
}, 100)
|
||
}
|
||
} else {
|
||
console.log('⚠️ onShow中未找到分类参数')
|
||
console.log('尝试从URL中解析参数...')
|
||
|
||
// 尝试从当前页面的URL中解析参数
|
||
const currentUrl = currentPage.route || ''
|
||
console.log('当前页面路由:', currentUrl)
|
||
|
||
// 如果URL中有查询参数,尝试解析
|
||
if (currentPage.$page && currentPage.$page.fullPath) {
|
||
const fullPath = currentPage.$page.fullPath
|
||
console.log('完整路径:', fullPath)
|
||
|
||
// 尝试解析查询参数
|
||
const queryIndex = fullPath.indexOf('?')
|
||
if (queryIndex > -1) {
|
||
const queryString = fullPath.substring(queryIndex + 1)
|
||
console.log('查询字符串:', queryString)
|
||
|
||
// 简单解析查询参数
|
||
const params = new URLSearchParams(queryString)
|
||
const urlCategoryId = params.get('categoryId')
|
||
if (urlCategoryId) {
|
||
console.log('✅ 从URL解析到分类参数:', urlCategoryId)
|
||
selectPrimaryCategory(urlCategoryId)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log('=== category页面onShow执行完成 ===')
|
||
})
|
||
|
||
// 初始化页面
|
||
const initPage = () => {
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
statusBarHeight.value = systemInfo.statusBarHeight || 0
|
||
|
||
// 保持与主页一致的固定高度计算,不进行动态调整
|
||
// 这样在移动端会与主页的视觉体验保持一致(主页占位符固定为44px)
|
||
headerHeight.value = 10
|
||
|
||
// 获取页面参数
|
||
const pages = getCurrentPages()
|
||
if (pages.length > 0) {
|
||
const currentPage = pages[pages.length - 1]
|
||
pageParams.value = currentPage.options || {}
|
||
}
|
||
|
||
// 加载分类数据
|
||
primaryCategories.value = medicineCategories
|
||
}
|
||
|
||
// 选择一级分类
|
||
const selectPrimaryCategory = (categoryId: string) => {
|
||
console.log('=== selectPrimaryCategory函数开始执行 ===')
|
||
console.log('传入的categoryId:', categoryId)
|
||
console.log('当前时间:', Date.now())
|
||
|
||
// 验证categoryId是否有效
|
||
if (!categoryId) {
|
||
console.error('categoryId为空,使用默认分类')
|
||
categoryId = 'cold'
|
||
}
|
||
|
||
console.log('验证后的categoryId:', categoryId)
|
||
console.log('当前activePrimary的值:', activePrimary.value)
|
||
|
||
// 更新活动分类
|
||
activePrimary.value = categoryId
|
||
console.log('更新后的activePrimary:', activePrimary.value)
|
||
|
||
// 更新当前分类信息
|
||
const category = medicineCategories.find(cat => cat.id === categoryId)
|
||
if (category) {
|
||
currentCategoryName.value = category.name
|
||
currentCategoryDesc.value = category.desc
|
||
console.log('✅ 找到分类:', category.name, '描述:', category.desc)
|
||
} else {
|
||
console.error('❌ 未找到分类ID:', categoryId, ',使用默认分类')
|
||
// 如果找不到对应的分类,使用第一个分类
|
||
if (medicineCategories.length > 0) {
|
||
const firstCategory = medicineCategories[0]
|
||
currentCategoryName.value = firstCategory.name
|
||
currentCategoryDesc.value = firstCategory.desc
|
||
activePrimary.value = firstCategory.id
|
||
categoryId = firstCategory.id
|
||
console.log('使用默认分类:', firstCategory.name)
|
||
}
|
||
}
|
||
|
||
console.log('准备加载商品数据...')
|
||
|
||
// 加载对应商品
|
||
if (mockProducts[categoryId]) {
|
||
productList.value = mockProducts[categoryId]
|
||
console.log('✅ 加载mock商品数据成功')
|
||
console.log('分类:', categoryId)
|
||
console.log('商品数量:', mockProducts[categoryId].length)
|
||
console.log('商品列表:', mockProducts[categoryId])
|
||
} else {
|
||
productList.value = generateDefaultProducts(categoryId)
|
||
console.log('✅ 加载默认商品数据成功')
|
||
console.log('分类:', categoryId)
|
||
console.log('商品数量:', productList.value.length)
|
||
console.log('商品列表:', productList.value)
|
||
}
|
||
|
||
hasMore.value = true
|
||
|
||
// 验证数据是否已正确更新
|
||
console.log('数据更新验证:')
|
||
console.log('activePrimary:', activePrimary.value)
|
||
console.log('currentCategoryName:', currentCategoryName.value)
|
||
console.log('currentCategoryDesc:', currentCategoryDesc.value)
|
||
console.log('productList长度:', productList.value.length)
|
||
|
||
console.log('=== selectPrimaryCategory函数执行完成 ===')
|
||
}
|
||
|
||
// 添加到购物车
|
||
const addToCart = (product: any) => {
|
||
// 获取现有购物车数据
|
||
const cartData = uni.getStorageSync('cart')
|
||
let cartItems: any[] = []
|
||
|
||
if (cartData) {
|
||
try {
|
||
cartItems = JSON.parse(cartData as string) as any[]
|
||
} catch (e) {
|
||
console.error('解析购物车数据失败', e)
|
||
}
|
||
}
|
||
|
||
// 检查商品是否已存在
|
||
const existingItem = cartItems.find((item: any) => item.id === product.id)
|
||
|
||
if (existingItem) {
|
||
existingItem.quantity++
|
||
} else {
|
||
// 添加新商品
|
||
cartItems.push({
|
||
id: product.id,
|
||
shopId: product.shopId || 'shop_default',
|
||
shopName: product.shopName || product.manufacturer || '自营店铺',
|
||
name: product.name,
|
||
price: product.price,
|
||
image: product.image,
|
||
spec: product.specification || '默认规格',
|
||
quantity: 1,
|
||
selected: true
|
||
})
|
||
}
|
||
|
||
// 保存回存储
|
||
uni.setStorageSync('cart', JSON.stringify(cartItems))
|
||
|
||
uni.showToast({
|
||
title: '已添加到购物车',
|
||
icon: 'success'
|
||
})
|
||
cartCount.value++
|
||
}
|
||
|
||
// 导航函数
|
||
const navigateToSearch = () => uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
||
const navigateToCart = () => uni.navigateTo({ url: '/pages/medicine/cart' })
|
||
const navigateToProduct = (product: any) => {
|
||
uni.navigateTo({
|
||
url: `/pages/mall/consumer/product-detail?productId=${product.id}&price=${product.price}&originalPrice=${product.originalPrice || ''}`
|
||
})
|
||
}
|
||
|
||
// 相机功能
|
||
const onCamera = () => {
|
||
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 onScan = () => {
|
||
uni.scanCode({
|
||
success: (res) => {
|
||
console.log('扫码成功:', res)
|
||
uni.showToast({
|
||
title: '扫码成功: ' + res.result,
|
||
icon: 'none'
|
||
})
|
||
},
|
||
fail: (err) => {
|
||
console.error('扫码失败:', err)
|
||
}
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.category-page {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background-color: #f8fafc;
|
||
display: flex;
|
||
flex-direction: column;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
||
}
|
||
/* 搜索栏 */
|
||
.search-bar {
|
||
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);
|
||
}
|
||
|
||
/* 搜索栏 */
|
||
/* 导航栏搜索框容器内边距调整 */
|
||
.search-container {
|
||
height: 44px; /* 调整为与消息页一致的高度 */
|
||
padding: 0 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
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;
|
||
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-inner-search-text {
|
||
font-size: 12px; /* 字体稍微变小 */
|
||
color: #ffffff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.icon {
|
||
font-size: 22px;
|
||
color: white;
|
||
}
|
||
|
||
.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-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;
|
||
}
|
||
|
||
/* 搜索按钮高度微调 */
|
||
.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; /* 随搜索框高度减小而减小 */
|
||
}
|
||
|
||
.cart-badge {
|
||
position: absolute;
|
||
top: -5px;
|
||
right: -5px;
|
||
background: #FF5722;
|
||
color: white;
|
||
font-size: 10px;
|
||
min-width: 18px;
|
||
height: 18px;
|
||
border-radius: 9px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 4px;
|
||
border: 2px solid #4CAF50;
|
||
}
|
||
|
||
/* 分类内容区 */
|
||
.category-content {
|
||
display: flex;
|
||
flex-direction: row; /* 强制水平排列 */
|
||
/* margin-top: 44px; 已通过 style 动态绑定 */
|
||
padding: 0 16px;
|
||
max-width: 1400px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
width: 100%;
|
||
gap: 20px;
|
||
/* height: calc(100vh - 44px); 已通过 style 动态绑定 */
|
||
overflow: hidden; /* 防止整体滚动 */
|
||
}
|
||
|
||
/* 左侧一级分类 */
|
||
.primary-category {
|
||
width: 120px;
|
||
height: 100%; /* 占满父容器高度 */
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 12px 0;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
flex-shrink: 0;
|
||
overflow-y: auto; /* 允许内部滚动 */
|
||
}
|
||
|
||
.primary-item {
|
||
display: flex;
|
||
flex-direction: column; /* 图标和文字垂直排列 */
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 12px 8px;
|
||
margin: 4px 8px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
color: #666;
|
||
text-align: center;
|
||
}
|
||
|
||
.primary-item:hover {
|
||
transform: translateY(-2px); /* 悬停时向上浮动 */
|
||
}
|
||
|
||
.primary-item.active {
|
||
color: white !important;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.primary-icon {
|
||
font-size: 24px;
|
||
margin-bottom: 6px;
|
||
margin-right: 0; /* 移除右边距 */
|
||
text-align: center;
|
||
display: block;
|
||
}
|
||
|
||
.primary-name {
|
||
font-size: 13px;
|
||
line-height: 1.4;
|
||
display: block;
|
||
}
|
||
|
||
/* 右侧内容区 */
|
||
.product-content {
|
||
flex: 1;
|
||
height: 100%; /* 占满父容器高度 */
|
||
padding: 0; /* 移除内边距,交给内部元素 */
|
||
overflow-y: auto; /* 允许内部滚动 */
|
||
}
|
||
|
||
.category-header {
|
||
margin-bottom: 16px;
|
||
padding: 16px 8px 0 8px;
|
||
position: sticky;
|
||
top: 0;
|
||
background-color: #f8fafc;
|
||
z-index: 10;
|
||
}
|
||
|
||
.category-title {
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.category-desc {
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
/* 商品网格 */
|
||
.product-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.product-card {
|
||
background: white;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
border: 1px solid #e0e0e0;
|
||
position: relative;
|
||
}
|
||
|
||
.product-card:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||
}
|
||
|
||
.product-badge {
|
||
position: absolute;
|
||
top: 12px;
|
||
left: 12px;
|
||
background: #FF5722;
|
||
color: white;
|
||
font-size: 11px;
|
||
padding: 4px 12px;
|
||
border-radius: 12px;
|
||
font-weight: 600;
|
||
z-index: 2;
|
||
}
|
||
|
||
.product-image {
|
||
width: 100%;
|
||
height: 160px;
|
||
object-fit: cover;
|
||
background: white;
|
||
}
|
||
|
||
.product-info {
|
||
padding: 16px;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 4px;
|
||
display: block;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.product-spec {
|
||
font-size: 13px;
|
||
color: #666;
|
||
margin-bottom: 12px;
|
||
display: block;
|
||
}
|
||
|
||
.price-section {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.current-price {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
|
||
.price-symbol {
|
||
font-size: 14px;
|
||
color: #FF5722;
|
||
}
|
||
|
||
.price-value {
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
color: #FF5722;
|
||
margin-left: 2px;
|
||
}
|
||
|
||
.original-price {
|
||
font-size: 13px;
|
||
color: #999;
|
||
text-decoration: line-through;
|
||
}
|
||
|
||
.product-meta {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.manufacturer {
|
||
color: #666;
|
||
}
|
||
|
||
.sales-count {
|
||
color: #999;
|
||
}
|
||
|
||
.product-action {
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.cart-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
background: #4CAF50;
|
||
color: white;
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.cart-btn:hover {
|
||
background: #388E3C;
|
||
}
|
||
|
||
.cart-icon {
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 空状态 */
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 60px 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 60px;
|
||
color: #4CAF50;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 18px;
|
||
color: #333;
|
||
font-weight: bold;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.empty-desc {
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
/* 加载更多 */
|
||
.load-more {
|
||
text-align: center;
|
||
padding: 20px 0;
|
||
color: #999;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.load-text {
|
||
display: inline-block;
|
||
padding: 8px 16px;
|
||
background: #f5f5f5;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
/* ===== 响应式设计 ===== */
|
||
|
||
/* 小屏手机 (小于414px) */
|
||
@media screen and (max-width: 414px) {
|
||
.category-content {
|
||
/* flex-direction: column; 移除这一行,保持 row 布局 */
|
||
padding: 0 8px;
|
||
gap: 10px;
|
||
}
|
||
|
||
.primary-category {
|
||
width: 80px; /* 减小宽度 */
|
||
/* display: flex; 移除flex布局,保持默认 */
|
||
/* flex-wrap: wrap; 移除换行 */
|
||
padding: 8px 0;
|
||
}
|
||
|
||
.primary-item {
|
||
/* width: calc(25% - 8px); 移除百分比宽度 */
|
||
width: auto; /* 恢复自动宽度 */
|
||
margin: 4px;
|
||
padding: 8px 4px;
|
||
/* text-align: center; 已经在通用样式中设置 */
|
||
}
|
||
|
||
.primary-icon {
|
||
margin-right: 0;
|
||
margin-bottom: 4px;
|
||
font-size: 20px;
|
||
}
|
||
|
||
.primary-name {
|
||
font-size: 11px;
|
||
}
|
||
|
||
.product-grid {
|
||
grid-template-columns: repeat(2, 1fr); /* 改为双列显示 */
|
||
gap: 8px;
|
||
padding: 0 4px 20px 4px; /* 增加底部内边距 */
|
||
}
|
||
|
||
/* 手机端商品卡片极简模式 - 仿照主页样式 */
|
||
.product-spec,
|
||
.manufacturer,
|
||
.original-price,
|
||
.sales-info,
|
||
.product-badge { /* 分类页也隐藏角标,保持整洁 */
|
||
display: none;
|
||
}
|
||
|
||
.product-info {
|
||
padding: 6px;
|
||
}
|
||
|
||
.product-image {
|
||
height: 100px; /* 由于分类页右侧空间更窄,图片高度设得更小一点 */
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 12px;
|
||
height: 32px;
|
||
line-height: 1.3;
|
||
margin-bottom: 2px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.price-section {
|
||
margin-bottom: 0;
|
||
margin-top: 4px;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
}
|
||
|
||
.price-symbol {
|
||
font-size: 10px;
|
||
}
|
||
|
||
.price-value {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.product-meta {
|
||
display: none; /* 隐藏整个元数据行 */
|
||
}
|
||
|
||
.search-container {
|
||
padding: 0 12px;
|
||
height: 55px;
|
||
}
|
||
|
||
.search-box {
|
||
padding: 8px 16px;
|
||
}
|
||
|
||
.category-header {
|
||
padding: 12px 4px 0 4px;
|
||
}
|
||
}
|
||
|
||
/* 中屏手机/小平板 (415px-768px) */
|
||
@media screen and (min-width: 415px) and (max-width: 768px) {
|
||
.product-grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16px;
|
||
}
|
||
}
|
||
|
||
/* 平板设备 (769px-1024px) */
|
||
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
||
.product-grid {
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 20px;
|
||
}
|
||
}
|
||
|
||
/* 桌面端 (1025px以上) */
|
||
@media screen and (min-width: 1025px) {
|
||
.category-content {
|
||
gap: 30px;
|
||
padding: 0 24px;
|
||
}
|
||
|
||
.primary-category {
|
||
width: 160px;
|
||
padding: 16px 0;
|
||
}
|
||
|
||
.primary-item {
|
||
padding: 16px 20px;
|
||
margin: 6px 12px;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.primary-icon {
|
||
font-size: 20px;
|
||
min-width: 28px;
|
||
}
|
||
|
||
.primary-name {
|
||
font-size: 15px;
|
||
}
|
||
|
||
.product-content {
|
||
padding: 20px 0;
|
||
}
|
||
|
||
.category-header {
|
||
margin-bottom: 24px;
|
||
padding: 0 12px;
|
||
}
|
||
|
||
.category-title {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.category-desc {
|
||
font-size: 15px;
|
||
}
|
||
|
||
.product-grid {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 24px;
|
||
}
|
||
|
||
.product-card {
|
||
border-radius: 14px;
|
||
}
|
||
|
||
.product-info {
|
||
padding: 20px;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.product-spec {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.price-value {
|
||
font-size: 22px;
|
||
}
|
||
}
|
||
|
||
/* 大桌面端 (1400px以上) */
|
||
@media screen and (min-width: 1400px) {
|
||
.category-content {
|
||
max-width: 1600px;
|
||
gap: 40px;
|
||
padding: 0 32px;
|
||
}
|
||
|
||
.primary-category {
|
||
width: 200px;
|
||
padding: 20px 0;
|
||
}
|
||
|
||
.primary-item {
|
||
padding: 18px 24px;
|
||
margin: 8px 16px;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.primary-icon {
|
||
font-size: 22px;
|
||
min-width: 32px;
|
||
}
|
||
|
||
.primary-name {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.product-content {
|
||
padding: 24px 0;
|
||
}
|
||
|
||
.category-header {
|
||
margin-bottom: 28px;
|
||
padding: 0 16px;
|
||
}
|
||
|
||
.category-title {
|
||
font-size: 26px;
|
||
}
|
||
|
||
.category-desc {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.product-grid {
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 28px;
|
||
}
|
||
|
||
.product-card {
|
||
border-radius: 16px;
|
||
}
|
||
|
||
.product-image {
|
||
height: 180px;
|
||
}
|
||
|
||
.product-info {
|
||
padding: 24px;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 17px;
|
||
}
|
||
|
||
.product-spec {
|
||
font-size: 15px;
|
||
}
|
||
|
||
.price-value {
|
||
font-size: 24px;
|
||
}
|
||
}
|
||
</style> |