Files
medical-mall/pages/mall/consumer/favorites.uvue

309 lines
7.5 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="favorites-page">
<view class="product-grid">
<view v-if="favorites.length === 0" class="empty-state">
<text class="empty-icon">❤️</text>
<text class="empty-text">暂无收藏商品</text>
<button class="go-shopping-btn" @click="goShopping">去逛逛</button>
</view>
<view v-else v-for="(product, index) in favorites" :key="index" class="product-item" @click="goToDetail(product.id)">
<image :src="product.image" class="product-image" mode="aspectFill" />
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<text class="product-price">¥{{ product.price }}</text>
<view class="product-footer">
<text class="product-sales">已售 {{ product.sales }}</text>
<view class="action-btns">
<view class="cart-btn" @click.stop="addToCart(product)">
<text class="cart-icon">🛒</text>
</view>
<view class="remove-btn" @click.stop="removeFavorite(product.id)">
<text class="remove-icon">🗑️</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted } from 'vue'
import { supabaseService } from '@/utils/supabaseService.uts'
type Product = {
id: string
name: string
price: number
image: string
sales: number
shopId?: string
shopName?: string
}
const favorites = ref<Product[]>([])
onMounted(() => {
loadFavorites()
})
const addToCart = async (product: Product) => {
uni.showLoading({ title: '添加中' })
const success = await supabaseService.addToCart(product.id, 1)
uni.hideLoading()
if (success) {
uni.showToast({ title: '已添加到购物车', icon: 'success' })
} else {
uni.showToast({ title: '添加失败', icon: 'none' })
}
}
const loadFavorites = async () => {
const res = await supabaseService.getFavorites()
// Map response
favorites.value = res.map((item: any): Product => {
let prod: any = null
if (item instanceof UTSJSONObject) {
prod = item.get('ml_products')
} else {
prod = item['ml_products']
}
let image = '/static/default-product.png'
let id = ''
let name = '未知商品'
let price = 0
let sales = 0
if (prod != null) {
if (prod instanceof UTSJSONObject) {
id = prod.getString('id') || ''
name = prod.getString('name') || '未知商品'
price = prod.getNumber('base_price') || 0
image = prod.getString('main_image_url') || image
sales = prod.getNumber('sale_count') || 0
// 如果 main_image_url 为空,尝试解析 image_urls
if (image === '/static/default-product.png') {
const imgUrls = prod.getString('image_urls')
if (imgUrls) {
try {
const arr = JSON.parse(imgUrls)
if (Array.isArray(arr) && arr.length > 0) image = arr[0] as string
} catch(e) {}
}
}
} else {
id = (prod['id'] as string) || ''
name = (prod['name'] as string) || '未知商品'
price = (prod['base_price'] as number) || 0
image = (prod['main_image_url'] as string) || image
sales = (prod['sale_count'] as number) || 0
if (image === '/static/default-product.png') {
const imgUrls = prod['image_urls'] as string
if (imgUrls) {
try {
const arr = JSON.parse(imgUrls)
if (Array.isArray(arr) && arr.length > 0) image = arr[0] as string
} catch(e) {}
}
}
}
} else {
// 如果没取到商品,尝试直接从 item 取 target_id
if (item instanceof UTSJSONObject) {
id = item.getString('target_id') || ''
} else {
id = (item['target_id'] as string) || ''
}
}
return {
id: id,
name: name,
price: price,
image: image,
sales: sales,
shopId: '',
shopName: ''
}
})
}
const goShopping = () => {
uni.switchTab({
url: '/pages/mall/consumer/index'
})
}
const goToDetail = (id: string) => {
uni.navigateTo({
url: `/pages/mall/consumer/product-detail?productId=${id}`
})
}
const removeFavorite = async (id: string) => {
uni.showModal({
title: '取消收藏',
content: '确定要取消收藏该商品吗?',
success: async (res) => {
if (res.confirm) {
// toggleFavorite 返回最新的状态true=已收藏false=未收藏
const isStillFavorite = await supabaseService.toggleFavorite(id)
if (!isStillFavorite) {
// 现在的状态是"未收藏",说明取消成功
// Remove from local list
const index = favorites.value.findIndex(item => item.id === id)
if (index !== -1) {
favorites.value.splice(index, 1)
}
uni.showToast({
title: '已取消收藏',
icon: 'none'
})
} else {
uni.showToast({
title: '取消失败',
icon: 'none'
})
}
}
}
})
}
</script>
<style>
.favorites-page {
padding: 15px;
background-color: #f5f5f5;
min-height: 100vh;
}
.product-grid {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.empty-state {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 100px;
}
.empty-icon {
font-size: 60px;
margin-bottom: 20px;
color: #ddd;
}
.empty-text {
font-size: 16px;
color: #999;
margin-bottom: 20px;
}
.go-shopping-btn {
background-color: #ff5000;
color: white;
padding: 8px 24px;
border-radius: 20px;
font-size: 14px;
border: none;
}
.product-item {
width: calc(50% - 8px);
background-color: white;
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.product-image {
width: 100%;
height: 170px;
background-color: #f5f5f5;
}
.product-info {
padding: 10px;
display: flex;
flex-direction: column;
flex: 1;
}
.product-name {
font-size: 14px;
color: #333;
margin-bottom: 6px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
height: 40px;
line-height: 20px;
}
.product-price {
font-size: 16px;
color: #ff5000;
font-weight: bold;
margin-bottom: 6px;
}
.product-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
}
.product-sales {
font-size: 12px;
color: #999;
}
.action-btns {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.cart-btn, .remove-btn {
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.cart-btn {
background-color: #ff5000;
}
.cart-icon {
font-size: 14px;
color: white;
}
.remove-btn {
background-color: #f0f0f0;
}
.remove-icon {
font-size: 14px;
}
</style>