feat(user): identity gate for wallet/gift card and custom nav icons
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
فرمت هر خط:
|
فرمت هر خط:
|
||||||
- YYYY-MM-DD | موضوع | مسیر فایل(ها) | نکتهٔ ۱ خطی
|
- YYYY-MM-DD | موضوع | مسیر فایل(ها) | نکتهٔ ۱ خطی
|
||||||
|
|
||||||
|
- 2026-10-19 | قفل احراز واریز/هدیه | `useWalletPage` + `useGiftCardPage` + `assertCustomerIdentityVerified` روی withdraw و giftcard/use | is_legal=0 دیالوگ میدهد و API هم رد میکند
|
||||||
|
- 2026-10-19 | آیکون پشتیبانی/هشدار/قوانین | `assets/img/boursecu-support-icon.png` + `price-trigger` + `rules` و `navigation.ts` / `useUserPage.ts` | آدمک=پشتیبانی، زنگ دلار=هشدار قیمت سایدبار، برگه=قوانین
|
||||||
- 2026-09-08 | لیست کارت هدیه کاربر | `views/base/giftCard/` + `api/giftCard.ts` + سرور `GiftCardController::mine` / `GET customer/giftcard/mine` | زیر فرم: available (اختصاصی + عمومی use_once=0 تککد) و history؛ تخفیف فقط نمایش/سبد نه giftcard/use
|
- 2026-09-08 | لیست کارت هدیه کاربر | `views/base/giftCard/` + `api/giftCard.ts` + سرور `GiftCardController::mine` / `GET customer/giftcard/mine` | زیر فرم: available (اختصاصی + عمومی use_once=0 تککد) و history؛ تخفیف فقط نمایش/سبد نه giftcard/use
|
||||||
- 2026-09-08 | تب کارت هدیه/تخفیف در تراکنشها | `transactions/config/boursecuTabs.ts` + `useTransactionsPageData.ts` + `Item.vue` | fetchMode giftcard/discount از historyِ mine؛ واحد تخفیف٪/طلا در Item
|
- 2026-09-08 | تب کارت هدیه/تخفیف در تراکنشها | `transactions/config/boursecuTabs.ts` + `useTransactionsPageData.ts` + `Item.vue` | fetchMode giftcard/discount از historyِ mine؛ واحد تخفیف٪/طلا در Item
|
||||||
- 2026-09-08 | گیر کردن در لاگین بعد رفرش | `app/authBoot.ts` + `setupRouter.ts` | finger unlock در sessionStorage؛ بعد ۳دقیقه fingerprintChecked ریست؛ لاگین بدون userInfo نرود home
|
- 2026-09-08 | گیر کردن در لاگین بعد رفرش | `app/authBoot.ts` + `setupRouter.ts` | finger unlock در sessionStorage؛ بعد ۳دقیقه fingerprintChecked ریست؛ لاگین بدون userInfo نرود home
|
||||||
|
|||||||
Vendored
+1
@@ -56,6 +56,7 @@ declare module 'vue' {
|
|||||||
Format: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/format.vue')['default']
|
Format: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/format.vue')['default']
|
||||||
GoldDayCalculatorModal: typeof import('./src/components/GoldDayCalculatorModal.vue')['default']
|
GoldDayCalculatorModal: typeof import('./src/components/GoldDayCalculatorModal.vue')['default']
|
||||||
GoldLoading: typeof import('./src/components/GoldLoading.vue')['default']
|
GoldLoading: typeof import('./src/components/GoldLoading.vue')['default']
|
||||||
|
IdentityRequiredDialog: typeof import('./src/components/IdentityRequiredDialog.vue')['default']
|
||||||
InstallButton: typeof import('./src/components/InstallButton.vue')['default']
|
InstallButton: typeof import('./src/components/InstallButton.vue')['default']
|
||||||
ListAnim: typeof import('./src/components/ListAnim.vue')['default']
|
ListAnim: typeof import('./src/components/ListAnim.vue')['default']
|
||||||
Mode: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/mode.vue')['default']
|
Mode: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/mode.vue')['default']
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<van-dialog
|
||||||
|
v-model:show="open"
|
||||||
|
class="completeButtonStyle"
|
||||||
|
confirm-button-text="تکمیل اطلاعات"
|
||||||
|
show-confirm-button
|
||||||
|
@confirm="goProfile"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center p-2">
|
||||||
|
<BasicIcon color="orange" icon="vuesax-linear:info-circle" size="50" />
|
||||||
|
<div class="mt-2 text-center">{{ message }}</div>
|
||||||
|
</div>
|
||||||
|
</van-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
|
import router from '@/router'
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
show?: boolean
|
||||||
|
action?: string
|
||||||
|
}>(),
|
||||||
|
{ show: false, action: 'این کار' },
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:show': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const open = computed({
|
||||||
|
get: () => props.show,
|
||||||
|
set: (value: boolean) => emit('update:show', value),
|
||||||
|
})
|
||||||
|
|
||||||
|
const message = computed(
|
||||||
|
() =>
|
||||||
|
`برای ${props.action}، لطفا اطلاعات خود را تکمیل کنید یا در صورت تکمیل اطلاعات، منتظر تایید اطلاعات خود بمانید.`,
|
||||||
|
)
|
||||||
|
|
||||||
|
function goProfile() {
|
||||||
|
router.push({ name: 'user' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -57,9 +57,21 @@ const BOURSECU_PRIMARY_NAV: AppNavItem[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export const SECONDARY_NAV: AppNavItem[] = [
|
export const SECONDARY_NAV: AppNavItem[] = [
|
||||||
{ id: 'notifyTrigger', routeName: 'notifyTrigger', label: 'هشدار قیمت', icon: 'vuesax-linear:bill' },
|
{
|
||||||
|
id: 'notifyTrigger',
|
||||||
|
routeName: 'notifyTrigger',
|
||||||
|
label: 'هشدار قیمت',
|
||||||
|
icon: 'vuesax-linear:bill',
|
||||||
|
iconSrc: getBoursecuActionIconSrc('priceTrigger'),
|
||||||
|
},
|
||||||
{ id: 'about', routeName: 'about', label: 'درباره ما', icon: 'vuesax-linear:info-circle' },
|
{ id: 'about', routeName: 'about', label: 'درباره ما', icon: 'vuesax-linear:info-circle' },
|
||||||
{ id: 'rules', routeName: 'rules', label: 'قوانین و مقررات', icon: 'vuesax-linear:task' },
|
{
|
||||||
|
id: 'rules',
|
||||||
|
routeName: 'rules',
|
||||||
|
label: 'قوانین و مقررات',
|
||||||
|
icon: 'vuesax-linear:task',
|
||||||
|
iconSrc: getBoursecuActionIconSrc('rules'),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export function getPrimaryNavItems(): AppNavItem[] {
|
export function getPrimaryNavItems(): AppNavItem[] {
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import boursecuBuySellIcon from '@/assets/img/boursecu-buy-sell-icon.png'
|
|||||||
import boursecuAssetsIcon from '@/assets/img/boursecu-assets-icon.png'
|
import boursecuAssetsIcon from '@/assets/img/boursecu-assets-icon.png'
|
||||||
import boursecuHomeIcon from '@/assets/img/boursecu-home-icon.png'
|
import boursecuHomeIcon from '@/assets/img/boursecu-home-icon.png'
|
||||||
import boursecuSettingsIcon from '@/assets/img/boursecu-settings-icon.png'
|
import boursecuSettingsIcon from '@/assets/img/boursecu-settings-icon.png'
|
||||||
|
import boursecuSupportIcon from '@/assets/img/boursecu-support-icon.png'
|
||||||
|
import boursecuPriceTriggerIcon from '@/assets/img/boursecu-price-trigger-icon.png'
|
||||||
|
import boursecuRulesIcon from '@/assets/img/boursecu-rules-icon.png'
|
||||||
|
|
||||||
export function getConfigApp(key: string) {
|
export function getConfigApp(key: string) {
|
||||||
const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {}
|
const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {}
|
||||||
@@ -77,6 +80,9 @@ export function getBoursecuActionIconSrc(actionId: string): string | undefined {
|
|||||||
convert: boursecuConvertIcon,
|
convert: boursecuConvertIcon,
|
||||||
home: boursecuHomeIcon,
|
home: boursecuHomeIcon,
|
||||||
settings: boursecuSettingsIcon,
|
settings: boursecuSettingsIcon,
|
||||||
|
support: boursecuSupportIcon,
|
||||||
|
priceTrigger: boursecuPriceTriggerIcon,
|
||||||
|
rules: boursecuRulesIcon,
|
||||||
}
|
}
|
||||||
|
|
||||||
return icons[actionId]
|
return icons[actionId]
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/** احراز نشده = is_legal صفر. ۱ و ۲ یعنی اطلاعات ثبت شده و معامله مجاز است. */
|
||||||
|
export function isCustomerIdentityVerified(isLegal: unknown): boolean {
|
||||||
|
return Number(isLegal ?? 0) !== 0
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
type CustomerGiftCardItem,
|
type CustomerGiftCardItem,
|
||||||
} from '@/api/giftCard'
|
} from '@/api/giftCard'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
import { isCustomerIdentityVerified } from '@/utils/customerIdentity'
|
||||||
|
|
||||||
export interface GiftCardPageContext {
|
export interface GiftCardPageContext {
|
||||||
form: Ref<{ code: string }>
|
form: Ref<{ code: string }>
|
||||||
@@ -17,6 +19,7 @@ export interface GiftCardPageContext {
|
|||||||
listLoading: Ref<boolean>
|
listLoading: Ref<boolean>
|
||||||
loadMyGiftCards: () => Promise<void>
|
loadMyGiftCards: () => Promise<void>
|
||||||
fillCode: (code: string) => void
|
fillCode: (code: string) => void
|
||||||
|
identityRequiredVisible: Ref<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GIFT_CARD_PAGE_CONTEXT_KEY: InjectionKey<GiftCardPageContext> =
|
export const GIFT_CARD_PAGE_CONTEXT_KEY: InjectionKey<GiftCardPageContext> =
|
||||||
@@ -29,6 +32,8 @@ export function useGiftCardPage() {
|
|||||||
const available = ref<CustomerGiftCardItem[]>([])
|
const available = ref<CustomerGiftCardItem[]>([])
|
||||||
const history = ref<CustomerGiftCardItem[]>([])
|
const history = ref<CustomerGiftCardItem[]>([])
|
||||||
const listLoading = ref(false)
|
const listLoading = ref(false)
|
||||||
|
const identityRequiredVisible = ref(false)
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const disableButton = computed(() => {
|
const disableButton = computed(() => {
|
||||||
if (!loading.value) {
|
if (!loading.value) {
|
||||||
@@ -56,6 +61,10 @@ export function useGiftCardPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function registerGiftCard() {
|
async function registerGiftCard() {
|
||||||
|
if (!isCustomerIdentityVerified(userStore.userInfo?.is_legal)) {
|
||||||
|
identityRequiredVisible.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
const code = form.value.code.trim()
|
const code = form.value.code.trim()
|
||||||
if (!code) return
|
if (!code) return
|
||||||
|
|
||||||
@@ -94,6 +103,7 @@ export function useGiftCardPage() {
|
|||||||
listLoading,
|
listLoading,
|
||||||
loadMyGiftCards,
|
loadMyGiftCards,
|
||||||
fillCode,
|
fillCode,
|
||||||
|
identityRequiredVisible,
|
||||||
}
|
}
|
||||||
|
|
||||||
provide(GIFT_CARD_PAGE_CONTEXT_KEY, context)
|
provide(GIFT_CARD_PAGE_CONTEXT_KEY, context)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<div class="gift-card-page-root">
|
<div class="gift-card-page-root">
|
||||||
<DesktopGiftCardView v-show="isDesktop" />
|
<DesktopGiftCardView v-show="isDesktop" />
|
||||||
<MobileGiftCardView v-show="!isDesktop" />
|
<MobileGiftCardView v-show="!isDesktop" />
|
||||||
|
<IdentityRequiredDialog v-model:show="identityRequiredVisible" action="ثبت کد هدیه" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -10,10 +11,11 @@ import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
|||||||
import { useGiftCardPage } from './composables/useGiftCardPage'
|
import { useGiftCardPage } from './composables/useGiftCardPage'
|
||||||
import DesktopGiftCardView from './desktop/DesktopGiftCardView.vue'
|
import DesktopGiftCardView from './desktop/DesktopGiftCardView.vue'
|
||||||
import MobileGiftCardView from './mobile/MobileGiftCardView.vue'
|
import MobileGiftCardView from './mobile/MobileGiftCardView.vue'
|
||||||
|
import IdentityRequiredDialog from '@/components/IdentityRequiredDialog.vue'
|
||||||
|
|
||||||
const isDesktop = useDesktopBreakpoint()
|
const isDesktop = useDesktopBreakpoint()
|
||||||
|
|
||||||
useGiftCardPage()
|
const { identityRequiredVisible } = useGiftCardPage()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ export function useUserPage() {
|
|||||||
id: 'support',
|
id: 'support',
|
||||||
label: 'پشتیبانی',
|
label: 'پشتیبانی',
|
||||||
icon: 'vuesax-linear:messages-2',
|
icon: 'vuesax-linear:messages-2',
|
||||||
|
iconSrc: getBoursecuActionIconSrc('support'),
|
||||||
onClick: () => router.push({ name: 'support' }),
|
onClick: () => router.push({ name: 'support' }),
|
||||||
visible: isUserSupportChatEnabled(),
|
visible: isUserSupportChatEnabled(),
|
||||||
badge: unreadSupportCount.value > 0 ? unreadSupportCount.value : undefined,
|
badge: unreadSupportCount.value > 0 ? unreadSupportCount.value : undefined,
|
||||||
@@ -187,6 +188,7 @@ export function useUserPage() {
|
|||||||
id: 'rules',
|
id: 'rules',
|
||||||
label: 'قوانین و مقررات',
|
label: 'قوانین و مقررات',
|
||||||
icon: 'vuesax-linear:task',
|
icon: 'vuesax-linear:task',
|
||||||
|
iconSrc: getBoursecuActionIconSrc('rules'),
|
||||||
onClick: () => router.push({ name: 'rules' }),
|
onClick: () => router.push({ name: 'rules' }),
|
||||||
visible: true,
|
visible: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { useInfiniteScroll } from '@vueuse/core'
|
|||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import type { Card } from '@/models/wallet'
|
import type { Card } from '@/models/wallet'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
import { isCustomerIdentityVerified } from '@/utils/customerIdentity'
|
||||||
import { useConfigStore } from '@/store/configStore'
|
import { useConfigStore } from '@/store/configStore'
|
||||||
import {
|
import {
|
||||||
getWalletDepositQuickAmountsRial,
|
getWalletDepositQuickAmountsRial,
|
||||||
@@ -37,6 +38,7 @@ export interface WalletPageContext {
|
|||||||
showConfirmCode: Ref<boolean>
|
showConfirmCode: Ref<boolean>
|
||||||
withdrawInfModal: Ref<boolean>
|
withdrawInfModal: Ref<boolean>
|
||||||
infoVisible: Ref<boolean>
|
infoVisible: Ref<boolean>
|
||||||
|
identityRequiredVisible: Ref<boolean>
|
||||||
refOtpInput: Ref<InstanceType<typeof OtpInput> | null>
|
refOtpInput: Ref<InstanceType<typeof OtpInput> | null>
|
||||||
form: Ref<Transaction>
|
form: Ref<Transaction>
|
||||||
extraNote: Ref<string>
|
extraNote: Ref<string>
|
||||||
@@ -84,6 +86,13 @@ export function useWalletPage() {
|
|||||||
const chargeAccountModal = ref(false)
|
const chargeAccountModal = ref(false)
|
||||||
const showConfirmCode = ref(false)
|
const showConfirmCode = ref(false)
|
||||||
const withdrawInfModal = ref(false)
|
const withdrawInfModal = ref(false)
|
||||||
|
const identityRequiredVisible = ref(false)
|
||||||
|
|
||||||
|
function ensureIdentity(): boolean {
|
||||||
|
if (isCustomerIdentityVerified(userStore.userInfo?.is_legal)) return true
|
||||||
|
identityRequiredVisible.value = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
const infoVisible = ref(false)
|
const infoVisible = ref(false)
|
||||||
const refOtpInput = ref<InstanceType<typeof OtpInput> | null>(null)
|
const refOtpInput = ref<InstanceType<typeof OtpInput> | null>(null)
|
||||||
const mobileDepositScrollEl = ref<HTMLElement | null>(null)
|
const mobileDepositScrollEl = ref<HTMLElement | null>(null)
|
||||||
@@ -190,6 +199,7 @@ export function useWalletPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function ok() {
|
async function ok() {
|
||||||
|
if (!ensureIdentity()) return
|
||||||
try {
|
try {
|
||||||
if (form.value.value !== undefined) {
|
if (form.value.value !== undefined) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -211,6 +221,7 @@ export function useWalletPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleOnComplete(value: string) {
|
async function handleOnComplete(value: string) {
|
||||||
|
if (!ensureIdentity()) return
|
||||||
try {
|
try {
|
||||||
infoVisible.value = true
|
infoVisible.value = true
|
||||||
showConfirmCode.value = false
|
showConfirmCode.value = false
|
||||||
@@ -268,6 +279,7 @@ export function useWalletPage() {
|
|||||||
showConfirmCode,
|
showConfirmCode,
|
||||||
withdrawInfModal,
|
withdrawInfModal,
|
||||||
infoVisible,
|
infoVisible,
|
||||||
|
identityRequiredVisible,
|
||||||
refOtpInput,
|
refOtpInput,
|
||||||
form,
|
form,
|
||||||
extraNote,
|
extraNote,
|
||||||
|
|||||||
@@ -78,6 +78,8 @@
|
|||||||
|
|
||||||
<SkeletonLoading v-model:show="infoVisible" />
|
<SkeletonLoading v-model:show="infoVisible" />
|
||||||
|
|
||||||
|
<IdentityRequiredDialog v-model:show="identityRequiredVisible" action="واریز یا برداشت" />
|
||||||
|
|
||||||
<WalletDepositGuideModal
|
<WalletDepositGuideModal
|
||||||
v-model:visible="chargeAccountModal"
|
v-model:visible="chargeAccountModal"
|
||||||
:support-link="depositGuideSupportLink"
|
:support-link="depositGuideSupportLink"
|
||||||
@@ -117,6 +119,7 @@ import MobileWalletView from './mobile/MobileWalletView.vue'
|
|||||||
import SkeletonLoading from './components/SkeletonLoading.vue'
|
import SkeletonLoading from './components/SkeletonLoading.vue'
|
||||||
import WalletAddCardForm from './components/WalletAddCardForm.vue'
|
import WalletAddCardForm from './components/WalletAddCardForm.vue'
|
||||||
import WalletDepositGuideModal from './components/WalletDepositGuideModal.vue'
|
import WalletDepositGuideModal from './components/WalletDepositGuideModal.vue'
|
||||||
|
import IdentityRequiredDialog from '@/components/IdentityRequiredDialog.vue'
|
||||||
|
|
||||||
const isDesktop = useDesktopBreakpoint()
|
const isDesktop = useDesktopBreakpoint()
|
||||||
|
|
||||||
@@ -126,6 +129,7 @@ const {
|
|||||||
showConfirmCode,
|
showConfirmCode,
|
||||||
withdrawInfModal,
|
withdrawInfModal,
|
||||||
infoVisible,
|
infoVisible,
|
||||||
|
identityRequiredVisible,
|
||||||
refOtpInput,
|
refOtpInput,
|
||||||
extraNote,
|
extraNote,
|
||||||
supportMessengerText,
|
supportMessengerText,
|
||||||
|
|||||||
Reference in New Issue
Block a user