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

@@ -229,7 +229,7 @@
<script setup lang="uts">
import { ref, reactive, onMounted, computed } from 'vue'
import supabaseService from '@/utils/supabaseService.uts'
import { supabaseService } from '@/utils/supabaseService.uts'
import type { Product } from '@/utils/supabaseService.uts'
// 状态定义
@@ -248,28 +248,9 @@ const priceSortAsc = ref(false) // 价格排序是否为升序
const searchHistory = ref<string[]>([])
const hotSearchList = ref<any[]>([])
const guessList = ref<any[]>([])
const allGuessItems = ref<any[]>([]) // 缓存所有猜你喜欢商品
const searchResults = ref<any[]>([])
// 模拟数据库
const mockDatabase = {
hot: [
{ keyword: '感冒灵', hot: true },
{ keyword: '布洛芬', hot: true },
{ keyword: '口罩', hot: true },
{ keyword: '维生素C', hot: false },
{ keyword: '板蓝根', hot: false },
{ keyword: '创可贴', hot: false },
],
guess: [
{ id: 'g1', name: '医用外科口罩', price: 19.9, image: 'https://picsum.photos/200/200?random=1', sales: '1万+' },
{ id: 'g2', name: '酒精消毒液', price: 9.9, image: 'https://picsum.photos/200/200?random=2', sales: '5000+' },
{ id: 'g3', name: '电子体温计', price: 29.9, image: 'https://picsum.photos/200/200?random=3', sales: '2000+' },
{ id: 'g4', name: '碘伏消毒液', price: 5.5, image: 'https://picsum.photos/200/200?random=4', sales: '1000+' },
{ id: 'g5', name: '退热贴', price: 15.8, image: 'https://picsum.photos/200/200?random=5', sales: '3000+' },
{ id: 'g6', name: '棉签', price: 3.9, image: 'https://picsum.photos/200/200?random=6', sales: '8000+' },
]
}
// 搜索建议
const searchSuggestions = computed(() => {
if (!searchKeyword.value) return []
@@ -327,22 +308,35 @@ const initPage = () => {
}
// 加载基础数据
const loadData = () => {
// loading.value = true // 不使用全局loading避免影响搜索状态
const loadData = async () => {
isError.value = false
// 模拟网络请求
setTimeout(() => {
try {
loadSearchHistory()
hotSearchList.value = mockDatabase.hot
guessList.value = mockDatabase.guess
// loading.value = false // 不使用全局loading
} catch (e) {
isError.value = true
// loading.value = false
}
}, 500)
try {
loadSearchHistory()
// 获取热门商品作为热门搜索推荐和猜你喜欢
// 获取更多数据以便"换一批"
const hotProducts = await supabaseService.getHotProducts(30)
hotSearchList.value = hotProducts.slice(0, 10).map((p: any) => ({
keyword: p.name,
hot: true
}))
allGuessItems.value = hotProducts.map((p: any) => ({
id: p.id,
name: p.name,
price: p.base_price,
image: p.main_image_url || '/static/default.jpg',
sales: typeof p.sale_count === 'number' ? p.sale_count : 0
}))
// 初始显示随机6个
refreshGuessListItems()
} catch (e) {
console.error('Load data failed', e)
isError.value = true
}
}
// 点击重试
@@ -441,13 +435,12 @@ const selectSuggestion = (suggestion: string) => {
const currentPage = ref(1)
const performSearch = () => {
const performSearch = async () => {
// 再次强制设置状态,确保万无一失
showResults.value = true
loading.value = true
// 重置页码
currentPage.value = 1
// 保持旧数据直到新数据回来,或者依靠 loading 状态完全遮罩
// 使用 Supabase 搜索真实数据
const keyword = searchKeyword.value.trim()
@@ -462,25 +455,39 @@ const performSearch = () => {
if (activeSort.value === 'price') {
sortBy = 'price'
ascending = priceSortAsc.value
}
} else if (activeSort.value === 'default') {
sortBy = 'default'
}
supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
.then((response) => {
searchResults.value = response.data as any[]
hasMore.value = response.hasmore
loading.value = false
// 如果无结果,显示空状态
if (searchResults.value.length === 0) {
// empty-result 组件会自动显示
}
})
.catch((error) => {
console.error('搜索失败:', error)
loading.value = false
// 可以显示错误提示,但为了用户体验,先不显示
// 保持搜索结果为空让empty-result显示
})
try {
const response = await supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
searchResults.value = response.data.map((p: any) => {
let tag = ''
if (p.tags) {
try {
const tags = (typeof p.tags === 'string') ? JSON.parse(p.tags) : p.tags
if (Array.isArray(tags) && tags.length > 0) tag = String(tags[0])
} catch(e) {}
}
return {
id: p.id,
name: p.name,
image: p.main_image_url || '/static/default.jpg',
price: p.base_price,
specification: p.specification || '标准规格',
tag: tag,
sales: p.sale_count || 0
}
})
hasMore.value = response.hasmore
} catch(e) {
console.error('Search failed', e)
} finally {
loading.value = false
}
}
// 切换排序
@@ -499,7 +506,7 @@ const switchSort = (type: string) => {
performSearch()
}
const loadMore = () => {
const loadMore = async () => {
if (loading.value || !hasMore.value || !searchKeyword.value.trim()) return
loading.value = true
@@ -513,79 +520,70 @@ const loadMore = () => {
if (activeSort.value === 'price') {
sortBy = 'price'
ascending = priceSortAsc.value
}
} else if (activeSort.value === 'default') {
sortBy = 'default'
}
supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
.then((response) => {
searchResults.value.push(...(response.data as any[]))
hasMore.value = response.hasmore
loading.value = false
})
.catch((error) => {
console.error('加载更多失败:', error)
loading.value = false
// 加载失败时,假设没有更多数据
hasMore.value = false
})
try {
const response = await supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
const newItems = response.data.map((p: any) => {
let tag = ''
if (p.tags) {
try {
const tags = (typeof p.tags === 'string') ? JSON.parse(p.tags) : p.tags
if (Array.isArray(tags) && tags.length > 0) tag = String(tags[0])
} catch(e) {}
}
return {
id: p.id,
name: p.name,
image: p.main_image_url || '/static/default.jpg',
price: p.base_price,
specification: p.specification || '标准规格',
tag: tag,
sales: p.sale_count || 0
}
})
searchResults.value.push(...newItems)
hasMore.value = response.hasmore
} catch(e) {
console.error('Load more failed', e)
hasMore.value = false
} finally {
loading.value = false
}
}
const refreshGuessList = () => {
uni.showLoading({ title: '刷新中' })
setTimeout(() => {
guessList.value = guessList.value.sort(() => Math.random() - 0.5)
uni.hideLoading()
}, 500)
setTimeout(() => {
refreshGuessListItems()
uni.hideLoading()
}, 500)
}
const refreshGuessListItems = () => {
if (allGuessItems.value.length > 0) {
// 简单的随机乱序并取前6个
const shuffled = [...allGuessItems.value].sort(() => Math.random() - 0.5)
guessList.value = shuffled.slice(0, 6)
}
}
const viewProductDetail = (item: any) => {
// 跳转详情页逻辑
console.log('查看商品', item)
// 跳转详情页逻辑 - 传递必要的参数作为预加载/fallback
uni.navigateTo({
url: `/pages/mall/consumer/product-detail?productId=${item.id}&price=${item.price}&originalPrice=${item.original_price || ''}&name=${encodeURIComponent(item.name)}&image=${encodeURIComponent(item.image)}`
url: `/pages/mall/consumer/product-detail?productId=${item.id}&price=${item.price}&name=${encodeURIComponent(item.name)}`
})
}
// 添加到购物车
// 添加到购物车 - 搜索列表无法选择规格,跳转详情页
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_search_default',
shopName: product.shopName || (product.tag === '自营' ? '平台自营大药房' : '优质大药房'),
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'
})
uni.showToast({ title: '请选择规格', icon: 'none' })
setTimeout(() => {
viewProductDetail(product)
}, 800)
}
const openCamera = () => {