consumerm模块完成度90%,完善消费者和商家端数据库表,商品、聊天、订单数据对接好了supabase,和商家端对接了聊天功能,安卓端编译通过了css样式,剩余几个页面在处理函数规范问题
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<view class="shop-detail-page">
|
||||
<scroll-view class="page-scroll" scroll-y="true" @scrolltolower="onScrollToLower" refresher-enabled="true" @refresherrefresh="onRefresherRefresh" :refresher-triggered="isRefresherTriggered">
|
||||
<!-- 店铺头部信息 -->
|
||||
<view class="shop-header">
|
||||
<image :src="merchant.shop_banner || '/static/default-banner.png'" class="shop-banner" mode="aspectFill" />
|
||||
<image :src="merchant.shop_banner != '' ? merchant.shop_banner : '/static/default-banner.png'" class="shop-banner" mode="aspectFill" />
|
||||
<view class="shop-info-card">
|
||||
<image :src="merchant.shop_logo || '/static/default-shop.png'" class="shop-logo" />
|
||||
<image :src="merchant.shop_logo != '' ? merchant.shop_logo : '/static/default-shop.png'" class="shop-logo" />
|
||||
<view class="shop-basic-info">
|
||||
<text class="shop-name">{{ merchant.shop_name }}</text>
|
||||
<view class="shop-stats">
|
||||
@@ -21,7 +22,7 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="shop-desc">{{ merchant.shop_description || '这家店很懒,什么都没写~' }}</text>
|
||||
<text class="shop-desc">{{ merchant.shop_description != '' ? merchant.shop_description : '这家店很懒,什么都没写~' }}</text>
|
||||
|
||||
<!-- 优惠券列表 (新增) -->
|
||||
<view class="shop-coupons" v-if="coupons.length > 0">
|
||||
@@ -63,12 +64,12 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
|
||||
import { MerchantType, ProductType } from '@/types/mall-types.uts'
|
||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||
|
||||
@@ -97,12 +98,15 @@ const merchant = ref<MerchantType>({
|
||||
const products = ref<ProductType[]>([])
|
||||
const isFollowed = ref(false)
|
||||
const coupons = ref<any[]>([]) // 新增优惠券
|
||||
const isRefresherTriggered = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
const pages = getCurrentPages()
|
||||
const options = pages[pages.length - 1].options as any
|
||||
// Search传递的是 id (shop_id), 其他地方可能传递 merchantId
|
||||
const paramId = (options['merchantId'] || options['id']) as string
|
||||
const mId = options['merchantId']
|
||||
const pId = options['id']
|
||||
const paramId = (mId != null ? mId : pId) as string
|
||||
|
||||
if (paramId) {
|
||||
console.log('Page mounted with params:', paramId)
|
||||
@@ -129,31 +133,41 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
// 下拉刷新
|
||||
const onRefresherRefresh = () => {
|
||||
isRefresherTriggered.value = true
|
||||
currentPage.value = 1
|
||||
hasMore.value = true
|
||||
isLoading.value = false
|
||||
|
||||
if (currentMerchantId.value != '') {
|
||||
const id = currentMerchantId.value
|
||||
// 重新加载所有数据
|
||||
loadShopData(id)
|
||||
loadCoupons(id)
|
||||
loadShopProducts(id)
|
||||
Promise.all([
|
||||
loadShopData(id),
|
||||
loadCoupons(id),
|
||||
loadShopProducts(id)
|
||||
]).then(() => {
|
||||
isRefresherTriggered.value = false
|
||||
})
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
isRefresherTriggered.value = false
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
const onScrollToLower = () => {
|
||||
if (hasMore.value && !isLoading.value && currentMerchantId.value != '') {
|
||||
console.log('Scroll to lower, loading more...')
|
||||
loadShopProducts(currentMerchantId.value)
|
||||
}
|
||||
}
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
onRefresherRefresh()
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
// 触底加载更多
|
||||
if (hasMore.value && !isLoading.value && currentMerchantId.value != '') {
|
||||
console.log('Reach bottom, loading more...')
|
||||
loadShopProducts(currentMerchantId.value)
|
||||
}
|
||||
onScrollToLower()
|
||||
})
|
||||
|
||||
const loadShopData = async (id: string) => {
|
||||
@@ -166,15 +180,15 @@ const loadShopData = async (id: string) => {
|
||||
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 != null ? shop.shop_logo : '/static/default-shop.png',
|
||||
shop_banner: shop.shop_banner != null ? shop.shop_banner : '/static/default-banner.png',
|
||||
shop_description: shop.description != null ? shop.description : '',
|
||||
contact_name: shop.contact_name != null ? shop.contact_name : '',
|
||||
contact_phone: shop.contact_phone != null ? shop.contact_phone : '',
|
||||
shop_status: 1, // 默认正常
|
||||
rating: shop.rating_avg || 5.0,
|
||||
total_sales: shop.total_sales || 0,
|
||||
created_at: shop.created_at || ''
|
||||
rating: shop.rating_avg != null ? shop.rating_avg : 5.0,
|
||||
total_sales: shop.total_sales != null ? shop.total_sales : 0,
|
||||
created_at: shop.created_at != null ? shop.created_at : ''
|
||||
}
|
||||
|
||||
// 检查关注状态
|
||||
@@ -204,7 +218,7 @@ const loadCoupons = async (id: string) => {
|
||||
|
||||
const claimCoupon = async (coupon: any) => {
|
||||
const userId = supabaseService.getCurrentUserId()
|
||||
if (!userId) {
|
||||
if (userId == null) {
|
||||
uni.navigateTo({ url: '/pages/auth/login' })
|
||||
return
|
||||
}
|
||||
@@ -297,24 +311,49 @@ const loadShopProducts = async (id: string) => {
|
||||
|
||||
// 安全获取属性的方式,处理字段名称不一样的问题
|
||||
const safeItem = item as any
|
||||
const safePrice = (safeItem['base_price'] || safeItem['price'] || 0) as number
|
||||
const safeMarketPrice = (safeItem['market_price'] || safeItem['original_price'] || safePrice) as number
|
||||
const safeStock = (safeItem['total_stock'] || safeItem['available_stock'] || safeItem['stock'] || 0) as number
|
||||
const safeSales = (safeItem['sale_count'] || safeItem['sales'] || 0) as number
|
||||
let safePrice = safeItem['base_price'] as number
|
||||
if (safePrice == null) {
|
||||
const p = safeItem['price'] as number
|
||||
safePrice = p != null ? p : 0
|
||||
}
|
||||
|
||||
let safeMarketPrice = safeItem['market_price'] as number
|
||||
if (safeMarketPrice == null) {
|
||||
const mp = safeItem['original_price'] as number
|
||||
safeMarketPrice = mp != null ? mp : safePrice
|
||||
}
|
||||
|
||||
let safeStock = safeItem['total_stock'] as number
|
||||
if (safeStock == null) {
|
||||
let as_ = safeItem['available_stock'] as number
|
||||
if (as_ == null) {
|
||||
const s = safeItem['stock'] as number
|
||||
safeStock = s != null ? s : 0
|
||||
} else {
|
||||
safeStock = as_
|
||||
}
|
||||
}
|
||||
|
||||
let safeSales = safeItem['sale_count'] as number
|
||||
if (safeSales == null) {
|
||||
const s = safeItem['sales'] as number
|
||||
safeSales = s != null ? s : 0
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
merchant_id: item.merchant_id,
|
||||
category_id: item.category_id,
|
||||
name: item.name,
|
||||
description: item.description || '',
|
||||
description: item.description != null ? item.description : '',
|
||||
images: images,
|
||||
price: safePrice,
|
||||
original_price: safeMarketPrice,
|
||||
stock: safeStock,
|
||||
sales: safeSales,
|
||||
status: 1,
|
||||
created_at: item.created_at || ''
|
||||
created_at: item.created_at != null ? item.created_at : '',
|
||||
updated_at: item.updated_at != null ? item.updated_at : ''
|
||||
}
|
||||
})
|
||||
|
||||
@@ -355,14 +394,14 @@ const checkFollowStatus = async (shopId: string) => {
|
||||
|
||||
const toggleFollow = async () => {
|
||||
const userId = supabaseService.getCurrentUserId()
|
||||
if (!userId) {
|
||||
if (userId == null) {
|
||||
uni.navigateTo({ url: '/pages/auth/login' })
|
||||
return
|
||||
}
|
||||
|
||||
// 这里的 merchant.value.id 假如是 ML_SHOPS.id
|
||||
const shopId = merchant.value.id
|
||||
if (!shopId) return
|
||||
if (shopId == null || shopId == '') return
|
||||
|
||||
uni.showLoading({ title: '处理中' })
|
||||
|
||||
@@ -393,7 +432,7 @@ const toggleFollow = async () => {
|
||||
|
||||
const contactService = () => {
|
||||
const currentUser = supabaseService.getCurrentUserId()
|
||||
if (!currentUser) {
|
||||
if (currentUser == null) {
|
||||
uni.navigateTo({ url: '/pages/user/login' })
|
||||
return
|
||||
}
|
||||
@@ -410,7 +449,7 @@ const contactService = () => {
|
||||
const addToCart = async (product: ProductType) => {
|
||||
uni.showLoading({ title: '添加中...' })
|
||||
|
||||
const success = await supabaseService.addToCart(product.id, 1)
|
||||
const success = await supabaseService.addToCart(product.id, 1, '')
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
@@ -437,7 +476,15 @@ const goToProduct = (id: string) => {
|
||||
<style>
|
||||
.shop-detail-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shop-header {
|
||||
@@ -544,8 +591,7 @@ const goToProduct = (id: string) => {
|
||||
}
|
||||
|
||||
.shop-desc {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
|
||||
color: #666;
|
||||
padding: 10px 15px 0;
|
||||
line-height: 1.4;
|
||||
@@ -603,13 +649,15 @@ const goToProduct = (id: string) => {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #ff4444;
|
||||
writing-mode: vertical-rl; /* Note: writing-mode may not work in all environments, used flex direction in product detail instead, but let's try or use flex col */
|
||||
}
|
||||
.coupon-btn-label {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
writing-mode: vertical-rl;
|
||||
}
|
||||
flex-direction: column;
|
||||
}
|
||||
.coupon-btn-label {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
width: 14px; /* Force vertical flow by width constraint if needed, or just let it stack naturally if char by char */
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.product-section {
|
||||
padding: 15px;
|
||||
@@ -628,12 +676,12 @@ const goToProduct = (id: string) => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
width: calc(50% - 5px);
|
||||
width: 48%; /* Fallback for calc(50% - 5px) */
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
@@ -659,9 +707,8 @@ const goToProduct = (id: string) => {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
lines: 2;
|
||||
overflow: hidden;
|
||||
height: 40px;
|
||||
line-height: 20px;
|
||||
@@ -677,17 +724,14 @@ const goToProduct = (id: string) => {
|
||||
.price-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.cart-btn {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: #ff4444;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.cart-icon {
|
||||
@@ -713,13 +757,13 @@ const goToProduct = (id: string) => {
|
||||
*/
|
||||
@media (min-width: 768px) {
|
||||
.product-item {
|
||||
width: calc(33.33% - 7px) !important; /* Tablet: 3 items */
|
||||
width: 32% !important; /* Tablet: 3 items */
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.product-item {
|
||||
width: calc(16.66% - 9px) !important; /* PC: 6 items */
|
||||
width: 16% !important; /* PC: 6 items */
|
||||
}
|
||||
|
||||
.shop-info-card, .shop-header, .product-section {
|
||||
@@ -729,4 +773,4 @@ const goToProduct = (id: string) => {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user