继续完善
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<view class="product-detail-page">
|
||||
<!-- 商品图片轮播 -->
|
||||
<view class="product-images">
|
||||
<swiper class="image-swiper" :indicator-dots="true" :autoplay="false">
|
||||
<swiper class="image-swiper" :indicator-dots="true" :autoplay="false" @change="onSwiperChange">
|
||||
<swiper-item v-for="(image, index) in product.images" :key="index">
|
||||
<image :src="image" class="product-image" mode="aspectFit" />
|
||||
</swiper-item>
|
||||
@@ -34,6 +34,23 @@
|
||||
<text class="enter-shop" @click.stop="goToShop">进店 ></text>
|
||||
</view>
|
||||
|
||||
<!-- 功能主治(药品功能) -->
|
||||
<view class="function-section" v-if="product.usage">
|
||||
<text class="function-title">功能主治</text>
|
||||
<text class="function-content">{{ product.usage }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 商品参数 -->
|
||||
<view class="params-section" @click="showParamsModal">
|
||||
<text class="params-title">商品参数</text>
|
||||
<view class="params-summary">
|
||||
<text class="params-item" v-if="product.specification">规格: {{ product.specification }}</text>
|
||||
<text class="params-item" v-if="product.expiry_date">有效期: {{ product.expiry_date }}</text>
|
||||
<text class="params-item" v-if="product.approval_number">批准文号: {{ product.approval_number }}</text>
|
||||
</view>
|
||||
<text class="params-arrow">></text>
|
||||
</view>
|
||||
|
||||
<!-- 规格选择 -->
|
||||
<view class="spec-section" @click="showSpecModal">
|
||||
<text class="spec-title">规格</text>
|
||||
@@ -65,6 +82,15 @@
|
||||
<view class="product-description">
|
||||
<view class="section-title">商品详情</view>
|
||||
<text class="description-text">{{ product.description || '暂无详细描述' }}</text>
|
||||
<!-- 商品详情图片 -->
|
||||
<view class="detail-images" v-if="product.images && product.images.length > 0">
|
||||
<image v-for="(img, index) in product.images"
|
||||
:key="index"
|
||||
:src="img"
|
||||
class="detail-image"
|
||||
mode="widthFix"
|
||||
@click="previewImage(index)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
@@ -104,6 +130,50 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品参数弹窗 -->
|
||||
<view v-if="showParams" class="params-modal" @click="hideParamsModal">
|
||||
<view class="params-content" @click.stop>
|
||||
<view class="params-header">
|
||||
<text class="params-title">商品参数</text>
|
||||
<text class="close-btn" @click="hideParamsModal">×</text>
|
||||
</view>
|
||||
<view class="params-list">
|
||||
<view class="params-item" v-if="product.specification">
|
||||
<text class="params-label">规格</text>
|
||||
<text class="params-value">{{ product.specification }}</text>
|
||||
</view>
|
||||
<view class="params-item" v-if="product.usage">
|
||||
<text class="params-label">功能主治</text>
|
||||
<text class="params-value">{{ product.usage }}</text>
|
||||
</view>
|
||||
<view class="params-item" v-if="product.side_effects">
|
||||
<text class="params-label">副作用</text>
|
||||
<text class="params-value">{{ product.side_effects }}</text>
|
||||
</view>
|
||||
<view class="params-item" v-if="product.precautions">
|
||||
<text class="params-label">注意事项</text>
|
||||
<text class="params-value">{{ product.precautions }}</text>
|
||||
</view>
|
||||
<view class="params-item" v-if="product.expiry_date">
|
||||
<text class="params-label">有效期</text>
|
||||
<text class="params-value">{{ product.expiry_date }}</text>
|
||||
</view>
|
||||
<view class="params-item" v-if="product.storage_conditions">
|
||||
<text class="params-label">储存条件</text>
|
||||
<text class="params-value">{{ product.storage_conditions }}</text>
|
||||
</view>
|
||||
<view class="params-item" v-if="product.approval_number">
|
||||
<text class="params-label">批准文号</text>
|
||||
<text class="params-value">{{ product.approval_number }}</text>
|
||||
</view>
|
||||
<view class="params-item" v-if="product.tags && product.tags.length > 0">
|
||||
<text class="params-label">标签</text>
|
||||
<text class="params-value">{{ product.tags.join(', ') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -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<string>
|
||||
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<string>
|
||||
|
||||
// 处理图片字段:优先使用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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user