This commit is contained in:
2026-07-02 00:34:07 +03:30
parent e097f6b2bb
commit a41dd6ef9e
12 changed files with 93 additions and 26 deletions
+5 -2
View File
@@ -13,6 +13,7 @@
:size="props.size" :size="props.size"
:type="props.type" :type="props.type"
class="" class=""
@input="handlerChange"
@change="handlerChange" @change="handlerChange"
/> />
<a-input <a-input
@@ -27,6 +28,7 @@
:inputmode="props.inputmode" :inputmode="props.inputmode"
:pattern="props.pattern" :pattern="props.pattern"
class="" class=""
@input="handlerChange"
@change="handlerChange" @change="handlerChange"
/> />
</div> </div>
@@ -89,8 +91,9 @@ function normalizeValue(value: unknown) {
return normalized return normalized
} }
const handlerChange = (e: any) => { const handlerChange = (e: Event | string) => {
const normalized = normalizeValue(e.target.value) const raw = typeof e === 'string' ? e : (e.target as HTMLInputElement | null)?.value
const normalized = normalizeValue(raw)
emits('update:value', normalized) emits('update:value', normalized)
emits('onchange', normalized) emits('onchange', normalized)
} }
+30 -16
View File
@@ -38,6 +38,7 @@ import { persianToEnglishDigits } from '@/utils/persianDigits'
export const mobileValidation = (title?: string): Rule => { export const mobileValidation = (title?: string): Rule => {
const label = title ?? 'شماره تلفن' const label = title ?? 'شماره تلفن'
return { return {
transform: (value) => persianToEnglishDigits(String(value ?? '').trim()),
validator: (_rule, value) => { validator: (_rule, value) => {
const normalized = persianToEnglishDigits(String(value ?? '').trim()) const normalized = persianToEnglishDigits(String(value ?? '').trim())
if (!normalized) { if (!normalized) {
@@ -94,14 +95,20 @@ const isValidNationalCode = (code) => {
return +chars[9] === lastDigit return +chars[9] === lastDigit
} }
export const requireNationalCode = (code?: string) => { export const requireNationalCode = (code?: string) => {
return {required: true, validator: validateNCode, trigger: 'change'} return {
required: true,
transform: (value) => persianToEnglishDigits(String(value ?? '').trim()),
validator: validateNCode,
trigger: 'change',
}
} }
async function validateNCode(rule: Rule, value: any) { async function validateNCode(rule: Rule, value: any) {
if (!value) { const normalized = persianToEnglishDigits(String(value ?? '').trim())
if (!normalized) {
return Promise.reject('وارد کردن کد ملی الزامیست') return Promise.reject('وارد کردن کد ملی الزامیست')
} }
if (isValidNationalCode(value)) return Promise.resolve() if (isValidNationalCode(normalized)) return Promise.resolve()
else return Promise.reject('کد ملی وارد شده نامعتبر است') else return Promise.reject('کد ملی وارد شده نامعتبر است')
} }
@@ -153,13 +160,16 @@ let isBuyP=true
let NumP=0 let NumP=0
let groupTypeP: string | undefined let groupTypeP: string | undefined
let meltedBalanceP = 0 let meltedBalanceP = 0
const checkNumber = async (_rule: Rule, value: number) => { const checkNumber = async (_rule: Rule, value: number | string) => {
const normalizedRaw = persianToEnglishDigits(String(value ?? '').trim())
if (!normalizedRaw) {
if (!value) {
return Promise.reject('تعداد محصول را مشخص کنید'); return Promise.reject('تعداد محصول را مشخص کنید');
} }
if (value % 1 !== 0) { const numericValue = Number(normalizedRaw)
if (!Number.isFinite(numericValue)) {
return Promise.reject('تعداد محصول را مشخص کنید');
}
if (numericValue % 1 !== 0) {
return Promise.reject('استفاده از اعداد اعشاری مجاز نمی باشد') return Promise.reject('استفاده از اعداد اعشاری مجاز نمی باشد')
} }
if (isBuyP){ if (isBuyP){
@@ -170,14 +180,14 @@ if (isBuyP){
if (!meltedBalanceP && isMainMetalWalletDealMethod(methodP)){ if (!meltedBalanceP && isMainMetalWalletDealMethod(methodP)){
return Promise.reject(getInsufficientGoldWalletMessage(groupTypeP, methodP)) return Promise.reject(getInsufficientGoldWalletMessage(groupTypeP, methodP))
} }
if (priceP * value > availableCash && isCashLikeDealMethod(methodP)) if (priceP * numericValue > availableCash && isCashLikeDealMethod(methodP))
return Promise.reject(getInsufficientCashWalletMessage(groupTypeP)) return Promise.reject(getInsufficientCashWalletMessage(groupTypeP))
if (amountP * value > meltedBalanceP && isMainMetalWalletDealMethod(methodP)) if (amountP * numericValue > meltedBalanceP && isMainMetalWalletDealMethod(methodP))
return Promise.reject(getInsufficientGoldWalletMessage(groupTypeP, methodP)) return Promise.reject(getInsufficientGoldWalletMessage(groupTypeP, methodP))
else else
return Promise.resolve(); return Promise.resolve();
}else { }else {
if (value > NumP) if (numericValue > NumP)
return Promise.reject('موجودی محصول ناکافی است') return Promise.reject('موجودی محصول ناکافی است')
else else
return Promise.resolve(); return Promise.resolve();
@@ -200,6 +210,7 @@ export const justNumber2 = (
meltedBalanceP = meltedBalance ?? 0 meltedBalanceP = meltedBalance ?? 0
return { return {
required: true, required: true,
transform: (value) => persianToEnglishDigits(String(value ?? '').trim()),
validator: checkNumber, validator: checkNumber,
trigger: 'change' trigger: 'change'
} }
@@ -211,6 +222,7 @@ export const justNumber3 = (price: number, Num?:number ,isBuy?:boolean) => {
isBuyP=isBuy isBuyP=isBuy
return { return {
required: true, required: true,
transform: (value) => persianToEnglishDigits(String(value ?? '').trim()),
validator: checkNumber, validator: checkNumber,
trigger: 'change' trigger: 'change'
} }
@@ -233,14 +245,16 @@ export const guideTypePassword = ref<{}>({
export const passwordRules = () => { export const passwordRules = () => {
return [ return [
{ {
transform: (value) => persianToEnglishDigits(String(value ?? '')),
validator: (rule: Rule, value: any) => { validator: (rule: Rule, value: any) => {
if (value.length < 6) { const normalized = persianToEnglishDigits(String(value ?? ''))
if (normalized.length < 6) {
return Promise.reject('رمز عبور حداقل 6 رقم') return Promise.reject('رمز عبور حداقل 6 رقم')
} }
const lengthCriteria = value.length >= 8 const lengthCriteria = normalized.length >= 8
const letterCriteria = /[A-Za-z]/.test(value) const letterCriteria = /[A-Za-z]/.test(normalized)
const numberCriteria = /\d/.test(value) const numberCriteria = /\d/.test(normalized)
const specialCharacterCriteria = /[@$!%*#?&]/.test(value) const specialCharacterCriteria = /[@$!%*#?&]/.test(normalized)
const criteriaMet = [ const criteriaMet = [
lengthCriteria, lengthCriteria,
letterCriteria, letterCriteria,
+24 -2
View File
@@ -19,11 +19,33 @@ export function shouldNormalizeInputDigits(options: {
inputmode?: string inputmode?: string
}): boolean { }): boolean {
if (options.isPassword) { if (options.isPassword) {
return false return true
} }
if (options.type === 'price' || options.type === 'number') { const type = String(options.type ?? '').trim().toLowerCase()
if (type === 'price' || type === 'number' || type === 'tel') {
return true return true
} }
const mode = String(options.inputmode ?? '').trim().toLowerCase() const mode = String(options.inputmode ?? '').trim().toLowerCase()
return mode === 'numeric' || mode === 'decimal' || mode === 'number' return mode === 'numeric' || mode === 'decimal' || mode === 'number'
} }
/** تبدیل ارقام فارسی/عربی در فیلدهای عددی فرم‌های احراز هویت و پروفایل */
export function normalizeAuthFormFields(fields: {
mobile?: string | null
password?: string | null
token?: string | null
n_code?: string | number | null
}) {
if (fields.mobile != null && fields.mobile !== '') {
fields.mobile = persianToEnglishDigits(String(fields.mobile).trim())
}
if (fields.password != null && fields.password !== '') {
fields.password = persianToEnglishDigits(String(fields.password))
}
if (fields.token != null && fields.token !== '') {
fields.token = persianToEnglishDigits(String(fields.token).trim())
}
if (fields.n_code != null && fields.n_code !== '') {
fields.n_code = persianToEnglishDigits(String(fields.n_code).trim())
}
}
@@ -19,7 +19,8 @@
<a-form-item :rules="[requireNationalCode()]" label="شماره ملی" name="n_code"> <a-form-item :rules="[requireNationalCode()]" label="شماره ملی" name="n_code">
<BasicInput <BasicInput
v-model:value="userFormState.n_code" v-model:value="userFormState.n_code"
type="number" type="text"
inputmode="numeric"
:disabled="userFormState.is_legal == 1 || userFormState.is_legal == 2" :disabled="userFormState.is_legal == 1 || userFormState.is_legal == 2"
/> />
</a-form-item> </a-form-item>
@@ -16,6 +16,7 @@ import { getUserAvatarUrl } from '@/utils/file'
import { useConfigStore } from '@/store/configStore' import { useConfigStore } from '@/store/configStore'
import { getConfigApp, isBoursecuProject, getBoursecuActionIconSrc } from '@/utils/config' import { getConfigApp, isBoursecuProject, getBoursecuActionIconSrc } from '@/utils/config'
import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare' import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare'
import { normalizeAuthFormFields } from '@/utils/persianDigits'
export interface UserMenuAction { export interface UserMenuAction {
id: string id: string
@@ -207,6 +208,7 @@ export function useUserPage() {
formRef.value?.validateFields().then(async () => { formRef.value?.validateFields().then(async () => {
try { try {
loading.value = true loading.value = true
normalizeAuthFormFields(userFormState.value)
await updateMe(userFormState.value) await updateMe(userFormState.value)
await userStore.getMyInfo() await userStore.getMyInfo()
localAvatarUrl.value = getUserAvatarUrl(userStore.userInfo) localAvatarUrl.value = getUserAvatarUrl(userStore.userInfo)
@@ -224,6 +226,7 @@ export function useUserPage() {
formPassRef.value?.validateFields().then(async () => { formPassRef.value?.validateFields().then(async () => {
try { try {
loading.value = true loading.value = true
normalizeAuthFormFields(userPassForm.value)
await updatePass(userPassForm.value) await updatePass(userPassForm.value)
await userStore.getMyInfo() await userStore.getMyInfo()
editPassVisible.value = false editPassVisible.value = false
+11 -1
View File
@@ -103,7 +103,12 @@
<a-form-item v-if="!isSMS" :rules="[required('کلمه عبور')]" class="home_appear_fade_in_down" label="کلمه عبور" <a-form-item v-if="!isSMS" :rules="[required('کلمه عبور')]" class="home_appear_fade_in_down" label="کلمه عبور"
name="password"> name="password">
<!-- <BasicInput v-model:value="userFormState.password" size="large" place-holder="کلمه عبور"/>--> <!-- <BasicInput v-model:value="userFormState.password" size="large" place-holder="کلمه عبور"/>-->
<AInputPassword v-model:value="userFormState.password" place-holder="کلمه عبور" size="large"/> <BasicInput
v-model:value="userFormState.password"
is-password
place-holder="کلمه عبور"
size="large"
/>
</a-form-item> </a-form-item>
<div v-else-if="!stepTwoVerify" :class="variant === 'desktop' ? 'h-0' : 'h-[90px]'" /> <div v-else-if="!stepTwoVerify" :class="variant === 'desktop' ? 'h-0' : 'h-[90px]'" />
<div v-else> <div v-else>
@@ -387,6 +392,7 @@ import {
isTWA isTWA
} from "@/utils/config"; } from "@/utils/config";
import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue"; import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue";
import { normalizeAuthFormFields } from '@/utils/persianDigits'
import {getConfig} from "@/api/config"; import {getConfig} from "@/api/config";
import shareAppleIcon from '@/assets/img/share-apple.png' import shareAppleIcon from '@/assets/img/share-apple.png'
import addToHomeScreenIcon from '@/assets/img/Add-to-homeScreen.png' import addToHomeScreenIcon from '@/assets/img/Add-to-homeScreen.png'
@@ -433,6 +439,7 @@ async function reSendCode() {
userFormState.token = undefined userFormState.token = undefined
// refOtpInput.value?.clearInput() // refOtpInput.value?.clearInput()
enableSendAgainCode.value = false enableSendAgainCode.value = false
normalizeAuthFormFields(userFormState)
await loginCustomer(userFormState) await loginCustomer(userFormState)
reset() reset()
} }
@@ -472,11 +479,13 @@ function useStyle() {
function handleOnChange(value: string) { function handleOnChange(value: string) {
userFormState.token = value userFormState.token = value
normalizeAuthFormFields(userFormState)
// formRef.value?.validateFields() // formRef.value?.validateFields()
} }
async function handleOnComplete(value: string) { async function handleOnComplete(value: string) {
userFormState.token = value userFormState.token = value
normalizeAuthFormFields(userFormState)
phoneVerification() phoneVerification()
} }
@@ -501,6 +510,7 @@ function phoneVerification() {
formRef.value?.validateFields().then(async () => { formRef.value?.validateFields().then(async () => {
try { try {
loading.value = true loading.value = true
normalizeAuthFormFields(userFormState)
if (isSMS.value) { if (isSMS.value) {
if (!stepTwoVerify.value) { if (!stepTwoVerify.value) {
userFormState.token = undefined userFormState.token = undefined
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="font-bold text-center text-xl">ورود / ثبت نام</div> <div class="font-bold text-center text-xl">ورود / ثبت نام</div>
<a-form :model="userFormState" name="basic" @finish="phoneVerification" class="!mt-5"> <a-form :model="userFormState" name="basic" @finish="phoneVerification" class="!mt-5">
<a-form-item label="شماره تلفن همراه" name="mobile" :rules="[required,mobileValidation('شماره تلفن همراه')]"> <a-form-item label="شماره تلفن همراه" name="mobile" :rules="[required,mobileValidation('شماره تلفن همراه')]">
<BasicInput v-model:value="userFormState.mobile" type="tel" size="large" place-holder="شماره تلفن همراه"/> <BasicInput v-model:value="userFormState.mobile" type="tel" inputmode="numeric" size="large" place-holder="شماره تلفن همراه"/>
<div class="text-xs mt-1 text-gray-400">مالکیت شماره باید به نام خودتان باشد</div> <div class="text-xs mt-1 text-gray-400">مالکیت شماره باید به نام خودتان باشد</div>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
+5 -1
View File
@@ -64,6 +64,7 @@
v-model:value="userFormState.mobile" v-model:value="userFormState.mobile"
:place-holder="variant === 'desktop' ? '۰۹۱۲۳۴۵۶۷۸۹' : 'شماره تلفن'" :place-holder="variant === 'desktop' ? '۰۹۱۲۳۴۵۶۷۸۹' : 'شماره تلفن'"
:size="variant === 'desktop' ? 'large' : undefined" :size="variant === 'desktop' ? 'large' : undefined"
type="tel"
inputmode="numeric" inputmode="numeric"
/> />
</a-form-item> </a-form-item>
@@ -73,8 +74,9 @@
label="کلمه عبور" label="کلمه عبور"
name="password" name="password"
> >
<AInputPassword <BasicInput
v-model:value="userFormState.password" v-model:value="userFormState.password"
is-password
place-holder="کلمه عبور" place-holder="کلمه عبور"
:size="variant === 'desktop' ? 'large' : undefined" :size="variant === 'desktop' ? 'large' : undefined"
/> />
@@ -236,6 +238,7 @@ import router from '@/router'
import {useConfigStore} from "@/store/configStore"; import {useConfigStore} from "@/store/configStore";
import {useSettingsStore} from '@/store/settingsStore' import {useSettingsStore} from '@/store/settingsStore'
import {getProjectLoginLogo, getProjectLoginTaglineLines, hasCustomLoginLogoLayout} from "@/utils/config"; import {getProjectLoginLogo, getProjectLoginTaglineLines, hasCustomLoginLogoLayout} from "@/utils/config";
import { normalizeAuthFormFields } from '@/utils/persianDigits'
withDefaults( withDefaults(
defineProps<{ defineProps<{
@@ -288,6 +291,7 @@ function registerAccount() {
URLUserType() === 'single' ? userFormState.type = 2 : userFormState.type = 1 URLUserType() === 'single' ? userFormState.type = 2 : userFormState.type = 1
// console.log(userFormState) // console.log(userFormState)
if (!userFormState.referral_id || userFormState.referral_id === '') delete userFormState.referral_id if (!userFormState.referral_id || userFormState.referral_id === '') delete userFormState.referral_id
normalizeAuthFormFields(userFormState)
const { id } = await registerCustomer(userFormState) const { id } = await registerCustomer(userFormState)
registeredInfo.value = true registeredInfo.value = true
// console.log(registeredInfo.value) // console.log(registeredInfo.value)
@@ -17,6 +17,7 @@ import { useWalletStore } from '@/store/walletStore'
import { useSettingsStore } from '@/store/settingsStore' import { useSettingsStore } from '@/store/settingsStore'
import { setTruth } from '@/app/authBoot' import { setTruth } from '@/app/authBoot'
import { completeLoginNavigation } from '@/layouts/composables/useMobileScrollReset' import { completeLoginNavigation } from '@/layouts/composables/useMobileScrollReset'
import { normalizeAuthFormFields } from '@/utils/persianDigits'
export function useLoginForm() { export function useLoginForm() {
const { time, onFinish, reset } = useCountdownTimer('00:02:00', 'mm:ss') const { time, onFinish, reset } = useCountdownTimer('00:02:00', 'mm:ss')
@@ -41,6 +42,7 @@ export function useLoginForm() {
async function reSendCode() { async function reSendCode() {
userFormState.token = undefined userFormState.token = undefined
enableSendAgainCode.value = false enableSendAgainCode.value = false
normalizeAuthFormFields(userFormState)
await loginCustomer(userFormState) await loginCustomer(userFormState)
reset() reset()
} }
@@ -64,10 +66,12 @@ export function useLoginForm() {
function handleOnChange(value: string) { function handleOnChange(value: string) {
userFormState.token = value userFormState.token = value
normalizeAuthFormFields(userFormState)
} }
async function handleOnComplete(value: string) { async function handleOnComplete(value: string) {
userFormState.token = value userFormState.token = value
normalizeAuthFormFields(userFormState)
phoneVerification() phoneVerification()
} }
@@ -103,6 +107,7 @@ export function useLoginForm() {
formRef.value?.validateFields().then(async () => { formRef.value?.validateFields().then(async () => {
try { try {
loading.value = true loading.value = true
normalizeAuthFormFields(userFormState)
if (isSMS.value) { if (isSMS.value) {
if (!stepTwoVerify.value) { if (!stepTwoVerify.value) {
userFormState.token = undefined userFormState.token = undefined
@@ -5,6 +5,7 @@ import type { User } from '@/models/login'
import { loading, URLUserType } from '@/utils' import { loading, URLUserType } from '@/utils'
import { registerCustomer } from '@/api/login' import { registerCustomer } from '@/api/login'
import router from '@/router' import router from '@/router'
import { normalizeAuthFormFields } from '@/utils/persianDigits'
export function useRegisterForm() { export function useRegisterForm() {
const registeredInfo = ref(false) const registeredInfo = ref(false)
@@ -31,6 +32,7 @@ export function useRegisterForm() {
if (!userFormState.referral_id || userFormState.referral_id === '') { if (!userFormState.referral_id || userFormState.referral_id === '') {
delete userFormState.referral_id delete userFormState.referral_id
} }
normalizeAuthFormFields(userFormState)
await registerCustomer(userFormState) await registerCustomer(userFormState)
registeredInfo.value = true registeredInfo.value = true
} catch (e: any) { } catch (e: any) {
@@ -107,8 +107,9 @@ function goToAboutUs() {
label="کلمه عبور" label="کلمه عبور"
name="password" name="password"
> >
<AInputPassword <BasicInput
v-model:value="userFormState.password" v-model:value="userFormState.password"
is-password
place-holder="کلمه عبور" place-holder="کلمه عبور"
size="large" size="large"
/> />
@@ -82,13 +82,15 @@ function handleSuccessDismiss() {
v-model:value="userFormState.mobile" v-model:value="userFormState.mobile"
place-holder="۰۹۱۲۳۴۵۶۷۸۹" place-holder="۰۹۱۲۳۴۵۶۷۸۹"
size="large" size="large"
type="tel"
inputmode="numeric" inputmode="numeric"
/> />
</a-form-item> </a-form-item>
<a-form-item :rules="[required()]" label="کلمه عبور" name="password"> <a-form-item :rules="[required()]" label="کلمه عبور" name="password">
<AInputPassword <BasicInput
v-model:value="userFormState.password" v-model:value="userFormState.password"
is-password
place-holder="کلمه عبور" place-holder="کلمه عبور"
size="large" size="large"
/> />