consumer模块完成90%,前端完成supabase对接
This commit is contained in:
@@ -20,24 +20,42 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<!-- 商品列表 (按店铺分组) -->
|
||||
<view class="products-section">
|
||||
<!-- 调试信息 -->
|
||||
<!-- 调试信息 -->
|
||||
<view v-if="checkoutItems.length > 0" class="debug-info">
|
||||
<text class="debug-text">调试:共{{ checkoutItems.length }}件商品,总价计算:{{ totalAmount }}</text>
|
||||
<text class="debug-text">共 {{ checkoutItems.length }} 件商品</text>
|
||||
</view>
|
||||
<view v-if="checkoutItems.length > 0">
|
||||
<view v-for="item in checkoutItems" :key="item.id" class="product-item">
|
||||
<image class="product-image" :src="item.product_image" />
|
||||
<view class="product-info">
|
||||
<text class="product-name">{{ item.product_name }}</text>
|
||||
<text v-if="item.sku_specifications" class="product-spec">{{ getSpecText(item.sku_specifications) }}</text>
|
||||
<view class="product-bottom">
|
||||
<text class="product-price">¥{{ item.price }}</text>
|
||||
<text class="product-quantity">×{{ item.quantity }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="shopGroups.length > 0">
|
||||
<view v-for="group in shopGroups" :key="group.shopId" class="shop-group">
|
||||
<view class="shop-header">
|
||||
<text class="shop-icon">🏪</text>
|
||||
<text class="shop-name">{{ group.shopName }}</text>
|
||||
</view>
|
||||
|
||||
<view v-for="item in group.items" :key="item.id" class="product-item">
|
||||
<image class="product-image" :src="item.product_image" mode="aspectFill" />
|
||||
<view class="product-info">
|
||||
<text class="product-name">{{ item.product_name }}</text>
|
||||
<text v-if="item.sku_specifications" class="product-spec">{{ getSpecText(item.sku_specifications) }}</text>
|
||||
<view class="product-bottom">
|
||||
<text class="product-price">¥{{ item.price }}</text>
|
||||
<text class="product-quantity">×{{ item.quantity }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 店铺小计 -->
|
||||
<view class="shop-subtotal">
|
||||
<text class="subtotal-label">配送方式</text>
|
||||
<text class="subtotal-value">快递 免邮</text>
|
||||
</view>
|
||||
<view class="shop-subtotal">
|
||||
<text class="subtotal-text">小计: </text>
|
||||
<text class="subtotal-price">¥{{ getGroupTotal(group) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="no-products">
|
||||
<text class="no-products-text">暂无商品信息</text>
|
||||
@@ -286,6 +304,8 @@ type CheckoutItemType = {
|
||||
sku_specifications: any
|
||||
price: number
|
||||
quantity: number
|
||||
shop_id?: string
|
||||
shop_name?: string
|
||||
}
|
||||
|
||||
type DeliveryOptionType = {
|
||||
@@ -329,6 +349,32 @@ const showSaveConfirm = ref<boolean>(false)
|
||||
const smartAddressInput = ref<string>('')
|
||||
|
||||
// 计算属性 - 修复价格同步问题
|
||||
// 按店铺分组商品
|
||||
const shopGroups = computed(() => {
|
||||
const groups = new Map<string, any>()
|
||||
checkoutItems.value.forEach(item => {
|
||||
// 使用类型断言访问可能的额外属性
|
||||
const rawItem = item as any
|
||||
const shopId = rawItem.shop_id || 'unknown'
|
||||
if (!groups.has(shopId)) {
|
||||
groups.set(shopId, {
|
||||
shopId: shopId,
|
||||
shopName: rawItem.shop_name || '商城优选',
|
||||
merchant_id: rawItem.merchant_id || rawItem.shop_id,
|
||||
items: [] as any[]
|
||||
})
|
||||
}
|
||||
groups.get(shopId).items.push(item)
|
||||
})
|
||||
return Array.from(groups.values())
|
||||
})
|
||||
|
||||
const getGroupTotal = (group: any) => {
|
||||
return group.items.reduce((sum: number, item: any) => {
|
||||
return sum + (Number(item.price) * Number(item.quantity))
|
||||
}, 0).toFixed(2)
|
||||
}
|
||||
|
||||
const totalAmount = computed(() => {
|
||||
console.log('计算商品总价,checkoutItems:', checkoutItems.value)
|
||||
if (!checkoutItems.value || checkoutItems.value.length === 0) {
|
||||
@@ -405,34 +451,38 @@ watch(checkoutItems, (newItems) => {
|
||||
|
||||
// 页面加载时监听eventChannel
|
||||
onLoad(() => {
|
||||
// 优先检查Storage中是否有"立即购买"的数据
|
||||
let dataLoaded = false
|
||||
// 优先检查Storage中是否有结算数据 (支持 buy_now 和 cart 两种模式)
|
||||
const checkoutType = uni.getStorageSync('checkout_type')
|
||||
if (checkoutType === 'buy_now') {
|
||||
console.log('检测到立即购买模式,从Storage加载数据')
|
||||
if (checkoutType === 'buy_now' || checkoutType === 'cart') {
|
||||
console.log(`检测到结算模式(${checkoutType}),从Storage加载数据`)
|
||||
const itemsStr = uni.getStorageSync('checkout_items')
|
||||
if (itemsStr) {
|
||||
try {
|
||||
const items = JSON.parse(itemsStr as string)
|
||||
console.log('从Storage加载的商品数据:', items)
|
||||
processCheckoutItems(items)
|
||||
|
||||
// 清除Storage,避免污染下次进入(刷新页面时可能需要保留?暂时不清除,或者在离开页面时清除)
|
||||
// uni.removeStorageSync('checkout_type')
|
||||
// uni.removeStorageSync('checkout_items')
|
||||
loadDefaultAddress()
|
||||
return // 成功加载,直接返回
|
||||
if (items && Array.isArray(items) && items.length > 0) {
|
||||
processCheckoutItems(items)
|
||||
dataLoaded = true
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析立即购买数据失败', e)
|
||||
console.error('解析结算数据失败', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有从checkout_items加载到数据,则尝试从通用购物车Storage加载 (回退方案)
|
||||
if (!dataLoaded) {
|
||||
console.log('未找到预结算数据,尝试从购物车本地存储加载')
|
||||
loadFromLocalStorage()
|
||||
} else {
|
||||
// 如果已经加载了数据,还需要单独加载地址,因为loadFromLocalStorage通常会附带加载地址
|
||||
loadDefaultAddress()
|
||||
}
|
||||
|
||||
// 从上一页获取数据
|
||||
const eventChannel = uni.getEventChannel ? uni.getEventChannel() : null
|
||||
|
||||
// 默认先尝试从本地存储加载(确保有数据)
|
||||
loadFromLocalStorage()
|
||||
|
||||
if (eventChannel) {
|
||||
eventChannel.on('acceptData', (data: any) => {
|
||||
console.log('接收到商品数据:', data)
|
||||
@@ -539,9 +589,6 @@ const loadFromLocalStorage = () => {
|
||||
if (selectedCartItems.length > 0) {
|
||||
// 转换为CheckoutItemType格式
|
||||
const convertedItems: CheckoutItemType[] = selectedCartItems.map(item => {
|
||||
// 确保价格和数量是数字
|
||||
let price = typeof item.price === 'string' ? parseFloat(item.price) : Number(item.price)
|
||||
if (isNaN(price)) price = 0
|
||||
|
||||
let quantity = typeof item.quantity === 'string' ? parseInt(item.quantity) : Number(item.quantity)
|
||||
if (isNaN(quantity) || quantity < 1) quantity = 1
|
||||
@@ -553,7 +600,7 @@ const loadFromLocalStorage = () => {
|
||||
product_name: item.name || '',
|
||||
product_image: item.image || '',
|
||||
sku_specifications: item.spec ? { spec: item.spec } : {},
|
||||
price: price,
|
||||
price: Number(item.price) || 0,
|
||||
quantity: quantity
|
||||
}
|
||||
})
|
||||
@@ -1247,8 +1294,7 @@ const submitOrder = async () => {
|
||||
uni.showLoading({ title: '提交中...' })
|
||||
|
||||
try {
|
||||
const userId = getCurrentUserId()
|
||||
// 确保使用当前登录用户ID (如果本地存储为空,可能需要处理)
|
||||
const userId = supabaseService.getCurrentUserId()
|
||||
if (!userId) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
@@ -1258,59 +1304,77 @@ const submitOrder = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
// 准备订单项数据
|
||||
// 注意:需根据 checkoutItems 的实际结构转换为 createOrder 需要的 CartItem 结构
|
||||
// 假设 checkoutItems 已经包含了 product_id, quantity, price, name, image 等字段
|
||||
const orderItems = checkoutItems.value.map((item: any): any => ({
|
||||
id: item.id || '', // 这是一个临时ID或者购物车ID,createOrder 中会使用 product_id
|
||||
product_id: item.product_id || item.id, // 确保有 product_id
|
||||
quantity: item.quantity,
|
||||
price: item.price,
|
||||
product_name: item.name,
|
||||
product_image: item.image,
|
||||
spec: item.spec,
|
||||
checked: true
|
||||
}))
|
||||
// 准备按店铺分组数据
|
||||
const groups = shopGroups.value.map((group: any): any => {
|
||||
return {
|
||||
merchant_id: group.merchant_id || group.shopId,
|
||||
shopName: group.shopName,
|
||||
items: group.items.map((item: any): any => ({
|
||||
id: item.id, // 用于清理购物车
|
||||
product_id: item.product_id,
|
||||
sku_id: item.sku_id,
|
||||
quantity: item.quantity,
|
||||
price: item.price,
|
||||
product_name: item.product_name,
|
||||
product_image: item.product_image,
|
||||
specifications: item.sku_specifications // 保持原始对象,createOrder 会处理序列化
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
// 调用 Supabase 服务创建订单
|
||||
const result = await supabaseService.createOrder(
|
||||
userId,
|
||||
selectedAddress.value!.id, // 地址ID
|
||||
actualAmount.value, // 实付金额
|
||||
orderItems
|
||||
)
|
||||
// 调用 Supabase 服务创建多店铺订单
|
||||
const result = await supabaseService.createOrdersByShop({
|
||||
shipping_address: selectedAddress.value,
|
||||
shopGroups: groups,
|
||||
deliveryFee: deliveryFee.value,
|
||||
discountAmount: discountAmount.value
|
||||
})
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (result.success) {
|
||||
// 清除购买的商品 (如果来自购物车,应该在 createOrder 成功后清除,或者这里手动清除本地存储)
|
||||
// 这里我们假设购物车清理逻辑可能在 createOrder 后端处理,或者需要在这里清除本地
|
||||
// 清除结算商品
|
||||
try {
|
||||
uni.removeStorageSync('checkout_items')
|
||||
uni.removeStorageSync('checkout_type')
|
||||
} catch(e) {
|
||||
console.error('清除结算商品失败', e)
|
||||
}
|
||||
|
||||
const activeOrderId = result.data as string
|
||||
const orderIds = result.orderIds
|
||||
|
||||
// 跳转支付页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=${activeOrderId}&amount=${actualAmount.value}&productAmount=${totalAmount.value}&deliveryFee=${deliveryFee.value}&discountAmount=${discountAmount.value}`
|
||||
})
|
||||
if (orderIds.length === 1) {
|
||||
// 单个订单跳转支付
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=${orderIds[0]}&amount=${actualAmount.value}`
|
||||
})
|
||||
} else {
|
||||
// 多个订单跳转到订单列表
|
||||
uni.showToast({
|
||||
title: `成功创建${orderIds.length}个订单`,
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: '/pages/mall/consumer/orders'
|
||||
})
|
||||
}, 1500)
|
||||
}
|
||||
} else {
|
||||
throw new Error(result.error)
|
||||
throw new Error(result.error || '创建订单失败')
|
||||
}
|
||||
|
||||
} catch (err: any) {
|
||||
uni.hideLoading()
|
||||
console.error('创建订单失败:', err)
|
||||
console.error('提交订单错误:', err)
|
||||
uni.showToast({
|
||||
title: err.message || '订单创建失败',
|
||||
title: err.message || '提交订单失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 生成订单号
|
||||
const generateOrderNo = (): string => {
|
||||
const date = new Date()
|
||||
@@ -1463,6 +1527,62 @@ const goBack = () => {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shop-group {
|
||||
background-color: #fff;
|
||||
margin: 10px 0;
|
||||
border-radius: 12px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.shop-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.shop-icon {
|
||||
font-size: 18px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.shop-subtotal {
|
||||
display: flex;
|
||||
justify-content: flex-end; /* 右对齐 */
|
||||
align-items: center;
|
||||
padding-top: 10px;
|
||||
margin-top: 5px;
|
||||
border-top: 1px dashed #f0f0f0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.subtotal-label {
|
||||
color: #666;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.subtotal-value {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.subtotal-text {
|
||||
color: #333;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.subtotal-price {
|
||||
color: #ff4757;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
display: flex;
|
||||
padding: 15px 0;
|
||||
|
||||
Reference in New Issue
Block a user