fix persian number in input
This commit is contained in:
Vendored
+2
@@ -94,6 +94,8 @@ declare module '@vue/runtime-core' {
|
|||||||
SmsCreditWidget: typeof import('./src/components/SmsCreditWidget.vue')['default']
|
SmsCreditWidget: typeof import('./src/components/SmsCreditWidget.vue')['default']
|
||||||
SmsNegativeBalanceHost: typeof import('./src/components/SmsNegativeBalanceHost.vue')['default']
|
SmsNegativeBalanceHost: typeof import('./src/components/SmsNegativeBalanceHost.vue')['default']
|
||||||
SmsNegativeBalanceModal: typeof import('./src/components/SmsNegativeBalanceModal.vue')['default']
|
SmsNegativeBalanceModal: typeof import('./src/components/SmsNegativeBalanceModal.vue')['default']
|
||||||
|
SubscriptionExpiryHost: typeof import('./src/components/SubscriptionExpiryHost.vue')['default']
|
||||||
|
SubscriptionExpiryModal: typeof import('./src/components/SubscriptionExpiryModal.vue')['default']
|
||||||
TahesabRetryModal: typeof import('./src/components/TahesabRetryModal.vue')['default']
|
TahesabRetryModal: typeof import('./src/components/TahesabRetryModal.vue')['default']
|
||||||
ThemeMenuSection: typeof import('./src/core/components/ThemeMenuSection.vue')['default']
|
ThemeMenuSection: typeof import('./src/core/components/ThemeMenuSection.vue')['default']
|
||||||
ThemeToggle: typeof import('./src/core/components/ThemeToggle.vue')['default']
|
ThemeToggle: typeof import('./src/core/components/ThemeToggle.vue')['default']
|
||||||
|
|||||||
@@ -36,8 +36,8 @@
|
|||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { sanitizeNumericString, parseNumericValue } from '@core/utils/convert'
|
||||||
|
|
||||||
//Props
|
|
||||||
interface Prop {
|
interface Prop {
|
||||||
title?: String
|
title?: String
|
||||||
value?: String | Number
|
value?: String | Number
|
||||||
@@ -53,45 +53,66 @@ interface Prop {
|
|||||||
step?: String | Number
|
step?: String | Number
|
||||||
}
|
}
|
||||||
|
|
||||||
const addonBgColor = computed(() => props.addonBgColor)
|
|
||||||
const addonTextColor = computed(() => props.addonTextColor)
|
|
||||||
//emits
|
|
||||||
const localValue = computed({
|
|
||||||
set(value) {
|
|
||||||
// console.log(value?.toString().replaceAll(',', ''))
|
|
||||||
props.type === 'price'
|
|
||||||
? emits('update:value', value?.toString().replaceAll(',', ''))
|
|
||||||
: emits('update:value', value)
|
|
||||||
},
|
|
||||||
get() {
|
|
||||||
let a =
|
|
||||||
props.type === 'price'
|
|
||||||
? props.value != null && props.value !== ''
|
|
||||||
? usePrice(props.value as number, true, '').trim()
|
|
||||||
: ''
|
|
||||||
: props.value
|
|
||||||
// console.log(props.value)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Prop>(), {
|
const props = withDefaults(defineProps<Prop>(), {
|
||||||
isPassword: false
|
isPassword: false,
|
||||||
})
|
})
|
||||||
const emits = defineEmits(['update:value', 'onchange'])
|
const emits = defineEmits(['update:value', 'onchange'])
|
||||||
|
|
||||||
|
const addonBgColor = computed(() => props.addonBgColor)
|
||||||
|
const addonTextColor = computed(() => props.addonTextColor)
|
||||||
|
|
||||||
|
const isNumericInput = computed(() => {
|
||||||
|
const t = String(props.type ?? '')
|
||||||
|
return t === 'price' || t === 'number' || t === 'tel'
|
||||||
|
})
|
||||||
|
|
||||||
|
function cleanNumeric(raw: unknown): string {
|
||||||
|
// قیمت و tel معمولاً بدون اعشار لازم؛ number ممکن است اعشار داشته باشد
|
||||||
|
const allowDecimal = String(props.type) === 'number'
|
||||||
|
return sanitizeNumericString(raw, { allowDecimal })
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPriceDisplay(raw: unknown): string {
|
||||||
|
const cleaned = sanitizeNumericString(raw, { allowDecimal: false })
|
||||||
|
if (!cleaned) return ''
|
||||||
|
const num = parseNumericValue(cleaned)
|
||||||
|
if (!Number.isFinite(num)) return cleaned
|
||||||
|
return usePrice(num, true, '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const localValue = computed({
|
||||||
|
set(value) {
|
||||||
|
if (isNumericInput.value) {
|
||||||
|
emits('update:value', cleanNumeric(value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emits('update:value', value)
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
if (props.type === 'price') {
|
||||||
|
if (props.value == null || props.value === '') return ''
|
||||||
|
return formatPriceDisplay(props.value)
|
||||||
|
}
|
||||||
|
if (isNumericInput.value) {
|
||||||
|
if (props.value == null || props.value === '') return ''
|
||||||
|
return cleanNumeric(props.value)
|
||||||
|
}
|
||||||
|
return props.value
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const handlerChange = (e: any) => {
|
const handlerChange = (e: any) => {
|
||||||
// emits('onchange', e.target.value)
|
const raw = e?.target?.value
|
||||||
props.type === 'price'
|
if (isNumericInput.value) {
|
||||||
? emits('update:value', e.target.value?.toString().replaceAll(',', ''))
|
const cleaned = cleanNumeric(raw)
|
||||||
: emits('update:value', e.target.value)
|
emits('update:value', cleaned)
|
||||||
props.type === 'price'
|
emits('onchange', cleaned)
|
||||||
? emits('onchange', e.target.value?.toString().replaceAll(',', ''))
|
return
|
||||||
: emits('onchange', e.target.value)
|
}
|
||||||
// console.log(e.target.value)
|
emits('update:value', raw)
|
||||||
|
emits('onchange', raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
//css style
|
|
||||||
const { prefixCls } = usePrefix('input')
|
const { prefixCls } = usePrefix('input')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -99,10 +120,6 @@ const { prefixCls } = usePrefix('input')
|
|||||||
@prefix: ~'@{prefixCls}-input';
|
@prefix: ~'@{prefixCls}-input';
|
||||||
|
|
||||||
.@{prefix} {
|
.@{prefix} {
|
||||||
/* .ant-input-group-addon:first-child {
|
|
||||||
!* border: #d9d9d9 1px solid;
|
|
||||||
border-radius: 0 6px 6px 0;*!
|
|
||||||
}*/
|
|
||||||
.ant-input-group {
|
.ant-input-group {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
@@ -113,13 +130,6 @@ const { prefixCls } = usePrefix('input')
|
|||||||
border-bottom-left-radius: @border-radius-base;
|
border-bottom-left-radius: @border-radius-base;
|
||||||
border-top-left-radius: @border-radius-base;
|
border-top-left-radius: @border-radius-base;
|
||||||
}
|
}
|
||||||
/*.ant-input-group > .ant-input-rtl:first-child,
|
|
||||||
.ant-input-group-rtl .ant-input-group-addon:first-child {
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
border-bottom-left-radius: @border-radius-base;
|
|
||||||
border-top-left-radius: @border-radius-base;
|
|
||||||
}*/
|
|
||||||
.ant-input-group > .ant-input:last-child,
|
.ant-input-group > .ant-input:last-child,
|
||||||
.ant-input-group-addon:last-child {
|
.ant-input-group-addon:last-child {
|
||||||
border-radius: 0 @border-radius-base @border-radius-base 0;
|
border-radius: 0 @border-radius-base @border-radius-base 0;
|
||||||
|
|||||||
+61
-19
@@ -1,22 +1,64 @@
|
|||||||
//const e2p = (s: string) => s.replace(/\d/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[+d])
|
/**
|
||||||
//const p2a = (s : string) => s.replace(/[۰-۹]/g, (d) => '٠١٢٣٤٥٦٧٨٩'['۰۱۲۳۴۵۶۷۸۹'.indexOf(d)])
|
* تبدیل ارقام فارسی/عربی به لاتین (انگلیسی) و پاکسازی ورودیهای عددی/قیمتی.
|
||||||
//const a2p = (s : string) => s.replace(/[٠-٩]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'['٠١٢٣٤٥٦٧٨٩'.indexOf(d)])
|
*/
|
||||||
//const e2a = (s : string) => s.replace(/\d/g, (d) => '٠١٢٣٤٥٦٧٨٩'[+d])
|
|
||||||
//_sample
|
|
||||||
//e2p('asdf1234') // asdf۱۲۳۴
|
|
||||||
//e2a('asdf1234') // asdf١٢٣٤
|
|
||||||
//p2e('asdf۱۲۳۴') // asdf1234
|
|
||||||
//a2e('asdf١٢٣٤') // asdf1234
|
|
||||||
//p2a('asdf۱۲۳۴') // asdf١٢٣٤
|
|
||||||
//a2p('asdf١٢٣٤') // asdf۱۲۳۴
|
|
||||||
//٠٩١٣٥٥١۷٤٢٥
|
|
||||||
//۰۹۱۳۵۵۱۷۴۲۵
|
|
||||||
|
|
||||||
const a2e = (s: string) => s.replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d).toString())
|
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹'
|
||||||
const p2e = (s: string) => s.replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d).toString())
|
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩'
|
||||||
|
|
||||||
function persian2english(s: string) {
|
export function persian2english(s: string): string {
|
||||||
const temp = p2e(s)
|
return String(s ?? '')
|
||||||
return a2e(temp)
|
.replace(/[۰-۹]/g, (d) => String(PERSIAN_DIGITS.indexOf(d)))
|
||||||
|
.replace(/[٠-٩]/g, (d) => String(ARABIC_DIGITS.indexOf(d)))
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SanitizeNumericOptions = {
|
||||||
|
/** اجازهٔ نقطه اعشار (پیشفرض true) */
|
||||||
|
allowDecimal?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ارقام فارسی/عربی → لاتین، حذف جداکننده هزار، یکسانسازی ممیز.
|
||||||
|
* خروجی فقط شامل ارقام لاتین و (اختیاری) یک نقطه است.
|
||||||
|
*/
|
||||||
|
export function sanitizeNumericString(
|
||||||
|
raw: unknown,
|
||||||
|
options: SanitizeNumericOptions = {},
|
||||||
|
): string {
|
||||||
|
const allowDecimal = options.allowDecimal !== false
|
||||||
|
let s = persian2english(String(raw ?? '').trim())
|
||||||
|
.replace(/[٬,\s]/g, '')
|
||||||
|
.replace(/٫/g, '.')
|
||||||
|
.replace(/\//g, '.')
|
||||||
|
|
||||||
|
if (!allowDecimal) {
|
||||||
|
return s.replace(/[^\d-]/g, '').replace(/(?!^)-/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
let out = ''
|
||||||
|
let hasDot = false
|
||||||
|
let hasSign = false
|
||||||
|
for (const ch of s) {
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
out += ch
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (ch === '.' && !hasDot) {
|
||||||
|
hasDot = true
|
||||||
|
out += ch
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (ch === '-' && !hasSign && out.length === 0) {
|
||||||
|
hasSign = true
|
||||||
|
out += ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
/** پارس امن عدد از رشتهٔ فارسی/لاتین/با کاما */
|
||||||
|
export function parseNumericValue(raw: unknown): number {
|
||||||
|
const cleaned = sanitizeNumericString(raw)
|
||||||
|
if (!cleaned || cleaned === '-' || cleaned === '.') return NaN
|
||||||
|
const n = Number(cleaned)
|
||||||
|
return Number.isFinite(n) ? n : NaN
|
||||||
}
|
}
|
||||||
export { persian2english }
|
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
export function usePrice(price: number, digit = true, unit = 'ریال') {
|
import { persian2english, parseNumericValue, sanitizeNumericString } from '@core/utils/convert'
|
||||||
let _price = price.toString()
|
|
||||||
if (digit) {
|
export function usePrice(price: number | string, digit = true, unit = 'ریال') {
|
||||||
_price = _price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
const n = typeof price === 'number' ? price : parseNumericValue(price)
|
||||||
|
let _price = Number.isFinite(n) ? String(n) : persian2english(String(price ?? ''))
|
||||||
|
if (digit && Number.isFinite(n)) {
|
||||||
|
_price = _price.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||||
}
|
}
|
||||||
return `${_price} ${unit}`
|
return `${_price} ${unit}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTowDecimal(num: string | number): string | number {
|
export function useTowDecimal(num: string | number): string | number {
|
||||||
const temp = num.toString().match(/^-?\d+(?:\.\d{0,2})?/)
|
const cleaned = sanitizeNumericString(num, { allowDecimal: true })
|
||||||
|
const temp = cleaned.match(/^-?\d+(?:\.\d{0,2})?/)
|
||||||
if (temp && temp.length > 0) return temp[0]
|
if (temp && temp.length > 0) return temp[0]
|
||||||
return Math.round(+num * 100) / 100
|
const n = parseNumericValue(cleaned)
|
||||||
|
return Number.isFinite(n) ? Math.round(n * 100) / 100 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFormat(num: string | number, unit = '') {
|
export function useFormat(num: string | number, unit = '') {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
* منبع: ستون amount + JSON فیلد data (بدون ستون DB جدا).
|
* منبع: ستون amount + JSON فیلد data (بدون ستون DB جدا).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { parseNumericValue } from '@core/utils/convert'
|
||||||
|
|
||||||
export type AccountingSyncDocRecord = {
|
export type AccountingSyncDocRecord = {
|
||||||
amount?: number | string | null
|
amount?: number | string | null
|
||||||
type?: string | null
|
type?: string | null
|
||||||
@@ -12,8 +14,7 @@ export type AccountingSyncDocRecord = {
|
|||||||
|
|
||||||
function parseNum(value: unknown): number {
|
function parseNum(value: unknown): number {
|
||||||
if (value == null || value === '') return NaN
|
if (value == null || value === '') return NaN
|
||||||
const n = Number(String(value).replace(/,/g, '').replace('/', '.'))
|
return parseNumericValue(value)
|
||||||
return Number.isFinite(n) ? n : NaN
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseDocPayload(data?: string | null): Record<string, unknown> | null {
|
export function parseDocPayload(data?: string | null): Record<string, unknown> | null {
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:formatter="(v: string) => `${v}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
|
:formatter="(v: string) => `${v}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
|
||||||
:parser="(v: string) => v.replace(/,/g, '')"
|
:parser="(v: string) => sanitizeNumericString(v, { allowDecimal: false })"
|
||||||
@update:value="onPriceChange"
|
@update:value="onPriceChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -144,6 +144,7 @@ import {
|
|||||||
switchValueFromChecked,
|
switchValueFromChecked,
|
||||||
type SettingFieldType
|
type SettingFieldType
|
||||||
} from './settingTypes'
|
} from './settingTypes'
|
||||||
|
import { parseNumericValue, sanitizeNumericString } from '@core/utils/convert'
|
||||||
import dayjs, { type Dayjs } from 'dayjs'
|
import dayjs, { type Dayjs } from 'dayjs'
|
||||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ function onTimeChange(val: Dayjs | string | null | undefined) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const priceValue = computed(() => {
|
const priceValue = computed(() => {
|
||||||
const n = parseFloat(String(props.modelValue || '').replace(/,/g, ''))
|
const n = parseNumericValue(props.modelValue || '')
|
||||||
return Number.isFinite(n) ? n : undefined
|
return Number.isFinite(n) ? n : undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -243,25 +244,11 @@ function onPriceChange(val: number | string | null) {
|
|||||||
emit('update:value', '')
|
emit('update:value', '')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
emit('update:value', String(val))
|
emit('update:value', sanitizeNumericString(val, { allowDecimal: false }))
|
||||||
}
|
|
||||||
|
|
||||||
function normalizePlainNumberInput(value: string): string {
|
|
||||||
const latin = value.replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d)))
|
|
||||||
let out = ''
|
|
||||||
let hasDot = false
|
|
||||||
for (const ch of latin.replace(/,/g, '')) {
|
|
||||||
if (ch >= '0' && ch <= '9') out += ch
|
|
||||||
else if (ch === '.' && !hasDot) {
|
|
||||||
hasDot = true
|
|
||||||
out += ch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPlainNumberChange(val: string) {
|
function onPlainNumberChange(val: string) {
|
||||||
emit('update:value', normalizePlainNumberInput(val ?? ''))
|
emit('update:value', sanitizeNumericString(val ?? '', { allowDecimal: true }))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Const } from '@/model/configuration'
|
import type { Const } from '@/model/configuration'
|
||||||
import { conditionalOrderEnabledSetting, isStoreApp, priceChannelAccessSetting, priceProviderSetting, showToUsersChatSetting } from '@/utils/config'
|
import { conditionalOrderEnabledSetting, isStoreApp, priceChannelAccessSetting, priceProviderSetting, showToUsersChatSetting } from '@/utils/config'
|
||||||
|
import { sanitizeNumericString } from '@core/utils/convert'
|
||||||
|
|
||||||
export type SettingFieldType =
|
export type SettingFieldType =
|
||||||
| 'text'
|
| 'text'
|
||||||
@@ -517,10 +518,10 @@ export function formatValueForSave(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (type === 'price') {
|
if (type === 'price') {
|
||||||
return value === '' ? '0' : String(value).replace(/,/g, '')
|
return value === '' ? '0' : sanitizeNumericString(value, { allowDecimal: false })
|
||||||
}
|
}
|
||||||
if (type === 'number') {
|
if (type === 'number') {
|
||||||
return String(value ?? '').replace(/,/g, '')
|
return sanitizeNumericString(value ?? '', { allowDecimal: true })
|
||||||
}
|
}
|
||||||
if (type === 'time') {
|
if (type === 'time') {
|
||||||
return normalizeTimeValue(value)
|
return normalizeTimeValue(value)
|
||||||
|
|||||||
@@ -41,8 +41,8 @@
|
|||||||
:place-holder="amountPlaceholder"
|
:place-holder="amountPlaceholder"
|
||||||
@onchange="onAmountChange"
|
@onchange="onAmountChange"
|
||||||
/>
|
/>
|
||||||
<div v-if="isMoney && Number(normalizedAmount) > 10" class="wallet-charge-form__hint">
|
<div v-if="isMoney && normalizedAmount > 10" class="wallet-charge-form__hint">
|
||||||
معادل {{ usePrice(Number(normalizedAmount) / 10, true, 'تومان') }}
|
معادل {{ usePrice(normalizedAmount / 10, true, 'تومان') }}
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
@@ -58,6 +58,10 @@ import type { FormInstance } from 'ant-design-vue'
|
|||||||
import type { Charge } from '@/model/wallet'
|
import type { Charge } from '@/model/wallet'
|
||||||
import { inputRules } from '@/utils/inputRules'
|
import { inputRules } from '@/utils/inputRules'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import {
|
||||||
|
normalizeAmountRaw,
|
||||||
|
parseAmount,
|
||||||
|
} from '@/pages/menuPages/customers/base/utils/walletChargeForm'
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@@ -124,35 +128,16 @@ const notePlaceholder = computed(() =>
|
|||||||
isDischarge.value ? 'دلیل کسر موجودی' : 'دلیل افزایش موجودی',
|
isDischarge.value ? 'دلیل کسر موجودی' : 'دلیل افزایش موجودی',
|
||||||
)
|
)
|
||||||
|
|
||||||
/** ارقام فارسی/عربی → لاتین؛ جداکننده هزار حذف؛ ممیز یکسان */
|
|
||||||
function normalizeAmountRaw(raw: string | number | undefined | null): string {
|
|
||||||
return String(raw ?? '')
|
|
||||||
.trim()
|
|
||||||
.replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d)))
|
|
||||||
.replace(/[٠-٩]/g, (d) => String('٠١٢٣٤٥٦٧٨٩'.indexOf(d)))
|
|
||||||
.replace(/[٬,\s]/g, '')
|
|
||||||
.replace(/٫/g, '.')
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasDecimalSeparator(raw: string): boolean {
|
function hasDecimalSeparator(raw: string): boolean {
|
||||||
// بعد از حذف جداکننده هزار، نقطه = اعشار
|
|
||||||
return raw.includes('.')
|
return raw.includes('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseAmount(raw: string | number | undefined | null): number {
|
|
||||||
const normalized = normalizeAmountRaw(raw)
|
|
||||||
if (!normalized) return 0
|
|
||||||
const num = Number(normalized)
|
|
||||||
return Number.isFinite(num) ? Math.abs(num) : NaN
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizedAmount = computed(() => parseAmount(props.modelValue.amountAbs))
|
const normalizedAmount = computed(() => parseAmount(props.modelValue.amountAbs))
|
||||||
|
|
||||||
function onAmountChange(raw: string | number) {
|
function onAmountChange(raw: string | number) {
|
||||||
if (!requiresInteger.value) return
|
if (!requiresInteger.value) return
|
||||||
const text = normalizeAmountRaw(raw)
|
const text = normalizeAmountRaw(raw)
|
||||||
if (!hasDecimalSeparator(text)) return
|
if (!hasDecimalSeparator(text)) return
|
||||||
// قطع اعشار — فقط بخش صحیح بماند
|
|
||||||
const whole = text.split('.')[0] ?? ''
|
const whole = text.split('.')[0] ?? ''
|
||||||
props.modelValue.amountAbs = whole === '' ? undefined : whole
|
props.modelValue.amountAbs = whole === '' ? undefined : whole
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
import type { Charge, WalletChargeDirection } from '@/model/wallet'
|
import type { Charge, WalletChargeDirection } from '@/model/wallet'
|
||||||
|
import { parseNumericValue, sanitizeNumericString } from '@core/utils/convert'
|
||||||
|
|
||||||
|
/** ارقام فارسی/عربی → لاتین؛ جداکننده هزار حذف؛ ممیز یکسان */
|
||||||
|
export function normalizeAmountRaw(raw: string | number | undefined | null): string {
|
||||||
|
return sanitizeNumericString(raw, { allowDecimal: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseAmount(raw: string | number | undefined | null): number {
|
||||||
|
const n = parseNumericValue(raw)
|
||||||
|
return Number.isFinite(n) ? Math.abs(n) : 0
|
||||||
|
}
|
||||||
|
|
||||||
export function createEmptyChargeForm(customerId?: number): Charge {
|
export function createEmptyChargeForm(customerId?: number): Charge {
|
||||||
return {
|
return {
|
||||||
@@ -13,7 +24,7 @@ export function createEmptyChargeForm(customerId?: number): Charge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function buildChargePayload(form: Charge, productId?: number | null): Charge {
|
export function buildChargePayload(form: Charge, productId?: number | null): Charge {
|
||||||
const amount = Math.abs(Number(form.amountAbs) || 0)
|
const amount = parseAmount(form.amountAbs)
|
||||||
const signed = form.direction === 'discharge' ? -amount : amount
|
const signed = form.direction === 'discharge' ? -amount : amount
|
||||||
const payload: Charge = {
|
const payload: Charge = {
|
||||||
value: signed,
|
value: signed,
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹'
|
import { parseNumericValue, sanitizeNumericString } from '@core/utils/convert'
|
||||||
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩'
|
|
||||||
|
|
||||||
function toAsciiDigits(value: string): string {
|
|
||||||
return value
|
|
||||||
.replace(/[۰-۹]/g, (d) => String(PERSIAN_DIGITS.indexOf(d)))
|
|
||||||
.replace(/[٠-٩]/g, (d) => String(ARABIC_DIGITS.indexOf(d)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** پارس مبلغ محدودیت سفارش — ارقام فارسی/عربی و جداکننده هزارگان */
|
/** پارس مبلغ محدودیت سفارش — ارقام فارسی/عربی و جداکننده هزارگان */
|
||||||
export function parseOrderLimitCashValue(value: unknown): number | undefined {
|
export function parseOrderLimitCashValue(value: unknown): number | undefined {
|
||||||
@@ -13,12 +6,12 @@ export function parseOrderLimitCashValue(value: unknown): number | undefined {
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const raw = toAsciiDigits(String(value).trim().replace(/[,\s٬]/g, ''))
|
const raw = sanitizeNumericString(value, { allowDecimal: false })
|
||||||
if (raw === '') {
|
if (raw === '') {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const num = Number(raw)
|
const num = parseNumericValue(raw)
|
||||||
if (!Number.isFinite(num) || num < 0) {
|
if (!Number.isFinite(num) || num < 0) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user