From d57592ca7d17ce3d209458375b3de10683099cca Mon Sep 17 00:00:00 2001 From: cyh666666 <2398882793@qq.com> Date: Fri, 30 Jan 2026 17:29:02 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/mall/consumer/address-edit.uvue | 199 +- pages/mall/consumer/address-list copy.uvue | 219 +++ pages/mall/consumer/address-list.uvue | 87 +- pages/mall/consumer/cart copy.uvue | 225 ++- pages/mall/consumer/cart.uvue | 254 ++- pages/mall/consumer/checkout copy 2.uvue | 1733 +++++++++++++++++ pages/mall/consumer/checkout.uvue | 491 ++++- .../mall/consumer/product-detail copy 2.uvue | 1392 +++++++++++++ pages/mall/consumer/product-detail copy.uvue | 1163 +++++++++++ ...roduct-detail copy完成商品详情数据获取.uvue} | 223 ++- ...oduct-detail copy完成图片数量数据获取.uvue | 1359 +++++++++++++ pages/mall/consumer/product-detail.uvue | 452 ++++- types/mall-types.uts | 9 + utils/supabaseService.uts | 604 +++++- 14 files changed, 8003 insertions(+), 407 deletions(-) create mode 100644 pages/mall/consumer/address-list copy.uvue create mode 100644 pages/mall/consumer/checkout copy 2.uvue create mode 100644 pages/mall/consumer/product-detail copy 2.uvue create mode 100644 pages/mall/consumer/product-detail copy.uvue rename pages/mall/consumer/{product-detail - 副本.uvue => product-detail copy完成商品详情数据获取.uvue} (72%) create mode 100644 pages/mall/consumer/product-detail copy完成图片数量数据获取.uvue diff --git a/pages/mall/consumer/address-edit.uvue b/pages/mall/consumer/address-edit.uvue index 18ee45d0..ce30fb61 100644 --- a/pages/mall/consumer/address-edit.uvue +++ b/pages/mall/consumer/address-edit.uvue @@ -53,6 +53,7 @@ + + diff --git a/pages/mall/consumer/address-list.uvue b/pages/mall/consumer/address-list.uvue index 1b6aeefa..9938dd2a 100644 --- a/pages/mall/consumer/address-list.uvue +++ b/pages/mall/consumer/address-list.uvue @@ -36,6 +36,7 @@ + + diff --git a/pages/mall/consumer/checkout.uvue b/pages/mall/consumer/checkout.uvue index 3c744516..d96e6e2a 100644 --- a/pages/mall/consumer/checkout.uvue +++ b/pages/mall/consumer/checkout.uvue @@ -120,8 +120,42 @@ + + + + + + + - + + + + + {{ address.recipient_name }} + {{ address.phone }} + + 默认 + + + {{ getFullAddress(address) }} + + + + + + + + + 📍 + 暂无收货地址 + + + + + + 本地地址(未同步) @@ -138,8 +172,8 @@ - - + + 📍 暂无收货地址 @@ -241,6 +275,7 @@ + + diff --git a/pages/mall/consumer/product-detail copy.uvue b/pages/mall/consumer/product-detail copy.uvue new file mode 100644 index 00000000..5e7fae15 --- /dev/null +++ b/pages/mall/consumer/product-detail copy.uvue @@ -0,0 +1,1163 @@ + + + + + + diff --git a/pages/mall/consumer/product-detail - 副本.uvue b/pages/mall/consumer/product-detail copy完成商品详情数据获取.uvue similarity index 72% rename from pages/mall/consumer/product-detail - 副本.uvue rename to pages/mall/consumer/product-detail copy完成商品详情数据获取.uvue index 500dd3ed..63b4a9ad 100644 --- a/pages/mall/consumer/product-detail - 副本.uvue +++ b/pages/mall/consumer/product-detail copy完成商品详情数据获取.uvue @@ -109,6 +109,7 @@ + + diff --git a/pages/mall/consumer/product-detail.uvue b/pages/mall/consumer/product-detail.uvue index 27e3757e..a1da08fa 100644 --- a/pages/mall/consumer/product-detail.uvue +++ b/pages/mall/consumer/product-detail.uvue @@ -3,7 +3,7 @@ - + @@ -34,6 +34,23 @@ 进店 > + + + 功能主治 + {{ product.usage }} + + + + + 商品参数 + + 规格: {{ product.specification }} + 有效期: {{ product.expiry_date }} + 批准文号: {{ product.approval_number }} + + > + + 规格 @@ -65,6 +82,15 @@ 商品详情 {{ product.description || '暂无详细描述' }} + + + + @@ -104,6 +130,50 @@ + + + + + + 商品参数 + × + + + + 规格 + {{ product.specification }} + + + 功能主治 + {{ product.usage }} + + + 副作用 + {{ product.side_effects }} + + + 注意事项 + {{ product.precautions }} + + + 有效期 + {{ product.expiry_date }} + + + 储存条件 + {{ product.storage_conditions }} + + + 批准文号 + {{ product.approval_number }} + + + 标签 + {{ product.tags.join(', ') }} + + + + @@ -148,7 +218,8 @@ export default { selectedSkuId: '', selectedSpec: '', quantity: 1, - isFavorite: false + isFavorite: false, + showParams: false } }, onLoad(options: any) { @@ -241,42 +312,186 @@ export default { async loadProductDetail(productId: string, options: any = {}) { // 尝试从数据库加载 - let dbProduct = null + let dbProductRaw = null try { console.log('正在尝试从数据库加载商品详情:', productId) - dbProduct = await supabaseService.getProductById(productId) - console.log('数据库返回的商品详情:', dbProduct) + dbProductRaw = await supabaseService.getProductById(productId) + console.log('数据库返回的商品详情 (原始数据):', dbProductRaw) + + // 调试:打印数据库返回的所有字段 + if (dbProductRaw) { + console.log('数据库返回字段详情:') + if (Array.isArray(dbProductRaw)) { + console.log('返回数据是数组,长度:', dbProductRaw.length) + if (dbProductRaw.length > 0) { + const firstItem = dbProductRaw[0] + console.log('数组第一个元素:', firstItem) + for (const key in firstItem) { + console.log(` ${key}:`, firstItem[key], typeof firstItem[key]) + } + } + } else { + console.log('返回数据是对象') + for (const key in dbProductRaw) { + console.log(` ${key}:`, dbProductRaw[key], typeof dbProductRaw[key]) + } + } + } } catch (e) { console.error('Failed to load product from DB', e) } + // 处理数据库返回数据:可能是数组或对象 + let dbProduct = null + if (dbProductRaw) { + if (Array.isArray(dbProductRaw)) { + if (dbProductRaw.length > 0) { + dbProduct = dbProductRaw[0] // 取数组第一个元素 + } else { + console.warn('数据库返回空数组') + } + } else { + dbProduct = dbProductRaw // 已经是对象 + } + } + if (dbProduct) { console.log('使用数据库数据渲染页面') - // 使用数据库数据 - const images = [] as Array - if (dbProduct.image) images.push(dbProduct.image) - else if (options.image) images.push(decodeURIComponent(options.image as string)) - else images.push('/static/product1.jpg') - // 补充模拟图片 - images.push('/static/product2.jpg') - images.push('/static/product3.jpg') + // 调试:打印dbProduct的详细结构和类型 + console.log('dbProduct类型:', typeof dbProduct) + console.log('dbProduct原型:', Object.getPrototypeOf(dbProduct)) + console.log('dbProduct的键:') + for (let key in dbProduct) { + console.log(' ', key, ':', dbProduct[key], '类型:', typeof dbProduct[key]) + } + + // 验证必要字段,如果关键字段缺失则使用模拟数据 + // 注意:数据库返回的字段可能与本地ProductType不完全匹配 + console.log('验证必要字段,dbProduct:', dbProduct) + + // 尝试多种方式访问属性 + const idValue = dbProduct.id !== undefined ? dbProduct.id : (dbProduct['id'] !== undefined ? dbProduct['id'] : undefined) + const nameValue = dbProduct.name !== undefined ? dbProduct.name : (dbProduct['name'] !== undefined ? dbProduct['name'] : undefined) + const priceValue = dbProduct.price !== undefined ? dbProduct.price : (dbProduct['price'] !== undefined ? dbProduct['price'] : undefined) + + const hasId = idValue !== undefined && idValue !== null + const hasName = nameValue !== undefined && nameValue !== null + const hasPrice = priceValue !== undefined && priceValue !== null + + const hasRequiredFields = dbProduct && hasId && hasName && hasPrice + console.log('字段检查 - id:', idValue, 'hasId:', hasId, 'name:', nameValue, 'hasName:', hasName, 'price:', priceValue, 'hasPrice:', hasPrice) + console.log('hasRequiredFields:', hasRequiredFields) + + if (!hasRequiredFields) { + console.warn('数据库返回数据缺少必要字段,使用模拟数据') + // 继续执行,会进入下面的else分支 + dbProduct = null + } else { + // 更新dbProduct的字段为实际值,确保后续使用正确的属性访问 + if (dbProduct.id === undefined && idValue !== undefined) dbProduct.id = idValue + if (dbProduct.name === undefined && nameValue !== undefined) dbProduct.name = nameValue + if (dbProduct.price === undefined && priceValue !== undefined) dbProduct.price = priceValue + // 使用数据库数据 - 处理字段映射 + // 数据库Product接口和本地ProductType接口字段可能不同 + const images = [] as Array + + // 处理图片字段:优先使用images字段,其次使用image字段 + console.log('处理数据库图片字段:') + console.log('dbProduct.images:', dbProduct.images, '类型:', typeof dbProduct.images) + console.log('dbProduct.image:', dbProduct.image, '类型:', typeof dbProduct.image) + + // 尝试从数据库的images字段获取图片(可能是字符串或数组) + if (dbProduct.images) { + let imagesArray: any[] = [] + if (typeof dbProduct.images === 'string') { + try { + imagesArray = JSON.parse(dbProduct.images) + console.log('解析images字符串成功:', imagesArray) + } catch (e) { + console.error('解析images字段失败:', e, dbProduct.images) + // 如果不是JSON,尝试按逗号分割 + if (dbProduct.images.includes(',')) { + imagesArray = dbProduct.images.split(',').map((img: string) => img.trim()) + } else if (dbProduct.images) { + imagesArray = [dbProduct.images] + } + } + } else if (Array.isArray(dbProduct.images)) { + imagesArray = dbProduct.images + } + + if (imagesArray.length > 0) { + console.log('从数据库images字段获取图片数组:', imagesArray) + for (const img of imagesArray) { + if (typeof img === 'string' && img) { + images.push(img) + } + } + } + } + + // 如果没有从images字段获取到图片,尝试使用image字段 + if (images.length === 0 && dbProduct.image) { + console.log('使用单张图片字段:', dbProduct.image) + images.push(dbProduct.image) + } + + // 如果仍然没有图片,使用传入的图片或默认图片 + if (images.length === 0) { + if (options.image) { + images.push(decodeURIComponent(options.image as string)) + } else { + images.push('/static/product1.jpg') + } + } + + // 补充模拟图片(如果图片数量不足3张) + const needSupplementCount = 3 - images.length + if (needSupplementCount > 0) { + const supplementalImages = ['/static/product2.jpg', '/static/product3.jpg'] + for (let i = 0; i < needSupplementCount && i < supplementalImages.length; i++) { + images.push(supplementalImages[i]) + } + } + + console.log('最终图片数组:', images) + + // 映射字段:数据库shop_id对应本地merchant_id + const merchantId = dbProduct.shop_id || dbProduct.merchant_id || 'merchant_001' + + // 确保数值字段有效 + const price = typeof dbProduct.price === 'number' ? dbProduct.price : 0 + const stock = (dbProduct.stock != null && !isNaN(Number(dbProduct.stock))) ? Math.floor(Number(dbProduct.stock)) : 100 + const sales = (dbProduct.sales != null && !isNaN(Number(dbProduct.sales))) ? Math.floor(Number(dbProduct.sales)) : 50 - this.product = { - id: dbProduct.id, - merchant_id: dbProduct.shop_id || 'merchant_001', - category_id: dbProduct.category_id, - name: dbProduct.name, - description: dbProduct.description || '这是一个高品质的商品,具有优秀的性能和优美的外观设计。采用环保材料,经过严格质检,保证用户的使用体验。', - images: images, - price: dbProduct.price, - original_price: dbProduct.original_price || null, - stock: dbProduct.stock !== undefined ? dbProduct.stock : 0, // 确保使用数据库中的库存 - sales: dbProduct.sales !== undefined ? dbProduct.sales : 0, // 确保使用数据库中的销量 - status: 1, - created_at: dbProduct.created_at || '2024-01-01' - } as ProductType - console.log('页面 product 对象已更新:', this.product) + this.product = { + id: dbProduct.id || productId, + merchant_id: merchantId, + category_id: dbProduct.category_id || 'cat_001', + name: dbProduct.name || '商品名称', + description: dbProduct.description || '这是一个高品质的商品,具有优秀的性能和优美的外观设计。采用环保材料,经过严格质检,保证用户的使用体验。', + images: images, + price: price, + original_price: (dbProduct.original_price != null && !isNaN(Number(dbProduct.original_price))) ? Number(dbProduct.original_price) : null, + stock: stock, + sales: sales, + status: 1, + created_at: dbProduct.created_at || '2024-01-01', + // 药品相关字段 + specification: dbProduct.specification || null, + usage: dbProduct.usage || null, + side_effects: dbProduct.side_effects || null, + precautions: dbProduct.precautions || null, + expiry_date: dbProduct.expiry_date || null, + storage_conditions: dbProduct.storage_conditions || null, + approval_number: dbProduct.approval_number || null, + tags: dbProduct.tags ? (typeof dbProduct.tags === 'string' ? JSON.parse(dbProduct.tags) : dbProduct.tags) : [] + } as ProductType + console.log('页面 product 对象已更新:', this.product) + console.log('商品图片数组:', this.product.images) + console.log('商品价格:', this.product.price, '库存:', this.product.stock, '销量:', this.product.sales) + } } else { console.log('数据库无数据或加载失败,使用模拟数据') // 数据库无数据时,使用原有模拟逻辑 @@ -378,6 +593,10 @@ export default { } ] }, + + onSwiperChange(e: any) { + this.currentImageIndex = e.detail.current + }, showSpecModal() { this.showSpec = true @@ -626,6 +845,21 @@ export default { getAvailableStock() { return this.getMaxQuantity() + }, + + previewImage(index: number) { + uni.previewImage({ + current: index, + urls: this.product.images + }) + }, + + showParamsModal() { + this.showParams = true + }, + + hideParamsModal() { + this.showParams = false } } } @@ -993,4 +1227,166 @@ export default { width: 100rpx; text-align: right; } + +/* 功能主治样式 */ +.function-section { + background-color: #fff; + padding: 30rpx; + margin-bottom: 20rpx; +} + +.function-title { + font-size: 30rpx; + color: #333; + font-weight: bold; + margin-bottom: 15rpx; + display: block; +} + +.function-content { + font-size: 28rpx; + color: #666; + line-height: 1.5; +} + +/* 商品参数样式 */ +.params-section { + background-color: #fff; + padding: 30rpx; + margin-bottom: 20rpx; + display: flex; + align-items: center; +} + +.params-title { + font-size: 30rpx; + color: #333; + width: 120rpx; + flex-shrink: 0; +} + +.params-summary { + flex: 1; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; +} + +.params-item { + font-size: 26rpx; + color: #666; + line-height: 1.5; + margin-right: 20rpx; + margin-bottom: 5rpx; + white-space: nowrap; +} + +.params-arrow { + font-size: 28rpx; + color: #999; + flex-shrink: 0; + margin-left: 10rpx; +} + +/* 商品参数弹窗样式 */ +.params-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: flex-end; + z-index: 1000; +} + +.params-content { + background-color: #fff; + width: 100%; + max-height: 80vh; + border-radius: 20rpx 20rpx 0 0; + padding: 30rpx; +} + +.params-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30rpx; + padding-bottom: 20rpx; + border-bottom: 1rpx solid #eee; +} + +.params-title { + font-size: 32rpx; + font-weight: bold; + color: #333; + width: auto; +} + +.params-list { + max-height: 60vh; + overflow-y: auto; +} + +.params-item { + display: flex; + align-items: flex-start; + padding: 20rpx 0; + border-bottom: 1rpx solid #f5f5f5; +} + +.params-label { + font-size: 28rpx; + color: #333; + font-weight: bold; + width: 150rpx; + flex-shrink: 0; +} + +.params-value { + flex: 1; + font-size: 28rpx; + color: #666; + line-height: 1.5; +} + +/* 商品详情图片样式 */ +.detail-images { + margin-top: 30rpx; +} + +.detail-image { + width: 100%; + margin-bottom: 20rpx; + border-radius: 10rpx; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); +} + +/* 电脑端适配 */ +@media (min-width: 768px) { + .params-section { + padding: 20rpx 30rpx; + } + + .params-summary { + flex-wrap: nowrap; + justify-content: space-between; + } + + .params-item { + flex: 1; + margin-right: 0; + text-align: center; + white-space: normal; + word-break: break-word; + padding: 0 10rpx; + } + + .params-arrow { + margin-left: 20rpx; + } +} diff --git a/types/mall-types.uts b/types/mall-types.uts index 6a54f7ee..de9460f9 100644 --- a/types/mall-types.uts +++ b/types/mall-types.uts @@ -85,6 +85,15 @@ export type ProductType = { sales: number status: number created_at: string + // 药品相关字段 + specification?: string | null // 规格说明 + usage?: string | null // 用法用量 + side_effects?: string | null // 副作用 + precautions?: string | null // 注意事项 + expiry_date?: string | null // 有效期 + storage_conditions?: string | null // 储存条件 + approval_number?: string | null // 批准文号 + tags?: Array | null // 商品标签 } // 商品SKU类型 diff --git a/utils/supabaseService.uts b/utils/supabaseService.uts index bac53939..3ee23527 100644 --- a/utils/supabaseService.uts +++ b/utils/supabaseService.uts @@ -25,14 +25,46 @@ export interface Product { original_price?: number image?: string manufacturer: string - sales: number - stock: number + sales?: number + stock?: number badge?: string shop_id?: string shop_name?: string created_at?: string } +export interface CartItem { + id: string + user_id: string + product_id: string + sku_id?: string + quantity: number + selected: boolean + product_name?: string + product_image?: string + product_price?: number + product_specification?: string + shop_id?: string + shop_name?: string + created_at?: string + updated_at?: string +} + +export interface UserAddress { + id: string + user_id: string + recipient_name: string + phone: string + province: string + city: string + district: string + detail_address: string + postal_code?: string + is_default: boolean + created_at?: string + updated_at?: string +} + export interface PaginatedResponse { data: T[] total: number @@ -42,6 +74,17 @@ export interface PaginatedResponse { } class SupabaseService { + // 获取当前用户ID + private getCurrentUserId(): string | null { + try { + const userId = uni.getStorageSync('user_id') + return userId ? userId as string : null + } catch (e) { + console.error('获取用户ID失败:', e) + return null + } + } + // 获取所有分类 async getCategories(): Promise { try { @@ -176,7 +219,7 @@ class SupabaseService { .select('*') .eq('id', productId) .single() - .execute() + .executeAs() if (response.error) { console.error('获取商品详情失败:', response.error) @@ -304,6 +347,561 @@ class SupabaseService { return [] } } + + // 获取当前用户的购物车商品(关联商品和店铺信息) + async getCartItems(): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.warn('用户未登录,无法获取购物车') + return [] + } + + // 查询购物车表,并关联商品表(使用内联关联) + const response = await supa + .from('ml_shopping_cart') + .select(` + id, + user_id, + product_id, + sku_id, + quantity, + selected, + created_at, + updated_at, + products!inner ( + id, + name, + image, + price, + specification, + shop_id, + shop_name + ) + `) + .eq('user_id', userId) + .order('created_at', { ascending: false }) + .execute() + + if (response.error) { + console.error('获取购物车失败:', response.error) + return [] + } + + // 处理返回数据,构建CartItem数组 + const cartItems: CartItem[] = [] + if (response.data && Array.isArray(response.data)) { + for (const item of response.data) { + const product = item.products as any + + cartItems.push({ + id: item.id as string, + user_id: item.user_id as string, + product_id: item.product_id as string, + sku_id: item.sku_id as string, + quantity: item.quantity as number, + selected: item.selected as boolean, + product_name: product?.name as string, + product_image: product?.image as string, + product_price: product?.price as number, + product_specification: product?.specification as string, + shop_id: product?.shop_id as string, + shop_name: product?.shop_name as string, + created_at: item.created_at as string, + updated_at: item.updated_at as string + }) + } + } + + return cartItems + } catch (error) { + console.error('获取购物车异常:', error) + return [] + } + } + + // 添加商品到购物车 + async addToCart(productId: string, quantity: number = 1, skuId?: string): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法添加商品到购物车') + return false + } + + // 检查商品是否已在购物车中 + const existingResponse = await supa + .from('ml_shopping_cart') + .select('*') + .eq('user_id', userId) + .eq('product_id', productId) + .eq('sku_id', skuId || '') + .single() + .execute() + + let response + if (existingResponse.data) { + // 商品已存在,更新数量 + const existingItem = existingResponse.data as any + response = await supa + .from('ml_shopping_cart') + .update({ + quantity: (existingItem.quantity || 0) + quantity, + updated_at: new Date().toISOString() + }) + .eq('id', existingItem.id) + .execute() + } else { + // 商品不存在,添加新记录 + response = await supa + .from('ml_shopping_cart') + .insert({ + user_id: userId, + product_id: productId, + sku_id: skuId || null, + quantity: quantity, + selected: true, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + }) + .execute() + } + + if (response.error) { + console.error('添加商品到购物车失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('添加商品到购物车异常:', error) + return false + } + } + + // 更新购物车商品数量 + async updateCartItemQuantity(cartItemId: string, quantity: number): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法更新购物车') + return false + } + + if (quantity < 1) { + // 数量小于1时删除商品 + return await this.deleteCartItem(cartItemId) + } + + const response = await supa + .from('ml_shopping_cart') + .update({ + quantity: quantity, + updated_at: new Date().toISOString() + }) + .eq('id', cartItemId) + .eq('user_id', userId) + .execute() + + if (response.error) { + console.error('更新购物车商品数量失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('更新购物车商品数量异常:', error) + return false + } + } + + // 更新购物车商品选中状态 + async updateCartItemSelection(cartItemId: string, selected: boolean): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法更新购物车') + return false + } + + const response = await supa + .from('ml_shopping_cart') + .update({ + selected: selected, + updated_at: new Date().toISOString() + }) + .eq('id', cartItemId) + .eq('user_id', userId) + .execute() + + if (response.error) { + console.error('更新购物车商品选中状态失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('更新购物车商品选中状态异常:', error) + return false + } + } + + // 批量更新购物车商品选中状态 + async batchUpdateCartItemSelection(cartItemIds: string[], selected: boolean): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法更新购物车') + return false + } + + const response = await supa + .from('ml_shopping_cart') + .update({ + selected: selected, + updated_at: new Date().toISOString() + }) + .eq('user_id', userId) + .in('id', cartItemIds) + .execute() + + if (response.error) { + console.error('批量更新购物车商品选中状态失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('批量更新购物车商品选中状态异常:', error) + return false + } + } + + // 删除购物车商品 + async deleteCartItem(cartItemId: string): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法删除购物车商品') + return false + } + + const response = await supa + .from('ml_shopping_cart') + .delete() + .eq('id', cartItemId) + .eq('user_id', userId) + .execute() + + if (response.error) { + console.error('删除购物车商品失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('删除购物车商品异常:', error) + return false + } + } + + // 批量删除购物车商品 + async batchDeleteCartItems(cartItemIds: string[]): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法删除购物车商品') + return false + } + + const response = await supa + .from('ml_shopping_cart') + .delete() + .eq('user_id', userId) + .in('id', cartItemIds) + .execute() + + if (response.error) { + console.error('批量删除购物车商品失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('批量删除购物车商品异常:', error) + return false + } + } + + // 清空购物车 + async clearCart(): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法清空购物车') + return false + } + + const response = await supa + .from('ml_shopping_cart') + .delete() + .eq('user_id', userId) + .execute() + + if (response.error) { + console.error('清空购物车失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('清空购物车异常:', error) + return false + } + } + + // 获取当前用户的所有地址 + async getAddresses(): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.warn('用户未登录,无法获取地址') + return [] + } + + const response = await supa + .from('ml_user_addresses') + .select('*') + .eq('user_id', userId) + .order('is_default', { ascending: false }) + .order('created_at', { ascending: false }) + .execute() + + if (response.error) { + console.error('获取地址失败:', response.error) + return [] + } + + return response.data as UserAddress[] + } catch (error) { + console.error('获取地址异常:', error) + return [] + } + } + + // 根据ID获取地址详情 + async getAddressById(addressId: string): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.warn('用户未登录,无法获取地址') + return null + } + + const response = await supa + .from('ml_user_addresses') + .select('*') + .eq('id', addressId) + .eq('user_id', userId) + .single() + .execute() + + if (response.error) { + console.error('获取地址详情失败:', response.error) + return null + } + + return response.data as UserAddress + } catch (error) { + console.error('获取地址详情异常:', error) + return null + } + } + + // 添加新地址 + async addAddress(address: { + recipient_name: string + phone: string + province: string + city: string + district: string + detail_address: string + postal_code?: string + is_default?: boolean + }): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法添加地址') + return false + } + + // 如果设置为默认地址,需要先取消其他默认地址 + if (address.is_default) { + await this.clearDefaultAddress(userId) + } + + const response = await supa + .from('ml_user_addresses') + .insert({ + user_id: userId, + recipient_name: address.recipient_name, + phone: address.phone, + province: address.province, + city: address.city, + district: address.district, + detail_address: address.detail_address, + postal_code: address.postal_code || null, + is_default: address.is_default || false, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + }) + .execute() + + if (response.error) { + console.error('添加地址失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('添加地址异常:', error) + return false + } + } + + // 更新地址 + async updateAddress(addressId: string, address: { + recipient_name?: string + phone?: string + province?: string + city?: string + district?: string + detail_address?: string + postal_code?: string + is_default?: boolean + }): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法更新地址') + return false + } + + // 如果设置为默认地址,需要先取消其他默认地址 + if (address.is_default) { + await this.clearDefaultAddress(userId) + } + + const response = await supa + .from('ml_user_addresses') + .update({ + ...address, + updated_at: new Date().toISOString() + }) + .eq('id', addressId) + .eq('user_id', userId) + .execute() + + if (response.error) { + console.error('更新地址失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('更新地址异常:', error) + return false + } + } + + // 删除地址 + async deleteAddress(addressId: string): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法删除地址') + return false + } + + const response = await supa + .from('ml_user_addresses') + .delete() + .eq('id', addressId) + .eq('user_id', userId) + .execute() + + if (response.error) { + console.error('删除地址失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('删除地址异常:', error) + return false + } + } + + // 设置默认地址 + async setDefaultAddress(addressId: string): Promise { + try { + const userId = this.getCurrentUserId() + if (!userId) { + console.error('用户未登录,无法设置默认地址') + return false + } + + // 先取消所有默认地址 + await this.clearDefaultAddress(userId) + + // 设置新的默认地址 + const response = await supa + .from('ml_user_addresses') + .update({ + is_default: true, + updated_at: new Date().toISOString() + }) + .eq('id', addressId) + .eq('user_id', userId) + .execute() + + if (response.error) { + console.error('设置默认地址失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('设置默认地址异常:', error) + return false + } + } + + // 清除用户的默认地址(内部方法) + private async clearDefaultAddress(userId: string): Promise { + try { + const response = await supa + .from('ml_user_addresses') + .update({ + is_default: false, + updated_at: new Date().toISOString() + }) + .eq('user_id', userId) + .eq('is_default', true) + .execute() + + if (response.error) { + console.error('清除默认地址失败:', response.error) + return false + } + + return true + } catch (error) { + console.error('清除默认地址异常:', error) + return false + } + } } // 导出单例实例