From 444178df052b8266e263eb20f70dc6bf1d61e4b6 Mon Sep 17 00:00:00 2001 From: adonis Date: Thu, 20 Aug 2026 21:35:18 +0330 Subject: [PATCH] feat: add gold day calculator and polish desktop UI Co-authored-by: Cursor --- src/App.vue | 11 +- src/components/BasicPacks/BasicModal.vue | 10 +- src/components/GoldDayCalculatorModal.vue | 307 ++++++++++++++++++ src/composables/useGoldDayCalculator.ts | 12 + .../desktop/styles/desktop-page-common.less | 8 +- src/utils/useDraggableFloatingButton.ts | 78 ++++- .../gold/desktop/styles/desktop-gold.less | 8 +- .../home/desktop/styles/desktop-home.less | 8 +- .../base/user/components/UserProfileForm.vue | 19 +- .../base/user/composables/useUserPage.ts | 8 + .../base/user/desktop/DesktopUserView.vue | 24 +- .../user/desktop/styles/desktop-user.less | 75 +++-- src/views/base/user/mobile/MobileUserView.vue | 15 +- .../base/wallet/desktop/DesktopWalletView.vue | 63 ++-- .../wallet/desktop/styles/desktop-wallet.less | 21 ++ 15 files changed, 574 insertions(+), 93 deletions(-) create mode 100644 src/components/GoldDayCalculatorModal.vue create mode 100644 src/composables/useGoldDayCalculator.ts diff --git a/src/App.vue b/src/App.vue index 8d30894..3b63ebc 100644 --- a/src/App.vue +++ b/src/App.vue @@ -98,6 +98,8 @@ > + + @@ -121,6 +123,7 @@ import { useApplyDesktopLayoutClass, useDesktopBreakpoint } from '@/layouts/comp import AppCreditLink from '@/components/AppCreditLink.vue' import { usePwaInstall } from '@/composables/usePwaInstall' import PwaInstallGuideModal from '@/components/pwa/PwaInstallGuideModal.vue' +import GoldDayCalculatorModal from '@/components/GoldDayCalculatorModal.vue' import { useUserStore } from '@/store/userStore' import { useSettingsStore } from '@/store/settingsStore' import { connectChatIfExists, disconnectChat, isSettingFlagOn, unreadSupportCount, useChatService } from '@/utils/chat' @@ -464,12 +467,4 @@ watch(latestIncomingNotification, (payload) => { transform: scale(0.94); box-shadow: 0 4px 14px rgba(15, 23, 42, 0.12); } - -/* در دسکتاپ، دکمه پشتیبانی کنار سایدبار و پایین صفحه قرار می‌گیرد (مانند khaneghi_user) */ -html.app-layout--desktop { - #support-button { - bottom: 12px; - right: calc(var(--app-desktop-sidebar-width, 260px) + 20px); - } -} diff --git a/src/components/BasicPacks/BasicModal.vue b/src/components/BasicPacks/BasicModal.vue index 7eb394d..5f45df1 100644 --- a/src/components/BasicPacks/BasicModal.vue +++ b/src/components/BasicPacks/BasicModal.vue @@ -2,7 +2,7 @@
+
@@ -25,8 +28,7 @@ + + + + diff --git a/src/composables/useGoldDayCalculator.ts b/src/composables/useGoldDayCalculator.ts new file mode 100644 index 0000000..39b6aef --- /dev/null +++ b/src/composables/useGoldDayCalculator.ts @@ -0,0 +1,12 @@ +import { ref } from 'vue' + +/** باز/بسته بودن مدال محاسبه‌گر طلا — مشترک بین منوی کاربر و همبرگر */ +export const goldDayCalculatorVisible = ref(false) + +export function openGoldDayCalculator() { + goldDayCalculatorVisible.value = true +} + +export function closeGoldDayCalculator() { + goldDayCalculatorVisible.value = false +} diff --git a/src/layouts/desktop/styles/desktop-page-common.less b/src/layouts/desktop/styles/desktop-page-common.less index ccd26ea..8e20870 100644 --- a/src/layouts/desktop/styles/desktop-page-common.less +++ b/src/layouts/desktop/styles/desktop-page-common.less @@ -111,13 +111,17 @@ html.app-layout--desktop { } .dhome-topbar { + position: sticky; + top: 0; + z-index: 30; display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; - margin-bottom: 28px; - padding-bottom: 24px; + margin: -4px 0 20px; + padding: 12px 0 20px; border-bottom: 1px solid rgba(15, 23, 42, 0.06); + background: #f6f7f9; &__eyebrow { font-size: 12px; diff --git a/src/utils/useDraggableFloatingButton.ts b/src/utils/useDraggableFloatingButton.ts index 6f28c07..6c2c515 100644 --- a/src/utils/useDraggableFloatingButton.ts +++ b/src/utils/useDraggableFloatingButton.ts @@ -1,7 +1,9 @@ import { computed, onMounted, onUnmounted, ref } from 'vue' const DRAG_THRESHOLD = 6 -const POSITION_VERSION = 1 +/** v2: دسکتاپ از کنار سایدبار تا انتهای viewport (نه ستون ۴۲۶px) */ +const POSITION_VERSION = 2 +const MOBILE_CONTENT_MAX = 426 type FabPosition = { x: number; y: number } @@ -11,6 +13,11 @@ type SavedFabOffset = { dy: number } +type HorizontalBounds = { + offsetX: number + contentWidth: number +} + type DraggableFloatingOptions = { width?: number height?: number @@ -40,6 +47,46 @@ function readBottomChromeHeight(): number { return measured > 0 ? measured : nav } +function isDesktopLayout(): boolean { + const el = document.documentElement + return ( + el.classList.contains('app-layout--desktop') + || el.getAttribute('data-layout') === 'desktop' + ) +} + +function readCssLengthPx(varName: string): number | null { + const raw = getComputedStyle(document.documentElement).getPropertyValue(varName).trim() + if (!raw) return null + + if (raw.endsWith('px')) { + const n = parseFloat(raw) + return Number.isNaN(n) ? null : n + } + + if (raw.endsWith('rem')) { + const rem = parseFloat(raw) + const root = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16 + return Number.isNaN(rem) ? null : rem * root + } + + const n = parseFloat(raw) + return Number.isNaN(n) ? null : n +} + +/** سایدبار دسکتاپ معمولاً سمت راست (RTL) است. */ +function readDesktopSidebarWidthPx(): number { + if (document.documentElement.classList.contains('app-layout--desktop-no-sidebar')) { + return 0 + } + + return ( + readCssLengthPx('--app-desktop-sidebar-width') + ?? readCssLengthPx('--app-sidebar-width') + ?? 260 + ) +} + export function useDraggableFloatingButton( storageKey: string, options: DraggableFloatingOptions = {} @@ -66,22 +113,28 @@ export function useDraggableFloatingButton( touchAction: 'none' })) - function getViewportWidth() { - return Math.min(window.innerWidth, 426) - } - - function getHorizontalOffset() { + function getHorizontalBounds(): HorizontalBounds { const vw = window.innerWidth - const contentWidth = getViewportWidth() - return Math.max(0, (vw - contentWidth) / 2) + if (isDesktopLayout()) { + const sidebar = readDesktopSidebarWidthPx() + return { + offsetX: 0, + contentWidth: Math.max(width, vw - sidebar) + } + } + + const contentWidth = Math.min(vw, MOBILE_CONTENT_MAX) + return { + offsetX: Math.max(0, (vw - contentWidth) / 2), + contentWidth + } } function getDefaultPosition(): FabPosition { const vh = window.innerHeight - const offsetX = getHorizontalOffset() - const contentWidth = getViewportWidth() - const bottomChrome = readBottomChromeHeight() + const { offsetX, contentWidth } = getHorizontalBounds() + const bottomChrome = isDesktopLayout() ? 0 : readBottomChromeHeight() return { x: offsetX + contentWidth - width - defaultRightOffset, @@ -100,8 +153,7 @@ export function useDraggableFloatingButton( function clampToViewport() { const vh = window.innerHeight - const offsetX = getHorizontalOffset() - const contentWidth = getViewportWidth() + const { offsetX, contentWidth } = getHorizontalBounds() const minX = offsetX const maxX = offsetX + contentWidth - width diff --git a/src/views/base/gold/desktop/styles/desktop-gold.less b/src/views/base/gold/desktop/styles/desktop-gold.less index d53b767..e095a0f 100644 --- a/src/views/base/gold/desktop/styles/desktop-gold.less +++ b/src/views/base/gold/desktop/styles/desktop-gold.less @@ -34,13 +34,17 @@ } .dgold-topbar { + position: sticky; + top: 0; + z-index: 30; display: flex; align-items: flex-start; gap: 16px; max-width: 720px; - margin: 0 auto 24px; - padding-bottom: 24px; + margin: -4px auto 20px; + padding: 12px 0 20px; border-bottom: 1px solid rgba(15, 23, 42, 0.06); + background: #f6f7f9; &__back { display: inline-flex; diff --git a/src/views/base/home/desktop/styles/desktop-home.less b/src/views/base/home/desktop/styles/desktop-home.less index 72d8973..94e7c23 100644 --- a/src/views/base/home/desktop/styles/desktop-home.less +++ b/src/views/base/home/desktop/styles/desktop-home.less @@ -53,13 +53,17 @@ /* Topbar */ .dhome-topbar { + position: sticky; + top: 0; + z-index: 30; display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; - margin-bottom: 28px; - padding-bottom: 24px; + margin: -4px 0 20px; + padding: 12px 0 20px; border-bottom: 1px solid rgba(15, 23, 42, 0.06); + background: #f6f7f9; &__eyebrow { font-size: 12px; diff --git a/src/views/base/user/components/UserProfileForm.vue b/src/views/base/user/components/UserProfileForm.vue index 7b92ab4..580ad79 100644 --- a/src/views/base/user/components/UserProfileForm.vue +++ b/src/views/base/user/components/UserProfileForm.vue @@ -1,5 +1,5 @@