fix(ui): center page loading spinner and use accent spin color

Apply shared app-page-content-loading layout and theme-aligned spin dots across the app.
This commit is contained in:
2026-08-31 02:36:29 -04:00
parent b1cf0639f5
commit e9012e26fb
6 changed files with 58 additions and 32 deletions
+11
View File
@@ -7,17 +7,27 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
ABadge: typeof import('ant-design-vue/es')['Badge']
AButton: typeof import('ant-design-vue/es')['Button'] AButton: typeof import('ant-design-vue/es')['Button']
ADivider: typeof import('ant-design-vue/es')['Divider'] ADivider: typeof import('ant-design-vue/es')['Divider']
AForm: typeof import('ant-design-vue/es')['Form'] AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem'] AFormItem: typeof import('ant-design-vue/es')['FormItem']
AImage: typeof import('ant-design-vue/es')['Image']
AInput: typeof import('ant-design-vue/es')['Input'] AInput: typeof import('ant-design-vue/es')['Input']
AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
AModal: typeof import('ant-design-vue/es')['Modal'] AModal: typeof import('ant-design-vue/es')['Modal']
AppCreditLink: typeof import('./src/components/AppCreditLink.vue')['default'] AppCreditLink: typeof import('./src/components/AppCreditLink.vue')['default']
ARadio: typeof import('ant-design-vue/es')['Radio']
ARadioButton: typeof import('ant-design-vue/es')['RadioButton'] ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASlider: typeof import('ant-design-vue/es')['Slider']
ASpin: typeof import('ant-design-vue/es')['Spin'] ASpin: typeof import('ant-design-vue/es')['Spin']
ASwitch: typeof import('ant-design-vue/es')['Switch']
ATag: typeof import('ant-design-vue/es')['Tag']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
BaseDatePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue')['default'] BaseDatePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue')['default']
Basic: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/basic.vue')['default'] Basic: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/basic.vue')['default']
BasicActionSheet: typeof import('./src/components/BasicPacks/BasicActionSheet.vue')['default'] BasicActionSheet: typeof import('./src/components/BasicPacks/BasicActionSheet.vue')['default']
@@ -69,5 +79,6 @@ declare module 'vue' {
TransactionSkeleton: typeof import('./src/components/TransactionSkeleton.vue')['default'] TransactionSkeleton: typeof import('./src/components/TransactionSkeleton.vue')['default']
VanActionSheet: typeof import('vant/es')['ActionSheet'] VanActionSheet: typeof import('vant/es')['ActionSheet']
VanDialog: typeof import('vant/es')['Dialog'] VanDialog: typeof import('vant/es')['Dialog']
VanField: typeof import('vant/es')['Field']
} }
} }
+2 -2
View File
@@ -240,9 +240,9 @@
} }
.ant-spin-dot-item { .ant-spin-dot-item {
background-color: @App-primary-color !important; background-color: var(--color-primary, var(--App-secondary-color)) !important;
} }
.ant-spin-dot i { .ant-spin-dot i {
background-color: @App-primary-color !important; background-color: var(--color-primary, var(--App-secondary-color)) !important;
} }
+11
View File
@@ -465,4 +465,15 @@ body:has(.ant-modal-root .ant-modal-wrap:not([style*='display: none'])) {
} }
.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover{ .ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover{
color: white !important; color: white !important;
}
/* لودینگ محتوای صفحه — وسط ناحیه محتوا (موبایل و دسکتاپ یکسان) */
.app-page-content-loading {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 100%;
min-height: min(52vh, 22rem);
padding: 2rem 1rem;
} }
@@ -1,20 +1,21 @@
<template> <template>
<div class="dapp-notif"> <div class="dapp-notif">
<header class="dapp-notif__top"> <BrowserNotificationPermissionBanner class="mb-4 max-w-3xl w-full" />
<div>
<p class="dapp-notif__eyebrow">اعلان‌ها</p>
<h1 class="dapp-notif__title">پیام‌های مدیریت</h1>
</div>
</header>
<BrowserNotificationPermissionBanner class="mb-4 max-w-3xl" /> <div v-if="loading && !items.length" class="app-page-content-loading">
<a-spin size="large" />
</div>
<a-spin :spinning="loading"> <div
<div v-if="!items.length" class="dapp-notif__empty"> v-else-if="!items.length"
<BasicIcon icon="vuesax-linear:notification-bing" size="36" /> class="dapp-notif__empty"
<p>اعلانی برای نمایش نیست.</p> >
</div> <BasicIcon icon="vuesax-linear:notification-bing" size="36" />
<div v-else class="dapp-notif__list"> <p>اعلانی برای نمایش نیست.</p>
</div>
<a-spin v-else :spinning="loading">
<div class="dapp-notif__list">
<AppNotificationCard <AppNotificationCard
v-for="item in items" v-for="item in items"
:key="item.id" :key="item.id"
@@ -40,16 +41,10 @@ const { loading, items, openItem } = useAppNotificationsPageContext()
.dapp-notif { .dapp-notif {
@apply p-6 min-h-full; @apply p-6 min-h-full;
} }
.dapp-notif__eyebrow {
@apply m-0 text-xs text-gray-500;
}
.dapp-notif__title {
@apply m-0 mt-1 text-xl font-bold;
}
.dapp-notif__empty { .dapp-notif__empty {
@apply mt-10 flex flex-col items-center gap-2 text-gray-500; @apply flex flex-col items-center gap-2 text-gray-500;
} }
.dapp-notif__list { .dapp-notif__list {
@apply mt-6 flex flex-col gap-3 max-w-3xl; @apply flex flex-col gap-3 w-full;
} }
</style> </style>
+2 -2
View File
@@ -1,7 +1,7 @@
<template> <template>
<div class="app-notifications-page-root"> <div class="app-notifications-page-root">
<DesktopAppNotificationsView v-show="isDesktop" /> <DesktopAppNotificationsView v-if="isDesktop" />
<MobileAppNotificationsView v-show="!isDesktop" /> <MobileAppNotificationsView v-else />
<BasicActionSheet <BasicActionSheet
v-model:show="detailVisible" v-model:show="detailVisible"
@@ -3,13 +3,22 @@
<MobilePageHeader title="اعلان‌ها" show-back /> <MobilePageHeader title="اعلان‌ها" show-back />
<div class="app-notif-page__body mpage-content"> <div class="app-notif-page__body mpage-content">
<BrowserNotificationPermissionBanner class="mb-3" /> <BrowserNotificationPermissionBanner class="mb-3" />
<a-spin :spinning="loading">
<div v-if="!items.length" class="app-notif-page__empty"> <div v-if="loading && !items.length" class="app-page-content-loading">
<BasicIcon icon="vuesax-linear:notification-bing" size="40" class="text-gray-400" /> <a-spin size="large" />
<p class="m-0 mt-3 font-bold text-gray-700">اعلانی نیست</p> </div>
<p class="m-0 mt-1 text-sm text-gray-500">هنوز اعلانی از طرف مدیریت ارسال نشده است.</p>
</div> <div
<div v-else class="flex flex-col gap-3"> v-else-if="!items.length"
class="app-notif-page__empty"
>
<BasicIcon icon="vuesax-linear:notification-bing" size="40" class="text-gray-400" />
<p class="m-0 mt-3 font-bold text-gray-700">اعلانی نیست</p>
<p class="m-0 mt-1 text-sm text-gray-500">هنوز اعلانی از طرف مدیریت ارسال نشده است.</p>
</div>
<a-spin v-else :spinning="loading">
<div class="flex flex-col gap-3">
<AppNotificationCard <AppNotificationCard
v-for="item in items" v-for="item in items"
:key="item.id" :key="item.id"