consumer模块完成度95%,安卓端大部分页面能正常获取数据,页面样式显示基本正常,逐渐完善;消费者端的积分、余额、评价、优惠券等小模块正在完善

This commit is contained in:
cyh666666
2026-03-02 17:21:19 +08:00
parent df84fd8642
commit 7e74b88e1e
34 changed files with 17088 additions and 1751 deletions

View File

@@ -379,90 +379,53 @@ export default {
async loadProductDetail(productId: string, options: any = {}) {
uni.showLoading({ title: '加载中...' })
try {
const dbProductResponse = await supabaseService.getProductById(productId)
let dbProduct: UTSJSONObject | null = null
if (Array.isArray(dbProductResponse)) {
const arr = dbProductResponse as any[]
if (arr.length > 0) {
dbProduct = arr[0] as UTSJSONObject
}
} else if (dbProductResponse != null) {
dbProduct = dbProductResponse as UTSJSONObject
}
const dbProduct = await supabaseService.getProductById(productId)
if (dbProduct != null) {
// Map DB product to local product
const dbObj = dbProduct as UTSJSONObject
// 使用 getProductById 返回的 Product 对象
this.product = {
id: dbProduct['id'] as string,
merchant_id: (dbProduct['merchant_id'] ?? dbProduct['shop_id'] ?? '') as string,
category_id: (dbProduct['category_id'] ?? '') as string,
name: dbProduct['name'] as string,
description: (dbProduct['description'] ?? '') as string,
images: [] as string[],
price: (dbProduct['base_price'] ?? dbProduct['price'] ?? 0) as number,
original_price: (dbProduct['market_price'] ?? dbProduct['original_price'] ?? 0) as number,
stock: (dbProduct['available_stock'] ?? dbProduct['total_stock'] ?? dbProduct['stock'] ?? 0) as number,
sales: (dbProduct['sale_count'] ?? dbProduct['sales'] ?? 0) as number,
status: dbProduct['status'] != null ? dbProduct['status'] as number : 1,
created_at: (dbProduct['created_at'] ?? new Date().toISOString()) as string,
// Attributes
specification: dbProduct['specification'] as string | null,
usage: dbProduct['usage'] as string | null,
side_effects: dbProduct['side_effects'] as string | null,
precautions: dbProduct['precautions'] as string | null,
expiry_date: dbProduct['expiry_date'] as string | null,
storage_conditions: dbProduct['storage_conditions'] as string | null,
approval_number: dbProduct['approval_number'] as string | null,
id: dbProduct.id,
merchant_id: dbProduct.merchant_id ?? '',
category_id: dbProduct.category_id ?? '',
name: dbProduct.name,
description: dbProduct.description ?? '',
images: dbProduct.images ?? [] as string[],
price: dbProduct.price ?? dbProduct.base_price ?? 0,
original_price: dbProduct.original_price ?? dbProduct.market_price ?? 0,
stock: dbProduct.stock ?? dbProduct.total_stock ?? 0,
sales: dbProduct.sale_count ?? 0,
status: dbProduct.status ?? 1,
created_at: dbProduct.created_at ?? new Date().toISOString(),
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: [] as string[]
} as ProductType
// Handle Images
if (dbProduct['image_urls'] != null) {
// 解析 tags
if (dbProduct.tags != null && dbProduct.tags != '') {
try {
const imageUrls = dbProduct['image_urls']
const parsed = typeof imageUrls === 'string' ? JSON.parse(imageUrls) : imageUrls
if (Array.isArray(parsed)) {
this.product.images = (parsed as any[]).map((i: any): string => i as string)
}
} catch (e) { console.error('Error parsing image_urls', e) }
}
// Fallback to main_image_url if no images found
if (this.product.images.length === 0 && dbProduct['main_image_url'] != null) {
this.product.images.push(dbProduct['main_image_url'] as string)
}
// Fallback to 'image' field (legacy)
if (this.product.images.length === 0 && dbProduct['image'] != null) {
this.product.images.push(dbProduct['image'] as string)
}
// Final fallback
if (this.product.images.length === 0) {
this.product.images.push('/static/default-product.png')
}
// Handle Tags
if (dbProduct['tags'] != null) {
try {
const tagsData = dbProduct['tags']
const parsedTags = typeof tagsData === 'string' ? JSON.parse(tagsData) : tagsData
const parsedTags = JSON.parse(dbProduct.tags)
if (Array.isArray(parsedTags)) {
this.product.tags = (parsedTags as any[]).map((t: any): string => t as string)
}
} catch (e) {}
} catch(e) {}
}
// Handle Images - 使用 main_image_url 作为后备
if (this.product.images.length == 0 && dbProduct.main_image_url != null && dbProduct.main_image_url != '') {
this.product.images.push(dbProduct.main_image_url)
}
// Final fallback
if (this.product.images.length == 0) {
this.product.images.push('/static/default-product.png')
}
// Handle JSON attributes if present
const attributes = dbProduct['attributes']
if (attributes != null && typeof attributes === 'string') {
try {
const attrs = JSON.parse(attributes) as UTSJSONObject | null
if (attrs != null) {
// Merge attributes into product if they match keys
if (attrs['specification'] != null) this.product.specification = attrs['specification'] as string
if (attrs['usage'] != null) this.product.usage = attrs['usage'] as string
}
} catch(e) {}
}
console.log('商品详情加载成功:', this.product.name, '库存:', this.product.stock, '销量:', this.product.sales)
} else {
throw new Error('No product found')
}
@@ -471,12 +434,21 @@ export default {
// Fallback to options if available
this.product.id = productId
const opts = options as UTSJSONObject
const nameOpt = opts['name'] as string | null
this.product.name = (nameOpt != null && nameOpt !== '') ? decodeURIComponent(nameOpt) ?? '未知商品' : '未知商品'
const priceOpt = opts['price'] as string | null
this.product.price = (priceOpt != null && priceOpt !== '') ? parseFloat(priceOpt) : 0
const imageOpt = opts['image'] as string | null
const decodedImage = (imageOpt != null && imageOpt !== '') ? decodeURIComponent(imageOpt) : null
const nameOpt = opts['name']
this.product.name = (nameOpt != null && nameOpt != '') ? decodeURIComponent(nameOpt as string) ?? '未知商品' : '未知商品'
// price 可能是 string 或 number 类型
const priceOpt = opts['price']
if (typeof priceOpt == 'number') {
this.product.price = priceOpt as number
} else if (typeof priceOpt == 'string') {
this.product.price = parseFloat(priceOpt as string)
} else {
this.product.price = 0
}
const imageOpt = opts['image']
const decodedImage = (imageOpt != null && imageOpt != '') ? decodeURIComponent(imageOpt as string) : null
this.product.images = decodedImage != null ? [decodedImage] : ['/static/default-product.png']
}
@@ -499,22 +471,23 @@ export default {
try {
const shopResponse = await supabaseService.getShopByMerchantId(merchantId)
if (shopResponse != null) {
const shop = shopResponse as UTSJSONObject
// 直接使用 Shop 对象的属性
this.merchant = {
id: shop['id'] as string,
user_id: shop['merchant_id'] as string,
shop_name: shop['shop_name'] as string,
shop_logo: (shop['shop_logo'] ?? '/static/default-shop.png') as string,
shop_banner: (shop['shop_banner'] ?? '/static/default-banner.png') as string,
shop_description: (shop['description'] ?? '') as string,
contact_name: (shop['contact_name'] ?? '店主') as string,
contact_phone: (shop['contact_phone'] ?? '') as string,
id: shopResponse.id,
user_id: shopResponse.merchant_id,
shop_name: shopResponse.shop_name,
shop_logo: shopResponse.shop_logo ?? '/static/default-shop.png',
shop_banner: shopResponse.shop_banner ?? '/static/default-banner.png',
shop_description: shopResponse.description ?? '',
contact_name: shopResponse.contact_name ?? '店主',
contact_phone: shopResponse.contact_phone ?? '',
shop_status: 1,
rating: (shop['rating_avg'] ?? 5.0) as number,
total_sales: (shop['total_sales'] ?? 0) as number,
created_at: (shop['created_at'] ?? new Date().toISOString()) as string
rating: shopResponse.rating_avg ?? 5.0,
total_sales: shopResponse.total_sales ?? 0,
created_at: shopResponse.created_at ?? new Date().toISOString()
} as MerchantType
realMerchantLoaded = true
console.log('店铺信息加载成功:', this.merchant.shop_name)
}
} catch (e) {
console.error('Load shop failed', e)
@@ -555,33 +528,30 @@ export default {
const skus = await supabaseService.getProductSkus(productId)
if (skus.length > 0) {
console.log('加载到商品SKU:', skus.length)
this.productSkus = skus.map((skuData): ProductSkuType => {
const sku = skuData as UTSJSONObject
this.productSkus = []
for (let i = 0; i < skus.length; i++) {
const skuData = skus[i]
// 解析 specifications JSON 字符串
let specs: UTSJSONObject = {}
const specsData = sku['specifications']
if (specsData != null) {
if (skuData.specifications != null && skuData.specifications != '') {
try {
if (typeof specsData === 'string') {
specs = JSON.parse(specsData) as UTSJSONObject
} else {
// 假设已经是对象
specs = specsData as UTSJSONObject
}
specs = JSON.parse(skuData.specifications) as UTSJSONObject
} catch(e) {
console.error('解析SKU规格失败', e)
console.error('解析SKU规格失败', e)
}
}
return {
id: sku['id'] as string,
product_id: sku['product_id'] as string,
sku_code: sku['sku_code'] as string,
specifications: specs,
price: sku['price'] as number,
stock: sku['stock'] != null ? sku['stock'] as number : 0,
image_url: sku['image_url'] != null ? sku['image_url'] as string : '',
status: sku['status'] != null ? sku['status'] as number : 1
} as ProductSkuType
})
}
const sku: ProductSkuType = {
id: skuData.id,
product_id: skuData.product_id,
sku_code: skuData.sku_code,
specifications: specs,
price: skuData.price,
stock: skuData.stock ?? 0,
image_url: skuData.image_url ?? '',
status: skuData.status ?? 1
}
this.productSkus.push(sku)
}
return
}
} catch (e) {
@@ -594,17 +564,54 @@ export default {
if (this.product.merchant_id == '') return
// Safety check for cached service definition
try {
// @ts-ignore
const couponData = await supabaseService.fetchShopCoupons(this.product.merchant_id)
this.coupons = couponData as Array<CouponTemplateType>
} catch (e) {
try {
// @ts-ignore
const couponData2 = await supabaseService.getAvailableCoupons(this.product.merchant_id)
this.coupons = couponData2 as Array<CouponTemplateType>
} catch (e2) {
console.warn('SupabaseService coupon methods not available:', e2)
// 解析优惠券数据
this.coupons = []
if (couponData != null && couponData.length > 0) {
for (let i = 0; i < couponData.length; i++) {
const item = couponData[i]
const couponObj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
const getSafeString = (key: string): string => {
const val = couponObj.get(key)
if (val == null) return ''
if (typeof val == 'string') return val
return ''
}
const getSafeNumber = (key: string): number => {
const val = couponObj.get(key)
if (val == null) return 0
if (typeof val == 'number') return val
return 0
}
const coupon: CouponTemplateType = {
id: getSafeString('id'),
name: getSafeString('name'),
description: getSafeString('description'),
coupon_type: getSafeNumber('coupon_type'),
discount_type: getSafeNumber('discount_type'),
discount_value: getSafeNumber('discount_value'),
min_order_amount: getSafeNumber('min_order_amount'),
max_discount_amount: getSafeNumber('max_discount_amount'),
total_quantity: getSafeNumber('total_quantity'),
per_user_limit: getSafeNumber('per_user_limit'),
usage_limit: getSafeNumber('usage_limit'),
merchant_id: getSafeString('merchant_id'),
category_ids: [] as string[],
product_ids: [] as string[],
user_type_limit: getSafeNumber('user_type_limit'),
start_time: getSafeString('start_time'),
end_time: getSafeString('end_time'),
status: getSafeNumber('status'),
created_at: getSafeString('created_at')
}
this.coupons.push(coupon)
}
}
} catch (e) {
console.warn('SupabaseService coupon methods not available:', e)
}
},
@@ -801,9 +808,16 @@ export default {
},
goToShop() {
if (this.merchant.user_id != null && this.merchant.user_id !== '') {
const merchantId = this.merchant.id ?? this.product.merchant_id ?? ''
if (merchantId != '') {
console.log('进店点击merchantId:', merchantId)
uni.navigateTo({
url: `/pages/mall/consumer/shop-detail?merchantId=${this.merchant.id}`
url: `/pages/mall/consumer/shop-detail?merchantId=${merchantId}`
})
} else {
uni.showToast({
title: '店铺信息加载中',
icon: 'none'
})
}
},