fixed card display &...

This commit is contained in:
2026-07-01 12:23:09 +02:00
44 changed files with 935 additions and 281 deletions
+14
View File
@@ -112,6 +112,7 @@ import {
import { useDraggableFloatingButton } from '@/utils/useDraggableFloatingButton'
import AppCreditLink from '@/components/AppCreditLink.vue'
import { useRoute } from 'vue-router'
import { recoverMobileViewportAfterAuth } from '@/layouts/composables/useMobileScrollReset'
const VersionUpdateModal = defineAsyncComponent(
() => import('@/modules/version/VersionUpdateModal.vue')
@@ -251,13 +252,26 @@ watch(showSplash, (visible) => {
}
})
watch(
() => route.name,
(toName, fromName) => {
if (isDesktop.value) return
if (fromName === 'login' || toName === 'home') {
recoverMobileViewportAfterAuth()
}
},
{ flush: 'post' },
)
</script>
<style lang="less">
/* ارتفاع نوار اعتبار + safe-area؛ منوی پایین با bottom: var(--app-credit-footer-offset) بالاتر از این نوار می‌نشیند */
:root {
--app-credit-bar-inner: 28px;
--app-safe-top: env(safe-area-inset-top, 0px);
--app-safe-bottom: env(safe-area-inset-bottom, 0px);
--app-home-header-offset: calc(4.25rem + var(--app-safe-top));
--app-credit-footer-offset: calc(var(--app-credit-bar-inner) + var(--app-safe-bottom));
--app-nav-height: 80px;
--app-bottom-chrome-height: calc(var(--app-nav-height) + var(--app-credit-footer-offset));
+2
View File
@@ -11,6 +11,7 @@ import { useUserStore } from '@/store/userStore'
import { getToken } from '@/utils'
import '@/utils/useDateTime'
import { initPriceRealtimeChannel } from '@/utils/priceSocket'
import { initMobileScrollRestoration } from '@/layouts/composables/useMobileScrollReset'
type BootWindow = Window & {
__BOOT_TIMEOUT__?: number
@@ -122,6 +123,7 @@ export async function bootstrapApp() {
}
setupRouterGuards()
initMobileScrollRestoration()
app.mount('#app')
+8 -1
View File
@@ -7,7 +7,7 @@ import { useFundUserStore } from '@/store/fundStore'
import type { NavigationGuardNext } from 'vue-router'
import { useAuthStore } from '@/store/useAuthStore'
import { getConfigApp } from '@/utils/config'
import { getFingerPosition, setFingerPosition } from '@/app/authBoot'
import { recoverMobileViewportAfterAuth } from '@/layouts/composables/useMobileScrollReset'
const PUBLIC_ROUTE_NAMES = ['login', '404', 'educate', 'aboutUs'] as const
@@ -119,6 +119,13 @@ export function setupRouterGuards() {
}
})
router.afterEach((to, from) => {
if (document.documentElement.classList.contains('app-layout--desktop')) return
if (from.name === 'login') {
recoverMobileViewportAfterAuth()
}
})
watch(currentRoute, async () => (userStore.userInfo ? '' : await start()))
}
@@ -0,0 +1,125 @@
import { nextTick } from 'vue'
import router from '@/router'
let recoveryTimers: ReturnType<typeof setTimeout>[] = []
function clearRecoveryTimers() {
recoveryTimers.forEach(clearTimeout)
recoveryTimers = []
}
function blurFocusedField() {
const active = document.activeElement
if (active instanceof HTMLElement && active !== document.body) {
active.blur()
}
}
function clearBodyScrollLock() {
document.body.classList.remove('van-overflow-hidden')
document.body.style.removeProperty('position')
document.body.style.removeProperty('top')
document.body.style.removeProperty('left')
document.body.style.removeProperty('right')
document.body.style.removeProperty('width')
document.body.style.removeProperty('height')
document.body.style.removeProperty('min-height')
document.body.style.removeProperty('overflow')
document.body.style.removeProperty('transform')
document.documentElement.style.removeProperty('overflow')
document.documentElement.style.removeProperty('height')
document.documentElement.style.removeProperty('transform')
const appRoot = document.getElementById('app')
if (appRoot) {
appRoot.style.removeProperty('position')
appRoot.style.removeProperty('top')
appRoot.style.removeProperty('height')
appRoot.style.removeProperty('min-height')
appRoot.style.removeProperty('overflow')
appRoot.style.removeProperty('transform')
}
}
function resetAllScrollPositions() {
window.scrollTo(0, 0)
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
document
.querySelectorAll('.main-with-bottom-chrome-inner, .login-page, .login-page-root')
.forEach((node) => {
if (node instanceof HTMLElement) {
node.scrollTop = 0
}
})
}
function kickViewportReflow() {
window.dispatchEvent(new Event('resize'))
if (window.visualViewport) {
window.scrollTo(0, 0)
}
}
function runViewportRecovery() {
clearBodyScrollLock()
resetAllScrollPositions()
kickViewportReflow()
}
function scheduleViewportRecovery() {
clearRecoveryTimers()
runViewportRecovery()
nextTick(() => {
requestAnimationFrame(() => {
runViewportRecovery()
for (const delay of [50, 150, 300, 500]) {
recoveryTimers.push(window.setTimeout(runViewportRecovery, delay))
}
})
})
}
/** Reset scroll offsets after navigation — fixes iOS safe-area drift. */
export function resetMobileAppScroll() {
runViewportRecovery()
nextTick(() => {
requestAnimationFrame(runViewportRecovery)
})
}
export async function dismissMobileKeyboard() {
blurFocusedField()
await new Promise((resolve) => window.setTimeout(resolve, 120))
}
export async function prepareMobileViewportForAppShell() {
blurFocusedField()
resetAllScrollPositions()
await dismissMobileKeyboard()
}
/** Full recovery after auth — runs after home shell is mounted. */
export function recoverMobileViewportAfterAuth() {
scheduleViewportRecovery()
}
export const resetMobileAppScrollAfterLogin = recoverMobileViewportAfterAuth
export async function completeLoginNavigation() {
await prepareMobileViewportForAppShell()
await router.push({ name: 'home' })
await router.isReady()
recoverMobileViewportAfterAuth()
}
export function initMobileScrollRestoration() {
if (typeof history !== 'undefined' && 'scrollRestoration' in history) {
history.scrollRestoration = 'manual'
}
}
+21 -5
View File
@@ -1,9 +1,9 @@
<template>
<div>
<div class="mobile-app-layout">
<div :class="['main-with-bottom-chrome', { 'main-with-bottom-chrome--no-nav': hideMenu }]">
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'">
<component :is="Component" class="main-with-bottom-chrome-inner" />
<router-view v-slot="{ Component, route: activeRoute }">
<transition :name="activeRoute.meta.transition || 'fade'">
<component :is="Component" :key="activeRoute.fullPath" class="main-with-bottom-chrome-inner" />
</transition>
</router-view>
</div>
@@ -19,13 +19,16 @@
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { computed, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useConfigStore } from '@/store/configStore'
import { getProjectMobileNavLogo, isBoursecuProject } from '@/utils/config'
import { useAppNavigation } from '../composables/useAppNavigation'
import { recoverMobileViewportAfterAuth, resetMobileAppScroll } from '../composables/useMobileScrollReset'
import MobileBottomNav from './MobileBottomNav.vue'
const config = useConfigStore()
const route = useRoute()
const isBoursecu = isBoursecuProject()
const navLogo = computed(() => getProjectMobileNavLogo(config.config?.avatar))
@@ -34,6 +37,19 @@ const {
navItemClass,
navigateTo,
} = useAppNavigation()
watch(
() => route.fullPath,
() => {
resetMobileAppScroll()
},
{ flush: 'post' },
)
onMounted(() => {
resetMobileAppScroll()
recoverMobileViewportAfterAuth()
})
</script>
<style lang="less" src="./mobile-layout.less"></style>
+14 -2
View File
@@ -131,8 +131,16 @@
opacity: 0;
}
.mobile-app-layout {
height: 100%;
max-height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.main-with-bottom-chrome {
height: calc(100vh - var(--app-bottom-chrome-height, 108px));
height: calc(100dvh - var(--app-bottom-chrome-height, 108px));
overflow: hidden;
display: flex;
flex-direction: column;
@@ -148,7 +156,11 @@
overscroll-behavior-y: contain;
}
.main-with-bottom-chrome-inner--contained {
overflow: hidden;
}
.main-with-bottom-chrome--no-nav,
.main-with-bottom-chrome--no-nav .main-with-bottom-chrome-inner {
height: calc(100vh - var(--app-credit-footer-offset, 28px));
height: calc(100dvh - var(--app-credit-footer-offset, 28px));
}
+27
View File
@@ -5,6 +5,33 @@
*/
html:not(.app-layout--desktop) {
html,
body,
#app {
min-height: 0;
height: 100dvh;
max-height: 100dvh;
overflow: hidden;
}
body {
overscroll-behavior: none;
}
.desktopConfig,
.app-main {
min-height: 0;
height: 100dvh;
max-height: 100dvh;
overflow: hidden;
}
.app-main > * {
height: 100%;
max-height: 100%;
overflow: hidden;
}
/* Teleport to body — dock bar must span viewport, not shrink to content */
.app-dock-add-bar,
.wallet-card-slider {
+153 -5
View File
@@ -1,10 +1,35 @@
/* Shared mobile page chrome */
/* Shared mobile page chrome (inner pages — not home .app-header) */
:root {
--mpage-header-bar: 2.75rem;
--mpage-header-offset: calc(4rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
--mpage-subheader-offset: calc(var(--mpage-header-bar) + var(--app-safe-top, env(safe-area-inset-top, 0px)));
--mpage-content-with-subheader: calc(6.5rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
--mpage-scroll-max-height: calc(100dvh - 50px - var(--app-safe-top, env(safe-area-inset-top, 0px)));
--mobile-info-support-block: calc(5rem + var(--app-safe-bottom, env(safe-area-inset-bottom, 0px)));
}
/* Fixed top bar: background fills under status bar; content sits below safe area */
.mobile-top-chrome {
top: 0;
box-sizing: border-box;
padding-top: calc(0.75rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
}
.mobile-top-chrome.h-12,
.mobile-top-chrome.min-h-12 {
height: auto;
min-height: calc(3rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
}
.mpage-header {
display: flex;
align-items: center;
gap: 0.5rem;
top: 0;
box-sizing: border-box;
padding: 0.75rem;
padding-top: calc(0.75rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
background: #fff;
color: @App-primary-color;
font-size: 1rem;
@@ -38,7 +63,7 @@
}
.mpage-subheader {
top: 2.75rem;
top: var(--mpage-subheader-offset);
z-index: 1999;
padding: 0.75rem;
background: #fff;
@@ -47,14 +72,137 @@
}
.mpage-content {
padding-top: 4rem;
padding-top: var(--mpage-header-offset);
}
.mpage-content--with-subheader {
padding-top: 6.5rem;
padding-top: var(--mpage-content-with-subheader);
}
.mpage-scroll {
overflow-y: auto;
max-height: calc(100vh - 50px);
max-height: var(--mpage-scroll-max-height);
}
.mobile-page-body--pt10 {
padding-top: calc(2.5rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
}
.mobile-page-body--pt15 {
padding-top: calc(3.75rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
}
.mobile-page-body--pt20 {
padding-top: calc(5rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
}
/* Info pages (about, educate): fixed shell, scroll inside content box only */
.mobile-info-page {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
max-height: 100%;
overflow: hidden;
}
.mobile-info-page__header {
z-index: 1999;
}
/* Decorative page background — fixed to viewport, never scrolls on iOS */
.mobile-page-bg,
.mobile-info-page__bg {
pointer-events: none;
position: fixed;
z-index: 0;
top: 0;
left: 0;
right: 0;
bottom: var(--app-bottom-chrome-height, 108px);
overflow: hidden;
max-width: 100vw;
box-sizing: border-box;
img {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: auto;
}
}
.mobile-page-bg--no-nav,
.mobile-info-page__bg {
bottom: var(--app-credit-footer-offset, 28px);
}
.mobile-page-bg--cover {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
html:not(.app-layout--desktop) {
@media screen and (min-width: 426px) and (max-width: 767.98px) {
.mobile-page-bg,
.mobile-info-page__bg {
width: 426px;
max-width: 426px;
left: 50%;
right: auto;
transform: translateX(-50%);
}
}
}
.mobile-info-page__main {
position: relative;
z-index: 1;
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
padding-top: calc(var(--mpage-header-offset) + 1rem);
padding-right: 2.5rem;
padding-left: 2.5rem;
padding-bottom: calc(
var(--mobile-info-support-block, 5rem) + var(--app-credit-footer-offset, 28px) + 1.25rem
);
pointer-events: none;
> * {
pointer-events: auto;
}
}
.mobile-info-page__support {
bottom: var(--app-credit-footer-offset, 28px);
box-sizing: border-box;
padding: 1rem 2.5rem;
padding-bottom: calc(1rem + var(--app-safe-bottom, env(safe-area-inset-bottom, 0px)));
background: #fff;
box-shadow: 0 -2px 8px rgba(15, 23, 42, 0.06);
z-index: 1998;
}
.mobile-content-box {
display: flex;
flex-direction: column;
flex: 1 1 0;
min-height: 0;
overflow: hidden;
margin-bottom: 0.75rem;
}
.mobile-content-box__scroll {
flex: 1 1 0;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
overscroll-behavior-y: contain;
}
@@ -1,13 +1,16 @@
<template>
<div
:class="variant === 'desktop'
? (isBoursecu ? 'dabout-content--boursecu' : 'dabout-content--default')
: (isBoursecuProject()
? 'mt-5 border-solid border-2 border-primary text-primary rounded-[12px] p-4 text-xs leading-5'
: 'bg-primary text-white rounded-[12px] p-4 text-xs leading-5')"
class="dabout-content"
:class="[
variant === 'desktop'
? (isBoursecu ? 'dabout-content--boursecu' : 'dabout-content--default')
: (isBoursecuProject()
? 'border-solid border-2 border-primary text-primary rounded-[12px] p-4 text-xs leading-5 mobile-content-box'
: 'bg-primary text-white rounded-[12px] p-4 text-xs leading-5 mobile-content-box'),
'dabout-content',
]"
>
<template v-for="(section, sectionIndex) in aboutSections" :key="sectionIndex">
<div :class="variant !== 'desktop' ? 'mobile-content-box__scroll' : undefined">
<template v-for="(section, sectionIndex) in aboutSections" :key="sectionIndex">
<div
v-if="sectionIndex > 0"
:class="variant === 'desktop' ? 'dabout-content__divider' : 'w-full h-1px my-3 bg-tertiary'"
@@ -46,6 +49,7 @@
v-if="variant !== 'desktop'"
class="w-full h-1px my-3 bg-tertiary"
></div>
</div>
</div>
</template>
@@ -1,5 +1,5 @@
<template>
<div :class="variant === 'desktop' ? 'dabout-support' : 'wrap-fix bottom-0 px-10 pb-3rem flex gap-2 bg-white'">
<div :class="variant === 'desktop' ? 'dabout-support' : 'wrap-fix mobile-info-page__support flex gap-2'">
<a
class="w-full"
:href="supportHref"
+9
View File
@@ -20,5 +20,14 @@ useAboutPage()
.about-page-root {
min-height: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
</style>
<style lang="less">
.about-page-root.main-with-bottom-chrome-inner {
overflow: hidden;
}
</style>
@@ -1,24 +1,18 @@
<template>
<div>
<div class="bg-white wrap-fix z-200001 py-3 px-3 text-primary font-bold drop-shadow flex">
<div class="mobile-info-page">
<div class="mobile-info-page__bg" aria-hidden="true">
<img :src="img" alt="" />
</div>
<div class="bg-white wrap-fix mobile-top-chrome mobile-info-page__header px-3 pb-3 text-primary font-bold drop-shadow flex">
<div class="cursor-pointer" @click="router.back()">
<BasicIcon icon="vuesax-linear:arrow-right-1" />
</div>
<div class="mr-2">درباره ما</div>
</div>
<div class="pt-20">
<div>
<img :src="img" alt="" class="mt-2 w-full absolute bottom-0" />
</div>
<div class="text-justify text-base z-200000 relative">
<div class="px-10">
<div class="overflow-y-auto h-65vh pb-20">
<AboutSectionsContent variant="mobile" />
</div>
</div>
<AboutSupportActions variant="mobile" />
</div>
<div class="mobile-info-page__main">
<AboutSectionsContent variant="mobile" />
</div>
<AboutSupportActions variant="mobile" />
</div>
</template>
@@ -2,41 +2,43 @@
<div
:class="variant === 'desktop'
? 'daboutus-content'
: 'mt-5 text-5 border-solid border-2 border-[#093D51] text-[#093D51] rounded-[12px] p-4'"
: 'text-5 border-solid border-2 border-[#093D51] text-[#093D51] rounded-[12px] p-4 mobile-content-box'"
>
<img
v-if="variant !== 'desktop'"
:src="config.config?.avatar"
class="mb-2 mt-5 w-10rem"
style="justify-self: center"
alt=""
/>
<div :class="variant !== 'desktop' ? 'mobile-content-box__scroll' : undefined">
<img
v-if="variant !== 'desktop'"
:src="config.config?.avatar"
class="mb-2 mt-5 w-10rem"
style="justify-self: center"
alt=""
/>
<div :class="variant === 'desktop' ? 'daboutus-content__body' : 'text-sm leading-7 space-y-2'">
<h2 :class="variant === 'desktop' ? 'daboutus-content__title' : 'text-base font-bold mb-3'">درباره ما</h2>
<div :class="variant === 'desktop' ? 'daboutus-content__body' : 'text-sm leading-7 space-y-2'">
<h2 :class="variant === 'desktop' ? 'daboutus-content__title' : 'text-base font-bold mb-3'">درباره ما</h2>
<div :class="variant === 'desktop' ? 'daboutus-content__paragraph' : 'mt-3'">
گروسی گالری بستری امن و آنلاین برای خرید و فروش طلای آبشده فراهم کرده تا کاربران بتوانند سرمایه‌های خرد خود را به راحتی پس‌انداز و مدیریت کنند.
<div :class="variant === 'desktop' ? 'daboutus-content__paragraph' : 'mt-3'">
گروسی گالری بستری امن و آنلاین برای خرید و فروش طلای آبشده فراهم کرده تا کاربران بتوانند سرمایه‌های خرد خود را به راحتی پس‌انداز و مدیریت کنند.
</div>
<div :class="variant === 'desktop' ? 'daboutus-content__paragraph' : 'mt-3'">
هدف ما ایجاد تجربه‌ای سریع، شفاف و کاربرپسند در معاملات طلای آبشده است.
</div>
</div>
<div :class="variant === 'desktop' ? 'daboutus-content__paragraph' : 'mt-3'">
هدف ما ایجاد تجربه‌ای سریع، شفاف و کاربرپسند در معاملات طلای آبشده است.
</div>
</div>
<div :class="variant === 'desktop' ? 'daboutus-content__enamad' : ''" style="justify-self: center">
<a
referrerpolicy="origin"
target="_blank"
href="https://trustseal.enamad.ir/?id=710578&Code=DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
>
<img
<div :class="variant === 'desktop' ? 'daboutus-content__enamad' : ''" style="justify-self: center">
<a
referrerpolicy="origin"
src="https://trustseal.enamad.ir/logo.aspx?id=710578&Code=DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
alt="enamad"
style="cursor:pointer"
code="DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
/>
</a>
target="_blank"
href="https://trustseal.enamad.ir/?id=710578&Code=DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
>
<img
referrerpolicy="origin"
src="https://trustseal.enamad.ir/logo.aspx?id=710578&Code=DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
alt="enamad"
style="cursor:pointer"
code="DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
/>
</a>
</div>
</div>
</div>
</template>
+3
View File
@@ -20,5 +20,8 @@ useAboutUsPage()
.about-us-page-root {
min-height: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
</style>
@@ -1,22 +1,16 @@
<template>
<div>
<div class="wrap-fix z-1 bg-white py-3 px-3 text-black font-bold drop-shadow flex">
<div class="mobile-info-page">
<div class="mobile-info-page__bg" aria-hidden="true">
<img :src="img" alt="" />
</div>
<div class="wrap-fix mobile-top-chrome mobile-info-page__header bg-white px-3 pb-3 text-black font-bold drop-shadow flex">
<div class="cursor-pointer" @click="router.back()">
<BasicIcon icon="vuesax-linear:arrow-right-1" />
</div>
<div class="mr-2">درباره ما</div>
</div>
<div class="pt-20">
<div>
<img :src="img" alt="" class="mt-2 w-full absolute bottom-0" />
</div>
<div class="text-justify text-base z-200000 relative">
<div class="px-10">
<div class="overflow-y-auto h-65vh pb-20 border-[#093D51]">
<AboutUsContentPanel variant="mobile" />
</div>
</div>
</div>
<div class="mobile-info-page__main">
<AboutUsContentPanel variant="mobile" />
</div>
</div>
</template>
+100 -28
View File
@@ -2,28 +2,73 @@
<a-form
ref="formRef"
:model="address"
:class="variant === 'desktop' ? 'daddr-form' : '!mt-5'"
:class="variant === 'desktop' ? 'daddr-form' : 'address-form-mobile'"
name="address-form"
>
<div :class="variant === 'desktop' ? 'daddr-form__fields' : 'flex flex-col items-center'">
<div
v-if="variant !== 'desktop'"
class="font-bold text-lg mb-2"
>
آدرس
<template v-if="variant === 'desktop'">
<div class="daddr-form__fields">
<div class="daddr-form__grid">
<a-form-item label="استان" name="province" :rules="[required()]">
<BasicSelect
v-model:value="address.province"
:items="provinceList"
value-name="title"
key-name="title"
size="large"
@change="changeProvince"
/>
</a-form-item>
<a-form-item label="شهر" name="city" :rules="[required()]">
<BasicSelect
v-model:value="address.city"
:disabled="!cityList"
value-name="title"
:items="cityList"
key-name="title"
size="large"
/>
</a-form-item>
<a-form-item label="کد پستی" name="zip" :rules="[required()]">
<BasicInput
v-model:value="address.zip"
placeholder="کد پستی را وارد نمایید"
size="large"
/>
</a-form-item>
<a-form-item label="تلفن تماس" name="phone" :rules="[required()]">
<BasicInput
v-model:value="address.phone"
placeholder="تلفن تماس را وارد نمایید"
size="large"
/>
</a-form-item>
<a-form-item
label="آدرس کامل"
name="address"
:rules="[required()]"
class="daddr-form__address-item"
>
<BasicTextArea
v-model:value="address.address"
placeholder="آدرس کامل را وارد نمایید"
/>
</a-form-item>
<BasicButton class="daddr-form__submit" type="primary" @click="handleOk">
{{ editId ? 'ویرایش آدرس' : 'ثبت آدرس' }}
</BasicButton>
</div>
</div>
<div
:class="variant === 'desktop'
? 'daddr-form__grid'
: 'flex flex-col gap-4 pb-20 w-full h-60vh overflow-y-scroll'"
>
</template>
<template v-else>
<div class="address-form-mobile__title">آدرس</div>
<div class="address-form-mobile__scroll">
<a-form-item label="استان" name="province" :rules="[required()]">
<BasicSelect
v-model:value="address.province"
:items="provinceList"
value-name="title"
key-name="title"
:size="variant === 'desktop' ? 'large' : undefined"
@change="changeProvince"
/>
</a-form-item>
@@ -34,43 +79,33 @@
value-name="title"
:items="cityList"
key-name="title"
:size="variant === 'desktop' ? 'large' : undefined"
/>
</a-form-item>
<a-form-item label="کد پستی" name="zip" :rules="[required()]">
<BasicInput
v-model:value="address.zip"
placeholder="کد پستی را وارد نمایید"
:size="variant === 'desktop' ? 'large' : undefined"
/>
</a-form-item>
<a-form-item label="تلفن تماس" name="phone" :rules="[required()]">
<BasicInput
v-model:value="address.phone"
placeholder="تلفن تماس را وارد نمایید"
:size="variant === 'desktop' ? 'large' : undefined"
/>
</a-form-item>
<a-form-item
label="آدرس کامل"
name="address"
:rules="[required()]"
:class="variant === 'desktop' ? 'daddr-form__address-item' : ''"
>
<a-form-item label="آدرس کامل" name="address" :rules="[required()]">
<BasicTextArea
v-model:value="address.address"
placeholder="آدرس کامل را وارد نمایید"
/>
</a-form-item>
<BasicButton
:class="variant === 'desktop' ? 'daddr-form__submit' : '!text-white px-3 pb-4rem'"
type="primary"
@click="handleOk"
>
</div>
<div class="address-form-mobile__footer">
<BasicButton class="w-full !text-white" type="primary" @click="handleOk">
{{ editId ? 'ویرایش آدرس' : 'ثبت آدرس' }}
</BasicButton>
</div>
</div>
</template>
</a-form>
</template>
@@ -99,3 +134,40 @@ const {
handleOk,
} = useAddressPageContext()
</script>
<style scoped>
.address-form-mobile {
display: flex;
flex-direction: column;
height: calc(80dvh - 3.5rem);
min-height: 0;
margin-top: 0.75rem;
}
.address-form-mobile__title {
flex-shrink: 0;
margin-bottom: 0.5rem;
font-size: 1.125rem;
font-weight: 700;
}
.address-form-mobile__scroll {
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
gap: 1rem;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overscroll-behavior-y: contain;
padding-bottom: 0.5rem;
}
.address-form-mobile__footer {
flex-shrink: 0;
padding-top: 0.75rem;
padding-bottom: calc(0.25rem + env(safe-area-inset-bottom, 0px));
background: #fff;
border-top: 1px solid #f3f4f6;
}
</style>
@@ -1,13 +1,13 @@
<template>
<div>
<MobilePageHeader title="آدرس ها" show-back />
<div>
<img :src="img" alt="" class="mt-2 w-full absolute bottom-0" />
<div class="relative">
<div class="mobile-page-bg" aria-hidden="true">
<img :src="img" alt="" />
</div>
<MobilePageHeader title="آدرس ها" show-back />
<div
:class="loading ? 'h-screen' : ''"
class="px-3 mpage-content pb-2 overflow-y-scroll mpage-scroll"
class="relative z-1 px-3 mpage-content pb-2 overflow-y-scroll mpage-scroll"
>
<div
v-if="isBazretalaProject()"
@@ -1,12 +1,12 @@
<template>
<div>
<div class="wrap-fix z-1 bg-white py-3 px-3 text-black font-bold drop-shadow flex">
<div class="wrap-fix mobile-top-chrome z-1 bg-white px-3 pb-3 text-black font-bold drop-shadow flex">
<div class="cursor-pointer" @click="router.back()">
<BasicIcon icon="vuesax-linear:arrow-right-1" />
</div>
<div class="mr-2">آموزش نرم افزار</div>
</div>
<div class="pt-10">
<div class="mobile-page-body--pt10">
<div class="text-justify text-base z-200000 relative">
<div class="px-10">
<div class="overflow-y-auto h-60vh pb-10 border-[#093D51]">
@@ -1,7 +1,7 @@
<template>
<div>
<div
class="bg-white wrap-fix py-3 px-3 text-primary font-bold drop-shadow flex h-12 z-20 justify-between"
class="bg-white wrap-fix mobile-top-chrome px-3 pb-3 text-primary font-bold drop-shadow flex min-h-12 z-20 justify-between"
>
<div class="flex">
<div class="cursor-pointer" @click="goBack()">
@@ -10,7 +10,7 @@
<div class="mr-2">{{ info?.title }}</div>
</div>
</div>
<div class="pt-20 flex flex-col justify-center items-center">
<div class="mobile-page-body--pt20 flex flex-col justify-center items-center">
<a-spin :spinning="loading">
<div class="bg-sky-100 w-[120px] aspect-square flex justify-center rounded-xl">
<img v-if="info?.group_type == 'گوی'" :src="goyImg" alt="" />
@@ -1,7 +1,7 @@
<template>
<div>
<div
class="bg-white w-full py-3 px-3 text-primary font-bold drop-shadow fixed md:absolute top-0 flex h-12 z-20"
class="bg-white w-full mobile-top-chrome px-3 pb-3 text-primary font-bold drop-shadow fixed md:absolute top-0 flex min-h-12 z-20"
>
<div @click="goBack()">
<BasicIcon icon="vuesax-linear:arrow-right-1" />
@@ -22,7 +22,7 @@
"
/>
<div
class="pt-15 flex flex-col relative z-10"
class="mobile-page-body--pt15 flex flex-col relative z-10"
:class="isBazretalaProject() ? 'text-white' : 'text-tertiary'"
>
<div class="font-bold mr-5 z-20">موجودی دارایی ها</div>
@@ -1,7 +1,7 @@
<template>
<div>
<div
class="bg-white wrap-fix py-3 px-3 text-primary font-bold drop-shadow flex h-12 z-20 justify-between"
class="bg-white wrap-fix mobile-top-chrome px-3 pb-3 text-primary font-bold drop-shadow flex min-h-12 z-20 justify-between"
>
<div class="flex">
<div class="cursor-pointer" @click="goBack()">
@@ -11,7 +11,7 @@
</div>
</div>
<a-spin :spinning="loading">
<div class="pt-20 flex flex-col justify-center items-center">
<div class="mobile-page-body--pt20 flex flex-col justify-center items-center">
<div class="bg-sky-100 w-[120px] aspect-square flex justify-center rounded-xl">
<img :src="itemImg" alt="" />
</div>
@@ -1,6 +1,6 @@
<template>
<div>
<MobilePageHeader title="کارت هدیه" />
<MobilePageHeader title="کارت هدیه" show-back />
<div class="px-5 mpage-content pb-2">
<div class="border-gray-200 border-1 rounded-xl p-5 trans h-40 relative">
<bg_main />
+22 -2
View File
@@ -1,7 +1,7 @@
<template>
<div
ref="headerRef"
class="app-header px-4 py-2 top-0 z-30 wrap-fix background-color-transition"
class="app-header px-4 pb-2 top-0 wrap-fix background-color-transition"
:class="{ 'app-header--scrolled': isHeaderScrolled }"
>
<div class="flex justify-between my-2 items-center">
@@ -40,7 +40,8 @@
</template>
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
import { computed, nextTick, onActivated, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/store/userStore'
import router from '@/router'
import Icon from '@/components/BasicPacks/BasicIcon.vue'
@@ -48,6 +49,7 @@ import { getProjectMobileNavLogo, isBoursecuProject } from '@/utils/config'
const headerRef = ref<HTMLElement | null>(null)
const isHeaderScrolled = ref(false)
const route = useRoute()
const userStore = useUserStore()
const projectLogo = computed(() => getProjectMobileNavLogo())
@@ -158,6 +160,21 @@ onMounted(async () => {
requestAnimationFrame(() => setupScrollTracking())
})
onActivated(() => {
isHeaderScrolled.value = false
setupScrollTracking()
})
watch(
() => route.fullPath,
() => {
if (route.name === 'home') {
isHeaderScrolled.value = false
nextTick(() => setupScrollTracking())
}
},
)
onUnmounted(() => {
teardownScrollTracking()
})
@@ -169,6 +186,9 @@ defineEmits<{
<style scoped>
.app-header {
z-index: 1999;
box-sizing: border-box;
padding-top: calc(0.5rem + var(--app-safe-top, env(safe-area-inset-top, 0px)));
background-color: transparent;
}
+10
View File
@@ -25,6 +25,8 @@ import BalanceSheet from './components/BalanceSheet.vue'
import ShareModal from './components/ShareModal.vue'
import HomePageShell from './shell/HomePageShell.vue'
import { useHomePage } from './composables/useHomePage'
import { onMounted } from 'vue'
import { recoverMobileViewportAfterAuth } from '@/layouts/composables/useMobileScrollReset'
const {
userStore,
@@ -37,6 +39,10 @@ const {
goldVal,
logout,
} = useHomePage()
onMounted(() => {
recoverMobileViewportAfterAuth()
})
</script>
<style scoped>
@@ -47,6 +53,10 @@ const {
</style>
<style lang="less">
.home-page-root.main-with-bottom-chrome-inner {
scroll-padding-top: var(--app-home-header-offset, calc(4.25rem + env(safe-area-inset-top, 0px)));
}
.odometer {
font-size: 60px;
text-align: center;
@@ -1,5 +1,5 @@
<template>
<div class="mt-10 p-5 home-mobile-root">
<div class="home-mobile-root p-5">
<div class="app-header-scroll-sentinel" aria-hidden="true" />
<component :is="layoutComponent" :layout-config="layoutConfig" />
<AppHeader @logout="$emit('logout')" />
@@ -26,6 +26,7 @@ const layoutConfig = computed(() => getHomeLayoutConfig(getHomeLayoutId()))
<style scoped>
.home-mobile-root {
position: relative;
padding-top: var(--app-home-header-offset, calc(4.25rem + env(safe-area-inset-top, 0px)));
}
.app-header-scroll-sentinel {
+23 -1
View File
@@ -2,7 +2,7 @@
<div class="physical-receive-page-root" :class="{ 'physical-receive-page-root--desktop': isDesktop }">
<MobilePhysicalReceiveChrome v-show="!isDesktop" />
<div :class="isDesktop ? 'dpr' : ''">
<div :class="isDesktop ? 'dpr' : 'physical-receive-page-root__shell'">
<div :class="isDesktop ? 'dpr__container' : 'physical-receive-page-root__content'">
<DesktopPhysicalReceiveChrome v-show="isDesktop" />
<router-view />
@@ -21,16 +21,38 @@ const { isDesktop } = usePhysicalReceiveShell()
<style scoped>
.physical-receive-page-root {
display: flex;
flex-direction: column;
min-height: 100%;
height: 100%;
overflow: hidden;
}
.physical-receive-page-root__shell {
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.physical-receive-page-root__content {
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.physical-receive-page-root--desktop {
height: auto;
min-height: 0;
overflow: visible;
}
</style>
<style lang="less">
.physical-receive-page-root.main-with-bottom-chrome-inner {
overflow: hidden;
}
</style>
@@ -1,7 +1,7 @@
<template>
<div>
<div ref="el" class="px-3 mpage-content pb-2">
<div class="flex gap-2">
<div class="physical-new-request">
<div class="physical-new-request__toolbar mpage-content">
<div class="flex gap-2 px-3">
<BasicSelect
v-model:value="selectedProduct"
:items="items"
@@ -17,18 +17,24 @@
افزودن
</BasicButton>
</div>
<div class="h-70vh rounded-lg bg-white mt-2 border-gray-200 border-1 overflow-y-auto p-2">
</div>
<div class="physical-new-request__items px-3">
<div class="physical-new-request__items-scroll">
<div
v-for="val in selectedProducts"
:key="val.product.id"
class="rounded-lg p-2 bg-gray-100 mb-2"
class="rounded-lg p-2 bg-gray-100 mb-2 last:mb-0"
>
<PhysicalReceiveCartRow :item="val" @remove="handleRemove" />
</div>
</div>
</div>
<div class="physical-new-request__footer px-3">
<BasicButton
:disabled-b="selectedProducts.length == 0"
class="w-full absolute bottom-10 mt-2"
class="w-full"
@click="handelFirstSubmit()"
>
ثبت
@@ -74,3 +80,46 @@ const {
handleAddress,
} = useNewPhysicalRequestContext()
</script>
<style scoped>
.physical-new-request {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
overflow: hidden;
}
.physical-new-request__toolbar {
flex-shrink: 0;
padding-bottom: 0.5rem;
}
.physical-new-request__items {
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
padding-bottom: 0.5rem;
}
.physical-new-request__items-scroll {
flex: 1 1 0;
min-height: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overscroll-behavior-y: contain;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
background: #fff;
padding: 0.5rem;
}
.physical-new-request__footer {
flex-shrink: 0;
padding-top: 0.75rem;
padding-bottom: 0.75rem;
background: #fff;
border-top: 1px solid #f3f4f6;
}
</style>
@@ -1,7 +1,5 @@
<template>
<div class="physical-receive-mobile-chrome" style="max-height: calc(100vh - 50px)">
<MobilePageHeader :title="pageTitle" show-back />
</div>
<MobilePageHeader :title="pageTitle" show-back />
</template>
<script lang="ts" setup>
+4 -2
View File
@@ -1,5 +1,5 @@
<template>
<div class="products-page-root">
<div class="products-page-root main-with-bottom-chrome-inner--contained">
<ResponsiveViewPair>
<template #mobile>
<MobileProductsView />
@@ -104,7 +104,9 @@ const {
<style scoped>
.products-page-root {
min-height: 100%;
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
</style>
@@ -1,41 +1,45 @@
<template>
<div class="products-mobile-root">
<div class="bottom-0 fixed top-0">
<img :src="img" alt="" class="w-inherit" :style="{ backgroundRepeat: 'repeat' }" />
<div class="mobile-page-bg" aria-hidden="true">
<img :src="img" alt="" />
</div>
<MobilePageHeader title="خرید و فروش" />
<a-spin :spinning="loading">
<div class="!h-full overflow-y-scroll mpage-content mpage-scroll">
<!-- بنر بسته بودن فروشگاه -->
<div v-if="storeClosed" class="mx-4 mt-4 closed-banner">
<div class="closed-banner__icon">🔒</div>
<div class="closed-banner__body">
<div class="closed-banner__title">فروشگاه بسته است</div>
<div v-if="storeClosedMessage" class="closed-banner__hint">{{ storeClosedMessage }}</div>
</div>
<div class="products-mobile-root__chrome mpage-content">
<div v-if="storeClosed" class="mx-4 mt-4 closed-banner">
<div class="closed-banner__icon">🔒</div>
<div class="closed-banner__body">
<div class="closed-banner__title">فروشگاه بسته است</div>
<div v-if="storeClosedMessage" class="closed-banner__hint">{{ storeClosedMessage }}</div>
</div>
</div>
<div class="w-full flex justify-center top-0 py-5">
<ARadioGroup
v-model:value="activekey"
size="large"
button-style="solid"
@change="changeTab"
<div class="products-mobile-root__tabs">
<ARadioGroup
v-model:value="activekey"
size="large"
button-style="solid"
@change="changeTab"
>
<ARadioButton class="cats" value="همه">همه</ARadioButton>
<ARadioButton
v-for="item in typeItems"
:key="item"
class="cats"
:value="item"
>
<ARadioButton class="cats" value="همه">همه</ARadioButton>
<ARadioButton
v-for="item in typeItems"
:key="item"
class="cats"
:value="item"
>
{{ item }}
</ARadioButton>
</ARadioGroup>
</div>
{{ item }}
</ARadioButton>
</ARadioGroup>
</div>
</div>
<div class="mx-5 pb-30 pt-2" style="color: #1b2b07">
<div class="products-mobile-root__scroll">
<a-spin :spinning="loading">
<div
class="products-mobile-root__list mx-5 pb-[calc(var(--app-bottom-chrome-height)+1rem)] pt-2"
style="color: #1b2b07"
>
<div v-for="item in items" :key="item.id" class="mt-3">
<ProductItem
:ProductItem="item"
@@ -44,8 +48,8 @@
/>
</div>
</div>
</div>
</a-spin>
</a-spin>
</div>
</div>
</template>
@@ -71,6 +75,48 @@ const storeClosed = computed(() => getPriceDisplayState() === 'closed' || isStor
const storeClosedMessage = computed(() => getStoreClosedMessage())
</script>
<style scoped>
.products-mobile-root {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
max-height: 100%;
overflow: hidden;
}
.products-mobile-root__chrome {
position: relative;
z-index: 1;
flex: 0 0 auto;
}
.products-mobile-root__tabs {
display: flex;
justify-content: center;
width: 100%;
padding: 1.25rem 0.75rem 0.75rem;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.products-mobile-root__scroll {
position: relative;
z-index: 1;
flex: 1 1 0;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
overscroll-behavior-y: contain;
}
.products-mobile-root__list {
min-height: min-content;
}
</style>
<style lang="less">
.cats:hover span {
--un-text-opacity: 1;
@@ -1,31 +1,35 @@
<template>
<div
:class="variant === 'desktop' ? 'drules-content' : 'rules-panel mt-5 border-solid border-2 border-primary text-primary rounded-[12px] p-4'"
:class="variant === 'desktop'
? 'drules-content'
: 'rules-panel border-solid border-2 border-primary text-primary rounded-[12px] p-4 mobile-content-box'"
>
<div :class="variant === 'desktop' ? 'drules-content__title' : 'font-bold text-sm text-primary'">
{{ rulesTitle }}
</div>
<div
:class="variant === 'desktop' ? 'drules-content__divider' : 'w-full h-1px my-3 bg-secondary'"
></div>
<div
v-for="(block, blockIndex) in rulesBlocks"
:key="`block-${blockIndex}`"
:class="variant === 'desktop' ? 'drules-content__block' : 'rules-block'"
>
<div
v-if="block.title"
:class="variant === 'desktop' ? 'drules-content__block-title' : 'font-bold text-sm text-primary mt-4 mb-2'"
>
{{ block.title }}
<div :class="variant !== 'desktop' ? 'mobile-content-box__scroll' : undefined">
<div :class="variant === 'desktop' ? 'drules-content__title' : 'font-bold text-sm text-primary'">
{{ rulesTitle }}
</div>
<p
v-for="(rule, index) in block.items"
:key="`rule-${blockIndex}-${index}`"
:class="variant === 'desktop' ? 'drules-content__paragraph' : 'rules-paragraph'"
<div
:class="variant === 'desktop' ? 'drules-content__divider' : 'w-full h-1px my-3 bg-secondary'"
></div>
<div
v-for="(block, blockIndex) in rulesBlocks"
:key="`block-${blockIndex}`"
:class="variant === 'desktop' ? 'drules-content__block' : 'rules-block'"
>
{{ rule }}
</p>
<div
v-if="block.title"
:class="variant === 'desktop' ? 'drules-content__block-title' : 'font-bold text-sm text-primary mt-4 mb-2'"
>
{{ block.title }}
</div>
<p
v-for="(rule, index) in block.items"
:key="`rule-${blockIndex}-${index}`"
:class="variant === 'desktop' ? 'drules-content__paragraph' : 'rules-paragraph'"
>
{{ rule }}
</p>
</div>
</div>
</div>
</template>
@@ -1,5 +1,5 @@
<template>
<div :class="variant === 'desktop' ? 'drules-support' : 'wrap-fix bottom-0 px-10 pb-3rem flex gap-2 bg-white'">
<div :class="variant === 'desktop' ? 'drules-support' : 'wrap-fix mobile-info-page__support flex gap-2'">
<a
class="w-full"
:href="supportHref"
+9
View File
@@ -20,5 +20,14 @@ useRulesPage()
.rules-page-root {
min-height: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
</style>
<style lang="less">
.rules-page-root.main-with-bottom-chrome-inner {
overflow: hidden;
}
</style>
@@ -1,31 +1,19 @@
<template>
<div>
<div class="wrap-fix z-1 bg-white py-3 px-3 text-primary font-bold drop-shadow flex">
<div class="cursor-pointer" @click="router.back()">
<BasicIcon icon="vuesax-linear:arrow-right-1" />
</div>
<div class="mr-2">قوانین و مقررات</div>
<div class="mobile-info-page">
<div class="mobile-info-page__bg" aria-hidden="true">
<img :src="img" alt="" />
</div>
<div class="pt-20">
<div>
<img :src="img" alt="" class="mt-2 w-full absolute bottom-0" />
</div>
<div class="text-base z-200000 relative">
<div class="px-10">
<div class="overflow-y-auto h-60vh pb-20 border-primary">
<RulesContentPanel variant="mobile" />
</div>
</div>
<RulesSupportActions variant="mobile" />
</div>
<MobilePageHeader title="قوانین و مقررات" show-back />
<div class="mobile-info-page__main">
<RulesContentPanel variant="mobile" />
</div>
<RulesSupportActions variant="mobile" />
</div>
</template>
<script lang="ts" setup>
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import img from '@/assets/img/user_ly1.svg'
import router from '@/router'
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
import RulesContentPanel from '../components/RulesContentPanel.vue'
import RulesSupportActions from '../components/RulesSupportActions.vue'
</script>
@@ -1,6 +1,10 @@
<template>
<div>
<div class="absolute inset-0 bg-cover bg-center opacity-60 pointer-events-none"></div>
<div class="relative">
<div
class="mobile-page-bg mobile-page-bg--cover opacity-60"
aria-hidden="true"
:style="{ backgroundImage: `url(${img})` }"
></div>
<MobilePageHeader title="تراکنش ها">
<template #trailing>
<BasicIcon icon="vuesax-linear:filter" @click="showFilter = true" />
@@ -8,7 +12,7 @@
</MobilePageHeader>
<!-- <div ref="el" :class="loading ? 'h-screen' : ''" class="px-3 mpage-content pb-10">-->
<div ref="el" class="transactions-page-body px-3 pt-16 pb-24">
<div ref="el" class="transactions-page-body relative z-1 px-3 mpage-content pb-24">
<list-anim v-if="!loading">
<Item v-for="item in items" :key="item.id" :item="item" />
<div :ref="bindMobileObserver" v-show="!loading" class="w-full opacity-0 relative">
@@ -29,7 +33,7 @@
<BasicActionSheet v-model:show="showFilter">
<div class="font-bold text-lg">فیلتر</div>
<div class="pb-20 mt-5">
<div class="pb-10 mt-5">
<a-form-item class="w-full gap-2 !flex-col" label="تاریخ" name="password">
<BaseDatePicker v-model:value="dateFilter" class="w-full" size="large" />
</a-form-item>
@@ -52,6 +56,7 @@ import BaseDatePicker from '@/components/BasicPacks/BaseDatePicker/src/BaseDateP
import ListAnim from '@/components/ListAnim.vue'
import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
import img from '@/assets/img/user_ly1.svg'
import { useTransactionsPageContext } from './composables/useTransactionsPage'
const el = ref<HTMLElement | null>(null)
@@ -22,7 +22,7 @@
<div v-if="showSubmit" class="md:w-[395px] pt-3rem px-3">
<BasicButton
:disabled="loading"
class="w-full py-2 mb-20"
class="w-full py-2 "
html-type="submit"
type="primary"
>
@@ -1,37 +1,43 @@
<template>
<a-form ref="formRef" :model="userFormState" class="!mt-5" name="basic" @finish="ok">
<a-form-item :rules="[isValidPersian()]" label="نام" name="fname">
<BasicInput v-model:value="userFormState.fname" />
</a-form-item>
<a-form-item :rules="[isValidPersian()]" label="نام خانوادگی" name="lname">
<BasicInput v-model:value="userFormState.lname" />
</a-form-item>
<a-form-item label="تاریخ تولد" name="birth_date">
<BaseDatePicker v-model:value="userFormState.birth_date" class="w-full" inputReadOnly />
</a-form-item>
<a-form-item :rules="[requireNationalCode()]" label="شماره ملی" name="n_code">
<BasicInput
v-model:value="userFormState.n_code"
type="number"
:disabled="userFormState.is_legal == 1 || userFormState.is_legal == 2"
/>
</a-form-item>
<a-form-item label="نام شهر" name="city">
<BasicInput v-model:value="userFormState.city" />
</a-form-item>
<a-form-item v-if="showSubmit">
<div class="wrap-fix md:w-[395px] bottom-5 px-3">
<BasicButton
:disabled="loading"
class="w-full py-2 mb-20"
html-type="submit"
type="primary"
>
<a-spin v-if="loading" size="small" />
<span v-else>ثبت</span>
</BasicButton>
</div>
</a-form-item>
<a-form
ref="formRef"
:model="userFormState"
:class="showSubmit ? 'user-profile-form-mobile' : '!mt-5'"
name="basic"
@finish="ok"
>
<div :class="showSubmit ? 'user-profile-form-mobile__scroll' : undefined">
<a-form-item :rules="[isValidPersian()]" label="نام" name="fname">
<BasicInput v-model:value="userFormState.fname" />
</a-form-item>
<a-form-item :rules="[isValidPersian()]" label="نام خانوادگی" name="lname">
<BasicInput v-model:value="userFormState.lname" />
</a-form-item>
<a-form-item label="تاریخ تولد" name="birth_date">
<BaseDatePicker v-model:value="userFormState.birth_date" class="w-full" inputReadOnly />
</a-form-item>
<a-form-item :rules="[requireNationalCode()]" label="شماره ملی" name="n_code">
<BasicInput
v-model:value="userFormState.n_code"
type="number"
:disabled="userFormState.is_legal == 1 || userFormState.is_legal == 2"
/>
</a-form-item>
<a-form-item label="نام شهر" name="city">
<BasicInput v-model:value="userFormState.city" />
</a-form-item>
</div>
<div v-if="showSubmit" class="user-profile-form-mobile__footer">
<BasicButton
:disabled="loading"
class="w-full py-2"
html-type="submit"
type="primary"
>
<a-spin v-if="loading" size="small" />
<span v-else>ثبت</span>
</BasicButton>
</div>
</a-form>
</template>
@@ -57,3 +63,30 @@ const {
ok,
} = useUserPageContext()
</script>
<style scoped>
.user-profile-form-mobile {
display: flex;
flex-direction: column;
flex: 1 1 0;
min-height: 0;
height: 100%;
}
.user-profile-form-mobile__scroll {
flex: 1 1 0;
min-height: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overscroll-behavior-y: contain;
padding-bottom: 0.5rem;
}
.user-profile-form-mobile__footer {
flex-shrink: 0;
padding-top: 0.75rem;
padding-bottom: calc(0.25rem + env(safe-area-inset-bottom, 0px));
background: #fff;
border-top: 1px solid #f3f4f6;
}
</style>
+21 -9
View File
@@ -4,15 +4,15 @@
<MobileUserView v-show="!isDesktop" />
<!-- ویرایش اطلاعات — موبایل -->
<BasicActionSheet v-if="!isDesktop" v-model:show="editPassVisible">
<div class="flex justify-center justify-between">
<div class="font-bold text-lg">ویرایش اطلاعات</div>
<div class="flex flex-col justify-center items-center">
<BasicIcon :icon="isLegal.icon" size="30" :class="isLegal.color" />
<p class="text-[10px] mt-1" :class="isLegal.color">{{ isLegal.title }}</p>
<BasicActionSheet v-if="!isDesktop" v-model:show="editPassVisible" class="h-80vh">
<div class="user-edit-sheet">
<div class="user-edit-sheet__header flex justify-between items-start">
<div class="font-bold text-lg">ویرایش اطلاعات</div>
<div class="flex flex-col justify-center items-center">
<BasicIcon :icon="isLegal.icon" size="30" :class="isLegal.color" />
<p class="text-[10px] mt-1" :class="isLegal.color">{{ isLegal.title }}</p>
</div>
</div>
</div>
<div class="h60vh overflow-y-auto pb20">
<UserProfileForm show-submit />
</div>
</BasicActionSheet>
@@ -39,7 +39,7 @@
<!-- ویرایش رمزعبور — موبایل -->
<BasicActionSheet v-if="!isDesktop" v-model:show="editPasswordVisible">
<div class="font-bold text-lg">ویرایش رمزعبور</div>
<div class="h35vh overflow-y-auto">
<div class="h25vh overflow-y-auto">
<UserPasswordForm show-submit />
</div>
</BasicActionSheet>
@@ -87,6 +87,18 @@ const {
min-height: 100%;
height: 100%;
}
.user-edit-sheet {
display: flex;
flex-direction: column;
height: calc(80dvh - 3.5rem);
min-height: 0;
}
.user-edit-sheet__header {
flex-shrink: 0;
margin-bottom: 0.75rem;
}
</style>
<style lang="less">
+25 -16
View File
@@ -1,17 +1,14 @@
<template>
<div class="relative">
<div>
<img :src="img" alt="" class="mt-2 w-full absolute bottom-0" />
<div class="mobile-user-view">
<div class="mobile-page-bg" aria-hidden="true">
<img :src="img" alt="" />
</div>
<div class="bg-white w-full py-3 px-3 text-primary font-bold drop-shadow flex">
<div class="cursor-pointer" @click="router.back()">
<BasicIcon icon="vuesax-linear:arrow-right-1" />
</div>
<div class="mr-2 mt-2">اطلاعات حساب کاربری</div>
</div>
<div class="pt-16 pb-8 z-30 relative">
<MobilePageHeader title="اطلاعات حساب کاربری" show-back />
<div
class="mobile-user-view__scroll mpage-content pb-[calc(var(--app-bottom-chrome-height)+1rem)]"
>
<div
class="border-gray-200 border-1 rounded-xl p-5 trans flex flex-col items-center mx-5 z-1000 relative bg-white shadow-lg"
class="border-gray-200 border-1 rounded-xl p-5 trans flex flex-col items-center mx-5 relative bg-white shadow-lg"
>
<div class="text-primary font-bold w-full text-center flex justify-between">
نام و نام خانوادگی:
@@ -63,7 +60,7 @@
<div>ویرایش اطلاعات کاربری</div>
</div>
</BasicButton>
<div class="h-45vh overflow-y-auto mt-4">
<div class="w-full mt-4">
<BasicButton
v-for="action in menuActions"
:key="action.id"
@@ -94,7 +91,7 @@
import img from '@/assets/img/user_ly1.svg'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import router from '@/router'
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
import { useUserPageContext } from '../composables/useUserPage'
const {
@@ -108,11 +105,23 @@ const {
</script>
<style scoped>
.relative {
.mobile-user-view {
position: relative;
z-index: 40;
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
overflow: hidden;
}
.mobile-user-view__scroll {
position: relative;
z-index: 1;
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
max-height: 100vh;
-webkit-overflow-scrolling: touch;
overscroll-behavior-y: contain;
}
.user-menu-icon-image {
@@ -1,6 +1,10 @@
<template>
<div>
<div class="absolute inset-0 bg-cover bg-center opacity-60" :style="{ backgroundImage: `url(${img})` }"></div>
<div class="relative">
<div
class="mobile-page-bg mobile-page-bg--cover opacity-60"
aria-hidden="true"
:style="{ backgroundImage: `url(${img})` }"
></div>
<MobilePageHeader title="کیف پول" />
<div class="mpage-subheader wrap-fix flex justify-evenly">
<div class="flex justify-evenly w-full px-2 h-11 items-center bg-gray-50 relative rounded">
@@ -118,7 +122,7 @@
:ref="(node) => { mobileDepositScrollEl = (node as HTMLElement | null) }"
:class="loading ? 'h-screen' : ''"
class="px-3 overflow-y-scroll w-full h-[49vh] pb-16"
style="max-height: calc(100vh - 50px)"
style="max-height: var(--mpage-scroll-max-height)"
>
<list-anim v-if="!loading" :numberItemsAnim="4">
<Item v-for="item in depositItems" :key="item.id" :item="item" />
+2 -1
View File
@@ -377,6 +377,7 @@ import { useWalletStore } from '@/store/walletStore'
import { useSettingsStore } from '@/store/settingsStore'
import { syncPriceRealtimeChannel } from '@/utils/priceSocket'
import { setTruth } from '@/app/authBoot'
import { completeLoginNavigation } from '@/layouts/composables/useMobileScrollReset'
import {useConfigStore} from "@/store/configStore";
import {
getConfigApp,
@@ -488,7 +489,7 @@ async function login(accessToken: string | undefined) {
/* if (userStore.userInfo?.type === (URLUserType() === 'customer' ? 1 : 2)) {*/
await walletStore.getMyWallet()
setTruth()
await router.push({ name: 'home' })
await completeLoginNavigation()
/* } else {
removeToken()
userStore.removeInformation()
+3 -2
View File
@@ -16,6 +16,7 @@ import { useUserStore } from '@/store/userStore'
import { useWalletStore } from '@/store/walletStore'
import { useSettingsStore } from '@/store/settingsStore'
import { setTruth } from '@/app/authBoot'
import { completeLoginNavigation } from '@/layouts/composables/useMobileScrollReset'
export function useLoginForm() {
const { time, onFinish, reset } = useCountdownTimer('00:02:00', 'mm:ss')
@@ -55,7 +56,7 @@ export function useLoginForm() {
const fingerPrint = await authenticateWithFingerprint()
if (fingerPrint) {
setTruth()
await router.push({ name: 'home' })
await completeLoginNavigation()
} else {
message.error('خطا در اعتبارسنجی')
}
@@ -75,7 +76,7 @@ export function useLoginForm() {
await userStore.getMyInfo()
await walletStore.getMyWallet()
setTruth()
await router.push({ name: 'home' })
await completeLoginNavigation()
}
function getcode() {
+9 -2
View File
@@ -78,11 +78,18 @@ const isGaroosi = computed(() => config.config?.name === 'garoosi')
<style scoped>
.login-page-root {
width: 100%;
min-height: 100vh;
height: 100%;
max-height: 100dvh;
overflow: hidden;
}
.login-page {
min-height: calc(100vh - var(--app-credit-footer-offset, 28px));
height: 100%;
max-height: calc(100dvh - var(--app-credit-footer-offset, 28px));
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}
.login-page__content {
+5 -1
View File
@@ -32,6 +32,10 @@ const isBoursecu = isBoursecuProject()
<style scoped>
.login-page {
min-height: calc(100vh - var(--app-credit-footer-offset, 28px));
min-height: calc(100dvh - var(--app-credit-footer-offset, 28px));
max-height: calc(100dvh - var(--app-credit-footer-offset, 28px));
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
}
</style>