1405/04/10-fix bugs
This commit is contained in:
+1
-1
@@ -155,7 +155,7 @@ const {
|
||||
} = useDraggableFloatingButton('ghatreh_support_button_position_v1', {
|
||||
width: 118,
|
||||
height: 42,
|
||||
defaultBottomOffset: 100,
|
||||
defaultBottomGap: 12,
|
||||
defaultRightOffset: 16,
|
||||
})
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { createVueApp } from '@/app/createApp'
|
||||
import { setupRouterGuards } from '@/app/setupRouter'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import { initPriceRealtimeChannel } from '@/utils/priceSocket'
|
||||
import { initMobileScrollRestoration } from '@/layouts/composables/useMobileScrollReset'
|
||||
|
||||
type BootWindow = Window & {
|
||||
__BOOT_TIMEOUT__?: number
|
||||
@@ -74,6 +75,7 @@ export async function bootstrapApp() {
|
||||
await withTimeout(settingsStore.fetchSettings(), 45000, 'Settings loading timeout')
|
||||
initPriceRealtimeChannel((key) => settingsStore.getSettingByKey(key))
|
||||
|
||||
initMobileScrollRestoration()
|
||||
setupRouterGuards()
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
setFingerTimeOut,
|
||||
setHasFingerPrint
|
||||
} from '@/app/authBoot'
|
||||
import { recoverMobileViewportAfterAuth } from '@/layouts/composables/useMobileScrollReset'
|
||||
|
||||
export function setupRouterGuards() {
|
||||
const userStore = useUserStore()
|
||||
@@ -115,5 +116,12 @@ 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()))
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
@import (less) './fonts.less';
|
||||
@import (less) '../../layouts/styles/desktop-shell.less';
|
||||
@import (less) '../../layouts/styles/mobile-frame.less';
|
||||
@import (less) '../../layouts/styles/mobile-page-common.less';
|
||||
@import (less) '../../layouts/desktop/styles/desktop-page-common.less';
|
||||
@import (less) '../../views/base/fund/desktop/styles/desktop-fund.less';
|
||||
@import (less) '../../views/base/transactions/desktop/styles/desktop-transactions.less';
|
||||
@import (less) '../../views/base/wallet/desktop/styles/desktop-wallet.less';
|
||||
@import (less) '../../views/base/user/desktop/styles/desktop-user.less';
|
||||
@import (less) '../../views/base/priceTrigger/desktop/styles/desktop-price-trigger.less';
|
||||
@import (less) '../../views/base/home/desktop/styles/desktop-home.less';
|
||||
@import (less) '../../layouts/desktop/styles/desktop-inner-pages.less';
|
||||
|
||||
@@ -59,6 +61,21 @@ body {
|
||||
|
||||
:root {
|
||||
color-scheme: only light;
|
||||
--app-safe-top: env(safe-area-inset-top, 0px);
|
||||
--app-safe-bottom: env(safe-area-inset-bottom, 0px);
|
||||
--app-credit-bar-inner: 0px;
|
||||
--app-home-header-offset: calc(4.25rem + var(--app-safe-top));
|
||||
--app-credit-footer-offset: var(--app-safe-bottom);
|
||||
--app-nav-height: 80px;
|
||||
--app-bottom-chrome-height: calc(var(--app-nav-height) + var(--app-safe-bottom));
|
||||
--app-add-card-height: 88px;
|
||||
--wallet-card-dock-overlap: 52px;
|
||||
--app-z-nav: 5000;
|
||||
--app-z-wallet-dock: 4490;
|
||||
--app-z-wallet-add-bar: 4510;
|
||||
--app-z-action-sheet: 5200;
|
||||
--app-z-dialog: 5300;
|
||||
--app-z-modal: 5500;
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
@@ -83,13 +100,13 @@ body {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/*.van-action-sheet{
|
||||
z-index: 2001 !important;
|
||||
.van-action-sheet{
|
||||
z-index: 5001 !important;
|
||||
}
|
||||
|
||||
.van-overlay{
|
||||
z-index: 2000 !important;
|
||||
}*/
|
||||
//.van-overlay{
|
||||
// z-index: 2000 !important;
|
||||
//}
|
||||
|
||||
.color-transition {
|
||||
transition: color 0.3s ease;
|
||||
|
||||
@@ -18,6 +18,7 @@ export const GUEST_PRIMARY_NAV: AppNavItem[] = [
|
||||
|
||||
/** منوی FAB موبایل + بخش فرعی سایدبار دسکتاپ — کاربر لاگینشده */
|
||||
export const AUTH_SECONDARY_NAV: AppNavItem[] = [
|
||||
{ id: 'notifyTrigger', routeName: 'notifyTrigger', label: 'هشدار قیمت', icon: 'vuesax-linear:bill' },
|
||||
{ id: 'buyProduct', routeName: 'buyProduct', label: 'فروشگاه', icon: 'vuesax-linear:coin' },
|
||||
{ id: 'fund', routeName: 'fund', label: 'دارایی ها', icon: 'vuesax-linear:archive-1' },
|
||||
{ id: 'user', routeName: 'user', label: 'حساب کاربری', icon: 'vuesax-linear:user' },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="mobile-app-layout">
|
||||
<MobileOverflowMenu
|
||||
:show="popVisible"
|
||||
@close="closeOverflowMenu"
|
||||
@@ -7,27 +7,34 @@
|
||||
/>
|
||||
|
||||
<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>
|
||||
|
||||
<MobileBottomNav
|
||||
v-if="!hideMenu"
|
||||
:nav-item-class="navItemClass"
|
||||
@navigate="navigateTo"
|
||||
@toggle-overflow="toggleOverflowMenu"
|
||||
/>
|
||||
<Teleport to="body">
|
||||
<MobileBottomNav
|
||||
v-if="!hideMenu"
|
||||
:nav-item-class="navItemClass"
|
||||
@navigate="navigateTo"
|
||||
@toggle-overflow="toggleOverflowMenu"
|
||||
/>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useAppNavigation } from '../composables/useAppNavigation'
|
||||
import { recoverMobileViewportAfterAuth, resetMobileAppScroll } from '../composables/useMobileScrollReset'
|
||||
import MobileBottomNav from './MobileBottomNav.vue'
|
||||
import MobileOverflowMenu from './MobileOverflowMenu.vue'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const {
|
||||
popVisible,
|
||||
hideMenu,
|
||||
@@ -36,6 +43,19 @@ const {
|
||||
toggleOverflowMenu,
|
||||
closeOverflowMenu,
|
||||
} = useAppNavigation()
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
() => {
|
||||
resetMobileAppScroll()
|
||||
},
|
||||
{ flush: 'post' },
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
resetMobileAppScroll()
|
||||
recoverMobileViewportAfterAuth()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" src="./mobile-layout.less"></style>
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
<div class="tabs bottom-0">
|
||||
<template v-for="item in leftTabs" :key="item.id">
|
||||
<div :class="navItemClass(item.routeName)" class="tab" @click="$emit('navigate', item.routeName)">
|
||||
<a-badge v-if="item.routeName === 'cart'" :count="cartCount">
|
||||
<Icon :icon="item.icon" size="24" />
|
||||
</a-badge>
|
||||
<Icon v-else :icon="item.icon" size="24" />
|
||||
<div class="tab__icon">
|
||||
<a-badge v-if="item.routeName === 'cart'" :count="cartCount">
|
||||
<Icon :icon="item.icon" size="24" />
|
||||
</a-badge>
|
||||
<Icon v-else :icon="item.icon" size="24" />
|
||||
</div>
|
||||
<div class="text-xs mt-1 font-bold">{{ item.label }}</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -21,10 +23,12 @@
|
||||
|
||||
<template v-for="item in rightTabs" :key="item.id">
|
||||
<div :class="navItemClass(item.routeName)" class="tab" @click="$emit('navigate', item.routeName)">
|
||||
<a-badge v-if="item.routeName === 'cart'" :count="cartCount">
|
||||
<Icon :icon="item.icon" size="24" />
|
||||
</a-badge>
|
||||
<Icon v-else :icon="item.icon" size="24" />
|
||||
<div class="tab__icon">
|
||||
<a-badge v-if="item.routeName === 'cart'" :count="cartCount">
|
||||
<Icon :icon="item.icon" size="24" />
|
||||
</a-badge>
|
||||
<Icon v-else :icon="item.icon" size="24" />
|
||||
</div>
|
||||
<div class="text-xs mt-1 font-bold">{{ item.label }}</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -61,3 +65,19 @@ const primaryItems = computed(() => getPrimaryNavItems(isToken()))
|
||||
const leftTabs = computed(() => primaryItems.value.slice(0, 2))
|
||||
const rightTabs = computed(() => primaryItems.value.slice(2))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tab__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.tab__icon :deep(.ant-badge) {
|
||||
color: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<Transition>
|
||||
<div
|
||||
v-if="show"
|
||||
class="top-0 bottom-0 bg-black wrap-fix z-2000 opacity-60"
|
||||
class="top-0 bottom-0 bg-black wrap-fix z-5000 opacity-60"
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</Transition>
|
||||
<Transition name="slide-fade">
|
||||
<div v-if="show" class="wrap-fix bottom-11vh z-2000">
|
||||
<div v-if="show" class="wrap-fix bottom-11vh z-5000 flex justify-center items-end">
|
||||
<div class="relative flex flex-col items-center">
|
||||
<div
|
||||
class="flex items-center justify-center bg-white rounded-full w-[30px] h-[30px] mb-2"
|
||||
@@ -37,6 +37,17 @@
|
||||
<div>دریافت طلای فیزیکی</div>
|
||||
</div>
|
||||
</BasicButton>
|
||||
<BasicButton
|
||||
v-if="authenticated"
|
||||
icon-size="16"
|
||||
class="!font-bold w-15rem"
|
||||
@click="go('notifyTrigger')"
|
||||
>
|
||||
<div class="flex gap-2 items-center">
|
||||
<BasicIcon icon="vuesax-linear:bill" size="16" class="absolute right-2" />
|
||||
<div>هشدار قیمت</div>
|
||||
</div>
|
||||
</BasicButton>
|
||||
<BasicButton
|
||||
v-if="authenticated"
|
||||
icon-size="16"
|
||||
@@ -48,17 +59,17 @@
|
||||
<div>خرید محصول</div>
|
||||
</div>
|
||||
</BasicButton>
|
||||
<BasicButton
|
||||
v-if="authenticated"
|
||||
icon-size="16"
|
||||
class="!font-bold w-15rem"
|
||||
@click="go('list')"
|
||||
>
|
||||
<div class="flex gap-2 items-center">
|
||||
<BasicIcon icon="vuesax-linear:receipt-item" size="16" class="absolute right-2" />
|
||||
<div>لیست دریافتی ها</div>
|
||||
</div>
|
||||
</BasicButton>
|
||||
<!-- <BasicButton-->
|
||||
<!-- v-if="authenticated"-->
|
||||
<!-- icon-size="16"-->
|
||||
<!-- class="!font-bold w-15rem"-->
|
||||
<!-- @click="go('list')"-->
|
||||
<!-- >-->
|
||||
<!-- <div class="flex gap-2 items-center">-->
|
||||
<!-- <BasicIcon icon="vuesax-linear:receipt-item" size="16" class="absolute right-2" />-->
|
||||
<!-- <div>لیست دریافتی ها</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </BasicButton>-->
|
||||
<BasicButton
|
||||
v-if="authenticated"
|
||||
icon-size="16"
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
.bottom-appbar {
|
||||
z-index: var(--app-z-nav, 5000);
|
||||
height: var(--app-nav-height, 80px);
|
||||
position: fixed;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
background: #fff;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
height: calc(var(--app-nav-height, 80px) + var(--app-safe-bottom, env(safe-area-inset-bottom, 0px)));
|
||||
padding-bottom: var(--app-safe-bottom, env(safe-area-inset-bottom, 0px));
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
background: #fff;
|
||||
|
||||
@media screen and (min-width: 426px) {
|
||||
width: 426px;
|
||||
@@ -39,6 +40,16 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
|
||||
&.text-primary .tab__icon,
|
||||
&.text-primary .tab__icon .ant-badge {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&.text-gray-400 .tab__icon,
|
||||
&.text-gray-400 .tab__icon .ant-badge {
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.main_in {
|
||||
@@ -121,19 +132,38 @@
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.mobile-app-layout {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-with-bottom-chrome {
|
||||
min-height: calc(100vh - var(--app-bottom-chrome-height, 80px));
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-with-bottom-chrome-inner {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
min-height: min(100%, calc(100vh - var(--app-bottom-chrome-height, 80px)));
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
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 {
|
||||
min-height: 100vh;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,42 @@
|
||||
/**
|
||||
* Mobile shell width rules.
|
||||
* Below 426px: full viewport (phones).
|
||||
* 426px–767px: centered 426px frame (tablet-ish mobile).
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
.app-dock-add-bar,
|
||||
.wallet-card-slider {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 425.98px) {
|
||||
body {
|
||||
display: block !important;
|
||||
@@ -8,6 +46,7 @@ html:not(.app-layout--desktop) {
|
||||
|
||||
#app,
|
||||
.desktopConfig,
|
||||
.app-main,
|
||||
.main-with-bottom-chrome,
|
||||
.main-with-bottom-chrome-inner {
|
||||
width: 100% !important;
|
||||
@@ -25,6 +64,16 @@ html:not(.app-layout--desktop) {
|
||||
margin: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.app-dock-add-bar,
|
||||
.wallet-card-slider {
|
||||
width: 100% !important;
|
||||
max-width: 100vw !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
margin: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 426px) and (max-width: 767.98px) {
|
||||
@@ -47,5 +96,15 @@ html:not(.app-layout--desktop) {
|
||||
margin: 0 !important;
|
||||
transform: translateX(-50%) !important;
|
||||
}
|
||||
|
||||
.app-dock-add-bar,
|
||||
.wallet-card-slider {
|
||||
width: 426px !important;
|
||||
max-width: 426px !important;
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
margin: 0 !important;
|
||||
transform: translateX(-50%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-13
@@ -85,22 +85,12 @@ const router = createRouter({
|
||||
name: 'address',
|
||||
component: () => import('@/views/base/address/index.vue')
|
||||
},
|
||||
/* {
|
||||
path: 'physicalReceive',
|
||||
name: 'physicalReceive',
|
||||
component: () => import('@/views/base/physicalReceive/index.vue'),
|
||||
},*/
|
||||
// {
|
||||
// path: 'list',
|
||||
// name: 'list',
|
||||
// component: () => import('@/views/base/physicalReceive/components/RequestList.vue'),
|
||||
// },
|
||||
{
|
||||
path: 'physicalReceive',
|
||||
name: 'physicalReceive',
|
||||
redirect:{name: 'list'},
|
||||
// component: () => import('@/views/base/physicalReceive/index.vue'),
|
||||
children:[
|
||||
redirect: { name: 'list' },
|
||||
component: () => import('@/views/base/physicalReceive/index.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'list',
|
||||
|
||||
@@ -1,24 +1,53 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const DRAG_THRESHOLD = 6
|
||||
const POSITION_VERSION = 1
|
||||
|
||||
type FabPosition = { x: number; y: number }
|
||||
|
||||
type SavedFabOffset = {
|
||||
version: number
|
||||
dx: number
|
||||
dy: number
|
||||
}
|
||||
|
||||
type DraggableFloatingOptions = {
|
||||
width?: number
|
||||
height?: number
|
||||
defaultBottomOffset?: number
|
||||
defaultBottomGap?: number
|
||||
defaultRightOffset?: number
|
||||
}
|
||||
|
||||
function readBottomChromeHeight(): number {
|
||||
const styles = getComputedStyle(document.documentElement)
|
||||
const direct = parseFloat(styles.getPropertyValue('--app-bottom-chrome-height'))
|
||||
if (!Number.isNaN(direct) && direct > 0) {
|
||||
return direct
|
||||
}
|
||||
|
||||
const nav = parseFloat(styles.getPropertyValue('--app-nav-height')) || 80
|
||||
const creditOffset = parseFloat(styles.getPropertyValue('--app-credit-footer-offset'))
|
||||
if (!Number.isNaN(creditOffset) && creditOffset > 0) {
|
||||
return nav + creditOffset
|
||||
}
|
||||
|
||||
const probe = document.createElement('div')
|
||||
probe.style.cssText = 'position:fixed;visibility:hidden;pointer-events:none;height:var(--app-bottom-chrome-height)'
|
||||
document.body.appendChild(probe)
|
||||
const measured = probe.getBoundingClientRect().height
|
||||
document.body.removeChild(probe)
|
||||
|
||||
return measured > 0 ? measured : nav
|
||||
}
|
||||
|
||||
export function useDraggableFloatingButton(
|
||||
storageKey: string,
|
||||
options: DraggableFloatingOptions = {}
|
||||
) {
|
||||
const width = options.width ?? 96
|
||||
const height = options.height ?? 44
|
||||
const defaultBottomOffset = options.defaultBottomOffset ?? 92
|
||||
const defaultRightOffset = options.defaultRightOffset ?? 20
|
||||
const defaultBottomGap = options.defaultBottomGap ?? 12
|
||||
const defaultRightOffset = options.defaultRightOffset ?? 16
|
||||
|
||||
const pos = ref<FabPosition>({ x: 0, y: 0 })
|
||||
const dragging = ref(false)
|
||||
@@ -44,26 +73,40 @@ export function useDraggableFloatingButton(
|
||||
function getHorizontalOffset() {
|
||||
const vw = window.innerWidth
|
||||
const contentWidth = getViewportWidth()
|
||||
|
||||
return Math.max(0, (vw - contentWidth) / 2)
|
||||
}
|
||||
|
||||
function resetToDefault() {
|
||||
function getDefaultPosition(): FabPosition {
|
||||
const vh = window.innerHeight
|
||||
const offsetX = getHorizontalOffset()
|
||||
const contentWidth = getViewportWidth()
|
||||
const bottomChrome = readBottomChromeHeight()
|
||||
|
||||
pos.value = {
|
||||
return {
|
||||
x: offsetX + contentWidth - width - defaultRightOffset,
|
||||
y: vh - defaultBottomOffset - height
|
||||
y: vh - bottomChrome - defaultBottomGap - height
|
||||
}
|
||||
}
|
||||
|
||||
function applySavedOffset(dx: number, dy: number) {
|
||||
const base = getDefaultPosition()
|
||||
pos.value = {
|
||||
x: base.x + dx,
|
||||
y: base.y + dy
|
||||
}
|
||||
clampToViewport()
|
||||
}
|
||||
|
||||
function clampToViewport() {
|
||||
const vw = window.innerWidth
|
||||
const vh = window.innerHeight
|
||||
const offsetX = getHorizontalOffset()
|
||||
const contentWidth = getViewportWidth()
|
||||
const minX = offsetX
|
||||
const maxX = offsetX + contentWidth - width
|
||||
|
||||
pos.value = {
|
||||
x: Math.max(0, Math.min(vw - width, pos.value.x)),
|
||||
x: Math.max(minX, Math.min(maxX, pos.value.x)),
|
||||
y: Math.max(0, Math.min(vh - height, pos.value.y))
|
||||
}
|
||||
}
|
||||
@@ -72,25 +115,56 @@ export function useDraggableFloatingButton(
|
||||
try {
|
||||
const raw = localStorage.getItem(storageKey)
|
||||
if (!raw) return false
|
||||
const parsed = JSON.parse(raw) as FabPosition
|
||||
if (typeof parsed?.x === 'number' && typeof parsed?.y === 'number') {
|
||||
pos.value = parsed
|
||||
clampToViewport()
|
||||
|
||||
const parsed = JSON.parse(raw) as SavedFabOffset
|
||||
if (
|
||||
parsed?.version === POSITION_VERSION
|
||||
&& typeof parsed.dx === 'number'
|
||||
&& typeof parsed.dy === 'number'
|
||||
) {
|
||||
applySavedOffset(parsed.dx, parsed.dy)
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function savePosition() {
|
||||
localStorage.setItem(storageKey, JSON.stringify(pos.value))
|
||||
const base = getDefaultPosition()
|
||||
const payload: SavedFabOffset = {
|
||||
version: POSITION_VERSION,
|
||||
dx: pos.value.x - base.x,
|
||||
dy: pos.value.y - base.y
|
||||
}
|
||||
localStorage.setItem(storageKey, JSON.stringify(payload))
|
||||
}
|
||||
|
||||
function resetToDefault() {
|
||||
pos.value = getDefaultPosition()
|
||||
}
|
||||
|
||||
function handleResize() {
|
||||
clampToViewport()
|
||||
savePosition()
|
||||
try {
|
||||
const raw = localStorage.getItem(storageKey)
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw) as SavedFabOffset
|
||||
if (
|
||||
parsed?.version === POSITION_VERSION
|
||||
&& typeof parsed.dx === 'number'
|
||||
&& typeof parsed.dy === 'number'
|
||||
) {
|
||||
applySavedOffset(parsed.dx, parsed.dy)
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
resetToDefault()
|
||||
}
|
||||
|
||||
function handlePointerDown(event: PointerEvent) {
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-white wrap-fix z-200001 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>
|
||||
<div class="pt-20 pb-24">
|
||||
<MobilePageHeader title="درباره ما" show-back />
|
||||
<div class="mpage-content pb-[calc(var(--app-bottom-chrome-height)+1rem)]">
|
||||
<div class="px-5">
|
||||
<AboutSectionsContent variant="mobile" />
|
||||
</div>
|
||||
@@ -16,8 +11,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import router from '@/router'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
||||
import AboutSectionsContent from '../components/AboutSectionsContent.vue'
|
||||
import AboutSupportActions from '../components/AboutSupportActions.vue'
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="absolute inset-0 bg-cover bg-center opacity-60 z--1" :style="{ backgroundImage: `url(${img})` }" />
|
||||
<div class="bg-white drop-shadow z-[1999] wrap-fix">
|
||||
<div class="py-3 px-3 font-bold flex items-center justify-between">
|
||||
<div class="mpage-header wrap-fix z-[1999]">
|
||||
<div class="py-3 px-3 font-bold flex items-center justify-between w-full">
|
||||
<div>سبد خرید</div>
|
||||
<div v-if="price" class="flex gap-1 items-center text-xs flex-1 justify-end">
|
||||
<span class="text-gray-600 text-xs">قیمت خرید از کشکول گلد:</span>
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<GoldLoading v-model:show="infoVisible" title="" />
|
||||
<div class="px-5 pt-26 pb-30" :class="loading ? 'h-[60vh]' : ''">
|
||||
<div class="px-5 mpage-content pb-[calc(var(--app-bottom-chrome-height)+1rem)]" :class="loading ? 'h-[60vh]' : ''">
|
||||
<template v-if="loading">
|
||||
<TransactionSkeleton v-for="i in 3" :key="i" class="mt-4" />
|
||||
</template>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useWalletAssets } from '@/composables/useWalletAssets'
|
||||
import type { WalletMetalTotal } from '@/models/walletAssets'
|
||||
import { fundPageInventoryCards, formatMetalTotal as buildMetalTotalLabel } from '@/utils/productAssets'
|
||||
import { getLastPrices } from '@/utils/price'
|
||||
import img from '@/assets/img/cash_img.svg'
|
||||
import img from '@/assets/img/user_ly1.svg'
|
||||
import empty from '@/assets/img/cash_empty.svg'
|
||||
|
||||
export interface FundPageContext {
|
||||
@@ -80,7 +80,7 @@ export function useFundPage(_isDesktop: Ref<boolean>) {
|
||||
countBasedInventory,
|
||||
fundHeaderBackground: img,
|
||||
fundEmptyImage: empty,
|
||||
fundEmptyText: 'هنوز محصولی خریداری نکردهاید',
|
||||
fundEmptyText: 'یک گنج جدید بساز!',
|
||||
formatMetalTotal,
|
||||
openFundInfo,
|
||||
goBack,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="fund-page-root">
|
||||
<div class="fund-page-root main-with-bottom-chrome-inner--contained">
|
||||
<DesktopFundView v-show="isDesktop" />
|
||||
<MobileFundView v-show="!isDesktop" />
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-white w-full py-3 px-3 text-black font-bold drop-shadow fixed md:absolute top-0 flex h-12 z-20">
|
||||
<div @click="goBack()">
|
||||
<div class="fund-mobile-root">
|
||||
<div
|
||||
class="fund-mobile-root__header bg-white w-full mobile-top-chrome px-3 pb-3 text-primary font-bold drop-shadow flex min-h-12 z-20"
|
||||
>
|
||||
<div class="cursor-pointer" @click="goBack()">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" />
|
||||
</div>
|
||||
<div class="mr-2">دارایی ها</div>
|
||||
</div>
|
||||
<div class="bg-[#EFCED9] w-screen md:w-full h-[70vh] fixed md:absolute overflow-hidden">
|
||||
|
||||
<div class="fund-mobile-root__hero text-tertiary bg-primary">
|
||||
<img
|
||||
:src="fundHeaderBackground"
|
||||
alt=""
|
||||
class="mt-[-4rem] mr-10 max-w-[22rem] self-end pointer-events-none absolute bottom-0 left-0 opacity-20"
|
||||
class="fund-mobile-root__hero-bg"
|
||||
/>
|
||||
<div class="pt-15 flex flex-col relative z-10">
|
||||
<div class="text-gray-700 font-bold mr-5 z-20">موجودی دارایی ها</div>
|
||||
<div class="fund-mobile-root__hero-content flex flex-col">
|
||||
<div class="font-bold mr-5 z-20">موجودی دارایی ها</div>
|
||||
|
||||
<div
|
||||
v-if="meta?.has_count_based_products"
|
||||
@@ -59,14 +62,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-[20rem]">
|
||||
<div
|
||||
class="bg-gray-100 w-screen md:w-full z-20 relative rounded-t-3xl h-[calc(100vh-20rem-70px)] overflow-y-auto pb-15"
|
||||
style="box-shadow: 0px -2px 4px -2px #0000001a"
|
||||
>
|
||||
<div class="fund-mobile-root__scroll">
|
||||
<div class="fund-mobile-root__panel bg-gray-100 rounded-t-3xl">
|
||||
<div
|
||||
v-if="inventoryCards.length === 0 && !loading"
|
||||
class="w-full h-full flex justify-center items-center"
|
||||
class="w-full min-h-[40vh] flex justify-center items-center"
|
||||
>
|
||||
<div>
|
||||
<img :src="fundEmptyImage" alt="" class="w-70" />
|
||||
@@ -112,3 +112,60 @@ const {
|
||||
goBack,
|
||||
} = useFundPageContext()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fund-mobile-root {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fund-mobile-root__header {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.fund-mobile-root__hero {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
padding-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.fund-mobile-root__hero-bg {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
.fund-mobile-root__hero-content {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
padding: 1rem 0 0;
|
||||
}
|
||||
|
||||
.fund-mobile-root__scroll {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
margin-top: -2rem;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-y: contain;
|
||||
}
|
||||
|
||||
.fund-mobile-root__panel {
|
||||
min-height: 100%;
|
||||
padding-bottom: calc(var(--app-bottom-chrome-height, 80px) + 1rem);
|
||||
box-shadow: 0 -2px 4px -2px #0000001a;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
type="text"
|
||||
/>
|
||||
</a-form-item>
|
||||
<p :class="variant === 'desktop' ? 'dgift-form__hint' : 'font-bold text-xs mt-1'">
|
||||
<p :class="variant === 'desktop' ? 'dgift-form__hint' : 'gift-card-form__hint'">
|
||||
کد کارت هدیه خود را در فیلد بالا وارد کنید و سپس دکمه ثبت را بزنید.
|
||||
</p>
|
||||
<p :class="variant === 'desktop' ? 'dgift-form__case-note' : 'font-bold text-xs mt-3'">
|
||||
<p :class="variant === 'desktop' ? 'dgift-form__case-note' : 'gift-card-form__case-note'">
|
||||
*حساس به حروف بزرگ و کوچک
|
||||
</p>
|
||||
<a-form-item>
|
||||
@@ -39,3 +39,24 @@ withDefaults(defineProps<{ variant?: 'mobile' | 'desktop' }>(), { variant: 'mobi
|
||||
|
||||
const { form, disableButton, registerGiftCard } = useGiftCardPageContext()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.gift-card-form__hint,
|
||||
.gift-card-form__case-note {
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.65;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.gift-card-form__hint {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.gift-card-form__case-note {
|
||||
margin-top: 0.75rem;
|
||||
color: #64748b;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,48 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-white py-3 px-3 text-black font-bold drop-shadow z-[1999] wrap-fix">کارت هدیه</div>
|
||||
<div class="px-5 pt-26 pb-2">
|
||||
<a-form :model="form" class="!mt-5" name="basic" @finish="registerGiftCard">
|
||||
<div class="border-gray-200 border-1 rounded-xl p-5 trans h-40 relative">
|
||||
<bg_main />
|
||||
<div class="z-100 relative">
|
||||
<a-form-item :rules="[required()]" label="کد کارت هدیه" name="code">
|
||||
<BasicInput
|
||||
v-model:value="form.code"
|
||||
place-holder="کد کارت هدیه را وارد کنید"
|
||||
size="large"
|
||||
type="text"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div class="font-bold text-xs mt-1">
|
||||
کد کارت هدیه خود را در فیلد بالا وارد کنید و سپس دکمه ثبت را بزنید.
|
||||
</div>
|
||||
<div class="font-bold text-xs mt-3">*حساس به حروف بزرگ و کوچک</div>
|
||||
</div>
|
||||
<div class="gift-card-mobile">
|
||||
<MobilePageHeader title="کارت هدیه" show-back />
|
||||
|
||||
<div class="gift-card-mobile__content mpage-content px-5 pb-[calc(var(--app-bottom-chrome-height)+1rem)]">
|
||||
<div class="gift-card-mobile__card border-gray-200 border-1 rounded-xl p-5 relative overflow-hidden">
|
||||
<bg_main />
|
||||
<div class="relative z-10">
|
||||
<GiftCardForm variant="mobile" />
|
||||
</div>
|
||||
<a-form-item>
|
||||
<BasicButton
|
||||
:disabled="disableButton"
|
||||
class="w-full py-2 mt-5"
|
||||
html-type="submit"
|
||||
type="primary"
|
||||
>
|
||||
<a-spin v-if="loading" size="small" />
|
||||
<div v-else>ثبت کد</div>
|
||||
</BasicButton>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { loading } from '@/utils'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import Bg_main from '@/components/bg_main.vue'
|
||||
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||
import { required } from '@/utils/inputRules'
|
||||
import { useGiftCardPageContext } from '../composables/useGiftCardPage'
|
||||
|
||||
const { form, disableButton, registerGiftCard } = useGiftCardPageContext()
|
||||
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
||||
import GiftCardForm from '../components/GiftCardForm.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.gift-card-mobile__card {
|
||||
min-height: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="h-35rem overflow-y-auto">
|
||||
<div class="h-35rem overflow-y-auto px-5">
|
||||
<div class=" !overflow-y-scroll bg-white py-3 text-primary font-bold flex justify-evenly">
|
||||
<!-- <div @click="isBuy = false" class="w-full text-center" :class="!isBuy?'text-primary':'text-gray-400'">خرید طلا</div>
|
||||
<div @click="isBuy = true" class="w-full text-center" :class="isBuy?'text-primary':'text-gray-400'">فروش طلا</div>-->
|
||||
@@ -25,11 +25,14 @@
|
||||
<div class="w-full items-center flex flex-col">
|
||||
<div class="text-gray-700 font-bold">{{ !isBuy ? 'قیمت فروش' : 'قیمت خرید' }}</div>
|
||||
<div class="flex gap-1 mt-3 text-xs items-center">
|
||||
<Vue3Odometer
|
||||
:value="displayedGoldPrice"
|
||||
:class="[!isBuy ? 'text-red-700' : 'text-green-700', 'o-text !text-2xl font-bold color-transition']"
|
||||
style="font-family: iran-sans; direction: ltr"
|
||||
/>
|
||||
<div class="gold-live-price">
|
||||
<Vue3Odometer
|
||||
:key="isBuy ? 'buy' : 'sell'"
|
||||
:value="displayedGoldPrice"
|
||||
:class="[!isBuy ? 'text-red-700' : 'text-green-700', 'o-text !text-2xl font-bold color-transition']"
|
||||
style="font-family: iran-sans; direction: ltr"
|
||||
/>
|
||||
</div>
|
||||
ریال
|
||||
</div>
|
||||
</div>
|
||||
@@ -591,13 +594,10 @@ const formatter = (value: number) => {
|
||||
|
||||
const price = computed((): Price => <Price>lastPrice.value ?? 0)
|
||||
|
||||
const displayedGoldPrice = ref<number>(0)
|
||||
|
||||
watch([() => lastPrice.value, isBuy], () => {
|
||||
if (lastPrice.value) {
|
||||
displayedGoldPrice.value = isBuy.value ? (lastPrice.value.buy ?? 0) : (lastPrice.value.sell ?? 0)
|
||||
}
|
||||
}, { immediate: true })
|
||||
const displayedGoldPrice = computed(() => {
|
||||
if (!lastPrice.value) return 0
|
||||
return isBuy.value ? (lastPrice.value.buy ?? 0) : (lastPrice.value.sell ?? 0)
|
||||
})
|
||||
|
||||
async function initPrice() {
|
||||
try {
|
||||
@@ -605,9 +605,6 @@ async function initPrice() {
|
||||
await setIntervalGetAllPrice()
|
||||
loading.value = true
|
||||
price.value = lastPrice.value
|
||||
if (lastPrice.value) {
|
||||
displayedGoldPrice.value = isBuy.value ? (lastPrice.value.buy ?? 0) : (lastPrice.value.sell ?? 0)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
@@ -683,4 +680,14 @@ async function ok() {
|
||||
right: 100%;
|
||||
transform: translate(100%, 0);
|
||||
}
|
||||
|
||||
.gold-live-price {
|
||||
:deep(.odometer) {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
:deep(.odometer-digit) {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
@logout="showLogoutConfirm = true"
|
||||
@open-balance="openWholeBalanceVisible()"
|
||||
/>
|
||||
<div v-show="!isDesktop" class="mt-20">
|
||||
<div v-show="!isDesktop" class="home-mobile-root pb-[calc(var(--app-bottom-chrome-height)+1rem)]">
|
||||
<div class="app-header-scroll-sentinel" aria-hidden="true" />
|
||||
<div
|
||||
v-if="!loading"
|
||||
class="bg-white w-full md:w-auto h-fit px-5 m-[-1px]"
|
||||
@@ -335,7 +336,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in mt-6 pb-25">
|
||||
<div class=" w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in mt-6 pb-[calc(var(--app-bottom-chrome-height)+1rem)]">
|
||||
<div class="text-center w-full">
|
||||
<div>بالاترین قیمت امروز</div>
|
||||
<div v-if="lastPrice" class="flex flex-col items-center mt-3">
|
||||
@@ -363,32 +364,7 @@
|
||||
<div v-else class="bg-white w-full md:w-auto h-[80vh] px-5 m-[-1px] flex justify-center">
|
||||
<HomeSkeleton class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isDesktop"
|
||||
:class="y > 50 ? 'bg-white shadow opacity-95' : ''"
|
||||
class="px-4 py-2 top-0 z-5 wrap-fix background-color-transition"
|
||||
>
|
||||
<div class="flex justify-between my-2">
|
||||
<div class="flex items-center gap-2" @click.stop="router.push({ name: 'user' })">
|
||||
<!-- <img :src="testimg" alt="" class="w-[24px] h-[24px]">-->
|
||||
<div
|
||||
class="z-100000 rounded-full border-1"
|
||||
:class="y > 50 ? '' : 'border-black'"
|
||||
>
|
||||
<Icon icon="vuesax-linear:user" size="20" />
|
||||
</div>
|
||||
<div :class="y > 50 ? '' : ''" class="color-transition font-bold">
|
||||
{{ userStore.userInfo?.name }}
|
||||
</div>
|
||||
</div>
|
||||
<Icon
|
||||
:color="y > 50 ? '' : ''"
|
||||
class="color-transition"
|
||||
icon="vuesax-linear:logout"
|
||||
@click="showLogoutConfirm = true"
|
||||
/>
|
||||
</div>
|
||||
<AppHeader @logout="showLogoutConfirm = true" />
|
||||
</div>
|
||||
<DialogVant v-model:show="showLogoutConfirm" :closeOnClickOverlay="false" :timeOut="-1">
|
||||
<!-- <template v-slot:img>
|
||||
@@ -494,7 +470,7 @@ import { usePrice, usePriceWithoutType, useTowDecimal } from '../../../utils/use
|
||||
import {computed, createVNode, onActivated, onMounted, onUnmounted, ref, watch} from 'vue'
|
||||
import router from '@/router'
|
||||
import * as echarts from 'echarts'
|
||||
import { useIntersectionObserver, useWindowScroll } from '@vueuse/core'
|
||||
import { useIntersectionObserver } from '@vueuse/core'
|
||||
import { loading, removeToken } from '@/utils'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { useWalletStore } from '@/store/walletStore'
|
||||
@@ -526,6 +502,7 @@ import { message, Modal } from 'ant-design-vue'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import BasicModal from '@/components/BasicPacks/BasicModal.vue'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import AppHeader from '@/views/base/home/components/AppHeader.vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const walletStore = useWalletStore()
|
||||
@@ -533,7 +510,6 @@ const isDesktop = useDesktopBreakpoint()
|
||||
const chartLoading = ref<boolean>(false)
|
||||
const openModal = ref<boolean>(false)
|
||||
const wholeBalanceVisible = ref<boolean>(false)
|
||||
const { x, y } = useWindowScroll()
|
||||
const series = ref<Chart[]>([{ data: [] }])
|
||||
const key = ref<number>(0)
|
||||
const main = ref(null)
|
||||
@@ -939,6 +915,20 @@ function copyCode() {
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.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 {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.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)));
|
||||
}
|
||||
|
||||
.home-banner {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,221 +1,14 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<div ref="el" class="px-3 pt-15"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<BasicSelect v-model:value="selectedProduct" :items="items"
|
||||
class="w-full"
|
||||
key-name="id"
|
||||
:show-search="false"
|
||||
value-name="title"
|
||||
size="large"
|
||||
placeholder="لطفا محصول را انتخاب کنید"
|
||||
:has-tooltip="true"/>
|
||||
<BasicButton :disabled-b="selectedProduct==null" type="primary" @click="handleAdd">افزودن</BasicButton>
|
||||
</div>
|
||||
<div class="h-70vh rounded-lg bg-white mt-2 border-gray-200 border-1 overflow-y-auto p-2">
|
||||
<div v-for="(val,index) in selectedProducts" class="rounded-lg p-2 bg-gray-100 mb-2">
|
||||
<div class="flex">
|
||||
<div class="bg-gray-100 m-2 rounded-lg w-28 h-28 flex justify-center p-2">
|
||||
<img v-if="val.product.group_type=='گوی'" :src="goyImg">
|
||||
<img v-if="val.product.group_type=='مکعب'" :src="mokaabImg">
|
||||
<img v-if="val.product.group_type=='سکه'" :src="sekkeImg">
|
||||
<img v-if="val.product.group_type=='شمش ونوس'" :src="venusImg">
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div class="flex justify-between">
|
||||
<div class="font-bold text-lg">
|
||||
{{ val.product.title }}
|
||||
</div>
|
||||
<BasicButton icon="vuesax-linear:trash" icon-class="text-red" @click="handleRemove(val.product.id)"/>
|
||||
</div>
|
||||
<div class="flex mt-3 justify-between items-center">
|
||||
<div>تعداد موجودی:</div>
|
||||
<div class="flex gap-1 items-center">
|
||||
<div class="font-bold">{{ val.product.wallet.gold }}</div>
|
||||
<div>{{ val.product.unit }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center mt-2">
|
||||
<div>تعداد درخواستی:</div>
|
||||
<div class="flex gap-1 items-center w-40%">
|
||||
<BasicInput v-model:value="val.count" addon-before="عدد"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BasicButton :disabled-b="selectedProducts.length==0" class="w-full absolute bottom-10 mt-2"
|
||||
@click="handelFirstSubmit()">ثبت
|
||||
</BasicButton>
|
||||
</div>
|
||||
<!-- <ActionSheet v-model:show="editPassVisible">salam</ActionSheet>-->
|
||||
</div>
|
||||
<BasicActionSheet v-model:show="showAddress">
|
||||
<div class="h-120 flex flex-col ">
|
||||
<div class="text-center">
|
||||
انتخاب آدرس
|
||||
</div>
|
||||
<div class="overflow-y-scroll h-85">
|
||||
<ARadioGroup v-model:value="selectedAddressID">
|
||||
<div class="flex flex-col ">
|
||||
<div class="text-base rounded-lg p-4 m-2 shadow-md font-medium" v-for="item in address">
|
||||
<ARadio :value="item.id">{{ item.province }} / {{ item.city }} / {{ item.address }}</ARadio>
|
||||
</div>
|
||||
</div>
|
||||
</ARadioGroup>
|
||||
</div>
|
||||
<div class="flex gap-2 pt-2">
|
||||
|
||||
<BasicButton class=" " @click="showAddress=false;addAddressVisible=true">افزودن آدرس +</BasicButton>
|
||||
<BasicButton class="w-2/3 " type="primary" @click="handelsubmit">ثبت نهایی</BasicButton>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</BasicActionSheet>
|
||||
<BasicActionSheet v-model:show="addAddressVisible" >
|
||||
<div class=" ">
|
||||
<New_EditAddress @onOk="handleAddress"></New_EditAddress>
|
||||
</div>
|
||||
|
||||
</BasicActionSheet>
|
||||
<MobileNewRequestView v-if="!isDesktop" />
|
||||
<DesktopNewRequestView v-if="isDesktop" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useNewPhysicalRequestPage } from '../composables/useNewPhysicalRequestPage'
|
||||
import { usePhysicalReceiveShellContext } from '../composables/usePhysicalReceiveShell'
|
||||
import DesktopNewRequestView from '../desktop/DesktopNewRequestView.vue'
|
||||
import MobileNewRequestView from '../mobile/MobileNewRequestView.vue'
|
||||
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import router from '@/router'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import BasicSelect from '@/components/BasicPacks/BasicSelect.vue'
|
||||
import {onMounted, ref} from 'vue'
|
||||
import {loading} from '@/utils'
|
||||
import {getProductInventory} from '@/api/products'
|
||||
import type {CountPInventory, PInventory} from '@/models/products'
|
||||
import goyImg from '@/assets/img/goyImg.svg'
|
||||
import mokaabImg from '@/assets/img/mokaabImg.svg'
|
||||
import sekkeImg from '@/assets/img/sekkeImg.svg'
|
||||
import venusImg from "@/assets/img/venousImg.svg"
|
||||
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||
import {cloneDeep} from 'lodash'
|
||||
import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue"
|
||||
import {getCity, tableAddress} from "@/api/address"
|
||||
import type {Address} from "@/models/address"
|
||||
import Item from "@/views/base/address/components/Item.vue"
|
||||
import {physicalReceiveApi} from "@/api/physicalreceive"
|
||||
import {message} from "ant-design-vue"
|
||||
import {required} from "@/utils/inputRules"
|
||||
import BasicTextArea from "@/components/BasicPacks/BasicTextArea.vue"
|
||||
import New_EditAddress from "@/views/base/address/components/New_EditAddress.vue"
|
||||
|
||||
const items = ref<PInventory[]>()
|
||||
const baseItems = ref<PInventory[]>()
|
||||
const selectedAddressID = ref<number>()
|
||||
const selectedProduct = ref<number>()
|
||||
const productsByWalletId = ref<Array>([])
|
||||
const address = ref<Address>()
|
||||
const selectedProducts = ref<CountPInventory[]>([])
|
||||
const showAddress = ref<boolean>(false)
|
||||
const addAddressVisible = ref<boolean>(false)
|
||||
|
||||
function handleAdd() {
|
||||
console.log(selectedProducts.value)
|
||||
selectedProducts.value?.push({
|
||||
count: 0,
|
||||
product: items.value?.find(x => x.id === selectedProduct.value)
|
||||
})
|
||||
items.value?.splice(items.value?.findIndex(x => x.id === selectedProduct.value), 1)
|
||||
selectedProduct.value = undefined
|
||||
}
|
||||
function handleAddress() {
|
||||
getAddress()
|
||||
addAddressVisible.value = false
|
||||
showAddress.value = true
|
||||
}
|
||||
|
||||
function handleRemove(id: number) {
|
||||
const findItem = baseItems.value?.find(x => x.id === id)
|
||||
console.log(baseItems.value, id)
|
||||
if (findItem) {
|
||||
items.value?.push(findItem)
|
||||
}
|
||||
selectedProducts.value?.splice(selectedProducts.value?.findIndex(x => x.product.id === id), 1)
|
||||
}
|
||||
|
||||
async function initProductWallet() {
|
||||
try {
|
||||
loading.value = true
|
||||
items.value = (await getProductInventory()).rows.filter(x => (x.title !== 'آبشده نقدی' && x.wallet?.gold && x.wallet?.gold > 0))
|
||||
baseItems.value = cloneDeep(items.value)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handelFirstSubmit() {
|
||||
try {
|
||||
const zeroAmount = selectedProducts.value.map(item => item.count <= 0)
|
||||
if (zeroAmount.includes(true)) {
|
||||
message.error(`لطفاً تعداد درخواستی محصول را وارد کنید.`)
|
||||
}
|
||||
else {
|
||||
showAddress.value = true
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function handelsubmit() {
|
||||
try {
|
||||
loading.value = true
|
||||
productsByWalletId.value = selectedProducts.value.map((val) => {
|
||||
return {
|
||||
wallet_id: val.product.wallet.id,
|
||||
value: val.count
|
||||
}
|
||||
})
|
||||
await physicalReceiveApi({
|
||||
address_id: selectedAddressID.value,
|
||||
wallets: productsByWalletId.value
|
||||
}
|
||||
)
|
||||
await initProductWallet()
|
||||
message.success("درخواست شما با موفقیت ارسال شد")
|
||||
selectedProducts.value = []
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
showAddress.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await initProductWallet()
|
||||
await getAddress()
|
||||
})
|
||||
async function getAddress(){
|
||||
let {rows} = await tableAddress({page: 0, limit: 150})
|
||||
address.value = rows
|
||||
}
|
||||
|
||||
const { isDesktop } = usePhysicalReceiveShellContext()
|
||||
useNewPhysicalRequestPage()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/*.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}*/
|
||||
/*.trans{
|
||||
transition: height 1s;
|
||||
}*/
|
||||
</style>
|
||||
|
||||
@@ -1,59 +1,18 @@
|
||||
<template>
|
||||
<div class="physical-receive-page-root">
|
||||
<DesktopPhysicalReceiveListView
|
||||
v-show="isDesktop"
|
||||
:request-items="requestItems"
|
||||
@add="openAdd"
|
||||
/>
|
||||
<div v-show="!isDesktop">
|
||||
<div class="absolute inset-0 bg-cover bg-center opacity-60" :style="{ backgroundImage: `url(${img})` }" />
|
||||
<div class="bg-white w-full transaction-list py-3 px-3 font-bold drop-shadow flex justify-between z-10">
|
||||
<div>لیست دریافت فیزیکی</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4 pt-13 pb-35 px-2">
|
||||
<RequestItem v-for="item in requestItems" :key="item.id" :item="item" />
|
||||
</div>
|
||||
<Transition mode="out-in" name="fade">
|
||||
<div
|
||||
class="wrap-fix bottom-0 h-[19vh] bg-[#FBFBFC] shadow-lg rounded-3xl flex justify-center pt-3 z-200"
|
||||
style="box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 26px 11px"
|
||||
@click="openAdd"
|
||||
>
|
||||
<BasicIcon icon="vuesax-linear:add" />
|
||||
<div class="cursor-pointer">افزودن درخواست جدید</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
<MobileRequestListView v-show="!isDesktop" />
|
||||
<DesktopPhysicalReceiveListView
|
||||
v-show="isDesktop"
|
||||
:request-items="requestItems"
|
||||
@add="openAdd"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { getPhysicalReceiveList } from '@/api/physicalreceive'
|
||||
import type { Physicalreceive } from '@/models/physicalreceive'
|
||||
import RequestItem from '@/views/base/physicalReceive/components/RequestItem.vue'
|
||||
import router from '@/router'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import img from '@/assets/img/user_ly1.svg'
|
||||
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||
import { usePhysicalReceiveList } from '../composables/usePhysicalReceiveList'
|
||||
import { usePhysicalReceiveShellContext } from '../composables/usePhysicalReceiveShell'
|
||||
import DesktopPhysicalReceiveListView from '../desktop/DesktopPhysicalReceiveListView.vue'
|
||||
import MobileRequestListView from '../mobile/MobileRequestListView.vue'
|
||||
|
||||
const isDesktop = useDesktopBreakpoint()
|
||||
const requestItems = ref<Physicalreceive[]>([])
|
||||
|
||||
const openAdd = () => {
|
||||
router.push({ name: 'new' })
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const { rows } = await getPhysicalReceiveList()
|
||||
requestItems.value = rows
|
||||
})
|
||||
const { isDesktop } = usePhysicalReceiveShellContext()
|
||||
const { requestItems, openAdd } = usePhysicalReceiveList()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.physical-receive-page-root {
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,196 +1,67 @@
|
||||
<template>
|
||||
<div class="flex justify-center items-center text-5">
|
||||
در حال بروزرسانی...
|
||||
<div
|
||||
class="physical-receive-page-root main-with-bottom-chrome-inner--contained"
|
||||
:class="{ 'physical-receive-page-root--desktop': isDesktop }"
|
||||
>
|
||||
<MobilePhysicalReceiveChrome v-show="!isDesktop" />
|
||||
|
||||
<div :class="isDesktop ? '' : 'physical-receive-page-root__shell'">
|
||||
<div :class="isDesktop ? '' : 'physical-receive-page-root__content'">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!--<template>-->
|
||||
<!-- <div class="bottom-0 fixed top-0">-->
|
||||
<!-- <img :src="img" alt="" class=" w-inherit "-->
|
||||
<!-- :style="{ backgroundRepeat: 'repeat', }"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<!-- <a-spin :spinning="loading">-->
|
||||
<script lang="ts" setup>
|
||||
import { usePhysicalReceiveShell } from './composables/usePhysicalReceiveShell'
|
||||
import MobilePhysicalReceiveChrome from './mobile/MobilePhysicalReceiveChrome.vue'
|
||||
|
||||
<!-- <div class=" !h-full overflow-y-scroll">-->
|
||||
const { isDesktop } = usePhysicalReceiveShell()
|
||||
</script>
|
||||
|
||||
<!-- <div class="w-full flex justify-center top-0 py-5" style="align-self: center">-->
|
||||
<!-- <ARadioGroup @change="changeTab" v-model:value="activekey" button-style="solid">-->
|
||||
<!-- <ARadioButton class="cats" value="همه">همه</ARadioButton>-->
|
||||
<!-- <ARadioButton class="cats" v-for="item in typeItems.filter(a => a != 'آبشده')" :value="item">{{ item }}</ARadioButton>-->
|
||||
<!-- </ARadioGroup>-->
|
||||
<!-- </div>-->
|
||||
<style scoped>
|
||||
.physical-receive-page-root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<!-- <div class="mx-5 pb-30 pt-2" style="color: #1b2b07">-->
|
||||
<!-- <div class="mt-3" v-for="item in items?.filter(a => a.group_type != 'آبشده')">-->
|
||||
<!-- <ProductItem :ProductItem="item" @click="clickProduct(item)"></ProductItem>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-spin>-->
|
||||
<!-- <!– <BasicActionSheet v-model:show="showFilter">–>-->
|
||||
<!-- <!– <indexGold ></indexGold>–>-->
|
||||
<!-- <!– </BasicActionSheet>–>-->
|
||||
.physical-receive-page-root__shell {
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<!-- <BasicActionSheet placement="bottom" v-model:show="showFilter">-->
|
||||
<!-- <div class="">-->
|
||||
<!--<!– <indexGold v-if="selectedProduct?.group_type=='آبشده'" :ProductItem="selectedProduct"></indexGold>–>-->
|
||||
<!-- <ReceiveDealProduct :ProductItem="selectedProduct"></ReceiveDealProduct>-->
|
||||
<!-- </div>-->
|
||||
<!-- </BasicActionSheet>-->
|
||||
.physical-receive-page-root__content {
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<!--</template>-->
|
||||
.physical-receive-page-root__content > * {
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<!--<script setup lang="ts">-->
|
||||
<!--import { onMounted, onUnmounted, ref, watch } from 'vue'-->
|
||||
<!--import router from '@/router'-->
|
||||
<!--import ProductItem from '@/views/base/products/components/ProductItem.vue'-->
|
||||
<!--import { getProducts } from '@/api/products'-->
|
||||
<!--import {-->
|
||||
<!-- getLastPrices,-->
|
||||
<!-- lastPrice,-->
|
||||
<!-- stopPriceInterval-->
|
||||
<!--} from '@/utils/price'-->
|
||||
<!--import { loading } from '@/utils'-->
|
||||
<!--import { Price } from '@/models/home'-->
|
||||
<!--import type { Product } from '@/models/products'-->
|
||||
<!--import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'-->
|
||||
<!--import { cloneDeep } from 'lodash'-->
|
||||
<!--import img from "@/assets/img/user_ly1.svg";-->
|
||||
<!--import ReceiveDealProduct from '@/views/base/physicalReceive/components/ReceiveDealProduct.vue'-->
|
||||
.physical-receive-page-root--desktop {
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--const activekey = ref('آبشده')-->
|
||||
<!--const showFilter = ref()-->
|
||||
<!--const selectedProduct = ref<Product>()-->
|
||||
|
||||
<!--function clickProduct(item: Product) {-->
|
||||
<!-- if(item?.show){-->
|
||||
<!-- showFilter.value = true-->
|
||||
<!-- selectedProduct.value = cloneDeep(item)-->
|
||||
<!-- }-->
|
||||
<!--}-->
|
||||
|
||||
<!--function changeTab() {-->
|
||||
<!-- if (activekey.value != 'همه') {-->
|
||||
<!-- items.value = firstItems.value?.filter((val) =>-->
|
||||
<!-- val.group_type == activekey.value-->
|
||||
<!-- )-->
|
||||
<!-- } else items.value = firstItems.value-->
|
||||
|
||||
<!--}-->
|
||||
|
||||
<!--const firstItems = ref<Product[]>()-->
|
||||
<!--const price = ref<Price>()-->
|
||||
<!--const items = ref<Product[]>()-->
|
||||
<!--const typeItems = ref([])-->
|
||||
<!--onMounted(async () => {-->
|
||||
<!-- try {-->
|
||||
<!-- loading.value = true-->
|
||||
<!-- items.value = await getProducts()-->
|
||||
<!-- items.value?.forEach((val, index) => {-->
|
||||
<!-- if (!typeItems.value.includes(val.group_type)){-->
|
||||
<!-- typeItems.value.push(val.group_type)-->
|
||||
<!-- }-->
|
||||
<!-- // typeItems.value[index] = val.group_type-->
|
||||
<!-- })-->
|
||||
<!-- firstItems.value = items.value-->
|
||||
<!-- await initPrice()-->
|
||||
<!-- // await setIntervalGetLastPrice()-->
|
||||
<!-- // console.log('run product')-->
|
||||
|
||||
<!-- } catch (e) {-->
|
||||
<!-- console.log(e)-->
|
||||
<!-- } finally {-->
|
||||
<!-- loading.value = false-->
|
||||
<!-- if (router.currentRoute.value.query.cat=="coin"){-->
|
||||
<!-- activekey.value="سکه"-->
|
||||
<!-- changeTab()-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- if (router.currentRoute.value.query.state) {-->
|
||||
<!-- if (router.currentRoute.value.query.product) {-->
|
||||
<!-- clickProduct(items.value?.find(x => x.title === 'سکه'))-->
|
||||
<!-- } else {-->
|
||||
<!-- clickProduct(items.value?.find(x => x.title === 'آبشده نقدی'))-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!--})-->
|
||||
<!--onUnmounted(() => {-->
|
||||
<!-- stopPriceInterval()-->
|
||||
<!-- // console.log('stop product')-->
|
||||
<!--})-->
|
||||
|
||||
<!--async function initPrice() {-->
|
||||
<!-- try {-->
|
||||
<!-- // chartLoading.value = true-->
|
||||
<!-- await getLastPrices()-->
|
||||
<!-- // await setIntervalGetPrice()-->
|
||||
<!-- loading.value = true-->
|
||||
<!-- price.value = lastPrice.value-->
|
||||
<!-- } catch (e) {-->
|
||||
<!-- console.log(e)-->
|
||||
<!-- } finally {-->
|
||||
<!-- loading.value = false-->
|
||||
<!-- // chartLoading.value = false-->
|
||||
<!-- }-->
|
||||
<!--}-->
|
||||
<!--</script>-->
|
||||
|
||||
<!--<style lang="less">-->
|
||||
<!--.cats{-->
|
||||
<!-- font-size: x-small;-->
|
||||
<!--}-->
|
||||
<!--.cats:hover span {-->
|
||||
<!-- --un-text-opacity: 1;-->
|
||||
<!-- color: rgba(206, 163, 76, var(--un-text-opacity))!important;-->
|
||||
<!--}-->
|
||||
|
||||
<!--</style>-->
|
||||
|
||||
|
||||
<!--<!–-->
|
||||
<!--<template>-->
|
||||
<!-- <div style="max-height: calc(100vh - 50px)">-->
|
||||
<!-- <div class="bg-white py-3 px-3 z-100000 text-primary font-bold drop-shadow flex flex-col wrap-fix">-->
|
||||
<!-- <div class="flex">-->
|
||||
<!-- <div @click="router.back()">-->
|
||||
<!-- <BasicIcon icon="vuesax-linear:arrow-right-1"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-if="route.name=='list'" class="mr-2">دریافت طلای فیزیکی</div>-->
|
||||
<!-- <div v-else class="mr-2">دریافت طلای فیزیکی-درخواست جدید</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div>-->
|
||||
<!-- <router-view></router-view>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!--</template>-->
|
||||
|
||||
<!--<script lang="ts" setup>-->
|
||||
<!--import {ref} from "vue";-->
|
||||
<!--import RequestList from "@/views/base/physicalReceive/components/RequestList.vue";-->
|
||||
<!--import NewRequest from "@/views/base/physicalReceive/components/NewRequest.vue";-->
|
||||
<!--import router from "@/router";-->
|
||||
<!--import BasicIcon from "@/components/BasicPacks/BasicIcon.vue";-->
|
||||
<!--import {useRoute} from "vue-router";-->
|
||||
|
||||
<!--const activekey=ref(1)-->
|
||||
<!--const route=useRoute()-->
|
||||
<!--</script>-->
|
||||
|
||||
<!--<style scoped>-->
|
||||
<!--/*.fade-enter-active,-->
|
||||
<!--.fade-leave-active {-->
|
||||
<!-- transition: opacity 0.2s ease;-->
|
||||
<!--}-->
|
||||
|
||||
<!--.fade-enter-from,-->
|
||||
<!--.fade-leave-to {-->
|
||||
<!-- opacity: 0;-->
|
||||
<!--}*/-->
|
||||
<!--/*.trans{-->
|
||||
<!-- transition: height 1s;-->
|
||||
<!--}*/-->
|
||||
<!--</style>-->
|
||||
<!--–>-->
|
||||
<style lang="less">
|
||||
.physical-receive-page-root.main-with-bottom-chrome-inner {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,90 +1,145 @@
|
||||
<template>
|
||||
<div @click="infoVisible = true" class="shadow mb-2 p-2 rounded-lg">
|
||||
<div v-if="variant === 'desktop'" class="dptr-card">
|
||||
<p class="dptr-card__price">{{ usePrice(item.target_price) }}</p>
|
||||
|
||||
<div class="dptr-card__meta">
|
||||
<div class="dptr-card__row">
|
||||
<span class="dptr-card__label">جهت قیمت:</span>
|
||||
<span class="dptr-card__value">{{ returnPriceCondition(item.price_condition) }}</span>
|
||||
</div>
|
||||
<div class="dptr-card__row">
|
||||
<span class="dptr-card__label">نوع هشدار:</span>
|
||||
<span class="dptr-card__value">{{ returnAction(item.action) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dptr-card__actions">
|
||||
<button
|
||||
type="button"
|
||||
class="dptr-card__icon-btn dptr-card__icon-btn--edit"
|
||||
@click.stop="emits('edit', item)"
|
||||
>
|
||||
<BasicIcon color="green" icon="vuesax-linear:edit" size="18" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="dptr-card__icon-btn dptr-card__icon-btn--danger"
|
||||
@click.stop="deleteConfirmVisible = true"
|
||||
>
|
||||
<BasicIcon color="red" icon="vuesax-linear:trash" size="18" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dptr-card__footer">
|
||||
<span class="dptr-card__date">{{ toJalali(item.created_at) }}</span>
|
||||
<a-switch
|
||||
v-model:checked="isActive"
|
||||
checked-children="فعال"
|
||||
un-checked-children="غیر فعال"
|
||||
@change="changeActive"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogVant v-model:show="deleteConfirmVisible" :closeOnClickOverlay="false" :timeOut="-1" teleport="body">
|
||||
<template #title>
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="p-5 text-sm">آیا از حذف هشدار اطمینان دارید؟</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #button>
|
||||
<div class="text-center text-red flex items-center justify-center" @click="deleteItem">
|
||||
<a-spin :spinning="deleteLoading" size="small">تایید</a-spin>
|
||||
</div>
|
||||
<div class="text-center" @click="deleteConfirmVisible = false">انصراف</div>
|
||||
</template>
|
||||
</DialogVant>
|
||||
</div>
|
||||
|
||||
<div v-else @click="infoVisible = true" class="shadow mb-2 p-2 rounded-lg">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<div class="font-bold text-base mb-2">{{usePrice(item.target_price) }}</div>
|
||||
<div class="font-bold text-base mb-2">{{ usePrice(item.target_price) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 ">
|
||||
<BasicIcon color="red" icon="vuesax-linear:trash" @click.stop="deleteConfirmVisible = true" class="p-1"/>
|
||||
<BasicIcon color="green" icon="vuesax-linear:edit" @click.stop="emits('edit',item)" class="p-1"/>
|
||||
<div class="flex gap-2">
|
||||
<BasicIcon
|
||||
class="p-1"
|
||||
color="red"
|
||||
icon="vuesax-linear:trash"
|
||||
@click.stop="deleteConfirmVisible = true"
|
||||
/>
|
||||
<BasicIcon
|
||||
class="p-1"
|
||||
color="green"
|
||||
icon="vuesax-linear:edit"
|
||||
@click.stop="emits('edit', item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 mt-2">
|
||||
<BasicInfoItem is-line title="جهت قیمت" :value="returnPriceCondition(item.price_condition)"/>
|
||||
|
||||
<BasicInfoItem is-line title="نوع هشدار" :value="returnAction(item.action)"/>
|
||||
<BasicInfoItem is-line title="جهت قیمت" :value="returnPriceCondition(item.price_condition)" />
|
||||
<BasicInfoItem is-line title="نوع هشدار" :value="returnAction(item.action)" />
|
||||
</div>
|
||||
<a-divider class="!my-3" />
|
||||
|
||||
<div class="flex justify-between mt-1">
|
||||
<div class="mb-2">{{toJalali(item.created_at)}}</div>
|
||||
<a-switch v-model:checked="isActive" checked-children="فعال" un-checked-children="غیر فعال" @change="changeActive"/>
|
||||
<div class="mb-2">{{ toJalali(item.created_at) }}</div>
|
||||
<a-switch
|
||||
v-model:checked="isActive"
|
||||
checked-children="فعال"
|
||||
un-checked-children="غیر فعال"
|
||||
@change="changeActive"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- <teleport to="app">-->
|
||||
<DialogVant v-model:show="deleteConfirmVisible" teleport="body" :closeOnClickOverlay="false" :timeOut="-1">
|
||||
<!-- <template v-slot:img>
|
||||
<img :src="registerDelayImage" alt="" class="opacity-80">
|
||||
</template>-->
|
||||
|
||||
<template v-slot:title>
|
||||
<DialogVant v-model:show="deleteConfirmVisible" :closeOnClickOverlay="false" :timeOut="-1" teleport="body">
|
||||
<template #title>
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="p-5 text-sm">
|
||||
آیا از حذف هشدار اطمینان دارید؟
|
||||
</div>
|
||||
<div class="p-5 text-sm">آیا از حذف هشدار اطمینان دارید؟</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:button>
|
||||
<template #button>
|
||||
<div class="text-center text-red flex items-center justify-center" @click="deleteItem">
|
||||
<a-spin :spinning="deleteLoading" size="small">
|
||||
تایید
|
||||
</a-spin>
|
||||
</div>
|
||||
<div class="text-center" @click="deleteConfirmVisible = false">
|
||||
انصراف
|
||||
<a-spin :spinning="deleteLoading" size="small">تایید</a-spin>
|
||||
</div>
|
||||
<div class="text-center" @click="deleteConfirmVisible = false">انصراف</div>
|
||||
</template>
|
||||
|
||||
</DialogVant>
|
||||
<!-- </teleport>-->
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { usePrice, usePriceWithoutType } from '../../../../utils/usePrice'
|
||||
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import { toJalali, toJalaliNotime, toJalaliOutOfUTC, toTime } from '../../../../utils/useDateTime'
|
||||
import { usePrice } from '../../../../utils/usePrice'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { toJalali } from '../../../../utils/useDateTime'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import type { PriceTrigger } from '@/models/PriceTrigger'
|
||||
import BasicInfoItem from '@/components/BasicPacks/BasicInfoItem.vue'
|
||||
import DialogVant from '@/components/DialogVant.vue'
|
||||
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
|
||||
import { deletePriceTrigger, updatePriceTrigger } from '@/api/triggerPrice'
|
||||
import { loading } from '@/utils'
|
||||
|
||||
const infoVisible = ref<boolean>(false)
|
||||
|
||||
const isActive = ref<boolean>(false)
|
||||
|
||||
const infoVisible = ref(false)
|
||||
const isActive = ref(false)
|
||||
const deleteLoading = ref(false)
|
||||
const deleteConfirmVisible = ref(false)
|
||||
|
||||
interface Prop {
|
||||
item: PriceTrigger
|
||||
}
|
||||
onMounted(()=> isActive.value = props.item.is_active)
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item: PriceTrigger
|
||||
variant?: 'mobile' | 'desktop'
|
||||
}>(),
|
||||
{ variant: 'mobile' },
|
||||
)
|
||||
|
||||
const emits = defineEmits(['edit', 'refresh'])
|
||||
|
||||
onMounted(() => {
|
||||
isActive.value = props.item.is_active
|
||||
})
|
||||
|
||||
function changeActive(val: boolean) {
|
||||
console.log(val)
|
||||
try {
|
||||
loading.value = true
|
||||
updatePriceTrigger(props?.item.id, {is_active: val})
|
||||
updatePriceTrigger(props?.item.id, { is_active: val })
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
isActive.value = !isActive.value
|
||||
@@ -95,15 +150,13 @@ function changeActive(val: boolean) {
|
||||
|
||||
function returnPriceCondition(val: string) {
|
||||
switch (val) {
|
||||
case '==': return 'مساوی'
|
||||
case '<': return 'کوچکتر'
|
||||
case '>': return 'بزرگتر'
|
||||
case '==':
|
||||
return 'مساوی'
|
||||
case '<':
|
||||
return 'کوچکتر'
|
||||
case '>':
|
||||
return 'بزرگتر'
|
||||
}
|
||||
/* if (val === '=='){
|
||||
return 'مساوی'
|
||||
} else if (val === '<'){
|
||||
return 'کوچکتر'
|
||||
}*/
|
||||
}
|
||||
|
||||
async function deleteItem() {
|
||||
@@ -121,41 +174,12 @@ async function deleteItem() {
|
||||
|
||||
function returnAction(val: string) {
|
||||
switch (val) {
|
||||
case 'sms': return 'پیامک'
|
||||
case 'email': return 'ایمیل'
|
||||
case 'notify': return 'نوتیفیکیشن'
|
||||
// case '==': return 'مساوی'
|
||||
case 'sms':
|
||||
return 'پیامک'
|
||||
case 'email':
|
||||
return 'ایمیل'
|
||||
case 'notify':
|
||||
return 'نوتیفیکیشن'
|
||||
}
|
||||
/* if (val === '=='){
|
||||
return 'مساوی'
|
||||
} else if (val === '<'){
|
||||
return 'کوچکتر'
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
const props = withDefaults(defineProps<Prop>(), {})
|
||||
const emits = defineEmits(['edit','refresh'])
|
||||
|
||||
const isUp = computed(() => props.item.type === 3 || props.item.type === 1 || props.item.type === 8)
|
||||
const isGold = computed(() => props.item.type === 1 || props.item.type === 2 || props.item.type === 10)
|
||||
/*const title = computed(() => {
|
||||
if (isUp.value) {
|
||||
if (isGold.value) {
|
||||
return 'واریز ریالی'
|
||||
} else {
|
||||
return 'خرید طلا'
|
||||
}
|
||||
} else {
|
||||
if (isGold.value) {
|
||||
return 'برداشت ریالی'
|
||||
} else {
|
||||
return 'فروش طلا'
|
||||
}
|
||||
}
|
||||
})*/
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,197 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-white py-3 px-3 text-primary font-bold drop-shadow wrap-fix flex z-10">
|
||||
<div @click="router.back()">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" />
|
||||
</div>
|
||||
<div class="mr-2">هشدارهای فعلی</div>
|
||||
</div>
|
||||
<!-- <a-spin :spinning="loading">-->
|
||||
<div ref="el" :class="loading? 'h-screen': ''" class="px-3 pt-16 pb-2 overflow-y-scroll"
|
||||
style="max-height: calc(100vh - 50px)">
|
||||
<div class="price-trigger-page-root">
|
||||
<DesktopPriceTriggerView v-show="isDesktop" />
|
||||
<MobilePriceTriggerView v-show="!isDesktop" />
|
||||
|
||||
<list-anim v-if="!loading" class="pb-12vh">
|
||||
<Item v-for="item in items" :key="item.id" :item="item" @edit="openEdit" @refresh="initItems" />
|
||||
<!-- <TransactionSkeleton v-if="loadingMore" class="mt-1 mb-8" />-->
|
||||
</list-anim>
|
||||
<div v-if="loading">
|
||||
<TransactionSkeleton v-for="i in 13" :key="i" class="mt-1" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- </a-spin>-->
|
||||
<BasicActionSheet v-model:show="addTriggerVisible" teleport="body">
|
||||
<a-form ref="formRef" :model="form" class="!mt-5" name="basic"
|
||||
@finish="ok()">
|
||||
<div class="flex flex-col gap-4 pb-12 w-full">
|
||||
<div class="font-bold text-lg">هشدار قیمت</div>
|
||||
<a-form-item :rules="[required()]" class="home_appear_fade_in_down" label="جهت قیمت" name="price_condition">
|
||||
<BasicSelect :items="typeItems" v-model:value="form.price_condition" :show-search="false" value-name="value"
|
||||
key-name="key"
|
||||
placeholder="انتخاب نوع جهت قیمت"
|
||||
size="large"></BasicSelect>
|
||||
</a-form-item>
|
||||
<a-form-item :rules="[required()]" class="home_appear_fade_in_down" label="قیمت" name="target_price">
|
||||
<BasicInput v-model:value="form.target_price" place-holder="وارد کردن قیمت" type="price" size="large"
|
||||
addon-before="ریال" />
|
||||
</a-form-item>
|
||||
<a-form-item :rules="[required()]" class="home_appear_fade_in_down" name="action"
|
||||
label="نوع هشدار را انتخاب کنید">
|
||||
<a-radio-group v-model:value="form.action">
|
||||
<a-radio :style="radioStyle" value="sms">ارسال پیامک</a-radio>
|
||||
<a-radio :style="radioStyle" value="email">ارسال ایمیل</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-spin :spinning="loading">
|
||||
<BasicButton class="w-full" type="primary" html-type="submit">
|
||||
{{ editId ? 'ویرایش هشدار' : 'ثبت هشدار' }}
|
||||
</BasicButton>
|
||||
</a-spin>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
<BasicActionSheet v-if="!isDesktop" v-model:show="addTriggerVisible" teleport="body">
|
||||
<div class="font-bold text-lg mb-2">هشدار قیمت</div>
|
||||
<PriceTriggerForm />
|
||||
</BasicActionSheet>
|
||||
<!-- <div class="bg-primary w-3rem h-3rem flex items-center justify-center rounded-lg fixed bottom-12vh left-8vw"
|
||||
@click="openAdd()">
|
||||
<BasicIcon icon="vuesax-linear:add" color="white" />
|
||||
</div>-->
|
||||
<Transition mode="out-in" name="fade">
|
||||
<div
|
||||
class="wrap-fix bottom-0 h-[16vh] bg-[#FBFBFC] shadow-lg rounded-3xl flex justify-center pt-3 z-200"
|
||||
style="box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 26px 11px"
|
||||
@click="openAdd()">
|
||||
<BasicIcon icon="vuesax-linear:add" />
|
||||
<div>افزودن هشدار جدید</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createPriceTrigger, getPriceTriggers, updatePriceTrigger } from '@/api/triggerPrice'
|
||||
|
||||
const radioStyle = reactive({
|
||||
display: '',
|
||||
height: '30px',
|
||||
lineHeight: '30px'
|
||||
})
|
||||
import type { SelectItem } from '@/models/SelectItem'
|
||||
|
||||
// import Item from "./components/Item.vue";
|
||||
/*import {lengthValidation, required} from "@/utils/inputRules";
|
||||
import {loading} from "@/utils";
|
||||
import BasicButton from "@/components/BasicPacks/BasicButton.vue";
|
||||
import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue";
|
||||
import BasicInput from "@/components/BasicPacks/BasicInput.vue";*/
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import type { TableEngineInput, TableOutput } from '@/models/table'
|
||||
import { loading, loadingMore } from '@/utils'
|
||||
// import {getOrders} from "@/api/order";
|
||||
import { getTransactionTable } from '@/api/transaction'
|
||||
import type { Transaction } from '@/models/transaction'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import BaseDatePicker from '@/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue'
|
||||
import { useInfiniteScroll, useScroll } from '@vueuse/core'
|
||||
import ListAnim from '@/components/ListAnim.vue'
|
||||
import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
|
||||
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
|
||||
import { mobileValidation, required } from '@/utils/inputRules'
|
||||
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||
import type { User } from '@/models/login'
|
||||
import { type FormInstance, message } from 'ant-design-vue'
|
||||
import type { PriceTrigger } from '@/models/PriceTrigger'
|
||||
import BasicSelect from '@/components/BasicPacks/BasicSelect.vue'
|
||||
import Item from './components/Item.vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import router from '@/router'
|
||||
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||
import { usePriceTriggerPage } from './composables/usePriceTriggerPage'
|
||||
import DesktopPriceTriggerView from './desktop/DesktopPriceTriggerView.vue'
|
||||
import MobilePriceTriggerView from './mobile/MobilePriceTriggerView.vue'
|
||||
import PriceTriggerForm from './components/PriceTriggerForm.vue'
|
||||
|
||||
const items = ref<PriceTrigger[]>([])
|
||||
const addTriggerVisible = ref<boolean>(false)
|
||||
const editId = ref<number | undefined>(undefined)
|
||||
const el = ref()
|
||||
const isDesktop = useDesktopBreakpoint()
|
||||
|
||||
const form = ref<PriceTrigger>({
|
||||
price_condition: '',
|
||||
is_active: true,
|
||||
action: '',
|
||||
target_price: ''
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
onMounted(() => {
|
||||
initItems()
|
||||
})
|
||||
|
||||
function resetFields() {
|
||||
form.value = {
|
||||
price_condition: '',
|
||||
is_active: true,
|
||||
action: '',
|
||||
target_price: ''
|
||||
}
|
||||
}
|
||||
|
||||
function openAdd() {
|
||||
resetFields()
|
||||
editId.value = undefined
|
||||
addTriggerVisible.value = true
|
||||
}
|
||||
|
||||
async function initItems() {
|
||||
try {
|
||||
items.value = []
|
||||
items.value = await getPriceTriggers()
|
||||
// console.log(items.value)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openEdit(val: PriceTrigger) {
|
||||
form.value = cloneDeep(val)
|
||||
editId.value = form.value.id
|
||||
addTriggerVisible.value = true
|
||||
}
|
||||
|
||||
async function ok() {
|
||||
formRef.value?.validateFields().then(async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
editId.value ? await updatePriceTrigger(editId.value, form.value) : await createPriceTrigger(form.value)
|
||||
message.success('هشدار با موفقیت ایجاد شد')
|
||||
await initItems()
|
||||
addTriggerVisible.value = false
|
||||
//
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const typeItems: SelectItem[] = [
|
||||
{
|
||||
value: 'بزرگتر',
|
||||
key: '>'
|
||||
},
|
||||
{
|
||||
value: 'کوچکتر',
|
||||
key: '<'
|
||||
},
|
||||
{
|
||||
value: 'مساوی',
|
||||
key: '=='
|
||||
}
|
||||
]
|
||||
const { addTriggerVisible } = usePriceTriggerPage()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
.price-trigger-page-root {
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,54 +1,68 @@
|
||||
<template>
|
||||
<div :class="isDesktop ? 'dpage products-page' : ''">
|
||||
<div class="bottom-0 fixed top-0 -z-10 pointer-events-none">
|
||||
<img :src="img" alt="" class=" w-inherit "
|
||||
:style="{ backgroundRepeat: 'repeat', }"
|
||||
/>
|
||||
</div>
|
||||
<a-spin :spinning="productsPageLoading">
|
||||
<div class="relative z-0 !h-full overflow-y-scroll">
|
||||
<div class="w-full flex justify-center top-0 py-5" style="align-self: center">
|
||||
<ARadioGroup @change="changeProductsTab" v-model:value="productsActiveKey" button-style="solid">
|
||||
<ARadioButton class="cats" value="همه">همه</ARadioButton>
|
||||
<ARadioButton class="cats" v-for="item in productsTypeItems" :key="item" :value="item">{{ item }}</ARadioButton>
|
||||
</ARadioGroup>
|
||||
<div
|
||||
class="products-page-root main-with-bottom-chrome-inner--contained"
|
||||
:class="isDesktop ? 'dpage products-page' : ''"
|
||||
>
|
||||
<MobileProductsView v-if="!isDesktop" @select="clickProduct" />
|
||||
|
||||
<template v-else>
|
||||
<div class="bottom-0 fixed top-0 -z-10 pointer-events-none">
|
||||
<img :src="img" alt="" class="w-inherit" :style="{ backgroundRepeat: 'repeat' }" />
|
||||
</div>
|
||||
<div class="mx-5 flex justify-end" @click="viewVertical=!viewVertical">
|
||||
<BasicIcon v-if="viewVertical" icon="vuesax-linear:row-vertical"></BasicIcon>
|
||||
<BasicIcon v-else icon="vuesax-linear:element-3"></BasicIcon>
|
||||
<a-spin :spinning="productsPageLoading">
|
||||
<div class="relative z-0 !h-full overflow-y-scroll">
|
||||
<div class="w-full flex justify-center top-0 py-5" style="align-self: center">
|
||||
<ARadioGroup
|
||||
v-model:value="productsActiveKey"
|
||||
button-style="solid"
|
||||
@change="changeProductsTab"
|
||||
>
|
||||
<ARadioButton class="cats" value="همه">همه</ARadioButton>
|
||||
<ARadioButton
|
||||
v-for="item in productsTypeItems"
|
||||
:key="item"
|
||||
class="cats"
|
||||
:value="item"
|
||||
>
|
||||
{{ item }}
|
||||
</ARadioButton>
|
||||
</ARadioGroup>
|
||||
</div>
|
||||
<div class="mx-5 flex justify-end" @click="viewVertical = !viewVertical">
|
||||
<BasicIcon v-if="viewVertical" icon="vuesax-linear:row-vertical" />
|
||||
<BasicIcon v-else icon="vuesax-linear:element-3" />
|
||||
</div>
|
||||
|
||||
<div class="mx-5 pb-30 pt-2 grid grid-cols-2 gap-3 items-stretch">
|
||||
<template v-if="viewVertical">
|
||||
<ProductItemVertical
|
||||
v-for="item in productsItems"
|
||||
:key="item.id"
|
||||
class="h-full"
|
||||
:ProductItem="item"
|
||||
@click="clickProduct(item)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ProductItem
|
||||
v-for="item in productsItems"
|
||||
:key="item.id"
|
||||
class="col-span-2 h-full"
|
||||
:ProductItem="item"
|
||||
@click="clickProduct(item)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<BasicActionSheet placement="bottom" v-model:show="showFilter">
|
||||
<div>
|
||||
<indexGold v-if="selectedProduct?.group_type == 'آبشده'" :ProductItem="selectedProduct" />
|
||||
<DealProduct v-else :ProductItem="selectedProduct" @close="showFilter = false" />
|
||||
</div>
|
||||
|
||||
<div class="mx-5 pb-30 pt-2 grid grid-cols-2 gap-3 items-stretch">
|
||||
<template v-if="viewVertical">
|
||||
<ProductItemVertical
|
||||
v-for="item in productsItems"
|
||||
:key="item.id"
|
||||
class="h-full"
|
||||
:ProductItem="item"
|
||||
@click="clickProduct(item)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ProductItem
|
||||
v-for="item in productsItems"
|
||||
:key="item.id"
|
||||
class="col-span-2 h-full"
|
||||
:ProductItem="item"
|
||||
@click="clickProduct(item)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a-spin>
|
||||
|
||||
<BasicActionSheet placement="bottom" v-model:show="showFilter">
|
||||
<div class="">
|
||||
<indexGold v-if="selectedProduct?.group_type=='آبشده'" :ProductItem="selectedProduct"></indexGold>
|
||||
<DealProduct v-else :ProductItem="selectedProduct" @close="showFilter = false"></DealProduct>
|
||||
</div>
|
||||
</BasicActionSheet>
|
||||
|
||||
</BasicActionSheet>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -61,9 +75,10 @@ import type { Product } from '@/models/products'
|
||||
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
|
||||
import indexGold from '@/views/base/gold/index.vue'
|
||||
import DealProduct from '@/views/base/products/components/DealProduct.vue'
|
||||
import img from "@/assets/img/user_ly1.svg";
|
||||
import BasicIcon from "@/components/BasicPacks/BasicIcon.vue";
|
||||
import ProductItemVertical from "@/views/base/products/components/ProductItemVertical.vue";
|
||||
import img from '@/assets/img/user_ly1.svg'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import ProductItemVertical from '@/views/base/products/components/ProductItemVertical.vue'
|
||||
import MobileProductsView from '@/views/base/products/mobile/MobileProductsView.vue'
|
||||
import { isProductVisible } from '@/utils/productPrice'
|
||||
import {
|
||||
productsActiveKey,
|
||||
@@ -76,7 +91,7 @@ import {
|
||||
onProductsPageProviderUnmounted,
|
||||
} from '@/views/base/products/composables/useProductsPageData'
|
||||
|
||||
const showFilter = ref()
|
||||
const showFilter = ref<boolean>()
|
||||
const selectedProduct = ref<Product>()
|
||||
const viewVertical = ref(true)
|
||||
const isDesktop = useDesktopBreakpoint()
|
||||
@@ -98,9 +113,9 @@ onMounted(async () => {
|
||||
|
||||
if (router.currentRoute.value.query.state) {
|
||||
if (router.currentRoute.value.query.product) {
|
||||
clickProduct(productsItems.value?.find((x) => x.title === 'سکه'))
|
||||
clickProduct(productsItems.value?.find((x) => x.title === 'سکه')!)
|
||||
} else {
|
||||
clickProduct(productsItems.value?.find((x) => x.title === 'آبشده نقدی'))
|
||||
clickProduct(productsItems.value?.find((x) => x.title === 'آبشده نقدی')!)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -110,13 +125,18 @@ onUnmounted(() => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.products-page-root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.cats{
|
||||
font-size: x-small;
|
||||
.products-page-root.main-with-bottom-chrome-inner {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.cats:hover span {
|
||||
--un-text-opacity: 1;
|
||||
color: rgba(206, 163, 76, var(--un-text-opacity))!important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<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>
|
||||
<div class="pt-20 pb-24 px-5">
|
||||
<MobilePageHeader title="قوانین و مقررات" show-back />
|
||||
<div class="mpage-content pb-[calc(var(--app-bottom-chrome-height)+1rem)] px-5">
|
||||
<RulesContentPanel variant="mobile" />
|
||||
<RulesSupportActions variant="mobile" />
|
||||
</div>
|
||||
@@ -14,8 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import router from '@/router'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
||||
import RulesContentPanel from '../components/RulesContentPanel.vue'
|
||||
import RulesSupportActions from '../components/RulesSupportActions.vue'
|
||||
</script>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :style="{ backgroundImage: `url(${img})` }" class="fixed top-0 right-0 left-0 bottom-0"></div>
|
||||
<div class="absolute inset-0 bg-cover bg-center opacity-60"></div>
|
||||
<div
|
||||
class="bg-white w-full transaction-list py-3 px-3 font-bold drop-shadow flex justify-between z-10"
|
||||
>
|
||||
<div>لیست تراکنش ها</div>
|
||||
<BasicIcon icon="vuesax-linear:filter" @click="showFilter = true" />
|
||||
<div class="mobile-transactions-view">
|
||||
<div class="mobile-page-bg" aria-hidden="true">
|
||||
<img :src="img" alt="" />
|
||||
</div>
|
||||
|
||||
<MobilePageHeader title="لیست تراکنش ها">
|
||||
<template #trailing>
|
||||
<button type="button" class="mobile-transactions-view__filter" @click="showFilter = true">
|
||||
<BasicIcon icon="vuesax-linear:filter" />
|
||||
</button>
|
||||
</template>
|
||||
</MobilePageHeader>
|
||||
|
||||
<div
|
||||
ref="el"
|
||||
:class="loading ? 'h-screen' : ''"
|
||||
class="px-3 pt-16 pb-4"
|
||||
style="max-height: calc(100vh - 50px)"
|
||||
class="mobile-transactions-view__content mpage-content px-3 pb-[calc(var(--app-bottom-chrome-height)+1rem)]"
|
||||
>
|
||||
<list-anim v-if="!loading">
|
||||
<Item v-for="item in items" :key="item.id" :item="item" />
|
||||
@@ -22,7 +24,7 @@
|
||||
<TransactionSkeleton v-if="loadingMore" class="mt-1 mb-8" />
|
||||
</list-anim>
|
||||
<TransactionSkeleton v-for="i in 13" v-else :key="i" class="mt-1" />
|
||||
<div v-if="items.length <= 0 && !loading" class="flex flex-col items-center justify-center h-80vh">
|
||||
<div v-if="items.length <= 0 && !loading" class="flex flex-col items-center justify-center min-h-[50vh]">
|
||||
<DotLottieVue
|
||||
class="w-11rem h-11rem"
|
||||
autoplay
|
||||
@@ -31,9 +33,10 @@
|
||||
<div class="font-bold">{{ emptyText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
@@ -54,6 +57,7 @@ import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import BaseDatePicker from '@/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue'
|
||||
import ListAnim from '@/components/ListAnim.vue'
|
||||
import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
|
||||
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
||||
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
|
||||
import img from '@/assets/img/user_ly1.svg'
|
||||
import { useTransactionsPageContext } from '../composables/useTransactionsPage'
|
||||
@@ -71,14 +75,27 @@ const {
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.transaction-list {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: 0;
|
||||
@media screen and (min-width: 426px) {
|
||||
width: 426px;
|
||||
left: auto;
|
||||
right: auto;
|
||||
}
|
||||
.mobile-transactions-view {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.mobile-transactions-view__content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-inline: 0.75rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mobile-transactions-view__filter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -87,6 +87,12 @@ export function useUserPage() {
|
||||
icon: 'vuesax-linear:lock',
|
||||
onClick: () => { editPasswordVisible.value = true },
|
||||
},
|
||||
{
|
||||
id: 'notify',
|
||||
label: 'هشدار قیمت',
|
||||
icon: 'vuesax-linear:bill',
|
||||
onClick: () => router.push({ name: 'notifyTrigger' }),
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
label: 'درباره ما',
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
<template>
|
||||
<div class="relative">
|
||||
<div>
|
||||
<!-- <img :src="gr_bottom" alt="" class="absolute bottom-20">-->
|
||||
<img :src="img" alt="" class="mt-2 w-full absolute bottom-0" />
|
||||
<div class="relative 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 font-bold drop-shadow flex">
|
||||
<div @click="router.back()" class="cursor-pointer">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" />
|
||||
</div>
|
||||
<div class="mr-2 items-center">اطلاعات حساب کاربری</div>
|
||||
</div>
|
||||
<div class="pt-16 pb-8 z-30 relative">
|
||||
<MobilePageHeader title="اطلاعات حساب کاربری" show-back />
|
||||
<div class="mpage-content pb-[calc(var(--app-bottom-chrome-height)+1rem)] z-30 relative">
|
||||
<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"
|
||||
>
|
||||
@@ -92,16 +86,16 @@
|
||||
<div>آدرس ها</div>
|
||||
</div>
|
||||
</BasicButton>-->
|
||||
<!-- <BasicButton-->
|
||||
<!-- class="w-full mt-2"-->
|
||||
<!-- size="large"-->
|
||||
<!-- @click="$router.push({ name: 'notifyTrigger' })"-->
|
||||
<!-- >-->
|
||||
<!-- <div class="flex">-->
|
||||
<!-- <BasicIcon icon="vuesax-linear:bill" class="absolute right-8" />-->
|
||||
<!-- <div>هشدار قیمت</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </BasicButton>-->
|
||||
<BasicButton
|
||||
class="w-full mt-2"
|
||||
size="large"
|
||||
@click="$router.push({ name: 'notifyTrigger' })"
|
||||
>
|
||||
<div class="flex">
|
||||
<BasicIcon icon="vuesax-linear:bill" class="absolute right-8" />
|
||||
<div>هشدار قیمت</div>
|
||||
</div>
|
||||
</BasicButton>
|
||||
<BasicButton
|
||||
class="mt-2 w-full"
|
||||
icon=""
|
||||
@@ -222,6 +216,7 @@ import { useWindowScroll } from '@vueuse/core/index'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { useUserPageContext } from '../composables/useUserPage'
|
||||
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const walletStore = useWalletStore()
|
||||
@@ -282,17 +277,7 @@ async function logout() {
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
.relative {
|
||||
overflow-y: auto;
|
||||
max-height: 100vh;
|
||||
//scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 50; /* بالاتر از همه */
|
||||
.mobile-user-view {
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="absolute inset-0 bg-cover bg-center opacity-60" :style="{ backgroundImage: `url(${img})` }"></div>
|
||||
<div class="bg-white py-3 px-3 font-bold drop-shadow z-[1999] wrap-fix">کیف پول</div>
|
||||
<div class="mpage-header wrap-fix z-[1999]">کیف پول</div>
|
||||
<div
|
||||
class="bg-white wrap-fix py-3 top-11 px-3 text-primary font-bold flex justify-evenly z-[1999]"
|
||||
class="mpage-subheader wrap-fix fixed left-0 right-0 px-3 text-primary font-bold flex justify-evenly z-[1999]"
|
||||
>
|
||||
<div class="flex justify-evenly w-full px-2 h-11 items-center bg-gray-50 relative rounded">
|
||||
<div
|
||||
@@ -29,7 +29,7 @@
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-5 pt-26 pb-2">
|
||||
<div class="px-5 mpage-content--with-subheader pb-[calc(var(--app-bottom-chrome-height)+1rem)]">
|
||||
<a-form :model="form" class="!mt-5" name="basic" @finish="ok()">
|
||||
<div class="border-gray-200 border-1 rounded-xl p-5 trans h-40 relative">
|
||||
<bg_main />
|
||||
@@ -99,8 +99,7 @@
|
||||
v-if="isDeposit"
|
||||
: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)"
|
||||
class="px-3 overflow-y-scroll w-full h-[49vh] pb-[calc(var(--app-bottom-chrome-height)+1rem)] mpage-scroll"
|
||||
>
|
||||
<list-anim v-if="!loading" :numberItemsAnim="4">
|
||||
<Item
|
||||
@@ -122,7 +121,7 @@
|
||||
:items="cardItems"
|
||||
v-model:id="form.card_id"
|
||||
@refresh="getCards"
|
||||
class="card-slider-wrap mt-[2vh] px-3 fixed bottom-10 right-0 left-0"
|
||||
class="card-slider-wrap mt-[2vh] px-3 wallet-dock-width fixed bottom-[calc(var(--app-bottom-chrome-height)+var(--app-add-card-height)-var(--wallet-card-dock-overlap))] z-[var(--app-z-wallet-dock)]"
|
||||
style="touch-action: none"
|
||||
/>
|
||||
<div
|
||||
@@ -137,8 +136,7 @@
|
||||
<Transition mode="out-in" name="fade">
|
||||
<div
|
||||
v-if="!isDeposit"
|
||||
class="add-card fixed bottom-0 h-[18vh] bg-[#FBFBFC] w-full shadow-lg rounded-3xl flex justify-center pt-3 z-200"
|
||||
style="box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 26px 11px"
|
||||
class="app-dock-add-bar"
|
||||
@click="showAddCard = true"
|
||||
>
|
||||
<div>افزودن کارت جدید</div>
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@ import {
|
||||
|
||||
export default defineConfig({
|
||||
shortcuts: [
|
||||
// ...
|
||||
['wallet-dock-width', 'fixed inset-x-0 w-full max-w-[426px] left-0 right-0'],
|
||||
['app-dock-add-bar', 'wallet-dock-width !fixed bottom-[var(--app-bottom-chrome-height)] z-[var(--app-z-wallet-add-bar)] h-[var(--app-add-card-height)] flex items-center justify-center gap-1 rounded-t-3xl bg-[#FBFBFC] shadow-[0_1px_26px_11px_rgba(0,0,0,0.1)] cursor-pointer box-border'],
|
||||
],
|
||||
theme: {
|
||||
colors: {
|
||||
|
||||
Reference in New Issue
Block a user