consumerm模块完成度90%,完善消费者和商家端数据库表,商品、聊天、订单数据对接好了supabase,和商家端对接了聊天功能,安卓端编译通过了css样式,剩余几个页面在处理函数规范问题

This commit is contained in:
cyh666666
2026-02-24 17:17:49 +08:00
parent e2f1dfb097
commit e606c597ca
174 changed files with 37917 additions and 4444 deletions

View File

@@ -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>