Show negative SMS wallet warning modal on admin page loads.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -212,11 +212,19 @@ watch(modalVisible, (open) => {
|
|||||||
if (open) showLabelHint.value = false
|
if (open) showLabelHint.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function onOpenChargeFromWarning() {
|
||||||
|
modalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (isCompactDesktop.value) startLabelHintLoop()
|
if (isCompactDesktop.value) startLabelHintLoop()
|
||||||
|
window.addEventListener('sms-open-charge-modal', onOpenChargeFromWarning)
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(stopLabelHintTimers)
|
onUnmounted(() => {
|
||||||
|
stopLabelHintTimers()
|
||||||
|
window.removeEventListener('sms-open-charge-modal', onOpenChargeFromWarning)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import SmsNegativeBalanceModal from '@/components/SmsNegativeBalanceModal.vue'
|
||||||
|
import { useSmsWalletStore } from '@/store/smsWalletStore'
|
||||||
|
|
||||||
|
const store = useSmsWalletStore()
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => store.visible && store.loaded && store.negativeWarningVisible,
|
||||||
|
set: (value: boolean) => {
|
||||||
|
if (!value) store.dismissNegativeWarning()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function openCharge() {
|
||||||
|
// باز کردن مودال شارژ از ویجت هدر با رویداد سفارشی ساده
|
||||||
|
window.dispatchEvent(new CustomEvent('sms-open-charge-modal'))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SmsNegativeBalanceModal
|
||||||
|
v-model:visible="visible"
|
||||||
|
:balance="store.balance"
|
||||||
|
@charge="openCharge"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import BasicModal from '@/components/BasicModal.vue'
|
||||||
|
import BaseButton from '@core/components/BaseButton'
|
||||||
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
visible: boolean
|
||||||
|
balance: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:visible': [value: boolean]
|
||||||
|
charge: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCharge() {
|
||||||
|
emit('charge')
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(value: number) {
|
||||||
|
return Number(value || 0).toLocaleString('fa-IR')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BasicModal
|
||||||
|
:visible="visible"
|
||||||
|
width="28rem"
|
||||||
|
title="هشدار موجودی پیامک"
|
||||||
|
:custom-footer="true"
|
||||||
|
@close="closeModal"
|
||||||
|
@update:visible="emit('update:visible', $event)"
|
||||||
|
>
|
||||||
|
<div class="sms-neg-modal">
|
||||||
|
<div class="sms-neg-modal__banner">
|
||||||
|
<Icon icon="vuesax-linear:warning-2" :size="22" class="sms-neg-modal__icon" />
|
||||||
|
<div>
|
||||||
|
<p class="sms-neg-modal__title">موجودی کیف پول پیامک منفی است</p>
|
||||||
|
<p class="sms-neg-modal__text">
|
||||||
|
تا زمان شارژ و مثبت شدن موجودی، این هشدار با هر بار ورود به صفحات ادمین نمایش داده میشود.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sms-neg-modal__balance">
|
||||||
|
<span>موجودی فعلی</span>
|
||||||
|
<strong>{{ formatNumber(balance) }} ریال</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<BaseButton @click="closeModal">بستن</BaseButton>
|
||||||
|
<BaseButton type="primary" @click="openCharge">اطلاعات واریز / شارژ</BaseButton>
|
||||||
|
</template>
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.sms-neg-modal {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-neg-modal__banner {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.85rem 1rem;
|
||||||
|
border-radius: 0.85rem;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
background: linear-gradient(135deg, #fef2f2, #fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-neg-modal__icon {
|
||||||
|
color: #dc2626;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-neg-modal__title {
|
||||||
|
margin: 0 0 0.35rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-neg-modal__text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: var(--admin-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-neg-modal__balance {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: var(--admin-bg-muted);
|
||||||
|
border: 1px solid var(--admin-border);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-neg-modal__balance strong {
|
||||||
|
color: #b91c1c;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -90,6 +90,7 @@
|
|||||||
</ALayout>
|
</ALayout>
|
||||||
<BasicMobileBottomNav v-if="isMobile" />
|
<BasicMobileBottomNav v-if="isMobile" />
|
||||||
<BaseSettings />
|
<BaseSettings />
|
||||||
|
<SmsNegativeBalanceHost />
|
||||||
</ALayout>
|
</ALayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@ import { isMobile } from '@core/utils/is'
|
|||||||
import BaseSettings from '@core/layouts/BaseSettings.vue'
|
import BaseSettings from '@core/layouts/BaseSettings.vue'
|
||||||
import BasicMobileHeader from '@/components/BasicMobileHeader.vue'
|
import BasicMobileHeader from '@/components/BasicMobileHeader.vue'
|
||||||
import BasicMobileBottomNav from '@/components/BasicMobileBottomNav.vue'
|
import BasicMobileBottomNav from '@/components/BasicMobileBottomNav.vue'
|
||||||
|
import SmsNegativeBalanceHost from '@/components/SmsNegativeBalanceHost.vue'
|
||||||
const { prefixCls } = usePrefix('basic-layout')
|
const { prefixCls } = usePrefix('basic-layout')
|
||||||
const layoutStore = useLayoutStore()
|
const layoutStore = useLayoutStore()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
import { getSmsWalletSummary, type SmsDepositInfo } from '@/api/smsPanel'
|
import { getSmsWalletSummary, type SmsDepositInfo } from '@/api/smsPanel'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
|
||||||
@@ -18,6 +19,8 @@ export const useSmsWalletStore = defineStore('smsWallet', () => {
|
|||||||
const deposit = ref<SmsDepositInfo>({ ...EMPTY_DEPOSIT })
|
const deposit = ref<SmsDepositInfo>({ ...EMPTY_DEPOSIT })
|
||||||
const loaded = ref(false)
|
const loaded = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
/** هشدار موجودی منفی — با هر refresh اگر هنوز منفی باشد دوباره باز میشود */
|
||||||
|
const negativeWarningVisible = ref(false)
|
||||||
|
|
||||||
/** ویجت فقط وقتی billing/پنل پیامک فعال است نمایش داده میشود. */
|
/** ویجت فقط وقتی billing/پنل پیامک فعال است نمایش داده میشود. */
|
||||||
const visible = computed(() => userStore.smsBillingEnabled)
|
const visible = computed(() => userStore.smsBillingEnabled)
|
||||||
@@ -25,6 +28,7 @@ export const useSmsWalletStore = defineStore('smsWallet', () => {
|
|||||||
async function refresh() {
|
async function refresh() {
|
||||||
if (!visible.value) {
|
if (!visible.value) {
|
||||||
loaded.value = false
|
loaded.value = false
|
||||||
|
negativeWarningVisible.value = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (loading.value) return
|
if (loading.value) return
|
||||||
@@ -36,6 +40,7 @@ export const useSmsWalletStore = defineStore('smsWallet', () => {
|
|||||||
isBlocked.value = res.is_blocked
|
isBlocked.value = res.is_blocked
|
||||||
deposit.value = res.deposit ?? { ...EMPTY_DEPOSIT }
|
deposit.value = res.deposit ?? { ...EMPTY_DEPOSIT }
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
|
negativeWarningVisible.value = res.balance < 0
|
||||||
} catch {
|
} catch {
|
||||||
// خطای موجودی نباید مزاحم کاربر شود؛ ویجت در صورت نبود داده مخفی میماند
|
// خطای موجودی نباید مزاحم کاربر شود؛ ویجت در صورت نبود داده مخفی میماند
|
||||||
} finally {
|
} finally {
|
||||||
@@ -43,6 +48,10 @@ export const useSmsWalletStore = defineStore('smsWallet', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dismissNegativeWarning() {
|
||||||
|
negativeWarningVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
balance,
|
balance,
|
||||||
isBlocked,
|
isBlocked,
|
||||||
@@ -50,6 +59,8 @@ export const useSmsWalletStore = defineStore('smsWallet', () => {
|
|||||||
loaded,
|
loaded,
|
||||||
loading,
|
loading,
|
||||||
visible,
|
visible,
|
||||||
refresh
|
negativeWarningVisible,
|
||||||
|
refresh,
|
||||||
|
dismissNegativeWarning
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user