Files
medical-mall/pages/mall/consumer/category copy 3.uvue

1468 lines
40 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="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>
<!-- 导航栏占位 - 需要包含statusBarHeight + 搜索框高度44px -->
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
<!-- 分类内容区 -->
<view class="category-content">
<!-- 左侧一级分类 -->
<scroll-view
:scroll-y="true"
class="primary-category"
:scroll-top="scrollTop"
:scroll-with-animation="true"
>
<view
v-for="item in primaryCategories"
:key="item.id"
:class="['primary-item', { active: isPrimaryActive(item.id) }]"
@click="selectPrimaryCategory(item.id)"
:style="{ backgroundColor: getPrimaryItemBgColor(item) }"
>
<text class="primary-icon">{{ item.icon }}</text>
<text class="primary-name">{{ item.name }}</text>
</view>
</scroll-view>
<!-- 右侧商品列表 -->
<scroll-view
:scroll-y="true"
class="product-content"
@scrolltolower="loadMore"
:lower-threshold="50"
>
<!-- 分类标题 -->
<view class="category-header">
<text class="category-title">{{ currentCategoryName }}</text>
<text class="category-desc">{{ currentCategoryDesc }}</text>
</view>
<!-- 二级分类 -->
<view v-if="subCategories.length > 0" class="sub-category-section">
<view class="sub-category-list">
<view
v-for="sub in subCategories"
:key="sub.id"
:class="['sub-category-item', { active: isSubActive(sub.id) }]"
@click="selectSubCategory(sub.id)"
>
<text class="sub-category-icon">{{ sub.icon }}</text>
<text class="sub-category-name">{{ sub.name }}</text>
</view>
</view>
</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)"
>
<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.base_price ?? product.price ?? 0 }}</text>
<view class="product-add-btn" @click.stop="addToCart(product)">
<text class="add-icon">+</text>
</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'
import { onLoad, onShow } from '@dcloudio/uni-app'
import supabaseService from '@/utils/supabaseService.uts'
import type { Product } from '@/utils/supabaseService.uts'
type LocalCategory = {
id: string
name: string
icon: string
description: string
color: string
}
// 响应式数据
const statusBarHeight = ref(0)
const headerHeight = ref(44)
const primaryCategories = ref<LocalCategory[]>([])
const subCategories = ref<LocalCategory[]>([]) // 二级分类列表
const productList = ref<Product[]>([])
const activePrimary = ref<string>('')
const activeSubCategory = ref<string>('') // 当前选中的二级分类
const selectedParentId = ref<string>('') // 当前选中的一级分类ID用于高亮显示
const cartCount = ref(3)
const hasMore = ref(true)
const hasLoadedFromParams = ref(false)
const currentPage = ref(1)
const loading = ref(false)
const scrollTop = ref(0)
const pendingCategoryId = ref('') // 待处理的分类ID从其他页面跳转过来时暂存
// 获取当前分类信息
const currentCategoryName = ref('')
const currentCategoryDesc = ref('')
// 页面参数
const pageParams = ref<any>({})
// 加载商品数据
async function loadProducts(): Promise<void> {
if (loading.value) return
if (activePrimary.value == '') {
console.warn('activePrimary为空无法加载商品')
return
}
loading.value = true
try {
console.log('开始加载商品分类ID:', activePrimary.value, '页码:', currentPage.value)
const response = await supabaseService.getProductsByCategory(activePrimary.value, currentPage.value)
console.log('商品加载结果:', {
dataCount: response.data.length,
total: response.total,
hasmore: response.hasmore,
page: currentPage.value
})
if (currentPage.value == 1) {
productList.value = response.data
} else {
productList.value.push(...response.data)
}
hasMore.value = response.hasmore
// 更新当前分类信息 - 先在一级分类中查找,再在二级分类中查找
let foundCat: LocalCategory | null = null
for (let i = 0; i < primaryCategories.value.length; i++) {
if (primaryCategories.value[i].id == activePrimary.value) {
foundCat = primaryCategories.value[i]
break
}
}
if (foundCat == null) {
for (let i = 0; i < subCategories.value.length; i++) {
if (subCategories.value[i].id == activePrimary.value) {
foundCat = subCategories.value[i]
break
}
}
}
if (foundCat != null) {
currentCategoryName.value = foundCat.name
currentCategoryDesc.value = foundCat.description
}
console.log('商品列表加载完成,当前总数量:', productList.value.length)
} catch (error) {
console.error('加载商品数据失败:', error)
if (currentPage.value == 1) {
productList.value = []
}
} finally {
loading.value = false
}
}
// 加载二级分类
async function loadSubCategories(parentId: string): Promise<void> {
console.log('加载二级分类父级ID:', parentId)
try {
const subCats = await supabaseService.getSubCategories(parentId)
console.log('获取到二级分类数量:', subCats.length)
const categories: LocalCategory[] = []
for (let i = 0; i < subCats.length; i++) {
const cat = subCats[i]
categories.push({
id: cat.id,
name: cat.name,
icon: cat.icon,
description: cat.description,
color: cat.color
})
}
subCategories.value = categories
} catch (e) {
console.error('加载二级分类失败:', e)
subCategories.value = []
}
}
// 判断一级分类是否选中
function isPrimaryActive(categoryId: string): boolean {
return selectedParentId.value == categoryId
}
// 判断二级分类是否选中
function isSubActive(subCategoryId: string): boolean {
return activeSubCategory.value == subCategoryId || activePrimary.value == subCategoryId
}
// 获取一级分类的背景色
function getPrimaryItemBgColor(item: LocalCategory): string {
if (isPrimaryActive(item.id)) {
return item.color
}
return 'transparent'
}
// 选择二级分类
async function selectSubCategory(subCategoryId: string): Promise<void> {
console.log('选择二级分类:', subCategoryId)
activeSubCategory.value = subCategoryId
// 使用二级分类ID加载商品
currentPage.value = 1
hasMore.value = true
activePrimary.value = subCategoryId // 临时设置为二级分类ID用于加载商品
await loadProducts()
}
// 选择一级分类 - 必须在 loadCategories 之前定义
// originalCategoryId: 可能是一级分类ID也可能是二级分类ID
async function selectPrimaryCategory(originalCategoryId: string): Promise<void> {
console.log('=== selectPrimaryCategory函数开始执行 ===')
console.log('传入的categoryId:', originalCategoryId)
if (originalCategoryId == '') {
console.error('categoryId为空尝试使用第一个分类')
if (primaryCategories.value.length > 0) {
originalCategoryId = primaryCategories.value[0].id
} else {
console.error('没有可用的分类')
return
}
}
// 检查传入的是否是一级分类ID
let targetParentId = originalCategoryId
let targetSubId = ''
console.log('当前一级分类列表长度:', primaryCategories.value.length)
let foundInPrimary: LocalCategory | null = null
for (let i = 0; i < primaryCategories.value.length; i++) {
if (primaryCategories.value[i].id == originalCategoryId) {
foundInPrimary = primaryCategories.value[i]
break
}
}
console.log('在一级分类中查找结果:', foundInPrimary != null)
if (foundInPrimary == null) {
// 传入的可能是二级分类ID需要查找其父级分类
console.log('传入的ID不在一级分类中可能是二级分类ID尝试查找父级分类')
// 从服务器获取分类信息以确定父级
try {
const categoryInfo = await supabaseService.getCategoryById(originalCategoryId)
if (categoryInfo != null && categoryInfo.parent_id != null && categoryInfo.parent_id != '') {
console.log('找到父级分类ID:', categoryInfo.parent_id)
// 检查父级分类ID是否在一级分类列表中
console.log('查找父级分类ID:', categoryInfo.parent_id)
let parentInPrimary: LocalCategory | null = null
for (let i = 0; i < primaryCategories.value.length; i++) {
if (primaryCategories.value[i].id == categoryInfo.parent_id) {
parentInPrimary = primaryCategories.value[i]
break
}
}
console.log('父级分类查找结果:', parentInPrimary != null)
if (parentInPrimary != null) {
console.log('父级分类在列表中找到:', parentInPrimary.name)
targetParentId = categoryInfo.parent_id!
targetSubId = originalCategoryId // 记住要选中的二级分类
} else {
console.log('父级分类不在列表中,使用第一个分类')
// 打印当前列表中的所有分类ID
for (let i = 0; i < primaryCategories.value.length; i++) {
console.log('列表中的分类:', primaryCategories.value[i].id, primaryCategories.value[i].name)
}
if (primaryCategories.value.length > 0) {
targetParentId = primaryCategories.value[0].id
}
}
} else {
console.log('未找到父级分类,使用第一个分类')
if (primaryCategories.value.length > 0) {
targetParentId = primaryCategories.value[0].id
}
}
} catch (e) {
console.error('获取分类信息失败:', e)
if (primaryCategories.value.length > 0) {
targetParentId = primaryCategories.value[0].id
}
}
}
console.log('最终选中的一级分类ID:', targetParentId)
console.log('需要选中的二级分类ID:', targetSubId)
// 设置一级分类高亮
selectedParentId.value = targetParentId
activePrimary.value = targetParentId
// 加载二级分类
await loadSubCategories(targetParentId)
// 如果有要选中的二级分类
if (targetSubId != '') {
activeSubCategory.value = targetSubId
} else {
// 如果没有指定二级分类,但有二级分类列表,默认选中第一个
if (subCategories.value.length > 0) {
activeSubCategory.value = subCategories.value[0].id
targetSubId = subCategories.value[0].id
console.log('默认选中第一个二级分类:', subCategories.value[0].name)
} else {
activeSubCategory.value = ''
}
}
// 自动滚动到选中位置
let foundIndex = -1
for (let i = 0; i < primaryCategories.value.length; i++) {
if (primaryCategories.value[i].id == targetParentId) {
foundIndex = i
break
}
}
if (foundIndex != -1) {
// 获取系统信息
const systemInfo = uni.getSystemInfoSync()
let itemHeight = 70
if (systemInfo.windowWidth > 1025) {
itemHeight = 80
}
const scrollViewHeight = systemInfo.windowHeight - systemInfo.statusBarHeight - 44
const targetScrollTop = (foundIndex * itemHeight) - (scrollViewHeight / 2) + (itemHeight / 2)
scrollTop.value = Math.max(0, targetScrollTop)
console.log(`滚动左侧菜单: index=${foundIndex}, target=${scrollTop.value}`)
}
// 查找分类信息
let foundCategory: LocalCategory | null = null
for (let i = 0; i < primaryCategories.value.length; i++) {
if (primaryCategories.value[i].id == targetParentId) {
foundCategory = primaryCategories.value[i]
break
}
}
if (foundCategory != null) {
currentCategoryName.value = foundCategory.name
currentCategoryDesc.value = foundCategory.description
} else {
console.log('分类信息未找到,使用第一个分类的信息')
if (primaryCategories.value.length > 0) {
const firstCategory = primaryCategories.value[0]
currentCategoryName.value = firstCategory.name
currentCategoryDesc.value = firstCategory.description
}
}
currentPage.value = 1
hasMore.value = true
// 如果有选中的二级分类使用二级分类ID加载商品否则使用一级分类ID
const categoryIdForProducts = (targetSubId != '') ? targetSubId : targetParentId
activePrimary.value = categoryIdForProducts // 临时设置为要加载的分类ID
await loadProducts()
}
async function loadCategories(): Promise<void> {
try {
// 只获取一级分类parent_id 为 null 的分类)
const categoriesData = await supabaseService.getParentCategories()
console.log('加载一级分类数据成功,数量:', categoriesData.length)
// 映射数据并添加默认颜色,防止选中时背景透明导致文字看不清
// 过滤掉医药健康相关分类
const categories: LocalCategory[] = []
for (let i = 0; i < categoriesData.length; i++) {
const cat = categoriesData[i]
const name = cat.name
console.log('一级分类:', cat.id, name)
if (name.includes('医药') || name.includes('健康')) {
console.log('过滤掉分类:', name)
continue
}
categories.push({
id: cat.id,
name: cat.name,
icon: cat.icon,
description: cat.description,
color: cat.color
})
}
console.log('最终一级分类列表数量:', categories.length)
if (categories.length > 0) {
primaryCategories.value = categories
// 检查是否有待处理的分类ID从其他页面跳转过来时暂存
if (pendingCategoryId.value != '') {
console.log('发现待处理的分类ID:', pendingCategoryId.value)
// 直接调用 selectPrimaryCategory它会处理一级或二级分类ID
const idToSelect = pendingCategoryId.value
pendingCategoryId.value = '' // 清除暂存
selectPrimaryCategory(idToSelect)
return
}
// 检查是否有预设的分类ID
if (activePrimary.value != '') {
console.log('有预设的分类ID:', activePrimary.value)
const target = categories.find((c: LocalCategory): boolean => c.id == activePrimary.value)
if (target != null) {
console.log('找到目标分类,执行选中:', target.name)
selectPrimaryCategory(activePrimary.value)
return
}
}
// 默认选中第一个分类或"厨具"分类
const defaultCategory = categories.find((c: LocalCategory): boolean => c.name.includes('厨具')) ?? categories[0]
if (defaultCategory != null) {
console.log('设置默认分类:', defaultCategory.name)
selectPrimaryCategory(defaultCategory.id)
}
} else {
console.warn('从Supabase获取的分类数据为空')
}
} catch (error) {
console.error('加载分类数据失败:', error)
}
}
// 加载更多
function loadMore(): void {
if (hasMore.value && !loading.value) {
currentPage.value++
loadProducts()
}
}
// 生命周期
onMounted(() => {
loadCategories().then(() => {
setTimeout(() => {
if (!hasLoadedFromParams.value && activePrimary.value != '') {
loadProducts()
}
}, 300)
})
})
// 页面显示时检查是否有参数传递过来
onShow(() => {
console.log('=== category页面onShow被调用 ===')
// 检查是否有存储的分类选择
const savedCategoryId = uni.getStorageSync('selectedCategory')
console.log('onShow检查Storage:', savedCategoryId)
if (savedCategoryId != null && savedCategoryId != '') {
const targetId = savedCategoryId as string
console.log('onShow发现存储的分类ID:', targetId)
// 清除存储,避免下次进入默认选中
uni.removeStorageSync('selectedCategory')
// 确保分类数据已加载
if (primaryCategories.value.length > 0) {
// 如果当前未选中或选中的不是目标分类,则切换
if (activePrimary.value != targetId) {
console.log('onShow执行切换分类:', targetId)
selectPrimaryCategory(targetId)
} else {
console.log('当前已是目标分类:', targetId)
}
} else {
// 如果分类数据未加载暂存ID等待loadCategories完成后处理
console.log('分类数据尚未加载暂存ID等待加载')
pendingCategoryId.value = targetId
}
}
})
// 页面加载时处理参数 - 这是处理分类切换的主要入口
onLoad((options: any) => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight
console.log('=== category页面onLoad被调用 ===')
let categoryId = ''
let categoryName = ''
// 首先检查传入的options参数
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
const optCategoryId = optObj.getString('categoryId') ?? ''
if (optCategoryId !== '') {
categoryId = optCategoryId
categoryName = optObj.getString('name') ?? ''
console.log('✅ onLoad中找到分类参数:', categoryId, categoryName)
}
// 如果options中没有尝试从getCurrentPages()获取
if (categoryId == '') {
const pages = getCurrentPages()
if (pages.length > 0) {
const currentPage = pages[pages.length - 1]
const rawPageOptions = currentPage.options ?? {}
console.log('从getCurrentPages()获取参数:', rawPageOptions)
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
if (pageCategoryId !== '') {
categoryId = pageCategoryId
categoryName = pageOptObj.getString('name') ?? ''
console.log('✅ 从getCurrentPages()找到分类参数:', categoryId, categoryName)
}
}
}
// 如果有找到分类ID则选中对应的分类
if (categoryId != '') {
hasLoadedFromParams.value = true
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中未找到分类参数将使用从数据库加载的第一个分类')
// 不再使用硬编码的默认分类loadCategories 会设置第一个分类
}
console.log('=== category页面onLoad执行完成 ===')
})
// 添加到购物车
async function addToCart(product: Product): Promise<void> {
uni.showLoading({ title: '检查商品...' })
try {
const pid = (product.id ?? '').toString()
const merchantId = product.merchant_id ?? ''
if (pid === '') {
uni.hideLoading()
uni.showToast({ title: '商品无效', icon: 'none' })
return
}
// 检查商品是否有SKU
const skus = await supabaseService.getProductSkus(pid)
uni.hideLoading()
if (skus.length > 0) {
// 有规格,提示并跳转到商品详情页选择规格
uni.showToast({
title: '请选择规格',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/mall/consumer/product-detail?id=' + pid
})
}, 500)
} else {
// 无规格,直接加入购物车
uni.showLoading({ title: '添加中...' })
const success = await supabaseService.addToCart(pid, 1, '', merchantId)
uni.hideLoading()
if (success) {
uni.showToast({
title: '已添加到购物车',
icon: 'success'
})
cartCount.value++
} else {
uni.showToast({
title: '添加失败,请先登录',
icon: 'none'
})
}
}
} catch (e) {
console.error('添加到购物车异常', e)
uni.hideLoading()
uni.showToast({ title: '操作失败', icon: 'none' })
}
}
// 导航函数
function navigateToSearch(): void { uni.navigateTo({ url: '/pages/mall/consumer/search' }) }
function navigateToCart(): void { uni.navigateTo({ url: '/pages/mall/consumer/cart' }) }
function navigateToProduct(product: Product): void {
const id = (product.id ?? '').toString()
if (id === '') return
const price = (product.base_price ?? 0).toString()
const originalPrice = (product.market_price ?? '').toString()
const name = encodeURIComponent(product.name ?? '')
const image = encodeURIComponent(product.main_image_url ?? '')
uni.navigateTo({
url: `/pages/mall/consumer/product-detail?id=${id}&productId=${id}&price=${price}&originalPrice=${originalPrice}&name=${name}&image=${image}`
})
}
// 相机功能
function 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)
}
})
}
// 扫码功能
function onScan(): void {
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%;
height: 100%;
overflow: hidden;
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-color: #4CAF50;
z-index: 1000;
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
}
/* 导航栏占位 */
.navbar-placeholder {
flex-shrink: 0;
}
/* 搜索栏 */
/* 导航栏搜索框容器内边距调整 */
.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;
align-items: center;
transition: all 0.3s ease;
width: 100%;
height: 32px;
}
.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: normal;
}
.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 {
flex: 1;
height: 0px;
display: flex;
flex-direction: row;
padding: 0 16px;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
width: 100%;
overflow: hidden;
}
/* 左侧一级分类 */
.primary-category {
width: 63px;
height: 100%;
margin-right: 6px;
background: white;
border-radius: 8px;
padding: 6px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
flex-shrink: 0;
}
.primary-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 12px 4px;
margin: 4px 6px;
border-radius: 8px;
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: 20px;
margin-bottom: 4px;
margin-right: 0;
text-align: center;
}
.primary-name {
font-size: 11px;
line-height: 1.25;
}
/* 右侧内容区 */
.product-content {
flex: 1;
height: 100%;
padding: 0;
}
.category-header {
margin-bottom: 16px;
padding: 16px 8px 0 8px;
/* position: sticky; REMOVED for uniapp-x support */
/* 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;
}
/* 二级分类 */
.sub-category-section {
padding: 8px 8px;
background: white;
margin-bottom: 8px;
}
.sub-category-list {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
width: 100%;
justify-content: space-between;
}
.sub-category-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 8px 4px;
background: #f5f5f5;
border-radius: 8px;
flex: 1;
min-width: 50px;
}
.sub-category-item.active {
background: #4CAF50;
}
.sub-category-item.active .sub-category-name {
color: white;
}
.sub-category-icon {
font-size: 16px;
margin-bottom: 4px;
}
.sub-category-name {
font-size: 11px;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
width: 100%;
}
/* 商品网格 */
.product-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
/* grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); REMOVED for uniapp-x support */
/* gap: 20px; removed for compatibility */
padding: 10px; /* add padding to compensate */
width: 100%;
}
.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;
}
.product-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
padding: 8px;
}
.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: bold;
/* cursor: pointer; removed for uniapp-x support */
transition: all 0.2s ease;
}
.cart-btn:hover {
background: #388E3C;
}
.cart-icon {
font-size: 14px;
margin-right: 6px; /* gap replacement */
}
/* 空状态 */
.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; REMOVED for uniapp-x support */
padding: 8px 16px;
background: #f5f5f5;
border-radius: 20px;
}
/* ===== 响应式设计 ===== */
/* 小屏手机 (小于414px) */
@media screen and (max-width: 414px) {
.search-container {
padding: 0 12px;
height: 44px;
}
.search-box {
padding: 0 4px 0 12px;
margin: 0;
height: 30px;
}
.category-content {
padding: 0 4px;
}
.primary-category {
width: 56px;
padding: 4px 0;
margin-right: 4px;
border-radius: 8px;
}
.primary-item {
margin: 2px 2px;
padding: 4px 2px;
border-radius: 6px;
}
.primary-icon {
margin-right: 0;
margin-bottom: 2px;
font-size: 16px;
}
.primary-name {
font-size: 9px;
line-height: 1.1;
}
.product-grid {
padding: 0 4px 20px 4px;
}
.product-card {
width: 48%;
margin: 1%;
}
.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;
lines: 2;
}
.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;
}
.category-header {
padding: 8px 4px 0 4px;
}
.category-title {
font-size: 14px;
}
.category-desc {
font-size: 11px;
}
}
/* 中屏手机 (415px-768px) */
@media screen and (min-width: 415px) and (max-width: 768px) {
.search-container {
padding: 0 16px;
height: 44px;
}
.primary-category {
width: 62px;
padding: 6px 0;
margin-right: 6px;
border-radius: 8px;
}
.primary-item {
margin: 2px 2px;
padding: 5px 2px;
border-radius: 6px;
}
.primary-icon {
font-size: 18px;
margin-bottom: 3px;
}
.primary-name {
font-size: 10px;
line-height: 1.1;
}
.product-card {
width: 46%;
margin: 2%;
}
}
/* 平板设备 (769px-1024px) */
@media screen and (min-width: 769px) and (max-width: 1024px) {
.search-container {
padding: 0 16px;
height: 44px;
}
.primary-category {
width: 100px;
padding: 10px 0;
margin-right: 16px;
}
.primary-item {
margin: 4px 4px;
padding: 10px 6px;
}
.primary-icon {
font-size: 22px;
margin-bottom: 5px;
}
.primary-name {
font-size: 12px;
}
.product-card {
width: 30%;
margin: 1.5%;
}
}
/* 桌面端 (1025px以上) */
@media screen and (min-width: 1025px) {
.search-container {
padding: 0 16px;
height: 44px;
}
.category-content {
padding: 0 24px;
}
.primary-category {
width: 160px;
padding: 16px 0;
margin-right: 30px;
}
.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-card {
border-radius: 14px;
width: 22%;
margin: 1.5%;
}
.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;
padding: 0 32px;
}
.primary-category {
margin-right: 40px;
}
.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-card {
border-radius: 16px;
width: 17%;
margin: 1.5%;
}
.product-image {
height: 180px;
}
.product-info {
padding: 24px;
}
.product-name {
font-size: 17px;
}
.product-spec {
font-size: 15px;
}
.price-value {
font-size: 24px;
}
}
</style>