remove toPersianDigits
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
# Responsive layout vs data fetching
|
||||||
|
|
||||||
|
When adding or changing pages with mobile/desktop views:
|
||||||
|
|
||||||
|
## UI layer (responsive)
|
||||||
|
|
||||||
|
- Switch layouts with **`v-show`**, not **`v-if`**, so resize does not destroy child components.
|
||||||
|
- Prefer `ResponsiveViewPair` (`src/layouts/components/ResponsiveViewPair.vue`) or the home shell pattern.
|
||||||
|
- Keep **`<router-view>`** outside responsive chrome wrappers (header/topbar only in chrome components).
|
||||||
|
- Modal vs ActionSheet may use `v-if` by breakpoint; they must not own initial page data fetching.
|
||||||
|
|
||||||
|
## Data layer (fetch once)
|
||||||
|
|
||||||
|
- Call the page composable **once** in the route `index.vue` shell (e.g. `useXPage()` / `useXPageProvider()`).
|
||||||
|
- Put `onMounted` API calls only in that shell composable, never in `mobile/*` or `desktop/*` views.
|
||||||
|
- Mobile/Desktop views use **`useXPageContext()`** (provide/inject) — read-only UI.
|
||||||
|
- Do not call APIs inside `useDesktopBreakpoint()` watchers.
|
||||||
|
|
||||||
|
## Shared caches
|
||||||
|
|
||||||
|
- Use `runOnceAsync` (`src/composables/useOnceAsync.ts`) for store-level dedup when the same endpoint may be hit from multiple mounts.
|
||||||
|
- Reuse existing module caches (`useWalletAssets`, Pinia stores) before adding new fetch logic.
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
- Open Network tab, load a page, resize across 768px — **no duplicate requests** for the same endpoint.
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {usePrefix} from "@/utils/usePrefix";
|
import {usePrefix} from "@/utils/usePrefix";
|
||||||
import {computed} from "vue";
|
import {computed} from "vue";
|
||||||
import {usePrice} from "@/components/BasicPacks/BasicPriceInput/src/utils/usePrice";
|
import {usePrice} from "@/utils/usePrice";
|
||||||
|
|
||||||
//Props
|
//Props
|
||||||
interface Prop {
|
interface Prop {
|
||||||
@@ -59,13 +59,14 @@ const addonTextColor = computed(() => props.addonTextColor)
|
|||||||
//emits
|
//emits
|
||||||
const localValue = computed({
|
const localValue = computed({
|
||||||
set(value) {
|
set(value) {
|
||||||
// console.log(value?.toString().replaceAll(',', ''))
|
props.type === 'price'
|
||||||
props.type === 'price' ? emits('update:value', value?.toString().replaceAll(',', '')) : emits('update:value', value)
|
? emits('update:value', value?.toString().replaceAll(',', ''))
|
||||||
|
: emits('update:value', value)
|
||||||
},
|
},
|
||||||
get() {
|
get() {
|
||||||
let a = props.type === 'price' ? props.value ? usePrice(props.value as number, true, '').trim() : '' : props.value
|
return props.type === 'price'
|
||||||
// console.log(props.value)
|
? props.value ? usePrice(props.value as number, true, '').trim() : ''
|
||||||
return a
|
: props.value
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -75,10 +76,12 @@ const props = withDefaults(defineProps<Prop>(), {
|
|||||||
const emits = defineEmits(['update:value', 'onchange'])
|
const emits = defineEmits(['update:value', 'onchange'])
|
||||||
|
|
||||||
const handlerChange = (e: any) => {
|
const handlerChange = (e: any) => {
|
||||||
// emits('onchange', e.target.value)
|
props.type === 'price'
|
||||||
props.type === 'price' ? emits('update:value', e.target.value?.toString().replaceAll(',', '')) : emits('update:value', e.target.value)
|
? emits('update:value', e.target.value?.toString().replaceAll(',', ''))
|
||||||
props.type === 'price' ? emits('onchange', e.target.value?.toString().replaceAll(',', '')) : emits('onchange', e.target.value)
|
: emits('update:value', e.target.value)
|
||||||
// console.log('sas')
|
props.type === 'price'
|
||||||
|
? emits('onchange', e.target.value?.toString().replaceAll(',', ''))
|
||||||
|
: emits('onchange', e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
//css style
|
//css style
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {usePrice} from './utils/usePrice'
|
import {usePrice} from '@/utils/usePrice'
|
||||||
import {computed} from "vue";
|
import {computed} from "vue";
|
||||||
import BasicInput from "../../BasicInput.vue";
|
import BasicInput from "../../BasicInput.vue";
|
||||||
|
|
||||||
@@ -23,9 +23,7 @@ const props = withDefaults(defineProps<Props>(), {})
|
|||||||
|
|
||||||
const _val = computed({
|
const _val = computed({
|
||||||
set(val: string) {
|
set(val: string) {
|
||||||
// const temp = val.toString().replaceAll(',', '')
|
emit('update:value', val.toString().replaceAll(',', ''))
|
||||||
const temp = val.toString().replaceAll(',', '')
|
|
||||||
emit('update:value', temp)
|
|
||||||
},
|
},
|
||||||
get() {
|
get() {
|
||||||
return props.value ? usePrice(props.value as number, true, '').trim() : ''
|
return props.value ? usePrice(props.value as number, true, '').trim() : ''
|
||||||
|
|||||||
@@ -1,17 +1 @@
|
|||||||
export function usePrice(price: number, digit = true, unit = 'ریال') {
|
export { usePrice, usePriceWithoutType, useTowDecimal, useFormat } from '@/utils/usePrice'
|
||||||
let _price = price.toString()
|
|
||||||
if (digit) {
|
|
||||||
_price = _price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
|
||||||
}
|
|
||||||
return `${_price} ${unit}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useTowDecimal(num: string | number): string | number {
|
|
||||||
const temp = num.toString().match(/^-?\d+(?:\.\d{0,2})?/)
|
|
||||||
if (temp && temp.length > 0) return temp[0]
|
|
||||||
return Math.round(+num * 100) / 100
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useFormat(num: string | number, unit = '') {
|
|
||||||
return usePrice(Math.abs(+useTowDecimal(num)), true, unit)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹'
|
|
||||||
|
|
||||||
function formatGroupedPrice(price: string | number | null | undefined, digit: boolean) {
|
function formatGroupedPrice(price: string | number | null | undefined, digit: boolean) {
|
||||||
let value = price === null || price === undefined ? '' : String(price)
|
let value = price === null || price === undefined ? '' : String(price)
|
||||||
if (digit) {
|
if (digit) {
|
||||||
value = value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
value = value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||||
}
|
}
|
||||||
return value.replace(/\d/g, (d) => PERSIAN_DIGITS[Number(d)] ?? d)
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatNumber(value: string | number | null | undefined, grouped = false): string {
|
||||||
|
return formatGroupedPrice(value, grouped)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePrice(price: number, digit = true, unit = 'ریال') {
|
export function usePrice(price: number, digit = true, unit = 'ریال') {
|
||||||
@@ -87,6 +89,6 @@ export function getTransactionGoldAmount(item: TransactionPriceSource): number {
|
|||||||
|
|
||||||
export function formatGoldAmount(amount?: number | null): string {
|
export function formatGoldAmount(amount?: number | null): string {
|
||||||
const num = Number(amount)
|
const num = Number(amount)
|
||||||
if (!Number.isFinite(num)) return '۰'
|
if (!Number.isFinite(num)) return '0'
|
||||||
return formatGroupedPrice(Number(num.toFixed(3)), false)
|
return formatGroupedPrice(Number(num.toFixed(3)), false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div>مبلغ پاداش :</div>
|
<div>مبلغ پاداش :</div>
|
||||||
<div class="mr-4">{{item.gold}}</div>
|
<div class="mr-4">{{ formatGoldAmount(item.gold) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="mt-2 rounded-[8px] p-2 text-xs" :class="item.is_legal === 0? 'bg-red-100 text-red-600':'bg-green-100 test-green-600'">{{item.is_legal === 0? 'احراز هویت نشده':'احراز هویت شده'}}</div>-->
|
<!-- <div class="mt-2 rounded-[8px] p-2 text-xs" :class="item.is_legal === 0? 'bg-red-100 text-red-600':'bg-green-100 test-green-600'">{{item.is_legal === 0? 'احراز هویت نشده':'احراز هویت شده'}}</div>-->
|
||||||
<div class="flex text-[#9CA3AF]">
|
<div class="flex text-[#9CA3AF]">
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { formatGoldAmount } from '@/utils/usePrice'
|
||||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
import { toJalali, toJalaliNotime } from '../../../../utils/useDateTime'
|
import { toJalali, toJalaliNotime } from '../../../../utils/useDateTime'
|
||||||
import type { Gifts } from '@/models/gifts'
|
import type { Gifts } from '@/models/gifts'
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-gray-500 flex gap-1 text-xs items-center">
|
<div class="text-gray-500 flex gap-1 text-xs items-center">
|
||||||
<div :class="item.gold > 0 ? 'text-green-600':'text-red-600'" class="text-base number-ltr">
|
<div :class="item.gold > 0 ? 'text-green-600':'text-red-600'" class="text-base number-ltr">
|
||||||
{{ item.gold }}
|
{{ formatGoldAmount(item.gold) }}
|
||||||
</div>
|
</div>
|
||||||
{{item.unit}}
|
{{item.unit}}
|
||||||
</div>
|
</div>
|
||||||
@@ -39,14 +39,14 @@
|
|||||||
<div class="flex justify-between mb-3">
|
<div class="flex justify-between mb-3">
|
||||||
<div class="text-primary">مقدار {{props.title}} مبادله شده:</div>
|
<div class="text-primary">مقدار {{props.title}} مبادله شده:</div>
|
||||||
<div :class="item.gold >= 0? 'text-green-600' : 'text-red-600'" class="flex text-xs items-center">
|
<div :class="item.gold >= 0? 'text-green-600' : 'text-red-600'" class="flex text-xs items-center">
|
||||||
<div class="text-base ml-1 number-ltr">{{ item.gold }}</div>
|
<div class="text-base ml-1 number-ltr">{{ formatGoldAmount(item.gold) }}</div>
|
||||||
{{item.unit}}
|
{{item.unit}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between mb-3">
|
<div class="flex justify-between mb-3">
|
||||||
<div class="text-primary">موجودی {{ props.title }}:</div>
|
<div class="text-primary">موجودی {{ props.title }}:</div>
|
||||||
<div class="flex text-xs text-yellow-700 items-center">
|
<div class="flex text-xs text-yellow-700 items-center">
|
||||||
<div class="text-base ml-1">{{ item.balance_gold }}</div>
|
<div class="text-base ml-1">{{ formatGoldAmount(item.balance_gold) }}</div>
|
||||||
{{item.unit}}
|
{{item.unit}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,6 +88,7 @@ import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue";
|
|||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import BasicButton from "@/components/BasicPacks/BasicButton.vue";
|
import BasicButton from "@/components/BasicPacks/BasicButton.vue";
|
||||||
import type {Transaction} from "@/models/transaction";
|
import type {Transaction} from "@/models/transaction";
|
||||||
|
import {formatGoldAmount, usePriceWithoutType} from "../../../../../utils/usePrice";
|
||||||
import {toJalaliNotime, toTime} from "../../../../../utils/useDateTime";
|
import {toJalaliNotime, toTime} from "../../../../../utils/useDateTime";
|
||||||
|
|
||||||
const infoVisible = ref<boolean>(false)
|
const infoVisible = ref<boolean>(false)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<Vue3Odometer
|
<Vue3Odometer
|
||||||
:value="displayGoldVal"
|
:value="displayGoldVal"
|
||||||
class="o-text !text-xl font-bold"
|
class="o-text !text-xl font-bold"
|
||||||
style="font-family: iran-sans; direction: ltr"
|
style="direction: ltr"
|
||||||
/>
|
/>
|
||||||
ریال
|
ریال
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -113,10 +113,6 @@ onMounted(async () => {
|
|||||||
chartMounted.value = true
|
chartMounted.value = true
|
||||||
})
|
})
|
||||||
|
|
||||||
const toPersianNumber = (num: number) => {
|
|
||||||
return num.toString().replace(/\d/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[d])
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartOptions = computed(() => {
|
const chartOptions = computed(() => {
|
||||||
const fontFamily = getProjectFontFamily()
|
const fontFamily = getProjectFontFamily()
|
||||||
|
|
||||||
@@ -144,7 +140,7 @@ const chartOptions = computed(() => {
|
|||||||
tickAmount: 8,
|
tickAmount: 8,
|
||||||
show: false,
|
show: false,
|
||||||
labels: {
|
labels: {
|
||||||
formatter: (val: number) => toPersianNumber(usePrice(val, true, 'ه.ریال')),
|
formatter: (val: number) => usePrice(val, true, 'ه.ریال'),
|
||||||
style: { fontSize: '13px', fontFamily },
|
style: { fontSize: '13px', fontFamily },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -165,7 +161,7 @@ const chartOptions = computed(() => {
|
|||||||
})}`
|
})}`
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formatter: (val: number) => toPersianNumber(usePrice(val, true, 'ه.ریال')),
|
formatter: (val: number) => usePrice(val, true, 'ه.ریال'),
|
||||||
style: { fontFamily },
|
style: { fontFamily },
|
||||||
},
|
},
|
||||||
colors: [config.config.color],
|
colors: [config.config.color],
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
<Vue3Odometer
|
<Vue3Odometer
|
||||||
:value="displayGoldVal"
|
:value="displayGoldVal"
|
||||||
class="dhome-hero-price__odometer o-text"
|
class="dhome-hero-price__odometer o-text"
|
||||||
style="font-family: iran-sans"
|
|
||||||
/>
|
/>
|
||||||
<span class="dhome-hero-price__unit">ریال</span>
|
<span class="dhome-hero-price__unit">ریال</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="text-base ml-1">{{ item.gold }}</div>
|
<div class="text-base ml-1">{{ formatGoldAmount(item.gold) }}</div>
|
||||||
{{ item.unit }}
|
{{ item.unit }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-end text-xs text-yellow-700">
|
<div class="flex flex-col items-end text-xs text-yellow-700">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="text-base ml-1">{{ item.balance_gold }}</div>
|
<div class="text-base ml-1">{{ formatGoldAmount(item.balance_gold) }}</div>
|
||||||
{{ getProductQuantityUnit(item.wallet.product) }}
|
{{ getProductQuantityUnit(item.wallet.product) }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getProductUnitWeightLabel(item.wallet.product)" class="text-gray-500">
|
<div v-if="getProductUnitWeightLabel(item.wallet.product)" class="text-gray-500">
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
{{ getGoldWalletLabel(item.wallet?.product?.group_type) }}:
|
{{ getGoldWalletLabel(item.wallet?.product?.group_type) }}:
|
||||||
</div>
|
</div>
|
||||||
<div class="flex text-xs text-yellow-700 items-center">
|
<div class="flex text-xs text-yellow-700 items-center">
|
||||||
<div class="text-base ml-1">{{ item.balance_gold }}</div>
|
<div class="text-base ml-1">{{ formatGoldAmount(item.balance_gold) }}</div>
|
||||||
{{ getWeightUnit() }}
|
{{ getWeightUnit() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { usePriceWithoutType } from '@/utils/usePrice'
|
import { formatGoldAmount, usePriceWithoutType } from '@/utils/usePrice'
|
||||||
import type { Transaction } from '@/models/transaction'
|
import type { Transaction } from '@/models/transaction'
|
||||||
import {
|
import {
|
||||||
getCashWalletLabel,
|
getCashWalletLabel,
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
<div class="flex justify-between mb-3">
|
<div class="flex justify-between mb-3">
|
||||||
<div class="text-black">مقدار {{ getMetalTypeTradeLabel() }} مبادله شده:</div>
|
<div class="text-black">مقدار {{ getMetalTypeTradeLabel() }} مبادله شده:</div>
|
||||||
<div :class="item.gold >= 0? 'text-green-600' : 'text-red-600'" class="flex text-xs items-center">
|
<div :class="item.gold >= 0? 'text-green-600' : 'text-red-600'" class="flex text-xs items-center">
|
||||||
<div class="text-base ml-1 number-ltr">{{ item.gold }}</div>
|
<div class="text-base ml-1 number-ltr">{{ formatGoldAmount(item.gold) }}</div>
|
||||||
گرم
|
گرم
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
<div class="flex justify-between mb-3">
|
<div class="flex justify-between mb-3">
|
||||||
<div class="text-black">{{ getGoldWalletLabel() }}:</div>
|
<div class="text-black">{{ getGoldWalletLabel() }}:</div>
|
||||||
<div class="flex text-xs text-yellow-700 items-center">
|
<div class="flex text-xs text-yellow-700 items-center">
|
||||||
<div class="text-base ml-1">{{ item.balance_gold }}</div>
|
<div class="text-base ml-1">{{ formatGoldAmount(item.balance_gold) }}</div>
|
||||||
{{ getWeightUnit() }}
|
{{ getWeightUnit() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Icon from "@/components/BasicPacks/BasicIcon.vue";
|
import Icon from "@/components/BasicPacks/BasicIcon.vue";
|
||||||
import {getTransactionTradePrice, usePriceWithoutType} from "../../../../utils/usePrice";
|
import {formatGoldAmount, getTransactionTradePrice, usePriceWithoutType} from "../../../../utils/usePrice";
|
||||||
import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue";
|
import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue";
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import BasicButton from "@/components/BasicPacks/BasicButton.vue";
|
import BasicButton from "@/components/BasicPacks/BasicButton.vue";
|
||||||
|
|||||||
Reference in New Issue
Block a user