fix(ui): repair PWA install, credit footer, and boursecu copy
Make install guide and BasicButton clicks reliable, align montzar credit bar, remove calculator quick action, and use مس in about-software default text. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+20
-6
@@ -73,17 +73,17 @@
|
|||||||
<div
|
<div
|
||||||
v-if="!showSplash && !isDesktop && !isTvBoardRoute"
|
v-if="!showSplash && !isDesktop && !isTvBoardRoute"
|
||||||
class="app-credit-footer app-credit-footer--mobile"
|
class="app-credit-footer app-credit-footer--mobile"
|
||||||
:class="{ 'app-credit-footer--login': isLoginRoute }"
|
:class="{ 'app-credit-footer--login': showLoginCreditExtras }"
|
||||||
>
|
>
|
||||||
<EnamadBadge v-if="isLoginRoute" size="sm" />
|
<EnamadBadge v-if="showLoginCreditExtras" size="sm" />
|
||||||
<AppCreditLink variant="mobile" />
|
<AppCreditLink variant="mobile" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!showSplash && showDesktopFloatingCredit && !isTvBoardRoute"
|
v-if="!showSplash && showDesktopFloatingCredit && !isTvBoardRoute"
|
||||||
class="app-credit-footer app-credit-footer--desktop-float"
|
class="app-credit-footer app-credit-footer--desktop-float"
|
||||||
:class="{ 'app-credit-footer--login': isLoginRoute }"
|
:class="{ 'app-credit-footer--login': showLoginCreditExtras }"
|
||||||
>
|
>
|
||||||
<EnamadBadge v-if="isLoginRoute" size="sm" />
|
<EnamadBadge v-if="showLoginCreditExtras" size="sm" />
|
||||||
<AppCreditLink variant="float" />
|
<AppCreditLink variant="float" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -297,6 +297,12 @@ watch(
|
|||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
const showLoginCreditExtras = computed(() => {
|
||||||
|
if (!isLoginRoute.value) return false
|
||||||
|
const href = String(settingsStore.getSettingByKey('enamad_url') ?? '').trim()
|
||||||
|
const src = String(settingsStore.getSettingByKey('enamad_logo') ?? '').trim()
|
||||||
|
return Boolean(href && src)
|
||||||
|
})
|
||||||
const { latestIncomingNotification } = useChatService()
|
const { latestIncomingNotification } = useChatService()
|
||||||
let lastConnectedChatSessionKey = ''
|
let lastConnectedChatSessionKey = ''
|
||||||
let lastSupportToastAt = 0
|
let lastSupportToastAt = 0
|
||||||
@@ -424,13 +430,14 @@ watch(latestIncomingNotification, (payload) => {
|
|||||||
min-height: var(--app-credit-bar-inner);
|
min-height: var(--app-credit-bar-inner);
|
||||||
padding: 4px 0 var(--app-safe-bottom);
|
padding: 4px 0 var(--app-safe-bottom);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 -2px 4px -2px #0000001a;
|
box-shadow: 0 3.75px 16px 0 rgba(0, 0, 0, 0.43);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-credit-footer--login.app-credit-footer--mobile {
|
.app-credit-footer--login.app-credit-footer--mobile {
|
||||||
@@ -461,6 +468,7 @@ watch(latestIncomingNotification, (payload) => {
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
@@ -470,13 +478,19 @@ watch(latestIncomingNotification, (payload) => {
|
|||||||
|
|
||||||
&__logo {
|
&__logo {
|
||||||
width: auto;
|
width: auto;
|
||||||
|
height: 12px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--mobile {
|
&--mobile {
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
color: inherit;
|
color: #000;
|
||||||
|
|
||||||
|
.app-credit-link__text {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
.app-credit-link__logo {
|
.app-credit-link__logo {
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
|||||||
@@ -7,11 +7,16 @@
|
|||||||
:class="`app-credit-link--${variant}`"
|
:class="`app-credit-link--${variant}`"
|
||||||
>
|
>
|
||||||
<span class="app-credit-link__text">توسعه و طراحی توسط</span>
|
<span class="app-credit-link__text">توسعه و طراحی توسط</span>
|
||||||
<img src="/logo-montzar.png" alt="montzar" class="app-credit-link__logo" />
|
<img :src="logoSrc" alt="montzar" class="app-credit-link__logo" />
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
const logoSrc = `${import.meta.env.BASE_URL || '/'}logo-montzar.png`.replace(
|
||||||
|
/([^:]\/)\/+/g,
|
||||||
|
'$1',
|
||||||
|
)
|
||||||
|
|
||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
variant?: 'mobile' | 'sidebar' | 'float' | 'tv'
|
variant?: 'mobile' | 'sidebar' | 'float' | 'tv'
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<AButton :disabled="props.disabledB" :type="props.type" :size="props.size" :class="[prefixCls, _class]" :loading="props.loading">
|
<AButton
|
||||||
|
:disabled="props.disabledB"
|
||||||
|
:type="props.type"
|
||||||
|
:size="props.size"
|
||||||
|
:class="[prefixCls, _class]"
|
||||||
|
:loading="props.loading"
|
||||||
|
:html-type="props.htmlType"
|
||||||
|
@click="onClick"
|
||||||
|
>
|
||||||
<!-- py-1 px-2-->
|
<!-- py-1 px-2-->
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-1 justify-center flex-row-reverse gap-2 py-1 px-2"
|
class="flex items-center gap-1 justify-center flex-row-reverse gap-2 py-1 px-2"
|
||||||
@@ -28,7 +36,7 @@ interface Props {
|
|||||||
size?: string
|
size?: string
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
disabledB?:boolean
|
disabledB?:boolean
|
||||||
|
htmlType?: 'button' | 'submit' | 'reset'
|
||||||
// prefixIcon?: string
|
// prefixIcon?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +59,17 @@ const { prefixCls } = usePrefix('base-button')
|
|||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
iconSize: baseButtonIconSize,
|
iconSize: baseButtonIconSize,
|
||||||
// iconLeft: false,
|
// iconLeft: false,
|
||||||
iconClass: ''
|
iconClass: '',
|
||||||
|
htmlType: 'submit',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
click: [event: MouseEvent]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function onClick(event: MouseEvent) {
|
||||||
|
emit('click', event)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|||||||
@@ -20,11 +20,17 @@ const emit = defineEmits<{
|
|||||||
close: []
|
close: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const activePlatform = ref<PwaPlatform>(props.platform ?? getDefaultPwaPlatform())
|
function normalizePlatform(value?: string | null): PwaPlatform {
|
||||||
|
return value === 'ios' || value === 'android' ? value : getDefaultPwaPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
const activePlatform = ref<PwaPlatform>(normalizePlatform(props.platform))
|
||||||
const stepIndex = ref(0)
|
const stepIndex = ref(0)
|
||||||
|
|
||||||
const guide = computed(() => PWA_INSTALL_GUIDE[activePlatform.value])
|
const guide = computed(
|
||||||
const currentStep = computed(() => guide.value.steps[stepIndex.value])
|
() => PWA_INSTALL_GUIDE[normalizePlatform(activePlatform.value)] ?? PWA_INSTALL_GUIDE.android,
|
||||||
|
)
|
||||||
|
const currentStep = computed(() => guide.value.steps[stepIndex.value] ?? guide.value.steps[0])
|
||||||
const totalSteps = computed(() => guide.value.steps.length)
|
const totalSteps = computed(() => guide.value.steps.length)
|
||||||
const isFirstStep = computed(() => stepIndex.value === 0)
|
const isFirstStep = computed(() => stepIndex.value === 0)
|
||||||
const isLastStep = computed(() => stepIndex.value === totalSteps.value - 1)
|
const isLastStep = computed(() => stepIndex.value === totalSteps.value - 1)
|
||||||
@@ -34,7 +40,7 @@ watch(
|
|||||||
() => props.open,
|
() => props.open,
|
||||||
(visible) => {
|
(visible) => {
|
||||||
if (!visible) return
|
if (!visible) return
|
||||||
activePlatform.value = props.platform ?? getDefaultPwaPlatform()
|
activePlatform.value = normalizePlatform(props.platform)
|
||||||
stepIndex.value = 0
|
stepIndex.value = 0
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -42,7 +48,7 @@ watch(
|
|||||||
watch(
|
watch(
|
||||||
() => props.platform,
|
() => props.platform,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value) activePlatform.value = value
|
if (value) activePlatform.value = normalizePlatform(value)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,8 +58,9 @@ function close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function selectPlatform(next: PwaPlatform) {
|
function selectPlatform(next: PwaPlatform) {
|
||||||
if (activePlatform.value === next) return
|
const platform = normalizePlatform(next)
|
||||||
activePlatform.value = next
|
if (activePlatform.value === platform) return
|
||||||
|
activePlatform.value = platform
|
||||||
stepIndex.value = 0
|
stepIndex.value = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
import type { PwaPlatform } from '@/modules/pwa/pwaInstallGuideSteps'
|
import type { PwaPlatform } from '@/modules/pwa/pwaInstallGuideSteps'
|
||||||
import {
|
import {
|
||||||
getDefaultPwaPlatform,
|
getDefaultPwaPlatform,
|
||||||
@@ -77,7 +78,10 @@ export function usePwaInstall() {
|
|||||||
// فقط وقتی واقعاً داخل PWA/TWA هستیم راهنما را نشان نده
|
// فقط وقتی واقعاً داخل PWA/TWA هستیم راهنما را نشان نده
|
||||||
if (isStandalonePwa() || isTWA()) return
|
if (isStandalonePwa() || isTWA()) return
|
||||||
consumeInstallBanner()
|
consumeInstallBanner()
|
||||||
platform.value = nextPlatform ?? getDefaultPwaPlatform()
|
const next = nextPlatform === 'ios' || nextPlatform === 'android'
|
||||||
|
? nextPlatform
|
||||||
|
: getDefaultPwaPlatform()
|
||||||
|
platform.value = next
|
||||||
guideOpen.value = true
|
guideOpen.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,16 +112,24 @@ export function usePwaInstall() {
|
|||||||
|
|
||||||
/** نصب مستقیم مرورگر؛ اگر ممکن نبود مدال آموزش. */
|
/** نصب مستقیم مرورگر؛ اگر ممکن نبود مدال آموزش. */
|
||||||
async function tryInstallOrOpenGuide(nextPlatform?: PwaPlatform) {
|
async function tryInstallOrOpenGuide(nextPlatform?: PwaPlatform) {
|
||||||
|
ensureListener()
|
||||||
refreshOfferInstall()
|
refreshOfferInstall()
|
||||||
if (isStandalonePwa() || isTWA()) return false
|
|
||||||
|
if (isStandalonePwa() || isTWA()) {
|
||||||
|
message.info('نسخه وباپ از قبل نصب شده است')
|
||||||
|
offerInstall.value = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const installed = await triggerNativePwaInstall()
|
const installed = await triggerNativePwaInstall()
|
||||||
if (installed) {
|
if (installed) {
|
||||||
offerInstall.value = false
|
offerInstall.value = false
|
||||||
consumeInstallBanner()
|
consumeInstallBanner()
|
||||||
|
message.success('نصب وباپ آغاز شد')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chrome روی HTTP/IP یا Safari معمولاً prompt بومی ندارد — راهنما را باز کن
|
||||||
openGuide(nextPlatform)
|
openGuide(nextPlatform)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ export function initPwaInstallPromptListener() {
|
|||||||
if (installListenerInitialized || typeof window === 'undefined') return
|
if (installListenerInitialized || typeof window === 'undefined') return
|
||||||
installListenerInitialized = true
|
installListenerInitialized = true
|
||||||
|
|
||||||
|
window.addEventListener('beforeinstallprompt', (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
deferredInstallPrompt = e as BeforeInstallPromptEvent
|
||||||
|
})
|
||||||
|
|
||||||
window.addEventListener(PWA_MANIFEST_READY_EVENT, (event) => {
|
window.addEventListener(PWA_MANIFEST_READY_EVENT, (event) => {
|
||||||
const installEvent = (event as CustomEvent<{ installEvent?: Event | null }>).detail?.installEvent
|
const installEvent = (event as CustomEvent<{ installEvent?: Event | null }>).detail?.installEvent
|
||||||
|
|
||||||
@@ -103,6 +108,10 @@ export function initPwaInstallPromptListener() {
|
|||||||
deferredInstallPrompt = installEvent as BeforeInstallPromptEvent
|
deferredInstallPrompt = installEvent as BeforeInstallPromptEvent
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
window.addEventListener('appinstalled', () => {
|
||||||
|
deferredInstallPrompt = null
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function triggerNativePwaInstall() {
|
export async function triggerNativePwaInstall() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export function getDefaultAboutSoftwareText(projectName: string) {
|
export function getDefaultAboutSoftwareText(projectName: string) {
|
||||||
const name = projectName.trim() || 'این نرمافزار'
|
const name = projectName.trim() || 'این نرمافزار'
|
||||||
return `${name} بستری امن و آنلاین برای خرید و فروش طلا فراهم کرده تا کاربران بتوانند سرمایههای خود را بهسادگی مدیریت کنند.\n\nهدف ما ارائه تجربهای سریع، شفاف و کاربرپسند در معاملات طلا است.`
|
return `${name} بستری امن و آنلاین برای خرید و فروش مس فراهم کرده تا کاربران بتوانند سرمایههای خود را بهسادگی مدیریت کنند.\n\nهدف ما ارائه تجربهای سریع، شفاف و کاربرپسند در معاملات مس است.`
|
||||||
}
|
}
|
||||||
|
|
||||||
function looksLikeHtml(value: string) {
|
function looksLikeHtml(value: string) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { getBoursecuActionIconSrc } from '@/utils/config'
|
import { getBoursecuActionIconSrc } from '@/utils/config'
|
||||||
import { openGoldDayCalculator } from '@/composables/useGoldDayCalculator'
|
|
||||||
import type { HomeQuickActionItem } from '../types'
|
import type { HomeQuickActionItem } from '../types'
|
||||||
|
|
||||||
export const boursecuQuickActions: HomeQuickActionItem[] = [
|
export const boursecuQuickActions: HomeQuickActionItem[] = [
|
||||||
@@ -45,10 +44,4 @@ export const boursecuQuickActions: HomeQuickActionItem[] = [
|
|||||||
iconSrc: getBoursecuActionIconSrc('convert'),
|
iconSrc: getBoursecuActionIconSrc('convert'),
|
||||||
route: { name: 'buyProduct' },
|
route: { name: 'buyProduct' },
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'calculator',
|
|
||||||
label: 'محاسبهگر',
|
|
||||||
icon: 'vuesax-linear:chart-2',
|
|
||||||
handler: () => openGoldDayCalculator(),
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
>
|
>
|
||||||
دانلود اپ اندروید
|
دانلود اپ اندروید
|
||||||
</a>
|
</a>
|
||||||
<BasicButton type="primary" class="w-full py-2" html-type="button" @click.prevent="installWebApp">
|
<BasicButton type="primary" class="w-full py-2" html-type="button" @click="installWebApp">
|
||||||
<div>نصب نسخه وب اپ</div>
|
<div>نصب نسخه وب اپ</div>
|
||||||
</BasicButton>
|
</BasicButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user