consumer模块完成90%,前端完成supabase对接

This commit is contained in:
2026-02-03 17:11:50 +08:00
parent b6200cda28
commit 8a535e3f38
69 changed files with 5020 additions and 33273 deletions

View File

@@ -39,52 +39,14 @@
<scroll-view
scroll-y
class="main-scroll"
:style="{ height: scrollHeight + 'px' }"
refresher-enabled
:refresher-triggered="refreshing"
:lower-threshold="50"
@refresherrefresh="onRefresh"
@scrolltolower="loadMore"
@scroll="handleScroll"
>
<!-- 智能健康卡片 -->
<view class="smart-health-card" :style="{ marginTop: (statusBarHeight + 44 + 10) + 'px' }">
<view class="health-content">
<view class="health-header">
<text class="health-title">智能健康助手</text>
<text class="health-subtitle">根据您的健康数据推荐</text>
</view>
<view class="health-tips">
<text class="tip-item">💡 按时用药提醒</text>
<text class="tip-item">📋 健康记录跟踪</text>
<text class="tip-item">🩺 在线问诊咨询</text>
</view>
</view>
</view>
<!-- 智能分类网格 - 完全响应式 -->
<view class="smart-categories">
<view class="section-header">
<text class="section-title">智能分类</text>
<text class="section-desc">快速定位所需药品</text>
</view>
<view class="category-grid">
<view
v-for="category in categories"
:key="category.id"
class="category-card"
@click="switchCategory(category)"
:style="{ '--card-color': category.color }"
>
<view class="card-icon">
<text>{{ category.icon }}</text>
</view>
<text class="card-name">{{ category.name }}</text>
<text class="card-desc">{{ category.desc }}</text>
</view>
</view>
</view>
<!-- 健康资讯轮播 -->
<!-- 健康资讯轮播 (Moved Up) -->
<view class="health-news">
<view class="news-header">
<text class="news-title">健康资讯</text>
@@ -120,8 +82,48 @@
</swiper>
</view>
<!-- 智能服务入口 -->
<view class="smart-services">
<!-- 智能健康卡片 (Hidden) -->
<!-- <view class="smart-health-card" :style="{ marginTop: (statusBarHeight + 44 + 10) + 'px' }">
<view class="health-content">
<view class="health-header">
<text class="health-title">智能健康助手</text>
<text class="health-subtitle">根据您的健康数据推荐</text>
</view>
<view class="health-tips">
<text class="tip-item">💡 按时用药提醒</text>
<text class="tip-item">📋 健康记录跟踪</text>
<text class="tip-item">🩺 在线问诊咨询</text>
</view>
</view>
</view> -->
<!-- 智能分类网格 - 完全响应式 -->
<view class="smart-categories">
<view class="section-header">
<text class="section-title">智能分类</text>
<text class="section-desc">快速定位所需药品</text>
</view>
<view class="category-grid">
<view
v-for="category in categories"
:key="category.id"
class="category-card"
@click="switchCategory(category)"
:style="{ '--card-color': category.color }"
>
<view class="card-icon">
<text>{{ category.icon }}</text>
</view>
<text class="card-name">{{ category.name }}</text>
<text class="card-desc">{{ category.desc }}</text>
</view>
</view>
</view>
<!-- 健康资讯轮播 (Original Position - Removed) -->
<!-- 智能服务入口 (Hidden) -->
<!-- <view class="smart-services">
<view class="services-grid">
<view class="service-card" @click="navigateToConsultation">
<view class="service-icon" style="background: #2196F3;">
@@ -152,14 +154,14 @@
<text class="service-desc">健康管理助手</text>
</view>
</view>
</view>
</view> -->
<!-- 热销药品专区 -->
<view class="hot-products">
<view class="section-header">
<view class="title-section">
<text class="section-icon">🔥</text>
<text class="section-title">热销品</text>
<text class="section-title">热销品</text>
</view>
<view class="sort-tabs">
<text
@@ -180,30 +182,30 @@
class="product-card"
@click="navigateToProduct(product)"
>
<view class="product-badge" v-if="product.badge">{{ product.badge }}</view>
<view class="product-badge" v-if="product.is_hot">热销</view>
<image
class="product-image"
:src="product.image"
:src="product.main_image_url"
mode="aspectFill"
/>
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<text class="product-spec">{{ product.specification }}</text>
<!-- spec is omitted if not available -->
<view class="price-section">
<view class="current-price">
<text class="price-symbol">¥</text>
<text class="price-value">{{ product.price }}</text>
<text class="price-value">{{ product.base_price }}</text>
</view>
<text class="original-price" v-if="product.originalPrice > product.price">
¥{{ product.originalPrice }}
<text class="original-price" v-if="product.market_price != null && product.market_price! > product.base_price">
¥{{ product.market_price }}
</text>
</view>
<view class="product-meta">
<text class="manufacturer">{{ product.manufacturer }}</text>
<text class="manufacturer">{{ product.brand_name || product.shop_name || '自营' }}</text>
<view class="sales-info">
<text class="sales-count">已售{{ product.sales }}</text>
<text class="sales-count">已售{{ product.sale_count }}</text>
</view>
</view>
@@ -216,10 +218,14 @@
</view>
</view>
</view>
<!-- 加载状态提示 -->
<view class="load-more-status" v-if="loading || showLoadMore">
<text class="loading-text">正在加载更多商品...</text>
</view>
</view>
<!-- 家庭常备药 -->
<view class="family-medicine">
<!-- 家庭常备药 (Hidden) -->
<!-- <view class="family-medicine">
<view class="section-header">
<view class="title-section">
<text class="section-icon">🏠</text>
@@ -242,12 +248,12 @@
<text class="family-desc">{{ item.desc }}</text>
</view>
</view>
</view>
</view> -->
<!-- 智能推荐模块已隐藏 -->
<!-- 健康提醒 -->
<view class="health-reminder">
<!-- 健康提醒 (Hidden) -->
<!-- <view class="health-reminder">
<view class="reminder-content">
<text class="reminder-icon">⏰</text>
<view class="reminder-text">
@@ -258,7 +264,7 @@
<text class="action-text">查看</text>
</view>
</view>
</view>
</view> -->
<!-- 底部安全区域 -->
<view class="safe-area"></view>
@@ -280,7 +286,7 @@ const refreshing = ref(false)
const loading = ref(false)
const isFirstShow = ref(true)
const hasMore = ref(true)
const activeSort = ref('sales')
const activeSort = ref('recommend') // 默认展示智能推荐
const activeFilter = ref('recommend')
const currentPage = ref(1)
@@ -290,6 +296,7 @@ const recommendedProducts = ref<Product[]>([])
// 屏幕尺寸检测
const isMobile = ref(false)
const showLoadMore = ref(false)
// 导航栏显示控制
const showNavbar = ref(true)
@@ -358,12 +365,12 @@ const loadCategories = async () => {
}
// 获取热销商品(根据当前排序方式)
const loadHotProducts = async () => {
const loadHotProducts = async (targetLimit: number = 6) => {
try {
let products: Product[] = []
const limit = 6
const limit = targetLimit
console.log('加载热销商品,当前排序方式:', activeSort.value)
console.log('加载热销商品,当前排序方式:', activeSort.value, 'limit:', limit)
switch (activeSort.value) {
case 'sales':
@@ -522,9 +529,8 @@ const initPage = () => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight || 0
// 计算滚动区域高度 - 使用整个窗口高度
const windowHeight = systemInfo.windowHeight
scrollHeight.value = windowHeight
// 计算滚动区域高度 - 不再需要手动计算,使用 Flex 布局自动撑开
// scrollHeight.value = windowHeight - 50
// 检测屏幕尺寸
const screenWidth = systemInfo.screenWidth || systemInfo.windowWidth
@@ -609,6 +615,7 @@ const switchCategory = (category: any) => {
// 切换排序
const switchSort = (sortId: string) => {
activeSort.value = sortId
hasMore.value = true // 重置加载更多状态
// 重新加载热销商品,排序由 Supabase 服务处理
loadHotProducts()
}
@@ -641,75 +648,77 @@ const onRefresh = () => {
// 加载更多
const loadMore = async () => {
if (loading.value || !hasMore.value) return
console.log('=== 触发触底事件 ===')
if (loading.value) {
console.log('正在加载中,跳过')
return
}
showLoadMore.value = true
loading.value = true
try {
// 增加限制以加载更多推荐商品
const currentLimit = recommendedProducts.value.length + 6
await loadRecommendedProducts(currentLimit)
// 获取当前热销商品的数量
const currentCount = hotProducts.value.length
const nextLimit = currentCount + 6
// 假设如果返回的商品数量小于请求的限制,则没有更多数据
if (recommendedProducts.value.length < currentLimit) {
console.log('开始加载更多,当前数量:', currentCount, '目标数量:', nextLimit)
// 加载更多热销商品
await loadHotProducts(nextLimit)
// 检查是否还有更多数据
if (hotProducts.value.length === currentCount) {
hasMore.value = false
uni.showToast({
title: '没有更多了',
icon: 'none'
})
} else {
// 还有数据,或者是刚加载了一批
/* uni.showToast({
title: '加载完成',
icon: 'success'
}) */
}
uni.showToast({
title: '加载完成',
icon: 'success'
})
} catch (error) {
console.error('加载更多失败:', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
loading.value = false
// 稍微延迟隐藏加载条,让用户看到
setTimeout(() => {
showLoadMore.value = false
}, 500)
}
}
// 添加到购物车
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 addToCart = async (product: any) => {
uni.showLoading({ title: '添加中...' })
try {
// 尝试调用 Supabase 服务添加
const success = await supabaseService.addToCart(product.id, 1)
if (success) {
uni.showToast({
title: '已添加到购物车',
icon: 'success'
})
} else {
// 失败(如未登录),回退到本地存储或提示登录
// 这里简单提示失败
uni.showToast({
title: '添加失败,请先登录',
icon: 'none'
})
}
}
// 检查商品是否已存在
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'
})
} catch (e) {
console.error('添加到购物车异常', e)
uni.showToast({
title: '操作异常',
icon: 'none'
})
} finally {
uni.hideLoading()
}
}
// 导航函数
@@ -718,18 +727,16 @@ const navigateToNews = () => uni.navigateTo({ url: '/pages/news/list' })
const navigateToProduct = (product: any) => {
// 使用productId如果存在作为跳转的商品ID否则使用id
const productId = product.productId || product.id
// 传递完整的参数,确保商品详情页能正确加载
// 移除 URLSearchParams 内部的 encodeURIComponent因为 append 会自动编码
// 或者直接构建 URL 字符串以确保兼容性
const name = product.name || ''
const image = product.image || '/static/product1.jpg'
const price = product.price?.toString() || '0'
const originalPrice = (product.original_price || product.originalPrice || (product.price * 1.2).toFixed(2))?.toString()
// 使用 main_image_url
const image = product.main_image_url || product.image || '/static/product1.jpg'
const price = (product.base_price || product.price || 0).toString()
const originalPrice = (product.market_price || product.original_price || (parseFloat(price) * 1.2).toFixed(2))?.toString()
// 手动构建URL避免双重编码问题
uni.navigateTo({
url: `/pages/mall/consumer/product-detail?id=${productId}&productId=${productId}&price=${price}&originalPrice=${originalPrice}&name=${encodeURIComponent(name)}&image=${encodeURIComponent(image)}`
url: `/pages/mall/consumer/product-detail?id=${productId}&price=${price}&originalPrice=${originalPrice}&name=${encodeURIComponent(name)}&image=${encodeURIComponent(image)}`
})
}
const navigateToCategory = (item: any) => {
@@ -754,7 +761,8 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
.medic-home {
width: 100%;
min-height: 100vh;
height: 100vh;
overflow: hidden;
background: #f8fafc;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
line-height: 1.5;
@@ -762,6 +770,12 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
flex-direction: column;
}
.main-scroll {
flex: 1;
height: 1px; /* 让 flex 生效并允许滚动 */
width: 100%;
}
/* 智能导航栏 - 重新设计布局 */
.smart-navbar {
position: fixed;
@@ -1222,13 +1236,17 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
/* 产品网格 */
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
display: block;
column-count: 2;
column-gap: 10px;
margin-top: 20px;
min-height: 500px; /* 确保有足够高度触发滚动 */
padding-bottom: 20px;
}
.product-card {
break-inside: avoid;
margin-bottom: 10px;
background: #f8f9fa;
border-radius: 12px;
overflow: hidden;
@@ -1796,6 +1814,19 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
display: none; /* 隐藏描述 */
}
.load-more-status {
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.loading-text {
color: #888;
font-size: 14px;
}
.products-grid,
.recommend-grid {
grid-template-columns: repeat(2, 1fr); /* 手机端调整为双列显示 */
@@ -1964,7 +1995,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
}
.products-grid {
grid-template-columns: repeat(2, 1fr);
column-count: 2;
}
.recommend-grid {
@@ -2031,7 +2062,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
}
.products-grid {
grid-template-columns: repeat(3, 1fr);
column-count: 3;
}
.recommend-grid {
@@ -2068,7 +2099,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
}
.products-grid {
grid-template-columns: repeat(4, 1fr);
column-count: 4;
}
.recommend-grid {
@@ -2092,7 +2123,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
}
.products-grid {
grid-template-columns: repeat(4, 1fr);
column-count: 4;
}
.recommend-grid {