This commit is contained in:
parent
7e12363976
commit
0aee63988c
@ -41,7 +41,10 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center text-xs" :class="{ 'text-[#FF00B7]': expireDateInfo.highlight }">
|
<div
|
||||||
|
class="text-center text-xs text-white tabular-nums"
|
||||||
|
:class="{ 'text-[#FF00B7]': expireDateInfo.highlight }"
|
||||||
|
>
|
||||||
{{ expireDateInfo.text }}
|
{{ expireDateInfo.text }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,6 +94,7 @@ import DeviceList from '@/components/user-center/DeviceList.vue'
|
|||||||
import PaymentMethod from '@/components/user-center/PaymentMethod.vue'
|
import PaymentMethod from '@/components/user-center/PaymentMethod.vue'
|
||||||
import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue'
|
import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { formatExpireDate } from '../subscription'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -120,38 +124,7 @@ const currentPlanIndex = computed(() => {
|
|||||||
|
|
||||||
const expireDateInfo = computed(() => {
|
const expireDateInfo = computed(() => {
|
||||||
const first = props.alreadySubscribed[0]
|
const first = props.alreadySubscribed[0]
|
||||||
let text = ''
|
return formatExpireDate(first)
|
||||||
let highlight = false
|
|
||||||
|
|
||||||
if (!first || !first.expireDate) {
|
|
||||||
text = '尚未购买套餐'
|
|
||||||
highlight = true
|
|
||||||
} else {
|
|
||||||
// 尝试解析日期,兼容多种格式
|
|
||||||
const dateStr = first.expireDate.replace(/ /g, 'T')
|
|
||||||
const expireDateTime = new Date(dateStr)
|
|
||||||
|
|
||||||
if (isNaN(expireDateTime.getTime())) {
|
|
||||||
text = '套餐信息无效'
|
|
||||||
} else if (expireDateTime < new Date()) {
|
|
||||||
const year = expireDateTime.getFullYear()
|
|
||||||
const month = String(expireDateTime.getMonth() + 1).padStart(2, '0')
|
|
||||||
const day = String(expireDateTime.getDate()).padStart(2, '0')
|
|
||||||
text = `已于 ${year}/${month}/${day} 到期`
|
|
||||||
highlight = true
|
|
||||||
} else {
|
|
||||||
const year = expireDateTime.getFullYear()
|
|
||||||
const month = String(expireDateTime.getMonth() + 1).padStart(2, '0')
|
|
||||||
const day = String(expireDateTime.getDate()).padStart(2, '0')
|
|
||||||
const hour = String(expireDateTime.getHours()).padStart(2, '0')
|
|
||||||
const minute = String(expireDateTime.getMinutes()).padStart(2, '0')
|
|
||||||
const second = String(expireDateTime.getSeconds()).padStart(2, '0')
|
|
||||||
text = `到期时间:${year}/${month}/${day} ${hour}:${minute}:${second}`
|
|
||||||
highlight = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { text, highlight }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
|
|||||||
@ -8,7 +8,10 @@
|
|||||||
<img src="../avatar.png" class="size-[60px]" alt="" />
|
<img src="../avatar.png" class="size-[60px]" alt="" />
|
||||||
<div class="flex h-full flex-col justify-center text-white">
|
<div class="flex h-full flex-col justify-center text-white">
|
||||||
<div class="text-xl font-semibold">{{ userInfo.email }}</div>
|
<div class="text-xl font-semibold">{{ userInfo.email }}</div>
|
||||||
<div class="text-xs" :class="{ 'text-[#FF00B7]': expireDateInfo.highlight }">
|
<div
|
||||||
|
class="text-xs tabular-nums"
|
||||||
|
:class="{ 'text-[#FF00B7]': expireDateInfo.highlight }"
|
||||||
|
>
|
||||||
{{ expireDateInfo.text }}
|
{{ expireDateInfo.text }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -78,6 +81,7 @@ import DeviceList from '@/components/user-center/DeviceList.vue'
|
|||||||
import PaymentMethod from '@/components/user-center/PaymentMethod.vue'
|
import PaymentMethod from '@/components/user-center/PaymentMethod.vue'
|
||||||
import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue'
|
import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { formatExpireDate } from '../subscription'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -107,38 +111,7 @@ const currentPlanIndex = computed(() => {
|
|||||||
|
|
||||||
const expireDateInfo = computed(() => {
|
const expireDateInfo = computed(() => {
|
||||||
const first = props.alreadySubscribed[0]
|
const first = props.alreadySubscribed[0]
|
||||||
let text = ''
|
return formatExpireDate(first)
|
||||||
let highlight = false
|
|
||||||
|
|
||||||
if (!first || !first.expireDate) {
|
|
||||||
text = '尚未购买套餐'
|
|
||||||
highlight = true
|
|
||||||
} else {
|
|
||||||
// 尝试解析日期,兼容多种格式
|
|
||||||
const dateStr = first.expireDate.replace(/ /g, 'T')
|
|
||||||
const expireDateTime = new Date(dateStr)
|
|
||||||
|
|
||||||
if (isNaN(expireDateTime.getTime())) {
|
|
||||||
text = '套餐信息无效'
|
|
||||||
} else if (expireDateTime < new Date()) {
|
|
||||||
const year = expireDateTime.getFullYear()
|
|
||||||
const month = String(expireDateTime.getMonth() + 1).padStart(2, '0')
|
|
||||||
const day = String(expireDateTime.getDate()).padStart(2, '0')
|
|
||||||
text = `已于 ${year}/${month}/${day} 到期`
|
|
||||||
highlight = true
|
|
||||||
} else {
|
|
||||||
const year = expireDateTime.getFullYear()
|
|
||||||
const month = String(expireDateTime.getMonth() + 1).padStart(2, '0')
|
|
||||||
const day = String(expireDateTime.getDate()).padStart(2, '0')
|
|
||||||
const hour = String(expireDateTime.getHours()).padStart(2, '0')
|
|
||||||
const minute = String(expireDateTime.getMinutes()).padStart(2, '0')
|
|
||||||
const second = String(expireDateTime.getSeconds()).padStart(2, '0')
|
|
||||||
text = `到期时间:${year}/${month}/${day} ${hour}:${minute}:${second}`
|
|
||||||
highlight = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { text, highlight }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
|
|||||||
@ -74,6 +74,7 @@ import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue'
|
|||||||
import Logo from '@/pages/Home/logo.svg?component'
|
import Logo from '@/pages/Home/logo.svg?component'
|
||||||
import MobileLogo from '@/pages/Home/mobile-logo.svg?component'
|
import MobileLogo from '@/pages/Home/mobile-logo.svg?component'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -144,48 +145,57 @@ const handlePlanSelect = (id: string) => {
|
|||||||
selectedPlanId.value = id
|
selectedPlanId.value = id
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePay = (methodId: number | string) => {
|
const handlePay = async (methodId: number | string) => {
|
||||||
if (isPaying.value) return
|
if (isPaying.value) return
|
||||||
|
|
||||||
|
// 1. 查找套餐并校验
|
||||||
const plan = plans.value.find((p) => p.id === selectedPlanId.value)
|
const plan = plans.value.find((p) => p.id === selectedPlanId.value)
|
||||||
if (!plan) return
|
if (!plan) {
|
||||||
|
toast.error('请选择有效的订阅套餐')
|
||||||
const already = alreadySubscribed.value.find((s: any) => s.subscribe_id === plan.planId)
|
return
|
||||||
const isRenewal = !!already
|
|
||||||
const api = isRenewal ? '/api/v1/public/order/renewal' : '/api/v1/public/order/purchase'
|
|
||||||
const params: any = {
|
|
||||||
subscribe_id: plan.planId,
|
|
||||||
quantity: plan.quantity,
|
|
||||||
payment: methodId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRenewal) {
|
|
||||||
params.user_subscribe_id = already.id
|
|
||||||
}
|
|
||||||
isPaying.value = true
|
isPaying.value = true
|
||||||
request
|
|
||||||
.post(api, params)
|
try {
|
||||||
.then((res: any) => {
|
// 2. 检查订阅状态以决定是“购买”还是“续费”
|
||||||
request
|
const { list } = await request.get('/api/v1/public/user/subscribe', { includeExpired: 'all' })
|
||||||
.post('/api/v1/public/portal/order/checkout', {
|
const existingSub = list.find((s: any) => s.subscribe_id === plan.planId)
|
||||||
orderNo: res.order_no,
|
|
||||||
returnUrl: `${window.location.origin}/user-center?order_no=${res.order_no}`,
|
const isRenewal = !!existingSub
|
||||||
})
|
const api = isRenewal ? '/api/v1/public/order/renewal' : '/api/v1/public/order/purchase'
|
||||||
.then((checkoutRes: any) => {
|
|
||||||
if (checkoutRes.type === 'url' && checkoutRes.checkout_url) {
|
const orderParams = {
|
||||||
localStorage.setItem('pending_order_no', res.order_no)
|
subscribe_id: plan.planId,
|
||||||
console.log('pending_order_no', res.order_no)
|
quantity: plan.quantity,
|
||||||
setTimeout(() => {
|
payment: methodId,
|
||||||
window.location.href = checkoutRes.checkout_url
|
...(isRenewal && { user_subscribe_id: existingSub.id }), // 仅续费时添加此字段
|
||||||
})
|
}
|
||||||
}
|
|
||||||
})
|
// 3. 创建订单
|
||||||
.finally(() => {
|
const orderRes: any = await request.post(api, orderParams)
|
||||||
isPaying.value = false
|
if (!orderRes?.order_no) throw new Error('订单创建失败')
|
||||||
})
|
|
||||||
})
|
// 4. 获取支付收银台链接
|
||||||
.catch(() => {
|
const checkoutRes: any = await request.post('/api/v1/public/portal/order/checkout', {
|
||||||
isPaying.value = false
|
orderNo: orderRes.order_no,
|
||||||
|
returnUrl: `${window.location.origin}/user-center?order_no=${orderRes.order_no}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 5. 执行跳转
|
||||||
|
if (checkoutRes.type === 'url' && checkoutRes.checkout_url) {
|
||||||
|
localStorage.setItem('pending_order_no', orderRes.order_no)
|
||||||
|
window.location.href = checkoutRes.checkout_url
|
||||||
|
} else {
|
||||||
|
throw new Error('支付网关配置异常')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('支付流程异常:', error)
|
||||||
|
// 使用你之前调整过字号的 Toaster 提示错误
|
||||||
|
toast.error(error.message || '发起支付失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
isPaying.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
|
|||||||
59
src/pages/UserCenter/subscription.ts
Normal file
59
src/pages/UserCenter/subscription.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* 订阅对象的接口定义(根据你的后端数据结构调整)
|
||||||
|
*/
|
||||||
|
interface SubscriptionItem {
|
||||||
|
expire_time?: string | number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExpireInfo {
|
||||||
|
text: string
|
||||||
|
highlight: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化订阅到期信息
|
||||||
|
* @param sub 整个订阅对象 item
|
||||||
|
*/
|
||||||
|
export function formatExpireDate(sub?: SubscriptionItem | null): ExpireInfo {
|
||||||
|
// 1. 处理对象不存在的情况
|
||||||
|
if (!sub || !sub.expire_time) {
|
||||||
|
return { text: '尚未购买套餐', highlight: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 解析日期(兼容 ISO 字符串和时间戳)
|
||||||
|
const timestamp = sub.expire_time
|
||||||
|
const isMs = typeof timestamp === 'number' && timestamp > 10000000000
|
||||||
|
const expireDate = new Date(
|
||||||
|
isMs ? timestamp : typeof timestamp === 'number' ? timestamp * 1000 : timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isNaN(expireDate.getTime())) {
|
||||||
|
return { text: '套餐信息无效', highlight: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 提取日期组件
|
||||||
|
const now = new Date()
|
||||||
|
const isExpired = expireDate < now
|
||||||
|
|
||||||
|
const y = expireDate.getFullYear()
|
||||||
|
const m = String(expireDate.getMonth() + 1).padStart(2, '0')
|
||||||
|
const d = String(expireDate.getDate()).padStart(2, '0')
|
||||||
|
const dateStr = `${y}/${m}/${d}`
|
||||||
|
|
||||||
|
// 4. 根据是否过期返回不同格式
|
||||||
|
if (isExpired) {
|
||||||
|
return {
|
||||||
|
text: `已于 ${dateStr} 到期`,
|
||||||
|
highlight: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hh = String(expireDate.getHours()).padStart(2, '0')
|
||||||
|
const mm = String(expireDate.getMinutes()).padStart(2, '0')
|
||||||
|
const ss = String(expireDate.getSeconds()).padStart(2, '0')
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: `到期时间:${dateStr} ${hh}:${mm}:${ss}`,
|
||||||
|
highlight: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user