完善下单逻辑及其ui展示,修复支付倒计时显示错误bug
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
<view class="status-title-row">
|
||||
<text class="status-emoji">{{ getStatusIcon() }}</text>
|
||||
<text class="status-text">{{ getStatusText() }}</text>
|
||||
<text v-if="getPendingCountdownText() != ''" class="status-countdown">{{ getPendingCountdownText() }}</text>
|
||||
</view>
|
||||
<text class="status-desc">{{ getStatusDesc() }}</text>
|
||||
</view>
|
||||
@@ -135,7 +136,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="action-right">
|
||||
<view v-if="order?.order_status === 1" class="btn-group">
|
||||
<view v-if="order?.order_status === 1 && !isTimeoutOrder()" class="btn-group">
|
||||
<button class="btn" @click="cancelOrder">取消订单</button>
|
||||
<button class="btn primary" @click="payOrder">立即支付</button>
|
||||
</view>
|
||||
@@ -157,9 +158,11 @@
|
||||
<button class="btn primary" @click="goToReview">评价订单</button>
|
||||
</view>
|
||||
|
||||
<view v-if="order?.order_status === 5" class="btn-group">
|
||||
<button class="btn primary" @click="rePurchase">重新购买</button>
|
||||
</view>
|
||||
<view v-if="shouldShowCancelledActions()" class="btn-group">
|
||||
<button class="btn" @click="deleteOrder">删除订单</button>
|
||||
<button class="btn" @click="viewSimilar">看相似</button>
|
||||
<button class="btn primary" @click="rePurchase">再次购买</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -167,16 +170,21 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { onBackPress, onHide, onLoad, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||
import { goToLogin } from '@/utils/utils.uts'
|
||||
import supa from '@/components/supadb/aksupainstance.uts'
|
||||
import { formatCountdownHMS, getOrderDisplayStatus, getRemainingSeconds, isOrderPayExpired, ORDER_STATUS_CANCELLED, ORDER_STATUS_PENDING, ORDER_TIMEOUT_CANCEL_REASON, PAYMENT_STATUS_TIMEOUT, PAYMENT_STATUS_UNPAID, type OrderStatusSource } from '@/utils/orderStatus.uts'
|
||||
|
||||
// 定义订单类型
|
||||
type OrderType = {
|
||||
order_no: string,
|
||||
order_status: number,
|
||||
payment_status: number,
|
||||
cancel_reason: string,
|
||||
pay_expire_at: string,
|
||||
consumer_deleted_at: string,
|
||||
total_amount: number,
|
||||
product_amount: number,
|
||||
shipping_fee: number,
|
||||
@@ -221,10 +229,51 @@ const orderItems = ref<OrderItemType[]>([])
|
||||
const shopName = ref('店铺名称')
|
||||
const deliveryAddress = ref<AddressType | null>(null)
|
||||
const deliveryInfo = ref<DeliveryInfoType | null>(null)
|
||||
const nowTick = ref<number>(Date.now())
|
||||
let detailTicker = 0
|
||||
|
||||
const toOrderStatusSource = (): OrderStatusSource | null => {
|
||||
const currentOrder = order.value
|
||||
if (currentOrder == null) return null
|
||||
return {
|
||||
order_status: currentOrder.order_status,
|
||||
payment_status: currentOrder.payment_status,
|
||||
pay_expire_at: currentOrder.pay_expire_at,
|
||||
created_at: currentOrder.created_at,
|
||||
cancel_reason: currentOrder.cancel_reason
|
||||
}
|
||||
}
|
||||
|
||||
const isTimeoutOrder = (): boolean => {
|
||||
const source = toOrderStatusSource()
|
||||
if (source == null) return false
|
||||
return isOrderPayExpired(source)
|
||||
}
|
||||
|
||||
const getPendingCountdownText = (): string => {
|
||||
const source = toOrderStatusSource()
|
||||
if (source == null) return ''
|
||||
const currentTick = nowTick.value
|
||||
if (currentTick < 0) return ''
|
||||
if (getOrderDisplayStatus(source) != 'pending') return ''
|
||||
return formatCountdownHMS(getRemainingSeconds(source))
|
||||
}
|
||||
|
||||
const shouldShowCancelledActions = (): boolean => {
|
||||
const source = toOrderStatusSource()
|
||||
if (source == null) return false
|
||||
return getOrderDisplayStatus(source) == 'cancelled'
|
||||
}
|
||||
|
||||
// 辅助函数 - 必须在调用前定义
|
||||
const getStatusText = (): string => {
|
||||
const status = order.value?.order_status ?? 0
|
||||
const source = toOrderStatusSource()
|
||||
if (source != null) {
|
||||
const displayStatus = getOrderDisplayStatus(source)
|
||||
if (displayStatus == 'pending') return '待付款'
|
||||
if (displayStatus == 'cancelled') return '已取消'
|
||||
}
|
||||
if (status == 1) return '待付款'
|
||||
if (status == 2) return '待发货'
|
||||
if (status == 3) return '待收货'
|
||||
@@ -237,6 +286,20 @@ const getStatusText = (): string => {
|
||||
|
||||
const getStatusDesc = (): string => {
|
||||
const status = order.value?.order_status ?? 0
|
||||
const source = toOrderStatusSource()
|
||||
if (source != null) {
|
||||
const displayStatus = getOrderDisplayStatus(source)
|
||||
if (displayStatus == 'pending') {
|
||||
return '请在 ' + getPendingCountdownText() + ' 内支付'
|
||||
}
|
||||
if (displayStatus == 'cancelled') {
|
||||
const currentReason = order.value?.cancel_reason ?? ''
|
||||
if (currentReason.indexOf('超时') >= 0) {
|
||||
return '订单超时未支付,已自动取消'
|
||||
}
|
||||
return '订单已取消'
|
||||
}
|
||||
}
|
||||
if (status == 1) return '请尽快完成支付'
|
||||
if (status == 2) return '商家正在打包商品'
|
||||
if (status == 3) return '商品正在赶往您的地址'
|
||||
@@ -249,6 +312,7 @@ const getStatusDesc = (): string => {
|
||||
|
||||
const getStatusIcon = (): string => {
|
||||
const status = order.value?.order_status ?? 0
|
||||
if (shouldShowCancelledActions()) return '⏰'
|
||||
if (status === 1) return '💳'
|
||||
if (status === 2) return '📦'
|
||||
if (status === 3) return '🚚'
|
||||
@@ -257,8 +321,14 @@ const getStatusIcon = (): string => {
|
||||
}
|
||||
|
||||
const getStatusClass = (): string => {
|
||||
const source = toOrderStatusSource()
|
||||
if (source != null) {
|
||||
const displayStatus = getOrderDisplayStatus(source)
|
||||
if (displayStatus == 'pending') return 'status-pending'
|
||||
if (displayStatus == 'cancelled') return 'status-cancelled'
|
||||
}
|
||||
const status = order.value?.order_status ?? 0
|
||||
return `status-${status}`
|
||||
return 'status-' + status
|
||||
}
|
||||
|
||||
const getFullAddress = (addr: any): string => {
|
||||
@@ -389,6 +459,10 @@ const loadOrderDetail = async () => {
|
||||
order.value = {
|
||||
order_no: (dataObj.get('order_no') ?? '') as string,
|
||||
order_status: (dataObj.get('order_status') ?? 1) as number,
|
||||
payment_status: (dataObj.get('payment_status') ?? 1) as number,
|
||||
cancel_reason: (dataObj.get('cancel_reason') ?? '') as string,
|
||||
pay_expire_at: (dataObj.get('pay_expire_at') ?? '') as string,
|
||||
consumer_deleted_at: (dataObj.get('consumer_deleted_at') ?? '') as string,
|
||||
total_amount: (dataObj.get('total_amount') ?? 0) as number,
|
||||
product_amount: (dataObj.get('product_amount') ?? 0) as number,
|
||||
shipping_fee: (dataObj.get('shipping_fee') ?? 0) as number,
|
||||
@@ -424,6 +498,15 @@ const loadOrderDetail = async () => {
|
||||
orderItems.value.push(orderItem)
|
||||
}
|
||||
}
|
||||
|
||||
if (order.value.consumer_deleted_at != '') {
|
||||
order.value = null
|
||||
uni.showToast({ title: '订单不存在', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({ url: '/pages/mall/consumer/orders' })
|
||||
}, 600)
|
||||
return
|
||||
}
|
||||
|
||||
const addressRaw = dataObj.get('shipping_address')
|
||||
console.log('[loadOrderDetail] 收货地址数据:', addressRaw)
|
||||
@@ -470,6 +553,7 @@ const loadOrderDetail = async () => {
|
||||
}
|
||||
|
||||
console.log('[loadOrderDetail] 订单详情加载成功,商品数量:', orderItems.value.length)
|
||||
syncTimeoutState()
|
||||
} else {
|
||||
uni.showToast({ title: '订单不存在', icon: 'none' })
|
||||
}
|
||||
@@ -481,6 +565,35 @@ const loadOrderDetail = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const syncTimeoutState = (): void => {
|
||||
nowTick.value = Date.now()
|
||||
const currentOrder = order.value
|
||||
if (currentOrder == null) return
|
||||
if (currentOrder.order_status == ORDER_STATUS_PENDING && currentOrder.payment_status == PAYMENT_STATUS_UNPAID && isTimeoutOrder()) {
|
||||
currentOrder.order_status = ORDER_STATUS_CANCELLED
|
||||
currentOrder.payment_status = PAYMENT_STATUS_TIMEOUT
|
||||
if (currentOrder.cancel_reason == '') {
|
||||
currentOrder.cancel_reason = ORDER_TIMEOUT_CANCEL_REASON
|
||||
}
|
||||
supabaseService.expireOrder(orderId.value)
|
||||
}
|
||||
}
|
||||
|
||||
const startDetailTicker = (): void => {
|
||||
if (detailTicker > 0) return
|
||||
syncTimeoutState()
|
||||
detailTicker = setInterval(() => {
|
||||
syncTimeoutState()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const stopDetailTicker = (): void => {
|
||||
if (detailTicker > 0) {
|
||||
clearInterval(detailTicker)
|
||||
detailTicker = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 动作函数
|
||||
const contactService = () => {
|
||||
const userId = supabaseService.getCurrentUserId()
|
||||
@@ -508,13 +621,49 @@ const contactService = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const payOrder = () => {
|
||||
const payOrder = async () => {
|
||||
if (isTimeoutOrder()) {
|
||||
uni.showToast({ title: '订单已取消,不能继续支付', icon: 'none' })
|
||||
loadOrderDetail()
|
||||
return
|
||||
}
|
||||
|
||||
const totalAmount = order.value?.total_amount ?? 0
|
||||
const userId = supabaseService.getCurrentUserId()
|
||||
if (userId == null || userId === '') {
|
||||
goToLogin(`/pages/mall/consumer/payment?orderId=${orderId.value}&amount=${totalAmount}`)
|
||||
return
|
||||
}
|
||||
|
||||
const latestOrder = await supabaseService.getOrderDetail(orderId.value)
|
||||
if (latestOrder != null) {
|
||||
const latestObj = JSON.parse(JSON.stringify(latestOrder)) as UTSJSONObject
|
||||
const latestStatus = latestObj.getNumber('order_status') ?? 1
|
||||
const latestPaymentStatus = latestObj.getNumber('payment_status') ?? 1
|
||||
const latestCancelReason = latestObj.getString('cancel_reason') ?? ''
|
||||
const latestPayExpireAt = latestObj.getString('pay_expire_at') ?? ''
|
||||
if (order.value != null) {
|
||||
order.value.order_status = latestStatus
|
||||
order.value.payment_status = latestPaymentStatus
|
||||
order.value.cancel_reason = latestCancelReason
|
||||
order.value.pay_expire_at = latestPayExpireAt
|
||||
}
|
||||
if (isTimeoutOrder()) {
|
||||
await supabaseService.expireOrder(orderId.value)
|
||||
if (order.value != null) {
|
||||
order.value.order_status = ORDER_STATUS_CANCELLED
|
||||
order.value.payment_status = PAYMENT_STATUS_TIMEOUT
|
||||
order.value.cancel_reason = ORDER_TIMEOUT_CANCEL_REASON
|
||||
}
|
||||
uni.showToast({ title: '订单已取消,不能继续支付', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (latestStatus != ORDER_STATUS_PENDING || latestPaymentStatus != PAYMENT_STATUS_UNPAID) {
|
||||
uni.showToast({ title: '订单状态已变更,不能继续支付', icon: 'none' })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=${orderId.value}&amount=${totalAmount}`
|
||||
})
|
||||
@@ -522,23 +671,14 @@ const payOrder = () => {
|
||||
|
||||
const doCancelOrder = async () => {
|
||||
try {
|
||||
const updatePayload = new UTSJSONObject()
|
||||
updatePayload.set('order_status', 5)
|
||||
updatePayload.set('updated_at', new Date().toISOString())
|
||||
|
||||
const result = await supa
|
||||
.from('ml_orders')
|
||||
.update(updatePayload)
|
||||
.eq('id', orderId.value)
|
||||
.execute()
|
||||
|
||||
if (result.error == null) {
|
||||
const result = await supabaseService.cancelOrder(orderId.value)
|
||||
if (result) {
|
||||
if (order.value != null) {
|
||||
order.value.order_status = 5
|
||||
order.value.order_status = ORDER_STATUS_CANCELLED
|
||||
order.value.payment_status = PAYMENT_STATUS_TIMEOUT
|
||||
}
|
||||
uni.showToast({ title: '订单已取消' })
|
||||
} else {
|
||||
console.error('[doCancelOrder] 取消订单失败:', result.error)
|
||||
uni.showToast({ title: '取消失败', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -547,6 +687,34 @@ const doCancelOrder = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const deleteOrder = async () => {
|
||||
uni.showLoading({ title: '删除中...' })
|
||||
try {
|
||||
const success = await supabaseService.softDeleteOrderForConsumer(orderId.value)
|
||||
uni.hideLoading()
|
||||
if (!success) {
|
||||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: '订单已删除', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({ url: '/pages/mall/consumer/orders' })
|
||||
}, 600)
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const viewSimilar = () => {
|
||||
if (orderItems.value.length == 0) {
|
||||
uni.showToast({ title: '暂无相似商品', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const keyword = orderItems.value[0].product_name != '' ? orderItems.value[0].product_name : '商品'
|
||||
uni.navigateTo({ url: `/pages/mall/consumer/search?keyword=${encodeURIComponent(keyword)}` })
|
||||
}
|
||||
|
||||
const cancelOrder = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
@@ -801,6 +969,21 @@ onLoad((options) => {
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (orderId.value != '') {
|
||||
loadOrderDetail()
|
||||
}
|
||||
startDetailTicker()
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
stopDetailTicker()
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
stopDetailTicker()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -868,12 +1051,26 @@ onLoad((options) => {
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.status-countdown {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
font-size: 14px;
|
||||
opacity: 0.95;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: linear-gradient(135deg, #ff9000, #ff5000);
|
||||
}
|
||||
|
||||
.status-cancelled {
|
||||
background: linear-gradient(135deg, #9aa5b1, #6b7280);
|
||||
}
|
||||
|
||||
/* 分享免单入口 */
|
||||
.share-free-entry {
|
||||
margin-top: 20px;
|
||||
|
||||
Reference in New Issue
Block a user