diff --git a/src/components/user-center/UserCenterSkeleton.vue b/src/components/user-center/UserCenterSkeleton.vue new file mode 100644 index 0000000..9e6c51c --- /dev/null +++ b/src/components/user-center/UserCenterSkeleton.vue @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/UserCenter/DesktopLayout/index.vue b/src/pages/UserCenter/DesktopLayout/index.vue index bc5499b..9c46830 100644 --- a/src/pages/UserCenter/DesktopLayout/index.vue +++ b/src/pages/UserCenter/DesktopLayout/index.vue @@ -1,18 +1,26 @@ - + - - - - {{ userInfo.email }} - + + + + + + + {{ userInfo.email }} + + + - + + + + @@ -43,18 +51,34 @@ class="ml-2.5 flex items-center overflow-hidden rounded-4xl bg-[#A8FF53] pt-[32px] pb-[22px]" > - + + + + + + - $emit('pay', id)" - /> + + + + + $emit('pay', id)" + /> + @@ -65,6 +89,7 @@ import { computed } from 'vue' import PlanCard from '@/components/user-center/PlanCard.vue' import DeviceList from '@/components/user-center/DeviceList.vue' import PaymentMethod from '@/components/user-center/PaymentMethod.vue' +import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue' import { Button } from '@/components/ui/button' import { toast } from 'vue-sonner' import { useRouter } from 'vue-router' @@ -78,6 +103,9 @@ const props = defineProps<{ selectedPlanId: string selectedPlan: any isPaying: boolean + isUserLoading: boolean + isPlansLoading: boolean + isPaymentsLoading: boolean }>() const emit = defineEmits(['select-plan', 'pay', 'refresh']) @@ -139,4 +167,14 @@ function logout() { .tracking-tight { font-family: 'Inter', 'PingFang SC', sans-serif; } + +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.3s ease; +} + +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} diff --git a/src/pages/UserCenter/MobileLayout/index.vue b/src/pages/UserCenter/MobileLayout/index.vue index 8b53732..2fe3ad3 100644 --- a/src/pages/UserCenter/MobileLayout/index.vue +++ b/src/pages/UserCenter/MobileLayout/index.vue @@ -2,29 +2,53 @@ - - - {{ userInfo.email }} - - {{ expireDateInfo.text }} + + + + + + {{ userInfo.email }} + + {{ expireDateInfo.text }} + + - + - + + + + - + + + + + + - $emit('pay', id)" - /> + + + + + $emit('pay', id)" + /> + @@ -52,6 +76,7 @@ import { computed } from 'vue' import PlanCard from '@/components/user-center/PlanCard.vue' import DeviceList from '@/components/user-center/DeviceList.vue' import PaymentMethod from '@/components/user-center/PaymentMethod.vue' +import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue' import { Button } from '@/components/ui/button' import { toast } from 'vue-sonner' import { useRouter } from 'vue-router' @@ -65,6 +90,9 @@ const props = defineProps<{ selectedPlanId: string selectedPlan: any isPaying: boolean + isUserLoading: boolean + isPlansLoading: boolean + isPaymentsLoading: boolean }>() const emit = defineEmits(['select-plan', 'pay', 'refresh']) @@ -126,4 +154,14 @@ function logout() { .tracking-tight { font-family: 'Inter', 'PingFang SC', sans-serif; } + +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.3s ease; +} + +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} diff --git a/src/pages/UserCenter/index.vue b/src/pages/UserCenter/index.vue index cb9963f..e7643fa 100644 --- a/src/pages/UserCenter/index.vue +++ b/src/pages/UserCenter/index.vue @@ -22,36 +22,46 @@ - - - - - - + + + + + + + + + + @@ -64,6 +74,7 @@ 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 UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue' import Logo from '@/pages/Home/logo.svg?component' import MobileLogo from '@/pages/Home/mobile-logo.svg?component' import request from '@/utils/request' @@ -73,6 +84,9 @@ const router = useRouter() // --- State --- const selectedPlanId = ref('p2') const isPaying = ref(false) +const isUserLoading = ref(true) +const isPlansLoading = ref(true) +const isPaymentsLoading = ref(true) const orderStatusDialogRef = ref | null>(null) const selectedPayment = ref('alipay') @@ -152,57 +166,77 @@ const handlePay = (methodId: number | string) => { params.user_subscribe_id = already.id } isPaying.value = true - request.post(api, params).then((res: any) => { - request - .post('/api/v1/public/portal/order/checkout', { - orderNo: res.order_no, - returnUrl: `${window.location.origin}/user-center?order_no=${res.order_no}`, - }) - .then((checkoutRes: any) => { - if (checkoutRes.type === 'url' && 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 - }) - } - }) - .finally(() => { - isPaying.value = false - }) - }).catch(() => { - isPaying.value = false - }) + request + .post(api, params) + .then((res: any) => { + request + .post('/api/v1/public/portal/order/checkout', { + orderNo: res.order_no, + returnUrl: `${window.location.origin}/user-center?order_no=${res.order_no}`, + }) + .then((checkoutRes: any) => { + if (checkoutRes.type === 'url' && 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 + }) + } + }) + .finally(() => { + isPaying.value = false + }) + }) + .catch(() => { + isPaying.value = false + }) } -function init() { - // 用户信息 & 设备列表 - request.get('/api/v1/public/user/info').then((res: any) => { - devices.value = res.user_devices - const emailInfo = res.auth_methods?.find((item: any) => item.auth_type === 'email') - if (emailInfo) { - userSubInfo.value.email = emailInfo.auth_identifier - } - }) +async function init() { + // 1. 用户信息 & 设备列表 + isUserLoading.value = true + request + .get('/api/v1/public/user/info') + .then((res: any) => { + devices.value = res.user_devices + const emailInfo = res.auth_methods?.find((item: any) => item.auth_type === 'email') + if (emailInfo) { + userSubInfo.value.email = emailInfo.auth_identifier + } + }) + .finally(() => { + isUserLoading.value = false + }) - // 已订阅列表 + // 2. 已订阅列表 (非阻塞主要展示) request.get('/api/v1/public/user/subscribe').then((res: any) => { alreadySubscribed.value = res.list || [] }) - // 订阅套餐列表 - request.get('/api/v1/public/subscribe/list').then((res: any) => { - plans.value = mapPlans(res.list || []) - if (plans.value.length > 0) { - selectedPlanId.value = plans.value[0].id - } - }) + // 3. 订阅套餐列表 + isPlansLoading.value = true + request + .get('/api/v1/public/subscribe/list') + .then((res: any) => { + plans.value = mapPlans(res.list || []) + if (plans.value.length > 0) { + selectedPlanId.value = plans.value[0].id + } + }) + .finally(() => { + isPlansLoading.value = false + }) - // 获取支付方式 - request.get('/api/v1/public/payment/methods').then((res: any) => { - console.log(res) - payments.value = res.list?.filter((p: any) => p.platform !== 'apple_iap') || [] - }) + // 4. 获取支付方式 + isPaymentsLoading.value = true + request + .get('/api/v1/public/payment/methods') + .then((res: any) => { + payments.value = res.list?.filter((p: any) => p.platform !== 'apple_iap') || [] + }) + .finally(() => { + isPaymentsLoading.value = false + }) } onMounted(() => { @@ -220,6 +254,18 @@ function handleStatusClose() { } + +