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:
2026-06-14 10:08:46 +03:30
co-authored by Cursor
parent 4404b4cb92
commit a5e458ed25
234 changed files with 18425 additions and 5503 deletions
+188
View File
@@ -0,0 +1,188 @@
import { computed, onMounted, reactive, ref } from 'vue'
import type { FormInstance } from 'ant-design-vue'
import { message } from 'ant-design-vue'
import OtpInput from 'vue3-otp-input'
import type { Login } from '@/models/login'
import {
authenticateWithFingerprint,
getToken,
loading,
setToken,
} from '@/utils'
import useCountdownTimer from '@/utils/useCountdownTimer'
import { loginCustomer, verifyToken } from '@/api/login'
import router from '@/router'
import { useUserStore } from '@/store/userStore'
import { useWalletStore } from '@/store/walletStore'
import { useSettingsStore } from '@/store/settingsStore'
import { setTruth } from '@/main'
export function useLoginForm() {
const { time, onFinish, reset } = useCountdownTimer('00:02:00', 'mm:ss')
const enableSendAgainCode = ref(false)
const stepTwoVerify = ref(false)
const formRef = ref<FormInstance>()
const userStore = useUserStore()
const walletStore = useWalletStore()
const refOtpInput = ref<InstanceType<typeof OtpInput> | null>(null)
const isSMS = ref(true)
const isRegister = ref<boolean | undefined>()
const showPWAInstallGuide = ref(false)
const userFormState = reactive<Login>({
provider: 'customer',
})
onFinish(() => {
enableSendAgainCode.value = true
})
async function reSendCode() {
userFormState.token = undefined
enableSendAgainCode.value = false
await loginCustomer(userFormState)
reset()
}
function setIsSMS(val: boolean) {
isSMS.value = val
if (!val) stepTwoVerify.value = false
userFormState.token = ''
userFormState.password = ''
}
async function enterFingerPrint() {
const fingerPrint = await authenticateWithFingerprint()
if (fingerPrint) {
setTruth()
await router.push({ name: 'home' })
} else {
message.error('خطا در اعتبارسنجی')
}
}
function handleOnChange(value: string) {
userFormState.token = value
}
async function handleOnComplete(value: string) {
userFormState.token = value
phoneVerification()
}
async function login(accessToken: string | undefined) {
setToken(accessToken)
await userStore.getMyInfo()
await walletStore.getMyWallet()
setTruth()
await router.push({ name: 'home' })
}
function getcode() {
if ('OTPCredential' in window) {
const ac = new AbortController()
navigator.credentials
.get({
otp: { transport: ['sms'] },
signal: ac.signal,
})
.then((otp) => {
userFormState.token = otp?.code
refOtpInput.value?.fillInput(otp?.code)
phoneVerification()
ac.abort()
})
.catch(() => {
ac.abort()
})
}
}
function phoneVerification() {
formRef.value?.validateFields().then(async () => {
try {
loading.value = true
if (isSMS.value) {
if (!stepTwoVerify.value) {
userFormState.token = undefined
userFormState.password = undefined
userFormState.provider = 'customer'
const { id } = await loginCustomer(userFormState)
userFormState.id = id
stepTwoVerify.value = true
getcode()
reset()
} else {
const { access_token } = await verifyToken(userFormState)
await login(access_token)
}
} else {
userFormState.token = undefined
const { access_token } = await loginCustomer(userFormState)
await login(access_token)
}
} catch (e) {
console.log(e)
} finally {
loading.value = false
}
})
}
function editPhoneNumber() {
stepTwoVerify.value = false
}
async function isOpenRegister() {
const settingsStore = useSettingsStore()
await settingsStore.fetchSettings()
isRegister.value = settingsStore.settings.find(
(s) => s?.key === 'open_register' && s?.is_active === true,
)
}
function installWebApp() {
showPWAInstallGuide.value = true
}
function proceedWithInstall() {
showPWAInstallGuide.value = false
}
const submitLabel = computed(() => {
if (!isSMS.value) return 'ورود'
return stepTwoVerify.value ? 'ورود' : 'ارسال کد'
})
const showFingerprint = computed(
() => Boolean(getToken() && userStore.userInfo?.finger_print),
)
onMounted(async () => {
await isOpenRegister()
})
return {
time,
enableSendAgainCode,
stepTwoVerify,
formRef,
refOtpInput,
isSMS,
isRegister,
showPWAInstallGuide,
userFormState,
loading,
submitLabel,
showFingerprint,
setIsSMS,
reSendCode,
enterFingerPrint,
handleOnChange,
handleOnComplete,
phoneVerification,
editPhoneNumber,
installWebApp,
proceedWithInstall,
}
}
@@ -0,0 +1,45 @@
import { inject, onMounted, provide, reactive, type InjectionKey } from 'vue'
import router from '@/router'
import { useLoginForm } from './useLoginForm'
import { useRegisterForm } from './useRegisterForm'
export interface LoginPageContext {
enterType: boolean
setEnterType: (val: boolean) => void
loginForm: ReturnType<typeof useLoginForm>
registerForm: ReturnType<typeof useRegisterForm>
}
export const LOGIN_PAGE_CONTEXT_KEY: InjectionKey<LoginPageContext> = Symbol('loginPageContext')
export function useLoginPage() {
const loginForm = useLoginForm()
const registerForm = useRegisterForm()
const context = reactive<LoginPageContext>({
enterType: false,
setEnterType(val: boolean) {
context.enterType = val
},
loginForm,
registerForm,
})
provide(LOGIN_PAGE_CONTEXT_KEY, context)
onMounted(() => {
if (router.currentRoute.value.query.id) {
context.enterType = true
}
})
return context
}
export function useLoginPageContext() {
const context = inject(LOGIN_PAGE_CONTEXT_KEY)
if (!context) {
throw new Error('useLoginPageContext must be used within useLoginPage')
}
return context
}
@@ -0,0 +1,60 @@
import { onMounted, reactive, ref } from 'vue'
import type { FormInstance } from 'ant-design-vue'
import { message } from 'ant-design-vue'
import type { User } from '@/models/login'
import { loading, URLUserType } from '@/utils'
import { registerCustomer } from '@/api/login'
import router from '@/router'
export function useRegisterForm() {
const registeredInfo = ref(false)
const disableForm = ref(false)
const userFormState = reactive<User>({
access_token: '',
})
const formRef = ref<FormInstance>()
onMounted(() => {
if (router.currentRoute.value.query.id) {
userFormState.referral_id = router.currentRoute.value.query.id as string
disableForm.value = true
}
})
function registerAccount() {
formRef.value?.validateFields().then(async () => {
try {
loading.value = true
URLUserType() === 'single' ? (userFormState.type = 2) : (userFormState.type = 1)
if (!userFormState.referral_id || userFormState.referral_id === '') {
delete userFormState.referral_id
}
await registerCustomer(userFormState)
registeredInfo.value = true
} catch (e: any) {
console.log(e)
if (e.response?.status === 422) {
message.error('این شماره همراه قبلا ثبت نام کرده است')
}
} finally {
loading.value = false
}
})
}
function dismissSuccess() {
registeredInfo.value = false
}
return {
registeredInfo,
disableForm,
userFormState,
formRef,
loading,
registerAccount,
dismissSuccess,
}
}