继续完善购物逻辑闭环,consumer模块完成度80%
This commit is contained in:
@@ -22,17 +22,26 @@
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<view class="products-section">
|
||||
<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 v-if="checkoutItems.length > 0" class="debug-info">
|
||||
<text class="debug-text">调试:共{{ checkoutItems.length }}件商品,总价计算:{{ totalAmount }}</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>
|
||||
<view v-else class="no-products">
|
||||
<text class="no-products-text">暂无商品信息</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 配送方式 -->
|
||||
@@ -210,11 +219,28 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 确认保存弹窗 -->
|
||||
<view v-if="showSaveConfirm" class="confirm-popup-mask">
|
||||
<view class="confirm-popup">
|
||||
<view class="confirm-header">
|
||||
<text class="confirm-title">保存地址</text>
|
||||
</view>
|
||||
<view class="confirm-content">
|
||||
<text class="confirm-message">是否保存该地址用于下次使用?</text>
|
||||
</view>
|
||||
<view class="confirm-buttons">
|
||||
<button class="confirm-btn cancel" @click="handleSaveConfirm(false)">仅本次</button>
|
||||
<button class="confirm-btn confirm" @click="handleSaveConfirm(true)">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ref, onMounted, computed, watch, onUnmounted, getCurrentInstance } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
type CheckoutItemType = {
|
||||
id: string
|
||||
@@ -267,10 +293,44 @@ const showNewAddressForm = ref<boolean>(false)
|
||||
const showSaveConfirm = ref<boolean>(false)
|
||||
const smartAddressInput = ref<string>('')
|
||||
|
||||
// 计算属性
|
||||
// 计算属性 - 修复价格同步问题
|
||||
const totalAmount = computed(() => {
|
||||
return checkoutItems.value.reduce((sum, item) =>
|
||||
sum + (item.price * item.quantity), 0)
|
||||
console.log('计算商品总价,checkoutItems:', checkoutItems.value)
|
||||
if (!checkoutItems.value || checkoutItems.value.length === 0) {
|
||||
console.log('商品列表为空,返回0')
|
||||
return 0
|
||||
}
|
||||
|
||||
// 确保每个商品的价格和数量都是数字类型,并计算总和
|
||||
const total = checkoutItems.value.reduce((sum, item) => {
|
||||
// 确保item存在且包含必要的属性
|
||||
if (!item) return sum
|
||||
|
||||
// 将价格转换为数字(确保是数字类型)
|
||||
let price = 0
|
||||
if (item.price !== undefined && item.price !== null) {
|
||||
price = typeof item.price === 'string' ? parseFloat(item.price) : Number(item.price)
|
||||
}
|
||||
|
||||
// 将数量转换为数字
|
||||
let quantity = 0
|
||||
if (item.quantity !== undefined && item.quantity !== null) {
|
||||
quantity = typeof item.quantity === 'string' ? parseInt(item.quantity) : Number(item.quantity)
|
||||
}
|
||||
|
||||
// 验证转换后的数字是否有效
|
||||
if (isNaN(price) || isNaN(quantity) || price <= 0 || quantity <= 0) {
|
||||
console.warn('商品价格或数量无效:', item, 'price:', price, 'quantity:', quantity)
|
||||
return sum
|
||||
}
|
||||
|
||||
const itemTotal = price * quantity
|
||||
console.log(`商品 ${item.product_name},单价: ${price},数量: ${quantity},小计: ${itemTotal}`)
|
||||
return sum + itemTotal
|
||||
}, 0)
|
||||
|
||||
console.log('商品总价计算结果:', total)
|
||||
return total
|
||||
})
|
||||
|
||||
const deliveryFee = computed(() => {
|
||||
@@ -282,6 +342,7 @@ const discountAmount = computed(() => {
|
||||
if (!selectedCoupon.value || !selectedCoupon.value.template) return 0
|
||||
|
||||
const coupon = selectedCoupon.value.template
|
||||
// 确保使用计算后的商品总价进行比较
|
||||
if (totalAmount.value < coupon.min_order_amount) return 0
|
||||
|
||||
// 简单处理:假设都是满减券
|
||||
@@ -289,14 +350,105 @@ const discountAmount = computed(() => {
|
||||
})
|
||||
|
||||
const actualAmount = computed(() => {
|
||||
let amount = totalAmount.value + deliveryFee.value - discountAmount.value
|
||||
// 确保所有值都是数字类型
|
||||
const total = typeof totalAmount.value === 'number' ? totalAmount.value : 0
|
||||
const delivery = typeof deliveryFee.value === 'number' ? deliveryFee.value : 0
|
||||
const discount = typeof discountAmount.value === 'number' ? discountAmount.value : 0
|
||||
|
||||
// 正确计算:商品总价 + 运费 - 优惠减免
|
||||
let amount = total + delivery - discount
|
||||
|
||||
// 金额必须大于等于0
|
||||
return amount > 0 ? amount : 0
|
||||
})
|
||||
|
||||
// 监听checkoutItems变化 - 调试用
|
||||
watch(checkoutItems, (newItems) => {
|
||||
console.log('checkoutItems变化了:', newItems)
|
||||
console.log('商品总价计算:', totalAmount.value)
|
||||
}, { deep: true })
|
||||
|
||||
// 页面加载时监听eventChannel
|
||||
onLoad(() => {
|
||||
// 优先检查Storage中是否有"立即购买"的数据
|
||||
const checkoutType = uni.getStorageSync('checkout_type')
|
||||
if (checkoutType === 'buy_now') {
|
||||
console.log('检测到立即购买模式,从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 // 成功加载,直接返回
|
||||
} catch (e) {
|
||||
console.error('解析立即购买数据失败', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从上一页获取数据
|
||||
const eventChannel = uni.getEventChannel ? uni.getEventChannel() : null
|
||||
if (eventChannel) {
|
||||
eventChannel.on('acceptData', (data: any) => {
|
||||
console.log('接收到商品数据:', data)
|
||||
let items = data.selectedItems || []
|
||||
processCheckoutItems(items)
|
||||
loadDefaultAddress()
|
||||
})
|
||||
} else {
|
||||
// 如果没有eventChannel,尝试从本地存储加载(例如从购物车进入)
|
||||
loadFromLocalStorage()
|
||||
}
|
||||
})
|
||||
|
||||
// 处理商品数据清洗
|
||||
const processCheckoutItems = (items: any[]) => {
|
||||
// 数据清洗:确保价格和数量是数字类型
|
||||
if (items && items.length > 0) {
|
||||
items = items.map((item: any) => {
|
||||
// 确保价格是数字
|
||||
let price = item.price
|
||||
if (price !== undefined && price !== null) {
|
||||
price = typeof price === 'string' ? parseFloat(price) : Number(item.price)
|
||||
if (isNaN(price)) price = 0
|
||||
} else {
|
||||
price = 0
|
||||
}
|
||||
|
||||
// 确保数量是数字
|
||||
let quantity = item.quantity
|
||||
if (quantity !== undefined && quantity !== null) {
|
||||
quantity = typeof quantity === 'string' ? parseInt(quantity) : Number(item.quantity)
|
||||
if (isNaN(quantity) || quantity < 1) quantity = 1
|
||||
} else {
|
||||
quantity = 1
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
price: Number(price.toFixed(2)), // 保留两位小数
|
||||
quantity: quantity
|
||||
}
|
||||
})
|
||||
}
|
||||
checkoutItems.value = items
|
||||
// 调试:打印每个商品的价格
|
||||
if (checkoutItems.value && checkoutItems.value.length > 0) {
|
||||
console.log('清洗后商品价格明细:')
|
||||
checkoutItems.value.forEach((item: any, index: number) => {
|
||||
console.log(`商品${index}:`, item.product_name, '价格:', item.price, '类型:', typeof item.price, '数量:', item.quantity)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
loadCheckoutData()
|
||||
|
||||
// 监听地址更新事件
|
||||
uni.$on('addressUpdated', (updatedAddressList: any) => {
|
||||
addressList.value = updatedAddressList
|
||||
@@ -316,16 +468,40 @@ onUnmounted(() => {
|
||||
uni.$off('addressUpdated')
|
||||
})
|
||||
|
||||
// 加载结算数据
|
||||
const loadCheckoutData = () => {
|
||||
// 从上一页获取数据
|
||||
const eventChannel = uni.getEventChannel()
|
||||
if (eventChannel) {
|
||||
eventChannel.on('acceptData', (data: any) => {
|
||||
checkoutItems.value = data.selectedItems || []
|
||||
loadDefaultAddress()
|
||||
})
|
||||
// 从本地存储加载结算数据(例如从购物车进入)
|
||||
const loadFromLocalStorage = () => {
|
||||
// 尝试从本地存储获取购物车选中的商品
|
||||
const cartData = uni.getStorageSync('cart')
|
||||
if (cartData) {
|
||||
try {
|
||||
const cartItems = JSON.parse(cartData as string) as any[]
|
||||
// 筛选选中的商品
|
||||
const selectedCartItems = cartItems.filter(item => item.selected === true)
|
||||
if (selectedCartItems.length > 0) {
|
||||
// 转换为CheckoutItemType格式
|
||||
const convertedItems: CheckoutItemType[] = selectedCartItems.map(item => ({
|
||||
id: item.id,
|
||||
product_id: item.productId,
|
||||
sku_id: item.id, // 购物车中item.id就是SKU ID
|
||||
product_name: item.name,
|
||||
product_image: item.image,
|
||||
sku_specifications: item.spec ? { spec: item.spec } : {},
|
||||
price: item.price,
|
||||
quantity: item.quantity
|
||||
}))
|
||||
checkoutItems.value = convertedItems
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析购物车数据失败:', e)
|
||||
}
|
||||
}
|
||||
loadDefaultAddress()
|
||||
}
|
||||
|
||||
// 加载结算数据(兼容旧版本,现在主要在onLoad中处理)
|
||||
const loadCheckoutData = () => {
|
||||
// 为了兼容性保留,但主要逻辑已在onLoad中实现
|
||||
loadFromLocalStorage()
|
||||
}
|
||||
|
||||
// 加载默认地址
|
||||
@@ -459,7 +635,7 @@ const saveNewAddress = async () => {
|
||||
}
|
||||
|
||||
const userId = getCurrentUserId()
|
||||
if (!userId) return
|
||||
// if (!userId) return // 允许未登录用户保存地址用于演示
|
||||
|
||||
// 触发保存确认弹窗
|
||||
showSaveConfirm.value = true
|
||||
@@ -722,106 +898,52 @@ const submitOrder = async () => {
|
||||
})
|
||||
return
|
||||
}
|
||||
// 模拟提交成功跳转
|
||||
// 实际项目中应等待API返回
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=ORDER_MOCK_${Date.now()}&amount=${actualAmount.value}`
|
||||
})
|
||||
return;
|
||||
|
||||
const userId = getCurrentUserId()
|
||||
if (!userId) {
|
||||
uni.showToast({
|
||||
title: '用户信息错误',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 生成订单号
|
||||
const orderNo = generateOrderNo()
|
||||
|
||||
const orderData = {
|
||||
order_no: orderNo,
|
||||
user_id: userId,
|
||||
merchant_id: 'default', // 这里需要根据商品确定商家
|
||||
status: 1, // 待支付
|
||||
total_amount: totalAmount.value,
|
||||
discount_amount: discountAmount.value,
|
||||
delivery_fee: deliveryFee.value,
|
||||
actual_amount: actualAmount.value,
|
||||
payment_method: 0, // 待选择
|
||||
payment_status: 0,
|
||||
delivery_address: selectedAddress.value,
|
||||
remark: remark.value,
|
||||
created_at: new Date().toISOString()
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建订单
|
||||
/* const { data: order, error: orderError } = await supa
|
||||
.from('orders')
|
||||
.insert(orderData)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (orderError !== null) {
|
||||
throw orderError
|
||||
}
|
||||
|
||||
// 创建订单商品项
|
||||
const orderItems = checkoutItems.value.map(item => ({
|
||||
order_id: order.id,
|
||||
product_id: item.product_id,
|
||||
sku_id: item.sku_id,
|
||||
product_name: item.product_name,
|
||||
sku_specifications: item.sku_specifications,
|
||||
price: item.price,
|
||||
quantity: item.quantity,
|
||||
total_amount: item.price * item.quantity
|
||||
}))
|
||||
|
||||
const { error: itemsError } = await supa
|
||||
.from('order_items')
|
||||
.insert(orderItems)
|
||||
|
||||
if (itemsError !== null) {
|
||||
throw itemsError
|
||||
}
|
||||
|
||||
// 使用优惠券
|
||||
if (selectedCoupon.value) {
|
||||
const { error: couponError } = await supa
|
||||
.from('user_coupons')
|
||||
.update({
|
||||
status: 2, // 已使用
|
||||
used_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', selectedCoupon.value.id)
|
||||
|
||||
if (couponError !== null) {
|
||||
console.error('更新优惠券状态失败:', couponError)
|
||||
}
|
||||
}
|
||||
|
||||
// 清空购物车
|
||||
await clearShoppingCart()
|
||||
|
||||
// 跳转到支付页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=${order.id}&amount=${actualAmount.value}`
|
||||
}) */
|
||||
// MOCK ORDER SUBMISSION
|
||||
// 模拟创建成功
|
||||
try {
|
||||
const mockOrderId = `order_${Date.now()}`
|
||||
|
||||
// MOCK ORDER SUBMISSION
|
||||
// 模拟创建成功
|
||||
const mockOrderId = `order_mock_${Date.now()}`
|
||||
// 创建订单对象
|
||||
const newOrder = {
|
||||
id: mockOrderId,
|
||||
order_no: generateOrderNo(),
|
||||
user_id: getCurrentUserId() || 'user_001',
|
||||
merchant_id: checkoutItems.value[0]?.product_id || 'merchant_001', // 简化处理,取第一个商品的merchant
|
||||
status: 1, // 待支付
|
||||
total_amount: totalAmount.value,
|
||||
discount_amount: discountAmount.value,
|
||||
delivery_fee: deliveryFee.value,
|
||||
actual_amount: actualAmount.value,
|
||||
payment_method: 0,
|
||||
payment_status: 0,
|
||||
delivery_address: selectedAddress.value,
|
||||
items: checkoutItems.value,
|
||||
created_at: new Date().toISOString()
|
||||
}
|
||||
|
||||
// 保存到本地存储
|
||||
const storedOrders = uni.getStorageSync('orders')
|
||||
let orders: any[] = []
|
||||
if (storedOrders) {
|
||||
try {
|
||||
orders = JSON.parse(storedOrders as string) as any[]
|
||||
} catch (e) {
|
||||
console.error('解析订单数据失败', e)
|
||||
}
|
||||
}
|
||||
orders.unshift(newOrder)
|
||||
uni.setStorageSync('orders', JSON.stringify(orders))
|
||||
|
||||
uni.showLoading({ title: '提交中...' })
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
uni.hideLoading()
|
||||
|
||||
// 携带价格详情跳转
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=${mockOrderId}&amount=${actualAmount.value}`
|
||||
url: `/pages/mall/consumer/payment?orderId=${mockOrderId}&amount=${actualAmount.value}&productAmount=${totalAmount.value}&deliveryFee=${deliveryFee.value}&discountAmount=${discountAmount.value}`
|
||||
})
|
||||
|
||||
} catch (err) {
|
||||
} catch (err) {
|
||||
console.error('创建订单失败:', err)
|
||||
uni.showToast({
|
||||
title: '订单创建失败',
|
||||
@@ -967,6 +1089,20 @@ const goBack = () => {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.debug-info {
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.debug-text {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
display: flex;
|
||||
padding: 15px 0;
|
||||
@@ -1505,4 +1641,76 @@ const goBack = () => {
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
}
|
||||
/* 确认弹窗样式 */
|
||||
.confirm-popup-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10002;
|
||||
}
|
||||
|
||||
.confirm-popup {
|
||||
background-color: #ffffff;
|
||||
width: 80%;
|
||||
max-width: 320px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.confirm-header {
|
||||
padding: 20px 0 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.confirm-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.confirm-content {
|
||||
padding: 0 20px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.confirm-message {
|
||||
font-size: 16px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.confirm-buttons {
|
||||
display: flex;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
flex: 1;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
background-color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.confirm-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.confirm-btn.cancel {
|
||||
color: #666666;
|
||||
border-right: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.confirm-btn.confirm {
|
||||
color: #007aff;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user