consumer模块完成90%,前端完成supabase对接
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user