consumer模块完成度95%,安卓端大部分页面能正常获取数据,页面样式显示基本正常,逐渐完善;消费者端的积分、余额、评价、优惠券等小模块正在完善
This commit is contained in:
@@ -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'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user