consumer模块完成95%,在和商家端对接聊天购物闭环
This commit is contained in:
@@ -15,12 +15,12 @@
|
||||
<text class="search-placeholder">请输入药品名称、症状或品牌</text>
|
||||
|
||||
<!-- 扫码图标 -->
|
||||
<view class="nav-icon-btn" @click="onScan">
|
||||
<view class="nav-icon-btn" @click.stop="onScan">
|
||||
<text class="nav-icon">🔳</text>
|
||||
</view>
|
||||
|
||||
<!-- 相机图标 -->
|
||||
<view class="nav-camera-btn" @click="onCamera">
|
||||
<view class="nav-camera-btn" @click.stop="onCamera">
|
||||
<text class="nav-camera-icon">📷</text>
|
||||
</view>
|
||||
|
||||
@@ -47,40 +47,12 @@
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<!-- 健康资讯轮播 (Moved Up) -->
|
||||
<!-- 健康资讯轮播 (Hidden) -->
|
||||
<!--
|
||||
<view class="health-news">
|
||||
<view class="news-header">
|
||||
<text class="news-title">健康资讯</text>
|
||||
<text class="news-more" @click="navigateToNews">更多 ></text>
|
||||
</view>
|
||||
<swiper
|
||||
class="news-swiper"
|
||||
:autoplay="true"
|
||||
:interval="4000"
|
||||
:duration="500"
|
||||
:circular="true"
|
||||
:indicator-dots="true"
|
||||
indicator-color="rgba(255,255,255,0.3)"
|
||||
indicator-active-color="#4CAF50"
|
||||
>
|
||||
<swiper-item
|
||||
v-for="news in healthNews"
|
||||
:key="news.id"
|
||||
class="news-item"
|
||||
>
|
||||
<view class="news-content" @click="viewNewsDetail(news)">
|
||||
<image
|
||||
class="news-image"
|
||||
:src="news.image"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="news-overlay">
|
||||
<text class="news-tag">{{ news.tag }}</text>
|
||||
<text class="news-caption">{{ news.title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
...
|
||||
</view>
|
||||
-->
|
||||
|
||||
<!-- 智能健康卡片 (Hidden) -->
|
||||
<!-- <view class="smart-health-card" :style="{ marginTop: (statusBarHeight + 44 + 10) + 'px' }">
|
||||
@@ -98,12 +70,19 @@
|
||||
</view> -->
|
||||
|
||||
<!-- 智能分类网格 - 完全响应式 -->
|
||||
<view class="smart-categories">
|
||||
<view class="smart-categories" :style="{ marginTop: (statusBarHeight + 44 + 10) + 'px' }">
|
||||
<view class="section-header">
|
||||
<text class="section-title">智能分类</text>
|
||||
<text class="section-desc">快速定位所需药品</text>
|
||||
<view class="category-tabs-pills">
|
||||
<view :class="['tab-pill', { active: categoryTab == 'category' }]" @click="categoryTab = 'category'">
|
||||
<text class="tab-text">智能分类</text>
|
||||
</view>
|
||||
<view :class="['tab-pill', { active: categoryTab == 'brand' }]" @click="categoryTab = 'brand'">
|
||||
<text class="tab-text">品牌甄选</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="section-desc">快速定位</text>
|
||||
</view>
|
||||
<view class="category-grid">
|
||||
<view class="category-grid" v-if="categoryTab === 'category'">
|
||||
<view
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
@@ -118,6 +97,22 @@
|
||||
<text class="card-desc">{{ category.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="category-grid" v-else>
|
||||
<view
|
||||
v-for="brand in brands"
|
||||
:key="brand.id"
|
||||
class="category-card"
|
||||
@click="switchBrand(brand)"
|
||||
style="--card-color: #5785e5"
|
||||
>
|
||||
<image v-if="brand.logo_url" :src="brand.logo_url" mode="aspectFit" class="brand-logo" style="width: 40px; height: 40px; border-radius: 20px;" />
|
||||
<view v-else class="card-icon">
|
||||
<text>🏢</text>
|
||||
</view>
|
||||
<text class="card-name">{{ brand.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 健康资讯轮播 (Original Position - Removed) -->
|
||||
@@ -210,7 +205,7 @@
|
||||
</view>
|
||||
|
||||
<view class="product-action">
|
||||
<view class="cart-btn" @click="addToCart(product)">
|
||||
<view class="cart-btn" @click.stop="addToCart(product)">
|
||||
<text class="cart-icon">+</text>
|
||||
<text class="cart-text">加入购物车</text>
|
||||
</view>
|
||||
@@ -276,7 +271,7 @@
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import supabaseService from '@/utils/supabaseService.uts'
|
||||
import type { Product, Category } from '@/utils/supabaseService.uts'
|
||||
import type { Product, Category, Brand } from '@/utils/supabaseService.uts'
|
||||
import { getCurrentUser } from '@/utils/store.uts'
|
||||
|
||||
// 响应式数据
|
||||
@@ -305,7 +300,9 @@ const scrollThreshold = 30 // 降低滚动阈值,使其更灵敏
|
||||
const scrollingUp = ref(false)
|
||||
|
||||
// 分类数据 - 从Supabase获取
|
||||
const categoryTab = ref<string>('category')
|
||||
const categories = ref<Category[]>([])
|
||||
const brands = ref<Brand[]>([])
|
||||
|
||||
// 排序标签
|
||||
const sortTabs = [
|
||||
@@ -344,26 +341,34 @@ const loadCategories = async () => {
|
||||
try {
|
||||
const categoriesData = await supabaseService.getCategories()
|
||||
// 映射字段:根据ml_categories表结构映射
|
||||
categories.value = categoriesData.map((cat: any) => ({
|
||||
const mappedCategories = categoriesData.map((cat: any) => ({
|
||||
id: cat.id,
|
||||
name: cat.name,
|
||||
icon: cat.icon_url || '📦', // 使用icon_url字段
|
||||
desc: cat.description || '', // 使用description字段
|
||||
color: '#4CAF50' // 默认颜色,表中可能没有color字段
|
||||
}))
|
||||
// 保持原始顺序或按ID排序,移除随机打乱
|
||||
categories.value = mappedCategories
|
||||
} catch (error) {
|
||||
console.error('加载分类数据失败:', error)
|
||||
// 如果加载失败,使用默认分类作为后备
|
||||
categories.value = [
|
||||
{ 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' }
|
||||
]
|
||||
categories.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 获取品牌数据
|
||||
const loadBrands = async () => {
|
||||
try {
|
||||
const brandsData = await supabaseService.getBrands()
|
||||
// 保持原始顺序
|
||||
brands.value = brandsData
|
||||
} catch (e) {
|
||||
console.error('加载品牌失败:', e)
|
||||
brands.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 获取热销商品(根据当前排序方式)
|
||||
const loadHotProducts = async (targetLimit: number = 6) => {
|
||||
try {
|
||||
@@ -403,6 +408,10 @@ const loadHotProducts = async (targetLimit: number = 6) => {
|
||||
}
|
||||
|
||||
console.log('加载到的商品数量:', products.length)
|
||||
if (products.length > 0) {
|
||||
console.log('Sample Product Merchant IDs:')
|
||||
products.slice(0, 3).forEach(p => console.log(` - Product: ${p.name}, MerchantID: ${p.merchant_id}`))
|
||||
}
|
||||
hotProducts.value = products
|
||||
} catch (error) {
|
||||
console.error('加载热销商品失败:', error)
|
||||
@@ -425,6 +434,7 @@ const initData = async () => {
|
||||
console.error('加载用户资料失败:', error)
|
||||
}
|
||||
await loadCategories()
|
||||
await loadBrands()
|
||||
await loadHotProducts()
|
||||
await loadRecommendedProducts()
|
||||
}
|
||||
@@ -581,8 +591,8 @@ const switchCategory = (category: any) => {
|
||||
console.log('=== switchCategory函数开始执行 ===')
|
||||
console.log('分类ID:', category.id, '分类名称:', category.name)
|
||||
|
||||
// 清除可能存在的旧数据
|
||||
uni.removeStorageSync('selectedCategory')
|
||||
// 使用Storage传递参数,确保switchTab后能被读取
|
||||
uni.setStorageSync('selectedCategory', category.id)
|
||||
|
||||
// 生成唯一的时间戳和随机参数,确保每次跳转都是新的页面
|
||||
const timestamp = Date.now()
|
||||
@@ -590,26 +600,24 @@ const switchCategory = (category: any) => {
|
||||
|
||||
// 构建带参数的URL,直接通过URL传递分类信息
|
||||
const url = `/pages/mall/consumer/category?categoryId=${category.id}&name=${encodeURIComponent(category.name)}×tamp=${timestamp}&random=${randomParam}`
|
||||
|
||||
console.log('跳转URL:', url)
|
||||
console.log('分类ID参数:', category.id)
|
||||
console.log('时间戳:', timestamp)
|
||||
console.log('随机参数:', randomParam)
|
||||
|
||||
// 使用uni.reLaunch跳转到分类页面,关闭所有页面并打开新页面
|
||||
// 这样可以确保每次跳转都是全新的页面实例,避免页面缓存问题
|
||||
// 虽然这会关闭当前主页,但可以确保分类页面总是重新加载
|
||||
uni.reLaunch({
|
||||
url: url,
|
||||
success: () => {
|
||||
console.log('✅ 使用reLaunch跳转到分类页面成功')
|
||||
console.log('=== switchCategory函数执行完成 ===')
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('❌ 跳转到分类页面失败:', err)
|
||||
console.log('=== switchCategory函数执行完成 ===')
|
||||
}
|
||||
})
|
||||
|
||||
uni.switchTab({
|
||||
url: '/pages/mall/consumer/category',
|
||||
success: () => {
|
||||
// 通过 event channel 或 globalData 传递
|
||||
const app = getApp()
|
||||
if (app.globalData != null) {
|
||||
app.globalData['selectedCategory'] = category.id
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const switchBrand = (brand: Brand) => {
|
||||
// 假设跳转到搜索结果页或者分类页带 filter
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/search?keyword=${encodeURIComponent(brand.name)}&type=brand&brandId=${brand.id}`
|
||||
})
|
||||
}
|
||||
|
||||
// 切换排序
|
||||
@@ -635,15 +643,27 @@ const viewNewsDetail = (news: any) => {
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = () => {
|
||||
const onRefresh = async () => {
|
||||
refreshing.value = true
|
||||
setTimeout(() => {
|
||||
refreshing.value = false
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 1500)
|
||||
|
||||
try {
|
||||
// 重新加载数据
|
||||
await initData()
|
||||
} catch (e) {
|
||||
console.error('刷新数据失败:', e)
|
||||
} finally {
|
||||
// 延迟关闭刷新动画,确保用户能看到刷新过程
|
||||
setTimeout(() => {
|
||||
refreshing.value = false
|
||||
// 延迟显示提示,避免与动画冲突
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 200)
|
||||
}, 800)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
@@ -692,8 +712,7 @@ const loadMore = async () => {
|
||||
}
|
||||
|
||||
// 添加到购物车
|
||||
const addToCart = async (product: any, e: any | null = null) => {
|
||||
e?.stopPropagation()
|
||||
const addToCart = async (product: any) => {
|
||||
uni.showLoading({ title: '添加中...' })
|
||||
try {
|
||||
// 尝试调用 Supabase 服务添加
|
||||
@@ -750,44 +769,6 @@ const navigateToPrescription = () => uni.navigateTo({ url: '/pages/medicine/pres
|
||||
const navigateToOTC = () => uni.navigateTo({ url: '/pages/medicine/otc' })
|
||||
const navigateToHealthTools = () => uni.navigateTo({ url: '/pages/medicine/tools' })
|
||||
const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders' })
|
||||
|
||||
// 扫码功能
|
||||
const onScan = (e: any | null) => {
|
||||
e?.stopPropagation()
|
||||
uni.scanCode({
|
||||
success: (res) => {
|
||||
console.log('扫码结果:' + res.result)
|
||||
uni.showToast({
|
||||
title: '扫码成功',
|
||||
icon: 'success'
|
||||
})
|
||||
// 这里可以添加基于扫码结果的逻辑,比如跳转到商品详情
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('扫码失败:', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 相机功能e: any | null) => {
|
||||
e?.stopPropagation()
|
||||
const onCamera = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sourceType: ['camera'],
|
||||
success: (res) => {
|
||||
console.log('拍照结果:', res.tempFilePaths)
|
||||
uni.showToast({
|
||||
title: '拍摄成功',
|
||||
icon: 'success'
|
||||
})
|
||||
// 这里可以添加基于图片的逻辑,比如图搜
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('拍照失败:', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -1004,10 +985,51 @@ const onCamera = () => {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.category-tabs-pills {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #f0f2f5;
|
||||
padding: 3px;
|
||||
border-radius: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab-pill {
|
||||
padding: 6px 18px;
|
||||
border-radius: 17px;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tab-pill.active {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab-pill.active .tab-text {
|
||||
color: #4CAF50;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
color: #666;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.section-title.active {
|
||||
color: #4CAF50;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.section-desc {
|
||||
|
||||
Reference in New Issue
Block a user