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

@@ -66,7 +66,7 @@
<view v-for="(_, index) in 6"
:key="index"
class="password-dot">
<text v-if="password.length > index">●</text>
<text v-if="password.length > index" class="password-dot-text">●</text>
</view>
</view>
<text class="forgot-password" @click="forgotPassword">忘记密码?</text>
@@ -136,109 +136,9 @@ const productAmount = ref<number>(0) // 商品总价
const deliveryFee = ref<number>(0) // 运费
const discountAmount = ref<number>(0) // 优惠减免
// 生命周期
onMounted(() => {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage.options as any
if (options.orderId) {
orderId.value = options.orderId
loadOrderInfo()
}
if (options.amount) {
amount.value = parseFloat(options.amount)
}
// 获取传递的价格详情
if (options.productAmount) {
productAmount.value = parseFloat(options.productAmount)
}
if (options.deliveryFee) {
deliveryFee.value = parseFloat(options.deliveryFee)
}
if (options.discountAmount) {
discountAmount.value = parseFloat(options.discountAmount)
}
// 如果没有传详情,尝试根据总价估算(兼容旧逻辑,但优先使用传参)
if (!options.productAmount && amount.value > 0) {
calculatePriceDetails(amount.value)
}
loadPaymentMethods()
loadUserBalance()
})
// 监听返回操作(包含系统返回键和导航栏返回按钮)
onBackPress((options) => {
// 如果是通过代码主动调用 navigateBack 返回,则允许
if (options.from === 'navigateBack') {
return false
}
// 否则拦截返回,显示确认弹窗
goBack()
return true
})
// 更新本地存储中的订单状态
const updateOrderInStorage = (status: number) => {
try {
// 尝试从 'orders' 读取 (checkout页面写入的key)
const ordersStr = uni.getStorageSync('orders')
let orders: any[] = []
if (ordersStr) {
orders = JSON.parse(ordersStr as string) as any[]
}
const index = orders.findIndex((o: any) => o.id === orderId.value)
if (index !== -1) {
orders[index].status = status
orders[index].payment_status = status === 2 ? 1 : 0 // 2=待发货(已支付), 1=待支付(未支付)
orders[index].updated_at = new Date().toISOString()
// 确保更新的是 'orders' key
uni.setStorageSync('orders', JSON.stringify(orders))
console.log('订单状态已更新到Storage (orders):', orderId.value, status)
} else {
console.warn('在Storage (orders)中未找到订单:', orderId.value)
}
} catch (e) {
console.error('更新订单状态失败', e)
}
}
// 加载订单信息
const loadOrderInfo = async () => {
try {
if (!orderId.value) return
const order = await supabaseService.getOrderDetail(orderId.value)
if (order) {
orderNo.value = order.order_no
// Only update amount if not passed via options (options is priority for UI flow usually, but DB is source of truth)
// But checking consistency is good
const dbAmount = Number(order.total_amount)
if (dbAmount > 0) {
amount.value = dbAmount
}
if (order.items && order.items.length > 0) {
// Could update product name etc if displayed
}
} else {
// Fallback or error
console.warn('Order not found in DB', orderId.value)
if (!orderNo.value) orderNo.value = 'ORD_PENDING_' + Date.now()
}
} catch (err) {
console.error('加载订单信息异常:', err)
}
}
// 加载支付方式
// 加载支付方式(必须在 onMounted 之前定义)
const loadPaymentMethods = () => {
paymentMethods.value = [
const methods: PaymentMethodType[] = [
{
id: 'wechat',
name: '微信支付',
@@ -268,9 +168,10 @@ const loadPaymentMethods = () => {
enabled: true
}
]
paymentMethods.value = methods
}
// 加载用户余额
// 加载用户余额(必须在 onMounted 之前定义)
const loadUserBalance = async () => {
try {
const balance = await supabaseService.getUserBalance()
@@ -281,21 +182,215 @@ const loadUserBalance = async () => {
}
}
// 计算价格明细(必须在 onMounted 之前定义)
const calculatePriceDetails = (totalAmount: number) => {
// 模拟计算各项费用
// 假设商品总价占总金额的80%运费占10%优惠减免占10%
productAmount.value = totalAmount * 0.8
deliveryFee.value = totalAmount * 0.1
discountAmount.value = totalAmount * 0.1
// 确保总和等于应付金额
const calculatedTotal = productAmount.value + deliveryFee.value - discountAmount.value
if (Math.abs(calculatedTotal - totalAmount) > 0.01) {
// 调整商品总价以匹配应付金额
productAmount.value = totalAmount + discountAmount.value - deliveryFee.value
}
}
// 更新本地存储中的订单状态(必须在 onMounted 之前定义)
const updateOrderInStorage = (targetOrderId: string, status: number) => {
try {
// 尝试从 'orders' 读取 (checkout页面写入的key)
const ordersStr = uni.getStorageSync('orders')
let orders: Record<string, any>[] = []
if (ordersStr != null) {
const parsed = JSON.parse(ordersStr as string)
if (Array.isArray(parsed)) {
for (let i = 0; i < parsed.length; i++) {
orders.push(parsed[i] as Record<string, any>)
}
}
}
let foundIndex = -1
for (let i = 0; i < orders.length; i++) {
const o = orders[i]
if (o['id'] === targetOrderId) {
foundIndex = i
break
}
}
if (foundIndex !== -1) {
orders[foundIndex]['status'] = status
orders[foundIndex]['payment_status'] = status === 2 ? 1 : 0 // 2=待发货(已支付), 1=待支付(未支付)
orders[foundIndex]['updated_at'] = new Date().toISOString()
// 确保更新的是 'orders' key
uni.setStorageSync('orders', JSON.stringify(orders))
console.log('订单状态已更新到Storage (orders):', targetOrderId, status)
} else {
console.warn('在Storage (orders)中未找到订单:', targetOrderId)
}
} catch (e) {
console.error('更新订单状态失败', e)
}
}
// 取消支付,更新订单状态(必须在 goBack 之前定义)
const cancelPayment = async () => {
try {
// 这里应该调用API更新订单状态为待支付status: 1
// 模拟更新订单状态
// 更新本地存储
updateOrderInStorage(orderId.value, 1) // 1: 待支付
// 发布订单更新事件让profile页面可以刷新数据
uni.$emit('orderUpdated', { orderId: orderId.value, status: 1 })
uni.showToast({
title: '已保存到待支付订单',
icon: 'success'
})
// 延迟返回,让用户看到提示
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (err) {
console.error('取消支付异常:', err)
uni.showToast({
title: '操作失败',
icon: 'none'
})
}
}
// 返回(必须在 onBackPress 之前定义)
const goBack = () => {
uni.showModal({
title: '取消支付',
content: '确定要取消支付吗?取消后订单将保存到待支付订单中',
confirmText: '取消支付',
cancelText: '继续支付',
success: (res) => {
if (res.confirm) {
// 用户确认取消支付,更新订单状态为待支付
cancelPayment()
}
}
})
}
// 加载订单信息(必须在 onMounted 之前定义)
const loadOrderInfo = async () => {
try {
if (orderId.value == '') return
const order = await supabaseService.getOrderDetail(orderId.value)
if (order != null) {
const orderObj = order as Record<string, any>
orderNo.value = orderObj['order_no'] as string
// Only update amount if not passed via options (options is priority for UI flow usually, but DB is source of truth)
// But checking consistency is good
const totalAmount = orderObj['total_amount']
const dbAmount = totalAmount != null ? parseFloat(totalAmount.toString()) : 0
if (dbAmount > 0) {
amount.value = dbAmount
}
const items = orderObj['items']
if (items != null && Array.isArray(items) && items.length > 0) {
// Could update product name etc if displayed
}
} else {
// Fallback or error
console.warn('Order not found in DB', orderId.value)
if (orderNo.value == '') orderNo.value = 'ORD_PENDING_' + Date.now()
}
} catch (err) {
console.error('加载订单信息异常:', err)
}
}
// 生命周期
onMounted(() => {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage.options as Record<string, any>
const orderIdValue = options['orderId']
if (orderIdValue != null) {
orderId.value = orderIdValue as string
loadOrderInfo()
}
const amountValue = options['amount']
if (amountValue != null) {
amount.value = parseFloat(amountValue.toString())
}
// 获取传递的价格详情
const productAmountValue = options['productAmount']
if (productAmountValue != null) {
productAmount.value = parseFloat(productAmountValue.toString())
}
const deliveryFeeValue = options['deliveryFee']
if (deliveryFeeValue != null) {
deliveryFee.value = parseFloat(deliveryFeeValue.toString())
}
const discountAmountValue = options['discountAmount']
if (discountAmountValue != null) {
discountAmount.value = parseFloat(discountAmountValue.toString())
}
// 如果没有传详情,尝试根据总价估算(兼容旧逻辑,但优先使用传参)
if (productAmountValue == null && amount.value > 0) {
calculatePriceDetails(amount.value)
}
loadPaymentMethods()
loadUserBalance()
})
// 监听返回操作(包含系统返回键和导航栏返回按钮)
onBackPress((options) => {
// 如果是通过代码主动调用 navigateBack 返回,则允许
if (options.from === 'navigateBack') {
return false
}
// 否则拦截返回,显示确认弹窗
goBack()
return true
})
// 获取当前用户ID
const getCurrentUserId = (): string => {
const userStore = uni.getStorageSync('userInfo')
return userStore?.id || ''
if (userStore != null) {
const userObj = userStore as Record<string, any>
const id = userObj['id']
if (id != null) {
return id as string
}
}
return ''
}
// 获取支付方式图标
const getMethodIcon = (methodId: string): string => {
const icons: Record<string, string> = {
wechat: '💳',
alipay: '💳',
balance: '💰',
bankcard: '💳'
if (methodId === 'wechat') {
return '💳'
} else if (methodId === 'alipay') {
return '💳'
} else if (methodId === 'balance') {
return '💰'
} else if (methodId === 'bankcard') {
return '💳'
}
return icons[methodId] || '💳'
return '💳'
}
// 选择支付方式
@@ -319,14 +414,16 @@ const getPayButtonText = (): string => {
return '余额不足'
}
const texts: Record<string, string> = {
wechat: '微信支付',
alipay: '支付宝支付',
balance: '余额支付',
bankcard: '银行卡支付'
if (selectedMethod.value === 'wechat') {
return '微信支付'
} else if (selectedMethod.value === 'alipay') {
return '支付宝支付'
} else if (selectedMethod.value === 'balance') {
return '余额支付'
} else if (selectedMethod.value === 'bankcard') {
return '银行卡支付'
}
return texts[selectedMethod.value] || '确认支付'
return '确认支付'
}
// 减少商品库存
@@ -363,29 +460,37 @@ const confirmPayment = async () => {
}
isPaying.value = true
uni.showLoading({ title: '支付中...' })
try {
// Call Supabase Service to handle payment
console.log('[confirmPayment] 开始支付, orderId:', orderId.value, 'method:', selectedMethod.value)
const success = await supabaseService.payOrder(orderId.value, selectedMethod.value, amount.value)
console.log('[confirmPayment] 支付结果:', success)
if (!success) {
throw new Error('Payment processing failed')
console.error('[confirmPayment] payOrder 返回 false')
uni.hideLoading()
uni.showToast({
title: '支付处理失败',
icon: 'none'
})
isPaying.value = false
return
}
// 更新订单状态
updateOrderInStorage(2) // 2: 待发货(已支付)
uni.hideLoading()
updateOrderInStorage(orderId.value, 2)
// 支付成功
uni.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
})
// 发布订单更新事件让profile页面可以刷新数据
uni.$emit('orderUpdated', { orderId: orderId.value, status: 2 }) // 2: 待发货
uni.$emit('orderUpdated', { orderId: orderId.value, status: 2 })
// 跳转到支付成功页面
setTimeout(() => {
uni.redirectTo({
url: `/pages/mall/consumer/payment-success?orderId=${orderId.value}`
@@ -393,56 +498,42 @@ const confirmPayment = async () => {
}, 1500)
} catch (err) {
console.error('支付失败:', err)
console.error('[confirmPayment] 支付异常:', err)
uni.hideLoading()
uni.showToast({
title: '支付失败',
icon: 'none'
})
} finally {
isPaying.value = false
}
}
// 获取支付方式代码
const getPaymentMethodCode = (methodId: string): number => {
const codes: Record<string, number> = {
wechat: 1,
alipay: 2,
balance: 3,
bankcard: 4
if (methodId === 'wechat') {
return 1
} else if (methodId === 'alipay') {
return 2
} else if (methodId === 'balance') {
return 3
} else if (methodId === 'bankcard') {
return 4
}
return codes[methodId] || 0
return 0
}
// 输入密码
const inputPassword = (num: string) => {
if (password.value.length >= 6) return
password.value += num
}
// 删除密码
const deletePassword = () => {
if (password.value.length > 0) {
password.value = password.value.slice(0, -1)
}
}
// 监听密码输入
watch(password, (newPassword) => {
if (newPassword.length === 6) {
// 自动验证密码
verifyPassword()
}
})
// 验证密码
// 验证密码(必须在 watch 之前定义)
const verifyPassword = async () => {
// 这里应该验证支付密码,这里简单模拟
const userId = getCurrentUserId()
try {
// 模拟验证
await new Promise(resolve => setTimeout(resolve, 500))
await new Promise<void>((resolve: (value: void) => void) => {
setTimeout(() => {
resolve()
}, 500)
})
// 假设密码正确
const isCorrect = true
@@ -463,6 +554,27 @@ const verifyPassword = async () => {
}
}
// 输入密码
const inputPassword = (num: string) => {
if (password.value.length >= 6) return
password.value += num
}
// 删除密码
const deletePassword = () => {
if (password.value.length > 0) {
password.value = password.value.slice(0, -1)
}
}
// 监听密码输入
watch(password, (newPassword: string) => {
if (newPassword.length === 6) {
// 自动验证密码
verifyPassword()
}
})
// 忘记密码
const forgotPassword = () => {
uni.navigateTo({
@@ -470,99 +582,17 @@ const forgotPassword = () => {
})
}
// 计算价格明细
const calculatePriceDetails = (totalAmount: number) => {
// 模拟计算各项费用
// 假设商品总价占总金额的80%运费占10%优惠减免占10%
productAmount.value = totalAmount * 0.8
deliveryFee.value = totalAmount * 0.1
discountAmount.value = totalAmount * 0.1
// 确保总和等于应付金额
const calculatedTotal = productAmount.value + deliveryFee.value - discountAmount.value
if (Math.abs(calculatedTotal - totalAmount) > 0.01) {
// 调整商品总价以匹配应付金额
productAmount.value = totalAmount + discountAmount.value - deliveryFee.value
}
}
// 在组件卸载时移除返回键监听
onUnmounted(() => {
// uni.offBackPress() 在uni-app中不需要手动移除
})
// 返回
const goBack = () => {
uni.showModal({
title: '取消支付',
content: '确定要取消支付吗?取消后订单将保存到待支付订单中',
confirmText: '取消支付',
cancelText: '继续支付',
success: async (res) => {
if (res.confirm) {
// 用户确认取消支付,更新订单状态为待支付
await cancelPayment()
} else {
// 用户选择继续支付,留在当前页面
return
}
}
})
}
// 取消支付,更新订单状态
const cancelPayment = async () => {
try {
// 这里应该调用API更新订单状态为待支付status: 1
// 模拟更新订单状态
/* const { error } = await supa
.from('orders')
.update({
status: 1, // 待支付
updated_at: new Date().toISOString()
})
.eq('id', orderId.value)
if (error !== null) {
console.error('更新订单状态失败:', error)
uni.showToast({
title: '操作失败',
icon: 'none'
})
return
} */
// 更新本地存储
updateOrderInStorage(orderId.value, 1) // 1: 待支付
// 发布订单更新事件让profile页面可以刷新数据
uni.$emit('orderUpdated', { orderId: orderId.value, status: 1 })
uni.showToast({
title: '已保存到待支付订单',
icon: 'success'
})
// 延迟返回,让用户看到提示
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (err) {
console.error('取消支付异常:', err)
uni.showToast({
title: '操作失败',
icon: 'none'
})
}
}
</script>
<style scoped>
.payment-page {
display: flex;
flex-direction: column;
height: 100vh;
flex: 1;
background-color: #f5f5f5;
}
@@ -589,7 +619,7 @@ const cancelPayment = async () => {
.payment-content {
flex: 1;
overflow-y: auto;
/* overflow-y: auto; */
}
/* 价格明细部分 */
@@ -640,7 +670,7 @@ const cancelPayment = async () => {
}
.order-no {
display: block;
/* display: block; */
font-size: 12px;
color: #999999;
text-align: center;
@@ -653,7 +683,7 @@ const cancelPayment = async () => {
}
.section-title {
display: block;
/* display: block; */
font-size: 16px;
font-weight: bold;
color: #333333;
@@ -663,7 +693,7 @@ const cancelPayment = async () => {
.method-list {
display: flex;
flex-direction: column;
gap: 10px;
/* gap: 10px; */
}
.method-item {
@@ -673,6 +703,7 @@ const cancelPayment = async () => {
padding: 15px;
border: 1px solid #e5e5e5;
border-radius: 8px;
margin-bottom: 10px;
}
.method-item.selected {
@@ -765,7 +796,7 @@ const cancelPayment = async () => {
}
.password-title {
display: block;
/* display: block; */
font-size: 16px;
color: #333333;
margin-bottom: 30px;
@@ -774,7 +805,7 @@ const cancelPayment = async () => {
.password-input {
display: flex;
justify-content: center;
gap: 15px;
/* gap: 15px; */
margin-bottom: 20px;
}
@@ -786,9 +817,10 @@ const cancelPayment = async () => {
display: flex;
align-items: center;
justify-content: center;
margin: 0 7.5px;
}
.password-dot text {
.password-dot-text {
color: #ffffff;
font-size: 8px;
}
@@ -809,7 +841,7 @@ const cancelPayment = async () => {
.price-summary {
display: flex;
align-items: baseline;
align-items: flex-end;
}
.summary-label {
@@ -847,18 +879,22 @@ const cancelPayment = async () => {
}
.keyboard-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 1px;
display: flex;
flex-wrap: wrap;
/* grid-template-columns: repeat(3, 1fr); uvue unsupported */
/* grid-gap: 1px; uvue unsupported */
background-color: #e5e5e5;
}
.keyboard-key {
width: 33.33%;
background-color: #ffffff;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #f5f5f5; /* mimic grid gap */
box-sizing: border-box;
}
.key-text {