feat: add required kyc blocking modal and fix copper asset units to kilogram
This commit is contained in:
Vendored
+1
@@ -68,6 +68,7 @@ declare module 'vue' {
|
|||||||
PwaInstallGuideModal: typeof import('./src/components/pwa/PwaInstallGuideModal.vue')['default']
|
PwaInstallGuideModal: typeof import('./src/components/pwa/PwaInstallGuideModal.vue')['default']
|
||||||
PwaInstallStepIllustration: typeof import('./src/components/pwa/PwaInstallStepIllustration.vue')['default']
|
PwaInstallStepIllustration: typeof import('./src/components/pwa/PwaInstallStepIllustration.vue')['default']
|
||||||
RangePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/range-picker.vue')['default']
|
RangePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/range-picker.vue')['default']
|
||||||
|
RequiredKycModal: typeof import('./src/components/RequiredKycModal.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SelectInRange: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/select-in-range.vue')['default']
|
SelectInRange: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/select-in-range.vue')['default']
|
||||||
|
|||||||
+6
-1
@@ -30,9 +30,10 @@
|
|||||||
<component :is="Component" :key="route.fullPath" />
|
<component :is="Component" :key="route.fullPath" />
|
||||||
</router-view>
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
|
<RequiredKycModal v-if="!showSplash && !isTvBoardRoute && !isLoginRoute" />
|
||||||
</AConfigProvider>
|
</AConfigProvider>
|
||||||
<div
|
<div
|
||||||
v-if="showSupportButton && !showSplash && route.name !== 'support' && route.name !== 'login' && !isTvBoardRoute"
|
v-if="showSupportButton && !showSplash && route.name !== 'support' && route.name !== 'login' && !isTvBoardRoute && !isKycRequired"
|
||||||
id="support-button"
|
id="support-button"
|
||||||
ref="supportButtonEl"
|
ref="supportButtonEl"
|
||||||
class="fixed z-[5100] cursor-grab select-none rounded-full"
|
class="fixed z-[5100] cursor-grab select-none rounded-full"
|
||||||
@@ -132,6 +133,7 @@ import { usePwaInstall } from '@/composables/usePwaInstall'
|
|||||||
import PwaInstallBanner from '@/components/pwa/PwaInstallBanner.vue'
|
import PwaInstallBanner from '@/components/pwa/PwaInstallBanner.vue'
|
||||||
import PwaInstallGuideModal from '@/components/pwa/PwaInstallGuideModal.vue'
|
import PwaInstallGuideModal from '@/components/pwa/PwaInstallGuideModal.vue'
|
||||||
import GoldDayCalculatorModal from '@/components/GoldDayCalculatorModal.vue'
|
import GoldDayCalculatorModal from '@/components/GoldDayCalculatorModal.vue'
|
||||||
|
import RequiredKycModal from '@/components/RequiredKycModal.vue'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import { useSettingsStore } from '@/store/settingsStore'
|
import { useSettingsStore } from '@/store/settingsStore'
|
||||||
import {
|
import {
|
||||||
@@ -299,6 +301,9 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const isKycRequired = computed(
|
||||||
|
() => Boolean(getToken()) && (userStore.userInfo?.is_legal === 0 || !userStore.userInfo?.is_legal),
|
||||||
|
)
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
const showLoginCreditExtras = computed(() => {
|
const showLoginCreditExtras = computed(() => {
|
||||||
if (!isLoginRoute.value) return false
|
if (!isLoginRoute.value) return false
|
||||||
|
|||||||
@@ -82,6 +82,16 @@ export function setupRouterGuards() {
|
|||||||
return goToLogin(next)
|
return goToLogin(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// کاربر احراز هویت نشده نباید بتواند به صفحات داخلی دسترسی پیدا کند
|
||||||
|
if (
|
||||||
|
userStore.userInfo &&
|
||||||
|
(userStore.userInfo.is_legal === 0 || !userStore.userInfo.is_legal) &&
|
||||||
|
!isPublicRoute(to) &&
|
||||||
|
to.name !== 'home'
|
||||||
|
) {
|
||||||
|
return goToHome(next)
|
||||||
|
}
|
||||||
|
|
||||||
if (to.name === 'notifyTrigger') {
|
if (to.name === 'notifyTrigger') {
|
||||||
const { useSettingsStore } = await import('@/store/settingsStore')
|
const { useSettingsStore } = await import('@/store/settingsStore')
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
|||||||
@@ -0,0 +1,352 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:open="isModalVisible"
|
||||||
|
:closable="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:keyboard="false"
|
||||||
|
:footer="null"
|
||||||
|
:centered="true"
|
||||||
|
:z-index="5500"
|
||||||
|
:width="modalWidth"
|
||||||
|
class="required-kyc-modal"
|
||||||
|
wrap-class-name="required-kyc-modal-wrap"
|
||||||
|
>
|
||||||
|
<div class="kyc-modal-content text-right" dir="rtl">
|
||||||
|
<!-- State 1: Need to enter/edit KYC information -->
|
||||||
|
<template v-if="!isPendingApproval || isEditing">
|
||||||
|
<div class="text-center mb-5">
|
||||||
|
<div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-amber-50 text-amber-700 border border-amber-200 mb-3 shadow-sm">
|
||||||
|
<BasicIcon icon="vuesax-bold:security-user" size="32" color="#b45309" />
|
||||||
|
</div>
|
||||||
|
<h2 class="text-lg font-bold text-slate-800 mb-1">
|
||||||
|
احراز هویت الزامی است
|
||||||
|
</h2>
|
||||||
|
<p class="text-xs text-slate-500 leading-6 max-w-sm mx-auto">
|
||||||
|
کاربر گرامی، جهت حفظ امنیت حساب و امکان انجام معاملات مس و واریز/برداشت وجه، لازم است مشخصات هویتی خود را ثبت نمایید.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formState"
|
||||||
|
layout="vertical"
|
||||||
|
class="kyc-form space-y-2"
|
||||||
|
@finish="onSubmit"
|
||||||
|
>
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-3 gap-y-0">
|
||||||
|
<a-form-item
|
||||||
|
label="نام"
|
||||||
|
name="fname"
|
||||||
|
:rules="[required('نام'), isValidPersian()]"
|
||||||
|
>
|
||||||
|
<BasicInput
|
||||||
|
v-model:value="formState.fname"
|
||||||
|
placeholder="مثال: علی"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="نام خانوادگی"
|
||||||
|
name="lname"
|
||||||
|
:rules="[required('نام خانوادگی'), isValidPersian()]"
|
||||||
|
>
|
||||||
|
<BasicInput
|
||||||
|
v-model:value="formState.lname"
|
||||||
|
placeholder="مثال: محمدی"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="کد ملی (۱۰ رقم)"
|
||||||
|
name="n_code"
|
||||||
|
:rules="[requireNationalCode()]"
|
||||||
|
>
|
||||||
|
<BasicInput
|
||||||
|
v-model:value="formState.n_code"
|
||||||
|
type="text"
|
||||||
|
inputmode="numeric"
|
||||||
|
maxlength="10"
|
||||||
|
placeholder="مثال: ۰۰۱۲۳۴۵۶۷۸"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-3 gap-y-0">
|
||||||
|
<a-form-item
|
||||||
|
label="تاریخ تولد"
|
||||||
|
name="birth_date"
|
||||||
|
:rules="[required('تاریخ تولد')]"
|
||||||
|
>
|
||||||
|
<BaseDatePicker
|
||||||
|
v-model:value="formState.birth_date"
|
||||||
|
class="w-full"
|
||||||
|
input-read-only
|
||||||
|
placeholder="انتخاب تاریخ تولد"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="شهر"
|
||||||
|
name="city"
|
||||||
|
:rules="[required('شهر')]"
|
||||||
|
>
|
||||||
|
<BasicInput
|
||||||
|
v-model:value="formState.city"
|
||||||
|
placeholder="مثال: تهران"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-4 flex flex-col gap-2.5">
|
||||||
|
<BasicButton
|
||||||
|
type="primary"
|
||||||
|
html-type="submit"
|
||||||
|
:disabled="loading"
|
||||||
|
class="w-full h-11 text-sm font-semibold !rounded-xl"
|
||||||
|
>
|
||||||
|
<a-spin v-if="loading" size="small" />
|
||||||
|
<span v-else>ثبت و استعلام هویت</span>
|
||||||
|
</BasicButton>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between gap-2 pt-1">
|
||||||
|
<button
|
||||||
|
v-if="isEditing && isPendingApproval"
|
||||||
|
type="button"
|
||||||
|
class="text-xs text-slate-500 hover:text-slate-700 py-1 px-2 cursor-pointer transition-colors"
|
||||||
|
@click="isEditing = false"
|
||||||
|
>
|
||||||
|
انصراف از ویرایش
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="text-xs text-red-600 hover:text-red-700 flex items-center gap-1 py-1 px-2 mr-auto cursor-pointer transition-colors"
|
||||||
|
@click="onLogout"
|
||||||
|
>
|
||||||
|
<BasicIcon icon="vuesax-linear:logout" size="14" color="#dc2626" />
|
||||||
|
<span>خروج از حساب کاربری</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- State 2: Already submitted, waiting for Admin review -->
|
||||||
|
<template v-else>
|
||||||
|
<div class="text-center mb-5">
|
||||||
|
<div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-amber-50 text-amber-700 border border-amber-200 mb-3 shadow-sm">
|
||||||
|
<BasicIcon icon="vuesax-bold:clock" size="32" color="#d97706" />
|
||||||
|
</div>
|
||||||
|
<h2 class="text-lg font-bold text-slate-800 mb-1">
|
||||||
|
در انتظار تأیید اطلاعات هویتی
|
||||||
|
</h2>
|
||||||
|
<p class="text-xs text-slate-600 leading-6 max-w-sm mx-auto">
|
||||||
|
اطلاعات شما با موفقیت در سامانه ثبت شده و در صف بررسی و تایید کارشناسان مدیریت قرار دارد. پس از تایید هویت، دسترسی به تمام بخشها فعال خواهد شد.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-slate-50 rounded-xl p-3.5 border border-slate-200 text-xs text-slate-700 space-y-2 mb-5">
|
||||||
|
<div class="flex justify-between items-center py-1 border-b border-slate-200/60">
|
||||||
|
<span class="text-slate-500">نام و نام خانوادگی:</span>
|
||||||
|
<span class="font-medium text-slate-800">{{ formState.fname }} {{ formState.lname }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center py-1 border-b border-slate-200/60">
|
||||||
|
<span class="text-slate-500">کد ملی:</span>
|
||||||
|
<span class="font-medium text-slate-800 tracking-wider">{{ formState.n_code }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="formState.birth_date" class="flex justify-between items-center py-1 border-b border-slate-200/60">
|
||||||
|
<span class="text-slate-500">تاریخ تولد:</span>
|
||||||
|
<span class="font-medium text-slate-800">{{ formState.birth_date }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="formState.city" class="flex justify-between items-center py-1">
|
||||||
|
<span class="text-slate-500">شهر:</span>
|
||||||
|
<span class="font-medium text-slate-800">{{ formState.city }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2.5">
|
||||||
|
<BasicButton
|
||||||
|
type="primary"
|
||||||
|
:disabled="checkingStatus"
|
||||||
|
class="w-full h-11 text-sm font-semibold !rounded-xl"
|
||||||
|
@click="onCheckStatus"
|
||||||
|
>
|
||||||
|
<a-spin v-if="checkingStatus" size="small" />
|
||||||
|
<span v-else>بررسی مجدد وضعیت تأیید</span>
|
||||||
|
</BasicButton>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between gap-2 pt-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="text-xs text-slate-600 hover:text-slate-800 flex items-center gap-1 py-1 px-2 cursor-pointer transition-colors"
|
||||||
|
@click="isEditing = true"
|
||||||
|
>
|
||||||
|
<BasicIcon icon="vuesax-linear:edit-2" size="14" color="#475569" />
|
||||||
|
<span>ویرایش مجدد اطلاعات</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="text-xs text-red-600 hover:text-red-700 flex items-center gap-1 py-1 px-2 mr-auto cursor-pointer transition-colors"
|
||||||
|
@click="onLogout"
|
||||||
|
>
|
||||||
|
<BasicIcon icon="vuesax-linear:logout" size="14" color="#dc2626" />
|
||||||
|
<span>خروج از حساب</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, reactive, ref, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||||
|
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||||
|
import BaseDatePicker from '@/components/BasicPacks/BaseDatePicker'
|
||||||
|
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
import { updateMe } from '@/api/me'
|
||||||
|
import { getToken } from '@/utils'
|
||||||
|
import { isValidPersian, requireNationalCode, required } from '@/utils/inputRules'
|
||||||
|
import { normalizeAuthFormFields } from '@/utils/persianDigits'
|
||||||
|
import { performUserLogout } from '@/utils/authSession'
|
||||||
|
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const isDesktop = useDesktopBreakpoint()
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const loading = ref(false)
|
||||||
|
const checkingStatus = ref(false)
|
||||||
|
const isEditing = ref(false)
|
||||||
|
|
||||||
|
const formState = reactive({
|
||||||
|
fname: '',
|
||||||
|
lname: '',
|
||||||
|
n_code: '',
|
||||||
|
birth_date: '',
|
||||||
|
city: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const modalWidth = computed(() => (isDesktop.value ? '460px' : '92vw'))
|
||||||
|
|
||||||
|
const isModalVisible = computed(() => {
|
||||||
|
if (!getToken()) return false
|
||||||
|
if (route.name === 'login' || route.name === 'tvMonitor' || route.name === 'aboutSoftware') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const info = userStore.userInfo
|
||||||
|
if (!info) return false
|
||||||
|
// 0 indicates unverified. 1 and 2 indicate verified customer.
|
||||||
|
return info.is_legal === 0 || !info.is_legal
|
||||||
|
})
|
||||||
|
|
||||||
|
const isPendingApproval = computed(() => {
|
||||||
|
const info = userStore.userInfo
|
||||||
|
if (!info) return false
|
||||||
|
// If national code is set and user is still unverified (is_legal === 0)
|
||||||
|
return Boolean(info.n_code && info.is_legal === 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
function populateFormFromUser() {
|
||||||
|
const info = userStore.userInfo
|
||||||
|
if (!info) return
|
||||||
|
formState.fname = info.fname || ''
|
||||||
|
formState.lname = info.lname || ''
|
||||||
|
formState.n_code = info.n_code || ''
|
||||||
|
formState.birth_date = info.birth_date || ''
|
||||||
|
formState.city = info.city || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => userStore.userInfo,
|
||||||
|
() => {
|
||||||
|
populateFormFromUser()
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
async function onSubmit() {
|
||||||
|
try {
|
||||||
|
await formRef.value?.validateFields()
|
||||||
|
loading.value = true
|
||||||
|
normalizeAuthFormFields(formState)
|
||||||
|
|
||||||
|
const payload: any = {
|
||||||
|
...userStore.userInfo,
|
||||||
|
fname: formState.fname,
|
||||||
|
lname: formState.lname,
|
||||||
|
n_code: formState.n_code,
|
||||||
|
birth_date: formState.birth_date,
|
||||||
|
city: formState.city,
|
||||||
|
}
|
||||||
|
|
||||||
|
const res: any = await updateMe(payload)
|
||||||
|
await userStore.getMyInfo()
|
||||||
|
|
||||||
|
const currentLegal = userStore.userInfo?.is_legal
|
||||||
|
if (currentLegal === 1 || currentLegal === 2) {
|
||||||
|
message.success('احراز هویت شما با موفقیت انجام و تأیید شد.')
|
||||||
|
isEditing.value = false
|
||||||
|
} else {
|
||||||
|
isEditing.value = false
|
||||||
|
message.info(res?.message || 'اطلاعات هویتی شما ثبت شد و در انتظار تأیید مدیریت قرار گرفت.')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMsg = error?.response?.data?.message || error?.message
|
||||||
|
if (errorMsg) {
|
||||||
|
message.error(errorMsg)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onCheckStatus() {
|
||||||
|
try {
|
||||||
|
checkingStatus.value = true
|
||||||
|
await userStore.getMyInfo()
|
||||||
|
const currentLegal = userStore.userInfo?.is_legal
|
||||||
|
if (currentLegal === 1 || currentLegal === 2) {
|
||||||
|
message.success('حساب کاربری شما تأیید شد!')
|
||||||
|
} else {
|
||||||
|
message.info('اطلاعات شما همچنان در صف بررسی و تایید مدیریت است.')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error('خطا در بررسی وضعیت. لطفاً مجدداً تلاش کنید.')
|
||||||
|
} finally {
|
||||||
|
checkingStatus.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onLogout() {
|
||||||
|
await performUserLogout()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.kyc-modal-content {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item) {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-form-item-label > label) {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import router from '@/router'
|
||||||
|
import { removeToken } from '@/utils'
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
import { useWalletStore } from '@/store/walletStore'
|
||||||
|
import { useWalletAssets } from '@/composables/useWalletAssets'
|
||||||
|
import { resetHomePageData } from '@/views/base/home/composables/useHomePageData'
|
||||||
|
import { resetWalletPageData } from '@/views/base/wallet/composables/useWalletPageData'
|
||||||
|
import { resetAddressPageData } from '@/views/base/address/composables/useAddressPageData'
|
||||||
|
import { resetTransactionsPageData } from '@/views/base/transactions/composables/useTransactionsPageData'
|
||||||
|
import { resetProductsPageData } from '@/views/base/products/composables/useProductsPageData'
|
||||||
|
|
||||||
|
export async function performUserLogout() {
|
||||||
|
try {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const walletStore = useWalletStore()
|
||||||
|
userStore.userInfo = undefined
|
||||||
|
walletStore.walletInfo = undefined
|
||||||
|
try {
|
||||||
|
const { clearWalletAssets } = useWalletAssets()
|
||||||
|
clearWalletAssets()
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
try { resetHomePageData() } catch {}
|
||||||
|
try { resetWalletPageData() } catch {}
|
||||||
|
try { resetAddressPageData() } catch {}
|
||||||
|
try { resetTransactionsPageData() } catch {}
|
||||||
|
try { resetProductsPageData() } catch {}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Logout cleanup error:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeToken()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await router.push({ name: 'login' })
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ interface BankDefinition {
|
|||||||
aliases: string[]
|
aliases: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const bankLogoModules = import.meta.glob<string>('@/assets/img/banks/*.{png,svg}', {
|
const bankLogoModules = import.meta.glob<string>('../assets/img/banks/*.{png,svg}', {
|
||||||
eager: true,
|
eager: true,
|
||||||
import: 'default',
|
import: 'default',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -186,6 +186,14 @@ function sumBoursecuHomeProductInventories(
|
|||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveBoursecuCopperUnit(unit?: string | null): string {
|
||||||
|
const trimmed = (unit ?? '').trim()
|
||||||
|
if (!trimmed || trimmed === 'گرم' || trimmed === 'gram' || trimmed === 'g') {
|
||||||
|
return 'کیلوگرم'
|
||||||
|
}
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
|
||||||
function findMetalTotalById(metalTotals: WalletMetalTotal[], metalId: number) {
|
function findMetalTotalById(metalTotals: WalletMetalTotal[], metalId: number) {
|
||||||
return metalTotals.find((metal) => metal.metal_id === metalId)
|
return metalTotals.find((metal) => metal.metal_id === metalId)
|
||||||
}
|
}
|
||||||
@@ -195,7 +203,8 @@ function formatBoursecuMetalRowValue(metal: WalletMetalTotal): string {
|
|||||||
if (metal.metal_id === METAL_GOLD) {
|
if (metal.metal_id === METAL_GOLD) {
|
||||||
return `${amount} عدد`
|
return `${amount} عدد`
|
||||||
}
|
}
|
||||||
return `${amount}${metal.unit ?? ''}`
|
const unit = metal.metal_id === METAL_COPPER ? resolveBoursecuCopperUnit(metal.unit) : (metal.unit ?? 'کیلوگرم')
|
||||||
|
return `${amount} ${unit}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatRialNumber(value: number): string {
|
function formatRialNumber(value: number): string {
|
||||||
@@ -237,8 +246,11 @@ function buildBoursecuCopperPopupLines(summary: {
|
|||||||
const metalTotals = summary.metal_totals ?? []
|
const metalTotals = summary.metal_totals ?? []
|
||||||
const copperMetal = findMetalTotalById(metalTotals, METAL_COPPER)
|
const copperMetal = findMetalTotalById(metalTotals, METAL_COPPER)
|
||||||
const shamshMetal = findMetalTotalById(metalTotals, METAL_GOLD)
|
const shamshMetal = findMetalTotalById(metalTotals, METAL_GOLD)
|
||||||
const weightUnit = copperMetal?.unit || 'کیلوگرم'
|
const weightUnit = resolveBoursecuCopperUnit(copperMetal?.unit)
|
||||||
const totalWeight = sumBoursecuHomeProductInventories(inventoryCards, countProducts)
|
const totalWeight =
|
||||||
|
copperMetal != null && Number(copperMetal.total_weight ?? 0) > 0
|
||||||
|
? Number(copperMetal.total_weight)
|
||||||
|
: sumBoursecuHomeProductInventories(inventoryCards, countProducts)
|
||||||
|
|
||||||
const katodCard = findBoursecuProductCard(inventoryCards, countProducts, isBoursecuKatodCard)
|
const katodCard = findBoursecuProductCard(inventoryCards, countProducts, isBoursecuKatodCard)
|
||||||
const maftolCard = findBoursecuProductCard(inventoryCards, countProducts, isBoursecuMaftolCard)
|
const maftolCard = findBoursecuProductCard(inventoryCards, countProducts, isBoursecuMaftolCard)
|
||||||
@@ -319,10 +331,10 @@ export function buildBoursecuHomeAssetRows(
|
|||||||
const countProducts = summary.count_products ?? []
|
const countProducts = summary.count_products ?? []
|
||||||
const metalTotals = summary.metal_totals ?? []
|
const metalTotals = summary.metal_totals ?? []
|
||||||
const copperMetal = findMetalTotalById(metalTotals, METAL_COPPER)
|
const copperMetal = findMetalTotalById(metalTotals, METAL_COPPER)
|
||||||
const weightUnit = copperMetal?.unit || 'کیلوگرم'
|
const weightUnit = resolveBoursecuCopperUnit(copperMetal?.unit)
|
||||||
const totalCopperWeight =
|
const totalCopperWeight =
|
||||||
copperMetal != null
|
copperMetal != null && Number(copperMetal.total_weight ?? 0) > 0
|
||||||
? Number(copperMetal.total_weight ?? 0)
|
? Number(copperMetal.total_weight)
|
||||||
: sumBoursecuHomeProductInventories(inventoryCards, countProducts)
|
: sumBoursecuHomeProductInventories(inventoryCards, countProducts)
|
||||||
|
|
||||||
const cash = Number(summary.cash_wallet ?? 0)
|
const cash = Number(summary.cash_wallet ?? 0)
|
||||||
@@ -387,7 +399,7 @@ export function resolveSummaryMetalWeight(
|
|||||||
|
|
||||||
/** متن توضیح مودال دارایی فلزی — بر اساس metal_totals سرور، بدون هاردکد طلا */
|
/** متن توضیح مودال دارایی فلزی — بر اساس metal_totals سرور، بدون هاردکد طلا */
|
||||||
export function formatMetalAssetsInfoText(metal: WalletMetalTotal): string {
|
export function formatMetalAssetsInfoText(metal: WalletMetalTotal): string {
|
||||||
const unit = metal.unit || 'گرم'
|
const unit = metal.metal_id === METAL_COPPER ? resolveBoursecuCopperUnit(metal.unit) : (metal.unit || 'کیلوگرم')
|
||||||
return `${metal.label}: مجموع کیف محصولات (${metal.product_weight} ${unit}) + موجودی کیف اصلی (${metal.melted_weight} ${unit})`
|
return `${metal.label}: مجموع کیف محصولات (${metal.product_weight} ${unit}) + موجودی کیف اصلی (${metal.melted_weight} ${unit})`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +522,7 @@ export function computeMetalRialValue(
|
|||||||
const metalId = Number(metal.metal_id ?? 0)
|
const metalId = Number(metal.metal_id ?? 0)
|
||||||
const unitPrice =
|
const unitPrice =
|
||||||
livePricesByMetal?.[metalId] ??
|
livePricesByMetal?.[metalId] ??
|
||||||
(metalId === METAL_GOLD ? liveGoldPrice : 0)
|
(metalId === METAL_COPPER || metalId === METAL_GOLD ? liveGoldPrice : 0)
|
||||||
|
|
||||||
return sum + weight * unitPrice
|
return sum + weight * unitPrice
|
||||||
}, 0)
|
}, 0)
|
||||||
@@ -534,7 +546,7 @@ export function formatInventoryCardValue(card: {
|
|||||||
display_type?: string
|
display_type?: string
|
||||||
}): string {
|
}): string {
|
||||||
const quantity = Number(card.quantity ?? 0)
|
const quantity = Number(card.quantity ?? 0)
|
||||||
const unit = card.unit ?? (card.display_type === 'count' ? 'عدد' : 'گرم')
|
const unit = card.unit ?? (card.display_type === 'count' ? 'عدد' : 'کیلوگرم')
|
||||||
const formatted =
|
const formatted =
|
||||||
card.display_type === 'count'
|
card.display_type === 'count'
|
||||||
? usePriceWithoutType(quantity, false)
|
? usePriceWithoutType(quantity, false)
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ export function isKilogramUnit(unit?: string | null) {
|
|||||||
return value === TRADE_WEIGHT_UNIT_KILOGRAM || value === 'kilogram' || value === 'kg'
|
return value === TRADE_WEIGHT_UNIT_KILOGRAM || value === 'kilogram' || value === 'kg'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** واحد نمایش از ستینگ — خالی یا نامعتبر = گرم */
|
/** واحد نمایش از ستینگ — در بورس مس پیشفرض همیشه کیلوگرم است */
|
||||||
export function resolveTradeWeightUnit(settingValue?: string | number | null) {
|
export function resolveTradeWeightUnit(settingValue?: string | number | null) {
|
||||||
|
if (!settingValue) {
|
||||||
|
return TRADE_WEIGHT_UNIT_KILOGRAM
|
||||||
|
}
|
||||||
return isKilogramUnit(settingValue?.toString()) ? TRADE_WEIGHT_UNIT_KILOGRAM : TRADE_WEIGHT_UNIT_GRAM
|
return isKilogramUnit(settingValue?.toString()) ? TRADE_WEIGHT_UNIT_KILOGRAM : TRADE_WEIGHT_UNIT_GRAM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,8 +175,10 @@ import {
|
|||||||
formatPortfolioProfitPercent,
|
formatPortfolioProfitPercent,
|
||||||
formatPortfolioProfitText,
|
formatPortfolioProfitText,
|
||||||
resolveAssetsRialValue,
|
resolveAssetsRialValue,
|
||||||
|
resolveBoursecuCopperUnit,
|
||||||
resolveMetalAssetsDisplayLabel,
|
resolveMetalAssetsDisplayLabel,
|
||||||
} from '@/utils/productAssets'
|
} from '@/utils/productAssets'
|
||||||
|
import { formatWeightAmount } from '@/utils/usePrice'
|
||||||
import type { WalletMetalTotal } from '@/models/walletAssets'
|
import type { WalletMetalTotal } from '@/models/walletAssets'
|
||||||
import { useAssetsPortfolioChart } from './composables/useAssetsPortfolioChart'
|
import { useAssetsPortfolioChart } from './composables/useAssetsPortfolioChart'
|
||||||
import assetsCopperBanner from '@/assets/img/assets-copper-banner.webp'
|
import assetsCopperBanner from '@/assets/img/assets-copper-banner.webp'
|
||||||
@@ -265,10 +267,13 @@ const profitClass = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const summaryItems = computed(() => {
|
const summaryItems = computed(() => {
|
||||||
const items = metalTotals.value.map((metal) => ({
|
const items = metalTotals.value.map((metal) => {
|
||||||
label: assetsMetalLabel(metal),
|
const unit = metal.metal_id === 3 ? resolveBoursecuCopperUnit(metal.unit) : (metal.unit || 'کیلوگرم')
|
||||||
value: `${metal.total_weight} ${metal.unit}`,
|
return {
|
||||||
}))
|
label: assetsMetalLabel(metal),
|
||||||
|
value: `${formatWeightAmount(Number(metal.total_weight ?? 0))} ${unit}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
items.push(
|
items.push(
|
||||||
{ label: 'کیف پول ریالی', value: usePrice(cashWallet.value) },
|
{ label: 'کیف پول ریالی', value: usePrice(cashWallet.value) },
|
||||||
{ label: 'ارزش ریالی مس', value: usePrice(metalRialValue.value) },
|
{ label: 'ارزش ریالی مس', value: usePrice(metalRialValue.value) },
|
||||||
@@ -287,10 +292,11 @@ const assetRows = computed(() => {
|
|||||||
const profit = portfolioInsight.value?.unrealized_profit_rial
|
const profit = portfolioInsight.value?.unrealized_profit_rial
|
||||||
const changeClass = profit == null || profit === 0 ? '' : profit > 0 ? 'is-profit' : 'is-loss'
|
const changeClass = profit == null || profit === 0 ? '' : profit > 0 ? 'is-profit' : 'is-loss'
|
||||||
const rows = metalTotals.value.flatMap((metal) => {
|
const rows = metalTotals.value.flatMap((metal) => {
|
||||||
|
const unit = metal.metal_id === 3 ? resolveBoursecuCopperUnit(metal.unit) : (metal.unit || 'کیلوگرم')
|
||||||
const head = {
|
const head = {
|
||||||
key: `metal-${metal.metal_id}`,
|
key: `metal-${metal.metal_id}`,
|
||||||
name: assetsMetalLabel(metal),
|
name: assetsMetalLabel(metal),
|
||||||
balance: `${metal.total_weight} ${metal.unit}`,
|
balance: `${formatWeightAmount(Number(metal.total_weight ?? 0))} ${unit}`,
|
||||||
rial: usePrice(metalRialValue.value),
|
rial: usePrice(metalRialValue.value),
|
||||||
change: pct || '—',
|
change: pct || '—',
|
||||||
changeClass,
|
changeClass,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import {
|
|||||||
formatPortfolioProfitPercent,
|
formatPortfolioProfitPercent,
|
||||||
formatPortfolioProfitText,
|
formatPortfolioProfitText,
|
||||||
resolveAssetsRialValue,
|
resolveAssetsRialValue,
|
||||||
|
resolveBoursecuCopperUnit,
|
||||||
} from '@/utils/productAssets'
|
} from '@/utils/productAssets'
|
||||||
import { formatWeightAmount, usePriceWithoutType } from '@/utils/usePrice'
|
import { formatWeightAmount, usePriceWithoutType } from '@/utils/usePrice'
|
||||||
import { getTradeBuyPrice, lastPrice } from '@/utils/price'
|
import { getTradeBuyPrice, lastPrice } from '@/utils/price'
|
||||||
@@ -76,7 +77,7 @@ const props = withDefaults(
|
|||||||
const { assetsSummary } = useWalletAssets()
|
const { assetsSummary } = useWalletAssets()
|
||||||
|
|
||||||
const portfolio = computed(() => assetsSummary.value?.portfolio)
|
const portfolio = computed(() => assetsSummary.value?.portfolio)
|
||||||
const weightUnit = computed(() => assetsSummary.value?.meta?.weight_unit || 'گرم')
|
const weightUnit = computed(() => resolveBoursecuCopperUnit(assetsSummary.value?.meta?.weight_unit))
|
||||||
|
|
||||||
const totalRial = computed(() =>
|
const totalRial = computed(() =>
|
||||||
Number(portfolio.value?.total_rial_value ?? assetsSummary.value?.total_rial_value ?? 0)
|
Number(portfolio.value?.total_rial_value ?? assetsSummary.value?.total_rial_value ?? 0)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import {
|
|||||||
formatPortfolioProfitLabel,
|
formatPortfolioProfitLabel,
|
||||||
formatPortfolioProfitPercent,
|
formatPortfolioProfitPercent,
|
||||||
formatPortfolioProfitText,
|
formatPortfolioProfitText,
|
||||||
|
resolveBoursecuCopperUnit,
|
||||||
} from '@/utils/productAssets'
|
} from '@/utils/productAssets'
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
@@ -55,7 +56,7 @@ const props = withDefaults(
|
|||||||
const { assetsSummary } = useWalletAssets()
|
const { assetsSummary } = useWalletAssets()
|
||||||
|
|
||||||
const portfolio = computed(() => assetsSummary.value?.portfolio)
|
const portfolio = computed(() => assetsSummary.value?.portfolio)
|
||||||
const weightUnit = computed(() => assetsSummary.value?.meta?.weight_unit || 'گرم')
|
const weightUnit = computed(() => resolveBoursecuCopperUnit(assetsSummary.value?.meta?.weight_unit))
|
||||||
|
|
||||||
const hasProfitInsight = computed(() => portfolio.value?.unrealized_profit_rial != null)
|
const hasProfitInsight = computed(() => portfolio.value?.unrealized_profit_rial != null)
|
||||||
const profitTone = computed(() => formatPortfolioProfitLabel(portfolio.value?.unrealized_profit_rial))
|
const profitTone = computed(() => formatPortfolioProfitLabel(portfolio.value?.unrealized_profit_rial))
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { getBoursecuActionIconSrc } from '@/utils/config'
|
|||||||
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||||
import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare'
|
import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare'
|
||||||
import { normalizeAuthFormFields } from '@/utils/persianDigits'
|
import { normalizeAuthFormFields } from '@/utils/persianDigits'
|
||||||
|
import { performUserLogout } from '@/utils/authSession'
|
||||||
import { useSettingsStore } from '@/store/settingsStore'
|
import { useSettingsStore } from '@/store/settingsStore'
|
||||||
import { useTrackingSystemsFeature } from '@/composables/useTrackingSystemsFeature'
|
import { useTrackingSystemsFeature } from '@/composables/useTrackingSystemsFeature'
|
||||||
import { openGoldDayCalculator } from '@/composables/useGoldDayCalculator'
|
import { openGoldDayCalculator } from '@/composables/useGoldDayCalculator'
|
||||||
@@ -300,18 +301,8 @@ export function useUserPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function logout() {
|
async function logout() {
|
||||||
userStore.userInfo = undefined
|
|
||||||
walletStore.walletInfo = undefined
|
|
||||||
clearWalletAssets()
|
|
||||||
resetHomePageData()
|
|
||||||
resetWalletPageData()
|
|
||||||
resetAddressPageData()
|
|
||||||
resetTransactionsPageData()
|
|
||||||
resetProductsPageData()
|
|
||||||
removeToken()
|
|
||||||
showLogoutConfirm.value = false
|
showLogoutConfirm.value = false
|
||||||
await router.push({ name: 'login' })
|
await performUserLogout()
|
||||||
window.location.reload()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -68,11 +68,11 @@
|
|||||||
<div class="text-black">مقدار {{ getMetalTypeTradeLabel() }} مبادله شده:</div>
|
<div class="text-black">مقدار {{ getMetalTypeTradeLabel() }} مبادله شده:</div>
|
||||||
<div :class="item.gold >= 0? 'text-green-600' : 'text-red-600'" class="flex text-xs items-center">
|
<div :class="item.gold >= 0? 'text-green-600' : 'text-red-600'" class="flex text-xs items-center">
|
||||||
<div class="text-base ml-1 number-ltr">{{ formatGoldAmount(item.gold) }}</div>
|
<div class="text-base ml-1 number-ltr">{{ formatGoldAmount(item.gold) }}</div>
|
||||||
گرم
|
{{ getWeightUnit() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.order" class="flex justify-between mb-3">
|
<div v-if="item.order" class="flex justify-between mb-3">
|
||||||
<div class="text-black">قیمت هر گرم {{ getTradePriceUnitLabel() }} حین معامله:</div>
|
<div class="text-black">قیمت هر {{ getWeightUnit() }} {{ getTradePriceUnitLabel() }} حین معامله:</div>
|
||||||
<div class="flex text-xs items-center number-ltr">
|
<div class="flex text-xs items-center number-ltr">
|
||||||
<div class="text-base ml-1">{{ usePriceWithoutType(item.order?.price?? 0) }}</div>
|
<div class="text-base ml-1">{{ usePriceWithoutType(item.order?.price?? 0) }}</div>
|
||||||
ریال
|
ریال
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
v-if="settingsStore.isReferralFieldVisible"
|
v-if="settingsStore.isReferralFieldVisible || disableForm"
|
||||||
:rules="referralIdRules"
|
:rules="referralIdRules"
|
||||||
:class="variant === 'desktop' ? '' : 'home_appear_fade_in_down'"
|
:class="variant === 'desktop' ? '' : 'home_appear_fade_in_down'"
|
||||||
label="کد معرف"
|
label="کد معرف"
|
||||||
|
|||||||
@@ -56,7 +56,9 @@
|
|||||||
درباره مس
|
درباره مس
|
||||||
</button>
|
</button>
|
||||||
<span class="dauth-form-links__sep" aria-hidden="true">|</span>
|
<span class="dauth-form-links__sep" aria-hidden="true">|</span>
|
||||||
<span class="dauth-form-links__text">پشتیبانی</span>
|
<button type="button" class="dauth-form-links__link" @click="onSupportClick">
|
||||||
|
پشتیبانی
|
||||||
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="dauth-page-footer__status">
|
<div class="dauth-page-footer__status">
|
||||||
<span class="dauth-page-footer__status-icon" aria-hidden="true">
|
<span class="dauth-page-footer__status-icon" aria-hidden="true">
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ function handleSuccessDismiss() {
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
v-if="settingsStore.isReferralFieldVisible"
|
v-if="settingsStore.isReferralFieldVisible || disableForm"
|
||||||
:rules="referralIdRules"
|
:rules="referralIdRules"
|
||||||
label="کد معرف"
|
label="کد معرف"
|
||||||
name="referral_id"
|
name="referral_id"
|
||||||
|
|||||||
@@ -1191,18 +1191,19 @@
|
|||||||
/* ─── موبایل (<768): لوگو + کارت + پشتیبانی، وسطچین عمودی ─── */
|
/* ─── موبایل (<768): لوگو + کارت + پشتیبانی، وسطچین عمودی ─── */
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.dauth.dauth--boursecu {
|
.dauth.dauth--boursecu {
|
||||||
/* 100vh آیفون پشت نوار سافاری است؛ اسکرول داخل 100dvh تا دکمهٔ ثبتنام برسد */
|
/* 100vh آیفون پشت نوار سافاری است؛ اسکرول روان در ارتفاع واقعی بدون قفل شدن */
|
||||||
height: 100dvh;
|
height: auto !important;
|
||||||
max-height: 100dvh;
|
min-height: 100dvh !important;
|
||||||
min-height: 0;
|
max-height: none !important;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden !important;
|
||||||
overflow-y: auto;
|
overflow-y: auto !important;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch !important;
|
||||||
|
touch-action: pan-y !important;
|
||||||
|
overscroll-behavior-y: auto !important;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
justify-content: safe center;
|
padding: 16px 0 calc(36px + env(safe-area-inset-bottom, 0px));
|
||||||
padding: 12px 0 calc(24px + env(safe-area-inset-bottom, 0px));
|
|
||||||
|
|
||||||
.dauth-page-logo {
|
.dauth-page-logo {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -1367,14 +1368,18 @@
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* شناور پایین روی دکمهٔ ثبتنام مینشیند؛ زیر کارت بیاید */
|
/* دکمه شناور پشتیبانی در موبایل: زیر کارت و بالاتر از لینکهای فوتر نمایش داده شود و شناور روی فرم نیفتد */
|
||||||
.dauth-form-support--fab {
|
.dauth-form-support--fab {
|
||||||
position: static !important;
|
position: static !important;
|
||||||
|
display: inline-flex !important;
|
||||||
top: auto !important;
|
top: auto !important;
|
||||||
left: auto !important;
|
left: auto !important;
|
||||||
|
right: auto !important;
|
||||||
|
bottom: auto !important;
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
margin: 4px 0 0;
|
margin: 14px auto 0 !important;
|
||||||
|
z-index: 10 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,16 @@ onUnmounted(() => {
|
|||||||
/* موبایل لاگین: تمامعرض — بدون فریم ۴۲۶ و حاشیه سفید دوطرف */
|
/* موبایل لاگین: تمامعرض — بدون فریم ۴۲۶ و حاشیه سفید دوطرف */
|
||||||
@media screen and (max-width: 767.98px) {
|
@media screen and (max-width: 767.98px) {
|
||||||
html.login-page-active {
|
html.login-page-active {
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
-webkit-overflow-scrolling: touch !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 100dvh !important;
|
||||||
|
overscroll-behavior-y: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
justify-content: unset !important;
|
justify-content: unset !important;
|
||||||
@@ -71,6 +81,9 @@ onUnmounted(() => {
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
background: #0b192e !important;
|
background: #0b192e !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 100dvh !important;
|
||||||
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktopConfig,
|
.desktopConfig,
|
||||||
@@ -90,12 +103,12 @@ onUnmounted(() => {
|
|||||||
.app-main,
|
.app-main,
|
||||||
.login-page-root,
|
.login-page-root,
|
||||||
.dauth.dauth--boursecu {
|
.dauth.dauth--boursecu {
|
||||||
min-height: 0 !important;
|
min-height: 100dvh !important;
|
||||||
height: 100dvh !important;
|
height: auto !important;
|
||||||
max-height: 100dvh !important;
|
max-height: none !important;
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch !important;
|
||||||
background: #0b192e !important;
|
background: #0b192e !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user