Split desktop and mobile layouts with shared page data so APIs fetch once per context.
Add layout shells, desktop views, module-level page caches, and UI fixes for auth, home, wallet, transactions, address, and related pages. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<a-form
|
||||
:model="form"
|
||||
class="!mt-0"
|
||||
name="gift-card-form"
|
||||
@finish="registerGiftCard"
|
||||
>
|
||||
<a-form-item :rules="[required()]" label="کد کارت هدیه" name="code">
|
||||
<BasicInput
|
||||
v-model:value="form.code"
|
||||
place-holder="کد کارت هدیه را وارد کنید"
|
||||
size="large"
|
||||
type="text"
|
||||
/>
|
||||
</a-form-item>
|
||||
<p :class="variant === 'desktop' ? 'dgift-form__hint' : 'font-bold text-xs mt-3'">
|
||||
کد کارت هدیه خود را در فیلد بالا وارد کنید و سپس دکمه ثبت را بزنید.
|
||||
</p>
|
||||
<a-form-item>
|
||||
<BasicButton
|
||||
:disabled="disableButton"
|
||||
:class="variant === 'desktop' ? 'dgift-form__submit' : 'w-full py-2 mt-5'"
|
||||
html-type="submit"
|
||||
type="primary"
|
||||
>
|
||||
<a-spin v-if="loading" size="small" />
|
||||
<span v-else>ثبت کد</span>
|
||||
</BasicButton>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||
import { required } from '@/utils/inputRules'
|
||||
import { useGiftCardPageContext } from '../composables/useGiftCardPage'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
variant?: 'mobile' | 'desktop'
|
||||
}>(),
|
||||
{ variant: 'mobile' },
|
||||
)
|
||||
|
||||
const {
|
||||
form,
|
||||
disableButton,
|
||||
loading,
|
||||
registerGiftCard,
|
||||
} = useGiftCardPageContext()
|
||||
</script>
|
||||
@@ -0,0 +1,58 @@
|
||||
import { computed, inject, provide, ref, type ComputedRef, type InjectionKey, type Ref } from 'vue'
|
||||
import { loading } from '@/utils'
|
||||
import { registerCodeGiftCard } from '@/api/giftCard'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
export interface GiftCardPageContext {
|
||||
form: Ref<{ code: string }>
|
||||
disableButton: ComputedRef<boolean>
|
||||
loading: typeof loading
|
||||
registerGiftCard: () => Promise<void>
|
||||
}
|
||||
|
||||
export const GIFT_CARD_PAGE_CONTEXT_KEY: InjectionKey<GiftCardPageContext> =
|
||||
Symbol('giftCardPageContext')
|
||||
|
||||
export function useGiftCardPage() {
|
||||
const form = ref<{ code: string }>({
|
||||
code: '',
|
||||
})
|
||||
|
||||
const disableButton = computed(() => {
|
||||
if (!loading.value) {
|
||||
return !form.value.code || form.value.code.trim().length === 0
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
async function registerGiftCard() {
|
||||
try {
|
||||
loading.value = true
|
||||
await registerCodeGiftCard(form.value.code)
|
||||
message.success('کد کارت هدیه با موفقیت ثبت شد')
|
||||
form.value.code = ''
|
||||
} catch (e: any) {
|
||||
message.error(e?.response?.data?.message || 'خطا در ثبت کد کارت هدیه')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const context: GiftCardPageContext = {
|
||||
form,
|
||||
disableButton,
|
||||
loading,
|
||||
registerGiftCard,
|
||||
}
|
||||
|
||||
provide(GIFT_CARD_PAGE_CONTEXT_KEY, context)
|
||||
return context
|
||||
}
|
||||
|
||||
export function useGiftCardPageContext() {
|
||||
const context = inject(GIFT_CARD_PAGE_CONTEXT_KEY)
|
||||
if (!context) {
|
||||
throw new Error('useGiftCardPageContext must be used within a page that calls useGiftCardPage()')
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="dgift">
|
||||
<div class="dgift__container">
|
||||
<header class="dgift-topbar">
|
||||
<button type="button" class="dgift-topbar__back" @click="router.back()">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="18" />
|
||||
<span>بازگشت</span>
|
||||
</button>
|
||||
<div class="dgift-topbar__meta">
|
||||
<p class="dgift-topbar__eyebrow">هدایا</p>
|
||||
<h1 class="dgift-topbar__title">ثبت کارت هدیه</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="dgift-layout">
|
||||
<section class="dgift-surface dgift-form">
|
||||
<div class="dgift-form__header">
|
||||
<div class="dgift-form__icon">
|
||||
<BasicIcon icon="vuesax-linear:gift" size="28" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="dgift-form__title">ثبت کد کارت هدیه</h2>
|
||||
<p class="dgift-form__subtitle">کد دریافتی خود را وارد کنید تا اعتبار به حساب شما اضافه شود</p>
|
||||
</div>
|
||||
</div>
|
||||
<GiftCardForm variant="desktop" />
|
||||
</section>
|
||||
|
||||
<aside class="dgift-surface dgift-guide">
|
||||
<h3 class="dgift-guide__title">راهنمای استفاده</h3>
|
||||
<ol class="dgift-guide__steps">
|
||||
<li>کد کارت هدیه را از پیامک یا کارت فیزیکی دریافت کنید.</li>
|
||||
<li>کد را بدون فاصله اضافی در فیلد مربوطه وارد کنید.</li>
|
||||
<li>روی «ثبت کد» بزنید و پیام تأیید را بررسی کنید.</li>
|
||||
</ol>
|
||||
<div class="dgift-guide__note">
|
||||
<BasicIcon icon="vuesax-linear:info-circle" size="18" />
|
||||
<p>هر کد فقط یکبار قابل استفاده است و پس از ثبت موفق، موجودی کیف پول بهروزرسانی میشود.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import router from '@/router'
|
||||
import GiftCardForm from '../components/GiftCardForm.vue'
|
||||
</script>
|
||||
@@ -0,0 +1,186 @@
|
||||
/* Premium desktop gift card page */
|
||||
|
||||
.dgift {
|
||||
min-height: 100%;
|
||||
background: #f6f7f9;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.dgift__container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 28px 36px 40px;
|
||||
}
|
||||
|
||||
.dgift-topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 28px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid rgba(15, 23, 42, 0.06);
|
||||
|
||||
&__back {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #6b7280;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
padding: 8px 0;
|
||||
|
||||
&:hover {
|
||||
color: #111827;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__eyebrow {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #9ca3af;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.03em;
|
||||
color: #111827;
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.dgift-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.dgift-surface {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(15, 23, 42, 0.06);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.dgift-form {
|
||||
padding: 28px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #eef0f3;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 14px;
|
||||
background: rgba(9, 61, 81, 0.08);
|
||||
color: @App-primary-color;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: #6b7280;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__hint {
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: #6b7280;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
&__submit {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.dgift-guide {
|
||||
padding: 24px;
|
||||
position: sticky;
|
||||
top: 24px;
|
||||
|
||||
&__title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
&__steps {
|
||||
margin: 0 0 20px;
|
||||
padding: 0 20px 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
&__note {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
padding: 14px;
|
||||
border-radius: 12px;
|
||||
background: #f9fafb;
|
||||
border: 1px solid #eef0f3;
|
||||
color: #6b7280;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
svg,
|
||||
.basic-icon {
|
||||
color: @App-primary-color;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1023.98px) {
|
||||
.dgift-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dgift-guide {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-white py-3 px-3 text-black font-bold drop-shadow z-[1999] wrap-fix">کارت هدیه</div>
|
||||
<div class="px-5 pt-26 pb-2">
|
||||
<a-form :model="form" class="!mt-5" name="basic" @finish="registerGiftCard">
|
||||
<div class="border-gray-200 border-1 rounded-xl p-5 trans h-40 relative">
|
||||
<bg_main />
|
||||
<div class="z-100 relative">
|
||||
<a-form-item :rules="[required()]" label="کد کارت هدیه" name="code">
|
||||
<BasicInput
|
||||
v-model:value="form.code"
|
||||
place-holder="کد کارت هدیه را وارد کنید"
|
||||
size="large"
|
||||
type="text"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div class="font-bold text-xs mt-3">
|
||||
کد کارت هدیه خود را در فیلد بالا وارد کنید و سپس دکمه ثبت را بزنید.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-form-item>
|
||||
<BasicButton
|
||||
:disabled="disableButton"
|
||||
class="w-full py-2 mt-5"
|
||||
html-type="submit"
|
||||
type="primary"
|
||||
>
|
||||
<div>
|
||||
<a-spin v-if="loading" size="small" />
|
||||
<div v-else>ثبت کد</div>
|
||||
</div>
|
||||
</BasicButton>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="gift-card-page-root">
|
||||
<DesktopGiftCardView v-show="isDesktop" />
|
||||
<MobileGiftCardView v-show="!isDesktop" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { loading } from '@/utils'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import Bg_main from '@/components/bg_main.vue'
|
||||
import { registerCodeGiftCard } from '@/api/giftCard'
|
||||
import { message } from 'ant-design-vue'
|
||||
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||
import { required } from '@/utils/inputRules'
|
||||
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||
import { useGiftCardPage } from './composables/useGiftCardPage'
|
||||
import DesktopGiftCardView from './desktop/DesktopGiftCardView.vue'
|
||||
import MobileGiftCardView from './mobile/MobileGiftCardView.vue'
|
||||
|
||||
const form = ref<{ code: string }>({
|
||||
code: ''
|
||||
})
|
||||
const isDesktop = useDesktopBreakpoint()
|
||||
|
||||
const disableButton = computed(() => {
|
||||
if (!loading.value) {
|
||||
return !form.value.code || form.value.code.trim().length === 0
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
async function registerGiftCard() {
|
||||
try {
|
||||
loading.value = true
|
||||
await registerCodeGiftCard(form.value.code)
|
||||
message.success('کد کارت هدیه با موفقیت ثبت شد')
|
||||
form.value.code = ''
|
||||
} catch (e: any) {
|
||||
message.error(e?.response?.data?.message || 'خطا در ثبت کد کارت هدیه')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
useGiftCardPage()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.gift-card-page-root {
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-white py-3 px-3 text-black font-bold drop-shadow z-[1999] wrap-fix">کارت هدیه</div>
|
||||
<div class="px-5 pt-26 pb-2">
|
||||
<div class="border-gray-200 border-1 rounded-xl p-5 trans h-40 relative">
|
||||
<bg_main />
|
||||
<div class="z-100 relative">
|
||||
<GiftCardForm variant="mobile" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Bg_main from '@/components/bg_main.vue'
|
||||
import GiftCardForm from '../components/GiftCardForm.vue'
|
||||
</script>
|
||||
Reference in New Issue
Block a user