登录对接
This commit is contained in:
parent
033dda7744
commit
2b0741fdb9
@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<div v-if="!devices?.length" class="w-full text-center">暂无绑定设备</div>
|
||||
<div class="grid min-h-[76px] grid-cols-2 gap-3">
|
||||
<div
|
||||
v-for="device in devices"
|
||||
|
||||
191
src/components/user-center/OrderStatusDialog.vue
Normal file
191
src/components/user-center/OrderStatusDialog.vue
Normal file
@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<Dialog :open="open" @update:open="handleClose">
|
||||
<DialogContent
|
||||
:show-close-button="false"
|
||||
@pointer-down-outside="(event) => event.preventDefault()"
|
||||
@focus-outside="(event) => event.preventDefault()"
|
||||
class="max-w-[345px] rounded-[32px] border-none bg-[#E5E5E5] p-6"
|
||||
>
|
||||
<div class="flex flex-col items-center py-6">
|
||||
<h2 class="mb-1 text-xl font-semibold text-black">订单状态</h2>
|
||||
<div class="mb-10 text-[24px] font-bold text-black">
|
||||
{{ statusInfo.title }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="statusInfo.description"
|
||||
class="mb-8 text-center text-sm whitespace-pre-line text-gray-600"
|
||||
>
|
||||
{{ statusInfo.description }}
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex w-full gap-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="h-[50px] flex-1 rounded-[25px] bg-[#D1D1D1] text-lg font-bold text-gray-600 hover:bg-gray-300"
|
||||
@click="handleClose"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
<Button
|
||||
class="h-[50px] flex-1 rounded-[25px] bg-[#ADFF5B] text-lg font-bold text-black hover:bg-[#9EE64F]"
|
||||
@click="handleClose"
|
||||
>
|
||||
返回首页
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const emit = defineEmits(['close', 'refresh'])
|
||||
|
||||
const open = ref(false)
|
||||
const orderNo = ref<string | null>(null)
|
||||
const status = ref<number>(1) // 1: Pending, 2: Paid, 3: Finished, 4: Close, 5: Failed, -1: Error
|
||||
const countdown = ref('')
|
||||
let timer: any = null
|
||||
let countdownTimer: any = null
|
||||
let createdAt: Date | null = null
|
||||
|
||||
const statusInfo = computed(() => {
|
||||
switch (status.value) {
|
||||
case -1:
|
||||
return {
|
||||
title: '检查失败',
|
||||
description: '检查支付状态失败,请稍后刷新页面或联系客服',
|
||||
}
|
||||
case 1:
|
||||
return {
|
||||
title: '待支付',
|
||||
description: `订单正在处理中\n剩余时间: ${countdown.value}`,
|
||||
}
|
||||
case 2:
|
||||
return {
|
||||
title: '支付确认中',
|
||||
description: '订单已支付,系统正在确认,请稍候...',
|
||||
}
|
||||
case 3:
|
||||
return {
|
||||
title: '支付成功',
|
||||
description: '您的订单已支付完成',
|
||||
}
|
||||
case 4:
|
||||
return {
|
||||
title: '订单已关闭',
|
||||
description: '该订单已超时或被手动关闭',
|
||||
}
|
||||
case 5:
|
||||
return {
|
||||
title: '支付失败',
|
||||
description: '订单支付失败,请检查支付信息并重试',
|
||||
}
|
||||
default:
|
||||
return {
|
||||
title: '待支付',
|
||||
description: `订单正在处理中\n剩余时间: ${countdown.value}`,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function handleClose() {
|
||||
stopPolling()
|
||||
open.value = false
|
||||
emit('close')
|
||||
}
|
||||
|
||||
async function checkStatus() {
|
||||
if (!orderNo.value) return
|
||||
|
||||
try {
|
||||
const res: any = await request.get('/api/v1/public/order/detail', { order_no: orderNo.value })
|
||||
// 1. 保存订单创建时间(用于倒计时)
|
||||
if (!createdAt && res.created_at) {
|
||||
const timestamp = res.created_at
|
||||
const isMilliseconds = timestamp > 10000000000
|
||||
createdAt = new Date(isMilliseconds ? timestamp : timestamp * 1000)
|
||||
startCountdown()
|
||||
}
|
||||
|
||||
status.value = res.status
|
||||
|
||||
// 2. 根据状态处理业务逻辑
|
||||
if (status.value === 3) {
|
||||
// Finished
|
||||
stopPolling()
|
||||
emit('refresh')
|
||||
} else if (status.value === 4 || status.value === 5) {
|
||||
// Closed or Failed
|
||||
stopPolling()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 查询失败:', error)
|
||||
status.value = -1
|
||||
stopPolling()
|
||||
}
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
stopPolling()
|
||||
checkStatus()
|
||||
timer = setInterval(checkStatus, 3000)
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
countdownTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function startCountdown() {
|
||||
if (!createdAt) return
|
||||
|
||||
const updateCountdown = () => {
|
||||
const now = new Date()
|
||||
// 假设订单有效期为 15 分钟 (900 秒)
|
||||
const expiryTime = new Date(createdAt!.getTime() + 900 * 1000)
|
||||
const diff = expiryTime.getTime() - now.getTime()
|
||||
console.log('diff', diff)
|
||||
if (diff <= 0) {
|
||||
countdown.value = '00:00'
|
||||
clearInterval(countdownTimer)
|
||||
return
|
||||
}
|
||||
|
||||
const minutes = Math.floor(diff / 1000 / 60)
|
||||
const seconds = Math.floor((diff / 1000) % 60)
|
||||
countdown.value = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
updateCountdown()
|
||||
if (countdownTimer) clearInterval(countdownTimer)
|
||||
countdownTimer = setInterval(updateCountdown, 1000)
|
||||
}
|
||||
|
||||
function show(no: string) {
|
||||
orderNo.value = no
|
||||
status.value = 1
|
||||
createdAt = null
|
||||
countdown.value = '15:00'
|
||||
open.value = true
|
||||
startPolling()
|
||||
}
|
||||
|
||||
defineExpose({ show })
|
||||
|
||||
onUnmounted(() => {
|
||||
stopPolling()
|
||||
})
|
||||
</script>
|
||||
@ -51,20 +51,27 @@
|
||||
@refresh="init"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<OrderStatusDialog ref="orderStatusDialogRef" @close="handleStatusClose" @refresh="init" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import MobileLayout from './MobileLayout/index.vue'
|
||||
import DesktopLayout from './DesktopLayout/index.vue'
|
||||
import OrderStatusDialog from '@/components/user-center/OrderStatusDialog.vue'
|
||||
import Logo from '@/pages/Home/logo.svg?component'
|
||||
import MobileLogo from '@/pages/Home/mobile-logo.svg?component'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
// --- State ---
|
||||
const selectedPlanId = ref('p2')
|
||||
const selectedPayment = ref('alipay') // This variable is no longer directly used for payment selection in the UI, but kept for potential future use or if other parts of the app rely on it.
|
||||
const orderStatusDialogRef = ref<InstanceType<typeof OrderStatusDialog> | null>(null)
|
||||
const selectedPayment = ref('alipay')
|
||||
|
||||
const devices = ref<any[]>([])
|
||||
const plans = ref<any[]>([])
|
||||
@ -130,7 +137,7 @@ const handlePay = (methodId: number | string) => {
|
||||
|
||||
const already = alreadySubscribed.value.find((s: any) => s.subscribe_id === plan.planId)
|
||||
const isRenewal = !!already
|
||||
const api = isRenewal ? '/api/v1//public/order/renewal' : '/api/v1/public/order/purchase'
|
||||
const api = isRenewal ? '/api/v1/public/order/renewal' : '/api/v1/public/order/purchase'
|
||||
const params: any = {
|
||||
subscribe_id: plan.planId,
|
||||
quantity: plan.quantity,
|
||||
@ -145,11 +152,15 @@ const handlePay = (methodId: number | string) => {
|
||||
request
|
||||
.post('/api/v1/public/portal/order/checkout', {
|
||||
orderNo: res.order_no,
|
||||
returnUrl: window.location.origin,
|
||||
returnUrl: `${window.location.origin}/user-center?order_no=${res.order_no}`,
|
||||
})
|
||||
.then((checkoutRes: any) => {
|
||||
if (checkoutRes.type === 'url' && checkoutRes.checkout_url) {
|
||||
window.location.href = checkoutRes.checkout_url
|
||||
localStorage.setItem('pending_order_no', res.order_no)
|
||||
console.log('pending_order_no', res.order_no)
|
||||
setTimeout(() => {
|
||||
window.location.href = checkoutRes.checkout_url
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -184,7 +195,20 @@ function init() {
|
||||
payments.value = res.list?.filter((p: any) => p.platform !== 'apple_iap') || []
|
||||
})
|
||||
}
|
||||
init()
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
const orderNo = (route.query.order_no as string) || localStorage.getItem('pending_order_no')
|
||||
if (orderNo) {
|
||||
orderStatusDialogRef.value?.show(orderNo)
|
||||
}
|
||||
})
|
||||
|
||||
function handleStatusClose() {
|
||||
localStorage.removeItem('pending_order_no')
|
||||
// 清除 URL 中的 order_no,防止刷新再次弹出
|
||||
router.replace({ query: { ...route.query, order_no: undefined } })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user