Display and accept SuperAdmin money amounts in tomans.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-08 13:27:31 +03:30
co-authored by Cursor
parent 68de9a2337
commit 8e696bb591
7 changed files with 55 additions and 27 deletions
+13 -3
View File
@@ -1,7 +1,16 @@
<script setup lang="ts">
import { computed } from 'vue'
import { fromGroupedInput, toGroupedDisplay } from '@/utils/money'
import {
fromGroupedInput,
rialToToman,
toGroupedDisplay,
tomanToRial,
} from '@/utils/money'
/**
* Money field bound to **rial** storage values (API/DB).
* User types and sees **toman** (÷10 / ×10 at the edges).
*/
const props = withDefaults(
defineProps<{
value: number | null | undefined
@@ -22,10 +31,11 @@ const emit = defineEmits<{
(e: 'update:value', value: number): void
}>()
const display = computed(() => toGroupedDisplay(props.value ?? 0))
const display = computed(() => toGroupedDisplay(rialToToman(props.value ?? 0)))
function onInput(raw: string | number) {
emit('update:value', fromGroupedInput(String(raw ?? ''), props.allowNegative))
const toman = fromGroupedInput(String(raw ?? ''), props.allowNegative)
emit('update:value', tomanToRial(toman))
}
</script>
+6 -6
View File
@@ -351,8 +351,8 @@ onMounted(loadDetail)
</a-form-item>
</a-col>
<a-col :xs="24" :md="8">
<a-form-item label="قیمت دوره (ریال)">
<MoneyInput v-model:value="form.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item label="قیمت دوره (تومان)">
<MoneyInput v-model:value="form.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
</a-col>
</a-row>
@@ -397,8 +397,8 @@ onMounted(loadDetail)
</a-form-item>
</a-col>
<a-col :xs="24" :md="8">
<a-form-item label="مبلغ (ریال)">
<MoneyInput v-model:value="renewForm.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item label="مبلغ (تومان)">
<MoneyInput v-model:value="renewForm.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
</a-col>
</a-row>
@@ -466,8 +466,8 @@ onMounted(loadDetail)
</a-form-item>
</a-col>
</a-row>
<a-form-item label="قیمت دوره (ریال)">
<MoneyInput v-model:value="createForm.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item label="قیمت دوره (تومان)">
<MoneyInput v-model:value="createForm.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
</a-form>
</a-modal>
+4 -4
View File
@@ -377,8 +377,8 @@ onMounted(loadDetail)
</a-form-item>
</a-col>
<a-col :xs="24" :md="8">
<a-form-item label="قیمت دوره (ریال)">
<MoneyInput v-model:value="form.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item label="قیمت دوره (تومان)">
<MoneyInput v-model:value="form.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
</a-col>
</a-row>
@@ -426,8 +426,8 @@ onMounted(loadDetail)
</a-form-item>
</a-col>
<a-col :xs="24" :md="8">
<a-form-item label="مبلغ (ریال)">
<MoneyInput v-model:value="renewForm.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item label="مبلغ (تومان)">
<MoneyInput v-model:value="renewForm.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
</a-col>
</a-row>
+3 -3
View File
@@ -122,7 +122,7 @@ onMounted(loadTenants)
:class="{ active: toneFilter === 'healthy' }"
@click="toggleToneFilter('healthy')"
>
بالای ۱۰۰٬۰۰۰ — سبز
بالای ۱۰٬۰۰۰ — سبز
</button>
<button
type="button"
@@ -130,7 +130,7 @@ onMounted(loadTenants)
:class="{ active: toneFilter === 'warning' }"
@click="toggleToneFilter('warning')"
>
۱۰٬۰۰۰ تا ۱۰۰٬۰۰۰ — زرد
۱٬۰۰۰ تا ۱۰٬۰۰۰ — زرد
</button>
<button
type="button"
@@ -138,7 +138,7 @@ onMounted(loadTenants)
:class="{ active: toneFilter === 'critical' }"
@click="toggleToneFilter('critical')"
>
زیر ۱۰٬۰۰۰ — قرمز
زیر ۱٬۰۰۰ — قرمز
</button>
</div>
+3 -3
View File
@@ -422,11 +422,11 @@ onMounted(async () => {
<span>موجودی کیف پول</span>
<a-tag v-if="form.isBlocked" color="error">ارسال مسدود</a-tag>
</div>
<strong>{{ formatNumber(summary.balanceRials) }} <small>ریال</small></strong>
<strong>{{ formatMoney(summary.balanceRials) }}</strong>
</div>
<div class="stat-card sky">
<span>قیمت هر پارت</span>
<strong>{{ formatNumber(summary.perPartRials) }} <small>ریال</small></strong>
<strong>{{ formatMoney(summary.perPartRials) }}</strong>
</div>
<div class="stat-card emerald">
<span>امروز</span>
@@ -458,7 +458,7 @@ onMounted(async () => {
<MoneyInput
v-model:value="chargeForm.amountRials"
allow-negative
placeholder="مبلغ (ریال)"
placeholder="مبلغ (تومان)"
/>
<a-input v-model:value="chargeForm.note" size="large" placeholder="توضیح (اختیاری)" />
<a-button type="primary" size="large" :loading="charging" @click="openChargeConfirm">
+6 -6
View File
@@ -396,8 +396,8 @@ onMounted(loadDetail)
</a-form-item>
</a-col>
</a-row>
<a-form-item v-if="!form.isFree" label="قیمت دوره (ریال)">
<MoneyInput v-model:value="form.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item v-if="!form.isFree" label="قیمت دوره (تومان)">
<MoneyInput v-model:value="form.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
<p v-else class="field-hint">
دوره رایگان فعال است — مبلغ صفر می‌شود. مدت ۱ / ۳ / ۶ / ۱۲ ماه می‌تواند رایگان باشد.
@@ -448,8 +448,8 @@ onMounted(loadDetail)
</a-form-item>
</a-col>
</a-row>
<a-form-item v-if="!renewForm.isFree" label="مبلغ (ریال)">
<MoneyInput v-model:value="renewForm.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item v-if="!renewForm.isFree" label="مبلغ (تومان)">
<MoneyInput v-model:value="renewForm.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
<p v-else class="field-hint">
دوره رایگان — مبلغ صفر. مدت ۱ / ۳ / ۶ / ۱۲ ماه می‌تواند رایگان باشد.
@@ -523,8 +523,8 @@ onMounted(loadDetail)
<a-form-item label="دوره رایگان">
<a-switch v-model:checked="createForm.isFree" />
</a-form-item>
<a-form-item v-if="!createForm.isFree" label="قیمت دوره (ریال)">
<MoneyInput v-model:value="createForm.priceRials" placeholder="مثلاً 1,500,000" />
<a-form-item v-if="!createForm.isFree" label="قیمت دوره (تومان)">
<MoneyInput v-model:value="createForm.priceRials" placeholder="مثلاً 150,000" />
</a-form-item>
<p v-else class="field-hint">
دوره رایگان — مبلغ صفر. مدت ۱ / ۳ / ۶ / ۱۲ ماه می‌تواند رایگان باشد.
+20 -2
View File
@@ -1,4 +1,21 @@
/** Display/parse helpers for rial money inputs with thousand separators. */
/**
* SuperAdmin money helpers.
*
* Storage / product remote APIs use integer **rials**.
* SuperAdmin UI displays and accepts **tomans** (rial ÷ 10).
*/
/** Convert stored rial amount to toman for display/input. */
export function rialToToman(value: number | null | undefined) {
if (value == null || Number.isNaN(Number(value))) return 0
return Math.trunc(Number(value) / 10)
}
/** Convert UI toman amount to stored rial. */
export function tomanToRial(value: number | null | undefined) {
if (value == null || Number.isNaN(Number(value))) return 0
return Math.trunc(Number(value)) * 10
}
export function toGroupedDisplay(value: number | null | undefined) {
if (value == null || Number.isNaN(Number(value))) return ''
@@ -23,7 +40,8 @@ export function formatNumber(value: number | null | undefined) {
return new Intl.NumberFormat('fa-IR').format(value)
}
/** Format a **rial** storage value as toman for SuperAdmin UI. */
export function formatMoney(value: number | null | undefined) {
if (value == null) return '—'
return `${formatNumber(value)} ریال`
return `${formatNumber(rialToToman(value))} تومان`
}