consumerm模块完成度90%,完善消费者和商家端数据库表,商品、聊天、订单数据对接好了supabase,和商家端对接了聊天功能,安卓端编译通过了css样式,剩余几个页面在处理函数规范问题
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<!-- 消费者端 - 商品详情页 -->
|
||||
<template>
|
||||
<view class="product-detail-page">
|
||||
<scroll-view class="page-scroll" scroll-y="true">
|
||||
<!-- 商品图片轮播 -->
|
||||
<view class="product-images">
|
||||
<swiper class="image-swiper" :indicator-dots="true" :autoplay="false" @change="onSwiperChange">
|
||||
@@ -23,7 +24,7 @@
|
||||
|
||||
<!-- 店铺信息 -->
|
||||
<view class="shop-info" @click="goToShop">
|
||||
<image :src="merchant.shop_logo || '/static/default-shop.png'" class="shop-logo" />
|
||||
<image :src="merchant.shop_logo ?? '/static/default-shop.png'" class="shop-logo" />
|
||||
<view class="shop-details">
|
||||
<text class="shop-name" @click.stop="goToShop">{{ merchant.shop_name }}</text>
|
||||
<view class="shop-stats-row">
|
||||
@@ -67,7 +68,7 @@
|
||||
<!-- 规格选择 -->
|
||||
<view class="spec-section" @click="showSpecModal" v-if="productSkus.length > 0">
|
||||
<text class="spec-title">规格</text>
|
||||
<text class="spec-selected">{{ selectedSpec || '请选择规格' }}</text>
|
||||
<text class="spec-selected">{{ selectedSpec ?? '请选择规格' }}</text>
|
||||
<text class="spec-arrow">></text>
|
||||
</view>
|
||||
|
||||
@@ -94,7 +95,7 @@
|
||||
<!-- 商品详情 -->
|
||||
<view class="product-description">
|
||||
<view class="section-title">商品详情</view>
|
||||
<text class="description-text">{{ product.description || '暂无详细描述' }}</text>
|
||||
<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"
|
||||
@@ -105,6 +106,7 @@
|
||||
@click="previewImage(index)" />
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-actions">
|
||||
@@ -136,7 +138,7 @@
|
||||
<text class="spec-title">选择规格</text>
|
||||
<text class="close-btn" @click="hideSpecModal">×</text>
|
||||
</view>
|
||||
<view class="spec-list">
|
||||
<scroll-view class="spec-list" direction="vertical">
|
||||
<view v-for="sku in productSkus" :key="sku.id"
|
||||
class="spec-item"
|
||||
:class="{ active: selectedSkuId === sku.id }"
|
||||
@@ -145,7 +147,7 @@
|
||||
<text class="spec-price">¥{{ sku.price }}</text>
|
||||
<text class="spec-stock">库存{{ sku.stock }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -156,7 +158,7 @@
|
||||
<text class="params-title">商品参数</text>
|
||||
<text class="close-btn" @click="hideParamsModal">×</text>
|
||||
</view>
|
||||
<view class="params-list">
|
||||
<scroll-view class="params-list" direction="vertical">
|
||||
<view class="params-item" v-if="product.specification">
|
||||
<text class="params-label">规格</text>
|
||||
<text class="params-value">{{ product.specification }}</text>
|
||||
@@ -189,7 +191,7 @@
|
||||
<text class="params-label">标签</text>
|
||||
<text class="params-value">{{ product.tags.join(', ') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -272,7 +274,7 @@ export default {
|
||||
}
|
||||
},
|
||||
onLoad(options: any) {
|
||||
const productId = options.productId as string || options.id as string
|
||||
const productId = (options['productId'] ?? options['id']) as string
|
||||
const productPrice = options.price ? parseFloat(options.price) : null
|
||||
const productOriginalPrice = options.originalPrice ? parseFloat(options.originalPrice) : null
|
||||
|
||||
@@ -370,7 +372,7 @@ export default {
|
||||
uni.showLoading({ title: '加载中...' })
|
||||
try {
|
||||
const dbProductResponse = await supabaseService.getProductById(productId)
|
||||
let dbProduct: any = null
|
||||
let dbProduct: any | null = null
|
||||
if (Array.isArray(dbProductResponse) && dbProductResponse.length > 0) {
|
||||
dbProduct = dbProductResponse[0]
|
||||
} else if (dbProductResponse && !Array.isArray(dbProductResponse)) {
|
||||
@@ -381,25 +383,25 @@ export default {
|
||||
// Map DB product to local product
|
||||
this.product = {
|
||||
id: dbProduct.id,
|
||||
merchant_id: dbProduct.merchant_id || dbProduct.shop_id || '',
|
||||
category_id: dbProduct.category_id || '',
|
||||
merchant_id: dbProduct.merchant_id ?? dbProduct.shop_id ?? '',
|
||||
category_id: dbProduct.category_id ?? '',
|
||||
name: dbProduct.name,
|
||||
description: dbProduct.description || '',
|
||||
description: dbProduct.description ?? '',
|
||||
images: [] as string[],
|
||||
price: dbProduct.base_price || dbProduct.price || 0,
|
||||
original_price: dbProduct.market_price || dbProduct.original_price || 0,
|
||||
stock: dbProduct.available_stock || dbProduct.total_stock || dbProduct.stock || 0,
|
||||
sales: dbProduct.sale_count || dbProduct.sales || 0,
|
||||
price: dbProduct.base_price ?? dbProduct.price ?? 0,
|
||||
original_price: dbProduct.market_price ?? dbProduct.original_price ?? 0,
|
||||
stock: dbProduct.available_stock ?? dbProduct.total_stock ?? dbProduct.stock ?? 0,
|
||||
sales: dbProduct.sale_count ?? dbProduct.sales ?? 0,
|
||||
status: dbProduct.status !== undefined ? dbProduct.status : 1,
|
||||
created_at: dbProduct.created_at || new Date().toISOString(),
|
||||
created_at: dbProduct.created_at ?? new Date().toISOString(),
|
||||
// Attributes
|
||||
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,
|
||||
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
|
||||
|
||||
@@ -482,15 +484,15 @@ export default {
|
||||
id: shop.id,
|
||||
user_id: shop.merchant_id,
|
||||
shop_name: shop.shop_name,
|
||||
shop_logo: shop.shop_logo || '/static/default-shop.png',
|
||||
shop_banner: shop.shop_banner || '/static/default-banner.png',
|
||||
shop_description: shop.description || '',
|
||||
contact_name: shop.contact_name || '店主',
|
||||
contact_phone: shop.contact_phone || '',
|
||||
shop_logo: shop.shop_logo ?? '/static/default-shop.png',
|
||||
shop_banner: shop.shop_banner ?? '/static/default-banner.png',
|
||||
shop_description: shop.description ?? '',
|
||||
contact_name: shop.contact_name ?? '店主',
|
||||
contact_phone: shop.contact_phone ?? '',
|
||||
shop_status: 1,
|
||||
rating: shop.rating_avg || 5.0,
|
||||
total_sales: shop.total_sales || 0,
|
||||
created_at: shop.created_at || new Date().toISOString()
|
||||
rating: shop.rating_avg ?? 5.0,
|
||||
total_sales: shop.total_sales ?? 0,
|
||||
created_at: shop.created_at ?? new Date().toISOString()
|
||||
} as MerchantType
|
||||
realMerchantLoaded = true
|
||||
}
|
||||
@@ -547,7 +549,7 @@ export default {
|
||||
specifications: specs,
|
||||
price: sku.price,
|
||||
stock: sku.stock !== undefined ? sku.stock : 0,
|
||||
image_url: sku.image_url || '',
|
||||
image_url: sku.image_url != null ? sku.image_url : '',
|
||||
status: sku.status !== undefined ? sku.status : 1
|
||||
} as ProductSkuType
|
||||
})
|
||||
@@ -560,7 +562,7 @@ export default {
|
||||
|
||||
// 新增:加载优惠券
|
||||
async loadCoupons() {
|
||||
if (!this.product.merchant_id) return
|
||||
if (this.product.merchant_id == '') return
|
||||
// Safety check for cached service definition
|
||||
// @ts-ignore
|
||||
if (typeof supabaseService.fetchShopCoupons === 'function') {
|
||||
@@ -574,12 +576,12 @@ export default {
|
||||
|
||||
// 新增:联系客服(商家)
|
||||
contactMerchant() {
|
||||
if (!supabaseService.getCurrentUserId()) {
|
||||
if (supabaseService.getCurrentUserId() == '') {
|
||||
uni.navigateTo({ url: '/pages/auth/login' })
|
||||
return
|
||||
}
|
||||
// Navigate to chat
|
||||
const merchId = this.merchant.user_id || this.merchant.id || this.product.merchant_id;
|
||||
const merchId = this.merchant.user_id ?? this.merchant.id ?? this.product.merchant_id;
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/chat?merchantId=${merchId}&merchantName=${this.merchant.shop_name}`
|
||||
})
|
||||
@@ -596,7 +598,7 @@ export default {
|
||||
// 新增:领取优惠券
|
||||
async claimCoupon(coupon: any) {
|
||||
const userId = supabaseService.getCurrentUserId()
|
||||
if (!userId) {
|
||||
if (userId == '') {
|
||||
uni.navigateTo({ url: '/pages/auth/login' })
|
||||
return
|
||||
}
|
||||
@@ -621,7 +623,7 @@ export default {
|
||||
},
|
||||
|
||||
formatDate(dateStr: string): string {
|
||||
if (!dateStr) return ''
|
||||
if (dateStr == '') return ''
|
||||
const date = new Date(dateStr)
|
||||
return `${date.getFullYear()}.${date.getMonth()+1}.${date.getDate()}`
|
||||
},
|
||||
@@ -729,7 +731,7 @@ export default {
|
||||
},
|
||||
|
||||
async toggleFavorite() {
|
||||
if (!this.product.id) return
|
||||
if (this.product.id == '') return
|
||||
uni.showLoading({ title: '处理中' })
|
||||
|
||||
try {
|
||||
@@ -830,8 +832,15 @@ export default {
|
||||
<style>
|
||||
.product-detail-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 120rpx;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.product-images {
|
||||
@@ -881,7 +890,7 @@ export default {
|
||||
.original-price {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
text-decoration-line: line-through;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
@@ -991,15 +1000,18 @@ export default {
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
flex-direction: column;
|
||||
z-index: 1000;
|
||||
}
|
||||
.popup-content {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 1000rpx;
|
||||
}
|
||||
.popup-header {
|
||||
display: flex;
|
||||
@@ -1019,7 +1031,7 @@ export default {
|
||||
color: #999;
|
||||
}
|
||||
.coupon-list-scroll {
|
||||
max-height: 60vh;
|
||||
flex: 1;
|
||||
}
|
||||
.coupon-item {
|
||||
display: flex;
|
||||
@@ -1168,6 +1180,7 @@ export default {
|
||||
.product-description {
|
||||
background-color: #fff;
|
||||
padding: 30rpx;
|
||||
padding-bottom: 140rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
@@ -1259,16 +1272,19 @@ export default {
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end; /* UVUE 推荐用 flex 布局对齐 */
|
||||
flex-direction: column;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.spec-content {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 1000rpx;
|
||||
}
|
||||
|
||||
.spec-header {
|
||||
@@ -1287,8 +1303,7 @@ export default {
|
||||
}
|
||||
|
||||
.spec-list {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.spec-item {
|
||||
@@ -1333,7 +1348,6 @@ export default {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.function-content {
|
||||
@@ -1391,16 +1405,19 @@ export default {
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end; /* UVUE 推荐用 flex 布局对齐 */
|
||||
flex-direction: column;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.params-content {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 1000rpx;
|
||||
}
|
||||
|
||||
.params-header {
|
||||
@@ -1420,8 +1437,7 @@ export default {
|
||||
}
|
||||
|
||||
.params-list {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.params-item {
|
||||
@@ -1473,8 +1489,6 @@ export default {
|
||||
flex: 1;
|
||||
margin-right: 0;
|
||||
text-align: center;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
@@ -1482,4 +1496,4 @@ export default {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user