Merge branch 'modular' into modulartest
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews -Indexes
|
||||
</IfModule>
|
||||
|
||||
# Handle Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
#RewriteBase /
|
||||
RewriteRule ^ /index.html [L]
|
||||
|
||||
|
||||
</IfModule>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
import {usePrefix} from "@/utils/usePrefix";
|
||||
import {computed} from "vue";
|
||||
import {usePrice} from "@/utils/usePrice";
|
||||
import {persianToEnglishDigits, shouldNormalizeInputDigits} from '@/utils/persianDigits';
|
||||
|
||||
//Props
|
||||
interface Prop {
|
||||
@@ -59,9 +60,7 @@ const addonTextColor = computed(() => props.addonTextColor)
|
||||
//emits
|
||||
const localValue = computed({
|
||||
set(value) {
|
||||
props.type === 'price'
|
||||
? emits('update:value', value?.toString().replaceAll(',', ''))
|
||||
: emits('update:value', value)
|
||||
emits('update:value', normalizeValue(value))
|
||||
},
|
||||
get() {
|
||||
return props.type === 'price'
|
||||
@@ -75,16 +74,26 @@ const props = withDefaults(defineProps<Prop>(), {
|
||||
})
|
||||
const emits = defineEmits(['update:value', 'onchange'])
|
||||
|
||||
const handlerChange = (e: any) => {
|
||||
props.type === 'price'
|
||||
? emits('update:value', e.target.value?.toString().replaceAll(',', ''))
|
||||
: emits('update:value', e.target.value)
|
||||
props.type === 'price'
|
||||
? emits('onchange', e.target.value?.toString().replaceAll(',', ''))
|
||||
: emits('onchange', e.target.value)
|
||||
function normalizeValue(value: unknown) {
|
||||
if (value == null || value === '') {
|
||||
return value
|
||||
}
|
||||
|
||||
let normalized = String(value)
|
||||
if (shouldNormalizeInputDigits(props)) {
|
||||
normalized = persianToEnglishDigits(normalized)
|
||||
}
|
||||
if (props.type === 'price') {
|
||||
normalized = normalized.replaceAll(',', '')
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
//css style
|
||||
const handlerChange = (e: any) => {
|
||||
const normalized = normalizeValue(e.target.value)
|
||||
emits('update:value', normalized)
|
||||
emits('onchange', normalized)
|
||||
}
|
||||
const {prefixCls} = usePrefix('input')
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BasicInput v-model:value="_val">
|
||||
<BasicInput v-model:value="_val" type="price">
|
||||
<template v-for="(_, slotKey) in $slots" #[slotKey]="data">
|
||||
<slot :name="slotKey" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
@@ -10,6 +10,7 @@
|
||||
import {usePrice} from '@/utils/usePrice'
|
||||
import {computed} from "vue";
|
||||
import BasicInput from "../../BasicInput.vue";
|
||||
import {persianToEnglishDigits} from '@/utils/persianDigits'
|
||||
|
||||
interface Props {
|
||||
value?: string | number
|
||||
@@ -23,7 +24,7 @@ const props = withDefaults(defineProps<Props>(), {})
|
||||
|
||||
const _val = computed({
|
||||
set(val: string) {
|
||||
emit('update:value', val.toString().replaceAll(',', ''))
|
||||
emit('update:value', persianToEnglishDigits(val).replaceAll(',', ''))
|
||||
},
|
||||
get() {
|
||||
return props.value ? usePrice(props.value as number, true, '').trim() : ''
|
||||
|
||||
@@ -78,6 +78,10 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
const metalTypeLabel = computed(() => resolveMetalTypeLabel(metalTypeSettingValue.value))
|
||||
const meltedGoldLabel = computed(() => resolveMeltedGoldLabel(metalTypeSettingValue.value))
|
||||
const tradeWeightUnit = computed(() => resolveTradeWeightUnit(getSettingByKey('unit')))
|
||||
const isReferralRequired = computed(() => {
|
||||
const value = getSettingByKey('referral_id')
|
||||
return String(value ?? '').trim().toLowerCase() === 'required'
|
||||
})
|
||||
|
||||
return {
|
||||
settings,
|
||||
@@ -86,6 +90,7 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
mergeSettings,
|
||||
getSettingByKey,
|
||||
isSettingTruthy,
|
||||
isReferralRequired,
|
||||
metalTypeSettingValue,
|
||||
metalTypeLabel,
|
||||
meltedGoldLabel,
|
||||
|
||||
@@ -33,12 +33,13 @@ import type {Rule} from 'ant-design-vue/es/form'
|
||||
import {useWalletStore} from "@/store/walletStore";
|
||||
import { isCashLikeDealMethod, isMainMetalWalletDealMethod, getInsufficientCashWalletMessage, getInsufficientGoldWalletMessage } from '@/utils/productDealMethod'
|
||||
import { getCashWalletBalance } from '@/utils/productAssets'
|
||||
import { persianToEnglishDigits } from '@/utils/persianDigits'
|
||||
|
||||
export const mobileValidation = (title?: string): Rule => {
|
||||
const label = title ?? 'شماره تلفن'
|
||||
return {
|
||||
validator: (_rule, value) => {
|
||||
const normalized = String(value ?? '').trim()
|
||||
const normalized = persianToEnglishDigits(String(value ?? '').trim())
|
||||
if (!normalized) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
@@ -273,7 +274,7 @@ export const passwordRules = () => {
|
||||
export const englishNumberRule = () => {
|
||||
return {
|
||||
validator: (_: any, value: string | number) => {
|
||||
const str = value != null ? String(value) : ''
|
||||
const str = persianToEnglishDigits(value)
|
||||
if (!str) return Promise.resolve()
|
||||
const isEnglishDigitsOnly = /^[0-9]+$/.test(str)
|
||||
if (!isEnglishDigitsOnly) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹'
|
||||
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩'
|
||||
|
||||
/** تبدیل ارقام فارسی/عربی و جداکننده اعشار به فرمت انگلیسی */
|
||||
export function persianToEnglishDigits(value: string | number | null | undefined): string {
|
||||
if (value == null) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return String(value)
|
||||
.replace(/[۰-۹]/g, (d) => String(PERSIAN_DIGITS.indexOf(d)))
|
||||
.replace(/[٠-٩]/g, (d) => String(ARABIC_DIGITS.indexOf(d)))
|
||||
.replace(/٫/g, '.')
|
||||
}
|
||||
|
||||
export function shouldNormalizeInputDigits(options: {
|
||||
isPassword?: boolean
|
||||
type?: string
|
||||
inputmode?: string
|
||||
}): boolean {
|
||||
if (options.isPassword) {
|
||||
return false
|
||||
}
|
||||
if (options.type === 'price' || options.type === 'number') {
|
||||
return true
|
||||
}
|
||||
const mode = String(options.inputmode ?? '').trim().toLowerCase()
|
||||
return mode === 'numeric' || mode === 'decimal' || mode === 'number'
|
||||
}
|
||||
@@ -1,5 +1,31 @@
|
||||
/* Desktop standalone gold trade page */
|
||||
|
||||
.gold-page {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.gold-embed {
|
||||
max-height: min(70vh, 35rem);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.gold-form-card {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.gold-fee-box {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.dgold {
|
||||
min-height: 100%;
|
||||
background: #f6f7f9;
|
||||
@@ -73,7 +99,27 @@
|
||||
margin-bottom: 2rem !important;
|
||||
}
|
||||
|
||||
.dgold__panel .bottom-18 {
|
||||
.dgold__panel .gold-fee-box {
|
||||
position: static !important;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.dgold {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.dgold-topbar {
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.dgold-topbar__title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.dgold__panel {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="isStandaloneDesktop ? 'dgold' : 'h-35rem overflow-y-auto'">
|
||||
<div :class="rootClass">
|
||||
<header v-if="isStandaloneDesktop" class="dgold-topbar">
|
||||
<button type="button" class="dgold-topbar__back" @click="router.back()">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="18" />
|
||||
@@ -10,8 +10,18 @@
|
||||
<h1 class="dgold-topbar__title">خرید و فروش {{ tradeProductLabel }}</h1>
|
||||
</div>
|
||||
</header>
|
||||
<div :class="isStandaloneDesktop ? 'dgold__panel' : ''">
|
||||
<div class=" !overflow-y-scroll bg-white py-3 text-primary font-bold flex justify-evenly">
|
||||
<MobilePageHeader
|
||||
v-else-if="isStandaloneMobile"
|
||||
:title="`خرید و فروش ${tradeProductLabel}`"
|
||||
show-back
|
||||
/>
|
||||
<div :class="panelClass">
|
||||
<div
|
||||
:class="[
|
||||
'!overflow-y-scroll bg-white py-3 text-primary font-bold flex justify-evenly',
|
||||
isStandaloneMobile ? 'mpage-subheader wrap-fix' : '',
|
||||
]"
|
||||
>
|
||||
<!-- <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>-->
|
||||
<div class="flex justify-evenly w-full px-2 h-11 items-center bg-gray-50 relative rounded ">
|
||||
@@ -28,11 +38,11 @@
|
||||
class="absolute bg-white h-8 w-1/2 rounded-lg animate right"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="userStore" class=" mt-5 mb-25vh pb-2">
|
||||
<div v-if="userStore" :class="contentClass">
|
||||
<a-spin :spinning="!price">
|
||||
<div v-if="!loading" :class="!isBuy? ' bg-red-100' : ' bg-green-100'"
|
||||
class="w-full relative rounded-lg h-[88px] flex flex-col justify-center background-color-transition">
|
||||
<img :src="img1" alt="" class="h-[88px] absolute">
|
||||
<img :src="img1" alt="" class="h-[88px] absolute inset-0 w-full object-cover">
|
||||
<div class="w-full items-center flex flex-col">
|
||||
<div class="text-gray-700 font-bold">{{ !isBuy ? 'قیمت فروش' : 'قیمت خرید' }}</div>
|
||||
<div v-if="price" class="flex gap-1 mt-3 text-xs items-center">
|
||||
@@ -53,7 +63,7 @@
|
||||
</div>
|
||||
</a-spin>
|
||||
<a-form :model="form" class="!mt-5" name="basic" @finish="okConfirm()">
|
||||
<div class="border-gray-200 border-1 rounded-xl p-5 h-58 relative">
|
||||
<div class="border-gray-200 border-1 rounded-xl p-5 gold-form-card relative">
|
||||
<bg_main />
|
||||
<div class="z-100 relative">
|
||||
<a-form-item>
|
||||
@@ -138,7 +148,7 @@
|
||||
موجودی کیف پول ناکافی می باشد.
|
||||
</div>
|
||||
</a-form-item>
|
||||
<div class=" w-full bottom-18 px-6 rounded-xl py-1 border-1 bg-white">
|
||||
<div class="w-full px-4 rounded-xl py-2 border-1 bg-white gold-fee-box">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>کارمزد:</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
@@ -278,6 +288,7 @@ import { useUserStore } from '@/store/userStore'
|
||||
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
|
||||
import BasicModal from '@/components/BasicPacks/BasicModal.vue'
|
||||
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
||||
import { justNumber } from '@/utils/inputRules'
|
||||
import GoldSkeleton from '@/views/base/gold/components/GoldSkeleton.vue'
|
||||
import GoldLoading from '@/components/GoldLoading.vue'
|
||||
@@ -336,6 +347,20 @@ interface Prop {
|
||||
const props = withDefaults(defineProps<Prop>(), {})
|
||||
const isDesktop = useDesktopBreakpoint()
|
||||
const isStandaloneDesktop = computed(() => isDesktop.value && !props.ProductItem)
|
||||
const isStandaloneMobile = computed(() => !isDesktop.value && !props.ProductItem)
|
||||
const rootClass = computed(() => {
|
||||
if (isStandaloneDesktop.value) return 'dgold'
|
||||
if (props.ProductItem) return 'gold-embed'
|
||||
return 'gold-page'
|
||||
})
|
||||
const panelClass = computed(() => (isStandaloneDesktop.value ? 'dgold__panel' : ''))
|
||||
const contentClass = computed(() => {
|
||||
if (isStandaloneDesktop.value) return 'mt-5 mb-25vh pb-2'
|
||||
if (isStandaloneMobile.value) {
|
||||
return 'px-3 mpage-content mpage-content--with-subheader pb-[calc(var(--app-bottom-chrome-height)+1rem)]'
|
||||
}
|
||||
return 'mt-5 px-3 pb-4'
|
||||
})
|
||||
const isBuy = ref<boolean>(true)
|
||||
|
||||
const tradeProductLabel = computed(() => {
|
||||
|
||||
@@ -277,6 +277,7 @@ import Icon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
|
||||
import { justNumber2, justNumber3, required } from '@/utils/inputRules'
|
||||
import { persianToEnglishDigits } from '@/utils/persianDigits'
|
||||
import GoldSkeleton from '@/views/base/gold/components/GoldSkeleton.vue'
|
||||
import GoldLoading from '@/components/GoldLoading.vue'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
@@ -678,16 +679,7 @@ watch(
|
||||
)
|
||||
|
||||
// تابع تبدیل اعداد فارسی به انگلیسی
|
||||
const persianToEnglish = (str: string | number | undefined): string => {
|
||||
if (!str) return ''
|
||||
const persianNumbers = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']
|
||||
const englishNumbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
|
||||
let result = str.toString()
|
||||
for (let i = 0; i < 10; i++) {
|
||||
result = result.replace(new RegExp(persianNumbers[i], 'g'), englishNumbers[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
const persianToEnglish = persianToEnglishDigits
|
||||
|
||||
watch(() => form.value.goldValue, (newVal) => {
|
||||
if (newVal !== undefined && newVal !== null && newVal !== '') {
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:rules="isBoursecuProject() ? [] : [required()]"
|
||||
:rules="referralIdRules"
|
||||
:class="variant === 'desktop' ? '' : 'home_appear_fade_in_down'"
|
||||
label="کد معرف"
|
||||
name="referral_id"
|
||||
@@ -88,7 +88,7 @@
|
||||
<BasicInput
|
||||
v-model:value="userFormState.referral_id"
|
||||
:disabled="disableForm"
|
||||
:place-holder="variant === 'desktop' ? 'کد معرف (اختیاری)' : 'کد معرف'"
|
||||
:place-holder="referralIdPlaceholder"
|
||||
:size="variant === 'desktop' ? 'large' : undefined"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -226,7 +226,7 @@ import type { User } from '@/models/login'
|
||||
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import { loading, URLUserType } from '@/utils'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import registerDelayImage from '@/assets/img/registerDelay.svg'
|
||||
@@ -234,7 +234,8 @@ import DialogVantOneButton from '@/components/DialogVantOneButton.vue'
|
||||
import { registerCustomer } from '@/api/login'
|
||||
import router from '@/router'
|
||||
import {useConfigStore} from "@/store/configStore";
|
||||
import {getProjectLoginLogo, getProjectLoginTaglineLines, hasCustomLoginLogoLayout, isBoursecuProject} from "@/utils/config";
|
||||
import {useSettingsStore} from '@/store/settingsStore'
|
||||
import {getProjectLoginLogo, getProjectLoginTaglineLines, hasCustomLoginLogoLayout} from "@/utils/config";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
@@ -244,6 +245,13 @@ withDefaults(
|
||||
)
|
||||
|
||||
const config = useConfigStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const referralIdRules = computed(() =>
|
||||
settingsStore.isReferralRequired ? [required('کد معرف')] : [],
|
||||
)
|
||||
const referralIdPlaceholder = computed(() =>
|
||||
settingsStore.isReferralRequired ? 'کد معرف' : 'کد معرف (اختیاری)',
|
||||
)
|
||||
|
||||
// const stepTwoVerify = ref<boolean>(false)
|
||||
const registeredInfo = ref<boolean>(false)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { mobileValidation, required } from '@/utils/inputRules'
|
||||
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
|
||||
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||
import DialogVantOneButton from '@/components/DialogVantOneButton.vue'
|
||||
import registerDelayImage from '@/assets/img/registerDelay.svg'
|
||||
import { useConfigStore } from '@/store/configStore'
|
||||
import { isBoursecuProject } from '@/utils/config'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import { useLoginPageContext } from '../../composables/useLoginPage'
|
||||
import router from '@/router'
|
||||
|
||||
@@ -14,6 +15,13 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const config = useConfigStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const referralIdRules = computed(() =>
|
||||
settingsStore.isReferralRequired ? [required('کد معرف')] : [],
|
||||
)
|
||||
const referralIdPlaceholder = computed(() =>
|
||||
settingsStore.isReferralRequired ? 'کد معرف' : 'کد معرف (اختیاری)',
|
||||
)
|
||||
const {
|
||||
registeredInfo,
|
||||
disableForm,
|
||||
@@ -87,14 +95,14 @@ function handleSuccessDismiss() {
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:rules="isBoursecuProject() ? [] : [required()]"
|
||||
:rules="referralIdRules"
|
||||
label="کد معرف"
|
||||
name="referral_id"
|
||||
>
|
||||
<BasicInput
|
||||
v-model:value="userFormState.referral_id"
|
||||
:disabled="disableForm"
|
||||
place-holder="کد معرف (اختیاری)"
|
||||
:place-holder="referralIdPlaceholder"
|
||||
size="large"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
Reference in New Issue
Block a user