feat: 恢复余额支付 + 门票暂停购买状态展示 + 静默处理门票产品404
This commit is contained in:
@@ -86,10 +86,12 @@ export function request(options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const error = normalizeError(res, `请求错误 ${res.statusCode}`)
|
const error = normalizeError(res, `请求错误 ${res.statusCode}`)
|
||||||
|
if (!options.silent) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: error.message,
|
title: error.message,
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
|
}
|
||||||
reject(error)
|
reject(error)
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ const toTicketOrder = (backendData = {}) => ({
|
|||||||
export const ticketApi = {
|
export const ticketApi = {
|
||||||
getTicketProduct() {
|
getTicketProduct() {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/tickets/product'
|
url: '/api/tickets/product',
|
||||||
|
silent: true
|
||||||
}).then(toTicketProduct)
|
}).then(toTicketProduct)
|
||||||
},
|
},
|
||||||
getTicketAccount(params = {}) {
|
getTicketAccount(params = {}) {
|
||||||
|
|||||||
@@ -52,6 +52,25 @@
|
|||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else-if="ticketPaused" class="ticket-section">
|
||||||
|
<view class="card ticket-paused-card">
|
||||||
|
<text class="ticket-paused-title">门票暂停购买</text>
|
||||||
|
<text class="ticket-paused-desc">门票暂未开放购买,请稍后再来</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="ticketRecords.length" class="ticket-records-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">我的门票</text>
|
||||||
|
</view>
|
||||||
|
<view v-for="record in ticketRecords" :key="record.id" class="card ticket-record-card">
|
||||||
|
<view class="ticket-record-top">
|
||||||
|
<text class="ticket-record-no">订单号 {{ record.order_no }}</text>
|
||||||
|
<text class="ticket-status-badge" :class="record.status">{{ record.status_label }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="ticket-record-date">{{ formatDate(record.created_at) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<view v-else class="load-more-tip">加载中...</view>
|
<view v-else class="load-more-tip">加载中...</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -118,6 +137,7 @@ const isLoadingMore = ref(false)
|
|||||||
let loadRequestId = 0
|
let loadRequestId = 0
|
||||||
|
|
||||||
const ticketProduct = ref(null)
|
const ticketProduct = ref(null)
|
||||||
|
const ticketPaused = ref(false)
|
||||||
const ticketQty = ref(1)
|
const ticketQty = ref(1)
|
||||||
const ticketSubmitting = ref(false)
|
const ticketSubmitting = ref(false)
|
||||||
const ticketRecords = ref([])
|
const ticketRecords = ref([])
|
||||||
@@ -199,8 +219,11 @@ const loadTicketProduct = async () => {
|
|||||||
if (ticketProduct.value) return
|
if (ticketProduct.value) return
|
||||||
try {
|
try {
|
||||||
ticketProduct.value = await ticketApi.getTicketProduct()
|
ticketProduct.value = await ticketApi.getTicketProduct()
|
||||||
} catch {
|
ticketPaused.value = false
|
||||||
// ignore
|
} catch (error) {
|
||||||
|
if (error?.code === 40400 || error?.statusCode === 404) {
|
||||||
|
ticketPaused.value = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +285,7 @@ const goDetail = (item) => {
|
|||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
if (activeTab.value === 'tickets') {
|
if (activeTab.value === 'tickets') {
|
||||||
ticketProduct.value = null
|
ticketProduct.value = null
|
||||||
|
ticketPaused.value = false
|
||||||
ticketRecords.value = []
|
ticketRecords.value = []
|
||||||
await Promise.all([loadTicketProduct(), loadTicketRecords()])
|
await Promise.all([loadTicketProduct(), loadTicketRecords()])
|
||||||
} else {
|
} else {
|
||||||
@@ -477,6 +501,25 @@ defineExpose({ refresh, loadMore })
|
|||||||
margin-bottom: $spacing-md;
|
margin-bottom: $spacing-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket-paused-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: $spacing-xl $spacing-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-paused-title {
|
||||||
|
display: block;
|
||||||
|
color: $text-color-main;
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-paused-desc {
|
||||||
|
display: block;
|
||||||
|
margin-top: $spacing-xs;
|
||||||
|
color: $text-color-muted;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.product-head,
|
.product-head,
|
||||||
.amount-row,
|
.amount-row,
|
||||||
.stepper-row {
|
.stepper-row {
|
||||||
|
|||||||
@@ -23,12 +23,24 @@
|
|||||||
|
|
||||||
<view class="card method-card">
|
<view class="card method-card">
|
||||||
<text class="card-title">支付方式</text>
|
<text class="card-title">支付方式</text>
|
||||||
<view class="method-item active">
|
<view
|
||||||
|
v-if="!isStoredValueOrder"
|
||||||
|
class="method-item"
|
||||||
|
:class="{ active: payMethod === 'balance', disabled: !canUseBalance }"
|
||||||
|
@tap="selectMethod('balance')"
|
||||||
|
>
|
||||||
|
<view>
|
||||||
|
<text class="method-title">余额支付</text>
|
||||||
|
<text class="method-sub">当前余额 ¥{{ balance.balance }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="method-check">{{ payMethod === 'balance' ? '已选' : '' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="method-item" :class="{ active: payMethod === 'wechat' }" @tap="selectMethod('wechat')">
|
||||||
<view>
|
<view>
|
||||||
<text class="method-title">微信支付</text>
|
<text class="method-title">微信支付</text>
|
||||||
<text class="method-sub">使用微信支付完成订单</text>
|
<text class="method-sub">使用微信支付完成订单</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="method-check">已选</text>
|
<text class="method-check">{{ payMethod === 'wechat' ? '已选' : '' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -49,19 +61,28 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onUnmounted, ref } from 'vue'
|
import { computed, onUnmounted, ref } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { memberApi } from '@/modules/comprehensive/api/member'
|
||||||
import { orderApi } from '@/modules/comprehensive/api/order'
|
import { orderApi } from '@/modules/comprehensive/api/order'
|
||||||
import { formatDateTime } from '@/modules/comprehensive/utils/date'
|
import { formatDateTime } from '@/modules/comprehensive/utils/date'
|
||||||
|
import { toAmountNumber } from '@/modules/comprehensive/utils/money'
|
||||||
|
|
||||||
const orderId = ref('')
|
const orderId = ref('')
|
||||||
const order = ref(null)
|
const order = ref(null)
|
||||||
|
const balance = ref({ balance: '0.00' })
|
||||||
|
const payMethod = ref('balance')
|
||||||
const isSubmitting = ref(false)
|
const isSubmitting = ref(false)
|
||||||
const nowTimestamp = ref(Date.now())
|
const nowTimestamp = ref(Date.now())
|
||||||
let countdownTimer = null
|
let countdownTimer = null
|
||||||
|
|
||||||
const canPay = computed(() => order.value?.status === 'pending_pay')
|
const isStoredValueOrder = computed(() => order.value?.business_type === 'stored_value')
|
||||||
|
const canUseBalance = computed(() => {
|
||||||
|
if (isStoredValueOrder.value) return false
|
||||||
|
return toAmountNumber(balance.value.balance) >= toAmountNumber(order.value?.amount)
|
||||||
|
})
|
||||||
|
const canPay = computed(() => order.value?.status === 'pending_pay' && (payMethod.value !== 'balance' || canUseBalance.value))
|
||||||
const payButtonText = computed(() => {
|
const payButtonText = computed(() => {
|
||||||
if (order.value?.status !== 'pending_pay') return '订单不可支付'
|
if (order.value?.status !== 'pending_pay') return '订单不可支付'
|
||||||
return '微信支付'
|
return payMethod.value === 'balance' ? '余额支付' : '微信支付'
|
||||||
})
|
})
|
||||||
const paymentRemainingSeconds = computed(() => {
|
const paymentRemainingSeconds = computed(() => {
|
||||||
if (!order.value?.expire_at) return 0
|
if (!order.value?.expire_at) return 0
|
||||||
@@ -86,7 +107,17 @@ onUnmounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const loadPage = async () => {
|
const loadPage = async () => {
|
||||||
order.value = await orderApi.getOrderDetail(orderId.value)
|
const [orderData, balanceData] = await Promise.all([
|
||||||
|
orderApi.getOrderDetail(orderId.value),
|
||||||
|
memberApi.getBalance()
|
||||||
|
])
|
||||||
|
order.value = orderData
|
||||||
|
balance.value = balanceData
|
||||||
|
if (isStoredValueOrder.value) {
|
||||||
|
payMethod.value = 'wechat'
|
||||||
|
} else if (!canUseBalance.value) {
|
||||||
|
payMethod.value = 'wechat'
|
||||||
|
}
|
||||||
updateCountdownTimer()
|
updateCountdownTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,13 +139,33 @@ const stopCountdown = () => {
|
|||||||
|
|
||||||
const padTime = (value) => String(value).padStart(2, '0')
|
const padTime = (value) => String(value).padStart(2, '0')
|
||||||
|
|
||||||
|
const selectMethod = (method) => {
|
||||||
|
if (method === 'balance') {
|
||||||
|
if (isStoredValueOrder.value) {
|
||||||
|
uni.showToast({ title: '储值卡订单仅支持微信支付', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!canUseBalance.value) {
|
||||||
|
uni.showToast({ title: '余额不足', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payMethod.value = method
|
||||||
|
}
|
||||||
|
|
||||||
const pay = async () => {
|
const pay = async () => {
|
||||||
if (!canPay.value || isSubmitting.value) return
|
if (!canPay.value || isSubmitting.value) return
|
||||||
|
if (isStoredValueOrder.value && payMethod.value === 'balance') return
|
||||||
isSubmitting.value = true
|
isSubmitting.value = true
|
||||||
try {
|
try {
|
||||||
|
if (payMethod.value === 'balance') {
|
||||||
|
await orderApi.payOrderByBalance(order.value.id)
|
||||||
|
uni.showToast({ title: '支付成功', icon: 'success' })
|
||||||
|
} else {
|
||||||
const paymentData = await orderApi.payOrderByWechat(order.value.id)
|
const paymentData = await orderApi.payOrderByWechat(order.value.id)
|
||||||
await requestWechatPayment(paymentData.wechat)
|
await requestWechatPayment(paymentData.wechat)
|
||||||
uni.showToast({ title: '支付已提交', icon: 'success' })
|
uni.showToast({ title: '支付已提交', icon: 'success' })
|
||||||
|
}
|
||||||
await loadPage()
|
await loadPage()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.redirectTo({ url: `/pages/comprehensive/orders/detail?id=${order.value.id}` })
|
uni.redirectTo({ url: `/pages/comprehensive/orders/detail?id=${order.value.id}` })
|
||||||
@@ -218,6 +269,10 @@ const requestWechatPayment = (wechatPayload) => new Promise((resolve, reject) =>
|
|||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.method-item.disabled {
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
.method-title,
|
.method-title,
|
||||||
.method-sub {
|
.method-sub {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
Reference in New Issue
Block a user